@selesai/code 0.13.3 → 0.13.4
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 +11 -0
- package/dist/defaults/models.json +17 -1
- package/dist/extensions/cost-reconcile.test.ts +24 -5
- package/dist/extensions/cost-reconcile.ts +14 -7
- package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
- package/dist/extensions/pi-intercom/README.md +4 -5
- package/dist/extensions/pi-intercom/config.test.ts +3 -31
- package/dist/extensions/pi-intercom/config.ts +0 -15
- package/dist/extensions/pi-intercom/index.ts +9 -46
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +49 -57
- package/dist/extensions/pi-intercom/package.json +1 -1
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +20 -0
- package/dist/extensions/pi-intercom/reply-tracker.ts +8 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +27 -0
- package/dist/extensions/pi-subagents/docs/tool-reference.md +4 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +2 -2
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +48 -243
- package/dist/extensions/pi-subagents/skills/council-mode/references/pass-contracts.md +150 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +87 -37
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +29 -233
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +49 -8
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +34 -27
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +157 -28
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -0
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +45 -2
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +3 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +13 -2
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +5 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +10 -2
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +11 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +98 -1
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +138 -4
- package/dist/extensions/pi-subagents/src/runs/shared/background-process-options.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
- package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +52 -3
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +47 -1
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +45 -18
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +21 -2
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +15 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +34 -1
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +11 -3
- package/dist/extensions/pi-subagents/src/tui/render-helpers.ts +31 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +597 -112
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +40 -1
- package/dist/extensions/pi-subagents/src/workflows/host-command.ts +6 -1
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +53 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +55 -6
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +111 -1
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +206 -38
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +522 -31
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +123 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +57 -1
- package/dist/extensions/pi-subagents/test/unit/background-process-options.test.ts +17 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +44 -4
- package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +20 -3
- package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +93 -17
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +103 -26
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +58 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +22 -2
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +13 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +54 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +48 -1
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +21 -6
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +11 -0
- package/dist/extensions/pi-web-agent/CHANGELOG.md +410 -0
- package/dist/extensions/pi-web-agent/README.md +131 -0
- package/dist/extensions/pi-web-agent/package.json +4 -2
- package/dist/extensions/pi-web-agent/src/backends/config.ts +62 -5
- package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -0
- package/dist/extensions/pi-web-agent/src/backends/factory.ts +96 -6
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +183 -47
- package/dist/extensions/pi-web-agent/src/extension.ts +62 -25
- package/dist/extensions/pi-web-agent/src/extract/readability.ts +19 -11
- package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +5 -4
- package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +2 -25
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +5 -2
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +2 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +72 -7
- package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +8 -1
- package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +28 -3
- package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +4 -0
- package/dist/extensions/pi-web-agent/src/orchestration/url.ts +35 -0
- package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +18 -6
- package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +14 -2
- package/dist/extensions/pi-web-agent/src/readers/github-reader.ts +150 -0
- package/dist/extensions/pi-web-agent/src/readers/limits.ts +3 -0
- package/dist/extensions/pi-web-agent/src/readers/pdf-reader.ts +87 -0
- package/dist/extensions/pi-web-agent/src/readers/resolver.ts +25 -0
- package/dist/extensions/pi-web-agent/src/readers/types.ts +11 -0
- package/dist/extensions/pi-web-agent/src/readers/youtube-reader.ts +79 -0
- package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +32 -5
- package/dist/extensions/pi-web-agent/src/search/exa.ts +109 -0
- package/dist/extensions/pi-web-agent/src/search/fanout.ts +154 -0
- package/dist/extensions/pi-web-agent/src/search/tavily.ts +113 -0
- package/dist/extensions/pi-web-agent/src/search/youcom.ts +109 -0
- package/dist/extensions/pi-web-agent/src/tools/web-search.ts +22 -9
- package/dist/extensions/pi-web-agent/src/types.ts +19 -4
- package/package.json +1 -1
|
@@ -81,6 +81,16 @@ function withTerminalWidth<T>(columns: number, fn: () => T): T {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function withMockedDateNow<T>(now: number, fn: () => T): T {
|
|
85
|
+
const original = Date.now;
|
|
86
|
+
Date.now = () => now;
|
|
87
|
+
try {
|
|
88
|
+
return fn();
|
|
89
|
+
} finally {
|
|
90
|
+
Date.now = original;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
84
94
|
describe("renderSubagentResult fork indicator", () => {
|
|
85
95
|
it("renders result-owned nested children for foreground single, parallel, and chain runs", () => {
|
|
86
96
|
const cases = [
|
|
@@ -301,8 +311,8 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
301
311
|
}, { expanded: false }, theme).render(160).join("\n");
|
|
302
312
|
|
|
303
313
|
assert.match(compact, /parallel \[mixed\]/);
|
|
304
|
-
assert.match(compact, /
|
|
305
|
-
assert.match(compact, /
|
|
314
|
+
assert.match(compact, /scan \[fresh\]/);
|
|
315
|
+
assert.match(compact, /fix \[fork\]/);
|
|
306
316
|
|
|
307
317
|
const expanded = renderSubagentResult!({
|
|
308
318
|
content: [{ type: "text", text: "done" }],
|
|
@@ -317,8 +327,8 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
317
327
|
}, { expanded: true }, theme).render(160).join("\n");
|
|
318
328
|
|
|
319
329
|
assert.match(expanded, /parallel \[mixed\]/);
|
|
320
|
-
assert.match(expanded, /
|
|
321
|
-
assert.match(expanded, /
|
|
330
|
+
assert.match(expanded, /scan \[fresh\]/);
|
|
331
|
+
assert.match(expanded, /fix \[fork\]/);
|
|
322
332
|
});
|
|
323
333
|
|
|
324
334
|
it("uses compacted tool-call summaries when messages were stripped", () => {
|
|
@@ -435,12 +445,16 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
435
445
|
content: [{ type: "text" as const, text: testCase.name }],
|
|
436
446
|
details: { mode: "single" as const, results: [child] },
|
|
437
447
|
};
|
|
438
|
-
const compact
|
|
439
|
-
|
|
448
|
+
const [compact, expanded] = withMockedDateNow(0, () => [
|
|
449
|
+
renderSubagentResult!(result, { expanded: false }, theme).render(120).join("\n"),
|
|
450
|
+
renderSubagentResult!(result, { expanded: true }, theme).render(120).join("\n"),
|
|
451
|
+
]);
|
|
440
452
|
|
|
441
453
|
assert.equal(firstGrapheme(compact), testCase.glyph, `${testCase.name} compact glyph`);
|
|
442
454
|
assert.equal(firstGrapheme(expanded), testCase.glyph, `${testCase.name} expanded glyph`);
|
|
443
455
|
assert.match((expanded.split("\n")[0] ?? "").trimEnd(), new RegExp(`reviewer(?: \\| [^·]+)? · ${testCase.label}$`), `${testCase.name} expanded label`);
|
|
456
|
+
const compactEvidence = { detached: "Detached", stopped: "Stopped", interrupted: "Paused", failed: "Error" }[testCase.name];
|
|
457
|
+
if (compactEvidence) assert.match(compact, new RegExp(`⎿ ${compactEvidence}`), `${testCase.name} compact evidence`);
|
|
444
458
|
}
|
|
445
459
|
});
|
|
446
460
|
|
|
@@ -671,15 +685,15 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
671
685
|
const expanded = renderSubagentResult!(result, { expanded: true }, theme).render(160).join("\n");
|
|
672
686
|
|
|
673
687
|
assert.match(compact, /^■ parallel/);
|
|
674
|
-
assert.match(compact, /■
|
|
688
|
+
assert.match(compact, /■ three/);
|
|
675
689
|
assert.doesNotMatch(compact, /running agent/);
|
|
676
690
|
assert.match(expanded, /^■ parallel[^\n]* · detached/);
|
|
677
|
-
assert.match(expanded, /■
|
|
678
|
-
assert.match(expanded, /■
|
|
679
|
-
assert.match(expanded, /■
|
|
691
|
+
assert.match(expanded, /■ one[^\n]* · detached/);
|
|
692
|
+
assert.match(expanded, /■ two[^\n]* · stopped/);
|
|
693
|
+
assert.match(expanded, /■ three[^\n]* · paused/);
|
|
680
694
|
assert.doesNotMatch(expanded, /running agent/);
|
|
681
|
-
assert.match(expanded, /✗
|
|
682
|
-
assert.match(expanded, /✓
|
|
695
|
+
assert.match(expanded, /✗ four · failed/);
|
|
696
|
+
assert.match(expanded, /✓ five · completed/);
|
|
683
697
|
});
|
|
684
698
|
|
|
685
699
|
it("renders a stale-running interrupted aggregate as paused", () => {
|
|
@@ -836,8 +850,8 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
836
850
|
],
|
|
837
851
|
},
|
|
838
852
|
}, { expanded: false }, theme).render(160).join("\n");
|
|
839
|
-
assert.match(multi, /
|
|
840
|
-
assert.match(multi, /
|
|
853
|
+
assert.match(multi, /scan \(claude-haiku-4-5 · thinking low\)/);
|
|
854
|
+
assert.match(multi, /fix \(gpt-5-mini\)/);
|
|
841
855
|
|
|
842
856
|
const expanded = renderSubagentResult!({
|
|
843
857
|
content: [{ type: "text", text: "done" }],
|
|
@@ -854,10 +868,10 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
854
868
|
}],
|
|
855
869
|
},
|
|
856
870
|
}, { expanded: true }, theme).render(160).join("\n");
|
|
857
|
-
assert.match(expanded, /
|
|
871
|
+
assert.match(expanded, /review \(gpt-5\.5 · thinking high\)/);
|
|
858
872
|
});
|
|
859
873
|
|
|
860
|
-
it("
|
|
874
|
+
it("strips repeated agent prefixes in expanded running multi-result rows", () => {
|
|
861
875
|
const sessionName = "reviewer: Review the diff";
|
|
862
876
|
const widget = renderSubagentResult!({
|
|
863
877
|
content: [{ type: "text", text: "(running...)" }],
|
|
@@ -877,11 +891,11 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
877
891
|
}, { expanded: true }, theme);
|
|
878
892
|
|
|
879
893
|
const text = widget.render(160).join("\n");
|
|
880
|
-
assert.match(text, /
|
|
881
|
-
assert.doesNotMatch(text, /
|
|
894
|
+
assert.match(text, /Review the diff[^\n]* · running/);
|
|
895
|
+
assert.doesNotMatch(text, /reviewer:\s+Review the diff/);
|
|
882
896
|
});
|
|
883
897
|
|
|
884
|
-
it("keeps running compact result output stable when progress is unchanged",
|
|
898
|
+
it("keeps running compact result output stable at the same render clock when progress is unchanged", () => {
|
|
885
899
|
const result = {
|
|
886
900
|
content: [{ type: "text" as const, text: "(running...)" }],
|
|
887
901
|
details: {
|
|
@@ -910,9 +924,10 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
910
924
|
}],
|
|
911
925
|
},
|
|
912
926
|
};
|
|
913
|
-
const first =
|
|
914
|
-
|
|
915
|
-
|
|
927
|
+
const [first, second] = withMockedDateNow(0, () => [
|
|
928
|
+
renderSubagentResult!(result, { expanded: false }, theme).render(120),
|
|
929
|
+
renderSubagentResult!(result, { expanded: false }, theme).render(120),
|
|
930
|
+
]);
|
|
916
931
|
|
|
917
932
|
assert.deepEqual(second, first);
|
|
918
933
|
});
|
|
@@ -1016,14 +1031,14 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
1016
1031
|
}, { expanded: false }, theme);
|
|
1017
1032
|
|
|
1018
1033
|
const lines = widget.render(120);
|
|
1019
|
-
const pendingIndex = lines.findIndex((line) => /Step 2:
|
|
1034
|
+
const pendingIndex = lines.findIndex((line) => /Step 2\/2: second/.test(line));
|
|
1020
1035
|
assert.notEqual(pendingIndex, -1);
|
|
1021
|
-
assert.match(lines[pendingIndex]!, /◦ Step 2:
|
|
1036
|
+
assert.match(lines[pendingIndex]!, /◦ Step 2\/2: second · pending/);
|
|
1022
1037
|
assert.doesNotMatch(lines[pendingIndex]!, /0ms/);
|
|
1023
1038
|
assert.doesNotMatch(lines[pendingIndex + 1] ?? "", /Done \(no text output\)/);
|
|
1024
1039
|
});
|
|
1025
1040
|
|
|
1026
|
-
it("uses running/done wording and
|
|
1041
|
+
it("uses running/done wording and readable labels for live parallel rendering", () => {
|
|
1027
1042
|
const widget = renderSubagentResult!({
|
|
1028
1043
|
content: [{ type: "text", text: "(running...)" }],
|
|
1029
1044
|
details: {
|
|
@@ -1063,8 +1078,8 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
1063
1078
|
|
|
1064
1079
|
const text = widget.render(120).join("\n");
|
|
1065
1080
|
assert.match(text, /parallel · 2 agents running · 0\/3 done/);
|
|
1066
|
-
assert.match(text, /
|
|
1067
|
-
assert.doesNotMatch(text, /Step 3: worker/);
|
|
1081
|
+
assert.match(text, /third task/);
|
|
1082
|
+
assert.doesNotMatch(text, /Step 3\/3: worker/);
|
|
1068
1083
|
assert.doesNotMatch(text, /Agent 1: worker/);
|
|
1069
1084
|
});
|
|
1070
1085
|
|
|
@@ -1097,7 +1112,7 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
1097
1112
|
assert.match(text, /parallel · 1 agent running · 1\/3 done/);
|
|
1098
1113
|
});
|
|
1099
1114
|
|
|
1100
|
-
it("labels active chain parallel groups with chain step and
|
|
1115
|
+
it("labels active chain parallel groups with chain step and readable candidates", () => {
|
|
1101
1116
|
const widget = renderSubagentResult!({
|
|
1102
1117
|
content: [{ type: "text", text: "running" }],
|
|
1103
1118
|
details: {
|
|
@@ -1127,8 +1142,9 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
1127
1142
|
|
|
1128
1143
|
const text = widget.render(120).join("\n");
|
|
1129
1144
|
assert.match(text, /chain · step 1\/3 · parallel group: 2 agents running · 0\/3 done/);
|
|
1130
|
-
assert.match(text, /
|
|
1131
|
-
assert.match(text, /
|
|
1145
|
+
assert.match(text, /Scan the repository/);
|
|
1146
|
+
assert.match(text, /review/);
|
|
1147
|
+
assert.match(text, /Agent 3: agent-3 · pending/);
|
|
1132
1148
|
assert.doesNotMatch(text, /Step 1: scout/);
|
|
1133
1149
|
});
|
|
1134
1150
|
|
|
@@ -1172,13 +1188,13 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
1172
1188
|
|
|
1173
1189
|
const text = widget.render(120).join("\n");
|
|
1174
1190
|
assert.match(text, /chain · step 2\/3 · parallel group: 2 agents running · 0\/2 done/);
|
|
1175
|
-
assert.match(text, /
|
|
1176
|
-
assert.match(text, /
|
|
1191
|
+
assert.match(text, /scan/);
|
|
1192
|
+
assert.match(text, /review/);
|
|
1177
1193
|
assert.doesNotMatch(text, /planner/);
|
|
1178
1194
|
assert.doesNotMatch(text, /Agent 1\/2: planner/);
|
|
1179
1195
|
});
|
|
1180
1196
|
|
|
1181
|
-
it("uses logical chain progress and
|
|
1197
|
+
it("uses logical chain progress and readable labels for completed mixed chains", () => {
|
|
1182
1198
|
const progress = [
|
|
1183
1199
|
{ index: 0, agent: "planner", status: "completed" as const, task: "plan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 1 },
|
|
1184
1200
|
{ index: 1, agent: "scout", status: "completed" as const, task: "scan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 1 },
|
|
@@ -1206,10 +1222,10 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
1206
1222
|
|
|
1207
1223
|
const text = widget.render(120).join("\n");
|
|
1208
1224
|
assert.match(text, /chain · step 3\/3/);
|
|
1209
|
-
assert.match(text, /Step 1:
|
|
1210
|
-
assert.match(text, /
|
|
1211
|
-
assert.match(text, /
|
|
1212
|
-
assert.match(text, /Step 3:
|
|
1225
|
+
assert.match(text, /Step 1\/3: plan/);
|
|
1226
|
+
assert.match(text, /Step 2\/3: parallel group/);
|
|
1227
|
+
assert.match(text, /Scan completed targets/);
|
|
1228
|
+
assert.match(text, /Step 3\/3: write/);
|
|
1213
1229
|
assert.doesNotMatch(text, /step 4\/4/);
|
|
1214
1230
|
});
|
|
1215
1231
|
|
|
@@ -1234,7 +1250,159 @@ describe("renderSubagentResult fork indicator", () => {
|
|
|
1234
1250
|
|
|
1235
1251
|
const text = widget.render(120).join("\n");
|
|
1236
1252
|
assert.match(text, /chain · step 1\/3/);
|
|
1237
|
-
assert.match(text, /Step 1:
|
|
1253
|
+
assert.match(text, /Step 1\/3: scan/);
|
|
1238
1254
|
assert.doesNotMatch(text, /parallel group:/);
|
|
1239
1255
|
});
|
|
1256
|
+
|
|
1257
|
+
it("keeps single-child foreground labels aligned across summary and result views", () => {
|
|
1258
|
+
const result = {
|
|
1259
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1260
|
+
details: {
|
|
1261
|
+
mode: "single" as const,
|
|
1262
|
+
results: [{
|
|
1263
|
+
agent: "reviewer",
|
|
1264
|
+
sessionName: "reviewer: Review the current docs",
|
|
1265
|
+
task: "Review the current docs",
|
|
1266
|
+
exitCode: 0,
|
|
1267
|
+
messages: [],
|
|
1268
|
+
usage: emptyUsage,
|
|
1269
|
+
}],
|
|
1270
|
+
},
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
const summary = renderSubagentSummary!(result, {}, theme).render(180).join("\n");
|
|
1274
|
+
assert.match(summary, /reviewer/);
|
|
1275
|
+
assert.doesNotMatch(summary, /Step 1\/1/);
|
|
1276
|
+
assert.doesNotMatch(summary, /Agent 1\/1/);
|
|
1277
|
+
assert.doesNotMatch(summary, /reviewer:\s+Review the current docs/);
|
|
1278
|
+
|
|
1279
|
+
for (const expanded of [false, true]) {
|
|
1280
|
+
const text = renderSubagentResult!(result, { expanded }, theme).render(180).join("\n");
|
|
1281
|
+
assert.match(text, /reviewer/, expanded ? "expanded final result" : "compact final result");
|
|
1282
|
+
assert.doesNotMatch(text, /Step 1\/1/);
|
|
1283
|
+
assert.doesNotMatch(text, /Agent 1\/1/);
|
|
1284
|
+
assert.doesNotMatch(text, /reviewer:\s+Review the current docs/);
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
it("renders foreground top-level parallel results with readable group labels", () => {
|
|
1289
|
+
const result = {
|
|
1290
|
+
content: [{ type: "text" as const, text: "running" }],
|
|
1291
|
+
details: {
|
|
1292
|
+
mode: "parallel" as const,
|
|
1293
|
+
totalSteps: 2,
|
|
1294
|
+
results: [
|
|
1295
|
+
{
|
|
1296
|
+
agent: "scout",
|
|
1297
|
+
sessionName: "scout: Gather context",
|
|
1298
|
+
task: "Gather context",
|
|
1299
|
+
exitCode: 0,
|
|
1300
|
+
messages: [],
|
|
1301
|
+
usage: emptyUsage,
|
|
1302
|
+
progress: { index: 0, agent: "scout", status: "running" as const, task: "Gather context", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
agent: "reviewer",
|
|
1306
|
+
sessionName: "reviewer · Review diff",
|
|
1307
|
+
task: "Review diff",
|
|
1308
|
+
exitCode: 0,
|
|
1309
|
+
messages: [],
|
|
1310
|
+
usage: emptyUsage,
|
|
1311
|
+
progress: { index: 1, agent: "reviewer", status: "running" as const, task: "Review diff", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
|
|
1312
|
+
},
|
|
1313
|
+
],
|
|
1314
|
+
},
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
for (const expanded of [false, true]) {
|
|
1318
|
+
const text = renderSubagentResult!(result, { expanded }, theme).render(220).join("\n");
|
|
1319
|
+
assert.match(text, /Gather context/);
|
|
1320
|
+
assert.match(text, /Review diff/);
|
|
1321
|
+
assert.doesNotMatch(text, /Agent \d+\/2:/);
|
|
1322
|
+
assert.doesNotMatch(text, /scout:\s+Gather context/);
|
|
1323
|
+
assert.doesNotMatch(text, /reviewer\s*[·:]\s+Review diff/);
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
it("keeps logical Step n\/m labels for foreground chain parallel groups", () => {
|
|
1328
|
+
const result = {
|
|
1329
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1330
|
+
details: {
|
|
1331
|
+
mode: "chain" as const,
|
|
1332
|
+
chainAgents: ["planner", "[scout+reviewer]", "writer"],
|
|
1333
|
+
totalSteps: 3,
|
|
1334
|
+
results: [
|
|
1335
|
+
{ agent: "planner", task: "Plan", exitCode: 0, messages: [], usage: emptyUsage },
|
|
1336
|
+
{ agent: "scout", sessionName: "scout: Gather context", task: "Gather context", exitCode: 0, messages: [], usage: emptyUsage },
|
|
1337
|
+
{ agent: "reviewer", sessionName: "reviewer: Review diff", task: "Review diff", exitCode: 0, messages: [], usage: emptyUsage },
|
|
1338
|
+
{ agent: "writer", task: "Write", exitCode: 0, messages: [], usage: emptyUsage },
|
|
1339
|
+
],
|
|
1340
|
+
},
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
for (const expanded of [false, true]) {
|
|
1344
|
+
const text = renderSubagentResult!(result, { expanded }, theme).render(220).join("\n");
|
|
1345
|
+
assert.match(text, /Step 2\/3: parallel group/);
|
|
1346
|
+
assert.match(text, /Gather context/);
|
|
1347
|
+
assert.match(text, /Review diff/);
|
|
1348
|
+
assert.doesNotMatch(text, /scout:\s+Gather context/);
|
|
1349
|
+
assert.doesNotMatch(text, /reviewer:\s+Review diff/);
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
it("keeps duplicate unlabeled foreground agents distinguishable", () => {
|
|
1354
|
+
const result = {
|
|
1355
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1356
|
+
details: {
|
|
1357
|
+
mode: "parallel" as const,
|
|
1358
|
+
totalSteps: 2,
|
|
1359
|
+
results: [
|
|
1360
|
+
{ agent: "reviewer", task: "Inspect the same files", exitCode: 0, messages: [], usage: emptyUsage },
|
|
1361
|
+
{ agent: "reviewer", task: "Inspect the same files", exitCode: 0, messages: [], usage: emptyUsage },
|
|
1362
|
+
],
|
|
1363
|
+
},
|
|
1364
|
+
};
|
|
1365
|
+
|
|
1366
|
+
for (const expanded of [false, true]) {
|
|
1367
|
+
const text = renderSubagentResult!(result, { expanded }, theme).render(180).join("\n");
|
|
1368
|
+
const rows = text.split("\n").filter((line) => /Inspect the same files/.test(line) && !/task:/.test(line));
|
|
1369
|
+
assert.equal(rows.length, 2);
|
|
1370
|
+
assert.notEqual(rows[0], rows[1], "duplicate unlabeled rows need stable disambiguators");
|
|
1371
|
+
for (const row of rows) {
|
|
1372
|
+
assert.equal(row.match(/Inspect the same files/g)?.length, 1, "duplicate rows should render the candidate only once");
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
it("keeps final-result failure and output evidence on dedicated lines", () => {
|
|
1378
|
+
const result = {
|
|
1379
|
+
content: [{ type: "text" as const, text: "failed" }],
|
|
1380
|
+
details: {
|
|
1381
|
+
mode: "parallel" as const,
|
|
1382
|
+
totalSteps: 1,
|
|
1383
|
+
results: [{
|
|
1384
|
+
agent: "reviewer",
|
|
1385
|
+
sessionName: "reviewer: Review the current docs",
|
|
1386
|
+
task: "[Write to: /tmp/review.md] Review the current docs",
|
|
1387
|
+
exitCode: 1,
|
|
1388
|
+
error: "Review failed after checking the current docs",
|
|
1389
|
+
messages: [],
|
|
1390
|
+
usage: emptyUsage,
|
|
1391
|
+
artifactPaths: { outputPath: "/tmp/review-artifact.md" },
|
|
1392
|
+
children: [nestedChild("failed-child", "failed")],
|
|
1393
|
+
}],
|
|
1394
|
+
},
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
for (const expanded of [false, true]) {
|
|
1398
|
+
const lines = renderSubagentResult!(result, { expanded }, theme).render(180);
|
|
1399
|
+
const text = lines.join("\n");
|
|
1400
|
+
assert.ok(lines.some((line) => /^\s+output: \/tmp\/review\.md$/.test(line)), "output evidence should be its own line");
|
|
1401
|
+
assert.ok(lines.some((line) => new RegExp(`^\\s+${expanded ? "artifacts" : "output"}: \/tmp\/review-artifact\\.md$`).test(line)), "artifact evidence should be its own line");
|
|
1402
|
+
if (expanded) assert.match(text, /failed-child · failed/, "nested failure evidence should remain visible");
|
|
1403
|
+
assert.ok(lines.some((line) => /(?:Error|error): Review failed after checking the current docs/.test(line)), expanded ? "expanded result should keep failure evidence" : "compact result should keep failure evidence");
|
|
1404
|
+
assert.doesNotMatch(text, /Agent 1\/1: reviewer.*(?:Error|error):/);
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1240
1408
|
});
|