@yugenlab/vaayu 0.1.11 → 0.1.12

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 (50) hide show
  1. package/chunks/{agentic-tool-loop-O3NUV7KG.js → agentic-tool-loop-NQESOBLC.js} +2 -2
  2. package/chunks/akasha-5C5Q6NMP.js +12 -0
  3. package/chunks/{chunk-7XV5ISV7.js → chunk-26K6DS6N.js} +2 -2
  4. package/chunks/chunk-5E3ZS5SW.js +529 -0
  5. package/chunks/{chunk-D46QTN3G.js → chunk-ARZCIITZ.js} +47 -18
  6. package/chunks/{chunk-ZYY6N3SP.js → chunk-FEDPZOZ5.js} +548 -389
  7. package/chunks/{chunk-3AYSJ7WB.js → chunk-GWYC7R2L.js} +13 -7
  8. package/chunks/chunk-H46F2Y6R.js +134 -0
  9. package/chunks/{chunk-QV4GPIPT.js → chunk-HXHDP2PZ.js} +8 -4
  10. package/chunks/chunk-KVQH4LE7.js +396 -0
  11. package/chunks/{chunk-Z576WVLG.js → chunk-LJCT7UYP.js} +17 -7
  12. package/chunks/{chunk-LJUEMPLG.js → chunk-M2RLX5LU.js} +32 -14
  13. package/chunks/{chunk-IGKYKEKT.js → chunk-NAQKA54E.js} +8 -2
  14. package/chunks/{chunk-F6RNEGFX.js → chunk-PZ4AQ22L.js} +78 -13
  15. package/chunks/{chunk-G2QREGXK.js → chunk-R273KC7J.js} +275 -2
  16. package/chunks/{chunk-A3HOZBC5.js → chunk-RVKTGKFD.js} +2 -2
  17. package/chunks/{chunk-VCUJES75.js → chunk-TSOQ2CT3.js} +763 -620
  18. package/chunks/{chunk-V2ZIKDN4.js → chunk-VEZ2DI2M.js} +16 -5
  19. package/chunks/{chunk-W4PVGBUH.js → chunk-XP3NIH5F.js} +7 -3
  20. package/chunks/{chunk-7AYYXHYZ.js → chunk-Y6IZH6FT.js} +19 -4
  21. package/chunks/{chunk-JZTFJE7M.js → chunk-YRTGGYJU.js} +14 -10
  22. package/chunks/{consolidation-indexer-VIWOP6VO.js → consolidation-indexer-KPXORCJ4.js} +9 -9
  23. package/chunks/database-BX3LVYXS.js +11 -0
  24. package/chunks/{day-consolidation-HMHSXIOM.js → day-consolidation-CR3TJFAL.js} +5 -5
  25. package/chunks/{dist-CY5NX2IK.js → dist-ESCM3CP5.js} +31 -21
  26. package/chunks/graphrag-73XA7LBX.js +14 -0
  27. package/chunks/hierarchical-temporal-search-GHKVKNZ6.js +8 -0
  28. package/chunks/hybrid-search-OD756RDV.js +20 -0
  29. package/chunks/{memory-store-LEERUQGL.js → memory-store-4GCBR2DZ.js} +4 -4
  30. package/chunks/periodic-consolidation-IINCHP6L.js +11 -0
  31. package/chunks/{postgres-7GZDDX77.js → postgres-YLCUNVPQ.js} +2 -2
  32. package/chunks/recall-64RROTUC.js +21 -0
  33. package/chunks/search-JVCDNTAJ.js +19 -0
  34. package/chunks/{session-store-O3TS7DUY.js → session-store-3EDQZEDS.js} +12 -6
  35. package/chunks/{sqlite-7BC4DJTN.js → sqlite-4N7YH2KK.js} +2 -2
  36. package/chunks/{src-6GVZTUH6.js → src-OPSDZEFI.js} +2 -2
  37. package/chunks/{suncalc-NOHGYHDU.js → suncalc-RM7URNUR.js} +2 -2
  38. package/chunks/{tree-RSHKDTCR.js → tree-FIUVGJ5J.js} +2 -2
  39. package/chunks/{vasana-engine-BJFHJVGM.js → vasana-engine-W4PYWT5H.js} +5 -5
  40. package/gateway.js +2358 -768
  41. package/package.json +1 -1
  42. package/pair-cli.js +2 -2
  43. package/chunks/chunk-2OBLQJYJ.js +0 -198
  44. package/chunks/chunk-67DXWEKG.js +0 -123
  45. package/chunks/graphrag-T2QWNX57.js +0 -14
  46. package/chunks/hierarchical-temporal-search-U6DG74IR.js +0 -8
  47. package/chunks/hybrid-search-BYTXCOXP.js +0 -20
  48. package/chunks/periodic-consolidation-D6SSKZ7H.js +0 -11
  49. package/chunks/recall-LNRQVATQ.js +0 -21
  50. package/chunks/search-BIODUW2P.js +0 -19
package/gateway.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import "./chunks/chunk-IEKAYVA3.js";
2
3
  import {
3
4
  SqliteStorage
4
5
  } from "./chunks/chunk-EG37M4QL.js";
@@ -7,28 +8,30 @@ import {
7
8
  buildAgentTree,
8
9
  buildKaalaHealthNode
9
10
  } from "./chunks/chunk-G5VYCA6O.js";
10
- import "./chunks/chunk-VCUJES75.js";
11
- import "./chunks/chunk-JZTFJE7M.js";
12
- import "./chunks/chunk-F6RNEGFX.js";
13
- import "./chunks/chunk-QV4GPIPT.js";
14
- import "./chunks/chunk-W4PVGBUH.js";
15
- import "./chunks/chunk-7XV5ISV7.js";
16
- import "./chunks/chunk-IEKAYVA3.js";
11
+ import "./chunks/chunk-TSOQ2CT3.js";
12
+ import "./chunks/chunk-LJCT7UYP.js";
13
+ import "./chunks/chunk-YRTGGYJU.js";
14
+ import "./chunks/chunk-PZ4AQ22L.js";
15
+ import "./chunks/chunk-HXHDP2PZ.js";
16
+ import "./chunks/chunk-XP3NIH5F.js";
17
+ import "./chunks/chunk-5E3ZS5SW.js";
18
+ import "./chunks/chunk-26K6DS6N.js";
17
19
  import {
18
20
  IMessageAdapter,
19
21
  TelegramAdapter,
22
+ TringAdapter,
20
23
  TwitterAdapter,
21
24
  WhatsAppAdapter,
22
25
  generateMantra,
23
26
  require_lib,
24
27
  validateMantra
25
- } from "./chunks/chunk-G2QREGXK.js";
28
+ } from "./chunks/chunk-R273KC7J.js";
26
29
  import {
27
30
  searchSessions
28
- } from "./chunks/chunk-3AYSJ7WB.js";
29
- import "./chunks/chunk-7AYYXHYZ.js";
30
- import "./chunks/chunk-LJUEMPLG.js";
31
- import "./chunks/chunk-D46QTN3G.js";
31
+ } from "./chunks/chunk-GWYC7R2L.js";
32
+ import "./chunks/chunk-Y6IZH6FT.js";
33
+ import "./chunks/chunk-M2RLX5LU.js";
34
+ import "./chunks/chunk-ARZCIITZ.js";
32
35
  import {
33
36
  addTurn,
34
37
  createSession,
@@ -38,18 +41,17 @@ import {
38
41
  listTurnsWithTimestamps,
39
42
  loadSession,
40
43
  updateSessionMeta
41
- } from "./chunks/chunk-ZYY6N3SP.js";
42
- import "./chunks/chunk-A3HOZBC5.js";
43
- import "./chunks/chunk-Z576WVLG.js";
44
- import "./chunks/chunk-V2ZIKDN4.js";
45
- import "./chunks/chunk-67DXWEKG.js";
46
- import "./chunks/chunk-2OBLQJYJ.js";
44
+ } from "./chunks/chunk-FEDPZOZ5.js";
45
+ import "./chunks/chunk-RVKTGKFD.js";
46
+ import "./chunks/chunk-VEZ2DI2M.js";
47
+ import "./chunks/chunk-H46F2Y6R.js";
48
+ import "./chunks/chunk-KVQH4LE7.js";
47
49
  import {
48
50
  __commonJS,
49
51
  __export,
50
52
  __require,
51
53
  __toESM
52
- } from "./chunks/chunk-IGKYKEKT.js";
54
+ } from "./chunks/chunk-NAQKA54E.js";
53
55
 
54
56
  // ../node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
