@belonguniverseai/react-sdk 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/api/client.d.ts +58 -32
  2. package/dist/api/task-schedules.d.ts +138 -255
  3. package/dist/component/{arc-DpA4gw3l.js → arc-TLcl3N8_.js} +2 -2
  4. package/dist/component/{architectureDiagram-3BPJPVTR-C8WjH-Q1.js → architectureDiagram-3BPJPVTR-DYcaPx2M.js} +3 -3
  5. package/dist/component/{blockDiagram-GPEHLZMM-O9IURu7q.js → blockDiagram-GPEHLZMM-BdjruLO5.js} +4 -4
  6. package/dist/component/{c4Diagram-AAUBKEIU-B4cFy6te.js → c4Diagram-AAUBKEIU-Bd2ebSgB.js} +3 -3
  7. package/dist/component/{channel-7qPdE580.js → channel-CYFijF_t.js} +2 -2
  8. package/dist/component/{chunk-2J33WTMH-CUyqMMUN.js → chunk-2J33WTMH-B06Y_xVy.js} +2 -2
  9. package/dist/component/{chunk-4BX2VUAB-4MjzMD47.js → chunk-4BX2VUAB-DVpS0I_p.js} +2 -2
  10. package/dist/component/{chunk-55IACEB6-BOiOLlJY.js → chunk-55IACEB6-DmZctFkK.js} +2 -2
  11. package/dist/component/{chunk-727SXJPM-D7rQvYjL.js → chunk-727SXJPM-A5r5oL1_.js} +6 -6
  12. package/dist/component/{chunk-AQP2D5EJ-B4yIZLgM.js → chunk-AQP2D5EJ-D4G4syYu.js} +4 -4
  13. package/dist/component/{chunk-FMBD7UC4-CHC657o0.js → chunk-FMBD7UC4-CbRxWx_A.js} +2 -2
  14. package/dist/component/{chunk-ND2GUHAM-BAq6BoPv.js → chunk-ND2GUHAM-OX1-DIU1.js} +2 -2
  15. package/dist/component/{chunk-QZHKN3VN-DSE2KpkL.js → chunk-QZHKN3VN-DLQ8oUCc.js} +2 -2
  16. package/dist/component/{classDiagram-4FO5ZUOK-Dp6GpqVA.js → classDiagram-4FO5ZUOK-4S9mdFC1.js} +3 -3
  17. package/dist/component/{classDiagram-v2-Q7XG4LA2-Dp6GpqVA.js → classDiagram-v2-Q7XG4LA2-4S9mdFC1.js} +3 -3
  18. package/dist/component/{cose-bilkent-S5V4N54A-D7qy0jl5.js → cose-bilkent-S5V4N54A-BIrVaakt.js} +2 -2
  19. package/dist/component/{dagre-BM42HDAG-Bp1xKCuc.js → dagre-BM42HDAG-BoPUzTvD.js} +2 -2
  20. package/dist/component/{diagram-2AECGRRQ-BgnTJHhy.js → diagram-2AECGRRQ-PIXCGxIt.js} +3 -3
  21. package/dist/component/{diagram-5GNKFQAL-D2Sf6fvv.js → diagram-5GNKFQAL-CbJxbGBh.js} +4 -4
  22. package/dist/component/{diagram-KO2AKTUF-B6q2Givz.js → diagram-KO2AKTUF-Da1E68Eo.js} +3 -3
  23. package/dist/component/{diagram-LMA3HP47-Dd-PZ9QO.js → diagram-LMA3HP47-dRbk5sBa.js} +3 -3
  24. package/dist/component/{diagram-OG6HWLK6-v65-inn8.js → diagram-OG6HWLK6-CcKT8mhn.js} +4 -4
  25. package/dist/component/{erDiagram-TEJ5UH35-s5ngcfah.js → erDiagram-TEJ5UH35-3ATYT9J-.js} +5 -5
  26. package/dist/component/{flowDiagram-I6XJVG4X-C-3l7ZN2.js → flowDiagram-I6XJVG4X-D9PLm9Od.js} +7 -7
  27. package/dist/component/{ganttDiagram-6RSMTGT7-AFajly3C.js → ganttDiagram-6RSMTGT7-F_XUzD1p.js} +3 -3
  28. package/dist/component/{gitGraphDiagram-PVQCEYII-DLcn7CA-.js → gitGraphDiagram-PVQCEYII-13gVhAGc.js} +4 -4
  29. package/dist/component/{highlighted-body-OFNGDK62-C7pDeGQU.js → highlighted-body-OFNGDK62-DCxRI4UV.js} +2 -2
  30. package/dist/component/index.js +2 -2
  31. package/dist/component/{infoDiagram-5YYISTIA-BuR4-KVY.js → infoDiagram-5YYISTIA-DymxA7AN.js} +2 -2
  32. package/dist/component/{ishikawaDiagram-YF4QCWOH-DlsfzgFK.js → ishikawaDiagram-YF4QCWOH-C3sTn76C.js} +2 -2
  33. package/dist/component/{journeyDiagram-JHISSGLW-D0rpdvR5.js → journeyDiagram-JHISSGLW-Cq0hOAfJ.js} +5 -5
  34. package/dist/component/{kanban-definition-UN3LZRKU-Cz0Dixqc.js → kanban-definition-UN3LZRKU-5tBWjkp7.js} +3 -3
  35. package/dist/component/{linear-BVVr39mA.js → linear-BT4blD8s.js} +2 -2
  36. package/dist/component/{mermaid-GHXKKRXX-BEhqkLaB.js → mermaid-GHXKKRXX-DXPXjnb7.js} +38570 -40748
  37. package/dist/component/{mindmap-definition-RKZ34NQL-wo3l0Jy7.js → mindmap-definition-RKZ34NQL-DO-OuAQQ.js} +4 -4
  38. package/dist/component/{pieDiagram-4H26LBE5-TtxT_WMN.js → pieDiagram-4H26LBE5-BkEktPwZ.js} +4 -4
  39. package/dist/component/{quadrantDiagram-W4KKPZXB-DK6ShvdY.js → quadrantDiagram-W4KKPZXB-CaJWg9ep.js} +3 -3
  40. package/dist/component/{requirementDiagram-4Y6WPE33-Z0IavHMQ.js → requirementDiagram-4Y6WPE33-CmZrtZqe.js} +4 -4
  41. package/dist/component/{sankeyDiagram-5OEKKPKP-C408nFHk.js → sankeyDiagram-5OEKKPKP-Cp95M1aj.js} +2 -2
  42. package/dist/component/{sequenceDiagram-3UESZ5HK-CMFVhmkn.js → sequenceDiagram-3UESZ5HK-C0x0WvhR.js} +4 -4
  43. package/dist/component/{stateDiagram-AJRCARHV-C6Z5V-Yt.js → stateDiagram-AJRCARHV-H10vaGL3.js} +3 -3
  44. package/dist/component/{stateDiagram-v2-BHNVJYJU-CztCn1yf.js → stateDiagram-v2-BHNVJYJU-rj7ljkc-.js} +3 -3
  45. package/dist/component/{timeline-definition-PNZ67QCA-D_9a8hKa.js → timeline-definition-PNZ67QCA-Cil1QaqZ.js} +3 -3
  46. package/dist/component/{vennDiagram-CIIHVFJN-HuWQgr4e.js → vennDiagram-CIIHVFJN-Dk8PKmfb.js} +2 -2
  47. package/dist/component/{wardleyDiagram-YWT4CUSO-CeUvdi76.js → wardleyDiagram-YWT4CUSO-Xq5Hnmio.js} +3 -3
  48. package/dist/component/{xychartDiagram-2RQKCTM6-DNmBGn_r.js → xychartDiagram-2RQKCTM6-dinwR4Zm.js} +3 -3
  49. package/dist/components/embed/BetaBadge.d.ts +12 -0
  50. package/dist/components/embed/DisclaimerGate.d.ts +16 -0
  51. package/dist/components/embed/EmbedHeader.d.ts +11 -4
  52. package/dist/components/embed/LegalShortDisclaimer.d.ts +11 -0
  53. package/dist/components/embed/LocalePills.d.ts +18 -0
  54. package/dist/components/embed/WelcomeIntro.d.ts +3 -1
  55. package/dist/components/embed/rail-flags.d.ts +10 -0
  56. package/dist/embed/brand-marks.d.ts +5 -5
  57. package/dist/embed/copy.d.ts +13 -0
  58. package/dist/embed/legal-disclaimer.d.ts +23 -0
  59. package/dist/embed/presets.d.ts +5 -1
  60. package/dist/embed/task-schedule-store.d.ts +3 -13
  61. package/dist/embed/voice/call-active-marker.d.ts +67 -30
  62. package/dist/embed/voice/line-dispatch.d.ts +22 -20
  63. package/dist/embed/voice/realtime-call.d.ts +12 -0
  64. package/dist/embed/voice/reconnect-offer.d.ts +14 -5
  65. package/dist/embed/voice/voice-token.d.ts +2 -12
  66. package/dist/embed/voice-call-lines.d.ts +4 -25
  67. package/dist/embed/voice-call.d.ts +112 -73
  68. package/dist/i18n/messages/en.d.ts +12 -6
  69. package/dist/i18n/messages/surfaces/embedMisc.d.ts +0 -3
  70. package/dist/i18n/messages/surfaces/stream.d.ts +28 -0
  71. package/dist/stream/belong-chat-transport.d.ts +101 -587
  72. package/dist/stream/message-metadata.d.ts +6 -36
  73. package/dist/stream/run-line-progress.d.ts +6 -5
  74. package/dist/stream/stream-state-store.d.ts +1 -25
  75. package/dist/stream/tool-action-label.d.ts +16 -0
  76. package/package.json +1 -1
  77. package/dist/components/embed/PoweredByBelong.d.ts +0 -7
  78. package/dist/embed/voice/agent-queue.d.ts +0 -106
  79. package/dist/embed/voice/call-roster-store.d.ts +0 -110
