@athenaintel/react 0.53.1 → 0.55.0

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 (196) hide show
  1. package/README.md +37 -1
  2. package/dist/agent-runtime/src/call-recap.cjs +2 -3
  3. package/dist/agent-runtime/src/call-recap.cjs.map +1 -1
  4. package/dist/agent-runtime/src/call-recap.d.ts +1 -2
  5. package/dist/agent-runtime/src/call-recap.js +1 -2
  6. package/dist/agent-runtime/src/call-recap.js.map +1 -1
  7. package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +7 -3
  8. package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
  9. package/dist/agent-runtime/src/deep-agent-transport-recipe.d.ts +10 -2
  10. package/dist/agent-runtime/src/deep-agent-transport-recipe.js +8 -4
  11. package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
  12. package/dist/agent-runtime/src/dictation-session.d.ts +158 -0
  13. package/dist/agent-runtime/src/edit-command.cjs +15 -3
  14. package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
  15. package/dist/agent-runtime/src/edit-command.d.ts +8 -0
  16. package/dist/agent-runtime/src/edit-command.js +15 -3
  17. package/dist/agent-runtime/src/edit-command.js.map +1 -1
  18. package/dist/agent-runtime/src/generated/runconfig-contract.cjs +10 -0
  19. package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  20. package/dist/agent-runtime/src/generated/runconfig-contract.d.ts +25 -1
  21. package/dist/agent-runtime/src/generated/runconfig-contract.js +10 -0
  22. package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  23. package/dist/agent-runtime/src/history-pages.cjs +279 -0
  24. package/dist/agent-runtime/src/history-pages.cjs.map +1 -0
  25. package/dist/agent-runtime/src/history-pages.d.ts +252 -0
  26. package/dist/agent-runtime/src/history-pages.js +279 -0
  27. package/dist/agent-runtime/src/history-pages.js.map +1 -0
  28. package/dist/agent-runtime/src/index.d.ts +8 -7
  29. package/dist/agent-runtime/src/message-window.cjs +111 -7
  30. package/dist/agent-runtime/src/message-window.cjs.map +1 -1
  31. package/dist/agent-runtime/src/message-window.d.ts +66 -6
  32. package/dist/agent-runtime/src/message-window.js +112 -8
  33. package/dist/agent-runtime/src/message-window.js.map +1 -1
  34. package/dist/agent-runtime/src/offline-status.cjs.map +1 -1
  35. package/dist/agent-runtime/src/offline-status.d.ts +64 -2
  36. package/dist/agent-runtime/src/offline-status.js.map +1 -1
  37. package/dist/agent-runtime/src/runtime.d.ts +4 -3
  38. package/dist/agent-runtime/src/subagent-transcripts.cjs +23 -1
  39. package/dist/agent-runtime/src/subagent-transcripts.cjs.map +1 -1
  40. package/dist/agent-runtime/src/subagent-transcripts.js +25 -3
  41. package/dist/agent-runtime/src/subagent-transcripts.js.map +1 -1
  42. package/dist/agent-runtime/src/subagents.cjs +112 -0
  43. package/dist/agent-runtime/src/subagents.cjs.map +1 -1
  44. package/dist/agent-runtime/src/subagents.d.ts +103 -0
  45. package/dist/agent-runtime/src/subagents.js +113 -1
  46. package/dist/agent-runtime/src/subagents.js.map +1 -1
  47. package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +37 -9
  48. package/dist/agent-runtime/src/thread-handoff.d.ts +1 -1
  49. package/dist/agent-runtime/src/tool-metadata.cjs +8 -0
  50. package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
  51. package/dist/agent-runtime/src/tool-metadata.js +8 -0
  52. package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
  53. package/dist/agent-runtime/src/use-deep-agent-thread.cjs +22 -2
  54. package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  55. package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +9 -1
  56. package/dist/agent-runtime/src/use-deep-agent-thread.js +23 -3
  57. package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  58. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +106 -25
  59. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  60. package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +41 -6
  61. package/dist/agent-runtime/src/use-deep-agent-window-sync.js +109 -28
  62. package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
  63. package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +5 -9
  64. package/dist/agent-runtime/src/voice-presentation.cjs +34 -0
  65. package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
  66. package/dist/agent-runtime/src/voice-presentation.d.ts +35 -0
  67. package/dist/agent-runtime/src/voice-presentation.js +34 -0
  68. package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
  69. package/dist/assets/AthenaAssetEmbed.cjs +4 -2
  70. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  71. package/dist/assets/AthenaAssetEmbed.d.ts +17 -8
  72. package/dist/assets/AthenaAssetEmbed.js +4 -2
  73. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  74. package/dist/assets/use-asset-embed.cjs.map +1 -1
  75. package/dist/assets/use-asset-embed.d.ts +7 -5
  76. package/dist/assets/use-asset-embed.js.map +1 -1
  77. package/dist/auth/account-identity.cjs +69 -0
  78. package/dist/auth/account-identity.cjs.map +1 -0
  79. package/dist/auth/account-identity.d.ts +57 -0
  80. package/dist/auth/account-identity.js +69 -0
  81. package/dist/auth/account-identity.js.map +1 -0
  82. package/dist/chat/AthenaChat.cjs +1 -1
  83. package/dist/chat/AthenaChat.cjs.map +1 -1
  84. package/dist/chat/AthenaChat.js +1 -1
  85. package/dist/chat/AthenaChat.js.map +1 -1
  86. package/dist/chat/super-grouping/SuperGroupingCard.cjs +51 -43
  87. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  88. package/dist/chat/super-grouping/SuperGroupingCard.js +51 -43
  89. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  90. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +9 -4
  91. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  92. package/dist/chat/super-grouping/SuperGroupingTranscript.js +9 -4
  93. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  94. package/dist/chat/super-grouping/activity-timeline.cjs +7 -4
  95. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  96. package/dist/chat/super-grouping/activity-timeline.d.ts +10 -1
  97. package/dist/chat/super-grouping/activity-timeline.js +7 -4
  98. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  99. package/dist/chat/super-grouping/earlier-steps-button.cjs +50 -0
  100. package/dist/chat/super-grouping/earlier-steps-button.cjs.map +1 -0
  101. package/dist/chat/super-grouping/earlier-steps-button.d.ts +11 -0
  102. package/dist/chat/super-grouping/earlier-steps-button.js +50 -0
  103. package/dist/chat/super-grouping/earlier-steps-button.js.map +1 -0
  104. package/dist/chat/super-grouping/toolkit-map.cjs +1 -0
  105. package/dist/chat/super-grouping/toolkit-map.cjs.map +1 -1
  106. package/dist/chat/super-grouping/toolkit-map.js +1 -0
  107. package/dist/chat/super-grouping/toolkit-map.js.map +1 -1
  108. package/dist/chat/super-grouping/use-turn-steps.cjs +28 -0
  109. package/dist/chat/super-grouping/use-turn-steps.cjs.map +1 -0
  110. package/dist/chat/super-grouping/use-turn-steps.d.ts +23 -0
  111. package/dist/chat/super-grouping/use-turn-steps.js +28 -0
  112. package/dist/chat/super-grouping/use-turn-steps.js.map +1 -0
  113. package/dist/chat/use-statewire-approval-request.cjs +5 -5
  114. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  115. package/dist/chat/use-statewire-approval-request.d.ts +7 -0
  116. package/dist/chat/use-statewire-approval-request.js +5 -5
  117. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  118. package/dist/chat-ui/mentions/use-account-workspace-id.cjs +18 -58
  119. package/dist/chat-ui/mentions/use-account-workspace-id.cjs.map +1 -1
  120. package/dist/chat-ui/mentions/use-account-workspace-id.js +18 -58
  121. package/dist/chat-ui/mentions/use-account-workspace-id.js.map +1 -1
  122. package/dist/chat-ui/messages/voice-delegation-card.cjs +93 -25
  123. package/dist/chat-ui/messages/voice-delegation-card.cjs.map +1 -1
  124. package/dist/chat-ui/messages/voice-delegation-card.d.ts +16 -7
  125. package/dist/chat-ui/messages/voice-delegation-card.js +95 -27
  126. package/dist/chat-ui/messages/voice-delegation-card.js.map +1 -1
  127. package/dist/chat-ui/statewire.cjs +6 -0
  128. package/dist/chat-ui/statewire.cjs.map +1 -1
  129. package/dist/chat-ui/statewire.d.ts +3 -1
  130. package/dist/chat-ui/statewire.js +7 -1
  131. package/dist/chat-ui/statewire.js.map +1 -1
  132. package/dist/chat-ui/thread/handoff-cursor.cjs +2 -2
  133. package/dist/chat-ui/thread/handoff-cursor.cjs.map +1 -1
  134. package/dist/chat-ui/thread/handoff-cursor.js +3 -3
  135. package/dist/chat-ui/thread/handoff-cursor.js.map +1 -1
  136. package/dist/chat-ui/thread/statewire-history-edge.cjs +4 -0
  137. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  138. package/dist/chat-ui/thread/statewire-history-edge.d.ts +11 -1
  139. package/dist/chat-ui/thread/statewire-history-edge.js +5 -1
  140. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  141. package/dist/collab/client.cjs +134 -7
  142. package/dist/collab/client.cjs.map +1 -1
  143. package/dist/collab/client.d.ts +44 -0
  144. package/dist/collab/client.js +134 -7
  145. package/dist/collab/client.js.map +1 -1
  146. package/dist/collab/index.d.ts +1 -1
  147. package/dist/composer/QuoteContext.cjs +3 -3
  148. package/dist/composer/QuoteContext.cjs.map +1 -1
  149. package/dist/composer/QuoteContext.js +3 -3
  150. package/dist/composer/QuoteContext.js.map +1 -1
  151. package/dist/keryx-http/src/initial-sync.cjs +121 -0
  152. package/dist/keryx-http/src/initial-sync.cjs.map +1 -0
  153. package/dist/keryx-http/src/initial-sync.d.ts +77 -0
  154. package/dist/keryx-http/src/initial-sync.js +104 -0
  155. package/dist/keryx-http/src/initial-sync.js.map +1 -0
  156. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs +8 -0
  157. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs.map +1 -1
  158. package/dist/keryx-http/src/unconfirmed-update-attributions.d.ts +6 -0
  159. package/dist/keryx-http/src/unconfirmed-update-attributions.js +8 -0
  160. package/dist/keryx-http/src/unconfirmed-update-attributions.js.map +1 -1
  161. package/dist/provider/AthenaProvider.cjs +11 -3
  162. package/dist/provider/AthenaProvider.cjs.map +1 -1
  163. package/dist/provider/AthenaProvider.js +12 -4
  164. package/dist/provider/AthenaProvider.js.map +1 -1
  165. package/dist/provider/statewire-window-context.cjs +6 -0
  166. package/dist/provider/statewire-window-context.cjs.map +1 -1
  167. package/dist/provider/statewire-window-context.d.ts +10 -1
  168. package/dist/provider/statewire-window-context.js +6 -0
  169. package/dist/provider/statewire-window-context.js.map +1 -1
  170. package/dist/react/package.json.cjs +1 -1
  171. package/dist/react/package.json.js +1 -1
  172. package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -0
  173. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  174. package/dist/runtime/useAthenaStatewireRuntime.d.ts +7 -1
  175. package/dist/runtime/useAthenaStatewireRuntime.js +2 -0
  176. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  177. package/dist/styles.css +1 -1
  178. package/dist/threads/api.cjs +1 -2
  179. package/dist/threads/api.cjs.map +1 -1
  180. package/dist/threads/api.d.ts +3 -2
  181. package/dist/threads/api.js +1 -2
  182. package/dist/threads/api.js.map +1 -1
  183. package/dist/threads/sessions-query.cjs +2 -0
  184. package/dist/threads/sessions-query.cjs.map +1 -1
  185. package/dist/threads/sessions-query.js +2 -0
  186. package/dist/threads/sessions-query.js.map +1 -1
  187. package/dist/threads/statewire-thread-list.cjs +5 -7
  188. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  189. package/dist/threads/statewire-thread-list.js +7 -9
  190. package/dist/threads/statewire-thread-list.js.map +1 -1
  191. package/dist/tools/subagent-task-tool-ui.cjs +15 -5
  192. package/dist/tools/subagent-task-tool-ui.cjs.map +1 -1
  193. package/dist/tools/subagent-task-tool-ui.d.ts +5 -4
  194. package/dist/tools/subagent-task-tool-ui.js +16 -6
  195. package/dist/tools/subagent-task-tool-ui.js.map +1 -1
  196. package/package.json +1 -1
