@jini-ai/chat 0.6.5 → 0.6.7

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 (161) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/core/accepted-input.d.ts +54 -0
  3. package/dist/core/accepted-input.js +165 -0
  4. package/dist/core/agent-plugin-chat-state.d.ts +43 -0
  5. package/dist/core/agent-plugin-chat-state.js +48 -0
  6. package/dist/core/assistant-client/a2ui-action-poster.js +32 -28
  7. package/dist/core/assistant-client/messages.d.ts +1 -1
  8. package/dist/core/assistant-client/messages.js +2 -2
  9. package/dist/core/assistant-client/transport.d.ts +2 -2
  10. package/dist/core/assistant-client/transport.js +184 -97
  11. package/dist/core/assistant-content.d.ts +2 -1
  12. package/dist/core/assistant-content.js +9 -11
  13. package/dist/core/browser/page-actions.js +4 -2
  14. package/dist/core/composer-history.d.ts +16 -9
  15. package/dist/core/composer-history.js +28 -16
  16. package/dist/core/conversation-title.d.ts +3 -0
  17. package/dist/core/conversation-title.js +3 -0
  18. package/dist/core/durable-projection.js +20 -13
  19. package/dist/core/durable-runs/continuation.d.ts +4 -0
  20. package/dist/core/durable-runs/continuation.js +35 -6
  21. package/dist/core/durable-runs/ledger-contracts.d.ts +2 -1
  22. package/dist/core/durable-runs/policies.d.ts +1 -0
  23. package/dist/core/durable-runs/policies.js +1 -0
  24. package/dist/core/durable-runs/ports.d.ts +6 -2
  25. package/dist/core/index.d.ts +2 -0
  26. package/dist/core/index.js +2 -0
  27. package/dist/core/messages.d.ts +2 -0
  28. package/dist/core/partial-json.d.ts +6 -7
  29. package/dist/core/partial-json.js +81 -47
  30. package/dist/core/persistence/title.js +4 -2
  31. package/dist/core/question-answer.d.ts +23 -0
  32. package/dist/core/question-answer.js +66 -0
  33. package/dist/core/question-form/format.d.ts +6 -6
  34. package/dist/core/question-form/format.js +16 -17
  35. package/dist/core/question-form/normalize.d.ts +18 -0
  36. package/dist/core/question-form/normalize.js +112 -122
  37. package/dist/core/question-form/scan.js +141 -203
  38. package/dist/core/run-activity.js +62 -49
  39. package/dist/core/run-events/entry.d.ts +10 -1
  40. package/dist/core/run-events/entry.js +117 -87
  41. package/dist/core/todos.d.ts +1 -1
  42. package/dist/core/todos.js +60 -85
  43. package/dist/core/tool-events.js +14 -12
  44. package/dist/core/transcript.js +62 -16
  45. package/dist/core/turn-usage.d.ts +2 -1
  46. package/dist/core/turn-usage.js +56 -27
  47. package/dist/core/user-text-redaction.js +3 -0
  48. package/dist/core/util/manifest.js +77 -82
  49. package/dist/core/util/markdown-context.js +51 -53
  50. package/dist/core/util/parser.js +136 -144
  51. package/dist/core/util/pointer.js +16 -16
  52. package/dist/core/util/strip.js +57 -67
  53. package/dist/core/util/validate.js +15 -17
  54. package/dist/embed-widget/browser/session-store.js +21 -12
  55. package/dist/embed-widget/transports/fetch-sse.js +108 -95
  56. package/dist/embed-widget/vanilla/index.js +81 -70
  57. package/dist/react/agent-bridge/frontend-session-bridge.js +80 -52
  58. package/dist/react/components/AttachmentPreviewModal.js +25 -1
  59. package/dist/react/components/ChatSettingsDialog.d.ts +13 -0
  60. package/dist/react/components/ChatSettingsDialog.hooks.d.ts +115 -0
  61. package/dist/react/components/ChatSettingsDialog.hooks.js +40 -0
  62. package/dist/react/components/ChatSettingsDialog.js +72 -0
  63. package/dist/react/components/ChatSettingsSegmented.d.ts +18 -0
  64. package/dist/react/components/ChatSettingsSegmented.js +11 -0
  65. package/dist/react/components/Composer.d.ts +5 -1
  66. package/dist/react/components/Composer.js +126 -82
  67. package/dist/react/components/ConversationList.js +25 -39
  68. package/dist/react/components/ConversationTitleEditor.d.ts +27 -0
  69. package/dist/react/components/ConversationTitleEditor.js +54 -0
  70. package/dist/react/components/Icon.d.ts +1 -1
  71. package/dist/react/components/Icon.js +25 -45
  72. package/dist/react/components/Markdown.js +134 -123
  73. package/dist/react/components/McpUiSurfaceCard.d.ts +2 -2
  74. package/dist/react/components/McpUiSurfaceCard.js +30 -9
  75. package/dist/react/components/MessageList.d.ts +13 -13
  76. package/dist/react/components/MessageList.hooks.d.ts +6 -1
  77. package/dist/react/components/MessageList.hooks.js +19 -3
  78. package/dist/react/components/MessageList.js +34 -10
  79. package/dist/react/components/MessageRow.js +48 -32
  80. package/dist/react/components/MessageRow.usage.d.ts +5 -2
  81. package/dist/react/components/MessageRow.usage.js +20 -11
  82. package/dist/react/components/MidRunUserMessage.js +16 -3
  83. package/dist/react/components/QuestionForm.js +148 -76
  84. package/dist/react/components/QuestionsPanel.js +1 -1
  85. package/dist/react/components/TodoCard.js +12 -6
  86. package/dist/react/components/ToolCard.js +47 -40
  87. package/dist/react/components/composer-discovery.js +6 -1
  88. package/dist/react/components/mcp-ui-surface-state.d.ts +6 -11
  89. package/dist/react/components/mcp-ui-surface-state.js +23 -8
  90. package/dist/react/ext-event-renderer-registry.d.ts +8 -0
  91. package/dist/react/ext-event-renderer-registry.js +5 -2
  92. package/dist/react/features/chat-pane/components/AgentPluginUsage.d.ts +7 -0
  93. package/dist/react/features/chat-pane/components/AgentPluginUsage.js +45 -0
  94. package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +55 -19
  95. package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
  96. package/dist/react/features/chat-pane/components/ChatPane.js +192 -74
  97. package/dist/react/features/chat-pane/components/RuntimeDropUp.d.ts +29 -0
  98. package/dist/react/features/chat-pane/components/RuntimeDropUp.js +45 -0
  99. package/dist/react/features/chat-pane/create-daemon-attachment-uploader.js +80 -47
  100. package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js +44 -40
  101. package/dist/react/features/chat-pane/create-mid-run-message-poster.d.ts +6 -6
  102. package/dist/react/features/chat-pane/create-mid-run-message-poster.js +8 -8
  103. package/dist/react/features/chat-pane/create-typed-answer-poster.d.ts +3 -3
  104. package/dist/react/features/chat-pane/create-typed-answer-poster.js +3 -3
  105. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.d.ts +6 -1
  106. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js +11 -5
  107. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +12 -7
  108. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +340 -149
  109. package/dist/react/features/chat-pane/hooks/useChatPaneControlsHeight.hooks.js +7 -6
  110. package/dist/react/features/chat-pane/hooks/useChatPaneNewTabLinks.hooks.js +28 -10
  111. package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.d.ts +3 -3
  112. package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +60 -12
  113. package/dist/react/features/chat-pane/hooks/useRuntimeDropUp.hooks.d.ts +49 -0
  114. package/dist/react/features/chat-pane/hooks/useRuntimeDropUp.hooks.js +143 -0
  115. package/dist/react/features/chat-pane/model-options.d.ts +18 -1
  116. package/dist/react/features/chat-pane/model-options.js +49 -20
  117. package/dist/react/features/chat-pane/rules.d.ts +8 -9
  118. package/dist/react/features/chat-pane/rules.js +84 -46
  119. package/dist/react/features/chat-pane/styles.d.ts +1 -1
  120. package/dist/react/features/chat-pane/styles.js +156 -37
  121. package/dist/react/features/chat-pane/types.d.ts +17 -8
  122. package/dist/react/hooks/assistant-chats.js +12 -5
  123. package/dist/react/hooks/composer-draft-cache.js +116 -118
  124. package/dist/react/hooks/composer-history-storage.js +5 -5
  125. package/dist/react/hooks/fab-position.js +7 -4
  126. package/dist/react/hooks/useAnsweredCards.d.ts +21 -4
  127. package/dist/react/hooks/useAnsweredCards.js +38 -11
  128. package/dist/react/hooks/useArtifactStream.js +28 -22
  129. package/dist/react/hooks/useComposer.d.ts +20 -0
  130. package/dist/react/hooks/useComposer.js +22 -9
  131. package/dist/react/hooks/useComposerHistory.hooks.d.ts +6 -1
  132. package/dist/react/hooks/useComposerHistory.hooks.js +39 -19
  133. package/dist/react/hooks/useComposerHistoryKeys.hooks.js +24 -14
  134. package/dist/react/hooks/useConversation.d.ts +7 -0
  135. package/dist/react/hooks/useConversation.js +23 -10
  136. package/dist/react/hooks/useExtEventGroups.js +39 -39
  137. package/dist/react/hooks/useQuestionForms.js +21 -26
  138. package/dist/react/hooks/useRunStream.js +10 -2
  139. package/dist/react/index.d.ts +7 -1
  140. package/dist/react/index.js +9 -0
  141. package/dist/react/message-blocks.d.ts +6 -4
  142. package/dist/react/message-blocks.js +94 -72
  143. package/dist/react/slots.d.ts +2 -2
  144. package/dist/react/tool-call-display.js +19 -17
  145. package/dist/server/durable-runs/recover.d.ts +1 -1
  146. package/dist/server/durable-runs/recover.js +139 -76
  147. package/dist/server/run-finalizer/entry.js +25 -15
  148. package/dist/store/legacy/sqlite/conversations.d.ts +11 -11
  149. package/dist/store/legacy/sqlite/conversations.js +69 -98
  150. package/dist/store/legacy/sqlite/messages.d.ts +10 -2
  151. package/dist/store/legacy/sqlite/messages.js +113 -71
  152. package/dist/store/runtime/chat-history-store.js +9 -3
  153. package/dist/store/runtime/durable-run-store.d.ts +2 -2
  154. package/dist/store/runtime/durable-run-store.js +165 -117
  155. package/dist/store/runtime/history-tools.js +118 -82
  156. package/dist/store/runtime/run-ledger.d.ts +4 -1
  157. package/dist/store/runtime/run-ledger.js +56 -16
  158. package/dist/store/sql/cursor.js +38 -18
  159. package/dist/store/sql/store.d.ts +18 -0
  160. package/dist/store/sql/store.js +46 -15
  161. package/package.json +12 -10
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.6.7 — 2026-10-22
2
+
3
+ - 2026-10-22: Memory fixes (durable run store reads its own row, ledger parses events once), shared settings segmented control, Slack token rule restored, coverage.
4
+
5
+ ## 0.6.6 — 2026-10-16
6
+
7
+ - Staged attachment rows sit 8px apart so the top row's remove target stays 44px (4884cf15).
8
+ - The Done line for tokens-only runtimes uses run timestamps and the known total (9fd89d41).
9
+ - An unknown tool on the read-only gateway points the model to discovery, never a mutation 409 (75d37c32).
10
+ - A run that could not continue says why, and a wait for the owner is never reported as "no progress" (13e90536).
11
+ - The model picker shows the default model by name, not its raw id (6b4b613d).
12
+
1
13
  ## 0.6.5 — 2026-10-15
