@yugenlab/vaayu 0.1.0 → 0.1.2

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 (31) hide show
  1. package/chunks/{chunk-L7JICQBW.js → chunk-2ARPXEDC.js} +5 -3
  2. package/chunks/{chunk-IIET2K6D.js → chunk-5Z2BKSFF.js} +81 -603
  3. package/chunks/{chunk-MINFB5LT.js → chunk-C76USAC5.js} +30 -6
  4. package/chunks/{chunk-URGEODS5.js → chunk-DQMAQ2VL.js} +4 -4
  5. package/chunks/{chunk-JZU37VQ5.js → chunk-F35MWELH.js} +6 -4
  6. package/chunks/chunk-F4T7POKM.js +545 -0
  7. package/chunks/{chunk-H76V36OF.js → chunk-FPNQLJLD.js} +4 -4
  8. package/chunks/{chunk-KDRROLVN.js → chunk-NBXCXQ3H.js} +2 -2
  9. package/chunks/{chunk-YSU3BWV6.js → chunk-OWBBY5XP.js} +2 -2
  10. package/chunks/{chunk-SMVJRPAH.js → chunk-PRXQW76U.js} +46 -6
  11. package/chunks/{chunk-S4TBVCL2.js → chunk-SLA2OIMG.js} +5 -3
  12. package/chunks/chunk-UQLPHNGH.js +123 -0
  13. package/chunks/{chunk-ITIVYGUG.js → chunk-UW6E7IC4.js} +6 -4
  14. package/chunks/{chunk-HAPVUJ6A.js → chunk-XRHUKKBC.js} +9 -7
  15. package/chunks/{chunk-U6OLJ36B.js → chunk-YJRXLRTE.js} +21 -122
  16. package/chunks/{consolidation-indexer-TOTTDZXW.js → consolidation-indexer-A46RJU4R.js} +6 -5
  17. package/chunks/{day-consolidation-NKO63HZQ.js → day-consolidation-GQ2FDCR2.js} +2 -2
  18. package/chunks/{graphrag-ZI2FSU7S.js → graphrag-6YZ5YPLK.js} +4 -3
  19. package/chunks/{hierarchical-temporal-search-ZD46UMKR.js → hierarchical-temporal-search-VA4D3SON.js} +2 -2
  20. package/chunks/{hybrid-search-ZVLZVGFS.js → hybrid-search-6XMUT66S.js} +6 -5
  21. package/chunks/periodic-consolidation-N5MR77ZN.js +11 -0
  22. package/chunks/{postgres-3ZXBYTPC.js → postgres-WLH3D5HG.js} +2 -2
  23. package/chunks/{recall-GMVHWQWW.js → recall-THTI6ZO2.js} +5 -4
  24. package/chunks/{search-7HZETVMZ.js → search-V7DJ3VNL.js} +5 -4
  25. package/chunks/{session-store-XKPGKXUS.js → session-store-GRKGTMHI.js} +4 -3
  26. package/chunks/{sqlite-JPF5TICX.js → sqlite-DHUQGPR5.js} +2 -2
  27. package/chunks/{src-QAXOD5SB.js → src-54LTTDTH.js} +18 -14
  28. package/chunks/vasana-engine-Z4RXW2SB.js +10 -0
  29. package/gateway.js +1429 -264
  30. package/package.json +1 -1
  31. package/chunks/periodic-consolidation-BPKOZDGB.js +0 -10
package/gateway.js CHANGED
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- SqliteStorage
4
- } from "./chunks/chunk-SMVJRPAH.js";
5
- import "./chunks/chunk-MINFB5LT.js";
6
- import "./chunks/chunk-IIET2K6D.js";
7
- import "./chunks/chunk-H76V36OF.js";
8
- import "./chunks/chunk-HAPVUJ6A.js";
9
- import "./chunks/chunk-URGEODS5.js";
10
- import "./chunks/chunk-YSU3BWV6.js";
11
- import "./chunks/chunk-IEKAYVA3.js";
12
2
  import {
13
3
  buildAgentTree,
14
4
  buildKaalaHealthNode
15
5
  } from "./chunks/chunk-G5VYCA6O.js";
6
+ import {
7
+ SqliteStorage
8
+ } from "./chunks/chunk-PRXQW76U.js";
9
+ import "./chunks/chunk-C76USAC5.js";
10
+ import "./chunks/chunk-5Z2BKSFF.js";
11
+ import "./chunks/chunk-F4T7POKM.js";
12
+ import "./chunks/chunk-FPNQLJLD.js";
13
+ import "./chunks/chunk-XRHUKKBC.js";
14
+ import "./chunks/chunk-DQMAQ2VL.js";
15
+ import "./chunks/chunk-OWBBY5XP.js";
16
+ import "./chunks/chunk-IEKAYVA3.js";
16
17
  import {
17
18
  IMessageAdapter,
18
19
  TelegramAdapter,
@@ -22,13 +23,13 @@ import {
22
23
  require_lib,
23
24
  validateMantra
24
25
  } from "./chunks/chunk-MJ74G5RB.js";
25
- import "./chunks/chunk-S4TBVCL2.js";
26
- import "./chunks/chunk-JZU37VQ5.js";
26
+ import "./chunks/chunk-SLA2OIMG.js";
27
+ import "./chunks/chunk-F35MWELH.js";
27
28
  import "./chunks/chunk-JAWZ7ANC.js";
28
- import "./chunks/chunk-KDRROLVN.js";
29
+ import "./chunks/chunk-NBXCXQ3H.js";
29
30
  import {
30
31
  searchSessions
31
- } from "./chunks/chunk-ITIVYGUG.js";
32
+ } from "./chunks/chunk-UW6E7IC4.js";
32
33
  import "./chunks/chunk-E5A3SCDJ.js";
33
34
  import {
34
35
  addTurn,
@@ -39,8 +40,9 @@ import {
39
40
  listTurnsWithTimestamps,
40
41
  loadSession,
41
42
  updateSessionMeta
42
- } from "./chunks/chunk-L7JICQBW.js";
43
- import "./chunks/chunk-U6OLJ36B.js";
43
+ } from "./chunks/chunk-2ARPXEDC.js";
44
+ import "./chunks/chunk-YJRXLRTE.js";
45
+ import "./chunks/chunk-UQLPHNGH.js";
44
46
  import "./chunks/chunk-KC6NRZ7U.js";
