@zhushanwen/pi-subagent-workflow 8.5.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 +13 -16
- 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 +142 -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 +157 -0
- package/src/interface/__tests__/subagent-tool-prompt.test.ts +12 -0
- 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 +34 -13
- package/src/interface/commands.ts +3 -3
- package/src/interface/format.ts +2 -2
- package/src/interface/helpers.ts +3 -3
- 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 +288 -19
- package/src/interface/subagent-tool-schema.ts +163 -0
- package/src/interface/subagent-tool.ts +65 -129
- package/src/interface/subagents.ts +5 -5
- 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} +132 -18
- 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 -256
- package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
- package/src/execution/__tests__/chat-engine-routing.test.ts +0 -597
- 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 -385
- 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 -189
- package/src/execution/__tests__/execution-record.test.ts +0 -1284
- 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 -138
- 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 -238
- 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 -593
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/lifecycle-manager.test.ts +0 -337
- 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 -109
- package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
- package/src/execution/__tests__/model-resolver.test.ts +0 -413
- 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 -574
- package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
- package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
- package/src/execution/__tests__/output-collector.test.ts +0 -61
- 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 -644
- 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__/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 -311
- package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
- package/src/execution/__tests__/spawn-args.test.ts +0 -434
- 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__/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 -506
- package/src/execution/__tests__/temp-prompt.test.ts +0 -53
- package/src/execution/__tests__/timeout-integration.test.ts +0 -397
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/__tests__/tool-action.test.ts +0 -385
- 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 -193
- package/src/execution/agent-result-mapper.ts +0 -90
- package/src/execution/alive-store.ts +0 -92
- package/src/execution/argv-mirror.ts +0 -109
- 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 -567
- 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 -648
- 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 -304
- package/src/execution/execute-options-mapper.ts +0 -111
- package/src/execution/execution-record.ts +0 -906
- 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 -491
- package/src/execution/lifecycle-predicates.ts +0 -65
- package/src/execution/manifest-store.ts +0 -256
- package/src/execution/model-config-service.ts +0 -204
- package/src/execution/model-resolver.ts +0 -276
- package/src/execution/notifier.ts +0 -260
- package/src/execution/output-collector.ts +0 -88
- 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 -1548
- 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 -2119
- package/src/execution/subprocess-agent-runner.ts +0 -303
- 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 -910
- 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 -174
- 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 -336
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -208
- package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -157
- package/src/orchestration/__tests__/args-validator.test.ts +0 -143
- package/src/orchestration/__tests__/concurrency-gate.test.ts +0 -125
- package/src/orchestration/__tests__/config-loader.test.ts +0 -503
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -724
- 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 -314
- package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
- package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -277
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
- package/src/orchestration/__tests__/lifecycle.test.ts +0 -625
- package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -1971
- package/src/orchestration/__tests__/script-lint.test.ts +0 -831
- package/src/orchestration/__tests__/skill-discovery.test.ts +0 -157
- package/src/orchestration/__tests__/test-mocks.ts +0 -197
- 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 -520
- package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
- package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -110
- 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 -93
- package/src/orchestration/args-validator.ts +0 -127
- package/src/orchestration/concurrency-gate.ts +0 -69
- package/src/orchestration/config-loader.ts +0 -315
- package/src/orchestration/error-recovery.ts +0 -862
- package/src/orchestration/execute-agent-call.ts +0 -217
- package/src/orchestration/launcher.ts +0 -410
- package/src/orchestration/lifecycle.ts +0 -396
- package/src/orchestration/models/__tests__/budget.test.ts +0 -367
- 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 -148
- package/src/orchestration/models/ports.ts +0 -164
- package/src/orchestration/models/run-runtime.ts +0 -89
- 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 -292
- 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 -92
- package/src/orchestration/worker-handle.ts +0 -115
- package/src/orchestration/worker-host.ts +0 -98
- package/src/orchestration/worker-script-builder.ts +0 -408
- 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__/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/agent-event.ts +0 -13
- package/src/shared/agent-ref.ts +0 -57
- package/src/shared/meta-parser.ts +0 -261
- package/src/shared/resource-discovery.ts +0 -811
- package/src/shared/resource-meta.ts +0 -65
- package/src/shared/schema-jsonify.ts +0 -56
- 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 -1307
- package/workflows/review-fix-loop.js +0 -1396
- package/workflows/scatter-gather.js +0 -178
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
// src/runtime/worktree-registry.ts
|
|
2
|
-
//
|
|
3
|
-
// 全局 worktree 注册表:跨 repo 记录所有活 pi-sub-* worktree。
|
|
4
|
-
//
|
|
5
|
-
// 取代旧的 per-cwd 扫描 + .session mapping sidecar 链。
|
|
6
|
-
// 旧 reaper 的两个根本缺陷由此消除:
|
|
7
|
-
// 1. 触发覆盖:旧 scan 扫「当前 cwd 对应的 repo」,workspace 根 / 非 git 目录启动时
|
|
8
|
-
// rev-parse 报错整个挂掉;且 tmpdir 下的 checkout 永远不会被 pi cwd "看到"。
|
|
9
|
-
// → 新 scan 遍历全局注册表,不依赖 cwd 是否 git repo。
|
|
10
|
-
// 2. 判据脆弱:旧 scan 用 .finalized/.cancelled 终态 marker 作主判据,进程崩溃时
|
|
11
|
-
// 无人写终态 → 孤儿永久泄漏。→ 新判据:pid 死活一条判到底。
|
|
12
|
-
//
|
|
13
|
-
// 并发模型:
|
|
14
|
-
// - 跨进程互斥(D5a):add/updatePid/remove 的 load→mutate→save 全程持
|
|
15
|
-
// proper-lockfile 异步锁(<worktrees.json>.lock,协议登记 data-source-registry.md §6)。
|
|
16
|
-
// 锁不可用(重试耗尽)时降级为无锁 RMW + warn——注册表是 best-effort 数据,
|
|
17
|
-
// 降级不比锁前更差,条目丢失由 reaper 对账兜底(worktree-manager scan 的
|
|
18
|
-
// 双向 diff,见 reconcileWithPhysical)。
|
|
19
|
-
// - 同步 IO(readFileSync/writeFileSync)持锁执行:锁内临界区毫秒级,
|
|
20
|
-
// async 锁 + sync IO 组合在单线程 event loop 内无 interleaving。
|
|
21
|
-
// - 原子写:写 .tmp → rename,防写一半崩溃产生损坏 JSON。
|
|
22
|
-
|
|
23
|
-
import * as fs from "node:fs";
|
|
24
|
-
import * as path from "node:path";
|
|
25
|
-
|
|
26
|
-
import { withFileLock } from "@zhushanwen/pi-file-lock";
|
|
27
|
-
import { getLogger } from "@zhushanwen/pi-extension-logger";
|
|
28
|
-
|
|
29
|
-
import { bestEffort } from "./best-effort.ts";
|
|
30
|
-
|
|
31
|
-
const logger = getLogger("subagents");
|
|
32
|
-
|
|
33
|
-
/** create→spawn 宽限期(ms):pid=0 条目超过此阈值判 create 后崩溃。 */
|
|
34
|
-
export const SPAWN_GRACE_MS = 60_000;
|
|
35
|
-
|
|
36
|
-
/** JSON 缩进空格数(可读性 + diff 友好)。 */
|
|
37
|
-
const JSON_INDENT = 2;
|
|
38
|
-
|
|
39
|
-
/** tmp 随机段:36 进制取 8 字符(跳过 "0." 前缀),与 pid 组合保证并发唯一。 */
|
|
40
|
-
const TMP_RANDOM_BASE = 36;
|
|
41
|
-
const TMP_RANDOM_SLICE_START = 2; // 跳过 Math.random 字符串的 "0." 前缀
|
|
42
|
-
const TMP_RANDOM_SLICE_END = 10;
|
|
43
|
-
|
|
44
|
-
/** 注册表 JSON 顶层结构的运行时类型守卫。 */
|
|
45
|
-
function isRegistryData(value: unknown): value is { entries: WorktreeEntry[] } {
|
|
46
|
-
return (
|
|
47
|
-
typeof value === "object" &&
|
|
48
|
-
value !== null &&
|
|
49
|
-
"entries" in value &&
|
|
50
|
-
Array.isArray(value.entries)
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 注册表条目:一条 = 一个活 worktree。
|
|
56
|
-
* 字段全部来自 WorktreeHandle + session-runner 已捕获的 child.pid,零新数据源。
|
|
57
|
-
*/
|
|
58
|
-
export interface WorktreeEntry {
|
|
59
|
-
/** 主仓库根目录(git -C <repo> 操作目标)。 */
|
|
60
|
-
readonly repo: string;
|
|
61
|
-
/** 分支名("pi-sub-<recordId>")。 */
|
|
62
|
-
readonly branch: string;
|
|
63
|
-
/** checkout 目录(tmpdir 下,= WorktreeHandle.path)。 */
|
|
64
|
-
readonly checkout: string;
|
|
65
|
-
/** 子进程 pid(0 = create-spawn 窗口,尚未拿到 pid)。 */
|
|
66
|
-
readonly pid: number;
|
|
67
|
-
/** 创建时间戳(ms,SPAWN_GRACE 判据 + 调试用)。 */
|
|
68
|
-
readonly createdAt: number;
|
|
69
|
-
/**
|
|
70
|
-
* 对应 subagent session jsonl 文件全路径(诊断/兼容字段,reaper 据 pid 死活判孤儿)。
|
|
71
|
-
* session-runner first header 拿到 pid 时补全(create 时 record.sessionFile 尚未确定)。
|
|
72
|
-
* 可选:旧 worktrees.json / 非 worktree 模式无此字段,向后兼容(undefined 时 reaper 走原 pid 判据)。
|
|
73
|
-
*/
|
|
74
|
-
readonly sessionFile?: string;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 全局 worktree 注册表。
|
|
79
|
-
*
|
|
80
|
-
* 文件位置:<agentDir>/subagents/worktrees.json(repo 无关层级,跨 repo 共享)
|
|
81
|
-
* 格式:{ "entries": WorktreeEntry[] }
|
|
82
|
-
*/
|
|
83
|
-
export class WorktreeRegistry {
|
|
84
|
-
private readonly filePath: string;
|
|
85
|
-
|
|
86
|
-
constructor(agentDir: string) {
|
|
87
|
-
this.filePath = path.join(agentDir, "subagents", "worktrees.json");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* 新增条目(create 成功后调,pid=0 占位)。
|
|
92
|
-
* 同 branch 已存在则覆盖(防残留覆盖)。
|
|
93
|
-
* 跨进程锁内 RMW(D5a);锁降级路径见 mutate。
|
|
94
|
-
*/
|
|
95
|
-
async add(entry: WorktreeEntry): Promise<void> {
|
|
96
|
-
await this.mutate((entries) => {
|
|
97
|
-
const idx = entries.findIndex((e) => e.branch === entry.branch);
|
|
98
|
-
if (idx >= 0) {
|
|
99
|
-
entries[idx] = entry;
|
|
100
|
-
} else {
|
|
101
|
-
entries.push(entry);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 更新 pid(runSpawn spawn() 返回后同步调)。
|
|
108
|
-
* branch 不存在则忽略(create 后崩溃 + reaper 已清的竞态)。
|
|
109
|
-
* sessionFile 可选补全:传入时填入 entry(reaper 据 pid 死活判孤儿,不读本字段)。
|
|
110
|
-
*/
|
|
111
|
-
async updatePid(branch: string, pid: number, sessionFile?: string): Promise<void> {
|
|
112
|
-
await this.mutate(
|
|
113
|
-
(entries) => {
|
|
114
|
-
const idx = entries.findIndex((e) => e.branch === branch);
|
|
115
|
-
if (idx >= 0) {
|
|
116
|
-
entries[idx] = {
|
|
117
|
-
...entries[idx],
|
|
118
|
-
pid,
|
|
119
|
-
...(sessionFile !== undefined ? { sessionFile } : {}),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
{ branch, pid },
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 移除条目(cleanup/reaper 清理后调)。
|
|
129
|
-
* branch 不存在则忽略(幂等)。
|
|
130
|
-
*/
|
|
131
|
-
async remove(branch: string): Promise<void> {
|
|
132
|
-
await this.mutate(
|
|
133
|
-
(entries) => {
|
|
134
|
-
const filtered = entries.filter((e) => e.branch !== branch);
|
|
135
|
-
if (filtered.length !== entries.length) {
|
|
136
|
-
entries.length = 0;
|
|
137
|
-
entries.push(...filtered);
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
{ branch },
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* 锁内 RMW 统一入口:withLock(load → mutate → save)。
|
|
146
|
-
*
|
|
147
|
-
* 降级语义(对齐本类既有 best-effort 约定——注册表写失败不阻断 create/cleanup
|
|
148
|
-
* 主流程):锁获取失败(重试耗尽 ELOCKED 等)→ warn + 无锁执行同一段 RMW
|
|
149
|
-
* (= D5a 之前的 last-write-wins 行为,条目丢失由 reaper 对账兜底),
|
|
150
|
-
* 不抛错、永不 reject(调用方含 session-runner 的 fire-and-forget 回调)。
|
|
151
|
-
*/
|
|
152
|
-
private async mutate(
|
|
153
|
-
mutate: (entries: WorktreeEntry[]) => void,
|
|
154
|
-
context?: { branch?: string; pid?: number },
|
|
155
|
-
): Promise<void> {
|
|
156
|
-
const run = (): void => {
|
|
157
|
-
const entries = this.load();
|
|
158
|
-
mutate(entries);
|
|
159
|
-
this.save(entries, context);
|
|
160
|
-
};
|
|
161
|
-
try {
|
|
162
|
-
await withFileLock(this.filePath, () => {
|
|
163
|
-
run();
|
|
164
|
-
return Promise.resolve();
|
|
165
|
-
});
|
|
166
|
-
} catch (lockErr) {
|
|
167
|
-
// 锁不可用(ELOCKED 重试耗尽 / 锁目录损坏等):降级无锁 RMW。
|
|
168
|
-
// 竞争窗口内可能丢条目(旧缺陷形态),由 reaper 对账(scan 双向 diff)收敛。
|
|
169
|
-
logger.warn("[worktree] registry lock unavailable, degraded to lock-free RMW", {
|
|
170
|
-
...(context ?? {}),
|
|
171
|
-
err: lockErr instanceof Error ? lockErr.message : String(lockErr),
|
|
172
|
-
});
|
|
173
|
-
try {
|
|
174
|
-
run();
|
|
175
|
-
} catch (err) {
|
|
176
|
-
bestEffort(err, "worktree registry degraded RMW");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* 加载全部条目(reaper 遍历用)。
|
|
183
|
-
* 文件不存在 / 解析失败 / IO 错误 → 返回空数组(视为无活 worktree)。
|
|
184
|
-
*/
|
|
185
|
-
load(): WorktreeEntry[] {
|
|
186
|
-
try {
|
|
187
|
-
const raw = fs.readFileSync(this.filePath, "utf-8");
|
|
188
|
-
const parsed = JSON.parse(raw) as unknown;
|
|
189
|
-
if (isRegistryData(parsed)) {
|
|
190
|
-
return parsed.entries;
|
|
191
|
-
}
|
|
192
|
-
return [];
|
|
193
|
-
} catch {
|
|
194
|
-
// 文件不存在(首次运行)/ 解析失败(损坏)/ IO 错误 → 空注册表
|
|
195
|
-
return [];
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* 原子写入全部条目。
|
|
201
|
-
* best-effort:写入失败不阻断主流程(create/cleanup 的 git 操作已执行,
|
|
202
|
-
* 注册表与 git 状态的短暂不一致靠下次 reaper 对账收敛)。
|
|
203
|
-
* 写盘失败时 warn 日志(带 branch/pid 上下文,补全失败可观测闭环)。
|
|
204
|
-
*/
|
|
205
|
-
private save(entries: WorktreeEntry[], context?: { branch?: string; pid?: number }): void {
|
|
206
|
-
try {
|
|
207
|
-
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
208
|
-
// tmp 名带 pid + 随机段:锁降级(无锁并发 RMW)时多进程 tmp 互不覆盖
|
|
209
|
-
const tmp = `${this.filePath}.tmp_${process.pid}_${Math.random().toString(TMP_RANDOM_BASE).slice(TMP_RANDOM_SLICE_START, TMP_RANDOM_SLICE_END)}`;
|
|
210
|
-
fs.writeFileSync(tmp, JSON.stringify({ entries }, null, JSON_INDENT), "utf-8");
|
|
211
|
-
fs.renameSync(tmp, this.filePath);
|
|
212
|
-
} catch (err) {
|
|
213
|
-
bestEffort(err, "worktree registry save");
|
|
214
|
-
// [worktree-reaper-fix] 补全写盘失败静默吞错时,条目 pid 恒 0、60s 后被 reaper 误删
|
|
215
|
-
// 活 worktree 且无诊断线索。此 warn 与 reaper scan 的 pid=0 warn 呼应,形成闭环。
|
|
216
|
-
logger.warn(
|
|
217
|
-
"[worktree] registry save failed; pid may stay 0 and be reaped by orphan reaper",
|
|
218
|
-
{ ...(context ?? {}), err: err instanceof Error ? err.message : String(err) },
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Module-scope helpers: accessible to BOTH the IIFE body AND the outer
|
|
3
|
-
// .then()/.catch() handlers (which run outside the IIFE). _workerLogs/
|
|
4
|
-
// _pushWorkerLog/_safePost + the parentPort/workerData handles must all live
|
|
5
|
-
// here — a previous version declared _safePost inside the IIFE, so the
|
|
6
|
-
// .then()/.catch() return/error handlers threw ReferenceError: _safePost is
|
|
7
|
-
// not defined, crashing the Worker on EVERY script return (exit code 1) and
|
|
8
|
-
// losing all diagnostics. require() is cached, so destructuring once at module
|
|
9
|
-
// load and reusing everywhere avoids the redundant require calls that used to
|
|
10
|
-
// appear in the IIFE and the outer .then/.catch.
|
|
11
|
-
const { parentPort: _parentPort, workerData: _workerData } = require("node:worker_threads");
|
|
12
|
-
const _workerLogs = [];
|
|
13
|
-
// IF6(#12): known agent() fields — hoisted to module scope, built once per worker
|
|
14
|
-
// (was rebuilt inside agent() on every call; field set is call-invariant).
|
|
15
|
-
const _KNOWN_FIELDS = new Set(["prompt", "description", "schema", "model", "scene", "label", "task", "agent", "phase", "skill", "timeoutMs", "cwd", "fork", "worktree", "returnMeta", "thinkingLevel", "engine"]);
|
|
16
|
-
function _pushWorkerLog(level, args) {
|
|
17
|
-
try { _workerLogs.push({ level, message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }); } catch (e) { /* swallow */ }
|
|
18
|
-
}
|
|
19
|
-
// ── safePostMessage wrapper: 统一 postMessage 防御(DataCloneError 等)──
|
|
20
|
-
// Module-scope so the outer .then/.catch return/error handlers can use it.
|
|
21
|
-
// context 取值约定(诊断标识):固定为消息类型字面量——
|
|
22
|
-
// "agent-call" / "workflow-call" / "return" / "error",调用方据此在日志里
|
|
23
|
-
// 一眼定位是哪类 postMessage 失败。新增调用点必须传对应 context。
|
|
24
|
-
function _safePost(msg, context) {
|
|
25
|
-
try { _parentPort.postMessage(msg); return true; }
|
|
26
|
-
catch (e) {
|
|
27
|
-
const errMsg = e && e.message ? e.message : String(e);
|
|
28
|
-
const stack = e && e.stack ? e.stack : "";
|
|
29
|
-
_pushWorkerLog("error", ["[postMessage failed:" + context + "]", errMsg, stack]);
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
(async () => {
|
|
34
|
-
const parentPort = _parentPort;
|
|
35
|
-
const workerData = _workerData;
|
|
36
|
-
|
|
37
|
-
if (!parentPort) {
|
|
38
|
-
throw new Error("Workflow worker: parentPort is null — not running in a Worker thread");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ── Intercept console.* to avoid leaking worker diagnostics into the input area ──
|
|
42
|
-
console.log = function (...args) { _pushWorkerLog("log", args); };
|
|
43
|
-
console.warn = function (...args) { _pushWorkerLog("warn", args); };
|
|
44
|
-
console.error = function (...args) { _pushWorkerLog("error", args); };
|
|
45
|
-
console.info = function (...args) { _pushWorkerLog("info", args); };
|
|
46
|
-
|
|
47
|
-
// ── Internal state ──
|
|
48
|
-
let _callIdCounter = 0;
|
|
49
|
-
let _agentCallCount = 0;
|
|
50
|
-
const _pendingCalls = new Map();
|
|
51
|
-
const _callCache = workerData.callCache instanceof Map
|
|
52
|
-
? workerData.callCache
|
|
53
|
-
: new Map(Object.entries(workerData.callCache || {}).map(([k, v]) => [Number(k), v]));
|
|
54
|
-
|
|
55
|
-
// ── Injected globals ──
|
|
56
|
-
const $ARGS = (workerData.args && typeof workerData.args === "object") ? workerData.args : {};
|
|
57
|
-
const args = $ARGS;
|
|
58
|
-
const $WORKSPACE = typeof workerData.workspace === "string" ? workerData.workspace : "";
|
|
59
|
-
const _budgetData = {
|
|
60
|
-
total: (workerData.budget && workerData.budget.maxTokens) || 0,
|
|
61
|
-
_spentTokens: (workerData.budget && workerData.budget.usedTokens) ?? 0,
|
|
62
|
-
_spentCost: (workerData.budget && workerData.budget.usedCost) ?? 0,
|
|
63
|
-
};
|
|
64
|
-
const $BUDGET = {
|
|
65
|
-
get total() { return _budgetData.total; },
|
|
66
|
-
spent() { return _budgetData._spentTokens; },
|
|
67
|
-
remaining() { return Math.max(0, _budgetData.total - _budgetData._spentTokens); },
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// ── Run-level model/thinkingLevel override (Option B symmetric single-path) ──
|
|
71
|
-
// Injected from workerData (set by workerHost from RunSpec.model/thinkingLevel).
|
|
72
|
-
// agent() reads these as run-level defaults; per-call explicit opts still win.
|
|
73
|
-
const $MODEL = (workerData.model && typeof workerData.model === "string") ? workerData.model : undefined;
|
|
74
|
-
const $THINKING_LEVEL = (workerData.thinkingLevel && typeof workerData.thinkingLevel === "string") ? workerData.thinkingLevel : undefined;
|
|
75
|
-
|
|
76
|
-
// ── WorkflowAbortedError ──
|
|
77
|
-
class WorkflowAbortedError extends Error {
|
|
78
|
-
constructor(reason) {
|
|
79
|
-
super("Workflow aborted: " + (reason || "No reason"));
|
|
80
|
-
this.name = "WorkflowAbortedError";
|
|
81
|
-
this.reason = reason || "";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ── Message handler (main thread → worker) ──
|
|
86
|
-
parentPort.on("message", (msg) => {
|
|
87
|
-
if (msg.type === "agent-result") {
|
|
88
|
-
const pending = _pendingCalls.get(msg.callId);
|
|
89
|
-
if (pending) {
|
|
90
|
-
_pendingCalls.delete(msg.callId);
|
|
91
|
-
if (typeof msg.result !== "undefined") {
|
|
92
|
-
_callCache.set(msg.callId, msg.result);
|
|
93
|
-
}
|
|
94
|
-
// 失败不传播到 agent 外部:resolve 而非 reject。
|
|
95
|
-
// 旧实现在 result.error 时 reject,单 agent 失败会冒到 worker 顶层 .catch()
|
|
96
|
-
// → 发 type:"error" → handleScriptError → rebuildRuntime → SIGKILL 同伴进程,
|
|
97
|
-
// 把单点失败放大成整批崩溃。改为始终 resolve(错误时回退到 content 文本),
|
|
98
|
-
// 让 parallel() 下的脚本容错循环(parseResult → null → skip)自然接管。
|
|
99
|
-
// 错误原因已由主线程 executeAgentCall → trace.update(result.error) 保留在 trace/TUI,
|
|
100
|
-
// 不丢失。失败 resolve 为空字符串是既定容错策略。
|
|
101
|
-
// parsedOutput: validated data object from structured-output execute().
|
|
102
|
-
// Fallback to content (raw text) when no schema was requested or on error.
|
|
103
|
-
// W2 改动 9(b):returnMeta===true 时 resolve {value,sessionFile,worktreePath,error,usage,durationMs,sessionId}
|
|
104
|
-
// (对称缓存重放 9c);否则单值。usage/durationMs/sessionId 为 rfl 仪表透传(tier-1 §7.1,
|
|
105
|
-
// AgentResult 已含、此处原样转发,缺省 undefined 兼容旧主线程)。
|
|
106
|
-
const _value = msg.result.parsedOutput ?? msg.result.content;
|
|
107
|
-
if (pending.returnMeta) {
|
|
108
|
-
pending.resolve({
|
|
109
|
-
value: _value,
|
|
110
|
-
sessionFile: msg.result.sessionFile,
|
|
111
|
-
worktreePath: msg.result.worktreePath,
|
|
112
|
-
error: msg.result.error,
|
|
113
|
-
usage: msg.result.usage,
|
|
114
|
-
durationMs: msg.result.durationMs,
|
|
115
|
-
sessionId: msg.result.sessionId,
|
|
116
|
-
});
|
|
117
|
-
} else {
|
|
118
|
-
pending.resolve(_value);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
} else if (msg.type === "workflow-result") {
|
|
122
|
-
const pending = _pendingCalls.get(msg.callId);
|
|
123
|
-
if (pending) {
|
|
124
|
-
_pendingCalls.delete(msg.callId);
|
|
125
|
-
pending.resolve(msg.result);
|
|
126
|
-
}
|
|
127
|
-
} else if (msg.type === "abort") {
|
|
128
|
-
const err = new WorkflowAbortedError(msg.reason);
|
|
129
|
-
_pendingCalls.forEach((p) => { p.reject(err); });
|
|
130
|
-
_pendingCalls.clear();
|
|
131
|
-
} else if (msg.type === "budget-update" && msg.budget) {
|
|
132
|
-
_budgetData._spentTokens = msg.budget.usedTokens ?? _budgetData._spentTokens;
|
|
133
|
-
_budgetData._spentCost = msg.budget.usedCost ?? _budgetData._spentCost;
|
|
134
|
-
}
|
|
135
|
-
// "budget-warning" is informational; no required handling
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
let _currentPhase = "";
|
|
139
|
-
function phase(name) { _currentPhase = String(name); }
|
|
140
|
-
|
|
141
|
-
function log(msg) {
|
|
142
|
-
try { parentPort.postMessage({ type: "log", phase: _currentPhase, message: String(msg) }); } catch(e) { /* swallow */ }
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
async function agent(firstArg, secondArg) {
|
|
146
|
-
let opts;
|
|
147
|
-
if (typeof firstArg === "string") {
|
|
148
|
-
opts = {
|
|
149
|
-
prompt: firstArg,
|
|
150
|
-
description: (secondArg && typeof secondArg === "object" && secondArg.label) || undefined,
|
|
151
|
-
schema: (secondArg && typeof secondArg === "object" && secondArg.schema) || undefined,
|
|
152
|
-
model: (secondArg && typeof secondArg === "object" && secondArg.model) || $MODEL,
|
|
153
|
-
scene: (secondArg && typeof secondArg === "object" && secondArg.scene) || undefined,
|
|
154
|
-
phase: (secondArg && typeof secondArg === "object" && secondArg.phase) || undefined,
|
|
155
|
-
thinkingLevel: (secondArg && typeof secondArg === "object" && secondArg.thinkingLevel) || $THINKING_LEVEL,
|
|
156
|
-
// P4 D9③:step 级 engine 显式指定(仅限必须某引擎独有能力的场景)
|
|
157
|
-
engine: (secondArg && typeof secondArg === "object" && secondArg.engine) || undefined,
|
|
158
|
-
};
|
|
159
|
-
} else if (typeof firstArg === "object" && firstArg !== null) {
|
|
160
|
-
if (firstArg.prompt) {
|
|
161
|
-
opts = firstArg;
|
|
162
|
-
} else if (firstArg.task || firstArg.agent) {
|
|
163
|
-
// fork/worktree/returnMeta forwarded here (parity with agent({prompt,...}) and direct-pass
|
|
164
|
-
// branches). Previously this shortcut dropped them (w1 only noted, not wired).
|
|
165
|
-
opts = {
|
|
166
|
-
prompt: firstArg.task || firstArg.prompt || "",
|
|
167
|
-
description: firstArg.label || firstArg.description,
|
|
168
|
-
agent: firstArg.agent,
|
|
169
|
-
schema: firstArg.schema,
|
|
170
|
-
model: firstArg.model || $MODEL,
|
|
171
|
-
scene: firstArg.scene,
|
|
172
|
-
skill: firstArg.skill,
|
|
173
|
-
timeoutMs: firstArg.timeoutMs,
|
|
174
|
-
cwd: firstArg.cwd,
|
|
175
|
-
fork: firstArg.fork,
|
|
176
|
-
worktree: firstArg.worktree,
|
|
177
|
-
returnMeta: firstArg.returnMeta,
|
|
178
|
-
thinkingLevel: firstArg.thinkingLevel || $THINKING_LEVEL,
|
|
179
|
-
engine: firstArg.engine,
|
|
180
|
-
};
|
|
181
|
-
} else {
|
|
182
|
-
opts = firstArg;
|
|
183
|
-
}
|
|
184
|
-
} else {
|
|
185
|
-
throw new Error("agent() requires a prompt string or options object as first argument");
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Run-level fallback for object branches (opts = firstArg direct assign).
|
|
189
|
-
// Priority: agent() explicit > $MODEL global (set from workerData.model).
|
|
190
|
-
if (!opts.model && $MODEL) opts.model = $MODEL;
|
|
191
|
-
if (!opts.thinkingLevel && $THINKING_LEVEL) opts.thinkingLevel = $THINKING_LEVEL;
|
|
192
|
-
|
|
193
|
-
// Validate known agent() fields to catch API misuse early (_KNOWN_FIELDS at module scope)
|
|
194
|
-
const _unknownFields = Object.keys(opts).filter((k) => !_KNOWN_FIELDS.has(k));
|
|
195
|
-
if (_unknownFields.length > 0) {
|
|
196
|
-
_pushWorkerLog("warn", ["[workflow] agent() received unknown fields: " + _unknownFields.join(", ") + ". Known fields: prompt, description, schema, model, scene, label, task, agent, phase, skill, timeoutMs, cwd, fork, worktree, returnMeta, thinkingLevel, engine"]);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const callId = _callIdCounter;
|
|
200
|
-
_callIdCounter++;
|
|
201
|
-
_agentCallCount++;
|
|
202
|
-
if (_callCache.has(callId)) {
|
|
203
|
-
const cached = _callCache.get(callId);
|
|
204
|
-
// 与 live handler 对齐:失败也 resolve(回退 content),不 throw。
|
|
205
|
-
// 见 agent-result 消息处理的注释:拒绝传播失败到 agent 外部。
|
|
206
|
-
// W2 改动 9(c):returnMeta 分支(对称 handler 9b),opts 仍在闭包作用域。
|
|
207
|
-
if (!cached) return undefined;
|
|
208
|
-
const _cachedValue = cached.parsedOutput ?? cached.content;
|
|
209
|
-
if (opts.returnMeta === true) {
|
|
210
|
-
return {
|
|
211
|
-
value: _cachedValue,
|
|
212
|
-
sessionFile: cached.sessionFile,
|
|
213
|
-
worktreePath: cached.worktreePath,
|
|
214
|
-
error: cached.error,
|
|
215
|
-
usage: cached.usage,
|
|
216
|
-
durationMs: cached.durationMs,
|
|
217
|
-
sessionId: cached.sessionId,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return _cachedValue;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const _effectivePhase = opts.phase || _currentPhase;
|
|
224
|
-
delete opts.phase;
|
|
225
|
-
|
|
226
|
-
if (!_safePost({ type: "agent-call", callId, opts, phase: _effectivePhase }, "agent-call")) {
|
|
227
|
-
return Promise.reject(new Error("postMessage failed for agent-call (callId=" + callId + "): see workerLogs"));
|
|
228
|
-
}
|
|
229
|
-
return new Promise((resolve, reject) => {
|
|
230
|
-
_pendingCalls.set(callId, { resolve, reject, returnMeta: opts.returnMeta === true });
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async function parallel(calls) {
|
|
235
|
-
if (typeof calls === "function") { return calls(); }
|
|
236
|
-
const settled = await Promise.allSettled(calls.map((c) => {
|
|
237
|
-
if (c && typeof c.then === "function") { return c; }
|
|
238
|
-
if (typeof c === "function") { return c(); }
|
|
239
|
-
if (typeof c === "object" && c !== null && (c.task || c.agent)) { return agent(c); }
|
|
240
|
-
return agent(c);
|
|
241
|
-
}));
|
|
242
|
-
return settled.map((r) => {
|
|
243
|
-
if (r.status === "fulfilled") {
|
|
244
|
-
const v = r.value;
|
|
245
|
-
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
246
|
-
// 主线程 fallback(postAgentResult/postResult serialization failed)回发的对象含 error 字段
|
|
247
|
-
// → 归一化为 failed 形状,与脚本侧 r.status === "failed" 检查统一
|
|
248
|
-
if (typeof v.error === "string" && v.error.length > 0) return { status: "failed", error: v.error };
|
|
249
|
-
return v;
|
|
250
|
-
}
|
|
251
|
-
return { status: "failed", error: "agent returned non-object result (type=" + typeof v + ")" };
|
|
252
|
-
}
|
|
253
|
-
const reason = r.reason;
|
|
254
|
-
const errMsg = reason instanceof Error ? reason.message : String(reason);
|
|
255
|
-
return { status: "failed", error: errMsg };
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
async function pipeline(firstArg, ...restStages) {
|
|
260
|
-
// Single-arg mode: pipeline([stage1, stage2, ...])
|
|
261
|
-
if (Array.isArray(firstArg) && restStages.length === 0) {
|
|
262
|
-
let result;
|
|
263
|
-
for (let i = 0; i < firstArg.length; i++) {
|
|
264
|
-
try { result = await firstArg[i](result); }
|
|
265
|
-
catch (e) {
|
|
266
|
-
const msg = e && e.message ? e.message : String(e);
|
|
267
|
-
_pushWorkerLog("error", ["[pipeline stage " + i + " failed]", msg]);
|
|
268
|
-
throw e;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return result;
|
|
272
|
-
}
|
|
273
|
-
// Cartesian product mode: pipeline([items], stage1, stage2, ...)
|
|
274
|
-
if (Array.isArray(firstArg) && restStages.length > 0 && typeof restStages[0] === "function") {
|
|
275
|
-
const results = [];
|
|
276
|
-
for (let idx = 0; idx < firstArg.length; idx++) {
|
|
277
|
-
const item = firstArg[idx];
|
|
278
|
-
let val = item;
|
|
279
|
-
let failed = false;
|
|
280
|
-
for (const stage of restStages) {
|
|
281
|
-
if (failed) break;
|
|
282
|
-
try { val = await stage(val); }
|
|
283
|
-
catch (e) {
|
|
284
|
-
const msg = e && e.message ? e.message : String(e);
|
|
285
|
-
_pushWorkerLog("error", ["[pipeline cartesian stage failed for item " + (idx + 1) + "]", msg]);
|
|
286
|
-
val = null; failed = true;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
results.push(val);
|
|
290
|
-
}
|
|
291
|
-
return results;
|
|
292
|
-
}
|
|
293
|
-
throw new Error("pipeline() expects pipeline([stage1, ...]) or pipeline([items], stage1, ...)");
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// ── workflow global — nested workflow invocation ──
|
|
297
|
-
async function workflow(name, args) {
|
|
298
|
-
if (typeof name !== "string" || name.length === 0) {
|
|
299
|
-
throw new Error("workflow() requires a workflow name string as first argument");
|
|
300
|
-
}
|
|
301
|
-
const workflowArgs = (typeof args === "object" && args !== null) ? args : {};
|
|
302
|
-
const callId = _callIdCounter;
|
|
303
|
-
_callIdCounter++;
|
|
304
|
-
if (!_safePost({ type: "workflow-call", callId, name, args: workflowArgs }, "workflow-call")) {
|
|
305
|
-
return Promise.reject(new Error("postMessage failed for workflow-call (name=" + name + "): see workerLogs"));
|
|
306
|
-
}
|
|
307
|
-
return new Promise((resolve, reject) => {
|
|
308
|
-
_pendingCalls.set(callId, { resolve, reject });
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// ── User workflow script ──
|
|
313
|
-
// sample exercising template features
|
|
314
|
-
const name = $ARGS.name ?? "default";
|
|
315
|
-
phase("build");
|
|
316
|
-
const spec = await agent("build it", { label: "builder", schema: { type: "object" } });
|
|
317
|
-
const results = await parallel([
|
|
318
|
-
agent({ task: "unit tests", agent: "./tester.md" }),
|
|
319
|
-
agent({ task: "lint", agent: "./linter.md", skill: "lint-skill" }),
|
|
320
|
-
]);
|
|
321
|
-
await pipeline([(x) => x, (x) => x]);
|
|
322
|
-
await workflow("deploy", { env: "prod" });
|
|
323
|
-
log("done " + name + " " + $WORKSPACE + " " + $BUDGET.remaining());
|
|
324
|
-
module.exports = { execute: async (ctx) => ctx.agent("finalize") };
|
|
325
|
-
|
|
326
|
-
// ── Auto-invoke execute() for module.exports pattern ──
|
|
327
|
-
if (typeof module !== "undefined" && module.exports && typeof module.exports.execute === "function") {
|
|
328
|
-
return await module.exports.execute({ agent, parallel, pipeline, phase, log, workflow, $ARGS, $WORKSPACE, $BUDGET });
|
|
329
|
-
}
|
|
330
|
-
})().then((result) => {
|
|
331
|
-
const runId = (_workerData.args && typeof _workerData.args === "object" && _workerData.args._runId) || "";
|
|
332
|
-
_safePost({ type: "return", runId, result, workerLogs: _workerLogs }, "return");
|
|
333
|
-
}).catch((err) => {
|
|
334
|
-
const runId = (_workerData.args && typeof _workerData.args === "object" && _workerData.args._runId) || "";
|
|
335
|
-
_safePost({ type: "error", runId, error: err.message || String(err), workerLogs: _workerLogs }, "error");
|
|
336
|
-
});
|