@belonguniverseai/react-sdk 0.6.20 → 0.6.22

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 (177) hide show
  1. package/README.md +69 -0
  2. package/dist/api/client.d.ts +784 -43
  3. package/dist/api/goals.d.ts +179 -0
  4. package/dist/api/issue-reports.d.ts +13 -0
  5. package/dist/api/query-keys.d.ts +18 -0
  6. package/dist/api/questions.d.ts +2 -6
  7. package/dist/api/report-client-error.d.ts +3 -0
  8. package/dist/api/retry-fetch.d.ts +19 -0
  9. package/dist/api/skill-library.d.ts +100 -0
  10. package/dist/api/skills.d.ts +3 -1
  11. package/dist/app/dock-outlet.d.ts +15 -5
  12. package/dist/browser/allowed-ops.d.ts +18 -0
  13. package/dist/browser/host-mcp.d.ts +31 -0
  14. package/dist/component/{abnfDiagram-N423BO3Z-BhpvofGI.js → abnfDiagram-N423BO3Z-D4mVRMw9.js} +4 -4
  15. package/dist/component/{arc-BDj2q5CZ.js → arc-GBp6w3tF.js} +2 -2
  16. package/dist/component/{architectureDiagram-T3A2C74G-CWQqfm2n.js → architectureDiagram-T3A2C74G-Cxm-USYQ.js} +3 -3
  17. package/dist/component/{blockDiagram-VBNYF7ZC-DegT976u.js → blockDiagram-VBNYF7ZC-BAiLpxJf.js} +4 -4
  18. package/dist/component/{c4Diagram-5PPSVZJV-CSnfnID_.js → c4Diagram-5PPSVZJV-Sy6sLJOj.js} +3 -3
  19. package/dist/component/{channel-CUQQeqtV.js → channel-DPbaAkCI.js} +2 -2
  20. package/dist/component/{chunk-2GRJ4B5K-BmCkMXuE.js → chunk-2GRJ4B5K-8r8YJzCc.js} +2 -2
  21. package/dist/component/{chunk-2Q5K7J3B-ACFPsFU_.js → chunk-2Q5K7J3B-RZqny7-R.js} +2 -2
  22. package/dist/component/{chunk-5RXB4S5H-Bu-wqNe4.js → chunk-5RXB4S5H-QFCY9Dtl.js} +5 -5
  23. package/dist/component/{chunk-5VM5RSS4-DDzdObQ5.js → chunk-5VM5RSS4-BG8q5ODa.js} +2 -2
  24. package/dist/component/{chunk-6Q2QTUOP-35aGDtbe.js → chunk-6Q2QTUOP-D6ec2nI7.js} +2 -2
  25. package/dist/component/{chunk-GF5L2VYU-CBooNwOJ.js → chunk-GF5L2VYU-D7yROYf0.js} +6 -6
  26. package/dist/component/{chunk-JWPE2WC7-CZN_bZMl.js → chunk-JWPE2WC7-CmGlHqne.js} +2 -2
  27. package/dist/component/{chunk-KBJHAD2P-Cj08hUgE.js → chunk-KBJHAD2P-W03s7swc.js} +2 -2
  28. package/dist/component/{chunk-RYQCIY6F-CC4FyEwj.js → chunk-RYQCIY6F-D0oyMUJ7.js} +2 -2
  29. package/dist/component/{chunk-XXDRQBXY-CJW3AYPF.js → chunk-XXDRQBXY-D4s0eyp4.js} +2 -2
  30. package/dist/component/{classDiagram-JCYQIIEL-LpH5TtI1.js → classDiagram-JCYQIIEL-DwNAC2XG.js} +3 -3
  31. package/dist/component/{classDiagram-v2-OCEON4UE-LpH5TtI1.js → classDiagram-v2-OCEON4UE-DwNAC2XG.js} +3 -3
  32. package/dist/component/{cose-bilkent-JH36ORCC-h8wo2OH9.js → cose-bilkent-JH36ORCC-DhGvXDdi.js} +2 -2
  33. package/dist/component/{cynefinDiagram-MW4NZA55-DuieCsNY.js → cynefinDiagram-MW4NZA55-BCUeX7pB.js} +3 -3
  34. package/dist/component/{dagre-VZM6K2ZE-DqMvwkom.js → dagre-VZM6K2ZE-DWofHoga.js} +3 -3
  35. package/dist/component/{diagram-7IWD3JNH-BThr9-R3.js → diagram-7IWD3JNH-DjCFF_Dr.js} +4 -4
  36. package/dist/component/{diagram-B4RE2ZJO-rjoRYPDB.js → diagram-B4RE2ZJO-CSovrpFW.js} +3 -3
  37. package/dist/component/{diagram-LBJQPF4R-C41ccwxG.js → diagram-LBJQPF4R-Cgpae-sR.js} +3 -3
  38. package/dist/component/{diagram-Q27KOJAE-DPN9msRq.js → diagram-Q27KOJAE-DikMPfLU.js} +4 -4
  39. package/dist/component/{diagram-UB23O5K3-DSjf5_Nk.js → diagram-UB23O5K3-sQU6rZbO.js} +3 -3
  40. package/dist/component/{ebnfDiagram-BXEA7PRR-vPL9oktk.js → ebnfDiagram-BXEA7PRR-B9t8-G8R.js} +4 -4
  41. package/dist/component/{erDiagram-JOGREHBK-Kc3mZe4M.js → erDiagram-JOGREHBK-DKg1HhSl.js} +5 -5
  42. package/dist/component/{flowDiagram-UKHOOZJN-2aUAXRZU.js → flowDiagram-UKHOOZJN-BO8jpT1D.js} +7 -7
  43. package/dist/component/{ganttDiagram-PKOTCBZU-DMZtqJSP.js → ganttDiagram-PKOTCBZU-BPXYyB9Q.js} +3 -3
  44. package/dist/component/{gitGraphDiagram-DS77QQ5N-E77l0Z1u.js → gitGraphDiagram-DS77QQ5N-BjVLFBUS.js} +4 -4
  45. package/dist/component/{highlighted-body-OFNGDK62-C1DkgW7U.js → highlighted-body-OFNGDK62-BKot66c3.js} +2 -2
  46. package/dist/component/{index-Dips44uG.js → index-i49DPVb4.js} +960 -464
  47. package/dist/component/index.js +2 -2
  48. package/dist/component/{infoDiagram-6WML65LV-CpvnNYd0.js → infoDiagram-6WML65LV-BkAC5Bwv.js} +2 -2
  49. package/dist/component/{ishikawaDiagram-WSZJBQD7-B1q_lFLX.js → ishikawaDiagram-WSZJBQD7-oplkIs_t.js} +2 -2
  50. package/dist/component/{journeyDiagram-NVQOT4AX-6uEmqBOC.js → journeyDiagram-NVQOT4AX-CuliMCSv.js} +5 -5
  51. package/dist/component/{kanban-definition-27J2QSJJ-C2QioWX9.js → kanban-definition-27J2QSJJ-DdzOdEiL.js} +3 -3
  52. package/dist/component/{linear-BT5VqKju.js → linear-zZDwdGmK.js} +2 -2
  53. package/dist/component/{mermaid-GHXKKRXX-C8vyJd7j.js → mermaid-GHXKKRXX-aX5umm-y.js} +49795 -42603
  54. package/dist/component/{mindmap-definition-FAOFIHXS-D9m5nI-X.js → mindmap-definition-FAOFIHXS-DD2HF9oE.js} +4 -4
  55. package/dist/component/{pegDiagram-VL7TDLO6-C6Voiy3J.js → pegDiagram-VL7TDLO6-DyiDl9bF.js} +4 -4
  56. package/dist/component/{pieDiagram-7S7Q4E2Y-sqStHf6X.js → pieDiagram-7S7Q4E2Y-DflxKS_B.js} +4 -4
  57. package/dist/component/{quadrantDiagram-CIZ2JOQS-J4smKtsQ.js → quadrantDiagram-CIZ2JOQS-nwwayVOP.js} +3 -3
  58. package/dist/component/{railroadDiagram-AXF67PYL-DWSgdnlw.js → railroadDiagram-AXF67PYL-BTtD2eN9.js} +4 -4
  59. package/dist/component/{requirementDiagram-LRYGKXZP-CD2psvke.js → requirementDiagram-LRYGKXZP-B37q1LKL.js} +4 -4
  60. package/dist/component/{sankeyDiagram-W5VNT64P-BTctWXxs.js → sankeyDiagram-W5VNT64P-zY0W9eje.js} +2 -2
  61. package/dist/component/{sequenceDiagram-SI44F4Z6-BSr4jTRw.js → sequenceDiagram-SI44F4Z6-CmQiqB8F.js} +4 -4
  62. package/dist/component/{sizeCapture-X5ZJPWSS-DDKZl6MR.js → sizeCapture-X5ZJPWSS-dF3FvIT8.js} +2 -2
  63. package/dist/component/snapdom-DkzCA8S0.js +7061 -0
  64. package/dist/component/{stateDiagram-OKZ733FA-DpICd9vK.js → stateDiagram-OKZ733FA-CX61F3L0.js} +3 -3
  65. package/dist/component/{stateDiagram-v2-UEYNNEHI-CDWkukZc.js → stateDiagram-v2-UEYNNEHI-DnFZhhu3.js} +3 -3
  66. package/dist/component/{swimlanes-SLNWSIFB-C2qUEfbN.js → swimlanes-SLNWSIFB-CX9XGBvw.js} +4 -4
  67. package/dist/component/{swimlanesDiagram-ULZ7WXOC-BATYiMa7.js → swimlanesDiagram-ULZ7WXOC-Bqkgx7qk.js} +3 -3
  68. package/dist/component/{timeline-definition-Z64GVDOM-BANf8HvQ.js → timeline-definition-Z64GVDOM-BUX4OSm1.js} +3 -3
  69. package/dist/component/{vennDiagram-T6HMQDX7-ByIwO7x1.js → vennDiagram-T6HMQDX7-BpVOq4Rh.js} +2 -2
  70. package/dist/component/{wardleyDiagram-T6FBY63Y-5_6KGTWP.js → wardleyDiagram-T6FBY63Y-BUDGa8Mv.js} +3 -3
  71. package/dist/component/{xychartDiagram-ELKLHX3M-KS7oMXi0.js → xychartDiagram-ELKLHX3M-FIA_LnIu.js} +3 -3
  72. package/dist/components/embed/EmbedComposer.d.ts +11 -1
  73. package/dist/components/embed/EmbedHeader.d.ts +7 -1
  74. package/dist/components/embed/EmbedRail.d.ts +18 -2
  75. package/dist/components/embed/GoalActions.d.ts +15 -0
  76. package/dist/components/embed/GoalBadge.d.ts +17 -0
  77. package/dist/components/embed/GoalClearDialog.d.ts +6 -0
  78. package/dist/components/embed/GoalClearedLine.d.ts +3 -0
  79. package/dist/components/embed/GoalCompletionCard.d.ts +12 -0
  80. package/dist/components/embed/GoalComposeSheet.d.ts +19 -0
  81. package/dist/components/embed/GoalProposalCard.d.ts +29 -0
  82. package/dist/components/embed/GoalRailView.d.ts +15 -0
  83. package/dist/components/embed/GoalRefusalCard.d.ts +22 -0
  84. package/dist/components/embed/GoalSetCard.d.ts +5 -0
  85. package/dist/components/embed/GoalSnoozeMenu.d.ts +14 -0
  86. package/dist/components/embed/GoalStatusRow.d.ts +6 -0
  87. package/dist/components/embed/GoalTimeline.d.ts +6 -0
  88. package/dist/components/embed/MobileNavBar.d.ts +5 -1
  89. package/dist/components/embed/QuestionCard.d.ts +12 -1
  90. package/dist/components/embed/ScheduleNotices.d.ts +23 -0
  91. package/dist/components/embed/SentAsGoalTag.d.ts +1 -0
  92. package/dist/components/embed/SubagentsPaneBody.d.ts +3 -0
  93. package/dist/components/embed/rail-flags.d.ts +13 -0
  94. package/dist/components/feedback/IssueReportController.d.ts +1 -0
  95. package/dist/components/feedback/IssueReportDialog.d.ts +6 -0
  96. package/dist/components/skills/ConfirmDeleteSkillDialog.d.ts +9 -0
  97. package/dist/components/skills/SkillLibraryToolSync.d.ts +4 -0
  98. package/dist/components/skills/SkillsListSurface.d.ts +26 -0
  99. package/dist/components/skills/UploadSkillDialog.d.ts +32 -0
  100. package/dist/components/skills/skill-archive-file.d.ts +15 -0
  101. package/dist/components/ui/badge.d.ts +1 -1
  102. package/dist/embed/client-error-log.d.ts +72 -0
  103. package/dist/embed/copy.d.ts +1 -0
  104. package/dist/embed/dev-mode.d.ts +5 -7
  105. package/dist/embed/dock-open-request.d.ts +32 -0
  106. package/dist/embed/dock-state.d.ts +1 -1
  107. package/dist/embed/goal-actions-model.d.ts +9 -0
  108. package/dist/embed/goal-cap-notice.d.ts +36 -0
  109. package/dist/embed/goal-deep-link.d.ts +24 -0
  110. package/dist/embed/goal-edit-command.d.ts +25 -0
  111. package/dist/embed/goal-journal.d.ts +53 -0
  112. package/dist/embed/goal-live-status.d.ts +2 -0
  113. package/dist/embed/goal-plain-units.d.ts +35 -0
  114. package/dist/embed/goal-progress.d.ts +15 -0
  115. package/dist/embed/goal-proposal-dismissal.d.ts +11 -0
  116. package/dist/embed/goal-resume-time.d.ts +22 -0
  117. package/dist/embed/goal-row-model.d.ts +27 -0
  118. package/dist/embed/goal-runway.d.ts +5 -0
  119. package/dist/embed/goal-schedule-handoff.d.ts +12 -0
  120. package/dist/embed/goal-status-label.d.ts +3 -0
  121. package/dist/embed/goal-time-usage.d.ts +19 -0
  122. package/dist/embed/issue-report-bus.d.ts +14 -0
  123. package/dist/embed/issue-report-open.d.ts +8 -0
  124. package/dist/embed/issue-report.d.ts +41 -0
  125. package/dist/embed/locale.d.ts +1 -1
  126. package/dist/embed/screen-capture.d.ts +74 -0
  127. package/dist/embed/slash-commands.d.ts +24 -0
  128. package/dist/embed/task-schedule-command.d.ts +7 -0
  129. package/dist/embed/voice/line-run-reader.d.ts +8 -0
  130. package/dist/embed/voice-call.d.ts +4 -0
  131. package/dist/i18n/messages/en.d.ts +385 -13
  132. package/dist/i18n/messages/it.d.ts +3 -0
  133. package/dist/i18n/messages/surfaces/aiElements.d.ts +52 -0
  134. package/dist/i18n/messages/surfaces/app.d.ts +18 -0
  135. package/dist/i18n/messages/surfaces/connectors.d.ts +235 -0
  136. package/dist/i18n/messages/surfaces/embedMisc.d.ts +45 -0
  137. package/dist/i18n/messages/surfaces/feedback.d.ts +93 -0
  138. package/dist/i18n/messages/surfaces/files.d.ts +37 -0
  139. package/dist/i18n/messages/surfaces/header.d.ts +44 -0
  140. package/dist/i18n/messages/surfaces/login.d.ts +8 -0
  141. package/dist/i18n/messages/surfaces/plan.d.ts +17 -0
  142. package/dist/i18n/messages/surfaces/prompts.d.ts +43 -0
  143. package/dist/i18n/messages/surfaces/scheduled.d.ts +108 -0
  144. package/dist/i18n/messages/surfaces/sessions.d.ts +23 -0
  145. package/dist/i18n/messages/surfaces/skills.d.ts +216 -0
  146. package/dist/i18n/messages/surfaces/stats.d.ts +18 -0
  147. package/dist/i18n/messages/surfaces/stream.d.ts +290 -27
  148. package/dist/i18n/messages/surfaces/usage.d.ts +26 -0
  149. package/dist/i18n/messages/surfaces/workspace.d.ts +30 -0
  150. package/dist/public-api.d.ts +1 -1
  151. package/dist/routes/chat/part-renderer-extra-props.d.ts +43 -0
  152. package/dist/routes/chat/use-goal-receipt.d.ts +37 -0
  153. package/dist/routes/chat/use-host-draft-request.d.ts +2 -0
  154. package/dist/routes/chat/use-session-goal.d.ts +226 -0
  155. package/dist/routes/chat.d.ts +10 -1
  156. package/dist/routes/goal.d.ts +1 -0
  157. package/dist/routes/skills.d.ts +2 -0
  158. package/dist/stream/activity-items.d.ts +25 -0
  159. package/dist/stream/ai-sdk-part-renderers.d.ts +70 -25
  160. package/dist/stream/belong-chat-transport.d.ts +147 -2
  161. package/dist/stream/belong-data-parts.d.ts +55 -7
  162. package/dist/stream/goal-store.d.ts +43 -0
  163. package/dist/stream/message-continuation.d.ts +29 -0
  164. package/dist/stream/message-metadata.d.ts +1 -0
  165. package/dist/stream/message-parts.d.ts +42 -0
  166. package/dist/stream/run-line-progress.d.ts +1 -0
  167. package/dist/stream/select-goal-set-part-id.d.ts +2 -0
  168. package/dist/stream/select-latest-goal.d.ts +13 -0
  169. package/dist/stream/subagent-controls-store.d.ts +20 -0
  170. package/dist/stream/subagent-lanes-model.d.ts +175 -0
  171. package/dist/stream/subagent-lanes.d.ts +89 -0
  172. package/dist/stream/use-publish-subagent-controls.d.ts +11 -0
  173. package/dist/stream/workspace-focus-store.d.ts +7 -1
  174. package/dist/test/render.d.ts +18 -1
  175. package/dist/test/subagent-fixtures.d.ts +24 -0
  176. package/dist/types/public.d.ts +55 -2
  177. package/package.json +1 -1
