titan-agent 6.1.0 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +25 -5
  2. package/dist/config/schema.js +23 -0
  3. package/dist/config/schema.js.map +1 -1
  4. package/dist/eval/harness.js +2 -1
  5. package/dist/eval/harness.js.map +1 -1
  6. package/dist/gateway/routes/telephony.js +497 -0
  7. package/dist/gateway/routes/telephony.js.map +1 -0
  8. package/dist/gateway/routes/tests.js +3 -1
  9. package/dist/gateway/routes/tests.js.map +1 -1
  10. package/dist/gateway/server.js +6 -0
  11. package/dist/gateway/server.js.map +1 -1
  12. package/dist/skills/builtin/phone_call.js +66 -0
  13. package/dist/skills/builtin/phone_call.js.map +1 -0
  14. package/dist/skills/registry.js +2 -0
  15. package/dist/skills/registry.js.map +1 -1
  16. package/dist/telephony/dograhClient.js +175 -0
  17. package/dist/telephony/dograhClient.js.map +1 -0
  18. package/dist/telephony/optOutStore.js +71 -0
  19. package/dist/telephony/optOutStore.js.map +1 -0
  20. package/dist/telephony/phonePolicy.js +120 -0
  21. package/dist/telephony/phonePolicy.js.map +1 -0
  22. package/dist/utils/constants.js +1 -1
  23. package/dist/utils/constants.js.map +1 -1
  24. package/dist/utils/safety.js +12 -1
  25. package/dist/utils/safety.js.map +1 -1
  26. package/package.json +1 -1
  27. package/ui/dist/assets/AuditPanel-DuxwAH5U.js +1 -0
  28. package/ui/dist/assets/{AutonomyPanel-zsQ6kIP7.js → AutonomyPanel-VWBgurnv.js} +1 -1
  29. package/ui/dist/assets/{AutopilotPanel-NlxdRleV.js → AutopilotPanel-Dw8MYOtj.js} +1 -1
  30. package/ui/dist/assets/{AutoresearchPanel-CHeEcUmN.js → AutoresearchPanel-CpEX0Zub.js} +1 -1
  31. package/ui/dist/assets/{BackupPanel-C7RHr9Gu.js → BackupPanel-n41_6i6x.js} +1 -1
  32. package/ui/dist/assets/{BrowserPanel-Cxu9bCLW.js → BrowserPanel-DEBTIt6h.js} +1 -1
  33. package/ui/dist/assets/{CPActivity-B0QSTuSq.js → CPActivity-em82PTiQ.js} +1 -1
  34. package/ui/dist/assets/{CPAgentDetail-BUqGIkaM.js → CPAgentDetail-Bd8lKqcL.js} +1 -1
  35. package/ui/dist/assets/{CPAgents-DO0RVLLA.js → CPAgents-GRBrDd6c.js} +1 -1
  36. package/ui/dist/assets/{CPApprovals-DRceMgME.js → CPApprovals-DdLaCgJZ.js} +1 -1
  37. package/ui/dist/assets/{CPCosts-D9l4mkiW.js → CPCosts-Cm8Hv9fv.js} +1 -1
  38. package/ui/dist/assets/{CPDashboard-DaCCdMjs.js → CPDashboard-Dm9vUKLu.js} +1 -1
  39. package/ui/dist/assets/{CPFiles-DTphWHsR.js → CPFiles-DxMn1x2m.js} +1 -1
  40. package/ui/dist/assets/{CPGoals-DeJBRV3M.js → CPGoals-Wr1tCn6y.js} +1 -1
  41. package/ui/dist/assets/{CPInbox-DsT5O636.js → CPInbox-CGYpK1rG.js} +2 -2
  42. package/ui/dist/assets/{CPIssueDetail-D8pG5Tnq.js → CPIssueDetail-dTU59sgQ.js} +1 -1
  43. package/ui/dist/assets/{CPIssues-oeffaZSb.js → CPIssues-wgjI2eqS.js} +1 -1
  44. package/ui/dist/assets/CPLayout-CBtGEYSs.js +2 -0
  45. package/ui/dist/assets/{CPOrg-CesGXL8r.js → CPOrg-eT6LNmyy.js} +1 -1
  46. package/ui/dist/assets/{CPRuns-CfYgZUGv.js → CPRuns-B3sdo5g2.js} +1 -1
  47. package/ui/dist/assets/{CPSocial-CK0qGSa0.js → CPSocial-B99Mfee_.js} +3 -3
  48. package/ui/dist/assets/{CPVoice-iIkC-KnQ.js → CPVoice-D4J6yFWu.js} +1 -1
  49. package/ui/dist/assets/{ChannelsPanel-DTS5SXlC.js → ChannelsPanel--lhZqYLD.js} +1 -1
  50. package/ui/dist/assets/{CheckpointsPanel-c5dwo2eW.js → CheckpointsPanel-DaOIUcEi.js} +1 -1
  51. package/ui/dist/assets/{CommandPostHub-BsdfjoBN.js → CommandPostHub-Pq9lQMW5.js} +3 -3
  52. package/ui/dist/assets/{CronPanel-BkeP_mIS.js → CronPanel-CywjWM7O.js} +1 -1
  53. package/ui/dist/assets/{DataTable-Gajb24Ni.js → DataTable-Bt0cxehB.js} +1 -1
  54. package/ui/dist/assets/{DreamPanel-CGrozxjo.js → DreamPanel-Ct7MuLzD.js} +1 -1
  55. package/ui/dist/assets/{EmptyState-B3lWjJU-.js → EmptyState-iLOrbdU7.js} +1 -1
  56. package/ui/dist/assets/{EvalHarnessPanel-DQ4O9caO.js → EvalHarnessPanel-7KKDzeXw.js} +2 -2
  57. package/ui/dist/assets/{EvalPanel-BOUHXllR.js → EvalPanel-70LzkLDO.js} +1 -1
  58. package/ui/dist/assets/{FileViewer-00_4kM0_.js → FileViewer-h6A7YYTm.js} +1 -1
  59. package/ui/dist/assets/{FilesPanel-DISpHxEJ.js → FilesPanel-l8qC6eLD.js} +1 -1
  60. package/ui/dist/assets/{FleetPanel-Vz7_88zK.js → FleetPanel-Z3ResHIb.js} +1 -1
  61. package/ui/dist/assets/{HomelabPanel-CCqGyERB.js → HomelabPanel-9QhrXzo1.js} +1 -1
  62. package/ui/dist/assets/InfraView-G1q6vF0Q.js +2 -0
  63. package/ui/dist/assets/{InlineEditableField-BGjdtpGQ.js → InlineEditableField-DsVfGRH2.js} +1 -1
  64. package/ui/dist/assets/{Input-CzDjGc5Y.js → Input-Bzh0-cXU.js} +1 -1
  65. package/ui/dist/assets/{IntegrationsPanel-BOe9ybbQ.js → IntegrationsPanel-DBOdLYJ8.js} +1 -1
  66. package/ui/dist/assets/IntelligenceView-J8Nbh4zs.js +2 -0
  67. package/ui/dist/assets/{LearningPanel-5zb6yYZW.js → LearningPanel-CiSLVxUX.js} +1 -1
  68. package/ui/dist/assets/{LogsPanel-C_1bqy9b.js → LogsPanel-CPAAItsq.js} +1 -1
  69. package/ui/dist/assets/{McpPanel-CY7_7erZ.js → McpPanel-MYWE2Zqs.js} +1 -1
  70. package/ui/dist/assets/{MemoryGraphPanel-DsF8er1w.js → MemoryGraphPanel-BFr51D_1.js} +1 -1
  71. package/ui/dist/assets/{MemoryWikiPanel-D4KPpOX7.js → MemoryWikiPanel-BA7GUxhg.js} +1 -1
  72. package/ui/dist/assets/{MeshPanel-C_cNMUcI.js → MeshPanel-5BDbwBVR.js} +1 -1
  73. package/ui/dist/assets/{MissionCanvas-BMw5bJcw.js → MissionCanvas-CrDZIQX6.js} +1 -1
  74. package/ui/dist/assets/{MissionChat-CrgPUYhU.js → MissionChat-BDTWMQjj.js} +1 -1
  75. package/ui/dist/assets/{MissionLibrary-ASkOLTWA.js → MissionLibrary-DEPH2eEu.js} +1 -1
  76. package/ui/dist/assets/{MissionStart-DDoZMkie.js → MissionStart-COLFZLHB.js} +1 -1
  77. package/ui/dist/assets/{Modal-DTzLKnUQ.js → Modal-nvni-LGR.js} +1 -1
  78. package/ui/dist/assets/{NvidiaPanel-dtkQr6gS.js → NvidiaPanel-BCgVrdWt.js} +1 -1
  79. package/ui/dist/assets/{OrganismPanel-DLfHCoYh.js → OrganismPanel-Cab4MV9i.js} +1 -1
  80. package/ui/dist/assets/{OverviewPanel-COcMqu-y.js → OverviewPanel-isNUxBki.js} +1 -1
  81. package/ui/dist/assets/{PageHeader-BXEGoqDX.js → PageHeader-BJ1jHtOy.js} +1 -1
  82. package/ui/dist/assets/{PersonaProfilesPanel-BKWsHE1j.js → PersonaProfilesPanel-BGiDuDPj.js} +1 -1
  83. package/ui/dist/assets/{PersonasPanel-tsXvN_8B.js → PersonasPanel-4-USk8fm.js} +1 -1
  84. package/ui/dist/assets/PhoneDeskPanel-CUz5YVUY.js +11 -0
  85. package/ui/dist/assets/{RecipesPanel-B8GOxD4_.js → RecipesPanel-DJd6Otdy.js} +1 -1
  86. package/ui/dist/assets/{SecurityPanel-CqhRhXZ8.js → SecurityPanel-CKqTnhNY.js} +1 -1
  87. package/ui/dist/assets/{SelfImprovePanel-Di0XhdJJ.js → SelfImprovePanel-BiCjKpTM.js} +1 -1
  88. package/ui/dist/assets/{SelfProposalsPanel-CbgdHeQY.js → SelfProposalsPanel-BQCwEHdF.js} +1 -1
  89. package/ui/dist/assets/{SessionsPanel-DBdDx51G.js → SessionsPanel-MTMSODP7.js} +1 -1
  90. package/ui/dist/assets/{SessionsTab-C8cEt83E.js → SessionsTab-CEFFZWMK.js} +1 -1
  91. package/ui/dist/assets/{SettingsPanel-BAenxGtq.js → SettingsPanel-CniESkQQ.js} +1 -1
  92. package/ui/dist/assets/SettingsView-2AwXqlvc.js +2 -0
  93. package/ui/dist/assets/{SkeletonLoader-DGQ0Q7vy.js → SkeletonLoader-IpWkLmoy.js} +1 -1
  94. package/ui/dist/assets/{SkillsPanel-r91e0ype.js → SkillsPanel-CCP-gCR4.js} +1 -1
  95. package/ui/dist/assets/{SomaView-CGoJDjEX.js → SomaView-BK_thK9H.js} +1 -1
  96. package/ui/dist/assets/{StatCard-D0Q7T1EQ.js → StatCard-I0fVw2CH.js} +1 -1
  97. package/ui/dist/assets/{StatusBadge-Do8GEljT.js → StatusBadge-e3tFf05B.js} +1 -1
  98. package/ui/dist/assets/{Tabs-B-g5Ghos.js → Tabs-vGpPiBzr.js} +1 -1
  99. package/ui/dist/assets/{TeamsPanel-BrT1eigj.js → TeamsPanel-AFIQBzRK.js} +1 -1
  100. package/ui/dist/assets/{TelemetryPanel-C9vV_ydx.js → TelemetryPanel-DKZGVmcp.js} +1 -1
  101. package/ui/dist/assets/{TitanCanvas-BPVdmdYo.js → TitanCanvas-CqvuuIjd.js} +4 -4
  102. package/ui/dist/assets/{ToolsView-COEuSnLh.js → ToolsView-DnIkrUvP.js} +2 -2
  103. package/ui/dist/assets/{Tooltip-Ip86jr39.js → Tooltip-DNc5AWi0.js} +1 -1
  104. package/ui/dist/assets/{TraceViewer-BcK-DNNV.js → TraceViewer-MD9YRFVc.js} +1 -1
  105. package/ui/dist/assets/{TrainingPanel-s5yKr9vs.js → TrainingPanel-BfXoR7_N.js} +1 -1
  106. package/ui/dist/assets/{VoiceOverlay-BKtgGRK8.js → VoiceOverlay-Bl81c4yf.js} +1 -1
  107. package/ui/dist/assets/{VramPanel-C2vulpkc.js → VramPanel-Ba8H8Qs2.js} +1 -1
  108. package/ui/dist/assets/{WatchView-GXZc0v1H.js → WatchView-CoLHPGd7.js} +1 -1
  109. package/ui/dist/assets/{WorkTab-CCQpoRi6.js → WorkTab-UppfR_AF.js} +1 -1
  110. package/ui/dist/assets/{WorkflowsPanel-CjU5wOPV.js → WorkflowsPanel-CdN7Epxr.js} +1 -1
  111. package/ui/dist/assets/{archive-Cty0CkR8.js → archive-DWgNklp0.js} +1 -1
  112. package/ui/dist/assets/{arrow-left-pfqo1Fw_.js → arrow-left-CW5NV7E0.js} +1 -1
  113. package/ui/dist/assets/{book-open-B6YYAKPS.js → book-open-DRI_Y6cW.js} +1 -1
  114. package/ui/dist/assets/{bot-CF5xQRRg.js → bot-DSGZRM5O.js} +1 -1
  115. package/ui/dist/assets/{chart-column-DmdEaxIF.js → chart-column-D6yK9tts.js} +1 -1
  116. package/ui/dist/assets/{check-B06kj6lQ.js → check-Cxp-6q4k.js} +1 -1
  117. package/ui/dist/assets/{chevron-right-BANsqvxz.js → chevron-right-DkIMxdwH.js} +1 -1
  118. package/ui/dist/assets/{chevron-up-DZTojCej.js → chevron-up-BbDu8x1d.js} +1 -1
  119. package/ui/dist/assets/{circle-check-big-2NAZFdp9.js → circle-check-big-DnJPBAGU.js} +1 -1
  120. package/ui/dist/assets/{circle-stop-AU_Yx5I3.js → circle-stop-DOe0OacY.js} +1 -1
  121. package/ui/dist/assets/clipboard-list-Dj5RQbT8.js +6 -0
  122. package/ui/dist/assets/{clock-DXDS0v3F.js → clock-BS8fh7Rt.js} +1 -1
  123. package/ui/dist/assets/{dollar-sign-DLLEescF.js → dollar-sign-DMXBeaTj.js} +1 -1
  124. package/ui/dist/assets/{download-CsU2Repn.js → download-BykARpbG.js} +1 -1
  125. package/ui/dist/assets/{external-link-Chx9uvWw.js → external-link-BfXhMi9L.js} +1 -1
  126. package/ui/dist/assets/{file-text-RZVxniN-.js → file-text-CUkSytWL.js} +1 -1
  127. package/ui/dist/assets/{flask-conical-BMH8pJKb.js → flask-conical-BS4cuENu.js} +1 -1
  128. package/ui/dist/assets/{folder-DgwmNgPQ.js → folder-BDwoUobN.js} +1 -1
  129. package/ui/dist/assets/{funnel-BR7hTVZW.js → funnel-xr6Dd1ub.js} +1 -1
  130. package/ui/dist/assets/{git-branch-B5R0dCYG.js → git-branch-C0Qk52W8.js} +1 -1
  131. package/ui/dist/assets/{index-BUCi9exS.js → index-CmaGnHzR.js} +30 -30
  132. package/ui/dist/assets/index-OZm1iJy9.css +1 -0
  133. package/ui/dist/assets/{layers-DGEsxpYY.js → layers-DPHYdeRk.js} +1 -1
  134. package/ui/dist/assets/{legacy-DgcX067d.js → legacy-DzagVk5C.js} +1 -1
  135. package/ui/dist/assets/{lightbulb-BvNv5S2z.js → lightbulb-Bj6cj6Xl.js} +1 -1
  136. package/ui/dist/assets/{list-todo-BYS3IIni.js → list-todo-Cm4jNFHV.js} +1 -1
  137. package/ui/dist/assets/{message-square-I2Wa53bg.js → message-square-BrWLAovN.js} +1 -1
  138. package/ui/dist/assets/{mic-CLF69wwf.js → mic-BXNK4d7X.js} +1 -1
  139. package/ui/dist/assets/{missions-a23pAr7Z.js → missions-DEjMYSre.js} +1 -1
  140. package/ui/dist/assets/{network-DPPIz6Io.js → network-CrQn1GKv.js} +1 -1
  141. package/ui/dist/assets/{pause-4M6u54u8.js → pause-DS5xgDMT.js} +1 -1
  142. package/ui/dist/assets/{pencil-CmthDu_c.js → pencil-DAI6FtFQ.js} +1 -1
  143. package/ui/dist/assets/{play-C5er2jT3.js → play-DxBy837b.js} +1 -1
  144. package/ui/dist/assets/{plug-Cj409xHk.js → plug-D0Bstfm0.js} +1 -1
  145. package/ui/dist/assets/{plus-C63FIH_4.js → plus-DxgNIt8h.js} +1 -1
  146. package/ui/dist/assets/{proxy-CV0RFkNZ.js → proxy-KYomRnLW.js} +1 -1
  147. package/ui/dist/assets/{radio-DJcw-8Fl.js → radio-2vniRFTO.js} +1 -1
  148. package/ui/dist/assets/{refresh-cw-D7RrQJQl.js → refresh-cw-BFBnnWj8.js} +1 -1
  149. package/ui/dist/assets/{rotate-ccw-wDOKy6Sz.js → rotate-ccw-B9wWlKRc.js} +1 -1
  150. package/ui/dist/assets/{save-Cy8s5sjb.js → save--wCYre_u.js} +1 -1
  151. package/ui/dist/assets/{search-BXNHN7Ra.js → search-DOD1aOm8.js} +1 -1
  152. package/ui/dist/assets/{send-DU7Jy0ph.js → send-Bb57UxT7.js} +1 -1
  153. package/ui/dist/assets/{server-Cg6QHqG3.js → server-CBsCfofY.js} +1 -1
  154. package/ui/dist/assets/{shield-check-nn5B33Zk.js → shield-check-B24N03VU.js} +1 -1
  155. package/ui/dist/assets/{terminal-D3qHXhYo.js → terminal-CZ77Swtc.js} +1 -1
  156. package/ui/dist/assets/{test-tube-qD4BUs0u.js → test-tube-CkkvIu5j.js} +1 -1
  157. package/ui/dist/assets/{toggle-right-B8VL5BEg.js → toggle-right-DgphAedS.js} +1 -1
  158. package/ui/dist/assets/{trash-2-BscnSMjc.js → trash-2-C5KNXS-O.js} +1 -1
  159. package/ui/dist/assets/{trending-up-CusGuaj_.js → trending-up-B3H8xrGD.js} +1 -1
  160. package/ui/dist/assets/{trophy-SEdQm3z6.js → trophy-C-xSgHD2.js} +1 -1
  161. package/ui/dist/assets/{volume-2-lPPI8coL.js → volume-2-BXwRjeve.js} +1 -1
  162. package/ui/dist/assets/{wifi-off-Deo-Hgei.js → wifi-off-DOm__Zn6.js} +1 -1
  163. package/ui/dist/index.html +2 -2
  164. package/ui/dist/sw.js +1 -1
  165. package/ui/dist/assets/AuditPanel-Dln-nzxR.js +0 -6
  166. package/ui/dist/assets/CPLayout-DVvrBIqI.js +0 -2
  167. package/ui/dist/assets/InfraView-KVwWRhIp.js +0 -2
  168. package/ui/dist/assets/IntelligenceView-CGUmFDYY.js +0 -2
  169. package/ui/dist/assets/SettingsView-DqklKAud.js +0 -2
  170. package/ui/dist/assets/index-CUxC9gxc.css +0 -1
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { dirname, join } from "path";
5
+ import { hashPhoneForOptOut, normalizePhoneNumber, redactPhoneNumber } from "./phonePolicy.js";
6
+ function titanHome() {
7
+ const envHome = process.env.TITAN_HOME?.trim();
8
+ if (envHome) {
9
+ if (envHome === "~") return homedir();
10
+ if (envHome.startsWith("~/")) return join(homedir(), envHome.slice(2));
11
+ return envHome;
12
+ }
13
+ return join(homedir(), ".titan");
14
+ }
15
+ function storePath() {
16
+ return join(titanHome(), "telephony-opt-outs.json");
17
+ }
18
+ function readStore() {
19
+ const path = storePath();
20
+ if (!existsSync(path)) return { optOuts: [] };
21
+ try {
22
+ const raw = JSON.parse(readFileSync(path, "utf8"));
23
+ return { optOuts: Array.isArray(raw.optOuts) ? raw.optOuts.filter(isOptOutRecord) : [] };
24
+ } catch {
25
+ return { optOuts: [] };
26
+ }
27
+ }
28
+ function writeStore(store) {
29
+ const path = storePath();
30
+ mkdirSync(dirname(path), { recursive: true });
31
+ writeFileSync(path, JSON.stringify(store, null, 2), "utf8");
32
+ }
33
+ function isOptOutRecord(value) {
34
+ if (!value || typeof value !== "object") return false;
35
+ const record = value;
36
+ return typeof record.phoneHash === "string" && typeof record.phoneRedacted === "string" && typeof record.reason === "string" && typeof record.recordedAt === "string";
37
+ }
38
+ function listTelephonyOptOuts() {
39
+ return readStore().optOuts.slice().sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
40
+ }
41
+ function isTelephonyOptedOut(phoneHash, optOutHash) {
42
+ if (!phoneHash && !optOutHash) return void 0;
43
+ return listTelephonyOptOuts().find(
44
+ (entry) => (phoneHash ? entry.phoneHash === phoneHash : false) || (optOutHash ? entry.optOutHash === optOutHash : false)
45
+ );
46
+ }
47
+ function recordTelephonyOptOut(opts) {
48
+ const normalized = normalizePhoneNumber(opts.phoneNumber) ?? opts.phoneNumber;
49
+ const store = readStore();
50
+ const optOutHash = hashPhoneForOptOut(normalized);
51
+ const prior = store.optOuts.find((entry2) => entry2.phoneHash === opts.phoneHash || entry2.optOutHash === optOutHash);
52
+ if (prior) return prior;
53
+ const entry = {
54
+ phoneHash: opts.phoneHash,
55
+ optOutHash,
56
+ phoneRedacted: redactPhoneNumber(normalized),
57
+ reason: opts.reason.slice(0, 160),
58
+ source: opts.source ?? "dograh_inbound",
59
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
60
+ callId: opts.callId
61
+ };
62
+ store.optOuts.push(entry);
63
+ writeStore(store);
64
+ return entry;
65
+ }
66
+ export {
67
+ isTelephonyOptedOut,
68
+ listTelephonyOptOuts,
69
+ recordTelephonyOptOut
70
+ };
71
+ //# sourceMappingURL=optOutStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telephony/optOutStore.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { homedir } from 'os';\nimport { dirname, join } from 'path';\nimport { hashPhoneForOptOut, normalizePhoneNumber, redactPhoneNumber } from './phonePolicy.js';\n\nexport interface TelephonyOptOut {\n phoneHash: string;\n optOutHash?: string;\n phoneRedacted: string;\n reason: string;\n source: 'dograh_inbound' | 'manual';\n recordedAt: string;\n callId?: string;\n}\n\ntype StoreShape = { optOuts: TelephonyOptOut[] };\n\nfunction titanHome(): string {\n const envHome = process.env.TITAN_HOME?.trim();\n if (envHome) {\n if (envHome === '~') return homedir();\n if (envHome.startsWith('~/')) return join(homedir(), envHome.slice(2));\n return envHome;\n }\n return join(homedir(), '.titan');\n}\n\nfunction storePath(): string {\n return join(titanHome(), 'telephony-opt-outs.json');\n}\n\nfunction readStore(): StoreShape {\n const path = storePath();\n if (!existsSync(path)) return { optOuts: [] };\n try {\n const raw = JSON.parse(readFileSync(path, 'utf8')) as Partial<StoreShape>;\n return { optOuts: Array.isArray(raw.optOuts) ? raw.optOuts.filter(isOptOutRecord) : [] };\n } catch {\n return { optOuts: [] };\n }\n}\n\nfunction writeStore(store: StoreShape): void {\n const path = storePath();\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(store, null, 2), 'utf8');\n}\n\nfunction isOptOutRecord(value: unknown): value is TelephonyOptOut {\n if (!value || typeof value !== 'object') return false;\n const record = value as Record<string, unknown>;\n return typeof record.phoneHash === 'string'\n && typeof record.phoneRedacted === 'string'\n && typeof record.reason === 'string'\n && typeof record.recordedAt === 'string';\n}\n\nexport function listTelephonyOptOuts(): TelephonyOptOut[] {\n return readStore().optOuts.slice().sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));\n}\n\nexport function isTelephonyOptedOut(phoneHash: string | undefined, optOutHash?: string): TelephonyOptOut | undefined {\n if (!phoneHash && !optOutHash) return undefined;\n return listTelephonyOptOuts().find((entry) =>\n (phoneHash ? entry.phoneHash === phoneHash : false) ||\n (optOutHash ? entry.optOutHash === optOutHash : false),\n );\n}\n\nexport function recordTelephonyOptOut(opts: {\n phoneNumber: string;\n phoneHash: string;\n reason: string;\n source?: TelephonyOptOut['source'];\n callId?: string;\n}): TelephonyOptOut {\n const normalized = normalizePhoneNumber(opts.phoneNumber) ?? opts.phoneNumber;\n const store = readStore();\n const optOutHash = hashPhoneForOptOut(normalized);\n const prior = store.optOuts.find((entry) => entry.phoneHash === opts.phoneHash || entry.optOutHash === optOutHash);\n if (prior) return prior;\n const entry: TelephonyOptOut = {\n phoneHash: opts.phoneHash,\n optOutHash,\n phoneRedacted: redactPhoneNumber(normalized),\n reason: opts.reason.slice(0, 160),\n source: opts.source ?? 'dograh_inbound',\n recordedAt: new Date().toISOString(),\n callId: opts.callId,\n };\n store.optOuts.push(entry);\n writeStore(store);\n return entry;\n}\n"],"mappings":";AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;AAC9B,SAAS,oBAAoB,sBAAsB,yBAAyB;AAc5E,SAAS,YAAoB;AAC3B,QAAM,UAAU,QAAQ,IAAI,YAAY,KAAK;AAC7C,MAAI,SAAS;AACX,QAAI,YAAY,IAAK,QAAO,QAAQ;AACpC,QAAI,QAAQ,WAAW,IAAI,EAAG,QAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AACrE,WAAO;AAAA,EACT;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACjC;AAEA,SAAS,YAAoB;AAC3B,SAAO,KAAK,UAAU,GAAG,yBAAyB;AACpD;AAEA,SAAS,YAAwB;AAC/B,QAAM,OAAO,UAAU;AACvB,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,EAAE,SAAS,CAAC,EAAE;AAC5C,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AACjD,WAAO,EAAE,SAAS,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,QAAQ,OAAO,cAAc,IAAI,CAAC,EAAE;AAAA,EACzF,QAAQ;AACN,WAAO,EAAE,SAAS,CAAC,EAAE;AAAA,EACvB;AACF;AAEA,SAAS,WAAW,OAAyB;AAC3C,QAAM,OAAO,UAAU;AACvB,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,MAAM;AAC5D;AAEA,SAAS,eAAe,OAA0C;AAChE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAS;AACf,SAAO,OAAO,OAAO,cAAc,YAC9B,OAAO,OAAO,kBAAkB,YAChC,OAAO,OAAO,WAAW,YACzB,OAAO,OAAO,eAAe;AACpC;AAEO,SAAS,uBAA0C;AACxD,SAAO,UAAU,EAAE,QAAQ,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,cAAc,EAAE,UAAU,CAAC;AAC5F;AAEO,SAAS,oBAAoB,WAA+B,YAAkD;AACnH,MAAI,CAAC,aAAa,CAAC,WAAY,QAAO;AACtC,SAAO,qBAAqB,EAAE;AAAA,IAAK,CAAC,WACjC,YAAY,MAAM,cAAc,YAAY,WAC5C,aAAa,MAAM,eAAe,aAAa;AAAA,EAClD;AACF;AAEO,SAAS,sBAAsB,MAMlB;AAClB,QAAM,aAAa,qBAAqB,KAAK,WAAW,KAAK,KAAK;AAClE,QAAM,QAAQ,UAAU;AACxB,QAAM,aAAa,mBAAmB,UAAU;AAChD,QAAM,QAAQ,MAAM,QAAQ,KAAK,CAACA,WAAUA,OAAM,cAAc,KAAK,aAAaA,OAAM,eAAe,UAAU;AACjH,MAAI,MAAO,QAAO;AAClB,QAAM,QAAyB;AAAA,IAC7B,WAAW,KAAK;AAAA,IAChB;AAAA,IACA,eAAe,kBAAkB,UAAU;AAAA,IAC3C,QAAQ,KAAK,OAAO,MAAM,GAAG,GAAG;AAAA,IAChC,QAAQ,KAAK,UAAU;AAAA,IACvB,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC,QAAQ,KAAK;AAAA,EACf;AACA,QAAM,QAAQ,KAAK,KAAK;AACxB,aAAW,KAAK;AAChB,SAAO;AACT;","names":["entry"]}
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ import { createHmac } from "crypto";
3
+ const E164_RE = /^\+[1-9]\d{7,14}$/;
4
+ const SAFE_PURPOSE_MAX = 160;
5
+ function normalizePhoneNumber(value) {
6
+ if (typeof value !== "string") return null;
7
+ const trimmed = value.trim();
8
+ if (trimmed.startsWith("+")) {
9
+ const normalized = `+${trimmed.slice(1).replace(/\D/g, "")}`;
10
+ return E164_RE.test(normalized) ? normalized : null;
11
+ }
12
+ return null;
13
+ }
14
+ function redactPhoneNumber(value) {
15
+ const normalized = normalizePhoneNumber(value) ?? "";
16
+ if (!normalized) return "[invalid-phone]";
17
+ if (normalized.length <= 8) return `${normalized.slice(0, 2)}\u2022\u2022${normalized.slice(-2)}`;
18
+ return `${normalized.slice(0, 5)}\u2022\u2022\u2022\u2022${normalized.slice(-2)}`;
19
+ }
20
+ function hashPhoneForApproval(config, normalizedPhoneNumber) {
21
+ const key = config.dograh.apiKey || "titan-telephony-approval";
22
+ return createHmac("sha256", key).update(normalizedPhoneNumber).digest("hex");
23
+ }
24
+ function hashPhoneForOptOut(normalizedPhoneNumber) {
25
+ return createHmac("sha256", "titan-telephony-opt-out-v1").update(normalizedPhoneNumber).digest("hex");
26
+ }
27
+ function normalizeMode(value) {
28
+ return value === "campaign" || value === "callback" || value === "test" ? value : "callback";
29
+ }
30
+ function safePurpose(value) {
31
+ if (typeof value !== "string") return "";
32
+ return value.replace(/[\r\n\t]+/g, " ").replace(/\+\d[\d\s().-]{6,}\d/g, "[phone]").slice(0, SAFE_PURPOSE_MAX).trim();
33
+ }
34
+ function optionalPositiveInt(value) {
35
+ if (value === void 0 || value === null || value === "") return void 0;
36
+ const n = typeof value === "number" ? value : Number(value);
37
+ return Number.isInteger(n) && n > 0 ? n : void 0;
38
+ }
39
+ function validateOutboundCallRequest(config, input) {
40
+ if (!config.enabled) {
41
+ return { ok: false, reason: "Telephony is disabled." };
42
+ }
43
+ if (!config.dograh.apiKey) {
44
+ return { ok: false, reason: "Dograh API key is not configured." };
45
+ }
46
+ const toNumber = normalizePhoneNumber(input.toNumber);
47
+ if (!toNumber) {
48
+ return { ok: false, reason: "Outbound call requires a valid E.164 destination number." };
49
+ }
50
+ const mode = normalizeMode(input.mode);
51
+ if (mode === "campaign" && !config.allowCampaigns) {
52
+ return {
53
+ ok: false,
54
+ reason: "Campaign outbound calls are disabled. Enable telephony.allowCampaigns and compliance controls first.",
55
+ toNumberRedacted: redactPhoneNumber(toNumber),
56
+ toNumberHash: hashPhoneForApproval(config, toNumber),
57
+ toNumberOptOutHash: hashPhoneForOptOut(toNumber),
58
+ mode
59
+ };
60
+ }
61
+ if (mode === "campaign") {
62
+ if (!config.recordingDisclosure.trim()) {
63
+ return {
64
+ ok: false,
65
+ reason: "Campaign outbound calls require a recording disclosure before dialing.",
66
+ toNumberRedacted: redactPhoneNumber(toNumber),
67
+ mode
68
+ };
69
+ }
70
+ if (!config.optOutKeywords.some((keyword) => keyword.trim())) {
71
+ return {
72
+ ok: false,
73
+ reason: "Campaign outbound calls require at least one opt-out keyword before dialing.",
74
+ toNumberRedacted: redactPhoneNumber(toNumber),
75
+ mode
76
+ };
77
+ }
78
+ if (!Number.isInteger(config.maxCallsPerHour) || config.maxCallsPerHour < 1) {
79
+ return {
80
+ ok: false,
81
+ reason: "Campaign outbound calls require telephony.maxCallsPerHour to be at least 1.",
82
+ toNumberRedacted: redactPhoneNumber(toNumber),
83
+ mode
84
+ };
85
+ }
86
+ }
87
+ const workflowId = typeof input.workflowId === "string" && input.workflowId.trim() ? input.workflowId.trim() : config.dograh.defaultOutboundWorkflowId;
88
+ if (!workflowId) {
89
+ return {
90
+ ok: false,
91
+ reason: "Dograh outbound workflow ID is not configured.",
92
+ toNumberRedacted: redactPhoneNumber(toNumber),
93
+ toNumberHash: hashPhoneForApproval(config, toNumber),
94
+ toNumberOptOutHash: hashPhoneForOptOut(toNumber),
95
+ mode
96
+ };
97
+ }
98
+ const purpose = safePurpose(input.purpose) || "Outbound Dograh call";
99
+ return {
100
+ ok: true,
101
+ toNumber,
102
+ toNumberRedacted: redactPhoneNumber(toNumber),
103
+ toNumberHash: hashPhoneForApproval(config, toNumber),
104
+ toNumberOptOutHash: hashPhoneForOptOut(toNumber),
105
+ workflowId,
106
+ purpose,
107
+ mode,
108
+ requiresApproval: config.requireApprovalForOutbound || mode === "campaign",
109
+ telephonyConfigurationId: optionalPositiveInt(input.telephonyConfigurationId),
110
+ fromPhoneNumberId: optionalPositiveInt(input.fromPhoneNumberId)
111
+ };
112
+ }
113
+ export {
114
+ hashPhoneForApproval,
115
+ hashPhoneForOptOut,
116
+ normalizePhoneNumber,
117
+ redactPhoneNumber,
118
+ validateOutboundCallRequest
119
+ };
120
+ //# sourceMappingURL=phonePolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telephony/phonePolicy.ts"],"sourcesContent":["import { createHmac } from 'crypto';\nimport type { TitanConfig } from '../config/schema.js';\n\nexport type OutboundCallMode = 'test' | 'callback' | 'campaign';\n\nexport interface OutboundCallPolicyInput {\n toNumber?: unknown;\n workflowId?: unknown;\n purpose?: unknown;\n mode?: unknown;\n telephonyConfigurationId?: unknown;\n fromPhoneNumberId?: unknown;\n}\n\nexport interface OutboundCallPolicyResult {\n ok: boolean;\n reason?: string;\n toNumber?: string;\n toNumberRedacted?: string;\n toNumberHash?: string;\n toNumberOptOutHash?: string;\n workflowId?: string;\n purpose?: string;\n mode?: OutboundCallMode;\n requiresApproval?: boolean;\n telephonyConfigurationId?: number;\n fromPhoneNumberId?: number;\n}\n\ntype TelephonyConfig = TitanConfig['telephony'];\n\nconst E164_RE = /^\\+[1-9]\\d{7,14}$/;\nconst SAFE_PURPOSE_MAX = 160;\n\nexport function normalizePhoneNumber(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n if (trimmed.startsWith('+')) {\n const normalized = `+${trimmed.slice(1).replace(/\\D/g, '')}`;\n return E164_RE.test(normalized) ? normalized : null;\n }\n return null;\n}\n\nexport function redactPhoneNumber(value: string | undefined | null): string {\n const normalized = normalizePhoneNumber(value) ?? '';\n if (!normalized) return '[invalid-phone]';\n if (normalized.length <= 8) return `${normalized.slice(0, 2)}••${normalized.slice(-2)}`;\n return `${normalized.slice(0, 5)}••••${normalized.slice(-2)}`;\n}\n\nexport function hashPhoneForApproval(config: TelephonyConfig, normalizedPhoneNumber: string): string {\n const key = config.dograh.apiKey || 'titan-telephony-approval';\n return createHmac('sha256', key).update(normalizedPhoneNumber).digest('hex');\n}\n\nexport function hashPhoneForOptOut(normalizedPhoneNumber: string): string {\n return createHmac('sha256', 'titan-telephony-opt-out-v1').update(normalizedPhoneNumber).digest('hex');\n}\n\nfunction normalizeMode(value: unknown): OutboundCallMode {\n return value === 'campaign' || value === 'callback' || value === 'test' ? value : 'callback';\n}\n\nfunction safePurpose(value: unknown): string {\n if (typeof value !== 'string') return '';\n return value.replace(/[\\r\\n\\t]+/g, ' ').replace(/\\+\\d[\\d\\s().-]{6,}\\d/g, '[phone]').slice(0, SAFE_PURPOSE_MAX).trim();\n}\n\nfunction optionalPositiveInt(value: unknown): number | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n const n = typeof value === 'number' ? value : Number(value);\n return Number.isInteger(n) && n > 0 ? n : undefined;\n}\n\nexport function validateOutboundCallRequest(\n config: TelephonyConfig,\n input: OutboundCallPolicyInput,\n): OutboundCallPolicyResult {\n if (!config.enabled) {\n return { ok: false, reason: 'Telephony is disabled.' };\n }\n if (!config.dograh.apiKey) {\n return { ok: false, reason: 'Dograh API key is not configured.' };\n }\n\n const toNumber = normalizePhoneNumber(input.toNumber);\n if (!toNumber) {\n return { ok: false, reason: 'Outbound call requires a valid E.164 destination number.' };\n }\n\n const mode = normalizeMode(input.mode);\n if (mode === 'campaign' && !config.allowCampaigns) {\n return {\n ok: false,\n reason: 'Campaign outbound calls are disabled. Enable telephony.allowCampaigns and compliance controls first.',\n toNumberRedacted: redactPhoneNumber(toNumber),\n toNumberHash: hashPhoneForApproval(config, toNumber),\n toNumberOptOutHash: hashPhoneForOptOut(toNumber),\n mode,\n };\n }\n\n if (mode === 'campaign') {\n if (!config.recordingDisclosure.trim()) {\n return {\n ok: false,\n reason: 'Campaign outbound calls require a recording disclosure before dialing.',\n toNumberRedacted: redactPhoneNumber(toNumber),\n mode,\n };\n }\n if (!config.optOutKeywords.some((keyword) => keyword.trim())) {\n return {\n ok: false,\n reason: 'Campaign outbound calls require at least one opt-out keyword before dialing.',\n toNumberRedacted: redactPhoneNumber(toNumber),\n mode,\n };\n }\n if (!Number.isInteger(config.maxCallsPerHour) || config.maxCallsPerHour < 1) {\n return {\n ok: false,\n reason: 'Campaign outbound calls require telephony.maxCallsPerHour to be at least 1.',\n toNumberRedacted: redactPhoneNumber(toNumber),\n mode,\n };\n }\n }\n\n const workflowId = typeof input.workflowId === 'string' && input.workflowId.trim()\n ? input.workflowId.trim()\n : config.dograh.defaultOutboundWorkflowId;\n if (!workflowId) {\n return {\n ok: false,\n reason: 'Dograh outbound workflow ID is not configured.',\n toNumberRedacted: redactPhoneNumber(toNumber),\n toNumberHash: hashPhoneForApproval(config, toNumber),\n toNumberOptOutHash: hashPhoneForOptOut(toNumber),\n mode,\n };\n }\n\n const purpose = safePurpose(input.purpose) || 'Outbound Dograh call';\n return {\n ok: true,\n toNumber,\n toNumberRedacted: redactPhoneNumber(toNumber),\n toNumberHash: hashPhoneForApproval(config, toNumber),\n toNumberOptOutHash: hashPhoneForOptOut(toNumber),\n workflowId,\n purpose,\n mode,\n requiresApproval: config.requireApprovalForOutbound || mode === 'campaign',\n telephonyConfigurationId: optionalPositiveInt(input.telephonyConfigurationId),\n fromPhoneNumberId: optionalPositiveInt(input.fromPhoneNumberId),\n };\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AA+B3B,MAAM,UAAU;AAChB,MAAM,mBAAmB;AAElB,SAAS,qBAAqB,OAA+B;AAClE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,UAAM,aAAa,IAAI,QAAQ,MAAM,CAAC,EAAE,QAAQ,OAAO,EAAE,CAAC;AAC1D,WAAO,QAAQ,KAAK,UAAU,IAAI,aAAa;AAAA,EACjD;AACA,SAAO;AACT;AAEO,SAAS,kBAAkB,OAA0C;AAC1E,QAAM,aAAa,qBAAqB,KAAK,KAAK;AAClD,MAAI,CAAC,WAAY,QAAO;AACxB,MAAI,WAAW,UAAU,EAAG,QAAO,GAAG,WAAW,MAAM,GAAG,CAAC,CAAC,eAAK,WAAW,MAAM,EAAE,CAAC;AACrF,SAAO,GAAG,WAAW,MAAM,GAAG,CAAC,CAAC,2BAAO,WAAW,MAAM,EAAE,CAAC;AAC7D;AAEO,SAAS,qBAAqB,QAAyB,uBAAuC;AACnG,QAAM,MAAM,OAAO,OAAO,UAAU;AACpC,SAAO,WAAW,UAAU,GAAG,EAAE,OAAO,qBAAqB,EAAE,OAAO,KAAK;AAC7E;AAEO,SAAS,mBAAmB,uBAAuC;AACxE,SAAO,WAAW,UAAU,4BAA4B,EAAE,OAAO,qBAAqB,EAAE,OAAO,KAAK;AACtG;AAEA,SAAS,cAAc,OAAkC;AACvD,SAAO,UAAU,cAAc,UAAU,cAAc,UAAU,SAAS,QAAQ;AACpF;AAEA,SAAS,YAAY,OAAwB;AAC3C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO,MAAM,QAAQ,cAAc,GAAG,EAAE,QAAQ,yBAAyB,SAAS,EAAE,MAAM,GAAG,gBAAgB,EAAE,KAAK;AACtH;AAEA,SAAS,oBAAoB,OAAoC;AAC/D,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI,QAAO;AAClE,QAAM,IAAI,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC1D,SAAO,OAAO,UAAU,CAAC,KAAK,IAAI,IAAI,IAAI;AAC5C;AAEO,SAAS,4BACd,QACA,OAC0B;AAC1B,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,EAAE,IAAI,OAAO,QAAQ,yBAAyB;AAAA,EACvD;AACA,MAAI,CAAC,OAAO,OAAO,QAAQ;AACzB,WAAO,EAAE,IAAI,OAAO,QAAQ,oCAAoC;AAAA,EAClE;AAEA,QAAM,WAAW,qBAAqB,MAAM,QAAQ;AACpD,MAAI,CAAC,UAAU;AACb,WAAO,EAAE,IAAI,OAAO,QAAQ,2DAA2D;AAAA,EACzF;AAEA,QAAM,OAAO,cAAc,MAAM,IAAI;AACrC,MAAI,SAAS,cAAc,CAAC,OAAO,gBAAgB;AACjD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,kBAAkB,kBAAkB,QAAQ;AAAA,MAC5C,cAAc,qBAAqB,QAAQ,QAAQ;AAAA,MACnD,oBAAoB,mBAAmB,QAAQ;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,YAAY;AACvB,QAAI,CAAC,OAAO,oBAAoB,KAAK,GAAG;AACtC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,kBAAkB,kBAAkB,QAAQ;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,OAAO,eAAe,KAAK,CAAC,YAAY,QAAQ,KAAK,CAAC,GAAG;AAC5D,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,kBAAkB,kBAAkB,QAAQ;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,OAAO,UAAU,OAAO,eAAe,KAAK,OAAO,kBAAkB,GAAG;AAC3E,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,kBAAkB,kBAAkB,QAAQ;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,OAAO,MAAM,eAAe,YAAY,MAAM,WAAW,KAAK,IAC7E,MAAM,WAAW,KAAK,IACtB,OAAO,OAAO;AAClB,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,kBAAkB,kBAAkB,QAAQ;AAAA,MAC5C,cAAc,qBAAqB,QAAQ,QAAQ;AAAA,MACnD,oBAAoB,mBAAmB,QAAQ;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,YAAY,MAAM,OAAO,KAAK;AAC9C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,IACA,kBAAkB,kBAAkB,QAAQ;AAAA,IAC5C,cAAc,qBAAqB,QAAQ,QAAQ;AAAA,IACnD,oBAAoB,mBAAmB,QAAQ;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB,OAAO,8BAA8B,SAAS;AAAA,IAChE,0BAA0B,oBAAoB,MAAM,wBAAwB;AAAA,IAC5E,mBAAmB,oBAAoB,MAAM,iBAAiB;AAAA,EAChE;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
- const TITAN_VERSION = "6.1.0";
4
+ const TITAN_VERSION = "6.2.0";
5
5
  const TITAN_CODENAME = "Living Canvas";
