@wrongstack/core 0.9.20 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +37 -33
  2. package/dist/{agent-bridge-Dti3KXGk.d.ts → agent-bridge-DBBGP5O4.d.ts} +1 -1
  3. package/dist/{agent-subagent-runner-U-rs7kk7.d.ts → agent-subagent-runner-KwufTWwn.d.ts} +3 -3
  4. package/dist/{compactor-D7X96RLZ.d.ts → compactor-D1RHFRmF.d.ts} +1 -1
  5. package/dist/{config-CLXMDOSs.d.ts → config-CJXBka2r.d.ts} +38 -2
  6. package/dist/{context-zkZeILpr.d.ts → context-7u93AcGD.d.ts} +94 -0
  7. package/dist/coordination/index.d.ts +12 -12
  8. package/dist/coordination/index.js +130 -58
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/{default-config-Uzla85H-.d.ts → default-config-DEXI4jsl.d.ts} +10 -1
  11. package/dist/defaults/index.d.ts +24 -24
  12. package/dist/defaults/index.js +611 -95
  13. package/dist/defaults/index.js.map +1 -1
  14. package/dist/{events-DH-9r-_C.d.ts → events-Bw1K5E6K.d.ts} +8 -1
  15. package/dist/execution/index.d.ts +18 -15
  16. package/dist/execution/index.js +309 -61
  17. package/dist/execution/index.js.map +1 -1
  18. package/dist/extension/index.d.ts +6 -6
  19. package/dist/extension/index.js.map +1 -1
  20. package/dist/goal-store-BeRsj7YX.d.ts +188 -0
  21. package/dist/{index-CFO9QmJo.d.ts → index-DIt5TIaF.d.ts} +8 -8
  22. package/dist/{index-BIHJ4uII.d.ts → index-DgQWnZxP.d.ts} +5 -5
  23. package/dist/index.d.ts +56 -35
  24. package/dist/index.js +742 -203
  25. package/dist/index.js.map +1 -1
  26. package/dist/infrastructure/index.d.ts +6 -6
  27. package/dist/kernel/index.d.ts +9 -9
  28. package/dist/kernel/index.js +57 -8
  29. package/dist/kernel/index.js.map +1 -1
  30. package/dist/{mcp-servers-DkESgh0G.d.ts → mcp-servers-BgINZzuo.d.ts} +3 -3
  31. package/dist/models/index.d.ts +3 -3
  32. package/dist/models/index.js +163 -10
  33. package/dist/models/index.js.map +1 -1
  34. package/dist/models-registry-gwMAo6E3.d.ts +86 -0
  35. package/dist/{multi-agent-DNp6lAzg.d.ts → multi-agent-C1O8EFzb.d.ts} +45 -4
  36. package/dist/{multi-agent-coordinator-CAhsegPz.d.ts → multi-agent-coordinator-DoVSBFNx.d.ts} +2 -2
  37. package/dist/{null-fleet-bus-Dnl19vmf.d.ts → null-fleet-bus-T-s3JaOt.d.ts} +8 -6
  38. package/dist/observability/index.d.ts +2 -2
  39. package/dist/{path-resolver-CHiBL0DD.d.ts → path-resolver-BfyK-I7w.d.ts} +2 -2
  40. package/dist/{permission-H35s9Evv.d.ts → permission-B6sldrSp.d.ts} +1 -1
  41. package/dist/{permission-policy-CT-nRmTn.d.ts → permission-policy-CtNscWOA.d.ts} +10 -9
  42. package/dist/{plan-templates-Bs8iRwi6.d.ts → plan-templates-BWs6Je7j.d.ts} +8 -8
  43. package/dist/{provider-runner-BZdDrWrS.d.ts → provider-runner-BmPe2RuU.d.ts} +3 -3
  44. package/dist/{retry-policy-J9N_PM40.d.ts → retry-policy-KF18W4dg.d.ts} +1 -1
  45. package/dist/sdd/index.d.ts +8 -8
  46. package/dist/sdd/index.js +130 -58
  47. package/dist/sdd/index.js.map +1 -1
  48. package/dist/security/index.d.ts +65 -4
  49. package/dist/security/index.js +67 -19
  50. package/dist/security/index.js.map +1 -1
  51. package/dist/{selector-CFTh3Z6p.d.ts → selector-DmXxpFyM.d.ts} +1 -1
  52. package/dist/{session-reader-C7JJlxOw.d.ts → session-reader-CCSBfQmd.d.ts} +1 -1
  53. package/dist/storage/index.d.ts +7 -7
  54. package/dist/storage/index.js +125 -6
  55. package/dist/storage/index.js.map +1 -1
  56. package/dist/{system-prompt-CneIxVip.d.ts → system-prompt-CM6zOhd2.d.ts} +19 -1
  57. package/dist/{tool-executor-flTuxsqO.d.ts → tool-executor-D7RxLtg4.d.ts} +4 -4
  58. package/dist/types/index.d.ts +16 -16
  59. package/dist/types/index.js +344 -14
  60. package/dist/types/index.js.map +1 -1
  61. package/dist/utils/index.d.ts +23 -3
  62. package/dist/utils/index.js +66 -1
  63. package/dist/utils/index.js.map +1 -1
  64. package/dist/{wstack-paths-BCgmTNlG.d.ts → wstack-paths-eMXnY1_X.d.ts} +8 -0
  65. package/package.json +1 -1
  66. package/dist/goal-store-C7jcumEh.d.ts +0 -96
  67. package/dist/models-registry-OG_30xqZ.d.ts +0 -46