@@ -2,33 +2,62 @@ import { z } from 'zod';
2
2
  declare const callActiveMarkerSchema: z.ZodObject<{
3
3
  active: z.ZodLiteral<true>;
4
4
  startedAtMs: z.ZodNumber;
5
- /** Recap of the call so far, same shape/cap as `buildCallContextDigest()`.
6
- * Empty until the first user/agent timeline row lands. */
5
+ updatedAtMs: z.ZodNumber;
6
+ redialCount: z.ZodNumber;
7
7
  contextDigest: z.ZodString;
8
- }, "strip", z.ZodTypeAny, {
9
- active: true;
10
- startedAtMs: number;
11
- contextDigest: string;
12
- }, {
13
- active: true;
14
- startedAtMs: number;
15
- contextDigest: string;
16
- }>;
8
+ }, z.core.$strip>;
17
9
  export type CallActiveMarker = z.infer<typeof callActiveMarkerSchema>;
18
10
  /** The tab's persisted call marker, or null when absent/malformed — a
19
11
  * corrupted or pre-this-feature record just means "nothing to redial". */
20
12
  export declare const readCallActiveMarker: () => CallActiveMarker | null;
21
- /** Mark the call live — called on the `in_call` transition. Starts with an
22
- * empty digest; {@link refreshCallActiveMarkerContext} fills it in as the
23
- * call progresses. */
24
- export declare const writeCallActiveMarker: () => void;
13
+ /**
14
+ * Why the call that just reached `in_call` was dialled.
15
+ *
16
+ * `"user"` a dial the user asked for outright, which is a fresh intent: it
17
+ * inherits nothing from an interrupted call, redial streak included.
18
+ * `"redial"` — a reconnect of an interrupted call, which CONTINUES that
19
+ * streak. `voice-call.ts` maps its `startCall({ isReconnect })` option
20
+ * straight onto this, so the rail's manual "Reconnect" click also continues
21
+ * the streak rather than resetting it: the count only ever gates the
22
+ * AUTOMATIC redial, so continuing it errs toward asking the user again rather
23
+ * than toward reopening the microphone on its own.
24
+ */
25
+ export type CallDialOrigin = "user" | "redial";
26
+ /** Mark the call live — called on the `in_call` transition. `updatedAtMs`
27
+ * starts equal to `startedAtMs` (no heartbeat has ticked yet). `redialCount`
28
+ * carries over from the interrupted call on a `"redial"` (see the module doc:
29
+ * reaching `in_call` is NOT proof a redial worked — only the 30s healthy-call
30
+ * timer's {@link resetRedialCount} is) and starts at zero on a `"user"` dial.
31
+ * Starts with an empty digest; {@link refreshCallActiveMarkerContext} fills it
32
+ * in as the call progresses. */
33
+ export declare const writeCallActiveMarker: (origin: CallDialOrigin) => void;
25
34
  /** Update the persisted digest of an already-active marker. No-op when no
26
35
  * marker is active — a digest has nothing to attach itself to once the call
27
36
  * has ended. */
