@wrongstack/core 0.292.0 → 0.293.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 (65) hide show
  1. package/dist/coordination/index.d.ts +1 -1
  2. package/dist/coordination/index.d.ts.map +1 -1
  3. package/dist/coordination/index.js +163 -39
  4. package/dist/coordination/index.js.map +3 -3
  5. package/dist/coordination/mailbox-http-router.d.ts +38 -0
  6. package/dist/coordination/mailbox-http-router.d.ts.map +1 -1
  7. package/dist/coordination/provider-status-tracker.d.ts.map +1 -1
  8. package/dist/core/fallback-model.d.ts.map +1 -1
  9. package/dist/core/fallback-profile-manager.d.ts +0 -2
  10. package/dist/core/fallback-profile-manager.d.ts.map +1 -1
  11. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  12. package/dist/defaults/index.js +351 -318
  13. package/dist/defaults/index.js.map +4 -4
  14. package/dist/execution/compaction-core.d.ts +3 -0
  15. package/dist/execution/compaction-core.d.ts.map +1 -1
  16. package/dist/execution/compactor.d.ts +4 -0
  17. package/dist/execution/compactor.d.ts.map +1 -1
  18. package/dist/execution/index.js +141 -50
  19. package/dist/execution/index.js.map +4 -4
  20. package/dist/execution/intelligent-compactor.d.ts +5 -1
  21. package/dist/execution/intelligent-compactor.d.ts.map +1 -1
  22. package/dist/execution/one-shot-llm.d.ts.map +1 -1
  23. package/dist/execution/selective-compactor.d.ts +4 -0
  24. package/dist/execution/selective-compactor.d.ts.map +1 -1
  25. package/dist/goal/phase-orchestrator.d.ts +4 -0
  26. package/dist/goal/phase-orchestrator.d.ts.map +1 -1
  27. package/dist/hooks/runner.d.ts +0 -2
  28. package/dist/hooks/runner.d.ts.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +421 -248
  32. package/dist/index.js.map +4 -4
  33. package/dist/models/alibaba-token-plan-catalog.d.ts +85 -0
  34. package/dist/models/alibaba-token-plan-catalog.d.ts.map +1 -0
  35. package/dist/models/index.d.ts +1 -0
  36. package/dist/models/index.d.ts.map +1 -1
  37. package/dist/models/index.js +200 -15
  38. package/dist/models/index.js.map +4 -4
  39. package/dist/models/llm-selector.d.ts +4 -0
  40. package/dist/models/llm-selector.d.ts.map +1 -1
  41. package/dist/models/models-registry.d.ts +8 -0
  42. package/dist/models/models-registry.d.ts.map +1 -1
  43. package/dist/security/index.js +0 -23
  44. package/dist/security/index.js.map +2 -2
  45. package/dist/security/permission-policy.d.ts +0 -23
  46. package/dist/security/permission-policy.d.ts.map +1 -1
  47. package/dist/storage/config-loader.d.ts.map +1 -1
  48. package/dist/storage/index.js +6 -5
  49. package/dist/storage/index.js.map +2 -2
  50. package/dist/tools/index.js +8 -1
  51. package/dist/tools/index.js.map +2 -2
  52. package/dist/types/config.d.ts +9 -16
  53. package/dist/types/config.d.ts.map +1 -1
  54. package/dist/types/index.js +7 -4
  55. package/dist/types/index.js.map +2 -2
  56. package/dist/types/provider.d.ts.map +1 -1
  57. package/dist/utils/connectivity.d.ts +36 -0
  58. package/dist/utils/connectivity.d.ts.map +1 -0
  59. package/dist/utils/index.d.ts +1 -0
  60. package/dist/utils/index.d.ts.map +1 -1
  61. package/dist/utils/index.js +59 -11
  62. package/dist/utils/index.js.map +4 -4
  63. package/dist/utils/merge-models-payload.d.ts +9 -0
  64. package/dist/utils/merge-models-payload.d.ts.map +1 -1
  65. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1366,12 +1366,43 @@ async function backupConfigFile(filePath, paths) {
1366
1366
  }
1367
1367
  }
1368
1368
 
1369
+ // src/utils/connectivity.ts
1370
+ var DEFAULT_PROBE_URL = "https://1.1.1.1";
1371
+ var DEFAULT_TIMEOUT_MS = 5e3;
1372
+ var DEFAULT_TTL_MS = 3e4;
1373
+ var cached;
1374
+ async function checkConnectivity(opts) {
1375
+ const url = opts?.probeUrl ?? DEFAULT_PROBE_URL;
1376
+ const timeout = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1377
+ const ttl = opts?.ttlMs ?? DEFAULT_TTL_MS;
1378
+ if (cached && ttl > 0 && Date.now() - cached.at < ttl) {
1379
+ return cached.ok;
1380
+ }
1381
+ cached = { ok: await probe(url, timeout), at: Date.now() };
1382
+ return cached.ok;
1383
+ }
1384
+ async function probe(url, timeoutMs) {
1385
+ const controller = new AbortController();
1386
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1387
+ try {
1388
+ await fetch(url, { method: "HEAD", signal: controller.signal });
1389
+ return true;
1390
+ } catch {
1391
+ return false;
1392
+ } finally {
1393
+ clearTimeout(timer);
1394
+ }
1395
+ }
1396
+ function resetConnectivityCache() {
1397
+ cached = void 0;
1398
+ }
1399
+
1369
1400
  // src/utils/cache-key.ts
1370
1401
  import { createHash } from "node:crypto";
1371
1402
  var keyCache = /* @__PURE__ */ new WeakMap();
1372
1403
  function deriveCachePrefixKey(systemPrompt) {
1373
- const cached = keyCache.get(systemPrompt);
1374
- if (cached !== void 0) return cached;
1404
+ const cached2 = keyCache.get(systemPrompt);
1405
+ if (cached2 !== void 0) return cached2;
1375
1406
  const h = createHash("sha256");
1376
1407
  for (const block of systemPrompt) h.update(block.text).update("\0");
1377
1408
  const key = `ws-${h.digest("hex").slice(0, 32)}`;
@@ -2349,8 +2380,8 @@ function resolveTokenSavingTier(val, maxContext) {
2349
2380
  return "off";
2350
2381
  }
2351
2382
  if (maxContext < 32e3) return "medium";
2352
- if (maxContext < 96e3) return "light";
2353
- return "off";
2383
+ if (maxContext < 128e3) return "light";
2384
+ return "minimal";
2354
2385
  }
2355
2386
  return normalizeTokenSavingTier(val);
2356
2387
  }
@@ -2360,7 +2391,6 @@ function resolveFleetChatVerbosity(autonomy) {
2360
2391
  if (explicit && FLEET_CHAT_VERBOSITY_VALUES.includes(explicit)) {
2361
2392
  return explicit;
2362
2393
  }
2363
- if (autonomy?.streamFleet === false) return "off";
2364
2394
  return "off";
2365
2395
  }
2366
2396
  var DEFAULT_TUI_THINKING_WORD = "thinking";