@@ -0,0 +1,72 @@
1
+ /**
2
+ * A bounded tail of what already went wrong in this browser tab, so an issue
3
+ * report can carry it (issue-report-open.ts reads this).
4
+ *
5
+ * This is the ONE input to a report that is unrecoverable after the fact: a
6
+ * render that threw or a 500 on a dock route leaves no server-side trace tied
7
+ * to the user's complaint.
8
+ *
9
+ * Deliberately narrow: kind, a truncated message, and for a request failure
10
+ * the route and status. We never read a response body into this buffer
11
+ * ourselves — our own failures (`reportClientError`'s call sites) record a
12
+ * route and a status code, never the body. But the two global listeners
13
+ * below capture host-authored error text verbatim, and a host application
14
+ * can legally do `throw new Error(await response.text())` — at that point
15
+ * the "message" a window `error`/`unhandledrejection` event carries IS a
16
+ * response body, word for word. There is no way to strip that out without
17
+ * inspecting arbitrary Error objects we cannot parse, so the mitigation is a
18
+ * hard cap: host-originated text gets
19
+ * `MAX_HOST_CLIENT_ERROR_MESSAGE_CHARS` (120), well short of a useful
20
+ * response body, while our own controlled messages keep the full
21
+ * `MAX_CLIENT_ERROR_MESSAGE_CHARS` (500). See `recordClientError` vs.
22
+ * `recordHostClientError` below.
23
+ *
24
+ * Module-level store in the same grain as approval-bus.ts: one per page, no
25
+ * React, readable from anywhere in the dock.
26
+ */
27
+ export declare const MAX_CLIENT_ERROR_ENTRIES = 20;
28
+ /**
29
+ * Cap for a message recorded through the CONTROLLED path — our own
30
+ * `reportClientError` call sites (`api/report-client-error.ts`). We author
31
+ * that text ourselves (a fixed set of transport-failure kinds), so it is
32
+ * known-safe at the full length.
33
+ */
34
+ export declare const MAX_CLIENT_ERROR_MESSAGE_CHARS = 500;
35
+ /**
36
+ * Cap for a message recorded through the UNCONTROLLED path — the global
37
+ * `window` `error` / `unhandledrejection` listeners below. That text is
38
+ * authored by the HOST PAGE, not by us: a host that does
39
+ * `throw new Error(await response.text())` hands its rejection message
40
+ * straight to `onRejection`, and without a hard cap a response body would
41
+ * ride into the next issue report intact. Kept far below the controlled cap
42
+ * on purpose — this is a privacy boundary, not a display truncation.
43
+ */
44
+ export declare const MAX_HOST_CLIENT_ERROR_MESSAGE_CHARS = 120;
45
+ export type ClientErrorEntry = {
46
+ readonly at: string;
47
+ readonly kind: string;
48
+ readonly message: string;
49
+ readonly route?: string;
50
+ readonly status?: number;
51
+ };
52
+ type ClientErrorInput = {
53
+ readonly kind: string;
54
+ readonly message: string;
55
+ readonly route?: string;
56
+ readonly status?: number;
57
+ readonly at?: string;
58
+ };
59
+ /**
60
+ * Record an error from the CONTROLLED path: our own `reportClientError` call
61
+ * sites. Gets the full `MAX_CLIENT_ERROR_MESSAGE_CHARS` cap — see that
62
+ * constant's doc comment for why the two paths are capped differently.
63
+ */
64
+ export declare const recordClientError: (entry: ClientErrorInput) => void;
65
+ export declare const peekClientErrors: () => readonly ClientErrorEntry[];
66
+ /**
67
+ * Subscribe to uncaught errors and unhandled rejections. Returns the
68
+ * uninstaller; safe to call more than once (each call returns its own).
69
+ */
70
+ export declare const installGlobalClientErrorCapture: (target: Window) => (() => void);
71
+ export declare const __resetClientErrorLogForTest: () => void;
72
+ export {};
@@ -105,6 +105,7 @@ export type DockCopy = {
105
105
  readonly english: string;
106
106
  readonly spanish: string;
107
107
  readonly portuguese: string;
108
+ readonly italian: string;
108
109
  };
