@opengeni/react 1.4.4 → 1.4.5-canary.37458084490001

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 (234) hide show
  1. package/README.md +17 -6
  2. package/dist/artifacts.d.ts +1 -0
  3. package/dist/artifacts.js +22 -15
  4. package/dist/artifacts.js.map +1 -1
  5. package/dist/{browser-viewer-5EEAS3IU.js → browser-viewer-CV23VIHB.js} +8 -6
  6. package/dist/chunk-3ZC6QPOM.js +174 -0
  7. package/dist/chunk-3ZC6QPOM.js.map +1 -0
  8. package/dist/{chunk-YVFRBXST.js → chunk-4BCVCJSU.js} +2 -2
  9. package/dist/{chunk-JHTIMBOL.js → chunk-5JSUNNQN.js} +26 -4
  10. package/dist/{chunk-JHTIMBOL.js.map → chunk-5JSUNNQN.js.map} +1 -1
  11. package/dist/{chunk-I6OY3MWC.js → chunk-6FOTSVWU.js} +2 -2
  12. package/dist/{chunk-IUHHPMAX.js → chunk-7RRBX5EM.js} +14 -85
  13. package/dist/chunk-7RRBX5EM.js.map +1 -0
  14. package/dist/{chunk-L2J3PU7A.js → chunk-7ZB7I3KP.js} +24 -98
  15. package/dist/chunk-7ZB7I3KP.js.map +1 -0
  16. package/dist/{chunk-LVS3MH5T.js → chunk-ATIRUHBZ.js} +128 -10
  17. package/dist/chunk-ATIRUHBZ.js.map +1 -0
  18. package/dist/chunk-BOFEQC6K.js +162 -0
  19. package/dist/chunk-BOFEQC6K.js.map +1 -0
  20. package/dist/{chunk-2OQA3CPB.js → chunk-CESBPQP5.js} +19 -5
  21. package/dist/chunk-CESBPQP5.js.map +1 -0
  22. package/dist/{chunk-IYQCWGU5.js → chunk-GHJU7XG7.js} +35 -62
  23. package/dist/chunk-GHJU7XG7.js.map +1 -0
  24. package/dist/{chunk-G6YRCIYJ.js → chunk-HNVWMDNN.js} +55 -60
  25. package/dist/chunk-HNVWMDNN.js.map +1 -0
  26. package/dist/{chunk-PHBFJCEU.js → chunk-I5EGRXXG.js} +110 -7
  27. package/dist/chunk-I5EGRXXG.js.map +1 -0
  28. package/dist/{chunk-PBCZL4JP.js → chunk-INIZSNXT.js} +2 -2
  29. package/dist/{chunk-XS67YG5R.js → chunk-IX3K4THH.js} +24 -3
  30. package/dist/chunk-IX3K4THH.js.map +1 -0
  31. package/dist/chunk-IZV5FTJC.js +55 -0
  32. package/dist/chunk-IZV5FTJC.js.map +1 -0
  33. package/dist/chunk-JDJJMBB3.js +1883 -0
  34. package/dist/chunk-JDJJMBB3.js.map +1 -0
  35. package/dist/{chunk-C5BF4L26.js → chunk-KHF2VRXF.js} +7 -7
  36. package/dist/{chunk-QMMCJGFI.js → chunk-L5OKM2VX.js} +2 -2
  37. package/dist/chunk-LHF6BVM4.js +83 -0
  38. package/dist/chunk-LHF6BVM4.js.map +1 -0
  39. package/dist/chunk-N7S72EXK.js +25 -0
  40. package/dist/chunk-N7S72EXK.js.map +1 -0
  41. package/dist/{chunk-CVYCLFMH.js → chunk-PEHTX3V7.js} +19 -1
  42. package/dist/chunk-PEHTX3V7.js.map +1 -0
  43. package/dist/chunk-R5R5RQQD.js +51 -0
  44. package/dist/chunk-R5R5RQQD.js.map +1 -0
  45. package/dist/chunk-RI42H5IU.js +326 -0
  46. package/dist/chunk-RI42H5IU.js.map +1 -0
  47. package/dist/{chunk-JH7V627O.js → chunk-SHLZMAJN.js} +2 -11
  48. package/dist/chunk-SHLZMAJN.js.map +1 -0
  49. package/dist/{chunk-EAOSEIXB.js → chunk-TZQSHCZN.js} +692 -1996
  50. package/dist/chunk-TZQSHCZN.js.map +1 -0
  51. package/dist/chunk-ZIB4H5LN.js +101 -0
  52. package/dist/chunk-ZIB4H5LN.js.map +1 -0
  53. package/dist/client.d.ts +9 -3
  54. package/dist/clipboard.js +1 -1
  55. package/dist/components/browser-viewer.d.ts +6 -0
  56. package/dist/components/composer.d.ts +3 -69
  57. package/dist/components/human-input-form.d.ts +3 -32
  58. package/dist/components/markdown.d.ts +8 -0
  59. package/dist/components/model-policy-picker.d.ts +4 -37
  60. package/dist/components/opper-mark.d.ts +7 -0
  61. package/dist/components/provider-recovery-notice.d.ts +15 -0
  62. package/dist/components/remark-soft-line-breaks.d.ts +38 -0
  63. package/dist/components/sandbox-workspace.d.ts +13 -0
  64. package/dist/components/session-chrome.d.ts +5 -3
  65. package/dist/components/session-conversation.d.ts +15 -3
  66. package/dist/composer-messages.d.ts +74 -0
  67. package/dist/composer-messages.js +9 -0
  68. package/dist/composer.js +15 -10
  69. package/dist/{computer-viewer-ELNLZGHI.js → computer-viewer-K4CKYRX6.js} +4 -4
  70. package/dist/connect-setup.d.ts +2 -0
  71. package/dist/connect.d.ts +1 -1
  72. package/dist/connect.js +21 -6
  73. package/dist/connect.js.map +1 -1
  74. package/dist/{embedded-voice-NIL4RDX3.js → embedded-voice-3ZFWWJ6O.js} +6 -4
  75. package/dist/{embedded-voice-NIL4RDX3.js.map → embedded-voice-3ZFWWJ6O.js.map} +1 -1
  76. package/dist/hooks/shared-machines-list.d.ts +4 -0
  77. package/dist/hooks/use-client-config-flags.d.ts +6 -0
  78. package/dist/hooks/use-goal.d.ts +2 -1
  79. package/dist/human-input-model.d.ts +40 -0
  80. package/dist/index.d.ts +4 -1
  81. package/dist/index.js +116 -73
  82. package/dist/index.js.map +1 -1
  83. package/dist/interaction.js +5 -5
  84. package/dist/{latest-question-2POFPYNB.js → latest-question-SWC6QEVN.js} +3 -3
  85. package/dist/lib/clipboard.d.ts +7 -0
  86. package/dist/lib/provider-recovery.d.ts +49 -0
  87. package/dist/machines.js +2 -2
  88. package/dist/model-mark-paths.d.ts +34 -0
  89. package/dist/model-mark-paths.js +16 -0
  90. package/dist/model-picker-model.d.ts +111 -0
  91. package/dist/model-picker-model.js +25 -0
  92. package/dist/model-picker-model.js.map +1 -0
  93. package/dist/{model-policy-picker-menu-WRNSRFQC.js → model-policy-picker-menu-NM4RPTY6.js} +6 -4
  94. package/dist/model-policy-picker-menu-NM4RPTY6.js.map +1 -0
  95. package/dist/model-policy.d.ts +10 -0
  96. package/dist/model-policy.js +5 -1
  97. package/dist/organization-model.d.ts +32 -0
  98. package/dist/organization-model.js +65 -0
  99. package/dist/organization-model.js.map +1 -0
  100. package/dist/{platform-activity-row-PJJVATTZ.js → platform-activity-row-NJ47YBXW.js} +7 -84
  101. package/dist/platform-activity-row-NJ47YBXW.js.map +1 -0
  102. package/dist/queue-presentation-model.d.ts +72 -0
  103. package/dist/queue-presentation-model.js +38 -0
  104. package/dist/queue-presentation-model.js.map +1 -0
  105. package/dist/{queue-surface-implementation-67FS7FUI.js → queue-surface-implementation-YPJFQ3O6.js} +24 -252
  106. package/dist/queue-surface-implementation-YPJFQ3O6.js.map +1 -0
  107. package/dist/realtime.js +5 -3
  108. package/dist/{sandbox-files-34CJJNOY.js → sandbox-files-RCVCMXG7.js} +5 -5
  109. package/dist/sandbox-files-RCVCMXG7.js.map +1 -0
  110. package/dist/sandbox-label-model.d.ts +23 -0
  111. package/dist/sandbox-label-model.js +28 -0
  112. package/dist/sandbox-label-model.js.map +1 -0
  113. package/dist/session-attention-model.d.ts +8 -0
  114. package/dist/session-attention-model.js +46 -0
  115. package/dist/session-attention-model.js.map +1 -0
  116. package/dist/session-feedback-model.d.ts +20 -0
  117. package/dist/session-feedback-model.js +56 -0
  118. package/dist/session-feedback-model.js.map +1 -0
  119. package/dist/session-list-model.d.ts +96 -0
  120. package/dist/session-list-model.js +219 -0
  121. package/dist/session-list-model.js.map +1 -0
  122. package/dist/session-status-model.d.ts +19 -0
  123. package/dist/session-ui.js +26 -17
  124. package/dist/session-ui.js.map +1 -1
  125. package/dist/session.js +8 -6
  126. package/dist/testing/session-scenarios.d.ts +10 -0
  127. package/dist/testing/session-scenarios.js +303 -0
  128. package/dist/testing/session-scenarios.js.map +1 -0
  129. package/dist/timeline/genie-copy.d.ts +3 -0
  130. package/dist/timeline/genie-loading.d.ts +11 -0
  131. package/dist/timeline/index.d.ts +1 -1
  132. package/dist/timeline/notice-presentation.d.ts +7 -0
  133. package/dist/timeline/patch-apply-command.d.ts +18 -0
  134. package/dist/timeline/platform-activity-presentation.d.ts +7 -0
  135. package/dist/timeline/presented-image.d.ts +6 -1
  136. package/dist/timeline/question-nav-model.d.ts +23 -0
  137. package/dist/timeline/registry.d.ts +10 -1
  138. package/dist/timeline/retained-loaders.d.ts +1 -1
  139. package/dist/timeline/tool-presentation.d.ts +172 -0
  140. package/dist/timeline/turn-summary-model.d.ts +90 -0
  141. package/dist/timeline/turn-summary.d.ts +4 -74
  142. package/dist/timeline/work-presentation.d.ts +42 -0
  143. package/dist/timeline-model.d.ts +24 -0
  144. package/dist/timeline-model.js +152 -0
  145. package/dist/timeline-model.js.map +1 -0
  146. package/package.json +47 -3
  147. package/src/artifacts.ts +1 -0
  148. package/src/client.ts +9 -3
  149. package/src/components/browser-viewer.tsx +23 -2
  150. package/src/components/claude-mark.tsx +2 -1
  151. package/src/components/composer.tsx +4 -144
  152. package/src/components/grok-mark.tsx +2 -1
  153. package/src/components/human-input-form.tsx +13 -124
  154. package/src/components/markdown.tsx +19 -1
  155. package/src/components/message-timeline.tsx +45 -99
  156. package/src/components/model-mark.tsx +2 -1
  157. package/src/components/model-policy-picker-menu.tsx +30 -85
  158. package/src/components/model-policy-picker.tsx +15 -148
  159. package/src/components/opper-mark.tsx +18 -0
  160. package/src/components/provider-recovery-notice.tsx +41 -0
  161. package/src/components/queue-surface-implementation.tsx +20 -349
  162. package/src/components/remark-soft-line-breaks.ts +166 -0
  163. package/src/components/sandbox-workspace.tsx +17 -1
  164. package/src/components/session-chrome.tsx +114 -99
  165. package/src/components/session-conversation.tsx +55 -6
  166. package/src/components/session-status.tsx +17 -16
  167. package/src/composer-messages.ts +150 -0
  168. package/src/connect-setup.tsx +45 -11
  169. package/src/connect.ts +1 -1
  170. package/src/hooks/shared-machines-list.ts +34 -1
  171. package/src/hooks/use-client-config-flags.ts +8 -0
  172. package/src/hooks/use-goal.ts +66 -10
  173. package/src/human-input-model.ts +147 -0
  174. package/src/index.ts +16 -0
  175. package/src/lib/clipboard.ts +29 -0
  176. package/src/lib/format.ts +7 -0
  177. package/src/lib/provider-recovery.ts +202 -0
  178. package/src/model-mark-paths.ts +53 -0
  179. package/src/model-picker-model.ts +330 -0
  180. package/src/model-policy.ts +43 -2
  181. package/src/organization-model.ts +118 -0
  182. package/src/queue-presentation-model.ts +464 -0
  183. package/src/sandbox-label-model.ts +41 -0
  184. package/src/session-attention-model.ts +59 -0
  185. package/src/session-feedback-model.ts +95 -0
  186. package/src/session-list-model.ts +327 -0
  187. package/src/session-status-model.ts +62 -0
  188. package/src/testing/session-scenarios.ts +326 -0
  189. package/src/timeline/activity-rail.tsx +5 -17
  190. package/src/timeline/genie-copy.ts +19 -0
  191. package/src/timeline/genie-loading.tsx +13 -18
  192. package/src/timeline/index.ts +1 -0
  193. package/src/timeline/notice-presentation.ts +22 -0
  194. package/src/timeline/patch-apply-command.tsx +135 -0
  195. package/src/timeline/platform-activity-presentation.ts +122 -0
  196. package/src/timeline/platform-activity-row.tsx +6 -96
  197. package/src/timeline/presented-image.ts +16 -2
  198. package/src/timeline/question-nav-model.ts +28 -0
  199. package/src/timeline/registry.ts +11 -1
  200. package/src/timeline/retained-loaders.ts +9 -4
  201. package/src/timeline/rolling-activity.tsx +7 -19
  202. package/src/timeline/tool-presentation.ts +1316 -0
  203. package/src/timeline/tool-renderers.tsx +226 -1025
  204. package/src/timeline/turn-summary-model.ts +338 -0
  205. package/src/timeline/turn-summary.tsx +21 -326
  206. package/src/timeline/work-presentation.ts +139 -0
  207. package/src/timeline-model.ts +108 -0
  208. package/styles/compiled.css +3 -0
  209. package/styles/connect.css +4 -0
  210. package/dist/chunk-2OQA3CPB.js.map +0 -1
  211. package/dist/chunk-CVYCLFMH.js.map +0 -1
  212. package/dist/chunk-EAOSEIXB.js.map +0 -1
  213. package/dist/chunk-G6YRCIYJ.js.map +0 -1
  214. package/dist/chunk-HXYOSSQO.js +0 -117
  215. package/dist/chunk-HXYOSSQO.js.map +0 -1
  216. package/dist/chunk-IUHHPMAX.js.map +0 -1
  217. package/dist/chunk-IYQCWGU5.js.map +0 -1
  218. package/dist/chunk-JH7V627O.js.map +0 -1
  219. package/dist/chunk-L2J3PU7A.js.map +0 -1
  220. package/dist/chunk-LVS3MH5T.js.map +0 -1
  221. package/dist/chunk-PHBFJCEU.js.map +0 -1
  222. package/dist/chunk-XS67YG5R.js.map +0 -1
  223. package/dist/platform-activity-row-PJJVATTZ.js.map +0 -1
  224. package/dist/queue-surface-implementation-67FS7FUI.js.map +0 -1
  225. /package/dist/{browser-viewer-5EEAS3IU.js.map → browser-viewer-CV23VIHB.js.map} +0 -0
  226. /package/dist/{chunk-YVFRBXST.js.map → chunk-4BCVCJSU.js.map} +0 -0
  227. /package/dist/{chunk-I6OY3MWC.js.map → chunk-6FOTSVWU.js.map} +0 -0
  228. /package/dist/{chunk-PBCZL4JP.js.map → chunk-INIZSNXT.js.map} +0 -0
  229. /package/dist/{chunk-C5BF4L26.js.map → chunk-KHF2VRXF.js.map} +0 -0
  230. /package/dist/{chunk-QMMCJGFI.js.map → chunk-L5OKM2VX.js.map} +0 -0
  231. /package/dist/{computer-viewer-ELNLZGHI.js.map → composer-messages.js.map} +0 -0
  232. /package/dist/{model-policy-picker-menu-WRNSRFQC.js.map → computer-viewer-K4CKYRX6.js.map} +0 -0
  233. /package/dist/{latest-question-2POFPYNB.js.map → latest-question-SWC6QEVN.js.map} +0 -0
  234. /package/dist/{sandbox-files-34CJJNOY.js.map → model-mark-paths.js.map} +0 -0
