@belonguniverseai/react-sdk 0.4.2 → 0.4.3

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/agent-grants.d.ts +33 -0
  2. package/dist/api/client.d.ts +97 -10
  3. package/dist/api/retry-fetch.d.ts +3 -1
  4. package/dist/api/task-schedules.d.ts +14 -0
  5. package/dist/browser/agent-link.d.ts +18 -0
  6. package/dist/browser/agent-touch.d.ts +105 -0
  7. package/dist/browser/spa-navigate.d.ts +24 -1
  8. package/dist/component/{arc-COH48Rtu.js → arc-ByLm_58u.js} +2 -2
  9. package/dist/component/{architectureDiagram-3BPJPVTR-B5qmE5k5.js → architectureDiagram-3BPJPVTR-DcYT4v9Q.js} +3 -3
  10. package/dist/component/{blockDiagram-GPEHLZMM-lkovkWkX.js → blockDiagram-GPEHLZMM-CHk-YSun.js} +4 -4
  11. package/dist/component/{c4Diagram-AAUBKEIU-CaWDjxTr.js → c4Diagram-AAUBKEIU-CEV2fwh-.js} +3 -3
  12. package/dist/component/{channel-DNnfyuhY.js → channel-D67v09ii.js} +2 -2
  13. package/dist/component/{chunk-2J33WTMH-I7HgfCPn.js → chunk-2J33WTMH-Bsl2GBCp.js} +2 -2
  14. package/dist/component/{chunk-4BX2VUAB-CZBTp8pg.js → chunk-4BX2VUAB-BKmdmdRY.js} +2 -2
  15. package/dist/component/{chunk-55IACEB6-C-RnqcTj.js → chunk-55IACEB6-DnliqOsL.js} +2 -2
  16. package/dist/component/{chunk-727SXJPM-BFV7YyWV.js → chunk-727SXJPM-O0BgSnK_.js} +6 -6
  17. package/dist/component/{chunk-AQP2D5EJ-BDVikNJT.js → chunk-AQP2D5EJ-CwBA3VVY.js} +4 -4
  18. package/dist/component/{chunk-FMBD7UC4-C864-npb.js → chunk-FMBD7UC4-CG5DaKp9.js} +2 -2
  19. package/dist/component/{chunk-ND2GUHAM-DuitYR9A.js → chunk-ND2GUHAM-COsoTCa0.js} +2 -2
  20. package/dist/component/{chunk-QZHKN3VN-B9uI1TYV.js → chunk-QZHKN3VN-9FjIin4_.js} +2 -2
  21. package/dist/component/{classDiagram-4FO5ZUOK-BUdCEjaj.js → classDiagram-4FO5ZUOK-DyQO35S9.js} +3 -3
  22. package/dist/component/{classDiagram-v2-Q7XG4LA2-BUdCEjaj.js → classDiagram-v2-Q7XG4LA2-DyQO35S9.js} +3 -3
  23. package/dist/component/{cose-bilkent-S5V4N54A-Dgk__ohs.js → cose-bilkent-S5V4N54A-BTN6Bz9E.js} +2 -2
  24. package/dist/component/{dagre-BM42HDAG-DGHFHFPf.js → dagre-BM42HDAG-COU3yvtF.js} +2 -2
  25. package/dist/component/{diagram-2AECGRRQ-DvGSzrwW.js → diagram-2AECGRRQ-r4LO_uyu.js} +3 -3
  26. package/dist/component/{diagram-5GNKFQAL-XebuIyjn.js → diagram-5GNKFQAL-CraSe8h_.js} +4 -4
  27. package/dist/component/{diagram-KO2AKTUF-slUL6XEO.js → diagram-KO2AKTUF-Dh-L6_aX.js} +3 -3
  28. package/dist/component/{diagram-LMA3HP47-BEPY8GOZ.js → diagram-LMA3HP47-Bv8QREaH.js} +3 -3
  29. package/dist/component/{diagram-OG6HWLK6-DHI_0aIk.js → diagram-OG6HWLK6-Jf-6CDV4.js} +4 -4
  30. package/dist/component/{erDiagram-TEJ5UH35-D-6DbJr-.js → erDiagram-TEJ5UH35-CN8X2drH.js} +5 -5
  31. package/dist/component/{flowDiagram-I6XJVG4X-Cw87OG_x.js → flowDiagram-I6XJVG4X-CloAsK8k.js} +7 -7
  32. package/dist/component/{ganttDiagram-6RSMTGT7-B47s5HUB.js → ganttDiagram-6RSMTGT7-DAlRM4jt.js} +3 -3
  33. package/dist/component/{gitGraphDiagram-PVQCEYII-DqFW3uiq.js → gitGraphDiagram-PVQCEYII-BPVdEwuf.js} +4 -4
  34. package/dist/component/{highlighted-body-OFNGDK62-CBU3EdTE.js → highlighted-body-OFNGDK62-BEd7ynwt.js} +2 -2
  35. package/dist/component/index.js +2 -2
  36. package/dist/component/{infoDiagram-5YYISTIA-_0W78xb_.js → infoDiagram-5YYISTIA-CyJi6c7l.js} +2 -2
  37. package/dist/component/{ishikawaDiagram-YF4QCWOH-CvybNkGE.js → ishikawaDiagram-YF4QCWOH-CNKCQHOP.js} +2 -2
  38. package/dist/component/{journeyDiagram-JHISSGLW-DgLrQs9G.js → journeyDiagram-JHISSGLW-DFN0WY5F.js} +5 -5
  39. package/dist/component/{kanban-definition-UN3LZRKU-ReYnw5Xl.js → kanban-definition-UN3LZRKU-BT6YDPZA.js} +3 -3
  40. package/dist/component/{linear-Cih5KQ00.js → linear-BcyQd0z0.js} +2 -2
  41. package/dist/component/{mermaid-GHXKKRXX-B6ix-Z04.js → mermaid-GHXKKRXX-DiPCfS1n.js} +28006 -27373
  42. package/dist/component/{mindmap-definition-RKZ34NQL-hRhkTdF6.js → mindmap-definition-RKZ34NQL-BND7KvId.js} +4 -4
  43. package/dist/component/{pieDiagram-4H26LBE5-DAAEUZCY.js → pieDiagram-4H26LBE5-CvDdRFnN.js} +4 -4
  44. package/dist/component/{quadrantDiagram-W4KKPZXB-Benxdf_G.js → quadrantDiagram-W4KKPZXB-DMHCLEev.js} +3 -3
  45. package/dist/component/{requirementDiagram-4Y6WPE33-BxvkbDYP.js → requirementDiagram-4Y6WPE33-BomcVKb4.js} +4 -4
  46. package/dist/component/{sankeyDiagram-5OEKKPKP-RPwwVwu3.js → sankeyDiagram-5OEKKPKP-D4rmtFCn.js} +2 -2
  47. package/dist/component/{sequenceDiagram-3UESZ5HK-C-prnAtS.js → sequenceDiagram-3UESZ5HK-C0iEDbuE.js} +4 -4
  48. package/dist/component/{stateDiagram-AJRCARHV-BEIQAx9e.js → stateDiagram-AJRCARHV-x0PonbWT.js} +3 -3
  49. package/dist/component/{stateDiagram-v2-BHNVJYJU-BD5dkY8l.js → stateDiagram-v2-BHNVJYJU-6Lq1LlSV.js} +3 -3
  50. package/dist/component/{timeline-definition-PNZ67QCA-DQJPMkfL.js → timeline-definition-PNZ67QCA-CIx8lwoQ.js} +3 -3
  51. package/dist/component/{vennDiagram-CIIHVFJN-CreOqXMc.js → vennDiagram-CIIHVFJN-B27Ya-3D.js} +2 -2
  52. package/dist/component/{wardleyDiagram-YWT4CUSO--dbQqySn.js → wardleyDiagram-YWT4CUSO-efmvFwPj.js} +3 -3
  53. package/dist/component/{xychartDiagram-2RQKCTM6-BOSkmQiX.js → xychartDiagram-2RQKCTM6-DNJmorKa.js} +3 -3
  54. package/dist/components/ai-elements/dock-link.d.ts +27 -0
  55. package/dist/components/ai-elements/link-safety-modal.d.ts +46 -1
  56. package/dist/components/ai-elements/message.d.ts +29 -2
  57. package/dist/components/ai-elements/prompt-input.d.ts +1 -1
  58. package/dist/components/embed/file-preview-kind.d.ts +6 -1
  59. package/dist/components/embed/parse-csv.d.ts +22 -5
  60. package/dist/components/embed/use-preview-bytes.d.ts +12 -0
  61. package/dist/components/embed/use-workspace-preview-file.d.ts +34 -0
  62. package/dist/components/scheduled/ScheduledDetailSurface.d.ts +2 -2
  63. package/dist/components/ui/input-group.d.ts +7 -4
  64. package/dist/embed/slash-commands.d.ts +41 -12
  65. package/dist/embed/voice/line-dispatch.d.ts +51 -16
  66. package/dist/embed/voice/line-run-reader.d.ts +32 -54
  67. package/dist/embed/voice/line-slots.d.ts +6 -2
  68. package/dist/embed/voice-call-lines.d.ts +38 -11
  69. package/dist/i18n/messages/en.d.ts +16 -5
  70. package/dist/i18n/messages/surfaces/aiElements.d.ts +3 -0
  71. package/dist/i18n/messages/surfaces/scheduled.d.ts +9 -0
  72. package/dist/i18n/messages/surfaces/stream.d.ts +31 -16
  73. package/dist/stream/chat-upload.d.ts +29 -3
  74. package/dist/stream/run-line-progress.d.ts +62 -0
  75. package/dist/stream/sandbox-output.d.ts +31 -3
  76. package/dist/stream/stream-state-store.d.ts +56 -0
  77. package/dist/stream/tool-error-copy.d.ts +4 -0
  78. package/dist/test/fetch-stubs.d.ts +16 -0
  79. package/package.json +1 -1