109
110
  readonly menu: MenuCopy;
110
111
  /** One line of scope in the user's own nouns (spec 2026-09-03). */
@@ -16,11 +16,12 @@ export type AgentRuntime = (typeof AGENT_RUNTIMES)[number];
16
16
  /**
17
17
  * LLM providers the dev panel can swap the model to. Honored by the MASTRA
18
18
  * runtime only (its model resolves per turn); the other runtimes own their
19
- * model wiring and ignore the override. `gateway` is the generic router
19
+ * model wiring and ignore the override. Production requires the worker's
20
+ * ALLOW_MODEL_OVERRIDES opt-in. `gateway` is the generic router
20
21
  * (Vercel AI Gateway): pair it with a free-typed `provider/model` slug
21
22
  * (e.g. `deepseek/deepseek-v4-pro`) to reach any model the gateway serves.
22
23
  */
23
- export declare const LLM_PROVIDERS: readonly ["anthropic", "google", "openai", "cerebras", "gateway", "openrouter"];
24
+ export declare const LLM_PROVIDERS: readonly ["anthropic", "google", "openai", "cerebras", "gateway", "openrouter", "vertex"];
24
25
  export type LlmProvider = (typeof LLM_PROVIDERS)[number];
25
26
  /**
26
27
  * A free-typed model id must be safe to ride the wire verbatim: bounded, no
@@ -37,11 +38,8 @@ export type LlmProvider = (typeof LLM_PROVIDERS)[number];
37
38
  * `claude-haiku-4-5@20251001`, `vertex`'s own `DEFAULT_MODEL_IDS` value in
38
39
  * llm-model-resolver.ts) — this dev panel is the one UI that free-types a
39
40
  * model id, so a charset drift here is the one that actually blocks a live
40
- * smoke rather than just failing a schema test. NOTE: `LLM_PROVIDERS` above
41
- * does not include "vertex" yet (mirrored by the transport's `llmProvider`
42
- * enum) — the dev panel cannot *select* vertex as a provider, so this
43
- * free-typed model id is the only path that reaches it today; a future
44
- * vertex-enum PR must update both this file and the transport together.
41
+ * smoke rather than just failing a schema test. The transport's provider enum
42
+ * must also accept every provider selectable here, including Vertex.
45
43
  */
