@united-workforce/cli 0.6.1 → 0.8.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/README.md +120 -5
- package/dist/.build-fingerprint +1 -1
- package/dist/__tests__/agent-resolution-llm-free.test.js +9 -2
- package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -1
- package/dist/__tests__/broker-prompt.test.d.ts +10 -0
- package/dist/__tests__/broker-prompt.test.d.ts.map +1 -0
- package/dist/__tests__/broker-prompt.test.js +129 -0
- package/dist/__tests__/broker-prompt.test.js.map +1 -0
- package/dist/__tests__/broker-step-active-turns.test.d.ts +20 -0
- package/dist/__tests__/broker-step-active-turns.test.d.ts.map +1 -0
- package/dist/__tests__/broker-step-active-turns.test.js +428 -0
- package/dist/__tests__/broker-step-active-turns.test.js.map +1 -0
- package/dist/__tests__/broker-step-turn-chain-phase2.test.d.ts +13 -0
- package/dist/__tests__/broker-step-turn-chain-phase2.test.d.ts.map +1 -0
- package/dist/__tests__/broker-step-turn-chain-phase2.test.js +429 -0
- package/dist/__tests__/broker-step-turn-chain-phase2.test.js.map +1 -0
- package/dist/__tests__/config.test.js +33 -37
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/e2e-broker-step-suspend.test.d.ts +18 -0
- package/dist/__tests__/e2e-broker-step-suspend.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-broker-step-suspend.test.js +313 -0
- package/dist/__tests__/e2e-broker-step-suspend.test.js.map +1 -0
- package/dist/__tests__/e2e-broker-step.test.d.ts +13 -0
- package/dist/__tests__/e2e-broker-step.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-broker-step.test.js +278 -0
- package/dist/__tests__/e2e-broker-step.test.js.map +1 -0
- package/dist/__tests__/e2e-mock-agent.test.js +1 -1
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
- package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.d.ts +28 -0
- package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.js +322 -0
- package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.js.map +1 -0
- package/dist/__tests__/log-tag-validity.test.d.ts +2 -0
- package/dist/__tests__/log-tag-validity.test.d.ts.map +1 -0
- package/dist/__tests__/log-tag-validity.test.js +110 -0
- package/dist/__tests__/log-tag-validity.test.js.map +1 -0
- package/dist/__tests__/setup-agent-discovery.test.js +35 -23
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
- package/dist/__tests__/setup-no-llm.test.js +5 -2
- package/dist/__tests__/setup-no-llm.test.js.map +1 -1
- package/dist/__tests__/step-ask.test.js +9 -6
- package/dist/__tests__/step-ask.test.js.map +1 -1
- package/dist/__tests__/step-show-json.test.js +5 -5
- package/dist/__tests__/step-show-json.test.js.map +1 -1
- package/dist/__tests__/step-show-text.test.d.ts +2 -0
- package/dist/__tests__/step-show-text.test.d.ts.map +1 -0
- package/dist/__tests__/step-show-text.test.js +192 -0
- package/dist/__tests__/step-show-text.test.js.map +1 -0
- package/dist/__tests__/step-turns-cli-subprocess.test.d.ts +21 -0
- package/dist/__tests__/step-turns-cli-subprocess.test.d.ts.map +1 -0
- package/dist/__tests__/step-turns-cli-subprocess.test.js +356 -0
- package/dist/__tests__/step-turns-cli-subprocess.test.js.map +1 -0
- package/dist/__tests__/step-turns-panorama-phase3.test.d.ts +21 -0
- package/dist/__tests__/step-turns-panorama-phase3.test.d.ts.map +1 -0
- package/dist/__tests__/step-turns-panorama-phase3.test.js +476 -0
- package/dist/__tests__/step-turns-panorama-phase3.test.js.map +1 -0
- package/dist/__tests__/step-turns.test.d.ts +24 -0
- package/dist/__tests__/step-turns.test.d.ts.map +1 -0
- package/dist/__tests__/step-turns.test.js +646 -0
- package/dist/__tests__/step-turns.test.js.map +1 -0
- package/dist/__tests__/store-turn-chain.test.d.ts +2 -0
- package/dist/__tests__/store-turn-chain.test.d.ts.map +1 -0
- package/dist/__tests__/store-turn-chain.test.js +341 -0
- package/dist/__tests__/store-turn-chain.test.js.map +1 -0
- package/dist/__tests__/thread-agent-failure-suspended.test.js +3 -3
- package/dist/__tests__/thread-agent-failure-suspended.test.js.map +1 -1
- package/dist/__tests__/thread-list-limit-offset.test.d.ts +24 -0
- package/dist/__tests__/thread-list-limit-offset.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-limit-offset.test.js +254 -0
- package/dist/__tests__/thread-list-limit-offset.test.js.map +1 -0
- package/dist/__tests__/thread-list-template-ms-date.test.js +7 -2
- package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -1
- package/dist/__tests__/thread-poke.test.js +6 -6
- package/dist/__tests__/thread-poke.test.js.map +1 -1
- package/dist/__tests__/thread-resume.test.js +2 -2
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-suspend-step.test.js +1 -1
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread.test.js +28 -14
- package/dist/__tests__/thread.test.js.map +1 -1
- package/dist/cli.js +910 -344
- package/dist/cli.js.map +1 -1
- package/dist/commands/broker-step.d.ts +117 -0
- package/dist/commands/broker-step.d.ts.map +1 -0
- package/dist/commands/broker-step.js +654 -0
- package/dist/commands/broker-step.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +2 -23
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +43 -51
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/setup.d.ts +6 -4
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +24 -27
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/step.d.ts +54 -6
- package/dist/commands/step.d.ts.map +1 -1
- package/dist/commands/step.js +484 -134
- package/dist/commands/step.js.map +1 -1
- package/dist/commands/thread.d.ts +4 -0
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +77 -151
- package/dist/commands/thread.js.map +1 -1
- package/dist/output-mappers.d.ts +8 -0
- package/dist/output-mappers.d.ts.map +1 -1
- package/dist/output-mappers.js +72 -18
- package/dist/output-mappers.js.map +1 -1
- package/dist/schemas.d.ts +3 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +17 -3
- package/dist/schemas.js.map +1 -1
- package/dist/store.d.ts +147 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +254 -1
- package/dist/store.js.map +1 -1
- package/dist/text-renderers.d.ts.map +1 -1
- package/dist/text-renderers.js +27 -2
- package/dist/text-renderers.js.map +1 -1
- package/package.json +7 -5
- package/src/__tests__/agent-resolution-llm-free.test.ts +14 -2
- package/src/__tests__/broker-prompt.test.ts +142 -0
- package/src/__tests__/broker-step-active-turns.test.ts +509 -0
- package/src/__tests__/broker-step-turn-chain-phase2.test.ts +525 -0
- package/src/__tests__/config.test.ts +35 -39
- package/src/__tests__/e2e-broker-step-suspend.test.ts +351 -0
- package/src/__tests__/e2e-broker-step.test.ts +320 -0
- package/src/__tests__/e2e-mock-agent.test.ts +1 -1
- package/src/__tests__/e2e-thread-resume-timeout-suspend.test.ts +360 -0
- package/src/__tests__/log-tag-validity.test.ts +124 -0
- package/src/__tests__/setup-agent-discovery.test.ts +35 -23
- package/src/__tests__/setup-no-llm.test.ts +5 -2
- package/src/__tests__/step-ask.test.ts +9 -6
- package/src/__tests__/step-show-json.test.ts +5 -5
- package/src/__tests__/step-show-text.test.ts +236 -0
- package/src/__tests__/step-turns-cli-subprocess.test.ts +411 -0
- package/src/__tests__/step-turns-panorama-phase3.test.ts +579 -0
- package/src/__tests__/step-turns.test.ts +734 -0
- package/src/__tests__/store-turn-chain.test.ts +386 -0
- package/src/__tests__/thread-agent-failure-suspended.test.ts +3 -3
- package/src/__tests__/thread-list-limit-offset.test.ts +305 -0
- package/src/__tests__/thread-list-template-ms-date.test.ts +7 -2
- package/src/__tests__/thread-poke.test.ts +6 -6
- package/src/__tests__/thread-resume.test.ts +2 -2
- package/src/__tests__/thread-suspend-step.test.ts +1 -1
- package/src/__tests__/thread.test.ts +29 -15
- package/src/cli.ts +1056 -483
- package/src/commands/broker-step.ts +913 -0
- package/src/commands/config.ts +2 -24
- package/src/commands/prompt.ts +43 -51
- package/src/commands/setup.ts +25 -29
- package/src/commands/step.ts +645 -176
- package/src/commands/thread.ts +87 -192
- package/src/output-mappers.ts +99 -21
- package/src/schemas.ts +32 -2
- package/src/store.ts +297 -2
- package/src/text-renderers.ts +35 -2
- package/dist/__tests__/adapter-json-roundtrip.test.d.ts +0 -2
- package/dist/__tests__/adapter-json-roundtrip.test.d.ts.map +0 -1
- package/dist/__tests__/adapter-json-roundtrip.test.js +0 -160
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +0 -1
- package/dist/__tests__/spawn-agent-json.test.d.ts +0 -2
- package/dist/__tests__/spawn-agent-json.test.d.ts.map +0 -1
- package/dist/__tests__/spawn-agent-json.test.js +0 -79
- package/dist/__tests__/spawn-agent-json.test.js.map +0 -1
- package/src/__tests__/adapter-json-roundtrip.test.ts +0 -193
- package/src/__tests__/spawn-agent-json.test.ts +0 -100
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec 4 (issue #435, Phase 2) — verification contract for the RFC #95 loop
|
|
3
|
+
* `timeout → suspend (checkpoint) → resume`.
|
|
4
|
+
*
|
|
5
|
+
* This is verification-only: NO resume code changed in Phase 2. The test proves
|
|
6
|
+
* the *existing* `uwf thread resume` path already satisfies the timeout-suspend
|
|
7
|
+
* resume contract by wiring the spec-3 producer to the resume consumer:
|
|
8
|
+
*
|
|
9
|
+
* 1. Drive a real sumeru send-timeout through `executeBrokerStep` (the SSE
|
|
10
|
+
* stream ends in `suspend`, exactly as Spec 3 verifies) so the thread's
|
|
11
|
+
* head step is a genuine `$status: "$SUSPEND"` node and the `(threadId,
|
|
12
|
+
* role)` broker session is mapped to the sumeru session.
|
|
13
|
+
* 2. Seed the thread to `suspended` (mirroring what `finalizeAgentStep` does
|
|
14
|
+
* after a suspended broker step) and assert `cmdThreadShow` reports
|
|
15
|
+
* `suspended` with the timeout reason — a valid resume precondition.
|
|
16
|
+
* 3. Call `cmdThreadResume`. Assert it is accepted, issues a FRESH
|
|
17
|
+
* `broker.send()` for the suspended role on the SAME mapped session (so the
|
|
18
|
+
* sumeru adapter resumes by `nativeId` rather than starting over), delivers
|
|
19
|
+
* the `-p` supplement as the continuation prompt, and — when that resumed
|
|
20
|
+
* send now completes (`kind:"completed"`) — advances the thread out of
|
|
21
|
+
* `suspended` (here straight to `end`).
|
|
22
|
+
*
|
|
23
|
+
* The second send is a `done` stream, so the gate opens and the thread proceeds;
|
|
24
|
+
* if it had timed out again it would simply re-arm `suspended` (Spec 3 path),
|
|
25
|
+
* never an error.
|
|
26
|
+
*/
|
|
27
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
28
|
+
import { tmpdir } from "node:os";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import { putSchema } from "@ocas/core";
|
|
31
|
+
import { createProcessLogger } from "@united-workforce/util";
|
|
32
|
+
import { getConfigPath } from "@united-workforce/util-agent";
|
|
33
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
34
|
+
import { executeBrokerStep, openBrokerSessionStore } from "../commands/broker-step.js";
|
|
35
|
+
import { cmdThreadResume, cmdThreadShow } from "../commands/thread.js";
|
|
36
|
+
import { createUwfStore } from "../store.js";
|
|
37
|
+
import { seedThreads } from "./thread-test-helpers.js";
|
|
38
|
+
function sseFrame(id, event, data) {
|
|
39
|
+
return `id: ${id}\nevent: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
40
|
+
}
|
|
41
|
+
function buildSseResponse(frames) {
|
|
42
|
+
const encoder = new TextEncoder();
|
|
43
|
+
const stream = new ReadableStream({
|
|
44
|
+
start(controller) {
|
|
45
|
+
for (const frame of frames)
|
|
46
|
+
controller.enqueue(encoder.encode(frame));
|
|
47
|
+
controller.close();
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
return new Response(stream, {
|
|
51
|
+
status: 200,
|
|
52
|
+
headers: { "Content-Type": "text/event-stream; charset=utf-8" },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function buildJsonResponse(status, body) {
|
|
56
|
+
return new Response(JSON.stringify(body), {
|
|
57
|
+
status,
|
|
58
|
+
headers: { "Content-Type": "application/json" },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const PLANNER_OUTPUT_SCHEMA = {
|
|
62
|
+
title: "planner-output",
|
|
63
|
+
type: "object",
|
|
64
|
+
required: ["$status", "plan"],
|
|
65
|
+
properties: {
|
|
66
|
+
$status: { type: "string", enum: ["done", "failed"] },
|
|
67
|
+
plan: { type: "string" },
|
|
68
|
+
},
|
|
69
|
+
additionalProperties: false,
|
|
70
|
+
};
|
|
71
|
+
const PLANNER_RAW_OUTPUT = `---
|
|
72
|
+
$status: done
|
|
73
|
+
plan: ship it
|
|
74
|
+
---
|
|
75
|
+
the plan body`;
|
|
76
|
+
const HOST = "http://127.0.0.1:7900";
|
|
77
|
+
const GATEWAY = "planner-gw";
|
|
78
|
+
const ALIAS = "planner-agent";
|
|
79
|
+
const SESSION_ID = "ses_resume_e2e";
|
|
80
|
+
const THREAD_ID = "06FCBROKERRESUMESTEP0001";
|
|
81
|
+
const ROLE = "planner";
|
|
82
|
+
const NATIVE_ID = "ses_native_abc";
|
|
83
|
+
const ELAPSED_MS = 1800000;
|
|
84
|
+
const WORKFLOW_NAME = "broker-resume-e2e";
|
|
85
|
+
const SUPPLEMENT = "继续上次未完成的任务";
|
|
86
|
+
function buildConfig() {
|
|
87
|
+
return {
|
|
88
|
+
agents: { [ALIAS]: { host: HOST, gateway: GATEWAY } },
|
|
89
|
+
defaultAgent: ALIAS,
|
|
90
|
+
agentOverrides: null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Write the on-disk `config.yaml` that `cmdThreadResume` reloads via
|
|
95
|
+
* `loadWorkflowConfig`. Must use the Phase-3 `{host, gateway}` shape (the
|
|
96
|
+
* normalizer rejects the legacy `{command}` form).
|
|
97
|
+
*/
|
|
98
|
+
async function writeConfig(storageRoot) {
|
|
99
|
+
const yaml = `defaultAgent: ${ALIAS}\nagentOverrides: null\nagents:\n ${ALIAS}:\n host: ${HOST}\n gateway: ${GATEWAY}\n`;
|
|
100
|
+
await writeFile(getConfigPath(storageRoot), yaml, "utf8");
|
|
101
|
+
}
|
|
102
|
+
async function buildWorkflow(uwf) {
|
|
103
|
+
const frontmatterHash = (await putSchema(uwf.store, PLANNER_OUTPUT_SCHEMA));
|
|
104
|
+
const workflow = {
|
|
105
|
+
version: 1,
|
|
106
|
+
name: WORKFLOW_NAME,
|
|
107
|
+
description: "broker step resume end-to-end",
|
|
108
|
+
roles: {
|
|
109
|
+
planner: {
|
|
110
|
+
description: "plans things",
|
|
111
|
+
goal: "produce a plan",
|
|
112
|
+
capabilities: [],
|
|
113
|
+
procedure: "think hard",
|
|
114
|
+
output: "frontmatter+body",
|
|
115
|
+
frontmatter: frontmatterHash,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
graph: {
|
|
119
|
+
planner: {
|
|
120
|
+
// Non-empty $END prompt: the resumed `done` stream routes through the
|
|
121
|
+
// post-step moderator, which rejects an empty edge template.
|
|
122
|
+
done: { role: "$END", prompt: "done", location: null },
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
const startHash = (await uwf.store.cas.put(uwf.schemas.startNode, {
|
|
127
|
+
workflow: await uwf.store.cas.put(uwf.schemas.workflow, workflow),
|
|
128
|
+
prompt: "p",
|
|
129
|
+
cwd: "/tmp/work",
|
|
130
|
+
}));
|
|
131
|
+
return { workflow, startHash };
|
|
132
|
+
}
|
|
133
|
+
function suspendStream() {
|
|
134
|
+
return buildSseResponse([
|
|
135
|
+
sseFrame(1, "turn", {
|
|
136
|
+
type: "@sumeru/turn",
|
|
137
|
+
value: { index: 0, role: "user", content: "edge prompt", timestamp: "", toolCalls: null },
|
|
138
|
+
}),
|
|
139
|
+
sseFrame(2, "turn", {
|
|
140
|
+
type: "@sumeru/turn",
|
|
141
|
+
value: { index: 1, role: "assistant", content: "draft1", timestamp: "", toolCalls: null },
|
|
142
|
+
}),
|
|
143
|
+
sseFrame(3, "suspend", {
|
|
144
|
+
type: "@sumeru/suspend",
|
|
145
|
+
value: { reason: "timeout", nativeId: NATIVE_ID, elapsedMs: ELAPSED_MS },
|
|
146
|
+
}),
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
function completedStream() {
|
|
150
|
+
return buildSseResponse([
|
|
151
|
+
sseFrame(1, "turn", {
|
|
152
|
+
type: "@sumeru/turn",
|
|
153
|
+
value: {
|
|
154
|
+
index: 1,
|
|
155
|
+
role: "assistant",
|
|
156
|
+
content: PLANNER_RAW_OUTPUT,
|
|
157
|
+
timestamp: "",
|
|
158
|
+
toolCalls: null,
|
|
159
|
+
},
|
|
160
|
+
}),
|
|
161
|
+
sseFrame(2, "done", {
|
|
162
|
+
type: "@sumeru/summary",
|
|
163
|
+
value: { turnCount: 2, tokens: { in: 9, out: 4 }, durationMs: 42 },
|
|
164
|
+
}),
|
|
165
|
+
]);
|
|
166
|
+
}
|
|
167
|
+
function resolveFetchUrl(input) {
|
|
168
|
+
if (typeof input === "string")
|
|
169
|
+
return input;
|
|
170
|
+
if (input instanceof URL)
|
|
171
|
+
return input.href;
|
|
172
|
+
return input.url;
|
|
173
|
+
}
|
|
174
|
+
function makePlog(tmpDir) {
|
|
175
|
+
return createProcessLogger({
|
|
176
|
+
storageRoot: tmpDir,
|
|
177
|
+
context: { thread: THREAD_ID, workflow: WORKFLOW_NAME },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
describe("uwf thread resume — timeout-suspended thread resumes via fresh send (issue #435)", () => {
|
|
181
|
+
let tmpDir;
|
|
182
|
+
let savedOcasHome;
|
|
183
|
+
let calls;
|
|
184
|
+
// First send (the step that suspends) → suspend stream; every send after the
|
|
185
|
+
// first (the resume) → completed stream. A counter, not a swap, so the resume
|
|
186
|
+
// genuinely re-enters the same stub.
|
|
187
|
+
let messageCallCount;
|
|
188
|
+
beforeEach(async () => {
|
|
189
|
+
savedOcasHome = process.env.OCAS_HOME;
|
|
190
|
+
tmpDir = await mkdtemp(join(tmpdir(), "broker-resume-e2e-"));
|
|
191
|
+
process.env.OCAS_HOME = join(tmpDir, "cas");
|
|
192
|
+
calls = [];
|
|
193
|
+
messageCallCount = 0;
|
|
194
|
+
vi.stubGlobal("fetch", async (input, init) => {
|
|
195
|
+
const url = resolveFetchUrl(input);
|
|
196
|
+
const method = init?.method ?? "GET";
|
|
197
|
+
const body = typeof init?.body === "string" ? init.body : "";
|
|
198
|
+
calls.push({ url, method, body });
|
|
199
|
+
if (url.endsWith(`/gateways/${GATEWAY}/sessions`)) {
|
|
200
|
+
return buildJsonResponse(201, {
|
|
201
|
+
type: "@sumeru/session",
|
|
202
|
+
value: { id: SESSION_ID, gateway: GATEWAY },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (url.endsWith(`/sessions/${SESSION_ID}/messages`)) {
|
|
206
|
+
messageCallCount += 1;
|
|
207
|
+
return messageCallCount === 1 ? suspendStream() : completedStream();
|
|
208
|
+
}
|
|
209
|
+
return buildJsonResponse(500, { error: "unexpected url", url });
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
afterEach(async () => {
|
|
213
|
+
vi.unstubAllGlobals();
|
|
214
|
+
if (savedOcasHome === undefined)
|
|
215
|
+
delete process.env.OCAS_HOME;
|
|
216
|
+
else
|
|
217
|
+
process.env.OCAS_HOME = savedOcasHome;
|
|
218
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
219
|
+
});
|
|
220
|
+
/**
|
|
221
|
+
* Drive a real send-timeout through `executeBrokerStep`, then seed the thread
|
|
222
|
+
* to `suspended` at the produced `$SUSPEND` step (mirroring `finalizeAgentStep`
|
|
223
|
+
* after a suspended broker step). Returns the suspend step hash and reason.
|
|
224
|
+
*/
|
|
225
|
+
async function suspendThread(uwf, workflow, startHash) {
|
|
226
|
+
const result = await executeBrokerStep({
|
|
227
|
+
storageRoot: tmpDir,
|
|
228
|
+
uwf,
|
|
229
|
+
config: buildConfig(),
|
|
230
|
+
workflow,
|
|
231
|
+
threadId: THREAD_ID,
|
|
232
|
+
role: ROLE,
|
|
233
|
+
edgePrompt: "make a plan",
|
|
234
|
+
effectiveCwd: "/tmp/work",
|
|
235
|
+
startHash,
|
|
236
|
+
prevHash: null,
|
|
237
|
+
agentOverride: null,
|
|
238
|
+
previousAttempts: null,
|
|
239
|
+
plog: makePlog(tmpDir),
|
|
240
|
+
});
|
|
241
|
+
const reason = result.frontmatter.reason;
|
|
242
|
+
await seedThreads(tmpDir, {
|
|
243
|
+
[THREAD_ID]: {
|
|
244
|
+
head: result.stepHash,
|
|
245
|
+
status: "suspended",
|
|
246
|
+
suspendedRole: ROLE,
|
|
247
|
+
suspendMessage: reason,
|
|
248
|
+
completedAt: null,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
return { suspendHash: result.stepHash, reason };
|
|
252
|
+
}
|
|
253
|
+
test("a timeout-suspended thread is shown as suspended, then resume advances it past the gate", async () => {
|
|
254
|
+
const uwf = await createUwfStore(tmpDir);
|
|
255
|
+
const { workflow, startHash } = await buildWorkflow(uwf);
|
|
256
|
+
await writeConfig(tmpDir);
|
|
257
|
+
const { suspendHash, reason } = await suspendThread(uwf, workflow, startHash);
|
|
258
|
+
// Precondition: the thread sits in `suspended` carrying the timeout reason.
|
|
259
|
+
const show = await cmdThreadShow(tmpDir, THREAD_ID);
|
|
260
|
+
expect(show.status).toBe("suspended");
|
|
261
|
+
expect(show.suspendedRole).toBe(ROLE);
|
|
262
|
+
expect(show.suspendMessage).toBe(reason);
|
|
263
|
+
expect(show.suspendMessage).toContain(NATIVE_ID);
|
|
264
|
+
// Resume is accepted and the resumed send completes, so the thread leaves
|
|
265
|
+
// `suspended` and advances (here straight to `end` via the `done` edge).
|
|
266
|
+
const resumeOut = await cmdThreadResume(tmpDir, THREAD_ID, SUPPLEMENT, null);
|
|
267
|
+
expect(resumeOut.status).toBe("end");
|
|
268
|
+
expect(resumeOut.done).toBe(true);
|
|
269
|
+
expect(resumeOut.error).toBeNull();
|
|
270
|
+
expect(resumeOut.head).not.toBe(suspendHash);
|
|
271
|
+
// And `thread show` agrees the gate is gone.
|
|
272
|
+
const showAfter = await cmdThreadShow(tmpDir, THREAD_ID);
|
|
273
|
+
expect(showAfter.status).toBe("end");
|
|
274
|
+
expect(showAfter.suspendedRole).toBeNull();
|
|
275
|
+
expect(showAfter.suspendMessage).toBeNull();
|
|
276
|
+
});
|
|
277
|
+
test("resume issues a FRESH send reusing the SAME mapped session (no new createSession)", async () => {
|
|
278
|
+
const uwf = await createUwfStore(tmpDir);
|
|
279
|
+
const { workflow, startHash } = await buildWorkflow(uwf);
|
|
280
|
+
await writeConfig(tmpDir);
|
|
281
|
+
await suspendThread(uwf, workflow, startHash);
|
|
282
|
+
await cmdThreadResume(tmpDir, THREAD_ID, SUPPLEMENT, null);
|
|
283
|
+
// Exactly ONE createSession (during the suspend) — resume reuses the cached
|
|
284
|
+
// (threadId, role) → sessionId mapping rather than spawning a new session.
|
|
285
|
+
const createCalls = calls.filter((c) => c.url.endsWith(`/gateways/${GATEWAY}/sessions`));
|
|
286
|
+
expect(createCalls).toHaveLength(1);
|
|
287
|
+
// TWO sends, both addressed to the SAME session id: the suspended send and
|
|
288
|
+
// the resume continuation. The sumeru adapter resumes by nativeId off this
|
|
289
|
+
// shared session.
|
|
290
|
+
const messageCalls = calls.filter((c) => c.url.endsWith("/messages"));
|
|
291
|
+
expect(messageCalls).toHaveLength(2);
|
|
292
|
+
for (const call of messageCalls) {
|
|
293
|
+
expect(call.url).toContain(`/sessions/${SESSION_ID}/messages`);
|
|
294
|
+
}
|
|
295
|
+
// The broker session row still points at the same session for a future resume.
|
|
296
|
+
const sessionStore = openBrokerSessionStore(tmpDir);
|
|
297
|
+
try {
|
|
298
|
+
const row = sessionStore.getSession(THREAD_ID, ROLE);
|
|
299
|
+
expect(row?.sessionId).toBe(SESSION_ID);
|
|
300
|
+
expect(row?.host).toBe(HOST);
|
|
301
|
+
expect(row?.gateway).toBe(GATEWAY);
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
sessionStore.close();
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
test("the -p supplement is delivered as the continuation prompt on the resume send", async () => {
|
|
308
|
+
const uwf = await createUwfStore(tmpDir);
|
|
309
|
+
const { workflow, startHash } = await buildWorkflow(uwf);
|
|
310
|
+
await writeConfig(tmpDir);
|
|
311
|
+
await suspendThread(uwf, workflow, startHash);
|
|
312
|
+
await cmdThreadResume(tmpDir, THREAD_ID, SUPPLEMENT, null);
|
|
313
|
+
const messageCalls = calls.filter((c) => c.url.endsWith("/messages"));
|
|
314
|
+
const resumeSend = messageCalls[1];
|
|
315
|
+
expect(resumeSend).toBeDefined();
|
|
316
|
+
// The resume prompt = suspend reason + the operator supplement; both ride
|
|
317
|
+
// the assembled prompt body of the continuation send.
|
|
318
|
+
expect(resumeSend?.body).toContain(SUPPLEMENT);
|
|
319
|
+
expect(resumeSend?.body).toContain(NATIVE_ID);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
//# sourceMappingURL=e2e-thread-resume-timeout-suspend.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e-thread-resume-timeout-suspend.test.js","sourceRoot":"","sources":["../../src/__tests__/e2e-thread-resume-timeout-suspend.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAiB,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIvD,SAAS,QAAQ,CAAC,EAAU,EAAE,KAAa,EAAE,IAAa;IACxD,OAAO,OAAO,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;AACzE,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAgB;IACxC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,cAAc,CAAa;QAC5C,KAAK,CAAC,UAAU;YACd,KAAK,MAAM,KAAK,IAAI,MAAM;gBAAE,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACtE,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KACF,CAAC,CAAC;IACH,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE;QAC1B,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,cAAc,EAAE,kCAAkC,EAAE;KAChE,CAAC,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAc,EAAE,IAAa;IACtD,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAChD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,qBAAqB,GAAG;IAC5B,KAAK,EAAE,gBAAgB;IACvB,IAAI,EAAE,QAAiB;IACvB,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE;QACV,OAAO,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;QAC9D,IAAI,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE;KAClC;IACD,oBAAoB,EAAE,KAAK;CAC5B,CAAC;AAEF,MAAM,kBAAkB,GAAG;;;;cAIb,CAAC;AAEf,MAAM,IAAI,GAAG,uBAAuB,CAAC;AACrC,MAAM,OAAO,GAAG,YAAY,CAAC;AAC7B,MAAM,KAAK,GAAG,eAAe,CAAC;AAC9B,MAAM,UAAU,GAAG,gBAAgB,CAAC;AACpC,MAAM,SAAS,GAAG,0BAAsC,CAAC;AACzD,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,SAAS,GAAG,gBAAgB,CAAC;AACnC,MAAM,UAAU,GAAG,OAAO,CAAC;AAC3B,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAC1C,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,SAAS,WAAW;IAClB,OAAO;QACL,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QACrD,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,IAAI;KACrB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,WAAW,CAAC,WAAmB;IAC5C,MAAM,IAAI,GAAG,iBAAiB,KAAK,sCAAsC,KAAK,gBAAgB,IAAI,kBAAkB,OAAO,IAAI,CAAC;IAChI,MAAM,SAAS,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAa;IAIxC,MAAM,eAAe,GAAG,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAW,CAAC;IACtF,MAAM,QAAQ,GAAoB;QAChC,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,+BAA+B;QAC5C,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,WAAW,EAAE,cAAc;gBAC3B,IAAI,EAAE,gBAAgB;gBACtB,YAAY,EAAE,EAAE;gBAChB,SAAS,EAAE,YAAY;gBACvB,MAAM,EAAE,kBAAkB;gBAC1B,WAAW,EAAE,eAAe;aAC7B;SACF;QACD,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,sEAAsE;gBACtE,6DAA6D;gBAC7D,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;aACvD;SACF;KACF,CAAC;IACF,MAAM,SAAS,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE;QAChE,QAAQ,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC;QACjE,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,WAAW;KACjB,CAAC,CAAW,CAAC;IACd,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,gBAAgB,CAAC;QACtB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE;YAClB,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;SAC1F,CAAC;QACF,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE;YAClB,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;SAC1F,CAAC;QACF,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE;YACrB,IAAI,EAAE,iBAAiB;YACvB,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE;SACzE,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,gBAAgB,CAAC;QACtB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE;YAClB,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE;gBACL,KAAK,EAAE,CAAC;gBACR,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,kBAAkB;gBAC3B,SAAS,EAAE,EAAE;gBACb,SAAS,EAAE,IAAI;aAChB;SACF,CAAC;QACF,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE;YAClB,IAAI,EAAE,iBAAiB;YACvB,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;SACnE,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CAAC,KAA6B;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,YAAY,GAAG;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IAC5C,OAAO,KAAK,CAAC,GAAG,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc;IAC9B,OAAO,mBAAmB,CAAC;QACzB,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE;KACxD,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,kFAAkF,EAAE,GAAG,EAAE;IAChG,IAAI,MAAc,CAAC;IACnB,IAAI,aAAiC,CAAC;IACtC,IAAI,KAAkB,CAAC;IACvB,6EAA6E;IAC7E,8EAA8E;IAC9E,qCAAqC;IACrC,IAAI,gBAAwB,CAAC;IAE7B,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QACtC,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC5C,KAAK,GAAG,EAAE,CAAC;QACX,gBAAgB,GAAG,CAAC,CAAC;QACrB,EAAE,CAAC,UAAU,CACX,OAAO,EACP,KAAK,EAAE,KAA6B,EAAE,IAA6B,EAAqB,EAAE;YACxF,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;YACrC,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAClC,IAAI,GAAG,CAAC,QAAQ,CAAC,aAAa,OAAO,WAAW,CAAC,EAAE,CAAC;gBAClD,OAAO,iBAAiB,CAAC,GAAG,EAAE;oBAC5B,IAAI,EAAE,iBAAiB;oBACvB,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE;iBAC5C,CAAC,CAAC;YACL,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,CAAC,aAAa,UAAU,WAAW,CAAC,EAAE,CAAC;gBACrD,gBAAgB,IAAI,CAAC,CAAC;gBACtB,OAAO,gBAAgB,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;YACtE,CAAC;YACD,OAAO,iBAAiB,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC;QAClE,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;;YACzD,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,aAAa,CAAC;QAC3C,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH;;;;OAIG;IACH,KAAK,UAAU,aAAa,CAC1B,GAAa,EACb,QAAyB,EACzB,SAAiB;QAEjB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;YACrC,WAAW,EAAE,MAAM;YACnB,GAAG;YACH,MAAM,EAAE,WAAW,EAAE;YACrB,QAAQ;YACR,QAAQ,EAAE,SAAS;YACnB,IAAI,EAAE,IAAI;YACV,UAAU,EAAE,aAAa;YACzB,YAAY,EAAE,WAAW;YACzB,SAAS;YACT,QAAQ,EAAE,IAAI;YACd,aAAa,EAAE,IAAI;YACnB,gBAAgB,EAAE,IAAI;YACtB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC;SACvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAI,MAAM,CAAC,WAAuC,CAAC,MAAgB,CAAC;QAChF,MAAM,WAAW,CAAC,MAAM,EAAE;YACxB,CAAC,SAAS,CAAC,EAAE;gBACX,IAAI,EAAE,MAAM,CAAC,QAAQ;gBACrB,MAAM,EAAE,WAAW;gBACnB,aAAa,EAAE,IAAI;gBACnB,cAAc,EAAE,MAAM;gBACtB,WAAW,EAAE,IAAI;aAClB;SACF,CAAC,CAAC;QACH,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,yFAAyF,EAAE,KAAK,IAAI,EAAE;QACzG,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QAE1B,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAE9E,4EAA4E;QAC5E,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,cAAwB,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAE3D,0EAA0E;QAC1E,yEAAyE;QACzE,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAC7E,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE7C,6CAA6C;QAC7C,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACzD,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC3C,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;QACnG,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QAE1B,MAAM,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC9C,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAE3D,4EAA4E;QAC5E,2EAA2E;QAC3E,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,OAAO,WAAW,CAAC,CAAC,CAAC;QACzF,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAEpC,2EAA2E;QAC3E,2EAA2E;QAC3E,kBAAkB;QAClB,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAChC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,aAAa,UAAU,WAAW,CAAC,CAAC;QACjE,CAAC;QAED,+EAA+E;QAC/E,MAAM,YAAY,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC9F,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QAE1B,MAAM,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC9C,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAE3D,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACtE,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACjC,0EAA0E;QAC1E,sDAAsD;QACtD,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC/C,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-tag-validity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/log-tag-validity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { describe, expect, test } from "vitest";
|
|
4
|
+
/**
|
|
5
|
+
* Static regression guard for log tags (#426).
|
|
6
|
+
*
|
|
7
|
+
* Every `log()` call site uses a hand-written 8-char Crockford Base32 tag.
|
|
8
|
+
* Crockford Base32 excludes I, L, O, U to avoid visual ambiguity, and
|
|
9
|
+
* `assertValidLogTag()` (util/process-logger/log-tag.ts) throws at runtime
|
|
10
|
+
* when a tag contains an illegal character.
|
|
11
|
+
*
|
|
12
|
+
* The bug: `PL_FRONTMATTER_FAIL = "F4FA1L7Z"` (a leet spelling of
|
|
13
|
+
* "FRONTMATTER FAIL") smuggled an `L` into the tag. It only fires on the
|
|
14
|
+
* frontmatter-extraction-failure path, so it stayed dormant until a planner
|
|
15
|
+
* step failed extraction — then the failure logger itself crashed the process,
|
|
16
|
+
* masking the real error.
|
|
17
|
+
*
|
|
18
|
+
* This test scans the source of the cli + broker packages and asserts that
|
|
19
|
+
* EVERY literal tag — whether written inline as `log("XXXXXXXX", ...)` or as a
|
|
20
|
+
* `const PL_* = "XXXXXXXX"` constant — is a valid Crockford Base32 tag. A new
|
|
21
|
+
* illegal tag, in any file, fails here at build time instead of at runtime.
|
|
22
|
+
*/
|
|
23
|
+
// Crockford Base32 alphabet — no I, L, O, U (mirrors util/src/base32.ts).
|
|
24
|
+
const CROCKFORD_BASE32_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
25
|
+
const TAG_CHAR_SET = new Set(CROCKFORD_BASE32_ALPHABET.split(""));
|
|
26
|
+
const TAG_LENGTH = 8;
|
|
27
|
+
function isValidLogTag(tag) {
|
|
28
|
+
if (tag.length !== TAG_LENGTH) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
for (const ch of tag) {
|
|
32
|
+
if (!TAG_CHAR_SET.has(ch.toUpperCase())) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
// Roots scanned for log-tag literals, relative to this test file.
|
|
39
|
+
const SCAN_ROOTS = [
|
|
40
|
+
join(__dirname, ".."), // packages/cli/src
|
|
41
|
+
join(__dirname, "..", "..", "..", "broker", "src"), // packages/broker/src
|
|
42
|
+
];
|
|
43
|
+
async function collectTsFiles(dir) {
|
|
44
|
+
const out = [];
|
|
45
|
+
let names;
|
|
46
|
+
try {
|
|
47
|
+
names = await readdir(dir);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
for (const name of names) {
|
|
53
|
+
if (name === "node_modules" || name === "dist") {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const full = join(dir, name);
|
|
57
|
+
const info = await stat(full);
|
|
58
|
+
if (info.isDirectory()) {
|
|
59
|
+
out.push(...(await collectTsFiles(full)));
|
|
60
|
+
}
|
|
61
|
+
else if (info.isFile() && name.endsWith(".ts") && !name.endsWith(".test.ts")) {
|
|
62
|
+
out.push(full);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
// Matches `log("XXXXXXXX"` call sites and `... = "XXXXXXXX"` tag constants.
|
|
68
|
+
// The capturing group grabs an 8-char alphanumeric literal; isValidLogTag then
|
|
69
|
+
// decides legality. We intentionally over-collect (any 8-char string assigned
|
|
70
|
+
// to a PL_/TAG const or passed as log()'s first arg) and validate each.
|
|
71
|
+
const LOG_CALL_RE = /\blog\(\s*"([0-9A-Za-z]{8})"/g;
|
|
72
|
+
const TAG_CONST_RE = /\bconst\s+(?:PL_[A-Z0-9_]+|[A-Z0-9_]*TAG[A-Z0-9_]*)\s*=\s*"([0-9A-Za-z]{8})"/g;
|
|
73
|
+
async function collectTagOccurrences() {
|
|
74
|
+
const occurrences = [];
|
|
75
|
+
for (const root of SCAN_ROOTS) {
|
|
76
|
+
const files = await collectTsFiles(root);
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
const content = await readFile(file, "utf8");
|
|
79
|
+
for (const re of [LOG_CALL_RE, TAG_CONST_RE]) {
|
|
80
|
+
re.lastIndex = 0;
|
|
81
|
+
let m = re.exec(content);
|
|
82
|
+
while (m !== null) {
|
|
83
|
+
occurrences.push({ tag: m[1], file, context: m[0] });
|
|
84
|
+
m = re.exec(content);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return occurrences;
|
|
90
|
+
}
|
|
91
|
+
describe("log tag validity (#426 regression guard)", () => {
|
|
92
|
+
test("collects at least the known PL_ tag constants", async () => {
|
|
93
|
+
const occurrences = await collectTagOccurrences();
|
|
94
|
+
// Sanity: the scan must actually find tags, otherwise the regex/paths broke
|
|
95
|
+
// and the guard below would pass vacuously.
|
|
96
|
+
expect(occurrences.length).toBeGreaterThanOrEqual(10);
|
|
97
|
+
});
|
|
98
|
+
test("every log tag literal in cli + broker is valid Crockford Base32", async () => {
|
|
99
|
+
const occurrences = await collectTagOccurrences();
|
|
100
|
+
const invalid = occurrences.filter((o) => !isValidLogTag(o.tag));
|
|
101
|
+
const report = invalid.map((o) => ` ${o.tag} (${o.context}) in ${o.file}`).join("\n");
|
|
102
|
+
expect(invalid, `Illegal Crockford Base32 log tags found:\n${report}`).toEqual([]);
|
|
103
|
+
});
|
|
104
|
+
test("the specific F4FA1L7Z bug (#426) stays fixed", async () => {
|
|
105
|
+
const occurrences = await collectTagOccurrences();
|
|
106
|
+
const offenders = occurrences.filter((o) => o.tag === "F4FA1L7Z");
|
|
107
|
+
expect(offenders).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=log-tag-validity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-tag-validity.test.js","sourceRoot":"","sources":["../../src/__tests__/log-tag-validity.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,16 +8,16 @@ import { _agentNameFromBinary, _printAgentMenu, cmdSetup } from "../commands/set
|
|
|
8
8
|
// ─── _agentNameFromBinary ────────────────────────────────────────────────────
|
|
9
9
|
describe("_agentNameFromBinary", () => {
|
|
10
10
|
test("strips uwf- prefix", () => {
|
|
11
|
-
expect(_agentNameFromBinary("uwf-
|
|
11
|
+
expect(_agentNameFromBinary("uwf-builtin")).toBe("builtin");
|
|
12
12
|
});
|
|
13
13
|
test("strips uwf- prefix for compound names", () => {
|
|
14
|
-
expect(_agentNameFromBinary("uwf-
|
|
14
|
+
expect(_agentNameFromBinary("uwf-some-gateway")).toBe("some-gateway");
|
|
15
15
|
});
|
|
16
16
|
test("returns as-is when no uwf- prefix", () => {
|
|
17
|
-
expect(_agentNameFromBinary("
|
|
17
|
+
expect(_agentNameFromBinary("builtin")).toBe("builtin");
|
|
18
18
|
});
|
|
19
|
-
test("handles uwf-
|
|
20
|
-
expect(_agentNameFromBinary("uwf-
|
|
19
|
+
test("handles uwf-mock", () => {
|
|
20
|
+
expect(_agentNameFromBinary("uwf-mock")).toBe("mock");
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
23
|
// ─── _printAgentMenu ─────────────────────────────────────────────────────────
|
|
@@ -27,9 +27,9 @@ describe("_printAgentMenu", () => {
|
|
|
27
27
|
vi.spyOn(console, "log").mockImplementation((...args) => {
|
|
28
28
|
logs.push(args.join(" "));
|
|
29
29
|
});
|
|
30
|
-
_printAgentMenu(["uwf-
|
|
31
|
-
expect(logs.some((l) => l.includes("
|
|
32
|
-
expect(logs.some((l) => l.includes("
|
|
30
|
+
_printAgentMenu(["uwf-builtin", "uwf-mock"]);
|
|
31
|
+
expect(logs.some((l) => l.includes("Built-in"))).toBe(true);
|
|
32
|
+
expect(logs.some((l) => l.includes("Mock"))).toBe(true);
|
|
33
33
|
vi.restoreAllMocks();
|
|
34
34
|
});
|
|
35
35
|
test("prints unknown agents with binary name as label", () => {
|
|
@@ -56,39 +56,48 @@ describe("cmdSetup agent configuration (engine config is LLM-free, issue #143)",
|
|
|
56
56
|
const result = await cmdSetup({ agent: "claude-code", storageRoot });
|
|
57
57
|
expect(result.defaultAgent).toBe("claude-code");
|
|
58
58
|
const config = parse(readFileSync(join(storageRoot, "config.yaml"), "utf8"));
|
|
59
|
-
expect(config.agents["claude-code"]).toEqual({
|
|
59
|
+
expect(config.agents["claude-code"]).toEqual({
|
|
60
|
+
host: "http://127.0.0.1:7900",
|
|
61
|
+
gateway: "claude-code",
|
|
62
|
+
});
|
|
60
63
|
expect(config.defaultAgent).toBe("claude-code");
|
|
61
64
|
});
|
|
62
65
|
test("preserves existing agents when adding new one", async () => {
|
|
63
|
-
await cmdSetup({ agent: "
|
|
66
|
+
await cmdSetup({ agent: "builtin", storageRoot });
|
|
64
67
|
await cmdSetup({ agent: "claude-code", storageRoot });
|
|
65
68
|
const config = parse(readFileSync(join(storageRoot, "config.yaml"), "utf8"));
|
|
66
|
-
expect(config.agents.
|
|
69
|
+
expect(config.agents.builtin).toBeDefined();
|
|
67
70
|
expect(config.agents["claude-code"]).toBeDefined();
|
|
68
71
|
expect(config.defaultAgent).toBe("claude-code");
|
|
69
72
|
});
|
|
70
73
|
test("updates defaultAgent on re-run with different agent", async () => {
|
|
71
|
-
await cmdSetup({ agent: "
|
|
74
|
+
await cmdSetup({ agent: "mock", storageRoot });
|
|
72
75
|
const config1 = parse(readFileSync(join(storageRoot, "config.yaml"), "utf8"));
|
|
73
|
-
expect(config1.defaultAgent).toBe("
|
|
76
|
+
expect(config1.defaultAgent).toBe("mock");
|
|
74
77
|
await cmdSetup({ agent: "builtin", storageRoot });
|
|
75
78
|
const config2 = parse(readFileSync(join(storageRoot, "config.yaml"), "utf8"));
|
|
76
79
|
expect(config2.defaultAgent).toBe("builtin");
|
|
77
80
|
});
|
|
78
81
|
test("normalizes agent name with uwf- prefix to bare name", async () => {
|
|
79
|
-
const result = await cmdSetup({ agent: "uwf-
|
|
80
|
-
expect(result.defaultAgent).toBe("
|
|
82
|
+
const result = await cmdSetup({ agent: "uwf-builtin", storageRoot });
|
|
83
|
+
expect(result.defaultAgent).toBe("builtin");
|
|
81
84
|
const config = parse(readFileSync(join(storageRoot, "config.yaml"), "utf8"));
|
|
82
|
-
expect(config.agents.
|
|
83
|
-
|
|
85
|
+
expect(config.agents.builtin).toEqual({
|
|
86
|
+
host: "http://127.0.0.1:7900",
|
|
87
|
+
gateway: "builtin",
|
|
88
|
+
});
|
|
89
|
+
expect(config.defaultAgent).toBe("builtin");
|
|
84
90
|
// Verify no duplicate uwf- prefix
|
|
85
|
-
expect(config.agents["uwf-
|
|
91
|
+
expect(config.agents["uwf-builtin"]).toBeUndefined();
|
|
86
92
|
});
|
|
87
93
|
test("normalizes uwf-claude-code to claude-code", async () => {
|
|
88
94
|
const result = await cmdSetup({ agent: "uwf-claude-code", storageRoot });
|
|
89
95
|
expect(result.defaultAgent).toBe("claude-code");
|
|
90
96
|
const config = parse(readFileSync(join(storageRoot, "config.yaml"), "utf8"));
|
|
91
|
-
expect(config.agents["claude-code"]).toEqual({
|
|
97
|
+
expect(config.agents["claude-code"]).toEqual({
|
|
98
|
+
host: "http://127.0.0.1:7900",
|
|
99
|
+
gateway: "claude-code",
|
|
100
|
+
});
|
|
92
101
|
expect(config.defaultAgent).toBe("claude-code");
|
|
93
102
|
// Verify no duplicate uwf- prefix
|
|
94
103
|
expect(config.agents["uwf-claude-code"]).toBeUndefined();
|
|
@@ -97,14 +106,17 @@ describe("cmdSetup agent configuration (engine config is LLM-free, issue #143)",
|
|
|
97
106
|
// First create a config that contains legacy LLM fields
|
|
98
107
|
const { writeFileSync, mkdirSync } = await import("node:fs");
|
|
99
108
|
mkdirSync(storageRoot, { recursive: true });
|
|
100
|
-
writeFileSync(join(storageRoot, "config.yaml"), "providers:\n openai: { baseUrl: x, apiKey: y }\nmodels:\n default: { provider: openai, name: gpt-4o }\ndefaultModel: default\nagents:\n
|
|
101
|
-
await cmdSetup({ agent: "
|
|
109
|
+
writeFileSync(join(storageRoot, "config.yaml"), "providers:\n openai: { baseUrl: x, apiKey: y }\nmodels:\n default: { provider: openai, name: gpt-4o }\ndefaultModel: default\nagents:\n builtin: { host: 'http://127.0.0.1:7900', gateway: builtin }\ndefaultAgent: builtin\n", "utf8");
|
|
110
|
+
await cmdSetup({ agent: "builtin", storageRoot });
|
|
102
111
|
const config = parse(readFileSync(join(storageRoot, "config.yaml"), "utf8"));
|
|
103
112
|
expect(config.providers).toBeUndefined();
|
|
104
113
|
expect(config.models).toBeUndefined();
|
|
105
114
|
expect(config.defaultModel).toBeUndefined();
|
|
106
|
-
expect(config.agents.
|
|
107
|
-
|
|
115
|
+
expect(config.agents.builtin).toEqual({
|
|
116
|
+
host: "http://127.0.0.1:7900",
|
|
117
|
+
gateway: "builtin",
|
|
118
|
+
});
|
|
119
|
+
expect(config.defaultAgent).toBe("builtin");
|
|
108
120
|
});
|
|
109
121
|
});
|
|
110
122
|
//# sourceMappingURL=setup-agent-discovery.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-agent-discovery.test.js","sourceRoot":"","sources":["../../src/__tests__/setup-agent-discovery.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC3E,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEvF,gFAAgF;AAEhF,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"setup-agent-discovery.test.js","sourceRoot":"","sources":["../../src/__tests__/setup-agent-discovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ describe("cmdSetup — non-interactive, no LLM args (issue #143)", () => {
|
|
|
23
23
|
});
|
|
24
24
|
test("preserves existing agentOverrides on rewrite", async () => {
|
|
25
25
|
tempDir = mkdtempSync(join(tmpdir(), "uwf-setup-"));
|
|
26
|
-
writeFileSync(join(tempDir, "config.yaml"), "agents:\n hermes: {
|
|
26
|
+
writeFileSync(join(tempDir, "config.yaml"), "agents:\n hermes: { host: 'http://127.0.0.1:7900', gateway: hermes }\ndefaultAgent: hermes\nagentOverrides:\n solve-issue:\n coder: claude-code\n", "utf8");
|
|
27
27
|
await cmdSetup({ agent: "hermes", storageRoot: tempDir });
|
|
28
28
|
const cfg = parse(readFileSync(join(tempDir, "config.yaml"), "utf8"));
|
|
29
29
|
expect(cfg.agentOverrides).toEqual({ "solve-issue": { coder: "claude-code" } });
|
|
@@ -34,7 +34,10 @@ describe("cmdSetup — non-interactive, no LLM args (issue #143)", () => {
|
|
|
34
34
|
const cfg = parse(readFileSync(join(tempDir, "config.yaml"), "utf8"));
|
|
35
35
|
expect(cfg.defaultAgent).toBe("claude-code");
|
|
36
36
|
const agents = cfg.agents;
|
|
37
|
-
expect(agents["claude-code"]).toEqual({
|
|
37
|
+
expect(agents["claude-code"]).toEqual({
|
|
38
|
+
host: "http://127.0.0.1:7900",
|
|
39
|
+
gateway: "claude-code",
|
|
40
|
+
});
|
|
38
41
|
});
|
|
39
42
|
});
|
|
40
43
|
describe("cmdSetup public surface — provider/model helpers removed (issue #143)", () => {
|