@bastani/atomic 0.8.1-1 → 0.8.2-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.
- package/CHANGELOG.md +13 -0
- package/dist/builtin/intercom/config.ts +3 -4
- package/dist/builtin/intercom/index.ts +6 -6
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/agent-dir.ts +11 -2
- package/dist/builtin/mcp/cli.js +12 -6
- package/dist/builtin/mcp/config.ts +31 -22
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/agents/agents.ts +63 -23
- package/dist/builtin/subagents/src/agents/skills.ts +21 -21
- package/dist/builtin/subagents/src/extension/index.ts +9 -8
- package/dist/builtin/subagents/src/runs/shared/run-history.ts +13 -10
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +3 -3
- package/dist/builtin/subagents/src/shared/artifacts.ts +18 -17
- package/dist/builtin/subagents/src/shared/types.ts +4 -4
- package/dist/builtin/web-access/config-paths.ts +11 -0
- package/dist/builtin/web-access/exa.ts +3 -2
- package/dist/builtin/web-access/gemini-api.ts +2 -1
- package/dist/builtin/web-access/gemini-search.ts +2 -1
- package/dist/builtin/web-access/gemini-web-config.ts +2 -1
- package/dist/builtin/web-access/github-extract.ts +2 -1
- package/dist/builtin/web-access/index.ts +11 -8
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/web-access/perplexity.ts +2 -1
- package/dist/builtin/web-access/video-extract.ts +2 -1
- package/dist/builtin/web-access/youtube-extract.ts +2 -1
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +4 -0
- package/dist/builtin/workflows/builtin/open-claude-design.ts +39 -22
- package/dist/builtin/workflows/builtin/ralph.ts +7 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/workflow/SKILL.md +28 -20
- package/dist/builtin/workflows/skills/workflow/references/design-checklist.md +8 -4
- package/dist/builtin/workflows/skills/workflow/references/running-workflows.md +52 -23
- package/dist/builtin/workflows/skills/workflow/references/sdk-authoring.md +41 -12
- package/dist/builtin/workflows/src/extension/config-loader.ts +13 -14
- package/dist/builtin/workflows/src/extension/discovery.ts +4 -6
- package/dist/builtin/workflows/src/extension/index.ts +675 -524
- package/dist/builtin/workflows/src/extension/runtime.ts +40 -16
- package/dist/builtin/workflows/src/extension/wiring.ts +3 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +43 -33
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +34 -10
- package/dist/builtin/workflows/src/shared/types.ts +1 -5
- package/dist/builtin/workflows/src/tui/graph-view.ts +245 -75
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +23 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +259 -149
- package/dist/builtin/workflows/src/tui/status-helpers.ts +3 -3
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +99 -10
- package/dist/builtin/workflows/src/tui/switcher.ts +4 -5
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +29 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -8
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +59 -4
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +3 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +31 -8
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +9 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +11 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/model-registry.d.ts +3 -2
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +25 -8
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts +3 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +97 -58
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader.d.ts +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +37 -36
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +5 -4
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +7 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +29 -8
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js +2 -2
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +2 -2
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/todos.d.ts.map +1 -1
- package/dist/core/tools/todos.js +3 -3
- package/dist/core/tools/todos.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +6 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/atomic-banner.d.ts +4 -0
- package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -0
- package/dist/modes/interactive/components/atomic-banner.js +34 -0
- package/dist/modes/interactive/components/atomic-banner.js.map +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +99 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.js +450 -0
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -0
- package/dist/modes/interactive/components/chat-transcript.d.ts +69 -0
- package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-transcript.js +183 -0
- package/dist/modes/interactive/components/chat-transcript.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +16 -4
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +110 -137
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +2 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +2 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +9 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +192 -137
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/catppuccin-mocha.json +5 -5
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +11 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +2 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +2 -2
- package/dist/utils/version-check.js.map +1 -1
- package/package.json +1 -1
|
@@ -37,16 +37,18 @@
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
import {
|
|
40
|
-
|
|
40
|
+
ChatTranscriptComponent,
|
|
41
41
|
CustomEditor,
|
|
42
|
-
|
|
42
|
+
ScrollableComponentViewport,
|
|
43
43
|
SessionManager,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
LiveChatEntriesController,
|
|
45
|
+
chatEntriesFromAgentMessages,
|
|
46
|
+
renderChatMessageEntry,
|
|
47
47
|
type AgentSession,
|
|
48
48
|
type AgentSessionEvent,
|
|
49
|
-
type
|
|
49
|
+
type ChatMessageEntry,
|
|
50
|
+
type ChatMessageRenderOptions,
|
|
51
|
+
type ChatTranscriptRole,
|
|
50
52
|
} from "@bastani/atomic";
|
|
51
53
|
import { Box, Container, Spacer, Text } from "@earendil-works/pi-tui";
|
|
52
54
|
import type { Component, EditorComponent, EditorTheme, TUI } from "@earendil-works/pi-tui";
|
|
@@ -84,6 +86,8 @@ export interface StageChatViewOpts {
|
|
|
84
86
|
piKeybindings?: unknown;
|
|
85
87
|
/** Currently installed host editor factory, inherited from extension `ctx.ui.setEditorComponent()`. */
|
|
86
88
|
piEditorFactory?: (tui: TUI, theme: EditorTheme, keybindings: unknown) => EditorComponent;
|
|
89
|
+
/** Parent chat rendering settings and extension renderers inherited from the host UI. */
|
|
90
|
+
getChatRenderSettings?: () => Partial<Omit<ChatMessageRenderOptions, "ui" | "cwd" | "markdownTheme">> | undefined;
|
|
87
91
|
/**
|
|
88
92
|
* Optional accessor returning the current terminal row count. The chat
|
|
89
93
|
* surface expands its body band to roughly `viewportRows` minus the fixed
|
|
@@ -100,7 +104,7 @@ export interface StageChatViewOpts {
|
|
|
100
104
|
* canonical user-visible string without knowing about the Pi-box payload.
|
|
101
105
|
*/
|
|
102
106
|
interface BaseEntry {
|
|
103
|
-
readonly role:
|
|
107
|
+
readonly role: ChatTranscriptRole;
|
|
104
108
|
readonly text: string;
|
|
105
109
|
}
|
|
106
110
|
interface UserEntry extends BaseEntry {
|
|
@@ -137,7 +141,8 @@ type TranscriptEntry =
|
|
|
137
141
|
| ThinkingEntry
|
|
138
142
|
| SystemEntry
|
|
139
143
|
| ToolEntry
|
|
140
|
-
| NoticeEntry
|
|
144
|
+
| NoticeEntry
|
|
145
|
+
| ChatMessageEntry;
|
|
141
146
|
type AgentSnapshotMessage = AgentSession["messages"][number];
|
|
142
147
|
|
|
143
148
|
// ---------------------------------------------------------------------------
|
|
@@ -155,10 +160,6 @@ const VIEW_LINE_COUNT = 32;
|
|
|
155
160
|
const HEADER_ROWS = 1;
|
|
156
161
|
/** Single dim rule between header and body. */
|
|
157
162
|
const SEP_ROWS = 1;
|
|
158
|
-
/** Loader: top rule + body + bottom rule when streaming. */
|
|
159
|
-
const LOADER_ROWS = 3;
|
|
160
|
-
/** Editor: top rule + ` ❯ … ` + bottom rule, always present. */
|
|
161
|
-
const EDITOR_ROWS = 3;
|
|
162
163
|
/** Footer: two dim lines. */
|
|
163
164
|
const FOOTER_ROWS = 2;
|
|
164
165
|
/** Hint strip: dashed rule + key bindings line. */
|
|
@@ -174,45 +175,6 @@ const FG_RESET = "\x1b[39m";
|
|
|
174
175
|
const WEIGHT_RESET = "\x1b[22m";
|
|
175
176
|
const ITALIC_RESET = "\x1b[23m";
|
|
176
177
|
|
|
177
|
-
// ---------------------------------------------------------------------------
|
|
178
|
-
// Pi chat transcript adapter
|
|
179
|
-
// ---------------------------------------------------------------------------
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Composes stage transcript rows with the same spacing rules as pi's
|
|
183
|
-
* InteractiveMode chat container. Workflow chrome (header, loader, footer,
|
|
184
|
-
* hints) remains owned by StageChatView; the base chat body is just the
|
|
185
|
-
* canonical coding-agent message components inside a pi-tui Container.
|
|
186
|
-
*/
|
|
187
|
-
class PiChatTranscriptComponent implements Component {
|
|
188
|
-
constructor(
|
|
189
|
-
private readonly entries: readonly TranscriptEntry[],
|
|
190
|
-
private readonly renderEntry: (entry: TranscriptEntry) => Component,
|
|
191
|
-
) {}
|
|
192
|
-
|
|
193
|
-
render(width: number): string[] {
|
|
194
|
-
const container = new Container();
|
|
195
|
-
for (const entry of this.entries) {
|
|
196
|
-
addTranscriptEntry(container, this.renderEntry(entry), entry.role);
|
|
197
|
-
}
|
|
198
|
-
return container.render(width);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
invalidate(): void {}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function addTranscriptEntry(container: Container, component: Component, role: TranscriptEntry["role"]): void {
|
|
205
|
-
// Mirror InteractiveMode.addMessageToChat:
|
|
206
|
-
// - user/custom/system-like rows get a spacer only when something already
|
|
207
|
-
// exists above them;
|
|
208
|
-
// - assistant rows own their leading whitespace internally;
|
|
209
|
-
// - tool rows attach directly below the assistant turn that requested them.
|
|
210
|
-
if ((role === "user" || role === "notice" || role === "system") && container.children.length > 0) {
|
|
211
|
-
container.addChild(new Spacer(1));
|
|
212
|
-
}
|
|
213
|
-
container.addChild(component);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
178
|
// ---------------------------------------------------------------------------
|
|
217
179
|
// StageChatView
|
|
218
180
|
// ---------------------------------------------------------------------------
|
|
@@ -229,6 +191,7 @@ export class StageChatView implements Component {
|
|
|
229
191
|
private requestRender: (() => void) | undefined;
|
|
230
192
|
private getViewportRows?: () => number | undefined;
|
|
231
193
|
private editor: EditorComponent | undefined;
|
|
194
|
+
private getChatRenderSettings?: () => Partial<Omit<ChatMessageRenderOptions, "ui" | "cwd" | "markdownTheme">> | undefined;
|
|
232
195
|
|
|
233
196
|
private inputBuffer = "";
|
|
234
197
|
private transcript: TranscriptEntry[] = [];
|
|
@@ -250,6 +213,9 @@ export class StageChatView implements Component {
|
|
|
250
213
|
private optimisticUserSignatures = new Set<string>();
|
|
251
214
|
/** Chat-mode repaint driver for Pi-style loaders/spinners. */
|
|
252
215
|
private animationTimer: ReturnType<typeof setInterval> | undefined;
|
|
216
|
+
/** Scrollable fixed-height body viewport for attached chat history. */
|
|
217
|
+
private bodyViewport = new ScrollableComponentViewport();
|
|
218
|
+
private liveChat: LiveChatEntriesController;
|
|
253
219
|
|
|
254
220
|
private _unsubscribeStore: (() => void) | null = null;
|
|
255
221
|
private _unsubscribeHandle: (() => void) | null = null;
|
|
@@ -265,6 +231,8 @@ export class StageChatView implements Component {
|
|
|
265
231
|
this.onClose = opts.onClose;
|
|
266
232
|
this.requestRender = opts.requestRender;
|
|
267
233
|
this.getViewportRows = opts.getViewportRows;
|
|
234
|
+
this.getChatRenderSettings = opts.getChatRenderSettings;
|
|
235
|
+
this.liveChat = new LiveChatEntriesController(this.transcript);
|
|
268
236
|
this.editor = this._createEditor(opts.piTui, opts.piKeybindings, opts.piEditorFactory);
|
|
269
237
|
|
|
270
238
|
// Seed transcript from the live SDK session at attach time, plus any
|
|
@@ -345,10 +313,7 @@ export class StageChatView implements Component {
|
|
|
345
313
|
|
|
346
314
|
private _snapshotMessagesFromHandle(): void {
|
|
347
315
|
if (!this.handle) return;
|
|
348
|
-
|
|
349
|
-
const entry = transcriptEntryFromSnapshotMessage(message);
|
|
350
|
-
if (entry) this.transcript.push(entry);
|
|
351
|
-
}
|
|
316
|
+
this.liveChat.appendMessages(this.handle.messages);
|
|
352
317
|
}
|
|
353
318
|
|
|
354
319
|
private _snapshotMessagesFromSessionFile(stage: StageSnapshot | undefined): void {
|
|
@@ -356,30 +321,36 @@ export class StageChatView implements Component {
|
|
|
356
321
|
const sessionFile = this.handle?.sessionFile ?? stage?.sessionFile;
|
|
357
322
|
if (sessionFile === undefined) return;
|
|
358
323
|
|
|
359
|
-
let
|
|
324
|
+
let messages: readonly AgentSnapshotMessage[];
|
|
360
325
|
try {
|
|
361
|
-
|
|
326
|
+
messages = SessionManager.open(sessionFile).buildSessionContext().messages as readonly AgentSnapshotMessage[];
|
|
362
327
|
} catch {
|
|
363
328
|
return;
|
|
364
329
|
}
|
|
365
330
|
|
|
366
|
-
|
|
367
|
-
if (!isSessionMessageEntry(entry)) continue;
|
|
368
|
-
const transcriptEntry = transcriptEntryFromSnapshotMessage(entry.message as AgentSnapshotMessage);
|
|
369
|
-
if (transcriptEntry) this.transcript.push(transcriptEntry);
|
|
370
|
-
}
|
|
331
|
+
this.liveChat.appendMessages(messages);
|
|
371
332
|
}
|
|
372
333
|
|
|
373
334
|
private _appendEvent(event: AgentSessionEvent): boolean {
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
// when present; delta-only events remain supported for SDK/test shims.
|
|
335
|
+
// Shared live transcript ingestion covers assistant/user/custom messages
|
|
336
|
+
// and tool start/update/end rows. StageChatView keeps workflow-only status
|
|
337
|
+
// events (pause, compaction captions, animation state) locally.
|
|
378
338
|
const type = String((event as { type?: unknown }).type ?? "");
|
|
339
|
+
if (type === "message_start") {
|
|
340
|
+
const message = (event as { message?: unknown }).message;
|
|
341
|
+
if (isUserMessageLike(message)) {
|
|
342
|
+
const signature = userMessageSignature(extractMessageText(message.content));
|
|
343
|
+
if (this.optimisticUserSignatures.delete(signature)) return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (isSharedLiveChatEvent(type)) {
|
|
347
|
+
return this.liveChat.applyEvent(event);
|
|
348
|
+
}
|
|
379
349
|
switch (type) {
|
|
380
350
|
case "agent_start":
|
|
381
351
|
this.sdkBusy = true;
|
|
382
352
|
this.toolEntryIndexes.clear();
|
|
353
|
+
this.liveChat.clearPendingTools();
|
|
383
354
|
this.statusMessage = "";
|
|
384
355
|
return true;
|
|
385
356
|
|
|
@@ -387,6 +358,7 @@ export class StageChatView implements Component {
|
|
|
387
358
|
this.sdkBusy = false;
|
|
388
359
|
this.streamingAssistantIndex = undefined;
|
|
389
360
|
this.streamingThinkingIndex = undefined;
|
|
361
|
+
this.liveChat.clearPendingTools();
|
|
390
362
|
this.statusMessage = "";
|
|
391
363
|
return true;
|
|
392
364
|
|
|
@@ -538,7 +510,7 @@ export class StageChatView implements Component {
|
|
|
538
510
|
changed = this._updateAssistantFromMessage(message) || changed;
|
|
539
511
|
for (const [toolCallId, index] of this.toolEntryIndexes.entries()) {
|
|
540
512
|
const entry = this.transcript[index];
|
|
541
|
-
if (entry
|
|
513
|
+
if (isLocalToolEntry(entry) && entry.state === "pending") {
|
|
542
514
|
this.transcript[index] = { ...entry, text: entry.text };
|
|
543
515
|
}
|
|
544
516
|
this.toolEntryIndexes.set(toolCallId, index);
|
|
@@ -586,7 +558,7 @@ export class StageChatView implements Component {
|
|
|
586
558
|
return true;
|
|
587
559
|
}
|
|
588
560
|
const index = role === "assistant" ? this.streamingAssistantIndex : this.streamingThinkingIndex;
|
|
589
|
-
if (index !== undefined && this.transcript[index]
|
|
561
|
+
if (index !== undefined && isLocalTextRoleEntry(this.transcript[index], role)) {
|
|
590
562
|
if (this.transcript[index]?.text === text) return false;
|
|
591
563
|
this.transcript[index] = { role, text } as TranscriptEntry;
|
|
592
564
|
return true;
|
|
@@ -624,7 +596,7 @@ export class StageChatView implements Component {
|
|
|
624
596
|
text: string,
|
|
625
597
|
): void {
|
|
626
598
|
const last = this.transcript[this.transcript.length - 1];
|
|
627
|
-
if (last
|
|
599
|
+
if (isLocalTextRoleEntry(last, role)) {
|
|
628
600
|
this.transcript[this.transcript.length - 1] = { role, text } as TranscriptEntry;
|
|
629
601
|
} else {
|
|
630
602
|
this.transcript.push({ role, text } as TranscriptEntry);
|
|
@@ -636,7 +608,7 @@ export class StageChatView implements Component {
|
|
|
636
608
|
delta: string,
|
|
637
609
|
): void {
|
|
638
610
|
const index = role === "assistant" ? this.streamingAssistantIndex : this.streamingThinkingIndex;
|
|
639
|
-
if (index !== undefined && this.transcript[index]
|
|
611
|
+
if (index !== undefined && isLocalTextRoleEntry(this.transcript[index], role)) {
|
|
640
612
|
const current = this.transcript[index];
|
|
641
613
|
this.transcript[index] = { role, text: current.text + delta } as TranscriptEntry;
|
|
642
614
|
return;
|
|
@@ -651,7 +623,7 @@ export class StageChatView implements Component {
|
|
|
651
623
|
let changed = false;
|
|
652
624
|
for (const [toolCallId, index] of this.toolEntryIndexes.entries()) {
|
|
653
625
|
const entry = this.transcript[index];
|
|
654
|
-
if (entry
|
|
626
|
+
if (!isLocalToolEntry(entry) || entry.state !== "pending") continue;
|
|
655
627
|
changed = this._upsertToolEntry({
|
|
656
628
|
toolCallId,
|
|
657
629
|
name: entry.name,
|
|
@@ -673,7 +645,7 @@ export class StageChatView implements Component {
|
|
|
673
645
|
const mappedIndex = update.toolCallId ? this.toolEntryIndexes.get(update.toolCallId) : undefined;
|
|
674
646
|
const index = mappedIndex ?? findToolEntryIndex(this.transcript, update.toolCallId, update.name);
|
|
675
647
|
const existing = index !== undefined && index >= 0 ? this.transcript[index] : undefined;
|
|
676
|
-
const previous = existing
|
|
648
|
+
const previous = isLocalToolEntry(existing) ? existing : undefined;
|
|
677
649
|
const output = update.output || previous?.output;
|
|
678
650
|
const name = previous?.name ?? update.name;
|
|
679
651
|
const args = update.args ?? previous?.args;
|
|
@@ -731,7 +703,8 @@ export class StageChatView implements Component {
|
|
|
731
703
|
}
|
|
732
704
|
|
|
733
705
|
private _hasPendingToolEntries(): boolean {
|
|
734
|
-
return this.
|
|
706
|
+
return this.liveChat.pendingToolIds().length > 0 ||
|
|
707
|
+
this.transcript.some((entry) => isLocalToolEntry(entry) && entry.state === "pending");
|
|
735
708
|
}
|
|
736
709
|
|
|
737
710
|
private _syncAnimationTick(): void {
|
|
@@ -783,21 +756,23 @@ export class StageChatView implements Component {
|
|
|
783
756
|
blocked,
|
|
784
757
|
omitTopRule: loaderLines.length > 0,
|
|
785
758
|
});
|
|
786
|
-
const footerLines = this._renderFooter(w, stage, { paused, streaming, settled });
|
|
787
759
|
const hintsLines = this._renderHints(w, { paused, streaming, settled });
|
|
788
760
|
|
|
789
761
|
const fixed =
|
|
790
|
-
|
|
791
|
-
|
|
762
|
+
HEADER_ROWS +
|
|
763
|
+
SEP_ROWS +
|
|
792
764
|
loaderLines.length +
|
|
793
765
|
editorLines.length +
|
|
794
|
-
|
|
795
|
-
|
|
766
|
+
FOOTER_ROWS +
|
|
767
|
+
HINTS_ROWS;
|
|
796
768
|
const totalRows = this._viewLineCount();
|
|
797
769
|
const bodyBudget = Math.max(1, totalRows - fixed);
|
|
770
|
+
this.bodyViewport.setVisibleRows(bodyBudget);
|
|
771
|
+
if (blocked) this.bodyViewport.scrollToBottom();
|
|
798
772
|
const bodyLines = blocked
|
|
799
773
|
? this._renderBlockedBody(w, bodyBudget, stage)
|
|
800
774
|
: this._renderBody(w, bodyBudget, stage, { paused, streaming, settled });
|
|
775
|
+
const footerLines = this._renderFooter(w, stage, { paused, streaming, settled });
|
|
801
776
|
|
|
802
777
|
const lines = [
|
|
803
778
|
...headerLines,
|
|
@@ -953,7 +928,9 @@ export class StageChatView implements Component {
|
|
|
953
928
|
// transcript component so the attached stage chat uses the same message
|
|
954
929
|
// spacing and coding-agent message widgets as the main interactive chat.
|
|
955
930
|
if (this.transcript.length > 0) {
|
|
956
|
-
components.push(
|
|
931
|
+
components.push(
|
|
932
|
+
new ChatTranscriptComponent(this.transcript, (entry) => this._renderEntry(entry)),
|
|
933
|
+
);
|
|
957
934
|
}
|
|
958
935
|
|
|
959
936
|
// Stream a static status message (e.g. "pausing…") as a dim trailing row.
|
|
@@ -962,24 +939,8 @@ export class StageChatView implements Component {
|
|
|
962
939
|
components.push(new Text(paint(this.statusMessage, this.theme.dim), 2, 0));
|
|
963
940
|
}
|
|
964
941
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
// off-screen history is not rebuilt just to be sliced away.
|
|
968
|
-
return this._renderComponentTail(components, width, budget);
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
private _renderComponentTail(components: Component[], width: number, budget: number): string[] {
|
|
972
|
-
const chunks: string[][] = [];
|
|
973
|
-
let lineCount = 0;
|
|
974
|
-
for (let i = components.length - 1; i >= 0; i--) {
|
|
975
|
-
const lines = components[i]!.render(width);
|
|
976
|
-
chunks.push(lines);
|
|
977
|
-
lineCount += lines.length;
|
|
978
|
-
if (lineCount >= budget) break;
|
|
979
|
-
}
|
|
980
|
-
const flat: string[] = [];
|
|
981
|
-
for (let i = chunks.length - 1; i >= 0; i--) flat.push(...chunks[i]!);
|
|
982
|
-
return this._fitToBudget(flat, budget, width);
|
|
942
|
+
this.bodyViewport.setComponents(components);
|
|
943
|
+
return this.bodyViewport.render(width);
|
|
983
944
|
}
|
|
984
945
|
|
|
985
946
|
private _fitToBudget(lines: string[], budget: number, width: number): string[] {
|
|
@@ -1038,57 +999,68 @@ export class StageChatView implements Component {
|
|
|
1038
999
|
// -------------------------------------------------------------------------
|
|
1039
1000
|
|
|
1040
1001
|
private _renderEntry(entry: TranscriptEntry): Component {
|
|
1002
|
+
if (isChatMessageEntry(entry)) {
|
|
1003
|
+
return renderChatMessageEntry(entry, this._chatMessageRenderOptions());
|
|
1004
|
+
}
|
|
1041
1005
|
switch (entry.role) {
|
|
1042
1006
|
case "user":
|
|
1043
|
-
return
|
|
1007
|
+
return renderChatMessageEntry(
|
|
1008
|
+
{ role: "user", kind: "user", text: entry.text },
|
|
1009
|
+
this._chatMessageRenderOptions(),
|
|
1010
|
+
);
|
|
1044
1011
|
case "assistant":
|
|
1045
|
-
return
|
|
1012
|
+
return renderChatMessageEntry(
|
|
1013
|
+
{ role: "assistant", kind: "assistant", message: assistantMessageForText(entry.text) },
|
|
1014
|
+
this._chatMessageRenderOptions(),
|
|
1015
|
+
);
|
|
1046
1016
|
case "thinking":
|
|
1047
|
-
return
|
|
1017
|
+
return renderChatMessageEntry(
|
|
1018
|
+
{ role: "assistant", kind: "assistant", message: assistantMessageForThinking(entry.text) },
|
|
1019
|
+
this._chatMessageRenderOptions(),
|
|
1020
|
+
);
|
|
1048
1021
|
case "tool":
|
|
1049
|
-
return this.
|
|
1022
|
+
return renderChatMessageEntry(this._toolEntryToChatMessage(entry), this._chatMessageRenderOptions());
|
|
1050
1023
|
case "notice":
|
|
1051
1024
|
return this._noticeRow(entry);
|
|
1052
1025
|
case "system":
|
|
1053
|
-
return
|
|
1026
|
+
return renderChatMessageEntry(
|
|
1027
|
+
{ role: "system", kind: "system", text: entry.text },
|
|
1028
|
+
this._chatMessageRenderOptions(),
|
|
1029
|
+
);
|
|
1054
1030
|
}
|
|
1055
1031
|
}
|
|
1056
1032
|
|
|
1057
|
-
private
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1033
|
+
private _toolEntryToChatMessage(entry: ToolEntry): ChatMessageEntry {
|
|
1034
|
+
const toolCallId = entry.toolCallId ?? `workflow-${entry.name}`;
|
|
1035
|
+
return {
|
|
1036
|
+
role: "tool",
|
|
1037
|
+
kind: "tool",
|
|
1038
|
+
toolName: entry.name,
|
|
1039
|
+
toolCallId,
|
|
1040
|
+
args: toolArgsForRender(entry),
|
|
1041
|
+
isPartial: entry.state === "pending",
|
|
1042
|
+
result:
|
|
1043
|
+
entry.state !== "pending" || entry.output
|
|
1044
|
+
? {
|
|
1045
|
+
role: "toolResult",
|
|
1046
|
+
toolCallId,
|
|
1047
|
+
toolName: entry.name,
|
|
1048
|
+
content: entry.output ? [{ type: "text", text: entry.output }] : [],
|
|
1049
|
+
isError: entry.state === "error",
|
|
1050
|
+
timestamp: Date.now(),
|
|
1051
|
+
}
|
|
1052
|
+
: undefined,
|
|
1053
|
+
};
|
|
1067
1054
|
}
|
|
1068
1055
|
|
|
1069
|
-
private
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
process.cwd(),
|
|
1078
|
-
);
|
|
1079
|
-
if (entry.state !== "pending" || entry.output) {
|
|
1080
|
-
component.updateResult(
|
|
1081
|
-
{
|
|
1082
|
-
content: entry.output
|
|
1083
|
-
? [{ type: "text", text: entry.output }]
|
|
1084
|
-
: [],
|
|
1085
|
-
isError: entry.state === "error",
|
|
1086
|
-
details: {},
|
|
1087
|
-
},
|
|
1088
|
-
entry.state === "pending",
|
|
1089
|
-
);
|
|
1090
|
-
}
|
|
1091
|
-
return component;
|
|
1056
|
+
private _chatMessageRenderOptions(): ChatMessageRenderOptions {
|
|
1057
|
+
const inherited = this.getChatRenderSettings?.();
|
|
1058
|
+
return {
|
|
1059
|
+
...inherited,
|
|
1060
|
+
ui: this._toolTui(),
|
|
1061
|
+
cwd: process.cwd(),
|
|
1062
|
+
showImages: inherited?.showImages ?? true,
|
|
1063
|
+
};
|
|
1092
1064
|
}
|
|
1093
1065
|
|
|
1094
1066
|
private _toolTui(): TUI {
|
|
@@ -1249,9 +1221,15 @@ export class StageChatView implements Component {
|
|
|
1249
1221
|
const top = layoutRow(width, " ", " " + lTop, rTop + " ", t);
|
|
1250
1222
|
|
|
1251
1223
|
// Bottom line — left: messages / duration; right: caption
|
|
1224
|
+
const history = this.bodyViewport.getMaxScroll() > 0
|
|
1225
|
+
? this.bodyViewport.getScrollFromBottom() > 0
|
|
1226
|
+
? ` · history ↑${this.bodyViewport.getScrollFromBottom()}`
|
|
1227
|
+
: " · history bottom"
|
|
1228
|
+
: "";
|
|
1252
1229
|
const lBot =
|
|
1253
1230
|
paint(`◇ ${messages} messages`, t.dim) +
|
|
1254
|
-
(dur ? " " + paint(`· ${dur}`, t.dim) : "")
|
|
1231
|
+
(dur ? " " + paint(`· ${dur}`, t.dim) : "") +
|
|
1232
|
+
paint(history, t.dim);
|
|
1255
1233
|
const rBot = flags.streaming
|
|
1256
1234
|
? paint("streaming · live", t.accent)
|
|
1257
1235
|
: flags.paused
|
|
@@ -1303,8 +1281,10 @@ export class StageChatView implements Component {
|
|
|
1303
1281
|
streaming: boolean;
|
|
1304
1282
|
settled: boolean;
|
|
1305
1283
|
}): Array<{ key: string; label: string; emphasis?: boolean }> {
|
|
1284
|
+
const historyHint = { key: "PgUp/PgDn", label: "history" };
|
|
1306
1285
|
if (flags.settled) {
|
|
1307
1286
|
return [
|
|
1287
|
+
historyHint,
|
|
1308
1288
|
{ key: "Ctrl+D", label: "back to graph", emphasis: true },
|
|
1309
1289
|
{ key: "Esc", label: "close" },
|
|
1310
1290
|
];
|
|
@@ -1313,6 +1293,7 @@ export class StageChatView implements Component {
|
|
|
1313
1293
|
return [
|
|
1314
1294
|
{ key: "↵", label: "resume with message", emphasis: true },
|
|
1315
1295
|
{ key: "Ctrl+P", label: "resume empty" },
|
|
1296
|
+
historyHint,
|
|
1316
1297
|
{ key: "Ctrl+D", label: "back" },
|
|
1317
1298
|
{ key: "Esc", label: "close" },
|
|
1318
1299
|
];
|
|
@@ -1322,6 +1303,7 @@ export class StageChatView implements Component {
|
|
|
1322
1303
|
{ key: "↵", label: "steer", emphasis: true },
|
|
1323
1304
|
{ key: "Ctrl+F", label: "follow-up", emphasis: true },
|
|
1324
1305
|
{ key: "Ctrl+P", label: "pause" },
|
|
1306
|
+
historyHint,
|
|
1325
1307
|
{ key: "Ctrl+D", label: "back" },
|
|
1326
1308
|
{ key: "Esc", label: "interrupt" },
|
|
1327
1309
|
];
|
|
@@ -1330,6 +1312,7 @@ export class StageChatView implements Component {
|
|
|
1330
1312
|
{ key: "↵", label: "send", emphasis: true },
|
|
1331
1313
|
{ key: "Ctrl+F", label: "follow-up" },
|
|
1332
1314
|
{ key: "Ctrl+P", label: "pause" },
|
|
1315
|
+
historyHint,
|
|
1333
1316
|
{ key: "Ctrl+D", label: "back" },
|
|
1334
1317
|
{ key: "Esc", label: "close" },
|
|
1335
1318
|
];
|
|
@@ -1351,11 +1334,18 @@ export class StageChatView implements Component {
|
|
|
1351
1334
|
return " ".repeat(width);
|
|
1352
1335
|
}
|
|
1353
1336
|
|
|
1337
|
+
wantsMouseScrollTracking(): boolean {
|
|
1338
|
+
return true;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1354
1341
|
// -------------------------------------------------------------------------
|
|
1355
1342
|
// Input
|
|
1356
1343
|
// -------------------------------------------------------------------------
|
|
1357
1344
|
|
|
1358
1345
|
handleInput(data: string): boolean {
|
|
1346
|
+
if (this.bodyViewport.handleInput(data)) {
|
|
1347
|
+
return true;
|
|
1348
|
+
}
|
|
1359
1349
|
if (data === "\x04") {
|
|
1360
1350
|
this.onDetach();
|
|
1361
1351
|
return true;
|
|
@@ -1438,7 +1428,8 @@ export class StageChatView implements Component {
|
|
|
1438
1428
|
this.requestRender?.();
|
|
1439
1429
|
return;
|
|
1440
1430
|
}
|
|
1441
|
-
this.
|
|
1431
|
+
this.liveChat.appendUserText(text);
|
|
1432
|
+
this.bodyViewport.scrollToBottom();
|
|
1442
1433
|
this.optimisticUserSignatures.add(userMessageSignature(text));
|
|
1443
1434
|
this.requestRender?.();
|
|
1444
1435
|
try {
|
|
@@ -1491,8 +1482,22 @@ export class StageChatView implements Component {
|
|
|
1491
1482
|
get _inputBuffer(): string {
|
|
1492
1483
|
return this.inputBuffer;
|
|
1493
1484
|
}
|
|
1494
|
-
get _transcript():
|
|
1495
|
-
|
|
1485
|
+
get _transcript(): ReadonlyArray<
|
|
1486
|
+
TranscriptEntry & {
|
|
1487
|
+
readonly text: string;
|
|
1488
|
+
readonly toolCallId: string;
|
|
1489
|
+
readonly state: string;
|
|
1490
|
+
readonly output: string;
|
|
1491
|
+
}
|
|
1492
|
+
> {
|
|
1493
|
+
return this.transcript.flatMap((entry) => transcriptDebugEntries(entry)) as ReadonlyArray<
|
|
1494
|
+
TranscriptEntry & {
|
|
1495
|
+
readonly text: string;
|
|
1496
|
+
readonly toolCallId: string;
|
|
1497
|
+
readonly state: string;
|
|
1498
|
+
readonly output: string;
|
|
1499
|
+
}
|
|
1500
|
+
>;
|
|
1496
1501
|
}
|
|
1497
1502
|
get _statusMessage(): string {
|
|
1498
1503
|
return this.statusMessage;
|
|
@@ -1503,15 +1508,112 @@ export class StageChatView implements Component {
|
|
|
1503
1508
|
get _hasAnimationTick(): boolean {
|
|
1504
1509
|
return this.animationTimer !== undefined;
|
|
1505
1510
|
}
|
|
1511
|
+
get _bodyScrollFromBottom(): number {
|
|
1512
|
+
return this.bodyViewport.getScrollFromBottom();
|
|
1513
|
+
}
|
|
1514
|
+
get _lastBodyMaxScroll(): number {
|
|
1515
|
+
return this.bodyViewport.getMaxScroll();
|
|
1516
|
+
}
|
|
1506
1517
|
}
|
|
1507
1518
|
|
|
1508
1519
|
// ---------------------------------------------------------------------------
|
|
1509
1520
|
// Module-private helpers
|
|
1510
1521
|
// ---------------------------------------------------------------------------
|
|
1511
1522
|
|
|
1512
|
-
type AssistantComponentMessage =
|
|
1513
|
-
|
|
1514
|
-
|
|
1523
|
+
type AssistantComponentMessage = Extract<ChatMessageEntry, { kind: "assistant" }>["message"];
|
|
1524
|
+
|
|
1525
|
+
function transcriptDebugEntries(entry: TranscriptEntry): Array<TranscriptEntry & {
|
|
1526
|
+
readonly text: string;
|
|
1527
|
+
readonly toolCallId: string;
|
|
1528
|
+
readonly state: string;
|
|
1529
|
+
readonly output: string;
|
|
1530
|
+
}> {
|
|
1531
|
+
if (isChatMessageEntry(entry) && entry.kind === "assistant") {
|
|
1532
|
+
const entries: Array<TranscriptEntry & { text: string; toolCallId: string; state: string; output: string }> = [];
|
|
1533
|
+
const thinking = extractThinkingText(entry.message.content);
|
|
1534
|
+
const text = extractMessageText(entry.message.content);
|
|
1535
|
+
if (thinking) entries.push({ role: "thinking", text: thinking, toolCallId: "", state: "", output: "" } as TranscriptEntry & { text: string; toolCallId: string; state: string; output: string });
|
|
1536
|
+
if (text || entries.length === 0) entries.push({ ...entry, text, toolCallId: "", state: "", output: "" });
|
|
1537
|
+
return entries;
|
|
1538
|
+
}
|
|
1539
|
+
return [{
|
|
1540
|
+
...entry,
|
|
1541
|
+
text: transcriptDebugText(entry),
|
|
1542
|
+
toolCallId: transcriptDebugToolCallId(entry),
|
|
1543
|
+
state: transcriptDebugToolState(entry),
|
|
1544
|
+
output: transcriptDebugToolOutput(entry),
|
|
1545
|
+
} as TranscriptEntry & { text: string; toolCallId: string; state: string; output: string }];
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
function transcriptDebugText(entry: TranscriptEntry): string {
|
|
1549
|
+
if ("text" in entry && typeof entry.text === "string") return entry.text;
|
|
1550
|
+
if (isChatMessageEntry(entry)) {
|
|
1551
|
+
switch (entry.kind) {
|
|
1552
|
+
case "assistant":
|
|
1553
|
+
return extractMessageText(entry.message.content);
|
|
1554
|
+
case "tool":
|
|
1555
|
+
return entry.result
|
|
1556
|
+
? extractToolResultText(entry.result)
|
|
1557
|
+
: `${entry.toolName} ${typeof entry.args === "string" ? entry.args : JSON.stringify(entry.args ?? {})}`;
|
|
1558
|
+
case "bashExecution":
|
|
1559
|
+
return entry.message.output || entry.message.command;
|
|
1560
|
+
case "user":
|
|
1561
|
+
case "system":
|
|
1562
|
+
return entry.text;
|
|
1563
|
+
case "custom":
|
|
1564
|
+
return extractMessageText(entry.message.content);
|
|
1565
|
+
case "branchSummary":
|
|
1566
|
+
case "compactionSummary":
|
|
1567
|
+
return entry.message.summary;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return "";
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
function transcriptDebugToolCallId(entry: TranscriptEntry): string {
|
|
1574
|
+
if (isChatMessageEntry(entry) && entry.kind === "tool") return entry.toolCallId;
|
|
1575
|
+
if ("toolCallId" in entry && typeof entry.toolCallId === "string") return entry.toolCallId;
|
|
1576
|
+
return "";
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
function transcriptDebugToolState(entry: TranscriptEntry): string {
|
|
1580
|
+
if (isChatMessageEntry(entry) && entry.kind === "tool") {
|
|
1581
|
+
if (entry.result?.isError) return "error";
|
|
1582
|
+
return entry.isPartial === false ? "success" : "pending";
|
|
1583
|
+
}
|
|
1584
|
+
if ("state" in entry && typeof entry.state === "string") return entry.state;
|
|
1585
|
+
return "";
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
function transcriptDebugToolOutput(entry: TranscriptEntry): string {
|
|
1589
|
+
if (isChatMessageEntry(entry) && entry.kind === "tool") return entry.result ? extractToolResultText(entry.result) : "";
|
|
1590
|
+
if ("output" in entry && typeof entry.output === "string") return entry.output;
|
|
1591
|
+
return "";
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
function isSharedLiveChatEvent(type: string): boolean {
|
|
1595
|
+
return type === "message_start" ||
|
|
1596
|
+
type === "message_update" ||
|
|
1597
|
+
type === "message_end" ||
|
|
1598
|
+
type === "tool_execution_start" ||
|
|
1599
|
+
type === "tool_execution_update" ||
|
|
1600
|
+
type === "tool_execution_end";
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
function isChatMessageEntry(entry: TranscriptEntry): entry is ChatMessageEntry {
|
|
1604
|
+
return "kind" in entry && entry.role !== "notice";
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
function isLocalToolEntry(entry: TranscriptEntry | undefined): entry is ToolEntry {
|
|
1608
|
+
return entry?.role === "tool" && !("kind" in entry);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
function isLocalTextRoleEntry<T extends "user" | "assistant" | "thinking" | "system">(
|
|
1612
|
+
entry: TranscriptEntry | undefined,
|
|
1613
|
+
role: T,
|
|
1614
|
+
): entry is Extract<TranscriptEntry, { role: T; text: string }> {
|
|
1615
|
+
return entry?.role === role && !("kind" in entry) && "text" in entry;
|
|
1616
|
+
}
|
|
1515
1617
|
|
|
1516
1618
|
function assistantMessageForText(text: string): AssistantComponentMessage {
|
|
1517
1619
|
return {
|
|
@@ -1541,6 +1643,10 @@ function isMessageLike(message: unknown): message is { role?: unknown; content?:
|
|
|
1541
1643
|
return message !== null && typeof message === "object" && "role" in message;
|
|
1542
1644
|
}
|
|
1543
1645
|
|
|
1646
|
+
function isUserMessageLike(message: unknown): message is { role: "user"; content?: unknown } {
|
|
1647
|
+
return isMessageLike(message) && message.role === "user";
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1544
1650
|
function userMessageSignature(text: string): string {
|
|
1545
1651
|
return text.trim();
|
|
1546
1652
|
}
|
|
@@ -1677,11 +1783,15 @@ function transcriptEntryFromSnapshotMessage(
|
|
|
1677
1783
|
}
|
|
1678
1784
|
}
|
|
1679
1785
|
|
|
1680
|
-
function
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
"
|
|
1786
|
+
function extractThinkingText(content: unknown): string {
|
|
1787
|
+
if (!Array.isArray(content)) return "";
|
|
1788
|
+
const parts: string[] = [];
|
|
1789
|
+
for (const item of content) {
|
|
1790
|
+
if (item == null || typeof item !== "object") continue;
|
|
1791
|
+
const thinking = (item as { type?: unknown; thinking?: unknown }).thinking;
|
|
1792
|
+
if ((item as { type?: unknown }).type === "thinking" && typeof thinking === "string") parts.push(thinking);
|
|
1793
|
+
}
|
|
1794
|
+
return parts.join("\n\n");
|
|
1685
1795
|
}
|
|
1686
1796
|
|
|
1687
1797
|
function extractMessageText(content: unknown): string {
|
|
@@ -1716,12 +1826,12 @@ function findToolEntryIndex(
|
|
|
1716
1826
|
if (toolCallId !== undefined) {
|
|
1717
1827
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
1718
1828
|
const entry = entries[i];
|
|
1719
|
-
if (entry
|
|
1829
|
+
if (isLocalToolEntry(entry) && entry.toolCallId === toolCallId) return i;
|
|
1720
1830
|
}
|
|
1721
1831
|
}
|
|
1722
1832
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
1723
1833
|
const entry = entries[i];
|
|
1724
|
-
if (entry
|
|
1834
|
+
if (isLocalToolEntry(entry) && entry.name === name && entry.state === "pending") return i;
|
|
1725
1835
|
}
|
|
1726
1836
|
return -1;
|
|
1727
1837
|
}
|