@zhushanwen/pi-subagent-workflow 8.5.0 → 8.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -16
- package/relay/relay.mjs +1 -1
- package/src/host/__tests__/pi-host.test.ts +214 -0
- package/src/host/pi-host.ts +109 -0
- package/src/index.ts +142 -32
- package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
- package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
- package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
- package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
- package/src/injectors/engine-awareness.ts +123 -0
- package/src/injectors/model-list-injector.ts +1 -1
- package/src/injectors/subagent-list-injector.ts +8 -8
- package/src/injectors/workflow-list-injector.ts +8 -8
- package/src/interface/__tests__/detectors.test.ts +3 -3
- package/src/interface/__tests__/subagent-tool-path-guard.test.ts +157 -0
- package/src/interface/__tests__/subagent-tool-prompt.test.ts +12 -0
- package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
- package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
- package/src/interface/bg-notify-render.ts +34 -13
- package/src/interface/commands.ts +3 -3
- package/src/interface/format.ts +2 -2
- package/src/interface/helpers.ts +3 -3
- package/src/interface/list-component.ts +4 -4
- package/src/interface/list-shared.ts +2 -2
- package/src/interface/list-view.ts +2 -2
- package/src/interface/subagent-actions.ts +288 -19
- package/src/interface/subagent-tool-schema.ts +163 -0
- package/src/interface/subagent-tool.ts +65 -129
- package/src/interface/subagents.ts +5 -5
- package/src/interface/tool-render.ts +34 -2
- package/src/interface/tool-workflow-script.ts +4 -4
- package/src/interface/tool-workflow.ts +6 -6
- package/src/interface/views/WorkflowsView.ts +5 -5
- package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
- package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
- package/src/interface/views/detail-content.ts +4 -4
- package/src/interface/views/format.ts +4 -4
- package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +132 -18
- package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
- package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
- package/src/execution/__tests__/agent-registry.test.ts +0 -363
- package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
- package/src/execution/__tests__/alive-store.test.ts +0 -147
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
- package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
- package/src/execution/__tests__/bg-notify-render.test.ts +0 -256
- package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
- package/src/execution/__tests__/chat-engine-routing.test.ts +0 -597
- package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
- package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
- package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
- package/src/execution/__tests__/config.test.ts +0 -110
- package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
- package/src/execution/__tests__/crash-recovery.test.ts +0 -320
- package/src/execution/__tests__/delivery-methods.test.ts +0 -385
- package/src/execution/__tests__/dialog-queue.test.ts +0 -299
- package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
- package/src/execution/__tests__/execute-nesting.test.ts +0 -323
- package/src/execution/__tests__/execute-options-mapper.test.ts +0 -189
- package/src/execution/__tests__/execution-record.test.ts +0 -1284
- package/src/execution/__tests__/finalize-record.test.ts +0 -368
- package/src/execution/__tests__/finalized-marker.test.ts +0 -82
- package/src/execution/__tests__/format-schema-instruction.test.ts +0 -138
- package/src/execution/__tests__/format.test.ts +0 -458
- package/src/execution/__tests__/gc-timer.test.ts +0 -184
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
- package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
- package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
- package/src/execution/__tests__/helpers/spawn-mock.ts +0 -238
- package/src/execution/__tests__/host-mode.test.ts +0 -87
- package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
- package/src/execution/__tests__/index-session-start.test.ts +0 -593
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/lifecycle-manager.test.ts +0 -337
- package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
- package/src/execution/__tests__/list-component.test.ts +0 -401
- package/src/execution/__tests__/list-fields.test.ts +0 -109
- package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
- package/src/execution/__tests__/model-resolver.test.ts +0 -413
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
- package/src/execution/__tests__/nested-visibility.test.ts +0 -325
- package/src/execution/__tests__/notifier-flush.test.ts +0 -574
- package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
- package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
- package/src/execution/__tests__/output-collector.test.ts +0 -61
- package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
- package/src/execution/__tests__/path-encoding.test.ts +0 -104
- package/src/execution/__tests__/pi-invocation.test.ts +0 -134
- package/src/execution/__tests__/record-store.test.ts +0 -1057
- package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
- package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
- package/src/execution/__tests__/relay-agent.test.ts +0 -448
- package/src/execution/__tests__/relay-env.test.ts +0 -42
- package/src/execution/__tests__/resource-policy.test.ts +0 -109
- package/src/execution/__tests__/rpc-mode.test.ts +0 -89
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
- package/src/execution/__tests__/run-spawn-edges.test.ts +0 -644
- package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
- package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
- package/src/execution/__tests__/sdk-contract.test.ts +0 -273
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
- package/src/execution/__tests__/session-file-gc.test.ts +0 -293
- package/src/execution/__tests__/session-pending.test.ts +0 -197
- package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
- package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
- package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -311
- package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
- package/src/execution/__tests__/spawn-args.test.ts +0 -434
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
- package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
- package/src/execution/__tests__/spawned-children.test.ts +0 -92
- package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
- package/src/execution/__tests__/status-refactor.test.ts +0 -345
- package/src/execution/__tests__/stdin-writer.test.ts +0 -463
- package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
- package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
- package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
- package/src/execution/__tests__/subagent-service.test.ts +0 -788
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -506
- package/src/execution/__tests__/temp-prompt.test.ts +0 -53
- package/src/execution/__tests__/timeout-integration.test.ts +0 -397
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/__tests__/tool-action.test.ts +0 -385
- package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
- package/src/execution/__tests__/turn-limiter.test.ts +0 -65
- package/src/execution/__tests__/ui-channels.test.ts +0 -187
- package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
- package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
- package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
- package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
- package/src/execution/__tests__/worktree-manager.test.ts +0 -633
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
- package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
- package/src/execution/__tests__/worktree-registry.test.ts +0 -199
- package/src/execution/agent-registry.ts +0 -193
- package/src/execution/agent-result-mapper.ts +0 -90
- package/src/execution/alive-store.ts +0 -92
- package/src/execution/argv-mirror.ts +0 -109
- package/src/execution/best-effort.ts +0 -38
- package/src/execution/channel-registry-access.ts +0 -144
- package/src/execution/concurrency-pool.ts +0 -116
- package/src/execution/config.ts +0 -96
- package/src/execution/dialog-queue.ts +0 -330
- package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
- package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
- package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
- package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
- package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
- package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
- package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
- package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
- package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
- package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
- package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
- package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
- package/src/execution/engine/__tests__/paths.test.ts +0 -39
- package/src/execution/engine/__tests__/registry.test.ts +0 -120
- package/src/execution/engine/__tests__/routing.test.ts +0 -231
- package/src/execution/engine/common/data-dir.ts +0 -62
- package/src/execution/engine/common/errors.ts +0 -183
- package/src/execution/engine/common/event-journal.ts +0 -254
- package/src/execution/engine/common/journal-replay.ts +0 -62
- package/src/execution/engine/common/kill-chain.ts +0 -221
- package/src/execution/engine/common/nesting-guard.ts +0 -50
- package/src/execution/engine/common/persona-router.ts +0 -108
- package/src/execution/engine/common/pool-manager.ts +0 -226
- package/src/execution/engine/common/schema-emulation.ts +0 -189
- package/src/execution/engine/common/session-view-projection.ts +0 -51
- package/src/execution/engine/engine-discovery.ts +0 -65
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
- package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
- package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
- package/src/execution/engine/engines/pi/reader.ts +0 -48
- package/src/execution/engine/engines/pi/registration.ts +0 -35
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
- package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
- package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -567
- package/src/execution/engine/engines/zcode/constants.ts +0 -43
- package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/engine/engines/zcode/parser.ts +0 -436
- package/src/execution/engine/engines/zcode/preparer.ts +0 -363
- package/src/execution/engine/engines/zcode/reader.ts +0 -381
- package/src/execution/engine/engines/zcode/registration.ts +0 -37
- package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -648
- package/src/execution/engine/host-task-spec.ts +0 -47
- package/src/execution/engine/model-prompt.ts +0 -59
- package/src/execution/engine/paths.ts +0 -42
- package/src/execution/engine/port.ts +0 -153
- package/src/execution/engine/registry.ts +0 -123
- package/src/execution/engine/routing.ts +0 -218
- package/src/execution/engine/types.ts +0 -304
- package/src/execution/execute-options-mapper.ts +0 -111
- package/src/execution/execution-record.ts +0 -906
- package/src/execution/finalize-record.ts +0 -251
- package/src/execution/finalized-marker.ts +0 -51
- package/src/execution/get-state-handshake.ts +0 -104
- package/src/execution/host-mode.ts +0 -56
- package/src/execution/idle-gc.ts +0 -47
- package/src/execution/lifecycle-manager.ts +0 -491
- package/src/execution/lifecycle-predicates.ts +0 -65
- package/src/execution/manifest-store.ts +0 -256
- package/src/execution/model-config-service.ts +0 -204
- package/src/execution/model-resolver.ts +0 -276
- package/src/execution/notifier.ts +0 -260
- package/src/execution/output-collector.ts +0 -88
- package/src/execution/path-encoding.ts +0 -60
- package/src/execution/pi-invocation.ts +0 -120
- package/src/execution/record-entry.ts +0 -132
- package/src/execution/record-store.ts +0 -1234
- package/src/execution/relay-env.ts +0 -37
- package/src/execution/session-context-resolver.ts +0 -64
- package/src/execution/session-file-gc.ts +0 -120
- package/src/execution/session-pending.ts +0 -164
- package/src/execution/session-reconstructor.ts +0 -678
- package/src/execution/session-runner.ts +0 -1548
- package/src/execution/sessions-index.ts +0 -304
- package/src/execution/spawn-event-adapter.ts +0 -363
- package/src/execution/stdin-writer.ts +0 -198
- package/src/execution/stream-sink.ts +0 -126
- package/src/execution/subagent-service.ts +0 -2119
- package/src/execution/subprocess-agent-runner.ts +0 -303
- package/src/execution/temp-prompt.ts +0 -62
- package/src/execution/tombstone-store.ts +0 -72
- package/src/execution/turn-limiter.ts +0 -102
- package/src/execution/types.ts +0 -910
- package/src/execution/ui-channels.ts +0 -216
- package/src/execution/ui-interaction-model.ts +0 -48
- package/src/execution/ui-request-handler-factory.ts +0 -212
- package/src/execution/ui-request-observability.ts +0 -81
- package/src/execution/ui-request-queue.ts +0 -174
- package/src/execution/worktree-manager.ts +0 -688
- package/src/execution/worktree-registry.ts +0 -222
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -336
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -208
- package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -157
- package/src/orchestration/__tests__/args-validator.test.ts +0 -143
- package/src/orchestration/__tests__/concurrency-gate.test.ts +0 -125
- package/src/orchestration/__tests__/config-loader.test.ts +0 -503
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -724
- package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
- package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
- package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
- package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -314
- package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
- package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -277
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
- package/src/orchestration/__tests__/lifecycle.test.ts +0 -625
- package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -1971
- package/src/orchestration/__tests__/script-lint.test.ts +0 -831
- package/src/orchestration/__tests__/skill-discovery.test.ts +0 -157
- package/src/orchestration/__tests__/test-mocks.ts +0 -197
- package/src/orchestration/__tests__/worker-host.test.ts +0 -120
- package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -520
- package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
- package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -110
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
- package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
- package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
- package/src/orchestration/agent-opts-resolver.ts +0 -93
- package/src/orchestration/args-validator.ts +0 -127
- package/src/orchestration/concurrency-gate.ts +0 -69
- package/src/orchestration/config-loader.ts +0 -315
- package/src/orchestration/error-recovery.ts +0 -862
- package/src/orchestration/execute-agent-call.ts +0 -217
- package/src/orchestration/launcher.ts +0 -410
- package/src/orchestration/lifecycle.ts +0 -396
- package/src/orchestration/models/__tests__/budget.test.ts +0 -367
- package/src/orchestration/models/__tests__/trace.test.ts +0 -408
- package/src/orchestration/models/agent-call.ts +0 -83
- package/src/orchestration/models/budget.ts +0 -148
- package/src/orchestration/models/ports.ts +0 -164
- package/src/orchestration/models/run-runtime.ts +0 -89
- package/src/orchestration/models/run-spec.ts +0 -83
- package/src/orchestration/models/run-state.ts +0 -44
- package/src/orchestration/models/trace.ts +0 -183
- package/src/orchestration/models/types.ts +0 -292
- package/src/orchestration/models/workflow-run.ts +0 -254
- package/src/orchestration/models/workflow-script-registry.ts +0 -35
- package/src/orchestration/models/workflow-script.ts +0 -117
- package/src/orchestration/script-lint.ts +0 -766
- package/src/orchestration/skill-discovery.ts +0 -92
- package/src/orchestration/worker-handle.ts +0 -115
- package/src/orchestration/worker-host.ts +0 -98
- package/src/orchestration/worker-script-builder.ts +0 -408
- package/src/orchestration/workflow-files.ts +0 -85
- package/src/orchestration/workflow-script-registry-impl.ts +0 -133
- package/src/shared/__tests__/agent-ref.test.ts +0 -34
- package/src/shared/__tests__/meta-parser.test.ts +0 -304
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
- package/src/shared/__tests__/resource-discovery.test.ts +0 -736
- package/src/shared/__tests__/resource-meta.test.ts +0 -51
- package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
- package/src/shared/agent-event.ts +0 -13
- package/src/shared/agent-ref.ts +0 -57
- package/src/shared/meta-parser.ts +0 -261
- package/src/shared/resource-discovery.ts +0 -811
- package/src/shared/resource-meta.ts +0 -65
- package/src/shared/schema-jsonify.ts +0 -56
- package/src/shared/xml-injection.ts +0 -35
- package/workflows/README.md +0 -81
- package/workflows/_shared/agent-refs.cjs +0 -40
- package/workflows/chain.js +0 -131
- package/workflows/map-reduce.js +0 -174
- package/workflows/parallel.js +0 -148
- package/workflows/review-fix-loop-utils.cjs +0 -1307
- package/workflows/review-fix-loop.js +0 -1396
- package/workflows/scatter-gather.js +0 -178
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Golden snapshot 固化测试:记录 notifier.buildLlmContent 在各种输入下的输出。
|
|
3
|
-
*
|
|
4
|
-
* 此测试在迁移**前**运行并固化结果——迁移后必须逐字节一致(G4)。
|
|
5
|
-
*
|
|
6
|
-
* 这些测试直接测试 BgNotifier 的输出内容,不涉及 send/handle 机制,
|
|
7
|
-
* 因此迁移后应通过内核装配 + 同一 buildLlmContent 产生相同输出。
|
|
8
|
-
*/
|
|
9
|
-
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import { describe, expect, it } from "vitest";
|
|
11
|
-
import { createNotifier, type BgNotifyRecord, type NotifierHost } from "../notifier.ts";
|
|
12
|
-
import { renderBgNotifyMessage } from "../../interface/bg-notify-render.ts";
|
|
13
|
-
|
|
14
|
-
/** 渲染锁用 mock theme(与 bg-notify-render.test.ts 同款:透传文本,记录色 token)。 */
|
|
15
|
-
function makeRenderTheme(): { theme: Theme } {
|
|
16
|
-
return {
|
|
17
|
-
theme: {
|
|
18
|
-
fg: (_tag: string, text: string) => text,
|
|
19
|
-
bold: (text: string) => text,
|
|
20
|
-
bg: (_color: string, text: string) => text,
|
|
21
|
-
} as unknown as Theme,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** 创建 mock host,notifier 会立即 flush(hasRunningBackground=false)。 */
|
|
26
|
-
function makeMockHost(): NotifierHost {
|
|
27
|
-
return {
|
|
28
|
-
sendMessage: () => {},
|
|
29
|
-
hasRunningBackground: () => false,
|
|
30
|
-
isIdle: () => true,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* 调用 notifier.notify 并捕获 sendMessage 的 content 参数。
|
|
36
|
-
* 前置条件:hasRunningBackground=false → notify 立即 flush。
|
|
37
|
-
*/
|
|
38
|
-
function captureNotificationContent(record: BgNotifyRecord): string {
|
|
39
|
-
let capturedContent = "";
|
|
40
|
-
const host: NotifierHost = {
|
|
41
|
-
sendMessage: (msg) => {
|
|
42
|
-
capturedContent = (msg as { content: string }).content;
|
|
43
|
-
},
|
|
44
|
-
hasRunningBackground: () => false,
|
|
45
|
-
isIdle: () => true,
|
|
46
|
-
};
|
|
47
|
-
const notifier = createNotifier(host);
|
|
48
|
-
notifier.notify(record);
|
|
49
|
-
notifier.dispose();
|
|
50
|
-
return capturedContent;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
describe("U3_UNIT notifier golden snapshots (pre-migration freeze)", () => {
|
|
54
|
-
it("single closed success — one-shot completed notification", () => {
|
|
55
|
-
const content = captureNotificationContent({
|
|
56
|
-
id: "sa-ptr-7",
|
|
57
|
-
status: "closed",
|
|
58
|
-
agent: "worker",
|
|
59
|
-
result: "done",
|
|
60
|
-
startedAt: 1,
|
|
61
|
-
endedAt: 2,
|
|
62
|
-
});
|
|
63
|
-
expect(content).toBe('Subagent "worker" (sa-ptr-7) completed. Result:\ndone');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("single closed + sessionFile — pointer line appended", () => {
|
|
67
|
-
const content = captureNotificationContent({
|
|
68
|
-
id: "sa-ptr-2",
|
|
69
|
-
status: "closed",
|
|
70
|
-
agent: "w",
|
|
71
|
-
result: "final result",
|
|
72
|
-
sessionFile: "/tmp/sessions/child-2.jsonl",
|
|
73
|
-
startedAt: 1,
|
|
74
|
-
endedAt: 2,
|
|
75
|
-
});
|
|
76
|
-
expect(content).toBe(
|
|
77
|
-
'Subagent "w" (sa-ptr-2) completed. Result:\nfinal result\n\nFull transcript: /tmp/sessions/child-2.jsonl',
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("single running round (chatMode) — round notification", () => {
|
|
82
|
-
const content = captureNotificationContent({
|
|
83
|
-
id: "sa-ptr-4",
|
|
84
|
-
status: "running",
|
|
85
|
-
agent: "w",
|
|
86
|
-
round: 2,
|
|
87
|
-
result: "round2 text",
|
|
88
|
-
startedAt: 1,
|
|
89
|
-
endedAt: 2,
|
|
90
|
-
});
|
|
91
|
-
expect(content).toBe('Subagent "w" (sa-ptr-4) finished a round. Reply:\nround2 text');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("single running round + sessionFile — pointer appended to round notification", () => {
|
|
95
|
-
const content = captureNotificationContent({
|
|
96
|
-
id: "sa-ptr-1",
|
|
97
|
-
status: "running",
|
|
98
|
-
agent: "w",
|
|
99
|
-
round: 1,
|
|
100
|
-
result: "round1 text",
|
|
101
|
-
sessionFile: "/tmp/sessions/child-1.jsonl",
|
|
102
|
-
startedAt: 1,
|
|
103
|
-
endedAt: 2,
|
|
104
|
-
});
|
|
105
|
-
expect(content).toBe(
|
|
106
|
-
'Subagent "w" (sa-ptr-1) finished a round. Reply:\nround1 text\n\nFull transcript: /tmp/sessions/child-1.jsonl',
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("single cancelled — cancelled message, no pointer", () => {
|
|
111
|
-
const content = captureNotificationContent({
|
|
112
|
-
id: "sa-ptr-5",
|
|
113
|
-
status: "closed",
|
|
114
|
-
closedReason: "cancelled",
|
|
115
|
-
agent: "w",
|
|
116
|
-
startedAt: 1,
|
|
117
|
-
endedAt: 2,
|
|
118
|
-
});
|
|
119
|
-
expect(content).toBe('Subagent "w" (sa-ptr-5) cancelled.');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("single gc-failed — error message, no pointer", () => {
|
|
123
|
-
const content = captureNotificationContent({
|
|
124
|
-
id: "sa-ptr-6",
|
|
125
|
-
status: "closed",
|
|
126
|
-
closedReason: "gc",
|
|
127
|
-
agent: "w",
|
|
128
|
-
error: "spawn EPIPE",
|
|
129
|
-
startedAt: 1,
|
|
130
|
-
endedAt: 2,
|
|
131
|
-
});
|
|
132
|
-
expect(content).toBe('Subagent "w" (sa-ptr-6) failed: spawn EPIPE');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("single gc-failed + patchFile — failed takes priority, no patch hint", () => {
|
|
136
|
-
const content = captureNotificationContent({
|
|
137
|
-
id: "sa-ptr-8",
|
|
138
|
-
status: "closed",
|
|
139
|
-
closedReason: "gc",
|
|
140
|
-
agent: "w",
|
|
141
|
-
error: "spawn EPIPE",
|
|
142
|
-
patchFile: "/tmp/patches/sa-ptr-8.patch",
|
|
143
|
-
startedAt: 1,
|
|
144
|
-
endedAt: 2,
|
|
145
|
-
});
|
|
146
|
-
expect(content).toBe('Subagent "w" (sa-ptr-8) failed: spawn EPIPE');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("single closed + patchFile + sessionFile — patch hint + pointer", () => {
|
|
150
|
-
const content = captureNotificationContent({
|
|
151
|
-
id: "sa-ptr-3",
|
|
152
|
-
status: "closed",
|
|
153
|
-
agent: "w",
|
|
154
|
-
result: "did work",
|
|
155
|
-
patchFile: "/tmp/patches/sa-ptr-3.patch",
|
|
156
|
-
sessionFile: "/tmp/sessions/child-3.jsonl",
|
|
157
|
-
startedAt: 1,
|
|
158
|
-
endedAt: 2,
|
|
159
|
-
});
|
|
160
|
-
expect(content).toContain("git apply /tmp/patches/sa-ptr-3.patch");
|
|
161
|
-
expect(content.endsWith("\n\nFull transcript: /tmp/sessions/child-3.jsonl")).toBe(true);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("closed + totalRounds — chatMode close with round stats", () => {
|
|
165
|
-
const content = captureNotificationContent({
|
|
166
|
-
id: "sa-rounds-1",
|
|
167
|
-
status: "closed",
|
|
168
|
-
agent: "w",
|
|
169
|
-
totalRounds: 3,
|
|
170
|
-
result: "",
|
|
171
|
-
sessionFile: "/tmp/sessions/child-rounds.jsonl",
|
|
172
|
-
startedAt: 1,
|
|
173
|
-
endedAt: 2,
|
|
174
|
-
});
|
|
175
|
-
expect(content).toBe(
|
|
176
|
-
"Subagent \"w\" (sa-rounds-1) completed after 3 rounds. Result:\n\n\nFull transcript: /tmp/sessions/child-rounds.jsonl",
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("closed no totalRounds — one-shot, no round stats", () => {
|
|
181
|
-
const content = captureNotificationContent({
|
|
182
|
-
id: "sa-rounds-2",
|
|
183
|
-
status: "closed",
|
|
184
|
-
agent: "w",
|
|
185
|
-
result: "done",
|
|
186
|
-
sessionFile: "/tmp/sessions/child-oneshot.jsonl",
|
|
187
|
-
startedAt: 1,
|
|
188
|
-
endedAt: 2,
|
|
189
|
-
});
|
|
190
|
-
expect(content).toBe(
|
|
191
|
-
'Subagent "w" (sa-rounds-2) completed. Result:\ndone\n\nFull transcript: /tmp/sessions/child-oneshot.jsonl',
|
|
192
|
-
);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
describe("notifier golden — batch merge (60s window, two records)", () => {
|
|
197
|
-
it("two closed records joined with separator", () => {
|
|
198
|
-
let capturedContent = "";
|
|
199
|
-
let capturedDetails: unknown;
|
|
200
|
-
const host: NotifierHost = {
|
|
201
|
-
sendMessage: (msg) => {
|
|
202
|
-
capturedContent = (msg as { content: string }).content;
|
|
203
|
-
capturedDetails = (msg as { details: unknown }).details;
|
|
204
|
-
},
|
|
205
|
-
hasRunningBackground: () => true, // true → 合批窗口激活
|
|
206
|
-
isIdle: () => true,
|
|
207
|
-
};
|
|
208
|
-
const notifier = createNotifier(host);
|
|
209
|
-
|
|
210
|
-
// 两条通知入队(不立即 flush——hasRunningBackground=true)
|
|
211
|
-
notifier.notify({
|
|
212
|
-
id: "batch-1", status: "closed", agent: "w1", result: "result1",
|
|
213
|
-
startedAt: 1, endedAt: 2,
|
|
214
|
-
});
|
|
215
|
-
notifier.notify({
|
|
216
|
-
id: "batch-2", status: "closed", agent: "w2", result: "result2",
|
|
217
|
-
startedAt: 3, "endedAt": 4,
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// 手动 flush(模拟窗口到期)
|
|
221
|
-
notifier.flushPendingNotifications();
|
|
222
|
-
|
|
223
|
-
expect(capturedContent).toBe(
|
|
224
|
-
'Subagent "w1" (batch-1) completed. Result:\nresult1\n\n---\n\nSubagent "w2" (batch-2) completed. Result:\nresult2',
|
|
225
|
-
);
|
|
226
|
-
// details 结构(must-fix #13 / #6 锁死):items 元素必须是 record 本体(顶层
|
|
227
|
-
// status/agent/id/result——bg-notify-render 的 extractBgNotifyRecord 按此读取),
|
|
228
|
-
// 拦截内核 items 规则回归(曾错装 payload 导致渲染降级默认样式)。
|
|
229
|
-
const details = capturedDetails as { batch: boolean; items: unknown[] };
|
|
230
|
-
expect(details.batch).toBe(true);
|
|
231
|
-
expect(details.items).toHaveLength(2);
|
|
232
|
-
expect(details.items[0]).toMatchObject({
|
|
233
|
-
id: "batch-1",
|
|
234
|
-
status: "closed",
|
|
235
|
-
agent: "w1",
|
|
236
|
-
result: "result1",
|
|
237
|
-
startedAt: 1,
|
|
238
|
-
endedAt: 2,
|
|
239
|
-
});
|
|
240
|
-
expect(details.items[1]).toMatchObject({
|
|
241
|
-
id: "batch-2",
|
|
242
|
-
status: "closed",
|
|
243
|
-
agent: "w2",
|
|
244
|
-
result: "result2",
|
|
245
|
-
});
|
|
246
|
-
// 端到端渲染锁(#6):合批 details 直接喂 bg-notify-render 必须走批量分支成功渲染
|
|
247
|
-
// (非 undefined 兜底),两条 agent 均可见。
|
|
248
|
-
const { theme } = makeRenderTheme();
|
|
249
|
-
const comp = renderBgNotifyMessage(
|
|
250
|
-
{ details: capturedDetails },
|
|
251
|
-
{ expanded: false },
|
|
252
|
-
theme,
|
|
253
|
-
);
|
|
254
|
-
expect(comp).toBeDefined();
|
|
255
|
-
const joined = comp!.render(80).join("\n");
|
|
256
|
-
expect(joined).toContain("w1");
|
|
257
|
-
expect(joined).toContain("w2");
|
|
258
|
-
|
|
259
|
-
notifier.dispose();
|
|
260
|
-
});
|
|
261
|
-
});
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
// src/execution/__tests__/one-shot-upgrade.test.ts
|
|
2
|
-
//
|
|
3
|
-
// SP-5 one-shot upgrade:非 chatMode active record 收到 message 时自动升级 chatMode。
|
|
4
|
-
//
|
|
5
|
-
// 场景:one-shot subagent(conversation=false)完成首轮后 record 仍在内存(running/idle),
|
|
6
|
-
// LLM 调 message 续聊 → messageHandler 检测非 chatMode + active → 置 chatMode=true(upgrade)→
|
|
7
|
-
// 走 deliverMessage 统一路径(热路径或冷路径 resume)。
|
|
8
|
-
//
|
|
9
|
-
// mock 策略:真实 SubagentService + mock runSpawn(受控),getChildByRecord/spawnedChildren
|
|
10
|
-
// 提供真实 Map 语义(deliverMessage 热路径需要 child 存在)。
|
|
11
|
-
|
|
12
|
-
import * as fs from "node:fs";
|
|
13
|
-
import * as os from "node:os";
|
|
14
|
-
import * as path from "node:path";
|
|
15
|
-
|
|
16
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
17
|
-
|
|
18
|
-
// mock logger
|
|
19
|
-
const { loggerMock } = vi.hoisted(() => ({
|
|
20
|
-
loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() },
|
|
21
|
-
}));
|
|
22
|
-
vi.mock("@zhushanwen/pi-extension-logger", () => ({ getLogger: () => loggerMock }));
|
|
23
|
-
|
|
24
|
-
// mock session-runner:runSpawn 受控 + killAllSpawnedChildren 空实现;
|
|
25
|
-
// getChildByRecord/spawnedChildren 提供真实 Map 语义。
|
|
26
|
-
vi.mock("../session-runner.ts", () => {
|
|
27
|
-
const spawnedChildren = new Map<string, unknown>();
|
|
28
|
-
return {
|
|
29
|
-
runSpawn: vi.fn(),
|
|
30
|
-
killAllSpawnedChildren: vi.fn(),
|
|
31
|
-
spawnedChildren,
|
|
32
|
-
getChildByRecord: (id: string): unknown => spawnedChildren.get(id),
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
import { runSpawn, spawnedChildren } from "../session-runner.ts";
|
|
37
|
-
import type { SessionRunnerContext } from "../session-runner.ts";
|
|
38
|
-
import { createRecord } from "../execution-record.ts";
|
|
39
|
-
import { ModelConfigService } from "../model-config-service.ts";
|
|
40
|
-
import type { ModelInfo } from "../model-resolver.ts";
|
|
41
|
-
import { RecordStore } from "../record-store.ts";
|
|
42
|
-
import { SubagentService } from "../subagent-service.ts";
|
|
43
|
-
import type { PiLike } from "../subagent-service.ts";
|
|
44
|
-
import type { AgentResult, ExecutionRecord } from "../types.ts";
|
|
45
|
-
import { messageHandler } from "../../interface/subagent-actions.ts";
|
|
46
|
-
|
|
47
|
-
const mockRunSpawn = vi.mocked(runSpawn);
|
|
48
|
-
|
|
49
|
-
const STUB_MODEL: ModelInfo = { id: "test-model", name: "Test", provider: "test", reasoning: false };
|
|
50
|
-
|
|
51
|
-
function makeTmpAgentDir(): string {
|
|
52
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), "upgrade-test-"));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function makePi(): PiLike & {
|
|
56
|
-
appendEntry: ReturnType<typeof vi.fn>;
|
|
57
|
-
events: { emit: ReturnType<typeof vi.fn> };
|
|
58
|
-
sendMessage: ReturnType<typeof vi.fn>;
|
|
59
|
-
} {
|
|
60
|
-
return { appendEntry: vi.fn(), events: { emit: vi.fn() }, sendMessage: vi.fn() };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function makeResult(success: boolean): AgentResult {
|
|
64
|
-
return {
|
|
65
|
-
text: success ? "done" : "err",
|
|
66
|
-
turns: 1,
|
|
67
|
-
durationMs: 100,
|
|
68
|
-
success,
|
|
69
|
-
error: success ? undefined : "boom",
|
|
70
|
-
sessionId: "sess-1",
|
|
71
|
-
toolCalls: [],
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
interface ServiceInternals {
|
|
76
|
-
store: RecordStore;
|
|
77
|
-
sessionRootId: string | null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** 非 chatMode background record(one-shot 模式)。 */
|
|
81
|
-
function makeOneShotRecord(
|
|
82
|
-
sessionRootId: string,
|
|
83
|
-
status: "running" = "running",
|
|
84
|
-
id = "sa-oneshot",
|
|
85
|
-
): ExecutionRecord {
|
|
86
|
-
const record = createRecord(id, {
|
|
87
|
-
agent: "general-purpose",
|
|
88
|
-
model: "test/test-model",
|
|
89
|
-
thinkingLevel: "low",
|
|
90
|
-
mode: "background",
|
|
91
|
-
task: "one-shot task",
|
|
92
|
-
slug: "oneshot",
|
|
93
|
-
startedAt: 1000,
|
|
94
|
-
rootSessionId: sessionRootId,
|
|
95
|
-
// chatMode 不传 = undefined = 非 chatMode(one-shot)
|
|
96
|
-
});
|
|
97
|
-
record.status = status;
|
|
98
|
-
record.controller = new AbortController();
|
|
99
|
-
// v4 B-1:one-shot 完成后 record 为 running(idle 折入 running)+ isResumable(无活进程)。
|
|
100
|
-
// sessionFile 总设(resumeRound 冷路径校验需要,热路径无害)。
|
|
101
|
-
record.sessionFile = "/tmp/fake-session.jsonl";
|
|
102
|
-
record.round = 1;
|
|
103
|
-
return record;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// ============================================================
|
|
107
|
-
// SP-5 one-shot upgrade
|
|
108
|
-
// ============================================================
|
|
109
|
-
|
|
110
|
-
describe("SP-5 one-shot upgrade(message → chatMode + 冷 resume)", () => {
|
|
111
|
-
let agentDir: string;
|
|
112
|
-
let service: SubagentService;
|
|
113
|
-
let store: RecordStore;
|
|
114
|
-
let sessionRootId: string;
|
|
115
|
-
|
|
116
|
-
beforeEach(() => {
|
|
117
|
-
agentDir = makeTmpAgentDir();
|
|
118
|
-
const modelService = new ModelConfigService({ agentDir });
|
|
119
|
-
service = new SubagentService({ cwd: agentDir, modelService });
|
|
120
|
-
service.initSession({ pi: makePi(), sessionId: "root-session" });
|
|
121
|
-
const internals = service as unknown as ServiceInternals;
|
|
122
|
-
store = internals.store;
|
|
123
|
-
sessionRootId = internals.sessionRootId!;
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
afterEach(() => {
|
|
127
|
-
service.dispose();
|
|
128
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// TC-1: one-shot done 后 message 触发 upgrade(chatMode=true)
|
|
132
|
-
// 场景:one-shot running record → message → chatMode 被置 true → deliverMessage 热路径(child 存在)
|
|
133
|
-
it("TC-1: one-shot running record 收到 message → chatMode 升级为 true", async () => {
|
|
134
|
-
const record = makeOneShotRecord(sessionRootId, "running");
|
|
135
|
-
store.register(record);
|
|
136
|
-
|
|
137
|
-
// 注册 mock child 让 deliverMessage 走热路径
|
|
138
|
-
const mockChild = { killed: false, pid: 12345, stdin: { write: vi.fn(() => true) } };
|
|
139
|
-
spawnedChildren.set(record.id, mockChild as unknown);
|
|
140
|
-
|
|
141
|
-
// 验证升级前 chatMode 为 falsy
|
|
142
|
-
expect(record.chatMode).toBeFalsy();
|
|
143
|
-
|
|
144
|
-
await messageHandler(service, { subagentId: record.id, text: "follow-up" });
|
|
145
|
-
|
|
146
|
-
// 验证 chatMode 被升级
|
|
147
|
-
expect(record.chatMode).toBe(true);
|
|
148
|
-
// 验证 record 仍为 running(deliverMessage 热路径设 running)
|
|
149
|
-
expect(record.status).toBe("running");
|
|
150
|
-
// 验证 record 在内存中(非终态,未被 archive)
|
|
151
|
-
expect(store.getMutable(record.id)).toBeDefined();
|
|
152
|
-
|
|
153
|
-
// 清理
|
|
154
|
-
spawnedChildren.delete(record.id);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// TC-2: upgrade 后走冷路径 resume
|
|
158
|
-
// 场景:one-shot idle record(进程已死)→ message → chatMode=true → deliverMessage 冷路径 → resumeRound
|
|
159
|
-
it("TC-2: one-shot idle record 收到 message → chatMode 升级 + 冷路径 resume", async () => {
|
|
160
|
-
// one-shot 完成态 record(running + isResumable)需要 sessionFile(resumeRound 冷路径校验)
|
|
161
|
-
fs.writeFileSync("/tmp/fake-session.jsonl", "");
|
|
162
|
-
const record = makeOneShotRecord(sessionRootId);
|
|
163
|
-
store.register(record);
|
|
164
|
-
|
|
165
|
-
// mock runSpawn 返回成功(resume 后的 spawn)
|
|
166
|
-
mockRunSpawn.mockImplementation(async (_ctx: SessionRunnerContext) => {
|
|
167
|
-
return makeResult(true);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
expect(record.chatMode).toBeFalsy();
|
|
171
|
-
|
|
172
|
-
await messageHandler(service, { subagentId: record.id, text: "resume message" });
|
|
173
|
-
|
|
174
|
-
// 验证 chatMode 被升级
|
|
175
|
-
expect(record.chatMode).toBe(true);
|
|
176
|
-
// 验证走了 resumeRound → runSpawn(冷路径)
|
|
177
|
-
expect(mockRunSpawn).toHaveBeenCalled();
|
|
178
|
-
// record 被 resumeRound 设为 running,runAndFinalize 完成后保持 running(v4 B-1 idle 折入 running)
|
|
179
|
-
// 关键是 record 不在终态
|
|
180
|
-
expect(record.status).not.toBe("closed");
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// TC-3: upgrade 后 record 可续聊(非终态)
|
|
184
|
-
// 场景:验证升级后的 record 不会被 finalize 为终态,仍可接受后续 message
|
|
185
|
-
it("TC-3: upgrade 后 record 可续聊(非终态,仍在内存)", async () => {
|
|
186
|
-
const record = makeOneShotRecord(sessionRootId);
|
|
187
|
-
store.register(record);
|
|
188
|
-
|
|
189
|
-
// runSpawn 返回成功
|
|
190
|
-
mockRunSpawn.mockImplementation(async () => makeResult(true));
|
|
191
|
-
|
|
192
|
-
await messageHandler(service, { subagentId: record.id, text: "first resume" });
|
|
193
|
-
|
|
194
|
-
// chatMode 已升级
|
|
195
|
-
expect(record.chatMode).toBe(true);
|
|
196
|
-
|
|
197
|
-
// 关键验证:record 仍在内存(getMutable 返回非 undefined)——未被 archive 到终态
|
|
198
|
-
const mutable = store.getMutable(record.id);
|
|
199
|
-
expect(mutable).toBeDefined();
|
|
200
|
-
expect(mutable!.chatMode).toBe(true);
|
|
201
|
-
|
|
202
|
-
// 验证 status 不是终态(closed 是终态;running 是非终态,v4 B-1 idle 折入 running)
|
|
203
|
-
expect(record.status).toBe("running");
|
|
204
|
-
});
|
|
205
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// src/__tests__/output-collector.test.ts
|
|
2
|
-
//
|
|
3
|
-
// 锁定 extractParsedOutput 纯函数契约——它被 collectResult 直接消费,决定
|
|
4
|
-
// AgentResult.parsedOutput 字段。toUsageTotal / collectResponseText 已删除
|
|
5
|
-
// (usage 收口进 getTotalUsage,text 收口进 getFullText,均在 execution-record.test 测)。
|
|
6
|
-
import { describe, expect, it } from "vitest";
|
|
7
|
-
|
|
8
|
-
import { extractParsedOutput } from "../output-collector.ts";
|
|
9
|
-
import type { ToolCall } from "../types.ts";
|
|
10
|
-
|
|
11
|
-
// ============================================================
|
|
12
|
-
// extractParsedOutput
|
|
13
|
-
// ============================================================
|
|
14
|
-
|
|
15
|
-
describe("extractParsedOutput", () => {
|
|
16
|
-
it("returns undefined for empty toolCalls", () => {
|
|
17
|
-
expect(extractParsedOutput([])).toBeUndefined();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("returns undefined when no structured-output call exists", () => {
|
|
21
|
-
const calls: ToolCall[] = [
|
|
22
|
-
{ toolName: "bash", result: { details: "x" } },
|
|
23
|
-
{ toolName: "read", result: { details: "y" } },
|
|
24
|
-
];
|
|
25
|
-
expect(extractParsedOutput(calls)).toBeUndefined();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("returns undefined when structured-output has no result.details", () => {
|
|
29
|
-
const calls: ToolCall[] = [
|
|
30
|
-
{ toolName: "structured-output", result: { content: [] } },
|
|
31
|
-
{ toolName: "structured-output", result: {} },
|
|
32
|
-
{ toolName: "structured-output" },
|
|
33
|
-
];
|
|
34
|
-
expect(extractParsedOutput(calls)).toBeUndefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("returns details when exactly one structured-output call has details", () => {
|
|
38
|
-
const calls: ToolCall[] = [
|
|
39
|
-
{ toolName: "bash" },
|
|
40
|
-
{ toolName: "structured-output", result: { details: { answer: 42 } } },
|
|
41
|
-
];
|
|
42
|
-
expect(extractParsedOutput(calls)).toEqual({ answer: 42 });
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("returns the LAST structured-output details (reverse iteration)", () => {
|
|
46
|
-
const calls: ToolCall[] = [
|
|
47
|
-
{ toolName: "structured-output", result: { details: "first" } },
|
|
48
|
-
{ toolName: "bash" },
|
|
49
|
-
{ toolName: "structured-output", result: { details: "second" } },
|
|
50
|
-
];
|
|
51
|
-
expect(extractParsedOutput(calls)).toBe("second");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("ignores isError structured-output calls without details, picks one with details", () => {
|
|
55
|
-
const calls: ToolCall[] = [
|
|
56
|
-
{ toolName: "structured-output", isError: true, result: { content: [{ type: "text", text: "bad" }] } },
|
|
57
|
-
{ toolName: "structured-output", result: { details: { ok: true } } },
|
|
58
|
-
];
|
|
59
|
-
expect(extractParsedOutput(calls)).toEqual({ ok: true });
|
|
60
|
-
});
|
|
61
|
-
});
|