{"version":3,"sources":["../src/hostAttention.ts"],"sourcesContent":["// The host-attention channel (R3-307) — what the host is waiting for, right now.\n//\n// THE GAP THIS CLOSES. R3-298 bounded every host protocol call and classified each as\n// `attended` or `unattended`, but classification is per `(scheme, method)` and\n// attendedness is really a property of a MOMENT: `spaces:mount` is unattended when the\n// grant is already held and attended on first use, because the host raises consent INSIDE\n// the request. A per-method table cannot tell those apart, so R3-298 put anything that MAY\n// prompt on the long (attended) bound — which means the common grant-held path also waits\n// ten minutes before it reports a fault, and a caller can only say \"still waiting on\n// something that may need you\", never \"tap your passkey\".\n//\n// Only the HOST knows a prompt is on screen right now and what it is. So it says so, on a\n// channel every app can read, and two things become possible:\n//\n//   1. A surface can name what it is waiting for. \"Waiting for your passkey…\" instead of a\n//      spinner is most of a user's ability to act — especially on the provider-setup\n//      wizard's Test-connection step, whose whole purpose is to prove setup worked.\n//   2. The attended bounds become a fact rather than a guess. A may-prompt call runs on the\n//      SHORT bound and is suspended only while the host says a person is being asked\n//      (`protocolDeadline.ts`, `sandboxUtils.withDeadline`).\n//\n// WHAT IT DELIBERATELY DOES NOT CARRY. No secret value, no secret id, no app key, no\n// capability, no resource name — nothing from which a held grant could be inferred. It is\n// readable by every app at the baseline principal, so it says only that the HOST is busy\n// and with what KIND of prompt. That is information the user is already looking at: a host\n// modal is visible chrome. (The residual it does leave — one app observing that some prompt\n// is up while another app's call is in flight — is the accepted low-bandwidth L0 signalling\n// class of the threat model §7.6, and carries no identifying detail to signal WITH.)\nimport { createPushChannel } from './pushChannel';\nimport { HOST_ATTENTION, REQUEST_HOST_ATTENTION } from './generated/protocol';\n\n/**\n * What kind of host prompt is up.\n *\n * A closed set on purpose: it is the coarsest vocabulary that still lets a surface write a\n * sentence a user can act on, and adding a finer kind would start to describe the specific\n * thing being asked about — which is the disclosure this channel refuses.\n *\n * - `passkey` — a WebAuthn assertion. The user must tap their authenticator.\n * - `consent` — a capability/consent decision (mount, create, share, import, reference).\n * - `picker` — a host-drawn chooser or entry surface (the powerbox, the add-secret modal).\n * - `confirmation` — a notice or confirm the user must dismiss.\n */\nexport type HostAttentionKind = 'passkey' | 'consent' | 'picker' | 'confirmation';\n\n/** Whether the host is waiting on the user right now, and for what. */\nexport interface HostAttention {\n  /** `true` while a host prompt is on screen awaiting the user. */\n  awaiting: boolean;\n  /** The kind of prompt on screen — the innermost one, when they nest (an add-secret modal\n   *  that reaches the passkey unlock reports `passkey`). `null` when not awaiting. */\n  kind: HostAttentionKind | null;\n  /** `Date.now()` when the current wait began — the start of the whole continuous span, not\n   *  of the innermost prompt. `null` when not awaiting. */\n  since: number | null;\n}\n\n/** Assumed before the host reports, and the value on a host that never pushes this channel\n *  at all: nobody is being asked. Defaulting the OTHER way would suspend every may-prompt\n *  deadline forever on an older host, which is the hang this whole line of work removes. */\nexport const NO_HOST_ATTENTION: HostAttention = { awaiting: false, kind: null, since: null };\n\nconst KINDS: ReadonlySet<string> = new Set(['passkey', 'consent', 'picker', 'confirmation']);\n\n/** Tolerant parse: a host that pushes a malformed payload is ignored, not believed. An\n *  `awaiting` frame with no valid `kind` still counts as awaiting — the wait is the\n *  load-bearing bit and the copy can fall back to a generic sentence. */\nconst parseHostAttention = (value: unknown): HostAttention | undefined => {\n  const a = value as Partial<HostAttention> | null | undefined;\n  if (!a || typeof a !== 'object' || typeof a.awaiting !== 'boolean') return undefined;\n  if (!a.awaiting) return NO_HOST_ATTENTION;\n  return {\n    awaiting: true,\n    kind: typeof a.kind === 'string' && KINDS.has(a.kind) ? (a.kind as HostAttentionKind) : null,\n    since: typeof a.since === 'number' ? a.since : null,\n  };\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `host-attention` and\n// answers `request-host-attention` (wire format: site-main channelBridge.ts). Read\n// DEFENSIVELY — a host that does not yet push it simply never answers, `initial` stands,\n// and every caller behaves exactly as it did before this channel existed.\nconst channel = createPushChannel<HostAttention>({\n  pushType: HOST_ATTENTION,\n  requestType: REQUEST_HOST_ATTENTION,\n  initial: NO_HOST_ATTENTION,\n  parse: (msg) => parseHostAttention(msg.attention),\n});\n\n/** Returns what the host is waiting for right now. Poll for a one-off read. */\nexport const getHostAttention = (): HostAttention => channel.get();\n\n/**\n * Subscribe to host-attention changes. The listener is invoked immediately with the current\n * value, then again on every change. Returns an unsubscribe fn.\n */\nexport const onHostAttentionChange = (listener: (attention: HostAttention) => void): (() => void) =>\n  channel.onChange(listener);\n\n/** React hook returning what the host is waiting for, re-rendering on change. */\nexport const useHostAttention = (): HostAttention => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BA,yBAAkC;AAClC,sBAAuD;AA+BhD,MAAM,oBAAmC,EAAE,UAAU,OAAO,MAAM,MAAM,OAAO,KAAK;AAE3F,MAAM,QAA6B,oBAAI,IAAI,CAAC,WAAW,WAAW,UAAU,cAAc,CAAC;AAK3F,MAAM,qBAAqB,CAAC,UAA8C;AACxE,QAAM,IAAI;AACV,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,OAAO,EAAE,aAAa,UAAW,QAAO;AAC3E,MAAI,CAAC,EAAE,SAAU,QAAO;AACxB,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM,OAAO,EAAE,SAAS,YAAY,MAAM,IAAI,EAAE,IAAI,IAAK,EAAE,OAA6B;AAAA,IACxF,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;AAAA,EACjD;AACF;AAMA,MAAM,cAAU,sCAAiC;AAAA,EAC/C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAQ,mBAAmB,IAAI,SAAS;AAClD,CAAC;AAGM,MAAM,mBAAmB,MAAqB,QAAQ,IAAI;AAM1D,MAAM,wBAAwB,CAAC,aACpC,QAAQ,SAAS,QAAQ;AAGpB,MAAM,mBAAmB,MAAqB,QAAQ,IAAI;","names":[]}