@@ -1,56 +1,4 @@
1
- /**
2
- * Minimal SSE consumer for a BACKGROUND line's run stream — the Operator's
3
- * ear on lines the chat surface isn't showing. Reads the AI-SDK data-stream
4
- * protocol from `GET /v1/runs/:runId/stream` and surfaces only what the call
5
- * needs: tool phases, approval-needed, the final text, errors. No `useChat`,
6
- * no rendering, no reconnect/resume (that's `BelongChatTransport`'s job for
7
- * the foreground chat surface — a background line reader just watches once).
8
- * Fetch is injected for tests.
9
- *
10
- * Wire shapes mirror the real protocol pinned in `belong-chat-transport.ts` /
11
- * `belong-data-parts.ts` — NOT the placeholder `text-delta` / `finish` shapes
12
- * from the original task brief:
13
- * - text is a DELTA part: `{ type: "text", id, text }` — accumulate `.text`.
14
- * - `data-tool-call` carries a `state` ("started" | "completed" | "failed");
15
- * only "started" is a phase change worth reporting.
16
- * - `data-approval-request` carries a `state` ("pending" | "approved" |
17
- * "rejected" | "amend_requested" | "expired"); only "pending" means an
18
- * approval is newly needed.
19
- * - `data-navigation` carries `{ routeName, params }` — a background agent
20
- * asking the HOST to navigate. The foreground renderer executes these via
21
- * `peekRouteHandler` (see `route-dispatch.ts`, the shared seam); a
22
- * background line has no renderer to pick it up, so `onNavigate` here
23
- * dispatches through that SAME seam instead. Unlike the foreground's
24
- * reload-replay (guarded by its `historical` flag + `useFireOnce`), this
25
- * reader has no reconnect/replay at all (see the module doc above) — each
26
- * SSE line is parsed and applied exactly once by the read loop below, so
27
- * no separate dedupe is needed here to keep a navigation from re-firing.
28
- * - there is no `finish` / `error` part. The terminal signal is
29
- * `data-status` with `status` ∈ "continuing" | "completed" | "cancelled" |
30
- * "failed" | "throttled". "continuing" is the max-turns auto-continue —
31
- * NOT terminal, keep reading the same stream. "completed" finishes with
32
- * the accumulated text; "cancelled" | "failed" | "throttled" are errors.
33
- *
34
- * BOOT RE-ATTACH (F3/R4): this reader is also opened for a run that already
35
- * finished before the read starts (a persisted `runId` re-attached after a
36
- * reload — see `attachLineReader` in line-dispatch.ts). Two ways that shows
37
- * up on the wire, both a CLEAN convergence rather than a failure:
38
- * - the run is still known server-side and just replays its full history
39
- * (§36 — `GET .../stream` with no cursor replays from the start): the
40
- * already-written terminal `data-status` event arrives like any other
41
- * part and is handled by the SAME "completed"/"cancelled"/"failed"
42
- * branch below — no special-casing needed, this is the ordinary path.
43
- * - the run (or its task) is truly gone server-side — 404/410 — which
44
- * reads identically to "nothing left to report", not an error: `onFinish`
45
- * fires with empty text instead of `onError`, so a boot re-attach never
46
- * paints a stale run as failed just because its record has since expired
47
- * or the session behind it was deleted.
48
- *
49
- * SSE parsing here is LINE-BY-LINE (split on "\n", not "\n\n") — the server
50
- * sends exactly one `data:` line per event. `id:` lines carry the event
51
- * cursor (ignored — v1 has no reconnect/resume) and `:`-prefixed lines are
52
- * keepalive comments.
53
- */
1
+ import { RunStatusKind } from '../../stream/belong-data-parts.ts';
54
2
  export type WatchLineRunInput = {
55
3
  readonly runId: string;
56
4
  readonly onPhase: (toolName: string) => void;
@@ -65,6 +13,36 @@ export type WatchLineRunInput = {
65
13
  mimeType: string;
66
14
  }) => void;
