@zhushanwen/pi-subagent-workflow 8.6.0 → 8.7.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/package.json +9 -13
- package/relay/relay.mjs +1 -1
- package/src/host/__tests__/pi-host.test.ts +214 -0
- package/src/host/pi-host.ts +109 -0
- package/src/index.ts +89 -32
- package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
- package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
- package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
- package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
- package/src/injectors/engine-awareness.ts +123 -0
- package/src/injectors/model-list-injector.ts +1 -1
- package/src/injectors/subagent-list-injector.ts +8 -8
- package/src/injectors/workflow-list-injector.ts +8 -8
- package/src/interface/__tests__/detectors.test.ts +3 -3
- package/src/interface/__tests__/subagent-tool-path-guard.test.ts +2 -2
- package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
- package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
- package/src/interface/bg-notify-render.ts +4 -4
- package/src/interface/commands.ts +3 -3
- package/src/interface/format.ts +2 -2
- package/src/interface/helpers.ts +1 -1
- package/src/interface/list-component.ts +4 -4
- package/src/interface/list-shared.ts +2 -2
- package/src/interface/list-view.ts +2 -2
- package/src/interface/subagent-actions.ts +263 -11
- package/src/interface/subagent-tool-schema.ts +20 -13
- package/src/interface/subagent-tool.ts +11 -6
- package/src/interface/subagents.ts +3 -3
- package/src/interface/tool-render.ts +34 -2
- package/src/interface/tool-workflow-script.ts +4 -4
- package/src/interface/tool-workflow.ts +6 -6
- package/src/interface/views/WorkflowsView.ts +5 -5
- package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
- package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
- package/src/interface/views/detail-content.ts +4 -4
- package/src/interface/views/format.ts +4 -4
- package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +11 -11
- package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
- package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
- package/src/execution/__tests__/agent-registry.test.ts +0 -363
- package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
- package/src/execution/__tests__/alive-store.test.ts +0 -147
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
- package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
- package/src/execution/__tests__/bg-notify-render.test.ts +0 -329
- package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
- package/src/execution/__tests__/chat-engine-routing.test.ts +0 -601
- package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
- package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
- package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
- package/src/execution/__tests__/config.test.ts +0 -110
- package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
- package/src/execution/__tests__/crash-recovery.test.ts +0 -320
- package/src/execution/__tests__/delivery-methods.test.ts +0 -422
- package/src/execution/__tests__/dialog-queue.test.ts +0 -299
- package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
- package/src/execution/__tests__/execute-nesting.test.ts +0 -323
- package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
- package/src/execution/__tests__/execution-record.test.ts +0 -1394
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +0 -171
- package/src/execution/__tests__/finalize-record.test.ts +0 -368
- package/src/execution/__tests__/finalized-marker.test.ts +0 -82
- package/src/execution/__tests__/format-schema-instruction.test.ts +0 -169
- package/src/execution/__tests__/format.test.ts +0 -458
- package/src/execution/__tests__/gc-timer.test.ts +0 -184
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
- package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
- package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
- package/src/execution/__tests__/helpers/spawn-mock.ts +0 -242
- package/src/execution/__tests__/host-mode.test.ts +0 -87
- package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
- package/src/execution/__tests__/index-session-start.test.ts +0 -672
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/lifecycle-manager.test.ts +0 -383
- package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
- package/src/execution/__tests__/list-component.test.ts +0 -401
- package/src/execution/__tests__/list-fields.test.ts +0 -140
- package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
- package/src/execution/__tests__/model-resolver.test.ts +0 -465
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
- package/src/execution/__tests__/nested-visibility.test.ts +0 -325
- package/src/execution/__tests__/notifier-flush.test.ts +0 -612
- package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
- package/src/execution/__tests__/notify-ledger.test.ts +0 -826
- package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
- package/src/execution/__tests__/output-collector.test.ts +0 -358
- package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
- package/src/execution/__tests__/path-encoding.test.ts +0 -104
- package/src/execution/__tests__/pi-invocation.test.ts +0 -134
- package/src/execution/__tests__/record-store.test.ts +0 -1057
- package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
- package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
- package/src/execution/__tests__/relay-agent.test.ts +0 -448
- package/src/execution/__tests__/relay-env.test.ts +0 -42
- package/src/execution/__tests__/resource-policy.test.ts +0 -109
- package/src/execution/__tests__/rpc-mode.test.ts +0 -89
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
- package/src/execution/__tests__/run-spawn-edges.test.ts +0 -687
- package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
- package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -199
- package/src/execution/__tests__/sdk-contract.test.ts +0 -273
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
- package/src/execution/__tests__/session-file-gc.test.ts +0 -293
- package/src/execution/__tests__/session-pending.test.ts +0 -197
- package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
- package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
- package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -350
- package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
- package/src/execution/__tests__/spawn-args.test.ts +0 -445
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
- package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
- package/src/execution/__tests__/spawned-children.test.ts +0 -92
- package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
- package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
- package/src/execution/__tests__/status-refactor.test.ts +0 -345
- package/src/execution/__tests__/stdin-writer.test.ts +0 -463
- package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
- package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
- package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
- package/src/execution/__tests__/subagent-service.test.ts +0 -788
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -599
- package/src/execution/__tests__/temp-prompt.test.ts +0 -53
- package/src/execution/__tests__/timeout-integration.test.ts +0 -615
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/__tests__/tool-action.test.ts +0 -476
- package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
- package/src/execution/__tests__/turn-limiter.test.ts +0 -65
- package/src/execution/__tests__/ui-channels.test.ts +0 -187
- package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
- package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
- package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
- package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
- package/src/execution/__tests__/worktree-manager.test.ts +0 -633
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
- package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
- package/src/execution/__tests__/worktree-registry.test.ts +0 -199
- package/src/execution/agent-registry.ts +0 -199
- package/src/execution/agent-result-mapper.ts +0 -90
- package/src/execution/alive-store.ts +0 -92
- package/src/execution/argv-mirror.ts +0 -113
- package/src/execution/best-effort.ts +0 -38
- package/src/execution/channel-registry-access.ts +0 -144
- package/src/execution/concurrency-pool.ts +0 -116
- package/src/execution/config.ts +0 -96
- package/src/execution/dialog-queue.ts +0 -330
- package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
- package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
- package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
- package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
- package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
- package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
- package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
- package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
- package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
- package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
- package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
- package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
- package/src/execution/engine/__tests__/paths.test.ts +0 -39
- package/src/execution/engine/__tests__/registry.test.ts +0 -120
- package/src/execution/engine/__tests__/routing.test.ts +0 -231
- package/src/execution/engine/common/data-dir.ts +0 -62
- package/src/execution/engine/common/errors.ts +0 -183
- package/src/execution/engine/common/event-journal.ts +0 -254
- package/src/execution/engine/common/journal-replay.ts +0 -62
- package/src/execution/engine/common/kill-chain.ts +0 -221
- package/src/execution/engine/common/nesting-guard.ts +0 -50
- package/src/execution/engine/common/persona-router.ts +0 -108
- package/src/execution/engine/common/pool-manager.ts +0 -226
- package/src/execution/engine/common/schema-emulation.ts +0 -189
- package/src/execution/engine/common/session-view-projection.ts +0 -51
- package/src/execution/engine/engine-discovery.ts +0 -65
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
- package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
- package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
- package/src/execution/engine/engines/pi/reader.ts +0 -48
- package/src/execution/engine/engines/pi/registration.ts +0 -35
- 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__/parser.test.ts +0 -246
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
- package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
- package/src/execution/engine/engines/zcode/constants.ts +0 -43
- package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/engine/engines/zcode/parser.ts +0 -436
- package/src/execution/engine/engines/zcode/preparer.ts +0 -363
- package/src/execution/engine/engines/zcode/reader.ts +0 -381
- package/src/execution/engine/engines/zcode/registration.ts +0 -37
- package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -658
- package/src/execution/engine/host-task-spec.ts +0 -47
- package/src/execution/engine/model-prompt.ts +0 -59
- package/src/execution/engine/paths.ts +0 -42
- package/src/execution/engine/port.ts +0 -153
- package/src/execution/engine/registry.ts +0 -123
- package/src/execution/engine/routing.ts +0 -218
- package/src/execution/engine/types.ts +0 -309
- package/src/execution/execute-options-mapper.ts +0 -112
- package/src/execution/execution-record.ts +0 -965
- package/src/execution/finalize-record.ts +0 -251
- package/src/execution/finalized-marker.ts +0 -51
- package/src/execution/get-state-handshake.ts +0 -104
- package/src/execution/host-mode.ts +0 -56
- package/src/execution/idle-gc.ts +0 -47
- package/src/execution/lifecycle-manager.ts +0 -513
- package/src/execution/lifecycle-predicates.ts +0 -65
- package/src/execution/manifest-store.ts +0 -256
- package/src/execution/model-config-service.ts +0 -219
- package/src/execution/model-resolver.ts +0 -248
- package/src/execution/notifier.ts +0 -330
- package/src/execution/notify-ledger.ts +0 -580
- package/src/execution/output-collector.ts +0 -228
- package/src/execution/path-encoding.ts +0 -60
- package/src/execution/pi-invocation.ts +0 -120
- package/src/execution/record-entry.ts +0 -132
- package/src/execution/record-store.ts +0 -1234
- package/src/execution/relay-env.ts +0 -37
- package/src/execution/session-context-resolver.ts +0 -64
- package/src/execution/session-file-gc.ts +0 -120
- package/src/execution/session-pending.ts +0 -164
- package/src/execution/session-reconstructor.ts +0 -678
- package/src/execution/session-runner.ts +0 -1781
- package/src/execution/sessions-index.ts +0 -304
- package/src/execution/spawn-event-adapter.ts +0 -363
- package/src/execution/stdin-writer.ts +0 -198
- package/src/execution/stream-sink.ts +0 -126
- package/src/execution/subagent-service.ts +0 -2141
- package/src/execution/subprocess-agent-runner.ts +0 -317
- package/src/execution/temp-prompt.ts +0 -62
- package/src/execution/tombstone-store.ts +0 -72
- package/src/execution/turn-limiter.ts +0 -102
- package/src/execution/types.ts +0 -973
- package/src/execution/ui-channels.ts +0 -216
- package/src/execution/ui-interaction-model.ts +0 -48
- package/src/execution/ui-request-handler-factory.ts +0 -212
- package/src/execution/ui-request-observability.ts +0 -81
- package/src/execution/ui-request-queue.ts +0 -184
- package/src/execution/worktree-manager.ts +0 -688
- package/src/execution/worktree-registry.ts +0 -222
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -349
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -202
- package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -152
- package/src/orchestration/__tests__/args-validator.test.ts +0 -143
- package/src/orchestration/__tests__/config-loader.test.ts +0 -503
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -809
- package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
- package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
- package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
- package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -451
- package/src/orchestration/__tests__/jsonl-run-store-corrupt-entry.test.ts +0 -150
- package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
- package/src/orchestration/__tests__/jsonl-run-store-retention.test.ts +0 -202
- package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -600
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
- package/src/orchestration/__tests__/lifecycle.test.ts +0 -659
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +0 -95
- package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -2025
- package/src/orchestration/__tests__/script-lint.test.ts +0 -831
- package/src/orchestration/__tests__/skill-discovery.test.ts +0 -201
- package/src/orchestration/__tests__/test-mocks.ts +0 -197
- package/src/orchestration/__tests__/worker-exit-without-result.test.ts +0 -368
- package/src/orchestration/__tests__/worker-host.test.ts +0 -120
- package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -563
- package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
- package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -129
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
- package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
- package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
- package/src/orchestration/agent-opts-resolver.ts +0 -174
- package/src/orchestration/args-validator.ts +0 -127
- package/src/orchestration/config-loader.ts +0 -315
- package/src/orchestration/error-recovery.ts +0 -1018
- package/src/orchestration/execute-agent-call.ts +0 -256
- package/src/orchestration/launcher.ts +0 -455
- package/src/orchestration/lifecycle.ts +0 -399
- package/src/orchestration/models/__tests__/budget.test.ts +0 -307
- package/src/orchestration/models/__tests__/trace.test.ts +0 -408
- package/src/orchestration/models/agent-call.ts +0 -83
- package/src/orchestration/models/budget.ts +0 -118
- package/src/orchestration/models/ports.ts +0 -164
- package/src/orchestration/models/run-runtime.ts +0 -104
- package/src/orchestration/models/run-spec.ts +0 -83
- package/src/orchestration/models/run-state.ts +0 -44
- package/src/orchestration/models/trace.ts +0 -183
- package/src/orchestration/models/types.ts +0 -301
- package/src/orchestration/models/workflow-run.ts +0 -254
- package/src/orchestration/models/workflow-script-registry.ts +0 -35
- package/src/orchestration/models/workflow-script.ts +0 -117
- package/src/orchestration/script-lint.ts +0 -766
- package/src/orchestration/skill-discovery.ts +0 -115
- package/src/orchestration/worker-handle.ts +0 -115
- package/src/orchestration/worker-host.ts +0 -98
- package/src/orchestration/worker-script-builder.ts +0 -421
- package/src/orchestration/workflow-files.ts +0 -85
- package/src/orchestration/workflow-script-registry-impl.ts +0 -133
- package/src/shared/__tests__/agent-ref.test.ts +0 -34
- package/src/shared/__tests__/meta-parser.test.ts +0 -304
- package/src/shared/__tests__/model-ref.test.ts +0 -306
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
- package/src/shared/__tests__/resource-discovery.test.ts +0 -736
- package/src/shared/__tests__/resource-meta.test.ts +0 -51
- package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
- package/src/shared/__tests__/timer-delay.test.ts +0 -61
- package/src/shared/agent-event.ts +0 -13
- package/src/shared/agent-ref.ts +0 -57
- package/src/shared/meta-parser.ts +0 -261
- package/src/shared/model-ref.ts +0 -286
- package/src/shared/resource-discovery.ts +0 -811
- package/src/shared/resource-meta.ts +0 -65
- package/src/shared/schema-env.ts +0 -44
- package/src/shared/schema-jsonify.ts +0 -58
- package/src/shared/timer-delay.ts +0 -54
- package/src/shared/xml-injection.ts +0 -35
- package/workflows/README.md +0 -81
- package/workflows/_shared/agent-refs.cjs +0 -40
- package/workflows/chain.js +0 -131
- package/workflows/map-reduce.js +0 -174
- package/workflows/parallel.js +0 -148
- package/workflows/review-fix-loop-utils.cjs +0 -1309
- package/workflows/review-fix-loop.js +0 -1404
- package/workflows/scatter-gather.js +0 -178
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
// src/execution/engine/common/event-journal.ts
|
|
2
|
-
//
|
|
3
|
-
// 宿主 event journal(P2 公共降级层)。设计权威源:
|
|
4
|
-
// docs/architecture/subagent-engine-abstraction.md D6(第②级归属宿主——host 统一落盘
|
|
5
|
-
// 则全引擎免费获得、格式唯一)+ §3.3.6 event journal 格式(JSONL 中立 v1)。
|
|
6
|
-
//
|
|
7
|
-
// 为什么 host 落盘而非各 adapter 缓存:adapter 各自缓存会演变出六种格式;host 消费
|
|
8
|
-
// onEvent 统一落盘,read 第②级(journal 重放)与探针 golden 语料共用同一份产物。
|
|
9
|
-
//
|
|
10
|
-
// 写入纪律(§3.3.6):onEvent 回调内追加写(有界缓冲 + 批量 flush),run 终态后
|
|
11
|
-
// flush 并 fsync 一次。journal 不随池删(D5),生命周期跟随 record。
|
|
12
|
-
//
|
|
13
|
-
// 失败语义:journal 是降级链第②级(尽力而为的数据源)——写失败不炸主流程:
|
|
14
|
-
// warn 一次 + 置 failed,后续 append 丢弃、close 立即返回(不静默:warn 有留痕)。
|
|
15
|
-
|
|
16
|
-
import { appendFile, mkdir, open } from "node:fs/promises";
|
|
17
|
-
import { readFileSync } from "node:fs";
|
|
18
|
-
import { dirname } from "node:path";
|
|
19
|
-
|
|
20
|
-
import { getLogger } from "@zhushanwen/pi-extension-logger";
|
|
21
|
-
|
|
22
|
-
import type { AgentEvent } from "../../types.ts";
|
|
23
|
-
|
|
24
|
-
const logger = getLogger("subagents");
|
|
25
|
-
|
|
26
|
-
// ============================================================
|
|
27
|
-
// 中立行格式(§3.3.6 v1)
|
|
28
|
-
// ============================================================
|
|
29
|
-
|
|
30
|
-
/** journal 单行(JSONL)。seq 是 host 侧单调递增序号——重放顺序权威。 */
|
|
31
|
-
export interface JournalLine {
|
|
32
|
-
v: 1;
|
|
33
|
-
/** host 落盘时刻(Date.now(),ms)。 */
|
|
34
|
-
ts: number;
|
|
35
|
-
/** = RunContext.taskId = record.id(journal 文件名与池引用计数 key)。 */
|
|
36
|
-
taskId: string;
|
|
37
|
-
engineId: string;
|
|
38
|
-
seq: number;
|
|
39
|
-
/** AgentEvent 原样(onEvent 回调对象的 JSON.stringify 直接产物,无二次变换)。 */
|
|
40
|
-
event: AgentEvent;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ============================================================
|
|
44
|
-
// JournalWriter
|
|
45
|
-
// ============================================================
|
|
46
|
-
|
|
47
|
-
/** flush 触发的缓冲阈值:行数与字节数任一到达即批量落盘(有界缓冲)。 */
|
|
48
|
-
const FLUSH_THRESHOLD_LINES = 64;
|
|
49
|
-
// eslint-disable-next-line no-magic-numbers -- 32KB = 32 * 1024 bytes 缓冲字节换算常数
|
|
50
|
-
const FLUSH_THRESHOLD_BYTES = 32 * 1024;
|
|
51
|
-
|
|
52
|
-
/** FileHandle 的结构子集(close 期 fsync 用;注入 fake 可测)。 */
|
|
53
|
-
export interface FileHandleLike {
|
|
54
|
-
sync(): Promise<void>;
|
|
55
|
-
close(): Promise<void>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** writer 的文件系统依赖面(结构接口:测试注入 fake,免 vi.mock 整个 fs 模块)。 */
|
|
59
|
-
export interface JournalFsDeps {
|
|
60
|
-
mkdir(path: string, opts: { recursive: boolean }): Promise<unknown>;
|
|
61
|
-
appendFile(path: string, data: string): Promise<void>;
|
|
62
|
-
open(path: string, flags: string): Promise<FileHandleLike>;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const defaultFs: JournalFsDeps = {
|
|
66
|
-
mkdir: (p, o) => mkdir(p, o),
|
|
67
|
-
appendFile: (p, d) => appendFile(p, d, "utf8"),
|
|
68
|
-
open: (p, f) => open(p, f),
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/** JournalWriter 构造参数。 */
|
|
72
|
-
export interface JournalWriterOpts {
|
|
73
|
-
/** journal 文件绝对路径(经 paths.ts resolveJournalPath 派生,禁自拼)。 */
|
|
74
|
-
path: string;
|
|
75
|
-
taskId: string;
|
|
76
|
-
engineId: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* 追加写 writer:append 同步入队(onEvent 是同步回调,不阻塞事件流),批量异步
|
|
81
|
-
* flush;close = flush 全部 + fsync 一次(§3.3.6 写入纪律)。
|
|
82
|
-
*
|
|
83
|
-
* 文件惰性创建:首个 flush 才 mkdir + appendFile——无事件的任务不产生空 journal。
|
|
84
|
-
*/
|
|
85
|
-
export class JournalWriter {
|
|
86
|
-
private readonly opts: JournalWriterOpts;
|
|
87
|
-
private readonly fs: JournalFsDeps;
|
|
88
|
-
private readonly warn: (msg: string) => void;
|
|
89
|
-
|
|
90
|
-
private buffer: string[] = [];
|
|
91
|
-
private bufferedBytes = 0;
|
|
92
|
-
private seq = 0;
|
|
93
|
-
/** 串行化异步写(promise 链),防交错 append。 */
|
|
94
|
-
private chain: Promise<void> = Promise.resolve();
|
|
95
|
-
private failed = false;
|
|
96
|
-
private closed = false;
|
|
97
|
-
/** 是否成功落过盘——close 的 fsync 只对已存在的文件做(无事件任务不产生空文件)。 */
|
|
98
|
-
private wrote = false;
|
|
99
|
-
|
|
100
|
-
constructor(opts: JournalWriterOpts, fs: JournalFsDeps = defaultFs, warn: (msg: string) => void = defaultWarn) {
|
|
101
|
-
this.opts = opts;
|
|
102
|
-
this.fs = fs;
|
|
103
|
-
this.warn = warn;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** 追加一个事件(同步入队;缓冲达阈值自动排队 flush)。closed/failed 后丢弃。 */
|
|
107
|
-
append(event: AgentEvent): void {
|
|
108
|
-
if (this.failed || this.closed) return;
|
|
109
|
-
const line: JournalLine = {
|
|
110
|
-
v: 1,
|
|
111
|
-
ts: Date.now(),
|
|
112
|
-
taskId: this.opts.taskId,
|
|
113
|
-
engineId: this.opts.engineId,
|
|
114
|
-
seq: this.seq++,
|
|
115
|
-
event,
|
|
116
|
-
};
|
|
117
|
-
const serialized = JSON.stringify(line);
|
|
118
|
-
this.buffer.push(serialized);
|
|
119
|
-
this.bufferedBytes += serialized.length;
|
|
120
|
-
if (this.buffer.length >= FLUSH_THRESHOLD_LINES || this.bufferedBytes >= FLUSH_THRESHOLD_BYTES) {
|
|
121
|
-
void this.flush();
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** 把缓冲写入磁盘(串行排队;等待此前所有排队写完成)。 */
|
|
126
|
-
flush(): Promise<void> {
|
|
127
|
-
if (this.failed || this.buffer.length === 0) return this.chain;
|
|
128
|
-
const chunk = `${this.buffer.join("\n")}\n`;
|
|
129
|
-
this.buffer = [];
|
|
130
|
-
this.bufferedBytes = 0;
|
|
131
|
-
this.chain = this.chain.then(() => this.writeChunk(chunk));
|
|
132
|
-
return this.chain;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** run 终态后调用:flush 全部 + fsync 一次 + 关闭(幂等)。 */
|
|
136
|
-
async close(): Promise<void> {
|
|
137
|
-
if (this.closed) return;
|
|
138
|
-
this.closed = true;
|
|
139
|
-
await this.flush();
|
|
140
|
-
if (this.failed || !this.wrote) return;
|
|
141
|
-
// fsync 一次(§3.3.6):进程崩溃后已 flush 的行不丢——record 读第②级的一致性依据
|
|
142
|
-
this.chain = this.chain.then(async () => {
|
|
143
|
-
const fh = await this.fs.open(this.opts.path, "r");
|
|
144
|
-
try {
|
|
145
|
-
await fh.sync();
|
|
146
|
-
} finally {
|
|
147
|
-
await fh.close();
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
await this.chain;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** 是否已写失败(降级诊断用;failed 后 journal 不可作为②级数据源)。 */
|
|
154
|
-
get isFailed(): boolean {
|
|
155
|
-
return this.failed;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 重定向落盘路径(对齐点③:journal 路径权威 = 引擎声明的池 key)。
|
|
160
|
-
* 仅在尚未落盘(wrote=false)时允许——已 flush 过的文件搬家会制造两份半截 journal,
|
|
161
|
-
* 重放语义破坏;已落盘时 warn 拒绝(不静默)。事件在重定向前到达的场景由调用方
|
|
162
|
-
* 契约保证(RunContext.onPoolResolved 注释:引擎须在首个事件 emit 前回调)。
|
|
163
|
-
*/
|
|
164
|
-
retarget(path: string): void {
|
|
165
|
-
if (this.closed || this.failed) return;
|
|
166
|
-
if (this.wrote) {
|
|
167
|
-
this.warn(
|
|
168
|
-
`[event-journal] retarget ignored for task ${this.opts.taskId}: journal already flushed to ` +
|
|
169
|
-
`${this.opts.path} (events must not precede onPoolResolved)`,
|
|
170
|
-
);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
this.opts.path = path;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** 当前落盘路径(handle.journalPath 回填数据源)。 */
|
|
177
|
-
get path(): string {
|
|
178
|
-
return this.opts.path;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private async writeChunk(chunk: string): Promise<void> {
|
|
182
|
-
try {
|
|
183
|
-
await this.fs.mkdir(dirname(this.opts.path), { recursive: true });
|
|
184
|
-
await this.fs.appendFile(this.opts.path, chunk);
|
|
185
|
-
this.wrote = true;
|
|
186
|
-
} catch (err) {
|
|
187
|
-
// 尽力而为语义:一次写失败即永久放弃(重试追写会产生乱序行),warn 留痕不静默
|
|
188
|
-
this.failed = true;
|
|
189
|
-
this.buffer = [];
|
|
190
|
-
this.warn(
|
|
191
|
-
`[event-journal] write failed, journal for task ${this.opts.taskId} is unavailable ` +
|
|
192
|
-
`(read falls back to lower tiers): ${err instanceof Error ? err.message : String(err)}`,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function defaultWarn(msg: string): void {
|
|
199
|
-
logger.warn(msg);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ============================================================
|
|
203
|
-
// 重放
|
|
204
|
-
// ============================================================
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 重放 journal:读取路径下全部事件,重放即得 AgentEvent 流(read 第②级)。
|
|
208
|
-
*
|
|
209
|
-
* - 文件不存在 → [](降级链语义:②级不可达不算错误,调用方落 ③级);
|
|
210
|
-
* - 损坏行跳过(追加写产物末行可能截断;跳过优于整体失败——设计 C5「三级都不 throw」);
|
|
211
|
-
* - 按 seq 稳定排序后返回(重放顺序权威是 seq,不依赖文件行序的隐式保证,§3.3.6)。
|
|
212
|
-
*/
|
|
213
|
-
export function replayJournal(path: string): AgentEvent[] {
|
|
214
|
-
let raw: string;
|
|
215
|
-
try {
|
|
216
|
-
raw = readFileSync(path, "utf8");
|
|
217
|
-
} catch {
|
|
218
|
-
return [];
|
|
219
|
-
}
|
|
220
|
-
const lines: JournalLine[] = [];
|
|
221
|
-
for (const row of raw.split("\n")) {
|
|
222
|
-
const trimmed = row.trim();
|
|
223
|
-
if (trimmed === "") continue;
|
|
224
|
-
const parsed = parseLine(trimmed);
|
|
225
|
-
if (parsed !== undefined) lines.push(parsed);
|
|
226
|
-
}
|
|
227
|
-
lines.sort((a, b) => a.seq - b.seq);
|
|
228
|
-
return lines.map((l) => l.event);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/** 单行 parse + 结构 guard(v=1 + event 形状最小判别:object 且 type 为 string)。 */
|
|
232
|
-
function parseLine(trimmed: string): JournalLine | undefined {
|
|
233
|
-
let parsed: unknown;
|
|
234
|
-
try {
|
|
235
|
-
parsed = JSON.parse(trimmed);
|
|
236
|
-
} catch {
|
|
237
|
-
return undefined;
|
|
238
|
-
}
|
|
239
|
-
if (typeof parsed !== "object" || parsed === null) return undefined;
|
|
240
|
-
const rec = parsed as Record<string, unknown>;
|
|
241
|
-
const event = rec.event;
|
|
242
|
-
if (rec.v !== 1 || typeof rec.ts !== "number" || typeof rec.seq !== "number") return undefined;
|
|
243
|
-
if (typeof event !== "object" || event === null || typeof (event as Record<string, unknown>).type !== "string") {
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
v: 1,
|
|
248
|
-
ts: rec.ts,
|
|
249
|
-
taskId: typeof rec.taskId === "string" ? rec.taskId : "",
|
|
250
|
-
engineId: typeof rec.engineId === "string" ? rec.engineId : "",
|
|
251
|
-
seq: rec.seq,
|
|
252
|
-
event: event as AgentEvent,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// src/execution/engine/common/journal-replay.ts
|
|
2
|
-
//
|
|
3
|
-
// read 第②级(宿主 event journal 重放)的公共实现(P4,对齐点①接线)。设计权威源:
|
|
4
|
-
// docs/architecture/subagent-engine-abstraction.md D6 + §3.3.6「重放等价性」——
|
|
5
|
-
// journal 重放与 live 通路共用同一 reducer(updateFromEvent 范式),不引入第二套
|
|
6
|
-
// 解析器;conformance C5 断言重放 turns 与 live 一致。
|
|
7
|
-
//
|
|
8
|
-
// 为什么放 common:zcode/pi 的 read() ②级降级是同一段逻辑(replayJournal 拿事件流 →
|
|
9
|
-
// live reducer 累积 turns → 投影 SessionView)——放引擎各自实现会漂移出两份形状。
|
|
10
|
-
|
|
11
|
-
import { createRecord, updateFromEvent } from "../../execution-record.ts";
|
|
12
|
-
import type { AgentEvent } from "../../types.ts";
|
|
13
|
-
import type { EngineHandle, SessionView } from "../types.ts";
|
|
14
|
-
import { replayJournal } from "./event-journal.ts";
|
|
15
|
-
import { aggregateUsage, toReplayedTurn } from "./session-view-projection.ts";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* journal → SessionView(read 第②级)。
|
|
19
|
-
* 返回 undefined = ②级不可达(journal 路径缺省 / 文件不存在 / 无事件),调用方落 ③级。
|
|
20
|
-
* coarse 引擎(zcode)journal 只含合成事件,重放退化为摘要级——D6 已声明的保真度
|
|
21
|
-
* 下限,非缺陷。
|
|
22
|
-
*/
|
|
23
|
-
export function replayJournalToSessionView(
|
|
24
|
-
handle: EngineHandle,
|
|
25
|
-
engineId: string,
|
|
26
|
-
): SessionView | undefined {
|
|
27
|
-
const journalPath = handle.data.journalPath;
|
|
28
|
-
if (journalPath === undefined) return undefined;
|
|
29
|
-
const events = replayJournal(journalPath);
|
|
30
|
-
if (events.length === 0) return undefined;
|
|
31
|
-
return eventsToSessionView(events, engineId, sessionIdFromHandle(handle));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** 事件流 → SessionView(live reducer 累积 turns——重放等价性的实现体)。 */
|
|
35
|
-
export function eventsToSessionView(
|
|
36
|
-
events: readonly AgentEvent[],
|
|
37
|
-
engineId: string,
|
|
38
|
-
sessionId?: string,
|
|
39
|
-
): SessionView {
|
|
40
|
-
const record = createRecord("journal-replay", {
|
|
41
|
-
agent: "journal-replay",
|
|
42
|
-
model: "",
|
|
43
|
-
mode: "background",
|
|
44
|
-
task: "",
|
|
45
|
-
slug: "",
|
|
46
|
-
startedAt: 0,
|
|
47
|
-
});
|
|
48
|
-
for (const ev of events) updateFromEvent(record, ev);
|
|
49
|
-
return {
|
|
50
|
-
engineId,
|
|
51
|
-
...(sessionId !== undefined ? { sessionId } : {}),
|
|
52
|
-
turns: record.turns.map(toReplayedTurn),
|
|
53
|
-
usage: aggregateUsage(record.turns),
|
|
54
|
-
source: "journal",
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** handle.sessionRef 的 sessionId 提取(引擎自定义键,运行时 guard)。 */
|
|
59
|
-
function sessionIdFromHandle(handle: EngineHandle): string | undefined {
|
|
60
|
-
const v = handle.data.sessionRef["sessionId"];
|
|
61
|
-
return typeof v === "string" ? v : undefined;
|
|
62
|
-
}
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
// src/execution/engine/common/kill-chain.ts
|
|
2
|
-
//
|
|
3
|
-
// 超时杀链与 abort 两级中断(P2 公共降级层)。设计权威源:
|
|
4
|
-
// docs/architecture/subagent-engine-abstraction.md D1(abort 分级:引擎原生中断 →
|
|
5
|
-
// 公共杀链兜底;CLI-only 引擎直接杀链,杀死后宿主合成终态)+ §3.3.3 engine_timeout /
|
|
6
|
-
// engine_run_failed 行 + 附录 A「CLI 超时」行(6/6 引擎全缺 → 宿主公共层全补)。
|
|
7
|
-
//
|
|
8
|
-
// 为什么放公共层:没有任何引擎有 CLI 级超时(§2.2 表),杀链写一次全引擎复用(D4)。
|
|
9
|
-
|
|
10
|
-
import { getLogger } from "@zhushanwen/pi-extension-logger";
|
|
11
|
-
|
|
12
|
-
import { engineTimeoutDetail } from "./errors.ts";
|
|
13
|
-
import type { AgentOutcome, AgentTaskSpec } from "../types.ts";
|
|
14
|
-
import { DEFAULT_ENGINE_ID } from "../registry.ts";
|
|
15
|
-
|
|
16
|
-
const logger = getLogger("subagents");
|
|
17
|
-
|
|
18
|
-
// ============================================================
|
|
19
|
-
// 杀链(SIGTERM → grace → SIGKILL)
|
|
20
|
-
// ============================================================
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 可杀子进程的结构形状(Node ChildProcess 的结构子集)。
|
|
24
|
-
* 用结构接口而非 ChildProcess 类型:测试可注入 fake(ChildProcess 全字段构造过重),
|
|
25
|
-
* 且未来 driver host 的常驻进程句柄只要满足此形状即可复用杀链。
|
|
26
|
-
*/
|
|
27
|
-
export interface KillableChild {
|
|
28
|
-
/** 非 null = 进程已退出(自然退出码)。 */
|
|
29
|
-
readonly exitCode: number | null;
|
|
30
|
-
/** 非 null = 进程被信号杀死。exitCode/signalCode 任一非 null 即已退出。 */
|
|
31
|
-
readonly signalCode: string | null;
|
|
32
|
-
/** 发信号。返回 false = 进程已不存在(kill no-op)。 */
|
|
33
|
-
kill(signal?: NodeJS.Signals | number): boolean;
|
|
34
|
-
once(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): unknown;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** SIGTERM 优雅窗口默认值(ms)。实测校准点:设计 §5 待验证检查点⑤(zcode 对 SIGTERM 的响应时序)。 */
|
|
38
|
-
export const DEFAULT_KILL_GRACE_MS = 5_000;
|
|
39
|
-
|
|
40
|
-
/** SIGKILL 发出后的收尸等待上限(ms)——SIGKILL 后进程必死(D 状态罕见),有界等待防挂死。 */
|
|
41
|
-
const SIGKILL_REAP_TIMEOUT_MS = 10_000;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 宿主超时 abort 的 signal.reason 标记(对齐点④)。mergeTimeoutSignal(SAR 侧超时
|
|
45
|
-
* 合并链)产出;引擎 abort 合成终态时判别「超时 vs 用户 cancel」——超时统一走
|
|
46
|
-
* synthesizeTimeoutOutcome(engine_timeout),cancel 维持中止标记(engine_run_failed)。
|
|
47
|
-
*/
|
|
48
|
-
export const HOST_TIMEOUT_ABORT_REASON = "agent-call-timeout";
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 杀链:SIGTERM → 等待 graceMs → 仍存活则 SIGKILL。
|
|
52
|
-
*
|
|
53
|
-
* @returns 'terminated' = SIGTERM 优雅退出(或进程已自行退出);'killed' = 走了 SIGKILL。
|
|
54
|
-
*/
|
|
55
|
-
export async function killChain(
|
|
56
|
-
child: KillableChild,
|
|
57
|
-
opts: { graceMs: number },
|
|
58
|
-
): Promise<"terminated" | "killed"> {
|
|
59
|
-
// 已退出(自然/已被杀)→ 无需发信号,按优雅终止口径返回
|
|
60
|
-
if (child.exitCode !== null || child.signalCode !== null) return "terminated";
|
|
61
|
-
|
|
62
|
-
const exited = waitForExit(child);
|
|
63
|
-
safeKill(child, "SIGTERM");
|
|
64
|
-
|
|
65
|
-
const graceful = await raceTimeout(exited, opts.graceMs);
|
|
66
|
-
if (graceful === "settled") return "terminated";
|
|
67
|
-
// grace 超时后进程可能恰好在检查前一刻退出——复核退出态,避免误杀已死进程
|
|
68
|
-
if (child.exitCode !== null || child.signalCode !== null) return "terminated";
|
|
69
|
-
|
|
70
|
-
safeKill(child, "SIGKILL");
|
|
71
|
-
// 有界收尸:无论等到与否都返回 'killed'(信号已发出,返回值表达「走了 SIGKILL」)
|
|
72
|
-
await raceTimeout(exited, SIGKILL_REAP_TIMEOUT_MS);
|
|
73
|
-
return "killed";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* 发信号兜底包裹:进程恰在退出态检查与 kill 之间自退时,ChildProcess.kill 可能抛
|
|
78
|
-
* (zsub 实测经验)——幂等吞掉并 debug 留痕,不阻断杀链语义(对已退进程信号本就是
|
|
79
|
-
* no-op)。收口自 zcode launcher 的内联实现(对齐点②:单一权威)。
|
|
80
|
-
*/
|
|
81
|
-
function safeKill(child: KillableChild, signal: NodeJS.Signals): void {
|
|
82
|
-
try {
|
|
83
|
-
child.kill(signal);
|
|
84
|
-
} catch (err) {
|
|
85
|
-
logger.debug(
|
|
86
|
-
`[kill-chain] ${signal} on exited process: ${err instanceof Error ? err.message : String(err)}`,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ============================================================
|
|
92
|
-
// 超时终态合成
|
|
93
|
-
// ============================================================
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* 合成 engine_timeout 终态(宿主超时杀链走完后的 AgentOutcome)。
|
|
97
|
-
* 含 stdout 尾部 2000 字 + 「可用 engine: pi 重跑」建议(§3.3.3 第 6 行);
|
|
98
|
-
* exitCode: null = 被信号杀死(AgentOutcome 的杀链判据,§3.3.5)。
|
|
99
|
-
*/
|
|
100
|
-
export function synthesizeTimeoutOutcome(
|
|
101
|
-
task: AgentTaskSpec,
|
|
102
|
-
stdoutTail: string,
|
|
103
|
-
engineId: string = DEFAULT_ENGINE_ID,
|
|
104
|
-
): AgentOutcome {
|
|
105
|
-
return {
|
|
106
|
-
content: "",
|
|
107
|
-
// slug 进错误信息:单看 outcome(record 之外)也能定位是哪个任务超时
|
|
108
|
-
error: `engine_timeout: [slug=${task.slug}] ${engineTimeoutDetail(stdoutTail)}`,
|
|
109
|
-
// 被信号杀死:退出码语义为 null(§3.3.5 AgentOutcome.exitCode 注释)
|
|
110
|
-
exitCode: null,
|
|
111
|
-
engineId,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ============================================================
|
|
116
|
-
// abort 两级编排
|
|
117
|
-
// ============================================================
|
|
118
|
-
|
|
119
|
-
/** 原生中断后的宽限窗口默认值(ms):中断指令送达 → 引擎自行收尾的等待上限。 */
|
|
120
|
-
export const DEFAULT_NATIVE_INTERRUPT_GRACE_MS = 3_000;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* abort 两级编排 helper(D1 abort 分级的公共实现):
|
|
124
|
-
* signal abort 时 ①先调引擎原生中断(tryNativeInterrupt,若有)并给宽限窗口,
|
|
125
|
-
* 窗口内未停(或无原生中断——CLI-only 引擎)则 ②走杀链兜底。
|
|
126
|
-
*
|
|
127
|
-
* 进程自然退出时以 'terminated' settle(挂在 exit 事件上,杀链对已退进程是 no-op),
|
|
128
|
-
* 调用方可安全 await;signal 永不 abort 且进程已退时也会 settle,不悬挂。
|
|
129
|
-
*/
|
|
130
|
-
export function abortWithFallback(
|
|
131
|
-
child: KillableChild,
|
|
132
|
-
signal: AbortSignal,
|
|
133
|
-
tryNativeInterrupt?: () => Promise<void>,
|
|
134
|
-
opts?: { graceMs?: number; nativeGraceMs?: number },
|
|
135
|
-
): Promise<"terminated" | "killed"> {
|
|
136
|
-
const graceMs = opts?.graceMs ?? DEFAULT_KILL_GRACE_MS;
|
|
137
|
-
const nativeGraceMs = opts?.nativeGraceMs ?? DEFAULT_NATIVE_INTERRUPT_GRACE_MS;
|
|
138
|
-
|
|
139
|
-
return new Promise<"terminated" | "killed">((resolve) => {
|
|
140
|
-
let settled = false;
|
|
141
|
-
// 三个 handler 互相引用(finish 移除 onAbort / onAbort 触发 runChain / runChain
|
|
142
|
-
// 兜底 finish),用函数声明 + 提升消解声明环,避免 use-before-define。
|
|
143
|
-
function finish(result: "terminated" | "killed"): void {
|
|
144
|
-
if (settled) return;
|
|
145
|
-
settled = true;
|
|
146
|
-
signal.removeEventListener("abort", onAbort);
|
|
147
|
-
resolve(result);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function onExit(): void {
|
|
151
|
-
finish("terminated");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function runChain(): void {
|
|
155
|
-
void (async () => {
|
|
156
|
-
if (tryNativeInterrupt) {
|
|
157
|
-
try {
|
|
158
|
-
await tryNativeInterrupt();
|
|
159
|
-
} catch (err) {
|
|
160
|
-
// 原生中断失败(协议错/管道断)→ 直接落杀链,中断失败不阻断兜底。
|
|
161
|
-
// debug 级留诊断线索:这不是错误终态,只是该引擎优雅中断不可用
|
|
162
|
-
logger.debug(
|
|
163
|
-
`[kill-chain] native interrupt failed, falling back to kill chain: ${
|
|
164
|
-
err instanceof Error ? err.message : String(err)
|
|
165
|
-
}`,
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
const stopped = await raceTimeout(waitForExit(child), nativeGraceMs);
|
|
169
|
-
if (stopped === "settled" || settled) return; // 原生中断生效,进程已停
|
|
170
|
-
}
|
|
171
|
-
finish(await killChain(child, { graceMs }));
|
|
172
|
-
})();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function onAbort(): void {
|
|
176
|
-
runChain();
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// 进程自然退出(含原生中断生效)→ 优雅终止口径。
|
|
180
|
-
// once listener 不显式移除:触发时 finish 已幂等,单 child 单 listener 无堆积。
|
|
181
|
-
child.once("exit", onExit);
|
|
182
|
-
if (child.exitCode !== null || child.signalCode !== null) {
|
|
183
|
-
finish("terminated");
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (signal.aborted) runChain();
|
|
188
|
-
else signal.addEventListener("abort", onAbort, { once: true });
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ── 内部工具 ────────────────────────────────────────────────────
|
|
193
|
-
|
|
194
|
-
/** 等 child 退出(已退出立即 settle;否则挂一次性 exit listener)。 */
|
|
195
|
-
function waitForExit(child: KillableChild): Promise<void> {
|
|
196
|
-
return new Promise<void>((resolve) => {
|
|
197
|
-
if (child.exitCode !== null || child.signalCode !== null) {
|
|
198
|
-
resolve();
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
child.once("exit", () => resolve());
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/** promise vs 超时:超时先到返回 'timeout'(promise 继续但被放弃等待)。 */
|
|
206
|
-
function raceTimeout(p: Promise<void>, ms: number): Promise<"settled" | "timeout"> {
|
|
207
|
-
return new Promise<"settled" | "timeout">((resolve) => {
|
|
208
|
-
const timer = setTimeout(() => resolve("timeout"), ms);
|
|
209
|
-
p.then(
|
|
210
|
-
() => {
|
|
211
|
-
clearTimeout(timer);
|
|
212
|
-
resolve("settled");
|
|
213
|
-
},
|
|
214
|
-
() => {
|
|
215
|
-
// 被 await 的 exit promise 不会 reject;防御分支保持语义完整(超时口径胜出前出错按 settled 处理)
|
|
216
|
-
clearTimeout(timer);
|
|
217
|
-
resolve("settled");
|
|
218
|
-
},
|
|
219
|
-
);
|
|
220
|
-
});
|
|
221
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// src/execution/engine/common/nesting-guard.ts
|
|
2
|
-
//
|
|
3
|
-
// 嵌套防护(P2 公共降级层)。设计权威源:
|
|
4
|
-
// docs/architecture/subagent-engine-abstraction.md D8(嵌套防护双层):
|
|
5
|
-
// 统一 XYZ_AGENT_SUBAGENT=1 标记(所有引擎 spawn 都注入,引擎 adapter 检测到即拒绝
|
|
6
|
-
// 递归派发)+ 剥离各引擎原生标记(CC 的 CLAUDECODE / zsub 的 ZSW_NESTED / pi 的
|
|
7
|
-
// PI_SUBAGENT_*)防继承泄漏——子代理环境的旧标记会让孙代理误判自己已在嵌套层。
|
|
8
|
-
//
|
|
9
|
-
// 为什么 env 标记是唯一跨引擎可靠手段(被否方案见设计):「隔离目录里不装扩展」
|
|
10
|
-
// 依赖配置洁癖,且 opencode/CC 会吃项目级配置;env 由宿主显式控制,随 spawn 必达。
|
|
11
|
-
|
|
12
|
-
import { nestedSpawnRejectedError } from "./errors.ts";
|
|
13
|
-
|
|
14
|
-
/** 统一嵌套标记 env 名(D8)。值恒 '1'。 */
|
|
15
|
-
export const NESTED_SPAWN_ENV = "XYZ_AGENT_SUBAGENT";
|
|
16
|
-
|
|
17
|
-
/** 需剥离的引擎原生嵌套标记(精确名)。 */
|
|
18
|
-
const NATIVE_NESTED_KEYS: readonly string[] = ["CLAUDECODE", "ZSW_NESTED"];
|
|
19
|
-
|
|
20
|
-
/** 需剥离的引擎原生嵌套标记前缀(pi 家族)。 */
|
|
21
|
-
const NATIVE_NESTED_PREFIXES: readonly string[] = ["PI_SUBAGENT_"];
|
|
22
|
-
|
|
23
|
-
/** env 对象形状(NodeJS.ProcessEnv 的结构子集,测试可传普通对象)。 */
|
|
24
|
-
export type SpawnEnv = Record<string, string | undefined>;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* 构造子代理 spawn env:注入 XYZ_AGENT_SUBAGENT=1 + 剥离引擎原生嵌套标记。
|
|
28
|
-
* 返回新对象,不改入参(spawn env 组装链中的多层 spread 安全)。
|
|
29
|
-
*/
|
|
30
|
-
export function buildNestedSpawnEnv(baseEnv: SpawnEnv): SpawnEnv {
|
|
31
|
-
const env: SpawnEnv = {};
|
|
32
|
-
for (const [key, value] of Object.entries(baseEnv)) {
|
|
33
|
-
if (NATIVE_NESTED_KEYS.includes(key)) continue;
|
|
34
|
-
if (NATIVE_NESTED_PREFIXES.some((prefix) => key.startsWith(prefix))) continue;
|
|
35
|
-
env[key] = value;
|
|
36
|
-
}
|
|
37
|
-
env[NESTED_SPAWN_ENV] = "1";
|
|
38
|
-
return env;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 嵌套 spawn 防护断言:检测到统一标记(本进程已是 subagent)抛
|
|
43
|
-
* EngineError(nested_spawn_rejected)——文案说明防护规则、指向 task 内自行完成。
|
|
44
|
-
* 调用点:subagent 工具入口(进程创建前拒绝,D11 处置三级)。
|
|
45
|
-
*/
|
|
46
|
-
export function assertNotNestedSpawn(env: SpawnEnv): void {
|
|
47
|
-
if (env[NESTED_SPAWN_ENV] === "1") {
|
|
48
|
-
throw nestedSpawnRejectedError();
|
|
49
|
-
}
|
|
50
|
-
}
|