@jini-ai/chat 0.5.6 → 0.5.8
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.
- package/dist/core/ag-ui/entry.js +2 -0
- package/dist/core/assistant-client/durable-run-subscription.d.ts +4 -1
- package/dist/core/assistant-client/durable-run-subscription.js +13 -4
- package/dist/core/durable-projection.d.ts +3 -1
- package/dist/core/durable-projection.js +40 -7
- package/dist/core/events.d.ts +3 -0
- package/dist/core/history-tool-metadata.d.ts +23 -0
- package/dist/core/history-tool-metadata.js +13 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +4 -0
- package/dist/core/persistence/title.d.ts +24 -0
- package/dist/core/persistence/title.js +60 -9
- package/dist/core/run-activity.d.ts +15 -2
- package/dist/core/run-activity.js +6 -4
- package/dist/core/run-events/entry.d.ts +12 -1
- package/dist/core/run-events/entry.js +19 -6
- package/dist/core/tool-call-detail.d.ts +51 -0
- package/dist/core/tool-call-detail.js +67 -0
- package/dist/core/turn-usage.d.ts +50 -0
- package/dist/core/turn-usage.js +114 -0
- package/dist/core/usage-format.d.ts +10 -0
- package/dist/core/usage-format.js +15 -0
- package/dist/embed-widget/browser/lazy-loader.d.ts +34 -0
- package/dist/embed-widget/browser/lazy-loader.js +116 -0
- package/dist/embed-widget/react/dialog.hooks.d.ts +28 -0
- package/dist/embed-widget/react/dialog.hooks.js +54 -0
- package/dist/embed-widget/react/embed.d.ts +1 -0
- package/dist/embed-widget/react/embed.js +5 -3
- package/dist/embed-widget/react/header.d.ts +3 -1
- package/dist/embed-widget/react/header.hooks.d.ts +13 -0
- package/dist/embed-widget/react/header.hooks.js +32 -0
- package/dist/embed-widget/react/header.js +4 -14
- package/dist/embed-widget/vanilla/index.d.ts +63 -0
- package/dist/embed-widget/vanilla/index.js +319 -0
- package/dist/embed-widget/vanilla/state.d.ts +48 -0
- package/dist/embed-widget/vanilla/state.js +120 -0
- package/dist/embed-widget/vanilla/style.css +29 -0
- package/dist/react/chat-pane.d.ts +2 -2
- package/dist/react/chat-pane.js +1 -1
- package/dist/react/components/A2uiSurfaceCard.d.ts +5 -1
- package/dist/react/components/A2uiSurfaceCard.js +9 -9
- package/dist/react/components/AttachmentPreviewModal.hooks.d.ts +7 -0
- package/dist/react/components/AttachmentPreviewModal.hooks.js +1 -1
- package/dist/react/components/ChatFab.d.ts +4 -1
- package/dist/react/components/ChatFab.js +2 -2
- package/dist/react/components/Composer.d.ts +2 -0
- package/dist/react/components/Composer.js +5 -2
- package/dist/react/components/ConversationList.d.ts +8 -1
- package/dist/react/components/ConversationList.hooks.d.ts +18 -0
- package/dist/react/components/ConversationList.hooks.js +25 -0
- package/dist/react/components/ConversationList.js +12 -5
- package/dist/react/components/ConversationList.rules.d.ts +25 -0
- package/dist/react/components/ConversationList.rules.js +19 -0
- package/dist/react/components/Markdown.js +6 -6
- package/dist/react/components/McpUiSurfaceCard.d.ts +1 -1
- package/dist/react/components/McpUiSurfaceCard.js +8 -4
- package/dist/react/components/MessageList.d.ts +8 -13
- package/dist/react/components/MessageList.hooks.d.ts +4 -0
- package/dist/react/components/MessageList.hooks.js +6 -1
- package/dist/react/components/MessageList.js +23 -4
- package/dist/react/components/MessageRow.d.ts +23 -1
- package/dist/react/components/MessageRow.js +61 -36
- package/dist/react/components/MessageRow.usage.d.ts +48 -0
- package/dist/react/components/MessageRow.usage.js +47 -0
- package/dist/react/components/MidRunUserMessage.d.ts +17 -0
- package/dist/react/components/MidRunUserMessage.js +41 -0
- package/dist/react/components/SentAttachment.d.ts +19 -0
- package/dist/react/components/SentAttachment.hooks.d.ts +44 -0
- package/dist/react/components/SentAttachment.hooks.js +56 -0
- package/dist/react/components/SentAttachment.js +14 -0
- package/dist/react/components/ToolCallGroup.d.ts +12 -0
- package/dist/react/components/ToolCallGroup.hooks.d.ts +20 -0
- package/dist/react/components/ToolCallGroup.hooks.js +23 -0
- package/dist/react/components/ToolCallGroup.js +13 -0
- package/dist/react/components/ToolCard.js +41 -22
- package/dist/react/components/mcp-ui-surface-state.d.ts +21 -0
- package/dist/react/components/mcp-ui-surface-state.js +26 -0
- package/dist/react/ext-event-renderer-registry.d.ts +7 -0
- package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
- package/dist/react/features/chat-pane/components/ChatPane.js +20 -7
- package/dist/react/features/chat-pane/create-mid-run-message-poster.d.ts +44 -0
- package/dist/react/features/chat-pane/create-mid-run-message-poster.js +69 -0
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +4 -1
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +65 -10
- package/dist/react/features/chat-pane/hooks/useChatPaneNewTabLinks.hooks.d.ts +25 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneNewTabLinks.hooks.js +70 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +4 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSuggestion.hooks.d.ts +9 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSuggestion.hooks.js +11 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSurface.hooks.d.ts +17 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSurface.hooks.js +15 -0
- package/dist/react/features/chat-pane/index.d.ts +2 -1
- package/dist/react/features/chat-pane/index.js +1 -0
- package/dist/react/features/chat-pane/rules.d.ts +27 -0
- package/dist/react/features/chat-pane/rules.js +32 -0
- package/dist/react/features/chat-pane/styles.d.ts +1 -1
- package/dist/react/features/chat-pane/styles.js +92 -1
- package/dist/react/features/chat-pane/types.d.ts +39 -1
- package/dist/react/hooks/composer-draft-cache.d.ts +15 -0
- package/dist/react/hooks/composer-draft-cache.js +20 -0
- package/dist/react/hooks/useAnsweredCards.d.ts +20 -0
- package/dist/react/hooks/useAnsweredCards.js +23 -0
- package/dist/react/hooks/useComposer.d.ts +14 -0
- package/dist/react/hooks/useComposer.js +78 -6
- package/dist/react/hooks/useConversation.d.ts +8 -0
- package/dist/react/hooks/useConversation.js +34 -0
- package/dist/react/hooks/useRunActivity.d.ts +5 -3
- package/dist/react/hooks/useRunActivity.js +4 -3
- package/dist/react/index.d.ts +8 -2
- package/dist/react/index.js +6 -1
- package/dist/react/message-blocks.d.ts +22 -3
- package/dist/react/message-blocks.js +25 -3
- package/dist/react/tool-call-display.d.ts +90 -0
- package/dist/react/tool-call-display.js +97 -0
- package/dist/server/durable-runs/recover.js +19 -11
- package/dist/server/run-finalizer/entry.js +2 -1
- package/dist/store/ports.d.ts +25 -2
- package/dist/store/runtime/durable-run-store.js +5 -2
- package/dist/store/runtime/entry.d.ts +1 -0
- package/dist/store/runtime/entry.js +1 -0
- package/dist/store/runtime/history-tools.d.ts +27 -0
- package/dist/store/runtime/history-tools.js +169 -0
- package/dist/store/runtime/run-ledger.js +5 -2
- package/dist/store/sql/store.js +52 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js +22 -0
- package/package.json +48 -18
package/dist/core/ag-ui/entry.js
CHANGED
|
@@ -15,6 +15,8 @@ function reduceUsagePayload(payload) {
|
|
|
15
15
|
...(typeof usage.input_tokens === "number" ? { inputTokens: usage.input_tokens } : {}),
|
|
16
16
|
...(typeof usage.output_tokens === "number" ? { outputTokens: usage.output_tokens } : {}),
|
|
17
17
|
...(typeof payload.costUsd === "number" ? { costUsd: payload.costUsd } : {}),
|
|
18
|
+
...(typeof payload.cumulativeCostUsd === "number" ? { cumulativeCostUsd: payload.cumulativeCostUsd } : {}),
|
|
19
|
+
...(typeof payload.costSessionId === "string" ? { costSessionId: payload.costSessionId } : {}),
|
|
18
20
|
...(typeof payload.durationMs === "number" ? { durationMs: payload.durationMs } : {}),
|
|
19
21
|
};
|
|
20
22
|
}
|
|
@@ -6,10 +6,13 @@ export interface DurableRunBinding {
|
|
|
6
6
|
readonly conversationId: string;
|
|
7
7
|
}
|
|
8
8
|
export interface DurableSubscriptionPorts {
|
|
9
|
+
/** Absent recover intent preserves legacy recovery; false requests only the saved checkpoint. */
|
|
9
10
|
readonly read: (required: {
|
|
10
11
|
runId: string;
|
|
11
12
|
messageId?: string;
|
|
12
|
-
}, optional: {
|
|
13
|
+
}, optional: {
|
|
14
|
+
recover?: boolean;
|
|
15
|
+
}) => Promise<ChatMessage | null | "gone">;
|
|
13
16
|
readonly stream: (required: {
|
|
14
17
|
runId: string;
|
|
15
18
|
cursor: string;
|
|
@@ -6,6 +6,7 @@ export function followDurableRun({ runId: initialRunId, handlers, signal, bindin
|
|
|
6
6
|
let cursor = "";
|
|
7
7
|
let stopped = false;
|
|
8
8
|
let reading = false;
|
|
9
|
+
let recoveryPending = true;
|
|
9
10
|
let dropped = false;
|
|
10
11
|
let stopStream;
|
|
11
12
|
let stopTimer;
|
|
@@ -26,7 +27,7 @@ export function followDurableRun({ runId: initialRunId, handlers, signal, bindin
|
|
|
26
27
|
function open() {
|
|
27
28
|
dropped = false;
|
|
28
29
|
stopStream?.();
|
|
29
|
-
stopStream = ports.stream({ runId, cursor, frame, dropped: () => { dropped = true; void read().catch(() => undefined); } }, {});
|
|
30
|
+
stopStream = ports.stream({ runId, cursor, frame, dropped: () => { dropped = true; recoveryPending = true; void read().catch(() => undefined); } }, {});
|
|
30
31
|
}
|
|
31
32
|
function frame(kind, data, nextCursor) {
|
|
32
33
|
if (stopped)
|
|
@@ -36,6 +37,7 @@ export function followDurableRun({ runId: initialRunId, handlers, signal, bindin
|
|
|
36
37
|
const outcome = translateRunFrame({ kind, raw: data, notices }, {});
|
|
37
38
|
// An attempt's error/end may be followed by a continuation. Only the row can finalize it.
|
|
38
39
|
if (outcome.terminal || outcome.error) {
|
|
40
|
+
recoveryPending = true;
|
|
39
41
|
void read().catch(() => undefined);
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
@@ -52,8 +54,11 @@ export function followDurableRun({ runId: initialRunId, handlers, signal, bindin
|
|
|
52
54
|
reading = true;
|
|
53
55
|
stopTimer?.();
|
|
54
56
|
stopTimer = undefined;
|
|
57
|
+
const recover = recoveryPending;
|
|
58
|
+
// Consume only when a read starts: faults during its await latch one follow-up recovery.
|
|
59
|
+
recoveryPending = false;
|
|
55
60
|
try {
|
|
56
|
-
const message = await ports.read({ runId, ...(messageId ? { messageId } : {}) }, {});
|
|
61
|
+
const message = await ports.read({ runId, ...(messageId ? { messageId } : {}) }, { recover });
|
|
57
62
|
if (stopped || !message)
|
|
58
63
|
return;
|
|
59
64
|
if (message === "gone") {
|
|
@@ -64,8 +69,12 @@ export function followDurableRun({ runId: initialRunId, handlers, signal, bindin
|
|
|
64
69
|
}
|
|
65
70
|
finally {
|
|
66
71
|
reading = false;
|
|
67
|
-
if (!stopped)
|
|
68
|
-
|
|
72
|
+
if (!stopped) {
|
|
73
|
+
if (recoveryPending)
|
|
74
|
+
void read().catch(() => undefined);
|
|
75
|
+
else
|
|
76
|
+
stopTimer = ports.schedule({ work: () => void read().catch(() => undefined), delayMs: 1_000 }, {});
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
}
|
|
71
80
|
function applySavedMessage(message) {
|
|
@@ -24,5 +24,7 @@ export declare function terminalMessageNotice({ message }: {
|
|
|
24
24
|
* visible text can justify a Continued divider; the recovery marker still records the attempt. */
|
|
25
25
|
export declare function recoveredRunEvents({ saved }: {
|
|
26
26
|
saved: readonly AgentEvent[];
|
|
27
|
-
},
|
|
27
|
+
}, { notice }?: {
|
|
28
|
+
notice?: 'visible' | 'silent';
|
|
29
|
+
}): AgentEvent[];
|
|
28
30
|
//# sourceMappingURL=durable-projection.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function identity(event) {
|
|
1
|
+
function identity(event, usageOccurrences) {
|
|
2
2
|
const wire = event;
|
|
3
3
|
if (wire.eventId !== undefined)
|
|
4
4
|
return `event:${wire.eventId}`;
|
|
@@ -8,7 +8,15 @@ function identity(event) {
|
|
|
8
8
|
return `use:${event.id}`;
|
|
9
9
|
if (event.kind === 'tool_result')
|
|
10
10
|
return `result:${event.toolUseId}`;
|
|
11
|
-
|
|
11
|
+
const snapshot = JSON.stringify(event);
|
|
12
|
+
if (event.kind === 'usage') {
|
|
13
|
+
// Two segments can have identical counters. Match occurrence-for-occurrence during replay,
|
|
14
|
+
// rather than treating equal charges as the same event and silently dropping one segment.
|
|
15
|
+
const occurrence = (usageOccurrences.get(snapshot) ?? 0) + 1;
|
|
16
|
+
usageOccurrences.set(snapshot, occurrence);
|
|
17
|
+
return `usage:${snapshot}:${occurrence}`;
|
|
18
|
+
}
|
|
19
|
+
return snapshot;
|
|
12
20
|
}
|
|
13
21
|
function textOf(events) {
|
|
14
22
|
return events.filter((event) => event.kind === 'text').map((event) => event.text).join('');
|
|
@@ -18,7 +26,9 @@ function textOf(events) {
|
|
|
18
26
|
export function mergeRunEvents({ saved, incoming }, _optional = {}) {
|
|
19
27
|
const previous = saved ?? [];
|
|
20
28
|
const result = [...previous];
|
|
21
|
-
const
|
|
29
|
+
const savedUsage = new Map();
|
|
30
|
+
const incomingUsage = new Map();
|
|
31
|
+
const keys = new Set(previous.map(event => identity(event, savedUsage)));
|
|
22
32
|
const before = textOf(previous);
|
|
23
33
|
const after = textOf(incoming);
|
|
24
34
|
let skip = after.startsWith(before) ? before.length : after.length;
|
|
@@ -29,9 +39,12 @@ export function mergeRunEvents({ saved, incoming }, _optional = {}) {
|
|
|
29
39
|
if (text)
|
|
30
40
|
result.push({ ...event, text });
|
|
31
41
|
}
|
|
32
|
-
else
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
else {
|
|
43
|
+
const key = identity(event, incomingUsage);
|
|
44
|
+
if (!keys.has(key)) {
|
|
45
|
+
keys.add(key);
|
|
46
|
+
result.push(event);
|
|
47
|
+
}
|
|
35
48
|
}
|
|
36
49
|
}
|
|
37
50
|
return result;
|
|
@@ -44,6 +57,9 @@ export function continuingRunNotice({ events, active }, _optional = {}) {
|
|
|
44
57
|
const marker = list.findLastIndex((event) => event.kind === 'status' && event.code === 'run_recovering');
|
|
45
58
|
if (marker < 0)
|
|
46
59
|
return null;
|
|
60
|
+
const markerEvent = list[marker];
|
|
61
|
+
if (markerEvent?.kind === 'status' && markerEvent.label === '')
|
|
62
|
+
return null;
|
|
47
63
|
const progressed = list.slice(marker + 1).some((event) => event.kind === 'text' || event.kind === 'tool_use' || event.kind === 'tool_result');
|
|
48
64
|
return progressed ? null : 'Continuing…';
|
|
49
65
|
}
|
|
@@ -63,7 +79,24 @@ export function terminalMessageNotice({ message }, _optional = {}) {
|
|
|
63
79
|
}
|
|
64
80
|
/** The first answer has no earlier segment to divide, even if startup was retried. Only saved
|
|
65
81
|
* visible text can justify a Continued divider; the recovery marker still records the attempt. */
|
|
66
|
-
export function recoveredRunEvents({ saved },
|
|
82
|
+
export function recoveredRunEvents({ saved }, { notice = 'visible' } = {}) {
|
|
83
|
+
if (notice === 'silent') {
|
|
84
|
+
// A missing rollout is an expected reconstruction fallback, not a failed user turn. Keep an
|
|
85
|
+
// invisible attempt marker for fencing/diagnostic offsets, and preserve all unrelated work.
|
|
86
|
+
const attemptStart = saved.findLastIndex(event => event.kind === 'status' && event.code === 'run_recovering') + 1;
|
|
87
|
+
const diagnostic = saved.findIndex((event, index) => index >= attemptStart && ((event.kind === 'status' && /no rollout found for thread id/.test(`${event.label}\n${event.detail ?? ''}`)) ||
|
|
88
|
+
(event.kind === 'raw' && /no rollout found for thread id/.test(event.line))));
|
|
89
|
+
const events = saved.filter((event, index) => {
|
|
90
|
+
if (diagnostic < 0 || index < attemptStart)
|
|
91
|
+
return true;
|
|
92
|
+
if (event.kind === 'raw')
|
|
93
|
+
return !/no rollout found for thread id/.test(event.line);
|
|
94
|
+
if (event.kind === 'status')
|
|
95
|
+
return event.label !== 'Run failed — the agent process exited without answering' && !/no rollout found for thread id/.test(`${event.label}\n${event.detail ?? ''}`);
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
return [...events, { kind: 'status', code: 'run_recovering', label: '' }];
|
|
99
|
+
}
|
|
67
100
|
const hasAnswer = saved.some(event => event.kind === 'text' && event.text.trim().length > 0);
|
|
68
101
|
return [...saved, ...(hasAnswer ? [{ kind: 'text', text: '\n\n---\n\nContinued\n\n' }] : []),
|
|
69
102
|
{ kind: 'status', code: 'run_recovering', label: 'Continuing…' }];
|
package/dist/core/events.d.ts
CHANGED
|
@@ -65,6 +65,9 @@ export type AgentEvent = {
|
|
|
65
65
|
inputTokens?: number;
|
|
66
66
|
outputTokens?: number;
|
|
67
67
|
costUsd?: number;
|
|
68
|
+
/** CLI session snapshot, persisted in events_json; reconciled across turns before display. */
|
|
69
|
+
cumulativeCostUsd?: number;
|
|
70
|
+
costSessionId?: string;
|
|
68
71
|
durationMs?: number;
|
|
69
72
|
stopReason?: string;
|
|
70
73
|
} | {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Discovery and read approval for the generic history tools, shared with host projections. */
|
|
2
|
+
export declare const assistantHistoryToolMetadata: {
|
|
3
|
+
readonly assistant_chats_list: {
|
|
4
|
+
readonly search: {
|
|
5
|
+
readonly keywords: "previous earlier interrupted chat conversation history search resume continue what we were doing";
|
|
6
|
+
readonly queries: readonly ["I had to start a new chat. Can you find what we were doing?", "Find my previous conversation."];
|
|
7
|
+
};
|
|
8
|
+
readonly approval: {
|
|
9
|
+
readonly class: "read";
|
|
10
|
+
readonly confirmation: "direct";
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
readonly assistant_chat_read: {
|
|
14
|
+
readonly search: {
|
|
15
|
+
readonly keywords: "read previous chat transcript history tail messages resume interrupted continue conversation";
|
|
16
|
+
};
|
|
17
|
+
readonly approval: {
|
|
18
|
+
readonly class: "read";
|
|
19
|
+
readonly confirmation: "direct";
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=history-tool-metadata.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Discovery and read approval for the generic history tools, shared with host projections. */
|
|
2
|
+
export const assistantHistoryToolMetadata = {
|
|
3
|
+
assistant_chats_list: {
|
|
4
|
+
search: { keywords: 'previous earlier interrupted chat conversation history search resume continue what we were doing',
|
|
5
|
+
queries: ['I had to start a new chat. Can you find what we were doing?', 'Find my previous conversation.'] },
|
|
6
|
+
approval: { class: 'read', confirmation: 'direct' },
|
|
7
|
+
},
|
|
8
|
+
assistant_chat_read: {
|
|
9
|
+
search: { keywords: 'read previous chat transcript history tail messages resume interrupted continue conversation' },
|
|
10
|
+
approval: { class: 'read', confirmation: 'direct' },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=history-tool-metadata.js.map
|
package/dist/core/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './messages.js';
|
|
|
8
8
|
export * from './partial-json.js';
|
|
9
9
|
export * from './tool-events.js';
|
|
10
10
|
export * from './tool-output.js';
|
|
11
|
+
export * from './tool-call-detail.js';
|
|
11
12
|
export * from './transport.js';
|
|
12
13
|
export * from './tools.js';
|
|
13
14
|
export * from './todos.js';
|
|
@@ -32,4 +33,7 @@ export { recoveredRunEvents } from './durable-projection.js';
|
|
|
32
33
|
export { composerHistoryKeyAction, createComposerHistoryState, mergeComposerHistory, normalizeComposerHistory, transitionComposerHistory, } from './composer-history.js';
|
|
33
34
|
export type { ComposerHistoryKeyInput, ComposerHistoryState, ComposerHistoryStoragePort } from './composer-history.js';
|
|
34
35
|
export * from './user-text-redaction.js';
|
|
36
|
+
export * from './history-tool-metadata.js';
|
|
37
|
+
export { turnUsage, conversationUsage, replySegments, isReplyBoundary, MID_RUN_USER_MESSAGE_EVENT_NAME, type ChatUsageSummary } from './turn-usage.js';
|
|
38
|
+
export { formatUsageCost } from './usage-format.js';
|
|
35
39
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from './messages.js';
|
|
|
8
8
|
export * from './partial-json.js';
|
|
9
9
|
export * from './tool-events.js';
|
|
10
10
|
export * from './tool-output.js';
|
|
11
|
+
export * from './tool-call-detail.js';
|
|
11
12
|
export * from './transport.js';
|
|
12
13
|
export * from './tools.js';
|
|
13
14
|
export * from './todos.js';
|
|
@@ -31,4 +32,7 @@ export * from './persistence/index.js';
|
|
|
31
32
|
export { recoveredRunEvents } from './durable-projection.js';
|
|
32
33
|
export { composerHistoryKeyAction, createComposerHistoryState, mergeComposerHistory, normalizeComposerHistory, transitionComposerHistory, } from './composer-history.js';
|
|
33
34
|
export * from './user-text-redaction.js';
|
|
35
|
+
export * from './history-tool-metadata.js';
|
|
36
|
+
export { turnUsage, conversationUsage, replySegments, isReplyBoundary, MID_RUN_USER_MESSAGE_EVENT_NAME } from './turn-usage.js';
|
|
37
|
+
export { formatUsageCost } from './usage-format.js';
|
|
34
38
|
//# sourceMappingURL=index.js.map
|
|
@@ -23,6 +23,18 @@
|
|
|
23
23
|
* 2. `LATIN_NEVER_TRAILING` trims function words off the end. The original's word cap lands
|
|
24
24
|
* mid-clause on question-shaped prompts and strands them ("...Posts Do I").
|
|
25
25
|
*/
|
|
26
|
+
/** Remove presentation markup from title text while preserving link labels and inline code.
|
|
27
|
+
* Shared by prompt derivation, stored-title display and product naming adapters; no DOM or I/O.
|
|
28
|
+
* Fenced programs and reference definitions are not titles. Only paired emphasis is stripped:
|
|
29
|
+
* glob stars and identifier underscores are meaningful text. Older saved titles may already have
|
|
30
|
+
* been cut through a markdown span; cut links keep their partial label, unpaired emphasis stays.
|
|
31
|
+
* @param input.text - A prompt or a previously saved title.
|
|
32
|
+
* @returns Plain text with whitespace collapsed; identifiers keep their internal underscores.
|
|
33
|
+
* @complexity O(n²) worst-case regex time for unmatched spans; O(n) space in title/prompt length.
|
|
34
|
+
*/
|
|
35
|
+
export declare function stripConversationTitleMarkdown({ text }: {
|
|
36
|
+
text: string;
|
|
37
|
+
}, _options?: {}): string;
|
|
26
38
|
/**
|
|
27
39
|
* Returns a short display title for a conversation, or `''` when the prompt yields nothing
|
|
28
40
|
* usable (a bare URL, a lone code fence, pure punctuation). Callers should treat `''` as "leave
|
|
@@ -33,5 +45,17 @@
|
|
|
33
45
|
*/
|
|
34
46
|
export declare function deriveConversationTitle({ prompt }: {
|
|
35
47
|
prompt: string;
|
|
48
|
+
}, { casing }?: {
|
|
49
|
+
casing?: 'title' | 'sentence';
|
|
36
50
|
}): string;
|
|
51
|
+
/** Sentence-case only a known heuristic title using the original prompt's proper names/acronyms.
|
|
52
|
+
* Manual names, model-authored names and unloaded transcripts keep their saved wording, with
|
|
53
|
+
* presentation markup removed and the display ceiling applied; no writes.
|
|
54
|
+
* @complexity O(n) time/space in the first prompt's length, using the existing bounded heuristic.
|
|
55
|
+
*/
|
|
56
|
+
export declare function conversationTitleForDisplay({ title, titleSource, firstPrompt }: {
|
|
57
|
+
title: string | null;
|
|
58
|
+
titleSource: 'fallback' | 'generated' | 'manual';
|
|
59
|
+
firstPrompt?: string | undefined;
|
|
60
|
+
}, _options?: {}): string | null;
|
|
37
61
|
//# sourceMappingURL=title.d.ts.map
|
|
@@ -76,23 +76,60 @@ const LATIN_NEVER_TRAILING = new Set([
|
|
|
76
76
|
'from', 'into', 'about', 'so', 'just', 'also', 'too', 'up', 'out',
|
|
77
77
|
]);
|
|
78
78
|
/**
|
|
79
|
-
* Strips the parts of a prompt that are never a good title: fenced
|
|
79
|
+
* Strips the parts of a prompt that are never a good title: fenced code, URLs, and
|
|
80
80
|
* `@handle`/`#tag` tokens. Each of these is high-entropy and low-meaning in a six-word summary.
|
|
81
|
+
* Inline code keeps its label with delimiters removed, so a filename can still name the task.
|
|
81
82
|
*/
|
|
82
83
|
function cleanPrompt(prompt) {
|
|
83
|
-
return prompt
|
|
84
|
-
.replace(/```[\s\S]*?```/g, ' ')
|
|
85
|
-
.replace(/`[^`]*`/g, ' ')
|
|
84
|
+
return stripConversationTitleMarkdown({ text: prompt })
|
|
86
85
|
.replace(/https?:\/\/\S+/g, ' ')
|
|
87
86
|
.replace(/[@#][\w.-]+/g, ' ')
|
|
88
87
|
.replace(/[“”"']/g, '')
|
|
89
88
|
.replace(/\s+/g, ' ')
|
|
90
89
|
.trim();
|
|
91
90
|
}
|
|
91
|
+
/** Remove presentation markup from title text while preserving link labels and inline code.
|
|
92
|
+
* Shared by prompt derivation, stored-title display and product naming adapters; no DOM or I/O.
|
|
93
|
+
* Fenced programs and reference definitions are not titles. Only paired emphasis is stripped:
|
|
94
|
+
* glob stars and identifier underscores are meaningful text. Older saved titles may already have
|
|
95
|
+
* been cut through a markdown span; cut links keep their partial label, unpaired emphasis stays.
|
|
96
|
+
* @param input.text - A prompt or a previously saved title.
|
|
97
|
+
* @returns Plain text with whitespace collapsed; identifiers keep their internal underscores.
|
|
98
|
+
* @complexity O(n²) worst-case regex time for unmatched spans; O(n) space in title/prompt length.
|
|
99
|
+
*/
|
|
100
|
+
export function stripConversationTitleMarkdown({ text }, _options = {}) {
|
|
101
|
+
return text
|
|
102
|
+
.replace(/```[\s\S]*?(?:```|$)/g, ' ')
|
|
103
|
+
.replace(/^\s{0,3}\[[^\]\n]+\]:[^\n]*(?:\n|$)/gm, ' ')
|
|
104
|
+
.replace(/!?\[([^\[\]\n]+)\]\((?:[^()[\]\n]|\([^()[\]\n]*\))*(?:\)|$)/g, '$1')
|
|
105
|
+
.replace(/\[([^\[\]\n]+)\]\[[^\[\]\n]*(?:\]|$)/g, '$1')
|
|
106
|
+
.replace(/^\s{0,3}#{1,6}\s+|[ \t]+#+[ \t]*$/gm, '')
|
|
107
|
+
.replace(/(?<![\w\\*])(\*\*|\*)(?=[^\s*])([^*\n]*?[^\s*\\])\1(?![\w*])/g, '$2')
|
|
108
|
+
.replace(/(?<![\w\\_])(__|_)(?=[^\s_])([^\n]*?[^\s\\])\1(?![\w_])/g, '$2')
|
|
109
|
+
.replace(/`|~~/g, '')
|
|
110
|
+
.replace(/!?\[([^\]\n]*)(?=\n|$)/g, '$1')
|
|
111
|
+
// Unescape after markup removal so an escaped literal marker cannot become emphasis.
|
|
112
|
+
.replace(/\\([\\`*_[\]~])/g, '$1')
|
|
113
|
+
.replace(/\s+/g, ' ')
|
|
114
|
+
.trim();
|
|
115
|
+
}
|
|
116
|
+
/** Keep complete words at the display ceiling. A single oversized token still gets a bounded
|
|
117
|
+
* prefix: pasted blobs must never become megabyte-sized stored/listed titles.
|
|
118
|
+
* @complexity O(n) time/space, bounded by the title ceiling after the initial length check.
|
|
119
|
+
*/
|
|
120
|
+
function truncateTitle(title) {
|
|
121
|
+
if (title.length <= MAX_TITLE_CHARS)
|
|
122
|
+
return title;
|
|
123
|
+
const prefix = title.slice(0, MAX_TITLE_CHARS);
|
|
124
|
+
if (/\s/.test(title.charAt(MAX_TITLE_CHARS)))
|
|
125
|
+
return prefix.trimEnd();
|
|
126
|
+
const lastSpace = prefix.lastIndexOf(' ');
|
|
127
|
+
return (lastSpace > 0 ? prefix.slice(0, lastSpace) : prefix).trimEnd();
|
|
128
|
+
}
|
|
92
129
|
function toTitleCase(word) {
|
|
93
130
|
return word ? word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() : word;
|
|
94
131
|
}
|
|
95
|
-
function trimLatinTitle(input) {
|
|
132
|
+
function trimLatinTitle(input, { casing }) {
|
|
96
133
|
const words = input
|
|
97
134
|
.replace(LEADING_LATIN_FILLER, '')
|
|
98
135
|
.replace(/[^\p{L}\p{N}\s-]/gu, ' ')
|
|
@@ -104,7 +141,8 @@ function trimLatinTitle(input) {
|
|
|
104
141
|
while (words.length > 0 && LATIN_NEVER_TRAILING.has(words[words.length - 1].toLowerCase())) {
|
|
105
142
|
words.pop();
|
|
106
143
|
}
|
|
107
|
-
|
|
144
|
+
const title = casing === 'title' ? words.map(toTitleCase).join(' ') : words.join(' ');
|
|
145
|
+
return title ? title.charAt(0).toUpperCase() + title.slice(1) : title;
|
|
108
146
|
}
|
|
109
147
|
function trimCjkTitle(input) {
|
|
110
148
|
let title = input.trim();
|
|
@@ -122,16 +160,29 @@ function trimCjkTitle(input) {
|
|
|
122
160
|
*
|
|
123
161
|
* @complexity O(n) in prompt length; no allocation beyond the intermediate strings.
|
|
124
162
|
*/
|
|
125
|
-
export function deriveConversationTitle({ prompt }) {
|
|
163
|
+
export function deriveConversationTitle({ prompt }, { casing = 'title' } = {}) {
|
|
126
164
|
const cleaned = cleanPrompt(prompt);
|
|
127
165
|
if (!cleaned)
|
|
128
166
|
return '';
|
|
129
167
|
const firstClause = cleaned.split(/[\n\r。!?!?]/)[0]?.trim() ?? cleaned;
|
|
130
168
|
if (!firstClause)
|
|
131
169
|
return '';
|
|
132
|
-
const title = CJK_PATTERN.test(firstClause) ? trimCjkTitle(firstClause) : trimLatinTitle(firstClause);
|
|
170
|
+
const title = CJK_PATTERN.test(firstClause) ? trimCjkTitle(firstClause) : trimLatinTitle(firstClause, { casing });
|
|
133
171
|
// Truncated without an ellipsis: this is a working title, not prose, and a `…` would end up stored
|
|
134
172
|
// and shown as if the author wrote it.
|
|
135
|
-
return title
|
|
173
|
+
return truncateTitle(title);
|
|
174
|
+
}
|
|
175
|
+
/** Sentence-case only a known heuristic title using the original prompt's proper names/acronyms.
|
|
176
|
+
* Manual names, model-authored names and unloaded transcripts keep their saved wording, with
|
|
177
|
+
* presentation markup removed and the display ceiling applied; no writes.
|
|
178
|
+
* @complexity O(n) time/space in the first prompt's length, using the existing bounded heuristic.
|
|
179
|
+
*/
|
|
180
|
+
export function conversationTitleForDisplay({ title, titleSource, firstPrompt }, _options = {}) {
|
|
181
|
+
if (!title)
|
|
182
|
+
return title;
|
|
183
|
+
if (titleSource === 'generated' && firstPrompt && title === deriveConversationTitle({ prompt: firstPrompt })) {
|
|
184
|
+
return deriveConversationTitle({ prompt: firstPrompt }, { casing: 'sentence' });
|
|
185
|
+
}
|
|
186
|
+
return truncateTitle(stripConversationTitleMarkdown({ text: title })) || null;
|
|
136
187
|
}
|
|
137
188
|
//# sourceMappingURL=title.js.map
|
|
@@ -34,6 +34,19 @@ export declare function humanizeToolName({ name }: {
|
|
|
34
34
|
}, { input }?: {
|
|
35
35
|
input?: (unknown) | undefined;
|
|
36
36
|
}): string;
|
|
37
|
+
/** Whether the person already answered a card, before its tool call returned. */
|
|
38
|
+
export type IsAnsweredCard = (card: {
|
|
39
|
+
name: string;
|
|
40
|
+
data: unknown;
|
|
41
|
+
}) => boolean;
|
|
42
|
+
export interface RunActivityOptions {
|
|
43
|
+
/**
|
|
44
|
+
* A card the person answered no longer waits on them, even though its tool call stays open while
|
|
45
|
+
* the confirmed work runs (a publish can take minutes). Without this the line kept saying
|
|
46
|
+
* "Waiting for your answer above" under a card that already read "Done." (owner, 2026-10-11).
|
|
47
|
+
*/
|
|
48
|
+
isAnsweredCard?: IsAnsweredCard | undefined;
|
|
49
|
+
}
|
|
37
50
|
/**
|
|
38
51
|
* The latest signal in a running turn's events.
|
|
39
52
|
*
|
|
@@ -46,7 +59,7 @@ export declare function humanizeToolName({ name }: {
|
|
|
46
59
|
*/
|
|
47
60
|
export declare function deriveRunActivity({ events }: {
|
|
48
61
|
events: readonly AgentEvent[] | undefined;
|
|
49
|
-
}): RunActivityState;
|
|
62
|
+
}, { isAnsweredCard }?: RunActivityOptions): RunActivityState;
|
|
50
63
|
/**
|
|
51
64
|
* Whether the run is waiting on the person: a card (form, confirm, sign-in) arrived while its tool
|
|
52
65
|
* call is still open. Hosts use it to hide "still working" notices, which read as a hung run while
|
|
@@ -56,7 +69,7 @@ export declare function deriveRunActivity({ events }: {
|
|
|
56
69
|
*/
|
|
57
70
|
export declare function isAwaitingAnswer({ events }: {
|
|
58
71
|
events: readonly AgentEvent[] | undefined;
|
|
59
|
-
}): boolean;
|
|
72
|
+
}, options?: RunActivityOptions): boolean;
|
|
60
73
|
/** `4` → `"4 s"`, `80` → `"1 m 20 s"`, `120` → `"2 m 0 s"`. */
|
|
61
74
|
export declare function formatActivityClock({ totalSeconds }: {
|
|
62
75
|
totalSeconds: number;
|
|
@@ -67,6 +67,8 @@ function isVisible(event) {
|
|
|
67
67
|
function isAnswerCard(scan, name, data) {
|
|
68
68
|
if (!CARD_EXT_NAMES.has(name))
|
|
69
69
|
return false;
|
|
70
|
+
if (scan.isAnsweredCard?.({ name, data }) === true)
|
|
71
|
+
return false;
|
|
70
72
|
if (name !== A2UI_EXT_NAME)
|
|
71
73
|
return true;
|
|
72
74
|
const displayOnlyId = displayOnlySurfaceIdOf({ message: data });
|
|
@@ -109,8 +111,8 @@ function scanEvent(scan, event, index) {
|
|
|
109
111
|
*
|
|
110
112
|
* @complexity O(n) in events, O(open tool calls) extra space.
|
|
111
113
|
*/
|
|
112
|
-
export function deriveRunActivity({ events }) {
|
|
113
|
-
const scan = { open: [], lastVisible: null, lastCardIndex: -1, retry: null, progress: null, visibleCount: 0, displayOnlySurfaces: new Set() };
|
|
114
|
+
export function deriveRunActivity({ events }, { isAnsweredCard } = {}) {
|
|
115
|
+
const scan = { open: [], lastVisible: null, lastCardIndex: -1, retry: null, progress: null, visibleCount: 0, displayOnlySurfaces: new Set(), isAnsweredCard };
|
|
114
116
|
(events ?? []).forEach((event, index) => scanEvent(scan, event, index));
|
|
115
117
|
const visibleCount = scan.visibleCount;
|
|
116
118
|
const lastVisibleIndex = scan.lastVisible?.index ?? -1;
|
|
@@ -161,8 +163,8 @@ export function deriveRunActivity({ events }) {
|
|
|
161
163
|
*
|
|
162
164
|
* @complexity O(n) in events.
|
|
163
165
|
*/
|
|
164
|
-
export function isAwaitingAnswer({ events }) {
|
|
165
|
-
return deriveRunActivity({ events }).activity.kind === 'awaiting-answer';
|
|
166
|
+
export function isAwaitingAnswer({ events }, options = {}) {
|
|
167
|
+
return deriveRunActivity({ events }, options).activity.kind === 'awaiting-answer';
|
|
166
168
|
}
|
|
167
169
|
/** `4` → `"4 s"`, `80` → `"1 m 20 s"`, `120` → `"2 m 0 s"`. */
|
|
168
170
|
export function formatActivityClock({ totalSeconds }) {
|
|
@@ -293,6 +293,15 @@ export interface RunFrameOutcome {
|
|
|
293
293
|
readonly error?: Error;
|
|
294
294
|
readonly terminal?: Extract<ChatRunStatus, "succeeded" | "failed" | "canceled">;
|
|
295
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Per-connection memory for {@link translateRunFrame}: which channels are mid-way through an echo
|
|
298
|
+
* line. Only a line's first chunk carries the {@link ECHO_LINE} prefix, so without it the rest of a
|
|
299
|
+
* split `user` echo stays as `raw` — tool-result prose that durable recovery then reads as executor
|
|
300
|
+
* diagnostics. Pass one fresh `{}` per daemon connection (each connection replays from event 0).
|
|
301
|
+
*/
|
|
302
|
+
export interface RunFrameCarry {
|
|
303
|
+
openEchoChannels?: Set<string>;
|
|
304
|
+
}
|
|
296
305
|
/**
|
|
297
306
|
* Translates one named daemon SSE frame (`event:` name plus raw `data:` text) into its effect on the
|
|
298
307
|
* turn. The browser's `subscribeToRun` dispatches the result to `RunHandlers`; the server finalizer
|
|
@@ -309,5 +318,7 @@ export declare function translateRunFrame({ kind, raw, notices }: {
|
|
|
309
318
|
kind: string;
|
|
310
319
|
raw: string | undefined;
|
|
311
320
|
notices: RunNotices;
|
|
312
|
-
},
|
|
321
|
+
}, { carry }?: {
|
|
322
|
+
carry?: RunFrameCarry;
|
|
323
|
+
}): RunFrameOutcome;
|
|
313
324
|
//# sourceMappingURL=entry.d.ts.map
|
|
@@ -32,6 +32,8 @@ export function parseUsageEvent({ payload }, _options = {}) {
|
|
|
32
32
|
...(typeof usage.input_tokens === "number" ? { inputTokens: usage.input_tokens } : {}),
|
|
33
33
|
...(typeof usage.output_tokens === "number" ? { outputTokens: usage.output_tokens } : {}),
|
|
34
34
|
...(typeof payload.costUsd === "number" ? { costUsd: payload.costUsd } : {}),
|
|
35
|
+
...(typeof payload.cumulativeCostUsd === "number" ? { cumulativeCostUsd: payload.cumulativeCostUsd } : {}),
|
|
36
|
+
...(typeof payload.costSessionId === "string" ? { costSessionId: payload.costSessionId } : {}),
|
|
35
37
|
...(typeof payload.durationMs === "number" ? { durationMs: payload.durationMs } : {}),
|
|
36
38
|
};
|
|
37
39
|
}
|
|
@@ -439,19 +441,30 @@ function heartbeatStatus(line) {
|
|
|
439
441
|
const ECHO_LINE = /^\{"type":"(?:stream_event|assistant|user)"/;
|
|
440
442
|
/**
|
|
441
443
|
* One CLI stdout/stderr chunk → events. Every JSON line that is a heartbeat also yields a typed
|
|
442
|
-
* `status` event; echo lines ({@link ECHO_LINE}) are dropped
|
|
443
|
-
*
|
|
444
|
+
* `status` event; echo lines ({@link ECHO_LINE}) are dropped — with a `carry`, including the later
|
|
445
|
+
* chunks of a split one; everything else stays in ONE `raw` event, verbatim. A chunk with no JSON
|
|
446
|
+
* lines at all (a plain-format CLI) is one `raw` event, as before.
|
|
444
447
|
*/
|
|
445
|
-
function chunkEvent(raw) {
|
|
448
|
+
function chunkEvent(raw, channel, carry) {
|
|
446
449
|
const frame = parseWire(raw);
|
|
447
450
|
if (!frame)
|
|
448
451
|
return [];
|
|
449
452
|
const chunk = wireString(frame.payload?.chunk);
|
|
453
|
+
const open = carry ? (carry.openEchoChannels ??= new Set()) : undefined;
|
|
450
454
|
const statuses = [];
|
|
451
455
|
let kept = "";
|
|
452
456
|
let droppedAny = false;
|
|
457
|
+
let first = true;
|
|
453
458
|
for (const piece of chunk.split(/(?<=\n)/)) {
|
|
454
|
-
|
|
459
|
+
// Only a chunk's first piece can continue the previous chunk's line; splitting on "\n" starts
|
|
460
|
+
// every later piece on a fresh line. Channels are tracked apart: stderr can interleave mid-line.
|
|
461
|
+
const echo = (first && open?.has(channel) === true) || ECHO_LINE.test(piece.trimStart());
|
|
462
|
+
first = false;
|
|
463
|
+
if (echo && !piece.endsWith("\n"))
|
|
464
|
+
open?.add(channel);
|
|
465
|
+
else
|
|
466
|
+
open?.delete(channel);
|
|
467
|
+
if (echo) {
|
|
455
468
|
droppedAny = true;
|
|
456
469
|
continue;
|
|
457
470
|
}
|
|
@@ -497,7 +510,7 @@ function endOutcome(raw, notices) {
|
|
|
497
510
|
// where the daemon's plain pre-spawn reason ("The assistant could not start: ...") and the
|
|
498
511
|
// host's start refusals reach the user.
|
|
499
512
|
/** Fold one named daemon frame into events, a failure and an optional terminal status. */
|
|
500
|
-
export function translateRunFrame({ kind, raw, notices },
|
|
513
|
+
export function translateRunFrame({ kind, raw, notices }, { carry } = {}) {
|
|
501
514
|
switch (kind) {
|
|
502
515
|
case "agent": {
|
|
503
516
|
const frame = parseWire(raw);
|
|
@@ -506,7 +519,7 @@ export function translateRunFrame({ kind, raw, notices }, _options = {}) {
|
|
|
506
519
|
}
|
|
507
520
|
case "stdout":
|
|
508
521
|
case "stderr":
|
|
509
|
-
return { events: chunkEvent(raw) };
|
|
522
|
+
return { events: chunkEvent(raw, kind, carry) };
|
|
510
523
|
case "error": {
|
|
511
524
|
const frame = parseWire(raw);
|
|
512
525
|
const message = wireString(frame?.payload?.message) || "agent run failed";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type RedactUserText } from './user-text-redaction.js';
|
|
2
|
+
/** The note keys a row shows instead of (or beside) a result. English text doubles as the i18n key, as everywhere else in this package. */
|
|
3
|
+
export type ToolCallDetailNote = 'Waiting for the result or answer…' | 'No result was recorded for this call.' | 'The run ended before this call returned a result.' | 'Finished with no output.' | 'Failed without an error message.';
|
|
4
|
+
export interface ToolCallDetail {
|
|
5
|
+
/** The call's input, pretty-printed and redacted; `null` when there is none or it cannot be serialized. */
|
|
6
|
+
readonly inputText: string | null;
|
|
7
|
+
/** The result's text, re-indented when JSON and redacted; `null` when there is no result text. */
|
|
8
|
+
readonly outputText: string | null;
|
|
9
|
+
/** Why there is no output, when there is none; `null` when the output says it all. */
|
|
10
|
+
readonly noteKey: ToolCallDetailNote | null;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolCallDetailOptions {
|
|
13
|
+
readonly runStreaming?: boolean;
|
|
14
|
+
readonly runSucceeded?: boolean;
|
|
15
|
+
/** Per-block display cap; a longer input or output is cut with an ellipsis. Defaults to 10 000. */
|
|
16
|
+
readonly maxChars?: number;
|
|
17
|
+
/** Defaults to the chat text policy, so a tool call never shows a key the rest of the chat would hide. */
|
|
18
|
+
readonly redact?: RedactUserText;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Builds the readable detail for one tool call.
|
|
22
|
+
*
|
|
23
|
+
* @param required.input - The `tool_use` event's input, any shape.
|
|
24
|
+
* @param required.result - The paired `tool_result`, when one arrived.
|
|
25
|
+
* @param options - Run state that explains a missing result, the display cap, and the redactor.
|
|
26
|
+
* @returns Display-ready input and output text plus a note when the output is absent.
|
|
27
|
+
* @complexity O(n) in the serialized input plus result length.
|
|
28
|
+
*/
|
|
29
|
+
export declare function describeToolCallDetail({ input, result }: {
|
|
30
|
+
readonly input: unknown;
|
|
31
|
+
readonly result?: {
|
|
32
|
+
readonly content: string;
|
|
33
|
+
readonly isError?: boolean;
|
|
34
|
+
} | undefined;
|
|
35
|
+
}, { runStreaming, runSucceeded, maxChars, redact }?: ToolCallDetailOptions): ToolCallDetail;
|
|
36
|
+
/** Per-block cap for the text a collapsed row keeps mounted — see {@link describeCollapsedToolCall}. */
|
|
37
|
+
export declare const COLLAPSED_DETAIL_MAX_CHARS = 400;
|
|
38
|
+
/**
|
|
39
|
+
* The short, single-node text a collapsed row keeps in the DOM in place of its full detail. A long
|
|
40
|
+
* chat mounts hundreds of collapsed rows (713 in a 38-message chat, 3.45 MB of hidden text at the
|
|
41
|
+
* full 10 000-char cap), so the full panel is mounted only while open; this keeps the gist —
|
|
42
|
+
* input, then output or the note explaining its absence — readable through the row's
|
|
43
|
+
* `textContent` for a `page.*` DOM reader that never clicks.
|
|
44
|
+
*
|
|
45
|
+
* @param required - Same as {@link describeToolCallDetail}.
|
|
46
|
+
* @param options - Same as {@link describeToolCallDetail}; `maxChars` defaults to {@link COLLAPSED_DETAIL_MAX_CHARS}.
|
|
47
|
+
* @returns Input (when it has a JSON form) and output or note, joined by a newline — never empty, since a missing output always carries a note.
|
|
48
|
+
* @complexity O(n) in the serialized input plus result length (redaction runs before the cap).
|
|
49
|
+
*/
|
|
50
|
+
export declare function describeCollapsedToolCall(required: Parameters<typeof describeToolCallDetail>[0], options?: ToolCallDetailOptions): string;
|
|
51
|
+
//# sourceMappingURL=tool-call-detail.d.ts.map
|