28
37
  export declare const refreshCallActiveMarkerContext: (contextDigest: string) => void;
38
+ /** How often `voice-call.ts` re-stamps `updatedAtMs` while a call is
39
+ * `in_call` — the proof-of-life cadence a future reload's staleness check is
40
+ * accurate to. */
41
+ export declare const MARKER_HEARTBEAT_MS = 10000;
42
+ /** Refresh the marker's proof-of-life stamp. Called on a
43
+ * `MARKER_HEARTBEAT_MS` interval for as long as the call is live — see the
44
+ * module doc comment for why this can't just ride `refreshCallActiveMarkerContext`.
45
+ * No-op when no marker is active, mirroring `refreshCallActiveMarkerContext`:
46
+ * a heartbeat has nothing left to touch once the call has ended. */
47
+ export declare const touchCallActiveMarker: () => void;
48
+ /** Record one more auto-redial attempt against the persisted marker. No-op
49
+ * when no marker is active — mirrors `refreshCallActiveMarkerContext`. */
50
+ export declare const noteRedialAttempt: () => void;
51
+ /** Zero the redial counter — called once a (re)dialed call has stayed up long
52
+ * enough to call healthy, so it stops counting against the next drop. No-op
53
+ * when no marker is active — mirrors `refreshCallActiveMarkerContext`. */
54
+ export declare const resetRedialCount: () => void;
29
55
  /** Call ended (explicit hangup or terminal error) — no reload should redial. */
30
56
  export declare const clearCallActiveMarker: () => void;
31
- export type ReconnectOfferDecision = "offer" | "skip";
57
+ /** What the dock's boot effect should do about an interrupted call: dial it
58
+ * back automatically, put the one-click offer on the rail's call button, or
59
+ * leave the dock alone. */
60
+ export type ReconnectAction = "redial" | "offer" | "skip";
32
61
  /**
33
62
  * How long after the interrupted call started we still OFFER to reconnect. A
34
63
  * reload lands in seconds; a tab the browser restored from a much older session
@@ -37,29 +66,37 @@ export type ReconnectOfferDecision = "offer" | "skip";
37
66
  */
38
67
  export declare const RECONNECT_OFFER_MAX_AGE_MS = 600000;
39
68
  /**
40
- * Whether the dock's boot effect should OFFER to reconnect an interrupted call.
41
- *
42
- * This deliberately decides an offer, never a dial. Auto-dialing on load opens
43
- * the MICROPHONE with no user gesture: the origin already holds mic permission
44
- * (a call was live moments ago), so the browser does not re-prompt and the mic
45
- * would come back silently on the next page load — on hosts that receive the
46
- * SDK through an unpinnable bundle, with no opt-in of their own. Requiring one
47
- * click to redial is both the honest consent story and what browsers' autoplay/
48
- * gesture rules expect; the marker is kept either way so the redial can still
49
- * carry the interrupted call's `contextDigest`.
69
+ * How stale the marker may be and still earn an AUTOMATIC redial. A reload
70
+ * lands within a couple of seconds; a tab the browser restored from a much
71
+ * older session must not silently reopen the microphone. Read against
72
+ * `updatedAtMs` (the heartbeat), never `startedAtMs` a call that has been
73
+ * live for 20 minutes must still come back.
74
+ */
75
+ export declare const REDIAL_MAX_STALENESS_MS = 120000;
76
+ /** Consecutive automatic redials before falling back to the button. A host
77
+ * that crash-loops must not reopen the mic on every load. */
78
+ export declare const MAX_CONSECUTIVE_REDIALS = 2;
79
+ /**
80
+ * What the dock's boot effect should do about a call this tab was in when the
81
+ * page went away.
50
82
  *
51
83
  * Pure so the branching is unit-testable without React or WebRTC: a marker
52
84
  * means a call was live when the page went away; `tokenReady` mirrors the same
53
85
  * auth-readiness gate the login screen polls; `alreadyOffered` is the one-shot
54
- * guard (the offer is raised at most once per page load); and `startedAtMs` is
55
- * consumed against `nowMs` so a stale marker cannot offer forever.
86
+ * guard (the decision is acted on at most once per page load); and the
87
+ * marker's timestamps are consumed against `nowMs` so a stale marker can
88
+ * neither dial nor offer forever.
89
+ *
90
+ * The gates are what make an unattended `"redial"` acceptable — see the module
91
+ * doc. `"offer"` is the same one-click fallback this file has always decided,
92
+ * now reached only when a redial's gates say no.
56
93
  */
57
- export declare const decideReconnectOffer: (input: {
94
+ export declare const decideReconnectAction: (input: {
58
95
  readonly marker: CallActiveMarker | null;
59
96
  readonly tokenReady: boolean;
60
97
  readonly alreadyOffered: boolean;
61
98
  readonly nowMs: number;
62
- }) => ReconnectOfferDecision;
99
+ }) => ReconnectAction;
63
100
  export declare const peekReconnectOffered: () => boolean;
