titan-agent 5.0.2 → 5.0.3

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 (176) hide show
  1. package/dist/agent/agent.js +48 -3
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/agentLoop.js +83 -5
  4. package/dist/agent/agentLoop.js.map +1 -1
  5. package/dist/agent/commandPost.js +1 -1
  6. package/dist/agent/commandPost.js.map +1 -1
  7. package/dist/agent/goalProposer.js +2 -2
  8. package/dist/agent/goalProposer.js.map +1 -1
  9. package/dist/agent/missionDriver.js +1 -1
  10. package/dist/agent/missionDriver.js.map +1 -1
  11. package/dist/agent/promptBudget.js +85 -0
  12. package/dist/agent/promptBudget.js.map +1 -0
  13. package/dist/agent/structuredSpawn.js +1 -1
  14. package/dist/agent/structuredSpawn.js.map +1 -1
  15. package/dist/agent/subtaskTaxonomy.js +1 -1
  16. package/dist/agent/subtaskTaxonomy.js.map +1 -1
  17. package/dist/agent/systemPromptParts.js +10 -1
  18. package/dist/agent/systemPromptParts.js.map +1 -1
  19. package/dist/agent/toolRunner.js +16 -0
  20. package/dist/agent/toolRunner.js.map +1 -1
  21. package/dist/agent/toolSearch.js +4 -1
  22. package/dist/agent/toolSearch.js.map +1 -1
  23. package/dist/analytics/bugReports.js +1 -1
  24. package/dist/analytics/bugReports.js.map +1 -1
  25. package/dist/channels/messenger.js +1 -1
  26. package/dist/channels/messenger.js.map +1 -1
  27. package/dist/eval/harness.js +141 -0
  28. package/dist/eval/harness.js.map +1 -0
  29. package/dist/gateway/server.js +374 -74
  30. package/dist/gateway/server.js.map +1 -1
  31. package/dist/hooks/shellHooks.js +1 -1
  32. package/dist/hooks/shellHooks.js.map +1 -1
  33. package/dist/lib/auto-heal/repair-strategies.js.map +1 -1
  34. package/dist/memory/promptIncludes.js +58 -0
  35. package/dist/memory/promptIncludes.js.map +1 -0
  36. package/dist/organism/alertsStore.js +70 -0
  37. package/dist/organism/alertsStore.js.map +1 -0
  38. package/dist/plugins/memoryRetrieval.js.map +1 -1
  39. package/dist/providers/ollama.js +7 -7
  40. package/dist/providers/ollama.js.map +1 -1
  41. package/dist/safety/invariants.js +60 -0
  42. package/dist/safety/invariants.js.map +1 -0
  43. package/dist/safety/opusReview.js +1 -1
  44. package/dist/safety/opusReview.js.map +1 -1
  45. package/dist/security/commandScanner.js +2 -2
  46. package/dist/security/commandScanner.js.map +1 -1
  47. package/dist/security/secretGuard.js +4 -4
  48. package/dist/security/secretGuard.js.map +1 -1
  49. package/dist/skills/builtin/widget_gallery.js +28 -1
  50. package/dist/skills/builtin/widget_gallery.js.map +1 -1
  51. package/dist/skills/frontmatterLoader.js +119 -0
  52. package/dist/skills/frontmatterLoader.js.map +1 -0
  53. package/dist/skills/registry.js +20 -0
  54. package/dist/skills/registry.js.map +1 -1
  55. package/dist/testing/testHealthMonitor.js +1 -2
  56. package/dist/testing/testHealthMonitor.js.map +1 -1
  57. package/dist/utils/constants.js +2 -2
  58. package/dist/utils/constants.js.map +1 -1
  59. package/dist/utils/replyQuality.js +1 -1
  60. package/dist/utils/replyQuality.js.map +1 -1
  61. package/dist/utils/tokens.js +1 -1
  62. package/dist/utils/tokens.js.map +1 -1
  63. package/docs/bleeding-edge-agents-2026.md +450 -0
  64. package/docs/langchain-analysis.md +598 -0
  65. package/docs/langchain-code-analysis.md +363 -0
  66. package/docs/space-agent-analysis.md +300 -0
  67. package/package.json +1 -1
  68. package/ui/dist/assets/{AuditPanel-G7YA1HzV.js → AuditPanel-B84Mp16G.js} +2 -2
  69. package/ui/dist/assets/AutonomyPanel-DOtiTFxV.js +11 -0
  70. package/ui/dist/assets/{AutopilotPanel-CHRjxdh0.js → AutopilotPanel-nTb1Dnru.js} +1 -1
  71. package/ui/dist/assets/AutoresearchPanel-D46mX8VF.js +6 -0
  72. package/ui/dist/assets/BackupPanel-DGM1XXbG.js +1 -0
  73. package/ui/dist/assets/BrowserPanel-Cn1tTN3y.js +6 -0
  74. package/ui/dist/assets/{CPAgents-D5533PhK.js → CPAgents-CEraUkME.js} +1 -1
  75. package/ui/dist/assets/{CPDashboard-C-GgqDsI.js → CPDashboard-B_yidGAe.js} +2 -2
  76. package/ui/dist/assets/CPFiles-BBS8jtYH.js +1 -0
  77. package/ui/dist/assets/CPGoals-DL5v21TZ.js +1 -0
  78. package/ui/dist/assets/CPInbox-CyLQJBYF.js +11 -0
  79. package/ui/dist/assets/{CPSocial-mUQsrSh5.js → CPSocial-BkEtQ1Um.js} +3 -3
  80. package/ui/dist/assets/ChannelsPanel-CD2kHhA5.js +1 -0
  81. package/ui/dist/assets/CheckpointsPanel-BrUTFPu_.js +1 -0
  82. package/ui/dist/assets/CommandPostHub-BPPaUv1B.js +29 -0
  83. package/ui/dist/assets/CronPanel-CsfQctFp.js +1 -0
  84. package/ui/dist/assets/DaemonPanel-CNUggBbL.js +1 -0
  85. package/ui/dist/assets/DataTable-DuAEp_QJ.js +1 -0
  86. package/ui/dist/assets/{EmptyState-D60-wQrz.js → EmptyState-DFrAEZDm.js} +1 -1
  87. package/ui/dist/assets/EvalPanel-DEX0a5-b.js +1 -0
  88. package/ui/dist/assets/{FilesPanel-BNN3h_HW.js → FilesPanel-DATsiAqG.js} +1 -1
  89. package/ui/dist/assets/FleetPanel-QYQKqx4W.js +1 -0
  90. package/ui/dist/assets/{HomelabPanel-1mfhRBh6.js → HomelabPanel-DhuXd3ZD.js} +2 -2
  91. package/ui/dist/assets/{InfraView-Df6SFI7b.js → InfraView-eS7cpESw.js} +2 -2
  92. package/ui/dist/assets/InlineEditableField-zIAnW4AR.js +1 -0
  93. package/ui/dist/assets/{Input-DYukme8A.js → Input-bFsLI0fq.js} +1 -1
  94. package/ui/dist/assets/IntegrationsPanel-C_FswSRN.js +1 -0
  95. package/ui/dist/assets/IntelligenceView-smQ6aBwx.js +2 -0
  96. package/ui/dist/assets/{LearningPanel-BPx05bBu.js → LearningPanel-BEgF_iND.js} +1 -1
  97. package/ui/dist/assets/{LogsPanel-D3Qfp2SE.js → LogsPanel-Br1P8ST6.js} +1 -1
  98. package/ui/dist/assets/McpPanel-ByvQ12J_.js +1 -0
  99. package/ui/dist/assets/{MemoryGraphPanel-BFovwaSG.js → MemoryGraphPanel-BGOeSaET.js} +1 -1
  100. package/ui/dist/assets/MemoryWikiPanel-CR8btd66.js +11 -0
  101. package/ui/dist/assets/MeshPanel-BjkcSOMz.js +11 -0
  102. package/ui/dist/assets/NvidiaPanel-NYt42w7L.js +1 -0
  103. package/ui/dist/assets/OrganismPanel-PHvISvVn.js +1 -0
  104. package/ui/dist/assets/OverviewPanel-q35zdMr6.js +6 -0
  105. package/ui/dist/assets/{PageHeader-BdvxKoad.js → PageHeader-Cwn3OALc.js} +1 -1
  106. package/ui/dist/assets/PaperclipPanel-BDpQki0d.js +1 -0
  107. package/ui/dist/assets/{PersonasPanel-BpI6Npxv.js → PersonasPanel-DxrGW5C4.js} +1 -1
  108. package/ui/dist/assets/RecipesPanel-CYRdBx5u.js +1 -0
  109. package/ui/dist/assets/{SecurityPanel-CBDsEAFz.js → SecurityPanel-i1QMctV0.js} +1 -1
  110. package/ui/dist/assets/SelfImprovePanel-DbybAZWp.js +1 -0
  111. package/ui/dist/assets/SelfProposalsPanel-DtcTUDDd.js +2 -0
  112. package/ui/dist/assets/SessionsPanel-B7QmOizR.js +1 -0
  113. package/ui/dist/assets/SessionsTab-BdJj_vsI.js +1 -0
  114. package/ui/dist/assets/{SettingsPanel-BiWHsOAJ.js → SettingsPanel-DnEvJUFe.js} +1 -1
  115. package/ui/dist/assets/SettingsView-C39dk_yr.js +2 -0
  116. package/ui/dist/assets/{SkeletonLoader-CGtpZJ-7.js → SkeletonLoader-CsiR8ED9.js} +1 -1
  117. package/ui/dist/assets/{SkillsPanel-Z_9jA6dU.js → SkillsPanel-DM4qBFDS.js} +1 -1
  118. package/ui/dist/assets/{SomaView-AP3BXqf-.js → SomaView-CWnPKEQI.js} +1 -1
  119. package/ui/dist/assets/{StatCard-CrnvXPg5.js → StatCard-CY8lgeWm.js} +1 -1
  120. package/ui/dist/assets/{StatusBadge-B6r5EWBA.js → StatusBadge-CGvKbP7R.js} +1 -1
  121. package/ui/dist/assets/TeamsPanel-Bf6GaUni.js +1 -0
  122. package/ui/dist/assets/{TelemetryPanel-D6o14H-i.js → TelemetryPanel-JZ90gJXC.js} +1 -1
  123. package/ui/dist/assets/TitanCanvas-Hk49NFcA.js +1092 -0
  124. package/ui/dist/assets/ToolsView-Cq7Fuq3i.js +2 -0
  125. package/ui/dist/assets/{Tooltip-DNsYGHC9.js → Tooltip-CcoZrKsl.js} +1 -1
  126. package/ui/dist/assets/{TraceViewer-TOpdmqLF.js → TraceViewer-ojGf0drx.js} +1 -1
  127. package/ui/dist/assets/TrainingPanel-CWnP4H2l.js +1 -0
  128. package/ui/dist/assets/{VoiceOverlay-XIyCbAP7.js → VoiceOverlay-Dn6iaYgd.js} +1 -1
  129. package/ui/dist/assets/VramPanel-CLd9Ggck.js +1 -0
  130. package/ui/dist/assets/WatchView-CQBemwsm.js +13 -0
  131. package/ui/dist/assets/WorkTab-BOfTN-Bd.js +1 -0
  132. package/ui/dist/assets/WorkflowsPanel-qzNS0p0u.js +11 -0
  133. package/ui/dist/assets/{arrow-left-CQF-yBIU.js → arrow-left-c-8OFZUV.js} +1 -1
  134. package/ui/dist/assets/{chart-column-1smg0GbX.js → chart-column-x6L66Qw7.js} +1 -1
  135. package/ui/dist/assets/{circle-check-big-BiMDFx6C.js → circle-check-big-WaW3U3Xl.js} +1 -1
  136. package/ui/dist/assets/{dollar-sign-DMYH4Q_a.js → dollar-sign-D2Oce4Ru.js} +1 -1
  137. package/ui/dist/assets/{download-BYFd-yl6.js → download-YvPDLlFJ.js} +1 -1
  138. package/ui/dist/assets/eye-off-DIMcxsdQ.js +6 -0
  139. package/ui/dist/assets/{funnel-pWBglhfw.js → funnel-DqD9srZu.js} +1 -1
  140. package/ui/dist/assets/{git-branch-Cgqic2Us.js → git-branch-0FamUEbU.js} +1 -1
  141. package/ui/dist/assets/index-D932CbpQ.css +1 -0
  142. package/ui/dist/assets/index-NatBSFxj.js +227 -0
  143. package/ui/dist/assets/{legacy-BHbi-Nm_.js → legacy-DOO7F5cq.js} +1 -1
  144. package/ui/dist/assets/{lightbulb-D_y0Mtyq.js → lightbulb-Bk6KlR6q.js} +1 -1
  145. package/ui/dist/assets/pause-DDC_zUiJ.js +6 -0
  146. package/ui/dist/assets/{play-2xR4_zUG.js → play-BPXbHToG.js} +1 -1
  147. package/ui/dist/assets/{plug-DhvhYYy_.js → plug-Dxp-sWVF.js} +1 -1
  148. package/ui/dist/assets/proxy-vU7v4NVM.js +9 -0
  149. package/ui/dist/assets/square-Bn_0tYME.js +6 -0
  150. package/ui/dist/assets/target-BrtxUtzl.js +6 -0
  151. package/ui/dist/assets/toggle-right-CYphlpN5.js +11 -0
  152. package/ui/dist/assets/{trash-2-DmRaMz9e.js → trash-2-C_Jsp23A.js} +1 -1
  153. package/ui/dist/assets/{trending-up-DsDcs3Jo.js → trending-up-DrtLViSm.js} +1 -1
  154. package/ui/dist/assets/trophy-DdRzAOfo.js +6 -0
  155. package/ui/dist/index.html +2 -2
  156. package/ui/dist/assets/CPFiles-G7veSjMg.js +0 -6
  157. package/ui/dist/assets/CPGoals-C3DlKJrJ.js +0 -1
  158. package/ui/dist/assets/CPInbox-D10curQs.js +0 -16
  159. package/ui/dist/assets/ChannelsPanel-M3pO2htW.js +0 -1
  160. package/ui/dist/assets/CommandPostHub-CW9OY1A4.js +0 -37
  161. package/ui/dist/assets/InlineEditableField-CH-jR3LC.js +0 -11
  162. package/ui/dist/assets/IntegrationsPanel-EaN999Te.js +0 -1
  163. package/ui/dist/assets/IntelligenceView-Q4DBmJpJ.js +0 -2
  164. package/ui/dist/assets/McpPanel-zC7jTaSx.js +0 -6
  165. package/ui/dist/assets/MeshPanel-CqtYZ74K.js +0 -11
  166. package/ui/dist/assets/NvidiaPanel-BVIZFHet.js +0 -1
  167. package/ui/dist/assets/SelfImprovePanel-PSCYO6sx.js +0 -11
  168. package/ui/dist/assets/SessionsTab-Cn3dGgjX.js +0 -1
  169. package/ui/dist/assets/SettingsView-3BSIzAfW.js +0 -2
  170. package/ui/dist/assets/TitanCanvas-cnb7R1gS.js +0 -1056
  171. package/ui/dist/assets/ToolsView-Dp-xUWJG.js +0 -2
  172. package/ui/dist/assets/WorkTab-Pgq-iLz9.js +0 -1
  173. package/ui/dist/assets/WorkflowsPanel-B91LeW7r.js +0 -21
  174. package/ui/dist/assets/eye-BfW7UcEC.js +0 -11
  175. package/ui/dist/assets/index-BWSnB6Kr.js +0 -227
  176. package/ui/dist/assets/index-Dtw1pbjc.css +0 -1
