@zhushanwen/pi-subagent-workflow 7.4.0 → 8.1.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/README.md +15 -0
- package/package.json +7 -4
- package/scripts/rfl.mjs +308 -0
- package/skills/workflow-script-format/SKILL.md +1 -1
- package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +1 -0
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +10 -4
- package/src/execution/__tests__/before-agent-start-injection.test.ts +132 -0
- package/src/execution/__tests__/bg-notify-render.test.ts +15 -15
- package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +365 -0
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +190 -0
- package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +215 -0
- package/src/execution/__tests__/conversation-wiring.test.ts +198 -0
- package/src/execution/__tests__/crash-recovery.test.ts +8 -2
- package/src/execution/__tests__/delivery-methods.test.ts +385 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +241 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +49 -2
- package/src/execution/__tests__/execute-nesting.test.ts +20 -72
- package/src/execution/__tests__/execution-record.test.ts +199 -0
- package/src/execution/__tests__/finalize-record.test.ts +197 -15
- package/src/execution/__tests__/format.test.ts +131 -7
- package/src/execution/__tests__/gc-timer.test.ts +184 -0
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +254 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +37 -10
- package/src/execution/__tests__/index-session-start-identity.test.ts +371 -0
- package/src/execution/__tests__/index-session-start.test.ts +257 -5
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +211 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +337 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +116 -0
- package/src/execution/__tests__/list-component.test.ts +59 -5
- package/src/execution/__tests__/list-fields.test.ts +109 -0
- package/src/execution/__tests__/model-resolver.test.ts +38 -1
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +287 -0
- package/src/execution/__tests__/nested-visibility.test.ts +325 -0
- package/src/execution/__tests__/notifier-flush.test.ts +209 -7
- package/src/execution/__tests__/one-shot-upgrade.test.ts +205 -0
- package/src/execution/__tests__/parent-child-matrix.test.ts +336 -0
- package/src/execution/__tests__/record-store.test.ts +442 -54
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +11 -12
- package/src/execution/__tests__/recursive-visibility-env.test.ts +18 -20
- package/src/execution/__tests__/resource-policy.test.ts +109 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +267 -0
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +253 -0
- package/src/execution/__tests__/run-spawn-edges.test.ts +18 -25
- package/src/execution/__tests__/run-spawn-integration.test.ts +29 -25
- package/src/execution/__tests__/run-spawn-resume.test.ts +322 -0
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +14 -11
- package/src/execution/__tests__/session-pending.test.ts +61 -2
- package/src/execution/__tests__/session-reconstructor.test.ts +4 -4
- package/src/execution/__tests__/session-runner-epipe.test.ts +178 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +15 -21
- package/src/execution/__tests__/session-start-reaper.test.ts +10 -8
- package/src/execution/__tests__/spawn-args.test.ts +127 -49
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +1 -0
- package/src/execution/__tests__/spawned-children.test.ts +92 -0
- package/src/execution/__tests__/status-refactor.test.ts +345 -0
- package/src/execution/__tests__/stdin-writer.test.ts +97 -0
- package/src/execution/__tests__/subagent-service-message-close.test.ts +629 -0
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +180 -0
- package/src/execution/__tests__/subagent-service.test.ts +49 -11
- package/src/execution/__tests__/timeout-integration.test.ts +27 -13
- package/src/execution/__tests__/tool-action.test.ts +12 -10
- package/src/execution/__tests__/truncline-snapshot.test.ts +81 -0
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +194 -0
- package/src/execution/__tests__/worktree-manager.test.ts +300 -90
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +17 -13
- package/src/execution/__tests__/worktree-reconcile.integration.test.ts +181 -0
- package/src/execution/__tests__/worktree-registry.test.ts +72 -34
- package/src/execution/agent-result-mapper.ts +4 -1
- package/src/execution/argv-mirror.ts +21 -2
- package/src/execution/channel-registry-access.ts +3 -1
- package/src/execution/execution-record.ts +126 -9
- package/src/execution/finalize-record.ts +99 -13
- package/src/execution/idle-gc.ts +47 -0
- package/src/execution/lifecycle-manager.ts +491 -0
- package/src/execution/lifecycle-predicates.ts +65 -0
- package/src/execution/manifest-store.ts +61 -16
- package/src/execution/model-resolver.ts +26 -5
- package/src/execution/notifier.ts +69 -12
- package/src/execution/pi-invocation.ts +21 -1
- package/src/execution/record-entry.ts +118 -0
- package/src/execution/record-store.ts +844 -108
- package/src/execution/session-pending.ts +121 -49
- package/src/execution/session-reconstructor.ts +224 -7
- package/src/execution/session-runner.ts +713 -316
- package/src/execution/sessions-index.ts +304 -0
- package/src/execution/stdin-writer.ts +93 -7
- package/src/execution/stream-sink.ts +20 -3
- package/src/execution/subagent-service.ts +917 -138
- package/src/execution/temp-prompt.ts +8 -3
- package/src/execution/turn-limiter.ts +14 -0
- package/src/execution/types.ts +218 -19
- package/src/execution/worktree-manager.ts +449 -59
- package/src/execution/worktree-registry.ts +97 -29
- package/src/index.ts +318 -20
- package/src/injectors/subagent-list-injector.ts +26 -8
- package/src/injectors/workflow-list-injector.ts +25 -8
- package/src/interface/__tests__/subagent-tool-prompt.test.ts +17 -4
- package/src/interface/__tests__/tool-render.test.ts +10 -8
- package/src/interface/__tests__/tool-workflow-script-generate.test.ts +103 -26
- package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +179 -0
- package/src/interface/bg-notify-render.ts +32 -8
- package/src/interface/command-actions.ts +26 -7
- package/src/interface/commands.ts +21 -22
- package/src/interface/format.ts +53 -20
- package/src/interface/gui-mappers.ts +6 -8
- package/src/interface/helpers.ts +170 -10
- package/src/interface/list-component.ts +53 -14
- package/src/interface/subagent-actions.ts +235 -17
- package/src/interface/subagent-tool.ts +81 -16
- package/src/interface/subagents.ts +2 -1
- package/src/interface/tool-render.ts +21 -27
- package/src/interface/tool-workflow-script.ts +29 -33
- package/src/interface/tool-workflow.ts +67 -100
- package/src/interface/views/WorkflowsView.ts +89 -32
- package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +264 -0
- package/src/interface/views/detail-content.ts +1 -1
- package/src/interface/views/format.ts +3 -3
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +333 -0
- package/src/orchestration/__tests__/args-validator.test.ts +1 -1
- package/src/orchestration/__tests__/config-loader.test.ts +38 -0
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +394 -4
- package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +4 -4
- package/src/orchestration/__tests__/execute-agent-call.test.ts +144 -1
- package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +171 -0
- package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +820 -19
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -2
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +96 -0
- package/src/orchestration/__tests__/lifecycle.test.ts +332 -149
- package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +1108 -19
- package/src/orchestration/__tests__/skill-discovery.test.ts +130 -61
- package/src/orchestration/__tests__/test-mocks.ts +197 -0
- package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +164 -0
- package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +110 -0
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -2
- package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +110 -0
- package/src/orchestration/__tests__/workflows-e2e.test.ts +38 -40
- package/src/orchestration/agent-opts-resolver.ts +4 -1
- package/src/orchestration/args-validator.ts +2 -2
- package/src/orchestration/config-loader.ts +30 -1
- package/src/orchestration/error-recovery.ts +137 -30
- package/src/orchestration/execute-agent-call.ts +44 -10
- package/src/orchestration/jsonl-run-store.ts +397 -71
- package/src/orchestration/launcher.ts +7 -1
- package/src/orchestration/lifecycle.ts +145 -133
- package/src/orchestration/models/__tests__/trace.test.ts +408 -0
- package/src/orchestration/models/budget.ts +1 -1
- package/src/orchestration/models/run-runtime.ts +15 -17
- package/src/orchestration/models/run-spec.ts +2 -2
- package/src/orchestration/models/run-state.ts +3 -3
- package/src/orchestration/models/trace.ts +95 -15
- package/src/orchestration/models/types.ts +8 -9
- package/src/orchestration/models/workflow-run.ts +50 -71
- package/src/orchestration/models/workflow-script.ts +32 -1
- package/src/orchestration/skill-discovery.ts +30 -0
- package/src/orchestration/worker-handle.ts +1 -1
- package/src/orchestration/worker-host.ts +1 -1
- package/src/orchestration/worker-script-builder.ts +38 -11
- package/src/shared/__tests__/agent-ref.test.ts +34 -0
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +280 -0
- package/src/shared/__tests__/resource-discovery.test.ts +79 -0
- package/src/shared/__tests__/schema-jsonify.test.ts +81 -0
- package/src/shared/agent-ref.ts +22 -1
- package/src/shared/resource-discovery.ts +162 -59
- package/src/shared/schema-jsonify.ts +56 -0
- package/workflows/review-fix-loop-utils.cjs +542 -32
- package/workflows/review-fix-loop.js +462 -109
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workflow Extension — workflow tool(
|
|
2
|
+
* Workflow Extension — workflow tool(3 actions,FR-5 tool 收口)。
|
|
3
3
|
*
|
|
4
4
|
* 合并原 tool-workflow.ts + tool-workflow-run.ts 为单 tool。
|
|
5
5
|
*
|
|
6
6
|
* Actions:
|
|
7
7
|
* - run: registry.get → runWorkflow(直接启动,无需用户确认)
|
|
8
8
|
* - status: 列出 runs(deps.runs)
|
|
9
|
-
* - pause: 调 pauseRun
|
|
10
|
-
* - resume: 调 resumeRun
|
|
11
9
|
* - abort: 调 abortRun
|
|
12
10
|
*
|
|
13
|
-
* **restart 不包含**(D-9
|
|
11
|
+
* **restart 不包含**(D-9 废弃);**pause/resume 不包含**(一次性生命周期——run
|
|
12
|
+
* 不可挂起,提前停止用 abort,要新结果开新 run)。
|
|
14
13
|
*
|
|
15
14
|
* 层归属:Interface。依赖 Pi SDK + Engine lifecycle/launcher + helpers。
|
|
16
15
|
*
|
|
@@ -35,8 +34,7 @@ import { type Static, Type } from "typebox";
|
|
|
35
34
|
import { SLUG_MAX_LENGTH } from "../execution/execute-options-mapper.ts";
|
|
36
35
|
import { THINKING_ORDER } from "../execution/model-resolver.ts";
|
|
37
36
|
import type { LauncherDeps } from "../orchestration/launcher.ts";
|
|
38
|
-
import {
|
|
39
|
-
import { abortRun, pauseRun, resumeRun, runWorkflow } from "../orchestration/lifecycle.ts";
|
|
37
|
+
import { abortRun, runWorkflow } from "../orchestration/lifecycle.ts";
|
|
40
38
|
import type { RunStore } from "../orchestration/models/ports.ts";
|
|
41
39
|
import type { WorkflowRun } from "../orchestration/models/workflow-run.ts";
|
|
42
40
|
import { mapRunIcon, mapRunStatus, toGuiCtx } from "./gui-mappers.ts";
|
|
@@ -54,15 +52,11 @@ import { formatElapsed, renderTextFallback } from "./views/format.ts";
|
|
|
54
52
|
export type WorkflowAction =
|
|
55
53
|
| "run"
|
|
56
54
|
| "status"
|
|
57
|
-
| "pause"
|
|
58
|
-
| "resume"
|
|
59
55
|
| "abort";
|
|
60
56
|
|
|
61
57
|
const WORKFLOW_ACTIONS: readonly WorkflowAction[] = [
|
|
62
58
|
"run",
|
|
63
59
|
"status",
|
|
64
|
-
"pause",
|
|
65
|
-
"resume",
|
|
66
60
|
"abort",
|
|
67
61
|
];
|
|
68
62
|
|
|
@@ -80,7 +74,7 @@ const WorkflowParams = Type.Object({
|
|
|
80
74
|
}),
|
|
81
75
|
),
|
|
82
76
|
runId: Type.Optional(
|
|
83
|
-
Type.String({ description: "Workflow run ID (
|
|
77
|
+
Type.String({ description: "Workflow run ID (abort action)" }),
|
|
84
78
|
),
|
|
85
79
|
args: Type.Optional(
|
|
86
80
|
Type.Record(Type.String(), Type.Unknown(), {
|
|
@@ -96,7 +90,7 @@ const WorkflowParams = Type.Object({
|
|
|
96
90
|
description: "Run-level model override in 'provider/modelId' format. When set, all agents spawned by this run inherit it by default (unless a per-call agent() opts.model is set). Omit to inherit the main agent's model.",
|
|
97
91
|
})),
|
|
98
92
|
thinkingLevel: Type.Optional(StringEnum(THINKING_ORDER, {
|
|
99
|
-
description: "Run-level thinkingLevel override (off/minimal/low/medium/high/xhigh/max). All agents in this run inherit it by default. Omit to
|
|
93
|
+
description: "Run-level thinkingLevel override (off/minimal/low/medium/high/xhigh/max). All agents in this run inherit it by default. Omit to default each agent to its model's highest available level.",
|
|
100
94
|
})),
|
|
101
95
|
});
|
|
102
96
|
|
|
@@ -229,7 +223,7 @@ interface RunSummary {
|
|
|
229
223
|
export type WorkflowToolDetails =
|
|
230
224
|
| { action: "run"; runId: string; status: "running" | "not_found" | "invalid_args"; name: string; slug?: string; stateFile?: string; __gui__?: GuiRenderResult }
|
|
231
225
|
| { action: "status"; runs: RunSummary[]; __gui__?: GuiRenderResult }
|
|
232
|
-
| { action: "
|
|
226
|
+
| { action: "abort"; runId: string; status: string; reason?: string; __gui__?: GuiRenderResult };
|
|
233
227
|
|
|
234
228
|
/** Result returned by the `workflow` tool's execute. */
|
|
235
229
|
export interface ToolResult {
|
|
@@ -255,8 +249,9 @@ function withGui(
|
|
|
255
249
|
/** 按 WorkflowToolDetails 构造对应的 GuiComponent。 */
|
|
256
250
|
export function buildWorkflowGui(details: WorkflowToolDetails) {
|
|
257
251
|
if (details.action === "run") {
|
|
258
|
-
// not_found
|
|
259
|
-
//
|
|
252
|
+
// not_found 曾是「isError:true + not_found details」的错误形态(W4 前返回值 isError 被
|
|
253
|
+
// pi 丢弃);W4 后该错误改为 throw(details 不再产出此形态),本分支保留消费历史
|
|
254
|
+
// session entry / 防御性渲染,不能走通用 mapper 的 done/check 成功映射。
|
|
260
255
|
if (details.status === "not_found") {
|
|
261
256
|
return guiComponent("stats-line", {
|
|
262
257
|
items: [{ label: "run", value: "not found", severity: "danger" as const }],
|
|
@@ -283,14 +278,12 @@ export function buildWorkflowGui(details: WorkflowToolDetails) {
|
|
|
283
278
|
}),
|
|
284
279
|
});
|
|
285
280
|
}
|
|
286
|
-
//
|
|
287
|
-
// abort 是破坏性终止、pause 是挂起(非成功完成),用 warn 区分;resume 保留 ok
|
|
288
|
-
const severity = details.action === "abort" || details.action === "pause" ? "warn" as const : "ok" as const;
|
|
281
|
+
// abort(唯一 lifecycle action):破坏性终止非成功完成,用 warn 与成功区分
|
|
289
282
|
return guiComponent("stats-line", {
|
|
290
283
|
items: [{
|
|
291
284
|
label: details.action,
|
|
292
285
|
value: details.runId.slice(0, RUNID_SHORT),
|
|
293
|
-
severity,
|
|
286
|
+
severity: "warn" as const,
|
|
294
287
|
}],
|
|
295
288
|
});
|
|
296
289
|
}
|
|
@@ -298,7 +291,8 @@ export function buildWorkflowGui(details: WorkflowToolDetails) {
|
|
|
298
291
|
// ── Tool registration ────────────────────────────────────────
|
|
299
292
|
|
|
300
293
|
/**
|
|
301
|
-
* 注册 workflow tool(
|
|
294
|
+
* 注册 workflow tool(3 actions: run / status / abort;pause/resume 已随一次性
|
|
295
|
+
* 生命周期移除——enum 拒绝由 pi 核心校验拦截,见 F3)。
|
|
302
296
|
*
|
|
303
297
|
* @param pi ExtensionAPI
|
|
304
298
|
* @param deps LauncherDeps(LifecycleDeps + registry)
|
|
@@ -318,9 +312,9 @@ export function registerWorkflowTool(
|
|
|
318
312
|
name: "workflow",
|
|
319
313
|
label: "Workflow",
|
|
320
314
|
description:
|
|
321
|
-
"Execute and control workflows: run (start), status,
|
|
315
|
+
"Execute and control workflows: run (start), status, abort.\n" +
|
|
322
316
|
"Replaces workflow + workflow-run tools.",
|
|
323
|
-
promptSnippet: "Run,
|
|
317
|
+
promptSnippet: "Run, abort, or check workflow status",
|
|
324
318
|
promptGuidelines: [
|
|
325
319
|
"PRIORITY: When user says 'workflow', 'run workflow', try run action FIRST.",
|
|
326
320
|
"All listed workflows run DIRECTLY with action:run — refs/descriptions come from " +
|
|
@@ -329,10 +323,11 @@ export function registerWorkflowTool(
|
|
|
329
323
|
"workflow-script generate for patterns already covered by available workflows.",
|
|
330
324
|
"run: pass the absolute .js path from <available_workflows> <location> as name, then start in background (no user confirmation needed).",
|
|
331
325
|
"Do NOT poll status after starting — results appear automatically via notifyDone.",
|
|
326
|
+
"Runs are one-shot: there is no pause/resume — to stop a run early use abort; for a fresh result start a new run.",
|
|
332
327
|
"Call shapes (JSON): " +
|
|
333
328
|
"- run: {\"action\":\"run\",\"name\":\"<script>\",\"args\":{...},\"tokens\":N,\"time\":N,\"model\":\"<provider/modelId>\",\"thinkingLevel\":\"<level>\"}. " +
|
|
334
329
|
"- status: {\"action\":\"status\"}. " +
|
|
335
|
-
"-
|
|
330
|
+
"- abort: {\"action\":\"abort\",\"runId\":\"<id>\"} (optional: {\"error\":\"<reason>\"}).",
|
|
336
331
|
"Budget: Do NOT set tokens/time unless the user explicitly requests a limit. Built-in workflows run unlimited by default.",
|
|
337
332
|
"Model/thinkingLevel: omit by default (inherit main agent's model). Only set model/thinkingLevel when the user explicitly requests a specific model or thinking depth for this run.",
|
|
338
333
|
"Anti-patterns: Flattening args sub-fields (task/items/...) to the top level — they belong inside args. Calling {\"action\":\"run\"} without name.",
|
|
@@ -351,11 +346,13 @@ export function registerWorkflowTool(
|
|
|
351
346
|
): Promise<ToolResult> {
|
|
352
347
|
// P1-2: Honor abort signal up-front
|
|
353
348
|
if (signal?.aborted) {
|
|
354
|
-
|
|
349
|
+
// throw(W4b):pi 只对 execute throw 置 isError:true,返回值里的 isError
|
|
350
|
+
// 被 agent-loop 丢弃(agent-loop.js:453-483)——文案原样进 toolResult。
|
|
351
|
+
throw new Error("Operation aborted before start");
|
|
355
352
|
}
|
|
356
|
-
// P1-6: Reentry guard
|
|
353
|
+
// P1-6: Reentry guard(acquire 失败时尚未持有 guard,throw 前无需 release)
|
|
357
354
|
if (!acquireReentryGuard(reentryRef)) {
|
|
358
|
-
|
|
355
|
+
throw new Error(REENTRY_BUSY_MESSAGE);
|
|
359
356
|
}
|
|
360
357
|
try {
|
|
361
358
|
let result: ToolResult;
|
|
@@ -369,19 +366,13 @@ export function registerWorkflowTool(
|
|
|
369
366
|
case "status":
|
|
370
367
|
result = actionStatus(deps);
|
|
371
368
|
break;
|
|
372
|
-
case "pause":
|
|
373
|
-
result = await actionLifecycle("pause", params, deps);
|
|
374
|
-
break;
|
|
375
|
-
case "resume":
|
|
376
|
-
result = await actionLifecycle("resume", params, deps);
|
|
377
|
-
break;
|
|
378
369
|
case "abort":
|
|
379
370
|
result = await actionLifecycle("abort", params, deps);
|
|
380
371
|
break;
|
|
381
372
|
default: {
|
|
382
373
|
// Exhaustiveness check — 新增 WorkflowAction 成员时未补 case,tsc 在此报错。
|
|
383
374
|
const _exhaustive: never = action;
|
|
384
|
-
|
|
375
|
+
throw new Error(`Unknown action: ${String(_exhaustive)}`);
|
|
385
376
|
}
|
|
386
377
|
}
|
|
387
378
|
// GUI 协议:RPC 模式下附加 __gui__ 到 details
|
|
@@ -428,30 +419,27 @@ export async function actionRun(
|
|
|
428
419
|
): Promise<ToolResult> {
|
|
429
420
|
const name = params.name;
|
|
430
421
|
if (!name) {
|
|
431
|
-
|
|
422
|
+
throw new Error("run requires 'name' parameter (absolute .js path from <available_workflows> <location>). Correct: {\"action\":\"run\",\"name\":\"<ref>\",\"args\":{...}}");
|
|
432
423
|
}
|
|
433
424
|
// 弱模型常见误用(P0 静默失败):把 task/items 等 args 子字段平铺到 workflow params
|
|
434
425
|
// 顶层(缺 args 嵌套)。args ?? {} 会静默 args={},启动缺参 run 不报错——比 subagent
|
|
435
426
|
// 平铺事故更严重。m6:先 registry.getPath(动态参数集来源——schema 即 SSOT),
|
|
436
427
|
// not_found 优先返回;平铺检测报错带 Correct 正例纠正。
|
|
437
428
|
const script = await deps.registry.getPath(name);
|
|
438
|
-
|
|
439
|
-
|
|
429
|
+
// W4c:config-loader 的 toCachedMeta 对不可读/不存在文件返回 available:false 的
|
|
430
|
+
// stub(非 undefined),仅判 !script 会绕过 not_found → 空 sourceCode 假启动
|
|
431
|
+
//(W4b verifier 探针实测复现)。与下方 suggestions 分支的 wf.available 过滤口径对齐。
|
|
432
|
+
if (!script || !script.available) {
|
|
433
|
+
// 模糊匹配建议。throw(W4):pi 只对 execute throw 置 isError:true,
|
|
434
|
+
// 返回值里的 isError 被 agent-loop 丢弃(agent-loop.js:453-483)——文案原样进 toolResult。
|
|
440
435
|
const all = await deps.registry.loadAll();
|
|
441
436
|
const available = all.filter((wf) => wf.available);
|
|
442
437
|
const suggestions = available
|
|
443
438
|
.map((wf) => ` - ${wf.name}: ${wf.meta.description || "(no description)"}`)
|
|
444
439
|
.join("\n");
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
type: "text",
|
|
449
|
-
text: `Workflow '${name}' not found. Available:\n${suggestions || " (none)"}\nUse <location> from <available_workflows> for the absolute .js path.`,
|
|
450
|
-
},
|
|
451
|
-
],
|
|
452
|
-
details: { action: "run", runId: "", status: "not_found", name },
|
|
453
|
-
isError: true,
|
|
454
|
-
};
|
|
440
|
+
throw new Error(
|
|
441
|
+
`Workflow '${name}' not found. Available:\n${suggestions || " (none)"}\nUse <location> from <available_workflows> for the absolute .js path.`,
|
|
442
|
+
);
|
|
455
443
|
}
|
|
456
444
|
|
|
457
445
|
// m6:动态参数集(schema 即 SSOT)→ 平铺检测;无 parameters → 单次 warn + 跳过
|
|
@@ -466,17 +454,15 @@ export async function actionRun(
|
|
|
466
454
|
}
|
|
467
455
|
const flattened = findFlattenedArgKeys(params, knownKeys, knownPatterns);
|
|
468
456
|
if (flattened.length > 0) {
|
|
469
|
-
|
|
457
|
+
throw new Error(
|
|
470
458
|
`Detected ${flattened.join(", ")} at top level — they belong inside 'args'. ` +
|
|
471
459
|
`Correct: {"action":"run","name":"${name}","args":{${flattened.map((k) => `"${k}": "<value>"`).join(", ")}}}`,
|
|
472
|
-
true,
|
|
473
460
|
);
|
|
474
461
|
}
|
|
475
462
|
// slug 运行时护栏(与 subagent startHandler 对称的纵深防御;schema maxLength 是第一道关卡)
|
|
476
463
|
if (params.slug !== undefined && params.slug.length > SLUG_MAX_LENGTH) {
|
|
477
|
-
|
|
464
|
+
throw new Error(
|
|
478
465
|
`slug exceeds ${SLUG_MAX_LENGTH} chars (got ${params.slug.length}). Shorten to a kebab-case label, e.g. "fix-login", "extract-urls".`,
|
|
479
|
-
true,
|
|
480
466
|
);
|
|
481
467
|
}
|
|
482
468
|
const args = params.args ?? {};
|
|
@@ -484,36 +470,25 @@ export async function actionRun(
|
|
|
484
470
|
const time = params.time;
|
|
485
471
|
|
|
486
472
|
// 构建 RunSpec + 启动(m3:parameters 从 script.meta 拷贝——chokepoint 校验用;
|
|
487
|
-
// 校验失败 →
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
);
|
|
507
|
-
} catch (err) {
|
|
508
|
-
if (err instanceof ArgsValidationError) {
|
|
509
|
-
return {
|
|
510
|
-
content: [{ type: "text", text: err.message }],
|
|
511
|
-
details: { action: "run", runId: "", status: "invalid_args", name: script.name },
|
|
512
|
-
isError: true,
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
throw err;
|
|
516
|
-
}
|
|
473
|
+
// 校验失败 → ArgsValidationError 直接 throw 给 pi(W4:err.message 含 §5.3 指引,
|
|
474
|
+
// pi catch 后原文案进 toolResult content 并置 isError:true),其他错误保持传播)
|
|
475
|
+
const runId = await runWorkflow(
|
|
476
|
+
{
|
|
477
|
+
scriptSource: script.toExecutable(),
|
|
478
|
+
args,
|
|
479
|
+
budgetTokens: tokens,
|
|
480
|
+
budgetTimeMs: time,
|
|
481
|
+
scriptName: script.name,
|
|
482
|
+
slug: params.slug,
|
|
483
|
+
scriptPath: script.path,
|
|
484
|
+
description: script.meta.description,
|
|
485
|
+
parameters: script.meta.parameters,
|
|
486
|
+
model: params.model,
|
|
487
|
+
thinkingLevel: params.thinkingLevel,
|
|
488
|
+
},
|
|
489
|
+
deps,
|
|
490
|
+
signal,
|
|
491
|
+
);
|
|
517
492
|
|
|
518
493
|
return {
|
|
519
494
|
content: [
|
|
@@ -551,33 +526,28 @@ function actionStatus(deps: LauncherDeps): ToolResult {
|
|
|
551
526
|
};
|
|
552
527
|
}
|
|
553
528
|
|
|
554
|
-
// ──
|
|
529
|
+
// ── abort lifecycle action ────────────────────────────────────
|
|
555
530
|
|
|
531
|
+
// 一次性生命周期:abort 是唯一的提前停止方式(pause/resume 已随 D-2 移除),
|
|
532
|
+
// action 参数保留字面量类型与 WorkflowAction 单成员分发对齐。
|
|
556
533
|
async function actionLifecycle(
|
|
557
|
-
action: "
|
|
534
|
+
action: "abort",
|
|
558
535
|
params: WorkflowToolParams,
|
|
559
536
|
deps: LauncherDeps,
|
|
560
537
|
): Promise<ToolResult> {
|
|
561
538
|
const runId = params.runId;
|
|
562
539
|
if (!runId) {
|
|
563
|
-
|
|
540
|
+
throw new Error(`'runId' is required for ${action}. Correct: {"action":"${action}","runId":"<id>"} (use action:"status" to find runId)`);
|
|
564
541
|
}
|
|
565
542
|
const run = deps.runs.get(runId);
|
|
566
543
|
if (!run) {
|
|
567
|
-
|
|
544
|
+
throw new Error(
|
|
568
545
|
`Workflow '${runId}' not found. Use action:status to list active runs and their runIds.`,
|
|
569
|
-
true,
|
|
570
546
|
);
|
|
571
547
|
}
|
|
572
548
|
try {
|
|
573
549
|
const oldStatus = run.state.status;
|
|
574
|
-
|
|
575
|
-
await pauseRun(runId, deps);
|
|
576
|
-
} else if (action === "resume") {
|
|
577
|
-
await resumeRun(runId, deps);
|
|
578
|
-
} else {
|
|
579
|
-
await abortRun(runId, deps, params.error);
|
|
580
|
-
}
|
|
550
|
+
await abortRun(runId, deps, params.error);
|
|
581
551
|
const newStatus = run.state.status;
|
|
582
552
|
const reasonSuffix = run.state.reason ? ` (${run.state.reason})` : "";
|
|
583
553
|
return {
|
|
@@ -590,8 +560,9 @@ async function actionLifecycle(
|
|
|
590
560
|
details: { action, runId, status: newStatus, reason: run.state.reason },
|
|
591
561
|
};
|
|
592
562
|
} catch (err) {
|
|
563
|
+
// throw(W4b):abortRun 失败改 throw(原 return isError 被 pi 丢弃),"Error: " 前缀保持
|
|
593
564
|
const msg = err instanceof Error ? err.message : String(err);
|
|
594
|
-
|
|
565
|
+
throw new Error(`Error: ${msg}`);
|
|
595
566
|
}
|
|
596
567
|
}
|
|
597
568
|
|
|
@@ -612,10 +583,6 @@ function toRunSummary(run: WorkflowRun, store: RunStore): RunSummary {
|
|
|
612
583
|
};
|
|
613
584
|
}
|
|
614
585
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
details: undefined,
|
|
619
|
-
isError: isError || undefined,
|
|
620
|
-
};
|
|
621
|
-
}
|
|
586
|
+
// W4b:原 textResult(text, isError) helper 已删除——23 处错误路径全部改 throw
|
|
587
|
+
// (pi 只对 execute throw 置 isError:true,返回值里的 isError 被 agent-loop 丢弃,
|
|
588
|
+
// agent-loop.js:453-483),且本文件无非错误纯文本结果用途,无残留调用方。
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
*
|
|
18
18
|
* 关键实现点:(a) overlay 第二参数必须传,否则 view 不全屏;(b) trace 必须 per-render
|
|
19
19
|
* 重读(run.state.trace.toArray 返回内部数组引用,trace.append 后下次 render 可见),
|
|
20
|
-
*
|
|
21
|
-
* (saveWorkflow 内部对非 tmp workflow 返回错误消息);(d)
|
|
22
|
-
*
|
|
20
|
+
* 配条件失效 tick(签名比对,IF11)+ requestRender 保证运行中刷新;(c) 's' save 模式无条件可用
|
|
21
|
+
* (saveWorkflow 内部对非 tmp workflow 返回错误消息);(d) abort 失败时 notify 反馈,
|
|
22
|
+
* 不静默吞(run 一次性生命周期——无 pause/resume,abort 是唯一提前停止方式)。
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { promises as fsPromises } from "node:fs";
|
|
@@ -30,12 +30,14 @@ import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
|
30
30
|
import { Key, matchesKey } from "@earendil-works/pi-tui";
|
|
31
31
|
|
|
32
32
|
import {
|
|
33
|
+
countAllToolCalls,
|
|
33
34
|
getAllToolCalls,
|
|
34
35
|
projectLiveProgress,
|
|
35
36
|
} from "../../execution/execution-record.ts";
|
|
36
37
|
import type { ExecutionTraceNode } from "../../orchestration/models/types.ts";
|
|
37
38
|
import type { WorkflowRun } from "../../orchestration/models/workflow-run.ts";
|
|
38
39
|
import { saveWorkflow } from "../../orchestration/workflow-files.ts";
|
|
40
|
+
import { displayAgentName } from "../../shared/agent-ref.ts";
|
|
39
41
|
import {
|
|
40
42
|
BOX_BORDER_CHARS,
|
|
41
43
|
BUDGET_TOKENS_DIVISOR,
|
|
@@ -122,14 +124,75 @@ interface TuiLike {
|
|
|
122
124
|
|
|
123
125
|
/**
|
|
124
126
|
* view 可触发的 lifecycle 操作(由调用方注入,避免 view 直接依赖 Engine 函数)。
|
|
125
|
-
* 每个 action 接收 runId;调用方绑到
|
|
127
|
+
* 每个 action 接收 runId;调用方绑到 abortRun(一次性生命周期——仅 abort)。
|
|
126
128
|
*/
|
|
127
129
|
export interface ViewActions {
|
|
128
|
-
pause: (runId: string) => Promise<void>;
|
|
129
|
-
resume: (runId: string) => Promise<void>;
|
|
130
130
|
abort: (runId: string) => Promise<void>;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
// ── 渲染签名(IF11/TC7/DM6:tick 条件失效的判据,渲染动态字段 SSOT)──────
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* computeRenderSignature — 渲染输出中全部动态决定字段的签名(now 参数化,供测试)。
|
|
137
|
+
*
|
|
138
|
+
* 纯度说明:now 是显式参数,但 live 节点的 elapsedSeconds 经 projectLiveProgress
|
|
139
|
+
* 内的 computeElapsedSeconds 现算(record.endedAt 缺省时取 Date.now())——即使两次
|
|
140
|
+
* 调用传同一 now,跨秒的真实时钟也会使签名变。该内含实时源只朝「多失效」方向
|
|
141
|
+
* 偏离(多失效、不多绘、更不漏绘),对「签名同 → 跳过重绘」的单向判据安全。
|
|
142
|
+
*
|
|
143
|
+
* 用途:200ms tick 用它与上次签名比对——相同则该帧内容无可见变化,跳过清缓存 +
|
|
144
|
+
* requestRender(纯等待场景零重建零重绘);不同则走现状失效路径。可见行为逐帧
|
|
145
|
+
* 等价的前提 = 签名字段集覆盖当前渲染的全部动态字段(漏字段 = 该字段渲染滞后一拍,
|
|
146
|
+
* ES7;签名取原始值时粒度细于显示量化值,只多失效不多绘不漏绘)。
|
|
147
|
+
*
|
|
148
|
+
* **字段核对表**(每字段 ↔ 渲染消费点 file:line,完备性唯一证据——测试只能证
|
|
149
|
+
* 「已入字段变 → 签名变」,不能证无漏字段):
|
|
150
|
+
*
|
|
151
|
+
* | 签名字段 | 消费点 |
|
|
152
|
+
* |---|---|
|
|
153
|
+
* | run.state.status | WorkflowsView.ts renderHeader :652 / detail-content.ts :75(statusDotStr+statusLabel)/ renderFooter :695-699 |
|
|
154
|
+
* | 秒桶 Math.floor(now/1000) | renderHeader :651 formatElapsed(现算 elapsed)+ detail-content.ts :69-72(now 参与未完成节点 elapsed)|
|
|
155
|
+
* | completed/total(节点 status 推导)| renderHeader :649-652 |
|
|
156
|
+
* | budget 量化值(tokens round-k + cost toFixed(4)=BUDGET_COST_DECIMALS,与 :654 同精度——第 3-4 位小数变化是可见变化)| renderHeader :654 / saveTraceToFile :942 |
|
|
157
|
+
* | run.state.errorLogs 指纹 = length+末条 level:message(errorLogs 仅经 push + slice(-MAX_ERROR_LOGS) 变异——append-only + 前向淘汰、条目不可变;封顶后 length 不变内容移,单取 length 会漏失效,而任何可见变化必伴随 length 变或末条变,故 length+末条是完备且最小的指纹)| detail-content.ts :174-191(renderWorkerLogSection:total 标签 + 末 20 条)|
|
|
158
|
+
* | 节点 stepIndex | nodeParts 首字段(trace 数组序参与拼接,节点重排→签名变)/ saveTraceToFile :947(trace 导出 `### [#stepIndex]` 标题)|
|
|
159
|
+
* | 节点 sessionFile | detail-content.ts :314-317(renderSessionSection)|
|
|
160
|
+
* | 节点 status | 节点行 :832 statusDotStr / detail :75 |
|
|
161
|
+
* | live.totalTokens | 节点行 :836 / detail :79 / :275 |
|
|
162
|
+
* | 工具计数(签名路径用 countAllToolCalls 免克隆计数,与 getAllToolCalls(node.live).length 恒等——projectLiveProgress 投影无 toolCallCount 字段,不得引用)| 节点行 :837 / detail :80 / :223 |
|
|
163
|
+
* | live.elapsedSeconds | 节点行 :838 / detail :81 / :276 |
|
|
164
|
+
* | live.turns | detail :224 / :276 |
|
|
165
|
+
* | live.eventLog.length(append-only,长度变化即尾部窗口右移出新事件)| detail :222(filter turn_end)+ :231-235 |
|
|
166
|
+
* | live.currentActivity(type+label 均入签名)| detail :227-228 |
|
|
167
|
+
* | live.lastError(内容入签名,防同存在性不同文本漏绘)| detail :277-278 |
|
|
168
|
+
*
|
|
169
|
+
* 维护约定(DS8):WorkflowsView/detail-content 新增任何动态展示字段必须同步
|
|
170
|
+
* 并入本签名并更新上表,否则该字段渲染滞后一拍。本文件编辑会使表内 WorkflowsView
|
|
171
|
+
* 行号漂移——改动核对表下方代码后必须 grep 实际位置刷新本表。
|
|
172
|
+
*/
|
|
173
|
+
/** 秒桶宽度(ms):签名取 Math.floor(now / SECOND_MS),秒桶推进 = 可见 elapsed 变化。 */
|
|
174
|
+
const SECOND_MS = 1000;
|
|
175
|
+
|
|
176
|
+
export function computeRenderSignature(run: WorkflowRun, now: number): string {
|
|
177
|
+
const traceArr = run.state.trace.toArray();
|
|
178
|
+
const completed = traceArr.filter((n) => n.status === "completed").length;
|
|
179
|
+
const budget = run.state.budget;
|
|
180
|
+
// budget 量化串与 renderHeader budgetStr 同源同精度(round-k token / toFixed(4) cost)
|
|
181
|
+
const budgetPart = `${Math.round(budget.usedTokens / BUDGET_TOKENS_DIVISOR)}k/${budget.maxTokens ? `${Math.round(budget.maxTokens / BUDGET_TOKENS_DIVISOR)}k` : "∞"}::$${budget.usedCost.toFixed(BUDGET_COST_DECIMALS)}`;
|
|
182
|
+
// errorLogs 指纹:detail 只渲染 total 标签 + 末 20 条(renderWorkerLogSection),
|
|
183
|
+
// 而 errorLogs 仅经 push + slice(-MAX_ERROR_LOGS) 变异(append-only + 前向淘汰、
|
|
184
|
+
// 条目不可变)——封顶后 length 不变内容移,单取 length 漏失效;任何可见变化必
|
|
185
|
+
// 伴随「length 变」或「末条变」,故 length+末条(level:message)是完备最小指纹。
|
|
186
|
+
const logs = run.state.errorLogs;
|
|
187
|
+
const lastLog = logs && logs.length > 0 ? `${logs[logs.length - 1].level}:${logs[logs.length - 1].message}` : "-";
|
|
188
|
+
const errorLogsPart = `${logs?.length ?? 0}:${lastLog}`;
|
|
189
|
+
const nodeParts = traceArr.map((n) => {
|
|
190
|
+
const live = n.live ? projectLiveProgress(n.live) : undefined;
|
|
191
|
+
return `${n.stepIndex}:${n.status}:${n.sessionFile ?? "-"}:${live?.totalTokens ?? -1}:${n.live ? countAllToolCalls(n.live) : -1}:${live?.elapsedSeconds ?? -1}:${live?.turns ?? -1}:${live?.eventLog.length ?? -1}:${live?.currentActivity ? `${live.currentActivity.type}:${live.currentActivity.label}` : "-"}:${live?.lastError ?? "-"}`;
|
|
192
|
+
});
|
|
193
|
+
return [run.state.status, Math.floor(now / SECOND_MS), `${completed}/${traceArr.length}`, budgetPart, errorLogsPart, ...nodeParts].join("|");
|
|
194
|
+
}
|
|
195
|
+
|
|
133
196
|
// ── View state ────────────────────────────────────────────────
|
|
134
197
|
|
|
135
198
|
interface ViewState {
|
|
@@ -181,7 +244,7 @@ function createInitialState(): ViewState {
|
|
|
181
244
|
* @param run WorkflowRun 聚合根(读 state.status/spec/trace/meta)
|
|
182
245
|
* @param theme ThemeLike(避免直接 import Pi runtime)
|
|
183
246
|
* @param ctx ExtensionContext(调 ui.custom 渲染 + ui.notify 错误反馈)
|
|
184
|
-
* @param actions lifecycle 操作(
|
|
247
|
+
* @param actions lifecycle 操作(abort),由调用方注入
|
|
185
248
|
*/
|
|
186
249
|
export function createWorkflowsView(
|
|
187
250
|
run: WorkflowRun,
|
|
@@ -212,10 +275,17 @@ export function createWorkflowsView(
|
|
|
212
275
|
const requestRender = () => tui.requestRender();
|
|
213
276
|
|
|
214
277
|
// ── 轮询 tick:engine 无事件推送,view 自轮询 trace 变化 ──
|
|
215
|
-
// 每 200ms
|
|
216
|
-
//
|
|
278
|
+
// 每 200ms 条件失效(IF11/TC7):先算渲染签名(computeRenderSignature,
|
|
279
|
+
// 覆盖 header/节点行/L2 detail 全部动态字段),与上次相同 → 该帧内容无可见
|
|
280
|
+
// 变化,直接 return(不清 cache、不 requestRender——纯等待场景零重建零重绘);
|
|
281
|
+
// 不同 → 现状路径(清缓存 + requestRender)。lastSignature 初始 undefined,
|
|
282
|
+
// 首 tick 必失效(保证首绘)。签名计算 O(N)(N=trace 节点数)远低于全量 render。
|
|
283
|
+
let lastSignature: string | undefined;
|
|
217
284
|
const tick = setInterval(() => {
|
|
218
285
|
if (state.disposed) return;
|
|
286
|
+
const signature = computeRenderSignature(run, Date.now());
|
|
287
|
+
if (signature === lastSignature) return;
|
|
288
|
+
lastSignature = signature;
|
|
219
289
|
cache.key = undefined;
|
|
220
290
|
cache.lines = undefined;
|
|
221
291
|
requestRender();
|
|
@@ -358,21 +428,9 @@ export function createWorkflowsView(
|
|
|
358
428
|
return;
|
|
359
429
|
}
|
|
360
430
|
|
|
361
|
-
// ── Lifecycle shortcuts (no restart per D-9) ──
|
|
362
|
-
if (data === "p") {
|
|
363
|
-
if (run.state.status === "running") {
|
|
364
|
-
void actions.pause(run.runId)
|
|
365
|
-
.then(() => { cache.key = undefined; requestRender(); })
|
|
366
|
-
.catch((err: Error) => ctx.ui.notify(`Pause failed: ${err.message}`, "error"));
|
|
367
|
-
} else if (run.state.status === "paused") {
|
|
368
|
-
void actions.resume(run.runId)
|
|
369
|
-
.then(() => { cache.key = undefined; requestRender(); })
|
|
370
|
-
.catch((err: Error) => ctx.ui.notify(`Resume failed: ${err.message}`, "error"));
|
|
371
|
-
}
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
431
|
+
// ── Lifecycle shortcuts (no restart per D-9; no pause/resume — one-shot) ──
|
|
374
432
|
if (data === "a") {
|
|
375
|
-
if (run.state.status === "running"
|
|
433
|
+
if (run.state.status === "running") {
|
|
376
434
|
void actions.abort(run.runId)
|
|
377
435
|
.then(() => { cache.key = undefined; requestRender(); })
|
|
378
436
|
.catch((err: Error) => ctx.ui.notify(`Abort failed: ${err.message}`, "error"));
|
|
@@ -498,7 +556,7 @@ export function createWorkflowsView(
|
|
|
498
556
|
// │ ● 2 deploy 0/1 │ ● tester model ... │
|
|
499
557
|
// │ (pad) │ (pad) │
|
|
500
558
|
// ╰───────────────────────────────────────────────────╯
|
|
501
|
-
// ↑↓ phase · ⏎ enter ·
|
|
559
|
+
// ↑↓ phase · ⏎ enter · a abort · s save · esc back ← footer (框外)
|
|
502
560
|
//
|
|
503
561
|
// body = sidebar(SIDEBAR_WIDTH) │ main(rest)
|
|
504
562
|
// save overlay 活跃时居中覆盖 body。
|
|
@@ -621,10 +679,8 @@ function renderFooter(
|
|
|
621
679
|
? "↑↓ agent · ⏎ detail"
|
|
622
680
|
: "↑↓ agent · ⏎ prompt · PgUp/PgDn scroll";
|
|
623
681
|
const actionParts: string[] = [];
|
|
624
|
-
|
|
625
|
-
if (status === "running" || status === "paused") {
|
|
682
|
+
if (run.state.status === "running") {
|
|
626
683
|
actionParts.push("a abort");
|
|
627
|
-
actionParts.push(status === "paused" ? "p resume" : "p pause");
|
|
628
684
|
}
|
|
629
685
|
actionParts.push("s save");
|
|
630
686
|
actionParts.push("S trace");
|
|
@@ -765,7 +821,7 @@ function renderLevel1(
|
|
|
765
821
|
const tokStr = live.totalTokens > 0 ? `${Math.round(live.totalTokens / BUDGET_TOKENS_DIVISOR)}k tok` : "";
|
|
766
822
|
const tcCount = getAllToolCalls(node.live).length;
|
|
767
823
|
const elapsed = formatElapsedSeconds(live.elapsedSeconds);
|
|
768
|
-
rightLines.push(`${pointer}${dot} ${node.agent} ${node.model} ${tokStr} · ${tcCount} tools · ${elapsed}`);
|
|
824
|
+
rightLines.push(`${pointer}${dot} ${displayAgentName(node.agent)} ${node.model} ${tokStr} · ${tcCount} tools · ${elapsed}`);
|
|
769
825
|
} else {
|
|
770
826
|
const elapsed = formatElapsed(
|
|
771
827
|
node.startedAt,
|
|
@@ -774,7 +830,7 @@ function renderLevel1(
|
|
|
774
830
|
const tok = node.result?.usage;
|
|
775
831
|
const tokStr = tok ? `${Math.round((tok.input + tok.output) / BUDGET_TOKENS_DIVISOR)}k tok` : "";
|
|
776
832
|
const tcCount = node.result?.toolCalls?.length ?? 0;
|
|
777
|
-
rightLines.push(`${pointer}${dot} ${node.agent} ${node.model} ${tokStr} · ${tcCount} tools · ${elapsed}`);
|
|
833
|
+
rightLines.push(`${pointer}${dot} ${displayAgentName(node.agent)} ${node.model} ${tokStr} · ${tcCount} tools · ${elapsed}`);
|
|
778
834
|
}
|
|
779
835
|
}
|
|
780
836
|
state.agentScrollOffset = agentStart;
|
|
@@ -808,9 +864,10 @@ function renderLevel2(
|
|
|
808
864
|
const a = agents[i];
|
|
809
865
|
const pointer = i === state.agentIdx ? "❯ " : " ";
|
|
810
866
|
const maxNameWidth = SIDEBAR_WIDTH - AGENT_NAME_BUDGET;
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
867
|
+
const agentRef = displayAgentName(a.agent);
|
|
868
|
+
const agentName = visibleLen(agentRef) > maxNameWidth
|
|
869
|
+
? agentRef.slice(0, maxNameWidth - 1) + ELLIPSIS
|
|
870
|
+
: agentRef;
|
|
814
871
|
leftLines.push(`${pointer}${agentName}`);
|
|
815
872
|
}
|
|
816
873
|
|