package/src/client.ts CHANGED
@@ -193,7 +193,7 @@ type SessionClientMethods = Pick<
193
193
 
194
194
  /** Compact list transport is optional for scripted and older clients. */
195
195
  export type SessionClientLike = SessionClientMethods &
196
- Partial<Pick<OpenGeniClient, "listSessionSummaryPage">>;
196
+ Partial<Pick<OpenGeniClient, "listSessionSummaryPage" | "findGoal">>;
197
197
 
198
198
  /**
199
199
  * Tenant-safe client surface required by the session-only React entry.
@@ -233,9 +233,15 @@ export type EmbeddedSessionEventClientLike = Pick<OpenGeniClient, "getSession" |
233
233
  export type EmbeddedSessionReadClientLike = EmbeddedSessionEventClientLike &
234
234
  Pick<OpenGeniClient, "getSession" | "updateSession">;
235
235
 
236
- /** Exact client surface required by {@link useGoal}. */
236
+ /**
237
+ * Exact client surface required by {@link useGoal}. `findGoal` is optional:
238
+ * when present the hook reads a goal-less session as a successful `null`
239
+ * instead of an absorbed 404.
240
+ */
237
241
  export type EmbeddedGoalClientLike = EmbeddedSessionEventClientLike &
238
- Pick<OpenGeniClient, "getGoal" | "updateGoal" | "deleteGoal">;
242
+ Pick<OpenGeniClient, "getGoal" | "updateGoal" | "deleteGoal"> & {
243
+ findGoal?: OpenGeniClient["findGoal"] | undefined;
244
+ };
239
245
 
