@selesai/code 0.8.5 → 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 +28 -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/models.json +5 -0
- package/dist/defaults/settings.json +1 -0
- package/dist/extensions/agent-browser.test.ts +178 -2
- package/dist/extensions/agent-browser.ts +2 -0
- package/dist/extensions/copy-turn.test.ts +229 -0
- package/dist/extensions/copy-turn.ts +11 -0
- package/dist/extensions/grep-app/index.test.ts +356 -1
- package/dist/extensions/grep-app/index.ts +6 -1
- package/dist/extensions/handoff-new.test.ts +351 -161
- package/dist/extensions/handoff-new.ts +3 -0
- package/dist/extensions/inline-skills.test.ts +93 -0
- package/dist/extensions/inline-skills.ts +3 -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/builder.md +5 -2
- package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
- 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 -11
- 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 +4 -4
- 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 +16 -15
- 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 +59 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +146 -23
- 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 +17 -6
- 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 +11 -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/notify.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +224 -42
- 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 +35 -26
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +108 -9
- 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/background/wait-subscriptions.ts +2 -1
- 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/foreground-history.ts +7 -4
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +368 -50
- 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/session-lineage.ts +71 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +66 -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 +129 -17
- 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/src/workflows/workflow-auto-relaunch.ts +28 -0
- 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/handoff-adoption.test.ts +103 -0
- 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 +91 -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/session-lineage.test.ts +73 -0
- 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-auto-relaunch.test.ts +45 -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/ponytail/index.js +11 -0
- package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
- package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
- package/dist/extensions/ponytail/test/extension.test.js +274 -140
- package/dist/extensions/ponytail/test/helpers.test.js +280 -92
- package/dist/extensions/question/index.ts +33 -0
- package/dist/extensions/question/question-list.ts +28 -0
- package/dist/extensions/question/row-layout.ts +3 -0
- package/dist/extensions/question/tests/batch.test.ts +103 -65
- 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 +75 -35
- package/dist/extensions/question/tests/question-list.test.ts +640 -198
- package/dist/extensions/question/tests/row-layout.test.ts +193 -114
- package/dist/extensions/question/tests/schemas.test.ts +42 -19
- package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
- package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
- package/dist/extensions/question/tests/wizard.test.ts +771 -50
- package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
- package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
- package/dist/extensions/rtk.test.ts +180 -1
- package/dist/extensions/tokenin-onboarding.ts +3 -0
- package/dist/extensions/undo.test.ts +720 -0
- package/dist/extensions/undo.ts +6 -0
- package/dist/extensions/web-agent-onboarding.test.ts +415 -0
- package/dist/extensions/workflow/extension.ts +3 -3
- package/dist/extensions/workflow/modes.ts +41 -19
- 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 +16 -15
- 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/plans/workflow-autoloop-reference.md +1 -2
- package/docs/plans/workflow-handoff-carryover.md +108 -0
- package/docs/settings.md +6 -0
- package/package.json +4 -1
|
@@ -104,6 +104,19 @@ describe("resolveIntercomBridge", () => {
|
|
|
104
104
|
assert.equal(bridge.extensionDir, NATIVE_INTERCOM_EXTENSION_DIR);
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
+
it("uses a per-run override instead of the global config", () => {
|
|
108
|
+
const bridge = resolveIntercomBridge({
|
|
109
|
+
config: { mode: "always", resultDelivery: true },
|
|
110
|
+
override: { mode: "off" },
|
|
111
|
+
context: "fresh",
|
|
112
|
+
orchestratorTarget: "main",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
assert.equal(bridge.active, false);
|
|
116
|
+
assert.equal(bridge.mode, "off");
|
|
117
|
+
assert.equal(bridge.resultDelivery, false);
|
|
118
|
+
});
|
|
119
|
+
|
|
107
120
|
it("can disable external grouped-result delivery without disabling supervisor coordination", () => {
|
|
108
121
|
const bridge = resolveIntercomBridge({
|
|
109
122
|
config: { mode: "always", resultDelivery: false },
|
|
@@ -166,17 +179,18 @@ describe("applyIntercomBridgeToAgent", () => {
|
|
|
166
179
|
instruction: "Intercom orchestration channel:\n- Need a decision or blocked: contact_supervisor({ reason: \"need_decision\", message: \"<question>\" })\n- Blocked/update: contact_supervisor({ reason: \"progress_update\", message: \"UPDATE: <summary>\" })",
|
|
167
180
|
};
|
|
168
181
|
|
|
169
|
-
it("injects
|
|
182
|
+
it("injects contact_supervisor and prompt instructions", () => {
|
|
170
183
|
const updated = applyIntercomBridgeToAgent(makeAgent({ tools: ["read", "bash"] }), activeBridge);
|
|
171
|
-
assert.deepEqual(updated.tools, ["read", "bash", "
|
|
184
|
+
assert.deepEqual(updated.tools, ["read", "bash", "contact_supervisor"]);
|
|
172
185
|
assert.match(updated.systemPrompt, /Intercom orchestration channel:/);
|
|
173
186
|
assert.match(updated.systemPrompt, /contact_supervisor/);
|
|
187
|
+
assert.doesNotMatch(updated.systemPrompt ?? "", /Generic intercom/);
|
|
174
188
|
});
|
|
175
189
|
|
|
176
190
|
it("is idempotent", () => {
|
|
177
191
|
const first = applyIntercomBridgeToAgent(makeAgent({ tools: ["read"] }), activeBridge);
|
|
178
192
|
const second = applyIntercomBridgeToAgent(first, activeBridge);
|
|
179
|
-
assert.equal(second.tools?.filter((tool) => tool === "intercom").length,
|
|
193
|
+
assert.equal(second.tools?.filter((tool) => tool === "intercom").length, 0);
|
|
180
194
|
assert.equal(second.tools?.filter((tool) => tool === "contact_supervisor").length, 1);
|
|
181
195
|
assert.equal(second.systemPrompt, first.systemPrompt);
|
|
182
196
|
});
|
|
@@ -184,10 +198,15 @@ describe("applyIntercomBridgeToAgent", () => {
|
|
|
184
198
|
it("does not block native supervisor tools for agents with explicit extension allowlists", () => {
|
|
185
199
|
const agent = makeAgent({ tools: ["read"], extensions: ["/tmp/other-extension/index.ts"] });
|
|
186
200
|
const updated = applyIntercomBridgeToAgent(agent, activeBridge);
|
|
187
|
-
assert.deepEqual(updated.tools, ["read", "
|
|
201
|
+
assert.deepEqual(updated.tools, ["read", "contact_supervisor"]);
|
|
188
202
|
assert.match(updated.systemPrompt, /contact_supervisor/);
|
|
189
203
|
});
|
|
190
204
|
|
|
205
|
+
it("preserves explicitly requested external intercom tools", () => {
|
|
206
|
+
const updated = applyIntercomBridgeToAgent(makeAgent({ tools: ["read", "intercom"] }), activeBridge);
|
|
207
|
+
assert.deepEqual(updated.tools, ["read", "intercom", "contact_supervisor"]);
|
|
208
|
+
});
|
|
209
|
+
|
|
191
210
|
it("does not widen explicit empty or MCP-only builtin allowlists", () => {
|
|
192
211
|
const emptyTools = applyIntercomBridgeToAgent(makeAgent({ tools: [] }), activeBridge);
|
|
193
212
|
assert.deepEqual(emptyTools.tools, []);
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
arbitrateCompletionGuardRescue,
|
|
5
5
|
createTaskMutationArbiter,
|
|
6
6
|
isCompletionGuardFailure,
|
|
7
|
+
mapArbiterDecision,
|
|
7
8
|
maybeRescueCompletionGuardFailure,
|
|
8
9
|
type TaskMutationArbiter,
|
|
9
10
|
} from "../../src/runs/shared/llm-intent-arbiter.ts";
|
|
@@ -30,7 +31,7 @@ function stubArbiter(verdict: "read-only" | "implementation" | "unavailable" | "
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
describe("arbitrateCompletionGuardRescue", () => {
|
|
33
|
-
it("rescues
|
|
34
|
+
it("rescues on a read-only verdict, keeps the failure otherwise", async () => {
|
|
34
35
|
const rescued = await arbitrateCompletionGuardRescue({
|
|
35
36
|
guardTriggered: true,
|
|
36
37
|
task: "t",
|
|
@@ -47,6 +48,18 @@ describe("arbitrateCompletionGuardRescue", () => {
|
|
|
47
48
|
}
|
|
48
49
|
});
|
|
49
50
|
|
|
51
|
+
it("never arbitrates from partial evidence on tasks over 8000 chars", async () => {
|
|
52
|
+
const middle = "And now apply the fix in the middle of this long task. ";
|
|
53
|
+
const longTask = "Review the setup. ".repeat(400) + middle + "Review more. ".repeat(400);
|
|
54
|
+
assert.ok(longTask.length > 8000);
|
|
55
|
+
const kept = await arbitrateCompletionGuardRescue({
|
|
56
|
+
guardTriggered: true,
|
|
57
|
+
task: longTask,
|
|
58
|
+
arbiter: stubArbiter("read-only"),
|
|
59
|
+
});
|
|
60
|
+
assert.deepEqual(kept, { triggered: true, rescued: false });
|
|
61
|
+
});
|
|
62
|
+
|
|
50
63
|
it("keeps the guard verdict without an arbiter or on arbiter failure", async () => {
|
|
51
64
|
assert.deepEqual(
|
|
52
65
|
await arbitrateCompletionGuardRescue({ guardTriggered: true, task: "t" }),
|
|
@@ -63,6 +76,48 @@ describe("arbitrateCompletionGuardRescue", () => {
|
|
|
63
76
|
});
|
|
64
77
|
});
|
|
65
78
|
|
|
79
|
+
describe("mapArbiterDecision", () => {
|
|
80
|
+
it("only a high-confidence read_only rescues", () => {
|
|
81
|
+
assert.equal(mapArbiterDecision({ classification: "read_only", confidence: "high" }), "read-only");
|
|
82
|
+
for (const confidence of ["low", "medium", undefined] as const) {
|
|
83
|
+
assert.equal(
|
|
84
|
+
mapArbiterDecision({ classification: "read_only", confidence }),
|
|
85
|
+
"implementation",
|
|
86
|
+
String(confidence),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
assert.equal(mapArbiterDecision({ classification: "implementation", confidence: "high" }), "implementation");
|
|
90
|
+
assert.equal(mapArbiterDecision(undefined), "unavailable");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("arbiter auth receiver", () => {
|
|
95
|
+
it("resolves registry credentials through the registry receiver (class instance state)", async () => {
|
|
96
|
+
// Prototype method reading instance state: a detached call would lose
|
|
97
|
+
// `this` and fail auth. The fix must call it as a method on the registry.
|
|
98
|
+
class FakeRegistry {
|
|
99
|
+
readonly key = "instance-key";
|
|
100
|
+
async getApiKeyAndHeaders(_model: unknown) {
|
|
101
|
+
return { ok: true, apiKey: this.key };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
let captured: Record<string, unknown> | undefined;
|
|
105
|
+
const ctx = {
|
|
106
|
+
model: { provider: "test", id: "model-1", api: "test-api" },
|
|
107
|
+
modelRegistry: new FakeRegistry(),
|
|
108
|
+
} as never;
|
|
109
|
+
const arbiter = createTaskMutationArbiter(ctx, {
|
|
110
|
+
streamFn: async (_model: unknown, _context: unknown, opts: Record<string, unknown>) => {
|
|
111
|
+
captured = opts;
|
|
112
|
+
throw new Error("stream fail");
|
|
113
|
+
},
|
|
114
|
+
})!;
|
|
115
|
+
const verdict = await arbiter("t");
|
|
116
|
+
assert.equal(verdict, "unavailable"); // stream deliberately fails
|
|
117
|
+
assert.equal(captured?.apiKey, "instance-key", "registry credential must reach the stream function");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
66
121
|
describe("isCompletionGuardFailure", () => {
|
|
67
122
|
it("recognizes the guard error prefix", () => {
|
|
68
123
|
assert.equal(isCompletionGuardFailure({ error: GUARD_ERROR }), true);
|
|
@@ -7,6 +7,7 @@ import { handleMissionAction } from "../../src/missions/actions.ts";
|
|
|
7
7
|
import { collectGoalContinuationNotices } from "../../src/missions/goal-driver.ts";
|
|
8
8
|
import { createMission, readMission, resolveMissionStoreLocation, updateMission } from "../../src/missions/store.ts";
|
|
9
9
|
import { missionStatePath } from "../../src/missions/workflow-state.ts";
|
|
10
|
+
import type { RetainedChild } from "../../src/runs/background/retained-children.ts";
|
|
10
11
|
|
|
11
12
|
function fixture() {
|
|
12
13
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-goal-mission-"));
|
|
@@ -46,7 +47,7 @@ describe("goal mission continuation", () => {
|
|
|
46
47
|
const resumed = collectGoalContinuationNotices({
|
|
47
48
|
location: test.location,
|
|
48
49
|
ownerSessionId: "session-1",
|
|
49
|
-
retainedChildren: [{ runId: "child-review", parentRunId: "workflow-1", agent: "reviewer", taskSummary: "Review", completedAt: 1, sessionPath: "/tmp/child.jsonl" }],
|
|
50
|
+
retainedChildren: [{ runId: "child-review", parentRunId: "workflow-1", state: "complete", agent: "reviewer", taskSummary: "Review", completedAt: 1, sessionPath: "/tmp/child.jsonl", resumability: { state: "resumable", sessionPath: "/tmp/child.jsonl" } }],
|
|
50
51
|
turnId: 3,
|
|
51
52
|
});
|
|
52
53
|
assert.equal(resumed.length, 1);
|
|
@@ -85,8 +86,8 @@ describe("goal mission continuation", () => {
|
|
|
85
86
|
location: test.location,
|
|
86
87
|
ownerSessionId: "session-1",
|
|
87
88
|
retainedChildren: [
|
|
88
|
-
{ runId: "child-old", parentRunId: "workflow-old", agent: "reviewer", taskSummary: "Old", completedAt: 2, sessionPath: "/tmp/old.jsonl" },
|
|
89
|
-
{ runId: "child-new", parentRunId: "workflow-new", agent: "reviewer", taskSummary: "New", completedAt: 1, sessionPath: "/tmp/new.jsonl" },
|
|
89
|
+
{ runId: "child-old", parentRunId: "workflow-old", state: "complete", agent: "reviewer", taskSummary: "Old", completedAt: 2, sessionPath: "/tmp/old.jsonl", resumability: { state: "resumable", sessionPath: "/tmp/old.jsonl" } },
|
|
90
|
+
{ runId: "child-new", parentRunId: "workflow-new", state: "complete", agent: "reviewer", taskSummary: "New", completedAt: 1, sessionPath: "/tmp/new.jsonl", resumability: { state: "resumable", sessionPath: "/tmp/new.jsonl" } },
|
|
90
91
|
],
|
|
91
92
|
turnId: 1,
|
|
92
93
|
});
|
|
@@ -99,6 +100,69 @@ describe("goal mission continuation", () => {
|
|
|
99
100
|
}
|
|
100
101
|
});
|
|
101
102
|
|
|
103
|
+
it("resumes a mission-linked child older than ten unrelated children", () => {
|
|
104
|
+
const test = fixture();
|
|
105
|
+
try {
|
|
106
|
+
const mission = createMission(test.location, {
|
|
107
|
+
title: "Continue linked work",
|
|
108
|
+
objective: "Resume the linked worker",
|
|
109
|
+
goal: true,
|
|
110
|
+
budget: { tokens: 100 },
|
|
111
|
+
status: "active",
|
|
112
|
+
ownerSessionId: "session-1",
|
|
113
|
+
});
|
|
114
|
+
updateMission(test.location, mission.id, { addRuns: [{ runId: "workflow-mission", mode: "workflow", status: "complete" }] });
|
|
115
|
+
const retainedChildren: RetainedChild[] = [
|
|
116
|
+
...Array.from({ length: 10 }, (_, offset) => {
|
|
117
|
+
const index = 10 - offset;
|
|
118
|
+
return { runId: `child-${index}`, parentRunId: `workflow-unrelated-${index}`, state: "complete" as const, agent: "worker", taskSummary: "Unrelated", completedAt: index, resumability: { state: "resumable" as const, sessionPath: `/tmp/child-${index}.jsonl` } };
|
|
119
|
+
}),
|
|
120
|
+
{ runId: "child-0", parentRunId: "workflow-mission", state: "complete", agent: "worker", taskSummary: "Linked", completedAt: 0, resumability: { state: "resumable", sessionPath: "/tmp/child-0.jsonl" } },
|
|
121
|
+
];
|
|
122
|
+
const notices = collectGoalContinuationNotices({
|
|
123
|
+
location: test.location,
|
|
124
|
+
ownerSessionId: "session-1",
|
|
125
|
+
retainedChildren,
|
|
126
|
+
turnId: 1,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
assert.equal(retainedChildren.length, 11);
|
|
130
|
+
assert.equal(notices.length, 1);
|
|
131
|
+
assert.match(notices[0]!.message, /Resume retained child child-0 \(worker\)/);
|
|
132
|
+
assert.doesNotMatch(notices[0]!.message, /child-10/);
|
|
133
|
+
} finally {
|
|
134
|
+
fs.rmSync(test.root, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("does not resume non-resumable retained children from goal notices", () => {
|
|
139
|
+
const test = fixture();
|
|
140
|
+
try {
|
|
141
|
+
const mission = createMission(test.location, {
|
|
142
|
+
title: "Ship auth refresh",
|
|
143
|
+
objective: "Implement and validate token refresh",
|
|
144
|
+
goal: true,
|
|
145
|
+
budget: { tokens: 100 },
|
|
146
|
+
status: "active",
|
|
147
|
+
ownerSessionId: "session-1",
|
|
148
|
+
});
|
|
149
|
+
updateMission(test.location, mission.id, { addRuns: [{ runId: "workflow-stopped", mode: "workflow", status: "complete" }] });
|
|
150
|
+
|
|
151
|
+
const notices = collectGoalContinuationNotices({
|
|
152
|
+
location: test.location,
|
|
153
|
+
ownerSessionId: "session-1",
|
|
154
|
+
retainedChildren: [{ runId: "child-stopped", parentRunId: "workflow-stopped", state: "stopped", agent: "worker", taskSummary: "Stopped", completedAt: 1, resumability: { state: "not-resumable", reason: "stopped run" } }],
|
|
155
|
+
turnId: 1,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
assert.equal(notices.length, 1);
|
|
159
|
+
assert.doesNotMatch(notices[0]!.message, /Resume retained child/);
|
|
160
|
+
assert.match(notices[0]!.message, /Next ready action: Continue objective: Implement and validate token refresh/);
|
|
161
|
+
} finally {
|
|
162
|
+
fs.rmSync(test.root, { recursive: true, force: true });
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
102
166
|
it("supports pause, resume, and opt-out through mission.update", () => {
|
|
103
167
|
const test = fixture();
|
|
104
168
|
try {
|
|
@@ -17,7 +17,7 @@ function projectFixture() {
|
|
|
17
17
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-mission-lifecycle-"));
|
|
18
18
|
const projectRoot = path.join(root, "project");
|
|
19
19
|
fs.mkdirSync(projectRoot, { recursive: true });
|
|
20
|
-
return { root, projectRoot, missionConfig: { globalIndexDir: path.join(root, "global-index") } };
|
|
20
|
+
return { root, projectRoot, missionConfig: { directory: ".pi-subagents/missions", globalIndexDir: path.join(root, "global-index") } };
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
describe("mission launch lifecycle", () => {
|
|
@@ -34,6 +34,20 @@ async function waitForFile(filePath: string, timeoutMs = 10_000): Promise<void>
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
describe("mission store", () => {
|
|
37
|
+
it("stores default missions outside the project and preserves configured project storage", () => {
|
|
38
|
+
const test = fixture();
|
|
39
|
+
try {
|
|
40
|
+
assert.equal(path.relative(test.projectRoot, test.location.missionDir).startsWith(".."), true);
|
|
41
|
+
assert.equal(test.location.missionDir.startsWith(path.join(test.agentDir, "missions", "projects")), true);
|
|
42
|
+
assert.equal(
|
|
43
|
+
resolveMissionStoreLocation({ projectRoot: test.projectRoot, agentDir: test.agentDir, config: { directory: ".pi/subagents/missions" } }).missionDir,
|
|
44
|
+
path.join(test.projectRoot, ".pi/subagents", "missions"),
|
|
45
|
+
);
|
|
46
|
+
} finally {
|
|
47
|
+
fs.rmSync(test.root, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
37
51
|
it("creates, reads, updates, lists, and globally indexes project missions", () => {
|
|
38
52
|
const test = fixture();
|
|
39
53
|
try {
|
|
@@ -68,6 +68,13 @@ describe("model fallback helpers", () => {
|
|
|
68
68
|
);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
+
it("rejects fallback models that the active registry cannot resolve", () => {
|
|
72
|
+
assert.throws(
|
|
73
|
+
() => buildModelCandidates("gpt-5-mini", ["does-not-exist"], availableModels),
|
|
74
|
+
/Unknown subagent model 'does-not-exist'/,
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
71
78
|
it("detects retryable provider/model failures", () => {
|
|
72
79
|
assert.equal(isRetryableModelFailure("rate limit exceeded for provider"), true);
|
|
73
80
|
assert.equal(isRetryableModelFailure("model unavailable"), true);
|
|
@@ -161,6 +168,17 @@ describe("resolveSubagentModelOverride (cross-session inherit, issue #266)", ()
|
|
|
161
168
|
);
|
|
162
169
|
});
|
|
163
170
|
|
|
171
|
+
it("rejects explicit models that the active registry cannot resolve", () => {
|
|
172
|
+
assert.throws(
|
|
173
|
+
() => resolveSubagentModelOverride("does-not-exist", parentModel, availableModels),
|
|
174
|
+
/Unknown subagent model 'does-not-exist'/,
|
|
175
|
+
);
|
|
176
|
+
assert.throws(
|
|
177
|
+
() => resolveSubagentModelOverride("does-not-exist:high", parentModel, availableModels),
|
|
178
|
+
/Unknown subagent model 'does-not-exist:high'/,
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
164
182
|
it("returns undefined when inheriting but no parent model is known", () => {
|
|
165
183
|
// No parent session model available: fall back to the prior behavior of
|
|
166
184
|
// emitting no override rather than inventing an invalid one.
|
|
@@ -150,7 +150,7 @@ describe("native supervisor channel", () => {
|
|
|
150
150
|
channel.start();
|
|
151
151
|
channel.dispose();
|
|
152
152
|
|
|
153
|
-
assert.deepEqual(registeredTools, [NATIVE_SUPERVISOR_TOOL_NAME
|
|
153
|
+
assert.deepEqual(registeredTools, [NATIVE_SUPERVISOR_TOOL_NAME]);
|
|
154
154
|
assert.deepEqual(sent.map(({ message }) => message.details?.id), [matchingId]);
|
|
155
155
|
assert.deepEqual(sent[0]?.options, { triggerTurn: true });
|
|
156
156
|
assert.equal(channel.pending.has(matchingId), false, "disposed channel clears pending requests");
|
|
@@ -553,7 +553,7 @@ describe("native supervisor channel", () => {
|
|
|
553
553
|
registeredTools.set(tool.name, tool);
|
|
554
554
|
},
|
|
555
555
|
};
|
|
556
|
-
registerNativeSupervisorClient(pi as never
|
|
556
|
+
registerNativeSupervisorClient(pi as never);
|
|
557
557
|
const controller = new AbortController();
|
|
558
558
|
controller.abort();
|
|
559
559
|
|
|
@@ -39,9 +39,23 @@ const savedEnv = {
|
|
|
39
39
|
[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV]: process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV],
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
async function removeDirWithRetry(dir: string): Promise<void> {
|
|
43
|
+
let lastError: unknown;
|
|
44
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
45
|
+
try {
|
|
46
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
47
|
+
return;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
lastError = error;
|
|
50
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw lastError;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
afterEach(async () => {
|
|
43
57
|
for (const route of routes.splice(0)) {
|
|
44
|
-
|
|
58
|
+
await removeDirWithRetry(path.dirname(route.eventSink));
|
|
45
59
|
}
|
|
46
60
|
for (const [key, value] of Object.entries(savedEnv)) {
|
|
47
61
|
if (value === undefined) delete process.env[key];
|
|
@@ -97,6 +111,15 @@ describe("nested route index", () => {
|
|
|
97
111
|
const tokens = new Set([first.capabilityToken, second.capabilityToken]);
|
|
98
112
|
assert.ok(tokens.has(indexed.capabilityToken), "indexed route must be one of the two created routes");
|
|
99
113
|
});
|
|
114
|
+
|
|
115
|
+
it("recovers a route from its index when the route file is missing", () => {
|
|
116
|
+
const route = trackRoute("late-route-root");
|
|
117
|
+
const routeFile = path.join(path.dirname(route.eventSink), "route.json");
|
|
118
|
+
fs.rmSync(routeFile);
|
|
119
|
+
|
|
120
|
+
assert.equal(buildNestedRouteIndex().get("late-route-root")?.capabilityToken, route.capabilityToken);
|
|
121
|
+
assert.equal(JSON.parse(fs.readFileSync(routeFile, "utf-8")).capabilityToken, route.capabilityToken);
|
|
122
|
+
});
|
|
100
123
|
});
|
|
101
124
|
|
|
102
125
|
describe("nested event route validation", () => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { describe, it } from "node:test";
|
|
6
|
+
import { resolveNodeExecutable } from "../../src/shared/node-executable.ts";
|
|
7
|
+
|
|
8
|
+
describe("Node executable resolution", () => {
|
|
9
|
+
it("uses an executable Node path", () => {
|
|
10
|
+
assert.equal(resolveNodeExecutable(process.execPath), process.execPath);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("falls back to PATH node for a standalone Pi executable", () => {
|
|
14
|
+
assert.equal(resolveNodeExecutable(path.join(os.tmpdir(), process.platform === "win32" ? "pi.exe" : "pi")), process.platform === "win32" ? "node.exe" : "node");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("falls back to PATH node for a stale Node path", () => {
|
|
18
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-node-executable-"));
|
|
19
|
+
try {
|
|
20
|
+
const staleNode = path.join(root, process.platform === "win32" ? "node.exe" : "node");
|
|
21
|
+
assert.equal(resolveNodeExecutable(staleNode), process.platform === "win32" ? "node.exe" : "node");
|
|
22
|
+
} finally {
|
|
23
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -134,6 +134,14 @@ describe("registerSubagentNotify", () => {
|
|
|
134
134
|
});
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
+
it("does not attach async status snapshots to subagent-notify details", async () => {
|
|
138
|
+
const { notifier, sent } = createPi("session-a");
|
|
139
|
+
assert.equal(await notifier.deliver(completionResult({ id: "direct-no-snapshot" })), true);
|
|
140
|
+
const message = sent[0]?.message as { customType?: string; details?: Record<string, unknown> } | undefined;
|
|
141
|
+
assert.equal(message?.customType, "subagent-notify");
|
|
142
|
+
assert.equal(message?.details?.asyncSnapshot, undefined);
|
|
143
|
+
});
|
|
144
|
+
|
|
137
145
|
it("acknowledges direct delivery only after sendMessage accepts it", async () => {
|
|
138
146
|
const { notifier, sent } = createPi("session-a");
|
|
139
147
|
assert.equal(await notifier.deliver(completionResult({ id: "direct-accepted" })), true);
|