55
57
  var require_identity = __commonJS({
@@ -13080,8 +13082,8 @@ function createSessionLock() {
13080
13082
  const run = async (sessionId, fn, options) => {
13081
13083
  const previous = sessionLocks.get(sessionId)?.tail ?? Promise.resolve();
13082
13084
  let release;
13083
- const gate = new Promise((resolve) => {
13084
- release = resolve;
13085
+ const gate = new Promise((resolve3) => {
13086
+ release = resolve3;
13085
13087
  });
13086
13088
  sessionLocks.set(sessionId, { tail: gate });
13087
13089
  const controller = new AbortController();
@@ -16353,7 +16355,7 @@ async function runCodex(config, prompt, signal, dynamicSystemPrompt) {
16353
16355
  child.stdin.write(prompt);
16354
16356
  child.stdin.end();
16355
16357
  }
16356
- const code = await new Promise((resolve, reject) => {
16358
+ const code = await new Promise((resolve3, reject) => {
16357
16359
  child.on("error", (err) => {
16358
16360
  clearTimeout(timer);
16359
16361
  if (err.code === "ENOENT") {
@@ -16366,7 +16368,7 @@ async function runCodex(config, prompt, signal, dynamicSystemPrompt) {
16366
16368
  reject(err);
16367
16369
  }
16368
16370
  });
16369
- child.on("close", (exitCode) => resolve(exitCode));
16371
+ child.on("close", (exitCode) => resolve3(exitCode));
16370
16372
  });
16371
16373
  clearTimeout(timer);
16372
16374
  let output = null;
@@ -16517,7 +16519,7 @@ async function runClaudeCode(config, prompt, signal, dynamicSystemPrompt) {
16517
16519
  }
16518
16520
  signal.addEventListener("abort", () => child.kill("SIGKILL"), { once: true });
16519
16521
  }
16520
- const code = await new Promise((resolve, reject) => {
16522
+ const code = await new Promise((resolve3, reject) => {
16521
16523
  child.on("error", (err) => {
16522
16524
  clearTimeout(timer);
16523
16525
  if (err.code === "ENOENT") {
@@ -16530,7 +16532,7 @@ async function runClaudeCode(config, prompt, signal, dynamicSystemPrompt) {
16530
16532
  reject(err);
16531
16533
  }
16532
16534
  });
16533
- child.on("close", (exitCode) => resolve(exitCode));
16535
+ child.on("close", (exitCode) => resolve3(exitCode));
16534
16536
  });
16535
16537
  clearTimeout(timer);
16536
16538
  try {
@@ -17606,7 +17608,7 @@ var resolveOllamaBinary = (entry) => {
17606
17608
  return "ollama";
17607
17609
  };
17608
17610
  var fetchOllamaModelsFromCli = async (binPath, host) => {
17609
- return new Promise((resolve) => {
17611
+ return new Promise((resolve3) => {
17610
17612
  execFile(
17611
17613
  binPath,
17612
17614
  ["list"],
@@ -17621,16 +17623,16 @@ var fetchOllamaModelsFromCli = async (binPath, host) => {
17621
17623
  if (error) {
17622
17624
  const message = error instanceof Error ? error.message : String(error);
17623
17625
  const details = stderr?.toString().trim();
17624
- resolve({ error: details ? `${message} (${details})` : message });
17626
+ resolve3({ error: details ? `${message} (${details})` : message });
17625
17627
  return;
17626
17628
  }
17627
17629
  const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
17628
17630
  if (lines.length <= 1) {
17629
- resolve({ models: [] });
17631
+ resolve3({ models: [] });
17630
17632
  return;
17631
17633
  }
17632
17634
  const models = lines.slice(1).map((line) => line.trim()).filter(Boolean).map((line) => line.split(/\s+/)[0]).filter((model) => Boolean(model));
17633
- resolve({ models });
17635
+ resolve3({ models });
17634
17636
  }
17635
17637
  );
17636
17638
  });
@@ -17672,8 +17674,8 @@ async function fetchModelList(providerId, entry, logger) {
17672
17674
  try {
17673
17675
  if (entry.type === "ollama") {
17674
17676
  const requestTimeoutMs2 = 8e3;
17675
- const sleep = (ms) => new Promise((resolve) => {
17676
- setTimeout(resolve, ms);
17677
+ const sleep = (ms) => new Promise((resolve3) => {
17678
+ setTimeout(resolve3, ms);
17677
17679
  });
17678
17680
  let lastError;
17679
17681
  let lastApiError;
@@ -19582,9 +19584,182 @@ function isToolAllowed(policy, toolName) {
19582
19584
  // packages/tools/src/mcp-manager.ts
19583
19585
  import fs7 from "node:fs";
19584
19586
  import path7 from "node:path";
19587
+ import { spawn as spawn4 } from "node:child_process";
19588
+
19589
+ // packages/tools/src/mcp-stdio-client.ts
19585
19590
  import { spawn as spawn3 } from "node:child_process";
19591
+ import { createInterface } from "node:readline";
19592
+ var McpStdioClient = class {
19593
+ process = null;
19594
+ reader = null;
19595
+ nextId = 1;
19596
+ pending = /* @__PURE__ */ new Map();
19597
+ timeoutMs;
19598
+ /* ── Reconnect state ─────────────────────────────────────────── */
19599
+ maxReconnectAttempts;
19600
+ reconnectAttempts = 0;
19601
+ lastCommand = "";
19602
+ lastArgs = [];
19603
+ lastEnv;
19604
+ constructor(options) {
19605
+ this.timeoutMs = options?.timeoutMs ?? 15e3;
19606
+ this.maxReconnectAttempts = options?.maxReconnectAttempts ?? 3;
19607
+ }
19608
+ /** Spawn the MCP server process and initialize JSON-RPC communication. */
19609
+ async connect(command, args = [], env) {
19610
+ this.lastCommand = command;
19611
+ this.lastArgs = args;
19612
+ this.lastEnv = env;
19613
+ this.reconnectAttempts = 0;
19614
+ const child = spawn3(command, args, {
19615
+ stdio: ["pipe", "pipe", "ignore"],
19616
+ env: { ...process.env, ...env ?? {} }
19617
+ });
19618
+ this.process = child;
19619
+ this.reader = createInterface({ input: child.stdout });
19620
+ this.reader.on("line", (line) => this.handleLine(line));
19621
+ child.on("exit", () => {
19622
+ for (const [id, pending] of this.pending) {
19623
+ clearTimeout(pending.timer);
19624
+ pending.reject(new Error("MCP server process exited"));
19625
+ this.pending.delete(id);
19626
+ }
19627
+ this.process = null;
19628
+ });
19629
+ child.on("error", (err) => {
19630
+ for (const [id, pending] of this.pending) {
19631
+ clearTimeout(pending.timer);
19632
+ pending.reject(err);
19633
+ this.pending.delete(id);
19634
+ }
19635
+ this.process = null;
19636
+ });
19637
+ await this.call("initialize", {
19638
+ protocolVersion: "2024-11-05",
19639
+ capabilities: {},
19640
+ clientInfo: { name: "vaayu-gateway", version: "0.1.0" }
19641
+ });
19642
+ this.writeLine(
19643
+ JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })
19644
+ );
19645
+ }
19646
+ /** Send a JSON-RPC request and wait for the response. Auto-reconnects on dead process. */
19647
+ async call(method, params) {
19648
+ if (!this.connected && this.lastCommand) {
19649
+ await this.reconnect();
19650
+ }
19651
+ if (!this.connected) {
19652
+ throw new Error("McpStdioClient is not connected");
19653
+ }
19654
+ const id = this.nextId++;
19655
+ const request = { jsonrpc: "2.0", id, method, params };
19656
+ this.writeLine(JSON.stringify(request));
19657
+ return new Promise((resolve3, reject) => {
19658
+ const timer = setTimeout(() => {
19659
+ this.pending.delete(id);
19660
+ reject(new Error(`MCP request timed out after ${this.timeoutMs}ms: ${method}`));
19661
+ }, this.timeoutMs);
19662
+ this.pending.set(id, { resolve: resolve3, reject, timer });
19663
+ });
19664
+ }
19665
+ /**
19666
+ * Re-connect to the MCP server after a crash.
19667
+ * Uses exponential backoff (100ms * 2^attempt). Throws after max attempts.
19668
+ */
19669
+ async reconnect() {
19670
+ if (this.connected) return;
19671
+ if (!this.lastCommand) {
19672
+ throw new Error("Cannot reconnect: no previous connection params");
19673
+ }
19674
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
19675
+ throw new Error(
19676
+ `McpStdioClient reconnect failed after ${this.maxReconnectAttempts} attempts`
19677
+ );
19678
+ }
19679
+ this.disconnect();
19680
+ const backoffMs = 100 * Math.pow(2, this.reconnectAttempts);
19681
+ this.reconnectAttempts++;
19682
+ await new Promise((r) => setTimeout(r, backoffMs));
19683
+ await this.connect(this.lastCommand, this.lastArgs, this.lastEnv);
19684
+ }
19685
+ /**
19686
+ * Check if the MCP server process is alive and responsive.
19687
+ * Sends a lightweight tools/list ping. Returns false on any failure.
19688
+ */
19689
+ async isHealthy() {
19690
+ if (!this.connected) return false;
19691
+ try {
19692
+ await this.listTools();
19693
+ return true;
19694
+ } catch {
19695
+ return false;
19696
+ }
19697
+ }
19698
+ /** List available tools from the MCP server. */
19699
+ async listTools() {
19700
+ const result = await this.call("tools/list");
19701
+ return result.tools;
19702
+ }
19703
+ /** Call a tool by name with arguments. */
19704
+ async callTool(name, args) {
19705
+ return await this.call("tools/call", {
19706
+ name,
19707
+ arguments: args
19708
+ });
19709
+ }
19710
+ /** Kill the child process and clean up. */
19711
+ disconnect() {
19712
+ for (const [id, pending] of this.pending) {
19713
+ clearTimeout(pending.timer);
19714
+ pending.reject(new Error("McpStdioClient disconnected"));
19715
+ this.pending.delete(id);
19716
+ }
19717
+ if (this.process && !this.process.killed) {
19718
+ this.process.kill("SIGTERM");
19719
+ }
19720
+ this.process = null;
19721
+ if (this.reader) {
19722
+ this.reader.close();
19723
+ this.reader = null;
19724
+ }
19725
+ }
19726
+ /** Whether the client is currently connected. */
19727
+ get connected() {
19728
+ return this.process !== null && !this.process.killed;
19729
+ }
19730
+ /** Handle an incoming NDJSON line from the MCP server stdout. */
19731
+ handleLine(line) {
19732
+ const trimmed = line.trim();
19733
+ if (!trimmed) return;
19734
+ let parsed;
19735
+ try {
19736
+ parsed = JSON.parse(trimmed);
19737
+ } catch {
19738
+ return;
19739
+ }
19740
+ if (typeof parsed.id !== "number") return;
19741
+ const pending = this.pending.get(parsed.id);
19742
+ if (!pending) return;
19743
+ this.pending.delete(parsed.id);
19744
+ clearTimeout(pending.timer);
19745
+ if (parsed.error) {
19746
+ pending.reject(
19747
+ new Error(`MCP error ${parsed.error.code}: ${parsed.error.message}`)
19748
+ );
19749
+ } else {
19750
+ pending.resolve(parsed.result);
19751
+ }
19752
+ }
19753
+ /** Write a single NDJSON line to the child process stdin. */
19754
+ writeLine(data2) {
19755
+ this.process?.stdin?.write(data2 + "\n");
19756
+ }
19757
+ };
19758
+
19759
+ // packages/tools/src/mcp-manager.ts
19586
19760
  var McpServerManager = class {
19587
19761
  servers = /* @__PURE__ */ new Map();
19762
+ stdioClients = /* @__PURE__ */ new Map();
19588
19763
  logger;
19589
19764
  constructor(configs = [], options = {}) {
19590
19765
  this.logger = options.logger;
@@ -19628,19 +19803,42 @@ var McpServerManager = class {
19628
19803
  this.startServer(state);
19629
19804
  }
19630
19805
  }
19806
+ /** Check health of all stdio clients. Returns map of serverId to healthy status. */
19807
+ async healthCheck() {
19808
+ const results = /* @__PURE__ */ new Map();
19809
+ for (const [id, client] of this.stdioClients) {
19810
+ try {
19811
+ if (!client.connected) {
19812
+ results.set(id, false);
19813
+ continue;
19814
+ }
19815
+ await client.listTools();
19816
+ results.set(id, true);
19817
+ } catch {
19818
+ results.set(id, false);
19819
+ this.stdioClients.delete(id);
19820
+ }
19821
+ }
19822
+ return results;
19823
+ }
19824
+ /** Stop all managed MCP server processes and disconnect stdio clients. */
19631
19825
  stopAll() {
19632
19826
  for (const state of this.servers.values()) {
19633
19827
  if (state.process && !state.process.killed) {
19634
19828
  state.process.kill("SIGTERM");
19635
19829
  }
19636
19830
  }
19831
+ for (const [id, client] of this.stdioClients) {
19832
+ client.disconnect();
19833
+ this.stdioClients.delete(id);
19834
+ }
19637
19835
  }
19638
19836
  startServer(state) {
19639
19837
  if (state.process && !state.process.killed) {
19640
19838
  return;
19641
19839
  }
19642
19840
  const { command, args, env, cwd } = state.config;
19643
- const child = spawn3(command, args ?? [], {
19841
+ const child = spawn4(command, args ?? [], {
19644
19842
  cwd: cwd ?? process.cwd(),
19645
19843
  env: {
19646
19844
  ...process.env,
@@ -19718,6 +19916,7 @@ var McpServerManager = class {
19718
19916
  {
19719
19917
  method: "POST",
19720
19918
  headers: {
19919
+ accept: "application/json",
19721
19920
  "content-type": "application/json",
19722
19921
  ...authToken ? { authorization: `Bearer ${authToken}` } : {}
19723
19922
  },
@@ -19729,6 +19928,16 @@ var McpServerManager = class {
19729
19928
  signal: ctx.signal
19730
19929
  }
19731
19930
  );
19931
+ const contentType = response.headers.get("content-type") ?? "";
19932
+ if (!contentType.toLowerCase().includes("application/json")) {
19933
+ return {
19934
+ ok: false,
19935
+ error: {
19936
+ code: "mcp_protocol_error",
19937
+ message: `MCP HTTP response content-type mismatch: expected application/json, got ${contentType || "unknown"}`
19938
+ }
19939
+ };
19940
+ }
19732
19941
  const data2 = await response.json().catch(() => null);
19733
19942
  if (!response.ok || !data2) {
19734
19943
  return {
@@ -19753,13 +19962,45 @@ var McpServerManager = class {
19753
19962
  };
19754
19963
  }
19755
19964
  }
19756
- return {
19757
- ok: false,
19758
- error: {
19759
- code: "mcp_unavailable",
19760
- message: "MCP invocation not wired yet"
19965
+ return this.invokeViaStdio(state, toolName, input);
19966
+ }
19967
+ /** Invoke an MCP tool via stdio JSON-RPC. Lazily connects on first call. */
19968
+ async invokeViaStdio(state, toolName, input) {
19969
+ const serverId = state.config.id;
19970
+ try {
19971
+ let client = this.stdioClients.get(serverId);
19972
+ if (!client || !client.connected) {
19973
+ client = new McpStdioClient({ timeoutMs: 3e4 });
19974
+ await client.connect(
19975
+ state.config.command,
19976
+ state.config.args ?? [],
19977
+ state.config.env
19978
+ );
19979
+ this.stdioClients.set(serverId, client);
19980
+ this.logger?.info("mcp_stdio_connected", { serverId });
19761
19981
  }
19762
- };
19982
+ const result = await client.callTool(
19983
+ toolName,
19984
+ input ?? {}
19985
+ );
19986
+ const textContent = result.content?.filter((c) => c.type === "text").map((c) => c.text).join("\n");
19987
+ if (result.isError) {
19988
+ return {
19989
+ ok: false,
19990
+ error: { code: "mcp_tool_error", message: textContent || "Tool execution failed" }
19991
+ };
19992
+ }
19993
+ return { ok: true, output: textContent || result };
19994
+ } catch (error) {
19995
+ this.stdioClients.delete(serverId);
19996
+ return {
19997
+ ok: false,
19998
+ error: {
19999
+ code: "mcp_stdio_error",
20000
+ message: error instanceof Error ? error.message : "MCP stdio invocation failed"
20001
+ }
20002
+ };
20003
+ }
19763
20004
  }
19764
20005
  };
19765
20006
 
@@ -19993,7 +20234,7 @@ async function loadSunCalc() {
19993
20234
  sunCalcCache = void 0;
19994
20235
  }
19995
20236
  try {
19996
- const mod = await import("./chunks/suncalc-NOHGYHDU.js");
20237
+ const mod = await import("./chunks/suncalc-RM7URNUR.js");
19997
20238
  const candidates = [
19998
20239
  mod.default,
19999
20240
  mod["module.exports"],
@@ -23917,7 +24158,7 @@ function buildSkillTools(dataDir) {
23917
24158
  };
23918
24159
  }
23919
24160
  const driver = payload.driver ?? process.env.VAAYU_SANDBOX_DRIVER ?? "docker";
23920
- const { createSandboxRunner: createSandboxRunner2 } = await import("./chunks/src-6GVZTUH6.js");
24161
+ const { createSandboxRunner: createSandboxRunner2 } = await import("./chunks/src-OPSDZEFI.js");
23921
24162
  const runner = createSandboxRunner2(driver);
23922
24163
  const result = await runner.run({
23923
24164
  command: "bash",
@@ -24591,6 +24832,330 @@ function registerBuiltinTools(registry, options = {}) {
24591
24832
  }
24592
24833
  }
24593
24834
 
24835
+ // packages/tools/src/skill-resolver.ts
24836
+ import { execSync } from "node:child_process";
24837
+ import { existsSync as existsSync2, readdirSync, readFileSync } from "node:fs";
24838
+ import { join, resolve } from "node:path";
24839
+ import { homedir } from "node:os";
24840
+ var EXEC_OPTS = {
24841
+ encoding: "utf8",
24842
+ timeout: 1e4,
24843
+ stdio: ["ignore", "pipe", "ignore"]
24844
+ };
24845
+ var HIGH_RISK_PATTERNS2 = ["sudo ", "rm -rf", "eval(", "exec(", "child_process"];
24846
+ var MEDIUM_RISK_PATTERNS2 = ["chmod 777", "npm install"];
24847
+ var SkillResolver = class {
24848
+ localRoot;
24849
+ installDir;
24850
+ logger;
24851
+ constructor(options) {
24852
+ this.localRoot = options?.localSkillsRoot ?? "ecosystem/skills";
24853
+ this.installDir = options?.installDir ?? join(homedir(), ".chitragupta", "skills");
24854
+ this.logger = options?.logger;
24855
+ }
24856
+ /**
24857
+ * Search for skills matching an intent across all sources.
24858
+ * Sources are checked in order: local registry, npm, GitHub.
24859
+ */
24860
+ async search(intent) {
24861
+ const keywords = intent.toLowerCase().split(/\s+/).filter(Boolean);
24862
+ if (keywords.length === 0) return [];
24863
+ const results = [];
24864
+ results.push(...this.searchLocal(keywords));
24865
+ results.push(...this.searchNpm(keywords));
24866
+ results.push(...this.searchGitHub(keywords));
24867
+ return results;
24868
+ }
24869
+ /**
24870
+ * Guard: validate skill safety before execution.
24871
+ * Checks for dangerous code patterns and manifest validity.
24872
+ */
24873
+ guard(candidate) {
24874
+ const warnings = [];
24875
+ const blockedReasons = [];
24876
+ if (candidate.manifestValid === false) {
24877
+ blockedReasons.push("invalid_manifest: missing name or tools array");
24878
+ }
24879
+ if (candidate.source === "local") {
24880
+ const skillPath = join(this.localRoot, candidate.name);
24881
+ const patterns = this.scanFilePatterns(skillPath);
24882
+ blockedReasons.push(...patterns.blocked);
24883
+ warnings.push(...patterns.warnings);
24884
+ }
24885
+ return { safe: blockedReasons.length === 0, warnings, blockedReasons };
24886
+ }
24887
+ /**
24888
+ * Install a skill candidate and return the installed skill info.
24889
+ * Returns null on any failure.
24890
+ */
24891
+ async install(candidate) {
24892
+ try {
24893
+ const targetDir = join(this.installDir, candidate.name);
24894
+ if (candidate.source === "npm") {
24895
+ execSync(`npm install --prefix ${this.installDir} ${candidate.name}`, EXEC_OPTS);
24896
+ } else if (candidate.source === "github" && candidate.url) {
24897
+ execSync(`gh repo clone ${candidate.url} ${targetDir}`, EXEC_OPTS);
24898
+ } else if (candidate.source === "local") {
24899
+ return this.readInstalledSkill(join(this.localRoot, candidate.name), candidate);
24900
+ } else {
24901
+ this.logger?.warn("skill_install_unsupported_source", { source: candidate.source });
24902
+ return null;
24903
+ }
24904
+ return this.readInstalledSkill(targetDir, candidate);
24905
+ } catch (err) {
24906
+ this.logger?.warn("skill_install_failed", {
24907
+ name: candidate.name,
24908
+ error: err instanceof Error ? err.message : String(err)
24909
+ });
24910
+ return null;
24911
+ }
24912
+ }
24913
+ /* ── Private: search sources ──────────────────────────────────── */
24914
+ /** Scan local skills root for directories with SKILL.md or manifest.json. */
24915
+ searchLocal(keywords) {
24916
+ try {
24917
+ if (!existsSync2(this.localRoot)) return [];
24918
+ const entries2 = readdirSync(this.localRoot, { withFileTypes: true });
24919
+ const candidates = [];
24920
+ for (const entry of entries2) {
24921
+ if (!entry.isDirectory()) continue;
24922
+ const dirPath = join(this.localRoot, entry.name);
24923
+ const hasSkillMd = existsSync2(join(dirPath, "SKILL.md"));
24924
+ const hasManifest = existsSync2(join(dirPath, "manifest.json"));
24925
+ if (!hasSkillMd && !hasManifest) continue;
24926
+ const manifest = this.readManifest(dirPath);
24927
+ const searchText = `${entry.name} ${manifest?.description ?? ""}`.toLowerCase();
24928
+ const matches = keywords.some((kw) => searchText.includes(kw));
24929
+ if (!matches) continue;
24930
+ candidates.push({
24931
+ name: entry.name,
24932
+ source: "local",
24933
+ description: typeof manifest?.description === "string" ? manifest.description : "",
24934
+ version: typeof manifest?.version === "string" ? manifest.version : void 0,
24935
+ manifestValid: this.isManifestValid(manifest)
24936
+ });
24937
+ }
24938
+ return candidates;
24939
+ } catch (err) {
24940
+ this.logger?.warn("skill_search_local_failed", { error: err instanceof Error ? err.message : String(err) });
24941
+ return [];
24942
+ }
24943
+ }
24944
+ /** Search npm for chitragupta-skill packages matching keywords. */
24945
+ searchNpm(keywords) {
24946
+ try {
24947
+ const keyword = keywords[0] ?? "";
24948
+ const raw = execSync(`npm search chitragupta-skill-${keyword} --json 2>/dev/null`, EXEC_OPTS);
24949
+ const parsed = JSON.parse(typeof raw === "string" ? raw : "[]");
24950
+ if (!Array.isArray(parsed)) return [];
24951
+ return parsed.slice(0, 10).map((pkg) => ({
24952
+ name: pkg.name ?? "unknown",
24953
+ source: "npm",
24954
+ description: pkg.description ?? "",
24955
+ version: pkg.version
24956
+ }));
24957
+ } catch (err) {
24958
+ this.logger?.warn("skill_search_npm_failed", { error: err instanceof Error ? err.message : String(err) });
24959
+ return [];
24960
+ }
24961
+ }
24962
+ /** Search GitHub for repos with the chitragupta-skill topic. */
24963
+ searchGitHub(keywords) {
24964
+ try {
24965
+ const raw = execSync(
24966
+ `gh search repos --topic chitragupta-skill --json name,description,url --limit 5 2>/dev/null`,
24967
+ EXEC_OPTS
24968
+ );
24969
+ const parsed = JSON.parse(typeof raw === "string" ? raw : "[]");
24970
+ if (!Array.isArray(parsed)) return [];
24971
+ const kw = keywords.join(" ").toLowerCase();
24972
+ return parsed.filter((r) => `${r.name ?? ""} ${r.description ?? ""}`.toLowerCase().includes(kw)).map((r) => ({
24973
+ name: r.name ?? "unknown",
24974
+ source: "github",
24975
+ description: r.description ?? "",
24976
+ url: r.url
24977
+ }));
24978
+ } catch (err) {
24979
+ this.logger?.warn("skill_search_github_failed", { error: err instanceof Error ? err.message : String(err) });
24980
+ return [];
24981
+ }
24982
+ }
24983
+ /* ── Private: helpers ─────────────────────────────────────────── */
24984
+ /** Read and parse manifest.json from a skill directory. */
24985
+ readManifest(dirPath) {
24986
+ try {
24987
+ const content = readFileSync(join(dirPath, "manifest.json"), "utf8");
24988
+ return JSON.parse(content);
24989
+ } catch {
24990
+ return null;
24991
+ }
24992
+ }
24993
+ /** Check whether a manifest has the required fields (name + tools array). */
24994
+ isManifestValid(manifest) {
24995
+ if (!manifest) return false;
24996
+ return typeof manifest.name === "string" && Array.isArray(manifest.tools);
24997
+ }
24998
+ /** Scan files in a directory for dangerous patterns. */
24999
+ scanFilePatterns(dirPath) {
25000
+ const blocked = [];
25001
+ const warnings = [];
25002
+ try {
25003
+ if (!existsSync2(dirPath)) return { blocked, warnings };
25004
+ const files = readdirSync(dirPath).filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
25005
+ for (const file of files) {
25006
+ const content = readFileSync(join(dirPath, file), "utf8");
25007
+ for (const pattern of HIGH_RISK_PATTERNS2) {
25008
+ if (content.includes(pattern)) blocked.push(`${file}: contains '${pattern}'`);
25009
+ }
25010
+ for (const pattern of MEDIUM_RISK_PATTERNS2) {
25011
+ if (content.includes(pattern)) warnings.push(`${file}: contains '${pattern}'`);
25012
+ }
25013
+ }
25014
+ } catch (err) {
25015
+ this.logger?.warn("skill_scan_patterns_failed", { dirPath, error: err instanceof Error ? err.message : String(err) });
25016
+ }
25017
+ return { blocked, warnings };
25018
+ }
25019
+ /** Read tool definitions from an installed skill's manifest. */
25020
+ readInstalledSkill(skillPath, candidate) {
25021
+ const manifest = this.readManifest(skillPath);
25022
+ if (!manifest || !Array.isArray(manifest.tools)) return null;
25023
+ const toolDefs = manifest.tools.map((t2) => ({
25024
+ name: typeof t2.name === "string" ? t2.name : "unknown",
25025
+ description: typeof t2.description === "string" ? t2.description : ""
25026
+ }));
25027
+ return { name: candidate.name, path: resolve(skillPath), source: candidate.source, toolDefinitions: toolDefs };
25028
+ }
25029
+ };
25030
+
25031
+ // packages/tools/src/skill-learner.ts
25032
+ var SkillLearner = class _SkillLearner {
25033
+ /** In-memory feedback store (flushes to persistent storage when wired). */
25034
+ feedback = [];
25035
+ gaps = /* @__PURE__ */ new Map();
25036
+ successCounts = /* @__PURE__ */ new Map();
25037
+ failureCounts = /* @__PURE__ */ new Map();
25038
+ gapThreshold;
25039
+ promotionThreshold;
25040
+ demotionThreshold;
25041
+ logger;
25042
+ constructor(options) {
25043
+ this.gapThreshold = options?.gapThreshold ?? 3;
25044
+ this.promotionThreshold = options?.promotionThreshold ?? 5;
25045
+ this.demotionThreshold = options?.demotionThreshold ?? 3;
25046
+ this.logger = options?.logger;
25047
+ }
25048
+ /** Record successful skill execution. */
25049
+ recordSuccess(skillName, intent, sessionId, feedback) {
25050
+ this.feedback.push({
25051
+ skillName,
25052
+ intent,
25053
+ outcome: "success",
25054
+ feedback,
25055
+ sessionId,
25056
+ timestamp: Date.now()
25057
+ });
25058
+ this.successCounts.set(skillName, (this.successCounts.get(skillName) ?? 0) + 1);
25059
+ this.logger?.info("skill_success_recorded", { skillName, total: this.successCounts.get(skillName) });
25060
+ }
25061
+ /** Record skill failure or user rejection. */
25062
+ recordFailure(skillName, intent, sessionId, reason) {
25063
+ this.feedback.push({
25064
+ skillName,
25065
+ intent,
25066
+ outcome: "failure",
25067
+ feedback: reason,
25068
+ sessionId,
25069
+ timestamp: Date.now()
25070
+ });
25071
+ this.failureCounts.set(skillName, (this.failureCounts.get(skillName) ?? 0) + 1);
25072
+ this.logger?.info("skill_failure_recorded", { skillName, reason, total: this.failureCounts.get(skillName) });
25073
+ }
25074
+ /** Record skill gap (no skill matched user intent). */
25075
+ recordGap(intent, sessionId) {
25076
+ const existing = this.gaps.get(intent);
25077
+ const now = (/* @__PURE__ */ new Date()).toISOString();
25078
+ if (existing) {
25079
+ existing.occurrences += 1;
25080
+ existing.lastSeen = now;
25081
+ if (!existing.sessionIds.includes(sessionId)) existing.sessionIds.push(sessionId);
25082
+ } else {
25083
+ this.gaps.set(intent, { intent, occurrences: 1, firstSeen: now, lastSeen: now, sessionIds: [sessionId] });
25084
+ }
25085
+ this.feedback.push({
25086
+ skillName: "",
25087
+ intent,
25088
+ outcome: "gap",
25089
+ sessionId,
25090
+ timestamp: Date.now()
25091
+ });
25092
+ this.logger?.info("skill_gap_recorded", { intent, occurrences: this.gaps.get(intent)?.occurrences });
25093
+ }
25094
+ /** Get skill gaps that have exceeded the occurrence threshold. */
25095
+ getActionableGaps() {
25096
+ const actionable = [];
25097
+ for (const gap of this.gaps.values()) {
25098
+ if (gap.occurrences >= this.gapThreshold) actionable.push(gap);
25099
+ }
25100
+ return actionable;
25101
+ }
25102
+ /** Evaluate whether a skill should be promoted (lab -> production). */
25103
+ evaluatePromotion(skillName) {
25104
+ return (this.successCounts.get(skillName) ?? 0) >= this.promotionThreshold;
25105
+ }
25106
+ /** Evaluate whether a skill should be demoted/disabled. */
25107
+ evaluateDemotion(skillName) {
25108
+ return (this.failureCounts.get(skillName) ?? 0) >= this.demotionThreshold;
25109
+ }
25110
+ /** Get raw feedback records for analysis. */
25111
+ getFeedback() {
25112
+ return this.feedback;
25113
+ }
25114
+ /** Reset counters (for testing or periodic cleanup). */
25115
+ reset() {
25116
+ this.feedback.length = 0;
25117
+ this.gaps.clear();
25118
+ this.successCounts.clear();
25119
+ this.failureCounts.clear();
25120
+ }
25121
+ /** Wire 4: Serialize learner state for persistence. */
25122
+ serialize() {
25123
+ return {
25124
+ feedback: [...this.feedback],
25125
+ gaps: Object.fromEntries(this.gaps),
25126
+ successCounts: Object.fromEntries(this.successCounts),
25127
+ failureCounts: Object.fromEntries(this.failureCounts)
25128
+ };
25129
+ }
25130
+ /** Wire 4: Restore from persisted snapshot. */
25131
+ static restore(snapshot, options) {
25132
+ const learner = new _SkillLearner(options);
25133
+ for (const fb of snapshot.feedback) learner.feedback.push(fb);
25134
+ for (const [intent, gap] of Object.entries(snapshot.gaps)) learner.gaps.set(intent, gap);
25135
+ for (const [k, v] of Object.entries(snapshot.successCounts)) learner.successCounts.set(k, v);
25136
+ for (const [k, v] of Object.entries(snapshot.failureCounts)) learner.failureCounts.set(k, v);
25137
+ return learner;
25138
+ }
25139
+ /** Wire 4: Flush state to a JSON file for cross-restart persistence. */
25140
+ async flush(filePath) {
25141
+ const { writeFile, mkdir } = await import("node:fs/promises");
25142
+ const { dirname: dirname4 } = await import("node:path");
25143
+ await mkdir(dirname4(filePath), { recursive: true });
25144
+ await writeFile(filePath, JSON.stringify(this.serialize(), null, 2), "utf-8");
25145
+ }
25146
+ /** Wire 4: Load state from a JSON file (best-effort). */
25147
+ static async load(filePath, options) {
25148
+ try {
25149
+ const { readFile } = await import("node:fs/promises");
25150
+ const raw = await readFile(filePath, "utf-8");
25151
+ const snapshot = JSON.parse(raw);
25152
+ return _SkillLearner.restore(snapshot, options);
25153
+ } catch {
25154
+ return null;
25155
+ }
25156
+ }
25157
+ };
25158
+
24594
25159
  // apps/gateway/dist/agent/commands/skill-lifecycle.js
24595
25160
  async function handleSkillLifecycleCommand(params) {
24596
25161
  const { action, fromPath, deleteSource, ackRisk, notes, locale, session, message, storage, toolRegistry, toolPolicy, routingDecision, routingResult } = params;
@@ -26152,6 +26717,45 @@ function buildGreetingContinuationReply(params) {
26152
26717
  }
26153
26718
  return topic ? `${userPrefix}I'm still with you. We were on "${topic}". Want to continue from there?` : `${userPrefix}I'm still with you. Want to continue where we left off?`;
26154
26719
  }
26720
+ function buildRecentGreetingReply(params) {
26721
+ const { userName, locale } = params;
26722
+ const style = normalizeStyle(params.style);
26723
+ const userPrefix = buildUserPrefix2(userName);
26724
+ if (locale === "te") {
26725
+ return `${userPrefix}\u0C07\u0C2A\u0C4D\u0C2A\u0C41\u0C21\u0C47 \u0C2E\u0C28\u0C02 hi \u0C1A\u0C46\u0C2A\u0C4D\u0C2A\u0C41\u0C15\u0C41\u0C28\u0C4D\u0C28\u0C3E\u0C02. \u0C28\u0C47\u0C28\u0C41 \u0C07\u0C15\u0C4D\u0C15\u0C21\u0C47 \u0C09\u0C28\u0C4D\u0C28\u0C3E\u0C28\u0C41 - \u0C07\u0C15 \u0C0F\u0C2E\u0C3F \u0C1A\u0C47\u0C26\u0C4D\u0C26\u0C3E\u0C02?`;
26726
+ }
26727
+ if (locale === "es") {
26728
+ return `${userPrefix}Hace un momento ya nos saludamos. Sigo aqui contigo. Que hacemos ahora?`;
26729
+ }
26730
+ if (locale === "de") {
26731
+ return `${userPrefix}Wir haben uns gerade eben schon begrusst. Ich bin da. Was machen wir als nachstes?`;
26732
+ }
26733
+ if (locale === "pl") {
26734
+ return `${userPrefix}Przed chwila juz sie przywitalismy. Jestem tutaj. Co robimy dalej?`;
26735
+ }
26736
+ if (locale === "hi") {
26737
+ return `${userPrefix}\u0905\u092D\u0940 \u0915\u0941\u091B \u092E\u093F\u0928\u091F \u092A\u0939\u0932\u0947 \u0939\u0940 \u0939\u092E\u0928\u0947 hi \u0915\u0939\u093E \u0925\u093E\u0964 \u092E\u0948\u0902 \u092F\u0939\u0940\u0902 \u0939\u0942\u0901, \u0905\u092C \u0915\u094D\u092F\u093E \u0915\u0930\u0947\u0902?`;
26738
+ }
26739
+ if (locale === "ja") {
26740
+ return `${userPrefix}\u3055\u3063\u304D\u3082\u3046\u6328\u62F6\u3057\u305F\u3088\u3002\u79C1\u306F\u3053\u3053\u306B\u3044\u308B\u3002\u6B21\u306F\u4F55\u3059\u308B?`;
26741
+ }
26742
+ if (style === "professional") {
26743
+ return `${userPrefix}We just greeted a moment ago. I'm here. How may I help now?`;
26744
+ }
26745
+ if (style === "direct") {
26746
+ return `${userPrefix}We just said hi. I'm here. What's next?`;
26747
+ }
26748
+ if (style === "chatty") {
26749
+ return `${userPrefix}Yep, we just said hi a moment ago. I'm right here with you. What are we doing next?`;
26750
+ }
26751
+ if (style === "sarcastic") {
26752
+ return `${userPrefix}Yep, hi already happened a minute ago. Still here. What's the next move?`;
26753
+ }
26754
+ if (style === "flirty") {
26755
+ return `${userPrefix}We just said hi a moment ago. I'm still with you. What shall we do next?`;
26756
+ }
26757
+ return `${userPrefix}We just said hi a moment ago. I'm here with you. What should we do next?`;
26758
+ }
26155
26759
  function buildAffectionReply(params) {
26156
26760
  const { userName, locale } = params;
26157
26761
  const style = normalizeStyle(params.style);
@@ -26531,20 +27135,24 @@ function buildConversationStyleSystem(params) {
26531
27135
  // apps/gateway/dist/agent/quick-response-smalltalk.js
26532
27136
  import { randomUUID as randomUUID7 } from "node:crypto";
26533
27137
  var RESUME_TOPIC_CUE_RE = /\b(continue|resume|pick\s+up|pick-up|where\s+we\s+left|left\s+off|carry\s+on|again|back\s+to|from\s+before|as\s+we\s+were|malli|phir)\b/i;
26534
- var SMALLTALK_MODEL_PREFIX = "smalltalk.";
26535
- function isSmalltalkResponseModel(model) {
26536
- if (typeof model !== "string")
26537
- return false;
26538
- return model.startsWith(SMALLTALK_MODEL_PREFIX);
26539
- }
27138
+ var SAME_TURN_ECHO_MS = 2e4;
26540
27139
  function wantsTopicContinuationCue(text) {
26541
27140
  const trimmed = text.trim();
26542
27141
  if (!trimmed)
26543
27142
  return false;
26544
27143
  return RESUME_TOPIC_CUE_RE.test(trimmed);
26545
27144
  }
27145
+ function shouldSkipCurrentTurnEcho(params) {
27146
+ const { content, trimmed, createdAt, now } = params;
27147
+ if (content !== trimmed)
27148
+ return false;
27149
+ const createdAtMs = createdAt ? Date.parse(createdAt) : Number.NaN;
27150
+ if (!Number.isFinite(createdAtMs))
27151
+ return false;
27152
+ return now - createdAtMs <= SAME_TURN_ECHO_MS;
27153
+ }
26546
27154
  async function resolveRecentTopicHint(params) {
26547
- const { isEstablishedSession, storage, session, trimmed, assistantName, isPureGreeting: isPureGreeting2, isPureAck: isPureAck2 } = params;
27155
+ const { isEstablishedSession, storage, session, trimmed, assistantName, isPureGreeting: isPureGreeting2, isPureAck: isPureAck2, maxTopicAgeMs } = params;
26548
27156
  if (!isEstablishedSession)
26549
27157
  return void 0;
26550
27158
  try {
@@ -26559,6 +27167,7 @@ async function resolveRecentTopicHint(params) {
26559
27167
  if (event.role === "assistant" && pendingUserEvent) {
26560
27168
  candidates.push({
26561
27169
  content: pendingUserEvent.content.trim(),
27170
+ createdAt: pendingUserEvent.createdAt,
26562
27171
  assistantModel: typeof event.metadata?.model === "string" ? event.metadata.model : void 0
26563
27172
  });
26564
27173
  pendingUserEvent = void 0;
@@ -26567,22 +27176,30 @@ async function resolveRecentTopicHint(params) {
26567
27176
  if (pendingUserEvent) {
26568
27177
  candidates.push({
26569
27178
  content: pendingUserEvent.content.trim(),
27179
+ createdAt: pendingUserEvent.createdAt,
26570
27180
  assistantModel: void 0
26571
27181
  });
26572
27182
  }
26573
- let skippedCurrentTurn = false;
27183
+ const now = Date.now();
26574
27184
  for (const candidate of [...candidates].reverse()) {
26575
27185
  const content = candidate.content;
26576
27186
  if (!content)
26577
27187
  continue;
26578
- if (!skippedCurrentTurn && content === trimmed) {
26579
- skippedCurrentTurn = true;
27188
+ if (shouldSkipCurrentTurnEcho({
27189
+ content,
27190
+ trimmed,
27191
+ createdAt: candidate.createdAt,
27192
+ now
27193
+ })) {
26580
27194
  continue;
26581
27195
  }
26582
- if (isPureGreeting2(content, assistantName) || isPureAck2(content, assistantName)) {
26583
- continue;
27196
+ if (typeof maxTopicAgeMs === "number" && Number.isFinite(maxTopicAgeMs) && maxTopicAgeMs > 0) {
27197
+ const candidateTime = candidate.createdAt ? Date.parse(candidate.createdAt) : Number.NaN;
27198
+ if (Number.isFinite(candidateTime) && now - candidateTime > maxTopicAgeMs) {
27199
+ continue;
27200
+ }
26584
27201
  }
26585
- if (candidate.assistantModel && !isSmalltalkResponseModel(candidate.assistantModel)) {
27202
+ if (isPureGreeting2(content, assistantName) || isPureAck2(content, assistantName)) {
26586
27203
  continue;
26587
27204
  }
26588
27205
  const topic = content.replace(/\s+/g, " ").replace(/[!?.,;:]+$/g, "");
@@ -26594,6 +27211,43 @@ async function resolveRecentTopicHint(params) {
26594
27211
  }
26595
27212
  return void 0;
26596
27213
  }
27214
+ function resolveContinuityWindowMs(windowMinutes) {
27215
+ const minutes = Number.isFinite(windowMinutes) ? Math.max(1, Math.min(180, Number(windowMinutes))) : 8;
27216
+ return minutes * 6e4;
27217
+ }
27218
+ async function resolveGreetingContinuityDecision(params) {
27219
+ const { continuityEnabled, continuityWindowMinutes, isEstablishedSession, storage, session, trimmed, assistantName, isPureGreeting: isPureGreeting2, isPureAck: isPureAck2 } = params;
27220
+ const hasResumeCue = wantsTopicContinuationCue(trimmed);
27221
+ if (!isEstablishedSession)
27222
+ return { continuation: hasResumeCue, repeatedGreeting: false };
27223
+ if (!continuityEnabled && !hasResumeCue)
27224
+ return { continuation: false, repeatedGreeting: false };
27225
+ const windowMs = resolveContinuityWindowMs(continuityWindowMinutes);
27226
+ const updatedAtMs = session.updatedAt ? Date.parse(session.updatedAt) : Number.NaN;
27227
+ const now = Date.now();
27228
+ const recentSessionActivity = Number.isFinite(updatedAtMs) && now - updatedAtMs <= windowMs;
27229
+ if (!hasResumeCue) {
27230
+ if (!continuityEnabled || !recentSessionActivity) {
27231
+ return { continuation: false, repeatedGreeting: false };
27232
+ }
27233
+ const repeatedWindowMs = Math.min(windowMs, 2e4);
27234
+ if (Number.isFinite(updatedAtMs) && now - updatedAtMs <= repeatedWindowMs) {
27235
+ return { continuation: false, repeatedGreeting: true };
27236
+ }
27237
+ return { continuation: true, repeatedGreeting: false };
27238
+ }
27239
+ const topic = await resolveRecentTopicHint({
27240
+ isEstablishedSession,
27241
+ storage,
27242
+ session,
27243
+ trimmed,
27244
+ assistantName,
27245
+ isPureGreeting: isPureGreeting2,
27246
+ isPureAck: isPureAck2,
27247
+ maxTopicAgeMs: hasResumeCue ? void 0 : windowMs
27248
+ });
27249
+ return { continuation: true, repeatedGreeting: false, topic };
27250
+ }
26597
27251
  async function recordSmalltalkFastLane(params) {
26598
27252
  const { routingResult, storage, sessionId, locale, matched, mode, intent, confidence, threshold } = params;
26599
27253
  routingResult.smalltalkFastLane = {
@@ -26643,8 +27297,8 @@ async function maybeHandleSemanticSmalltalk(params) {
26643
27297
  preferEmbeddings: true
26644
27298
  }).catch(() => null);
26645
27299
  let timeoutHandle;
26646
- const timeoutPromise = new Promise((resolve) => {
26647
- timeoutHandle = setTimeout(() => resolve(SEMANTIC_TIMEOUT_SENTINEL), SEMANTIC_NLU_BUDGET_MS);
27300
+ const timeoutPromise = new Promise((resolve3) => {
27301
+ timeoutHandle = setTimeout(() => resolve3(SEMANTIC_TIMEOUT_SENTINEL), SEMANTIC_NLU_BUDGET_MS);
26648
27302
  });
26649
27303
  const interpretedOrTimeout = await Promise.race([
26650
27304
  interpretedPromise,
@@ -26778,7 +27432,6 @@ async function handleQuickResponses(params) {
26778
27432
  const mode = params.mode ?? "full";
26779
27433
  const locationAssignments = mode === "full" ? extractLocationAssignments2(message.text) : null;
26780
27434
  const isEstablishedSession = session.updatedAt !== session.createdAt;
26781
- const isMatureSession = isEstablishedSession && Date.now() - new Date(session.createdAt).getTime() > 3 * 6e4;
26782
27435
  const conversationStyle = resolveConversationStyle({
26783
27436
  sessionId: session.id,
26784
27437
  text: trimmed,
@@ -26791,6 +27444,12 @@ async function handleQuickResponses(params) {
26791
27444
  existing: profile.toneTags
26792
27445
  })
26793
27446
  };
27447
+ if (conversationStyle.source === "explicit" && conversationStyle.changed && conversationStyle.style !== "default") {
27448
+ const updated = updateProfile({ toneTags: profileForStyledReplies.toneTags ?? profile.toneTags });
27449
+ broadcastEvent("profile.updated", updated);
27450
+ void appendPreferenceMemory(`Tone: ${conversationStyle.style}`, ["preference", "style", "tone"]).catch(() => {
27451
+ });
27452
+ }
26794
27453
  const emitLocal = async (model, replyText) => {
26795
27454
  const reply = normalizeAgentReply(replyText);
26796
27455
  await appendAssistantEvent(storage, session.id, reply, {
@@ -26815,9 +27474,10 @@ async function handleQuickResponses(params) {
26815
27474
  const emitFastLaneHit = (mode2, intent) => emitSmalltalkFastLane({ matched: true, mode: mode2, intent, confidence: 1, threshold: 1 });
26816
27475
  const emitSmalltalk = async (kind) => {
26817
27476
  await emitFastLaneHit(kind === "greeting" ? "greeting_rule" : "ack_rule", kind === "greeting" ? "smalltalk.greeting" : "smalltalk.ack");
26818
- if (kind === "greeting" && (isMatureSession || wantsTopicContinuationCue(trimmed))) {
26819
- const hasResumeCue = wantsTopicContinuationCue(trimmed);
26820
- const topic = hasResumeCue ? await resolveRecentTopicHint({
27477
+ if (kind === "greeting") {
27478
+ const greetingContinuity = await resolveGreetingContinuityDecision({
27479
+ continuityEnabled: params.continuity?.enabled ?? true,
27480
+ continuityWindowMinutes: params.continuity?.windowMinutes,
26821
27481
  isEstablishedSession,
26822
27482
  storage,
26823
27483
  session,
@@ -26825,13 +27485,22 @@ async function handleQuickResponses(params) {
26825
27485
  assistantName: profile.assistantName,
26826
27486
  isPureGreeting: isPureGreeting2,
26827
27487
  isPureAck: isPureAck2
26828
- }) : void 0;
26829
- return emitLocal("smalltalk.greeting", buildGreetingContinuationReply({
26830
- userName: profile.userName?.trim(),
26831
- locale,
26832
- topic,
26833
- style: conversationStyle.style
26834
- }));
27488
+ });
27489
+ if (greetingContinuity.repeatedGreeting) {
27490
+ return emitLocal("smalltalk.greeting", buildRecentGreetingReply({
27491
+ userName: profile.userName?.trim(),
27492
+ locale,
27493
+ style: conversationStyle.style
27494
+ }));
27495
+ }
27496
+ if (greetingContinuity.continuation) {
27497
+ return emitLocal("smalltalk.greeting", buildGreetingContinuationReply({
27498
+ userName: profile.userName?.trim(),
27499
+ locale,
27500
+ topic: greetingContinuity.topic,
27501
+ style: conversationStyle.style
27502
+ }));
27503
+ }
26835
27504
  }
26836
27505
  return emitLocal(kind === "greeting" ? "smalltalk.greeting" : "smalltalk.ack", buildSmalltalkReply2(profileForStyledReplies, kind, message.channel, locale));
26837
27506
  };
@@ -30226,21 +30895,30 @@ function isAbortLikeError(error) {
30226
30895
  if (!error)
30227
30896
  return false;
30228
30897
  const textFrom = (value) => typeof value === "string" ? value : String(value ?? "");
30898
+ const hasAbortMarker = (value) => /abort|aborted|aborterror|abortederror/i.test(value);
30229
30899
  if (error instanceof Error) {
30230
30900
  const name = textFrom(error.name);
30231
30901
  const message = textFrom(error.message);
30232
- if (/abort/i.test(name) || /abort|aborted/i.test(message))
30902
+ if (hasAbortMarker(name) || hasAbortMarker(message))
30903
+ return true;
30904
+ const cause = error.cause;
30905
+ if (cause && isAbortLikeError(cause))
30233
30906
  return true;
30234
30907
  return false;
30235
30908
  }
30236
30909
  if (typeof error === "string") {
30237
- return /abort|aborted/i.test(error);
30910
+ return hasAbortMarker(error);
30238
30911
  }
30239
30912
  if (typeof error === "object" && error !== null) {
30240
30913
  const entry = error;
30241
30914
  const name = textFrom(entry.name);
30242
30915
  const message = textFrom(entry.message);
30243
- return /abort/i.test(name) || /abort|aborted/i.test(message);
30916
+ const code = textFrom(entry.code);
30917
+ if (hasAbortMarker(name) || hasAbortMarker(message) || hasAbortMarker(code))
30918
+ return true;
30919
+ if (entry.cause && isAbortLikeError(entry.cause))
30920
+ return true;
30921
+ return false;
30244
30922
  }
30245
30923
  return false;
30246
30924
  }
@@ -30931,8 +31609,21 @@ async function handleToolPlanning(params) {
30931
31609
  await appendAssistantEvent(storage, session.id, locationPrompt, { providerId: "tool", model: promptModel }, message);
30932
31610
  return buildEarlyResult(session, promptModel, locationPrompt, routingDecision, routingResult);
30933
31611
  }
31612
+ let recommendedToolsForDecision = [];
31613
+ if (chitraguptaBridge) {
31614
+ try {
31615
+ recommendedToolsForDecision = chitraguptaBridge.recommend(normalized.norm);
31616
+ } catch {
31617
+ recommendedToolsForDecision = [];
31618
+ }
31619
+ }
30934
31620
  const tool = toolRegistry.get(toolName);
30935
31621
  if (tool) {
31622
+ const hasChosenRecommendation = recommendedToolsForDecision.includes(toolName);
31623
+ const preferredTool = recommendedToolsForDecision.find((name) => name && name !== toolName);
31624
+ if (!hasChosenRecommendation && preferredTool) {
31625
+ chitraguptaBridge?.onSkillRejected(preferredTool, toolName);
31626
+ }
30936
31627
  if (signal?.aborted) {
30937
31628
  return buildEarlyResult(session, "agent.aborted", t(locale, "system.request_cancelled"), routingDecision, routingResult);
30938
31629
  }
@@ -31879,12 +32570,24 @@ function resolveInitialProviderAndModel(params) {
31879
32570
  tier: terminalChain.tier
31880
32571
  });
31881
32572
  } else {
31882
- resolvedProviderId = "__template__";
31883
- capturedTemplateResponse = terminalChain.templateResponse;
31884
- logger.warn("provider_all_exhausted_template_fallback", {
31885
- sessionId,
31886
- templateResponse: terminalChain.templateResponse?.slice(0, 80)
31887
- });
32573
+ const finalFallbackProviders = ["google", "openai"];
32574
+ const configuredFallback = providers.list().find((provider) => finalFallbackProviders.includes(provider.id));
32575
+ if (configuredFallback) {
32576
+ resolvedProviderId = configuredFallback.id;
32577
+ resolvedModel = void 0;
32578
+ logger.warn("provider_resolved_via_final_template_fallback", {
32579
+ sessionId,
32580
+ fallbackProviderId: configuredFallback.id,
32581
+ tier: terminalChain.tier
32582
+ });
32583
+ } else {
32584
+ resolvedProviderId = "__template__";
32585
+ capturedTemplateResponse = terminalChain.templateResponse;
32586
+ logger.warn("provider_all_exhausted_template_fallback", {
32587
+ sessionId,
32588
+ templateResponse: terminalChain.templateResponse?.slice(0, 80)
32589
+ });
32590
+ }
31888
32591
  }
31889
32592
  }
31890
32593
  return {
@@ -36737,6 +37440,19 @@ function mapMargaComplexityToTier(complexity) {
36737
37440
  return "fast";
36738
37441
  return complexity === "complex" || complexity === "expert" ? "deep" : "fast";
36739
37442
  }
37443
+ async function resolveMargaDecisionLocalFirst(input) {
37444
+ if (!input.autoRouting)
37445
+ return null;
37446
+ const localClassifier = input.localClassifier ?? ((text, hasTools) => classifyMessage(text, hasTools));
37447
+ const localDecision = localClassifier(input.routingText, input.hasTools);
37448
+ if (localDecision)
37449
+ return localDecision;
37450
+ return await input.runtime.chitraguptaBridge?.decideRoute({
37451
+ message: input.routingText,
37452
+ hasTools: input.hasTools,
37453
+ bindingStrategy: "hybrid"
37454
+ }) ?? null;
37455
+ }
36740
37456
  var MANAS_TYPE_THRESHOLDS = {
36741
37457
  system_command: 0.5,
36742
37458
  conversation: 0.5,
@@ -36749,6 +37465,31 @@ var MANAS_TYPE_THRESHOLDS = {
36749
37465
  agentic_task: 0.65
36750
37466
  };
36751
37467
  var MANAS_SHORTCIRCUIT_FALLBACK = 0.55;
37468
+ var DEFAULT_POLICY_SIDECAR_CONFIDENCE = {
37469
+ minConfidenceForCautious: 0.7,
37470
+ minConfidenceForDeny: 0.82,
37471
+ minConfidenceForSafeFastLane: 0.55,
37472
+ downgradePolicy: "standard"
37473
+ };
37474
+ function applyPolicySidecarConfidenceOverride(policyClassifier, config) {
37475
+ if (policyClassifier.source !== "sidecar")
37476
+ return policyClassifier;
37477
+ if (policyClassifier.policy === "standard")
37478
+ return policyClassifier;
37479
+ const thresholds = { ...DEFAULT_POLICY_SIDECAR_CONFIDENCE, ...config ?? {} };
37480
+ const minConfidence = policyClassifier.policy === "deny" ? thresholds.minConfidenceForDeny : policyClassifier.policy === "cautious" ? thresholds.minConfidenceForCautious : thresholds.minConfidenceForSafeFastLane;
37481
+ if (policyClassifier.confidence >= minConfidence)
37482
+ return policyClassifier;
37483
+ const downgraded = thresholds.downgradePolicy;
37484
+ return {
37485
+ ...policyClassifier,
37486
+ policy: downgraded,
37487
+ reasons: [
37488
+ ...policyClassifier.reasons ?? [],
37489
+ `policy_confidence_override:${policyClassifier.policy}->${downgraded}:${policyClassifier.confidence.toFixed(2)}<${minConfidence.toFixed(2)}`
37490
+ ]
37491
+ };
37492
+ }
36752
37493
  async function resolveRoutingDecision(input) {
36753
37494
  const { routingText, locale, languageSource, autoRouting, sessionId, payload, prefs, profile, providers, config, runtime, deps, sessionRoute, hasPayloadOverride, sessionPinExpired, budgetPressure, latencyPreference, conversationStyleHint } = input;
36754
37495
  const activeSessionPin = !hasPayloadOverride && !sessionPinExpired && sessionRoute.pinProviderId ? {
@@ -36798,6 +37539,7 @@ async function resolveRoutingDecision(input) {
36798
37539
  };
36799
37540
  }
36800
37541
  }
37542
+ policyClassifier = applyPolicySidecarConfidenceOverride(policyClassifier, config.routing.policySidecarConfidence);
36801
37543
  const naturalCommand = detectNaturalLanguageCommand(routingText, locale);
36802
37544
  const commandText = naturalCommand ?? payload.message.text;
36803
37545
  const command = parseCommand(commandText);
@@ -36812,11 +37554,12 @@ async function resolveRoutingDecision(input) {
36812
37554
  looksLikeRecentRecap: deps.looksLikeRecentRecap
36813
37555
  });
36814
37556
  const hasTools = runtime.toolRegistry.list().length > 0;
36815
- const margaDecision = autoRouting ? await runtime.chitraguptaBridge?.decideRoute({
36816
- message: routingText,
37557
+ const margaDecision = await resolveMargaDecisionLocalFirst({
37558
+ autoRouting,
37559
+ routingText,
36817
37560
  hasTools,
36818
- bindingStrategy: "hybrid"
36819
- }) ?? classifyMessage(routingText, hasTools) : null;
37561
+ runtime
37562
+ });
36820
37563
  const margaRoutingDecision = autoRouting && margaDecision?.providerId && providers.get(margaDecision.providerId) ? {
36821
37564
  target: {
36822
37565
  providerId: margaDecision.providerId,
@@ -37357,8 +38100,8 @@ function buildObservationMask(toolName, lineCount, charCount, turnsAgo) {
37357
38100
  var DEFAULT_CHAT_ATTEMPT_TIMEOUT_MS = 15e3;
37358
38101
  var MIN_CHAT_ATTEMPT_TIMEOUT_MS = 1e3;
37359
38102
  var MAX_CHAT_ATTEMPT_TIMEOUT_MS = 12e4;
37360
- var GENERAL_LOCAL_TIMEOUT_CAP_MS = 8e3;
37361
- var SMALLTALK_LOCAL_TIMEOUT_CAP_MS = 3500;
38103
+ var GENERAL_LOCAL_TIMEOUT_CAP_MS = 6e3;
38104
+ var SMALLTALK_LOCAL_TIMEOUT_CAP_MS = 2500;
37362
38105
  var DEFAULT_CHAT_MAX_ATTEMPTS = 3;
37363
38106
  var MIN_CHAT_MAX_ATTEMPTS = 1;
37364
38107
  var MAX_CHAT_MAX_ATTEMPTS = 10;
@@ -37521,7 +38264,8 @@ async function runChatWithFallback(params) {
37521
38264
  listProviders: listProviders2,
37522
38265
  preferCliForConversation
37523
38266
  });
37524
- const maxAttempts = resolveChatMaxAttempts(config);
38267
+ const configuredMaxAttempts = resolveChatMaxAttempts(config);
38268
+ const maxAttempts = params.actionabilityKind === "smalltalk" || params.actionabilityKind === "general" ? Math.min(configuredMaxAttempts, 2) : configuredMaxAttempts;
37525
38269
  const attempts = builtAttempts;
37526
38270
  if (attempts.length > maxAttempts) {
37527
38271
  logger.info("provider_attempts_limited", {
@@ -38284,9 +39028,32 @@ function resolveSmalltalkProviderFallback(params) {
38284
39028
 
38285
39029
  // apps/gateway/dist/agent/loop-chat-result.js
38286
39030
  async function handleChatPhase(input) {
38287
- const { deps, session, payload, runSignal, runId, routingDecision, routingResult, actionability, margaDecision, resolvedProviderId, resolvedModel, semanticCacheCategory, semanticCacheRule, routingText, context, retrievalHits, retrievalDecision, normalizedOutboundMeta, combinedSystem, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, listProviderModels, profile, locale, toolPolicy, budgetFallbackTarget, maybeAppendSmritiMemorySafe, enqueueSmritiMemoryAppendSafe, allowMemory, memoryAllowed, memoryManager, stripModelThinking: stripModelThinking2, manasRouteHint } = input;
39031
+ const { deps, session, payload, runSignal, runId, routingDecision, routingResult, actionability, margaDecision, resolvedProviderId, resolvedModel, semanticCacheCategory, semanticCacheRule, routingText, context, retrievalHits, retrievalDecision, normalizedOutboundMeta, combinedSystem, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, listProviderModels, profile, locale, conversationStyleHint, turiyaSignal, toolPolicy, budgetFallbackTarget, maybeAppendSmritiMemorySafe, enqueueSmritiMemoryAppendSafe = maybeAppendSmritiMemorySafe, allowMemory, memoryAllowed, memoryManager, stripModelThinking: stripModelThinking2, manasRouteHint } = input;
38288
39032
  const { runtime, formatProviderError: formatProviderError2, lastAnthropicCallAt } = deps;
38289
39033
  const { config, logger, storage, providers, getProvider } = runtime;
39034
+ const profileForConversationalFallback = conversationStyleHint && conversationStyleHint !== "default" ? {
39035
+ ...profile,
39036
+ toneTags: styleToToneTags({
39037
+ style: conversationStyleHint,
39038
+ existing: profile.toneTags
39039
+ })
39040
+ } : profile;
39041
+ const emitTuriyaOutcome = (reward, context2) => {
39042
+ if (!runtime.chitraguptaBridge || !turiyaSignal?.tier)
39043
+ return;
39044
+ const boundedReward = Number(Math.max(-1, Math.min(1, reward)).toFixed(4));
39045
+ const outcomeContext = {
39046
+ confidence: Number(turiyaSignal.confidence.toFixed(4)),
39047
+ costEstimate: Number(turiyaSignal.costEstimate.toFixed(4)),
39048
+ ...context2 ?? {}
39049
+ };
39050
+ void runtime.chitraguptaBridge.recordTuriyaOutcome({
39051
+ tier: turiyaSignal.tier,
39052
+ reward: boundedReward,
39053
+ context: outcomeContext
39054
+ }).catch(() => {
39055
+ });
39056
+ };
38290
39057
  if (margaDecision?.skipLLM) {
38291
39058
  logger.info("marga_skip_llm", {
38292
39059
  sessionId: session.id,
@@ -38294,7 +39061,7 @@ async function handleChatPhase(input) {
38294
39061
  complexity: margaDecision.complexity
38295
39062
  });
38296
39063
  const skipKind = margaDecision.taskType === "heartbeat" ? "ack" : "greeting";
38297
- const skipContent = deps.buildSmalltalkReply(runtime.profile, skipKind, payload.message.channel, locale);
39064
+ const skipContent = deps.buildSmalltalkReply(profileForConversationalFallback, skipKind, payload.message.channel, locale);
38298
39065
  const skipModel = `marga:${margaDecision.taskType}`;
38299
39066
  await appendAssistantEvent(storage, session.id, skipContent, {
38300
39067
  providerId: "marga",
@@ -38312,6 +39079,7 @@ async function handleChatPhase(input) {
38312
39079
  });
38313
39080
  routingResult.selected = { providerId: "marga", model: skipModel };
38314
39081
  routingResult.fallbackUsed = false;
39082
+ emitTuriyaOutcome(0.95, { margaSkip: 1, providerFailure: 0, fallbackUsed: 0 });
38315
39083
  return buildEarlyResult(session, skipModel, skipContent, routingDecision, routingResult);
38316
39084
  }
38317
39085
  if (!margaDecision?.skipLLM && manasRouteHint?.action === "skip_llm") {
@@ -38377,6 +39145,7 @@ async function handleChatPhase(input) {
38377
39145
  }, payload.message);
38378
39146
  routingResult.selected = cached2.selected;
38379
39147
  routingResult.fallbackUsed = false;
39148
+ emitTuriyaOutcome(0.8, { semanticCacheHit: 1, providerFailure: 0, fallbackUsed: 0 });
38380
39149
  return buildEarlyResult(session, cacheModel, cached2.responseContent, routingDecision, routingResult);
38381
39150
  }
38382
39151
  const agenticTools = runtime.toolRegistry.list().filter((t2) => deps.isToolAllowed(toolPolicy, t2.name)).filter((t2) => !t2.name.startsWith("memory.")).map((t2) => ({
@@ -38443,7 +39212,8 @@ async function handleChatPhase(input) {
38443
39212
  routingResult.fallbackUsed = false;
38444
39213
  return buildEarlyResult(session, recoveryModel, recoveryContent, routingDecision, routingResult);
38445
39214
  }
38446
- const effectiveActionability = inferSmalltalkActionability(actionability, payload.message.text, profile, { isPureGreeting: deps.isPureGreeting, isPureAck: deps.isPureAck }, margaDecision);
39215
+ const inferredActionability = inferSmalltalkActionability(actionability, payload.message.text, profile, { isPureGreeting: deps.isPureGreeting, isPureAck: deps.isPureAck }, margaDecision);
39216
+ const effectiveActionability = inferredActionability.kind === "smalltalk" || margaDecision?.taskType !== "smalltalk" ? inferredActionability : { kind: "smalltalk", reason: "smalltalk_greeting" };
38447
39217
  if (escalation.reason !== "request_aborted" && effectiveActionability.kind !== "smalltalk") {
38448
39218
  const degradedToolResult = await handleToolPlanning({
38449
39219
  session,
@@ -38493,14 +39263,38 @@ async function handleChatPhase(input) {
38493
39263
  });
38494
39264
  if (degradedToolResult) {
38495
39265
  routingResult.escalation = escalation;
39266
+ emitTuriyaOutcome(0.3, { degradedToolRecovery: 1, providerFailure: 1, fallbackUsed: 1 });
38496
39267
  return degradedToolResult;
38497
39268
  }
39269
+ if (actionability.kind === "tool" || routingResult.actionability?.kind === "tool") {
39270
+ const recoveryContent = "I'm in recovery mode right now and can still run deterministic tools. Try a direct ask like weather, forecast, notes, reminders, or time.";
39271
+ const recoveryModel = "tool.recovery";
39272
+ routingResult.escalation = escalation;
39273
+ await appendAssistantEvent(storage, session.id, recoveryContent, {
39274
+ providerId: "tool",
39275
+ model: recoveryModel
39276
+ }, payload.message);
39277
+ await maybeAppendSmritiMemorySafe({
39278
+ userText: payload.message.text,
39279
+ assistantText: recoveryContent,
39280
+ sessionId: session.id,
39281
+ channel: payload.message.channel,
39282
+ chatId: payload.message.chatId,
39283
+ userId: payload.message.userId,
39284
+ providerId: "tool",
39285
+ model: recoveryModel
39286
+ });
39287
+ routingResult.selected = { providerId: "tool", model: recoveryModel };
39288
+ routingResult.fallbackUsed = false;
39289
+ emitTuriyaOutcome(0.15, { toolRecoveryPrompt: 1, providerFailure: 1, fallbackUsed: 1 });
39290
+ return buildEarlyResult(session, recoveryModel, recoveryContent, routingDecision, routingResult);
39291
+ }
38498
39292
  }
38499
39293
  const smalltalkFallback = resolveSmalltalkProviderFallback({
38500
39294
  actionability: effectiveActionability,
38501
39295
  escalation,
38502
39296
  buildSmalltalkReply: deps.buildSmalltalkReply,
38503
- profile,
39297
+ profile: profileForConversationalFallback,
38504
39298
  channel: payload.message.channel,
38505
39299
  locale
38506
39300
  });
@@ -38522,6 +39316,7 @@ async function handleChatPhase(input) {
38522
39316
  });
38523
39317
  routingResult.selected = { providerId: "local", model: smalltalkFallback.model };
38524
39318
  routingResult.fallbackUsed = false;
39319
+ emitTuriyaOutcome(0.25, { smalltalkFallback: 1, providerFailure: 1, fallbackUsed: 1 });
38525
39320
  return buildEarlyResult(session, smalltalkFallback.model, smalltalkFallback.content, routingDecision, routingResult);
38526
39321
  }
38527
39322
  const content = chatResult.friendlyMessage ? chatResult.friendlyMessage : formatProviderError2(chatResult.error ?? new Error("Provider failed"));
@@ -38543,12 +39338,13 @@ async function handleChatPhase(input) {
38543
39338
  });
38544
39339
  routingResult.selected = { providerId: "error", model: errorModel };
38545
39340
  routingResult.fallbackUsed = false;
39341
+ emitTuriyaOutcome(-1, { errorEscalation: 1, providerFailure: 1, fallbackUsed: 1 });
38546
39342
  return buildEarlyResult(session, errorModel, content, routingDecision, routingResult);
38547
39343
  }
38548
39344
  const { response, provider, model, fallbackUsed } = chatResult;
38549
39345
  let finalResponse = response;
38550
39346
  if (response.toolCalls?.length && agenticTools.length > 0) {
38551
- const { runAgenticToolLoop } = await import("./chunks/agentic-tool-loop-O3NUV7KG.js");
39347
+ const { runAgenticToolLoop } = await import("./chunks/agentic-tool-loop-NQESOBLC.js");
38552
39348
  const agenticResult = await runAgenticToolLoop({
38553
39349
  toolCalls: response.toolCalls,
38554
39350
  initialContent: response.content,
@@ -38616,6 +39412,11 @@ async function handleChatPhase(input) {
38616
39412
  }
38617
39413
  });
38618
39414
  }
39415
+ emitTuriyaOutcome(fallbackUsed ? 0.55 : 1, {
39416
+ providerFailure: 0,
39417
+ fallbackUsed: fallbackUsed ? 1 : 0,
39418
+ semanticCacheWrite: cacheStored ? 1 : 0
39419
+ });
38619
39420
  return finalizeChatRun({
38620
39421
  storage,
38621
39422
  session,
@@ -38664,11 +39465,11 @@ async function executeAgentRun(params) {
38664
39465
  const { deps, session, payload, runId, sessionRoute, runSignal } = params;
38665
39466
  const { runtime, stripModelThinking: stripModelThinking2, formatProviderError: formatProviderError2, buildProfileSystemPrompt: buildProfileSystemPrompt2, smritiAutoMemoryReady, smritiAutoMemorySessions, runSmritiAutoMemoryScript, smritiClient, smritiRetrieveEnabled, smritiRetrieveLimit, listProviderModels, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, inferProviderFromModel: inferProviderFromModel2, lastAnthropicCallAt, memoryManager } = deps;
38666
39467
  const { config, logger, storage, providers, soulPrompt, getProvider, memoryAllowed, maybeAppendSmritiMemory, enqueueSmritiMemoryAppend } = runtime;
38667
- const normalizationFromMessage = payload.message.normalization;
38668
- const normalizedIncoming = normalizeIncomingText(payload.message.text ?? "");
38669
- const hasMatchingNormalization = normalizationFromMessage?.audit?.raw === normalizedIncoming.raw && normalizationFromMessage?.audit?.norm === normalizedIncoming.norm;
38670
- const normalizedAuditContext = hasMatchingNormalization && normalizationFromMessage?.audit ? normalizationFromMessage.audit : buildNormalizationAuditContext(normalizedIncoming);
38671
- const normalizedOutboundMeta = hasMatchingNormalization && normalizationFromMessage?.outbound ? normalizationFromMessage.outbound : buildNormalizationOutboundMeta(normalizedIncoming);
39468
+ const normFrom = payload.message.normalization;
39469
+ const normInc = normalizeIncomingText(payload.message.text ?? "");
39470
+ const normMatch = normFrom?.audit?.raw === normInc.raw && normFrom?.audit?.norm === normInc.norm;
39471
+ const normalizedAuditContext = normMatch && normFrom?.audit ? normFrom.audit : buildNormalizationAuditContext(normInc);
39472
+ const normalizedOutboundMeta = normMatch && normFrom?.outbound ? normFrom.outbound : buildNormalizationOutboundMeta(normInc);
38672
39473
  const routingText = normalizedAuditContext.norm || payload.message.text;
38673
39474
  const retrievalText = normalizedAuditContext.search || normalizedAuditContext.norm || payload.message.text;
38674
39475
  const message = {
@@ -38768,16 +39569,8 @@ async function executeAgentRun(params) {
38768
39569
  const pinUntilMs = sessionRoute.pinUntil ? Date.parse(sessionRoute.pinUntil) : Number.NaN;
38769
39570
  const sessionPinExpired = Boolean(sessionRoute.pinProviderId) && Number.isFinite(pinUntilMs) && pinUntilMs <= Date.now();
38770
39571
  if (sessionPinExpired && storage.setSessionRoute) {
38771
- void storage.setSessionRoute(session.id, {
38772
- agentId: sessionRoute.agentId,
38773
- laneId: sessionRoute.laneId,
38774
- queueMode: sessionRoute.queueMode,
38775
- announceMode: sessionRoute.announceMode
38776
- }).catch((error) => {
38777
- logger.warn("session_route_pin_clear_failed", {
38778
- sessionId: session.id,
38779
- error: error instanceof Error ? error.message : String(error)
38780
- });
39572
+ void storage.setSessionRoute(session.id, { agentId: sessionRoute.agentId, laneId: sessionRoute.laneId, queueMode: sessionRoute.queueMode, announceMode: sessionRoute.announceMode }).catch((e) => {
39573
+ logger.warn("session_route_pin_clear_failed", { sessionId: session.id, error: e instanceof Error ? e.message : String(e) });
38781
39574
  });
38782
39575
  }
38783
39576
  const earlyBudgetState = await evaluateBudgetState({ storage, config, logger });
@@ -38816,7 +39609,7 @@ async function executeAgentRun(params) {
38816
39609
  latencyPreference,
38817
39610
  conversationStyleHint: earlyStyleHint !== "default" ? earlyStyleHint : void 0
38818
39611
  });
38819
- const { command, actionability, policyClassifier, margaDecision, routingDecision, complexitySignal, scoringResult, earlyRoutingResult, activeSessionPin, manasRouteHint, manasClassification, complexityProfile34 } = routing;
39612
+ const { command, actionability, policyClassifier, margaDecision, routingDecision, complexitySignal, scoringResult, earlyRoutingResult, activeSessionPin, turiyaSignal, manasRouteHint, manasClassification, complexityProfile34 } = routing;
38820
39613
  if (!command) {
38821
39614
  const allowMemoryEarly = memoryAllowed() && payload.memoryMode !== "disabled";
38822
39615
  const maybeAppendSmritiMemorySafeEarly = allowMemoryEarly ? maybeAppendSmritiMemory : async () => {
@@ -39090,9 +39883,10 @@ async function executeAgentRun(params) {
39090
39883
  });
39091
39884
  if (runSignal.aborted)
39092
39885
  throw new Error("Request aborted");
39886
+ let chitraguptaRecs = [];
39093
39887
  try {
39094
- const chitraguptaRecs = runtime.chitraguptaBridge?.recommend(payload.message.text);
39095
- if (chitraguptaRecs && chitraguptaRecs.length > 0) {
39888
+ chitraguptaRecs = runtime.chitraguptaBridge?.recommend(payload.message.text) ?? [];
39889
+ if (chitraguptaRecs.length > 0) {
39096
39890
  logger.debug("chitragupta_recommendations", {
39097
39891
  sessionId: session.id,
39098
39892
  query: payload.message.text.slice(0, 100),
@@ -39123,7 +39917,7 @@ async function executeAgentRun(params) {
39123
39917
  });
39124
39918
  if (preChatResult)
39125
39919
  return preChatResult;
39126
- return handleChatPhase({
39920
+ const chatPhaseResult = await handleChatPhase({
39127
39921
  margaDecision,
39128
39922
  deps,
39129
39923
  session,
@@ -39150,6 +39944,8 @@ async function executeAgentRun(params) {
39150
39944
  locale,
39151
39945
  toolPolicy,
39152
39946
  budgetFallbackTarget,
39947
+ conversationStyleHint: conversationStyle.style,
39948
+ turiyaSignal,
39153
39949
  maybeAppendSmritiMemorySafe,
39154
39950
  enqueueSmritiMemoryAppendSafe,
39155
39951
  allowMemory,
@@ -39158,6 +39954,22 @@ async function executeAgentRun(params) {
39158
39954
  stripModelThinking: stripModelThinking2,
39159
39955
  manasRouteHint
39160
39956
  });
39957
+ try {
39958
+ const bridge = runtime.chitraguptaBridge;
39959
+ if (bridge && chitraguptaRecs.length > 0) {
39960
+ const toolCallNames = (chatPhaseResult.response.toolCalls ?? []).map((toolCall) => toolCall?.name?.trim()).filter((name) => Boolean(name));
39961
+ if (toolCallNames.length > 0) {
39962
+ const chosenTool = toolCallNames[0] ?? "llm";
39963
+ for (const rec of chitraguptaRecs) {
39964
+ if (rec && !toolCallNames.includes(rec)) {
39965
+ bridge.onSkillRejected(rec, chosenTool);
39966
+ }
39967
+ }
39968
+ }
39969
+ }
39970
+ } catch {
39971
+ }
39972
+ return chatPhaseResult;
39161
39973
  }
39162
39974
 
39163
39975
  // apps/gateway/dist/routing-trace.js
@@ -39631,6 +40443,16 @@ function createAgentLoop(deps) {
39631
40443
  error: error instanceof Error ? error.message : String(error)
39632
40444
  });
39633
40445
  }
40446
+ try {
40447
+ const bridge = runtime.chitraguptaBridge;
40448
+ const noToolUsed = !result.response.toolCalls?.length;
40449
+ const isSubstantive = payload.message.text.trim().length > 10;
40450
+ const isNonTemplate = result.response.model !== "template:deterministic" && result.response.model !== "system.queued" && result.response.model !== "system.continuity";
40451
+ if (bridge && noToolUsed && isSubstantive && isNonTemplate) {
40452
+ bridge.recordSkillGap(payload.message.text, result.session.id);
40453
+ }
40454
+ } catch {
40455
+ }
39634
40456
  return result;
39635
40457
  }
39636
40458
  return runAgent;
@@ -44142,7 +44964,7 @@ async function forwardToBridge(runtime, connectionId, request) {
44142
44964
  if (!bridgeConnection) {
44143
44965
  throw new Error("Bridge connection not found or disconnected");
44144
44966
  }
44145
- return new Promise((resolve, reject) => {
44967
+ return new Promise((resolve3, reject) => {
44146
44968
  const requestId = `fwd-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
44147
44969
  const timer = setTimeout(() => {
44148
44970
  pendingBridgeRequests.delete(requestId);
@@ -44152,7 +44974,7 @@ async function forwardToBridge(runtime, connectionId, request) {
44152
44974
  resolve: (result) => {
44153
44975
  clearTimeout(timer);
44154
44976
  pendingBridgeRequests.delete(requestId);
44155
- resolve(result);
44977
+ resolve3(result);
44156
44978
  }
44157
44979
  });
44158
44980
  bridgeConnection.sendEvent("bridge.request", {
@@ -44211,10 +45033,10 @@ function createBridgeExecutor(bridgeRegistry, connections) {
44211
45033
  }
44212
45034
  const timeoutMs = ctx?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
44213
45035
  const requestId = `anriva-tool-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
44214
- return new Promise((resolve) => {
45036
+ return new Promise((resolve3) => {
44215
45037
  const timer = setTimeout(() => {
44216
45038
  pendingBridgeRequests.delete(requestId);
44217
- resolve({
45039
+ resolve3({
44218
45040
  ok: false,
44219
45041
  error: { code: "bridge_timeout", message: `Bridge request timed out after ${timeoutMs}ms` }
44220
45042
  });
@@ -44223,7 +45045,7 @@ function createBridgeExecutor(bridgeRegistry, connections) {
44223
45045
  ctx.signal.addEventListener("abort", () => {
44224
45046
  clearTimeout(timer);
44225
45047
  pendingBridgeRequests.delete(requestId);
44226
- resolve({
45048
+ resolve3({
44227
45049
  ok: false,
44228
45050
  error: { code: "aborted", message: "Request aborted" }
44229
45051
  });
@@ -44233,7 +45055,7 @@ function createBridgeExecutor(bridgeRegistry, connections) {
44233
45055
  resolve: (result) => {
44234
45056
  clearTimeout(timer);
44235
45057
  pendingBridgeRequests.delete(requestId);
44236
- resolve({
45058
+ resolve3({
44237
45059
  ok: result.ok,
44238
45060
  output: result.payload,
44239
45061
  error: result.error
@@ -45984,7 +46806,7 @@ function createKaalaBroker(deps, options) {
45984
46806
  dispatch(lane);
45985
46807
  }
45986
46808
  };
45987
- const spawn7 = (input) => {
46809
+ const spawn8 = (input) => {
45988
46810
  if (!options.enabled) {
45989
46811
  throw new Error("Kaala broker is disabled.");
45990
46812
  }
@@ -46036,13 +46858,13 @@ function createKaalaBroker(deps, options) {
46036
46858
  parentRunId: input.parentRunId,
46037
46859
  metadata: input.metadata
46038
46860
  };
46039
- let resolve;
46861
+ let resolve3;
46040
46862
  const promise = new Promise((res) => {
46041
- resolve = res;
46863
+ resolve3 = res;
46042
46864
  });
46043
46865
  const entry = {
46044
46866
  record,
46045
- resolve,
46867
+ resolve: resolve3,
46046
46868
  promise,
46047
46869
  cancelled: false,
46048
46870
  controller: new AbortController()
@@ -46090,8 +46912,8 @@ function createKaalaBroker(deps, options) {
46090
46912
  if (entry.record.endedAt) return entry.record;
46091
46913
  if (!timeoutMs) return entry.promise;
46092
46914
  let timeoutId;
46093
- const timeoutPromise = new Promise((resolve) => {
46094
- timeoutId = setTimeout(() => resolve(void 0), timeoutMs);
46915
+ const timeoutPromise = new Promise((resolve3) => {
46916
+ timeoutId = setTimeout(() => resolve3(void 0), timeoutMs);
46095
46917
  });
46096
46918
  const result = await Promise.race([entry.promise, timeoutPromise]);
46097
46919
  if (timeoutId) clearTimeout(timeoutId);
@@ -46163,7 +46985,7 @@ function createKaalaBroker(deps, options) {
46163
46985
  };
46164
46986
  };
46165
46987
  return {
46166
- spawn: spawn7,
46988
+ spawn: spawn8,
46167
46989
  list,
46168
46990
  get,
46169
46991
  wait: wait2,
@@ -46190,8 +47012,8 @@ function buildMetrics(entry, result) {
46190
47012
  }
46191
47013
  async function runWithTimeout(action, timeoutMs) {
46192
47014
  let timeoutId;
46193
- const timeoutPromise = new Promise((resolve) => {
46194
- timeoutId = setTimeout(() => resolve({ timedOut: true }), timeoutMs);
47015
+ const timeoutPromise = new Promise((resolve3) => {
47016
+ timeoutId = setTimeout(() => resolve3({ timedOut: true }), timeoutMs);
46195
47017
  });
46196
47018
  try {
46197
47019
  const result = await Promise.race([
@@ -46746,8 +47568,15 @@ var CHANNEL_IDS = [
46746
47568
  "imessage",
46747
47569
  "web"
46748
47570
  ];
46749
- var isChannelId = (value) => Boolean(value) && CHANNEL_IDS.includes(value);
46750
- var toChannelId = (value) => isChannelId(value) ? value : void 0;
47571
+ var CHANNEL_ALIASES = {
47572
+ whispr: "signal"
47573
+ };
47574
+ var isChannelId = (value) => Boolean(value) && (CHANNEL_IDS.includes(normalizeChannelId(value)) || CHANNEL_IDS.includes(value));
47575
+ var toChannelId = (value) => isChannelId(value) ? normalizeChannelId(value) : void 0;
47576
+ function normalizeChannelId(value) {
47577
+ const normalized = CHANNEL_ALIASES[value.trim().toLowerCase()];
47578
+ return normalized ?? value.toLowerCase();
47579
+ }
46751
47580
 
46752
47581
  // apps/gateway/dist/kaala/audit.js
46753
47582
  import { randomUUID as randomUUID25 } from "node:crypto";
@@ -50007,20 +50836,20 @@ function hasRegexSupport(lang) {
50007
50836
 
50008
50837
  // packages/nlu/src/prototypes.ts
50009
50838
  var import_yaml = __toESM(require_dist(), 1);
50010
- import { readFileSync } from "node:fs";
50011
- import { join, dirname } from "node:path";
50839
+ import { readFileSync as readFileSync2 } from "node:fs";
50840
+ import { join as join2, dirname } from "node:path";
50012
50841
  import { fileURLToPath } from "node:url";
50013
50842
  var __dirname = dirname(fileURLToPath(import.meta.url));
50014
- var YAML_PATH = join(__dirname, "intent-prototypes.yaml");
50843
+ var YAML_PATH = join2(__dirname, "intent-prototypes.yaml");
50015
50844
  var cached = null;
50016
50845
  function loadIntentPrototypes() {
50017
50846
  if (cached) return cached;
50018
50847
  let raw;
50019
50848
  try {
50020
- raw = readFileSync(YAML_PATH, "utf-8");
50849
+ raw = readFileSync2(YAML_PATH, "utf-8");
50021
50850
  } catch {
50022
- const fallbackPath = join(__dirname, "..", "src", "intent-prototypes.yaml");
50023
- raw = readFileSync(fallbackPath, "utf-8");
50851
+ const fallbackPath = join2(__dirname, "..", "src", "intent-prototypes.yaml");
50852
+ raw = readFileSync2(fallbackPath, "utf-8");
50024
50853
  }
50025
50854
  const parsed = (0, import_yaml.parse)(raw);
50026
50855
  cached = parsed;
@@ -50369,6 +51198,9 @@ var DEFAULT_HYBRID_THRESHOLD = 0.6;
50369
51198
  var DEFAULT_EMBEDDING_THRESHOLD = 0.62;
50370
51199
  var PREFER_EMBEDDING_TIMEOUT_MS = 350;
50371
51200
  var DEFAULT_SMALLTALK_CLOUD_TIMEOUT_MS = 900;
51201
+ var DEFAULT_OLLAMA_CHAT_MODEL = "qwen3:8b";
51202
+ var SHORT_CHECKIN_PACK_LOCALE_SEPARATOR = /[-_]/;
51203
+ var WHITESPACE_RE = /\s+/g;
50372
51204
  var SOCIAL_CHECKIN_RE = /\b(h(?:ow|w)\s*(?:are|r)\s*(?:you|u)|how(?:'s|\s+is)\s+it\s+going|what(?:'s|\s+is)\s+up|you\s+there|all\s+good|namaste|namaskar(?:am|a)?|namaskaram|vanakkam|bagunava|bagunnava|bagnava|ela\s+undh?i|em[i]?\s+chest(?:h|)un(?:n|)av(?:u|a)?|em[i]?\s+chest(?:h|)un(?:n|)ar(?:u|a)?|como\s+estas|como\s+esta|que\s+tal|wie\s+geht(?:\s+es|s)(?:\s+(?:dir|ihnen))?|alles\s+gut|comment\s+(?:ca|ça)\s+va|ca\s+va|ça\s+va|kaise\s+(?:ho|hai|hain)|aap\s+kaise\s+(?:ho|hain)|kya\s+haal(?:\s+hai)?|kya\s+chal\s+raha\s+hai|कैसे\s+(?:हो|हैं)|क्या\s+हाल(?:\s+है)?|क्या\s+चल\s+रहा\s+है|お元気|元気(?:ですか)?|調子どう|調子はどう|genki(?:\s+desu\s+ka)?|konnichiwa|ohayo(?:u)?|nasilsin|nasilsiniz|как\s+дела|كيف\s+حالك)\b/i;
50373
51205
  var SOCIAL_ACTION_GUARD_RE = /\b(weather|forecast|clima|tiempo|wetter|pogoda|meteo|rain|snow|temperature|temp|router|network|wifi|device|client|connected|lan|scan|time|clock|remind|note|notes|search|find|nearest|hospital|train|rail|journey|memory|session|provider|model|language|lang|tone|style|emoji|persona|set|delete|forget|clear|music|play|transcrib|calendar|event|todo|joke|funny|meme|story|poem|quote|news|latest|top|tell\s+me|who\s+(?:are|r)\s+(?:you|u)|what\s+are\s+you|what\s+can\s+(?:you|u)\s+do)\b/i;
50374
51206
  var SHORT_SOCIAL_TOKEN_RE = /[^\p{L}\p{N}']+/gu;
@@ -50460,7 +51292,36 @@ function normalizeBaseUrl4(value) {
50460
51292
  return void 0;
50461
51293
  return trimmed.replace(/\/$/, "");
50462
51294
  }
50463
- function looksLikeGenericSocialCheckin(text) {
51295
+ function normalizeCheckinPhrase(input) {
51296
+ return input.toLowerCase().replace(SHORT_SOCIAL_TOKEN_RE, " ").trim().replace(WHITESPACE_RE, " ");
51297
+ }
51298
+ function resolveLocalePhrasePack(packs, language) {
51299
+ if (!packs)
51300
+ return /* @__PURE__ */ new Set();
51301
+ const locale = (language ?? "").trim().toLowerCase();
51302
+ const baseLocale = locale.split(SHORT_CHECKIN_PACK_LOCALE_SEPARATOR)[0] ?? "";
51303
+ const candidates = [
51304
+ packs[locale],
51305
+ packs[baseLocale],
51306
+ packs.default,
51307
+ packs["*"]
51308
+ ].filter((value) => Array.isArray(value));
51309
+ const terms = /* @__PURE__ */ new Set();
51310
+ for (const pack of candidates) {
51311
+ for (const phrase of pack) {
51312
+ const normalized = normalizeCheckinPhrase(phrase);
51313
+ if (!normalized)
51314
+ continue;
51315
+ terms.add(normalized);
51316
+ for (const token of normalized.split(" ")) {
51317
+ if (token)
51318
+ terms.add(token);
51319
+ }
51320
+ }
51321
+ }
51322
+ return terms;
51323
+ }
51324
+ function looksLikeGenericSocialCheckin(text, options) {
50464
51325
  const trimmed = text.trim();
50465
51326
  if (!trimmed || trimmed.startsWith("/"))
50466
51327
  return false;
@@ -50481,12 +51342,16 @@ function looksLikeGenericSocialCheckin(text) {
50481
51342
  return false;
50482
51343
  if (SOCIAL_CHECKIN_RE.test(trimmed))
50483
51344
  return true;
51345
+ const localeTerms = resolveLocalePhrasePack(options?.phrasePacks, options?.language);
51346
+ const normalizedText = normalizeCheckinPhrase(trimmed);
51347
+ if (localeTerms.size > 0 && localeTerms.has(normalizedText))
51348
+ return true;
50484
51349
  if (words.length > 3)
50485
51350
  return false;
50486
51351
  const normalizedWords = words.map((word) => word.toLowerCase().replace(SHORT_SOCIAL_TOKEN_RE, "")).filter(Boolean);
50487
51352
  if (!normalizedWords.length || normalizedWords.length > 3)
50488
51353
  return false;
50489
- return normalizedWords.every((word) => SHORT_SOCIAL_TOKENS.has(word));
51354
+ return normalizedWords.every((word) => SHORT_SOCIAL_TOKENS.has(word) || localeTerms.has(word));
50490
51355
  }
50491
51356
  async function detectRequestLanguage(text, config, logger) {
50492
51357
  const provider = config.nlu?.langDetectProvider ?? "auto";
@@ -50507,7 +51372,7 @@ function resolveDefaultModel(providerId, entry) {
50507
51372
  return entry.models[0];
50508
51373
  if (entry.type === "ollama") {
50509
51374
  const required = entry.requiredModels?.find((model) => !/embed|bge/i.test(model));
50510
- return required ?? "llama3.2:1b";
51375
+ return required ?? DEFAULT_OLLAMA_CHAT_MODEL;
50511
51376
  }
50512
51377
  if (entry.type === "minimax")
50513
51378
  return "MiniMax-M2.5-Lightning";
@@ -51300,7 +52165,15 @@ function createHybridNluInterpreter(params) {
51300
52165
  if (langDetectEnabled) {
51301
52166
  detectedLang = await detectRequestLanguage(request.text, config, logger);
51302
52167
  }
51303
- if (request.preferEmbeddings && looksLikeGenericSocialCheckin(request.text)) {
52168
+ const fastEmbeddingCandidates = request.preferEmbeddings ? embeddingCandidates.map((candidate) => ({
52169
+ ...candidate,
52170
+ models: candidate.models.slice(0, 1),
52171
+ timeoutMs: Math.min(candidate.timeoutMs, PREFER_EMBEDDING_TIMEOUT_MS)
52172
+ })) : embeddingCandidates;
52173
+ if (request.preferEmbeddings && looksLikeGenericSocialCheckin(request.text, {
52174
+ language: detectedLang,
52175
+ phrasePacks: config.nlu?.shortCheckinPacks
52176
+ })) {
51304
52177
  if (smalltalkCloudFallbackEnabled && candidates.length > 0) {
51305
52178
  const smalltalkCandidates = prioritizeSmalltalkFallbackCandidates(candidates, {
51306
52179
  preferredProviderId: llmConfig?.smalltalkCloudProviderId,
@@ -51326,7 +52199,7 @@ function createHybridNluInterpreter(params) {
51326
52199
  return mergeNluResults(rulesResult, deterministicSmalltalk, hybridThreshold);
51327
52200
  }
51328
52201
  if (embeddingFirst) {
51329
- const embeddingResult2 = await classifyWithEmbeddings(request, embeddingCandidates, logger, embeddingThreshold, detectedLang);
52202
+ const embeddingResult2 = await classifyWithEmbeddings(request, fastEmbeddingCandidates, logger, embeddingThreshold, detectedLang);
51330
52203
  if (embeddingResult2) {
51331
52204
  if (hasRegexSupport(detectedLang) && rulesResult && rulesResult.intent !== "unknown") {
51332
52205
  if (rulesResult.intent === embeddingResult2.intent) {
@@ -51350,16 +52223,16 @@ function createHybridNluInterpreter(params) {
51350
52223
  return { ...rulesResult, language: detectedLang };
51351
52224
  }
51352
52225
  if (request.preferEmbeddings) {
51353
- const fastEmbeddingCandidates = embeddingCandidates.map((candidate) => ({
51354
- ...candidate,
51355
- models: candidate.models.slice(0, 1),
51356
- timeoutMs: Math.min(candidate.timeoutMs, PREFER_EMBEDDING_TIMEOUT_MS)
51357
- }));
51358
- const embeddingOnlyResult = await classifyWithEmbeddings(request, fastEmbeddingCandidates, logger, embeddingThreshold, detectedLang);
51359
- if (embeddingOnlyResult) {
51360
- return mergeNluResults(rulesResult, embeddingOnlyResult, hybridThreshold);
52226
+ if (!embeddingFirst) {
52227
+ const embeddingOnlyResult = await classifyWithEmbeddings(request, fastEmbeddingCandidates, logger, embeddingThreshold, detectedLang);
52228
+ if (embeddingOnlyResult) {
52229
+ return mergeNluResults(rulesResult, embeddingOnlyResult, hybridThreshold);
52230
+ }
51361
52231
  }
51362
- if (looksLikeGenericSocialCheckin(request.text)) {
52232
+ if (looksLikeGenericSocialCheckin(request.text, {
52233
+ language: detectedLang,
52234
+ phrasePacks: config.nlu?.shortCheckinPacks
52235
+ })) {
51363
52236
  return {
51364
52237
  intent: "smalltalk.greeting",
51365
52238
  confidence: 0.61,
@@ -51779,8 +52652,73 @@ function isLoopbackHost(host) {
51779
52652
  import fs39 from "node:fs";
51780
52653
  import os10 from "node:os";
51781
52654
  import path37 from "node:path";
52655
+ import { pathToFileURL } from "node:url";
51782
52656
 
51783
52657
  // packages/config/src/schema-nlu.ts
52658
+ var DEFAULT_SHORT_CHECKIN_PACKS = {
52659
+ en: [
52660
+ "hi",
52661
+ "hello",
52662
+ "hey",
52663
+ "how are you",
52664
+ "whats up",
52665
+ "just checking"
52666
+ ],
52667
+ es: [
52668
+ "hola",
52669
+ "como estas",
52670
+ "que tal",
52671
+ "todo bien"
52672
+ ],
52673
+ fr: [
52674
+ "bonjour",
52675
+ "salut",
52676
+ "ca va",
52677
+ "comment ca va"
52678
+ ],
52679
+ de: [
52680
+ "hallo",
52681
+ "wie gehts",
52682
+ "alles gut",
52683
+ "guten morgen"
52684
+ ],
52685
+ hi: [
52686
+ "namaste",
52687
+ "kaise ho",
52688
+ "aap kaise hain",
52689
+ "kya haal hai"
52690
+ ],
52691
+ te: [
52692
+ "namaskaram",
52693
+ "bagunnava",
52694
+ "em chesthunavu",
52695
+ "ela unnava"
52696
+ ],
52697
+ ja: [
52698
+ "konnichiwa",
52699
+ "genki desu ka",
52700
+ "ohayo",
52701
+ "konbanwa"
52702
+ ],
52703
+ ar: [
52704
+ "marhaba",
52705
+ "ahlan",
52706
+ "kayfa haluk",
52707
+ "kayf halak"
52708
+ ],
52709
+ tr: [
52710
+ "merhaba",
52711
+ "nasilsin",
52712
+ "nasilsiniz",
52713
+ "iyi misin"
52714
+ ],
52715
+ ru: [
52716
+ "privet",
52717
+ "kak dela",
52718
+ "zdravstvuy",
52719
+ "dobryy den"
52720
+ ]
52721
+ };
51784
52722
  var nluSchema = external_exports.object({
51785
52723
  enabled: external_exports.boolean().default(false),
51786
52724
  // Remote NLU service is optional; Vaayu always has in-process rule NLU.
@@ -51800,6 +52738,9 @@ var nluSchema = external_exports.object({
51800
52738
  langDetectMinConfidence: external_exports.number().min(0).max(1).default(0.55),
51801
52739
  // Minimum cosine similarity for embedding-based intent classification.
51802
52740
  embeddingThreshold: external_exports.number().min(0).max(1).default(0.62),
52741
+ // Locale-indexed short-checkin phrase packs for greeting/check-in fast lane.
52742
+ // Top-10 locales ship by default (en, es, fr, de, hi, te, ja, ar, tr, ru).
52743
+ shortCheckinPacks: external_exports.record(external_exports.string(), external_exports.array(external_exports.string())).default(DEFAULT_SHORT_CHECKIN_PACKS),
51803
52744
  // GLiNER v2.1 endpoint for language-agnostic NER slot filling.
51804
52745
  glinerEndpoint: external_exports.string().default("http://localhost:8501"),
51805
52746
  // GLiNER API path (supports FastAPI adapters like /gliner-2).
@@ -51856,6 +52797,12 @@ var nluSchema = external_exports.object({
51856
52797
  }).default({});
51857
52798
 
51858
52799
  // packages/config/src/schema-channels.ts
52800
+ var normalizeTringPlatform = (value) => {
52801
+ const normalized = value.trim().toLowerCase();
52802
+ if (normalized === "whispr") return "signal";
52803
+ return normalized;
52804
+ };
52805
+ var tringPlatformSchema = external_exports.string().transform((value) => normalizeTringPlatform(value)).pipe(external_exports.enum(["whatsapp", "telegram", "signal"]));
51859
52806
  var telegramSchema = external_exports.object({
51860
52807
  enabled: external_exports.boolean().default(false),
51861
52808
  botToken: external_exports.string().optional(),
@@ -51955,11 +52902,24 @@ var imessageSchema = external_exports.object({
51955
52902
  });
51956
52903
  }
51957
52904
  }).default({});
52905
+ var tringSchema = external_exports.object({
52906
+ enabled: external_exports.boolean().default(false),
52907
+ daemonUrl: external_exports.string().default("http://localhost:8787"),
52908
+ tringBin: external_exports.string().optional(),
52909
+ platforms: external_exports.array(tringPlatformSchema).default(["whatsapp", "telegram", "signal"]),
52910
+ pollIntervalMs: external_exports.number().int().min(500).max(6e4).default(2e3),
52911
+ autoStartDaemon: external_exports.boolean().default(true),
52912
+ allowedChats: external_exports.array(external_exports.string()).optional(),
52913
+ providerId: external_exports.string().optional(),
52914
+ model: external_exports.string().default("mock-1"),
52915
+ system: external_exports.string().optional()
52916
+ }).default({});
51958
52917
  var channelsSchema = external_exports.object({
51959
52918
  telegram: telegramSchema,
51960
52919
  twitter: twitterSchema,
51961
52920
  whatsapp: whatsappSchema,
51962
- imessage: imessageSchema
52921
+ imessage: imessageSchema,
52922
+ tring: tringSchema
51963
52923
  }).default({});
51964
52924
 
51965
52925
  // packages/config/src/schema.ts
@@ -52152,6 +53112,15 @@ var routingSchema = external_exports.object({
52152
53112
  reasoning: external_exports.object({
52153
53113
  mode: external_exports.enum(["off", "auto", "on"]).default("off"),
52154
53114
  modelOverrides: external_exports.record(external_exports.string(), external_exports.string()).default({})
53115
+ }).default({}),
53116
+ policySidecarConfidence: external_exports.object({
53117
+ // Avoid low-confidence deep routing signals from sidecar.
53118
+ minConfidenceForCautious: external_exports.number().min(0).max(1).default(0.7),
53119
+ minConfidenceForDeny: external_exports.number().min(0).max(1).default(0.82),
53120
+ // Keep safe-fast-lane reasonably confident too.
53121
+ minConfidenceForSafeFastLane: external_exports.number().min(0).max(1).default(0.55),
53122
+ // Policy to apply when sidecar confidence is below threshold.
53123
+ downgradePolicy: external_exports.enum(["standard", "safe_fast_lane"]).default("standard")
52155
53124
  }).default({})
52156
53125
  }).default({});
52157
53126
  var mcpServerSchema = external_exports.object({
@@ -54515,7 +55484,7 @@ var FileStorage = class {
54515
55484
  import path25 from "node:path";
54516
55485
  async function createStorage(options) {
54517
55486
  if (options.driver === "sqlite") {
54518
- const module = await import("./chunks/sqlite-7BC4DJTN.js");
55487
+ const module = await import("./chunks/sqlite-4N7YH2KK.js");
54519
55488
  const sqlitePath = options.sqlitePath ?? path25.join(options.dataDir, "vaayu.db");
54520
55489
  return new module.SqliteStorage({ sqlitePath });
54521
55490
  }
@@ -54523,7 +55492,7 @@ async function createStorage(options) {
54523
55492
  if (!options.postgresUrl) {
54524
55493
  throw new Error("Postgres driver requires postgresUrl");
54525
55494
  }
54526
- const module = await import("./chunks/postgres-7GZDDX77.js");
55495
+ const module = await import("./chunks/postgres-YLCUNVPQ.js");
54527
55496
  return module.PostgresStorage.create({ url: options.postgresUrl });
54528
55497
  }
54529
55498
  return new FileStorage({ dataDir: options.dataDir });
@@ -55311,131 +56280,15 @@ var IdempotencyStore = class {
55311
56280
  }
55312
56281
  };
55313
56282
 
55314
- // apps/gateway/dist/models-cache.js
56283
+ // apps/gateway/dist/memory/smriti.js
55315
56284
  import fs30 from "node:fs";
55316
56285
  import path28 from "node:path";
55317
- function readCacheFile(cachePath, logger) {
55318
- if (!fs30.existsSync(cachePath))
55319
- return null;
55320
- try {
55321
- const raw = fs30.readFileSync(cachePath, "utf8");
55322
- if (!raw.trim())
55323
- return null;
55324
- const parsed = JSON.parse(raw);
55325
- if (!parsed || parsed.version !== 1)
55326
- return null;
55327
- return parsed;
55328
- } catch (error) {
55329
- logger?.warn("provider_model_cache_read_failed", {
55330
- error: error instanceof Error ? error.message : String(error)
55331
- });
55332
- return null;
55333
- }
55334
- }
55335
- function writeCacheFile(cachePath, cache, logger) {
55336
- try {
55337
- fs30.mkdirSync(path28.dirname(cachePath), { recursive: true });
55338
- const providers = {};
55339
- for (const [providerId, entry] of cache.entries()) {
55340
- providers[providerId] = entry;
55341
- }
55342
- const payload = {
55343
- version: 1,
55344
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
55345
- providers
55346
- };
55347
- fs30.writeFileSync(cachePath, JSON.stringify(payload, null, 2));
55348
- } catch (error) {
55349
- logger?.warn("provider_model_cache_write_failed", {
55350
- error: error instanceof Error ? error.message : String(error)
55351
- });
55352
- }
55353
- }
55354
- function createProviderModelCacheStore(params) {
55355
- const { cachePath, logger } = params;
55356
- const cache = /* @__PURE__ */ new Map();
55357
- const existing = readCacheFile(cachePath, logger);
55358
- if (existing?.providers) {
55359
- for (const [providerId, entry] of Object.entries(existing.providers)) {
55360
- cache.set(providerId, entry);
55361
- }
55362
- }
55363
- let pending = null;
55364
- const flush = () => writeCacheFile(cachePath, cache, logger);
55365
- const scheduleFlush = () => {
55366
- if (pending)
55367
- return;
55368
- pending = setTimeout(() => {
55369
- pending = null;
55370
- flush();
55371
- }, 500);
55372
- };
55373
- return {
55374
- cache,
55375
- get: (providerId) => cache.get(providerId),
55376
- set: (providerId, entry) => {
55377
- cache.set(providerId, entry);
55378
- scheduleFlush();
55379
- },
55380
- update: (providerId, update) => {
55381
- const existingEntry = cache.get(providerId);
55382
- if (!existingEntry)
55383
- return;
55384
- cache.set(providerId, { ...existingEntry, ...update });
55385
- scheduleFlush();
55386
- },
55387
- flush
55388
- };
55389
- }
55390
-
55391
- // apps/gateway/dist/models-refresh.js
55392
- function startModelRefreshLoop(params) {
55393
- const { enabled, intervalMs, jitterMs, initialDelayMs, providerIds, refreshProvider, logger } = params;
55394
- if (!enabled || providerIds.length === 0) {
55395
- return null;
55396
- }
55397
- let stopped = false;
55398
- let timer = null;
55399
- const nextDelay = () => {
55400
- const jitter = jitterMs > 0 ? Math.floor(Math.random() * jitterMs) : 0;
55401
- return intervalMs + jitter;
55402
- };
55403
- const runCycle = async () => {
55404
- if (stopped)
55405
- return;
55406
- for (const providerId of providerIds) {
55407
- if (stopped)
55408
- return;
55409
- try {
55410
- await refreshProvider(providerId);
55411
- } catch (error) {
55412
- logger.warn("provider_models_refresh_failed", {
55413
- providerId,
55414
- error: error instanceof Error ? error.message : String(error)
55415
- });
55416
- }
55417
- }
55418
- timer = setTimeout(runCycle, nextDelay());
55419
- };
55420
- timer = setTimeout(runCycle, Math.max(0, initialDelayMs ?? intervalMs));
55421
- return {
55422
- stop: () => {
55423
- stopped = true;
55424
- if (timer)
55425
- clearTimeout(timer);
55426
- }
55427
- };
55428
- }
55429
-
55430
- // apps/gateway/dist/memory/smriti.js
55431
- import fs31 from "node:fs";
55432
- import path29 from "node:path";
55433
56286
  import { randomUUID as randomUUID34 } from "node:crypto";
55434
- import { spawn as spawn4 } from "node:child_process";
56287
+ import { spawn as spawn5 } from "node:child_process";
55435
56288
  var _chitraguptaMemory = null;
55436
56289
  async function getChitraguptaMemory() {
55437
56290
  if (!_chitraguptaMemory) {
55438
- _chitraguptaMemory = await import("./chunks/dist-CY5NX2IK.js");
56291
+ _chitraguptaMemory = await import("./chunks/dist-ESCM3CP5.js");
55439
56292
  }
55440
56293
  return _chitraguptaMemory;
55441
56294
  }
@@ -55473,7 +56326,7 @@ function computeRetryDelayMs(retries) {
55473
56326
  function createSmritiHelpers(deps) {
55474
56327
  const { logger, getProfile, memoryAllowed, markdownMemory, smritiClient, smritiSource, smritiAutoCapture, smritiAutoCaptureMode, smritiDefaultTags, smritiBaseUrl, smritiApiKey, smritiPrefix, smritiOwner, smritiTimeoutMs, smritiAutoMemoryReady, smritiAutoMemoryPython, smritiAutoMemorySessionScript, smritiAutoMemoryPromptScript, storageRoot, repoRoot } = deps;
55475
56328
  const smritiState = {};
55476
- const outboxPath2 = path29.join(storageRoot ?? repoRoot, OUTBOX_FILE);
56329
+ const outboxPath2 = path28.join(storageRoot ?? repoRoot, OUTBOX_FILE);
55477
56330
  let outboxLoaded = false;
55478
56331
  let outboxEntries2 = [];
55479
56332
  let outboxOps = Promise.resolve();
@@ -55488,7 +56341,7 @@ function createSmritiHelpers(deps) {
55488
56341
  if (outboxLoaded)
55489
56342
  return;
55490
56343
  try {
55491
- const raw = await fs31.promises.readFile(outboxPath2, "utf8");
56344
+ const raw = await fs30.promises.readFile(outboxPath2, "utf8");
55492
56345
  const parsed = JSON.parse(raw);
55493
56346
  outboxEntries2 = Array.isArray(parsed) ? parsed.filter(isSmritiOutboxEntry) : [];
55494
56347
  } catch {
@@ -55497,12 +56350,12 @@ function createSmritiHelpers(deps) {
55497
56350
  outboxLoaded = true;
55498
56351
  }
55499
56352
  async function persistOutboxUnsafe() {
55500
- const dir = path29.dirname(outboxPath2);
55501
- await fs31.promises.mkdir(dir, { recursive: true });
56353
+ const dir = path28.dirname(outboxPath2);
56354
+ await fs30.promises.mkdir(dir, { recursive: true });
55502
56355
  const tempPath = `${outboxPath2}.tmp`;
55503
- await fs31.promises.writeFile(tempPath, `${JSON.stringify(outboxEntries2, null, 2)}
56356
+ await fs30.promises.writeFile(tempPath, `${JSON.stringify(outboxEntries2, null, 2)}
55504
56357
  `, "utf8");
55505
- await fs31.promises.rename(tempPath, outboxPath2);
56358
+ await fs30.promises.rename(tempPath, outboxPath2);
55506
56359
  }
55507
56360
  function shouldSkipDailyTranscriptWrite() {
55508
56361
  return isSmritiMemoryEnabled() && isSmritiWriteThroughEnabled();
@@ -55767,11 +56620,11 @@ function createSmritiHelpers(deps) {
55767
56620
  if (!smritiAutoMemoryReady)
55768
56621
  return null;
55769
56622
  const scriptPath = params.kind === "session" ? smritiAutoMemorySessionScript : smritiAutoMemoryPromptScript;
55770
- if (!fs31.existsSync(scriptPath))
56623
+ if (!fs30.existsSync(scriptPath))
55771
56624
  return null;
55772
56625
  const env = buildSmritiAutoMemoryEnv({ userId: params.userId });
55773
- return await new Promise((resolve) => {
55774
- const child = spawn4(smritiAutoMemoryPython, [scriptPath], {
56626
+ return await new Promise((resolve3) => {
56627
+ const child = spawn5(smritiAutoMemoryPython, [scriptPath], {
55775
56628
  env,
55776
56629
  stdio: ["pipe", "pipe", "pipe"]
55777
56630
  });
@@ -55779,7 +56632,7 @@ function createSmritiHelpers(deps) {
55779
56632
  let stderr = "";
55780
56633
  const timeout = setTimeout(() => {
55781
56634
  child.kill("SIGKILL");
55782
- resolve(null);
56635
+ resolve3(null);
55783
56636
  }, 15e3);
55784
56637
  child.stdout.on("data", (chunk) => {
55785
56638
  stdout += chunk.toString();
@@ -55793,7 +56646,7 @@ function createSmritiHelpers(deps) {
55793
56646
  kind: params.kind,
55794
56647
  error: error.message
55795
56648
  });
55796
- resolve(null);
56649
+ resolve3(null);
55797
56650
  });
55798
56651
  child.on("close", () => {
55799
56652
  clearTimeout(timeout);
@@ -55805,17 +56658,17 @@ function createSmritiHelpers(deps) {
55805
56658
  }
55806
56659
  const trimmed = stdout.trim();
55807
56660
  if (!trimmed)
55808
- return resolve(null);
56661
+ return resolve3(null);
55809
56662
  try {
55810
56663
  const parsed = JSON.parse(trimmed);
55811
56664
  const message = typeof parsed.systemMessage === "string" ? parsed.systemMessage.trim() : "";
55812
- resolve(message || null);
56665
+ resolve3(message || null);
55813
56666
  } catch (error) {
55814
56667
  logger.warn("smriti_auto_memory_parse_failed", {
55815
56668
  kind: params.kind,
55816
56669
  error: error instanceof Error ? error.message : String(error)
55817
56670
  });
55818
- resolve(null);
56671
+ resolve3(null);
55819
56672
  }
55820
56673
  });
55821
56674
  if (params.input)
@@ -55866,6 +56719,122 @@ function createSmritiHelpers(deps) {
55866
56719
  };
55867
56720
  }
55868
56721
 
56722
+ // apps/gateway/dist/models-cache.js
56723
+ import fs31 from "node:fs";
56724
+ import path29 from "node:path";
56725
+ function readCacheFile(cachePath, logger) {
56726
+ if (!fs31.existsSync(cachePath))
56727
+ return null;
56728
+ try {
56729
+ const raw = fs31.readFileSync(cachePath, "utf8");
56730
+ if (!raw.trim())
56731
+ return null;
56732
+ const parsed = JSON.parse(raw);
56733
+ if (!parsed || parsed.version !== 1)
56734
+ return null;
56735
+ return parsed;
56736
+ } catch (error) {
56737
+ logger?.warn("provider_model_cache_read_failed", {
56738
+ error: error instanceof Error ? error.message : String(error)
56739
+ });
56740
+ return null;
56741
+ }
56742
+ }
56743
+ function writeCacheFile(cachePath, cache, logger) {
56744
+ try {
56745
+ fs31.mkdirSync(path29.dirname(cachePath), { recursive: true });
56746
+ const providers = {};
56747
+ for (const [providerId, entry] of cache.entries()) {
56748
+ providers[providerId] = entry;
56749
+ }
56750
+ const payload = {
56751
+ version: 1,
56752
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
56753
+ providers
56754
+ };
56755
+ fs31.writeFileSync(cachePath, JSON.stringify(payload, null, 2));
56756
+ } catch (error) {
56757
+ logger?.warn("provider_model_cache_write_failed", {
56758
+ error: error instanceof Error ? error.message : String(error)
56759
+ });
56760
+ }
56761
+ }
56762
+ function createProviderModelCacheStore(params) {
56763
+ const { cachePath, logger } = params;
56764
+ const cache = /* @__PURE__ */ new Map();
56765
+ const existing = readCacheFile(cachePath, logger);
56766
+ if (existing?.providers) {
56767
+ for (const [providerId, entry] of Object.entries(existing.providers)) {
56768
+ cache.set(providerId, entry);
56769
+ }
56770
+ }
56771
+ let pending = null;
56772
+ const flush = () => writeCacheFile(cachePath, cache, logger);
56773
+ const scheduleFlush = () => {
56774
+ if (pending)
56775
+ return;
56776
+ pending = setTimeout(() => {
56777
+ pending = null;
56778
+ flush();
56779
+ }, 500);
56780
+ };
56781
+ return {
56782
+ cache,
56783
+ get: (providerId) => cache.get(providerId),
56784
+ set: (providerId, entry) => {
56785
+ cache.set(providerId, entry);
56786
+ scheduleFlush();
56787
+ },
56788
+ update: (providerId, update) => {
56789
+ const existingEntry = cache.get(providerId);
56790
+ if (!existingEntry)
56791
+ return;
56792
+ cache.set(providerId, { ...existingEntry, ...update });
56793
+ scheduleFlush();
56794
+ },
56795
+ flush
56796
+ };
56797
+ }
56798
+
56799
+ // apps/gateway/dist/models-refresh.js
56800
+ function startModelRefreshLoop(params) {
56801
+ const { enabled, intervalMs, jitterMs, initialDelayMs, providerIds, refreshProvider, logger } = params;
56802
+ if (!enabled || providerIds.length === 0) {
56803
+ return null;
56804
+ }
56805
+ let stopped = false;
56806
+ let timer = null;
56807
+ const nextDelay = () => {
56808
+ const jitter = jitterMs > 0 ? Math.floor(Math.random() * jitterMs) : 0;
56809
+ return intervalMs + jitter;
56810
+ };
56811
+ const runCycle = async () => {
56812
+ if (stopped)
56813
+ return;
56814
+ for (const providerId of providerIds) {
56815
+ if (stopped)
56816
+ return;
56817
+ try {
56818
+ await refreshProvider(providerId);
56819
+ } catch (error) {
56820
+ logger.warn("provider_models_refresh_failed", {
56821
+ providerId,
56822
+ error: error instanceof Error ? error.message : String(error)
56823
+ });
56824
+ }
56825
+ }
56826
+ timer = setTimeout(runCycle, nextDelay());
56827
+ };
56828
+ timer = setTimeout(runCycle, Math.max(0, initialDelayMs ?? intervalMs));
56829
+ return {
56830
+ stop: () => {
56831
+ stopped = true;
56832
+ if (timer)
56833
+ clearTimeout(timer);
56834
+ }
56835
+ };
56836
+ }
56837
+
55869
56838
  // apps/gateway/dist/runtime/memory-markdown.js
55870
56839
  import fs32 from "node:fs";
55871
56840
  import path30 from "node:path";
@@ -55910,65 +56879,259 @@ function createMarkdownMemoryWriter(params) {
55910
56879
  return { enabled, dailyDir, longTermPath, appendDaily, appendLongTerm };
55911
56880
  }
55912
56881
 
55913
- // apps/gateway/dist/gateway/summary-llm.js
55914
- function buildOutboundPromptText4(messages) {
55915
- return messages.map((message) => `${message.role}: ${message.content}`).join("\n");
55916
- }
55917
- function createSummaryLLM(getProvider, getModel, logger) {
55918
- return {
55919
- async generate(prompt) {
55920
- const provider = getProvider();
55921
- if (!provider) {
55922
- logger?.warn("summary_llm_no_provider");
55923
- throw new Error("No provider available for summarization");
56882
+ // apps/gateway/dist/gateway/config-path.js
56883
+ import fs33 from "node:fs";
56884
+ import path31 from "node:path";
56885
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
56886
+ var GATEWAY_CONFIG_FILENAMES = [
56887
+ "vaayu.config.json",
56888
+ "vaayu.config.yaml",
56889
+ "vaayu.config.yml"
56890
+ ];
56891
+ function resolvePreferredGatewayConfigPath(params) {
56892
+ const envConfigPath = params?.envConfigPath ?? process.env.VAAYU_CONFIG;
56893
+ if (envConfigPath?.trim()) {
56894
+ return void 0;
56895
+ }
56896
+ const existsSync5 = params?.existsSync ?? fs33.existsSync;
56897
+ const moduleFilePath = params?.moduleFilePath ?? fileURLToPath2(import.meta.url);
56898
+ let currentDir = path31.dirname(moduleFilePath);
56899
+ for (let depth = 0; depth < 10; depth += 1) {
56900
+ for (const filename of GATEWAY_CONFIG_FILENAMES) {
56901
+ const candidate = path31.join(currentDir, filename);
56902
+ if (existsSync5(candidate)) {
56903
+ return candidate;
55924
56904
  }
55925
- const model = getModel() ?? "gpt-4o-mini";
56905
+ }
56906
+ const parent = path31.dirname(currentDir);
56907
+ if (parent === currentDir)
56908
+ break;
56909
+ currentDir = parent;
56910
+ }
56911
+ return void 0;
56912
+ }
56913
+
56914
+ // apps/gateway/dist/gateway/identity-loader.js
56915
+ import fs34 from "node:fs";
56916
+ import path32 from "node:path";
56917
+ var MAX_PROMPT_CHARS_DEFAULT = 12e3;
56918
+ function uniqueStrings(values) {
56919
+ const seen = /* @__PURE__ */ new Set();
56920
+ const out = [];
56921
+ for (const value of values) {
56922
+ if (!value)
56923
+ continue;
56924
+ if (seen.has(value))
56925
+ continue;
56926
+ seen.add(value);
56927
+ out.push(value);
56928
+ }
56929
+ return out;
56930
+ }
56931
+ function buildCandidateRoots(repoRoot) {
56932
+ const roots = [repoRoot];
56933
+ let current = repoRoot;
56934
+ for (let i = 0; i < 3; i += 1) {
56935
+ const parent = path32.dirname(current);
56936
+ if (!parent || parent === current)
56937
+ break;
56938
+ roots.push(parent);
56939
+ current = parent;
56940
+ }
56941
+ return uniqueStrings(roots);
56942
+ }
56943
+ function readFirstExisting(roots, filenames) {
56944
+ for (const root of roots) {
56945
+ for (const name of filenames) {
56946
+ const candidate = path32.join(root, name);
55926
56947
  try {
55927
- const messages = [
55928
- {
55929
- role: "system",
55930
- content: "You are a concise summarizer. Generate clear, factual summaries that preserve important details."
55931
- },
55932
- {
55933
- role: "user",
55934
- content: prompt
56948
+ if (!fs34.existsSync(candidate))
56949
+ continue;
56950
+ const text = fs34.readFileSync(candidate, "utf8").trim();
56951
+ if (!text)
56952
+ continue;
56953
+ return { file: candidate, text };
56954
+ } catch {
56955
+ }
56956
+ }
56957
+ }
56958
+ return null;
56959
+ }
56960
+ function extractIdentityFields(identity, user) {
56961
+ const fields = {};
56962
+ if (identity) {
56963
+ const nameMatch = identity.match(/(?:^|\n)\s*(?:-\s*)?(?:\*\*)?Name(?:\*\*)?:\s*(?:\*\*)?(.+?)(?:\*\*)?$/im);
56964
+ if (nameMatch?.[1]) {
56965
+ const raw = nameMatch[1].trim().replace(/^_\(?|_?\)?$/g, "");
56966
+ if (raw && !raw.startsWith("(") && raw.length < 64) {
56967
+ fields.assistantName = raw;
56968
+ }
56969
+ }
56970
+ const emojiMatch = identity.match(/(?:^|\n)\s*(?:-\s*)?(?:\*\*)?Emoji(?:\*\*)?:\s*(?:\*\*)?(.+?)(?:\*\*)?$/im);
56971
+ if (emojiMatch?.[1]) {
56972
+ const raw = emojiMatch[1].trim().replace(/^_\(?|_?\)?$/g, "");
56973
+ if (raw && !raw.startsWith("(") && raw.length < 8) {
56974
+ fields.emoji = raw;
56975
+ }
56976
+ }
56977
+ }
56978
+ if (user) {
56979
+ if (!fields.userName) {
56980
+ const callMatch = user.match(/(?:^|\n)\s*(?:What to call them|Call them|User name|userName):\s*(.+?)$/im);
56981
+ if (callMatch?.[1]) {
56982
+ const raw = callMatch[1].trim().split(/\s*[/,]\s*/)[0];
56983
+ if (raw && raw.length < 64) {
56984
+ fields.userName = raw;
56985
+ }
56986
+ }
56987
+ }
56988
+ if (!fields.assistantName) {
56989
+ const assistantCallMatch = user.match(/(?:^|\n)\s*(?:What to call assistant|Assistant name|assistantName):\s*(.+?)$/im);
56990
+ if (assistantCallMatch?.[1]) {
56991
+ const raw = assistantCallMatch[1].trim();
56992
+ if (raw && raw.length < 64) {
56993
+ fields.assistantName = raw;
56994
+ }
56995
+ }
56996
+ }
56997
+ }
56998
+ return fields;
56999
+ }
57000
+ function truncatePrompt(text, maxChars) {
57001
+ if (text.length <= maxChars)
57002
+ return text;
57003
+ return `${text.slice(0, maxChars)}
57004
+
57005
+ [...identity truncated]`;
57006
+ }
57007
+ function loadWorkspaceIdentityPrompt(params) {
57008
+ const roots = buildCandidateRoots(params.repoRoot);
57009
+ const maxChars = params.maxPromptChars ?? MAX_PROMPT_CHARS_DEFAULT;
57010
+ const filesLoaded = [];
57011
+ const parts = [];
57012
+ const soul = readFirstExisting(roots, ["SOUL.md", "soul.md"]);
57013
+ if (soul) {
57014
+ filesLoaded.push(soul.file);
57015
+ parts.push(soul.text);
57016
+ }
57017
+ const identity = readFirstExisting(roots, ["IDENTITY.md", "identity.md"]);
57018
+ if (identity) {
57019
+ filesLoaded.push(identity.file);
57020
+ parts.push(identity.text);
57021
+ }
57022
+ const personality = readFirstExisting(roots, ["PERSONALITY.md", "personality.md"]);
57023
+ if (personality) {
57024
+ filesLoaded.push(personality.file);
57025
+ parts.push(personality.text);
57026
+ }
57027
+ const user = readFirstExisting(roots, ["USER.md", "user.md"]);
57028
+ if (user) {
57029
+ filesLoaded.push(user.file);
57030
+ parts.push(user.text);
57031
+ }
57032
+ const fields = extractIdentityFields(identity?.text, user?.text);
57033
+ const prompt = truncatePrompt(parts.filter(Boolean).join("\n\n"), maxChars).trim();
57034
+ return { prompt, rootsChecked: roots, filesLoaded, fields };
57035
+ }
57036
+ function reloadIdentity(params) {
57037
+ return loadWorkspaceIdentityPrompt(params);
57038
+ }
57039
+
57040
+ // apps/gateway/dist/gateway/setup-smriti.js
57041
+ import fs35 from "node:fs";
57042
+ import path33 from "node:path";
57043
+ function resolveSmritiRuntimeConfig(params) {
57044
+ const { config, repoRoot, logger } = params;
57045
+ const smritiConfig = config.smriti ?? {};
57046
+ const smritiEnabled = smritiConfig.enabled ?? false;
57047
+ const smritiBaseUrl = smritiConfig.baseUrl ?? "http://127.0.0.1:7788";
57048
+ const smritiConfigPath = process.env.SMRITI_CONFIG_PATH ?? path33.resolve(repoRoot, "smriti", "smriti.config.json");
57049
+ let smritiLocalConfig = null;
57050
+ if (smritiEnabled && smritiBaseUrl && fs35.existsSync(smritiConfigPath)) {
57051
+ try {
57052
+ const raw = fs35.readFileSync(smritiConfigPath, "utf8");
57053
+ smritiLocalConfig = JSON.parse(raw);
57054
+ } catch (error) {
57055
+ logger.warn("smriti_config_load_failed", {
57056
+ error: error instanceof Error ? error.message : String(error)
57057
+ });
57058
+ }
57059
+ }
57060
+ let smritiApiKey = smritiConfig.apiKey;
57061
+ if (!smritiApiKey && smritiEnabled && smritiBaseUrl) {
57062
+ try {
57063
+ const parsed = new URL(smritiBaseUrl);
57064
+ if (isLoopbackHost(parsed.hostname)) {
57065
+ const storageRoot = smritiLocalConfig?.storage?.root;
57066
+ if (storageRoot) {
57067
+ const baseDir = path33.dirname(smritiConfigPath);
57068
+ const resolvedStorage = path33.isAbsolute(storageRoot) ? storageRoot : path33.resolve(baseDir, storageRoot);
57069
+ const keyPath = path33.join(resolvedStorage, "keys", "master.key");
57070
+ if (fs35.existsSync(keyPath)) {
57071
+ const key = fs35.readFileSync(keyPath, "utf8").trim();
57072
+ if (key) {
57073
+ smritiApiKey = key;
57074
+ logger.info("smriti_master_key_loaded", { path: keyPath });
57075
+ }
55935
57076
  }
55936
- ];
55937
- const requestMeta = buildChatMeta({
55938
- purpose: "summary",
55939
- providerId: provider.id,
55940
- model,
55941
- normalization: buildNormalizationOutboundMeta(normalizeIncomingText(buildOutboundPromptText4(messages)))
55942
- });
55943
- const response = await chatWithPolicy({
55944
- provider,
55945
- request: buildChatRequest({
55946
- model,
55947
- messages,
55948
- temperature: 0.3,
55949
- maxTokens: 1024,
55950
- metadata: requestMeta
55951
- }),
55952
- logger,
55953
- meta: requestMeta
55954
- });
55955
- logger?.info("summary_llm_generated", {
55956
- provider: provider.id,
55957
- model,
55958
- inputLength: prompt.length,
55959
- outputLength: response.content.length
55960
- });
55961
- return response.content;
55962
- } catch (error) {
55963
- const message = error instanceof Error ? error.message : String(error);
55964
- logger?.error("summary_llm_error", {
55965
- error: message,
55966
- provider: provider.id,
55967
- model
55968
- });
55969
- throw error;
57077
+ }
55970
57078
  }
57079
+ } catch (error) {
57080
+ logger.warn("smriti_master_key_load_failed", {
57081
+ error: error instanceof Error ? error.message : String(error)
57082
+ });
55971
57083
  }
57084
+ }
57085
+ const smritiPrefix = smritiConfig.prefix;
57086
+ const smritiOwner = smritiConfig.owner;
57087
+ const smritiAutoCapture = smritiConfig.autoCapture ?? false;
57088
+ const smritiAutoCaptureMode = smritiConfig.autoCaptureMode ?? "exchange";
57089
+ const smritiRetrieveEnabled = smritiConfig.retrieveEnabled ?? false;
57090
+ const smritiRetrieveLimit = smritiConfig.retrieveLimit ?? 5;
57091
+ const smritiDefaultTags = smritiConfig.defaultTags ?? void 0;
57092
+ const smritiSource = smritiConfig.source;
57093
+ const smritiAutoMemoryEnabled = smritiConfig.autoMemoryEnabled ?? false;
57094
+ const smritiAutoMemoryPython = smritiConfig.autoMemoryPython ?? "python3";
57095
+ const smritiAutoMemoryFallbackRoot = path33.resolve(repoRoot, "skills-core", "smriti-auto-memory", "hooks", "scripts");
57096
+ const smritiAutoMemoryRootCandidates = [
57097
+ smritiAutoMemoryFallbackRoot,
57098
+ path33.resolve(repoRoot, "..", "ecosystem", "skills", "smriti-auto-memory", "hooks", "scripts"),
57099
+ path33.resolve(repoRoot, "skills", "smriti-auto-memory", "hooks", "scripts")
57100
+ ];
57101
+ const smritiAutoMemoryDefaultRoot = smritiAutoMemoryRootCandidates.find((candidate) => fs35.existsSync(candidate)) ?? smritiAutoMemoryFallbackRoot;
57102
+ const smritiAutoMemoryRoot = smritiConfig.autoMemoryPath ?? smritiAutoMemoryDefaultRoot;
57103
+ const smritiAutoMemorySessionScript = path33.join(smritiAutoMemoryRoot, "session-start.py");
57104
+ const smritiAutoMemoryPromptScript = path33.join(smritiAutoMemoryRoot, "user-prompt.py");
57105
+ const smritiAutoMemoryReady = smritiEnabled && smritiAutoMemoryEnabled && Boolean(smritiApiKey) && fs35.existsSync(smritiAutoMemorySessionScript) && fs35.existsSync(smritiAutoMemoryPromptScript);
57106
+ const smritiClientOptions = smritiEnabled && smritiBaseUrl ? {
57107
+ baseUrl: smritiBaseUrl,
57108
+ apiKey: smritiApiKey,
57109
+ timeoutMs: smritiConfig.timeoutMs,
57110
+ prefix: smritiPrefix ? smritiPrefix.replace(/\/$/, "") : smritiOwner ? `user/${normalizeSmritiOwner(smritiOwner)}` : void 0
57111
+ } : void 0;
57112
+ return {
57113
+ smritiConfig,
57114
+ smritiEnabled,
57115
+ smritiBaseUrl,
57116
+ smritiApiKey,
57117
+ smritiPrefix,
57118
+ smritiOwner,
57119
+ smritiAutoCapture,
57120
+ smritiAutoCaptureMode,
57121
+ smritiRetrieveEnabled,
57122
+ smritiRetrieveLimit,
57123
+ smritiDefaultTags,
57124
+ smritiSource,
57125
+ smritiAutoMemoryEnabled,
57126
+ smritiAutoMemoryPython,
57127
+ smritiAutoMemoryRoot,
57128
+ smritiAutoMemorySessionScript,
57129
+ smritiAutoMemoryPromptScript,
57130
+ smritiAutoMemoryReady,
57131
+ smritiClientOptions,
57132
+ smritiVectorProvider: smritiLocalConfig?.vector?.provider,
57133
+ smritiVectorFallbacks: smritiLocalConfig?.vector?.fallbackProviders,
57134
+ smritiEmbeddingProviders: smritiLocalConfig?.embeddings?.providerPriority
55972
57135
  };
55973
57136
  }
55974
57137
 
@@ -56028,8 +57191,8 @@ function cleanupPairings(pairingStore, channelPairingStore) {
56028
57191
  }
56029
57192
 
56030
57193
  // apps/gateway/dist/gateway/tool-registry.js
56031
- import path31 from "node:path";
56032
- import fs33 from "node:fs";
57194
+ import path34 from "node:path";
57195
+ import fs36 from "node:fs";
56033
57196
 
56034
57197
  // apps/gateway/dist/gateway/smriti-status.js
56035
57198
  async function buildSmritiStatus(params) {
@@ -56269,13 +57432,13 @@ async function setupToolRegistry(params) {
56269
57432
  });
56270
57433
  const autoMcpServers = [];
56271
57434
  if (gwEnv.holoCube.enabled && gwEnv.holoCube.bridgeUrl) {
56272
- const defaultHoloManifestPath = path31.resolve(repoRoot, "..", "ecosystem", "skills", "holo-cube", "server", "mcp.manifest.json");
57435
+ const defaultHoloManifestPath = path34.resolve(repoRoot, "..", "ecosystem", "skills", "holo-cube", "server", "mcp.manifest.json");
56273
57436
  const holoManifestCandidates = [
56274
57437
  defaultHoloManifestPath,
56275
- path31.resolve(repoRoot, "skills-core", "holo-cube", "server", "mcp.manifest.json"),
56276
- path31.resolve(repoRoot, "skills", "holo-cube", "server", "mcp.manifest.json")
57438
+ path34.resolve(repoRoot, "skills-core", "holo-cube", "server", "mcp.manifest.json"),
57439
+ path34.resolve(repoRoot, "skills", "holo-cube", "server", "mcp.manifest.json")
56277
57440
  ];
56278
- const holoManifestPath = holoManifestCandidates.find((candidate) => fs33.existsSync(candidate)) ?? defaultHoloManifestPath;
57441
+ const holoManifestPath = holoManifestCandidates.find((candidate) => fs36.existsSync(candidate)) ?? defaultHoloManifestPath;
56279
57442
  autoMcpServers.push({
56280
57443
  id: "holocube",
56281
57444
  command: "node",
@@ -56302,23 +57465,23 @@ async function setupToolRegistry(params) {
56302
57465
  }
56303
57466
 
56304
57467
  // apps/gateway/dist/gateway/tool-policy.js
56305
- import fs34 from "node:fs";
56306
- import path32 from "node:path";
57468
+ import fs37 from "node:fs";
57469
+ import path35 from "node:path";
56307
57470
  function createToolPolicyLoader(logger) {
56308
57471
  return (customPath, baseDir) => {
56309
57472
  const cwd = baseDir ?? process.cwd();
56310
- const fallbackPolicyPath = path32.resolve(cwd, "skills-core", "skills.config.json");
57473
+ const fallbackPolicyPath = path35.resolve(cwd, "skills-core", "skills.config.json");
56311
57474
  const defaultCandidates = [
56312
57475
  fallbackPolicyPath,
56313
- path32.resolve(cwd, "skills", "skills.config.json")
57476
+ path35.resolve(cwd, "skills", "skills.config.json")
56314
57477
  ];
56315
57478
  const configuredBase = customPath ?? process.env.VAAYU_SKILLS_CONFIG;
56316
- const firstExisting = defaultCandidates.find((candidate) => fs34.existsSync(candidate));
56317
- const policyPath = configuredBase ? path32.isAbsolute(configuredBase) ? configuredBase : path32.resolve(cwd, configuredBase) : firstExisting ?? fallbackPolicyPath;
56318
- if (!fs34.existsSync(policyPath))
57479
+ const firstExisting = defaultCandidates.find((candidate) => fs37.existsSync(candidate));
57480
+ const policyPath = configuredBase ? path35.isAbsolute(configuredBase) ? configuredBase : path35.resolve(cwd, configuredBase) : firstExisting ?? fallbackPolicyPath;
57481
+ if (!fs37.existsSync(policyPath))
56319
57482
  return void 0;
56320
57483
  try {
56321
- const raw = fs34.readFileSync(policyPath, "utf8");
57484
+ const raw = fs37.readFileSync(policyPath, "utf8");
56322
57485
  const parsed = JSON.parse(raw);
56323
57486
  return {
56324
57487
  allow: Array.isArray(parsed.allow) ? parsed.allow : [],
@@ -57150,8 +58313,8 @@ function createWeatherDefaultsUpdater(params) {
57150
58313
  }
57151
58314
 
57152
58315
  // apps/gateway/dist/skills.js
57153
- import fs35 from "node:fs";
57154
- import path33 from "node:path";
58316
+ import fs38 from "node:fs";
58317
+ import path36 from "node:path";
57155
58318
  var SKILL_PACK_SKIP_DIRS = /* @__PURE__ */ new Set([
57156
58319
  "assets",
57157
58320
  "resources",
@@ -57165,17 +58328,17 @@ var SKILL_PACK_SKIP_DIRS = /* @__PURE__ */ new Set([
57165
58328
  ".tmp"
57166
58329
  ]);
57167
58330
  function isSkillPackDir(dir) {
57168
- return fs35.existsSync(path33.join(dir, "manifest.json")) && fs35.existsSync(path33.join(dir, "SKILL.md"));
58331
+ return fs38.existsSync(path36.join(dir, "manifest.json")) && fs38.existsSync(path36.join(dir, "SKILL.md"));
57169
58332
  }
57170
58333
  function collectSkillPackDirs(root) {
57171
- if (!fs35.existsSync(root)) {
58334
+ if (!fs38.existsSync(root)) {
57172
58335
  return [];
57173
58336
  }
57174
58337
  const out = [];
57175
58338
  const stack = [];
57176
- const entries2 = fs35.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !SKILL_PACK_SKIP_DIRS.has(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
58339
+ const entries2 = fs38.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !SKILL_PACK_SKIP_DIRS.has(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
57177
58340
  for (const entry of entries2.reverse()) {
57178
- stack.push(path33.join(root, entry.name));
58341
+ stack.push(path36.join(root, entry.name));
57179
58342
  }
57180
58343
  while (stack.length) {
57181
58344
  const dir = stack.pop();
@@ -57187,21 +58350,21 @@ function collectSkillPackDirs(root) {
57187
58350
  }
57188
58351
  let children;
57189
58352
  try {
57190
- children = fs35.readdirSync(dir, { withFileTypes: true });
58353
+ children = fs38.readdirSync(dir, { withFileTypes: true });
57191
58354
  } catch {
57192
58355
  continue;
57193
58356
  }
57194
58357
  const ordered = children.filter((child) => child.isDirectory()).filter((child) => !SKILL_PACK_SKIP_DIRS.has(child.name)).filter((child) => !child.name.startsWith(".")).sort((a, b) => a.name.localeCompare(b.name));
57195
58358
  for (const child of ordered.reverse()) {
57196
- stack.push(path33.join(dir, child.name));
58359
+ stack.push(path36.join(dir, child.name));
57197
58360
  }
57198
58361
  }
57199
58362
  return out;
57200
58363
  }
57201
58364
  function resolveSkillAssetPath(options) {
57202
- const baseRoot = path33.resolve(options.repoRoot, options.root);
57203
- const direct = path33.join(baseRoot, options.skillId, ...options.relativePath);
57204
- if (fs35.existsSync(direct)) {
58365
+ const baseRoot = path36.resolve(options.repoRoot, options.root);
58366
+ const direct = path36.join(baseRoot, options.skillId, ...options.relativePath);
58367
+ if (fs38.existsSync(direct)) {
57205
58368
  return direct;
57206
58369
  }
57207
58370
  const maxDepth = 5;
@@ -57216,7 +58379,7 @@ function resolveSkillAssetPath(options) {
57216
58379
  continue;
57217
58380
  let children;
57218
58381
  try {
57219
- children = fs35.readdirSync(next.dir, { withFileTypes: true });
58382
+ children = fs38.readdirSync(next.dir, { withFileTypes: true });
57220
58383
  } catch {
57221
58384
  continue;
57222
58385
  }
@@ -57227,10 +58390,10 @@ function resolveSkillAssetPath(options) {
57227
58390
  continue;
57228
58391
  if (child.name.startsWith("."))
57229
58392
  continue;
57230
- const childDir = path33.join(next.dir, child.name);
58393
+ const childDir = path36.join(next.dir, child.name);
57231
58394
  if (child.name === options.skillId) {
57232
- const candidate = path33.join(childDir, ...options.relativePath);
57233
- if (fs35.existsSync(candidate)) {
58395
+ const candidate = path36.join(childDir, ...options.relativePath);
58396
+ if (fs38.existsSync(candidate)) {
57234
58397
  return candidate;
57235
58398
  }
57236
58399
  }
@@ -57244,30 +58407,30 @@ function resolveSkillAssetPath(options) {
57244
58407
  }
57245
58408
  function loadSkillManifests(includeLab, baseDir) {
57246
58409
  const rootDir = baseDir ?? process.cwd();
57247
- const ecosystemDir = path33.resolve(rootDir, "..", "ecosystem");
58410
+ const ecosystemDir = path36.resolve(rootDir, "..", "ecosystem");
57248
58411
  const coreStableRoots = [
57249
- path33.resolve(rootDir, "skills-core"),
58412
+ path36.resolve(rootDir, "skills-core"),
57250
58413
  // Legacy fallback during migration.
57251
- path33.resolve(rootDir, "skills")
58414
+ path36.resolve(rootDir, "skills")
57252
58415
  ];
57253
58416
  const roots = [
57254
58417
  ...coreStableRoots.map((dir) => ({ dir, status: "stable" })),
57255
58418
  // Approved third-party skills
57256
- { dir: path33.resolve(ecosystemDir, "skills"), status: "stable" },
58419
+ { dir: path36.resolve(ecosystemDir, "skills"), status: "stable" },
57257
58420
  // Community submissions (discoverable, not tool-executable by default)
57258
- { dir: path33.resolve(ecosystemDir, "skill-community"), status: "community" }
58421
+ { dir: path36.resolve(ecosystemDir, "skill-community"), status: "community" }
57259
58422
  ];
57260
58423
  if (includeLab) {
57261
- roots.push({ dir: path33.resolve(ecosystemDir, "skill-lab"), status: "lab" });
58424
+ roots.push({ dir: path36.resolve(ecosystemDir, "skill-lab"), status: "lab" });
57262
58425
  }
57263
58426
  const manifests = [];
57264
58427
  const seen = /* @__PURE__ */ new Set();
57265
58428
  for (const root of roots) {
57266
58429
  const packDirs = collectSkillPackDirs(root.dir);
57267
58430
  for (const packDir of packDirs) {
57268
- const manifestPath = path33.join(packDir, "manifest.json");
58431
+ const manifestPath = path36.join(packDir, "manifest.json");
57269
58432
  try {
57270
- const raw = fs35.readFileSync(manifestPath, "utf8");
58433
+ const raw = fs38.readFileSync(manifestPath, "utf8");
57271
58434
  const data2 = JSON.parse(raw);
57272
58435
  if (!data2.id) {
57273
58436
  continue;
@@ -58074,6 +59237,7 @@ function setupGatewayStores(params) {
58074
59237
  let twitterAdapter;
58075
59238
  let whatsappAdapter;
58076
59239
  let imessageAdapter;
59240
+ let tringAdapter;
58077
59241
  let skillManifests = loadSkillManifests(gwEnv.skillsLabEnabled, repoRoot);
58078
59242
  const telegramAdapterRef = {
58079
59243
  get: () => telegramAdapter,
@@ -58099,6 +59263,12 @@ function setupGatewayStores(params) {
58099
59263
  imessageAdapter = adapter;
58100
59264
  }
58101
59265
  };
59266
+ const tringAdapterRef = {
59267
+ get: () => tringAdapter,
59268
+ set: (adapter) => {
59269
+ tringAdapter = adapter;
59270
+ }
59271
+ };
58102
59272
  const skillManifestsRef = {
58103
59273
  get: () => skillManifests,
58104
59274
  set: (skills) => {
@@ -58198,6 +59368,11 @@ function setupGatewayStores(params) {
58198
59368
  });
58199
59369
  return;
58200
59370
  }
59371
+ const tringChannel = normalizeTringChannel(channel);
59372
+ if (tringAdapter && tringChannel) {
59373
+ await sendTringMessage(tringAdapter, tringChannel, chatId, content);
59374
+ return;
59375
+ }
58201
59376
  if (channel === "hub") {
58202
59377
  const profile = getProfile?.();
58203
59378
  await sendHubReply({
@@ -58213,7 +59388,15 @@ function setupGatewayStores(params) {
58213
59388
  }
58214
59389
  }
58215
59390
  async function isChannelAllowed(channel, chatId, userId) {
59391
+ const tringPlatforms = (config.channels.tring.platforms ?? []).map((platform) => normalizeTringPlatform2(platform)).filter((platform) => Boolean(platform));
59392
+ const tringEnabledFor = (platform) => Boolean(config.channels.tring.enabled && tringPlatforms.includes(platform));
59393
+ const tringAllowedChats = config.channels.tring.allowedChats ?? [];
58216
59394
  if (channel === "telegram") {
59395
+ if (!config.channels.telegram.enabled && tringEnabledFor("telegram")) {
59396
+ if (tringAllowedChats.length === 0)
59397
+ return true;
59398
+ return tringAllowedChats.includes(chatId);
59399
+ }
58217
59400
  const configured = config.channels.telegram.allowedChats ?? [];
58218
59401
  const requirePairing = config.channels.telegram.requirePairing ?? configured.length === 0;
58219
59402
  if (!requirePairing && configured.includes(chatId))
@@ -58227,12 +59410,23 @@ function setupGatewayStores(params) {
58227
59410
  return true;
58228
59411
  }
58229
59412
  if (channel === "whatsapp") {
59413
+ if (!config.channels.whatsapp.enabled && tringEnabledFor("whatsapp")) {
59414
+ if (tringAllowedChats.length === 0)
59415
+ return true;
59416
+ return tringAllowedChats.includes(chatId);
59417
+ }
58230
59418
  const allowedNumbers = config.channels.whatsapp.allowedNumbers ?? [];
58231
59419
  if (allowedNumbers.length === 0)
58232
59420
  return true;
58233
59421
  if (chatId && allowedNumbers.includes(chatId))
58234
59422
  return true;
58235
59423
  }
59424
+ if ((channel === "signal" || channel === "whispr") && tringEnabledFor("signal")) {
59425
+ if (tringAllowedChats.length === 0)
59426
+ return true;
59427
+ if (chatId && tringAllowedChats.includes(chatId))
59428
+ return true;
59429
+ }
58236
59430
  if (channel === "imessage") {
58237
59431
  const allowedChats = config.channels.imessage.allowedChats ?? [];
58238
59432
  if (allowedChats.length === 0)
@@ -58257,6 +59451,7 @@ function setupGatewayStores(params) {
58257
59451
  twitterAdapterRef,
58258
59452
  whatsappAdapterRef,
58259
59453
  imessageAdapterRef,
59454
+ tringAdapterRef,
58260
59455
  skillManifestsRef,
58261
59456
  broadcastEvent,
58262
59457
  getProvider,
@@ -58264,6 +59459,26 @@ function setupGatewayStores(params) {
58264
59459
  isChannelAllowed
58265
59460
  };
58266
59461
  }
59462
+ async function sendTringMessage(adapter, platform, chatId, content) {
59463
+ await adapter.sendMessage(platform, chatId, content);
59464
+ }
59465
+ function normalizeTringPlatform2(platform) {
59466
+ const normalized = platform.trim().toLowerCase();
59467
+ if (normalized === "whispr")
59468
+ return "signal";
59469
+ if (normalized === "telegram" || normalized === "whatsapp" || normalized === "signal")
59470
+ return normalized;
59471
+ return void 0;
59472
+ }
59473
+ function normalizeTringChannel(channel) {
59474
+ if (channel === "telegram")
59475
+ return "telegram";
59476
+ if (channel === "whatsapp")
59477
+ return "whatsapp";
59478
+ if (channel === "signal" || channel === "whispr")
59479
+ return "signal";
59480
+ return void 0;
59481
+ }
58267
59482
 
58268
59483
  // apps/gateway/dist/gateway/setup-tools.js
58269
59484
  async function setupGatewayTools(ctx) {
@@ -58340,271 +59555,244 @@ async function setupGatewayTools(ctx) {
58340
59555
  };
58341
59556
  }
58342
59557
 
58343
- // apps/gateway/dist/gateway/setup-smriti.js
58344
- import fs36 from "node:fs";
58345
- import path34 from "node:path";
58346
- function resolveSmritiRuntimeConfig(params) {
58347
- const { config, repoRoot, logger } = params;
58348
- const smritiConfig = config.smriti ?? {};
58349
- const smritiEnabled = smritiConfig.enabled ?? false;
58350
- const smritiBaseUrl = smritiConfig.baseUrl ?? "http://127.0.0.1:7788";
58351
- const smritiConfigPath = process.env.SMRITI_CONFIG_PATH ?? path34.resolve(repoRoot, "smriti", "smriti.config.json");
58352
- let smritiLocalConfig = null;
58353
- if (smritiEnabled && smritiBaseUrl && fs36.existsSync(smritiConfigPath)) {
58354
- try {
58355
- const raw = fs36.readFileSync(smritiConfigPath, "utf8");
58356
- smritiLocalConfig = JSON.parse(raw);
58357
- } catch (error) {
58358
- logger.warn("smriti_config_load_failed", {
58359
- error: error instanceof Error ? error.message : String(error)
58360
- });
58361
- }
58362
- }
58363
- let smritiApiKey = smritiConfig.apiKey;
58364
- if (!smritiApiKey && smritiEnabled && smritiBaseUrl) {
58365
- try {
58366
- const parsed = new URL(smritiBaseUrl);
58367
- if (isLoopbackHost(parsed.hostname)) {
58368
- const storageRoot = smritiLocalConfig?.storage?.root;
58369
- if (storageRoot) {
58370
- const baseDir = path34.dirname(smritiConfigPath);
58371
- const resolvedStorage = path34.isAbsolute(storageRoot) ? storageRoot : path34.resolve(baseDir, storageRoot);
58372
- const keyPath = path34.join(resolvedStorage, "keys", "master.key");
58373
- if (fs36.existsSync(keyPath)) {
58374
- const key = fs36.readFileSync(keyPath, "utf8").trim();
58375
- if (key) {
58376
- smritiApiKey = key;
58377
- logger.info("smriti_master_key_loaded", { path: keyPath });
58378
- }
59558
+ // apps/gateway/dist/gateway/summary-llm.js
59559
+ function buildOutboundPromptText4(messages) {
59560
+ return messages.map((message) => `${message.role}: ${message.content}`).join("\n");
59561
+ }
59562
+ function createSummaryLLM(getProvider, getModel, logger) {
59563
+ return {
59564
+ async generate(prompt) {
59565
+ const provider = getProvider();
59566
+ if (!provider) {
59567
+ logger?.warn("summary_llm_no_provider");
59568
+ throw new Error("No provider available for summarization");
59569
+ }
59570
+ const model = getModel() ?? "gpt-4o-mini";
59571
+ try {
59572
+ const messages = [
59573
+ {
59574
+ role: "system",
59575
+ content: "You are a concise summarizer. Generate clear, factual summaries that preserve important details."
59576
+ },
59577
+ {
59578
+ role: "user",
59579
+ content: prompt
58379
59580
  }
58380
- }
59581
+ ];
59582
+ const requestMeta = buildChatMeta({
59583
+ purpose: "summary",
59584
+ providerId: provider.id,
59585
+ model,
59586
+ normalization: buildNormalizationOutboundMeta(normalizeIncomingText(buildOutboundPromptText4(messages)))
59587
+ });
59588
+ const response = await chatWithPolicy({
59589
+ provider,
59590
+ request: buildChatRequest({
59591
+ model,
59592
+ messages,
59593
+ temperature: 0.3,
59594
+ maxTokens: 1024,
59595
+ metadata: requestMeta
59596
+ }),
59597
+ logger,
59598
+ meta: requestMeta
59599
+ });
59600
+ logger?.info("summary_llm_generated", {
59601
+ provider: provider.id,
59602
+ model,
59603
+ inputLength: prompt.length,
59604
+ outputLength: response.content.length
59605
+ });
59606
+ return response.content;
59607
+ } catch (error) {
59608
+ const message = error instanceof Error ? error.message : String(error);
59609
+ logger?.error("summary_llm_error", {
59610
+ error: message,
59611
+ provider: provider.id,
59612
+ model
59613
+ });
59614
+ throw error;
58381
59615
  }
58382
- } catch (error) {
58383
- logger.warn("smriti_master_key_load_failed", {
58384
- error: error instanceof Error ? error.message : String(error)
58385
- });
58386
59616
  }
58387
- }
58388
- const smritiPrefix = smritiConfig.prefix;
58389
- const smritiOwner = smritiConfig.owner;
58390
- const smritiAutoCapture = smritiConfig.autoCapture ?? false;
58391
- const smritiAutoCaptureMode = smritiConfig.autoCaptureMode ?? "exchange";
58392
- const smritiRetrieveEnabled = smritiConfig.retrieveEnabled ?? false;
58393
- const smritiRetrieveLimit = smritiConfig.retrieveLimit ?? 5;
58394
- const smritiDefaultTags = smritiConfig.defaultTags ?? void 0;
58395
- const smritiSource = smritiConfig.source;
58396
- const smritiAutoMemoryEnabled = smritiConfig.autoMemoryEnabled ?? false;
58397
- const smritiAutoMemoryPython = smritiConfig.autoMemoryPython ?? "python3";
58398
- const smritiAutoMemoryFallbackRoot = path34.resolve(repoRoot, "skills-core", "smriti-auto-memory", "hooks", "scripts");
58399
- const smritiAutoMemoryRootCandidates = [
58400
- smritiAutoMemoryFallbackRoot,
58401
- path34.resolve(repoRoot, "..", "ecosystem", "skills", "smriti-auto-memory", "hooks", "scripts"),
58402
- path34.resolve(repoRoot, "skills", "smriti-auto-memory", "hooks", "scripts")
58403
- ];
58404
- const smritiAutoMemoryDefaultRoot = smritiAutoMemoryRootCandidates.find((candidate) => fs36.existsSync(candidate)) ?? smritiAutoMemoryFallbackRoot;
58405
- const smritiAutoMemoryRoot = smritiConfig.autoMemoryPath ?? smritiAutoMemoryDefaultRoot;
58406
- const smritiAutoMemorySessionScript = path34.join(smritiAutoMemoryRoot, "session-start.py");
58407
- const smritiAutoMemoryPromptScript = path34.join(smritiAutoMemoryRoot, "user-prompt.py");
58408
- const smritiAutoMemoryReady = smritiEnabled && smritiAutoMemoryEnabled && Boolean(smritiApiKey) && fs36.existsSync(smritiAutoMemorySessionScript) && fs36.existsSync(smritiAutoMemoryPromptScript);
58409
- const smritiClientOptions = smritiEnabled && smritiBaseUrl ? {
58410
- baseUrl: smritiBaseUrl,
58411
- apiKey: smritiApiKey,
58412
- timeoutMs: smritiConfig.timeoutMs,
58413
- prefix: smritiPrefix ? smritiPrefix.replace(/\/$/, "") : smritiOwner ? `user/${normalizeSmritiOwner(smritiOwner)}` : void 0
58414
- } : void 0;
58415
- return {
58416
- smritiConfig,
58417
- smritiEnabled,
58418
- smritiBaseUrl,
58419
- smritiApiKey,
58420
- smritiPrefix,
58421
- smritiOwner,
58422
- smritiAutoCapture,
58423
- smritiAutoCaptureMode,
58424
- smritiRetrieveEnabled,
58425
- smritiRetrieveLimit,
58426
- smritiDefaultTags,
58427
- smritiSource,
58428
- smritiAutoMemoryEnabled,
58429
- smritiAutoMemoryPython,
58430
- smritiAutoMemoryRoot,
58431
- smritiAutoMemorySessionScript,
58432
- smritiAutoMemoryPromptScript,
58433
- smritiAutoMemoryReady,
58434
- smritiClientOptions,
58435
- smritiVectorProvider: smritiLocalConfig?.vector?.provider,
58436
- smritiVectorFallbacks: smritiLocalConfig?.vector?.fallbackProviders,
58437
- smritiEmbeddingProviders: smritiLocalConfig?.embeddings?.providerPriority
58438
59617
  };
58439
59618
  }
58440
59619
 
58441
- // apps/gateway/dist/gateway/identity-loader.js
58442
- import fs37 from "node:fs";
58443
- import path35 from "node:path";
58444
- var MAX_PROMPT_CHARS_DEFAULT = 12e3;
58445
- function uniqueStrings(values) {
58446
- const seen = /* @__PURE__ */ new Set();
58447
- const out = [];
58448
- for (const value of values) {
58449
- if (!value)
58450
- continue;
58451
- if (seen.has(value))
58452
- continue;
58453
- seen.add(value);
58454
- out.push(value);
59620
+ // apps/gateway/dist/gateway/setup-core.js
59621
+ var HARD_DEPRECATED_MODELS = {
59622
+ anthropic: /* @__PURE__ */ new Set([
59623
+ "claude-sonnet-4-20250514",
59624
+ "claude-opus-4-5",
59625
+ "claude-opus-4-5-20251101",
59626
+ "claude-3-5-haiku-20241022"
59627
+ ])
59628
+ };
59629
+ var KOSHA_DISCOVERY_REFRESH_MS = 5 * 60 * 1e3;
59630
+ var KOSHA_PATH_ENV = "KOSHA_DISCOVERY_PATH";
59631
+ var koshaState = {};
59632
+ async function resolveKoshaRegistry(refresh) {
59633
+ const now = Date.now();
59634
+ if (!refresh && koshaState.registry && koshaState.resolvedAt) {
59635
+ if (now - koshaState.resolvedAt < KOSHA_DISCOVERY_REFRESH_MS) {
59636
+ return {
59637
+ registry: koshaState.registry,
59638
+ source: "cached"
59639
+ };
59640
+ }
58455
59641
  }
58456
- return out;
58457
- }
58458
- function buildCandidateRoots(repoRoot) {
58459
- const roots = [repoRoot];
58460
- let current = repoRoot;
58461
- for (let i = 0; i < 3; i += 1) {
58462
- const parent = path35.dirname(current);
58463
- if (!parent || parent === current)
58464
- break;
58465
- roots.push(parent);
58466
- current = parent;
59642
+ if (koshaState.loadingPromise) {
59643
+ return koshaState.loadingPromise;
58467
59644
  }
58468
- return uniqueStrings(roots);
58469
- }
58470
- function readFirstExisting(roots, filenames) {
58471
- for (const root of roots) {
58472
- for (const name of filenames) {
58473
- const candidate = path35.join(root, name);
59645
+ const loadRegistry = async () => {
59646
+ const importTargets = [];
59647
+ const envPath = process.env[KOSHA_PATH_ENV];
59648
+ if (envPath) {
59649
+ importTargets.push(envPath);
59650
+ if (envPath.endsWith("dist/index.js") === false) {
59651
+ importTargets.push(path37.join(envPath, "dist", "index.js"));
59652
+ }
59653
+ }
59654
+ const localCandidate = path37.resolve(process.cwd(), "..", "kosha-discovery", "dist", "index.js");
59655
+ importTargets.push(localCandidate);
59656
+ importTargets.push("kosha-discovery");
59657
+ let lastFailure;
59658
+ let lastFailureTarget;
59659
+ for (const rawTarget of importTargets) {
59660
+ const target = fs39.existsSync(rawTarget) ? pathToFileURL(path37.resolve(rawTarget)).href : rawTarget;
59661
+ const source = rawTarget;
58474
59662
  try {
58475
- if (!fs37.existsSync(candidate))
59663
+ const mod = await import(target);
59664
+ if (!mod?.createKosha) {
59665
+ lastFailure = `Kosha module missing createKosha export: ${source}`;
59666
+ lastFailureTarget = source;
58476
59667
  continue;
58477
- const text = fs37.readFileSync(candidate, "utf8").trim();
58478
- if (!text)
59668
+ }
59669
+ const registry = await mod.createKosha();
59670
+ if (!registry?.models || typeof registry.models !== "function") {
59671
+ lastFailure = `Invalid Kosha registry shape from ${source}`;
59672
+ lastFailureTarget = source;
58479
59673
  continue;
58480
- return { file: candidate, text };
58481
- } catch {
59674
+ }
59675
+ return { registry, source };
59676
+ } catch (error) {
59677
+ lastFailure = error instanceof Error ? error.message : String(error);
59678
+ lastFailureTarget = source;
58482
59679
  }
58483
59680
  }
59681
+ return {
59682
+ error: lastFailure ?? "kosha discovery unavailable",
59683
+ source: lastFailureTarget
59684
+ };
59685
+ };
59686
+ const start = Date.now();
59687
+ const inFlight = loadRegistry().finally(() => {
59688
+ koshaState.loadingPromise = void 0;
59689
+ });
59690
+ koshaState.loadingPromise = inFlight;
59691
+ const resolution = await inFlight;
59692
+ if (!resolution.registry) {
59693
+ const elapsedMs = Date.now() - start;
59694
+ return {
59695
+ ...resolution,
59696
+ error: `${resolution.error} (refresh_ms=${elapsedMs})`
59697
+ };
58484
59698
  }
58485
- return null;
59699
+ if (resolution.source === void 0) {
59700
+ resolution.source = "resolved";
59701
+ }
59702
+ koshaState.registry = resolution.registry;
59703
+ koshaState.resolvedAt = Date.now();
59704
+ return {
59705
+ ...resolution,
59706
+ source: resolution.source
59707
+ };
58486
59708
  }
58487
- function extractIdentityFields(identity, user) {
58488
- const fields = {};
58489
- if (identity) {
58490
- const nameMatch = identity.match(/(?:^|\n)\s*(?:-\s*)?(?:\*\*)?Name(?:\*\*)?:\s*(?:\*\*)?(.+?)(?:\*\*)?$/im);
58491
- if (nameMatch?.[1]) {
58492
- const raw = nameMatch[1].trim().replace(/^_\(?|_?\)?$/g, "");
58493
- if (raw && !raw.startsWith("(") && raw.length < 64) {
58494
- fields.assistantName = raw;
58495
- }
58496
- }
58497
- const emojiMatch = identity.match(/(?:^|\n)\s*(?:-\s*)?(?:\*\*)?Emoji(?:\*\*)?:\s*(?:\*\*)?(.+?)(?:\*\*)?$/im);
58498
- if (emojiMatch?.[1]) {
58499
- const raw = emojiMatch[1].trim().replace(/^_\(?|_?\)?$/g, "");
58500
- if (raw && !raw.startsWith("(") && raw.length < 8) {
58501
- fields.emoji = raw;
58502
- }
59709
+ function resolveProviderEnvApiKey(envKeys) {
59710
+ for (const key of envKeys) {
59711
+ const value = process.env[key];
59712
+ if (value?.trim()) {
59713
+ return value.trim();
58503
59714
  }
58504
59715
  }
58505
- if (user) {
58506
- if (!fields.userName) {
58507
- const callMatch = user.match(/(?:^|\n)\s*(?:What to call them|Call them|User name|userName):\s*(.+?)$/im);
58508
- if (callMatch?.[1]) {
58509
- const raw = callMatch[1].trim().split(/\s*[/,]\s*/)[0];
58510
- if (raw && raw.length < 64) {
58511
- fields.userName = raw;
58512
- }
58513
- }
59716
+ return void 0;
59717
+ }
59718
+ function enrichProvidersWithEnvDefaults(baseEntries) {
59719
+ const resolvedEntries = { ...baseEntries };
59720
+ const fallbackSpecs = [
59721
+ {
59722
+ providerId: "google",
59723
+ type: "google",
59724
+ defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta",
59725
+ envKeys: ["GOOGLE_API_KEY", "GEMINI_API_KEY", "VAAYU_GOOGLE_API_KEY", "VAAYU_PROVIDER_GOOGLE_API_KEY"]
59726
+ },
59727
+ {
59728
+ providerId: "openai",
59729
+ type: "openai-compatible",
59730
+ defaultBaseUrl: "https://api.openai.com/v1",
59731
+ envKeys: ["OPENAI_API_KEY", "VAAYU_OPENAI_API_KEY", "VAAYU_PROVIDER_OPENAI_API_KEY", "VAAYU_EMBEDDING_API_KEY"]
58514
59732
  }
58515
- if (!fields.assistantName) {
58516
- const assistantCallMatch = user.match(/(?:^|\n)\s*(?:What to call assistant|Assistant name|assistantName):\s*(.+?)$/im);
58517
- if (assistantCallMatch?.[1]) {
58518
- const raw = assistantCallMatch[1].trim();
58519
- if (raw && raw.length < 64) {
58520
- fields.assistantName = raw;
58521
- }
58522
- }
59733
+ ];
59734
+ for (const spec of fallbackSpecs) {
59735
+ const existing = resolvedEntries[spec.providerId];
59736
+ const apiKey = resolveProviderEnvApiKey(spec.envKeys);
59737
+ const baseUrl = (() => {
59738
+ const explicit = process.env[`VAAYU_PROVIDER_${spec.providerId.toUpperCase()}_BASE_URL`];
59739
+ return existing?.baseUrl || explicit || spec.defaultBaseUrl;
59740
+ })();
59741
+ if (!existing && !apiKey) {
59742
+ continue;
58523
59743
  }
59744
+ resolvedEntries[spec.providerId] = {
59745
+ ...existing,
59746
+ type: existing?.type || spec.type,
59747
+ baseUrl: existing?.baseUrl || baseUrl,
59748
+ apiKey: existing?.apiKey || apiKey
59749
+ };
58524
59750
  }
58525
- return fields;
59751
+ return resolvedEntries;
58526
59752
  }
58527
- function truncatePrompt(text, maxChars) {
58528
- if (text.length <= maxChars)
58529
- return text;
58530
- return `${text.slice(0, maxChars)}
58531
-
58532
- [...identity truncated]`;
58533
- }
58534
- function loadWorkspaceIdentityPrompt(params) {
58535
- const roots = buildCandidateRoots(params.repoRoot);
58536
- const maxChars = params.maxPromptChars ?? MAX_PROMPT_CHARS_DEFAULT;
58537
- const filesLoaded = [];
58538
- const parts = [];
58539
- const soul = readFirstExisting(roots, ["SOUL.md", "soul.md"]);
58540
- if (soul) {
58541
- filesLoaded.push(soul.file);
58542
- parts.push(soul.text);
58543
- }
58544
- const identity = readFirstExisting(roots, ["IDENTITY.md", "identity.md"]);
58545
- if (identity) {
58546
- filesLoaded.push(identity.file);
58547
- parts.push(identity.text);
58548
- }
58549
- const personality = readFirstExisting(roots, ["PERSONALITY.md", "personality.md"]);
58550
- if (personality) {
58551
- filesLoaded.push(personality.file);
58552
- parts.push(personality.text);
58553
- }
58554
- const user = readFirstExisting(roots, ["USER.md", "user.md"]);
58555
- if (user) {
58556
- filesLoaded.push(user.file);
58557
- parts.push(user.text);
59753
+ async function listProviderModelsFromKosha(providerId, refresh, overrideLayers) {
59754
+ const resolution = await resolveKoshaRegistry(refresh);
59755
+ if (!resolution.registry) {
59756
+ return {
59757
+ models: [],
59758
+ source: resolution.source === "cached" ? "cached" : "fallback",
59759
+ error: resolution.error ?? "kosha_discovery_unavailable"
59760
+ };
58558
59761
  }
58559
- const fields = extractIdentityFields(identity?.text, user?.text);
58560
- const prompt = truncatePrompt(parts.filter(Boolean).join("\n\n"), maxChars).trim();
58561
- return { prompt, rootsChecked: roots, filesLoaded, fields };
58562
- }
58563
- function reloadIdentity(params) {
58564
- return loadWorkspaceIdentityPrompt(params);
58565
- }
58566
-
58567
- // apps/gateway/dist/gateway/config-path.js
58568
- import fs38 from "node:fs";
58569
- import path36 from "node:path";
58570
- import { fileURLToPath as fileURLToPath2 } from "node:url";
58571
- var GATEWAY_CONFIG_FILENAMES = [
58572
- "vaayu.config.json",
58573
- "vaayu.config.yaml",
58574
- "vaayu.config.yml"
58575
- ];
58576
- function resolvePreferredGatewayConfigPath(params) {
58577
- const envConfigPath = params?.envConfigPath ?? process.env.VAAYU_CONFIG;
58578
- if (envConfigPath?.trim()) {
58579
- return void 0;
59762
+ const registry = resolution.registry;
59763
+ const cards = /* @__PURE__ */ new Map();
59764
+ const direct = registry.models({
59765
+ provider: providerId,
59766
+ mode: "chat"
59767
+ });
59768
+ for (const card of direct) {
59769
+ const key = `${card.provider ?? providerId}:${card.id}`;
59770
+ cards.set(key, card);
58580
59771
  }
58581
- const existsSync3 = params?.existsSync ?? fs38.existsSync;
58582
- const moduleFilePath = params?.moduleFilePath ?? fileURLToPath2(import.meta.url);
58583
- let currentDir = path36.dirname(moduleFilePath);
58584
- for (let depth = 0; depth < 10; depth += 1) {
58585
- for (const filename of GATEWAY_CONFIG_FILENAMES) {
58586
- const candidate = path36.join(currentDir, filename);
58587
- if (existsSync3(candidate)) {
58588
- return candidate;
59772
+ if (!cards.size) {
59773
+ for (const card of registry.models({
59774
+ originProvider: providerId,
59775
+ mode: "chat"
59776
+ })) {
59777
+ const key = `${card.provider ?? providerId}:${card.id}`;
59778
+ if (!cards.has(key)) {
59779
+ cards.set(key, card);
58589
59780
  }
58590
59781
  }
58591
- const parent = path36.dirname(currentDir);
58592
- if (parent === currentDir)
58593
- break;
58594
- currentDir = parent;
58595
59782
  }
58596
- return void 0;
59783
+ if (!cards.size) {
59784
+ return {
59785
+ models: [],
59786
+ source: resolution.source === "cached" ? "cached" : "fallback",
59787
+ error: `No chat models found in kosha for provider "${providerId}"`
59788
+ };
59789
+ }
59790
+ const rawModels = Array.from(cards.values()).map((card) => card.id);
59791
+ return {
59792
+ models: applyModelOverrides(Array.from(new Set(rawModels)), overrideLayers),
59793
+ source: "kosha"
59794
+ };
58597
59795
  }
58598
-
58599
- // apps/gateway/dist/gateway/setup-core.js
58600
- var HARD_DEPRECATED_MODELS = {
58601
- anthropic: /* @__PURE__ */ new Set([
58602
- "claude-sonnet-4-20250514",
58603
- "claude-opus-4-5",
58604
- "claude-opus-4-5-20251101",
58605
- "claude-3-5-haiku-20241022"
58606
- ])
58607
- };
58608
59796
  function stripHardDeprecatedModels(providerId, models) {
58609
59797
  const blocked = HARD_DEPRECATED_MODELS[providerId];
58610
59798
  if (!blocked || blocked.size === 0)
@@ -58659,6 +59847,7 @@ async function setupGatewayCore() {
58659
59847
  }
58660
59848
  const state = { startedAt: Date.now(), httpRequestCount: 0 };
58661
59849
  const idempotency = new IdempotencyStore(12e4);
59850
+ config.providers.entries = enrichProvidersWithEnvDefaults(config.providers.entries);
58662
59851
  const providers = createRegistry(config.providers);
58663
59852
  if (providers.list().length === 0) {
58664
59853
  providers.register(new MockProvider({ id: "mock" }));
@@ -58766,6 +59955,21 @@ async function setupGatewayCore() {
58766
59955
  error = remote.error;
58767
59956
  }
58768
59957
  }
59958
+ if (!models.length) {
59959
+ const kosha = await listProviderModelsFromKosha(providerId, refresh, overrideLayers);
59960
+ if (kosha.models.length) {
59961
+ models = stripHardDeprecatedModels(providerId, applyModelOverrides(kosha.models, overrideLayers));
59962
+ modelSource = kosha.source;
59963
+ error ??= kosha.error;
59964
+ } else if (kosha.error) {
59965
+ error = error ? `${error}; ${kosha.error}` : kosha.error;
59966
+ logger.warn("model_list_kosha_unavailable", {
59967
+ providerId,
59968
+ reason: kosha.error,
59969
+ refresh
59970
+ });
59971
+ }
59972
+ }
58769
59973
  if (!models.length && cached2?.models?.length) {
58770
59974
  const cachedModels = stripHardDeprecatedModels(providerId, applyModelOverrides(cached2.models, overrideLayers));
58771
59975
  if (cachedModels.length) {
@@ -58893,7 +60097,7 @@ async function setupGatewayCore() {
58893
60097
  "proactiveAllowed",
58894
60098
  "proactiveAllowedChannels"
58895
60099
  ]) {
58896
- if (Object.prototype.hasOwnProperty.call(raw, key) && raw[key] === null) {
60100
+ if (Object.hasOwn(raw, key) && raw[key] === null) {
58897
60101
  cleared[key] = void 0;
58898
60102
  }
58899
60103
  }
@@ -60344,14 +61548,14 @@ async function bootstrapCliProviders(logger, providers) {
60344
61548
  }
60345
61549
  }
60346
61550
 
60347
- // packages/backup/src/manager.js
61551
+ // packages/backup/src/manager.ts
60348
61552
  import path42 from "node:path";
60349
61553
 
60350
- // packages/backup/src/storage.js
61554
+ // packages/backup/src/storage.ts
60351
61555
  import path39 from "node:path";
60352
61556
  import { spawnSync as spawnSync2 } from "node:child_process";
60353
61557
 
60354
- // packages/backup/src/utils.js
61558
+ // packages/backup/src/utils.ts
60355
61559
  import fs40 from "node:fs";
60356
61560
  import path38 from "node:path";
60357
61561
  import os11 from "node:os";
@@ -60382,8 +61586,7 @@ function createSnapshotId(date = /* @__PURE__ */ new Date()) {
60382
61586
  function readJson3(filePath, fallback) {
60383
61587
  try {
60384
61588
  const raw = fs40.readFileSync(filePath, "utf8");
60385
- if (!raw)
60386
- return fallback;
61589
+ if (!raw) return fallback;
60387
61590
  return JSON.parse(raw);
60388
61591
  } catch {
60389
61592
  return fallback;
@@ -60403,12 +61606,11 @@ function copyDirSafe(src, dest) {
60403
61606
  fs40.cpSync(src, dest, { recursive: true });
60404
61607
  }
60405
61608
  function deleteDirSafe(dir) {
60406
- if (!fs40.existsSync(dir))
60407
- return;
61609
+ if (!fs40.existsSync(dir)) return;
60408
61610
  fs40.rmSync(dir, { recursive: true, force: true });
60409
61611
  }
60410
61612
 
60411
- // packages/backup/src/storage.js
61613
+ // packages/backup/src/storage.ts
60412
61614
  async function backupStorage(params) {
60413
61615
  const { storage, snapshotDir, logger } = params;
60414
61616
  const driver = storage.driver;
@@ -60436,7 +61638,11 @@ async function backupStorage(params) {
60436
61638
  }
60437
61639
  if (driver === "postgres") {
60438
61640
  const dest = path39.join(storageDir, "postgres.dump");
60439
- const result = spawnSync2("pg_dump", ["--format=c", "--file", dest, storage.postgresUrl ?? ""], { stdio: "pipe" });
61641
+ const result = spawnSync2(
61642
+ "pg_dump",
61643
+ ["--format=c", "--file", dest, storage.postgresUrl ?? ""],
61644
+ { stdio: "pipe" }
61645
+ );
60440
61646
  if (result.status !== 0) {
60441
61647
  const error = result.stderr?.toString() || "pg_dump failed";
60442
61648
  logger.warn("backup_pg_dump_failed", { error });
@@ -60472,7 +61678,11 @@ async function restoreStorage(params) {
60472
61678
  }
60473
61679
  if (storage.driver === "postgres") {
60474
61680
  const dumpPath = path39.join(storageDir, "postgres.dump");
60475
- const result = spawnSync2("pg_restore", ["--clean", "--if-exists", "--dbname", storage.postgresUrl ?? "", dumpPath], { stdio: "pipe" });
61681
+ const result = spawnSync2(
61682
+ "pg_restore",
61683
+ ["--clean", "--if-exists", "--dbname", storage.postgresUrl ?? "", dumpPath],
61684
+ { stdio: "pipe" }
61685
+ );
60476
61686
  if (result.status !== 0) {
60477
61687
  const error = result.stderr?.toString() || "pg_restore failed";
60478
61688
  logger.warn("backup_pg_restore_failed", { error });
@@ -60486,15 +61696,14 @@ async function restoreStorage(params) {
60486
61696
  }
60487
61697
  }
60488
61698
 
60489
- // packages/backup/src/qdrant.js
61699
+ // packages/backup/src/qdrant.ts
60490
61700
  import fs41 from "node:fs";
60491
61701
  import path40 from "node:path";
60492
61702
  var DEFAULT_COLLECTION2 = "vaayu-smriti";
60493
61703
  var DEFAULT_TIMEOUT_MS3 = 8e3;
60494
61704
  function buildHeaders(apiKey) {
60495
61705
  const headers = { "content-type": "application/json" };
60496
- if (apiKey)
60497
- headers["api-key"] = apiKey;
61706
+ if (apiKey) headers["api-key"] = apiKey;
60498
61707
  return headers;
60499
61708
  }
60500
61709
  async function fetchJson3(url, init, timeoutMs) {
@@ -60520,7 +61729,11 @@ async function backupQdrant(params) {
60520
61729
  const collection = config.collection ?? DEFAULT_COLLECTION2;
60521
61730
  const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
60522
61731
  try {
60523
- const createResp = await fetchJson3(`${url}/collections/${collection}/snapshots`, { method: "POST", headers: buildHeaders(config.apiKey), body: "{}" }, timeoutMs);
61732
+ const createResp = await fetchJson3(
61733
+ `${url}/collections/${collection}/snapshots`,
61734
+ { method: "POST", headers: buildHeaders(config.apiKey), body: "{}" },
61735
+ timeoutMs
61736
+ );
60524
61737
  const snapshotName = createResp?.result?.name ?? createResp?.name;
60525
61738
  if (!snapshotName) {
60526
61739
  return { ok: false, error: "Missing snapshot name" };
@@ -60544,7 +61757,7 @@ async function backupQdrant(params) {
60544
61757
  }
60545
61758
  }
60546
61759
 
60547
- // packages/backup/src/manifest.js
61760
+ // packages/backup/src/manifest.ts
60548
61761
  import path41 from "node:path";
60549
61762
  var INDEX_NAME = "index.json";
60550
61763
  var MANIFEST_NAME = "manifest.json";
@@ -60572,10 +61785,9 @@ function saveBackupManifest(snapshotDir, manifest) {
60572
61785
  writeJson2(getManifestPath(snapshotDir), manifest);
60573
61786
  }
60574
61787
 
60575
- // packages/backup/src/manager.js
61788
+ // packages/backup/src/manager.ts
60576
61789
  var shouldRun = (lastAt, intervalMinutes) => {
60577
- if (!lastAt)
60578
- return true;
61790
+ if (!lastAt) return true;
60579
61791
  const elapsed = Date.now() - Date.parse(lastAt);
60580
61792
  return elapsed >= intervalMinutes * 6e4;
60581
61793
  };
@@ -60656,11 +61868,9 @@ function createBackupManager(params) {
60656
61868
  return restoreStorage({ storage, snapshotDir, logger });
60657
61869
  };
60658
61870
  const start = () => {
60659
- if (!config.enabled || timer)
60660
- return;
61871
+ if (!config.enabled || timer) return;
60661
61872
  timer = setInterval(() => {
60662
- if (!config.enabled)
60663
- return;
61873
+ if (!config.enabled) return;
60664
61874
  if (shouldRun(lastStorageRunAt, config.intervalMinutes)) {
60665
61875
  void run("scheduled").catch((error) => {
60666
61876
  logger.warn("backup_scheduled_run_failed", {
@@ -60678,8 +61888,7 @@ function createBackupManager(params) {
60678
61888
  }
60679
61889
  };
60680
61890
  const stop = () => {
60681
- if (!timer)
60682
- return;
61891
+ if (!timer) return;
60683
61892
  clearInterval(timer);
60684
61893
  timer = void 0;
60685
61894
  };
@@ -60704,8 +61913,7 @@ function updateIndex(localDir, manifest, keepLast, keepDays) {
60704
61913
  const next = [manifest, ...index.backups.filter((item) => item.id !== manifest.id)];
60705
61914
  const now = Date.now();
60706
61915
  const filtered = next.filter((item, idx) => {
60707
- if (keepLast && idx < keepLast)
60708
- return true;
61916
+ if (keepLast && idx < keepLast) return true;
60709
61917
  if (keepDays) {
60710
61918
  const created = Date.parse(item.startedAt);
60711
61919
  return now - created <= keepDays * 24 * 60 * 60 * 1e3;
@@ -60720,8 +61928,8 @@ function updateIndex(localDir, manifest, keepLast, keepDays) {
60720
61928
  }
60721
61929
 
60722
61930
  // apps/gateway/dist/providers/health.js
60723
- import { spawn as spawn5 } from "node:child_process";
60724
- import { existsSync as existsSync2 } from "node:fs";
61931
+ import { spawn as spawn6 } from "node:child_process";
61932
+ import { existsSync as existsSync3 } from "node:fs";
60725
61933
  import net from "node:net";
60726
61934
  var DEFAULT_OLLAMA_URL = "http://127.0.0.1:11434";
60727
61935
  var parseModel = (model) => {
@@ -60741,8 +61949,8 @@ var matchesRequired = (required, installed) => {
60741
61949
  }
60742
61950
  return required.base === installed.base;
60743
61951
  };
60744
- var wait = (ms) => new Promise((resolve) => {
60745
- setTimeout(resolve, ms);
61952
+ var wait = (ms) => new Promise((resolve3) => {
61953
+ setTimeout(resolve3, ms);
60746
61954
  });
60747
61955
  var normalizeBaseUrl5 = (value) => {
60748
61956
  if (!value)
@@ -60835,7 +62043,7 @@ var resolveOllamaBinary2 = () => {
60835
62043
  return process.env.OLLAMA_PATH;
60836
62044
  const candidates = ["/opt/homebrew/bin/ollama", "/usr/local/bin/ollama", "/usr/bin/ollama"];
60837
62045
  for (const candidate of candidates) {
60838
- if (existsSync2(candidate))
62046
+ if (existsSync3(candidate))
60839
62047
  return candidate;
60840
62048
  }
60841
62049
  return "ollama";
@@ -60864,24 +62072,24 @@ var isTcpListening = async (baseUrl) => {
60864
62072
  }
60865
62073
  if (!host || !Number.isFinite(port) || port <= 0)
60866
62074
  return false;
60867
- return await new Promise((resolve) => {
62075
+ return await new Promise((resolve3) => {
60868
62076
  const socket = net.connect({ host, port });
60869
62077
  const timeout = setTimeout(() => {
60870
62078
  socket.destroy();
60871
- resolve(false);
62079
+ resolve3(false);
60872
62080
  }, 350);
60873
62081
  socket.once("connect", () => {
60874
62082
  clearTimeout(timeout);
60875
62083
  socket.destroy();
60876
- resolve(true);
62084
+ resolve3(true);
60877
62085
  });
60878
62086
  socket.once("error", () => {
60879
62087
  clearTimeout(timeout);
60880
- resolve(false);
62088
+ resolve3(false);
60881
62089
  });
60882
62090
  });
60883
62091
  };
60884
- var tryStartOllama = async (logger, params) => new Promise((resolve) => {
62092
+ var tryStartOllama = async (logger, params) => new Promise((resolve3) => {
60885
62093
  try {
60886
62094
  const bin = resolveOllamaBinary2();
60887
62095
  const env = { ...process.env };
@@ -60892,7 +62100,7 @@ var tryStartOllama = async (logger, params) => new Promise((resolve) => {
60892
62100
  if (params?.keepAlive) {
60893
62101
  env.OLLAMA_KEEP_ALIVE = params.keepAlive;
60894
62102
  }
60895
- const proc = spawn5(bin, ["serve"], {
62103
+ const proc = spawn6(bin, ["serve"], {
60896
62104
  detached: true,
60897
62105
  stdio: "ignore",
60898
62106
  env
@@ -60908,7 +62116,7 @@ var tryStartOllama = async (logger, params) => new Promise((resolve) => {
60908
62116
  host,
60909
62117
  keepAlive: params?.keepAlive
60910
62118
  });
60911
- resolve(false);
62119
+ resolve3(false);
60912
62120
  });
60913
62121
  proc.once("exit", (code, signal) => {
60914
62122
  if (settled)
@@ -60921,7 +62129,7 @@ var tryStartOllama = async (logger, params) => new Promise((resolve) => {
60921
62129
  host,
60922
62130
  keepAlive: params?.keepAlive
60923
62131
  });
60924
- resolve(false);
62132
+ resolve3(false);
60925
62133
  });
60926
62134
  setTimeout(() => {
60927
62135
  if (settled)
@@ -60932,7 +62140,7 @@ var tryStartOllama = async (logger, params) => new Promise((resolve) => {
60932
62140
  host,
60933
62141
  keepAlive: params?.keepAlive
60934
62142
  });
60935
- resolve(true);
62143
+ resolve3(true);
60936
62144
  }, 300);
60937
62145
  } catch (error) {
60938
62146
  logger.warn("ollama_autostart_failed", {
@@ -60941,7 +62149,7 @@ var tryStartOllama = async (logger, params) => new Promise((resolve) => {
60941
62149
  host: resolveOllamaHost(params?.baseUrl),
60942
62150
  keepAlive: params?.keepAlive
60943
62151
  });
60944
- resolve(false);
62152
+ resolve3(false);
60945
62153
  }
60946
62154
  });
60947
62155
  async function fetchOllamaModels(entry, providerId, logger) {
@@ -61667,25 +62875,108 @@ function acquireGatewayInstanceLock(params) {
61667
62875
  throw new Error(`Gateway instance lock unavailable: ${lockPath}`);
61668
62876
  }
61669
62877
 
62878
+ // apps/gateway/dist/chitragupta-bridge/bridge.js
62879
+ import { dirname as dirname2, extname, resolve as resolve2 } from "node:path";
62880
+
62881
+ // apps/gateway/dist/chitragupta-bridge/turiya-helpers.js
62882
+ async function classifyTuriya(baseUrls, timeoutMs, params) {
62883
+ const text = params.text?.trim();
62884
+ if (!text)
62885
+ return null;
62886
+ for (const baseUrl of baseUrls) {
62887
+ const controller = new AbortController();
62888
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
62889
+ try {
62890
+ const response = await fetch(`${baseUrl}/api/turiya/classify`, {
62891
+ method: "POST",
62892
+ headers: { "content-type": "application/json" },
62893
+ body: JSON.stringify({ text, messageCount: params.messageCount, hasTools: params.hasTools, memoryHits: params.memoryHits }),
62894
+ signal: controller.signal
62895
+ });
62896
+ if (!response.ok)
62897
+ continue;
62898
+ const payload = await response.json();
62899
+ if (typeof payload.tier !== "string")
62900
+ continue;
62901
+ return {
62902
+ tier: payload.tier,
62903
+ confidence: typeof payload.confidence === "number" ? payload.confidence : 0.5,
62904
+ costEstimate: typeof payload.costEstimate === "number" ? payload.costEstimate : 0
62905
+ };
62906
+ } catch {
62907
+ } finally {
62908
+ clearTimeout(timeout);
62909
+ }
62910
+ }
62911
+ return null;
62912
+ }
62913
+ async function recordTuriyaOutcome(baseUrls, timeoutMs, params) {
62914
+ for (const baseUrl of baseUrls) {
62915
+ const controller = new AbortController();
62916
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
62917
+ try {
62918
+ await fetch(`${baseUrl}/api/turiya/outcome`, {
62919
+ method: "POST",
62920
+ headers: { "content-type": "application/json" },
62921
+ body: JSON.stringify(params),
62922
+ signal: controller.signal
62923
+ });
62924
+ return;
62925
+ } catch {
62926
+ } finally {
62927
+ clearTimeout(timeout);
62928
+ }
62929
+ }
62930
+ }
62931
+
61670
62932
  // apps/gateway/dist/chitragupta-bridge/bridge.js
61671
62933
  var CACHE_TTL_MS = 500;
62934
+ var DEFAULT_SKILL_LEARNER_STATE_FILE = "skill-learner-state.json";
61672
62935
  var ChitraguptaBridge = class _ChitraguptaBridge {
61673
62936
  orchestrator;
62937
+ vidyaBridge;
61674
62938
  logger;
61675
62939
  margaBaseUrl;
61676
62940
  margaTimeoutMs;
61677
62941
  skillsUsedPerSession = /* @__PURE__ */ new Map();
61678
62942
  shiksha;
61679
62943
  crystallizer;
62944
+ skillResolver;
62945
+ skillLearner;
62946
+ skillLearnerStatePath;
62947
+ loggedShikshaUnavailable = false;
62948
+ loggedCrystallizerUnavailable = false;
61680
62949
  recCache = null;
61681
62950
  recCacheQuery = "";
61682
- constructor(orchestrator, logger, margaBaseUrl, margaTimeoutMs, shiksha = null, crystallizer = null) {
62951
+ constructor(orchestrator, vidyaBridge, logger, margaBaseUrl, margaTimeoutMs, shiksha = null, crystallizer = null, skillResolver, skillLearner, skillLearnerStatePath) {
61683
62952
  this.orchestrator = orchestrator;
62953
+ this.vidyaBridge = vidyaBridge;
61684
62954
  this.logger = logger;
61685
62955
  this.margaBaseUrl = margaBaseUrl.replace(/\/+$/, "");
61686
62956
  this.margaTimeoutMs = margaTimeoutMs;
61687
62957
  this.shiksha = shiksha;
61688
62958
  this.crystallizer = crystallizer;
62959
+ this.skillResolver = skillResolver ?? new SkillResolver({
62960
+ logger: { info: (m, d) => logger.info(m, d), warn: (m, d) => logger.warn(m, d) }
62961
+ });
62962
+ this.skillLearner = skillLearner ?? new SkillLearner({
62963
+ logger: { info: (m, d) => logger.info(m, d), warn: (m, d) => logger.warn(m, d) }
62964
+ });
62965
+ this.skillLearnerStatePath = skillLearnerStatePath ?? _ChitraguptaBridge.resolveSkillLearnerStatePath();
62966
+ }
62967
+ static resolveSkillLearnerStatePath(configPersistPath) {
62968
+ const explicitPath = process.env.VAAYU_SKILL_LEARNER_STATE_PATH?.trim();
62969
+ if (explicitPath)
62970
+ return resolve2(explicitPath);
62971
+ const configuredBase = configPersistPath?.trim();
62972
+ if (configuredBase) {
62973
+ const baseDir = extname(configuredBase) ? dirname2(configuredBase) : configuredBase;
62974
+ return resolve2(baseDir, DEFAULT_SKILL_LEARNER_STATE_FILE);
62975
+ }
62976
+ const home = process.env.CHITRAGUPTA_HOME?.trim();
62977
+ if (home)
62978
+ return resolve2(home, DEFAULT_SKILL_LEARNER_STATE_FILE);
62979
+ return resolve2(process.cwd(), ".chitragupta", DEFAULT_SKILL_LEARNER_STATE_FILE);
61689
62980
  }
61690
62981
  /* ── Factory ───────────────────────────────────────────────────── */
61691
62982
  /** Create a bridge instance. Returns null if Chitragupta is unavailable. */
@@ -61705,24 +62996,29 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61705
62996
  }
61706
62997
  if (!vidya)
61707
62998
  throw lastError ?? new Error("Chitragupta skill runtime not found");
61708
- const registry = new vidya.SkillRegistry();
61709
- const bridge = new vidya.VidyaBridge(registry);
62999
+ const Ctor = vidya;
63000
+ const registry = new Ctor.SkillRegistry();
63001
+ const bridge = new Ctor.VidyaBridge(registry);
61710
63002
  const autoLearn = config.enableAutoLearn ?? false;
61711
63003
  let shiksha = null;
61712
- if (autoLearn && vidya.ShikshaController) {
63004
+ if (autoLearn && Ctor.ShikshaController) {
61713
63005
  try {
61714
- shiksha = new vidya.ShikshaController({ registry });
63006
+ shiksha = new Ctor.ShikshaController({ registry });
61715
63007
  } catch {
61716
63008
  }
61717
63009
  }
63010
+ if (autoLearn && !shiksha)
63011
+ logger.warn("chitragupta_shiksha_unavailable", { autoLearn });
61718
63012
  let crystallizer = null;
61719
- if (vidya.SkillCrystallizer) {
63013
+ if (Ctor.SkillCrystallizer) {
61720
63014
  try {
61721
- crystallizer = new vidya.SkillCrystallizer();
63015
+ crystallizer = new Ctor.SkillCrystallizer();
61722
63016
  } catch {
61723
63017
  }
61724
63018
  }
61725
- const orchestrator = new vidya.VidyaOrchestrator({ registry, bridge }, { persistPath: config.persistPath, enableAutoLearn: autoLearn, enableAutoComposition: true });
63019
+ if (!crystallizer)
63020
+ logger.warn("chitragupta_crystallizer_unavailable");
63021
+ const orchestrator = new Ctor.VidyaOrchestrator({ registry, bridge }, { persistPath: config.persistPath, enableAutoLearn: autoLearn, enableAutoComposition: true });
61726
63022
  await orchestrator.initialize();
61727
63023
  logger.info("chitragupta_bridge_initialized", {
61728
63024
  initialized: orchestrator.isInitialized,
@@ -61733,14 +63029,17 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61733
63029
  const margaBaseUrl = process.env.VAAYU_CHITRAGUPTA_BASE_URL?.trim() || "http://localhost:3141";
61734
63030
  const timeoutFromEnv = Number.parseInt(process.env.VAAYU_CHITRAGUPTA_MARGA_TIMEOUT_MS ?? "", 10);
61735
63031
  const margaTimeoutMs = Number.isFinite(timeoutFromEnv) && timeoutFromEnv > 0 ? timeoutFromEnv : 150;
61736
- return new _ChitraguptaBridge(orchestrator, logger, margaBaseUrl, margaTimeoutMs, shiksha, crystallizer);
63032
+ const logAdapter = { info: (m, d) => logger.info(m, d), warn: (m, d) => logger.warn(m, d) };
63033
+ const skillResolver = new SkillResolver({ logger: logAdapter });
63034
+ const skillLearnerStatePath = _ChitraguptaBridge.resolveSkillLearnerStatePath(config.persistPath);
63035
+ const skillLearner = await SkillLearner.load(skillLearnerStatePath, { logger: logAdapter }) ?? new SkillLearner({ logger: logAdapter });
63036
+ return new _ChitraguptaBridge(orchestrator, bridge, logger, margaBaseUrl, margaTimeoutMs, shiksha, crystallizer, skillResolver, skillLearner, skillLearnerStatePath);
61737
63037
  } catch (err) {
61738
- logger.debug("chitragupta_bridge_unavailable", {
61739
- reason: err instanceof Error ? err.message : String(err)
61740
- });
63038
+ logger.debug("chitragupta_bridge_unavailable", { reason: err instanceof Error ? err.message : String(err) });
61741
63039
  return null;
61742
63040
  }
61743
63041
  }
63042
+ /** Get Marga base URL candidates for HTTP calls. */
61744
63043
  getMargaBaseUrlCandidates() {
61745
63044
  const c = this.margaBaseUrl.replace(/\/+$/, "");
61746
63045
  const r = [c];
@@ -61750,6 +63049,7 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61750
63049
  r.push("http://127.0.0.1:3141");
61751
63050
  return r;
61752
63051
  }
63052
+ /* ── Marga Routing ────────────────────────────────────────────── */
61753
63053
  /** Query Chitragupta's stateless routing endpoint. Returns null when unavailable. */
61754
63054
  async decideRoute(params) {
61755
63055
  const message = params.message?.trim();
@@ -61774,10 +63074,6 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61774
63074
  });
61775
63075
  if (!response.ok) {
61776
63076
  lastError = `HTTP ${response.status}`;
61777
- this.logger.debug("chitragupta_marga_http_error", {
61778
- status: response.status,
61779
- baseUrl
61780
- });
61781
63077
  continue;
61782
63078
  }
61783
63079
  const payload = await response.json();
@@ -61785,17 +63081,8 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61785
63081
  const modelId = typeof payload.modelId === "string" ? payload.modelId.trim() : "";
61786
63082
  if (!providerId || !modelId) {
61787
63083
  lastError = "invalid_payload";
61788
- this.logger.debug("chitragupta_marga_invalid_payload", {
61789
- baseUrl
61790
- });
61791
63084
  continue;
61792
63085
  }
61793
- if (baseUrl !== this.margaBaseUrl) {
61794
- this.logger.debug("chitragupta_marga_baseurl_fallback_used", {
61795
- configuredBaseUrl: this.margaBaseUrl,
61796
- resolvedBaseUrl: baseUrl
61797
- });
61798
- }
61799
63086
  return {
61800
63087
  providerId,
61801
63088
  modelId,
@@ -61811,12 +63098,28 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61811
63098
  clearTimeout(timeout);
61812
63099
  }
61813
63100
  }
61814
- this.logger.debug("chitragupta_marga_unavailable", {
61815
- reason: lastError,
61816
- baseUrl: this.margaBaseUrl
61817
- });
63101
+ this.logger.debug("chitragupta_marga_unavailable", { reason: lastError, baseUrl: this.margaBaseUrl });
61818
63102
  return null;
61819
63103
  }
63104
+ /* ── Tool Registration ────────────────────────────────────────── */
63105
+ /** Register gateway tools with Vidya. Safe no-op when unavailable. */
63106
+ registerTools(tools) {
63107
+ if (!tools.length)
63108
+ return 0;
63109
+ const fn = this.vidyaBridge?.registerToolsAsSkills;
63110
+ if (typeof fn !== "function")
63111
+ return 0;
63112
+ try {
63113
+ const normalized = tools.map((t2) => ({ name: t2.name, description: t2.description ?? "", inputSchema: t2.inputSchema, tags: t2.tags, aliases: t2.aliases })).filter((t2) => t2.name.trim().length > 0);
63114
+ if (!normalized.length)
63115
+ return 0;
63116
+ fn.call(this.vidyaBridge, normalized);
63117
+ return normalized.length;
63118
+ } catch (error) {
63119
+ this.logger.debug("chitragupta_bridge_tool_registration_failed", { error: error instanceof Error ? error.message : String(error) });
63120
+ return 0;
63121
+ }
63122
+ }
61820
63123
  /* ── Recommend ─────────────────────────────────────────────────── */
61821
63124
  /** Return recommended skill/tool names for a query (cached per-turn). */
61822
63125
  recommend(query) {
@@ -61824,8 +63127,7 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61824
63127
  if (this.recCache && this.recCacheQuery === query && Date.now() - this.recCache.ts < CACHE_TTL_MS) {
61825
63128
  return this.recCache.names;
61826
63129
  }
61827
- const matches = this.orchestrator.recommend(query);
61828
- const names = matches.map((m) => m.manifest.name);
63130
+ const names = this.orchestrator.recommend(query).map((m) => m.manifest.name);
61829
63131
  this.recCache = { names, ts: Date.now() };
61830
63132
  this.recCacheQuery = query;
61831
63133
  return names;
@@ -61840,17 +63142,14 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61840
63142
  this.orchestrator.onSkillExecuted(toolName, success, latencyMs, sessionId);
61841
63143
  if (sessionId) {
61842
63144
  const list = this.skillsUsedPerSession.get(sessionId) ?? [];
61843
- if (!list.includes(toolName)) {
63145
+ if (!list.includes(toolName))
61844
63146
  list.push(toolName);
61845
- }
61846
63147
  this.skillsUsedPerSession.set(sessionId, list);
61847
63148
  }
61848
63149
  } catch {
61849
63150
  }
61850
63151
  }
61851
- /**
61852
- * Report when the LLM picks a different tool than Chitragupta recommended.
61853
- */
63152
+ /** Report when the LLM picks a different tool than Chitragupta recommended. */
61854
63153
  onSkillRejected(rejectedTool, chosenTool) {
61855
63154
  try {
61856
63155
  this.orchestrator.onSkillRejected(rejectedTool, chosenTool);
@@ -61858,113 +63157,138 @@ var ChitraguptaBridge = class _ChitraguptaBridge {
61858
63157
  }
61859
63158
  }
61860
63159
  /* ── Session lifecycle ─────────────────────────────────────────── */
61861
- /**
61862
- * Flush skill tracking data for a completed session.
61863
- */
63160
+ /** Flush skill tracking data for a completed session. */
61864
63161
  onSessionEnd(sessionId) {
61865
63162
  try {
61866
63163
  const skillsUsed = this.skillsUsedPerSession.get(sessionId) ?? [];
61867
63164
  this.orchestrator.onSessionEnd(sessionId, skillsUsed);
61868
63165
  this.skillsUsedPerSession.delete(sessionId);
63166
+ void this.skillLearner.flush(this.skillLearnerStatePath).catch((error) => {
63167
+ this.logger.warn("skill_learner_flush_failed", { error: error instanceof Error ? error.message : String(error) });
63168
+ });
61869
63169
  } catch {
61870
63170
  }
61871
63171
  }
61872
- /* ── Crystallization Pipeline (Gap 3) ─────────────────────────── */
63172
+ /* ── Crystallization Pipeline ─────────────────────────────────── */
61873
63173
  /** Run crystallization on mature Vidhis. Fire-and-forget safe. */
61874
63174
  tryCrystallize(vidhis) {
61875
63175
  try {
61876
- if (!this.crystallizer || vidhis.length === 0)
63176
+ if (vidhis.length === 0)
61877
63177
  return 0;
63178
+ if (!this.crystallizer) {
63179
+ if (!this.loggedCrystallizerUnavailable) {
63180
+ this.loggedCrystallizerUnavailable = true;
63181
+ this.logger.warn("chitragupta_crystallizer_unavailable");
63182
+ }
63183
+ return 0;
63184
+ }
61878
63185
  const results = this.crystallizer.crystallize([...vidhis]);
61879
63186
  const ok = results.filter((s) => s.status === "registered" || s.status === "approved");
61880
- if (ok.length > 0) {
63187
+ if (ok.length > 0)
61881
63188
  this.logger.info("chitragupta_crystallized_skills", { count: ok.length, names: ok.map((s) => s.skillName) });
61882
- }
61883
63189
  return ok.length;
61884
63190
  } catch {
61885
63191
  return 0;
61886
63192
  }
61887
63193
  }
61888
- /* ── Proactive Scan / Vasana (Gap 4) ──────────────────────────── */
63194
+ /* ── Proactive Scan / Vasana ──────────────────────────────────── */
61889
63195
  /** Scan Vasana tendencies for proactive skill learning. Fire-and-forget safe. */
61890
63196
  proactiveScan(vasanas) {
61891
63197
  try {
61892
- if (!this.shiksha || vasanas.length === 0)
63198
+ if (vasanas.length === 0)
61893
63199
  return [];
63200
+ if (!this.shiksha) {
63201
+ if (!this.loggedShikshaUnavailable) {
63202
+ this.loggedShikshaUnavailable = true;
63203
+ this.logger.warn("chitragupta_shiksha_unavailable", { autoLearn: false });
63204
+ }
63205
+ return [];
63206
+ }
61894
63207
  const suggestions = this.shiksha.proactiveScan(vasanas);
61895
- if (suggestions.length > 0) {
63208
+ if (suggestions.length > 0)
61896
63209
  this.logger.info("chitragupta_proactive_suggestions", { count: suggestions.length, topics: suggestions.slice(0, 5) });
61897
- }
61898
63210
  return suggestions;
61899
63211
  } catch {
61900
63212
  return [];
61901
63213
  }
61902
63214
  }
61903
- /* ── Turiya Bandit Routing ─────────────────────────────────────── */
63215
+ /* ── Turiya Bandit Routing (delegated) ────────────────────────── */
61904
63216
  /** Query Chitragupta's Turiya contextual bandit. Returns null when unavailable. */
61905
63217
  async classifyTuriya(params) {
61906
- const text = params.text?.trim();
61907
- if (!text)
63218
+ return classifyTuriya(this.getMargaBaseUrlCandidates(), this.margaTimeoutMs, params);
63219
+ }
63220
+ /** Send reward feedback to Chitragupta's Turiya router. Fire-and-forget. */
63221
+ async recordTuriyaOutcome(params) {
63222
+ return recordTuriyaOutcome(this.getMargaBaseUrlCandidates(), this.margaTimeoutMs, params);
63223
+ }
63224
+ /* ── Skill Resolution Pipeline ────────────────────────────────── */
63225
+ /** Search for a skill matching the given intent. Returns first safe candidate or null. */
63226
+ async resolveSkill(intent) {
63227
+ try {
63228
+ const candidates = await this.skillResolver.search(intent);
63229
+ for (const candidate of candidates) {
63230
+ const guardResult = this.skillResolver.guard(candidate);
63231
+ if (guardResult.safe)
63232
+ return candidate;
63233
+ this.logger.debug("skill_candidate_blocked", { name: candidate.name, reasons: guardResult.blockedReasons });
63234
+ }
61908
63235
  return null;
61909
- const baseUrls = this.getMargaBaseUrlCandidates();
61910
- for (const baseUrl of baseUrls) {
61911
- const controller = new AbortController();
61912
- const timeout = setTimeout(() => controller.abort(), this.margaTimeoutMs);
61913
- try {
61914
- const response = await fetch(`${baseUrl}/api/turiya/classify`, {
61915
- method: "POST",
61916
- headers: { "content-type": "application/json" },
61917
- body: JSON.stringify({
61918
- text,
61919
- messageCount: params.messageCount,
61920
- hasTools: params.hasTools,
61921
- memoryHits: params.memoryHits
61922
- }),
61923
- signal: controller.signal
63236
+ } catch {
63237
+ return null;
63238
+ }
63239
+ }
63240
+ /** Record a skill gap and auto-trigger search if threshold exceeded. */
63241
+ recordSkillGap(intent, sessionId) {
63242
+ try {
63243
+ this.skillLearner.recordGap(intent, sessionId);
63244
+ const actionable = this.skillLearner.getActionableGaps();
63245
+ if (actionable.length > 0) {
63246
+ this.logger.info("skill_gaps_actionable", { count: actionable.length, intents: actionable.map((g) => g.intent) });
63247
+ void this.autoResolveGaps(actionable).catch(() => {
61924
63248
  });
61925
- if (!response.ok)
61926
- continue;
61927
- const payload = await response.json();
61928
- if (typeof payload.tier !== "string")
61929
- continue;
61930
- return {
61931
- tier: payload.tier,
61932
- confidence: typeof payload.confidence === "number" ? payload.confidence : 0.5,
61933
- costEstimate: typeof payload.costEstimate === "number" ? payload.costEstimate : 0
61934
- };
61935
- } catch {
61936
- } finally {
61937
- clearTimeout(timeout);
61938
63249
  }
63250
+ } catch {
61939
63251
  }
61940
- return null;
61941
63252
  }
61942
- /** Send reward feedback to Chitragupta's Turiya router. Fire-and-forget. */
61943
- async recordTuriyaOutcome(params) {
61944
- const baseUrls = this.getMargaBaseUrlCandidates();
61945
- for (const baseUrl of baseUrls) {
61946
- const controller = new AbortController();
61947
- const timeout = setTimeout(() => controller.abort(), this.margaTimeoutMs);
61948
- try {
61949
- await fetch(`${baseUrl}/api/turiya/outcome`, {
61950
- method: "POST",
61951
- headers: { "content-type": "application/json" },
61952
- body: JSON.stringify(params),
61953
- signal: controller.signal
61954
- });
61955
- return;
61956
- } catch {
61957
- } finally {
61958
- clearTimeout(timeout);
63253
+ /**
63254
+ * Auto-resolve skill gaps by searching for and installing matching candidates.
63255
+ * Processes at most 3 gaps per invocation to avoid runaway resolution loops.
63256
+ */
63257
+ async autoResolveGaps(gaps) {
63258
+ for (const gap of gaps.slice(0, 3)) {
63259
+ const candidate = await this.resolveSkill(gap.intent);
63260
+ if (candidate) {
63261
+ const installed = await this.skillResolver.install(candidate);
63262
+ if (installed) {
63263
+ this.logger.info("skill_auto_installed", { name: installed.name, source: installed.source });
63264
+ }
61959
63265
  }
61960
63266
  }
61961
63267
  }
63268
+ /** Record skill execution result for learning. */
63269
+ recordSkillFeedback(skillName, intent, sessionId, success, feedback) {
63270
+ try {
63271
+ if (success) {
63272
+ this.skillLearner.recordSuccess(skillName, intent, sessionId, feedback);
63273
+ if (this.skillLearner.evaluatePromotion(skillName))
63274
+ this.logger.info("skill_promotion_ready", { skillName });
63275
+ } else {
63276
+ this.skillLearner.recordFailure(skillName, intent, sessionId, feedback ?? "unknown");
63277
+ if (this.skillLearner.evaluateDemotion(skillName))
63278
+ this.logger.warn("skill_demotion_triggered", { skillName });
63279
+ }
63280
+ } catch {
63281
+ }
63282
+ }
61962
63283
  /* ── Cleanup ───────────────────────────────────────────────────── */
61963
63284
  /** Persist state and release resources. */
61964
63285
  dispose() {
61965
63286
  try {
61966
63287
  this.orchestrator.persist().catch(() => {
61967
63288
  });
63289
+ void this.skillLearner.flush(this.skillLearnerStatePath).catch((error) => {
63290
+ this.logger.warn("skill_learner_flush_failed", { error: error instanceof Error ? error.message : String(error) });
63291
+ });
61968
63292
  this.skillsUsedPerSession.clear();
61969
63293
  this.recCache = null;
61970
63294
  this.logger.info("chitragupta_bridge_disposed");
@@ -62508,22 +63832,22 @@ function json(res, status, body) {
62508
63832
  res.end(JSON.stringify(body));
62509
63833
  }
62510
63834
  function readBody(req) {
62511
- return new Promise((resolve) => {
63835
+ return new Promise((resolve3) => {
62512
63836
  const chunks = [];
62513
63837
  req.on("data", (chunk) => chunks.push(chunk));
62514
63838
  req.on("end", () => {
62515
63839
  const raw = Buffer.concat(chunks).toString("utf8");
62516
63840
  if (!raw) {
62517
- resolve(void 0);
63841
+ resolve3(void 0);
62518
63842
  return;
62519
63843
  }
62520
63844
  try {
62521
- resolve(JSON.parse(raw));
63845
+ resolve3(JSON.parse(raw));
62522
63846
  } catch {
62523
- resolve(void 0);
63847
+ resolve3(void 0);
62524
63848
  }
62525
63849
  });
62526
- req.on("error", () => resolve(void 0));
63850
+ req.on("error", () => resolve3(void 0));
62527
63851
  });
62528
63852
  }
62529
63853
 
@@ -65070,7 +66394,7 @@ ${issues}`
65070
66394
  if (records.length === 0) {
65071
66395
  return "No subagents have run yet.";
65072
66396
  }
65073
- const { buildAgentTree: buildAgentTree2 } = await import("./chunks/tree-RSHKDTCR.js");
66397
+ const { buildAgentTree: buildAgentTree2 } = await import("./chunks/tree-FIUVGJ5J.js");
65074
66398
  const roots = buildAgentTree2(records);
65075
66399
  const lines = [];
65076
66400
  const render = (nodes, depth) => {
@@ -65185,7 +66509,7 @@ import path46 from "node:path";
65185
66509
  import fs45 from "node:fs";
65186
66510
  import { Readable } from "node:stream";
65187
66511
  import { pipeline as pipeline2 } from "node:stream/promises";
65188
- import { spawn as spawn6 } from "node:child_process";
66512
+ import { spawn as spawn7 } from "node:child_process";
65189
66513
  import { randomUUID as randomUUID42 } from "node:crypto";
65190
66514
  function sanitizeArtifactFileName(value) {
65191
66515
  const base = path46.basename(value || "file");
@@ -65244,15 +66568,15 @@ async function stageTelegramAttachment(params) {
65244
66568
  return { localPath, downloadUrl: url };
65245
66569
  }
65246
66570
  async function extractPdfText(pdfPath) {
65247
- return await new Promise((resolve) => {
65248
- const child = spawn6("pdftotext", ["-layout", pdfPath, "-"], {
66571
+ return await new Promise((resolve3) => {
66572
+ const child = spawn7("pdftotext", ["-layout", pdfPath, "-"], {
65249
66573
  stdio: ["ignore", "pipe", "pipe"]
65250
66574
  });
65251
66575
  let stdout = "";
65252
66576
  let stderr = "";
65253
66577
  const timeout = setTimeout(() => {
65254
66578
  child.kill("SIGKILL");
65255
- resolve({ ok: false, error: "pdftotext_timeout" });
66579
+ resolve3({ ok: false, error: "pdftotext_timeout" });
65256
66580
  }, 6e4);
65257
66581
  child.stdout.on("data", (chunk) => {
65258
66582
  stdout += chunk.toString();
@@ -65263,18 +66587,18 @@ async function extractPdfText(pdfPath) {
65263
66587
  child.on("error", (err) => {
65264
66588
  clearTimeout(timeout);
65265
66589
  const code = err?.code;
65266
- resolve({ ok: false, error: code === "ENOENT" ? "pdftotext_missing" : err.message });
66590
+ resolve3({ ok: false, error: code === "ENOENT" ? "pdftotext_missing" : err.message });
65267
66591
  });
65268
66592
  child.on("close", (code) => {
65269
66593
  clearTimeout(timeout);
65270
66594
  if (code !== 0) {
65271
- resolve({
66595
+ resolve3({
65272
66596
  ok: false,
65273
66597
  error: stderr.trim() || `pdftotext_exit_${code}`
65274
66598
  });
65275
66599
  return;
65276
66600
  }
65277
- resolve({ ok: true, text: stdout });
66601
+ resolve3({ ok: true, text: stdout });
65278
66602
  });
65279
66603
  });
65280
66604
  }
@@ -66375,6 +67699,210 @@ function setupIMessage(deps) {
66375
67699
  return adapter;
66376
67700
  }
66377
67701
 
67702
+ // apps/gateway/dist/channel/tring-setup.js
67703
+ function setupTring(deps) {
67704
+ const { runtime, tringConfig, isChannelAllowed } = deps;
67705
+ const { config, logger, storage, broadcastEvent, sessionIdentityMap } = runtime;
67706
+ const resolveSession = (message) => resolveSessionWithPolicy(storage, {
67707
+ channel: message.channel,
67708
+ chatId: message.chatId,
67709
+ userId: message.userId,
67710
+ chatType: message.chatType,
67711
+ threadId: message.threadId,
67712
+ workspaceId: message.workspaceId
67713
+ }, config.session, sessionIdentityMap);
67714
+ const handler = async (message) => {
67715
+ try {
67716
+ const allowed = await isChannelAllowed(message.channel, message.chatId, message.userId);
67717
+ if (!allowed) {
67718
+ logger.debug("tring_message_rejected", {
67719
+ channel: message.channel,
67720
+ chatId: message.chatId,
67721
+ userId: message.userId,
67722
+ reason: "not_allowed"
67723
+ });
67724
+ return;
67725
+ }
67726
+ broadcastEvent("channel.message", {
67727
+ channel: message.channel,
67728
+ direction: "inbound",
67729
+ chatId: message.chatId,
67730
+ userId: message.userId,
67731
+ text: message.text,
67732
+ time: message.time
67733
+ });
67734
+ const session = await resolveSession(message);
67735
+ const result = await runtime.runAgent({
67736
+ providerId: tringConfig.providerId,
67737
+ model: tringConfig.model,
67738
+ system: tringConfig.system,
67739
+ message: {
67740
+ channel: message.channel,
67741
+ chatId: message.chatId,
67742
+ userId: message.userId,
67743
+ chatType: message.chatType ?? "dm",
67744
+ text: message.text,
67745
+ time: message.time,
67746
+ raw: message.raw
67747
+ }
67748
+ });
67749
+ const reply = result.response.content;
67750
+ if (!reply)
67751
+ return;
67752
+ const platform = message.channel;
67753
+ await adapter.sendMessage(platform, message.chatId, reply);
67754
+ broadcastEvent("channel.message", {
67755
+ channel: message.channel,
67756
+ direction: "outbound",
67757
+ chatId: message.chatId,
67758
+ userId: message.userId,
67759
+ text: reply,
67760
+ time: (/* @__PURE__ */ new Date()).toISOString(),
67761
+ sessionId: session.id
67762
+ });
67763
+ } catch (error) {
67764
+ logger.error("tring_message_handler_error", {
67765
+ error: error instanceof Error ? error.message : String(error),
67766
+ channel: message.channel,
67767
+ chatId: message.chatId
67768
+ });
67769
+ }
67770
+ };
67771
+ const adapter = new TringAdapter({
67772
+ daemonUrl: tringConfig.daemonUrl ?? "http://localhost:8787",
67773
+ tringBin: tringConfig.tringBin,
67774
+ platforms: (tringConfig.platforms ?? ["whatsapp", "telegram", "signal"]).map((platform) => normalizeTringPlatform3(platform)).filter((platform) => Boolean(platform)),
67775
+ pollIntervalMs: tringConfig.pollIntervalMs ?? 2e3,
67776
+ autoStartDaemon: tringConfig.autoStartDaemon ?? true,
67777
+ allowedChats: tringConfig.allowedChats
67778
+ }, handler);
67779
+ return adapter;
67780
+ }
67781
+ function normalizeTringPlatform3(platform) {
67782
+ const normalized = platform.trim().toLowerCase();
67783
+ if (normalized === "whispr")
67784
+ return "signal";
67785
+ if (normalized === "whatsapp" || normalized === "telegram" || normalized === "signal")
67786
+ return normalized;
67787
+ return void 0;
67788
+ }
67789
+
67790
+ // apps/gateway/dist/channel/channel-autostart.js
67791
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, mkdirSync } from "node:fs";
67792
+ import { join as join3, dirname as dirname3 } from "node:path";
67793
+ var STATE_FILE_NAME = "channel-pairings.json";
67794
+ var STATE_VERSION = 1;
67795
+ var ChannelAutoStart = class {
67796
+ stateFile;
67797
+ logger;
67798
+ states;
67799
+ constructor(options) {
67800
+ this.stateFile = join3(options.storageRoot, STATE_FILE_NAME);
67801
+ this.logger = options.logger;
67802
+ this.states = /* @__PURE__ */ new Map();
67803
+ }
67804
+ /** Load persisted pairing states from disk. */
67805
+ load() {
67806
+ if (!existsSync4(this.stateFile)) {
67807
+ this.logger.info("channel_autostart_no_state_file", {
67808
+ path: this.stateFile
67809
+ });
67810
+ return;
67811
+ }
67812
+ try {
67813
+ const raw = readFileSync3(this.stateFile, "utf-8");
67814
+ const parsed = JSON.parse(raw);
67815
+ if (!isPersistedState(parsed)) {
67816
+ this.logger.warn("channel_autostart_invalid_state", {
67817
+ path: this.stateFile
67818
+ });
67819
+ return;
67820
+ }
67821
+ for (const entry of parsed.channels) {
67822
+ this.states.set(entry.channel, entry);
67823
+ }
67824
+ this.logger.info("channel_autostart_loaded", {
67825
+ count: this.states.size,
67826
+ channels: [...this.states.keys()]
67827
+ });
67828
+ } catch (err) {
67829
+ this.logger.warn("channel_autostart_load_error", {
67830
+ error: err instanceof Error ? err.message : String(err),
67831
+ path: this.stateFile
67832
+ });
67833
+ }
67834
+ }
67835
+ /** Save current pairing states to disk. */
67836
+ save() {
67837
+ const data2 = {
67838
+ version: STATE_VERSION,
67839
+ channels: [...this.states.values()]
67840
+ };
67841
+ try {
67842
+ const dir = dirname3(this.stateFile);
67843
+ if (!existsSync4(dir)) {
67844
+ mkdirSync(dir, { recursive: true });
67845
+ }
67846
+ writeFileSync(this.stateFile, JSON.stringify(data2, null, 2), "utf-8");
67847
+ this.logger.info("channel_autostart_saved", {
67848
+ count: this.states.size,
67849
+ path: this.stateFile
67850
+ });
67851
+ } catch (err) {
67852
+ this.logger.warn("channel_autostart_save_error", {
67853
+ error: err instanceof Error ? err.message : String(err),
67854
+ path: this.stateFile
67855
+ });
67856
+ }
67857
+ }
67858
+ /** Mark a channel as paired (or unpaired). */
67859
+ setPaired(channel, paired, data2) {
67860
+ const existing = this.states.get(channel);
67861
+ const state = {
67862
+ channel,
67863
+ paired,
67864
+ pairingData: data2 ?? existing?.pairingData,
67865
+ lastActive: (/* @__PURE__ */ new Date()).toISOString()
67866
+ };
67867
+ this.states.set(channel, state);
67868
+ this.logger.info("channel_autostart_set_paired", {
67869
+ channel,
67870
+ paired,
67871
+ hasData: data2 !== void 0
67872
+ });
67873
+ }
67874
+ /** Check if a channel was previously paired. */
67875
+ isPaired(channel) {
67876
+ return this.states.get(channel)?.paired === true;
67877
+ }
67878
+ /** Get all channels currently marked as paired. */
67879
+ getPairedChannels() {
67880
+ return [...this.states.values()].filter((s) => s.paired);
67881
+ }
67882
+ /** Update the lastActive timestamp for a channel. */
67883
+ touch(channel) {
67884
+ const existing = this.states.get(channel);
67885
+ if (existing) {
67886
+ existing.lastActive = (/* @__PURE__ */ new Date()).toISOString();
67887
+ this.states.set(channel, existing);
67888
+ }
67889
+ }
67890
+ /** Get pairing data for a channel (e.g., tokens, config). */
67891
+ getPairingData(channel) {
67892
+ return this.states.get(channel)?.pairingData;
67893
+ }
67894
+ };
67895
+ function isPersistedState(value) {
67896
+ if (typeof value !== "object" || value === null)
67897
+ return false;
67898
+ const obj = value;
67899
+ if (typeof obj["version"] !== "number")
67900
+ return false;
67901
+ if (!Array.isArray(obj["channels"]))
67902
+ return false;
67903
+ return obj["channels"].every((c) => typeof c === "object" && c !== null && typeof c["channel"] === "string" && typeof c["paired"] === "boolean");
67904
+ }
67905
+
66378
67906
  // apps/gateway/dist/gateway/restart-notice.js
66379
67907
  import fs46 from "node:fs";
66380
67908
  import path47 from "node:path";
@@ -66436,7 +67964,7 @@ async function maybeBootstrapSmritiDev(params) {
66436
67964
 
66437
67965
  // apps/gateway/dist/gateway/start-runtime-surface.js
66438
67966
  async function startRuntimeSurface(params) {
66439
- const { state, repoRoot, methods, logger, runtime, idempotency, storage, providers, config, channelPairingStore, channelPairingTtlSeconds, cleanupPairings: cleanupPairings2, isChannelAllowed, sendChannelMessage, broadcastEvent, gwEnv, artifactsRoot, isValidPairingCode: isValidPairingCode2, handleMemoryCommand, handleForgetCommand, createToolApprovalRequest, guardToolExecution: guardToolExecution2, inferProviderFromModel: inferProviderFromModel2, providerModelAliasMap, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, listProviderModels, formatSafeErrorMessage: formatSafeErrorMessage2, updateWeatherDefaults, telegramAdapterRef, twitterAdapterRef, whatsappAdapterRef, imessageAdapterRef, proactiveManager, storageRoot, smritiClient, profile, smritiSource, smritiAutoMemoryEnabled, smritiAutoMemoryReady, smritiEnabled, smritiApiKey, smritiAutoMemoryRoot, inboxWorker, runtimeServices, flushSmritiMemoryOutbox, clearSessionResetListener, chitraguptaBridge, mcpManager } = params;
67967
+ const { state, repoRoot, methods, logger, runtime, idempotency, storage, providers, config, channelPairingStore, channelPairingTtlSeconds, cleanupPairings: cleanupPairings2, isChannelAllowed, sendChannelMessage, broadcastEvent, gwEnv, artifactsRoot, isValidPairingCode: isValidPairingCode2, handleMemoryCommand, handleForgetCommand, createToolApprovalRequest, guardToolExecution: guardToolExecution2, inferProviderFromModel: inferProviderFromModel2, providerModelAliasMap, resolveModelAlias: resolveModelAlias2, normalizeModelId: normalizeModelId2, listProviderModels, formatSafeErrorMessage: formatSafeErrorMessage2, updateWeatherDefaults, telegramAdapterRef, twitterAdapterRef, whatsappAdapterRef, imessageAdapterRef, tringAdapterRef, proactiveManager, storageRoot, smritiClient, profile, smritiSource, smritiAutoMemoryEnabled, smritiAutoMemoryReady, smritiEnabled, smritiApiKey, smritiAutoMemoryRoot, inboxWorker, runtimeServices, flushSmritiMemoryOutbox, clearSessionResetListener, chitraguptaBridge, mcpManager } = params;
66440
67968
  const server = createHttpServer(state, repoRoot, methods, logger, {
66441
67969
  storage,
66442
67970
  storageConfig: config.storage,
@@ -66458,6 +67986,17 @@ async function startRuntimeSurface(params) {
66458
67986
  server.listen(config.gateway.port, config.gateway.host, () => {
66459
67987
  logger.info("gateway_listening", { host: config.gateway.host, port: config.gateway.port });
66460
67988
  });
67989
+ const autoStart = new ChannelAutoStart({ storageRoot, logger });
67990
+ autoStart.load();
67991
+ for (const state2 of autoStart.getPairedChannels()) {
67992
+ const channelKey = state2.channel;
67993
+ if (!config.channels[channelKey]?.enabled) {
67994
+ logger.info("channel_auto_available", {
67995
+ channel: state2.channel,
67996
+ lastActive: state2.lastActive
67997
+ });
67998
+ }
67999
+ }
66461
68000
  if (config.channels.telegram.enabled) {
66462
68001
  const geoConfig = {
66463
68002
  geoBaseUrl: gwEnv.weather.geoBaseUrl ?? "https://geocoding-api.open-meteo.com/v1",
@@ -66492,6 +68031,8 @@ async function startRuntimeSurface(params) {
66492
68031
  });
66493
68032
  telegramAdapterRef.set(adapter);
66494
68033
  adapter.start();
68034
+ autoStart.setPaired("telegram", true);
68035
+ autoStart.save();
66495
68036
  logger.info("telegram_adapter_started", { pollIntervalMs: config.channels.telegram.pollIntervalMs });
66496
68037
  }
66497
68038
  if (config.channels.twitter.enabled) {
@@ -66503,6 +68044,8 @@ async function startRuntimeSurface(params) {
66503
68044
  });
66504
68045
  twitterAdapterRef.set(twitterAdapter);
66505
68046
  twitterAdapter.start();
68047
+ autoStart.setPaired("twitter", true);
68048
+ autoStart.save();
66506
68049
  logger.info("twitter_adapter_started", {
66507
68050
  pollIntervalMs: config.channels.twitter.pollIntervalMs,
66508
68051
  monitorMentions: config.channels.twitter.monitorMentions
@@ -66517,6 +68060,8 @@ async function startRuntimeSurface(params) {
66517
68060
  });
66518
68061
  whatsappAdapterRef.set(waAdapter);
66519
68062
  waAdapter.start();
68063
+ autoStart.setPaired("whatsapp", true);
68064
+ autoStart.save();
66520
68065
  logger.info("whatsapp_adapter_started", { webhookPort: config.channels.whatsapp.webhookPort });
66521
68066
  }
66522
68067
  if (config.channels.imessage.enabled) {
@@ -66528,11 +68073,32 @@ async function startRuntimeSurface(params) {
66528
68073
  });
66529
68074
  imessageAdapterRef.set(imAdapter);
66530
68075
  imAdapter.start();
68076
+ autoStart.setPaired("imessage", true);
68077
+ autoStart.save();
66531
68078
  logger.info("imessage_adapter_started", { pollIntervalMs: config.channels.imessage.pollIntervalMs });
66532
68079
  }
68080
+ if (config.channels.tring.enabled) {
68081
+ const tAdapter = setupTring({
68082
+ runtime,
68083
+ tringConfig: config.channels.tring,
68084
+ isChannelAllowed,
68085
+ formatSafeErrorMessage: formatSafeErrorMessage2
68086
+ });
68087
+ tringAdapterRef.set(tAdapter);
68088
+ tAdapter.start();
68089
+ autoStart.setPaired("tring", true);
68090
+ autoStart.save();
68091
+ logger.info("tring_adapter_started", {
68092
+ platforms: config.channels.tring.platforms,
68093
+ pollIntervalMs: config.channels.tring.pollIntervalMs
68094
+ });
68095
+ }
66533
68096
  proactiveManager.registerChannel("hub", createHubDelivery(broadcastEvent));
66534
- if (config.channels.telegram.enabled && telegramAdapterRef.get()) {
66535
- const allowedChats = config.channels.telegram.allowedChats ?? [];
68097
+ const tringPlatforms = (config.channels.tring.platforms ?? []).map((platform) => normalizeTringPlatform4(platform));
68098
+ const telegramViaTring = config.channels.tring.enabled && tringAdapterRef.get() && tringPlatforms.includes("telegram");
68099
+ const whatsappViaTring = config.channels.tring.enabled && tringAdapterRef.get() && tringPlatforms.includes("whatsapp");
68100
+ if (config.channels.telegram.enabled && telegramAdapterRef.get() || telegramViaTring) {
68101
+ const allowedChats = config.channels.telegram.enabled ? config.channels.telegram.allowedChats ?? [] : config.channels.tring.allowedChats ?? [];
66536
68102
  proactiveManager.registerChannel("telegram", createTelegramDelivery((chatId, text) => sendChannelMessage("telegram", chatId, text), () => allowedChats));
66537
68103
  logger.info("proactive_telegram_registered", { chatCount: allowedChats.length });
66538
68104
  }
@@ -66541,8 +68107,8 @@ async function startRuntimeSurface(params) {
66541
68107
  proactiveManager.registerChannel("twitter", createTwitterDelivery((userId, text) => sendChannelMessage("twitter", userId, text), () => allowedUsers));
66542
68108
  logger.info("proactive_twitter_registered", { userCount: allowedUsers.length });
66543
68109
  }
66544
- if (config.channels.whatsapp.enabled && whatsappAdapterRef.get()) {
66545
- const allowedNumbers = config.channels.whatsapp.allowedNumbers ?? [];
68110
+ if (config.channels.whatsapp.enabled && whatsappAdapterRef.get() || whatsappViaTring) {
68111
+ const allowedNumbers = config.channels.whatsapp.enabled ? config.channels.whatsapp.allowedNumbers ?? [] : config.channels.tring.allowedChats ?? [];
66546
68112
  proactiveManager.registerChannel("whatsapp", createWhatsAppDelivery((to, text) => sendChannelMessage("whatsapp", to, text), () => allowedNumbers));
66547
68113
  logger.info("proactive_whatsapp_registered", { numberCount: allowedNumbers.length });
66548
68114
  }
@@ -66595,6 +68161,9 @@ async function startRuntimeSurface(params) {
66595
68161
  const imAdapter = imessageAdapterRef.get();
66596
68162
  if (imAdapter)
66597
68163
  imAdapter.stop?.();
68164
+ const trAdapter = tringAdapterRef.get();
68165
+ if (trAdapter)
68166
+ trAdapter.stop?.();
66598
68167
  mcpManager.stopAll?.();
66599
68168
  wss.close();
66600
68169
  server.close(() => {
@@ -66606,6 +68175,12 @@ async function startRuntimeSurface(params) {
66606
68175
  process.on("SIGTERM", shutdown);
66607
68176
  logger.info("gateway_started", { uptimeMs: Date.now() - state.startedAt });
66608
68177
  }
68178
+ function normalizeTringPlatform4(platform) {
68179
+ const normalized = platform.trim().toLowerCase();
68180
+ if (normalized === "whispr")
68181
+ return "signal";
68182
+ return normalized;
68183
+ }
66609
68184
 
66610
68185
  // apps/gateway/dist/rpc/methods/cores.js
66611
68186
  function createCoreMethods(ctx) {
@@ -69920,11 +71495,11 @@ function createOnboardingMethods(_ctx) {
69920
71495
  const fs48 = await import("node:fs");
69921
71496
  const os12 = await import("node:os");
69922
71497
  const path52 = await import("node:path");
69923
- const { execSync } = await import("node:child_process");
71498
+ const { execSync: execSync2 } = await import("node:child_process");
69924
71499
  let codexInstalled = false;
69925
71500
  let codexLoggedIn = false;
69926
71501
  try {
69927
- execSync("which codex", { encoding: "utf-8", stdio: "pipe" });
71502
+ execSync2("which codex", { encoding: "utf-8", stdio: "pipe" });
69928
71503
  codexInstalled = true;
69929
71504
  const codexAuthPath = path52.default.join(process.env.CODEX_HOME ?? path52.default.join(os12.default.homedir(), ".codex"), "auth.json");
69930
71505
  if (fs48.default.existsSync(codexAuthPath)) {
@@ -69935,7 +71510,7 @@ function createOnboardingMethods(_ctx) {
69935
71510
  let claudeInstalled = false;
69936
71511
  let claudeLoggedIn = false;
69937
71512
  try {
69938
- execSync("which claude", { encoding: "utf-8", stdio: "pipe" });
71513
+ execSync2("which claude", { encoding: "utf-8", stdio: "pipe" });
69939
71514
  claudeInstalled = true;
69940
71515
  const claudeCredPath = path52.default.join(os12.default.homedir(), ".claude", ".credentials.json");
69941
71516
  if (fs48.default.existsSync(claudeCredPath)) {
@@ -70355,7 +71930,7 @@ function buildGatewayMethods(input) {
70355
71930
  // apps/gateway/dist/gateway/start.js
70356
71931
  async function startGateway() {
70357
71932
  const core = await setupGatewayCore();
70358
- let { configPath, config, repoRoot, storageRoot, artifactsRoot, soulPrompt, logger, state, idempotency, providers, providerModelAliasMap, lastAnthropicCallAt, sessionIdentityMap, listProviderModels, storage, profile, getProfile, updateProfile, memoryAllowed, memoryManager, gwEnv, smritiConfig, smritiEnabled, smritiBaseUrl, smritiApiKey, smritiPrefix, smritiOwner, smritiVectorProvider, smritiVectorFallbacks, smritiEmbeddingProviders, smritiRetrieveEnabled, smritiRetrieveLimit, smritiSource, smritiAutoMemoryEnabled, smritiAutoMemoryRoot, smritiAutoMemoryReady, smritiClient, smritiHelpers, toolRegistry, toolPolicy, loadToolPolicyFromFile, mcpManager, guardian, connections, connectionAuth, pendingChallenges, pairingStore, channelPairingStore, deviceActivity, smritiAutoMemorySessions, channelPairingTtlSeconds, telegramAdapterRef, twitterAdapterRef, whatsappAdapterRef, imessageAdapterRef, skillManifestsRef, broadcastEvent, getProvider, sendChannelMessage, isChannelAllowed, runTool, createToolApprovalRequest, executeApprovedTool, handleMemoryCommand, handleForgetCommand, updateWeatherDefaults, pairingStepSeconds, cleanupPairings: cleanupPairings2, isValidPairingCode: isValidPairingCode2 } = core;
71933
+ let { configPath, config, repoRoot, storageRoot, artifactsRoot, soulPrompt, logger, state, idempotency, providers, providerModelAliasMap, lastAnthropicCallAt, sessionIdentityMap, listProviderModels, storage, profile, getProfile, updateProfile, memoryAllowed, memoryManager, gwEnv, smritiConfig, smritiEnabled, smritiBaseUrl, smritiApiKey, smritiPrefix, smritiOwner, smritiVectorProvider, smritiVectorFallbacks, smritiEmbeddingProviders, smritiRetrieveEnabled, smritiRetrieveLimit, smritiSource, smritiAutoMemoryEnabled, smritiAutoMemoryRoot, smritiAutoMemoryReady, smritiClient, smritiHelpers, toolRegistry, toolPolicy, loadToolPolicyFromFile, mcpManager, guardian, connections, connectionAuth, pendingChallenges, pairingStore, channelPairingStore, deviceActivity, smritiAutoMemorySessions, channelPairingTtlSeconds, telegramAdapterRef, twitterAdapterRef, whatsappAdapterRef, imessageAdapterRef, tringAdapterRef, skillManifestsRef, broadcastEvent, getProvider, sendChannelMessage, isChannelAllowed, runTool, createToolApprovalRequest, executeApprovedTool, handleMemoryCommand, handleForgetCommand, updateWeatherDefaults, pairingStepSeconds, cleanupPairings: cleanupPairings2, isValidPairingCode: isValidPairingCode2 } = core;
70359
71934
  const instanceLock = acquireGatewayInstanceLock({
70360
71935
  rootDir: storageRoot,
70361
71936
  host: config.gateway.host,
@@ -70384,6 +71959,20 @@ async function startGateway() {
70384
71959
  logger,
70385
71960
  enableAutoLearn: config.learnings?.autoLearn ?? false
70386
71961
  });
71962
+ if (chitraguptaBridge) {
71963
+ const currentTools = toolRegistry.list();
71964
+ const registered = chitraguptaBridge.registerTools(currentTools.map((tool) => ({
71965
+ name: tool.name,
71966
+ description: tool.description,
71967
+ inputSchema: tool.inputSchema,
71968
+ tags: tool.tags,
71969
+ aliases: tool.aliases
71970
+ })));
71971
+ logger.info("chitragupta_tools_registered", {
71972
+ totalTools: currentTools.length,
71973
+ registeredTools: registered
71974
+ });
71975
+ }
70387
71976
  setSessionResetListener((session) => {
70388
71977
  chitraguptaBridge?.onSessionEnd(session.id);
70389
71978
  });
@@ -70647,6 +72236,7 @@ async function startGateway() {
70647
72236
  twitterAdapterRef,
70648
72237
  whatsappAdapterRef,
70649
72238
  imessageAdapterRef,
72239
+ tringAdapterRef,
70650
72240
  proactiveManager,
70651
72241
  storageRoot,
70652
72242
  smritiClient,