@@ -1596,14 +1596,14 @@ ${msg.content}
1596
1596
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1597
1597
  }
1598
1598
  });
1599
- app.post("/api/sessions/:id/steer", (req, res) => {
1599
+ app.post("/api/sessions/:id/steer", async (req, res) => {
1600
1600
  try {
1601
1601
  const { message } = req.body;
1602
1602
  if (!message || typeof message !== "string") {
1603
1603
  res.status(400).json({ error: "message is required" });
1604
1604
  return;
1605
1605
  }
1606
- const { pushSteer } = require("../agent/agentLoop.js");
1606
+ const { pushSteer } = await import("../agent/agentLoop.js");
1607
1607
  pushSteer(req.params.id, message);
1608
1608
  res.json({ ok: true, sessionId: req.params.id });
1609
1609
  } catch (e) {
@@ -1611,9 +1611,9 @@ ${msg.content}
1611
1611
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1612
1612
  }
1613
1613
  });
1614
- app.get("/api/sessions/:id/checkpoints", (req, res) => {
1614
+ app.get("/api/sessions/:id/checkpoints", async (req, res) => {
1615
1615
  try {
1616
- const { listCheckpoints } = require("../checkpoint/manager.js");
1616
+ const { listCheckpoints } = await import("../checkpoint/manager.js");
1617
1617
  const checkpoints = listCheckpoints(req.params.id);
1618
1618
  res.json({ checkpoints });
1619
1619
  } catch (e) {
@@ -1621,9 +1621,9 @@ ${msg.content}
1621
1621
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1622
1622
  }
1623
1623
  });
1624
- app.post("/api/sessions/:id/checkpoints/:checkpointId/restore", (req, res) => {
1624
+ app.post("/api/sessions/:id/checkpoints/:checkpointId/restore", async (req, res) => {
1625
1625
  try {
1626
- const { restoreCheckpoint } = require("../checkpoint/manager.js");
1626
+ const { restoreCheckpoint } = await import("../checkpoint/manager.js");
1627
1627
  const result = restoreCheckpoint(req.params.id, req.params.checkpointId);
1628
1628
  res.json({ ok: result.success, restored: result.restored, errors: result.errors });
1629
1629
  } catch (e) {
@@ -1631,9 +1631,9 @@ ${msg.content}
1631
1631
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1632
1632
  }
1633
1633
  });
1634
- app.post("/api/guest", (_req, res) => {
1634
+ app.post("/api/guest", async (_req, res) => {
1635
1635
  try {
1636
- const { createGuestSession } = require("../agent/session.js");
1636
+ const { createGuestSession } = await import("../agent/session.js");
1637
1637
  const session = createGuestSession();
1638
1638
  res.json({ id: session.id, userId: session.userId, createdAt: session.createdAt });
1639
1639
  } catch (e) {
@@ -1641,11 +1641,11 @@ ${msg.content}
1641
1641
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1642
1642
  }
1643
1643
  });
1644
- app.get("/api/sessions/:id/history", (req, res) => {
1644
+ app.get("/api/sessions/:id/history", async (req, res) => {
1645
1645
  try {
1646
- const { listCheckpoints } = require("../checkpoint/manager.js");
1646
+ const { listCheckpoints } = await import("../checkpoint/manager.js");
1647
1647
  const checkpoints = listCheckpoints(req.params.id);
1648
- const { getHistory: getHistory2 } = require("../memory/memory.js");
1648
+ const { getHistory: getHistory2 } = await import("../memory/memory.js");
1649
1649
  const messages = getHistory2(req.params.id, 100);
1650
1650
  res.json({ checkpoints, messages, sessionId: req.params.id });
1651
1651
  } catch (e) {
@@ -1653,9 +1653,9 @@ ${msg.content}
1653
1653
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1654
1654
  }
1655
1655
  });
1656
- app.get("/api/prompt-includes", (_req, res) => {
1656
+ app.get("/api/prompt-includes", async (_req, res) => {
1657
1657
  try {
1658
- const { discoverPromptIncludes } = require("../promptincludes/discover.js");
1658
+ const { discoverPromptIncludes } = await import("../promptincludes/discover.js");
1659
1659
  const includes = discoverPromptIncludes();
1660
1660
  res.json({ includes });
1661
1661
  } catch (e) {
@@ -1771,9 +1771,9 @@ ${msg.content}
1771
1771
  return;
1772
1772
  }
1773
1773
  const shareId = `${req.params.id}-${Date.now().toString(36)}`;
1774
- const sharePath = `${require("os").homedir()}/.titan/shares/${shareId}.json`;
1775
- require("fs").mkdirSync(require("path").dirname(sharePath), { recursive: true });
1776
- require("fs").writeFileSync(sharePath, JSON.stringify({ sessionId: req.params.id, format, history, createdAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
1774
+ const sharePath = `${homedir()}/.titan/shares/${shareId}.json`;
1775
+ fs.mkdirSync(dirname(sharePath), { recursive: true });
1776
+ fs.writeFileSync(sharePath, JSON.stringify({ sessionId: req.params.id, format, history, createdAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
1777
1777
  res.json({ ok: true, shareId, url: `/api/shares/${shareId}` });
1778
1778
  } catch (e) {
1779
1779
  logger.error(COMPONENT, `Endpoint error: ${e.message}`);
@@ -1782,12 +1782,12 @@ ${msg.content}
1782
1782
  });
1783
1783
  app.get("/api/shares/:shareId", async (req, res) => {
1784
1784
  try {
1785
- const sharePath = `${require("os").homedir()}/.titan/shares/${req.params.shareId}.json`;
1786
- if (!require("fs").existsSync(sharePath)) {
1785
+ const sharePath = `${homedir()}/.titan/shares/${req.params.shareId}.json`;
1786
+ if (!fs.existsSync(sharePath)) {
1787
1787
  res.status(404).json({ error: "Share not found" });
1788
1788
  return;
1789
1789
  }
1790
- const data = JSON.parse(require("fs").readFileSync(sharePath, "utf-8"));
1790
+ const data = JSON.parse(fs.readFileSync(sharePath, "utf-8"));
1791
1791
  res.json(data);
1792
1792
  } catch (e) {
1793
1793
  logger.error(COMPONENT, `Endpoint error: ${e.message}`);
@@ -1936,6 +1936,17 @@ ${msg.content}
1936
1936
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1937
1937
  }
1938
1938
  });
1939
+ app.get("/api/widget-gallery", async (_req, res) => {
1940
+ try {
1941
+ const { listTemplates, listCategories } = await import("../skills/builtin/widget_gallery.js");
1942
+ const templates = listTemplates();
1943
+ const categories = listCategories();
1944
+ res.json({ count: templates.length, categories, templates });
1945
+ } catch (e) {
1946
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
1947
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
1948
+ }
1949
+ });
1939
1950
  app.get("/api/tools", (req, res) => {
1940
1951
  const includeSchema = req.query.include === "schema";
1941
1952
  const q = typeof req.query.q === "string" ? req.query.q.toLowerCase() : "";
@@ -2194,22 +2205,135 @@ ${msg.content}
2194
2205
  res.status(501).json({ error: "Not implemented" });
2195
2206
  });
2196
2207
  app.get("/api/organism/alerts", async (_req, res) => {
2197
- res.status(501).json({ error: "Not implemented" });
2208
+ try {
2209
+ const { getAlerts } = await import("../organism/alertsStore.js");
2210
+ res.json({ alerts: getAlerts() });
2211
+ } catch (e) {
2212
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2213
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
2214
+ }
2198
2215
  });
2199
2216
  app.get("/api/organism/alerts/stats", async (_req, res) => {
2200
- res.status(501).json({ error: "Not implemented" });
2217
+ try {
2218
+ const { getAlertStats } = await import("../organism/alertsStore.js");
2219
+ res.json(getAlertStats());
2220
+ } catch (e) {
2221
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2222
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
2223
+ }
2201
2224
  });
2202
2225
  app.get("/api/organism/alerts/config", async (_req, res) => {
2203
- res.status(501).json({ error: "Not implemented" });
2226
+ try {
2227
+ const { getAlertConfig } = await import("../organism/alertsStore.js");
2228
+ res.json(getAlertConfig());
2229
+ } catch (e) {
2230
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2231
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
2232
+ }
2204
2233
  });
2205
2234
  app.post("/api/organism/alerts/config", async (_req, res) => {
2206
- res.status(501).json({ error: "Not implemented" });
2235
+ try {
2236
+ const { setAlertConfig } = await import("../organism/alertsStore.js");
2237
+ setAlertConfig(_req.body || {});
2238
+ res.json({ success: true });
2239
+ } catch (e) {
2240
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2241
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
2242
+ }
2207
2243
  });
2208
- app.post("/api/organism/alerts/:id/acknowledge", async (_req, res) => {
2209
- res.status(501).json({ error: "Not implemented" });
2244
+ app.post("/api/organism/alerts/:id/acknowledge", async (req, res) => {
2245
+ try {
2246
+ const { acknowledgeAlert } = await import("../organism/alertsStore.js");
2247
+ const ok = acknowledgeAlert(req.params.id);
2248
+ if (!ok) {
2249
+ res.status(404).json({ error: "Alert not found" });
2250
+ return;
2251
+ }
2252
+ res.json({ success: true });
2253
+ } catch (e) {
2254
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2255
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
2256
+ }
2210
2257
  });
2211
2258
  app.delete("/api/organism/alerts/old", async (_req, res) => {
2212
- res.status(501).json({ error: "Not implemented" });
2259
+ try {
2260
+ const { deleteOldAlerts } = await import("../organism/alertsStore.js");
2261
+ const removed = deleteOldAlerts();
2262
+ res.json({ removed });
2263
+ } catch (e) {
2264
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2265
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
2266
+ }
2267
+ });
2268
+ app.post("/api/eval/run", async (req, res) => {
2269
+ try {
2270
+ const { suite } = req.body;
2271
+ const {
2272
+ runEvalSuite,
2273
+ WIDGET_CREATION_SUITE,
2274
+ SAFETY_SUITE,
2275
+ TOOL_ROUTING_SUITE,
2276
+ GATE_FORMAT_SUITE
2277
+ } = await import("../eval/harness.js");
2278
+ const systemWidgetShortcuts = [
2279
+ { pattern: /\b(?:backups?|snapshots?|archives?)\b/i, source: "system:backup", name: "Backup Manager", w: 6, h: 6 },
2280
+ { pattern: /\b(?:training|train|specialists?|models?)\b/i, source: "system:training", name: "Training Dashboard", w: 6, h: 6 },
2281
+ { pattern: /\b(?:recipes?|playbooks?|workflows?|jarvis)\b/i, source: "system:recipes", name: "Recipe Kitchen", w: 6, h: 6 },
2282
+ { pattern: /\b(?:vram|gpu|memory|nvidia)\b/i, source: "system:vram", name: "VRAM Monitor", w: 6, h: 6 },
2283
+ { pattern: /\b(?:teams?|members?|roles?|permissions?|rbac)\b/i, source: "system:teams", name: "Team Hub", w: 6, h: 6 },
2284
+ { pattern: /\b(?:cron|schedules?|jobs?|timers?)\b/i, source: "system:cron", name: "Cron Scheduler", w: 6, h: 6 },
2285
+ { pattern: /\b(?:checkpoints?|restores?|save state)\b/i, source: "system:checkpoints", name: "Checkpoints", w: 6, h: 5 },
2286
+ { pattern: /\b(?:organism|drives?|safety|alerts?|guardrails?)\b/i, source: "system:organism", name: "Organism Monitor", w: 6, h: 6 },
2287
+ { pattern: /\b(?:fleet|nodes?|routes?|mesh)\b/i, source: "system:fleet", name: "Fleet Router", w: 6, h: 5 },
2288
+ { pattern: /\b(?:captcha|browsers?|form fill|web automation)\b/i, source: "system:browser", name: "Browser Tools", w: 6, h: 5 },
2289
+ { pattern: /\b(?:paperclip|sidecars?|helpers?)\b/i, source: "system:paperclip", name: "Paperclip", w: 6, h: 5 },
2290
+ { pattern: /\b(?:tests?|flaky|failing|coverage|eval)\b/i, source: "system:eval", name: "Test Lab", w: 6, h: 6 }
2291
+ ];
2292
+ const agentCall = async (input, testName) => {
2293
+ const shortcut = systemWidgetShortcuts.find((s) => s.pattern.test(input));
2294
+ if (shortcut) {
2295
+ return {
2296
+ content: `Added the **${shortcut.name}** widget to your canvas.
2297
+
2298
+ _____widget
2299
+ { "name": "${shortcut.name}", "format": "system", "source": "${shortcut.source}", "w": ${shortcut.w}, "h": ${shortcut.h} }`,
2300
+ toolsUsed: []
2301
+ };
2302
+ }
2303
+ const userId = testName ? `eval-${testName.replace(/\s+/g, "-").toLowerCase()}` : "eval-harness";
2304
+ const result2 = await processMessage(input, "eval", userId, {});
2305
+ return {
2306
+ content: result2.content || "",
2307
+ toolsUsed: result2.toolsUsed ?? []
2308
+ };
2309
+ };
2310
+ let cases;
2311
+ switch (suite) {
2312
+ case "widget-creation":
2313
+ cases = WIDGET_CREATION_SUITE;
2314
+ break;
2315
+ case "safety":
2316
+ cases = SAFETY_SUITE;
2317
+ break;
2318
+ case "tool-routing":
2319
+ cases = TOOL_ROUTING_SUITE;
2320
+ break;
2321
+ case "gate-format":
2322
+ cases = GATE_FORMAT_SUITE;
2323
+ break;
2324
+ default:
2325
+ res.status(400).json({ error: `Unknown suite: ${suite}. Choose: widget-creation, safety, tool-routing, gate-format, continuation.` });
2326
+ return;
2327
+ }
2328
+ const result = await runEvalSuite(suite, cases, agentCall);
2329
+ res.json(result);
2330
+ } catch (e) {
2331
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2332
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
2333
+ }
2334
+ });
2335
+ app.get("/api/eval/suites", async (_req, res) => {
2336
+ res.json({ suites: ["widget-creation", "safety", "tool-routing", "gate-format"] });
2213
2337
  });
2214
2338
  app.get("/api/tests/health", async (_req, res) => {
2215
2339
  try {
@@ -2329,9 +2453,16 @@ ${msg.content}
2329
2453
  });
2330
2454
  app.get("/api/test-health/summary", async (_req, res) => {
2331
2455
  try {
2332
- const { getTestHealthSummary } = await import("../testing/testHealthMonitor.js");
2333
- const summary = getTestHealthSummary();
2334
- res.json(summary);
2456
+ const { getTestHealthSummary, getFlakyTests } = await import("../testing/testHealthMonitor.js");
2457
+ const raw = getTestHealthSummary();
2458
+ const flaky = getFlakyTests().length;
2459
+ res.json({
2460
+ total: raw.total,
2461
+ passing: raw.passing,
2462
+ failing: raw.failing,
2463
+ flaky,
2464
+ coverage: raw.coveragePct
2465
+ });
2335
2466
  } catch (e) {
2336
2467
  logger.error(COMPONENT, `Endpoint error: ${e.message}`);
2337
2468
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
@@ -2341,7 +2472,8 @@ ${msg.content}
2341
2472
  try {
2342
2473
  const { getFailingTests } = await import("../testing/testHealthMonitor.js");
2343
2474
  const limit = _req.query.limit ? parseInt(_req.query.limit, 10) : 10;
2344
- const tests = getFailingTests().slice(0, limit);
2475
+ const names = getFailingTests().slice(0, limit);
2476
+ const tests = names.map((name) => ({ name, suite: "", error: "", lastFailed: "", attempts: 0 }));
2345
2477
  res.json({ count: tests.length, tests });
2346
2478
  } catch (e) {
2347
2479
  res.status(500).json({ error: e.message, tests: [] });
@@ -2352,7 +2484,8 @@ ${msg.content}
2352
2484
  const { getFlakyTests } = await import("../testing/testHealthMonitor.js");
2353
2485
  const threshold = _req.query.threshold ? parseFloat(_req.query.threshold) : 0.4;
2354
2486
  const limit = _req.query.limit ? parseInt(_req.query.limit, 10) : 10;
2355
- const tests = getFlakyTests(threshold).slice(0, limit);
2487
+ const names = getFlakyTests(threshold).slice(0, limit);
2488
+ const tests = names.map((name) => ({ name, suite: "", passRate: 0, runs: 0 }));
2356
2489
  res.json({ count: tests.length, threshold, tests });
2357
2490
  } catch (e) {
2358
2491
  res.status(500).json({ error: e.message, tests: [] });
@@ -2784,6 +2917,47 @@ ${msg.content}
2784
2917
  return;
2785
2918
  }
2786
2919
  const safeUserId = channel === "api" ? "api-user" : userId || "api-user";
2920
+ const systemWidgetShortcuts = [
2921
+ { pattern: /\b(?:backups?|snapshots?|archives?)\b/i, source: "system:backup", name: "Backup Manager", w: 6, h: 6 },
2922
+ { pattern: /\b(?:training|train|specialists?|models?)\b/i, source: "system:training", name: "Training Dashboard", w: 6, h: 6 },
2923
+ { pattern: /\b(?:recipes?|playbooks?|workflows?|jarvis)\b/i, source: "system:recipes", name: "Recipe Kitchen", w: 6, h: 6 },
2924
+ { pattern: /\b(?:vram|gpu|memory|nvidia)\b/i, source: "system:vram", name: "VRAM Monitor", w: 6, h: 6 },
2925
+ { pattern: /\b(?:teams?|members?|roles?|permissions?|rbac)\b/i, source: "system:teams", name: "Team Hub", w: 6, h: 6 },
2926
+ { pattern: /\b(?:cron|schedules?|jobs?|timers?)\b/i, source: "system:cron", name: "Cron Scheduler", w: 6, h: 6 },
2927
+ { pattern: /\b(?:checkpoints?|restores?|save state)\b/i, source: "system:checkpoints", name: "Checkpoints", w: 6, h: 5 },
2928
+ { pattern: /\b(?:organism|drives?|safety|alerts?|guardrails?)\b/i, source: "system:organism", name: "Organism Monitor", w: 6, h: 6 },
2929
+ { pattern: /\b(?:fleet|nodes?|routes?|mesh)\b/i, source: "system:fleet", name: "Fleet Router", w: 6, h: 5 },
2930
+ { pattern: /\b(?:captcha|browsers?|form fill|web automation)\b/i, source: "system:browser", name: "Browser Tools", w: 6, h: 5 },
2931
+ { pattern: /\b(?:paperclip|sidecars?|helpers?)\b/i, source: "system:paperclip", name: "Paperclip", w: 6, h: 5 },
2932
+ { pattern: /\b(?:tests?|flaky|failing|coverage|eval)\b/i, source: "system:eval", name: "Test Lab", w: 6, h: 6 }
2933
+ ];
2934
+ const matchedShortcut = systemWidgetShortcuts.find((s) => s.pattern.test(content));
2935
+ if (matchedShortcut) {
2936
+ const gateText = `_____widget
2937
+ { "name": "${matchedShortcut.name}", "format": "system", "source": "${matchedShortcut.source}", "w": ${matchedShortcut.w}, "h": ${matchedShortcut.h} }`;
2938
+ const responseText = `Added the **${matchedShortcut.name}** widget to your canvas.`;
2939
+ titanRequestsTotal.increment({ channel, status: "ok" });
2940
+ if (req.headers.accept === "text/event-stream") {
2941
+ res.setHeader("Content-Type", "text/event-stream");
2942
+ res.flushHeaders();
2943
+ res.write(`event: token
2944
+ data: ${JSON.stringify({ text: responseText })}
2945
+
2946
+ `);
2947
+ res.write(`event: token
2948
+ data: ${JSON.stringify({ text: "\n\n" + gateText })}
2949
+
2950
+ `);
2951
+ res.write(`event: done
2952
+ data: ${JSON.stringify({ content: responseText + "\n\n" + gateText, sessionId: requestedSessionId || null, durationMs: 0, toolsUsed: [] })}
2953
+
2954
+ `);
2955
+ res.end();
2956
+ } else {
2957
+ res.json({ content: responseText + "\n\n" + gateText, sessionId: requestedSessionId || null, toolsUsed: [], model: "system", durationMs: 0 });
2958
+ }
2959
+ return;
2960
+ }
2787
2961
  const startTime = process.hrtime.bigint();
2788
2962
  const wantsSSE = req.headers.accept === "text/event-stream";
2789
2963
  if (requestedSessionId && !/^[a-zA-Z0-9_:-]{1,128}$/.test(requestedSessionId)) {
@@ -3436,6 +3610,119 @@ data: ${JSON.stringify(structured)}
3436
3610
  if (org.tickIntervalMs !== void 0) draft.organism.tickIntervalMs = Number(org.tickIntervalMs);
3437
3611
  changedFields.push("organism");
3438
3612
  }
3613
+ const handleNestedBool = (section, key, target) => {
3614
+ const sec = body[section];
3615
+ if (sec && key in sec) {
3616
+ target[key] = Boolean(sec[key]);
3617
+ changedFields.push(`${section}.${key}`);
3618
+ }
3619
+ };
3620
+ if (body.autonomy !== void 0 && typeof body.autonomy === "object") {
3621
+ const a = body.autonomy;
3622
+ if (!draft.autonomy) draft.autonomy = {};
3623
+ const da = draft.autonomy;
3624
+ if (a.mode !== void 0) da.mode = a.mode;
3625
+ if (a.autoProposeGoals !== void 0) da.autoProposeGoals = Boolean(a.autoProposeGoals);
3626
+ if (a.proactiveInitiative !== void 0) da.proactiveInitiative = Boolean(a.proactiveInitiative);
3627
+ changedFields.push("autonomy");
3628
+ }
3629
+ if (body.selfMod !== void 0 && typeof body.selfMod === "object") {
3630
+ const s = body.selfMod;
3631
+ if (!draft.selfMod) draft.selfMod = {};
3632
+ if (s.enabled !== void 0) draft.selfMod.enabled = Boolean(s.enabled);
3633
+ if (s.autoPR !== void 0) draft.selfMod.autoPR = Boolean(s.autoPR);
3634
+ changedFields.push("selfMod");
3635
+ }
3636
+ if (body.commandPost !== void 0 && typeof body.commandPost === "object") {
3637
+ const cp = body.commandPost;
3638
+ if (!draft.commandPost) draft.commandPost = {};
3639
+ if (cp.enabled !== void 0) draft.commandPost.enabled = Boolean(cp.enabled);
3640
+ changedFields.push("commandPost");
3641
+ }
3642
+ if (body.mesh !== void 0 && typeof body.mesh === "object") {
3643
+ const m = body.mesh;
3644
+ if (!draft.mesh) draft.mesh = {};
3645
+ if (m.enabled !== void 0) draft.mesh.enabled = Boolean(m.enabled);
3646
+ changedFields.push("mesh");
3647
+ }
3648
+ if (body.autopilot !== void 0 && typeof body.autopilot === "object") {
3649
+ const ap = body.autopilot;
3650
+ if (!draft.autopilot) draft.autopilot = {};
3651
+ if (ap.enabled !== void 0) draft.autopilot.enabled = Boolean(ap.enabled);
3652
+ if (ap.goals !== void 0 && typeof ap.goals === "object") {
3653
+ const apg = ap.goals;
3654
+ const dag = draft.autopilot;
3655
+ if (!dag.goals) dag.goals = {};
3656
+ if (apg.selfInitiate !== void 0) dag.goals.selfInitiate = Boolean(apg.selfInitiate);
3657
+ }
3658
+ changedFields.push("autopilot");
3659
+ }
3660
+ if (body.brain !== void 0 && typeof body.brain === "object") {
3661
+ const b = body.brain;
3662
+ if (!draft.brain) draft.brain = {};
3663
+ if (b.enabled !== void 0) draft.brain.enabled = Boolean(b.enabled);
3664
+ changedFields.push("brain");
3665
+ }
3666
+ if (body.mcp !== void 0 && typeof body.mcp === "object") {
3667
+ const mcp = body.mcp;
3668
+ if (!draft.mcp) draft.mcp = { server: {} };
3669
+ if (mcp.server !== void 0 && typeof mcp.server === "object") {
3670
+ const srv = mcp.server;
3671
+ const dmcp = draft.mcp;
3672
+ if (!dmcp.server) dmcp.server = {};
3673
+ if (srv.enabled !== void 0) dmcp.server.enabled = Boolean(srv.enabled);
3674
+ }
3675
+ changedFields.push("mcp");
3676
+ }
3677
+ if (body.training !== void 0 && typeof body.training === "object") {
3678
+ const t = body.training;
3679
+ if (!draft.training) draft.training = {};
3680
+ if (t.enabled !== void 0) draft.training.enabled = Boolean(t.enabled);
3681
+ changedFields.push("training");
3682
+ }
3683
+ if (body.teams !== void 0 && typeof body.teams === "object") {
3684
+ const t = body.teams;
3685
+ if (!draft.teams) draft.teams = {};
3686
+ if (t.enabled !== void 0) draft.teams.enabled = Boolean(t.enabled);
3687
+ changedFields.push("teams");
3688
+ }
3689
+ if (body.tunnel !== void 0 && typeof body.tunnel === "object") {
3690
+ const t = body.tunnel;
3691
+ if (!draft.tunnel) draft.tunnel = {};
3692
+ if (t.enabled !== void 0) draft.tunnel.enabled = Boolean(t.enabled);
3693
+ changedFields.push("tunnel");
3694
+ }
3695
+ if (body.vault !== void 0 && typeof body.vault === "object") {
3696
+ const v = body.vault;
3697
+ const dsec = draft.security;
3698
+ if (!dsec.vault) dsec.vault = {};
3699
+ if (v.enabled !== void 0) dsec.vault.enabled = Boolean(v.enabled);
3700
+ changedFields.push("security.vault");
3701
+ }
3702
+ if (body.capsolver !== void 0 && typeof body.capsolver === "object") {
3703
+ const c = body.capsolver;
3704
+ if (!draft.capsolver) draft.capsolver = {};
3705
+ if (c.enabled !== void 0) draft.capsolver.enabled = Boolean(c.enabled);
3706
+ changedFields.push("capsolver");
3707
+ }
3708
+ if (body.deliberation !== void 0 && typeof body.deliberation === "object") {
3709
+ const d = body.deliberation;
3710
+ if (!draft.deliberation) draft.deliberation = {};
3711
+ if (d.autoDetect !== void 0) draft.deliberation.autoDetect = Boolean(d.autoDetect);
3712
+ changedFields.push("deliberation");
3713
+ }
3714
+ if (body.selfImprove !== void 0 && typeof body.selfImprove === "object") {
3715
+ const si = body.selfImprove;
3716
+ if (!draft.selfImprove) draft.selfImprove = {};
3717
+ if (si.autoApply !== void 0) draft.selfImprove.autoApply = Boolean(si.autoApply);
3718
+ changedFields.push("selfImprove");
3719
+ }
3720
+ if (body.memory !== void 0 && typeof body.memory === "object") {
3721
+ const mem = body.memory;
3722
+ if (!draft.memory) draft.memory = {};
3723
+ if (mem.vectorSearchEnabled !== void 0) draft.memory.vectorSearchEnabled = Boolean(mem.vectorSearchEnabled);
3724
+ changedFields.push("memory");
3725
+ }
3439
3726
  if (changedFields.length === 0) {
3440
3727
  const validFields = [
3441
3728
  "model",
@@ -3471,7 +3758,22 @@ data: ${JSON.stringify(structured)}
3471
3758
  "homeAssistantToken",
3472
3759
  "voice",
3473
3760
  "nvidia",
3474
- "organism"
3761
+ "organism",
3762
+ "autonomy",
3763
+ "selfMod",
3764
+ "commandPost",
3765
+ "mesh",
3766
+ "autopilot",
3767
+ "brain",
3768
+ "mcp",
3769
+ "training",
3770
+ "teams",
3771
+ "tunnel",
3772
+ "vault",
3773
+ "capsolver",
3774
+ "deliberation",
3775
+ "selfImprove",
3776
+ "memory"
3475
3777
  ];
3476
3778
  res.status(400).json({ error: "No recognized fields in request body", validFields });
3477
3779
  return;
@@ -4233,7 +4535,7 @@ data: ${JSON.stringify(structured)}
4233
4535
  app.get("/api/backup/list", async (_req, res) => {
4234
4536
  try {
4235
4537
  const { listBackups } = await import("../storage/backup.js");
4236
- res.json(listBackups());
4538
+ res.json({ backups: listBackups() });
4237
4539
  } catch (e) {
4238
4540
  logger.error(COMPONENT, `Endpoint error: ${e.message}`);
4239
4541
  res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
@@ -7001,8 +7303,8 @@ Retrying after failure: ${run.error || "unknown error"}`;
7001
7303
  sessionAbortTimes.set(requestedSessionId, Date.now());
7002
7304
  }
7003
7305
  let effectiveTtsEngine = ttsEngine;
7004
- let effectiveTtsUrl = ttsUrl;
7005
- let effectiveTtsModel = "f5-tts-mlx";
7306
+ const effectiveTtsUrl = ttsUrl;
7307
+ const effectiveTtsModel = "f5-tts-mlx";
7006
7308
  try {
7007
7309
  const probe = await fetch(`${effectiveTtsUrl}/health`, { signal: AbortSignal.timeout(5e3) });
7008
7310
  if (!probe.ok) effectiveTtsEngine = "unavailable";
@@ -8341,32 +8643,6 @@ td{padding:10px 12px;font-size:14px;vertical-align:middle}
8341
8643
  logger.info(COMPONENT, `Messenger webhook: /api/messenger/webhook + /messenger/webhook (verify token: ${messengerAdapter.getVerifyToken()})`);
8342
8644
  }
8343
8645
  {
8344
- let getTwilioConfig2 = function() {
8345
- const cfg = loadConfig();
8346
- const c = cfg.channels?.twilio || {};
8347
- return {
8348
- authToken: c.authToken || process.env.TWILIO_AUTH_TOKEN || "",
8349
- phoneNumber: c.phoneNumber || process.env.TWILIO_PHONE_NUMBER || "",
8350
- voice: c.voice || "andrew",
8351
- allowedCallers: c.allowedCallers || [],
8352
- publicHost: c.publicHost || process.env.TWILIO_PUBLIC_HOST || ""
8353
- };
8354
- }, computeSignedUrl2 = function(req) {
8355
- const cfg = getTwilioConfig2();
8356
- const host2 = cfg.publicHost.replace(/\/$/, "") || `https://${req.headers.host}`;
8357
- return host2 + req.originalUrl;
8358
- }, checkTwilioAuth2 = function(req) {
8359
- const { authToken } = getTwilioConfig2();
8360
- if (!authToken) {
8361
- logger.warn(COMPONENT, "Twilio authToken not configured \u2014 signature check SKIPPED");
8362
- return true;
8363
- }
8364
- const signature = req.headers["x-twilio-signature"] || "";
8365
- const url = computeSignedUrl2(req);
8366
- const params = req.body || {};
8367
- return validateTwilioSignature(authToken, signature, url, params);
8368
- };
8369
- var getTwilioConfig = getTwilioConfig2, computeSignedUrl = computeSignedUrl2, checkTwilioAuth = checkTwilioAuth2;
8370
8646
  const {
8371
8647
  twimlPlayAndGather,
8372
8648
  twimlPauseAndRedirect,
@@ -8387,14 +8663,41 @@ td{padding:10px 12px;font-size:14px;vertical-align:middle}
8387
8663
  } = await import("../channels/twilio-voice.js");
8388
8664
  const twilioCfg = config.channels?.twilio;
8389
8665
  const twilioEnabled = twilioCfg?.enabled !== false;
8666
+ const getTwilioConfig = () => {
8667
+ const cfg = loadConfig();
8668
+ const c = cfg.channels?.twilio || {};
8669
+ return {
8670
+ authToken: c.authToken || process.env.TWILIO_AUTH_TOKEN || "",
8671
+ phoneNumber: c.phoneNumber || process.env.TWILIO_PHONE_NUMBER || "",
8672
+ voice: c.voice || "andrew",
8673
+ allowedCallers: c.allowedCallers || [],
8674
+ publicHost: c.publicHost || process.env.TWILIO_PUBLIC_HOST || ""
8675
+ };
8676
+ };
8390
8677
  const urlEncoded = express.urlencoded({ extended: false });
8678
+ const computeSignedUrl = (req) => {
8679
+ const cfg = getTwilioConfig();
8680
+ const host2 = cfg.publicHost.replace(/\/$/, "") || `https://${req.headers.host}`;
8681
+ return host2 + req.originalUrl;
8682
+ };
8683
+ const checkTwilioAuth = (req) => {
8684
+ const { authToken } = getTwilioConfig();
8685
+ if (!authToken) {
8686
+ logger.warn(COMPONENT, "Twilio authToken not configured \u2014 signature check SKIPPED");
8687
+ return true;
8688
+ }
8689
+ const signature = req.headers["x-twilio-signature"] || "";
8690
+ const url = computeSignedUrl(req);
8691
+ const params = req.body || {};
8692
+ return validateTwilioSignature(authToken, signature, url, params);
8693
+ };
8391
8694
  app.post("/api/twilio/voice-webhook", urlEncoded, async (req, res) => {
8392
8695
  try {
8393
8696
  if (!twilioEnabled) {
8394
8697
  res.type("text/xml").send(twimlReject());
8395
8698
  return;
8396
8699
  }
8397
- if (!checkTwilioAuth2(req)) {
8700
+ if (!checkTwilioAuth(req)) {
8398
8701
  logger.warn(COMPONENT, "Twilio voice-webhook: signature invalid");
8399
8702
  res.status(403).send("forbidden");
8400
8703
  return;
@@ -8403,7 +8706,7 @@ td{padding:10px 12px;font-size:14px;vertical-align:middle}
8403
8706
  const to = req.body?.To || "";
8404
8707
  const direction = req.body?.Direction || "inbound";
8405
8708
  const callSid = req.body?.CallSid || "";
8406
- const { allowedCallers, voice, publicHost } = getTwilioConfig2();
8709
+ const { allowedCallers, voice, publicHost } = getTwilioConfig();
8407
8710
  const isOutbound = direction.startsWith("outbound");
8408
8711
  const humanNumber = isOutbound ? to : from;
8409
8712
  if (allowedCallers.length > 0 && !isAllowedCaller(humanNumber, allowedCallers)) {
@@ -8433,7 +8736,7 @@ td{padding:10px 12px;font-size:14px;vertical-align:middle}
8433
8736
  res.type("text/xml").send(twimlReject());
8434
8737
  return;
8435
8738
  }
8436
- if (!checkTwilioAuth2(req)) {
8739
+ if (!checkTwilioAuth(req)) {
8437
8740
  res.status(403).send("forbidden");
8438
8741
  return;
8439
8742
  }
@@ -8442,7 +8745,7 @@ td{padding:10px 12px;font-size:14px;vertical-align:middle}
8442
8745
  const to = req.body?.To || "";
8443
8746
  const direction = req.body?.Direction || "inbound";
8444
8747
  const speechResult = (req.body?.SpeechResult || "").trim();
8445
- const { voice, publicHost, allowedCallers } = getTwilioConfig2();
8748
+ const { voice, publicHost, allowedCallers } = getTwilioConfig();
8446
8749
  const isOutbound = direction.startsWith("outbound");
8447
8750
  const humanNumber = isOutbound ? to : from;
8448
8751
  if (allowedCallers.length > 0 && !isAllowedCaller(humanNumber, allowedCallers)) {
@@ -8528,7 +8831,7 @@ td{padding:10px 12px;font-size:14px;vertical-align:middle}
8528
8831
  res.type("text/xml").send(twimlReject());
8529
8832
  return;
8530
8833
  }
8531
- if (!checkTwilioAuth2(req)) {
8834
+ if (!checkTwilioAuth(req)) {
8532
8835
  logger.warn(COMPONENT, `voice-poll signature rejected (method=${req.method}, jobId=${req.query.jobId})`);
8533
8836
  res.status(403).send("forbidden");
8534
8837
  return;
@@ -8536,7 +8839,7 @@ td{padding:10px 12px;font-size:14px;vertical-align:middle}
8536
8839
  const jobId = req.query.jobId || "";
8537
8840
  const job = getVoiceJob(jobId);
8538
8841
  logger.info(COMPONENT, `voice-poll method=${req.method} jobId=${jobId.slice(0, 8)} status=${job?.status || "missing"}`);
8539
- const { voice, publicHost } = getTwilioConfig2();
8842
+ const { voice, publicHost } = getTwilioConfig();
8540
8843
  const host2 = publicHost.replace(/\/$/, "") || `https://${req.headers.host}`;
8541
8844
  const pollUrl = `${host2}/api/twilio/voice-poll?jobId=${jobId}`;
8542
8845
  const gatherUrl = `${host2}/api/twilio/voice-gather`;
@@ -9077,7 +9380,7 @@ Your task: ${monitor.prompt}`;
9077
9380
  logger.info(COMPONENT, "Health monitor started (60s interval)");
9078
9381
  const SECRET_PATTERNS = [
9079
9382
  // Bearer / Basic auth headers
9080
- /\b[Bb]earer\s+[A-Za-z0-9_\-\.]{20,}/g,
9383
+ /\b[Bb]earer\s+[A-Za-z0-9_\-.]{20,}/g,
9081
9384
  /\b[Bb]asic\s+[A-Za-z0-9+/=]{20,}/g,
9082
9385
  // API key prefixes (OpenAI, Anthropic, Groq, etc.)
9083
9386
  /\b(sk|pk)-[A-Za-z0-9]{20,}/g,
@@ -9184,12 +9487,9 @@ Channels: ${Array.from(channels.values()).map((c) => `${c.displayName} (${c.getS
9184
9487
  logger.info(COMPONENT, `Skills: ${getSkills().length} loaded`);
9185
9488
  logger.info(COMPONENT, `Tools: ${getRegisteredTools().length} registered`);
9186
9489
  try {
9187
- const { readFileSync, existsSync } = require("fs");
9188
- const { join: join2 } = require("path");
9189
- const { homedir: homedir2 } = require("os");
9190
- const markerPath = join2(homedir2(), ".titan", "install-marker.json");
9191
- if (existsSync(markerPath)) {
9192
- const marker = JSON.parse(readFileSync(markerPath, "utf-8"));
9490
+ const markerPath = join(homedir(), ".titan", "install-marker.json");
9491
+ if (fs.existsSync(markerPath)) {
9492
+ const marker = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
9193
9493
  if (marker.previousVersion && marker.previousVersion !== TITAN_VERSION) {
9194
9494
  logger.info(COMPONENT, `
9195
9495
  \u{1F680} Welcome to TITAN v${TITAN_VERSION}! Upgraded from v${marker.previousVersion}.`);