titan-agent 6.3.6 → 6.4.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 (164) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/dist/agent/agent.js +6 -2
  3. package/dist/agent/agent.js.map +1 -1
  4. package/dist/agent/autoSkillGen.js +55 -17
  5. package/dist/agent/autoSkillGen.js.map +1 -1
  6. package/dist/agent/learningCurator.js +383 -0
  7. package/dist/agent/learningCurator.js.map +1 -0
  8. package/dist/agent/trajectoryLogger.js +2 -2
  9. package/dist/agent/trajectoryLogger.js.map +1 -1
  10. package/dist/config/schema.js +22 -0
  11. package/dist/config/schema.js.map +1 -1
  12. package/dist/gateway/server.js +6 -1
  13. package/dist/gateway/server.js.map +1 -1
  14. package/dist/utils/constants.js +1 -1
  15. package/dist/utils/constants.js.map +1 -1
  16. package/package.json +1 -1
  17. package/ui/dist/assets/{AuditPanel-C7rrUHBM.js → AuditPanel-CLc6IVvA.js} +1 -1
  18. package/ui/dist/assets/{AutonomyPanel-CcOxsveF.js → AutonomyPanel-E6arcbAb.js} +2 -2
  19. package/ui/dist/assets/{AutopilotPanel-CjrYEF2s.js → AutopilotPanel-AhNEPP_2.js} +1 -1
  20. package/ui/dist/assets/{AutoresearchPanel-CBdGRAdZ.js → AutoresearchPanel-BJRXdUNa.js} +1 -1
  21. package/ui/dist/assets/{BackupPanel-BhU-utCn.js → BackupPanel-CGQAxeWf.js} +1 -1
  22. package/ui/dist/assets/{BrowserPanel-Dr-B5Kva.js → BrowserPanel-BylOyNpQ.js} +1 -1
  23. package/ui/dist/assets/{CPActivity-C5kJWqWc.js → CPActivity-7zS0dXGf.js} +1 -1
  24. package/ui/dist/assets/{CPAgentDetail-BVE3LoLA.js → CPAgentDetail-DUi0ygHI.js} +1 -1
  25. package/ui/dist/assets/{CPAgents-BKBSyXHs.js → CPAgents-BZYynbga.js} +1 -1
  26. package/ui/dist/assets/{CPApprovals-AkRGu05y.js → CPApprovals-CwNTmqzq.js} +1 -1
  27. package/ui/dist/assets/{CPCosts-DC5mPwVm.js → CPCosts-5hylxGvH.js} +1 -1
  28. package/ui/dist/assets/{CPDashboard-C7G-H6HW.js → CPDashboard-5f8qcBKy.js} +1 -1
  29. package/ui/dist/assets/{CPFiles-IhcbsdvY.js → CPFiles-CR9Nn3_p.js} +1 -1
  30. package/ui/dist/assets/{CPGoals-BL0rrvT5.js → CPGoals-CVSxXk1i.js} +1 -1
  31. package/ui/dist/assets/{CPInbox-CBUkqu4i.js → CPInbox-DD4RYSpf.js} +1 -1
  32. package/ui/dist/assets/{CPIssueDetail-HuI9tPu4.js → CPIssueDetail-Dpuc_wTi.js} +1 -1
  33. package/ui/dist/assets/{CPIssues-Do9d-evH.js → CPIssues-DsPZLTGm.js} +1 -1
  34. package/ui/dist/assets/CPLayout-CDkrYE-H.js +2 -0
  35. package/ui/dist/assets/{CPOrg-WHkSBNGU.js → CPOrg-C121ZFWy.js} +1 -1
  36. package/ui/dist/assets/{CPRuns-B4Pz5QkG.js → CPRuns-BUdKn-uk.js} +1 -1
  37. package/ui/dist/assets/{CPSocial-DayQlwZL.js → CPSocial-CriG1P45.js} +1 -1
  38. package/ui/dist/assets/{CPVoice-C2ry-Muk.js → CPVoice-BNhgXUE1.js} +1 -1
  39. package/ui/dist/assets/{ChannelsPanel-CeIEB9Es.js → ChannelsPanel-9SjAkSHg.js} +1 -1
  40. package/ui/dist/assets/{CheckpointsPanel-BkqqvLDL.js → CheckpointsPanel-D2sCPz2u.js} +1 -1
  41. package/ui/dist/assets/{CommandPostHub-Jd5_PAh3.js → CommandPostHub-DHTVn2_E.js} +3 -3
  42. package/ui/dist/assets/{CronPanel-COkBBcTV.js → CronPanel-CLQ0_Z_9.js} +1 -1
  43. package/ui/dist/assets/{DataTable-YQ9l5Xab.js → DataTable-CrBYCT2O.js} +1 -1
  44. package/ui/dist/assets/{DreamPanel-B5M--_sB.js → DreamPanel-BAkiDqQc.js} +1 -1
  45. package/ui/dist/assets/EmptyState-DSuZC-Tg.js +1 -0
  46. package/ui/dist/assets/{EvalHarnessPanel-DXU9jNmm.js → EvalHarnessPanel-BykaelQd.js} +1 -1
  47. package/ui/dist/assets/{EvalPanel-CY1kmmoG.js → EvalPanel-BlFByCOv.js} +1 -1
  48. package/ui/dist/assets/{FileViewer-BPLxBXEr.js → FileViewer-B0WedL9k.js} +1 -1
  49. package/ui/dist/assets/{FilesPanel-B5RpGquX.js → FilesPanel-DwUzP4Hv.js} +2 -2
  50. package/ui/dist/assets/{FleetPanel-1g-MrF81.js → FleetPanel-JW9Pk1FM.js} +1 -1
  51. package/ui/dist/assets/{HomelabPanel-CxkWfeeN.js → HomelabPanel-DigWCjaM.js} +1 -1
  52. package/ui/dist/assets/InfraView-0iQPmcD1.js +2 -0
  53. package/ui/dist/assets/{InlineEditableField-BuXVLQiL.js → InlineEditableField-Car7F635.js} +1 -1
  54. package/ui/dist/assets/{Input-C6OxbFDH.js → Input-BGGQDAyP.js} +1 -1
  55. package/ui/dist/assets/{IntegrationsPanel-CPZclot5.js → IntegrationsPanel-BGEkU649.js} +1 -1
  56. package/ui/dist/assets/IntelligenceView-wm2Pw46I.js +2 -0
  57. package/ui/dist/assets/LearningPanel-CGENRFnq.js +1 -0
  58. package/ui/dist/assets/{LogsPanel-XfC4fyQ6.js → LogsPanel-DZ_K2xOL.js} +1 -1
  59. package/ui/dist/assets/{McpPanel-CBN7xizL.js → McpPanel-CNB1BMz-.js} +1 -1
  60. package/ui/dist/assets/MemoryGraphPanel-FQxiv5x5.js +16 -0
  61. package/ui/dist/assets/{MemoryWikiPanel-BNv9ycwr.js → MemoryWikiPanel-C1w7xDNl.js} +2 -7
  62. package/ui/dist/assets/{MeshPanel-GFPkWStS.js → MeshPanel-DjYZh3P3.js} +1 -1
  63. package/ui/dist/assets/{MissionCanvas-BR9tT-Vs.js → MissionCanvas-BvbSfN_4.js} +1 -1
  64. package/ui/dist/assets/{MissionChat-B_fuBUn5.js → MissionChat-Do8j0nvp.js} +1 -1
  65. package/ui/dist/assets/{MissionLibrary-C9fCMdpu.js → MissionLibrary-CTzkJFJ7.js} +1 -1
  66. package/ui/dist/assets/{MissionStart-Bb2fCB2u.js → MissionStart-_-qlVcRK.js} +1 -1
  67. package/ui/dist/assets/{Modal-9_l_CYE2.js → Modal-CNnSfYJm.js} +1 -1
  68. package/ui/dist/assets/{NvidiaPanel-CiB9Nlbq.js → NvidiaPanel-CLoX_gD2.js} +1 -1
  69. package/ui/dist/assets/{OrganismPanel-d6mPFcBq.js → OrganismPanel-DBy9J7Vl.js} +1 -1
  70. package/ui/dist/assets/{OverviewPanel-DF7eBJCF.js → OverviewPanel-B7JFNH2w.js} +1 -1
  71. package/ui/dist/assets/PageHeader-CwcMp_4A.js +1 -0
  72. package/ui/dist/assets/{PersonaProfilesPanel-qAgZyu1B.js → PersonaProfilesPanel-CmZhQC8H.js} +1 -1
  73. package/ui/dist/assets/{PersonasPanel-CxDpp_3j.js → PersonasPanel-DSP0tTKz.js} +1 -1
  74. package/ui/dist/assets/{PhoneDeskPanel-CLp3E_Ry.js → PhoneDeskPanel-DZSs7aPr.js} +1 -1
  75. package/ui/dist/assets/{RecipesPanel-DqMMQnyc.js → RecipesPanel-BW0hW2sW.js} +1 -1
  76. package/ui/dist/assets/{SecurityPanel-BQ3-aMmO.js → SecurityPanel-_Saqrvw7.js} +1 -1
  77. package/ui/dist/assets/{SelfImprovePanel-C-ylXP-k.js → SelfImprovePanel-D5s-1XCU.js} +1 -1
  78. package/ui/dist/assets/{SelfProposalsPanel-d5W-0A3P.js → SelfProposalsPanel-D24kB0uM.js} +2 -2
  79. package/ui/dist/assets/{SessionsPanel-DzbNc8iW.js → SessionsPanel-BCxsCKwr.js} +1 -1
  80. package/ui/dist/assets/{SessionsTab-CuRh8SAw.js → SessionsTab-C5fvnjtl.js} +1 -1
  81. package/ui/dist/assets/{SettingsPanel-C1FbNM3W.js → SettingsPanel-B7jrFmeR.js} +1 -1
  82. package/ui/dist/assets/SettingsView-CQMHjpN8.js +2 -0
  83. package/ui/dist/assets/{SkeletonLoader-CrZUFaIM.js → SkeletonLoader-C42HbL9c.js} +1 -1
  84. package/ui/dist/assets/{SkillsPanel-eyCYqSuP.js → SkillsPanel-DYG8avbV.js} +1 -1
  85. package/ui/dist/assets/{SomaView-CvHD2r-j.js → SomaView-8Rwvw2sJ.js} +1 -1
  86. package/ui/dist/assets/{StatCard-_MlD8k-5.js → StatCard-vp0doZoE.js} +1 -1
  87. package/ui/dist/assets/{StatusBadge-MIQ4EfQR.js → StatusBadge-C4dl4Z7r.js} +1 -1
  88. package/ui/dist/assets/{Tabs-DkooY-W7.js → Tabs-B3hK5PRj.js} +1 -1
  89. package/ui/dist/assets/{TeamsPanel-fa--osWi.js → TeamsPanel-Bgc-EYSL.js} +1 -1
  90. package/ui/dist/assets/{TelemetryPanel-BEEWcoNE.js → TelemetryPanel-DexYjpd5.js} +1 -1
  91. package/ui/dist/assets/{TitanCanvas-LWyPGpf5.js → TitanCanvas-Da6TxxdJ.js} +52 -27
  92. package/ui/dist/assets/{ToolsView-UCDMsvb1.js → ToolsView-DkP9NfCj.js} +2 -2
  93. package/ui/dist/assets/{Tooltip-DrpHNalN.js → Tooltip-Chg08x7x.js} +1 -1
  94. package/ui/dist/assets/{TraceViewer-XsmYbc0e.js → TraceViewer-CojtXASo.js} +1 -1
  95. package/ui/dist/assets/{TrainingPanel-CmJXLn4n.js → TrainingPanel-tgn1BmiH.js} +1 -1
  96. package/ui/dist/assets/{VoiceOverlay-D2N-c76K.js → VoiceOverlay-D_jfyVe9.js} +1 -1
  97. package/ui/dist/assets/{VramPanel-h6zjNx-6.js → VramPanel-DXrdQK3J.js} +1 -1
  98. package/ui/dist/assets/{WatchView-DDNt13KH.js → WatchView-BitQHsCy.js} +1 -1
  99. package/ui/dist/assets/{WorkTab-CLXkjuee.js → WorkTab-6hdXBR7d.js} +1 -1
  100. package/ui/dist/assets/{WorkflowsPanel-BYtLGQVm.js → WorkflowsPanel-BlB2iBik.js} +1 -1
  101. package/ui/dist/assets/{archive-BSupLPZM.js → archive-aaQ0bgj_.js} +1 -1
  102. package/ui/dist/assets/{arrow-left-BaVVZjqf.js → arrow-left-xxx_FceU.js} +1 -1
  103. package/ui/dist/assets/{book-open-CZxMkLs8.js → book-open-B00IpKZA.js} +1 -1
  104. package/ui/dist/assets/{bot-Cv-edYov.js → bot-C4CRICZX.js} +1 -1
  105. package/ui/dist/assets/{chart-column-BSVdQ9RP.js → chart-column-lnilxGq2.js} +1 -1
  106. package/ui/dist/assets/{check-DbgTEg8O.js → check-BJMHM0tu.js} +1 -1
  107. package/ui/dist/assets/{chevron-right-DfToq0aQ.js → chevron-right-CA8HOEP4.js} +1 -1
  108. package/ui/dist/assets/{chevron-up-DMS2Tjip.js → chevron-up-n_3KVmjQ.js} +1 -1
  109. package/ui/dist/assets/{circle-check-big-BBQqeCVt.js → circle-check-big-hiNdH5H3.js} +1 -1
  110. package/ui/dist/assets/{circle-stop-CZNS3Uri.js → circle-stop-BlIwj_3n.js} +1 -1
  111. package/ui/dist/assets/{clipboard-list-CzMXlUCb.js → clipboard-list-BnOtwHQm.js} +1 -1
  112. package/ui/dist/assets/{clock-BvWNugZo.js → clock-BFgCJnqQ.js} +1 -1
  113. package/ui/dist/assets/{dollar-sign-pghzkByE.js → dollar-sign-CBBudQd7.js} +1 -1
  114. package/ui/dist/assets/{download-CupMD2Qi.js → download-C-DdF4cx.js} +1 -1
  115. package/ui/dist/assets/{external-link-8Pnd2MvS.js → external-link-CPAx2HBG.js} +1 -1
  116. package/ui/dist/assets/{file-text-rkojc4Uq.js → file-text-CyC_OxlA.js} +1 -1
  117. package/ui/dist/assets/{flask-conical-BtbE3rIA.js → flask-conical-BKbKUYhz.js} +1 -1
  118. package/ui/dist/assets/{folder-65U1Euur.js → folder-7bWz46dM.js} +1 -1
  119. package/ui/dist/assets/{funnel-D9FpWHn9.js → funnel-C_bR-Dz5.js} +1 -1
  120. package/ui/dist/assets/{git-branch-D6YV_OC1.js → git-branch-DGWdkudk.js} +1 -1
  121. package/ui/dist/assets/{index-BslUQuVl.js → index-CgHHe41p.js} +19 -19
  122. package/ui/dist/assets/index-CtD9JOx2.css +1 -0
  123. package/ui/dist/assets/{layers-rc37Q5yX.js → layers-3BMDaZuk.js} +1 -1
  124. package/ui/dist/assets/{legacy-BydeSW77.js → legacy-VWR8oDEy.js} +1 -1
  125. package/ui/dist/assets/{lightbulb-BEx7BdxD.js → lightbulb-CPhcUxli.js} +1 -1
  126. package/ui/dist/assets/link-CfLpUR24.js +6 -0
  127. package/ui/dist/assets/{list-todo-6N1AZe4i.js → list-todo-DgAmn8ot.js} +1 -1
  128. package/ui/dist/assets/{message-square-BEGY8pHF.js → message-square-WJp1dTaM.js} +1 -1
  129. package/ui/dist/assets/{mic-CGj6cLoV.js → mic-DjXDp1Js.js} +1 -1
  130. package/ui/dist/assets/{missions-BohoEPZo.js → missions-CwDL2FHE.js} +1 -1
  131. package/ui/dist/assets/{network-DxIkmFJR.js → network-C_MOo5My.js} +1 -1
  132. package/ui/dist/assets/{pause-CxvyFedA.js → pause-CovS6Xwq.js} +1 -1
  133. package/ui/dist/assets/{pencil-D5LooNFk.js → pencil-C6SVDEUL.js} +1 -1
  134. package/ui/dist/assets/{play-6W8amqUG.js → play-D3Nbm1Cz.js} +1 -1
  135. package/ui/dist/assets/{plug-C3Ecy-Ji.js → plug-Dpu9RRBi.js} +1 -1
  136. package/ui/dist/assets/{plus-BtmCDNeU.js → plus-CJKkM9fM.js} +1 -1
  137. package/ui/dist/assets/{proxy-BD29vUSb.js → proxy-CaK_YXZW.js} +1 -1
  138. package/ui/dist/assets/{radio-Bl9Tibea.js → radio-BTFO9FV9.js} +1 -1
  139. package/ui/dist/assets/{refresh-cw-BHphkMjv.js → refresh-cw-_C-4Cd_3.js} +1 -1
  140. package/ui/dist/assets/{rotate-ccw-DQzGCkVc.js → rotate-ccw-DWyVAH19.js} +1 -1
  141. package/ui/dist/assets/{save-Bk90yLic.js → save-B02XWwgN.js} +1 -1
  142. package/ui/dist/assets/{search-DxM6zcDJ.js → search-CHteSk-9.js} +1 -1
  143. package/ui/dist/assets/{send-ChZ5Hsne.js → send-DkMQeZAz.js} +1 -1
  144. package/ui/dist/assets/{server-CSOB40Ah.js → server-CvEvPDpX.js} +1 -1
  145. package/ui/dist/assets/{shield-check-uMK1alGs.js → shield-check-DQeYCvwF.js} +1 -1
  146. package/ui/dist/assets/{terminal-lIpm0q0K.js → terminal-BVErXh5C.js} +1 -1
  147. package/ui/dist/assets/{test-tube-DuTInDo7.js → test-tube-JSgqEqJs.js} +1 -1
  148. package/ui/dist/assets/{toggle-right-j4jgj1NQ.js → toggle-right-B3Qz7cQt.js} +1 -1
  149. package/ui/dist/assets/{trash-2-2Qmp8Fyr.js → trash-2-kiHv5iAF.js} +1 -1
  150. package/ui/dist/assets/{trending-up-BydxQces.js → trending-up-xhvUQXWt.js} +1 -1
  151. package/ui/dist/assets/{trophy-B8Fq83wO.js → trophy-G3zNxlQS.js} +1 -1
  152. package/ui/dist/assets/{volume-2-B6qThcBF.js → volume-2-CELMZXsT.js} +1 -1
  153. package/ui/dist/assets/{wifi-off-BX5XYuML.js → wifi-off-DKKYhVWL.js} +1 -1
  154. package/ui/dist/index.html +2 -2
  155. package/ui/dist/sw.js +1 -1
  156. package/ui/dist/assets/CPLayout-DlpdPzBu.js +0 -2
  157. package/ui/dist/assets/EmptyState-DtSIPEVv.js +0 -1
  158. package/ui/dist/assets/InfraView-CYH3ieie.js +0 -2
  159. package/ui/dist/assets/IntelligenceView-DqwlFo91.js +0 -2
  160. package/ui/dist/assets/LearningPanel-DHQSZZGb.js +0 -1
  161. package/ui/dist/assets/MemoryGraphPanel-BBxgK2Lo.js +0 -16
  162. package/ui/dist/assets/PageHeader-BQEb_uAl.js +0 -1
  163. package/ui/dist/assets/SettingsView-ZTklN8Q3.js +0 -2
  164. package/ui/dist/assets/index-DmgXt1oB.css +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/agent/learningCurator.ts"],"sourcesContent":["/**\n * TITAN — Learning Curator\n *\n * Ported from the MIT-licensed Hermes Agent background review / curator pattern:\n * https://github.com/NousResearch/hermes-agent\n *\n * Copyright (c) 2025 Nous Research\n * MIT License. See THIRD_PARTY_NOTICES.md for attribution.\n *\n * Hermes runs a background review pass over recent work and updates memory or\n * skills when durable learning appears. This TITAN-native port keeps the same\n * operational shape but writes into TITAN's existing trajectory and\n * per-agent lesson systems instead of introducing a parallel skill store.\n */\nimport { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport logger from '../utils/logger.js';\nimport { TITAN_HOME } from '../utils/constants.js';\nimport { getRecentTrajectories, getSequenceSignature, type TaskTrajectory } from './trajectoryLogger.js';\nimport { recordLesson } from './agentLessons.js';\nimport { loadConfig } from '../config/config.js';\n\n/**\n * v6.4.0 — read curator knobs from `learningCurator` config section, with\n * fallback to constants when config is unavailable (e.g. during unit\n * tests that don't initialize a TitanConfig). The shape mirrors the Zod\n * schema in src/config/schema.ts.\n */\ninterface CuratorConfig {\n enabled: boolean;\n intervalHours: number;\n minTrajectories: number;\n staleAfterDays: number;\n archiveAfterDays: number;\n}\n\nfunction loadCuratorConfig(): CuratorConfig {\n try {\n const cfg = loadConfig() as unknown as { learningCurator?: Partial<CuratorConfig> };\n const lc = cfg.learningCurator ?? {};\n return {\n enabled: lc.enabled ?? true,\n intervalHours: lc.intervalHours ?? DEFAULT_INTERVAL_HOURS,\n minTrajectories: lc.minTrajectories ?? DEFAULT_MIN_TRAJECTORIES,\n staleAfterDays: lc.staleAfterDays ?? DEFAULT_STALE_AFTER_DAYS,\n archiveAfterDays: lc.archiveAfterDays ?? DEFAULT_ARCHIVE_AFTER_DAYS,\n };\n } catch {\n return {\n enabled: true,\n intervalHours: DEFAULT_INTERVAL_HOURS,\n minTrajectories: DEFAULT_MIN_TRAJECTORIES,\n staleAfterDays: DEFAULT_STALE_AFTER_DAYS,\n archiveAfterDays: DEFAULT_ARCHIVE_AFTER_DAYS,\n };\n }\n}\n\nconst COMPONENT = 'LearningCurator';\nconst STATE_PATH = join(TITAN_HOME, 'learning-curator-state.json');\nconst REPORTS_DIR = join(TITAN_HOME, 'logs', 'learning-curator');\nconst DEFAULT_INTERVAL_HOURS = 24;\nconst DEFAULT_MIN_TRAJECTORIES = 3;\nconst DEFAULT_STALE_AFTER_DAYS = 30;\nconst DEFAULT_ARCHIVE_AFTER_DAYS = 90;\n\nexport type LearningPatternLifecycleState = 'active' | 'watch' | 'stale' | 'archived';\n\nexport interface LearningPatternState {\n taskType: string;\n signature: string;\n toolSequence: string[];\n successes: number;\n failures: number;\n transientFailures: number;\n state: LearningPatternLifecycleState;\n firstSeenAt: string;\n lastSeenAt: string;\n lastSuccessAt: string | null;\n lastFailureAt: string | null;\n commonFailureTool: string | null;\n}\n\nexport interface LearningPatternTransitionCounts {\n checked: number;\n markedStale: number;\n archived: number;\n reactivated: number;\n}\n\nexport interface LearningCuratorState {\n lastRunAt: string | null;\n lastRunDurationMs?: number | null;\n lastRunSummary: string | null;\n lastReportPath?: string | null;\n runCount: number;\n paused: boolean;\n patterns: Record<string, LearningPatternState>;\n}\n\nexport interface CuratorReviewResult {\n shouldRun: boolean;\n lessonsRecorded: number;\n summary: string;\n reportPath: string | null;\n transitions: LearningPatternTransitionCounts;\n}\n\nfunction defaultState(): LearningCuratorState {\n return {\n lastRunAt: null,\n lastRunDurationMs: null,\n lastRunSummary: null,\n lastReportPath: null,\n runCount: 0,\n paused: false,\n patterns: {},\n };\n}\n\nexport function loadLearningCuratorState(): LearningCuratorState {\n if (!existsSync(STATE_PATH)) return defaultState();\n try {\n const parsed = JSON.parse(readFileSync(STATE_PATH, 'utf-8')) as Partial<LearningCuratorState>;\n return { ...defaultState(), ...parsed };\n } catch {\n return defaultState();\n }\n}\n\nexport function saveLearningCuratorState(state: LearningCuratorState): void {\n try {\n mkdirSync(dirname(STATE_PATH), { recursive: true });\n atomicWriteText(STATE_PATH, JSON.stringify(state, null, 2));\n } catch (err) {\n logger.warn(COMPONENT, `Failed to save curator state: ${(err as Error).message}`);\n }\n}\n\nfunction atomicWriteText(path: string, content: string): void {\n const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;\n writeFileSync(tmp, content, 'utf-8');\n try {\n renameSync(tmp, path);\n } catch (err) {\n try { unlinkSync(tmp); } catch { /* ignore cleanup */ }\n throw err;\n }\n}\n\nexport function shouldRunLearningReview(now: Date = new Date(), intervalHours = DEFAULT_INTERVAL_HOURS): boolean {\n const state = loadLearningCuratorState();\n if (state.paused) return false;\n\n if (!state.lastRunAt) {\n saveLearningCuratorState({\n ...state,\n lastRunAt: now.toISOString(),\n lastRunDurationMs: 0,\n lastRunSummary: 'Seeded learning curator; first review deferred until interval elapses.',\n });\n return false;\n }\n\n const lastRun = Date.parse(state.lastRunAt);\n if (!Number.isFinite(lastRun)) return true;\n return now.getTime() - lastRun >= intervalHours * 60 * 60 * 1000;\n}\n\nexport function runLearningReview(\n trigger: TaskTrajectory,\n now: Date = new Date(),\n minTrajectories = DEFAULT_MIN_TRAJECTORIES,\n): CuratorReviewResult {\n const started = Date.now();\n const emptyTransitions: LearningPatternTransitionCounts = {\n checked: 0,\n markedStale: 0,\n archived: 0,\n reactivated: 0,\n };\n\n if (!shouldRunLearningReview(now)) {\n return {\n shouldRun: false,\n lessonsRecorded: 0,\n summary: 'Learning review deferred by interval gate.',\n reportPath: null,\n transitions: emptyTransitions,\n };\n }\n\n const recent = getRecentTrajectories(200, { taskType: trigger.taskType });\n if (recent.length < minTrajectories) {\n const summary = `Skipped ${trigger.taskType}: only ${recent.length}/${minTrajectories} trajectories.`;\n const reportPath = writeLearningReviewReport(now, {\n taskType: trigger.taskType,\n summary,\n lessons: [],\n transitions: emptyTransitions,\n recentCount: recent.length,\n durationMs: Date.now() - started,\n });\n persistReview(now, summary, reportPath, Date.now() - started);\n return {\n shouldRun: true,\n lessonsRecorded: 0,\n summary: 'Not enough trajectory history to curate.',\n reportPath,\n transitions: emptyTransitions,\n };\n }\n\n const transitions = applyLearningPatternTransitions(now);\n const lessons = deriveLessons(trigger, recent);\n for (const lesson of lessons) {\n recordLesson('default', {\n kind: lesson.kind,\n topic: lesson.topic,\n sessionId: trigger.sessionId,\n text: lesson.text,\n });\n }\n\n const summary = lessons.length > 0\n ? `Recorded ${lessons.length} ${trigger.taskType} learning lesson(s).`\n : `Reviewed ${recent.length} ${trigger.taskType} trajectories; nothing durable to save.`;\n const durationMs = Date.now() - started;\n const reportPath = writeLearningReviewReport(now, {\n taskType: trigger.taskType,\n summary,\n lessons,\n transitions,\n recentCount: recent.length,\n durationMs,\n });\n persistReview(now, summary, reportPath, durationMs);\n return { shouldRun: true, lessonsRecorded: lessons.length, summary, reportPath, transitions };\n}\n\nfunction persistReview(now: Date, summary: string, reportPath: string | null, durationMs: number): void {\n const state = loadLearningCuratorState();\n saveLearningCuratorState({\n ...state,\n lastRunAt: now.toISOString(),\n lastRunDurationMs: durationMs,\n lastRunSummary: summary,\n lastReportPath: reportPath,\n runCount: state.runCount + 1,\n });\n}\n\ninterface DerivedLesson {\n kind: 'fail' | 'win';\n topic: string;\n text: string;\n}\n\ninterface LearningReviewReportInput {\n taskType: string;\n summary: string;\n lessons: DerivedLesson[];\n transitions: LearningPatternTransitionCounts;\n recentCount: number;\n durationMs: number;\n}\n\nexport function writeLearningReviewReport(now: Date, report: LearningReviewReportInput): string | null {\n try {\n const stamp = now.toISOString().replace(/[-:]/g, '').replace(/\\.\\d{3}Z$/, 'Z');\n const dir = join(REPORTS_DIR, stamp);\n mkdirSync(dir, { recursive: true });\n const jsonPath = join(dir, 'run.json');\n const mdPath = join(dir, 'REPORT.md');\n atomicWriteText(jsonPath, JSON.stringify({\n generatedAt: now.toISOString(),\n taskType: report.taskType,\n summary: report.summary,\n recentCount: report.recentCount,\n lessons: report.lessons,\n transitions: report.transitions,\n durationMs: report.durationMs,\n }, null, 2));\n atomicWriteText(mdPath, renderLearningReviewReport(now, report));\n return mdPath;\n } catch (err) {\n logger.warn(COMPONENT, `Failed to write curator report: ${(err as Error).message}`);\n return null;\n }\n}\n\nfunction renderLearningReviewReport(now: Date, report: LearningReviewReportInput): string {\n const lessons = report.lessons.length > 0\n ? report.lessons.map(lesson => `- ${lesson.kind.toUpperCase()} / ${lesson.topic}: ${lesson.text}`).join('\\n')\n : '- No durable lessons recorded.';\n\n return `# TITAN Learning Curator Report\n\n- Generated: ${now.toISOString()}\n- Task type: ${report.taskType}\n- Recent trajectories checked: ${report.recentCount}\n- Duration: ${report.durationMs}ms\n- Summary: ${report.summary}\n\n## Pattern Transitions\n- Checked: ${report.transitions.checked}\n- Marked stale: ${report.transitions.markedStale}\n- Archived: ${report.transitions.archived}\n- Reactivated: ${report.transitions.reactivated}\n\n## Lessons\n${lessons}\n`;\n}\n\nfunction deriveLessons(trigger: TaskTrajectory, recent: TaskTrajectory[]): DerivedLesson[] {\n const lessons: DerivedLesson[] = [];\n const signature = getSequenceSignature(trigger.toolSequence);\n const sameSequence = recent.filter(t => getSequenceSignature(t.toolSequence) === signature);\n const successes = sameSequence.filter(t => t.success);\n const failures = sameSequence.filter(t => !t.success && isDurableLearningFailure(t));\n\n if (successes.length >= DEFAULT_MIN_TRAJECTORIES) {\n lessons.push({\n kind: 'win',\n topic: trigger.taskType,\n text: `For ${trigger.taskType} tasks, the tool sequence ${signature} is proven across ${successes.length} successful trajectories; prefer it before inventing a new flow, then verify the final artifact before reporting done.`,\n });\n }\n\n if (failures.length >= 2) {\n const failedTool = mostCommonFailureTool(failures);\n lessons.push({\n kind: 'fail',\n topic: trigger.taskType,\n text: `For ${trigger.taskType} tasks, repeated failures happened in the ${signature} flow${failedTool ? ` around ${failedTool}` : ''}; inspect the last tool result and change approach instead of retrying the same sequence blindly.`,\n });\n }\n\n const recentFailure = recent.find(t => !t.success && t.timestamp < trigger.timestamp);\n if (trigger.success && recentFailure && signature !== getSequenceSignature(recentFailure.toolSequence)) {\n lessons.push({\n kind: 'win',\n topic: trigger.taskType,\n text: `When a ${trigger.taskType} attempt stalls or fails, switching to ${signature} has produced a successful recovery path; use this as the first fallback candidate for similar work.`,\n });\n }\n\n return lessons;\n}\n\nexport function ingestTrajectoryLearningSignal(trajectory: TaskTrajectory): LearningPatternState {\n const state = loadLearningCuratorState();\n const now = trajectory.timestamp || new Date().toISOString();\n const signature = getSequenceSignature(trajectory.toolSequence);\n const key = patternKey(trajectory.taskType, signature);\n const existing = state.patterns[key];\n const transientFailure = !trajectory.success && !isDurableLearningFailure(trajectory);\n const failureTool = !trajectory.success && !transientFailure ? mostCommonFailureTool([trajectory]) : '';\n\n const pattern: LearningPatternState = {\n taskType: trajectory.taskType,\n signature,\n toolSequence: trajectory.toolSequence,\n successes: existing?.successes ?? 0,\n failures: existing?.failures ?? 0,\n transientFailures: existing?.transientFailures ?? 0,\n state: existing?.state ?? 'active',\n firstSeenAt: existing?.firstSeenAt ?? now,\n lastSeenAt: now,\n lastSuccessAt: existing?.lastSuccessAt ?? null,\n lastFailureAt: existing?.lastFailureAt ?? null,\n commonFailureTool: existing?.commonFailureTool ?? null,\n };\n\n if (trajectory.success) {\n pattern.successes += 1;\n pattern.lastSuccessAt = now;\n pattern.state = 'active';\n } else if (transientFailure) {\n pattern.transientFailures += 1;\n } else {\n pattern.failures += 1;\n pattern.lastFailureAt = now;\n pattern.commonFailureTool = failureTool || pattern.commonFailureTool;\n if (pattern.failures >= 2) pattern.state = 'watch';\n }\n\n state.patterns[key] = pattern;\n saveLearningCuratorState(state);\n return pattern;\n}\n\nexport function applyLearningPatternTransitions(\n now: Date = new Date(),\n staleAfterDays = DEFAULT_STALE_AFTER_DAYS,\n archiveAfterDays = DEFAULT_ARCHIVE_AFTER_DAYS,\n): LearningPatternTransitionCounts {\n const state = loadLearningCuratorState();\n const counts: LearningPatternTransitionCounts = {\n checked: 0,\n markedStale: 0,\n archived: 0,\n reactivated: 0,\n };\n const staleCutoff = now.getTime() - staleAfterDays * 24 * 60 * 60 * 1000;\n const archiveCutoff = now.getTime() - archiveAfterDays * 24 * 60 * 60 * 1000;\n\n for (const pattern of Object.values(state.patterns)) {\n counts.checked += 1;\n const lastSeen = Date.parse(pattern.lastSeenAt);\n if (!Number.isFinite(lastSeen)) continue;\n\n if (lastSeen <= archiveCutoff && pattern.state !== 'archived') {\n pattern.state = 'archived';\n counts.archived += 1;\n } else if (lastSeen <= staleCutoff && pattern.state === 'active') {\n pattern.state = 'stale';\n counts.markedStale += 1;\n } else if (lastSeen > staleCutoff && pattern.state === 'stale') {\n pattern.state = 'active';\n counts.reactivated += 1;\n }\n }\n\n if (counts.markedStale > 0 || counts.archived > 0 || counts.reactivated > 0) {\n saveLearningCuratorState(state);\n }\n return counts;\n}\n\nexport function renderLearningPatternGuidance(taskType: string, limit = 3): string | null {\n const state = loadLearningCuratorState();\n const patterns = Object.values(state.patterns)\n .filter(pattern => pattern.taskType === taskType && pattern.state !== 'archived')\n .sort((a, b) => patternSortScore(b) - patternSortScore(a))\n .slice(0, limit);\n\n if (patterns.length === 0) return null;\n\n const lines = ['Continuous learning patterns from recent TITAN trajectories:'];\n for (const pattern of patterns) {\n if (pattern.state === 'watch') {\n const failureDetail = pattern.commonFailureTool ? ` around ${pattern.commonFailureTool}` : '';\n lines.push(`- Watch ${pattern.signature}: ${pattern.failures} durable failure(s)${failureDetail}; inspect the failed tool result before repeating this flow.`);\n } else {\n lines.push(`- Prefer ${pattern.signature}: ${pattern.successes} win(s), ${pattern.failures} durable failure(s); verify the final artifact before reporting done.`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction patternSortScore(pattern: LearningPatternState): number {\n const stateBoost = pattern.state === 'watch' ? 1000 : pattern.state === 'active' ? 500 : 0;\n return stateBoost + pattern.successes * 10 - pattern.failures;\n}\n\nfunction patternKey(taskType: string, signature: string): string {\n return `${taskType}:${signature}`;\n}\n\nfunction isDurableLearningFailure(trajectory: TaskTrajectory): boolean {\n if (trajectory.success) return false;\n const text = trajectory.toolDetails\n .map(detail => `${detail.name}\\n${detail.resultSnippet ?? ''}`)\n .join('\\n')\n .toLowerCase();\n\n return ![\n 'command not found',\n 'module not found',\n 'cannot find module',\n 'no such file or directory',\n 'not configured',\n 'missing api key',\n 'missing token',\n 'invalid api key',\n 'credentials are not configured',\n 'authentication required',\n 'unauthorized',\n 'rate limit',\n 'temporarily unavailable',\n 'network error',\n 'econnreset',\n 'etimedout',\n ].some(marker => text.includes(marker));\n}\n\nfunction mostCommonFailureTool(failures: TaskTrajectory[]): string {\n const counts = new Map<string, number>();\n for (const trajectory of failures) {\n for (const detail of trajectory.toolDetails) {\n if (!detail.success) counts.set(detail.name, (counts.get(detail.name) ?? 0) + 1);\n }\n }\n return [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? '';\n}\n\nexport function processTrajectoryForLearningReview(trajectory: TaskTrajectory): void {\n try {\n // v6.4.0 — respect the config-level enable flag so operators who\n // turn the curator off in titan.json don't pay any cost per task.\n const cfg = loadCuratorConfig();\n if (!cfg.enabled) return;\n ingestTrajectoryLearningSignal(trajectory);\n const result = runLearningReview(trajectory, new Date(), cfg.minTrajectories);\n if (result.shouldRun) logger.info(COMPONENT, result.summary);\n } catch (err) {\n logger.warn(COMPONENT, `Learning review failed: ${(err as Error).message}`);\n }\n}\n"],"mappings":";AAcA,SAAS,YAAY,WAAW,cAAc,YAAY,YAAY,qBAAqB;AAC3F,SAAS,SAAS,YAAY;AAC9B,OAAO,YAAY;AACnB,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB,4BAAiD;AACjF,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAgB3B,SAAS,oBAAmC;AACxC,MAAI;AACA,UAAM,MAAM,WAAW;AACvB,UAAM,KAAK,IAAI,mBAAmB,CAAC;AACnC,WAAO;AAAA,MACH,SAAS,GAAG,WAAW;AAAA,MACvB,eAAe,GAAG,iBAAiB;AAAA,MACnC,iBAAiB,GAAG,mBAAmB;AAAA,MACvC,gBAAgB,GAAG,kBAAkB;AAAA,MACrC,kBAAkB,GAAG,oBAAoB;AAAA,IAC7C;AAAA,EACJ,QAAQ;AACJ,WAAO;AAAA,MACH,SAAS;AAAA,MACT,eAAe;AAAA,MACf,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,IACtB;AAAA,EACJ;AACJ;AAEA,MAAM,YAAY;AAClB,MAAM,aAAa,KAAK,YAAY,6BAA6B;AACjE,MAAM,cAAc,KAAK,YAAY,QAAQ,kBAAkB;AAC/D,MAAM,yBAAyB;AAC/B,MAAM,2BAA2B;AACjC,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AA4CnC,SAAS,eAAqC;AAC1C,SAAO;AAAA,IACH,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,UAAU,CAAC;AAAA,EACf;AACJ;AAEO,SAAS,2BAAiD;AAC7D,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO,aAAa;AACjD,MAAI;AACA,UAAM,SAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,WAAO,EAAE,GAAG,aAAa,GAAG,GAAG,OAAO;AAAA,EAC1C,QAAQ;AACJ,WAAO,aAAa;AAAA,EACxB;AACJ;AAEO,SAAS,yBAAyB,OAAmC;AACxE,MAAI;AACA,cAAU,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,oBAAgB,YAAY,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,EAC9D,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,iCAAkC,IAAc,OAAO,EAAE;AAAA,EACpF;AACJ;AAEA,SAAS,gBAAgB,MAAc,SAAuB;AAC1D,QAAM,MAAM,GAAG,IAAI,IAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AAChD,gBAAc,KAAK,SAAS,OAAO;AACnC,MAAI;AACA,eAAW,KAAK,IAAI;AAAA,EACxB,SAAS,KAAK;AACV,QAAI;AAAE,iBAAW,GAAG;AAAA,IAAG,QAAQ;AAAA,IAAuB;AACtD,UAAM;AAAA,EACV;AACJ;AAEO,SAAS,wBAAwB,MAAY,oBAAI,KAAK,GAAG,gBAAgB,wBAAiC;AAC7G,QAAM,QAAQ,yBAAyB;AACvC,MAAI,MAAM,OAAQ,QAAO;AAEzB,MAAI,CAAC,MAAM,WAAW;AAClB,6BAAyB;AAAA,MACrB,GAAG;AAAA,MACH,WAAW,IAAI,YAAY;AAAA,MAC3B,mBAAmB;AAAA,MACnB,gBAAgB;AAAA,IACpB,CAAC;AACD,WAAO;AAAA,EACX;AAEA,QAAM,UAAU,KAAK,MAAM,MAAM,SAAS;AAC1C,MAAI,CAAC,OAAO,SAAS,OAAO,EAAG,QAAO;AACtC,SAAO,IAAI,QAAQ,IAAI,WAAW,gBAAgB,KAAK,KAAK;AAChE;AAEO,SAAS,kBACZ,SACA,MAAY,oBAAI,KAAK,GACrB,kBAAkB,0BACC;AACnB,QAAM,UAAU,KAAK,IAAI;AACzB,QAAM,mBAAoD;AAAA,IACtD,SAAS;AAAA,IACT,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,EACjB;AAEA,MAAI,CAAC,wBAAwB,GAAG,GAAG;AAC/B,WAAO;AAAA,MACH,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,aAAa;AAAA,IACjB;AAAA,EACJ;AAEA,QAAM,SAAS,sBAAsB,KAAK,EAAE,UAAU,QAAQ,SAAS,CAAC;AACxE,MAAI,OAAO,SAAS,iBAAiB;AACjC,UAAMA,WAAU,WAAW,QAAQ,QAAQ,UAAU,OAAO,MAAM,IAAI,eAAe;AACrF,UAAMC,cAAa,0BAA0B,KAAK;AAAA,MAC9C,UAAU,QAAQ;AAAA,MAClB,SAAAD;AAAA,MACA,SAAS,CAAC;AAAA,MACV,aAAa;AAAA,MACb,aAAa,OAAO;AAAA,MACpB,YAAY,KAAK,IAAI,IAAI;AAAA,IAC7B,CAAC;AACD,kBAAc,KAAKA,UAASC,aAAY,KAAK,IAAI,IAAI,OAAO;AAC5D,WAAO;AAAA,MACH,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,MACT,YAAAA;AAAA,MACA,aAAa;AAAA,IACjB;AAAA,EACJ;AAEA,QAAM,cAAc,gCAAgC,GAAG;AACvD,QAAM,UAAU,cAAc,SAAS,MAAM;AAC7C,aAAW,UAAU,SAAS;AAC1B,iBAAa,WAAW;AAAA,MACpB,MAAM,OAAO;AAAA,MACb,OAAO,OAAO;AAAA,MACd,WAAW,QAAQ;AAAA,MACnB,MAAM,OAAO;AAAA,IACjB,CAAC;AAAA,EACL;AAEA,QAAM,UAAU,QAAQ,SAAS,IAC3B,YAAY,QAAQ,MAAM,IAAI,QAAQ,QAAQ,yBAC9C,YAAY,OAAO,MAAM,IAAI,QAAQ,QAAQ;AACnD,QAAM,aAAa,KAAK,IAAI,IAAI;AAChC,QAAM,aAAa,0BAA0B,KAAK;AAAA,IAC9C,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO;AAAA,IACpB;AAAA,EACJ,CAAC;AACD,gBAAc,KAAK,SAAS,YAAY,UAAU;AAClD,SAAO,EAAE,WAAW,MAAM,iBAAiB,QAAQ,QAAQ,SAAS,YAAY,YAAY;AAChG;AAEA,SAAS,cAAc,KAAW,SAAiB,YAA2B,YAA0B;AACpG,QAAM,QAAQ,yBAAyB;AACvC,2BAAyB;AAAA,IACrB,GAAG;AAAA,IACH,WAAW,IAAI,YAAY;AAAA,IAC3B,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,UAAU,MAAM,WAAW;AAAA,EAC/B,CAAC;AACL;AAiBO,SAAS,0BAA0B,KAAW,QAAkD;AACnG,MAAI;AACA,UAAM,QAAQ,IAAI,YAAY,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,aAAa,GAAG;AAC7E,UAAM,MAAM,KAAK,aAAa,KAAK;AACnC,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAClC,UAAM,WAAW,KAAK,KAAK,UAAU;AACrC,UAAM,SAAS,KAAK,KAAK,WAAW;AACpC,oBAAgB,UAAU,KAAK,UAAU;AAAA,MACrC,aAAa,IAAI,YAAY;AAAA,MAC7B,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,aAAa,OAAO;AAAA,MACpB,SAAS,OAAO;AAAA,MAChB,aAAa,OAAO;AAAA,MACpB,YAAY,OAAO;AAAA,IACvB,GAAG,MAAM,CAAC,CAAC;AACX,oBAAgB,QAAQ,2BAA2B,KAAK,MAAM,CAAC;AAC/D,WAAO;AAAA,EACX,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,mCAAoC,IAAc,OAAO,EAAE;AAClF,WAAO;AAAA,EACX;AACJ;AAEA,SAAS,2BAA2B,KAAW,QAA2C;AACtF,QAAM,UAAU,OAAO,QAAQ,SAAS,IAClC,OAAO,QAAQ,IAAI,YAAU,KAAK,OAAO,KAAK,YAAY,CAAC,MAAM,OAAO,KAAK,KAAK,OAAO,IAAI,EAAE,EAAE,KAAK,IAAI,IAC1G;AAEN,SAAO;AAAA;AAAA,eAEI,IAAI,YAAY,CAAC;AAAA,eACjB,OAAO,QAAQ;AAAA,iCACG,OAAO,WAAW;AAAA,cACrC,OAAO,UAAU;AAAA,aAClB,OAAO,OAAO;AAAA;AAAA;AAAA,aAGd,OAAO,YAAY,OAAO;AAAA,kBACrB,OAAO,YAAY,WAAW;AAAA,cAClC,OAAO,YAAY,QAAQ;AAAA,iBACxB,OAAO,YAAY,WAAW;AAAA;AAAA;AAAA,EAG7C,OAAO;AAAA;AAET;AAEA,SAAS,cAAc,SAAyB,QAA2C;AACvF,QAAM,UAA2B,CAAC;AAClC,QAAM,YAAY,qBAAqB,QAAQ,YAAY;AAC3D,QAAM,eAAe,OAAO,OAAO,OAAK,qBAAqB,EAAE,YAAY,MAAM,SAAS;AAC1F,QAAM,YAAY,aAAa,OAAO,OAAK,EAAE,OAAO;AACpD,QAAM,WAAW,aAAa,OAAO,OAAK,CAAC,EAAE,WAAW,yBAAyB,CAAC,CAAC;AAEnF,MAAI,UAAU,UAAU,0BAA0B;AAC9C,YAAQ,KAAK;AAAA,MACT,MAAM;AAAA,MACN,OAAO,QAAQ;AAAA,MACf,MAAM,OAAO,QAAQ,QAAQ,6BAA6B,SAAS,qBAAqB,UAAU,MAAM;AAAA,IAC5G,CAAC;AAAA,EACL;AAEA,MAAI,SAAS,UAAU,GAAG;AACtB,UAAM,aAAa,sBAAsB,QAAQ;AACjD,YAAQ,KAAK;AAAA,MACT,MAAM;AAAA,MACN,OAAO,QAAQ;AAAA,MACf,MAAM,OAAO,QAAQ,QAAQ,6CAA6C,SAAS,QAAQ,aAAa,WAAW,UAAU,KAAK,EAAE;AAAA,IACxI,CAAC;AAAA,EACL;AAEA,QAAM,gBAAgB,OAAO,KAAK,OAAK,CAAC,EAAE,WAAW,EAAE,YAAY,QAAQ,SAAS;AACpF,MAAI,QAAQ,WAAW,iBAAiB,cAAc,qBAAqB,cAAc,YAAY,GAAG;AACpG,YAAQ,KAAK;AAAA,MACT,MAAM;AAAA,MACN,OAAO,QAAQ;AAAA,MACf,MAAM,UAAU,QAAQ,QAAQ,0CAA0C,SAAS;AAAA,IACvF,CAAC;AAAA,EACL;AAEA,SAAO;AACX;AAEO,SAAS,+BAA+B,YAAkD;AAC7F,QAAM,QAAQ,yBAAyB;AACvC,QAAM,MAAM,WAAW,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC3D,QAAM,YAAY,qBAAqB,WAAW,YAAY;AAC9D,QAAM,MAAM,WAAW,WAAW,UAAU,SAAS;AACrD,QAAM,WAAW,MAAM,SAAS,GAAG;AACnC,QAAM,mBAAmB,CAAC,WAAW,WAAW,CAAC,yBAAyB,UAAU;AACpF,QAAM,cAAc,CAAC,WAAW,WAAW,CAAC,mBAAmB,sBAAsB,CAAC,UAAU,CAAC,IAAI;AAErG,QAAM,UAAgC;AAAA,IAClC,UAAU,WAAW;AAAA,IACrB;AAAA,IACA,cAAc,WAAW;AAAA,IACzB,WAAW,UAAU,aAAa;AAAA,IAClC,UAAU,UAAU,YAAY;AAAA,IAChC,mBAAmB,UAAU,qBAAqB;AAAA,IAClD,OAAO,UAAU,SAAS;AAAA,IAC1B,aAAa,UAAU,eAAe;AAAA,IACtC,YAAY;AAAA,IACZ,eAAe,UAAU,iBAAiB;AAAA,IAC1C,eAAe,UAAU,iBAAiB;AAAA,IAC1C,mBAAmB,UAAU,qBAAqB;AAAA,EACtD;AAEA,MAAI,WAAW,SAAS;AACpB,YAAQ,aAAa;AACrB,YAAQ,gBAAgB;AACxB,YAAQ,QAAQ;AAAA,EACpB,WAAW,kBAAkB;AACzB,YAAQ,qBAAqB;AAAA,EACjC,OAAO;AACH,YAAQ,YAAY;AACpB,YAAQ,gBAAgB;AACxB,YAAQ,oBAAoB,eAAe,QAAQ;AACnD,QAAI,QAAQ,YAAY,EAAG,SAAQ,QAAQ;AAAA,EAC/C;AAEA,QAAM,SAAS,GAAG,IAAI;AACtB,2BAAyB,KAAK;AAC9B,SAAO;AACX;AAEO,SAAS,gCACZ,MAAY,oBAAI,KAAK,GACrB,iBAAiB,0BACjB,mBAAmB,4BACY;AAC/B,QAAM,QAAQ,yBAAyB;AACvC,QAAM,SAA0C;AAAA,IAC5C,SAAS;AAAA,IACT,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,EACjB;AACA,QAAM,cAAc,IAAI,QAAQ,IAAI,iBAAiB,KAAK,KAAK,KAAK;AACpE,QAAM,gBAAgB,IAAI,QAAQ,IAAI,mBAAmB,KAAK,KAAK,KAAK;AAExE,aAAW,WAAW,OAAO,OAAO,MAAM,QAAQ,GAAG;AACjD,WAAO,WAAW;AAClB,UAAM,WAAW,KAAK,MAAM,QAAQ,UAAU;AAC9C,QAAI,CAAC,OAAO,SAAS,QAAQ,EAAG;AAEhC,QAAI,YAAY,iBAAiB,QAAQ,UAAU,YAAY;AAC3D,cAAQ,QAAQ;AAChB,aAAO,YAAY;AAAA,IACvB,WAAW,YAAY,eAAe,QAAQ,UAAU,UAAU;AAC9D,cAAQ,QAAQ;AAChB,aAAO,eAAe;AAAA,IAC1B,WAAW,WAAW,eAAe,QAAQ,UAAU,SAAS;AAC5D,cAAQ,QAAQ;AAChB,aAAO,eAAe;AAAA,IAC1B;AAAA,EACJ;AAEA,MAAI,OAAO,cAAc,KAAK,OAAO,WAAW,KAAK,OAAO,cAAc,GAAG;AACzE,6BAAyB,KAAK;AAAA,EAClC;AACA,SAAO;AACX;AAEO,SAAS,8BAA8B,UAAkB,QAAQ,GAAkB;AACtF,QAAM,QAAQ,yBAAyB;AACvC,QAAM,WAAW,OAAO,OAAO,MAAM,QAAQ,EACxC,OAAO,aAAW,QAAQ,aAAa,YAAY,QAAQ,UAAU,UAAU,EAC/E,KAAK,CAAC,GAAG,MAAM,iBAAiB,CAAC,IAAI,iBAAiB,CAAC,CAAC,EACxD,MAAM,GAAG,KAAK;AAEnB,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAM,QAAQ,CAAC,8DAA8D;AAC7E,aAAW,WAAW,UAAU;AAC5B,QAAI,QAAQ,UAAU,SAAS;AAC3B,YAAM,gBAAgB,QAAQ,oBAAoB,WAAW,QAAQ,iBAAiB,KAAK;AAC3F,YAAM,KAAK,WAAW,QAAQ,SAAS,KAAK,QAAQ,QAAQ,sBAAsB,aAAa,8DAA8D;AAAA,IACjK,OAAO;AACH,YAAM,KAAK,YAAY,QAAQ,SAAS,KAAK,QAAQ,SAAS,YAAY,QAAQ,QAAQ,uEAAuE;AAAA,IACrK;AAAA,EACJ;AACA,SAAO,MAAM,KAAK,IAAI;AAC1B;AAEA,SAAS,iBAAiB,SAAuC;AAC7D,QAAM,aAAa,QAAQ,UAAU,UAAU,MAAO,QAAQ,UAAU,WAAW,MAAM;AACzF,SAAO,aAAa,QAAQ,YAAY,KAAK,QAAQ;AACzD;AAEA,SAAS,WAAW,UAAkB,WAA2B;AAC7D,SAAO,GAAG,QAAQ,IAAI,SAAS;AACnC;AAEA,SAAS,yBAAyB,YAAqC;AACnE,MAAI,WAAW,QAAS,QAAO;AAC/B,QAAM,OAAO,WAAW,YACnB,IAAI,YAAU,GAAG,OAAO,IAAI;AAAA,EAAK,OAAO,iBAAiB,EAAE,EAAE,EAC7D,KAAK,IAAI,EACT,YAAY;AAEjB,SAAO,CAAC;AAAA,IACJ;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,EAAE,KAAK,YAAU,KAAK,SAAS,MAAM,CAAC;AAC1C;AAEA,SAAS,sBAAsB,UAAoC;AAC/D,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,cAAc,UAAU;AAC/B,eAAW,UAAU,WAAW,aAAa;AACzC,UAAI,CAAC,OAAO,QAAS,QAAO,IAAI,OAAO,OAAO,OAAO,IAAI,OAAO,IAAI,KAAK,KAAK,CAAC;AAAA,IACnF;AAAA,EACJ;AACA,SAAO,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK;AACxE;AAEO,SAAS,mCAAmC,YAAkC;AACjF,MAAI;AAGA,UAAM,MAAM,kBAAkB;AAC9B,QAAI,CAAC,IAAI,QAAS;AAClB,mCAA+B,UAAU;AACzC,UAAM,SAAS,kBAAkB,YAAY,oBAAI,KAAK,GAAG,IAAI,eAAe;AAC5E,QAAI,OAAO,UAAW,QAAO,KAAK,WAAW,OAAO,OAAO;AAAA,EAC/D,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,2BAA4B,IAAc,OAAO,EAAE;AAAA,EAC9E;AACJ;","names":["summary","reportPath"]}
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync, statSync } from "fs";
3
3
  import { join } from "path";
4
- import { homedir } from "os";
5
4
  import logger from "../utils/logger.js";
5
+ import { TITAN_HOME } from "../utils/constants.js";
6
6
  const COMPONENT = "TrajectoryLogger";
7
- const TRAJECTORIES_DIR = join(homedir(), ".titan", "trajectories");
7
+ const TRAJECTORIES_DIR = join(TITAN_HOME, "trajectories");
8
8
  const TASK_TRAJECTORIES_FILE = join(TRAJECTORIES_DIR, "task-trajectories.jsonl");
9
9
  const MAX_FILE_SIZE = 50 * 1024 * 1024;
10
10
  function ensureDir() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/trajectoryLogger.ts"],"sourcesContent":["/**\n * TITAN — Task-Level Trajectory Logger\n *\n * Records task-level trajectories (task, model, tool sequence, success, rounds, duration)\n * to ~/.titan/trajectories/task-trajectories.jsonl for auto-skill generation.\n *\n * Separate from trajectoryCapture.ts which produces ChatML format for LoRA fine-tuning.\n * Inspired by Hermes trajectory.py.\n */\nimport { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync, statSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'TrajectoryLogger';\nconst TRAJECTORIES_DIR = join(homedir(), '.titan', 'trajectories');\nconst TASK_TRAJECTORIES_FILE = join(TRAJECTORIES_DIR, 'task-trajectories.jsonl');\nconst MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB\n\n// ── Types ─────────────────────────────────────────────────────────\nexport interface TaskTrajectory {\n id: string;\n timestamp: string;\n /** First 500 chars of the user message */\n task: string;\n taskType: string;\n model: string;\n /** Ordered tool names as executed */\n toolSequence: string[];\n /** Detailed tool call records */\n toolDetails: Array<{\n name: string;\n args: Record<string, unknown>;\n success: boolean;\n resultSnippet: string;\n }>;\n success: boolean;\n rounds: number;\n durationMs: number;\n sessionId: string;\n}\n\n// ── Ensure directory exists ───────────────────────────────────────\nfunction ensureDir(): void {\n if (!existsSync(TRAJECTORIES_DIR)) mkdirSync(TRAJECTORIES_DIR, { recursive: true });\n}\n\n// ── Auto-rotate ───────────────────────────────────────────────────\nfunction rotateIfNeeded(): void {\n if (!existsSync(TASK_TRAJECTORIES_FILE)) return;\n try {\n const stat = statSync(TASK_TRAJECTORIES_FILE);\n if (stat.size > MAX_FILE_SIZE) {\n logger.warn(COMPONENT, `Task trajectories file exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB — rotating`);\n const rotated = `${TASK_TRAJECTORIES_FILE}.${Date.now()}.bak`;\n try { writeFileSync(rotated, readFileSync(TASK_TRAJECTORIES_FILE)); } catch { /* ignore */ }\n writeFileSync(TASK_TRAJECTORIES_FILE, '');\n }\n } catch { /* ignore stat errors */ }\n}\n\n// ── Public API ────────────────────────────────────────────────────\n\n/**\n * Log a task-level trajectory.\n * Fire-and-forget — errors are logged but never thrown.\n */\nexport function logTrajectory(trajectory: TaskTrajectory): void {\n try {\n ensureDir();\n rotateIfNeeded();\n appendFileSync(TASK_TRAJECTORIES_FILE, JSON.stringify(trajectory) + '\\n', 'utf-8');\n logger.debug(COMPONENT, `Logged trajectory: ${trajectory.taskType}, ${trajectory.toolSequence.length} tools, success=${trajectory.success}`);\n } catch (err) {\n logger.warn(COMPONENT, `Failed to log trajectory: ${(err as Error).message}`);\n }\n}\n\n/**\n * Read recent trajectories, optionally filtered.\n */\nexport function getRecentTrajectories(\n limit: number = 50,\n filter?: { taskType?: string; success?: boolean },\n): TaskTrajectory[] {\n if (!existsSync(TASK_TRAJECTORIES_FILE)) return [];\n\n try {\n const content = readFileSync(TASK_TRAJECTORIES_FILE, 'utf-8');\n const lines = content.split('\\n').filter(l => l.trim());\n let trajectories: TaskTrajectory[] = [];\n\n // Read from the end for efficiency\n for (let i = lines.length - 1; i >= 0 && trajectories.length < limit * 2; i--) {\n try {\n trajectories.push(JSON.parse(lines[i]));\n } catch { /* skip malformed lines */ }\n }\n\n // Apply filters\n if (filter?.taskType) {\n trajectories = trajectories.filter(t => t.taskType === filter.taskType);\n }\n if (filter?.success !== undefined) {\n trajectories = trajectories.filter(t => t.success === filter.success);\n }\n\n return trajectories.slice(0, limit);\n } catch {\n return [];\n }\n}\n\n/**\n * Get the tool sequence signature for matching (joined with →).\n */\nexport function getSequenceSignature(toolSequence: string[]): string {\n return toolSequence.join(' → ');\n}\n\n/**\n * Count how many successful trajectories match a given task type and tool sequence.\n */\nexport function countMatchingTrajectories(taskType: string, toolSequence: string[]): number {\n const recent = getRecentTrajectories(200, { taskType, success: true });\n const targetSig = getSequenceSignature(toolSequence);\n return recent.filter(t => getSequenceSignature(t.toolSequence) === targetSig).length;\n}\n"],"mappings":";AASA,SAAS,YAAY,cAAc,eAAe,gBAAgB,WAAW,gBAAgB;AAC7F,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,OAAO,YAAY;AAEnB,MAAM,YAAY;AAClB,MAAM,mBAAmB,KAAK,QAAQ,GAAG,UAAU,cAAc;AACjE,MAAM,yBAAyB,KAAK,kBAAkB,yBAAyB;AAC/E,MAAM,gBAAgB,KAAK,OAAO;AA0BlC,SAAS,YAAkB;AACvB,MAAI,CAAC,WAAW,gBAAgB,EAAG,WAAU,kBAAkB,EAAE,WAAW,KAAK,CAAC;AACtF;AAGA,SAAS,iBAAuB;AAC5B,MAAI,CAAC,WAAW,sBAAsB,EAAG;AACzC,MAAI;AACA,UAAM,OAAO,SAAS,sBAAsB;AAC5C,QAAI,KAAK,OAAO,eAAe;AAC3B,aAAO,KAAK,WAAW,kCAAkC,gBAAgB,OAAO,IAAI,oBAAe;AACnG,YAAM,UAAU,GAAG,sBAAsB,IAAI,KAAK,IAAI,CAAC;AACvD,UAAI;AAAE,sBAAc,SAAS,aAAa,sBAAsB,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAe;AAC3F,oBAAc,wBAAwB,EAAE;AAAA,IAC5C;AAAA,EACJ,QAAQ;AAAA,EAA2B;AACvC;AAQO,SAAS,cAAc,YAAkC;AAC5D,MAAI;AACA,cAAU;AACV,mBAAe;AACf,mBAAe,wBAAwB,KAAK,UAAU,UAAU,IAAI,MAAM,OAAO;AACjF,WAAO,MAAM,WAAW,sBAAsB,WAAW,QAAQ,KAAK,WAAW,aAAa,MAAM,mBAAmB,WAAW,OAAO,EAAE;AAAA,EAC/I,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,6BAA8B,IAAc,OAAO,EAAE;AAAA,EAChF;AACJ;AAKO,SAAS,sBACZ,QAAgB,IAChB,QACgB;AAChB,MAAI,CAAC,WAAW,sBAAsB,EAAG,QAAO,CAAC;AAEjD,MAAI;AACA,UAAM,UAAU,aAAa,wBAAwB,OAAO;AAC5D,UAAM,QAAQ,QAAQ,MAAM,IAAI,EAAE,OAAO,OAAK,EAAE,KAAK,CAAC;AACtD,QAAI,eAAiC,CAAC;AAGtC,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,KAAK,aAAa,SAAS,QAAQ,GAAG,KAAK;AAC3E,UAAI;AACA,qBAAa,KAAK,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC;AAAA,MAC1C,QAAQ;AAAA,MAA6B;AAAA,IACzC;AAGA,QAAI,QAAQ,UAAU;AAClB,qBAAe,aAAa,OAAO,OAAK,EAAE,aAAa,OAAO,QAAQ;AAAA,IAC1E;AACA,QAAI,QAAQ,YAAY,QAAW;AAC/B,qBAAe,aAAa,OAAO,OAAK,EAAE,YAAY,OAAO,OAAO;AAAA,IACxE;AAEA,WAAO,aAAa,MAAM,GAAG,KAAK;AAAA,EACtC,QAAQ;AACJ,WAAO,CAAC;AAAA,EACZ;AACJ;AAKO,SAAS,qBAAqB,cAAgC;AACjE,SAAO,aAAa,KAAK,UAAK;AAClC;AAKO,SAAS,0BAA0B,UAAkB,cAAgC;AACxF,QAAM,SAAS,sBAAsB,KAAK,EAAE,UAAU,SAAS,KAAK,CAAC;AACrE,QAAM,YAAY,qBAAqB,YAAY;AACnD,SAAO,OAAO,OAAO,OAAK,qBAAqB,EAAE,YAAY,MAAM,SAAS,EAAE;AAClF;","names":[]}
1
+ {"version":3,"sources":["../../src/agent/trajectoryLogger.ts"],"sourcesContent":["/**\n * TITAN — Task-Level Trajectory Logger\n *\n * Records task-level trajectories (task, model, tool sequence, success, rounds, duration)\n * to ~/.titan/trajectories/task-trajectories.jsonl for auto-skill generation.\n *\n * Separate from trajectoryCapture.ts which produces ChatML format for LoRA fine-tuning.\n * Inspired by Hermes trajectory.py.\n */\nimport { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync, statSync } from 'fs';\nimport { join } from 'path';\nimport logger from '../utils/logger.js';\nimport { TITAN_HOME } from '../utils/constants.js';\n\nconst COMPONENT = 'TrajectoryLogger';\nconst TRAJECTORIES_DIR = join(TITAN_HOME, 'trajectories');\nconst TASK_TRAJECTORIES_FILE = join(TRAJECTORIES_DIR, 'task-trajectories.jsonl');\nconst MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB\n\n// ── Types ─────────────────────────────────────────────────────────\nexport interface TaskTrajectory {\n id: string;\n timestamp: string;\n /** First 500 chars of the user message */\n task: string;\n taskType: string;\n model: string;\n /** Ordered tool names as executed */\n toolSequence: string[];\n /** Detailed tool call records */\n toolDetails: Array<{\n name: string;\n args: Record<string, unknown>;\n success: boolean;\n resultSnippet: string;\n }>;\n success: boolean;\n rounds: number;\n durationMs: number;\n sessionId: string;\n}\n\n// ── Ensure directory exists ───────────────────────────────────────\nfunction ensureDir(): void {\n if (!existsSync(TRAJECTORIES_DIR)) mkdirSync(TRAJECTORIES_DIR, { recursive: true });\n}\n\n// ── Auto-rotate ───────────────────────────────────────────────────\nfunction rotateIfNeeded(): void {\n if (!existsSync(TASK_TRAJECTORIES_FILE)) return;\n try {\n const stat = statSync(TASK_TRAJECTORIES_FILE);\n if (stat.size > MAX_FILE_SIZE) {\n logger.warn(COMPONENT, `Task trajectories file exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB — rotating`);\n const rotated = `${TASK_TRAJECTORIES_FILE}.${Date.now()}.bak`;\n try { writeFileSync(rotated, readFileSync(TASK_TRAJECTORIES_FILE)); } catch { /* ignore */ }\n writeFileSync(TASK_TRAJECTORIES_FILE, '');\n }\n } catch { /* ignore stat errors */ }\n}\n\n// ── Public API ────────────────────────────────────────────────────\n\n/**\n * Log a task-level trajectory.\n * Fire-and-forget — errors are logged but never thrown.\n */\nexport function logTrajectory(trajectory: TaskTrajectory): void {\n try {\n ensureDir();\n rotateIfNeeded();\n appendFileSync(TASK_TRAJECTORIES_FILE, JSON.stringify(trajectory) + '\\n', 'utf-8');\n logger.debug(COMPONENT, `Logged trajectory: ${trajectory.taskType}, ${trajectory.toolSequence.length} tools, success=${trajectory.success}`);\n } catch (err) {\n logger.warn(COMPONENT, `Failed to log trajectory: ${(err as Error).message}`);\n }\n}\n\n/**\n * Read recent trajectories, optionally filtered.\n */\nexport function getRecentTrajectories(\n limit: number = 50,\n filter?: { taskType?: string; success?: boolean },\n): TaskTrajectory[] {\n if (!existsSync(TASK_TRAJECTORIES_FILE)) return [];\n\n try {\n const content = readFileSync(TASK_TRAJECTORIES_FILE, 'utf-8');\n const lines = content.split('\\n').filter(l => l.trim());\n let trajectories: TaskTrajectory[] = [];\n\n // Read from the end for efficiency\n for (let i = lines.length - 1; i >= 0 && trajectories.length < limit * 2; i--) {\n try {\n trajectories.push(JSON.parse(lines[i]));\n } catch { /* skip malformed lines */ }\n }\n\n // Apply filters\n if (filter?.taskType) {\n trajectories = trajectories.filter(t => t.taskType === filter.taskType);\n }\n if (filter?.success !== undefined) {\n trajectories = trajectories.filter(t => t.success === filter.success);\n }\n\n return trajectories.slice(0, limit);\n } catch {\n return [];\n }\n}\n\n/**\n * Get the tool sequence signature for matching (joined with →).\n */\nexport function getSequenceSignature(toolSequence: string[]): string {\n return toolSequence.join(' → ');\n}\n\n/**\n * Count how many successful trajectories match a given task type and tool sequence.\n */\nexport function countMatchingTrajectories(taskType: string, toolSequence: string[]): number {\n const recent = getRecentTrajectories(200, { taskType, success: true });\n const targetSig = getSequenceSignature(toolSequence);\n return recent.filter(t => getSequenceSignature(t.toolSequence) === targetSig).length;\n}\n"],"mappings":";AASA,SAAS,YAAY,cAAc,eAAe,gBAAgB,WAAW,gBAAgB;AAC7F,SAAS,YAAY;AACrB,OAAO,YAAY;AACnB,SAAS,kBAAkB;AAE3B,MAAM,YAAY;AAClB,MAAM,mBAAmB,KAAK,YAAY,cAAc;AACxD,MAAM,yBAAyB,KAAK,kBAAkB,yBAAyB;AAC/E,MAAM,gBAAgB,KAAK,OAAO;AA0BlC,SAAS,YAAkB;AACvB,MAAI,CAAC,WAAW,gBAAgB,EAAG,WAAU,kBAAkB,EAAE,WAAW,KAAK,CAAC;AACtF;AAGA,SAAS,iBAAuB;AAC5B,MAAI,CAAC,WAAW,sBAAsB,EAAG;AACzC,MAAI;AACA,UAAM,OAAO,SAAS,sBAAsB;AAC5C,QAAI,KAAK,OAAO,eAAe;AAC3B,aAAO,KAAK,WAAW,kCAAkC,gBAAgB,OAAO,IAAI,oBAAe;AACnG,YAAM,UAAU,GAAG,sBAAsB,IAAI,KAAK,IAAI,CAAC;AACvD,UAAI;AAAE,sBAAc,SAAS,aAAa,sBAAsB,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAe;AAC3F,oBAAc,wBAAwB,EAAE;AAAA,IAC5C;AAAA,EACJ,QAAQ;AAAA,EAA2B;AACvC;AAQO,SAAS,cAAc,YAAkC;AAC5D,MAAI;AACA,cAAU;AACV,mBAAe;AACf,mBAAe,wBAAwB,KAAK,UAAU,UAAU,IAAI,MAAM,OAAO;AACjF,WAAO,MAAM,WAAW,sBAAsB,WAAW,QAAQ,KAAK,WAAW,aAAa,MAAM,mBAAmB,WAAW,OAAO,EAAE;AAAA,EAC/I,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,6BAA8B,IAAc,OAAO,EAAE;AAAA,EAChF;AACJ;AAKO,SAAS,sBACZ,QAAgB,IAChB,QACgB;AAChB,MAAI,CAAC,WAAW,sBAAsB,EAAG,QAAO,CAAC;AAEjD,MAAI;AACA,UAAM,UAAU,aAAa,wBAAwB,OAAO;AAC5D,UAAM,QAAQ,QAAQ,MAAM,IAAI,EAAE,OAAO,OAAK,EAAE,KAAK,CAAC;AACtD,QAAI,eAAiC,CAAC;AAGtC,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,KAAK,aAAa,SAAS,QAAQ,GAAG,KAAK;AAC3E,UAAI;AACA,qBAAa,KAAK,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC;AAAA,MAC1C,QAAQ;AAAA,MAA6B;AAAA,IACzC;AAGA,QAAI,QAAQ,UAAU;AAClB,qBAAe,aAAa,OAAO,OAAK,EAAE,aAAa,OAAO,QAAQ;AAAA,IAC1E;AACA,QAAI,QAAQ,YAAY,QAAW;AAC/B,qBAAe,aAAa,OAAO,OAAK,EAAE,YAAY,OAAO,OAAO;AAAA,IACxE;AAEA,WAAO,aAAa,MAAM,GAAG,KAAK;AAAA,EACtC,QAAQ;AACJ,WAAO,CAAC;AAAA,EACZ;AACJ;AAKO,SAAS,qBAAqB,cAAgC;AACjE,SAAO,aAAa,KAAK,UAAK;AAClC;AAKO,SAAS,0BAA0B,UAAkB,cAAgC;AACxF,QAAM,SAAS,sBAAsB,KAAK,EAAE,UAAU,SAAS,KAAK,CAAC;AACrE,QAAM,YAAY,qBAAqB,YAAY;AACnD,SAAO,OAAO,OAAO,OAAK,qBAAqB,EAAE,YAAY,MAAM,SAAS,EAAE;AAClF;","names":[]}
@@ -1366,6 +1366,28 @@ const TitanConfigSchema = z.object({
1366
1366
  gratitude: z.number().min(0).max(1).default(0.05)
1367
1367
  }).default({})
1368
1368
  }).default({}),
1369
+ /**
1370
+ * Learning Curator (v6.4.0+) — background review pass that turns
1371
+ * repeated successful tool sequences into durable "win" lessons and
1372
+ * repeated structural failures into "watch" warnings. Lessons are
1373
+ * surfaced in the agent's system prompt at task start; reports land
1374
+ * at ~/.titan/logs/learning-curator/<stamp>/.
1375
+ *
1376
+ * Defaults are conservative: 24h between review runs, requires 3
1377
+ * trajectories of a given task type before extracting lessons,
1378
+ * promotes patterns to "stale" after 30 days idle and archives them
1379
+ * after 90. Set `enabled: false` to skip the curator entirely.
1380
+ *
1381
+ * Implementation lives in src/agent/learningCurator.ts (MIT-licensed
1382
+ * port of the NousResearch/hermes-agent background-review pattern).
1383
+ */
1384
+ learningCurator: z.object({
1385
+ enabled: z.boolean().default(true),
1386
+ intervalHours: z.number().min(1).max(720).default(24),
1387
+ minTrajectories: z.number().min(2).max(50).default(3),
1388
+ staleAfterDays: z.number().min(1).max(365).default(30),
1389
+ archiveAfterDays: z.number().min(1).max(730).default(90)
1390
+ }).default({}),
1369
1391
  /**
1370
1392
  * Persona Profiles (v5.5.24+ — Dad Mode plumbing) — TITAN can run as
1371
1393
  * different personas based on time-of-day, channel, or caller. The