@@ -2710,11 +2740,20 @@ var DefaultSystemPromptBuilder = class {
2710
2740
  const layer5 = await this.buildMode();
2711
2741
  const layer6 = ctx.subagent ? "" : await this.buildActivePlan();
2712
2742
  const core = [
2713
- tagBlock({ type: "text", text: layer1 }, "identity"),
2714
- tagBlock({ type: "text", text: layer2 }, "tool-usage")
2743
+ tagBlock(
2744
+ { type: "text", text: layer1, cache_control: { type: "ephemeral" } },
2745
+ "identity"
2746
+ ),
2747
+ tagBlock(
2748
+ { type: "text", text: layer2, cache_control: { type: "ephemeral" } },
2749
+ "tool-usage"
2750
+ )
2715
2751
  ];
2716
2752
  const session = [
2717
- tagBlock({ type: "text", text: layer3WithDir }, "environment")
2753
+ tagBlock(
2754
+ { type: "text", text: layer3WithDir, cache_control: { type: "ephemeral" } },
2755
+ "environment"
2756
+ )
2718
2757
  ];
2719
2758
  const volatile = [];
2720
2759
  if (layer4.trim()) {
@@ -3099,8 +3138,8 @@ ${agentList}`;
3099
3138
  modelCapabilities?.supportsVision ? 1 : 0,
3100
3139
  modelCapabilities?.supportsReasoning ? 1 : 0
3101
3140
  ].join("\0");
3102
- const cached = this.envCacheByRoot.get(cacheKey);
3103
- if (cached) return cached;
3141
+ const cached2 = this.envCacheByRoot.get(cacheKey);
3142
+ if (cached2) return cached2;
3104
3143
  const today = this.opts.todayIso ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3105
3144
  const platform4 = `${os2.platform()} ${os2.release()}`;
3106
3145
  const effShell = effectiveShell(os2.platform(), process.env["WRONGSTACK_SHELL"]);
@@ -3460,8 +3499,8 @@ var compactCache = /* @__PURE__ */ new WeakMap();
3460
3499
  function compactToolDefinitionForWire(tool, opts = {}) {
3461
3500
  const useDefaultOptions = opts.descriptionMaxChars === void 0 && opts.schemaDescriptionMaxChars === void 0;
3462
3501
  if (useDefaultOptions && typeof tool === "object" && tool !== null) {
3463
- const cached = compactCache.get(tool);
3464
- if (cached) return cached;
3502
+ const cached2 = compactCache.get(tool);
3503
+ if (cached2) return cached2;
3465
3504
  }
3466
3505
  const compact = {
3467
3506
  name: tool.name,
@@ -3649,8 +3688,8 @@ function realAnchoredInputTokens(messages, anchorTokens, anchorMsgCount) {
3649
3688
  return anchorTokens + delta;
3650
3689
  }
3651
3690
  function estimateToolDefTokens(tool) {
3652
- const cached = tool._estDefTokens;
3653
- if (typeof cached === "number" && cached > 0) return cached;
3691
+ const cached2 = tool._estDefTokens;
3692
+ if (typeof cached2 === "number" && cached2 > 0) return cached2;
3654
3693
  const compact = compactToolDefinitionForWire(tool);
3655
3694
  return RoughTokenEstimate(tool.name) + RoughTokenEstimate(compact.description) + RoughTokenEstimate(JSON.stringify(compact.inputSchema));
3656
3695
  }
@@ -3661,9 +3700,9 @@ function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = C
3661
3700
  } else if (Array.isArray(messages)) {
3662
3701
  for (const m of messages) {
3663
3702
  if (typeof m === "object" && m !== null && "content" in m) {
3664
- const cached = m._estTokens;
3665
- if (typeof cached === "number" && cached > 0) {
3666
- messagesTokens += cached;
3703
+ const cached2 = m._estTokens;
3704
+ if (typeof cached2 === "number" && cached2 > 0) {
3705
+ messagesTokens += cached2;
3667
3706
  continue;
3668
3707
  }
3669
3708
  const content = m.content;
@@ -4291,8 +4330,8 @@ var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
4291
4330
  var CACHE_MAX_SIZE = 2e3;
4292
4331
  var NEVER_MATCH = /[^\s\S]/;
4293
4332
  function getCachedGlob(pattern) {
4294
- const cached = COMPILED_GLOB_CACHE.get(pattern);
4295
- if (cached) return cached;
4333
+ const cached2 = COMPILED_GLOB_CACHE.get(pattern);
4334
+ if (cached2) return cached2;
4296
4335
  if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
4297
4336
  const keys = [...COMPILED_GLOB_CACHE.keys()];
4298
4337
  for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
@@ -4817,15 +4856,30 @@ function mergeCustomModelDefs(providerCustomModels, configModels) {
4817
4856
  }
4818
4857
 
4819
4858
  // src/utils/merge-models-payload.ts
4859
+ var REMOVE_PROVIDERS_KEY = "_removeProviders";
4860
+ var REMOVE_MODELS_KEY = "_removeModels";
4820
4861
  function mergeModelsPayload(base, overlay) {
4862
+ const removeProviders = Array.isArray(overlay[REMOVE_PROVIDERS_KEY]) ? overlay[REMOVE_PROVIDERS_KEY] : [];
4863
+ const removeModels = overlay[REMOVE_MODELS_KEY] && typeof overlay[REMOVE_MODELS_KEY] === "object" ? overlay[REMOVE_MODELS_KEY] : {};
4821
4864
  const out = {};
4822
4865
  for (const [id, provider] of Object.entries(base)) {
4823
4866
  out[id] = cloneProvider(provider);
4824
4867
  }
4825
4868
  for (const [id, ovProvider] of Object.entries(overlay)) {
4869
+ if (id === REMOVE_PROVIDERS_KEY || id === REMOVE_MODELS_KEY) continue;
4826
4870
  const existing = out[id];
4827
4871
  out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
4828
4872
  }
4873
+ for (const providerId of removeProviders) {
4874
+ delete out[providerId];
4875
+ }
4876
+ for (const [providerId, modelIds] of Object.entries(removeModels)) {
4877
+ const provider = out[providerId];
4878
+ if (!provider || !provider.models) continue;
4879
+ for (const modelId of modelIds) {
4880
+ delete provider.models[modelId];
4881
+ }
4882
+ }
4829
4883
  return out;
4830
4884
  }
4831
4885
  function mergeProvider(base, overlay) {
@@ -7181,9 +7235,10 @@ var BEHAVIOR_DEFAULTS = {
7181
7235
  prompts: true,
7182
7236
  // 'auto' → resolveTokenSavingTier picks a concrete tier from the model's
7183
7237
  // context window ONCE per session (cache-safe): lean prompt on small
7184
- // windows (<32k medium, <96k light) where the fixed identity+tool prose is
7185
- // a big fraction; full prompt (off) on 96k+ so nothing changes for the
7186
- // common large-window case. Explicit tiers are respected verbatim.
7238
+ // windows (<32k medium, <128k light) where the fixed identity+tool prose
7239
+ // is a big fraction; minimal trimming on >=128k so modern large-window
7240
+ // models still get cost savings without capability loss. Explicit tiers
7241
+ // are respected verbatim.
7187
7242
  tokenSavingMode: "auto",
7188
7243
  allowOutsideProjectRoot: true
7189
7244
  },
@@ -7251,7 +7306,7 @@ var BEHAVIOR_DEFAULTS = {
7251
7306
  // Mirrored from the top-level yolo default so the autonomy subsystem
7252
7307
  // (which reads autonomy.yolo) stays consistent with config.yolo.
7253
7308
  yolo: false,
7254
- streamFleet: true,
7309
+ fleetChatVerbosity: "off",
7255
7310
  chime: false,
7256
7311
  confirmExit: true,
7257
7312
  mouseMode: false,
@@ -7269,7 +7324,7 @@ var BEHAVIOR_DEFAULTS = {
7269
7324
  // silently omitted, and surfaced as a per-request warning. Users who
7270
7325
  // want a specific effort can opt in via `/settings` or the WebUI panel.
7271
7326
  reasoning: { mode: "auto" },
7272
- cache: {}
7327
+ cache: { ttl: "1h" }
7273
7328
  }
7274
7329
  };
7275
7330
  function isPlainRecord(value) {
@@ -7978,9 +8033,9 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
7978
8033
  try {
7979
8034
  const stat27 = await fs9.stat(file);
7980
8035
  mtimeMs = stat27.mtimeMs;
7981
- const cached = this.jsonCache.get(file);
7982
- if (cached && cached.mtimeMs === mtimeMs) {
7983
- return structuredClone(cached.value);
8036
+ const cached2 = this.jsonCache.get(file);
8037
+ if (cached2 && cached2.mtimeMs === mtimeMs) {
8038
+ return structuredClone(cached2.value);
7984
8039
  }
7985
8040
  } catch (err) {
7986
8041
  if (err.code === "ENOENT") {
@@ -11759,8 +11814,8 @@ var candidateCache = /* @__PURE__ */ new Map();
11759
11814
  function agentPrompt(id) {
11760
11815
  const envDir = process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"] ?? "";
11761
11816
  const cacheKey = `${envDir}\0${id}`;
11762
- const cached = promptCache.get(cacheKey);
11763
- if (cached !== void 0) return cached;
11817
+ const cached2 = promptCache.get(cacheKey);
11818
+ if (cached2 !== void 0) return cached2;
11764
11819
  const fileName = `${id}.md`;
11765
11820
  let resolved = "";
11766
11821
  for (const dir of agentPromptDirCandidates(envDir)) {
@@ -11776,8 +11831,8 @@ function agentPrompt(id) {
11776
11831
  function agentPromptDirCandidates(envDir) {
11777
11832
  const globalRoot = process.env["WRONGSTACK_HOME"] || path19.join(os6.homedir(), ".wrongstack");
11778
11833
  const candKey = `${envDir}\0${globalRoot}`;
11779
- const cached = candidateCache.get(candKey);
11780
- if (cached !== void 0) return cached;
11834
+ const cached2 = candidateCache.get(candKey);
11835
+ if (cached2 !== void 0) return cached2;
11781
11836
  const here = path19.dirname(fileURLToPath3(import.meta.url));
11782
11837
  const explicitDir = envDir || void 0;
11783
11838
  const candidates = [
@@ -15839,8 +15894,8 @@ import { fileURLToPath as fileURLToPath4 } from "node:url";
15839
15894
  var textCache = /* @__PURE__ */ new Map();
15840
15895
  var rootCandidates;
15841
15896
  function readBundledInstructionText(relativePath) {
15842
- const cached = textCache.get(relativePath);
15843
- if (cached !== void 0) return cached;
15897
+ const cached2 = textCache.get(relativePath);
15898
+ if (cached2 !== void 0) return cached2;
15844
15899
  let resolved = "";
15845
15900
  for (const root of instructionRootCandidates()) {
15846
15901
  try {
@@ -17815,7 +17870,8 @@ function effectiveInputTokens(usage) {
17815
17870
  }
17816
17871
  var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|prompt is too long|too long|exceeds the context|\btokens\b.*exceed|too many tokens|reduce the length|resulted in \d+ tokens|input.{0,12}too (?:large|long)|context_length_exceeded/i;
17817
17872
  var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
17818
- var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit/i;
17873
+ var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
17874
+ var RATE_LIMIT_EXCEEDED_RE = /rate[-_\s]*limit[-_\s]*exceeded/i;
17819
17875
  function classifyProviderError(status, body, message) {
17820
17876
  const type = body?.type;
17821
17877
  const text2 = [message, body?.message, type, body?.raw].filter(Boolean).join("\n");
@@ -17823,6 +17879,9 @@ function classifyProviderError(status, body, message) {
17823
17879
  if (status === 408) return "timeout";
17824
17880
  if (status === 599) return "stream_hang";
17825
17881
  if (status === 402 || QUOTA_EXHAUSTED_RE.test(text2)) return "quota_exhausted";
17882
+ if (status === 429 && body?.message && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
17883
+ return "quota_exhausted";
17884
+ }
17826
17885
  if (type === "rate_limit_error" || status === 429) return "rate_limit";
17827
17886
  if (type === "overloaded_error" || status === 529) return "overloaded";
17828
17887
  if (status >= 500) return "server";
@@ -18545,6 +18604,12 @@ function createFallbackModelExtension(deps) {
18545
18604
  continue;
18546
18605
  const status = shouldFallback(lastErr);
18547
18606
  if (status === null) break;
18607
+ if (tracker && !tracker.isAvailable(entry.providerId, entry.model)) {
18608
+ deps.logger?.warn(
18609
+ `provider-status: "${entry.providerId}/${entry.model}" entered the waiting room since the chain was computed \u2014 skipping`
18610
+ );
18611
+ continue;
18612
+ }
18548
18613
  const targetProviderId = entry.providerId;
18549
18614
  const targetModel = entry.model;
18550
18615
  if (targetProviderId === ctx_.provider.id && targetModel === ctx_.model) continue;
@@ -19350,8 +19415,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
19350
19415
  async awaitTasks(taskIds) {
19351
19416
  return Promise.all(
19352
19417
  taskIds.map((id) => {
19353
- const cached = this.completedResults.find((r) => r.taskId === id);
19354
- if (cached) return cached;
19418
+ const cached2 = this.completedResults.find((r) => r.taskId === id);
19419
+ if (cached2) return cached2;
19355
19420
  return new Promise((resolve34, reject) => {
19356
19421
  const timeout = setTimeout(() => {
19357
19422
  this.off("task.completed", handler);
@@ -21318,8 +21383,8 @@ var Director = class _Director {
21318
21383
  awaitTasks(taskIds) {
21319
21384
  return Promise.all(
21320
21385
  taskIds.map((id) => {
21321
- const cached = this.completed.get(id);
21322
- if (cached) return cached;
21386
+ const cached2 = this.completed.get(id);
21387
+ if (cached2) return cached2;
21323
21388
  const existing = this.taskWaiters.get(id);
21324
21389
  if (existing) return existing.promise;
21325
21390
  let resolve34;
@@ -22815,17 +22880,17 @@ var SessionCheckpointCas = class {
22815
22880
  if (!normalized) throw new Error("invalid relative path");
22816
22881
  const output = path25.resolve(target, ...normalized.split("/"));
22817
22882
  if (!isInside(target, output)) throw new Error("path escapes checkpoint target");
22818
- let probe = output;
22883
+ let probe2 = output;
22819
22884
  for (; ; ) {
22820
22885
  try {
22821
- const real = await fsp9.realpath(probe);
22886
+ const real = await fsp9.realpath(probe2);
22822
22887
  if (!isInside(realTarget, real)) throw new Error("path resolves through a symlink outside checkpoint target");
22823
22888
  return output;
22824
22889
  } catch (err) {
22825
22890
  if (err.code !== "ENOENT") throw err;
22826
- const parent = path25.dirname(probe);
22827
- if (parent === probe) throw err;
22828
- probe = parent;
22891
+ const parent = path25.dirname(probe2);
22892
+ if (parent === probe2) throw err;
22893
+ probe2 = parent;
22829
22894
  }
22830
22895
  }
22831
22896
  }
@@ -23422,13 +23487,13 @@ var DefaultSessionStore = class _DefaultSessionStore {
23422
23487
  try {
23423
23488
  const s = await fsp11.stat(file);
23424
23489
  const stat27 = { mtimeMs: s.mtimeMs, size: s.size };
23425
- const cached = this._loadCache.get(id);
23426
- if (cached && cached.mtimeMs === stat27.mtimeMs && cached.size === stat27.size) {
23490
+ const cached2 = this._loadCache.get(id);
23491
+ if (cached2 && cached2.mtimeMs === stat27.mtimeMs && cached2.size === stat27.size) {
23427
23492
  cacheHit = true;
23428
23493
  this._loadCache.delete(id);
23429
- this._loadCache.set(id, cached);
23430
- if (mode.full) return cached.data;
23431
- return { ...cached.data, messages: [] };
23494
+ this._loadCache.set(id, cached2);
23495
+ if (mode.full) return cached2.data;
23496
+ return { ...cached2.data, messages: [] };
23432
23497
  }
23433
23498
  const events = [];
23434
23499
  let sessionStartEvent;
@@ -23959,8 +24024,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
23959
24024
  return shardKeys;
23960
24025
  }
23961
24026
  async readOrBuildShardManifest(shardKey) {
23962
- const cached = this.shardManifestCache.get(shardKey);
23963
- if (cached) return cached;
24027
+ const cached2 = this.shardManifestCache.get(shardKey);
24028
+ if (cached2) return cached2;
23964
24029
  const manifestPath = this.shardManifestPath(shardKey);
23965
24030
  try {
23966
24031
  const raw = await fsp11.readFile(manifestPath, "utf8");
@@ -28102,6 +28167,8 @@ import { timingSafeEqual } from "node:crypto";
28102
28167
  var MAILBOX_HTTP_MAX_BODY_BYTES = 256 * 1024;
28103
28168
  var MAILBOX_HTTP_RATE_LIMIT_PER_MINUTE = 120;
28104
28169
  var MAILBOX_HTTP_RATE_LIMIT_WINDOW_MS = 6e4;
28170
+ var MAILBOX_HTTP_DEFAULT_MAX_AGE_MS = 60 * 60 * 1e3;
28171
+ var MAILBOX_HTTP_MAX_AGE_CEILING_MS = 7 * 24 * 60 * 60 * 1e3;
28105
28172
  function authorizeMailboxBearerToken(request, expectedToken) {
28106
28173
  const header = request.headers.authorization;
28107
28174
  if (typeof header !== "string") return { allowed: false };
@@ -28145,6 +28212,7 @@ var MailboxHttpRateLimiter = class {
28145
28212
  };
28146
28213
  function createMailboxHttpRouter(options) {
28147
28214
  const maxBodyBytes = options.maxBodyBytes ?? MAILBOX_HTTP_MAX_BODY_BYTES;
28215
+ const defaultMaxAgeMs = options.defaultMaxAgeMs ?? void 0;
28148
28216
  const closeSseStreams = /* @__PURE__ */ new Set();
28149
28217
  return {
28150
28218
  async handle(request, response, routePath) {
@@ -28159,15 +28227,17 @@ function createMailboxHttpRouter(options) {
28159
28227
  if (!access10.allowed) {
28160
28228
  const forwardedFor = request.headers["x-forwarded-for"];
28161
28229
  const clientIp = (Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor)?.split(",")[0]?.trim() ?? request.socket?.remoteAddress ?? "unknown";
28162
- console.warn(JSON.stringify({
28163
- level: "warn",
28164
- event: "mailbox.http_auth_failure",
28165
- message: `Mailbox HTTP auth rejected for ${request.method ?? "?"} ${request.url ?? "?"} from ${clientIp}`,
28166
- method: request.method,
28167
- url: request.url,
28168
- clientIp,
28169
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
28170
- }));
28230
+ console.warn(
28231
+ JSON.stringify({
28232
+ level: "warn",
28233
+ event: "mailbox.http_auth_failure",
28234
+ message: `Mailbox HTTP auth rejected for ${request.method ?? "?"} ${request.url ?? "?"} from ${clientIp}`,
28235
+ method: request.method,
28236
+ url: request.url,
28237
+ clientIp,
28238
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
28239
+ })
28240
+ );
28171
28241
  writeJson(
28172
28242
  response,
28173
28243
  access10.status ?? 401,
@@ -28194,7 +28264,9 @@ function createMailboxHttpRouter(options) {
28194
28264
  method,
28195
28265
  url,
28196
28266
  maxBodyBytes,
28197
- closeSseStreams
28267
+ defaultMaxAgeMs,
28268
+ closeSseStreams,
28269
+ routePath
28198
28270
  );
28199
28271
  } catch (error2) {
28200
28272
  const code = error2 instanceof MailboxHttpValidationError ? "VALIDATION_ERROR" : "INTERNAL_ERROR";
@@ -28213,85 +28285,134 @@ function createMailboxHttpRouter(options) {
28213
28285
  }
28214
28286
  };
28215
28287
  }
28216
- async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, method, url, maxBodyBytes, closeSseStreams) {
28217
- if (method === "POST" && url === "/mailbox/send") {
28288
+ async function dispatchMailboxRoute(mailbox, eventEmitter, request, response, method, url, maxBodyBytes, defaultMaxAgeMs, closeSseStreams, routePath) {
28289
+ if (routePath !== void 0 && routePath.indexOf("?") === 0) {
28290
+ throw validationError(`routePath must not start with '?' (got ${JSON.stringify(routePath)})`);
28291
+ }
28292
+ const queryIndex = url.indexOf("?");
28293
+ const path76 = queryIndex === -1 ? url : url.slice(0, queryIndex);
28294
+ if (method === "POST" && path76 === "/mailbox/send") {
28218
28295
  const input = validateSend(await readJsonBody(request, maxBodyBytes));
28219
28296
  writeJson(response, 201, await mailbox.send(input));
28220
28297
  return;
28221
28298
  }
28222
- if (method === "POST" && url === "/mailbox/query") {
28299
+ if (method === "POST" && path76 === "/mailbox/query") {
28300
+ const queryContext = parseSinceMs(url, defaultMaxAgeMs);
28301
+ if ("error" in queryContext) {
28302
+ writeJson(response, 400, { error: queryContext.error });
28303
+ return;
28304
+ }
28223
28305
  const messages = await mailbox.query(validateQuery(await readJsonBody(request, maxBodyBytes)));
28224
- writeJson(response, 200, { data: messages, count: messages.length });
28306
+ const filtered = filterMailboxMessagesByTimestamp(messages, queryContext.minTimestampIso);
28307
+ writeJson(response, 200, { data: filtered, count: filtered.length });
28225
28308
  return;
28226
28309
  }
28227
- if (method === "POST" && url === "/mailbox/check") {
28310
+ if (method === "POST" && path76 === "/mailbox/check") {
28311
+ const queryContext = parseSinceMs(url, defaultMaxAgeMs);
28312
+ if ("error" in queryContext) {
28313
+ writeJson(response, 400, { error: queryContext.error });
28314
+ return;
28315
+ }
28228
28316
  const result = await checkMailbox(
28229
28317
  mailbox,
28230
- validateCheck(await readJsonBody(request, maxBodyBytes))
28318
+ validateCheck(await readJsonBody(request, maxBodyBytes)),
28319
+ queryContext.minTimestampIso
28231
28320
  );
28232
28321
  writeJson(response, 200, result);
28233
28322
  return;
28234
28323
  }
28235
- if (method === "POST" && url === "/mailbox/ack") {
28324
+ if (method === "POST" && path76 === "/mailbox/ack") {
28236
28325
  const updated = await mailbox.ack(validateAck(await readJsonBody(request, maxBodyBytes)));
28237
28326
  writeJson(response, 200, { updated });
28238
28327
  return;
28239
28328
  }
28240
- if (method === "POST" && url === "/mailbox/ack-many") {
28329
+ if (method === "POST" && path76 === "/mailbox/ack-many") {
28241
28330
  const updated = await mailbox.ackMany(
28242
28331
  validateAckMany(await readJsonBody(request, maxBodyBytes))
28243
28332
  );
28244
28333
  writeJson(response, 200, { updated, count: updated.length });
28245
28334
  return;
28246
28335
  }
28247
- if (method === "POST" && url === "/mailbox/unread-count") {
28336
+ if (method === "POST" && path76 === "/mailbox/unread-count") {
28248
28337
  const body = await readJsonBody(request, maxBodyBytes);
28249
- writeJson(response, 200, { count: await mailbox.unreadCount(requireString(body, "forAgentId")) });
28338
+ writeJson(response, 200, {
28339
+ count: await mailbox.unreadCount(requireString(body, "forAgentId"))
28340
+ });
28250
28341
  return;
28251
28342
  }
28252
- if (method === "POST" && url === "/mailbox/agents/register") {
28253
- await mailbox.registerAgent(validateAgentRegistration(await readJsonBody(request, maxBodyBytes)));
28343
+ if (method === "POST" && path76 === "/mailbox/agents/register") {
28344
+ await mailbox.registerAgent(
28345
+ validateAgentRegistration(await readJsonBody(request, maxBodyBytes))
28346
+ );
28254
28347
  writeJson(response, 200, { ok: true });
28255
28348
  return;
28256
28349
  }
28257
- if (method === "POST" && url === "/mailbox/agents/heartbeat") {
28350
+ if (method === "POST" && path76 === "/mailbox/agents/heartbeat") {
28258
28351
  await mailbox.heartbeat(validateAgentHeartbeat(await readJsonBody(request, maxBodyBytes)));
28259
28352
  writeJson(response, 200, { ok: true });
28260
28353
  return;
28261
28354
  }
28262
- if (method === "POST" && url === "/mailbox/register-client") {
28263
- await mailbox.registerClient(validateClientRegistration(await readJsonBody(request, maxBodyBytes)));
28355
+ if (method === "POST" && path76 === "/mailbox/register-client") {
28356
+ await mailbox.registerClient(
28357
+ validateClientRegistration(await readJsonBody(request, maxBodyBytes))
28358
+ );
28264
28359
  writeJson(response, 200, { ok: true });
28265
28360
  return;
28266
28361
  }
28267
- if (method === "POST" && url === "/mailbox/heartbeat") {
28268
- await mailbox.clientHeartbeat(validateClientHeartbeat(await readJsonBody(request, maxBodyBytes)));
28362
+ if (method === "POST" && path76 === "/mailbox/heartbeat") {
28363
+ await mailbox.clientHeartbeat(
28364
+ validateClientHeartbeat(await readJsonBody(request, maxBodyBytes))
28365
+ );
28269
28366
  writeJson(response, 200, { ok: true });
28270
28367
  return;
28271
28368
  }
28272
- if (method === "POST" && url === "/mailbox/purge-clients") {
28369
+ if (method === "POST" && path76 === "/mailbox/purge-clients") {
28273
28370
  writeJson(response, 200, { ok: true, purged: await mailbox.purgeClients() });
28274
28371
  return;
28275
28372
  }
28276
- if (method === "GET" && url === "/mailbox/agents") {
28373
+ if (method === "GET" && path76 === "/mailbox/agents") {
28277
28374
  const agents = await mailbox.getAgentStatuses();
28278
28375
  writeJson(response, 200, { data: agents, count: agents.length });
28279
28376
  return;
28280
28377
  }
28281
- if (method === "GET" && url === "/mailbox/agents/online") {
28378
+ if (method === "GET" && path76 === "/mailbox/agents/online") {
28282
28379
  const agents = await mailbox.getOnlineAgents();
28283
28380
  writeJson(response, 200, { data: agents, count: agents.length });
28284
28381
  return;
28285
28382
  }
28286
- if (method === "GET" && url === "/mailbox/events" && eventEmitter) {
28287
- handleSse(request, response, eventEmitter, closeSseStreams);
28383
+ if (method === "GET" && path76 === "/mailbox/events" && eventEmitter) {
28384
+ const queryContext = parseSinceMs(url, defaultMaxAgeMs);
28385
+ if ("error" in queryContext) {
28386
+ writeJson(response, 400, { error: queryContext.error });
28387
+ return;
28388
+ }
28389
+ handleSse(request, response, eventEmitter, queryContext.minTimestampIso, closeSseStreams);
28288
28390
  return;
28289
28391
  }
28290
28392
  writeJson(response, 404, {
28291
28393
  error: { code: "NOT_FOUND", message: `no route for ${method} ${url}` }
28292
28394
  });
28293
28395
  }
28294
- function handleSse(request, response, eventEmitter, closeSseStreams) {
28396
+ function extractEventTimestamp(event) {
28397
+ if (event === null || typeof event !== "object") return void 0;
28398
+ const top = event.timestamp;
28399
+ if (typeof top === "string") return top;
28400
+ const nestedKeys = ["messageSent", "ackUpdated"];
28401
+ for (const key of nestedKeys) {
28402
+ const nested = event[key];
28403
+ if (nested !== null && typeof nested === "object") {
28404
+ const inner = nested.timestamp;
28405
+ if (typeof inner === "string") return inner;
28406
+ }
28407
+ }
28408
+ return void 0;
28409
+ }
28410
+ function isEventOlderThan(event, minTimestampIso) {
28411
+ const eventTimestamp = extractEventTimestamp(event);
28412
+ if (eventTimestamp === void 0) return false;
28413
+ return eventTimestamp < minTimestampIso;
28414
+ }
28415
+ function handleSse(request, response, eventEmitter, minTimestampIso, closeSseStreams) {
28295
28416
  response.writeHead(200, {
28296
28417
  "Content-Type": "text/event-stream",
28297
28418
  "Cache-Control": "no-store",
@@ -28301,6 +28422,9 @@ function handleSse(request, response, eventEmitter, closeSseStreams) {
28301
28422
  response.write(": connected\n\n");
28302
28423
  const unsubscribe2 = eventEmitter.subscribe((event) => {
28303
28424
  try {
28425
+ if (minTimestampIso !== void 0 && isEventOlderThan(event, minTimestampIso)) {
28426
+ return;
28427
+ }
28304
28428
  response.write(`data: ${JSON.stringify(event)}
28305
28429
 
28306
28430
  `);
@@ -28410,6 +28534,52 @@ function optionalNumber(object, key) {
28410
28534
  }
28411
28535
  return value;
28412
28536
  }
28537
+ function parseSinceMs(url, defaultMaxAgeMs) {
28538
+ const queryStart = url.indexOf("?");
28539
+ if (queryStart === -1) return resolveDefault(defaultMaxAgeMs, Date.now());
28540
+ const params = new URLSearchParams(url.slice(queryStart + 1));
28541
+ if (!params.has("sinceMs")) return resolveDefault(defaultMaxAgeMs, Date.now());
28542
+ const raw = params.get("sinceMs");
28543
+ if (raw === null || raw === void 0 || raw === "") {
28544
+ return {
28545
+ error: {
28546
+ code: "VALIDATION_ERROR",
28547
+ message: 'query parameter "sinceMs" is required (integer in milliseconds) when present'
28548
+ }
28549
+ };
28550
+ }
28551
+ if (!/^\d+$/.test(raw)) {
28552
+ return {
28553
+ error: {
28554
+ code: "VALIDATION_ERROR",
28555
+ message: 'query parameter "sinceMs" must be a non-negative integer (milliseconds)'
28556
+ }
28557
+ };
28558
+ }
28559
+ const requestedMs = Number(raw);
28560
+ if (!Number.isFinite(requestedMs) || requestedMs > Number.MAX_SAFE_INTEGER) {
28561
+ return {
28562
+ error: {
28563
+ code: "VALIDATION_ERROR",
28564
+ message: `query parameter "sinceMs" is out of range (max ${Number.MAX_SAFE_INTEGER})`
28565
+ }
28566
+ };
28567
+ }
28568
+ const now = Date.now();
28569
+ if (requestedMs === 0) return { minTimestampIso: void 0 };
28570
+ const effectiveMs = Math.min(requestedMs, MAILBOX_HTTP_MAX_AGE_CEILING_MS);
28571
+ return { minTimestampIso: new Date(now - effectiveMs).toISOString() };
28572
+ }
28573
+ function resolveDefault(defaultMaxAgeMs, now) {
28574
+ if (defaultMaxAgeMs === void 0 || !Number.isFinite(defaultMaxAgeMs) || defaultMaxAgeMs < 0 || defaultMaxAgeMs === 0) {
28575
+ return { minTimestampIso: void 0 };
28576
+ }
28577
+ return { minTimestampIso: new Date(now - defaultMaxAgeMs).toISOString() };
28578
+ }
28579
+ function filterMailboxMessagesByTimestamp(messages, minTimestampIso) {
28580
+ if (minTimestampIso === void 0) return messages.slice();
28581
+ return messages.filter((message) => message.timestamp >= minTimestampIso);
28582
+ }
28413
28583
  function optionalBoolean(object, key) {
28414
28584
  if (typeof object !== "object" || object === null) return void 0;
28415
28585
  const value = object[key];
@@ -28530,7 +28700,7 @@ function validateQuery(body) {
28530
28700
  if (incompleteOnly !== void 0) result.incompleteOnly = incompleteOnly;
28531
28701
  return result;
28532
28702
  }
28533
- async function checkMailbox(mailbox, input) {
28703
+ async function checkMailbox(mailbox, input, minTimestampIso) {
28534
28704
  const limit = input.limit ?? 20;
28535
28705
  const markRead = input.markRead ?? true;
28536
28706
  const completed = input.completed ?? false;
@@ -28538,8 +28708,9 @@ async function checkMailbox(mailbox, input) {
28538
28708
  const batches = await Promise.all(
28539
28709
  targets.map((to) => mailbox.query({ to, unreadBy: input.agentId, limit }))
28540
28710
  );
28711
+ const withinWindow = filterMailboxMessagesByTimestamp(batches.flat(), minTimestampIso);
28541
28712
  const seen = /* @__PURE__ */ new Set();
28542
- const messages = batches.flat().filter((message) => {
28713
+ const messages = withinWindow.filter((message) => {
28543
28714
  if (seen.has(message.id) || message.from === input.agentId) return false;
28544
28715
  seen.add(message.id);
28545
28716
  return true;
@@ -28913,11 +29084,11 @@ var DefaultMailbox = class {
28913
29084
  async query(q) {
28914
29085
  const queryType = q.type === void 0 ? void 0 : normalizeMailboxMessageType(q.type);
28915
29086
  const needFullScan = q.unreadBy !== void 0 || q.since !== void 0;
28916
- const cached = await this._readAllCached(false);
29087
+ const cached2 = await this._readAllCached(false);
28917
29088
  let candidates;
28918
- let candidatesComplete = !this._messageCacheTruncated || cached !== this._messageCache;
29089
+ let candidatesComplete = !this._messageCacheTruncated || cached2 !== this._messageCache;
28919
29090
  if (needFullScan) {
28920
- candidates = cached;
29091
+ candidates = cached2;
28921
29092
  } else {
28922
29093
  if (q.to !== void 0) {
28923
29094
  const direct = this._byTo.get(q.to);
@@ -28931,7 +29102,7 @@ var DefaultMailbox = class {
28931
29102
  candidates = Array.from(this._byFrom.get(q.from) ?? []);
28932
29103
  candidatesComplete = !this._messageCacheTruncated;
28933
29104
  } else {
28934
- candidates = cached;
29105
+ candidates = cached2;
28935
29106
  }
28936
29107
  }
28937
29108
  const limit = q.limit ?? 50;
@@ -30102,19 +30273,24 @@ var ProviderModelStatusTracker = class {
30102
30273
  let newState = s.state;
30103
30274
  let reason = "";
30104
30275
  const quotaExhausted = kind === "quota_exhausted" || isQuotaExhausted(kind, status, message);
30276
+ const endpointUnreachable = isEndpointUnreachable(kind, status, message);
30105
30277
  if (quotaExhausted) {
30106
30278
  newState = "blocked";
30107
30279
  reason = "quota_exhausted";
30108
30280
  s.stateExpiresAt = now + this.cfg.quotaBlockDurationMs;
30281
+ } else if (endpointUnreachable) {
30282
+ newState = "blocked";
30283
+ reason = "endpoint_unreachable";
30284
+ s.stateExpiresAt = now + this.cfg.quotaBlockDurationMs;
30109
30285
  }
30110
- if (!quotaExhausted && s.state === "healthy") {
30286
+ if (!quotaExhausted && !endpointUnreachable && s.state === "healthy") {
30111
30287
  if (s.consecutiveFailures >= this.cfg.degradedAfterFailures) {
30112
30288
  newState = "degraded";
30113
30289
  reason = `consecutive_failures_${s.consecutiveFailures}`;
30114
30290
  s.stateExpiresAt = now + this.cfg.degradedDurationMs;
30115
30291
  }
30116
30292
  }
30117
- if (!quotaExhausted && (s.state === "degraded" || s.state === "healthy")) {
30293
+ if (!quotaExhausted && !endpointUnreachable && (s.state === "degraded" || s.state === "healthy")) {
30118
30294
  if (s.rateLimitHits >= this.cfg.blockAfterRateLimitHits) {
30119
30295
  newState = "blocked";
30120
30296
  reason = `rate_limit_threshold_${this.cfg.blockAfterRateLimitHits}`;
@@ -30444,13 +30620,20 @@ function unpairKey(key) {
30444
30620
  if (idx === -1) return [key, ""];
30445
30621
  return [key.slice(0, idx), key.slice(idx + 1)];
30446
30622
  }
30447
- var QUOTA_EXHAUSTED_RE2 = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit/i;
30623
+ var QUOTA_EXHAUSTED_RE2 = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)|rate[-_\s]*limit[-_\s]*exceeded/i;
30448
30624
  function isQuotaExhausted(kind, status, message) {
30449
30625
  if (status === 402) return true;
30450
30626
  if (kind !== "rate_limit" && kind !== "quota_exhausted" && kind !== "invalid_request" && kind !== "auth")
30451
30627
  return false;
30452
30628
  return QUOTA_EXHAUSTED_RE2.test(message);
30453
30629
  }
30630
+ var ENDPOINT_UNREACHABLE_RE = /(?:upstream|origin|backend|endpoint)[-_\s]*(?:unreachable|refused|connect(?:ion)?[-_\s]*(?:refused|error|fail)|unavailable|down|timeout)|no[-_\s]*(?:healthy|valid)[-_\s]*upstream|cannot[-_\s]*connect|connection[-_\s]*(?:refused|reset|closed|timed?[-_\s]out)/i;
30631
+ function isEndpointUnreachable(kind, status, message) {
30632
+ if (status !== 502 && status !== 503 && status !== 0) return false;
30633
+ if (kind !== "server" && kind !== "network" && kind !== "overloaded" && kind !== "timeout")
30634
+ return false;
30635
+ return ENDPOINT_UNREACHABLE_RE.test(message);
30636
+ }
30454
30637
  function safeCount(value) {
30455
30638
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : 0;
30456
30639
  }
@@ -39485,25 +39668,30 @@ var WHITESPACE_COLLAPSE_PATTERN = /\s+/g;
39485
39668
  function compactionDebugEnabled() {
39486
39669
  return process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1";
39487
39670
  }
39671
+ var _debugLogger;
39672
+ function setCompactionDebugLogger(logger) {
39673
+ _debugLogger = logger;
39674
+ }
39488
39675
  function emitCompactionMetrics(event, metrics) {
39489
39676
  if (!compactionDebugEnabled()) return;
39490
- console.log(
39491
- JSON.stringify({
39492
- level: "debug",
39493
- event,
39494
- messageCount: metrics.messageCount,
39495
- preserveStart: metrics.preserveStart,
39496
- fastPathIterations: metrics.fastPathIterations,
39497
- fastPathInnerIterations: metrics.fastPathInnerIterations,
39498
- // Ratios — anything > 2.0 indicates the inner loop is running more than expected
39499
- fastPathInnerPerOuter: metrics.fastPathIterations > 0 ? metrics.fastPathInnerIterations / metrics.fastPathIterations : 0,
39500
- fullPassIterations: metrics.fullPassIterations,
39501
- fullPassInnerIterations: metrics.fullPassInnerIterations,
39502
- fullPassInnerPerOuter: metrics.fullPassIterations > 0 ? metrics.fullPassInnerIterations / metrics.fullPassIterations : 0,
39503
- tokensSaved: metrics.tokensSaved,
39504
- changed: metrics.changed
39505
- })
39506
- );
39677
+ const ctx = {
39678
+ event,
39679
+ messageCount: metrics.messageCount,
39680
+ preserveStart: metrics.preserveStart,
39681
+ fastPathIterations: metrics.fastPathIterations,
39682
+ fastPathInnerIterations: metrics.fastPathInnerIterations,
39683
+ fastPathInnerPerOuter: metrics.fastPathIterations > 0 ? metrics.fastPathInnerIterations / metrics.fastPathIterations : 0,
39684
+ fullPassIterations: metrics.fullPassIterations,
39685
+ fullPassInnerIterations: metrics.fullPassInnerIterations,
39686
+ fullPassInnerPerOuter: metrics.fullPassIterations > 0 ? metrics.fullPassInnerIterations / metrics.fullPassIterations : 0,
39687
+ tokensSaved: metrics.tokensSaved,
39688
+ changed: metrics.changed
39689
+ };
39690
+ if (_debugLogger) {
39691
+ _debugLogger.debug(`compaction: ${event}`, ctx);
39692
+ } else {
39693
+ console.log(JSON.stringify({ level: "debug", ...ctx }));
39694
+ }
39507
39695
  }
39508
39696
  var estimateMessages = estimateMessageTokens;
39509
39697
  function hasTextContent(m) {
@@ -39535,18 +39723,20 @@ function findPreserveStart(messages, preserveK) {
39535
39723
  preserveStart--;
39536
39724
  }
39537
39725
  if (compactionDebugEnabled()) {
39538
- console.log(
39539
- JSON.stringify({
39540
- level: "debug",
39541
- event: "compaction.find_preserve_start.ended",
39542
- messageCount: messages.length,
39543
- preserveK,
39544
- preserveStart,
39545
- pairRepairIterations,
39546
- pairRepairInnerIterations,
39547
- pairRepairInnerPerOuter: pairRepairIterations > 0 ? pairRepairInnerIterations / pairRepairIterations : 0
39548
- })
39549
- );
39726
+ const ctx = {
39727
+ event: "compaction.find_preserve_start.ended",
39728
+ messageCount: messages.length,
39729
+ preserveK,
39730
+ preserveStart,
39731
+ pairRepairIterations,
39732
+ pairRepairInnerIterations,
39733
+ pairRepairInnerPerOuter: pairRepairIterations > 0 ? pairRepairInnerIterations / pairRepairIterations : 0
39734
+ };
39735
+ if (_debugLogger) {
39736
+ _debugLogger.debug("compaction: find_preserve_start.ended", ctx);
39737
+ } else {
39738
+ console.log(JSON.stringify({ level: "debug", ...ctx }));
39739
+ }
39550
39740
  }
39551
39741
  return preserveStart;
39552
39742
  }
@@ -39578,8 +39768,8 @@ function eliseOldToolResults(messages, opts) {
39578
39768
  const preserveStart = findPreserveStart(messages, opts.preserveK);
39579
39769
  const tokenCache = /* @__PURE__ */ new Map();
39580
39770
  const tokensFor = (b) => {
39581
- const cached = tokenCache.get(b);
39582
- if (cached !== void 0) return cached;
39771
+ const cached2 = tokenCache.get(b);
39772
+ if (cached2 !== void 0) return cached2;
39583
39773
  const t2 = b.type === "tool_result" ? estimateToolResultTokens(b.content) : b.type === "tool_use" ? estimateToolInputTokens(b.input) : 0;
39584
39774
  tokenCache.set(b, t2);
39585
39775
  return t2;
@@ -39661,16 +39851,18 @@ function eliseOldToolResults(messages, opts) {
39661
39851
  if (compactionDebugEnabled()) {
39662
39852
  const ratio = fullPassInnerIterations / fullPassIterations;
39663
39853
  if (ratio > 10) {
39664
- console.error(
39665
- JSON.stringify({
39666
- level: "error",
39667
- event: "compaction.elision.regression",
39668
- message: `fullPassInnerPerOuter=${ratio.toFixed(2)} exceeds threshold 10 \u2014 possible O(n\xB7m) regression`,
39669
- messageCount: messages.length,
39670
- fullPassIterations,
39671
- fullPassInnerIterations
39672
- })
39673
- );
39854
+ const ctx = {
39855
+ event: "compaction.elision.regression",
39856
+ message: `fullPassInnerPerOuter=${ratio.toFixed(2)} exceeds threshold 10 \u2014 possible O(n\xB7m) regression`,
39857
+ messageCount: messages.length,
39858
+ fullPassIterations,
39859
+ fullPassInnerIterations
39860
+ };
39861
+ if (_debugLogger) {
39862
+ _debugLogger.error(`compaction: elision.regression \u2014 ratio ${ratio.toFixed(2)}`, ctx);
39863
+ } else {
39864
+ console.error(JSON.stringify({ level: "error", ...ctx }));
39865
+ }
39674
39866
  }
39675
39867
  }
39676
39868
  }
@@ -40630,10 +40822,13 @@ var HybridCompactor = class {
40630
40822
  preserveK;
40631
40823
  eliseThreshold;
40632
40824
  smart;
40825
+ logger;
40633
40826
  constructor(opts = {}) {
40634
40827
  this.preserveK = opts.preserveK ?? 5;
40635
40828
  this.eliseThreshold = opts.eliseThreshold ?? 2e3;
40636
40829
  this.smart = opts.smart ?? false;
40830
+ this.logger = opts.logger ?? noOpLogger;
40831
+ setCompactionDebugLogger(this.logger);
40637
40832
  }
40638
40833
  async compact(ctx, opts = {}) {
40639
40834
  const beforeTokens = estimateMessages(ctx.messages);
@@ -41436,8 +41631,8 @@ var DefaultDesignKitLoader = class {
41436
41631
  }
41437
41632
  async readBody(id, stack) {
41438
41633
  const key = `${id.toLowerCase()}:${stack ?? "*"}`;
41439
- const cached = this.bodyCache.get(key);
41440
- if (cached !== void 0) return cached;
41634
+ const cached2 = this.bodyCache.get(key);
41635
+ if (cached2 !== void 0) return cached2;
41441
41636
  const m = await this.find(id);
41442
41637
  if (!m) throw new Error(`Design kit "${id}" not found`);
41443
41638
  const raw = await fs20.readFile(m.path, "utf8");
@@ -43791,6 +43986,7 @@ var IntelligentCompactor = class {
43791
43986
  summarizerPrompt;
43792
43987
  summarizerModel;
43793
43988
  oneShotOrchestrator;
43989
+ logger;
43794
43990
  constructor(opts) {
43795
43991
  this.provider = opts.provider;
43796
43992
  this.warnThreshold = opts.warnThreshold ?? 0.5;
@@ -43802,6 +43998,8 @@ var IntelligentCompactor = class {
43802
43998
  this.summarizerPrompt = opts.summarizerPrompt ?? readBundledInstructionText("llm/intelligent-compactor-summarizer.md");
43803
43999
  this.summarizerModel = opts.summarizerModel;
43804
44000
  this.oneShotOrchestrator = opts.oneShotOrchestrator;
44001
+ this.logger = opts.logger ?? noOpLogger;
44002
+ setCompactionDebugLogger(this.logger);
43805
44003
  }
43806
44004
  async compact(ctx, opts = {}) {
43807
44005
  const beforeTokens = estimateMessages(ctx.messages);
@@ -44597,11 +44795,13 @@ var LLMSelector = class {
44597
44795
  systemPrompt;
44598
44796
  maxOutputTokens;
44599
44797
  oneShotOrchestrator;
44798
+ logger;
44600
44799
  constructor(opts) {
44601
44800
  this.provider = opts.provider;
44602
44801
  this.model = opts.model ?? "unknown";
44802
+ this.logger = opts.logger ?? noOpLogger;
44603
44803
  if (this.model === "unknown" && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
44604
- console.warn(
44804
+ this.logger.warn(
44605
44805
  "[LLMSelector] model not set \u2014 selector will use the provider default. Set `model` explicitly in LLMSelectorOptions to silence this warning."
44606
44806
  );
44607
44807
  }
@@ -44651,14 +44851,9 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
44651
44851
  }
44652
44852
  } catch (err) {
44653
44853
  if (err instanceof Error) {
44654
- console.warn(
44655
- JSON.stringify({
44656
- level: "warn",
44657
- event: "llm_selector.call_failed",
44658
- message: `selector call failed, using recency fallback: ${err.message}`,
44659
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
44660
- })
44661
- );
44854
+ this.logger.warn(`selector call failed, using recency fallback: ${err.message}`, {
44855
+ event: "llm_selector.call_failed"
44856
+ });
44662
44857
  }
44663
44858
  return this.fallbackSelect(messages, effectiveBudget);
44664
44859
  } finally {
@@ -44765,6 +44960,7 @@ var SelectiveCompactor = class {
44765
44960
  eliseThreshold;
44766
44961
  summarizerModel;
44767
44962
  summarizerPrompt;
44963
+ logger;
44768
44964
  constructor(opts) {
44769
44965
  this.provider = opts.provider;
44770
44966
  this.selector = opts.selector ?? new LLMSelector({ provider: opts.provider, model: opts.selectorModel, maxOutputTokens: opts.selectorMaxOutputTokens });
@@ -44775,8 +44971,10 @@ var SelectiveCompactor = class {
44775
44971
  this.preserveK = opts.preserveK ?? 4;
44776
44972
  this.eliseThreshold = opts.eliseThreshold ?? 300;
44777
44973
  this.summarizerModel = opts.summarizerModel ?? opts.selectorModel;
44974
+ this.logger = opts.logger ?? noOpLogger;
44975
+ setCompactionDebugLogger(this.logger);
44778
44976
  if (this.summarizerModel === void 0 && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
44779
- console.warn(
44977
+ this.logger.warn(
44780
44978
  "[SelectiveCompactor] summarizerModel not set \u2014 will fall back to ctx.model at summarize time. Set `summarizerModel` explicitly to silence this warning."
44781
44979
  );
44782
44980
  }
@@ -45305,8 +45503,8 @@ var DefaultSkillLoader = class {
45305
45503
  }
45306
45504
  async readBody(name) {
45307
45505
  const key = name.toLowerCase();
45308
- const cached = this.bodyCache.get(key);
45309
- if (cached !== void 0) return cached;
45506
+ const cached2 = this.bodyCache.get(key);
45507
+ if (cached2 !== void 0) return cached2;
45310
45508
  const m = await this.find(name);
45311
45509
  if (!m) throw new Error(`Skill "${name}" not found`);
45312
45510
  const body = await fs22.readFile(m.path, "utf8");
@@ -45315,8 +45513,8 @@ var DefaultSkillLoader = class {
45315
45513
  }
45316
45514
  async readSaveBody(name) {
45317
45515
  const key = `save:${name.toLowerCase()}`;
45318
- const cached = this.bodyCache.get(key);
45319
- if (cached !== void 0) return cached;
45516
+ const cached2 = this.bodyCache.get(key);
45517
+ if (cached2 !== void 0) return cached2;
45320
45518
  const m = await this.find(name);
45321
45519
  if (!m) throw new Error(`Skill "${name}" not found`);
45322
45520
  const savePath = path42.join(path42.dirname(m.path), "SKILL.save.md");
@@ -46059,19 +46257,19 @@ async function canonicalizeCandidatePath(candidate, ctx) {
46059
46257
  if (path45.isAbsolute(candidate)) return candidate;
46060
46258
  const absolute = path45.resolve(ctx.projectRoot, candidate);
46061
46259
  const canonicalRoot = await realpath3(ctx.projectRoot).catch(() => ctx.projectRoot);
46062
- let probe = absolute;
46260
+ let probe2 = absolute;
46063
46261
  const missingSegments = [];
46064
46262
  while (true) {
46065
46263
  try {
46066
- const canonical = path45.join(await realpath3(probe), ...missingSegments);
46264
+ const canonical = path45.join(await realpath3(probe2), ...missingSegments);
46067
46265
  return relativeToProject(canonical, canonicalRoot);
46068
46266
  } catch (cause) {
46069
46267
  const code = cause.code;
46070
46268
  if (code !== "ENOENT" && code !== "ENOTDIR") return absolute;
46071
- const parent = path45.dirname(probe);
46072
- if (parent === probe) return absolute;
46073
- missingSegments.unshift(path45.basename(probe));
46074
- probe = parent;
46269
+ const parent = path45.dirname(probe2);
46270
+ if (parent === probe2) return absolute;
46271
+ missingSegments.unshift(path45.basename(probe2));
46272
+ probe2 = parent;
46075
46273
  }
46076
46274
  }
46077
46275
  }
@@ -47945,6 +48143,7 @@ var DefaultModelsRegistry = class {
47945
48143
  overlayUrl;
47946
48144
  overlayFile;
47947
48145
  overlayCacheFile;
48146
+ logger;
47948
48147
  constructor(opts) {
47949
48148
  this.cacheFile = opts.cacheFile;
47950
48149
  this.url = opts.url ?? process.env[ENV_URL_KEY] ?? DEFAULT_URL;
@@ -47958,6 +48157,7 @@ var DefaultModelsRegistry = class {
47958
48157
  this.overlayUrl = opts.overlayUrl;
47959
48158
  this.overlayFile = opts.overlayFile;
47960
48159
  this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path49.join(path49.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
48160
+ this.logger = opts.logger ?? noOpLogger;
47961
48161
  }
47962
48162
  async load(opts = {}) {
47963
48163
  if (this.payload && !opts.force) return this.payload;
@@ -47993,29 +48193,31 @@ var DefaultModelsRegistry = class {
47993
48193
  */
47994
48194
  async loadBase(opts = {}, overlayAvailable = false) {
47995
48195
  if (!opts.force) {
47996
- const cached = await this.readCacheAt(this.cacheFile);
47997
- if (cached && this.isFresh(cached.fetchedAt)) {
47998
- this.fetchedAt = new Date(cached.fetchedAt);
47999
- return cached.payload;
48196
+ const cached2 = await this.readCacheAt(this.cacheFile);
48197
+ if (cached2 && this.isFresh(cached2.fetchedAt)) {
48198
+ this.fetchedAt = new Date(cached2.fetchedAt);
48199
+ return cached2.payload;
48000
48200
  }
48001
48201
  }
48002
48202
  try {
48003
48203
  return await this.refreshBase();
48004
48204
  } catch (err) {
48005
- const cached = await this.readCacheAt(this.cacheFile);
48006
- if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
48007
- this.fetchedAt = new Date(cached.fetchedAt);
48205
+ const cached2 = await this.readCacheAt(this.cacheFile);
48206
+ if (cached2 && this.isWithinMaxStaleAge(cached2.fetchedAt)) {
48207
+ this.fetchedAt = new Date(cached2.fetchedAt);
48008
48208
  const ageSeconds = Math.floor((Date.now() - this.fetchedAt.getTime()) / 1e3);
48009
- console.warn(
48010
- `ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); using stale cache from ${formatAge(ageSeconds)} ago. Run \`wstack models refresh\` to retry.`
48209
+ this.logger.warn(
48210
+ `ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); using stale cache from ${formatAge(ageSeconds)} ago. Run \`wstack models refresh\` to retry.`,
48211
+ { event: "models_registry.stale_cache_fallback" }
48011
48212
  );
48012
- return cached.payload;
48213
+ return cached2.payload;
48013
48214
  }
