@zhushanwen/subagent-core 0.6.0 → 0.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/README.md +1 -1
- package/dist/chunk-KXVBIVOU.js +16 -0
- package/dist/chunk-T2SYBW3I.js +24 -0
- package/dist/chunk-VURUAGMM.js +2305 -0
- package/dist/{types-C3fE3R9x.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +770 -668
- package/dist/{types-C3fE3R9x.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +770 -668
- package/dist/execution/engine/engine-discovery-scan.cjs +2210 -0
- package/dist/execution/engine/engine-discovery-scan.d.cts +4 -0
- package/dist/execution/engine/engine-discovery-scan.d.ts +4 -0
- package/dist/execution/engine/engine-discovery-scan.js +20 -0
- package/dist/execution/engine/paths.cjs +6 -23
- package/dist/execution/engine/paths.d.cts +1 -8
- package/dist/execution/engine/paths.d.ts +1 -8
- package/dist/execution/engine/paths.js +1 -1
- package/dist/execution/relay-env.cjs +12 -28
- package/dist/execution/relay-env.d.cts +1 -33
- package/dist/execution/relay-env.d.ts +1 -33
- package/dist/execution/relay-env.js +5 -3
- package/dist/index.cjs +14045 -16059
- package/dist/index.d.cts +1035 -1579
- package/dist/index.d.ts +1035 -1579
- package/dist/index.js +9838 -13800
- package/dist.bundle/index.cjs +28875 -30323
- package/package.json +20 -26
- package/src/__tests__/record-store-last-line.test.ts +16 -7
- package/src/core/error-message.ts +7 -6
- package/src/core/host-services.ts +8 -2
- package/src/core/notify-ports.ts +17 -2
- package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
- package/src/execution/__tests__/collect-coordinator-service.test.ts +36 -47
- package/src/execution/__tests__/collect-mixed-dispatch.test.ts +20 -53
- package/src/execution/__tests__/delivery-methods.test.ts +179 -237
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
- package/src/execution/__tests__/execute-nesting.test.ts +20 -13
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +20 -33
- package/src/execution/__tests__/gc-timer.test.ts +7 -22
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
- package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
- package/src/execution/__tests__/idle-gc.test.ts +200 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
- package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
- package/src/execution/__tests__/record-store.test.ts +60 -6
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
- package/src/execution/__tests__/round-supervisor.test.ts +293 -0
- package/src/execution/__tests__/session-pending.test.ts +119 -17
- package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +1 -11
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
- package/src/execution/__tests__/subagent-service.test.ts +2 -2
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
- package/src/execution/__tests__/sync-collect-recovery.test.ts +21 -36
- package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
- package/src/execution/agent-registry.ts +6 -2
- package/src/execution/dialog-queue.ts +6 -73
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
- package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
- package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
- package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
- package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +81 -54
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
- package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
- package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +7 -2
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
- package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
- package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
- package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
- package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
- package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
- package/src/execution/engine/__tests__/registry.test.ts +206 -1
- package/src/execution/engine/__tests__/routing.test.ts +97 -0
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
- package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
- package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
- package/src/execution/engine/client/client-options.ts +103 -0
- package/src/execution/engine/client/engine-client.ts +687 -0
- package/src/execution/engine/client/mirror.ts +167 -0
- package/src/execution/engine/client/pid-file.ts +392 -0
- package/src/execution/engine/client/reaper.ts +95 -0
- package/src/execution/engine/client/remote-engine.ts +441 -0
- package/src/execution/engine/client/reverse-router.ts +255 -0
- package/src/execution/engine/common/capability-gate.ts +109 -22
- package/src/execution/engine/common/errors.ts +9 -25
- package/src/execution/engine/common/event-journal.ts +4 -50
- package/src/execution/engine/common/journal-replay.ts +3 -1
- package/src/execution/engine/common/kill-chain.ts +18 -251
- package/src/execution/engine/common/nesting-guard.ts +18 -114
- package/src/execution/engine/common/pool-manager.ts +4 -0
- package/src/execution/engine/common/session-view-service.ts +56 -84
- package/src/execution/engine/config.ts +151 -0
- package/src/execution/engine/d8-compat.ts +346 -0
- package/src/execution/engine/engine-discovery-roots.ts +107 -0
- package/src/execution/engine/engine-discovery-scan.ts +269 -0
- package/src/execution/engine/engine-discovery.ts +30 -7
- package/src/execution/engine/engine-inspect-package.ts +282 -0
- package/src/execution/engine/engine-manifest.ts +244 -0
- package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
- package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
- package/src/execution/engine/host/host-bridge.ts +150 -0
- package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
- package/src/execution/engine/host/pi-host-binding.ts +91 -0
- package/src/execution/engine/host/spawned-children.ts +139 -0
- package/src/execution/engine/host-task-spec.ts +29 -2
- package/src/execution/engine/model-validation.ts +55 -2
- package/src/execution/engine/paths.ts +16 -41
- package/src/execution/engine/port.ts +48 -2
- package/src/execution/engine/registry.ts +153 -34
- package/src/execution/engine/routing.ts +225 -52
- package/src/execution/engine/types.ts +25 -200
- package/src/execution/finalize-record.ts +25 -0
- package/src/execution/idle-gc.ts +89 -14
- package/src/execution/lifecycle-manager.ts +10 -7
- package/src/execution/lifecycle-predicates.ts +10 -8
- package/src/execution/record-store.ts +29 -2
- package/src/execution/relay-env.ts +19 -36
- package/src/execution/round-supervisor/domain.ts +89 -0
- package/src/execution/round-supervisor/index.ts +33 -0
- package/src/execution/round-supervisor/notify-accounting.ts +87 -0
- package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
- package/src/execution/round-supervisor/service-binding.test.ts +661 -0
- package/src/execution/round-supervisor/service-binding.ts +273 -0
- package/src/execution/round-supervisor/supervisor.ts +426 -0
- package/src/execution/session-pending.ts +68 -74
- package/src/execution/settled-watchdog.ts +39 -0
- package/src/execution/subagent-service.ts +746 -400
- package/src/execution/subprocess-agent-runner.ts +16 -8
- package/src/execution/types.ts +41 -117
- package/src/execution/ui-channels.ts +13 -214
- package/src/execution/ui-request-handler-factory.ts +7 -4
- package/src/execution/ui-request-observability.ts +2 -2
- package/src/execution/workflow-state-root.ts +67 -0
- package/src/execution/worktree-git-ops.ts +5 -0
- package/src/execution/worktree-manager.ts +11 -1
- package/src/index.ts +55 -26
- package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +144 -59
- package/src/orchestration/models/types.ts +43 -42
- package/src/shared/zcode-model-ref.ts +31 -0
- package/dist/chunk-43ONBFZX.js +0 -240
- package/dist/chunk-A4IVZWVX.js +0 -31
- package/dist/chunk-APZY4IME.js +0 -27
- package/dist/chunk-V3VHZ2VX.js +0 -59
- package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
- package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
- package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
- package/dist/execution/engine/engines/zcode/constants.js +0 -54
- package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
- package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
- package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
- package/dist/execution/engine/engines/zcode/reader.js +0 -9
- package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
- package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
- package/src/__tests__/session-runner.test.ts +0 -69
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
- package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
- package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
- package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
- package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
- package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
- package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
- package/src/execution/__tests__/output-collector.test.ts +0 -469
- package/src/execution/__tests__/pi-invocation.test.ts +0 -166
- package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
- package/src/execution/__tests__/rpc-mode.test.ts +0 -89
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
- package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
- 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 -207
- package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
- package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
- package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
- package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
- package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
- package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
- package/src/execution/__tests__/spawn-args.test.ts +0 -446
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
- package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
- package/src/execution/__tests__/spawned-children.test.ts +0 -236
- package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
- package/src/execution/__tests__/stdin-writer.test.ts +0 -464
- package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
- package/src/execution/__tests__/temp-prompt.test.ts +0 -53
- package/src/execution/__tests__/timeout-integration.test.ts +0 -616
- 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-request-handler.test.ts +0 -205
- package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
- package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
- package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
- package/src/execution/engine/__tests__/paths.test.ts +0 -39
- 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/engines/pi/__tests__/pi-engine.test.ts +0 -615
- package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
- package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
- package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
- package/src/execution/engine/engines/pi/output-collector.ts +0 -317
- package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
- package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
- package/src/execution/engine/engines/pi/reader.ts +0 -59
- package/src/execution/engine/engines/pi/registration.ts +0 -37
- package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
- package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
- package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
- package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
- package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
- package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
- package/src/execution/engine/engines/zcode/connection.ts +0 -598
- package/src/execution/engine/engines/zcode/constants.ts +0 -176
- package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
- package/src/execution/engine/engines/zcode/parser.ts +0 -102
- package/src/execution/engine/engines/zcode/preparer.ts +0 -235
- 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/session-channel.ts +0 -968
- package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
- package/src/execution/round-settlement.ts +0 -93
- package/src/execution/ui-request-queue.ts +0 -215
- package/src/shared/schema-env.ts +0 -44
|
@@ -29,6 +29,13 @@ import type { AgentCallOpts, AgentResult } from "../../orchestration/models/type
|
|
|
29
29
|
import { ModelConfigService, setModelConfigService } from "../model-config-service.ts";
|
|
30
30
|
import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
|
|
31
31
|
import { replayJournal } from "../engine/common/event-journal.ts";
|
|
32
|
+
import { clearEngines, registerEngine } from "../engine/registry.ts";
|
|
33
|
+
import type { EnginePort, RunContext } from "../engine/port.ts";
|
|
34
|
+
import type {
|
|
35
|
+
AgentOutcome,
|
|
36
|
+
EngineCapabilities,
|
|
37
|
+
ProbeReport,
|
|
38
|
+
} from "../engine/types.ts";
|
|
32
39
|
import type { SubprocessAgentRunnerDeps } from "../subprocess-agent-runner.ts";
|
|
33
40
|
import type { SubagentService } from "../subagent-service.ts";
|
|
34
41
|
import { SubprocessAgentRunner } from "../subprocess-agent-runner.ts";
|
|
@@ -48,9 +55,41 @@ function makeMockResult(overrides: Partial<AgentResult> = {}): AgentResult {
|
|
|
48
55
|
};
|
|
49
56
|
}
|
|
50
57
|
|
|
58
|
+
/** 协议替身的引擎内「任务声明 → 执行选项」映射(原 core PiEngine
|
|
59
|
+
* agentCallToExecuteOptions 的最小同构还原——[W3] 该映射本体已随 inproc pi 引擎目录 迁入
|
|
60
|
+
* pi-subagent-cli 引擎包,此处仅为保留 executeAndAwait 委托断言面)。 */
|
|
61
|
+
function fakeAgentCallToExecuteOptions(task: AgentCallOpts, ctxModel?: ModelInfo): Record<string, unknown> {
|
|
62
|
+
const rawSlug = task.description ?? task.agent ?? "workflow-agent";
|
|
63
|
+
return {
|
|
64
|
+
task: task.prompt,
|
|
65
|
+
slug: rawSlug.length > 35 ? rawSlug.slice(0, 35) : rawSlug,
|
|
66
|
+
agent: task.agent,
|
|
67
|
+
model: task.model,
|
|
68
|
+
thinkingLevel: task.thinkingLevel,
|
|
69
|
+
skillPath: task.skillPath,
|
|
70
|
+
appendSystemPrompt: task.appendSystemPrompt,
|
|
71
|
+
schema: task.schema,
|
|
72
|
+
schemaEnv: task.schemaEnv,
|
|
73
|
+
maxTurns: task.maxTurns,
|
|
74
|
+
graceTurns: task.graceTurns,
|
|
75
|
+
ctxModel,
|
|
76
|
+
fork: task.fork,
|
|
77
|
+
worktree: task.worktree,
|
|
78
|
+
cwd: task.cwd,
|
|
79
|
+
conversation: task.conversation,
|
|
80
|
+
idleTimeoutMs: task.idleTimeoutMs,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 引擎 run 捕获面(T3.19 直传保真断言用:SAR → engine.run 的 task 原样性)。 */
|
|
85
|
+
const engineRunSpy = vi.fn();
|
|
86
|
+
|
|
51
87
|
/** 创建 mock SubagentService(只实现 executeAndAwait)。
|
|
52
|
-
* [D4 聚合连带] SAR 构造器经 asEngineService
|
|
53
|
-
*
|
|
88
|
+
* [D4 聚合连带] SAR 构造器经 asEngineService 显式视图取引擎服务面——fake 的 face
|
|
89
|
+
* 即自身,getter 直接返回 self。
|
|
90
|
+
* [W3 改写] 同步注册「委托式」替身 pi 引擎:run() 还原引擎内 task→ExecuteOptions
|
|
91
|
+
* 映射后委托 mock 的 executeAndAwait(原 inproc PiEngine 的 workflow 分支形态)——
|
|
92
|
+
* resolveHostPiEnginePort 对已注册 port 原样返回,SAR 路由命中替身。 */
|
|
54
93
|
function createMockService(impl?: typeof vi.fn): SubagentService {
|
|
55
94
|
const executeAndAwait = impl ?? vi.fn().mockResolvedValue(makeMockResult());
|
|
56
95
|
// partial mock(仅 executeAndAwait + asEngineService self-引用)——SAR 测试路径
|
|
@@ -58,6 +97,50 @@ function createMockService(impl?: typeof vi.fn): SubagentService {
|
|
|
58
97
|
const partial: { executeAndAwait: typeof executeAndAwait; asEngineService?: unknown } = { executeAndAwait };
|
|
59
98
|
const service = partial as unknown as SubagentService;
|
|
60
99
|
partial.asEngineService = service;
|
|
100
|
+
|
|
101
|
+
const fakePort: EnginePort = {
|
|
102
|
+
id: "pi",
|
|
103
|
+
capabilities: (): EngineCapabilities => ({
|
|
104
|
+
schemaEnforcement: "native",
|
|
105
|
+
steer: "unsupported",
|
|
106
|
+
conversation: "native",
|
|
107
|
+
personaInjection: "flag",
|
|
108
|
+
eventGranularity: "stream",
|
|
109
|
+
sandbox: "emulated",
|
|
110
|
+
sessionRead: "full",
|
|
111
|
+
resume: "native",
|
|
112
|
+
interrupt: "kill-only",
|
|
113
|
+
permissionMode: "native",
|
|
114
|
+
maxTurns: true,
|
|
115
|
+
}),
|
|
116
|
+
probe: async (): Promise<ProbeReport> => ({ ok: true, engineVersion: "fake", checks: [] }),
|
|
117
|
+
run: async (task: AgentCallOpts, ctx: RunContext) => {
|
|
118
|
+
engineRunSpy(task, ctx);
|
|
119
|
+
const wfResult = await executeAndAwait(
|
|
120
|
+
fakeAgentCallToExecuteOptions(task, ctx.ctxModel),
|
|
121
|
+
ctx.signal,
|
|
122
|
+
ctx.onEvent,
|
|
123
|
+
ctx.stream,
|
|
124
|
+
) as { content: string; parsedOutput?: unknown; durationMs?: number; error?: string };
|
|
125
|
+
const outcome: AgentOutcome = {
|
|
126
|
+
content: wfResult.content,
|
|
127
|
+
parsedOutput: wfResult.parsedOutput,
|
|
128
|
+
durationMs: wfResult.durationMs,
|
|
129
|
+
error: wfResult.error,
|
|
130
|
+
engineId: "pi",
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
handle: {
|
|
134
|
+
data: { v: 1, engineId: "pi", sessionRef: {}, poolKey: "shared", adapterVersion: "fake-sar" },
|
|
135
|
+
},
|
|
136
|
+
outcome,
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
interact: async () => ({ ok: false, code: "engine_interact_failed", message: "not supported in this test" }),
|
|
140
|
+
read: async () => ({ engineId: "pi", turns: [], source: "outcome-only" }),
|
|
141
|
+
};
|
|
142
|
+
clearEngines();
|
|
143
|
+
registerEngine("pi", () => fakePort);
|
|
61
144
|
return service;
|
|
62
145
|
}
|
|
63
146
|
|
|
@@ -83,6 +166,9 @@ function makeBaseOpts(): AgentCallOpts {
|
|
|
83
166
|
describe("SubprocessAgentRunner (wave-4 delegate)", () => {
|
|
84
167
|
beforeEach(() => {
|
|
85
168
|
configureCore({ dataRoot: () => "/fake-sar-data-root", log: () => {} });
|
|
169
|
+
// [U-2 一致性修复] pi 未注册时 resolveHostPiEnginePort 返回 engine_not_found stub
|
|
170
|
+
// ——替身 pi 引擎在 createMockService 内按测试服务注册([W3] 委托式协议替身)。
|
|
171
|
+
engineRunSpy.mockClear();
|
|
86
172
|
});
|
|
87
173
|
|
|
88
174
|
afterEach(() => {
|
|
@@ -441,7 +527,7 @@ describe("SubprocessAgentRunner (wave-4 delegate)", () => {
|
|
|
441
527
|
const workflowOnEvent = vi.fn();
|
|
442
528
|
await sar.run(makeBaseOpts(), new AbortController().signal, workflowOnEvent);
|
|
443
529
|
|
|
444
|
-
// journal 落在 <dataDir>/
|
|
530
|
+
// journal 落在 <dataDir>/inproc pi 引擎目录/shared/journal-<taskId>.jsonl
|
|
445
531
|
const journalFile = join(dataDir, "engines", "pi", "shared");
|
|
446
532
|
const files = readdirSync(journalFile).filter((f) => f.startsWith("journal-") && f.endsWith(".jsonl"));
|
|
447
533
|
expect(files.length).toBe(1);
|
|
@@ -491,17 +577,14 @@ describe("SubprocessAgentRunner (wave-4 delegate)", () => {
|
|
|
491
577
|
});
|
|
492
578
|
|
|
493
579
|
// ────────────────────────────────────────────────
|
|
494
|
-
// T3.19:
|
|
580
|
+
// T3.19: SAR → engine.run 任务声明直传保真
|
|
581
|
+
// [W3 改写] 「AgentCallOpts → ExecuteOptions 映射点在 PiEngine 边界」随 inproc pi 引擎目录
|
|
582
|
+
// 删除迁入 pi-subagent-cli(引擎包 spawn-args 测试守护);core 侧存留契约 =
|
|
583
|
+
// SAR 对 AgentCallOpts 直传零映射(engine.run 的 task 原样性)。
|
|
495
584
|
// ────────────────────────────────────────────────
|
|
496
|
-
describe("T3.19
|
|
497
|
-
it("prompt
|
|
498
|
-
|
|
499
|
-
const mockService = createMockService(
|
|
500
|
-
vi.fn().mockImplementation((opts: Record<string, unknown>) => {
|
|
501
|
-
capturedOpts = opts;
|
|
502
|
-
return Promise.resolve(makeMockResult());
|
|
503
|
-
}),
|
|
504
|
-
);
|
|
585
|
+
describe("T3.19 直传保真(D6:SAR 直传零映射,任务声明原样到达 engine.run)", () => {
|
|
586
|
+
it("prompt/agent/skillPath/schemaEnv 原样到达 engine.run", async () => {
|
|
587
|
+
const mockService = createMockService();
|
|
505
588
|
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
506
589
|
const sar = new SubprocessAgentRunner(deps);
|
|
507
590
|
|
|
@@ -514,35 +597,24 @@ describe("SubprocessAgentRunner (wave-4 delegate)", () => {
|
|
|
514
597
|
};
|
|
515
598
|
await sar.run(opts, new AbortController().signal);
|
|
516
599
|
|
|
517
|
-
expect(
|
|
518
|
-
|
|
519
|
-
expect(
|
|
520
|
-
expect(
|
|
521
|
-
|
|
522
|
-
expect(
|
|
600
|
+
expect(engineRunSpy).toHaveBeenCalledTimes(1);
|
|
601
|
+
const [task] = engineRunSpy.mock.calls[0] as [AgentCallOpts, unknown];
|
|
602
|
+
expect(task.prompt).toBe("do the thing");
|
|
603
|
+
expect(task.agent).toBe("code-reviewer");
|
|
604
|
+
expect(task.skillPath).toBe("/skills/code-review.md");
|
|
605
|
+
expect(task.schemaEnv).toBe('{"type":"object"}');
|
|
523
606
|
});
|
|
524
607
|
|
|
525
|
-
it("schema
|
|
526
|
-
|
|
527
|
-
const mockService = createMockService(
|
|
528
|
-
vi.fn().mockImplementation((opts: Record<string, unknown>) => {
|
|
529
|
-
capturedOpts = opts;
|
|
530
|
-
return Promise.resolve(makeMockResult());
|
|
531
|
-
}),
|
|
532
|
-
);
|
|
608
|
+
it("schema 原样透传为原始对象", async () => {
|
|
609
|
+
const mockService = createMockService();
|
|
533
610
|
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
534
611
|
const sar = new SubprocessAgentRunner(deps);
|
|
535
612
|
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
schema: { type: "object", properties: { name: { type: "string" } } },
|
|
539
|
-
};
|
|
540
|
-
await sar.run(opts, new AbortController().signal);
|
|
613
|
+
const schema = { type: "object", properties: { name: { type: "string" } } };
|
|
614
|
+
await sar.run({ ...makeBaseOpts(), schema }, new AbortController().signal);
|
|
541
615
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
properties: { name: { type: "string" } },
|
|
545
|
-
});
|
|
616
|
+
const [task] = engineRunSpy.mock.calls[0] as [AgentCallOpts, unknown];
|
|
617
|
+
expect(task.schema).toEqual(schema);
|
|
546
618
|
});
|
|
547
619
|
});
|
|
548
620
|
|
|
@@ -62,35 +62,18 @@ import * as path from "node:path";
|
|
|
62
62
|
|
|
63
63
|
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from "vitest";
|
|
64
64
|
|
|
65
|
-
const { loggerMock
|
|
65
|
+
const { loggerMock } = vi.hoisted(() => ({
|
|
66
66
|
loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() },
|
|
67
|
-
runSpawnMock: vi.fn(async () => ({
|
|
68
|
-
text: "ok",
|
|
69
|
-
turns: 1,
|
|
70
|
-
durationMs: 10,
|
|
71
|
-
success: true,
|
|
72
|
-
sessionId: "spawned",
|
|
73
|
-
toolCalls: [],
|
|
74
|
-
})),
|
|
75
67
|
}));
|
|
76
68
|
// mock logger:路径从 __tests__ 出发是 ../../core/(src/core/logger.ts——subagent-service
|
|
77
69
|
// 经 ../core/logger.ts 引用的同一模块)。曾写 ../core/logger.ts 指向不存在的
|
|
78
70
|
// src/execution/core/,vi.mock 静默失效(D4 上限用例首次断言日志时暴露)。
|
|
79
71
|
vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
80
72
|
|
|
81
|
-
// mock session-runner
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// 相对路径「层级」与「落位」都可能漂移,见 collect-coordinator-service.test.ts)。
|
|
86
|
-
vi.mock("../engine/engines/pi/session-runner.ts", () => ({
|
|
87
|
-
runSpawn: runSpawnMock,
|
|
88
|
-
killAllSpawnedChildren: vi.fn(),
|
|
89
|
-
killRecordChildWithEscalation: vi.fn(),
|
|
90
|
-
getChildByRecord: vi.fn(() => undefined),
|
|
91
|
-
registerSpawnedChildForRecord: vi.fn(),
|
|
92
|
-
spawnedChildren: new Map(),
|
|
93
|
-
}));
|
|
73
|
+
// [W3 改写] mock session-runner(execute 链 runSpawn 替身)随 inproc pi 引擎目录 删除消亡——
|
|
74
|
+
// execute 链改为协议 seam(registerFakePiEngine 替身 + 显式 settle 应答)。
|
|
75
|
+
import { registerFakePiEngine, type FakePiEnginePort } from "./helpers/fake-engine-port.ts";
|
|
76
|
+
import { clearEngines } from "../engine/registry.ts";
|
|
94
77
|
|
|
95
78
|
import { SUBAGENT_RECORD_CUSTOM_TYPE } from "../record-entry.ts";
|
|
96
79
|
import { ManifestStore } from "../manifest-store.ts";
|
|
@@ -275,6 +258,12 @@ describe("sync collect recovery (U5 E1/E9) — 真实文件通路", () => {
|
|
|
275
258
|
|
|
276
259
|
/** 恢复侧 service:断言 pi(覆写不落盘,断言内存面)/ 写文件 pi(覆写真实落盘,
|
|
277
260
|
* E1 读覆写后末条——kill -9 真实链路同构)两用。 */
|
|
261
|
+
/** [W3] 协议替身注册(per-test 调用;afterEach clearEngines 统一清理)。 */
|
|
262
|
+
function makeRecoveryFake(): FakePiEnginePort {
|
|
263
|
+
clearEngines();
|
|
264
|
+
return registerFakePiEngine();
|
|
265
|
+
}
|
|
266
|
+
|
|
278
267
|
function makeRecoveryService(pi: AssertPi | WritingPi): SubagentService {
|
|
279
268
|
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
280
269
|
const modelRegistry: ModelRegistryLike = {
|
|
@@ -564,15 +553,13 @@ describe("sync collect recovery (U5 E1/E9) — 真实文件通路", () => {
|
|
|
564
553
|
|
|
565
554
|
it("E9 dispose:缓冲终态成员逐条转 async notify + 落标;在跑成员走现有退出路径(不通知)", async () => {
|
|
566
555
|
// 复用 execute 真链:成员 1 受控终态(入缓冲后批因成员 2 在跑不闭合),成员 2 悬置 running
|
|
567
|
-
|
|
568
|
-
runSpawnMock.mockImplementationOnce(() => new Promise((res) => { resolve1 = res; }));
|
|
569
|
-
runSpawnMock.mockImplementationOnce(() => new Promise(() => { /* 悬置到 dispose */ }));
|
|
556
|
+
const fake = makeRecoveryFake();
|
|
570
557
|
const pi = makeAssertPi();
|
|
571
558
|
const service = makeRecoveryService(pi);
|
|
572
559
|
const spy = spyNotifier(service);
|
|
573
560
|
const h1 = await service.execute({ task: "terminal one", slug: "one", collect: "sync" });
|
|
574
561
|
await service.execute({ task: "running two", slug: "two", collect: "sync" });
|
|
575
|
-
await until(() =>
|
|
562
|
+
await until(() => fake.runs.length >= 2);
|
|
576
563
|
|
|
577
564
|
// 成员 1 子文件就位(E9 落标 getFullRecord 冷路径数据源)
|
|
578
565
|
const sessionsDir = getSubagentSessionDir(agentDir, agentDir);
|
|
@@ -599,9 +586,10 @@ describe("sync collect recovery (U5 E1/E9) — 真实文件通路", () => {
|
|
|
599
586
|
);
|
|
600
587
|
|
|
601
588
|
// 成员 1 终态:入缓冲,成员 2 在跑 → 批不闭合(零批投递)
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
589
|
+
fake.runs[0]!.settle({ content: "ok-terminal" });
|
|
590
|
+
// 等 settle 收尾链排空(finalize→route 入缓冲;成员 2 悬置 → 零投递)
|
|
591
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
592
|
+
expect(spy.notify.mock.calls.length + spy.notifyBatch.mock.calls.length).toBe(0);
|
|
605
593
|
expect(spy.notifyBatch).not.toHaveBeenCalled();
|
|
606
594
|
|
|
607
595
|
// dispose(E9):成员 1 逐条转 async 写账 + 落标;成员 2 不通知(现有退出路径)
|
|
@@ -1071,23 +1059,20 @@ describe("sync collect recovery (U5 E1/E9) — 真实文件通路", () => {
|
|
|
1071
1059
|
// 成员 1:失败终态(archive 出内存)且不写子文件 → getFullRecord 双 miss
|
|
1072
1060
|
//(内存已出 + 子文件缺失——子文件被 GC/删除的窗口形态);
|
|
1073
1061
|
// 成员 2:SP-5 成功回退(留内存)→ getFullRecord 内存命中(正常落标对照)。
|
|
1074
|
-
|
|
1075
|
-
let resolve2!: (v: { text: string; turns: number; durationMs: number; success: boolean; sessionId: string; toolCalls: [] }) => void;
|
|
1076
|
-
runSpawnMock.mockImplementationOnce(() => new Promise((res) => { resolve1 = res; }));
|
|
1077
|
-
runSpawnMock.mockImplementationOnce(() => new Promise((res) => { resolve2 = res; }));
|
|
1062
|
+
const fake = makeRecoveryFake();
|
|
1078
1063
|
// 写文件 pi:标记 entry 真实落盘主文件,E1 扫描通路(rebuildEntryRecord)可重解析
|
|
1079
1064
|
const pi = makeWritingPi(mainFile);
|
|
1080
1065
|
const service = makeRecoveryService(pi);
|
|
1081
1066
|
const spy = spyNotifier(service);
|
|
1082
1067
|
const h1 = await service.execute({ task: "miss one", slug: "one", collect: "sync" });
|
|
1083
1068
|
const h2 = await service.execute({ task: "full two", slug: "two", collect: "sync" });
|
|
1084
|
-
await until(() =>
|
|
1069
|
+
await until(() => fake.runs.length >= 2);
|
|
1085
1070
|
|
|
1086
1071
|
// 成员 1 子文件故意缺失(getFullRecord miss 前提);成员 2 内存命中不依赖子文件。
|
|
1087
1072
|
// 注:两成员的 settle 链竞态可能拆两批([U8] 合批窗口形态)——S11 断言与批切分
|
|
1088
1073
|
// 无关,只锁「批成员并集覆盖」与「miss 成员兜底落标」。
|
|
1089
|
-
|
|
1090
|
-
|
|
1074
|
+
fake.runs[0]!.settle({ content: "boom-miss", error: "boom-miss" });
|
|
1075
|
+
fake.runs[1]!.settle({ content: "ok-full" });
|
|
1091
1076
|
await until(() => spy.notifyBatch.mock.calls.length > 0);
|
|
1092
1077
|
await new Promise((resolve) => setTimeout(resolve, 50)); // flushBatch 落标同步链排空
|
|
1093
1078
|
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// src/execution/__tests__/workflow-state-root.test.ts
|
|
2
|
+
//
|
|
3
|
+
// [F-1 修复] pi 宿主 WorkflowRun state 读侧装配同源布局测试。
|
|
4
|
+
//
|
|
5
|
+
// 背景(装配错位事故面):round-supervisor sweep 与 idle-gc 曾用 FileRunStore 缺省根
|
|
6
|
+
// `<dataRoot>/workflow-state`(zcode 宿主布局)读 workflow run state,而 pi 宿主真实
|
|
7
|
+
// 落盘 = JsonlRunStore 的 `<sessionDir>/workflow-state/<runId>.jsonl`——两目录生产不
|
|
8
|
+
// 相交 → findStateByIdSync 恒 missing → sweep 按终态补注销**活跃 run**;WorkflowRun
|
|
9
|
+
// GC 恒空转。修复后装配点传 resolvePiWorkflowStateDir()(execution/workflow-state-root.ts,
|
|
10
|
+
// 与 extensions/universal/subagent-workflow/src/session-lifecycle.ts resolveSessionDir
|
|
11
|
+
// 同规则重写)。
|
|
12
|
+
//
|
|
13
|
+
// 本套件用 mkdtemp 真实目录布局(非 mock fs)证明四件事:
|
|
14
|
+
// ① resolvePiWorkflowStateDir 探测语义两分支(sessionScopedDir 存在/不存在);
|
|
15
|
+
// ② FileRunStore({stateDir}) findStateByIdSync 在真实布局命中 running/终态/missing;
|
|
16
|
+
// ③ sweep 装配链(runPendingReconcileSweepForService,env 指向 tmp agentDir)端到端:
|
|
17
|
+
// running run 不补注销(修复前被误注销的事故方向)、终态 run 补注销;
|
|
18
|
+
// ④ idle-gc 同布局:30 天 running run 文件被终态化写回(findStateByIdSync 变 done)。
|
|
19
|
+
//
|
|
20
|
+
// 写删目标全部 mkdtempSync 自建自删(禁触真实数据目录纪律)。
|
|
21
|
+
|
|
22
|
+
import * as fs from "node:fs";
|
|
23
|
+
import * as os from "node:os";
|
|
24
|
+
import * as path from "node:path";
|
|
25
|
+
|
|
26
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
27
|
+
|
|
28
|
+
import { FileRunStore } from "../../orchestration/file-run-store.ts";
|
|
29
|
+
import { Budget } from "../../orchestration/models/budget.ts";
|
|
30
|
+
import { Trace } from "../../orchestration/models/trace.ts";
|
|
31
|
+
import { WorkflowRun } from "../../orchestration/models/workflow-run.ts";
|
|
32
|
+
import { startIdleGc } from "../idle-gc.ts";
|
|
33
|
+
import { RecordStore } from "../record-store.ts";
|
|
34
|
+
import { runPendingReconcileSweepForService } from "../round-supervisor/service-binding.ts";
|
|
35
|
+
import type { RoundSupervisorBinding } from "../round-supervisor/service-binding.ts";
|
|
36
|
+
import { resolvePiWorkflowStateDir } from "../workflow-state-root.ts";
|
|
37
|
+
|
|
38
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
39
|
+
const GC_INTERVAL_MS = 60 * 60 * 1000;
|
|
40
|
+
|
|
41
|
+
let tmpDir: string;
|
|
42
|
+
let stopGc: (() => void) | undefined;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wf-state-root-"));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
stopGc?.();
|
|
50
|
+
stopGc = undefined;
|
|
51
|
+
vi.unstubAllEnvs();
|
|
52
|
+
vi.useRealTimers();
|
|
53
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/** 与 session-lifecycle.resolveSessionDir 同规则的 cwd-slug(测试构造期望布局用)。 */
|
|
57
|
+
function slugOf(cwd: string): string {
|
|
58
|
+
return `--${cwd.replace(/^\//, "").replace(/\//g, "-")}--`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 构造可持久化的 WorkflowRun(对齐 file-run-store.test.ts makeRun 模式)。 */
|
|
62
|
+
function makeRun(
|
|
63
|
+
runId: string,
|
|
64
|
+
opts: { status?: "running" | "done"; startedAt?: string } = {},
|
|
65
|
+
): WorkflowRun {
|
|
66
|
+
const status = opts.status ?? "running";
|
|
67
|
+
return WorkflowRun.reconstruct(
|
|
68
|
+
runId,
|
|
69
|
+
{
|
|
70
|
+
scriptSource: "export function execute() { return 'ok'; }",
|
|
71
|
+
args: { topic: "demo" },
|
|
72
|
+
scriptName: "test-script",
|
|
73
|
+
scriptPath: "/fake/test.js",
|
|
74
|
+
parameters: { type: "object" },
|
|
75
|
+
budgetTokens: 1000,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
status,
|
|
79
|
+
...(status === "done" ? { reason: "completed" as const } : {}),
|
|
80
|
+
budget: new Budget({ maxTokens: 1000, usedTokens: 1, usedCost: 0, totalCallCount: 1 }),
|
|
81
|
+
calls: new Map(),
|
|
82
|
+
trace: new Trace(),
|
|
83
|
+
errorLogs: [],
|
|
84
|
+
scriptResult: undefined,
|
|
85
|
+
},
|
|
86
|
+
{ startedAt: opts.startedAt ?? "2026-08-30T00:00:00.000Z" },
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
describe("resolvePiWorkflowStateDir 探测语义(session-lifecycle resolveSessionDir 同规则)", () => {
|
|
91
|
+
it("sessionScopedDir 存在 → 用 <agentDir>/sessions/<slug>/workflow-state(实例隔离布局)", () => {
|
|
92
|
+
const cwd = path.join(tmpDir, "proj"); // 形式 cwd(不要求真实存在)
|
|
93
|
+
const agentDir = path.join(tmpDir, "agent");
|
|
94
|
+
const sessionScopedDir = path.join(agentDir, "sessions", slugOf(cwd));
|
|
95
|
+
fs.mkdirSync(sessionScopedDir, { recursive: true });
|
|
96
|
+
expect(resolvePiWorkflowStateDir({ agentDir, cwd })).toBe(path.join(sessionScopedDir, "workflow-state"));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("sessionScopedDir 不存在 → 回退 <agentDir>/workflow-state(JsonlRunStore 首写 mkdir 的根布局)", () => {
|
|
100
|
+
const agentDir = path.join(tmpDir, "agent");
|
|
101
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
102
|
+
expect(resolvePiWorkflowStateDir({ agentDir, cwd: path.join(tmpDir, "fresh-proj") })).toBe(
|
|
103
|
+
path.join(agentDir, "workflow-state"),
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("FileRunStore({stateDir}) × 真实 JsonlRunStore 布局(findStateByIdSync 读侧判据)", () => {
|
|
109
|
+
it("真实布局命中:running → {kind:running};done → {kind:terminal, reason};无文件 → {kind:missing}", async () => {
|
|
110
|
+
const stateDir = path.join(tmpDir, "sessions", slugOf("/x/y"), "workflow-state");
|
|
111
|
+
const store = new FileRunStore({ stateDir });
|
|
112
|
+
await store.save(makeRun("wf-live")); // running(首写不节流)
|
|
113
|
+
await store.save(makeRun("wf-done", { status: "done" }));
|
|
114
|
+
|
|
115
|
+
expect(store.findStateByIdSync("wf-live")).toEqual({ kind: "running" });
|
|
116
|
+
expect(store.findStateByIdSync("wf-done")).toEqual({ kind: "terminal", reason: "completed" });
|
|
117
|
+
expect(store.findStateByIdSync("wf-never")).toEqual({ kind: "missing" });
|
|
118
|
+
// 落盘路径形状与 pi 壳 JsonlRunStore 同构:<sessionDir>/workflow-state/<runId>.jsonl
|
|
119
|
+
expect(fs.existsSync(path.join(stateDir, "wf-live.jsonl"))).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("sweep 装配链端到端(runPendingReconcileSweepForService × 真实布局)", () => {
|
|
124
|
+
/**
|
|
125
|
+
* 装配 harness:env 指向 tmp agentDir(生产装配 resolvePiWorkflowStateDir() 无参走
|
|
126
|
+
* env + process.cwd()),run state 由 FileRunStore({stateDir}) 写入解析出的目录——
|
|
127
|
+
* 证明「生产装配点读的目录 = run state 真实落盘目录」(同源布局闭环)。
|
|
128
|
+
*/
|
|
129
|
+
function setupSweep(): { agentDir: string; store: FileRunStore } {
|
|
130
|
+
const agentDir = path.join(tmpDir, "agent");
|
|
131
|
+
vi.stubEnv("PI_CODING_AGENT_DIR", agentDir);
|
|
132
|
+
const store = new FileRunStore({ stateDir: resolvePiWorkflowStateDir() }); // 与生产装配同参
|
|
133
|
+
return { agentDir, store };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function makeBinding(sessionFile: string, appended: Array<{ customType: string; data: unknown }>): RoundSupervisorBinding {
|
|
137
|
+
return {
|
|
138
|
+
getStore: () => new RecordStore(path.join(tmpDir, "records")),
|
|
139
|
+
getPi: () =>
|
|
140
|
+
({
|
|
141
|
+
appendEntry: (type: string, data: unknown) => appended.push({ customType: type, data }),
|
|
142
|
+
events: { emit: vi.fn() },
|
|
143
|
+
sendMessage: vi.fn(),
|
|
144
|
+
}) as unknown as NonNullable<ReturnType<RoundSupervisorBinding["getPi"]>>,
|
|
145
|
+
getSessionRootId: () => "root",
|
|
146
|
+
getMainSessionFile: () => sessionFile,
|
|
147
|
+
finalizeClosed: vi.fn(),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function writeRegister(sessionFile: string, id: string): void {
|
|
152
|
+
fs.writeFileSync(
|
|
153
|
+
sessionFile,
|
|
154
|
+
JSON.stringify({ customType: "pending:register", data: { id, type: "workflow", name: id } }) + "\n",
|
|
155
|
+
"utf-8",
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
it("活跃 workflow run(running state 文件在盘)→ sweep 不补注销(修复前被误注销)", async () => {
|
|
160
|
+
const { agentDir, store } = setupSweep();
|
|
161
|
+
await store.save(makeRun("wf-live"));
|
|
162
|
+
|
|
163
|
+
const sessionFile = path.join(agentDir, "main-session.jsonl");
|
|
164
|
+
writeRegister(sessionFile, "wf-live");
|
|
165
|
+
const appended: Array<{ customType: string; data: unknown }> = [];
|
|
166
|
+
runPendingReconcileSweepForService(makeBinding(sessionFile, appended), false);
|
|
167
|
+
expect(appended).toHaveLength(0); // 活跃 run 不注销——事故方向的回归钉
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("终态 workflow run(done state 文件在盘)→ sweep 补注销(reason 取 run reason)", async () => {
|
|
171
|
+
const { agentDir, store } = setupSweep();
|
|
172
|
+
await store.save(makeRun("wf-done", { status: "done" }));
|
|
173
|
+
|
|
174
|
+
const sessionFile = path.join(agentDir, "main-session.jsonl");
|
|
175
|
+
writeRegister(sessionFile, "wf-done");
|
|
176
|
+
const appended: Array<{ customType: string; data: unknown }> = [];
|
|
177
|
+
runPendingReconcileSweepForService(makeBinding(sessionFile, appended), false);
|
|
178
|
+
expect(appended).toEqual([
|
|
179
|
+
{ customType: "pending:unregister", data: { id: "wf-done", reason: "completed", status: "completed" } },
|
|
180
|
+
]);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe("idle-gc 同布局(WorkflowRun GC 读对根)", () => {
|
|
185
|
+
/**
|
|
186
|
+
* GC interval 用 fake timers 推进;但 gcWorkflowRuns 的 loadAll/save 是**真实 fs IO**
|
|
187
|
+
* (libuv 线程池回调不属被 fake 的 timer API)——推进后经 setImmediate(toFake 排除,
|
|
188
|
+
* 保持真实调度)反复让出事件循环排空在途 IO。
|
|
189
|
+
*/
|
|
190
|
+
async function flushRealIo(): Promise<void> {
|
|
191
|
+
for (let i = 0; i < 50; i++) {
|
|
192
|
+
await new Promise<void>((resolve) => setImmediate(() => resolve()));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
it("真实布局 30 天 running run → GC 终态化写回(findStateByIdSync 变 terminal/time_limited)", async () => {
|
|
197
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "setInterval", "clearInterval"] });
|
|
198
|
+
const agentDir = path.join(tmpDir, "agent");
|
|
199
|
+
const cwd = path.join(tmpDir, "proj");
|
|
200
|
+
const sessionScopedDir = path.join(agentDir, "sessions", slugOf(cwd));
|
|
201
|
+
fs.mkdirSync(sessionScopedDir, { recursive: true });
|
|
202
|
+
const stateDir = resolvePiWorkflowStateDir({ agentDir, cwd });
|
|
203
|
+
const runStore = new FileRunStore({ stateDir });
|
|
204
|
+
await runStore.save(
|
|
205
|
+
makeRun("wf-stale", { startedAt: new Date(Date.now() - 31 * DAY_MS).toISOString() }),
|
|
206
|
+
);
|
|
207
|
+
expect(runStore.findStateByIdSync("wf-stale")).toEqual({ kind: "running" });
|
|
208
|
+
|
|
209
|
+
stopGc = startIdleGc(new RecordStore(path.join(tmpDir, "records")), runStore);
|
|
210
|
+
await vi.advanceTimersByTimeAsync(GC_INTERVAL_MS + 1);
|
|
211
|
+
await flushRealIo();
|
|
212
|
+
|
|
213
|
+
// 终态已落盘(同目录 append 终态快照行)——GC 读侧与落盘侧同根闭环
|
|
214
|
+
expect(runStore.findStateByIdSync("wf-stale")).toEqual({ kind: "terminal", reason: "time_limited" });
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("窗内 running run 不动(GC 判据在正确根上按 startedAt 生效)", async () => {
|
|
218
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "setInterval", "clearInterval"] });
|
|
219
|
+
const agentDir = path.join(tmpDir, "agent");
|
|
220
|
+
const stateDir = resolvePiWorkflowStateDir({ agentDir, cwd: tmpDir });
|
|
221
|
+
const runStore = new FileRunStore({ stateDir });
|
|
222
|
+
await runStore.save(
|
|
223
|
+
makeRun("wf-fresh", { startedAt: new Date(Date.now() - 1 * DAY_MS).toISOString() }),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
stopGc = startIdleGc(new RecordStore(path.join(tmpDir, "records")), runStore);
|
|
227
|
+
await vi.advanceTimersByTimeAsync(GC_INTERVAL_MS + 1);
|
|
228
|
+
await flushRealIo();
|
|
229
|
+
|
|
230
|
+
expect(runStore.findStateByIdSync("wf-fresh")).toEqual({ kind: "running" });
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -20,7 +20,10 @@ import { parseResourceMeta } from "../shared/meta-parser.ts";
|
|
|
20
20
|
import { lintAgentMeta } from "../orchestration/script-lint.ts";
|
|
21
21
|
import type { AgentMeta, RoutingExample } from "../shared/resource-meta.ts";
|
|
22
22
|
import type { AgentConfig } from "./model-resolver.ts";
|
|
23
|
-
import {
|
|
23
|
+
import { EngineNotFoundError, listEngines } from "./engine/registry.ts";
|
|
24
|
+
// [W8] 存在性校验经补扫通道(快照未命中触发一次三级补扫,W4 ensureEngineDiscovered
|
|
25
|
+
// 语义——宿主未接线补扫参数时与裸 hasEngine 等价)。
|
|
26
|
+
import { hasEngineWithRescan } from "./engine/routing.ts";
|
|
24
27
|
|
|
25
28
|
const logger = getLogger("subagents");
|
|
26
29
|
|
|
@@ -134,7 +137,8 @@ function resolveAgentEngine(
|
|
|
134
137
|
filePath: string,
|
|
135
138
|
): string | undefined {
|
|
136
139
|
const engine = agentMeta?.engine ?? extractYamlField(yamlBlock, "engine");
|
|
137
|
-
|
|
140
|
+
// [W8] 补扫通道:快照未命中先一次三级补扫再判(装了引擎包 → 下次解析即可用)。
|
|
141
|
+
if (engine !== undefined && !hasEngineWithRescan(engine)) {
|
|
138
142
|
throw new EngineNotFoundError(engine, listEngines(), filePath);
|
|
139
143
|
}
|
|
140
144
|
return engine;
|
|
@@ -84,79 +84,12 @@ function dialogTimeoutLogMessage(req: UiRequest, waitedMs: number): string {
|
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
// ──
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
* (string & {}) 兜底:Pi 未来新增 method 或未知 method 走字符串字面量类型。 */
|
|
94
|
-
export type UiMethod =
|
|
95
|
-
| "select"
|
|
96
|
-
| "confirm"
|
|
97
|
-
| "input"
|
|
98
|
-
| "editor"
|
|
99
|
-
| "notify"
|
|
100
|
-
| "setStatus"
|
|
101
|
-
| "setWidget"
|
|
102
|
-
| "setTitle"
|
|
103
|
-
| "set_editor_text"
|
|
104
|
-
| (string & {});
|
|
105
|
-
|
|
106
|
-
/** UI 请求(session-runner 构造后传给 handler)。
|
|
107
|
-
*
|
|
108
|
-
* method 是判别字段,决定排队策略(dialog 排队)和业务路由(channel 分发)。
|
|
109
|
-
* method 特定字段按 method 可选出现(与 ExtensionUiRequest 1:1,由 session-runner 从
|
|
110
|
-
* ExtensionUiRequest 平铺构造)。channel/channelPayload 由 parseChannel 填充。
|
|
111
|
-
*
|
|
112
|
-
* 契约来源:.fix-plans/00-master-summary.md §二 2.2。 */
|
|
113
|
-
export interface UiRequest {
|
|
114
|
-
/** Pi rpc-types.ts 的 method(select/confirm/input/editor 为 dialog 类)。 */
|
|
115
|
-
method: UiMethod;
|
|
116
|
-
/** 请求 id(从 extension_ui_request envelope 顶层提取,用于 response 关联)。 */
|
|
117
|
-
id: string;
|
|
118
|
-
// method 特定字段(按 method 可选,与 ExtensionUiRequest 1:1)
|
|
119
|
-
title?: string;
|
|
120
|
-
options?: string[];
|
|
121
|
-
message?: string;
|
|
122
|
-
placeholder?: string;
|
|
123
|
-
prefill?: string;
|
|
124
|
-
notifyType?: string;
|
|
125
|
-
statusKey?: string;
|
|
126
|
-
statusText?: string | undefined;
|
|
127
|
-
widgetKey?: string;
|
|
128
|
-
widgetLines?: string[] | undefined;
|
|
129
|
-
widgetPlacement?: "aboveEditor" | "belowEditor";
|
|
130
|
-
text?: string;
|
|
131
|
-
timeout?: number;
|
|
132
|
-
/** channel 名(从 method 对应字段的 NUL 前缀解析)。
|
|
133
|
-
* select → 从 title 解析;setWidget → 从 widgetLines[0] 解析;其他 → undefined。
|
|
134
|
-
* 已知值:"ask_user"(select)、"gui_widget"(setWidget)。handler 按 channel 分发。 */
|
|
135
|
-
channel?: string;
|
|
136
|
-
/** channel 解析后的结构化 payload(已 JSON.parse)。
|
|
137
|
-
* ask_user: {questions, allowCancel};gui_widget: {component};无 channel: undefined。 */
|
|
138
|
-
channelPayload?: unknown;
|
|
139
|
-
/** 内部元数据字段:发起该 UI 请求的子进程 pid(由 session-runner.handleUiRequest 从
|
|
140
|
-
* child.pid 填入)。L2 队列据此关联 rejectChildDialogs(child close 时批量 reject)。
|
|
141
|
-
* 下划线前缀表示内部字段,非 Pi 协议字段,不参与 stdin 回写。 */
|
|
142
|
-
_childPid?: number;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** UI 响应(handler 返回,session-runner 按 shape 回写 stdin)。
|
|
146
|
-
* - {value}: select/input/editor 的答案
|
|
147
|
-
* - {confirmed}: confirm 的答案
|
|
148
|
-
* - {cancelled}: 取消(child close / handler 抛错 / 用户取消)
|
|
149
|
-
* - {ack}: fire-and-forget(当前不透传到 TUI,留作协议完整) */
|
|
150
|
-
export type UiResponse =
|
|
151
|
-
| { value: string }
|
|
152
|
-
| { confirmed: boolean }
|
|
153
|
-
| { cancelled: true }
|
|
154
|
-
| { ack: true };
|
|
155
|
-
|
|
156
|
-
/** UI 请求 handler 签名(单函数,按 req.method 内部路由)。
|
|
157
|
-
* 实现方负责:channel 业务路由(ask_user → AskUserComponent)+ 默认转发(ctx.ui.*)。
|
|
158
|
-
* 抛错由调用方(DialogGlobalQueue / session-runner)兜底为 {cancelled:true}。 */
|
|
159
|
-
export type UiRequestHandler = (req: UiRequest) => Promise<UiResponse>;
|
|
87
|
+
// ── 类型再导出(W7 / R3 S-A + R4-S③:SSOT 迁 @zhushanwen/subagent-engine-sdk
|
|
88
|
+
// src/ui-types.ts,结构等价;队列实现本体留 core——壳侧 host/askUser 消费端与
|
|
89
|
+
// inproc 过渡链路同样消费该类型面。双向可赋值断言挂本包 typecheck 断言族
|
|
90
|
+
// (contract-closure),漂移编译期可抓。 ──
|
|
91
|
+
export type { UiMethod, UiRequest, UiRequestHandler, UiResponse } from "@zhushanwen/subagent-engine-sdk";
|
|
92
|
+
import type { UiRequest, UiRequestHandler, UiResponse } from "@zhushanwen/subagent-engine-sdk";
|
|
160
93
|
|
|
161
94
|
// ── DialogGlobalQueue 实现 ──
|
|
162
95
|
|