64
101
  export declare const markReconnectOffered: () => void;
65
102
  /** Test-only: forget the one-shot guard between test cases. */
@@ -17,14 +17,14 @@
17
17
  * a workspace must land in THAT workspace, not the shared legacy root — so the
18
18
  * create body carries the effective `workspaceExternalId` (absent when the host
19
19
  * runs unscoped). The line's registry label seeds the session title when one
20
- * exists; otherwise (U4 — the common case for a freshly materialized line,
21
- * since `dispatchToBackgroundLine` no longer seeds the registry label with the
22
- * agent's own name) the REQUEST itself does, so the sessions list still names
23
- * the background conversation by what it's actually about ("Reconcile the
24
- * July invoices"), not the agent that happened to run it.
20
+ * exists; otherwise (U4 — the common case, since nothing seeds a freshly
21
+ * materialized line's registry label any more) the REQUEST itself does, so the
22
+ * sessions list still names the background conversation by what it's actually
23
+ * about ("Reconcile the July invoices"), not the agent that happened to run
24
+ * it.
25
25
  *
26
26
  * BOOT RE-ATTACH (F3/R4): `attachLineReader` below is the ONE seam that wires
27
- * a VOICE-dispatched line-run reader onto a line, shared by a fresh dispatch
27
+ * a SLOT-tracked line-run reader onto a line, shared by a fresh dispatch
28
28
  * (this module, right after a POST accepts a run) and `reattachPendingLineRuns`
29
29
  * (called once at boot, after `initAgentLines()` rehydrates the registry)
30
30
  * resuming a run that was still going when the page reloaded. Both paths
@@ -32,26 +32,28 @@
32
32
  * navigate/artifact/finish/error callbacks — a reload must behave exactly like
33
33
  * the live dispatch it is standing in for, not a parallel, easier-to-drift copy.
34
34
  *
35
- * TWO READER KINDS (BEL-90). This module now attaches two, and which callback
36
- * set a caller gets is a correctness question, not a style one:
35
+ * TWO READER KINDS (BEL-90). This module attaches two, and which callback set
36
+ * a caller gets is a correctness question, not a style one. NEITHER speaks —
37
+ * a background line never reaches the call (see voice-call.ts's agent-line
38
+ * section) — but they write different things:
37
39
  *
38
- * - `attachLineReader` — the voice-NARRATION reader. Its callbacks are not
39
- * plain setters: `deliverLineReply`/`deliverLineFailure` push
40
- * `conversation.item.create` events into an active voice call and ask the
41
- * model to respond, `notifyLinePhase` appends timeline rows and nudges
42
- * spoken progress, `deliverLineArtifact` opens a preview panel. Correct
43
- * for a run the operator dispatched BY VOICE.
40
+ * - `attachLineReader` — the SLOT reader, for a run this module dispatched.
41
+ * It owns the durable slot runId (`persistSlotRun`, set on dispatch and
42
+ * cleared on terminal), writes the registry through
43
+ * `deliverLineReply`/`deliverLineFailure`/`notifyLinePhase`/
44
+ * `notifyLineApprovalNeeded`, and additionally opens an artifact preview
45
+ * and dispatches host route navigation.
44
46
  * - `attachLineProgressReader` — the plain-progress reader, for a run the
45
47
  * user started by TYPING that was still in flight at reload. It goes
46
- * through `reportRunLineProgress` and touches only the registry, so a
47
- * typed run can never make the agent start talking about background
48
- * progress mid-call.
48
+ * through `reportRunLineProgress`, touches only the registry, and — see
49
+ * its own doc deliberately writes no slot runId, opens no panel, and
50
+ * drives no navigation.
49
51
  */
50
52
  /** Abort every live line reader and invalidate their callbacks (call teardown).
51
53
  * Subscribed below to fire whenever a call leaves in_call/connecting. */
52
54
  export declare const abortAllLineReaders: () => void;
53
55
  /** Test-only: drop every plain-progress reader (and the active-session latch)
54
- * so a case never inherits either from the previous case. The narration
56
+ * so a case never inherits either from the previous case. The slot
55
57
  * readers have `abortAllLineReaders`; these deliberately do NOT ride on it — a
56
58
  * voice call ending has nothing to do with a typed run still streaming. */
57
59
  export declare const __detachLineProgressReadersForTest: () => void;
@@ -66,8 +68,8 @@ export declare const __detachLineProgressReadersForTest: () => void;
66
68
  * TWO paths, because the two dispatch kinds have genuinely different
67
69
  * contracts (BEL-90 / B1 — see `attachLineProgressReader`'s doc):
68
70
  *
69
- * - F3/R4, UNCHANGED — a line whose durable SLOT carries a runId was
70
- * dispatched by voice. It gets the narration reader, on every line that has
71
+ * - F3/R4, UNCHANGED — a line whose durable SLOT carries a runId went
72
+ * through `dispatchToLine`. It gets the slot reader, on every line that has
71
73
  * one, INCLUDING when its session is the one on screen. That last part is
72
74
  * load-bearing: `persistSlotRun(line, null)` in that reader's terminal
73
75
  * callbacks is the durable slot's clearer, and skipping the on-screen line
@@ -26,6 +26,12 @@ export type RealtimeCallHandle = {
26
26
  readonly sendEvent: (event: Record<string, unknown>) => void;
27
27
  /** Toggle the local mic track without renegotiating. */
28
28
  readonly setMuted: (muted: boolean) => void;
29
+ /** Ask the browser to play the agent's audio again after it refused (see
30
+ * {@link ConnectRealtimeCallInput.onAudioBlocked}). Resolves true only when
31
+ * playback actually started. Call it STRAIGHT from the user's tap handler:
32
+ * the `play()` inside runs before the first await, so the gesture the
33
+ * browser is holding out for is still on the stack. */
34
+ readonly resumeAudio: () => Promise<boolean>;
29
35
  /** Tear down mic, peer connection, and audio playback. Idempotent. */
30
36
  readonly close: () => void;
31
37
  };
@@ -52,5 +58,11 @@ export type ConnectRealtimeCallInput = {
52
58
  readonly local: MediaStream;
53
59
  readonly remote: MediaStream;
54
60
  }) => void;
61
+ /** Fired when the browser REFUSES to play the agent's audio. `autoplay` is
62
+ * a request, not a guarantee — a page that reloaded mid-call has no fresh
63
+ * user gesture behind it — and a refusal we never asked about is completely
64
+ * silent: the call looks connected and says nothing. The store turns this
65
+ * into a one-tap "Tap to hear" control (`resumeAudio` above). */
66
+ readonly onAudioBlocked?: () => void;
55
67
  };
56
68
  export declare const connectRealtimeCall: (input: ConnectRealtimeCallInput) => Promise<ConnectRealtimeCallResult>;
@@ -2,12 +2,19 @@
2
2
  * "A call was interrupted — tap to reconnect" — the one-bit store that turns a
3
3
  * surviving call marker (`call-active-marker.ts`) into a USER-DRIVEN redial.
4
4
  *
5
- * The dock's boot effect raises the offer; the rail's CallButton reads it and,
6
- * while it stands, dials with `{ isReconnect: true }` (carrying the interrupted
5
+ * This is the FALLBACK half of reload survival: the dock's boot effect dials
6
+ * an interrupted call back on its own when the marker clears
7
+ * `decideReconnectAction`'s gates, and raises this offer when it does not —
8
+ * a marker too stale to prove the call was live moments ago, or one already
9
+ * redialed up to the consecutive-redial cap. It raises the offer again when a
10
+ * redial the gates DID allow fails to connect, so a flaky call-token endpoint
11
+ * costs the user one automatic attempt rather than the whole call (see
12
+ * `watchRedialForFallback` in BelongDock.tsx). Whenever it stands, the rail's
13
+ * CallButton dials with `{ isReconnect: true }` (carrying the interrupted
7
14
  * call's context digest) and labels itself "Reconnect" instead of "Start voice
8
15
  * call". That is the whole mechanism: ONE user click, on a control that is
9
- * already always visible in the rail — no new surface, and crucially no
10
- * microphone access without a gesture.
16
+ * already always visible in the rail — no new surface, and no microphone
17
+ * access without a gesture on exactly the marker the gates declined to trust.
11
18
  *
12
19
  * Why a separate store rather than a field on `CallState`: the call state is
13
20
  * rebuilt from `IDLE` on every dial and every terminal error, which would wipe
@@ -18,7 +25,9 @@
18
25
  */
19
26
  export declare const peekReconnectOffer: () => boolean;
20
27
  export declare const subscribeReconnectOffer: (listener: () => void) => (() => void);
21
- /** Raise the offer (dock boot, when `decideReconnectOffer` says so). */
28
+ /** Raise the offer: at dock boot when `decideReconnectAction` says `"offer"`
29
+ * (a marker too stale, or too many redials deep, to dial back on its own), and
30
+ * when a redial it DID allow ends in `error` without ever reaching the call. */
22
31
  export declare const offerReconnect: () => void;
23
32
  /**
24
33
  * Take the offer down. Called when the user acts on it (the reconnect click),
@@ -6,20 +6,10 @@ import { z } from 'zod';
6
6
  */
7
7
  declare const voiceTokensSchema: z.ZodObject<{
8
8
  deepgramToken: z.ZodString;
9
- deepgramWssUrl: z.ZodString;
9
+ deepgramWssUrl: z.ZodURL;
10
10
  elevenlabsToken: z.ZodString;
11
11
  voiceId: z.ZodString;
12
- }, "strip", z.ZodTypeAny, {
13
- deepgramToken: string;
14
- deepgramWssUrl: string;
15
- elevenlabsToken: string;
16
- voiceId: string;
17
- }, {
18
- deepgramToken: string;
19
- deepgramWssUrl: string;
20
- elevenlabsToken: string;
21
- voiceId: string;
22
- }>;
12
+ }, z.core.$strip>;
23
13
  export type VoiceTokens = z.infer<typeof voiceTokensSchema>;
24
14
  export type VoiceTokenResult = {
25
15
  ok: true;
@@ -4,9 +4,9 @@ export declare const MAX_AGENT_LINES = 3;
4
4
  * "idle" is the not-busy resting state — no work in flight, and nothing
5
5
  * completed/failed yet worth reporting (a fresh call, or a line rebound onto
6
6
  * a persisted session with no activity this call). "ready" is the DISTINCT
7
- * terminal state after a run actually finished successfully it carries a
8
- * result (`lastText`) worth showing, unlike "idle" which never conflates the
9
- * two anymore (pre-CallScreen-board, both were folded into "ready").
7
+ * terminal state after a run actually finished successfully, unlike "idle"
8
+ * which never conflates the two anymore (pre-CallScreen-board, both were
9
+ * folded into "ready").
10
10
  */
11
11
  export type AgentLineStatus = "idle" | "working" | "ready" | "failed" | "needs_approval";
12
12
  export type AgentLine = {
@@ -28,7 +28,6 @@ export type AgentLine = {
28
28
  readonly sessionId: string | null;
29
29
  readonly label: string;
30
30
  readonly status: AgentLineStatus;
31
- readonly lastText: string;
32
31
  /** The line's current run, for `stop_agent` to target — bound by the
33
32
  * line-run reader once a background dispatch's POST returns a runId, and
34
33
  * cleared back to `null` when that run reaches a terminal state.
@@ -158,26 +157,6 @@ export declare const isBoundToAnotherLine: (sessionId: string) => boolean;
158
157
  */
159
158
  export declare const foregroundLineNumber: (sessionId: string | null) => number;
160
159
  export declare const openAgentLine: (label: string) => AgentLine | null;
161
- /**
162
- * Materialize a line AT A GIVEN NUMBER, unlike `openAgentLine`'s lowest-FREE
163
- * allocation. F1: role→line is fixed and positional (main=1, cli=2,
164
- * consulting=3 — `call-roster-store.ts`), so a background dispatch to a role
165
- * whose line has never been touched must land EXACTLY on that role's number,
166
- * never wherever happens to be free. `dispatchToBackgroundLine`
167
- * (voice-call.ts) is the one production caller: "asking the CLI agent for
168
- * something IS bringing it on" (design doc) means the line materializes on
169
- * first use instead of reporting `unknown_line` for a line that simply
170
- * hasn't been dispatched to yet.
171
- *
172
- * Returns `null` when `line` is outside the switchboard's
173
- * 1..MAX_AGENT_LINES range, or when it is already occupied — the caller's
174
- * own `getLine(line) === undefined` check is expected to make the second
175
- * case unreachable in practice (this function is only ever called after
176
- * that check fails), but the guard keeps the contract sound on its own
177
- * terms rather than trusting every future caller to get the check right
178
- * (§28).
179
- */
180
- export declare const openAgentLineAt: (line: number, label: string) => AgentLine | null;
181
160
  /**
182
161
  * RELEASE a line — the mirror of `persistSlotBinding`'s adoption: clears the
183
162
  * in-memory entry AND its durable slot together, so the freed number is
@@ -266,7 +245,7 @@ export declare const bindLineRun: (line: number, runId: string | null) => void;
266
245
  * is a harmless no-op, which is what keeps the terminal path above uniform.
267
246
  */
268
247
  export declare const persistSlotRun: (line: number, runId: string | null) => void;
269
- export declare const setLineStatus: (line: number, status: AgentLineStatus, lastText?: string) => void;
248
+ export declare const setLineStatus: (line: number, status: AgentLineStatus) => void;
270
249
  /** CallScreen's agent board reads this as the live "what is it doing right
271
250
  * now" line under a working line's status dot — written on every phase
272
251
  * notification (`notifyLinePhase`) and cleared (`null`) once the line's run
@@ -1,5 +1,6 @@
1
1
  import { AgentLineStatus } from './voice-call-lines.ts';
2
2
  import { WorkPhase } from './voice-narration.ts';
3
+ import { CallDialOrigin } from './voice/call-active-marker.ts';
3
4
  export type CallStatus = "idle" | "connecting" | "in_call" | "error";
4
5
  /**
5
6
  * One row of the call's execution timeline (the CallScreen activity card):
@@ -11,12 +12,15 @@ export type CallTimelineEntry = {
11
12
  readonly id: number;
12
13
  readonly atMs: number;
13
14
  readonly kind: "user" | "agent" | "step" | "success" | "error" | "skill" | "schedule";
15
+ /** What the row SAYS. Empty for the two terminal kinds (`success`/`error`),
16
+ * whose whole meaning is the kind itself — CallScreen renders their own
17
+ * label and ignores this. See {@link TEXTLESS_KINDS}. */
14
18
  readonly text: string;
15
19
  /** The switchboard line this row concerns; `null` for a foreground (line 1)
16
- * row. CallScreen's `labelFor` resolves `line ?? 1` to look up the agent's
17
- * short board label for `step`/`success`/`error` rows (X2b) the label
18
- * column names the agent, so `text` itself never repeats a title prefix or
19
- * an "A2"-style tag. */
20
+ * row. Bookkeeping only no surface labels a row with an agent name any
21
+ * more (there is one agent, and it is the chat on screen), so this exists
22
+ * so a marker written for a background line can be told apart from one
23
+ * written for the foreground. */
20
24
  readonly line: number | null;
21
25
  };
22
26
  export type CallState = {
@@ -39,20 +43,63 @@ export type CallState = {
39
43
  /** True between the user finishing an utterance and the model responding —
40
44
  * the "Thinking…" window on the call screen. */
41
45
  readonly thinking: boolean;
46
+ /**
47
+ * Why the dial currently in flight (or the call it produced) happened —
48
+ * `"user"` for one the user asked for, `"redial"` for a reconnect. Set on
49
+ * the `connecting` transition and read back by two very different consumers:
50
+ * the marker write below (a reconnect CONTINUES the interrupted call's
51
+ * redial streak) and CallScreen, which says "Reconnecting…" rather than
52
+ * "Connecting…" so a microphone that reopened on its own explains itself.
53
+ */
54
+ readonly dialOrigin: CallDialOrigin;
42
55
  /** Wall-clock start of the current call; 0 while idle. */
43
56
  readonly callStartedAtMs: number;
44
57
  /** Rolling execution timeline, oldest first (capped). */
45
58
  readonly timeline: readonly CallTimelineEntry[];
59
+ /** The browser REFUSED to play the agent's audio (autoplay is a request,
60
+ * not a guarantee — an automatically redialed call after a page reload has
61
+ * no fresh user gesture behind it). Drives CallScreen's one-tap "Tap to
62
+ * hear" control; {@link resumeCallAudio} clears it. */
63
+ readonly audioBlocked: boolean;
46
64
  readonly errorMessage?: string;
47
65
  };
48
66
  /**
49
- * ChatRoute pushes Belong-agent progress into the call timeline (tool-phase
50
- * steps while a delegated run streams). No-op when no call is active.
67
+ * ChatRoute pushes a call-relevant NOTICE onto the timeline — the sentences
68
+ * that explain why nothing ran (a skill lookup miss, a catalog that hasn't
69
+ * loaded), an artifact that opened on screen, a voice-resolved approval.
70
+ * No-op when no call is active.
71
+ *
72
+ * `step` is the only kind ChatRoute writes: it is the one kind whose TEXT is
73
+ * rendered (a `success`/`error` row is its kind label and nothing else — see
74
+ * {@link TEXTLESS_KINDS}), so a notice pushed as anything else would have its
75
+ * sentence silently dropped.
76
+ *
77
+ * Keeps the consecutive-identical-row window: these are free-form sentences
78
+ * written from event handlers that can fire twice on one underlying fact (the
79
+ * voice re-asking for the same missing skill, say), and a verbatim repeat
80
+ * seconds apart says nothing new. The agent's own ACTION rows are the
81
+ * opposite case and have their own entry point — {@link pushCallAction}.
51
82
  */
52
83
  export declare const pushCallStep: (text: string) => void;
53
- /** ChatRoute surfaces call-relevant notices (skill lookup misses, catalog
54
- * failures) on the timeline so the user can SEE why nothing ran. */
55
- export declare const pushCallNotice: (kind: "step" | "error", text: string) => void;
84
+ /**
85
+ * ChatRoute pushes one ACTION onto the call timeline one line per thing the
86
+ * agent did ("Read a file", "Ran a command"). No-op when no call is active.
87
+ *
88
+ * Deliberately NOT {@link pushCallStep}: the action vocabulary
89
+ * (`tool-action-label.ts`) is a fixed ~15-string set of parameterless
90
+ * constants, so "read three files" is three BYTE-IDENTICAL rows and the
91
+ * dedupe window folded them into one — an agent that read three files logged
92
+ * one, and five unrecognized tools (all "Worked on it") logged one. On a
93
+ * surface whose whole job is "let me follow along while I listen", that is
94
+ * the difference between working and not.
95
+ *
96
+ * Safe to opt out because this call site is already idempotent at source:
97
+ * chat.tsx's `loggedActionPartIdsRef` keys on the tool-call PART id, so the
98
+ * same call re-arriving as started → completed writes exactly one row. Same
99
+ * reasoning, same opt-out, as the terminal markers in `deliverAgentReply` /
100
+ * `deliverAgentFailure`.
101
+ */
102
+ export declare const pushCallAction: (text: string) => void;
56
103
  /** Hard ceiling on the rendered digest — a compact recap, not a transcript. */
57
104
  export declare const CONTEXT_DIGEST_MAX_CHARS = 1000;
58
105
  /**
@@ -116,44 +163,6 @@ declare let lineDispatcher: ((req: {
116
163
  readonly isNewLine: boolean;
117
164
  }) => void) | null;
118
165
  export declare const registerCallLineDispatcher: (dispatcher: NonNullable<typeof lineDispatcher>) => (() => void);
119
- /**
120
- * Last-resort delivery for `flushLineQueuesOnCallEnd` when no `lineDispatcher`
121
- * is wired (the M7 gap — ChatRoute unmounted). Self-registered by
122
- * line-dispatch.ts (see the bottom of that file) with the real
123
- * `dispatchToLine` — deliberately NOT imported here directly. `line-dispatch.ts`
124
- * already imports six functions FROM this module (buildCallContextDigest,
125
- * the deliver/notify callbacks, peekCallState, subscribeCallState), so a
126
- * static import the other way would close a two-way cycle between
127
- * voice-call.ts and line-dispatch.ts — forbidden outright by
128
- * `.dependency-cruiser.cjs`'s `no-circular` rule (severity: error, applies
129
- * everywhere, not just the hexagonal backend layers). Registering FROM
130
- * line-dispatch.ts, in the SAME direction its existing dependency already
131
- * runs, avoids the cycle entirely; that file's own `subscribeCallState`
132
- * self-subscribe (its module doc, right above `abortAllLineReaders`) is the
133
- * identical idiom already established there.
134
- *
135
- * Investigated and confirmed SOUND as a fallback: chat.tsx's own
136
- * `registerCallLineDispatcher` wrapper does nothing beyond routing line 1 to
137
- * `sendCallText` and calling this exact function, unmodified, for any other
138
- * line — see that effect in chat.tsx. Held/queued items only ever exist for
139
- * background lines (line 1 never enqueues — see the busy-line branch above),
140
- * so every item this fallback ever sees is exactly the shape ChatRoute's
141
- * wrapper would have passed through unchanged. No ChatRoute-specific
142
- * behavior is lost by calling it directly.
143
- *
144
- * Deliberately SEPARATE from `lineDispatcher` — not folded into
145
- * `releaseHeldRequest` itself — so `pumpLineQueue`'s live-queue draining is
146
- * UNAFFECTED: it must keep checking `lineDispatcher` and leaving an item
147
- * queued (not popped) when nothing is wired, per its own doc. Only the
148
- * end-of-call flush, which has nothing left to leave a drained item queued
149
- * IN, reaches for this.
150
- */
151
- declare let lineDispatchFallback: ((req: {
152
- readonly line: number;
153
- readonly request: string;
154
- readonly isNewLine: boolean;
155
- }) => Promise<void>) | null;
156
- export declare const registerLineDispatchFallback: (dispatcher: NonNullable<typeof lineDispatchFallback>) => (() => void);
157
166
  export declare const registerCallLineViewSwitcher: (switcher: (sessionId: string) => void) => (() => void);
158
167
  export declare const registerCallClearNotifier: (notifier: (sessionId: string) => void) => (() => void);
159
168
  /**
@@ -174,10 +183,11 @@ export declare const notifyCallProgress: (hint: string) => void;
174
183
  * small work badge while the agent is busy. */
175
184
  export declare const setCallWorkPhase: (phase: CallState["workPhase"]) => void;
176
185
  /**
177
- * Click-path twin of `show_agent`: the lines strip chip on CallScreen
178
- * calls this directly (no tool-call round trip, no ack). No-op when the line
179
- * is unknown, never bound to a session, or no view switcher is registered
180
- * (e.g. ChatRoute hasn't mounted).
186
+ * The lines strip chip on CallScreen calls this directly (no tool-call round
187
+ * trip, no ack — there is no voice tool for it any more, which is why nothing
188
+ * here reports an outcome): switch the chat panel to the line's session and
189
+ * minimize the call screen. No-op when the line is unknown, never bound to a
190
+ * session, or no view switcher is registered (e.g. ChatRoute hasn't mounted).
181
191
  */
182
192
  export declare const showLineOnScreen: (line: number) => void;
183
193
  /**
@@ -198,7 +208,41 @@ export declare const showLineOnScreen: (line: number) => void;
198
208
  export declare const startCall: (options?: {
199
209
  readonly isReconnect?: boolean;
200
210
  }) => Promise<void>;
211
+ /**
212
+ * The error panel's "Call again". Dials as a RECONNECT whenever an interrupted
213
+ * call's marker is still there — which, by `keepsCallActiveMarker`, is exactly
214
+ * the case worth reconnecting to: a redial that never became a call. Its
215
+ * `contextDigest` is the only surviving record of what the user was doing, so
216
+ * a bare `startCall()` here opened a COLD call the user had to re-explain,
217
+ * while the recap the failure path deliberately preserved went unread.
218
+ *
219
+ * Deliberately does NOT dismiss the error first: `dismissCallError`'s idle
220
+ * transition is one of the two moments allowed to keep the marker, but only
221
+ * because ✕ means "put the panel away" — routing a RETRY through it would
222
+ * still hand `startCall` a marker whose digest it had to re-read after a
223
+ * needless round trip. `startCall`'s own `connecting` patch replaces the error
224
+ * state (and its panel) synchronously, so there is nothing left to dismiss.
225
+ */
226
+ export declare const retryCall: () => Promise<void>;
227
+ /**
228
+ * Hang up. The one ending that is an explicit GIVE-UP, so it always takes the
229
+ * interrupted call's marker and any standing reconnect offer with it — stated
230
+ * here rather than left to `keepsCallActiveMarker`, whose `error → idle`
231
+ * branch exists for the ✕ that merely puts the error panel away. Both clears
232
+ * run BEFORE the transition, so that branch sees no marker and agrees.
233
+ */
201
234
  export declare const endCall: () => void;
235
+ /**
236
+ * The user tapped "Tap to hear" — ask the browser to play the agent's audio
237
+ * again, now that a real gesture is on the stack, and take the control down
238
+ * only once playback has ACTUALLY started. Clearing it optimistically would
239
+ * leave a silent call with nothing left to tap.
240
+ *
241
+ * Deliberately not `async`: CallScreen calls this straight from the click
242
+ * handler, and the transport's `play()` runs before the first await, so the
243
+ * gesture the browser is holding out for is still on the stack.
244
+ */
245
+ export declare const resumeCallAudio: () => void;
202
246
  export declare const toggleCallMuted: () => void;
203
247
  /** Dismiss a sticky error state (the overlay's ✕). */
204
248
  export declare const dismissCallError: () => void;
@@ -218,9 +262,9 @@ export declare const deliverAgentReply: (text: string) => void;
218
262
  * must tell the user something went wrong and offer to retry.
219
263
  */
220
264
  export declare const deliverAgentFailure: (message: string) => void;
221
- /** A background line finished: relay its final text, naming the agent by
222
- * role. */
223
- export declare const deliverLineReply: (line: number, text: string) => void;
265
+ /** A background line finished: converge its chip. Nothing is spoken see
266
+ * this section's header. */
267
+ export declare const deliverLineReply: (line: number) => void;
224
268
  /**
225
269
  * A background line published an artifact: OPEN it on screen, mirroring
226
270
  * chat.tsx's foreground auto-open effect exactly — ALWAYS docked (a persisted
@@ -234,26 +278,21 @@ export declare const deliverLineArtifact: (line: number, artifact: {
234
278
  readonly title: string;
235
279
  readonly mimeType: string;
236
280
  }) => void;
237
- /** A background line failed: tell the user, naming the agent by role. */
238
- export declare const deliverLineFailure: (line: number, message: string) => void;
239
- /** A background line is blocked on a tool approval — steer the user to it,
240
- * by role, and tell the model WHAT it wants to do. W3: the old text asked
241
- * the user to say a magic phrase ("show <title>") and never said what was
242
- * being approved unanswerable on a phone call, where there is no screen to
243
- * "go look at". `reason` is the approval part's own `reason` field, threaded
244
- * from the run stream via `onApprovalNeeded` (line-run-reader.ts). */
245
- export declare const notifyLineApprovalNeeded: (line: number, timelineText: string, reason: string) => void;
281
+ /** A background line failed: converge its chip. Nothing is spoken see this
282
+ * section's header. */
283
+ export declare const deliverLineFailure: (line: number) => void;
284
+ /** A background line is blocked on a tool approval paint its chip. The
285
+ * approval itself is resolved from the rail's typed chat, on screen, where
286
+ * the user can read what is being approved; the call is not told, and takes
287
+ * no `reason`, because it has no way to name whose approval it is. */
288
+ export declare const notifyLineApprovalNeeded: (line: number) => void;
246
289
  /** A background line advanced a phase — record it as the line's live step
247
- * (CallScreen's agent board reads `currentStepLabel`/`currentPhase` off the
248
- * registry) and nudge a spoken update. X2a: deliberately NO timeline row per
249
- * phase change any more — the board's own card already shows the identical
250
- * string live, and a 12-step run used to write 12 near-identical Activity
251
- * rows that buried the actual results; the durable Activity-log row for a
252
- * background line's work is the ONE `dispatchToBackgroundLine` appends up
253
- * front. The registry write is unconditional (line-dispatch's own `isStale`
254
- * guard already keeps a stale-call phase from ever reaching here); the
255
- * spoken nudge stays guarded like its delivery siblings so a phase that DOES
256
- * arrive with no live call just updates the board silently. */
290
+ * (CallScreen's agent board and the rail read `currentStepLabel`/
291
+ * `currentPhase` off the registry). X2a: deliberately NO timeline row per
292
+ * phase change — the board's own card already shows the identical string
293
+ * live, and a 12-step run used to write 12 near-identical Activity rows that
294
+ * buried the actual results. The write is unconditional: line-dispatch's own
295
+ * `isStale` guard already keeps a stale-call phase from ever reaching here. */
257
296
  export declare const notifyLinePhase: (line: number, phaseLabel: string, phase?: WorkPhase | null) => void;
258
297
  /** True while a call holds the audio path — ChatRoute gates TTS autoplay on it. */
259
298
  export declare const isCallActive: () => boolean;