67
15
  readonly onFinish: (finalText: string) => void;
68
- readonly onError: (message: string, kind: "terminal" | "transport") => void;
16
+ /**
17
+ * The run did not finish cleanly. `cause` is a discriminated shape rather
18
+ * than a bare `"terminal" | "transport"` string because the two carry
19
+ * genuinely different data (§21): a TERMINAL knows exactly which non-success
20
+ * status the wire reported, and callers must be able to tell a user-pressed
21
+ * Stop (`cancelled`) from a real breakage (`failed`) — the transport already
22
+ * renders the first as an ordinary finish and only the second as an error,
23
+ * and a rail badge that disagrees is the §42 mistake. A TRANSPORT failure
24
+ * knows nothing about the run's state at all: the server task may well still
25
+ * be running.
26
+ */
27
+ readonly onError: (message: string, cause: {
28
+ readonly kind: "terminal";
29
+ readonly status: Exclude<RunStatusKind, "continuing">;
30
+ } | {
31
+ readonly kind: "transport";
32
+ }) => void;
33
+ /**
34
+ * "The stream just delivered something" — fired once per chunk read off the
35
+ * socket, INCLUDING the server's `: keepalive` comment every 15s (which
36
+ * `parseSseLine` otherwise drops). It carries no payload because its only
37
+ * job is liveness: BEL-90's progress reader is the sole listener on a
38
+ * backgrounded typed run after a reload, and the parked run cursor's
39
+ * age-out gate (`peekLiveParkedRun`) needs a re-stamp from SOMEONE or a run
40
+ * doing two minutes of silent tool work reads as dead (§41).
41
+ *
42
+ * OPTIONAL because only that one caller owns a cursor. The narration reader
43
+ * (`attachLineReader`) rehydrates from the durable slot runId, which has no
44
+ * age gate, so there is nothing for it to keep fresh.
45
+ */
46
+ readonly onTick?: () => void;
69
47
  };
70
48
  export declare const watchLineRun: (input: WatchLineRunInput, fetchFn?: typeof fetch) => (() => void);
@@ -34,8 +34,12 @@
34
34
  * streaming server-side. Back-compatible the same way `line1` is: a bag
35
35
  * written before this field existed has no `runId` key at all, and the
