@selesai/code 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.js +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/extensions/ascii.txt +9 -0
- package/dist/extensions/package.json +1 -2
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
- package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
- package/dist/extensions/pi-hermes-memory/README.md +643 -0
- package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
- package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
- package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
- package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
- package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
- package/dist/extensions/pi-hermes-memory/package.json +66 -0
- package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
- package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
- package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
- package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
- package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
- package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
- package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
- package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
- package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
- package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
- package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
- package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
- package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
- package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
- package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
- package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
- package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
- package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
- package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
- package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
- package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
- package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
- package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
- package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
- package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
- package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
- package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
- package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
- package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
- package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
- package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
- package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
- package/dist/extensions/pi-subagents/AGENTS.md +5 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
- package/dist/extensions/pi-subagents/README.md +12 -6
- package/dist/extensions/pi-subagents/VISION.md +81 -0
- package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
- package/dist/extensions/pi-subagents/docs/agents.md +488 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
- package/dist/extensions/pi-subagents/docs/models.md +27 -4
- package/dist/extensions/pi-subagents/docs/observability.md +10 -9
- package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
- package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
- package/dist/extensions/pi-subagents/package-lock.json +389 -2
- package/dist/extensions/pi-subagents/package.json +3 -1
- package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
- package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
- package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
- package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
- package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
- package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
- package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
- package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
- package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
- package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
- package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
- package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
- package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
- package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
- package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
- package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
- package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
- package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
- package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
- package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
- package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
- package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
- package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
- package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
- package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
- package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
- package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
- package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
- package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
- package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
- package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
- package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
- package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
- package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
- package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
- package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
- package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -7
- package/dist/skills/workflow/SKILL.md +65 -50
- package/package.json +112 -110
- package/dist/extensions/enable-readonly-tools.test.ts +0 -63
- package/dist/extensions/enable-readonly-tools.ts +0 -24
- package/dist/extensions/model-prompt-injector/config.json +0 -16
- package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
- package/dist/extensions/model-prompt-injector/index.ts +0 -148
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { writePrivateAtomicJson } from "../shared/atomic-json.ts";
|
|
4
|
-
import type { WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState } from "../shared/types.ts";
|
|
4
|
+
import type { ExternalCliReceiptMetadata, WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState, WorkflowRecoveryAction, WorkflowTerminalOutcome, WorkflowTerminalResolution } from "../shared/types.ts";
|
|
5
5
|
import type { WorkflowReceiptResumeReference, WorkflowScriptChildResult } from "./scripted-workflow.ts";
|
|
6
|
+
import { parseWorkflowChildSummary } from "./workflow-child-summary.ts";
|
|
7
|
+
import { HOST_STEP_MAX_COUNT, assertUniqueHostStepIds, parseHostStepNode } from "../runs/shared/host-step-status.ts";
|
|
8
|
+
import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../runs/shared/lane-metadata.ts";
|
|
6
9
|
|
|
7
10
|
export type { WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState } from "../shared/types.ts";
|
|
8
11
|
|
|
9
12
|
export const WORKFLOW_RECEIPT_VERSION = 1;
|
|
10
13
|
export const WORKFLOW_RECEIPT_FILE = "workflow-receipt.json";
|
|
14
|
+
const MAX_WORKFLOW_RECEIPT_BYTES = 2 * 1024 * 1024;
|
|
11
15
|
|
|
12
16
|
const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
13
17
|
|
|
@@ -32,30 +36,42 @@ export function buildWorkflowReceipt(input: {
|
|
|
32
36
|
workflowRunId: string;
|
|
33
37
|
state: WorkflowReceiptState;
|
|
34
38
|
children: WorkflowScriptChildResult[];
|
|
39
|
+
hostSteps?: WorkflowReceipt["hostSteps"];
|
|
40
|
+
workflowChildren?: WorkflowReceipt["workflowChildren"];
|
|
41
|
+
terminalOutcome?: WorkflowTerminalOutcome;
|
|
35
42
|
createdAt?: number;
|
|
36
43
|
}): WorkflowReceipt {
|
|
37
44
|
const workflowRunId = assertSafeRunId(input.workflowRunId, "workflowRunId");
|
|
45
|
+
if (input.workflowChildren?.workflowRunId !== undefined && input.workflowChildren.workflowRunId !== workflowRunId) throw new Error("workflowChildren workflowRunId does not match its receipt.");
|
|
38
46
|
const entries: Record<string, WorkflowReceiptEntry> = Object.create(null) as Record<string, WorkflowReceiptEntry>;
|
|
39
47
|
for (const child of input.children) {
|
|
40
48
|
const key = assertKey(child.key, "workflow receipt child key");
|
|
41
49
|
if (entries[key]) throw new Error(`Workflow receipt has duplicate child key '${key}'.`);
|
|
50
|
+
const lane = normalizeWorkflowLaneMetadata(child.lane, `workflow receipt child '${key}'.lane`);
|
|
51
|
+
assertWorkflowLaneKey(lane, key, `workflow receipt child '${key}'.lane`);
|
|
42
52
|
const runIds = [...new Set((child.continuation?.runIds ?? (child.runId ? [child.runId] : [])).filter((runId) => typeof runId === "string" && runId.trim()).map((runId) => runId.trim()))];
|
|
43
53
|
const latestRunId = runIds.at(-1);
|
|
44
54
|
const resumability = child.resumability ?? { state: "not-resumable", reason: child.runId ? "resumability was not recorded" : "child produced no run id" };
|
|
45
55
|
if (resumability.state === "resumable" && !latestRunId) throw new Error(`Workflow receipt child '${key}' is resumable but has no retained run id.`);
|
|
46
56
|
const base = {
|
|
47
57
|
key,
|
|
58
|
+
...(lane ? { lane } : {}),
|
|
59
|
+
...(child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}),
|
|
48
60
|
...(child.agent ? { agent: child.agent } : {}),
|
|
49
61
|
...(child.requestedContext ? { requestedContext: child.requestedContext } : {}),
|
|
50
62
|
...(child.resolvedContext ? { resolvedContext: child.resolvedContext } : {}),
|
|
51
63
|
...(child.outputReference ? { outputReference: child.outputReference } : {}),
|
|
64
|
+
...(child.externalAdapter ? { externalAdapter: child.externalAdapter } : {}),
|
|
52
65
|
continuation: { runIds },
|
|
53
66
|
};
|
|
54
67
|
entries[key] = resumability.state === "resumable"
|
|
55
68
|
? { ...base, latestRunId: latestRunId!, resumability }
|
|
56
69
|
: { ...base, ...(latestRunId ? { latestRunId } : {}), resumability };
|
|
57
70
|
}
|
|
58
|
-
|
|
71
|
+
if (input.hostSteps && input.hostSteps.length > HOST_STEP_MAX_COUNT) throw new Error(`Workflow receipt has more than ${HOST_STEP_MAX_COUNT} host steps.`);
|
|
72
|
+
const hostSteps = input.hostSteps?.map((hostStep, index) => parseHostStepNode(hostStep, `workflow receipt hostSteps[${index}]`));
|
|
73
|
+
if (hostSteps) assertUniqueHostStepIds(hostSteps, "workflow receipt");
|
|
74
|
+
return { version: WORKFLOW_RECEIPT_VERSION, workflowRunId, state: input.state, createdAt: input.createdAt ?? Date.now(), entries, ...(hostSteps?.length ? { hostSteps } : {}), ...(input.workflowChildren ? { workflowChildren: input.workflowChildren } : {}), ...(input.terminalOutcome ? { terminalOutcome: input.terminalOutcome } : {}) };
|
|
59
75
|
}
|
|
60
76
|
|
|
61
77
|
export function writeWorkflowReceipt(asyncDir: string, receipt: WorkflowReceipt): string {
|
|
@@ -64,6 +80,136 @@ export function writeWorkflowReceipt(asyncDir: string, receipt: WorkflowReceipt)
|
|
|
64
80
|
return receiptPath;
|
|
65
81
|
}
|
|
66
82
|
|
|
83
|
+
function readWorkflowReceiptFile(receiptPath: string): unknown {
|
|
84
|
+
let fd: number | undefined;
|
|
85
|
+
try {
|
|
86
|
+
fd = fs.openSync(receiptPath, "r");
|
|
87
|
+
const stat = fs.fstatSync(fd);
|
|
88
|
+
if (!stat.isFile()) throw new Error("workflow receipt is not a regular file.");
|
|
89
|
+
if (stat.size > MAX_WORKFLOW_RECEIPT_BYTES) throw new Error(`workflow receipt exceeds the ${MAX_WORKFLOW_RECEIPT_BYTES}-byte limit.`);
|
|
90
|
+
const buffer = Buffer.allocUnsafe(stat.size);
|
|
91
|
+
let offset = 0;
|
|
92
|
+
while (offset < buffer.length) {
|
|
93
|
+
const bytesRead = fs.readSync(fd, buffer, offset, buffer.length - offset, null);
|
|
94
|
+
if (bytesRead <= 0) throw new Error("workflow receipt ended before its declared size.");
|
|
95
|
+
offset += bytesRead;
|
|
96
|
+
}
|
|
97
|
+
return JSON.parse(buffer.toString("utf-8")) as unknown;
|
|
98
|
+
} finally {
|
|
99
|
+
if (fd !== undefined) fs.closeSync(fd);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const EXTERNAL_CLI_CAPABILITIES = {
|
|
104
|
+
stop: true,
|
|
105
|
+
steer: false,
|
|
106
|
+
resume: false,
|
|
107
|
+
structuredOutput: false,
|
|
108
|
+
toolEvents: false,
|
|
109
|
+
supervisor: "unsupported",
|
|
110
|
+
forkContext: false,
|
|
111
|
+
extensionBindings: false,
|
|
112
|
+
} as const;
|
|
113
|
+
|
|
114
|
+
function parseExternalCliReceiptMetadata(value: unknown, key: string, source: string): ExternalCliReceiptMetadata | undefined {
|
|
115
|
+
if (value === undefined) return undefined;
|
|
116
|
+
const label = `Invalid workflow receipt '${source}': entry '${key}' externalAdapter`;
|
|
117
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
|
|
118
|
+
const metadata = value as Record<string, unknown>;
|
|
119
|
+
const unknownMetadata = Object.keys(metadata).filter((field) => !["adapter", "capabilities", "safety", "outputArtifacts", "handoff", "supervisor", "nonResumableReason"].includes(field));
|
|
120
|
+
if (unknownMetadata.length > 0) throw new Error(`${label} has unsupported fields: ${unknownMetadata.join(", ")}.`);
|
|
121
|
+
const adapter = metadata.adapter;
|
|
122
|
+
if (!adapter || typeof adapter !== "object" || Array.isArray(adapter)) throw new Error(`${label}.adapter must be an object.`);
|
|
123
|
+
const adapterRecord = adapter as Record<string, unknown>;
|
|
124
|
+
const unknownAdapter = Object.keys(adapterRecord).filter((field) => !["id", "version", "executionMode"].includes(field));
|
|
125
|
+
if (unknownAdapter.length > 0) throw new Error(`${label}.adapter has unsupported fields: ${unknownAdapter.join(", ")}.`);
|
|
126
|
+
if ((adapterRecord.id !== "external-cli" && adapterRecord.id !== "codex-exec" && adapterRecord.id !== "codex-exec-writer" && adapterRecord.id !== "claude-code" && adapterRecord.id !== "claude-code-writer" && adapterRecord.id !== "cursor-agent" && adapterRecord.id !== "cursor-agent-writer" && adapterRecord.id !== "grok-build") || adapterRecord.version !== 1 || adapterRecord.executionMode !== (adapterRecord.id === "cursor-agent" || adapterRecord.id === "cursor-agent-writer" || adapterRecord.id === "grok-build" ? "one-shot-prompt-file" : "one-shot-stdin")) throw new Error(`${label}.adapter is invalid.`);
|
|
127
|
+
const capabilities = metadata.capabilities;
|
|
128
|
+
if (!capabilities || typeof capabilities !== "object" || Array.isArray(capabilities)) throw new Error(`${label}.capabilities must be an object.`);
|
|
129
|
+
const capabilityRecord = capabilities as Record<string, unknown>;
|
|
130
|
+
const unknownCapabilities = Object.keys(capabilityRecord).filter((field) => !(field in EXTERNAL_CLI_CAPABILITIES));
|
|
131
|
+
if (unknownCapabilities.length > 0) throw new Error(`${label}.capabilities has unsupported fields: ${unknownCapabilities.join(", ")}.`);
|
|
132
|
+
for (const [capability, expected] of Object.entries(EXTERNAL_CLI_CAPABILITIES)) {
|
|
133
|
+
if (capabilityRecord[capability] !== expected) throw new Error(`${label}.capabilities.${capability} is invalid.`);
|
|
134
|
+
}
|
|
135
|
+
const safety = metadata.safety;
|
|
136
|
+
if (adapterRecord.id === "codex-exec") {
|
|
137
|
+
if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
|
|
138
|
+
const safetyRecord = safety as Record<string, unknown>;
|
|
139
|
+
const unknownSafety = Object.keys(safetyRecord).filter((field) => !["sandbox", "approvalPolicy", "ephemeral"].includes(field));
|
|
140
|
+
if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
|
|
141
|
+
if (safetyRecord.sandbox !== "read-only" || safetyRecord.approvalPolicy !== "never" || safetyRecord.ephemeral !== true) throw new Error(`${label}.safety is invalid.`);
|
|
142
|
+
} else if (adapterRecord.id === "codex-exec-writer") {
|
|
143
|
+
if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
|
|
144
|
+
const safetyRecord = safety as Record<string, unknown>;
|
|
145
|
+
const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "sandbox", "approvalPolicy", "ephemeral"].includes(field));
|
|
146
|
+
if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
|
|
147
|
+
if (safetyRecord.access !== "workspace-write" || safetyRecord.sandbox !== "workspace-write" || safetyRecord.approvalPolicy !== "never" || safetyRecord.ephemeral !== true) throw new Error(`${label}.safety is invalid.`);
|
|
148
|
+
} else if (adapterRecord.id === "claude-code") {
|
|
149
|
+
if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
|
|
150
|
+
const safetyRecord = safety as Record<string, unknown>;
|
|
151
|
+
const legacy = safetyRecord.authentication === undefined;
|
|
152
|
+
const unknownSafety = Object.keys(safetyRecord).filter((field) => !(legacy
|
|
153
|
+
? ["permissionMode", "tools", "mcp", "settingSources", "sessionPersistence"]
|
|
154
|
+
: ["access", "authentication", "permissionMode", "tools", "mcp", "settingSources", "userSettingsTrust", "sessionPersistence"]).includes(field));
|
|
155
|
+
if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
|
|
156
|
+
if (legacy) {
|
|
157
|
+
if (safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "none" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "none" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
|
|
158
|
+
} else if (safetyRecord.access !== "read-only" || safetyRecord.authentication !== "existing-cli-required" || safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "none" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "user" || safetyRecord.userSettingsTrust !== "required" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
|
|
159
|
+
} else if (adapterRecord.id === "claude-code-writer") {
|
|
160
|
+
if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
|
|
161
|
+
const safetyRecord = safety as Record<string, unknown>;
|
|
162
|
+
const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "permissionMode", "tools", "mcp", "settingSources", "userSettingsTrust", "sessionPersistence"].includes(field));
|
|
163
|
+
if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
|
|
164
|
+
if (safetyRecord.access !== "workspace-write" || safetyRecord.authentication !== "existing-cli-required" || safetyRecord.permissionMode !== "acceptEdits" || safetyRecord.tools !== "Read,Write,Edit,Glob,Grep" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "user" || safetyRecord.userSettingsTrust !== "required" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
|
|
165
|
+
} else if (adapterRecord.id === "cursor-agent" || adapterRecord.id === "cursor-agent-writer") {
|
|
166
|
+
if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
|
|
167
|
+
const safetyRecord = safety as Record<string, unknown>;
|
|
168
|
+
const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "mode", "sandbox", "workspaceTrust", "sessionReuse"].includes(field));
|
|
169
|
+
if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
|
|
170
|
+
const writer = adapterRecord.id === "cursor-agent-writer";
|
|
171
|
+
if (safetyRecord.access !== (writer ? "workspace-write" : "read-only") || safetyRecord.authentication !== "cursor-api-key-or-existing-login" || safetyRecord.mode !== (writer ? "print" : "ask") || safetyRecord.sandbox !== "enabled" || safetyRecord.workspaceTrust !== "existing-required" || safetyRecord.sessionReuse !== false) throw new Error(`${label}.safety is invalid.`);
|
|
172
|
+
} else if (adapterRecord.id === "grok-build") {
|
|
173
|
+
if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
|
|
174
|
+
const safetyRecord = safety as Record<string, unknown>;
|
|
175
|
+
const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "permissionMode", "tools", "deniedTools", "sandbox", "webSearch", "subagents", "config", "updates", "sessionPersistence"].includes(field));
|
|
176
|
+
if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
|
|
177
|
+
if (safetyRecord.access !== "read-only" || safetyRecord.authentication !== "xai-api-key-required" || safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "read_file,grep,list_dir" || safetyRecord.deniedTools !== "run_terminal_cmd,search_replace,Agent,Bash,Edit,Write,MCPTool" || safetyRecord.sandbox !== "read-only" || safetyRecord.webSearch !== false || safetyRecord.subagents !== false || safetyRecord.config !== "temporary-home" || safetyRecord.updates !== "disabled" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
|
|
178
|
+
} else if (safety !== undefined) throw new Error(`${label}.safety is invalid for the generic adapter.`);
|
|
179
|
+
const handoff = metadata.handoff;
|
|
180
|
+
if (!handoff || typeof handoff !== "object" || Array.isArray(handoff)) throw new Error(`${label}.handoff must be an object.`);
|
|
181
|
+
const handoffRecord = handoff as Record<string, unknown>;
|
|
182
|
+
const unknownHandoff = Object.keys(handoffRecord).filter((field) => field !== "mode");
|
|
183
|
+
if (unknownHandoff.length > 0) throw new Error(`${label}.handoff has unsupported fields: ${unknownHandoff.join(", ")}.`);
|
|
184
|
+
if (handoffRecord.mode !== "fresh") throw new Error(`${label}.handoff is invalid.`);
|
|
185
|
+
const supervisor = metadata.supervisor;
|
|
186
|
+
if (!supervisor || typeof supervisor !== "object" || Array.isArray(supervisor)) throw new Error(`${label}.supervisor must be an object.`);
|
|
187
|
+
const supervisorRecord = supervisor as Record<string, unknown>;
|
|
188
|
+
const unknownSupervisor = Object.keys(supervisorRecord).filter((field) => !["mode", "reason"].includes(field));
|
|
189
|
+
if (unknownSupervisor.length > 0) throw new Error(`${label}.supervisor has unsupported fields: ${unknownSupervisor.join(", ")}.`);
|
|
190
|
+
if (supervisorRecord.mode !== "unsupported" || typeof supervisorRecord.reason !== "string" || !supervisorRecord.reason.trim()) throw new Error(`${label}.supervisor is invalid.`);
|
|
191
|
+
if (typeof metadata.nonResumableReason !== "string" || !metadata.nonResumableReason.trim()) throw new Error(`${label}.nonResumableReason is missing.`);
|
|
192
|
+
const outputArtifacts = metadata.outputArtifacts;
|
|
193
|
+
if (outputArtifacts !== undefined) {
|
|
194
|
+
if (!outputArtifacts || typeof outputArtifacts !== "object" || Array.isArray(outputArtifacts)) throw new Error(`${label}.outputArtifacts must be an object.`);
|
|
195
|
+
const unknownArtifacts = Object.keys(outputArtifacts).filter((field) => !["stdoutPath", "stderrPath", "finalOutputPath"].includes(field));
|
|
196
|
+
if (unknownArtifacts.length > 0) throw new Error(`${label}.outputArtifacts has unsupported fields: ${unknownArtifacts.join(", ")}.`);
|
|
197
|
+
for (const field of ["stdoutPath", "stderrPath", "finalOutputPath"] as const) {
|
|
198
|
+
const artifactPath = (outputArtifacts as Record<string, unknown>)[field];
|
|
199
|
+
if (artifactPath !== undefined && (typeof artifactPath !== "string" || !artifactPath.trim())) throw new Error(`${label}.outputArtifacts.${field} must be a non-empty string.`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return value as ExternalCliReceiptMetadata;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function parseTerminalOutcome(value: unknown, label: string): WorkflowTerminalOutcome | undefined {
|
|
206
|
+
if (value === undefined) return undefined;
|
|
207
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
|
|
208
|
+
const outcome = value as Record<string, unknown>;
|
|
209
|
+
if (outcome.state !== "partial" || (outcome.reason !== "budget_exhausted" && outcome.reason !== "timeout")) throw new Error(`${label} is invalid.`);
|
|
210
|
+
return { state: "partial", reason: outcome.reason };
|
|
211
|
+
}
|
|
212
|
+
|
|
67
213
|
function parseEntry(value: unknown, key: string, source: string): WorkflowReceiptEntry {
|
|
68
214
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' must be an object.`);
|
|
69
215
|
const entry = value as Record<string, unknown>;
|
|
@@ -84,16 +230,47 @@ function parseEntry(value: unknown, key: string, source: string): WorkflowReceip
|
|
|
84
230
|
const reason = (resumability as Record<string, unknown>).reason;
|
|
85
231
|
if (state === "resumable" && latestRunId === undefined) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' resumable entry has no retained run id.`);
|
|
86
232
|
if (state === "not-resumable" && (typeof reason !== "string" || !reason.trim())) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' non-resumable reason is missing.`);
|
|
87
|
-
|
|
233
|
+
const terminalOutcome = parseTerminalOutcome(entry.terminalOutcome, `Invalid workflow receipt '${source}': entry '${key}' terminalOutcome`);
|
|
234
|
+
parseExternalCliReceiptMetadata(entry.externalAdapter, key, source);
|
|
235
|
+
const lane = normalizeWorkflowLaneMetadata(entry.lane, `Invalid workflow receipt '${source}': entry '${key}'.lane`);
|
|
236
|
+
assertWorkflowLaneKey(lane, key, `Invalid workflow receipt '${source}': entry '${key}'.lane`);
|
|
237
|
+
return { ...(value as WorkflowReceiptEntry), ...(lane ? { lane } : {}), ...(terminalOutcome ? { terminalOutcome } : {}) };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function parseWorkflowResolution(value: unknown, source: string): WorkflowTerminalResolution | undefined {
|
|
241
|
+
if (value === undefined) return undefined;
|
|
242
|
+
if (value !== "settled-awaiting-resume" && value !== "failed-child" && value !== "interrupted-child") throw new Error(`Invalid workflow receipt '${source}': workflowResolution is invalid.`);
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function parseRecovery(value: unknown, workflowRunId: string, entries: Record<string, WorkflowReceiptEntry>, source: string): WorkflowRecoveryAction[] | undefined {
|
|
247
|
+
if (value === undefined) return undefined;
|
|
248
|
+
if (!Array.isArray(value)) throw new Error(`Invalid workflow receipt '${source}': recovery must be an array.`);
|
|
249
|
+
return value.map((item, index) => {
|
|
250
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) throw new Error(`Invalid workflow receipt '${source}': recovery[${index}] must be an object.`);
|
|
251
|
+
const action = item as Record<string, unknown>;
|
|
252
|
+
const key = action.key;
|
|
253
|
+
const resume = action.resume;
|
|
254
|
+
if (typeof key !== "string" || !entries[key] || action.call !== "runs.run" || action.taskRequired !== true || !resume || typeof resume !== "object" || Array.isArray(resume)) throw new Error(`Invalid workflow receipt '${source}': recovery[${index}] is invalid.`);
|
|
255
|
+
const reference = resume as Record<string, unknown>;
|
|
256
|
+
if (reference.workflowRunId !== workflowRunId || reference.key !== key || reference.latest !== true || entries[key].resumability.state !== "resumable") throw new Error(`Invalid workflow receipt '${source}': recovery[${index}] does not identify a resumable entry.`);
|
|
257
|
+
return { key, call: "runs.run", resume: { workflowRunId, key, latest: true }, taskRequired: true };
|
|
258
|
+
});
|
|
88
259
|
}
|
|
89
260
|
|
|
90
261
|
export function readWorkflowReceipt(asyncDirRoot: string, workflowRunId: string): WorkflowReceipt {
|
|
91
262
|
const receiptPath = workflowReceiptPath(asyncDirRoot, workflowRunId);
|
|
92
263
|
let value: unknown;
|
|
93
264
|
try {
|
|
94
|
-
value =
|
|
265
|
+
value = readWorkflowReceiptFile(receiptPath);
|
|
95
266
|
} catch (error) {
|
|
96
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
|
267
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
268
|
+
const workflowDir = path.dirname(receiptPath);
|
|
269
|
+
if (fs.existsSync(path.join(workflowDir, "status.json")) || fs.existsSync(path.join(workflowDir, "events.jsonl"))) {
|
|
270
|
+
throw new Error(`Workflow receipt '${workflowRunId}' is not available because the workflow may still be active or terminal receipt writing failed. Use direct child run IDs from status/events for direct resume after the normal retained-child checks.`);
|
|
271
|
+
}
|
|
272
|
+
throw new Error(`Workflow receipt '${workflowRunId}' was not found.`);
|
|
273
|
+
}
|
|
97
274
|
throw new Error(`Workflow receipt '${workflowRunId}' could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
|
|
98
275
|
}
|
|
99
276
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid workflow receipt '${receiptPath}': expected an object.`);
|
|
@@ -107,7 +284,19 @@ export function readWorkflowReceipt(asyncDirRoot: string, workflowRunId: string)
|
|
|
107
284
|
if (!receipt.entries || typeof receipt.entries !== "object" || Array.isArray(receipt.entries)) throw new Error(`Invalid workflow receipt '${receiptPath}': entries must be an object.`);
|
|
108
285
|
const entries: Record<string, WorkflowReceiptEntry> = Object.create(null) as Record<string, WorkflowReceiptEntry>;
|
|
109
286
|
for (const [key, entry] of Object.entries(receipt.entries as Record<string, unknown>)) entries[assertKey(key, "workflow receipt key")] = parseEntry(entry, key, receiptPath);
|
|
110
|
-
|
|
287
|
+
const workflowChildren = parseWorkflowChildSummary(receipt.workflowChildren);
|
|
288
|
+
if (workflowChildren && workflowChildren.workflowRunId !== workflowRunId) throw new Error(`Workflow receipt '${receiptPath}' is stale: workflowChildren.workflowRunId does not match.`);
|
|
289
|
+
let hostSteps: NonNullable<WorkflowReceipt["hostSteps"]> | undefined;
|
|
290
|
+
if (receipt.hostSteps !== undefined) {
|
|
291
|
+
if (!Array.isArray(receipt.hostSteps)) throw new Error(`Invalid workflow receipt '${receiptPath}': hostSteps must be an array.`);
|
|
292
|
+
if (receipt.hostSteps.length > HOST_STEP_MAX_COUNT) throw new Error(`Invalid workflow receipt '${receiptPath}': hostSteps exceeds ${HOST_STEP_MAX_COUNT} entries.`);
|
|
293
|
+
hostSteps = receipt.hostSteps.map((hostStep, index) => parseHostStepNode(hostStep, `${receiptPath} hostSteps[${index}]`));
|
|
294
|
+
assertUniqueHostStepIds(hostSteps, receiptPath);
|
|
295
|
+
}
|
|
296
|
+
const workflowResolution = parseWorkflowResolution(receipt.workflowResolution, receiptPath);
|
|
297
|
+
const terminalOutcome = parseTerminalOutcome(receipt.terminalOutcome, `Invalid workflow receipt '${receiptPath}': terminalOutcome`);
|
|
298
|
+
const recovery = parseRecovery(receipt.recovery, workflowRunId, entries, receiptPath);
|
|
299
|
+
return { version: 1, workflowRunId, state: receipt.state, createdAt: receipt.createdAt, entries, ...(hostSteps?.length ? { hostSteps } : {}), ...(workflowChildren ? { workflowChildren } : {}), ...(workflowResolution ? { workflowResolution } : {}), ...(terminalOutcome ? { terminalOutcome } : {}), ...(recovery ? { recovery } : {}) };
|
|
111
300
|
}
|
|
112
301
|
|
|
113
302
|
export function resolveWorkflowReceiptResumeEntry(input: {
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AsyncStatus,
|
|
3
|
+
WorkflowRecoveryAction,
|
|
4
|
+
WorkflowTerminalOutcome,
|
|
5
|
+
WorkflowTerminalResolution,
|
|
6
|
+
Usage,
|
|
7
|
+
} from "../shared/types.ts";
|
|
8
|
+
import type { WorkflowReceipt } from "./workflow-receipt.ts";
|
|
9
|
+
import { workflowChildSummary } from "./workflow-child-summary.ts";
|
|
10
|
+
|
|
11
|
+
export const UNSUPPORTED_DETACHED_WORKFLOW_CONTINUATION = "unsupported-continuation: detached workflow child settled, but JavaScript workflow continuation was not persisted. Resume the workflow explicitly instead of treating the completed child as top-level workflow completion.";
|
|
12
|
+
export const INTERRUPTED_DETACHED_CHILD = "Interrupted. Waiting for explicit next action.";
|
|
13
|
+
export const EVIDENCE_PERSISTENCE_FAILED = "evidence-persistence-failed";
|
|
14
|
+
|
|
15
|
+
export type WorkflowStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
16
|
+
outputPathMapping?: { requestedPath: string; savedPath: string };
|
|
17
|
+
interrupted?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface WorkflowPublicChild {
|
|
21
|
+
workflowKey?: string;
|
|
22
|
+
agent?: string;
|
|
23
|
+
/** Human-readable display name for the child session, when derived at launch. */
|
|
24
|
+
sessionName?: string;
|
|
25
|
+
runId?: string;
|
|
26
|
+
usage?: Usage;
|
|
27
|
+
sessionFile?: string;
|
|
28
|
+
output: string;
|
|
29
|
+
outputState: "present" | "absent";
|
|
30
|
+
structuredOutput?: unknown;
|
|
31
|
+
success: boolean;
|
|
32
|
+
terminalOutcome?: WorkflowTerminalOutcome;
|
|
33
|
+
outputReference?: string;
|
|
34
|
+
outputPathMapping?: { requestedPath: string; savedPath: string };
|
|
35
|
+
stopped?: boolean;
|
|
36
|
+
interrupted?: boolean;
|
|
37
|
+
detached?: boolean;
|
|
38
|
+
error?: string;
|
|
39
|
+
artifactPaths?: { outputPath: string };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface WorkflowSettlementPlan {
|
|
43
|
+
status: AsyncStatus;
|
|
44
|
+
publicResult: Record<string, unknown>;
|
|
45
|
+
receipt?: WorkflowReceipt;
|
|
46
|
+
recovery: WorkflowRecoveryAction[];
|
|
47
|
+
completionEvent?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function cloneWorkflowStatus(status: AsyncStatus): AsyncStatus {
|
|
51
|
+
return {
|
|
52
|
+
...status,
|
|
53
|
+
steps: status.steps?.map((step) => ({ ...step })),
|
|
54
|
+
workflow: status.workflow ? { ...status.workflow, trace: [...(status.workflow.trace ?? [])] } : status.workflow,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function workflowState(status: AsyncStatus): "completed" | "failed" | "paused" | "stopped" {
|
|
59
|
+
return status.state === "complete" ? "completed" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function withWorkflowChildren(status: AsyncStatus): AsyncStatus {
|
|
63
|
+
return {
|
|
64
|
+
...status,
|
|
65
|
+
workflowChildren: workflowChildSummary({
|
|
66
|
+
parentToolCallId: status.toolCallId ?? status.runId,
|
|
67
|
+
workflowRunId: status.runId,
|
|
68
|
+
workflowState: workflowState(status),
|
|
69
|
+
inventoryComplete: true,
|
|
70
|
+
trace: status.workflow?.trace,
|
|
71
|
+
steps: status.steps,
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function findWorkflowSettlementStep(status: AsyncStatus, childRunId: string, workflowKey?: string, sessionFile?: string): WorkflowStatusStep | undefined {
|
|
77
|
+
const exact = status.steps?.find((candidate) => candidate.runId === childRunId);
|
|
78
|
+
if (exact || !workflowKey || !sessionFile) return exact;
|
|
79
|
+
const candidates = status.steps?.filter((candidate) => candidate.runId === undefined
|
|
80
|
+
&& candidate.workflowKey === workflowKey
|
|
81
|
+
&& candidate.sessionFile === sessionFile) ?? [];
|
|
82
|
+
return candidates.length === 1 ? candidates[0] : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function promoteSettledPausedWorkflow(status: AsyncStatus, now = Date.now()): AsyncStatus | undefined {
|
|
86
|
+
if (status.mode !== "workflow" || status.state !== "paused") return undefined;
|
|
87
|
+
const next = cloneWorkflowStatus(status);
|
|
88
|
+
const stillOpen = next.steps?.some((candidate) =>
|
|
89
|
+
candidate.status === "running"
|
|
90
|
+
|| (candidate.status === "paused" && candidate.activityState === "needs_attention")
|
|
91
|
+
) === true;
|
|
92
|
+
if (stillOpen || !next.steps?.length) return undefined;
|
|
93
|
+
const failed = next.steps.some((candidate) => candidate.status === "failed");
|
|
94
|
+
next.lastUpdate = now;
|
|
95
|
+
next.state = "failed";
|
|
96
|
+
next.endedAt = now;
|
|
97
|
+
delete next.activityState;
|
|
98
|
+
if (!failed) next.error = UNSUPPORTED_DETACHED_WORKFLOW_CONTINUATION;
|
|
99
|
+
return withWorkflowChildren(next);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function applyDetachedChildSettlement(
|
|
103
|
+
status: AsyncStatus,
|
|
104
|
+
input: { childRunId: string; result: { exitCode: number | null; error?: string; interrupted?: boolean; sessionFile?: string; sessionName?: string; stopped?: boolean }; workflowKey?: string; now?: number },
|
|
105
|
+
): AsyncStatus | undefined {
|
|
106
|
+
if (status.mode !== "workflow" || status.state !== "paused") return undefined;
|
|
107
|
+
const next = cloneWorkflowStatus(status);
|
|
108
|
+
const step = findWorkflowSettlementStep(next, input.childRunId, input.workflowKey, input.result.sessionFile);
|
|
109
|
+
if (!step) return undefined;
|
|
110
|
+
step.runId ??= input.childRunId;
|
|
111
|
+
const succeeded = input.result.exitCode === 0 && !input.result.error && !input.result.interrupted;
|
|
112
|
+
const failedSiblingError = next.steps?.find((candidate) => {
|
|
113
|
+
const candidateStep = candidate as WorkflowStatusStep;
|
|
114
|
+
return candidateStep !== step && candidateStep.status === "failed" && !candidateStep.interrupted && candidateStep.error;
|
|
115
|
+
})?.error;
|
|
116
|
+
const interruptedChildError = next.steps?.find((candidate) => {
|
|
117
|
+
const candidateStep = candidate as WorkflowStatusStep;
|
|
118
|
+
return candidateStep.status === "failed" && candidateStep.interrupted && candidateStep.error;
|
|
119
|
+
})?.error;
|
|
120
|
+
const now = input.now ?? Date.now();
|
|
121
|
+
step.status = succeeded ? "completed" : "failed";
|
|
122
|
+
step.endedAt = now;
|
|
123
|
+
delete step.activityState;
|
|
124
|
+
delete step.currentTool;
|
|
125
|
+
delete step.currentToolStartedAt;
|
|
126
|
+
if (input.result.sessionFile) step.sessionFile = input.result.sessionFile;
|
|
127
|
+
if (input.result.sessionName) step.sessionName = input.result.sessionName;
|
|
128
|
+
if (succeeded) {
|
|
129
|
+
delete step.error;
|
|
130
|
+
delete step.interrupted;
|
|
131
|
+
} else if (input.result.interrupted || input.result.stopped) {
|
|
132
|
+
step.error = input.result.error ?? INTERRUPTED_DETACHED_CHILD;
|
|
133
|
+
step.interrupted = true;
|
|
134
|
+
if (input.result.stopped) step.stopped = true;
|
|
135
|
+
} else {
|
|
136
|
+
delete step.interrupted;
|
|
137
|
+
delete step.stopped;
|
|
138
|
+
if (input.result.error) step.error = input.result.error;
|
|
139
|
+
}
|
|
140
|
+
next.lastUpdate = now;
|
|
141
|
+
const promoted = promoteSettledPausedWorkflow(next, now);
|
|
142
|
+
if (promoted?.state === "failed" && failedSiblingError) promoted.error = failedSiblingError;
|
|
143
|
+
else if (promoted?.state === "failed" && (input.result.interrupted || input.result.stopped)) promoted.error = input.result.error ?? INTERRUPTED_DETACHED_CHILD;
|
|
144
|
+
else if (promoted?.state === "failed" && input.result.error) promoted.error = input.result.error;
|
|
145
|
+
else if (promoted?.state === "failed" && interruptedChildError) promoted.error = interruptedChildError;
|
|
146
|
+
return withWorkflowChildren(promoted ?? next);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function classifyWorkflowSettlement(status: AsyncStatus, interrupted = false): WorkflowTerminalResolution | undefined {
|
|
150
|
+
if (status.state !== "complete" && status.state !== "failed") return undefined;
|
|
151
|
+
if (status.steps?.some((step) => {
|
|
152
|
+
const candidate = step as WorkflowStatusStep;
|
|
153
|
+
return candidate.status === "failed" && !candidate.interrupted;
|
|
154
|
+
})) return "failed-child";
|
|
155
|
+
if (interrupted || status.steps?.some((step) => {
|
|
156
|
+
const candidate = step as WorkflowStatusStep;
|
|
157
|
+
return candidate.status === "stopped" || candidate.stopped || candidate.interrupted || candidate.error === INTERRUPTED_DETACHED_CHILD;
|
|
158
|
+
})) return "interrupted-child";
|
|
159
|
+
return "settled-awaiting-resume";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function workflowRecoveryActions(receipt: WorkflowReceipt | undefined): WorkflowRecoveryAction[] {
|
|
163
|
+
if (!receipt) return [];
|
|
164
|
+
return Object.values(receipt.entries).flatMap((entry) => entry.resumability.state === "resumable"
|
|
165
|
+
? [{ key: entry.key, call: "runs.run" as const, resume: { workflowRunId: receipt.workflowRunId, key: entry.key, latest: true as const }, taskRequired: true as const }]
|
|
166
|
+
: []);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function workflowTerminalOutcomeForResult(result: { timedOut?: boolean; turnBudgetExceeded?: boolean; toolBudgetBlocked?: boolean }): WorkflowTerminalOutcome | undefined {
|
|
170
|
+
if (result.timedOut) return { state: "partial", reason: "timeout" };
|
|
171
|
+
if (result.turnBudgetExceeded || result.toolBudgetBlocked) return { state: "partial", reason: "budget_exhausted" };
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function workflowOutputPathMappingSummary(children: readonly unknown[]): string {
|
|
176
|
+
const mappings = children.flatMap((entry) => {
|
|
177
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
|
|
178
|
+
const child = entry as Partial<WorkflowPublicChild>;
|
|
179
|
+
return child.outputPathMapping
|
|
180
|
+
? [`'${child.workflowKey ?? "child"}': requested ${child.outputPathMapping.requestedPath} -> saved ${child.outputPathMapping.savedPath}`]
|
|
181
|
+
: [];
|
|
182
|
+
});
|
|
183
|
+
return mappings.length > 0 ? ` Output path mappings: ${mappings.join("; ")}.` : "";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function planWorkflowSettlement(input: {
|
|
187
|
+
status: AsyncStatus;
|
|
188
|
+
summary: string;
|
|
189
|
+
children: WorkflowPublicChild[];
|
|
190
|
+
baseResult: Record<string, unknown>;
|
|
191
|
+
receipt?: WorkflowReceipt;
|
|
192
|
+
receiptPath?: string;
|
|
193
|
+
receiptPersistenceError?: string;
|
|
194
|
+
resolution?: WorkflowTerminalResolution;
|
|
195
|
+
terminalOutcome?: WorkflowTerminalOutcome;
|
|
196
|
+
now?: number;
|
|
197
|
+
eventMetadata?: Record<string, unknown>;
|
|
198
|
+
}): WorkflowSettlementPlan {
|
|
199
|
+
const now = input.now ?? Date.now();
|
|
200
|
+
let status = withWorkflowChildren(cloneWorkflowStatus(input.status));
|
|
201
|
+
let summary = input.summary;
|
|
202
|
+
let receipt = input.receipt;
|
|
203
|
+
let resolution = input.resolution;
|
|
204
|
+
if (input.receiptPersistenceError) {
|
|
205
|
+
const diagnostic = `${EVIDENCE_PERSISTENCE_FAILED}: ${input.receiptPersistenceError}`;
|
|
206
|
+
status = withWorkflowChildren({ ...status, state: "failed", error: diagnostic, activityState: undefined, endedAt: now, lastUpdate: now });
|
|
207
|
+
summary = `${diagnostic} Available child evidence was preserved, but workflow completion was not accepted.`;
|
|
208
|
+
receipt = undefined;
|
|
209
|
+
if (!resolution) resolution = classifyWorkflowSettlement(status);
|
|
210
|
+
}
|
|
211
|
+
const recovery = workflowRecoveryActions(receipt);
|
|
212
|
+
if (receipt) {
|
|
213
|
+
receipt = {
|
|
214
|
+
...receipt,
|
|
215
|
+
state: status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed",
|
|
216
|
+
workflowChildren: status.workflowChildren,
|
|
217
|
+
...(resolution ? { workflowResolution: resolution, recovery } : {}),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const publicResult: Record<string, unknown> = {
|
|
221
|
+
...input.baseResult,
|
|
222
|
+
success: status.state === "complete",
|
|
223
|
+
state: status.state,
|
|
224
|
+
summary,
|
|
225
|
+
error: status.state === "complete" ? undefined : status.error ?? summary,
|
|
226
|
+
stopped: status.stopped ? true : undefined,
|
|
227
|
+
activityState: status.activityState,
|
|
228
|
+
workflowChildren: status.workflowChildren,
|
|
229
|
+
results: input.children,
|
|
230
|
+
...(input.terminalOutcome ? { terminalOutcome: input.terminalOutcome } : {}),
|
|
231
|
+
...(resolution ? { workflowResolution: resolution, recovery } : {}),
|
|
232
|
+
...(receipt && input.receiptPath ? { workflowReceipt: { path: input.receiptPath, receipt } } : {}),
|
|
233
|
+
timestamp: now,
|
|
234
|
+
};
|
|
235
|
+
const terminal = status.state === "complete" || status.state === "failed" || status.state === "partial" || status.state === "stopped";
|
|
236
|
+
const completionEvent = terminal ? {
|
|
237
|
+
type: "subagent.workflow.completed",
|
|
238
|
+
state: status.state,
|
|
239
|
+
...(resolution ? { workflowResolution: resolution } : {}),
|
|
240
|
+
...(input.terminalOutcome ? { terminalOutcome: input.terminalOutcome } : {}),
|
|
241
|
+
...(status.error ? { error: status.error } : {}),
|
|
242
|
+
...(status.activityState ? { activityState: status.activityState } : {}),
|
|
243
|
+
...input.eventMetadata,
|
|
244
|
+
} : undefined;
|
|
245
|
+
return { status, publicResult, receipt, recovery, completionEvent };
|
|
246
|
+
}
|
|
@@ -36,7 +36,7 @@ const ISOLATED_ENV_KEYS = [
|
|
|
36
36
|
"SELESAI_SUBAGENT_MAX_DEPTH",
|
|
37
37
|
"SELESAI_SUBAGENT_EXTRA_AGENT_DIRS",
|
|
38
38
|
"SELESAI_SUBAGENT_PARENT_SESSION",
|
|
39
|
-
"
|
|
39
|
+
"SELESAI_SUBAGENT_SELESAI_BINARY",
|
|
40
40
|
"SELESAI_SUBAGENTS_CODING_AGENT_PACKAGE_ROOT",
|
|
41
41
|
] as const;
|
|
42
42
|
|
|
@@ -186,7 +186,7 @@ Report active tools.`;
|
|
|
186
186
|
process.env.SELESAI_SUBAGENT_MAX_DEPTH = "1";
|
|
187
187
|
process.env.SELESAI_SUBAGENT_EXTRA_AGENT_DIRS = BOGUS_EXTRA_DIRS;
|
|
188
188
|
process.env.SELESAI_SUBAGENT_PARENT_SESSION = "polluted-parent";
|
|
189
|
-
process.env.
|
|
189
|
+
process.env.SELESAI_SUBAGENT_SELESAI_BINARY = BOGUS_PI_BINARY;
|
|
190
190
|
process.env.SELESAI_SUBAGENTS_CODING_AGENT_PACKAGE_ROOT = BOGUS_PI_PACKAGE_ROOT;
|
|
191
191
|
|
|
192
192
|
try {
|