titan-agent 5.0.2 → 5.1.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 (178) hide show
  1. package/dist/agent/agent.js +49 -3
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/agentLoop.js +83 -5
  4. package/dist/agent/agentLoop.js.map +1 -1
  5. package/dist/agent/commandPost.js +1 -1
  6. package/dist/agent/commandPost.js.map +1 -1
  7. package/dist/agent/goalProposer.js +2 -2
  8. package/dist/agent/goalProposer.js.map +1 -1
  9. package/dist/agent/missionDriver.js +1 -1
  10. package/dist/agent/missionDriver.js.map +1 -1
  11. package/dist/agent/promptBudget.js +85 -0
  12. package/dist/agent/promptBudget.js.map +1 -0
  13. package/dist/agent/structuredSpawn.js +1 -1
  14. package/dist/agent/structuredSpawn.js.map +1 -1
  15. package/dist/agent/subtaskTaxonomy.js +1 -1
  16. package/dist/agent/subtaskTaxonomy.js.map +1 -1
  17. package/dist/agent/systemPromptParts.js +10 -1
  18. package/dist/agent/systemPromptParts.js.map +1 -1
  19. package/dist/agent/toolRunner.js +16 -0
  20. package/dist/agent/toolRunner.js.map +1 -1
  21. package/dist/agent/toolSearch.js +4 -1
  22. package/dist/agent/toolSearch.js.map +1 -1
  23. package/dist/analytics/bugReports.js +1 -1
  24. package/dist/analytics/bugReports.js.map +1 -1
  25. package/dist/channels/messenger.js +1 -1
  26. package/dist/channels/messenger.js.map +1 -1
  27. package/dist/eval/harness.js +148 -0
  28. package/dist/eval/harness.js.map +1 -0
  29. package/dist/gateway/server.js +374 -74
  30. package/dist/gateway/server.js.map +1 -1
  31. package/dist/hooks/shellHooks.js +1 -1
  32. package/dist/hooks/shellHooks.js.map +1 -1
  33. package/dist/lib/auto-heal/repair-strategies.js.map +1 -1
  34. package/dist/memory/promptIncludes.js +58 -0
  35. package/dist/memory/promptIncludes.js.map +1 -0
  36. package/dist/organism/alertsStore.js +70 -0
  37. package/dist/organism/alertsStore.js.map +1 -0
  38. package/dist/plugins/memoryRetrieval.js.map +1 -1
  39. package/dist/providers/ollama.js +7 -7
  40. package/dist/providers/ollama.js.map +1 -1
  41. package/dist/safety/invariants.js +60 -0
  42. package/dist/safety/invariants.js.map +1 -0
  43. package/dist/safety/opusReview.js +1 -1
  44. package/dist/safety/opusReview.js.map +1 -1
  45. package/dist/security/commandScanner.js +2 -2
  46. package/dist/security/commandScanner.js.map +1 -1
  47. package/dist/security/secretGuard.js +4 -4
  48. package/dist/security/secretGuard.js.map +1 -1
  49. package/dist/skills/builtin/widget_gallery.js +28 -1
  50. package/dist/skills/builtin/widget_gallery.js.map +1 -1
  51. package/dist/skills/frontmatterLoader.js +119 -0
  52. package/dist/skills/frontmatterLoader.js.map +1 -0
  53. package/dist/skills/registry.js +20 -0
  54. package/dist/skills/registry.js.map +1 -1
  55. package/dist/testing/testHealthMonitor.js +1 -2
  56. package/dist/testing/testHealthMonitor.js.map +1 -1
  57. package/dist/utils/constants.js +2 -2
  58. package/dist/utils/constants.js.map +1 -1
  59. package/dist/utils/replyQuality.js +1 -1
  60. package/dist/utils/replyQuality.js.map +1 -1
  61. package/dist/utils/safety.js +25 -0
  62. package/dist/utils/safety.js.map +1 -0
  63. package/dist/utils/tokens.js +1 -1
  64. package/dist/utils/tokens.js.map +1 -1
  65. package/docs/bleeding-edge-agents-2026.md +450 -0
  66. package/docs/langchain-analysis.md +598 -0
  67. package/docs/langchain-code-analysis.md +363 -0
  68. package/docs/space-agent-analysis.md +300 -0
  69. package/package.json +1 -1
  70. package/ui/dist/assets/{AuditPanel-G7YA1HzV.js → AuditPanel-B84Mp16G.js} +2 -2
  71. package/ui/dist/assets/AutonomyPanel-DOtiTFxV.js +11 -0
  72. package/ui/dist/assets/{AutopilotPanel-CHRjxdh0.js → AutopilotPanel-nTb1Dnru.js} +1 -1
  73. package/ui/dist/assets/AutoresearchPanel-D46mX8VF.js +6 -0
  74. package/ui/dist/assets/BackupPanel-DGM1XXbG.js +1 -0
  75. package/ui/dist/assets/BrowserPanel-Cn1tTN3y.js +6 -0
  76. package/ui/dist/assets/{CPAgents-D5533PhK.js → CPAgents-CEraUkME.js} +1 -1
  77. package/ui/dist/assets/{CPDashboard-C-GgqDsI.js → CPDashboard-B_yidGAe.js} +2 -2
  78. package/ui/dist/assets/CPFiles-BBS8jtYH.js +1 -0
  79. package/ui/dist/assets/CPGoals-DL5v21TZ.js +1 -0
  80. package/ui/dist/assets/CPInbox-CyLQJBYF.js +11 -0
  81. package/ui/dist/assets/{CPSocial-mUQsrSh5.js → CPSocial-BkEtQ1Um.js} +3 -3
  82. package/ui/dist/assets/ChannelsPanel-CD2kHhA5.js +1 -0
  83. package/ui/dist/assets/CheckpointsPanel-BrUTFPu_.js +1 -0
  84. package/ui/dist/assets/CommandPostHub-BPPaUv1B.js +29 -0
  85. package/ui/dist/assets/CronPanel-CsfQctFp.js +1 -0
  86. package/ui/dist/assets/DaemonPanel-CNUggBbL.js +1 -0
  87. package/ui/dist/assets/DataTable-DuAEp_QJ.js +1 -0
  88. package/ui/dist/assets/{EmptyState-D60-wQrz.js → EmptyState-DFrAEZDm.js} +1 -1
  89. package/ui/dist/assets/EvalPanel-DEX0a5-b.js +1 -0
  90. package/ui/dist/assets/{FilesPanel-BNN3h_HW.js → FilesPanel-DATsiAqG.js} +1 -1
  91. package/ui/dist/assets/FleetPanel-QYQKqx4W.js +1 -0
  92. package/ui/dist/assets/{HomelabPanel-1mfhRBh6.js → HomelabPanel-DhuXd3ZD.js} +2 -2
  93. package/ui/dist/assets/{InfraView-Df6SFI7b.js → InfraView-eS7cpESw.js} +2 -2
  94. package/ui/dist/assets/InlineEditableField-zIAnW4AR.js +1 -0
  95. package/ui/dist/assets/{Input-DYukme8A.js → Input-bFsLI0fq.js} +1 -1
  96. package/ui/dist/assets/IntegrationsPanel-C_FswSRN.js +1 -0
  97. package/ui/dist/assets/IntelligenceView-smQ6aBwx.js +2 -0
  98. package/ui/dist/assets/{LearningPanel-BPx05bBu.js → LearningPanel-BEgF_iND.js} +1 -1
  99. package/ui/dist/assets/{LogsPanel-D3Qfp2SE.js → LogsPanel-Br1P8ST6.js} +1 -1
  100. package/ui/dist/assets/McpPanel-ByvQ12J_.js +1 -0
  101. package/ui/dist/assets/{MemoryGraphPanel-BFovwaSG.js → MemoryGraphPanel-BGOeSaET.js} +1 -1
  102. package/ui/dist/assets/MemoryWikiPanel-CR8btd66.js +11 -0
  103. package/ui/dist/assets/MeshPanel-BjkcSOMz.js +11 -0
  104. package/ui/dist/assets/NvidiaPanel-NYt42w7L.js +1 -0
  105. package/ui/dist/assets/OrganismPanel-PHvISvVn.js +1 -0
  106. package/ui/dist/assets/OverviewPanel-q35zdMr6.js +6 -0
  107. package/ui/dist/assets/{PageHeader-BdvxKoad.js → PageHeader-Cwn3OALc.js} +1 -1
  108. package/ui/dist/assets/PaperclipPanel-BDpQki0d.js +1 -0
  109. package/ui/dist/assets/{PersonasPanel-BpI6Npxv.js → PersonasPanel-DxrGW5C4.js} +1 -1
  110. package/ui/dist/assets/RecipesPanel-CYRdBx5u.js +1 -0
  111. package/ui/dist/assets/{SecurityPanel-CBDsEAFz.js → SecurityPanel-i1QMctV0.js} +1 -1
  112. package/ui/dist/assets/SelfImprovePanel-DbybAZWp.js +1 -0
  113. package/ui/dist/assets/SelfProposalsPanel-DtcTUDDd.js +2 -0
  114. package/ui/dist/assets/SessionsPanel-B7QmOizR.js +1 -0
  115. package/ui/dist/assets/SessionsTab-BdJj_vsI.js +1 -0
  116. package/ui/dist/assets/{SettingsPanel-BiWHsOAJ.js → SettingsPanel-DnEvJUFe.js} +1 -1
  117. package/ui/dist/assets/SettingsView-C39dk_yr.js +2 -0
  118. package/ui/dist/assets/{SkeletonLoader-CGtpZJ-7.js → SkeletonLoader-CsiR8ED9.js} +1 -1
  119. package/ui/dist/assets/{SkillsPanel-Z_9jA6dU.js → SkillsPanel-DM4qBFDS.js} +1 -1
  120. package/ui/dist/assets/{SomaView-AP3BXqf-.js → SomaView-CWnPKEQI.js} +1 -1
  121. package/ui/dist/assets/{StatCard-CrnvXPg5.js → StatCard-CY8lgeWm.js} +1 -1
  122. package/ui/dist/assets/{StatusBadge-B6r5EWBA.js → StatusBadge-CGvKbP7R.js} +1 -1
  123. package/ui/dist/assets/TeamsPanel-Bf6GaUni.js +1 -0
  124. package/ui/dist/assets/{TelemetryPanel-D6o14H-i.js → TelemetryPanel-JZ90gJXC.js} +1 -1
  125. package/ui/dist/assets/TitanCanvas-Hk49NFcA.js +1092 -0
  126. package/ui/dist/assets/ToolsView-Cq7Fuq3i.js +2 -0
  127. package/ui/dist/assets/{Tooltip-DNsYGHC9.js → Tooltip-CcoZrKsl.js} +1 -1
  128. package/ui/dist/assets/{TraceViewer-TOpdmqLF.js → TraceViewer-ojGf0drx.js} +1 -1
  129. package/ui/dist/assets/TrainingPanel-CWnP4H2l.js +1 -0
  130. package/ui/dist/assets/{VoiceOverlay-XIyCbAP7.js → VoiceOverlay-Dn6iaYgd.js} +1 -1
  131. package/ui/dist/assets/VramPanel-CLd9Ggck.js +1 -0
  132. package/ui/dist/assets/WatchView-CQBemwsm.js +13 -0
  133. package/ui/dist/assets/WorkTab-BOfTN-Bd.js +1 -0
  134. package/ui/dist/assets/WorkflowsPanel-qzNS0p0u.js +11 -0
  135. package/ui/dist/assets/{arrow-left-CQF-yBIU.js → arrow-left-c-8OFZUV.js} +1 -1
  136. package/ui/dist/assets/{chart-column-1smg0GbX.js → chart-column-x6L66Qw7.js} +1 -1
  137. package/ui/dist/assets/{circle-check-big-BiMDFx6C.js → circle-check-big-WaW3U3Xl.js} +1 -1
  138. package/ui/dist/assets/{dollar-sign-DMYH4Q_a.js → dollar-sign-D2Oce4Ru.js} +1 -1
  139. package/ui/dist/assets/{download-BYFd-yl6.js → download-YvPDLlFJ.js} +1 -1
  140. package/ui/dist/assets/eye-off-DIMcxsdQ.js +6 -0
  141. package/ui/dist/assets/{funnel-pWBglhfw.js → funnel-DqD9srZu.js} +1 -1
  142. package/ui/dist/assets/{git-branch-Cgqic2Us.js → git-branch-0FamUEbU.js} +1 -1
  143. package/ui/dist/assets/index-D932CbpQ.css +1 -0
  144. package/ui/dist/assets/index-NatBSFxj.js +227 -0
  145. package/ui/dist/assets/{legacy-BHbi-Nm_.js → legacy-DOO7F5cq.js} +1 -1
  146. package/ui/dist/assets/{lightbulb-D_y0Mtyq.js → lightbulb-Bk6KlR6q.js} +1 -1
  147. package/ui/dist/assets/pause-DDC_zUiJ.js +6 -0
  148. package/ui/dist/assets/{play-2xR4_zUG.js → play-BPXbHToG.js} +1 -1
  149. package/ui/dist/assets/{plug-DhvhYYy_.js → plug-Dxp-sWVF.js} +1 -1
  150. package/ui/dist/assets/proxy-vU7v4NVM.js +9 -0
  151. package/ui/dist/assets/square-Bn_0tYME.js +6 -0
  152. package/ui/dist/assets/target-BrtxUtzl.js +6 -0
  153. package/ui/dist/assets/toggle-right-CYphlpN5.js +11 -0
  154. package/ui/dist/assets/{trash-2-DmRaMz9e.js → trash-2-C_Jsp23A.js} +1 -1
  155. package/ui/dist/assets/{trending-up-DsDcs3Jo.js → trending-up-DrtLViSm.js} +1 -1
  156. package/ui/dist/assets/trophy-DdRzAOfo.js +6 -0
  157. package/ui/dist/index.html +2 -2
  158. package/ui/dist/assets/CPFiles-G7veSjMg.js +0 -6
  159. package/ui/dist/assets/CPGoals-C3DlKJrJ.js +0 -1
  160. package/ui/dist/assets/CPInbox-D10curQs.js +0 -16
  161. package/ui/dist/assets/ChannelsPanel-M3pO2htW.js +0 -1
  162. package/ui/dist/assets/CommandPostHub-CW9OY1A4.js +0 -37
  163. package/ui/dist/assets/InlineEditableField-CH-jR3LC.js +0 -11
  164. package/ui/dist/assets/IntegrationsPanel-EaN999Te.js +0 -1
  165. package/ui/dist/assets/IntelligenceView-Q4DBmJpJ.js +0 -2
  166. package/ui/dist/assets/McpPanel-zC7jTaSx.js +0 -6
  167. package/ui/dist/assets/MeshPanel-CqtYZ74K.js +0 -11
  168. package/ui/dist/assets/NvidiaPanel-BVIZFHet.js +0 -1
  169. package/ui/dist/assets/SelfImprovePanel-PSCYO6sx.js +0 -11
  170. package/ui/dist/assets/SessionsTab-Cn3dGgjX.js +0 -1
  171. package/ui/dist/assets/SettingsView-3BSIzAfW.js +0 -2
  172. package/ui/dist/assets/TitanCanvas-cnb7R1gS.js +0 -1056
  173. package/ui/dist/assets/ToolsView-Dp-xUWJG.js +0 -2
  174. package/ui/dist/assets/WorkTab-Pgq-iLz9.js +0 -1
  175. package/ui/dist/assets/WorkflowsPanel-B91LeW7r.js +0 -21
  176. package/ui/dist/assets/eye-BfW7UcEC.js +0 -11
  177. package/ui/dist/assets/index-BWSnB6Kr.js +0 -227
  178. package/ui/dist/assets/index-Dtw1pbjc.css +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/skills/registry.ts"],"sourcesContent":["/**\n * TITAN — Skills Registry\n * Discovers, loads, and manages skills from bundled, workspace, and marketplace sources.\n */\nimport { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport vm from 'vm';\nimport { TITAN_HOME, TITAN_SKILLS_DIR } from '../utils/constants.js';\nimport { registerTool, type ToolHandler } from '../agent/toolRunner.js';\nimport { ensureDir } from '../utils/helpers.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'Skills';\nconst DISABLED_SKILLS_PATH = join(TITAN_HOME, 'disabled-skills.json');\n\nexport interface SkillMeta {\n name: string;\n description: string;\n version: string;\n author?: string;\n source: 'bundled' | 'workspace' | 'marketplace';\n enabled: boolean;\n}\n\nconst registeredSkills: Map<string, SkillMeta> = new Map();\n\n/** Maps skill name → tool names belonging to that skill */\nconst skillToolMap: Map<string, Set<string>> = new Map();\n\n/** Register a built-in skill (tool handler + metadata) */\nexport function registerSkill(meta: SkillMeta, handler: ToolHandler): void {\n registeredSkills.set(meta.name, meta);\n // Track which tools belong to this skill\n if (!skillToolMap.has(meta.name)) {\n skillToolMap.set(meta.name, new Set());\n }\n skillToolMap.get(meta.name)!.add(handler.name);\n registerTool(handler);\n logger.debug(COMPONENT, `Registered skill: ${meta.name} (${meta.source})`);\n}\n\n/** Get all registered skills (with persisted enabled/disabled state applied) */\nexport function getSkills(): SkillMeta[] {\n const disabled = loadDisabledSkills();\n return Array.from(registeredSkills.values()).map(s => ({\n ...s,\n enabled: !disabled.includes(s.name),\n }));\n}\n\n/** Get a skill by name */\nexport function getSkill(name: string): SkillMeta | undefined {\n return registeredSkills.get(name);\n}\n\n/** Get tool names belonging to a skill */\nexport function getSkillTools(skillName: string): string[] {\n return Array.from(skillToolMap.get(skillName) || []);\n}\n\n/** Check if a skill is enabled */\nexport function isSkillEnabled(skillName: string): boolean {\n return !loadDisabledSkills().includes(skillName);\n}\n\n/** Check if a specific tool's parent skill is enabled */\nexport function isToolSkillEnabled(toolName: string): boolean {\n for (const [skillName, tools] of skillToolMap.entries()) {\n if (tools.has(toolName)) {\n return isSkillEnabled(skillName);\n }\n }\n return true; // Tools not belonging to any skill are always enabled\n}\n\n/** Toggle a skill on/off. Returns the new enabled state. */\nexport function toggleSkill(skillName: string): boolean {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n let nowEnabled: boolean;\n\n if (idx >= 0) {\n disabled.splice(idx, 1);\n nowEnabled = true;\n } else {\n disabled.push(skillName);\n nowEnabled = false;\n }\n\n saveDisabledSkills(disabled);\n logger.info(COMPONENT, `Skill \"${skillName}\" ${nowEnabled ? 'enabled' : 'disabled'}`);\n return nowEnabled;\n}\n\n/** Set a skill's enabled state explicitly */\nexport function setSkillEnabled(skillName: string, enabled: boolean): void {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n\n if (enabled && idx >= 0) {\n disabled.splice(idx, 1);\n } else if (!enabled && idx < 0) {\n disabled.push(skillName);\n }\n\n saveDisabledSkills(disabled);\n}\n\n/** Load disabled skills list from disk */\nfunction loadDisabledSkills(): string[] {\n try {\n if (existsSync(DISABLED_SKILLS_PATH)) {\n return JSON.parse(readFileSync(DISABLED_SKILLS_PATH, 'utf-8')) as string[];\n }\n } catch {\n // Corrupt file — treat as empty\n }\n return [];\n}\n\n/** Save disabled skills list to disk */\nfunction saveDisabledSkills(disabled: string[]): void {\n try {\n const dir = dirname(DISABLED_SKILLS_PATH);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(DISABLED_SKILLS_PATH, JSON.stringify(disabled, null, 2), 'utf-8');\n } catch (e) {\n logger.warn(COMPONENT, `Failed to save disabled skills: ${(e as Error).message}`);\n }\n}\n\n/** Discover workspace skills from ~/.titan/workspace/skills/ */\nexport function discoverWorkspaceSkills(): SkillMeta[] {\n ensureDir(TITAN_SKILLS_DIR);\n const discovered: SkillMeta[] = [];\n\n if (!existsSync(TITAN_SKILLS_DIR)) return discovered;\n\n const entries = readdirSync(TITAN_SKILLS_DIR, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const skillDir = join(TITAN_SKILLS_DIR, entry.name);\n const skillMdPath = join(skillDir, 'SKILL.md');\n\n if (!existsSync(skillMdPath)) continue;\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8');\n const meta = parseSkillMd(content, entry.name);\n if (meta) {\n discovered.push({ ...meta, source: 'workspace', enabled: true });\n }\n } catch (error) {\n logger.warn(COMPONENT, `Failed to load skill ${entry.name}: ${(error as Error).message}`);\n }\n }\n\n logger.info(COMPONENT, `Discovered ${discovered.length} workspace skills`);\n return discovered;\n}\n\n/** Parse SKILL.md frontmatter to extract metadata */\nfunction parseSkillMd(content: string, fallbackName: string): Omit<SkillMeta, 'source' | 'enabled'> | null {\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!frontmatterMatch) {\n return {\n name: fallbackName,\n description: content.split('\\n')[0] || 'No description',\n version: '1.0.0',\n };\n }\n\n const frontmatter = frontmatterMatch[1];\n const name = frontmatter.match(/name:\\s*(.+)/)?.[1]?.trim() || fallbackName;\n const description = frontmatter.match(/description:\\s*(.+)/)?.[1]?.trim() || 'No description';\n const version = frontmatter.match(/version:\\s*(.+)/)?.[1]?.trim() || '1.0.0';\n const author = frontmatter.match(/author:\\s*(.+)/)?.[1]?.trim();\n\n return { name, description, version, author };\n}\n\n/** Initialize all built-in skills */\nexport async function initBuiltinSkills(): Promise<void> {\n logger.info(COMPONENT, 'Loading built-in skills...');\n\n // Import and register built-in skills\n const { registerShellSkill } = await import('./builtin/shell.js');\n const { registerFilesystemSkill } = await import('./builtin/filesystem.js');\n const { registerWebSearchSkill } = await import('./builtin/web_search.js');\n const { registerCronSkill } = await import('./builtin/cron.js');\n const { registerWebhookSkill } = await import('./builtin/webhook.js');\n const { registerMemorySkill } = await import('./builtin/memory_skill.js');\n const { registerBrowserSkill } = await import('./builtin/browser.js');\n const { registerSessionsSkill } = await import('./builtin/sessions.js');\n const { registerProcessSkill } = await import('./builtin/process.js');\n const { registerWebFetchSkill } = await import('./builtin/web_fetch.js');\n const { registerApplyPatchSkill } = await import('./builtin/apply_patch.js');\n const { registerAutoGenerateSkill } = await import('./builtin/auto_generate.js');\n const { registerVisionSkill } = await import('./builtin/vision.js');\n const { registerVoiceSkills } = await import('./builtin/voice.js');\n const { registerMemoryGraphSkill } = await import('./builtin/memory_graph.js');\n const { initWebBrowserTool } = await import('./builtin/web_browser.js');\n const { registerGitHubSkill } = await import('./builtin/github.js');\n const { registerEmailSkill } = await import('./builtin/email.js');\n const { registerComputerUseSkill } = await import('./builtin/computer_use.js');\n const { registerImageGenSkill } = await import('./builtin/image_gen.js');\n const { registerPdfSkill } = await import('./builtin/pdf.js');\n const { registerCalendarSkill } = await import('./builtin/calendar.js');\n const { registerSmartHomeSkill } = await import('./builtin/smart_home.js');\n const { registerDataAnalysisSkill } = await import('./builtin/data_analysis.js');\n const { registerSkyvernSkill } = await import('./builtin/skyvern.js');\n const { registerWebBrowseLlmSkill } = await import('./builtin/web_browse_llm.js');\n const { registerIncomeTrackerSkill } = await import('./builtin/income_tracker.js');\n const { registerFreelanceMonitorSkill } = await import('./builtin/freelance_monitor.js');\n const { registerContentPublisherSkill } = await import('./builtin/content_publisher.js');\n const { registerLeadScorerSkill } = await import('./builtin/lead_scorer.js');\n const { registerHunterSkill } = await import('./builtin/hunter.js');\n const { registerCodeExecSkill } = await import('./builtin/code_exec.js');\n const { registerExecuteCodeSkill } = await import('./builtin/executeCode.js');\n const { registerWeatherSkill } = await import('./builtin/weather.js');\n const { registerGoalsSkill } = await import('./builtin/goals.js');\n const { registerXPosterSkill } = await import('./builtin/x_poster.js');\n const { initModelSwitchTool } = await import('./builtin/model_switch.js');\n const { registerRagSkill } = await import('./builtin/rag.js');\n const { registerDeepResearchSkill } = await import('./builtin/deep_research.js');\n const { registerSystemInfoSkill } = await import('./builtin/system_info.js');\n const { registerPersonaManagerSkill } = await import('./builtin/persona_manager.js');\n const { registerResearchPipelineSkill } = await import('./builtin/research_pipeline.js');\n const { registerAutoresearchSkill } = await import('./builtin/autoresearch.js');\n const { registerSelfDoctorSkill } = await import('./builtin/self_doctor.js');\n const { registerInteractionTrackerSkill } = await import('./builtin/interaction_tracker.js');\n const { registerFeedbackTrackerSkill } = await import('./builtin/feedback_tracker.js');\n const { registerGrowthExperimentsSkill } = await import('./builtin/growth_experiments.js');\n const { registerContentCalendarSkill } = await import('./builtin/content_calendar.js');\n const { registerSlackSkill } = await import('./builtin/slack.js');\n const { registerRevenueCatKBSkill } = await import('./builtin/revenuecat_kb.js');\n const { registerWeeklyReportSkill } = await import('./builtin/weekly_report.js');\n const { registerSelfImproveSkill } = await import('./builtin/self_improve.js');\n const { registerGepaSkill } = await import('./builtin/gepa.js');\n const { registerModelTrainerSkill } = await import('./builtin/model_trainer.js');\n const { registerSocialSchedulerSkill } = await import('./builtin/social_scheduler.js');\n const { registerStructuredOutputSkill } = await import('./builtin/structured_output.js');\n const { registerWorkflowsSkill } = await import('./builtin/workflows.js');\n const { registerAgentHandoffSkill } = await import('./builtin/agent_handoff.js');\n const { registerKnowledgeBaseSkill } = await import('./builtin/knowledge_base.js');\n const { registerEventTriggersSkill } = await import('./builtin/event_triggers.js');\n const { registerA2AProtocolSkill } = await import('./builtin/a2a_protocol.js');\n const { registerEvalsSkill } = await import('./builtin/evals.js');\n const { registerApprovalGatesSkill } = await import('./builtin/approval_gates.js');\n const { registerVRAMSkills } = await import('./builtin/vram.js');\n const { registerSecurityScanSkill } = await import('./builtin/security_scan.js');\n const { registerChangelogGenSkill } = await import('./builtin/changelog_gen.js');\n const { registerJiraLinearSkill } = await import('./builtin/jira_linear.js');\n const { registerAuditTrailSkill } = await import('./builtin/audit_trail.js');\n const { registerVisualPlanSkill } = await import('./builtin/visual_plan.js');\n const { registerScreenRecordSkill } = await import('./builtin/screen_record.js');\n const { registerSessionTeleportSkill } = await import('./builtin/session_teleport.js');\n const { registerCrossProviderSkill } = await import('./builtin/cross_provider.js');\n const { registerSentrySkill } = await import('./builtin/sentry.js');\n const { registerVideoSkill } = await import('./builtin/video.js');\n const { registerMixtureOfAgentsSkill } = await import('./builtin/mixture_of_agents.js');\n const { registerAgentDebateSkill } = await import('./builtin/agent_debate.js');\n const { registerFileCheckpointsSkill } = await import('./builtin/file_checkpoints.js');\n const { registerVerifyPageSkill } = await import('./builtin/verify_page.js');\n const { registerAgentMessagingSkill } = await import('./builtin/agent_messaging.js');\n const { registerFacebookSkill } = await import('./builtin/facebook.js');\n const { registerFBAutopilotSkill } = await import('./builtin/fb_autopilot.js');\n const { registerWidgetGallerySkill } = await import('./builtin/widget_gallery.js');\n\n const registrations: [string, () => void][] = [\n ['shell', registerShellSkill],\n ['filesystem', registerFilesystemSkill],\n ['web_search', registerWebSearchSkill],\n ['cron', registerCronSkill],\n ['webhook', registerWebhookSkill],\n ['memory', registerMemorySkill],\n ['browser', registerBrowserSkill],\n ['sessions', registerSessionsSkill],\n ['process', registerProcessSkill],\n ['web_fetch', registerWebFetchSkill],\n ['apply_patch', registerApplyPatchSkill],\n ['auto_generate', registerAutoGenerateSkill],\n ['vision', registerVisionSkill],\n ['voice', registerVoiceSkills],\n ['memory_graph', registerMemoryGraphSkill],\n ['web_browser', initWebBrowserTool],\n ['github', registerGitHubSkill],\n ['email', registerEmailSkill],\n ['computer_use', registerComputerUseSkill],\n ['image_gen', registerImageGenSkill],\n ['pdf', registerPdfSkill],\n ['calendar', registerCalendarSkill],\n ['smart_home', registerSmartHomeSkill],\n ['data_analysis', registerDataAnalysisSkill],\n ['skyvern', registerSkyvernSkill],\n ['web_browse_llm', registerWebBrowseLlmSkill],\n ['income_tracker', registerIncomeTrackerSkill],\n ['freelance_monitor', registerFreelanceMonitorSkill],\n ['content_publisher', registerContentPublisherSkill],\n ['lead_scorer', registerLeadScorerSkill],\n ['hunter', registerHunterSkill],\n ['code_exec', registerCodeExecSkill],\n ['execute_code', registerExecuteCodeSkill],\n ['weather', registerWeatherSkill],\n ['goals', registerGoalsSkill],\n ['x_poster', registerXPosterSkill],\n ['model_switch', initModelSwitchTool],\n ['rag', registerRagSkill],\n ['deep_research', registerDeepResearchSkill],\n ['system_info', registerSystemInfoSkill],\n ['persona_manager', registerPersonaManagerSkill],\n ['research_pipeline', registerResearchPipelineSkill],\n ['autoresearch', registerAutoresearchSkill],\n ['self_doctor', registerSelfDoctorSkill],\n ['interaction_tracker', registerInteractionTrackerSkill],\n ['feedback_tracker', registerFeedbackTrackerSkill],\n ['growth_experiments', registerGrowthExperimentsSkill],\n ['content_calendar', registerContentCalendarSkill],\n ['slack', registerSlackSkill],\n ['revenuecat_kb', registerRevenueCatKBSkill],\n ['weekly_report', registerWeeklyReportSkill],\n ['self_improve', registerSelfImproveSkill],\n ['gepa', registerGepaSkill],\n ['model_trainer', registerModelTrainerSkill],\n ['social_scheduler', registerSocialSchedulerSkill],\n ['structured_output', registerStructuredOutputSkill],\n ['workflows', registerWorkflowsSkill],\n ['agent_handoff', registerAgentHandoffSkill],\n ['knowledge_base', registerKnowledgeBaseSkill],\n ['event_triggers', registerEventTriggersSkill],\n ['evals', registerEvalsSkill],\n ['a2a_protocol', registerA2AProtocolSkill],\n ['approval_gates', registerApprovalGatesSkill],\n ['vram', registerVRAMSkills],\n ['security_scan', registerSecurityScanSkill],\n ['changelog_gen', registerChangelogGenSkill],\n ['jira_linear', registerJiraLinearSkill],\n ['audit_trail', registerAuditTrailSkill],\n ['visual_plan', registerVisualPlanSkill],\n ['screen_record', registerScreenRecordSkill],\n ['session_teleport', registerSessionTeleportSkill],\n ['cross_provider', registerCrossProviderSkill],\n ['sentry', registerSentrySkill],\n ['video', registerVideoSkill],\n ['mixture_of_agents', registerMixtureOfAgentsSkill],\n ['agent_debate', registerAgentDebateSkill],\n ['file_checkpoints', registerFileCheckpointsSkill],\n ['verify_page', registerVerifyPageSkill],\n ['agent_messaging', registerAgentMessagingSkill],\n ['facebook', registerFacebookSkill],\n ['fb_autopilot', registerFBAutopilotSkill],\n ['widget_gallery', registerWidgetGallerySkill],\n ];\n\n for (const [name, fn] of registrations) {\n try { fn(); } catch (e) { logger.warn(COMPONENT, `Failed to register skill \"${name}\": ${(e as Error).message}`); }\n }\n\n // Register planner as an LLM-invocable tool\n const { registerPlannerTool } = await import('../agent/planner.js');\n try { registerPlannerTool(); } catch (e) { logger.warn(COMPONENT, `Failed to register planner: ${(e as Error).message}`); }\n\n // Register TopFacts context engine plugin (DeerFlow-inspired persistent memory)\n try {\n const { createTopFactsPlugin } = await import('../plugins/topFacts.js');\n const { createMemoryRetrievalPlugin } = await import('../plugins/memoryRetrieval.js');\n const { registerPlugin } = await import('../plugins/registry.js');\n const topFacts = createTopFactsPlugin();\n registerPlugin(topFacts);\n if (topFacts.bootstrap) await topFacts.bootstrap({});\n const memoryRetrieval = createMemoryRetrievalPlugin();\n registerPlugin(memoryRetrieval);\n if (memoryRetrieval.bootstrap) await memoryRetrieval.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register TopFacts plugin: ${(e as Error).message}`); }\n\n // Register SmartCompress context engine plugin (task-type-aware compression)\n try {\n const { createSmartCompressPlugin } = await import('../plugins/smartCompress.js');\n const { registerPlugin: regPlugin } = await import('../plugins/registry.js');\n const smartCompress = createSmartCompressPlugin();\n regPlugin(smartCompress);\n if (smartCompress.bootstrap) await smartCompress.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register SmartCompress plugin: ${(e as Error).message}`); }\n\n // Register tool_search + tool_expand — meta-tools for discovering tools on demand\n // tool_expand is the progressive disclosure extension (Hermes competitive gap fix)\n const { getToolSearchHandler, getToolExpandHandler } = await import('../agent/toolSearch.js');\n try { registerTool(getToolSearchHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_search: ${(e as Error).message}`); }\n try { registerTool(getToolExpandHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_expand: ${(e as Error).message}`); }\n\n // F3: Register procedural memory tools (Hermes-inspired skill learning)\n try {\n const { saveSkill, searchSkills } = await import('./proceduralMemory.js');\n registerTool({\n name: 'save_skill',\n description: 'Save a reusable approach/technique as a procedural skill for future tasks. Use this when you discover an effective approach that could help in similar future situations.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Short descriptive name for the skill (e.g., \"Deploy Node.js app to Docker\")' },\n tags: { type: 'array', items: { type: 'string' }, description: 'Tags for searchability (e.g., [\"docker\", \"deployment\", \"nodejs\"])' },\n content: { type: 'string', description: 'The reusable approach/technique in markdown format. Include key steps, commands, and gotchas.' },\n },\n required: ['name', 'tags', 'content'],\n },\n execute: async (args: Record<string, unknown>) => {\n const name = args.name as string;\n const tags = (args.tags as string[]) || [];\n const content = args.content as string;\n if (!name || !content) return 'Error: name and content are required';\n const skill = saveSkill(name, tags, content);\n return `Skill saved: \"${skill.name}\" (tags: ${skill.tags.join(', ')}). It will be auto-recalled in future tasks matching these tags.`;\n },\n });\n registerTool({\n name: 'recall_skill',\n description: 'Search for previously saved procedural skills by keyword or tag. Returns reusable approaches from past tasks.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query — keywords or tags to find relevant skills' },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = args.query as string;\n if (!query) return 'Error: query is required';\n const results = searchSkills(query, 5);\n if (results.length === 0) return 'No matching skills found. Consider saving useful approaches with save_skill.';\n return results.map(s =>\n `### ${s.name}\\nTags: ${s.tags.join(', ')} | Used ${s.useCount}x\\n${s.content.slice(0, 800)}`\n ).join('\\n\\n---\\n\\n');\n },\n });\n logger.info(COMPONENT, 'Registered procedural memory tools (save_skill, recall_skill)');\n } catch (e) { logger.warn(COMPONENT, `Failed to register procedural memory tools: ${(e as Error).message}`); }\n\n logger.info(COMPONENT, `Loaded ${registeredSkills.size} built-in skills`);\n\n // Load dev skills (only in dev mode — skip import entirely in production)\n if (process.env.NODE_ENV !== 'production' || process.env.TITAN_DEV) {\n const { initDevSkills } = await import('./dev/loader.js');\n await initDevSkills();\n }\n\n // Load NVIDIA skills (optional — only when TITAN_NVIDIA=1 or nvidia.enabled in config)\n try {\n let nvidiaEnabled = process.env.TITAN_NVIDIA === '1';\n if (!nvidiaEnabled) {\n try {\n const { loadConfig: _loadConfig } = await import('../config/config.js');\n const cfg = _loadConfig() as Record<string, unknown>;\n const nvCfg = cfg.nvidia as Record<string, unknown> | undefined;\n nvidiaEnabled = nvCfg?.enabled === true;\n } catch { /* config not available in test env */ }\n }\n if (nvidiaEnabled) {\n const { initNvidiaSkills } = await import('./nvidia/loader.js');\n await initNvidiaSkills();\n }\n } catch (err) {\n logger.warn(COMPONENT, `NVIDIA skills failed to load: ${(err as Error).message}`);\n }\n\n // Load personal skills (private, gitignored — only when TITAN_PERSONAL=1)\n // Primary location: dist/skills/personal/loader.js (co-located with dist/skills/registry.js\n // so `../registry` resolves to the SAME module instance — tools register into the correct registry)\n // Fallback: ~/.titan/personal/loader.js (legacy / TITAN_PERSONAL_DIR override)\n if (process.env.TITAN_PERSONAL === '1') {\n try {\n const { pathToFileURL, fileURLToPath } = await import('node:url');\n const { join: _join, dirname: _dirname } = await import('node:path');\n // Compute dist/skills/ dir from this file's location (works on any machine)\n const thisDir = _dirname(fileURLToPath(import.meta.url));\n const distPersonalDir = _join(thisDir, 'personal');\n // TITAN_PERSONAL_DIR env var overrides; otherwise try dist-local first, then ~/.titan/personal/\n const personalDir = process.env.TITAN_PERSONAL_DIR\n || (existsSync(_join(distPersonalDir, 'loader.js')) ? distPersonalDir : _join(TITAN_HOME, 'personal'));\n const loaderPath = _join(personalDir, 'loader.js');\n if (existsSync(loaderPath)) {\n // Inject the main app's registerSkill into a global so the personal bundle\n // (which has its own bundled copy) uses the correct shared toolRegistry instance.\n (globalThis as Record<string, unknown>).__titanRegisterSkill = registerSkill;\n const { initPersonalSkills } = await import(pathToFileURL(loaderPath).href) as { initPersonalSkills: () => Promise<void> };\n await initPersonalSkills();\n } else {\n logger.warn(COMPONENT, `TITAN_PERSONAL=1 but ${loaderPath} not found — run: npm run build:personal`);\n }\n } catch (err) {\n logger.warn(COMPONENT, `Personal skills failed to load: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Discover and load user skills from ~/.titan/skills/ (all subdirs).\n * Supports:\n * 1. JavaScript files (.js) that export default { name, description, parameters, execute }\n * 2. YAML skill definitions (.yaml/.yml) with inline scripts\n * 3. Auto-generated skills from ~/.titan/skills/auto/\n */\nexport async function loadAutoSkills(): Promise<void> {\n const skillsRoot = join(TITAN_HOME, 'skills');\n if (!existsSync(skillsRoot)) return;\n\n logger.info(COMPONENT, 'Scanning for user skills...');\n let loadedCount = 0;\n\n // Scan both root and all subdirectories\n const dirsToScan = [skillsRoot];\n const entries = readdirSync(skillsRoot, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) dirsToScan.push(join(skillsRoot, entry.name));\n }\n\n for (const dir of dirsToScan) {\n const files = readdirSync(dir).filter(f =>\n f.endsWith('.js') || f.endsWith('.yaml') || f.endsWith('.yml')\n );\n\n for (const file of files) {\n const filePath = join(dir, file);\n try {\n if (file.endsWith('.js')) {\n // JavaScript skill — export default { name, description, parameters, execute }\n const modulePath = `file://${filePath}?t=${Date.now()}`;\n const mod = await import(modulePath);\n if (mod.default && mod.default.name && mod.default.execute) {\n const handler = mod.default as ToolHandler;\n if (registeredSkills.has(handler.name)) continue; // Skip duplicates\n registerSkill({\n name: handler.name,\n description: handler.description || 'User skill',\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, handler);\n loadedCount++;\n }\n } else {\n // YAML skill definition\n const loaded = loadYamlSkill(filePath);\n if (loaded && !registeredSkills.has(loaded.name)) {\n registerSkill({\n name: loaded.name,\n description: loaded.description,\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, loaded);\n loadedCount++;\n }\n }\n } catch (e: unknown) {\n logger.warn(COMPONENT, `Failed to load skill ${file}: ${(e as Error).message}`);\n }\n }\n }\n\n if (loadedCount > 0) {\n logger.info(COMPONENT, `Loaded ${loadedCount} user skill(s) from ~/.titan/skills/`);\n }\n}\n\n/**\n * Load a YAML skill definition.\n * Format:\n * name: my_tool\n * description: What it does\n * parameters:\n * myParam:\n * type: string\n * description: A parameter\n * required: true\n * script: |\n * // JavaScript code. Use `args.myParam` for inputs.\n * // Return a string result.\n * return \"Hello \" + args.myParam;\n */\nfunction loadYamlSkill(filePath: string): ToolHandler | null {\n const content = readFileSync(filePath, 'utf-8');\n\n // Simple YAML parser (no dependency needed for this basic format)\n const name = content.match(/^name:\\s*(.+)$/m)?.[1]?.trim();\n const description = content.match(/^description:\\s*(.+)$/m)?.[1]?.trim();\n const scriptMatch = content.match(/^script:\\s*\\|\\n([\\s\\S]+?)(?=\\n\\w|\\n$|$)/m);\n const script = scriptMatch?.[1]?.replace(/^ {2}/gm, ''); // Remove YAML indent\n\n if (!name || !description || !script) {\n logger.debug(COMPONENT, `Skipping ${filePath}: missing name, description, or script`);\n return null;\n }\n\n // Parse parameters section\n const paramsSection = content.match(/^parameters:\\n((?:\\s{2}\\w[\\s\\S]*?)(?=\\nscript:|\\n\\w|\\n$))/m);\n const properties: Record<string, Record<string, unknown>> = {};\n const required: string[] = [];\n\n if (paramsSection) {\n const paramLines = paramsSection[1].split('\\n');\n let currentParam = '';\n for (const line of paramLines) {\n const paramMatch = line.match(/^\\s{2}(\\w+):\\s*$/);\n if (paramMatch) {\n currentParam = paramMatch[1];\n properties[currentParam] = {};\n continue;\n }\n if (currentParam) {\n const typeMatch = line.match(/^\\s{4}type:\\s*(.+)$/);\n const descMatch = line.match(/^\\s{4}description:\\s*(.+)$/);\n const reqMatch = line.match(/^\\s{4}required:\\s*true$/);\n const defMatch = line.match(/^\\s{4}default:\\s*(.+)$/);\n if (typeMatch) properties[currentParam].type = typeMatch[1].trim();\n if (descMatch) properties[currentParam].description = descMatch[1].trim();\n if (reqMatch) required.push(currentParam);\n if (defMatch) properties[currentParam].default = defMatch[1].trim();\n }\n }\n }\n\n // Create the execute function from the script\n const handler: ToolHandler = {\n name,\n description,\n parameters: {\n type: 'object',\n properties,\n required: required.length > 0 ? required : undefined,\n },\n execute: async (args: Record<string, unknown>) => {\n try {\n // Run in a restricted VM context — no access to globalThis, process, eval, or Function\n const safeRequire = (mod: string) => {\n // SECURITY: child_process, http, https removed — YAML skills must use builtin tools for shell/network\n const allowed = ['fs', 'path', 'os', 'crypto', 'url', 'util'];\n if (!allowed.includes(mod)) throw new Error(`Module \"${mod}\" not allowed in YAML skills`);\n return require(mod); // eslint-disable-line @typescript-eslint/no-require-imports\n };\n const sandbox: Record<string, unknown> = {\n args,\n require: safeRequire,\n console: { log: console.log },\n JSON,\n Math,\n Date,\n String,\n Number,\n Array,\n Object,\n RegExp,\n Map,\n Set,\n Promise,\n setTimeout,\n Buffer,\n };\n // Wrap the user script in an async IIFE so `return` works and we can await it\n const wrapped = `(async function() { ${script} })()`;\n const result = await vm.runInNewContext(wrapped, sandbox, { timeout: 10000 });\n return typeof result === 'string' ? result : JSON.stringify(result, null, 2);\n } catch (err) {\n return `Error: ${(err as Error).message}`;\n }\n },\n };\n\n logger.debug(COMPONENT, `Loaded YAML skill: ${name} from ${filePath}`);\n return handler;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/skills/registry.ts"],"sourcesContent":["/**\n * TITAN — Skills Registry\n * Discovers, loads, and manages skills from bundled, workspace, and marketplace sources.\n */\nimport { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport vm from 'vm';\nimport { TITAN_HOME, TITAN_SKILLS_DIR } from '../utils/constants.js';\nimport { registerTool, type ToolHandler } from '../agent/toolRunner.js';\nimport { ensureDir } from '../utils/helpers.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'Skills';\nconst DISABLED_SKILLS_PATH = join(TITAN_HOME, 'disabled-skills.json');\n\nexport interface SkillMeta {\n name: string;\n description: string;\n version: string;\n author?: string;\n source: 'bundled' | 'workspace' | 'marketplace' | 'frontmatter';\n enabled: boolean;\n}\n\nconst registeredSkills: Map<string, SkillMeta> = new Map();\n\n/** Maps skill name → tool names belonging to that skill */\nconst skillToolMap: Map<string, Set<string>> = new Map();\n\n/** Register a built-in skill (tool handler + metadata) */\nexport function registerSkill(meta: SkillMeta, handler: ToolHandler): void {\n registeredSkills.set(meta.name, meta);\n // Track which tools belong to this skill\n if (!skillToolMap.has(meta.name)) {\n skillToolMap.set(meta.name, new Set());\n }\n skillToolMap.get(meta.name)!.add(handler.name);\n registerTool(handler);\n logger.debug(COMPONENT, `Registered skill: ${meta.name} (${meta.source})`);\n}\n\n/** Get all registered skills (with persisted enabled/disabled state applied) */\nexport function getSkills(): SkillMeta[] {\n const disabled = loadDisabledSkills();\n return Array.from(registeredSkills.values()).map(s => ({\n ...s,\n enabled: !disabled.includes(s.name),\n }));\n}\n\n/** Get a skill by name */\nexport function getSkill(name: string): SkillMeta | undefined {\n return registeredSkills.get(name);\n}\n\n/** Get tool names belonging to a skill */\nexport function getSkillTools(skillName: string): string[] {\n return Array.from(skillToolMap.get(skillName) || []);\n}\n\n/** Check if a skill is enabled */\nexport function isSkillEnabled(skillName: string): boolean {\n return !loadDisabledSkills().includes(skillName);\n}\n\n/** Check if a specific tool's parent skill is enabled */\nexport function isToolSkillEnabled(toolName: string): boolean {\n for (const [skillName, tools] of skillToolMap.entries()) {\n if (tools.has(toolName)) {\n return isSkillEnabled(skillName);\n }\n }\n return true; // Tools not belonging to any skill are always enabled\n}\n\n/** Toggle a skill on/off. Returns the new enabled state. */\nexport function toggleSkill(skillName: string): boolean {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n let nowEnabled: boolean;\n\n if (idx >= 0) {\n disabled.splice(idx, 1);\n nowEnabled = true;\n } else {\n disabled.push(skillName);\n nowEnabled = false;\n }\n\n saveDisabledSkills(disabled);\n logger.info(COMPONENT, `Skill \"${skillName}\" ${nowEnabled ? 'enabled' : 'disabled'}`);\n return nowEnabled;\n}\n\n/** Set a skill's enabled state explicitly */\nexport function setSkillEnabled(skillName: string, enabled: boolean): void {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n\n if (enabled && idx >= 0) {\n disabled.splice(idx, 1);\n } else if (!enabled && idx < 0) {\n disabled.push(skillName);\n }\n\n saveDisabledSkills(disabled);\n}\n\n/** Load disabled skills list from disk */\nfunction loadDisabledSkills(): string[] {\n try {\n if (existsSync(DISABLED_SKILLS_PATH)) {\n return JSON.parse(readFileSync(DISABLED_SKILLS_PATH, 'utf-8')) as string[];\n }\n } catch {\n // Corrupt file — treat as empty\n }\n return [];\n}\n\n/** Save disabled skills list to disk */\nfunction saveDisabledSkills(disabled: string[]): void {\n try {\n const dir = dirname(DISABLED_SKILLS_PATH);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(DISABLED_SKILLS_PATH, JSON.stringify(disabled, null, 2), 'utf-8');\n } catch (e) {\n logger.warn(COMPONENT, `Failed to save disabled skills: ${(e as Error).message}`);\n }\n}\n\n/** Discover workspace skills from ~/.titan/workspace/skills/ */\nexport function discoverWorkspaceSkills(): SkillMeta[] {\n ensureDir(TITAN_SKILLS_DIR);\n const discovered: SkillMeta[] = [];\n\n if (!existsSync(TITAN_SKILLS_DIR)) return discovered;\n\n const entries = readdirSync(TITAN_SKILLS_DIR, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const skillDir = join(TITAN_SKILLS_DIR, entry.name);\n const skillMdPath = join(skillDir, 'SKILL.md');\n\n if (!existsSync(skillMdPath)) continue;\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8');\n const meta = parseSkillMd(content, entry.name);\n if (meta) {\n discovered.push({ ...meta, source: 'workspace', enabled: true });\n }\n } catch (error) {\n logger.warn(COMPONENT, `Failed to load skill ${entry.name}: ${(error as Error).message}`);\n }\n }\n\n logger.info(COMPONENT, `Discovered ${discovered.length} workspace skills`);\n return discovered;\n}\n\n/** Parse SKILL.md frontmatter to extract metadata */\nfunction parseSkillMd(content: string, fallbackName: string): Omit<SkillMeta, 'source' | 'enabled'> | null {\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!frontmatterMatch) {\n return {\n name: fallbackName,\n description: content.split('\\n')[0] || 'No description',\n version: '1.0.0',\n };\n }\n\n const frontmatter = frontmatterMatch[1];\n const name = frontmatter.match(/name:\\s*(.+)/)?.[1]?.trim() || fallbackName;\n const description = frontmatter.match(/description:\\s*(.+)/)?.[1]?.trim() || 'No description';\n const version = frontmatter.match(/version:\\s*(.+)/)?.[1]?.trim() || '1.0.0';\n const author = frontmatter.match(/author:\\s*(.+)/)?.[1]?.trim();\n\n return { name, description, version, author };\n}\n\n/** Initialize all built-in skills */\nexport async function initBuiltinSkills(): Promise<void> {\n logger.info(COMPONENT, 'Loading built-in skills...');\n\n // Import and register built-in skills\n const { registerShellSkill } = await import('./builtin/shell.js');\n const { registerFilesystemSkill } = await import('./builtin/filesystem.js');\n const { registerWebSearchSkill } = await import('./builtin/web_search.js');\n const { registerCronSkill } = await import('./builtin/cron.js');\n const { registerWebhookSkill } = await import('./builtin/webhook.js');\n const { registerMemorySkill } = await import('./builtin/memory_skill.js');\n const { registerBrowserSkill } = await import('./builtin/browser.js');\n const { registerSessionsSkill } = await import('./builtin/sessions.js');\n const { registerProcessSkill } = await import('./builtin/process.js');\n const { registerWebFetchSkill } = await import('./builtin/web_fetch.js');\n const { registerApplyPatchSkill } = await import('./builtin/apply_patch.js');\n const { registerAutoGenerateSkill } = await import('./builtin/auto_generate.js');\n const { registerVisionSkill } = await import('./builtin/vision.js');\n const { registerVoiceSkills } = await import('./builtin/voice.js');\n const { registerMemoryGraphSkill } = await import('./builtin/memory_graph.js');\n const { initWebBrowserTool } = await import('./builtin/web_browser.js');\n const { registerGitHubSkill } = await import('./builtin/github.js');\n const { registerEmailSkill } = await import('./builtin/email.js');\n const { registerComputerUseSkill } = await import('./builtin/computer_use.js');\n const { registerImageGenSkill } = await import('./builtin/image_gen.js');\n const { registerPdfSkill } = await import('./builtin/pdf.js');\n const { registerCalendarSkill } = await import('./builtin/calendar.js');\n const { registerSmartHomeSkill } = await import('./builtin/smart_home.js');\n const { registerDataAnalysisSkill } = await import('./builtin/data_analysis.js');\n const { registerSkyvernSkill } = await import('./builtin/skyvern.js');\n const { registerWebBrowseLlmSkill } = await import('./builtin/web_browse_llm.js');\n const { registerIncomeTrackerSkill } = await import('./builtin/income_tracker.js');\n const { registerFreelanceMonitorSkill } = await import('./builtin/freelance_monitor.js');\n const { registerContentPublisherSkill } = await import('./builtin/content_publisher.js');\n const { registerLeadScorerSkill } = await import('./builtin/lead_scorer.js');\n const { registerHunterSkill } = await import('./builtin/hunter.js');\n const { registerCodeExecSkill } = await import('./builtin/code_exec.js');\n const { registerExecuteCodeSkill } = await import('./builtin/executeCode.js');\n const { registerWeatherSkill } = await import('./builtin/weather.js');\n const { registerGoalsSkill } = await import('./builtin/goals.js');\n const { registerXPosterSkill } = await import('./builtin/x_poster.js');\n const { initModelSwitchTool } = await import('./builtin/model_switch.js');\n const { registerRagSkill } = await import('./builtin/rag.js');\n const { registerDeepResearchSkill } = await import('./builtin/deep_research.js');\n const { registerSystemInfoSkill } = await import('./builtin/system_info.js');\n const { registerPersonaManagerSkill } = await import('./builtin/persona_manager.js');\n const { registerResearchPipelineSkill } = await import('./builtin/research_pipeline.js');\n const { registerAutoresearchSkill } = await import('./builtin/autoresearch.js');\n const { registerSelfDoctorSkill } = await import('./builtin/self_doctor.js');\n const { registerInteractionTrackerSkill } = await import('./builtin/interaction_tracker.js');\n const { registerFeedbackTrackerSkill } = await import('./builtin/feedback_tracker.js');\n const { registerGrowthExperimentsSkill } = await import('./builtin/growth_experiments.js');\n const { registerContentCalendarSkill } = await import('./builtin/content_calendar.js');\n const { registerSlackSkill } = await import('./builtin/slack.js');\n const { registerRevenueCatKBSkill } = await import('./builtin/revenuecat_kb.js');\n const { registerWeeklyReportSkill } = await import('./builtin/weekly_report.js');\n const { registerSelfImproveSkill } = await import('./builtin/self_improve.js');\n const { registerGepaSkill } = await import('./builtin/gepa.js');\n const { registerModelTrainerSkill } = await import('./builtin/model_trainer.js');\n const { registerSocialSchedulerSkill } = await import('./builtin/social_scheduler.js');\n const { registerStructuredOutputSkill } = await import('./builtin/structured_output.js');\n const { registerWorkflowsSkill } = await import('./builtin/workflows.js');\n const { registerAgentHandoffSkill } = await import('./builtin/agent_handoff.js');\n const { registerKnowledgeBaseSkill } = await import('./builtin/knowledge_base.js');\n const { registerEventTriggersSkill } = await import('./builtin/event_triggers.js');\n const { registerA2AProtocolSkill } = await import('./builtin/a2a_protocol.js');\n const { registerEvalsSkill } = await import('./builtin/evals.js');\n const { registerApprovalGatesSkill } = await import('./builtin/approval_gates.js');\n const { registerVRAMSkills } = await import('./builtin/vram.js');\n const { registerSecurityScanSkill } = await import('./builtin/security_scan.js');\n const { registerChangelogGenSkill } = await import('./builtin/changelog_gen.js');\n const { registerJiraLinearSkill } = await import('./builtin/jira_linear.js');\n const { registerAuditTrailSkill } = await import('./builtin/audit_trail.js');\n const { registerVisualPlanSkill } = await import('./builtin/visual_plan.js');\n const { registerScreenRecordSkill } = await import('./builtin/screen_record.js');\n const { registerSessionTeleportSkill } = await import('./builtin/session_teleport.js');\n const { registerCrossProviderSkill } = await import('./builtin/cross_provider.js');\n const { registerSentrySkill } = await import('./builtin/sentry.js');\n const { registerVideoSkill } = await import('./builtin/video.js');\n const { registerMixtureOfAgentsSkill } = await import('./builtin/mixture_of_agents.js');\n const { registerAgentDebateSkill } = await import('./builtin/agent_debate.js');\n const { registerFileCheckpointsSkill } = await import('./builtin/file_checkpoints.js');\n const { registerVerifyPageSkill } = await import('./builtin/verify_page.js');\n const { registerAgentMessagingSkill } = await import('./builtin/agent_messaging.js');\n const { registerFacebookSkill } = await import('./builtin/facebook.js');\n const { registerFBAutopilotSkill } = await import('./builtin/fb_autopilot.js');\n const { registerWidgetGallerySkill } = await import('./builtin/widget_gallery.js');\n\n const registrations: [string, () => void][] = [\n ['shell', registerShellSkill],\n ['filesystem', registerFilesystemSkill],\n ['web_search', registerWebSearchSkill],\n ['cron', registerCronSkill],\n ['webhook', registerWebhookSkill],\n ['memory', registerMemorySkill],\n ['browser', registerBrowserSkill],\n ['sessions', registerSessionsSkill],\n ['process', registerProcessSkill],\n ['web_fetch', registerWebFetchSkill],\n ['apply_patch', registerApplyPatchSkill],\n ['auto_generate', registerAutoGenerateSkill],\n ['vision', registerVisionSkill],\n ['voice', registerVoiceSkills],\n ['memory_graph', registerMemoryGraphSkill],\n ['web_browser', initWebBrowserTool],\n ['github', registerGitHubSkill],\n ['email', registerEmailSkill],\n ['computer_use', registerComputerUseSkill],\n ['image_gen', registerImageGenSkill],\n ['pdf', registerPdfSkill],\n ['calendar', registerCalendarSkill],\n ['smart_home', registerSmartHomeSkill],\n ['data_analysis', registerDataAnalysisSkill],\n ['skyvern', registerSkyvernSkill],\n ['web_browse_llm', registerWebBrowseLlmSkill],\n ['income_tracker', registerIncomeTrackerSkill],\n ['freelance_monitor', registerFreelanceMonitorSkill],\n ['content_publisher', registerContentPublisherSkill],\n ['lead_scorer', registerLeadScorerSkill],\n ['hunter', registerHunterSkill],\n ['code_exec', registerCodeExecSkill],\n ['execute_code', registerExecuteCodeSkill],\n ['weather', registerWeatherSkill],\n ['goals', registerGoalsSkill],\n ['x_poster', registerXPosterSkill],\n ['model_switch', initModelSwitchTool],\n ['rag', registerRagSkill],\n ['deep_research', registerDeepResearchSkill],\n ['system_info', registerSystemInfoSkill],\n ['persona_manager', registerPersonaManagerSkill],\n ['research_pipeline', registerResearchPipelineSkill],\n ['autoresearch', registerAutoresearchSkill],\n ['self_doctor', registerSelfDoctorSkill],\n ['interaction_tracker', registerInteractionTrackerSkill],\n ['feedback_tracker', registerFeedbackTrackerSkill],\n ['growth_experiments', registerGrowthExperimentsSkill],\n ['content_calendar', registerContentCalendarSkill],\n ['slack', registerSlackSkill],\n ['revenuecat_kb', registerRevenueCatKBSkill],\n ['weekly_report', registerWeeklyReportSkill],\n ['self_improve', registerSelfImproveSkill],\n ['gepa', registerGepaSkill],\n ['model_trainer', registerModelTrainerSkill],\n ['social_scheduler', registerSocialSchedulerSkill],\n ['structured_output', registerStructuredOutputSkill],\n ['workflows', registerWorkflowsSkill],\n ['agent_handoff', registerAgentHandoffSkill],\n ['knowledge_base', registerKnowledgeBaseSkill],\n ['event_triggers', registerEventTriggersSkill],\n ['evals', registerEvalsSkill],\n ['a2a_protocol', registerA2AProtocolSkill],\n ['approval_gates', registerApprovalGatesSkill],\n ['vram', registerVRAMSkills],\n ['security_scan', registerSecurityScanSkill],\n ['changelog_gen', registerChangelogGenSkill],\n ['jira_linear', registerJiraLinearSkill],\n ['audit_trail', registerAuditTrailSkill],\n ['visual_plan', registerVisualPlanSkill],\n ['screen_record', registerScreenRecordSkill],\n ['session_teleport', registerSessionTeleportSkill],\n ['cross_provider', registerCrossProviderSkill],\n ['sentry', registerSentrySkill],\n ['video', registerVideoSkill],\n ['mixture_of_agents', registerMixtureOfAgentsSkill],\n ['agent_debate', registerAgentDebateSkill],\n ['file_checkpoints', registerFileCheckpointsSkill],\n ['verify_page', registerVerifyPageSkill],\n ['agent_messaging', registerAgentMessagingSkill],\n ['facebook', registerFacebookSkill],\n ['fb_autopilot', registerFBAutopilotSkill],\n ['widget_gallery', registerWidgetGallerySkill],\n ];\n\n for (const [name, fn] of registrations) {\n try { fn(); } catch (e) { logger.warn(COMPONENT, `Failed to register skill \"${name}\": ${(e as Error).message}`); }\n }\n\n // Register planner as an LLM-invocable tool\n const { registerPlannerTool } = await import('../agent/planner.js');\n try { registerPlannerTool(); } catch (e) { logger.warn(COMPONENT, `Failed to register planner: ${(e as Error).message}`); }\n\n // Register TopFacts context engine plugin (DeerFlow-inspired persistent memory)\n try {\n const { createTopFactsPlugin } = await import('../plugins/topFacts.js');\n const { createMemoryRetrievalPlugin } = await import('../plugins/memoryRetrieval.js');\n const { registerPlugin } = await import('../plugins/registry.js');\n const topFacts = createTopFactsPlugin();\n registerPlugin(topFacts);\n if (topFacts.bootstrap) await topFacts.bootstrap({});\n const memoryRetrieval = createMemoryRetrievalPlugin();\n registerPlugin(memoryRetrieval);\n if (memoryRetrieval.bootstrap) await memoryRetrieval.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register TopFacts plugin: ${(e as Error).message}`); }\n\n // Register SmartCompress context engine plugin (task-type-aware compression)\n try {\n const { createSmartCompressPlugin } = await import('../plugins/smartCompress.js');\n const { registerPlugin: regPlugin } = await import('../plugins/registry.js');\n const smartCompress = createSmartCompressPlugin();\n regPlugin(smartCompress);\n if (smartCompress.bootstrap) await smartCompress.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register SmartCompress plugin: ${(e as Error).message}`); }\n\n // Register tool_search + tool_expand — meta-tools for discovering tools on demand\n // tool_expand is the progressive disclosure extension (Hermes competitive gap fix)\n const { getToolSearchHandler, getToolExpandHandler } = await import('../agent/toolSearch.js');\n try { registerTool(getToolSearchHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_search: ${(e as Error).message}`); }\n try { registerTool(getToolExpandHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_expand: ${(e as Error).message}`); }\n\n // F3: Register procedural memory tools (Hermes-inspired skill learning)\n try {\n const { saveSkill, searchSkills } = await import('./proceduralMemory.js');\n registerTool({\n name: 'save_skill',\n description: 'Save a reusable approach/technique as a procedural skill for future tasks. Use this when you discover an effective approach that could help in similar future situations.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Short descriptive name for the skill (e.g., \"Deploy Node.js app to Docker\")' },\n tags: { type: 'array', items: { type: 'string' }, description: 'Tags for searchability (e.g., [\"docker\", \"deployment\", \"nodejs\"])' },\n content: { type: 'string', description: 'The reusable approach/technique in markdown format. Include key steps, commands, and gotchas.' },\n },\n required: ['name', 'tags', 'content'],\n },\n execute: async (args: Record<string, unknown>) => {\n const name = args.name as string;\n const tags = (args.tags as string[]) || [];\n const content = args.content as string;\n if (!name || !content) return 'Error: name and content are required';\n const skill = saveSkill(name, tags, content);\n return `Skill saved: \"${skill.name}\" (tags: ${skill.tags.join(', ')}). It will be auto-recalled in future tasks matching these tags.`;\n },\n });\n registerTool({\n name: 'recall_skill',\n description: 'Search for previously saved procedural skills by keyword or tag. Returns reusable approaches from past tasks.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query — keywords or tags to find relevant skills' },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = args.query as string;\n if (!query) return 'Error: query is required';\n const results = searchSkills(query, 5);\n if (results.length === 0) return 'No matching skills found. Consider saving useful approaches with save_skill.';\n return results.map(s =>\n `### ${s.name}\\nTags: ${s.tags.join(', ')} | Used ${s.useCount}x\\n${s.content.slice(0, 800)}`\n ).join('\\n\\n---\\n\\n');\n },\n });\n logger.info(COMPONENT, 'Registered procedural memory tools (save_skill, recall_skill)');\n } catch (e) { logger.warn(COMPONENT, `Failed to register procedural memory tools: ${(e as Error).message}`); }\n\n logger.info(COMPONENT, `Loaded ${registeredSkills.size} built-in skills`);\n\n // Load dev skills (only in dev mode — skip import entirely in production)\n if (process.env.NODE_ENV !== 'production' || process.env.TITAN_DEV) {\n const { initDevSkills } = await import('./dev/loader.js');\n await initDevSkills();\n }\n\n // Load NVIDIA skills (optional — only when TITAN_NVIDIA=1 or nvidia.enabled in config)\n try {\n let nvidiaEnabled = process.env.TITAN_NVIDIA === '1';\n if (!nvidiaEnabled) {\n try {\n const { loadConfig: _loadConfig } = await import('../config/config.js');\n const cfg = _loadConfig() as Record<string, unknown>;\n const nvCfg = cfg.nvidia as Record<string, unknown> | undefined;\n nvidiaEnabled = nvCfg?.enabled === true;\n } catch { /* config not available in test env */ }\n }\n if (nvidiaEnabled) {\n const { initNvidiaSkills } = await import('./nvidia/loader.js');\n await initNvidiaSkills();\n }\n } catch (err) {\n logger.warn(COMPONENT, `NVIDIA skills failed to load: ${(err as Error).message}`);\n }\n\n // Load personal skills (private, gitignored — only when TITAN_PERSONAL=1)\n // Primary location: dist/skills/personal/loader.js (co-located with dist/skills/registry.js\n // so `../registry` resolves to the SAME module instance — tools register into the correct registry)\n // Fallback: ~/.titan/personal/loader.js (legacy / TITAN_PERSONAL_DIR override)\n if (process.env.TITAN_PERSONAL === '1') {\n try {\n const { pathToFileURL, fileURLToPath } = await import('node:url');\n const { join: _join, dirname: _dirname } = await import('node:path');\n // Compute dist/skills/ dir from this file's location (works on any machine)\n const thisDir = _dirname(fileURLToPath(import.meta.url));\n const distPersonalDir = _join(thisDir, 'personal');\n // TITAN_PERSONAL_DIR env var overrides; otherwise try dist-local first, then ~/.titan/personal/\n const personalDir = process.env.TITAN_PERSONAL_DIR\n || (existsSync(_join(distPersonalDir, 'loader.js')) ? distPersonalDir : _join(TITAN_HOME, 'personal'));\n const loaderPath = _join(personalDir, 'loader.js');\n if (existsSync(loaderPath)) {\n // Inject the main app's registerSkill into a global so the personal bundle\n // (which has its own bundled copy) uses the correct shared toolRegistry instance.\n (globalThis as Record<string, unknown>).__titanRegisterSkill = registerSkill;\n const { initPersonalSkills } = await import(pathToFileURL(loaderPath).href) as { initPersonalSkills: () => Promise<void> };\n await initPersonalSkills();\n } else {\n logger.warn(COMPONENT, `TITAN_PERSONAL=1 but ${loaderPath} not found — run: npm run build:personal`);\n }\n } catch (err) {\n logger.warn(COMPONENT, `Personal skills failed to load: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Discover and load user skills from ~/.titan/skills/ (all subdirs).\n * Supports:\n * 1. JavaScript files (.js) that export default { name, description, parameters, execute }\n * 2. YAML skill definitions (.yaml/.yml) with inline scripts\n * 3. Auto-generated skills from ~/.titan/skills/auto/\n */\nexport async function loadAutoSkills(): Promise<void> {\n const skillsRoot = join(TITAN_HOME, 'skills');\n if (!existsSync(skillsRoot)) return;\n\n logger.info(COMPONENT, 'Scanning for user skills...');\n let loadedCount = 0;\n\n // Scan both root and all subdirectories\n const dirsToScan = [skillsRoot];\n const entries = readdirSync(skillsRoot, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) dirsToScan.push(join(skillsRoot, entry.name));\n }\n\n for (const dir of dirsToScan) {\n const files = readdirSync(dir).filter(f =>\n f.endsWith('.js') || f.endsWith('.yaml') || f.endsWith('.yml')\n );\n\n for (const file of files) {\n const filePath = join(dir, file);\n try {\n if (file.endsWith('.js')) {\n // JavaScript skill — export default { name, description, parameters, execute }\n const modulePath = `file://${filePath}?t=${Date.now()}`;\n const mod = await import(modulePath);\n if (mod.default && mod.default.name && mod.default.execute) {\n const handler = mod.default as ToolHandler;\n if (registeredSkills.has(handler.name)) continue; // Skip duplicates\n registerSkill({\n name: handler.name,\n description: handler.description || 'User skill',\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, handler);\n loadedCount++;\n }\n } else {\n // YAML skill definition\n const loaded = loadYamlSkill(filePath);\n if (loaded && !registeredSkills.has(loaded.name)) {\n registerSkill({\n name: loaded.name,\n description: loaded.description,\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, loaded);\n loadedCount++;\n }\n }\n } catch (e: unknown) {\n logger.warn(COMPONENT, `Failed to load skill ${file}: ${(e as Error).message}`);\n }\n }\n }\n\n if (loadedCount > 0) {\n logger.info(COMPONENT, `Loaded ${loadedCount} user skill(s) from ~/.titan/skills/`);\n }\n\n // Load frontmatter skills (*.skill.md) — Space Agent / Hermes parity\n try {\n const { getFrontmatterToolHandlers } = await import('./frontmatterLoader.js');\n const fmHandlers = getFrontmatterToolHandlers();\n for (const { name, handler } of fmHandlers) {\n if (registeredSkills.has(name)) continue;\n registerSkill({\n name: handler.name,\n description: handler.description,\n version: '1.0.0',\n source: 'frontmatter',\n enabled: true,\n }, handler);\n loadedCount++;\n }\n if (fmHandlers.length > 0) {\n logger.info(COMPONENT, `Loaded ${fmHandlers.length} frontmatter skill(s)`);\n }\n } catch (e) {\n logger.warn(COMPONENT, `Frontmatter skills failed to load: ${(e as Error).message}`);\n }\n}\n\n/**\n * Load a YAML skill definition.\n * Format:\n * name: my_tool\n * description: What it does\n * parameters:\n * myParam:\n * type: string\n * description: A parameter\n * required: true\n * script: |\n * // JavaScript code. Use `args.myParam` for inputs.\n * // Return a string result.\n * return \"Hello \" + args.myParam;\n */\nfunction loadYamlSkill(filePath: string): ToolHandler | null {\n const content = readFileSync(filePath, 'utf-8');\n\n // Simple YAML parser (no dependency needed for this basic format)\n const name = content.match(/^name:\\s*(.+)$/m)?.[1]?.trim();\n const description = content.match(/^description:\\s*(.+)$/m)?.[1]?.trim();\n const scriptMatch = content.match(/^script:\\s*\\|\\n([\\s\\S]+?)(?=\\n\\w|\\n$|$)/m);\n const script = scriptMatch?.[1]?.replace(/^ {2}/gm, ''); // Remove YAML indent\n\n if (!name || !description || !script) {\n logger.debug(COMPONENT, `Skipping ${filePath}: missing name, description, or script`);\n return null;\n }\n\n // Parse parameters section\n const paramsSection = content.match(/^parameters:\\n((?:\\s{2}\\w[\\s\\S]*?)(?=\\nscript:|\\n\\w|\\n$))/m);\n const properties: Record<string, Record<string, unknown>> = {};\n const required: string[] = [];\n\n if (paramsSection) {\n const paramLines = paramsSection[1].split('\\n');\n let currentParam = '';\n for (const line of paramLines) {\n const paramMatch = line.match(/^\\s{2}(\\w+):\\s*$/);\n if (paramMatch) {\n currentParam = paramMatch[1];\n properties[currentParam] = {};\n continue;\n }\n if (currentParam) {\n const typeMatch = line.match(/^\\s{4}type:\\s*(.+)$/);\n const descMatch = line.match(/^\\s{4}description:\\s*(.+)$/);\n const reqMatch = line.match(/^\\s{4}required:\\s*true$/);\n const defMatch = line.match(/^\\s{4}default:\\s*(.+)$/);\n if (typeMatch) properties[currentParam].type = typeMatch[1].trim();\n if (descMatch) properties[currentParam].description = descMatch[1].trim();\n if (reqMatch) required.push(currentParam);\n if (defMatch) properties[currentParam].default = defMatch[1].trim();\n }\n }\n }\n\n // Create the execute function from the script\n const handler: ToolHandler = {\n name,\n description,\n parameters: {\n type: 'object',\n properties,\n required: required.length > 0 ? required : undefined,\n },\n execute: async (args: Record<string, unknown>) => {\n try {\n // Run in a restricted VM context — no access to globalThis, process, eval, or Function\n const safeRequire = (mod: string) => {\n // SECURITY: child_process, http, https removed — YAML skills must use builtin tools for shell/network\n const allowed = ['fs', 'path', 'os', 'crypto', 'url', 'util'];\n if (!allowed.includes(mod)) throw new Error(`Module \"${mod}\" not allowed in YAML skills`);\n return require(mod); // eslint-disable-line @typescript-eslint/no-require-imports\n };\n const sandbox: Record<string, unknown> = {\n args,\n require: safeRequire,\n console: { log: console.log },\n JSON,\n Math,\n Date,\n String,\n Number,\n Array,\n Object,\n RegExp,\n Map,\n Set,\n Promise,\n setTimeout,\n Buffer,\n };\n // Wrap the user script in an async IIFE so `return` works and we can await it\n const wrapped = `(async function() { ${script} })()`;\n const result = await vm.runInNewContext(wrapped, sandbox, { timeout: 10000 });\n return typeof result === 'string' ? result : JSON.stringify(result, null, 2);\n } catch (err) {\n return `Error: ${(err as Error).message}`;\n }\n },\n };\n\n logger.debug(COMPONENT, `Loaded YAML skill: ${name} from ${filePath}`);\n return handler;\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, readdirSync, appendFileSync, existsSync } from "fs";
2
+ import { readFileSync, readdirSync, appendFileSync, existsSync, mkdirSync } from "fs";
3
3
  import { join } from "path";
4
4
  import { spawn } from "child_process";
5
5
  const COVERAGE_SUMMARY = join(process.cwd(), "coverage", "coverage-summary.json");
@@ -16,7 +16,6 @@ function ensureLogDir() {
16
16
  try {
17
17
  const dir = join(process.env.HOME || "/tmp", ".titan");
18
18
  if (!existsSync(dir)) {
19
- const { mkdirSync } = require("fs");
20
19
  mkdirSync(dir, { recursive: true });
21
20
  }
22
21
  } catch {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/testHealthMonitor.ts"],"sourcesContent":["/**\n * TITAN — Test Health Monitor\n * Aggregates test suite health from coverage reports, test file counts,\n * and a persisted JSONL run log.\n */\nimport { readFileSync, readdirSync, appendFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { spawn } from 'child_process';\n\nconst COVERAGE_SUMMARY = join(process.cwd(), 'coverage', 'coverage-summary.json');\nconst TESTS_DIR = join(process.cwd(), 'tests');\nconst RUN_LOG = join(process.env.HOME || '/tmp', '.titan', 'test-runs.jsonl');\n\nfunction countTestFiles(dir = TESTS_DIR): number {\n try {\n return readdirSync(dir).filter(f => f.endsWith('.test.ts') || f.endsWith('.spec.ts')).length;\n } catch { return 0; }\n}\n\nfunction ensureLogDir(): void {\n try {\n const dir = join(process.env.HOME || '/tmp', '.titan');\n if (!existsSync(dir)) {\n // mkdirSync requires import, use shell instead\n const { mkdirSync } = require('fs');\n mkdirSync(dir, { recursive: true });\n }\n } catch { /* ok */ }\n}\n\ninterface TestRunRecord {\n timestamp: string;\n status: string;\n passed: number;\n failed: number;\n failingTests: string[];\n durationMs: number;\n}\n\nfunction readRunLog(): TestRunRecord[] {\n if (!existsSync(RUN_LOG)) return [];\n try {\n return readFileSync(RUN_LOG, 'utf-8')\n .split('\\n')\n .filter(Boolean)\n .map(line => JSON.parse(line) as TestRunRecord);\n } catch { return []; }\n}\n\n/** Run vitest with JSON reporter and capture detailed results */\nexport async function runTestsDetailed(opts?: {\n pattern?: string;\n watch?: boolean;\n coverage?: boolean;\n timeout?: number;\n}): Promise<{\n status: string;\n passed: number;\n failed: number;\n failingTests: string[];\n exitCode: number | null;\n}> {\n const args = ['vitest', opts?.watch ? '' : 'run', '--reporter=json'];\n if (opts?.pattern) args.push(opts.pattern);\n if (opts?.coverage) args.push('--coverage');\n if (opts?.timeout) args.push('--testTimeout', String(opts.timeout));\n\n const filtered = args.filter(Boolean) as string[];\n\n return new Promise((resolve) => {\n const start = Date.now();\n const child = spawn('npx', filtered, { cwd: process.cwd(), shell: true });\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (d) => { stdout += d.toString(); });\n child.stderr?.on('data', (d) => { stderr += d.toString(); });\n\n child.on('close', (code) => {\n const passMatch = stdout.match(/(\\d+)\\s+passed/);\n const failMatch = stdout.match(/(\\d+)\\s+failed/);\n const passed = passMatch ? parseInt(passMatch[1], 10) : 0;\n const failed = failMatch ? parseInt(failMatch[1], 10) : 0;\n\n // Parse JSON reporter output for failing test names\n const failingTests: string[] = [];\n try {\n const jsonStart = stdout.indexOf('{');\n if (jsonStart >= 0) {\n const jsonStr = stdout.slice(jsonStart);\n const report = JSON.parse(jsonStr);\n const suites = report.testResults || [];\n for (const suite of suites) {\n const assertions = suite.assertionResults || [];\n for (const assertion of assertions) {\n if (assertion.status === 'failed') {\n failingTests.push(assertion.title || 'unknown');\n }\n }\n }\n }\n } catch { /* JSON parse may fail if output is truncated */ }\n\n const record: TestRunRecord = {\n timestamp: new Date().toISOString(),\n status: code === 0 ? 'passed' : 'failed',\n passed,\n failed,\n failingTests,\n durationMs: Date.now() - start,\n };\n\n ensureLogDir();\n try {\n appendFileSync(RUN_LOG, JSON.stringify(record) + '\\n');\n } catch { /* non-critical */ }\n\n resolve({\n status: code === 0 ? 'passed' : 'failed',\n passed,\n failed,\n failingTests,\n exitCode: code,\n });\n });\n });\n}\n\nexport function getTestHealthSummary(): Record<string, unknown> {\n let total = 0;\n let passing = 0;\n let failing = 0;\n let coveragePct = 0;\n\n try {\n const summary = JSON.parse(readFileSync(COVERAGE_SUMMARY, 'utf-8'));\n const files = Object.keys(summary).filter(k => k !== 'total');\n total = files.length;\n passing = total; // coverage present implies tests ran and passed\n failing = 0;\n coveragePct = summary.total?.lines?.pct ?? 0;\n } catch {\n total = countTestFiles();\n }\n\n return {\n status: total > 0 ? 'ok' : 'no_data',\n total,\n passing,\n failing,\n coveragePct,\n testFiles: countTestFiles(),\n };\n}\n\nexport function getTestHealth(): Record<string, unknown> {\n return getTestHealthSummary();\n}\n\nexport function getFailingTests(): string[] {\n const runs = readRunLog();\n if (runs.length === 0) return [];\n const latest = runs[runs.length - 1];\n return latest.failingTests || [];\n}\n\nexport function getFlakyTests(threshold = 0.4): string[] {\n const runs = readRunLog();\n if (runs.length < 2) return [];\n\n const testHistory = new Map<string, { passes: number; fails: number }>();\n for (const run of runs) {\n const failedSet = new Set(run.failingTests || []);\n // We don't know which tests passed, only which failed.\n // For simplicity, treat any test that appears in any failure list as a candidate.\n for (const name of failedSet) {\n const entry = testHistory.get(name) || { passes: 0, fails: 0 };\n entry.fails++;\n testHistory.set(name, entry);\n }\n }\n\n const flaky: string[] = [];\n for (const [name, record] of testHistory) {\n const total = record.passes + record.fails;\n if (total > 0) {\n const failRate = record.fails / total;\n // A test is flaky if it fails sometimes but not always\n if (failRate > threshold && failRate < 1.0) {\n flaky.push(name);\n }\n }\n }\n return flaky;\n}\n\nexport function getRecentTestRuns(limit = 10): unknown[] {\n const runs = readRunLog();\n return runs.slice(-limit).reverse();\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/testing/testHealthMonitor.ts"],"sourcesContent":["/**\n * TITAN — Test Health Monitor\n * Aggregates test suite health from coverage reports, test file counts,\n * and a persisted JSONL run log.\n */\nimport { readFileSync, readdirSync, appendFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { spawn } from 'child_process';\n\nconst COVERAGE_SUMMARY = join(process.cwd(), 'coverage', 'coverage-summary.json');\nconst TESTS_DIR = join(process.cwd(), 'tests');\nconst RUN_LOG = join(process.env.HOME || '/tmp', '.titan', 'test-runs.jsonl');\n\nfunction countTestFiles(dir = TESTS_DIR): number {\n try {\n return readdirSync(dir).filter(f => f.endsWith('.test.ts') || f.endsWith('.spec.ts')).length;\n } catch { return 0; }\n}\n\nfunction ensureLogDir(): void {\n try {\n const dir = join(process.env.HOME || '/tmp', '.titan');\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n } catch { /* ok */ }\n}\n\ninterface TestRunRecord {\n timestamp: string;\n status: string;\n passed: number;\n failed: number;\n failingTests: string[];\n durationMs: number;\n}\n\nfunction readRunLog(): TestRunRecord[] {\n if (!existsSync(RUN_LOG)) return [];\n try {\n return readFileSync(RUN_LOG, 'utf-8')\n .split('\\n')\n .filter(Boolean)\n .map(line => JSON.parse(line) as TestRunRecord);\n } catch { return []; }\n}\n\n/** Run vitest with JSON reporter and capture detailed results */\nexport async function runTestsDetailed(opts?: {\n pattern?: string;\n watch?: boolean;\n coverage?: boolean;\n timeout?: number;\n}): Promise<{\n status: string;\n passed: number;\n failed: number;\n failingTests: string[];\n exitCode: number | null;\n}> {\n const args = ['vitest', opts?.watch ? '' : 'run', '--reporter=json'];\n if (opts?.pattern) args.push(opts.pattern);\n if (opts?.coverage) args.push('--coverage');\n if (opts?.timeout) args.push('--testTimeout', String(opts.timeout));\n\n const filtered = args.filter(Boolean) as string[];\n\n return new Promise((resolve) => {\n const start = Date.now();\n const child = spawn('npx', filtered, { cwd: process.cwd(), shell: true });\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (d) => { stdout += d.toString(); });\n child.stderr?.on('data', (d) => { stderr += d.toString(); });\n\n child.on('close', (code) => {\n const passMatch = stdout.match(/(\\d+)\\s+passed/);\n const failMatch = stdout.match(/(\\d+)\\s+failed/);\n const passed = passMatch ? parseInt(passMatch[1], 10) : 0;\n const failed = failMatch ? parseInt(failMatch[1], 10) : 0;\n\n // Parse JSON reporter output for failing test names\n const failingTests: string[] = [];\n try {\n const jsonStart = stdout.indexOf('{');\n if (jsonStart >= 0) {\n const jsonStr = stdout.slice(jsonStart);\n const report = JSON.parse(jsonStr);\n const suites = report.testResults || [];\n for (const suite of suites) {\n const assertions = suite.assertionResults || [];\n for (const assertion of assertions) {\n if (assertion.status === 'failed') {\n failingTests.push(assertion.title || 'unknown');\n }\n }\n }\n }\n } catch { /* JSON parse may fail if output is truncated */ }\n\n const record: TestRunRecord = {\n timestamp: new Date().toISOString(),\n status: code === 0 ? 'passed' : 'failed',\n passed,\n failed,\n failingTests,\n durationMs: Date.now() - start,\n };\n\n ensureLogDir();\n try {\n appendFileSync(RUN_LOG, JSON.stringify(record) + '\\n');\n } catch { /* non-critical */ }\n\n resolve({\n status: code === 0 ? 'passed' : 'failed',\n passed,\n failed,\n failingTests,\n exitCode: code,\n });\n });\n });\n}\n\nexport function getTestHealthSummary(): Record<string, unknown> {\n let total = 0;\n let passing = 0;\n let failing = 0;\n let coveragePct = 0;\n\n try {\n const summary = JSON.parse(readFileSync(COVERAGE_SUMMARY, 'utf-8'));\n const files = Object.keys(summary).filter(k => k !== 'total');\n total = files.length;\n passing = total; // coverage present implies tests ran and passed\n failing = 0;\n coveragePct = summary.total?.lines?.pct ?? 0;\n } catch {\n total = countTestFiles();\n }\n\n return {\n status: total > 0 ? 'ok' : 'no_data',\n total,\n passing,\n failing,\n coveragePct,\n testFiles: countTestFiles(),\n };\n}\n\nexport function getTestHealth(): Record<string, unknown> {\n return getTestHealthSummary();\n}\n\nexport function getFailingTests(): string[] {\n const runs = readRunLog();\n if (runs.length === 0) return [];\n const latest = runs[runs.length - 1];\n return latest.failingTests || [];\n}\n\nexport function getFlakyTests(threshold = 0.4): string[] {\n const runs = readRunLog();\n if (runs.length < 2) return [];\n\n const testHistory = new Map<string, { passes: number; fails: number }>();\n for (const run of runs) {\n const failedSet = new Set(run.failingTests || []);\n // We don't know which tests passed, only which failed.\n // For simplicity, treat any test that appears in any failure list as a candidate.\n for (const name of failedSet) {\n const entry = testHistory.get(name) || { passes: 0, fails: 0 };\n entry.fails++;\n testHistory.set(name, entry);\n }\n }\n\n const flaky: string[] = [];\n for (const [name, record] of testHistory) {\n const total = record.passes + record.fails;\n if (total > 0) {\n const failRate = record.fails / total;\n // A test is flaky if it fails sometimes but not always\n if (failRate > threshold && failRate < 1.0) {\n flaky.push(name);\n }\n }\n }\n return flaky;\n}\n\nexport function getRecentTestRuns(limit = 10): unknown[] {\n const runs = readRunLog();\n return runs.slice(-limit).reverse();\n}\n"],"mappings":"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","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 = "5.0.2";
4
+ const TITAN_VERSION = "5.1.0";
5
5
  const TITAN_CODENAME = "Spacewalk";
6
6
  const TITAN_NAME = "TITAN";
7
7
  const TITAN_FULL_NAME = "The Intelligent Task Automation Network";
@@ -56,7 +56,7 @@ const DEFAULT_GATEWAY_HOST = "0.0.0.0";
56
56
  const DEFAULT_GATEWAY_PORT = 48420;
57
57
  const DEFAULT_WEB_PORT = 48421;
58
58
  const DEFAULT_MODEL = "anthropic/claude-sonnet-4-20250514";
59
- const DEFAULT_MAX_TOKENS = 8192;
59
+ const DEFAULT_MAX_TOKENS = 12e3;
60
60
  const DEFAULT_TEMPERATURE = 0.7;
61
61
  const MAX_CONTEXT_MESSAGES = 50;
62
62
  const SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
@@ -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 = '5.0.2';\nexport const TITAN_CODENAME = 'Spacewalk';\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');\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\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\nexport const DEFAULT_MAX_TOKENS = 8192;\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;AAGrE,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AAGzB,MAAM,gBAAgB;AACtB,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 = '5.1.0';\nexport const TITAN_CODENAME = 'Spacewalk';\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');\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\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\nexport const DEFAULT_MAX_TOKENS = 12000;\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;AAGrE,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AAGzB,MAAM,gBAAgB;AACtB,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":[]}
@@ -11,7 +11,7 @@ function validateReply(reply, commenterFirstName) {
11
11
  const lastTwo = trimmed.slice(-2);
12
12
  const endsInPunctuation = /[.!?…]/.test(lastChar);
13
13
  const endsInQuote = /["')\]}]/.test(lastChar);
14
- const endsInEmoji = /[^\x00-\x7F]/.test(lastChar) || /[^\x00-\x7F][\uFE0F\u200D]?/.test(lastTwo);
14
+ const endsInEmoji = /[^\p{ASCII}]/u.test(lastChar) || /[^\p{ASCII}][\uFE0F\u200D]?/u.test(lastTwo);
15
15
  if (!endsInPunctuation && !endsInQuote && !endsInEmoji) {
16
16
  return {
17
17
  valid: false,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/replyQuality.ts"],"sourcesContent":["/**\n * TITAN — Reply Quality Validation\n *\n * Pure, fully testable validation for outbound replies (FB comments, Messenger, etc.)\n * Catches problems that the sanitizer misses because they're about the REPLY SHAPE\n * rather than content patterns:\n *\n * - Mid-sentence truncation (e.g. \"I must've imagined the whole\" — cut off)\n * - Overly apologetic / self-deprecating tone (bad PR for the brand)\n * - Empty or too-short replies\n * - Replies that are just the commenter's name back at them\n *\n * Created after the 2026-04-14 truncation incident where minimax-m2.7 returned\n * \"You're right Tony, I must've imagined the whole\" — 47 chars, no punctuation,\n * cut off mid-word.\n */\n\nexport interface ReplyValidation {\n valid: boolean;\n reason?: string;\n /** Suggested action: 'reject' | 'retry' | 'fallback' */\n action?: 'reject' | 'retry' | 'fallback';\n}\n\n/**\n * Validate the quality and completeness of a reply.\n * Returns { valid: true } if the reply can be posted as-is.\n * Returns { valid: false, reason, action } with a human-readable reason and\n * a suggested corrective action.\n */\nexport function validateReply(reply: string, commenterFirstName?: string): ReplyValidation {\n const trimmed = reply.trim();\n\n // ── Empty / too short ──────────────────────────────────────\n if (!trimmed) {\n return { valid: false, reason: 'empty', action: 'fallback' };\n }\n if (trimmed.length < 8) {\n return { valid: false, reason: 'too_short', action: 'fallback' };\n }\n\n // ── Truncation detection ──────────────────────────────────\n // A complete reply should end in terminal punctuation, an emoji,\n // a quote mark, or a closing bracket. Anything else means the model\n // was mid-sentence when it stopped.\n const lastChar = trimmed.slice(-1);\n const lastTwo = trimmed.slice(-2);\n const endsInPunctuation = /[.!?…]/.test(lastChar);\n const endsInQuote = /[\"')\\]}]/.test(lastChar);\n // Emoji detection — any non-ASCII char at the end is likely an emoji\n const endsInEmoji = /[^\\x00-\\x7F]/.test(lastChar) || /[^\\x00-\\x7F][\\uFE0F\\u200D]?/.test(lastTwo);\n\n if (!endsInPunctuation && !endsInQuote && !endsInEmoji) {\n return {\n valid: false,\n reason: `truncated_no_terminal_punct (ends with \"${trimmed.slice(-15)}\")`,\n action: 'retry',\n };\n }\n\n // ── Mid-word truncation detection ─────────────────────────\n // Words like \"the whole\" at the end (no noun following) are suspicious\n // when combined with no proper closing.\n // A reply ending in these \"dangling\" phrases usually means the model cut off.\n const danglingEndings = [\n /\\bthe\\s+whole\\s*$/i,\n /\\bthe\\s+entire\\s*$/i,\n /\\ba\\s+(?:little|bit|lot|few)\\s*$/i,\n /\\bmust['']?(?:ve|\\s+have)\\s*$/i,\n /\\bgoing\\s+to\\s*$/i,\n /\\bsupposed\\s+to\\s*$/i,\n /\\btrying\\s+to\\s*$/i,\n /\\bI\\s*$/i,\n /\\bmy\\s*$/i,\n /\\band\\s*$/i,\n /\\bor\\s*$/i,\n /\\bbut\\s*$/i,\n /\\bso\\s*$/i,\n /\\bfor\\s*$/i,\n /\\bof\\s*$/i,\n /\\bwith\\s*$/i,\n ];\n for (const pattern of danglingEndings) {\n if (pattern.test(trimmed)) {\n return {\n valid: false,\n reason: `truncated_dangling_phrase`,\n action: 'retry',\n };\n }\n }\n\n // ── Overly apologetic / self-deprecating tone ──────────────\n // TITAN's brand voice is confident. Admitting to being broken or wrong\n // publicly (except playfully) damages credibility. Detect the \"You're\n // right, I'm wrong\" shapes.\n const selfDeprecatingPatterns = [\n /^you['']?re right,?\\s+(?:I|tony|sorry|my)/i,\n /^(?:sorry|apologies|my bad|you['']?re correct),?\\s+I/i,\n /^I (?:apologize|was wrong|messed up|screwed up|got it wrong)/i,\n /\\bI (?:must['']?(?:ve| have)|apparently|clearly) (?:hallucinat|imagined|made up|fabricated)/i,\n /\\byou['']?re right[,.\\s]+I was wrong/i,\n ];\n for (const pattern of selfDeprecatingPatterns) {\n if (pattern.test(trimmed)) {\n return {\n valid: false,\n reason: 'too_self_deprecating (breaks brand voice)',\n action: 'fallback',\n };\n }\n }\n\n // ── Just echoing the commenter's name ──────────────────────\n if (commenterFirstName) {\n // Reply is ONLY the name + minimal decoration\n const justNamePattern = new RegExp(\n `^(?:hi|hey|hello|thanks|thank you),?\\\\s*${commenterFirstName}[.!?\\\\s]*$`,\n 'i',\n );\n if (justNamePattern.test(trimmed)) {\n return {\n valid: false,\n reason: 'just_name_echo',\n action: 'fallback',\n };\n }\n }\n\n // ── Too many exclamation marks (cheesy) ────────────────────\n const exclamationCount = (trimmed.match(/!/g) || []).length;\n if (exclamationCount >= 4) {\n return {\n valid: false,\n reason: 'too_many_exclamations',\n action: 'retry',\n };\n }\n\n // ── Passes all checks ──────────────────────────────────────\n return { valid: true };\n}\n\n/**\n * Generate a safe fallback reply when validation fails.\n * Returns a neutral, on-brand reply that works for any comment.\n */\nexport function getSafeFallbackReply(commenterFirstName: string): string {\n const fallbacks = [\n `Thanks for the comment, ${commenterFirstName}! 🤖`,\n `Appreciate you stopping by, ${commenterFirstName}! 🙌`,\n `Always good to hear from the community. 🚀`,\n `Noted, ${commenterFirstName}. Back to work for me! ⚡`,\n `Cheers for that, ${commenterFirstName}! 💪`,\n ];\n return fallbacks[Math.floor(Math.random() * fallbacks.length)];\n}\n"],"mappings":";AA8BO,SAAS,cAAc,OAAe,oBAA8C;AACvF,QAAM,UAAU,MAAM,KAAK;AAG3B,MAAI,CAAC,SAAS;AACV,WAAO,EAAE,OAAO,OAAO,QAAQ,SAAS,QAAQ,WAAW;AAAA,EAC/D;AACA,MAAI,QAAQ,SAAS,GAAG;AACpB,WAAO,EAAE,OAAO,OAAO,QAAQ,aAAa,QAAQ,WAAW;AAAA,EACnE;AAMA,QAAM,WAAW,QAAQ,MAAM,EAAE;AACjC,QAAM,UAAU,QAAQ,MAAM,EAAE;AAChC,QAAM,oBAAoB,SAAS,KAAK,QAAQ;AAChD,QAAM,cAAc,WAAW,KAAK,QAAQ;AAE5C,QAAM,cAAc,eAAe,KAAK,QAAQ,KAAK,8BAA8B,KAAK,OAAO;AAE/F,MAAI,CAAC,qBAAqB,CAAC,eAAe,CAAC,aAAa;AACpD,WAAO;AAAA,MACH,OAAO;AAAA,MACP,QAAQ,2CAA2C,QAAQ,MAAM,GAAG,CAAC;AAAA,MACrE,QAAQ;AAAA,IACZ;AAAA,EACJ;AAMA,QAAM,kBAAkB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,aAAW,WAAW,iBAAiB;AACnC,QAAI,QAAQ,KAAK,OAAO,GAAG;AACvB,aAAO;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACZ;AAAA,IACJ;AAAA,EACJ;AAMA,QAAM,0BAA0B;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,aAAW,WAAW,yBAAyB;AAC3C,QAAI,QAAQ,KAAK,OAAO,GAAG;AACvB,aAAO;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACZ;AAAA,IACJ;AAAA,EACJ;AAGA,MAAI,oBAAoB;AAEpB,UAAM,kBAAkB,IAAI;AAAA,MACxB,2CAA2C,kBAAkB;AAAA,MAC7D;AAAA,IACJ;AACA,QAAI,gBAAgB,KAAK,OAAO,GAAG;AAC/B,aAAO;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACZ;AAAA,IACJ;AAAA,EACJ;AAGA,QAAM,oBAAoB,QAAQ,MAAM,IAAI,KAAK,CAAC,GAAG;AACrD,MAAI,oBAAoB,GAAG;AACvB,WAAO;AAAA,MACH,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA,IACZ;AAAA,EACJ;AAGA,SAAO,EAAE,OAAO,KAAK;AACzB;AAMO,SAAS,qBAAqB,oBAAoC;AACrE,QAAM,YAAY;AAAA,IACd,2BAA2B,kBAAkB;AAAA,IAC7C,+BAA+B,kBAAkB;AAAA,IACjD;AAAA,IACA,UAAU,kBAAkB;AAAA,IAC5B,oBAAoB,kBAAkB;AAAA,EAC1C;AACA,SAAO,UAAU,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,MAAM,CAAC;AACjE;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/replyQuality.ts"],"sourcesContent":["/**\n * TITAN — Reply Quality Validation\n *\n * Pure, fully testable validation for outbound replies (FB comments, Messenger, etc.)\n * Catches problems that the sanitizer misses because they're about the REPLY SHAPE\n * rather than content patterns:\n *\n * - Mid-sentence truncation (e.g. \"I must've imagined the whole\" — cut off)\n * - Overly apologetic / self-deprecating tone (bad PR for the brand)\n * - Empty or too-short replies\n * - Replies that are just the commenter's name back at them\n *\n * Created after the 2026-04-14 truncation incident where minimax-m2.7 returned\n * \"You're right Tony, I must've imagined the whole\" — 47 chars, no punctuation,\n * cut off mid-word.\n */\n\nexport interface ReplyValidation {\n valid: boolean;\n reason?: string;\n /** Suggested action: 'reject' | 'retry' | 'fallback' */\n action?: 'reject' | 'retry' | 'fallback';\n}\n\n/**\n * Validate the quality and completeness of a reply.\n * Returns { valid: true } if the reply can be posted as-is.\n * Returns { valid: false, reason, action } with a human-readable reason and\n * a suggested corrective action.\n */\nexport function validateReply(reply: string, commenterFirstName?: string): ReplyValidation {\n const trimmed = reply.trim();\n\n // ── Empty / too short ──────────────────────────────────────\n if (!trimmed) {\n return { valid: false, reason: 'empty', action: 'fallback' };\n }\n if (trimmed.length < 8) {\n return { valid: false, reason: 'too_short', action: 'fallback' };\n }\n\n // ── Truncation detection ──────────────────────────────────\n // A complete reply should end in terminal punctuation, an emoji,\n // a quote mark, or a closing bracket. Anything else means the model\n // was mid-sentence when it stopped.\n const lastChar = trimmed.slice(-1);\n const lastTwo = trimmed.slice(-2);\n const endsInPunctuation = /[.!?…]/.test(lastChar);\n const endsInQuote = /[\"')\\]}]/.test(lastChar);\n // Emoji detection — any non-ASCII char at the end is likely an emoji\n const endsInEmoji = /[^\\p{ASCII}]/u.test(lastChar) || /[^\\p{ASCII}][\\uFE0F\\u200D]?/u.test(lastTwo);\n\n if (!endsInPunctuation && !endsInQuote && !endsInEmoji) {\n return {\n valid: false,\n reason: `truncated_no_terminal_punct (ends with \"${trimmed.slice(-15)}\")`,\n action: 'retry',\n };\n }\n\n // ── Mid-word truncation detection ─────────────────────────\n // Words like \"the whole\" at the end (no noun following) are suspicious\n // when combined with no proper closing.\n // A reply ending in these \"dangling\" phrases usually means the model cut off.\n const danglingEndings = [\n /\\bthe\\s+whole\\s*$/i,\n /\\bthe\\s+entire\\s*$/i,\n /\\ba\\s+(?:little|bit|lot|few)\\s*$/i,\n /\\bmust['']?(?:ve|\\s+have)\\s*$/i,\n /\\bgoing\\s+to\\s*$/i,\n /\\bsupposed\\s+to\\s*$/i,\n /\\btrying\\s+to\\s*$/i,\n /\\bI\\s*$/i,\n /\\bmy\\s*$/i,\n /\\band\\s*$/i,\n /\\bor\\s*$/i,\n /\\bbut\\s*$/i,\n /\\bso\\s*$/i,\n /\\bfor\\s*$/i,\n /\\bof\\s*$/i,\n /\\bwith\\s*$/i,\n ];\n for (const pattern of danglingEndings) {\n if (pattern.test(trimmed)) {\n return {\n valid: false,\n reason: `truncated_dangling_phrase`,\n action: 'retry',\n };\n }\n }\n\n // ── Overly apologetic / self-deprecating tone ──────────────\n // TITAN's brand voice is confident. Admitting to being broken or wrong\n // publicly (except playfully) damages credibility. Detect the \"You're\n // right, I'm wrong\" shapes.\n const selfDeprecatingPatterns = [\n /^you['']?re right,?\\s+(?:I|tony|sorry|my)/i,\n /^(?:sorry|apologies|my bad|you['']?re correct),?\\s+I/i,\n /^I (?:apologize|was wrong|messed up|screwed up|got it wrong)/i,\n /\\bI (?:must['']?(?:ve| have)|apparently|clearly) (?:hallucinat|imagined|made up|fabricated)/i,\n /\\byou['']?re right[,.\\s]+I was wrong/i,\n ];\n for (const pattern of selfDeprecatingPatterns) {\n if (pattern.test(trimmed)) {\n return {\n valid: false,\n reason: 'too_self_deprecating (breaks brand voice)',\n action: 'fallback',\n };\n }\n }\n\n // ── Just echoing the commenter's name ──────────────────────\n if (commenterFirstName) {\n // Reply is ONLY the name + minimal decoration\n const justNamePattern = new RegExp(\n `^(?:hi|hey|hello|thanks|thank you),?\\\\s*${commenterFirstName}[.!?\\\\s]*$`,\n 'i',\n );\n if (justNamePattern.test(trimmed)) {\n return {\n valid: false,\n reason: 'just_name_echo',\n action: 'fallback',\n };\n }\n }\n\n // ── Too many exclamation marks (cheesy) ────────────────────\n const exclamationCount = (trimmed.match(/!/g) || []).length;\n if (exclamationCount >= 4) {\n return {\n valid: false,\n reason: 'too_many_exclamations',\n action: 'retry',\n };\n }\n\n // ── Passes all checks ──────────────────────────────────────\n return { valid: true };\n}\n\n/**\n * Generate a safe fallback reply when validation fails.\n * Returns a neutral, on-brand reply that works for any comment.\n */\nexport function getSafeFallbackReply(commenterFirstName: string): string {\n const fallbacks = [\n `Thanks for the comment, ${commenterFirstName}! 🤖`,\n `Appreciate you stopping by, ${commenterFirstName}! 🙌`,\n `Always good to hear from the community. 🚀`,\n `Noted, ${commenterFirstName}. Back to work for me! ⚡`,\n `Cheers for that, ${commenterFirstName}! 💪`,\n ];\n return fallbacks[Math.floor(Math.random() * fallbacks.length)];\n}\n"],"mappings":";AA8BO,SAAS,cAAc,OAAe,oBAA8C;AACvF,QAAM,UAAU,MAAM,KAAK;AAG3B,MAAI,CAAC,SAAS;AACV,WAAO,EAAE,OAAO,OAAO,QAAQ,SAAS,QAAQ,WAAW;AAAA,EAC/D;AACA,MAAI,QAAQ,SAAS,GAAG;AACpB,WAAO,EAAE,OAAO,OAAO,QAAQ,aAAa,QAAQ,WAAW;AAAA,EACnE;AAMA,QAAM,WAAW,QAAQ,MAAM,EAAE;AACjC,QAAM,UAAU,QAAQ,MAAM,EAAE;AAChC,QAAM,oBAAoB,SAAS,KAAK,QAAQ;AAChD,QAAM,cAAc,WAAW,KAAK,QAAQ;AAE5C,QAAM,cAAc,gBAAgB,KAAK,QAAQ,KAAK,+BAA+B,KAAK,OAAO;AAEjG,MAAI,CAAC,qBAAqB,CAAC,eAAe,CAAC,aAAa;AACpD,WAAO;AAAA,MACH,OAAO;AAAA,MACP,QAAQ,2CAA2C,QAAQ,MAAM,GAAG,CAAC;AAAA,MACrE,QAAQ;AAAA,IACZ;AAAA,EACJ;AAMA,QAAM,kBAAkB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,aAAW,WAAW,iBAAiB;AACnC,QAAI,QAAQ,KAAK,OAAO,GAAG;AACvB,aAAO;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACZ;AAAA,IACJ;AAAA,EACJ;AAMA,QAAM,0BAA0B;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,aAAW,WAAW,yBAAyB;AAC3C,QAAI,QAAQ,KAAK,OAAO,GAAG;AACvB,aAAO;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACZ;AAAA,IACJ;AAAA,EACJ;AAGA,MAAI,oBAAoB;AAEpB,UAAM,kBAAkB,IAAI;AAAA,MACxB,2CAA2C,kBAAkB;AAAA,MAC7D;AAAA,IACJ;AACA,QAAI,gBAAgB,KAAK,OAAO,GAAG;AAC/B,aAAO;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACZ;AAAA,IACJ;AAAA,EACJ;AAGA,QAAM,oBAAoB,QAAQ,MAAM,IAAI,KAAK,CAAC,GAAG;AACrD,MAAI,oBAAoB,GAAG;AACvB,WAAO;AAAA,MACH,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA,IACZ;AAAA,EACJ;AAGA,SAAO,EAAE,OAAO,KAAK;AACzB;AAMO,SAAS,qBAAqB,oBAAoC;AACrE,QAAM,YAAY;AAAA,IACd,2BAA2B,kBAAkB;AAAA,IAC7C,+BAA+B,kBAAkB;AAAA,IACjD;AAAA,IACA,UAAU,kBAAkB;AAAA,IAC5B,oBAAoB,kBAAkB;AAAA,EAC1C;AACA,SAAO,UAAU,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,MAAM,CAAC;AACjE;","names":[]}
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ function isDangerous(message) {
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);
5
+ }
6
+ function isDangerousCommand(command) {
7
+ const DANGEROUS_PATTERNS = [
8
+ /\brm\s+-[rfRF]/,
9
+ />\s*\/dev\/null\s*2>&1.*rm/,
10
+ /curl.*\|.*sh/,
11
+ /wget.*\|.*sh/,
12
+ /:(){ :|:& };:/,
13
+ // fork bomb
14
+ /dd\s+if=.+of=\/dev\//,
15
+ /mkfs\./,
16
+ />\s*\/etc\/passwd/,
17
+ /shutdown\s+-h\s+now/
18
+ ];
19
+ return DANGEROUS_PATTERNS.some((re) => re.test(command));
20
+ }
21
+ export {
22
+ isDangerous,
23
+ isDangerousCommand
24
+ };
25
+ //# sourceMappingURL=safety.js.map
@@ -0,0 +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"],"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;","names":[]}
@@ -2,7 +2,7 @@
2
2
  function estimateTokens(text) {
3
3
  if (!text || text.length === 0) return 0;
4
4
  const len = text.length;
5
- const symbolDensity = (text.match(/[{}();:=<>\[\]\/\\|&!?@#$%~^*+\-`\"']/g) || []).length / len;
5
+ const symbolDensity = (text.match(/[{}();:=<>[\]/\\|&!?@#$%~^*+\-`"']/g) || []).length / len;
6
6
  const divisor = symbolDensity > 0.08 ? 2.5 : symbolDensity > 0.03 ? 3 : 3.8;
7
7
  return Math.ceil(len / divisor);
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/tokens.ts"],"sourcesContent":["/**\n * Fast token estimation without heavy dependencies.\n * \n * GPT/claude-style tokenizers:\n * - English prose: ~3.5-4 chars per token\n * - Code / symbols: ~1.5-2.5 chars per token \n * - Mixed content: ~2.5-3 chars per token\n * \n * The old `text.length / 4` undercounts code by 30-50%.\n * We use a blended estimate that weights symbol density higher.\n */\nexport function estimateTokens(text: string): number {\n if (!text || text.length === 0) return 0;\n\n const len = text.length;\n // Symbol-heavy text (code, markdown, JSON) tokenizes at ~2 chars/token\n const symbolDensity =\n (text.match(/[{}();:=<>\\[\\]\\/\\\\|&!?@#$%~^*+\\-`\\\"']/g) || []).length / len;\n\n // Base divisor: 4 for prose, down to 2.5 for code-heavy text\n const divisor = symbolDensity > 0.08 ? 2.5 : symbolDensity > 0.03 ? 3.0 : 3.8;\n\n return Math.ceil(len / divisor);\n}\n\n/** Estimate tokens for an array of messages (summing content fields). */\nexport function estimateMessageTokens(messages: { content?: unknown }[]): number {\n return messages.reduce((sum, m) => {\n const text = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);\n return sum + estimateTokens(text);\n }, 0);\n}\n"],"mappings":";AAWO,SAAS,eAAe,MAAsB;AACjD,MAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AAEvC,QAAM,MAAM,KAAK;AAEjB,QAAM,iBACD,KAAK,MAAM,wCAAwC,KAAK,CAAC,GAAG,SAAS;AAG1E,QAAM,UAAU,gBAAgB,OAAO,MAAM,gBAAgB,OAAO,IAAM;AAE1E,SAAO,KAAK,KAAK,MAAM,OAAO;AAClC;AAGO,SAAS,sBAAsB,UAA2C;AAC7E,SAAO,SAAS,OAAO,CAAC,KAAK,MAAM;AAC/B,UAAM,OAAO,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,KAAK,UAAU,EAAE,OAAO;AACjF,WAAO,MAAM,eAAe,IAAI;AAAA,EACpC,GAAG,CAAC;AACR;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/tokens.ts"],"sourcesContent":["/**\n * Fast token estimation without heavy dependencies.\n * \n * GPT/claude-style tokenizers:\n * - English prose: ~3.5-4 chars per token\n * - Code / symbols: ~1.5-2.5 chars per token \n * - Mixed content: ~2.5-3 chars per token\n * \n * The old `text.length / 4` undercounts code by 30-50%.\n * We use a blended estimate that weights symbol density higher.\n */\nexport function estimateTokens(text: string): number {\n if (!text || text.length === 0) return 0;\n\n const len = text.length;\n // Symbol-heavy text (code, markdown, JSON) tokenizes at ~2 chars/token\n const symbolDensity =\n (text.match(/[{}();:=<>[\\]/\\\\|&!?@#$%~^*+\\-`\"']/g) || []).length / len;\n\n // Base divisor: 4 for prose, down to 2.5 for code-heavy text\n const divisor = symbolDensity > 0.08 ? 2.5 : symbolDensity > 0.03 ? 3.0 : 3.8;\n\n return Math.ceil(len / divisor);\n}\n\n/** Estimate tokens for an array of messages (summing content fields). */\nexport function estimateMessageTokens(messages: { content?: unknown }[]): number {\n return messages.reduce((sum, m) => {\n const text = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);\n return sum + estimateTokens(text);\n }, 0);\n}\n"],"mappings":";AAWO,SAAS,eAAe,MAAsB;AACjD,MAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AAEvC,QAAM,MAAM,KAAK;AAEjB,QAAM,iBACD,KAAK,MAAM,qCAAqC,KAAK,CAAC,GAAG,SAAS;AAGvE,QAAM,UAAU,gBAAgB,OAAO,MAAM,gBAAgB,OAAO,IAAM;AAE1E,SAAO,KAAK,KAAK,MAAM,OAAO;AAClC;AAGO,SAAS,sBAAsB,UAA2C;AAC7E,SAAO,SAAS,OAAO,CAAC,KAAK,MAAM;AAC/B,UAAM,OAAO,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,KAAK,UAAU,EAAE,OAAO;AACjF,WAAO,MAAM,eAAe,IAAI;AAAA,EACpC,GAAG,CAAC;AACR;","names":[]}