36
36
  * schema defaults it to `null` (nothing was in flight when that bag was
37
- * written). Line 1 never carries a meaningful value hereits `runId` is
38
- * always `null` (see `AgentLine.runId`'s doc in voice-call-lines.ts).
37
+ * written). Line 1's slot `runId` stays `null` in practicenothing dispatches
38
+ * to line 1 and `persistSlotRun` is the only writer — but do not read that as
39
+ * "line 1 has no run": the IN-MEMORY `AgentLine.runId` is no longer always null
40
+ * for line 1, since a reload seeds it from the parked run cursor (BEL-90 — see
41
+ * `AgentLine.runId`'s doc in voice-call-lines.ts). This field is the VOICE
42
+ * dispatch's durable binding specifically, not "the line's run".
39
43
  */
40
44
  /** Every line number that gets a persisted slot — all three agent lines. */
41
45
  declare const SLOT_LINES: readonly [1, 2, 3];
@@ -31,10 +31,16 @@ export type AgentLine = {
31
31
  readonly lastText: string;
32
32
  /** The line's current run, for `stop_agent` to target — bound by the
33
33
  * line-run reader once a background dispatch's POST returns a runId, and
34
- * cleared back to `null` when that run reaches a terminal state. Line 1
35
- * (foreground) never gets this written: its runId is resolved at stop-time
36
- * from the stream-state store instead (the same source the on-screen Stop
37
- * button reads), so this stays `null` for line 1 always. */
34
+ * cleared back to `null` when that run reaches a terminal state.
35
+ *
36
+ * Line 1 never has this WRITTEN by a dispatch (line-dispatch.ts never
37
+ * dispatches to it), and its stop-time runId is still resolved from the
38
+ * stream-state store — the same source the on-screen Stop button reads. It
39
+ * is no longer always `null` for line 1, though: a REHYDRATE seeds it from
40
+ * the parked run cursor on every line alike (BEL-90 — see
41
+ * `inFlightRehydration`), because a run started by typing keeps streaming
42
+ * across a reload and a chip that reads "idle" for it is a lie. Nothing
43
+ * about the stop path changed; only the reload seed did. */
38
44
  readonly runId: string | null;
39
45
  /** The live "what is it doing right now" label (e.g. "writing code"),
40
46
  * written by `setLineStep` as phase notifications arrive while `status`
@@ -114,6 +120,20 @@ export declare const lineBoundToSession: (sessionId: string) => AgentLine | unde
114
120
  * already holds must SWITCH to Agent 1, not adopt (duplicate) it onto whatever
115
121
  * background line the user happens to be viewing. */
116
122
  export declare const lineOwningSession: (sessionId: string) => AgentLine | undefined;
123
+ /**
124
+ * EVERY line bound to `sessionId` — the plural form `markSessionSeen` and
125
+ * `releaseLinesForSession` below already work in, for the same reason they do:
126
+ * more than one line can hold one session (line 1 aliases whatever the chat is
127
+ * showing, so a background line the user opened is held by both), and a legacy
128
+ * `belong:voice-line-slots` bag written before the `isBoundToAnotherLine`
129
+ * guards landed can rehydrate two background lines onto one session.
130
+ *
131
+ * `lineOwningSession` answers a different question — "which ONE agent should a
132
+ * History reopen switch to" — and stays singular. Anything that WRITES per-line
133
+ * state for a session (BEL-90's `reportRunLineProgress`) must use this one, or
134
+ * the second holder freezes at whatever it last said.
135
+ */
136
+ export declare const linesOwningSession: (sessionId: string) => readonly AgentLine[];
117
137
  /** True when `sessionId` is already the session of some line OTHER than line
118
138
  * 1 — chat.tsx's line-1 aliasing guard uses this to skip rebinding line 1
119
139
  * onto a background line's session while the user is viewing it via
@@ -209,14 +229,21 @@ export declare const bindLineRun: (line: number, runId: string | null) => void;
209
229
  * persisted slot together, so the ONE thing a page reload cannot observe by
210
230
  * itself — "is this line's run still going?" — survives the reload instead
211
231
  * of silently reverting to a lying "idle". `line-dispatch.ts`'s
212
- * `attachLineReader` is the ONE seam that calls this, shared by a live
213
- * dispatch and the boot re-attach (`reattachPendingLineRuns`) alike — same
214
- * discipline as `persistSlotBinding`'s ADOPTION seam.
232
+ * `attachLineReader` is the seam that SETS it, shared by a live dispatch and
233
+ * the boot re-attach (`reattachPendingLineRuns`) alike — same discipline as
234
+ * `persistSlotBinding`'s ADOPTION seam.
235
+ *
236
+ * There is a SECOND clearer (BEL-90): `run-line-progress.ts` calls this with
237
+ * `null` on every terminal, including for the session currently on screen.
238
+ * Without it, a background line whose run finished while the user was LOOKING
239
+ * at that line never reached `attachLineReader`'s terminal callbacks, so the
240
+ * slot kept a stale runId and every later reload rehydrated the chip as a
241
+ * permanently stuck "working" (§41).
215
242
  *
216
- * Line 1 never goes through this (line-dispatch.ts never dispatches to line
217
- * 1 — see its module doc): its `runId` stays the always-`null` value
218
- * `bindLineRun` already tolerates, and its own run is resolved at stop-time
219
- * from the stream-state store instead (`AgentLine.runId`'s doc).
243
+ * Line 1 never has this SET (line-dispatch.ts never dispatches to line 1 —
244
+ * see its module doc); its own run is resolved at stop-time from the
245
+ * stream-state store instead (`AgentLine.runId`'s doc). Clearing it for line 1
246
+ * is a harmless no-op, which is what keeps the terminal path above uniform.
220
247
  */
221
248
  export declare const persistSlotRun: (line: number, runId: string | null) => void;
222
249
  export declare const setLineStatus: (line: number, status: AgentLineStatus, lastText?: string) => void;
@@ -53,17 +53,22 @@ export declare const en: {
53
53
  readonly browserResolveResource: "Looked up a resource";
54
54
  readonly browserAwaitProcessed: "Waited for processing";
55
55
  readonly browserAssertCreated: "Checked for an existing resource";
56
+ readonly browserRefreshView: "Refreshed the page";
56
57
  readonly failedBadge: "failed";
57
58
  readonly executingTasks: "Working on it…";
58
59
  readonly workedSteps_one: "Worked · {{count}} step";
59
60
  readonly workedSteps_other: "Worked · {{count}} steps";
61
+ readonly cliAuthStop: {
62
+ readonly noGrant: "Connect to Simetrik to continue";
63
+ readonly reconnectFailed: "Couldn't connect to Simetrik";
64
+ readonly cliAuthRequired: "Reconnecting to Simetrik…";
65
+ readonly reconnecting: "Reconnecting to Simetrik…";
66
+ };
67
+ readonly reconnectingBadge: "reconnecting";
60
68
  readonly resumingAfterPause: "Taking longer than expected — picking it back up…";
61
69
  readonly open: "Open";
62
- readonly reconnectNeeded: "The {{provider}} session needs reconnecting to continue.";
63
- readonly reconnect: "Reconnect";
64
- readonly reconnecting: "Reconnecting…";
65
- readonly reconnected: "Reconnected";
66
- readonly retry: "Retry";
70
+ readonly reconnectRateLimited: "{{provider}} is busy trying again automatically in {{seconds}}s.";
71
+ readonly reconnectRateLimitedStill: "{{provider}} is still busy. We'll keep trying on our own.";
67
72
  };
68
73
  readonly stats: {
69
74
  readonly title: "Session stats";
@@ -128,6 +133,9 @@ export declare const en: {
128
133
  readonly customEmailLabel: "Custom email enabled";
129
134
  readonly customEmailDescription: "Each run reads the latest version of this template and sends an email only when its condition matches.";
130
135
  readonly customEmailTemplate: "HTML template";
136
+ readonly emailNotificationLabel: "Completion email enabled";
137
+ readonly emailRecipients: "Recipients";
138
+ readonly emailAccountRecipient: "Schedule owner's account email";
131
139
  readonly summaryOnce: "Once on {{time}}";
132
140
  readonly summaryDaily: "Daily at {{time}}";
133
141
  readonly summaryWeekdays: "Weekdays at {{time}}";
@@ -372,6 +380,7 @@ export declare const en: {
372
380
  readonly copy: "Copy";
373
381
  readonly copied: "Copied";
374
382
  readonly openExternalLink: "Open external link?";
383
+ readonly openInAppLink: "Open this page?";
375
384
  readonly openLinkDescriptionPrefix: "This will open";
376
385
  readonly openLinkDescriptionSuffix: "in a new tab.";
377
386
  readonly cancel: "Cancel";
@@ -527,6 +536,8 @@ export declare const en: {
527
536
  readonly notDelivered: "Not delivered — please resend";
528
537
  readonly runFailed: "Run failed";
529
538
  readonly uploadFailed: "Attachment upload failed — remove it or try again.";
539
+ readonly uploadRejectedType: "“{{filename}}” isn’t a file type the assistant can read — remove it. Text, data, config, code, document and image files work; archives and compiled binaries don’t.";
540
+ readonly uploadRejectedSecret: "“{{filename}}” looks like an environment/secrets file — it can’t be attached. Remove it and paste only the values you need.";
530
541
  readonly authExpired: "Your session expired — please sign in to {{name}} again, then retry.";
531
542
  readonly backendUnavailable: "Can't reach the assistant service — check your connection.";
532
543
  readonly streamError: "Stream error — {{message}}";
@@ -33,6 +33,7 @@ export declare const aiElementsEn: {
33
33
  readonly copy: "Copy";
34
34
  readonly copied: "Copied";
35
35
  readonly openExternalLink: "Open external link?";
36
+ readonly openInAppLink: "Open this page?";
36
37
  readonly openLinkDescriptionPrefix: "This will open";
37
38
  readonly openLinkDescriptionSuffix: "in a new tab.";
38
39
  readonly cancel: "Cancel";
@@ -84,6 +85,7 @@ export declare const aiElementsEs: {
84
85
  copy: string;
85
86
  copied: string;
86
87
  openExternalLink: string;
88
+ openInAppLink: string;
87
89
  openLinkDescriptionPrefix: string;
88
90
  openLinkDescriptionSuffix: string;
89
91
  cancel: string;
@@ -135,6 +137,7 @@ export declare const aiElementsPt: {
135
137
  copy: string;
136
138
  copied: string;
137
139
  openExternalLink: string;
140
+ openInAppLink: string;
138
141
  openLinkDescriptionPrefix: string;
139
142
  openLinkDescriptionSuffix: string;
140
143
  cancel: string;
@@ -26,6 +26,9 @@ export declare const scheduledEn: {
26
26
  readonly customEmailLabel: "Custom email enabled";
27
27
  readonly customEmailDescription: "Each run reads the latest version of this template and sends an email only when its condition matches.";
28
28
  readonly customEmailTemplate: "HTML template";
29
+ readonly emailNotificationLabel: "Completion email enabled";
30
+ readonly emailRecipients: "Recipients";
31
+ readonly emailAccountRecipient: "Schedule owner's account email";
29
32
  readonly summaryOnce: "Once on {{time}}";
30
33
  readonly summaryDaily: "Daily at {{time}}";
31
34
  readonly summaryWeekdays: "Weekdays at {{time}}";
@@ -120,6 +123,9 @@ export declare const scheduledEs: {
120
123
  customEmailLabel: string;
121
124
  customEmailDescription: string;
122
125
  customEmailTemplate: string;
126
+ emailNotificationLabel: string;
127
+ emailRecipients: string;
128
+ emailAccountRecipient: string;
123
129
  summaryOnce: string;
124
130
  summaryDaily: string;
125
131
  summaryWeekdays: string;
@@ -214,6 +220,9 @@ export declare const scheduledPt: {
214
220
  customEmailLabel: string;
215
221
  customEmailDescription: string;
216
222
  customEmailTemplate: string;
223
+ emailNotificationLabel: string;
224
+ emailRecipients: string;
225
+ emailAccountRecipient: string;
217
226
  summaryOnce: string;
218
227
  summaryDaily: string;
219
228
  summaryWeekdays: string;
@@ -2,7 +2,7 @@
2
2
  * Stream part-renderer strings (`src/stream/ai-sdk-part-renderers.tsx`): the
3
3
  * cards the assistant's live stream renders — approval + choice widgets, tool
4
4
  * activity tasks, browser step labels, consolidated terminal groups, artifact
5
- * cards, and the CLI reconnect chip. Status-code badges (`ok` / `no_handler` /
5
+ * cards, and the CLI reconnect note. Status-code badges (`ok` / `no_handler` /
6
6
  * wire `data.status`) are technical chrome and stay verbatim, per the same
7
7
  * rule as `embed/copy.ts`.
8
8
  */
@@ -53,17 +53,22 @@ export declare const streamEn: {
53
53
  readonly browserResolveResource: "Looked up a resource";
54
54
  readonly browserAwaitProcessed: "Waited for processing";
55
55
  readonly browserAssertCreated: "Checked for an existing resource";
56
+ readonly browserRefreshView: "Refreshed the page";
56
57
  readonly failedBadge: "failed";
57
58
  readonly executingTasks: "Working on it…";
58
59
  readonly workedSteps_one: "Worked · {{count}} step";
59
60
  readonly workedSteps_other: "Worked · {{count}} steps";
61
+ readonly cliAuthStop: {
62
+ readonly noGrant: "Connect to Simetrik to continue";
63
+ readonly reconnectFailed: "Couldn't connect to Simetrik";
64
+ readonly cliAuthRequired: "Reconnecting to Simetrik…";
65
+ readonly reconnecting: "Reconnecting to Simetrik…";
66
+ };
67
+ readonly reconnectingBadge: "reconnecting";
60
68
  readonly resumingAfterPause: "Taking longer than expected — picking it back up…";
61
69
  readonly open: "Open";
62
- readonly reconnectNeeded: "The {{provider}} session needs reconnecting to continue.";
63
- readonly reconnect: "Reconnect";
64
- readonly reconnecting: "Reconnecting…";
65
- readonly reconnected: "Reconnected";
66
- readonly retry: "Retry";
70
+ readonly reconnectRateLimited: "{{provider}} is busy trying again automatically in {{seconds}}s.";
71
+ readonly reconnectRateLimitedStill: "{{provider}} is still busy. We'll keep trying on our own.";
67
72
  };
68
73
  };
69
74
  export declare const streamEs: {
@@ -113,17 +118,22 @@ export declare const streamEs: {
113
118
  browserResolveResource: string;
114
119
  browserAwaitProcessed: string;
115
120
  browserAssertCreated: string;
121
+ browserRefreshView: string;
116
122
  failedBadge: string;
117
123
  executingTasks: string;
118
124
  workedSteps_one: string;
119
125
  workedSteps_other: string;
126
+ cliAuthStop: {
127
+ noGrant: string;
128
+ reconnectFailed: string;
129
+ cliAuthRequired: string;
130
+ reconnecting: string;
131
+ };
132
+ reconnectingBadge: string;
120
133
  resumingAfterPause: string;
121
134
  open: string;
122
- reconnectNeeded: string;
123
- reconnect: string;
124
- reconnecting: string;
125
- reconnected: string;
126
- retry: string;
135
+ reconnectRateLimited: string;
136
+ reconnectRateLimitedStill: string;
127
137
  };
128
138
  };
129
139
  export declare const streamPt: {
@@ -173,16 +183,21 @@ export declare const streamPt: {
173
183
  browserResolveResource: string;
174
184
  browserAwaitProcessed: string;
175
185
  browserAssertCreated: string;
186
+ browserRefreshView: string;
176
187
  failedBadge: string;
177
188
  executingTasks: string;
178
189
  workedSteps_one: string;
179
190
  workedSteps_other: string;
191
+ cliAuthStop: {
192
+ noGrant: string;
193
+ reconnectFailed: string;
194
+ cliAuthRequired: string;
195
+ reconnecting: string;
196
+ };
197
+ reconnectingBadge: string;
180
198
  resumingAfterPause: string;
181
199
  open: string;
182
- reconnectNeeded: string;
183
- reconnect: string;
184
- reconnecting: string;
185
- reconnected: string;
186
- retry: string;
200
+ reconnectRateLimited: string;
201
+ reconnectRateLimitedStill: string;
187
202
  };
188
203
  };
@@ -1,8 +1,34 @@
1
1
  import { FileUIPart } from 'ai';
2
- /** Normalise to the server's allow-list by extension (browsers report .md/.csv inconsistently). */
2
+ /**
3
+ * Normalise to the server's allow-list by extension (browsers report .md/.csv/
4
+ * .yaml inconsistently). An unknown extension keeps whatever the browser said,
5
+ * and an unknown extension with NO browser type reports `application/octet-
6
+ * stream` — deliberately NOT `text/plain`, which used to smuggle `.pyc`/`.pkl`
7
+ * and other typeless binaries onto the text allow-list where they were inlined
8
+ * to the model as mojibake. The server refuses those with a named reason.
9
+ */
3
10
  export declare const resolveMimeType: (file: File) => string;
4
- /** Upload one file to the server, returning its fileId (or null on failure). */
5
- export declare const uploadOneFile: (apiBaseUrl: string, file: File) => Promise<string | null>;
11
+ /** Why an attachment upload failed — a named reason, not a bare null (§19/§20). */
12
+ export type AttachmentUploadReason = "workspace_scope_missing" | "secrets_by_convention" | "unsupported_file_type" | "upload_failed";
13
+ /**
14
+ * A failed attachment upload, carrying the filename + reason so the composer can
15
+ * tell the user WHICH file was refused and WHY instead of a generic banner
16
+ * (007-plan C6 — a silent/anonymous upload failure is the known defect class).
17
+ */
18
+ export declare class AttachmentUploadError extends Error {
19
+ readonly filename: string;
20
+ readonly reason: AttachmentUploadReason;
21
+ constructor(filename: string, reason: AttachmentUploadReason);
22
+ }
23
+ export type UploadOneFileResult = {
24
+ readonly ok: true;
25
+ readonly fileId: string;
26
+ } | {
27
+ readonly ok: false;
28
+ readonly reason: AttachmentUploadReason;
29
+ };
30
+ /** Upload one file to the server, returning its fileId or a named refusal. */
31
+ export declare const uploadOneFile: (apiBaseUrl: string, file: File) => Promise<UploadOneFileResult>;
6
32
  /**
7
33
  * Bridge the PromptInput's `file` parts (data-url FileUIParts) to the
8
34
  * upload-first wire: upload each, in order, and return the server fileIds.
@@ -0,0 +1,62 @@
1
+ import { RunStatusKind } from './belong-data-parts.ts';
2
+ /**
3
+ * What a run reported, reduced to the only four things a rail chip can show.
4
+ *
5
+ * `activity` is the tool-LESS turn's path to a truthful "working" badge: a
6
+ * turn that only writes text calls no tools, so `tool_started` never fires and
7
+ * the chip would otherwise sit at "idle" through the whole answer.
8
+ *
9
+ * There is deliberately NO `text` variant. An accumulator feeding
10
+ * `AgentLine.lastText` was specified and dropped: nothing in the dock renders
11
+ * `lastText`, and because this seam no-ops while the session is on screen the
12
+ * accumulator could only ever have collected the post-switch TAIL of an
13
+ * answer. Re-add it when something renders it.
14
+ *
15
+ * `terminal` carries the run's OWN id, and it is not decoration: the durable
16
+ * slot runId this seam clears is shared with the voice dispatch, and a line can
17
+ * hold a live voice run at the same moment a DIFFERENT (typed) run on the same
18
+ * session finishes. Clearing by session alone unbinds `stop_agent`'s target for
19
+ * a run that is still going. `status` is spelled from the wire union rather
20
+ * than by hand so a new `RunStatusKind` fails CI here (§21).
21
+ */
22
+ export type RunLineProgressEvent = {
23
+ readonly kind: "activity";
24
+ } | {
25
+ readonly kind: "tool_started";
26
+ readonly toolName: string;
27
+ } | {
28
+ readonly kind: "approval_pending";
29
+ } | {
30
+ readonly kind: "terminal";
31
+ readonly runId: string;
32
+ readonly status: Exclude<RunStatusKind, "continuing">;
33
+ };
34
+ /**
35
+ * Report one run event for `sessionId`. Silently does nothing when no agent
36
+ * line owns that session.
37
+ *
38
+ * Two writes with two different scopes, in this order:
39
+ *
40
+ * 1. A TERMINAL clears the line's durable slot runId — including for the
41
+ * session on screen — but ONLY when the slot still names the run that just
42
+ * ended. `attachLineReader`'s terminal callbacks used to be the only code
43
+ * in the tree that ever cleared it, so a background line's run that
44
+ * finished while the user was LOOKING at that line left a stale runId
45
+ * behind, and every later reload rehydrated the chip as a permanently
46
+ * stuck "working" (§41 violation). The id check is what keeps that fix
47
+ * from becoming its own bug: the slot is also `stop_agent`'s target, and a
48
+ * line can hold a LIVE voice run at the moment an older typed run on the
49
+ * same session terminates. Clearing that would leave a running agent
50
+ * unstoppable and its chip claiming "done" mid-run.
51
+ * 2. The BADGE writes are skipped for the session on screen. That line's
52
+ * status is chat.tsx's exclusive business — it is mounted, it has the real
53
+ * stream status, and it drives the call narration. Leaving the foreground
54
+ * byte-for-byte unchanged is what makes this seam safe to add.
55
+ *
56
+ * Applied to EVERY line bound to the session, not the first match — same
57
+ * reasoning as `markSessionSeen` / `releaseLinesForSession`, which have always
58
+ * iterated: line 1 aliases whatever the chat is showing, so a background line
59
+ * the user opened is held by both, and a legacy slots bag can rehydrate two
60
+ * lines onto one session. Writing only the first left the other frozen.
61
+ */
62
+ export declare const reportRunLineProgress: (sessionId: string, event: RunLineProgressEvent) => void;
@@ -1,7 +1,23 @@
1
- import { SandboxCommandData, ToolCallData } from './belong-data-parts.ts';
1
+ import { SandboxCommandData, ToolCallData, ToolErrorSeverity } from './belong-data-parts.ts';
2
2
  /** The shell tools across runtimes; both carry `input.command` + `output.exitCode`. */
3
3
  export declare const SHELL_TOOL_NAMES: ReadonlySet<string>;
4
4
  export declare const isShellToolName: (toolName: string) => boolean;
5
+ /**
6
+ * The ONE place that decides whether a wire severity means "SURFACES on the
7
+ * collapsed activity line".
8
+ *
9
+ * It used to mean "red". BEL-96 dropped red from the execution surfaces
10
+ * entirely — every failure now reads orange, stopping or not — so severity no
11
+ * longer picks a colour. What it still picks is whether the user is shown the
12
+ * failure at all: an ordinary one stays folded inside "Worked · N steps", a
13
+ * stopping one is lifted onto the collapsed line.
14
+ *
15
+ * Every consumer used to be a bare `=== "stop"`, so a third member added to
16
+ * `ToolErrorSeverity` would have compiled clean everywhere and silently
17
+ * rendered as non-stop. `.exhaustive()` turns that into a CI failure at this
18
+ * single site.
19
+ */
20
+ export declare const isStoppingSeverity: (severity: ToolErrorSeverity | undefined) => boolean;
5
21
  /**
6
22
  * BEL-97 — how a finished shell command's outcome should be coloured.
7
23
  *
@@ -9,12 +25,24 @@ export declare const isShellToolName: (toolName: string) => boolean;
9
25
  * failed with exit 0 (successful execution despite wrapper error),
10
26
  * or timed out (still running in the sandbox).
11
27
  * - `"notice"` — an ORDINARY failure (non-zero exit, or a tool error with no
12
- * stop-severity). Orange, NOT red: the agent kept going.
28
+ * stop-severity). Orange, and folded away.
13
29
  * - `"stop"` — an agent-execution-STOPPING failure (a denied permission, an
14
- * aborting error: `error.severity === "stop"`). Red.
30
+ * aborting error: `error.severity === "stop"`). Also orange
31
+ * since BEL-96 — what sets it apart is that it SURFACES on
32
+ * the collapsed line, not that it is a different colour.
15
33
  *
16
34
  * One source of truth shared by `TerminalTask` (its status badge) and
17
35
  * `ActivityGroup` (its aggregate failure badge).
36
+ *
37
+ * BEL-96 — ORDER MATTERS, and the two observable-outcome guards go FIRST.
38
+ * §42 words both as absolutes ("`exit 0` is ALWAYS a success regardless of wire
39
+ * state"; a timed-out command "reads 'still running', not 'failed'"), and a
40
+ * `severity` check ahead of them defeated both: `exit0 + stop` measured "stop",
41
+ * `timeout + stop` measured "stop". That was unreachable while nothing on the
42
+ * server emitted a stop severity, but `tool-executor.ts` now forwards
43
+ * `error.severity` for ANY registry tool including `run_shell`. Observable
44
+ * outcome beats declared state — the same rule the exit-0 guard already
45
+ * encoded, now applied consistently.
18
46
  */
19
47
  export type TerminalSeverity = "none" | "notice" | "stop";
20
48
  export declare const terminalCommandSeverity: (command: ToolCallData) => TerminalSeverity;
@@ -92,6 +92,62 @@ export declare const clearStreamState: (workspaceId?: string | null) => void;
92
92
  * message overwrites the cursor via {@link writeStreamState}.
93
93
  */
94
94
  export declare const readResumableStreamState: (workspaceId?: string | null) => PersistedStreamState | null;
95
+ /**
96
+ * Was a run still in flight for `sessionId` when this page last looked — the
97
+ * ONE query behind "a reload must not lie about a backgrounded agent"
98
+ * (BEL-90 / §41). The parked-cursor map already carries exactly this truth
99
+ * for every session this tab drove: it lives in `sessionStorage`, so it
100
+ * survives a genuine reload and is correctly absent in a brand-new tab, which
101
+ * owns no reader for the run.
102
+ *
103
+ * Gated on the same {@link RESUME_MAX_AGE_MS} window {@link setActiveSessionId}
104
+ * and {@link readResumableStreamState} use, so a run abandoned by a worker
105
+ * restart ages out into `null` instead of pinning a badge at "working"
106
+ * forever. Non-throwing and null-returning like its neighbours.
107
+ *
108
+ * That gate is only honest while SOMETHING re-stamps the cursor for a live but
109
+ * silent run — see {@link touchParkedRun}. The transport does it from its
110
+ * `: keepalive` handler; a reader that adopts a run WITHOUT a transport (the
111
+ * boot re-attach in `line-dispatch.ts`) must do it too, or a genuinely
112
+ * streaming run reads "idle" on the next reload.
113
+ *
114
+ * NOTE the boundary this does NOT cover: a line's DURABLE slot runId
115
+ * (`line-slots.ts`) is written by voice dispatch and is not consulted here,
116
+ * so a slot runId left behind by a reader torn down mid-flight
117
+ * (`abortAllLineReaders`) bypasses this freshness gate entirely — that path is
118
+ * `resetForCallStart`'s to clean up, not this one's.
119
+ */
120
+ export declare const peekLiveParkedRun: (sessionId: string) => string | null;
121
+ /**
122
+ * Prove `runId` is still alive: re-stamp `sessionId`'s parked cursor's
123
+ * `updatedAt` so the {@link RESUME_MAX_AGE_MS} gate in
124
+ * {@link peekLiveParkedRun} keeps returning it (BEL-90 / §41).
125
+ *
126
+ * This is the SAME job the transport's `onKeepalive` → `#persist()` does, made
127
+ * callable by the one reader that has no transport behind it: after a reload
128
+ * `line-dispatch.ts`'s progress reader is the only listener on a backgrounded
129
+ * typed run's stream, so without this a run doing 2 minutes of silent tool work
130
+ * ages out of the cursor map and the next reload paints its chip "idle" while
131
+ * it is still streaming.
132
+ *
133
+ * Deliberately touches ONLY the parked map, never the selected pointer: the
134
+ * caller attaches exclusively to sessions that are NOT on screen, and moving
135
+ * the selection would hijack the user's chat. No-op unless the cursor still
136
+ * names `runId` — a cursor that moved on belongs to a newer run, and the
137
+ * caller has no claim on it.
138
+ */
139
+ export declare const touchParkedRun: (sessionId: string, runId: string) => void;
140
+ /**
141
+ * `runId` reached a terminal state — drop `sessionId`'s parked cursor so
142
+ * nothing rehydrates the line as "working" for a run that is already done
143
+ * (BEL-90 / §41, the mirror of {@link touchParkedRun}).
144
+ *
145
+ * The transport gets this for free from `onStreamTerminal` → `#persist()` with
146
+ * a null `#runId`; the progress reader has no transport, so it calls this. Same
147
+ * ownership guard: a cursor naming a DIFFERENT run is someone else's live run
148
+ * and is left alone.
149
+ */
150
+ export declare const releaseParkedRun: (sessionId: string, runId: string) => void;
95
151
  /**
96
152
  * Select one Session (sessions menu switch / new). A Session with a live cursor
97
153
  * parked by this tab restores that cursor so the next ChatRoute mount resumes
@@ -0,0 +1,4 @@
1
+ import { TFunction } from 'i18next';
2
+ import { ToolCallData } from './belong-data-parts.ts';
3
+ /** Localised copy for a tool error, or the server's message when unmapped. */
4
+ export declare const toolErrorText: (t: TFunction, error: ToolCallData["error"]) => string | undefined;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared fetch-stub bodies for the preview surfaces.
3
+ *
4
+ * `usePreviewBytes` reads a text preview as BYTES (`res.arrayBuffer()`) rather
5
+ * than `res.text()`, so it can fall back to windows-1252 when a file isn't
6
+ * valid UTF-8 — a stub that only implements `text()` therefore no longer models
7
+ * a Response the hook can consume. `textResponseStub` implements both, so a
8
+ * test can stay agnostic about which one the code under test reaches for.
9
+ */
10
+ export type TextResponseStub = {
11
+ readonly ok: true;
12
+ readonly status: 200;
13
+ readonly text: () => Promise<string>;
14
+ readonly arrayBuffer: () => Promise<ArrayBuffer>;
15
+ };
16
+ export declare const textResponseStub: (text: string) => TextResponseStub;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@belonguniverseai/react-sdk",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Belong — embeddable React AI assistant dock. Mount <BelongWidget> in your app tree with getToken dependency-injection auth.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",