46
44
  export declare const isValidLlmModelId: (value: string) => boolean;
47
45
  /** True when the typed text is the cheat code (trim + case-insensitive). */
@@ -0,0 +1,32 @@
1
+ import { DockResizeState } from './dock-layout.ts';
2
+ type DockOpenSize = "expanded" | "fullscreen";
3
+ /** A pending size: an explicit one, or `default` = `open()` with no size. */
4
+ type PendingDockSize = DockOpenSize | "default";
5
+ /** The public `BelongOpenOptions.draft` ceiling. */
6
+ export declare const DOCK_OPEN_DRAFT_MAX_CHARS = 4000;
7
+ /**
8
+ * Record a host open request and notify the dock. Parsed at the host boundary
9
+ * (§4): an invalid request is ignored with one console warning — never a throw
10
+ * into the host page. A request without a draft leaves a still-pending draft
11
+ * in place (it did not ask to cancel it); a newer draft replaces it.
12
+ */
13
+ export declare const requestDockOpen: (options: unknown) => void;
14
+ /** The pending size WITHOUT consuming it — for render-time initializers. */
15
+ export declare const peekPendingDockSize: () => PendingDockSize | null;
16
+ /** Consume the pending size: returned once, then null. */
17
+ export declare const takePendingDockSize: () => PendingDockSize | null;
18
+ /** Consume the pending draft: returned once, then null. */
19
+ export declare const takePendingDockDraft: () => string | null;
20
+ /** Notified once per accepted request; returns an unsubscribe. */
21
+ export declare const subscribeDockOpenRequest: (listener: () => void) => (() => void);
22
+ /**
23
+ * The size a request resolves to from the dock's current one. `default` shows
24
+ * the dock without changing an open size (a collapsed dock opens); an explicit
25
+ * size wins. Fullscreen is a desktop size: on a phone the open sheet already
26
+ * fills the viewport, so it resolves to `expanded` — the same rule the dock's
27
+ * initializer applies to a persisted fullscreen.
28
+ */
29
+ export declare const resolveRequestedDockSize: (requested: PendingDockSize, current: DockResizeState, mobile: boolean) => DockResizeState;
30
+ /** Test-only: nothing pending, no subscribers. */
31
+ export declare const __resetDockOpenRequestForTest: () => void;
32
+ export {};
@@ -5,7 +5,7 @@
5
5
  * in the stream-state store; this module owns only view + size. Best-effort:
6
6
  * disabled/quota'd storage just means no cross-reload restore.
7
7
  */
8
- export type DockView = "chat" | "files" | "scheduled" | "prompts" | "connectors";
8
+ export type DockView = "chat" | "goal" | "files" | "scheduled" | "prompts" | "connectors" | "skills";
9
9
  export type DockSize = "collapsed" | "expanded" | "fullscreen";
10
10
  export type DockState = {
11
11
  readonly view: DockView;
@@ -0,0 +1,9 @@
1
+ import { GoalStatus } from '../api/goals.ts';
2
+ export type GoalActionAvailability = {
3
+ readonly pause: boolean;
4
+ readonly resume: boolean;
5
+ readonly wake: boolean;
6
+ readonly snooze: boolean;
7
+ readonly clear: boolean;
8
+ };
9
+ export declare const goalActionAvailability: (status: GoalStatus) => GoalActionAvailability;
@@ -0,0 +1,36 @@
1
+ import { PostMessageError } from '../stream/belong-chat-transport.ts';
2
+ export type GoalRefusal = {
3
+ readonly kind: "exists";
4
+ } | {
5
+ readonly kind: "disabled";
6
+ } | {
7
+ readonly kind: "cap_user";
8
+ readonly limit: number;
9
+ } | {
10
+ readonly kind: "cap_tenant";
11
+ } | {
12
+ readonly kind: "plan_mode";
13
+ } | {
14
+ readonly kind: "not_sent";
15
+ };
16
+ /**
17
+ * Classify a refused `goal` send. `null` when the error is not a goal
18
+ * refusal at all — the caller's existing stream-error path still owns that.
19
+ *
20
+ * A non-`PostMessageError` means the send never reached the server (a
21
+ * network failure, an aborted fetch, …) — that is always `not_sent`,
22
+ * whatever the JS error happens to be, because the caller only classifies
23
+ * failures of a send IT started (see `handleCreateSendError` in
24
+ * `use-session-goal.ts`). A `PostMessageError` whose status/body the server
25
+ * never documented for `/goal` (a 500 `run_failed`, say) is left to the
26
+ * chat's own generic error surface instead of being misread as a refusal.
27
+ */
28
+ export declare const goalRefusalFromError: (error: unknown) => GoalRefusal | null;
29
+ /**
30
+ * A refused POST, whatever the reason. `PostMessageError.message` embeds the
31
+ * server's status AND its raw body (a wire enum, untranslated English prose,
32
+ * or a Zod issue list) — never show that to a user: the dock renders its own
33
+ * i18n'd fallback instead. The memory note "server prose isn't localized" is
34
+ * exactly this failure, and §42's "reads as breakage" is the cost.
35
+ */
36
+ export declare const isPostMessageRefusal: (error: unknown) => error is PostMessageError;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Ruling 4's exact contract (spec §4/§9) — a goal-transition email carries a
3
+ * button back into the dock: `<base>?belongSession=<id>&belongView=goal`
4
+ * (`goalDeepLinkUrl` on the cloud side builds it; this reads it). Two rules:
5
+ * the param names are `belongSession` and `belongView` — a typo on either
6
+ * side is a link that silently does nothing — and the params are STRIPPED
7
+ * once read, so a reload of the dock does not yank the user back into a
8
+ * goal they have already looked at.
9
+ *
10
+ * `view` is accepted ONLY when it is exactly `"goal"` — no other value is a
11
+ * link this reader recognizes — so `GoalDeepLink` can carry the literal type
12
+ * rather than a wider `EmbedView`, and a caller never has to re-check it.
13
+ * Any other combination (missing `belongSession`, missing/mismatched
14
+ * `belongView`, or no params at all) returns `null` and leaves the query
15
+ * string untouched — a page whose query string was never ours to rewrite.
16
+ */
17
+ export type GoalDeepLink = {
18
+ readonly sessionId: string;
19
+ readonly view: "goal";
20
+ };
21
+ export declare const consumeGoalDeepLink: (location: {
22
+ readonly search: string;
23
+ readonly replaceSearch: (next: string) => void;
24
+ }) => GoalDeepLink | null;
@@ -0,0 +1,25 @@
1
+ import { GoalActionOutcome, GoalEditInput } from '../api/goals.ts';
2
+ import { BuiltinCommand } from './slash-commands.ts';
3
+ /** The three `/goal` sub-commands that map onto `POST .../goal/edit`. */
4
+ type GoalEditCommand = Extract<BuiltinCommand, {
5
+ readonly action: "edit";
6
+ } | {
7
+ readonly action: "turns";
8
+ } | {
9
+ readonly action: "budget";
10
+ }>;
11
+ /** `editSessionGoal`'s full outcome union, including the client-only
12
+ * `"no_change"` case (`api/goals.ts`'s 400 `goal_edit_no_change` mapping). */
13
+ type GoalEditOutcome = GoalActionOutcome | {
14
+ readonly status: "no_change";
15
+ };
16
+ type GoalEditNoticeKey = "goal.revived" | "goal.edited" | "goal.notEditable" | "goal.editUsage" | "goal.none" | "goal.disabled" | "goal.error";
17
+ /** Command → `POST .../goal/edit` request body. */
18
+ export declare const goalEditRequestBody: (command: GoalEditCommand) => GoalEditInput;
19
+ /**
20
+ * `editSessionGoal` outcome → the i18n key for the local notice. A `revived`
21
+ * edit (a raised cap that un-stalled a `budget_limited` goal) gets its own
22
+ * copy distinct from a plain edit — see `GoalActionOutcome`'s `revived` flag.
23
+ */
24
+ export declare const goalEditNoticeKey: (outcome: GoalEditOutcome) => GoalEditNoticeKey;
25
+ export {};
@@ -0,0 +1,53 @@
1
+ import { GoalJournalEntry } from '../api/goals.ts';
2
+ /**
3
+ * A status change is named by the status it reached — the dock already has
4
+ * localized words for all twelve, and "blocked" tells the reader more than
5
+ * "transition" ever could. Everything else is named by its kind.
6
+ *
7
+ * `deferred` is the one exception (final review, Medium 2). `statusLabel`
8
+ * words are written for the PILL, where they answer "what is this goal doing
9
+ * right now": "waiting for a time" is right there and wrong in a history,
10
+ * where each line is something that HAPPENED at a named moment — and, since
11
+ * `/goal snooze` now writes no note, that label would be the whole line.
12
+ * The kind's own "Went to sleep" reads as an event, which is what the
13
+ * timeline is made of.
14
+ */
15
+ export declare const goalJournalLabelKey: (entry: GoalJournalEntry) => string;
16
+ /**
17
+ * An entry earns a line when it SAYS something: it changed the status, or it
18
+ * carries a note. A bare accounting row (kind `turn`, no note, no status
19
+ * move) is bookkeeping — putting it in History would push the things that
20
+ * actually happened out of a ten-row window.
21
+ */
22
+ export declare const visibleGoalJournal: (journal: readonly GoalJournalEntry[] | undefined) => readonly GoalJournalEntry[];
23
+ export type GoalJournalDay = {
24
+ readonly key: string;
25
+ readonly entries: readonly GoalJournalEntry[];
26
+ };
27
+ /**
28
+ * Local calendar day key (yyyy-mm-dd) for `date`, in the VIEWER's zone —
29
+ * not `toISOString()`, which is UTC and would put a 21:00 local entry on
30
+ * tomorrow's list for half the world. Shared by `groupGoalJournalByDay` and
31
+ * `isYesterdayKey` so both use one definition of "day".
32
+ */
33
+ export declare const goalDayKey: (date: Date) => string;
34
+ /**
35
+ * True when `dayKey` (as produced by `goalDayKey`) names the calendar day
36
+ * immediately before `now`'s day, in the viewer's zone. Backs the
37
+ * Timeline's "Yesterday" heading (appendix B "Timeline": `Today`,
38
+ * `Yesterday`, `Sep 12`). Walks `now` back one calendar day via the `Date`
39
+ * constructor's day-of-month rollover rather than subtracting 24h of
40
+ * milliseconds, so a DST-transition day still lands on the correct date.
41
+ */
42
+ export declare const isYesterdayKey: (dayKey: string, now: Date) => boolean;
43
+ /**
44
+ * Day buckets for the History timeline (appendix B "Timeline"), computed in
45
+ * the VIEWER's zone — the reader's "today" is the only one that matters, and
46
+ * the server's midnight is not it. `now` is injected (§25) so "Today" is
47
+ * testable.
48
+ *
49
+ * An entry whose `at` will not parse keeps its place in the list rather than
50
+ * disappearing: losing a line of history to a bad timestamp is a §41 loss,
51
+ * and the label is the load-bearing half of the row anyway.
52
+ */
53
+ export declare const groupGoalJournalByDay: (entries: readonly GoalJournalEntry[], now: Date) => readonly GoalJournalDay[];
@@ -0,0 +1,2 @@
1
+ import { GoalStatus } from '../api/goals.ts';
2
+ export declare const isLiveGoalStatus: (status: GoalStatus) => boolean;
@@ -0,0 +1,35 @@
1
+ import { GoalView } from '../api/goals.ts';
2
+ export type GoalElapsed = {
3
+ readonly kind: "just_started";
4
+ } | {
5
+ readonly kind: "minutes";
6
+ readonly minutes: number;
7
+ } | {
8
+ readonly kind: "hours";
9
+ readonly hours: number;
10
+ readonly minutes: number;
11
+ } | {
12
+ readonly kind: "days";
13
+ readonly days: number;
14
+ };
15
+ export declare const goalElapsed: (input: {
16
+ readonly startedAt: string | null | undefined;
17
+ readonly now: Date;
18
+ }) => GoalElapsed | null;
19
+ /** Turns left, but only once the cap is close enough that the number changes
20
+ * what the reader does about it. `null` otherwise, and `null` for a cap of
21
+ * zero (never a division by it — §10). */
22
+ export declare const goalTurnsRemaining: (goal: Pick<GoalView, "turnsUsed" | "maxTurns">) => number | null;
23
+ /**
24
+ * The runway's plain numbers read straight off the goal's OWN stored caps,
25
+ * never a preset name — task-15 review, Low 4. `GoalSetCard` used to fall
26
+ * back to `runway ?? "normal"` for a pre-0071 row (`runway: null`), which
27
+ * labeled a goal that actually carries the Long preset's numbers as "Normal".
28
+ * When there is no preset to name honestly, describe the ACTUAL caps in the
29
+ * same steps-and-days vocabulary `goal-runway.ts`'s preset copy uses (spec
30
+ * ruling 1) instead of claiming a preset the row doesn't carry.
31
+ */
32
+ export declare const goalRunwayPlain: (goal: Pick<GoalView, "maxTurns" | "maxActiveSeconds">) => {
33
+ readonly steps: number;
34
+ readonly days: number;
35
+ };
@@ -0,0 +1,15 @@
1
+ import { GoalView } from '../api/goals.ts';
2
+ export type GoalProgressModel = {
3
+ readonly percent: number;
4
+ readonly band: "barely" | "normal" | "almost_out";
5
+ readonly turnsUsed: number;
6
+ readonly maxTurns: number;
7
+ readonly runningSeconds: number;
8
+ readonly exact: {
9
+ readonly tokensUsed: number;
10
+ readonly tokenBudget: number;
11
+ readonly activeSeconds: number;
12
+ readonly maxActiveSeconds: number;
13
+ };
14
+ };
15
+ export declare const goalProgressModel: (goal: GoalView) => GoalProgressModel;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Ruling 5 (spec §7) — "Not now" on the proposal card, persisted per device.
3
+ * localStorage only for v1: a server-side dismissal marker is a later slice
4
+ * (spec "Not in this design"), so a reload on ANOTHER device shows the offer
5
+ * again — documented, acceptable. Every read/write is wrapped in try/catch:
6
+ * a private window throws on `localStorage` access, and this must never be
7
+ * the thing that breaks the card.
8
+ */
9
+ export declare const goalProposalDismissalKey: (sessionId: string) => string;
10
+ export declare const readGoalProposalDismissed: (sessionId: string) => boolean;
11
+ export declare const writeGoalProposalDismissed: (sessionId: string) => void;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * "resumes 09:00" — the goal pill's one piece of copy built from a time
3
+ * rather than a status key (`goal-status-label.ts`).
4
+ *
5
+ * Formatted in the VIEWER's locale and timezone, client-side, from the
6
+ * UTC instant the backend sends: the person reading the pill is the person
7
+ * who has to know whether 09:00 has happened yet, and a server-rendered
8
+ * time would be in the server's zone. `now` is a parameter rather than a
9
+ * `new Date()` inside (CLAUDE.md §25 — do not hide time), which is also
10
+ * what makes the same-day branch testable.
11
+ *
12
+ * Same day → the time alone ("09:00"); any other day → date and time
13
+ * ("Sep 12, 09:00"), because "09:00" on its own reads as *this morning*
14
+ * and a goal that is asleep until tomorrow is exactly the case where that
15
+ * misreads. Null for a missing or unparsable instant — the caller falls
16
+ * back to the plain "scheduled" label rather than rendering "Invalid Date".
17
+ */
18
+ export declare const formatGoalResumeTime: (input: {
19
+ readonly resumeAt: string | null | undefined;
20
+ readonly now: Date;
21
+ readonly locale: string;
22
+ }) => string | null;
@@ -0,0 +1,27 @@
1
+ import { GoalElapsed } from './goal-plain-units.ts';
2
+ import { GoalView } from '../api/goals.ts';
3
+ export type GoalRowTone = "live" | "calm" | "attention";
4
+ export type GoalRowActionKind = "cancel_setup" | "pause" | "resume" | "wake_now" | "details" | "answer" | "review" | "show_me" | "tell_it" | "give_it_more" | "new_goal";
5
+ export type GoalRowAction = {
6
+ readonly kind: GoalRowActionKind;
7
+ readonly labelKey: string;
8
+ };
9
+ export type GoalRowCopy = {
10
+ readonly key: string;
11
+ readonly vars?: Readonly<Record<string, string | number>>;
12
+ };
13
+ export type GoalRowModel = {
14
+ readonly tone: GoalRowTone;
15
+ readonly headline: GoalRowCopy;
16
+ readonly detail: GoalRowCopy | null;
17
+ /** Non-null only where the row shows a clock (working, checking, paused).
18
+ * The component renders it through `goal.units.*` and passes the result
19
+ * into `detail.vars` as `elapsed`. */
20
+ readonly elapsed: GoalElapsed | null;
21
+ /** Non-null ONLY for `deferred`. The component formats it in the viewer's
22
+ * zone and passes it into `headline.vars` as `time`. */
23
+ readonly resumeAt: string | null;
24
+ readonly primary: GoalRowAction | null;
25
+ readonly secondary: GoalRowAction | null;
26
+ };
27
+ export declare const goalRowModel: (goal: GoalView, now: Date) => GoalRowModel | null;
@@ -0,0 +1,5 @@
1
+ export type GoalRunwayPreset = "quick" | "normal" | "long";
2
+ export type GoalRunwayLabelKey = "goal.compose.runwayQuick" | "goal.compose.runwayNormal" | "goal.compose.runwayLong";
3
+ export type GoalRunwayDetailKey = "goal.compose.runwayQuickDetail" | "goal.compose.runwayNormalDetail" | "goal.compose.runwayLongDetail";
4
+ export declare const goalRunwayLabelKey: (preset: GoalRunwayPreset) => GoalRunwayLabelKey;
5
+ export declare const goalRunwayDetailKey: (preset: GoalRunwayPreset) => GoalRunwayDetailKey;
@@ -0,0 +1,12 @@
1
+ export declare const goalScheduleCommand: (objective: string) => string;
2
+ /**
3
+ * The i18n key naming the cadence this objective's own words imply, or null
4
+ * when they imply none (final review, Low 5: spec §5 asks the ending's button
5
+ * to say "Run this again {{cadence}}", and the flat "on a schedule" never
6
+ * named the cadence `goalScheduleCommand` had already guessed).
7
+ *
8
+ * A KEY, not the `phrase` above: the phrase is the `/schedule` parser's own
9
+ * English input and must stay English, while the button is read by a user in
10
+ * their own language.
11
+ */
12
+ export declare const goalScheduleCadenceKey: (objective: string) => string | null;
@@ -0,0 +1,3 @@
1
+ import { GoalStatus } from '../api/goals.ts';
2
+ export type GoalStatusLabelKey = "goal.statusLabel.clarifying" | "goal.statusLabel.active" | "goal.statusLabel.completionClaimed" | "goal.statusLabel.paused" | "goal.statusLabel.deferred" | "goal.statusLabel.waitingForUser" | "goal.statusLabel.waitingForApproval" | "goal.statusLabel.waitingForUiAction" | "goal.statusLabel.blocked" | "goal.statusLabel.budgetLimited" | "goal.statusLabel.complete" | "goal.statusLabel.cleared";
3
+ export declare const goalStatusLabelKey: (status: GoalStatus) => GoalStatusLabelKey;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `GoalBadge`'s time-usage line, pulled out to a pure helper (task-9 review,
3
+ * finding 4). The prior version always rendered `Math.round(seconds / 60)`
4
+ * minutes, which read as "130 of 10080 min" for a week-long cap — CLAUDE.md
5
+ * §11 asks for human units, not a five-digit minute count.
6
+ *
7
+ * Rule: minutes when the cap is under 60 minutes, hours when the cap is
8
+ * under 48 hours, else days — chosen from the CAP so `used`/`max` always
9
+ * share one unit ("2 of 10 h", never "90 min of 10 h"). Both values round
10
+ * to integers in that unit; the caller (`GoalBadge.tsx`) applies
11
+ * `toLocaleString()` for large counts, same as the tokens line.
12
+ */
13
+ export type GoalTimeUnit = "min" | "h" | "d";
14
+ export type GoalTimeUsage = {
15
+ readonly used: number;
16
+ readonly max: number;
17
+ readonly unit: GoalTimeUnit;
18
+ };
19
+ export declare const formatGoalTimeUsage: (activeSeconds: number, maxActiveSeconds: number) => GoalTimeUsage;
@@ -0,0 +1,14 @@
1
+ import { CapturedScreenshot } from './screen-capture.ts';
2
+ import { IssueReportClientContext } from './issue-report.ts';
3
+ import { ClientErrorEntry } from './client-error-log.ts';
4
+ export type IssueReportRequest = {
5
+ readonly sessionId: string | null;
6
+ readonly taskId: string | null;
7
+ readonly screenshot: CapturedScreenshot | null;
8
+ readonly context: IssueReportClientContext;
9
+ readonly clientErrors: readonly ClientErrorEntry[];
10
+ };
11
+ export declare const openIssueReport: (request: IssueReportRequest) => void;
12
+ export declare const closeIssueReport: () => void;
13
+ export declare const peekIssueReport: () => IssueReportRequest | null;
14
+ export declare const subscribeIssueReport: (listener: () => void) => (() => void);
@@ -0,0 +1,8 @@
1
+ import { captureScreen } from './screen-capture.ts';
2
+ import { DockView } from './dock-state.ts';
3
+ export declare const requestIssueReport: (input: {
4
+ readonly dockView: DockView;
5
+ readonly taskId?: string | null;
6
+ /** Injected in tests. */
7
+ readonly capture?: typeof captureScreen;
8
+ }) => Promise<void>;
@@ -0,0 +1,41 @@
1
+ import { DockView } from './dock-state.ts';
2
+ /**
3
+ * The server's own caps (`apps/belong-cloud/src/features/issue-reports/issue-report.schemas.ts`,
4
+ * `hostUrl`/`userAgent`), mirrored here so a page with a long URL or an
5
+ * unusual user agent string never produces a report the route refuses with
6
+ * `invalid_meta`. Without this, the ONE feature whose job is catching the
7
+ * unreportable becomes the thing that cannot be reported: the SDK collapses
8
+ * that 400 to `request_failed` and the dialog says "Try again," which never
9
+ * succeeds for that user.
10
+ */
11
+ export declare const MAX_HOST_URL_CHARS = 2000;
12
+ export declare const MAX_USER_AGENT_CHARS = 500;
13
+ export type IssueReportViewport = {
14
+ readonly widthPx: number;
15
+ readonly heightPx: number;
16
+ /** Device pixel ratio x100 — an integer, per §11. */
17
+ readonly dprX100: number;
18
+ };
19
+ export type IssueReportClientContext = {
20
+ readonly hostUrl: string;
21
+ readonly dockView: DockView;
22
+ readonly sdkVersion: string;
23
+ readonly sdkBuiltAt: string | null;
24
+ readonly viewport: IssueReportViewport;
25
+ readonly locale: string;
26
+ readonly userAgent: string;
27
+ readonly capturedAt: string;
28
+ };
29
+ export declare const readViewport: (win: {
30
+ readonly innerWidth: number;
31
+ readonly innerHeight: number;
32
+ readonly devicePixelRatio: number;
33
+ }) => IssueReportViewport;
34
+ export declare const buildIssueReportContext: (input: {
35
+ readonly dockView: DockView;
36
+ readonly hostUrl: string;
37
+ readonly locale: string;
38
+ readonly userAgent: string;
39
+ readonly viewport: IssueReportViewport;
40
+ readonly capturedAt: string;
41
+ }) => IssueReportClientContext;
@@ -6,7 +6,7 @@ export declare const DEFAULT_LOCALE: Locale;
6
6
  * picker (rail cycle button, avatar menu, welcome card) read from here.