45
47
  import {
46
48
  __commonJS,
@@ -11313,7 +11315,7 @@ var require_websocket_server = __commonJS({
11313
11315
  });
11314
11316
 
11315
11317
  // apps/gateway/dist/agent/loop.js
11316
- import { randomUUID as randomUUID15 } from "node:crypto";
11318
+ import { randomUUID as randomUUID16 } from "node:crypto";
11317
11319
 
11318
11320
  // packages/guardian/src/guardian.js
11319
11321
  import * as crypto3 from "node:crypto";
@@ -12953,7 +12955,7 @@ async function appendAssistantEvent(storage, sessionId, content, metadata, sourc
12953
12955
  return event;
12954
12956
  }
12955
12957
  function buildEarlyResult(session, model, content, routingDecision, routingResult) {
12956
- const includeRouting = Boolean(routingDecision || routingResult.budget?.applied || routingResult.deterministicTrace || routingResult.retrievalGate);
12958
+ const includeRouting = Boolean(routingDecision || routingResult.budget?.applied || routingResult.deterministicTrace || routingResult.retrievalGate || routingResult.scoring || routingResult.providerResolution || routingResult.smalltalkFastLane || routingResult.escalation || routingResult.policyClassifier || routingResult.semanticCache || routingResult.activeToolDiscovery);
12957
12959
  const normalized = normalizeAgentReply(content);
12958
12960
  return {
12959
12961
  session,
@@ -13098,7 +13100,7 @@ async function resolveSessionRoute(params) {
13098
13100
  }
13099
13101
 
13100
13102
  // apps/gateway/dist/agent/loop-execute.js
13101
- import { randomUUID as randomUUID13 } from "node:crypto";
13103
+ import { randomUUID as randomUUID14 } from "node:crypto";
13102
13104
 
13103
13105
  // apps/gateway/dist/model-catalog.js
13104
13106
  var FREE = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
@@ -15853,8 +15855,8 @@ function createRegistry(config) {
15853
15855
  defaultId: config.default,
15854
15856
  policy: config.policy
15855
15857
  });
15856
- const entries = config.entries ?? {};
15857
- for (const [id, providerConfig] of Object.entries(entries)) {
15858
+ const entries2 = config.entries ?? {};
15859
+ for (const [id, providerConfig] of Object.entries(entries2)) {
15858
15860
  const provider = createProvider(id, providerConfig);
15859
15861
  registry.register(provider, {
15860
15862
  mode: providerModeFor(providerConfig.type)
@@ -19720,8 +19722,8 @@ function slugifySkillName(name) {
19720
19722
  function copyDir(src, dest) {
19721
19723
  if (!fs8.existsSync(src)) return;
19722
19724
  fs8.mkdirSync(dest, { recursive: true });
19723
- const entries = fs8.readdirSync(src, { withFileTypes: true });
19724
- for (const entry of entries) {
19725
+ const entries2 = fs8.readdirSync(src, { withFileTypes: true });
19726
+ for (const entry of entries2) {
19725
19727
  const from = path8.join(src, entry.name);
19726
19728
  const to = path8.join(dest, entry.name);
19727
19729
  if (entry.isDirectory()) {
@@ -23152,9 +23154,9 @@ function buildSkillUpdateTools(dataDir) {
23152
23154
  error: { code: "invalid_registry", message: error instanceof Error ? error.message : String(error) }
23153
23155
  };
23154
23156
  }
23155
- const entries = Array.isArray(registry.skills) ? registry.skills : [];
23157
+ const entries2 = Array.isArray(registry.skills) ? registry.skills : [];
23156
23158
  const targetId = payload?.id?.trim();
23157
- const selected = targetId ? entries.filter((e) => e.id === targetId) : entries;
23159
+ const selected = targetId ? entries2.filter((e) => e.id === targetId) : entries2;
23158
23160
  if (!selected.length) {
23159
23161
  return { ok: false, error: { code: "missing_skill", message: "No registry entry found." } };
23160
23162
  }
@@ -23440,13 +23442,13 @@ function collectSkillScanFiles(rootDir) {
23440
23442
  while (queue.length) {
23441
23443
  const dir = queue.shift();
23442
23444
  if (!dir) continue;
23443
- let entries;
23445
+ let entries2;
23444
23446
  try {
23445
- entries = fs15.readdirSync(dir, { withFileTypes: true });
23447
+ entries2 = fs15.readdirSync(dir, { withFileTypes: true });
23446
23448
  } catch {
23447
23449
  continue;
23448
23450
  }
23449
- for (const entry of entries) {
23451
+ for (const entry of entries2) {
23450
23452
  if (entry.name.startsWith(".")) continue;
23451
23453
  const full = path15.join(dir, entry.name);
23452
23454
  if (entry.isDirectory()) {
@@ -25675,6 +25677,9 @@ Use /onboard to see your full setup status.`;
25675
25677
  return null;
25676
25678
  }
25677
25679
 
25680
+ // apps/gateway/dist/agent/quick-responses.js
25681
+ import { randomUUID as randomUUID6 } from "node:crypto";
25682
+
25678
25683
  // apps/gateway/dist/runtime/continuity-prompt.js
25679
25684
  var DEFAULT_LOOKBACK_MS = 24 * 60 * 60 * 1e3;
25680
25685
  var DEFAULT_PROMPT_TTL_MS = 30 * 60 * 1e3;
@@ -26122,12 +26127,64 @@ async function handleQuickResponses(params) {
26122
26127
  }, message);
26123
26128
  return buildEarlyResult(session, model, reply, routingDecision, routingResult);
26124
26129
  };
26125
- const emitSmalltalk = async (kind) => emitLocal(kind === "greeting" ? "smalltalk.greeting" : "smalltalk.ack", buildSmalltalkReply2(profile, kind, message.channel, locale));
26126
- const emitIdentity = async () => emitLocal("smalltalk.identity", buildIdentityReply({
26127
- assistantName: profile.assistantName?.trim() || "Vaayu",
26128
- locale
26129
- }));
26130
+ const emitSmalltalkFastLane = async (params2) => {
26131
+ routingResult.smalltalkFastLane = {
26132
+ matched: params2.matched,
26133
+ mode: params2.mode,
26134
+ intent: params2.intent,
26135
+ confidence: params2.confidence,
26136
+ threshold: params2.threshold,
26137
+ metric: params2.matched ? "smalltalk.fast_lane.hit" : "smalltalk.fast_lane.miss"
26138
+ };
26139
+ try {
26140
+ await storage.appendAudit?.({
26141
+ id: randomUUID6(),
26142
+ time: (/* @__PURE__ */ new Date()).toISOString(),
26143
+ type: routingResult.smalltalkFastLane.metric,
26144
+ payload: {
26145
+ sessionId: session.id,
26146
+ locale,
26147
+ mode: params2.mode,
26148
+ intent: params2.intent,
26149
+ confidence: params2.confidence,
26150
+ threshold: params2.threshold,
26151
+ matched: params2.matched
26152
+ }
26153
+ });
26154
+ } catch {
26155
+ }
26156
+ };
26157
+ const emitSmalltalk = async (kind) => {
26158
+ await emitSmalltalkFastLane({
26159
+ matched: true,
26160
+ mode: kind === "greeting" ? "greeting_rule" : "ack_rule",
26161
+ intent: kind === "greeting" ? "smalltalk.greeting" : "smalltalk.ack",
26162
+ confidence: 1,
26163
+ threshold: 1
26164
+ });
26165
+ return emitLocal(kind === "greeting" ? "smalltalk.greeting" : "smalltalk.ack", buildSmalltalkReply2(profile, kind, message.channel, locale));
26166
+ };
26167
+ const emitIdentity = async () => {
26168
+ await emitSmalltalkFastLane({
26169
+ matched: true,
26170
+ mode: "identity_rule",
26171
+ intent: "smalltalk.identity",
26172
+ confidence: 1,
26173
+ threshold: 1
26174
+ });
26175
+ return emitLocal("smalltalk.identity", buildIdentityReply({
26176
+ assistantName: profile.assistantName?.trim() || "Vaayu",
26177
+ locale
26178
+ }));
26179
+ };
26130
26180
  if (SMALLTALK_CHECKIN_RE.test(trimmed)) {
26181
+ await emitSmalltalkFastLane({
26182
+ matched: true,
26183
+ mode: "checkin_rule",
26184
+ intent: "smalltalk.checkin",
26185
+ confidence: 1,
26186
+ threshold: 1
26187
+ });
26131
26188
  return emitLocal("smalltalk.checkin", buildCheckinReply({
26132
26189
  assistantName: profile.assistantName?.trim() || "Vaayu",
26133
26190
  locale
@@ -26140,6 +26197,13 @@ async function handleQuickResponses(params) {
26140
26197
  return emitSmalltalk("ack");
26141
26198
  }
26142
26199
  if (SMALLTALK_JOKE_RE.test(trimmed)) {
26200
+ await emitSmalltalkFastLane({
26201
+ matched: true,
26202
+ mode: "joke_rule",
26203
+ intent: "smalltalk.joke",
26204
+ confidence: 1,
26205
+ threshold: 1
26206
+ });
26143
26207
  return emitLocal("smalltalk.joke", buildJokeReply(locale));
26144
26208
  }
26145
26209
  if (SMALLTALK_IDENTITY_RE.test(trimmed)) {
@@ -26168,10 +26232,33 @@ async function handleQuickResponses(params) {
26168
26232
  });
26169
26233
  const confidence = normalizeConfidence(interpreted?.confidence);
26170
26234
  if (interpreted?.intent === "smalltalk.greeting" && confidence >= SEMANTIC_SMALLTALK_CONFIDENCE) {
26171
- return emitSmalltalk("greeting");
26235
+ await emitSmalltalkFastLane({
26236
+ matched: true,
26237
+ mode: "semantic_greeting",
26238
+ intent: interpreted.intent,
26239
+ confidence,
26240
+ threshold: SEMANTIC_SMALLTALK_CONFIDENCE
26241
+ });
26242
+ return emitLocal("smalltalk.greeting", buildSmalltalkReply2(profile, "greeting", message.channel, locale));
26172
26243
  }
26173
26244
  if (interpreted?.intent === "smalltalk.ack" && confidence >= SEMANTIC_SMALLTALK_CONFIDENCE) {
26174
- return emitSmalltalk("ack");
26245
+ await emitSmalltalkFastLane({
26246
+ matched: true,
26247
+ mode: "semantic_ack",
26248
+ intent: interpreted.intent,
26249
+ confidence,
26250
+ threshold: SEMANTIC_SMALLTALK_CONFIDENCE
26251
+ });
26252
+ return emitLocal("smalltalk.ack", buildSmalltalkReply2(profile, "ack", message.channel, locale));
26253
+ }
26254
+ if (interpreted?.intent === "smalltalk.greeting" || interpreted?.intent === "smalltalk.ack") {
26255
+ await emitSmalltalkFastLane({
26256
+ matched: false,
26257
+ mode: interpreted.intent === "smalltalk.greeting" ? "semantic_greeting" : "semantic_ack",
26258
+ intent: interpreted.intent,
26259
+ confidence,
26260
+ threshold: SEMANTIC_SMALLTALK_CONFIDENCE
26261
+ });
26175
26262
  }
26176
26263
  } catch {
26177
26264
  }
@@ -26272,7 +26359,7 @@ ${cross.summary}` : `Here\u2019s what we were discussing${deviceLabel}: ${cross.
26272
26359
  }
26273
26360
 
26274
26361
  // apps/gateway/dist/agent/intents/free-text.js
26275
- import { randomUUID as randomUUID6 } from "node:crypto";
26362
+ import { randomUUID as randomUUID7 } from "node:crypto";
26276
26363
 
26277
26364
  // apps/gateway/dist/runtime/text-smalltalk.js
26278
26365
  var WEATHER_TERMS = [
@@ -27670,7 +27757,7 @@ async function handleFreeTextIntents(params) {
27670
27757
  await storage.clearSessionPrefs(session.id);
27671
27758
  const updated = await storage.setSessionPrefs(session.id, { autoRouting: true });
27672
27759
  await storage.appendAudit({
27673
- id: randomUUID6(),
27760
+ id: randomUUID7(),
27674
27761
  time: (/* @__PURE__ */ new Date()).toISOString(),
27675
27762
  type: "session.prefs.set",
27676
27763
  payload: {
@@ -27698,7 +27785,7 @@ async function handleFreeTextIntents(params) {
27698
27785
  autoRouting: false
27699
27786
  });
27700
27787
  await storage.appendAudit({
27701
- id: randomUUID6(),
27788
+ id: randomUUID7(),
27702
27789
  time: (/* @__PURE__ */ new Date()).toISOString(),
27703
27790
  type: "session.prefs.set",
27704
27791
  payload: {
@@ -27734,7 +27821,7 @@ async function handleFreeTextIntents(params) {
27734
27821
  autoRouting: false
27735
27822
  });
27736
27823
  await storage.appendAudit({
27737
- id: randomUUID6(),
27824
+ id: randomUUID7(),
27738
27825
  time: (/* @__PURE__ */ new Date()).toISOString(),
27739
27826
  type: "session.prefs.set",
27740
27827
  payload: {
@@ -27773,7 +27860,7 @@ async function handleFreeTextIntents(params) {
27773
27860
  autoRouting: false
27774
27861
  });
27775
27862
  await storage.appendAudit({
27776
- id: randomUUID6(),
27863
+ id: randomUUID7(),
27777
27864
  time: (/* @__PURE__ */ new Date()).toISOString(),
27778
27865
  type: "session.prefs.set",
27779
27866
  payload: {
@@ -27892,7 +27979,7 @@ var LOG_LEVELS = ["debug", "info", "warn", "error"];
27892
27979
  var REDACTED2 = "[REDACTED]";
27893
27980
  var MAX_DEPTH = 6;
27894
27981
  var SENSITIVE_KEY_RE = /(token|api[_-]?key|authorization|x-api-key|secret|password|cookie|set-cookie|bearer)/i;
27895
- var TOKEN_LIKE_RE = /(sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)/g;
27982
+ var TOKEN_LIKE_RE = /(sk-[A-Za-z0-9]{20,}|sk-ant-[A-Za-z0-9_-]{16,}|ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|GOCSPX-[A-Za-z0-9_-]{20,}|AIza[0-9A-Za-z_-]{30,}|AKIA[0-9A-Z]{16}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)/g;
27896
27983
  var BEARER_RE = /Bearer\s+[A-Za-z0-9._-]+/g;
27897
27984
  var PRETTY_ALLOWED = /* @__PURE__ */ new Set(["json", "pretty"]);
27898
27985
  var PRETTY_LEVEL_COLOR = {
@@ -28190,13 +28277,13 @@ function detectMissingCapability(toolName, error) {
28190
28277
  }
28191
28278
 
28192
28279
  // apps/gateway/dist/agent/tool-runner.js
28193
- import { randomUUID as randomUUID9 } from "node:crypto";
28280
+ import { randomUUID as randomUUID10 } from "node:crypto";
28194
28281
 
28195
28282
  // apps/gateway/dist/agent/tool-executor.js
28196
- import { randomUUID as randomUUID8 } from "node:crypto";
28283
+ import { randomUUID as randomUUID9 } from "node:crypto";
28197
28284
 
28198
28285
  // apps/gateway/dist/agent/tool-scribe.js
28199
- import { randomUUID as randomUUID7 } from "node:crypto";
28286
+ import { randomUUID as randomUUID8 } from "node:crypto";
28200
28287
 
28201
28288
  // apps/gateway/dist/agent/scribe.js
28202
28289
  function shouldScribeToolReply(toolName) {
@@ -28302,7 +28389,7 @@ ${scribeUserPayload}`));
28302
28389
  if (response.usage && params.storage?.appendTokenUsage && params.sessionId) {
28303
28390
  try {
28304
28391
  await params.storage.appendTokenUsage({
28305
- id: randomUUID7(),
28392
+ id: randomUUID8(),
28306
28393
  sessionId: params.sessionId,
28307
28394
  providerId: provider.id,
28308
28395
  model: response.model ?? finalModel,
@@ -28355,7 +28442,7 @@ async function emitAnrivaRecord(storage, params) {
28355
28442
  const endTime = Date.now();
28356
28443
  const outputStr = params.output != null ? JSON.stringify(params.output).slice(0, 200) : void 0;
28357
28444
  const record = {
28358
- id: randomUUID8(),
28445
+ id: randomUUID9(),
28359
28446
  tool: params.toolName,
28360
28447
  anrivaUri: `anriva://${params.provider}/${params.service}/${params.action}`,
28361
28448
  user: params.userId,
@@ -28414,7 +28501,7 @@ async function executeTool(params) {
28414
28501
  }
28415
28502
  });
28416
28503
  await storage.appendAudit({
28417
- id: randomUUID8(),
28504
+ id: randomUUID9(),
28418
28505
  time: (/* @__PURE__ */ new Date()).toISOString(),
28419
28506
  type: "guardian.block",
28420
28507
  payload: {
@@ -28501,7 +28588,7 @@ async function executeTool(params) {
28501
28588
  reason: synthesized ? "tool.synth" : "tool"
28502
28589
  });
28503
28590
  await storage.appendAudit({
28504
- id: randomUUID8(),
28591
+ id: randomUUID9(),
28505
28592
  time: (/* @__PURE__ */ new Date()).toISOString(),
28506
28593
  type: "agent.run.tool",
28507
28594
  payload: {
@@ -28581,7 +28668,7 @@ async function executeTool(params) {
28581
28668
  }
28582
28669
  const content = renderToolFailure2(toolName, result, message.channel, toolInput);
28583
28670
  await storage.appendAudit({
28584
- id: randomUUID8(),
28671
+ id: randomUUID9(),
28585
28672
  time: (/* @__PURE__ */ new Date()).toISOString(),
28586
28673
  type: "agent.run.tool",
28587
28674
  payload: {
@@ -29380,6 +29467,115 @@ function prepareToolInput(params) {
29380
29467
  return { toolInput };
29381
29468
  }
29382
29469
 
29470
+ // apps/gateway/dist/router-backlog/active-tool-discovery.js
29471
+ var STOP_WORDS = /* @__PURE__ */ new Set([
29472
+ "a",
29473
+ "an",
29474
+ "the",
29475
+ "for",
29476
+ "to",
29477
+ "in",
29478
+ "on",
29479
+ "of",
29480
+ "and",
29481
+ "or",
29482
+ "my",
29483
+ "please",
29484
+ "show",
29485
+ "get",
29486
+ "run",
29487
+ "use",
29488
+ "tool"
29489
+ ]);
29490
+ function clamp01(value) {
29491
+ if (!Number.isFinite(value))
29492
+ return 0;
29493
+ return Math.max(0, Math.min(1, value));
29494
+ }
29495
+ function normalizeText2(input) {
29496
+ return input.toLowerCase().replace(/[^a-z0-9.\s_-]+/g, " ").replace(/\s+/g, " ").trim();
29497
+ }
29498
+ function tokenize2(input) {
29499
+ return normalizeText2(input).split(/[\s._-]+/).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
29500
+ }
29501
+ function overlapScore(queryTokens, candidateTokens) {
29502
+ if (!queryTokens.length || !candidateTokens.length)
29503
+ return 0;
29504
+ const candidateSet = new Set(candidateTokens);
29505
+ let hits = 0;
29506
+ for (const token of queryTokens) {
29507
+ if (candidateSet.has(token))
29508
+ hits += 1;
29509
+ }
29510
+ return hits / queryTokens.length;
29511
+ }
29512
+ function buildCandidateTokens(tool) {
29513
+ return [tool.name, tool.description ?? "", ...tool.aliases ?? [], ...tool.tags ?? []].flatMap((part) => tokenize2(part)).filter(Boolean);
29514
+ }
29515
+ function recommendedBoost(recommended, toolName) {
29516
+ const index = recommended.findIndex((name) => name === toolName);
29517
+ if (index < 0)
29518
+ return 0;
29519
+ if (index === 0)
29520
+ return 0.25;
29521
+ if (index === 1)
29522
+ return 0.18;
29523
+ if (index === 2)
29524
+ return 0.12;
29525
+ return 0.08;
29526
+ }
29527
+ function buildActiveToolDiscoveryPlan(params) {
29528
+ const query = normalizeText2(params.query ?? "");
29529
+ const queryTokens = tokenize2(query);
29530
+ const recommended = (params.recommendedTools ?? []).map((name) => normalizeText2(name));
29531
+ const minScore = Number.isFinite(params.minScore) ? Number(params.minScore) : 0.2;
29532
+ const maxCandidates = Number.isFinite(params.maxCandidates) ? Math.max(1, Math.floor(Number(params.maxCandidates))) : 5;
29533
+ const candidates = [];
29534
+ for (const tool of params.tools) {
29535
+ const reasons = [];
29536
+ const normalizedName = normalizeText2(tool.name);
29537
+ const aliases = (tool.aliases ?? []).map((alias) => normalizeText2(alias));
29538
+ let score = 0;
29539
+ if (normalizedName === query || aliases.includes(query)) {
29540
+ score = 1;
29541
+ reasons.push(normalizedName === query ? "exact_name" : "exact_alias");
29542
+ } else if (query && (normalizedName.includes(query) || aliases.some((alias) => alias.includes(query)))) {
29543
+ score = Math.max(score, 0.86);
29544
+ reasons.push("substring_match");
29545
+ }
29546
+ const tokenScore = overlapScore(queryTokens, buildCandidateTokens(tool));
29547
+ if (tokenScore > 0) {
29548
+ score = Math.max(score, 0.35 + tokenScore * 0.45);
29549
+ reasons.push("token_overlap");
29550
+ }
29551
+ const boost = recommendedBoost(recommended, normalizedName);
29552
+ if (boost > 0) {
29553
+ score += boost;
29554
+ reasons.push("recommended_boost");
29555
+ }
29556
+ const finalScore = clamp01(score);
29557
+ if (finalScore >= minScore) {
29558
+ candidates.push({
29559
+ name: tool.name,
29560
+ score: finalScore,
29561
+ reasons
29562
+ });
29563
+ }
29564
+ }
29565
+ candidates.sort((a, b) => {
29566
+ if (b.score !== a.score)
29567
+ return b.score - a.score;
29568
+ return a.name.localeCompare(b.name);
29569
+ });
29570
+ const limited = candidates.slice(0, maxCandidates);
29571
+ const mode = limited.length === 0 ? "capability_gap" : (limited[0]?.score ?? 0) >= 0.95 ? "deterministic_match" : "active_discovery";
29572
+ return {
29573
+ query,
29574
+ mode,
29575
+ candidates: limited
29576
+ };
29577
+ }
29578
+
29383
29579
  // apps/gateway/dist/agent/tool-runner.js
29384
29580
  function isWeatherIntent(intent) {
29385
29581
  return typeof intent === "string" && intent.startsWith("weather.");
@@ -29676,6 +29872,43 @@ async function handleToolPlanning(params) {
29676
29872
  "memory.consolidate",
29677
29873
  "memory.link"
29678
29874
  ].includes(tool2.name));
29875
+ const maybeRecordActiveToolDiscovery = async () => {
29876
+ if (routingResult.actionability?.kind !== "tool")
29877
+ return;
29878
+ let recommendedTools;
29879
+ try {
29880
+ recommendedTools = chitraguptaBridge?.recommend(normalized.norm);
29881
+ } catch {
29882
+ recommendedTools = void 0;
29883
+ }
29884
+ const discoveryPlan = buildActiveToolDiscoveryPlan({
29885
+ query: normalized.norm,
29886
+ tools: allowedTools.map((tool2) => ({
29887
+ name: tool2.name,
29888
+ description: tool2.description,
29889
+ aliases: tool2.aliases,
29890
+ tags: tool2.tags
29891
+ })),
29892
+ recommendedTools
29893
+ });
29894
+ if (discoveryPlan.mode === "deterministic_match")
29895
+ return;
29896
+ routingResult.activeToolDiscovery = discoveryPlan;
29897
+ try {
29898
+ await storage.appendAudit?.({
29899
+ id: randomUUID10(),
29900
+ time: (/* @__PURE__ */ new Date()).toISOString(),
29901
+ type: "agent.run.tool.discovery",
29902
+ payload: {
29903
+ sessionId: session.id,
29904
+ query: discoveryPlan.query,
29905
+ mode: discoveryPlan.mode,
29906
+ candidates: discoveryPlan.candidates
29907
+ }
29908
+ });
29909
+ } catch {
29910
+ }
29911
+ };
29679
29912
  let plan = forcedIntentPlan ?? explicitToolNamePlan ?? deterministicNluPlan ?? deterministicIntentFallbackPlan ?? failoverNluHintPlan;
29680
29913
  let plannerProviderId = null;
29681
29914
  let plannerModel = null;
@@ -29695,8 +29928,10 @@ async function handleToolPlanning(params) {
29695
29928
  if (!plan) {
29696
29929
  plan = deterministicIntentFallbackPlan;
29697
29930
  }
29698
- if (!plan)
29931
+ if (!plan) {
29932
+ await maybeRecordActiveToolDiscovery();
29699
29933
  return null;
29934
+ }
29700
29935
  }
29701
29936
  if (!plan) {
29702
29937
  const plannerAttempts = [];
@@ -29788,7 +30023,7 @@ async function handleToolPlanning(params) {
29788
30023
  if (plannerResult.usage && storage.appendTokenUsage) {
29789
30024
  try {
29790
30025
  await storage.appendTokenUsage({
29791
- id: randomUUID9(),
30026
+ id: randomUUID10(),
29792
30027
  sessionId: session.id,
29793
30028
  providerId: attemptProvider.id,
29794
30029
  model: plannerResult.model ?? attempt.model,
@@ -29835,6 +30070,7 @@ async function handleToolPlanning(params) {
29835
30070
  plan = forcedIntentPlan ?? explicitToolNamePlan ?? deterministicNluPlan ?? deterministicIntentFallbackPlan ?? failoverNluHintPlan;
29836
30071
  }
29837
30072
  if (plan?.action !== "tool" || !plan.tool) {
30073
+ await maybeRecordActiveToolDiscovery();
29838
30074
  return null;
29839
30075
  }
29840
30076
  applyDeterministicTrace({
@@ -29850,7 +30086,7 @@ async function handleToolPlanning(params) {
29850
30086
  if (routingResult.deterministicTrace) {
29851
30087
  try {
29852
30088
  await storage.appendAudit?.({
29853
- id: randomUUID9(),
30089
+ id: randomUUID10(),
29854
30090
  time: (/* @__PURE__ */ new Date()).toISOString(),
29855
30091
  type: "agent.run.route",
29856
30092
  payload: {
@@ -29918,7 +30154,7 @@ ${t(locale, "approval.pending", {
29918
30154
  }
29919
30155
  });
29920
30156
  await storage.appendAudit({
29921
- id: randomUUID9(),
30157
+ id: randomUUID10(),
29922
30158
  time: (/* @__PURE__ */ new Date()).toISOString(),
29923
30159
  type: "tool.policy_ephemeral_allow",
29924
30160
  payload: {
@@ -30476,6 +30712,16 @@ var MIN_CHAT_MAX_ATTEMPTS = 1;
30476
30712
  var MAX_CHAT_MAX_ATTEMPTS = 10;
30477
30713
  var LOCAL_PROVIDER_TYPES2 = /* @__PURE__ */ new Set(["ollama", "mock"]);
30478
30714
  var CLI_PROVIDER_TYPES2 = /* @__PURE__ */ new Set(["codex-cli", "claude-code", "copilot"]);
30715
+ function isAbortLikeError(error) {
30716
+ if (!error)
30717
+ return false;
30718
+ if (error instanceof DOMException && error.name === "AbortError")
30719
+ return true;
30720
+ if (error instanceof Error) {
30721
+ return /abort/i.test(error.name) || /abort|aborted/i.test(error.message);
30722
+ }
30723
+ return false;
30724
+ }
30479
30725
  function resolveChatAttemptTimeoutMs(config, providerId) {
30480
30726
  const routingTimeoutRaw = config.routing?.chatTimeoutMs;
30481
30727
  const routingTimeoutMs = typeof routingTimeoutRaw === "number" && Number.isFinite(routingTimeoutRaw) ? routingTimeoutRaw : DEFAULT_CHAT_ATTEMPT_TIMEOUT_MS;
@@ -30583,13 +30829,24 @@ async function runChatWithFallback(params) {
30583
30829
  });
30584
30830
  }
30585
30831
  if (attempts.length === 0) {
30586
- return { ok: false, error: new Error("Provider not found") };
30832
+ return {
30833
+ ok: false,
30834
+ error: new Error("Provider not found"),
30835
+ escalation: {
30836
+ policy: "provider_error",
30837
+ reason: "no_healthy_provider",
30838
+ retryable: true,
30839
+ responseModel: "provider.failure"
30840
+ }
30841
+ };
30587
30842
  }
30588
30843
  let response;
30589
30844
  let lastError;
30590
30845
  let providerUsed;
30591
30846
  let modelUsed;
30592
30847
  let fallbackUsed = false;
30848
+ let skippedCooldownCount = 0;
30849
+ let attemptedProviderCount = 0;
30593
30850
  const lastAnthropicCall = lastAnthropicCallAt.get(sessionId);
30594
30851
  for (const attempt of attempts) {
30595
30852
  const provider = getProvider(attempt.providerId);
@@ -30616,6 +30873,7 @@ async function runChatWithFallback(params) {
30616
30873
  until: new Date(cooldown.until).toISOString(),
30617
30874
  failures: cooldown.failures
30618
30875
  });
30876
+ skippedCooldownCount += 1;
30619
30877
  lastError = new Error(`Provider ${attempt.providerId} cooling down (${cooldown.reason})`);
30620
30878
  continue;
30621
30879
  }
@@ -30631,6 +30889,7 @@ async function runChatWithFallback(params) {
30631
30889
  lastError = new Error(`Provider ${attempt.providerId} unavailable (${health.status ?? "unknown"})`);
30632
30890
  continue;
30633
30891
  }
30892
+ attemptedProviderCount += 1;
30634
30893
  try {
30635
30894
  if (!pruningApplied && shouldPruneContext({
30636
30895
  config: pruningConfig,
@@ -30717,6 +30976,10 @@ async function runChatWithFallback(params) {
30717
30976
  }
30718
30977
  break;
30719
30978
  } catch (error) {
30979
+ if (signal?.aborted) {
30980
+ lastError = error;
30981
+ break;
30982
+ }
30720
30983
  const cooldown2 = registerProviderFailure(attempt.providerId, error, provider.type);
30721
30984
  logger.warn("provider_cooldown_set", {
30722
30985
  providerId: attempt.providerId,
@@ -30729,19 +30992,58 @@ async function runChatWithFallback(params) {
30729
30992
  }
30730
30993
  }
30731
30994
  if (!response || !providerUsed || !modelUsed) {
30732
- const allCooldowns = attempts.length > 0 && attempts.every((a) => {
30733
- const cd = getProviderCooldown(a.providerId);
30734
- return cd !== null;
30735
- });
30736
- if (allCooldowns) {
30995
+ const now = Date.now();
30996
+ const activeCooldowns = attempts.map((attempt) => getProviderCooldown(attempt.providerId)).filter((entry) => Boolean(entry));
30997
+ const allCandidatesCooling = attempts.length > 0 && skippedCooldownCount === attempts.length;
30998
+ if (allCandidatesCooling) {
30999
+ const retryAfterMs = activeCooldowns.length > 0 ? Math.max(0, Math.min(...activeCooldowns.map((entry) => entry.until - now))) : void 0;
30737
31000
  return {
30738
31001
  ok: false,
30739
31002
  error: lastError ?? new Error("All providers on cooldown"),
30740
- degraded: true,
31003
+ escalation: {
31004
+ policy: "degraded_tools_only",
31005
+ reason: "all_candidates_cooling_down",
31006
+ retryable: true,
31007
+ retryAfterMs,
31008
+ responseModel: "provider.degraded"
31009
+ },
30741
31010
  friendlyMessage: "I'm having trouble reaching my language models right now. I can still handle quick tasks like weather, reminders, and notes. Try again in a moment!"
30742
31011
  };
30743
31012
  }
30744
- return { ok: false, error: lastError ?? new Error("Provider failed") };
31013
+ if (signal?.aborted || isAbortLikeError(lastError)) {
31014
+ return {
31015
+ ok: false,
31016
+ error: lastError ?? new Error("Request aborted"),
31017
+ escalation: {
31018
+ policy: "provider_error",
31019
+ reason: "request_aborted",
31020
+ retryable: false,
31021
+ responseModel: "provider.failure"
31022
+ }
31023
+ };
31024
+ }
31025
+ if (attemptedProviderCount === 0) {
31026
+ return {
31027
+ ok: false,
31028
+ error: lastError ?? new Error("No healthy provider available"),
31029
+ escalation: {
31030
+ policy: "provider_error",
31031
+ reason: "no_healthy_provider",
31032
+ retryable: true,
31033
+ responseModel: "provider.failure"
31034
+ }
31035
+ };
31036
+ }
31037
+ return {
31038
+ ok: false,
31039
+ error: lastError ?? new Error("Provider failed"),
31040
+ escalation: {
31041
+ policy: "provider_error",
31042
+ reason: "attempts_exhausted",
31043
+ retryable: true,
31044
+ responseModel: "provider.failure"
31045
+ }
31046
+ };
30745
31047
  }
30746
31048
  return {
30747
31049
  ok: true,
@@ -30753,11 +31055,11 @@ async function runChatWithFallback(params) {
30753
31055
  }
30754
31056
 
30755
31057
  // apps/gateway/dist/agent/loop-user-event.js
30756
- import { randomUUID as randomUUID10 } from "node:crypto";
31058
+ import { randomUUID as randomUUID11 } from "node:crypto";
30757
31059
  async function persistUserEvent(params) {
30758
31060
  const { storage, sessionId, message, providerId, model, memoryManager, allowMemory, logger } = params;
30759
31061
  const userEvent = {
30760
- id: randomUUID10(),
31062
+ id: randomUUID11(),
30761
31063
  sessionId,
30762
31064
  role: "user",
30763
31065
  content: message.text,
@@ -30789,10 +31091,10 @@ async function persistUserEvent(params) {
30789
31091
  }
30790
31092
 
30791
31093
  // apps/gateway/dist/agent/loop-post-chat.js
30792
- import { randomUUID as randomUUID12 } from "node:crypto";
31094
+ import { randomUUID as randomUUID13 } from "node:crypto";
30793
31095
 
30794
31096
  // apps/gateway/dist/agent/memory-compaction.js
30795
- import { randomUUID as randomUUID11 } from "node:crypto";
31097
+ import { randomUUID as randomUUID12 } from "node:crypto";
30796
31098
  async function maybeCompactSession(params) {
30797
31099
  const { sessionId, providerId, model, memory, memoryAllowed, budgetFallbackTarget, storage, config, logger, getProvider } = params;
30798
31100
  if (!memoryAllowed || !memory.summaryEnabled) {
@@ -30861,7 +31163,7 @@ ${transcript}`
30861
31163
  await storage.setSessionSummary(sessionId, response.content, count);
30862
31164
  if (response.usage && storage.appendTokenUsage) {
30863
31165
  storage.appendTokenUsage({
30864
- id: randomUUID11(),
31166
+ id: randomUUID12(),
30865
31167
  sessionId,
30866
31168
  providerId: provider.id,
30867
31169
  model: response.model ?? summaryModel,
@@ -30885,7 +31187,7 @@ ${transcript}`
30885
31187
  async function finalizeChatRun(params) {
30886
31188
  const { storage, session, payload, config, provider, model, response, fallbackUsed, routingDecision, routingResult, budgetFallbackTarget, maybeAppendSmritiMemory, proactiveManager, memoryManager, allowMemory, memoryAllowed, getProvider, logger, stripModelThinking: stripModelThinking2 } = params;
30887
31189
  await storage.appendAudit({
30888
- id: randomUUID12(),
31190
+ id: randomUUID13(),
30889
31191
  time: (/* @__PURE__ */ new Date()).toISOString(),
30890
31192
  type: "agent.run.output",
30891
31193
  payload: {
@@ -30894,11 +31196,26 @@ async function finalizeChatRun(params) {
30894
31196
  model,
30895
31197
  fallbackUsed,
30896
31198
  routing: {
31199
+ auto: routingResult.auto,
31200
+ language: routingResult.language,
31201
+ languageSource: routingResult.languageSource,
31202
+ decisionLatencyMs: routingResult.decisionLatencyMs,
31203
+ tier: routingResult.tier,
31204
+ reason: routingResult.reason,
30897
31205
  requested: routingResult.requested,
30898
31206
  selected: routingResult.selected,
30899
31207
  fallbackUsed: routingResult.fallbackUsed,
31208
+ scoring: routingResult.scoring,
31209
+ providerResolution: routingResult.providerResolution,
30900
31210
  deterministicTrace: routingResult.deterministicTrace,
30901
- retrievalGate: routingResult.retrievalGate
31211
+ actionability: routingResult.actionability,
31212
+ retrievalGate: routingResult.retrievalGate,
31213
+ smalltalkFastLane: routingResult.smalltalkFastLane,
31214
+ escalation: routingResult.escalation,
31215
+ policyClassifier: routingResult.policyClassifier,
31216
+ semanticCache: routingResult.semanticCache,
31217
+ activeToolDiscovery: routingResult.activeToolDiscovery,
31218
+ budget: routingResult.budget
30902
31219
  },
30903
31220
  normalization: payload.message.normalization?.audit,
30904
31221
  // Backward-compatible key for older dashboards.
@@ -30911,7 +31228,7 @@ async function finalizeChatRun(params) {
30911
31228
  const outputTokens = response.usage.outputTokens ?? 0;
30912
31229
  const costMicro = estimateCostMicro(provider.id, model, inputTokens, outputTokens);
30913
31230
  await storage.appendTokenUsage({
30914
- id: randomUUID12(),
31231
+ id: randomUUID13(),
30915
31232
  sessionId: session.id,
30916
31233
  providerId: provider.id,
30917
31234
  model,
@@ -31019,7 +31336,7 @@ async function finalizeChatRun(params) {
31019
31336
  }, 0);
31020
31337
  routingResult.selected = { providerId: provider.id, model };
31021
31338
  routingResult.fallbackUsed = fallbackUsed;
31022
- const includeRouting = Boolean(routingDecision || routingResult.budget?.applied || routingResult.deterministicTrace || routingResult.retrievalGate);
31339
+ const includeRouting = Boolean(routingDecision || routingResult.budget?.applied || routingResult.deterministicTrace || routingResult.retrievalGate || routingResult.scoring || routingResult.providerResolution || routingResult.smalltalkFastLane || routingResult.escalation || routingResult.policyClassifier || routingResult.semanticCache || routingResult.activeToolDiscovery);
31023
31340
  return {
31024
31341
  session,
31025
31342
  response: {
@@ -31278,7 +31595,7 @@ function getFallbackPriority(providerType) {
31278
31595
  return 2;
31279
31596
  }
31280
31597
  async function resolveProviderAndModel(params) {
31281
- const { payload, profile, prefs, autoRouting, routingDecision, budgetState, reasoning, config, providers, providerHealth, listProviderModels, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, inferProviderFromModel: inferProviderFromModel2, logger, sessionId } = params;
31598
+ const { payload, profile, prefs, autoRouting, routingDecision, budgetState, reasoning, config, providers, providerHealth, listProviderModels, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, inferProviderFromModel: inferProviderFromModel2, logger, sessionId, sessionPin, toolPresence } = params;
31282
31599
  let manualProviderId;
31283
31600
  let manualModel;
31284
31601
  if (autoRouting) {
@@ -31326,11 +31643,56 @@ async function resolveProviderAndModel(params) {
31326
31643
  if (!resolvedProviderId) {
31327
31644
  throw new Error("Missing provider");
31328
31645
  }
31646
+ const resolutionSource = routingDecision?.target.providerId ? "routing" : manualProviderId || manualModel ? "manual" : "default";
31647
+ const overrides = [];
31648
+ const pushOverride = (params2) => {
31649
+ overrides.push(params2);
31650
+ };
31651
+ const currentTarget = () => ({ providerId: resolvedProviderId, ...resolvedModel ? { model: resolvedModel } : {} });
31652
+ const initialTarget = currentTarget();
31653
+ if (sessionPin?.providerId) {
31654
+ if (providers.get(sessionPin.providerId)) {
31655
+ const from = currentTarget();
31656
+ const changedProvider = sessionPin.providerId !== resolvedProviderId;
31657
+ const changedModel = Boolean(sessionPin.model) && sessionPin.model !== resolvedModel;
31658
+ if (changedProvider || changedModel) {
31659
+ resolvedProviderId = sessionPin.providerId;
31660
+ resolvedModel = sessionPin.model ?? resolvedModel;
31661
+ pushOverride({
31662
+ type: "session_pin_override",
31663
+ from,
31664
+ to: currentTarget(),
31665
+ reason: sessionPin.reason ?? "session_route_pin"
31666
+ });
31667
+ logger.info("provider_session_pin_override", {
31668
+ sessionId,
31669
+ fromProviderId: from.providerId,
31670
+ fromModel: from.model,
31671
+ toProviderId: resolvedProviderId,
31672
+ toModel: resolvedModel,
31673
+ pinReason: sessionPin.reason,
31674
+ pinUntil: sessionPin.until
31675
+ });
31676
+ }
31677
+ } else {
31678
+ logger.warn("provider_session_pin_missing_provider", {
31679
+ sessionId,
31680
+ providerId: sessionPin.providerId
31681
+ });
31682
+ }
31683
+ }
31329
31684
  if (budgetState?.overDaily && budgetState.fallbackTarget) {
31330
31685
  const fallbackTarget = budgetState.fallbackTarget;
31331
31686
  if (providers.get(fallbackTarget.providerId)) {
31687
+ const from = currentTarget();
31332
31688
  resolvedProviderId = fallbackTarget.providerId;
31333
31689
  resolvedModel = fallbackTarget.model;
31690
+ pushOverride({
31691
+ type: "budget_fallback",
31692
+ from,
31693
+ to: currentTarget(),
31694
+ reason: "budget.over_daily"
31695
+ });
31334
31696
  budgetState.applied = true;
31335
31697
  logger.info("routing_budget_override", {
31336
31698
  sessionId,
@@ -31346,6 +31708,48 @@ async function resolveProviderAndModel(params) {
31346
31708
  });
31347
31709
  }
31348
31710
  }
31711
+ const shouldPreferToolCapableProvider = Boolean(toolPresence?.hasTools) && toolPresence?.actionability === "tool";
31712
+ if (shouldPreferToolCapableProvider) {
31713
+ const currentProviderType = providers.get(resolvedProviderId)?.type;
31714
+ const currentPriority = getFallbackPriority(currentProviderType);
31715
+ if (currentPriority > 1) {
31716
+ const preferred = providers.list().slice().sort((a, b) => {
31717
+ const priorityDelta = getFallbackPriority(a.type) - getFallbackPriority(b.type);
31718
+ if (priorityDelta !== 0)
31719
+ return priorityDelta;
31720
+ return a.id.localeCompare(b.id);
31721
+ }).find((provider) => {
31722
+ if (!provider?.id || provider.id === resolvedProviderId)
31723
+ return false;
31724
+ const priority = getFallbackPriority(provider.type);
31725
+ if (priority > 1)
31726
+ return false;
31727
+ if (getProviderCooldown(provider.id))
31728
+ return false;
31729
+ const health = providerHealth?.[provider.id];
31730
+ if (health && health.status !== "ok")
31731
+ return false;
31732
+ return true;
31733
+ });
31734
+ if (preferred) {
31735
+ const from = currentTarget();
31736
+ resolvedProviderId = preferred.id;
31737
+ resolvedModel = void 0;
31738
+ pushOverride({
31739
+ type: "tool_presence_override",
31740
+ from,
31741
+ to: currentTarget(),
31742
+ reason: "local_or_cli_tool_capable_provider"
31743
+ });
31744
+ logger.info("provider_tool_presence_override", {
31745
+ sessionId,
31746
+ fromProviderId: from.providerId,
31747
+ toProviderId: preferred.id,
31748
+ actionability: toolPresence?.actionability
31749
+ });
31750
+ }
31751
+ }
31752
+ }
31349
31753
  const chooseFallbackProvider = (currentProviderId) => {
31350
31754
  const orderedFallbacks = [];
31351
31755
  for (const fallback of config.routing.fallback ?? []) {
@@ -31389,6 +31793,7 @@ async function resolveProviderAndModel(params) {
31389
31793
  if (!resolvedProviderId || !providers.get(resolvedProviderId)) {
31390
31794
  const fallbackTarget = chooseFallbackProvider(resolvedProviderId ?? null);
31391
31795
  if (fallbackTarget && providers.get(fallbackTarget.providerId)) {
31796
+ const from = resolvedProviderId ? currentTarget() : void 0;
31392
31797
  logger.warn("provider_missing_fallback", {
31393
31798
  missingProviderId: resolvedProviderId,
31394
31799
  fallbackProviderId: fallbackTarget.providerId,
@@ -31396,6 +31801,12 @@ async function resolveProviderAndModel(params) {
31396
31801
  });
31397
31802
  resolvedProviderId = fallbackTarget.providerId;
31398
31803
  resolvedModel = fallbackTarget.model;
31804
+ pushOverride({
31805
+ type: "provider_missing_fallback",
31806
+ from,
31807
+ to: currentTarget(),
31808
+ reason: "provider_missing"
31809
+ });
31399
31810
  } else {
31400
31811
  throw new Error("Provider not found");
31401
31812
  }
@@ -31405,6 +31816,7 @@ async function resolveProviderAndModel(params) {
31405
31816
  if (health && health.status !== "ok") {
31406
31817
  const fallbackTarget = chooseFallbackProvider(resolvedProviderId);
31407
31818
  if (fallbackTarget && providers.get(fallbackTarget.providerId)) {
31819
+ const from = currentTarget();
31408
31820
  logger.warn("provider_health_fallback", {
31409
31821
  providerId: resolvedProviderId,
31410
31822
  status: health.status,
@@ -31415,6 +31827,12 @@ async function resolveProviderAndModel(params) {
31415
31827
  });
31416
31828
  resolvedProviderId = fallbackTarget.providerId;
31417
31829
  resolvedModel = fallbackTarget.model;
31830
+ pushOverride({
31831
+ type: "provider_health_fallback",
31832
+ from,
31833
+ to: currentTarget(),
31834
+ reason: health.status
31835
+ });
31418
31836
  }
31419
31837
  }
31420
31838
  }
@@ -31422,6 +31840,7 @@ async function resolveProviderAndModel(params) {
31422
31840
  if (cooldown) {
31423
31841
  const fallbackTarget = chooseFallbackProvider(resolvedProviderId);
31424
31842
  if (fallbackTarget && providers.get(fallbackTarget.providerId)) {
31843
+ const from = currentTarget();
31425
31844
  logger.warn("provider_cooldown_fallback", {
31426
31845
  providerId: resolvedProviderId,
31427
31846
  reason: cooldown.reason,
@@ -31431,6 +31850,12 @@ async function resolveProviderAndModel(params) {
31431
31850
  });
31432
31851
  resolvedProviderId = fallbackTarget.providerId;
31433
31852
  resolvedModel = fallbackTarget.model;
31853
+ pushOverride({
31854
+ type: "provider_cooldown_fallback",
31855
+ from,
31856
+ to: currentTarget(),
31857
+ reason: cooldown.reason
31858
+ });
31434
31859
  }
31435
31860
  }
31436
31861
  let modelsInfo = await listProviderModels(resolvedProviderId, false);
@@ -31439,6 +31864,7 @@ async function resolveProviderAndModel(params) {
31439
31864
  if (supportsRemoteFetchFallback && modelsInfo.error) {
31440
31865
  const fallbackTarget = chooseFallbackProvider(resolvedProviderId);
31441
31866
  if (fallbackTarget && providers.get(fallbackTarget.providerId)) {
31867
+ const from = currentTarget();
31442
31868
  logger.warn("provider_models_fetch_fallback", {
31443
31869
  providerId: resolvedProviderId,
31444
31870
  model: resolvedModel,
@@ -31448,6 +31874,12 @@ async function resolveProviderAndModel(params) {
31448
31874
  });
31449
31875
  resolvedProviderId = fallbackTarget.providerId;
31450
31876
  resolvedModel = fallbackTarget.model;
31877
+ pushOverride({
31878
+ type: "models_fetch_fallback",
31879
+ from,
31880
+ to: currentTarget(),
31881
+ reason: modelsInfo.error
31882
+ });
31451
31883
  modelsInfo = await listProviderModels(resolvedProviderId, false);
31452
31884
  }
31453
31885
  }
@@ -31469,7 +31901,14 @@ async function resolveProviderAndModel(params) {
31469
31901
  fallbackModel: fallback ?? "none"
31470
31902
  });
31471
31903
  if (fallback) {
31904
+ const from = currentTarget();
31472
31905
  resolvedModel = fallback;
31906
+ pushOverride({
31907
+ type: "model_not_found_fallback",
31908
+ from,
31909
+ to: currentTarget(),
31910
+ reason: "model_not_in_provider_catalog"
31911
+ });
31473
31912
  }
31474
31913
  }
31475
31914
  }
@@ -31481,8 +31920,15 @@ async function resolveProviderAndModel(params) {
31481
31920
  if (modelsInfo.models.length) {
31482
31921
  const overrideKnown = modelsInfo.models.some((model) => normalizeModelId2(model) === normalizeModelId2(resolvedOverride));
31483
31922
  if (overrideKnown) {
31923
+ const from = currentTarget();
31484
31924
  resolvedModel = resolvedOverride;
31485
31925
  reasoningApplied = true;
31926
+ pushOverride({
31927
+ type: "reasoning_model_override",
31928
+ from,
31929
+ to: currentTarget(),
31930
+ reason: "reasoning.mode"
31931
+ });
31486
31932
  } else {
31487
31933
  logger.warn("reasoning_override_model_not_found", {
31488
31934
  providerId: resolvedProviderId,
@@ -31490,12 +31936,25 @@ async function resolveProviderAndModel(params) {
31490
31936
  });
31491
31937
  }
31492
31938
  } else {
31939
+ const from = currentTarget();
31493
31940
  resolvedModel = resolvedOverride;
31494
31941
  reasoningApplied = true;
31942
+ pushOverride({
31943
+ type: "reasoning_model_override",
31944
+ from,
31945
+ to: currentTarget(),
31946
+ reason: "reasoning.mode"
31947
+ });
31495
31948
  }
31496
31949
  }
31497
31950
  }
31498
- return { providerId: resolvedProviderId, model: resolvedModel, reasoningApplied };
31951
+ const trace = {
31952
+ source: resolutionSource,
31953
+ initial: initialTarget,
31954
+ resolved: currentTarget(),
31955
+ overrides
31956
+ };
31957
+ return { providerId: resolvedProviderId, model: resolvedModel, reasoningApplied, trace };
31499
31958
  }
31500
31959
 
31501
31960
  // apps/gateway/dist/agent/saved-models.js
@@ -31762,6 +32221,463 @@ function classifyMessage(text, hasTools) {
31762
32221
  }
31763
32222
  }
31764
32223
 
32224
+ // apps/gateway/dist/agent/router-scoring.js
32225
+ var LOCAL_PROVIDER_TYPES4 = /* @__PURE__ */ new Set(["ollama", "mock"]);
32226
+ var CLI_PROVIDER_TYPES4 = /* @__PURE__ */ new Set(["codex-cli", "claude-code", "copilot"]);
32227
+ function providerSupportsToolAwareRouting(providers, providerId) {
32228
+ const provider = providers.get(providerId);
32229
+ if (!provider)
32230
+ return false;
32231
+ return LOCAL_PROVIDER_TYPES4.has(provider.type) || CLI_PROVIDER_TYPES4.has(provider.type);
32232
+ }
32233
+ function sumSignals(signals) {
32234
+ let total = 0;
32235
+ for (const signal of signals) {
32236
+ total += signal.contribution;
32237
+ }
32238
+ return total;
32239
+ }
32240
+ function buildCandidate(params) {
32241
+ const { candidate, providers, complexitySignal, actionabilityKind, policyLabel, hasTools } = params;
32242
+ const signals = [];
32243
+ const pushSignal = (name, weight, value, note) => {
32244
+ signals.push({
32245
+ name,
32246
+ weight,
32247
+ value,
32248
+ contribution: weight * value,
32249
+ note
32250
+ });
32251
+ };
32252
+ pushSignal("base", 1, candidate.source === "marga" ? 0.5 : 0.4);
32253
+ if (candidate.source === "marga") {
32254
+ const confidence = Math.max(0, Math.min(1, candidate.margaConfidence ?? 0));
32255
+ pushSignal("marga_confidence", 0.35, confidence);
32256
+ }
32257
+ if (complexitySignal) {
32258
+ const aligned = candidate.decision.tier === complexitySignal.tier ? 1 : -0.5;
32259
+ pushSignal("complexity_alignment", 0.2, aligned, `signal:${complexitySignal.tier}:${complexitySignal.reason}`);
32260
+ }
32261
+ if (actionabilityKind === "smalltalk" || actionabilityKind === "command") {
32262
+ const fastAligned = candidate.decision.tier === "fast" ? 1 : -1;
32263
+ pushSignal("actionability_alignment", 0.22, fastAligned, `action:${actionabilityKind}`);
32264
+ } else if (actionabilityKind === "tool" && hasTools) {
32265
+ const toolAligned = providerSupportsToolAwareRouting(providers, candidate.decision.target.providerId) ? 1 : -0.35;
32266
+ pushSignal("tool_presence_alignment", 0.24, toolAligned, "action:tool");
32267
+ }
32268
+ if (policyLabel === "safe_fast_lane") {
32269
+ const fastAligned = candidate.decision.tier === "fast" ? 1 : -0.8;
32270
+ pushSignal("policy_alignment", 0.18, fastAligned, "policy:safe_fast_lane");
32271
+ } else if (policyLabel === "cautious" || policyLabel === "deny") {
32272
+ const deepAligned = candidate.decision.tier === "deep" ? 1 : -0.65;
32273
+ pushSignal("policy_alignment", 0.18, deepAligned, `policy:${policyLabel}`);
32274
+ }
32275
+ if (!providers.get(candidate.decision.target.providerId)) {
32276
+ pushSignal("provider_missing_penalty", 0.6, -1);
32277
+ }
32278
+ return {
32279
+ source: candidate.source,
32280
+ target: candidate.decision.target,
32281
+ tier: candidate.decision.tier,
32282
+ reason: candidate.decision.reason,
32283
+ score: Number(sumSignals(signals).toFixed(4)),
32284
+ signals
32285
+ };
32286
+ }
32287
+ function scoreRoutingDecision(params) {
32288
+ const candidatesInput = [];
32289
+ if (params.margaDecision) {
32290
+ candidatesInput.push({
32291
+ source: "marga",
32292
+ decision: params.margaDecision,
32293
+ margaConfidence: params.margaConfidence
32294
+ });
32295
+ }
32296
+ if (params.heuristicDecision) {
32297
+ candidatesInput.push({
32298
+ source: "heuristic",
32299
+ decision: params.heuristicDecision
32300
+ });
32301
+ }
32302
+ if (candidatesInput.length === 0) {
32303
+ return { decision: null, trace: null };
32304
+ }
32305
+ const scored = candidatesInput.map((candidate) => buildCandidate({
32306
+ candidate,
32307
+ providers: params.providers,
32308
+ complexitySignal: params.complexitySignal,
32309
+ actionabilityKind: params.actionabilityKind,
32310
+ policyLabel: params.policyLabel,
32311
+ hasTools: params.hasTools
32312
+ }));
32313
+ scored.sort((a, b) => {
32314
+ if (b.score !== a.score)
32315
+ return b.score - a.score;
32316
+ if (a.source !== b.source)
32317
+ return a.source === "marga" ? -1 : 1;
32318
+ const aLabel = `${a.target.providerId}:${a.target.model ?? ""}`;
32319
+ const bLabel = `${b.target.providerId}:${b.target.model ?? ""}`;
32320
+ return aLabel.localeCompare(bLabel);
32321
+ });
32322
+ const winner = scored[0];
32323
+ const decision = {
32324
+ target: winner.target,
32325
+ tier: winner.tier,
32326
+ reason: `score:${winner.source}:${winner.reason}`
32327
+ };
32328
+ const trace = {
32329
+ strategy: "multi_signal_v1",
32330
+ selectedSource: winner.source,
32331
+ selectedScore: winner.score,
32332
+ candidates: scored
32333
+ };
32334
+ return { decision, trace };
32335
+ }
32336
+
32337
+ // apps/gateway/dist/router-backlog/policy-classifier-contract.js
32338
+ function isRecord(value) {
32339
+ return Boolean(value) && typeof value === "object";
32340
+ }
32341
+ function isPolicyLabel(value) {
32342
+ return value === "safe_fast_lane" || value === "standard" || value === "cautious" || value === "deny";
32343
+ }
32344
+ function clamp012(value) {
32345
+ if (!Number.isFinite(value))
32346
+ return 0;
32347
+ return Math.max(0, Math.min(1, value));
32348
+ }
32349
+ function parsePolicyClassifierResponse(payload) {
32350
+ if (!isRecord(payload))
32351
+ return null;
32352
+ if (payload.ok !== true)
32353
+ return null;
32354
+ if (!isPolicyLabel(payload.policy))
32355
+ return null;
32356
+ const confidenceRaw = Number(payload.confidence);
32357
+ const reasons = Array.isArray(payload.reasons) ? payload.reasons.filter((item) => typeof item === "string") : [];
32358
+ const ttlSecRaw = Number(payload.ttlSec);
32359
+ return {
32360
+ ok: true,
32361
+ policy: payload.policy,
32362
+ confidence: clamp012(confidenceRaw),
32363
+ reasons,
32364
+ ttlSec: Number.isFinite(ttlSecRaw) ? Math.max(1, Math.floor(ttlSecRaw)) : 30,
32365
+ classifier: typeof payload.classifier === "string" ? payload.classifier : void 0,
32366
+ time: typeof payload.time === "string" ? payload.time : void 0
32367
+ };
32368
+ }
32369
+ async function classifyPolicyWithSidecar(params) {
32370
+ const timeoutMs = Number.isFinite(params.timeoutMs) ? Math.max(50, Math.floor(params.timeoutMs ?? 250)) : 250;
32371
+ const fetchImpl = params.fetchImpl ?? globalThis.fetch;
32372
+ const fallback = {
32373
+ ok: true,
32374
+ policy: "standard",
32375
+ confidence: 0.45,
32376
+ reasons: ["fallback_standard_policy"],
32377
+ ttlSec: 20,
32378
+ classifier: "fallback-v0",
32379
+ time: (/* @__PURE__ */ new Date()).toISOString()
32380
+ };
32381
+ if (typeof fetchImpl !== "function") {
32382
+ return {
32383
+ ok: false,
32384
+ source: "fallback",
32385
+ response: fallback,
32386
+ error: "fetch_unavailable"
32387
+ };
32388
+ }
32389
+ const baseUrl = params.baseUrl.replace(/\/+$/, "");
32390
+ const controller = new AbortController();
32391
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
32392
+ try {
32393
+ const response = await fetchImpl(`${baseUrl}/v1/classify`, {
32394
+ method: "POST",
32395
+ headers: { "content-type": "application/json" },
32396
+ body: JSON.stringify(params.request),
32397
+ signal: controller.signal
32398
+ });
32399
+ if (!response.ok) {
32400
+ return {
32401
+ ok: false,
32402
+ source: "fallback",
32403
+ response: fallback,
32404
+ error: `http_${response.status}`
32405
+ };
32406
+ }
32407
+ const data2 = await response.json();
32408
+ const parsed = parsePolicyClassifierResponse(data2);
32409
+ if (!parsed) {
32410
+ return {
32411
+ ok: false,
32412
+ source: "fallback",
32413
+ response: fallback,
32414
+ error: "invalid_payload"
32415
+ };
32416
+ }
32417
+ return {
32418
+ ok: true,
32419
+ source: "sidecar",
32420
+ response: parsed
32421
+ };
32422
+ } catch (error) {
32423
+ const message = error instanceof Error ? error.message : String(error);
32424
+ return {
32425
+ ok: false,
32426
+ source: "fallback",
32427
+ response: fallback,
32428
+ error: message
32429
+ };
32430
+ } finally {
32431
+ clearTimeout(timeout);
32432
+ }
32433
+ }
32434
+
32435
+ // apps/gateway/dist/router-backlog/semantic-cache-policy.js
32436
+ var DEFAULT_SEMANTIC_CACHE_POLICY = {
32437
+ smalltalk: {
32438
+ similarityThreshold: 0.82,
32439
+ ttlMs: 30 * 6e4,
32440
+ asyncVerify: false,
32441
+ maxVerifyWaitMs: 0
32442
+ },
32443
+ general: {
32444
+ similarityThreshold: 0.92,
32445
+ ttlMs: 5 * 6e4,
32446
+ asyncVerify: true,
32447
+ maxVerifyWaitMs: 250
32448
+ },
32449
+ memory: {
32450
+ similarityThreshold: 0.95,
32451
+ ttlMs: 2 * 6e4,
32452
+ asyncVerify: true,
32453
+ maxVerifyWaitMs: 300
32454
+ },
32455
+ tool: {
32456
+ similarityThreshold: 0.98,
32457
+ ttlMs: 45e3,
32458
+ asyncVerify: true,
32459
+ maxVerifyWaitMs: 350
32460
+ },
32461
+ safety: {
32462
+ similarityThreshold: 0.995,
32463
+ ttlMs: 2e4,
32464
+ asyncVerify: true,
32465
+ maxVerifyWaitMs: 500
32466
+ },
32467
+ factual: {
32468
+ similarityThreshold: 0.94,
32469
+ ttlMs: 3 * 6e4,
32470
+ asyncVerify: true,
32471
+ maxVerifyWaitMs: 280
32472
+ }
32473
+ };
32474
+ function clamp013(value) {
32475
+ if (!Number.isFinite(value))
32476
+ return 0;
32477
+ return Math.max(0, Math.min(1, value));
32478
+ }
32479
+ function mergePolicy(overrides) {
32480
+ if (!overrides)
32481
+ return DEFAULT_SEMANTIC_CACHE_POLICY;
32482
+ const next = { ...DEFAULT_SEMANTIC_CACHE_POLICY };
32483
+ for (const category of Object.keys(DEFAULT_SEMANTIC_CACHE_POLICY)) {
32484
+ if (overrides[category]) {
32485
+ const merged = {
32486
+ ...DEFAULT_SEMANTIC_CACHE_POLICY[category],
32487
+ ...overrides[category]
32488
+ };
32489
+ next[category] = {
32490
+ similarityThreshold: clamp013(merged.similarityThreshold),
32491
+ ttlMs: Math.max(1e3, Math.floor(merged.ttlMs)),
32492
+ asyncVerify: Boolean(merged.asyncVerify),
32493
+ maxVerifyWaitMs: Math.max(0, Math.floor(merged.maxVerifyWaitMs))
32494
+ };
32495
+ }
32496
+ }
32497
+ return next;
32498
+ }
32499
+ function inferSemanticCacheCategory(signals) {
32500
+ if (signals.sensitiveTopic)
32501
+ return "safety";
32502
+ if (signals.deterministicTool || signals.actionabilityKind === "tool")
32503
+ return "tool";
32504
+ if (signals.actionabilityKind === "smalltalk")
32505
+ return "smalltalk";
32506
+ if (signals.actionabilityKind === "memory" || signals.actionabilityKind === "recap")
32507
+ return "memory";
32508
+ const text = (signals.text ?? "").toLowerCase();
32509
+ if (/\b(who|what|when|where|why|how\s+many|fact|stats?|number|latest|today)\b/.test(text)) {
32510
+ return "factual";
32511
+ }
32512
+ if (signals.retrievalAllow) {
32513
+ return "memory";
32514
+ }
32515
+ return "general";
32516
+ }
32517
+ function resolveSemanticCacheRule(params) {
32518
+ const policy = mergePolicy(params.overrides);
32519
+ return policy[params.category];
32520
+ }
32521
+
32522
+ // apps/gateway/dist/router-backlog/semantic-response-cache.js
32523
+ var MAX_ENTRIES = 400;
32524
+ var CACHEABLE_CATEGORIES = /* @__PURE__ */ new Set([
32525
+ "smalltalk",
32526
+ "general",
32527
+ "factual"
32528
+ ]);
32529
+ var entries = [];
32530
+ function clamp014(value) {
32531
+ if (!Number.isFinite(value))
32532
+ return 0;
32533
+ return Math.max(0, Math.min(1, value));
32534
+ }
32535
+ function normalizeText3(input) {
32536
+ return input.toLowerCase().replace(/[^\p{L}\p{N}\s]+/gu, " ").replace(/\s+/g, " ").trim();
32537
+ }
32538
+ function toTokenSet(text) {
32539
+ if (!text)
32540
+ return /* @__PURE__ */ new Set();
32541
+ return new Set(text.split(" ").filter(Boolean));
32542
+ }
32543
+ function tokenJaccard(a, b) {
32544
+ if (a.size === 0 && b.size === 0)
32545
+ return 1;
32546
+ if (a.size === 0 || b.size === 0)
32547
+ return 0;
32548
+ let intersection = 0;
32549
+ for (const token of a) {
32550
+ if (b.has(token))
32551
+ intersection += 1;
32552
+ }
32553
+ const union = a.size + b.size - intersection;
32554
+ return union > 0 ? intersection / union : 0;
32555
+ }
32556
+ function bigramSet(text) {
32557
+ const compact = text.replace(/\s+/g, " ");
32558
+ if (compact.length < 2)
32559
+ return /* @__PURE__ */ new Set([compact]);
32560
+ const out = /* @__PURE__ */ new Set();
32561
+ for (let index = 0; index < compact.length - 1; index += 1) {
32562
+ out.add(compact.slice(index, index + 2));
32563
+ }
32564
+ return out;
32565
+ }
32566
+ function similarityScore(aRaw, bRaw) {
32567
+ const a = normalizeText3(aRaw);
32568
+ const b = normalizeText3(bRaw);
32569
+ if (!a || !b)
32570
+ return 0;
32571
+ if (a === b)
32572
+ return 1;
32573
+ const tokenScore = tokenJaccard(toTokenSet(a), toTokenSet(b));
32574
+ const bigramScore = tokenJaccard(bigramSet(a), bigramSet(b));
32575
+ return clamp014(0.7 * tokenScore + 0.3 * bigramScore);
32576
+ }
32577
+ function pruneExpired(nowMs) {
32578
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
32579
+ const entry = entries[index];
32580
+ if (entry && entry.expiresAtMs <= nowMs) {
32581
+ entries.splice(index, 1);
32582
+ }
32583
+ }
32584
+ }
32585
+ function isSemanticResponseCacheCategoryEnabled(category) {
32586
+ return CACHEABLE_CATEGORIES.has(category);
32587
+ }
32588
+ function lookupSemanticResponseCache(params) {
32589
+ const nowMs = params.nowMs ?? Date.now();
32590
+ pruneExpired(nowMs);
32591
+ if (!isSemanticResponseCacheCategoryEnabled(params.category)) {
32592
+ return {
32593
+ attempted: true,
32594
+ hit: false,
32595
+ reason: "category_disabled"
32596
+ };
32597
+ }
32598
+ const queryNorm = normalizeText3(params.query);
32599
+ if (!queryNorm) {
32600
+ return {
32601
+ attempted: true,
32602
+ hit: false,
32603
+ reason: "empty_query"
32604
+ };
32605
+ }
32606
+ let best;
32607
+ for (const entry of entries) {
32608
+ if (entry.sessionId !== params.sessionId)
32609
+ continue;
32610
+ if (entry.category !== params.category)
32611
+ continue;
32612
+ const similarity = similarityScore(queryNorm, entry.queryNorm);
32613
+ if (!best || similarity > best.similarity) {
32614
+ best = { entry, similarity };
32615
+ }
32616
+ }
32617
+ if (!best) {
32618
+ return {
32619
+ attempted: true,
32620
+ hit: false,
32621
+ reason: "no_candidate"
32622
+ };
32623
+ }
32624
+ if (best.similarity < clamp014(params.threshold)) {
32625
+ return {
32626
+ attempted: true,
32627
+ hit: false,
32628
+ reason: "threshold_not_met",
32629
+ similarity: best.similarity,
32630
+ ageMs: Math.max(0, nowMs - best.entry.createdAtMs)
32631
+ };
32632
+ }
32633
+ return {
32634
+ attempted: true,
32635
+ hit: true,
32636
+ reason: "hit",
32637
+ similarity: best.similarity,
32638
+ ageMs: Math.max(0, nowMs - best.entry.createdAtMs),
32639
+ entry: {
32640
+ responseContent: best.entry.responseContent,
32641
+ selected: best.entry.selected
32642
+ }
32643
+ };
32644
+ }
32645
+ function storeSemanticResponseCache(params) {
32646
+ const nowMs = params.nowMs ?? Date.now();
32647
+ pruneExpired(nowMs);
32648
+ if (!isSemanticResponseCacheCategoryEnabled(params.category)) {
32649
+ return false;
32650
+ }
32651
+ const queryNorm = normalizeText3(params.query);
32652
+ const responseContent = params.responseContent.trim();
32653
+ if (!queryNorm || !responseContent) {
32654
+ return false;
32655
+ }
32656
+ const ttlMs = Math.max(1e3, Math.floor(params.ttlMs));
32657
+ const next = {
32658
+ sessionId: params.sessionId,
32659
+ category: params.category,
32660
+ queryNorm,
32661
+ responseContent,
32662
+ selected: params.selected,
32663
+ createdAtMs: nowMs,
32664
+ expiresAtMs: nowMs + ttlMs
32665
+ };
32666
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
32667
+ const entry = entries[index];
32668
+ if (!entry)
32669
+ continue;
32670
+ if (entry.sessionId === next.sessionId && entry.category === next.category && entry.queryNorm === next.queryNorm) {
32671
+ entries.splice(index, 1);
32672
+ }
32673
+ }
32674
+ entries.unshift(next);
32675
+ if (entries.length > MAX_ENTRIES) {
32676
+ entries.length = MAX_ENTRIES;
32677
+ }
32678
+ return true;
32679
+ }
32680
+
31765
32681
  // apps/gateway/dist/agent/loop-execute.js
31766
32682
  function mapMargaComplexityToTier(complexity) {
31767
32683
  if (!complexity)
@@ -31812,31 +32728,114 @@ async function executeAgentRun(params) {
31812
32728
  const hasSessionOverride = Boolean(prefs?.providerId) || Boolean(prefs?.model);
31813
32729
  const autoRoutingPreference = typeof prefs?.autoRouting === "boolean" ? prefs.autoRouting : void 0;
31814
32730
  const autoRouting = hasPayloadOverride ? false : autoRoutingPreference ?? (hasSessionOverride ? false : profile.autoRoutingDefault ?? config.routing.enabled ?? false);
32731
+ const routingDecisionStartedAt = Date.now();
32732
+ const pinUntilMs = sessionRoute.pinUntil ? Date.parse(sessionRoute.pinUntil) : Number.NaN;
32733
+ const sessionPinExpired = Boolean(sessionRoute.pinProviderId) && Number.isFinite(pinUntilMs) && pinUntilMs <= Date.now();
32734
+ if (sessionPinExpired && storage.setSessionRoute) {
32735
+ void storage.setSessionRoute(session.id, {
32736
+ agentId: sessionRoute.agentId,
32737
+ laneId: sessionRoute.laneId,
32738
+ queueMode: sessionRoute.queueMode,
32739
+ announceMode: sessionRoute.announceMode
32740
+ }).catch((error) => {
32741
+ logger.warn("session_route_pin_clear_failed", {
32742
+ sessionId: session.id,
32743
+ error: error instanceof Error ? error.message : String(error)
32744
+ });
32745
+ });
32746
+ }
32747
+ const activeSessionPin = !hasPayloadOverride && !sessionPinExpired && sessionRoute.pinProviderId ? {
32748
+ providerId: sessionRoute.pinProviderId,
32749
+ model: sessionRoute.pinModel,
32750
+ reason: sessionRoute.pinReason,
32751
+ until: sessionRoute.pinUntil
32752
+ } : void 0;
32753
+ const policySidecarBaseUrl = (process.env.VAAYU_POLICY_CLASSIFIER_URL ?? process.env.VAAYU_POLICY_SIDECAR_URL ?? "").trim();
32754
+ const fallbackPolicyReasons = !policySidecarBaseUrl ? ["policy_sidecar_not_configured"] : ["policy_sidecar_fallback"];
32755
+ let policyClassifier = {
32756
+ source: "fallback",
32757
+ policy: "standard",
32758
+ confidence: 0.45,
32759
+ reasons: fallbackPolicyReasons,
32760
+ ttlSec: 20
32761
+ };
32762
+ if (policySidecarBaseUrl) {
32763
+ const policyResult = await classifyPolicyWithSidecar({
32764
+ baseUrl: policySidecarBaseUrl,
32765
+ timeoutMs: 250,
32766
+ request: {
32767
+ text: routingText,
32768
+ channel: payload.message.channel,
32769
+ hasTools: runtime.toolRegistry.list().length > 0,
32770
+ metadata: {
32771
+ sessionId: session.id,
32772
+ language: locale
32773
+ }
32774
+ }
32775
+ });
32776
+ if (policyResult.ok) {
32777
+ policyClassifier = {
32778
+ source: "sidecar",
32779
+ policy: policyResult.response.policy,
32780
+ confidence: policyResult.response.confidence,
32781
+ reasons: policyResult.response.reasons,
32782
+ ttlSec: policyResult.response.ttlSec
32783
+ };
32784
+ } else {
32785
+ policyClassifier = {
32786
+ source: "fallback",
32787
+ policy: policyResult.response.policy,
32788
+ confidence: policyResult.response.confidence,
32789
+ reasons: [...policyResult.response.reasons, policyResult.error].filter(Boolean),
32790
+ ttlSec: policyResult.response.ttlSec
32791
+ };
32792
+ }
32793
+ }
32794
+ const naturalCommand = detectNaturalLanguageCommand(routingText, locale);
32795
+ const commandText = naturalCommand ?? payload.message.text;
32796
+ const command = parseCommand(commandText);
32797
+ const actionability = classifyActionability({
32798
+ text: routingText,
32799
+ hasStructuredCommand: Boolean(command),
32800
+ languageHint: prefs?.language ?? profile.language,
32801
+ assistantName: profile.assistantName,
32802
+ isPureGreeting: deps.isPureGreeting,
32803
+ isPureAck: deps.isPureAck,
32804
+ looksLikeWeatherAsk: deps.looksLikeWeatherAsk,
32805
+ looksLikeRecentRecap: deps.looksLikeRecentRecap
32806
+ });
31815
32807
  const hasTools = runtime.toolRegistry.list().length > 0;
31816
32808
  const margaDecision = autoRouting ? await runtime.chitraguptaBridge?.decideRoute({
31817
32809
  message: routingText,
31818
32810
  hasTools,
31819
32811
  bindingStrategy: "hybrid"
31820
32812
  }) ?? classifyMessage(routingText, hasTools) : null;
31821
- const routingDecision = autoRouting ? (() => {
31822
- if (margaDecision?.providerId && providers.get(margaDecision.providerId)) {
31823
- return {
31824
- target: {
31825
- providerId: margaDecision.providerId,
31826
- model: margaDecision.modelId
31827
- },
31828
- tier: mapMargaComplexityToTier(margaDecision.complexity),
31829
- reason: `marga:${margaDecision.taskType}/${margaDecision.complexity}`
31830
- };
31831
- }
31832
- return buildRoutingDecision(routingText, session.id, config.routing);
31833
- })() : null;
32813
+ const margaRoutingDecision = autoRouting && margaDecision?.providerId && providers.get(margaDecision.providerId) ? {
32814
+ target: {
32815
+ providerId: margaDecision.providerId,
32816
+ model: margaDecision.modelId
32817
+ },
32818
+ tier: mapMargaComplexityToTier(margaDecision.complexity),
32819
+ reason: `marga:${margaDecision.taskType}/${margaDecision.complexity}`
32820
+ } : null;
32821
+ const heuristicRoutingDecision = autoRouting ? buildRoutingDecision(routingText, session.id, config.routing) : null;
31834
32822
  const reasoningConfig = config.routing.reasoning;
31835
32823
  const reasoningMode = reasoningConfig?.mode ?? "off";
31836
32824
  const complexitySignal = reasoningMode === "auto" ? margaDecision ? {
31837
32825
  tier: mapMargaComplexityToTier(margaDecision.complexity),
31838
32826
  reason: `marga:${margaDecision.taskType}/${margaDecision.complexity}`
31839
32827
  } : evaluateComplexitySignal(routingText, config.routing) : null;
32828
+ const scoringResult = autoRouting ? scoreRoutingDecision({
32829
+ providers,
32830
+ margaDecision: margaRoutingDecision,
32831
+ margaConfidence: margaDecision?.confidence,
32832
+ heuristicDecision: heuristicRoutingDecision,
32833
+ complexitySignal,
32834
+ actionabilityKind: actionability.kind,
32835
+ policyLabel: policyClassifier.policy,
32836
+ hasTools
32837
+ }) : { decision: null, trace: null };
32838
+ const routingDecision = autoRouting ? scoringResult.decision ?? margaRoutingDecision ?? heuristicRoutingDecision : null;
31840
32839
  const manualModelOverride = Boolean(payload.model) || !autoRouting && Boolean(prefs?.model);
31841
32840
  const reasoningEnabled = !manualModelOverride && (reasoningMode === "on" || reasoningMode === "auto" && complexitySignal?.tier === "deep");
31842
32841
  const budgetState = await evaluateBudgetState({ storage, config, logger });
@@ -31861,7 +32860,7 @@ async function executeAgentRun(params) {
31861
32860
  logger.warn("kaala_auto_delegate_failed", { err: error });
31862
32861
  }
31863
32862
  const resolvedSystem = payload.system ?? prefs?.system ?? profile.defaultSystem ?? payload.defaults?.system;
31864
- const { providerId: resolvedProviderId, model: resolvedModel } = await resolveProviderAndModel({
32863
+ const { providerId: resolvedProviderId, model: resolvedModel, trace: providerResolutionTrace } = await resolveProviderAndModel({
31865
32864
  payload,
31866
32865
  profile,
31867
32866
  prefs,
@@ -31880,8 +32879,14 @@ async function executeAgentRun(params) {
31880
32879
  normalizeModelId: normalizeModelId2,
31881
32880
  inferProviderFromModel: inferProviderFromModel2,
31882
32881
  logger,
31883
- sessionId: session.id
32882
+ sessionId: session.id,
32883
+ sessionPin: activeSessionPin,
32884
+ toolPresence: {
32885
+ hasTools,
32886
+ actionability: actionability.kind
32887
+ }
31884
32888
  });
32889
+ const routingDecisionLatencyMs = Math.max(0, Date.now() - routingDecisionStartedAt);
31885
32890
  const profileSystem = buildProfileSystemPrompt2(profile);
31886
32891
  const combinedSystem = buildCombinedSystemPrompt({
31887
32892
  soulPrompt,
@@ -31899,13 +32904,22 @@ async function executeAgentRun(params) {
31899
32904
  const routingResult = {
31900
32905
  auto: autoRouting,
31901
32906
  language: locale,
31902
- languageSource: languageDecision.source
32907
+ languageSource: languageDecision.source,
32908
+ decisionLatencyMs: routingDecisionLatencyMs,
32909
+ actionability,
32910
+ policyClassifier
31903
32911
  };
31904
32912
  if (routingDecision) {
31905
32913
  routingResult.tier = routingDecision.tier;
31906
32914
  routingResult.reason = routingDecision.reason;
31907
32915
  routingResult.requested = routingDecision.target;
31908
32916
  }
32917
+ if (scoringResult.trace) {
32918
+ routingResult.scoring = scoringResult.trace;
32919
+ }
32920
+ if (providerResolutionTrace) {
32921
+ routingResult.providerResolution = providerResolutionTrace;
32922
+ }
31909
32923
  if (budgetState) {
31910
32924
  routingResult.budget = {
31911
32925
  available: budgetState.available,
@@ -31919,20 +32933,6 @@ async function executeAgentRun(params) {
31919
32933
  fallback: budgetState.fallbackTarget
31920
32934
  };
31921
32935
  }
31922
- const naturalCommand = detectNaturalLanguageCommand(routingText, locale);
31923
- const commandText = naturalCommand ?? payload.message.text;
31924
- const command = parseCommand(commandText);
31925
- const actionability = classifyActionability({
31926
- text: routingText,
31927
- hasStructuredCommand: Boolean(command),
31928
- languageHint: prefs?.language ?? profile.language,
31929
- assistantName: profile.assistantName,
31930
- isPureGreeting: deps.isPureGreeting,
31931
- isPureAck: deps.isPureAck,
31932
- looksLikeWeatherAsk: deps.looksLikeWeatherAsk,
31933
- looksLikeRecentRecap: deps.looksLikeRecentRecap
31934
- });
31935
- routingResult.actionability = actionability;
31936
32936
  const retrievalDecision = shouldRetrieveContext({
31937
32937
  text: routingText,
31938
32938
  allowMemory,
@@ -31946,8 +32946,24 @@ async function executeAgentRun(params) {
31946
32946
  reason: retrievalDecision.reason,
31947
32947
  query: retrievalQueryText
31948
32948
  };
32949
+ const semanticCacheCategory = inferSemanticCacheCategory({
32950
+ text: routingText,
32951
+ actionabilityKind: actionability.kind,
32952
+ retrievalAllow: retrievalDecision.allow,
32953
+ deterministicTool: Boolean(routingResult.deterministicTrace?.tool)
32954
+ });
32955
+ const semanticCacheRule = resolveSemanticCacheRule({
32956
+ category: semanticCacheCategory
32957
+ });
32958
+ routingResult.semanticCache = {
32959
+ category: semanticCacheCategory,
32960
+ similarityThreshold: semanticCacheRule.similarityThreshold,
32961
+ ttlMs: semanticCacheRule.ttlMs,
32962
+ asyncVerify: semanticCacheRule.asyncVerify,
32963
+ maxVerifyWaitMs: semanticCacheRule.maxVerifyWaitMs
32964
+ };
31949
32965
  await storage.appendAudit({
31950
- id: randomUUID13(),
32966
+ id: randomUUID14(),
31951
32967
  time: (/* @__PURE__ */ new Date()).toISOString(),
31952
32968
  type: "agent.run.input",
31953
32969
  payload: {
@@ -31955,7 +32971,21 @@ async function executeAgentRun(params) {
31955
32971
  channel: payload.message.channel,
31956
32972
  providerId: resolvedProviderId,
31957
32973
  model: resolvedModel,
31958
- routing: routingResult.requested,
32974
+ routing: {
32975
+ auto: routingResult.auto,
32976
+ language: routingResult.language,
32977
+ languageSource: routingResult.languageSource,
32978
+ decisionLatencyMs: routingResult.decisionLatencyMs,
32979
+ tier: routingResult.tier,
32980
+ reason: routingResult.reason,
32981
+ requested: routingResult.requested,
32982
+ scoring: routingResult.scoring,
32983
+ providerResolution: routingResult.providerResolution,
32984
+ policyClassifier: routingResult.policyClassifier,
32985
+ semanticCache: routingResult.semanticCache
32986
+ },
32987
+ // Backward-compatible key for older dashboards.
32988
+ routingTarget: routingResult.requested,
31959
32989
  normalization: normalizedAuditContext,
31960
32990
  // Backward-compatible key for older dashboards.
31961
32991
  normalized: normalizedAuditContext,
@@ -32087,6 +33117,50 @@ async function executeAgentRun(params) {
32087
33117
  routingResult.fallbackUsed = false;
32088
33118
  return buildEarlyResult(session, skipModel, skipContent, routingDecision, routingResult);
32089
33119
  }
33120
+ const semanticCacheLookup = lookupSemanticResponseCache({
33121
+ sessionId: session.id,
33122
+ category: semanticCacheCategory,
33123
+ query: routingText,
33124
+ threshold: semanticCacheRule.similarityThreshold
33125
+ });
33126
+ if (routingResult.semanticCache) {
33127
+ routingResult.semanticCache.lookupAttempted = semanticCacheLookup.attempted;
33128
+ routingResult.semanticCache.lookupHit = semanticCacheLookup.hit;
33129
+ routingResult.semanticCache.lookupSimilarity = semanticCacheLookup.similarity;
33130
+ routingResult.semanticCache.lookupReason = semanticCacheLookup.reason;
33131
+ routingResult.semanticCache.lookupAgeMs = semanticCacheLookup.ageMs;
33132
+ }
33133
+ await storage.appendAudit({
33134
+ id: randomUUID14(),
33135
+ time: (/* @__PURE__ */ new Date()).toISOString(),
33136
+ type: "semantic.cache.lookup",
33137
+ payload: {
33138
+ sessionId: session.id,
33139
+ category: semanticCacheCategory,
33140
+ threshold: semanticCacheRule.similarityThreshold,
33141
+ hit: semanticCacheLookup.hit,
33142
+ similarity: semanticCacheLookup.similarity,
33143
+ reason: semanticCacheLookup.reason,
33144
+ ageMs: semanticCacheLookup.ageMs,
33145
+ queryPreview: routingText.slice(0, 120)
33146
+ }
33147
+ });
33148
+ if (semanticCacheLookup.hit && semanticCacheLookup.entry) {
33149
+ const cached2 = semanticCacheLookup.entry;
33150
+ const cacheModel = cached2.selected.model ?? "semantic.cache.hit";
33151
+ await appendAssistantEvent(storage, session.id, cached2.responseContent, {
33152
+ providerId: cached2.selected.providerId,
33153
+ model: cacheModel,
33154
+ semanticCache: {
33155
+ hit: true,
33156
+ reason: semanticCacheLookup.reason,
33157
+ similarity: semanticCacheLookup.similarity
33158
+ }
33159
+ }, payload.message);
33160
+ routingResult.selected = cached2.selected;
33161
+ routingResult.fallbackUsed = false;
33162
+ return buildEarlyResult(session, cacheModel, cached2.responseContent, routingDecision, routingResult);
33163
+ }
32090
33164
  const chatResult = await runChatWithFallback({
32091
33165
  sessionId: session.id,
32092
33166
  messageText: payload.message.text,
@@ -32126,8 +33200,10 @@ async function executeAgentRun(params) {
32126
33200
  signal: runSignal
32127
33201
  });
32128
33202
  if (!chatResult.ok) {
32129
- const content = chatResult.degraded && chatResult.friendlyMessage ? chatResult.friendlyMessage : formatProviderError2(chatResult.error ?? new Error("Provider failed"));
32130
- const errorModel = chatResult.degraded ? "provider.degraded" : "provider.failure";
33203
+ const escalation = chatResult.escalation;
33204
+ const content = escalation.policy === "degraded_tools_only" && chatResult.friendlyMessage ? chatResult.friendlyMessage : formatProviderError2(chatResult.error ?? new Error("Provider failed"));
33205
+ const errorModel = escalation.responseModel;
33206
+ routingResult.escalation = escalation;
32131
33207
  await appendAssistantEvent(storage, session.id, content, {
32132
33208
  providerId: "error",
32133
33209
  model: errorModel
@@ -32150,6 +33226,28 @@ async function executeAgentRun(params) {
32150
33226
  if (runSignal.aborted) {
32151
33227
  throw new Error("Request aborted");
32152
33228
  }
33229
+ const cacheStored = storeSemanticResponseCache({
33230
+ sessionId: session.id,
33231
+ category: semanticCacheCategory,
33232
+ query: routingText,
33233
+ responseContent: response.content,
33234
+ selected: { providerId: provider.id, model },
33235
+ ttlMs: semanticCacheRule.ttlMs
33236
+ });
33237
+ if (cacheStored) {
33238
+ await storage.appendAudit({
33239
+ id: randomUUID14(),
33240
+ time: (/* @__PURE__ */ new Date()).toISOString(),
33241
+ type: "semantic.cache.write",
33242
+ payload: {
33243
+ sessionId: session.id,
33244
+ category: semanticCacheCategory,
33245
+ ttlMs: semanticCacheRule.ttlMs,
33246
+ providerId: provider.id,
33247
+ model
33248
+ }
33249
+ });
33250
+ }
32153
33251
  return finalizeChatRun({
32154
33252
  storage,
32155
33253
  session,
@@ -32174,7 +33272,7 @@ async function executeAgentRun(params) {
32174
33272
  }
32175
33273
 
32176
33274
  // apps/gateway/dist/routing-trace.js
32177
- import { randomUUID as randomUUID14 } from "node:crypto";
33275
+ import { randomUUID as randomUUID15 } from "node:crypto";
32178
33276
  var MAX_TRACE_ENTRIES = 300;
32179
33277
  var traces = [];
32180
33278
  function compactText(input) {
@@ -32214,7 +33312,7 @@ function buildNluDecision(routing) {
32214
33312
  }
32215
33313
  function recordRoutingTrace(params) {
32216
33314
  const entry = {
32217
- id: randomUUID14(),
33315
+ id: randomUUID15(),
32218
33316
  time: (/* @__PURE__ */ new Date()).toISOString(),
32219
33317
  runId: params.runId,
32220
33318
  sessionId: params.sessionId,
@@ -32223,7 +33321,11 @@ function recordRoutingTrace(params) {
32223
33321
  userId: params.userId,
32224
33322
  textPreview: compactText(params.text),
32225
33323
  responseModel: params.responseModel,
33324
+ auto: params.routing?.auto,
33325
+ language: params.routing?.language,
33326
+ languageSource: params.routing?.languageSource,
32226
33327
  nluDecision: buildNluDecision(params.routing),
33328
+ decisionLatencyMs: params.routing?.decisionLatencyMs,
32227
33329
  tier: params.routing?.tier,
32228
33330
  reason: params.routing?.reason,
32229
33331
  requested: params.routing?.requested,
@@ -32232,6 +33334,14 @@ function recordRoutingTrace(params) {
32232
33334
  fallbackPath: buildFallbackPath(params.routing),
32233
33335
  deterministicTrace: params.routing?.deterministicTrace,
32234
33336
  actionability: params.routing?.actionability,
33337
+ retrievalGate: params.routing?.retrievalGate,
33338
+ scoring: params.routing?.scoring,
33339
+ providerResolution: params.routing?.providerResolution,
33340
+ smalltalkFastLane: params.routing?.smalltalkFastLane,
33341
+ escalation: params.routing?.escalation,
33342
+ policyClassifier: params.routing?.policyClassifier,
33343
+ semanticCache: params.routing?.semanticCache,
33344
+ activeToolDiscovery: params.routing?.activeToolDiscovery,
32235
33345
  budgetFallback: params.routing?.budget?.fallback
32236
33346
  };
32237
33347
  traces.unshift(entry);
@@ -32272,7 +33382,7 @@ function createAgentLoop(deps) {
32272
33382
  if (payload.signal?.aborted) {
32273
33383
  throw new Error("Request aborted");
32274
33384
  }
32275
- const runId = randomUUID15();
33385
+ const runId = randomUUID16();
32276
33386
  const sanitizeResult = sanitizeInput(payload.message.text, {
32277
33387
  maxLength: 5e4,
32278
33388
  allowInjection: false
@@ -32314,7 +33424,7 @@ function createAgentLoop(deps) {
32314
33424
  const inboxId = payload.inboxId ?? await (async () => {
32315
33425
  if (payload.skipInbox || !storage.enqueueSessionInbox)
32316
33426
  return void 0;
32317
- const recordId = randomUUID15();
33427
+ const recordId = randomUUID16();
32318
33428
  const record = {
32319
33429
  id: recordId,
32320
33430
  sessionId: session.id,
@@ -32561,7 +33671,7 @@ function createAgentLoop(deps) {
32561
33671
  routing: result.routing
32562
33672
  });
32563
33673
  await storage.appendAudit({
32564
- id: randomUUID15(),
33674
+ id: randomUUID16(),
32565
33675
  time: trace.time,
32566
33676
  type: "routing.trace",
32567
33677
  payload: trace
@@ -32649,7 +33759,7 @@ function requireAdmin(connectionAuth, connectionId, params, gatewayConfig, remot
32649
33759
  }
32650
33760
 
32651
33761
  // apps/gateway/dist/self-heal/index.js
32652
- import { randomUUID as randomUUID16 } from "node:crypto";
33762
+ import { randomUUID as randomUUID17 } from "node:crypto";
32653
33763
  var resolveTargets = (target) => {
32654
33764
  if (!target || target === "all")
32655
33765
  return ["kaala", "smriti", "qdrant"];
@@ -32752,7 +33862,7 @@ async function runSelfHeal(params) {
32752
33862
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
32753
33863
  if (runtime.storage.appendAudit) {
32754
33864
  await runtime.storage.appendAudit({
32755
- id: randomUUID16(),
33865
+ id: randomUUID17(),
32756
33866
  time: timestamp,
32757
33867
  type: "self_heal.run",
32758
33868
  payload: { target, mode, status, actions }
@@ -32918,7 +34028,7 @@ function createCoreMethods(ctx) {
32918
34028
  }
32919
34029
 
32920
34030
  // apps/gateway/dist/rpc/methods/sessions.js
32921
- import { randomUUID as randomUUID17 } from "node:crypto";
34031
+ import { randomUUID as randomUUID18 } from "node:crypto";
32922
34032
  import path18 from "node:path";
32923
34033
 
32924
34034
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
@@ -37944,9 +39054,9 @@ function isReasoningMode(value) {
37944
39054
  return value === "off" || value === "auto" || value === "on";
37945
39055
  }
37946
39056
  function parseReasoningOverrides(value) {
37947
- const entries = value.split(",").map((segment) => segment.trim()).filter(Boolean);
39057
+ const entries2 = value.split(",").map((segment) => segment.trim()).filter(Boolean);
37948
39058
  const overrides = {};
37949
- for (const entry of entries) {
39059
+ for (const entry of entries2) {
37950
39060
  const separator = entry.includes("=") ? "=" : entry.includes(":") ? ":" : null;
37951
39061
  if (!separator) continue;
37952
39062
  const index = entry.indexOf(separator);
@@ -38563,7 +39673,7 @@ function createSessionMethods(ctx) {
38563
39673
  }
38564
39674
  const record = await storage.createSession(payload.key);
38565
39675
  await storage.appendAudit({
38566
- id: randomUUID17(),
39676
+ id: randomUUID18(),
38567
39677
  time: (/* @__PURE__ */ new Date()).toISOString(),
38568
39678
  type: "session.create",
38569
39679
  payload: { sessionId: record.id, key: payload.key }
@@ -38659,7 +39769,7 @@ function createSessionMethods(ctx) {
38659
39769
  const sessionConfig = payload.scope && payload.scope.trim() ? { ...config.session, scope: payload.scope.trim() } : config.session;
38660
39770
  const record = await resolveSessionWithPolicy(storage, payload, sessionConfig, sessionIdentityMap);
38661
39771
  await storage.appendAudit({
38662
- id: randomUUID17(),
39772
+ id: randomUUID18(),
38663
39773
  time: (/* @__PURE__ */ new Date()).toISOString(),
38664
39774
  type: "session.resolve",
38665
39775
  payload: { sessionId: record.id, key: record.key }
@@ -38693,10 +39803,14 @@ function createSessionMethods(ctx) {
38693
39803
  agentId: payload.agentId,
38694
39804
  laneId: payload.laneId,
38695
39805
  queueMode: payload.queueMode,
38696
- announceMode: payload.announceMode
39806
+ announceMode: payload.announceMode,
39807
+ pinProviderId: payload.pinProviderId,
39808
+ pinModel: payload.pinModel,
39809
+ pinReason: payload.pinReason,
39810
+ pinUntil: payload.pinUntil
38697
39811
  });
38698
39812
  await storage.appendAudit({
38699
- id: randomUUID17(),
39813
+ id: randomUUID18(),
38700
39814
  time: (/* @__PURE__ */ new Date()).toISOString(),
38701
39815
  type: "session.route.set",
38702
39816
  payload: {
@@ -38704,7 +39818,11 @@ function createSessionMethods(ctx) {
38704
39818
  agentId: record.agentId,
38705
39819
  laneId: record.laneId,
38706
39820
  queueMode: record.queueMode,
38707
- announceMode: record.announceMode
39821
+ announceMode: record.announceMode,
39822
+ pinProviderId: record.pinProviderId,
39823
+ pinModel: record.pinModel,
39824
+ pinReason: record.pinReason,
39825
+ pinUntil: record.pinUntil
38708
39826
  }
38709
39827
  });
38710
39828
  return record;
@@ -38766,7 +39884,7 @@ function createSessionMethods(ctx) {
38766
39884
  });
38767
39885
  writeConfigFile(configPath, merged);
38768
39886
  await storage.appendAudit({
38769
- id: randomUUID17(),
39887
+ id: randomUUID18(),
38770
39888
  time: (/* @__PURE__ */ new Date()).toISOString(),
38771
39889
  type: "session.continuity.set",
38772
39890
  payload: { continuity: parsed }
@@ -38778,7 +39896,7 @@ function createSessionMethods(ctx) {
38778
39896
  }
38779
39897
 
38780
39898
  // apps/gateway/dist/rpc/methods/session-prefs.js
38781
- import { randomUUID as randomUUID18 } from "node:crypto";
39899
+ import { randomUUID as randomUUID19 } from "node:crypto";
38782
39900
  function createSessionPrefsMethods(ctx) {
38783
39901
  const { runtime, requireAdmin: requireAdmin2 } = ctx;
38784
39902
  const { storage } = runtime;
@@ -38816,7 +39934,7 @@ function createSessionPrefsMethods(ctx) {
38816
39934
  proactiveCooldownMs: payload.proactiveCooldownMs
38817
39935
  });
38818
39936
  await storage.appendAudit({
38819
- id: randomUUID18(),
39937
+ id: randomUUID19(),
38820
39938
  time: (/* @__PURE__ */ new Date()).toISOString(),
38821
39939
  type: "session.prefs.set",
38822
39940
  payload: {
@@ -38844,7 +39962,7 @@ function createSessionPrefsMethods(ctx) {
38844
39962
  }
38845
39963
  await storage.clearSessionPrefs(payload.sessionId);
38846
39964
  await storage.appendAudit({
38847
- id: randomUUID18(),
39965
+ id: randomUUID19(),
38848
39966
  time: (/* @__PURE__ */ new Date()).toISOString(),
38849
39967
  type: "session.prefs.clear",
38850
39968
  payload: { sessionId: payload.sessionId }
@@ -38878,7 +39996,7 @@ function createSessionPrefsMethods(ctx) {
38878
39996
  cleared.push({ sessionId: session.id, channel });
38879
39997
  }
38880
39998
  await storage.appendAudit({
38881
- id: randomUUID18(),
39999
+ id: randomUUID19(),
38882
40000
  time: (/* @__PURE__ */ new Date()).toISOString(),
38883
40001
  type: "sessions.routing.clear",
38884
40002
  payload: {
@@ -38894,7 +40012,7 @@ function createSessionPrefsMethods(ctx) {
38894
40012
  }
38895
40013
 
38896
40014
  // apps/gateway/dist/rpc/methods/devices.js
38897
- import { randomUUID as randomUUID19 } from "node:crypto";
40015
+ import { randomUUID as randomUUID20 } from "node:crypto";
38898
40016
  function createDeviceMethods(ctx) {
38899
40017
  const { runtime, deps, requireAdmin: requireAdmin2 } = ctx;
38900
40018
  const { storage, connectionAuth, pendingChallenges, broadcastEvent } = runtime;
@@ -38932,7 +40050,7 @@ function createDeviceMethods(ctx) {
38932
40050
  const now = (/* @__PURE__ */ new Date()).toISOString();
38933
40051
  const tokenData = issueDeviceToken();
38934
40052
  const record = {
38935
- id: randomUUID19(),
40053
+ id: randomUUID20(),
38936
40054
  name: payload?.name,
38937
40055
  type: payload?.type,
38938
40056
  role: payload?.role,
@@ -38948,7 +40066,7 @@ function createDeviceMethods(ctx) {
38948
40066
  };
38949
40067
  await storage.createDevice(record);
38950
40068
  await storage.appendAudit({
38951
- id: randomUUID19(),
40069
+ id: randomUUID20(),
38952
40070
  time: now,
38953
40071
  type: "device.create",
38954
40072
  payload: { deviceId: record.id, name: record.name, type: record.type }
@@ -38983,7 +40101,7 @@ function createDeviceMethods(ctx) {
38983
40101
  throw new Error("Device not found");
38984
40102
  }
38985
40103
  await storage.appendAudit({
38986
- id: randomUUID19(),
40104
+ id: randomUUID20(),
38987
40105
  time: (/* @__PURE__ */ new Date()).toISOString(),
38988
40106
  type: "device.update",
38989
40107
  payload: { deviceId: payload.id }
@@ -39004,7 +40122,7 @@ function createDeviceMethods(ctx) {
39004
40122
  throw new Error("Device not found");
39005
40123
  }
39006
40124
  await storage.appendAudit({
39007
- id: randomUUID19(),
40125
+ id: randomUUID20(),
39008
40126
  time: (/* @__PURE__ */ new Date()).toISOString(),
39009
40127
  type: "device.revoke",
39010
40128
  payload: { deviceId: payload.id }
@@ -39069,7 +40187,7 @@ function createDeviceMethods(ctx) {
39069
40187
  }
39070
40188
 
39071
40189
  // apps/gateway/dist/rpc/methods/pairings.js
39072
- import { randomBytes as randomBytes4, randomUUID as randomUUID20 } from "node:crypto";
40190
+ import { randomBytes as randomBytes4, randomUUID as randomUUID21 } from "node:crypto";
39073
40191
 
39074
40192
  // apps/gateway/dist/auth/pairing.js
39075
40193
  import { createHmac as createHmac2, randomBytes as randomBytes3 } from "node:crypto";
@@ -39175,7 +40293,7 @@ function createPairingMethods(ctx) {
39175
40293
  const now = /* @__PURE__ */ new Date();
39176
40294
  const seed = randomBytes4(32).toString("hex");
39177
40295
  const record = {
39178
- id: randomUUID20(),
40296
+ id: randomUUID21(),
39179
40297
  seed,
39180
40298
  name: payload?.name,
39181
40299
  type: payload?.type,
@@ -39228,7 +40346,7 @@ function createPairingMethods(ctx) {
39228
40346
  if (record.claimed?.deviceId) {
39229
40347
  throw new Error("Pairing already claimed");
39230
40348
  }
39231
- const deviceId = payload.deviceId ?? randomUUID20();
40349
+ const deviceId = payload.deviceId ?? randomUUID21();
39232
40350
  const publicKeyAlgorithm = payload.publicKeyAlgorithm === "p256" ? "p256" : "ed25519";
39233
40351
  record.claimed = {
39234
40352
  deviceId,
@@ -39275,7 +40393,7 @@ function createPairingMethods(ctx) {
39275
40393
  }
39276
40394
  const now = (/* @__PURE__ */ new Date()).toISOString();
39277
40395
  const device = {
39278
- id: record.claimed.deviceId ?? randomUUID20(),
40396
+ id: record.claimed.deviceId ?? randomUUID21(),
39279
40397
  name: record.claimed.name ?? record.name,
39280
40398
  type: record.claimed.type ?? record.type,
39281
40399
  role: record.claimed.role ?? record.role,
@@ -39291,7 +40409,7 @@ function createPairingMethods(ctx) {
39291
40409
  };
39292
40410
  await storage.createDevice(device);
39293
40411
  await storage.appendAudit({
39294
- id: randomUUID20(),
40412
+ id: randomUUID21(),
39295
40413
  time: now,
39296
40414
  type: "pairing.approve",
39297
40415
  payload: { pairingId: record.id, deviceId: device.id }
@@ -39314,7 +40432,7 @@ function createPairingMethods(ctx) {
39314
40432
  }
39315
40433
  pairingStore.delete(payload.id);
39316
40434
  await storage.appendAudit({
39317
- id: randomUUID20(),
40435
+ id: randomUUID21(),
39318
40436
  time: (/* @__PURE__ */ new Date()).toISOString(),
39319
40437
  type: "pairing.reject",
39320
40438
  payload: { pairingId: payload.id }
@@ -39326,7 +40444,7 @@ function createPairingMethods(ctx) {
39326
40444
  }
39327
40445
 
39328
40446
  // apps/gateway/dist/rpc/methods/channel-pairings.js
39329
- import { randomBytes as randomBytes5, randomUUID as randomUUID21 } from "node:crypto";
40447
+ import { randomBytes as randomBytes5, randomUUID as randomUUID22 } from "node:crypto";
39330
40448
  function createChannelPairingMethods(ctx) {
39331
40449
  const { runtime, deps, requireAdmin: requireAdmin2 } = ctx;
39332
40450
  const { storage, sendChannelMessage } = runtime;
@@ -39355,7 +40473,7 @@ function createChannelPairingMethods(ctx) {
39355
40473
  const now = Date.now();
39356
40474
  const seed = randomBytes5(32).toString("hex");
39357
40475
  const record = {
39358
- id: randomUUID21(),
40476
+ id: randomUUID22(),
39359
40477
  channel,
39360
40478
  seed,
39361
40479
  note: payload?.note,
@@ -39391,7 +40509,7 @@ function createChannelPairingMethods(ctx) {
39391
40509
  note: record.note
39392
40510
  });
39393
40511
  await storage.appendAudit({
39394
- id: randomUUID21(),
40512
+ id: randomUUID22(),
39395
40513
  time: (/* @__PURE__ */ new Date()).toISOString(),
39396
40514
  type: "channel.pairing.approve",
39397
40515
  payload: {
@@ -39420,7 +40538,7 @@ function createChannelPairingMethods(ctx) {
39420
40538
  }
39421
40539
  channelPairingStore.delete(payload.id);
39422
40540
  await storage.appendAudit({
39423
- id: randomUUID21(),
40541
+ id: randomUUID22(),
39424
40542
  time: (/* @__PURE__ */ new Date()).toISOString(),
39425
40543
  type: "channel.pairing.reject",
39426
40544
  payload: { pairingId: payload.id, channel: record.channel }
@@ -39435,7 +40553,7 @@ function createChannelPairingMethods(ctx) {
39435
40553
  }
39436
40554
 
39437
40555
  // apps/gateway/dist/rpc/methods/channel-allowlists.js
39438
- import { randomUUID as randomUUID22 } from "node:crypto";
40556
+ import { randomUUID as randomUUID23 } from "node:crypto";
39439
40557
  function createChannelAllowlistMethods(ctx) {
39440
40558
  const { runtime, requireAdmin: requireAdmin2 } = ctx;
39441
40559
  const { storage, sendChannelMessage } = runtime;
@@ -39461,7 +40579,7 @@ function createChannelAllowlistMethods(ctx) {
39461
40579
  throw new Error("Allow record not found");
39462
40580
  }
39463
40581
  await storage.appendAudit({
39464
- id: randomUUID22(),
40582
+ id: randomUUID23(),
39465
40583
  time: (/* @__PURE__ */ new Date()).toISOString(),
39466
40584
  type: "channel.allow.revoke",
39467
40585
  payload: {
@@ -39479,7 +40597,7 @@ function createChannelAllowlistMethods(ctx) {
39479
40597
  }
39480
40598
 
39481
40599
  // apps/gateway/dist/rpc/methods/approvals.js
39482
- import { randomUUID as randomUUID23 } from "node:crypto";
40600
+ import { randomUUID as randomUUID24 } from "node:crypto";
39483
40601
  function createApprovalMethods(ctx) {
39484
40602
  const { runtime, deps, requireAdmin: requireAdmin2 } = ctx;
39485
40603
  const { storage, connectionAuth, broadcastEvent, sendChannelMessage, maybeAppendSmritiMemory } = runtime;
@@ -39545,7 +40663,7 @@ function createApprovalMethods(ctx) {
39545
40663
  });
39546
40664
  if (rejected) {
39547
40665
  await storage.appendAudit({
39548
- id: randomUUID23(),
40666
+ id: randomUUID24(),
39549
40667
  time: now.toISOString(),
39550
40668
  type: "approval.reject",
39551
40669
  payload: {
@@ -39568,7 +40686,7 @@ function createApprovalMethods(ctx) {
39568
40686
  });
39569
40687
  if (approved) {
39570
40688
  await storage.appendAudit({
39571
- id: randomUUID23(),
40689
+ id: randomUUID24(),
39572
40690
  time: now.toISOString(),
39573
40691
  type: "approval.approve",
39574
40692
  payload: {
@@ -39618,7 +40736,7 @@ function createApprovalMethods(ctx) {
39618
40736
  });
39619
40737
  if (updated) {
39620
40738
  await storage.appendAudit({
39621
- id: randomUUID23(),
40739
+ id: randomUUID24(),
39622
40740
  time: (/* @__PURE__ */ new Date()).toISOString(),
39623
40741
  type: execution.ok ? "approval.fulfilled" : "approval.failed",
39624
40742
  payload: {
@@ -39638,7 +40756,7 @@ function createApprovalMethods(ctx) {
39638
40756
  }
39639
40757
 
39640
40758
  // apps/gateway/dist/rpc/methods/providers.js
39641
- import { randomUUID as randomUUID24 } from "node:crypto";
40759
+ import { randomUUID as randomUUID25 } from "node:crypto";
39642
40760
  import path20 from "node:path";
39643
40761
 
39644
40762
  // apps/gateway/dist/utils/errors.js
@@ -40004,10 +41122,10 @@ async function fetchOllamaModels(entry, providerId, logger) {
40004
41122
  return fetchModelList(providerId, entryWithBase, logger);
40005
41123
  }
40006
41124
  async function checkLocalProviders(params) {
40007
- const { entries, logger, update } = params;
41125
+ const { entries: entries2, logger, update } = params;
40008
41126
  const reports = {};
40009
41127
  const now = () => (/* @__PURE__ */ new Date()).toISOString();
40010
- for (const [providerId, entry] of Object.entries(entries)) {
41128
+ for (const [providerId, entry] of Object.entries(entries2)) {
40011
41129
  if (entry.type !== "ollama")
40012
41130
  continue;
40013
41131
  const required = resolveRequiredModels(entry);
@@ -40281,7 +41399,7 @@ function createProviderMethods(ctx) {
40281
41399
  });
40282
41400
  }
40283
41401
  await storage.appendAudit({
40284
- id: randomUUID24(),
41402
+ id: randomUUID25(),
40285
41403
  time: (/* @__PURE__ */ new Date()).toISOString(),
40286
41404
  type: "provider.ollama.autostart.set",
40287
41405
  payload: { enabled: payload.enabled }
@@ -40330,7 +41448,7 @@ function createProviderMethods(ctx) {
40330
41448
  meta: requestMeta
40331
41449
  });
40332
41450
  await storage.appendAudit({
40333
- id: randomUUID24(),
41451
+ id: randomUUID25(),
40334
41452
  time: (/* @__PURE__ */ new Date()).toISOString(),
40335
41453
  type: "provider.chat",
40336
41454
  payload: {
@@ -40908,7 +42026,7 @@ function createUsageMethods(ctx) {
40908
42026
  }
40909
42027
 
40910
42028
  // apps/gateway/dist/rpc/methods/channels.js
40911
- import { randomUUID as randomUUID25 } from "node:crypto";
42029
+ import { randomUUID as randomUUID26 } from "node:crypto";
40912
42030
  function createChannelMethods(ctx) {
40913
42031
  const { runtime } = ctx;
40914
42032
  const { config, providers, profile, updateProfile, broadcastEvent, storage, logger } = runtime;
@@ -40960,7 +42078,7 @@ function createChannelMethods(ctx) {
40960
42078
  });
40961
42079
  broadcastEvent("profile.updated", updated);
40962
42080
  await storage.appendAudit({
40963
- id: randomUUID25(),
42081
+ id: randomUUID26(),
40964
42082
  time: (/* @__PURE__ */ new Date()).toISOString(),
40965
42083
  type: "channel.setModel",
40966
42084
  payload: {
@@ -40995,7 +42113,7 @@ function createChannelMethods(ctx) {
40995
42113
  }
40996
42114
 
40997
42115
  // apps/gateway/dist/rpc/methods/models.js
40998
- import { randomUUID as randomUUID26 } from "node:crypto";
42116
+ import { randomUUID as randomUUID27 } from "node:crypto";
40999
42117
  function createModelMethods(ctx) {
41000
42118
  const { runtime } = ctx;
41001
42119
  const { providers, updateProfile, broadcastEvent, storage } = runtime;
@@ -41019,7 +42137,7 @@ function createModelMethods(ctx) {
41019
42137
  });
41020
42138
  broadcastEvent("profile.updated", updated);
41021
42139
  await storage.appendAudit({
41022
- id: randomUUID26(),
42140
+ id: randomUUID27(),
41023
42141
  time: (/* @__PURE__ */ new Date()).toISOString(),
41024
42142
  type: "model.set",
41025
42143
  payload: {
@@ -41402,7 +42520,7 @@ import path21 from "node:path";
41402
42520
 
41403
42521
  // packages/storage/src/file/io.ts
41404
42522
  import fs18 from "node:fs";
41405
- import { randomUUID as randomUUID27 } from "node:crypto";
42523
+ import { randomUUID as randomUUID28 } from "node:crypto";
41406
42524
 
41407
42525
  // packages/storage/src/file/types.ts
41408
42526
  var DEFAULT_STORE = {
@@ -41459,7 +42577,7 @@ var readJson2 = (path50, fallback) => {
41459
42577
  }
41460
42578
  };
41461
42579
  var writeJson = (path50, data2) => {
41462
- const tmp = `${path50}.${randomUUID27()}.tmp`;
42580
+ const tmp = `${path50}.${randomUUID28()}.tmp`;
41463
42581
  fs18.writeFileSync(tmp, JSON.stringify(data2, null, 2), "utf8");
41464
42582
  fs18.renameSync(tmp, path50);
41465
42583
  };
@@ -41595,7 +42713,7 @@ var writeAgentQueueStore = (paths, store) => {
41595
42713
 
41596
42714
  // packages/storage/src/file/sessions.ts
41597
42715
  import fs19 from "node:fs";
41598
- import { randomUUID as randomUUID28 } from "node:crypto";
42716
+ import { randomUUID as randomUUID29 } from "node:crypto";
41599
42717
  var createFileSessionStore = (paths) => {
41600
42718
  return {
41601
42719
  async getSessionByKey(key) {
@@ -41619,7 +42737,7 @@ var createFileSessionStore = (paths) => {
41619
42737
  }
41620
42738
  const now = (/* @__PURE__ */ new Date()).toISOString();
41621
42739
  const record = {
41622
- id: randomUUID28(),
42740
+ id: randomUUID29(),
41623
42741
  key,
41624
42742
  createdAt: now,
41625
42743
  updatedAt: now,
@@ -41918,7 +43036,7 @@ var createFileDeviceStore = (paths) => {
41918
43036
  };
41919
43037
 
41920
43038
  // packages/storage/src/file/channels.ts
41921
- import { randomUUID as randomUUID29 } from "node:crypto";
43039
+ import { randomUUID as randomUUID30 } from "node:crypto";
41922
43040
  var buildChannelKey = (channel, chatId, userId) => `${channel}:${chatId}:${userId ?? ""}`;
41923
43041
  var createFileChannelStore = (paths) => {
41924
43042
  return {
@@ -41959,7 +43077,7 @@ var createFileChannelStore = (paths) => {
41959
43077
  writeChannelStore(paths, store);
41960
43078
  return updated;
41961
43079
  }
41962
- const id = randomUUID29();
43080
+ const id = randomUUID30();
41963
43081
  const entry = {
41964
43082
  id,
41965
43083
  channel: record.channel,
@@ -41994,13 +43112,13 @@ var createFileChannelStore = (paths) => {
41994
43112
  };
41995
43113
 
41996
43114
  // packages/storage/src/file/approvals.ts
41997
- import { randomUUID as randomUUID30 } from "node:crypto";
43115
+ import { randomUUID as randomUUID31 } from "node:crypto";
41998
43116
  var createFileApprovalStore = (paths) => {
41999
43117
  return {
42000
43118
  async createApprovalRequest(request) {
42001
43119
  const store = readApprovalStore(paths);
42002
43120
  const now = (/* @__PURE__ */ new Date()).toISOString();
42003
- const id = request.id ?? randomUUID30();
43121
+ const id = request.id ?? randomUUID31();
42004
43122
  const existing = store.requests[id];
42005
43123
  if (existing) {
42006
43124
  return existing;
@@ -42062,7 +43180,7 @@ var createFileApprovalStore = (paths) => {
42062
43180
 
42063
43181
  // packages/storage/src/file/tool-policy.ts
42064
43182
  import fs20 from "node:fs";
42065
- import { randomUUID as randomUUID31 } from "node:crypto";
43183
+ import { randomUUID as randomUUID32 } from "node:crypto";
42066
43184
  var createFileToolPolicyStore = (paths) => {
42067
43185
  return {
42068
43186
  async getToolPolicy() {
@@ -42083,7 +43201,7 @@ var createFileToolPolicyStore = (paths) => {
42083
43201
  deny: policy.deny ?? [],
42084
43202
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
42085
43203
  };
42086
- const tmp = `${paths.toolPolicyPath}.${randomUUID31()}.tmp`;
43204
+ const tmp = `${paths.toolPolicyPath}.${randomUUID32()}.tmp`;
42087
43205
  fs20.writeFileSync(tmp, JSON.stringify(record, null, 2), "utf8");
42088
43206
  fs20.renameSync(tmp, paths.toolPolicyPath);
42089
43207
  return record;
@@ -42486,7 +43604,7 @@ var FileStorage = class {
42486
43604
  import path22 from "node:path";
42487
43605
  async function createStorage(options) {
42488
43606
  if (options.driver === "sqlite") {
42489
- const module = await import("./chunks/sqlite-JPF5TICX.js");
43607
+ const module = await import("./chunks/sqlite-DHUQGPR5.js");
42490
43608
  const sqlitePath = options.sqlitePath ?? path22.join(options.dataDir, "vaayu.db");
42491
43609
  return new module.SqliteStorage({ sqlitePath });
42492
43610
  }
@@ -42494,7 +43612,7 @@ async function createStorage(options) {
42494
43612
  if (!options.postgresUrl) {
42495
43613
  throw new Error("Postgres driver requires postgresUrl");
42496
43614
  }
42497
- const module = await import("./chunks/postgres-3ZXBYTPC.js");
43615
+ const module = await import("./chunks/postgres-WLH3D5HG.js");
42498
43616
  return module.PostgresStorage.create({ url: options.postgresUrl });
42499
43617
  }
42500
43618
  return new FileStorage({ dataDir: options.dataDir });
@@ -43130,14 +44248,14 @@ function createDualWriteStorage(opts) {
43130
44248
  }
43131
44249
 
43132
44250
  // apps/gateway/dist/rpc/methods/memories.js
43133
- import { randomUUID as randomUUID32 } from "node:crypto";
44251
+ import { randomUUID as randomUUID33 } from "node:crypto";
43134
44252
  function createMemoryMethods(ctx) {
43135
44253
  const { runtime, deps, requireAdmin: requireAdmin2 } = ctx;
43136
44254
  const { storage, logger } = runtime;
43137
44255
  const pendingDeletes = /* @__PURE__ */ new Map();
43138
44256
  const pendingPurges = /* @__PURE__ */ new Map();
43139
44257
  const issuePurgeToken = (connectionId, payload) => {
43140
- const token = randomUUID32();
44258
+ const token = randomUUID33();
43141
44259
  pendingPurges.set(token, {
43142
44260
  token,
43143
44261
  expiresAt: Date.now() + 5 * 60 * 1e3,
@@ -43379,7 +44497,7 @@ function createMemoryMethods(ctx) {
43379
44497
 
43380
44498
  // apps/gateway/dist/rpc/methods/proactives.js
43381
44499
  import path25 from "node:path";
43382
- import { randomUUID as randomUUID33 } from "node:crypto";
44500
+ import { randomUUID as randomUUID34 } from "node:crypto";
43383
44501
  function createProactiveMethods(ctx) {
43384
44502
  const { runtime, deps, requireAdmin: requireAdmin2 } = ctx;
43385
44503
  const { storage, logger } = runtime;
@@ -43507,7 +44625,7 @@ function createProactiveMethods(ctx) {
43507
44625
  });
43508
44626
  }
43509
44627
  await storage.appendAudit({
43510
- id: randomUUID33(),
44628
+ id: randomUUID34(),
43511
44629
  time: (/* @__PURE__ */ new Date()).toISOString(),
43512
44630
  type: "proactive.config.set",
43513
44631
  payload: { proactive: parsed }
@@ -43526,7 +44644,7 @@ function createProactiveMethods(ctx) {
43526
44644
  }
43527
44645
  deps.proactiveManager.stop();
43528
44646
  await storage.appendAudit({
43529
- id: randomUUID33(),
44647
+ id: randomUUID34(),
43530
44648
  time: (/* @__PURE__ */ new Date()).toISOString(),
43531
44649
  type: "proactive.pause",
43532
44650
  payload: {}
@@ -43549,7 +44667,7 @@ function createProactiveMethods(ctx) {
43549
44667
  }
43550
44668
  deps.proactiveManager.start();
43551
44669
  await storage.appendAudit({
43552
- id: randomUUID33(),
44670
+ id: randomUUID34(),
43553
44671
  time: (/* @__PURE__ */ new Date()).toISOString(),
43554
44672
  type: "proactive.resume",
43555
44673
  payload: {}
@@ -43790,7 +44908,7 @@ var MAX_MESSAGE_SIZE = 1024 * 1024;
43790
44908
  var MAX_MESSAGE_SIZE2 = 20 * 1024;
43791
44909
 
43792
44910
  // packages/core/src/sync.ts
43793
- import { randomUUID as randomUUID34 } from "node:crypto";
44911
+ import { randomUUID as randomUUID35 } from "node:crypto";
43794
44912
  import fs26 from "node:fs";
43795
44913
  import path28 from "node:path";
43796
44914
  var DEVICE_FILE = ".vaayu-device";
@@ -43806,7 +44924,7 @@ function getDeviceId(dataDir) {
43806
44924
  }
43807
44925
  } catch {
43808
44926
  }
43809
- const deviceId = `device_${randomUUID34().replace(/-/g, "").slice(0, 16)}`;
44927
+ const deviceId = `device_${randomUUID35().replace(/-/g, "").slice(0, 16)}`;
43810
44928
  try {
43811
44929
  fs26.writeFileSync(devicePath, deviceId, "utf8");
43812
44930
  } catch {
@@ -44341,7 +45459,7 @@ function createSyncMethods(ctx) {
44341
45459
  }
44342
45460
 
44343
45461
  // apps/gateway/dist/rpc/methods/reactions.js
44344
- import { randomUUID as randomUUID35 } from "node:crypto";
45462
+ import { randomUUID as randomUUID36 } from "node:crypto";
44345
45463
  function createReactionMethods(ctx) {
44346
45464
  const { runtime } = ctx;
44347
45465
  const { storage } = runtime;
@@ -44368,7 +45486,7 @@ function createReactionMethods(ctx) {
44368
45486
  }
44369
45487
  const reaction = storage.setMessageReaction(payload.sessionId, payload.messageId, payload.reaction);
44370
45488
  await storage.appendAudit({
44371
- id: randomUUID35(),
45489
+ id: randomUUID36(),
44372
45490
  time: (/* @__PURE__ */ new Date()).toISOString(),
44373
45491
  type: "message.react",
44374
45492
  payload: {
@@ -45202,7 +46320,7 @@ function resolveBridgeRequest(requestId, result) {
45202
46320
  }
45203
46321
 
45204
46322
  // apps/gateway/dist/gateway/bridge-registry.js
45205
- import { randomUUID as randomUUID36 } from "node:crypto";
46323
+ import { randomUUID as randomUUID37 } from "node:crypto";
45206
46324
  var BridgeRegistry = class {
45207
46325
  bridges = /* @__PURE__ */ new Map();
45208
46326
  /** connectionId → bridgeId for fast lookup on disconnect. */
@@ -45212,7 +46330,7 @@ var BridgeRegistry = class {
45212
46330
  * Returns the generated bridge ID.
45213
46331
  */
45214
46332
  register(connectionId, manifest) {
45215
- const id = `bridge-${manifest.provider}-${randomUUID36().slice(0, 8)}`;
46333
+ const id = `bridge-${manifest.provider}-${randomUUID37().slice(0, 8)}`;
45216
46334
  const now = (/* @__PURE__ */ new Date()).toISOString();
45217
46335
  const bridge = {
45218
46336
  id,
@@ -45676,8 +46794,8 @@ function collectSkillPackDirs(root) {
45676
46794
  }
45677
46795
  const out = [];
45678
46796
  const stack = [];
45679
- const entries = fs28.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !SKILL_PACK_SKIP_DIRS.has(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
45680
- for (const entry of entries.reverse()) {
46797
+ const entries2 = fs28.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !SKILL_PACK_SKIP_DIRS.has(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
46798
+ for (const entry of entries2.reverse()) {
45681
46799
  stack.push(path30.join(root, entry.name));
45682
46800
  }
45683
46801
  while (stack.length) {
@@ -45911,7 +47029,7 @@ function handleSkillLogoByIdRequest(skillId, _req, res, repoRoot) {
45911
47029
 
45912
47030
  // apps/gateway/dist/routes/bandha.js
45913
47031
  var import_qrcode2 = __toESM(require_lib(), 1);
45914
- import { randomBytes as randomBytes6, randomUUID as randomUUID37 } from "node:crypto";
47032
+ import { randomBytes as randomBytes6, randomUUID as randomUUID38 } from "node:crypto";
45915
47033
  var VALID_CHANNELS = /* @__PURE__ */ new Set([
45916
47034
  "telegram",
45917
47035
  "whatsapp",
@@ -45970,7 +47088,7 @@ function handleCreatePairing(req, res, channel, deps) {
45970
47088
  const now = Date.now();
45971
47089
  const seed = randomBytes6(32).toString("hex");
45972
47090
  const record = {
45973
- id: randomUUID37(),
47091
+ id: randomUUID38(),
45974
47092
  channel,
45975
47093
  seed,
45976
47094
  note: payload?.note,
@@ -46025,7 +47143,7 @@ function handleCancelPairing(res, id, deps) {
46025
47143
  }
46026
47144
  deps.channelPairingStore.delete(id);
46027
47145
  deps.storage.appendAudit({
46028
- id: randomUUID37(),
47146
+ id: randomUUID38(),
46029
47147
  time: (/* @__PURE__ */ new Date()).toISOString(),
46030
47148
  type: "bandha.pairing.cancelled",
46031
47149
  payload: { pairingId: id, channel: record.channel }
@@ -46053,7 +47171,7 @@ async function handleApprovePairing(res, id, deps) {
46053
47171
  note: record.note
46054
47172
  });
46055
47173
  await deps.storage.appendAudit({
46056
- id: randomUUID37(),
47174
+ id: randomUUID38(),
46057
47175
  time: (/* @__PURE__ */ new Date()).toISOString(),
46058
47176
  type: "bandha.pairing.approved",
46059
47177
  payload: {
@@ -46319,7 +47437,7 @@ function createHttpServer(state, repoRoot, methods, logger, healthOptions, bandh
46319
47437
  }
46320
47438
 
46321
47439
  // apps/gateway/dist/ws/setup.js
46322
- import { randomUUID as randomUUID38, randomBytes as randomBytes7, createHash as createHash6 } from "node:crypto";
47440
+ import { randomUUID as randomUUID39, randomBytes as randomBytes7, createHash as createHash6 } from "node:crypto";
46323
47441
 
46324
47442
  // ../node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
46325
47443
  var import_stream = __toESM(require_stream(), 1);
@@ -46341,7 +47459,7 @@ var SESSION_ID_METHOD_POLICY = {
46341
47459
  "message.react": { inferFromConnection: true },
46342
47460
  "message.reactions": { inferFromConnection: true }
46343
47461
  };
46344
- function isRecord(value) {
47462
+ function isRecord2(value) {
46345
47463
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
46346
47464
  }
46347
47465
  function parseSessionId(raw) {
@@ -46363,12 +47481,12 @@ function rememberConnectionSession(metadata, sessionId) {
46363
47481
  metadata.lastSessionId = normalized;
46364
47482
  }
46365
47483
  function extractSessionIdFromResult(method, result) {
46366
- if (!isRecord(result))
47484
+ if (!isRecord2(result))
46367
47485
  return void 0;
46368
47486
  if (method === "session.resolve" || method === "session.create" || method === "session.get") {
46369
47487
  return parseSessionId(result.id);
46370
47488
  }
46371
- if (method === "agent.run" && isRecord(result.session)) {
47489
+ if (method === "agent.run" && isRecord2(result.session)) {
46372
47490
  return parseSessionId(result.session.id);
46373
47491
  }
46374
47492
  return void 0;
@@ -46381,7 +47499,7 @@ function resolveSessionScopedParams(params) {
46381
47499
  }
46382
47500
  const knownSessionCount = metadata?.sessionIds.size ?? 0;
46383
47501
  const canInfer = Boolean(policy.inferFromConnection && knownSessionCount === 1);
46384
- const payload = isRecord(rawParams) ? { ...rawParams } : {};
47502
+ const payload = isRecord2(rawParams) ? { ...rawParams } : {};
46385
47503
  const rawSessionId = payload.sessionId;
46386
47504
  if (rawSessionId !== void 0 && rawSessionId !== null) {
46387
47505
  const explicitSessionId = parseSessionId(rawSessionId);
@@ -46508,7 +47626,7 @@ function setupWebSocket(params) {
46508
47626
  const connectionSessionMetadata = /* @__PURE__ */ new Map();
46509
47627
  const deviceActivityLog2 = getDeviceActivityLog();
46510
47628
  wss.on("connection", (socket, request) => {
46511
- const connectionId = randomUUID38();
47629
+ const connectionId = randomUUID39();
46512
47630
  const remoteAddress = request.socket.remoteAddress;
46513
47631
  let isConnected = false;
46514
47632
  let eventSeq = 0;
@@ -46986,7 +48104,7 @@ memory.allowed=${memoryAllowed() ? "yes" : "no"}`;
46986
48104
  }
46987
48105
 
46988
48106
  // apps/gateway/dist/channel/telegram-slash-session.js
46989
- import { randomUUID as randomUUID39 } from "node:crypto";
48107
+ import { randomUUID as randomUUID40 } from "node:crypto";
46990
48108
  async function handleSlashSession(ctx, commandName, commandArgs) {
46991
48109
  if (commandName !== "session")
46992
48110
  return null;
@@ -47011,7 +48129,7 @@ proactiveChannels=${channelLabel}`;
47011
48129
  if (["clear", "reset"].includes(sub)) {
47012
48130
  await storage.clearSessionPrefs(sessionId);
47013
48131
  await storage.appendAudit({
47014
- id: randomUUID39(),
48132
+ id: randomUUID40(),
47015
48133
  time: (/* @__PURE__ */ new Date()).toISOString(),
47016
48134
  type: "session.prefs.clear",
47017
48135
  payload: { sessionId, normalization }
@@ -47028,7 +48146,7 @@ proactiveChannels=${channelLabel}`;
47028
48146
  autoRouting: true
47029
48147
  });
47030
48148
  await storage.appendAudit({
47031
- id: randomUUID39(),
48149
+ id: randomUUID40(),
47032
48150
  time: (/* @__PURE__ */ new Date()).toISOString(),
47033
48151
  type: "session.prefs.set",
47034
48152
  payload: {
@@ -47051,7 +48169,7 @@ proactiveChannels=${channelLabel}`;
47051
48169
  autoRouting: false
47052
48170
  });
47053
48171
  await storage.appendAudit({
47054
- id: randomUUID39(),
48172
+ id: randomUUID40(),
47055
48173
  time: (/* @__PURE__ */ new Date()).toISOString(),
47056
48174
  type: "session.prefs.set",
47057
48175
  payload: {
@@ -47101,7 +48219,7 @@ proactiveChannels=${channelLabel}`;
47101
48219
  autoRouting: false
47102
48220
  });
47103
48221
  await storage.appendAudit({
47104
- id: randomUUID39(),
48222
+ id: randomUUID40(),
47105
48223
  time: (/* @__PURE__ */ new Date()).toISOString(),
47106
48224
  type: "session.prefs.set",
47107
48225
  payload: {
@@ -47130,7 +48248,7 @@ channels=${channelLabel}`;
47130
48248
  proactivePausedUntil: null
47131
48249
  });
47132
48250
  await storage.appendAudit({
47133
- id: randomUUID39(),
48251
+ id: randomUUID40(),
47134
48252
  time: (/* @__PURE__ */ new Date()).toISOString(),
47135
48253
  type: "session.prefs.set",
47136
48254
  payload: { sessionId, proactiveAllowed: updated.proactiveAllowed, normalization }
@@ -47144,7 +48262,7 @@ channels=${channelLabel}`;
47144
48262
  proactiveDeliver: false
47145
48263
  });
47146
48264
  await storage.appendAudit({
47147
- id: randomUUID39(),
48265
+ id: randomUUID40(),
47148
48266
  time: (/* @__PURE__ */ new Date()).toISOString(),
47149
48267
  type: "session.prefs.set",
47150
48268
  payload: { sessionId, proactiveAllowed: updated.proactiveAllowed, normalization }
@@ -47161,7 +48279,7 @@ channels=${channelLabel}`;
47161
48279
  proactivePausedUntil: until
47162
48280
  });
47163
48281
  await storage.appendAudit({
47164
- id: randomUUID39(),
48282
+ id: randomUUID40(),
47165
48283
  time: (/* @__PURE__ */ new Date()).toISOString(),
47166
48284
  type: "session.prefs.set",
47167
48285
  payload: { sessionId, proactivePausedUntil: updated.proactivePausedUntil, normalization }
@@ -47178,7 +48296,7 @@ channels=${channelLabel}`;
47178
48296
  proactiveAllowedChannels: null
47179
48297
  });
47180
48298
  await storage.appendAudit({
47181
- id: randomUUID39(),
48299
+ id: randomUUID40(),
47182
48300
  time: (/* @__PURE__ */ new Date()).toISOString(),
47183
48301
  type: "session.prefs.set",
47184
48302
  payload: { sessionId, proactiveAllowedChannels: updated2.proactiveAllowedChannels, normalization }
@@ -47190,7 +48308,7 @@ channels=${channelLabel}`;
47190
48308
  proactiveAllowedChannels: []
47191
48309
  });
47192
48310
  await storage.appendAudit({
47193
- id: randomUUID39(),
48311
+ id: randomUUID40(),
47194
48312
  time: (/* @__PURE__ */ new Date()).toISOString(),
47195
48313
  type: "session.prefs.set",
47196
48314
  payload: { sessionId, proactiveAllowedChannels: updated2.proactiveAllowedChannels, normalization }
@@ -47205,7 +48323,7 @@ channels=${channelLabel}`;
47205
48323
  proactiveAllowedChannels: channels
47206
48324
  });
47207
48325
  await storage.appendAudit({
47208
- id: randomUUID39(),
48326
+ id: randomUUID40(),
47209
48327
  time: (/* @__PURE__ */ new Date()).toISOString(),
47210
48328
  type: "session.prefs.set",
47211
48329
  payload: { sessionId, proactiveAllowedChannels: updated.proactiveAllowedChannels, normalization }
@@ -47218,7 +48336,7 @@ channels=${channelLabel}`;
47218
48336
  }
47219
48337
 
47220
48338
  // apps/gateway/dist/channel/telegram-slash-routing.js
47221
- import { randomUUID as randomUUID40 } from "node:crypto";
48339
+ import { randomUUID as randomUUID41 } from "node:crypto";
47222
48340
  async function handleSlashRouting(ctx, commandName, commandArgs) {
47223
48341
  const { runtime, config, telegramConfig, providers, storage, sessionId, broadcastEvent, updateProfile, appendPreferenceMemory, listProviderModels, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, inferProviderFromModel: inferProviderFromModel2 } = ctx;
47224
48342
  const clearSessionRoutingOverrides = async () => {
@@ -47353,7 +48471,7 @@ async function handleSlashRouting(ctx, commandName, commandArgs) {
47353
48471
  autoRouting: false
47354
48472
  });
47355
48473
  await storage.appendAudit({
47356
- id: randomUUID40(),
48474
+ id: randomUUID41(),
47357
48475
  time: (/* @__PURE__ */ new Date()).toISOString(),
47358
48476
  type: "session.prefs.set",
47359
48477
  payload: { sessionId }
@@ -47409,7 +48527,7 @@ autoRouting=${updated.autoRoutingDefault ? "on" : "off"}`;
47409
48527
  if (commandName === "reset") {
47410
48528
  await storage.clearSessionPrefs(sessionId);
47411
48529
  await storage.appendAudit({
47412
- id: randomUUID40(),
48530
+ id: randomUUID41(),
47413
48531
  time: (/* @__PURE__ */ new Date()).toISOString(),
47414
48532
  type: "session.prefs.clear",
47415
48533
  payload: { sessionId }
@@ -47429,7 +48547,7 @@ autoRouting=${updated.autoRoutingDefault ? "on" : "off"}`;
47429
48547
  autoRouting: commandArgs === "on"
47430
48548
  });
47431
48549
  await storage.appendAudit({
47432
- id: randomUUID40(),
48550
+ id: randomUUID41(),
47433
48551
  time: (/* @__PURE__ */ new Date()).toISOString(),
47434
48552
  type: "session.prefs.set",
47435
48553
  payload: {
@@ -47535,7 +48653,7 @@ function formatCubeResponse(output) {
47535
48653
  }
47536
48654
 
47537
48655
  // apps/gateway/dist/channel/telegram-tool-execution.js
47538
- import { randomUUID as randomUUID41 } from "node:crypto";
48656
+ import { randomUUID as randomUUID42 } from "node:crypto";
47539
48657
 
47540
48658
  // apps/gateway/dist/formatters/weather.js
47541
48659
  function formatDuration(seconds) {
@@ -48078,10 +49196,10 @@ function summarizeUnknownValue(value) {
48078
49196
  return candidate.trim();
48079
49197
  }
48080
49198
  }
48081
- const entries = Object.entries(record).slice(0, 3);
48082
- if (!entries.length)
49199
+ const entries2 = Object.entries(record).slice(0, 3);
49200
+ if (!entries2.length)
48083
49201
  return "{}";
48084
- return entries.map(([k, v]) => `${k}=${summarizeUnknownValue(v)}`).join(", ");
49202
+ return entries2.map(([k, v]) => `${k}=${summarizeUnknownValue(v)}`).join(", ");
48085
49203
  }
48086
49204
  return String(value);
48087
49205
  }
@@ -48404,7 +49522,7 @@ async function executeTelegramTool(input) {
48404
49522
  if (guardMessage) {
48405
49523
  if (storage) {
48406
49524
  await storage.appendAudit({
48407
- id: randomUUID41(),
49525
+ id: randomUUID42(),
48408
49526
  time: (/* @__PURE__ */ new Date()).toISOString(),
48409
49527
  type: "guardian.block",
48410
49528
  payload: {
@@ -48426,7 +49544,7 @@ async function executeTelegramTool(input) {
48426
49544
  });
48427
49545
  if (storage) {
48428
49546
  await storage.appendAudit({
48429
- id: randomUUID41(),
49547
+ id: randomUUID42(),
48430
49548
  time: (/* @__PURE__ */ new Date()).toISOString(),
48431
49549
  type: "tool.invoke",
48432
49550
  payload: {
@@ -49567,7 +50685,7 @@ import fs31 from "node:fs";
49567
50685
  import { Readable } from "node:stream";
49568
50686
  import { pipeline } from "node:stream/promises";
49569
50687
  import { spawn as spawn5 } from "node:child_process";
49570
- import { randomUUID as randomUUID42 } from "node:crypto";
50688
+ import { randomUUID as randomUUID43 } from "node:crypto";
49571
50689
  function sanitizeArtifactFileName(value) {
49572
50690
  const base = path33.basename(value || "file");
49573
50691
  const cleaned = base.replace(/[\u0000-\u001f\u007f]/g, "").replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
@@ -49620,7 +50738,7 @@ async function stageTelegramAttachment(params) {
49620
50738
  const baseName = sanitizeArtifactFileName(attachment.fileName ?? `${attachment.kind}-${attachment.id}`);
49621
50739
  const ext = inferAttachmentExtension(baseName, attachment.mimeType, attachment.kind);
49622
50740
  const finalName = ext ? `${baseName.replace(/\.[^.]+$/, "")}.${ext}` : baseName;
49623
- const localPath = path33.join(dir, `${Date.now()}-${attachment.kind}-${randomUUID42()}-${finalName}`);
50741
+ const localPath = path33.join(dir, `${Date.now()}-${attachment.kind}-${randomUUID43()}-${finalName}`);
49624
50742
  await downloadToFile(url, localPath, 12e4);
49625
50743
  return { localPath, downloadUrl: url };
49626
50744
  }
@@ -49681,7 +50799,7 @@ function buildTelegramSystemPrompt(base, options) {
49681
50799
  function escapeHtml(value) {
49682
50800
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
49683
50801
  }
49684
- function normalizeText2(value) {
50802
+ function normalizeText4(value) {
49685
50803
  return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u0000/g, "").trim();
49686
50804
  }
49687
50805
  function truncateTelegramText(value, maxChars) {
@@ -49738,7 +50856,7 @@ function renderMarkdownToTelegramHtml(value) {
49738
50856
  return escaped;
49739
50857
  }
49740
50858
  function formatTelegramReply(content, options = {}) {
49741
- const cleaned = normalizeText2(content || "");
50859
+ const cleaned = normalizeText4(content || "");
49742
50860
  const tone = options.tone ?? "default";
49743
50861
  const includeHeader = options.includeHeader ?? false;
49744
50862
  const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
@@ -51517,10 +52635,10 @@ function registerAnrivaSkillTools(params) {
51517
52635
  }
51518
52636
 
51519
52637
  // apps/gateway/dist/gateway/kaala-setup.js
51520
- import { randomUUID as randomUUID46 } from "node:crypto";
52638
+ import { randomUUID as randomUUID47 } from "node:crypto";
51521
52639
 
51522
52640
  // apps/agent-kaala/src/broker.ts
51523
- import { randomUUID as randomUUID43 } from "node:crypto";
52641
+ import { randomUUID as randomUUID44 } from "node:crypto";
51524
52642
  var nowIso3 = () => (/* @__PURE__ */ new Date()).toISOString();
51525
52643
  var toErrorMessage2 = (error) => {
51526
52644
  if (!error) return "Unknown error";
@@ -51644,7 +52762,7 @@ function createKaalaBroker(deps, options) {
51644
52762
  if (existingId && tasks.has(existingId)) {
51645
52763
  return tasks.get(existingId).record;
51646
52764
  }
51647
- const id = existingId ?? randomUUID43();
52765
+ const id = existingId ?? randomUUID44();
51648
52766
  const laneName = input.lane ?? options.defaultLane;
51649
52767
  const lane = getLane(laneName);
51650
52768
  const totalQueued = Array.from(lanes.values()).reduce(
@@ -52331,12 +53449,12 @@ function createKaalaHealthTracker(options) {
52331
53449
  }
52332
53450
 
52333
53451
  // apps/gateway/dist/kaala/audit.js
52334
- import { randomUUID as randomUUID44 } from "node:crypto";
53452
+ import { randomUUID as randomUUID45 } from "node:crypto";
52335
53453
  async function appendKaalaAudit(storage, payload) {
52336
53454
  if (!storage.appendAudit)
52337
53455
  return;
52338
53456
  await storage.appendAudit({
52339
- id: randomUUID44(),
53457
+ id: randomUUID45(),
52340
53458
  time: nowIso(),
52341
53459
  type: payload.type,
52342
53460
  payload: {
@@ -52777,7 +53895,7 @@ function createKaalaMonitor(params) {
52777
53895
  }
52778
53896
 
52779
53897
  // apps/gateway/dist/kaala/persist.js
52780
- import { randomUUID as randomUUID45 } from "node:crypto";
53898
+ import { randomUUID as randomUUID46 } from "node:crypto";
52781
53899
  var toAgentRunRecord = (record) => {
52782
53900
  const metadata = record.metadata ?? {};
52783
53901
  const metadataWithPolicy = record.toolPolicy ? { ...metadata, toolPolicy: record.toolPolicy } : metadata;
@@ -52861,7 +53979,7 @@ function extractResultSummary(result) {
52861
53979
  }
52862
53980
  function buildEvent(event) {
52863
53981
  return {
52864
- id: randomUUID45(),
53982
+ id: randomUUID46(),
52865
53983
  runId: event.taskId,
52866
53984
  time: event.time,
52867
53985
  type: event.type,
@@ -53647,7 +54765,7 @@ async function setupKaala(params) {
53647
54765
  if (!storage.appendAgentRunEvent)
53648
54766
  return;
53649
54767
  void storage.appendAgentRunEvent({
53650
- id: randomUUID46(),
54768
+ id: randomUUID47(),
53651
54769
  runId: "kaala.health",
53652
54770
  time: snapshot.updatedAt,
53653
54771
  type: "health",
@@ -53768,7 +54886,7 @@ async function setupKaala(params) {
53768
54886
  expiresAt: new Date(now.getTime() + 9e5).toISOString()
53769
54887
  });
53770
54888
  await storage.appendAudit({
53771
- id: randomUUID46(),
54889
+ id: randomUUID47(),
53772
54890
  time: now.toISOString(),
53773
54891
  type: "kaala.escalation.request",
53774
54892
  payload: {
@@ -53797,9 +54915,9 @@ async function setupKaala(params) {
53797
54915
 
53798
54916
  // packages/nlu/src/rules/text.ts
53799
54917
  var normalizePunctuation = (text) => text.replace(/^[,.;:]+|[,.;:]+$/g, "");
53800
- var normalizeText3 = (text) => normalizePunctuation(text.trim()).replace(/\s+/g, " ").toLowerCase();
53801
- function tokenize2(text) {
53802
- const normalized = normalizeText3(text);
54918
+ var normalizeText5 = (text) => normalizePunctuation(text.trim()).replace(/\s+/g, " ").toLowerCase();
54919
+ function tokenize3(text) {
54920
+ const normalized = normalizeText5(text);
53803
54921
  if (!normalized) return [];
53804
54922
  return normalized.split(/\s+/).map((token) => token.replace(/[^a-z0-9]+/g, "")).filter(Boolean);
53805
54923
  }
@@ -53849,7 +54967,7 @@ function adjacentSwapAtMostOne(a, b) {
53849
54967
  }
53850
54968
  function fuzzyHasKeyword(text, keywords) {
53851
54969
  if (!text.trim()) return false;
53852
- const tokens = tokenize2(text);
54970
+ const tokens = tokenize3(text);
53853
54971
  if (!tokens.length) return false;
53854
54972
  for (const token of tokens) {
53855
54973
  for (const keyword of keywords) {
@@ -53900,14 +55018,14 @@ var extractLocation = (text) => {
53900
55018
  return void 0;
53901
55019
  };
53902
55020
  var extractDays = (text) => {
53903
- const match = normalizeText3(text).match(/\b(\d{1,2})\s*(?:days|day)\b/i);
55021
+ const match = normalizeText5(text).match(/\b(\d{1,2})\s*(?:days|day)\b/i);
53904
55022
  const raw = match?.[1];
53905
55023
  if (!raw) return void 0;
53906
55024
  const value = Number.parseInt(raw, 10);
53907
55025
  return Number.isFinite(value) ? value : void 0;
53908
55026
  };
53909
55027
  var extractHours = (text) => {
53910
- const match = normalizeText3(text).match(/\b(\d{1,2})\s*(?:hours|hour)\b/i);
55028
+ const match = normalizeText5(text).match(/\b(\d{1,2})\s*(?:hours|hour)\b/i);
53911
55029
  const raw = match?.[1];
53912
55030
  if (!raw) return void 0;
53913
55031
  const value = Number.parseInt(raw, 10);
@@ -54016,7 +55134,7 @@ function parseIsoDateFromText(text) {
54016
55134
  return `${date}T${time}`;
54017
55135
  }
54018
55136
  function parseRelativeDueAt(text) {
54019
- const lower = normalizeText3(text);
55137
+ const lower = normalizeText5(text);
54020
55138
  const match = lower.match(/\b(in|after)\s+(\d{1,2})\s*(hours|hour|days|day)\b/);
54021
55139
  if (!match?.[2] || !match?.[3]) return void 0;
54022
55140
  const value = Number.parseInt(match[2], 10);
@@ -54028,7 +55146,7 @@ function parseRelativeDueAt(text) {
54028
55146
  return now.toISOString();
54029
55147
  }
54030
55148
  function parseTomorrowOrTodayDueAt(text) {
54031
- const lower = normalizeText3(text);
55149
+ const lower = normalizeText5(text);
54032
55150
  const hasTomorrow = /\btomorrow\b/.test(lower);
54033
55151
  const hasToday = /\btoday\b/.test(lower);
54034
55152
  if (!hasTomorrow && !hasToday) return void 0;
@@ -54143,7 +55261,7 @@ function normalizeTransitPlace(value) {
54143
55261
  return cleaned || void 0;
54144
55262
  }
54145
55263
  function parseTransitDepartureAt2(text) {
54146
- const normalized = normalizeText3(text);
55264
+ const normalized = normalizeText5(text);
54147
55265
  const iso = normalized.match(/\b(\d{4}-\d{2}-\d{2})(?:[t\s](\d{2}:\d{2}))?\b/i);
54148
55266
  if (iso?.[1]) {
54149
55267
  const time = iso[2] ?? "09:00";
@@ -54219,7 +55337,7 @@ var buildReflection = (intent) => {
54219
55337
  // packages/nlu/src/rules.ts
54220
55338
  function interpretRules(request) {
54221
55339
  const rawText = request.text ?? "";
54222
- const normalized = normalizeText3(rawText);
55340
+ const normalized = normalizeText5(rawText);
54223
55341
  if (!normalized) return null;
54224
55342
  if (isGreetingIntent(rawText)) {
54225
55343
  return {
@@ -56696,11 +57814,11 @@ function createGuardianRpcMethods(deps) {
56696
57814
  * Get audit log
56697
57815
  */
56698
57816
  "guardian.audit.list": async (params) => {
56699
- const entries = await guardian.getAuditLog({
57817
+ const entries2 = await guardian.getAuditLog({
56700
57818
  userId: params.userId,
56701
57819
  limit: params.limit
56702
57820
  });
56703
- return { entries };
57821
+ return { entries: entries2 };
56704
57822
  },
56705
57823
  /**
56706
57824
  * List authorized devices
@@ -57329,24 +58447,24 @@ function parseBoolean(value) {
57329
58447
  }
57330
58448
  function loadGatewayEnv(providerEntries) {
57331
58449
  const env = process.env;
57332
- const entries = providerEntries ?? {};
58450
+ const entries2 = providerEntries ?? {};
57333
58451
  return {
57334
58452
  tts: {
57335
- minimax: env.VAAYU_MINIMAX_TTS_API_KEY || entries.minimax?.apiKey ? {
57336
- apiKey: env.VAAYU_MINIMAX_TTS_API_KEY ?? entries.minimax?.apiKey,
57337
- baseUrl: env.VAAYU_MINIMAX_TTS_BASE_URL ?? entries.minimax?.baseUrl,
58453
+ minimax: env.VAAYU_MINIMAX_TTS_API_KEY || entries2.minimax?.apiKey ? {
58454
+ apiKey: env.VAAYU_MINIMAX_TTS_API_KEY ?? entries2.minimax?.apiKey,
58455
+ baseUrl: env.VAAYU_MINIMAX_TTS_BASE_URL ?? entries2.minimax?.baseUrl,
57338
58456
  path: env.VAAYU_MINIMAX_TTS_PATH,
57339
58457
  model: env.VAAYU_MINIMAX_TTS_MODEL,
57340
58458
  voice: env.VAAYU_MINIMAX_TTS_VOICE,
57341
58459
  emotion: env.VAAYU_MINIMAX_TTS_EMOTION
57342
58460
  } : void 0,
57343
58461
  openai: (() => {
57344
- const apiKey = env.VAAYU_OPENAI_TTS_API_KEY ?? env.OPENAI_TTS_API_KEY ?? env.OPENAI_API_KEY ?? env.VAAYU_OPENAI_API_KEY ?? entries.openai?.apiKey;
58462
+ const apiKey = env.VAAYU_OPENAI_TTS_API_KEY ?? env.OPENAI_TTS_API_KEY ?? env.OPENAI_API_KEY ?? env.VAAYU_OPENAI_API_KEY ?? entries2.openai?.apiKey;
57345
58463
  if (!apiKey)
57346
58464
  return void 0;
57347
58465
  return {
57348
58466
  apiKey,
57349
- baseUrl: env.VAAYU_OPENAI_TTS_BASE_URL ?? entries.openai?.baseUrl,
58467
+ baseUrl: env.VAAYU_OPENAI_TTS_BASE_URL ?? entries2.openai?.baseUrl,
57350
58468
  path: env.VAAYU_OPENAI_TTS_PATH,
57351
58469
  model: env.VAAYU_OPENAI_TTS_MODEL,
57352
58470
  voice: env.VAAYU_OPENAI_TTS_VOICE,
@@ -57596,7 +58714,7 @@ import { spawn as spawn6 } from "node:child_process";
57596
58714
  var _chitraguptaMemory = null;
57597
58715
  async function getChitraguptaMemory() {
57598
58716
  if (!_chitraguptaMemory) {
57599
- _chitraguptaMemory = await import("./chunks/src-QAXOD5SB.js");
58717
+ _chitraguptaMemory = await import("./chunks/src-54LTTDTH.js");
57600
58718
  }
57601
58719
  return _chitraguptaMemory;
57602
58720
  }
@@ -58155,7 +59273,7 @@ function setupToolGuardian(params) {
58155
59273
  }
58156
59274
 
58157
59275
  // apps/gateway/dist/gateway/tool-execution.js
58158
- import { randomUUID as randomUUID47 } from "node:crypto";
59276
+ import { randomUUID as randomUUID48 } from "node:crypto";
58159
59277
  function createToolExecutionHelpers(params) {
58160
59278
  const { storage, config, sessionIdentityMap, toolRegistry, toolPolicy, guardTool, broadcastEvent } = params;
58161
59279
  async function runTool(payload) {
@@ -58197,7 +59315,7 @@ function createToolExecutionHelpers(params) {
58197
59315
  }
58198
59316
  });
58199
59317
  await storage.appendAudit({
58200
- id: randomUUID47(),
59318
+ id: randomUUID48(),
58201
59319
  time: (/* @__PURE__ */ new Date()).toISOString(),
58202
59320
  type: "tool.policy_ephemeral_allow",
58203
59321
  payload: {
@@ -58222,7 +59340,7 @@ function createToolExecutionHelpers(params) {
58222
59340
  userId: payload.context?.userId ?? payload.session?.userId,
58223
59341
  channel: payload.context?.channel ?? payload.session?.channel,
58224
59342
  locale,
58225
- traceId: payload.context?.traceId ?? randomUUID47()
59343
+ traceId: payload.context?.traceId ?? randomUUID48()
58226
59344
  };
58227
59345
  const guard = await guardTool({
58228
59346
  toolName: payload.name,
@@ -58240,7 +59358,7 @@ function createToolExecutionHelpers(params) {
58240
59358
  guardian: { decision: "blocked", reason }
58241
59359
  });
58242
59360
  await storage.appendAudit({
58243
- id: randomUUID47(),
59361
+ id: randomUUID48(),
58244
59362
  time: (/* @__PURE__ */ new Date()).toISOString(),
58245
59363
  type: "guardian.block",
58246
59364
  payload: {
@@ -58258,7 +59376,7 @@ function createToolExecutionHelpers(params) {
58258
59376
  }
58259
59377
  const result = await tool.handler(payload.input, ctx);
58260
59378
  await storage.appendAudit({
58261
- id: randomUUID47(),
59379
+ id: randomUUID48(),
58262
59380
  time: (/* @__PURE__ */ new Date()).toISOString(),
58263
59381
  type: "tool.invoke",
58264
59382
  payload: {
@@ -58290,7 +59408,7 @@ function createToolExecutionHelpers(params) {
58290
59408
  expiresAt: new Date(now.getTime() + 6e5).toISOString()
58291
59409
  });
58292
59410
  await storage.appendAudit({
58293
- id: randomUUID47(),
59411
+ id: randomUUID48(),
58294
59412
  time: now.toISOString(),
58295
59413
  type: "approval.request",
58296
59414
  payload: {
@@ -58345,7 +59463,7 @@ function createToolExecutionHelpers(params) {
58345
59463
  userId: request.userId,
58346
59464
  channel: request.channel,
58347
59465
  locale,
58348
- traceId: randomUUID47()
59466
+ traceId: randomUUID48()
58349
59467
  };
58350
59468
  const guard = await guardTool({
58351
59469
  toolName: request.toolName,
@@ -58363,7 +59481,7 @@ function createToolExecutionHelpers(params) {
58363
59481
  guardian: { decision: "blocked", reason }
58364
59482
  });
58365
59483
  await storage.appendAudit({
58366
- id: randomUUID47(),
59484
+ id: randomUUID48(),
58367
59485
  time: (/* @__PURE__ */ new Date()).toISOString(),
58368
59486
  type: "guardian.block",
58369
59487
  payload: {
@@ -58388,7 +59506,7 @@ function createToolExecutionHelpers(params) {
58388
59506
  policy: approvalPolicyContext
58389
59507
  });
58390
59508
  await storage.appendAudit({
58391
- id: randomUUID47(),
59509
+ id: randomUUID48(),
58392
59510
  time: (/* @__PURE__ */ new Date()).toISOString(),
58393
59511
  type: "tool.invoke",
58394
59512
  payload: {
@@ -58419,12 +59537,12 @@ function createToolExecutionHelpers(params) {
58419
59537
  }
58420
59538
 
58421
59539
  // apps/gateway/dist/gateway/tool-memory-commands.js
58422
- import { randomUUID as randomUUID48 } from "node:crypto";
59540
+ import { randomUUID as randomUUID49 } from "node:crypto";
58423
59541
  function createMemoryCommandHandlers(params) {
58424
59542
  const { toolRegistry, toolPolicy, guardTool, storage, updateProfile, broadcastEvent, smritiHelpers, pendingMemoryDeletes } = params;
58425
59543
  const pendingMemoryPurges = /* @__PURE__ */ new Map();
58426
59544
  function issuePurgeToken(sessionId, ids, options) {
58427
- const token = randomUUID48();
59545
+ const token = randomUUID49();
58428
59546
  const entry = {
58429
59547
  token,
58430
59548
  sessionId,
@@ -58528,7 +59646,7 @@ This token expires in 5 minutes.`
58528
59646
  if (!guard.allowed) {
58529
59647
  const reason = guard.response?.reason ?? "Guardian blocked this action.";
58530
59648
  await storage.appendAudit({
58531
- id: randomUUID48(),
59649
+ id: randomUUID49(),
58532
59650
  time: (/* @__PURE__ */ new Date()).toISOString(),
58533
59651
  type: "guardian.block",
58534
59652
  payload: {
@@ -58548,7 +59666,7 @@ This token expires in 5 minutes.`
58548
59666
  channel: payload.channel
58549
59667
  });
58550
59668
  await storage.appendAudit({
58551
- id: randomUUID48(),
59669
+ id: randomUUID49(),
58552
59670
  time: (/* @__PURE__ */ new Date()).toISOString(),
58553
59671
  type: "tool.invoke",
58554
59672
  payload: {
@@ -58677,7 +59795,7 @@ This token expires in 5 minutes.`
58677
59795
  if (!guard2.allowed) {
58678
59796
  const reason = guard2.response?.reason ?? "Guardian blocked this action.";
58679
59797
  await storage.appendAudit({
58680
- id: randomUUID48(),
59798
+ id: randomUUID49(),
58681
59799
  time: (/* @__PURE__ */ new Date()).toISOString(),
58682
59800
  type: "guardian.block",
58683
59801
  payload: {
@@ -58693,7 +59811,7 @@ This token expires in 5 minutes.`
58693
59811
  }
58694
59812
  const result2 = await tool.handler({ limit: 1 }, { sessionId: payload.sessionId, userId: payload.userId, channel: payload.channel });
58695
59813
  await storage.appendAudit({
58696
- id: randomUUID48(),
59814
+ id: randomUUID49(),
58697
59815
  time: (/* @__PURE__ */ new Date()).toISOString(),
58698
59816
  type: "tool.invoke",
58699
59817
  payload: {
@@ -58744,7 +59862,7 @@ This token expires in 5 minutes.`
58744
59862
  if (!guard.allowed) {
58745
59863
  const reason = guard.response?.reason ?? "Guardian blocked this action.";
58746
59864
  await storage.appendAudit({
58747
- id: randomUUID48(),
59865
+ id: randomUUID49(),
58748
59866
  time: (/* @__PURE__ */ new Date()).toISOString(),
58749
59867
  type: "guardian.block",
58750
59868
  payload: {
@@ -58760,7 +59878,7 @@ This token expires in 5 minutes.`
58760
59878
  }
58761
59879
  const result = await searchTool.handler({ query: trimmed, limit: 3 }, { sessionId: payload.sessionId, userId: payload.userId, channel: payload.channel });
58762
59880
  await storage.appendAudit({
58763
- id: randomUUID48(),
59881
+ id: randomUUID49(),
58764
59882
  time: (/* @__PURE__ */ new Date()).toISOString(),
58765
59883
  type: "tool.invoke",
58766
59884
  payload: {
@@ -58907,7 +60025,7 @@ function createWeatherDefaultsUpdater(params) {
58907
60025
 
58908
60026
  // apps/gateway/dist/channel/hub-format.js
58909
60027
  var DEFAULT_MAX_CHARS2 = 4e3;
58910
- var normalizeText4 = (value) => value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u0000/g, "").trim();
60028
+ var normalizeText6 = (value) => value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u0000/g, "").trim();
58911
60029
  var truncateText = (value, maxChars) => {
58912
60030
  if (value.length <= maxChars)
58913
60031
  return value;
@@ -58930,7 +60048,7 @@ var buildHeader2 = (assistantName) => {
58930
60048
  return `**${name}**`;
58931
60049
  };
58932
60050
  function formatHubReply(content, options = {}) {
58933
- const cleaned = normalizeText4(content || "");
60051
+ const cleaned = normalizeText6(content || "");
58934
60052
  const tone = options.tone ?? "default";
58935
60053
  const includeHeader = options.includeHeader ?? false;
58936
60054
  const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS2;
@@ -61016,10 +62134,14 @@ function startSelfHealMonitor(params) {
61016
62134
  const { runtime, config, logger, smritiConfigured, monitorIntervalMs, repairCooldownMs, runOnStart } = params;
61017
62135
  const intervalMs = Math.max(1e4, monitorIntervalMs ?? config.broker.heartbeatMs ?? 15e3);
61018
62136
  const cooldownMs = Math.max(3e4, repairCooldownMs ?? intervalMs * 2);
62137
+ const routerWindowMs = Math.max(3e4, intervalMs * 4);
62138
+ const routerFallbackStormThreshold = 5;
62139
+ const routerCooldownChurnThreshold = 4;
61019
62140
  const initialSmritiRetrieveEnabled = runtime.smriti?.retrieveEnabled !== false;
61020
62141
  let tickRunning = false;
61021
62142
  let kaalaDegraded = false;
61022
62143
  let smritiDegraded = false;
62144
+ let routerDegraded = false;
61023
62145
  let lastKaalaRepairAt = 0;
61024
62146
  let lastSmritiRepairAt = 0;
61025
62147
  const emitServiceState = (service, status, reason) => {
@@ -61066,6 +62188,41 @@ function startSelfHealMonitor(params) {
61066
62188
  logger.info("self_heal_restored", { service: "smriti" });
61067
62189
  emitServiceState("smriti", "restored");
61068
62190
  };
62191
+ const markRouterDegraded = (reason) => {
62192
+ if (routerDegraded)
62193
+ return;
62194
+ routerDegraded = true;
62195
+ logger.warn("self_heal_degraded", { service: "router", reason });
62196
+ emitServiceState("router", "degraded", reason);
62197
+ };
62198
+ const markRouterRestored = () => {
62199
+ if (!routerDegraded)
62200
+ return;
62201
+ routerDegraded = false;
62202
+ logger.info("self_heal_restored", { service: "router" });
62203
+ emitServiceState("router", "restored");
62204
+ };
62205
+ const evaluateRouterStorm = () => {
62206
+ const now = Date.now();
62207
+ const windowStart = now - routerWindowMs;
62208
+ const recent = listRoutingTraces({ limit: 80 }).filter((row) => {
62209
+ const timeMs = Date.parse(row.time);
62210
+ return Number.isFinite(timeMs) && timeMs >= windowStart;
62211
+ });
62212
+ let fallbackCount = 0;
62213
+ let cooldownCount = 0;
62214
+ for (const row of recent) {
62215
+ if (row.fallbackUsed) {
62216
+ fallbackCount += 1;
62217
+ }
62218
+ const hasCooldownOverride = row.providerResolution?.overrides?.some((override) => override.type === "provider_cooldown_fallback") ?? false;
62219
+ if (hasCooldownOverride || row.escalation?.reason === "all_candidates_cooling_down") {
62220
+ cooldownCount += 1;
62221
+ }
62222
+ }
62223
+ const unstable = fallbackCount >= routerFallbackStormThreshold || cooldownCount >= routerCooldownChurnThreshold;
62224
+ return { unstable, fallbackCount, cooldownCount, sampleCount: recent.length };
62225
+ };
61069
62226
  const maybeRepairKaala = async (reason) => {
61070
62227
  const now = Date.now();
61071
62228
  if (now - lastKaalaRepairAt < cooldownMs)
@@ -61128,6 +62285,14 @@ function startSelfHealMonitor(params) {
61128
62285
  }
61129
62286
  }
61130
62287
  }
62288
+ const routerStorm = evaluateRouterStorm();
62289
+ if (routerStorm.unstable) {
62290
+ const reason = `fallback_storm:${routerStorm.fallbackCount}/cooldown_churn:${routerStorm.cooldownCount}`;
62291
+ markRouterDegraded(reason);
62292
+ await maybeRepairKaala(reason);
62293
+ } else {
62294
+ markRouterRestored();
62295
+ }
61131
62296
  } catch (error) {
61132
62297
  logger.warn("self_heal_monitor_tick_failed", {
61133
62298
  error: error instanceof Error ? error.message : String(error)