@@ -1,64 +1,24 @@
1
- import { useQuery } from "@tanstack/react-query";
2
- import { useAthenaCredentialIdentityEpoch } from "../../auth/AthenaCredentialSourceContext.js";
3
- import { jwtSubject, jwtScope } from "../../auth/credential-source.js";
1
+ import { useAccountIdentityQuery } from "../../auth/account-identity.js";
4
2
  import { useCatalogQueryClient } from "../../catalog/runtime-catalog.js";
5
- import { keyPartFingerprint } from "../../lib/use-athena-rest-query.js";
6
- import { withCredentialRenewal } from "../../runtime/credential-renewal.js";
7
- import { fetchAccountIdentity } from "../../threads/api.js";
8
- const tokenGetterIds = /* @__PURE__ */ new WeakMap();
9
- let lastTokenGetterId = 0;
10
- function tokenGetterId(getToken) {
11
- if (getToken === void 0) return "";
12
- let id = tokenGetterIds.get(getToken);
13
- if (id === void 0) {
14
- lastTokenGetterId += 1;
15
- id = lastTokenGetterId;
16
- tokenGetterIds.set(getToken, id);
17
- }
18
- return `getter:${id}`;
19
- }
20
- function heldTokenIdentity(token) {
21
- if (!token) return "";
22
- const subject = jwtSubject(token);
23
- if (subject === null) return `token:${keyPartFingerprint(token)}`;
24
- const scope = jwtScope(token);
25
- return scope === null ? `sub:${keyPartFingerprint(subject)}` : `sub:${keyPartFingerprint(subject)}|scope:${keyPartFingerprint(scope)}`;
26
- }
27
3
  function useAccountIdentityRead(auth, { enabled, tokenGetter }) {
28
- const queryClient = useCatalogQueryClient();
29
- const identityEpoch = useAthenaCredentialIdentityEpoch();
30
- const query = useQuery(
31
- {
32
- queryKey: [
33
- "athena-sdk",
34
- "account",
35
- (auth == null ? void 0 : auth.backendUrl) ?? "",
36
- (auth == null ? void 0 : auth.userId) ?? "",
37
- (auth == null ? void 0 : auth.apiKey) ? keyPartFingerprint(auth.apiKey) : "",
38
- tokenGetterId(tokenGetter),
39
- heldTokenIdentity(auth == null ? void 0 : auth.accessToken),
40
- identityEpoch
41
- ],
42
- queryFn: async ({ signal }) => {
43
- if (!auth) return null;
44
- return withCredentialRenewal(
45
- async ({ force } = {}) => {
46
- var _a, _b;
47
- return {
48
- token: (force ? await ((_a = auth.renewAccessToken) == null ? void 0 : _a.call(auth)) : await ((_b = auth.getAccessToken) == null ? void 0 : _b.call(auth))) ?? auth.accessToken ?? null,
49
- apiKey: auth.apiKey,
50
- credentials: auth.credentials
51
- };
52
- },
53
- (resolved) => fetchAccountIdentity(auth.backendUrl, resolved, { signal })
54
- );
55
- },
56
- enabled: auth !== null && enabled,
57
- retry: false,
58
- staleTime: 6e4
4
+ const query = useAccountIdentityQuery({
5
+ credential: {
6
+ backendUrl: (auth == null ? void 0 : auth.backendUrl) ?? "",
7
+ principal: auth == null ? void 0 : auth.userId,
8
+ apiKey: auth == null ? void 0 : auth.apiKey,
9
+ heldToken: auth == null ? void 0 : auth.accessToken,
10
+ tokenGetter
11
+ },
12
+ resolveAuth: async ({ force } = {}) => {
13
+ var _a, _b;
14
+ return {
15
+ token: (force ? await ((_a = auth == null ? void 0 : auth.renewAccessToken) == null ? void 0 : _a.call(auth)) : await ((_b = auth == null ? void 0 : auth.getAccessToken) == null ? void 0 : _b.call(auth))) ?? (auth == null ? void 0 : auth.accessToken) ?? null,
16
+ apiKey: auth == null ? void 0 : auth.apiKey
17
+ };
59
18
  },
60
- queryClient
61
- );
19
+ enabled: auth !== null && enabled,
20
+ queryClient: useCatalogQueryClient()
21
+ });
62
22
  return query.data;
63
23
  }
64
24
  function useAccountIdentity(auth, options = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-account-workspace-id.js","sources":["../../../src/chat-ui/mentions/use-account-workspace-id.ts"],"sourcesContent":["import { useQuery } from '@tanstack/react-query';\nimport { useAthenaCredentialIdentityEpoch } from '../../auth/AthenaCredentialSourceContext';\nimport { jwtScope, jwtSubject } from '../../auth/credential-source';\nimport { useCatalogQueryClient } from '../../catalog/runtime-catalog';\nimport { keyPartFingerprint } from '../../lib/use-athena-rest-query';\nimport { withCredentialRenewal } from '../../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../../runtime/statewire-auth-headers';\nimport { type AccountIdentity, fetchAccountIdentity } from '../../threads/api';\nimport type { MentionAuthContext } from './suggestions/data/api-client';\n\n/** The credential's agora identity, as far as it is known yet. */\nexport interface AccountIdentityState {\n /** Agora's user id (`user_…`) for the credential; unknown until `/me` answers. */\n userId: string | undefined;\n /** The host's workspace when it names one, else the credential's current workspace. */\n workspaceId: string | undefined;\n}\n\nexport interface AccountIdentityOptions {\n /**\n * The provider's token getter, which `auth.getAccessToken` wraps (`AthenaChat`\n * passes `useAthenaConfig().getToken`). Its identity keeps two providers\n * apart when only their getters know who they sign in.\n */\n tokenGetter?: AthenaStatewireTokenGetter;\n}\n\n/** One id per token getter, so the key can name a getter without holding it. */\nconst tokenGetterIds = new WeakMap<AthenaStatewireTokenGetter, number>();\nlet lastTokenGetterId = 0;\n\nfunction tokenGetterId(getToken: AthenaStatewireTokenGetter | undefined): string {\n if (getToken === undefined) return '';\n let id = tokenGetterIds.get(getToken);\n if (id === undefined) {\n lastTokenGetterId += 1;\n id = lastTokenGetterId;\n tokenGetterIds.set(getToken, id);\n }\n return `getter:${id}`;\n}\n\n/**\n * Who a held token acts as, and where, without the token: its JWT subject and\n * the claims scoping it to a workspace or org (`jwtScope`), or the token\n * itself when it is opaque, fingerprinted. A renewal for the same person and\n * scope keeps it, so a rotation is not a new identity; the same person's token\n * scoped to another workspace is.\n */\nfunction heldTokenIdentity(token: string | null | undefined): string {\n if (!token) return '';\n const subject = jwtSubject(token);\n if (subject === null) return `token:${keyPartFingerprint(token)}`;\n const scope = jwtScope(token);\n return scope === null\n ? `sub:${keyPartFingerprint(subject)}`\n : `sub:${keyPartFingerprint(subject)}|scope:${keyPartFingerprint(scope)}`;\n}\n\n/**\n * One `/me` read per credential, through the host's query cache (never a\n * process-wide user-id map), keyed by the credential the read is sent with and\n * never by the secret: the backend, the signed-in person, the API key's\n * fingerprint, the provider's token getter, who the held token acts as and\n * which workspace it is scoped to, and the provider's credential identity\n * epoch (`useAthenaCredentialIdentityEpoch`), which moves once the getter has\n * handed out a token that acts as someone else. A renewal for the same person\n * and scope keeps the key, so the answer stays put; an API-key switch, a held\n * token scoped to another workspace, or a getter now signing in someone else,\n * reads `/me` again under a new key, never a cached answer for the previous\n * credential. The epoch follows the person, not the scope: a host getter that\n * alone hands out the same person's token for another workspace is read again\n * once the answer goes stale. `enabled` is the caller's: a reader that only\n * needs the workspace skips the read when the host names one.\n */\nfunction useAccountIdentityRead(\n auth: MentionAuthContext | null,\n { enabled, tokenGetter }: { enabled: boolean } & AccountIdentityOptions\n): AccountIdentity | null | undefined {\n const queryClient = useCatalogQueryClient();\n const identityEpoch = useAthenaCredentialIdentityEpoch();\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'account',\n auth?.backendUrl ?? '',\n auth?.userId ?? '',\n auth?.apiKey ? keyPartFingerprint(auth.apiKey) : '',\n tokenGetterId(tokenGetter),\n heldTokenIdentity(auth?.accessToken),\n identityEpoch,\n ],\n queryFn: async ({ signal }) => {\n if (!auth) return null;\n return withCredentialRenewal(\n async ({ force }: { force?: boolean } = {}) => ({\n token:\n (force ? await auth.renewAccessToken?.() : await auth.getAccessToken?.()) ??\n auth.accessToken ??\n null,\n apiKey: auth.apiKey,\n credentials: auth.credentials,\n }),\n (resolved) => fetchAccountIdentity(auth.backendUrl, resolved, { signal })\n );\n },\n enabled: auth !== null && enabled,\n retry: false,\n staleTime: 60_000,\n },\n queryClient\n );\n return query.data;\n}\n\n/** The workspace mentions search: the host's when it names one, else the credential's own. */\nexport function useAccountWorkspaceId(auth: MentionAuthContext | null): string | undefined {\n const identity = useAccountIdentityRead(auth, { enabled: !auth?.workspaceId });\n return auth?.workspaceId ?? identity?.workspaceId ?? undefined;\n}\n\n/**\n * The credential's agora user id and workspace from the same read. The auth\n * context carries the identity provider's user id, not agora's, so \"who is\n * the viewer\" (an answer label's \"for you\") needs this one, and it reads\n * `/me` even when the host names a workspace.\n */\nexport function useAccountIdentity(\n auth: MentionAuthContext | null,\n options: AccountIdentityOptions = {}\n): AccountIdentityState {\n const identity = useAccountIdentityRead(auth, { enabled: true, ...options });\n return {\n userId: identity?.userId,\n workspaceId: auth?.workspaceId ?? identity?.workspaceId ?? undefined,\n };\n}\n"],"names":[],"mappings":";;;;;;;AA4BA,MAAM,qCAAqB,QAAA;AAC3B,IAAI,oBAAoB;AAExB,SAAS,cAAc,UAA0D;AAC/E,MAAI,aAAa,OAAW,QAAO;AACnC,MAAI,KAAK,eAAe,IAAI,QAAQ;AACpC,MAAI,OAAO,QAAW;AACpB,yBAAqB;AACrB,SAAK;AACL,mBAAe,IAAI,UAAU,EAAE;AAAA,EACjC;AACA,SAAO,UAAU,EAAE;AACrB;AASA,SAAS,kBAAkB,OAA0C;AACnE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,WAAW,KAAK;AAChC,MAAI,YAAY,KAAM,QAAO,SAAS,mBAAmB,KAAK,CAAC;AAC/D,QAAM,QAAQ,SAAS,KAAK;AAC5B,SAAO,UAAU,OACb,OAAO,mBAAmB,OAAO,CAAC,KAClC,OAAO,mBAAmB,OAAO,CAAC,UAAU,mBAAmB,KAAK,CAAC;AAC3E;AAkBA,SAAS,uBACP,MACA,EAAE,SAAS,eACyB;AACpC,QAAM,cAAc,sBAAA;AACpB,QAAM,gBAAgB,iCAAA;AACtB,QAAM,QAAQ;AAAA,IACZ;AAAA,MACE,UAAU;AAAA,QACR;AAAA,QACA;AAAA,SACA,6BAAM,eAAc;AAAA,SACpB,6BAAM,WAAU;AAAA,SAChB,6BAAM,UAAS,mBAAmB,KAAK,MAAM,IAAI;AAAA,QACjD,cAAc,WAAW;AAAA,QACzB,kBAAkB,6BAAM,WAAW;AAAA,QACnC;AAAA,MAAA;AAAA,MAEF,SAAS,OAAO,EAAE,aAAa;AAC7B,YAAI,CAAC,KAAM,QAAO;AAClB,eAAO;AAAA,UACL,OAAO,EAAE,MAAA,IAA+B,OAAC;;AAAO;AAAA,cAC9C,QACG,QAAQ,QAAM,UAAK,qBAAL,iCAA4B,QAAM,UAAK,mBAAL,mCACjD,KAAK,eACL;AAAA,cACF,QAAQ,KAAK;AAAA,cACb,aAAa,KAAK;AAAA,YAAA;AAAA;AAAA,UAEpB,CAAC,aAAa,qBAAqB,KAAK,YAAY,UAAU,EAAE,QAAQ;AAAA,QAAA;AAAA,MAE5E;AAAA,MACA,SAAS,SAAS,QAAQ;AAAA,MAC1B,OAAO;AAAA,MACP,WAAW;AAAA,IAAA;AAAA,IAEb;AAAA,EAAA;AAEF,SAAO,MAAM;AACf;AAcO,SAAS,mBACd,MACA,UAAkC,IACZ;AACtB,QAAM,WAAW,uBAAuB,MAAM,EAAE,SAAS,MAAM,GAAG,SAAS;AAC3E,SAAO;AAAA,IACL,QAAQ,qCAAU;AAAA,IAClB,cAAa,6BAAM,iBAAe,qCAAU,gBAAe;AAAA,EAAA;AAE/D;"}