6
6
  const TITAN_NAME = "TITAN";
7
7
  const TITAN_FULL_NAME = "The Intelligent Task Automation Network";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '6.1.0';\nexport const TITAN_CODENAME = 'Living Canvas';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// v6.1.0 — Mission Chat\nexport const MISSIONS_DIR = join(TITAN_HOME, 'missions');\nexport const PLAYS_DIR = join(TITAN_HOME, 'plays');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\n// v6.0.1 — Hardcoded floor only. The actual default at runtime is picked\n// by `getDefaultModelId()` in `src/providers/defaultModel.ts`, which\n// detects the user's available API keys (Anthropic / OpenAI / Google /\n// OpenRouter) and selects accordingly. Falls back to local Ollama when\n// no cloud keys are set. This constant is the last-resort fallback for\n// callsites that don't go through the schema thunk.\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n/** v5.4.1: User-preference ceiling. Providers clamp per-model via\n * clampMaxTokens() so this can be high without causing 400s on\n * capped endpoints (e.g. Claude Sonnet 4 8K, Cohere 4K). */\nexport const DEFAULT_MAX_TOKENS = 200000;\nexport const DEFAULT_TEMPERATURE = 0.7;\nexport const MAX_CONTEXT_MESSAGES = 50;\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\n// Security\nexport const DEFAULT_SANDBOX_MODE = 'host';\n/** Default allowed tools. Empty = allow ALL registered tools.\n * Use security.deniedTools to block specific tools instead. */\nexport const ALLOWED_TOOLS_DEFAULT: string[] = [];\nexport const DENIED_TOOLS_DEFAULT: string[] = [];\n"],"mappings":";AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAW1B,aAAa;AAAA;AAYnB,SAAS,mBAA2B;AAChC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,GAAG;AACtC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC1B,aAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC3C;AACA,QAAI,YAAY,KAAK;AACjB,aAAO,QAAQ;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACnC;AACO,MAAM,aAAa,iBAAiB;AACpC,MAAM,oBAAoB,KAAK,YAAY,YAAY;AACvD,MAAM,gBAAgB,KAAK,YAAY,UAAU;AACjD,MAAM,kBAAkB,KAAK,YAAY,WAAW;AACpD,MAAM,mBAAmB,KAAK,iBAAiB,QAAQ;AACvD,MAAM,iBAAiB,KAAK,YAAY,MAAM;AAC9C,MAAM,mBAAmB,KAAK,YAAY,QAAQ;AAGlD,MAAM,YAAY,KAAK,iBAAiB,WAAW;AACnD,MAAM,UAAU,KAAK,iBAAiB,SAAS;AAC/C,MAAM,WAAW,KAAK,iBAAiB,UAAU;AACjD,MAAM,oBAAoB;AAC1B,MAAM,eAAe,KAAK,YAAY,cAAc;AACpD,MAAM,sBAAsB,KAAK,YAAY,sBAAsB;AACnE,MAAM,wBAAwB,KAAK,YAAY,aAAa;AAG5D,MAAM,qBAAqB,KAAK,YAAY,qBAAqB;AACjE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,yBAAyB,KAAK,YAAY,wBAAwB;AACxE,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,MAAM,wBAAwB,KAAK,YAAY,wBAAwB;AACvE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,oBAAoB,KAAK,YAAY,oBAAoB;AAG/D,MAAM,eAAe,KAAK,YAAY,UAAU;AAChD,MAAM,YAAY,KAAK,YAAY,OAAO;AAG1C,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AASzB,MAAM,gBAAgB;AAItB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB,KAAK,KAAK;AAGrC,MAAM,uBAAuB;AAG7B,MAAM,wBAAkC,CAAC;AACzC,MAAM,uBAAiC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '6.2.0';\nexport const TITAN_CODENAME = 'Living Canvas';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// v6.1.0 — Mission Chat\nexport const MISSIONS_DIR = join(TITAN_HOME, 'missions');\nexport const PLAYS_DIR = join(TITAN_HOME, 'plays');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\n// v6.0.1 — Hardcoded floor only. The actual default at runtime is picked\n// by `getDefaultModelId()` in `src/providers/defaultModel.ts`, which\n// detects the user's available API keys (Anthropic / OpenAI / Google /\n// OpenRouter) and selects accordingly. Falls back to local Ollama when\n// no cloud keys are set. This constant is the last-resort fallback for\n// callsites that don't go through the schema thunk.\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n/** v5.4.1: User-preference ceiling. Providers clamp per-model via\n * clampMaxTokens() so this can be high without causing 400s on\n * capped endpoints (e.g. Claude Sonnet 4 8K, Cohere 4K). */\nexport const DEFAULT_MAX_TOKENS = 200000;\nexport const DEFAULT_TEMPERATURE = 0.7;\nexport const MAX_CONTEXT_MESSAGES = 50;\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\n// Security\nexport const DEFAULT_SANDBOX_MODE = 'host';\n/** Default allowed tools. Empty = allow ALL registered tools.\n * Use security.deniedTools to block specific tools instead. */\nexport const ALLOWED_TOOLS_DEFAULT: string[] = [];\nexport const DENIED_TOOLS_DEFAULT: string[] = [];\n"],"mappings":";AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAW1B,aAAa;AAAA;AAYnB,SAAS,mBAA2B;AAChC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,GAAG;AACtC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC1B,aAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC3C;AACA,QAAI,YAAY,KAAK;AACjB,aAAO,QAAQ;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACnC;AACO,MAAM,aAAa,iBAAiB;AACpC,MAAM,oBAAoB,KAAK,YAAY,YAAY;AACvD,MAAM,gBAAgB,KAAK,YAAY,UAAU;AACjD,MAAM,kBAAkB,KAAK,YAAY,WAAW;AACpD,MAAM,mBAAmB,KAAK,iBAAiB,QAAQ;AACvD,MAAM,iBAAiB,KAAK,YAAY,MAAM;AAC9C,MAAM,mBAAmB,KAAK,YAAY,QAAQ;AAGlD,MAAM,YAAY,KAAK,iBAAiB,WAAW;AACnD,MAAM,UAAU,KAAK,iBAAiB,SAAS;AAC/C,MAAM,WAAW,KAAK,iBAAiB,UAAU;AACjD,MAAM,oBAAoB;AAC1B,MAAM,eAAe,KAAK,YAAY,cAAc;AACpD,MAAM,sBAAsB,KAAK,YAAY,sBAAsB;AACnE,MAAM,wBAAwB,KAAK,YAAY,aAAa;AAG5D,MAAM,qBAAqB,KAAK,YAAY,qBAAqB;AACjE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,yBAAyB,KAAK,YAAY,wBAAwB;AACxE,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,MAAM,wBAAwB,KAAK,YAAY,wBAAwB;AACvE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,oBAAoB,KAAK,YAAY,oBAAoB;AAG/D,MAAM,eAAe,KAAK,YAAY,UAAU;AAChD,MAAM,YAAY,KAAK,YAAY,OAAO;AAG1C,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AASzB,MAAM,gBAAgB;AAItB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB,KAAK,KAAK;AAGrC,MAAM,uBAAuB;AAG7B,MAAM,wBAAkC,CAAC;AACzC,MAAM,uBAAiC,CAAC;","names":[]}
