@selesai/code 0.5.19 → 0.5.21
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/dist/extensions/handoff-new.test.ts +8 -9
- package/dist/extensions/handoff-new.ts +6 -8
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +31 -0
- package/dist/extensions/pi-intercom/broker/spawn.ts +5 -3
- package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
- package/dist/extensions/pi-subagents/README.md +602 -220
- package/dist/extensions/pi-subagents/banner.png +0 -0
- package/dist/extensions/pi-subagents/index.ts +1 -0
- package/dist/extensions/pi-subagents/install.mjs +3 -3
- package/dist/extensions/pi-subagents/package-lock.json +3405 -0
- package/dist/extensions/pi-subagents/package.json +26 -14
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
- package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
- package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
- package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
- package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
- package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
- package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
- package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
- package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
- package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
- package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
- package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
- package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
- package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
- package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
- package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
- package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
- package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
- package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
- package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
- package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
- package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
- package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
- package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
- package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
- package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
- package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
- package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
- package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
- package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
- package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
- package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
- package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
- package/dist/extensions/question/batch.ts +39 -0
- package/dist/extensions/question/dialog-adapter.ts +23 -1
- package/dist/extensions/question/index.ts +237 -7
- package/dist/extensions/question/schemas.ts +28 -15
- package/dist/extensions/question/tests/batch.test.ts +26 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
- package/dist/extensions/question/tui-adapter.ts +2 -3
- package/dist/extensions/question/types.ts +37 -0
- package/dist/extensions/question/ui-protocol.ts +3 -3
- package/dist/skills/batch-grill-me/SKILL.md +3 -1
- package/package.json +2 -1
|
@@ -63,6 +63,33 @@ describe("slash live state", { skip: !available ? "slash-live-state.ts not impor
|
|
|
63
63
|
assert.equal(snapshot.version > 0, true);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
+
it("does not assign a parallel child update to another chain placeholder", () => {
|
|
67
|
+
clearSlashSnapshots!();
|
|
68
|
+
const details = buildSlashInitialResult!("req-parallel", {
|
|
69
|
+
chain: [{ parallel: [{ agent: "scout", task: "map" }, { agent: "context-builder", task: "analyze" }] }],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
applySlashUpdate!("req-parallel", {
|
|
73
|
+
requestId: "req-parallel",
|
|
74
|
+
progress: [{
|
|
75
|
+
index: 1,
|
|
76
|
+
agent: "context-builder",
|
|
77
|
+
status: "running",
|
|
78
|
+
task: "analyze",
|
|
79
|
+
currentTool: "find",
|
|
80
|
+
recentTools: [],
|
|
81
|
+
recentOutput: [],
|
|
82
|
+
toolCount: 38,
|
|
83
|
+
tokens: 1_000,
|
|
84
|
+
durationMs: 1_000,
|
|
85
|
+
}],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const results = getSlashRenderableSnapshot!(details).result.details.results;
|
|
89
|
+
assert.equal(results[0]?.progress?.currentTool, undefined);
|
|
90
|
+
assert.equal(results[1]?.progress?.currentTool, "find");
|
|
91
|
+
});
|
|
92
|
+
|
|
66
93
|
it("creates stable placeholders for a 40-step worker/reviewer chain", () => {
|
|
67
94
|
clearSlashSnapshots!();
|
|
68
95
|
const chain = Array.from({ length: 40 }, (_, index) => ({
|
|
@@ -3,7 +3,7 @@ import { describe, it } from "node:test";
|
|
|
3
3
|
import { tryImport } from "../support/helpers.ts";
|
|
4
4
|
|
|
5
5
|
interface TopLevelAsyncModule {
|
|
6
|
-
applyForceTopLevelAsyncOverride<T extends { async?: boolean; clarify?: boolean }>(
|
|
6
|
+
applyForceTopLevelAsyncOverride<T extends { async?: boolean; clarify?: boolean; foregroundOnly?: boolean }>(
|
|
7
7
|
params: T,
|
|
8
8
|
depth: number,
|
|
9
9
|
forceTopLevelAsync: boolean,
|
|
@@ -23,6 +23,12 @@ describe("force top-level async helper", { skip: !available ? "pi packages not a
|
|
|
23
23
|
assert.equal(next.agent, "worker");
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
it("keeps bridge-owned foreground calls attached", () => {
|
|
27
|
+
const params = { async: false, clarify: false, foregroundOnly: true };
|
|
28
|
+
const next = mod!.applyForceTopLevelAsyncOverride(params, 0, true);
|
|
29
|
+
assert.equal(next, params);
|
|
30
|
+
});
|
|
31
|
+
|
|
26
32
|
it("leaves nested calls unchanged", () => {
|
|
27
33
|
const params = { async: false, clarify: true };
|
|
28
34
|
const next = mod!.applyForceTopLevelAsyncOverride(params, 1, true);
|
|
@@ -51,6 +51,8 @@ interface AgentConfig {
|
|
|
51
51
|
extensions?: string[];
|
|
52
52
|
subagentOnlyExtensions?: string[];
|
|
53
53
|
skills?: string[];
|
|
54
|
+
skillPath?: string[];
|
|
55
|
+
filePath?: string;
|
|
54
56
|
thinking?: string;
|
|
55
57
|
systemPromptMode?: string;
|
|
56
58
|
inheritProjectContext?: boolean;
|
|
@@ -59,6 +61,7 @@ interface AgentConfig {
|
|
|
59
61
|
output?: string | false;
|
|
60
62
|
reads?: string[] | false;
|
|
61
63
|
progress?: boolean;
|
|
64
|
+
toolBudget?: { soft?: number; hard: number; block?: string[] | "*" };
|
|
62
65
|
mcpDirectTools?: string[];
|
|
63
66
|
maxSubagentDepth?: number;
|
|
64
67
|
completionGuard?: boolean;
|
|
@@ -153,6 +156,8 @@ export async function tryImport<T>(specifier: string): Promise<T | null> {
|
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
|
|
159
|
+
let writeCallSeq = 0;
|
|
160
|
+
|
|
156
161
|
export const events = {
|
|
157
162
|
assistantMessage(text: string, model = "mock/test-model"): object {
|
|
158
163
|
return {
|
|
@@ -167,6 +172,33 @@ export const events = {
|
|
|
167
172
|
};
|
|
168
173
|
},
|
|
169
174
|
|
|
175
|
+
/** Assistant write tool call plus its successful tool result, as one completed write. */
|
|
176
|
+
completedWrite(filePath: string, content: string, model = "mock/test-model"): object[] {
|
|
177
|
+
const id = `write-${++writeCallSeq}`;
|
|
178
|
+
return [
|
|
179
|
+
{
|
|
180
|
+
type: "message_end",
|
|
181
|
+
message: {
|
|
182
|
+
role: "assistant",
|
|
183
|
+
content: [{ type: "toolCall", id, name: "write", arguments: { path: filePath, content } }],
|
|
184
|
+
model,
|
|
185
|
+
stopReason: "toolUse",
|
|
186
|
+
usage: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0, cost: { total: 0.001 } },
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: "tool_result_end",
|
|
191
|
+
message: {
|
|
192
|
+
role: "toolResult",
|
|
193
|
+
toolCallId: id,
|
|
194
|
+
toolName: "write",
|
|
195
|
+
isError: false,
|
|
196
|
+
content: [{ type: "text", text: `Wrote ${filePath}` }],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
},
|
|
201
|
+
|
|
170
202
|
toolStart(toolName: string, args: Record<string, unknown> = {}): object {
|
|
171
203
|
return { type: "tool_execution_start", toolName, args };
|
|
172
204
|
},
|
|
@@ -167,6 +167,40 @@ function defaultResponse() {
|
|
|
167
167
|
return { output: "ok", exitCode: 0 };
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
function writeDeclaredFiles(response) {
|
|
171
|
+
if (!Array.isArray(response.writeFiles)) return;
|
|
172
|
+
for (const file of response.writeFiles) {
|
|
173
|
+
if (!file || typeof file.path !== "string" || typeof file.content !== "string") continue;
|
|
174
|
+
const target = path.resolve(process.cwd(), file.path);
|
|
175
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
176
|
+
fs.writeFileSync(target, file.content, "utf-8");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function writeStructuredOutputCapture(response) {
|
|
181
|
+
if (!Object.prototype.hasOwnProperty.call(response, "structuredOutputCapture")) return;
|
|
182
|
+
const outputPath = process.env.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE;
|
|
183
|
+
if (!outputPath) return;
|
|
184
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
185
|
+
fs.writeFileSync(outputPath, JSON.stringify(response.structuredOutputCapture), "utf-8");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function writeToolDiagnostic(response) {
|
|
189
|
+
if (!Array.isArray(response.missingTools) || response.missingTools.length === 0) return;
|
|
190
|
+
const diagnosticPath = process.env.SELESAI_SUBAGENT_TOOL_DIAGNOSTIC_PATH;
|
|
191
|
+
const required = JSON.parse(process.env.SELESAI_SUBAGENT_REQUIRED_TOOLS ?? "[]");
|
|
192
|
+
if (!diagnosticPath || !Array.isArray(required)) return;
|
|
193
|
+
const missing = response.missingTools.filter((name) => typeof name === "string" && required.includes(name));
|
|
194
|
+
const available = required.filter((name) => !missing.includes(name));
|
|
195
|
+
fs.mkdirSync(path.dirname(diagnosticPath), { recursive: true });
|
|
196
|
+
fs.writeFileSync(diagnosticPath, JSON.stringify({
|
|
197
|
+
agent: process.env.SELESAI_SUBAGENT_CHILD_AGENT,
|
|
198
|
+
required,
|
|
199
|
+
available,
|
|
200
|
+
missing,
|
|
201
|
+
}), "utf-8");
|
|
202
|
+
}
|
|
203
|
+
|
|
170
204
|
function isJsonMode(args) {
|
|
171
205
|
for (let i = 0; i < args.length; i++) {
|
|
172
206
|
if (args[i] === "--mode") {
|
|
@@ -220,6 +254,15 @@ async function writeJsonlLine(entry) {
|
|
|
220
254
|
await writeStdout(`${line}\n`);
|
|
221
255
|
}
|
|
222
256
|
|
|
257
|
+
async function writeRawStdout(entry) {
|
|
258
|
+
if (Array.isArray(entry?.stdoutBase64Chunks)) {
|
|
259
|
+
for (const chunk of entry.stdoutBase64Chunks) {
|
|
260
|
+
if (typeof chunk === "string") await writeStdout(Buffer.from(chunk, "base64"));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (typeof entry?.stdoutRaw === "string") await writeStdout(entry.stdoutRaw);
|
|
264
|
+
}
|
|
265
|
+
|
|
223
266
|
function extractPlainText(entry) {
|
|
224
267
|
if (!entry || typeof entry !== "object") return "";
|
|
225
268
|
if (entry.type === "message_end") {
|
|
@@ -283,6 +326,7 @@ async function main() {
|
|
|
283
326
|
process.on("SIGTERM", () => {});
|
|
284
327
|
}
|
|
285
328
|
writeSessionFile(args);
|
|
329
|
+
writeToolDiagnostic(response);
|
|
286
330
|
fs.writeFileSync(
|
|
287
331
|
path.join(queueDir, `call-${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2)}.json`),
|
|
288
332
|
JSON.stringify({ args, systemPrompts: readSystemPromptRecords(args) }),
|
|
@@ -292,6 +336,16 @@ async function main() {
|
|
|
292
336
|
if (typeof response.delay === "number" && response.delay > 0) {
|
|
293
337
|
await new Promise((resolve) => setTimeout(resolve, response.delay));
|
|
294
338
|
}
|
|
339
|
+
if (typeof response.waitForPath === "string") {
|
|
340
|
+
const deadline = Date.now() + 30_000;
|
|
341
|
+
while (!fs.existsSync(response.waitForPath)) {
|
|
342
|
+
if (Date.now() >= deadline) fail(`Timed out waiting for mock release path: ${response.waitForPath}`);
|
|
343
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
writeDeclaredFiles(response);
|
|
348
|
+
writeStructuredOutputCapture(response);
|
|
295
349
|
|
|
296
350
|
if (Array.isArray(response.steps) && response.steps.length > 0) {
|
|
297
351
|
for (const step of response.steps) {
|
|
@@ -299,14 +353,17 @@ async function main() {
|
|
|
299
353
|
await new Promise((resolve) => setTimeout(resolve, step.delay));
|
|
300
354
|
}
|
|
301
355
|
if (Array.isArray(step?.jsonl) && step.jsonl.length > 0) {
|
|
302
|
-
|
|
356
|
+
await writeResponseEntries(step.jsonl, jsonMode, args);
|
|
303
357
|
}
|
|
358
|
+
await writeRawStdout(step);
|
|
304
359
|
if (typeof step?.stderr === "string" && step.stderr.length > 0) {
|
|
305
360
|
process.stderr.write(step.stderr);
|
|
306
361
|
}
|
|
307
362
|
}
|
|
308
363
|
} else if (Array.isArray(response.jsonl) && response.jsonl.length > 0) {
|
|
309
|
-
|
|
364
|
+
await writeResponseEntries(response.jsonl, jsonMode, args);
|
|
365
|
+
} else if (Array.isArray(response.stdoutBase64Chunks) || typeof response.stdoutRaw === "string") {
|
|
366
|
+
await writeRawStdout(response);
|
|
310
367
|
} else if (Array.isArray(response.echoEnv) && response.echoEnv.length > 0) {
|
|
311
368
|
const envSnapshot = Object.fromEntries(response.echoEnv.map((key) => [key, process.env[key] ?? null]));
|
|
312
369
|
const output = withAcceptanceReport(JSON.stringify(envSnapshot), args);
|
|
@@ -8,16 +8,26 @@ interface MockPiResponse {
|
|
|
8
8
|
stderr?: string;
|
|
9
9
|
exitCode?: number;
|
|
10
10
|
delay?: number;
|
|
11
|
+
waitForPath?: string;
|
|
11
12
|
keepAliveAfterFinalMessageMs?: number;
|
|
12
13
|
ignoreSigterm?: boolean;
|
|
13
14
|
jsonl?: unknown[];
|
|
15
|
+
stdoutRaw?: string;
|
|
16
|
+
stdoutBase64Chunks?: string[];
|
|
14
17
|
steps?: Array<{
|
|
15
18
|
delay?: number;
|
|
16
19
|
jsonl?: unknown[];
|
|
20
|
+
stdoutRaw?: string;
|
|
21
|
+
stdoutBase64Chunks?: string[];
|
|
17
22
|
stderr?: string;
|
|
18
23
|
}>;
|
|
19
24
|
echoEnv?: string[];
|
|
25
|
+
missingTools?: string[];
|
|
20
26
|
matchArgIncludes?: string | string[];
|
|
27
|
+
/** Files the mock child writes to disk before emitting output, standing in for its write-tool side effects. */
|
|
28
|
+
writeFiles?: Array<{ path: string; content: string }>;
|
|
29
|
+
/** Writes the structured-output capture file without emitting a structured_output tool event. */
|
|
30
|
+
structuredOutputCapture?: unknown;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
export interface MockPi {
|
|
@@ -58,17 +68,27 @@ export function createMockPi(): MockPi {
|
|
|
58
68
|
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-mock-cli-"));
|
|
59
69
|
const queueDir = path.join(rootDir, "queue");
|
|
60
70
|
const binDir = path.join(rootDir, "bin");
|
|
71
|
+
const piPackageDir = path.join(rootDir, "pi-package");
|
|
72
|
+
const cliScriptPath = path.join(piPackageDir, "dist", "cli.mjs");
|
|
61
73
|
ensureDir(queueDir);
|
|
62
74
|
ensureDir(binDir);
|
|
75
|
+
ensureDir(path.dirname(cliScriptPath));
|
|
76
|
+
fs.copyFileSync(SCRIPT_PATH, cliScriptPath);
|
|
77
|
+
fs.writeFileSync(
|
|
78
|
+
path.join(piPackageDir, "package.json"),
|
|
79
|
+
JSON.stringify({ name: "@selesai/code" }),
|
|
80
|
+
"utf-8",
|
|
81
|
+
);
|
|
63
82
|
|
|
64
83
|
const shellScriptPath = path.join(binDir, "pi");
|
|
65
84
|
const cmdScriptPath = path.join(binDir, "pi.cmd");
|
|
66
|
-
writeExecutable(shellScriptPath, `#!/bin/sh\nexec "${process.execPath}" "${
|
|
67
|
-
writeExecutable(cmdScriptPath, `@echo off\r\n"${process.execPath}" "${
|
|
85
|
+
writeExecutable(shellScriptPath, `#!/bin/sh\nexec "${process.execPath}" "${cliScriptPath}" "$@"\n`);
|
|
86
|
+
writeExecutable(cmdScriptPath, `@echo off\r\n"${process.execPath}" "${cliScriptPath}" %*\r\n`);
|
|
68
87
|
|
|
69
88
|
let installed = false;
|
|
70
89
|
let nextSequence = 0;
|
|
71
90
|
let originalPath: string | undefined;
|
|
91
|
+
let originalPiBinary: string | undefined;
|
|
72
92
|
let originalArgv1: string | undefined;
|
|
73
93
|
let originalQueueEnv: string | undefined;
|
|
74
94
|
|
|
@@ -80,25 +100,31 @@ export function createMockPi(): MockPi {
|
|
|
80
100
|
if (installed) return;
|
|
81
101
|
installed = true;
|
|
82
102
|
originalPath = process.env.PATH;
|
|
103
|
+
originalPiBinary = process.env.SELESAI_SUBAGENT_PI_BINARY;
|
|
83
104
|
originalQueueEnv = process.env.MOCK_PI_QUEUE_DIR;
|
|
84
105
|
process.env.PATH = `${binDir}${path.delimiter}${originalPath ?? ""}`;
|
|
85
|
-
process.env.MOCK_PI_QUEUE_DIR = queueDir;
|
|
86
106
|
if (process.platform === "win32") {
|
|
107
|
+
delete process.env.SELESAI_SUBAGENT_PI_BINARY;
|
|
87
108
|
originalArgv1 = process.argv[1];
|
|
88
|
-
process.argv[1] =
|
|
109
|
+
process.argv[1] = cliScriptPath;
|
|
110
|
+
} else {
|
|
111
|
+
process.env.SELESAI_SUBAGENT_PI_BINARY = shellScriptPath;
|
|
89
112
|
}
|
|
113
|
+
process.env.MOCK_PI_QUEUE_DIR = queueDir;
|
|
90
114
|
},
|
|
91
115
|
uninstall() {
|
|
92
116
|
if (!installed) return;
|
|
93
117
|
installed = false;
|
|
94
118
|
if (originalPath === undefined) delete process.env.PATH;
|
|
95
119
|
else process.env.PATH = originalPath;
|
|
96
|
-
if (
|
|
97
|
-
else process.env.
|
|
120
|
+
if (originalPiBinary === undefined) delete process.env.SELESAI_SUBAGENT_PI_BINARY;
|
|
121
|
+
else process.env.SELESAI_SUBAGENT_PI_BINARY = originalPiBinary;
|
|
98
122
|
if (process.platform === "win32") {
|
|
99
123
|
if (originalArgv1 === undefined) delete process.argv[1];
|
|
100
124
|
else process.argv[1] = originalArgv1;
|
|
101
125
|
}
|
|
126
|
+
if (originalQueueEnv === undefined) delete process.env.MOCK_PI_QUEUE_DIR;
|
|
127
|
+
else process.env.MOCK_PI_QUEUE_DIR = originalQueueEnv;
|
|
102
128
|
try {
|
|
103
129
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
104
130
|
} catch {}
|
|
@@ -3,14 +3,11 @@ import { mkdtempSync, rmSync } from "node:fs";
|
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
fauxAssistantMessage,
|
|
8
|
-
fauxText,
|
|
9
|
-
registerFauxProvider,
|
|
10
|
-
} from "@earendil-works/pi-ai";
|
|
6
|
+
import { fauxAssistantMessage, fauxProvider, fauxText, fauxToolCall } from "@earendil-works/pi-ai/providers/faux";
|
|
11
7
|
import {
|
|
12
8
|
createAgentSession,
|
|
13
9
|
DefaultResourceLoader,
|
|
10
|
+
ModelRuntime,
|
|
14
11
|
SessionManager,
|
|
15
12
|
SettingsManager,
|
|
16
13
|
} from "@selesai/code";
|
|
@@ -85,19 +82,6 @@ function parseArgs(argv) {
|
|
|
85
82
|
return parsed;
|
|
86
83
|
}
|
|
87
84
|
|
|
88
|
-
function createModelRegistry(model) {
|
|
89
|
-
return {
|
|
90
|
-
find: (provider, id) => provider === model.provider && id === model.id ? model : undefined,
|
|
91
|
-
getAll: () => [model],
|
|
92
|
-
getAvailable: () => [model],
|
|
93
|
-
hasConfiguredAuth: () => true,
|
|
94
|
-
isUsingOAuth: () => false,
|
|
95
|
-
getApiKeyAndHeaders: async () => ({ ok: true, apiKey: "faux", headers: {} }),
|
|
96
|
-
registerProvider: () => {},
|
|
97
|
-
unregisterProvider: () => {},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
85
|
function createSessionManager(parsed, cwd) {
|
|
102
86
|
if (parsed.sessionFile) {
|
|
103
87
|
const sessionDir = parsed.sessionDir ?? path.dirname(parsed.sessionFile);
|
|
@@ -112,19 +96,36 @@ function createSessionManager(parsed, cwd) {
|
|
|
112
96
|
async function main() {
|
|
113
97
|
const parsed = parseArgs(process.argv.slice(2));
|
|
114
98
|
const responseText = process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT ?? "CHILD_REAL_SESSION_OK";
|
|
99
|
+
const reportChildTools = process.env.SELESAI_SUBAGENTS_E2E_REPORT_CHILD_TOOLS === "1";
|
|
115
100
|
const cwd = process.cwd();
|
|
116
101
|
const ownedAgentDir = process.env.SELESAI_CODING_AGENT_DIR
|
|
117
102
|
? undefined
|
|
118
103
|
: mkdtempSync(path.join(os.tmpdir(), "pi-e2e-agent-dir-"));
|
|
119
104
|
const agentDir = process.env.SELESAI_CODING_AGENT_DIR ?? ownedAgentDir;
|
|
120
105
|
|
|
121
|
-
const faux =
|
|
106
|
+
const faux = fauxProvider({
|
|
122
107
|
provider: "faux-e2e-child",
|
|
123
108
|
models: [{ id: "child", contextWindow: 200_000 }],
|
|
124
109
|
});
|
|
125
|
-
const
|
|
110
|
+
const modelRuntime = await ModelRuntime.create({
|
|
111
|
+
authPath: path.join(agentDir, "auth.json"),
|
|
112
|
+
modelsPath: null,
|
|
113
|
+
allowModelNetwork: false,
|
|
114
|
+
});
|
|
115
|
+
modelRuntime.registerProvider(faux.provider.id, {
|
|
116
|
+
name: faux.provider.name,
|
|
117
|
+
api: faux.api,
|
|
118
|
+
apiKey: "faux",
|
|
119
|
+
streamSimple: faux.provider.streamSimple,
|
|
120
|
+
models: [...faux.models],
|
|
121
|
+
});
|
|
122
|
+
const model = modelRuntime.getModel(faux.provider.id, "child");
|
|
123
|
+
if (!model) throw new Error("faux child model was not registered");
|
|
124
|
+
let session;
|
|
126
125
|
faux.setResponses([
|
|
127
|
-
() =>
|
|
126
|
+
() => process.env.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE
|
|
127
|
+
? fauxAssistantMessage(fauxToolCall("structured_output", { value: { marker: "STRUCTURED_OUTPUT_OK" } }), { stopReason: "toolUse" })
|
|
128
|
+
: fauxAssistantMessage(fauxText(reportChildTools ? `ACTIVE_TOOLS:${session?.getActiveToolNames().sort().join(",") ?? ""}` : responseText), { stopReason: "stop" }),
|
|
128
129
|
]);
|
|
129
130
|
|
|
130
131
|
const settingsManager = SettingsManager.inMemory({
|
|
@@ -146,16 +147,17 @@ async function main() {
|
|
|
146
147
|
|
|
147
148
|
try {
|
|
148
149
|
await loader.reload();
|
|
149
|
-
const
|
|
150
|
+
const created = await createAgentSession({
|
|
150
151
|
cwd,
|
|
151
152
|
agentDir,
|
|
152
153
|
model,
|
|
153
|
-
|
|
154
|
+
modelRuntime,
|
|
154
155
|
resourceLoader: loader,
|
|
155
156
|
sessionManager: createSessionManager(parsed, cwd),
|
|
156
157
|
settingsManager,
|
|
157
158
|
tools: parsed.tools,
|
|
158
159
|
});
|
|
160
|
+
session = created.session;
|
|
159
161
|
|
|
160
162
|
session.subscribe((event) => {
|
|
161
163
|
if (
|
|
@@ -173,7 +175,6 @@ async function main() {
|
|
|
173
175
|
await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
|
|
174
176
|
session.dispose();
|
|
175
177
|
} finally {
|
|
176
|
-
faux.unregister();
|
|
177
178
|
if (ownedAgentDir) rmSync(ownedAgentDir, { recursive: true, force: true });
|
|
178
179
|
}
|
|
179
180
|
}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { type AssistantMessage, type Context, type ToolCall } from "@earendil-works/pi-ai";
|
|
5
6
|
import {
|
|
6
|
-
type AssistantMessage,
|
|
7
|
-
type Context,
|
|
8
7
|
type FauxContentBlock,
|
|
9
8
|
type FauxResponseStep,
|
|
10
9
|
fauxAssistantMessage,
|
|
11
10
|
fauxText,
|
|
12
11
|
fauxToolCall,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "@earendil-works/pi-ai";
|
|
12
|
+
fauxProvider,
|
|
13
|
+
} from "@earendil-works/pi-ai/providers/faux";
|
|
16
14
|
import {
|
|
17
15
|
type AgentSession,
|
|
18
16
|
type AgentSessionEvent,
|
|
19
|
-
type ModelRegistry,
|
|
20
17
|
createAgentSession,
|
|
18
|
+
ModelRuntime,
|
|
21
19
|
DefaultResourceLoader,
|
|
22
20
|
SessionManager,
|
|
23
21
|
SettingsManager,
|
|
@@ -34,6 +32,8 @@ export interface RealSessionRunOptions {
|
|
|
34
32
|
childText: string;
|
|
35
33
|
respond: FauxResponder;
|
|
36
34
|
timeoutMs?: number;
|
|
35
|
+
projectFiles?: Record<string, string>;
|
|
36
|
+
reportChildTools?: boolean;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export interface RealSessionRun {
|
|
@@ -98,51 +98,59 @@ function toAssistantMessage(reply: FauxReply): AssistantMessage {
|
|
|
98
98
|
return fauxAssistantMessage(content, { stopReason: hasToolCall ? "toolUse" : "stop" });
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function
|
|
102
|
-
return {
|
|
103
|
-
find: (provider: string, id: string) => provider === model.provider && id === model.id ? model : undefined,
|
|
104
|
-
getAll: () => [model],
|
|
105
|
-
getAvailable: () => [model],
|
|
106
|
-
hasConfiguredAuth: () => true,
|
|
107
|
-
isUsingOAuth: () => false,
|
|
108
|
-
getApiKeyAndHeaders: async () => ({ ok: true, apiKey: "faux", headers: {} }),
|
|
109
|
-
registerProvider: () => {},
|
|
110
|
-
unregisterProvider: () => {},
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function installChildPiShim(childText: string): () => void {
|
|
101
|
+
function installChildPiShim(childText: string, reportChildTools = false): () => void {
|
|
115
102
|
const rootDir = mkdtempSync(path.join(os.tmpdir(), "pi-real-session-cli-"));
|
|
116
103
|
const binDir = path.join(rootDir, "bin");
|
|
104
|
+
const piPackageDir = path.join(rootDir, "pi-package");
|
|
105
|
+
const childCliPath = path.join(piPackageDir, "dist", "cli.mjs");
|
|
117
106
|
const previousPath = process.env.PATH;
|
|
107
|
+
const previousPiBinary = process.env.SELESAI_SUBAGENT_PI_BINARY;
|
|
118
108
|
const previousChildText = process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT;
|
|
109
|
+
const previousReportTools = process.env.SELESAI_SUBAGENTS_E2E_REPORT_CHILD_TOOLS;
|
|
119
110
|
const previousArgv1 = process.argv[1];
|
|
120
111
|
|
|
121
112
|
writeFileSync(path.join(rootDir, ".keep"), "");
|
|
122
113
|
mkdirSync(binDir, { recursive: true });
|
|
114
|
+
mkdirSync(path.dirname(childCliPath), { recursive: true });
|
|
115
|
+
writeFileSync(childCliPath, `import ${JSON.stringify(pathToFileURL(CHILD_CLI_PATH).href)};\n`);
|
|
116
|
+
writeFileSync(
|
|
117
|
+
path.join(piPackageDir, "package.json"),
|
|
118
|
+
JSON.stringify({ name: "@selesai/code" }),
|
|
119
|
+
);
|
|
123
120
|
writeFileSync(
|
|
124
121
|
path.join(binDir, "pi"),
|
|
125
|
-
`#!/bin/sh\nexec "${process.execPath}" "${
|
|
122
|
+
`#!/bin/sh\nexec "${process.execPath}" "${childCliPath}" "$@"\n`,
|
|
126
123
|
{ mode: 0o755 },
|
|
127
124
|
);
|
|
128
125
|
writeFileSync(
|
|
129
126
|
path.join(binDir, "pi.cmd"),
|
|
130
|
-
`@echo off\r\n"${process.execPath}" "${
|
|
127
|
+
`@echo off\r\n"${process.execPath}" "${childCliPath}" %*\r\n`,
|
|
131
128
|
);
|
|
132
129
|
|
|
133
130
|
process.env.PATH = `${binDir}${path.delimiter}${previousPath ?? ""}`;
|
|
131
|
+
if (process.platform === "win32") {
|
|
132
|
+
delete process.env.SELESAI_SUBAGENT_PI_BINARY;
|
|
133
|
+
process.argv[1] = childCliPath;
|
|
134
|
+
} else {
|
|
135
|
+
process.env.SELESAI_SUBAGENT_PI_BINARY = path.join(binDir, "pi");
|
|
136
|
+
}
|
|
134
137
|
process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT = childText;
|
|
135
|
-
if (
|
|
138
|
+
if (reportChildTools) process.env.SELESAI_SUBAGENTS_E2E_REPORT_CHILD_TOOLS = "1";
|
|
139
|
+
else delete process.env.SELESAI_SUBAGENTS_E2E_REPORT_CHILD_TOOLS;
|
|
136
140
|
|
|
137
141
|
return () => {
|
|
138
142
|
if (previousPath === undefined) delete process.env.PATH;
|
|
139
143
|
else process.env.PATH = previousPath;
|
|
140
|
-
if (
|
|
141
|
-
else process.env.
|
|
144
|
+
if (previousPiBinary === undefined) delete process.env.SELESAI_SUBAGENT_PI_BINARY;
|
|
145
|
+
else process.env.SELESAI_SUBAGENT_PI_BINARY = previousPiBinary;
|
|
142
146
|
if (process.platform === "win32") {
|
|
143
147
|
if (previousArgv1 === undefined) delete process.argv[1];
|
|
144
148
|
else process.argv[1] = previousArgv1;
|
|
145
149
|
}
|
|
150
|
+
if (previousChildText === undefined) delete process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT;
|
|
151
|
+
else process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT = previousChildText;
|
|
152
|
+
if (previousReportTools === undefined) delete process.env.SELESAI_SUBAGENTS_E2E_REPORT_CHILD_TOOLS;
|
|
153
|
+
else process.env.SELESAI_SUBAGENTS_E2E_REPORT_CHILD_TOOLS = previousReportTools;
|
|
146
154
|
rmSync(rootDir, { recursive: true, force: true });
|
|
147
155
|
};
|
|
148
156
|
}
|
|
@@ -171,11 +179,10 @@ export async function runRealSubagentSession(options: RealSessionRunOptions): Pr
|
|
|
171
179
|
["SELESAI_SUBAGENT_MAX_DEPTH", process.env.SELESAI_SUBAGENT_MAX_DEPTH],
|
|
172
180
|
["SELESAI_SUBAGENT_PARENT_SESSION", process.env.SELESAI_SUBAGENT_PARENT_SESSION],
|
|
173
181
|
["SELESAI_SUBAGENT_PI_BINARY", process.env.SELESAI_SUBAGENT_PI_BINARY],
|
|
174
|
-
["
|
|
182
|
+
["SELESAI_SUBAGENTS_SELESAI_CODING_AGENT_PACKAGE_ROOT", process.env.SELESAI_SUBAGENTS_SELESAI_CODING_AGENT_PACKAGE_ROOT],
|
|
175
183
|
]);
|
|
176
|
-
const uninstallChildPi = installChildPiShim(options.childText);
|
|
184
|
+
const uninstallChildPi = installChildPiShim(options.childText, options.reportChildTools);
|
|
177
185
|
let session: AgentSession | undefined;
|
|
178
|
-
let faux: ReturnType<typeof registerFauxProvider> | undefined;
|
|
179
186
|
let disposed = false;
|
|
180
187
|
|
|
181
188
|
const dispose = async () => {
|
|
@@ -187,7 +194,6 @@ export async function runRealSubagentSession(options: RealSessionRunOptions): Pr
|
|
|
187
194
|
try {
|
|
188
195
|
session?.dispose();
|
|
189
196
|
} catch {}
|
|
190
|
-
faux?.unregister();
|
|
191
197
|
uninstallChildPi();
|
|
192
198
|
restoreEnv(envSnapshot);
|
|
193
199
|
try {
|
|
@@ -208,14 +214,32 @@ export async function runRealSubagentSession(options: RealSessionRunOptions): Pr
|
|
|
208
214
|
delete process.env.SELESAI_SUBAGENT_DEPTH;
|
|
209
215
|
delete process.env.SELESAI_SUBAGENT_MAX_DEPTH;
|
|
210
216
|
delete process.env.SELESAI_SUBAGENT_PARENT_SESSION;
|
|
211
|
-
delete process.env.
|
|
212
|
-
|
|
217
|
+
delete process.env.SELESAI_SUBAGENTS_SELESAI_CODING_AGENT_PACKAGE_ROOT;
|
|
218
|
+
for (const [relativePath, content] of Object.entries(options.projectFiles ?? {})) {
|
|
219
|
+
const target = path.resolve(cwd, relativePath);
|
|
220
|
+
if (target !== cwd && !target.startsWith(`${cwd}${path.sep}`)) throw new Error(`E2E project file escapes cwd: ${relativePath}`);
|
|
221
|
+
mkdirSync(path.dirname(target), { recursive: true });
|
|
222
|
+
writeFileSync(target, content, "utf-8");
|
|
223
|
+
}
|
|
213
224
|
|
|
214
|
-
faux =
|
|
225
|
+
const faux = fauxProvider({
|
|
215
226
|
provider: "faux-e2e-parent",
|
|
216
227
|
models: [{ id: "parent", contextWindow: 200_000 }],
|
|
217
228
|
});
|
|
218
|
-
const
|
|
229
|
+
const modelRuntime = await ModelRuntime.create({
|
|
230
|
+
authPath: path.join(home, "auth.json"),
|
|
231
|
+
modelsPath: null,
|
|
232
|
+
allowModelNetwork: false,
|
|
233
|
+
});
|
|
234
|
+
modelRuntime.registerProvider(faux.provider.id, {
|
|
235
|
+
name: faux.provider.name,
|
|
236
|
+
api: faux.api,
|
|
237
|
+
apiKey: "faux",
|
|
238
|
+
streamSimple: faux.provider.streamSimple,
|
|
239
|
+
models: [...faux.models],
|
|
240
|
+
});
|
|
241
|
+
const model = modelRuntime.getModel(faux.provider.id, "parent");
|
|
242
|
+
if (!model) throw new Error("faux parent model was not registered");
|
|
219
243
|
const respond = options.respond;
|
|
220
244
|
const responseFactory: FauxResponseStep = async (context, _streamOptions, state) => toAssistantMessage(await respond(context, state));
|
|
221
245
|
faux.setResponses(Array.from({ length: 8 }, () => responseFactory));
|
|
@@ -241,7 +265,7 @@ export async function runRealSubagentSession(options: RealSessionRunOptions): Pr
|
|
|
241
265
|
cwd,
|
|
242
266
|
agentDir: home,
|
|
243
267
|
model,
|
|
244
|
-
|
|
268
|
+
modelRuntime,
|
|
245
269
|
resourceLoader: loader,
|
|
246
270
|
sessionManager: SessionManager.create(cwd, path.join(home, "sessions")),
|
|
247
271
|
settingsManager,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import { acquireSessionLease } from "../../src/runs/shared/session-lease.ts";
|
|
4
|
+
|
|
5
|
+
const [sessionFile, rootDir, releaseSignal, mode = "hold"] = process.argv.slice(2);
|
|
6
|
+
if (!sessionFile || !rootDir) throw new Error("sessionFile and rootDir are required");
|
|
7
|
+
|
|
8
|
+
let lease;
|
|
9
|
+
try {
|
|
10
|
+
lease = acquireSessionLease({
|
|
11
|
+
sessionFile,
|
|
12
|
+
runId: `child-${process.pid}`,
|
|
13
|
+
sourceRunId: "source-child",
|
|
14
|
+
parentSessionId: "parent-child",
|
|
15
|
+
}, { rootDir });
|
|
16
|
+
} catch (error) {
|
|
17
|
+
process.stdout.write(`${JSON.stringify({ acquired: false, error: error instanceof Error ? error.message : String(error) })}\n`);
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
if (mode === "abandon-with-writer") {
|
|
21
|
+
lease.updateWriter({ state: "spawning" });
|
|
22
|
+
const writer = spawn(process.execPath, ["-e", "setInterval(() => {}, 1000)"], {
|
|
23
|
+
detached: true,
|
|
24
|
+
stdio: "ignore",
|
|
25
|
+
});
|
|
26
|
+
if (typeof writer.pid !== "number") throw new Error("writer pid is unavailable");
|
|
27
|
+
lease.updateWriter({ state: "running", pid: writer.pid });
|
|
28
|
+
writer.unref();
|
|
29
|
+
process.stdout.write(`${JSON.stringify({ acquired: true, owner: lease.owner, writerPid: writer.pid })}\n`);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
process.stdout.write(`${JSON.stringify({ acquired: true, owner: lease.owner })}\n`);
|
|
34
|
+
if (mode === "abandon") process.exit(0);
|
|
35
|
+
if (!releaseSignal) throw new Error("releaseSignal is required in hold mode");
|
|
36
|
+
|
|
37
|
+
const timer = setInterval(() => {
|
|
38
|
+
if (!fs.existsSync(releaseSignal)) return;
|
|
39
|
+
clearInterval(timer);
|
|
40
|
+
lease.release();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}, 20);
|