1
+ {"version":3,"file":"use-account-workspace-id.js","sources":["../../../src/chat-ui/mentions/use-account-workspace-id.ts"],"sourcesContent":["import { useAccountIdentityQuery } from '../../auth/account-identity';\nimport { useCatalogQueryClient } from '../../catalog/runtime-catalog';\nimport type { AthenaStatewireTokenGetter } from '../../runtime/statewire-auth-headers';\nimport type { AccountIdentity } from '../../threads/api';\nimport type { MentionAuthContext } from './suggestions/data/api-client';\n\n/** The credential's agora identity, as far as it is known yet. */\nexport interface AccountIdentityState {\n /** Agora's user id (`user_…`) for the credential; unknown until `/me` answers. */\n userId: string | undefined;\n /** The host's workspace when it names one, else the credential's current workspace. */\n workspaceId: string | undefined;\n}\n\nexport interface AccountIdentityOptions {\n /**\n * The provider's token getter, which `auth.getAccessToken` wraps (`AthenaChat`\n * passes `useAthenaConfig().getToken`). Its identity keeps two providers\n * apart when only their getters know who they sign in.\n */\n tokenGetter?: AthenaStatewireTokenGetter;\n}\n\n/**\n * The chat's read of the credential's identity (`useAccountIdentityQuery`),\n * through the host's query cache: inside a provider with a session list, the\n * list's own read under the same key, so the page sends one `/me`. `enabled`\n * is the caller's: a reader that only needs the workspace skips the read when\n * the host names one.\n */\nfunction useAccountIdentityRead(\n auth: MentionAuthContext | null,\n { enabled, tokenGetter }: { enabled: boolean } & AccountIdentityOptions\n): AccountIdentity | undefined {\n const query = useAccountIdentityQuery({\n credential: {\n backendUrl: auth?.backendUrl ?? '',\n principal: auth?.userId,\n apiKey: auth?.apiKey,\n heldToken: auth?.accessToken,\n tokenGetter,\n },\n resolveAuth: async ({ force }: { force?: boolean } = {}) => ({\n token:\n (force ? await auth?.renewAccessToken?.() : await auth?.getAccessToken?.()) ??\n auth?.accessToken ??\n null,\n apiKey: auth?.apiKey,\n }),\n enabled: auth !== null && enabled,\n queryClient: useCatalogQueryClient(),\n });\n return query.data;\n}\n\n/** The workspace mentions search: the host's when it names one, else the credential's own. */\nexport function useAccountWorkspaceId(auth: MentionAuthContext | null): string | undefined {\n const identity = useAccountIdentityRead(auth, { enabled: !auth?.workspaceId });\n return auth?.workspaceId ?? identity?.workspaceId ?? undefined;\n}\n\n/**\n * The credential's agora user id and workspace from the same read. The auth\n * context carries the identity provider's user id, not agora's, so \"who is\n * the viewer\" (an answer label's \"for you\") needs this one, and it reads\n * `/me` even when the host names a workspace.\n */\nexport function useAccountIdentity(\n auth: MentionAuthContext | null,\n options: AccountIdentityOptions = {}\n): AccountIdentityState {\n const identity = useAccountIdentityRead(auth, { enabled: true, ...options });\n return {\n userId: identity?.userId,\n workspaceId: auth?.workspaceId ?? identity?.workspaceId ?? undefined,\n };\n}\n"],"names":[],"mappings":";;AA8BA,SAAS,uBACP,MACA,EAAE,SAAS,eACkB;AAC7B,QAAM,QAAQ,wBAAwB;AAAA,IACpC,YAAY;AAAA,MACV,aAAY,6BAAM,eAAc;AAAA,MAChC,WAAW,6BAAM;AAAA,MACjB,QAAQ,6BAAM;AAAA,MACd,WAAW,6BAAM;AAAA,MACjB;AAAA,IAAA;AAAA,IAEF,aAAa,OAAO,EAAE,MAAA,IAA+B,OAAC;;AAAO;AAAA,QAC3D,QACG,QAAQ,QAAM,kCAAM,qBAAN,iCAA6B,QAAM,kCAAM,mBAAN,oCAClD,6BAAM,gBACN;AAAA,QACF,QAAQ,6BAAM;AAAA,MAAA;AAAA;AAAA,IAEhB,SAAS,SAAS,QAAQ;AAAA,IAC1B,aAAa,sBAAA;AAAA,EAAsB,CACpC;AACD,SAAO,MAAM;AACf;AAcO,SAAS,mBACd,MACA,UAAkC,IACZ;AACtB,QAAM,WAAW,uBAAuB,MAAM,EAAE,SAAS,MAAM,GAAG,SAAS;AAC3E,SAAO;AAAA,IACL,QAAQ,qCAAU;AAAA,IAClB,cAAa,6BAAM,iBAAe,qCAAU,gBAAe;AAAA,EAAA;AAE/D;"}
@@ -3,33 +3,87 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const react = require("@assistant-ui/react");
5
5
  const lucideReact = require("lucide-react");
