@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
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
// contract.agent-events.test.ts —— conformance C3(AgentEvent 不变量)+ 负例自证
|
|
2
2
|
// (A12「套件有牙」:故意破坏一个不变量的样本必须被断言器检出——用「注入坏
|
|
3
|
-
// parser
|
|
3
|
+
// parser」的形态:构造抽掉 message_end / usage 半映射的坏样本流,断言套件转红;
|
|
4
4
|
// 若断言器检不出破坏则本元测试失败)。
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
6
|
+
// 2026-09 重构(共享宿主 HOME):CLI spawn 链删除后引擎只剩 app-server 常驻链,
|
|
7
|
+
// C3 的引擎全链覆盖只走 app-server 形态(fake 常驻引擎 + stream 口径,文件末
|
|
8
|
+
// describe;app-server 设计 §3.4 不变量 1:text_delta 拼接 == read 全文、终态唯一
|
|
9
|
+
// turn.terminal 权威、message_end.usage 完整、tool 事件不合成——granularity 声明
|
|
10
|
+
// 与实际流出一致)。断言器行为正反例与负例自证均为手工构造样本,不依赖引擎实现。
|
|
11
11
|
|
|
12
12
|
import { describe, expect, it } from "vitest";
|
|
13
13
|
|
|
14
14
|
import type { AgentEvent } from "../../../types.ts";
|
|
15
|
-
import { ZCODE_APPSERVER_GOLDEN
|
|
16
|
-
import {
|
|
17
|
-
import { parseZcodeTerminal, synthesizeCoarseEvents } from "../../engines/zcode/parser.ts";
|
|
15
|
+
import { ZCODE_APPSERVER_GOLDEN } from "../../engines/zcode/golden-sample.ts";
|
|
16
|
+
import { ZCODE_SHARED_POOL_KEY } from "../../engines/zcode/constants.ts";
|
|
18
17
|
import {
|
|
19
18
|
goldenReadFullText,
|
|
20
19
|
makeAppserverHarness,
|
|
@@ -24,18 +23,7 @@ import {
|
|
|
24
23
|
checkAgentEventInvariants,
|
|
25
24
|
} from "./agent-event-invariants.ts";
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
function goldenCoarseEvents(): AgentEvent[] {
|
|
29
|
-
const terminal = parseZcodeTerminal(ZCODE_GOLDEN_STDOUT);
|
|
30
|
-
if (!terminal.ok) throw new Error("golden 样本解析失败");
|
|
31
|
-
return synthesizeCoarseEvents(terminal.payload.response, terminal.payload.usage);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
describe("conformance C3:AgentEvent 不变量(真实 parser 产出全绿)", () => {
|
|
35
|
-
it("zcode golden 合成事件满足五条不变量(coarse 口径)", () => {
|
|
36
|
-
assertAgentEventInvariants(goldenCoarseEvents(), { granularity: "coarse" });
|
|
37
|
-
});
|
|
38
|
-
|
|
26
|
+
describe("conformance C3:AgentEvent 不变量(断言器正反例,手工构造样本)", () => {
|
|
39
27
|
it("不变量 1:终态唯一——turn_end 后不得再出现非 error 事件", () => {
|
|
40
28
|
const events: AgentEvent[] = [{ type: "message_end" }, { type: "turn_end" }, { type: "text_delta", delta: "x" }];
|
|
41
29
|
const findings = checkAgentEventInvariants(events, { granularity: "coarse" });
|
|
@@ -113,8 +101,7 @@ describe("conformance 负例自证(A12:套件有牙)", () => {
|
|
|
113
101
|
|
|
114
102
|
// ============================================================
|
|
115
103
|
// [R6] C3 app-server 口径:fake 常驻引擎全链(eventGranularity=stream 形态)
|
|
116
|
-
// ——
|
|
117
|
-
// spawn 路径,双通道都是生产可达形态)。
|
|
104
|
+
// —— 2026-09 重构后唯一生产形态(CLI spawn 降级链已删,无钉扎/降级双通道)。
|
|
118
105
|
// ============================================================
|
|
119
106
|
|
|
120
107
|
describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake 常驻全链)", () => {
|
|
@@ -123,7 +110,7 @@ describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake
|
|
|
123
110
|
try {
|
|
124
111
|
const events: AgentEvent[] = [];
|
|
125
112
|
const { outcome } = await h.engine.run(
|
|
126
|
-
{
|
|
113
|
+
{ prompt: "做点什么", description: "c3-appserver", model: "conformance-provider/m1", cwd: h.workspace },
|
|
127
114
|
{ taskId: "sa-c3-appserver", poolKey: "", onEvent: (e) => events.push(e) },
|
|
128
115
|
);
|
|
129
116
|
|
|
@@ -158,7 +145,7 @@ describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake
|
|
|
158
145
|
try {
|
|
159
146
|
const events: AgentEvent[] = [];
|
|
160
147
|
const { outcome } = await h.engine.run(
|
|
161
|
-
{
|
|
148
|
+
{ prompt: "做点什么", description: "c3-no-terminal", model: "conformance-provider/m1", cwd: h.workspace },
|
|
162
149
|
{ taskId: "sa-c3-no-terminal", poolKey: "", onEvent: (e) => events.push(e) },
|
|
163
150
|
);
|
|
164
151
|
expect(outcome.error).toBeUndefined();
|
|
@@ -169,14 +156,14 @@ describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake
|
|
|
169
156
|
}
|
|
170
157
|
}, 20_000);
|
|
171
158
|
|
|
172
|
-
it("handle
|
|
159
|
+
it("handle 携带 shared poolKey(共享宿主 HOME 的 journal 分组锚点,①级读取钥匙随 handle 走)", async () => {
|
|
173
160
|
const h = makeAppserverHarness();
|
|
174
161
|
try {
|
|
175
162
|
const { handle, outcome } = await h.engine.run(
|
|
176
|
-
{
|
|
163
|
+
{ prompt: "做点什么", description: "c3-anchor", model: "conformance-provider/m1", cwd: h.workspace },
|
|
177
164
|
{ taskId: "sa-c3-anchor", poolKey: "" },
|
|
178
165
|
);
|
|
179
|
-
expect(handle.data.poolKey).toBe(
|
|
166
|
+
expect(handle.data.poolKey).toBe(ZCODE_SHARED_POOL_KEY);
|
|
180
167
|
expect(handle.data.sessionRef["sessionId"]).toBe(outcome.sessionId);
|
|
181
168
|
// golden 锚点:sessionId 来自 session.sessionId(projection.sessionId 恒 "unknown" 勿用)
|
|
182
169
|
expect(JSON.parse(ZCODE_APPSERVER_GOLDEN.createResponse).session.sessionId).toBe("sess_golden_r3_01");
|
|
@@ -25,10 +25,11 @@ import { describe, expect, it } from "vitest";
|
|
|
25
25
|
import { getSubagentService } from "../../../subagent-service.ts";
|
|
26
26
|
import { ZcodeEngine } from "../../engines/zcode/zcode-engine.ts";
|
|
27
27
|
import { createPiEngine } from "../../engines/pi/registration.ts";
|
|
28
|
+
import type { PiEngineService } from "../../engines/pi/pi-engine.ts";
|
|
28
29
|
import type { RunContext } from "../../port.ts";
|
|
29
30
|
import type { AgentEvent } from "../../../types.ts";
|
|
30
|
-
import type {
|
|
31
|
-
import { getPiInvocation } from "
|
|
31
|
+
import type { AgentCallOpts } from "../../../../orchestration/models/types.ts";
|
|
32
|
+
import { getPiInvocation } from "../../engines/pi/pi-invocation.ts";
|
|
32
33
|
import {
|
|
33
34
|
RELAY_ENV_NODE,
|
|
34
35
|
RELAY_ENV_RECORD_ID,
|
|
@@ -51,8 +52,8 @@ describe.skipIf(!LIVE)("conformance run 层(真实 spawn,手动门)", () =
|
|
|
51
52
|
testCtx.skip("SubagentService 未装配(需在真实会话进程内运行)");
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
|
-
const engine = createPiEngine(() => service);
|
|
55
|
-
const task:
|
|
55
|
+
const engine = createPiEngine(() => service as unknown as PiEngineService);
|
|
56
|
+
const task: AgentCallOpts = { prompt: "Reply with the single word: ok", description: "live-c2" };
|
|
56
57
|
const ctx: RunContext = { taskId: "sa-live-pi-c2", poolKey: "shared" };
|
|
57
58
|
const { outcome } = await engine.run(task, ctx);
|
|
58
59
|
expect(outcome.error).toBeUndefined();
|
|
@@ -68,9 +69,9 @@ describe.skipIf(!LIVE)("conformance run 层(真实 spawn,手动门)", () =
|
|
|
68
69
|
}, 60_000);
|
|
69
70
|
|
|
70
71
|
// [R6] 常驻通道的 conformance run 层(RA8「C1-C8 适配后全绿」的 live 面):
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
// engines/zcode/__tests__/zcode-engine.live.test.ts
|
|
72
|
+
// 跑最小任务——C2 outcome + C3 stream 不变量(app-server 设计 §3.4 不变量 1)。
|
|
73
|
+
// 2026-09 起单一 app-server 形态即缺省路径(无模式钉扎 env);更深断言(schema/
|
|
74
|
+
// abort/进程锚定)由 engines/zcode/__tests__/zcode-engine.live.test.ts 承载。
|
|
74
75
|
it("zcode:app-server 常驻通道 run 全链(C2 outcome 无 error + C3 stream 事件不变量)", async (testCtx) => {
|
|
75
76
|
const model = process.env["ZCODE_E2E_MODEL"];
|
|
76
77
|
if (model === undefined || model === "") {
|
|
@@ -79,11 +80,10 @@ describe.skipIf(!LIVE)("conformance run 层(真实 spawn,手动门)", () =
|
|
|
79
80
|
}
|
|
80
81
|
const engine = new ZcodeEngine({
|
|
81
82
|
engineDataDir: () => "/tmp/zcode-conformance-live-appserver",
|
|
82
|
-
processEnv: { ...process.env, XYZ_ZCODE_MODE: "appserver" },
|
|
83
83
|
});
|
|
84
84
|
const events: AgentEvent[] = [];
|
|
85
85
|
const { outcome } = await engine.run(
|
|
86
|
-
{
|
|
86
|
+
{ prompt: "Reply with the single word: ok", description: "live-appserver-c2", model, cwd: "/tmp" },
|
|
87
87
|
{ taskId: "sa-live-zcode-appserver", poolKey: "", onEvent: (e) => events.push(e) },
|
|
88
88
|
);
|
|
89
89
|
expect(outcome.error).toBeUndefined();
|
|
@@ -200,9 +200,9 @@ describe.skipIf(!LIVE)("conformance relay 变体(经代理 spawn 全链,手
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
try {
|
|
203
|
-
const engine = createPiEngine(() => service);
|
|
203
|
+
const engine = createPiEngine(() => service as unknown as PiEngineService);
|
|
204
204
|
const events: AgentEvent[] = [];
|
|
205
|
-
const task:
|
|
205
|
+
const task: AgentCallOpts = { prompt: "Reply with the single word: ok", description: "live-relay-c2" };
|
|
206
206
|
const ctx: RunContext = {
|
|
207
207
|
taskId: "sa-live-pi-relay",
|
|
208
208
|
poolKey: "shared",
|
|
@@ -221,7 +221,7 @@ describe.skipIf(!LIVE)("conformance relay 变体(经代理 spawn 全链,手
|
|
|
221
221
|
else process.env[key] = savedEnv[key];
|
|
222
222
|
}
|
|
223
223
|
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
224
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
224
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
225
225
|
}
|
|
226
226
|
});
|
|
227
227
|
});
|
|
@@ -15,7 +15,7 @@ import { describe, expect, it } from "vitest";
|
|
|
15
15
|
|
|
16
16
|
import type { AgentEvent } from "../../../types.ts";
|
|
17
17
|
import { JournalWriter, replayJournal } from "../../common/event-journal.ts";
|
|
18
|
-
import { parseSpawnLine } from "
|
|
18
|
+
import { parseSpawnLine } from "../../engines/pi/spawn-event-adapter.ts";
|
|
19
19
|
import { assertAgentEventInvariants } from "./agent-event-invariants.ts";
|
|
20
20
|
|
|
21
21
|
interface PiGoldenFile {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
// golden-replay.zcode.test.ts —— zcode 引擎 golden 回放层(conformance 免 LLM 默认
|
|
2
|
-
//
|
|
3
|
-
// zcode-golden-
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// golden-replay.zcode.test.ts —— zcode 引擎 golden 回放层(conformance 免 LLM 默认
|
|
2
|
+
// CI 层)。语料 = app-server NDJSON 帧序列(附录 A.2 任务生命周期帧序;采集 SSOT
|
|
3
|
+
// = engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json)——双副本 diff
|
|
4
|
+
// 校验(fixture 与 golden-sample.ts 内嵌副本一致,防漂移:更新样本必须同步两处,
|
|
5
|
+
// 探针的干跑校验消费内嵌副本,两处不一致 = 探针在测旧契约)+ 帧形态不变量断言
|
|
6
|
+
// (A8「golden 帧序列语料 diff 通过」的本仓段)。
|
|
6
7
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// 回放层,帧形态不变量在此断言;A8「golden 帧序列语料 diff 通过」的本仓段)。
|
|
8
|
+
// 2026-09 重构(共享宿主 HOME):CLI spawn 链删除后 spawn stdout 语料(单 JSON
|
|
9
|
+
// 样本及其 fixture)随 spawn 回归一并删除,本层仅保留 app-server 帧序列语料
|
|
10
|
+
// (常驻通道唯一生产形态)。
|
|
11
11
|
|
|
12
12
|
import { readFileSync } from "node:fs";
|
|
13
13
|
import { dirname, join } from "node:path";
|
|
@@ -15,86 +15,21 @@ import { fileURLToPath } from "node:url";
|
|
|
15
15
|
|
|
16
16
|
import { describe, expect, it } from "vitest";
|
|
17
17
|
|
|
18
|
-
import { ZCODE_APPSERVER_GOLDEN
|
|
19
|
-
import {
|
|
20
|
-
mapZcodeOutcomeUsage,
|
|
21
|
-
parseZcodeTerminal,
|
|
22
|
-
synthesizeCoarseEvents,
|
|
23
|
-
} from "../../engines/zcode/parser.ts";
|
|
24
|
-
import { assertAgentEventInvariants } from "./agent-event-invariants.ts";
|
|
18
|
+
import { ZCODE_APPSERVER_GOLDEN } from "../../engines/zcode/golden-sample.ts";
|
|
25
19
|
|
|
26
20
|
// conformance 目录(engine/__tests__/conformance)→ engine 根的相对定位
|
|
27
|
-
const
|
|
21
|
+
const appserverFixturePath = join(
|
|
28
22
|
dirname(fileURLToPath(import.meta.url)),
|
|
29
23
|
"..", "..", // conformance → __tests__ → engine
|
|
30
|
-
"engines", "zcode", "__tests__", "__fixtures__", "zcode-golden-
|
|
24
|
+
"engines", "zcode", "__tests__", "__fixtures__", "zcode-golden-appserver.json",
|
|
31
25
|
);
|
|
32
26
|
|
|
33
|
-
interface ZcodeGoldenFile {
|
|
34
|
-
_meta: { engineVersion: string; exitCode: number };
|
|
35
|
-
stdoutRaw: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const fixture = JSON.parse(readFileSync(fixturePath, "utf8")) as ZcodeGoldenFile;
|
|
39
|
-
|
|
40
|
-
// [R6] app-server 帧序列语料 fixture(采集 SSOT——双副本 diff 的另一副本)
|
|
41
|
-
const appserverFixturePath = join(dirname(fixturePath), "zcode-golden-appserver.json");
|
|
42
27
|
interface AppServerGoldenFile {
|
|
43
28
|
_meta: Record<string, unknown>;
|
|
44
29
|
frames: { createResponse: string; pushStream: string[]; terminal: string[]; readResponse: string };
|
|
45
30
|
}
|
|
46
31
|
const appserverFixture = JSON.parse(readFileSync(appserverFixturePath, "utf8")) as AppServerGoldenFile;
|
|
47
32
|
|
|
48
|
-
describe("zcode golden 回放(conformance C3,免 LLM/免二进制)", () => {
|
|
49
|
-
it("双副本 diff:golden-sample.ts 内嵌副本 === fixture stdoutRaw(防漂移,A12)", () => {
|
|
50
|
-
expect(ZCODE_GOLDEN_STDOUT).toBe(fixture.stdoutRaw);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("parser 对实录样本回归:sessionId/response/usage 形状完整(探针干跑同源断言)", () => {
|
|
54
|
-
const terminal = parseZcodeTerminal(fixture.stdoutRaw);
|
|
55
|
-
expect(terminal.ok).toBe(true);
|
|
56
|
-
if (!terminal.ok) return;
|
|
57
|
-
expect(terminal.payload.sessionId).toMatch(/^sess_/);
|
|
58
|
-
expect(terminal.payload.response).toBe("ok");
|
|
59
|
-
expect(terminal.payload.usage).toEqual({ input: 12599, output: 17, cacheRead: 512, cacheWrite: 0 });
|
|
60
|
-
// 终态层 usage(orchestration 版):cost 显式 0(无来源不给残缺)、contextTokens 取 projection
|
|
61
|
-
expect(terminal.payload.outcomeUsage).toEqual({
|
|
62
|
-
input: 12599,
|
|
63
|
-
output: 17,
|
|
64
|
-
cacheRead: 512,
|
|
65
|
-
cacheWrite: 0,
|
|
66
|
-
cost: 0,
|
|
67
|
-
contextTokens: 12616,
|
|
68
|
-
turns: 1,
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("coarse 事件合成满足产出不变量(turn_end 最后、其前必有 message_end、usage 完整)", () => {
|
|
73
|
-
const terminal = parseZcodeTerminal(fixture.stdoutRaw);
|
|
74
|
-
if (!terminal.ok) throw new Error("golden 样本解析失败(上一用例应已转红)");
|
|
75
|
-
const events = synthesizeCoarseEvents(terminal.payload.response, terminal.payload.usage);
|
|
76
|
-
assertAgentEventInvariants(events, { granularity: "coarse" });
|
|
77
|
-
expect(events.map((e) => e.type)).toEqual(["message_end", "turn_end"]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("usage 无来源样本:mapZcodeOutcomeUsage 显式缺省(不给残缺对象,不变量 2 的输入面)", () => {
|
|
81
|
-
expect(mapZcodeOutcomeUsage(undefined, undefined)).toBeUndefined();
|
|
82
|
-
expect(mapZcodeOutcomeUsage({ inputTokens: 1 }, undefined)).toEqual({
|
|
83
|
-
input: 1,
|
|
84
|
-
output: 0,
|
|
85
|
-
cacheRead: 0,
|
|
86
|
-
cacheWrite: 0,
|
|
87
|
-
cost: 0,
|
|
88
|
-
contextTokens: 0,
|
|
89
|
-
turns: 1,
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// ============================================================
|
|
95
|
-
// [R6] app-server 帧序列语料回放(常驻通道主力语料,conformance 层接管双副本 diff)
|
|
96
|
-
// ============================================================
|
|
97
|
-
|
|
98
33
|
describe("zcode app-server golden 回放(conformance C3 帧形态,免 LLM/免二进制)", () => {
|
|
99
34
|
it("双副本 diff:golden-sample.ts 内嵌副本 === fixture frames(防漂移,A8 本仓段)", () => {
|
|
100
35
|
expect(ZCODE_APPSERVER_GOLDEN.createResponse).toBe(appserverFixture.frames.createResponse);
|
|
@@ -141,7 +76,7 @@ describe("zcode app-server golden 回放(conformance C3 帧形态,免 LLM/
|
|
|
141
76
|
expect(readText).toBe(deltaJoined); // text_delta 拼接 == 收尾帧 == read 全文
|
|
142
77
|
});
|
|
143
78
|
|
|
144
|
-
it("终态权威帧:turn.terminal 存在且先于收尾帧;usage 与
|
|
79
|
+
it("终态权威帧:turn.terminal 存在且先于收尾帧;usage 与 read step-finish tokens 同源(漂移对照锚点)", () => {
|
|
145
80
|
const terminal = ZCODE_APPSERVER_GOLDEN.terminal.map((l) => JSON.parse(l) as {
|
|
146
81
|
params?: { kind?: string; status?: string; payload?: { usage?: { inputTokens: number; outputTokens: number } } };
|
|
147
82
|
});
|
|
@@ -151,11 +86,15 @@ describe("zcode app-server golden 回放(conformance C3 帧形态,免 LLM/
|
|
|
151
86
|
expect(terminal[terminalFrameIdx]?.params?.status).toBe("success");
|
|
152
87
|
expect(terminalFrameIdx).toBeLessThan(closingFrameIdx);
|
|
153
88
|
|
|
154
|
-
|
|
155
|
-
|
|
89
|
+
// usage 跨源对照锚点:收尾帧 usage == read 应答 step-finish tokens(同一轮真实
|
|
90
|
+
// 计费面,两源必须一致;旧 spawn golden 对照随语料删除)
|
|
91
|
+
const read = JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse) as {
|
|
92
|
+
messages: Array<{ info: { role: string }; parts: Array<{ type: string; tokens?: { input: number; output: number } }> }>;
|
|
93
|
+
};
|
|
94
|
+
const stepFinish = (read.messages.at(-1)?.parts ?? []).find((p) => p.type === "step-finish");
|
|
156
95
|
expect(terminal[closingFrameIdx]?.params?.payload?.usage).toEqual({
|
|
157
|
-
inputTokens:
|
|
158
|
-
outputTokens:
|
|
96
|
+
inputTokens: stepFinish?.tokens?.input,
|
|
97
|
+
outputTokens: stepFinish?.tokens?.output,
|
|
159
98
|
});
|
|
160
99
|
});
|
|
161
100
|
});
|
|
@@ -38,7 +38,7 @@ export interface AppserverHarness {
|
|
|
38
38
|
dispose(): Promise<void>;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/** 组装连到 fake-appserver
|
|
41
|
+
/** 组装连到 fake-appserver 的常驻引擎(单一 app-server 形态——2026-09 起无模式分派)。 */
|
|
42
42
|
export function makeAppserverHarness(opts: AppserverHarnessOptions = {}): AppserverHarness {
|
|
43
43
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "zcode-conformance-appserver-"));
|
|
44
44
|
const dataDir = path.join(tmpRoot, "data");
|
|
@@ -78,9 +78,6 @@ export function makeAppserverHarness(opts: AppserverHarnessOptions = {}): Appser
|
|
|
78
78
|
sources: { v2ConfigPath: v2Path },
|
|
79
79
|
processEnv: {
|
|
80
80
|
PATH: process.env.PATH ?? "",
|
|
81
|
-
// 定向 appserver(R5 D2①):不探不降——conformance 测常驻路径本体,缺省路径的
|
|
82
|
-
// 探针门控/降级链归 zcode-engine-degrade.test.ts
|
|
83
|
-
XYZ_ZCODE_MODE: "appserver",
|
|
84
81
|
FAKE_STATE_FILE: stateFile,
|
|
85
82
|
FAKE_SESSION_SCENARIO: scenarioFile,
|
|
86
83
|
},
|
|
@@ -92,7 +89,7 @@ export function makeAppserverHarness(opts: AppserverHarnessOptions = {}): Appser
|
|
|
92
89
|
dataDir,
|
|
93
90
|
dispose: () =>
|
|
94
91
|
engine.dispose().finally(() => {
|
|
95
|
-
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
92
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
96
93
|
}),
|
|
97
94
|
};
|
|
98
95
|
}
|
|
@@ -114,8 +111,8 @@ export function readFakeState(file: string): Array<Record<string, unknown>> {
|
|
|
114
111
|
/** fake 收到的客户端帧方法名序列(abort 链「stop 先于杀链」断言面)。 */
|
|
115
112
|
export function sentMethodNames(stateFile: string): string[] {
|
|
116
113
|
return readFakeState(stateFile)
|
|
117
|
-
.map((e) => e["frame"])
|
|
118
|
-
.filter((f): f is Record<string, unknown> => typeof f === "object" && f !== null && typeof f["method"] === "string")
|
|
114
|
+
.map((e) => (e as Record<string, unknown>)["frame"])
|
|
115
|
+
.filter((f): f is Record<string, unknown> => typeof f === "object" && f !== null && typeof (f as Record<string, unknown>)["method"] === "string")
|
|
119
116
|
.map((f) => f["method"] as string);
|
|
120
117
|
}
|
|
121
118
|
|
|
@@ -15,8 +15,20 @@ import { clearEngines, registerEngine } from "../registry.ts";
|
|
|
15
15
|
function stubEngine(id: string): EnginePort {
|
|
16
16
|
return {
|
|
17
17
|
id,
|
|
18
|
-
capabilities: () => ({
|
|
19
|
-
|
|
18
|
+
capabilities: () => ({
|
|
19
|
+
schemaEnforcement: "emulated",
|
|
20
|
+
steer: "unsupported",
|
|
21
|
+
conversation: "unsupported",
|
|
22
|
+
personaInjection: "prompt",
|
|
23
|
+
eventGranularity: "coarse",
|
|
24
|
+
sandbox: "none",
|
|
25
|
+
sessionRead: "outcome-only",
|
|
26
|
+
resume: "unsupported",
|
|
27
|
+
interrupt: "kill-only",
|
|
28
|
+
permissionMode: "ignored",
|
|
29
|
+
maxTurns: false,
|
|
30
|
+
}),
|
|
31
|
+
probe: async () => ({ ok: true, engineVersion: "test", checks: [] }),
|
|
20
32
|
run: async () => {
|
|
21
33
|
throw new Error("unused");
|
|
22
34
|
},
|
|
@@ -38,7 +50,7 @@ beforeEach(() => {
|
|
|
38
50
|
|
|
39
51
|
afterEach(() => {
|
|
40
52
|
clearEngines();
|
|
41
|
-
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
53
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
42
54
|
});
|
|
43
55
|
|
|
44
56
|
describe("syncEnginesFile", () => {
|
|
@@ -16,8 +16,20 @@ import { buildEngineModelsPromptAppend, buildSubagentEngineSection } from "../mo
|
|
|
16
16
|
function fakeEngine(id: string, models: Array<{ id: string; name?: string }> | null): EnginePort {
|
|
17
17
|
return {
|
|
18
18
|
id,
|
|
19
|
-
capabilities: () => ({
|
|
20
|
-
|
|
19
|
+
capabilities: () => ({
|
|
20
|
+
schemaEnforcement: "emulated",
|
|
21
|
+
steer: "unsupported",
|
|
22
|
+
conversation: "unsupported",
|
|
23
|
+
personaInjection: "prompt",
|
|
24
|
+
eventGranularity: "coarse",
|
|
25
|
+
sandbox: "none",
|
|
26
|
+
sessionRead: "outcome-only",
|
|
27
|
+
resume: "unsupported",
|
|
28
|
+
interrupt: "kill-only",
|
|
29
|
+
permissionMode: "ignored",
|
|
30
|
+
maxTurns: false,
|
|
31
|
+
}),
|
|
32
|
+
probe: async () => ({ ok: true, engineVersion: "test", checks: [] }),
|
|
21
33
|
run: async () => {
|
|
22
34
|
throw new Error("not used in this test");
|
|
23
35
|
},
|
|
@@ -38,7 +50,7 @@ beforeEach(() => {
|
|
|
38
50
|
|
|
39
51
|
afterEach(() => {
|
|
40
52
|
clearEngines();
|
|
41
|
-
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
53
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
42
54
|
});
|
|
43
55
|
|
|
44
56
|
describe("buildEngineModelsPromptAppend(defaultEngine 开关语义)", () => {
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
listEngines,
|
|
18
18
|
registerEngine,
|
|
19
19
|
} from "../registry.ts";
|
|
20
|
-
import type {
|
|
20
|
+
import type { SessionView } from "../types.ts";
|
|
21
|
+
import type { AgentCallOpts } from "../../../orchestration/models/types.ts";
|
|
21
22
|
|
|
22
23
|
/** 最小可运行假引擎(完整实现 EnginePort 五面——不 cast,防接口漂移失检)。 */
|
|
23
24
|
function makeFakeEngine(id: string): EnginePort {
|
|
@@ -34,10 +35,11 @@ function makeFakeEngine(id: string): EnginePort {
|
|
|
34
35
|
resume: "unsupported",
|
|
35
36
|
interrupt: "kill-only",
|
|
36
37
|
permissionMode: "fixed",
|
|
38
|
+
maxTurns: false,
|
|
37
39
|
}),
|
|
38
40
|
probe: () =>
|
|
39
41
|
Promise.resolve({ ok: true, engineVersion: "0.0.0-test", checks: [{ name: "stub", ok: true }] }),
|
|
40
|
-
run: (_task:
|
|
42
|
+
run: (_task: AgentCallOpts, _ctx: RunContext) =>
|
|
41
43
|
Promise.reject(new Error("fake engine: run not implemented")),
|
|
42
44
|
interact: () => Promise.resolve({ ok: false, code: "engine_capability_unsupported", message: "stub" }),
|
|
43
45
|
read: (_handle: Parameters<EnginePort["read"]>[0]): Promise<SessionView> =>
|
|
@@ -6,8 +6,10 @@ import { describe, expect, it } from "vitest";
|
|
|
6
6
|
import type { EnginePort, RunContext } from "../port.ts";
|
|
7
7
|
import { DEFAULT_ENGINE_ID, EngineNotFoundError } from "../registry.ts";
|
|
8
8
|
import { EngineError } from "../common/errors.ts";
|
|
9
|
-
import type {
|
|
10
|
-
import {
|
|
9
|
+
import type { ProbeReport, SessionView } from "../types.ts";
|
|
10
|
+
import type { AgentCallOpts } from "../../../orchestration/models/types.ts";
|
|
11
|
+
import { resolveEngineRouting, routeEngine, routeEngineForHost, type EngineRouteOptions, type HostRouteOptions } from "../routing.ts";
|
|
12
|
+
import type { EngineRouteResult } from "../routing.ts";
|
|
11
13
|
|
|
12
14
|
/** 最小可运行假引擎(probe 结果可注入)。 */
|
|
13
15
|
function makeFakeEngine(id: string, probeOk: boolean): EnginePort {
|
|
@@ -24,6 +26,7 @@ function makeFakeEngine(id: string, probeOk: boolean): EnginePort {
|
|
|
24
26
|
resume: "cold",
|
|
25
27
|
interrupt: "kill-only",
|
|
26
28
|
permissionMode: "native",
|
|
29
|
+
maxTurns: false,
|
|
27
30
|
}),
|
|
28
31
|
probe: () =>
|
|
29
32
|
Promise.resolve(
|
|
@@ -36,7 +39,7 @@ function makeFakeEngine(id: string, probeOk: boolean): EnginePort {
|
|
|
36
39
|
error: { code: "engine_probe_failed", recovery: "reinstall the engine binary and retry the probe" },
|
|
37
40
|
} satisfies ProbeReport,
|
|
38
41
|
),
|
|
39
|
-
run: (_t:
|
|
42
|
+
run: (_t: AgentCallOpts, _c: RunContext) => Promise.reject(new Error("fake: run not implemented")),
|
|
40
43
|
interact: () => Promise.resolve({ ok: false, code: "engine_capability_unsupported", message: "stub" }),
|
|
41
44
|
read: (_h: Parameters<EnginePort["read"]>[0]): Promise<SessionView> =>
|
|
42
45
|
Promise.resolve({ engineId: id, turns: [], source: "outcome-only" }),
|
|
@@ -162,9 +165,10 @@ describe("routeEngine:路由 + 探针 + 守卫编排(验收 1/2)", () => {
|
|
|
162
165
|
expect((err as EngineError).recovery).toContain("retry the probe");
|
|
163
166
|
});
|
|
164
167
|
|
|
165
|
-
it("守卫 b(首期与 a
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
+
it("守卫 b(首期与 a 合流):能力依赖声明无独立载体,frontmatter 指定(非 call)不阻断 fallback", async () => {
|
|
169
|
+
// [D6 合流] 原 AgentTaskSpec.requires 形状预留已随任务形状合流裁撤(无生产写入方,
|
|
170
|
+
// 见 AgentCallOpts 类型注释);行为面保持:frontmatter 指定(非 call)+ 无 model
|
|
171
|
+
// → 仍走 fallback(能力依赖声明的独立载体留给将来下钻)
|
|
168
172
|
const { opts } = makeRoute({ zcodeProbeOk: false, routing: { agentEngine: "zcode" } });
|
|
169
173
|
const result = await routeEngine(opts);
|
|
170
174
|
expect(result.engineFallback).toBeDefined();
|
|
@@ -229,3 +233,108 @@ describe("routeEngine:路由 + 探针 + 守卫编排(验收 1/2)", () => {
|
|
|
229
233
|
expect(result.engineFallback).toEqual({ from: "zcode", reason: "engine_probe_failed" });
|
|
230
234
|
});
|
|
231
235
|
});
|
|
236
|
+
|
|
237
|
+
// ============================================================
|
|
238
|
+
// routeEngineForHost(D3-② 路由单点:宿主两调用点的统一编排)
|
|
239
|
+
// ============================================================
|
|
240
|
+
|
|
241
|
+
describe("routeEngineForHost:宿主统一路由(D3-②)", () => {
|
|
242
|
+
/** 本地 pi 引擎实例替身(chat 域 = chatPiEngine / workflow 域 = SAR per-session DI)。 */
|
|
243
|
+
function makeLocalPi(): EnginePort {
|
|
244
|
+
return makeFakeEngine("local-pi", true);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** 装配 host 路由参数(registry 面复用 makeRoute 的注入件)。 */
|
|
248
|
+
function makeHostRoute(overrides?: Parameters<typeof makeRoute>[0]): {
|
|
249
|
+
hostOpts: HostRouteOptions;
|
|
250
|
+
probeCalls: string[];
|
|
251
|
+
engines: Map<string, EnginePort>;
|
|
252
|
+
piEngine: EnginePort;
|
|
253
|
+
} {
|
|
254
|
+
const { opts, probeCalls, engines } = makeRoute(overrides);
|
|
255
|
+
const piEngine = makeLocalPi();
|
|
256
|
+
const { routing, ...rest } = opts;
|
|
257
|
+
return { hostOpts: { ...rest, routing: routing ?? {}, piEngine }, probeCalls, engines, piEngine };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
it("pi 请求(缺省):同步短路——返回值不是 Promise(零微任务,缺省路径时序契约)且免探", () => {
|
|
261
|
+
const { hostOpts, piEngine, probeCalls } = makeHostRoute();
|
|
262
|
+
const routed = routeEngineForHost(hostOpts);
|
|
263
|
+
expect(routed).not.toBeInstanceOf(Promise);
|
|
264
|
+
const route = routed as EngineRouteResult;
|
|
265
|
+
expect(route.engine).toBe(piEngine); // 本地 pi 实例接管,不经 registry
|
|
266
|
+
expect(route.engineId).toBe("pi");
|
|
267
|
+
expect(route.source).toBe("default");
|
|
268
|
+
expect(probeCalls).toEqual([]); // pi 免探(D7 轻量口径,缺省路径零 probe 开销)
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("显式 engine='pi':同步短路同形(call source 留痕)", () => {
|
|
272
|
+
const { hostOpts, piEngine } = makeHostRoute({ routing: { callEngine: "pi" } });
|
|
273
|
+
const routed = routeEngineForHost(hostOpts);
|
|
274
|
+
expect(routed).not.toBeInstanceOf(Promise);
|
|
275
|
+
const route = routed as EngineRouteResult;
|
|
276
|
+
expect(route.engine).toBe(piEngine);
|
|
277
|
+
expect(route.source).toBe("call");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("非 pi 请求(frontmatter zcode + probe ok):返回 Promise,resolve 经注入获取引擎", async () => {
|
|
281
|
+
const { hostOpts, engines } = makeHostRoute({ routing: { agentEngine: "zcode" } });
|
|
282
|
+
const routed = routeEngineForHost(hostOpts);
|
|
283
|
+
expect(routed).toBeInstanceOf(Promise);
|
|
284
|
+
const route = await routed;
|
|
285
|
+
expect(route.engine).toBe(engines.get("zcode"));
|
|
286
|
+
expect(route.engineId).toBe("zcode");
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("probe 失败兜底回 pi:本地 pi 实例接管(不依赖 registry 的 pi 注册态)+ fallback 留痕", async () => {
|
|
290
|
+
const { hostOpts, piEngine } = makeHostRoute({
|
|
291
|
+
zcodeProbeOk: false,
|
|
292
|
+
routing: { agentEngine: "zcode" },
|
|
293
|
+
});
|
|
294
|
+
const route = await routeEngineForHost(hostOpts);
|
|
295
|
+
expect(route.engine).toBe(piEngine);
|
|
296
|
+
expect(route.engineId).toBe("pi");
|
|
297
|
+
expect(route.requestedEngineId).toBe("zcode");
|
|
298
|
+
expect(route.engineFallback).toEqual({ from: "zcode", reason: "engine_probe_failed" });
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it("registry 面未注册 pi:兜底/清单两口径都不把本地 pi 漏报(SAR 单测 mock 形态)", async () => {
|
|
302
|
+
// registry 面只含 zcode(probe 失败)——模拟「本地 pi 不在全局注册表」的注入形态
|
|
303
|
+
const engines = new Map<string, EnginePort>([["zcode", makeFakeEngine("zcode", false)]]);
|
|
304
|
+
const probeCalls: string[] = [];
|
|
305
|
+
const piEngine = makeLocalPi();
|
|
306
|
+
const mkOpts = (): HostRouteOptions => ({
|
|
307
|
+
routing: { agentEngine: "zcode" },
|
|
308
|
+
strict: false,
|
|
309
|
+
probe: (id) => {
|
|
310
|
+
probeCalls.push(id);
|
|
311
|
+
return engines.get(id)!.probe();
|
|
312
|
+
},
|
|
313
|
+
piEngine,
|
|
314
|
+
getEngineFn: (id) => {
|
|
315
|
+
const e = engines.get(id);
|
|
316
|
+
if (e === undefined) throw new EngineNotFoundError(id, [...engines.keys()]);
|
|
317
|
+
return e;
|
|
318
|
+
},
|
|
319
|
+
hasEngineFn: (id) => engines.has(id),
|
|
320
|
+
listEnginesFn: () => [...engines.keys()],
|
|
321
|
+
});
|
|
322
|
+
// probe 失败 + 无守卫 → 兜底回 pi:本地实例接管,不触 registry 的 pi 缺失
|
|
323
|
+
const route = await routeEngineForHost(mkOpts());
|
|
324
|
+
expect(route.engine).toBe(piEngine);
|
|
325
|
+
expect(route.engineId).toBe("pi");
|
|
326
|
+
expect(probeCalls).toEqual(["zcode"]);
|
|
327
|
+
// 未注册 id:engine_not_found 文案清单含 pi(本地 pi 恒可用,不漏报)
|
|
328
|
+
const ghostOpts: HostRouteOptions = {
|
|
329
|
+
...mkOpts(),
|
|
330
|
+
routing: { callEngine: "ghost" },
|
|
331
|
+
};
|
|
332
|
+
const err = await Promise.resolve(routeEngineForHost(ghostOpts)).then(
|
|
333
|
+
(r: unknown) => r,
|
|
334
|
+
(e: unknown) => e,
|
|
335
|
+
);
|
|
336
|
+
expect(err).toBeInstanceOf(Error);
|
|
337
|
+
expect((err as Error).message).toContain("engine_not_found");
|
|
338
|
+
expect((err as EngineNotFoundError).registered).toContain("pi");
|
|
339
|
+
});
|
|
340
|
+
});
|