@selesai/code 0.8.6 → 0.8.7
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 +16 -0
- package/dist/core/agent-session.js +13 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/provider-composer.js +17 -4
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/system-prompt.js +2 -8
- package/dist/core/system-prompt.test.js +6 -0
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/schema-prune.d.ts +16 -0
- package/dist/core/tools/schema-prune.js +39 -0
- package/dist/core/tools/schema-prune.test.d.ts +1 -0
- package/dist/core/tools/schema-prune.test.js +111 -0
- package/dist/defaults/settings.json +1 -0
- package/dist/extensions/model-prompt-injector/config.json +10 -0
- package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
- package/dist/extensions/model-prompt-injector/index.ts +151 -0
- package/dist/extensions/package.json +1 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
- package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
- package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
- package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/agents/worker.md +1 -1
- package/dist/extensions/pi-subagents/docs/configuration.md +78 -19
- package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
- package/dist/extensions/pi-subagents/docs/missions.md +5 -3
- package/dist/extensions/pi-subagents/docs/models.md +1 -1
- package/dist/extensions/pi-subagents/docs/observability.md +42 -2
- package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
- package/dist/extensions/pi-subagents/docs/workflows.md +5 -5
- package/dist/extensions/pi-subagents/install.mjs +2 -2
- package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +15 -14
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
- package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
- package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
- package/dist/extensions/pi-subagents/src/extension/config.ts +53 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +67 -13
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
- package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
- package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
- package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +5 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +205 -41
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +30 -24
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +101 -4
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +333 -45
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
- package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
- package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
- package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +50 -4
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +128 -16
- package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
- package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
- package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
- package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
- package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
- package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
- package/dist/extensions/question/index.ts +21 -6
- package/dist/extensions/question/question-list.ts +7 -1
- package/dist/extensions/question/row-layout.ts +3 -0
- package/dist/extensions/question/tests/exp.test.ts +70 -0
- package/dist/extensions/question/tests/exp2.test.ts +59 -0
- package/dist/extensions/question/tests/exp3.test.ts +47 -0
- package/dist/extensions/question/tests/helpers.test.ts +7 -0
- package/dist/extensions/question/tests/question-list.test.ts +140 -0
- package/dist/extensions/question/tests/row-layout.test.ts +13 -0
- package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
- package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
- package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
- package/dist/skills/pi-subagents/SKILL.md +1 -1
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/skills/pi-subagents/references/execution-controls.md +15 -14
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/docs/settings.md +6 -0
- package/package.json +3 -3
|
@@ -1,66 +1,159 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import {
|
|
4
|
+
EXTERNAL_RUN_LIMITS,
|
|
4
5
|
EXTERNAL_RUN_REGISTRY_KEY,
|
|
5
|
-
|
|
6
|
+
EXTERNAL_RUN_REGISTRY_VERSION,
|
|
7
|
+
listExternalRuns,
|
|
8
|
+
registerExternalRun,
|
|
6
9
|
snapshotExternalRuns,
|
|
10
|
+
unregisterExternalRun,
|
|
11
|
+
updateExternalRun,
|
|
7
12
|
} from "../../src/api/external-runs.ts";
|
|
8
13
|
|
|
9
14
|
function clearRegistry(): void {
|
|
10
15
|
delete (globalThis as Record<PropertyKey, unknown>)[Symbol.for(EXTERNAL_RUN_REGISTRY_KEY)];
|
|
11
16
|
}
|
|
12
17
|
|
|
13
|
-
test("external
|
|
18
|
+
test("external runs register, update, list, and unregister cached display records", () => {
|
|
14
19
|
clearRegistry();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
assert.deepEqual(registerExternalRun({
|
|
21
|
+
id: "review-1083",
|
|
22
|
+
sessionId: "session-a",
|
|
23
|
+
source: "interactive-shell",
|
|
24
|
+
label: "Dependency review",
|
|
25
|
+
state: "running",
|
|
26
|
+
startedAt: 100,
|
|
27
|
+
currentAction: "Inspecting package metadata",
|
|
28
|
+
}), {
|
|
29
|
+
id: "review-1083",
|
|
30
|
+
sessionId: "session-a",
|
|
31
|
+
source: "interactive-shell",
|
|
32
|
+
label: "Dependency review",
|
|
33
|
+
state: "running",
|
|
34
|
+
startedAt: 100,
|
|
35
|
+
currentAction: "Inspecting package metadata",
|
|
28
36
|
});
|
|
29
|
-
assert.deepEqual(snapshotExternalRuns("session-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
assert.deepEqual(snapshotExternalRuns("session-b"), []);
|
|
38
|
+
assert.deepEqual(updateExternalRun("session-a", "review-1083", {
|
|
39
|
+
state: "completed",
|
|
40
|
+
updatedAt: 190,
|
|
41
|
+
endedAt: 200,
|
|
42
|
+
preview: "Review complete",
|
|
43
|
+
reportPath: "/tmp/review.md",
|
|
44
|
+
}), {
|
|
45
|
+
id: "review-1083",
|
|
32
46
|
sessionId: "session-a",
|
|
33
|
-
source: "
|
|
47
|
+
source: "interactive-shell",
|
|
48
|
+
label: "Dependency review",
|
|
34
49
|
state: "completed",
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
startedAt: 100,
|
|
51
|
+
updatedAt: 190,
|
|
52
|
+
endedAt: 200,
|
|
53
|
+
currentAction: "Inspecting package metadata",
|
|
54
|
+
preview: "Review complete",
|
|
37
55
|
reportPath: "/tmp/review.md",
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
assert.
|
|
42
|
-
|
|
56
|
+
});
|
|
57
|
+
assert.deepEqual(listExternalRuns("session-a"), snapshotExternalRuns("session-a"));
|
|
58
|
+
assert.equal(unregisterExternalRun("session-a", "review-1083"), true);
|
|
59
|
+
assert.equal(unregisterExternalRun("session-a", "review-1083"), false);
|
|
60
|
+
assert.deepEqual(snapshotExternalRuns("session-a"), []);
|
|
43
61
|
clearRegistry();
|
|
44
62
|
});
|
|
45
63
|
|
|
46
|
-
test("external
|
|
64
|
+
test("external run validation is atomic and display text is bounded", () => {
|
|
47
65
|
clearRegistry();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
registerExternalRun({
|
|
67
|
+
id: "run",
|
|
68
|
+
sessionId: "session-a",
|
|
69
|
+
source: "tool",
|
|
70
|
+
label: "Initial",
|
|
71
|
+
state: "running",
|
|
72
|
+
startedAt: 1,
|
|
54
73
|
});
|
|
55
|
-
assert.throws(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
assert.throws(
|
|
75
|
+
() => updateExternalRun("session-a", "run", { state: "invalid" as never, label: "Changed" }),
|
|
76
|
+
/state is invalid/,
|
|
77
|
+
);
|
|
78
|
+
assert.equal(snapshotExternalRuns("session-a")[0]?.label, "Initial");
|
|
79
|
+
const updated = updateExternalRun("session-a", "run", {
|
|
80
|
+
label: `\u001b[31m${"x".repeat(EXTERNAL_RUN_LIMITS.maxTextLength + 20)}`,
|
|
81
|
+
preview: `line 1\n${"p".repeat(EXTERNAL_RUN_LIMITS.maxPreviewLength + 20)}`,
|
|
63
82
|
});
|
|
64
|
-
assert.
|
|
83
|
+
assert.equal(updated.label.length, EXTERNAL_RUN_LIMITS.maxTextLength);
|
|
84
|
+
assert.ok(updated.preview!.length <= EXTERNAL_RUN_LIMITS.maxPreviewLength);
|
|
85
|
+
assert.doesNotMatch(updated.label, /\u001b/);
|
|
86
|
+
assert.doesNotMatch(updated.preview!, /\n/);
|
|
87
|
+
assert.throws(
|
|
88
|
+
() => registerExternalRun({ id: "unsafe\nrun", sessionId: "session-a", source: "tool", label: "Unsafe", state: "running", startedAt: 1 }),
|
|
89
|
+
/display-safe/,
|
|
90
|
+
);
|
|
91
|
+
assert.throws(
|
|
92
|
+
() => registerExternalRun({ id: "extra", sessionId: "session-a", source: "tool", label: "Extra", state: "running", startedAt: 1, cwd: "/private" } as never),
|
|
93
|
+
/unknown fields: cwd/,
|
|
94
|
+
);
|
|
95
|
+
assert.throws(
|
|
96
|
+
() => registerExternalRun({ id: "invalid-date", sessionId: "session-a", source: "tool", label: "Invalid date", state: "running", startedAt: Number.MAX_SAFE_INTEGER }),
|
|
97
|
+
/safe timestamp/,
|
|
98
|
+
);
|
|
99
|
+
clearRegistry();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("external snapshots throw on malformed cached records by default", () => {
|
|
103
|
+
clearRegistry();
|
|
104
|
+
try {
|
|
105
|
+
(globalThis as Record<PropertyKey, unknown>)[Symbol.for(EXTERNAL_RUN_REGISTRY_KEY)] = {
|
|
106
|
+
version: EXTERNAL_RUN_REGISTRY_VERSION,
|
|
107
|
+
runs: new Map<string, unknown>([
|
|
108
|
+
["session-a\0bad", { id: "bad", sessionId: "session-a", source: "tool", label: "Bad", state: "running", startedAt: Number.NaN }],
|
|
109
|
+
["session-a\0good", { id: "good", sessionId: "session-a", source: "tool", label: "Good", state: "running", startedAt: 1 }],
|
|
110
|
+
]),
|
|
111
|
+
};
|
|
112
|
+
assert.throws(
|
|
113
|
+
() => snapshotExternalRuns("session-a"),
|
|
114
|
+
/Malformed cached external run 'session-a\0bad': External run startedAt must be a non-negative safe timestamp/,
|
|
115
|
+
);
|
|
116
|
+
} finally {
|
|
117
|
+
clearRegistry();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("external snapshots can remove malformed cached records for display-only callers", () => {
|
|
122
|
+
clearRegistry();
|
|
123
|
+
const diagnostics: string[] = [];
|
|
124
|
+
try {
|
|
125
|
+
(globalThis as Record<PropertyKey, unknown>)[Symbol.for(EXTERNAL_RUN_REGISTRY_KEY)] = {
|
|
126
|
+
version: EXTERNAL_RUN_REGISTRY_VERSION,
|
|
127
|
+
runs: new Map<string, unknown>([
|
|
128
|
+
["session-a\0bad", { id: "bad", sessionId: "session-a", source: "tool", label: "Bad", state: "running", startedAt: Number.NaN }],
|
|
129
|
+
["session-a\0good", { id: "good", sessionId: "session-a", source: "tool", label: "Good", state: "running", startedAt: 1 }],
|
|
130
|
+
]),
|
|
131
|
+
};
|
|
132
|
+
assert.deepEqual(snapshotExternalRuns("session-a", { ignoreMalformed: true, onMalformedRecord: (message) => diagnostics.push(message) }).map((run) => run.id), ["good"]);
|
|
133
|
+
assert.deepEqual(diagnostics, ["Malformed cached external run 'session-a\0bad': External run startedAt must be a non-negative safe timestamp."]);
|
|
134
|
+
assert.deepEqual(snapshotExternalRuns("session-a").map((run) => run.id), ["good"]);
|
|
135
|
+
} finally {
|
|
136
|
+
clearRegistry();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("external snapshots apply count and serialized byte caps without provider callbacks", () => {
|
|
141
|
+
clearRegistry();
|
|
142
|
+
for (let index = 0; index < EXTERNAL_RUN_LIMITS.maxSnapshotRuns + 5; index++) {
|
|
143
|
+
registerExternalRun({
|
|
144
|
+
id: `run-${index}`,
|
|
145
|
+
sessionId: "session-a",
|
|
146
|
+
source: "tool",
|
|
147
|
+
label: `Run ${index}`,
|
|
148
|
+
state: index === 0 ? "running" : "completed",
|
|
149
|
+
startedAt: index,
|
|
150
|
+
updatedAt: index,
|
|
151
|
+
preview: "p".repeat(EXTERNAL_RUN_LIMITS.maxPreviewLength),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const snapshot = snapshotExternalRuns("session-a");
|
|
155
|
+
assert.ok(snapshot.length <= EXTERNAL_RUN_LIMITS.maxSnapshotRuns);
|
|
156
|
+
assert.ok(Buffer.byteLength(JSON.stringify(snapshot), "utf8") <= EXTERNAL_RUN_LIMITS.maxSerializedBytes);
|
|
157
|
+
assert.equal(snapshot[0]?.id, "run-0", "active jobs sort ahead of terminal jobs");
|
|
65
158
|
clearRegistry();
|
|
66
159
|
});
|
|
@@ -3,6 +3,7 @@ import { describe, it } from "node:test";
|
|
|
3
3
|
import { Editor, type EditorComponent, visibleWidth } from "@earendil-works/pi-tui";
|
|
4
4
|
import type { ExtensionContext } from "@selesai/code";
|
|
5
5
|
import type { SubagentState } from "../../src/shared/types.ts";
|
|
6
|
+
import { EXTERNAL_RUN_REGISTRY_KEY, EXTERNAL_RUN_REGISTRY_VERSION, registerExternalRun } from "../../src/api/external-runs.ts";
|
|
6
7
|
import { collectFleetSnapshot } from "../../src/tui/fleet.ts";
|
|
7
8
|
import {
|
|
8
9
|
FLEET_STATUS_WIDGET_KEY,
|
|
@@ -13,6 +14,10 @@ import {
|
|
|
13
14
|
resolveFleetViewPlacement,
|
|
14
15
|
} from "../../src/tui/fleet-status.ts";
|
|
15
16
|
|
|
17
|
+
function clearExternalRuns(): void {
|
|
18
|
+
delete (globalThis as Record<PropertyKey, unknown>)[Symbol.for(EXTERNAL_RUN_REGISTRY_KEY)];
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
function stateForTest(): SubagentState {
|
|
17
22
|
return {
|
|
18
23
|
baseCwd: process.cwd(),
|
|
@@ -46,6 +51,73 @@ describe("below-editor subagent FleetView", () => {
|
|
|
46
51
|
assert.equal(formatFleetTokens(1_250_000), "↓ 1.3M tokens");
|
|
47
52
|
});
|
|
48
53
|
|
|
54
|
+
it("renders cached external jobs with an external marker and elapsed time", () => {
|
|
55
|
+
clearExternalRuns();
|
|
56
|
+
registerExternalRun({
|
|
57
|
+
id: "external-review",
|
|
58
|
+
sessionId: "session-current",
|
|
59
|
+
source: "interactive-shell",
|
|
60
|
+
label: "Dependency review",
|
|
61
|
+
state: "running",
|
|
62
|
+
startedAt: Date.now() - 11_000,
|
|
63
|
+
currentAction: "Inspecting package metadata",
|
|
64
|
+
});
|
|
65
|
+
const state = stateForTest();
|
|
66
|
+
state.activeAsyncCapacity = { used: 0, limit: 0 };
|
|
67
|
+
assert.deepEqual(collectFleetStatusEntries(state).map((entry) => ({ key: entry.key, agent: entry.agent, description: entry.description })), [{
|
|
68
|
+
key: "external:external-review",
|
|
69
|
+
agent: "external · Dependency review",
|
|
70
|
+
description: "Inspecting package metadata",
|
|
71
|
+
}]);
|
|
72
|
+
let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
|
|
73
|
+
const ctx = {
|
|
74
|
+
hasUI: true,
|
|
75
|
+
ui: {
|
|
76
|
+
setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
|
|
77
|
+
onTerminalInput() { return () => {}; },
|
|
78
|
+
getEditorText() { return ""; },
|
|
79
|
+
requestRender() {},
|
|
80
|
+
notify() {},
|
|
81
|
+
theme,
|
|
82
|
+
},
|
|
83
|
+
} as unknown as ExtensionContext;
|
|
84
|
+
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
|
|
85
|
+
try {
|
|
86
|
+
fleet.setContext(ctx);
|
|
87
|
+
const tui = { requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor };
|
|
88
|
+
const component = widgetFactory!(tui, theme);
|
|
89
|
+
const compact = component.render(80)[0]!;
|
|
90
|
+
assert.match(compact, /1 active job/);
|
|
91
|
+
assert.doesNotMatch(compact, /Async runs|tokens/);
|
|
92
|
+
fleet.handleKey("\x1b[B");
|
|
93
|
+
const expanded = component.render(80).join("\n");
|
|
94
|
+
assert.match(expanded, /external · Dependency review · running/);
|
|
95
|
+
assert.match(expanded, /11s/);
|
|
96
|
+
assert.doesNotMatch(expanded.split("\n").find((line) => line.includes("Dependency review"))!, /tokens/);
|
|
97
|
+
} finally {
|
|
98
|
+
fleet.dispose();
|
|
99
|
+
clearExternalRuns();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("warns when compact FleetView cannot inspect external jobs", () => {
|
|
104
|
+
clearExternalRuns();
|
|
105
|
+
const warnings: string[] = [];
|
|
106
|
+
const originalWarn = console.warn;
|
|
107
|
+
console.warn = (message?: unknown) => warnings.push(String(message));
|
|
108
|
+
(globalThis as Record<PropertyKey, unknown>)[Symbol.for(EXTERNAL_RUN_REGISTRY_KEY)] = {
|
|
109
|
+
version: EXTERNAL_RUN_REGISTRY_VERSION + 1,
|
|
110
|
+
runs: new Map(),
|
|
111
|
+
};
|
|
112
|
+
try {
|
|
113
|
+
assert.deepEqual(collectFleetStatusEntries(stateForTest()), []);
|
|
114
|
+
assert.match(warnings[0]!, /Failed to inspect external jobs/);
|
|
115
|
+
} finally {
|
|
116
|
+
console.warn = originalWarn;
|
|
117
|
+
clearExternalRuns();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
49
121
|
it("resolves configured FleetView placement with a below-editor fallback", () => {
|
|
50
122
|
assert.equal(resolveFleetViewPlacement(undefined), "belowEditor");
|
|
51
123
|
assert.equal(resolveFleetViewPlacement("belowEditor"), "belowEditor");
|
|
@@ -417,6 +489,16 @@ describe("below-editor subagent FleetView", () => {
|
|
|
417
489
|
thinking: "medium",
|
|
418
490
|
startedAt: 10,
|
|
419
491
|
lastUpdate: 20,
|
|
492
|
+
...(index === 4 ? {
|
|
493
|
+
children: [{
|
|
494
|
+
id: "nested-hidden-child",
|
|
495
|
+
parentRunId: "nested-4",
|
|
496
|
+
depth: 2,
|
|
497
|
+
path: [{ runId: "supervisor", stepIndex: 0 }, { runId: "nested-4" }],
|
|
498
|
+
state: "running" as const,
|
|
499
|
+
agent: "hidden-child",
|
|
500
|
+
}],
|
|
501
|
+
} : {}),
|
|
420
502
|
})),
|
|
421
503
|
});
|
|
422
504
|
let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
|
|
@@ -448,7 +530,8 @@ describe("below-editor subagent FleetView", () => {
|
|
|
448
530
|
assert.match(line!, new RegExp(state));
|
|
449
531
|
}
|
|
450
532
|
assert.doesNotMatch(lines, /leaf-4.*running/);
|
|
451
|
-
assert.
|
|
533
|
+
assert.doesNotMatch(lines, /hidden-child/);
|
|
534
|
+
assert.match(lines, /\+2 nested leaves/);
|
|
452
535
|
} finally {
|
|
453
536
|
fleet.dispose();
|
|
454
537
|
}
|
|
@@ -594,6 +677,174 @@ describe("below-editor subagent FleetView", () => {
|
|
|
594
677
|
}]);
|
|
595
678
|
});
|
|
596
679
|
|
|
680
|
+
it("renders a workflow-owned foreground child under its workflow parent", () => {
|
|
681
|
+
const state = stateForTest();
|
|
682
|
+
state.asyncJobs.set("workflow-1", {
|
|
683
|
+
asyncId: "workflow-1",
|
|
684
|
+
asyncDir: "/tmp/workflow-1",
|
|
685
|
+
status: "running",
|
|
686
|
+
mode: "workflow",
|
|
687
|
+
startedAt: 10,
|
|
688
|
+
updatedAt: 20,
|
|
689
|
+
nestedChildren: [{
|
|
690
|
+
id: "owner-nested",
|
|
691
|
+
parentRunId: "workflow-1",
|
|
692
|
+
depth: 1,
|
|
693
|
+
path: [{ runId: "workflow-1" }],
|
|
694
|
+
state: "running",
|
|
695
|
+
agent: "owner-nested",
|
|
696
|
+
}],
|
|
697
|
+
});
|
|
698
|
+
state.foregroundControls.set("child-1", {
|
|
699
|
+
runId: "child-1",
|
|
700
|
+
parentWorkflowRunId: "workflow-1",
|
|
701
|
+
workflowKey: "review",
|
|
702
|
+
mode: "single",
|
|
703
|
+
startedAt: 11,
|
|
704
|
+
updatedAt: 20,
|
|
705
|
+
activeChildren: new Map([[0, { index: 0, agent: "reviewer", startedAt: 11, updatedAt: 20 }]]),
|
|
706
|
+
nestedChildren: [{
|
|
707
|
+
id: "nested-review",
|
|
708
|
+
parentRunId: "child-1",
|
|
709
|
+
parentStepIndex: 0,
|
|
710
|
+
depth: 1,
|
|
711
|
+
path: [{ runId: "child-1", stepIndex: 0 }],
|
|
712
|
+
state: "running",
|
|
713
|
+
agent: "nested-reviewer",
|
|
714
|
+
}],
|
|
715
|
+
});
|
|
716
|
+
state.foregroundControls.set("child-2", {
|
|
717
|
+
runId: "child-2",
|
|
718
|
+
parentWorkflowRunId: "workflow-1",
|
|
719
|
+
workflowKey: "test",
|
|
720
|
+
mode: "single",
|
|
721
|
+
startedAt: 12,
|
|
722
|
+
updatedAt: 20,
|
|
723
|
+
activeChildren: new Map([[0, { index: 0, agent: "tester", startedAt: 12, updatedAt: 20 }]]),
|
|
724
|
+
nestedChildren: [{
|
|
725
|
+
id: "nested-test",
|
|
726
|
+
parentRunId: "child-2",
|
|
727
|
+
parentStepIndex: 0,
|
|
728
|
+
depth: 1,
|
|
729
|
+
path: [{ runId: "child-2", stepIndex: 0 }],
|
|
730
|
+
state: "running",
|
|
731
|
+
agent: "nested-tester",
|
|
732
|
+
}],
|
|
733
|
+
});
|
|
734
|
+
const entries = collectFleetStatusEntries(state);
|
|
735
|
+
assert.deepEqual(entries.map((entry) => [entry.key, entry.parentKey]), [
|
|
736
|
+
["async:workflow-1", undefined],
|
|
737
|
+
["foreground-active:child-1:0", "async:workflow-1"],
|
|
738
|
+
["foreground-active:child-2:0", "async:workflow-1"],
|
|
739
|
+
]);
|
|
740
|
+
|
|
741
|
+
let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
|
|
742
|
+
const ctx = {
|
|
743
|
+
hasUI: true,
|
|
744
|
+
ui: {
|
|
745
|
+
setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
|
|
746
|
+
onTerminalInput() { return () => {}; },
|
|
747
|
+
getEditorText() { return ""; },
|
|
748
|
+
requestRender() {},
|
|
749
|
+
notify() {},
|
|
750
|
+
theme,
|
|
751
|
+
},
|
|
752
|
+
} as unknown as ExtensionContext;
|
|
753
|
+
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
|
|
754
|
+
try {
|
|
755
|
+
fleet.setContext(ctx);
|
|
756
|
+
const component = widgetFactory!({ requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor }, theme);
|
|
757
|
+
const compact = component.render(120);
|
|
758
|
+
assert.match(compact[0]!, /2 active agents/, "workflow wrappers must not be counted as leaf agents");
|
|
759
|
+
assert.doesNotMatch(compact[0]!, /3 active agents/);
|
|
760
|
+
assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
|
|
761
|
+
const lines = component.render(120);
|
|
762
|
+
const workflowIndex = lines.findIndex((line) => line.includes("workflow · running"));
|
|
763
|
+
const childIndex = lines.findIndex((line) => line.includes("reviewer · running"));
|
|
764
|
+
const nestedIndex = lines.findIndex((line) => line.includes("nested-reviewer"));
|
|
765
|
+
const secondChildIndex = lines.findIndex((line) => line.includes("tester · running"));
|
|
766
|
+
const secondNestedIndex = lines.findIndex((line) => line.includes("nested-tester"));
|
|
767
|
+
const ownerNestedIndex = lines.findIndex((line) => line.includes("owner-nested"));
|
|
768
|
+
assert.ok(workflowIndex >= 0 && childIndex > workflowIndex && nestedIndex > childIndex);
|
|
769
|
+
assert.ok(secondChildIndex > nestedIndex && secondNestedIndex > secondChildIndex && ownerNestedIndex > secondNestedIndex);
|
|
770
|
+
assert.match(lines[childIndex]!, /├─.*reviewer/);
|
|
771
|
+
assert.match(lines[nestedIndex]!, /├─.*nested-reviewer/);
|
|
772
|
+
assert.match(lines[secondNestedIndex]!, /├─.*nested-tester/);
|
|
773
|
+
assert.match(lines[ownerNestedIndex]!, /└─.*owner-nested/);
|
|
774
|
+
} finally {
|
|
775
|
+
fleet.dispose();
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("renders recursive nested runs and steps within the existing row budget", () => {
|
|
780
|
+
const state = stateForTest();
|
|
781
|
+
state.asyncJobs.set("supervisor", {
|
|
782
|
+
asyncId: "supervisor",
|
|
783
|
+
asyncDir: "/tmp/supervisor",
|
|
784
|
+
status: "running",
|
|
785
|
+
mode: "single",
|
|
786
|
+
startedAt: 10,
|
|
787
|
+
updatedAt: 20,
|
|
788
|
+
steps: [{ agent: "supervisor", index: 0, status: "running" }],
|
|
789
|
+
nestedChildren: [{
|
|
790
|
+
id: "level-one",
|
|
791
|
+
parentRunId: "supervisor",
|
|
792
|
+
parentStepIndex: 0,
|
|
793
|
+
depth: 1,
|
|
794
|
+
path: [{ runId: "supervisor", stepIndex: 0 }],
|
|
795
|
+
state: "running",
|
|
796
|
+
agent: "level-one",
|
|
797
|
+
children: [{
|
|
798
|
+
id: "level-two",
|
|
799
|
+
parentRunId: "level-one",
|
|
800
|
+
depth: 2,
|
|
801
|
+
path: [{ runId: "supervisor", stepIndex: 0 }, { runId: "level-one" }],
|
|
802
|
+
state: "running",
|
|
803
|
+
mode: "parallel",
|
|
804
|
+
steps: [{
|
|
805
|
+
agent: "level-two",
|
|
806
|
+
status: "running",
|
|
807
|
+
children: [0, 1, 2, 3].map((index) => ({
|
|
808
|
+
id: `leaf-${index}`,
|
|
809
|
+
parentRunId: "level-two",
|
|
810
|
+
depth: 3,
|
|
811
|
+
path: [{ runId: "level-two" }],
|
|
812
|
+
state: "running" as const,
|
|
813
|
+
agent: `leaf-${index}`,
|
|
814
|
+
})),
|
|
815
|
+
}],
|
|
816
|
+
}],
|
|
817
|
+
}],
|
|
818
|
+
});
|
|
819
|
+
let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
|
|
820
|
+
const ctx = {
|
|
821
|
+
hasUI: true,
|
|
822
|
+
ui: {
|
|
823
|
+
setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
|
|
824
|
+
onTerminalInput() { return () => {}; },
|
|
825
|
+
getEditorText() { return ""; },
|
|
826
|
+
requestRender() {},
|
|
827
|
+
notify() {},
|
|
828
|
+
theme,
|
|
829
|
+
},
|
|
830
|
+
} as unknown as ExtensionContext;
|
|
831
|
+
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
|
|
832
|
+
try {
|
|
833
|
+
fleet.setContext(ctx);
|
|
834
|
+
const component = widgetFactory!({ requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor }, theme);
|
|
835
|
+
assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
|
|
836
|
+
const lines = component.render(140).join("\n");
|
|
837
|
+
assert.match(lines, /level-one/);
|
|
838
|
+
assert.match(lines, /level-two/);
|
|
839
|
+
assert.match(lines, /leaf-0/);
|
|
840
|
+
assert.match(lines, /leaf-1/);
|
|
841
|
+
assert.doesNotMatch(lines, /leaf-[23]/);
|
|
842
|
+
assert.match(lines, /\+2 nested leaves/);
|
|
843
|
+
} finally {
|
|
844
|
+
fleet.dispose();
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
|
|
597
848
|
it("uses the same item keys as the full inspector", () => {
|
|
598
849
|
const state = stateForTest();
|
|
599
850
|
state.foregroundControls.set("foreground", {
|