6
+ const require$$0 = require("react");
6
7
  const useAthenaRunLive = require("../../chat/use-athena-run-live.cjs");
8
+ const voicePresentation = require("../../agent-runtime/src/voice-presentation.cjs");
7
9
  const cnfast = require("cnfast");
8
- const VoiceDelegationCard = ({ text, live, className }) => /* @__PURE__ */ jsxRuntime.jsx(
9
- "div",
10
- {
11
- className: cnfast.cn("flex justify-end px-1 pb-0.5", className),
12
- "data-testid": "voice-delegation-card",
13
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2", children: [
14
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center gap-1.5", children: [
15
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex size-4 items-center justify-center rounded bg-primary/15", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AudioLinesIcon, { className: "size-2.5 text-primary", "aria-hidden": true }) }),
16
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-primary", children: "Voice task" }),
17
- live ? /* @__PURE__ */ jsxRuntime.jsxs(
18
- "span",
19
- {
20
- className: "inline-flex items-center gap-1 text-[10px] text-primary/80",
21
- "data-testid": "voice-delegation-live",
22
- children: [
23
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LoaderIcon, { className: "size-2.5 animate-spin", "aria-hidden": true }),
24
- "running"
25
- ]
26
- }
27
- ) : null
28
- ] }),
29
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap break-words text-sm text-foreground", children: text })
30
- ] })
10
+ const VoiceDelegationHeading = ({ live }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex size-4 items-center justify-center rounded bg-primary/15", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AudioLinesIcon, { className: "size-2.5 text-primary", "aria-hidden": true }) }),
12
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-primary", children: "Voice task" }),
13
+ live ? /* @__PURE__ */ jsxRuntime.jsxs(
14
+ "span",
15
+ {
16
+ className: "inline-flex items-center gap-1 text-[10px] text-primary/80",
17
+ "data-testid": "voice-delegation-live",
18
+ children: [
19
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LoaderIcon, { className: "size-2.5 animate-spin", "aria-hidden": true }),
20
+ "running"
21
+ ]
22
+ }
23
+ ) : null
24
+ ] });
25
+ const VoiceDelegationCard = ({
26
+ text,
27
+ live,
28
+ echo = false,
29
+ className
30
+ }) => {
31
+ const [open, setOpen] = require$$0.useState(false);
32
+ const textId = require$$0.useId();
33
+ if (echo) {
34
+ return /* @__PURE__ */ jsxRuntime.jsxs(
35
+ "div",
36
+ {
37
+ className: cnfast.cn("flex flex-col items-end px-1 pb-0.5", className),
38
+ "data-testid": "voice-delegation-card",
39
+ "data-echo": "true",
40
+ children: [
41
+ /* @__PURE__ */ jsxRuntime.jsxs(
42
+ "button",
43
+ {
44
+ type: "button",
45
+ className: "inline-flex items-center gap-1.5 rounded-full border border-primary/20 bg-primary/5 px-2 py-0.5 hover:bg-primary/10",
46
+ "aria-expanded": open,
47
+ "aria-controls": textId,
48
+ onClick: () => setOpen((value) => !value),
49
+ "data-testid": "voice-delegation-toggle",
50
+ children: [
51
+ /* @__PURE__ */ jsxRuntime.jsx(VoiceDelegationHeading, { live }),
52
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: open ? "Hide the task" : "Show the task" }),
53
+ /* @__PURE__ */ jsxRuntime.jsx(
54
+ lucideReact.ChevronDownIcon,
55
+ {
56
+ className: cnfast.cn("size-3 text-primary/70 transition-transform", open && "rotate-180"),
57
+ "aria-hidden": true
58
+ }
59
+ )
60
+ ]
61
+ }
62
+ ),
63
+ open ? /* @__PURE__ */ jsxRuntime.jsx(
64
+ "div",
65
+ {
66
+ id: textId,
67
+ className: "mt-1 max-w-[85%] whitespace-pre-wrap break-words rounded-xl border border-primary/20 bg-primary/5 px-3 py-2 text-sm text-foreground",
68
+ children: text
69
+ }
70
+ ) : null
71
+ ]
72
+ }
73
+ );
31
74
  }
32
- );
75
+ return /* @__PURE__ */ jsxRuntime.jsx(
76
+ "div",
77
+ {
78
+ className: cnfast.cn("flex justify-end px-1 pb-0.5", className),
79
+ "data-testid": "voice-delegation-card",
80
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2", children: [
81
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 flex items-center gap-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(VoiceDelegationHeading, { live }) }),
82
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap break-words text-sm text-foreground", children: text })
83
+ ] })
84
+ }
85
+ );
86
+ };
33
87
  const VoiceDelegationMessage = ({ className }) => {
34
88
  const text = react.useAuiState(
35
89
  (s) => s.message.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n")
@@ -43,8 +97,22 @@ const VoiceDelegationMessage = ({ className }) => {
43
97
  }
44
98
  return false;
45
99
  });
100
+ const echo = react.useAuiState(
101
+ (s) => {
102
+ var _a;
103
+ return ((_a = s.thread.messages[s.message.index]) == null ? void 0 : _a.id) === s.message.id && voicePresentation.voiceDelegationEchoesRequest(s.thread.messages, s.message.index);
104
+ }
105
+ );
46
106
  const isRunning = useAthenaRunLive.useAthenaRunLive();
47
- return /* @__PURE__ */ jsxRuntime.jsx(VoiceDelegationCard, { text, live: isRunning && isTrailingUserMessage, className });
107
+ return /* @__PURE__ */ jsxRuntime.jsx(
108
+ VoiceDelegationCard,
109
+ {
110
+ text,
111
+ live: isRunning && isTrailingUserMessage,
112
+ echo,
113
+ className
114
+ }
115
+ );
48
116
  };
49
117
  exports.VoiceDelegationCard = VoiceDelegationCard;
50
118
  exports.VoiceDelegationMessage = VoiceDelegationMessage;
