@zhushanwen/subagent-core 0.4.0 → 0.6.0
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/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
- package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
- package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
- package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
- package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
- package/dist/execution/engine/engines/zcode/constants.js +17 -27
- package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
- package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
- package/dist/execution/engine/engines/zcode/reader.js +2 -2
- package/dist/index.cjs +17100 -14082
- package/dist/index.d.cts +5336 -4033
- package/dist/index.d.ts +5336 -4033
- package/dist/index.js +17020 -14055
- package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
- package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
- package/dist.bundle/index.cjs +34933 -0
- package/package.json +3 -3
- package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
- package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
- package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
- package/src/__tests__/m2-append-content-probe.test.ts +0 -1
- package/src/__tests__/manifest-store.test.ts +10 -9
- package/src/__tests__/record-store-cache.test.ts +0 -2
- package/src/__tests__/record-store-index.test.ts +1 -2
- package/src/__tests__/review-fix-loop-script.test.ts +4 -2
- package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
- package/src/__tests__/robustness-low-batch1.test.ts +6 -5
- package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
- package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
- package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
- package/src/__tests__/session-runner.test.ts +1 -1
- package/src/core/__tests__/host-services.test.ts +2 -2
- package/src/core/__tests__/logger.test.ts +1 -1
- package/src/core/error-message.ts +9 -0
- package/src/core/host-services.ts +21 -5
- package/src/core/notify-ports.ts +18 -5
- package/src/execution/__tests__/agent-profile.test.ts +4 -4
- package/src/execution/__tests__/agent-registry.test.ts +370 -0
- package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
- package/src/execution/__tests__/agents-assembly.test.ts +2 -2
- package/src/execution/__tests__/alive-store.test.ts +1 -1
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
- package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
- package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
- package/src/execution/__tests__/collect-budget.test.ts +291 -0
- package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
- package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
- package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
- package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
- package/src/execution/__tests__/config.test.ts +1 -1
- package/src/execution/__tests__/delivery-methods.test.ts +75 -62
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
- package/src/execution/__tests__/dialog-queue.test.ts +2 -2
- package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
- package/src/execution/__tests__/execute-nesting.test.ts +34 -188
- package/src/execution/__tests__/execution-record.test.ts +217 -60
- package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
- package/src/execution/__tests__/finalize-record.test.ts +173 -17
- package/src/execution/__tests__/finalized-marker.test.ts +1 -1
- package/src/execution/__tests__/gc-timer.test.ts +56 -3
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
- package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
- package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
- package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
- package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
- package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
- package/src/execution/__tests__/model-resolver.test.ts +2 -2
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
- package/src/execution/__tests__/nested-visibility.test.ts +2 -0
- package/src/execution/__tests__/notify-batch.test.ts +500 -0
- package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
- package/src/execution/__tests__/output-collector.test.ts +117 -6
- package/src/execution/__tests__/pi-invocation.test.ts +36 -4
- package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
- package/src/execution/__tests__/record-store.test.ts +10 -9
- package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
- package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
- package/src/execution/__tests__/relay-env.test.ts +12 -1
- package/src/execution/__tests__/rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
- package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
- package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
- package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
- package/src/execution/__tests__/session-file-gc.test.ts +36 -1
- package/src/execution/__tests__/session-pending.test.ts +257 -14
- package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
- package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
- package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
- package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
- package/src/execution/__tests__/spawn-args.test.ts +11 -10
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
- package/src/execution/__tests__/spawn-event-adapter.test.ts +3 -3
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
- package/src/execution/__tests__/spawned-children.test.ts +2 -1
- package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
- package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
- package/src/execution/__tests__/stdin-writer.test.ts +6 -4
- package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
- package/src/execution/__tests__/subagent-actions-core.test.ts +48 -22
- package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
- package/src/execution/__tests__/subagent-service.test.ts +37 -30
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
- package/src/execution/__tests__/sync-collect-recovery.test.ts +1117 -0
- package/src/execution/__tests__/temp-prompt.test.ts +1 -1
- package/src/execution/__tests__/timeout-integration.test.ts +6 -5
- package/src/execution/__tests__/tombstone-store.test.ts +1 -1
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
- package/src/execution/__tests__/turn-limiter.test.ts +1 -1
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
- package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
- package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
- package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
- package/src/execution/__tests__/worktree-manager.test.ts +14 -5
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
- package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
- package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
- package/src/execution/__tests__/worktree-registry.test.ts +1 -1
- package/src/execution/agent-registry.ts +74 -30
- package/src/execution/agent-result-mapper.ts +3 -0
- package/src/execution/cold-resurrect.ts +199 -0
- package/src/execution/collect-coordinator.ts +200 -0
- package/src/execution/config.ts +77 -5
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
- package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
- package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
- package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +213 -53
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
- package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
- package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
- package/src/execution/engine/__tests__/registry.test.ts +4 -2
- package/src/execution/engine/__tests__/routing.test.ts +115 -6
- package/src/execution/engine/common/capability-gate.ts +92 -0
- package/src/execution/engine/common/journal-wiring.ts +97 -0
- package/src/execution/engine/common/kill-chain.ts +42 -10
- package/src/execution/engine/common/nesting-guard.ts +66 -0
- package/src/execution/engine/common/persona-router.ts +18 -8
- package/src/execution/engine/common/pool-manager.ts +332 -78
- package/src/execution/engine/common/session-view-service.ts +506 -0
- package/src/execution/engine/common/session-view-types.ts +125 -0
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
- package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
- package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
- package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
- package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
- package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
- package/src/execution/engine/engines/pi/reader.ts +11 -0
- package/src/execution/engine/engines/pi/registration.ts +4 -2
- package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
- package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
- package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
- package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
- package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
- package/src/execution/engine/engines/zcode/connection.ts +24 -11
- package/src/execution/engine/engines/zcode/constants.ts +75 -71
- package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
- package/src/execution/engine/engines/zcode/parser.ts +12 -346
- package/src/execution/engine/engines/zcode/preparer.ts +12 -143
- package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
- package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
- package/src/execution/engine/host-task-spec.ts +32 -36
- package/src/execution/engine/model-validation.ts +177 -0
- package/src/execution/engine/port.ts +24 -6
- package/src/execution/engine/routing.ts +75 -1
- package/src/execution/engine/types.ts +30 -103
- package/src/execution/execution-record.ts +229 -144
- package/src/execution/finalize-record.ts +141 -99
- package/src/execution/idle-gc.ts +28 -0
- package/src/execution/lifecycle-predicates.ts +1 -1
- package/src/execution/notifier.ts +234 -25
- package/src/execution/notify-host.ts +232 -0
- package/src/execution/record-entry.ts +16 -0
- package/src/execution/record-store.ts +280 -103
- package/src/execution/round-settlement.ts +93 -0
- package/src/execution/session-file-gc.ts +96 -62
- package/src/execution/session-reconstructor.ts +256 -143
- package/src/execution/sessions-index.ts +117 -47
- package/src/execution/settled-watchdog.ts +264 -52
- package/src/execution/subagent-actions-core.ts +84 -28
- package/src/execution/subagent-service.ts +1225 -964
- package/src/execution/subprocess-agent-runner.ts +144 -97
- package/src/execution/sync-rebuild.ts +87 -0
- package/src/execution/types.ts +72 -0
- package/src/execution/ui-request-handler-factory.ts +102 -74
- package/src/execution/ui-request-queue.ts +50 -19
- package/src/index.ts +442 -316
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
- package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
- package/src/orchestration/__tests__/args-validator.test.ts +14 -0
- package/src/orchestration/__tests__/config-loader.test.ts +1 -1
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
- package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
- package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
- package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
- package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
- package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
- package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
- package/src/orchestration/__tests__/script-generate.test.ts +5 -5
- package/src/orchestration/__tests__/script-lint.test.ts +17 -0
- package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
- package/src/orchestration/__tests__/test-mocks.ts +2 -0
- package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
- package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
- package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
- package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
- package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
- package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
- package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
- package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
- package/src/orchestration/agent-opts-resolver.ts +1 -1
- package/src/orchestration/args-validator.ts +53 -31
- package/src/orchestration/execute-agent-call.ts +19 -73
- package/src/orchestration/launcher.ts +128 -81
- package/src/orchestration/lifecycle.ts +43 -45
- package/src/orchestration/models/ports.ts +4 -4
- package/src/orchestration/models/run-runtime.ts +2 -2
- package/src/orchestration/models/trace.ts +1 -1
- package/src/orchestration/models/types.ts +111 -22
- package/src/orchestration/models/workflow-run.ts +1 -1
- package/src/orchestration/script-lint.ts +91 -58
- package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
- package/src/shared/__tests__/atomic-write.test.ts +1 -1
- package/src/shared/__tests__/meta-parser.test.ts +151 -0
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
- package/src/shared/__tests__/resource-discovery.test.ts +258 -215
- package/src/shared/atomic-write.ts +12 -9
- package/src/shared/meta-parser.ts +158 -84
- package/src/shared/model-ref.ts +15 -2
- package/src/shared/resource-discovery.ts +9 -183
- package/src/shared/schema-jsonify.ts +1 -1
- package/workflows/review-fix-loop-utils.cjs +151 -89
- package/workflows/review-fix-loop-utils.d.cts +287 -0
- package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
- package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
- package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
- package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
- package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/execute-options-mapper.ts +0 -115
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
// src/execution/engine/engines/pi/pi-engine.ts
|
|
2
2
|
//
|
|
3
|
-
// PiEngine
|
|
4
|
-
// docs/architecture/subagent-engine-abstraction.md §3.3.1
|
|
5
|
-
//
|
|
3
|
+
// PiEngine:pi 执行链的引擎边界(dual-track-convergence D2 后形态)。设计权威源:
|
|
4
|
+
// docs/architecture/subagent-engine-abstraction.md §3.3.1 / D1(四面 + handle 契约 +
|
|
5
|
+
// abort 分级)、D3(capabilities 链路接通口径)、docs/design/subagent-dual-track-convergence.md
|
|
6
|
+
// §3.3 D2(pi 执行轨物理下沉 + Service 旧轨收敛)+ D6(任务形状合流:run 入参 =
|
|
7
|
+
// AgentCallOpts 单一形状,本文件内一次直出为 spawn 编排参数,无中间态还原)。
|
|
6
8
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// 四件套物理归属 engines/pi/(launcher=session-runner / parser=spawn-event-adapter /
|
|
10
|
+
// preparer=temp-prompt+argv-mirror / reader=reader.ts),本文件是引擎边界:
|
|
11
|
+
// - run:chat 域轮次(ChatRoundTicket 交接——编排层预建 record/identity 后经
|
|
12
|
+
// EnginePort 进入)与 workflow 域(executeAndAwait 委托)两分支;
|
|
13
|
+
// - interact:原生实现(D2——原编排层 deliverMessage/resumeRound 的 pi RPC stdin
|
|
14
|
+
// 协议知识下沉:sendPromptCommand 直调 + streamingBehavior 映射 + EPIPE 兜底 +
|
|
15
|
+
// 冷路径 resume 交接),编排层经 engine.interact 调用;
|
|
16
|
+
// - read:第①级委托共享 reader readPiSessionView(engines/pi/reader.ts)。
|
|
12
17
|
//
|
|
13
|
-
// pi 专有语义的隔离点在 task
|
|
14
|
-
// schemaEnv
|
|
18
|
+
// pi 专有语义的隔离点在 agentCallToExecuteOptions(本文件的直出映射:prompt→task、
|
|
19
|
+
// description→slug 截断、schemaEnv 派生)——本文件不出现第二个 pi 语义翻译点。
|
|
15
20
|
|
|
16
21
|
import { execFile } from "node:child_process";
|
|
17
22
|
import * as fs from "node:fs";
|
|
@@ -19,17 +24,19 @@ import * as path from "node:path";
|
|
|
19
24
|
|
|
20
25
|
import { getLogger } from "../../../../core/logger.ts";
|
|
21
26
|
|
|
22
|
-
import type { AgentResult as WorkflowAgentResult } from "../../../../orchestration/models/types.ts";
|
|
23
|
-
import
|
|
24
|
-
import {
|
|
27
|
+
import type { AgentCallOpts, AgentResult as WorkflowAgentResult } from "../../../../orchestration/models/types.ts";
|
|
28
|
+
import { SLUG_MAX_LENGTH } from "../../../../orchestration/models/types.ts";
|
|
29
|
+
import { stringifySchemaCached } from "../../../../shared/schema-jsonify.ts";
|
|
30
|
+
import type { PiInvocation } from "./pi-invocation.ts";
|
|
31
|
+
import { getPiInvocation } from "./pi-invocation.ts";
|
|
32
|
+
import type { AgentConfig, ModelInfo, ResolvedModel } from "../../../model-resolver.ts";
|
|
25
33
|
import type { StatusFilter } from "../../../record-store.ts";
|
|
26
34
|
import type { SubagentStream } from "../../../stream-sink.ts";
|
|
27
|
-
import type { AgentEvent, ExecutionRecord, ExecuteOptions, SubagentRecord } from "../../../types.ts";
|
|
35
|
+
import type { AgentEvent, AgentResult, ExecutionRecord, ExecuteOptions, SubagentRecord } from "../../../types.ts";
|
|
28
36
|
import type { EnginePort, EngineRunResult, RunContext } from "../../port.ts";
|
|
29
37
|
import { replayJournalToSessionView } from "../../common/journal-replay.ts";
|
|
30
38
|
import type {
|
|
31
39
|
AgentOutcome,
|
|
32
|
-
AgentTaskSpec,
|
|
33
40
|
EngineCapabilities,
|
|
34
41
|
EngineHandle,
|
|
35
42
|
InteractAction,
|
|
@@ -37,8 +44,23 @@ import type {
|
|
|
37
44
|
ProbeReport,
|
|
38
45
|
SessionView,
|
|
39
46
|
} from "../../types.ts";
|
|
40
|
-
import { taskSpecToExecuteOptions } from "./task-spec-mapper.ts";
|
|
41
47
|
import { readPiSessionView } from "./reader.ts";
|
|
48
|
+
import type { SessionRunnerContext, SpawnResumeOpts } from "./session-runner.ts";
|
|
49
|
+
import { getChildByRecord, killRecordChildWithEscalation, spawnedChildren } from "./session-runner.ts";
|
|
50
|
+
import {
|
|
51
|
+
acquireActivateLock,
|
|
52
|
+
armIdleTimer,
|
|
53
|
+
DEFAULT_IDLE_TIMEOUT_MS,
|
|
54
|
+
disarmIdleTimer,
|
|
55
|
+
} from "../../../lifecycle-manager.ts";
|
|
56
|
+
import { bestEffort } from "../../../best-effort.ts";
|
|
57
|
+
import {
|
|
58
|
+
clearEpipeFailure,
|
|
59
|
+
EPIPE_FAILURE_THRESHOLD,
|
|
60
|
+
recordEpipeFailure,
|
|
61
|
+
sendPromptCommand,
|
|
62
|
+
} from "./stdin-writer.ts";
|
|
63
|
+
import { toErrorMessage } from "../../../../core/error-message.ts";
|
|
42
64
|
|
|
43
65
|
const logger = getLogger("subagents");
|
|
44
66
|
|
|
@@ -57,11 +79,63 @@ const PROBE_VERSION_TIMEOUT_MS = 10_000;
|
|
|
57
79
|
/** interact 经 sessionFile 兜底定位 record 时的 collectRecords 扫描上限。 */
|
|
58
80
|
const INTERACT_SCAN_LIMIT = 1000;
|
|
59
81
|
|
|
82
|
+
/** chat 域轮次的身份快照(编排层 resolveIdentity 的产物形态——引擎侧透传给 launcher;
|
|
83
|
+
* 身份解析产物是 host 编排件,不并入任务声明 AgentCallOpts,见 ChatRoundTicket 注释)。 */
|
|
84
|
+
export interface ChatRoundIdentity {
|
|
85
|
+
agent: string;
|
|
86
|
+
agentConfig: AgentConfig | undefined;
|
|
87
|
+
resolved: ResolvedModel;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* chat 域预备轮次交接包(D2 单轨):编排层(executeViaEngine / 冷路径续轮)预建的
|
|
92
|
+
* record/opts/identity/host 上下文经 run 的 ctx.taskId 交接给引擎。
|
|
93
|
+
*
|
|
94
|
+
* [u-3b D6 双形态消化评估结论:双形态保留(结构性裁决,非过渡态)]
|
|
95
|
+
* run 的 task 形参(AgentCallOpts,任务声明)与 ticket 是两个变化轴的载体,不可合一:
|
|
96
|
+
* - task 形参 = 纯数据任务声明(跨引擎持久化语义,workflow 域由 SAR 直传);
|
|
97
|
+
* - ticket = chat 域 host 编排交接件,主体是运行期对象与 Service 内部形态——
|
|
98
|
+
* ① ctx: SessionRunnerContext 回调簇 / signal / priority / stream 是运行期句柄
|
|
99
|
+
* (port 设计 §3.3.5 删字段去向:运行期对象不入任务声明);
|
|
100
|
+
* ② record: ExecutionRecord 是 Service 内部 record 生命周期对象;
|
|
101
|
+
* ③ resume: SpawnResumeOpts 是 session-runner 私有 spawn 选项;
|
|
102
|
+
* ④ opts: ExecuteOptions 是 Service 内部编排形状(D6 明确保留的类型),且是
|
|
103
|
+
* forkFromSessionFile 的唯一 lossless 载体(该字段仅 pi chat 域消费,不入
|
|
104
|
+
* 合流形状——chat pi fork-from 链路 = 壳 fork-from action → ExecuteOptions
|
|
105
|
+
* .forkFromSessionFile → 本 ticket → runChatRound → runAndFinalize → runSpawn,
|
|
106
|
+
* 全程不经任务声明,由 pi-engine 测试锁定零回归)。
|
|
107
|
+
* 把任一类塞进 AgentCallOpts 会让 engine 契约反向耦合 execution 内部类型。故 chat
|
|
108
|
+
* 分支 ticket 优先消费(task 形参仅满足 port 签名),workflow 分支消费 task 形参——
|
|
109
|
+
* 这是「编排交接」与「任务声明」的职责分界,不是待消除的中间态。
|
|
110
|
+
*/
|
|
111
|
+
export interface ChatRoundTicket {
|
|
112
|
+
record: ExecutionRecord;
|
|
113
|
+
opts: ExecuteOptions;
|
|
114
|
+
identity: ChatRoundIdentity;
|
|
115
|
+
ctx: SessionRunnerContext;
|
|
116
|
+
signal: AbortSignal | undefined;
|
|
117
|
+
priority: number;
|
|
118
|
+
stream?: SubagentStream;
|
|
119
|
+
/** resume 选项(冷路径续轮):重开已 idle 的 session 续聊。undefined = 新 session。 */
|
|
120
|
+
resume?: SpawnResumeOpts;
|
|
121
|
+
}
|
|
122
|
+
|
|
60
123
|
/**
|
|
61
124
|
* PiEngine 委托的编排服务面——SubagentService 的结构子集(鸭子类型:生产实现是
|
|
62
125
|
* SubagentService 单例;测试可注入 fake,不必构造整个 Service)。
|
|
63
126
|
* 为什么用结构接口而非直接 import SubagentService 类型:pi-engine 只依赖它实际消费的
|
|
64
127
|
* 方法面,防 Service 内部演进(增删私有方法)连锁影响引擎适配层。
|
|
128
|
+
*
|
|
129
|
+
* chat 域轮次面(takeChatRound/runChatRound/resumeChatRound/reportRecordTransition)
|
|
130
|
+
* 声明为可选项(u-2c 聚合后形态):两处生产绑定(registration.ts / SAR)均绑
|
|
131
|
+
* subagentService.asEngineService(= piEngineServiceAdapter),可选面实际恒被提供——
|
|
132
|
+
* 并非「仅 chat 绑定的实例提供、SAR 的实例不提供」。两域分支改由 taskId 空间区分:
|
|
133
|
+
* chat ticket 的 key 是 record.id(编排层预建时 set),SAR 的 taskId 是独立铸造的
|
|
134
|
+
* `sa-<uuid>`(subprocess-agent-runner.ts),恒不在 ticket map 中 → run 的
|
|
135
|
+
* takeChatRound 探测恒 miss → 恒走 executeAndAwait 分支。可选面保留 optional 的意义
|
|
136
|
+
* 是测试可注入仅含 workflow 面的 fake;requireResumeFace / runChatTicket 的 throw
|
|
137
|
+
* 分支在生产绑定(asEngineService 全量提供)下不可达,仅防 fake 绑定漏面时静默
|
|
138
|
+
* 丢消息/丢轮次。
|
|
65
139
|
*/
|
|
66
140
|
export interface PiEngineService {
|
|
67
141
|
executeAndAwait(
|
|
@@ -71,10 +145,17 @@ export interface PiEngineService {
|
|
|
71
145
|
stream?: SubagentStream,
|
|
72
146
|
): Promise<WorkflowAgentResult>;
|
|
73
147
|
getRecordForAction(id: string): ExecutionRecord;
|
|
74
|
-
deliverMessage(record: ExecutionRecord, text: string, interrupt: boolean): Promise<void>;
|
|
75
148
|
closeSubagent(record: ExecutionRecord, force: boolean): Promise<void>;
|
|
76
149
|
cancel(id: string): boolean;
|
|
77
150
|
collectRecords(limit: number, statusFilter?: StatusFilter): SubagentRecord[];
|
|
151
|
+
/** chat 域轮次交接(run 的 chat 分支入口):按 taskId 取走预备包(一次性消费)。 */
|
|
152
|
+
takeChatRound?(taskId: string): ChatRoundTicket | undefined;
|
|
153
|
+
/** 执行预备的 chat 轮次(编排归 Service:pool 槽 + runSpawn + 终态迁移)。 */
|
|
154
|
+
runChatRound?(ticket: ChatRoundTicket): Promise<AgentResult>;
|
|
155
|
+
/** 冷路径续轮(interact message 分支的编排回调:守卫 + record 迁移 + 预备轮次 kick-off)。 */
|
|
156
|
+
resumeChatRound?(record: ExecutionRecord, text: string): void;
|
|
157
|
+
/** record 状态迁移上报(热路径投递后让 runtime 派生缓存失效 / GUI 回流)。 */
|
|
158
|
+
reportRecordTransition?(record: ExecutionRecord): void;
|
|
78
159
|
}
|
|
79
160
|
|
|
80
161
|
/** PiEngine 构造依赖。 */
|
|
@@ -142,6 +223,8 @@ export class PiEngine implements EnginePort {
|
|
|
142
223
|
interrupt: "kill-only",
|
|
143
224
|
// argv-mirror 镜像主进程 --approve 等 flag
|
|
144
225
|
permissionMode: "native",
|
|
226
|
+
// [D3-④] turn limiter + spawn watchdog 估算兑现轮数上限(预检 gate 据此放行)
|
|
227
|
+
maxTurns: true,
|
|
145
228
|
};
|
|
146
229
|
}
|
|
147
230
|
|
|
@@ -197,14 +280,19 @@ export class PiEngine implements EnginePort {
|
|
|
197
280
|
return report;
|
|
198
281
|
}
|
|
199
282
|
|
|
200
|
-
/** D1
|
|
201
|
-
|
|
283
|
+
/** D1 主语义:合流任务声明(AgentCallOpts)一次直出 spawn 编排参数,委托
|
|
284
|
+
* executeAndAwait(行为零变化——D6 后无 spec 往返中间态)。 */
|
|
285
|
+
async run(task: AgentCallOpts, ctx: RunContext): Promise<EngineRunResult> {
|
|
202
286
|
const service = this.requireService();
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
287
|
+
// [D2 单轨] chat 域轮次:编排层(executeViaEngine / 冷路径续轮)预建的
|
|
288
|
+
// record/identity/host 上下文经 ctx.taskId 交接(ChatRoundTicket)。chat 域任务
|
|
289
|
+
// 由 ticket lossless 携带(record/identity/SessionRunnerContext/
|
|
290
|
+
// forkFromSessionFile/resume 是 host 编排件,不入任务声明——双形态保留的结构性
|
|
291
|
+
// 裁决见 ChatRoundTicket 注释),task 形参仅满足 port 签名;workflow 域(SAR)
|
|
292
|
+
// 无 ticket,走下方 executeAndAwait 分支。
|
|
293
|
+
const ticket = service.takeChatRound?.(ctx.taskId);
|
|
294
|
+
if (ticket) return this.runChatTicket(service, ticket);
|
|
295
|
+
const opts = agentCallToExecuteOptions(task, ctx.ctxModel);
|
|
208
296
|
// P4 引擎留痕(D9①):record 侧投影——实际执行引擎 id 恒 pi(本引擎身份);
|
|
209
297
|
// engineFallback 由路由层经 RunContext 透传(无 fallback 缺省,record 字段零噪声)
|
|
210
298
|
opts.engine = PI_ENGINE_ID;
|
|
@@ -222,10 +310,10 @@ export class PiEngine implements EnginePort {
|
|
|
222
310
|
}
|
|
223
311
|
|
|
224
312
|
/**
|
|
225
|
-
* D1
|
|
226
|
-
* close=closeSubagent / cancel=cancel
|
|
227
|
-
*
|
|
228
|
-
*
|
|
313
|
+
* D1 交互控制面:message 分支原生实现(D2——原编排层 deliverMessage/resumeRound 的
|
|
314
|
+
* pi RPC stdin 协议知识下沉引擎边界);close=closeSubagent / cancel=cancel 委托编排面。
|
|
315
|
+
* message 的 interrupt(steer 抢占 vs followUp 排队)映射为 pi streamingBehavior——
|
|
316
|
+
* pi 权威裁决 busy/idle:busy 时 followUp 入队/steer 抢占,idle 时开新 turn。
|
|
229
317
|
*/
|
|
230
318
|
async interact(handle: EngineHandle, action: InteractAction): Promise<InteractResult> {
|
|
231
319
|
const service = this.requireService();
|
|
@@ -239,23 +327,245 @@ export class PiEngine implements EnginePort {
|
|
|
239
327
|
}
|
|
240
328
|
const record = this.resolveRecord(service, handle);
|
|
241
329
|
if (!record) return notResumable(handle);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
330
|
+
return await this.interactRecord(record, action);
|
|
331
|
+
} catch (err) {
|
|
332
|
+
// 投递/交互面以 throw 表达业务拒绝(not ready / EPIPE 兜底耗尽等,文案自带行动语言)
|
|
333
|
+
// ——转结构化失败,调用方拿 code+message 而非异常
|
|
334
|
+
return {
|
|
335
|
+
ok: false,
|
|
336
|
+
code: "engine_interact_failed",
|
|
337
|
+
message: toErrorMessage(err),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* interact 的 record 锚定形态(编排层直传已归属校验的 record——chat 域编排面
|
|
344
|
+
* deliverChatMessage 的调用入口;port face interact = handle 解析 + 本方法)。
|
|
345
|
+
* 不做二次 store 查找/归属校验:调用方(编排层)持有同一 record 对象,与旧直调
|
|
346
|
+
* 形态的字段读写逐字节一致。
|
|
347
|
+
*/
|
|
348
|
+
async interactRecord(record: ExecutionRecord, action: InteractAction): Promise<InteractResult> {
|
|
349
|
+
const service = this.requireService();
|
|
350
|
+
try {
|
|
351
|
+
return await this.interactRecordInner(service, record, action);
|
|
248
352
|
} catch (err) {
|
|
249
|
-
//
|
|
353
|
+
// 投递/交互面以 throw 表达业务拒绝(not ready / EPIPE 兜底耗尽等,文案自带行动语言)
|
|
250
354
|
// ——转结构化失败,调用方拿 code+message 而非异常
|
|
251
355
|
return {
|
|
252
356
|
ok: false,
|
|
253
357
|
code: "engine_interact_failed",
|
|
254
|
-
message:
|
|
358
|
+
message: toErrorMessage(err),
|
|
255
359
|
};
|
|
256
360
|
}
|
|
257
361
|
}
|
|
258
362
|
|
|
363
|
+
/** interact 两形态的公共主体(cancel 归 handle 形态——record 锚定形态的调用方用
|
|
364
|
+
* 编排层 cancel,不走这里)。 */
|
|
365
|
+
private async interactRecordInner(
|
|
366
|
+
service: PiEngineService,
|
|
367
|
+
record: ExecutionRecord,
|
|
368
|
+
action: InteractAction,
|
|
369
|
+
): Promise<InteractResult> {
|
|
370
|
+
if (action.kind === "message") {
|
|
371
|
+
await this.deliverPrompt(service, record, action.payload, action.interrupt === true);
|
|
372
|
+
return { ok: true, delivered: true };
|
|
373
|
+
}
|
|
374
|
+
if (action.kind === "close") {
|
|
375
|
+
await service.closeSubagent(record, action.payload?.force === true);
|
|
376
|
+
return { ok: true, delivered: true };
|
|
377
|
+
}
|
|
378
|
+
throw new Error(
|
|
379
|
+
`[pi-engine] cancel is handle-form only (record-anchored callers use the orchestration cancel). ` +
|
|
380
|
+
`Recovery: internal wiring error — use interact(handle, {kind:'cancel'}) instead.`,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* message 投递原生实现(协议知识下沉的本体,V2 决策 3):按**进程死活**分流,不按
|
|
386
|
+
* record.status——
|
|
387
|
+
*
|
|
388
|
+
* 热路径(进程活):prompt + streamingBehavior 直写 child.stdin(sendPromptCommand)。
|
|
389
|
+
* 冷路径(进程死):acquireActivateLock + 编排侧续轮回调(resumeChatRound——重开
|
|
390
|
+
* session + prompt;仅崩溃/timeout kill/跨重启命中)。
|
|
391
|
+
*
|
|
392
|
+
* EPIPE 兜底:stdin 管道断(进程实际已死但 close 事件未到)→ 按值守卫清理死句柄 +
|
|
393
|
+
* 连续失败计数 → 冷路径 resume + 原消息重放;连续达阈值(防死循环)throw 行动语言。
|
|
394
|
+
*/
|
|
395
|
+
private async deliverPrompt(
|
|
396
|
+
service: PiEngineService,
|
|
397
|
+
record: ExecutionRecord,
|
|
398
|
+
text: string,
|
|
399
|
+
interrupt: boolean,
|
|
400
|
+
): Promise<void> {
|
|
401
|
+
// 新 turn,disarm idle timer(防 turn 期间误杀活进程,V2 决策 4)
|
|
402
|
+
disarmIdleTimer(record.id);
|
|
403
|
+
const child = getChildByRecord(record.id);
|
|
404
|
+
if (child && !child.killed) {
|
|
405
|
+
// 热路径:进程活(running/idle 都可能是热路径——V2 进程长驻,idle 态进程仍在内存)
|
|
406
|
+
record.status = "running";
|
|
407
|
+
// 刷新 pid 内存记账(resume spawn 后 child.pid 已变,顺便更新)
|
|
408
|
+
if (child.pid !== undefined) record.pid = child.pid;
|
|
409
|
+
try {
|
|
410
|
+
sendPromptCommand(child, text, { streamingBehavior: interrupt ? "steer" : "followUp" });
|
|
411
|
+
// 热路径成功,清零 EPIPE 连续失败计数([v4 A-1] 计数器在 stdin-writer)
|
|
412
|
+
clearEpipeFailure(record.id);
|
|
413
|
+
// [race-F5] 写后死进程检测:write 同步成功只代表数据进了内核 pipe 缓冲,子进程
|
|
414
|
+
// 可能在读取前死亡(gate/idle kill 竞速)。exitCode/signalCode 已非 null = 进程已死
|
|
415
|
+
//(close 事件可能尚未到达),缓冲中的消息将被静默丢弃。只 warn 留证(含消息类型),
|
|
416
|
+
// 不抛错不重试:终态回收已由 kill 路径保证,对死进程重试反而可能二次写。
|
|
417
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
418
|
+
logger.warn(
|
|
419
|
+
`[subagents] deliverMessage: child ${record.id} died around stdin write, message may be lost`,
|
|
420
|
+
{
|
|
421
|
+
msgType: interrupt ? "steer" : "followUp",
|
|
422
|
+
exitCode: child.exitCode,
|
|
423
|
+
signalCode: child.signalCode,
|
|
424
|
+
},
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
// 轮始执行态信号清除 + 迁移上报(residual-fixes U3 补全,与冷路径续轮对称):
|
|
428
|
+
// 新一轮开跑 = 无轮终信号——清上一轮 result(§5.4 isStreaming 公式要求
|
|
429
|
+
// result undefined 才显示 streaming)与 resumable,appendEntry 让 runtime/W18
|
|
430
|
+
// 派生缓存失效、GUI 侧从 waiting 切回 spinner。仅在投递成功后清(失败保留
|
|
431
|
+
// 上一轮信号,EPIPE 兜底走续轮时由其再清)。
|
|
432
|
+
record.result = undefined;
|
|
433
|
+
record.resumable = undefined;
|
|
434
|
+
service.reportRecordTransition?.(record);
|
|
435
|
+
} catch (err) {
|
|
436
|
+
// EPIPE 兜底:stdin 管道已断,进程实际已死但 close 事件尚未到达。
|
|
437
|
+
// 检测 EPIPE 关键词 → 进程按 dead 处理 → 自动转冷路径 resume + 消息重放。
|
|
438
|
+
// 本兜底不持 activateLock,但与冷路径共用续轮的在途守卫(编排侧
|
|
439
|
+
// resumesInFlight):resume 已在途时兜底的续轮调用被拒(throw 行动语言),
|
|
440
|
+
// 不会二次 spawn。
|
|
441
|
+
if (err instanceof Error && err.message.includes("EPIPE")) {
|
|
442
|
+
logger.warn(`[subagents] EPIPE on hot path for ${record.id}, falling back to cold path resume`, {
|
|
443
|
+
detail: err.message,
|
|
444
|
+
});
|
|
445
|
+
// 清理 spawnedChildren 中的死进程条目(让续轮能重新 spawn)。
|
|
446
|
+
// [M4] 按值守卫:仅当 Map 当前值仍是本次写 EPIPE 的 child 才删——若已被 resume
|
|
447
|
+
// spawn 覆盖为新 child(close 事件先于本 catch 到达的极端时序),不误删新注册
|
|
448
|
+
//(与 session-runner removeChildRegistration 同语义)。
|
|
449
|
+
if (spawnedChildren.get(record.id) === child) {
|
|
450
|
+
spawnedChildren.delete(record.id);
|
|
451
|
+
}
|
|
452
|
+
// 递增连续 EPIPE 计数([v4 A-1] helper 合并同步/异步路径计数)
|
|
453
|
+
const count = recordEpipeFailure(record.id);
|
|
454
|
+
if (count >= EPIPE_FAILURE_THRESHOLD) {
|
|
455
|
+
// 连续达阈值 EPIPE → 不再尝试 resume,throw 含恢复指引
|
|
456
|
+
clearEpipeFailure(record.id);
|
|
457
|
+
throw new Error(
|
|
458
|
+
`[subagents] EPIPE fallback exhausted for ${record.id}: ${count} consecutive EPIPE failures. ` +
|
|
459
|
+
`Recovery: use action:'close' to clean up, then action:'start' a new subagent.`,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
// 冷路径 resume + 原消息重放(v4 B-1: status 已 running,续轮守卫直接放行)
|
|
463
|
+
this.requireResumeFace(service)(record, text);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
// [T2⑧ / PS-3] 非 EPIPE 写失败(如 ERR_STREAM_DESTROYED——错误分类只认 "EPIPE"
|
|
467
|
+
// 子串,过窄):deliverPrompt 入口已无条件 disarmIdleTimer,若直接 rethrow 不
|
|
468
|
+
// re-arm——「timer armed」这个防泄漏前提在失败路径永久丢失:进程无人回收
|
|
469
|
+
//(无 timer、无轮、record=running)泄漏至宿主退出,hasRunningBackground 恒真
|
|
470
|
+
// 还会顺延其他完成通知。rethrow 前再武装 idle timer(含防御性降级),恢复进程
|
|
471
|
+
// 回收通道 + 通知放行门;调用方错误语义不变(仍 rethrow)。re-arm 挂载位在本
|
|
472
|
+
// catch(引擎边界)而非编排层 interactRecord:只有这里能区分「stdin 写失败」
|
|
473
|
+
// 与业务拒绝,编排层盲目 re-arm 会误武装。
|
|
474
|
+
this.rearmIdleTimerAfterHotPathFailure(record, err);
|
|
475
|
+
// 非 EPIPE 错误——不应发生,重新抛出让调用方处理
|
|
476
|
+
throw err;
|
|
477
|
+
}
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
// 冷路径:进程死(idle timer reap / 崩溃 / 跨重启),record 应为 idle-resumable → resume spawn。
|
|
481
|
+
// D3:acquireActivateLock 双保险——注意锁只覆盖续轮同步段,释放在子进程注册
|
|
482
|
+
//(session-runner spawnedChildren.set)之前(中间隔 pool.acquire await + tempFile 等异步点)。
|
|
483
|
+
// 真正的单写者守卫是续轮的在途守卫(编排侧 resumesInFlight):锁释放后、child
|
|
484
|
+
// 注册前到达的第二次冷路径 message 在续轮处被拒,不会二次 spawn。
|
|
485
|
+
const releaseLock = await acquireActivateLock(record.id);
|
|
486
|
+
try {
|
|
487
|
+
this.requireResumeFace(service)(record, text);
|
|
488
|
+
} finally {
|
|
489
|
+
releaseLock();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* [T2⑧ / PS-3] 热路径非 EPIPE 写失败后的 idle timer 再武装(deliverPrompt 入口
|
|
495
|
+
* 无条件 disarm 的对称恢复)。首选 record.idleTimeoutMs(armIdleTimer 对超域值
|
|
496
|
+
* fail-fast throw,U1),非法时防御性降级 DEFAULT_IDLE_TIMEOUT_MS;两级都失败落
|
|
497
|
+
* bestEffort 留证(不再向上抛——本方法服务于「进程回收通道不丢」,错误本体由
|
|
498
|
+
* 调用方 rethrow)。超时处置 = killRecordChildWithEscalation(与 idle GC 常规路径
|
|
499
|
+
* 同款升级杀链)。
|
|
500
|
+
*/
|
|
501
|
+
private rearmIdleTimerAfterHotPathFailure(record: ExecutionRecord, cause: unknown): void {
|
|
502
|
+
const detail = toErrorMessage(cause);
|
|
503
|
+
const onTimeout = (): void => {
|
|
504
|
+
killRecordChildWithEscalation(record.id, "idle timer (hot-path failure fallback)");
|
|
505
|
+
};
|
|
506
|
+
try {
|
|
507
|
+
armIdleTimer(record.id, onTimeout, record.idleTimeoutMs);
|
|
508
|
+
} catch {
|
|
509
|
+
try {
|
|
510
|
+
armIdleTimer(record.id, onTimeout, DEFAULT_IDLE_TIMEOUT_MS);
|
|
511
|
+
} catch (fallbackErr) {
|
|
512
|
+
bestEffort(fallbackErr, "rearmIdleTimer fallback (deliverPrompt non-EPIPE failure)", "error");
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
logger.warn(
|
|
517
|
+
`[subagents] deliverPrompt hot path failed for ${record.id}; idle timer re-armed to keep process recovery bounded`,
|
|
518
|
+
{ detail },
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** chat 域轮次执行回调的守卫提取(可选项缺失 = 引擎绑定形态错误——chat 编排面
|
|
523
|
+
* 必须由 chat 绑定的适配器提供;静默 no-op 会丢消息,宁可显式 throw)。 */
|
|
524
|
+
private requireResumeFace(service: PiEngineService): (record: ExecutionRecord, text: string) => void {
|
|
525
|
+
const resume = service.resumeChatRound;
|
|
526
|
+
if (!resume) {
|
|
527
|
+
throw new Error(
|
|
528
|
+
`[pi-engine] chat resume face unavailable on this engine binding (workflow-only service binding). ` +
|
|
529
|
+
`Recovery: internal wiring error — message delivery requires the chat-bound engine instance; report this.`,
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
return resume;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* chat 轮次执行(run 的 chat 分支主体):编排归 Service(runChatRound——pool 槽 +
|
|
537
|
+
* runSpawn + 终态迁移,行为零变化),引擎侧构造 handle/outcome。chat 编排不消费
|
|
538
|
+
* 返回值(notify/终态迁移在编排侧 kickOff 回调),handle/outcome 是 port 契约的
|
|
539
|
+
* 形态完备(interact/read 以 recordId/sessionFile 定位)。
|
|
540
|
+
*/
|
|
541
|
+
private async runChatTicket(service: PiEngineService, ticket: ChatRoundTicket): Promise<EngineRunResult> {
|
|
542
|
+
const runChatRound = service.runChatRound;
|
|
543
|
+
if (!runChatRound) {
|
|
544
|
+
throw new Error(
|
|
545
|
+
`[pi-engine] chat round runner unavailable on this engine binding (workflow-only service binding). ` +
|
|
546
|
+
`Recovery: internal wiring error — chat rounds require the chat-bound engine instance; report this.`,
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
const result = await runChatRound(ticket);
|
|
550
|
+
const sessionFile = ticket.record.sessionFile;
|
|
551
|
+
return {
|
|
552
|
+
handle: {
|
|
553
|
+
data: {
|
|
554
|
+
v: 1,
|
|
555
|
+
engineId: PI_ENGINE_ID,
|
|
556
|
+
// pi 定位符:recordId(interact 控制面 key)+ sessionFile(read 第①级)
|
|
557
|
+
sessionRef: {
|
|
558
|
+
recordId: ticket.record.id,
|
|
559
|
+
...(sessionFile !== undefined ? { sessionFile } : {}),
|
|
560
|
+
},
|
|
561
|
+
poolKey: PI_POOL_KEY,
|
|
562
|
+
adapterVersion: PI_ADAPTER_VERSION,
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
outcome: chatResultToOutcome(result, sessionFile, ticket.record.worktreeHandle?.path),
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
259
569
|
/**
|
|
260
570
|
* D6 read 三级降级:①pi session JSONL 原生读取(readPiSessionView——共享 reader
|
|
261
571
|
* 单一实现,sessionRead: 'full' 的依据)→ ②宿主 event journal 重放(P4 接线:
|
|
@@ -329,6 +639,68 @@ export class PiEngine implements EnginePort {
|
|
|
329
639
|
|
|
330
640
|
// ── 模块级纯函数(可单测)──
|
|
331
641
|
|
|
642
|
+
/**
|
|
643
|
+
* [D6 一次直出] 合流任务声明 AgentCallOpts → ExecuteOptions(pi spawn 编排参数)。
|
|
644
|
+
*
|
|
645
|
+
* 这是 pi 边界的唯一任务形状映射点,取代已删除的两级链路
|
|
646
|
+
* (execute-options-mapper.mapToExecuteOptions → task-spec-mapper.taskSpecToExecuteOptions
|
|
647
|
+
* ——原链路 AgentCallOpts→ExecuteOptions→AgentTaskSpec→ExecuteOptions 三态两映射,
|
|
648
|
+
* 缺省路径付双份多引擎税)。逐字段等值性(合流前 ↔ 合流后):
|
|
649
|
+
* task ← prompt(原 prompt→ExecuteOptions.task→spec.task 恒等往返)
|
|
650
|
+
* slug ← description ?? agent ?? "workflow-agent",超 SLUG_MAX_LENGTH(35) 截断
|
|
651
|
+
* (原 mapToExecuteOptions 的派生规则原样内联;截断常量迁至
|
|
652
|
+
* orchestration/models/types.ts 与 description 字段同文件)
|
|
653
|
+
* agent ← agent(原 spec.agent ?? persona.agentRef——agentRef 无生产写入方,
|
|
654
|
+
* 恒 undefined,随 PersonaSpec 裁撤)
|
|
655
|
+
* model ← model(显式 override 透传,ctxModel 不压扁填底)
|
|
656
|
+
* thinkingLevel ← thinkingLevel(原 effort 恒等映射层删除)
|
|
657
|
+
* skillPath / appendSystemPrompt ← 同名平铺(原 persona 收拢/还原层删除——往返恒等)
|
|
658
|
+
* schema ← schema
|
|
659
|
+
* schemaEnv ← schema 派生优先(stringifySchemaCached compact,与
|
|
660
|
+
* agent-opts-resolver 同函数同缓存,逐字节等值);无 schema 时
|
|
661
|
+
* schemaEnv 兜底(原 RunContext.schemaEnv 兜底通道的合流形态等价
|
|
662
|
+
* ——SAR 填 ctx.schemaEnv 的源即 opts.schemaEnv);均无 → undefined
|
|
663
|
+
* (BC-6:childEnv 不注入)
|
|
664
|
+
* maxTurns / graceTurns / fork / worktree / cwd / conversation / idleTimeoutMs ← 同名
|
|
665
|
+
* ctxModel ← runCtx.ctxModel(运行期字段,D-008 兼底链路)
|
|
666
|
+
* engine / engineFallback 由 run 调用方追加(P4 引擎留痕,D9①)
|
|
667
|
+
* 调用方扩展字段(scene/timeoutMs/skill/schemaEnv 已述/engine/returnMeta)中引擎
|
|
668
|
+
* 不消费的:timeoutMs 由 SAR mergeTimeoutSignal 合并进 signal、engine 由 SAR 路由层
|
|
669
|
+
* 消费、scene/returnMeta 为 worker 层字段——均不入 spawn 参数。
|
|
670
|
+
*
|
|
671
|
+
* 字段完整性对照表(实施期门⑤产物)固化在 __tests__/spawn-opts-direct.test.ts,
|
|
672
|
+
* 逐字段断言覆盖。
|
|
673
|
+
*/
|
|
674
|
+
export function agentCallToExecuteOptions(
|
|
675
|
+
task: AgentCallOpts,
|
|
676
|
+
ctxModel?: ModelInfo,
|
|
677
|
+
): ExecuteOptions {
|
|
678
|
+
// slug:优先 description,缺失回落 agent 名(保证非空)。超长截断。
|
|
679
|
+
const rawSlug = task.description ?? task.agent ?? "workflow-agent";
|
|
680
|
+
return {
|
|
681
|
+
task: task.prompt,
|
|
682
|
+
slug: rawSlug.length > SLUG_MAX_LENGTH ? rawSlug.slice(0, SLUG_MAX_LENGTH) : rawSlug,
|
|
683
|
+
agent: task.agent,
|
|
684
|
+
model: task.model,
|
|
685
|
+
thinkingLevel: task.thinkingLevel,
|
|
686
|
+
skillPath: task.skillPath,
|
|
687
|
+
appendSystemPrompt: task.appendSystemPrompt,
|
|
688
|
+
schema: task.schema,
|
|
689
|
+
// schemaEnv 派生优先(等值依据见函数注释);无 schema 且调用方持有预编码值时兜底
|
|
690
|
+
schemaEnv: task.schema !== undefined
|
|
691
|
+
? stringifySchemaCached(task.schema, "compact")
|
|
692
|
+
: task.schemaEnv,
|
|
693
|
+
maxTurns: task.maxTurns,
|
|
694
|
+
graceTurns: task.graceTurns,
|
|
695
|
+
ctxModel,
|
|
696
|
+
fork: task.fork,
|
|
697
|
+
worktree: task.worktree,
|
|
698
|
+
cwd: task.cwd,
|
|
699
|
+
conversation: task.conversation,
|
|
700
|
+
idleTimeoutMs: task.idleTimeoutMs,
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
332
704
|
/** sessionRef 取 string 值的运行时 guard(Record 索引读经 typeof 收窄,非裸取)。 */
|
|
333
705
|
function refString(ref: Record<string, string>, key: string): string | undefined {
|
|
334
706
|
const v = ref[key];
|
|
@@ -354,7 +726,7 @@ async function defaultProbeVersion(invocation: PiInvocation): Promise<string | u
|
|
|
354
726
|
// debug 级留诊断线索即可,不刷 info/warn
|
|
355
727
|
logger.debug(
|
|
356
728
|
`[pi-engine] probe version check failed (best-effort continue): ${
|
|
357
|
-
|
|
729
|
+
toErrorMessage(err)
|
|
358
730
|
}`,
|
|
359
731
|
);
|
|
360
732
|
return undefined;
|
|
@@ -376,7 +748,7 @@ function isInvocationResolvable(invocation: PiInvocation): boolean {
|
|
|
376
748
|
// 单目录探测失败(权限等)继续扫下一个——debug 级留线索即可
|
|
377
749
|
logger.debug(
|
|
378
750
|
`[pi-engine] PATH dir probe failed (continue scanning): ${dir}: ${
|
|
379
|
-
|
|
751
|
+
toErrorMessage(err)
|
|
380
752
|
}`,
|
|
381
753
|
);
|
|
382
754
|
}
|
|
@@ -384,10 +756,34 @@ function isInvocationResolvable(invocation: PiInvocation): boolean {
|
|
|
384
756
|
return false;
|
|
385
757
|
}
|
|
386
758
|
|
|
759
|
+
/** execution AgentResult(chat 轮次)→ AgentOutcome(port 契约形态完备;chat 编排
|
|
760
|
+
* 侧不消费——终态迁移/notify 在编排层)。 */
|
|
761
|
+
function chatResultToOutcome(
|
|
762
|
+
result: AgentResult,
|
|
763
|
+
sessionFile: string | undefined,
|
|
764
|
+
worktreePath: string | undefined,
|
|
765
|
+
): AgentOutcome {
|
|
766
|
+
return {
|
|
767
|
+
content: result.text,
|
|
768
|
+
...(result.parsedOutput !== undefined ? { parsedOutput: result.parsedOutput } : {}),
|
|
769
|
+
durationMs: result.durationMs,
|
|
770
|
+
...(result.error !== undefined ? { error: result.error } : {}),
|
|
771
|
+
sessionId: result.sessionId,
|
|
772
|
+
...(sessionFile !== undefined ? { sessionFile } : {}),
|
|
773
|
+
...(worktreePath !== undefined ? { worktreePath } : {}),
|
|
774
|
+
// toolCalls 不映射(execution ToolCall ≠ AgentOutcome 的 orchestration ToolCallEntry,
|
|
775
|
+
// chat 编排侧不消费本 outcome——终态记账在编排层 record 通路)
|
|
776
|
+
engineId: PI_ENGINE_ID,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
|
|
387
780
|
/** orchestration AgentResult → AgentOutcome(补 engineId;exitCode 无来源缺省)。 */
|
|
388
781
|
function workflowResultToOutcome(wf: WorkflowAgentResult, ctx?: RunContext): AgentOutcome {
|
|
389
782
|
return {
|
|
390
783
|
content: wf.content,
|
|
784
|
+
// [D5-③] 失败分诊标签透传(产出侧 output-collector → mapper → 此处 → SAR →
|
|
785
|
+
// executeAgentCall 读字段分诊)。成功路径 undefined 不落键。
|
|
786
|
+
...(wf.failureKind !== undefined ? { failureKind: wf.failureKind } : {}),
|
|
391
787
|
parsedOutput: wf.parsedOutput,
|
|
392
788
|
usage: wf.usage,
|
|
393
789
|
durationMs: wf.durationMs,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/execution/pi-invocation.ts
|
|
1
|
+
// src/execution/engine/engines/pi/pi-invocation.ts
|
|
2
2
|
//
|
|
3
3
|
// 定位 pi 二进制并组装 spawn 调用。Core 叶子原语(仅依赖 node 内置)。
|
|
4
4
|
//
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
|
|
19
|
-
import { isRelayActive, RELAY_ENV_NODE, RELAY_ENV_SCRIPT } from "
|
|
19
|
+
import { isRelayActive, RELAY_ENV_NODE, RELAY_ENV_SCRIPT } from "../../../relay-env.ts";
|
|
20
20
|
|
|
21
21
|
/** spawn 调用描述符:command + args(透传给 child_process.spawn)。 */
|
|
22
22
|
export interface PiInvocation {
|
|
@@ -17,6 +17,17 @@
|
|
|
17
17
|
// xyz-agent runtime 侧的 pi 历史读取链(getHistoryFromFilePath)是独立实现(shared
|
|
18
18
|
// Message 投影),不消费本模块——两链各自保持现状(P5 只对非 pi 引擎引入共享
|
|
19
19
|
// reader;pi 的 runtime 链路回归由现有测试守护)。
|
|
20
|
+
//
|
|
21
|
+
// [D2 实施期门②裁决:保留(2026-09-02,u-2a)] 现状 readPiSessionView 仅被
|
|
22
|
+
// PiEngine.read(①级原生层)消费、read 面生产零调用(pi 历史走 runtime 自有 JSONL
|
|
23
|
+
// 直读链)——但 EnginePort.read 是四能力面的非可选成员(port.ts),pi 的
|
|
24
|
+
// capabilities.sessionRead='full' 与 conformance read 降级契约(contract.read-
|
|
25
|
+
// degradation)以此为①级实现;删除本模块会使 pi 的 read 面空心化(②③级兜底直通),
|
|
26
|
+
// 与终态图(dual-track-convergence §3.5:engines/pi/ 四件套含 reader)矛盾。保留
|
|
27
|
+
// 不构成第三个 SessionView 装配实现:u-1a 的 session-view-service(GUI 生产链)对
|
|
28
|
+
// pi 分支维持防御性空返回(A1 守护,不 import 本模块),两链零交叠;read 面的
|
|
29
|
+
// 生产调用方归属终态「extension 内 EnginePort.read() 复用同一 module」接线
|
|
30
|
+
// (§3.5 GUI 读取路径),不在 u-2a 范围。
|
|
20
31
|
|
|
21
32
|
import { reconstructFromFile } from "../../../session-reconstructor.ts";
|
|
22
33
|
import type { SessionView } from "../../types.ts";
|
|
@@ -13,7 +13,7 @@ import { registerEngine } from "../../registry.ts";
|
|
|
13
13
|
import { PI_ENGINE_ID, PiEngine } from "./pi-engine.ts";
|
|
14
14
|
import type { PiEngineService } from "./pi-engine.ts";
|
|
15
15
|
|
|
16
|
-
export { PI_ADAPTER_VERSION, PI_ENGINE_ID,
|
|
16
|
+
export { PI_ADAPTER_VERSION, PI_ENGINE_ID, PiEngine } from "./pi-engine.ts";
|
|
17
17
|
export type { PiEngineDeps, PiEngineService } from "./pi-engine.ts";
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -31,5 +31,7 @@ export function createPiEngine(getService: () => PiEngineService | null): PiEngi
|
|
|
31
31
|
* 只会拿到引擎实例,真正 run 时才解析服务。
|
|
32
32
|
*/
|
|
33
33
|
export function registerPiEngine(): void {
|
|
34
|
-
|
|
34
|
+
// [D4 聚合连带] 查询/交互面聚合后 Service 不再整体结构化兼容 PiEngineService——
|
|
35
|
+
// 经 asEngineService 显式视图适配(成员集合与原直绑等价)。
|
|
36
|
+
registerEngine(PI_ENGINE_ID, () => createPiEngine(() => getSubagentService()?.asEngineService ?? null));
|
|
35
37
|
}
|