@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,408 +0,0 @@
|
|
|
1
|
-
// 测试框架:vitest
|
|
2
|
-
// 运行命令:npx vitest run src/orchestration/models/__tests__/trace.test.ts
|
|
3
|
-
//
|
|
4
|
-
// Trace 首个直接单测(W1TC1-W1TC12,.cw/swf-perf-impl/rt-w1-design.json):
|
|
5
|
-
// - W1TC1-3/9:byIndex 倒排索引一致性与 no-op 防御语义
|
|
6
|
-
// - W1TC4-8:result.content 裁剪(append/update 入口、8000 边界、patch 缺省、fromArray 原样保留)
|
|
7
|
-
// - W1TC10-11:集成——executeAgentCall 真链路 / jsonl save-load round-trip
|
|
8
|
-
// - W1TC12:重复 stepIndex 违规语义锚定(last-wins + remove 后 desync 孤儿)
|
|
9
|
-
|
|
10
|
-
import * as fs from "node:fs";
|
|
11
|
-
import * as os from "node:os";
|
|
12
|
-
import * as path from "node:path";
|
|
13
|
-
|
|
14
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
15
|
-
|
|
16
|
-
import { TRACE_RESULT_MAX_CHARS, Trace } from "../trace.js";
|
|
17
|
-
import { AgentCall } from "../agent-call.js";
|
|
18
|
-
import { Budget } from "../budget.js";
|
|
19
|
-
import { WorkflowRun } from "../workflow-run.js";
|
|
20
|
-
import type { ExecutionTraceNode, AgentResult, RunSpec } from "../types.js";
|
|
21
|
-
import type { AgentRunner } from "../ports.js";
|
|
22
|
-
import { executeAgentCall } from "../../execute-agent-call.js";
|
|
23
|
-
import { JsonlRunStore } from "../../jsonl-run-store.js";
|
|
24
|
-
|
|
25
|
-
// ── 测试辅助 ─────────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
/** 构造最小 ExecutionTraceNode(status 默认 pending,无 result)。 */
|
|
28
|
-
function makeTraceNode(stepIndex: number): ExecutionTraceNode {
|
|
29
|
-
return {
|
|
30
|
-
stepIndex,
|
|
31
|
-
agent: "test-agent",
|
|
32
|
-
task: "test task",
|
|
33
|
-
model: "default",
|
|
34
|
-
status: "pending",
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** 构造指定长度 content 的 AgentResult(sessionId 等可选字段透传)。 */
|
|
39
|
-
function makeResult(content: string, extras: Partial<AgentResult> = {}): AgentResult {
|
|
40
|
-
return { content, ...extras };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ── byIndex 索引一致性 ────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
describe("Trace byIndex 索引一致性", () => {
|
|
46
|
-
it("W1TC1: append/update/remove 后 find 命中且引用共享", () => {
|
|
47
|
-
const trace = new Trace();
|
|
48
|
-
const node0 = makeTraceNode(0);
|
|
49
|
-
const node1 = makeTraceNode(1);
|
|
50
|
-
const node2 = makeTraceNode(2);
|
|
51
|
-
trace.append(node0);
|
|
52
|
-
trace.append(node1);
|
|
53
|
-
trace.append(node2);
|
|
54
|
-
|
|
55
|
-
trace.update(1, { status: "completed" });
|
|
56
|
-
|
|
57
|
-
// byIndex 与 nodes 引用共享非拷贝
|
|
58
|
-
expect(trace.find(0)).toBe(node0);
|
|
59
|
-
expect(trace.find(1)).toBe(node1);
|
|
60
|
-
expect(trace.find(2)).toBe(node2);
|
|
61
|
-
// Map 值是节点引用,update 字段 mutate 可见
|
|
62
|
-
expect(trace.find(1)!.status).toBe("completed");
|
|
63
|
-
|
|
64
|
-
trace.removeByStepIndex(1);
|
|
65
|
-
expect(trace.find(1)).toBeUndefined();
|
|
66
|
-
expect(trace.length).toBe(2);
|
|
67
|
-
// 其余节点不受影响
|
|
68
|
-
expect(trace.find(0)).toBe(node0);
|
|
69
|
-
expect(trace.find(2)).toBe(node2);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// ── remove 后 re-append 同 stepIndex 覆盖 ─────────────────────
|
|
74
|
-
|
|
75
|
-
describe("Trace remove 后 re-append 同 stepIndex", () => {
|
|
76
|
-
it("W1TC2: rebuild discard 清理后重跑重发同 callId——旧节点不再可达", () => {
|
|
77
|
-
const trace = new Trace();
|
|
78
|
-
const nodeA = makeTraceNode(0);
|
|
79
|
-
trace.append(nodeA);
|
|
80
|
-
trace.removeByStepIndex(0);
|
|
81
|
-
|
|
82
|
-
const nodeB = makeTraceNode(0);
|
|
83
|
-
trace.append(nodeB);
|
|
84
|
-
|
|
85
|
-
expect(trace.find(0)).toBe(nodeB);
|
|
86
|
-
expect(trace.toArray()).toHaveLength(1);
|
|
87
|
-
expect(trace.toArray()[0]).toBe(nodeB);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// ── 重复 stepIndex 违规语义锚定(W1TC12)──────────────────────
|
|
92
|
-
|
|
93
|
-
describe("Trace 重复 stepIndex 违规语义锚定(W1TC12)", () => {
|
|
94
|
-
it("W1TC12: 重复 append find last-wins;重复 append 后 remove 呈 desync 孤儿", () => {
|
|
95
|
-
// ① 重复 append 同 stepIndex 且未 remove:find 返回第二个节点(Map
|
|
96
|
-
// last-wins;旧线性扫 first-match 会返回第一个)
|
|
97
|
-
const t1 = new Trace();
|
|
98
|
-
const first = makeTraceNode(0);
|
|
99
|
-
const second = makeTraceNode(0);
|
|
100
|
-
t1.append(first);
|
|
101
|
-
t1.append(second);
|
|
102
|
-
expect(t1.find(0)).toBe(second);
|
|
103
|
-
expect(t1.length).toBe(2);
|
|
104
|
-
|
|
105
|
-
// ② 重复 append 后 removeByStepIndex:findIndex 命中首个旧节点 splice、
|
|
106
|
-
// byIndex.delete 删掉整个键——第二个节点残留为孤儿(find 不可达但
|
|
107
|
-
// nodes.length=1)。desync 行为锚定:防未来改回线性扫时静默漂移
|
|
108
|
-
// (线性扫实现下 find(0) 会命中残留节点 second,本断言即失败)
|
|
109
|
-
const t2 = new Trace();
|
|
110
|
-
const a = makeTraceNode(0);
|
|
111
|
-
const b = makeTraceNode(0);
|
|
112
|
-
t2.append(a);
|
|
113
|
-
t2.append(b);
|
|
114
|
-
t2.removeByStepIndex(0);
|
|
115
|
-
expect(t2.find(0)).toBeUndefined();
|
|
116
|
-
expect(t2.length).toBe(1);
|
|
117
|
-
expect(t2.toArray()[0]).toBe(b);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// ── fromArray 重建 ───────────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
describe("Trace.fromArray 重建", () => {
|
|
124
|
-
it("W1TC3: 索引全命中 + 防御性拷贝语义保持", () => {
|
|
125
|
-
const node0 = makeTraceNode(0);
|
|
126
|
-
const node1 = makeTraceNode(1);
|
|
127
|
-
const node2 = makeTraceNode(2);
|
|
128
|
-
const src = [node0, node1, node2];
|
|
129
|
-
|
|
130
|
-
const trace = Trace.fromArray(src);
|
|
131
|
-
|
|
132
|
-
expect(trace.length).toBe(3);
|
|
133
|
-
// fromArray push {...node} 副本——find(i) 与 toArray()[i] 同引用(byIndex 命中副本)
|
|
134
|
-
for (let i = 0; i < 3; i++) {
|
|
135
|
-
expect(trace.find(i)).toBe(trace.toArray()[i]);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 传入数组后续 mutate 不影响 trace(浅拷贝语义保持)
|
|
139
|
-
src.push(makeTraceNode(3));
|
|
140
|
-
expect(trace.length).toBe(3);
|
|
141
|
-
src[0]!.status = "failed";
|
|
142
|
-
expect(trace.find(0)!.status).toBe("pending");
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// ── append 入口裁剪 ──────────────────────────────────────────
|
|
147
|
-
|
|
148
|
-
describe("Trace append 入口超长 result 裁剪", () => {
|
|
149
|
-
it("W1TC4: 标记含原始长度,节点引用不变,其余字段浅拷贝保留", () => {
|
|
150
|
-
const content = "x".repeat(10000);
|
|
151
|
-
const node = {
|
|
152
|
-
...makeTraceNode(0),
|
|
153
|
-
result: makeResult(content, { sessionId: "s1", parsedOutput: { a: 1 } }),
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const trace = new Trace();
|
|
157
|
-
trace.append(node);
|
|
158
|
-
|
|
159
|
-
const stored = trace.toArray()[0]!;
|
|
160
|
-
// 节点对象引用不变(mutate result 字段,不 push 副本——D-10 traceNode 引用共享)
|
|
161
|
-
expect(stored).toBe(node);
|
|
162
|
-
|
|
163
|
-
const marker = `\n…[trace result truncated, original 10000 chars]…\n`;
|
|
164
|
-
expect(stored.result!.content).toBe(content.slice(0, 4000) + marker + content.slice(-4000));
|
|
165
|
-
// 头尾与标记子串
|
|
166
|
-
expect(stored.result!.content.startsWith(content.slice(0, 4000))).toBe(true);
|
|
167
|
-
expect(stored.result!.content.endsWith(content.slice(-4000))).toBe(true);
|
|
168
|
-
expect(stored.result!.content).toContain("[trace result truncated, original 10000 chars]");
|
|
169
|
-
// result 其余字段浅拷贝保留
|
|
170
|
-
expect(stored.result!.sessionId).toBe("s1");
|
|
171
|
-
expect(stored.result!.parsedOutput).toEqual({ a: 1 });
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// ── update 入口裁剪 ──────────────────────────────────────────
|
|
176
|
-
|
|
177
|
-
describe("Trace update patch.result 超长裁剪", () => {
|
|
178
|
-
it("W1TC5: 节点持裁剪副本,patch 原对象不被污染(call.result 保真)", () => {
|
|
179
|
-
const trace = new Trace();
|
|
180
|
-
trace.append(makeTraceNode(0));
|
|
181
|
-
|
|
182
|
-
const full = makeResult("y".repeat(9000), { sessionId: "s1" });
|
|
183
|
-
trace.update(0, { result: full });
|
|
184
|
-
|
|
185
|
-
const stored = trace.find(0)!.result!;
|
|
186
|
-
const marker = `\n…[trace result truncated, original 9000 chars]…\n`;
|
|
187
|
-
expect(stored.content.length).toBe(4000 + marker.length + 4000);
|
|
188
|
-
expect(stored.content).toContain("original 9000 chars");
|
|
189
|
-
// 节点持裁剪副本新对象,与 patch.result 脱钩
|
|
190
|
-
expect(stored).not.toBe(full);
|
|
191
|
-
// 浅拷贝保留其他字段
|
|
192
|
-
expect(stored.sessionId).toBe("s1");
|
|
193
|
-
// AgentCall.result 保真——原对象不被污染(replay 数据源)
|
|
194
|
-
expect(full.content).toHaveLength(9000);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// ── 边界:恰好 8000 不裁 / 8001 裁 ────────────────────────────
|
|
199
|
-
|
|
200
|
-
describe("Trace 裁剪边界(严格大于 TRACE_RESULT_MAX_CHARS)", () => {
|
|
201
|
-
it("W1TC6: 恰好 8000 引用透传零拷贝;8001 触发裁剪", () => {
|
|
202
|
-
const trace = new Trace();
|
|
203
|
-
trace.append(makeTraceNode(0));
|
|
204
|
-
trace.append(makeTraceNode(1));
|
|
205
|
-
|
|
206
|
-
// 恰好 8000:不裁
|
|
207
|
-
const exact = makeResult("a".repeat(TRACE_RESULT_MAX_CHARS));
|
|
208
|
-
trace.update(0, { result: exact });
|
|
209
|
-
expect(trace.find(0)!.result).toBe(exact);
|
|
210
|
-
expect(trace.find(0)!.result!.content).toHaveLength(8000);
|
|
211
|
-
expect(trace.find(0)!.result!.content).not.toContain("truncated");
|
|
212
|
-
|
|
213
|
-
// 8001:裁剪(head/tail 固定 4000 比例,重叠段属预期)
|
|
214
|
-
const over = makeResult("b".repeat(TRACE_RESULT_MAX_CHARS + 1));
|
|
215
|
-
trace.update(1, { result: over });
|
|
216
|
-
expect(trace.find(1)!.result!.content).toContain("original 8001 chars");
|
|
217
|
-
expect(trace.find(1)!.result!.content).not.toBe(over.content);
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// ── patch.result 未提供不触发裁剪 ────────────────────────────
|
|
222
|
-
|
|
223
|
-
describe("Trace update patch.result 缺省", () => {
|
|
224
|
-
it("W1TC7: 只改 status/completedAt 等字段——已持 result 不被触碰", () => {
|
|
225
|
-
const origResult = makeResult("keep");
|
|
226
|
-
const node = { ...makeTraceNode(0), result: origResult };
|
|
227
|
-
const trace = new Trace();
|
|
228
|
-
trace.append(node);
|
|
229
|
-
|
|
230
|
-
trace.update(0, { status: "completed", completedAt: "2026-08-15T00:00:00Z" });
|
|
231
|
-
expect(trace.find(0)!.result!.content).toBe("keep");
|
|
232
|
-
expect(trace.find(0)!.result).toBe(origResult);
|
|
233
|
-
|
|
234
|
-
// 其他字段路径同样不影响 result
|
|
235
|
-
trace.update(0, { sessionId: "s9" });
|
|
236
|
-
expect(trace.find(0)!.result).toBe(origResult);
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// ── fromArray 原样保留(read 路径不二次裁剪)──────────────────
|
|
241
|
-
|
|
242
|
-
describe("Trace.fromArray 原样保留", () => {
|
|
243
|
-
it("W1TC8: 超长 content 不被二次裁剪;已含标记的形态逐字节不变", () => {
|
|
244
|
-
// 旧版本未裁剪的快照重水合:20000 字符全长保留
|
|
245
|
-
const long = {
|
|
246
|
-
...makeTraceNode(0),
|
|
247
|
-
result: makeResult("z".repeat(20000)),
|
|
248
|
-
};
|
|
249
|
-
const trace = Trace.fromArray([long]);
|
|
250
|
-
expect(trace.find(0)!.result!.content).toHaveLength(20000);
|
|
251
|
-
expect(trace.find(0)!.result!.content).not.toContain("truncated");
|
|
252
|
-
|
|
253
|
-
// 新快照 round-trip:已裁剪含标记的 content 经 fromArray 后逐字节不变(无标记嵌套)
|
|
254
|
-
const marker = `\n…[trace result truncated, original 10000 chars]…\n`;
|
|
255
|
-
const trimmed = "x".repeat(4000) + marker + "x".repeat(4000);
|
|
256
|
-
const trace2 = Trace.fromArray([{ ...makeTraceNode(0), result: makeResult(trimmed) }]);
|
|
257
|
-
expect(trace2.find(0)!.result!.content).toBe(trimmed);
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// ── no-op 防御语义回归 ───────────────────────────────────────
|
|
262
|
-
|
|
263
|
-
describe("Trace no-op 防御语义", () => {
|
|
264
|
-
it("W1TC9: update/remove 不存在的 stepIndex 不抛错、状态不变", () => {
|
|
265
|
-
const trace = new Trace();
|
|
266
|
-
trace.append(makeTraceNode(0));
|
|
267
|
-
|
|
268
|
-
expect(() => trace.update(999, { status: "completed" })).not.toThrow();
|
|
269
|
-
expect(() => trace.removeByStepIndex(999)).not.toThrow();
|
|
270
|
-
expect(trace.length).toBe(1);
|
|
271
|
-
expect(trace.find(999)).toBeUndefined();
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
// ── 集成:executeAgentCall 真链路(W1TC10)────────────────────
|
|
276
|
-
|
|
277
|
-
describe("W1TC10: executeAgentCall 真链路——call.result 全量、trace 节点持裁剪副本", () => {
|
|
278
|
-
it("W1TC10: runner.run 返回 12000 字符 → 两形态并存(AgentCall.result 不裁)", async () => {
|
|
279
|
-
const content = "w".repeat(12000);
|
|
280
|
-
const runner: AgentRunner = {
|
|
281
|
-
run: vi.fn().mockResolvedValue(makeResult(content, { sessionId: "s1" })),
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const trace = new Trace();
|
|
285
|
-
const traceNode = makeTraceNode(0);
|
|
286
|
-
trace.append(traceNode);
|
|
287
|
-
const call = new AgentCall(0, { prompt: "test task", agent: "worker" }, traceNode);
|
|
288
|
-
|
|
289
|
-
await executeAgentCall(call, runner, new Budget(), new AbortController().signal, trace);
|
|
290
|
-
|
|
291
|
-
// AgentCall.result 全量(worker cached replay 数据源保真)
|
|
292
|
-
expect(call.result!.content).toHaveLength(12000);
|
|
293
|
-
// trace 节点持裁剪副本
|
|
294
|
-
const stored = trace.find(0)!.result!;
|
|
295
|
-
const marker = `\n…[trace result truncated, original 12000 chars]…\n`;
|
|
296
|
-
expect(stored.content).toBe(content.slice(0, 4000) + marker + content.slice(-4000));
|
|
297
|
-
expect(stored.content.length).toBe(4000 + marker.length + 4000);
|
|
298
|
-
expect(stored.sessionId).toBe("s1");
|
|
299
|
-
expect(trace.find(0)!.status).toBe("completed");
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
// ── 集成:jsonl save/load round-trip(W1TC11)─────────────────
|
|
304
|
-
|
|
305
|
-
describe("W1TC11: jsonl save/load round-trip——落盘裁剪形态 + 重水合不二次裁", () => {
|
|
306
|
-
let tmpDir: string;
|
|
307
|
-
|
|
308
|
-
beforeEach(() => {
|
|
309
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wf-trace-trim-test-"));
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
afterEach(() => {
|
|
313
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
function makeSpec(): RunSpec {
|
|
317
|
-
return {
|
|
318
|
-
scriptSource: "module.exports = async () => {};",
|
|
319
|
-
args: {},
|
|
320
|
-
scriptName: "test-script",
|
|
321
|
-
scriptPath: "/tmp/test.js",
|
|
322
|
-
description: "test",
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** 构造含 10000 字符 result 已完成 call 的 WorkflowRun(模式对齐 jsonl-run-store-session-file.test.ts)。 */
|
|
327
|
-
function makeRunWithDoneCall(): WorkflowRun {
|
|
328
|
-
const trace = new Trace();
|
|
329
|
-
const node = makeTraceNode(0);
|
|
330
|
-
trace.append(node);
|
|
331
|
-
const call = new AgentCall(0, { prompt: "task", agent: "worker" }, node);
|
|
332
|
-
call.markRunning();
|
|
333
|
-
// markDone 存全量 result(AgentCall.result 不裁)
|
|
334
|
-
call.markDone(makeResult("q".repeat(10000), { sessionId: "session-abc" }));
|
|
335
|
-
call.setSessionId("session-abc");
|
|
336
|
-
// trace.update 经入口裁剪——节点持裁剪副本
|
|
337
|
-
trace.update(0, {
|
|
338
|
-
status: "completed",
|
|
339
|
-
result: call.result,
|
|
340
|
-
completedAt: new Date().toISOString(),
|
|
341
|
-
sessionId: "session-abc",
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
return new WorkflowRun(
|
|
345
|
-
"run-trim-001",
|
|
346
|
-
makeSpec(),
|
|
347
|
-
{
|
|
348
|
-
status: "done",
|
|
349
|
-
reason: "completed",
|
|
350
|
-
budget: new Budget(),
|
|
351
|
-
calls: new Map([[0, call]]),
|
|
352
|
-
trace,
|
|
353
|
-
errorLogs: [],
|
|
354
|
-
},
|
|
355
|
-
{ startedAt: new Date().toISOString(), completedAt: new Date().toISOString() },
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
it("W1TC11: save 落盘 trace 裁剪 + calls[].result 全量;loadAll 回读逐字节一致", async () => {
|
|
360
|
-
const entries: Array<{ type: string; customType?: string; data?: unknown }> = [];
|
|
361
|
-
const mockPi = {
|
|
362
|
-
appendEntry: vi.fn((type: string, data: unknown) => {
|
|
363
|
-
entries.push({ type: "custom", customType: type, data });
|
|
364
|
-
}),
|
|
365
|
-
};
|
|
366
|
-
const mockCtx = {
|
|
367
|
-
sessionManager: { getEntries: () => entries },
|
|
368
|
-
};
|
|
369
|
-
const store = new JsonlRunStore({
|
|
370
|
-
sessionDir: tmpDir,
|
|
371
|
-
pi: mockPi as never,
|
|
372
|
-
ctx: mockCtx as never,
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
const run = makeRunWithDoneCall();
|
|
376
|
-
await store.save(run);
|
|
377
|
-
|
|
378
|
-
// 读磁盘快照验证落盘形态
|
|
379
|
-
const raw = fs.readFileSync(
|
|
380
|
-
path.join(tmpDir, "workflow-state", "run-trim-001.jsonl"),
|
|
381
|
-
"utf8",
|
|
382
|
-
);
|
|
383
|
-
const snapshot = JSON.parse(raw.trim()) as {
|
|
384
|
-
state: {
|
|
385
|
-
trace: Array<{ result?: { content: string } }>;
|
|
386
|
-
calls: Array<{ result?: { content: string }; traceNode: { result?: { content: string } } }>;
|
|
387
|
-
};
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
// trace 投影瘦身生效:trace[0] 含标记
|
|
391
|
-
expect(snapshot.state.trace[0]!.result!.content).toContain(
|
|
392
|
-
"[trace result truncated, original 10000 chars]",
|
|
393
|
-
);
|
|
394
|
-
// calls[].traceNode 是同一裁剪后节点引用(剥 live 后的 rest),亦含标记
|
|
395
|
-
expect(snapshot.state.calls[0]!.traceNode.result!.content).toContain(
|
|
396
|
-
"[trace result truncated, original 10000 chars]",
|
|
397
|
-
);
|
|
398
|
-
// 顶层 calls[].result 字段不裁——全量 10000
|
|
399
|
-
expect(snapshot.state.calls[0]!.result!.content).toHaveLength(10000);
|
|
400
|
-
|
|
401
|
-
// loadAll 回读:fromArray 不再裁,与落盘形态逐字节一致(无标记嵌套)
|
|
402
|
-
const loaded = await store.loadAll();
|
|
403
|
-
expect(loaded).toHaveLength(1);
|
|
404
|
-
const restoredContent = loaded[0]!.state.trace.toArray()[0]!.result!.content;
|
|
405
|
-
expect(restoredContent).toBe(snapshot.state.trace[0]!.result!.content);
|
|
406
|
-
expect(restoredContent.match(/truncated/g)).toHaveLength(1);
|
|
407
|
-
});
|
|
408
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workflow Extension — AgentCall 实体
|
|
3
|
-
*
|
|
4
|
-
* 单次 agent 调用的数据 + 不变式守卫(D-12)。纯数据,无 execute 上帝方法——
|
|
5
|
-
* 执行编排(重试+预算+stale 检测)在 execute-agent-call.ts 的 free function。
|
|
6
|
-
*
|
|
7
|
-
* - 状态机:pending → running → done(不可逆)
|
|
8
|
-
* - markRunning 进入 running 并 attempts++(每次 retry 前调用)
|
|
9
|
-
* - markDone(result) 进入 done 并记录结果
|
|
10
|
-
* - traceNode 持有引用,但 AgentCall 不直接改其字段——trace 同步由
|
|
11
|
-
* Trace.update 负责(D-10 单一来源),AgentCall 只持有引用供 executeAgentCall 读取
|
|
12
|
-
*
|
|
13
|
-
* 层归属:Engine。
|
|
14
|
-
*
|
|
15
|
-
* 参考:domain-models.md §5(字段/不变式/设计决策)。
|
|
16
|
-
*/
|
|
17
|
-
import type { AgentCallOpts, AgentResult, ExecutionTraceNode } from "./types.ts";
|
|
18
|
-
|
|
19
|
-
/** AgentCall 生命周期状态。pending→running→done,不可逆。 */
|
|
20
|
-
export type AgentCallStatus = "pending" | "running" | "done";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* AgentCall 实体(在 RunState.calls Map 内)。
|
|
24
|
-
*
|
|
25
|
-
* 不变式:
|
|
26
|
-
* - status 转换严格 pending→running→done,反向抛错
|
|
27
|
-
* - done 时 result 必须已设置(markDone(result) 前置保证)
|
|
28
|
-
* - attempts 反映 dispatch 次数(markRunning 累加,含首次)
|
|
29
|
-
* - **无 execute 方法**(D-12:执行编排由 Engine executeAgentCall 函数承担)
|
|
30
|
-
*/
|
|
31
|
-
export class AgentCall {
|
|
32
|
-
readonly id: number;
|
|
33
|
-
readonly opts: AgentCallOpts;
|
|
34
|
-
status: AgentCallStatus = "pending";
|
|
35
|
-
attempts = 0;
|
|
36
|
-
result?: AgentResult;
|
|
37
|
-
/** Pi subprocess session ID(uuidv7,G-017 归此)。 */
|
|
38
|
-
sessionId?: string;
|
|
39
|
-
/** Session JSONL 绝对路径(finalizeCall 后从 result.sessionFile 填入,对齐 sessionId 模式)。 */
|
|
40
|
-
sessionFile?: string;
|
|
41
|
-
/** 与 Trace 共享的节点引用(D-10 单源)。AgentCall 不直接改其字段。 */
|
|
42
|
-
readonly traceNode: ExecutionTraceNode;
|
|
43
|
-
|
|
44
|
-
constructor(id: number, opts: AgentCallOpts, traceNode: ExecutionTraceNode) {
|
|
45
|
-
this.id = id;
|
|
46
|
-
this.opts = opts;
|
|
47
|
-
this.traceNode = traceNode;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 标记进入 running 状态(dispatch 前)。attempts++(含首次)。
|
|
52
|
-
* @throws 若已 done(不可重启)
|
|
53
|
-
*/
|
|
54
|
-
markRunning(): void {
|
|
55
|
-
if (this.status === "done") {
|
|
56
|
-
throw new Error(`AgentCall ${this.id} already done — cannot mark running`);
|
|
57
|
-
}
|
|
58
|
-
this.status = "running";
|
|
59
|
-
this.attempts += 1;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 标记完成(成功或失败均调用——result.error 区分)。
|
|
64
|
-
* @throws 若当前非 running(pending 不能直接跳 done,必须先 markRunning)
|
|
65
|
-
*/
|
|
66
|
-
markDone(result: AgentResult): void {
|
|
67
|
-
if (this.status !== "running") {
|
|
68
|
-
throw new Error(`AgentCall ${this.id} must be running to mark done (was ${this.status})`);
|
|
69
|
-
}
|
|
70
|
-
this.result = result;
|
|
71
|
-
this.status = "done";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** 记录 pi subprocess session ID(dispatch 成功后)。 */
|
|
75
|
-
setSessionId(sessionId: string): void {
|
|
76
|
-
this.sessionId = sessionId;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** 记录 session JSONL 绝对路径(finalizeCall 后,对齐 setSessionId 模式)。 */
|
|
80
|
-
setSessionFile(sessionFile: string): void {
|
|
81
|
-
this.sessionFile = sessionFile;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workflow Extension — Budget 值对象
|
|
3
|
-
*
|
|
4
|
-
* Token / cost 预算值对象(D-12)。纯数据 + 不变式守卫,无副作用。
|
|
5
|
-
*
|
|
6
|
-
* 设计:
|
|
7
|
-
* - 无 onConsume 回调(值对象不应持可变回调)。
|
|
8
|
-
* - soft limit 通知由 lifecycle 层 consume 后查 isSoftLimitReached 发出(职责分离)。
|
|
9
|
-
* - 90% 预警用查询式 isThresholdReached(无状态,可重复查)。
|
|
10
|
-
* - maxTokens===0 视为不限制(守卫,避免首个 agent 完成误判 budget_limited)。
|
|
11
|
-
*
|
|
12
|
-
* 层归属:Engine。
|
|
13
|
-
*
|
|
14
|
-
* 参考:domain-models.md §4(字段/不变式/操作)。
|
|
15
|
-
*/
|
|
16
|
-
import type { AgentUsage } from "./types.ts";
|
|
17
|
-
|
|
18
|
-
/** Soft limit:总调用数超此值发预警(FR-7,从 ConcurrencyGate 迁入)。 */
|
|
19
|
-
export const SOFT_MAX_AGENTS_WARNING = 500;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Budget 加权系数(token 口径)。
|
|
23
|
-
*
|
|
24
|
-
* usedTokens 不再是四项原始 token 简单求和,而是加权后的「等效消耗」,
|
|
25
|
-
* 反映不同 token 桶的真实计费/处理开销差异:
|
|
26
|
-
* - input(非缓存新增):首次见到、需完整处理的新内容,权重 1(基准)
|
|
27
|
-
* - cacheRead(命中缓存):读取历史,开销极低,按 1/50 折算(权重 0.02)
|
|
28
|
-
* - cacheWrite(首次写入缓存):本版本不计入 budget(权重 0)
|
|
29
|
-
* - output:模型自回归生成,开销最高,权重 2
|
|
30
|
-
*
|
|
31
|
-
* 这避免了长 session 中 cacheRead 随轮次单调累积导致 budget 被快速烧穿的失真
|
|
32
|
-
* (详见讨论:cacheRead 在 N 轮里被报 N 次,去重上下文只有一份)。
|
|
33
|
-
*/
|
|
34
|
-
export const INPUT_WEIGHT = 1;
|
|
35
|
-
export const CACHE_READ_WEIGHT = 0.02;
|
|
36
|
-
export const CACHE_WRITE_WEIGHT = 0;
|
|
37
|
-
export const OUTPUT_WEIGHT = 2;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Budget 值对象。
|
|
41
|
-
*
|
|
42
|
-
* 不变式(domain-models.md §4):
|
|
43
|
-
* - maxTokens > 0 守卫:maxTokens===0 或 undefined 视为不限制
|
|
44
|
-
* - maxCost > 0 守卫:同上
|
|
45
|
-
* - consume 只累加,不减;isExceeded 只读
|
|
46
|
-
* - 无回调字段——所有副作用由调用方在 consume 后查询决定
|
|
47
|
-
*/
|
|
48
|
-
export class Budget {
|
|
49
|
-
readonly maxTokens?: number;
|
|
50
|
-
readonly maxCost?: number;
|
|
51
|
-
readonly maxTimeMs?: number;
|
|
52
|
-
usedTokens = 0;
|
|
53
|
-
usedCost = 0;
|
|
54
|
-
/** 总调用计数(soft limit 用,从 ConcurrencyGate.totalCallCount 迁入)。 */
|
|
55
|
-
totalCallCount = 0;
|
|
56
|
-
|
|
57
|
-
constructor(opts: {
|
|
58
|
-
maxTokens?: number;
|
|
59
|
-
maxCost?: number;
|
|
60
|
-
maxTimeMs?: number;
|
|
61
|
-
usedTokens?: number;
|
|
62
|
-
usedCost?: number;
|
|
63
|
-
totalCallCount?: number;
|
|
64
|
-
} = {}) {
|
|
65
|
-
this.maxTokens = opts.maxTokens;
|
|
66
|
-
this.maxCost = opts.maxCost;
|
|
67
|
-
this.maxTimeMs = opts.maxTimeMs;
|
|
68
|
-
this.usedTokens = opts.usedTokens ?? 0;
|
|
69
|
-
this.usedCost = opts.usedCost ?? 0;
|
|
70
|
-
this.totalCallCount = opts.totalCallCount ?? 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* 累加一次 agent 调用的 usage(加权口径)。
|
|
75
|
-
*
|
|
76
|
-
* 四项 token 按各自权重(INPUT/CACHE_READ/CACHE_WRITE/OUTPUT_WEIGHT)折算后求和,
|
|
77
|
-
* 而非原始 token 数直接相加。retry 间的真实消耗如实记录,避免预算被低估。
|
|
78
|
-
* 详见上方权重常量的口径说明。
|
|
79
|
-
*/
|
|
80
|
-
consume(usage: AgentUsage): void {
|
|
81
|
-
// NaN 守卫——非有限值当 0 处理,防 usedTokens 变 NaN 导致 isExceeded() 永远 false(预算限制失效)
|
|
82
|
-
const numOrZero = (v: number | undefined): number =>
|
|
83
|
-
typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
84
|
-
this.usedTokens +=
|
|
85
|
-
numOrZero(usage.input) * INPUT_WEIGHT +
|
|
86
|
-
numOrZero(usage.output) * OUTPUT_WEIGHT +
|
|
87
|
-
numOrZero(usage.cacheRead) * CACHE_READ_WEIGHT +
|
|
88
|
-
numOrZero(usage.cacheWrite) * CACHE_WRITE_WEIGHT;
|
|
89
|
-
this.usedCost += numOrZero(usage.cost);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** 累加调用计数(每次 agent dispatch 后调用)。 */
|
|
93
|
-
incrementCallCount(): void {
|
|
94
|
-
this.totalCallCount += 1;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* 是否超 token / cost 预算(FR-3)。
|
|
99
|
-
*
|
|
100
|
-
* maxTokens===0 或 undefined 视为不限制(守卫);
|
|
101
|
-
* maxCost===0 或 undefined 视为不限制。
|
|
102
|
-
* 时间预算(maxTimeMs)不由本方法判断——它是 wall-clock 约束,需参照 startedAt,
|
|
103
|
-
* 由 lifecycle 层的 scheduleTimeBudget(runWorkflow 内 setTimeout)
|
|
104
|
-
* 独立调度,到期 abortRun(doneReason="time_limited")。
|
|
105
|
-
*/
|
|
106
|
-
isExceeded(): boolean {
|
|
107
|
-
if (this.maxTokens !== undefined && this.maxTokens > 0 && this.usedTokens >= this.maxTokens) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
return this.maxCost !== undefined && this.maxCost > 0 && this.usedCost >= this.maxCost;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* 是否达到 soft limit(FR-7)。
|
|
115
|
-
*
|
|
116
|
-
* totalCallCount > SOFT_MAX_AGENTS_WARNING(500)。
|
|
117
|
-
* 调用方(lifecycle)在 consume/incrementCallCount 后查询,
|
|
118
|
-
* 命中时发通知(无状态——可重复查询)。
|
|
119
|
-
*/
|
|
120
|
-
isSoftLimitReached(): boolean {
|
|
121
|
-
return this.totalCallCount > SOFT_MAX_AGENTS_WARNING;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 剩余 token 预算。maxTokens 未设或 ≤0 时返回 undefined(视为不限制)。
|
|
126
|
-
*
|
|
127
|
-
* 嵌套 workflow() 调用时由 executeNestedWorkflow 消费:子 run 的 budgetTokens
|
|
128
|
-
* 继承父 run 的剩余预算,实现父子预算隔离下的总量约束。
|
|
129
|
-
*/
|
|
130
|
-
remaining(): number | undefined {
|
|
131
|
-
if (this.maxTokens === undefined || this.maxTokens <= 0) return undefined;
|
|
132
|
-
return Math.max(0, this.maxTokens - this.usedTokens);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* 是否达到 token 预算的给定比例阈值(如 0.9 = 90% 预警)。
|
|
137
|
-
*
|
|
138
|
-
* 纯查询,无状态——调用方负责去重(旧 _budgetWarningSent 语义由 lifecycle 层用
|
|
139
|
-
* 外部 Set 或 once-listener 实现)。maxTokens 未设或为 0 时返回 false。
|
|
140
|
-
*/
|
|
141
|
-
isThresholdReached(ratio: number): boolean {
|
|
142
|
-
return (
|
|
143
|
-
this.maxTokens !== undefined &&
|
|
144
|
-
this.maxTokens > 0 &&
|
|
145
|
-
this.usedTokens >= this.maxTokens * ratio
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
}
|