@wrongstack/cli 0.303.0 → 0.305.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-AQMGKW6O.js → acp-5ZLGFHWP.js} +4 -2
- package/dist/{auth-K7CYVVKH.js → auth-EPERLKYI.js} +3 -3
- package/dist/boot/system-prompt-builder.d.ts +24 -1
- package/dist/boot/tui-project-switch.d.ts +1 -1
- package/dist/boot/tui-runtime-state.d.ts +14 -0
- package/dist/{chunk-C5NXM2SI.js → chunk-2H47YMCV.js} +5 -1
- package/dist/{chunk-FJVCVZIV.js → chunk-5KFLLTSQ.js} +2 -2
- package/dist/{chunk-JW75HY4F.js → chunk-FKWHSFX4.js} +33 -4
- package/dist/{chunk-LUAFQIXT.js → chunk-RHLQT3EV.js} +11 -1
- package/dist/{cli-main-37XO26GK.js → cli-main-6YE423OH.js} +479 -326
- package/dist/diag-doctor-WTBHPOMB.js +329 -0
- package/dist/execute-deps.d.ts +16 -0
- package/dist/{execution-MVOH6PAQ.js → execution-QYFWDD3Y.js} +70 -35
- package/dist/fleet/host-context.d.ts +2 -0
- package/dist/fleet/host-session-writer.d.ts +33 -0
- package/dist/fleet/host.d.ts +10 -0
- package/dist/{hq-CD77GVSS.js → hq-5G6D5YIU.js} +2 -2
- package/dist/{hq-server-DME6EWIO.js → hq-server-IVZLVCFV.js} +2 -2
- package/dist/index.js +17 -60
- package/dist/{mailbox-serve-DBOOOOWZ.js → mailbox-serve-V3QIJBDH.js} +2 -2
- package/dist/{mcp-AX6ZAFJR.js → mcp-MSARYOPN.js} +4 -2
- package/dist/replay-XU6ZD7GM.js +34 -0
- package/dist/slash-commands/memory-triage.d.ts +4 -17
- package/dist/subcommands/handlers/daemon-inventory.d.ts +41 -0
- package/dist/{webui-server-E3OPEKU5.js → webui-server-FTG7JAZU.js} +5 -5
- package/dist/wiring/domain-glossary.d.ts +58 -0
- package/dist/wiring/domain-terms-mirror.d.ts +52 -0
- package/dist/wiring/pipeline.d.ts +2 -0
- package/dist/wiring/replay.d.ts +11 -1
- package/dist/wiring/sage.d.ts +4 -16
- package/package.json +24 -23
- package/dist/diag-doctor-ZF5BF2MK.js +0 -169
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import {
|
|
2
|
+
API_VERSION
|
|
3
|
+
} from "./chunk-XJXDOF63.js";
|
|
4
|
+
import "./chunk-7OCVIDC7.js";
|
|
5
|
+
|
|
6
|
+
// src/subcommands/handlers/diag-doctor.ts
|
|
7
|
+
import * as fs2 from "node:fs/promises";
|
|
8
|
+
import * as os from "node:os";
|
|
9
|
+
import * as path2 from "node:path";
|
|
10
|
+
import { color, toErrorMessage } from "@wrongstack/core/utils";
|
|
11
|
+
|
|
12
|
+
// src/subcommands/handlers/daemon-inventory.ts
|
|
13
|
+
import * as fs from "node:fs/promises";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import {
|
|
16
|
+
chronicleProjectServerEndpoint,
|
|
17
|
+
chronicleProjectServerMetadataPath
|
|
18
|
+
} from "@wrongstack/core/chronicle";
|
|
19
|
+
import {
|
|
20
|
+
mailboxProjectServerEndpoint,
|
|
21
|
+
mailboxProjectServerMetadataPath
|
|
22
|
+
} from "@wrongstack/core/coordination";
|
|
23
|
+
import {
|
|
24
|
+
sessionCatalogProjectServerEndpoint,
|
|
25
|
+
sessionCatalogProjectServerMetadataPath
|
|
26
|
+
} from "@wrongstack/core/session-catalog";
|
|
27
|
+
import {
|
|
28
|
+
KANBAN_PROJECT_SERVER_METADATA_FILE,
|
|
29
|
+
kanbanProjectServerEndpoint
|
|
30
|
+
} from "@wrongstack/kanban";
|
|
31
|
+
import { isProjectEndpointLive } from "@wrongstack/persistence";
|
|
32
|
+
import { sageProjectServerEndpoint, sageProjectServerMetadataPath } from "@wrongstack/sage";
|
|
33
|
+
import {
|
|
34
|
+
projectIndexServerEndpoint,
|
|
35
|
+
projectIndexServerMetadataPath
|
|
36
|
+
} from "@wrongstack/tools/codebase-index";
|
|
37
|
+
function describeDaemons(options) {
|
|
38
|
+
const { projectRoot, projectDir } = options;
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
name: "kanban",
|
|
42
|
+
// In-repo `<projectRoot>/.wrongstack/`, not the global per-project state
|
|
43
|
+
// directory the other daemons use. Kanban deliberately keeps its server
|
|
44
|
+
// file next to the boards it owns.
|
|
45
|
+
metadataPath: path.join(projectRoot, ".wrongstack", KANBAN_PROJECT_SERVER_METADATA_FILE),
|
|
46
|
+
endpoint: kanbanProjectServerEndpoint(projectRoot)
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "sage",
|
|
50
|
+
endpoint: sageProjectServerEndpoint(projectRoot),
|
|
51
|
+
metadataPath: sageProjectServerMetadataPath(projectRoot)
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "chronicle",
|
|
55
|
+
endpoint: chronicleProjectServerEndpoint(projectDir),
|
|
56
|
+
metadataPath: chronicleProjectServerMetadataPath(projectDir)
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "mailbox",
|
|
60
|
+
endpoint: mailboxProjectServerEndpoint(projectDir),
|
|
61
|
+
metadataPath: mailboxProjectServerMetadataPath(projectDir)
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "session-catalog",
|
|
65
|
+
endpoint: sessionCatalogProjectServerEndpoint(projectDir),
|
|
66
|
+
metadataPath: sessionCatalogProjectServerMetadataPath(projectDir)
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "codebase-index",
|
|
70
|
+
endpoint: projectIndexServerEndpoint(projectRoot),
|
|
71
|
+
metadataPath: projectIndexServerMetadataPath(projectRoot)
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
async function readPid(metadataPath) {
|
|
76
|
+
if (!metadataPath) return void 0;
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(await fs.readFile(metadataPath, "utf8"));
|
|
79
|
+
return typeof parsed.pid === "number" ? parsed.pid : void 0;
|
|
80
|
+
} catch {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function endpointExists(endpoint) {
|
|
85
|
+
if (process.platform === "win32") return false;
|
|
86
|
+
try {
|
|
87
|
+
await fs.stat(endpoint);
|
|
88
|
+
return true;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function collectDaemonReports(options) {
|
|
94
|
+
return Promise.all(
|
|
95
|
+
describeDaemons(options).map(async (descriptor) => {
|
|
96
|
+
const live = await isProjectEndpointLive(descriptor.endpoint);
|
|
97
|
+
const status = live ? "live" : await endpointExists(descriptor.endpoint) ? "stale" : "stopped";
|
|
98
|
+
const pid = live ? await readPid(descriptor.metadataPath) : void 0;
|
|
99
|
+
return pid === void 0 ? { name: descriptor.name, endpoint: descriptor.endpoint, status } : { name: descriptor.name, endpoint: descriptor.endpoint, status, pid };
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
async function clearStaleDaemonEndpoints(options) {
|
|
104
|
+
const cleared = [];
|
|
105
|
+
for (const report of await collectDaemonReports(options)) {
|
|
106
|
+
if (report.status !== "stale") continue;
|
|
107
|
+
try {
|
|
108
|
+
await fs.rm(report.endpoint, { force: true });
|
|
109
|
+
cleared.push(report.name);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return cleared;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/subcommands/handlers/diag-doctor.ts
|
|
117
|
+
var diagCmd = async (_args, deps) => {
|
|
118
|
+
const cfg = deps.config;
|
|
119
|
+
const age = await deps.modelsRegistry.ageSeconds();
|
|
120
|
+
const lines = [
|
|
121
|
+
color.bold("WrongStack diagnostics"),
|
|
122
|
+
` apiVersion: ${API_VERSION}`,
|
|
123
|
+
` cwd: ${deps.cwd}`,
|
|
124
|
+
` projectRoot: ${deps.projectRoot}`,
|
|
125
|
+
` projectHash: ${deps.paths.projectHash}`,
|
|
126
|
+
` projectDir: ${deps.paths.projectDir}`,
|
|
127
|
+
` globalRoot: ${deps.paths.globalRoot}`,
|
|
128
|
+
` modelsCache: ${deps.paths.modelsCache}`,
|
|
129
|
+
` cacheAge: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never"}`,
|
|
130
|
+
` node: ${process.version}`,
|
|
131
|
+
` os: ${os.platform()} ${os.release()}`,
|
|
132
|
+
` provider: ${cfg.provider ?? "<unset>"}`,
|
|
133
|
+
` model: ${cfg.model ?? "<unset>"}`,
|
|
134
|
+
` tools: ${deps.toolRegistry?.list().length ?? 0}`,
|
|
135
|
+
` plugins: ${cfg.plugins?.length ?? 0}`,
|
|
136
|
+
` mcpServers: ${Object.keys(cfg.mcpServers ?? {}).length}`
|
|
137
|
+
];
|
|
138
|
+
deps.renderer.write(lines.join("\n") + "\n");
|
|
139
|
+
return 0;
|
|
140
|
+
};
|
|
141
|
+
async function reportDaemons(deps, opts) {
|
|
142
|
+
const inventory = { projectRoot: deps.projectRoot, projectDir: deps.paths.projectDir };
|
|
143
|
+
let reports = await collectDaemonReports(inventory);
|
|
144
|
+
deps.renderer.write(color.bold("WrongStack project daemons\n\n"));
|
|
145
|
+
const icon = (status) => status === "live" ? color.green("\u2713") : status === "stale" ? color.red("\u2717") : color.dim("\xB7");
|
|
146
|
+
for (const report of reports) {
|
|
147
|
+
const suffix = report.pid === void 0 ? "" : ` pid ${report.pid}`;
|
|
148
|
+
deps.renderer.write(
|
|
149
|
+
` ${icon(report.status)} ${report.name.padEnd(16)} ${report.status.padEnd(8)}${color.dim(report.endpoint + suffix)}
|
|
150
|
+
`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const stale = reports.filter((report) => report.status === "stale");
|
|
154
|
+
if (stale.length === 0) {
|
|
155
|
+
deps.renderer.write(color.green("\nNo wedged endpoints.\n"));
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
if (!opts.clear) {
|
|
159
|
+
deps.renderer.write(
|
|
160
|
+
color.amber(
|
|
161
|
+
`
|
|
162
|
+
${stale.length} stale endpoint${stale.length === 1 ? "" : "s"}: ${stale.map((report) => report.name).join(", ")}
|
|
163
|
+
`
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
deps.renderer.write(
|
|
167
|
+
color.dim(
|
|
168
|
+
" A daemon died without releasing its endpoint. Current daemons reclaim\n this automatically on next start; clear it now with:\n wstack doctor --daemons --clear-stale\n"
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
const cleared = await clearStaleDaemonEndpoints(inventory);
|
|
174
|
+
reports = await collectDaemonReports(inventory);
|
|
175
|
+
const remaining = reports.filter((report) => report.status === "stale");
|
|
176
|
+
if (cleared.length > 0) {
|
|
177
|
+
deps.renderer.write(color.green(`
|
|
178
|
+
Cleared: ${cleared.join(", ")}
|
|
179
|
+
`));
|
|
180
|
+
}
|
|
181
|
+
if (remaining.length > 0) {
|
|
182
|
+
deps.renderer.write(
|
|
183
|
+
color.red(`Still wedged: ${remaining.map((report) => report.name).join(", ")}
|
|
184
|
+
`)
|
|
185
|
+
);
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
deps.renderer.write(color.dim("Daemons restart on demand \u2014 no further action needed.\n"));
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
var doctorCmd = async (args, deps) => {
|
|
192
|
+
const flagged = (name) => deps.flags?.[name] === true || deps.flags?.[name] === "true" || args.includes(`--${name}`);
|
|
193
|
+
if (flagged("daemons")) {
|
|
194
|
+
return reportDaemons(deps, { clear: flagged("clear-stale") });
|
|
195
|
+
}
|
|
196
|
+
const checks = [];
|
|
197
|
+
const cfg = deps.config;
|
|
198
|
+
if (!cfg.provider)
|
|
199
|
+
checks.push({
|
|
200
|
+
name: "provider",
|
|
201
|
+
status: "fail",
|
|
202
|
+
detail: "no provider configured \u2014 run `wstack auth` to set up"
|
|
203
|
+
});
|
|
204
|
+
else checks.push({ name: "provider", status: "ok", detail: cfg.provider });
|
|
205
|
+
if (!cfg.model)
|
|
206
|
+
checks.push({
|
|
207
|
+
name: "model",
|
|
208
|
+
status: "fail",
|
|
209
|
+
detail: "no model configured \u2014 run `wstack auth` to configure"
|
|
210
|
+
});
|
|
211
|
+
else checks.push({ name: "model", status: "ok", detail: cfg.model });
|
|
212
|
+
if (cfg.provider) {
|
|
213
|
+
const providerCfg = cfg.providers?.[cfg.provider];
|
|
214
|
+
const hasVaultKey = typeof providerCfg?.apiKey === "string" && providerCfg.apiKey.length > 0;
|
|
215
|
+
const envHit = providerCfg?.envVars?.some((v) => process.env[v]) ?? false;
|
|
216
|
+
if (hasVaultKey || envHit)
|
|
217
|
+
checks.push({
|
|
218
|
+
name: "api key",
|
|
219
|
+
status: "ok",
|
|
220
|
+
detail: hasVaultKey ? "found in vault" : "found in env"
|
|
221
|
+
});
|
|
222
|
+
else
|
|
223
|
+
checks.push({
|
|
224
|
+
name: "api key",
|
|
225
|
+
status: "fail",
|
|
226
|
+
detail: `no key for "${cfg.provider}" in vault or env \u2014 run \`wstack auth ${cfg.provider}\``
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const age = await deps.modelsRegistry.ageSeconds();
|
|
231
|
+
if (!isFinite(age))
|
|
232
|
+
checks.push({
|
|
233
|
+
name: "models cache",
|
|
234
|
+
status: "warn",
|
|
235
|
+
detail: "never fetched \u2014 run `wstack models refresh`"
|
|
236
|
+
});
|
|
237
|
+
else if (age > 7 * 24 * 3600)
|
|
238
|
+
checks.push({
|
|
239
|
+
name: "models cache",
|
|
240
|
+
status: "warn",
|
|
241
|
+
detail: `${Math.round(age / 86400)} days old \u2014 run \`wstack models refresh\``
|
|
242
|
+
});
|
|
243
|
+
else
|
|
244
|
+
checks.push({ name: "models cache", status: "ok", detail: `${Math.round(age / 60)}m old` });
|
|
245
|
+
} catch (err) {
|
|
246
|
+
checks.push({
|
|
247
|
+
name: "models cache",
|
|
248
|
+
status: "warn",
|
|
249
|
+
detail: `read failed: ${toErrorMessage(err)}`
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
await fs2.access(deps.paths.secretsKey);
|
|
254
|
+
checks.push({ name: "secret vault", status: "ok", detail: deps.paths.secretsKey });
|
|
255
|
+
} catch {
|
|
256
|
+
checks.push({
|
|
257
|
+
name: "secret vault",
|
|
258
|
+
status: "warn",
|
|
259
|
+
detail: "not yet initialized (created lazily on first encrypt)"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
await fs2.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
264
|
+
const probe = path2.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
265
|
+
await fs2.writeFile(probe, "");
|
|
266
|
+
await fs2.unlink(probe);
|
|
267
|
+
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
268
|
+
} catch (err) {
|
|
269
|
+
checks.push({
|
|
270
|
+
name: "sessions writable",
|
|
271
|
+
status: "fail",
|
|
272
|
+
detail: `cannot write to ${deps.paths.projectSessions}: ${toErrorMessage(err)}`
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
const mcpEntries = Object.entries(cfg.mcpServers ?? {});
|
|
276
|
+
for (const [name, srv] of mcpEntries) {
|
|
277
|
+
if (!srv.enabled) continue;
|
|
278
|
+
if ((srv.transport === "sse" || srv.transport === "streamable-http") && !srv.url)
|
|
279
|
+
checks.push({ name: `mcp:${name}`, status: "fail", detail: "transport requires url" });
|
|
280
|
+
else if (srv.transport === "stdio" && !srv.command)
|
|
281
|
+
checks.push({
|
|
282
|
+
name: `mcp:${name}`,
|
|
283
|
+
status: "fail",
|
|
284
|
+
detail: "stdio transport requires command"
|
|
285
|
+
});
|
|
286
|
+
else
|
|
287
|
+
checks.push({
|
|
288
|
+
name: `mcp:${name}`,
|
|
289
|
+
status: "ok",
|
|
290
|
+
detail: `${srv.transport} ${srv.command ?? srv.url ?? ""}`.trim()
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
const major = Number.parseInt(process.version.replace(/^v/, "").split(".")[0] ?? "0", 10);
|
|
294
|
+
if (major < 22)
|
|
295
|
+
checks.push({ name: "node", status: "fail", detail: `${process.version} (need \u226522)` });
|
|
296
|
+
else checks.push({ name: "node", status: "ok", detail: process.version });
|
|
297
|
+
deps.renderer.write(color.bold("WrongStack doctor\n\n"));
|
|
298
|
+
let failed = 0;
|
|
299
|
+
let warned = 0;
|
|
300
|
+
for (const c of checks) {
|
|
301
|
+
const icon = c.status === "ok" ? color.green("\u2713") : c.status === "warn" ? color.amber("\u25CF") : color.red("\u2717");
|
|
302
|
+
deps.renderer.write(` ${icon} ${c.name.padEnd(20)} ${color.dim(c.detail)}
|
|
303
|
+
`);
|
|
304
|
+
if (c.status === "fail") failed++;
|
|
305
|
+
if (c.status === "warn") warned++;
|
|
306
|
+
}
|
|
307
|
+
deps.renderer.write("\n");
|
|
308
|
+
if (failed > 0) {
|
|
309
|
+
deps.renderer.write(
|
|
310
|
+
color.red(`${failed} failed, ${warned} warning${warned === 1 ? "" : "s"}
|
|
311
|
+
`)
|
|
312
|
+
);
|
|
313
|
+
return 1;
|
|
314
|
+
}
|
|
315
|
+
if (warned > 0) {
|
|
316
|
+
deps.renderer.write(
|
|
317
|
+
color.amber(`All checks passed (${warned} warning${warned === 1 ? "" : "s"})
|
|
318
|
+
`)
|
|
319
|
+
);
|
|
320
|
+
return 0;
|
|
321
|
+
}
|
|
322
|
+
deps.renderer.write(color.green("All checks passed.\n"));
|
|
323
|
+
return 0;
|
|
324
|
+
};
|
|
325
|
+
export {
|
|
326
|
+
diagCmd,
|
|
327
|
+
doctorCmd
|
|
328
|
+
};
|
|
329
|
+
//# sourceMappingURL=diag-doctor-WTBHPOMB.js.map
|
package/dist/execute-deps.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ export interface ToolPickerItem {
|
|
|
48
48
|
owner: string;
|
|
49
49
|
category: string;
|
|
50
50
|
enabled: boolean;
|
|
51
|
+
exposure: 'direct' | 'lazy' | 'disabled';
|
|
51
52
|
mutating: boolean;
|
|
52
53
|
permission: string;
|
|
53
54
|
descMode: 'extend' | 'simple';
|
|
@@ -89,6 +90,21 @@ export interface CoreDeps {
|
|
|
89
90
|
positional: string[];
|
|
90
91
|
slashRegistry: SlashCommandRegistry;
|
|
91
92
|
tokenCounter: TokenCounter;
|
|
93
|
+
/**
|
|
94
|
+
* Forward-declared session ref owned by the host (cli-main). When an
|
|
95
|
+
* in-process `/resume` swaps the agent's active writer, the resume
|
|
96
|
+
* handler repoints `sessionRef.current` so provider-side
|
|
97
|
+
* `getSessionId: () => sessionRef.current?.id` callbacks and the
|
|
98
|
+
* record-mode `bindReplayToContainer` binding follow the resumed
|
|
99
|
+
* session instead of staying pinned to the boot session.
|
|
100
|
+
*
|
|
101
|
+
* Optional: hosts that don't need post-resume propagation (or tests
|
|
102
|
+
* that predate the refactor) can omit it; provider calls then stay
|
|
103
|
+
* pinned to the boot session — same behavior as before this existed.
|
|
104
|
+
*/
|
|
105
|
+
sessionRef?: {
|
|
106
|
+
current: import('@wrongstack/core/types').SessionWriter | undefined;
|
|
107
|
+
} | undefined;
|
|
92
108
|
/** Atomically move this process's SessionRegistry ownership after explicit resume. */
|
|
93
109
|
activateSessionIdentity?: ((sessionId: string, target?: import('./wiring/session-registry.js').SessionIdentityTarget) => Promise<void>) | undefined;
|
|
94
110
|
/**
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-V3XH6XBJ.js";
|
|
11
11
|
import {
|
|
12
12
|
startCliHqConnection
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FKWHSFX4.js";
|
|
14
14
|
import "./chunk-WHOIR577.js";
|
|
15
15
|
import {
|
|
16
16
|
advanceToNextTask,
|
|
@@ -25,17 +25,17 @@ import {
|
|
|
25
25
|
trySaveSpecFromAIOutput,
|
|
26
26
|
trySaveTasksFromAIOutput
|
|
27
27
|
} from "./chunk-DGXNL7OT.js";
|
|
28
|
-
import {
|
|
29
|
-
WEBUI_SESSION_CHILD_CAPABILITIES,
|
|
30
|
-
writeWebuiSessionChildError,
|
|
31
|
-
writeWebuiSessionChildReady
|
|
32
|
-
} from "./chunk-3JPTNADJ.js";
|
|
33
28
|
import {
|
|
34
29
|
theme
|
|
35
30
|
} from "./chunk-QD544J2B.js";
|
|
36
31
|
import {
|
|
37
32
|
fmtTok
|
|
38
33
|
} from "./chunk-TYO2OVAD.js";
|
|
34
|
+
import {
|
|
35
|
+
WEBUI_SESSION_CHILD_CAPABILITIES,
|
|
36
|
+
writeWebuiSessionChildError,
|
|
37
|
+
writeWebuiSessionChildReady
|
|
38
|
+
} from "./chunk-3JPTNADJ.js";
|
|
39
39
|
import {
|
|
40
40
|
resolveActiveApiKey
|
|
41
41
|
} from "./chunk-SZ42FYPT.js";
|
|
@@ -203,7 +203,7 @@ async function runWebUIDispatch(ctx) {
|
|
|
203
203
|
const isSimpleUi = !isSessionChild && flags["simpleui"] === true;
|
|
204
204
|
agent.disableInteractiveConfirmation();
|
|
205
205
|
renderer.setSilent(true);
|
|
206
|
-
const { runWebUI } = await import("./webui-server-
|
|
206
|
+
const { runWebUI } = await import("./webui-server-FTG7JAZU.js");
|
|
207
207
|
const flagValue = (names) => {
|
|
208
208
|
for (const name of names) {
|
|
209
209
|
if (!Object.hasOwn(flags, name)) continue;
|
|
@@ -985,8 +985,13 @@ import {
|
|
|
985
985
|
DefaultSystemPromptBuilder,
|
|
986
986
|
setQueuedMessagesSnapshot
|
|
987
987
|
} from "@wrongstack/core/agent";
|
|
988
|
+
import { TOKENS } from "@wrongstack/core/kernel";
|
|
988
989
|
import { DefaultSessionStore } from "@wrongstack/core/storage";
|
|
989
|
-
import {
|
|
990
|
+
import {
|
|
991
|
+
activateProjectStateGuard,
|
|
992
|
+
resolveWstackPaths,
|
|
993
|
+
sessionScopedPath
|
|
994
|
+
} from "@wrongstack/core/utils";
|
|
990
995
|
async function switchProjectInPlace(ctx, targetRoot, displayName) {
|
|
991
996
|
const {
|
|
992
997
|
state,
|
|
@@ -1064,6 +1069,7 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
|
|
|
1064
1069
|
});
|
|
1065
1070
|
targetActivated = true;
|
|
1066
1071
|
await state.activateSessionIdentity(nextWriter.id, target);
|
|
1072
|
+
await activateProjectStateGuard(resolved);
|
|
1067
1073
|
} catch (err) {
|
|
1068
1074
|
if (!targetActivated) await targetClaim?.cancel().catch(() => void 0);
|
|
1069
1075
|
else if (oldWriter) {
|
|
@@ -1124,6 +1130,13 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
|
|
|
1124
1130
|
modeStore,
|
|
1125
1131
|
modeId: modeId ?? "default",
|
|
1126
1132
|
modePrompt: switchMode?.prompt ?? "",
|
|
1133
|
+
tokenSavingMode: config.features.tokenSavingMode,
|
|
1134
|
+
modelCapabilities: {
|
|
1135
|
+
maxContextTokens: context.provider.capabilities.maxContext,
|
|
1136
|
+
supportsTools: !!context.provider.capabilities.tools,
|
|
1137
|
+
supportsVision: !!context.provider.capabilities.vision,
|
|
1138
|
+
supportsReasoning: !!context.provider.capabilities.reasoning
|
|
1139
|
+
},
|
|
1127
1140
|
instructionPaths: {
|
|
1128
1141
|
globalDir: nextWpaths.globalInstructions,
|
|
1129
1142
|
projectDir: nextWpaths.inProjectInstructions,
|
|
@@ -1138,6 +1151,11 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
|
|
|
1138
1151
|
provider: context.provider.id,
|
|
1139
1152
|
model: context.model
|
|
1140
1153
|
});
|
|
1154
|
+
if (agent.container.has(TOKENS.SystemPromptBuilder)) {
|
|
1155
|
+
agent.container.override(TOKENS.SystemPromptBuilder, () => switchBuilder, {
|
|
1156
|
+
owner: "tui-project-switch"
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1141
1159
|
} catch (err) {
|
|
1142
1160
|
console.error(
|
|
1143
1161
|
JSON.stringify({
|
|
@@ -1298,6 +1316,7 @@ async function resumeSession(ctx, sessionId) {
|
|
|
1298
1316
|
throw err;
|
|
1299
1317
|
}
|
|
1300
1318
|
writerSwapped = true;
|
|
1319
|
+
state.sessionRef && (state.sessionRef.current = resumed.writer);
|
|
1301
1320
|
await agent.ctx.flushConversationJournal().catch((err) => {
|
|
1302
1321
|
console.error(
|
|
1303
1322
|
JSON.stringify({
|
|
@@ -1854,24 +1873,9 @@ function createSettingsAdapter(ctx) {
|
|
|
1854
1873
|
// src/boot/tui-theme-adapter.ts
|
|
1855
1874
|
import * as fs4 from "node:fs/promises";
|
|
1856
1875
|
import * as path9 from "node:path";
|
|
1876
|
+
import { THEME_PRESET_IDS } from "@wrongstack/core/types";
|
|
1857
1877
|
import { atomicWrite as atomicWrite2 } from "@wrongstack/core/utils";
|
|
1858
|
-
var VALID_PRESETS =
|
|
1859
|
-
"catppuccin",
|
|
1860
|
-
"tokyo-night",
|
|
1861
|
-
"nord",
|
|
1862
|
-
"cyberpunk",
|
|
1863
|
-
"dracula",
|
|
1864
|
-
"gruvbox-dark",
|
|
1865
|
-
"solarized-dark",
|
|
1866
|
-
"one-dark",
|
|
1867
|
-
"monokai",
|
|
1868
|
-
"rose-pine",
|
|
1869
|
-
"kanagawa",
|
|
1870
|
-
"ayu-dark",
|
|
1871
|
-
"everforest",
|
|
1872
|
-
"night-owl",
|
|
1873
|
-
"synthwave"
|
|
1874
|
-
]);
|
|
1878
|
+
var VALID_PRESETS = new Set(THEME_PRESET_IDS);
|
|
1875
1879
|
function createThemeAdapter({ configStore, wpaths }) {
|
|
1876
1880
|
return {
|
|
1877
1881
|
getThemePreset: () => {
|
|
@@ -2319,7 +2323,9 @@ async function finalizeExecutionCleanup(input) {
|
|
|
2319
2323
|
|
|
2320
2324
|
// src/execution-kanban-dispatch.ts
|
|
2321
2325
|
import { randomUUID } from "node:crypto";
|
|
2326
|
+
import { missingRequiredRuntimeTools } from "@wrongstack/core/agent-catalog";
|
|
2322
2327
|
import { WIDE_SUBAGENT_CAPABILITIES } from "@wrongstack/core/security";
|
|
2328
|
+
import { stripFrontmatter } from "@wrongstack/core/skills";
|
|
2323
2329
|
|
|
2324
2330
|
// src/kanban-dispatch-route.ts
|
|
2325
2331
|
import { fallbackProfileChain, parseModelRef as parseModelRef2 } from "@wrongstack/core/agent";
|
|
@@ -2358,21 +2364,43 @@ function createKanbanDispatchHandler({
|
|
|
2358
2364
|
} = resolveKanbanDispatchRoute(config, spawnOpts);
|
|
2359
2365
|
let agentDescription = description;
|
|
2360
2366
|
if (spawnOpts?.skills?.length && skillLoader) {
|
|
2361
|
-
const
|
|
2362
|
-
|
|
2367
|
+
const availableToolNames = spawnOpts.tools;
|
|
2368
|
+
const loaded = [];
|
|
2369
|
+
for (const skillName of spawnOpts.skills) {
|
|
2370
|
+
try {
|
|
2363
2371
|
const manifest = await skillLoader.find(skillName);
|
|
2364
|
-
if (!manifest)
|
|
2365
|
-
|
|
2366
|
-
|
|
2372
|
+
if (!manifest) {
|
|
2373
|
+
process.emitWarning(`Kanban dispatch skill not found, skipped: ${skillName}`, {
|
|
2374
|
+
code: "WRONGSTACK_KANBAN_SKILL_SKIPPED"
|
|
2375
|
+
});
|
|
2376
|
+
continue;
|
|
2377
|
+
}
|
|
2378
|
+
if (availableToolNames !== void 0 && missingRequiredRuntimeTools(manifest.requiredTools, availableToolNames).length > 0) {
|
|
2379
|
+
process.emitWarning(
|
|
2380
|
+
`Kanban dispatch skill "${skillName}" needs tools this worker does not have, skipped.`,
|
|
2381
|
+
{ code: "WRONGSTACK_KANBAN_SKILL_SKIPPED" }
|
|
2382
|
+
);
|
|
2383
|
+
continue;
|
|
2384
|
+
}
|
|
2385
|
+
const body = stripFrontmatter(await skillLoader.readBody(skillName)).trim();
|
|
2386
|
+
if (!body) continue;
|
|
2387
|
+
loaded.push(`## Required skill: ${skillName}
|
|
2367
2388
|
|
|
2368
|
-
${body}
|
|
2369
|
-
})
|
|
2370
|
-
|
|
2371
|
-
|
|
2389
|
+
${body}`);
|
|
2390
|
+
} catch (err) {
|
|
2391
|
+
process.emitWarning(
|
|
2392
|
+
`Kanban dispatch skill "${skillName}" could not be loaded, skipped: ${err instanceof Error ? err.message : String(err)}`,
|
|
2393
|
+
{ code: "WRONGSTACK_KANBAN_SKILL_SKIPPED" }
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
if (loaded.length > 0) {
|
|
2398
|
+
agentDescription = `${description}
|
|
2372
2399
|
|
|
2373
2400
|
# Required agentic skill instructions
|
|
2374
2401
|
|
|
2375
2402
|
${loaded.join("\n\n")}`;
|
|
2403
|
+
}
|
|
2376
2404
|
}
|
|
2377
2405
|
void (async () => {
|
|
2378
2406
|
const built = await sddSubagentFactory({
|
|
@@ -4738,6 +4766,7 @@ async function execute(deps) {
|
|
|
4738
4766
|
positional,
|
|
4739
4767
|
slashRegistry,
|
|
4740
4768
|
tokenCounter,
|
|
4769
|
+
sessionRef,
|
|
4741
4770
|
activateSessionIdentity,
|
|
4742
4771
|
updateInfo: initialUpdateInfo,
|
|
4743
4772
|
webuiSessionChild
|
|
@@ -4934,6 +4963,12 @@ async function execute(deps) {
|
|
|
4934
4963
|
activeSessionStore,
|
|
4935
4964
|
activateSessionIdentity,
|
|
4936
4965
|
detachActiveTodosCheckpoint,
|
|
4966
|
+
// Plumbed from cli-main.ts so `resumeSession` can repoint the ref
|
|
4967
|
+
// when an in-process `/resume` swaps the active writer. Optional
|
|
4968
|
+
// on TuiRuntimeState; tests/hosts that omit it revert to the
|
|
4969
|
+
// pre-refactor behavior where provider calls stay pinned to the
|
|
4970
|
+
// boot session.
|
|
4971
|
+
sessionRef,
|
|
4937
4972
|
pendingProjectSwitch: null,
|
|
4938
4973
|
autonomousCoordinator: null,
|
|
4939
4974
|
coordinatorRun: null,
|
|
@@ -5312,4 +5347,4 @@ export {
|
|
|
5312
5347
|
execute,
|
|
5313
5348
|
resolveReviewerFallbackModels
|
|
5314
5349
|
};
|
|
5315
|
-
//# sourceMappingURL=execution-
|
|
5350
|
+
//# sourceMappingURL=execution-QYFWDD3Y.js.map
|
|
@@ -6,6 +6,8 @@ export interface SkillResolutionReport {
|
|
|
6
6
|
selected: string[];
|
|
7
7
|
/** skill → why it was dropped, so the omission is never silent. */
|
|
8
8
|
dropped: Record<string, 'not-found' | 'missing-capability' | 'missing-tool' | 'budget' | 'empty'>;
|
|
9
|
+
/** Skills kept under budget by shortening their bundled body. */
|
|
10
|
+
trimmed: string[];
|
|
9
11
|
}
|
|
10
12
|
export declare function resolveHostSubagentSkillResolution(deps: MultiAgentDeps, roster: Record<string, SubagentConfig>, subCfg: SubagentConfig, availableToolNames?: readonly string[]): Promise<SkillResolutionReport>;
|
|
11
13
|
/** Backwards-compatible wrapper for callers that only need the prompt text. */
|
|
@@ -1,3 +1,36 @@
|
|
|
1
1
|
import type { SessionWriter } from '@wrongstack/core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Session writer for a subagent that has no journal of its own: its events are
|
|
4
|
+
* interleaved into the parent's JSONL.
|
|
5
|
+
*
|
|
6
|
+
* Lifecycle and rewind *control* stay no-ops — `writeCheckpoint`,
|
|
7
|
+
* `writeInFlightMarker`, `truncateToCheckpoint`, `clearSession` and `close`
|
|
8
|
+
* belong to the parent, and a subagent driving them would corrupt the parent's
|
|
9
|
+
* checkpoint chain and crash-recovery markers.
|
|
10
|
+
*
|
|
11
|
+
* Rewind *evidence* is a different thing and is forwarded. A subagent's file
|
|
12
|
+
* mutations are real edits to the user's working tree, made under the parent
|
|
13
|
+
* prompt that spawned it; dropping their `file_snapshot` records left `/rewind`
|
|
14
|
+
* quietly unable to undo them, with nothing in the transcript saying so. The
|
|
15
|
+
* parent writer files them against its own `activePromptIndex`, which is
|
|
16
|
+
* exactly the prompt a user rewinding this work would target.
|
|
17
|
+
*/
|
|
2
18
|
export declare function createParentSubagentSessionWriter(parentSession: SessionWriter): SessionWriter;
|
|
19
|
+
/**
|
|
20
|
+
* Wrap a subagent's own `SessionWriter` so its file mutations are ALSO recorded
|
|
21
|
+
* against the parent session.
|
|
22
|
+
*
|
|
23
|
+
* When a subagent gets a real per-subagent JSONL, its `file_snapshot` events
|
|
24
|
+
* land in that file — under the director run directory, which
|
|
25
|
+
* `DefaultSessionRewinder` never reads: it resolves one path, the session being
|
|
26
|
+
* rewound. So the richer, "proper" subagent-session path was the one that lost
|
|
27
|
+
* rewind coverage entirely, while the fallback shim above at least had the
|
|
28
|
+
* parent handle in reach.
|
|
29
|
+
*
|
|
30
|
+
* Recording to the parent as well keeps one authority for undo (the session the
|
|
31
|
+
* user actually rewinds) without taking anything away from the subagent's own
|
|
32
|
+
* transcript, which still receives every other event including the conversation
|
|
33
|
+
* that produced the edit.
|
|
34
|
+
*/
|
|
35
|
+
export declare function withParentFileSnapshots(subagentSession: SessionWriter, parentSession: SessionWriter): SessionWriter;
|
|
3
36
|
//# sourceMappingURL=host-session-writer.d.ts.map
|
package/dist/fleet/host.d.ts
CHANGED
|
@@ -211,6 +211,16 @@ export declare class MultiAgentHost {
|
|
|
211
211
|
* Returns null when auto-optimization is switched off.
|
|
212
212
|
*/
|
|
213
213
|
private getLearningOptimizer;
|
|
214
|
+
/**
|
|
215
|
+
* Break a dispatch tie with a model when the keyword heuristic is ambiguous.
|
|
216
|
+
*
|
|
217
|
+
* Routes through the `dispatcher` model-matrix slot when one is configured —
|
|
218
|
+
* picking a role is a short classification, so it belongs on a cheap fast
|
|
219
|
+
* model rather than the leader's — and the session default otherwise.
|
|
220
|
+
* Returning `null` on any failure leaves the dispatcher on its heuristic
|
|
221
|
+
* result, so routing degrades rather than breaking a spawn.
|
|
222
|
+
*/
|
|
223
|
+
private classifyDispatch;
|
|
214
224
|
/**
|
|
215
225
|
* Resolve a model for the distillation pass. Uses the `memory-curator` slot
|
|
216
226
|
* of the model matrix when one is configured — curating learned knowledge is
|
|
@@ -47,7 +47,7 @@ var hqCmd = async (args, deps) => {
|
|
|
47
47
|
return 1;
|
|
48
48
|
};
|
|
49
49
|
async function startServer(deps) {
|
|
50
|
-
const { startHqServer } = await import("./hq-server-
|
|
50
|
+
const { startHqServer } = await import("./hq-server-IVZLVCFV.js");
|
|
51
51
|
const dataDir = resolveDataDir(deps);
|
|
52
52
|
const flags = deps.flags ?? {};
|
|
53
53
|
const host = typeof flags["host"] === "string" ? flags["host"] : HQ_CLI_DEFAULT_HOST;
|
|
@@ -612,4 +612,4 @@ export {
|
|
|
612
612
|
hqCmd,
|
|
613
613
|
resolveAuditActor
|
|
614
614
|
};
|
|
615
|
-
//# sourceMappingURL=hq-
|
|
615
|
+
//# sourceMappingURL=hq-5G6D5YIU.js.map
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
readLocalSubagentTranscript,
|
|
16
16
|
sanitizeApiError,
|
|
17
17
|
startHqServer
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-RHLQT3EV.js";
|
|
19
19
|
import "./chunk-KE7E7DPX.js";
|
|
20
20
|
import "./chunk-Q5GTM25S.js";
|
|
21
21
|
import "./chunk-7OCVIDC7.js";
|
|
@@ -37,4 +37,4 @@ export {
|
|
|
37
37
|
sanitizeApiError,
|
|
38
38
|
startHqServer
|
|
39
39
|
};
|
|
40
|
-
//# sourceMappingURL=hq-server-
|
|
40
|
+
//# sourceMappingURL=hq-server-IVZLVCFV.js.map
|