2
14
 
3
15
  - Durable recovery no longer gives up on a finished CLI turn (8c6acfea): a saved successful Claude CLI `result` line carries `modelUsage.contextWindow`, which matched the context-limit pattern case-insensitively, so a dropped stream or restart ended the answer with "The assistant could not continue this answer" instead of continuing it. The pattern now requires a separator, and attempt diagnostics skip successful CLI result lines (`type: "result"`, `is_error: false`).
@@ -0,0 +1,54 @@
1
+ import type { AgentEvent } from './events.js';
2
+ import type { ChatAttachment, ChatMessage } from './messages.js';
3
+ export interface AcceptedChatInput {
4
+ id: string;
5
+ text: string;
6
+ attachments?: ChatAttachment[];
7
+ secretRedaction?: ChatMessage['secretRedaction'];
8
+ /** Wire ids replaced by this receipt remain seen by recall. */
9
+ aliases?: readonly string[];
10
+ }
11
+ /** Display queue constituents using their accepted identities; storage and delivery retain one turn.
12
+ * MOVE: Receipts travel in events_json, so separate bubbles survive a history reload without another store.
13
+ * @complexity O(m + e + i) time/space for m messages, e receipt events and i accepted queue inputs.
14
+ */
15
+ export declare function projectAcceptedInputMessages({ messages }: {
16
+ messages: readonly ChatMessage[];
17
+ }, _options?: {}): ChatMessage[];
18
+ type UserMessageEvent = Extract<AgentEvent, {
19
+ kind: 'ext';
20
+ }> & {
21
+ data: AcceptedChatInput & {
22
+ optimistic?: boolean;
23
+ toolUseId?: string;
24
+ afterInputId?: string;
25
+ inputs?: AcceptedChatInput[];
26
+ };
27
+ };
28
+ /** Reads the existing event-backed user bubble without depending on a host renderer. */
29
+ export declare function acceptedInputEvent({ event }: {
30
+ event: AgentEvent;
31
+ }, _options?: {}): UserMessageEvent | null;
32
+ /** Builds the same bubble for delivered answers and steering; queues use the existing pending row. */
33
+ export declare function acceptedInputUserEvent({ input, inputs, toolUseId, afterInputId }: {
34
+ input: AcceptedChatInput;
35
+ inputs?: AcceptedChatInput[];
36
+ toolUseId?: string;
37
+ afterInputId?: string;
38
+ }, { receipt }?: {
39
+ receipt?: 'optimistic' | 'accepted';
40
+ }): AgentEvent;
41
+ /** Projects saved answers and reconciles optimistic bubbles with wire events, occurrence for
42
+ * occurrence. Legacy steering receipts have no client id: only unmatched optimistic input may
43
+ * match by text; two real events with equal text always remain distinct.
44
+ * @complexity O(e + u + a) time, O(e + u) space for e events, u user inputs and total answer-label lookup cost a.
45
+ */
46
+ export declare function projectAcceptedInputEvents({ events }: {
47
+ events: readonly AgentEvent[];
48
+ }, _options?: {}): AgentEvent[];
49
+ /** The sole transcript-to-recall projection. A merged queued turn retains its original inputs. */
50
+ export declare function acceptedInputsFromMessages({ messages }: {
51
+ messages: readonly ChatMessage[];
52
+ }, _options?: {}): AcceptedChatInput[];
53
+ export {};
54
+ //# sourceMappingURL=accepted-input.d.ts.map
@@ -0,0 +1,165 @@
1
+ /** Accepted composer input has one identity across optimistic display, replay and recall. */
2
+ import { questionAnswerFromResult } from './question-answer.js';
3
+ import { MID_RUN_USER_MESSAGE_EVENT_NAME } from './turn-usage.js';
4
+ /** Display queue constituents using their accepted identities; storage and delivery retain one turn.
5
+ * MOVE: Receipts travel in events_json, so separate bubbles survive a history reload without another store.
6
+ * @complexity O(m + e + i) time/space for m messages, e receipt events and i accepted queue inputs.
7
+ */
8
+ export function projectAcceptedInputMessages({ messages }, _options = {}) {
9
+ return messages.flatMap(message => {
10
+ if (message.role !== 'user')
11
+ return [message];
12
+ const inputs = (message.events ?? []).flatMap(event => {
13
+ const input = acceptedInputEvent({ event });
14
+ return input ? [input.data] : [];
15
+ });
16
+ if (inputs.length === 0)
17
+ return [message];
18
+ const { attachments: _attachments, secretRedaction: _redaction, ...turn } = message;
19
+ // MOVE: Older receipts have no per-input files; retain those legacy files on the final bubble.
20
+ return inputs.map((input, index) => ({ ...turn, id: input.id, content: input.text,
21
+ attachments: input.attachments ?? (index === inputs.length - 1 ? message.attachments ?? [] : []), ...(input.secretRedaction ? { secretRedaction: input.secretRedaction } : {}) }));
22
+ });
23
+ }
24
+ /** Reads the existing event-backed user bubble without depending on a host renderer. */
25
+ export function acceptedInputEvent({ event }, _options = {}) {
26
+ if (event.kind !== 'ext' || event.name !== MID_RUN_USER_MESSAGE_EVENT_NAME)
27
+ return null;
28
+ const data = event.data;
29
+ return data && typeof data.id === 'string' && typeof data.text === 'string' ? event : null;
30
+ }
31
+ /** Project a persisted answer through the same receipt owner as composer input. */
32
+ function questionAnswerEvent({ event, input }, _options = {}) {
33
+ if (event.kind !== 'tool_result')
34
+ return null;
35
+ const text = questionAnswerFromResult({ result: event, input });
36
+ if (text === undefined)
37
+ return null;
38
+ return { kind: 'ext', name: MID_RUN_USER_MESSAGE_EVENT_NAME,
39
+ data: { id: `answer:${event.toolUseId}`, text, toolUseId: event.toolUseId } };
40
+ }
41
+ /** Builds the same bubble for delivered answers and steering; queues use the existing pending row. */
42
+ export function acceptedInputUserEvent({ input, inputs, toolUseId, afterInputId }, { receipt = 'optimistic' } = {}) {
43
+ return { kind: 'ext', name: MID_RUN_USER_MESSAGE_EVENT_NAME,
44
+ data: { ...input, ...(receipt === 'optimistic' ? { optimistic: true } : {}), ...(inputs === undefined ? {} : { inputs }), ...(afterInputId === undefined ? {} : { afterInputId }), ...(toolUseId === undefined ? {} : { toolUseId }) } };
45
+ }
46
+ function expandQuestionAnswers({ events }, _options = {}) {
47
+ const savedAnswers = new Set(events.flatMap(event => {
48
+ const input = acceptedInputEvent({ event });
49
+ return input && !input.data.optimistic && input.data.toolUseId ? [input.data.toolUseId] : [];
50
+ }));
51
+ // MOVE: Persisted picks carry values; the original tool input retains their human-readable labels.
52
+ const questions = new Map(events.flatMap(event => event.kind === 'tool_use' ? [[event.id, event.input]] : []));
53
+ return events.flatMap(event => {
54
+ const answer = questionAnswerEvent({ event, input: event.kind === 'tool_result' ? questions.get(event.toolUseId) : undefined });
55
+ return answer && !savedAnswers.has(answer.data.toolUseId) ? [event, answer] : [event];
56
+ });
57
+ }
58
+ function collectOptimisticInputs({ events }, _options = {}) {
59
+ const optimisticByText = new Map();
60
+ const optimisticByQuestion = new Map();
61
+ for (const event of events) {
62
+ const input = acceptedInputEvent({ event });
63
+ if (!input?.data.optimistic)
64
+ continue;
65
+ if (input.data.toolUseId)
66
+ optimisticByQuestion.set(input.data.toolUseId, input);
67
+ else {
68
+ const list = optimisticByText.get(input.data.text) ?? { events: [], index: 0 };
69
+ list.events.push(input);
70
+ optimisticByText.set(input.data.text, list);
71
+ }
72
+ }
73
+ return { byText: optimisticByText, byQuestion: optimisticByQuestion };
74
+ }
75
+ function matchOptimisticInput({ input, optimistic, seen }, _options = {}) {
76
+ const candidates = optimistic.byText.get(input.data.text);
77
+ const next = candidates?.events[candidates.index];
78
+ const follows = next?.data.afterInputId === undefined || seen.has(next.data.afterInputId);
79
+ // Keep predecessor testing before the caller adds this receipt's ids to seen.
80
+ return input.data.toolUseId ? optimistic.byQuestion.get(input.data.toolUseId) : follows ? next : undefined;
81
+ }
82
+ function mergeAcceptedReceipt({ input, pending }, _options = {}) {
83
+ return { ...input, data: { ...input.data, id: pending.data.id,
84
+ ...(pending.data.inputs ? { inputs: pending.data.inputs } : {}),
85
+ aliases: [...new Set([...(input.data.aliases ?? []), input.data.id, ...(pending.data.aliases ?? [])])] } };
86
+ }
87
+ function acceptedInputIds({ input }, _options = {}) {
88
+ return [input.data.id, ...(input.data.toolUseId ? [`answer:${input.data.toolUseId}`] : []), ...(input.data.aliases ?? [])];
89
+ }
90
+ function reconcileAcceptedInput({ event, optimistic, state }, _options = {}) {
91
+ const input = acceptedInputEvent({ event });
92
+ if (!input || input.data.optimistic)
93
+ return;
94
+ const { replacements, replaced, seen } = state;
95
+ const ids = acceptedInputIds({ input });
96
+ if (ids.some(id => seen.has(id))) {
97
+ replaced.add(event);
98
+ return;
99
+ }
100
+ const pending = matchOptimisticInput({ input, optimistic, seen });
101
+ for (const id of ids)
102
+ seen.add(id);
103
+ if (!pending)
104
+ return;
105
+ advanceOptimisticInput({ input, optimistic });
106
+ for (const id of [pending.data.id, ...(pending.data.aliases ?? [])])
107
+ seen.add(id);
108
+ optimistic.byQuestion.delete(input.data.toolUseId ?? '');
109
+ replaced.add(pending);
110
+ replacements.set(event, mergeAcceptedReceipt({ input, pending }));
111
+ }
112
+ function advanceOptimisticInput({ input, optimistic }, _options = {}) {
113
+ const candidates = optimistic.byText.get(input.data.text);
114
+ if (candidates && !input.data.toolUseId)
115
+ candidates.index += 1;
116
+ }
117
+ /** Projects saved answers and reconciles optimistic bubbles with wire events, occurrence for
118
+ * occurrence. Legacy steering receipts have no client id: only unmatched optimistic input may
119
+ * match by text; two real events with equal text always remain distinct.
120
+ * @complexity O(e + u + a) time, O(e + u) space for e events, u user inputs and total answer-label lookup cost a.
121
+ */
122
+ export function projectAcceptedInputEvents({ events }, _options = {}) {
123
+ const expanded = expandQuestionAnswers({ events });
124
+ const optimistic = collectOptimisticInputs({ events: expanded });
125
+ const state = { replacements: new Map(), replaced: new Set(), seen: new Set() };
126
+ for (const event of expanded)
127
+ reconcileAcceptedInput({ event, optimistic, state });
128
+ const { replacements, replaced } = state;
129
+ const projected = [];
130
+ const emitted = new Set();
131
+ for (const event of expanded) {
132
+ if (replaced.has(event))
133
+ continue;
134
+ const next = replacements.get(event) ?? event;
135
+ const input = acceptedInputEvent({ event: next });
136
+ if (input && emitted.has(input.data.id))
137
+ continue;
138
+ if (input)
139
+ emitted.add(input.data.id);
140
+ projected.push(next);
141
+ }
142
+ return projected;
143
+ }
144
+ /** The sole transcript-to-recall projection. A merged queued turn retains its original inputs. */
145
+ export function acceptedInputsFromMessages({ messages }, _options = {}) {
146
+ return messages.flatMap(message => {
147
+ if (message.role === 'user') {
148
+ if (message.inputPending)
149
+ return [];
150
+ // Queue constituents travel in events_json, already preserved by the existing chat store.
151
+ const receipts = (message.events ?? []).flatMap(event => {
152
+ const input = acceptedInputEvent({ event });
153
+ return input ? [input.data] : [];
154
+ });
155
+ return receipts.length > 0 ? receipts : [{ id: message.id, text: message.content }];
156
+ }
157
+ return projectAcceptedInputEvents({ events: message.events ?? [] }).flatMap(event => {
158
+ const input = acceptedInputEvent({ event });
159
+ if (!input)
160
+ return [];
161
+ return input.data.inputs?.map((receipt, index) => index === 0 ? { ...receipt, aliases: input.data.aliases ?? [] } : receipt) ?? [input.data];
162
+ });
163
+ });
164
+ }
165
+ //# sourceMappingURL=accepted-input.js.map
@@ -0,0 +1,43 @@
1
+ /** Browser-local per-chat decisions; the person's mode remains in the host's settings ledger. */
2
+ export interface AgentPluginChatState {
3
+ decision: {
4
+ selected: readonly string[];
5
+ asked?: boolean;
6
+ } | null;
7
+ excluded: readonly string[];
8
+ }
9
+ export interface AgentPluginUsage {
10
+ conversationId: string;
11
+ state: AgentPluginChatState;
12
+ plugins: readonly {
13
+ id: string;
14
+ name: string;
15
+ }[];
16
+ }
17
+ export interface ChatPluginStorage {
18
+ getItem(key: string): string | null;
19
+ setItem(key: string, value: string): void;
20
+ }
21
+ export declare const AGENT_PLUGIN_STATE_CHANGED = "jini:agent-plugin-state";
22
+ /** Corrupt/blocked storage permits a fresh question, as clearing localStorage does. */
23
+ export declare function readAgentPluginChatState({ conversationId }: {
24
+ conversationId: string;
25
+ }, { storage }?: {
26
+ storage?: ChatPluginStorage | undefined;
27
+ }): AgentPluginChatState;
28
+ /** Persist only decision/exclusions. Existing exclusions win over an in-flight server response. */
29
+ export declare function writeAgentPluginChatState({ conversationId, state }: {
30
+ conversationId: string;
31
+ state: AgentPluginChatState;
32
+ }, { storage }?: {
33
+ storage?: ChatPluginStorage | undefined;
34
+ }): void;
35
+ /** Called only for the protocol's structured plugin event, before delivering it to the transcript. */
36
+ export declare function captureAgentPluginUsage({ event }: {
37
+ event: {
38
+ kind: string;
39
+ name?: string;
40
+ data?: unknown;
41
+ };
42
+ }, _options?: {}): void;
43
+ //# sourceMappingURL=agent-plugin-chat-state.d.ts.map
@@ -0,0 +1,48 @@
1
+ export const AGENT_PLUGIN_STATE_CHANGED = 'jini:agent-plugin-state';
2
+ const key = (conversationId) => `jini:chat-agent-plugins:${conversationId}`;
3
+ function browserStorage() {
4
+ try {
5
+ return typeof window === 'undefined' ? undefined : window.localStorage;
6
+ }
7
+ catch {
8
+ return undefined;
9
+ }
10
+ }
11
+ function isStringList({ value }) {
12
+ return Array.isArray(value) && value.every(id => typeof id === 'string');
13
+ }
14
+ /** A stored row is usable only when its exclusions and (null or listed) decision are all string ids. */
15
+ function usableChatState({ value }) {
16
+ return value && isStringList({ value: value.excluded }) && (value.decision === null || isStringList({ value: value.decision?.selected })) ? value : null;
17
+ }
18
+ /** Corrupt/blocked storage permits a fresh question, as clearing localStorage does. */
19
+ export function readAgentPluginChatState({ conversationId }, { storage = browserStorage() } = {}) {
20
+ try {
21
+ const state = usableChatState({ value: JSON.parse(storage?.getItem(key(conversationId)) ?? 'null') });
22
+ if (state)
23
+ return state;
24
+ }
25
+ catch { /* Private browsing/corrupt rows behave as a new chat decision. */ }
26
+ return { decision: null, excluded: [] };
27
+ }
28
+ /** Persist only decision/exclusions. Existing exclusions win over an in-flight server response. */
29
+ export function writeAgentPluginChatState({ conversationId, state }, { storage = browserStorage() } = {}) {
30
+ const current = readAgentPluginChatState({ conversationId }, { storage });
31
+ const next = { decision: state.decision, excluded: [...new Set([...current.excluded, ...state.excluded])] };
32
+ try {
33
+ storage?.setItem(key(conversationId), JSON.stringify(next));
34
+ }
35
+ catch { /* Session can continue without persistence. */ }
36
+ if (typeof window !== 'undefined')
37
+ window.dispatchEvent(new CustomEvent(AGENT_PLUGIN_STATE_CHANGED, { detail: { conversationId, state: next } }));
38
+ }
39
+ /** Called only for the protocol's structured plugin event, before delivering it to the transcript. */
40
+ export function captureAgentPluginUsage({ event }, _options = {}) {
41
+ if (event.kind !== 'ext' || event.name !== 'agent_plugins')
42
+ return;
43
+ const usage = event.data;
44
+ if (typeof usage?.conversationId !== 'string' || !usage.state || !Array.isArray(usage.state.excluded))
45
+ return;
46
+ writeAgentPluginChatState({ conversationId: usage.conversationId, state: usage.state });
47
+ }
48
+ //# sourceMappingURL=agent-plugin-chat-state.js.map
@@ -80,34 +80,38 @@ export function createA2uiActionPoster({ baseUrl, path, fetch, reportFailure },
80
80
  }
