@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,211 +0,0 @@
|
|
|
1
|
-
// acquireActivateLock 30s 超时兜底单测 —— v4-lifecycle-convergence.md §3.3 A-2。
|
|
2
|
-
//
|
|
3
|
-
// 验证:前序 holder 持锁不 release(崩溃/死锁模拟)时,waiter
|
|
4
|
-
// - 等待 29999ms 仍 pending(未误伤正常排队)
|
|
5
|
-
// - 到 30001ms 抛含恢复指引的超时错误('activation timed out' + 'retry action: message')
|
|
6
|
-
//
|
|
7
|
-
// 测试策略:纯 Promise 链 + setTimeout 超时,用 vi.useFakeTimers() 控时,
|
|
8
|
-
// advanceTimersByTimeAsync 同时推进 fake timer 与 microtask flush。
|
|
9
|
-
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
_getActivateLockTailCountForTest,
|
|
14
|
-
_resetLifecycleState,
|
|
15
|
-
acquireActivateLock,
|
|
16
|
-
} from "../lifecycle-manager.ts";
|
|
17
|
-
|
|
18
|
-
describe("acquireActivateLock 30s 超时兜底 (v4 A-2)", () => {
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
vi.useFakeTimers();
|
|
21
|
-
_resetLifecycleState();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
_resetLifecycleState();
|
|
26
|
-
vi.useRealTimers();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("waiter 等待 29999ms 仍 pending;到 30001ms 抛含恢复指引的超时错误", async () => {
|
|
30
|
-
// 第一个 holder 持锁后不 release(模拟崩溃/死锁)
|
|
31
|
-
await acquireActivateLock("sa-stuck");
|
|
32
|
-
|
|
33
|
-
// 第二个 waiter 必然排队(前序未 release)
|
|
34
|
-
let errMsg: string | null = null;
|
|
35
|
-
let gotRelease: (() => void) | null = null;
|
|
36
|
-
const waiter = acquireActivateLock("sa-stuck")
|
|
37
|
-
.then((release) => {
|
|
38
|
-
gotRelease = release;
|
|
39
|
-
})
|
|
40
|
-
.catch((err: unknown) => {
|
|
41
|
-
errMsg = err instanceof Error ? err.message : String(err);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// flush microtask:waiter 进入 prev.then 等待链
|
|
45
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
46
|
-
expect(gotRelease).toBe(null); // 尚未拿到锁
|
|
47
|
-
expect(errMsg).toBe(null);
|
|
48
|
-
|
|
49
|
-
// 29999ms:未满 30s,waiter 仍 pending
|
|
50
|
-
await vi.advanceTimersByTimeAsync(29999);
|
|
51
|
-
expect(gotRelease).toBe(null);
|
|
52
|
-
expect(errMsg).toBe(null);
|
|
53
|
-
|
|
54
|
-
// 再推进 2ms(累计 30001ms):超时 timer 触发 → reject
|
|
55
|
-
await vi.advanceTimersByTimeAsync(2);
|
|
56
|
-
await waiter;
|
|
57
|
-
|
|
58
|
-
// 超时:未拿到锁,抛含恢复指引的错误
|
|
59
|
-
expect(gotRelease).toBe(null);
|
|
60
|
-
expect(errMsg).not.toBe(null);
|
|
61
|
-
expect(errMsg).toContain("activation timed out");
|
|
62
|
-
expect(errMsg).toContain("retry action: message");
|
|
63
|
-
expect(errMsg).toContain("sa-stuck");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("首次 acquire(无前序 holder)立即返回 release,不触发超时", async () => {
|
|
67
|
-
// 首次 acquire:prev=Promise.resolve(),立即 resolve,timer 被 clearTimeout 取消
|
|
68
|
-
const release = await acquireActivateLock("sa-fresh");
|
|
69
|
-
expect(typeof release).toBe("function");
|
|
70
|
-
|
|
71
|
-
// 推进远超 30s,不应有任何未捕获的超时 reject(timer 已清除)
|
|
72
|
-
await vi.advanceTimersByTimeAsync(60000);
|
|
73
|
-
release();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("[review 修复] 超时后前序 release → 链可推进:二次 acquire 立即获锁(锁链不瘫痪)", async () => {
|
|
77
|
-
// 瘫痪路径回归:waiter 超时 reject 后,若其 current 永久 pending,tail 永久
|
|
78
|
-
// pending → 前序 release 后后续 acquire 也只能 30s 超时(只能重启恢复)。
|
|
79
|
-
// 修复后超时回调 resolve 自身 current 放行链尾:release1 后 tail settle,
|
|
80
|
-
// 二次 acquire 的 prev 已 settle → 立即拿到 releaseFn。
|
|
81
|
-
const release1 = await acquireActivateLock("sa-recover");
|
|
82
|
-
|
|
83
|
-
let waiterErr: string | null = null;
|
|
84
|
-
const waiter = acquireActivateLock("sa-recover").catch((err: unknown) => {
|
|
85
|
-
waiterErr = err instanceof Error ? err.message : String(err);
|
|
86
|
-
});
|
|
87
|
-
await vi.advanceTimersByTimeAsync(0); // waiter 挂链
|
|
88
|
-
await vi.advanceTimersByTimeAsync(30001); // 超时 reject(+ 超时放行链尾)
|
|
89
|
-
await waiter;
|
|
90
|
-
expect(waiterErr).toContain("activation timed out");
|
|
91
|
-
|
|
92
|
-
release1(); // 前序释放 → tail settle(current 已被超时放行 resolve)
|
|
93
|
-
await vi.advanceTimersByTimeAsync(0); // flush microtask 链
|
|
94
|
-
|
|
95
|
-
// 二次 acquire:prev 是已 settle 的 tail → 立即拿到锁,不等 30s
|
|
96
|
-
const release2 = await acquireActivateLock("sa-recover");
|
|
97
|
-
expect(typeof release2).toBe("function");
|
|
98
|
-
release2();
|
|
99
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("[review 修复] 前序持续持锁时超时不放行后续 waiter 提前获锁(互斥保持)", async () => {
|
|
103
|
-
// 超时放行只 resolve 自身 current(tail 仍等 prev);前序未 release 时,
|
|
104
|
-
// 新 acquire 依然排队等待,不会与前序 holder 形成双写者。
|
|
105
|
-
const release1 = await acquireActivateLock("sa-mutex");
|
|
106
|
-
|
|
107
|
-
const waiter1 = acquireActivateLock("sa-mutex").catch(() => {});
|
|
108
|
-
await vi.advanceTimersByTimeAsync(30001); // waiter1 超时放行自身 current
|
|
109
|
-
await waiter1;
|
|
110
|
-
|
|
111
|
-
// 前序仍持锁:新 acquire 的 prev(= waiter1 的 tail)等待 release1,不提前获锁
|
|
112
|
-
let gotRelease2 = false;
|
|
113
|
-
const waiter2 = acquireActivateLock("sa-mutex").then(() => {
|
|
114
|
-
gotRelease2 = true;
|
|
115
|
-
});
|
|
116
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
117
|
-
expect(gotRelease2).toBe(false); // 未提前获锁
|
|
118
|
-
|
|
119
|
-
release1();
|
|
120
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
121
|
-
await waiter2; // release1 后链推进,waiter2 获锁
|
|
122
|
-
expect(gotRelease2).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("[review 修复] 超时者对称自清:前序 release 推进链尾后 Map 条目回收;前序持锁期间不提前删", async () => {
|
|
126
|
-
// round2 INFO 残留:超时者 releaseFn 永不被调用,原实现条目滞留 Map 直到下次
|
|
127
|
-
// 同 recordId acquire 覆盖。修复后挂 tail 尾部自清——tail settle(前序已
|
|
128
|
-
// release)时回收;tail pending(前序仍持锁)时回调不执行,条目保留(后续
|
|
129
|
-
// acquire 仍排队等待,互斥不被破坏)。
|
|
130
|
-
const release1 = await acquireActivateLock("sa-timeout-gc");
|
|
131
|
-
|
|
132
|
-
const waiter = acquireActivateLock("sa-timeout-gc").catch(() => {});
|
|
133
|
-
await vi.advanceTimersByTimeAsync(0); // waiter 挂链,Map 链尾已覆盖为 waiter 的 tail
|
|
134
|
-
expect(_getActivateLockTailCountForTest()).toBe(1);
|
|
135
|
-
|
|
136
|
-
await vi.advanceTimersByTimeAsync(30001); // 超时:reject + settleCurrent + tail.then 自清排队
|
|
137
|
-
await waiter;
|
|
138
|
-
// 前序仍持锁 → tail 仍 pending → 自清回调未执行 → 条目仍在
|
|
139
|
-
expect(_getActivateLockTailCountForTest()).toBe(1);
|
|
140
|
-
|
|
141
|
-
release1(); // 前序释放 → tail settle → 自清回调执行(identity 匹配,无新 waiter)
|
|
142
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
143
|
-
expect(_getActivateLockTailCountForTest()).toBe(0);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe("activateLockTails tail-identity 自清(release 后回收)", () => {
|
|
148
|
-
beforeEach(() => {
|
|
149
|
-
vi.useFakeTimers();
|
|
150
|
-
_resetLifecycleState();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
afterEach(() => {
|
|
154
|
-
_resetLifecycleState();
|
|
155
|
-
vi.useRealTimers();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("W3TC14: 无 waiter 时 release 后 Map 空 + release 后可再次 acquire", async () => {
|
|
159
|
-
const release = await acquireActivateLock("sa-gc");
|
|
160
|
-
// 自清新增的 test-only 观察点:acquire 后 Map 有 1 条链尾
|
|
161
|
-
expect(_getActivateLockTailCountForTest()).toBe(1);
|
|
162
|
-
|
|
163
|
-
release();
|
|
164
|
-
// 自清经 queueMicrotask 异步执行——advanceTimersByTimeAsync(0) flush microtask
|
|
165
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
166
|
-
// 无 waiter:Map 中链尾即本链尾,identity 匹配 → delete 回收
|
|
167
|
-
expect(_getActivateLockTailCountForTest()).toBe(0);
|
|
168
|
-
|
|
169
|
-
// release 后可再次 acquire:prev 回落 Promise.resolve(),立即 resolve 返回 release
|
|
170
|
-
const release2 = await acquireActivateLock("sa-gc");
|
|
171
|
-
expect(typeof release2).toBe("function");
|
|
172
|
-
release2();
|
|
173
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
174
|
-
expect(_getActivateLockTailCountForTest()).toBe(0);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it("W3TC15: 有 waiter 排队不删、最后释放者回收;不同 recordId 链独立", async () => {
|
|
178
|
-
const release1 = await acquireActivateLock("sa-q");
|
|
179
|
-
|
|
180
|
-
// 发起 acquire#2 同 recordId(不 await,.then 捕获 release2)
|
|
181
|
-
let release2: (() => void) | null = null;
|
|
182
|
-
const waiter = acquireActivateLock("sa-q").then((r) => {
|
|
183
|
-
release2 = r;
|
|
184
|
-
});
|
|
185
|
-
// #2 进入等待链——此刻 Map 链尾已被 #2 覆盖
|
|
186
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
187
|
-
|
|
188
|
-
release1();
|
|
189
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
190
|
-
// ES7 核心:waiter 的链尾仍在——identity 不匹配不删
|
|
191
|
-
expect(_getActivateLockTailCountForTest()).toBe(1);
|
|
192
|
-
|
|
193
|
-
// acquire#2 在 release1 后正常 resolve(等待链不被 delete 破坏——waiter 持
|
|
194
|
-
// acquire 时刻捕获的 prev Promise 引用而非 Map 查询)
|
|
195
|
-
await waiter;
|
|
196
|
-
expect(typeof release2).toBe("function");
|
|
197
|
-
|
|
198
|
-
// 另一 recordId 独立链:同时持有时各自独立计数
|
|
199
|
-
const releaseA = await acquireActivateLock("sa-a");
|
|
200
|
-
expect(_getActivateLockTailCountForTest()).toBe(2); // sa-q + sa-a
|
|
201
|
-
|
|
202
|
-
release2!();
|
|
203
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
204
|
-
// 最后释放者回收:release2 的 identity 匹配 → sa-q 条目回收,sa-a 仍在
|
|
205
|
-
expect(_getActivateLockTailCountForTest()).toBe(1);
|
|
206
|
-
|
|
207
|
-
releaseA();
|
|
208
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
209
|
-
expect(_getActivateLockTailCountForTest()).toBe(0);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
// lifecycle-manager 单测 —— V2 §5.2 模块 1(进程生命周期管理)五项职责。
|
|
2
|
-
//
|
|
3
|
-
// 测试策略:
|
|
4
|
-
// - idle timer / ceiling LRU 用 vi.useFakeTimers() + advanceTimersByTime(同时 mock
|
|
5
|
-
// Date.now(),让 lastTouched 时间戳可控)。
|
|
6
|
-
// - activate 互斥锁是纯 Promise 链(无 timer),用 advanceTimersByTimeAsync(0)
|
|
7
|
-
// flush microtask 验证串行化。
|
|
8
|
-
// - 每个用例 beforeEach 调 _resetLifecycleState() 隔离模块级单例状态。
|
|
9
|
-
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
DEFAULT_IDLE_TIMEOUT_MS,
|
|
14
|
-
DEFAULT_MAX_ALIVE_PROCESSES,
|
|
15
|
-
_resetLifecycleState,
|
|
16
|
-
acquireActivateLock,
|
|
17
|
-
armIdleTimer,
|
|
18
|
-
disarmIdleTimer,
|
|
19
|
-
evictIfOverCeiling,
|
|
20
|
-
getActiveProcessCount,
|
|
21
|
-
hasIdleTimer,
|
|
22
|
-
reapAllAliveProcesses,
|
|
23
|
-
registerActiveProcess,
|
|
24
|
-
scanOrphanProcesses,
|
|
25
|
-
touchActiveProcess,
|
|
26
|
-
unregisterActiveProcess,
|
|
27
|
-
} from "../lifecycle-manager.ts";
|
|
28
|
-
|
|
29
|
-
describe("lifecycle-manager — V2 §5.2 模块 1", () => {
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
vi.useFakeTimers();
|
|
32
|
-
_resetLifecycleState();
|
|
33
|
-
// [F-4 同源修复] env 隔离:「默认超时」用例依赖 XYZ_SUBAGENT_IDLE_TIMEOUT_MS
|
|
34
|
-
// 未设基线(getEnvIdleTimeoutMs 会覆盖 DEFAULT),宿主 export 即假红。空串 = 未设。
|
|
35
|
-
vi.stubEnv("XYZ_SUBAGENT_IDLE_TIMEOUT_MS", "");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
_resetLifecycleState();
|
|
40
|
-
vi.useRealTimers();
|
|
41
|
-
vi.unstubAllEnvs();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// ============================================================
|
|
45
|
-
// 职责 1:idle timer
|
|
46
|
-
// ============================================================
|
|
47
|
-
describe("职责1 idle timer", () => {
|
|
48
|
-
it("arm 后到 timeoutMs 触发 onTimeout(边界:差 1ms 不触发)", () => {
|
|
49
|
-
const onTimeout = vi.fn();
|
|
50
|
-
armIdleTimer("sa-1", onTimeout, 1000);
|
|
51
|
-
|
|
52
|
-
expect(hasIdleTimer("sa-1")).toBe(true);
|
|
53
|
-
vi.advanceTimersByTime(999);
|
|
54
|
-
expect(onTimeout).not.toHaveBeenCalled();
|
|
55
|
-
|
|
56
|
-
vi.advanceTimersByTime(1);
|
|
57
|
-
expect(onTimeout).toHaveBeenCalledTimes(1);
|
|
58
|
-
// 触发后自动从 Map 移除(不残留失效 entry)
|
|
59
|
-
expect(hasIdleTimer("sa-1")).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("disarm 后不再触发 onTimeout", () => {
|
|
63
|
-
const onTimeout = vi.fn();
|
|
64
|
-
armIdleTimer("sa-1", onTimeout, 1000);
|
|
65
|
-
disarmIdleTimer("sa-1");
|
|
66
|
-
|
|
67
|
-
expect(hasIdleTimer("sa-1")).toBe(false);
|
|
68
|
-
vi.advanceTimersByTime(5000);
|
|
69
|
-
expect(onTimeout).not.toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("disarm 不存在的 record 为 no-op(不抛错)", () => {
|
|
73
|
-
expect(() => disarmIdleTimer("never-armed")).not.toThrow();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("重复 arm 刷新 timer:旧 timer 作废、重新计时", () => {
|
|
77
|
-
const onTimeout = vi.fn();
|
|
78
|
-
armIdleTimer("sa-1", onTimeout, 1000);
|
|
79
|
-
vi.advanceTimersByTime(500); // 过了 500ms
|
|
80
|
-
|
|
81
|
-
armIdleTimer("sa-1", onTimeout, 1000); // 刷新:重新计 1000ms
|
|
82
|
-
vi.advanceTimersByTime(999);
|
|
83
|
-
expect(onTimeout).not.toHaveBeenCalled(); // 旧 timer 已作废,新的还没到
|
|
84
|
-
|
|
85
|
-
vi.advanceTimersByTime(1);
|
|
86
|
-
expect(onTimeout).toHaveBeenCalledTimes(1);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("默认超时 = DEFAULT_IDLE_TIMEOUT_MS", () => {
|
|
90
|
-
const onTimeout = vi.fn();
|
|
91
|
-
armIdleTimer("sa-1", onTimeout); // 不传 timeoutMs
|
|
92
|
-
|
|
93
|
-
vi.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT_MS - 1);
|
|
94
|
-
expect(onTimeout).not.toHaveBeenCalled();
|
|
95
|
-
|
|
96
|
-
vi.advanceTimersByTime(1);
|
|
97
|
-
expect(onTimeout).toHaveBeenCalledTimes(1);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("显式禁用值(0/负数)→ 不挂 timer(预算语义对齐:idle GC 可显式关闭;旧实现 0 落成 setTimeout(0) 立即 kill)", () => {
|
|
101
|
-
const onTimeout = vi.fn();
|
|
102
|
-
armIdleTimer("sa-disable-0", onTimeout, 0);
|
|
103
|
-
expect(hasIdleTimer("sa-disable-0")).toBe(false);
|
|
104
|
-
armIdleTimer("sa-disable-neg", onTimeout, -1);
|
|
105
|
-
expect(hasIdleTimer("sa-disable-neg")).toBe(false);
|
|
106
|
-
|
|
107
|
-
vi.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT_MS * 2);
|
|
108
|
-
expect(onTimeout).not.toHaveBeenCalled();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("显式禁用值顺带 disarm 已有 armed timer(禁用不形同虚设)", () => {
|
|
112
|
-
const onTimeout = vi.fn();
|
|
113
|
-
armIdleTimer("sa-disable-late", onTimeout, 1000);
|
|
114
|
-
expect(hasIdleTimer("sa-disable-late")).toBe(true);
|
|
115
|
-
armIdleTimer("sa-disable-late", onTimeout, 0);
|
|
116
|
-
expect(hasIdleTimer("sa-disable-late")).toBe(false);
|
|
117
|
-
vi.advanceTimersByTime(2000);
|
|
118
|
-
expect(onTimeout).not.toHaveBeenCalled();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// [U1] setTimeout 2^31-1 溢出 fail-fast:溢出 delay 被 Node 置 1ms 立即触发
|
|
122
|
-
//(「长空闲保活」变「立即 kill」),arm 入口拦截且错误含上限值与恢复指引。
|
|
123
|
-
// 显式禁用通道(<=0)不受影响(上方用例已锁)。
|
|
124
|
-
it("idleTimeoutMs 溢出(>2^31-1)→ fail-fast throw,不挂 timer", () => {
|
|
125
|
-
const onTimeout = vi.fn();
|
|
126
|
-
expect(() => armIdleTimer("sa-overflow", onTimeout, 3_000_000_000)).toThrowError(/2147483647/);
|
|
127
|
-
expect(() => armIdleTimer("sa-overflow", onTimeout, Number.MAX_SAFE_INTEGER)).toThrowError(
|
|
128
|
-
/Recovery/,
|
|
129
|
-
);
|
|
130
|
-
expect(hasIdleTimer("sa-overflow")).toBe(false);
|
|
131
|
-
vi.advanceTimersByTime(3_000_000_000);
|
|
132
|
-
expect(onTimeout).not.toHaveBeenCalled();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("env XYZ_SUBAGENT_IDLE_TIMEOUT_MS 溢出 → fail-fast throw(arm 入口统一拦截)", () => {
|
|
136
|
-
vi.stubEnv("XYZ_SUBAGENT_IDLE_TIMEOUT_MS", "3000000000");
|
|
137
|
-
const onTimeout = vi.fn();
|
|
138
|
-
expect(() => armIdleTimer("sa-env-overflow", onTimeout)).toThrowError(/2147483647/);
|
|
139
|
-
expect(hasIdleTimer("sa-env-overflow")).toBe(false);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// ============================================================
|
|
144
|
-
// 职责 2:全局 ceiling(LRU 挤出)
|
|
145
|
-
// ============================================================
|
|
146
|
-
describe("职责2 全局 ceiling", () => {
|
|
147
|
-
it("register 超过上限时 evictIfOverCeiling 挤出最久空闲", () => {
|
|
148
|
-
registerActiveProcess("sa-1"); // 最早
|
|
149
|
-
vi.advanceTimersByTime(10);
|
|
150
|
-
registerActiveProcess("sa-2");
|
|
151
|
-
vi.advanceTimersByTime(10);
|
|
152
|
-
registerActiveProcess("sa-3"); // 超限(size=3 > ceiling=2)
|
|
153
|
-
expect(getActiveProcessCount()).toBe(3);
|
|
154
|
-
|
|
155
|
-
const evicted: string[] = [];
|
|
156
|
-
evictIfOverCeiling((id) => evicted.push(id), 2);
|
|
157
|
-
|
|
158
|
-
expect(evicted).toEqual(["sa-1"]); // lastTouched 最老的被挤出
|
|
159
|
-
expect(getActiveProcessCount()).toBe(2);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it("不超过上限时 evictIfOverCeiling 不挤出", () => {
|
|
163
|
-
registerActiveProcess("sa-1");
|
|
164
|
-
registerActiveProcess("sa-2");
|
|
165
|
-
|
|
166
|
-
const evicted: string[] = [];
|
|
167
|
-
evictIfOverCeiling((id) => evicted.push(id), 5);
|
|
168
|
-
|
|
169
|
-
expect(evicted).toEqual([]);
|
|
170
|
-
expect(getActiveProcessCount()).toBe(2);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("touch 更新 LRU:被 touch 的不再是挤出候选", () => {
|
|
174
|
-
registerActiveProcess("sa-1");
|
|
175
|
-
vi.advanceTimersByTime(10);
|
|
176
|
-
registerActiveProcess("sa-2");
|
|
177
|
-
vi.advanceTimersByTime(10);
|
|
178
|
-
touchActiveProcess("sa-1"); // sa-1 变最新 → sa-2 变最老
|
|
179
|
-
|
|
180
|
-
const evicted: string[] = [];
|
|
181
|
-
evictIfOverCeiling((id) => evicted.push(id), 1);
|
|
182
|
-
|
|
183
|
-
expect(evicted).toEqual(["sa-2"]); // sa-2 最久未 touch,被挤出
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("touch 不存在的 record 为 no-op(不隐式创建)", () => {
|
|
187
|
-
expect(() => touchActiveProcess("never-registered")).not.toThrow();
|
|
188
|
-
expect(getActiveProcessCount()).toBe(0);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("unregister 清理活进程集合", () => {
|
|
192
|
-
registerActiveProcess("sa-1");
|
|
193
|
-
registerActiveProcess("sa-2");
|
|
194
|
-
unregisterActiveProcess("sa-1");
|
|
195
|
-
expect(getActiveProcessCount()).toBe(1);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("挤出时连带 disarm 被挤 record 的 idle timer", () => {
|
|
199
|
-
registerActiveProcess("sa-1");
|
|
200
|
-
armIdleTimer("sa-1", vi.fn(), 1000);
|
|
201
|
-
registerActiveProcess("sa-2");
|
|
202
|
-
vi.advanceTimersByTime(10);
|
|
203
|
-
registerActiveProcess("sa-3"); // sa-1 最老,将被挤出
|
|
204
|
-
|
|
205
|
-
evictIfOverCeiling(() => {
|
|
206
|
-
/* kill */
|
|
207
|
-
}, 2);
|
|
208
|
-
|
|
209
|
-
expect(hasIdleTimer("sa-1")).toBe(false); // 挤出 cascade disarm
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("unregister 连带 disarm idle timer(进程终态 timer 不残留)", () => {
|
|
213
|
-
registerActiveProcess("sa-1");
|
|
214
|
-
armIdleTimer("sa-1", vi.fn(), 1000);
|
|
215
|
-
unregisterActiveProcess("sa-1");
|
|
216
|
-
expect(hasIdleTimer("sa-1")).toBe(false);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it("默认上限 = DEFAULT_MAX_ALIVE_PROCESSES(不传 maxAlive)", () => {
|
|
220
|
-
for (let i = 0; i < DEFAULT_MAX_ALIVE_PROCESSES; i++) {
|
|
221
|
-
registerActiveProcess(`sa-${i}`);
|
|
222
|
-
vi.advanceTimersByTime(1); // 保证 lastTouched 严格递增
|
|
223
|
-
}
|
|
224
|
-
registerActiveProcess("sa-over"); // 超限
|
|
225
|
-
const evicted: string[] = [];
|
|
226
|
-
evictIfOverCeiling((id) => evicted.push(id)); // 用默认上限
|
|
227
|
-
|
|
228
|
-
expect(evicted).toEqual(["sa-0"]); // 默认上限下挤出最早
|
|
229
|
-
expect(getActiveProcessCount()).toBe(DEFAULT_MAX_ALIVE_PROCESSES);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("大幅超限时 while 循环挤出多个直到 ≤ ceiling", () => {
|
|
233
|
-
for (let i = 0; i < 5; i++) {
|
|
234
|
-
registerActiveProcess(`sa-${i}`);
|
|
235
|
-
vi.advanceTimersByTime(1);
|
|
236
|
-
}
|
|
237
|
-
const evicted: string[] = [];
|
|
238
|
-
evictIfOverCeiling((id) => evicted.push(id), 2);
|
|
239
|
-
|
|
240
|
-
expect(evicted).toEqual(["sa-0", "sa-1", "sa-2"]); // 挤出 3 个(5-2),按 LRU
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// ============================================================
|
|
245
|
-
// 职责 3:shutdown 收割
|
|
246
|
-
// ============================================================
|
|
247
|
-
describe("职责3 shutdown 收割", () => {
|
|
248
|
-
it("reapAllAliveProcesses 遍历活进程调 killFn 并返回列表", () => {
|
|
249
|
-
registerActiveProcess("sa-1");
|
|
250
|
-
registerActiveProcess("sa-2");
|
|
251
|
-
|
|
252
|
-
const killed: string[] = [];
|
|
253
|
-
const reaped = reapAllAliveProcesses((id) => killed.push(id));
|
|
254
|
-
|
|
255
|
-
expect(killed).toHaveLength(2);
|
|
256
|
-
expect(killed).toEqual(expect.arrayContaining(["sa-1", "sa-2"]));
|
|
257
|
-
expect(reaped).toEqual(expect.arrayContaining(["sa-1", "sa-2"]));
|
|
258
|
-
expect(getActiveProcessCount()).toBe(0);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("reap 后 idle timer 已 disarm(advance 不再触发)", () => {
|
|
262
|
-
registerActiveProcess("sa-1");
|
|
263
|
-
const onTimeout = vi.fn();
|
|
264
|
-
armIdleTimer("sa-1", onTimeout, 1000);
|
|
265
|
-
|
|
266
|
-
reapAllAliveProcesses(() => {
|
|
267
|
-
/* kill */
|
|
268
|
-
});
|
|
269
|
-
vi.advanceTimersByTime(10000);
|
|
270
|
-
|
|
271
|
-
expect(onTimeout).not.toHaveBeenCalled(); // timer 已被 reap 清理
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("无活进程时 reapAll 返回空列表", () => {
|
|
275
|
-
const reaped = reapAllAliveProcesses(() => {
|
|
276
|
-
/* kill */
|
|
277
|
-
});
|
|
278
|
-
expect(reaped).toEqual([]);
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// ============================================================
|
|
283
|
-
// 职责 4:孤儿扫描
|
|
284
|
-
// ============================================================
|
|
285
|
-
describe("职责4 孤儿扫描", () => {
|
|
286
|
-
it("pid 存活 + 不在活进程集合 = 孤儿", () => {
|
|
287
|
-
registerActiveProcess("sa-active"); // 在集合内
|
|
288
|
-
const records = [
|
|
289
|
-
{ id: "sa-active", pid: 100 },
|
|
290
|
-
{ id: "sa-orphan", pid: 200 }, // pid 活但不在集合 → 孤儿
|
|
291
|
-
{ id: "sa-dead", pid: 300 }, // pid 不活 → 非孤儿
|
|
292
|
-
{ id: "sa-nopid" }, // 无 pid → 非孤儿
|
|
293
|
-
];
|
|
294
|
-
|
|
295
|
-
const orphans = scanOrphanProcesses(records, (pid) => pid === 100 || pid === 200);
|
|
296
|
-
|
|
297
|
-
expect(orphans).toEqual(["sa-orphan"]);
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
it("pid 不存活 = 非孤儿", () => {
|
|
301
|
-
const records = [{ id: "sa-1", pid: 999 }];
|
|
302
|
-
const orphans = scanOrphanProcesses(records, () => false); // 全不活
|
|
303
|
-
expect(orphans).toEqual([]);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it("在活进程集合内 = 非孤儿(即使 pid 活)", () => {
|
|
307
|
-
registerActiveProcess("sa-1");
|
|
308
|
-
const records = [{ id: "sa-1", pid: 100 }];
|
|
309
|
-
const orphans = scanOrphanProcesses(records, () => true);
|
|
310
|
-
expect(orphans).toEqual([]);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("无 pid 的 record 不视为孤儿", () => {
|
|
314
|
-
const records = [
|
|
315
|
-
{ id: "sa-1" },
|
|
316
|
-
{ id: "sa-2", sessionFile: "/x.jsonl" },
|
|
317
|
-
];
|
|
318
|
-
const orphans = scanOrphanProcesses(records, () => true);
|
|
319
|
-
expect(orphans).toEqual([]);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
it("扫描是只读(不改活进程集合)", () => {
|
|
323
|
-
registerActiveProcess("sa-1");
|
|
324
|
-
scanOrphanProcesses([{ id: "sa-2", pid: 200 }], () => true);
|
|
325
|
-
expect(getActiveProcessCount()).toBe(1); // 集合不变
|
|
326
|
-
});
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// ============================================================
|
|
330
|
-
// 职责 5:activate 互斥锁
|
|
331
|
-
// ============================================================
|
|
332
|
-
describe("职责5 activate 互斥锁", () => {
|
|
333
|
-
it("首次 acquire 立即 resolve 返回 release 函数", async () => {
|
|
334
|
-
const release = await acquireActivateLock("sa-1");
|
|
335
|
-
expect(typeof release).toBe("function");
|
|
336
|
-
release();
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
it("并发 acquire 同一 recordId 串行化:第二次等首次 release", async () => {
|
|
340
|
-
const release1 = await acquireActivateLock("sa-1");
|
|
341
|
-
|
|
342
|
-
let secondResolved = false;
|
|
343
|
-
const secondPromise = acquireActivateLock("sa-1").then((r) => {
|
|
344
|
-
secondResolved = true;
|
|
345
|
-
return r;
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
await vi.advanceTimersByTimeAsync(0); // flush microtask
|
|
349
|
-
expect(secondResolved).toBe(false); // 还在等首次 release
|
|
350
|
-
|
|
351
|
-
release1();
|
|
352
|
-
await vi.advanceTimersByTimeAsync(0); // flush microtask
|
|
353
|
-
expect(secondResolved).toBe(true);
|
|
354
|
-
|
|
355
|
-
const release2 = await secondPromise;
|
|
356
|
-
release2();
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it("不同 recordId 不互斥(各自独立链)", async () => {
|
|
360
|
-
const release1 = await acquireActivateLock("sa-1");
|
|
361
|
-
|
|
362
|
-
let secondResolved = false;
|
|
363
|
-
acquireActivateLock("sa-2").then((r) => {
|
|
364
|
-
secondResolved = true;
|
|
365
|
-
r();
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
await vi.advanceTimersByTimeAsync(0); // flush microtask
|
|
369
|
-
expect(secondResolved).toBe(true); // sa-2 不被 sa-1 阻塞
|
|
370
|
-
|
|
371
|
-
release1();
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it("release 后同 recordId 可再次 acquire", async () => {
|
|
375
|
-
const r1 = await acquireActivateLock("sa-1");
|
|
376
|
-
r1();
|
|
377
|
-
|
|
378
|
-
const r2 = await acquireActivateLock("sa-1");
|
|
379
|
-
expect(typeof r2).toBe("function");
|
|
380
|
-
r2();
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
});
|