@@ -1 +1 @@
1
- {"version":3,"file":"voice-delegation-card.cjs","sources":["../../../src/chat-ui/messages/voice-delegation-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from \"@assistant-ui/react\";\nimport { AudioLinesIcon, LoaderIcon } from \"lucide-react\";\nimport type { FC } from \"react\";\nimport { useAthenaRunLive } from \"../../chat/use-athena-run-live\";\nimport { cn } from \"../../lib/utils\";\n\nexport interface VoiceDelegationCardProps {\n /** The live voice model's plain-language ask (never markdown). */\n text: string;\n /** Whether this delegation is plausibly the one running right now. */\n live: boolean;\n className?: string;\n}\n\n/**\n * The card for a voice delegation's anchor message: the unit of work the live\n * voice model handed to the thread. It replaces the plain user bubble on every\n * surface, so delegated work reads as delegated work instead of \"the user\n * typed this twice\" — the spoken ask itself sits just above it as a spoken\n * turn. The run it dispatched streams below as ordinary messages and tool\n * cards. Pure presenter; {@link VoiceDelegationMessage} binds it to the\n * message in scope.\n */\nexport const VoiceDelegationCard: FC<VoiceDelegationCardProps> = ({ text, live, className }) => (\n <div\n className={cn(\"flex justify-end px-1 pb-0.5\", className)}\n data-testid=\"voice-delegation-card\"\n >\n <div className=\"max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2\">\n <div className=\"mb-1 flex items-center gap-1.5\">\n <span className=\"inline-flex size-4 items-center justify-center rounded bg-primary/15\">\n <AudioLinesIcon className=\"size-2.5 text-primary\" aria-hidden />\n </span>\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-primary\">\n Voice task\n </span>\n {live ? (\n <span\n className=\"inline-flex items-center gap-1 text-[10px] text-primary/80\"\n data-testid=\"voice-delegation-live\"\n >\n <LoaderIcon className=\"size-2.5 animate-spin\" aria-hidden />\n running\n </span>\n ) : null}\n </div>\n <div className=\"whitespace-pre-wrap break-words text-sm text-foreground\">{text}</div>\n </div>\n </div>\n);\n\n/**\n * {@link VoiceDelegationCard} bound to the assistant-ui message in scope. The\n * live pulse shows only while this delegation is plausibly the one running:\n * the thread is running and no later user turn superseded it.\n */\nexport const VoiceDelegationMessage: FC<{ className?: string }> = ({ className }) => {\n const text = useAuiState((s) =>\n s.message.content\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\"),\n );\n const isTrailingUserMessage = useAuiState((s) => {\n const messages = s.thread.messages;\n for (let index = messages.length - 1; index >= 0; index--) {\n const candidate = messages[index];\n if (candidate?.role !== \"user\") continue;\n return candidate.id === s.message.id;\n }\n return false;\n });\n const isRunning = useAthenaRunLive();\n return (\n <VoiceDelegationCard text={text} live={isRunning && isTrailingUserMessage} className={className} />\n );\n};\n"],"names":["jsx","cn","jsxs","AudioLinesIcon","LoaderIcon","useAuiState","useAthenaRunLive"],"mappings":";;;;;;;AAyBO,MAAM,sBAAoD,CAAC,EAAE,MAAM,MAAM,gBAC9EA,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAWC,OAAAA,GAAG,gCAAgC,SAAS;AAAA,IACvD,eAAY;AAAA,IAEZ,UAAAC,2BAAAA,KAAC,OAAA,EAAI,WAAU,0EACb,UAAA;AAAA,MAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,QAAAF,2BAAAA,IAAC,QAAA,EAAK,WAAU,wEACd,UAAAA,2BAAAA,IAACG,8BAAe,WAAU,yBAAwB,eAAW,KAAA,CAAC,EAAA,CAChE;AAAA,QACAH,2BAAAA,IAAC,QAAA,EAAK,WAAU,gEAA+D,UAAA,cAE/E;AAAA,QACC,OACCE,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAY;AAAA,YAEZ,UAAA;AAAA,cAAAF,2BAAAA,IAACI,YAAAA,YAAA,EAAW,WAAU,yBAAwB,eAAW,MAAC;AAAA,cAAE;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,IAG5D;AAAA,MAAA,GACN;AAAA,MACAJ,2BAAAA,IAAC,OAAA,EAAI,WAAU,2DAA2D,UAAA,KAAA,CAAK;AAAA,IAAA,EAAA,CACjF;AAAA,EAAA;AACF;AAQK,MAAM,yBAAqD,CAAC,EAAE,gBAAgB;AACnF,QAAM,OAAOK,MAAAA;AAAAA,IAAY,CAAC,MACxB,EAAE,QAAQ,QACP,IAAI,CAAC,SAAU,KAAK,SAAS,SAAS,KAAK,OAAO,EAAG,EACrD,OAAO,OAAO,EACd,KAAK,IAAI;AAAA,EAAA;AAEd,QAAM,wBAAwBA,kBAAY,CAAC,MAAM;AAC/C,UAAM,WAAW,EAAE,OAAO;AAC1B,aAAS,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS;AACzD,YAAM,YAAY,SAAS,KAAK;AAChC,WAAI,uCAAW,UAAS,OAAQ;AAChC,aAAO,UAAU,OAAO,EAAE,QAAQ;AAAA,IACpC;AACA,WAAO;AAAA,EACT,CAAC;AACD,QAAM,YAAYC,iBAAAA,iBAAA;AAClB,wCACG,qBAAA,EAAoB,MAAY,MAAM,aAAa,uBAAuB,WAAsB;AAErG;;;"}
1
+ {"version":3,"file":"voice-delegation-card.cjs","sources":["../../../src/chat-ui/messages/voice-delegation-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from \"@assistant-ui/react\";\nimport { voiceDelegationEchoesRequest } from \"@athenaintel/agent-runtime\";\nimport { AudioLinesIcon, ChevronDownIcon, LoaderIcon } from \"lucide-react\";\nimport { type FC, useId, useState } from \"react\";\nimport { useAthenaRunLive } from \"../../chat/use-athena-run-live\";\nimport { cn } from \"../../lib/utils\";\n\nexport interface VoiceDelegationCardProps {\n /** The live voice model's plain-language ask (never markdown). */\n text: string;\n /** Whether this delegation is plausibly the one running right now. */\n live: boolean;\n /**\n * The person's own turn right above already shows this ask\n * (`voiceDelegationEchoesRequest`): only the header shows, and the task's\n * wording opens on request.\n */\n echo?: boolean;\n className?: string;\n}\n\nconst VoiceDelegationHeading: FC<{ live: boolean }> = ({ live }) => (\n <>\n <span className=\"inline-flex size-4 items-center justify-center rounded bg-primary/15\">\n <AudioLinesIcon className=\"size-2.5 text-primary\" aria-hidden />\n </span>\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-primary\">\n Voice task\n </span>\n {live ? (\n <span\n className=\"inline-flex items-center gap-1 text-[10px] text-primary/80\"\n data-testid=\"voice-delegation-live\"\n >\n <LoaderIcon className=\"size-2.5 animate-spin\" aria-hidden />\n running\n </span>\n ) : null}\n </>\n);\n\n/**\n * The card for a voice delegation's anchor message: the unit of work the live\n * voice model handed to the thread. It replaces the plain user bubble on every\n * surface, so delegated work reads as delegated work, not as the user typing\n * again. Right under the person's own words (`echo`) it is only their request\n * repeated, so it shrinks to its header and the task's wording opens on a tap;\n * otherwise (a follow-up whose spoken turn has not landed above it, a task with\n * no visible ask) it shows the ask in full. The run it dispatched streams below\n * as ordinary messages and tool cards. Pure presenter;\n * {@link VoiceDelegationMessage} binds it to the message in scope.\n */\nexport const VoiceDelegationCard: FC<VoiceDelegationCardProps> = ({\n text,\n live,\n echo = false,\n className,\n}) => {\n const [open, setOpen] = useState(false);\n const textId = useId();\n if (echo) {\n return (\n <div\n className={cn(\"flex flex-col items-end px-1 pb-0.5\", className)}\n data-testid=\"voice-delegation-card\"\n data-echo=\"true\"\n >\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1.5 rounded-full border border-primary/20 bg-primary/5 px-2 py-0.5 hover:bg-primary/10\"\n aria-expanded={open}\n aria-controls={textId}\n onClick={() => setOpen((value) => !value)}\n data-testid=\"voice-delegation-toggle\"\n >\n <VoiceDelegationHeading live={live} />\n <span className=\"sr-only\">{open ? \"Hide the task\" : \"Show the task\"}</span>\n <ChevronDownIcon\n className={cn(\"size-3 text-primary/70 transition-transform\", open && \"rotate-180\")}\n aria-hidden\n />\n </button>\n {open ? (\n <div\n id={textId}\n className=\"mt-1 max-w-[85%] whitespace-pre-wrap break-words rounded-xl border border-primary/20 bg-primary/5 px-3 py-2 text-sm text-foreground\"\n >\n {text}\n </div>\n ) : null}\n </div>\n );\n }\n return (\n <div\n className={cn(\"flex justify-end px-1 pb-0.5\", className)}\n data-testid=\"voice-delegation-card\"\n >\n <div className=\"max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2\">\n <div className=\"mb-1 flex items-center gap-1.5\">\n <VoiceDelegationHeading live={live} />\n </div>\n <div className=\"whitespace-pre-wrap break-words text-sm text-foreground\">{text}</div>\n </div>\n </div>\n );\n};\n\n/**\n * {@link VoiceDelegationCard} bound to the assistant-ui message in scope. The\n * live pulse shows only while this delegation is plausibly the one running:\n * the thread is running and no later user turn superseded it. It draws as an\n * echo when the person's own turn right above already shows the ask.\n */\nexport const VoiceDelegationMessage: FC<{ className?: string }> = ({ className }) => {\n const text = useAuiState((s) =>\n s.message.content\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\"),\n );\n const isTrailingUserMessage = useAuiState((s) => {\n const messages = s.thread.messages;\n for (let index = messages.length - 1; index >= 0; index--) {\n const candidate = messages[index];\n if (candidate?.role !== \"user\") continue;\n return candidate.id === s.message.id;\n }\n return false;\n });\n const echo = useAuiState(\n (s) =>\n s.thread.messages[s.message.index]?.id === s.message.id &&\n voiceDelegationEchoesRequest(s.thread.messages, s.message.index),\n );\n const isRunning = useAthenaRunLive();\n return (\n <VoiceDelegationCard\n text={text}\n live={isRunning && isTrailingUserMessage}\n echo={echo}\n className={className}\n />\n );\n};\n"],"names":["jsxs","Fragment","jsx","AudioLinesIcon","LoaderIcon","useState","useId","cn","ChevronDownIcon","useAuiState","voiceDelegationEchoesRequest","useAthenaRunLive"],"mappings":"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"}
@@ -1,25 +1,34 @@
1
- import type { FC } from "react";
1
+ import { type FC } from "react";
2
2
  export interface VoiceDelegationCardProps {
3
3
  /** The live voice model's plain-language ask (never markdown). */
4
4
  text: string;
5
5
  /** Whether this delegation is plausibly the one running right now. */
6
6
  live: boolean;
7
+ /**
8
+ * The person's own turn right above already shows this ask
9
+ * (`voiceDelegationEchoesRequest`): only the header shows, and the task's
10
+ * wording opens on request.
11
+ */
12
+ echo?: boolean;
7
13
  className?: string;
8
14
  }
