@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,116 +0,0 @@
|
|
|
1
|
-
// src/core/concurrency-pool.ts
|
|
2
|
-
//
|
|
3
|
-
// 并发控制 + 优先级排队。background=1000(单一优先级,保留 priority 机制供未来扩展)。
|
|
4
|
-
|
|
5
|
-
/** 队列条目:优先级 + resolver + rejecter + 入队序号(同优先级 FIFO)。 */
|
|
6
|
-
interface QueueEntry {
|
|
7
|
-
priority: number;
|
|
8
|
-
resolve: () => void;
|
|
9
|
-
reject: (err: Error) => void;
|
|
10
|
-
seq: number;
|
|
11
|
-
/** abort listener(用于 resolve/abort 时清理)。 */
|
|
12
|
-
onAbort?: () => void;
|
|
13
|
-
/** 关联的 signal(用于 resolve 时 removeEventListener)。 */
|
|
14
|
-
signal?: AbortSignal;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** 并发池接口(可注入,便于测试 mock)。 */
|
|
18
|
-
export interface ConcurrencyPool {
|
|
19
|
-
/** 按优先级排队获取槽位(0=最高)。可选 effectiveMaxConcurrent 覆盖实例级默认配额。可选 AbortSignal 在 abort 时 reject 排队条目。 */
|
|
20
|
-
acquire(priority: number, effectiveMaxConcurrent?: number, signal?: AbortSignal): Promise<void>;
|
|
21
|
-
/** 归还槽位。必须无条件执行(finally)。 */
|
|
22
|
-
release(): void;
|
|
23
|
-
/** 当前已占用槽位数(诊断/widget 用)。 */
|
|
24
|
-
readonly active: number;
|
|
25
|
-
/** 实例级最大并发配额。调用方可据此计算分层配额(max(1, maxConcurrent - depth))。 */
|
|
26
|
-
readonly maxConcurrent: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 默认实现:maxConcurrent 槽位 + 优先级队列。
|
|
31
|
-
*
|
|
32
|
-
* acquire(priority, effectiveMaxConcurrent?):
|
|
33
|
-
* effective = effectiveMaxConcurrent ?? maxConcurrent
|
|
34
|
-
* active < effective → active++, resolve
|
|
35
|
-
* 否则 → 入队 { priority, resolve, seq }, 队列按 priority 升序 + seq FIFO
|
|
36
|
-
*
|
|
37
|
-
* release():
|
|
38
|
-
* queue 非空 → 出队最高优先级 resolve(active 不变)
|
|
39
|
-
* queue 空 → active--(防下溢)
|
|
40
|
-
*/
|
|
41
|
-
export class DefaultConcurrencyPool implements ConcurrencyPool {
|
|
42
|
-
private _active = 0;
|
|
43
|
-
private readonly queue: QueueEntry[] = [];
|
|
44
|
-
private seqCounter = 0;
|
|
45
|
-
|
|
46
|
-
/** 下限 1——maxConcurrent=0 会让 acquire 永久排队死锁(C3 修复)。 */
|
|
47
|
-
readonly maxConcurrent: number;
|
|
48
|
-
|
|
49
|
-
constructor(maxConcurrent: number) {
|
|
50
|
-
this.maxConcurrent = Math.max(1, maxConcurrent);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
acquire(priority: number, effectiveMaxConcurrent?: number, signal?: AbortSignal): Promise<void> {
|
|
54
|
-
// effectiveMaxConcurrent 覆盖实例级默认配额(分层配额:调用方传 max(1, maxConcurrent - depth))。
|
|
55
|
-
// 不修改实例级 maxConcurrent——实例配额是全局共享上限,分层配额是本次 acquire 的局部上限。
|
|
56
|
-
const effective = effectiveMaxConcurrent ?? this.maxConcurrent;
|
|
57
|
-
if (this._active < effective) {
|
|
58
|
-
this._active += 1;
|
|
59
|
-
return Promise.resolve();
|
|
60
|
-
}
|
|
61
|
-
return new Promise<void>((resolve, reject) => {
|
|
62
|
-
const entry: QueueEntry = { priority, resolve, reject, seq: this.seqCounter++ };
|
|
63
|
-
// H2: abort 时 reject 排队条目并从 queue 移除,防止永久挂起
|
|
64
|
-
if (signal) {
|
|
65
|
-
if (signal.aborted) {
|
|
66
|
-
// S1: abort reject 需带 name="AbortError",对齐包内 AbortError 错误语义约定(消费方按 err.name 判别)
|
|
67
|
-
const err = new Error("acquire aborted");
|
|
68
|
-
err.name = "AbortError";
|
|
69
|
-
reject(err);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
entry.signal = signal;
|
|
73
|
-
entry.onAbort = (): void => {
|
|
74
|
-
const idx = this.queue.indexOf(entry);
|
|
75
|
-
if (idx >= 0) {
|
|
76
|
-
this.queue.splice(idx, 1);
|
|
77
|
-
// S1: abort reject 需带 name="AbortError",与 pre-aborted 分支一致
|
|
78
|
-
const err = new Error("acquire aborted");
|
|
79
|
-
err.name = "AbortError";
|
|
80
|
-
reject(err);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
signal.addEventListener("abort", entry.onAbort, { once: true });
|
|
84
|
-
}
|
|
85
|
-
this.queue.push(entry);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
release(): void {
|
|
90
|
-
if (this.queue.length > 0) {
|
|
91
|
-
// 取优先级最高(priority 最小)的;同优先级 FIFO(seq 最小)
|
|
92
|
-
let bestIdx = 0;
|
|
93
|
-
for (let i = 1; i < this.queue.length; i++) {
|
|
94
|
-
const cur = this.queue[i];
|
|
95
|
-
const best = this.queue[bestIdx];
|
|
96
|
-
if (cur.priority < best.priority || (cur.priority === best.priority && cur.seq < best.seq)) {
|
|
97
|
-
bestIdx = i;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const next = this.queue.splice(bestIdx, 1)[0];
|
|
101
|
-
// H2: 条目已获槽位——移除 abort listener(防 listener 泄漏到长生命周期 signal)
|
|
102
|
-
if (next.onAbort && next.signal) {
|
|
103
|
-
next.signal.removeEventListener("abort", next.onAbort);
|
|
104
|
-
}
|
|
105
|
-
next.resolve();
|
|
106
|
-
// active 不变(一个离开队列立即进入活跃)
|
|
107
|
-
} else if (this._active > 0) {
|
|
108
|
-
// 防御性下界:release 调用次数多于 acquire 时不让 active 为负
|
|
109
|
-
this._active -= 1;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
get active(): number {
|
|
114
|
-
return this._active;
|
|
115
|
-
}
|
|
116
|
-
}
|
package/src/execution/config.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// src/runtime/config/config.ts
|
|
2
|
-
//
|
|
3
|
-
// 全局配置(~/.pi/agent/subagents/config.json)。
|
|
4
|
-
// 仅保留 maxConcurrent(pool 大小)。模型解析已退化为「主 agent model 优先」,
|
|
5
|
-
// 不再有 category/fallback/session 级覆盖——相关字段读取时忽略。
|
|
6
|
-
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
|
-
import * as path from "node:path";
|
|
9
|
-
|
|
10
|
-
import type { SubagentsGlobalConfig } from "./types.ts";
|
|
11
|
-
|
|
12
|
-
// ============================================================
|
|
13
|
-
// 常量
|
|
14
|
-
// ============================================================
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 开箱默认配置(单一真相源,内联在代码里)。
|
|
18
|
-
*
|
|
19
|
-
* 历史教训 [HISTORICAL]:曾用包内 config.json(与 src/ 同级)作为默认值源,
|
|
20
|
-
* 但 config.json 被 .gitignore 排除且不应随 npm 包分发用户私有配置——导致
|
|
21
|
-
* npm pack 后读不到文件,catch 兜底用空字段,pi install 后首次执行抛错。
|
|
22
|
-
* 修复:默认值内联在代码里,不依赖任何包内文件。
|
|
23
|
-
*
|
|
24
|
-
* export 供守护测试断言 package.json startupConfig 声明与此深相等(防漂移)。
|
|
25
|
-
*/
|
|
26
|
-
export const DEFAULT_CONFIG: SubagentsGlobalConfig = {
|
|
27
|
-
version: 1,
|
|
28
|
-
maxConcurrent: 6,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/** 默认 maxConcurrent(DEFAULT_CONFIG 的镜像,sanitize 用)。 */
|
|
32
|
-
const DEFAULT_MAX_CONCURRENT = 6;
|
|
33
|
-
// ============================================================
|
|
34
|
-
// 路径
|
|
35
|
-
// ============================================================
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 配置文件路径(<agentDir>/subagents/config.json)。
|
|
39
|
-
* agentDir 由 Pi 核心 getAgentDir() 决定(读 PI_CODING_AGENT_DIR,默认 ~/.pi/agent),
|
|
40
|
-
* 与 Pi 主进程的目录约定完全一致——支持宿主经环境变量整体重定向。
|
|
41
|
-
*/
|
|
42
|
-
export function getGlobalConfigPath(agentDir: string): string {
|
|
43
|
-
return path.join(agentDir, "subagents", "config.json");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ============================================================
|
|
47
|
-
// 全局配置加载
|
|
48
|
-
// ============================================================
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 加载全局配置。文件不存在 / JSON 解析失败 / 字段缺失时返回默认配置。
|
|
52
|
-
* 旧 config.json 中的 categories/fallback/yoloByDefault 等字段读取时忽略
|
|
53
|
-
* (模型解析已退化为「主 agent model 优先」)。
|
|
54
|
-
*/
|
|
55
|
-
export function loadGlobalConfig(agentDir: string): SubagentsGlobalConfig {
|
|
56
|
-
const configPath = getGlobalConfigPath(agentDir);
|
|
57
|
-
try {
|
|
58
|
-
const raw = fs.readFileSync(configPath, "utf-8");
|
|
59
|
-
const parsed = JSON.parse(raw) as Partial<SubagentsGlobalConfig>;
|
|
60
|
-
// P4 引擎路由(D9):全局默认引擎 + strict 模式。非法值静默回缺省('pi' /
|
|
61
|
-
// false)——config.json 是用户手编文件,坏值不炸启动(与 maxConcurrent 同判)
|
|
62
|
-
const defaultEngine = sanitizeDefaultEngine(parsed.defaultEngine);
|
|
63
|
-
const engineRouting = sanitizeEngineRouting(parsed.engineRouting);
|
|
64
|
-
return {
|
|
65
|
-
version: parsed.version ?? DEFAULT_CONFIG.version,
|
|
66
|
-
maxConcurrent: sanitizeMaxConcurrent(parsed.maxConcurrent),
|
|
67
|
-
...(defaultEngine !== undefined ? { defaultEngine } : {}),
|
|
68
|
-
...(engineRouting !== undefined ? { engineRouting } : {}),
|
|
69
|
-
};
|
|
70
|
-
} catch {
|
|
71
|
-
return { ...DEFAULT_CONFIG };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** maxConcurrent 校验:正整数,否则默认。 */
|
|
76
|
-
function sanitizeMaxConcurrent(value: unknown): number {
|
|
77
|
-
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
78
|
-
? value
|
|
79
|
-
: DEFAULT_MAX_CONCURRENT;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* defaultEngine 校验:非空字符串透传,其余 undefined(缺省引擎由路由层落 'pi')。
|
|
84
|
-
* 为什么不在加载期对注册表校验 hasEngine:加载早于组合根注册(agentDir 解析在
|
|
85
|
-
* session_start),注册表此刻可能为空——校验归路由层(getEngine 抛 EngineNotFoundError)。
|
|
86
|
-
*/
|
|
87
|
-
function sanitizeDefaultEngine(value: unknown): string | undefined {
|
|
88
|
-
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** engineRouting 校验:仅认 strict 布尔,其余键忽略(向前兼容追加)。 */
|
|
92
|
-
function sanitizeEngineRouting(value: unknown): { strict: boolean } | undefined {
|
|
93
|
-
if (typeof value !== "object" || value === null) return undefined;
|
|
94
|
-
const strict = (value as Record<string, unknown>).strict;
|
|
95
|
-
return typeof strict === "boolean" ? { strict } : undefined;
|
|
96
|
-
}
|
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
// src/execution/dialog-queue.ts
|
|
2
|
-
//
|
|
3
|
-
// L2 跨子进程全局 dialog 串行队列。
|
|
4
|
-
//
|
|
5
|
-
// 进程单例语义:跨所有子进程共享,串行所有 dialog 类 UI 请求
|
|
6
|
-
//(isDialogMethod(method)===true,即 select/confirm/input/editor)。
|
|
7
|
-
//
|
|
8
|
-
// 设计动机(.fix-plans/00-master-summary.md §一 冲突 3):
|
|
9
|
-
// - L1 per-child 队列(session-runner.createUiRequestQueue)只解决同一子进程内的串行,
|
|
10
|
-
// 多个并行子进程仍可同时把 dialog 请求涌向父 UI(争输入焦点)。
|
|
11
|
-
// - L2 全局队列在主 agent handler 入口前再串行一次,保证同一时刻主 agent 只呈现一个 dialog。
|
|
12
|
-
// - 排队绑定 method 交互模型(dialog 才排队),不绑定 channel——排队是 Pi 协议固有属性。
|
|
13
|
-
//
|
|
14
|
-
// SR-4(child close reject):入队项带 child 引用,child close 时把该 child 的 pending dialog
|
|
15
|
-
// 全部 resolve 为 {cancelled:true},防 Promise 永挂 + 内存泄漏。
|
|
16
|
-
//
|
|
17
|
-
// handler 抛错兜底:catch → 回 {cancelled:true} → 继续处理下一个。不能让一个失败卡死队列。
|
|
18
|
-
//
|
|
19
|
-
// 调用方约定:只对 dialog 类(isDialogMethod===true)调 enqueue;fire-and-forget 由调用方
|
|
20
|
-
// 直接调 handler(见 ui-request-handler-factory.ts),不经过本队列。enqueue 内仍防御性兼容
|
|
21
|
-
// fire-and-forget(万一调用方未判):直接调 handler 返回,不入队串行。调用方不应依赖此防御。
|
|
22
|
-
|
|
23
|
-
import { isDialogMethod } from "./ui-interaction-model.ts";
|
|
24
|
-
|
|
25
|
-
// ── 类型定义(本模块是 UiRequest/UiResponse/UiRequestHandler 的规范来源) ──
|
|
26
|
-
// session-runner.ts 和 ui-channels.ts 复用这些类型(session-runner 再导出供测试 import)。
|
|
27
|
-
|
|
28
|
-
/** Pi extension_ui_request 的方法枚举(dialog + fire-and-forget 两类)。
|
|
29
|
-
* dialog 类:select/confirm/input/editor(占输入焦点,等响应)。
|
|
30
|
-
* fire-and-forget 类:notify/setStatus/setWidget/setTitle/set_editor_text(纯展示/写入)。
|
|
31
|
-
* (string & {}) 兜底:Pi 未来新增 method 或未知 method 走字符串字面量类型。 */
|
|
32
|
-
export type UiMethod =
|
|
33
|
-
| "select"
|
|
34
|
-
| "confirm"
|
|
35
|
-
| "input"
|
|
36
|
-
| "editor"
|
|
37
|
-
| "notify"
|
|
38
|
-
| "setStatus"
|
|
39
|
-
| "setWidget"
|
|
40
|
-
| "setTitle"
|
|
41
|
-
| "set_editor_text"
|
|
42
|
-
| (string & {});
|
|
43
|
-
|
|
44
|
-
/** UI 请求(session-runner 构造后传给 handler)。
|
|
45
|
-
*
|
|
46
|
-
* method 是判别字段,决定排队策略(dialog 排队)和业务路由(channel 分发)。
|
|
47
|
-
* method 特定字段按 method 可选出现(与 ExtensionUiRequest 1:1,由 session-runner 从
|
|
48
|
-
* ExtensionUiRequest 平铺构造)。channel/channelPayload 由 parseChannel 填充。
|
|
49
|
-
*
|
|
50
|
-
* 契约来源:.fix-plans/00-master-summary.md §二 2.2。 */
|
|
51
|
-
export interface UiRequest {
|
|
52
|
-
/** Pi rpc-types.ts 的 method(select/confirm/input/editor 为 dialog 类)。 */
|
|
53
|
-
method: UiMethod;
|
|
54
|
-
/** 请求 id(从 extension_ui_request envelope 顶层提取,用于 response 关联)。 */
|
|
55
|
-
id: string;
|
|
56
|
-
// method 特定字段(按 method 可选,与 ExtensionUiRequest 1:1)
|
|
57
|
-
title?: string;
|
|
58
|
-
options?: string[];
|
|
59
|
-
message?: string;
|
|
60
|
-
placeholder?: string;
|
|
61
|
-
prefill?: string;
|
|
62
|
-
notifyType?: string;
|
|
63
|
-
statusKey?: string;
|
|
64
|
-
statusText?: string | undefined;
|
|
65
|
-
widgetKey?: string;
|
|
66
|
-
widgetLines?: string[] | undefined;
|
|
67
|
-
widgetPlacement?: "aboveEditor" | "belowEditor";
|
|
68
|
-
text?: string;
|
|
69
|
-
timeout?: number;
|
|
70
|
-
/** channel 名(从 method 对应字段的 NUL 前缀解析)。
|
|
71
|
-
* select → 从 title 解析;setWidget → 从 widgetLines[0] 解析;其他 → undefined。
|
|
72
|
-
* 已知值:"ask_user"(select)、"gui_widget"(setWidget)。handler 按 channel 分发。 */
|
|
73
|
-
channel?: string;
|
|
74
|
-
/** channel 解析后的结构化 payload(已 JSON.parse)。
|
|
75
|
-
* ask_user: {questions, allowCancel};gui_widget: {component};无 channel: undefined。 */
|
|
76
|
-
channelPayload?: unknown;
|
|
77
|
-
/** 内部元数据字段:发起该 UI 请求的子进程 pid(由 session-runner.handleUiRequest 从
|
|
78
|
-
* child.pid 填入)。L2 队列据此关联 rejectChildDialogs(child close 时批量 reject)。
|
|
79
|
-
* 下划线前缀表示内部字段,非 Pi 协议字段,不参与 stdin 回写。 */
|
|
80
|
-
_childPid?: number;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** UI 响应(handler 返回,session-runner 按 shape 回写 stdin)。
|
|
84
|
-
* - {value}: select/input/editor 的答案
|
|
85
|
-
* - {confirmed}: confirm 的答案
|
|
86
|
-
* - {cancelled}: 取消(child close / handler 抛错 / 用户取消)
|
|
87
|
-
* - {ack}: fire-and-forget(当前不透传到 TUI,留作协议完整) */
|
|
88
|
-
export type UiResponse =
|
|
89
|
-
| { value: string }
|
|
90
|
-
| { confirmed: boolean }
|
|
91
|
-
| { cancelled: true }
|
|
92
|
-
| { ack: true };
|
|
93
|
-
|
|
94
|
-
/** UI 请求 handler 签名(单函数,按 req.method 内部路由)。
|
|
95
|
-
* 实现方负责:channel 业务路由(ask_user → AskUserComponent)+ 默认转发(ctx.ui.*)。
|
|
96
|
-
* 抛错由调用方(DialogGlobalQueue / session-runner)兜底为 {cancelled:true}。 */
|
|
97
|
-
export type UiRequestHandler = (req: UiRequest) => Promise<UiResponse>;
|
|
98
|
-
|
|
99
|
-
// ── DialogGlobalQueue 实现 ──
|
|
100
|
-
|
|
101
|
-
/** 入队项的 child 引用形状(只取 pid 用于 rejectChildDialogs 匹配)。 */
|
|
102
|
-
export interface DialogChildRef {
|
|
103
|
-
pid: number;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** enqueue 的可选项。child 用于 rejectChildDialogs 关联(child close 时批量 reject)。 */
|
|
107
|
-
export interface EnqueueOptions {
|
|
108
|
-
child?: DialogChildRef;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** 队列内一项:请求 + handler + resolve + 所属 child。
|
|
112
|
-
* pending 状态持有 resolve,handler 完成 / rejectChildDialogs 时调它 settle Promise。
|
|
113
|
-
* settled 标志保证只 settle 一次(rejectChildDialogs 与 handler 完成可能竞争)。 */
|
|
114
|
-
interface QueueItem {
|
|
115
|
-
req: UiRequest;
|
|
116
|
-
handler: UiRequestHandler;
|
|
117
|
-
resolve: (resp: UiResponse) => void;
|
|
118
|
-
childPid: number | undefined;
|
|
119
|
-
/** 是否已 settle(防 handler 完成 / rejectChildDialogs 重复 resolve)。 */
|
|
120
|
-
settled: boolean;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* L2 跨子进程全局 dialog 串行队列(进程单例)。
|
|
125
|
-
*
|
|
126
|
-
* 用法(createUiRequestHandlerForMode 返回的总 handler 内):
|
|
127
|
-
* ```ts
|
|
128
|
-
* const dialogQueue = new DialogGlobalQueue();
|
|
129
|
-
* return async (req: UiRequest) => {
|
|
130
|
-
* // 调用方负责判断:dialog 入队,fire-and-forget 直接调 realHandler
|
|
131
|
-
* if (isDialogMethod(req.method)) return dialogQueue.enqueue(req, realHandler);
|
|
132
|
-
* return realHandler(req);
|
|
133
|
-
* };
|
|
134
|
-
* ```
|
|
135
|
-
*
|
|
136
|
-
* 语义保证:
|
|
137
|
-
* - FIFO 串行:前一个 handler settle 后才处理下一个
|
|
138
|
-
* - SR-4:rejectChildDialogs(child) 把该 child 的 pending 全部 resolve 为 {cancelled:true}
|
|
139
|
-
* - handler 抛错兜底:catch → {cancelled:true} → 继续下一个(队列不卡死)
|
|
140
|
-
* - 调用方约定只对 dialog 类调 enqueue;fire-and-forget 由调用方直接调 handler 不入队
|
|
141
|
-
*(enqueue 内仍防御性兼容 fire-and-forget,但不保证行为)
|
|
142
|
-
*
|
|
143
|
-
* 线程模型:纯 Promise + 微任务驱动,无锁。Node 单线程 event loop 保证队列状态一致。
|
|
144
|
-
*
|
|
145
|
-
* 单 session 假设(M-2,与 index.ts lastSessionId 同源):本队列是进程级单例(实例挂在
|
|
146
|
-
* globalThis[Symbol.for("@zhushanwen/pi-subagents.dialogQueue")],见 getOrCreateDialogQueue)。
|
|
147
|
-
* rejectAll() 清空所有 pending dialog——无 per-session 隔离。Pi 当前架构保证单进程
|
|
148
|
-
* 单 session 串行(同进程不会并发多个 session),故 session_shutdown 调 rejectAll() 只会清掉
|
|
149
|
-
* 当前 session 的 pending。若未来 Pi 支持同进程多 session 并发,session A 退出会误清 session B
|
|
150
|
-
* 的 pending dialog——届时需改为 per-session 隔离(入队项 QueueItem 带 sessionId,rejectAll
|
|
151
|
-
* 改 rejectAllForSession(sessionId),session_shutdown 只清当前 session)。
|
|
152
|
-
*/
|
|
153
|
-
export class DialogGlobalQueue {
|
|
154
|
-
/** 等待处理的队列(FIFO)。正在处理的项从 queue shift 出后由 current 持有。 */
|
|
155
|
-
private queue: QueueItem[] = [];
|
|
156
|
-
/** 正在处理的项(handler 已调、未 settle)。用于 rejectChildDialogs 取消占位中的 dialog。 */
|
|
157
|
-
private current: QueueItem | undefined;
|
|
158
|
-
private processing = false;
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* 入队一个 UI 请求,返回 Promise<UiResponse>。
|
|
162
|
-
*
|
|
163
|
-
* 调用方约定:只对 dialog 类(isDialogMethod===true)调 enqueue。fire-and-forget 由
|
|
164
|
-
* 调用方(ui-request-handler-factory.ts)在 enqueue 前判 isDialogMethod 后直接调 handler,
|
|
165
|
-
* 不经过本队列。enqueue 内仍防御性兼容 fire-and-forget(万一调用方未判):直接调 handler 返回,
|
|
166
|
-
* 不入队串行,但调用方不应依赖此防御行为。
|
|
167
|
-
*
|
|
168
|
-
* dialog 项处理(TC-E4 case 1):进队列 FIFO 串行,等前一个 settle 后才调 handler
|
|
169
|
-
*(争输入焦点,防并发弹窗)。
|
|
170
|
-
*
|
|
171
|
-
* handler 抛错兜底:catch → 回 {cancelled:true}(dialog 路径,队列不卡死)。
|
|
172
|
-
* SR-4:opts.child 用于 rejectChildDialogs 关联(dialog 项会被批量 reject,含 current)。
|
|
173
|
-
*
|
|
174
|
-
* @param req UI 请求(约定只传 dialog 类;fire-and-forget 防御性兼容)
|
|
175
|
-
* @param handler 真正执行请求的 handler(TUI/GUI 模式分流后的 realHandler)
|
|
176
|
-
* @param opts 可选 child 引用(用于 rejectChildDialogs 关联)
|
|
177
|
-
* @returns handler 的响应;dialog 抛错时回 {cancelled:true};child close 时回 {cancelled:true}
|
|
178
|
-
*/
|
|
179
|
-
enqueue(
|
|
180
|
-
req: UiRequest,
|
|
181
|
-
handler: UiRequestHandler,
|
|
182
|
-
opts?: EnqueueOptions,
|
|
183
|
-
): Promise<UiResponse> {
|
|
184
|
-
// 防御性兼容 fire-and-forget:调用方约定不应传此类 req 进 enqueue(factory 层已判
|
|
185
|
-
// isDialogMethod 直接调 handler),但万一调用方未判,这里直接调 handler 不入队,避免阻塞。
|
|
186
|
-
// 直接返回 handler 真实结果(不做兜底形变为 cancelled——fire-and-forget 无串行语义)。
|
|
187
|
-
if (!isDialogMethod(req.method)) {
|
|
188
|
-
return handler(req);
|
|
189
|
-
}
|
|
190
|
-
// dialog:入队 FIFO 串行
|
|
191
|
-
return new Promise<UiResponse>((resolve) => {
|
|
192
|
-
this.queue.push({
|
|
193
|
-
req,
|
|
194
|
-
handler,
|
|
195
|
-
resolve,
|
|
196
|
-
childPid: opts?.child?.pid,
|
|
197
|
-
settled: false,
|
|
198
|
-
});
|
|
199
|
-
void this.processNext();
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* settle 一个 item(幂等)。handler 完成 / rejectChildDialogs / rejectAll 都通过本方法,
|
|
205
|
-
* settled 标志保证只 settle 一次(防竞争)。
|
|
206
|
-
*
|
|
207
|
-
* #19 单一推进点:本方法 settle Promise + 清状态后,**唯一**调 processNext 推进队列。
|
|
208
|
-
* processNext 尾部不再调 processNext(旧代码双重推进,虽靠 processing 标志幂等,但语义混乱)。
|
|
209
|
-
* 为什么推进必须在 settleItem 而非 processNext 尾部:rejectChildDialogs 取消一个永不 settle
|
|
210
|
-
* 的 current(handler 等用户输入卡死)时,processNext 的 `await item.handler` 永不 resume,
|
|
211
|
-
* 尾部不会执行;只有 settleItem 里的 processNext 才能打破死锁,推进下一个。
|
|
212
|
-
*/
|
|
213
|
-
private settleItem(item: QueueItem, resp: UiResponse): void {
|
|
214
|
-
if (item.settled) return;
|
|
215
|
-
item.settled = true;
|
|
216
|
-
item.resolve(resp);
|
|
217
|
-
// 若是正在处理的项,清空 current/processing 并推进队列(唯一推进点)。
|
|
218
|
-
// 非当前项(队列中被 reject)只 settle Promise,不影响 current/推进。
|
|
219
|
-
if (this.current === item) {
|
|
220
|
-
this.current = undefined;
|
|
221
|
-
this.processing = false;
|
|
222
|
-
void this.processNext();
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* SR-4:把指定 child 的所有 pending dialog resolve 为 {cancelled:true}。
|
|
228
|
-
*
|
|
229
|
-
* 触发场景:子进程 close(用户取消 / crash / 超时 kill)时,其 pending dialog 的 handler
|
|
230
|
-
* 可能永不 settle(等用户输入),导致 Promise 永挂 + 内存泄漏。本方法批量清理。
|
|
231
|
-
*
|
|
232
|
-
* 处理范围(TC-E4 case 2):
|
|
233
|
-
* - 正在处理中(current)的该 child 项:settle {cancelled:true},解阻塞队列推进下一个
|
|
234
|
-
* (关键:handler 可能永不 settle,必须由这里打破死锁)
|
|
235
|
-
* - 队列中等待处理的该 child 项:settle {cancelled:true} 并移除
|
|
236
|
-
*
|
|
237
|
-
* 不影响其他 child 的 pending dialog(TC-E4 case 2 子测试 2)。
|
|
238
|
-
*/
|
|
239
|
-
rejectChildDialogs(child: DialogChildRef): void {
|
|
240
|
-
// 先处理正在处理的项(可能永不 settle,必须由这里解阻塞)
|
|
241
|
-
if (this.current && this.current.childPid === child.pid) {
|
|
242
|
-
this.settleItem(this.current, { cancelled: true });
|
|
243
|
-
}
|
|
244
|
-
// 再处理队列中等待的项
|
|
245
|
-
if (this.queue.length === 0) return;
|
|
246
|
-
const remaining: QueueItem[] = [];
|
|
247
|
-
for (const item of this.queue) {
|
|
248
|
-
if (item.childPid === child.pid) {
|
|
249
|
-
// settle 该 child 的 pending Promise 为 cancelled
|
|
250
|
-
this.settleItem(item, { cancelled: true });
|
|
251
|
-
} else {
|
|
252
|
-
remaining.push(item);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
this.queue = remaining;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* 处理队列下一项(FIFO)。
|
|
260
|
-
*
|
|
261
|
-
* processing 标志保证串行:handler 运行期间 processing=true,新的 processNext 调用直接返回;
|
|
262
|
-
* handler settle 后由 settleItem 清 processing=false 并推进下一项(#19 单一推进点)。
|
|
263
|
-
*
|
|
264
|
-
* handler 抛错兜底(TC-E4 case 3):catch → settle {cancelled:true} → 继续。
|
|
265
|
-
* 不能让一个失败卡死队列(processing 永远 true)。
|
|
266
|
-
*/
|
|
267
|
-
private async processNext(): Promise<void> {
|
|
268
|
-
if (this.processing) return;
|
|
269
|
-
if (this.queue.length === 0) return;
|
|
270
|
-
this.processing = true;
|
|
271
|
-
const item = this.queue.shift()!;
|
|
272
|
-
this.current = item;
|
|
273
|
-
try {
|
|
274
|
-
const resp = await item.handler(item.req);
|
|
275
|
-
// handler 完成:settle(若已被 rejectChildDialogs 抢先 settle 则 noop)。
|
|
276
|
-
// settleItem 内会清 current/processing 并推进队列(#19 单一推进点)。
|
|
277
|
-
this.settleItem(item, resp);
|
|
278
|
-
} catch {
|
|
279
|
-
// handler 抛错兜底:回 cancelled,不向上抛(队列不能卡死)
|
|
280
|
-
this.settleItem(item, { cancelled: true });
|
|
281
|
-
}
|
|
282
|
-
// #19:不在尾部再调 processNext——推进统一由 settleItem 负责(避免双重推进)。
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* #10:把所有 pending dialog(queue + current)全部 settle 为 {cancelled:true},
|
|
287
|
-
* 并清空 queue/current/processing 状态。session_shutdown 调用,保证不留永挂 Promise。
|
|
288
|
-
*
|
|
289
|
-
* 约定签名:rejectAll(): void(无参,返 void)。Group C 的 index.ts session_shutdown 依赖此契约。
|
|
290
|
-
*
|
|
291
|
-
* 幂等:依赖 settleItem 的 settled 标志——重复调用只会对已 settled 项 noop。
|
|
292
|
-
* 顺序敏感(#19 推进点在 settleItem):必须先清空 queue 数组再 settle current,
|
|
293
|
-
* 否则 settleItem(current) 同步触发的 processNext 会从旧 queue 抢占下一项作为新 current,
|
|
294
|
-
* 避开本方法的 cancel 语义。清空后 processNext 看到空队列直接返回,新 current 不会被抢占。
|
|
295
|
-
*
|
|
296
|
-
* 单 session 假设(M-2):见类注释。本方法清空所有 pending 不分 session——依赖 Pi 单进程
|
|
297
|
-
* 单 session 串行保证。session_shutdown handler(index.ts)调用本方法时,进程内只会有当前
|
|
298
|
-
* session 的 pending dialog。多 session 并发场景的迁移策略(rejectAllForSession)见类注释。
|
|
299
|
-
*/
|
|
300
|
-
rejectAll(): void {
|
|
301
|
-
// 先捕获并清空队列——防 settleItem(current) 触发的 processNext 抢占同队列下一项
|
|
302
|
-
const items = this.queue;
|
|
303
|
-
this.queue = [];
|
|
304
|
-
// 再 settle current(processNext 此时看到空队列,不会抢占)
|
|
305
|
-
if (this.current) {
|
|
306
|
-
this.settleItem(this.current, { cancelled: true });
|
|
307
|
-
}
|
|
308
|
-
// settle 所有原队列项(Promise 必须 resolve,不能挂)
|
|
309
|
-
for (const item of items) {
|
|
310
|
-
this.settleItem(item, { cancelled: true });
|
|
311
|
-
}
|
|
312
|
-
// 状态清理(幂等:settleItem(current) 可能已清)
|
|
313
|
-
this.current = undefined;
|
|
314
|
-
this.processing = false;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/** 清空队列状态(仅在 rejectAll 之后调用)。pending Promise 必须先由 rejectAll settle。
|
|
318
|
-
* 不 settle Promise 的纯状态重置——单独调用会导致 Promise 永挂(footgun),故设为 private。
|
|
319
|
-
* 外部调用方应使用 rejectAll()(它 settle 所有 pending + 重置状态,是原子操作)。 */
|
|
320
|
-
private resetState(): void {
|
|
321
|
-
this.queue = [];
|
|
322
|
-
this.current = undefined;
|
|
323
|
-
this.processing = false;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** 当前队列长度(测试/诊断用)。含等待处理项(不含 current)。 */
|
|
327
|
-
get size(): number {
|
|
328
|
-
return this.queue.length;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// data-dir.test.ts —— dataDir 通道解析(XYZ_AGENT_DATA_DIR 权威通道 + piAgentDir 回退)。
|
|
2
|
-
//
|
|
3
|
-
// 三视角:①构建者——两级解析顺序正确;②使用者——独立 pi 用户(无 xyz env)拿到
|
|
4
|
-
// piAgentDir 作根;③观察者——回退路径 warn 一次(不留静默漂移),不刷屏。
|
|
5
|
-
//
|
|
6
|
-
// 注意:getAgentDir 在 vitest 下 alias 到 mocks/pi-coding-agent.ts(返回
|
|
7
|
-
// /home/user/.pi/agent)——本测试只断言两级解析顺序与 warn 行为,不测 pi SDK 内部。
|
|
8
|
-
|
|
9
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
10
|
-
|
|
11
|
-
import { getEngineDataDir, resetDataDirWarnForTests, XYZ_DATA_DIR_ENV } from "../../common/data-dir.ts";
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
resetDataDirWarnForTests();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe("getEngineDataDir", () => {
|
|
18
|
-
it("XYZ_AGENT_DATA_DIR 存在 → 权威通道直取,不 warn", () => {
|
|
19
|
-
const warnings: string[] = [];
|
|
20
|
-
const dir = getEngineDataDir({ [XYZ_DATA_DIR_ENV]: "/data/xyz-agent" }, (m) => warnings.push(m));
|
|
21
|
-
expect(dir).toBe("/data/xyz-agent");
|
|
22
|
-
expect(warnings).toEqual([]);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("空串/空白串视为缺失(truthy 语义与 shared getDataDir 的 || 归一对齐)", () => {
|
|
26
|
-
const warnings: string[] = [];
|
|
27
|
-
const dir = getEngineDataDir({ [XYZ_DATA_DIR_ENV]: " " }, (m) => warnings.push(m));
|
|
28
|
-
expect(dir).not.toBe(" ");
|
|
29
|
-
expect(warnings.length).toBe(1);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("缺 env → 回退 piAgentDir(getAgentDir(),mock 值)并 warn 一次", () => {
|
|
33
|
-
const warnings: string[] = [];
|
|
34
|
-
const warn = (m: string): void => warnings.push(m);
|
|
35
|
-
const dir1 = getEngineDataDir({}, warn);
|
|
36
|
-
expect(dir1).toBe("/home/user/.pi/agent"); // vitest mock 的 getAgentDir
|
|
37
|
-
expect(warnings.length).toBe(1);
|
|
38
|
-
expect(warnings[0]).toContain("XYZ_AGENT_DATA_DIR");
|
|
39
|
-
|
|
40
|
-
// warn once:第二次调用不再刷
|
|
41
|
-
const dir2 = getEngineDataDir({}, warn);
|
|
42
|
-
expect(dir2).toBe(dir1);
|
|
43
|
-
expect(warnings.length).toBe(1);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("reset 后再次回退会重新 warn(测试隔离钩子可用)", () => {
|
|
47
|
-
const warnings: string[] = [];
|
|
48
|
-
getEngineDataDir({}, (m) => warnings.push(m));
|
|
49
|
-
resetDataDirWarnForTests();
|
|
50
|
-
getEngineDataDir({}, (m) => warnings.push(m));
|
|
51
|
-
expect(warnings.length).toBe(2);
|
|
52
|
-
});
|
|
53
|
-
});
|