@@ -1,7 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  function isDangerous(message) {
3
3
  if (!message || typeof message !== "string") return false;
4
- return /\brm\s+-[a-zA-Z]*[rfRF]/.test(message) || /\bsudo\b/.test(message) || /\bchmod\s+777\b/.test(message);
4
+ const dangerousPatterns = [
5
+ /\brm\s+-[a-zA-Z]*[rfRF]/,
6
+ /\bsudo\b/,
7
+ /\bchmod\s+777\b/,
8
+ /\bcurl\b[^|\n;]*(?:\|\s*(?:ba)?sh\b)/,
9
+ /\bwget\b[^|\n;]*(?:\|\s*(?:ba)?sh\b)/,
10
+ /\|\s*(?:bash|sh|zsh|python|perl|ruby)\b/,
11
+ /\b(?:run|execute)\b[^;\n]*[;`]|`[^`]+`/,
12
+ /(?:^|[/"'\s])\.\.\/\.\.\//,
13
+ /\b(?:fetch|download|open|read)\s+(?:file|dict|gopher):\/\//
14
+ ];
15
+ return dangerousPatterns.some((re) => re.test(message));
5
16
  }
6
17
  function isDangerousCommand(command) {
7
18
  const DANGEROUS_PATTERNS = [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/safety.ts"],"sourcesContent":["/**\n * TITAN — Safety Utilities\n *\n * Pure, deterministic safety checks extracted from agent.ts for\n * unit-testability. Zero side effects, zero async.\n */\n\n/**\n * Detect dangerous shell commands in a user message.\n * Matches: rm -rf variants, sudo, chmod 777.\n */\nexport function isDangerous(message: string): boolean {\n if (!message || typeof message !== 'string') return false;\n return (\n /\\brm\\s+-[a-zA-Z]*[rfRF]/.test(message) ||\n /\\bsudo\\b/.test(message) ||\n /\\bchmod\\s+777\\b/.test(message)\n );\n}\n\n/**\n * Detect potentially dangerous shell commands (broader than isDangerous).\n * Used by shell hook pre-flight.\n */\nexport function isDangerousCommand(command: string): boolean {\n const DANGEROUS_PATTERNS = [\n /\\brm\\s+-[rfRF]/,\n />\\s*\\/dev\\/null\\s*2>&1.*rm/,\n /curl.*\\|.*sh/,\n /wget.*\\|.*sh/,\n /:(){ :|:& };:/, // fork bomb\n /dd\\s+if=.+of=\\/dev\\//,\n /mkfs\\./,\n />\\s*\\/etc\\/passwd/,\n /shutdown\\s+-h\\s+now/,\n ];\n return DANGEROUS_PATTERNS.some(re => re.test(command));\n}\n\n// ── Argument Red-Team Validators (Phase 6) ───────────────────────\n\n/**\n * Detect path traversal in a file path argument.\n * Matches: `..` sequences, absolute paths, home directory `~`.\n */\nexport function isPathTraversal(path: string): boolean {\n if (!path || typeof path !== 'string') return false;\n const normalized = path.trim();\n // .. anywhere in the path\n if (/(?:^|\\/)\\.\\.(?:\\/|$)/.test(normalized)) return true;\n // Absolute path\n if (normalized.startsWith('/')) return true;\n // Home directory expansion\n if (normalized.startsWith('~')) return true;\n return false;\n}\n\n/**\n * Detect shell metacharacters that could enable injection.\n * Matches: `;` `|` `&` `` ` `` `$()` `${}` `<` `>`.\n */\nexport function hasShellMetacharacters(command: string): boolean {\n if (!command || typeof command !== 'string') return false;\n return /[;|&`$(){}<>]/.test(command);\n}\n\n/**\n * Detect command chaining operators.\n * Matches: `;` `&&` `||` `|` (pipe).\n */\nexport function isCommandChaining(command: string): boolean {\n if (!command || typeof command !== 'string') return false;\n return /[;|&]/.test(command);\n}\n\n/**\n * Detect dangerous URL schemes (SSRF / local file access vectors).\n * Blocks: file://, dict://, gopher://, ftp://, sftp://\n */\nexport function isDangerousUrl(url: string): boolean {\n if (!url || typeof url !== 'string') return false;\n try {\n const parsed = new URL(url);\n const blockedSchemes = ['file:', 'dict:', 'gopher:', 'ftp:', 'sftp:'];\n return blockedSchemes.includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n"],"mappings":";AAWO,SAAS,YAAY,SAA0B;AAClD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,SACI,0BAA0B,KAAK,OAAO,KACtC,WAAW,KAAK,OAAO,KACvB,kBAAkB,KAAK,OAAO;AAEtC;AAMO,SAAS,mBAAmB,SAA0B;AACzD,QAAM,qBAAqB;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,SAAO,mBAAmB,KAAK,QAAM,GAAG,KAAK,OAAO,CAAC;AACzD;AAQO,SAAS,gBAAgB,MAAuB;AACnD,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,aAAa,KAAK,KAAK;AAE7B,MAAI,uBAAuB,KAAK,UAAU,EAAG,QAAO;AAEpD,MAAI,WAAW,WAAW,GAAG,EAAG,QAAO;AAEvC,MAAI,WAAW,WAAW,GAAG,EAAG,QAAO;AACvC,SAAO;AACX;AAMO,SAAS,uBAAuB,SAA0B;AAC7D,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,SAAO,gBAAgB,KAAK,OAAO;AACvC;AAMO,SAAS,kBAAkB,SAA0B;AACxD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,SAAO,QAAQ,KAAK,OAAO;AAC/B;AAMO,SAAS,eAAe,KAAsB;AACjD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,MAAI;AACA,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,iBAAiB,CAAC,SAAS,SAAS,WAAW,QAAQ,OAAO;AACpE,WAAO,eAAe,SAAS,OAAO,QAAQ;AAAA,EAClD,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/safety.ts"],"sourcesContent":["/**\n * TITAN — Safety Utilities\n *\n * Pure, deterministic safety checks extracted from agent.ts for\n * unit-testability. Zero side effects, zero async.\n */\n\n/**\n * Detect dangerous shell commands in a user message.\n * Matches: destructive shell patterns, privileged commands, and obvious\n * command-injection vectors. This is intentionally broader than the shell\n * hook scanner because agent-level routing must refuse before tool selection.\n */\nexport function isDangerous(message: string): boolean {\n if (!message || typeof message !== 'string') return false;\n const dangerousPatterns = [\n /\\brm\\s+-[a-zA-Z]*[rfRF]/,\n /\\bsudo\\b/,\n /\\bchmod\\s+777\\b/,\n /\\bcurl\\b[^|\\n;]*(?:\\|\\s*(?:ba)?sh\\b)/,\n /\\bwget\\b[^|\\n;]*(?:\\|\\s*(?:ba)?sh\\b)/,\n /\\|\\s*(?:bash|sh|zsh|python|perl|ruby)\\b/,\n /\\b(?:run|execute)\\b[^;\\n]*[;`]|`[^`]+`/,\n /(?:^|[/\"'\\s])\\.\\.\\/\\.\\.\\//,\n /\\b(?:fetch|download|open|read)\\s+(?:file|dict|gopher):\\/\\//,\n ];\n return dangerousPatterns.some(re => re.test(message));\n}\n\n/**\n * Detect potentially dangerous shell commands (broader than isDangerous).\n * Used by shell hook pre-flight.\n */\nexport function isDangerousCommand(command: string): boolean {\n const DANGEROUS_PATTERNS = [\n /\\brm\\s+-[rfRF]/,\n />\\s*\\/dev\\/null\\s*2>&1.*rm/,\n /curl.*\\|.*sh/,\n /wget.*\\|.*sh/,\n /:(){ :|:& };:/, // fork bomb\n /dd\\s+if=.+of=\\/dev\\//,\n /mkfs\\./,\n />\\s*\\/etc\\/passwd/,\n /shutdown\\s+-h\\s+now/,\n ];\n return DANGEROUS_PATTERNS.some(re => re.test(command));\n}\n\n// ── Argument Red-Team Validators (Phase 6) ───────────────────────\n\n/**\n * Detect path traversal in a file path argument.\n * Matches: `..` sequences, absolute paths, home directory `~`.\n */\nexport function isPathTraversal(path: string): boolean {\n if (!path || typeof path !== 'string') return false;\n const normalized = path.trim();\n // .. anywhere in the path\n if (/(?:^|\\/)\\.\\.(?:\\/|$)/.test(normalized)) return true;\n // Absolute path\n if (normalized.startsWith('/')) return true;\n // Home directory expansion\n if (normalized.startsWith('~')) return true;\n return false;\n}\n\n/**\n * Detect shell metacharacters that could enable injection.\n * Matches: `;` `|` `&` `` ` `` `$()` `${}` `<` `>`.\n */\nexport function hasShellMetacharacters(command: string): boolean {\n if (!command || typeof command !== 'string') return false;\n return /[;|&`$(){}<>]/.test(command);\n}\n\n/**\n * Detect command chaining operators.\n * Matches: `;` `&&` `||` `|` (pipe).\n */\nexport function isCommandChaining(command: string): boolean {\n if (!command || typeof command !== 'string') return false;\n return /[;|&]/.test(command);\n}\n\n/**\n * Detect dangerous URL schemes (SSRF / local file access vectors).\n * Blocks: file://, dict://, gopher://, ftp://, sftp://\n */\nexport function isDangerousUrl(url: string): boolean {\n if (!url || typeof url !== 'string') return false;\n try {\n const parsed = new URL(url);\n const blockedSchemes = ['file:', 'dict:', 'gopher:', 'ftp:', 'sftp:'];\n return blockedSchemes.includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n"],"mappings":";AAaO,SAAS,YAAY,SAA0B;AAClD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,oBAAoB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,SAAO,kBAAkB,KAAK,QAAM,GAAG,KAAK,OAAO,CAAC;AACxD;AAMO,SAAS,mBAAmB,SAA0B;AACzD,QAAM,qBAAqB;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,SAAO,mBAAmB,KAAK,QAAM,GAAG,KAAK,OAAO,CAAC;AACzD;AAQO,SAAS,gBAAgB,MAAuB;AACnD,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,aAAa,KAAK,KAAK;AAE7B,MAAI,uBAAuB,KAAK,UAAU,EAAG,QAAO;AAEpD,MAAI,WAAW,WAAW,GAAG,EAAG,QAAO;AAEvC,MAAI,WAAW,WAAW,GAAG,EAAG,QAAO;AACvC,SAAO;AACX;AAMO,SAAS,uBAAuB,SAA0B;AAC7D,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,SAAO,gBAAgB,KAAK,OAAO;AACvC;AAMO,SAAS,kBAAkB,SAA0B;AACxD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,SAAO,QAAQ,KAAK,OAAO;AAC/B;AAMO,SAAS,eAAe,KAAsB;AACjD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,MAAI;AACA,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,iBAAiB,CAAC,SAAS,SAAS,WAAW,QAAQ,OAAO;AACpE,WAAO,eAAe,SAAS,OAAO,QAAQ;AAAA,EAClD,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "titan-agent",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "description": "TITAN — TypeScript AI agent framework with multi-agent orchestration, provider routing, approval gates, receipts, evals, gateway APIs, and a React Mission Control dashboard. Open-source, MIT licensed.",
5
5
  "author": "Tony Elliott (https://github.com/Djtony707)",
6
6
  "repository": {
@@ -0,0 +1 @@
1
+ import{r as d,j as e,aJ as S,aK as A}from"./index-CmaGnHzR.js";import{C as v}from"./clipboard-list-Dj5RQbT8.js";import{R as C}from"./refresh-cw-BFBnnWj8.js";import{F as T}from"./funnel-xr6Dd1ub.js";function E(){var b;const[c,p]=d.useState([]),[r,h]=d.useState(null),[g,i]=d.useState(!0),[a,n]=d.useState(""),[l,x]=d.useState(""),m=async()=>{i(!0);try{const[t,s]=await Promise.all([S({source:a||void 0,action:l||void 0,limit:100}),A(24)]);p(t),h(s)}catch{}i(!1)};d.useEffect(()=>{m()},[a,l]);const j=t=>t==="success"?"text-[var(--success)]":t==="failure"?"text-[var(--error)]":t==="escalated"?"text-[var(--warning)]":"text-[var(--text-muted)]",f=t=>t==="success"?"bg-[var(--success)]/10 border-[var(--success)]/20":t==="failure"?"bg-[var(--error)]/10 border-[var(--error)]/20":t==="escalated"?"bg-[var(--warning)]/10 border-[var(--warning)]/20":"bg-[var(--bg-tertiary)] border-[var(--border)]",u=t=>t.startsWith("daemon")?"bg-purple-500/10 text-purple-400 border-purple-500/20":t==="autopilot"?"bg-blue-500/10 text-blue-400 border-blue-500/20":t==="user"?"bg-emerald-500/10 text-emerald-400 border-emerald-500/20":t==="initiative"?"bg-amber-500/10 text-amber-400 border-amber-500/20":"bg-[var(--bg-tertiary)] text-[var(--text-muted)] border-[var(--border)]",N=t=>{try{const s=new Date(t),o=new Date().getTime()-s.getTime();return o<6e4?`${Math.round(o/1e3)}s ago`:o<36e5?`${Math.round(o/6e4)}m ago`:o<864e5?`${Math.round(o/36e5)}h ago`:s.toLocaleDateString()}catch{return t}},y=[...new Set(c.map(t=>t.source))].sort(),w=[...new Set(c.map(t=>t.action))].sort();return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(v,{className:"w-6 h-6 text-[var(--accent)]"}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-bold text-[var(--text)]",children:"Audit Log"}),e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Every autonomous action logged for accountability"})]})]}),e.jsx("button",{onClick:m,className:"p-2 rounded-md border border-[var(--border)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] transition-colors",children:e.jsx(C,{size:14})})]}),r&&e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4",children:[e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Actions (24h)"}),e.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:r.totalActions})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Success Rate"}),e.jsxs("p",{className:`text-2xl font-bold ${r.successRate>=80?"text-[var(--success)]":r.successRate>=50?"text-[var(--warning)]":"text-[var(--error)]"}`,children:[r.successRate,"%"]})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Sources"}),e.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:Object.keys(r.bySource).length})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Top Tool"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--accent)] truncate",children:((b=r.topTools[0])==null?void 0:b.tool)||"none"})]})]}),r&&Object.keys(r.bySource).length>0&&e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--text)] mb-3",children:"Actions by Source (24h)"}),e.jsx("div",{className:"flex flex-wrap gap-2",children:Object.entries(r.bySource).sort((t,s)=>s[1]-t[1]).map(([t,s])=>e.jsxs("button",{onClick:()=>n(a===t?"":t),className:`px-3 py-1.5 text-xs rounded-md border transition-colors ${a===t?"bg-[var(--accent)] text-white border-[var(--accent)]":u(t)}`,children:[t,": ",s]},t))})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(T,{size:14,className:"text-[var(--text-muted)]"}),e.jsxs("select",{value:a,onChange:t=>n(t.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[e.jsx("option",{value:"",children:"All Sources"}),y.map(t=>e.jsx("option",{value:t,children:t},t))]}),e.jsxs("select",{value:l,onChange:t=>x(t.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[e.jsx("option",{value:"",children:"All Actions"}),w.map(t=>e.jsx("option",{value:t,children:t},t))]}),(a||l)&&e.jsx("button",{onClick:()=>{n(""),x("")},className:"text-xs text-[var(--accent)] hover:underline",children:"Clear"})]}),g?e.jsx("div",{className:"text-[var(--text-muted)] text-sm",children:"Loading audit log..."}):c.length===0?e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-8 text-center",children:[e.jsx(v,{className:"w-10 h-10 text-[var(--text-muted)] mx-auto mb-3"}),e.jsx("p",{className:"text-[var(--text-secondary)]",children:"No audit entries found"}),e.jsx("p",{className:"text-xs text-[var(--text-muted)]",children:"Autonomous actions will appear here as TITAN operates."})]}):e.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg overflow-hidden",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[var(--border)] text-[var(--text-muted)]",children:[e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Time"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Action"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Source"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Tool"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Result"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Detail"})]})}),e.jsx("tbody",{children:c.map((t,s)=>e.jsxs("tr",{className:"border-b border-[var(--border)]/50 last:border-0 hover:bg-[var(--bg-tertiary)] transition-colors",children:[e.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] whitespace-nowrap",children:N(t.timestamp)}),e.jsx("td",{className:"px-4 py-2 text-[var(--text)] font-medium",children:t.action}),e.jsx("td",{className:"px-4 py-2",children:e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${u(t.source)}`,children:t.source})}),e.jsx("td",{className:"px-4 py-2 text-[var(--text-secondary)] font-mono",children:t.tool||"-"}),e.jsx("td",{className:"px-4 py-2",children:t.result?e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${f(t.result)} ${j(t.result)}`,children:t.result}):"-"}),e.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] max-w-[200px] truncate",children:t.detail?JSON.stringify(t.detail):"-"})]},s))})]})})})]})}export{E as default};
@@ -1,4 +1,4 @@
1
- import{c as M,r as c,a8 as B,j as e,L as C,i as F,Z as E,a9 as O,t as T,b,f as g,B as q,U as G,G as V,h as I}from"./index-BUCi9exS.js";import{S as D}from"./save-Cy8s5sjb.js";import{C as H}from"./circle-check-big-2NAZFdp9.js";import{R as U}from"./refresh-cw-D7RrQJQl.js";import{R}from"./rotate-ccw-wDOKy6Sz.js";import{R as J}from"./radio-DJcw-8Fl.js";import{B as Z}from"./bot-CF5xQRRg.js";import{S as W}from"./server-Cg6QHqG3.js";import{N as K}from"./network-DPPIz6Io.js";import{P as Q}from"./play-C5er2jT3.js";import{M as X}from"./message-square-I2Wa53bg.js";import{D as Y}from"./TitanCanvas-BPVdmdYo.js";import"./chevron-up-DZTojCej.js";import"./plus-C63FIH_4.js";import"./file-text-RZVxniN-.js";import"./terminal-D3qHXhYo.js";import"./book-open-B6YYAKPS.js";import"./pencil-CmthDu_c.js";import"./search-BXNHN7Ra.js";import"./send-DU7Jy0ph.js";import"./mic-CLF69wwf.js";import"./flask-conical-BMH8pJKb.js";import"./clock-DXDS0v3F.js";import"./archive-Cty0CkR8.js";import"./test-tube-qD4BUs0u.js";import"./VoiceOverlay-BKtgGRK8.js";import"./chevron-right-BANsqvxz.js";import"./volume-2-lPPI8coL.js";import"./folder-DgwmNgPQ.js";import"./check-B06kj6lQ.js";import"./shield-check-nn5B33Zk.js";import"./external-link-Chx9uvWw.js";import"./wifi-off-Deo-Hgei.js";/**
1
+ import{c as M,r as c,a8 as B,j as e,L as C,i as F,Z as E,a9 as O,t as T,b,f as g,B as q,U as G,G as V,h as I}from"./index-CmaGnHzR.js";import{S as D}from"./save--wCYre_u.js";import{C as H}from"./circle-check-big-DnJPBAGU.js";import{R as U}from"./refresh-cw-BFBnnWj8.js";import{R}from"./rotate-ccw-B9wWlKRc.js";import{R as J}from"./radio-2vniRFTO.js";import{B as Z}from"./bot-DSGZRM5O.js";import{S as W}from"./server-CBsCfofY.js";import{N as K}from"./network-CrQn1GKv.js";import{P as Q}from"./play-DxBy837b.js";import{M as X}from"./message-square-BrWLAovN.js";import{D as Y}from"./TitanCanvas-CqvuuIjd.js";import"./chevron-up-BbDu8x1d.js";import"./plus-DxgNIt8h.js";import"./file-text-CUkSytWL.js";import"./terminal-CZ77Swtc.js";import"./book-open-DRI_Y6cW.js";import"./pencil-DAI6FtFQ.js";import"./search-DOD1aOm8.js";import"./send-Bb57UxT7.js";import"./mic-BXNK4d7X.js";import"./flask-conical-BS4cuENu.js";import"./clock-BS8fh7Rt.js";import"./archive-DWgNklp0.js";import"./test-tube-CkkvIu5j.js";import"./VoiceOverlay-Bl81c4yf.js";import"./chevron-right-DkIMxdwH.js";import"./volume-2-BXwRjeve.js";import"./folder-BDwoUobN.js";import"./check-Cxp-6q4k.js";import"./shield-check-B24N03VU.js";import"./external-link-BfXhMi9L.js";import"./wifi-off-DOm__Zn6.js";/**
2
2
  * @license lucide-react v0.513.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r,b as n,j as e}from"./index-BUCi9exS.js";import{P as c}from"./PageHeader-BXEGoqDX.js";import"./chevron-right-BANsqvxz.js";function p(){const[t,s]=r.useState(null),[l,i]=r.useState(!0);return r.useEffect(()=>{n("/api/config",{headers:{"Content-Type":"application/json"}}).then(a=>a.json()).then(a=>{var o,d;return s({mode:((o=a.autonomy)==null?void 0:o.mode)||"supervised",interval:(d=a.autonomy)==null?void 0:d.autopilotIntervalMs})}).catch(()=>s({mode:"supervised"})).finally(()=>i(!1))},[]),l?e.jsx("div",{className:"text-[var(--text-muted)]",children:"Loading autopilot config..."}):e.jsxs("div",{className:"space-y-6",children:[e.jsx(c,{title:"Autopilot",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"Agent"},{label:"Autopilot"}]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Mode"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)] capitalize",children:t==null?void 0:t.mode})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Interval"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)]",children:t!=null&&t.interval?`${Math.round(t.interval/6e4)} min`:"Not set"})]})]}),e.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Autopilot mode enables TITAN to run scheduled tasks automatically. Configure via titan.json."})})]})}export{p as default};
1
+ import{r,b as n,j as e}from"./index-CmaGnHzR.js";import{P as c}from"./PageHeader-BJ1jHtOy.js";import"./chevron-right-DkIMxdwH.js";function p(){const[t,s]=r.useState(null),[l,i]=r.useState(!0);return r.useEffect(()=>{n("/api/config",{headers:{"Content-Type":"application/json"}}).then(a=>a.json()).then(a=>{var o,d;return s({mode:((o=a.autonomy)==null?void 0:o.mode)||"supervised",interval:(d=a.autonomy)==null?void 0:d.autopilotIntervalMs})}).catch(()=>s({mode:"supervised"})).finally(()=>i(!1))},[]),l?e.jsx("div",{className:"text-[var(--text-muted)]",children:"Loading autopilot config..."}):e.jsxs("div",{className:"space-y-6",children:[e.jsx(c,{title:"Autopilot",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"Agent"},{label:"Autopilot"}]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Mode"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)] capitalize",children:t==null?void 0:t.mode})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Interval"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)]",children:t!=null&&t.interval?`${Math.round(t.interval/6e4)} min`:"Not set"})]})]}),e.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Autopilot mode enables TITAN to run scheduled tasks automatically. Configure via titan.json."})})]})}export{p as default};
@@ -1,4 +1,4 @@
1
- import{c as q,r as p,b as T,j as e,a7 as O,V as W}from"./index-BUCi9exS.js";import{F as A}from"./flask-conical-BMH8pJKb.js";import{P as D}from"./play-C5er2jT3.js";import{P as B}from"./pause-4M6u54u8.js";import{R as H}from"./refresh-cw-D7RrQJQl.js";import{T as V}from"./trophy-SEdQm3z6.js";import{T as G}from"./trending-up-CusGuaj_.js";import{C as U}from"./clock-DXDS0v3F.js";import{C as J}from"./chevron-right-BANsqvxz.js";/**
1
+ import{c as q,r as p,b as T,j as e,a7 as O,V as W}from"./index-CmaGnHzR.js";import{F as A}from"./flask-conical-BS4cuENu.js";import{P as D}from"./play-DxBy837b.js";import{P as B}from"./pause-DS5xgDMT.js";import{R as H}from"./refresh-cw-BFBnnWj8.js";import{T as V}from"./trophy-C-xSgHD2.js";import{T as G}from"./trending-up-B3H8xrGD.js";import{C as U}from"./clock-BS8fh7Rt.js";import{C as J}from"./chevron-right-DkIMxdwH.js";/**
2
2
  * @license lucide-react v0.513.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as s,aN as p,j as e,aO as u,aP as h}from"./index-BUCi9exS.js";import{P as g}from"./PageHeader-BXEGoqDX.js";import{P as b}from"./plus-C63FIH_4.js";import{R as y}from"./refresh-cw-D7RrQJQl.js";import{A as j}from"./archive-Cty0CkR8.js";import{C as v}from"./circle-check-big-2NAZFdp9.js";import"./chevron-right-BANsqvxz.js";function A(){const[c,m]=s.useState([]),[i,r]=s.useState(!0),[l,n]=s.useState(!1),[d,o]=s.useState(null),t=s.useCallback(async()=>{r(!0);try{const a=await p();m(a.backups||[])}catch{}r(!1)},[]);s.useEffect(()=>{t()},[t]);const x=async()=>{n(!0);try{await u(),await t()}catch{}n(!1)},f=async a=>{o(a);try{await h(a),alert("Backup verified successfully")}catch{alert("Backup verification failed")}o(null)};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(g,{title:"Backup Manager",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"System"},{label:"Backups"}]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:x,disabled:l,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#6366f1] text-white text-sm font-medium hover:bg-[#4f46e5] disabled:opacity-50",children:[e.jsx(b,{className:"w-4 h-4"})," ",l?"Creating...":"Create Backup"]}),e.jsxs("button",{onClick:t,disabled:i,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#27272a] text-[#a1a1aa] text-sm font-medium hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(y,{className:`w-4 h-4 ${i?"animate-spin":""}`})," Refresh"]})]}),c.length===0&&!i&&e.jsx("div",{className:"text-sm text-[#52525b]",children:"No backups found."}),e.jsx("div",{className:"space-y-2",children:c.map(a=>e.jsxs("div",{className:"flex items-center justify-between p-3 rounded-lg bg-[#0a0a0f] border border-[#27272a]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(j,{className:"w-4 h-4 text-[#6366f1]"}),e.jsxs("div",{children:[e.jsx("div",{className:"text-sm text-[#e4e4e7]",children:new Date(a.createdAt).toLocaleString()}),e.jsxs("div",{className:"text-xs text-[#52525b]",children:[(a.sizeBytes/1024/1024).toFixed(1)," MB"]})]})]}),e.jsxs("button",{onClick:()=>f(a.path),disabled:d===a.path,className:"flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-[#27272a] text-[#a1a1aa] text-xs hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(v,{className:"w-3.5 h-3.5"})," ",d===a.path?"Verifying...":"Verify"]})]},a.path))})]})}export{A as default};
1
+ import{r as s,aN as p,j as e,aO as u,aP as h}from"./index-CmaGnHzR.js";import{P as g}from"./PageHeader-BJ1jHtOy.js";import{P as b}from"./plus-DxgNIt8h.js";import{R as y}from"./refresh-cw-BFBnnWj8.js";import{A as j}from"./archive-DWgNklp0.js";import{C as v}from"./circle-check-big-DnJPBAGU.js";import"./chevron-right-DkIMxdwH.js";function A(){const[c,m]=s.useState([]),[i,r]=s.useState(!0),[l,n]=s.useState(!1),[d,o]=s.useState(null),t=s.useCallback(async()=>{r(!0);try{const a=await p();m(a.backups||[])}catch{}r(!1)},[]);s.useEffect(()=>{t()},[t]);const x=async()=>{n(!0);try{await u(),await t()}catch{}n(!1)},f=async a=>{o(a);try{await h(a),alert("Backup verified successfully")}catch{alert("Backup verification failed")}o(null)};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(g,{title:"Backup Manager",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"System"},{label:"Backups"}]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:x,disabled:l,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#6366f1] text-white text-sm font-medium hover:bg-[#4f46e5] disabled:opacity-50",children:[e.jsx(b,{className:"w-4 h-4"})," ",l?"Creating...":"Create Backup"]}),e.jsxs("button",{onClick:t,disabled:i,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#27272a] text-[#a1a1aa] text-sm font-medium hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(y,{className:`w-4 h-4 ${i?"animate-spin":""}`})," Refresh"]})]}),c.length===0&&!i&&e.jsx("div",{className:"text-sm text-[#52525b]",children:"No backups found."}),e.jsx("div",{className:"space-y-2",children:c.map(a=>e.jsxs("div",{className:"flex items-center justify-between p-3 rounded-lg bg-[#0a0a0f] border border-[#27272a]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(j,{className:"w-4 h-4 text-[#6366f1]"}),e.jsxs("div",{children:[e.jsx("div",{className:"text-sm text-[#e4e4e7]",children:new Date(a.createdAt).toLocaleString()}),e.jsxs("div",{className:"text-xs text-[#52525b]",children:[(a.sizeBytes/1024/1024).toFixed(1)," MB"]})]})]}),e.jsxs("button",{onClick:()=>f(a.path),disabled:d===a.path,className:"flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-[#27272a] text-[#a1a1aa] text-xs hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(v,{className:"w-3.5 h-3.5"})," ",d===a.path?"Verifying...":"Verify"]})]},a.path))})]})}export{A as default};
@@ -1,4 +1,4 @@
1
- import{c as i,r as s,j as e,G as m,aw as p}from"./index-BUCi9exS.js";import{P as h}from"./PageHeader-BXEGoqDX.js";import"./chevron-right-BANsqvxz.js";/**
1
+ import{c as i,r as s,j as e,G as m,aw as p}from"./index-CmaGnHzR.js";import{P as h}from"./PageHeader-BJ1jHtOy.js";import"./chevron-right-DkIMxdwH.js";/**
2
2
  * @license lucide-react v0.513.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as a,x as b,j as e,A as u,Z as j,W as S,K as A,T as E}from"./index-BUCi9exS.js";import"./Input-CzDjGc5Y.js";import{P as N}from"./PageHeader-BXEGoqDX.js";import{E as T}from"./EmptyState-B3lWjJU-.js";import{T as C}from"./Tabs-B-g5Ghos.js";import{S as w}from"./SkeletonLoader-DGQ0Q7vy.js";import{S as P}from"./shield-check-nn5B33Zk.js";import{D as $}from"./dollar-sign-DLLEescF.js";import{B as k}from"./bot-CF5xQRRg.js";import"./clsx-B-dksMZM.js";import"./chevron-right-BANsqvxz.js";function I(s){const r=Math.floor((Date.now()-new Date(s).getTime())/1e3);return r<60?`${r}s`:r<3600?`${Math.floor(r/60)}m`:r<86400?`${Math.floor(r/3600)}h`:`${Math.floor(r/86400)}d`}const D={agent:k,goal:E,budget:$,approval:P,error:A,tool:S,system:j},M={agent:"text-accent",goal:"text-success",budget:"text-warning",approval:"text-info",error:"text-error",tool:"text-text-secondary",system:"text-text-muted"};function K(){const[s,r]=a.useState([]),[i,f]=a.useState(!0),[o,x]=a.useState(null),[n,h]=a.useState("all"),l=a.useRef(null),c=a.useCallback(async()=>{try{const t=await b(100);r(t),x(null)}catch(t){x(t instanceof Error?t.message:"Failed to load activity")}f(!1)},[]);a.useEffect(()=>(c(),l.current=setInterval(c,1e4),()=>{l.current&&clearInterval(l.current)}),[c]);const p=Array.from(new Set(s.map(t=>t.type))),g=[{id:"all",label:"All",count:s.length},...p.map(t=>({id:t,label:t.charAt(0).toUpperCase()+t.slice(1),count:s.filter(d=>d.type===t).length}))],m=n==="all"?s:s.filter(t=>t.type===n);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(N,{title:"Activity",subtitle:"Auto-refreshes every 10s",breadcrumbs:[{label:"Command Post"},{label:"Activity"}]}),p.length>0&&e.jsx(C,{tabs:g,activeTab:n,onChange:h}),i&&e.jsx(w,{variant:"row",count:8}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!i&&!o&&m.length===0&&e.jsx(T,{icon:e.jsx(u,{size:32}),title:"No activity",description:"Agent activity will appear here as events occur."}),!i&&!o&&m.length>0&&e.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:m.map((t,d)=>{const v=D[t.type]??u,y=M[t.type]??"text-text-muted";return e.jsxs("div",{className:"flex items-start gap-3 px-4 py-2.5",children:[e.jsx(v,{size:14,className:`${y} mt-0.5 flex-shrink-0`}),e.jsx("span",{className:"flex-1 text-sm text-text-secondary",children:t.message}),t.agentId&&e.jsx("span",{className:"text-[10px] text-text-muted flex-shrink-0",children:t.agentId}),e.jsx("span",{className:"text-xs text-text-muted flex-shrink-0",children:I(t.timestamp)})]},`${t.id}-${d}`)})})]})}export{K as default};
1
+ import{r as a,x as b,j as e,A as u,Z as j,W as S,K as A,T as E}from"./index-CmaGnHzR.js";import"./Input-Bzh0-cXU.js";import{P as N}from"./PageHeader-BJ1jHtOy.js";import{E as T}from"./EmptyState-iLOrbdU7.js";import{T as C}from"./Tabs-vGpPiBzr.js";import{S as w}from"./SkeletonLoader-IpWkLmoy.js";import{S as P}from"./shield-check-B24N03VU.js";import{D as $}from"./dollar-sign-DMXBeaTj.js";import{B as k}from"./bot-DSGZRM5O.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DkIMxdwH.js";function I(s){const r=Math.floor((Date.now()-new Date(s).getTime())/1e3);return r<60?`${r}s`:r<3600?`${Math.floor(r/60)}m`:r<86400?`${Math.floor(r/3600)}h`:`${Math.floor(r/86400)}d`}const D={agent:k,goal:E,budget:$,approval:P,error:A,tool:S,system:j},M={agent:"text-accent",goal:"text-success",budget:"text-warning",approval:"text-info",error:"text-error",tool:"text-text-secondary",system:"text-text-muted"};function K(){const[s,r]=a.useState([]),[i,f]=a.useState(!0),[o,x]=a.useState(null),[n,h]=a.useState("all"),l=a.useRef(null),c=a.useCallback(async()=>{try{const t=await b(100);r(t),x(null)}catch(t){x(t instanceof Error?t.message:"Failed to load activity")}f(!1)},[]);a.useEffect(()=>(c(),l.current=setInterval(c,1e4),()=>{l.current&&clearInterval(l.current)}),[c]);const p=Array.from(new Set(s.map(t=>t.type))),g=[{id:"all",label:"All",count:s.length},...p.map(t=>({id:t,label:t.charAt(0).toUpperCase()+t.slice(1),count:s.filter(d=>d.type===t).length}))],m=n==="all"?s:s.filter(t=>t.type===n);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(N,{title:"Activity",subtitle:"Auto-refreshes every 10s",breadcrumbs:[{label:"Command Post"},{label:"Activity"}]}),p.length>0&&e.jsx(C,{tabs:g,activeTab:n,onChange:h}),i&&e.jsx(w,{variant:"row",count:8}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!i&&!o&&m.length===0&&e.jsx(T,{icon:e.jsx(u,{size:32}),title:"No activity",description:"Agent activity will appear here as events occur."}),!i&&!o&&m.length>0&&e.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:m.map((t,d)=>{const v=D[t.type]??u,y=M[t.type]??"text-text-muted";return e.jsxs("div",{className:"flex items-start gap-3 px-4 py-2.5",children:[e.jsx(v,{size:14,className:`${y} mt-0.5 flex-shrink-0`}),e.jsx("span",{className:"flex-1 text-sm text-text-secondary",children:t.message}),t.agentId&&e.jsx("span",{className:"text-[10px] text-text-muted flex-shrink-0",children:t.agentId}),e.jsx("span",{className:"text-xs text-text-muted flex-shrink-0",children:I(t.timestamp)})]},`${t.id}-${d}`)})})]})}export{K as default};
@@ -1 +1 @@
1
- import{p as O,u as Y,r as s,$ as G,Y as J,y as K,aB as Q,j as e,D as A,b9 as V}from"./index-BUCi9exS.js";import{B as T}from"./Input-CzDjGc5Y.js";import{S as c}from"./StatusBadge-Do8GEljT.js";import{P as x}from"./PageHeader-BXEGoqDX.js";import{E as W}from"./EmptyState-B3lWjJU-.js";import{T as X}from"./Tabs-B-g5Ghos.js";import{S as Z}from"./SkeletonLoader-DGQ0Q7vy.js";import{A as _}from"./arrow-left-pfqo1Fw_.js";import{C as ee}from"./circle-check-big-2NAZFdp9.js";import{D as te}from"./dollar-sign-DLLEescF.js";import{C as se}from"./clock-DXDS0v3F.js";import{S as ae}from"./save-Cy8s5sjb.js";import"./clsx-B-dksMZM.js";import"./chevron-right-BANsqvxz.js";function k(a){const o=Math.floor((Date.now()-new Date(a).getTime())/1e3);return o<60?`${o}s`:o<3600?`${Math.floor(o/60)}m`:o<86400?`${Math.floor(o/3600)}h`:`${Math.floor(o/86400)}d`}function re(a){return a?a<1e3?`${a}ms`:`${(a/1e3).toFixed(1)}s`:"-"}function je(){const{id:a}=O(),o=Y(),[r,m]=s.useState(null),[n,E]=s.useState([]),[u,M]=s.useState([]),[P,b]=s.useState(!0),[p,i]=s.useState(null),[d,D]=s.useState("overview"),[h,g]=s.useState(""),[f,j]=s.useState(""),[v,N]=s.useState(""),[y,S]=s.useState(""),[z,C]=s.useState(!1),[R,$]=s.useState([]),w=s.useCallback(async()=>{if(a){try{const l=(await G()).find(q=>q.id===a);if(!l){i("Agent not found"),b(!1);return}m(l),g(l.role),j(l.reportsTo??""),N(l.title??""),S(l.model??"");const[H,U]=await Promise.all([J(a),K({assignee:a})]);E(H),M(U),i(null)}catch(t){i(t instanceof Error?t.message:"Failed to load agent")}b(!1)}},[a]);s.useEffect(()=>{w()},[w]),s.useEffect(()=>{Q().then(t=>$(t)).catch(()=>{})},[]);const B=async()=>{if(a){C(!0);try{const t=await V(a,{role:h,reportsTo:f||void 0,title:v||void 0,model:y||void 0});m(t)}catch{}C(!1)}},L=[{id:"overview",label:"Overview"},{id:"runs",label:"Runs",count:n.length},{id:"config",label:"Config"}];if(P)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"..."}]}),e.jsx(Z,{variant:"card",count:1})]});if(p||!r)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"Error"}]}),e.jsxs("div",{className:"text-center py-12",children:[e.jsx("p",{className:"text-error text-sm mb-4",children:p??"Agent not found"}),e.jsx(T,{variant:"secondary",size:"sm",onClick:()=>o("/command-post/agents"),children:"Back to Agents"})]})]});const I=Date.now()-new Date(r.createdAt).getTime(),F=Math.floor(I/36e5);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(x,{title:r.name,subtitle:r.title??r.role,breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:r.name}],actions:e.jsxs(A,{to:"/command-post/agents",className:"flex items-center gap-1 text-xs text-text-muted hover:text-text",children:[e.jsx(_,{size:14})," Back"]})}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(c,{status:r.status}),e.jsxs("span",{className:"text-xs text-text-muted",children:["Last heartbeat: ",k(r.lastHeartbeat)," ago"]})]}),e.jsx(X,{tabs:L,activeTab:d,onChange:D}),d==="overview"&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(ee,{size:16,className:"text-success mx-auto mb-1"}),e.jsx("div",{className:"text-xl font-bold text-text",children:r.totalTasksCompleted}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Tasks Completed"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(te,{size:16,className:"text-warning mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:["$",r.totalCostUsd.toFixed(4)]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Total Cost"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(se,{size:16,className:"text-accent mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:[F,"h"]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Uptime"})]})]}),e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:[e.jsx("div",{className:"px-4 py-3 border-b border-border",children:e.jsx("h3",{className:"text-sm font-semibold text-text",children:"Assigned Issues"})}),u.length===0?e.jsx("div",{className:"py-8 text-center text-xs text-text-muted",children:"No assigned issues"}):e.jsx("div",{className:"divide-y divide-border",children:u.map(t=>e.jsxs(A,{to:`/command-post/issues/${t.id}`,className:"flex items-center gap-2 px-4 py-2 hover:bg-bg-tertiary transition-colors",children:[e.jsx(c,{status:t.status,variant:"dot",size:"sm"}),e.jsx("span",{className:"text-xs text-text flex-1 truncate",children:t.title}),e.jsx("span",{className:"text-[10px] text-text-muted font-mono",children:t.identifier})]},t.id))})]})]}),d==="runs"&&e.jsx("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:n.length===0?e.jsx(W,{title:"No runs",description:"This agent has not executed any runs yet."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border text-left",children:[e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Source"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Duration"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Tools"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Started"})]})}),e.jsx("tbody",{className:"divide-y divide-border",children:n.map(t=>e.jsxs("tr",{className:"hover:bg-bg-tertiary/50 transition-colors",children:[e.jsx("td",{className:"px-4 py-2 text-text-secondary capitalize",children:t.source}),e.jsx("td",{className:"px-4 py-2",children:e.jsx(c,{status:t.status,size:"sm"})}),e.jsx("td",{className:"px-4 py-2 text-text-secondary",children:re(t.durationMs)}),e.jsx("td",{className:"px-4 py-2 text-text-muted",children:t.toolsUsed.length}),e.jsxs("td",{className:"px-4 py-2 text-text-muted",children:[k(t.startedAt)," ago"]})]},t.id))})]})})}),d==="config"&&e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl p-4 space-y-4 max-w-md",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Role"}),e.jsx("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:h,onChange:t=>g(t.target.value),children:["ceo","manager","engineer","researcher","general"].map(t=>e.jsx("option",{value:t,children:t},t))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Reports To"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"Agent ID...",value:f,onChange:t=>j(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Title"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. Senior Engineer",value:v,onChange:t=>N(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Model"}),e.jsx("input",{list:"cp-agent-models",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. ollama/qwen3.5:cloud",value:y,onChange:t=>S(t.target.value)}),e.jsx("datalist",{id:"cp-agent-models",children:R.map(t=>e.jsx("option",{value:t.id,children:t.name??t.id},t.id))})]}),e.jsx(T,{size:"sm",icon:e.jsx(ae,{size:14}),onClick:B,loading:z,children:"Save"})]})]})}export{je as default};
1
+ import{p as O,u as Y,r as s,$ as G,Y as J,y as K,aB as Q,j as e,D as A,be as V}from"./index-CmaGnHzR.js";import{B as T}from"./Input-Bzh0-cXU.js";import{S as c}from"./StatusBadge-e3tFf05B.js";import{P as x}from"./PageHeader-BJ1jHtOy.js";import{E as W}from"./EmptyState-iLOrbdU7.js";import{T as X}from"./Tabs-vGpPiBzr.js";import{S as Z}from"./SkeletonLoader-IpWkLmoy.js";import{A as _}from"./arrow-left-CW5NV7E0.js";import{C as ee}from"./circle-check-big-DnJPBAGU.js";import{D as te}from"./dollar-sign-DMXBeaTj.js";import{C as se}from"./clock-BS8fh7Rt.js";import{S as ae}from"./save--wCYre_u.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DkIMxdwH.js";function k(a){const o=Math.floor((Date.now()-new Date(a).getTime())/1e3);return o<60?`${o}s`:o<3600?`${Math.floor(o/60)}m`:o<86400?`${Math.floor(o/3600)}h`:`${Math.floor(o/86400)}d`}function re(a){return a?a<1e3?`${a}ms`:`${(a/1e3).toFixed(1)}s`:"-"}function je(){const{id:a}=O(),o=Y(),[r,m]=s.useState(null),[n,E]=s.useState([]),[u,M]=s.useState([]),[P,b]=s.useState(!0),[p,i]=s.useState(null),[d,D]=s.useState("overview"),[h,g]=s.useState(""),[f,j]=s.useState(""),[v,N]=s.useState(""),[y,S]=s.useState(""),[z,C]=s.useState(!1),[R,$]=s.useState([]),w=s.useCallback(async()=>{if(a){try{const l=(await G()).find(q=>q.id===a);if(!l){i("Agent not found"),b(!1);return}m(l),g(l.role),j(l.reportsTo??""),N(l.title??""),S(l.model??"");const[H,U]=await Promise.all([J(a),K({assignee:a})]);E(H),M(U),i(null)}catch(t){i(t instanceof Error?t.message:"Failed to load agent")}b(!1)}},[a]);s.useEffect(()=>{w()},[w]),s.useEffect(()=>{Q().then(t=>$(t)).catch(()=>{})},[]);const B=async()=>{if(a){C(!0);try{const t=await V(a,{role:h,reportsTo:f||void 0,title:v||void 0,model:y||void 0});m(t)}catch{}C(!1)}},L=[{id:"overview",label:"Overview"},{id:"runs",label:"Runs",count:n.length},{id:"config",label:"Config"}];if(P)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"..."}]}),e.jsx(Z,{variant:"card",count:1})]});if(p||!r)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"Error"}]}),e.jsxs("div",{className:"text-center py-12",children:[e.jsx("p",{className:"text-error text-sm mb-4",children:p??"Agent not found"}),e.jsx(T,{variant:"secondary",size:"sm",onClick:()=>o("/command-post/agents"),children:"Back to Agents"})]})]});const I=Date.now()-new Date(r.createdAt).getTime(),F=Math.floor(I/36e5);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(x,{title:r.name,subtitle:r.title??r.role,breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:r.name}],actions:e.jsxs(A,{to:"/command-post/agents",className:"flex items-center gap-1 text-xs text-text-muted hover:text-text",children:[e.jsx(_,{size:14})," Back"]})}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(c,{status:r.status}),e.jsxs("span",{className:"text-xs text-text-muted",children:["Last heartbeat: ",k(r.lastHeartbeat)," ago"]})]}),e.jsx(X,{tabs:L,activeTab:d,onChange:D}),d==="overview"&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(ee,{size:16,className:"text-success mx-auto mb-1"}),e.jsx("div",{className:"text-xl font-bold text-text",children:r.totalTasksCompleted}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Tasks Completed"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(te,{size:16,className:"text-warning mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:["$",r.totalCostUsd.toFixed(4)]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Total Cost"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(se,{size:16,className:"text-accent mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:[F,"h"]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Uptime"})]})]}),e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:[e.jsx("div",{className:"px-4 py-3 border-b border-border",children:e.jsx("h3",{className:"text-sm font-semibold text-text",children:"Assigned Issues"})}),u.length===0?e.jsx("div",{className:"py-8 text-center text-xs text-text-muted",children:"No assigned issues"}):e.jsx("div",{className:"divide-y divide-border",children:u.map(t=>e.jsxs(A,{to:`/command-post/issues/${t.id}`,className:"flex items-center gap-2 px-4 py-2 hover:bg-bg-tertiary transition-colors",children:[e.jsx(c,{status:t.status,variant:"dot",size:"sm"}),e.jsx("span",{className:"text-xs text-text flex-1 truncate",children:t.title}),e.jsx("span",{className:"text-[10px] text-text-muted font-mono",children:t.identifier})]},t.id))})]})]}),d==="runs"&&e.jsx("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:n.length===0?e.jsx(W,{title:"No runs",description:"This agent has not executed any runs yet."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border text-left",children:[e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Source"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Duration"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Tools"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Started"})]})}),e.jsx("tbody",{className:"divide-y divide-border",children:n.map(t=>e.jsxs("tr",{className:"hover:bg-bg-tertiary/50 transition-colors",children:[e.jsx("td",{className:"px-4 py-2 text-text-secondary capitalize",children:t.source}),e.jsx("td",{className:"px-4 py-2",children:e.jsx(c,{status:t.status,size:"sm"})}),e.jsx("td",{className:"px-4 py-2 text-text-secondary",children:re(t.durationMs)}),e.jsx("td",{className:"px-4 py-2 text-text-muted",children:t.toolsUsed.length}),e.jsxs("td",{className:"px-4 py-2 text-text-muted",children:[k(t.startedAt)," ago"]})]},t.id))})]})})}),d==="config"&&e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl p-4 space-y-4 max-w-md",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Role"}),e.jsx("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:h,onChange:t=>g(t.target.value),children:["ceo","manager","engineer","researcher","general"].map(t=>e.jsx("option",{value:t,children:t},t))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Reports To"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"Agent ID...",value:f,onChange:t=>j(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Title"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. Senior Engineer",value:v,onChange:t=>N(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Model"}),e.jsx("input",{list:"cp-agent-models",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. ollama/qwen3.5:cloud",value:y,onChange:t=>S(t.target.value)}),e.jsx("datalist",{id:"cp-agent-models",children:R.map(t=>e.jsx("option",{value:t.id,children:t.name??t.id},t.id))})]}),e.jsx(T,{size:"sm",icon:e.jsx(ae,{size:14}),onClick:B,loading:z,children:"Save"})]})]})}export{je as default};
@@ -1 +1 @@
1
- import{r,$ as m,j as e,U as p,D as u,X as h,b as j}from"./index-BUCi9exS.js";import"./Input-CzDjGc5Y.js";import{S as g}from"./StatusBadge-Do8GEljT.js";import{P as f}from"./PageHeader-BXEGoqDX.js";import{E as b}from"./EmptyState-B3lWjJU-.js";import{S as N}from"./SkeletonLoader-DGQ0Q7vy.js";import"./clsx-B-dksMZM.js";import"./chevron-right-BANsqvxz.js";function y(a){const s=Math.floor((Date.now()-new Date(a).getTime())/1e3);return s<60?`${s}s`:s<3600?`${Math.floor(s/60)}m`:s<86400?`${Math.floor(s/3600)}h`:`${Math.floor(s/86400)}d`}function v(a){const s=a.split("/");return s[s.length-1]}function D(){const[a,s]=r.useState([]),[c,d]=r.useState(!0),[o,l]=r.useState(null),i=r.useCallback(async()=>{try{const t=await m();s(t),l(null)}catch(t){l(t instanceof Error?t.message:"Failed to load agents")}d(!1)},[]);r.useEffect(()=>{i()},[i]);const x=async(t,n)=>{n.preventDefault(),n.stopPropagation();try{await j(`/api/command-post/agents/${t}`,{method:"DELETE"}),await i()}catch{}};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(f,{title:"Agents",breadcrumbs:[{label:"Command Post"},{label:"Agents"}]}),c&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:e.jsx(N,{variant:"card",count:6})}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!c&&!o&&a.length===0&&e.jsx(b,{icon:e.jsx(p,{size:32}),title:"No agents registered",description:"Agents will appear here once they connect to Command Post."}),!c&&!o&&a.length>0&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:a.map(t=>e.jsxs(u,{to:`/command-post/agents/${t.id}`,className:"relative bg-bg-secondary border border-border rounded-xl p-4 hover:border-border-light transition-colors group",children:[t.role!=="ceo"&&e.jsx("button",{onClick:n=>x(t.id,n),className:"absolute top-3 right-3 p-1 rounded-md text-text-muted hover:text-error hover:bg-error/10 opacity-0 group-hover:opacity-100 transition-all",title:"Remove agent",children:e.jsx(h,{size:14})}),e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx("span",{className:"text-sm font-semibold text-text",children:t.name}),e.jsx(g,{status:t.status,size:"sm"})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Role"}),e.jsx("span",{className:"text-xs text-text-secondary capitalize",children:t.role})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Model"}),e.jsx("span",{className:"text-xs text-text-secondary truncate max-w-[140px]",children:v(t.model)})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Last Heartbeat"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:[y(t.lastHeartbeat)," ago"]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Tasks Done"}),e.jsx("span",{className:"text-xs text-text-secondary",children:t.totalTasksCompleted})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Cost"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:["$",t.totalCostUsd.toFixed(4)]})]})]})]},t.id))})]})}export{D as default};
1
+ import{r,$ as m,j as e,U as p,D as u,X as h,b as j}from"./index-CmaGnHzR.js";import"./Input-Bzh0-cXU.js";import{S as g}from"./StatusBadge-e3tFf05B.js";import{P as f}from"./PageHeader-BJ1jHtOy.js";import{E as b}from"./EmptyState-iLOrbdU7.js";import{S as N}from"./SkeletonLoader-IpWkLmoy.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DkIMxdwH.js";function y(a){const s=Math.floor((Date.now()-new Date(a).getTime())/1e3);return s<60?`${s}s`:s<3600?`${Math.floor(s/60)}m`:s<86400?`${Math.floor(s/3600)}h`:`${Math.floor(s/86400)}d`}function v(a){const s=a.split("/");return s[s.length-1]}function D(){const[a,s]=r.useState([]),[c,d]=r.useState(!0),[o,l]=r.useState(null),i=r.useCallback(async()=>{try{const t=await m();s(t),l(null)}catch(t){l(t instanceof Error?t.message:"Failed to load agents")}d(!1)},[]);r.useEffect(()=>{i()},[i]);const x=async(t,n)=>{n.preventDefault(),n.stopPropagation();try{await j(`/api/command-post/agents/${t}`,{method:"DELETE"}),await i()}catch{}};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(f,{title:"Agents",breadcrumbs:[{label:"Command Post"},{label:"Agents"}]}),c&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:e.jsx(N,{variant:"card",count:6})}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!c&&!o&&a.length===0&&e.jsx(b,{icon:e.jsx(p,{size:32}),title:"No agents registered",description:"Agents will appear here once they connect to Command Post."}),!c&&!o&&a.length>0&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:a.map(t=>e.jsxs(u,{to:`/command-post/agents/${t.id}`,className:"relative bg-bg-secondary border border-border rounded-xl p-4 hover:border-border-light transition-colors group",children:[t.role!=="ceo"&&e.jsx("button",{onClick:n=>x(t.id,n),className:"absolute top-3 right-3 p-1 rounded-md text-text-muted hover:text-error hover:bg-error/10 opacity-0 group-hover:opacity-100 transition-all",title:"Remove agent",children:e.jsx(h,{size:14})}),e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx("span",{className:"text-sm font-semibold text-text",children:t.name}),e.jsx(g,{status:t.status,size:"sm"})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Role"}),e.jsx("span",{className:"text-xs text-text-secondary capitalize",children:t.role})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Model"}),e.jsx("span",{className:"text-xs text-text-secondary truncate max-w-[140px]",children:v(t.model)})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Last Heartbeat"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:[y(t.lastHeartbeat)," ago"]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Tasks Done"}),e.jsx("span",{className:"text-xs text-text-secondary",children:t.totalTasksCompleted})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Cost"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:["$",t.totalCostUsd.toFixed(4)]})]})]})]},t.id))})]})}export{D as default};
@@ -1 +1 @@
1
- import{r as a,J as P,z,j as t,K as B,X as R,O as $,P as _}from"./index-BUCi9exS.js";import{B as A}from"./Input-CzDjGc5Y.js";import{S as D}from"./StatusBadge-Do8GEljT.js";import{P as M}from"./PageHeader-BXEGoqDX.js";import{E as L}from"./EmptyState-B3lWjJU-.js";import{T as W}from"./Tabs-B-g5Ghos.js";import{S as F}from"./SkeletonLoader-DGQ0Q7vy.js";import{e as H}from"./approvalHeadline-DB9SgR-9.js";import{S as I}from"./shield-check-nn5B33Zk.js";import{C as K}from"./check-B06kj6lQ.js";import"./clsx-B-dksMZM.js";import"./chevron-right-BANsqvxz.js";function U(r){const o=Math.floor((Date.now()-new Date(r).getTime())/1e3);return o<60?`${o}s`:o<3600?`${Math.floor(o/60)}m`:o<86400?`${Math.floor(o/3600)}h`:`${Math.floor(o/86400)}d`}function q(r){return r==="high"?"text-error bg-error/10 border-error/30":r==="medium"?"text-warn bg-warn/10 border-warn/30":"text-text-muted bg-bg-tertiary border-border"}const J=[{id:"all",label:"All"},{id:"pending",label:"Pending"},{id:"approved",label:"Approved"},{id:"rejected",label:"Rejected"}];function ae(){const[r,o]=a.useState([]),[m,k]=a.useState(!0),[i,d]=a.useState(null),[l,w]=a.useState("all"),[h,c]=a.useState(null),[b,v]=a.useState({}),[u,j]=a.useState(null),{toast:p}=P(),x=a.useCallback(async()=>{try{const e=await z();o(e),d(null)}catch(e){d(e instanceof Error?e.message:"Failed to load approvals")}k(!1)},[]);a.useEffect(()=>{x()},[x]);const y=async e=>{var s;c(e.id);try{const n=(s=b[e.id])==null?void 0:s.trim();await $(e.id,"user",n||void 0),await x();const f=e.type==="goal_proposal"||e.type==="soma_proposal",T=e.type==="custom"&&e.payload.kind==="driver_blocked";p("success",f?"Work approved — TITAN is starting…":T?"Unblocked — TITAN is resuming…":"Approved"),v(E=>{const N={...E};return delete N[e.id],N}),j(null)}catch(n){d(n instanceof Error?n.message:"Approve failed"),p("error","Approval failed")}c(null)},S=async e=>{c(e.id);try{await _(e.id,"user"),await x(),p("success","Rejected")}catch(s){d(s instanceof Error?s.message:"Reject failed"),p("error","Rejection failed")}c(null)},g=l==="all"?r:r.filter(e=>e.status===l),C=J.map(e=>({...e,count:e.id==="all"?r.length:r.filter(s=>s.status===e.id).length}));return t.jsxs("div",{className:"space-y-4",children:[t.jsx(M,{title:"Approvals",breadcrumbs:[{label:"Command Post"},{label:"Approvals"}]}),t.jsx(W,{tabs:C,activeTab:l,onChange:w}),m&&t.jsx(F,{variant:"row",count:5}),i&&t.jsx("div",{className:"text-center py-8 text-error text-sm",children:i}),!m&&!i&&g.length===0&&t.jsx(L,{icon:t.jsx(I,{size:32}),title:"No approvals",description:l==="pending"?"No pending approvals.":"No approvals to show."}),!m&&!i&&g.length>0&&t.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:g.map(e=>{const s=H(e);return t.jsxs("div",{className:"flex items-start gap-3 px-4 py-3",children:[t.jsxs("div",{className:"flex-1 min-w-0",children:[t.jsxs("div",{className:"flex items-center gap-2 mb-1.5 flex-wrap",children:[t.jsxs("span",{className:`text-[10px] px-1.5 py-0.5 rounded border font-medium uppercase tracking-wide ${q(s.urgency)}`,children:[s.urgency==="high"&&t.jsx(B,{size:10,className:"inline mr-0.5"}),s.kindLabel]}),t.jsx(D,{status:e.status,size:"sm"}),t.jsxs("span",{className:"text-xs text-text-muted",children:["from ",t.jsx("span",{className:"font-medium text-text-secondary",children:e.requestedBy})]})]}),t.jsx("div",{className:"text-sm text-text font-medium leading-snug mb-0.5",children:s.headline}),s.detail&&t.jsx("div",{className:"text-xs text-text-secondary leading-snug",children:s.detail}),e.decidedBy&&t.jsxs("div",{className:"text-[10px] text-text-muted mt-1",children:["Decided by ",e.decidedBy,e.decisionNote?`: ${e.decisionNote}`:""]})]}),t.jsx("span",{className:"text-xs text-text-muted flex-shrink-0 pt-0.5",children:U(e.createdAt)}),e.status==="pending"&&t.jsxs("div",{className:"flex flex-col items-end gap-1 flex-shrink-0",children:[t.jsxs("div",{className:"flex items-center gap-1",children:[e.type==="custom"&&e.payload.kind==="driver_blocked"&&t.jsx("button",{onClick:()=>j(u===e.id?null:e.id),className:"text-[10px] text-text-muted hover:text-text-secondary px-2 py-1 rounded hover:bg-bg-tertiary transition-colors",children:u===e.id?"Cancel":"Answer"}),t.jsx(A,{size:"sm",variant:"ghost",onClick:()=>y(e),loading:h===e.id,icon:t.jsx(K,{size:14}),children:"Approve"}),t.jsx(A,{size:"sm",variant:"ghost",onClick:()=>S(e),loading:h===e.id,icon:t.jsx(R,{size:14}),children:"Reject"})]}),u===e.id&&t.jsx("input",{type:"text",value:b[e.id]||"",onChange:n=>v(f=>({...f,[e.id]:n.target.value})),placeholder:"Type your answer...",className:"w-64 rounded-md border border-border bg-bg px-3 py-1.5 text-xs text-text outline-none focus:border-accent mt-1",onKeyDown:n=>{n.key==="Enter"&&y(e)},autoFocus:!0})]})]},e.id)})})]})}export{ae as default};
1
+ import{r as a,J as P,z,j as t,K as B,X as R,O as $,P as _}from"./index-CmaGnHzR.js";import{B as A}from"./Input-Bzh0-cXU.js";import{S as D}from"./StatusBadge-e3tFf05B.js";import{P as M}from"./PageHeader-BJ1jHtOy.js";import{E as L}from"./EmptyState-iLOrbdU7.js";import{T as W}from"./Tabs-vGpPiBzr.js";import{S as F}from"./SkeletonLoader-IpWkLmoy.js";import{e as H}from"./approvalHeadline-DB9SgR-9.js";import{S as I}from"./shield-check-B24N03VU.js";import{C as K}from"./check-Cxp-6q4k.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DkIMxdwH.js";function U(r){const o=Math.floor((Date.now()-new Date(r).getTime())/1e3);return o<60?`${o}s`:o<3600?`${Math.floor(o/60)}m`:o<86400?`${Math.floor(o/3600)}h`:`${Math.floor(o/86400)}d`}function q(r){return r==="high"?"text-error bg-error/10 border-error/30":r==="medium"?"text-warn bg-warn/10 border-warn/30":"text-text-muted bg-bg-tertiary border-border"}const J=[{id:"all",label:"All"},{id:"pending",label:"Pending"},{id:"approved",label:"Approved"},{id:"rejected",label:"Rejected"}];function ae(){const[r,o]=a.useState([]),[m,k]=a.useState(!0),[i,d]=a.useState(null),[l,w]=a.useState("all"),[h,c]=a.useState(null),[b,v]=a.useState({}),[u,j]=a.useState(null),{toast:p}=P(),x=a.useCallback(async()=>{try{const e=await z();o(e),d(null)}catch(e){d(e instanceof Error?e.message:"Failed to load approvals")}k(!1)},[]);a.useEffect(()=>{x()},[x]);const y=async e=>{var s;c(e.id);try{const n=(s=b[e.id])==null?void 0:s.trim();await $(e.id,"user",n||void 0),await x();const f=e.type==="goal_proposal"||e.type==="soma_proposal",T=e.type==="custom"&&e.payload.kind==="driver_blocked";p("success",f?"Work approved — TITAN is starting…":T?"Unblocked — TITAN is resuming…":"Approved"),v(E=>{const N={...E};return delete N[e.id],N}),j(null)}catch(n){d(n instanceof Error?n.message:"Approve failed"),p("error","Approval failed")}c(null)},S=async e=>{c(e.id);try{await _(e.id,"user"),await x(),p("success","Rejected")}catch(s){d(s instanceof Error?s.message:"Reject failed"),p("error","Rejection failed")}c(null)},g=l==="all"?r:r.filter(e=>e.status===l),C=J.map(e=>({...e,count:e.id==="all"?r.length:r.filter(s=>s.status===e.id).length}));return t.jsxs("div",{className:"space-y-4",children:[t.jsx(M,{title:"Approvals",breadcrumbs:[{label:"Command Post"},{label:"Approvals"}]}),t.jsx(W,{tabs:C,activeTab:l,onChange:w}),m&&t.jsx(F,{variant:"row",count:5}),i&&t.jsx("div",{className:"text-center py-8 text-error text-sm",children:i}),!m&&!i&&g.length===0&&t.jsx(L,{icon:t.jsx(I,{size:32}),title:"No approvals",description:l==="pending"?"No pending approvals.":"No approvals to show."}),!m&&!i&&g.length>0&&t.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:g.map(e=>{const s=H(e);return t.jsxs("div",{className:"flex items-start gap-3 px-4 py-3",children:[t.jsxs("div",{className:"flex-1 min-w-0",children:[t.jsxs("div",{className:"flex items-center gap-2 mb-1.5 flex-wrap",children:[t.jsxs("span",{className:`text-[10px] px-1.5 py-0.5 rounded border font-medium uppercase tracking-wide ${q(s.urgency)}`,children:[s.urgency==="high"&&t.jsx(B,{size:10,className:"inline mr-0.5"}),s.kindLabel]}),t.jsx(D,{status:e.status,size:"sm"}),t.jsxs("span",{className:"text-xs text-text-muted",children:["from ",t.jsx("span",{className:"font-medium text-text-secondary",children:e.requestedBy})]})]}),t.jsx("div",{className:"text-sm text-text font-medium leading-snug mb-0.5",children:s.headline}),s.detail&&t.jsx("div",{className:"text-xs text-text-secondary leading-snug",children:s.detail}),e.decidedBy&&t.jsxs("div",{className:"text-[10px] text-text-muted mt-1",children:["Decided by ",e.decidedBy,e.decisionNote?`: ${e.decisionNote}`:""]})]}),t.jsx("span",{className:"text-xs text-text-muted flex-shrink-0 pt-0.5",children:U(e.createdAt)}),e.status==="pending"&&t.jsxs("div",{className:"flex flex-col items-end gap-1 flex-shrink-0",children:[t.jsxs("div",{className:"flex items-center gap-1",children:[e.type==="custom"&&e.payload.kind==="driver_blocked"&&t.jsx("button",{onClick:()=>j(u===e.id?null:e.id),className:"text-[10px] text-text-muted hover:text-text-secondary px-2 py-1 rounded hover:bg-bg-tertiary transition-colors",children:u===e.id?"Cancel":"Answer"}),t.jsx(A,{size:"sm",variant:"ghost",onClick:()=>y(e),loading:h===e.id,icon:t.jsx(K,{size:14}),children:"Approve"}),t.jsx(A,{size:"sm",variant:"ghost",onClick:()=>S(e),loading:h===e.id,icon:t.jsx(R,{size:14}),children:"Reject"})]}),u===e.id&&t.jsx("input",{type:"text",value:b[e.id]||"",onChange:n=>v(f=>({...f,[e.id]:n.target.value})),placeholder:"Type your answer...",className:"w-64 rounded-md border border-border bg-bg px-3 py-1.5 text-xs text-text outline-none focus:border-accent mt-1",onKeyDown:n=>{n.key==="Enter"&&y(e)},autoFocus:!0})]})]},e.id)})})]})}export{ae as default};
@@ -1 +1 @@
1
- import{r as s,a2 as F,j as e,a3 as I,a4 as $}from"./index-BUCi9exS.js";import{B as u}from"./Input-CzDjGc5Y.js";import{S as w}from"./StatusBadge-Do8GEljT.js";import{P as A}from"./PageHeader-BXEGoqDX.js";import{E as L}from"./EmptyState-B3lWjJU-.js";import{M as W}from"./Modal-DTzLKnUQ.js";import{S as M}from"./SkeletonLoader-DGQ0Q7vy.js";import{P as G}from"./plus-C63FIH_4.js";import{D as H}from"./dollar-sign-DLLEescF.js";import{T as R}from"./trash-2-BscnSMjc.js";import"./clsx-B-dksMZM.js";import"./chevron-right-BANsqvxz.js";import"./proxy-CV0RFkNZ.js";function q(r){const l=r.limitUsd>0?r.currentSpend/r.limitUsd*100:0;return l>=100?"exceeded":l>=r.warningThresholdPercent?"warning":"healthy"}function ae(){const[r,l]=s.useState([]),[c,C]=s.useState(!0),[n,m]=s.useState(null),[S,a]=s.useState(!1),[k,g]=s.useState(!1),[o,p]=s.useState(""),[i,P]=s.useState("global"),[h,b]=s.useState(""),[j,B]=s.useState("monthly"),[f,v]=s.useState("10"),[y,z]=s.useState("80"),[N,T]=s.useState("warn"),d=s.useCallback(async()=>{try{const t=await F();l(t),m(null)}catch(t){m(t instanceof Error?t.message:"Failed to load budgets")}C(!1)},[]);s.useEffect(()=>{d()},[d]);const D=async()=>{if(o.trim()){g(!0);try{await $({name:o.trim(),scope:{type:i,targetId:h||void 0},period:j,limitUsd:parseFloat(f)||10,warningThresholdPercent:parseInt(y)||80,action:N,enabled:!0}),a(!1),p(""),b(""),v("10"),await d()}catch{}g(!1)}},E=async t=>{try{await I(t),await d()}catch{}};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(A,{title:"Costs & Budgets",breadcrumbs:[{label:"Command Post"},{label:"Costs"}],actions:e.jsx(u,{size:"sm",icon:e.jsx(G,{size:14}),onClick:()=>a(!0),children:"Create Budget"})}),c&&e.jsx(M,{variant:"row",count:4}),n&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:n}),!c&&!n&&r.length===0&&e.jsx(L,{icon:e.jsx(H,{size:32}),title:"No budgets",description:"Create budget policies to control agent spending.",action:{label:"Create Budget",onClick:()=>a(!0)}}),!c&&!n&&r.length>0&&e.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:r.map(t=>{const U=t.limitUsd>0?t.currentSpend/t.limitUsd*100:0,x=q(t);return e.jsxs("div",{className:"flex items-center gap-4 px-4 py-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("span",{className:"text-sm font-medium text-text",children:t.name}),e.jsx(w,{status:x,size:"sm"}),!t.enabled&&e.jsx(w,{status:"disabled",size:"sm"})]}),e.jsxs("div",{className:"flex items-center gap-3 text-[10px] text-text-muted",children:[e.jsxs("span",{className:"capitalize",children:[t.scope.type,t.scope.targetId?`: ${t.scope.targetId}`:""]}),e.jsx("span",{className:"capitalize",children:t.period}),e.jsxs("span",{children:["Action: ",t.action]})]})]}),e.jsxs("div",{className:"text-right flex-shrink-0",children:[e.jsxs("div",{className:"text-sm font-medium text-text",children:["$",t.currentSpend.toFixed(2)," / $",t.limitUsd.toFixed(2)]}),e.jsx("div",{className:"w-24 h-1.5 bg-bg-tertiary rounded-full overflow-hidden mt-1",children:e.jsx("div",{className:`h-full rounded-full transition-all ${x==="exceeded"?"bg-error":x==="warning"?"bg-warning":"bg-success"}`,style:{width:`${Math.min(100,U)}%`}})})]}),e.jsx("button",{onClick:()=>E(t.id),className:"p-1.5 rounded-md text-text-muted hover:text-error hover:bg-error/10 transition-colors flex-shrink-0",title:"Delete budget",children:e.jsx(R,{size:14})})]},t.id)})}),e.jsx(W,{open:S,onClose:()=>a(!1),title:"Create Budget Policy",footer:e.jsxs(e.Fragment,{children:[e.jsx(u,{variant:"secondary",size:"sm",onClick:()=>a(!1),children:"Cancel"}),e.jsx(u,{size:"sm",onClick:D,loading:k,disabled:!o.trim(),children:"Create"})]}),children:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Name"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"Budget name...",value:o,onChange:t=>p(t.target.value),autoFocus:!0})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Scope"}),e.jsxs("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:i,onChange:t=>P(t.target.value),children:[e.jsx("option",{value:"global",children:"Global"}),e.jsx("option",{value:"agent",children:"Agent"}),e.jsx("option",{value:"goal",children:"Goal"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Period"}),e.jsxs("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:j,onChange:t=>B(t.target.value),children:[e.jsx("option",{value:"daily",children:"Daily"}),e.jsx("option",{value:"weekly",children:"Weekly"}),e.jsx("option",{value:"monthly",children:"Monthly"})]})]})]}),i!=="global"&&e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Target ID"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:`${i} ID...`,value:h,onChange:t=>b(t.target.value)})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Limit (USD)"}),e.jsx("input",{type:"number",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:f,onChange:t=>v(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Warning %"}),e.jsx("input",{type:"number",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:y,onChange:t=>z(t.target.value)})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Action on Threshold"}),e.jsxs("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:N,onChange:t=>T(t.target.value),children:[e.jsx("option",{value:"warn",children:"Warn"}),e.jsx("option",{value:"pause",children:"Pause"}),e.jsx("option",{value:"stop",children:"Stop"})]})]})]})})]})}export{ae as default};
1
+ import{r as s,a2 as F,j as e,a3 as I,a4 as $}from"./index-CmaGnHzR.js";import{B as u}from"./Input-Bzh0-cXU.js";import{S as w}from"./StatusBadge-e3tFf05B.js";import{P as A}from"./PageHeader-BJ1jHtOy.js";import{E as L}from"./EmptyState-iLOrbdU7.js";import{M as W}from"./Modal-nvni-LGR.js";import{S as M}from"./SkeletonLoader-IpWkLmoy.js";import{P as G}from"./plus-DxgNIt8h.js";import{D as H}from"./dollar-sign-DMXBeaTj.js";import{T as R}from"./trash-2-C5KNXS-O.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DkIMxdwH.js";import"./proxy-KYomRnLW.js";function q(r){const l=r.limitUsd>0?r.currentSpend/r.limitUsd*100:0;return l>=100?"exceeded":l>=r.warningThresholdPercent?"warning":"healthy"}function ae(){const[r,l]=s.useState([]),[c,C]=s.useState(!0),[n,m]=s.useState(null),[S,a]=s.useState(!1),[k,g]=s.useState(!1),[o,p]=s.useState(""),[i,P]=s.useState("global"),[h,b]=s.useState(""),[j,B]=s.useState("monthly"),[f,v]=s.useState("10"),[y,z]=s.useState("80"),[N,T]=s.useState("warn"),d=s.useCallback(async()=>{try{const t=await F();l(t),m(null)}catch(t){m(t instanceof Error?t.message:"Failed to load budgets")}C(!1)},[]);s.useEffect(()=>{d()},[d]);const D=async()=>{if(o.trim()){g(!0);try{await $({name:o.trim(),scope:{type:i,targetId:h||void 0},period:j,limitUsd:parseFloat(f)||10,warningThresholdPercent:parseInt(y)||80,action:N,enabled:!0}),a(!1),p(""),b(""),v("10"),await d()}catch{}g(!1)}},E=async t=>{try{await I(t),await d()}catch{}};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(A,{title:"Costs & Budgets",breadcrumbs:[{label:"Command Post"},{label:"Costs"}],actions:e.jsx(u,{size:"sm",icon:e.jsx(G,{size:14}),onClick:()=>a(!0),children:"Create Budget"})}),c&&e.jsx(M,{variant:"row",count:4}),n&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:n}),!c&&!n&&r.length===0&&e.jsx(L,{icon:e.jsx(H,{size:32}),title:"No budgets",description:"Create budget policies to control agent spending.",action:{label:"Create Budget",onClick:()=>a(!0)}}),!c&&!n&&r.length>0&&e.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:r.map(t=>{const U=t.limitUsd>0?t.currentSpend/t.limitUsd*100:0,x=q(t);return e.jsxs("div",{className:"flex items-center gap-4 px-4 py-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("span",{className:"text-sm font-medium text-text",children:t.name}),e.jsx(w,{status:x,size:"sm"}),!t.enabled&&e.jsx(w,{status:"disabled",size:"sm"})]}),e.jsxs("div",{className:"flex items-center gap-3 text-[10px] text-text-muted",children:[e.jsxs("span",{className:"capitalize",children:[t.scope.type,t.scope.targetId?`: ${t.scope.targetId}`:""]}),e.jsx("span",{className:"capitalize",children:t.period}),e.jsxs("span",{children:["Action: ",t.action]})]})]}),e.jsxs("div",{className:"text-right flex-shrink-0",children:[e.jsxs("div",{className:"text-sm font-medium text-text",children:["$",t.currentSpend.toFixed(2)," / $",t.limitUsd.toFixed(2)]}),e.jsx("div",{className:"w-24 h-1.5 bg-bg-tertiary rounded-full overflow-hidden mt-1",children:e.jsx("div",{className:`h-full rounded-full transition-all ${x==="exceeded"?"bg-error":x==="warning"?"bg-warning":"bg-success"}`,style:{width:`${Math.min(100,U)}%`}})})]}),e.jsx("button",{onClick:()=>E(t.id),className:"p-1.5 rounded-md text-text-muted hover:text-error hover:bg-error/10 transition-colors flex-shrink-0",title:"Delete budget",children:e.jsx(R,{size:14})})]},t.id)})}),e.jsx(W,{open:S,onClose:()=>a(!1),title:"Create Budget Policy",footer:e.jsxs(e.Fragment,{children:[e.jsx(u,{variant:"secondary",size:"sm",onClick:()=>a(!1),children:"Cancel"}),e.jsx(u,{size:"sm",onClick:D,loading:k,disabled:!o.trim(),children:"Create"})]}),children:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Name"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"Budget name...",value:o,onChange:t=>p(t.target.value),autoFocus:!0})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Scope"}),e.jsxs("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:i,onChange:t=>P(t.target.value),children:[e.jsx("option",{value:"global",children:"Global"}),e.jsx("option",{value:"agent",children:"Agent"}),e.jsx("option",{value:"goal",children:"Goal"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Period"}),e.jsxs("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:j,onChange:t=>B(t.target.value),children:[e.jsx("option",{value:"daily",children:"Daily"}),e.jsx("option",{value:"weekly",children:"Weekly"}),e.jsx("option",{value:"monthly",children:"Monthly"})]})]})]}),i!=="global"&&e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Target ID"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:`${i} ID...`,value:h,onChange:t=>b(t.target.value)})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Limit (USD)"}),e.jsx("input",{type:"number",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:f,onChange:t=>v(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Warning %"}),e.jsx("input",{type:"number",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:y,onChange:t=>z(t.target.value)})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Action on Threshold"}),e.jsxs("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:N,onChange:t=>T(t.target.value),children:[e.jsx("option",{value:"warn",children:"Warn"}),e.jsx("option",{value:"pause",children:"Pause"}),e.jsx("option",{value:"stop",children:"Stop"})]})]})]})})]})}export{ae as default};