81
81
  const controller = new AbortController();
82
82
  const timer = setTimeout(() => controller.abort(), timeoutMs);
83
- try {
84
- const response = await fetch(endpoint, {
85
- method: "POST",
86
- credentials: "same-origin",
87
- headers: { ...options.headers, "content-type": "application/json" },
88
- body: JSON.stringify({ exchangeId, message }),
89
- signal: controller.signal,
90
- });
91
- if (!response.ok) {
92
- const detail = await response.text().catch(() => "");
93
- reportFailure({ args: [`[a2ui] action delivery failed (${response.status}) for surface ${exchangeId}`, detail] }, {});
94
- return { ok: false, reason: reasonForStatus(response.status) };
95
- }
96
- return { ok: true };
97
- }
98
- catch (error) {
99
- const timedOut = error instanceof Error && error.name === "AbortError";
100
- reportFailure({ args: [`[a2ui] action delivery failed for surface ${exchangeId}`, timedOut ? `timed out after ${timeoutMs}ms` : error] }, {});
101
- return {
102
- ok: false,
103
- reason: timedOut
104
- ? "This took too long to deliver and was given up on."
105
- : "Couldn't reach the server to deliver this action.",
106
- };
107
- }
108
- finally {
109
- clearTimeout(timer);
110
- }
83
+ // Cleared on every outcome; a `.finally` rather than try/finally so no path falls through.
84
+ return postOnce({ endpoint, exchangeId, message, timeoutMs, reportFailure, fetch, signal: controller.signal, headers: options.headers })
85
+ .finally(() => clearTimeout(timer));
111
86
  };