package/dist/index.js CHANGED
@@ -142,8 +142,10 @@ var ERROR_CODES = {
142
142
  // Container / Registry
143
143
  CONTAINER_TOKEN_ALREADY_BOUND: "CONTAINER_TOKEN_ALREADY_BOUND",
144
144
  CONTAINER_TOKEN_NOT_BOUND: "CONTAINER_TOKEN_NOT_BOUND",
145
+ CONTAINER_CIRCULAR_DEPENDENCY: "CONTAINER_CIRCULAR_DEPENDENCY",
145
146
  REGISTRY_DUPLICATE: "REGISTRY_DUPLICATE",
146
147
  REGISTRY_NOT_FOUND: "REGISTRY_NOT_FOUND",
148
+ REGISTRY_INVALID: "REGISTRY_INVALID",
147
149
  // File system
148
150
  FS_READ_FAILED: "FS_READ_FAILED",
149
151
  FS_WRITE_FAILED: "FS_WRITE_FAILED",
@@ -306,6 +308,16 @@ function isFsError(err) {
306
308
  // src/kernel/container.ts
307
309
  var Container = class {
308
310
  entries = /* @__PURE__ */ new Map();
311
+ /**
312
+ * Tokens currently mid-resolve. Tracked so we can detect circular
313
+ * dependencies (A → B → A) and throw a structured error instead of
314
+ * overflowing the call stack with "Maximum call stack size exceeded".
315
+ *
316
+ * Not a memoization cache — the per-entry `cache` field is the source
317
+ * of truth for "have I built this before?". This set only lives for
318
+ * the duration of a single resolve call.
319
+ */
320
+ resolving = /* @__PURE__ */ new Set();
309
321
  bind(token, factory, opts = {}) {
310
322
  if (this.entries.has(token)) {
311
323
  throw new WrongStackError({
@@ -366,14 +378,43 @@ var Container = class {
366
378
  if (entry.singleton && entry.cache !== void 0) {
367
379
  return entry.cache;
368
380
  }
369
- let value = entry.factory(this);
370
- for (const d of entry.decorators) {
371
- value = d(value, this);
381
+ if (this.resolving.has(token)) {
382
+ const cycle = this.describeCycle(token);
383
+ throw new WrongStackError({
384
+ message: `Container: circular dependency detected \u2014 ${cycle}`,
385
+ code: ERROR_CODES.CONTAINER_CIRCULAR_DEPENDENCY,
386
+ subsystem: "container",
387
+ context: { token: token.description, cycle }
388
+ });
389
+ }
390
+ this.resolving.add(token);
391
+ try {
392
+ let value = entry.factory(this);
393
+ for (const d of entry.decorators) {
394
+ value = d(value, this);
395
+ }
396
+ if (entry.singleton) {
397
+ entry.cache = value;
398
+ }
399
+ return value;
400
+ } finally {
401
+ this.resolving.delete(token);
372
402
  }
373
- if (entry.singleton) {
374
- entry.cache = value;
403
+ }
404
+ /**
405
+ * Build a human-readable description of the dependency cycle that
406
+ * caused the resolution to re-enter. Lists the tokens in the order
407
+ * they were entered, then appends the re-entered token to close the
408
+ * loop. Falls back to a generic message if the resolving set is
409
+ * somehow empty (shouldn't happen, but defensive).
410
+ */
411
+ describeCycle(reentry) {
412
+ const descs = [];
413
+ for (const t2 of this.resolving) {
414
+ descs.push(t2.description ?? "unknown");
375
415
  }
376
- return value;
416
+ descs.push(reentry.description ?? "unknown");
417
+ return descs.join(" \u2192 ");
377
418
  }
378
419
  has(token) {
379
420
  return this.entries.has(token);
@@ -644,7 +685,8 @@ var EventBus = class {
644
685
  }
645
686
  if (this.wildcards.length > 0) {
646
687
  const name = event;
647
- for (const { match, fn } of this.wildcards) {
688
+ const snapshot = this.wildcards.slice();
689
+ for (const { match, fn } of snapshot) {
648
690
  if (!match(name)) continue;
649
691
  try {
650
692
  fn(name, payload);
@@ -715,11 +757,19 @@ var ScopedEventBus = class extends EventBus {
715
757
  * Uses EventBus's public API directly to avoid triggering our own `on()`
716
758
  * override (which would consume a key slot for the wrapper, then orphan
717
759
  * our registration entry under a different key).
760
+ *
761
+ * When the wrapper fires, it cleans up BOTH the underlying EventBus
762
+ * listener AND the tracking entry — so `scopedListenerCount` returns to
763
+ * its pre-`once()` value without requiring the caller to invoke the
764
+ * returned unsubscribe. The returned `unsub` is still safe to call
765
+ * after auto-removal (its delete is a no-op and its off() finds
766
+ * nothing to remove).
718
767
  */
719
768
  once(event, fn) {
720
769
  const key = this.nextKey++;
721
770
  const wrapper = (payload) => {
722
771
  EventBus.prototype.off.call(this, event, wrapper);
772
+ this.registrations.delete(key);
723
773
  fn(payload);
724
774
  };
725
775
  EventBus.prototype.on.call(this, event, wrapper);
@@ -1024,6 +1074,14 @@ var DEFAULT_CONTEXT_CONFIG = Object.freeze({
1024
1074
  var DEFAULT_AUTONOMY_CONFIG = Object.freeze({
1025
1075
  autoProceedDelayMs: 45e3
1026
1076
  });
1077
+ var DEFAULT_SESSION_LOGGING_CONFIG = Object.freeze({
1078
+ auditLevel: "standard",
1079
+ sampling: {
1080
+ toolProgress: {
1081
+ sampleRate: 8
1082
+ }
1083
+ }
1084
+ });
1027
1085
 
1028
1086
  // src/types/secret-vault.ts
1029
1087
  var ENCRYPTED_PREFIX = "enc:v1:";
@@ -2140,6 +2198,71 @@ var DefaultSecretScrubber = class {
2140
2198
 
2141
2199
  // src/models/models-registry.ts
2142
2200
  init_atomic_write();
2201
+
2202
+ // src/utils/merge-models-payload.ts
2203
+ function mergeModelsPayload(base, overlay) {
2204
+ const out = {};
2205
+ for (const [id, provider] of Object.entries(base)) {
2206
+ out[id] = cloneProvider(provider);
2207
+ }
2208
+ for (const [id, ovProvider] of Object.entries(overlay)) {
2209
+ const existing = out[id];
2210
+ out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
2211
+ }
2212
+ return out;
2213
+ }
2214
+ function mergeProvider(base, overlay) {
2215
+ const models = {};
2216
+ for (const [mid, m] of Object.entries(base.models ?? {})) {
2217
+ models[mid] = { ...m };
2218
+ }
2219
+ for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
2220
+ const existing = models[mid];
2221
+ models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
2222
+ }
2223
+ return {
2224
+ ...base,
2225
+ // Overlay scalar fields win when explicitly provided; otherwise keep base.
2226
+ ...stripUndefined({
2227
+ id: overlay.id,
2228
+ name: overlay.name,
2229
+ npm: overlay.npm,
2230
+ api: overlay.api,
2231
+ env: overlay.env,
2232
+ doc: overlay.doc
2233
+ }),
2234
+ models
2235
+ };
2236
+ }
2237
+ function mergeModel(base, overlay) {
2238
+ const merged = { ...base, ...overlay };
2239
+ if (base.limit || overlay.limit) {
2240
+ merged.limit = { ...base.limit, ...overlay.limit };
2241
+ }
2242
+ if (base.cost || overlay.cost) {
2243
+ merged.cost = { ...base.cost, ...overlay.cost };
2244
+ }
2245
+ if (base.modalities || overlay.modalities) {
2246
+ merged.modalities = { ...base.modalities, ...overlay.modalities };
2247
+ }
2248
+ return merged;
2249
+ }
2250
+ function cloneProvider(p) {
2251
+ const models = {};
2252
+ for (const [mid, m] of Object.entries(p.models ?? {})) {
2253
+ models[mid] = { ...m };
2254
+ }
2255
+ return { ...p, models };
2256
+ }
2257
+ function stripUndefined(obj) {
2258
+ const out = {};
2259
+ for (const [k, v] of Object.entries(obj)) {
2260
+ if (v !== void 0) out[k] = v;
2261
+ }
2262
+ return out;
2263
+ }
2264
+
2265
+ // src/models/models-registry.ts
2143
2266
  var DEFAULT_URL = "https://models.dev/api.json";
2144
2267
  var DEFAULT_TTL_SECONDS = 24 * 3600;
2145
2268
  var FAMILY_BY_NPM = {
@@ -2167,7 +2290,10 @@ function classifyFamily(npm) {
2167
2290
  return FAMILY_BY_NPM[npm] ?? "unsupported";
2168
2291
  }
2169
2292
  var DefaultModelsRegistry = class {
2293
+ /** Merged (base + overlay) payload — what every reader sees. */
2170
2294
  payload;
2295
+ /** Memoised overlay payload (in-memory / fetched / file). */
2296
+ overlayPayload;
2171
2297
  fetchedAt;
2172
2298
  cacheFile;
2173
2299
  url;
@@ -2175,6 +2301,10 @@ var DefaultModelsRegistry = class {
2175
2301
  fetchImpl;
2176
2302
  seed;
2177
2303
  maxStaleAgeMs;
2304
+ overlay;
2305
+ overlayUrl;
2306
+ overlayFile;
2307
+ overlayCacheFile;
2178
2308
  constructor(opts) {
2179
2309
  this.cacheFile = opts.cacheFile;
2180
2310
  this.url = opts.url ?? DEFAULT_URL;
@@ -2183,6 +2313,10 @@ var DefaultModelsRegistry = class {
2183
2313
  this.seed = opts.seed;
2184
2314
  const maxStaleSeconds = opts.maxStaleAgeSeconds ?? 7 * 24 * 3600;
2185
2315
  this.maxStaleAgeMs = maxStaleSeconds * 1e3;
2316
+ this.overlay = opts.overlay;
2317
+ this.overlayUrl = opts.overlayUrl;
2318
+ this.overlayFile = opts.overlayFile;
2319
+ this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path6.join(path6.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
2186
2320
  }
2187
2321
  async load(opts = {}) {
2188
2322
  if (this.payload && !opts.force) return this.payload;
@@ -2191,27 +2325,45 @@ var DefaultModelsRegistry = class {
2191
2325
  this.fetchedAt = /* @__PURE__ */ new Date();
2192
2326
  return this.payload;
2193
2327
  }
2328
+ const overlay = await this.loadOverlay(opts);
2329
+ const base = await this.loadBase(opts, Object.keys(overlay).length > 0);
2330
+ this.payload = mergeModelsPayload(base, overlay);
2331
+ return this.payload;
2332
+ }
2333
+ /**
2334
+ * Load the models.dev base payload: fresh cache → network → stale cache.
2335
+ * On total failure, degrade to `{}` (so a non-empty overlay still drives
2336
+ * the catalog) rather than throwing — unless there's no curated overlay to
2337
+ * fall back on, in which case the original error propagates so pure-
2338
+ * models.dev setups still surface the problem.
2339
+ */
2340
+ async loadBase(opts = {}, overlayAvailable = false) {
2194
2341
  if (!opts.force) {
2195
- const cached = await this.readCache();
2342
+ const cached = await this.readCacheAt(this.cacheFile);
2196
2343
  if (cached && this.isFresh(cached.fetchedAt)) {
2197
- this.payload = cached.payload;
2198
2344
  this.fetchedAt = new Date(cached.fetchedAt);
2199
2345
  return cached.payload;
2200
2346
  }
2201
2347
  }
2202
2348
  try {
2203
- return await this.refresh();
2349
+ return await this.refreshBase();
2204
2350
  } catch (err) {
2205
- const cached = await this.readCache();
2351
+ const cached = await this.readCacheAt(this.cacheFile);
2206
2352
  if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
2207
- this.payload = cached.payload;
2208
2353
  this.fetchedAt = new Date(cached.fetchedAt);
2209
2354
  return cached.payload;
2210
2355
  }
2356
+ if (overlayAvailable) {
2357
+ console.warn(
2358
+ `ModelsRegistry: models.dev unavailable (${err instanceof Error ? err.message : String(err)}); serving curated overlay only.`
2359
+ );
2360
+ return {};
2361
+ }
2211
2362
  throw err;
2212
2363
  }
2213
2364
  }
2214
- async refresh() {
2365
+ /** Fetch + cache the models.dev base. Throws on failure (used by `refresh`). */
2366
+ async refreshBase() {
2215
2367
  const res = await this.fetchImpl(this.url, {
2216
2368
  method: "GET",
2217
2369
  headers: { accept: "application/json" }
@@ -2220,7 +2372,6 @@ var DefaultModelsRegistry = class {
2220
2372
  throw new Error(`ModelsRegistry: HTTP ${res.status} fetching ${this.url}`);
2221
2373
  }
2222
2374
  const json = await res.json();
2223
- this.payload = json;
2224
2375
  this.fetchedAt = /* @__PURE__ */ new Date();
2225
2376
  const envelope = {
2226
2377
  fetchedAt: this.fetchedAt.toISOString(),
@@ -2230,6 +2381,68 @@ var DefaultModelsRegistry = class {
2230
2381
  await atomicWrite(this.cacheFile, JSON.stringify(envelope));
2231
2382
  return json;
2232
2383
  }
2384
+ /**
2385
+ * Resolve the curated overlay, memoised. Order: in-memory `overlay` →
2386
+ * fetched `overlayUrl` (cached, same TTL/stale rules) → `overlayFile` on
2387
+ * disk. Never throws — a missing/broken overlay yields `{}`.
2388
+ */
2389
+ async loadOverlay(opts = {}) {
2390
+ if (this.overlayPayload && !opts.force) return this.overlayPayload;
2391
+ if (this.overlay) {
2392
+ this.overlayPayload = this.overlay;
2393
+ return this.overlayPayload;
2394
+ }
2395
+ const fetched = await this.loadOverlayFromUrl(opts);
2396
+ if (fetched) {
2397
+ this.overlayPayload = fetched;
2398
+ return fetched;
2399
+ }
2400
+ const fromFile = await this.readOverlayFile();
2401
+ this.overlayPayload = fromFile ?? {};
2402
+ return this.overlayPayload;
2403
+ }
2404
+ async loadOverlayFromUrl(opts) {
2405
+ if (!this.overlayUrl || !this.overlayCacheFile) return void 0;
2406
+ if (!opts.force) {
2407
+ const cached = await this.readCacheAt(this.overlayCacheFile);
2408
+ if (cached && this.isFresh(cached.fetchedAt)) return cached.payload;
2409
+ }
2410
+ try {
2411
+ const res = await this.fetchImpl(this.overlayUrl, {
2412
+ method: "GET",
2413
+ headers: { accept: "application/json" }
2414
+ });
2415
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
2416
+ const json = await res.json();
2417
+ const envelope = {
2418
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
2419
+ url: this.overlayUrl,
2420
+ payload: json
2421
+ };
2422
+ await atomicWrite(this.overlayCacheFile, JSON.stringify(envelope)).catch(() => {
2423
+ });
2424
+ return json;
2425
+ } catch {
2426
+ const cached = await this.readCacheAt(this.overlayCacheFile);
2427
+ if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) return cached.payload;
2428
+ return void 0;
2429
+ }
2430
+ }
2431
+ async readOverlayFile() {
2432
+ if (!this.overlayFile) return void 0;
2433
+ try {
2434
+ const raw = await fsp2.readFile(this.overlayFile, "utf8");
2435
+ return JSON.parse(raw);
2436
+ } catch {
2437
+ return void 0;
2438
+ }
2439
+ }
2440
+ async refresh() {
2441
+ const base = await this.refreshBase();
2442
+ const overlay = await this.loadOverlay({ force: true });
2443
+ this.payload = mergeModelsPayload(base, overlay);
2444
+ return this.payload;
2445
+ }
2233
2446
  async listProviders() {
2234
2447
  const payload = await this.load();
2235
2448
  return Object.values(payload).map((p) => this.resolveProvider(p));
@@ -2270,7 +2483,7 @@ var DefaultModelsRegistry = class {
2270
2483
  }
2271
2484
  async ageSeconds() {
2272
2485
  if (!this.fetchedAt) {
2273
- const cached = await this.readCache();
2486
+ const cached = await this.readCacheAt(this.cacheFile);
2274
2487
  if (!cached) return Number.POSITIVE_INFINITY;
2275
2488
  return (Date.now() - new Date(cached.fetchedAt).getTime()) / 1e3;
2276
2489
  }
@@ -2294,9 +2507,9 @@ var DefaultModelsRegistry = class {
2294
2507
  isWithinMaxStaleAge(fetchedAtIso) {
2295
2508
  return Date.now() - new Date(fetchedAtIso).getTime() < this.maxStaleAgeMs;
2296
2509
  }
2297
- async readCache() {
2510
+ async readCacheAt(file) {
2298
2511
  try {
2299
- const raw = await fsp2.readFile(this.cacheFile, "utf8");
2512
+ const raw = await fsp2.readFile(file, "utf8");
2300
2513
  return JSON.parse(raw);
2301
2514
  } catch {
2302
2515
  return void 0;
@@ -2868,6 +3081,141 @@ function topologicalSort(graph) {
2868
3081
  return result;
2869
3082
  }
2870
3083
 
3084
+ // src/security/capabilities.ts
3085
+ var ToolCapabilities = {
3086
+ /** Can execute arbitrary commands in the user's shell (the `bash` tool). */
3087
+ SHELL_ARBITRARY: "shell.arbitrary",
3088
+ /** Can write / modify / delete files inside the project. */
3089
+ FS_WRITE: "fs.write",
3090
+ /** Can write files outside the current project root (very high risk). */
3091
+ FS_WRITE_OUTSIDE_PROJECT: "fs.write.outside-project",
3092
+ /** Proxies tools from external MCP servers (unknown capability). */
3093
+ MCP_PROXY: "mcp.proxy",
3094
+ /** Can spawn or manage subagents / multi-agent tasks. */
3095
+ SUBAGENT_SPAWN: "subagent.spawn",
3096
+ /** Can mutate global or session configuration / trust state. */
3097
+ CONFIG_MUTATE: "config.mutate",
3098
+ /** Can install packages or run package managers with side effects. */
3099
+ PACKAGE_INSTALL: "package.install"
3100
+ };
3101
+ var DANGEROUS_FOR_SUBAGENTS = [
3102
+ ToolCapabilities.SHELL_ARBITRARY,
3103
+ ToolCapabilities.FS_WRITE,
3104
+ ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
3105
+ ToolCapabilities.MCP_PROXY,
3106
+ ToolCapabilities.SUBAGENT_SPAWN,
3107
+ ToolCapabilities.CONFIG_MUTATE,
3108
+ ToolCapabilities.PACKAGE_INSTALL
3109
+ ];
3110
+ function hasDangerousCapabilityForSubagents(toolOrCaps) {
3111
+ if (!toolOrCaps) return false;
3112
+ const input = toolOrCaps;
3113
+ const caps = Array.isArray(input) ? input : input?.capabilities ?? [];
3114
+ return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
3115
+ }
3116
+ function getDangerousCapabilities(toolOrCaps) {
3117
+ if (!toolOrCaps) return [];
3118
+ const input = toolOrCaps;
3119
+ const caps = Array.isArray(input) ? input : input?.capabilities ?? [];
3120
+ return caps.filter(
3121
+ (c) => DANGEROUS_FOR_SUBAGENTS.includes(c)
3122
+ );
3123
+ }
3124
+
3125
+ // src/utils/json-schema-validate.ts
3126
+ function validateAgainstSchema(value, schema) {
3127
+ const errors = [];
3128
+ walk2(value, schema, "", errors);
3129
+ return { ok: errors.length === 0, errors };
3130
+ }
3131
+ function walk2(value, schema, path35, errors) {
3132
+ if (schema.enum !== void 0) {
3133
+ if (!schema.enum.some((e) => deepEqual(e, value))) {
3134
+ errors.push({
3135
+ path: path35 || "<root>",
3136
+ message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
3137
+ });
3138
+ return;
3139
+ }
3140
+ }
3141
+ if (typeof schema.type === "string") {
3142
+ if (!checkType(value, schema.type)) {
3143
+ errors.push({
3144
+ path: path35 || "<root>",
3145
+ message: `expected ${schema.type}, got ${describeType(value)}`
3146
+ });
3147
+ return;
3148
+ }
3149
+ }
3150
+ if (schema.type === "object" && isPlainObject(value)) {
3151
+ const obj = value;
3152
+ for (const req of schema.required ?? []) {
3153
+ if (!(req in obj)) {
3154
+ errors.push({ path: joinPath(path35, req), message: "required property missing" });
3155
+ }
3156
+ }
3157
+ if (schema.properties) {
3158
+ for (const [key, subSchema] of Object.entries(schema.properties)) {
3159
+ if (key in obj) {
3160
+ walk2(obj[key], subSchema, joinPath(path35, key), errors);
3161
+ }
3162
+ }
3163
+ }
3164
+ }
3165
+ if (schema.type === "array" && Array.isArray(value) && schema.items) {
3166
+ value.forEach((item, i) => walk2(item, schema.items, `${path35}[${i}]`, errors));
3167
+ }
3168
+ }
3169
+ function checkType(value, type) {
3170
+ switch (type) {
3171
+ case "string":
3172
+ return typeof value === "string";
3173
+ case "number":
3174
+ return typeof value === "number" && !Number.isNaN(value);
3175
+ case "integer":
3176
+ return typeof value === "number" && Number.isInteger(value);
3177
+ case "boolean":
3178
+ return typeof value === "boolean";
3179
+ case "null":
3180
+ return value === null;
3181
+ case "array":
3182
+ return Array.isArray(value);
3183
+ case "object":
3184
+ return isPlainObject(value);
3185
+ default:
3186
+ return true;
3187
+ }
3188
+ }
3189
+ function isPlainObject(v) {
3190
+ return typeof v === "object" && v !== null && !Array.isArray(v);
3191
+ }
3192
+ function describeType(v) {
3193
+ if (v === null) return "null";
3194
+ if (Array.isArray(v)) return "array";
3195
+ return typeof v;
3196
+ }
3197
+ function joinPath(parent, key) {
3198
+ if (!parent) return key;
3199
+ return `${parent}.${key}`;
3200
+ }
3201
+ function deepEqual(a, b) {
3202
+ if (a === b) return true;
3203
+ if (typeof a !== typeof b) return false;
3204
+ if (a === null || b === null) return a === b;
3205
+ if (Array.isArray(a) && Array.isArray(b)) {
3206
+ return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
3207
+ }
3208
+ if (typeof a === "object" && typeof b === "object") {
3209
+ const ak = Object.keys(a);
3210
+ const bk = Object.keys(b);
3211
+ if (ak.length !== bk.length) return false;
3212
+ return ak.every(
3213
+ (k) => deepEqual(a[k], b[k])
3214
+ );
3215
+ }
3216
+ return false;
3217
+ }
3218
+
2871
3219
  // src/utils/tool-output-serializer.ts
2872
3220
  function createToolOutputSerializer(opts = {}) {
2873
3221
  const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
@@ -2949,18 +3297,45 @@ var ToolExecutor = class {
2949
3297
  budget = this.decrementBudget(result, budget);
2950
3298
  return { result, tool, durationMs: Date.now() - start };
2951
3299
  }
3300
+ const validation = validateAgainstSchema(use.input, tool.inputSchema);
3301
+ if (!validation.ok) {
3302
+ const errorDetails = validation.errors.map((e) => ` - ${e.path || "input"}: ${e.message}`).join("\n");
3303
+ const result = {
3304
+ type: "tool_result",
3305
+ tool_use_id: use.id,
3306
+ content: `Invalid arguments for tool "${tool.name}".
3307
+
3308
+ Validation errors:
3309
+ ${errorDetails}
3310
+
3311
+ Please call the tool again with arguments that match its inputSchema. You can use the "tool-help" tool with name="${tool.name}" to see the exact expected schema.`,
3312
+ is_error: true
3313
+ };
3314
+ budget = this.decrementBudget(result, budget);
3315
+ return { result, tool, durationMs: Date.now() - start };
3316
+ }
3317
+ const toolDangerousCaps = getDangerousCapabilities(tool);
3318
+ if (toolDangerousCaps.length > 0) {
3319
+ if (this.opts.events) ;
3320
+ }
2952
3321
  if (hasMalformedArguments(use.input)) {
2953
3322
  const result = this.malformedInputResult(use, extractMalformedRaw(use.input));
2954
3323
  budget = this.decrementBudget(result, budget);
2955
3324
  return { result, tool, durationMs: Date.now() - start };
2956
3325
  }
2957
3326
  const decision = await this.opts.permissionPolicy.evaluate(tool, use.input, ctx);
2958
- if (decision.permission === "deny") {
3327
+ let effectivePermission = decision.permission;
3328
+ const policy = this.opts.permissionPolicy;
3329
+ const yolo = policy.getYolo?.() === true || policy.getForceAllYolo?.() === true;
3330
+ if (toolDangerousCaps.length > 0 && effectivePermission === "auto" && !yolo) {
3331
+ effectivePermission = "confirm";
3332
+ }
3333
+ if (effectivePermission === "deny") {
2959
3334
  const result = this.deniedResult(use, decision.reason);
2960
3335
  budget = this.decrementBudget(result, budget);
2961
3336
  return { result, tool, durationMs: Date.now() - start };
2962
3337
  }
2963
- if (decision.permission === "confirm") {
3338
+ if (effectivePermission === "confirm") {
2964
3339
  if (this.opts.confirmAwaiter) {
2965
3340
  const choice = await this.opts.confirmAwaiter(tool, use.input, use.id, tool.name);
2966
3341
  if (choice !== "yes" && choice !== "always") {
@@ -2985,10 +3360,13 @@ var ToolExecutor = class {
2985
3360
  return { result: pending, tool, durationMs: Date.now() - start };
2986
3361
  }
2987
3362
  }
3363
+ const toolCapsForAudit = hasDangerousCapabilityForSubagents(tool) ? tool.capabilities ?? [] : [];
2988
3364
  const span = this.opts.tracer?.startSpan(`tool.${tool.name}`, {
2989
3365
  "tool.name": tool.name,
2990
3366
  "tool.mutating": tool.mutating,
2991
- "tool.permission": tool.permission
3367
+ "tool.permission": tool.permission,
3368
+ "tool.capabilities": JSON.stringify(tool.capabilities ?? []),
3369
+ "tool.has_dangerous_capabilities": toolCapsForAudit.length > 0
2992
3370
  });
2993
3371
  try {
2994
3372
  const result = await this.executeTool(tool, use, ctx, budget);
@@ -3931,9 +4309,11 @@ function resolveWstackPaths(opts) {
3931
4309
  globalPrompts: path6.join(globalRoot, "prompts"),
3932
4310
  cacheDir: path6.join(globalRoot, "cache"),
3933
4311
  modelsCache: path6.join(globalRoot, "cache", "models.dev.json"),
4312
+ modelsOverlayCache: path6.join(globalRoot, "cache", "models-overlay.json"),
3934
4313
  historyFile: path6.join(globalRoot, "history"),
3935
4314
  logFile: path6.join(globalRoot, "logs", "wrongstack.log"),
3936
4315
  projectDir,
4316
+ projectCodebaseIndex: path6.join(projectDir, "codebase-index"),
3937
4317
  projectMemory: path6.join(projectDir, "memory.md"),
3938
4318
  projectSessions: path6.join(projectDir, "sessions"),
3939
4319
  projectTrust: path6.join(projectDir, "trust.json"),
@@ -4042,100 +4422,6 @@ function buildChildEnv(optsOrSessionId) {
4042
4422
  return out;
4043
4423
  }
4044
4424
 
4045
- // src/utils/json-schema-validate.ts
4046
- function validateAgainstSchema(value, schema) {
4047
- const errors = [];
4048
- walk2(value, schema, "", errors);
4049
- return { ok: errors.length === 0, errors };
4050
- }
4051
- function walk2(value, schema, path35, errors) {
4052
- if (schema.enum !== void 0) {
4053
- if (!schema.enum.some((e) => deepEqual(e, value))) {
4054
- errors.push({
4055
- path: path35 || "<root>",
4056
- message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
4057
- });
4058
- return;
4059
- }
4060
- }
4061
- if (typeof schema.type === "string") {
4062
- if (!checkType(value, schema.type)) {
4063
- errors.push({
4064
- path: path35 || "<root>",
4065
- message: `expected ${schema.type}, got ${describeType(value)}`
4066
- });
4067
- return;
4068
- }
4069
- }
4070
- if (schema.type === "object" && isPlainObject(value)) {
4071
- const obj = value;
4072
- for (const req of schema.required ?? []) {
4073
- if (!(req in obj)) {
4074
- errors.push({ path: joinPath(path35, req), message: "required property missing" });
4075
- }
4076
- }
4077
- if (schema.properties) {
4078
- for (const [key, subSchema] of Object.entries(schema.properties)) {
4079
- if (key in obj) {
4080
- walk2(obj[key], subSchema, joinPath(path35, key), errors);
4081
- }
4082
- }
4083
- }
4084
- }
4085
- if (schema.type === "array" && Array.isArray(value) && schema.items) {
4086
- value.forEach((item, i) => walk2(item, schema.items, `${path35}[${i}]`, errors));
4087
- }
4088
- }
4089
- function checkType(value, type) {
4090
- switch (type) {
4091
- case "string":
4092
- return typeof value === "string";
4093
- case "number":
4094
- return typeof value === "number" && !Number.isNaN(value);
4095
- case "integer":
4096
- return typeof value === "number" && Number.isInteger(value);
4097
- case "boolean":
4098
- return typeof value === "boolean";
4099
- case "null":
4100
- return value === null;
4101
- case "array":
4102
- return Array.isArray(value);
4103
- case "object":
4104
- return isPlainObject(value);
4105
- default:
4106
- return true;
4107
- }
4108
- }
4109
- function isPlainObject(v) {
4110
- return typeof v === "object" && v !== null && !Array.isArray(v);
4111
- }
4112
- function describeType(v) {
4113
- if (v === null) return "null";
4114
- if (Array.isArray(v)) return "array";
4115
- return typeof v;
4116
- }
4117
- function joinPath(parent, key) {
4118
- if (!parent) return key;
4119
- return `${parent}.${key}`;
4120
- }
4121
- function deepEqual(a, b) {
4122
- if (a === b) return true;
4123
- if (typeof a !== typeof b) return false;
4124
- if (a === null || b === null) return a === b;
4125
- if (Array.isArray(a) && Array.isArray(b)) {
4126
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
4127
- }
4128
- if (typeof a === "object" && typeof b === "object") {
4129
- const ak = Object.keys(a);
4130
- const bk = Object.keys(b);
4131
- if (ak.length !== bk.length) return false;
4132
- return ak.every(
4133
- (k) => deepEqual(a[k], b[k])
4134
- );
4135
- }
4136
- return false;
4137
- }
4138
-
4139
4425
  // src/utils/json-repair.ts
4140
4426
  function completePartialObject(s) {
4141
4427
  if (!s.trim().startsWith("{")) return s;
@@ -4830,7 +5116,7 @@ function isPersistedQueueItem(v) {
4830
5116
  // src/storage/attachment-store.ts
4831
5117
  init_atomic_write();
4832
5118
  var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
4833
- var PLACEHOLDER_RE = /\[(pasted|image|file) #(\d+)\]/g;
5119
+ var PLACEHOLDER_RE = /\[(pasted|image|file) #(\d+)[^\]]*\]|\[file:([^\]]+)\]/g;
4834
5120
  var DefaultAttachmentStore = class {
4835
5121
  items = /* @__PURE__ */ new Map();
4836
5122
  refs = [];
@@ -4884,9 +5170,15 @@ var DefaultAttachmentStore = class {
4884
5170
  const idx = m.index ?? 0;
4885
5171
  const before = text.slice(lastIndex, idx);
4886
5172
  if (before) blocks.push({ type: "text", text: before });
4887
- const kind = prefixToKind(m[1]);
4888
- const seq = Number(m[2]);
4889
- const ref = this.refs.find((r) => r.kind === kind && r.seq === seq);
5173
+ let ref;
5174
+ if (m[3] !== void 0) {
5175
+ const wantPath = m[3];
5176
+ ref = findLast(this.refs, (r) => r.kind === "file" && refPath(r) === wantPath);
5177
+ } else {
5178
+ const kind = prefixToKind(m[1]);
5179
+ const seq = Number(m[2]);
5180
+ ref = this.refs.find((r) => r.kind === kind && r.seq === seq);
5181
+ }
4890
5182
  const att = ref ? this.items.get(ref.id) : void 0;
4891
5183
  if (!att) {
4892
5184
  blocks.push({ type: "text", text: m[0] });
@@ -4939,6 +5231,15 @@ function prefixToKind(prefix) {
4939
5231
  if (prefix === "image") return "image";
4940
5232
  return "file";
4941
5233
  }
5234
+ function refPath(ref) {
5235
+ return ref.meta.filename ?? ref.meta.label;
5236
+ }
5237
+ function findLast(arr, pred) {
5238
+ for (let i = arr.length - 1; i >= 0; i--) {
5239
+ if (pred(arr[i])) return arr[i];
5240
+ }
5241
+ return void 0;
5242
+ }
4942
5243
  function mergeAdjacentText(blocks) {
4943
5244
  const out = [];
4944
5245
  for (const b of blocks) {
@@ -5266,7 +5567,8 @@ var BEHAVIOR_DEFAULTS = {
5266
5567
  memory: true,
5267
5568
  modelsRegistry: true,
5268
5569
  skills: true
5269
- }
5570
+ },
5571
+ session: { ...DEFAULT_SESSION_LOGGING_CONFIG }
5270
5572
  };
5271
5573
  var ENV_MAP = {
5272
5574
  WRONGSTACK_PROVIDER: (c, v) => {
@@ -5774,6 +6076,101 @@ var SessionAnalyzer = class {
5774
6076
  }
5775
6077
  };
5776
6078
 
6079
+ // src/storage/session-event-bridge.ts
6080
+ var CORE_RECONSTRUCT_EVENTS = /* @__PURE__ */ new Set([
6081
+ "session_start",
6082
+ "session_resumed",
6083
+ "user_input",
6084
+ "llm_response",
6085
+ "tool_result",
6086
+ "checkpoint",
6087
+ "file_snapshot",
6088
+ "rewound",
6089
+ "in_flight_start",
6090
+ "in_flight_end",
6091
+ "session_end"
6092
+ ]);
6093
+ var STANDARD_AUDIT_EVENTS = /* @__PURE__ */ new Set([
6094
+ "llm_request",
6095
+ "tool_use",
6096
+ "tool_call_start",
6097
+ "tool_call_end",
6098
+ "compaction",
6099
+ "error",
6100
+ "message_truncated",
6101
+ "provider_retry",
6102
+ "provider_error"
6103
+ ]);
6104
+ var FULL_ONLY_EVENTS = /* @__PURE__ */ new Set([
6105
+ "tool_progress"
6106
+ ]);
6107
+ function isAllowed(type, level) {
6108
+ if (CORE_RECONSTRUCT_EVENTS.has(type)) return true;
6109
+ if (level === "minimal") return false;
6110
+ if (STANDARD_AUDIT_EVENTS.has(type)) return true;
6111
+ if (level === "standard") return false;
6112
+ if (FULL_ONLY_EVENTS.has(type)) {
6113
+ return level === "full";
6114
+ }
6115
+ return true;
6116
+ }
6117
+ function createSessionEventBridge(writer, level = "standard", options = {}) {
6118
+ const normalizedLevel = level ?? "standard";
6119
+ const progressCounters = /* @__PURE__ */ new Map();
6120
+ const toolProgressConfig = options.sampling?.toolProgress ?? {};
6121
+ const TOOL_PROGRESS_SAMPLE_RATE = toolProgressConfig.sampleRate ?? 8;
6122
+ function shouldSample(event) {
6123
+ if (event.type !== "tool_progress") return true;
6124
+ const progEvent = event;
6125
+ const innerType = progEvent.event?.type;
6126
+ if (innerType === "warning" || innerType === "metric" || innerType === "file_changed") {
6127
+ return true;
6128
+ }
6129
+ if (innerType === "log" || innerType === "partial_output") {
6130
+ const key = progEvent.id || progEvent.name;
6131
+ const count = (progressCounters.get(key) || 0) + 1;
6132
+ progressCounters.set(key, count);
6133
+ return count === 1 || count % TOOL_PROGRESS_SAMPLE_RATE === 0;
6134
+ }
6135
+ return true;
6136
+ }
6137
+ return {
6138
+ level: normalizedLevel,
6139
+ allows(type) {
6140
+ return isAllowed(type, normalizedLevel);
6141
+ },
6142
+ async append(event) {
6143
+ if (!writer) return;
6144
+ if (!isAllowed(event.type, normalizedLevel)) return;
6145
+ if (!shouldSample(event)) return;
6146
+ try {
6147
+ await writer.append(event);
6148
+ } catch (err) {
6149
+ }
6150
+ }
6151
+ };
6152
+ }
6153
+ function resolveAuditLevel(cfg) {
6154
+ const raw = cfg?.session?.auditLevel;
6155
+ if (raw === "minimal" || raw === "standard" || raw === "full") {
6156
+ return raw;
6157
+ }
6158
+ return "standard";
6159
+ }
6160
+ function resolveSessionLoggingConfig(cfg) {
6161
+ const session = cfg?.session ?? {};
6162
+ const auditLevel = resolveAuditLevel(cfg);
6163
+ const toolProgressSampleRate = session.sampling?.toolProgress?.sampleRate ?? 8;
6164
+ return {
6165
+ auditLevel,
6166
+ sampling: {
6167
+ toolProgress: {
6168
+ sampleRate: Math.max(1, Math.floor(toolProgressSampleRate))
6169
+ }
6170
+ }
6171
+ };
6172
+ }
6173
+
5777
6174
  // src/storage/todos-checkpoint.ts
5778
6175
  init_atomic_write();
5779
6176
  async function loadTodosCheckpoint(filePath) {
@@ -6262,8 +6659,6 @@ var DirectorStateCheckpoint = class {
6262
6659
  }
6263
6660
  }
6264
6661
  };
6265
-
6266
- // src/security/permission-policy.ts
6267
6662
  init_atomic_write();
6268
6663
  var DefaultPermissionPolicy = class {
6269
6664
  policy = {};
@@ -6504,31 +6899,21 @@ var DefaultPermissionPolicy = class {
6504
6899
  };
6505
6900
  var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
6506
6901
  /**
6507
- * Tools that are too dangerous to auto-approve even in a delegated
6508
- * subagent context. Subagents run non-interactively under a director
6509
- * and cannot answer prompts, but inherited authorization does not
6510
- * imply blanket permission for destructive or privilege-escalating
6511
- * operations. These tools remain at their declared `permission`
6512
- * level so the leader must explicitly allow them per-spawn.
6902
+ * Legacy name-based denylist.
6903
+ * @deprecated Prefer declaring `capabilities` on the Tool and using capability-based checks.
6513
6904
  */
6514
- static DENY = /* @__PURE__ */ new Set([
6905
+ static LEGACY_NAME_DENY = /* @__PURE__ */ new Set([
6515
6906
  "bash",
6516
- // arbitrary shell — use exec for constrained shell
6517
6907
  "write",
6518
- // arbitrary file write
6519
6908
  "edit",
6520
- // arbitrary in-project file modification (equivalent to write)
6521
6909
  "replace",
6522
- // arbitrary multi-file find/replace (equivalent to write)
6523
6910
  "scaffold",
6524
- // arbitrary file generation outside project root
6525
6911
  "patch",
6526
- // arbitrary diff application
6527
6912
  "install",
6528
- // installs from arbitrary package sources
6529
6913
  "exec"
6530
- // restricted shell but with arbitrary command args
6531
6914
  ]);
6915
+ // Note: hasDangerousCapabilityForSubagents is now the shared helper from capabilities.ts
6916
+ // The old private method was removed in favor of the centralized utility.
6532
6917
  /**
6533
6918
  * Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
6534
6919
  * unknown capability — they may wrap a shell or filesystem. They are
@@ -6539,12 +6924,16 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
6539
6924
  return name.startsWith("mcp__");
6540
6925
  }
6541
6926
  async evaluate(tool) {
6542
- const blocked = _AutoApprovePermissionPolicy.DENY.has(tool.name) || _AutoApprovePermissionPolicy.isMcpTool(tool.name);
6543
- if (tool.permission === "deny" || blocked) {
6927
+ const hasDangerousCap = hasDangerousCapabilityForSubagents(tool);
6928
+ const legacyNameBlock = _AutoApprovePermissionPolicy.LEGACY_NAME_DENY.has(tool.name);
6929
+ const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
6930
+ const blocked = tool.permission === "deny" || hasDangerousCap || legacyNameBlock || isMcp;
6931
+ if (blocked) {
6932
+ const reason = hasDangerousCap ? `tool declares dangerous capability (${tool.capabilities?.join(", ")}) \u2014 not auto-approved for subagents` : legacyNameBlock || isMcp ? `tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny";
6544
6933
  return {
6545
6934
  permission: "deny",
6546
6935
  source: "subagent_guard",
6547
- reason: blocked ? `tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny"
6936
+ reason
6548
6937
  };
6549
6938
  }
6550
6939
  return { permission: "auto", source: "yolo" };
@@ -7676,6 +8065,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
7676
8065
  events;
7677
8066
  failureMode;
7678
8067
  policyProvider;
8068
+ sessionBridge;
7679
8069
  /**
7680
8070
  * Once a compaction attempt reduces nothing (preserveK protects everything,
7681
8071
  * no oversized tool_results remain to elide), retrying on every iteration
@@ -7712,6 +8102,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
7712
8102
  this.events = opts.events;
7713
8103
  this.failureMode = opts.failureMode ?? "throw_on_hard";
7714
8104
  this.policyProvider = opts.policyProvider;
8105
+ this.sessionBridge = opts.sessionBridge;
7715
8106
  }
7716
8107
  /** Allow callers (e.g. model-switch in WebUI) to update the context window
7717
8108
  * denominator when the active model changes. */
@@ -7775,6 +8166,15 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
7775
8166
  report,
7776
8167
  aggressive
7777
8168
  });
8169
+ await this.sessionBridge?.append({
8170
+ type: "compaction",
8171
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
8172
+ before: report.before,
8173
+ after: report.after,
8174
+ level: pressure.level,
8175
+ aggressive,
8176
+ reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved }))
8177
+ });
7778
8178
  ctx.clearFileTracking();
7779
8179
  } catch (err) {
7780
8180
  const error = err instanceof Error ? err : new Error(String(err));
@@ -8686,6 +9086,13 @@ var SubagentBudget = class _SubagentBudget {
8686
9086
  tokenOutput = 0;
8687
9087
  costUsd = 0;
8688
9088
  startTime = null;
9089
+ /**
9090
+ * Timestamp of the most recent activity (iteration / tool call / token
9091
+ * usage / streamed progress). Drives the idle timeout — reset by
9092
+ * `markActivity()`. Initialised to `start()` time so a never-active agent
9093
+ * still eventually trips its idle window.
9094
+ */
9095
+ lastActivityTime = null;
8689
9096
  _onThreshold;
8690
9097
  /**
8691
9098
  * Hard cap on how long `_negotiateExtension` waits for the coordinator to
@@ -8729,6 +9136,24 @@ var SubagentBudget = class _SubagentBudget {
8729
9136
  }
8730
9137
  start() {
8731
9138
  this.startTime = Date.now();
9139
+ this.lastActivityTime = this.startTime;
9140
+ }
9141
+ /**
9142
+ * Reset the idle clock. Called on any sign of forward progress —
9143
+ * iterations, tool calls, token usage, and streamed tool/text progress —
9144
+ * so a long-but-productive subagent never trips its `idleTimeoutMs`.
9145
+ */
9146
+ markActivity() {
9147
+ this.lastActivityTime = Date.now();
9148
+ }
9149
+ /**
9150
+ * Milliseconds since the last activity signal. Returns 0 before `start()`
9151
+ * (nothing to measure yet). Used by the coordinator watchdog to decide
9152
+ * whether to re-arm (still active) or reap (genuinely idle).
9153
+ */
9154
+ idleMs() {
9155
+ const since = this.lastActivityTime ?? this.startTime;
9156
+ return since === null ? 0 : Date.now() - since;
8732
9157
  }
8733
9158
  /** Returns true if we're within 10% of any limit — useful for pre-flight checks. */
8734
9159
  isNearLimit() {
@@ -8776,8 +9201,13 @@ var SubagentBudget = class _SubagentBudget {
8776
9201
  if (this.limits.maxCostUsd !== void 0 && this.costUsd > this.limits.maxCostUsd) {
8777
9202
  exceeded.push({ kind: "cost", used: this.costUsd, limit: this.limits.maxCostUsd });
8778
9203
  }
8779
- if (elapsedMs !== void 0 && this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs) {
8780
- exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
9204
+ if (elapsedMs !== void 0) {
9205
+ const idle = this.idleMs();
9206
+ if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
9207
+ exceeded.push({ kind: "timeout", used: idle, limit: this.limits.idleTimeoutMs });
9208
+ } else if (this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs) {
9209
+ exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
9210
+ }
8781
9211
  }
8782
9212
  if (exceeded.length === 0) return [];
8783
9213
  if (!this._onThreshold) {
@@ -8882,6 +9312,9 @@ var SubagentBudget = class _SubagentBudget {
8882
9312
  if (ext.timeoutMs !== void 0) {
8883
9313
  this.limits.timeoutMs = ext.timeoutMs;
8884
9314
  }
9315
+ if (ext.idleTimeoutMs !== void 0) {
9316
+ this.limits.idleTimeoutMs = ext.idleTimeoutMs;
9317
+ }
8885
9318
  return decision;
8886
9319
  } finally {
8887
9320
  this._pendingNegotiations.delete(kind);
@@ -8889,16 +9322,19 @@ var SubagentBudget = class _SubagentBudget {
8889
9322
  }
8890
9323
  recordIteration() {
8891
9324
  this.iterations++;
9325
+ this.markActivity();
8892
9326
  void this.checkLimits();
8893
9327
  }
8894
9328
  recordToolCall() {
8895
9329
  this.toolCalls++;
9330
+ this.markActivity();
8896
9331
  void this.checkLimits();
8897
9332
  }
8898
9333
  recordUsage(usage, costUsd = 0) {
8899
9334
  this.tokenInput += usage.input;
8900
9335
  this.tokenOutput += usage.output;
8901
9336
  this.costUsd += costUsd;
9337
+ this.markActivity();
8902
9338
  void this.checkLimits();
8903
9339
  }
8904
9340
  /**
@@ -8913,15 +9349,22 @@ var SubagentBudget = class _SubagentBudget {
8913
9349
  * - `mode === 'auto'` + listener → throw `BudgetExceededError` (timeout is not extendable)
8914
9350
  */
8915
9351
  checkTimeout() {
8916
- if (this.startTime === null || this.limits.timeoutMs === void 0) return;
9352
+ if (this.startTime === null) return;
9353
+ const { timeoutMs, idleTimeoutMs } = this.limits;
9354
+ if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
8917
9355
  const elapsed = Date.now() - this.startTime;
8918
- if (elapsed <= this.limits.timeoutMs) return;
9356
+ const wallTripped = timeoutMs !== void 0 && elapsed > timeoutMs;
9357
+ const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
9358
+ if (!wallTripped && !idleTripped) return;
8919
9359
  void this.checkLimits(elapsed);
8920
9360
  }
8921
- /** Returns true if a timeout has occurred without throwing. Useful for races. */
9361
+ /** Returns true if a wall-clock or idle timeout has occurred without throwing. */
8922
9362
  isTimedOut() {
8923
- if (this.startTime === null || this.limits.timeoutMs === void 0) return false;
8924
- return Date.now() - this.startTime > this.limits.timeoutMs;
9363
+ if (this.startTime === null) return false;
9364
+ const { timeoutMs, idleTimeoutMs } = this.limits;
9365
+ if (timeoutMs !== void 0 && Date.now() - this.startTime > timeoutMs) return true;
9366
+ if (idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs) return true;
9367
+ return false;
8925
9368
  }
8926
9369
  usage() {
8927
9370
  return {
@@ -9028,6 +9471,7 @@ function makeAgentSubagentRunner(opts) {
9028
9471
  // Cheap: O(1) per progress event, and the budget short-circuits
9029
9472
  // when timeoutMs is unset (most subagents have one set anyway).
9030
9473
  events.on("tool.progress", () => {
9474
+ ctx.budget.markActivity();
9031
9475
  try {
9032
9476
  ctx.budget.checkTimeout();
9033
9477
  } catch (e) {
@@ -9044,6 +9488,7 @@ function makeAgentSubagentRunner(opts) {
9044
9488
  currentToolName = e.name;
9045
9489
  }),
9046
9490
  events.on("provider.text_delta", (e) => {
9491
+ ctx.budget.markActivity();
9047
9492
  streamingTextAcc = (streamingTextAcc + e.text).slice(-200);
9048
9493
  })
9049
9494
  );
@@ -11613,6 +12058,7 @@ var FLEET_ROSTER = {
11613
12058
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
11614
12059
  )
11615
12060
  };
12061
+ var DEFAULT_IDLE_TIMEOUT_MS = 15 * 60 * 1e3;
11616
12062
  var FLEET_ROSTER_BUDGETS = {
11617
12063
  "audit-log": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 5e3, maxToolCalls: 15e3 },
11618
12064
  "bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
@@ -11624,7 +12070,7 @@ var FLEET_ROSTER_BUDGETS = {
11624
12070
  )
11625
12071
  };
11626
12072
  var GENERIC_SUBAGENT_BUDGET = {
11627
- timeoutMs: 3 * 60 * 60 * 1e3,
12073
+ idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
11628
12074
  maxIterations: 5e3,
11629
12075
  maxToolCalls: 15e3
11630
12076
  };
@@ -11634,7 +12080,13 @@ function applyRosterBudget(cfg) {
11634
12080
  if (!defaultBudget) return cfg;
11635
12081
  return {
11636
12082
  ...cfg,
11637
- timeoutMs: cfg.timeoutMs ?? defaultBudget.timeoutMs,
12083
+ // Wall-clock cap is opt-in only: forward an explicit `cfg.timeoutMs`, but
12084
+ // do NOT impose the roster's historical multi-hour wall-clock default — it
12085
+ // killed agents that were still actively working. Reaping is idle-based.
12086
+ timeoutMs: cfg.timeoutMs,
12087
+ // Idle window is the default reaper. Resets on activity, so a long-but-
12088
+ // productive run is never killed; only a genuine stall is reaped.
12089
+ idleTimeoutMs: cfg.idleTimeoutMs ?? defaultBudget.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS,
11638
12090
  maxIterations: cfg.maxIterations ?? defaultBudget.maxIterations,
11639
12091
  maxToolCalls: cfg.maxToolCalls ?? defaultBudget.maxToolCalls,
11640
12092
  maxTokens: cfg.maxTokens ?? defaultBudget.maxTokens,
@@ -12196,13 +12648,17 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
12196
12648
  const rawMaxTokens = subagent.config.maxTokens;
12197
12649
  const rawMaxCostUsd = subagent.config.maxCostUsd;
12198
12650
  const rawTimeoutMs = subagent.config.timeoutMs;
12651
+ const rawIdleTimeoutMs = subagent.config.idleTimeoutMs;
12199
12652
  const configWithRosterDefaults = applyRosterBudget(subagent.config);
12200
12653
  const budget = new SubagentBudget({
12201
12654
  maxIterations: rawMaxIterations ?? this.config.defaultBudget?.maxIterations ?? configWithRosterDefaults.maxIterations,
12202
12655
  maxToolCalls: rawMaxToolCalls ?? this.config.defaultBudget?.maxToolCalls ?? configWithRosterDefaults.maxToolCalls,
12203
12656
  maxTokens: rawMaxTokens ?? this.config.defaultBudget?.maxTokens ?? configWithRosterDefaults.maxTokens,
12204
12657
  maxCostUsd: rawMaxCostUsd ?? this.config.defaultBudget?.maxCostUsd ?? configWithRosterDefaults.maxCostUsd,
12205
- timeoutMs: rawTimeoutMs ?? this.config.defaultBudget?.timeoutMs ?? configWithRosterDefaults.timeoutMs
12658
+ // Wall-clock cap is opt-in (explicit config / defaultBudget only); the
12659
+ // roster no longer supplies one. Idle is the default reaper.
12660
+ timeoutMs: rawTimeoutMs ?? this.config.defaultBudget?.timeoutMs ?? configWithRosterDefaults.timeoutMs,
12661
+ idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
12206
12662
  });
12207
12663
  subagent.activeBudget = budget;
12208
12664
  if (!this.runner) {
@@ -12249,63 +12705,79 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
12249
12705
  }
12250
12706
  async executeWithTimeout(runner, task, ctx, budget) {
12251
12707
  const initialTimeoutMs = budget.limits.timeoutMs;
12252
- if (initialTimeoutMs === void 0) return runner(task, ctx);
12708
+ const idleLimitMs = budget.limits.idleTimeoutMs;
12709
+ if (initialTimeoutMs === void 0 && idleLimitMs === void 0) {
12710
+ return runner(task, ctx);
12711
+ }
12253
12712
  const start = Date.now();
12254
12713
  let timer = null;
12255
12714
  const timeoutPromise = new Promise((_, reject) => {
12256
12715
  const armFor = (ms) => {
12257
12716
  if (timer) clearTimeout(timer);
12258
- timer = setTimeout(async () => {
12259
- const elapsed = Date.now() - start;
12260
- const limit = budget.limits.timeoutMs ?? initialTimeoutMs;
12261
- if (!budget.onThreshold) {
12262
- this.subagents.get(ctx.subagentId)?.abortController.abort();
12263
- reject(new BudgetExceededError("timeout", limit, elapsed));
12717
+ timer = setTimeout(onTick, Math.max(0, ms));
12718
+ };
12719
+ const scheduleNext = () => {
12720
+ const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.timeoutMs ?? initialTimeoutMs) - (Date.now() - start);
12721
+ const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
12722
+ armFor(Math.max(25, Math.min(wallRemaining, idleRemaining)));
12723
+ };
12724
+ const onTick = async () => {
12725
+ const elapsed = Date.now() - start;
12726
+ const wallLimit = initialTimeoutMs === void 0 ? void 0 : budget.limits.timeoutMs ?? initialTimeoutMs;
12727
+ const idleLimit = idleLimitMs === void 0 ? void 0 : budget.limits.idleTimeoutMs ?? idleLimitMs;
12728
+ const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
12729
+ const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
12730
+ if (idleExceeded && !wallExceeded) {
12731
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
12732
+ reject(new BudgetExceededError("timeout", idleLimit, budget.idleMs()));
12733
+ return;
12734
+ }
12735
+ if (!wallExceeded) {
12736
+ scheduleNext();
12737
+ return;
12738
+ }
12739
+ const limit = wallLimit;
12740
+ if (!budget.onThreshold) {
12741
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
12742
+ reject(new BudgetExceededError("timeout", limit, elapsed));
12743
+ return;
12744
+ }
12745
+ try {
12746
+ const result = budget.onThreshold({
12747
+ kind: "timeout",
12748
+ used: elapsed,
12749
+ limit,
12750
+ requestDecision: () => new Promise((resolveDecision) => {
12751
+ budget._events?.emit("budget.threshold_reached", {
12752
+ kind: "timeout",
12753
+ used: elapsed,
12754
+ limit,
12755
+ timeoutMs: 6e4,
12756
+ extend: (extra) => resolveDecision({ extend: extra }),
12757
+ deny: () => resolveDecision("stop")
12758
+ });
12759
+ })
12760
+ });
12761
+ const decision = typeof result === "string" ? result : await result;
12762
+ if (decision === "continue" || decision === "throw" || decision === "stop") {
12763
+ armFor(Math.max(1e3, limit));
12264
12764
  return;
12265
12765
  }
12266
- try {
12267
- const result = budget.onThreshold({
12268
- kind: "timeout",
12269
- used: elapsed,
12270
- limit,
12271
- requestDecision: () => new Promise((resolveDecision) => {
12272
- budget._events?.emit("budget.threshold_reached", {
12273
- kind: "timeout",
12274
- used: elapsed,
12275
- limit,
12276
- timeoutMs: 6e4,
12277
- extend: (extra) => resolveDecision({ extend: extra }),
12278
- deny: () => resolveDecision("stop")
12279
- });
12280
- })
12281
- });
12282
- const decision = typeof result === "string" ? result : await result;
12283
- if (decision === "continue") {
12284
- armFor(Math.max(1e3, limit));
12285
- return;
12286
- }
12287
- if (decision === "throw" || decision === "stop") {
12288
- armFor(Math.max(1e3, limit));
12289
- return;
12290
- }
12291
- if (decision.extend.timeoutMs !== void 0) {
12292
- budget.limits.timeoutMs = decision.extend.timeoutMs;
12293
- const newLimit = decision.extend.timeoutMs;
12294
- const remaining = Math.max(1e3, newLimit - elapsed);
12295
- armFor(remaining);
12296
- return;
12297
- }
12298
- this.subagents.get(ctx.subagentId)?.abortController.abort();
12299
- reject(new BudgetExceededError("timeout", limit, elapsed));
12300
- } catch (err) {
12301
- this.subagents.get(ctx.subagentId)?.abortController.abort();
12302
- reject(
12303
- err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
12304
- );
12766
+ if (decision.extend.timeoutMs !== void 0) {
12767
+ budget.limits.timeoutMs = decision.extend.timeoutMs;
12768
+ scheduleNext();
12769
+ return;
12305
12770
  }
12306
- }, ms);
12771
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
12772
+ reject(new BudgetExceededError("timeout", limit, elapsed));
12773
+ } catch (err) {
12774
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
12775
+ reject(
12776
+ err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
12777
+ );
12778
+ }
12307
12779
  };
12308
- armFor(initialTimeoutMs);
12780
+ scheduleNext();
12309
12781
  });
12310
12782
  try {
12311
12783
  return await Promise.race([runner(task, ctx), timeoutPromise]);
@@ -19343,16 +19815,16 @@ var allServers = () => ({
19343
19815
  function parseSkillRef(input) {
19344
19816
  const trimmed = input.trim().replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
19345
19817
  const atIdx = trimmed.indexOf("@");
19346
- let refPath;
19818
+ let refPath2;
19347
19819
  let ref;
19348
19820
  if (atIdx > 0) {
19349
- refPath = trimmed.slice(0, atIdx);
19821
+ refPath2 = trimmed.slice(0, atIdx);
19350
19822
  ref = trimmed.slice(atIdx + 1);
19351
19823
  } else {
19352
- refPath = trimmed;
19824
+ refPath2 = trimmed;
19353
19825
  ref = "main";
19354
19826
  }
19355
- const parts = refPath.split("/").filter(Boolean);
19827
+ const parts = refPath2.split("/").filter(Boolean);
19356
19828
  if (parts.length < 2) {
19357
19829
  throw new Error(`Invalid skill reference "${input}". Expected format: user/repo or user/repo@ref`);
19358
19830
  }
@@ -23917,6 +24389,15 @@ var Agent = class {
23917
24389
  this.events.emit("iteration.started", { ctx: this.ctx, index: i });
23918
24390
  this.injectPendingBtwNotes();
23919
24391
  const req = await this.buildAndRunRequestPipeline(opts);
24392
+ await this.ctx.session.append({
24393
+ type: "llm_request",
24394
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
24395
+ model: req.model,
24396
+ messageCount: req.messages.length,
24397
+ estimatedInputTokens: estimateRequestTokens(req.messages, req.system, req.tools ?? []).total,
24398
+ toolCount: (req.tools ?? []).length
24399
+ }).catch(() => {
24400
+ });
23920
24401
  let res;
23921
24402
  try {
23922
24403
  res = await customRunner(this.ctx, req);
@@ -24126,6 +24607,7 @@ var Agent = class {
24126
24607
  this.executionStrategy
24127
24608
  );
24128
24609
  const useById = new Map(selectedToolUses.map((u) => [u.id, u]));
24610
+ const resultsForMessage = [];
24129
24611
  for (const { result, tool, durationMs } of outputs) {
24130
24612
  if (result.type === "tool_confirm_pending") {
24131
24613
  const decision = await this.waitForConfirm({
@@ -24210,8 +24692,10 @@ var Agent = class {
24210
24692
  });
24211
24693
  }
24212
24694
  }
24695
+ resultsForMessage.push(reRunResult.result);
24213
24696
  continue;
24214
24697
  }
24698
+ resultsForMessage.push(result);
24215
24699
  const use = useById.get(result.tool_use_id);
24216
24700
  if (!use) continue;
24217
24701
  await this.pipelines.toolCall.run({
@@ -24244,7 +24728,7 @@ var Agent = class {
24244
24728
  }
24245
24729
  this.ctx.state.appendMessage({
24246
24730
  role: "user",
24247
- content: outputs.map((o) => o.result)
24731
+ content: resultsForMessage
24248
24732
  });
24249
24733
  await this.extensions.runAfterToolExecution(this.ctx, outputs);
24250
24734
  }
@@ -24609,6 +25093,50 @@ var InputBuilder = class {
24609
25093
  this.refs.push(ref);
24610
25094
  return placeholder;
24611
25095
  }
25096
+ /**
25097
+ * Register-only variant of `appendPaste`. Always stores the paste and
25098
+ * returns the inline `[pasted #N, L lines]` token WITHOUT mutating
25099
+ * `display` — the caller (TUI) owns its own editable buffer as the single
25100
+ * source of truth, inserts the token there, and expands the buffer at
25101
+ * submit. The collapse decision is the caller's (it gates this call); use
25102
+ * `wouldCollapse()` for that.
25103
+ */
25104
+ async registerPaste(text) {
25105
+ const ref = await this.store.add({
25106
+ kind: "text",
25107
+ data: text,
25108
+ meta: { label: paragraphLabel(text) }
25109
+ });
25110
+ this.refs.push(ref);
25111
+ const lines = text.split("\n").length;
25112
+ return `[pasted #${ref.seq}, ${lines} lines]`;
25113
+ }
25114
+ /**
25115
+ * Register-only variant of `appendImage` — see `registerPaste`. Returns a
25116
+ * seq-keyed `[image #N, LABEL]` token; does not touch `display`.
25117
+ */
25118
+ async registerImage(dataBase64, mediaType) {
25119
+ const label = `${mediaType.split("/")[1]?.toUpperCase() ?? "IMG"}`;
25120
+ const ref = await this.store.add({
25121
+ kind: "image",
25122
+ data: dataBase64,
25123
+ meta: { mediaType, label }
25124
+ });
25125
+ this.refs.push(ref);
25126
+ return `[image #${ref.seq}, ${label}]`;
25127
+ }
25128
+ /**
25129
+ * Register-only variant of `appendFile` — see `registerPaste`. Returns a
25130
+ * path-keyed `[file:<path>]` token (resolved by path at expand time); does
25131
+ * not touch `display`. The path is read from `meta.filename` (falling back
25132
+ * to `meta.label`).
25133
+ */
25134
+ async registerFile(input) {
25135
+ const ref = await this.store.add({ ...input, kind: "file" });
25136
+ this.refs.push(ref);
25137
+ const path35 = ref.meta.filename ?? ref.meta.label ?? String(ref.seq);
25138
+ return `[file:${path35}]`;
25139
+ }
24612
25140
  /**
24613
25141
  * Whether `appendPaste(text)` would collapse the text to a placeholder
24614
25142
  * (rather than inlining it). Lets a frontend decide where to route a paste
@@ -25091,6 +25619,14 @@ var ToolRegistry = class _ToolRegistry {
25091
25619
  context: { tool: tool.name }
25092
25620
  });
25093
25621
  }
25622
+ if (!tool.inputSchema || typeof tool.inputSchema !== "object") {
25623
+ throw new WrongStackError({
25624
+ message: `Tool "${tool.name}" has an invalid or missing inputSchema`,
25625
+ code: ERROR_CODES.REGISTRY_INVALID,
25626
+ subsystem: "container",
25627
+ context: { tool: tool.name }
25628
+ });
25629
+ }
25094
25630
  this.tools.set(tool.name, { tool, owner });
25095
25631
  }
25096
25632
  /**
@@ -25100,6 +25636,9 @@ var ToolRegistry = class _ToolRegistry {
25100
25636
  */
25101
25637
  tryRegister(tool, owner = "core") {
25102
25638
  if (this.tools.has(tool.name)) return false;
25639
+ if (!tool.inputSchema || typeof tool.inputSchema !== "object") {
25640
+ return false;
25641
+ }
25103
25642
  this.tools.set(tool.name, { tool, owner });
25104
25643
  return true;
25105
25644
  }
@@ -28591,6 +29130,6 @@ ${formatPlan(updated)}`
28591
29130
  };
28592
29131
  }
28593
29132
 
28594
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, hashRequest, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, pendingBtwCount, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
29133
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, hashRequest, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, pendingBtwCount, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveAuditLevel, resolveContextWindowPolicy, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
28595
29134
  //# sourceMappingURL=index.js.map
28596
29135
  //# sourceMappingURL=index.js.map