@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
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
// reader.test.ts —— sqlite 只读 SessionView(验收 4 的 mock 半边:真实 node:sqlite 建库
|
|
2
|
-
// → turns/usage/toolCalls 派生;db 缺失/表漂移 → 结构化错误供降级)。真机半边(实录池
|
|
3
|
-
// 内 db)在 zcode-engine.live.test.ts。
|
|
4
|
-
|
|
5
|
-
import * as fs from "node:fs";
|
|
6
|
-
import * as os from "node:os";
|
|
7
|
-
import * as path from "node:path";
|
|
8
|
-
|
|
9
|
-
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
10
|
-
|
|
11
|
-
import { readZcodeSessionView, ZcodeReaderError } from "../reader.ts";
|
|
12
|
-
|
|
13
|
-
let tmpDir: string;
|
|
14
|
-
let dbPath: string;
|
|
15
|
-
|
|
16
|
-
// 建出与 zcode 0.16.5 同形的三表最小 schema(列裁剪到 reader 消费面)
|
|
17
|
-
async function createDb(file: string): Promise<void> {
|
|
18
|
-
const { DatabaseSync } = (await import("node:sqlite")) as { DatabaseSync: new (p: string) => unknown };
|
|
19
|
-
type Db = {
|
|
20
|
-
exec: (s: string) => void;
|
|
21
|
-
prepare: (s: string) => { run: (...a: unknown[]) => void };
|
|
22
|
-
close: () => void;
|
|
23
|
-
};
|
|
24
|
-
const db = new DatabaseSync(file) as unknown as Db;
|
|
25
|
-
db.exec(
|
|
26
|
-
"CREATE TABLE session (id TEXT PRIMARY KEY, time_created INTEGER);" +
|
|
27
|
-
"CREATE TABLE message (id TEXT PRIMARY KEY, session_id TEXT, sequence INTEGER, data TEXT);" +
|
|
28
|
-
"CREATE TABLE part (id TEXT PRIMARY KEY, message_id TEXT, session_id TEXT, sequence INTEGER, data TEXT);",
|
|
29
|
-
);
|
|
30
|
-
const insertMessage = db.prepare("INSERT INTO message (id, session_id, sequence, data) VALUES (?, ?, ?, ?)");
|
|
31
|
-
const insertPart = db.prepare("INSERT INTO part (id, message_id, session_id, sequence, data) VALUES (?, ?, ?, ?, ?)");
|
|
32
|
-
|
|
33
|
-
// session 1(目标):user prompt + assistant 两轮(text/reasoning/tool/step-finish×2)
|
|
34
|
-
db.prepare("INSERT INTO session (id, time_created) VALUES (?, ?)").run("sess_target", 2000);
|
|
35
|
-
insertMessage.run("msg_user", "sess_target", 0, JSON.stringify({ role: "user" }));
|
|
36
|
-
insertPart.run("part_user_text", "msg_user", "sess_target", 0, JSON.stringify({ type: "text", text: "the task" }));
|
|
37
|
-
|
|
38
|
-
insertMessage.run("msg_asst", "sess_target", 1, JSON.stringify({ role: "assistant" }));
|
|
39
|
-
insertPart.run("p0", "msg_asst", "sess_target", 0, JSON.stringify({ type: "step-start" }));
|
|
40
|
-
insertPart.run("p1", "msg_asst", "sess_target", 1, JSON.stringify({ type: "reasoning", text: "thinking hard" }));
|
|
41
|
-
insertPart.run(
|
|
42
|
-
"p2",
|
|
43
|
-
"msg_asst",
|
|
44
|
-
"sess_target",
|
|
45
|
-
2,
|
|
46
|
-
JSON.stringify({
|
|
47
|
-
type: "tool",
|
|
48
|
-
tool: "Bash",
|
|
49
|
-
state: JSON.stringify({ status: "completed", input: { command: "ls" }, output: "file-a\nfile-b" }),
|
|
50
|
-
}),
|
|
51
|
-
);
|
|
52
|
-
insertPart.run(
|
|
53
|
-
"p3",
|
|
54
|
-
"msg_asst",
|
|
55
|
-
"sess_target",
|
|
56
|
-
3,
|
|
57
|
-
JSON.stringify({
|
|
58
|
-
type: "step-finish",
|
|
59
|
-
reason: "stop",
|
|
60
|
-
cost: 0,
|
|
61
|
-
tokens: { total: 110, input: 100, output: 10, reasoning: 0, cache: { read: 5, write: 0 } },
|
|
62
|
-
}),
|
|
63
|
-
);
|
|
64
|
-
// 第二轮(同 assistant message 内第二个 step 段)
|
|
65
|
-
insertPart.run("p4", "msg_asst", "sess_target", 4, JSON.stringify({ type: "text", text: "final answer" }));
|
|
66
|
-
insertPart.run(
|
|
67
|
-
"p5",
|
|
68
|
-
"msg_asst",
|
|
69
|
-
"sess_target",
|
|
70
|
-
5,
|
|
71
|
-
JSON.stringify({
|
|
72
|
-
type: "step-finish",
|
|
73
|
-
reason: "stop",
|
|
74
|
-
cost: 0.5,
|
|
75
|
-
tokens: { total: 60, input: 50, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
76
|
-
}),
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
// session 2(更早):验证「缺省取最新」与定向读取互不干扰
|
|
80
|
-
db.prepare("INSERT INTO session (id, time_created) VALUES (?, ?)").run("sess_older", 1000);
|
|
81
|
-
insertMessage.run("msg_old", "sess_older", 0, JSON.stringify({ role: "assistant" }));
|
|
82
|
-
insertPart.run("part_old", "msg_old", "sess_older", 0, JSON.stringify({ type: "text", text: "old turn" }));
|
|
83
|
-
insertPart.run(
|
|
84
|
-
"part_old2",
|
|
85
|
-
"msg_old",
|
|
86
|
-
"sess_older",
|
|
87
|
-
1,
|
|
88
|
-
JSON.stringify({ type: "step-finish", tokens: { input: 1, output: 1, cache: {} } }),
|
|
89
|
-
);
|
|
90
|
-
db.close();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
beforeAll(async () => {
|
|
94
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "zcode-reader-"));
|
|
95
|
-
dbPath = path.join(tmpDir, "db.sqlite");
|
|
96
|
-
await createDb(dbPath);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe("readZcodeSessionView:native 读取", () => {
|
|
100
|
-
it("turns 派生:两轮闭合、text/thinking/toolCalls/usage 齐备(验收 4)", async () => {
|
|
101
|
-
const view = await readZcodeSessionView(dbPath, "sess_target");
|
|
102
|
-
expect(view.engineId).toBe("zcode");
|
|
103
|
-
expect(view.source).toBe("native");
|
|
104
|
-
expect(view.sessionId).toBe("sess_target");
|
|
105
|
-
expect(view.turns).toHaveLength(2);
|
|
106
|
-
|
|
107
|
-
const [t1, t2] = view.turns!;
|
|
108
|
-
expect(t1!.text).toBe("");
|
|
109
|
-
expect(t1!.thinking).toBe("thinking hard");
|
|
110
|
-
expect(t1!.toolCalls).toHaveLength(1);
|
|
111
|
-
expect(t1!.toolCalls[0]!.toolName).toBe("Bash");
|
|
112
|
-
expect(t1!.toolCalls[0]!.args).toEqual({ command: "ls" });
|
|
113
|
-
expect(t1!.toolCalls[0]!.result).toEqual({ content: ["file-a\nfile-b"] });
|
|
114
|
-
expect(t1!.toolCalls[0]!.isError).toBeUndefined(); // status completed
|
|
115
|
-
expect(t1!.closed).toBe(true);
|
|
116
|
-
|
|
117
|
-
expect(t2!.text).toBe("final answer");
|
|
118
|
-
expect(t2!.closed).toBe(true);
|
|
119
|
-
|
|
120
|
-
// usage 聚合 = 两轮 usageDelta 之和(cost 累计第二段的 0.5)
|
|
121
|
-
expect(view.usage).toEqual({ input: 150, output: 20, cacheRead: 5, cacheWrite: 0, cost: 0.5, total: 175 });
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("缺省 sessionId 取池内最新 session(time_created DESC)", async () => {
|
|
125
|
-
const view = await readZcodeSessionView(dbPath);
|
|
126
|
-
expect(view.sessionId).toBe("sess_target");
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("tool 状态非 completed 标 isError", async () => {
|
|
130
|
-
// 单独建一个带失败工具调用的 session,避免污染上面的聚合断言
|
|
131
|
-
const failDb = path.join(tmpDir, "fail.sqlite");
|
|
132
|
-
await createDb(failDb);
|
|
133
|
-
const { DatabaseSync } = (await import("node:sqlite")) as { DatabaseSync: new (p: string) => unknown };
|
|
134
|
-
type Db = { prepare: (s: string) => { run: (...a: unknown[]) => void }; close: () => void };
|
|
135
|
-
const db = new DatabaseSync(failDb) as unknown as Db;
|
|
136
|
-
db.prepare("INSERT INTO session (id, time_created) VALUES (?, ?)").run("sess_fail", 3000);
|
|
137
|
-
db
|
|
138
|
-
.prepare("INSERT INTO message (id, session_id, sequence, data) VALUES (?, ?, ?, ?)")
|
|
139
|
-
.run("msg_f", "sess_fail", 0, JSON.stringify({ role: "assistant" }));
|
|
140
|
-
db
|
|
141
|
-
.prepare("INSERT INTO part (id, message_id, session_id, sequence, data) VALUES (?, ?, ?, ?, ?)")
|
|
142
|
-
.run(
|
|
143
|
-
"pf",
|
|
144
|
-
"msg_f",
|
|
145
|
-
"sess_fail",
|
|
146
|
-
0,
|
|
147
|
-
JSON.stringify({
|
|
148
|
-
type: "tool",
|
|
149
|
-
tool: "Edit",
|
|
150
|
-
state: JSON.stringify({ status: "error", input: { file: "a" }, output: "conflict" }),
|
|
151
|
-
}),
|
|
152
|
-
);
|
|
153
|
-
db.close();
|
|
154
|
-
const view = await readZcodeSessionView(failDb, "sess_fail");
|
|
155
|
-
expect(view.turns[0]!.toolCalls[0]!.isError).toBe(true);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
describe("readZcodeSessionView:结构化错误(供降级②③级,验收 4 负例)", () => {
|
|
160
|
-
beforeEach(() => {
|
|
161
|
-
// 无共享状态需重置;保留钩子位置说明负例组意图
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("db 文件缺失 → engine_session_read_failed(detail 含路径)", async () => {
|
|
165
|
-
const missing = path.join(tmpDir, "nope.sqlite");
|
|
166
|
-
try {
|
|
167
|
-
await readZcodeSessionView(missing, "sess_x");
|
|
168
|
-
expect.unreachable("should throw");
|
|
169
|
-
} catch (err) {
|
|
170
|
-
expect(err).toBeInstanceOf(ZcodeReaderError);
|
|
171
|
-
const e = err as ZcodeReaderError;
|
|
172
|
-
expect(e.code).toBe("engine_session_read_failed");
|
|
173
|
-
expect(e.detail).toContain(missing);
|
|
174
|
-
expect(e.message).toContain("降级");
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("表结构漂移(缺 part 表)→ 结构化错误含漂移提示", async () => {
|
|
179
|
-
const drifted = path.join(tmpDir, "drifted.sqlite");
|
|
180
|
-
const { DatabaseSync } = (await import("node:sqlite")) as { DatabaseSync: new (p: string) => unknown };
|
|
181
|
-
type Db = { exec: (s: string) => void; close: () => void };
|
|
182
|
-
const db = new DatabaseSync(drifted) as unknown as Db;
|
|
183
|
-
// 只建 session/message(zcode 升级把 part 改名的形态);塞一行 session 让定位
|
|
184
|
-
// 通过、失败必然发生在 part 查询(表缺失)
|
|
185
|
-
db.exec(
|
|
186
|
-
"CREATE TABLE session (id TEXT PRIMARY KEY, time_created INTEGER);" +
|
|
187
|
-
"CREATE TABLE message (id TEXT PRIMARY KEY, session_id TEXT, sequence INTEGER, data TEXT);" +
|
|
188
|
-
"INSERT INTO session (id, time_created) VALUES ('sess_d', 1);",
|
|
189
|
-
);
|
|
190
|
-
db.close();
|
|
191
|
-
try {
|
|
192
|
-
await readZcodeSessionView(drifted);
|
|
193
|
-
expect.unreachable("should throw");
|
|
194
|
-
} catch (err) {
|
|
195
|
-
const e = err as ZcodeReaderError;
|
|
196
|
-
expect(e.code).toBe("engine_session_read_failed");
|
|
197
|
-
expect(e.message).toContain("漂移");
|
|
198
|
-
expect(e.message).toContain("golden");
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("session 不存在 → 结构化错误(不静默空视图)", async () => {
|
|
203
|
-
try {
|
|
204
|
-
await readZcodeSessionView(dbPath, "sess_ghost");
|
|
205
|
-
expect.unreachable("should throw");
|
|
206
|
-
} catch (err) {
|
|
207
|
-
expect((err as ZcodeReaderError).detail).toContain("sess_ghost");
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// registration.test.ts —— 注册表接线(验收 6):'zcode' 可经 getEngine 获取、幂等
|
|
2
|
-
// 覆盖、engineDataDir 默认走 common/data-dir SSOT。clearEngines 隔离全局注册表状态。
|
|
3
|
-
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
|
|
6
|
-
// u0-data-discovery 注入化后 data-dir 回退值来自 HostServices.dataRoot() 端口——
|
|
7
|
-
// 用例内 configureCore 假宿主控制端口态,afterEach reset 防跨用例泄漏
|
|
8
|
-
import { configureCore, resetCoreForTests } from "../../../../../core/host-services.ts";
|
|
9
|
-
|
|
10
|
-
import { clearEngines, getEngine, hasEngine, listEngines } from "../../../registry.ts";
|
|
11
|
-
import { createZcodeEngine, registerZcodeEngine } from "../registration.ts";
|
|
12
|
-
|
|
13
|
-
const FAKE_DATA_ROOT = "/fake-registration-data-root";
|
|
14
|
-
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
clearEngines();
|
|
17
|
-
resetCoreForTests();
|
|
18
|
-
vi.restoreAllMocks();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe("registerZcodeEngine", () => {
|
|
22
|
-
it("登记后 getEngine('zcode') 可用,id/capabilities 正确", () => {
|
|
23
|
-
registerZcodeEngine(() => "/tmp/data");
|
|
24
|
-
expect(hasEngine("zcode")).toBe(true);
|
|
25
|
-
expect(listEngines()).toContain("zcode");
|
|
26
|
-
const engine = getEngine("zcode");
|
|
27
|
-
expect(engine.id).toBe("zcode");
|
|
28
|
-
expect(engine.capabilities().schemaEnforcement).toBe("emulated");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("幂等:重复注册覆盖不堆积(listEngines 单条)", () => {
|
|
32
|
-
registerZcodeEngine(() => "/tmp/data");
|
|
33
|
-
registerZcodeEngine(() => "/tmp/data2");
|
|
34
|
-
expect(listEngines().filter((id) => id === "zcode")).toHaveLength(1);
|
|
35
|
-
// 覆盖后单例按新工厂重建(engineDataDir 已换)
|
|
36
|
-
expect(hasEngine("zcode")).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("createZcodeEngine DI 工厂:deps 直达引擎", () => {
|
|
40
|
-
const engine = createZcodeEngine({ engineDataDir: () => "/tmp/x" });
|
|
41
|
-
expect(engine.id).toBe("zcode");
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe("engineDataDir 默认通道(common/data-dir SSOT)", () => {
|
|
46
|
-
it("缺省经 getEngineDataDir 解析:env 优先,缺失回落宿主数据根(warn 一次)", async () => {
|
|
47
|
-
// warn 断言走 getEngineDataDir 自带的 DI warn 参数——缺省 warn 通道是
|
|
48
|
-
// core logger facade(configureCore 后经 pi-host 桥接落文件,不经
|
|
49
|
-
// console.warn),spy console 永远收不到且会让 logger 落文件产生测试副作用。
|
|
50
|
-
const { getEngineDataDir, resetDataDirWarnForTests } = await import("../../../common/data-dir.ts");
|
|
51
|
-
const warns: string[] = [];
|
|
52
|
-
const prev = process.env["XYZ_AGENT_DATA_DIR"];
|
|
53
|
-
delete process.env["XYZ_AGENT_DATA_DIR"];
|
|
54
|
-
try {
|
|
55
|
-
resetCoreForTests();
|
|
56
|
-
configureCore({ dataRoot: () => FAKE_DATA_ROOT, log: () => {} });
|
|
57
|
-
resetDataDirWarnForTests();
|
|
58
|
-
// 回退分支:无 env → HostServices.dataRoot()(假宿主值)+ warn 一次
|
|
59
|
-
expect(getEngineDataDir(process.env, (m) => warns.push(m))).toBe(FAKE_DATA_ROOT);
|
|
60
|
-
expect(warns).toHaveLength(1);
|
|
61
|
-
expect(warns[0]).toContain("XYZ_AGENT_DATA_DIR");
|
|
62
|
-
// env 分支:显式注入即取 env 值(透传链证据见 common/data-dir.ts 文件头)
|
|
63
|
-
process.env["XYZ_AGENT_DATA_DIR"] = "/from-host";
|
|
64
|
-
expect(getEngineDataDir(process.env, (m) => warns.push(m))).toBe("/from-host");
|
|
65
|
-
expect(warns).toHaveLength(1);
|
|
66
|
-
} finally {
|
|
67
|
-
if (prev !== undefined) process.env["XYZ_AGENT_DATA_DIR"] = prev;
|
|
68
|
-
else delete process.env["XYZ_AGENT_DATA_DIR"];
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
});
|
package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
// session-channel-dispose-harvest.test.ts —— P0-1 U5 dispose 前收割单测(机制层)。
|
|
2
|
-
//
|
|
3
|
-
// 设计权威源:docs/design/timeout-zcode-turn-and-settled-watchdog.md §3.4(退化路径)、
|
|
4
|
-
// §6 D7(dispose 收割兜底)、§11 P-Z3(close 吞没 → dispose 触发 failAllTurns)。
|
|
5
|
-
// P-Z3 的「close 事件吞没」在真实子进程形态不可确定性注入(Node child 进程退出后
|
|
6
|
-
// stdio 关闭即触发 close 事件)——真实现场归 Gate B;本文件用 mock 连接承载机制层
|
|
7
|
-
// 可判定形态:不触发 onClose(= close 吞没的机制等价物)→ dispose → 在途 turn 收敛
|
|
8
|
-
// 为明确失败。与 zcode-engine-dispose.test.ts(fake-appserver 子进程集成面)互补。
|
|
9
|
-
//
|
|
10
|
-
// 覆盖:
|
|
11
|
-
// ① close 吞没形态:dispose 触发 failAllTurns——在途 turn 于 dispose 时收割
|
|
12
|
-
// (P-Z3 核心断言),收割后到达的终态不再改写(settled 守卫);
|
|
13
|
-
// ② grace 窗口内全部终态化:并发多会话 turn 全量收割,不留永久 pending;
|
|
14
|
-
// ③ 正常 close 主路径零回归:onClose 收割先行 → dispose no-op 不改写收割错误;
|
|
15
|
-
// ④ 终态已落定(成功)后 dispose:settled 守卫不改写已成功结果;
|
|
16
|
-
// ⑤ dispose 收割后 closeSession 短路:runTurn finally 不再发 session/close
|
|
17
|
-
// (防写死进程/惰性重建——D7 收割语义的必要闭环)+ 正常完成对照(发 close);
|
|
18
|
-
// ⑥ 二次 dispose 幂等。
|
|
19
|
-
|
|
20
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
21
|
-
|
|
22
|
-
import type { AppServerConnection } from "../connection.ts";
|
|
23
|
-
import {
|
|
24
|
-
SessionChannel,
|
|
25
|
-
type SessionTurnResult,
|
|
26
|
-
type SessionTurnOptions,
|
|
27
|
-
} from "../session-channel.ts";
|
|
28
|
-
|
|
29
|
-
// ============================================================
|
|
30
|
-
// mock 连接 + 驱动器(与 session-channel-turn-timers.test.ts 同形态,
|
|
31
|
-
// 增补 onClose handler 的手动触发面——close 吞没注入的机制等价物)
|
|
32
|
-
// ============================================================
|
|
33
|
-
|
|
34
|
-
interface MockChannel {
|
|
35
|
-
ch: SessionChannel;
|
|
36
|
-
/** 我方发出的请求帧流水(method 序可断言)。 */
|
|
37
|
-
requests: Array<{ method: string; params: unknown }>;
|
|
38
|
-
/** 手动触发连接崩溃广播(close 吞没形态 = 不触发;正常形态 = 测试显式触发)。 */
|
|
39
|
-
emitClose: (reason: string) => void;
|
|
40
|
-
emit: (method: string, params: unknown) => void;
|
|
41
|
-
emitTerminal: (sessionId: string | undefined, status: string) => void;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let sidSeq = 0;
|
|
45
|
-
|
|
46
|
-
function makeMockChannel(): MockChannel {
|
|
47
|
-
const notifications = new Map<string, (params: unknown) => void>();
|
|
48
|
-
const requests: Array<{ method: string; params: unknown }> = [];
|
|
49
|
-
const closeHandlers = new Set<(reason: string) => void>();
|
|
50
|
-
const conn = {
|
|
51
|
-
alive: true,
|
|
52
|
-
onNotification: (method: string, h: (params: unknown) => void) => {
|
|
53
|
-
notifications.set(method, h);
|
|
54
|
-
return () => notifications.delete(method);
|
|
55
|
-
},
|
|
56
|
-
onClose: (h: (reason: string) => void) => {
|
|
57
|
-
closeHandlers.add(h);
|
|
58
|
-
return () => closeHandlers.delete(h);
|
|
59
|
-
},
|
|
60
|
-
request: (method: string, params: unknown) => {
|
|
61
|
-
requests.push({ method, params });
|
|
62
|
-
if (method === "session/create") {
|
|
63
|
-
sidSeq += 1;
|
|
64
|
-
return Promise.resolve({ session: { sessionId: `sess_h${sidSeq}` } });
|
|
65
|
-
}
|
|
66
|
-
if (method === "session/send") return Promise.resolve({ accepted: true });
|
|
67
|
-
if (method === "session/read") return Promise.resolve({ messages: [] });
|
|
68
|
-
return Promise.resolve({});
|
|
69
|
-
},
|
|
70
|
-
} as unknown as AppServerConnection;
|
|
71
|
-
const ch = new SessionChannel(conn);
|
|
72
|
-
return {
|
|
73
|
-
ch,
|
|
74
|
-
requests,
|
|
75
|
-
emitClose: (reason) => {
|
|
76
|
-
for (const fn of [...closeHandlers]) fn(reason);
|
|
77
|
-
},
|
|
78
|
-
emit: (method, params) => notifications.get(method)?.(params),
|
|
79
|
-
emitTerminal: (sessionId, status) =>
|
|
80
|
-
notifications
|
|
81
|
-
.get("v4/telemetry/event")
|
|
82
|
-
?.({ kind: "turn.terminal", status, sessionId }),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
interface TurnTracker {
|
|
87
|
-
outcome: "pending" | "fulfilled" | "rejected";
|
|
88
|
-
result: SessionTurnResult | undefined;
|
|
89
|
-
error: unknown;
|
|
90
|
-
promise: Promise<SessionTurnResult>;
|
|
91
|
-
/** onSessionCreated 回填(getter——回调晚于 tracker 构造,值拷贝会恒空)。 */
|
|
92
|
-
readonly sessionId: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function startTurn(ch: SessionChannel, opts: SessionTurnOptions = {}): TurnTracker {
|
|
96
|
-
let sessionId = "";
|
|
97
|
-
const promise = ch.runTurn({ workspacePath: "/tmp/ws-harvest", mode: "yolo" }, "任务", {
|
|
98
|
-
onSessionCreated: (sid) => {
|
|
99
|
-
sessionId = sid;
|
|
100
|
-
},
|
|
101
|
-
...opts,
|
|
102
|
-
});
|
|
103
|
-
const t: TurnTracker = {
|
|
104
|
-
outcome: "pending",
|
|
105
|
-
result: undefined,
|
|
106
|
-
error: undefined,
|
|
107
|
-
promise,
|
|
108
|
-
get sessionId(): string {
|
|
109
|
-
return sessionId;
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
void promise.then(
|
|
113
|
-
(r) => {
|
|
114
|
-
t.outcome = "fulfilled";
|
|
115
|
-
t.result = r;
|
|
116
|
-
},
|
|
117
|
-
(e) => {
|
|
118
|
-
t.outcome = "rejected";
|
|
119
|
-
t.error = e;
|
|
120
|
-
},
|
|
121
|
-
);
|
|
122
|
-
return t;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** 排空微任务链(create→subscribe→send 全是已 resolve 的 promise——一拍 setTimeout 足够)。 */
|
|
126
|
-
function flush(): Promise<void> {
|
|
127
|
-
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function settle(t: TurnTracker): Promise<void> {
|
|
131
|
-
await t.promise.then(
|
|
132
|
-
() => undefined,
|
|
133
|
-
() => undefined,
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
let enginesChannels: SessionChannel[] = [];
|
|
138
|
-
afterEach(() => {
|
|
139
|
-
for (const ch of enginesChannels.splice(0)) ch.dispose();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// ============================================================
|
|
143
|
-
// dispose 收割(P0-1 U5/D7:close 缺失/被吞没的退化路径闭合)
|
|
144
|
-
// ============================================================
|
|
145
|
-
|
|
146
|
-
describe("dispose 收割(P-Z3 机制层:close 吞没 → dispose 触发 failAllTurns)", () => {
|
|
147
|
-
it("close 吞没形态:dispose 时在途 turn 收割为明确失败,迟到终态不再改写", async () => {
|
|
148
|
-
const m = makeMockChannel();
|
|
149
|
-
enginesChannels.push(m.ch);
|
|
150
|
-
const t = startTurn(m.ch);
|
|
151
|
-
await flush();
|
|
152
|
-
expect(t.outcome).toBe("pending"); // send 已发、无终态——在途
|
|
153
|
-
|
|
154
|
-
m.ch.dispose(); // close 吞没(onClose 未触发)→ dispose 收割兜底
|
|
155
|
-
await settle(t);
|
|
156
|
-
|
|
157
|
-
expect(t.outcome).toBe("rejected");
|
|
158
|
-
const err = t.error as Error;
|
|
159
|
-
expect(err).toBeInstanceOf(Error);
|
|
160
|
-
expect(err.message).toContain("已 dispose");
|
|
161
|
-
expect(err.message).toContain("收割");
|
|
162
|
-
expect(err.message).toContain("恢复指引");
|
|
163
|
-
// 收割后到达的权威终态:settled 守卫——落定结果不改写
|
|
164
|
-
m.emitTerminal(t.sessionId, "success");
|
|
165
|
-
expect(t.outcome).toBe("rejected");
|
|
166
|
-
expect((t.error as Error).message).toBe(err.message);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("grace 窗口内全部终态化:并发双会话 turn 全量收割,不留永久 pending", async () => {
|
|
170
|
-
const m = makeMockChannel();
|
|
171
|
-
enginesChannels.push(m.ch);
|
|
172
|
-
const t1 = startTurn(m.ch);
|
|
173
|
-
const t2 = startTurn(m.ch);
|
|
174
|
-
await flush();
|
|
175
|
-
expect(t1.sessionId).not.toBe(t2.sessionId); // 双会话确实并发
|
|
176
|
-
expect(t1.outcome).toBe("pending");
|
|
177
|
-
expect(t2.outcome).toBe("pending");
|
|
178
|
-
|
|
179
|
-
m.ch.dispose();
|
|
180
|
-
await Promise.all([settle(t1), settle(t2)]);
|
|
181
|
-
|
|
182
|
-
expect(t1.outcome).toBe("rejected");
|
|
183
|
-
expect(t2.outcome).toBe("rejected");
|
|
184
|
-
expect((t1.error as Error).message).toContain("已 dispose");
|
|
185
|
-
expect((t2.error as Error).message).toContain("已 dispose");
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("正常 close 主路径零回归:onClose 收割先行(错误=连接层 reason),dispose no-op 不改写", async () => {
|
|
189
|
-
const m = makeMockChannel();
|
|
190
|
-
enginesChannels.push(m.ch);
|
|
191
|
-
const t = startTurn(m.ch);
|
|
192
|
-
await flush();
|
|
193
|
-
|
|
194
|
-
m.emitClose("进程退出(code=0 signal=SIGTERM)"); // onClose 主路径收割(正常 close 到达)
|
|
195
|
-
await settle(t);
|
|
196
|
-
expect(t.outcome).toBe("rejected");
|
|
197
|
-
const onCloseErr = t.error as Error;
|
|
198
|
-
expect(onCloseErr.message).toContain("app-server 进程退出");
|
|
199
|
-
|
|
200
|
-
m.ch.dispose(); // activeTurns 已空——幂等 no-op,收割错误不被 dispose 文案改写
|
|
201
|
-
expect(t.outcome).toBe("rejected");
|
|
202
|
-
expect((t.error as Error).message).toBe(onCloseErr.message);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("终态已落定(成功)后 dispose:settled 守卫不改写已成功结果", async () => {
|
|
206
|
-
const m = makeMockChannel();
|
|
207
|
-
enginesChannels.push(m.ch);
|
|
208
|
-
const t = startTurn(m.ch);
|
|
209
|
-
await flush();
|
|
210
|
-
m.emitTerminal(t.sessionId, "success");
|
|
211
|
-
await settle(t);
|
|
212
|
-
expect(t.outcome).toBe("fulfilled");
|
|
213
|
-
|
|
214
|
-
m.ch.dispose(); // 已成功收尾的 turn 不受 dispose 收割影响
|
|
215
|
-
expect(t.outcome).toBe("fulfilled");
|
|
216
|
-
expect(t.result?.terminal).toEqual({ status: "success", source: "turn.terminal" });
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it("dispose 收割后 closeSession 短路:finally 不发 session/close(防写死进程/惰性重建);正常完成对照=发 close", async () => {
|
|
220
|
-
const m1 = makeMockChannel();
|
|
221
|
-
enginesChannels.push(m1.ch);
|
|
222
|
-
const t1 = startTurn(m1.ch);
|
|
223
|
-
await flush();
|
|
224
|
-
m1.ch.dispose();
|
|
225
|
-
await settle(t1);
|
|
226
|
-
await flush(); // runTurn finally 的 closeSession 已过
|
|
227
|
-
expect(t1.outcome).toBe("rejected");
|
|
228
|
-
expect(m1.requests.map((r) => r.method)).not.toContain("session/close");
|
|
229
|
-
|
|
230
|
-
// 对照:正常完成的 turn,finally 发 session/close(D4 用后即毁——零回归面)
|
|
231
|
-
const m2 = makeMockChannel();
|
|
232
|
-
enginesChannels.push(m2.ch);
|
|
233
|
-
const t2 = startTurn(m2.ch);
|
|
234
|
-
await flush();
|
|
235
|
-
m2.emitTerminal(t2.sessionId, "success");
|
|
236
|
-
await settle(t2);
|
|
237
|
-
expect(t2.outcome).toBe("fulfilled");
|
|
238
|
-
expect(m2.requests.map((r) => r.method)).toContain("session/close");
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it("二次 dispose 幂等:无在途 turn 时 dispose 是 no-op 不抛", async () => {
|
|
242
|
-
const m = makeMockChannel();
|
|
243
|
-
enginesChannels.push(m.ch);
|
|
244
|
-
expect(() => m.ch.dispose()).not.toThrow();
|
|
245
|
-
expect(() => m.ch.dispose()).not.toThrow();
|
|
246
|
-
});
|
|
247
|
-
});
|