7
7
  * Tied to the codec's `DOCK_LOCALES`: the `satisfies` below pins SDK ⊆
8
8
  * codec at compile time and locale.test.ts asserts full set equality. */
9
- export declare const SUPPORTED_LOCALES: readonly ["en", "es", "pt"];
9
+ export declare const SUPPORTED_LOCALES: readonly ["en", "es", "pt", "it"];
10
10
  /** Narrow an untrusted value (attribute, storage, navigator) to a Locale. */
11
11
  export declare const parseLocale: (value: unknown) => Locale | null;
12
12
  /**
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Rasterize the host page for an issue report.
3
+ *
4
+ * snapdom clones the DOM into an SVG foreignObject with styles inlined, then
5
+ * paints it to a canvas. It is the one such library that walks SHADOW ROOTS,
6
+ * which is not optional here: the dock itself lives in one, so anything else
7
+ * (html2canvas, notably) would photograph the host app with a hole where
8
+ * Belong is.
9
+ *
10
+ * KNOWN LIMITS — say them here so they are not re-discovered from a confusing
11
+ * report:
12
+ * • cross-origin images and cross-origin iframes come out blank;
13
+ * • this is a re-render of the DOM, not a photograph of pixels — a canvas
14
+ * element or an unusual CSS feature can differ from what the user saw;
15
+ * • it costs a beat on a large page, which is why capture runs on the click
16
+ * and the dialog opens after it.
17
+ *
18
+ * DEVICE PIXEL RATIO: snapdom's `toCanvas` defaults `dpr` to
19
+ * `window.devicePixelRatio || 1` and multiplies the OUTPUT canvas dimensions
20
+ * by it, on top of `scale`. `captureScale`/`MAX_SCREENSHOT_WIDTH_PX` below
21
+ * only account for `scale` — so left at its default, a capture on a 2x
22
+ * display would produce a canvas 2x wider than the documented 1600px cap
23
+ * promises, inflating bytes and pushing more captures into the
24
+ * `MAX_SCREENSHOT_BYTES` refusal. `dpr: 1` is passed explicitly so the
25
+ * output is in CSS pixels and `MAX_SCREENSHOT_WIDTH_PX` means what it says
26
+ * regardless of the display's pixel ratio.
27
+ *
28
+ * WEBP-WITH-PNG-FALLBACK: `defaultEncode` always *requests* "image/webp". The
29
+ * PNG fallback is not a second encode call in this file — it is the browser's
30
+ * own behavior. Per the HTML spec, `HTMLCanvasElement.toBlob` given a type it
31
+ * cannot encode must produce `image/png` instead. So on a UA without a WebP
32
+ * encoder, the very same call already yields a PNG blob, and `blob.type`
33
+ * carries the truth of which one it got. A later task propagates `blob.type`
34
+ * into the upload, and the route accepts both `image/webp` and `image/png` —
35
+ * that is exactly why it accepts two. One consequence: PNG ignores the
36
+ * `quality` argument entirely, so the low-quality retry below can be a no-op
37
+ * on those browsers (same bytes both times) — the designed outcome there is
38
+ * still a report with no picture rather than an oversized upload, never a
39
+ * silently-too-big send.
40
+ *
41
+ * Never throws: every failure is `null`, and the dialog then offers to send the
42
+ * report without a picture. A capture failure must not cost us the report.
43
+ *
44
+ * A THROWN failure (as opposed to the "over the cap even at low quality"
45
+ * refusal above, which is not a failure) is also recorded into the client-
46
+ * error ring buffer (`client-error-log.ts`), so the answer to "why is there
47
+ * no image" travels INSIDE the report itself — the one place an operator is
48
+ * already looking — rather than being indistinguishable from a user who
49
+ * removed the screenshot or an object-store put that failed server-side.
50
+ * Recording is wrapped in its own try/catch: it must not be able to violate
51
+ * the never-throws contract above, even if the recorder itself somehow threw.
52
+ *
53
+ * The library is imported lazily so it stays out of the dock's main chunk —
54
+ * most sessions never open this dialog.
55
+ */
56
+ export declare const MAX_SCREENSHOT_WIDTH_PX = 1600;
57
+ export declare const MAX_SCREENSHOT_BYTES = 2000000;
58
+ export type CapturedScreenshot = {
59
+ readonly blob: Blob;
60
+ readonly widthPx: number;
61
+ readonly heightPx: number;
62
+ };
63
+ export type Rasterizer = (element: Element, options: {
64
+ readonly scale: number;
65
+ readonly dpr: number;
66
+ }) => Promise<HTMLCanvasElement>;
67
+ export type Encoder = (canvas: HTMLCanvasElement, quality: number) => Promise<Blob | null>;
68
+ /** Shrink only what is wider than the cap; never upscale. */
69
+ export declare const captureScale: (elementWidthPx: number) => number;
70
+ export declare const captureScreen: (input: {
71
+ readonly element: Element;
72
+ readonly rasterize?: Rasterizer;
73
+ readonly encode?: Encoder;
74
+ }) => Promise<CapturedScreenshot | null>;