@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,184 +0,0 @@
|
|
|
1
|
-
// src/execution/__tests__/gc-timer.test.ts
|
|
2
|
-
//
|
|
3
|
-
// [M8] idle record GC 定时器测试(subagent-service.ts startGcTimer,L485-507)。
|
|
4
|
-
//
|
|
5
|
-
// 背景:GC 定时器(1h interval 扫描 + isResumable && idleSince 30 天 TTL + store.archive)
|
|
6
|
-
// 此前零测试覆盖——所有相关测试把 startGcTimer mock 成 no-op(index-session-start /
|
|
7
|
-
// crash-recovery / stream-sink-guard / session-start-reaper / index-session-start-identity)。
|
|
8
|
-
// 误判 isResumable/idleSince 会把可续聊 record 从内存错误归档(用户数据路径),
|
|
9
|
-
// 或永不触发(内存驻留失效)——两个方向都无回归拦截。
|
|
10
|
-
//
|
|
11
|
-
// 本文件用真实 SubagentService + 真实 startGcTimer(不 mock GC 逻辑本身),仅 mock
|
|
12
|
-
// session-runner(import 链依赖 + isResumable 的 hasLiveProcessHandle 查询点)与 logger。
|
|
13
|
-
// timer 全部 vi.useFakeTimers,不真实等待。
|
|
14
|
-
|
|
15
|
-
import type { ChildProcess } from "node:child_process";
|
|
16
|
-
import * as fs from "node:fs";
|
|
17
|
-
import * as os from "node:os";
|
|
18
|
-
import * as path from "node:path";
|
|
19
|
-
|
|
20
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
21
|
-
|
|
22
|
-
const { loggerMock } = vi.hoisted(() => ({
|
|
23
|
-
loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() },
|
|
24
|
-
}));
|
|
25
|
-
vi.mock("@zhushanwen/pi-extension-logger", () => ({ getLogger: () => loggerMock }));
|
|
26
|
-
|
|
27
|
-
// mock session-runner:runSpawn/killAllSpawnedChildren 占位(import 链需要),
|
|
28
|
-
// getChildByRecord 默认返回 undefined(无活进程 → isResumable=true),用例内按需覆盖。
|
|
29
|
-
vi.mock("../session-runner.ts", () => ({
|
|
30
|
-
runSpawn: vi.fn(),
|
|
31
|
-
killAllSpawnedChildren: vi.fn(),
|
|
32
|
-
getChildByRecord: vi.fn(() => undefined),
|
|
33
|
-
spawnedChildren: new Map(),
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
import { getChildByRecord } from "../session-runner.ts";
|
|
37
|
-
import { createRecord } from "../execution-record.ts";
|
|
38
|
-
import { ModelConfigService } from "../model-config-service.ts";
|
|
39
|
-
import { RecordStore } from "../record-store.ts";
|
|
40
|
-
import { SubagentService } from "../subagent-service.ts";
|
|
41
|
-
import type { ExecutionRecord } from "../types.ts";
|
|
42
|
-
|
|
43
|
-
const mockGetChildByRecord = vi.mocked(getChildByRecord);
|
|
44
|
-
|
|
45
|
-
/** 与 startGcTimer 内部常量一致(1h 扫描 / 30 天 TTL)。 */
|
|
46
|
-
const GC_INTERVAL_MS = 60 * 60 * 1000;
|
|
47
|
-
const IDLE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
48
|
-
|
|
49
|
-
/** initSession 注入的最小 pi duck-type(同 subagent-service PiLike 形状,结构匹配即可)。 */
|
|
50
|
-
interface PiStub {
|
|
51
|
-
appendEntry(customType: string, data?: unknown): void;
|
|
52
|
-
events: { emit(channel: string, data: unknown): void };
|
|
53
|
-
sendMessage(
|
|
54
|
-
message: { customType: string; content: string; display: boolean; details?: unknown },
|
|
55
|
-
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
|
56
|
-
): void;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function makePi(): PiStub {
|
|
60
|
-
return {
|
|
61
|
-
appendEntry: vi.fn(),
|
|
62
|
-
events: { emit: vi.fn() },
|
|
63
|
-
sendMessage: vi.fn(),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** 构造 running + 无活进程 + 指定 idleSince 的 record(GC 扫描的目标态)。
|
|
68
|
-
* listAllActive 只返回 status==="running"(createRecord 初始即 running)。 */
|
|
69
|
-
function makeIdleRecord(id: string, idleSince: number): ExecutionRecord {
|
|
70
|
-
const record = createRecord(id, {
|
|
71
|
-
agent: "general-purpose",
|
|
72
|
-
model: "test/model",
|
|
73
|
-
mode: "background",
|
|
74
|
-
task: "test",
|
|
75
|
-
slug: "test",
|
|
76
|
-
startedAt: idleSince,
|
|
77
|
-
rootSessionId: "root-session",
|
|
78
|
-
chatMode: true,
|
|
79
|
-
controller: new AbortController(),
|
|
80
|
-
});
|
|
81
|
-
record.idleSince = idleSince;
|
|
82
|
-
return record;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** 暴露私有字段供测试读取 store。 */
|
|
86
|
-
interface ServiceInternals {
|
|
87
|
-
store: RecordStore;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
describe("[M8] idle record GC 定时器(startGcTimer)", () => {
|
|
91
|
-
let agentDir: string;
|
|
92
|
-
let service: SubagentService;
|
|
93
|
-
let store: RecordStore;
|
|
94
|
-
|
|
95
|
-
beforeEach(() => {
|
|
96
|
-
vi.useFakeTimers();
|
|
97
|
-
agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "gc-timer-"));
|
|
98
|
-
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
99
|
-
service = new SubagentService({ cwd: agentDir, modelService });
|
|
100
|
-
service.initSession({ pi: makePi(), sessionId: "root-session" });
|
|
101
|
-
store = (service as unknown as ServiceInternals).store;
|
|
102
|
-
mockGetChildByRecord.mockReset();
|
|
103
|
-
// 默认无活进程(isResumable=true)——Path B:进程已回收、可冷路径 resume 的等待续聊态
|
|
104
|
-
mockGetChildByRecord.mockImplementation(() => undefined);
|
|
105
|
-
loggerMock.warn.mockClear();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
afterEach(() => {
|
|
109
|
-
service.dispose();
|
|
110
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
111
|
-
vi.useRealTimers();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("TTL 边界:超 1ms 归档、差 1ms 不归档(同一轮扫描)", () => {
|
|
115
|
-
// idleSince 相对 interval 首次触发时刻(fake clock + 1h)构造:
|
|
116
|
-
// over = fireAt - TTL - 1 → 扫描时 age = TTL + 1 > TTL → archive
|
|
117
|
-
// under = fireAt - TTL + 1 → 扫描时 age = TTL - 1 ≤ TTL → 留内存
|
|
118
|
-
const fireAt = Date.now() + GC_INTERVAL_MS;
|
|
119
|
-
store.register(makeIdleRecord("sa-over", fireAt - IDLE_TTL_MS - 1));
|
|
120
|
-
store.register(makeIdleRecord("sa-under", fireAt - IDLE_TTL_MS + 1));
|
|
121
|
-
|
|
122
|
-
service.startGcTimer();
|
|
123
|
-
vi.advanceTimersByTime(GC_INTERVAL_MS);
|
|
124
|
-
|
|
125
|
-
expect(store.getMutable("sa-over")).toBeUndefined(); // archive 从内存移除
|
|
126
|
-
expect(store.getMutable("sa-under")).toBeDefined(); // 未超 TTL 留内存
|
|
127
|
-
expect(loggerMock.warn).toHaveBeenCalledTimes(1);
|
|
128
|
-
expect(loggerMock.warn).toHaveBeenCalledWith(
|
|
129
|
-
expect.stringContaining("GC: archiving idle record sa-over"),
|
|
130
|
-
);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("有活进程的 record 不归档(isResumable=false,即使 idleSince 超 TTL)", () => {
|
|
134
|
-
const fireAt = Date.now() + GC_INTERVAL_MS;
|
|
135
|
-
// Path A:进程保活等待续聊(idle timer armed、child 未 kill)——归档会让活进程失管
|
|
136
|
-
store.register(makeIdleRecord("sa-live", fireAt - IDLE_TTL_MS - 1));
|
|
137
|
-
mockGetChildByRecord.mockImplementation(
|
|
138
|
-
(id: string): ChildProcess | undefined =>
|
|
139
|
-
id === "sa-live" ? ({ killed: false } as unknown as ChildProcess) : undefined,
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
service.startGcTimer();
|
|
143
|
-
vi.advanceTimersByTime(GC_INTERVAL_MS);
|
|
144
|
-
|
|
145
|
-
expect(store.getMutable("sa-live")).toBeDefined(); // 有活进程 → 不归档
|
|
146
|
-
expect(loggerMock.warn).not.toHaveBeenCalled();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("每小时重复扫描:上一轮差 1ms 的 record 下一轮超龄被归档", () => {
|
|
150
|
-
const fireAt = Date.now() + GC_INTERVAL_MS;
|
|
151
|
-
store.register(makeIdleRecord("sa-late", fireAt - IDLE_TTL_MS + 1));
|
|
152
|
-
|
|
153
|
-
service.startGcTimer();
|
|
154
|
-
vi.advanceTimersByTime(GC_INTERVAL_MS);
|
|
155
|
-
expect(store.getMutable("sa-late")).toBeDefined(); // 第一轮:age = TTL - 1,未到
|
|
156
|
-
|
|
157
|
-
vi.advanceTimersByTime(GC_INTERVAL_MS); // 第二轮:age = TTL + 1h - 1 > TTL
|
|
158
|
-
expect(store.getMutable("sa-late")).toBeUndefined();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("startGcTimer 幂等:重复调用不产生第二个 interval", () => {
|
|
162
|
-
const fireAt = Date.now() + GC_INTERVAL_MS;
|
|
163
|
-
store.register(makeIdleRecord("sa-over", fireAt - IDLE_TTL_MS - 1));
|
|
164
|
-
|
|
165
|
-
service.startGcTimer();
|
|
166
|
-
service.startGcTimer(); // 已有 timer 直接 return(防重复 interval)
|
|
167
|
-
vi.advanceTimersByTime(GC_INTERVAL_MS);
|
|
168
|
-
|
|
169
|
-
// 同一 record 一轮只扫一次(重复 interval 会对同一 record 产生第二条 GC warn)
|
|
170
|
-
expect(loggerMock.warn).toHaveBeenCalledTimes(1);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("dispose 后 interval 停止:超 TTL record 不再被归档", () => {
|
|
174
|
-
service.startGcTimer();
|
|
175
|
-
vi.advanceTimersByTime(GC_INTERVAL_MS); // 第一轮正常触发
|
|
176
|
-
service.dispose(); // stopGcTimer + disposeAllRecords
|
|
177
|
-
|
|
178
|
-
// dispose 后塞入超 TTL record,interval 已停 → 无论推进多久都不归档
|
|
179
|
-
store.register(makeIdleRecord("sa-after-dispose", Date.now() - IDLE_TTL_MS - 1));
|
|
180
|
-
vi.advanceTimersByTime(GC_INTERVAL_MS * 10);
|
|
181
|
-
|
|
182
|
-
expect(store.getMutable("sa-after-dispose")).toBeDefined();
|
|
183
|
-
});
|
|
184
|
-
});
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
// src/execution/__tests__/get-record-for-action-restart.test.ts
|
|
2
|
-
//
|
|
3
|
-
// [M10] getRecordForAction 跨重启磁盘重建分支测试(subagent-service.ts L933-951)。
|
|
4
|
-
//
|
|
5
|
-
// 背景:内存 miss → collectRecords(1000,"all",undefined).find(status==="running") →
|
|
6
|
-
// createRecord({chatMode: true}) → register → 回填 sessionFile/round。代码注释
|
|
7
|
-
// (L946-949)自设强制约束「改动此处必须带 S3 回归场景(跨重启 message 续聊验证)」
|
|
8
|
-
// ——该 S3 场景此前不存在,分支零测试。该分支含多个仅此处独有的决策:
|
|
9
|
-
// - 无条件 chatMode: true(v4 A-3 跨重启恢复入口,V3 方案 A 方向)
|
|
10
|
-
// - rootSessionFilter 传 undefined 后置校验(异树仍 throw not owned)
|
|
11
|
-
// - sessionFile/round 从磁盘重建结果回填(round 无磁盘持久化 → undefined)
|
|
12
|
-
//
|
|
13
|
-
// fixture 构造参照 record-store.test.ts 的 writeSessionJsonl(真实 .jsonl 文件,
|
|
14
|
-
// identity custom entry + assistant message;无 .alive sidecar → record-store
|
|
15
|
-
// sidecar 矩阵分支 4 → status="running" 跨重启可续聊态)。
|
|
16
|
-
|
|
17
|
-
import * as fs from "node:fs";
|
|
18
|
-
import * as os from "node:os";
|
|
19
|
-
import * as path from "node:path";
|
|
20
|
-
|
|
21
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
22
|
-
|
|
23
|
-
const { loggerMock } = vi.hoisted(() => ({
|
|
24
|
-
loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() },
|
|
25
|
-
}));
|
|
26
|
-
vi.mock("@zhushanwen/pi-extension-logger", () => ({ getLogger: () => loggerMock }));
|
|
27
|
-
|
|
28
|
-
// mock session-runner:import 链需要(getRecordForAction 本身不调 spawn,仅守卫读
|
|
29
|
-
// hasLiveProcessHandle——默认无活进程,与跨重启场景一致)。
|
|
30
|
-
vi.mock("../session-runner.ts", () => ({
|
|
31
|
-
runSpawn: vi.fn(),
|
|
32
|
-
killAllSpawnedChildren: vi.fn(),
|
|
33
|
-
getChildByRecord: vi.fn(() => undefined),
|
|
34
|
-
spawnedChildren: new Map(),
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
import { writeFinalized } from "../finalized-marker.ts";
|
|
38
|
-
import { ModelConfigService } from "../model-config-service.ts";
|
|
39
|
-
import { getSubagentSessionDir } from "../path-encoding.ts";
|
|
40
|
-
import { RecordStore } from "../record-store.ts";
|
|
41
|
-
import { SubagentService } from "../subagent-service.ts";
|
|
42
|
-
|
|
43
|
-
// 身份 env 名(与 subagent-service.ts 常量一致;beforeEach/afterEach 清理防泄漏——
|
|
44
|
-
// 测试进程可能继承 subagent env,会污染 rootCwd 编码目录与 sessionRootId 基线)。
|
|
45
|
-
const IDENTITY_ENV_KEYS = [
|
|
46
|
-
"PI_SUBAGENT_ROOT_SESSION_ID",
|
|
47
|
-
"PI_SUBAGENT_SELF_RECORD_ID",
|
|
48
|
-
"PI_SUBAGENT_DEPTH",
|
|
49
|
-
"PI_SUBAGENT_ROOT_CWD",
|
|
50
|
-
"PI_SUBAGENT_FORK_DEPTH",
|
|
51
|
-
] as const;
|
|
52
|
-
|
|
53
|
-
/** initSession 注入的最小 pi duck-type(同 subagent-service PiLike 形状,结构匹配即可)。 */
|
|
54
|
-
interface PiStub {
|
|
55
|
-
appendEntry(customType: string, data?: unknown): void;
|
|
56
|
-
events: { emit(channel: string, data: unknown): void };
|
|
57
|
-
sendMessage(
|
|
58
|
-
message: { customType: string; content: string; display: boolean; details?: unknown },
|
|
59
|
-
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
|
60
|
-
): void;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function makePi(): PiStub {
|
|
64
|
-
return {
|
|
65
|
-
appendEntry: vi.fn(),
|
|
66
|
-
events: { emit: vi.fn() },
|
|
67
|
-
sendMessage: vi.fn(),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** 写一个最小合法 session.jsonl(session header + identity entry + 1 条 assistant message)。
|
|
72
|
-
* 不写任何 sidecar(.alive/.cancelled/.finalized)→ sidecar 矩阵分支 4 → running。 */
|
|
73
|
-
function writeSessionJsonl(
|
|
74
|
-
sessionsDir: string,
|
|
75
|
-
identity: {
|
|
76
|
-
id: string;
|
|
77
|
-
rootSessionId: string;
|
|
78
|
-
parentRecordId?: string;
|
|
79
|
-
depth?: number;
|
|
80
|
-
chatMode?: boolean;
|
|
81
|
-
worktree?: boolean;
|
|
82
|
-
},
|
|
83
|
-
): string {
|
|
84
|
-
const file = path.join(sessionsDir, `${identity.id}.jsonl`);
|
|
85
|
-
const startedAt = 1_700_000_000_000;
|
|
86
|
-
const lines = [
|
|
87
|
-
JSON.stringify({
|
|
88
|
-
type: "session",
|
|
89
|
-
version: 3,
|
|
90
|
-
id: "sess-uuid",
|
|
91
|
-
timestamp: new Date(startedAt).toISOString(),
|
|
92
|
-
cwd: "/tmp",
|
|
93
|
-
}),
|
|
94
|
-
JSON.stringify({
|
|
95
|
-
type: "custom",
|
|
96
|
-
id: "id-1",
|
|
97
|
-
parentId: null,
|
|
98
|
-
timestamp: new Date(startedAt).toISOString(),
|
|
99
|
-
customType: "subagent-identity",
|
|
100
|
-
data: {
|
|
101
|
-
id: identity.id,
|
|
102
|
-
agent: "general-purpose",
|
|
103
|
-
mode: "background",
|
|
104
|
-
task: "restart recovery task",
|
|
105
|
-
slug: "restart-test",
|
|
106
|
-
startedAt,
|
|
107
|
-
rootSessionId: identity.rootSessionId,
|
|
108
|
-
...(identity.parentRecordId !== undefined ? { parentRecordId: identity.parentRecordId } : {}),
|
|
109
|
-
...(identity.depth !== undefined ? { depth: identity.depth } : {}),
|
|
110
|
-
...(identity.chatMode !== undefined ? { chatMode: identity.chatMode } : {}),
|
|
111
|
-
...(identity.worktree !== undefined ? { worktree: identity.worktree } : {}),
|
|
112
|
-
},
|
|
113
|
-
}),
|
|
114
|
-
JSON.stringify({
|
|
115
|
-
type: "message",
|
|
116
|
-
id: "msg-1",
|
|
117
|
-
parentId: "id-1",
|
|
118
|
-
timestamp: new Date(startedAt + 1000).toISOString(),
|
|
119
|
-
message: {
|
|
120
|
-
role: "assistant",
|
|
121
|
-
content: [{ type: "text", text: "first round done" }],
|
|
122
|
-
usage: { input: 10, output: 20, cacheRead: 0, cacheWrite: 0 },
|
|
123
|
-
stopReason: "stop",
|
|
124
|
-
timestamp: startedAt + 1000,
|
|
125
|
-
},
|
|
126
|
-
}),
|
|
127
|
-
];
|
|
128
|
-
fs.writeFileSync(file, `${lines.join("\n")}\n`, "utf-8");
|
|
129
|
-
return file;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** 暴露私有 store 供断言 register 副作用。 */
|
|
133
|
-
interface ServiceInternals {
|
|
134
|
-
store: RecordStore;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
describe("[M10] getRecordForAction 跨重启磁盘重建(S3 回归场景)", () => {
|
|
138
|
-
let agentDir: string;
|
|
139
|
-
let sessionsDir: string;
|
|
140
|
-
let service: SubagentService;
|
|
141
|
-
let store: RecordStore;
|
|
142
|
-
|
|
143
|
-
beforeEach(() => {
|
|
144
|
-
for (const k of IDENTITY_ENV_KEYS) delete process.env[k];
|
|
145
|
-
agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "restart-recon-"));
|
|
146
|
-
sessionsDir = getSubagentSessionDir(agentDir, agentDir);
|
|
147
|
-
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
148
|
-
|
|
149
|
-
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
150
|
-
service = new SubagentService({ cwd: agentDir, modelService });
|
|
151
|
-
// 根进程身份:无 env → sessionRootId = sessionId(自己是 root)、execCtxBaseline = null
|
|
152
|
-
service.initSession({ pi: makePi(), sessionId: "root-session" });
|
|
153
|
-
store = (service as unknown as ServiceInternals).store;
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
afterEach(() => {
|
|
157
|
-
service.dispose();
|
|
158
|
-
// maxRetries:collectRecords 触发的 fire-and-forget sessions-index 写可能与删除
|
|
159
|
-
// 并发(ENOTEMPTY 竞态,全量并行跑时机器负载高会放大窗口)
|
|
160
|
-
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
161
|
-
for (const k of IDENTITY_ENV_KEYS) delete process.env[k];
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("内存 miss + 磁盘 running(无 .alive)→ 重建 record:chatMode 无条件 true、sessionFile 回填、register 进内存", () => {
|
|
165
|
-
// identity entry 故意不带 chatMode 字段——证明重建分支的无条件 chatMode:true
|
|
166
|
-
//(不依赖磁盘是否记录过对话模式标志)
|
|
167
|
-
const file = writeSessionJsonl(sessionsDir, { id: "sa-restart-1", rootSessionId: "root-session" });
|
|
168
|
-
expect(store.getMutable("sa-restart-1")).toBeUndefined(); // 前置:内存确无
|
|
169
|
-
|
|
170
|
-
const record = service.getRecordForAction("sa-restart-1");
|
|
171
|
-
|
|
172
|
-
// [v4 A-3] 无条件 chatMode=true(跨重启恢复入口)
|
|
173
|
-
expect(record.chatMode).toBe(true);
|
|
174
|
-
// sessionFile 从磁盘重建结果回填
|
|
175
|
-
expect(record.sessionFile).toBe(file);
|
|
176
|
-
// round 从 found 回填:light 磁盘重建无 round(内存态字段,跨重启不恢复)→ undefined
|
|
177
|
-
expect(record.round).toBeUndefined();
|
|
178
|
-
// 身份字段从磁盘 identity entry 回填
|
|
179
|
-
expect(record.agent).toBe("general-purpose");
|
|
180
|
-
expect(record.task).toBe("restart recovery task");
|
|
181
|
-
expect(record.slug).toBe("restart-test");
|
|
182
|
-
expect(record.rootSessionId).toBe("root-session");
|
|
183
|
-
expect(record.parentRecordId).toBeUndefined();
|
|
184
|
-
expect(record.status).toBe("running");
|
|
185
|
-
// register 生效:进内存,二次调用走内存命中(同一引用)
|
|
186
|
-
expect(store.getMutable("sa-restart-1")).toBe(record);
|
|
187
|
-
expect(service.getRecordForAction("sa-restart-1")).toBe(record);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("rootSessionId 校验仍生效:异树 record(other-session)→ throw not found or not owned", () => {
|
|
191
|
-
// rootSessionFilter 传 undefined(扫全量磁盘),异树 record 会被 find 命中并重建,
|
|
192
|
-
// 但后置校验 record.rootSessionId !== this.sessionRootId 必须拦截
|
|
193
|
-
writeSessionJsonl(sessionsDir, { id: "sa-foreign", rootSessionId: "other-session" });
|
|
194
|
-
|
|
195
|
-
expect(() => service.getRecordForAction("sa-foreign")).toThrow(/not found or not owned/);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("直接父校验仍生效:孙级 record(parentRecordId=sa-parent)→ 主进程 throw direct parent", () => {
|
|
199
|
-
writeSessionJsonl(sessionsDir, {
|
|
200
|
-
id: "sa-grand",
|
|
201
|
-
rootSessionId: "root-session",
|
|
202
|
-
parentRecordId: "sa-parent",
|
|
203
|
-
depth: 2,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// 重建后 parentRecordId=sa-parent ≠ 主进程 baseline(undefined) → cross-layer 守卫拦截
|
|
207
|
-
expect(() => service.getRecordForAction("sa-grand")).toThrow(/direct parent/);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it(".finalized sidecar(closed 终态)不重建 → throw not found or not owned", () => {
|
|
211
|
-
const file = writeSessionJsonl(sessionsDir, { id: "sa-fin", rootSessionId: "root-session" });
|
|
212
|
-
writeFinalized(file); // sidecar 矩阵分支 2 → status=closed → find(status==="running") miss
|
|
213
|
-
|
|
214
|
-
expect(() => service.getRecordForAction("sa-fin")).toThrow(/not found or not owned/);
|
|
215
|
-
expect(store.getMutable("sa-fin")).toBeUndefined(); // 未重建注册
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// ============================================================
|
|
219
|
-
// [review round2] 跨重启 worktree 绑定丢失防护
|
|
220
|
-
// ============================================================
|
|
221
|
-
// 场景:worktree:true + conversation:true 的 subagent 在父进程重启后续聊。WorktreeHandle
|
|
222
|
-
// 不可序列化、重建后恒缺失,若无守卫 → 冷路径 resume 的 spawn cwd 静默回落主 repo
|
|
223
|
-
//(隔离失效,正是 worktree 要防的并发写冲突场景)。
|
|
224
|
-
it("[review round2] worktree record 跨重启重建 → hadWorktree 标记 + 续聊被拒(行动语言)", async () => {
|
|
225
|
-
writeSessionJsonl(sessionsDir, {
|
|
226
|
-
id: "sa-wt",
|
|
227
|
-
rootSessionId: "root-session",
|
|
228
|
-
worktree: true,
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
const record = service.getRecordForAction("sa-wt");
|
|
232
|
-
// hadWorktree 从磁盘 identity entry 的 worktree 标志恢复
|
|
233
|
-
expect(record.hadWorktree).toBe(true);
|
|
234
|
-
// handle 无法恢复(不可序列化)
|
|
235
|
-
expect(record.worktreeHandle).toBeUndefined();
|
|
236
|
-
|
|
237
|
-
// 冷路径续聊(无活进程)→ resumeRound 守卫拒绝,不 spawn 回落主 repo
|
|
238
|
-
await expect(service.deliverMessage(record, "resume after restart", false)).rejects.toThrow(
|
|
239
|
-
/worktree isolation.*lost when the parent process restarted/,
|
|
240
|
-
);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it("[review round2] 非 worktree record 跨重启重建 → 续聊不受 worktree 守卫拦截(向后兼容)", async () => {
|
|
244
|
-
// identity entry 无 worktree 字段(旧文件)→ found.worktree undefined → hadWorktree false
|
|
245
|
-
writeSessionJsonl(sessionsDir, { id: "sa-nowt", rootSessionId: "root-session" });
|
|
246
|
-
|
|
247
|
-
const record = service.getRecordForAction("sa-nowt");
|
|
248
|
-
expect(record.hadWorktree).toBe(false);
|
|
249
|
-
|
|
250
|
-
// deliverMessage 冷路径不被 worktree 守卫拦截(resume 正常发起;后续 spawn 编排
|
|
251
|
-
// 超出本用例关注点——runSpawn 在本文件是 no-op mock)
|
|
252
|
-
await expect(service.deliverMessage(record, "resume normal", false)).resolves.toBeUndefined();
|
|
253
|
-
});
|
|
254
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// src/__tests__/gui-mode-dispatch.test.ts
|
|
2
|
-
//
|
|
3
|
-
// S6: ctx.mode RPC 分发契约测试。
|
|
4
|
-
//
|
|
5
|
-
// 审查发现:sdk-contract.test.ts 只测 ctx.model 透传,未覆盖 ctx.mode === "rpc"
|
|
6
|
-
// 时 __gui__ 被填充、ctx.mode === "tui" 时不填充的 GUI 通道分发。
|
|
7
|
-
//
|
|
8
|
-
// adapter() 是纯函数,ctx.mode === "rpc" → details.__gui__ 被附加,
|
|
9
|
-
// ctx.mode === "tui"/"json"/"print" → details.__gui__ 为 undefined。
|
|
10
|
-
|
|
11
|
-
import type { GuiContext } from "@xyz-agent/extension-protocol";
|
|
12
|
-
import { describe, expect, it } from "vitest";
|
|
13
|
-
|
|
14
|
-
import type { AdapterInput } from "../../interface/subagent-actions.ts";
|
|
15
|
-
import { adapter } from "../../interface/subagent-actions.ts";
|
|
16
|
-
|
|
17
|
-
function makeStartInput(): AdapterInput {
|
|
18
|
-
return {
|
|
19
|
-
action: "start",
|
|
20
|
-
domain: {
|
|
21
|
-
subagentId: "test-id",
|
|
22
|
-
sessionFile: "/test/session.jsonl",
|
|
23
|
-
slug: "test-slug",
|
|
24
|
-
response: { status: "started" },
|
|
25
|
-
},
|
|
26
|
-
} as unknown as AdapterInput;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
describe("S6: ctx.mode dispatches __gui__ output correctly", () => {
|
|
30
|
-
it("ctx.mode=rpc → details.__gui__ is populated", () => {
|
|
31
|
-
const ctx = { mode: "rpc", hasUI: true } as GuiContext;
|
|
32
|
-
const result = adapter(makeStartInput(), ctx);
|
|
33
|
-
expect(result.details).toHaveProperty("__gui__");
|
|
34
|
-
expect(result.details.__gui__).toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("ctx.mode=tui → details.__gui__ is undefined (TUI renders differently)", () => {
|
|
38
|
-
const ctx = { mode: "tui", hasUI: true } as GuiContext;
|
|
39
|
-
const result = adapter(makeStartInput(), ctx);
|
|
40
|
-
expect(result.details.__gui__).toBeUndefined();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("ctx.mode=json → details.__gui__ is undefined (headless)", () => {
|
|
44
|
-
const ctx = { mode: "json", hasUI: false } as GuiContext;
|
|
45
|
-
const result = adapter(makeStartInput(), ctx);
|
|
46
|
-
expect(result.details.__gui__).toBeUndefined();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("ctx.mode=print → details.__gui__ is undefined (headless)", () => {
|
|
50
|
-
const ctx = { mode: "print", hasUI: false } as GuiContext;
|
|
51
|
-
const result = adapter(makeStartInput(), ctx);
|
|
52
|
-
expect(result.details.__gui__).toBeUndefined();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("ctx=undefined → details.__gui__ is undefined (backward compat)", () => {
|
|
56
|
-
const result = adapter(makeStartInput(), undefined);
|
|
57
|
-
expect(result.details.__gui__).toBeUndefined();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 创建一个结构兼容的 ExtensionAPI mock,供 sdk-contract / 注册契约测试用。
|
|
3
|
-
*
|
|
4
|
-
* ExtensionAPI 有 ~30 个必需方法(on/registerTool/registerCommand/...),逐个手写
|
|
5
|
-
* 不现实。本 helper 用 Proxy 把所有未显式 override 的方法/属性短路为 no-op 函数,
|
|
6
|
-
* 让对象**结构兼容** ExtensionAPI——避免 `as unknown as ExtensionAPI` 双重断言
|
|
7
|
-
* (taste/no-unsafe-cast 规则禁止)。测试只需 override 关心的方法:
|
|
8
|
-
*
|
|
9
|
-
* const pi = mockExtensionApi({
|
|
10
|
-
* registerCommand: (name) => { capturedName = name; },
|
|
11
|
-
* });
|
|
12
|
-
*
|
|
13
|
-
* Proxy 的 trap 对所有属性访问返回 override 值或 no-op 函数,运行时安全
|
|
14
|
-
* (注册 handler 只调用被 override 的方法,其余方法测试不触及)。
|
|
15
|
-
*/
|
|
16
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
17
|
-
|
|
18
|
-
export function mockExtensionApi(
|
|
19
|
-
overrides: Record<string, unknown> = {},
|
|
20
|
-
): ExtensionAPI {
|
|
21
|
-
const noop = (): void => { /* test mock: method not invoked by this test */ };
|
|
22
|
-
// Proxy<T> 泛型参数决定返回类型——直接声明为 ExtensionAPI,
|
|
23
|
-
// TS 接受(Proxy handler 对 target 的类型不约束 T)。
|
|
24
|
-
return new Proxy<ExtensionAPI>(overrides as ExtensionAPI, {
|
|
25
|
-
get(target, prop: string | symbol): unknown {
|
|
26
|
-
if (prop in target) return target[prop as keyof ExtensionAPI];
|
|
27
|
-
return noop;
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
}
|