@keyou007/agent-bot 0.1.24-alpha.5 → 0.1.24-alpha.6
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 +26 -0
- package/README.md +27 -3
- package/README.zh.md +27 -3
- package/SECURITY.md +12 -0
- package/config.example.yaml +5 -0
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/serverStatus.js +1 -0
- package/dist/cli/serverStatus.js.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexRuntime.js +202 -115
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/codex/ProviderSwitch.js +12 -13
- package/dist/codex/ProviderSwitch.js.map +1 -1
- package/dist/config/AgentExecutionDefaultsStore.js +4 -0
- package/dist/config/AgentExecutionDefaultsStore.js.map +1 -1
- package/dist/config/loadConfig.js +4 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/schema.js +3 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/feishu/CardRenderer.js +66 -8
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +1 -0
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +121 -17
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/index.js +43 -6
- package/dist/index.js.map +1 -1
- package/dist/local-files/LocalFileViewerServer.js +51 -4
- package/dist/local-files/LocalFileViewerServer.js.map +1 -1
- package/dist/local-files/TurnPreviewPage.js +7 -2
- package/dist/local-files/TurnPreviewPage.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +41 -6
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +615 -214
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/proxy/SessionOperationQueue.js +14 -0
- package/dist/proxy/SessionOperationQueue.js.map +1 -0
- package/dist/runtime/AcpRuntimeAdapter.js +4 -2
- package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
- package/dist/state/StateStore.js +22 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/TurnPreviewJournal.js +19 -7
- package/dist/state/TurnPreviewJournal.js.map +1 -1
- package/dist/state/migrations.js +6 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +1 -0
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +19 -6
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/dist/terminal/TerminalCards.js +13 -0
- package/dist/terminal/TerminalCards.js.map +1 -0
- package/dist/terminal/TerminalLinks.js +76 -0
- package/dist/terminal/TerminalLinks.js.map +1 -0
- package/dist/terminal/TerminalPage.js +212 -0
- package/dist/terminal/TerminalPage.js.map +1 -0
- package/dist/terminal/TerminalProcess.js +71 -0
- package/dist/terminal/TerminalProcess.js.map +1 -0
- package/dist/terminal/TerminalServer.js +639 -0
- package/dist/terminal/TerminalServer.js.map +1 -0
- package/docs/technical-reference.md +48 -0
- package/docs/technical-reference.zh.md +48 -0
- package/npm-shrinkwrap.json +55 -5
- package/package.json +11 -4
- package/skills/agent-bot/SKILL.md +9 -2
- package/src/cli/controlProtocol.ts +2 -1
- package/src/cli/serverStatus.ts +1 -0
- package/src/cli.ts +6 -1
- package/src/codex/CodexRuntime.ts +200 -123
- package/src/codex/ProviderSwitch.ts +11 -12
- package/src/config/AgentExecutionDefaultsStore.ts +2 -0
- package/src/config/loadConfig.ts +4 -0
- package/src/config/schema.ts +5 -1
- package/src/feishu/CardRenderer.ts +81 -8
- package/src/feishu/FeishuConnector.ts +1 -0
- package/src/feishu/FeishuTurnPresenter.ts +108 -16
- package/src/feishu/types.ts +1 -0
- package/src/index.ts +44 -7
- package/src/local-files/LocalFileViewerServer.ts +48 -4
- package/src/local-files/TurnPreviewPage.ts +7 -2
- package/src/presentation/TurnStateReducer.ts +46 -7
- package/src/presentation/turnViewTypes.ts +5 -0
- package/src/proxy/ProxySessionController.ts +625 -211
- package/src/proxy/SessionOperationQueue.ts +13 -0
- package/src/runtime/AcpRuntimeAdapter.ts +5 -3
- package/src/runtime/types.ts +8 -3
- package/src/state/StateStore.ts +38 -1
- package/src/state/TurnPreviewJournal.ts +19 -7
- package/src/state/migrations.ts +6 -0
- package/src/supervision/SafeRestartNotifier.ts +1 -0
- package/src/supervision/SafeRestartScheduler.ts +18 -6
- package/src/terminal/TerminalCards.ts +18 -0
- package/src/terminal/TerminalLinks.ts +61 -0
- package/src/terminal/TerminalPage.ts +213 -0
- package/src/terminal/TerminalProcess.ts +71 -0
- package/src/terminal/TerminalServer.ts +513 -0
|
@@ -3,6 +3,7 @@ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import http, { type IncomingMessage, type ServerResponse } from "node:http";
|
|
6
|
+
import type { Duplex } from "node:stream";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import hljs from "highlight.js/lib/common";
|
|
8
9
|
import dos from "highlight.js/lib/languages/dos";
|
|
@@ -283,11 +284,18 @@ export interface LocalFileViewerAddress {
|
|
|
283
284
|
publicConnectionKind?: NetworkConnectionKind;
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
export interface LocalFileViewerService {
|
|
288
|
+
handleRequest(request: IncomingMessage, response: ServerResponse): void;
|
|
289
|
+
handleUpgrade(request: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
290
|
+
close(): Promise<void>;
|
|
291
|
+
}
|
|
292
|
+
|
|
286
293
|
export class LocalFileViewerServer {
|
|
287
294
|
private server?: http.Server;
|
|
288
295
|
private secret?: Buffer;
|
|
289
296
|
private address?: LocalFileViewerAddress;
|
|
290
297
|
private basePath = "";
|
|
298
|
+
private readonly services = new Map<string, LocalFileViewerService>();
|
|
291
299
|
|
|
292
300
|
constructor(private readonly options: LocalFileViewerServerOptions) {}
|
|
293
301
|
|
|
@@ -315,6 +323,11 @@ export class LocalFileViewerServer {
|
|
|
315
323
|
});
|
|
316
324
|
});
|
|
317
325
|
server.on("clientError", (_error, socket) => socket.end("HTTP/1.1 400 Bad Request\r\n\r\n"));
|
|
326
|
+
server.on("upgrade", (request, socket, head) => {
|
|
327
|
+
const service = this.serviceFor(request);
|
|
328
|
+
if (service) service.handleUpgrade(request, socket, head);
|
|
329
|
+
else socket.end("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n");
|
|
330
|
+
});
|
|
318
331
|
|
|
319
332
|
let selectedPort = preferredPort;
|
|
320
333
|
try {
|
|
@@ -361,12 +374,36 @@ export class LocalFileViewerServer {
|
|
|
361
374
|
}
|
|
362
375
|
|
|
363
376
|
async close(): Promise<void> {
|
|
377
|
+
await Promise.all([...this.services.values()].map((service) => service.close()));
|
|
378
|
+
this.services.clear();
|
|
364
379
|
const server = this.server;
|
|
365
380
|
this.server = undefined;
|
|
366
381
|
this.address = undefined;
|
|
367
382
|
if (server) await closeServer(server);
|
|
368
383
|
}
|
|
369
384
|
|
|
385
|
+
mountService(name: string, service: LocalFileViewerService): { publicBaseUrl: string; unmount(): void } {
|
|
386
|
+
if (!this.address || !this.server) throw new Error("Preview server is not running.");
|
|
387
|
+
if (!/^[a-z][a-z0-9-]*$/u.test(name) || ["preview", "turn-preview", "assets", "view", "raw"].includes(name)) {
|
|
388
|
+
throw new Error("Invalid Preview service route.");
|
|
389
|
+
}
|
|
390
|
+
const basePath = `${this.basePath}/${name}`;
|
|
391
|
+
if (this.services.has(basePath)) throw new Error("Preview service route is already mounted.");
|
|
392
|
+
this.services.set(basePath, service);
|
|
393
|
+
const url = new URL(this.address.publicBaseUrl);
|
|
394
|
+
url.pathname = basePath;
|
|
395
|
+
return { publicBaseUrl: url.toString(), unmount: () => {
|
|
396
|
+
if (this.services.get(basePath) === service) this.services.delete(basePath);
|
|
397
|
+
} };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private serviceFor(request: IncomingMessage): LocalFileViewerService | undefined {
|
|
401
|
+
try {
|
|
402
|
+
const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
|
|
403
|
+
return [...this.services].find(([base]) => pathname === base || pathname.startsWith(`${base}/`))?.[1];
|
|
404
|
+
} catch { return undefined; }
|
|
405
|
+
}
|
|
406
|
+
|
|
370
407
|
createFileUrl(filePath: string, reference?: string): string | undefined {
|
|
371
408
|
if (!this.address || !this.secret) return undefined;
|
|
372
409
|
const absolutePath = normalizeAbsolutePath(filePath);
|
|
@@ -397,6 +434,8 @@ export class LocalFileViewerServer {
|
|
|
397
434
|
}
|
|
398
435
|
|
|
399
436
|
private async handleRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {
|
|
437
|
+
const service = this.serviceFor(request);
|
|
438
|
+
if (service) { service.handleRequest(request, response); return; }
|
|
400
439
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
401
440
|
response.setHeader("Allow", "GET, HEAD");
|
|
402
441
|
this.sendHtml(response, 405, errorPage("不支持的请求", "该服务只接受只读请求。"), request.method === "HEAD");
|
|
@@ -433,14 +472,16 @@ export class LocalFileViewerServer {
|
|
|
433
472
|
return;
|
|
434
473
|
}
|
|
435
474
|
const journal = this.options.previewJournal;
|
|
436
|
-
const
|
|
475
|
+
const stored = journal?.has(turnId) ? undefined : this.options.getTurnSnapshot?.(turnId);
|
|
476
|
+
const journalId = isTurnPreviewState(stored) ? stored.previewTurnId ?? turnId : turnId;
|
|
477
|
+
const recorded = journal?.has(journalId);
|
|
437
478
|
if (recorded && requestUrl.searchParams.get("events") === "1") {
|
|
438
|
-
this.serveJournalEvents(request, response,
|
|
479
|
+
this.serveJournalEvents(request, response, journalId, journal!);
|
|
439
480
|
return;
|
|
440
481
|
}
|
|
441
482
|
const detailKey = requestUrl.searchParams.get("detail");
|
|
442
483
|
let snapshot = recorded
|
|
443
|
-
? journal!.load(
|
|
484
|
+
? journal!.load(journalId, detailKey ?? false)
|
|
444
485
|
: this.options.getTurnSnapshot?.(turnId);
|
|
445
486
|
if (!recorded && this.options.loadTurnSnapshot && request.method !== "HEAD"
|
|
446
487
|
&& requestUrl.searchParams.get("events") !== "1" && !requestUrl.searchParams.has("detail")) {
|
|
@@ -467,7 +508,7 @@ export class LocalFileViewerServer {
|
|
|
467
508
|
const after = requestUrl.searchParams.get("detailAfter");
|
|
468
509
|
const outputAppend = recorded && after && tool?.kind === "tool" && tool.tool.kind === "command" && !tool.tool.error
|
|
469
510
|
&& !/repl/i.test(tool.tool.title) && snapshot.previewCursor !== undefined
|
|
470
|
-
? journal!.outputAppend(
|
|
511
|
+
? journal!.outputAppend(journalId, tool.id, Number(after), snapshot.previewCursor) : undefined;
|
|
471
512
|
const detail = outputAppend !== undefined && tool?.kind === "tool"
|
|
472
513
|
&& (snapshot.fullToolOutputs?.[tool.id]?.length ?? 0) > outputAppend.length
|
|
473
514
|
? { key, revision: String(tool.tool.previewRevision), content: "", outputAppend,
|
|
@@ -965,6 +1006,9 @@ export class LocalFileViewerServer {
|
|
|
965
1006
|
if (renderHtml) {
|
|
966
1007
|
// A response-level sandbox also isolates this document when opened outside the viewer iframe.
|
|
967
1008
|
response.setHeader("Content-Security-Policy", "sandbox allow-scripts; default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:; media-src data: blob:; font-src data:; base-uri 'none'; form-action 'none'; frame-ancestors 'self'");
|
|
1009
|
+
} else {
|
|
1010
|
+
// Raw HTML/SVG/XML must not gain the shared origin's storage or execution privileges.
|
|
1011
|
+
response.setHeader("Content-Security-Policy", "sandbox; default-src 'none'; style-src 'unsafe-inline'; img-src data: blob:; media-src data: blob:; base-uri 'none'; form-action 'none'; frame-ancestors 'self'");
|
|
968
1012
|
}
|
|
969
1013
|
response.setHeader("Accept-Ranges", "bytes");
|
|
970
1014
|
response.setHeader("Cache-Control", "no-store");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { approvalActivityId } from "../presentation/TurnStateReducer.js";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { enableDiagramFences, DIAGRAM_PREVIEW_CLIENT_SCRIPT, DIAGRAM_PREVIEW_CSS } from "./DiagramPreview.js";
|
|
4
5
|
import MarkdownIt from "markdown-it";
|
|
@@ -693,7 +694,10 @@ function renderApprovalNotice(request: ApprovalRequest, renderText: (value: stri
|
|
|
693
694
|
: `<div class="markdown">${renderText(reason)}</div>`;
|
|
694
695
|
const details = commandApproval
|
|
695
696
|
? `<details class="approval-details" data-activity-id="approval:${escapeAttribute(request.id)}"><summary>${language === "zh" ? "查看完整命令" : "View full command"}</summary><pre data-scroll-id="approval:${escapeAttribute(request.id)}:command">${escapeHtml(request.command!)}</pre></details>` : "";
|
|
696
|
-
|
|
697
|
+
const instruction = request.automatic
|
|
698
|
+
? language === "zh" ? "自动执行模式:正在自动同意。" : "Auto mode: approving automatically."
|
|
699
|
+
: language === "zh" ? "请在飞书任务卡片中确认或拒绝。" : "Approve or reject using the task card in Feishu.";
|
|
700
|
+
return `<section class="notice" data-preview-key="approval"><h2>${escapeHtml(title)}</h2>${description}${details}<p>${instruction}</p></section>`;
|
|
697
701
|
}
|
|
698
702
|
|
|
699
703
|
function renderFinalAnswer(markdown: string, turnId: string, renderText: (value: string) => string, language: TurnPreviewLanguage): string {
|
|
@@ -726,7 +730,8 @@ function renderTimeline(
|
|
|
726
730
|
projectCwd?: string,
|
|
727
731
|
deferDetails = false,
|
|
728
732
|
): string {
|
|
729
|
-
const activities = state.
|
|
733
|
+
const activities = state.approval
|
|
734
|
+
? state.activities.filter((a) => a.id !== approvalActivityId(state.turnId, state.approval!.id)) : state.activities ?? [];
|
|
730
735
|
const positions = new Map(activities.map((activity, index) => [activity.id, index]));
|
|
731
736
|
const reasoning = new Map<number, TurnReasoningItem[]>();
|
|
732
737
|
for (const item of turnReasoningItems(state)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentEvent, RemoteTurnDetails, ToolState } from "../runtime/types.js";
|
|
1
|
+
import type { AgentEvent, ApprovalDecision, ApprovalRequest, RemoteTurnDetails, ToolState } from "../runtime/types.js";
|
|
2
2
|
import { appendGeneratedImageMarkdown } from "../utils/generatedImageMarkdown.js";
|
|
3
3
|
import type { MessageReplyTarget } from "../feishu/types.js";
|
|
4
4
|
import { formatStorageSize } from "../utils/formatStorageSize.js";
|
|
@@ -113,6 +113,13 @@ export function reduceTurnEvent(state: TurnViewState, event: AgentEvent, complet
|
|
|
113
113
|
if (event.type === "turn_completed") next.finalResponse = event.finalResponse;
|
|
114
114
|
if (event.type === "agent_text_delta") next.assistantText = state.assistantText + event.text;
|
|
115
115
|
if (event.type === "plan_updated") next.plan = event.steps;
|
|
116
|
+
if (event.type === "approval_requested" || event.type === "approval_resolved") {
|
|
117
|
+
const request = event.type === "approval_requested" ? event.request
|
|
118
|
+
: state.approval?.id === event.requestId ? state.approval : undefined;
|
|
119
|
+
if (request) next.activities = next.activities.map((a) => a.id === approvalActivityId(event.turnId, request.id) && a.kind === "assistant"
|
|
120
|
+
? { ...a, text: approvalHistoryText(request, event.type === "approval_resolved" ? event.decision : undefined,
|
|
121
|
+
event.type === "approval_resolved" && event.automatic) } : a);
|
|
122
|
+
}
|
|
116
123
|
if (event.type === "progress") {
|
|
117
124
|
const id = event.activityId ?? "progress";
|
|
118
125
|
const old = state.activities.find((a) => a.id === id);
|
|
@@ -305,7 +312,7 @@ function reduceEvent(state: TurnViewState, event: AgentEvent): TurnViewState {
|
|
|
305
312
|
return {
|
|
306
313
|
...state,
|
|
307
314
|
...tracking,
|
|
308
|
-
status: state.approval ? "waiting_for_approval" : "tool_running",
|
|
315
|
+
status: state.approval && !state.approval.automatic ? "waiting_for_approval" : "tool_running",
|
|
309
316
|
activeTool: bounded,
|
|
310
317
|
activities: activityUpdate.activities,
|
|
311
318
|
activitiesTruncated: state.activitiesTruncated || activityUpdate.truncated,
|
|
@@ -317,10 +324,21 @@ function reduceEvent(state: TurnViewState, event: AgentEvent): TurnViewState {
|
|
|
317
324
|
case "tool_output_delta":
|
|
318
325
|
return reduceToolOutputDelta(state, event.toolId, event.delta);
|
|
319
326
|
case "approval_requested":
|
|
320
|
-
return { ...state, status: "waiting_for_approval", approval: event.request
|
|
327
|
+
return { ...state, status: event.request.automatic ? state.activeTool ? "tool_running" : "running" : "waiting_for_approval", approval: event.request,
|
|
328
|
+
activities: [...state.activities.filter((a) => a.id !== approvalActivityId(event.turnId, event.request.id)), {
|
|
329
|
+
kind: "assistant", id: approvalActivityId(event.turnId, event.request.id), text: bound(approvalHistoryText(event.request)),
|
|
330
|
+
}],
|
|
331
|
+
};
|
|
321
332
|
case "approval_resolved":
|
|
322
333
|
if (state.approval?.id !== event.requestId) return state;
|
|
323
|
-
return {
|
|
334
|
+
return {
|
|
335
|
+
...state, status: state.activeTool ? "tool_running" : "running", approval: undefined,
|
|
336
|
+
activities: upsertActivity(state.activities,
|
|
337
|
+
state.activities.findIndex((a) => a.id === approvalActivityId(event.turnId, event.requestId)), {
|
|
338
|
+
kind: "assistant", id: approvalActivityId(event.turnId, event.requestId),
|
|
339
|
+
text: bound(approvalHistoryText(state.approval, event.decision, event.automatic)),
|
|
340
|
+
}).activities,
|
|
341
|
+
};
|
|
324
342
|
case "turn_completed": {
|
|
325
343
|
const durationMs = Math.max(0, event.durationMs ?? Date.now() - state.startedAt);
|
|
326
344
|
return {
|
|
@@ -363,6 +381,27 @@ function reduceEvent(state: TurnViewState, event: AgentEvent): TurnViewState {
|
|
|
363
381
|
}
|
|
364
382
|
}
|
|
365
383
|
|
|
384
|
+
export function approvalActivityId(turnId: string, requestId: string): string {
|
|
385
|
+
return "approval:" + turnId + ":" + requestId;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function continueTurnView(state: TurnViewState, turnId: string, startedAt: number): TurnViewState {
|
|
389
|
+
return { ...state, turnId, previewTurnId: state.previewTurnId ?? state.turnId,
|
|
390
|
+
continuedFromTurnId: state.turnId, continuedToTurnId: undefined, segmentStartedAt: startedAt,
|
|
391
|
+
status: "running", completedAt: undefined, durationMs: undefined, finalResponse: undefined,
|
|
392
|
+
error: undefined, approval: undefined, activeTool: undefined, assistantText: "", progressText: undefined };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function approvalHistoryText(request: ApprovalRequest, decision?: ApprovalDecision, automatic = false): string {
|
|
396
|
+
const labels = { accept: "允许一次", acceptForSession: "本会话允许", decline: "拒绝", cancel: "取消请求" };
|
|
397
|
+
const choice = decision ? request.options.find((option) => option.id === decision)?.label ?? labels[decision] : undefined;
|
|
398
|
+
const heading = choice ? automatic ? "自动同意(auto)" : "用户选择:" + choice
|
|
399
|
+
: request.automatic ? "正在自动同意(auto)" : "等待选择";
|
|
400
|
+
return ["**审批请求 · " + heading + "**", request.title, request.reason,
|
|
401
|
+
request.command ? "~~~\n" + request.command + "\n~~~" : undefined,
|
|
402
|
+
].filter(Boolean).join("\n\n");
|
|
403
|
+
}
|
|
404
|
+
|
|
366
405
|
export function appendSteerMessage(state: TurnViewState, id: string, text: string, localImagePaths?: string[]): TurnViewState {
|
|
367
406
|
const normalized = text.trim();
|
|
368
407
|
if (!normalized && !localImagePaths?.length) return state;
|
|
@@ -442,7 +481,7 @@ function reduceToolUpdate(state: TurnViewState, tool: ToolState, fullOutput?: st
|
|
|
442
481
|
...tracking,
|
|
443
482
|
fullToolOutputs,
|
|
444
483
|
fullToolErrors,
|
|
445
|
-
status: state.approval ? "waiting_for_approval" : "tool_running",
|
|
484
|
+
status: state.approval && !state.approval.automatic ? "waiting_for_approval" : "tool_running",
|
|
446
485
|
activeTool: bounded,
|
|
447
486
|
activities: activityUpdate.activities,
|
|
448
487
|
activitiesTruncated: state.activitiesTruncated || activityUpdate.truncated,
|
|
@@ -458,7 +497,7 @@ function reduceToolUpdate(state: TurnViewState, tool: ToolState, fullOutput?: st
|
|
|
458
497
|
...tracking,
|
|
459
498
|
fullToolOutputs,
|
|
460
499
|
fullToolErrors,
|
|
461
|
-
status: state.approval ? "waiting_for_approval" : activeTool ? "tool_running" : "running",
|
|
500
|
+
status: state.approval && !state.approval.automatic ? "waiting_for_approval" : activeTool ? "tool_running" : "running",
|
|
462
501
|
activeTool,
|
|
463
502
|
activities: activityUpdate.activities,
|
|
464
503
|
activitiesTruncated: state.activitiesTruncated || activityUpdate.truncated,
|
|
@@ -473,7 +512,7 @@ function reduceToolUpdate(state: TurnViewState, tool: ToolState, fullOutput?: st
|
|
|
473
512
|
...tracking,
|
|
474
513
|
fullToolOutputs,
|
|
475
514
|
fullToolErrors,
|
|
476
|
-
status: state.approval ? "waiting_for_approval" : activeTool ? "tool_running" : "running",
|
|
515
|
+
status: state.approval && !state.approval.automatic ? "waiting_for_approval" : activeTool ? "tool_running" : "running",
|
|
477
516
|
activeTool,
|
|
478
517
|
activities: activityUpdate.activities,
|
|
479
518
|
activitiesTruncated: state.activitiesTruncated || activityUpdate.truncated,
|
|
@@ -36,6 +36,11 @@ export interface TurnViewState {
|
|
|
36
36
|
previewJournal?: boolean;
|
|
37
37
|
/** Byte cursor for the on-demand preview, never needed for recovery. */
|
|
38
38
|
previewCursor?: number;
|
|
39
|
+
/** Shared presentation journal across a plan/implementation handoff. */
|
|
40
|
+
previewTurnId?: string;
|
|
41
|
+
continuedFromTurnId?: string;
|
|
42
|
+
continuedToTurnId?: string;
|
|
43
|
+
segmentStartedAt?: number;
|
|
39
44
|
sessionId: string;
|
|
40
45
|
turnId: string;
|
|
41
46
|
agentLabel?: string;
|