titan-agent 6.5.2 → 7.0.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.
- package/README.md +180 -53
- package/assets/agent-skills/NOTICE-ECC.md +39 -0
- package/assets/agent-skills/agent-introspection-debugging/SKILL.md +153 -0
- package/assets/agent-skills/continuous-agent-loop/SKILL.md +45 -0
- package/assets/agent-skills/continuous-learning/SKILL.md +131 -0
- package/assets/agent-skills/continuous-learning/config.json +18 -0
- package/assets/agent-skills/continuous-learning/evaluate-session.sh +69 -0
- package/assets/agent-skills/production-audit/SKILL.md +206 -0
- package/assets/agent-skills/recursive-decision-ledger/SKILL.md +79 -0
- package/assets/agent-skills/scientific-thinking-literature-review/SKILL.md +192 -0
- package/assets/agent-skills/scientific-thinking-scholar-evaluation/SKILL.md +160 -0
- package/assets/agent-skills/verification-loop/SKILL.md +126 -0
- package/dist/agent/agent.js +67 -26
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/agentLoop.js +6 -2
- package/dist/agent/agentLoop.js.map +1 -1
- package/dist/agent/autoModeClassifier.js +26 -2
- package/dist/agent/autoModeClassifier.js.map +1 -1
- package/dist/agent/goalDriver.js +2 -0
- package/dist/agent/goalDriver.js.map +1 -1
- package/dist/agent/goalSpec.js +229 -0
- package/dist/agent/goalSpec.js.map +1 -0
- package/dist/agent/heartbeat.js +130 -0
- package/dist/agent/heartbeat.js.map +1 -0
- package/dist/agent/missionDecompose.js +38 -0
- package/dist/agent/missionDecompose.js.map +1 -0
- package/dist/agent/muscleMemory.js +291 -0
- package/dist/agent/muscleMemory.js.map +1 -0
- package/dist/agent/observationWriter.js +56 -0
- package/dist/agent/observationWriter.js.map +1 -0
- package/dist/agent/promptSectionCaps.js +6 -0
- package/dist/agent/promptSectionCaps.js.map +1 -1
- package/dist/agent/reminders.js +101 -0
- package/dist/agent/reminders.js.map +1 -0
- package/dist/agent/systemWidgets.js +41 -0
- package/dist/agent/systemWidgets.js.map +1 -0
- package/dist/agent/toolRunner.js +24 -0
- package/dist/agent/toolRunner.js.map +1 -1
- package/dist/agent/toolSearch.js +3 -0
- package/dist/agent/toolSearch.js.map +1 -1
- package/dist/channels/messenger.js +39 -0
- package/dist/channels/messenger.js.map +1 -1
- package/dist/config/config.js +7 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/schema.js +18 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/eval/harness.js +13 -3
- package/dist/eval/harness.js.map +1 -1
- package/dist/gateway/onboarding.js +91 -0
- package/dist/gateway/onboarding.js.map +1 -0
- package/dist/gateway/routes/skills.js +2 -1
- package/dist/gateway/routes/skills.js.map +1 -1
- package/dist/gateway/routes/studioRouter.js +88 -0
- package/dist/gateway/routes/studioRouter.js.map +1 -0
- package/dist/gateway/routes/watchRouter.js +4 -4
- package/dist/gateway/routes/watchRouter.js.map +1 -1
- package/dist/gateway/server.js +251 -43
- package/dist/gateway/server.js.map +1 -1
- package/dist/memory/taxonomy.js +117 -0
- package/dist/memory/taxonomy.js.map +1 -0
- package/dist/providers/modelCapabilities.js +13 -0
- package/dist/providers/modelCapabilities.js.map +1 -1
- package/dist/providers/ollama.js +8 -2
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +1 -1
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openai_compat.js +38 -2
- package/dist/providers/openai_compat.js.map +1 -1
- package/dist/providers/router.js +14 -0
- package/dist/providers/router.js.map +1 -1
- package/dist/recipes/runner.js +8 -1
- package/dist/recipes/runner.js.map +1 -1
- package/dist/recipes/store.js +2 -1
- package/dist/recipes/store.js.map +1 -1
- package/dist/skills/builtin/codebase_explore.js +214 -0
- package/dist/skills/builtin/codebase_explore.js.map +1 -0
- package/dist/skills/builtin/config_audit.js +160 -0
- package/dist/skills/builtin/config_audit.js.map +1 -0
- package/dist/skills/builtin/delegate_agent.js +107 -0
- package/dist/skills/builtin/delegate_agent.js.map +1 -0
- package/dist/skills/builtin/memory_map.js +38 -0
- package/dist/skills/builtin/memory_map.js.map +1 -0
- package/dist/skills/builtin/reminders.js +67 -0
- package/dist/skills/builtin/reminders.js.map +1 -0
- package/dist/skills/builtin/spec.js +139 -0
- package/dist/skills/builtin/spec.js.map +1 -0
- package/dist/skills/builtin/widget_gallery.js +12 -5
- package/dist/skills/builtin/widget_gallery.js.map +1 -1
- package/dist/skills/frontmatterLoader.js +7 -1
- package/dist/skills/frontmatterLoader.js.map +1 -1
- package/dist/skills/registry.js +12 -0
- package/dist/skills/registry.js.map +1 -1
- package/dist/substrate/traceBus.js.map +1 -1
- package/dist/telemetry/traceStore.js +127 -0
- package/dist/telemetry/traceStore.js.map +1 -0
- package/dist/utils/constants.js +1 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/contrast.js +69 -0
- package/dist/utils/contrast.js.map +1 -0
- package/dist/watch/sessionContext.js +14 -0
- package/dist/watch/sessionContext.js.map +1 -0
- package/dist/watch/sessionTrace.js +51 -0
- package/dist/watch/sessionTrace.js.map +1 -0
- package/dist/watch/studioBuffer.js +53 -0
- package/dist/watch/studioBuffer.js.map +1 -0
- package/package.json +4 -3
- package/ui/dist/assets/AboutYouMemory-CZKEOd_O.js +11 -0
- package/ui/dist/assets/AuditPanel-Bu71N5Er.js +1 -0
- package/ui/dist/assets/{AutonomyPanel-CmaXJV8d.js → AutonomyPanel-DB-39dX4.js} +2 -2
- package/ui/dist/assets/{AutopilotPanel-CbohPtXW.js → AutopilotPanel-Cj8oKxWv.js} +1 -1
- package/ui/dist/assets/{AutoresearchPanel-DCwiQ2v2.js → AutoresearchPanel-3s8pjm6Z.js} +2 -2
- package/ui/dist/assets/BackupPanel-EUQJMJE7.js +1 -0
- package/ui/dist/assets/{BrowserPanel-D6UaSxap.js → BrowserPanel-Beo4F5Ar.js} +2 -2
- package/ui/dist/assets/Button-MdXmb4_n.js +1 -0
- package/ui/dist/assets/CPActivity-B1Se9VVc.js +1 -0
- package/ui/dist/assets/CPAgentDetail-BYji6tT8.js +1 -0
- package/ui/dist/assets/CPAgents-BSUKucTe.js +1 -0
- package/ui/dist/assets/CPApprovals-B4v0em6e.js +1 -0
- package/ui/dist/assets/CPCosts-9ikh61Kv.js +1 -0
- package/ui/dist/assets/CPDashboard-sOok2T5v.js +1 -0
- package/ui/dist/assets/{CPFiles-BO73EZwJ.js → CPFiles-DB2nY5JE.js} +1 -1
- package/ui/dist/assets/CPGoals-8Z1yOY8e.js +1 -0
- package/ui/dist/assets/CPInbox-Cq6-UY8F.js +11 -0
- package/ui/dist/assets/CPIssueDetail-NAGfmJg8.js +1 -0
- package/ui/dist/assets/CPIssues-BzX6Rckx.js +1 -0
- package/ui/dist/assets/CPLayout-BcRM6QSg.js +2 -0
- package/ui/dist/assets/CPOrg-B7DQ4UQP.js +1 -0
- package/ui/dist/assets/CPRuns-D-cpPw_M.js +1 -0
- package/ui/dist/assets/{CPSocial-Qw5rKy_4.js → CPSocial-DshtmcK0.js} +3 -3
- package/ui/dist/assets/CPVoice-DfXRnQVU.js +1 -0
- package/ui/dist/assets/{ChannelsPanel-DS21E-Iz.js → ChannelsPanel-BnaTOeyf.js} +1 -1
- package/ui/dist/assets/CheckpointsPanel-C62naE4U.js +1 -0
- package/ui/dist/assets/CommandPostHub-B_L1CAss.js +24 -0
- package/ui/dist/assets/ConfirmDialog-VErHWAbo.js +1 -0
- package/ui/dist/assets/CronPanel-BwMrrZ5d.js +1 -0
- package/ui/dist/assets/{DataTable-PnhPeDBG.js → DataTable-BCqSUuN7.js} +1 -1
- package/ui/dist/assets/{DreamPanel-DIrtlk2K.js → DreamPanel-BNAcCLBB.js} +2 -2
- package/ui/dist/assets/EmptyState-D9kQf1PX.js +1 -0
- package/ui/dist/assets/{EvalHarnessPanel-uYb_ZEQG.js → EvalHarnessPanel-BaMLdBNm.js} +2 -2
- package/ui/dist/assets/EvalPanel-CW-SSjtg.js +1 -0
- package/ui/dist/assets/{FileViewer-CVO3TRs3.js → FileViewer-BL00nsd7.js} +1 -1
- package/ui/dist/assets/FilesPanel-CeC6YUSd.js +16 -0
- package/ui/dist/assets/FleetPanel-EyWF7l3W.js +1 -0
- package/ui/dist/assets/{HomelabPanel-BZvN4LPM.js → HomelabPanel-BRfKRYe7.js} +2 -2
- package/ui/dist/assets/InfraView-Jmqhg_Yc.js +2 -0
- package/ui/dist/assets/{InlineEditableField-mBzpbcLB.js → InlineEditableField-DgMrLDj1.js} +1 -1
- package/ui/dist/assets/Input-CBTwa_Iw.js +1 -0
- package/ui/dist/assets/IntegrationsPanel-DQL-7Ec6.js +1 -0
- package/ui/dist/assets/IntelligenceView-DxD78qfP.js +2 -0
- package/ui/dist/assets/LearningPanel-C_JzSbJU.js +1 -0
- package/ui/dist/assets/LiveStudio-Db5yeaWo.js +7 -0
- package/ui/dist/assets/{LogsPanel-DbP3Mwnr.js → LogsPanel-CpinKwaZ.js} +1 -1
- package/ui/dist/assets/McpPanel-D-MqN_f-.js +1 -0
- package/ui/dist/assets/MemoryGraphPanel-BnD5jRqf.js +1 -0
- package/ui/dist/assets/MemoryTaxonomyPanel-BBI202dd.js +6 -0
- package/ui/dist/assets/MemoryWikiPanel-Dn3XZHL0.js +6 -0
- package/ui/dist/assets/{MeshPanel-Bf-4brXL.js → MeshPanel-C-mRJpr0.js} +1 -1
- package/ui/dist/assets/{MissionCanvas-CJyhnauG.js → MissionCanvas-CbZW83Ch.js} +1 -1
- package/ui/dist/assets/{MissionChat-C2Nto3zM.js → MissionChat-Bs75jPAD.js} +1 -1
- package/ui/dist/assets/{MissionLibrary-RXyBHsLL.js → MissionLibrary-BxQ6SxPe.js} +1 -1
- package/ui/dist/assets/MissionStart-CBuBPasl.js +1 -0
- package/ui/dist/assets/Modal-ybPGvlQs.js +9 -0
- package/ui/dist/assets/{NvidiaPanel-4DlcQlyl.js → NvidiaPanel-DdG13OX4.js} +1 -1
- package/ui/dist/assets/OrganismPanel-xZWUJKyr.js +1 -0
- package/ui/dist/assets/OverviewPanel-B45ZYhJV.js +1 -0
- package/ui/dist/assets/{PageHeader-bnc85wQN.js → PageHeader-DdrbS1aq.js} +1 -1
- package/ui/dist/assets/PersonaProfilesPanel-CKER-EU5.js +8 -0
- package/ui/dist/assets/{PersonasPanel-dR8VppcD.js → PersonasPanel-CMMQ3nh3.js} +1 -1
- package/ui/dist/assets/{PhoneDeskPanel-CdVOY7H9.js → PhoneDeskPanel-Bw_Y-cVE.js} +2 -2
- package/ui/dist/assets/RecipesPanel-DzMcxpJJ.js +1 -0
- package/ui/dist/assets/SecurityPanel-BCq3Fzbr.js +1 -0
- package/ui/dist/assets/SelfImprovePanel-YE3hT8jK.js +1 -0
- package/ui/dist/assets/SelfProposalsPanel-DXuqBM6p.js +1 -0
- package/ui/dist/assets/SessionsPanel-DORz5goR.js +1 -0
- package/ui/dist/assets/SessionsTab-qYHDOuC8.js +1 -0
- package/ui/dist/assets/SettingsPanel-BbwPu1i7.js +1 -0
- package/ui/dist/assets/SettingsView-zgqkDDBJ.js +2 -0
- package/ui/dist/assets/{SkeletonLoader-Cax7YTqW.js → SkeletonLoader-r6OGXaU9.js} +1 -1
- package/ui/dist/assets/SkillsPanel-BjEOBMRo.js +11 -0
- package/ui/dist/assets/{SomaView-eG3wI4S1.js → SomaView-DLzApZVW.js} +2 -2
- package/ui/dist/assets/{StatCard-Djr487DV.js → StatCard-DLq8aBps.js} +1 -1
- package/ui/dist/assets/{StatusBadge-JpCoA33_.js → StatusBadge-BqlvIbdj.js} +1 -1
- package/ui/dist/assets/{Tabs-DJHu-roP.js → Tabs-DdUThlp1.js} +1 -1
- package/ui/dist/assets/TeamsPanel-uIIKoU7I.js +1 -0
- package/ui/dist/assets/{TelemetryPanel-D4PXsd-X.js → TelemetryPanel-dVr3yQQr.js} +1 -1
- package/ui/dist/assets/TitanCanvas-xHAJqwG2.js +945 -0
- package/ui/dist/assets/ToolsView-CG1mZdiy.js +2 -0
- package/ui/dist/assets/{Tooltip-B1LhTXJ8.js → Tooltip-C2tXJcrN.js} +1 -1
- package/ui/dist/assets/TraceViewer-BIMD_Osr.js +1 -0
- package/ui/dist/assets/TracesPanel-nMUiyMj5.js +1 -0
- package/ui/dist/assets/TrainingPanel-CT5R4HWm.js +1 -0
- package/ui/dist/assets/{VoiceOverlay-B_ss7Zg_.js → VoiceOverlay-Br4QnCtw.js} +3 -3
- package/ui/dist/assets/VramPanel-CdnJYytg.js +1 -0
- package/ui/dist/assets/WatchView-TNU6Ns1Q.js +13 -0
- package/ui/dist/assets/WorkTab-wTlX_UKP.js +1 -0
- package/ui/dist/assets/WorkflowBuilderPanel-CDpVt5ux.js +6 -0
- package/ui/dist/assets/WorkflowsPanel-zsFqsLdn.js +11 -0
- package/ui/dist/assets/{archive-gZFnGd5A.js → archive-Bh689DNg.js} +1 -1
- package/ui/dist/assets/{arrow-left-qA0Qer2h.js → arrow-left-WkVYLm80.js} +1 -1
- package/ui/dist/assets/{book-open-DIxdPqmD.js → book-open-ohEXgUsS.js} +1 -1
- package/ui/dist/assets/{bot-CvIF1uHi.js → bot-DvEcyQcD.js} +1 -1
- package/ui/dist/assets/{chart-column-BQiM-Td4.js → chart-column-w3goFd5f.js} +1 -1
- package/ui/dist/assets/{check-DgyDEFtq.js → check-CuRYh4Zh.js} +1 -1
- package/ui/dist/assets/{chevron-right-CQ1NTOtd.js → chevron-right-DbLSHmff.js} +1 -1
- package/ui/dist/assets/{circle-check-big-CAFcaPPH.js → circle-check-big-Bkba568b.js} +1 -1
- package/ui/dist/assets/{circle-stop-C-8xGv0n.js → circle-stop-Bx2ohEg8.js} +1 -1
- package/ui/dist/assets/{clipboard-list-9DdqBj1F.js → clipboard-list-Dj9tORnu.js} +1 -1
- package/ui/dist/assets/{clock-CF9BCIpL.js → clock-JyQAYy6X.js} +1 -1
- package/ui/dist/assets/{dollar-sign-LDRZBae6.js → dollar-sign-XxwPAA2a.js} +1 -1
- package/ui/dist/assets/{download-BBNXvBO0.js → download-D1izVEOL.js} +1 -1
- package/ui/dist/assets/{external-link-D_ji4Bec.js → external-link-DZ_rZsIt.js} +1 -1
- package/ui/dist/assets/{file-text-DrdTDEYp.js → file-text-Cfawu8Bs.js} +1 -1
- package/ui/dist/assets/{flask-conical-BGn5PgFX.js → flask-conical-Ql8QxhgY.js} +1 -1
- package/ui/dist/assets/{folder-BeZZUGnK.js → folder-DAdIw-Pb.js} +1 -1
- package/ui/dist/assets/{funnel-CAJgGO5M.js → funnel-FycfZcXo.js} +1 -1
- package/ui/dist/assets/{git-branch-BcXabeOs.js → git-branch-C2qrhGGu.js} +1 -1
- package/ui/dist/assets/index-Cml2x9pA.js +507 -0
- package/ui/dist/assets/index-yWybhcfI.css +1 -0
- package/ui/dist/assets/{layers-DxYDmPA_.js → layers-yzjtGlHB.js} +1 -1
- package/ui/dist/assets/{legacy-dHWI7k8P.js → legacy-D21WSMF1.js} +1 -1
- package/ui/dist/assets/{lightbulb-B0Du4SM4.js → lightbulb-BZJNK69S.js} +1 -1
- package/ui/dist/assets/{link-CUpaumqr.js → link-CnFkBkaY.js} +1 -1
- package/ui/dist/assets/{list-todo-CDDhmPQB.js → list-todo-BlgfQHup.js} +1 -1
- package/ui/dist/assets/{message-square-D5VWlv-Q.js → message-square-CRsG9u6_.js} +1 -1
- package/ui/dist/assets/{mic-Ddcc6Ann.js → mic-B32RHc7Y.js} +1 -1
- package/ui/dist/assets/{missions-2zox16Tc.js → missions-1iw8Z2_0.js} +1 -1
- package/ui/dist/assets/{network-DaJ7ugwX.js → network-C9RXQGrV.js} +1 -1
- package/ui/dist/assets/{pause-BukaK6rs.js → pause-BT1Bc2q3.js} +1 -1
- package/ui/dist/assets/{pencil-D_YwQBeg.js → pencil-BK5IU6o0.js} +1 -1
- package/ui/dist/assets/{play-BjdwtYMk.js → play-Cl1nC_gc.js} +1 -1
- package/ui/dist/assets/{plug-yISDdeW2.js → plug-DvcpOalj.js} +1 -1
- package/ui/dist/assets/{plus-BSjTdepW.js → plus-Dpo1qfrl.js} +1 -1
- package/ui/dist/assets/{radio-B7kX_yWE.js → radio-C5c9IyqX.js} +1 -1
- package/ui/dist/assets/{refresh-cw-CwDpzXUn.js → refresh-cw-C-iZkzuS.js} +1 -1
- package/ui/dist/assets/{rotate-ccw-7SQDTI0_.js → rotate-ccw-qzrB3SNw.js} +1 -1
- package/ui/dist/assets/{save-CjTPAQM0.js → save-DwR9wQVu.js} +1 -1
- package/ui/dist/assets/{search-BP7VlNxX.js → search-D10l0m3-.js} +1 -1
- package/ui/dist/assets/{send-C2XDuRPf.js → send-DtXGsaFW.js} +1 -1
- package/ui/dist/assets/{server-BE8W-AT4.js → server-BzisF3LL.js} +1 -1
- package/ui/dist/assets/settings-Dbwd1_-c.js +6 -0
- package/ui/dist/assets/{shield-check-s7la2J2K.js → shield-check-CXlDJyAW.js} +1 -1
- package/ui/dist/assets/square-BBC-JKzY.js +11 -0
- package/ui/dist/assets/{terminal-B8IK4WsO.js → terminal-DaBIlWEM.js} +1 -1
- package/ui/dist/assets/{test-tube-Xfn9DJeg.js → test-tube-CDATRD5t.js} +1 -1
- package/ui/dist/assets/{toggle-right-CHpzOIlj.js → toggle-right-DTmrC5RD.js} +1 -1
- package/ui/dist/assets/{trash-2-B1C85ePK.js → trash-2-CTeyjxD_.js} +1 -1
- package/ui/dist/assets/{trending-up-DDGHQZY1.js → trending-up-CXPRjRV4.js} +1 -1
- package/ui/dist/assets/{trophy-D0LhJSPA.js → trophy-DbVdXRtU.js} +1 -1
- package/ui/dist/assets/{volume-2-DMaE91b5.js → volume-2-DYDBmm3M.js} +1 -1
- package/ui/dist/assets/{wifi-off-DR9h8HGA.js → wifi-off-BBuIGbiP.js} +1 -1
- package/ui/dist/assets/zoom-out-D47_pRAO.js +16 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/sw.js +1 -1
- package/ui/dist/assets/AuditPanel-BaftX5MP.js +0 -1
- package/ui/dist/assets/BackupPanel-DAy9G7Wt.js +0 -1
- package/ui/dist/assets/CPActivity-BYDsUoMn.js +0 -1
- package/ui/dist/assets/CPAgentDetail-Bb9-GAJY.js +0 -1
- package/ui/dist/assets/CPAgents-BTZuGnki.js +0 -1
- package/ui/dist/assets/CPApprovals-BME2ARkO.js +0 -1
- package/ui/dist/assets/CPCosts-mOXwWq0i.js +0 -1
- package/ui/dist/assets/CPDashboard-CTlKGXXQ.js +0 -1
- package/ui/dist/assets/CPGoals-BNWhNP14.js +0 -1
- package/ui/dist/assets/CPInbox-ybe4r8LW.js +0 -16
- package/ui/dist/assets/CPIssueDetail-DaMMGbjw.js +0 -1
- package/ui/dist/assets/CPIssues-BgpQOISw.js +0 -1
- package/ui/dist/assets/CPLayout-bgErauTx.js +0 -2
- package/ui/dist/assets/CPOrg-C8l5SIWJ.js +0 -8
- package/ui/dist/assets/CPRuns-DB8S7loF.js +0 -1
- package/ui/dist/assets/CPVoice-DfwHPewA.js +0 -1
- package/ui/dist/assets/CheckpointsPanel-9-l_8GdG.js +0 -1
- package/ui/dist/assets/CommandPostHub-z55Yrhj1.js +0 -24
- package/ui/dist/assets/CronPanel-kEZK3gpx.js +0 -1
- package/ui/dist/assets/EmptyState-CfvU7SdB.js +0 -1
- package/ui/dist/assets/EvalPanel-CDG-njyj.js +0 -1
- package/ui/dist/assets/FilesPanel-CaLPog2e.js +0 -16
- package/ui/dist/assets/FleetPanel-BtlaV6pH.js +0 -1
- package/ui/dist/assets/InfraView-C0zEuFrR.js +0 -2
- package/ui/dist/assets/Input-CbrMbLgn.js +0 -1
- package/ui/dist/assets/IntegrationsPanel-B4TyR6jC.js +0 -1
- package/ui/dist/assets/IntelligenceView-DZ4HxYZ3.js +0 -2
- package/ui/dist/assets/LearningPanel-B4RIm_iQ.js +0 -1
- package/ui/dist/assets/McpPanel-BmNUFozc.js +0 -1
- package/ui/dist/assets/MemoryGraphPanel-JPurHV0L.js +0 -16
- package/ui/dist/assets/MemoryWikiPanel-DdvCAZHa.js +0 -6
- package/ui/dist/assets/MissionStart-wUkcEP0T.js +0 -1
- package/ui/dist/assets/Modal-BmS7C6FN.js +0 -1
- package/ui/dist/assets/OrganismPanel-C2LYDPrn.js +0 -1
- package/ui/dist/assets/OverviewPanel-DK8uuq4D.js +0 -1
- package/ui/dist/assets/PersonaProfilesPanel-DGtokXUu.js +0 -8
- package/ui/dist/assets/RecipesPanel-D3Zs2wxr.js +0 -1
- package/ui/dist/assets/SecurityPanel-BMERf796.js +0 -1
- package/ui/dist/assets/SelfImprovePanel-BxqWHXCU.js +0 -1
- package/ui/dist/assets/SelfProposalsPanel-CuC94vdx.js +0 -2
- package/ui/dist/assets/SessionsPanel-CQ8qvzp7.js +0 -1
- package/ui/dist/assets/SessionsTab-B3eHV3jf.js +0 -1
- package/ui/dist/assets/SettingsPanel-Cumls7N2.js +0 -1
- package/ui/dist/assets/SettingsView-BtA67pol.js +0 -2
- package/ui/dist/assets/SkillsPanel-CA415CT8.js +0 -11
- package/ui/dist/assets/TeamsPanel-C_Vu6vnY.js +0 -1
- package/ui/dist/assets/TitanCanvas-TuYZSZO7.js +0 -1117
- package/ui/dist/assets/ToolsView-BvTN6vFn.js +0 -2
- package/ui/dist/assets/TraceViewer-Bh8Jo3Dr.js +0 -1
- package/ui/dist/assets/TrainingPanel-Dmbd1rJ-.js +0 -1
- package/ui/dist/assets/VramPanel-CPICbZ2s.js +0 -1
- package/ui/dist/assets/WatchView-BCgLVfiV.js +0 -13
- package/ui/dist/assets/WorkTab-iptofXO9.js +0 -1
- package/ui/dist/assets/WorkflowsPanel-3RYkFHyf.js +0 -11
- package/ui/dist/assets/chevron-up-UyfdjRp6.js +0 -6
- package/ui/dist/assets/index-CUgepw5Q.js +0 -337
- package/ui/dist/assets/index-Dm6zcSTS.css +0 -1
- package/ui/dist/assets/proxy-CWET5FFX.js +0 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/substrate/traceBus.ts"],"sourcesContent":["/**\n * TITAN — Trace Bus (Soma substrate)\n *\n * Typed wrapper over the existing titanEvents EventEmitter in daemon.ts.\n * Provides a single canonical event stream for the organism layer — drives,\n * hormones, pressure fusion, and shadow rehearsal all subscribe here.\n *\n * Design invariant: this is NOT a parallel event system. It publishes to the\n * SAME underlying titanEvents emitter. That means existing consumers of\n * titanEvents (Command Post activity feed, SSE stream, daemon watchers)\n * continue to work unchanged. The organism just gets a typed facade.\n *\n * Zero cost when no subscribers: `emit()` is O(1) on the EventEmitter.\n * Safe to call from anywhere; safe to no-op during early boot.\n */\nimport { titanEvents } from '../agent/daemon.js';\n\n// ── Topic Contracts ──────────────────────────────────────────────\n\n/** Emitted at the start of processMessage before any LLM work runs. */\nexport interface TurnPreEvent {\n agentId: string;\n sessionId: string;\n channel: string;\n userId: string;\n message: string;\n taskType?: string;\n timestamp: string;\n}\n\n/** Emitted at the end of processMessage once the response is ready. */\nexport interface TurnPostEvent {\n agentId: string;\n sessionId: string;\n channel: string;\n userId: string;\n success: boolean;\n toolsUsed: string[];\n durationMs: number;\n costUsd?: number;\n model: string;\n timestamp: string;\n}\n\n/** Emitted whenever a tool is invoked inside a turn. */\nexport interface ToolCallEvent {\n agentId: string;\n sessionId: string;\n tool: string;\n argsPreview: string;\n timestamp: string;\n}\n\n/** Emitted when a tool returns (success or failure). */\nexport interface ToolResultEvent {\n agentId: string;\n sessionId: string;\n tool: string;\n success: boolean;\n durationMs: number;\n timestamp: string;\n}\n\n/** Emitted every driveTick (default 60s) with the full drive snapshot. */\nexport interface DriveTickEvent {\n timestamp: string;\n drives: Array<{\n id: string;\n label: string;\n satisfaction: number;\n setpoint: number;\n pressure: number;\n inputs?: Record<string, unknown>;\n }>;\n}\n\n/** Emitted when hormone levels change materially (for SSE/UI consumers). */\nexport interface HormoneUpdateEvent {\n timestamp: string;\n dominant: string | null;\n levels: Record<string, number>;\n}\n\n/** Emitted when combined pressure crosses the proposal threshold. */\nexport interface PressureThresholdEvent {\n timestamp: string;\n totalPressure: number;\n threshold: number;\n dominantDrives: string[];\n reason: string;\n}\n\n/** Emitted when Soma files a proposal (after shadow rehearsal). */\nexport interface SomaProposalEvent {\n timestamp: string;\n approvalId: string;\n proposedBy: string;\n title: string;\n /** Optional proposal description (added 2026-04-20 so the Command Post\n * SomaView can render the full card instead of title-only). */\n description?: string;\n /** Optional rationale — same reason as description. */\n rationale?: string;\n dominantDrives: string[];\n shadowVerdict?: {\n reversibilityScore: number;\n estimatedCostUsd: number;\n breakRisks: string[];\n };\n}\n\n/** Full topic → payload map. Extend here; both emit and on are typed from this. */\nexport interface TraceBusTopics {\n 'turn:pre': TurnPreEvent;\n 'turn:post': TurnPostEvent;\n 'tool:call': ToolCallEvent;\n 'tool:result': ToolResultEvent;\n 'drive:tick': DriveTickEvent;\n 'hormone:update': HormoneUpdateEvent;\n 'pressure:threshold': PressureThresholdEvent;\n 'soma:proposal': SomaProposalEvent;\n}\n\nexport type TraceBusTopic = keyof TraceBusTopics;\n\n// ── Publish / Subscribe ──────────────────────────────────────────\n\n/**\n * Emit a typed event on the trace bus. No-op when no subscribers are\n * registered for the topic (EventEmitter semantics). Safe during early boot.\n */\nexport function emit<T extends TraceBusTopic>(topic: T, payload: TraceBusTopics[T]): void {\n try {\n titanEvents.emit(topic, payload);\n } catch {\n /* emitter failures are never fatal to the caller */\n }\n}\n\n/**\n * Subscribe to a typed event. Returns an unsubscribe function. Use in\n * watcher registration, Command Post boot, or any long-lived module.\n */\nexport function on<T extends TraceBusTopic>(\n topic: T,\n handler: (payload: TraceBusTopics[T]) => void,\n): () => void {\n const wrapped = (payload: TraceBusTopics[T]) => {\n try {\n handler(payload);\n } catch {\n /* subscriber errors must not crash the emitter */\n }\n };\n titanEvents.on(topic, wrapped);\n return () => titanEvents.off(topic, wrapped);\n}\n\n/**\n * Subscribe once; auto-unsubscribes after the first event. Useful for tests\n * and one-shot handlers.\n */\nexport function once<T extends TraceBusTopic>(\n topic: T,\n handler: (payload: TraceBusTopics[T]) => void,\n): void {\n const wrapped = (payload: TraceBusTopics[T]) => {\n try {\n handler(payload);\n } catch {\n /* silent */\n }\n };\n titanEvents.once(topic, wrapped);\n}\n\n/** How many subscribers are currently attached to a topic. */\nexport function listenerCount(topic: TraceBusTopic): number {\n return titanEvents.listenerCount(topic);\n}\n"],"mappings":";AAeA,SAAS,mBAAmB;
|
|
1
|
+
{"version":3,"sources":["../../src/substrate/traceBus.ts"],"sourcesContent":["/**\n * TITAN — Trace Bus (Soma substrate)\n *\n * Typed wrapper over the existing titanEvents EventEmitter in daemon.ts.\n * Provides a single canonical event stream for the organism layer — drives,\n * hormones, pressure fusion, and shadow rehearsal all subscribe here.\n *\n * Design invariant: this is NOT a parallel event system. It publishes to the\n * SAME underlying titanEvents emitter. That means existing consumers of\n * titanEvents (Command Post activity feed, SSE stream, daemon watchers)\n * continue to work unchanged. The organism just gets a typed facade.\n *\n * Zero cost when no subscribers: `emit()` is O(1) on the EventEmitter.\n * Safe to call from anywhere; safe to no-op during early boot.\n */\nimport { titanEvents } from '../agent/daemon.js';\n\n// ── Topic Contracts ──────────────────────────────────────────────\n\n/** Emitted at the start of processMessage before any LLM work runs. */\nexport interface TurnPreEvent {\n agentId: string;\n sessionId: string;\n channel: string;\n userId: string;\n message: string;\n taskType?: string;\n timestamp: string;\n}\n\n/** Emitted at the end of processMessage once the response is ready. */\nexport interface TurnPostEvent {\n agentId: string;\n sessionId: string;\n channel: string;\n userId: string;\n success: boolean;\n toolsUsed: string[];\n durationMs: number;\n costUsd?: number;\n model: string;\n timestamp: string;\n}\n\n/** Emitted whenever a tool is invoked inside a turn. */\nexport interface ToolCallEvent {\n agentId: string;\n sessionId: string;\n tool: string;\n argsPreview: string;\n timestamp: string;\n /** Stable id linking this call → its result → diff → checkpoint → verdict.\n * (Live-agents event spine — all optional, additive, back-compatible.) */\n actionId?: string;\n /** Spawning agent, when this runs inside a sub-agent/roster role. */\n parentAgentId?: string;\n /** Synthesized roster role id, when applicable. */\n roleId?: string;\n /** Tool-loop round index within the turn. */\n round?: number;\n}\n\n/** Emitted when a tool returns (success or failure). */\nexport interface ToolResultEvent {\n agentId: string;\n sessionId: string;\n tool: string;\n success: boolean;\n durationMs: number;\n timestamp: string;\n /** Stable id shared with the originating ToolCallEvent (see above). */\n actionId?: string;\n parentAgentId?: string;\n roleId?: string;\n round?: number;\n /** Truncated result text for the live timeline. */\n resultPreview?: string;\n /** Unified diff for file-modifying tools (Live Studio diff stream). */\n diff?: string;\n /** Absolute/relative path the tool wrote, when applicable. */\n filePath?: string;\n /** shadowGit checkpoint id captured before the write (one-click revert). */\n checkpointId?: string;\n /** Grounded run verdict, when the evolution oracle has scored it. */\n verdict?: { score: number; grounded: boolean };\n /** Coarse task classification for routing/evolution aggregation. */\n taskType?: string;\n}\n\n/** Emitted every driveTick (default 60s) with the full drive snapshot. */\nexport interface DriveTickEvent {\n timestamp: string;\n drives: Array<{\n id: string;\n label: string;\n satisfaction: number;\n setpoint: number;\n pressure: number;\n inputs?: Record<string, unknown>;\n }>;\n}\n\n/** Emitted when hormone levels change materially (for SSE/UI consumers). */\nexport interface HormoneUpdateEvent {\n timestamp: string;\n dominant: string | null;\n levels: Record<string, number>;\n}\n\n/** Emitted when combined pressure crosses the proposal threshold. */\nexport interface PressureThresholdEvent {\n timestamp: string;\n totalPressure: number;\n threshold: number;\n dominantDrives: string[];\n reason: string;\n}\n\n/** Emitted when Soma files a proposal (after shadow rehearsal). */\nexport interface SomaProposalEvent {\n timestamp: string;\n approvalId: string;\n proposedBy: string;\n title: string;\n /** Optional proposal description (added 2026-04-20 so the Command Post\n * SomaView can render the full card instead of title-only). */\n description?: string;\n /** Optional rationale — same reason as description. */\n rationale?: string;\n dominantDrives: string[];\n shadowVerdict?: {\n reversibilityScore: number;\n estimatedCostUsd: number;\n breakRisks: string[];\n };\n}\n\n/** Full topic → payload map. Extend here; both emit and on are typed from this. */\nexport interface TraceBusTopics {\n 'turn:pre': TurnPreEvent;\n 'turn:post': TurnPostEvent;\n 'tool:call': ToolCallEvent;\n 'tool:result': ToolResultEvent;\n 'drive:tick': DriveTickEvent;\n 'hormone:update': HormoneUpdateEvent;\n 'pressure:threshold': PressureThresholdEvent;\n 'soma:proposal': SomaProposalEvent;\n}\n\nexport type TraceBusTopic = keyof TraceBusTopics;\n\n// ── Publish / Subscribe ──────────────────────────────────────────\n\n/**\n * Emit a typed event on the trace bus. No-op when no subscribers are\n * registered for the topic (EventEmitter semantics). Safe during early boot.\n */\nexport function emit<T extends TraceBusTopic>(topic: T, payload: TraceBusTopics[T]): void {\n try {\n titanEvents.emit(topic, payload);\n } catch {\n /* emitter failures are never fatal to the caller */\n }\n}\n\n/**\n * Subscribe to a typed event. Returns an unsubscribe function. Use in\n * watcher registration, Command Post boot, or any long-lived module.\n */\nexport function on<T extends TraceBusTopic>(\n topic: T,\n handler: (payload: TraceBusTopics[T]) => void,\n): () => void {\n const wrapped = (payload: TraceBusTopics[T]) => {\n try {\n handler(payload);\n } catch {\n /* subscriber errors must not crash the emitter */\n }\n };\n titanEvents.on(topic, wrapped);\n return () => titanEvents.off(topic, wrapped);\n}\n\n/**\n * Subscribe once; auto-unsubscribes after the first event. Useful for tests\n * and one-shot handlers.\n */\nexport function once<T extends TraceBusTopic>(\n topic: T,\n handler: (payload: TraceBusTopics[T]) => void,\n): void {\n const wrapped = (payload: TraceBusTopics[T]) => {\n try {\n handler(payload);\n } catch {\n /* silent */\n }\n };\n titanEvents.once(topic, wrapped);\n}\n\n/** How many subscribers are currently attached to a topic. */\nexport function listenerCount(topic: TraceBusTopic): number {\n return titanEvents.listenerCount(topic);\n}\n"],"mappings":";AAeA,SAAS,mBAAmB;AA8IrB,SAAS,KAA8B,OAAU,SAAkC;AACtF,MAAI;AACA,gBAAY,KAAK,OAAO,OAAO;AAAA,EACnC,QAAQ;AAAA,EAER;AACJ;AAMO,SAAS,GACZ,OACA,SACU;AACV,QAAM,UAAU,CAAC,YAA+B;AAC5C,QAAI;AACA,cAAQ,OAAO;AAAA,IACnB,QAAQ;AAAA,IAER;AAAA,EACJ;AACA,cAAY,GAAG,OAAO,OAAO;AAC7B,SAAO,MAAM,YAAY,IAAI,OAAO,OAAO;AAC/C;AAMO,SAAS,KACZ,OACA,SACI;AACJ,QAAM,UAAU,CAAC,YAA+B;AAC5C,QAAI;AACA,cAAQ,OAAO;AAAA,IACnB,QAAQ;AAAA,IAER;AAAA,EACJ;AACA,cAAY,KAAK,OAAO,OAAO;AACnC;AAGO,SAAS,cAAc,OAA8B;AACxD,SAAO,YAAY,cAAc,KAAK;AAC1C;","names":[]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, appendFileSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import logger from "../utils/logger.js";
|
|
6
|
+
const COMPONENT = "TraceStore";
|
|
7
|
+
const MAX_SPANS = 2e3;
|
|
8
|
+
function titanHome() {
|
|
9
|
+
const env = process.env.TITAN_HOME;
|
|
10
|
+
if (env) return env.startsWith("~/") ? join(homedir(), env.slice(2)) : env;
|
|
11
|
+
return join(homedir(), ".titan");
|
|
12
|
+
}
|
|
13
|
+
function spansPath() {
|
|
14
|
+
return join(titanHome(), "traces", "spans.jsonl");
|
|
15
|
+
}
|
|
16
|
+
let _seq = 0;
|
|
17
|
+
function nextId(startedAt) {
|
|
18
|
+
_seq = (_seq + 1) % 1e6;
|
|
19
|
+
return `sp_${startedAt.replace(/[^0-9]/g, "").slice(0, 17)}_${_seq.toString(36)}`;
|
|
20
|
+
}
|
|
21
|
+
function recordSpan(span) {
|
|
22
|
+
const full = { ...span, id: nextId(span.startedAt) };
|
|
23
|
+
try {
|
|
24
|
+
const path = spansPath();
|
|
25
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
26
|
+
appendFileSync(path, JSON.stringify(full) + "\n", "utf-8");
|
|
27
|
+
return full;
|
|
28
|
+
} catch (e) {
|
|
29
|
+
logger.debug(COMPONENT, `Failed to record span: ${e.message}`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function listSpans(opts = {}) {
|
|
34
|
+
const path = spansPath();
|
|
35
|
+
if (!existsSync(path)) return [];
|
|
36
|
+
let lines;
|
|
37
|
+
try {
|
|
38
|
+
lines = readFileSync(path, "utf-8").split("\n").filter(Boolean);
|
|
39
|
+
} catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
if (lines.length > MAX_SPANS) {
|
|
43
|
+
const trimmed = lines.slice(-MAX_SPANS);
|
|
44
|
+
try {
|
|
45
|
+
writeFileSync(path, trimmed.join("\n") + "\n", "utf-8");
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
lines = trimmed;
|
|
49
|
+
}
|
|
50
|
+
const spans = [];
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
try {
|
|
53
|
+
spans.push(JSON.parse(line));
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
spans.reverse();
|
|
58
|
+
const filtered = opts.sessionId ? spans.filter((s) => s.sessionId === opts.sessionId) : spans;
|
|
59
|
+
return opts.limit ? filtered.slice(0, opts.limit) : filtered;
|
|
60
|
+
}
|
|
61
|
+
function summarizeTraces(spans) {
|
|
62
|
+
const byModel = {};
|
|
63
|
+
let totalPrompt = 0, totalCompletion = 0, totalCost = 0, totalDur = 0, ok = 0;
|
|
64
|
+
for (const s of spans) {
|
|
65
|
+
totalPrompt += s.promptTokens || 0;
|
|
66
|
+
totalCompletion += s.completionTokens || 0;
|
|
67
|
+
totalCost += s.costUsd || 0;
|
|
68
|
+
totalDur += s.durationMs || 0;
|
|
69
|
+
if (s.ok) ok++;
|
|
70
|
+
const m = byModel[s.model] ||= { runs: 0, tokens: 0, costUsd: 0 };
|
|
71
|
+
m.runs++;
|
|
72
|
+
m.tokens += (s.promptTokens || 0) + (s.completionTokens || 0);
|
|
73
|
+
m.costUsd += s.costUsd || 0;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
totalRuns: spans.length,
|
|
77
|
+
okRuns: ok,
|
|
78
|
+
totalPromptTokens: totalPrompt,
|
|
79
|
+
totalCompletionTokens: totalCompletion,
|
|
80
|
+
totalCostUsd: Math.round(totalCost * 1e6) / 1e6,
|
|
81
|
+
avgDurationMs: spans.length ? Math.round(totalDur / spans.length) : 0,
|
|
82
|
+
byModel
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function toOTel(spans) {
|
|
86
|
+
return {
|
|
87
|
+
resourceSpans: [{
|
|
88
|
+
resource: { attributes: [{ key: "service.name", value: { stringValue: "titan-agent" } }] },
|
|
89
|
+
scopeSpans: [{
|
|
90
|
+
scope: { name: "titan.agent" },
|
|
91
|
+
spans: spans.map((s) => ({
|
|
92
|
+
traceId: s.runId || s.sessionId,
|
|
93
|
+
spanId: s.id,
|
|
94
|
+
name: "agent.run",
|
|
95
|
+
startTimeUnixNano: Date.parse(s.startedAt) * 1e6 || 0,
|
|
96
|
+
attributes: [
|
|
97
|
+
{ key: "titan.model", value: { stringValue: s.model } },
|
|
98
|
+
{ key: "titan.tools", value: { stringValue: s.toolsUsed.join(",") } },
|
|
99
|
+
{ key: "titan.prompt_tokens", value: { intValue: s.promptTokens } },
|
|
100
|
+
{ key: "titan.completion_tokens", value: { intValue: s.completionTokens } },
|
|
101
|
+
{ key: "titan.cost_usd", value: { doubleValue: s.costUsd } },
|
|
102
|
+
{ key: "titan.duration_ms", value: { intValue: s.durationMs } },
|
|
103
|
+
{ key: "titan.ok", value: { boolValue: s.ok } }
|
|
104
|
+
]
|
|
105
|
+
}))
|
|
106
|
+
}]
|
|
107
|
+
}]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function __resetTracesForTests() {
|
|
111
|
+
const path = spansPath();
|
|
112
|
+
if (existsSync(path)) {
|
|
113
|
+
try {
|
|
114
|
+
unlinkSync(path);
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
_seq = 0;
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
__resetTracesForTests,
|
|
122
|
+
listSpans,
|
|
123
|
+
recordSpan,
|
|
124
|
+
summarizeTraces,
|
|
125
|
+
toOTel
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=traceStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/telemetry/traceStore.ts"],"sourcesContent":["/**\n * TITAN — Run trace store (v7.0 observability, the #1 cross-market demand).\n *\n * Records one span per agent run (input/output/latency/tokens/cost/model/tools),\n * persisted as JSONL under `$TITAN_HOME/traces/spans.jsonl`, with pure\n * aggregation + OTel-style export for the runs/observability dashboard and any\n * external trace pipeline.\n *\n * `summarizeTraces()` and `toOTel()` are PURE (take a spans array) for tests;\n * `recordSpan()`/`listSpans()` are the file-backed surface.\n */\nimport { existsSync, mkdirSync, appendFileSync, readFileSync, writeFileSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'TraceStore';\n/** Keep the file bounded — newest N spans are retained. */\nconst MAX_SPANS = 2000;\n\nexport interface TraceSpan {\n id: string;\n sessionId: string;\n runId?: string;\n /** ISO timestamp the run started. */\n startedAt: string;\n durationMs: number;\n model: string;\n /** Truncated user input. */\n input: string;\n /** Truncated final output. */\n output: string;\n toolsUsed: string[];\n promptTokens: number;\n completionTokens: number;\n costUsd: number;\n ok: boolean;\n}\n\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 spansPath(): string { return join(titanHome(), 'traces', 'spans.jsonl'); }\n\nlet _seq = 0;\nfunction nextId(startedAt: string): string {\n // Deterministic-ish, collision-resistant without Date.now()/random (both banned\n // in some contexts): the caller's ISO start + a monotonic counter.\n _seq = (_seq + 1) % 1_000_000;\n return `sp_${startedAt.replace(/[^0-9]/g, '').slice(0, 17)}_${_seq.toString(36)}`;\n}\n\n/** Append a span. Best-effort — never throws into the agent path. */\nexport function recordSpan(span: Omit<TraceSpan, 'id'>): TraceSpan | null {\n const full: TraceSpan = { ...span, id: nextId(span.startedAt) };\n try {\n const path = spansPath();\n mkdirSync(join(path, '..'), { recursive: true });\n appendFileSync(path, JSON.stringify(full) + '\\n', 'utf-8');\n return full;\n } catch (e) {\n logger.debug(COMPONENT, `Failed to record span: ${(e as Error).message}`);\n return null;\n }\n}\n\n/** Read spans back (newest first). Bounded by `limit`. */\nexport function listSpans(opts: { sessionId?: string; limit?: number } = {}): TraceSpan[] {\n const path = spansPath();\n if (!existsSync(path)) return [];\n let lines: string[];\n try { lines = readFileSync(path, 'utf-8').split('\\n').filter(Boolean); } catch { return []; }\n // self-trim if the file grew past the cap\n if (lines.length > MAX_SPANS) {\n const trimmed = lines.slice(-MAX_SPANS);\n try { writeFileSync(path, trimmed.join('\\n') + '\\n', 'utf-8'); } catch { /* best effort */ }\n lines = trimmed;\n }\n const spans: TraceSpan[] = [];\n for (const line of lines) {\n try { spans.push(JSON.parse(line)); } catch { /* skip corrupt line */ }\n }\n spans.reverse(); // newest first\n const filtered = opts.sessionId ? spans.filter((s) => s.sessionId === opts.sessionId) : spans;\n return opts.limit ? filtered.slice(0, opts.limit) : filtered;\n}\n\nexport interface TraceSummary {\n totalRuns: number;\n okRuns: number;\n totalPromptTokens: number;\n totalCompletionTokens: number;\n totalCostUsd: number;\n avgDurationMs: number;\n /** Per-model run counts + token totals. */\n byModel: Record<string, { runs: number; tokens: number; costUsd: number }>;\n}\n\n/** Pure aggregation over a spans array. */\nexport function summarizeTraces(spans: TraceSpan[]): TraceSummary {\n const byModel: TraceSummary['byModel'] = {};\n let totalPrompt = 0, totalCompletion = 0, totalCost = 0, totalDur = 0, ok = 0;\n for (const s of spans) {\n totalPrompt += s.promptTokens || 0;\n totalCompletion += s.completionTokens || 0;\n totalCost += s.costUsd || 0;\n totalDur += s.durationMs || 0;\n if (s.ok) ok++;\n const m = (byModel[s.model] ||= { runs: 0, tokens: 0, costUsd: 0 });\n m.runs++;\n m.tokens += (s.promptTokens || 0) + (s.completionTokens || 0);\n m.costUsd += s.costUsd || 0;\n }\n return {\n totalRuns: spans.length,\n okRuns: ok,\n totalPromptTokens: totalPrompt,\n totalCompletionTokens: totalCompletion,\n totalCostUsd: Math.round(totalCost * 1e6) / 1e6,\n avgDurationMs: spans.length ? Math.round(totalDur / spans.length) : 0,\n byModel,\n };\n}\n\n/** Pure OTel-style export (resource spans). Take a spans array. */\nexport function toOTel(spans: TraceSpan[]): Record<string, unknown> {\n return {\n resourceSpans: [{\n resource: { attributes: [{ key: 'service.name', value: { stringValue: 'titan-agent' } }] },\n scopeSpans: [{\n scope: { name: 'titan.agent' },\n spans: spans.map((s) => ({\n traceId: s.runId || s.sessionId,\n spanId: s.id,\n name: 'agent.run',\n startTimeUnixNano: Date.parse(s.startedAt) * 1e6 || 0,\n attributes: [\n { key: 'titan.model', value: { stringValue: s.model } },\n { key: 'titan.tools', value: { stringValue: s.toolsUsed.join(',') } },\n { key: 'titan.prompt_tokens', value: { intValue: s.promptTokens } },\n { key: 'titan.completion_tokens', value: { intValue: s.completionTokens } },\n { key: 'titan.cost_usd', value: { doubleValue: s.costUsd } },\n { key: 'titan.duration_ms', value: { intValue: s.durationMs } },\n { key: 'titan.ok', value: { boolValue: s.ok } },\n ],\n })),\n }],\n }],\n };\n}\n\n/** Test helper. */\nexport function __resetTracesForTests(): void {\n const path = spansPath();\n if (existsSync(path)) { try { unlinkSync(path); } catch { /* fine */ } }\n _seq = 0;\n}\n"],"mappings":";AAWA,SAAS,YAAY,WAAW,gBAAgB,cAAc,eAAe,kBAAkB;AAC/F,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,OAAO,YAAY;AAEnB,MAAM,YAAY;AAElB,MAAM,YAAY;AAqBlB,SAAS,YAAoB;AACzB,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,IAAK,QAAO,IAAI,WAAW,IAAI,IAAI,KAAK,QAAQ,GAAG,IAAI,MAAM,CAAC,CAAC,IAAI;AACvE,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACnC;AACA,SAAS,YAAoB;AAAE,SAAO,KAAK,UAAU,GAAG,UAAU,aAAa;AAAG;AAElF,IAAI,OAAO;AACX,SAAS,OAAO,WAA2B;AAGvC,UAAQ,OAAO,KAAK;AACpB,SAAO,MAAM,UAAU,QAAQ,WAAW,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;AACnF;AAGO,SAAS,WAAW,MAA+C;AACtE,QAAM,OAAkB,EAAE,GAAG,MAAM,IAAI,OAAO,KAAK,SAAS,EAAE;AAC9D,MAAI;AACA,UAAM,OAAO,UAAU;AACvB,cAAU,KAAK,MAAM,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC/C,mBAAe,MAAM,KAAK,UAAU,IAAI,IAAI,MAAM,OAAO;AACzD,WAAO;AAAA,EACX,SAAS,GAAG;AACR,WAAO,MAAM,WAAW,0BAA2B,EAAY,OAAO,EAAE;AACxE,WAAO;AAAA,EACX;AACJ;AAGO,SAAS,UAAU,OAA+C,CAAC,GAAgB;AACtF,QAAM,OAAO,UAAU;AACvB,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACJ,MAAI;AAAE,YAAQ,aAAa,MAAM,OAAO,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAAA,EAAG,QAAQ;AAAE,WAAO,CAAC;AAAA,EAAG;AAE5F,MAAI,MAAM,SAAS,WAAW;AAC1B,UAAM,UAAU,MAAM,MAAM,CAAC,SAAS;AACtC,QAAI;AAAE,oBAAc,MAAM,QAAQ,KAAK,IAAI,IAAI,MAAM,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAoB;AAC3F,YAAQ;AAAA,EACZ;AACA,QAAM,QAAqB,CAAC;AAC5B,aAAW,QAAQ,OAAO;AACtB,QAAI;AAAE,YAAM,KAAK,KAAK,MAAM,IAAI,CAAC;AAAA,IAAG,QAAQ;AAAA,IAA0B;AAAA,EAC1E;AACA,QAAM,QAAQ;AACd,QAAM,WAAW,KAAK,YAAY,MAAM,OAAO,CAAC,MAAM,EAAE,cAAc,KAAK,SAAS,IAAI;AACxF,SAAO,KAAK,QAAQ,SAAS,MAAM,GAAG,KAAK,KAAK,IAAI;AACxD;AAcO,SAAS,gBAAgB,OAAkC;AAC9D,QAAM,UAAmC,CAAC;AAC1C,MAAI,cAAc,GAAG,kBAAkB,GAAG,YAAY,GAAG,WAAW,GAAG,KAAK;AAC5E,aAAW,KAAK,OAAO;AACnB,mBAAe,EAAE,gBAAgB;AACjC,uBAAmB,EAAE,oBAAoB;AACzC,iBAAa,EAAE,WAAW;AAC1B,gBAAY,EAAE,cAAc;AAC5B,QAAI,EAAE,GAAI;AACV,UAAM,IAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,EAAE;AACjE,MAAE;AACF,MAAE,WAAW,EAAE,gBAAgB,MAAM,EAAE,oBAAoB;AAC3D,MAAE,WAAW,EAAE,WAAW;AAAA,EAC9B;AACA,SAAO;AAAA,IACH,WAAW,MAAM;AAAA,IACjB,QAAQ;AAAA,IACR,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,cAAc,KAAK,MAAM,YAAY,GAAG,IAAI;AAAA,IAC5C,eAAe,MAAM,SAAS,KAAK,MAAM,WAAW,MAAM,MAAM,IAAI;AAAA,IACpE;AAAA,EACJ;AACJ;AAGO,SAAS,OAAO,OAA6C;AAChE,SAAO;AAAA,IACH,eAAe,CAAC;AAAA,MACZ,UAAU,EAAE,YAAY,CAAC,EAAE,KAAK,gBAAgB,OAAO,EAAE,aAAa,cAAc,EAAE,CAAC,EAAE;AAAA,MACzF,YAAY,CAAC;AAAA,QACT,OAAO,EAAE,MAAM,cAAc;AAAA,QAC7B,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,UACrB,SAAS,EAAE,SAAS,EAAE;AAAA,UACtB,QAAQ,EAAE;AAAA,UACV,MAAM;AAAA,UACN,mBAAmB,KAAK,MAAM,EAAE,SAAS,IAAI,OAAO;AAAA,UACpD,YAAY;AAAA,YACR,EAAE,KAAK,eAAe,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE;AAAA,YACtD,EAAE,KAAK,eAAe,OAAO,EAAE,aAAa,EAAE,UAAU,KAAK,GAAG,EAAE,EAAE;AAAA,YACpE,EAAE,KAAK,uBAAuB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE;AAAA,YAClE,EAAE,KAAK,2BAA2B,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE;AAAA,YAC1E,EAAE,KAAK,kBAAkB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE;AAAA,YAC3D,EAAE,KAAK,qBAAqB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE;AAAA,YAC9D,EAAE,KAAK,YAAY,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE;AAAA,UAClD;AAAA,QACJ,EAAE;AAAA,MACN,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AACJ;AAGO,SAAS,wBAA8B;AAC1C,QAAM,OAAO,UAAU;AACvB,MAAI,WAAW,IAAI,GAAG;AAAE,QAAI;AAAE,iBAAW,IAAI;AAAA,IAAG,QAAQ;AAAA,IAAa;AAAA,EAAE;AACvE,SAAO;AACX;","names":[]}
|
package/dist/utils/constants.js
CHANGED
|
@@ -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 = "
|
|
4
|
+
const TITAN_VERSION = "7.0.0";
|
|
5
5
|
const TITAN_CODENAME = "Living Canvas";
|
|
6
6
|
const TITAN_NAME = "TITAN";
|
|
7
7
|
const TITAN_FULL_NAME = "The Intelligent Task Automation Network";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '
|
|
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 = '7.0.0';\nexport const TITAN_CODENAME = 'Living Canvas';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// v6.1.0 — Mission Chat\nexport const MISSIONS_DIR = join(TITAN_HOME, 'missions');\nexport const PLAYS_DIR = join(TITAN_HOME, 'plays');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\n// v6.0.1 — Hardcoded floor only. The actual default at runtime is picked\n// by `getDefaultModelId()` in `src/providers/defaultModel.ts`, which\n// detects the user's available API keys (Anthropic / OpenAI / Google /\n// OpenRouter) and selects accordingly. Falls back to local Ollama when\n// no cloud keys are set. This constant is the last-resort fallback for\n// callsites that don't go through the schema thunk.\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n/** v5.4.1: User-preference ceiling. Providers clamp per-model via\n * clampMaxTokens() so this can be high without causing 400s on\n * capped endpoints (e.g. Claude Sonnet 4 8K, Cohere 4K). */\nexport const DEFAULT_MAX_TOKENS = 200000;\nexport const DEFAULT_TEMPERATURE = 0.7;\nexport const MAX_CONTEXT_MESSAGES = 50;\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\n// Security\nexport const DEFAULT_SANDBOX_MODE = 'host';\n/** Default allowed tools. Empty = allow ALL registered tools.\n * Use security.deniedTools to block specific tools instead. */\nexport const ALLOWED_TOOLS_DEFAULT: string[] = [];\nexport const DENIED_TOOLS_DEFAULT: string[] = [];\n"],"mappings":";AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAW1B,aAAa;AAAA;AAYnB,SAAS,mBAA2B;AAChC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,GAAG;AACtC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC1B,aAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC3C;AACA,QAAI,YAAY,KAAK;AACjB,aAAO,QAAQ;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACnC;AACO,MAAM,aAAa,iBAAiB;AACpC,MAAM,oBAAoB,KAAK,YAAY,YAAY;AACvD,MAAM,gBAAgB,KAAK,YAAY,UAAU;AACjD,MAAM,kBAAkB,KAAK,YAAY,WAAW;AACpD,MAAM,mBAAmB,KAAK,iBAAiB,QAAQ;AACvD,MAAM,iBAAiB,KAAK,YAAY,MAAM;AAC9C,MAAM,mBAAmB,KAAK,YAAY,QAAQ;AAGlD,MAAM,YAAY,KAAK,iBAAiB,WAAW;AACnD,MAAM,UAAU,KAAK,iBAAiB,SAAS;AAC/C,MAAM,WAAW,KAAK,iBAAiB,UAAU;AACjD,MAAM,oBAAoB;AAC1B,MAAM,eAAe,KAAK,YAAY,cAAc;AACpD,MAAM,sBAAsB,KAAK,YAAY,sBAAsB;AACnE,MAAM,wBAAwB,KAAK,YAAY,aAAa;AAG5D,MAAM,qBAAqB,KAAK,YAAY,qBAAqB;AACjE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,yBAAyB,KAAK,YAAY,wBAAwB;AACxE,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,MAAM,wBAAwB,KAAK,YAAY,wBAAwB;AACvE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,oBAAoB,KAAK,YAAY,oBAAoB;AAG/D,MAAM,eAAe,KAAK,YAAY,UAAU;AAChD,MAAM,YAAY,KAAK,YAAY,OAAO;AAG1C,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AASzB,MAAM,gBAAgB;AAItB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB,KAAK,KAAK;AAGrC,MAAM,uBAAuB;AAG7B,MAAM,wBAAkC,CAAC;AACzC,MAAM,uBAAiC,CAAC;","names":[]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
function hexToRgb(hex) {
|
|
3
|
+
const h = hex.trim().replace(/^#/, "");
|
|
4
|
+
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
5
|
+
if (!/^[0-9a-fA-F]{6}$/.test(full)) throw new Error(`Not a hex color: "${hex}"`);
|
|
6
|
+
return {
|
|
7
|
+
r: parseInt(full.slice(0, 2), 16),
|
|
8
|
+
g: parseInt(full.slice(2, 4), 16),
|
|
9
|
+
b: parseInt(full.slice(4, 6), 16)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function parseRgba(s) {
|
|
13
|
+
const m = s.trim().match(/^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:[,/\s]+([\d.]+))?\s*\)$/);
|
|
14
|
+
if (!m) throw new Error(`Not an rgb(a) color: "${s}"`);
|
|
15
|
+
return {
|
|
16
|
+
rgb: { r: Number(m[1]), g: Number(m[2]), b: Number(m[3]) },
|
|
17
|
+
a: m[4] === void 0 ? 1 : Number(m[4])
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function flatten(fg, base) {
|
|
21
|
+
const b = hexToRgb(base);
|
|
22
|
+
let fgRgb;
|
|
23
|
+
let a = 1;
|
|
24
|
+
if (fg.trim().startsWith("#")) {
|
|
25
|
+
fgRgb = hexToRgb(fg);
|
|
26
|
+
} else {
|
|
27
|
+
const p = parseRgba(fg);
|
|
28
|
+
fgRgb = p.rgb;
|
|
29
|
+
a = p.a;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
r: Math.round(fgRgb.r * a + b.r * (1 - a)),
|
|
33
|
+
g: Math.round(fgRgb.g * a + b.g * (1 - a)),
|
|
34
|
+
b: Math.round(fgRgb.b * a + b.b * (1 - a))
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function relativeLuminance(c) {
|
|
38
|
+
const lin = (v) => {
|
|
39
|
+
const s = v / 255;
|
|
40
|
+
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
41
|
+
};
|
|
42
|
+
return 0.2126 * lin(c.r) + 0.7152 * lin(c.g) + 0.0722 * lin(c.b);
|
|
43
|
+
}
|
|
44
|
+
function contrastRatio(a, b, base = "#ffffff") {
|
|
45
|
+
const toRgb = (x) => {
|
|
46
|
+
if (typeof x !== "string") return x;
|
|
47
|
+
return x.trim().startsWith("#") ? hexToRgb(x) : flatten(x, base);
|
|
48
|
+
};
|
|
49
|
+
const l1 = relativeLuminance(toRgb(a));
|
|
50
|
+
const l2 = relativeLuminance(toRgb(b));
|
|
51
|
+
const [hi, lo] = l1 >= l2 ? [l1, l2] : [l2, l1];
|
|
52
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
53
|
+
}
|
|
54
|
+
function meetsAA(ratio, level = "text") {
|
|
55
|
+
return ratio >= (level === "text" ? 4.5 : 3);
|
|
56
|
+
}
|
|
57
|
+
function ratioStr(ratio) {
|
|
58
|
+
return `${Math.round(ratio * 100) / 100}:1`;
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
contrastRatio,
|
|
62
|
+
flatten,
|
|
63
|
+
hexToRgb,
|
|
64
|
+
meetsAA,
|
|
65
|
+
parseRgba,
|
|
66
|
+
ratioStr,
|
|
67
|
+
relativeLuminance
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=contrast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/contrast.ts"],"sourcesContent":["/**\n * TITAN — WCAG 2.1 contrast math (v7.0 accessibility).\n *\n * Pure, dependency-free implementation of the WCAG relative-luminance and\n * contrast-ratio formulas. Used by the theme contrast-audit test (and available\n * at runtime for any \"is this color pair legible?\" check) so TITAN's themes are\n * verified to meet AA programmatically instead of by eyeballing.\n *\n * Spec: https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio\n * - AA normal text : ≥ 4.5:1\n * - AA large text : ≥ 3.0:1 (≥18.66px bold or ≥24px)\n * - AA UI component : ≥ 3.0:1 (borders, icons, focus rings)\n */\n\nexport interface Rgb { r: number; g: number; b: number }\n\n/** Parse `#rgb` / `#rrggbb` to 0–255 channels. Throws on anything else. */\nexport function hexToRgb(hex: string): Rgb {\n const h = hex.trim().replace(/^#/, '');\n const full = h.length === 3 ? h.split('').map((c) => c + c).join('') : h;\n if (!/^[0-9a-fA-F]{6}$/.test(full)) throw new Error(`Not a hex color: \"${hex}\"`);\n return {\n r: parseInt(full.slice(0, 2), 16),\n g: parseInt(full.slice(2, 4), 16),\n b: parseInt(full.slice(4, 6), 16),\n };\n}\n\n/** Parse `rgba(r,g,b,a)` / `rgb(r,g,b)` → { rgb, a }. Throws on anything else. */\nexport function parseRgba(s: string): { rgb: Rgb; a: number } {\n const m = s.trim().match(/^rgba?\\(\\s*([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)(?:[,/\\s]+([\\d.]+))?\\s*\\)$/);\n if (!m) throw new Error(`Not an rgb(a) color: \"${s}\"`);\n return {\n rgb: { r: Number(m[1]), g: Number(m[2]), b: Number(m[3]) },\n a: m[4] === undefined ? 1 : Number(m[4]),\n };\n}\n\n/**\n * Composite a (possibly translucent) color over an opaque base, returning the\n * resulting opaque color. Accepts hex or rgb(a) strings for `fg`; `base` must be\n * opaque hex. Needed because some theme surfaces are rgba over the page bg.\n */\nexport function flatten(fg: string, base: string): Rgb {\n const b = hexToRgb(base);\n let fgRgb: Rgb; let a = 1;\n if (fg.trim().startsWith('#')) {\n fgRgb = hexToRgb(fg);\n } else {\n const p = parseRgba(fg);\n fgRgb = p.rgb; a = p.a;\n }\n return {\n r: Math.round(fgRgb.r * a + b.r * (1 - a)),\n g: Math.round(fgRgb.g * a + b.g * (1 - a)),\n b: Math.round(fgRgb.b * a + b.b * (1 - a)),\n };\n}\n\n/** WCAG relative luminance (0=black … 1=white). */\nexport function relativeLuminance(c: Rgb): number {\n const lin = (v: number) => {\n const s = v / 255;\n return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);\n };\n return 0.2126 * lin(c.r) + 0.7152 * lin(c.g) + 0.0722 * lin(c.b);\n}\n\n/**\n * WCAG contrast ratio between two colors (1:1 … 21:1). Each color may be hex,\n * rgb(a), or an already-parsed Rgb. Translucent inputs are flattened over\n * `base` (default white) first.\n */\nexport function contrastRatio(a: string | Rgb, b: string | Rgb, base = '#ffffff'): number {\n const toRgb = (x: string | Rgb): Rgb => {\n if (typeof x !== 'string') return x;\n return x.trim().startsWith('#') ? hexToRgb(x) : flatten(x, base);\n };\n const l1 = relativeLuminance(toRgb(a));\n const l2 = relativeLuminance(toRgb(b));\n const [hi, lo] = l1 >= l2 ? [l1, l2] : [l2, l1];\n return (hi + 0.05) / (lo + 0.05);\n}\n\n/** True when the pair meets AA. `level`: 'text' (4.5:1) or 'large'|'ui' (3:1). */\nexport function meetsAA(ratio: number, level: 'text' | 'large' | 'ui' = 'text'): boolean {\n return ratio >= (level === 'text' ? 4.5 : 3.0);\n}\n\n/** Round to 2 dp for readable reporting. */\nexport function ratioStr(ratio: number): string {\n return `${Math.round(ratio * 100) / 100}:1`;\n}\n"],"mappings":";AAiBO,SAAS,SAAS,KAAkB;AACvC,QAAM,IAAI,IAAI,KAAK,EAAE,QAAQ,MAAM,EAAE;AACrC,QAAM,OAAO,EAAE,WAAW,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI;AACvE,MAAI,CAAC,mBAAmB,KAAK,IAAI,EAAG,OAAM,IAAI,MAAM,qBAAqB,GAAG,GAAG;AAC/E,SAAO;AAAA,IACH,GAAG,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAChC,GAAG,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAChC,GAAG,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,EACpC;AACJ;AAGO,SAAS,UAAU,GAAoC;AAC1D,QAAM,IAAI,EAAE,KAAK,EAAE,MAAM,2EAA2E;AACpG,MAAI,CAAC,EAAG,OAAM,IAAI,MAAM,yBAAyB,CAAC,GAAG;AACrD,SAAO;AAAA,IACH,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE;AAAA,IACzD,GAAG,EAAE,CAAC,MAAM,SAAY,IAAI,OAAO,EAAE,CAAC,CAAC;AAAA,EAC3C;AACJ;AAOO,SAAS,QAAQ,IAAY,MAAmB;AACnD,QAAM,IAAI,SAAS,IAAI;AACvB,MAAI;AAAY,MAAI,IAAI;AACxB,MAAI,GAAG,KAAK,EAAE,WAAW,GAAG,GAAG;AAC3B,YAAQ,SAAS,EAAE;AAAA,EACvB,OAAO;AACH,UAAM,IAAI,UAAU,EAAE;AACtB,YAAQ,EAAE;AAAK,QAAI,EAAE;AAAA,EACzB;AACA,SAAO;AAAA,IACH,GAAG,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE;AAAA,IACzC,GAAG,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE;AAAA,IACzC,GAAG,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE;AAAA,EAC7C;AACJ;AAGO,SAAS,kBAAkB,GAAgB;AAC9C,QAAM,MAAM,CAAC,MAAc;AACvB,UAAM,IAAI,IAAI;AACd,WAAO,KAAK,UAAU,IAAI,QAAQ,KAAK,KAAK,IAAI,SAAS,OAAO,GAAG;AAAA,EACvE;AACA,SAAO,SAAS,IAAI,EAAE,CAAC,IAAI,SAAS,IAAI,EAAE,CAAC,IAAI,SAAS,IAAI,EAAE,CAAC;AACnE;AAOO,SAAS,cAAc,GAAiB,GAAiB,OAAO,WAAmB;AACtF,QAAM,QAAQ,CAAC,MAAyB;AACpC,QAAI,OAAO,MAAM,SAAU,QAAO;AAClC,WAAO,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,SAAS,CAAC,IAAI,QAAQ,GAAG,IAAI;AAAA,EACnE;AACA,QAAM,KAAK,kBAAkB,MAAM,CAAC,CAAC;AACrC,QAAM,KAAK,kBAAkB,MAAM,CAAC,CAAC;AACrC,QAAM,CAAC,IAAI,EAAE,IAAI,MAAM,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AAC9C,UAAQ,KAAK,SAAS,KAAK;AAC/B;AAGO,SAAS,QAAQ,OAAe,QAAiC,QAAiB;AACrF,SAAO,UAAU,UAAU,SAAS,MAAM;AAC9C;AAGO,SAAS,SAAS,OAAuB;AAC5C,SAAO,GAAG,KAAK,MAAM,QAAQ,GAAG,IAAI,GAAG;AAC3C;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
+
const storage = new AsyncLocalStorage();
|
|
4
|
+
function currentSession() {
|
|
5
|
+
return storage.getStore() ?? null;
|
|
6
|
+
}
|
|
7
|
+
function runWithSession(ctx, fn) {
|
|
8
|
+
return storage.run(ctx, fn);
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
currentSession,
|
|
12
|
+
runWithSession
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=sessionContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/watch/sessionContext.ts"],"sourcesContent":["/**\n * TITAN — Session Context (live-agents event spine)\n *\n * Async-context-aware \"current session\" pointer, mirroring worktreeScope. When\n * tool execution is wrapped in `runWithSession`, every `executeTool` call inside\n * it (including nested/awaited sub-agent calls) sees the same `{sessionId,\n * agentId}` via `currentSession()`. sessionTrace uses this so `tool:call` /\n * `tool:result` events are attributed to the session that actually triggered\n * them — instead of falling back to the ambient env (which the gateway never\n * sets per request, so tool events were landing under \"unknown\").\n *\n * AsyncLocalStorage (not a module global) so two concurrent chats on the same\n * gateway each get their own session — no cross-contamination when their awaits\n * interleave. Neutral module (no agent/toolRunner imports) to avoid import cycles.\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nexport interface SessionContext {\n sessionId: string;\n agentId?: string;\n}\n\nconst storage = new AsyncLocalStorage<SessionContext>();\n\n/** Read the current session context, or null when outside any runWithSession. */\nexport function currentSession(): SessionContext | null {\n return storage.getStore() ?? null;\n}\n\n/** Run `fn` with the given session context active for all downstream tool calls. */\nexport function runWithSession<T>(ctx: SessionContext, fn: () => T | Promise<T>): T | Promise<T> {\n return storage.run(ctx, fn);\n}\n"],"mappings":";AAeA,SAAS,yBAAyB;AAOlC,MAAM,UAAU,IAAI,kBAAkC;AAG/C,SAAS,iBAAwC;AACpD,SAAO,QAAQ,SAAS,KAAK;AACjC;AAGO,SAAS,eAAkB,KAAqB,IAA0C;AAC7F,SAAO,QAAQ,IAAI,KAAK,EAAE;AAC9B;","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { emit as busEmit } from "../substrate/traceBus.js";
|
|
3
|
+
import { currentSession } from "./sessionContext.js";
|
|
4
|
+
const DEDUPE_TTL_MS = 3e4;
|
|
5
|
+
const DEDUPE_MAX = 4096;
|
|
6
|
+
const seen = /* @__PURE__ */ new Map();
|
|
7
|
+
function shouldEmit(key, now) {
|
|
8
|
+
const exp = seen.get(key);
|
|
9
|
+
if (exp !== void 0 && exp > now) return false;
|
|
10
|
+
seen.set(key, now + DEDUPE_TTL_MS);
|
|
11
|
+
if (seen.size > DEDUPE_MAX) {
|
|
12
|
+
for (const [k, e] of seen) if (e <= now) seen.delete(k);
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
function resetDedupe() {
|
|
17
|
+
seen.clear();
|
|
18
|
+
}
|
|
19
|
+
function ambientSessionId() {
|
|
20
|
+
return process.env.TITAN_SESSION_ID || void 0;
|
|
21
|
+
}
|
|
22
|
+
function resolveIds(input) {
|
|
23
|
+
const ctx = currentSession();
|
|
24
|
+
return {
|
|
25
|
+
sessionId: input.sessionId || ctx?.sessionId || ambientSessionId() || "unknown",
|
|
26
|
+
agentId: input.agentId || ctx?.agentId || "main"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function emitToolCall(input, nowMs) {
|
|
30
|
+
const now = nowMs ?? Date.now();
|
|
31
|
+
if (input.actionId && !shouldEmit(`tool:call|${input.actionId}`, now)) return false;
|
|
32
|
+
const { sessionId, agentId } = resolveIds(input);
|
|
33
|
+
busEmit("tool:call", { ...input, sessionId, agentId, timestamp: new Date(now).toISOString() });
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
function emitToolResult(input, nowMs) {
|
|
37
|
+
const now = nowMs ?? Date.now();
|
|
38
|
+
if (input.actionId && !shouldEmit(`tool:result|${input.actionId}`, now)) return false;
|
|
39
|
+
const { sessionId, agentId } = resolveIds(input);
|
|
40
|
+
busEmit("tool:result", { ...input, sessionId, agentId, timestamp: new Date(now).toISOString() });
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
DEDUPE_TTL_MS,
|
|
45
|
+
ambientSessionId,
|
|
46
|
+
emitToolCall,
|
|
47
|
+
emitToolResult,
|
|
48
|
+
resetDedupe,
|
|
49
|
+
shouldEmit
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=sessionTrace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/watch/sessionTrace.ts"],"sourcesContent":["/**\n * TITAN — Session Trace (live-agents event spine)\n *\n * The single, session-scoped, attributable fan-out for tool events. Stamps a\n * stable `actionId` and emits onto the existing trace bus (`tool:call` /\n * `tool:result`) — the topics that are typed + subscribed (watchRouter) +\n * humanized today but have NO producer. This is that producer.\n *\n * Two call sites feed it (built in later slices): a zero-blast-radius fallback\n * in `executeTool` and richer sibling emits in `agentLoop`. Because both can\n * fire for the same logical action, a short TTL dedupe (keyed on the shared\n * `actionId`) collapses them to one event. When no `actionId` is present the\n * event always emits (no dedupe — we never silently drop an unattributable\n * event).\n *\n * Design invariants: additive only, never throws to the caller, O(1) emit,\n * model-agnostic, and it touches no LLM and never the `claude` CLI.\n */\nimport { emit as busEmit, type ToolCallEvent, type ToolResultEvent } from '../substrate/traceBus.js';\nimport { currentSession } from './sessionContext.js';\n\n/** How long a given actionId stays deduped after first emit. */\nexport const DEDUPE_TTL_MS = 30_000;\n/** Soft cap on the dedupe registry before an opportunistic prune. */\nconst DEDUPE_MAX = 4096;\n\nconst seen = new Map<string, number>();\n\n/**\n * Pure dedupe primitive. Returns true the FIRST time a key is seen within the\n * TTL window, false on repeats. `now` is injected so it is deterministic and\n * unit-testable. Prunes opportunistically when the registry grows large.\n */\nexport function shouldEmit(key: string, now: number): boolean {\n const exp = seen.get(key);\n if (exp !== undefined && exp > now) return false;\n seen.set(key, now + DEDUPE_TTL_MS);\n if (seen.size > DEDUPE_MAX) {\n for (const [k, e] of seen) if (e <= now) seen.delete(k);\n }\n return true;\n}\n\n/** Test/maintenance hook — clears the dedupe registry. */\nexport function resetDedupe(): void {\n seen.clear();\n}\n\n/** Ambient session id, threaded by callers via env when no explicit id exists. */\nexport function ambientSessionId(): string | undefined {\n return process.env.TITAN_SESSION_ID || undefined;\n}\n\ntype ToolCallInput = Omit<ToolCallEvent, 'timestamp' | 'sessionId' | 'agentId'> &\n Partial<Pick<ToolCallEvent, 'sessionId' | 'agentId'>>;\ntype ToolResultInput = Omit<ToolResultEvent, 'timestamp' | 'sessionId' | 'agentId'> &\n Partial<Pick<ToolResultEvent, 'sessionId' | 'agentId'>>;\n\nfunction resolveIds<T extends { sessionId?: string; agentId?: string }>(input: T): { sessionId: string; agentId: string } {\n // Prefer the async-context session (set around tool execution by the agent\n // loop) so tool events attribute to the session that triggered them; fall\n // back to the ambient env, then 'unknown'.\n const ctx = currentSession();\n return {\n sessionId: input.sessionId || ctx?.sessionId || ambientSessionId() || 'unknown',\n agentId: input.agentId || ctx?.agentId || 'main',\n };\n}\n\n/**\n * Stamp + emit a `tool:call`. Returns true if emitted, false if deduped.\n * `nowMs` is injectable for tests; defaults to wall clock.\n */\nexport function emitToolCall(input: ToolCallInput, nowMs?: number): boolean {\n const now = nowMs ?? Date.now();\n if (input.actionId && !shouldEmit(`tool:call|${input.actionId}`, now)) return false;\n const { sessionId, agentId } = resolveIds(input);\n busEmit('tool:call', { ...input, sessionId, agentId, timestamp: new Date(now).toISOString() });\n return true;\n}\n\n/**\n * Stamp + emit a `tool:result`. Returns true if emitted, false if deduped.\n */\nexport function emitToolResult(input: ToolResultInput, nowMs?: number): boolean {\n const now = nowMs ?? Date.now();\n if (input.actionId && !shouldEmit(`tool:result|${input.actionId}`, now)) return false;\n const { sessionId, agentId } = resolveIds(input);\n busEmit('tool:result', { ...input, sessionId, agentId, timestamp: new Date(now).toISOString() });\n return true;\n}\n"],"mappings":";AAkBA,SAAS,QAAQ,eAAyD;AAC1E,SAAS,sBAAsB;AAGxB,MAAM,gBAAgB;AAE7B,MAAM,aAAa;AAEnB,MAAM,OAAO,oBAAI,IAAoB;AAO9B,SAAS,WAAW,KAAa,KAAsB;AAC1D,QAAM,MAAM,KAAK,IAAI,GAAG;AACxB,MAAI,QAAQ,UAAa,MAAM,IAAK,QAAO;AAC3C,OAAK,IAAI,KAAK,MAAM,aAAa;AACjC,MAAI,KAAK,OAAO,YAAY;AACxB,eAAW,CAAC,GAAG,CAAC,KAAK,KAAM,KAAI,KAAK,IAAK,MAAK,OAAO,CAAC;AAAA,EAC1D;AACA,SAAO;AACX;AAGO,SAAS,cAAoB;AAChC,OAAK,MAAM;AACf;AAGO,SAAS,mBAAuC;AACnD,SAAO,QAAQ,IAAI,oBAAoB;AAC3C;AAOA,SAAS,WAA+D,OAAkD;AAItH,QAAM,MAAM,eAAe;AAC3B,SAAO;AAAA,IACH,WAAW,MAAM,aAAa,KAAK,aAAa,iBAAiB,KAAK;AAAA,IACtE,SAAS,MAAM,WAAW,KAAK,WAAW;AAAA,EAC9C;AACJ;AAMO,SAAS,aAAa,OAAsB,OAAyB;AACxE,QAAM,MAAM,SAAS,KAAK,IAAI;AAC9B,MAAI,MAAM,YAAY,CAAC,WAAW,aAAa,MAAM,QAAQ,IAAI,GAAG,EAAG,QAAO;AAC9E,QAAM,EAAE,WAAW,QAAQ,IAAI,WAAW,KAAK;AAC/C,UAAQ,aAAa,EAAE,GAAG,OAAO,WAAW,SAAS,WAAW,IAAI,KAAK,GAAG,EAAE,YAAY,EAAE,CAAC;AAC7F,SAAO;AACX;AAKO,SAAS,eAAe,OAAwB,OAAyB;AAC5E,QAAM,MAAM,SAAS,KAAK,IAAI;AAC9B,MAAI,MAAM,YAAY,CAAC,WAAW,eAAe,MAAM,QAAQ,IAAI,GAAG,EAAG,QAAO;AAChF,QAAM,EAAE,WAAW,QAAQ,IAAI,WAAW,KAAK;AAC/C,UAAQ,eAAe,EAAE,GAAG,OAAO,WAAW,SAAS,WAAW,IAAI,KAAK,GAAG,EAAE,YAAY,EAAE,CAAC;AAC/F,SAAO;AACX;","names":[]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { on as busOn } from "../substrate/traceBus.js";
|
|
3
|
+
const MAX_PER_SESSION = 2e3;
|
|
4
|
+
const MAX_SESSIONS = 50;
|
|
5
|
+
const STUDIO_TOPICS = ["turn:pre", "turn:post", "tool:call", "tool:result"];
|
|
6
|
+
const buffers = /* @__PURE__ */ new Map();
|
|
7
|
+
function recordStudioEvent(topic, payload, nowMs) {
|
|
8
|
+
const sessionId = typeof payload?.sessionId === "string" ? payload.sessionId : void 0;
|
|
9
|
+
if (!sessionId) return;
|
|
10
|
+
const now = nowMs ?? Date.now();
|
|
11
|
+
let ring = buffers.get(sessionId);
|
|
12
|
+
if (ring) {
|
|
13
|
+
buffers.delete(sessionId);
|
|
14
|
+
} else {
|
|
15
|
+
ring = [];
|
|
16
|
+
if (buffers.size >= MAX_SESSIONS) {
|
|
17
|
+
const oldest = buffers.keys().next().value;
|
|
18
|
+
if (oldest !== void 0) buffers.delete(oldest);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
buffers.set(sessionId, ring);
|
|
22
|
+
ring.push({ topic, sessionId, ts: now, payload });
|
|
23
|
+
if (ring.length > MAX_PER_SESSION) ring.splice(0, ring.length - MAX_PER_SESSION);
|
|
24
|
+
}
|
|
25
|
+
function getStudioSession(sessionId) {
|
|
26
|
+
const ring = buffers.get(sessionId);
|
|
27
|
+
return ring ? [...ring] : [];
|
|
28
|
+
}
|
|
29
|
+
function listStudioSessions() {
|
|
30
|
+
return [...buffers.entries()].map(([sessionId, ring]) => ({ sessionId, count: ring.length, lastTs: ring.length ? ring[ring.length - 1].ts : 0 })).sort((a, b) => b.lastTs - a.lastTs);
|
|
31
|
+
}
|
|
32
|
+
function resetStudioBuffer() {
|
|
33
|
+
buffers.clear();
|
|
34
|
+
}
|
|
35
|
+
let attached = false;
|
|
36
|
+
function attachStudioCapture() {
|
|
37
|
+
if (attached) return;
|
|
38
|
+
attached = true;
|
|
39
|
+
for (const topic of STUDIO_TOPICS) {
|
|
40
|
+
busOn(topic, (payload) => recordStudioEvent(topic, payload || {}));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
MAX_PER_SESSION,
|
|
45
|
+
MAX_SESSIONS,
|
|
46
|
+
STUDIO_TOPICS,
|
|
47
|
+
attachStudioCapture,
|
|
48
|
+
getStudioSession,
|
|
49
|
+
listStudioSessions,
|
|
50
|
+
recordStudioEvent,
|
|
51
|
+
resetStudioBuffer
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=studioBuffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/watch/studioBuffer.ts"],"sourcesContent":["/**\n * TITAN — Studio Buffer (Live Studio, v7.2)\n *\n * A small always-on, per-session ring of spine events so a client that opens\n * `/api/studio/stream?sessionId=` mid-task (or reconnects) can replay what it\n * missed instantly, then continue live. Captures the same `tool:*`/`turn:*`\n * topics the event spine now produces (M1–M3), keyed by `sessionId`.\n *\n * Bounded both ways: at most MAX_PER_SESSION events per session and MAX_SESSIONS\n * sessions (LRU by last-write), so memory can't grow unbounded on a long-lived\n * gateway. Pure + injectable clock → unit-testable; capture attach is idempotent.\n */\nimport { on as busOn, type TraceBusTopic } from '../substrate/traceBus.js';\n\nexport interface StudioEvent {\n topic: string;\n sessionId: string;\n ts: number;\n payload: Record<string, unknown>;\n}\n\nexport const MAX_PER_SESSION = 2000;\nexport const MAX_SESSIONS = 50;\n\n/** Topics the Studio records + streams (the live-agents event spine). */\nexport const STUDIO_TOPICS: TraceBusTopic[] = ['turn:pre', 'turn:post', 'tool:call', 'tool:result'];\n\n/** sessionId → ring. Map insertion order doubles as the LRU queue. */\nconst buffers = new Map<string, StudioEvent[]>();\n\n/**\n * Record one spine event into its session ring. No-ops when the payload has no\n * `sessionId` (Studio is session-scoped — the global `/watch` firehose still\n * sees everything). Maintains LRU + both caps.\n */\nexport function recordStudioEvent(topic: string, payload: Record<string, unknown>, nowMs?: number): void {\n const sessionId = typeof payload?.sessionId === 'string' ? payload.sessionId : undefined;\n if (!sessionId) return;\n const now = nowMs ?? Date.now();\n\n let ring = buffers.get(sessionId);\n if (ring) {\n // refresh LRU position\n buffers.delete(sessionId);\n } else {\n ring = [];\n if (buffers.size >= MAX_SESSIONS) {\n const oldest = buffers.keys().next().value;\n if (oldest !== undefined) buffers.delete(oldest);\n }\n }\n buffers.set(sessionId, ring);\n\n ring.push({ topic, sessionId, ts: now, payload });\n if (ring.length > MAX_PER_SESSION) ring.splice(0, ring.length - MAX_PER_SESSION);\n}\n\n/** Snapshot of a session's ring (a copy — safe for the caller to mutate). */\nexport function getStudioSession(sessionId: string): StudioEvent[] {\n const ring = buffers.get(sessionId);\n return ring ? [...ring] : [];\n}\n\n/** Known sessions, most-recently-active first. */\nexport function listStudioSessions(): Array<{ sessionId: string; count: number; lastTs: number }> {\n return [...buffers.entries()]\n .map(([sessionId, ring]) => ({ sessionId, count: ring.length, lastTs: ring.length ? ring[ring.length - 1].ts : 0 }))\n .sort((a, b) => b.lastTs - a.lastTs);\n}\n\n/** Test/maintenance hook. */\nexport function resetStudioBuffer(): void {\n buffers.clear();\n}\n\nlet attached = false;\n/** Idempotently attach the capture listeners to the trace bus. */\nexport function attachStudioCapture(): void {\n if (attached) return;\n attached = true;\n for (const topic of STUDIO_TOPICS) {\n busOn(topic, (payload: unknown) => recordStudioEvent(topic, (payload as Record<string, unknown>) || {}));\n }\n}\n"],"mappings":";AAYA,SAAS,MAAM,aAAiC;AASzC,MAAM,kBAAkB;AACxB,MAAM,eAAe;AAGrB,MAAM,gBAAiC,CAAC,YAAY,aAAa,aAAa,aAAa;AAGlG,MAAM,UAAU,oBAAI,IAA2B;AAOxC,SAAS,kBAAkB,OAAe,SAAkC,OAAsB;AACrG,QAAM,YAAY,OAAO,SAAS,cAAc,WAAW,QAAQ,YAAY;AAC/E,MAAI,CAAC,UAAW;AAChB,QAAM,MAAM,SAAS,KAAK,IAAI;AAE9B,MAAI,OAAO,QAAQ,IAAI,SAAS;AAChC,MAAI,MAAM;AAEN,YAAQ,OAAO,SAAS;AAAA,EAC5B,OAAO;AACH,WAAO,CAAC;AACR,QAAI,QAAQ,QAAQ,cAAc;AAC9B,YAAM,SAAS,QAAQ,KAAK,EAAE,KAAK,EAAE;AACrC,UAAI,WAAW,OAAW,SAAQ,OAAO,MAAM;AAAA,IACnD;AAAA,EACJ;AACA,UAAQ,IAAI,WAAW,IAAI;AAE3B,OAAK,KAAK,EAAE,OAAO,WAAW,IAAI,KAAK,QAAQ,CAAC;AAChD,MAAI,KAAK,SAAS,gBAAiB,MAAK,OAAO,GAAG,KAAK,SAAS,eAAe;AACnF;AAGO,SAAS,iBAAiB,WAAkC;AAC/D,QAAM,OAAO,QAAQ,IAAI,SAAS;AAClC,SAAO,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC;AAC/B;AAGO,SAAS,qBAAkF;AAC9F,SAAO,CAAC,GAAG,QAAQ,QAAQ,CAAC,EACvB,IAAI,CAAC,CAAC,WAAW,IAAI,OAAO,EAAE,WAAW,OAAO,KAAK,QAAQ,QAAQ,KAAK,SAAS,KAAK,KAAK,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAClH,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AAC3C;AAGO,SAAS,oBAA0B;AACtC,UAAQ,MAAM;AAClB;AAEA,IAAI,WAAW;AAER,SAAS,sBAA4B;AACxC,MAAI,SAAU;AACd,aAAW;AACX,aAAW,SAAS,eAAe;AAC/B,UAAM,OAAO,CAAC,YAAqB,kBAAkB,OAAQ,WAAuC,CAAC,CAAC,CAAC;AAAA,EAC3G;AACJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "titan-agent",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "TITAN —
|
|
3
|
+
"version": "7.0.0",
|
|
4
|
+
"description": "TITAN — the AI agent that provably teaches itself. TypeScript agent framework with replay-verified self-learned skills (Muscle Memory), one-minute Welcome Mode setup, a living desk with heartbeat/reminders/proactive memory, multi-agent orchestration, 36-provider routing, approval gates, receipts, evals, and a React Mission Control dashboard. Open-source, MIT.",
|
|
5
5
|
"author": "Tony Elliott (https://github.com/Djtony707)",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -122,7 +122,8 @@
|
|
|
122
122
|
"research-agent",
|
|
123
123
|
"ai-gateway",
|
|
124
124
|
"model-routing",
|
|
125
|
-
"agent-delegation"
|
|
125
|
+
"agent-delegation",
|
|
126
|
+
"local-first"
|
|
126
127
|
],
|
|
127
128
|
"engines": {
|
|
128
129
|
"node": ">=22.0.0"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import{c as x,u as p,r as l,b as y,j as e,E as u,W as j}from"./index-Cml2x9pA.js";/**
|
|
2
|
+
* @license lucide-react v0.513.0 - ISC
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the ISC license.
|
|
5
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/const v=[["path",{d:"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z",key:"c3ymky"}]],g=x("heart",v);/**
|
|
7
|
+
* @license lucide-react v0.513.0 - ISC
|
|
8
|
+
*
|
|
9
|
+
* This source code is licensed under the ISC license.
|
|
10
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11
|
+
*/const k=[["path",{d:"M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z",key:"qazsjp"}],["path",{d:"M15 3v4a2 2 0 0 0 2 2h4",key:"40519r"}]],d=x("sticky-note",k);function o({icon:i,title:s,children:c}){return e.jsxs("div",{className:"rounded-xl border px-5 py-4 shadow-md",style:{background:"var(--theme-paper, var(--color-bg-secondary))",borderColor:"var(--theme-paper-line, var(--color-border))",color:"var(--theme-ink, var(--color-text))"},children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3 text-sm font-semibold",style:{fontFamily:"var(--theme-font-display, inherit)"},children:[e.jsx("span",{style:{color:"var(--theme-accent)"},children:i})," ",s]}),c]})}function N(){const i=p(),[s,c]=l.useState(null),[m,h]=l.useState(null);l.useEffect(()=>{y("/api/memory/about-me",{headers:{"Content-Type":"application/json"}}).then(t=>t.json()).then(t=>t.error?h(t.message||t.error):c(t)).catch(t=>h(t.message))},[]);const r=new Map;for(const t of(s==null?void 0:s.facts)??[]){const a=r.get(t.category)||[];a.push(t),r.set(t.category,a)}return e.jsxs("div",{className:"max-w-2xl mx-auto px-4 py-8 space-y-4",children:[e.jsx("h1",{className:"text-2xl font-semibold",style:{color:"var(--theme-ink, var(--color-text))",fontFamily:"var(--theme-font-display, inherit)"},children:"What I know about you"}),e.jsx("p",{className:"text-sm",style:{color:"var(--theme-ink-soft, var(--color-text-muted))"},children:"Everything here was learned from working together. Ask me in chat to remember or forget anything."}),m&&e.jsx(o,{icon:e.jsx(d,{size:16}),title:"Hmm",children:e.jsxs("p",{className:"text-sm",children:["I couldn't read my memory just now: ",m]})}),s&&e.jsxs(e.Fragment,{children:[e.jsx(o,{icon:e.jsx(g,{size:16}),title:"Who you've asked me to be",children:e.jsx("p",{className:"text-[14px] leading-relaxed whitespace-pre-wrap",children:s.identity||"Nothing set yet — tell me in chat, or set my identity in the Workshop."})}),e.jsx(o,{icon:e.jsx(u,{size:16}),title:"Things I've noticed",children:s.observations.length===0?e.jsx("p",{className:"text-sm opacity-70",children:"Nothing yet — I pick these up over time as we work."}):e.jsx("ul",{className:"space-y-1.5 text-[14px] leading-relaxed list-disc pl-5",children:s.observations.slice(-25).reverse().map((t,a)=>e.jsx("li",{children:t},a))})}),e.jsx(o,{icon:e.jsx(d,{size:16}),title:"Facts I keep",children:r.size===0?e.jsx("p",{className:"text-sm opacity-70",children:'No stored facts yet — say "remember that …" in chat.'}):e.jsx("div",{className:"space-y-3",children:[...r.entries()].map(([t,a])=>e.jsxs("div",{children:[e.jsx("div",{className:"text-[11px] uppercase tracking-wide mb-1 opacity-60",children:t}),e.jsx("ul",{className:"space-y-1 text-[13.5px]",children:a.slice(0,12).map(n=>e.jsxs("li",{children:[e.jsxs("span",{className:"opacity-70",children:[n.key,":"]})," ",n.value]},n.key))})]},t))})})]}),e.jsxs("button",{onClick:()=>i("/knowledge/memory-graph"),className:"flex items-center gap-1.5 text-xs hover:underline underline-offset-2",style:{color:"var(--theme-ink-soft, var(--color-text-muted))"},children:[e.jsx(j,{size:12})," The deep memory tools (graph, wiki, taxonomy) live in the Workshop"]})]})}export{N as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as S,r as d,j as t,aO as A,aP as C}from"./index-Cml2x9pA.js";import{P as T}from"./PageHeader-DdrbS1aq.js";import{R as $}from"./refresh-cw-C-iZkzuS.js";import{F as R}from"./funnel-FycfZcXo.js";import{C as F}from"./clipboard-list-Dj9tORnu.js";import"./chevron-right-DbLSHmff.js";function q(){var b;const{toast:v}=S(),[l,p]=d.useState([]),[r,h]=d.useState(null),[g,i]=d.useState(!0),[a,c]=d.useState(""),[n,x]=d.useState(""),u=async()=>{i(!0);try{const[e,s]=await Promise.all([A({source:a||void 0,action:n||void 0,limit:100}),C(24)]);p(e),h(s)}catch(e){v("error",`Couldn't load audit log — try again. (${e.message})`)}i(!1)};d.useEffect(()=>{u()},[a,n]);const j=e=>e==="success"?"text-[var(--success)]":e==="failure"?"text-[var(--error)]":e==="escalated"?"text-[var(--warning)]":"text-[var(--text-muted)]",f=e=>e==="success"?"bg-[var(--success)]/10 border-[var(--success)]/20":e==="failure"?"bg-[var(--error)]/10 border-[var(--error)]/20":e==="escalated"?"bg-[var(--warning)]/10 border-[var(--warning)]/20":"bg-[var(--bg-tertiary)] border-[var(--border)]",m=e=>e.startsWith("daemon")?"bg-purple-500/10 text-purple-400 border-purple-500/20":e==="autopilot"?"bg-blue-500/10 text-blue-400 border-blue-500/20":e==="user"?"bg-emerald-500/10 text-emerald-400 border-emerald-500/20":e==="initiative"?"bg-amber-500/10 text-amber-400 border-amber-500/20":"bg-[var(--bg-tertiary)] text-[var(--text-muted)] border-[var(--border)]",N=e=>{try{const s=new Date(e),o=new Date().getTime()-s.getTime();return o<6e4?`${Math.round(o/1e3)}s ago`:o<36e5?`${Math.round(o/6e4)}m ago`:o<864e5?`${Math.round(o/36e5)}h ago`:s.toLocaleDateString()}catch{return e}},y=[...new Set(l.map(e=>e.source))].sort(),w=[...new Set(l.map(e=>e.action))].sort();return t.jsxs("div",{className:"space-y-6",children:[t.jsx(T,{title:"Audit Log",subtitle:"Every autonomous action logged for accountability",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"System"},{label:"Audit"}],actions:t.jsx("button",{onClick:u,className:"p-2 rounded-md border border-[var(--border)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] transition-colors",children:t.jsx($,{size:14})})}),r&&t.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4",children:[t.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[t.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Actions (24h)"}),t.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:r.totalActions})]}),t.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[t.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Success Rate"}),t.jsxs("p",{className:`text-2xl font-bold ${r.successRate>=80?"text-[var(--success)]":r.successRate>=50?"text-[var(--warning)]":"text-[var(--error)]"}`,children:[r.successRate,"%"]})]}),t.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[t.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Sources"}),t.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:Object.keys(r.bySource).length})]}),t.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[t.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Top Tool"}),t.jsx("p",{className:"text-lg font-semibold text-[var(--accent)] truncate",children:((b=r.topTools[0])==null?void 0:b.tool)||"none"})]})]}),r&&Object.keys(r.bySource).length>0&&t.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[t.jsx("h2",{className:"text-sm font-semibold text-[var(--text)] mb-3",children:"Actions by Source (24h)"}),t.jsx("div",{className:"flex flex-wrap gap-2",children:Object.entries(r.bySource).sort((e,s)=>s[1]-e[1]).map(([e,s])=>t.jsxs("button",{onClick:()=>c(a===e?"":e),className:`px-3 py-1.5 text-xs rounded-md border transition-colors ${a===e?"bg-[var(--accent)] text-white border-[var(--accent)]":m(e)}`,children:[e,": ",s]},e))})]}),t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx(R,{size:14,className:"text-[var(--text-muted)]"}),t.jsxs("select",{value:a,onChange:e=>c(e.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[t.jsx("option",{value:"",children:"All Sources"}),y.map(e=>t.jsx("option",{value:e,children:e},e))]}),t.jsxs("select",{value:n,onChange:e=>x(e.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[t.jsx("option",{value:"",children:"All Actions"}),w.map(e=>t.jsx("option",{value:e,children:e},e))]}),(a||n)&&t.jsx("button",{onClick:()=>{c(""),x("")},className:"text-xs text-[var(--accent)] hover:underline",children:"Clear"})]}),g?t.jsx("div",{className:"text-[var(--text-muted)] text-sm",children:"Loading audit log..."}):l.length===0?t.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-8 text-center",children:[t.jsx(F,{className:"w-10 h-10 text-[var(--text-muted)] mx-auto mb-3"}),t.jsx("p",{className:"text-[var(--text-secondary)]",children:"No audit entries found"}),t.jsx("p",{className:"text-xs text-[var(--text-muted)]",children:"Autonomous actions will appear here as TITAN operates."})]}):t.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg overflow-hidden",children:t.jsx("div",{className:"overflow-x-auto",children:t.jsxs("table",{className:"w-full text-xs",children:[t.jsx("thead",{children:t.jsxs("tr",{className:"border-b border-[var(--border)] text-[var(--text-muted)]",children:[t.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Time"}),t.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Action"}),t.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Source"}),t.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Tool"}),t.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Result"}),t.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Detail"})]})}),t.jsx("tbody",{children:l.map((e,s)=>t.jsxs("tr",{className:"border-b border-[var(--border)]/50 last:border-0 hover:bg-[var(--bg-tertiary)] transition-colors",children:[t.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] whitespace-nowrap",children:N(e.timestamp)}),t.jsx("td",{className:"px-4 py-2 text-[var(--text)] font-medium",children:e.action}),t.jsx("td",{className:"px-4 py-2",children:t.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${m(e.source)}`,children:e.source})}),t.jsx("td",{className:"px-4 py-2 text-[var(--text-secondary)] font-mono",children:e.tool||"-"}),t.jsx("td",{className:"px-4 py-2",children:e.result?t.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${f(e.result)} ${j(e.result)}`,children:e.result}):"-"}),t.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] max-w-[200px] truncate",children:e.detail?JSON.stringify(e.detail):"-"})]},s))})]})})})]})}export{q as default};
|