48014
48215
  if (overlayAvailable) {
48015
- console.warn(
48216
+ this.logger.warn(
48016
48217
  `ModelsRegistry: models.dev unavailable (${toErrorMessage(
48017
48218
  err
48018
- )}); serving curated overlay only.`
48219
+ )}); serving curated overlay only.`,
48220
+ { event: "models_registry.overlay_only_fallback" }
48019
48221
  );
48020
48222
  return {};
48021
48223
  }
@@ -48084,8 +48286,8 @@ var DefaultModelsRegistry = class {
48084
48286
  async loadOverlayFromUrl(opts) {
48085
48287
  if (!this.overlayUrl || !this.overlayCacheFile) return void 0;
48086
48288
  if (!opts.force) {
48087
- const cached = await this.readCacheAt(this.overlayCacheFile);
48088
- if (cached && this.isFresh(cached.fetchedAt)) return cached.payload;
48289
+ const cached2 = await this.readCacheAt(this.overlayCacheFile);
48290
+ if (cached2 && this.isFresh(cached2.fetchedAt)) return cached2.payload;
48089
48291
  }
48090
48292
  try {
48091
48293
  const res = await this.fetchImpl(this.overlayUrl, {
@@ -48108,13 +48310,14 @@ var DefaultModelsRegistry = class {
48108
48310
  });
48109
48311
  return json;
48110
48312
  } catch {
48111
- const cached = await this.readCacheAt(this.overlayCacheFile);
48112
- if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
48113
- const ageSeconds = Math.floor((Date.now() - new Date(cached.fetchedAt).getTime()) / 1e3);
48114
- console.warn(
48115
- `ModelsRegistry: overlay unavailable; using stale overlay from ${formatAge(ageSeconds)} ago.`
48313
+ const cached2 = await this.readCacheAt(this.overlayCacheFile);
48314
+ if (cached2 && this.isWithinMaxStaleAge(cached2.fetchedAt)) {
48315
+ const ageSeconds = Math.floor((Date.now() - new Date(cached2.fetchedAt).getTime()) / 1e3);
48316
+ this.logger.warn(
48317
+ `ModelsRegistry: overlay unavailable; using stale overlay from ${formatAge(ageSeconds)} ago.`,
48318
+ { event: "models_registry.overlay_stale_fallback", ageSeconds }
48116
48319
  );
48117
- return cached.payload;
48320
+ return cached2.payload;
48118
48321
  }
48119
48322
  return void 0;
48120
48323
  }
@@ -48175,9 +48378,9 @@ var DefaultModelsRegistry = class {
48175
48378
  }
48176
48379
  async ageSeconds() {
48177
48380
  if (!this.fetchedAt) {
48178
- const cached = await this.readCacheAt(this.cacheFile);
48179
- if (!cached) return Number.POSITIVE_INFINITY;
48180
- return (Date.now() - new Date(cached.fetchedAt).getTime()) / 1e3;
48381
+ const cached2 = await this.readCacheAt(this.cacheFile);
48382
+ if (!cached2) return Number.POSITIVE_INFINITY;
48383
+ return (Date.now() - new Date(cached2.fetchedAt).getTime()) / 1e3;
48181
48384
  }
48182
48385
  return (Date.now() - this.fetchedAt.getTime()) / 1e3;
48183
48386
  }
@@ -48716,7 +48919,7 @@ async function startMetricsServer(opts) {
48716
48919
 
48717
48920
  // src/observability/otlp-metrics.ts
48718
48921
  var DEFAULT_INTERVAL_MS = 3e4;
48719
- var DEFAULT_TIMEOUT_MS = 1e4;
48922
+ var DEFAULT_TIMEOUT_MS2 = 1e4;
48720
48923
  function joinEndpoint(base) {
48721
48924
  if (/\/v1\/metrics\/?$/.test(base)) return base;
48722
48925
  return base.replace(/\/$/, "") + "/v1/metrics";
@@ -48779,7 +48982,7 @@ function buildOtlpMetricsRequest(sink, opts = {}) {
48779
48982
  function startOtlpMetricsExporter(opts) {
48780
48983
  const url = joinEndpoint(opts.endpoint);
48781
48984
  const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
48782
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
48985
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
48783
48986
  const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
48784
48987
  const onError = opts.onError ?? (() => {
48785
48988
  });
@@ -48869,7 +49072,7 @@ var CapturingSpan = class {
48869
49072
  };
48870
49073
  var DEFAULT_INTERVAL_MS2 = 5e3;
48871
49074
  var DEFAULT_BUFFER_CAP = 2048;
48872
- var DEFAULT_TIMEOUT_MS2 = 1e4;
49075
+ var DEFAULT_TIMEOUT_MS3 = 1e4;
48873
49076
  function joinEndpoint2(base) {
48874
49077
  if (/\/v1\/traces\/?$/.test(base)) return base;
48875
49078
  return base.replace(/\/$/, "") + "/v1/traces";
@@ -48910,7 +49113,7 @@ function startOtlpTraceExporter(opts) {
48910
49113
  const url = joinEndpoint2(opts.endpoint);
48911
49114
  const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS2;
48912
49115
  const maxBuffered = opts.maxBufferedSpans ?? DEFAULT_BUFFER_CAP;
48913
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
49116
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
48914
49117
  const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
48915
49118
  const onError = opts.onError ?? (() => {
48916
49119
  });
@@ -49266,9 +49469,6 @@ var DefaultPermissionPolicy = class {
49266
49469
  loaded = false;
49267
49470
  trustFile;
49268
49471
  yolo;
49269
- yoloDestructive;
49270
- /** Deprecated compatibility flag; no longer gates YOLO calls. */
49271
- confirmDestructive;
49272
49472
  /**
49273
49473
  * Session-scoped "soft deny" map. When the user presses 'n' (block once),
49274
49474
  * the tool+pattern is added here. If the LLM retries in the same session,
@@ -49321,8 +49521,6 @@ var DefaultPermissionPolicy = class {
49321
49521
  constructor(opts) {
49322
49522
  this.trustFile = opts.trustFile;
49323
49523
  this.yolo = opts.yolo ?? false;
49324
- this.yoloDestructive = opts.yoloDestructive ?? opts.forceAllYolo ?? false;
49325
- this.confirmDestructive = opts.confirmDestructive ?? false;
49326
49524
  this.promptDelegate = opts.promptDelegate;
49327
49525
  }
49328
49526
  /**
@@ -49343,24 +49541,6 @@ var DefaultPermissionPolicy = class {
49343
49541
  getYolo() {
49344
49542
  return this.yolo;
49345
49543
  }
49346
- /** Toggle the destructive YOLO override at runtime. */
49347
- setYoloDestructive(enabled) {
49348
- if (this.yoloDestructive !== enabled) this._evalCache.clear();
49349
- this.yoloDestructive = enabled;
49350
- }
49351
- /** Check whether the destructive YOLO override is active. */
49352
- getYoloDestructive() {
49353
- return this.yoloDestructive;
49354
- }
49355
- /** Toggle deprecated destructive confirmation compatibility flag. */
49356
- setConfirmDestructive(enabled) {
49357
- if (this.confirmDestructive !== enabled) this._evalCache.clear();
49358
- this.confirmDestructive = enabled;
49359
- }
49360
- /** Check deprecated destructive confirmation compatibility flag. */
49361
- getConfirmDestructive() {
49362
- return this.confirmDestructive;
49363
- }
49364
49544
  /** Read-only diagnostics for policy inspector/editor surfaces. */
49365
49545
  getPolicyDiagnostics() {
49366
49546
  return this.policyDiagnostics.map((diagnostic) => ({ ...diagnostic }));
@@ -49441,8 +49621,8 @@ var DefaultPermissionPolicy = class {
49441
49621
  const subject = subjectForToolInput(tool.name, input, tool.subjectKey);
49442
49622
  const cacheKey = `${tool.name}::${subject ?? tool.name}`;
49443
49623
  if (tool.name !== "write") {
49444
- const cached = this._evalCache.get(cacheKey);
49445
- if (cached !== void 0) return cached;
49624
+ const cached2 = this._evalCache.get(cacheKey);
49625
+ if (cached2 !== void 0) return cached2;
49446
49626
  }
49447
49627
  if (this.sessionDenied.has(cacheKey)) {
49448
49628
  this._logDeny(tool.name, subject, "session soft deny (user pressed no)");
@@ -53519,7 +53699,7 @@ function applyModelRuntime(req, opts) {
53519
53699
  }
53520
53700
 
53521
53701
  // src/execution/one-shot-llm.ts
53522
- var DEFAULT_TIMEOUT_MS3 = 3e4;
53702
+ var DEFAULT_TIMEOUT_MS4 = 3e4;
53523
53703
  var DEFAULT_MAX_TOKENS = 1024;
53524
53704
  var OneShotOrchestrator = class {
53525
53705
  opts;
@@ -53599,6 +53779,9 @@ var OneShotOrchestrator = class {
53599
53779
  for (const entry of usableChain) {
53600
53780
  if (!evaluateModelCalendar(config.modelAvailabilitySchedule, entry.providerId, entry.model).allowed)
53601
53781
  continue;
53782
+ if (tracker && !tracker.isAvailable(entry.providerId, entry.model)) {
53783
+ continue;
53784
+ }
53602
53785
  if (entry.providerId === provider.id && entry.model === target.model) continue;
53603
53786
  let fbProvider;
53604
53787
  try {
@@ -53682,7 +53865,7 @@ var OneShotOrchestrator = class {
53682
53865
  * timeout, composed with external cancellation when the caller supplies it.
53683
53866
  */
53684
53867
  resolveSignal(input) {
53685
- const timeoutSignal = AbortSignal.timeout(input.timeoutMs ?? DEFAULT_TIMEOUT_MS3);
53868
+ const timeoutSignal = AbortSignal.timeout(input.timeoutMs ?? DEFAULT_TIMEOUT_MS4);
53686
53869
  return input.signal ? AbortSignal.any([input.signal, timeoutSignal]) : timeoutSignal;
53687
53870
  }
53688
53871
  /**
@@ -54718,6 +54901,7 @@ var PhaseOrchestrator = class {
54718
54901
  taskRetryCounts = /* @__PURE__ */ new Map();
54719
54902
  // ── Git-worktree isolation (optional) ──────────────────────────────────────
54720
54903
  worktrees;
54904
+ logger;
54721
54905
  /** Per-phase worktree handles, keyed by phase id. */
54722
54906
  phaseWorktrees = /* @__PURE__ */ new Map();
54723
54907
  /** Serializes all merges back to the base branch (one at a time). */
@@ -54729,6 +54913,7 @@ var PhaseOrchestrator = class {
54729
54913
  this.ctx = opts.ctx;
54730
54914
  this.events = opts.events ?? this.createNoopEventBus();
54731
54915
  this.worktrees = opts.worktrees;
54916
+ this.logger = opts.logger ?? noOpLogger;
54732
54917
  this.opts = {
54733
54918
  maxConcurrentPhases: opts.maxConcurrentPhases ?? 1,
54734
54919
  maxConcurrentTasks: opts.maxConcurrentTasks ?? 2,
@@ -54801,12 +54986,7 @@ var PhaseOrchestrator = class {
54801
54986
  await Promise.allSettled([...this.phaseMergePromise.values()]);
54802
54987
  await this.mergeQueue.catch((err) => {
54803
54988
  const msg = toErrorMessage(err);
54804
- console.warn(JSON.stringify({
54805
- level: "warn",
54806
- event: "orchestrator.merge_queue_failed",
54807
- message: msg,
54808
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
54809
- }));
54989
+ this.logger.warn(msg, { event: "orchestrator.merge_queue_failed" });
54810
54990
  });
54811
54991
  }
54812
54992
  /** Pause: active phases continue, but no new phase starts. */
@@ -54818,12 +54998,7 @@ var PhaseOrchestrator = class {
54818
54998
  this.paused = false;
54819
54999
  this.tick().catch((err) => {
54820
55000
  const msg = toErrorMessage(err);
54821
- console.error(JSON.stringify({
54822
- level: "error",
54823
- event: "orchestrator.tick_failed",
54824
- message: msg,
54825
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
54826
- }));
55001
+ this.logger.error(msg, { event: "orchestrator.tick_failed" });
54827
55002
  });
54828
55003
  }
54829
55004
  /** Stop completely, including active phases. */
@@ -55044,13 +55219,7 @@ var PhaseOrchestrator = class {
55044
55219
  await Promise.allSettled(depPromises);
55045
55220
  this.mergeQueue = this.mergeQueue.then(() => this.mergeOne(phase, handle)).catch((err) => {
55046
55221
  const msg = toErrorMessage(err);
55047
- console.error(JSON.stringify({
55048
- level: "error",
55049
- event: "orchestrator.merge_failed",
55050
- phaseId: phase.id,
55051
- message: msg,
55052
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
55053
- }));
55222
+ this.logger.error(msg, { event: "orchestrator.merge_failed", phaseId: phase.id });
55054
55223
  this.markPhaseMergeFailed(phase, msg);
55055
55224
  });
55056
55225
  await this.mergeQueue;
@@ -55294,8 +55463,8 @@ var PhaseOrchestrator = class {
55294
55463
  });
55295
55464
  }
55296
55465
  getTrackerForPhase(phase) {
55297
- const cached = this.trackerCache.get(phase.id);
55298
- if (cached) return cached;
55466
+ const cached2 = this.trackerCache.get(phase.id);
55467
+ if (cached2) return cached2;
55299
55468
  const tracker = new TaskTracker({ store: new DefaultTaskStore() });
55300
55469
  tracker.setGraph(phase.taskGraph);
55301
55470
  this.trackerCache.set(phase.id, tracker);
@@ -56315,7 +56484,7 @@ var CheckpointManager = class {
56315
56484
 
56316
56485
  // src/hooks/shell-executor.ts
56317
56486
  import { spawn as spawn4 } from "node:child_process";
56318
- var DEFAULT_TIMEOUT_MS4 = 5e3;
56487
+ var DEFAULT_TIMEOUT_MS5 = 5e3;
56319
56488
  var MAX_OUTPUT_BYTES = 64 * 1024;
56320
56489
  var ALLOWED_SHELL_COMMANDS = /* @__PURE__ */ new Set([
56321
56490
  // POSIX shells + Windows shells
@@ -56441,7 +56610,7 @@ async function runShellHook(spec, input, logger) {
56441
56610
  return result.outcome;
56442
56611
  }
56443
56612
  async function runShellHookDetailed(spec, input, logger, options = {}) {
56444
- const timeoutMs = Math.max(1, Math.min(spec.timeoutMs ?? DEFAULT_TIMEOUT_MS4, 10 * 6e4));
56613
+ const timeoutMs = Math.max(1, Math.min(spec.timeoutMs ?? DEFAULT_TIMEOUT_MS5, 10 * 6e4));
56445
56614
  const argv = isCommandAllowed(spec.command);
56446
56615
  if (!argv) {
56447
56616
  logger?.warn?.(`hook rejected: command not in allowlist: ${spec.command}`);
@@ -56594,7 +56763,7 @@ function parseHookOutcome(stdout) {
56594
56763
  }
56595
56764
 
56596
56765
  // src/hooks/http-executor.ts
56597
- var DEFAULT_TIMEOUT_MS5 = 5e3;
56766
+ var DEFAULT_TIMEOUT_MS6 = 5e3;
56598
56767
  var MAX_OUTPUT_BYTES2 = 64 * 1024;
56599
56768
  function isAllowedUrl(raw) {
56600
56769
  try {
@@ -56616,7 +56785,7 @@ async function runHttpHookDetailed(spec, input, logger, options = {}) {
56616
56785
  }
56617
56786
  };
56618
56787
  }
56619
- const timeoutMs = Math.max(1, Math.min(spec.timeoutMs ?? DEFAULT_TIMEOUT_MS5, 10 * 6e4));
56788
+ const timeoutMs = Math.max(1, Math.min(spec.timeoutMs ?? DEFAULT_TIMEOUT_MS6, 10 * 6e4));
56620
56789
  const timeoutController = new AbortController();
56621
56790
  const timer = setTimeout(() => timeoutController.abort(new Error("hook timeout")), timeoutMs);
56622
56791
  timer.unref?.();
@@ -56821,7 +56990,7 @@ function hookMatcherMatches(matcher, toolName) {
56821
56990
  }
56822
56991
 
56823
56992
  // src/hooks/runner.ts
56824
- var DEFAULT_TIMEOUT_MS6 = 5e3;
56993
+ var DEFAULT_TIMEOUT_MS7 = 5e3;
56825
56994
  var MAX_TIMEOUT_MS = 10 * 6e4;
56826
56995
  function normalizePreToolOutcome(outcome) {
56827
56996
  if ("action" in outcome) return outcome;
@@ -56973,7 +57142,7 @@ var HookRunner = class {
56973
57142
  return this.registry.list(event).filter((e) => hookMatcherMatches(e.matcher, toolName));
56974
57143
  }
56975
57144
  async invoke(entry, payload, env) {
56976
- const allowNonPolicy = this.opts.allowNonPolicy ?? this.opts.allowShell ?? true;
57145
+ const allowNonPolicy = this.opts.allowNonPolicy ?? true;
56977
57146
  if (!allowNonPolicy && !entry.policy) return null;
56978
57147
  let result;
56979
57148
  if (entry.kind === "inprocess") {
@@ -57007,7 +57176,7 @@ var HookRunner = class {
57007
57176
  return this.failureOutcome(entry, payload, result.failure);
57008
57177
  }
57009
57178
  async invokeInProcess(entry, payload, env) {
57010
- const timeoutMs = Math.max(1, Math.min(entry.timeoutMs ?? DEFAULT_TIMEOUT_MS6, MAX_TIMEOUT_MS));
57179
+ const timeoutMs = Math.max(1, Math.min(entry.timeoutMs ?? DEFAULT_TIMEOUT_MS7, MAX_TIMEOUT_MS));
57011
57180
  const controller = new AbortController();
57012
57181
  let timedOut = false;
57013
57182
  const onParentAbort = () => controller.abort(env.signal?.reason);
@@ -59720,8 +59889,8 @@ function makePluginLLM(owner, hostLLM, providerRegistry, config, getLiveConfig,
59720
59889
  return { provider: liveProvider, providerName };
59721
59890
  }
59722
59891
  const cacheKey = `${providerName}|${model}`;
59723
- const cached = providerCache.get(cacheKey);
59724
- if (cached) return { provider: cached, providerName };
59892
+ const cached2 = providerCache.get(cacheKey);
59893
+ if (cached2) return { provider: cached2, providerName };
59725
59894
  let created;
59726
59895
  if (hostLLM.createProvider) {
59727
59896
  created = hostLLM.createProvider(providerName, model);
@@ -64102,9 +64271,9 @@ var ReplayProviderRunner = class {
64102
64271
  opts;
64103
64272
  async run(runOpts) {
64104
64273
  const hash4 = hashRequest(runOpts.request);
64105
- const cached = await this.opts.log.lookup(this.opts.sessionId, hash4);
64274
+ const cached2 = await this.opts.log.lookup(this.opts.sessionId, hash4);
64106
64275
  if (this.opts.mode === "replay") {
64107
- if (!cached) {
64276
+ if (!cached2) {
64108
64277
  this.opts.logger?.warn?.(
64109
64278
  `replay: no recorded response for hash ${hash4} (model ${runOpts.request.model})`
64110
64279
  );
@@ -64113,15 +64282,15 @@ var ReplayProviderRunner = class {
64113
64282
  );
64114
64283
  }
64115
64284
  this.opts.logger?.debug?.(
64116
- `replay: served cached response for hash ${hash4} (recorded ${cached.ts})`
64285
+ `replay: served cached response for hash ${hash4} (recorded ${cached2.ts})`
64117
64286
  );
64118
- return cached.response;
64287
+ return cached2.response;
64119
64288
  }
64120
- if (this.opts.mode === "auto" && cached) {
64289
+ if (this.opts.mode === "auto" && cached2) {
64121
64290
  this.opts.logger?.debug?.(
64122
64291
  `replay: auto-hit hash ${hash4}, served cached response`
64123
64292
  );
64124
- return cached.response;
64293
+ return cached2.response;
64125
64294
  }
64126
64295
  const response = await this.inner.run(runOpts);
64127
64296
  await this.opts.log.record({
@@ -64341,9 +64510,9 @@ var FileMemoryBackend = class {
64341
64510
  }
64342
64511
  async getIndex(file, scope) {
64343
64512
  const mtime = await this.getMtime(file);
64344
- const cached = this.indexCache.get(file);
64345
- if (cached && cached.mtimeMs === mtime) {
64346
- return cached;
64513
+ const cached2 = this.indexCache.get(file);
64514
+ if (cached2 && cached2.mtimeMs === mtime) {
64515
+ return cached2;
64347
64516
  }
64348
64517
  const entries = await this.loadEntries(file, scope, mtime);
64349
64518
  const index = buildInvertedIndex(entries);
@@ -70189,6 +70358,8 @@ export {
70189
70358
  MAILBOX_HEALTH_DEFAULT_FROM,
70190
70359
  MAILBOX_HEALTH_DEFAULT_INTERVAL_MS,
70191
70360
  MAILBOX_HEALTH_DEFAULT_TIMEOUT_MS,
70361
+ MAILBOX_HTTP_DEFAULT_MAX_AGE_MS,
70362
+ MAILBOX_HTTP_MAX_AGE_CEILING_MS,
70192
70363
  MAILBOX_HTTP_MAX_BODY_BYTES,
70193
70364
  MAILBOX_HTTP_RATE_LIMIT_PER_MINUTE,
70194
70365
  MAILBOX_HTTP_RATE_LIMIT_WINDOW_MS,
@@ -70352,6 +70523,7 @@ export {
70352
70523
  buildTranscriptFromEvents,
70353
70524
  buildUserContentBlocks,
70354
70525
  canonicalProjectRoot,
70526
+ checkConnectivity,
70355
70527
  childChronicleContext,
70356
70528
  classifyFamily,
70357
70529
  classifyMailboxRecipient,
@@ -70728,6 +70900,7 @@ export {
70728
70900
  repairToolUseAdjacency,
70729
70901
  repeatedReadPressure,
70730
70902
  resetCalibration,
70903
+ resetConnectivityCache,
70731
70904
  resolveAuditLevel,
70732
70905
  resolveBrainConfigDefaults,
70733
70906
  resolveBundledDesignKitsDir,