@zhushanwen/subagent-core 0.4.0 → 0.5.1
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/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
- package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
- package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
- package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
- package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
- package/dist/execution/engine/engines/zcode/constants.js +17 -27
- package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
- package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
- package/dist/execution/engine/engines/zcode/reader.js +2 -2
- package/dist/index.cjs +16418 -14070
- package/dist/index.d.cts +5136 -4016
- package/dist/index.d.ts +5136 -4016
- package/dist/index.js +16343 -14048
- package/dist/{types-DpUO16pj.d.cts → types-Dv4QhSJ_.d.cts} +215 -112
- package/dist/{types-DpUO16pj.d.ts → types-Dv4QhSJ_.d.ts} +215 -112
- package/package.json +3 -3
- package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
- package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
- package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
- package/src/__tests__/m2-append-content-probe.test.ts +0 -1
- package/src/__tests__/manifest-store.test.ts +10 -9
- package/src/__tests__/record-store-cache.test.ts +0 -2
- package/src/__tests__/record-store-index.test.ts +1 -2
- package/src/__tests__/review-fix-loop-script.test.ts +4 -2
- package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
- package/src/__tests__/robustness-low-batch1.test.ts +6 -5
- package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
- package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
- package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
- package/src/__tests__/session-runner.test.ts +1 -1
- package/src/core/__tests__/host-services.test.ts +2 -2
- package/src/core/__tests__/logger.test.ts +1 -1
- package/src/core/error-message.ts +9 -0
- package/src/core/host-services.ts +21 -5
- package/src/core/notify-ports.ts +18 -5
- package/src/execution/__tests__/agent-profile.test.ts +4 -4
- package/src/execution/__tests__/agent-registry.test.ts +370 -0
- package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
- package/src/execution/__tests__/agents-assembly.test.ts +2 -2
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
- package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
- package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
- package/src/execution/__tests__/delivery-methods.test.ts +75 -62
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
- package/src/execution/__tests__/dialog-queue.test.ts +2 -2
- package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
- package/src/execution/__tests__/execute-nesting.test.ts +34 -188
- package/src/execution/__tests__/execution-record.test.ts +217 -60
- package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
- package/src/execution/__tests__/finalize-record.test.ts +173 -17
- package/src/execution/__tests__/gc-timer.test.ts +55 -2
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
- package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
- package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
- package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
- package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
- package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
- package/src/execution/__tests__/model-resolver.test.ts +2 -2
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
- package/src/execution/__tests__/nested-visibility.test.ts +2 -0
- package/src/execution/__tests__/notify-ledger.test.ts +973 -18
- package/src/execution/__tests__/output-collector.test.ts +117 -6
- package/src/execution/__tests__/pi-invocation.test.ts +1 -1
- package/src/execution/__tests__/record-store.test.ts +10 -9
- package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
- package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
- package/src/execution/__tests__/rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
- package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
- package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
- package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
- package/src/execution/__tests__/session-file-gc.test.ts +35 -0
- package/src/execution/__tests__/session-pending.test.ts +257 -14
- package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
- package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
- package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
- package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
- package/src/execution/__tests__/spawn-args.test.ts +11 -10
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
- package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
- package/src/execution/__tests__/spawned-children.test.ts +2 -1
- package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
- package/src/execution/__tests__/stdin-writer.test.ts +6 -4
- package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
- package/src/execution/__tests__/subagent-actions-core.test.ts +42 -22
- package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +8 -8
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
- package/src/execution/__tests__/subagent-service.test.ts +37 -30
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
- package/src/execution/__tests__/temp-prompt.test.ts +1 -1
- package/src/execution/__tests__/timeout-integration.test.ts +6 -5
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
- package/src/execution/__tests__/turn-limiter.test.ts +1 -1
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
- package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
- package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
- package/src/execution/__tests__/worktree-manager.test.ts +14 -5
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
- package/src/execution/agent-registry.ts +74 -30
- package/src/execution/agent-result-mapper.ts +3 -0
- package/src/execution/cold-resurrect.ts +199 -0
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
- package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
- package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
- package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
- package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
- package/src/execution/engine/__tests__/registry.test.ts +4 -2
- package/src/execution/engine/__tests__/routing.test.ts +115 -6
- package/src/execution/engine/common/capability-gate.ts +92 -0
- package/src/execution/engine/common/journal-wiring.ts +97 -0
- package/src/execution/engine/common/kill-chain.ts +42 -10
- package/src/execution/engine/common/nesting-guard.ts +66 -0
- package/src/execution/engine/common/persona-router.ts +18 -8
- package/src/execution/engine/common/pool-manager.ts +332 -78
- package/src/execution/engine/common/session-view-service.ts +506 -0
- package/src/execution/engine/common/session-view-types.ts +125 -0
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
- package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
- package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
- package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
- package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
- package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
- package/src/execution/engine/engines/pi/reader.ts +11 -0
- package/src/execution/engine/engines/pi/registration.ts +4 -2
- package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
- package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
- package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
- package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
- package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +25 -319
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
- package/src/execution/engine/engines/zcode/connection.ts +24 -11
- package/src/execution/engine/engines/zcode/constants.ts +75 -71
- package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
- package/src/execution/engine/engines/zcode/parser.ts +12 -346
- package/src/execution/engine/engines/zcode/preparer.ts +12 -143
- package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
- package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
- package/src/execution/engine/host-task-spec.ts +32 -36
- package/src/execution/engine/model-validation.ts +177 -0
- package/src/execution/engine/port.ts +24 -6
- package/src/execution/engine/routing.ts +75 -1
- package/src/execution/engine/types.ts +30 -103
- package/src/execution/execution-record.ts +222 -144
- package/src/execution/finalize-record.ts +131 -93
- package/src/execution/idle-gc.ts +28 -0
- package/src/execution/lifecycle-predicates.ts +1 -1
- package/src/execution/notify-host.ts +212 -0
- package/src/execution/record-store.ts +119 -73
- package/src/execution/round-settlement.ts +93 -0
- package/src/execution/session-file-gc.ts +96 -62
- package/src/execution/session-reconstructor.ts +256 -143
- package/src/execution/sessions-index.ts +117 -47
- package/src/execution/settled-watchdog.ts +264 -52
- package/src/execution/subagent-actions-core.ts +22 -22
- package/src/execution/subagent-service.ts +815 -963
- package/src/execution/subprocess-agent-runner.ts +144 -97
- package/src/execution/types.ts +9 -0
- package/src/execution/ui-request-handler-factory.ts +102 -74
- package/src/execution/ui-request-queue.ts +50 -19
- package/src/index.ts +442 -316
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
- package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
- package/src/orchestration/__tests__/args-validator.test.ts +14 -0
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
- package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
- package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
- package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
- package/src/orchestration/__tests__/script-lint.test.ts +17 -0
- package/src/orchestration/__tests__/test-mocks.ts +2 -0
- package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
- package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
- package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
- package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
- package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
- package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
- package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
- package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
- package/src/orchestration/agent-opts-resolver.ts +1 -1
- package/src/orchestration/args-validator.ts +53 -31
- package/src/orchestration/execute-agent-call.ts +19 -73
- package/src/orchestration/launcher.ts +128 -81
- package/src/orchestration/lifecycle.ts +43 -45
- package/src/orchestration/models/ports.ts +4 -4
- package/src/orchestration/models/run-runtime.ts +2 -2
- package/src/orchestration/models/trace.ts +1 -1
- package/src/orchestration/models/types.ts +111 -22
- package/src/orchestration/models/workflow-run.ts +1 -1
- package/src/orchestration/script-lint.ts +91 -58
- package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
- package/src/shared/__tests__/meta-parser.test.ts +151 -0
- package/src/shared/__tests__/resource-discovery.test.ts +258 -215
- package/src/shared/atomic-write.ts +12 -9
- package/src/shared/meta-parser.ts +158 -84
- package/src/shared/model-ref.ts +15 -2
- package/src/shared/resource-discovery.ts +9 -183
- package/src/shared/schema-jsonify.ts +1 -1
- package/workflows/review-fix-loop-utils.cjs +151 -89
- package/workflows/review-fix-loop-utils.d.cts +287 -0
- package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
- 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__/preparer-appserver.test.ts +0 -387
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
- package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
- package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/execute-options-mapper.ts +0 -115
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
|
@@ -71,7 +71,7 @@ vi.mock("../alive-store.ts", () => ({
|
|
|
71
71
|
writeAliveMarker: vi.fn(),
|
|
72
72
|
}));
|
|
73
73
|
|
|
74
|
-
vi.mock("../temp-prompt.ts", () => ({
|
|
74
|
+
vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
|
|
75
75
|
writePromptToTempFile: vi.fn(async (agent: string, content: string) => {
|
|
76
76
|
captured.content = content;
|
|
77
77
|
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
@@ -83,7 +83,7 @@ vi.mock("../temp-prompt.ts", () => ({
|
|
|
83
83
|
import { spawn } from "node:child_process";
|
|
84
84
|
|
|
85
85
|
import { createRecord } from "../execution-record.ts";
|
|
86
|
-
import { runSpawn, type RunOptions, type SessionRunnerContext } from "../session-runner.ts";
|
|
86
|
+
import { runSpawn, type RunOptions, type SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
|
|
87
87
|
|
|
88
88
|
const mockSpawn = vi.mocked(spawn);
|
|
89
89
|
|
|
@@ -117,8 +117,9 @@ function makeRecord() {
|
|
|
117
117
|
return createRecord("wt-guidance-1", {
|
|
118
118
|
agent: "general-purpose",
|
|
119
119
|
model: "test/model",
|
|
120
|
-
mode: "
|
|
120
|
+
mode: "background",
|
|
121
121
|
task: "test task",
|
|
122
|
+
slug: "test",
|
|
122
123
|
startedAt: Date.now(),
|
|
123
124
|
rootSessionId: "s1",
|
|
124
125
|
parentRecordId: undefined,
|
|
@@ -128,7 +129,7 @@ function makeRecord() {
|
|
|
128
129
|
|
|
129
130
|
function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
|
|
130
131
|
return {
|
|
131
|
-
resolved: { model: { provider: "test", id: "model" }, thinkingLevel: undefined },
|
|
132
|
+
resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
|
|
132
133
|
agentConfig: undefined,
|
|
133
134
|
appendSystemPrompt: undefined,
|
|
134
135
|
skillPath: undefined,
|
|
@@ -29,7 +29,7 @@ vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
|
29
29
|
|
|
30
30
|
import type { EnginePort } from "../engine/port.ts";
|
|
31
31
|
import { clearEngines, getEngine, registerEngine } from "../engine/registry.ts";
|
|
32
|
-
import { getChildByRecord, killAllSpawnedChildren, spawnedChildren } from "../session-runner.ts";
|
|
32
|
+
import { getChildByRecord, killAllSpawnedChildren, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* 假 child:只关心 killed 标记 + kill 调用(killAllSpawnedChildren 遍历用)。
|
|
@@ -63,6 +63,7 @@ function makeDisposingEngine(
|
|
|
63
63
|
resume: "unsupported",
|
|
64
64
|
interrupt: "kill-only",
|
|
65
65
|
permissionMode: "fixed",
|
|
66
|
+
maxTurns: false,
|
|
66
67
|
}),
|
|
67
68
|
probe: () =>
|
|
68
69
|
Promise.resolve({ ok: true, engineVersion: "0.0.0-test", checks: [{ name: "stub", ok: true }] }),
|
|
@@ -30,14 +30,14 @@ vi.mock("node:child_process", async () => {
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
// buildSpawnArgs 包装为 spy(保留真实实现)——「非全等拒单不触达 spawn 参数组装」的承重断言。
|
|
33
|
-
vi.mock("../session-runner.ts", async (importOriginal) => {
|
|
34
|
-
const actual = await importOriginal<typeof import("../session-runner.ts")>();
|
|
33
|
+
vi.mock("../engine/engines/pi/session-runner.ts", async (importOriginal) => {
|
|
34
|
+
const actual = await importOriginal<typeof import("../engine/engines/pi/session-runner.ts")>();
|
|
35
35
|
return { ...actual, buildSpawnArgs: vi.fn(actual.buildSpawnArgs) };
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
import { ModelConfigService } from "../model-config-service.ts";
|
|
39
39
|
import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
|
|
40
|
-
import { buildSpawnArgs } from "../session-runner.ts";
|
|
40
|
+
import { buildSpawnArgs } from "../engine/engines/pi/session-runner.ts";
|
|
41
41
|
import type { PiLike } from "../subagent-service.ts";
|
|
42
42
|
import { SubagentService } from "../subagent-service.ts";
|
|
43
43
|
|
|
@@ -128,7 +128,7 @@ describe("start 同步期裁决(U1 验收 3:非全等 → isError 且无 spa
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it("全等入参通过裁决(reject 不发生,放行链路触达 spawn 参数组装)——守卫零误伤对照", async () => {
|
|
131
|
-
//
|
|
131
|
+
// 放行路径会进入轮次 kick-off(detached);execute resolve 即视为裁决放行。
|
|
132
132
|
// spawn 本身由 FakeChild 缺失抛错收口——本断言只关心「越过了同步裁决」:
|
|
133
133
|
// execute 不 reject,且 buildSpawnArgs 已被调(或即将被调的 record 已创建)。
|
|
134
134
|
const promise = service.execute({
|
|
@@ -33,7 +33,7 @@ vi.mock("../../core/logger.ts", () => ({
|
|
|
33
33
|
getLogger: () => loggerMock,
|
|
34
34
|
}));
|
|
35
35
|
|
|
36
|
-
import { respond, sendGetStateCommand, sendPromptCommand } from "../stdin-writer.ts";
|
|
36
|
+
import { respond, sendGetStateCommand, sendPromptCommand } from "../engine/engines/pi/stdin-writer.ts";
|
|
37
37
|
|
|
38
38
|
// ── helpers ──
|
|
39
39
|
|
|
@@ -362,8 +362,10 @@ describe("sendGetStateCommand", () => {
|
|
|
362
362
|
describe("writeStdinLine 背压 — write 返回 false 时 warn 不 throw", () => {
|
|
363
363
|
it("child.stdin.write 返回 false → warn 一次,函数正常返回(不 throw)", () => {
|
|
364
364
|
// 构造 write 永远返回 false 的假 stream(模拟内核缓冲满/HWM 到达)
|
|
365
|
+
// 保留 vi.fn 引用(经 PassThrough 视图后 write 的 mock 面不可达)
|
|
366
|
+
const fakeWrite = vi.fn((_chunk: unknown) => false);
|
|
365
367
|
const fakeStdin = {
|
|
366
|
-
write:
|
|
368
|
+
write: fakeWrite,
|
|
367
369
|
destroyed: false,
|
|
368
370
|
} as unknown as PassThrough;
|
|
369
371
|
const child = { stdin: fakeStdin } as unknown as ChildProcess;
|
|
@@ -371,8 +373,8 @@ describe("writeStdinLine 背压 — write 返回 false 时 warn 不 throw", () =
|
|
|
371
373
|
expect(() => respond(child, "req-backpressure", { value: "x" })).not.toThrow();
|
|
372
374
|
|
|
373
375
|
// write 被调一次(写入 command 行)
|
|
374
|
-
expect(
|
|
375
|
-
const writtenArg =
|
|
376
|
+
expect(fakeWrite).toHaveBeenCalledTimes(1);
|
|
377
|
+
const writtenArg = fakeWrite.mock.calls[0]![0] as string;
|
|
376
378
|
expect(writtenArg).toContain("extension_ui_response");
|
|
377
379
|
expect(writtenArg).toContain("req-backpressure");
|
|
378
380
|
expect(writtenArg.endsWith("\n")).toBe(true);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// widget 私货通道退役步骤 2(E 方案 §6.3)测试,两层:
|
|
4
4
|
// 判定层:createBackgroundStream 真实函数(vi.importActual 绕过文件级 mock)——
|
|
5
5
|
// gui+relay 激活不创建;tui / relay 未激活 / headless 原样创建;sink=null 恒降级。
|
|
6
|
-
// 接线层:SubagentService.execute(background →
|
|
6
|
+
// 接线层:SubagentService.execute(background → kickOffChatRound 同步段)透传
|
|
7
7
|
// mode/streamSink 给工厂——mode 传错 = 判定失效,故接线面必须锁定。
|
|
8
8
|
//
|
|
9
9
|
// spawn 层 mock(FakeChild),与 nested-visibility-env-propagation.test.ts 同模式。
|
|
@@ -103,7 +103,7 @@ vi.mock("../manifest-store.ts", () => {
|
|
|
103
103
|
return { ManifestStore: vi.fn(function (_recordsDir: string) { return new FakeManifestStore(); }) };
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
vi.mock("../temp-prompt.ts", () => ({
|
|
106
|
+
vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
|
|
107
107
|
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
108
108
|
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
109
109
|
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
@@ -156,7 +156,7 @@ const ctxModel: ModelInfo = { id: "m", name: "M", provider: "p", reasoning: fals
|
|
|
156
156
|
|
|
157
157
|
function setupService(mode: ExtensionMode | undefined, sessionId = "retire-it"): SubagentService {
|
|
158
158
|
const agentDir = "/tmp/stream-retire-it";
|
|
159
|
-
const modelService = new ModelConfigService({ agentDir });
|
|
159
|
+
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
160
160
|
modelService.initModel({
|
|
161
161
|
modelRegistry: makeEmptyRegistry(),
|
|
162
162
|
sessionId,
|
|
@@ -176,7 +176,7 @@ function setupService(mode: ExtensionMode | undefined, sessionId = "retire-it"):
|
|
|
176
176
|
return service;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
/** 等
|
|
179
|
+
/** 等 kickOffChatRound 的 detached 轮次执行走到 spawn(stream 创建先于此)。 */
|
|
180
180
|
async function waitForSpawnCall(): Promise<void> {
|
|
181
181
|
const baseline = mockSpawn.mock.calls.length;
|
|
182
182
|
const deadline = Date.now() + 2000;
|
|
@@ -187,7 +187,7 @@ async function waitForSpawnCall(): Promise<void> {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
async function executeBackground(service: SubagentService): Promise<void> {
|
|
190
|
-
await service.execute({ task: "retire test", ctxModel });
|
|
190
|
+
await service.execute({ task: "retire test", slug: "test", ctxModel });
|
|
191
191
|
await waitForSpawnCall();
|
|
192
192
|
// 推进 detached runSpawn 收尾(FakeChild close),防句柄悬挂干扰后续用例
|
|
193
193
|
lastSpawnedChild().emit("close", 0);
|
|
@@ -234,7 +234,7 @@ describe("退役步骤 2 判定层:createBackgroundStream", () => {
|
|
|
234
234
|
|
|
235
235
|
// ── 接线层:service → 工厂的参数透传(mode 传错 = 判定失效)──
|
|
236
236
|
|
|
237
|
-
describe("退役步骤 2 接线层:
|
|
237
|
+
describe("退役步骤 2 接线层:kickOffChatRound 透传 mode/sink 给工厂", () => {
|
|
238
238
|
beforeEach(() => {
|
|
239
239
|
vi.clearAllMocks();
|
|
240
240
|
for (const key of RELAY_ENV_KEYS) delete process.env[key];
|
|
@@ -42,9 +42,9 @@ import type {
|
|
|
42
42
|
ExecutionHandle,
|
|
43
43
|
ExecutionRecord,
|
|
44
44
|
SubagentRecord,
|
|
45
|
-
SubagentService,
|
|
46
45
|
SubagentToolDetails,
|
|
47
46
|
} from "../types.ts";
|
|
47
|
+
import type { SubagentService } from "../subagent-service.ts";
|
|
48
48
|
|
|
49
49
|
// ── 时钟固定(duration 快照确定性,见文件头)──
|
|
50
50
|
const FROZEN_NOW = 1788189209000;
|
|
@@ -132,17 +132,35 @@ function makeRec(over: Partial<SubagentRecord> = {}): SubagentRecord {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function makeService(over: Record<string, unknown> = {}): SubagentService {
|
|
135
|
-
|
|
135
|
+
// 聚合面形态对齐 subagent-service.ts D4:queries(读模型)与 chatActions(对话
|
|
136
|
+
// action 面)分组挂载,mock 按同构 key 装配(over 仍传平铺 key,最小化快照用例改动)。
|
|
137
|
+
const m = {
|
|
136
138
|
execute: vi.fn(),
|
|
137
|
-
findRecord: vi.fn(() => undefined),
|
|
138
139
|
cancel: vi.fn(() => false),
|
|
140
|
+
findRecord: vi.fn(() => undefined),
|
|
139
141
|
collectRecords: vi.fn(() => [] as SubagentRecord[]),
|
|
140
142
|
getFullRecord: vi.fn(() => undefined as SubagentRecord | undefined),
|
|
141
143
|
lookupRecordAnyState: vi.fn(() => undefined as SubagentRecord | undefined),
|
|
142
144
|
getRecordForAction: vi.fn(),
|
|
143
145
|
closeSubagent: vi.fn(),
|
|
144
|
-
|
|
146
|
+
deliverChatMessage: vi.fn(),
|
|
145
147
|
...over,
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
execute: m.execute,
|
|
151
|
+
cancel: m.cancel,
|
|
152
|
+
queries: {
|
|
153
|
+
findRecord: m.findRecord,
|
|
154
|
+
lookupRecordAnyState: m.lookupRecordAnyState,
|
|
155
|
+
collectRecords: m.collectRecords,
|
|
156
|
+
getFullRecord: m.getFullRecord,
|
|
157
|
+
onChange: vi.fn(() => () => {}),
|
|
158
|
+
},
|
|
159
|
+
chatActions: {
|
|
160
|
+
getRecordForAction: m.getRecordForAction,
|
|
161
|
+
closeSubagent: m.closeSubagent,
|
|
162
|
+
deliverChatMessage: m.deliverChatMessage,
|
|
163
|
+
},
|
|
146
164
|
} as unknown as SubagentService;
|
|
147
165
|
}
|
|
148
166
|
|
|
@@ -290,7 +308,7 @@ describe("⛔4 startHandler(校验 + 启动,快照 = pi-sw 实测)", () =>
|
|
|
290
308
|
});
|
|
291
309
|
|
|
292
310
|
it("正常启动 → 领域对象全字段 + execute 参数(task/slug trim、拍平透传、ctxModel/signal)", async () => {
|
|
293
|
-
const execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
311
|
+
const execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
294
312
|
mode: "background",
|
|
295
313
|
subagentId: "bg-9-abc",
|
|
296
314
|
sessionFile: "sess-9.jsonl",
|
|
@@ -334,7 +352,7 @@ describe("⛔4 startHandler(校验 + 启动,快照 = pi-sw 实测)", () =>
|
|
|
334
352
|
},
|
|
335
353
|
});
|
|
336
354
|
expect(execute).toHaveBeenCalledTimes(1);
|
|
337
|
-
expect(execute.mock.calls[0][0]).toEqual({
|
|
355
|
+
expect(execute.mock.calls[0]![0]).toEqual({
|
|
338
356
|
task: "long task", // trim 生效
|
|
339
357
|
slug: "long-running",
|
|
340
358
|
agent: "/x/agent.md",
|
|
@@ -489,6 +507,8 @@ describe("⛔4 cancelHandler(守卫 + 归属判定 + CAS 失败映射,快照
|
|
|
489
507
|
expect(
|
|
490
508
|
await errOf(() =>
|
|
491
509
|
cancelHandler(
|
|
510
|
+
// "sync" 是历史已裁撤值(ExecutionMode 现仅 "background")——经 as never
|
|
511
|
+
// 注入非法值专测守卫分支,运行时守卫按 !== "background" 拦截
|
|
492
512
|
makeService({ findRecord: vi.fn(() => makeRec({ id: "bg-1", mode: "sync" as never })) }),
|
|
493
513
|
{ subagentId: "bg-1" },
|
|
494
514
|
),
|
|
@@ -587,11 +607,11 @@ describe("⛔4 messageHandler(守卫 + upgrade + 投递,快照 = pi-sw 实
|
|
|
587
607
|
});
|
|
588
608
|
});
|
|
589
609
|
|
|
590
|
-
it("chatMode record →
|
|
591
|
-
const
|
|
610
|
+
it("chatMode record → deliverChatMessage(text trim + interrupt) + 领域对象", async () => {
|
|
611
|
+
const deliverChatMessage = vi.fn(async () => {});
|
|
592
612
|
const chatRecord = makeExecRecord({ id: "bg-1", chatMode: true, slug: "src-slug" });
|
|
593
613
|
const r = await messageHandler(
|
|
594
|
-
makeService({ getRecordForAction: vi.fn(() => chatRecord),
|
|
614
|
+
makeService({ getRecordForAction: vi.fn(() => chatRecord), deliverChatMessage }),
|
|
595
615
|
{ subagentId: "bg-1", text: " go on ", interrupt: true },
|
|
596
616
|
);
|
|
597
617
|
expect(r).toEqual({
|
|
@@ -600,18 +620,18 @@ describe("⛔4 messageHandler(守卫 + upgrade + 投递,快照 = pi-sw 实
|
|
|
600
620
|
slug: "src-slug",
|
|
601
621
|
response: { delivered: true },
|
|
602
622
|
});
|
|
603
|
-
expect(
|
|
623
|
+
expect(deliverChatMessage).toHaveBeenCalledWith(chatRecord, "go on", true);
|
|
604
624
|
});
|
|
605
625
|
|
|
606
626
|
it("one-shot upgrade:非 chatMode running record 收 message → 置位 chatMode 后投递", async () => {
|
|
607
|
-
const
|
|
627
|
+
const deliverChatMessage = vi.fn(async () => {});
|
|
608
628
|
const upgradeRec = makeExecRecord({ id: "bg-1", chatMode: false, status: "running" });
|
|
609
629
|
await messageHandler(
|
|
610
|
-
makeService({ getRecordForAction: vi.fn(() => upgradeRec),
|
|
630
|
+
makeService({ getRecordForAction: vi.fn(() => upgradeRec), deliverChatMessage }),
|
|
611
631
|
{ subagentId: "bg-1", text: "hi" },
|
|
612
632
|
);
|
|
613
633
|
expect(upgradeRec.chatMode).toBe(true);
|
|
614
|
-
expect(
|
|
634
|
+
expect(deliverChatMessage).toHaveBeenCalledWith(upgradeRec, "hi", false);
|
|
615
635
|
});
|
|
616
636
|
|
|
617
637
|
it("getRecordForAction 拒绝 + 无终态快照 → 原错误透传(文案最准原则)", async () => {
|
|
@@ -774,7 +794,7 @@ describe("⛔4 closeHandler(force 语义透传,快照 = pi-sw 实测)", ()
|
|
|
774
794
|
// forkFromHandler(守卫链 1–6)
|
|
775
795
|
// ============================================================
|
|
776
796
|
describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快照 = pi-sw 实测)", () => {
|
|
777
|
-
function makeForkService(source: SubagentRecord | undefined, execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
797
|
+
function makeForkService(source: SubagentRecord | undefined, execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
778
798
|
mode: "background",
|
|
779
799
|
subagentId: "bg-new-1",
|
|
780
800
|
sessionFile: "sess-new.jsonl",
|
|
@@ -899,7 +919,7 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
899
919
|
});
|
|
900
920
|
|
|
901
921
|
it("正常:显式 prompt → wrapForkFromPrompt 包装 + slug 派生 'src-slug-resumed' + execute 参数", async () => {
|
|
902
|
-
const execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
922
|
+
const execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
903
923
|
mode: "background",
|
|
904
924
|
subagentId: "bg-new-1",
|
|
905
925
|
sessionFile: "sess-new.jsonl",
|
|
@@ -928,7 +948,7 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
928
948
|
});
|
|
929
949
|
|
|
930
950
|
it("无 prompt → FORK_FROM_DEFAULT_PROMPT 逐字", async () => {
|
|
931
|
-
const execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
951
|
+
const execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
932
952
|
mode: "background",
|
|
933
953
|
subagentId: "bg-new-2",
|
|
934
954
|
sessionFile: "sess-new-2.jsonl",
|
|
@@ -955,7 +975,7 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
955
975
|
});
|
|
956
976
|
|
|
957
977
|
it("slug 派生:34 字符源 slug 截 27 + '-resumed';fallback 链 slug||agent||'resumed'", async () => {
|
|
958
|
-
const execute34 = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
978
|
+
const execute34 = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
959
979
|
mode: "background",
|
|
960
980
|
subagentId: "bg-new-3",
|
|
961
981
|
sessionFile: undefined,
|
|
@@ -965,9 +985,9 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
965
985
|
makeForkService(makeRec({ id: "bg-1", slug: "s".repeat(34), sessionFile: "sess-1.jsonl" }), execute34),
|
|
966
986
|
{ sourceSubagentId: "bg-1", prompt: "p" },
|
|
967
987
|
);
|
|
968
|
-
expect(execute34.mock.calls[0][0].slug).toBe(`${"s".repeat(27)}-resumed`);
|
|
988
|
+
expect(execute34.mock.calls[0]![0].slug).toBe(`${"s".repeat(27)}-resumed`);
|
|
969
989
|
|
|
970
|
-
const executeAgent = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
990
|
+
const executeAgent = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
971
991
|
mode: "background",
|
|
972
992
|
subagentId: "bg-new-4",
|
|
973
993
|
sessionFile: undefined,
|
|
@@ -980,9 +1000,9 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
980
1000
|
),
|
|
981
1001
|
{ sourceSubagentId: "bg-1", prompt: "p" },
|
|
982
1002
|
);
|
|
983
|
-
expect(executeAgent.mock.calls[0][0].slug).toBe("/home/u/agents/helper.md-resumed");
|
|
1003
|
+
expect(executeAgent.mock.calls[0]![0].slug).toBe("/home/u/agents/helper.md-resumed");
|
|
984
1004
|
|
|
985
|
-
const executeResumed = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
1005
|
+
const executeResumed = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
986
1006
|
mode: "background",
|
|
987
1007
|
subagentId: "bg-new-5",
|
|
988
1008
|
sessionFile: undefined,
|
|
@@ -992,6 +1012,6 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
992
1012
|
makeForkService(makeRec({ id: "bg-1", slug: "", agent: "", sessionFile: "sess-1.jsonl" }), executeResumed),
|
|
993
1013
|
{ sourceSubagentId: "bg-1", prompt: "p" },
|
|
994
1014
|
);
|
|
995
|
-
expect(executeResumed.mock.calls[0][0].slug).toBe("resumed-resumed");
|
|
1015
|
+
expect(executeResumed.mock.calls[0]![0].slug).toBe("resumed-resumed");
|
|
996
1016
|
});
|
|
997
1017
|
});
|
|
@@ -20,7 +20,7 @@ const { loggerMock, killEscalationSpy } = vi.hoisted(() => ({
|
|
|
20
20
|
vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
21
21
|
|
|
22
22
|
// mock session-runner(import 链需要 runSpawn/killAllSpawnedChildren/getChildByRecord 存在)
|
|
23
|
-
vi.mock("../session-runner.ts", () => ({
|
|
23
|
+
vi.mock("../engine/engines/pi/session-runner.ts", () => ({
|
|
24
24
|
runSpawn: vi.fn(),
|
|
25
25
|
killAllSpawnedChildren: vi.fn(),
|
|
26
26
|
getChildByRecord: vi.fn(() => undefined),
|
|
@@ -30,12 +30,13 @@ vi.mock("../session-runner.ts", () => ({
|
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
32
|
import { createRecord, updateFromEvent } from "../execution-record.ts";
|
|
33
|
+
import { createNotifyHost } from "../notify-host.ts";
|
|
33
34
|
import { ModelConfigService } from "../model-config-service.ts";
|
|
34
35
|
import { RecordStore } from "../record-store.ts";
|
|
35
36
|
import { SubagentService } from "../subagent-service.ts";
|
|
36
37
|
import type { PiLike } from "../subagent-service.ts";
|
|
37
38
|
import type { ExecutionRecord } from "../types.ts";
|
|
38
|
-
import { getChildByRecord } from "../session-runner.ts";
|
|
39
|
+
import { getChildByRecord } from "../engine/engines/pi/session-runner.ts";
|
|
39
40
|
|
|
40
41
|
// [T2④] kill 收敛入口 spy(工厂引用,断言服务侧三条路径收敛到它)
|
|
41
42
|
const killRecordChildWithEscalationMock = killEscalationSpy;
|
|
@@ -95,7 +96,7 @@ function setup(): {
|
|
|
95
96
|
pi: MockPi;
|
|
96
97
|
} {
|
|
97
98
|
const agentDir = makeTmpAgentDir();
|
|
98
|
-
const modelService = new ModelConfigService({ agentDir });
|
|
99
|
+
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
99
100
|
const service = new SubagentService({ cwd: agentDir, modelService });
|
|
100
101
|
// [C2] pi 提为外层引用(对照 real-chain :139-140 形态)——close 现状语义断言需读
|
|
101
102
|
// pi.sendMessage.mock.calls(末条通知 content 指针行),内联构造不保留引用。
|
|
@@ -121,23 +122,23 @@ describe("getRecordForAction 归属守卫(决策 3)", () => {
|
|
|
121
122
|
|
|
122
123
|
afterEach(() => {
|
|
123
124
|
service.dispose();
|
|
124
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
125
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
125
126
|
});
|
|
126
127
|
|
|
127
128
|
it("正常(rootSessionId 匹配)→ 返回可变 record", () => {
|
|
128
129
|
const record = makeRecord({ rootSessionId: sessionRootId });
|
|
129
130
|
store.register(record);
|
|
130
|
-
expect(service.getRecordForAction(record.id)).toBe(record);
|
|
131
|
+
expect(service.chatActions.getRecordForAction(record.id)).toBe(record);
|
|
131
132
|
});
|
|
132
133
|
|
|
133
134
|
it("不存在 → throw not found or not owned", () => {
|
|
134
|
-
expect(() => service.getRecordForAction("sa-nonexistent")).toThrow(/not found or not owned/);
|
|
135
|
+
expect(() => service.chatActions.getRecordForAction("sa-nonexistent")).toThrow(/not found or not owned/);
|
|
135
136
|
});
|
|
136
137
|
|
|
137
138
|
it("rootSessionId 不匹配 → throw not found or not owned(不区分 not found vs not owned,防信息泄露)", () => {
|
|
138
139
|
const record = makeRecord({ id: "sa-other", rootSessionId: "other-session" });
|
|
139
140
|
store.register(record);
|
|
140
|
-
expect(() => service.getRecordForAction("sa-other")).toThrow(/not found or not owned/);
|
|
141
|
+
expect(() => service.chatActions.getRecordForAction("sa-other")).toThrow(/not found or not owned/);
|
|
141
142
|
});
|
|
142
143
|
});
|
|
143
144
|
|
|
@@ -157,7 +158,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
157
158
|
|
|
158
159
|
afterEach(() => {
|
|
159
160
|
service.dispose();
|
|
160
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
161
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
161
162
|
});
|
|
162
163
|
|
|
163
164
|
it("running + force:false + 有活进程 → 置 closeAfterRound=true(不立即终态,等轮完)", async () => {
|
|
@@ -167,7 +168,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
167
168
|
const record = makeRecord({ status: "running" });
|
|
168
169
|
store.register(record);
|
|
169
170
|
|
|
170
|
-
await service.closeSubagent(record, false);
|
|
171
|
+
await service.chatActions.closeSubagent(record, false);
|
|
171
172
|
|
|
172
173
|
expect(record.closeAfterRound).toBe(true);
|
|
173
174
|
expect(record.status).toBe("running"); // 仍 running
|
|
@@ -181,7 +182,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
181
182
|
record.sessionFile = path.join(agentDir, "test.jsonl");
|
|
182
183
|
store.register(record);
|
|
183
184
|
|
|
184
|
-
await service.closeSubagent(record, false);
|
|
185
|
+
await service.chatActions.closeSubagent(record, false);
|
|
185
186
|
|
|
186
187
|
expect(record.status).toBe("closed");
|
|
187
188
|
expect(store.getMutable(record.id)).toBeUndefined(); // archived
|
|
@@ -191,7 +192,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
191
192
|
const record = makeRecord({ status: "running", sessionFile: path.join(agentDir, "run.jsonl") });
|
|
192
193
|
store.register(record);
|
|
193
194
|
|
|
194
|
-
await service.closeSubagent(record, true);
|
|
195
|
+
await service.chatActions.closeSubagent(record, true);
|
|
195
196
|
|
|
196
197
|
// v4 B-1:cancelled 折入 closed(closedReason='cancelled' 区分用户取消语义)
|
|
197
198
|
expect(record.status).toBe("closed");
|
|
@@ -203,7 +204,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
203
204
|
const record = makeRecord({ status: "closed" });
|
|
204
205
|
store.register(record);
|
|
205
206
|
|
|
206
|
-
await service.closeSubagent(record, false);
|
|
207
|
+
await service.chatActions.closeSubagent(record, false);
|
|
207
208
|
|
|
208
209
|
expect(record.status).toBe("closed"); // 不变
|
|
209
210
|
});
|
|
@@ -223,7 +224,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
223
224
|
expect(hasIdleTimer(record.id)).toBe(true);
|
|
224
225
|
|
|
225
226
|
try {
|
|
226
|
-
await service.closeSubagent(record, false);
|
|
227
|
+
await service.chatActions.closeSubagent(record, false);
|
|
227
228
|
} finally {
|
|
228
229
|
disarmIdleTimer(record.id); // 断言失败路径的兜底清理
|
|
229
230
|
}
|
|
@@ -242,7 +243,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
242
243
|
record.sessionFile = path.join(agentDir, "path-b.jsonl");
|
|
243
244
|
store.register(record);
|
|
244
245
|
|
|
245
|
-
await service.closeSubagent(record, false);
|
|
246
|
+
await service.chatActions.closeSubagent(record, false);
|
|
246
247
|
|
|
247
248
|
expect(record.status).toBe("closed");
|
|
248
249
|
expect(record.closedReason).toBe("user-close");
|
|
@@ -269,7 +270,7 @@ describe("closeSubagent 行为分流", () => {
|
|
|
269
270
|
expect(hasIdleTimer(record.id)).toBe(true);
|
|
270
271
|
|
|
271
272
|
try {
|
|
272
|
-
await service.closeSubagent(record, true);
|
|
273
|
+
await service.chatActions.closeSubagent(record, true);
|
|
273
274
|
} finally {
|
|
274
275
|
disarmIdleTimer(record.id); // 断言失败路径的兜底清理
|
|
275
276
|
}
|
|
@@ -299,7 +300,7 @@ describe("cancelBackground CAS 抢锁(A2-1)——cancel 迟到不覆写已
|
|
|
299
300
|
|
|
300
301
|
afterEach(() => {
|
|
301
302
|
service.dispose();
|
|
302
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
303
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
303
304
|
});
|
|
304
305
|
|
|
305
306
|
it("record 已 closed 未 archive(doFinalizeRecord Step 0 await 窗口形态)→ cancel 返回 false,不覆写终态/不写 tombstone/不 notify/不 archive", () => {
|
|
@@ -354,7 +355,7 @@ describe("[M5] closeAfterRound 消费点挂 onRoundSettled(chatMode 轮完成
|
|
|
354
355
|
afterEach(() => {
|
|
355
356
|
service.dispose();
|
|
356
357
|
_resetLifecycleState();
|
|
357
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
358
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
358
359
|
});
|
|
359
360
|
|
|
360
361
|
it("热路径轮完成(onRoundSettled)+ closeAfterRound=true → 消费标志终态化(closed+user-close + archive)", async () => {
|
|
@@ -428,7 +429,7 @@ describe("[C2] close 现状语义 + sessionFile 条件透传", () => {
|
|
|
428
429
|
afterEach(() => {
|
|
429
430
|
service.dispose();
|
|
430
431
|
_resetLifecycleState();
|
|
431
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
432
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
432
433
|
});
|
|
433
434
|
|
|
434
435
|
it("close 语义:轮次通知(含指针行)发出后 closeAfterRoundSettled 终态化 + 终态通知,两条都送达", async () => {
|
|
@@ -473,8 +474,10 @@ describe("[C2] close 现状语义 + sessionFile 条件透传", () => {
|
|
|
473
474
|
|
|
474
475
|
// running + 无活进程(getChildByRecord mock undefined)→ isResumable 放行 →
|
|
475
476
|
// 非 chatMode 映射 closed 通知,立即 flush(无其他 running background)
|
|
476
|
-
|
|
477
|
-
|
|
477
|
+
// [D4-①] notifyComplete 已随通知簇搬至 notify-host——按轴直接构造(同 pi/store
|
|
478
|
+
// 注入,行为与经 Service 的原路径等价;dedup 集为空与单次调用断言相容)
|
|
479
|
+
createNotifyHost({ getPi: () => pi, listRunning: () => store.listRunning(), getIsIdle: () => undefined })
|
|
480
|
+
.notifyComplete(record);
|
|
478
481
|
|
|
479
482
|
expect(pi.sendMessage).toHaveBeenCalledTimes(1);
|
|
480
483
|
const msg = pi.sendMessage.mock.calls[0]![0] as {
|
|
@@ -493,8 +496,8 @@ describe("[C2] close 现状语义 + sessionFile 条件透传", () => {
|
|
|
493
496
|
record.sessionFile = path.join(agentDir, "c2-chat.jsonl");
|
|
494
497
|
store.register(record);
|
|
495
498
|
|
|
496
|
-
|
|
497
|
-
|
|
499
|
+
createNotifyHost({ getPi: () => pi, listRunning: () => store.listRunning(), getIsIdle: () => undefined })
|
|
500
|
+
.notifyComplete(record);
|
|
498
501
|
|
|
499
502
|
expect(pi.sendMessage).toHaveBeenCalledTimes(1);
|
|
500
503
|
const msg = pi.sendMessage.mock.calls[0]![0] as {
|
|
@@ -526,7 +529,7 @@ describe("[C-4] onRoundSettled 轮次增量 + [C-1] close 终态通知", () => {
|
|
|
526
529
|
afterEach(() => {
|
|
527
530
|
service.dispose();
|
|
528
531
|
_resetLifecycleState();
|
|
529
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
532
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
530
533
|
});
|
|
531
534
|
|
|
532
535
|
/** 真实回调链(与生产一致:agent_settled → onRoundSettled),turns 经 updateFromEvent 累积。 */
|
|
@@ -601,7 +604,7 @@ describe("[C-4] onRoundSettled 轮次增量 + [C-1] close 终态通知", () => {
|
|
|
601
604
|
updateFromEvent(record, { type: "text_delta", delta: "LAST-ROUND-INCREMENT" });
|
|
602
605
|
updateFromEvent(record, { type: "turn_end" });
|
|
603
606
|
settleRound(record); // 第 1 条:轮次通知
|
|
604
|
-
await service.closeSubagent(record, false); // idle close(D2 路径②)
|
|
607
|
+
await service.chatActions.closeSubagent(record, false); // idle close(D2 路径②)
|
|
605
608
|
} finally {
|
|
606
609
|
disarmIdleTimer(record.id);
|
|
607
610
|
}
|
|
@@ -651,7 +654,7 @@ describe("[C-4] onRoundSettled 轮次增量 + [C-1] close 终态通知", () => {
|
|
|
651
654
|
record.sessionFile = path.join(agentDir, "oneshot-close.jsonl");
|
|
652
655
|
store.register(record);
|
|
653
656
|
|
|
654
|
-
await service.closeSubagent(record, false); // resumable → closeChatIdle → notifyClosed 拒绝
|
|
657
|
+
await service.chatActions.closeSubagent(record, false); // resumable → closeChatIdle → notifyClosed 拒绝
|
|
655
658
|
|
|
656
659
|
expect(record.status).toBe("closed");
|
|
657
660
|
expect(pi.sendMessage).not.toHaveBeenCalled();
|
|
@@ -672,7 +675,7 @@ describe("[C-4] onRoundSettled 轮次增量 + [C-1] close 终态通知", () => {
|
|
|
672
675
|
updateFromEvent(record, { type: "turn_end" });
|
|
673
676
|
settleRound(record); // 轮终:onRoundSettled 写 record.result = "ok"
|
|
674
677
|
expect(record.result).toBe("ok"); // 前提锚点:轮终真实值在位
|
|
675
|
-
await service.closeSubagent(record, false); // idle close → completeRecord + archive
|
|
678
|
+
await service.chatActions.closeSubagent(record, false); // idle close → completeRecord + archive
|
|
676
679
|
} finally {
|
|
677
680
|
disarmIdleTimer(record.id);
|
|
678
681
|
}
|
|
@@ -34,7 +34,7 @@ vi.mock("../alive-store.ts", () => ({
|
|
|
34
34
|
findForeignLiveInstance: foreignLiveSpy,
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
|
-
vi.mock("../session-runner.ts", () => ({
|
|
37
|
+
vi.mock("../engine/engines/pi/session-runner.ts", () => ({
|
|
38
38
|
runSpawn: vi.fn(),
|
|
39
39
|
killAllSpawnedChildren: vi.fn(),
|
|
40
40
|
getChildByRecord: vi.fn(() => undefined),
|
|
@@ -63,7 +63,7 @@ function makePi(): PiLike {
|
|
|
63
63
|
|
|
64
64
|
function setup(): { agentDir: string; service: SubagentService; store: RecordStore } {
|
|
65
65
|
const agentDir = makeTmpAgentDir();
|
|
66
|
-
const modelService = new ModelConfigService({ agentDir });
|
|
66
|
+
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
67
67
|
const service = new SubagentService({ cwd: agentDir, modelService });
|
|
68
68
|
service.initSession({ pi: makePi(), sessionId: "root-session" });
|
|
69
69
|
const store = (service as unknown as { store: RecordStore }).store;
|
|
@@ -79,7 +79,7 @@ describe("T5① child process skips orphan recovery scan", () => {
|
|
|
79
79
|
recover: ReturnType<typeof vi.spyOn>;
|
|
80
80
|
entryOnly: ReturnType<typeof vi.spyOn>;
|
|
81
81
|
} => {
|
|
82
|
-
const modelService = new ModelConfigService({ agentDir });
|
|
82
|
+
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
83
83
|
const svc = new SubagentService({ cwd: agentDir, modelService });
|
|
84
84
|
const store = (svc as unknown as { store: RecordStore }).store;
|
|
85
85
|
const recover = vi.spyOn(store, "recoverOrphanRecords").mockImplementation(() => {});
|
|
@@ -95,7 +95,7 @@ describe("T5① child process skips orphan recovery scan", () => {
|
|
|
95
95
|
afterEach(() => {
|
|
96
96
|
vi.unstubAllEnvs();
|
|
97
97
|
vi.restoreAllMocks();
|
|
98
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
98
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it("root process (no self-record env) runs the orphan recovery scan", () => {
|
|
@@ -127,7 +127,7 @@ describe("T5③ cold-lookup running candidate foreign-instance guard", () => {
|
|
|
127
127
|
|
|
128
128
|
afterEach(() => {
|
|
129
129
|
vi.restoreAllMocks();
|
|
130
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
130
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
const stubColdCandidate = (record: {
|
|
@@ -150,9 +150,9 @@ describe("T5③ cold-lookup running candidate foreign-instance guard", () => {
|
|
|
150
150
|
});
|
|
151
151
|
foreignLiveSpy.mockReturnValue({ pid: 4242, id: "sa-foreign", startedAt: Date.now() });
|
|
152
152
|
|
|
153
|
-
expect(() => service
|
|
153
|
+
expect(() => service["getRecordForAction"]("sa-foreign")).toThrow(ResurrectDeniedError);
|
|
154
154
|
try {
|
|
155
|
-
service
|
|
155
|
+
service["getRecordForAction"]("sa-foreign");
|
|
156
156
|
} catch (err) {
|
|
157
157
|
expect((err as Error).message).toContain("4242");
|
|
158
158
|
expect((err as Error).message).toContain("double-write");
|
|
@@ -168,7 +168,7 @@ describe("T5③ cold-lookup running candidate foreign-instance guard", () => {
|
|
|
168
168
|
sessionFile: "/tmp/fake-session-2.jsonl",
|
|
169
169
|
rootSessionId: "root-session",
|
|
170
170
|
});
|
|
171
|
-
const found = service
|
|
171
|
+
const found = service["getRecordForAction"]("sa-cold");
|
|
172
172
|
expect(found.id).toBe("sa-cold");
|
|
173
173
|
});
|
|
174
174
|
});
|