@selesai/code 0.5.20 → 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-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 +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS, runFileSystemOperationWithRetry, waitForFileSystemRetry } from "./file-system-retry.ts";
|
|
3
4
|
|
|
4
5
|
type AtomicJsonFs = Pick<typeof fs, "mkdirSync" | "writeFileSync" | "renameSync" | "rmSync">;
|
|
5
6
|
|
|
@@ -8,39 +9,13 @@ type AtomicJsonWriterOptions = {
|
|
|
8
9
|
now?: () => number;
|
|
9
10
|
pid?: number;
|
|
10
11
|
random?: () => number;
|
|
12
|
+
mode?: number;
|
|
11
13
|
retryRenameErrors?: boolean;
|
|
14
|
+
retryDirectoryErrors?: boolean;
|
|
12
15
|
retryDelaysMs?: readonly number[];
|
|
13
16
|
wait?: (delayMs: number) => void;
|
|
14
17
|
};
|
|
15
18
|
|
|
16
|
-
const DEFAULT_RENAME_RETRY_DELAYS_MS = [10, 25, 50, 100, 200, 500, 1000, 2000, 4000] as const;
|
|
17
|
-
const RETRYABLE_RENAME_ERROR_CODES = new Set(["EACCES", "EBUSY", "EPERM"]);
|
|
18
|
-
const WAIT_BUFFER = typeof SharedArrayBuffer !== "undefined" ? new SharedArrayBuffer(4) : undefined;
|
|
19
|
-
const WAIT_VIEW = WAIT_BUFFER ? new Int32Array(WAIT_BUFFER) : undefined;
|
|
20
|
-
|
|
21
|
-
function waitSync(delayMs: number): void {
|
|
22
|
-
if (delayMs <= 0) return;
|
|
23
|
-
if (WAIT_VIEW) {
|
|
24
|
-
try {
|
|
25
|
-
// writeAtomicJson is synchronous because callers often update status from sync callbacks.
|
|
26
|
-
// Atomics.wait gives Windows rename locks time to clear without burning CPU.
|
|
27
|
-
Atomics.wait(WAIT_VIEW, 0, 0, delayMs);
|
|
28
|
-
return;
|
|
29
|
-
} catch {
|
|
30
|
-
// Fall through to the portable busy wait below.
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const end = Date.now() + delayMs;
|
|
34
|
-
while (Date.now() < end) {
|
|
35
|
-
// Portable fallback for runtimes where Atomics.wait is unavailable.
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isRetryableRenameError(error: unknown): boolean {
|
|
40
|
-
const code = (error as NodeJS.ErrnoException | undefined)?.code;
|
|
41
|
-
return typeof code === "string" && RETRYABLE_RENAME_ERROR_CODES.has(code);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
19
|
function renameWithRetry(
|
|
45
20
|
fsImpl: AtomicJsonFs,
|
|
46
21
|
sourcePath: string,
|
|
@@ -48,16 +23,9 @@ function renameWithRetry(
|
|
|
48
23
|
retryDelaysMs: readonly number[],
|
|
49
24
|
wait: (delayMs: number) => void,
|
|
50
25
|
): void {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return;
|
|
55
|
-
} catch (error) {
|
|
56
|
-
const delayMs = retryDelaysMs[attempt];
|
|
57
|
-
if (delayMs === undefined || !isRetryableRenameError(error)) throw error;
|
|
58
|
-
wait(delayMs);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
26
|
+
runFileSystemOperationWithRetry(() => {
|
|
27
|
+
fsImpl.renameSync(sourcePath, targetPath);
|
|
28
|
+
}, { retryDelaysMs, wait });
|
|
61
29
|
}
|
|
62
30
|
|
|
63
31
|
export function createAtomicJsonWriter(options: AtomicJsonWriterOptions = {}): (filePath: string, payload: object) => void {
|
|
@@ -65,18 +33,24 @@ export function createAtomicJsonWriter(options: AtomicJsonWriterOptions = {}): (
|
|
|
65
33
|
const now = options.now ?? Date.now;
|
|
66
34
|
const pid = options.pid ?? process.pid;
|
|
67
35
|
const random = options.random ?? Math.random;
|
|
36
|
+
const mode = options.mode;
|
|
68
37
|
const retryRenameErrors = options.retryRenameErrors ?? process.platform === "win32";
|
|
69
|
-
const
|
|
70
|
-
const
|
|
38
|
+
const retryDirectoryErrors = options.retryDirectoryErrors ?? retryRenameErrors;
|
|
39
|
+
const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS;
|
|
40
|
+
const renameRetryDelaysMs = retryRenameErrors ? retryDelaysMs : [];
|
|
41
|
+
const directoryRetryDelaysMs = retryDirectoryErrors ? retryDelaysMs : [];
|
|
42
|
+
const wait = options.wait ?? waitForFileSystemRetry;
|
|
71
43
|
return (filePath: string, payload: object): void => {
|
|
72
|
-
|
|
44
|
+
runFileSystemOperationWithRetry(() => {
|
|
45
|
+
fsImpl.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
46
|
+
}, { retryDelaysMs: directoryRetryDelaysMs, wait });
|
|
73
47
|
const tempPath = path.join(
|
|
74
48
|
path.dirname(filePath),
|
|
75
49
|
`.${path.basename(filePath)}.${pid}.${now()}.${random().toString(36).slice(2)}.tmp`,
|
|
76
50
|
);
|
|
77
51
|
try {
|
|
78
|
-
fsImpl.writeFileSync(tempPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
79
|
-
renameWithRetry(fsImpl, tempPath, filePath,
|
|
52
|
+
fsImpl.writeFileSync(tempPath, JSON.stringify(payload, null, 2), mode === undefined ? "utf-8" : { encoding: "utf-8", mode });
|
|
53
|
+
renameWithRetry(fsImpl, tempPath, filePath, renameRetryDelaysMs, wait);
|
|
80
54
|
} finally {
|
|
81
55
|
fsImpl.rmSync(tempPath, { force: true });
|
|
82
56
|
}
|
|
@@ -84,3 +58,4 @@ export function createAtomicJsonWriter(options: AtomicJsonWriterOptions = {}): (
|
|
|
84
58
|
}
|
|
85
59
|
|
|
86
60
|
export const writeAtomicJson = createAtomicJsonWriter();
|
|
61
|
+
export const writePrivateAtomicJson = createAtomicJsonWriter({ mode: 0o600 });
|
|
@@ -3,6 +3,30 @@ import * as path from "node:path";
|
|
|
3
3
|
import type { Message } from "@earendil-works/pi-ai";
|
|
4
4
|
import { extractTextFromContent, extractToolArgsPreview } from "./utils.ts";
|
|
5
5
|
|
|
6
|
+
const MAX_TOOL_PAYLOAD_BYTES = 32 * 1024;
|
|
7
|
+
const TOOL_PAYLOAD_TRUNCATION_MARKER = "\n\n… payload truncated";
|
|
8
|
+
|
|
9
|
+
function boundedPayload(value: unknown, maxBytes = MAX_TOOL_PAYLOAD_BYTES): string | undefined {
|
|
10
|
+
let text: string;
|
|
11
|
+
if (typeof value === "string") text = value;
|
|
12
|
+
else {
|
|
13
|
+
try {
|
|
14
|
+
const serialized = JSON.stringify(value, null, 2);
|
|
15
|
+
if (serialized === undefined) return undefined;
|
|
16
|
+
text = serialized;
|
|
17
|
+
} catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (!text.trim()) return undefined;
|
|
22
|
+
const payload = Buffer.from(text, "utf-8");
|
|
23
|
+
if (payload.length <= maxBytes) return text;
|
|
24
|
+
const markerBytes = Buffer.byteLength(TOOL_PAYLOAD_TRUNCATION_MARKER, "utf-8");
|
|
25
|
+
let end = Math.max(0, maxBytes - markerBytes);
|
|
26
|
+
while (end > 0 && (payload[end]! & 0xc0) === 0x80) end--;
|
|
27
|
+
return `${payload.subarray(0, end).toString("utf-8")}${TOOL_PAYLOAD_TRUNCATION_MARKER}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
6
30
|
export const CHILD_TRANSCRIPT_ARTIFACT_VERSION = 1;
|
|
7
31
|
const DEFAULT_MAX_CHILD_TRANSCRIPT_BYTES = 50 * 1024 * 1024;
|
|
8
32
|
|
|
@@ -19,8 +43,10 @@ type ChildTranscriptMessage = Message & {
|
|
|
19
43
|
interface ChildTranscriptEvent {
|
|
20
44
|
type?: string;
|
|
21
45
|
message?: ChildTranscriptMessage;
|
|
46
|
+
toolCallId?: string;
|
|
22
47
|
toolName?: string;
|
|
23
48
|
args?: unknown;
|
|
49
|
+
isError?: boolean;
|
|
24
50
|
}
|
|
25
51
|
|
|
26
52
|
interface ChildTranscriptWriterInput {
|
|
@@ -147,6 +173,27 @@ export function createChildTranscriptWriter(input: ChildTranscriptWriterInput):
|
|
|
147
173
|
|
|
148
174
|
const writeMessage = (sourceEventType: string, message: ChildTranscriptMessage) => {
|
|
149
175
|
const text = extractTextFromContent(message.content);
|
|
176
|
+
if (message.role === "toolResult") {
|
|
177
|
+
const output = boundedPayload(text);
|
|
178
|
+
writeRecord({
|
|
179
|
+
...baseRecord("message"),
|
|
180
|
+
sourceEventType,
|
|
181
|
+
role: message.role,
|
|
182
|
+
toolCallId: message.toolCallId,
|
|
183
|
+
toolName: message.toolName,
|
|
184
|
+
isError: message.isError,
|
|
185
|
+
...(output ? { text: output, outputTruncated: output.includes("… payload truncated") } : {}),
|
|
186
|
+
message: {
|
|
187
|
+
role: message.role,
|
|
188
|
+
toolCallId: message.toolCallId,
|
|
189
|
+
toolName: message.toolName,
|
|
190
|
+
isError: message.isError,
|
|
191
|
+
content: output ? [{ type: "text", text: output }] : [],
|
|
192
|
+
...(message.timestamp !== undefined ? { timestamp: message.timestamp } : {}),
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
150
197
|
writeRecord({
|
|
151
198
|
...baseRecord("message"),
|
|
152
199
|
sourceEventType,
|
|
@@ -178,11 +225,14 @@ export function createChildTranscriptWriter(input: ChildTranscriptWriterInput):
|
|
|
178
225
|
}
|
|
179
226
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
180
227
|
const args = eventArgs(event);
|
|
228
|
+
const argsPayload = boundedPayload(args);
|
|
181
229
|
writeRecord({
|
|
182
230
|
...baseRecord("tool_start"),
|
|
183
231
|
sourceEventType: event.type,
|
|
232
|
+
...(event.toolCallId ? { toolCallId: event.toolCallId } : {}),
|
|
184
233
|
toolName: event.toolName,
|
|
185
234
|
...(Object.keys(args).length > 0 ? { argsPreview: extractToolArgsPreview(args) } : {}),
|
|
235
|
+
...(argsPayload ? { argsPayload } : {}),
|
|
186
236
|
});
|
|
187
237
|
return;
|
|
188
238
|
}
|
|
@@ -190,7 +240,9 @@ export function createChildTranscriptWriter(input: ChildTranscriptWriterInput):
|
|
|
190
240
|
writeRecord({
|
|
191
241
|
...baseRecord("tool_end"),
|
|
192
242
|
sourceEventType: event.type,
|
|
243
|
+
...(event.toolCallId ? { toolCallId: event.toolCallId } : {}),
|
|
193
244
|
...(event.toolName ? { toolName: event.toolName } : {}),
|
|
245
|
+
...(typeof event.isError === "boolean" ? { isError: event.isError } : {}),
|
|
194
246
|
});
|
|
195
247
|
}
|
|
196
248
|
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Preserve pi-subagents v0.33 and earlier environment configuration. */
|
|
2
|
+
const LEGACY_PREFIX = /^PI_SUBAGENTS?_/;
|
|
3
|
+
|
|
4
|
+
export function normalizeLegacySubagentEnv(env: NodeJS.ProcessEnv = process.env): void {
|
|
5
|
+
for (const [name, value] of Object.entries(env)) {
|
|
6
|
+
if (value === undefined || !LEGACY_PREFIX.test(name)) continue;
|
|
7
|
+
const selesaiName = name.replace(LEGACY_PREFIX, (prefix) => prefix === "PI_SUBAGENTS_" ? "SELESAI_SUBAGENTS_" : "SELESAI_SUBAGENT_");
|
|
8
|
+
if (env[selesaiName] === undefined) env[selesaiName] = value;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
normalizeLegacySubagentEnv();
|
|
13
|
+
|
|
14
|
+
export function readSubagentEnv(env: NodeJS.ProcessEnv, selesaiName: string): string | undefined {
|
|
15
|
+
return env[selesaiName] ?? env[selesaiName.replace(/^SELESAI_/, "PI_")];
|
|
16
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const WAIT_BUFFER = typeof SharedArrayBuffer !== "undefined" ? new SharedArrayBuffer(4) : undefined;
|
|
2
|
+
const WAIT_VIEW = WAIT_BUFFER ? new Int32Array(WAIT_BUFFER) : undefined;
|
|
3
|
+
const RETRYABLE_FILE_SYSTEM_ERROR_CODES = new Set(["EACCES", "EBUSY", "EPERM"]);
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS = [10, 25, 50, 100, 200, 500, 1000, 2000, 4000] as const;
|
|
6
|
+
|
|
7
|
+
export type FileSystemRetryOptions = {
|
|
8
|
+
retryDelaysMs?: readonly number[];
|
|
9
|
+
wait?: (delayMs: number) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function waitForFileSystemRetry(delayMs: number): void {
|
|
13
|
+
if (delayMs <= 0) return;
|
|
14
|
+
if (WAIT_VIEW) {
|
|
15
|
+
try {
|
|
16
|
+
// Callers are synchronous status/result writers; Atomics.wait gives
|
|
17
|
+
// Windows directory and rename locks time to clear without burning CPU.
|
|
18
|
+
Atomics.wait(WAIT_VIEW, 0, 0, delayMs);
|
|
19
|
+
return;
|
|
20
|
+
} catch {
|
|
21
|
+
// Fall through to the portable busy wait below.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const end = Date.now() + delayMs;
|
|
25
|
+
while (Date.now() < end) {
|
|
26
|
+
// Portable fallback for runtimes where Atomics.wait is unavailable.
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isRetryableFileSystemError(error: unknown): boolean {
|
|
31
|
+
const code = (error as NodeJS.ErrnoException | undefined)?.code;
|
|
32
|
+
return typeof code === "string" && RETRYABLE_FILE_SYSTEM_ERROR_CODES.has(code);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function runFileSystemOperationWithRetry<T>(operation: () => T, options: FileSystemRetryOptions = {}): T {
|
|
36
|
+
const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS;
|
|
37
|
+
const wait = options.wait ?? waitForFileSystemRetry;
|
|
38
|
+
for (let attempt = 0; ; attempt++) {
|
|
39
|
+
try {
|
|
40
|
+
return operation();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const delayMs = retryDelaysMs[attempt];
|
|
43
|
+
if (delayMs === undefined || !isRetryableFileSystemError(error)) throw error;
|
|
44
|
+
wait(delayMs);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { SessionManager } from "@selesai/code";
|
|
5
|
+
import { findModelInfo, type ModelInfo } from "./model-info.ts";
|
|
5
6
|
|
|
6
7
|
type SubagentExecutionContext = "fresh" | "fork";
|
|
7
8
|
|
|
@@ -35,6 +36,9 @@ interface ForkableSessionManager {
|
|
|
35
36
|
|
|
36
37
|
interface ForkContextResolverOptions {
|
|
37
38
|
openSession?: (path: string, sessionDir?: string) => BranchSessionManager;
|
|
39
|
+
/** Decide per child index whether a sanitized transcript must also disable the child's
|
|
40
|
+
* thinking. Defaults to true (the pre-existing conservative behavior) when omitted. */
|
|
41
|
+
forceThinkingOffForIndex?: (index: number) => boolean;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
interface ForkContextResolution {
|
|
@@ -51,6 +55,20 @@ export function resolveSubagentContext(value: unknown): SubagentExecutionContext
|
|
|
51
55
|
return value === "fork" ? "fork" : "fresh";
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
/** Decide whether a resolved child model uses Anthropic's provider or message API, which
|
|
59
|
+
* requires the sanitized fork to disable thinking. Unknown models stay conservative. */
|
|
60
|
+
export function forkedChildRequiresThinkingOff(
|
|
61
|
+
model: string | undefined,
|
|
62
|
+
availableModels?: ModelInfo[],
|
|
63
|
+
preferredProvider?: string,
|
|
64
|
+
): boolean {
|
|
65
|
+
if (!model) return true;
|
|
66
|
+
const info = findModelInfo(model, availableModels, preferredProvider);
|
|
67
|
+
if (!info) return true;
|
|
68
|
+
return info.provider.toLowerCase() === "anthropic"
|
|
69
|
+
|| info.api?.toLowerCase() === "anthropic-messages";
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
function isUnsafeAnthropicThinkingBlock(message: BranchSessionEntry["message"], block: unknown): boolean {
|
|
55
73
|
if (!message || !block || typeof block !== "object" || !("type" in block)) return false;
|
|
56
74
|
const provider = typeof message.provider === "string" ? message.provider.toLowerCase() : "";
|
|
@@ -94,7 +112,6 @@ function sanitizeUnsafeThinkingBlocks(entries: BranchSessionEntry[]): boolean {
|
|
|
94
112
|
entry.message.content = filtered;
|
|
95
113
|
sanitized = true;
|
|
96
114
|
}
|
|
97
|
-
if (sanitized) appendThinkingOffEntry(entries);
|
|
98
115
|
return sanitized;
|
|
99
116
|
}
|
|
100
117
|
|
|
@@ -150,6 +167,8 @@ export function createForkContextResolver(
|
|
|
150
167
|
if (!sessionFile) {
|
|
151
168
|
throw new Error("Session manager did not return a forked session file.");
|
|
152
169
|
}
|
|
170
|
+
const forceThinkingOff = (sanitized: boolean): boolean =>
|
|
171
|
+
sanitized && (options.forceThinkingOffForIndex?.(index) ?? true);
|
|
153
172
|
let thinkingOverride: "off" | undefined;
|
|
154
173
|
if (!fs.existsSync(sessionFile)) {
|
|
155
174
|
const header = sourceManager.getHeader?.();
|
|
@@ -157,13 +176,19 @@ export function createForkContextResolver(
|
|
|
157
176
|
if (!header || !entries) {
|
|
158
177
|
throw new Error(`Session manager returned a forked session file that does not exist and cannot be persisted by fallback: ${sessionFile}`);
|
|
159
178
|
}
|
|
160
|
-
if (sanitizeUnsafeThinkingBlocks(entries))
|
|
179
|
+
if (forceThinkingOff(sanitizeUnsafeThinkingBlocks(entries))) {
|
|
180
|
+
appendThinkingOffEntry(entries);
|
|
181
|
+
thinkingOverride = "off";
|
|
182
|
+
}
|
|
161
183
|
fs.mkdirSync(path.dirname(sessionFile), { recursive: true });
|
|
162
184
|
fs.writeFileSync(sessionFile, `${[header, ...entries].map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf-8");
|
|
163
185
|
} else {
|
|
164
186
|
const entries = readSessionEntries(sessionFile);
|
|
165
187
|
if (sanitizeUnsafeThinkingBlocks(entries)) {
|
|
166
|
-
|
|
188
|
+
if (forceThinkingOff(true)) {
|
|
189
|
+
appendThinkingOffEntry(entries);
|
|
190
|
+
thinkingOverride = "off";
|
|
191
|
+
}
|
|
167
192
|
fs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf-8");
|
|
168
193
|
}
|
|
169
194
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import type { AgentConfig } from "../agents/agents.ts";
|
|
4
|
+
|
|
5
|
+
export const AGENT_DEFINITION_PROJECTION_VERSION = 1 as const;
|
|
6
|
+
export const LAUNCH_BINDING_PROJECTION_VERSION = 1 as const;
|
|
7
|
+
|
|
8
|
+
function stableJson(value: unknown): string {
|
|
9
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
10
|
+
if (value && typeof value === "object") {
|
|
11
|
+
return `{${Object.entries(value as Record<string, unknown>)
|
|
12
|
+
.filter(([, entry]) => entry !== undefined)
|
|
13
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
14
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
15
|
+
.join(",")}}`;
|
|
16
|
+
}
|
|
17
|
+
return JSON.stringify(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function sha256(value: unknown): string {
|
|
21
|
+
return createHash("sha256").update(stableJson(value)).digest("hex");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function fileDigest(filePath: string): string | undefined {
|
|
25
|
+
try {
|
|
26
|
+
return createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
27
|
+
} catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Public-safe, deterministic evidence for the parsed launch-affecting agent definition. */
|
|
33
|
+
export function projectAgentDefinition(agent: AgentConfig): Record<string, unknown> {
|
|
34
|
+
return {
|
|
35
|
+
version: AGENT_DEFINITION_PROJECTION_VERSION,
|
|
36
|
+
name: agent.name,
|
|
37
|
+
localName: agent.localName,
|
|
38
|
+
packageName: agent.packageName,
|
|
39
|
+
filePath: agent.filePath,
|
|
40
|
+
fileContentDigest: fileDigest(agent.filePath),
|
|
41
|
+
systemPrompt: agent.systemPrompt,
|
|
42
|
+
systemPromptMode: agent.systemPromptMode,
|
|
43
|
+
inheritProjectContext: agent.inheritProjectContext,
|
|
44
|
+
inheritSkills: agent.inheritSkills,
|
|
45
|
+
model: agent.model,
|
|
46
|
+
fallbackModels: agent.fallbackModels,
|
|
47
|
+
thinking: agent.thinking,
|
|
48
|
+
tools: agent.tools,
|
|
49
|
+
mcpDirectTools: agent.mcpDirectTools,
|
|
50
|
+
extensions: agent.extensions,
|
|
51
|
+
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
52
|
+
skills: agent.skills,
|
|
53
|
+
skillPath: agent.skillPath,
|
|
54
|
+
output: agent.output,
|
|
55
|
+
defaultReads: agent.defaultReads,
|
|
56
|
+
defaultProgress: agent.defaultProgress,
|
|
57
|
+
defaultContext: agent.defaultContext,
|
|
58
|
+
defaultAsync: agent.defaultAsync,
|
|
59
|
+
defaultTimeoutMs: agent.defaultTimeoutMs,
|
|
60
|
+
defaultTurnBudget: agent.defaultTurnBudget,
|
|
61
|
+
defaultAcceptance: agent.defaultAcceptance,
|
|
62
|
+
acceptanceRole: agent.acceptanceRole,
|
|
63
|
+
interactive: agent.interactive,
|
|
64
|
+
maxSubagentDepth: agent.maxSubagentDepth,
|
|
65
|
+
completionGuard: agent.completionGuard,
|
|
66
|
+
toolBudget: agent.toolBudget,
|
|
67
|
+
memory: agent.memory,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function agentDefinitionDigest(agent: AgentConfig): string {
|
|
72
|
+
return sha256(projectAgentDefinition(agent));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface LaunchBindingInput {
|
|
76
|
+
definitionDigest: string;
|
|
77
|
+
/** Caller task; runtime acceptance/output task annotations are explicitly outside the preflight-known subset. */
|
|
78
|
+
task?: string;
|
|
79
|
+
model?: string;
|
|
80
|
+
modelCandidates?: string[];
|
|
81
|
+
thinking?: string;
|
|
82
|
+
systemPrompt?: string | null;
|
|
83
|
+
systemPromptMode?: AgentConfig["systemPromptMode"];
|
|
84
|
+
inheritProjectContext: boolean;
|
|
85
|
+
inheritSkills: boolean;
|
|
86
|
+
skills?: string[];
|
|
87
|
+
tools?: string[];
|
|
88
|
+
extensions?: string[];
|
|
89
|
+
subagentOnlyExtensions?: string[];
|
|
90
|
+
mcpDirectTools?: string[];
|
|
91
|
+
outputPath?: string;
|
|
92
|
+
outputMode?: string;
|
|
93
|
+
structuredOutputSchema?: unknown;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Canonical projection of the resolved inputs handed to the child. */
|
|
97
|
+
export function projectLaunchBinding(input: LaunchBindingInput): Record<string, unknown> {
|
|
98
|
+
return {
|
|
99
|
+
version: LAUNCH_BINDING_PROJECTION_VERSION,
|
|
100
|
+
definitionDigest: input.definitionDigest,
|
|
101
|
+
taskDigest: input.task === undefined ? undefined : sha256(input.task),
|
|
102
|
+
// The ordered candidate set already contains each attempted model; keeping only
|
|
103
|
+
// this set makes retries correlate to the same preflight binding.
|
|
104
|
+
modelCandidates: input.modelCandidates,
|
|
105
|
+
thinking: input.thinking,
|
|
106
|
+
systemPromptDigest: input.systemPrompt === undefined || input.systemPrompt === null ? undefined : sha256(input.systemPrompt),
|
|
107
|
+
systemPromptMode: input.systemPromptMode,
|
|
108
|
+
inheritProjectContext: input.inheritProjectContext,
|
|
109
|
+
inheritSkills: input.inheritSkills,
|
|
110
|
+
skills: input.skills,
|
|
111
|
+
tools: input.tools,
|
|
112
|
+
extensions: input.extensions,
|
|
113
|
+
subagentOnlyExtensions: input.subagentOnlyExtensions,
|
|
114
|
+
mcpDirectTools: input.mcpDirectTools,
|
|
115
|
+
outputPath: input.outputPath,
|
|
116
|
+
outputMode: input.outputMode,
|
|
117
|
+
structuredOutputSchema: input.structuredOutputSchema,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function launchBindingDigest(input: LaunchBindingInput): string {
|
|
122
|
+
return sha256(projectLaunchBinding(input));
|
|
123
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
1
|
+
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
2
2
|
export type ThinkingLevel = typeof THINKING_LEVELS[number];
|
|
3
3
|
export type ThinkingLevelMap = Partial<Record<ThinkingLevel, string | null>>;
|
|
4
4
|
|
|
@@ -6,6 +6,7 @@ export interface ModelInfo {
|
|
|
6
6
|
provider: string;
|
|
7
7
|
id: string;
|
|
8
8
|
fullId: string;
|
|
9
|
+
api?: string;
|
|
9
10
|
reasoning?: boolean;
|
|
10
11
|
thinkingLevelMap?: ThinkingLevelMap;
|
|
11
12
|
}
|
|
@@ -13,6 +14,7 @@ export interface ModelInfo {
|
|
|
13
14
|
interface RegistryModelLike {
|
|
14
15
|
provider: string;
|
|
15
16
|
id: string;
|
|
17
|
+
api?: string;
|
|
16
18
|
reasoning?: boolean;
|
|
17
19
|
thinkingLevelMap?: ThinkingLevelMap;
|
|
18
20
|
}
|
|
@@ -22,6 +24,7 @@ export function toModelInfo(model: RegistryModelLike): ModelInfo {
|
|
|
22
24
|
provider: model.provider,
|
|
23
25
|
id: model.id,
|
|
24
26
|
fullId: `${model.provider}/${model.id}`,
|
|
27
|
+
api: model.api,
|
|
25
28
|
reasoning: model.reasoning,
|
|
26
29
|
thinkingLevelMap: model.thinkingLevelMap,
|
|
27
30
|
};
|
|
@@ -30,7 +33,7 @@ export function toModelInfo(model: RegistryModelLike): ModelInfo {
|
|
|
30
33
|
/** Resolve the effective thinking level from a model string (which may contain a known suffix like `:high`)
|
|
31
34
|
* and an explicit thinking config value. Returns `undefined` when no thinking is applicable
|
|
32
35
|
* (e.g. no model was specified, or the model has no suffix and no config was provided). */
|
|
33
|
-
export function resolveEffectiveThinking(model: string | undefined, configThinking: string | undefined): string | undefined {
|
|
36
|
+
export function resolveEffectiveThinking(model: string | undefined, configThinking: string | false | undefined): string | undefined {
|
|
34
37
|
if (!model) return undefined;
|
|
35
38
|
const { thinkingSuffix } = splitKnownThinkingSuffix(model);
|
|
36
39
|
if (thinkingSuffix) return thinkingSuffix.slice(1);
|
|
@@ -63,15 +66,15 @@ export function findModelInfo(model: string | undefined, availableModels: ModelI
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export function getSupportedThinkingLevels(model: ModelInfo | undefined): ThinkingLevel[] {
|
|
66
|
-
if (!model) return
|
|
69
|
+
if (!model) return THINKING_LEVELS.filter((level) => level !== "max");
|
|
67
70
|
if (model.reasoning === false) return ["off"];
|
|
68
71
|
|
|
69
|
-
if (!model.thinkingLevelMap) return
|
|
72
|
+
if (!model.thinkingLevelMap) return THINKING_LEVELS.filter((level) => level !== "max");
|
|
70
73
|
|
|
71
74
|
const levels = THINKING_LEVELS.filter((level) => {
|
|
72
75
|
const mapped = model.thinkingLevelMap?.[level];
|
|
73
76
|
if (mapped === null) return false;
|
|
74
|
-
if (level === "xhigh") return mapped !== undefined;
|
|
77
|
+
if (level === "xhigh" || level === "max") return mapped !== undefined;
|
|
75
78
|
return true;
|
|
76
79
|
});
|
|
77
80
|
return levels;
|
|
@@ -6,7 +6,7 @@ import * as fs from "node:fs";
|
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import type { AgentConfig } from "../agents/agents.ts";
|
|
8
8
|
import { normalizeSkillInput } from "../agents/skills.ts";
|
|
9
|
-
import { CHAIN_RUNS_DIR, type AcceptanceInput, type JsonSchemaObject, type OutputMode, type ToolBudgetConfig } from "./types.ts";
|
|
9
|
+
import { CHAIN_RUNS_DIR, type AcceptanceInput, type AgentContract, type ChainGateLayer, type JsonSchemaObject, type OutputMode, type ToolBudgetConfig } from "./types.ts";
|
|
10
10
|
const CHAIN_DIR_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
11
11
|
const INITIAL_PROGRESS_CONTENT = "# Progress\n\n## Status\nIn Progress\n\n## Tasks\n\n## Files Changed\n\n## Notes\n";
|
|
12
12
|
|
|
@@ -57,6 +57,8 @@ export interface SequentialStep {
|
|
|
57
57
|
model?: string;
|
|
58
58
|
toolBudget?: ToolBudgetConfig;
|
|
59
59
|
acceptance?: AcceptanceInput;
|
|
60
|
+
agentContract?: AgentContract;
|
|
61
|
+
gateOn?: ChainGateLayer;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
/** Parallel task item within a parallel step */
|
|
@@ -77,6 +79,8 @@ export interface ParallelTaskItem {
|
|
|
77
79
|
model?: string;
|
|
78
80
|
toolBudget?: ToolBudgetConfig;
|
|
79
81
|
acceptance?: AcceptanceInput;
|
|
82
|
+
agentContract?: AgentContract;
|
|
83
|
+
gateOn?: ChainGateLayer;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
export interface DynamicExpandSpec {
|
|
@@ -106,6 +110,8 @@ export interface DynamicParallelStep {
|
|
|
106
110
|
phase?: string;
|
|
107
111
|
label?: string;
|
|
108
112
|
acceptance?: AcceptanceInput;
|
|
113
|
+
agentContract?: AgentContract;
|
|
114
|
+
gateOn?: ChainGateLayer;
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
/** Parallel step: multiple agents running concurrently */
|
|
@@ -115,6 +121,8 @@ export interface ParallelStep {
|
|
|
115
121
|
failFast?: boolean;
|
|
116
122
|
worktree?: boolean;
|
|
117
123
|
cwd?: string;
|
|
124
|
+
agentContract?: AgentContract;
|
|
125
|
+
gateOn?: ChainGateLayer;
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
/** Union type for chain steps */
|
|
@@ -16,7 +16,10 @@ export function formatActivityLabel(lastActivityAt: number | undefined, activity
|
|
|
16
16
|
}
|
|
17
17
|
const age = formatActivityAge(Math.max(0, now - lastActivityAt));
|
|
18
18
|
if (activityState === "needs_attention") return `no activity for ${age}`;
|
|
19
|
-
if (activityState === "active_long_running")
|
|
19
|
+
if (activityState === "active_long_running") {
|
|
20
|
+
const activityAge = age === "now" ? "now" : `${age} ago`;
|
|
21
|
+
return `active but long-running · last activity ${activityAge}`;
|
|
22
|
+
}
|
|
20
23
|
return age === "now" ? "active now" : `active ${age} ago`;
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -27,6 +30,7 @@ function isCompletedStepStatus(status: AsyncJobStep["status"]): boolean {
|
|
|
27
30
|
export function aggregateStepStatus(steps: StepStatusLike[]): AsyncJobStep["status"] {
|
|
28
31
|
if (steps.some((step) => step.status === "running")) return "running";
|
|
29
32
|
if (steps.some((step) => step.status === "failed")) return "failed";
|
|
33
|
+
if (steps.some((step) => step.status === "stopped")) return "stopped";
|
|
30
34
|
if (steps.some((step) => step.status === "paused")) return "paused";
|
|
31
35
|
if (steps.length > 0 && steps.every((step) => isCompletedStepStatus(step.status))) return "complete";
|
|
32
36
|
return "pending";
|
|
@@ -40,10 +44,12 @@ export function formatParallelOutcome(steps: StepStatusLike[], total: number, op
|
|
|
40
44
|
const running = steps.filter((step) => step.status === "running").length;
|
|
41
45
|
const done = steps.filter((step) => isCompletedStepStatus(step.status)).length;
|
|
42
46
|
const failed = steps.filter((step) => step.status === "failed").length;
|
|
47
|
+
const stopped = steps.filter((step) => step.status === "stopped").length;
|
|
43
48
|
const paused = steps.filter((step) => step.status === "paused").length;
|
|
44
49
|
const parts = [`${done}/${total} done`];
|
|
45
50
|
if (options.showRunning !== false && running > 0) parts.unshift(formatAgentRunningLabel(running));
|
|
46
51
|
if (failed > 0) parts.push(`${failed} failed`);
|
|
52
|
+
if (stopped > 0) parts.push(`${stopped} stopped`);
|
|
47
53
|
if (paused > 0) parts.push(`${paused} paused`);
|
|
48
54
|
return parts.join(" · ");
|
|
49
55
|
}
|