112
87
  }
88
+ /** One POST attempt and its classification; never rejects. @complexity O(1). */
89
+ async function postOnce(attempt) {
90
+ const { exchangeId, reportFailure } = attempt;
91
+ try {
92
+ const response = await attempt.fetch(attempt.endpoint, {
93
+ method: "POST",
94
+ credentials: "same-origin",
95
+ headers: { ...attempt.headers, "content-type": "application/json" },
96
+ body: JSON.stringify({ exchangeId, message: attempt.message }),
97
+ signal: attempt.signal,
98
+ });
99
+ if (!response.ok) {
100
+ const detail = await response.text().catch(() => "");
101
+ reportFailure({ args: [`[a2ui] action delivery failed (${response.status}) for surface ${exchangeId}`, detail] }, {});
102
+ return { ok: false, reason: reasonForStatus(response.status) };
103
+ }
104
+ return { ok: true };
105
+ }
106
+ catch (error) {
107
+ const timedOut = error instanceof Error && error.name === "AbortError";
108
+ reportFailure({ args: [`[a2ui] action delivery failed for surface ${exchangeId}`, timedOut ? `timed out after ${attempt.timeoutMs}ms` : error] }, {});
109
+ return {
110
+ ok: false,
111
+ reason: timedOut
112
+ ? "This took too long to deliver and was given up on."
113
+ : "Couldn't reach the server to deliver this action.",
114
+ };
115
+ }
116
+ }
113
117
  //# sourceMappingURL=a2ui-action-poster.js.map
