@wrongstack/cli 1.1.4 → 1.1.5

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.
Files changed (100) hide show
  1. package/dist/{acp-MZ5MQBFN.js → acp-NKUEYJDZ.js} +6 -5
  2. package/dist/acp-leader/acp-leader-confirm.d.ts +2 -0
  3. package/dist/acp-leader/acp-leader-host-binding.d.ts +1 -0
  4. package/dist/acp-leader/acp-leader-host.d.ts +2 -0
  5. package/dist/acp-leader/acp-leader-picker.d.ts +6 -3
  6. package/dist/acp-leader/acp-leader-stream.d.ts +1 -0
  7. package/dist/{auth-IHQS6AFW.js → auth-2ZG5DBHS.js} +4 -4
  8. package/dist/boot/launch-menu-arrow.d.ts +20 -0
  9. package/dist/boot/launch-menu-types.d.ts +36 -0
  10. package/dist/boot/launch-menu.d.ts +1 -57
  11. package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
  12. package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
  13. package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
  14. package/dist/{chunk-JKTJSLUT.js → chunk-5SGMUDP5.js} +242 -18
  15. package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
  16. package/dist/{chunk-2LTHDKZX.js → chunk-7BM4LGKQ.js} +18 -18
  17. package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
  18. package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
  19. package/dist/{chunk-AWRZWPJ3.js → chunk-JFYQGMMM.js} +99 -75
  20. package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
  21. package/dist/{chunk-GKGWPFW2.js → chunk-LUEO5HLZ.js} +1 -1
  22. package/dist/chunk-LYWOV4OX.js +252 -0
  23. package/dist/{chunk-COGI7EQU.js → chunk-M7FNHCE3.js} +621 -9
  24. package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
  25. package/dist/{chunk-TAE3EGBM.js → chunk-MEBXWI7U.js} +5 -5
  26. package/dist/{chunk-I5LJLKA3.js → chunk-MKMM5RIW.js} +12 -5
  27. package/dist/{chunk-ZF4HRR5F.js → chunk-P7MEEUGT.js} +275 -250
  28. package/dist/chunk-QLYPIJ7X.js +258 -0
  29. package/dist/chunk-RN5CFAVQ.js +292 -0
  30. package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
  31. package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
  32. package/dist/chunk-VEESMPTX.js +254 -0
  33. package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
  34. package/dist/{cli-context-E44HQ4ZO.js → cli-context-CQYGIWOU.js} +247 -236
  35. package/dist/{cli-main-OT63I2FX.js → cli-main-VRUJNUBX.js} +1743 -1491
  36. package/dist/cli-main-boot.d.ts +1 -1
  37. package/dist/config-doctor-known-keys.d.ts +33 -0
  38. package/dist/config-doctor.d.ts +2 -47
  39. package/dist/config-history-support.d.ts +36 -0
  40. package/dist/config-history.d.ts +1 -1
  41. package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
  42. package/dist/{execution-OK34CJKJ.js → execution-IV72RUSM.js} +5883 -5792
  43. package/dist/execution-tui.d.ts +30 -0
  44. package/dist/fleet/acp-wrongstack-bridge.d.ts +11 -3
  45. package/dist/fleet/host-acp.d.ts +3 -0
  46. package/dist/fleet/wrongforge-client.d.ts +116 -0
  47. package/dist/fleet/wrongforge-gateway.d.ts +20 -0
  48. package/dist/fleet/wrongforge-info.d.ts +32 -0
  49. package/dist/fleet/wrongforge-quota.d.ts +10 -0
  50. package/dist/fleet/wrongforge-runner.d.ts +37 -0
  51. package/dist/fleet/wrongforge-runtime.d.ts +10 -0
  52. package/dist/{hq-JD5AZVMG.js → hq-54PQYR5O.js} +311 -307
  53. package/dist/hq-server/sweeps.d.ts +28 -0
  54. package/dist/{hq-server-BI23EPM7.js → hq-server-YK7FBLL6.js} +3 -3
  55. package/dist/index.js +5 -5
  56. package/dist/mcp-22CFBX4H.js +236 -0
  57. package/dist/mcp-serve.d.ts +0 -63
  58. package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
  59. package/dist/{per-subcommand-help-4GYSCMLU.js → per-subcommand-help-MQGERD4E.js} +2 -2
  60. package/dist/plugin-llm-command.d.ts +9 -0
  61. package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
  62. package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
  63. package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
  64. package/dist/services/sdd/state.d.ts +2 -0
  65. package/dist/session-event-wiring-types.d.ts +62 -0
  66. package/dist/session-event-wiring.d.ts +2 -52
  67. package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
  68. package/dist/short-circuit-flags-AGHURHMQ.js +11 -0
  69. package/dist/slash-commands/command-context-fleet.d.ts +187 -0
  70. package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
  71. package/dist/slash-commands/command-context.d.ts +3 -282
  72. package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
  73. package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
  74. package/dist/subcommands/handlers/hq-token.d.ts +3 -0
  75. package/dist/subcommands/handlers/hq.d.ts +0 -21
  76. package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
  77. package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
  78. package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
  79. package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
  80. package/dist/{subcommands-YADUV7IJ.js → subcommands-OCJNO57E.js} +2 -2
  81. package/dist/tool-gateway-http.d.ts +30 -0
  82. package/dist/tool-gateway.d.ts +185 -0
  83. package/dist/tools-cli-44MLIUQO.js +509 -0
  84. package/dist/tools-serve-TRCET2M7.js +102 -0
  85. package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
  86. package/dist/tuneup-runtime-checks.d.ts +7 -0
  87. package/dist/tuneup-types.d.ts +118 -0
  88. package/dist/tuneup.d.ts +3 -122
  89. package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
  90. package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
  91. package/dist/{webui-server-CB64CFDG.js → webui-server-V2R5WQUK.js} +44 -6
  92. package/dist/wiring/provider-runtime-setup.d.ts +3 -1
  93. package/dist/wiring/session-runtime.d.ts +1 -1
  94. package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
  95. package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
  96. package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
  97. package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +49 -0
  98. package/package.json +1 -1
  99. package/dist/mcp-W7A5HUXV.js +0 -560
  100. package/dist/short-circuit-flags-QEXUS3BY.js +0 -11