240
246
  /** Exact client surface required by {@link useSessionLineage}. */
241
247
  export type EmbeddedSessionLineageClientLike = EmbeddedSessionEventClientLike &
@@ -82,6 +82,22 @@ import { InteractionInterventionBanner } from "./interaction-intervention-banner
82
82
  import { BrowserSelectControl } from "./browser-select-control";
83
83
  import { useViewerMenuDismiss } from "./use-viewer-menu-dismiss";
84
84
 
85
+ /** Auto-ending a lost attached-device browser: bounded retries for transient failures only. */
86
+ const STALE_BROWSER_END_MAX_ATTEMPTS = 3;
87
+ const STALE_BROWSER_END_RETRY_BASE_MS = 5_000;
88
+
89
+ /**
90
+ * Delay before retrying an automatic end of a lost attached-device browser, or null to stop.
91
+ * A refusal (4xx, e.g. 409 when the server will not end it) is never retried: re-sending it on
92
+ * every registry refresh looped about one request per second on prod.
93
+ */
94
+ export function staleBrowserEndRetryDelayMs(cause: unknown, failedAttempts: number): number | null {
95
+ const status = (cause as { status?: unknown } | null)?.status;
96
+ if (typeof status === "number" && status >= 400 && status < 500) return null;
97
+ if (failedAttempts >= STALE_BROWSER_END_MAX_ATTEMPTS) return null;
98
+ return STALE_BROWSER_END_RETRY_BASE_MS * 2 ** (failedAttempts - 1);
99
+ }
100
+
85
101
  export type BrowserViewerNotification = {
86
102
  kind: "error" | "info";
87
103
  message: string;
@@ -190,6 +206,7 @@ export function BrowserViewer({
190
206
  [registry.sessions],
191
207
  );
192
208
  const endedGenerationLossRef = useRef(new Set<string>());
209
+ const endStaleBrowserAttemptsRef = useRef(new Map<string, number>());
193
210
  const endStaleBrowser = registry.end;
194
211
  useEffect(() => {
195
212
  if (!enabled) return;
@@ -203,8 +220,12 @@ export function BrowserViewer({
203
220
  }
204
221
  if (endedGenerationLossRef.current.has(session.id)) continue;
205
222
  endedGenerationLossRef.current.add(session.id);
206
- void endStaleBrowser(session.id).catch(() => {
207
- endedGenerationLossRef.current.delete(session.id);
223
+ void endStaleBrowser(session.id).catch((cause: unknown) => {
224
+ const attempts = (endStaleBrowserAttemptsRef.current.get(session.id) ?? 0) + 1;
225
+ endStaleBrowserAttemptsRef.current.set(session.id, attempts);
226
+ const delay = staleBrowserEndRetryDelayMs(cause, attempts);
227
+ if (delay === null) return;
228
+ setTimeout(() => endedGenerationLossRef.current.delete(session.id), delay);
208
229
  });
209
230
  }
210
231
  }, [enabled, endStaleBrowser, registry.sessions]);
@@ -1,10 +1,11 @@
1
+ import { CLAUDE_MARK_PATH } from "../model-mark-paths";
1
2
  import type { SVGProps } from "react";
2
3
 
3
4
  /** ClaudeMark: Simple Icons, CC0; https://github.com/simple-icons/simple-icons */
4
5
  export function ClaudeMark(props: SVGProps<SVGSVGElement>) {
5
6
  return (
6
7
  <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
7
- <path d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z" />
8
+ <path d={CLAUDE_MARK_PATH} />
8
9
  </svg>
9
10
  );
10
11
  }
@@ -52,7 +52,8 @@ import {
52
52
  ComposerResponsiveContext,
53
53
  type ResponsiveBasis,
54
54
  } from "../lib/composer-responsive-context";
55
- import { composerSubmissionErrorMessage, formatBytes, formatRelativeTime } from "../lib/format";
55
+ import { composerSubmissionErrorMessage } from "../lib/format";
56
+ import { defaultChatComposerMessages, type ChatComposerMessages } from "../composer-messages";
56
57
  import type { PickerModelRow } from "../model-policy";
57
58
  import { useLightboxOptional } from "../timeline/screenshot-lightbox";
58
59
  import { OPEN_WORKSTREAM_CONTROL_EVENT } from "../workstream-control-event";
@@ -107,149 +108,8 @@ export type ComposerControlLinks = {
107
108
  sessionHref?: ((sessionId: string) => string) | undefined;
108
109
  };
109
110
 
110
- export type ChatComposerMessages = {
111
- messagePlaceholder: string;
112
- pausedPlaceholder: string;
113
- inputLabel: string;
114
- keyboardHint: string;
115
- slashCommandBlocked: string;
116
- controlChangedError: string;
117
- sendFailedError: string;
118
- dropFiles: string;
119
- attachFiles: string;
120
- pauseAriaLabel: string;
121
- pauseTitle: string;
122
- /** Stop control shown while a response runs (`runControl="stop"`). */
123
- stopAriaLabel?: string | undefined;
124
- stopTitle?: string | undefined;
125
- sendMessageAriaLabel: string;
126
- sendAndResumeAriaLabel: string;
127
- sendTitle: string;
128
- sendAndResumeTitle: string;
129
- annotationNotesRequired: string;
130
- workspacePaused: string;
131
- pausedHere: string;
132
- parentBlocker: string;
133
- narrowerPause: string;
134
- pausedBy: (displayName: string) => string;
135
- queuedAhead: (count: number) => string;
136
- resumingAndSending: string;
137
- nextMessageResumes: string;
138
- resumeThisWorkstream: string;
139
- resumeShort: string;
140
- hidePauseDetails: string;
141
- showPauseDetails: string;
142
- pauseReasonsLabel: string;
143
- pausedByLabel: string;
144
- alsoPausedByLabel: string;
145
- resumeWorkspace: string;
146
- resumeFromSession: string;
147
- sessionCanRun: string;
148
- stillPausedBy: (displayName: string) => string;
149
- restoredResourcesLabel: string;
150
- restoredFile: (fileId: string) => string;
151
- removeRestoredResource: (index: number) => string;
152
- uploading: string;
153
- uploadFailed: string;
154
- previewAttachment: (name: string) => string;
155
- previewUnavailable?: string | undefined;
156
- attachmentPreviewLabel: string;
157
- downloadAttachment: (name: string) => string;
158
- closeAttachmentPreview: string;
159
- retryAttachment: (name: string) => string;
160
- retryUpload: string;
161
- removeAttachment: (name: string) => string;
162
- confirmCommand: (name: string) => string;
163
- confirmDescription: (command: SlashCommand) => string;
164
- cancel: string;
165
- runCommand: (name: string) => string;
166
- commands: string;
167
- slashCommandsLabel: string;
168
- modelLabel: string;
169
- close: string;
170
- danger: string;
171
- draftConflict: string;
172
- useOtherDraft: string;
173
- keepMine: string;
174
- savingDraft: string;
175
- formatBytes: (bytes: number) => string;
176
- formatRelativeTime: (changedAt: string) => string;
177
- };
178
-
179
- export const defaultChatComposerMessages: ChatComposerMessages = {
180
- messagePlaceholder: "Message the agent…",
181
- pausedPlaceholder: "Message the agent — it will wait in the queue…",
182
- inputLabel: "Message the agent",
183
- // Shortcuts live in the send button's title; the footer stays quiet unless
184
- // the host supplies its own hint.
185
- keyboardHint: "",
186
- slashCommandBlocked:
187
- "That's a slash command — press Enter in the command list to run it, or edit the line to send a message.",
188
- controlChangedError:
189
- "This workstream was paused while you were sending. Nothing was sent, and your draft is still here.",
190
- sendFailedError: "Sending failed — your draft is still here. Try again.",
191
- dropFiles: "Drop files to attach",
192
- attachFiles: "Attach files",
193
- pauseAriaLabel: "Pause this workstream",
194
- pauseTitle: "Pause this workstream; queued prompts and approvals are preserved",
195
- stopAriaLabel: "Stop",
196
- stopTitle: "Stop the response",
197
- sendMessageAriaLabel: "Send message",
198
- sendAndResumeAriaLabel: "Add message to queue",
199
- sendTitle: "Queue message (Enter); steer with Cmd/Ctrl+Enter",
200
- sendAndResumeTitle: "Add to queue (Enter); steer now with Cmd/Ctrl+Enter",
201
- annotationNotesRequired: "Add a note to each quote before sending.",
202
- workspacePaused: "Workspace paused",
203
- pausedHere: "Paused here",
204
- parentBlocker: "parent",
205
- narrowerPause: "a narrower pause",
206
- pausedBy: (displayName) => `Paused by ${displayName}`,
207
- queuedAhead: (count) =>
208
- `${count} waiting prompt${count === 1 ? "" : "s"}; your message joins the queue.`,
209
- resumingAndSending: "Sending…",
210
- nextMessageResumes: "Messages wait in the queue until you resume.",
211
- resumeThisWorkstream: "Resume this workstream",
212
- resumeShort: "Resume",
213
- hidePauseDetails: "Hide pause details",
214
- showPauseDetails: "Show pause details",
215
- pauseReasonsLabel: "Reasons this workstream is paused",
216
- pausedByLabel: "Paused by ",
217
- alsoPausedByLabel: "Also paused by ",
218
- resumeWorkspace: "Resume workspace",
219
- resumeFromSession: "Resume from this session",
220
- sessionCanRun: "This session will be able to run",
221
- stillPausedBy: (displayName) => `Still paused by ${displayName}`,
222
- restoredResourcesLabel: "Restored prompt resources",
223
- restoredFile: (fileId) => `File ${fileId.slice(0, 8)}`,
224
- removeRestoredResource: (index) => `Remove restored resource ${index + 1}`,
225
- uploading: "Uploading",
226
- uploadFailed: "Upload failed",
227
- previewAttachment: (name) => `Preview ${name}`,
228
- previewUnavailable: "Preview unavailable",
229
- attachmentPreviewLabel: "Attachment preview",
230
- downloadAttachment: (name) => `Download ${name}`,
231
- closeAttachmentPreview: "Close",
232
- retryAttachment: (name) => `Retry ${name}`,
233
- retryUpload: "Retry upload",
234
- removeAttachment: (name) => `Remove ${name}`,
235
- confirmCommand: (name) => `Confirm /${name}`,
236
- confirmDescription: (command) => `Run /${command.name}? ${command.description}`,
237
- cancel: "Cancel",
238
- runCommand: (name) => `Run /${name}`,
239
- commands: "Commands",
240
- slashCommandsLabel: "Slash commands",
241
- modelLabel: "Model",
242
- close: "Close",
243
- danger: "danger",
244
- draftConflict: "This draft changed in another tab. Your local draft is still here.",
245
- useOtherDraft: "Use other draft",
246
- keepMine: "Keep mine",
247
- // Kept for embedder message overrides / back-compat. Routine autosave is
248
- // silent — only draft conflicts surface under the composer.
249
- savingDraft: "Saving draft…",
250
- formatBytes,
251
- formatRelativeTime,
252
- };
111
+ export { defaultChatComposerMessages } from "../composer-messages";
112
+ export type { ChatComposerMessages } from "../composer-messages";
253
113
 
254
114
  export type ComposerSubmitMode = "queue" | "steer";
255
115
  export type ComposerSubmitBlocker =
@@ -1,3 +1,4 @@
1
+ import { GROK_MARK_PATH } from "../model-mark-paths";
1
2
  import type { SVGProps } from "react";
2
3
 
3
4
  /**
@@ -27,7 +28,7 @@ import type { SVGProps } from "react";
27
28
  export function GrokMark(props: SVGProps<SVGSVGElement>) {
28
29
  return (
29
30
  <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
30
- <path d="M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815" />
31
+ <path d={GROK_MARK_PATH} />
31
32
  </svg>
32
33
  );
33
34
  }
@@ -1,5 +1,4 @@
1
1
  import type {
2
- HumanInputAnswer,
3
2
  HumanInputQuestion,
4
3
  SkillRecord,
5
4
  SessionHumanInputRequest,
@@ -9,64 +8,24 @@ import { ChevronDownIcon, ChevronUpIcon, MessageCircleQuestionIcon } from "lucid
9
8
  import { useEffect, useId, useRef, useState, type FormEvent, type ReactNode } from "react";
10
9
  import { cn } from "../lib/cn";
11
10
  import { useErrorMessage } from "../lib/error-message";
11
+ import {
12
+ answersFromDrafts,
13
+ defaultHumanInputFormMessages,
14
+ emptyDraft,
15
+ initialDrafts,
16
+ type HumanInputAnswerDraft,
17
+ type HumanInputFormMessages,
18
+ } from "../human-input-model";
12
19
 
13
- export type HumanInputAnswerDraft = {
14
- values: string[];
15
- other: string;
16
- otherSelected: boolean;
20
+ export {
21
+ answersFromDrafts,
22
+ defaultHumanInputFormMessages,
23
+ type HumanInputAnswerDraft,
24
+ type HumanInputFormMessages,
17
25
  };
18
26
 
19
27
  class SkillReviewVerificationError extends Error {}
20
28
 
21
- export type HumanInputFormMessages = {
22
- title: string;
23
- description: string;
24
- submit: string;
25
- skip: string;
26
- submitting: string;
27
- other: string;
28
- deadlineLabel: string;
29
- formatDeadline: (value: string) => string;
30
- required: string;
31
- otherRequired: string;
32
- minSelections: (count: number) => string;
33
- maxSelections: (count: number) => string;
34
- optional: string;
35
- /** Shown when the question list overflows the card and more content is below. */
36
- moreBelow: string;
37
- questionCount: (count: number) => string;
38
- collapse: string;
39
- expand: string;
40
- selectionHint: (min: number | null | undefined, max: number | null | undefined) => string | null;
41
- };
42
-
43
- export const defaultHumanInputFormMessages: HumanInputFormMessages = {
44
- title: "Input required",
45
- /** Multi-question chrome has no default subtitle; hosts may still override. */
46
- description: "",
47
- submit: "Send answers",
48
- skip: "Skip",
49
- submitting: "Submitting…",
50
- other: "Other",
51
- deadlineLabel: "Expires",
52
- formatDeadline,
53
- required: "This question is required.",
54
- otherRequired: "Enter a value for Other.",
55
- minSelections: (count) => `Choose at least ${count} option${count === 1 ? "" : "s"}.`,
56
- maxSelections: (count) => `Choose no more than ${count} option${count === 1 ? "" : "s"}.`,
57
- optional: "Optional",
58
- moreBelow: "More below",
59
- questionCount: (count) => `${count} questions`,
60
- collapse: "Collapse",
61
- expand: "Expand",
62
- selectionHint: (min, max) => {
63
- if (min != null && max != null) return `Choose ${min}–${max}.`;
64
- if (min != null) return `Choose at least ${min}.`;
65
- if (max != null) return `Choose up to ${max}.`;
66
- return null;
67
- },
68
- };
69
-
70
29
  export type HumanInputFormProps = {
71
30
  /** Scoped immutable revision reader. Saving a Skill fails closed without its full preview. */
72
31
  loadSkillReview?:
@@ -1006,73 +965,3 @@ function QuestionControls({
1006
965
  </>
1007
966
  );
1008
967
  }
1009
-
1010
- export function answersFromDrafts(
1011
- questions: HumanInputQuestion[],
1012
- drafts: Record<string, HumanInputAnswerDraft>,
1013
- messageOverrides: Partial<HumanInputFormMessages> = {},
1014
- ): { answers: HumanInputAnswer[]; errors: Record<string, string> } {
1015
- const messages = { ...defaultHumanInputFormMessages, ...messageOverrides };
1016
- const answers: HumanInputAnswer[] = [];
1017
- const errors: Record<string, string> = {};
1018
- for (const question of questions) {
1019
- const draft = drafts[question.id] ?? emptyDraft();
1020
- const values = question.kind === "text" ? draft.values.filter(Boolean) : draft.values;
1021
- const other = draft.otherSelected ? draft.other : "";
1022
- const hasOther = Boolean(other.trim());
1023
- const supplied = values.length + (hasOther ? 1 : 0);
1024
-
1025
- // Other-selected-but-empty must win over generic "required" — otherwise the
1026
- // user sees the wrong diagnosis next to a clearly selected control.
1027
- if (question.kind !== "text" && draft.otherSelected && !hasOther) {
1028
- errors[question.id] = messages.otherRequired;
1029
- continue;
1030
- }
1031
-
1032
- if (question.required && supplied === 0) {
1033
- errors[question.id] = messages.required;
1034
- continue;
1035
- }
1036
- if (question.kind !== "text") {
1037
- const min = question.validation?.minSelections;
1038
- const max = question.kind === "single_select" ? 1 : question.validation?.maxSelections;
1039
- if (min != null && supplied < min) {
1040
- errors[question.id] = messages.minSelections(min);
1041
- continue;
1042
- }
1043
- if (max != null && supplied > max) {
1044
- errors[question.id] = messages.maxSelections(max);
1045
- continue;
1046
- }
1047
- }
1048
- if (supplied > 0) {
1049
- answers.push({
1050
- questionId: question.id,
1051
- values,
1052
- ...(hasOther ? { other } : {}),
1053
- });
1054
- }
1055
- }
1056
- return { answers, errors };
1057
- }
1058
-
1059
- function initialDrafts(questions: HumanInputQuestion[]): Record<string, HumanInputAnswerDraft> {
1060
- return Object.fromEntries(questions.map((question) => [question.id, emptyDraft()]));
1061
- }
1062
-
1063
- function emptyDraft(): HumanInputAnswerDraft {
1064
- return { values: [], other: "", otherSelected: false };
1065
- }
1066
-
1067
- function formatDeadline(value: string): string {
1068
- const date = new Date(value);
1069
- if (Number.isNaN(date.getTime())) return value;
1070
- const ms = date.getTime() - Date.now();
1071
- if (ms <= 0) return "deadline passed";
1072
- const minutes = Math.round(ms / 60_000);
1073
- if (minutes < 1) return "in under a minute";
1074
- if (minutes < 60) return `in ${minutes}m`;
1075
- const hours = Math.round(minutes / 60);
1076
- if (hours < 48) return `in ${hours}h`;
1077
- return date.toLocaleString();
1078
- }
@@ -28,6 +28,7 @@ import { MOTION_INSPECT_SCALE } from "../lib/motion-inspect";
28
28
  import { CopyButton } from "./copy-button";
29
29
  import { PreviewLoading } from "./preview-loading";
30
30
  import type { observeMarkdownTableLayout } from "./markdown-table-layout";
31
+ import { remarkSoftLineBreaks } from "./remark-soft-line-breaks";
31
32
  import { softenStreamingMarkdown } from "./soften-streaming-markdown";
32
33
  import { createStreamReveal, rehypeStreamReveal, type StreamReveal } from "./stream-reveal";
33
34
  import { TooltipProvider } from "./tooltip";
@@ -81,6 +82,14 @@ export type MarkdownProps = {
81
82
  renderInteractiveBlock?: ((block: MarkdownInteractiveBlock) => ReactNode) | undefined;
82
83
  children: string;
83
84
  className?: string | undefined;
85
+ /**
86
+ * Render a single newline inside prose as a line break (`<br>`), the way chat
87
+ * apps show messages; CommonMark otherwise folds it into a space. Chat message
88
+ * bodies enable this. Leave it off for documents and other authored Markdown.
89
+ * Code (fenced and inline), lists, tables, and blank-line paragraphs are
90
+ * unaffected either way.
91
+ */
92
+ softLineBreaks?: boolean | undefined;
84
93
  /**
85
94
  * While true, newly arrived source fades in through tip ink (`.og-stream-ink`):
86
95
  * an age window over each append batch — fast streams keep a large soft band,
@@ -630,10 +639,19 @@ function MarkdownTable({ children, className, ...props }: ComponentPropsWithoutR
630
639
  );
631
640
  }
632
641
 
642
+ type RemarkPlugins = NonNullable<Parameters<typeof ReactMarkdown>[0]["remarkPlugins"]>;
643
+ const REMARK_PLUGINS: RemarkPlugins = [remarkGfm];
644
+ // Structural plugin types (see remark-soft-line-breaks.ts) need one cast at the seam.
645
+ const SOFT_LINE_BREAK_REMARK_PLUGINS = [
646
+ remarkGfm,
647
+ remarkSoftLineBreaks,
648
+ ] as unknown as RemarkPlugins;
649
+
633
650
  function MarkdownImpl({
634
651
  children,
635
652
  artifactHref,
636
653
  className,
654
+ softLineBreaks = false,
637
655
  streaming = false,
638
656
  onSandboxFile,
639
657
  renderInteractiveBlock,
@@ -760,7 +778,7 @@ function MarkdownImpl({
760
778
  )}
761
779
  >
762
780
  <ReactMarkdown
763
- remarkPlugins={[remarkGfm]}
781
+ remarkPlugins={softLineBreaks ? SOFT_LINE_BREAK_REMARK_PLUGINS : REMARK_PLUGINS}
764
782
  rehypePlugins={rehypePlugins}
765
783
  components={markdownComponents}
766
784
  urlTransform={markdownUrlTransform}