@@ -8,7 +8,7 @@ export interface AssistantConversation {
8
8
  updatedAt: number;
9
9
  }
10
10
  /**
11
- * The subset of a live transcript that is worth writing: user turns, and assistant turns whose run
11
+ * The subset of a live transcript that is worth writing: accepted user turns, and assistant turns whose run
12
12
  * has reached a terminal status.
13
13
  *
14
14
  * An assistant message that is still `queued` or `running` is rewritten on every delta, so
@@ -1,6 +1,6 @@
1
1
  import { isTerminalRunStatus } from "../messages.js";
2
2
  /**
3
- * The subset of a live transcript that is worth writing: user turns, and assistant turns whose run
3
+ * The subset of a live transcript that is worth writing: accepted user turns, and assistant turns whose run
4
4
  * has reached a terminal status.
5
5
  *
6
6
  * An assistant message that is still `queued` or `running` is rewritten on every delta, so
@@ -18,7 +18,7 @@ import { isTerminalRunStatus } from "../messages.js";
18
18
  * Deleting them here would trade a history bug for a forensics hole.
19
19
  */
20
20
  export function persistableMessages({ messages }, _options = {}) {
21
- return messages.filter((message) => message.role === "user" || isTerminalRunStatus({ status: message.runStatus }));
21
+ return messages.filter((message) => message.role === "user" ? !message.inputPending : isTerminalRunStatus({ status: message.runStatus }));
22
22
  }
23
23
  /**
24
24
  * The key the admin's chat writers use to remember "this message is already saved": the message id,
@@ -2,7 +2,7 @@ import type { AgentEvent } from "../events.js";
2
2
  import type { ChatMessage } from "../messages.js";
3
3
  import type { ChatTransport, RunHandlers, StartRunInput } from "../transport.js";
4
4
  import type { RunNotices } from "../run-events/entry.js";
5
- import type { createAgUiRunClient } from "./ag-ui-transport.js";
5
+ import type { AgUiRunClient } from "./ag-ui-transport.js";
6
6
  import type { DurableRunBinding } from "./durable-run-subscription.js";
7
7
  /** Per-turn dispatch input; hosts may return their fuller saved settings without UI dependencies. */
8
8
  export interface AssistantExecutionConfig {
@@ -127,7 +127,7 @@ export interface AssistantTransportPorts {
127
127
  messageId: unknown;
128
128
  conversationId?: string;
129
129
  }, optional: Record<string, never>) => Promise<Response>;
130
- readonly agUi: ReturnType<typeof createAgUiRunClient>;
130
+ readonly agUi: AgUiRunClient;
131
131
  }
132
132
  export interface ByokFrameContext {
133
133
  collected: AgentEvent[];