titan-agent 6.4.4 → 6.5.1

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 (196) hide show
  1. package/dist/agent/agentLessons.js.map +1 -1
  2. package/dist/agent/agentLoop.js +5 -1
  3. package/dist/agent/agentLoop.js.map +1 -1
  4. package/dist/agent/orchestrator.js +5 -14
  5. package/dist/agent/orchestrator.js.map +1 -1
  6. package/dist/agent/somaInitiative.js.map +1 -1
  7. package/dist/agent/toolRunner.js +35 -0
  8. package/dist/agent/toolRunner.js.map +1 -1
  9. package/dist/channels/discord.js +10 -6
  10. package/dist/channels/discord.js.map +1 -1
  11. package/dist/channels/slack.js +7 -2
  12. package/dist/channels/slack.js.map +1 -1
  13. package/dist/channels/telegram.js +1 -1
  14. package/dist/channels/telegram.js.map +1 -1
  15. package/dist/config/schema.js +6 -0
  16. package/dist/config/schema.js.map +1 -1
  17. package/dist/gateway/openai-compat.js +69 -10
  18. package/dist/gateway/openai-compat.js.map +1 -1
  19. package/dist/gateway/server.js +79 -0
  20. package/dist/gateway/server.js.map +1 -1
  21. package/dist/migrations/runner.js.map +1 -1
  22. package/dist/migrations/safeRun.js.map +1 -1
  23. package/dist/providers/anthropic.js +7 -4
  24. package/dist/providers/anthropic.js.map +1 -1
  25. package/dist/providers/base.js +8 -1
  26. package/dist/providers/base.js.map +1 -1
  27. package/dist/providers/google.js +7 -4
  28. package/dist/providers/google.js.map +1 -1
  29. package/dist/providers/ollama.js +8 -7
  30. package/dist/providers/ollama.js.map +1 -1
  31. package/dist/providers/openai.js +7 -4
  32. package/dist/providers/openai.js.map +1 -1
  33. package/dist/providers/openai_compat.js +7 -4
  34. package/dist/providers/openai_compat.js.map +1 -1
  35. package/dist/providers/router.js +7 -3
  36. package/dist/providers/router.js.map +1 -1
  37. package/dist/skills/builtin/backup.js.map +1 -1
  38. package/dist/skills/builtin/browser.js +11 -21
  39. package/dist/skills/builtin/browser.js.map +1 -1
  40. package/dist/skills/builtin/calendar.js +6 -2
  41. package/dist/skills/builtin/calendar.js.map +1 -1
  42. package/dist/skills/builtin/canvas_spaces.js.map +1 -1
  43. package/dist/skills/builtin/data_analysis.js +1 -1
  44. package/dist/skills/builtin/data_analysis.js.map +1 -1
  45. package/dist/skills/builtin/jira_linear.js +6 -4
  46. package/dist/skills/builtin/jira_linear.js.map +1 -1
  47. package/dist/skills/builtin/web_browser.js +6 -0
  48. package/dist/skills/builtin/web_browser.js.map +1 -1
  49. package/dist/skills/builtin/web_fetch.js +1 -0
  50. package/dist/skills/builtin/web_fetch.js.map +1 -1
  51. package/dist/storage/starterSpaces.js.map +1 -1
  52. package/dist/utils/constants.js +1 -1
  53. package/dist/utils/constants.js.map +1 -1
  54. package/package.json +2 -2
  55. package/ui/dist/assets/{AuditPanel-BOIeotPE.js → AuditPanel-BaftX5MP.js} +1 -1
  56. package/ui/dist/assets/{AutonomyPanel-Dv8ClpL9.js → AutonomyPanel-CmaXJV8d.js} +1 -1
  57. package/ui/dist/assets/{AutopilotPanel-Dv3-_6fZ.js → AutopilotPanel-CbohPtXW.js} +1 -1
  58. package/ui/dist/assets/{AutoresearchPanel-DdLmfGpC.js → AutoresearchPanel-DCwiQ2v2.js} +1 -1
  59. package/ui/dist/assets/{BackupPanel-DYr6APwV.js → BackupPanel-DAy9G7Wt.js} +1 -1
  60. package/ui/dist/assets/{BrowserPanel-DMZf6mNn.js → BrowserPanel-D6UaSxap.js} +1 -1
  61. package/ui/dist/assets/{CPActivity-B2kmaYR9.js → CPActivity-BYDsUoMn.js} +1 -1
  62. package/ui/dist/assets/{CPAgentDetail--ploJZFv.js → CPAgentDetail-Bb9-GAJY.js} +1 -1
  63. package/ui/dist/assets/{CPAgents-ctC99jCR.js → CPAgents-BTZuGnki.js} +1 -1
  64. package/ui/dist/assets/{CPApprovals-BZHaJ6d_.js → CPApprovals-BME2ARkO.js} +1 -1
  65. package/ui/dist/assets/{CPCosts--xJmtfEi.js → CPCosts-mOXwWq0i.js} +1 -1
  66. package/ui/dist/assets/{CPDashboard-rThYnRLi.js → CPDashboard-CTlKGXXQ.js} +1 -1
  67. package/ui/dist/assets/{CPFiles-IRzbOaxo.js → CPFiles-BO73EZwJ.js} +1 -1
  68. package/ui/dist/assets/{CPGoals-BQ4Kasli.js → CPGoals-BNWhNP14.js} +1 -1
  69. package/ui/dist/assets/{CPInbox-DYvoAJdg.js → CPInbox-ybe4r8LW.js} +1 -1
  70. package/ui/dist/assets/{CPIssueDetail-D4JUVm7B.js → CPIssueDetail-DaMMGbjw.js} +1 -1
  71. package/ui/dist/assets/{CPIssues-C_K0zuiB.js → CPIssues-BgpQOISw.js} +1 -1
  72. package/ui/dist/assets/CPLayout-bgErauTx.js +2 -0
  73. package/ui/dist/assets/{CPOrg-CAKwV8Kr.js → CPOrg-C8l5SIWJ.js} +1 -1
  74. package/ui/dist/assets/{CPRuns-COUYpSBB.js → CPRuns-DB8S7loF.js} +1 -1
  75. package/ui/dist/assets/{CPSocial-BCTgcO3V.js → CPSocial-Qw5rKy_4.js} +1 -1
  76. package/ui/dist/assets/{CPVoice-D-wxXUGf.js → CPVoice-DfwHPewA.js} +1 -1
  77. package/ui/dist/assets/{ChannelsPanel-Cx3dxaP9.js → ChannelsPanel-DS21E-Iz.js} +1 -1
  78. package/ui/dist/assets/{CheckpointsPanel-D2ATiPXs.js → CheckpointsPanel-9-l_8GdG.js} +1 -1
  79. package/ui/dist/assets/{CommandPostHub-DTs1EGm8.js → CommandPostHub-z55Yrhj1.js} +3 -3
  80. package/ui/dist/assets/{CronPanel-D0j9zDVW.js → CronPanel-kEZK3gpx.js} +1 -1
  81. package/ui/dist/assets/{DataTable-CgeOprjv.js → DataTable-PnhPeDBG.js} +1 -1
  82. package/ui/dist/assets/{DreamPanel-CJAI6Aka.js → DreamPanel-DIrtlk2K.js} +1 -1
  83. package/ui/dist/assets/{EmptyState-NMpt-ODJ.js → EmptyState-CfvU7SdB.js} +1 -1
  84. package/ui/dist/assets/{EvalHarnessPanel-ZPXlKHaK.js → EvalHarnessPanel-uYb_ZEQG.js} +1 -1
  85. package/ui/dist/assets/{EvalPanel-DpmEQQXh.js → EvalPanel-CDG-njyj.js} +1 -1
  86. package/ui/dist/assets/{FileViewer-B7Mm4cJL.js → FileViewer-CVO3TRs3.js} +1 -1
  87. package/ui/dist/assets/{FilesPanel-CvrGQwh3.js → FilesPanel-CaLPog2e.js} +1 -1
  88. package/ui/dist/assets/{FleetPanel-BDf9E7dQ.js → FleetPanel-BtlaV6pH.js} +1 -1
  89. package/ui/dist/assets/{HomelabPanel-CKGR1kGN.js → HomelabPanel-BZvN4LPM.js} +1 -1
  90. package/ui/dist/assets/InfraView-C0zEuFrR.js +2 -0
  91. package/ui/dist/assets/{InlineEditableField-JxqorUFO.js → InlineEditableField-mBzpbcLB.js} +1 -1
  92. package/ui/dist/assets/{Input-rfmX8eLw.js → Input-CbrMbLgn.js} +1 -1
  93. package/ui/dist/assets/{IntegrationsPanel-Ds5KCGUi.js → IntegrationsPanel-B4TyR6jC.js} +1 -1
  94. package/ui/dist/assets/IntelligenceView-DZ4HxYZ3.js +2 -0
  95. package/ui/dist/assets/{LearningPanel-CZUd4UsW.js → LearningPanel-B4RIm_iQ.js} +1 -1
  96. package/ui/dist/assets/{LogsPanel-CmUBA-jG.js → LogsPanel-DbP3Mwnr.js} +1 -1
  97. package/ui/dist/assets/{McpPanel-CVyjQ6H6.js → McpPanel-BmNUFozc.js} +1 -1
  98. package/ui/dist/assets/{MemoryGraphPanel-BK-AbsbF.js → MemoryGraphPanel-JPurHV0L.js} +1 -1
  99. package/ui/dist/assets/{MemoryWikiPanel-DkfHLaje.js → MemoryWikiPanel-DdvCAZHa.js} +1 -1
  100. package/ui/dist/assets/{MeshPanel-BCDzhBRO.js → MeshPanel-Bf-4brXL.js} +1 -1
  101. package/ui/dist/assets/{MissionCanvas-GvSYEkjz.js → MissionCanvas-CJyhnauG.js} +1 -1
  102. package/ui/dist/assets/{MissionChat-DpyGbBhk.js → MissionChat-C2Nto3zM.js} +1 -1
  103. package/ui/dist/assets/{MissionLibrary-Dwn2vP2g.js → MissionLibrary-RXyBHsLL.js} +1 -1
  104. package/ui/dist/assets/{MissionStart-DIYEotqX.js → MissionStart-wUkcEP0T.js} +1 -1
  105. package/ui/dist/assets/{Modal-BFii_RIq.js → Modal-BmS7C6FN.js} +1 -1
  106. package/ui/dist/assets/{NvidiaPanel-BCmLv4bY.js → NvidiaPanel-4DlcQlyl.js} +1 -1
  107. package/ui/dist/assets/{OrganismPanel-CDMC8-yf.js → OrganismPanel-C2LYDPrn.js} +1 -1
  108. package/ui/dist/assets/{OverviewPanel-C9ErRTB1.js → OverviewPanel-DK8uuq4D.js} +1 -1
  109. package/ui/dist/assets/{PageHeader-CawSrb9E.js → PageHeader-bnc85wQN.js} +1 -1
  110. package/ui/dist/assets/{PersonaProfilesPanel-B_hx3sLQ.js → PersonaProfilesPanel-DGtokXUu.js} +1 -1
  111. package/ui/dist/assets/{PersonasPanel-BhfAKQWd.js → PersonasPanel-dR8VppcD.js} +1 -1
  112. package/ui/dist/assets/{PhoneDeskPanel-Cew4jv5F.js → PhoneDeskPanel-CdVOY7H9.js} +1 -1
  113. package/ui/dist/assets/{RecipesPanel-KSNQil0R.js → RecipesPanel-D3Zs2wxr.js} +1 -1
  114. package/ui/dist/assets/{SecurityPanel-ojbdvuZR.js → SecurityPanel-BMERf796.js} +1 -1
  115. package/ui/dist/assets/{SelfImprovePanel-D743Dh0z.js → SelfImprovePanel-BxqWHXCU.js} +1 -1
  116. package/ui/dist/assets/{SelfProposalsPanel-DHPrgGsJ.js → SelfProposalsPanel-CuC94vdx.js} +1 -1
  117. package/ui/dist/assets/{SessionsPanel-Bd8dAJQa.js → SessionsPanel-CQ8qvzp7.js} +1 -1
  118. package/ui/dist/assets/{SessionsTab-BjyOfaQ9.js → SessionsTab-B3eHV3jf.js} +1 -1
  119. package/ui/dist/assets/{SettingsPanel-CWvFc8MJ.js → SettingsPanel-Cumls7N2.js} +1 -1
  120. package/ui/dist/assets/SettingsView-BtA67pol.js +2 -0
  121. package/ui/dist/assets/{SkeletonLoader-sjDa7Y96.js → SkeletonLoader-Cax7YTqW.js} +1 -1
  122. package/ui/dist/assets/{SkillsPanel-B0UwrZGA.js → SkillsPanel-CA415CT8.js} +1 -1
  123. package/ui/dist/assets/{SomaView-CXfqpdNP.js → SomaView-eG3wI4S1.js} +1 -1
  124. package/ui/dist/assets/{StatCard-B4qf3WOB.js → StatCard-Djr487DV.js} +1 -1
  125. package/ui/dist/assets/{StatusBadge-BtDtyL5L.js → StatusBadge-JpCoA33_.js} +1 -1
  126. package/ui/dist/assets/{Tabs-CeY7i2tk.js → Tabs-DJHu-roP.js} +1 -1
  127. package/ui/dist/assets/{TeamsPanel-BxFpHD58.js → TeamsPanel-C_Vu6vnY.js} +1 -1
  128. package/ui/dist/assets/{TelemetryPanel-D2L98GwC.js → TelemetryPanel-D4PXsd-X.js} +1 -1
  129. package/ui/dist/assets/{TitanCanvas-D7TL368k.js → TitanCanvas-TuYZSZO7.js} +4 -4
  130. package/ui/dist/assets/{ToolsView-BiW0mds2.js → ToolsView-BvTN6vFn.js} +2 -2
  131. package/ui/dist/assets/{Tooltip-C3exOcZa.js → Tooltip-B1LhTXJ8.js} +1 -1
  132. package/ui/dist/assets/{TraceViewer-z42wYVdE.js → TraceViewer-Bh8Jo3Dr.js} +1 -1
  133. package/ui/dist/assets/{TrainingPanel-BPVftaSt.js → TrainingPanel-Dmbd1rJ-.js} +1 -1
  134. package/ui/dist/assets/{VoiceOverlay-DNzDqitY.js → VoiceOverlay-B_ss7Zg_.js} +1 -1
  135. package/ui/dist/assets/{VramPanel-CF4l5fe_.js → VramPanel-CPICbZ2s.js} +1 -1
  136. package/ui/dist/assets/{WatchView-CewHUoxK.js → WatchView-BCgLVfiV.js} +1 -1
  137. package/ui/dist/assets/{WorkTab-ByC4niLi.js → WorkTab-iptofXO9.js} +1 -1
  138. package/ui/dist/assets/{WorkflowsPanel-Bsyhjv87.js → WorkflowsPanel-3RYkFHyf.js} +1 -1
  139. package/ui/dist/assets/{archive-Cz-kvWW5.js → archive-gZFnGd5A.js} +1 -1
  140. package/ui/dist/assets/{arrow-left-CGVxTClf.js → arrow-left-qA0Qer2h.js} +1 -1
  141. package/ui/dist/assets/{book-open-CrOs3smB.js → book-open-DIxdPqmD.js} +1 -1
  142. package/ui/dist/assets/{bot-C0wiWyCT.js → bot-CvIF1uHi.js} +1 -1
  143. package/ui/dist/assets/{chart-column-Dcc3a8m5.js → chart-column-BQiM-Td4.js} +1 -1
  144. package/ui/dist/assets/{check-BhmlTEFk.js → check-DgyDEFtq.js} +1 -1
  145. package/ui/dist/assets/{chevron-right-DJCsUSwz.js → chevron-right-CQ1NTOtd.js} +1 -1
  146. package/ui/dist/assets/{chevron-up-cs3cBR4O.js → chevron-up-UyfdjRp6.js} +1 -1
  147. package/ui/dist/assets/{circle-check-big-De2k5qVr.js → circle-check-big-CAFcaPPH.js} +1 -1
  148. package/ui/dist/assets/{circle-stop-u0L9iQ69.js → circle-stop-C-8xGv0n.js} +1 -1
  149. package/ui/dist/assets/{clipboard-list-DF1F3Vjf.js → clipboard-list-9DdqBj1F.js} +1 -1
  150. package/ui/dist/assets/{clock-Bc6IrFv8.js → clock-CF9BCIpL.js} +1 -1
  151. package/ui/dist/assets/{dollar-sign-BStL62ws.js → dollar-sign-LDRZBae6.js} +1 -1
  152. package/ui/dist/assets/{download-VXqww0Iy.js → download-BBNXvBO0.js} +1 -1
  153. package/ui/dist/assets/{external-link-BjN0VrNb.js → external-link-D_ji4Bec.js} +1 -1
  154. package/ui/dist/assets/{file-text-SIULFbzy.js → file-text-DrdTDEYp.js} +1 -1
  155. package/ui/dist/assets/{flask-conical-CsShOfpS.js → flask-conical-BGn5PgFX.js} +1 -1
  156. package/ui/dist/assets/{folder-DwOkgBGj.js → folder-BeZZUGnK.js} +1 -1
  157. package/ui/dist/assets/{funnel-CDnA2Enn.js → funnel-CAJgGO5M.js} +1 -1
  158. package/ui/dist/assets/{git-branch-BAFSlxgi.js → git-branch-BcXabeOs.js} +1 -1
  159. package/ui/dist/assets/{index-BlFxPvdg.js → index-CUgepw5Q.js} +64 -64
  160. package/ui/dist/assets/{layers-DKKOtsYx.js → layers-DxYDmPA_.js} +1 -1
  161. package/ui/dist/assets/{legacy-YQ03_WIq.js → legacy-dHWI7k8P.js} +1 -1
  162. package/ui/dist/assets/{lightbulb-D4Syh7fA.js → lightbulb-B0Du4SM4.js} +1 -1
  163. package/ui/dist/assets/{link-C_6ueq0o.js → link-CUpaumqr.js} +1 -1
  164. package/ui/dist/assets/{list-todo-3Me4V-34.js → list-todo-CDDhmPQB.js} +1 -1
  165. package/ui/dist/assets/{message-square-Dv6Q3VRO.js → message-square-D5VWlv-Q.js} +1 -1
  166. package/ui/dist/assets/{mic-BInf3BDE.js → mic-Ddcc6Ann.js} +1 -1
  167. package/ui/dist/assets/{missions-BUTRB5Lq.js → missions-2zox16Tc.js} +1 -1
  168. package/ui/dist/assets/{network-BsSfc5IE.js → network-DaJ7ugwX.js} +1 -1
  169. package/ui/dist/assets/{pause-CXCmPcHk.js → pause-BukaK6rs.js} +1 -1
  170. package/ui/dist/assets/{pencil-DAvceqn3.js → pencil-D_YwQBeg.js} +1 -1
  171. package/ui/dist/assets/{play-MuD1S1C5.js → play-BjdwtYMk.js} +1 -1
  172. package/ui/dist/assets/{plug-Chv5y4No.js → plug-yISDdeW2.js} +1 -1
  173. package/ui/dist/assets/{plus-SBD6A3Qb.js → plus-BSjTdepW.js} +1 -1
  174. package/ui/dist/assets/{proxy-C5UuKDtU.js → proxy-CWET5FFX.js} +1 -1
  175. package/ui/dist/assets/{radio-BySzBvYH.js → radio-B7kX_yWE.js} +1 -1
  176. package/ui/dist/assets/{refresh-cw-BdcqinTm.js → refresh-cw-CwDpzXUn.js} +1 -1
  177. package/ui/dist/assets/{rotate-ccw-CRA_s5tz.js → rotate-ccw-7SQDTI0_.js} +1 -1
  178. package/ui/dist/assets/{save-BWhXC8Tv.js → save-CjTPAQM0.js} +1 -1
  179. package/ui/dist/assets/{search-DF7UnWEk.js → search-BP7VlNxX.js} +1 -1
  180. package/ui/dist/assets/{send-CmvPtbvy.js → send-C2XDuRPf.js} +1 -1
  181. package/ui/dist/assets/{server-1PubNLgw.js → server-BE8W-AT4.js} +1 -1
  182. package/ui/dist/assets/{shield-check-BjuAv28a.js → shield-check-s7la2J2K.js} +1 -1
  183. package/ui/dist/assets/{terminal-BvDfNOXl.js → terminal-B8IK4WsO.js} +1 -1
  184. package/ui/dist/assets/{test-tube-CrzRsKdm.js → test-tube-Xfn9DJeg.js} +1 -1
  185. package/ui/dist/assets/{toggle-right-BRJ_dULZ.js → toggle-right-CHpzOIlj.js} +1 -1
  186. package/ui/dist/assets/{trash-2-Dtxmn6GE.js → trash-2-B1C85ePK.js} +1 -1
  187. package/ui/dist/assets/{trending-up-C7pfwW1S.js → trending-up-DDGHQZY1.js} +1 -1
  188. package/ui/dist/assets/{trophy-Dknrgn8P.js → trophy-D0LhJSPA.js} +1 -1
  189. package/ui/dist/assets/{volume-2-ahkfEwxT.js → volume-2-DMaE91b5.js} +1 -1
  190. package/ui/dist/assets/{wifi-off-wACQ7wec.js → wifi-off-DR9h8HGA.js} +1 -1
  191. package/ui/dist/index.html +1 -1
  192. package/ui/dist/sw.js +1 -1
  193. package/ui/dist/assets/CPLayout-B3Lr8puc.js +0 -2
  194. package/ui/dist/assets/InfraView-CreCQSNl.js +0 -2
  195. package/ui/dist/assets/IntelligenceView-YcrYQTVO.js +0 -2
  196. package/ui/dist/assets/SettingsView-Chh-hdRF.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/agentLessons.ts"],"sourcesContent":["/**\n * TITAN — Per-agent lessons.md (Reflexion-style failure memory).\n *\n * Why this exists\n * ---------------\n * The audit of TITAN's Command Post (2026-05-11) found that retrospectives\n * were computed in-memory per goal but never persisted as a per-agent\n * lookup. That meant the next time the same agent took on similar work,\n * it walked into the same failure modes blind.\n *\n * This module is the persistence layer for the Reflexion pattern as\n * documented in awesome-agent-harness pattern #6:\n *\n * \"When a step fails, a separate critic LLM writes a 1-3 sentence\n * verbal lesson ('I forgot to check the schema before inserting')\n * into lessons.md. Future planner turns read this file first.\"\n *\n * One file per agent at `~/.titan/agents/<agentId>/lessons.md`. Append-only\n * + dedup-on-content. The top-N most recent / highest-relevance entries\n * are injected into that agent's system prompt at the start of every run\n * via {@link renderAgentLessonsForPrompt}.\n *\n * Reference:\n * - Shinn et al. \"Reflexion: Language agents with verbal reinforcement\n * learning\" (NeurIPS 2023) — https://arxiv.org/abs/2303.11366\n * - Picrew/awesome-agent-harness pattern #6\n * - ~/.claude/projects/-Users-michaelelliott/memory/titan-v6-living-canvas.md\n */\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport logger from '../utils/logger.js';\nimport { recordJournalEvent } from './durableJournal.js';\n\nconst COMPONENT = 'AgentLessons';\n\n/** Lazy path resolution so tests can swap HOME / TITAN_HOME. */\nfunction titanHome(): string {\n const env = process.env.TITAN_HOME;\n if (env) return env.startsWith('~/') ? join(homedir(), env.slice(2)) : env;\n return join(homedir(), '.titan');\n}\nfunction lessonsPath(agentId: string): string {\n return join(titanHome(), 'agents', agentId, 'lessons.md');\n}\n\n/** A single lesson the agent has learned from a past failure or success. */\nexport interface AgentLesson {\n /** Short, verbal — the lesson the agent took away (1–3 sentences). */\n text: string;\n /** \"fail\" lessons come from things that went wrong; \"win\" lessons from\n * things that worked surprisingly well. Both are useful. Default fail. */\n kind?: 'fail' | 'win';\n /** Free-form context tag — what kind of task this was about (e.g.\n * \"merge-conflict\", \"json-extract\", \"vram-acquire\"). Used for fuzzy\n * lookup later. Optional. */\n topic?: string;\n /** Optional sessionId for traceback. */\n sessionId?: string;\n /** Optional goalId — when set, the lesson is also written into the\n * goal's durable journal so a resuming process can see it. */\n goalId?: string;\n}\n\n/** Cap on lesson count per agent. Older entries roll off the head. */\nconst LESSONS_PER_AGENT_CAP = 200;\n/** Cap on lessons rendered into the system prompt at run time. */\nconst PROMPT_RENDER_CAP = 12;\n\n/**\n * Append a lesson to the agent's lessons.md. Idempotent on near-duplicate\n * text (lowercased, whitespace-collapsed match) so the same lesson written\n * twice in close succession doesn't pollute the file.\n */\nexport function recordLesson(agentId: string, lesson: AgentLesson): void {\n if (!agentId || !lesson?.text || !lesson.text.trim()) return;\n try {\n const path = lessonsPath(agentId);\n try { mkdirSync(join(path, '..'), { recursive: true }); } catch { /* exists */ }\n\n const norm = (s: string) => s.toLowerCase().replace(/\\s+/g, ' ').trim();\n const incomingNorm = norm(lesson.text);\n\n // Read existing entries to dedup against recent ones.\n const existingRaw = existsSync(path) ? readFileSync(path, 'utf-8') : '';\n const recentNorms = parseLessonTexts(existingRaw)\n .slice(-20) // only dedup against last 20 — older content fades\n .map(norm);\n if (recentNorms.includes(incomingNorm)) {\n return; // already learned this lesson recently\n }\n\n // Append one entry in the documented format.\n const ts = new Date().toISOString();\n const kind = lesson.kind ?? 'fail';\n const topic = lesson.topic ? ` topic=\"${escapeAttr(lesson.topic)}\"` : '';\n const session = lesson.sessionId ? ` session=\"${escapeAttr(lesson.sessionId)}\"` : '';\n const block = `\\n- [${ts}] **${kind}**${topic}${session} — ${lesson.text.trim()}\\n`;\n\n // Make sure the file has a header on first write.\n if (!existsSync(path)) {\n writeFileSync(path, lessonsHeader(agentId), 'utf-8');\n }\n appendFileSync(path, block, 'utf-8');\n\n // Trim to cap if exceeded.\n trimIfOverCap(path);\n\n logger.info(COMPONENT, `Recorded ${kind}-lesson for ${agentId}${topic} — \"${lesson.text.slice(0, 80)}\"`);\n\n // v6.0 CP upgrade #4 — durable journal: surface lessons in the\n // per-context replay so resuming agents can see what was learned.\n recordJournalEvent(\n { agentId, goalId: lesson.goalId, sessionId: lesson.sessionId },\n 'lesson_recorded',\n { text: lesson.text.trim(), kind, topic: lesson.topic },\n );\n } catch (err) {\n logger.warn(COMPONENT, `Failed to write lesson for ${agentId}: ${(err as Error).message}`);\n }\n}\n\nfunction lessonsHeader(agentId: string): string {\n return [\n `# Lessons — ${agentId}`,\n ``,\n `Append-only verbal-reinforcement journal. Each entry is one lesson the`,\n `agent learned from a past attempt (failure or surprising win). The top`,\n `~${PROMPT_RENDER_CAP} most recent entries are injected into this agent's system`,\n `prompt at the start of every run.`,\n ``,\n `Format: \\`- [iso-ts] **kind** [topic=\"…\"] [session=\"…\"] — verbal lesson\\``,\n ``,\n `---`,\n ``,\n ].join('\\n');\n}\n\nfunction escapeAttr(s: string): string {\n return s.replace(/[\\\\\"\\n\\r]/g, c => c === '\\n' ? ' ' : c === '\\r' ? ' ' : '\\\\' + c).slice(0, 80);\n}\n\n/** Parse the lesson lines from the raw markdown. Returns text content only. */\nfunction parseLessonTexts(raw: string): string[] {\n const lines = raw.split(/\\r?\\n/);\n const out: string[] = [];\n for (const l of lines) {\n // Match the documented format. Capture everything after the trailing \" — \".\n const m = l.match(/^- \\[[^\\]]+\\]\\s+\\*\\*(fail|win)\\*\\*(?:[^—]*)—\\s+(.+)$/);\n if (m && m[2]) out.push(m[2]);\n }\n return out;\n}\n\nfunction trimIfOverCap(path: string): void {\n try {\n const raw = readFileSync(path, 'utf-8');\n const lines = raw.split(/\\r?\\n/);\n // Find the entries (lines starting with \"- [\")\n const entryIdx: number[] = [];\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].startsWith('- [')) entryIdx.push(i);\n }\n if (entryIdx.length <= LESSONS_PER_AGENT_CAP) return;\n // Drop the oldest (LESSONS_PER_AGENT_CAP / 4) when we tip over.\n const dropCount = Math.max(1, Math.floor(LESSONS_PER_AGENT_CAP / 4));\n const cutAt = entryIdx[dropCount] ?? entryIdx[0];\n // Header ends at first \"---\" line.\n const headerEnd = lines.findIndex(l => l.trim() === '---');\n const header = headerEnd >= 0 ? lines.slice(0, headerEnd + 2).join('\\n') : '';\n const kept = lines.slice(cutAt).join('\\n');\n writeFileSync(path, header + '\\n' + kept, 'utf-8');\n logger.info(COMPONENT, `Trimmed ${dropCount} oldest lessons from ${path}`);\n } catch { /* best-effort */ }\n}\n\n/**\n * Render the most relevant lessons for `agentId` as a prompt-ready block.\n * If `topic` is supplied, lessons matching that topic float to the top.\n * Returns empty string when there are no lessons yet (don't add a\n * prompt section with no content).\n */\nexport function renderAgentLessonsForPrompt(agentId: string, topic?: string): string {\n if (!agentId) return '';\n const path = lessonsPath(agentId);\n if (!existsSync(path)) return '';\n try {\n const raw = readFileSync(path, 'utf-8');\n const lines = raw.split(/\\r?\\n/);\n // Parse structured entries: kind + topic + text. Preserve source order so\n // lessons written within the same millisecond still render newest-first.\n const entries: Array<{ kind: string; topic: string; text: string; ts: string; index: number }> = [];\n for (let index = 0; index < lines.length; index++) {\n const l = lines[index];\n const m = l.match(/^- \\[([^\\]]+)\\]\\s+\\*\\*(fail|win)\\*\\*(?:\\s+topic=\"([^\"]+)\")?(?:[^—]*)—\\s+(.+)$/);\n if (m) entries.push({ ts: m[1], kind: m[2], topic: m[3] || '', text: m[4], index });\n }\n if (entries.length === 0) return '';\n\n // Sort: most recent first, with topic match boost.\n const wantsTopic = (topic || '').toLowerCase();\n const scored = entries.map(e => ({\n e,\n score: (wantsTopic && e.topic.toLowerCase() === wantsTopic ? 1 : 0)\n + (wantsTopic && e.topic.toLowerCase().includes(wantsTopic) ? 0.5 : 0),\n }));\n scored.sort((a, b) => b.score - a.score || b.e.ts.localeCompare(a.e.ts) || b.e.index - a.e.index);\n\n const top = scored.slice(0, PROMPT_RENDER_CAP).map(s => s.e);\n if (top.length === 0) return '';\n\n const out: string[] = [`## Lessons you've learned (top ${top.length})`];\n for (const e of top) {\n const tag = e.topic ? ` *(${e.topic})*` : '';\n out.push(`- **${e.kind}**${tag} — ${e.text}`);\n }\n return out.join('\\n');\n } catch {\n return '';\n }\n}\n\n/** Convenience helper — read raw lessons.md content (used by tests + UI). */\nexport function readLessonsMarkdown(agentId: string): string {\n const path = lessonsPath(agentId);\n return existsSync(path) ? readFileSync(path, 'utf-8') : '';\n}\n\n/** Test helper — wipe an agent's lessons file. */\nexport function __resetLessonsForTests(agentId: string): void {\n const path = lessonsPath(agentId);\n if (existsSync(path)) {\n try { unlinkSync(path); } catch { /* fine */ }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/agent/agentLessons.ts"],"sourcesContent":["/**\n * TITAN — Per-agent lessons.md (Reflexion-style failure memory).\n *\n * Why this exists\n * ---------------\n * The audit of TITAN's Command Post (2026-05-11) found that retrospectives\n * were computed in-memory per goal but never persisted as a per-agent\n * lookup. That meant the next time the same agent took on similar work,\n * it walked into the same failure modes blind.\n *\n * This module is the persistence layer for the Reflexion pattern as\n * documented in awesome-agent-harness pattern #6:\n *\n * \"When a step fails, a separate critic LLM writes a 1-3 sentence\n * verbal lesson ('I forgot to check the schema before inserting')\n * into lessons.md. Future planner turns read this file first.\"\n *\n * One file per agent at `~/.titan/agents/<agentId>/lessons.md`. Append-only\n * + dedup-on-content. The top-N most recent / highest-relevance entries\n * are injected into that agent's system prompt at the start of every run\n * via {@link renderAgentLessonsForPrompt}.\n *\n * Reference:\n * - Shinn et al. \"Reflexion: Language agents with verbal reinforcement\n * learning\" (NeurIPS 2023) — https://arxiv.org/abs/2303.11366\n * - Picrew/awesome-agent-harness pattern #6\n * - ~/.claude/projects/-Users-localuser/memory/titan-v6-living-canvas.md\n */\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport logger from '../utils/logger.js';\nimport { recordJournalEvent } from './durableJournal.js';\n\nconst COMPONENT = 'AgentLessons';\n\n/** Lazy path resolution so tests can swap HOME / TITAN_HOME. */\nfunction titanHome(): string {\n const env = process.env.TITAN_HOME;\n if (env) return env.startsWith('~/') ? join(homedir(), env.slice(2)) : env;\n return join(homedir(), '.titan');\n}\nfunction lessonsPath(agentId: string): string {\n return join(titanHome(), 'agents', agentId, 'lessons.md');\n}\n\n/** A single lesson the agent has learned from a past failure or success. */\nexport interface AgentLesson {\n /** Short, verbal — the lesson the agent took away (1–3 sentences). */\n text: string;\n /** \"fail\" lessons come from things that went wrong; \"win\" lessons from\n * things that worked surprisingly well. Both are useful. Default fail. */\n kind?: 'fail' | 'win';\n /** Free-form context tag — what kind of task this was about (e.g.\n * \"merge-conflict\", \"json-extract\", \"vram-acquire\"). Used for fuzzy\n * lookup later. Optional. */\n topic?: string;\n /** Optional sessionId for traceback. */\n sessionId?: string;\n /** Optional goalId — when set, the lesson is also written into the\n * goal's durable journal so a resuming process can see it. */\n goalId?: string;\n}\n\n/** Cap on lesson count per agent. Older entries roll off the head. */\nconst LESSONS_PER_AGENT_CAP = 200;\n/** Cap on lessons rendered into the system prompt at run time. */\nconst PROMPT_RENDER_CAP = 12;\n\n/**\n * Append a lesson to the agent's lessons.md. Idempotent on near-duplicate\n * text (lowercased, whitespace-collapsed match) so the same lesson written\n * twice in close succession doesn't pollute the file.\n */\nexport function recordLesson(agentId: string, lesson: AgentLesson): void {\n if (!agentId || !lesson?.text || !lesson.text.trim()) return;\n try {\n const path = lessonsPath(agentId);\n try { mkdirSync(join(path, '..'), { recursive: true }); } catch { /* exists */ }\n\n const norm = (s: string) => s.toLowerCase().replace(/\\s+/g, ' ').trim();\n const incomingNorm = norm(lesson.text);\n\n // Read existing entries to dedup against recent ones.\n const existingRaw = existsSync(path) ? readFileSync(path, 'utf-8') : '';\n const recentNorms = parseLessonTexts(existingRaw)\n .slice(-20) // only dedup against last 20 — older content fades\n .map(norm);\n if (recentNorms.includes(incomingNorm)) {\n return; // already learned this lesson recently\n }\n\n // Append one entry in the documented format.\n const ts = new Date().toISOString();\n const kind = lesson.kind ?? 'fail';\n const topic = lesson.topic ? ` topic=\"${escapeAttr(lesson.topic)}\"` : '';\n const session = lesson.sessionId ? ` session=\"${escapeAttr(lesson.sessionId)}\"` : '';\n const block = `\\n- [${ts}] **${kind}**${topic}${session} — ${lesson.text.trim()}\\n`;\n\n // Make sure the file has a header on first write.\n if (!existsSync(path)) {\n writeFileSync(path, lessonsHeader(agentId), 'utf-8');\n }\n appendFileSync(path, block, 'utf-8');\n\n // Trim to cap if exceeded.\n trimIfOverCap(path);\n\n logger.info(COMPONENT, `Recorded ${kind}-lesson for ${agentId}${topic} — \"${lesson.text.slice(0, 80)}\"`);\n\n // v6.0 CP upgrade #4 — durable journal: surface lessons in the\n // per-context replay so resuming agents can see what was learned.\n recordJournalEvent(\n { agentId, goalId: lesson.goalId, sessionId: lesson.sessionId },\n 'lesson_recorded',\n { text: lesson.text.trim(), kind, topic: lesson.topic },\n );\n } catch (err) {\n logger.warn(COMPONENT, `Failed to write lesson for ${agentId}: ${(err as Error).message}`);\n }\n}\n\nfunction lessonsHeader(agentId: string): string {\n return [\n `# Lessons — ${agentId}`,\n ``,\n `Append-only verbal-reinforcement journal. Each entry is one lesson the`,\n `agent learned from a past attempt (failure or surprising win). The top`,\n `~${PROMPT_RENDER_CAP} most recent entries are injected into this agent's system`,\n `prompt at the start of every run.`,\n ``,\n `Format: \\`- [iso-ts] **kind** [topic=\"…\"] [session=\"…\"] — verbal lesson\\``,\n ``,\n `---`,\n ``,\n ].join('\\n');\n}\n\nfunction escapeAttr(s: string): string {\n return s.replace(/[\\\\\"\\n\\r]/g, c => c === '\\n' ? ' ' : c === '\\r' ? ' ' : '\\\\' + c).slice(0, 80);\n}\n\n/** Parse the lesson lines from the raw markdown. Returns text content only. */\nfunction parseLessonTexts(raw: string): string[] {\n const lines = raw.split(/\\r?\\n/);\n const out: string[] = [];\n for (const l of lines) {\n // Match the documented format. Capture everything after the trailing \" — \".\n const m = l.match(/^- \\[[^\\]]+\\]\\s+\\*\\*(fail|win)\\*\\*(?:[^—]*)—\\s+(.+)$/);\n if (m && m[2]) out.push(m[2]);\n }\n return out;\n}\n\nfunction trimIfOverCap(path: string): void {\n try {\n const raw = readFileSync(path, 'utf-8');\n const lines = raw.split(/\\r?\\n/);\n // Find the entries (lines starting with \"- [\")\n const entryIdx: number[] = [];\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].startsWith('- [')) entryIdx.push(i);\n }\n if (entryIdx.length <= LESSONS_PER_AGENT_CAP) return;\n // Drop the oldest (LESSONS_PER_AGENT_CAP / 4) when we tip over.\n const dropCount = Math.max(1, Math.floor(LESSONS_PER_AGENT_CAP / 4));\n const cutAt = entryIdx[dropCount] ?? entryIdx[0];\n // Header ends at first \"---\" line.\n const headerEnd = lines.findIndex(l => l.trim() === '---');\n const header = headerEnd >= 0 ? lines.slice(0, headerEnd + 2).join('\\n') : '';\n const kept = lines.slice(cutAt).join('\\n');\n writeFileSync(path, header + '\\n' + kept, 'utf-8');\n logger.info(COMPONENT, `Trimmed ${dropCount} oldest lessons from ${path}`);\n } catch { /* best-effort */ }\n}\n\n/**\n * Render the most relevant lessons for `agentId` as a prompt-ready block.\n * If `topic` is supplied, lessons matching that topic float to the top.\n * Returns empty string when there are no lessons yet (don't add a\n * prompt section with no content).\n */\nexport function renderAgentLessonsForPrompt(agentId: string, topic?: string): string {\n if (!agentId) return '';\n const path = lessonsPath(agentId);\n if (!existsSync(path)) return '';\n try {\n const raw = readFileSync(path, 'utf-8');\n const lines = raw.split(/\\r?\\n/);\n // Parse structured entries: kind + topic + text. Preserve source order so\n // lessons written within the same millisecond still render newest-first.\n const entries: Array<{ kind: string; topic: string; text: string; ts: string; index: number }> = [];\n for (let index = 0; index < lines.length; index++) {\n const l = lines[index];\n const m = l.match(/^- \\[([^\\]]+)\\]\\s+\\*\\*(fail|win)\\*\\*(?:\\s+topic=\"([^\"]+)\")?(?:[^—]*)—\\s+(.+)$/);\n if (m) entries.push({ ts: m[1], kind: m[2], topic: m[3] || '', text: m[4], index });\n }\n if (entries.length === 0) return '';\n\n // Sort: most recent first, with topic match boost.\n const wantsTopic = (topic || '').toLowerCase();\n const scored = entries.map(e => ({\n e,\n score: (wantsTopic && e.topic.toLowerCase() === wantsTopic ? 1 : 0)\n + (wantsTopic && e.topic.toLowerCase().includes(wantsTopic) ? 0.5 : 0),\n }));\n scored.sort((a, b) => b.score - a.score || b.e.ts.localeCompare(a.e.ts) || b.e.index - a.e.index);\n\n const top = scored.slice(0, PROMPT_RENDER_CAP).map(s => s.e);\n if (top.length === 0) return '';\n\n const out: string[] = [`## Lessons you've learned (top ${top.length})`];\n for (const e of top) {\n const tag = e.topic ? ` *(${e.topic})*` : '';\n out.push(`- **${e.kind}**${tag} — ${e.text}`);\n }\n return out.join('\\n');\n } catch {\n return '';\n }\n}\n\n/** Convenience helper — read raw lessons.md content (used by tests + UI). */\nexport function readLessonsMarkdown(agentId: string): string {\n const path = lessonsPath(agentId);\n return existsSync(path) ? readFileSync(path, 'utf-8') : '';\n}\n\n/** Test helper — wipe an agent's lessons file. */\nexport function __resetLessonsForTests(agentId: string): void {\n const path = lessonsPath(agentId);\n if (existsSync(path)) {\n try { unlinkSync(path); } catch { /* fine */ }\n }\n}\n"],"mappings":"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","names":[]}
@@ -634,6 +634,9 @@ Try a COMPLETELY DIFFERENT strategy. Do NOT repeat the same tools or approach.`
634
634
  const chatOptions = {
635
635
  model: activeModel,
636
636
  messages: smartMessages,
637
+ // v6.5 — forward the request AbortSignal so Stop / client
638
+ // disconnect cancels in-flight generation at the provider fetch.
639
+ signal: ctx.signal,
637
640
  tools: ctx.activeTools.length > 0 ? ctx.activeTools : void 0,
638
641
  maxTokens: isVoice ? Math.min(ctx.config.agent.maxTokens, 300) : ctx.config.agent.maxTokens,
639
642
  temperature: ctx.config.agent.temperature,
@@ -1503,7 +1506,8 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
1503
1506
  temperature: ctx.config.agent.temperature,
1504
1507
  thinking: ctx.voiceFastPath ? false : thinkingMode !== "off",
1505
1508
  thinkingLevel: thinkingMode,
1506
- providerOptions: ctx.providerOptions
1509
+ providerOptions: ctx.providerOptions,
1510
+ signal: ctx.signal
1507
1511
  };
1508
1512
  let response;
1509
1513
  const respondStart = Date.now();