@zhushanwen/subagent-core 0.7.1 → 0.9.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/dist/{chunk-VURUAGMM.js → chunk-LZJOEDGQ.js} +603 -154
- package/dist/engine-discovery-scan-Da4Dkzsi.d.cts +818 -0
- package/dist/engine-discovery-scan-Da4Dkzsi.d.ts +818 -0
- package/dist/execution/engine/engine-discovery-scan.cjs +325 -154
- package/dist/execution/engine/engine-discovery-scan.d.cts +1 -2
- package/dist/execution/engine/engine-discovery-scan.d.ts +1 -2
- package/dist/execution/engine/engine-discovery-scan.js +1 -1
- package/dist/execution/relay-env.js +5 -4
- package/dist/index.cjs +9799 -7954
- package/dist/index.d.cts +1896 -1360
- package/dist/index.d.ts +1896 -1360
- package/dist/index.js +8952 -7513
- package/dist.bundle/index.cjs +7897 -6054
- package/package.json +5 -5
- package/src/__tests__/manifest-store.test.ts +16 -15
- package/src/__tests__/record-store-last-line.test.ts +12 -0
- package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
- package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
- package/src/core/logger.ts +1 -1
- package/src/execution/__tests__/alive-store.test.ts +117 -1
- package/src/execution/__tests__/batch-finalized.test.ts +206 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +117 -1
- package/src/execution/__tests__/{cold-resurrect.test.ts → cold-lookup.test.ts} +133 -33
- package/src/execution/__tests__/collect-coordinator-service.test.ts +7 -5
- package/src/execution/__tests__/conversation-continuation.test.ts +1452 -0
- package/src/execution/__tests__/delivery-methods.test.ts +90 -176
- package/src/execution/__tests__/dialog-queue.test.ts +70 -3
- package/src/execution/__tests__/dispose-manifest-recovery.test.ts +475 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +3 -3
- package/src/execution/__tests__/execute-nesting.test.ts +8 -5
- package/src/execution/__tests__/execution-record.test.ts +8 -148
- package/src/execution/__tests__/finalize-record.test.ts +295 -129
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +21 -29
- package/src/execution/__tests__/helpers/fake-engine-port.ts +13 -68
- package/src/execution/__tests__/helpers/legacy-sidecar.ts +33 -0
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +9 -5
- package/src/execution/__tests__/idle-gc.test.ts +60 -0
- package/src/execution/__tests__/inflight-production-wiring.test.ts +223 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +3 -254
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +43 -22
- package/src/execution/__tests__/model-config-service.test.ts +111 -0
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +10 -6
- package/src/execution/__tests__/nested-visibility.test.ts +13 -1
- package/src/execution/__tests__/notify-ledger.test.ts +42 -1
- package/src/execution/__tests__/rebuild-indexes.test.ts +329 -0
- package/src/execution/__tests__/record-binding.test.ts +613 -0
- package/src/execution/__tests__/record-origin.test.ts +318 -0
- package/src/execution/__tests__/record-store-intent-api.test.ts +600 -0
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +71 -3
- package/src/execution/__tests__/record-store.test.ts +72 -54
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +8 -5
- package/src/execution/__tests__/round-supervisor-workflow-origin.test.ts +205 -0
- package/src/execution/__tests__/run-orchestration-write-lease.test.ts +293 -0
- package/src/execution/__tests__/run-orchestration.test.ts +199 -0
- package/src/execution/__tests__/session-file-gc.test.ts +35 -4
- package/src/execution/__tests__/session-reconstructor.test.ts +78 -0
- package/src/execution/__tests__/state-marker.test.ts +288 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +8 -5
- package/src/execution/__tests__/subagent-actions-core.test.ts +67 -16
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +0 -1
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -11
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +17 -11
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +130 -53
- package/src/execution/__tests__/subagent-service.test.ts +17 -215
- package/src/execution/__tests__/subprocess-agent-runner-no-progress-killall.test.ts +138 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +105 -660
- package/src/execution/__tests__/sync-collect-recovery.test.ts +23 -16
- package/src/execution/__tests__/terminal-write-latency.test.ts +79 -0
- package/src/execution/__tests__/workflow-agent-dispatch.test.ts +782 -0
- package/src/execution/alive-store.ts +37 -28
- package/src/execution/{cold-resurrect.ts → cold-lookup.ts} +48 -45
- package/src/execution/config.ts +1 -1
- package/src/execution/conversation-continuation.ts +541 -0
- package/src/execution/dialog-queue.ts +60 -12
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +16 -1
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +17 -51
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +0 -5
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +5 -9
- package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +55 -53
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +0 -3
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
- package/src/execution/engine/__tests__/inflight-snapshot.test.ts +180 -0
- package/src/execution/engine/__tests__/model-prompt.test.ts +0 -3
- package/src/execution/engine/__tests__/registry.test.ts +0 -1
- package/src/execution/engine/__tests__/routing.test.ts +0 -1
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +0 -3
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +0 -3
- package/src/execution/engine/client/__tests__/engine-client-reap.test.ts +212 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +73 -1
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +2 -9
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +46 -12
- package/src/execution/engine/client/engine-client.ts +111 -26
- package/src/execution/engine/client/mirror.ts +9 -0
- package/src/execution/engine/client/remote-engine.ts +52 -34
- package/src/execution/engine/client/reverse-router.ts +6 -27
- package/src/execution/engine/common/__tests__/run-signals.test.ts +79 -0
- package/src/execution/engine/common/capability-gate.ts +27 -0
- package/src/execution/engine/common/data-dir.ts +3 -2
- package/src/execution/engine/common/errors.ts +1 -1
- package/src/execution/engine/common/journal-wiring.ts +18 -9
- package/src/execution/engine/common/run-signals.ts +83 -0
- package/src/execution/engine/d8-compat.ts +1 -1
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +3 -18
- package/src/execution/engine/host/host-bridge.ts +26 -41
- package/src/execution/engine/host/pi-host-binding.ts +5 -5
- package/src/execution/engine/host/spawned-children.ts +1 -1
- package/src/execution/engine/inflight-snapshot.ts +92 -0
- package/src/execution/engine/port.ts +52 -54
- package/src/execution/engine/types.ts +11 -14
- package/src/execution/execution-record.ts +15 -47
- package/src/execution/finalize-record.ts +94 -197
- package/src/execution/idle-gc.ts +9 -3
- package/src/execution/lifecycle-manager.ts +25 -361
- package/src/execution/lifecycle-predicates.ts +22 -19
- package/src/execution/manifest-store.ts +40 -42
- package/src/execution/model-config-service.ts +1 -1
- package/src/execution/notifier.ts +39 -1
- package/src/execution/notify-host.ts +13 -4
- package/src/execution/path-encoding.ts +6 -0
- package/src/execution/record-entry.ts +20 -2
- package/src/execution/record-store.ts +925 -160
- package/src/execution/relay-env.ts +8 -3
- package/src/execution/round-supervisor/domain.ts +17 -5
- package/src/execution/round-supervisor/service-binding.test.ts +89 -53
- package/src/execution/round-supervisor/service-binding.ts +71 -21
- package/src/execution/round-supervisor/supervisor.ts +23 -9
- package/src/execution/service/record-access.ts +547 -0
- package/src/execution/service/record-lifecycle.ts +542 -0
- package/src/execution/service/run-orchestration.ts +1647 -0
- package/src/execution/service/service-bootstrap.ts +111 -0
- package/src/execution/service/service-constants.ts +27 -0
- package/src/execution/service/session-baselines.ts +401 -0
- package/src/execution/service/sync-collect-domain.ts +442 -0
- package/src/execution/service/workflow-dispatch.ts +567 -0
- package/src/execution/session-file-gc.ts +11 -8
- package/src/execution/session-reconstructor.ts +44 -1
- package/src/execution/sessions-index.ts +20 -3
- package/src/execution/settled-watchdog.ts +86 -43
- package/src/execution/state-marker.ts +516 -0
- package/src/execution/stream-sink.ts +1 -1
- package/src/execution/subagent-actions-core.ts +53 -31
- package/src/execution/subagent-service.ts +643 -2955
- package/src/execution/subprocess-agent-runner.ts +46 -323
- package/src/execution/types.ts +60 -43
- package/src/execution/ui-request-handler-factory.ts +5 -0
- package/src/execution/worktree-manager.ts +1 -1
- package/src/execution/worktree-registry.ts +12 -8
- package/src/index.ts +16 -5
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -3
- package/src/orchestration/__tests__/agent-call-stream.test.ts +24 -49
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +4 -4
- package/src/orchestration/__tests__/file-run-store.test.ts +5 -7
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +2 -3
- package/src/orchestration/__tests__/run-snapshot.test.ts +27 -43
- package/src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts +274 -0
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +1 -1
- package/src/orchestration/models/ports.ts +20 -13
- package/src/orchestration/models/types.ts +11 -13
- package/src/orchestration/run-snapshot.ts +18 -22
- package/src/orchestration/worker-message-pump.ts +33 -57
- package/src/shared/atomic-write.ts +10 -8
- package/dist/chunk-T2SYBW3I.js +0 -24
- package/dist/engine-discovery-scan-ocpM8FmI.d.cts +0 -1608
- package/dist/engine-discovery-scan-ocpM8FmI.d.ts +0 -1608
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +0 -255
- package/src/execution/__tests__/finalized-marker.test.ts +0 -104
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -488
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +0 -75
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +0 -230
- package/src/execution/finalized-marker.ts +0 -70
- package/src/execution/tombstone-store.ts +0 -72
|
@@ -1,686 +1,131 @@
|
|
|
1
1
|
// src/execution/__tests__/subprocess-agent-runner.test.ts
|
|
2
2
|
//
|
|
3
|
-
//
|
|
3
|
+
// [H2 W4] SAR 壳装配契约测试——run() 已掏空为纯转调
|
|
4
|
+
// SubagentService.executeWorkflowAgent(编排归 service 单点,设计
|
|
5
|
+
// subagent-workflow-record-unification.md §5 W4 / §3.5 终态数据流)。
|
|
4
6
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
import { join } from "node:path";
|
|
20
|
-
|
|
21
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
// 本文件只锁壳契约面:
|
|
8
|
+
// 1. 构造签名(deps.subagentService + ctxModel 兼容——session-lifecycle.ts
|
|
9
|
+
// 装配点零改定的锚点);
|
|
10
|
+
// 2. run() 纯转调(opts/signal/onEvent/stream 原样透传 + parentRunId 用直调
|
|
11
|
+
// 占位 SAR_UNATTACHED_PARENT_RUN_ID + 返回值原样返回,零映射零吞错);
|
|
12
|
+
// 3. updateCtxModel 装配链兼容(model_select 刷新不炸;run() 不再消费 ctxModel);
|
|
13
|
+
// 4. mergeRunSignals re-export 可用(既有 import 路径契约)。
|
|
14
|
+
//
|
|
15
|
+
// [H2 W4 删除面] 旧编排内部用例(路由集成 / model 填底 / ctxModel 孪生守卫 /
|
|
16
|
+
// timeoutMs 合并 / onEvent-journal 桥接 / 直传保真 / no-progress 守护落点 /
|
|
17
|
+
// 全链 killAll)随 SAR 编排退役删除——service 落点的行为守护由
|
|
18
|
+
// workflow-agent-dispatch.test.ts(W2 承接:注册面/池顺序/守护 arm 键 record.id/
|
|
19
|
+
// 双刷新源/fire 追注/stream 自构/D7/D6/adopt 豁免)与 engine/__tests__/routing.test.ts
|
|
20
|
+
// (路由 helper 单点:三层优先级 + 守卫 a/b/c + strict + fallback)承接。
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
// 未 configureCore 即消费会抛 core_host_not_configured(SAR.run 内部调用,dataRoot
|
|
25
|
-
// 值不被本文件断言)——端口态显式配置,测试隔离经 resetCoreForTests
|
|
26
|
-
import { configureCore, resetCoreForTests } from "../../core/host-services.ts";
|
|
22
|
+
import { describe, expect, it, vi } from "vitest";
|
|
27
23
|
|
|
28
24
|
import type { AgentCallOpts, AgentResult } from "../../orchestration/models/types.ts";
|
|
29
|
-
import {
|
|
30
|
-
import
|
|
31
|
-
import { replayJournal } from "../engine/common/event-journal.ts";
|
|
32
|
-
import { clearEngines, registerEngine } from "../engine/registry.ts";
|
|
33
|
-
import type { EnginePort, RunContext } from "../engine/port.ts";
|
|
34
|
-
import type {
|
|
35
|
-
AgentOutcome,
|
|
36
|
-
EngineCapabilities,
|
|
37
|
-
ProbeReport,
|
|
38
|
-
} from "../engine/types.ts";
|
|
39
|
-
import type { SubprocessAgentRunnerDeps } from "../subprocess-agent-runner.ts";
|
|
25
|
+
import type { ModelInfo } from "../model-resolver.ts";
|
|
26
|
+
import { SubagentStream } from "../stream-sink.ts";
|
|
40
27
|
import type { SubagentService } from "../subagent-service.ts";
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
durationMs: 100,
|
|
51
|
-
error: undefined,
|
|
52
|
-
sessionId: undefined,
|
|
53
|
-
toolCalls: [],
|
|
54
|
-
...overrides,
|
|
55
|
-
};
|
|
28
|
+
import {
|
|
29
|
+
SAR_UNATTACHED_PARENT_RUN_ID,
|
|
30
|
+
SubprocessAgentRunner,
|
|
31
|
+
type SubprocessAgentRunnerDeps,
|
|
32
|
+
} from "../subprocess-agent-runner.ts";
|
|
33
|
+
import { mergeRunSignals } from "../engine/common/run-signals.ts";
|
|
34
|
+
|
|
35
|
+
function makeOpts(): AgentCallOpts {
|
|
36
|
+
return { prompt: "test task", agent: "worker" };
|
|
56
37
|
}
|
|
57
38
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* pi-subagent-cli 引擎包,此处仅为保留 executeAndAwait 委托断言面)。 */
|
|
61
|
-
function fakeAgentCallToExecuteOptions(task: AgentCallOpts, ctxModel?: ModelInfo): Record<string, unknown> {
|
|
62
|
-
const rawSlug = task.description ?? task.agent ?? "workflow-agent";
|
|
63
|
-
return {
|
|
64
|
-
task: task.prompt,
|
|
65
|
-
slug: rawSlug.length > 35 ? rawSlug.slice(0, 35) : rawSlug,
|
|
66
|
-
agent: task.agent,
|
|
67
|
-
model: task.model,
|
|
68
|
-
thinkingLevel: task.thinkingLevel,
|
|
69
|
-
skillPath: task.skillPath,
|
|
70
|
-
appendSystemPrompt: task.appendSystemPrompt,
|
|
71
|
-
schema: task.schema,
|
|
72
|
-
schemaEnv: task.schemaEnv,
|
|
73
|
-
maxTurns: task.maxTurns,
|
|
74
|
-
graceTurns: task.graceTurns,
|
|
75
|
-
ctxModel,
|
|
76
|
-
fork: task.fork,
|
|
77
|
-
worktree: task.worktree,
|
|
78
|
-
cwd: task.cwd,
|
|
79
|
-
conversation: task.conversation,
|
|
80
|
-
idleTimeoutMs: task.idleTimeoutMs,
|
|
81
|
-
};
|
|
39
|
+
function makeResult(): AgentResult {
|
|
40
|
+
return { content: "OK", durationMs: 100, toolCalls: [] };
|
|
82
41
|
}
|
|
83
42
|
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
* 即自身,getter 直接返回 self。
|
|
90
|
-
* [W3 改写] 同步注册「委托式」替身 pi 引擎:run() 还原引擎内 task→ExecuteOptions
|
|
91
|
-
* 映射后委托 mock 的 executeAndAwait(原 inproc PiEngine 的 workflow 分支形态)——
|
|
92
|
-
* resolveHostPiEnginePort 对已注册 port 原样返回,SAR 路由命中替身。 */
|
|
93
|
-
function createMockService(impl?: typeof vi.fn): SubagentService {
|
|
94
|
-
const executeAndAwait = impl ?? vi.fn().mockResolvedValue(makeMockResult());
|
|
95
|
-
// partial mock(仅 executeAndAwait + asEngineService self-引用)——SAR 测试路径
|
|
96
|
-
// 不触达其余成员,经 unknown 双跳收敛到 SubagentService 视图
|
|
97
|
-
const partial: { executeAndAwait: typeof executeAndAwait; asEngineService?: unknown } = { executeAndAwait };
|
|
98
|
-
const service = partial as unknown as SubagentService;
|
|
99
|
-
partial.asEngineService = service;
|
|
100
|
-
|
|
101
|
-
const fakePort: EnginePort = {
|
|
102
|
-
id: "pi",
|
|
103
|
-
capabilities: (): EngineCapabilities => ({
|
|
104
|
-
schemaEnforcement: "native",
|
|
105
|
-
steer: "unsupported",
|
|
106
|
-
conversation: "native",
|
|
107
|
-
personaInjection: "flag",
|
|
108
|
-
eventGranularity: "stream",
|
|
109
|
-
sandbox: "emulated",
|
|
110
|
-
sessionRead: "full",
|
|
111
|
-
resume: "native",
|
|
112
|
-
interrupt: "kill-only",
|
|
113
|
-
permissionMode: "native",
|
|
114
|
-
maxTurns: true,
|
|
115
|
-
}),
|
|
116
|
-
probe: async (): Promise<ProbeReport> => ({ ok: true, engineVersion: "fake", checks: [] }),
|
|
117
|
-
run: async (task: AgentCallOpts, ctx: RunContext) => {
|
|
118
|
-
engineRunSpy(task, ctx);
|
|
119
|
-
const wfResult = await executeAndAwait(
|
|
120
|
-
fakeAgentCallToExecuteOptions(task, ctx.ctxModel),
|
|
121
|
-
ctx.signal,
|
|
122
|
-
ctx.onEvent,
|
|
123
|
-
ctx.stream,
|
|
124
|
-
) as { content: string; parsedOutput?: unknown; durationMs?: number; error?: string };
|
|
125
|
-
const outcome: AgentOutcome = {
|
|
126
|
-
content: wfResult.content,
|
|
127
|
-
parsedOutput: wfResult.parsedOutput,
|
|
128
|
-
durationMs: wfResult.durationMs,
|
|
129
|
-
error: wfResult.error,
|
|
130
|
-
engineId: "pi",
|
|
131
|
-
};
|
|
132
|
-
return {
|
|
133
|
-
handle: {
|
|
134
|
-
data: { v: 1, engineId: "pi", sessionRef: {}, poolKey: "shared", adapterVersion: "fake-sar" },
|
|
135
|
-
},
|
|
136
|
-
outcome,
|
|
137
|
-
};
|
|
138
|
-
},
|
|
139
|
-
interact: async () => ({ ok: false, code: "engine_interact_failed", message: "not supported in this test" }),
|
|
140
|
-
read: async () => ({ engineId: "pi", turns: [], source: "outcome-only" }),
|
|
141
|
-
};
|
|
142
|
-
clearEngines();
|
|
143
|
-
registerEngine("pi", () => fakePort);
|
|
144
|
-
return service;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function makeBaseOpts(): AgentCallOpts {
|
|
148
|
-
return {
|
|
149
|
-
prompt: "test task",
|
|
150
|
-
agent: "worker",
|
|
151
|
-
cwd: "/some/path",
|
|
152
|
-
schema: undefined,
|
|
153
|
-
model: undefined,
|
|
154
|
-
scene: undefined,
|
|
155
|
-
description: undefined,
|
|
156
|
-
timeoutMs: undefined,
|
|
157
|
-
skill: undefined,
|
|
158
|
-
skillPath: undefined,
|
|
159
|
-
appendSystemPrompt: undefined,
|
|
160
|
-
schemaEnv: undefined,
|
|
161
|
-
};
|
|
43
|
+
/** mock SubagentService——只实现 executeWorkflowAgent(转调目标),其余成员不触达。 */
|
|
44
|
+
function createMockService(impl?: ReturnType<typeof vi.fn>): SubagentService {
|
|
45
|
+
const executeWorkflowAgent = impl ?? vi.fn().mockResolvedValue(makeResult());
|
|
46
|
+
const partial = { executeWorkflowAgent };
|
|
47
|
+
return partial as unknown as SubagentService;
|
|
162
48
|
}
|
|
163
49
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
187
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
188
|
-
|
|
189
|
-
const opts = makeBaseOpts();
|
|
190
|
-
const signal = new AbortController().signal;
|
|
191
|
-
|
|
192
|
-
const result = await sar.run(opts, signal);
|
|
193
|
-
|
|
194
|
-
expect(result.content).toBe("hello world");
|
|
195
|
-
expect(mockService.executeAndAwait).toHaveBeenCalledTimes(1);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("不 reject — 失败信息入 result.error", async () => {
|
|
199
|
-
const mockService = createMockService(
|
|
200
|
-
vi.fn().mockResolvedValue(makeMockResult({ error: "some error" })),
|
|
201
|
-
);
|
|
202
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
203
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
204
|
-
|
|
205
|
-
const result = await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
206
|
-
expect(result.error).toBe("some error");
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// ────────────────────────────────────────────────
|
|
211
|
-
// T3.2: parsedOutput 透传
|
|
212
|
-
// ────────────────────────────────────────────────
|
|
213
|
-
describe("T3.2 parsedOutput 透传", () => {
|
|
214
|
-
it("parsedOutput 直通", async () => {
|
|
215
|
-
const parsedData = { x: 1, y: 2 };
|
|
216
|
-
const mockService = createMockService(
|
|
217
|
-
vi.fn().mockResolvedValue(makeMockResult({ content: "ok", parsedOutput: parsedData })),
|
|
218
|
-
);
|
|
219
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
220
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
221
|
-
|
|
222
|
-
const result = await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
223
|
-
expect(result.parsedOutput).toEqual(parsedData);
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// ────────────────────────────────────────────────
|
|
228
|
-
// T3.4: cwd 透传
|
|
229
|
-
// ────────────────────────────────────────────────
|
|
230
|
-
describe("T3.4 cwd 透传", () => {
|
|
231
|
-
it("cwd 传入 executeAndAwait 的 ExecuteOptions", async () => {
|
|
232
|
-
let capturedOpts: Record<string, unknown> | undefined;
|
|
233
|
-
const mockService = createMockService(
|
|
234
|
-
vi.fn().mockImplementation((opts: Record<string, unknown>) => {
|
|
235
|
-
capturedOpts = opts;
|
|
236
|
-
return Promise.resolve(makeMockResult());
|
|
237
|
-
}),
|
|
238
|
-
);
|
|
239
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
240
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
241
|
-
|
|
242
|
-
const opts = { ...makeBaseOpts(), cwd: "/custom/cwd" };
|
|
243
|
-
await sar.run(opts, new AbortController().signal);
|
|
244
|
-
|
|
245
|
-
expect(capturedOpts).toBeDefined();
|
|
246
|
-
expect(capturedOpts!.cwd).toBe("/custom/cwd");
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// ────────────────────────────────────────────────
|
|
251
|
-
// T3.5: model 填底
|
|
252
|
-
// ────────────────────────────────────────────────
|
|
253
|
-
describe("T3.5 model 填底 (D-008)", () => {
|
|
254
|
-
it("opts.model 优先", async () => {
|
|
255
|
-
let capturedOpts: Record<string, unknown> | undefined;
|
|
256
|
-
const mockService = createMockService(
|
|
257
|
-
vi.fn().mockImplementation((opts: Record<string, unknown>) => {
|
|
258
|
-
capturedOpts = opts;
|
|
259
|
-
return Promise.resolve(makeMockResult());
|
|
260
|
-
}),
|
|
261
|
-
);
|
|
262
|
-
const ctxModel: ModelInfo = { id: "ctx-model", name: "Ctx Model", provider: "test", reasoning: false };
|
|
263
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService, ctxModel };
|
|
264
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
265
|
-
|
|
266
|
-
const opts = { ...makeBaseOpts(), model: "explicit-model" };
|
|
267
|
-
await sar.run(opts, new AbortController().signal);
|
|
268
|
-
|
|
269
|
-
expect(capturedOpts!.model).toBe("explicit-model");
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it("opts.model 空 → ctxModel", async () => {
|
|
273
|
-
let capturedOpts: Record<string, unknown> | undefined;
|
|
274
|
-
const mockService = createMockService(
|
|
275
|
-
vi.fn().mockImplementation((opts: Record<string, unknown>) => {
|
|
276
|
-
capturedOpts = opts;
|
|
277
|
-
return Promise.resolve(makeMockResult());
|
|
278
|
-
}),
|
|
279
|
-
);
|
|
280
|
-
const ctxModel: ModelInfo = { id: "ctx-model", name: "Ctx Model", provider: "test", reasoning: false };
|
|
281
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService, ctxModel };
|
|
282
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
283
|
-
|
|
284
|
-
const opts = { ...makeBaseOpts(), model: undefined };
|
|
285
|
-
await sar.run(opts, new AbortController().signal);
|
|
286
|
-
|
|
287
|
-
// 修复后:opts.model 不再从 ctxModel.id 填底,ctxModel 作为完整对象透传
|
|
288
|
-
expect(capturedOpts!.model).toBeUndefined();
|
|
289
|
-
expect(capturedOpts!.ctxModel).toBe(ctxModel);
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// ────────────────────────────────────────────────
|
|
294
|
-
// [U1 D2] RunContext.modelRef 接入:ctxModel 继承路径孪生守卫
|
|
295
|
-
// ────────────────────────────────────────────────
|
|
296
|
-
describe("U1 ctxModel twin guard (RunContext.modelRef 接入)", () => {
|
|
297
|
-
const prevDataDir = process.env["XYZ_AGENT_DATA_DIR"];
|
|
298
|
-
let tmpRoot = "";
|
|
299
|
-
|
|
300
|
-
/** 装配含指定快照的 ModelConfigService 单例(initModel 注入 registry)。 */
|
|
301
|
-
function installModelService(registry: ModelRegistryLike): void {
|
|
302
|
-
tmpRoot = mkdtempSync(join(tmpdir(), "sar-model-ref-"));
|
|
303
|
-
process.env["XYZ_AGENT_DATA_DIR"] = join(tmpRoot, "engine-data");
|
|
304
|
-
const svc = new ModelConfigService({ agentDir: join(tmpRoot, "agent"), cwd: tmpRoot });
|
|
305
|
-
svc.initModel({ modelRegistry: registry, sessionId: "sar-guard" });
|
|
306
|
-
setModelConfigService(svc);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
afterEach(() => {
|
|
310
|
-
// 单例 slot 无 reset API,重装一个空注册服务隔离后续用例(同文件后续
|
|
311
|
-
// describe 不消费 ctxModel 守卫,getGlobalConfig 对空目录返回默认值)
|
|
312
|
-
if (tmpRoot) {
|
|
313
|
-
const svc = new ModelConfigService({ agentDir: join(tmpRoot, "agent"), cwd: tmpRoot });
|
|
314
|
-
svc.initModel({ modelRegistry: { getAvailable: () => [], find: () => undefined, hasConfiguredAuth: () => false }, sessionId: "cleanup" });
|
|
315
|
-
setModelConfigService(svc);
|
|
316
|
-
rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
317
|
-
tmpRoot = "";
|
|
318
|
-
}
|
|
319
|
-
if (prevDataDir === undefined) delete process.env["XYZ_AGENT_DATA_DIR"];
|
|
320
|
-
else process.env["XYZ_AGENT_DATA_DIR"] = prevDataDir;
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it("P-A2 拒单路径:ctxModel 命中但 registry 含孪生 → run 返回 error(ambiguous),executeAndAwait 未被调", async () => {
|
|
324
|
-
const models = [
|
|
325
|
-
{ id: "GLM-5.3-Flash", name: "flash", provider: "zai-coding-cn", reasoning: false },
|
|
326
|
-
{ id: "glm-5.3-flash", name: "flash lower", provider: "zai-coding-cn", reasoning: false },
|
|
327
|
-
];
|
|
328
|
-
installModelService({
|
|
329
|
-
getAvailable: () => models,
|
|
330
|
-
find: (p, id) => models.find((m) => m.provider === p && m.id === id),
|
|
331
|
-
hasConfiguredAuth: () => true,
|
|
332
|
-
});
|
|
333
|
-
const mockService = createMockService();
|
|
334
|
-
const sar = new SubprocessAgentRunner({
|
|
335
|
-
subagentService: mockService as unknown as SubprocessAgentRunnerDeps["subagentService"],
|
|
336
|
-
ctxModel: { id: "GLM-5.3-Flash", name: "flash", provider: "zai-coding-cn", reasoning: false },
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
const result = await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
340
|
-
|
|
341
|
-
expect(result.error).toMatch(/ambiguous case variants/);
|
|
342
|
-
expect(result.content).toBe("");
|
|
343
|
-
// 守卫在 engine.run 之前:不产生任何 record/spawn(委托零发生)
|
|
344
|
-
expect(mockService.executeAndAwait).not.toHaveBeenCalled();
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it("P-A2 放行路径:无孪生 registry 下同 ctxModel 正常委托(守卫零误伤)", async () => {
|
|
348
|
-
const models = [
|
|
349
|
-
{ id: "GLM-5.3-Flash", name: "flash", provider: "zai-coding-cn", reasoning: false },
|
|
350
|
-
];
|
|
351
|
-
installModelService({
|
|
352
|
-
getAvailable: () => models,
|
|
353
|
-
find: (p, id) => models.find((m) => m.provider === p && m.id === id),
|
|
354
|
-
hasConfiguredAuth: () => true,
|
|
355
|
-
});
|
|
356
|
-
const mockService = createMockService(
|
|
357
|
-
vi.fn().mockResolvedValue(makeMockResult({ content: "ok" })),
|
|
358
|
-
);
|
|
359
|
-
const sar = new SubprocessAgentRunner({
|
|
360
|
-
subagentService: mockService as unknown as SubprocessAgentRunnerDeps["subagentService"],
|
|
361
|
-
ctxModel: { id: "GLM-5.3-Flash", name: "flash", provider: "zai-coding-cn", reasoning: false },
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
const result = await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
365
|
-
|
|
366
|
-
expect(result.error).toBeUndefined();
|
|
367
|
-
expect(result.content).toBe("ok");
|
|
368
|
-
expect(mockService.executeAndAwait).toHaveBeenCalledTimes(1);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it("ctxModel 未指定时守卫跳过(无单例也不阻断)", async () => {
|
|
372
|
-
const mockService = createMockService(
|
|
373
|
-
vi.fn().mockResolvedValue(makeMockResult({ content: "ok" })),
|
|
374
|
-
);
|
|
375
|
-
const sar = new SubprocessAgentRunner({
|
|
376
|
-
subagentService: mockService as unknown as SubprocessAgentRunnerDeps["subagentService"],
|
|
377
|
-
});
|
|
378
|
-
const result = await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
379
|
-
expect(result.content).toBe("ok");
|
|
380
|
-
expect(mockService.executeAndAwait).toHaveBeenCalledTimes(1);
|
|
381
|
-
});
|
|
50
|
+
describe("SubprocessAgentRunner (H2 W4 纯转调壳)", () => {
|
|
51
|
+
it("run() 纯转调 executeWorkflowAgent:opts/signal/onEvent/stream 原样透传,parentRunId 用直调占位", async () => {
|
|
52
|
+
const executeWorkflowAgent = vi.fn().mockResolvedValue(makeResult());
|
|
53
|
+
const deps: SubprocessAgentRunnerDeps = { subagentService: createMockService(executeWorkflowAgent) };
|
|
54
|
+
const sar = new SubprocessAgentRunner(deps);
|
|
55
|
+
|
|
56
|
+
const opts = makeOpts();
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const onEvent = vi.fn();
|
|
59
|
+
const stream = new SubagentStream("sar-test-stream", { setWidget: () => {} });
|
|
60
|
+
const result = await sar.run(opts, controller.signal, onEvent, stream);
|
|
61
|
+
|
|
62
|
+
expect(executeWorkflowAgent).toHaveBeenCalledTimes(1);
|
|
63
|
+
expect(executeWorkflowAgent).toHaveBeenCalledWith(
|
|
64
|
+
opts,
|
|
65
|
+
SAR_UNATTACHED_PARENT_RUN_ID,
|
|
66
|
+
controller.signal,
|
|
67
|
+
onEvent,
|
|
68
|
+
stream,
|
|
69
|
+
);
|
|
70
|
+
// 返回值原样返回(executeWorkflowAgent 返回类型即 orchestration AgentResult,零映射)
|
|
71
|
+
expect(result).toEqual(makeResult());
|
|
382
72
|
});
|
|
383
73
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
describe("H1 ctxModel refresh via updateCtxModel", () => {
|
|
388
|
-
it("updateCtxModel 后 run() 传入新的 ctxModel 而非旧值", async () => {
|
|
389
|
-
let capturedOpts: Record<string, unknown> | undefined;
|
|
390
|
-
const mockService = createMockService(
|
|
391
|
-
vi.fn().mockImplementation((opts: Record<string, unknown>) => {
|
|
392
|
-
capturedOpts = opts;
|
|
393
|
-
return Promise.resolve(makeMockResult());
|
|
394
|
-
}),
|
|
395
|
-
);
|
|
396
|
-
const oldModel: ModelInfo = { id: "old-model", name: "Old Model", provider: "test", reasoning: false };
|
|
397
|
-
const newModel: ModelInfo = { id: "new-model", name: "New Model", provider: "test", reasoning: false };
|
|
398
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService, ctxModel: oldModel };
|
|
399
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
74
|
+
it("onEvent/stream 缺省时占位 undefined 透传(位置参数对齐 service 签名)", async () => {
|
|
75
|
+
const executeWorkflowAgent = vi.fn().mockResolvedValue(makeResult());
|
|
76
|
+
const sar = new SubprocessAgentRunner({ subagentService: createMockService(executeWorkflowAgent) });
|
|
400
77
|
|
|
401
|
-
|
|
402
|
-
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
await sar.run(makeOpts(), controller.signal);
|
|
403
80
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
81
|
+
expect(executeWorkflowAgent).toHaveBeenCalledWith(
|
|
82
|
+
makeOpts(),
|
|
83
|
+
SAR_UNATTACHED_PARENT_RUN_ID,
|
|
84
|
+
controller.signal,
|
|
85
|
+
undefined,
|
|
86
|
+
undefined,
|
|
87
|
+
);
|
|
410
88
|
});
|
|
411
89
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
it("timeoutMs > 0 → merged signal 传给 executeAndAwait", async () => {
|
|
417
|
-
let capturedSignal: AbortSignal | undefined;
|
|
418
|
-
const mockService = createMockService(
|
|
419
|
-
vi.fn().mockImplementation((_opts: Record<string, unknown>, signal?: AbortSignal) => {
|
|
420
|
-
capturedSignal = signal;
|
|
421
|
-
return Promise.resolve(makeMockResult());
|
|
422
|
-
}),
|
|
423
|
-
);
|
|
424
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
425
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
426
|
-
|
|
427
|
-
const opts = { ...makeBaseOpts(), timeoutMs: 5000 };
|
|
428
|
-
await sar.run(opts, new AbortController().signal);
|
|
429
|
-
|
|
430
|
-
// 有 timeoutMs 时 merged signal 不等于原始 signal
|
|
431
|
-
expect(capturedSignal).toBeDefined();
|
|
432
|
-
});
|
|
90
|
+
it("service 同步抛错(路由失败/预检命中/嵌套超限)原样传播——壳不吞错不包装", async () => {
|
|
91
|
+
const boom = new Error("engine_not_found: zcode not registered");
|
|
92
|
+
const executeWorkflowAgent = vi.fn().mockRejectedValue(boom);
|
|
93
|
+
const sar = new SubprocessAgentRunner({ subagentService: createMockService(executeWorkflowAgent) });
|
|
433
94
|
|
|
434
|
-
|
|
435
|
-
vi.useFakeTimers();
|
|
436
|
-
let capturedSignal: AbortSignal | undefined;
|
|
437
|
-
const mockService = createMockService(
|
|
438
|
-
vi.fn().mockImplementation((_opts: Record<string, unknown>, signal?: AbortSignal) => {
|
|
439
|
-
capturedSignal = signal;
|
|
440
|
-
return Promise.resolve(makeMockResult());
|
|
441
|
-
}),
|
|
442
|
-
);
|
|
443
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
444
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
445
|
-
|
|
446
|
-
const opts = { ...makeBaseOpts(), timeoutMs: 50 };
|
|
447
|
-
sar.run(opts, new AbortController().signal);
|
|
448
|
-
|
|
449
|
-
// 推进 51ms 后 merged signal 应 abort
|
|
450
|
-
vi.advanceTimersByTime(51);
|
|
451
|
-
|
|
452
|
-
expect(capturedSignal?.aborted).toBe(true);
|
|
453
|
-
vi.useRealTimers();
|
|
454
|
-
});
|
|
95
|
+
await expect(sar.run(makeOpts(), new AbortController().signal)).rejects.toThrow(boom);
|
|
455
96
|
});
|
|
456
97
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
toolName: "read",
|
|
482
|
-
args: { path: "/a.txt" },
|
|
483
|
-
});
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it("无 workflow onEvent → 引擎仍收到包装 onEvent(P2 journal 通道恒开)", async () => {
|
|
487
|
-
let engineOnEvent: ((e: Record<string, unknown>) => void) | undefined;
|
|
488
|
-
const mockService = createMockService(
|
|
489
|
-
vi.fn().mockImplementation(
|
|
490
|
-
(_opts: Record<string, unknown>, _signal?: AbortSignal, onEvent?: (e: Record<string, unknown>) => void) => {
|
|
491
|
-
engineOnEvent = onEvent;
|
|
492
|
-
return Promise.resolve(makeMockResult());
|
|
493
|
-
},
|
|
494
|
-
),
|
|
495
|
-
);
|
|
496
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
497
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
498
|
-
|
|
499
|
-
await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
500
|
-
|
|
501
|
-
// P2 接线(设计 D6 第②级):SAR 包装 onEvent 写 journal 后转发——原 onEvent
|
|
502
|
-
// 未传时也恒传包装版(全引擎免费获得 journal;P1「undefined 透传」细节已被
|
|
503
|
-
// 有意变更,本用例锁定新语义:引擎侧 onEvent 通道恒开)
|
|
504
|
-
expect(typeof engineOnEvent).toBe("function");
|
|
505
|
-
// 包装版不向外抛(journal append 是同步入队)
|
|
506
|
-
expect(() => engineOnEvent?.({ type: "turn_end" })).not.toThrow();
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
it("P2 journal 接线:run 期间事件落盘 journal-<taskId>.jsonl(中立格式,可重放)", async () => {
|
|
510
|
-
vi.useFakeTimers({ toFake: [] }); // 真实 timers:journal 写盘走真实 fs
|
|
511
|
-
const dataDir = mkdtempSync(join(tmpdir(), "sar-journal-test-"));
|
|
512
|
-
const prevEnv = process.env.XYZ_AGENT_DATA_DIR;
|
|
513
|
-
process.env.XYZ_AGENT_DATA_DIR = dataDir;
|
|
514
|
-
try {
|
|
515
|
-
const mockService = createMockService(
|
|
516
|
-
vi.fn().mockImplementation(
|
|
517
|
-
(_opts: Record<string, unknown>, _signal?: AbortSignal, onEvent?: (e: Record<string, unknown>) => void) => {
|
|
518
|
-
onEvent?.({ type: "tool_start", toolName: "bash", args: { cmd: "ls" } });
|
|
519
|
-
onEvent?.({ type: "turn_end" });
|
|
520
|
-
return Promise.resolve(makeMockResult());
|
|
521
|
-
},
|
|
522
|
-
),
|
|
523
|
-
);
|
|
524
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
525
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
526
|
-
|
|
527
|
-
const workflowOnEvent = vi.fn();
|
|
528
|
-
await sar.run(makeBaseOpts(), new AbortController().signal, workflowOnEvent);
|
|
529
|
-
|
|
530
|
-
// journal 落在 <dataDir>/inproc pi 引擎目录/shared/journal-<taskId>.jsonl
|
|
531
|
-
const journalFile = join(dataDir, "engines", "pi", "shared");
|
|
532
|
-
const files = readdirSync(journalFile).filter((f) => f.startsWith("journal-") && f.endsWith(".jsonl"));
|
|
533
|
-
expect(files.length).toBe(1);
|
|
534
|
-
const replayed = replayJournal(join(journalFile, files[0] ?? ""));
|
|
535
|
-
expect(replayed).toEqual([
|
|
536
|
-
{ type: "tool_start", toolName: "bash", args: { cmd: "ls" } },
|
|
537
|
-
{ type: "turn_end" },
|
|
538
|
-
]);
|
|
539
|
-
// workflow onEvent 照常透传(journal 包装不吞事件)
|
|
540
|
-
expect(workflowOnEvent).toHaveBeenCalledTimes(2);
|
|
541
|
-
} finally {
|
|
542
|
-
if (prevEnv === undefined) delete process.env.XYZ_AGENT_DATA_DIR;
|
|
543
|
-
else process.env.XYZ_AGENT_DATA_DIR = prevEnv;
|
|
544
|
-
rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
545
|
-
}
|
|
546
|
-
});
|
|
98
|
+
it("构造签名兼容:ctxModel 可选注入(装配点零改定锚点)+ updateCtxModel 刷新不炸", async () => {
|
|
99
|
+
const ctxModel: ModelInfo = {
|
|
100
|
+
id: "test/model-x",
|
|
101
|
+
name: "Model X",
|
|
102
|
+
provider: "test",
|
|
103
|
+
reasoning: false,
|
|
104
|
+
contextWindow: 128_000,
|
|
105
|
+
};
|
|
106
|
+
const executeWorkflowAgent = vi.fn().mockResolvedValue(makeResult());
|
|
107
|
+
// ctxModel 注入构造(session-lifecycle.ts:537 形态)
|
|
108
|
+
const sar = new SubprocessAgentRunner({ subagentService: createMockService(executeWorkflowAgent), ctxModel });
|
|
109
|
+
// model_select 刷新链(extension index.ts 调用形态):不炸且不影响转调
|
|
110
|
+
sar.updateCtxModel(undefined);
|
|
111
|
+
sar.updateCtxModel(ctxModel);
|
|
112
|
+
|
|
113
|
+
await sar.run(makeOpts(), new AbortController().signal);
|
|
114
|
+
// run() 不再消费 ctxModel:转调用参只有 opts/parentRunId/signal/onEvent/stream
|
|
115
|
+
expect(executeWorkflowAgent).toHaveBeenCalledWith(
|
|
116
|
+
makeOpts(),
|
|
117
|
+
SAR_UNATTACHED_PARENT_RUN_ID,
|
|
118
|
+
expect.any(AbortSignal),
|
|
119
|
+
undefined,
|
|
120
|
+
undefined,
|
|
121
|
+
);
|
|
547
122
|
});
|
|
548
123
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const mockService = createMockService(
|
|
556
|
-
vi.fn().mockResolvedValue(makeMockResult()),
|
|
557
|
-
);
|
|
558
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
559
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
560
|
-
|
|
561
|
-
const ctrl = new AbortController();
|
|
562
|
-
const opts = { ...makeBaseOpts(), timeoutMs: 5000 };
|
|
563
|
-
|
|
564
|
-
// 启动 run(不 await,让它在后台)
|
|
565
|
-
const runPromise = sar.run(opts, ctrl.signal);
|
|
566
|
-
|
|
567
|
-
// 外部 abort
|
|
568
|
-
ctrl.abort();
|
|
569
|
-
|
|
570
|
-
await runPromise;
|
|
571
|
-
|
|
572
|
-
// 推进时间——timer 应已清理,不会造成副作用
|
|
573
|
-
vi.advanceTimersByTime(6000);
|
|
574
|
-
// 无异常 = listener 已正确清理
|
|
575
|
-
vi.useRealTimers();
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
// ────────────────────────────────────────────────
|
|
580
|
-
// T3.19: SAR → engine.run 任务声明直传保真
|
|
581
|
-
// [W3 改写] 「AgentCallOpts → ExecuteOptions 映射点在 PiEngine 边界」随 inproc pi 引擎目录
|
|
582
|
-
// 删除迁入 pi-subagent-cli(引擎包 spawn-args 测试守护);core 侧存留契约 =
|
|
583
|
-
// SAR 对 AgentCallOpts 直传零映射(engine.run 的 task 原样性)。
|
|
584
|
-
// ────────────────────────────────────────────────
|
|
585
|
-
describe("T3.19 直传保真(D6:SAR 直传零映射,任务声明原样到达 engine.run)", () => {
|
|
586
|
-
it("prompt/agent/skillPath/schemaEnv 原样到达 engine.run", async () => {
|
|
587
|
-
const mockService = createMockService();
|
|
588
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
589
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
590
|
-
|
|
591
|
-
const opts: AgentCallOpts = {
|
|
592
|
-
...makeBaseOpts(),
|
|
593
|
-
prompt: "do the thing",
|
|
594
|
-
agent: "code-reviewer",
|
|
595
|
-
schemaEnv: '{"type":"object"}',
|
|
596
|
-
skillPath: "/skills/code-review.md",
|
|
597
|
-
};
|
|
598
|
-
await sar.run(opts, new AbortController().signal);
|
|
599
|
-
|
|
600
|
-
expect(engineRunSpy).toHaveBeenCalledTimes(1);
|
|
601
|
-
const [task] = engineRunSpy.mock.calls[0] as [AgentCallOpts, unknown];
|
|
602
|
-
expect(task.prompt).toBe("do the thing");
|
|
603
|
-
expect(task.agent).toBe("code-reviewer");
|
|
604
|
-
expect(task.skillPath).toBe("/skills/code-review.md");
|
|
605
|
-
expect(task.schemaEnv).toBe('{"type":"object"}');
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
it("schema 原样透传为原始对象", async () => {
|
|
609
|
-
const mockService = createMockService();
|
|
610
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
611
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
612
|
-
|
|
613
|
-
const schema = { type: "object", properties: { name: { type: "string" } } };
|
|
614
|
-
await sar.run({ ...makeBaseOpts(), schema }, new AbortController().signal);
|
|
615
|
-
|
|
616
|
-
const [task] = engineRunSpy.mock.calls[0] as [AgentCallOpts, unknown];
|
|
617
|
-
expect(task.schema).toEqual(schema);
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
// ────────────────────────────────────────────────
|
|
622
|
-
// T3.18: executeAndAwait throw → catch → error
|
|
623
|
-
// ────────────────────────────────────────────────
|
|
624
|
-
describe("T3.18 异常处理", () => {
|
|
625
|
-
it("executeAndAwait throw → 不 reject,返回 error", async () => {
|
|
626
|
-
const mockService = createMockService(
|
|
627
|
-
vi.fn().mockRejectedValue(new Error("nesting depth exceeded")),
|
|
628
|
-
);
|
|
629
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
630
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
631
|
-
|
|
632
|
-
const result = await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
633
|
-
expect(result.error).toContain("nesting depth exceeded");
|
|
634
|
-
expect(result.content).toBe("");
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
it("非 Error throw → error 字段含 message", async () => {
|
|
638
|
-
const mockService = createMockService(
|
|
639
|
-
vi.fn().mockRejectedValue("raw string error"),
|
|
640
|
-
);
|
|
641
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
642
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
643
|
-
|
|
644
|
-
const result = await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
645
|
-
expect(result.error).toBe("raw string error");
|
|
646
|
-
});
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
// ────────────────────────────────────────────────
|
|
650
|
-
// U1: stream 透传给 executeAndAwait
|
|
651
|
-
// ────────────────────────────────────────────────
|
|
652
|
-
describe("U1 stream 透传", () => {
|
|
653
|
-
it("SAR.run 传 stream → executeAndAwait 第 4 参收到同一 stream 对象", async () => {
|
|
654
|
-
let capturedStream: unknown;
|
|
655
|
-
const mockService = createMockService(
|
|
656
|
-
vi.fn().mockImplementation((_opts, _sig, _onEvt, stream) => {
|
|
657
|
-
capturedStream = stream;
|
|
658
|
-
return Promise.resolve(makeMockResult());
|
|
659
|
-
}),
|
|
660
|
-
);
|
|
661
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
662
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
663
|
-
|
|
664
|
-
const fakeStream = { onDelta: vi.fn(), dispose: vi.fn() };
|
|
665
|
-
await sar.run(makeBaseOpts(), new AbortController().signal, undefined, fakeStream as never);
|
|
666
|
-
|
|
667
|
-
expect(capturedStream).toBe(fakeStream);
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
it("SAR.run 不传 stream → executeAndAwait 第 4 参为 undefined", async () => {
|
|
671
|
-
let capturedStream: unknown = "sentinel";
|
|
672
|
-
const mockService = createMockService(
|
|
673
|
-
vi.fn().mockImplementation((_opts, _sig, _onEvt, stream) => {
|
|
674
|
-
capturedStream = stream;
|
|
675
|
-
return Promise.resolve(makeMockResult());
|
|
676
|
-
}),
|
|
677
|
-
);
|
|
678
|
-
const deps: SubprocessAgentRunnerDeps = { subagentService: mockService };
|
|
679
|
-
const sar = new SubprocessAgentRunner(deps);
|
|
680
|
-
|
|
681
|
-
await sar.run(makeBaseOpts(), new AbortController().signal);
|
|
682
|
-
|
|
683
|
-
expect(capturedStream).toBeUndefined();
|
|
684
|
-
});
|
|
124
|
+
it("re-export 契约:mergeRunSignals 经本模块路径可用(既有 import 面)", () => {
|
|
125
|
+
// no-progress-killall 测试经本模块 import mergeRunSignals——锁 re-export 行存活
|
|
126
|
+
//(行为本体由 engine/common/run-signals 权威实现及其测试锁定)。
|
|
127
|
+
const handle = mergeRunSignals(new AbortController().signal, 60_000);
|
|
128
|
+
expect(handle.signal.aborted).toBe(false);
|
|
129
|
+
handle.dispose();
|
|
685
130
|
});
|
|
686
131
|
});
|