@@ -1,18 +1,19 @@
1
1
 
2
2
  // src/config-history.ts
3
3
  import { randomUUID } from "node:crypto";
4
+ import * as fs2 from "node:fs/promises";
5
+ import * as path2 from "node:path";
6
+ import { isSecretField as isSecretField2 } from "@wrongstack/core/security";
7
+ import { ERROR_CODES as ERROR_CODES2, FsError as FsError2 } from "@wrongstack/core/types";
8
+ import { atomicWrite, toErrorMessage, withFileLock, writeErr } from "@wrongstack/core/utils";
9
+
10
+ // src/config-history-support.ts
4
11
  import * as fs from "node:fs/promises";
5
12
  import os from "node:os";
6
13
  import * as path from "node:path";
7
14
  import { isSecretField } from "@wrongstack/core/security";
8
15
  import { ERROR_CODES, FsError } from "@wrongstack/core/types";
9
- import {
10
- atomicWrite,
11
- resolveWstackPaths,
12
- toErrorMessage,
13
- withFileLock,
14
- writeErr
15
- } from "@wrongstack/core/utils";
16
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
16
17
  var defaultUidFn = () => os.userInfo().uid;
17
18
  async function getFileUid(filePath) {
18
19
  try {
@@ -32,8 +33,6 @@ async function checkConfigOwnership(homeFn, uidFn = defaultUidFn, targetConfigPa
32
33
  return fileUid === callerUid;
33
34
  }
34
35
  var PROTECTED_BASENAMES = /* @__PURE__ */ new Set(["config.json", ".key", "index.json"]);
35
- var MAX_CONFIG_BACKUPS = 10;
36
- var MAX_CONFIG_HISTORY_ENTRIES = 50;
37
36
  function assertSafeToDelete(filename, parentDir) {
38
37
  if (PROTECTED_BASENAMES.has(filename)) {
39
38
  throw new FsError({
@@ -72,19 +71,6 @@ function assertSafeToDelete(filename, parentDir) {
72
71
  });
73
72
  }
74
73
  }
75
- async function safeDelete(filePath) {
76
- const dir = path.dirname(filePath);
77
- const filename = path.basename(filePath);
78
- try {
79
- assertSafeToDelete(filename, dir);
80
- await fs.unlink(filePath);
81
- } catch (err) {
82
- if (err instanceof Error && err.message.startsWith("Refusing")) {
83
- writeErr(`[config-history] SAFETY: ${err.message}
84
- `);
85
- }
86
- }
87
- }
88
74
  var REDACTED = "[REDACTED]";
89
75
  function maskValue(v) {
90
76
  if (Array.isArray(v)) return v.map(maskValue);
@@ -130,6 +116,31 @@ function reviveSecrets(masked, current) {
130
116
  }
131
117
  return out;
132
118
  }
119
+ var defaultHomeDir = () => os.homedir();
120
+ function activeProfileConfigPath(homeFn) {
121
+ const paths = homeFn === defaultHomeDir ? resolveWstackPaths({ projectRoot: process.cwd() }) : resolveWstackPaths({ projectRoot: process.cwd(), userHome: homeFn() });
122
+ return paths.profileConfig(paths.profileName);
123
+ }
124
+ function configPath(homeFn = defaultHomeDir, targetConfigPath) {
125
+ return targetConfigPath ?? activeProfileConfigPath(homeFn);
126
+ }
127
+
128
+ // src/config-history.ts
129
+ var MAX_CONFIG_BACKUPS = 10;
130
+ var MAX_CONFIG_HISTORY_ENTRIES = 50;
131
+ async function safeDelete(filePath) {
132
+ const dir = path2.dirname(filePath);
133
+ const filename = path2.basename(filePath);
134
+ try {
135
+ assertSafeToDelete(filename, dir);
136
+ await fs2.unlink(filePath);
137
+ } catch (err) {
138
+ if (err instanceof Error && err.message.startsWith("Refusing")) {
139
+ writeErr(`[config-history] SAFETY: ${err.message}
140
+ `);
141
+ }
142
+ }
143
+ }
133
144
  function diffSummary(oldCfg, newCfg) {
134
145
  const changes = [];
135
146
  const allKeys = /* @__PURE__ */ new Set([...Object.keys(oldCfg), ...Object.keys(newCfg)]);
@@ -137,7 +148,7 @@ function diffSummary(oldCfg, newCfg) {
137
148
  const o = JSON.stringify(oldCfg[k]);
138
149
  const n = JSON.stringify(newCfg[k]);
139
150
  if (o !== n) {
140
- if (isSecretField(k)) {
151
+ if (isSecretField2(k)) {
141
152
  changes.push(`${k}: [CHANGED]`);
142
153
  } else if (typeof newCfg[k] !== "object") {
143
154
  changes.push(`${k}: ${oldCfg[k] ?? "(unset)"} \u2192 ${newCfg[k]}`);
@@ -148,24 +159,16 @@ function diffSummary(oldCfg, newCfg) {
148
159
  }
149
160
  return changes.length > 0 ? changes.slice(0, 5).join(", ") : "no changes";
150
161
  }
151
- var defaultHomeDir = () => os.homedir();
152
- function activeProfileConfigPath(homeFn) {
153
- const paths = homeFn === defaultHomeDir ? resolveWstackPaths({ projectRoot: process.cwd() }) : resolveWstackPaths({ projectRoot: process.cwd(), userHome: homeFn() });
154
- return paths.profileConfig(paths.profileName);
155
- }
156
162
  function historyDir(homeFn = defaultHomeDir, targetConfigPath) {
157
- return path.join(path.dirname(configPath(homeFn, targetConfigPath)), "config.history", "entries");
163
+ return path2.join(path2.dirname(configPath(homeFn, targetConfigPath)), "config.history", "entries");
158
164
  }
159
165
  function historyIndexPath(homeFn = defaultHomeDir, targetConfigPath) {
160
- return path.join(
161
- path.dirname(configPath(homeFn, targetConfigPath)),
166
+ return path2.join(
167
+ path2.dirname(configPath(homeFn, targetConfigPath)),
162
168
  "config.history",
163
169
  "index.json"
164
170
  );
165
171
  }
166
- function configPath(homeFn = defaultHomeDir, targetConfigPath) {
167
- return targetConfigPath ?? activeProfileConfigPath(homeFn);
168
- }
169
172
  function backupLastPath(homeFn = defaultHomeDir, targetConfigPath) {
170
173
  return `${configPath(homeFn, targetConfigPath)}.last`;
171
174
  }
@@ -174,11 +177,11 @@ function entryId(ts) {
174
177
  }
175
178
  async function ensureHistoryDir(homeFn = defaultHomeDir, targetConfigPath) {
176
179
  try {
177
- await fs.mkdir(historyDir(homeFn, targetConfigPath), { recursive: true });
180
+ await fs2.mkdir(historyDir(homeFn, targetConfigPath), { recursive: true });
178
181
  } catch (err) {
179
- throw new FsError({
182
+ throw new FsError2({
180
183
  message: toErrorMessage(err),
181
- code: ERROR_CODES.FS_MKDIR_FAILED,
184
+ code: ERROR_CODES2.FS_MKDIR_FAILED,
182
185
  path: historyDir(homeFn, targetConfigPath),
183
186
  cause: err
184
187
  });
@@ -187,7 +190,7 @@ async function ensureHistoryDir(homeFn = defaultHomeDir, targetConfigPath) {
187
190
  async function readIndex(homeFn = defaultHomeDir, targetConfigPath) {
188
191
  let raw;
189
192
  try {
190
- raw = await fs.readFile(historyIndexPath(homeFn, targetConfigPath), "utf8");
193
+ raw = await fs2.readFile(historyIndexPath(homeFn, targetConfigPath), "utf8");
191
194
  } catch (err) {
192
195
  if (err.code !== "ENOENT") throw err;
193
196
  return rebuildIndexFromEntries(homeFn, targetConfigPath);
@@ -208,7 +211,7 @@ async function rebuildIndexFromEntries(homeFn = defaultHomeDir, targetConfigPath
208
211
  const dir = historyDir(homeFn, targetConfigPath);
209
212
  let files;
210
213
  try {
211
- files = (await fs.readdir(dir)).filter((file) => file.endsWith(".json"));
214
+ files = (await fs2.readdir(dir)).filter((file) => file.endsWith(".json"));
212
215
  } catch (err) {
213
216
  if (err.code === "ENOENT") return { version: 1, entries: [] };
214
217
  throw err;
@@ -218,7 +221,7 @@ async function rebuildIndexFromEntries(homeFn = defaultHomeDir, targetConfigPath
218
221
  const id = file.slice(0, -".json".length);
219
222
  let raw;
220
223
  try {
221
- raw = await fs.readFile(path.join(dir, file), "utf8");
224
+ raw = await fs2.readFile(path2.join(dir, file), "utf8");
222
225
  } catch (err) {
223
226
  if (err.code === "ENOENT") continue;
224
227
  entries.push({ id, timestamp: "", description: "" });
@@ -244,9 +247,9 @@ async function writeIndex(idx, homeFn = defaultHomeDir, targetConfigPath) {
244
247
  try {
245
248
  await atomicWrite(historyIndexPath(homeFn, targetConfigPath), JSON.stringify(idx, null, 2));
246
249
  } catch (err) {
247
- throw new FsError({
250
+ throw new FsError2({
248
251
  message: toErrorMessage(err),
249
- code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
252
+ code: ERROR_CODES2.FS_ATOMIC_WRITE_FAILED,
250
253
  path: historyIndexPath(homeFn, targetConfigPath),
251
254
  cause: err
252
255
  });
@@ -258,16 +261,16 @@ async function pruneHistoryEntries(idx, homeFn = defaultHomeDir, targetConfigPat
258
261
  const dir = historyDir(homeFn, targetConfigPath);
259
262
  for (const entry of removed) {
260
263
  try {
261
- await fs.unlink(path.join(dir, `${entry.id}.json`));
264
+ await fs2.unlink(path2.join(dir, `${entry.id}.json`));
262
265
  } catch {
263
266
  }
264
267
  }
265
268
  try {
266
- const files = await fs.readdir(dir);
269
+ const files = await fs2.readdir(dir);
267
270
  await Promise.all(
268
271
  files.filter((file) => file.endsWith(".json") && !keep.has(file)).map(async (file) => {
269
272
  try {
270
- await fs.unlink(path.join(dir, file));
273
+ await fs2.unlink(path2.join(dir, file));
271
274
  } catch {
272
275
  }
273
276
  })
@@ -281,7 +284,7 @@ async function backupCurrent(homeFn = defaultHomeDir, targetConfigPath) {
281
284
  const ts = Date.now();
282
285
  let content;
283
286
  try {
284
- content = await fs.readFile(cfg, "utf8");
287
+ content = await fs2.readFile(cfg, "utf8");
285
288
  } catch {
286
289
  }
287
290
  if (content !== void 0) {
@@ -300,11 +303,11 @@ async function backupCurrent(homeFn = defaultHomeDir, targetConfigPath) {
300
303
  }
301
304
  }
302
305
  try {
303
- const dir = path.dirname(cfg);
304
- const files = await fs.readdir(dir);
306
+ const dir = path2.dirname(cfg);
307
+ const files = await fs2.readdir(dir);
305
308
  const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
306
309
  for (const f of baks.slice(MAX_CONFIG_BACKUPS)) {
307
- await safeDelete(path.join(dir, f));
310
+ await safeDelete(path2.join(dir, f));
308
311
  }
309
312
  } catch (err) {
310
313
  writeErr(`[config-history] backup prune failed: ${toErrorMessage(err)}`);
@@ -322,16 +325,16 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
322
325
  diffSummary: diffSummary(oldCfg, newCfg)
323
326
  };
324
327
  try {
325
- await fs.writeFile(
326
- path.join(historyDir(homeFn, targetConfigPath), `${id}.json`),
328
+ await fs2.writeFile(
329
+ path2.join(historyDir(homeFn, targetConfigPath), `${id}.json`),
327
330
  JSON.stringify(entry, null, 2),
328
331
  { encoding: "utf8", mode: 384 }
329
332
  );
330
333
  } catch (err) {
331
- throw new FsError({
334
+ throw new FsError2({
332
335
  message: toErrorMessage(err),
333
- code: ERROR_CODES.FS_WRITE_FAILED,
334
- path: path.join(historyDir(homeFn, targetConfigPath), `${id}.json`),
336
+ code: ERROR_CODES2.FS_WRITE_FAILED,
337
+ path: path2.join(historyDir(homeFn, targetConfigPath), `${id}.json`),
335
338
  cause: err
336
339
  });
337
340
  }
@@ -350,10 +353,10 @@ async function listHistory(homeFn = defaultHomeDir, targetConfigPath) {
350
353
  }
351
354
  async function getHistoryEntry(id, homeFn = defaultHomeDir, targetConfigPath) {
352
355
  const dir = historyDir(homeFn, targetConfigPath);
353
- const filePath = path.join(dir, `${id}.json`);
354
- if (path.dirname(path.resolve(filePath)) !== path.resolve(dir)) return null;
356
+ const filePath = path2.join(dir, `${id}.json`);
357
+ if (path2.dirname(path2.resolve(filePath)) !== path2.resolve(dir)) return null;
355
358
  try {
356
- const raw = await fs.readFile(filePath, "utf8");
359
+ const raw = await fs2.readFile(filePath, "utf8");
357
360
  return JSON.parse(raw);
358
361
  } catch {
359
362
  return null;
@@ -374,7 +377,7 @@ async function restoreFromHistory(id, homeFn = defaultHomeDir, targetConfigPath)
374
377
  await backupCurrent(homeFn, targetConfigPath);
375
378
  let oldCfg = {};
376
379
  try {
377
- const raw = await fs.readFile(cfg, "utf8");
380
+ const raw = await fs2.readFile(cfg, "utf8");
378
381
  oldCfg = JSON.parse(raw);
379
382
  } catch {
380
383
  }
@@ -399,7 +402,7 @@ async function restoreLast(homeFn = defaultHomeDir, targetConfigPath) {
399
402
  const cfg = configPath(homeFn, targetConfigPath);
400
403
  let lastCfg = {};
401
404
  try {
402
- const raw = await fs.readFile(last, "utf8");
405
+ const raw = await fs2.readFile(last, "utf8");
403
406
  lastCfg = JSON.parse(raw);
404
407
  } catch {
405
408
  return { ok: false, error: "No prior backup found" };
@@ -410,7 +413,7 @@ async function restoreLast(homeFn = defaultHomeDir, targetConfigPath) {
410
413
  return withFileLock(cfg, async () => {
411
414
  let oldCfg = {};
412
415
  try {
413
- const raw = await fs.readFile(cfg, "utf8");
416
+ const raw = await fs2.readFile(cfg, "utf8");
414
417
  oldCfg = JSON.parse(raw);
415
418
  } catch {
416
419
  }
@@ -439,4 +442,4 @@ export {
439
442
  restoreFromHistory,
440
443
  restoreLast
441
444
  };
442
- //# sourceMappingURL=chunk-BIGSVBPI.js.map
445
+ //# sourceMappingURL=chunk-KNYIEQAY.js.map
@@ -47,4 +47,4 @@ export {
47
47
  trackAcpLeader,
48
48
  closeAcpLeaderSessions
49
49
  };
50
- //# sourceMappingURL=chunk-GKGWPFW2.js.map
50
+ //# sourceMappingURL=chunk-LUEO5HLZ.js.map
@@ -0,0 +1,252 @@
1
+
2
+ // src/tool-gateway.ts
3
+ import { Context } from "@wrongstack/core/agent";
4
+ import { ToolExecutor } from "@wrongstack/core/execution";
5
+ import { ToolRegistry } from "@wrongstack/core/registry";
6
+ import {
7
+ AutoApprovePermissionPolicy,
8
+ DefaultSecretScrubber,
9
+ ToolCapabilities
10
+ } from "@wrongstack/core/security";
11
+ import { normalizeTokenSavingTier } from "@wrongstack/core/types";
12
+ import { toErrorMessage } from "@wrongstack/core/utils";
13
+ import { systemSessionId } from "@wrongstack/primitives";
14
+ import { wireKanbanPorts } from "@wrongstack/runtime";
15
+ import { BROWSER_TOOL_NAMES } from "@wrongstack/tools/browser";
16
+ import { runStartupIndex, shutdownCodebaseIndexHost } from "@wrongstack/tools/codebase-index";
17
+ import { registerBuiltinToolTier } from "@wrongstack/tools/tool-tier";
18
+ function resolveGatewayRegistry(existing, config) {
19
+ wireKanbanPorts();
20
+ if (existing) return existing;
21
+ const registry = new ToolRegistry();
22
+ registerBuiltinToolTier({
23
+ registry,
24
+ tier: normalizeTokenSavingTier(config.features.tokenSavingMode)
25
+ });
26
+ return registry;
27
+ }
28
+ var READ_ONLY_CAPABILITIES = [
29
+ ToolCapabilities.FS_READ,
30
+ ToolCapabilities.NET_OUTBOUND
31
+ ];
32
+ function parseCapabilityGrants(raw) {
33
+ if (raw === void 0 || raw === false) return [];
34
+ if (raw === true || raw.trim() === "") {
35
+ throw new Error(
36
+ `--allow needs a comma-separated capability list, e.g. --allow fs.write (known: ${knownCapabilities()})`
37
+ );
38
+ }
39
+ const known = new Set(Object.values(ToolCapabilities));
40
+ const grants = raw.split(",").map((s) => s.trim()).filter(Boolean);
41
+ const unknown = grants.filter((g) => !known.has(g));
42
+ if (unknown.length > 0) {
43
+ throw new Error(
44
+ `--allow: unknown capability ${unknown.map((u) => `"${u}"`).join(", ")} (known: ${knownCapabilities()})`
45
+ );
46
+ }
47
+ return [...new Set(grants)];
48
+ }
49
+ function knownCapabilities() {
50
+ return Object.values(ToolCapabilities).join(", ");
51
+ }
52
+ function yoloServePolicy(yoloPlus) {
53
+ return new AutoApprovePermissionPolicy(
54
+ Object.values(ToolCapabilities),
55
+ yoloPlus ? { yoloPlus } : void 0
56
+ );
57
+ }
58
+ function resolveServeFsRestriction(config) {
59
+ return config.tools?.restrictToProjectRoot ?? true;
60
+ }
61
+ var GATEWAY_SESSION_ID = systemSessionId("tool-gateway");
62
+ function makeServeContext(cwd, projectRoot, signal, restrictFsToRoot = true) {
63
+ const provider = {
64
+ id: "mcp-serve",
65
+ capabilities: { maxContext: 0 },
66
+ complete: async () => {
67
+ throw new Error("no model provider in tool-gateway mode");
68
+ },
69
+ stream: () => {
70
+ throw new Error("no model provider in tool-gateway mode");
71
+ }
72
+ };
73
+ const session = {
74
+ id: GATEWAY_SESSION_ID,
75
+ append: async () => {
76
+ }
77
+ };
78
+ const tokenCounter = {
79
+ account: () => {
80
+ },
81
+ total: () => ({ input: 0, output: 0 }),
82
+ estimateCost: () => ({ total: 0 })
83
+ };
84
+ return new Context({
85
+ systemPrompt: [],
86
+ provider,
87
+ session,
88
+ signal,
89
+ tokenCounter,
90
+ cwd,
91
+ projectRoot,
92
+ allowOutsideProjectRoot: !restrictFsToRoot,
93
+ model: "mcp-serve",
94
+ tools: []
95
+ });
96
+ }
97
+ async function selectExposedTools(registry, ctx, policy, whitelist) {
98
+ const allowed = [];
99
+ for (const tool of registry.list()) {
100
+ if (whitelist && !whitelist.has(tool.name)) continue;
101
+ const decision = await policy.evaluate(tool, {}, ctx);
102
+ if (decision.permission === "auto") allowed.push(tool);
103
+ }
104
+ return allowed;
105
+ }
106
+ var SESSION_BOUND_TOOL_NAMES = /* @__PURE__ */ new Set([
107
+ "clarify",
108
+ "tool_use",
109
+ "tool_script",
110
+ "tool_search",
111
+ "present_artifact",
112
+ "todo",
113
+ "plan"
114
+ ]);
115
+ var PROCESS_BOUND_TOOL_NAMES = new Set(BROWSER_TOOL_NAMES);
116
+ var INDEX_READING_TOOL_NAMES = /* @__PURE__ */ new Set([
117
+ "codebase-search",
118
+ "codebase-context",
119
+ "codebase-impact-analysis",
120
+ "codebase-incoming-calls",
121
+ "codebase-outgoing-calls",
122
+ "codebase-repo-map",
123
+ "codebase-stats"
124
+ ]);
125
+ var REMOTE_CATCH_UP_INTERVAL_MS = 1e3;
126
+ function readsCodebaseIndex(name) {
127
+ return INDEX_READING_TOOL_NAMES.has(name);
128
+ }
129
+ function oneShotWithholdReason(name) {
130
+ if (SESSION_BOUND_TOOL_NAMES.has(name)) return "session";
131
+ if (PROCESS_BOUND_TOOL_NAMES.has(name)) return "process";
132
+ return void 0;
133
+ }
134
+ function withheldReason(gateway, registry, name) {
135
+ const bound = gateway.withheldBy(name);
136
+ if (bound === "session") {
137
+ return `Tool "${name}" only works inside an agent session and is not exposed here.`;
138
+ }
139
+ if (bound === "process") {
140
+ return `Tool "${name}" keeps its state in a long-lived process; use \`wstack mcp serve\` or \`wstack tools serve\` for it.`;
141
+ }
142
+ const tool = registry.get(name);
143
+ if (!tool) return `Unknown tool "${name}". List the exposed tools first.`;
144
+ const missing = (tool.capabilities ?? []).filter((c) => !gateway.capabilities.includes(c));
145
+ return gateway.mode === "yolo" || missing.length === 0 ? `Tool "${name}" is withheld by policy.` : `Tool "${name}" is withheld: it needs ${missing.join(", ")}. Pass --allow ${missing.join(",")} (or --yolo for every capability); deleting outside the project, git-history and download-and-run commands stay refused.`;
146
+ }
147
+ async function createToolGateway(options) {
148
+ const { registry, config, cwd, projectRoot } = options;
149
+ const restrictFsToRoot = resolveServeFsRestriction(config);
150
+ const controller = new AbortController();
151
+ const grants = options.yolo ? [] : [...new Set(options.allow ?? [])];
152
+ const mode = options.yolo ? "yolo" : grants.length > 0 ? "granted" : "safe";
153
+ const capabilities = options.yolo ? Object.values(ToolCapabilities) : [.../* @__PURE__ */ new Set([...READ_ONLY_CAPABILITIES, ...grants])];
154
+ const permissionPolicy = options.yolo ? yoloServePolicy(options.yoloPlus) : new AutoApprovePermissionPolicy(capabilities);
155
+ const executor = new ToolExecutor(registry, {
156
+ permissionPolicy,
157
+ secretScrubber: new DefaultSecretScrubber(),
158
+ maxToolTimeoutMs: config.tools?.maxToolTimeoutMs ?? 3e5,
159
+ perIterationOutputCapBytes: 1e6,
160
+ // Kanban tracks work; it does not gate it. Off unless the operator opts in.
161
+ // See wiring/pipeline.ts for why all four hosts must agree.
162
+ requireKanbanGovernance: config.tools?.kanbanGovernance ?? false
163
+ });
164
+ const selectionCtx = makeServeContext(cwd, projectRoot, controller.signal, restrictFsToRoot);
165
+ const selected = await selectExposedTools(
166
+ registry,
167
+ selectionCtx,
168
+ permissionPolicy,
169
+ options.whitelist
170
+ );
171
+ const withheld = (name) => {
172
+ const reason = oneShotWithholdReason(name);
173
+ if (options.oneShot) return reason !== void 0;
174
+ if (options.remoteAgent) return reason === "session";
175
+ return false;
176
+ };
177
+ const tools = selected.filter((t) => !withheld(t.name));
178
+ const byName = new Map(tools.map((t) => [t.name, t]));
179
+ const catchUpIndex = options.catchUpIndex ?? (async (root, signal) => {
180
+ await runStartupIndex({ projectRoot: root, signal });
181
+ });
182
+ let indexCurrent = false;
183
+ let lastCatchUpAt = 0;
184
+ let catchUpInFlight;
185
+ const runCatchUp = async () => {
186
+ try {
187
+ await catchUpIndex(projectRoot, controller.signal);
188
+ } catch (err) {
189
+ options.onWarning?.(`codebase index catch-up failed: ${toErrorMessage(err)}`);
190
+ } finally {
191
+ indexCurrent = true;
192
+ lastCatchUpAt = Date.now();
193
+ catchUpInFlight = void 0;
194
+ }
195
+ };
196
+ const indexNeedsCatchUp = () => {
197
+ if (options.oneShot) return !indexCurrent;
198
+ if (options.remoteAgent) return Date.now() - lastCatchUpAt >= REMOTE_CATCH_UP_INTERVAL_MS;
199
+ return false;
200
+ };
201
+ let counter = 0;
202
+ return {
203
+ mode,
204
+ capabilities,
205
+ projectRoot,
206
+ tools,
207
+ get: (name) => byName.get(name),
208
+ withheldBy: (name) => withheld(name) ? oneShotWithholdReason(name) : void 0,
209
+ async call(name, input, callOptions) {
210
+ const tool = byName.get(name);
211
+ if (!tool) return { kind: "not_exposed" };
212
+ if (readsCodebaseIndex(name) && (catchUpInFlight || indexNeedsCatchUp())) {
213
+ catchUpInFlight ??= runCatchUp();
214
+ await catchUpInFlight;
215
+ }
216
+ if (tool.mutating) indexCurrent = false;
217
+ const use = { type: "tool_use", id: `srv_${++counter}`, name, input };
218
+ const requestCtx = makeServeContext(
219
+ callOptions?.cwd ?? cwd,
220
+ projectRoot,
221
+ controller.signal,
222
+ restrictFsToRoot
223
+ );
224
+ const batch = await executor.executeBatch([use], requestCtx, "sequential");
225
+ const output = batch.outputs[0];
226
+ const result = output?.result;
227
+ if (!output || !result || result.type === "tool_confirm_pending") {
228
+ return { kind: "confirm_required" };
229
+ }
230
+ const isError = Boolean(result.is_error);
231
+ return {
232
+ kind: "result",
233
+ content: result.content,
234
+ isError,
235
+ settlement: output.settlement ?? (isError ? "failed" : "completed"),
236
+ durationMs: output.durationMs
237
+ };
238
+ },
239
+ async close() {
240
+ controller.abort();
241
+ await shutdownCodebaseIndexHost();
242
+ }
243
+ };
244
+ }
245
+
246
+ export {
247
+ resolveGatewayRegistry,
248
+ parseCapabilityGrants,
249
+ withheldReason,
250
+ createToolGateway
251
+ };
252
+ //# sourceMappingURL=chunk-LYWOV4OX.js.map