9
15
  /**
10
16
  * The card for a voice delegation's anchor message: the unit of work the live
11
17
  * voice model handed to the thread. It replaces the plain user bubble on every
12
- * surface, so delegated work reads as delegated work instead of "the user
13
- * typed this twice" — the spoken ask itself sits just above it as a spoken
14
- * turn. The run it dispatched streams below as ordinary messages and tool
15
- * cards. Pure presenter; {@link VoiceDelegationMessage} binds it to the
16
- * message in scope.
18
+ * surface, so delegated work reads as delegated work, not as the user typing
19
+ * again. Right under the person's own words (`echo`) it is only their request
20
+ * repeated, so it shrinks to its header and the task's wording opens on a tap;
21
+ * otherwise (a follow-up whose spoken turn has not landed above it, a task with
22
+ * no visible ask) it shows the ask in full. The run it dispatched streams below
23
+ * as ordinary messages and tool cards. Pure presenter;
24
+ * {@link VoiceDelegationMessage} binds it to the message in scope.
17
25
  */
18
26
  export declare const VoiceDelegationCard: FC<VoiceDelegationCardProps>;
19
27
  /**
20
28
  * {@link VoiceDelegationCard} bound to the assistant-ui message in scope. The
21
29
  * live pulse shows only while this delegation is plausibly the one running:
22
- * the thread is running and no later user turn superseded it.
30
+ * the thread is running and no later user turn superseded it. It draws as an
31
+ * echo when the person's own turn right above already shows the ask.
23
32
  */
24
33
  export declare const VoiceDelegationMessage: FC<{
25
34
  className?: string;
@@ -1,33 +1,87 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { useAuiState } from "@assistant-ui/react";
3
- import { AudioLinesIcon, LoaderIcon } from "lucide-react";
3
+ import { ChevronDownIcon, AudioLinesIcon, LoaderIcon } from "lucide-react";
4
+ import { useState, useId } from "react";
4
5
  import { useAthenaRunLive } from "../../chat/use-athena-run-live.js";
6
+ import { voiceDelegationEchoesRequest } from "../../agent-runtime/src/voice-presentation.js";
5
7
  import { cn } from "cnfast";
6
- const VoiceDelegationCard = ({ text, live, className }) => /* @__PURE__ */ jsx(
7
- "div",
8
- {
9
- className: cn("flex justify-end px-1 pb-0.5", className),
10
- "data-testid": "voice-delegation-card",
11
- children: /* @__PURE__ */ jsxs("div", { className: "max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2", children: [
12
- /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-1.5", children: [
13
- /* @__PURE__ */ jsx("span", { className: "inline-flex size-4 items-center justify-center rounded bg-primary/15", children: /* @__PURE__ */ jsx(AudioLinesIcon, { className: "size-2.5 text-primary", "aria-hidden": true }) }),
14
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-primary", children: "Voice task" }),
15
- live ? /* @__PURE__ */ jsxs(
16
- "span",
17
- {
18
- className: "inline-flex items-center gap-1 text-[10px] text-primary/80",
19
- "data-testid": "voice-delegation-live",
20
- children: [
21
- /* @__PURE__ */ jsx(LoaderIcon, { className: "size-2.5 animate-spin", "aria-hidden": true }),
22
- "running"
23
- ]
24
- }
25
- ) : null
26
- ] }),
27
- /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap break-words text-sm text-foreground", children: text })
28
- ] })
8
+ const VoiceDelegationHeading = ({ live }) => /* @__PURE__ */ jsxs(Fragment, { children: [
9
+ /* @__PURE__ */ jsx("span", { className: "inline-flex size-4 items-center justify-center rounded bg-primary/15", children: /* @__PURE__ */ jsx(AudioLinesIcon, { className: "size-2.5 text-primary", "aria-hidden": true }) }),
10
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-primary", children: "Voice task" }),
11
+ live ? /* @__PURE__ */ jsxs(
12
+ "span",
13
+ {
14
+ className: "inline-flex items-center gap-1 text-[10px] text-primary/80",
15
+ "data-testid": "voice-delegation-live",
16
+ children: [
17
+ /* @__PURE__ */ jsx(LoaderIcon, { className: "size-2.5 animate-spin", "aria-hidden": true }),
18
+ "running"
19
+ ]
20
+ }
21
+ ) : null
22
+ ] });
23
+ const VoiceDelegationCard = ({
24
+ text,
25
+ live,
26
+ echo = false,
27
+ className
28
+ }) => {
29
+ const [open, setOpen] = useState(false);
30
+ const textId = useId();
31
+ if (echo) {
32
+ return /* @__PURE__ */ jsxs(
33
+ "div",
34
+ {
35
+ className: cn("flex flex-col items-end px-1 pb-0.5", className),
36
+ "data-testid": "voice-delegation-card",
37
+ "data-echo": "true",
38
+ children: [
39
+ /* @__PURE__ */ jsxs(
40
+ "button",
41
+ {
42
+ type: "button",
43
+ className: "inline-flex items-center gap-1.5 rounded-full border border-primary/20 bg-primary/5 px-2 py-0.5 hover:bg-primary/10",
44
+ "aria-expanded": open,
45
+ "aria-controls": textId,
46
+ onClick: () => setOpen((value) => !value),
47
+ "data-testid": "voice-delegation-toggle",
48
+ children: [
49
+ /* @__PURE__ */ jsx(VoiceDelegationHeading, { live }),
50
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: open ? "Hide the task" : "Show the task" }),
51
+ /* @__PURE__ */ jsx(
52
+ ChevronDownIcon,
53
+ {
54
+ className: cn("size-3 text-primary/70 transition-transform", open && "rotate-180"),
55
+ "aria-hidden": true
56
+ }
57
+ )
58
+ ]
59
+ }
60
+ ),
61
+ open ? /* @__PURE__ */ jsx(
62
+ "div",
63
+ {
64
+ id: textId,
65
+ className: "mt-1 max-w-[85%] whitespace-pre-wrap break-words rounded-xl border border-primary/20 bg-primary/5 px-3 py-2 text-sm text-foreground",
66
+ children: text
67
+ }
68
+ ) : null
69
+ ]
70
+ }
71
+ );
29
72
  }
30
- );
73
+ return /* @__PURE__ */ jsx(
74
+ "div",
75
+ {
76
+ className: cn("flex justify-end px-1 pb-0.5", className),
77
+ "data-testid": "voice-delegation-card",
78
+ children: /* @__PURE__ */ jsxs("div", { className: "max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2", children: [
79
+ /* @__PURE__ */ jsx("div", { className: "mb-1 flex items-center gap-1.5", children: /* @__PURE__ */ jsx(VoiceDelegationHeading, { live }) }),
80
+ /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap break-words text-sm text-foreground", children: text })
81
+ ] })
82
+ }
83
+ );
84
+ };
31
85
  const VoiceDelegationMessage = ({ className }) => {
32
86
  const text = useAuiState(
33
87
  (s) => s.message.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n")
@@ -41,8 +95,22 @@ const VoiceDelegationMessage = ({ className }) => {
41
95
  }
42
96
  return false;
43
97
  });
