@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
|
@@ -5,7 +5,7 @@ import * as path from "node:path";
|
|
|
5
5
|
import type { MockPi } from "../support/helpers.ts";
|
|
6
6
|
import { createEventBus, createMockPi, createTempDir, events, removeTempDir, tryImport } from "../support/helpers.ts";
|
|
7
7
|
import { discoverAgents } from "../../src/agents/agents.ts";
|
|
8
|
-
import { INTERCOM_DETACH_REQUEST_EVENT } from "../../src/shared/types.ts";
|
|
8
|
+
import { DEFAULT_FORK_PREAMBLE, INTERCOM_DETACH_REQUEST_EVENT, SUBAGENT_ASYNC_STARTED_EVENT } from "../../src/shared/types.ts";
|
|
9
9
|
|
|
10
10
|
interface ExecutorModule {
|
|
11
11
|
createSubagentExecutor?: (...args: unknown[]) => {
|
|
@@ -19,10 +19,10 @@ interface ExecutorModule {
|
|
|
19
19
|
isError?: boolean;
|
|
20
20
|
content: Array<{ text?: string }>;
|
|
21
21
|
details?: {
|
|
22
|
-
context?: "fresh" | "fork";
|
|
22
|
+
context?: "fresh" | "fork" | "mixed";
|
|
23
23
|
mode?: "single" | "parallel" | "chain";
|
|
24
24
|
asyncId?: string;
|
|
25
|
-
results?: Array<{ detached?: boolean; exitCode?: number; skills?: string[] }>;
|
|
25
|
+
results?: Array<{ context?: "fresh" | "fork"; detached?: boolean; exitCode?: number; skills?: string[] }>;
|
|
26
26
|
};
|
|
27
27
|
}>;
|
|
28
28
|
};
|
|
@@ -277,6 +277,25 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
277
277
|
};
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
function makeSignedThinkingSessionManager(childSessionFile: string) {
|
|
281
|
+
const parentSessionFile = path.join(tempDir, "parent.jsonl");
|
|
282
|
+
fs.writeFileSync(parentSessionFile, '{"type":"session","version":1,"id":"parent","timestamp":"2026-04-16T00:00:00.000Z","cwd":"/tmp"}\n', "utf-8");
|
|
283
|
+
return {
|
|
284
|
+
getSessionId: () => "session-123",
|
|
285
|
+
getSessionFile: () => parentSessionFile,
|
|
286
|
+
getLeafId: () => "assistant-1",
|
|
287
|
+
openSession: () => ({
|
|
288
|
+
createBranchedSession: () => {
|
|
289
|
+
fs.writeFileSync(childSessionFile, [
|
|
290
|
+
{ type: "session", version: 1, id: "child", timestamp: "2026-04-16T00:00:00.000Z", cwd: "/tmp", parentSession: parentSessionFile },
|
|
291
|
+
{ type: "message", id: "assistant-1", parentId: null, timestamp: "2026-04-16T00:00:02.000Z", message: { role: "assistant", provider: "anthropic", api: "anthropic-messages", model: "anthropic/claude-sonnet-4-5", content: [{ type: "thinking", thinking: "private chain", thinkingSignature: "signed" }, { type: "text", text: "answer" }] } },
|
|
292
|
+
].map((entry) => JSON.stringify(entry)).join("\n") + "\n", "utf-8");
|
|
293
|
+
return childSessionFile;
|
|
294
|
+
},
|
|
295
|
+
}),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
280
299
|
it("runs a single agent when task is omitted", async () => {
|
|
281
300
|
const { manager } = makeSessionManagerRecorder();
|
|
282
301
|
const executor = makeExecutor();
|
|
@@ -419,12 +438,21 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
419
438
|
projectAgentsDir: null,
|
|
420
439
|
}));
|
|
421
440
|
|
|
441
|
+
const ctx = {
|
|
442
|
+
...makeCtx(manager),
|
|
443
|
+
modelRegistry: {
|
|
444
|
+
getAvailable: () => [
|
|
445
|
+
{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true },
|
|
446
|
+
{ provider: "anthropic", id: "claude-sonnet-4", api: "anthropic-messages", reasoning: true },
|
|
447
|
+
],
|
|
448
|
+
},
|
|
449
|
+
};
|
|
422
450
|
const result = await executor.execute(
|
|
423
451
|
"id",
|
|
424
452
|
{ agent: "worker", task: "test" },
|
|
425
453
|
new AbortController().signal,
|
|
426
454
|
undefined,
|
|
427
|
-
|
|
455
|
+
ctx,
|
|
428
456
|
);
|
|
429
457
|
|
|
430
458
|
assert.equal(result.isError, undefined);
|
|
@@ -432,6 +460,353 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
432
460
|
assert.deepEqual(modelArgs, ["openai/gpt-5-mini:off", "anthropic/claude-sonnet-4:off"]);
|
|
433
461
|
});
|
|
434
462
|
|
|
463
|
+
it("keeps requested thinking for non-Anthropic forked children without Anthropic fallbacks", async () => {
|
|
464
|
+
mockPi.reset();
|
|
465
|
+
mockPi.onCall({ output: "done" });
|
|
466
|
+
const parentSessionFile = path.join(tempDir, "parent.jsonl");
|
|
467
|
+
const childSessionFile = path.join(tempDir, "fork-keep-thinking.jsonl");
|
|
468
|
+
fs.writeFileSync(parentSessionFile, '{"type":"session","version":1,"id":"parent","timestamp":"2026-04-16T00:00:00.000Z","cwd":"/tmp"}\n', "utf-8");
|
|
469
|
+
const manager = {
|
|
470
|
+
getSessionId: () => "session-123",
|
|
471
|
+
getSessionFile: () => parentSessionFile,
|
|
472
|
+
getLeafId: () => "assistant-1",
|
|
473
|
+
openSession: () => ({
|
|
474
|
+
createBranchedSession: () => {
|
|
475
|
+
fs.writeFileSync(childSessionFile, [
|
|
476
|
+
{ type: "session", version: 1, id: "child", timestamp: "2026-04-16T00:00:00.000Z", cwd: "/tmp", parentSession: parentSessionFile },
|
|
477
|
+
{ type: "message", id: "assistant-1", parentId: null, timestamp: "2026-04-16T00:00:02.000Z", message: { role: "assistant", provider: "anthropic", api: "anthropic-messages", model: "anthropic/claude-sonnet-4-5", content: [{ type: "thinking", thinking: "private chain", thinkingSignature: "signed" }, { type: "text", text: "answer" }] } },
|
|
478
|
+
].map((entry) => JSON.stringify(entry)).join("\n") + "\n", "utf-8");
|
|
479
|
+
return childSessionFile;
|
|
480
|
+
},
|
|
481
|
+
}),
|
|
482
|
+
};
|
|
483
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
484
|
+
agents: [
|
|
485
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "openai/gpt-5-mini:high", thinking: "high" },
|
|
486
|
+
],
|
|
487
|
+
projectAgentsDir: null,
|
|
488
|
+
}));
|
|
489
|
+
|
|
490
|
+
const ctx = {
|
|
491
|
+
...makeCtx(manager),
|
|
492
|
+
modelRegistry: {
|
|
493
|
+
getAvailable: () => [{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true }],
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
const result = await executor.execute(
|
|
497
|
+
"id",
|
|
498
|
+
{ agent: "worker", task: "test" },
|
|
499
|
+
new AbortController().signal,
|
|
500
|
+
undefined,
|
|
501
|
+
ctx,
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
assert.equal(result.isError, undefined);
|
|
505
|
+
const args = readCallArgs();
|
|
506
|
+
assert.equal(args[args.indexOf("--model") + 1], "openai/gpt-5-mini:high");
|
|
507
|
+
const entries = fs.readFileSync(childSessionFile, "utf-8").trim().split("\n").map((line) => JSON.parse(line));
|
|
508
|
+
assert.deepEqual(entries[1].message.content, [{ type: "text", text: "answer" }]);
|
|
509
|
+
assert.equal(entries.length, 2);
|
|
510
|
+
const text = result.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
511
|
+
assert.equal(text.includes("fork context forced thinking off"), false);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it("keeps inherited model-scope violations as warnings in fork mode", async () => {
|
|
515
|
+
const childSessionFile = path.join(tempDir, "fork-model-scope-warning.jsonl");
|
|
516
|
+
const manager = makeSignedThinkingSessionManager(childSessionFile);
|
|
517
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
518
|
+
agents: [
|
|
519
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "openai/gpt-5-mini:high", thinking: "high" },
|
|
520
|
+
],
|
|
521
|
+
projectAgentsDir: null,
|
|
522
|
+
modelScope: { enforce: true, allow: ["anthropic/*"] },
|
|
523
|
+
}));
|
|
524
|
+
const ctx = {
|
|
525
|
+
...makeCtx(manager),
|
|
526
|
+
modelRegistry: {
|
|
527
|
+
getAvailable: () => [{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true }],
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
const warnings: string[] = [];
|
|
531
|
+
const originalWarn = console.warn;
|
|
532
|
+
console.warn = (message?: unknown) => warnings.push(String(message));
|
|
533
|
+
try {
|
|
534
|
+
const result = await executor.execute(
|
|
535
|
+
"id",
|
|
536
|
+
{ agent: "worker", task: "test" },
|
|
537
|
+
new AbortController().signal,
|
|
538
|
+
undefined,
|
|
539
|
+
ctx,
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
assert.equal(result.isError, undefined);
|
|
543
|
+
assert.equal(warnings.length, 1);
|
|
544
|
+
assert.match(warnings[0]!, /outside the configured subagent model scope/);
|
|
545
|
+
const args = readCallArgs();
|
|
546
|
+
assert.equal(args[args.indexOf("--model") + 1], "openai/gpt-5-mini:high");
|
|
547
|
+
} finally {
|
|
548
|
+
console.warn = originalWarn;
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it("warns when empty inheritance falls back to an out-of-scope agent model", async () => {
|
|
553
|
+
const childSessionFile = path.join(tempDir, "fork-empty-model-scope-warning.jsonl");
|
|
554
|
+
const manager = makeSignedThinkingSessionManager(childSessionFile);
|
|
555
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
556
|
+
agents: [
|
|
557
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "openai/gpt-5-mini", thinking: "high" },
|
|
558
|
+
],
|
|
559
|
+
projectAgentsDir: null,
|
|
560
|
+
modelScope: { enforce: true, allow: ["anthropic/*"] },
|
|
561
|
+
}));
|
|
562
|
+
const ctx = {
|
|
563
|
+
...makeCtx(manager),
|
|
564
|
+
modelRegistry: {
|
|
565
|
+
getAvailable: () => [{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true }],
|
|
566
|
+
},
|
|
567
|
+
};
|
|
568
|
+
const warnings: string[] = [];
|
|
569
|
+
const originalWarn = console.warn;
|
|
570
|
+
console.warn = (message?: unknown) => warnings.push(String(message));
|
|
571
|
+
try {
|
|
572
|
+
for (const model of ["", "inherit"]) {
|
|
573
|
+
const result = await executor.execute(
|
|
574
|
+
"id",
|
|
575
|
+
{ agent: "worker", task: "test", model },
|
|
576
|
+
new AbortController().signal,
|
|
577
|
+
undefined,
|
|
578
|
+
ctx,
|
|
579
|
+
);
|
|
580
|
+
assert.equal(result.isError, undefined);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
assert.equal(warnings.length, 2);
|
|
584
|
+
assert.equal(warnings.every((warning) => warning.includes("outside the configured subagent model scope")), true);
|
|
585
|
+
for (const args of readAllCallArgs()) {
|
|
586
|
+
assert.equal(args[args.indexOf("--model") + 1], "openai/gpt-5-mini:high");
|
|
587
|
+
}
|
|
588
|
+
} finally {
|
|
589
|
+
console.warn = originalWarn;
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it("notes the forced thinking downgrade in the result for Anthropic forked children", async () => {
|
|
594
|
+
mockPi.reset();
|
|
595
|
+
mockPi.onCall({ output: "done" });
|
|
596
|
+
const parentSessionFile = path.join(tempDir, "parent.jsonl");
|
|
597
|
+
const childSessionFile = path.join(tempDir, "fork-note-downgrade.jsonl");
|
|
598
|
+
fs.writeFileSync(parentSessionFile, '{"type":"session","version":1,"id":"parent","timestamp":"2026-04-16T00:00:00.000Z","cwd":"/tmp"}\n', "utf-8");
|
|
599
|
+
const manager = {
|
|
600
|
+
getSessionId: () => "session-123",
|
|
601
|
+
getSessionFile: () => parentSessionFile,
|
|
602
|
+
getLeafId: () => "assistant-1",
|
|
603
|
+
openSession: () => ({
|
|
604
|
+
createBranchedSession: () => {
|
|
605
|
+
fs.writeFileSync(childSessionFile, [
|
|
606
|
+
{ type: "session", version: 1, id: "child", timestamp: "2026-04-16T00:00:00.000Z", cwd: "/tmp", parentSession: parentSessionFile },
|
|
607
|
+
{ type: "message", id: "assistant-1", parentId: null, timestamp: "2026-04-16T00:00:02.000Z", message: { role: "assistant", provider: "anthropic", api: "anthropic-messages", model: "anthropic/claude-sonnet-4-5", content: [{ type: "thinking", thinking: "private chain", thinkingSignature: "signed" }, { type: "text", text: "answer" }] } },
|
|
608
|
+
].map((entry) => JSON.stringify(entry)).join("\n") + "\n", "utf-8");
|
|
609
|
+
return childSessionFile;
|
|
610
|
+
},
|
|
611
|
+
}),
|
|
612
|
+
};
|
|
613
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
614
|
+
agents: [
|
|
615
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "anthropic/claude-sonnet-4-5:high", thinking: "high" },
|
|
616
|
+
],
|
|
617
|
+
projectAgentsDir: null,
|
|
618
|
+
}));
|
|
619
|
+
|
|
620
|
+
const ctx = {
|
|
621
|
+
...makeCtx(manager),
|
|
622
|
+
modelRegistry: {
|
|
623
|
+
getAvailable: () => [{ provider: "anthropic", id: "claude-sonnet-4-5", api: "anthropic-messages", reasoning: true }],
|
|
624
|
+
},
|
|
625
|
+
};
|
|
626
|
+
const result = await executor.execute(
|
|
627
|
+
"id",
|
|
628
|
+
{ agent: "worker", task: "test" },
|
|
629
|
+
new AbortController().signal,
|
|
630
|
+
undefined,
|
|
631
|
+
ctx,
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
assert.equal(result.isError, undefined);
|
|
635
|
+
const text = result.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
636
|
+
assert.equal(text.includes("fork context forced thinking off for worker (child 0)"), true);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it("resolves inherit before classifying a forked child", async () => {
|
|
640
|
+
const childSessionFile = path.join(tempDir, "fork-inherit-thinking.jsonl");
|
|
641
|
+
const manager = makeSignedThinkingSessionManager(childSessionFile);
|
|
642
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
643
|
+
agents: [
|
|
644
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", thinking: "high" },
|
|
645
|
+
],
|
|
646
|
+
projectAgentsDir: null,
|
|
647
|
+
}));
|
|
648
|
+
const ctx = {
|
|
649
|
+
...makeCtx(manager),
|
|
650
|
+
model: { provider: "anthropic", id: "claude-sonnet-4-5" },
|
|
651
|
+
modelRegistry: {
|
|
652
|
+
getAvailable: () => [{ provider: "anthropic", id: "claude-sonnet-4-5", api: "anthropic-messages", reasoning: true }],
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
const result = await executor.execute(
|
|
657
|
+
"id",
|
|
658
|
+
{ agent: "worker", task: "test", model: "inherit" },
|
|
659
|
+
new AbortController().signal,
|
|
660
|
+
undefined,
|
|
661
|
+
ctx,
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
assert.equal(result.isError, undefined);
|
|
665
|
+
const args = readCallArgs();
|
|
666
|
+
assert.equal(args[args.indexOf("--model") + 1], "anthropic/claude-sonnet-4-5:off");
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it("classifies empty parallel model overrides as parent inheritance", async () => {
|
|
670
|
+
const childSessionFile = path.join(tempDir, "fork-empty-parallel-model.jsonl");
|
|
671
|
+
const manager = makeSignedThinkingSessionManager(childSessionFile);
|
|
672
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
673
|
+
agents: [
|
|
674
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "anthropic/claude-sonnet-4-5", thinking: "high" },
|
|
675
|
+
],
|
|
676
|
+
projectAgentsDir: null,
|
|
677
|
+
}));
|
|
678
|
+
const ctx = {
|
|
679
|
+
...makeCtx(manager),
|
|
680
|
+
model: { provider: "openai", id: "gpt-5-mini" },
|
|
681
|
+
modelRegistry: {
|
|
682
|
+
getAvailable: () => [
|
|
683
|
+
{ provider: "anthropic", id: "claude-sonnet-4-5", api: "anthropic-messages", reasoning: true },
|
|
684
|
+
{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true },
|
|
685
|
+
],
|
|
686
|
+
},
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const result = await executor.execute(
|
|
690
|
+
"id",
|
|
691
|
+
{ tasks: [{ agent: "worker", task: "test", model: "" }] },
|
|
692
|
+
new AbortController().signal,
|
|
693
|
+
undefined,
|
|
694
|
+
ctx,
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
assert.equal(result.isError, undefined);
|
|
698
|
+
const args = readCallArgs();
|
|
699
|
+
assert.equal(args[args.indexOf("--model") + 1], "openai/gpt-5-mini:high");
|
|
700
|
+
const text = result.content.filter((block) => block.text).map((block) => block.text).join("\n");
|
|
701
|
+
assert.equal(text.includes("fork context forced thinking off"), false);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
it("classifies the agent model when empty inheritance has no parent model", async () => {
|
|
705
|
+
const childSessionFile = path.join(tempDir, "fork-empty-model-without-parent.jsonl");
|
|
706
|
+
const manager = makeSignedThinkingSessionManager(childSessionFile);
|
|
707
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
708
|
+
agents: [
|
|
709
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "openai/gpt-5-mini", thinking: "high" },
|
|
710
|
+
],
|
|
711
|
+
projectAgentsDir: null,
|
|
712
|
+
}));
|
|
713
|
+
const ctx = {
|
|
714
|
+
...makeCtx(manager),
|
|
715
|
+
modelRegistry: {
|
|
716
|
+
getAvailable: () => [{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true }],
|
|
717
|
+
},
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
const result = await executor.execute(
|
|
721
|
+
"id",
|
|
722
|
+
{ tasks: [{ agent: "worker", task: "test", model: "" }] },
|
|
723
|
+
new AbortController().signal,
|
|
724
|
+
undefined,
|
|
725
|
+
ctx,
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
assert.equal(result.isError, undefined);
|
|
729
|
+
const args = readCallArgs();
|
|
730
|
+
assert.equal(args[args.indexOf("--model") + 1], "openai/gpt-5-mini:high");
|
|
731
|
+
const text = result.content.filter((block) => block.text).map((block) => block.text).join("\n");
|
|
732
|
+
assert.equal(text.includes("fork context forced thinking off"), false);
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it("classifies the effective model selected in Clarify", async () => {
|
|
736
|
+
const childSessionFile = path.join(tempDir, "fork-clarify-thinking.jsonl");
|
|
737
|
+
const manager = makeSignedThinkingSessionManager(childSessionFile);
|
|
738
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
739
|
+
agents: [
|
|
740
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "anthropic/claude-sonnet-4-5:high", thinking: "high" },
|
|
741
|
+
],
|
|
742
|
+
projectAgentsDir: null,
|
|
743
|
+
}));
|
|
744
|
+
const ctx = {
|
|
745
|
+
...makeCtx(manager),
|
|
746
|
+
hasUI: true,
|
|
747
|
+
ui: {
|
|
748
|
+
custom: async () => ({
|
|
749
|
+
confirmed: true,
|
|
750
|
+
runInBackground: false,
|
|
751
|
+
templates: ["test"],
|
|
752
|
+
behaviorOverrides: [{ model: "openai-codex/gpt-5.6-sol:high" }],
|
|
753
|
+
}),
|
|
754
|
+
},
|
|
755
|
+
model: { provider: "anthropic", id: "claude-sonnet-4-5" },
|
|
756
|
+
modelRegistry: {
|
|
757
|
+
getAvailable: () => [
|
|
758
|
+
{ provider: "anthropic", id: "claude-sonnet-4-5", api: "anthropic-messages", reasoning: true },
|
|
759
|
+
{ provider: "openai-codex", id: "gpt-5.6-sol", api: "openai-responses", reasoning: true },
|
|
760
|
+
],
|
|
761
|
+
},
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const result = await executor.execute(
|
|
765
|
+
"id",
|
|
766
|
+
{ agent: "worker", task: "test", clarify: true },
|
|
767
|
+
new AbortController().signal,
|
|
768
|
+
undefined,
|
|
769
|
+
ctx,
|
|
770
|
+
);
|
|
771
|
+
|
|
772
|
+
assert.equal(result.isError, undefined);
|
|
773
|
+
const args = readCallArgs();
|
|
774
|
+
assert.equal(args[args.indexOf("--model") + 1], "openai-codex/gpt-5.6-sol:high");
|
|
775
|
+
const text = result.content.filter((block) => block.text).map((block) => block.text).join("\n");
|
|
776
|
+
assert.equal(text.includes("fork context forced thinking off"), false);
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("includes the fork-thinking downgrade note on failed results", async () => {
|
|
780
|
+
mockPi.reset();
|
|
781
|
+
mockPi.onCall({ stderr: "task failed", exitCode: 1 });
|
|
782
|
+
const childSessionFile = path.join(tempDir, "fork-failed-thinking.jsonl");
|
|
783
|
+
const manager = makeSignedThinkingSessionManager(childSessionFile);
|
|
784
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
785
|
+
agents: [
|
|
786
|
+
{ name: "worker", description: "Worker", defaultContext: "fork", model: "anthropic/claude-sonnet-4-5:high", thinking: "high" },
|
|
787
|
+
],
|
|
788
|
+
projectAgentsDir: null,
|
|
789
|
+
}));
|
|
790
|
+
const ctx = {
|
|
791
|
+
...makeCtx(manager),
|
|
792
|
+
modelRegistry: {
|
|
793
|
+
getAvailable: () => [{ provider: "anthropic", id: "claude-sonnet-4-5", api: "anthropic-messages", reasoning: true }],
|
|
794
|
+
},
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
const result = await executor.execute(
|
|
798
|
+
"id",
|
|
799
|
+
{ agent: "worker", task: "test" },
|
|
800
|
+
new AbortController().signal,
|
|
801
|
+
undefined,
|
|
802
|
+
ctx,
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
assert.equal(result.isError, true);
|
|
806
|
+
const text = result.content.filter((block) => block.text).map((block) => block.text).join("\n");
|
|
807
|
+
assert.equal(text.includes("fork context forced thinking off for worker (child 0)"), true);
|
|
808
|
+
});
|
|
809
|
+
|
|
435
810
|
it("keeps default-fork context on run-path errors", async () => {
|
|
436
811
|
const parentSessionFile = path.join(tempDir, "parent.jsonl");
|
|
437
812
|
const { manager } = makeForkingSessionManagerRecorder({ sessionFile: parentSessionFile, leafId: "leaf-current" });
|
|
@@ -479,7 +854,8 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
479
854
|
);
|
|
480
855
|
|
|
481
856
|
assert.equal(result.isError, undefined);
|
|
482
|
-
assert.equal(result.details?.context,
|
|
857
|
+
assert.equal(result.details?.context, "fresh");
|
|
858
|
+
assert.equal(result.details?.results?.[0]?.context, "fresh");
|
|
483
859
|
assert.deepEqual(openedPaths, []);
|
|
484
860
|
assert.deepEqual(branchedLeafIds, []);
|
|
485
861
|
assert.notEqual(readSessionArgsFromCalls()[0], path.join(tempDir, "fork-1.jsonl"));
|
|
@@ -505,7 +881,8 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
505
881
|
);
|
|
506
882
|
|
|
507
883
|
assert.equal(result.isError, undefined);
|
|
508
|
-
assert.equal(result.details?.context, "
|
|
884
|
+
assert.equal(result.details?.context, "mixed");
|
|
885
|
+
assert.deepEqual(result.details?.results?.map((entry) => entry.context), ["fork", "fresh"]);
|
|
509
886
|
assert.deepEqual(openedPaths, [parentSessionFile]);
|
|
510
887
|
assert.deepEqual(branchedLeafIds, ["leaf-current"]);
|
|
511
888
|
const workerArgs = readCallArgsForTask("one");
|
|
@@ -536,7 +913,8 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
536
913
|
);
|
|
537
914
|
|
|
538
915
|
assert.equal(result.isError, undefined);
|
|
539
|
-
assert.equal(result.details?.context,
|
|
916
|
+
assert.equal(result.details?.context, "fresh");
|
|
917
|
+
assert.deepEqual(result.details?.results?.map((entry) => entry.context), ["fresh", "fresh"]);
|
|
540
918
|
assert.deepEqual(openedPaths, []);
|
|
541
919
|
});
|
|
542
920
|
|
|
@@ -560,7 +938,8 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
560
938
|
);
|
|
561
939
|
|
|
562
940
|
assert.equal(result.isError, undefined);
|
|
563
|
-
assert.equal(result.details?.context, "
|
|
941
|
+
assert.equal(result.details?.context, "mixed");
|
|
942
|
+
assert.deepEqual(result.details?.results?.map((entry) => entry.context), ["fresh", "fork"]);
|
|
564
943
|
assert.deepEqual(openedPaths, [parentSessionFile]);
|
|
565
944
|
assert.deepEqual(branchedLeafIds, ["leaf-current"]);
|
|
566
945
|
const scanArgs = readCallArgsForTask("scan");
|
|
@@ -1038,7 +1417,7 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
1038
1417
|
assert.equal(result.isError, undefined);
|
|
1039
1418
|
assert.match(result.content[0]?.text ?? "", /Parallel run detached for intercom coordination/);
|
|
1040
1419
|
assert.equal(detachEmitted, true);
|
|
1041
|
-
assert.equal(result.details?.results?.some((entry) => entry.detached === true && entry.exitCode ===
|
|
1420
|
+
assert.equal(result.details?.results?.some((entry) => entry.detached === true && entry.exitCode === -2), true);
|
|
1042
1421
|
});
|
|
1043
1422
|
|
|
1044
1423
|
it("runs top-level parallel async requests in the background", { skip: !asyncAvailable ? "jiti not available" : undefined }, async () => {
|
|
@@ -1065,6 +1444,207 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
|
|
|
1065
1444
|
assert.match(result.content[0]?.text ?? "", /Async parallel:/);
|
|
1066
1445
|
});
|
|
1067
1446
|
|
|
1447
|
+
it("keeps inherited model-scope warnings non-fatal in async parallel conversion", { skip: !asyncAvailable ? "jiti not available" : undefined }, async () => {
|
|
1448
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
1449
|
+
agents: [{ name: "worker", description: "Worker", model: "openai/gpt-5-mini" }],
|
|
1450
|
+
projectAgentsDir: null,
|
|
1451
|
+
modelScope: { enforce: true, allow: ["anthropic/*"] },
|
|
1452
|
+
}));
|
|
1453
|
+
const ctx = {
|
|
1454
|
+
...makeCtx(makeSessionManagerRecorder().manager),
|
|
1455
|
+
modelRegistry: {
|
|
1456
|
+
getAvailable: () => [{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true }],
|
|
1457
|
+
},
|
|
1458
|
+
};
|
|
1459
|
+
const warnings: string[] = [];
|
|
1460
|
+
const originalWarn = console.warn;
|
|
1461
|
+
console.warn = (message?: unknown) => warnings.push(String(message));
|
|
1462
|
+
try {
|
|
1463
|
+
const result = await executor.execute(
|
|
1464
|
+
"id",
|
|
1465
|
+
{ tasks: [{ agent: "worker", task: "test" }], async: true },
|
|
1466
|
+
new AbortController().signal,
|
|
1467
|
+
undefined,
|
|
1468
|
+
ctx,
|
|
1469
|
+
);
|
|
1470
|
+
assert.equal(result.isError, undefined);
|
|
1471
|
+
assert.ok(result.details?.asyncId);
|
|
1472
|
+
assert.equal(warnings.length, 1);
|
|
1473
|
+
assert.match(warnings[0]!, /outside the configured subagent model scope/);
|
|
1474
|
+
} finally {
|
|
1475
|
+
console.warn = originalWarn;
|
|
1476
|
+
}
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1479
|
+
it("keeps inherited model-scope warnings non-fatal after Clarify switches parallel work to background", { skip: !asyncAvailable ? "jiti not available" : undefined }, async () => {
|
|
1480
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
1481
|
+
agents: [{ name: "worker", description: "Worker", model: "openai/gpt-5-mini" }],
|
|
1482
|
+
projectAgentsDir: null,
|
|
1483
|
+
modelScope: { enforce: true, allow: ["anthropic/*"] },
|
|
1484
|
+
}));
|
|
1485
|
+
const ctx = {
|
|
1486
|
+
...makeCtx(makeSessionManagerRecorder().manager),
|
|
1487
|
+
hasUI: true,
|
|
1488
|
+
ui: {
|
|
1489
|
+
custom: async () => ({
|
|
1490
|
+
confirmed: true,
|
|
1491
|
+
runInBackground: true,
|
|
1492
|
+
templates: ["test"],
|
|
1493
|
+
behaviorOverrides: [{}],
|
|
1494
|
+
}),
|
|
1495
|
+
},
|
|
1496
|
+
modelRegistry: {
|
|
1497
|
+
getAvailable: () => [{ provider: "openai", id: "gpt-5-mini", api: "openai-responses", reasoning: true }],
|
|
1498
|
+
},
|
|
1499
|
+
};
|
|
1500
|
+
const warnings: string[] = [];
|
|
1501
|
+
const originalWarn = console.warn;
|
|
1502
|
+
console.warn = (message?: unknown) => warnings.push(String(message));
|
|
1503
|
+
try {
|
|
1504
|
+
const result = await executor.execute(
|
|
1505
|
+
"id",
|
|
1506
|
+
{ tasks: [{ agent: "worker", task: "test" }], clarify: true },
|
|
1507
|
+
new AbortController().signal,
|
|
1508
|
+
undefined,
|
|
1509
|
+
ctx,
|
|
1510
|
+
);
|
|
1511
|
+
assert.equal(result.isError, undefined);
|
|
1512
|
+
assert.ok(result.details?.asyncId);
|
|
1513
|
+
assert.equal(warnings.length, 2);
|
|
1514
|
+
assert.equal(warnings.every((warning) => warning.includes("outside the configured subagent model scope")), true);
|
|
1515
|
+
} finally {
|
|
1516
|
+
console.warn = originalWarn;
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1520
|
+
it("keeps raw goals at fork-wrapped async executor boundaries", { skip: !asyncAvailable ? "jiti not available" : undefined }, async () => {
|
|
1521
|
+
const rawSingleGoal = "Direct forked single raw goal";
|
|
1522
|
+
const rawParallelGoal = "Top-level forked first child raw goal";
|
|
1523
|
+
const rawParallelChainGoal = "Parallel chain first child raw goal";
|
|
1524
|
+
const paddedWorkflowGoal = " preserve this workflow padding ";
|
|
1525
|
+
const literalPreambleGoal = `${DEFAULT_FORK_PREAMBLE}\n\nTask:\nliteral fresh-context text`;
|
|
1526
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
1527
|
+
agents: [
|
|
1528
|
+
{ name: "echo", description: "Echo", defaultContext: "fork" },
|
|
1529
|
+
{ name: "second", description: "Second" },
|
|
1530
|
+
],
|
|
1531
|
+
projectAgentsDir: null,
|
|
1532
|
+
}));
|
|
1533
|
+
const started: Array<{ id?: string; goal?: string }> = [];
|
|
1534
|
+
executor.eventsApi.on(SUBAGENT_ASYNC_STARTED_EVENT, (event: unknown) => started.push(event as { id?: string; goal?: string }));
|
|
1535
|
+
const forkManager = () => makeForkingSessionManagerRecorder({
|
|
1536
|
+
sessionFile: path.join(tempDir, "parent-fork-goal.jsonl"),
|
|
1537
|
+
leafId: "fork-goal-leaf",
|
|
1538
|
+
}).manager;
|
|
1539
|
+
|
|
1540
|
+
for (const testCase of [
|
|
1541
|
+
{
|
|
1542
|
+
name: "direct single",
|
|
1543
|
+
goal: rawSingleGoal,
|
|
1544
|
+
params: { agent: "echo", task: rawSingleGoal, async: true, clarify: false },
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
name: "top-level parallel",
|
|
1548
|
+
goal: rawParallelGoal,
|
|
1549
|
+
params: { tasks: [{ agent: "echo", task: rawParallelGoal }, { agent: "second", task: "Second child" }], async: true, clarify: false },
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
name: "parallel chain whitespace workflow fallback",
|
|
1553
|
+
goal: rawParallelChainGoal,
|
|
1554
|
+
params: { task: " ", chain: [{ parallel: [{ agent: "echo", task: rawParallelChainGoal }, { agent: "second", task: "Second chain child" }] }], async: true, clarify: false },
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
name: "padded workflow goal",
|
|
1558
|
+
goal: paddedWorkflowGoal,
|
|
1559
|
+
params: { task: paddedWorkflowGoal, chain: [{ agent: "echo", task: "Forked child" }], async: true, clarify: false },
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: "literal preamble in fresh child",
|
|
1563
|
+
goal: literalPreambleGoal.slice(0, 120),
|
|
1564
|
+
allowsLiteralPreamble: true,
|
|
1565
|
+
params: { chain: [{ agent: "second", task: literalPreambleGoal }], async: true, clarify: false },
|
|
1566
|
+
},
|
|
1567
|
+
]) {
|
|
1568
|
+
const result = await executor.execute(
|
|
1569
|
+
`fork-goal-${testCase.name}`,
|
|
1570
|
+
testCase.params,
|
|
1571
|
+
new AbortController().signal,
|
|
1572
|
+
undefined,
|
|
1573
|
+
makeCtx(forkManager()),
|
|
1574
|
+
);
|
|
1575
|
+
assert.equal(result.isError, undefined, testCase.name);
|
|
1576
|
+
assert.ok(result.details?.asyncId, `${testCase.name}: expected an async id`);
|
|
1577
|
+
const event = started.find((entry) => entry.id === result.details?.asyncId);
|
|
1578
|
+
assert.ok(event, `${testCase.name}: missing async-started event for ${result.details?.asyncId}`);
|
|
1579
|
+
assert.equal(event.goal, testCase.goal, testCase.name);
|
|
1580
|
+
if (!("allowsLiteralPreamble" in testCase)) {
|
|
1581
|
+
assert.doesNotMatch(event.goal ?? "", /delegated subagent running from a fork/, testCase.name);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
it("uses edited raw goals when clarification switches each mode to background", { skip: !asyncAvailable ? "jiti not available" : undefined }, async () => {
|
|
1587
|
+
const executor = makeExecutorWithDiscoverAgents(() => ({
|
|
1588
|
+
agents: [
|
|
1589
|
+
{ name: "echo", description: "Echo", defaultContext: "fork" },
|
|
1590
|
+
{ name: "second", description: "Second" },
|
|
1591
|
+
],
|
|
1592
|
+
projectAgentsDir: null,
|
|
1593
|
+
}));
|
|
1594
|
+
const started: Array<{ id?: string; goal?: string }> = [];
|
|
1595
|
+
executor.eventsApi.on(SUBAGENT_ASYNC_STARTED_EVENT, (event: unknown) => started.push(event as { id?: string; goal?: string }));
|
|
1596
|
+
|
|
1597
|
+
for (const testCase of [
|
|
1598
|
+
{
|
|
1599
|
+
name: "single",
|
|
1600
|
+
goal: "Edited single goal",
|
|
1601
|
+
params: { agent: "echo", task: "Original single", async: true, clarify: true },
|
|
1602
|
+
templates: ["Edited single goal"],
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
name: "parallel",
|
|
1606
|
+
goal: "Edited parallel goal",
|
|
1607
|
+
params: { tasks: [{ agent: "echo", task: "Original parallel" }, { agent: "second", task: "Second child" }], async: true, clarify: true },
|
|
1608
|
+
templates: ["Edited parallel goal", "Second child"],
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
name: "chain",
|
|
1612
|
+
goal: "Edited chain goal",
|
|
1613
|
+
params: { chain: [{ agent: "echo", task: "Original chain" }], async: true, clarify: true },
|
|
1614
|
+
templates: ["Edited chain goal"],
|
|
1615
|
+
},
|
|
1616
|
+
]) {
|
|
1617
|
+
const forkManager = makeForkingSessionManagerRecorder({
|
|
1618
|
+
sessionFile: path.join(tempDir, `parent-clarify-${testCase.name}.jsonl`),
|
|
1619
|
+
leafId: `clarify-${testCase.name}-leaf`,
|
|
1620
|
+
}).manager;
|
|
1621
|
+
const ctx = {
|
|
1622
|
+
...makeCtx(forkManager),
|
|
1623
|
+
hasUI: true,
|
|
1624
|
+
ui: {
|
|
1625
|
+
custom: async () => ({
|
|
1626
|
+
confirmed: true,
|
|
1627
|
+
templates: testCase.templates,
|
|
1628
|
+
behaviorOverrides: testCase.templates.map(() => undefined),
|
|
1629
|
+
runInBackground: true,
|
|
1630
|
+
}),
|
|
1631
|
+
},
|
|
1632
|
+
};
|
|
1633
|
+
const result = await executor.execute(
|
|
1634
|
+
`clarify-goal-${testCase.name}`,
|
|
1635
|
+
testCase.params,
|
|
1636
|
+
new AbortController().signal,
|
|
1637
|
+
undefined,
|
|
1638
|
+
ctx,
|
|
1639
|
+
);
|
|
1640
|
+
assert.equal(result.isError, undefined, testCase.name);
|
|
1641
|
+
assert.ok(result.details?.asyncId, `${testCase.name}: expected an async id`);
|
|
1642
|
+
const event = started.find((entry) => entry.id === result.details?.asyncId);
|
|
1643
|
+
assert.equal(event?.goal, testCase.goal, testCase.name);
|
|
1644
|
+
assert.doesNotMatch(event?.goal ?? "", /delegated subagent running from a fork/, testCase.name);
|
|
1645
|
+
}
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1068
1648
|
it("runs async chain requests in the background when clarify is omitted", { skip: !asyncAvailable ? "jiti not available" : undefined }, async () => {
|
|
1069
1649
|
const executor = makeExecutor();
|
|
1070
1650
|
|