98
+ const echo = useAuiState(
99
+ (s) => {
100
+ var _a;
101
+ return ((_a = s.thread.messages[s.message.index]) == null ? void 0 : _a.id) === s.message.id && voiceDelegationEchoesRequest(s.thread.messages, s.message.index);
102
+ }
103
+ );
44
104
  const isRunning = useAthenaRunLive();
45
- return /* @__PURE__ */ jsx(VoiceDelegationCard, { text, live: isRunning && isTrailingUserMessage, className });
105
+ return /* @__PURE__ */ jsx(
106
+ VoiceDelegationCard,
107
+ {
108
+ text,
109
+ live: isRunning && isTrailingUserMessage,
110
+ echo,
111
+ className
112
+ }
113
+ );
46
114
  };
47
115
  export {
48
116
  VoiceDelegationCard,
@@ -1 +1 @@
1
- {"version":3,"file":"voice-delegation-card.js","sources":["../../../src/chat-ui/messages/voice-delegation-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from \"@assistant-ui/react\";\nimport { AudioLinesIcon, LoaderIcon } from \"lucide-react\";\nimport type { FC } from \"react\";\nimport { useAthenaRunLive } from \"../../chat/use-athena-run-live\";\nimport { cn } from \"../../lib/utils\";\n\nexport interface VoiceDelegationCardProps {\n /** The live voice model's plain-language ask (never markdown). */\n text: string;\n /** Whether this delegation is plausibly the one running right now. */\n live: boolean;\n className?: string;\n}\n\n/**\n * The card for a voice delegation's anchor message: the unit of work the live\n * voice model handed to the thread. It replaces the plain user bubble on every\n * surface, so delegated work reads as delegated work instead of \"the user\n * typed this twice\" — the spoken ask itself sits just above it as a spoken\n * turn. The run it dispatched streams below as ordinary messages and tool\n * cards. Pure presenter; {@link VoiceDelegationMessage} binds it to the\n * message in scope.\n */\nexport const VoiceDelegationCard: FC<VoiceDelegationCardProps> = ({ text, live, className }) => (\n <div\n className={cn(\"flex justify-end px-1 pb-0.5\", className)}\n data-testid=\"voice-delegation-card\"\n >\n <div className=\"max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2\">\n <div className=\"mb-1 flex items-center gap-1.5\">\n <span className=\"inline-flex size-4 items-center justify-center rounded bg-primary/15\">\n <AudioLinesIcon className=\"size-2.5 text-primary\" aria-hidden />\n </span>\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-primary\">\n Voice task\n </span>\n {live ? (\n <span\n className=\"inline-flex items-center gap-1 text-[10px] text-primary/80\"\n data-testid=\"voice-delegation-live\"\n >\n <LoaderIcon className=\"size-2.5 animate-spin\" aria-hidden />\n running\n </span>\n ) : null}\n </div>\n <div className=\"whitespace-pre-wrap break-words text-sm text-foreground\">{text}</div>\n </div>\n </div>\n);\n\n/**\n * {@link VoiceDelegationCard} bound to the assistant-ui message in scope. The\n * live pulse shows only while this delegation is plausibly the one running:\n * the thread is running and no later user turn superseded it.\n */\nexport const VoiceDelegationMessage: FC<{ className?: string }> = ({ className }) => {\n const text = useAuiState((s) =>\n s.message.content\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\"),\n );\n const isTrailingUserMessage = useAuiState((s) => {\n const messages = s.thread.messages;\n for (let index = messages.length - 1; index >= 0; index--) {\n const candidate = messages[index];\n if (candidate?.role !== \"user\") continue;\n return candidate.id === s.message.id;\n }\n return false;\n });\n const isRunning = useAthenaRunLive();\n return (\n <VoiceDelegationCard text={text} live={isRunning && isTrailingUserMessage} className={className} />\n );\n};\n"],"names":[],"mappings":";;;;;AAyBO,MAAM,sBAAoD,CAAC,EAAE,MAAM,MAAM,gBAC9E;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAW,GAAG,gCAAgC,SAAS;AAAA,IACvD,eAAY;AAAA,IAEZ,UAAA,qBAAC,OAAA,EAAI,WAAU,0EACb,UAAA;AAAA,MAAA,qBAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAU,wEACd,UAAA,oBAAC,kBAAe,WAAU,yBAAwB,eAAW,KAAA,CAAC,EAAA,CAChE;AAAA,QACA,oBAAC,QAAA,EAAK,WAAU,gEAA+D,UAAA,cAE/E;AAAA,QACC,OACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAY;AAAA,YAEZ,UAAA;AAAA,cAAA,oBAAC,YAAA,EAAW,WAAU,yBAAwB,eAAW,MAAC;AAAA,cAAE;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,IAG5D;AAAA,MAAA,GACN;AAAA,MACA,oBAAC,OAAA,EAAI,WAAU,2DAA2D,UAAA,KAAA,CAAK;AAAA,IAAA,EAAA,CACjF;AAAA,EAAA;AACF;AAQK,MAAM,yBAAqD,CAAC,EAAE,gBAAgB;AACnF,QAAM,OAAO;AAAA,IAAY,CAAC,MACxB,EAAE,QAAQ,QACP,IAAI,CAAC,SAAU,KAAK,SAAS,SAAS,KAAK,OAAO,EAAG,EACrD,OAAO,OAAO,EACd,KAAK,IAAI;AAAA,EAAA;AAEd,QAAM,wBAAwB,YAAY,CAAC,MAAM;AAC/C,UAAM,WAAW,EAAE,OAAO;AAC1B,aAAS,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS;AACzD,YAAM,YAAY,SAAS,KAAK;AAChC,WAAI,uCAAW,UAAS,OAAQ;AAChC,aAAO,UAAU,OAAO,EAAE,QAAQ;AAAA,IACpC;AACA,WAAO;AAAA,EACT,CAAC;AACD,QAAM,YAAY,iBAAA;AAClB,6BACG,qBAAA,EAAoB,MAAY,MAAM,aAAa,uBAAuB,WAAsB;AAErG;"}
1
+ {"version":3,"file":"voice-delegation-card.js","sources":["../../../src/chat-ui/messages/voice-delegation-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from \"@assistant-ui/react\";\nimport { voiceDelegationEchoesRequest } from \"@athenaintel/agent-runtime\";\nimport { AudioLinesIcon, ChevronDownIcon, LoaderIcon } from \"lucide-react\";\nimport { type FC, useId, useState } from \"react\";\nimport { useAthenaRunLive } from \"../../chat/use-athena-run-live\";\nimport { cn } from \"../../lib/utils\";\n\nexport interface VoiceDelegationCardProps {\n /** The live voice model's plain-language ask (never markdown). */\n text: string;\n /** Whether this delegation is plausibly the one running right now. */\n live: boolean;\n /**\n * The person's own turn right above already shows this ask\n * (`voiceDelegationEchoesRequest`): only the header shows, and the task's\n * wording opens on request.\n */\n echo?: boolean;\n className?: string;\n}\n\nconst VoiceDelegationHeading: FC<{ live: boolean }> = ({ live }) => (\n <>\n <span className=\"inline-flex size-4 items-center justify-center rounded bg-primary/15\">\n <AudioLinesIcon className=\"size-2.5 text-primary\" aria-hidden />\n </span>\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-primary\">\n Voice task\n </span>\n {live ? (\n <span\n className=\"inline-flex items-center gap-1 text-[10px] text-primary/80\"\n data-testid=\"voice-delegation-live\"\n >\n <LoaderIcon className=\"size-2.5 animate-spin\" aria-hidden />\n running\n </span>\n ) : null}\n </>\n);\n\n/**\n * The card for a voice delegation's anchor message: the unit of work the live\n * voice model handed to the thread. It replaces the plain user bubble on every\n * surface, so delegated work reads as delegated work, not as the user typing\n * again. Right under the person's own words (`echo`) it is only their request\n * repeated, so it shrinks to its header and the task's wording opens on a tap;\n * otherwise (a follow-up whose spoken turn has not landed above it, a task with\n * no visible ask) it shows the ask in full. The run it dispatched streams below\n * as ordinary messages and tool cards. Pure presenter;\n * {@link VoiceDelegationMessage} binds it to the message in scope.\n */\nexport const VoiceDelegationCard: FC<VoiceDelegationCardProps> = ({\n text,\n live,\n echo = false,\n className,\n}) => {\n const [open, setOpen] = useState(false);\n const textId = useId();\n if (echo) {\n return (\n <div\n className={cn(\"flex flex-col items-end px-1 pb-0.5\", className)}\n data-testid=\"voice-delegation-card\"\n data-echo=\"true\"\n >\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1.5 rounded-full border border-primary/20 bg-primary/5 px-2 py-0.5 hover:bg-primary/10\"\n aria-expanded={open}\n aria-controls={textId}\n onClick={() => setOpen((value) => !value)}\n data-testid=\"voice-delegation-toggle\"\n >\n <VoiceDelegationHeading live={live} />\n <span className=\"sr-only\">{open ? \"Hide the task\" : \"Show the task\"}</span>\n <ChevronDownIcon\n className={cn(\"size-3 text-primary/70 transition-transform\", open && \"rotate-180\")}\n aria-hidden\n />\n </button>\n {open ? (\n <div\n id={textId}\n className=\"mt-1 max-w-[85%] whitespace-pre-wrap break-words rounded-xl border border-primary/20 bg-primary/5 px-3 py-2 text-sm text-foreground\"\n >\n {text}\n </div>\n ) : null}\n </div>\n );\n }\n return (\n <div\n className={cn(\"flex justify-end px-1 pb-0.5\", className)}\n data-testid=\"voice-delegation-card\"\n >\n <div className=\"max-w-[85%] rounded-xl border border-primary/20 bg-primary/5 px-3 py-2\">\n <div className=\"mb-1 flex items-center gap-1.5\">\n <VoiceDelegationHeading live={live} />\n </div>\n <div className=\"whitespace-pre-wrap break-words text-sm text-foreground\">{text}</div>\n </div>\n </div>\n );\n};\n\n/**\n * {@link VoiceDelegationCard} bound to the assistant-ui message in scope. The\n * live pulse shows only while this delegation is plausibly the one running:\n * the thread is running and no later user turn superseded it. It draws as an\n * echo when the person's own turn right above already shows the ask.\n */\nexport const VoiceDelegationMessage: FC<{ className?: string }> = ({ className }) => {\n const text = useAuiState((s) =>\n s.message.content\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\"),\n );\n const isTrailingUserMessage = useAuiState((s) => {\n const messages = s.thread.messages;\n for (let index = messages.length - 1; index >= 0; index--) {\n const candidate = messages[index];\n if (candidate?.role !== \"user\") continue;\n return candidate.id === s.message.id;\n }\n return false;\n });\n const echo = useAuiState(\n (s) =>\n s.thread.messages[s.message.index]?.id === s.message.id &&\n voiceDelegationEchoesRequest(s.thread.messages, s.message.index),\n );\n const isRunning = useAthenaRunLive();\n return (\n <VoiceDelegationCard\n text={text}\n live={isRunning && isTrailingUserMessage}\n echo={echo}\n className={className}\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -6,6 +6,8 @@ const useStatewireApprovalRequest = require("../chat/use-statewire-approval-requ
6
6
  const StatewireRunRefusalNotice = require("../chat/StatewireRunRefusalNotice.cjs");
7
7
  const StatewireQueuedMessages = require("../chat/StatewireQueuedMessages.cjs");
8
8
  const statewireHistoryEdge = require("./thread/statewire-history-edge.cjs");
9
+ const earlierStepsButton = require("../chat/super-grouping/earlier-steps-button.cjs");
10
+ const useTurnSteps = require("../chat/super-grouping/use-turn-steps.cjs");
9
11
  const statewireWindowContext = require("../provider/statewire-window-context.cjs");
10
12
  const continuePill = require("./messages/continue-pill.cjs");
11
13
  const runtimeMessage = require("./messages/runtime-message.cjs");
@@ -23,7 +25,11 @@ exports.StatewireRunRefusalNotice = StatewireRunRefusalNotice.StatewireRunRefusa
23
25
  exports.StatewireQueuedMessages = StatewireQueuedMessages.StatewireQueuedMessages;
24
26
  exports.HistoryEdgeRow = statewireHistoryEdge.HistoryEdgeRow;
25
27
  exports.StatewireHistoryEdge = statewireHistoryEdge.StatewireHistoryEdge;
28
+ exports.EarlierStepsButton = earlierStepsButton.EarlierStepsButton;
29
+ exports.TurnStepsControlContext = useTurnSteps.TurnStepsControlContext;
30
+ exports.StatewireHistoryPagesContext = statewireWindowContext.StatewireHistoryPagesContext;
26
31
  exports.StatewireToolOutputsContext = statewireWindowContext.StatewireToolOutputsContext;
32
+ exports.useStatewireHistoryPages = statewireWindowContext.useStatewireHistoryPages;
27
33
  exports.useStatewireToolOutputs = statewireWindowContext.useStatewireToolOutputs;
28
34
  exports.ContinuePill = continuePill.ContinuePill;
29
35
  exports.RuntimeMessageCard = runtimeMessage.RuntimeMessageCard;
@@ -1 +1 @@
1
- {"version":3,"file":"statewire.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"statewire.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -15,7 +15,9 @@ export { type StatewireApprovalRequest, type StatewireApprovalWait, type Statewi
15
15
  export { RUN_START_REFUSAL_FALLBACK_MESSAGE, StatewireRunRefusalNotice, type StatewireRunRefusalNoticeProps, } from "../chat/StatewireRunRefusalNotice";
16
16
  export { StatewireQueuedMessages, type StatewireQueueEditorContext, type StatewireQueueEvent, type StatewireQueueRowContentContext, type StatewireQueueRowMarkerContext, type StatewireQueuedMessagesProps, } from "../chat/StatewireQueuedMessages";
17
17
  export { HistoryEdgeRow, type HistoryEdgeRowProps, StatewireHistoryEdge, type StatewireHistoryEdgeProps, } from "./thread/statewire-history-edge";
18
- export { StatewireToolOutputsContext, useStatewireToolOutputs, } from "../provider/statewire-window-context";
18
+ export { EarlierStepsButton } from "../chat/super-grouping/earlier-steps-button";
19
+ export { type TurnStepsControlProps, TurnStepsControlContext, } from "../chat/super-grouping/use-turn-steps";
20
+ export { StatewireHistoryPagesContext, StatewireToolOutputsContext, useStatewireHistoryPages, useStatewireToolOutputs, } from "../provider/statewire-window-context";
19
21
  export { ContinuePill } from "./messages/continue-pill";
20
22
  export { RuntimeMessageCard, type RuntimeMessageCardProps } from "./messages/runtime-message";
21
23
  export { WakeMarkerCard, type WakeMarkerCardProps, WakeMarkerMessage, } from "./messages/wake-marker";
@@ -4,7 +4,9 @@ import { useStatewireApprovalRequest, useStatewireApprovalWait } from "../chat/u
4
4
  import { RUN_START_REFUSAL_FALLBACK_MESSAGE, StatewireRunRefusalNotice } from "../chat/StatewireRunRefusalNotice.js";
5
5
  import { StatewireQueuedMessages } from "../chat/StatewireQueuedMessages.js";
6
6
  import { HistoryEdgeRow, StatewireHistoryEdge } from "./thread/statewire-history-edge.js";
7
- import { StatewireToolOutputsContext, useStatewireToolOutputs } from "../provider/statewire-window-context.js";
7
+ import { EarlierStepsButton } from "../chat/super-grouping/earlier-steps-button.js";
8
+ import { TurnStepsControlContext } from "../chat/super-grouping/use-turn-steps.js";
9
+ import { StatewireHistoryPagesContext, StatewireToolOutputsContext, useStatewireHistoryPages, useStatewireToolOutputs } from "../provider/statewire-window-context.js";
8
10
  import { ContinuePill } from "./messages/continue-pill.js";
9
11
  import { RuntimeMessageCard } from "./messages/runtime-message.js";
10
12
  import { WakeMarkerCard, WakeMarkerMessage } from "./messages/wake-marker.js";
@@ -13,6 +15,7 @@ import { StatewireContinueRunAffordance, StatewireContinueRunButton, StatewireRu
13
15
  import { HITL_PENDING_RESUME_GRACE_MS } from "../agent-runtime/src/interrupts.js";
14
16
  export {
15
17
  ContinuePill,
18
+ EarlierStepsButton,
16
19
  HITL_PENDING_RESUME_GRACE_MS,
17
20
  HistoryEdgeRow,
18
21
  RUN_START_REFUSAL_FALLBACK_MESSAGE,
@@ -21,10 +24,12 @@ export {
21
24
  StatewireContinueRunAffordance,
22
25
  StatewireContinueRunButton,
23
26
  StatewireHistoryEdge,
27
+ StatewireHistoryPagesContext,
24
28
  StatewireQueuedMessages,
25
29
  StatewireRunEndedFooter,
26
30
  StatewireRunRefusalNotice,
27
31
  StatewireToolOutputsContext,
32
+ TurnStepsControlContext,
28
33
  WaitingOnYouBanner,
29
34
  WaitingOnYouBannerView,
30
35
  WakeMarkerCard,
@@ -33,6 +38,7 @@ export {
33
38
  useBrowserDecisionConfirmer,
34
39
  useStatewireApprovalRequest,
35
40
  useStatewireApprovalWait,
41
+ useStatewireHistoryPages,
36
42
  useStatewireToolOutputs
37
43
  };
38
44
  //# sourceMappingURL=statewire.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"statewire.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
1
+ {"version":3,"file":"statewire.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
@@ -117,8 +117,8 @@ function startHandoffCursorReveal({
117
117
  }
118
118
  pagesLoaded += 1;
119
119
  waitingSince = now();
120
- }).catch(() => {
121
- pagesLoaded = Number.POSITIVE_INFINITY;
120
+ }).catch((error) => {
121
+ if (!editCommand.isHistoryPageLostToReconnect(error)) pagesLoaded = Number.POSITIVE_INFINITY;
122
122
  }).finally(() => {
123
123
  paging = false;
124
124
  pageLandsAt = now() + 2 * HANDOFF_CURSOR_POLL_MS;