titan-agent 5.4.1 → 5.4.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/skills/registry.ts"],"sourcesContent":["/**\n * TITAN — Skills Registry\n * Discovers, loads, and manages skills from bundled, workspace, and marketplace sources.\n */\nimport { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport vm from 'vm';\nimport { TITAN_HOME, TITAN_SKILLS_DIR } from '../utils/constants.js';\nimport { registerTool, type ToolHandler } from '../agent/toolRunner.js';\nimport { ensureDir } from '../utils/helpers.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'Skills';\nconst DISABLED_SKILLS_PATH = join(TITAN_HOME, 'disabled-skills.json');\n\nexport interface SkillMeta {\n name: string;\n description: string;\n version: string;\n author?: string;\n source: 'bundled' | 'workspace' | 'marketplace' | 'frontmatter';\n enabled: boolean;\n}\n\nconst registeredSkills: Map<string, SkillMeta> = new Map();\n\n/** Maps skill name → tool names belonging to that skill */\nconst skillToolMap: Map<string, Set<string>> = new Map();\n\n/** Register a built-in skill (tool handler + metadata) */\nexport function registerSkill(meta: SkillMeta, handler: ToolHandler): void {\n registeredSkills.set(meta.name, meta);\n // Track which tools belong to this skill\n if (!skillToolMap.has(meta.name)) {\n skillToolMap.set(meta.name, new Set());\n }\n skillToolMap.get(meta.name)!.add(handler.name);\n registerTool(handler);\n logger.debug(COMPONENT, `Registered skill: ${meta.name} (${meta.source})`);\n}\n\n/** Get all registered skills (with persisted enabled/disabled state applied) */\nexport function getSkills(): SkillMeta[] {\n const disabled = loadDisabledSkills();\n return Array.from(registeredSkills.values()).map(s => ({\n ...s,\n enabled: !disabled.includes(s.name),\n }));\n}\n\n/** Get a skill by name */\nexport function getSkill(name: string): SkillMeta | undefined {\n return registeredSkills.get(name);\n}\n\n/** Get tool names belonging to a skill */\nexport function getSkillTools(skillName: string): string[] {\n return Array.from(skillToolMap.get(skillName) || []);\n}\n\n/** Check if a skill is enabled */\nexport function isSkillEnabled(skillName: string): boolean {\n return !loadDisabledSkills().includes(skillName);\n}\n\n/** Check if a specific tool's parent skill is enabled */\nexport function isToolSkillEnabled(toolName: string): boolean {\n for (const [skillName, tools] of skillToolMap.entries()) {\n if (tools.has(toolName)) {\n return isSkillEnabled(skillName);\n }\n }\n return true; // Tools not belonging to any skill are always enabled\n}\n\n/** Toggle a skill on/off. Returns the new enabled state. */\nexport function toggleSkill(skillName: string): boolean {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n let nowEnabled: boolean;\n\n if (idx >= 0) {\n disabled.splice(idx, 1);\n nowEnabled = true;\n } else {\n disabled.push(skillName);\n nowEnabled = false;\n }\n\n saveDisabledSkills(disabled);\n logger.info(COMPONENT, `Skill \"${skillName}\" ${nowEnabled ? 'enabled' : 'disabled'}`);\n return nowEnabled;\n}\n\n/** Set a skill's enabled state explicitly */\nexport function setSkillEnabled(skillName: string, enabled: boolean): void {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n\n if (enabled && idx >= 0) {\n disabled.splice(idx, 1);\n } else if (!enabled && idx < 0) {\n disabled.push(skillName);\n }\n\n saveDisabledSkills(disabled);\n}\n\n/** Load disabled skills list from disk */\nfunction loadDisabledSkills(): string[] {\n try {\n if (existsSync(DISABLED_SKILLS_PATH)) {\n return JSON.parse(readFileSync(DISABLED_SKILLS_PATH, 'utf-8')) as string[];\n }\n } catch {\n // Corrupt file — treat as empty\n }\n return [];\n}\n\n/** Save disabled skills list to disk */\nfunction saveDisabledSkills(disabled: string[]): void {\n try {\n const dir = dirname(DISABLED_SKILLS_PATH);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(DISABLED_SKILLS_PATH, JSON.stringify(disabled, null, 2), 'utf-8');\n } catch (e) {\n logger.warn(COMPONENT, `Failed to save disabled skills: ${(e as Error).message}`);\n }\n}\n\n/** Discover workspace skills from ~/.titan/workspace/skills/ */\nexport function discoverWorkspaceSkills(): SkillMeta[] {\n ensureDir(TITAN_SKILLS_DIR);\n const discovered: SkillMeta[] = [];\n\n if (!existsSync(TITAN_SKILLS_DIR)) return discovered;\n\n const entries = readdirSync(TITAN_SKILLS_DIR, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const skillDir = join(TITAN_SKILLS_DIR, entry.name);\n const skillMdPath = join(skillDir, 'SKILL.md');\n\n if (!existsSync(skillMdPath)) continue;\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8');\n const meta = parseSkillMd(content, entry.name);\n if (meta) {\n discovered.push({ ...meta, source: 'workspace', enabled: true });\n }\n } catch (error) {\n logger.warn(COMPONENT, `Failed to load skill ${entry.name}: ${(error as Error).message}`);\n }\n }\n\n logger.info(COMPONENT, `Discovered ${discovered.length} workspace skills`);\n return discovered;\n}\n\n/** Parse SKILL.md frontmatter to extract metadata */\nfunction parseSkillMd(content: string, fallbackName: string): Omit<SkillMeta, 'source' | 'enabled'> | null {\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!frontmatterMatch) {\n return {\n name: fallbackName,\n description: content.split('\\n')[0] || 'No description',\n version: '1.0.0',\n };\n }\n\n const frontmatter = frontmatterMatch[1];\n const name = frontmatter.match(/name:\\s*(.+)/)?.[1]?.trim() || fallbackName;\n const description = frontmatter.match(/description:\\s*(.+)/)?.[1]?.trim() || 'No description';\n const version = frontmatter.match(/version:\\s*(.+)/)?.[1]?.trim() || '1.0.0';\n const author = frontmatter.match(/author:\\s*(.+)/)?.[1]?.trim();\n\n return { name, description, version, author };\n}\n\n/** Initialize all built-in skills */\nexport async function initBuiltinSkills(): Promise<void> {\n logger.info(COMPONENT, 'Loading built-in skills...');\n\n // Import and register built-in skills\n const { registerShellSkill } = await import('./builtin/shell.js');\n const { registerFilesystemSkill } = await import('./builtin/filesystem.js');\n const { registerWebSearchSkill } = await import('./builtin/web_search.js');\n const { registerCronSkill } = await import('./builtin/cron.js');\n const { registerWebhookSkill } = await import('./builtin/webhook.js');\n const { registerMemorySkill } = await import('./builtin/memory_skill.js');\n const { registerBrowserSkill } = await import('./builtin/browser.js');\n const { registerSessionsSkill } = await import('./builtin/sessions.js');\n const { registerProcessSkill } = await import('./builtin/process.js');\n const { registerWebFetchSkill } = await import('./builtin/web_fetch.js');\n const { registerApplyPatchSkill } = await import('./builtin/apply_patch.js');\n const { registerAutoGenerateSkill } = await import('./builtin/auto_generate.js');\n const { registerVisionSkill } = await import('./builtin/vision.js');\n const { registerVoiceSkills } = await import('./builtin/voice.js');\n const { registerMemoryGraphSkill } = await import('./builtin/memory_graph.js');\n const { initWebBrowserTool } = await import('./builtin/web_browser.js');\n const { registerGitHubSkill } = await import('./builtin/github.js');\n const { registerEmailSkill } = await import('./builtin/email.js');\n const { registerComputerUseSkill } = await import('./builtin/computer_use.js');\n const { registerImageGenSkill } = await import('./builtin/image_gen.js');\n const { registerPdfSkill } = await import('./builtin/pdf.js');\n const { registerCalendarSkill } = await import('./builtin/calendar.js');\n const { registerSmartHomeSkill } = await import('./builtin/smart_home.js');\n const { registerDataAnalysisSkill } = await import('./builtin/data_analysis.js');\n const { registerSkyvernSkill } = await import('./builtin/skyvern.js');\n const { registerWebBrowseLlmSkill } = await import('./builtin/web_browse_llm.js');\n const { registerIncomeTrackerSkill } = await import('./builtin/income_tracker.js');\n const { registerFreelanceMonitorSkill } = await import('./builtin/freelance_monitor.js');\n const { registerContentPublisherSkill } = await import('./builtin/content_publisher.js');\n const { registerLeadScorerSkill } = await import('./builtin/lead_scorer.js');\n const { registerHunterSkill } = await import('./builtin/hunter.js');\n const { registerCodeExecSkill } = await import('./builtin/code_exec.js');\n const { registerExecuteCodeSkill } = await import('./builtin/executeCode.js');\n const { registerWeatherSkill } = await import('./builtin/weather.js');\n const { registerGoalsSkill } = await import('./builtin/goals.js');\n const { registerXPosterSkill } = await import('./builtin/x_poster.js');\n const { initModelSwitchTool } = await import('./builtin/model_switch.js');\n const { registerRagSkill } = await import('./builtin/rag.js');\n const { registerDeepResearchSkill } = await import('./builtin/deep_research.js');\n const { registerSystemInfoSkill } = await import('./builtin/system_info.js');\n const { registerPersonaManagerSkill } = await import('./builtin/persona_manager.js');\n const { registerResearchPipelineSkill } = await import('./builtin/research_pipeline.js');\n const { registerAutoresearchSkill } = await import('./builtin/autoresearch.js');\n const { registerSelfDoctorSkill } = await import('./builtin/self_doctor.js');\n const { registerInteractionTrackerSkill } = await import('./builtin/interaction_tracker.js');\n const { registerFeedbackTrackerSkill } = await import('./builtin/feedback_tracker.js');\n const { registerGrowthExperimentsSkill } = await import('./builtin/growth_experiments.js');\n const { registerContentCalendarSkill } = await import('./builtin/content_calendar.js');\n const { registerSlackSkill } = await import('./builtin/slack.js');\n const { registerRevenueCatKBSkill } = await import('./builtin/revenuecat_kb.js');\n const { registerWeeklyReportSkill } = await import('./builtin/weekly_report.js');\n const { registerSelfImproveSkill } = await import('./builtin/self_improve.js');\n const { registerGepaSkill } = await import('./builtin/gepa.js');\n const { registerModelTrainerSkill } = await import('./builtin/model_trainer.js');\n const { registerSocialSchedulerSkill } = await import('./builtin/social_scheduler.js');\n const { registerStructuredOutputSkill } = await import('./builtin/structured_output.js');\n const { registerWorkflowsSkill } = await import('./builtin/workflows.js');\n const { registerAgentHandoffSkill } = await import('./builtin/agent_handoff.js');\n const { registerKnowledgeBaseSkill } = await import('./builtin/knowledge_base.js');\n const { registerEventTriggersSkill } = await import('./builtin/event_triggers.js');\n const { registerA2AProtocolSkill } = await import('./builtin/a2a_protocol.js');\n const { registerEvalsSkill } = await import('./builtin/evals.js');\n const { registerApprovalGatesSkill } = await import('./builtin/approval_gates.js');\n const { registerVRAMSkills } = await import('./builtin/vram.js');\n const { registerSecurityScanSkill } = await import('./builtin/security_scan.js');\n const { registerChangelogGenSkill } = await import('./builtin/changelog_gen.js');\n const { registerJiraLinearSkill } = await import('./builtin/jira_linear.js');\n const { registerAuditTrailSkill } = await import('./builtin/audit_trail.js');\n const { registerVisualPlanSkill } = await import('./builtin/visual_plan.js');\n const { registerScreenRecordSkill } = await import('./builtin/screen_record.js');\n const { registerSessionTeleportSkill } = await import('./builtin/session_teleport.js');\n const { registerCrossProviderSkill } = await import('./builtin/cross_provider.js');\n const { registerSentrySkill } = await import('./builtin/sentry.js');\n const { registerVideoSkill } = await import('./builtin/video.js');\n const { registerMixtureOfAgentsSkill } = await import('./builtin/mixture_of_agents.js');\n const { registerAgentDebateSkill } = await import('./builtin/agent_debate.js');\n const { registerFileCheckpointsSkill } = await import('./builtin/file_checkpoints.js');\n const { registerVerifyPageSkill } = await import('./builtin/verify_page.js');\n const { registerAgentMessagingSkill } = await import('./builtin/agent_messaging.js');\n const { registerFacebookSkill } = await import('./builtin/facebook.js');\n const { registerFBAutopilotSkill } = await import('./builtin/fb_autopilot.js');\n const { registerWidgetGallerySkill } = await import('./builtin/widget_gallery.js');\n\n const registrations: [string, () => void][] = [\n ['shell', registerShellSkill],\n ['filesystem', registerFilesystemSkill],\n ['web_search', registerWebSearchSkill],\n ['cron', registerCronSkill],\n ['webhook', registerWebhookSkill],\n ['memory', registerMemorySkill],\n ['browser', registerBrowserSkill],\n ['sessions', registerSessionsSkill],\n ['process', registerProcessSkill],\n ['web_fetch', registerWebFetchSkill],\n ['apply_patch', registerApplyPatchSkill],\n ['auto_generate', registerAutoGenerateSkill],\n ['vision', registerVisionSkill],\n ['voice', registerVoiceSkills],\n ['memory_graph', registerMemoryGraphSkill],\n ['web_browser', initWebBrowserTool],\n ['github', registerGitHubSkill],\n ['email', registerEmailSkill],\n ['computer_use', registerComputerUseSkill],\n ['image_gen', registerImageGenSkill],\n ['pdf', registerPdfSkill],\n ['calendar', registerCalendarSkill],\n ['smart_home', registerSmartHomeSkill],\n ['data_analysis', registerDataAnalysisSkill],\n ['skyvern', registerSkyvernSkill],\n ['web_browse_llm', registerWebBrowseLlmSkill],\n ['income_tracker', registerIncomeTrackerSkill],\n ['freelance_monitor', registerFreelanceMonitorSkill],\n ['content_publisher', registerContentPublisherSkill],\n ['lead_scorer', registerLeadScorerSkill],\n ['hunter', registerHunterSkill],\n ['code_exec', registerCodeExecSkill],\n ['execute_code', registerExecuteCodeSkill],\n ['weather', registerWeatherSkill],\n ['goals', registerGoalsSkill],\n ['x_poster', registerXPosterSkill],\n ['model_switch', initModelSwitchTool],\n ['rag', registerRagSkill],\n ['deep_research', registerDeepResearchSkill],\n ['system_info', registerSystemInfoSkill],\n ['persona_manager', registerPersonaManagerSkill],\n ['research_pipeline', registerResearchPipelineSkill],\n ['autoresearch', registerAutoresearchSkill],\n ['self_doctor', registerSelfDoctorSkill],\n ['interaction_tracker', registerInteractionTrackerSkill],\n ['feedback_tracker', registerFeedbackTrackerSkill],\n ['growth_experiments', registerGrowthExperimentsSkill],\n ['content_calendar', registerContentCalendarSkill],\n ['slack', registerSlackSkill],\n ['revenuecat_kb', registerRevenueCatKBSkill],\n ['weekly_report', registerWeeklyReportSkill],\n ['self_improve', registerSelfImproveSkill],\n ['gepa', registerGepaSkill],\n ['model_trainer', registerModelTrainerSkill],\n ['social_scheduler', registerSocialSchedulerSkill],\n ['structured_output', registerStructuredOutputSkill],\n ['workflows', registerWorkflowsSkill],\n ['agent_handoff', registerAgentHandoffSkill],\n ['knowledge_base', registerKnowledgeBaseSkill],\n ['event_triggers', registerEventTriggersSkill],\n ['evals', registerEvalsSkill],\n ['a2a_protocol', registerA2AProtocolSkill],\n ['approval_gates', registerApprovalGatesSkill],\n ['vram', registerVRAMSkills],\n ['security_scan', registerSecurityScanSkill],\n ['changelog_gen', registerChangelogGenSkill],\n ['jira_linear', registerJiraLinearSkill],\n ['audit_trail', registerAuditTrailSkill],\n ['visual_plan', registerVisualPlanSkill],\n ['screen_record', registerScreenRecordSkill],\n ['session_teleport', registerSessionTeleportSkill],\n ['cross_provider', registerCrossProviderSkill],\n ['sentry', registerSentrySkill],\n ['video', registerVideoSkill],\n ['mixture_of_agents', registerMixtureOfAgentsSkill],\n ['agent_debate', registerAgentDebateSkill],\n ['file_checkpoints', registerFileCheckpointsSkill],\n ['verify_page', registerVerifyPageSkill],\n ['agent_messaging', registerAgentMessagingSkill],\n ['facebook', registerFacebookSkill],\n ['fb_autopilot', registerFBAutopilotSkill],\n ['widget_gallery', registerWidgetGallerySkill],\n ];\n\n for (const [name, fn] of registrations) {\n try { fn(); } catch (e) { logger.warn(COMPONENT, `Failed to register skill \"${name}\": ${(e as Error).message}`); }\n }\n\n // Register planner as an LLM-invocable tool\n const { registerPlannerTool } = await import('../agent/planner.js');\n try { registerPlannerTool(); } catch (e) { logger.warn(COMPONENT, `Failed to register planner: ${(e as Error).message}`); }\n\n // Register TopFacts context engine plugin (DeerFlow-inspired persistent memory)\n try {\n const { createTopFactsPlugin } = await import('../plugins/topFacts.js');\n const { createMemoryRetrievalPlugin } = await import('../plugins/memoryRetrieval.js');\n const { registerPlugin } = await import('../plugins/registry.js');\n const topFacts = createTopFactsPlugin();\n registerPlugin(topFacts);\n if (topFacts.bootstrap) await topFacts.bootstrap({});\n const memoryRetrieval = createMemoryRetrievalPlugin();\n registerPlugin(memoryRetrieval);\n if (memoryRetrieval.bootstrap) await memoryRetrieval.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register TopFacts plugin: ${(e as Error).message}`); }\n\n // Register SmartCompress context engine plugin (task-type-aware compression)\n try {\n const { createSmartCompressPlugin } = await import('../plugins/smartCompress.js');\n const { registerPlugin: regPlugin } = await import('../plugins/registry.js');\n const smartCompress = createSmartCompressPlugin();\n regPlugin(smartCompress);\n if (smartCompress.bootstrap) await smartCompress.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register SmartCompress plugin: ${(e as Error).message}`); }\n\n // Register tool_search + tool_expand — meta-tools for discovering tools on demand\n // tool_expand is the progressive disclosure extension (Hermes competitive gap fix)\n const { getToolSearchHandler, getToolExpandHandler } = await import('../agent/toolSearch.js');\n try { registerTool(getToolSearchHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_search: ${(e as Error).message}`); }\n try { registerTool(getToolExpandHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_expand: ${(e as Error).message}`); }\n\n // F3: Register procedural memory tools (Hermes-inspired skill learning)\n try {\n const { saveSkill, searchSkills } = await import('./proceduralMemory.js');\n registerTool({\n name: 'save_skill',\n description: 'Save a reusable approach/technique as a procedural skill for future tasks. Use this when you discover an effective approach that could help in similar future situations.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Short descriptive name for the skill (e.g., \"Deploy Node.js app to Docker\")' },\n tags: { type: 'array', items: { type: 'string' }, description: 'Tags for searchability (e.g., [\"docker\", \"deployment\", \"nodejs\"])' },\n content: { type: 'string', description: 'The reusable approach/technique in markdown format. Include key steps, commands, and gotchas.' },\n },\n required: ['name', 'tags', 'content'],\n },\n execute: async (args: Record<string, unknown>) => {\n const name = args.name as string;\n const tags = (args.tags as string[]) || [];\n const content = args.content as string;\n if (!name || !content) return 'Error: name and content are required';\n const skill = saveSkill(name, tags, content);\n return `Skill saved: \"${skill.name}\" (tags: ${skill.tags.join(', ')}). It will be auto-recalled in future tasks matching these tags.`;\n },\n });\n registerTool({\n name: 'recall_skill',\n description: 'Search for previously saved procedural skills by keyword or tag. Returns reusable approaches from past tasks.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query — keywords or tags to find relevant skills' },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = args.query as string;\n if (!query) return 'Error: query is required';\n const results = searchSkills(query, 5);\n if (results.length === 0) return 'No matching skills found. Consider saving useful approaches with save_skill.';\n return results.map(s =>\n `### ${s.name}\\nTags: ${s.tags.join(', ')} | Used ${s.useCount}x\\n${s.content.slice(0, 800)}`\n ).join('\\n\\n---\\n\\n');\n },\n });\n logger.info(COMPONENT, 'Registered procedural memory tools (save_skill, recall_skill)');\n } catch (e) { logger.warn(COMPONENT, `Failed to register procedural memory tools: ${(e as Error).message}`); }\n\n logger.info(COMPONENT, `Loaded ${registeredSkills.size} built-in skills`);\n\n // Load dev skills (only in dev mode — skip import entirely in production)\n if (process.env.NODE_ENV !== 'production' || process.env.TITAN_DEV) {\n const { initDevSkills } = await import('./dev/loader.js');\n await initDevSkills();\n }\n\n // Load NVIDIA skills (optional — only when TITAN_NVIDIA=1 or nvidia.enabled in config)\n try {\n let nvidiaEnabled = process.env.TITAN_NVIDIA === '1';\n if (!nvidiaEnabled) {\n try {\n const { loadConfig: _loadConfig } = await import('../config/config.js');\n const cfg = _loadConfig() as Record<string, unknown>;\n const nvCfg = cfg.nvidia as Record<string, unknown> | undefined;\n nvidiaEnabled = nvCfg?.enabled === true;\n } catch { /* config not available in test env */ }\n }\n if (nvidiaEnabled) {\n const { initNvidiaSkills } = await import('./nvidia/loader.js');\n await initNvidiaSkills();\n }\n } catch (err) {\n logger.warn(COMPONENT, `NVIDIA skills failed to load: ${(err as Error).message}`);\n }\n\n // Load personal skills (private, gitignored — only when TITAN_PERSONAL=1)\n // Primary location: dist/skills/personal/loader.js (co-located with dist/skills/registry.js\n // so `../registry` resolves to the SAME module instance — tools register into the correct registry)\n // Fallback: ~/.titan/personal/loader.js (legacy / TITAN_PERSONAL_DIR override)\n if (process.env.TITAN_PERSONAL === '1') {\n try {\n const { pathToFileURL, fileURLToPath } = await import('node:url');\n const { join: _join, dirname: _dirname } = await import('node:path');\n // Compute dist/skills/ dir from this file's location (works on any machine)\n const thisDir = _dirname(fileURLToPath(import.meta.url));\n const distPersonalDir = _join(thisDir, 'personal');\n // TITAN_PERSONAL_DIR env var overrides; otherwise try dist-local first, then ~/.titan/personal/\n const personalDir = process.env.TITAN_PERSONAL_DIR\n || (existsSync(_join(distPersonalDir, 'loader.js')) ? distPersonalDir : _join(TITAN_HOME, 'personal'));\n const loaderPath = _join(personalDir, 'loader.js');\n if (existsSync(loaderPath)) {\n // Inject the main app's registerSkill into a global so the personal bundle\n // (which has its own bundled copy) uses the correct shared toolRegistry instance.\n (globalThis as Record<string, unknown>).__titanRegisterSkill = registerSkill;\n const { initPersonalSkills } = await import(pathToFileURL(loaderPath).href) as { initPersonalSkills: () => Promise<void> };\n await initPersonalSkills();\n } else {\n logger.warn(COMPONENT, `TITAN_PERSONAL=1 but ${loaderPath} not found — run: npm run build:personal`);\n }\n } catch (err) {\n logger.warn(COMPONENT, `Personal skills failed to load: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Discover and load user skills from ~/.titan/skills/ (all subdirs).\n * Supports:\n * 1. JavaScript files (.js) that export default { name, description, parameters, execute }\n * 2. YAML skill definitions (.yaml/.yml) with inline scripts\n * 3. Auto-generated skills from ~/.titan/skills/auto/\n */\nexport async function loadAutoSkills(): Promise<void> {\n const skillsRoot = join(TITAN_HOME, 'skills');\n if (!existsSync(skillsRoot)) return;\n\n logger.info(COMPONENT, 'Scanning for user skills...');\n let loadedCount = 0;\n\n // Scan both root and all subdirectories\n const dirsToScan = [skillsRoot];\n const entries = readdirSync(skillsRoot, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) dirsToScan.push(join(skillsRoot, entry.name));\n }\n\n for (const dir of dirsToScan) {\n const files = readdirSync(dir).filter(f =>\n f.endsWith('.js') || f.endsWith('.yaml') || f.endsWith('.yml')\n );\n\n for (const file of files) {\n const filePath = join(dir, file);\n try {\n if (file.endsWith('.js')) {\n // JavaScript skill — export default { name, description, parameters, execute }\n const modulePath = `file://${filePath}?t=${Date.now()}`;\n const mod = await import(modulePath);\n if (mod.default && mod.default.name && mod.default.execute) {\n const handler = mod.default as ToolHandler;\n if (registeredSkills.has(handler.name)) continue; // Skip duplicates\n registerSkill({\n name: handler.name,\n description: handler.description || 'User skill',\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, handler);\n loadedCount++;\n }\n } else {\n // YAML skill definition\n const loaded = loadYamlSkill(filePath);\n if (loaded && !registeredSkills.has(loaded.name)) {\n registerSkill({\n name: loaded.name,\n description: loaded.description,\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, loaded);\n loadedCount++;\n }\n }\n } catch (e: unknown) {\n logger.warn(COMPONENT, `Failed to load skill ${file}: ${(e as Error).message}`);\n }\n }\n }\n\n if (loadedCount > 0) {\n logger.info(COMPONENT, `Loaded ${loadedCount} user skill(s) from ~/.titan/skills/`);\n }\n\n // Load frontmatter skills (*.skill.md) — Space Agent / Hermes parity\n try {\n const { getFrontmatterToolHandlers } = await import('./frontmatterLoader.js');\n const fmHandlers = getFrontmatterToolHandlers();\n for (const { name, handler } of fmHandlers) {\n if (registeredSkills.has(name)) continue;\n registerSkill({\n name: handler.name,\n description: handler.description,\n version: '1.0.0',\n source: 'frontmatter',\n enabled: true,\n }, handler);\n loadedCount++;\n }\n if (fmHandlers.length > 0) {\n logger.info(COMPONENT, `Loaded ${fmHandlers.length} frontmatter skill(s)`);\n }\n } catch (e) {\n logger.warn(COMPONENT, `Frontmatter skills failed to load: ${(e as Error).message}`);\n }\n}\n\n/**\n * Load a YAML skill definition.\n * Format:\n * name: my_tool\n * description: What it does\n * parameters:\n * myParam:\n * type: string\n * description: A parameter\n * required: true\n * script: |\n * // JavaScript code. Use `args.myParam` for inputs.\n * // Return a string result.\n * return \"Hello \" + args.myParam;\n */\nfunction loadYamlSkill(filePath: string): ToolHandler | null {\n const content = readFileSync(filePath, 'utf-8');\n\n // Simple YAML parser (no dependency needed for this basic format)\n const name = content.match(/^name:\\s*(.+)$/m)?.[1]?.trim();\n const description = content.match(/^description:\\s*(.+)$/m)?.[1]?.trim();\n const scriptMatch = content.match(/^script:\\s*\\|\\n([\\s\\S]+?)(?=\\n\\w|\\n$|$)/m);\n const script = scriptMatch?.[1]?.replace(/^ {2}/gm, ''); // Remove YAML indent\n\n if (!name || !description || !script) {\n logger.debug(COMPONENT, `Skipping ${filePath}: missing name, description, or script`);\n return null;\n }\n\n // Parse parameters section\n const paramsSection = content.match(/^parameters:\\n((?:\\s{2}\\w[\\s\\S]*?)(?=\\nscript:|\\n\\w|\\n$))/m);\n const properties: Record<string, Record<string, unknown>> = {};\n const required: string[] = [];\n\n if (paramsSection) {\n const paramLines = paramsSection[1].split('\\n');\n let currentParam = '';\n for (const line of paramLines) {\n const paramMatch = line.match(/^\\s{2}(\\w+):\\s*$/);\n if (paramMatch) {\n currentParam = paramMatch[1];\n properties[currentParam] = {};\n continue;\n }\n if (currentParam) {\n const typeMatch = line.match(/^\\s{4}type:\\s*(.+)$/);\n const descMatch = line.match(/^\\s{4}description:\\s*(.+)$/);\n const reqMatch = line.match(/^\\s{4}required:\\s*true$/);\n const defMatch = line.match(/^\\s{4}default:\\s*(.+)$/);\n if (typeMatch) properties[currentParam].type = typeMatch[1].trim();\n if (descMatch) properties[currentParam].description = descMatch[1].trim();\n if (reqMatch) required.push(currentParam);\n if (defMatch) properties[currentParam].default = defMatch[1].trim();\n }\n }\n }\n\n // Create the execute function from the script\n const handler: ToolHandler = {\n name,\n description,\n parameters: {\n type: 'object',\n properties,\n required: required.length > 0 ? required : undefined,\n },\n execute: async (args: Record<string, unknown>) => {\n try {\n // Run in a restricted VM context — no access to globalThis, process, eval, or Function\n const safeRequire = (mod: string) => {\n // SECURITY: child_process, http, https removed — YAML skills must use builtin tools for shell/network\n const allowed = ['fs', 'path', 'os', 'crypto', 'url', 'util'];\n if (!allowed.includes(mod)) throw new Error(`Module \"${mod}\" not allowed in YAML skills`);\n return require(mod); // eslint-disable-line @typescript-eslint/no-require-imports\n };\n const sandbox: Record<string, unknown> = {\n args,\n require: safeRequire,\n console: { log: console.log },\n JSON,\n Math,\n Date,\n String,\n Number,\n Array,\n Object,\n RegExp,\n Map,\n Set,\n Promise,\n setTimeout,\n Buffer,\n };\n // Wrap the user script in an async IIFE so `return` works and we can await it\n const wrapped = `(async function() { ${script} })()`;\n const result = await vm.runInNewContext(wrapped, sandbox, { timeout: 10000 });\n return typeof result === 'string' ? result : JSON.stringify(result, null, 2);\n } catch (err) {\n return `Error: ${(err as Error).message}`;\n }\n },\n };\n\n logger.debug(COMPONENT, `Loaded YAML skill: ${name} from ${filePath}`);\n return handler;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/skills/registry.ts"],"sourcesContent":["/**\n * TITAN — Skills Registry\n * Discovers, loads, and manages skills from bundled, workspace, and marketplace sources.\n */\nimport { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport vm from 'vm';\nimport { TITAN_HOME, TITAN_SKILLS_DIR } from '../utils/constants.js';\nimport { registerTool, type ToolHandler } from '../agent/toolRunner.js';\nimport { ensureDir } from '../utils/helpers.js';\nimport logger from '../utils/logger.js';\nimport { loadConfig } from '../config/config.js';\n\nconst COMPONENT = 'Skills';\nconst DISABLED_SKILLS_PATH = join(TITAN_HOME, 'disabled-skills.json');\n\nexport interface SkillMeta {\n name: string;\n description: string;\n version: string;\n author?: string;\n source: 'bundled' | 'workspace' | 'marketplace' | 'frontmatter';\n enabled: boolean;\n}\n\nconst registeredSkills: Map<string, SkillMeta> = new Map();\n\n/** Maps skill name → tool names belonging to that skill */\nconst skillToolMap: Map<string, Set<string>> = new Map();\n\n/** Register a built-in skill (tool handler + metadata) */\nexport function registerSkill(meta: SkillMeta, handler: ToolHandler): void {\n registeredSkills.set(meta.name, meta);\n // Track which tools belong to this skill\n if (!skillToolMap.has(meta.name)) {\n skillToolMap.set(meta.name, new Set());\n }\n skillToolMap.get(meta.name)!.add(handler.name);\n registerTool(handler);\n logger.debug(COMPONENT, `Registered skill: ${meta.name} (${meta.source})`);\n}\n\n/** Get all registered skills (with persisted enabled/disabled state applied) */\nexport function getSkills(): SkillMeta[] {\n const disabled = loadDisabledSkills();\n return Array.from(registeredSkills.values()).map(s => ({\n ...s,\n enabled: !disabled.includes(s.name),\n }));\n}\n\n/** Get a skill by name */\nexport function getSkill(name: string): SkillMeta | undefined {\n return registeredSkills.get(name);\n}\n\n/** Get tool names belonging to a skill */\nexport function getSkillTools(skillName: string): string[] {\n return Array.from(skillToolMap.get(skillName) || []);\n}\n\n/** Check if a skill is enabled */\nexport function isSkillEnabled(skillName: string): boolean {\n return !loadDisabledSkills().includes(skillName);\n}\n\n/** Check if a specific tool's parent skill is enabled */\nexport function isToolSkillEnabled(toolName: string): boolean {\n for (const [skillName, tools] of skillToolMap.entries()) {\n if (tools.has(toolName)) {\n return isSkillEnabled(skillName);\n }\n }\n return true; // Tools not belonging to any skill are always enabled\n}\n\n/** Toggle a skill on/off. Returns the new enabled state. */\nexport function toggleSkill(skillName: string): boolean {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n let nowEnabled: boolean;\n\n if (idx >= 0) {\n disabled.splice(idx, 1);\n nowEnabled = true;\n } else {\n disabled.push(skillName);\n nowEnabled = false;\n }\n\n saveDisabledSkills(disabled);\n logger.info(COMPONENT, `Skill \"${skillName}\" ${nowEnabled ? 'enabled' : 'disabled'}`);\n return nowEnabled;\n}\n\n/** Set a skill's enabled state explicitly */\nexport function setSkillEnabled(skillName: string, enabled: boolean): void {\n const skill = registeredSkills.get(skillName);\n if (!skill) {\n throw new Error(`Skill \"${skillName}\" not found`);\n }\n\n const disabled = loadDisabledSkills();\n const idx = disabled.indexOf(skillName);\n\n if (enabled && idx >= 0) {\n disabled.splice(idx, 1);\n } else if (!enabled && idx < 0) {\n disabled.push(skillName);\n }\n\n saveDisabledSkills(disabled);\n}\n\n/** Load disabled skills list from disk */\nfunction loadDisabledSkills(): string[] {\n try {\n if (existsSync(DISABLED_SKILLS_PATH)) {\n return JSON.parse(readFileSync(DISABLED_SKILLS_PATH, 'utf-8')) as string[];\n }\n } catch {\n // Corrupt file — treat as empty\n }\n return [];\n}\n\n/** Save disabled skills list to disk */\nfunction saveDisabledSkills(disabled: string[]): void {\n try {\n const dir = dirname(DISABLED_SKILLS_PATH);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(DISABLED_SKILLS_PATH, JSON.stringify(disabled, null, 2), 'utf-8');\n } catch (e) {\n logger.warn(COMPONENT, `Failed to save disabled skills: ${(e as Error).message}`);\n }\n}\n\n/** Discover workspace skills from ~/.titan/workspace/skills/ */\nexport function discoverWorkspaceSkills(): SkillMeta[] {\n ensureDir(TITAN_SKILLS_DIR);\n const discovered: SkillMeta[] = [];\n\n if (!existsSync(TITAN_SKILLS_DIR)) return discovered;\n\n const entries = readdirSync(TITAN_SKILLS_DIR, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const skillDir = join(TITAN_SKILLS_DIR, entry.name);\n const skillMdPath = join(skillDir, 'SKILL.md');\n\n if (!existsSync(skillMdPath)) continue;\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8');\n const meta = parseSkillMd(content, entry.name);\n if (meta) {\n discovered.push({ ...meta, source: 'workspace', enabled: true });\n }\n } catch (error) {\n logger.warn(COMPONENT, `Failed to load skill ${entry.name}: ${(error as Error).message}`);\n }\n }\n\n logger.info(COMPONENT, `Discovered ${discovered.length} workspace skills`);\n return discovered;\n}\n\n/** Parse SKILL.md frontmatter to extract metadata */\nfunction parseSkillMd(content: string, fallbackName: string): Omit<SkillMeta, 'source' | 'enabled'> | null {\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!frontmatterMatch) {\n return {\n name: fallbackName,\n description: content.split('\\n')[0] || 'No description',\n version: '1.0.0',\n };\n }\n\n const frontmatter = frontmatterMatch[1];\n const name = frontmatter.match(/name:\\s*(.+)/)?.[1]?.trim() || fallbackName;\n const description = frontmatter.match(/description:\\s*(.+)/)?.[1]?.trim() || 'No description';\n const version = frontmatter.match(/version:\\s*(.+)/)?.[1]?.trim() || '1.0.0';\n const author = frontmatter.match(/author:\\s*(.+)/)?.[1]?.trim();\n\n return { name, description, version, author };\n}\n\n/** Initialize all built-in skills */\nexport async function initBuiltinSkills(): Promise<void> {\n logger.info(COMPONENT, 'Loading built-in skills...');\n\n // Import and register built-in skills\n const { registerShellSkill } = await import('./builtin/shell.js');\n const { registerFilesystemSkill } = await import('./builtin/filesystem.js');\n const { registerWebSearchSkill } = await import('./builtin/web_search.js');\n const { registerCronSkill } = await import('./builtin/cron.js');\n const { registerWebhookSkill } = await import('./builtin/webhook.js');\n const { registerMemorySkill } = await import('./builtin/memory_skill.js');\n const { registerBrowserSkill } = await import('./builtin/browser.js');\n const { registerSessionsSkill } = await import('./builtin/sessions.js');\n const { registerProcessSkill } = await import('./builtin/process.js');\n const { registerWebFetchSkill } = await import('./builtin/web_fetch.js');\n const { registerApplyPatchSkill } = await import('./builtin/apply_patch.js');\n const { registerAutoGenerateSkill } = await import('./builtin/auto_generate.js');\n const { registerVisionSkill } = await import('./builtin/vision.js');\n const { registerVoiceSkills } = await import('./builtin/voice.js');\n const { registerMemoryGraphSkill } = await import('./builtin/memory_graph.js');\n const { initWebBrowserTool } = await import('./builtin/web_browser.js');\n const { registerGitHubSkill } = await import('./builtin/github.js');\n const { registerEmailSkill } = await import('./builtin/email.js');\n const { registerComputerUseSkill } = await import('./builtin/computer_use.js');\n const { registerImageGenSkill } = await import('./builtin/image_gen.js');\n const { registerPdfSkill } = await import('./builtin/pdf.js');\n const { registerCalendarSkill } = await import('./builtin/calendar.js');\n const { registerSmartHomeSkill } = await import('./builtin/smart_home.js');\n const { registerDataAnalysisSkill } = await import('./builtin/data_analysis.js');\n const { registerSkyvernSkill } = await import('./builtin/skyvern.js');\n const { registerWebBrowseLlmSkill } = await import('./builtin/web_browse_llm.js');\n const { registerIncomeTrackerSkill } = await import('./builtin/income_tracker.js');\n const { registerFreelanceMonitorSkill } = await import('./builtin/freelance_monitor.js');\n const { registerContentPublisherSkill } = await import('./builtin/content_publisher.js');\n const { registerLeadScorerSkill } = await import('./builtin/lead_scorer.js');\n const { registerHunterSkill } = await import('./builtin/hunter.js');\n const { registerCodeExecSkill } = await import('./builtin/code_exec.js');\n const { registerExecuteCodeSkill } = await import('./builtin/executeCode.js');\n const { registerWeatherSkill } = await import('./builtin/weather.js');\n const { registerGoalsSkill } = await import('./builtin/goals.js');\n const { registerXPosterSkill } = await import('./builtin/x_poster.js');\n const { initModelSwitchTool } = await import('./builtin/model_switch.js');\n const { registerRagSkill } = await import('./builtin/rag.js');\n const { registerDeepResearchSkill } = await import('./builtin/deep_research.js');\n const { registerSystemInfoSkill } = await import('./builtin/system_info.js');\n const { registerPersonaManagerSkill } = await import('./builtin/persona_manager.js');\n const { registerResearchPipelineSkill } = await import('./builtin/research_pipeline.js');\n const { registerAutoresearchSkill } = await import('./builtin/autoresearch.js');\n const { registerSelfDoctorSkill } = await import('./builtin/self_doctor.js');\n const { registerInteractionTrackerSkill } = await import('./builtin/interaction_tracker.js');\n const { registerFeedbackTrackerSkill } = await import('./builtin/feedback_tracker.js');\n const { registerGrowthExperimentsSkill } = await import('./builtin/growth_experiments.js');\n const { registerContentCalendarSkill } = await import('./builtin/content_calendar.js');\n const { registerSlackSkill } = await import('./builtin/slack.js');\n const { registerRevenueCatKBSkill } = await import('./builtin/revenuecat_kb.js');\n const { registerWeeklyReportSkill } = await import('./builtin/weekly_report.js');\n const { registerSelfImproveSkill } = await import('./builtin/self_improve.js');\n const { registerGepaSkill } = await import('./builtin/gepa.js');\n const { registerModelTrainerSkill } = await import('./builtin/model_trainer.js');\n const { registerSocialSchedulerSkill } = await import('./builtin/social_scheduler.js');\n const { registerStructuredOutputSkill } = await import('./builtin/structured_output.js');\n const { registerWorkflowsSkill } = await import('./builtin/workflows.js');\n const { registerAgentHandoffSkill } = await import('./builtin/agent_handoff.js');\n const { registerKnowledgeBaseSkill } = await import('./builtin/knowledge_base.js');\n const { registerEventTriggersSkill } = await import('./builtin/event_triggers.js');\n const { registerA2AProtocolSkill } = await import('./builtin/a2a_protocol.js');\n const { registerEvalsSkill } = await import('./builtin/evals.js');\n const { registerApprovalGatesSkill } = await import('./builtin/approval_gates.js');\n const { registerVRAMSkills } = await import('./builtin/vram.js');\n const { registerSecurityScanSkill } = await import('./builtin/security_scan.js');\n const { registerChangelogGenSkill } = await import('./builtin/changelog_gen.js');\n const { registerJiraLinearSkill } = await import('./builtin/jira_linear.js');\n const { registerAuditTrailSkill } = await import('./builtin/audit_trail.js');\n const { registerVisualPlanSkill } = await import('./builtin/visual_plan.js');\n const { registerScreenRecordSkill } = await import('./builtin/screen_record.js');\n const { registerSessionTeleportSkill } = await import('./builtin/session_teleport.js');\n const { registerCrossProviderSkill } = await import('./builtin/cross_provider.js');\n const { registerSentrySkill } = await import('./builtin/sentry.js');\n const { registerVideoSkill } = await import('./builtin/video.js');\n const { registerMixtureOfAgentsSkill } = await import('./builtin/mixture_of_agents.js');\n const { registerAgentDebateSkill } = await import('./builtin/agent_debate.js');\n const { registerFileCheckpointsSkill } = await import('./builtin/file_checkpoints.js');\n const { registerVerifyPageSkill } = await import('./builtin/verify_page.js');\n const { registerAgentMessagingSkill } = await import('./builtin/agent_messaging.js');\n const { registerFacebookSkill } = await import('./builtin/facebook.js');\n const { registerFBAutopilotSkill } = await import('./builtin/fb_autopilot.js');\n const { registerWidgetGallerySkill } = await import('./builtin/widget_gallery.js');\n\n const config = loadConfig();\n const primitiveMode = config.skills.primitiveMode;\n\n if (primitiveMode) {\n logger.info(COMPONENT, 'PRIMITIVE MODE ENABLED — loading only shell, filesystem, web_search');\n }\n\n const registrations: [string, () => void][] = [\n ['shell', registerShellSkill],\n ['filesystem', registerFilesystemSkill],\n ['web_search', registerWebSearchSkill],\n ];\n\n if (!primitiveMode) {\n registrations.push(\n ['cron', registerCronSkill],\n ['webhook', registerWebhookSkill],\n ['memory', registerMemorySkill],\n ['browser', registerBrowserSkill],\n ['sessions', registerSessionsSkill],\n ['process', registerProcessSkill],\n ['web_fetch', registerWebFetchSkill],\n ['apply_patch', registerApplyPatchSkill],\n ['auto_generate', registerAutoGenerateSkill],\n ['vision', registerVisionSkill],\n ['voice', registerVoiceSkills],\n ['memory_graph', registerMemoryGraphSkill],\n ['web_browser', initWebBrowserTool],\n ['github', registerGitHubSkill],\n ['email', registerEmailSkill],\n ['computer_use', registerComputerUseSkill],\n ['image_gen', registerImageGenSkill],\n ['pdf', registerPdfSkill],\n ['calendar', registerCalendarSkill],\n ['smart_home', registerSmartHomeSkill],\n ['data_analysis', registerDataAnalysisSkill],\n ['skyvern', registerSkyvernSkill],\n ['web_browse_llm', registerWebBrowseLlmSkill],\n ['income_tracker', registerIncomeTrackerSkill],\n ['freelance_monitor', registerFreelanceMonitorSkill],\n ['content_publisher', registerContentPublisherSkill],\n ['lead_scorer', registerLeadScorerSkill],\n ['hunter', registerHunterSkill],\n ['code_exec', registerCodeExecSkill],\n ['execute_code', registerExecuteCodeSkill],\n ['weather', registerWeatherSkill],\n ['goals', registerGoalsSkill],\n ['x_poster', registerXPosterSkill],\n ['model_switch', initModelSwitchTool],\n ['rag', registerRagSkill],\n ['deep_research', registerDeepResearchSkill],\n ['system_info', registerSystemInfoSkill],\n ['persona_manager', registerPersonaManagerSkill],\n ['research_pipeline', registerResearchPipelineSkill],\n ['autoresearch', registerAutoresearchSkill],\n ['self_doctor', registerSelfDoctorSkill],\n ['interaction_tracker', registerInteractionTrackerSkill],\n ['feedback_tracker', registerFeedbackTrackerSkill],\n ['growth_experiments', registerGrowthExperimentsSkill],\n ['content_calendar', registerContentCalendarSkill],\n ['slack', registerSlackSkill],\n ['revenuecat_kb', registerRevenueCatKBSkill],\n ['weekly_report', registerWeeklyReportSkill],\n ['self_improve', registerSelfImproveSkill],\n ['gepa', registerGepaSkill],\n ['model_trainer', registerModelTrainerSkill],\n ['social_scheduler', registerSocialSchedulerSkill],\n ['structured_output', registerStructuredOutputSkill],\n ['workflows', registerWorkflowsSkill],\n ['agent_handoff', registerAgentHandoffSkill],\n ['knowledge_base', registerKnowledgeBaseSkill],\n ['event_triggers', registerEventTriggersSkill],\n ['evals', registerEvalsSkill],\n ['a2a_protocol', registerA2AProtocolSkill],\n ['approval_gates', registerApprovalGatesSkill],\n ['vram', registerVRAMSkills],\n ['security_scan', registerSecurityScanSkill],\n ['changelog_gen', registerChangelogGenSkill],\n ['jira_linear', registerJiraLinearSkill],\n ['audit_trail', registerAuditTrailSkill],\n ['visual_plan', registerVisualPlanSkill],\n ['screen_record', registerScreenRecordSkill],\n ['session_teleport', registerSessionTeleportSkill],\n ['cross_provider', registerCrossProviderSkill],\n ['sentry', registerSentrySkill],\n ['video', registerVideoSkill],\n ['mixture_of_agents', registerMixtureOfAgentsSkill],\n ['agent_debate', registerAgentDebateSkill],\n ['file_checkpoints', registerFileCheckpointsSkill],\n ['verify_page', registerVerifyPageSkill],\n ['agent_messaging', registerAgentMessagingSkill],\n ['facebook', registerFacebookSkill],\n ['fb_autopilot', registerFBAutopilotSkill],\n ['widget_gallery', registerWidgetGallerySkill],\n ['widget_gallery', registerWidgetGallerySkill],\n );\n }\n for (const [name, fn] of registrations) {\n try { fn(); } catch (e) { logger.warn(COMPONENT, `Failed to register skill \"${name}\": ${(e as Error).message}`); }\n }\n\n if (!primitiveMode) {\n // Register planner as an LLM-invocable tool\n const { registerPlannerTool } = await import('../agent/planner.js');\n try { registerPlannerTool(); } catch (e) { logger.warn(COMPONENT, `Failed to register planner: ${(e as Error).message}`); }\n\n // Register TopFacts context engine plugin (DeerFlow-inspired persistent memory)\n try {\n const { createTopFactsPlugin } = await import('../plugins/topFacts.js');\n const { createMemoryRetrievalPlugin } = await import('../plugins/memoryRetrieval.js');\n const { registerPlugin } = await import('../plugins/registry.js');\n const topFacts = createTopFactsPlugin();\n registerPlugin(topFacts);\n if (topFacts.bootstrap) await topFacts.bootstrap({});\n const memoryRetrieval = createMemoryRetrievalPlugin();\n registerPlugin(memoryRetrieval);\n if (memoryRetrieval.bootstrap) await memoryRetrieval.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register TopFacts plugin: ${(e as Error).message}`); }\n\n // Register SmartCompress context engine plugin (task-type-aware compression)\n try {\n const { createSmartCompressPlugin } = await import('../plugins/smartCompress.js');\n const { registerPlugin: regPlugin } = await import('../plugins/registry.js');\n const smartCompress = createSmartCompressPlugin();\n regPlugin(smartCompress);\n if (smartCompress.bootstrap) await smartCompress.bootstrap({});\n } catch (e) { logger.warn(COMPONENT, `Failed to register SmartCompress plugin: ${(e as Error).message}`); }\n\n // Register tool_search + tool_expand — meta-tools for discovering tools on demand\n // tool_expand is the progressive disclosure extension (Hermes competitive gap fix)\n const { getToolSearchHandler, getToolExpandHandler } = await import('../agent/toolSearch.js');\n try { registerTool(getToolSearchHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_search: ${(e as Error).message}`); }\n try { registerTool(getToolExpandHandler()); } catch (e) { logger.warn(COMPONENT, `Failed to register tool_expand: ${(e as Error).message}`); }\n\n // F3: Register procedural memory tools (Hermes-inspired skill learning)\n try {\n const { saveSkill, searchSkills } = await import('./proceduralMemory.js');\n registerTool({\n name: 'save_skill',\n description: 'Save a reusable approach/technique as a procedural skill for future tasks. Use this when you discover an effective approach that could help in similar future situations.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Short descriptive name for the skill (e.g., \"Deploy Node.js app to Docker\")' },\n tags: { type: 'array', items: { type: 'string' }, description: 'Tags for searchability (e.g., [\"docker\", \"deployment\", \"nodejs\"])' },\n content: { type: 'string', description: 'The reusable approach/technique in markdown format. Include key steps, commands, and gotchas.' },\n },\n required: ['name', 'tags', 'content'],\n },\n execute: async (args: Record<string, unknown>) => {\n const name = args.name as string;\n const tags = (args.tags as string[]) || [];\n const content = args.content as string;\n if (!name || !content) return 'Error: name and content are required';\n const skill = saveSkill(name, tags, content);\n return `Skill saved: \"${skill.name}\" (tags: ${skill.tags.join(', ')}). It will be auto-recalled in future tasks matching these tags.`;\n },\n });\n registerTool({\n name: 'recall_skill',\n description: 'Search for previously saved procedural skills by keyword or tag. Returns reusable approaches from past tasks.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query — keywords or tags to find relevant skills' },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = args.query as string;\n if (!query) return 'Error: query is required';\n const results = searchSkills(query, 5);\n if (results.length === 0) return 'No matching skills found. Consider saving useful approaches with save_skill.';\n return results.map(s =>\n `### ${s.name}\\nTags: ${s.tags.join(', ')} | Used ${s.useCount}x\\n${s.content.slice(0, 800)}`\n ).join('\\n\\n---\\n\\n');\n },\n });\n logger.info(COMPONENT, 'Registered procedural memory tools (save_skill, recall_skill)');\n } catch (e) { logger.warn(COMPONENT, `Failed to register procedural memory tools: ${(e as Error).message}`); }\n }\n\n logger.info(COMPONENT, `Loaded ${registeredSkills.size} built-in skills`);\n\n // Load dev skills (only in dev mode — skip import entirely in production)\n if (process.env.NODE_ENV !== 'production' || process.env.TITAN_DEV) {\n const { initDevSkills } = await import('./dev/loader.js');\n await initDevSkills();\n }\n\n // Load NVIDIA skills (optional — only when TITAN_NVIDIA=1 or nvidia.enabled in config)\n try {\n let nvidiaEnabled = process.env.TITAN_NVIDIA === '1';\n if (!nvidiaEnabled) {\n try {\n const { loadConfig: _loadConfig } = await import('../config/config.js');\n const cfg = _loadConfig() as Record<string, unknown>;\n const nvCfg = cfg.nvidia as Record<string, unknown> | undefined;\n nvidiaEnabled = nvCfg?.enabled === true;\n } catch { /* config not available in test env */ }\n }\n if (nvidiaEnabled) {\n const { initNvidiaSkills } = await import('./nvidia/loader.js');\n await initNvidiaSkills();\n }\n } catch (err) {\n logger.warn(COMPONENT, `NVIDIA skills failed to load: ${(err as Error).message}`);\n }\n\n // Load personal skills (private, gitignored — only when TITAN_PERSONAL=1)\n // Primary location: dist/skills/personal/loader.js (co-located with dist/skills/registry.js\n // so `../registry` resolves to the SAME module instance — tools register into the correct registry)\n // Fallback: ~/.titan/personal/loader.js (legacy / TITAN_PERSONAL_DIR override)\n if (process.env.TITAN_PERSONAL === '1') {\n try {\n const { pathToFileURL, fileURLToPath } = await import('node:url');\n const { join: _join, dirname: _dirname } = await import('node:path');\n // Compute dist/skills/ dir from this file's location (works on any machine)\n const thisDir = _dirname(fileURLToPath(import.meta.url));\n const distPersonalDir = _join(thisDir, 'personal');\n // TITAN_PERSONAL_DIR env var overrides; otherwise try dist-local first, then ~/.titan/personal/\n const personalDir = process.env.TITAN_PERSONAL_DIR\n || (existsSync(_join(distPersonalDir, 'loader.js')) ? distPersonalDir : _join(TITAN_HOME, 'personal'));\n const loaderPath = _join(personalDir, 'loader.js');\n if (existsSync(loaderPath)) {\n // Inject the main app's registerSkill into a global so the personal bundle\n // (which has its own bundled copy) uses the correct shared toolRegistry instance.\n (globalThis as Record<string, unknown>).__titanRegisterSkill = registerSkill;\n const { initPersonalSkills } = await import(pathToFileURL(loaderPath).href) as { initPersonalSkills: () => Promise<void> };\n await initPersonalSkills();\n } else {\n logger.warn(COMPONENT, `TITAN_PERSONAL=1 but ${loaderPath} not found — run: npm run build:personal`);\n }\n } catch (err) {\n logger.warn(COMPONENT, `Personal skills failed to load: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Discover and load user skills from ~/.titan/skills/ (all subdirs).\n * Supports:\n * 1. JavaScript files (.js) that export default { name, description, parameters, execute }\n * 2. YAML skill definitions (.yaml/.yml) with inline scripts\n * 3. Auto-generated skills from ~/.titan/skills/auto/\n */\nexport async function loadAutoSkills(): Promise<void> {\n const skillsRoot = join(TITAN_HOME, 'skills');\n if (!existsSync(skillsRoot)) return;\n\n logger.info(COMPONENT, 'Scanning for user skills...');\n let loadedCount = 0;\n\n // Scan both root and all subdirectories\n const dirsToScan = [skillsRoot];\n const entries = readdirSync(skillsRoot, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) dirsToScan.push(join(skillsRoot, entry.name));\n }\n\n for (const dir of dirsToScan) {\n const files = readdirSync(dir).filter(f =>\n f.endsWith('.js') || f.endsWith('.yaml') || f.endsWith('.yml')\n );\n\n for (const file of files) {\n const filePath = join(dir, file);\n try {\n if (file.endsWith('.js')) {\n // JavaScript skill — export default { name, description, parameters, execute }\n const modulePath = `file://${filePath}?t=${Date.now()}`;\n const mod = await import(modulePath);\n if (mod.default && mod.default.name && mod.default.execute) {\n const handler = mod.default as ToolHandler;\n if (registeredSkills.has(handler.name)) continue; // Skip duplicates\n registerSkill({\n name: handler.name,\n description: handler.description || 'User skill',\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, handler);\n loadedCount++;\n }\n } else {\n // YAML skill definition\n const loaded = loadYamlSkill(filePath);\n if (loaded && !registeredSkills.has(loaded.name)) {\n registerSkill({\n name: loaded.name,\n description: loaded.description,\n version: '1.0.0',\n source: 'workspace',\n enabled: true,\n }, loaded);\n loadedCount++;\n }\n }\n } catch (e: unknown) {\n logger.warn(COMPONENT, `Failed to load skill ${file}: ${(e as Error).message}`);\n }\n }\n }\n\n if (loadedCount > 0) {\n logger.info(COMPONENT, `Loaded ${loadedCount} user skill(s) from ~/.titan/skills/`);\n }\n\n // Load frontmatter skills (*.skill.md) — Space Agent / Hermes parity\n try {\n const { getFrontmatterToolHandlers } = await import('./frontmatterLoader.js');\n const fmHandlers = getFrontmatterToolHandlers();\n for (const { name, handler } of fmHandlers) {\n if (registeredSkills.has(name)) continue;\n registerSkill({\n name: handler.name,\n description: handler.description,\n version: '1.0.0',\n source: 'frontmatter',\n enabled: true,\n }, handler);\n loadedCount++;\n }\n if (fmHandlers.length > 0) {\n logger.info(COMPONENT, `Loaded ${fmHandlers.length} frontmatter skill(s)`);\n }\n } catch (e) {\n logger.warn(COMPONENT, `Frontmatter skills failed to load: ${(e as Error).message}`);\n }\n}\n\n/**\n * Load a YAML skill definition.\n * Format:\n * name: my_tool\n * description: What it does\n * parameters:\n * myParam:\n * type: string\n * description: A parameter\n * required: true\n * script: |\n * // JavaScript code. Use `args.myParam` for inputs.\n * // Return a string result.\n * return \"Hello \" + args.myParam;\n */\nfunction loadYamlSkill(filePath: string): ToolHandler | null {\n const content = readFileSync(filePath, 'utf-8');\n\n // Simple YAML parser (no dependency needed for this basic format)\n const name = content.match(/^name:\\s*(.+)$/m)?.[1]?.trim();\n const description = content.match(/^description:\\s*(.+)$/m)?.[1]?.trim();\n const scriptMatch = content.match(/^script:\\s*\\|\\n([\\s\\S]+?)(?=\\n\\w|\\n$|$)/m);\n const script = scriptMatch?.[1]?.replace(/^ {2}/gm, ''); // Remove YAML indent\n\n if (!name || !description || !script) {\n logger.debug(COMPONENT, `Skipping ${filePath}: missing name, description, or script`);\n return null;\n }\n\n // Parse parameters section\n const paramsSection = content.match(/^parameters:\\n((?:\\s{2}\\w[\\s\\S]*?)(?=\\nscript:|\\n\\w|\\n$))/m);\n const properties: Record<string, Record<string, unknown>> = {};\n const required: string[] = [];\n\n if (paramsSection) {\n const paramLines = paramsSection[1].split('\\n');\n let currentParam = '';\n for (const line of paramLines) {\n const paramMatch = line.match(/^\\s{2}(\\w+):\\s*$/);\n if (paramMatch) {\n currentParam = paramMatch[1];\n properties[currentParam] = {};\n continue;\n }\n if (currentParam) {\n const typeMatch = line.match(/^\\s{4}type:\\s*(.+)$/);\n const descMatch = line.match(/^\\s{4}description:\\s*(.+)$/);\n const reqMatch = line.match(/^\\s{4}required:\\s*true$/);\n const defMatch = line.match(/^\\s{4}default:\\s*(.+)$/);\n if (typeMatch) properties[currentParam].type = typeMatch[1].trim();\n if (descMatch) properties[currentParam].description = descMatch[1].trim();\n if (reqMatch) required.push(currentParam);\n if (defMatch) properties[currentParam].default = defMatch[1].trim();\n }\n }\n }\n\n // Create the execute function from the script\n const handler: ToolHandler = {\n name,\n description,\n parameters: {\n type: 'object',\n properties,\n required: required.length > 0 ? required : undefined,\n },\n execute: async (args: Record<string, unknown>) => {\n try {\n // Run in a restricted VM context — no access to globalThis, process, eval, or Function\n const safeRequire = (mod: string) => {\n // SECURITY: child_process, http, https removed — YAML skills must use builtin tools for shell/network\n const allowed = ['fs', 'path', 'os', 'crypto', 'url', 'util'];\n if (!allowed.includes(mod)) throw new Error(`Module \"${mod}\" not allowed in YAML skills`);\n return require(mod); // eslint-disable-line @typescript-eslint/no-require-imports\n };\n const sandbox: Record<string, unknown> = {\n args,\n require: safeRequire,\n console: { log: console.log },\n JSON,\n Math,\n Date,\n String,\n Number,\n Array,\n Object,\n RegExp,\n Map,\n Set,\n Promise,\n setTimeout,\n Buffer,\n };\n // Wrap the user script in an async IIFE so `return` works and we can await it\n const wrapped = `(async function() { ${script} })()`;\n const result = await vm.runInNewContext(wrapped, sandbox, { timeout: 10000 });\n return typeof result === 'string' ? result : JSON.stringify(result, null, 2);\n } catch (err) {\n return `Error: ${(err as Error).message}`;\n }\n },\n };\n\n logger.debug(COMPONENT, `Loaded YAML skill: ${name} from ${filePath}`);\n return handler;\n}\n"],"mappings":"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","names":[]}
@@ -5,7 +5,7 @@ import logger from "../utils/logger.js";
5
5
  const COMPONENT = "ActivityLog";
6
6
  const MAX_LINES = 1e3;
7
7
  let _inMemoryBuffer = [];
8
- let _bufferFlushMs = 5e3;
8
+ const _bufferFlushMs = 5e3;
9
9
  let _bufferTimer = null;
10
10
  function logActivity(event) {
11
11
  const entry = { t: Date.now(), ...event };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/telemetry/activityLog.ts"],"sourcesContent":["/**\n * TITAN — Activity Log (Phase 8)\n *\n * Lightweight append-only telemetry for \"what TITAN did today\".\n * Drives real Facebook \"activity\" posts instead of fictional templates.\n *\n * Format: JSON Lines (~/.titan/activity-log.jsonl)\n * { \"t\": 1714141200000, \"event\": \"tool_call\", \"tool\": \"write_file\", \"session\": \"abc\" }\n * { \"t\": 1714141205000, \"event\": \"agent_spawn\", \"agent\": \"builder\", \"task\": \"fix bug\" }\n *\n * Rotation: keep last 1000 lines (≈ 50-100KB).\n */\nimport { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';\nimport { ACTIVITY_LOG_PATH } from '../utils/constants.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'ActivityLog';\nconst MAX_LINES = 1000;\n\nexport type ActivityEventType =\n | 'tool_call'\n | 'agent_spawn'\n | 'agent_complete'\n | 'file_edit'\n | 'web_search'\n | 'web_fetch'\n | 'eval_run'\n | 'goal_complete'\n | 'self_improve_proposal'\n | 'error_recovery'\n | 'milestone';\n\nexport interface ActivityEvent {\n t: number; // timestamp ms\n event: ActivityEventType;\n [key: string]: unknown;\n}\n\nexport interface ActivitySummary {\n periodHours: number;\n toolCalls: number;\n agentSpawns: number;\n agentCompletions: number;\n fileEdits: number;\n webSearches: number;\n webFetches: number;\n evalRuns: number;\n goalsCompleted: number;\n selfImproveProposals: number;\n errorRecoveries: number;\n highlights: string[];\n}\n\nlet _inMemoryBuffer: ActivityEvent[] = [];\nlet _bufferFlushMs = 5000;\nlet _bufferTimer: ReturnType<typeof setTimeout> | null = null;\n\n/** Append an event to the activity log (buffered + flushed async) */\nexport function logActivity(event: Omit<ActivityEvent, 't'>): void {\n // The string indexer on ActivityEvent confuses TS's spread narrowing —\n // it loses sight of `event` being a required field after the spread.\n // Cast through unknown is safe because logActivity's parameter type\n // already requires the `event` discriminator.\n const entry = { t: Date.now(), ...event } as unknown as ActivityEvent;\n _inMemoryBuffer.push(entry);\n scheduleFlush();\n}\n\nfunction scheduleFlush(): void {\n if (_bufferTimer) return;\n _bufferTimer = setTimeout(() => {\n _bufferTimer = null;\n flushBuffer();\n }, _bufferFlushMs);\n}\n\n/** Flush buffered events to disk. Exposed for tests and shutdown hooks. */\nexport function flushBuffer(): void {\n if (_inMemoryBuffer.length === 0) return;\n const lines = _inMemoryBuffer.map(e => JSON.stringify(e)).join('\\n') + '\\n';\n _inMemoryBuffer = [];\n try {\n appendFileSync(ACTIVITY_LOG_PATH, lines, 'utf-8');\n enforceRotation();\n } catch (e) {\n logger.error(COMPONENT, `Failed to write activity log: ${(e as Error).message}`);\n }\n}\n\nfunction enforceRotation(): void {\n try {\n if (!existsSync(ACTIVITY_LOG_PATH)) return;\n const data = readFileSync(ACTIVITY_LOG_PATH, 'utf-8');\n const lines = data.split('\\n').filter(l => l.trim());\n if (lines.length <= MAX_LINES) return;\n const keep = lines.slice(-MAX_LINES);\n writeFileSync(ACTIVITY_LOG_PATH, keep.join('\\n') + '\\n', 'utf-8');\n logger.debug(COMPONENT, `Rotated activity log to ${keep.length} lines`);\n } catch (e) {\n logger.warn(COMPONENT, `Rotation failed: ${(e as Error).message}`);\n }\n}\n\n/** Read all events from disk + buffer */\nexport function readActivityEvents(): ActivityEvent[] {\n const events: ActivityEvent[] = [];\n try {\n if (existsSync(ACTIVITY_LOG_PATH)) {\n const data = readFileSync(ACTIVITY_LOG_PATH, 'utf-8');\n for (const line of data.split('\\n')) {\n if (!line.trim()) continue;\n try { events.push(JSON.parse(line) as ActivityEvent); } catch { /* skip bad line */ }\n }\n }\n } catch (e) {\n logger.warn(COMPONENT, `Failed to read activity log: ${(e as Error).message}`);\n }\n return events.concat(_inMemoryBuffer);\n}\n\n/** Summarize activity over the last N hours */\nexport function getActivitySummary(periodHours = 24): ActivitySummary {\n const cutoff = Date.now() - periodHours * 60 * 60 * 1000;\n const events = readActivityEvents().filter(e => e.t >= cutoff);\n\n const summary: ActivitySummary = {\n periodHours,\n toolCalls: 0,\n agentSpawns: 0,\n agentCompletions: 0,\n fileEdits: 0,\n webSearches: 0,\n webFetches: 0,\n evalRuns: 0,\n goalsCompleted: 0,\n selfImproveProposals: 0,\n errorRecoveries: 0,\n highlights: [],\n };\n\n for (const e of events) {\n switch (e.event) {\n case 'tool_call': summary.toolCalls++; break;\n case 'agent_spawn': summary.agentSpawns++; break;\n case 'agent_complete': summary.agentCompletions++; break;\n case 'file_edit': summary.fileEdits++; break;\n case 'web_search': summary.webSearches++; break;\n case 'web_fetch': summary.webFetches++; break;\n case 'eval_run': summary.evalRuns++; break;\n case 'goal_complete': summary.goalsCompleted++; break;\n case 'self_improve_proposal': summary.selfImproveProposals++; break;\n case 'error_recovery': summary.errorRecoveries++; break;\n case 'milestone':\n if (e.description && typeof e.description === 'string') {\n summary.highlights.push(e.description);\n }\n break;\n }\n }\n\n // Auto-detect highlights: milestone thresholds\n if (summary.toolCalls > 0 && summary.toolCalls % 1000 === 0) {\n summary.highlights.push(`Hit ${summary.toolCalls.toLocaleString()} tool calls`);\n }\n if (summary.agentSpawns > 0 && summary.agentSpawns % 100 === 0) {\n summary.highlights.push(`Spawned ${summary.agentSpawns} agents today`);\n }\n\n return summary;\n}\n\n/** Check if there's anything worth posting about */\nexport function hasInterestingActivity(periodHours = 24): boolean {\n const s = getActivitySummary(periodHours);\n return s.toolCalls > 0 || s.agentSpawns > 0 || s.fileEdits > 0 || s.highlights.length > 0;\n}\n\n/** Format a short narrative from the summary for the LLM prompt */\nexport function formatActivityNarrative(summary: ActivitySummary): string {\n const parts: string[] = [];\n if (summary.agentSpawns > 0) parts.push(`spawned ${summary.agentSpawns} sub-agent${summary.agentSpawns === 1 ? '' : 's'}`);\n if (summary.agentCompletions > 0) parts.push(`completed ${summary.agentCompletions} agent task${summary.agentCompletions === 1 ? '' : 's'}`);\n if (summary.toolCalls > 0) parts.push(`made ${summary.toolCalls.toLocaleString()} tool call${summary.toolCalls === 1 ? '' : 's'}`);\n if (summary.fileEdits > 0) parts.push(`edited ${summary.fileEdits} file${summary.fileEdits === 1 ? '' : 's'}`);\n if (summary.webSearches > 0) parts.push(`ran ${summary.webSearches} web search${summary.webSearches === 1 ? '' : 'es'}`);\n if (summary.evalRuns > 0) parts.push(`ran ${summary.evalRuns} eval suite${summary.evalRuns === 1 ? '' : 's'}`);\n if (summary.goalsCompleted > 0) parts.push(`completed ${summary.goalsCompleted} goal${summary.goalsCompleted === 1 ? '' : 's'}`);\n if (summary.selfImproveProposals > 0) parts.push(`filed ${summary.selfImproveProposals} self-improvement proposal${summary.selfImproveProposals === 1 ? '' : 's'}`);\n if (summary.errorRecoveries > 0) parts.push(`recovered from ${summary.errorRecoveries} error${summary.errorRecoveries === 1 ? '' : 's'}`);\n\n if (parts.length === 0) return '';\n\n let narrative = `In the last ${summary.periodHours}h, TITAN ${parts.join(', ')}.`;\n if (summary.highlights.length > 0) {\n narrative += ` Highlights: ${summary.highlights.join('; ')}.`;\n }\n return narrative;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/telemetry/activityLog.ts"],"sourcesContent":["/**\n * TITAN — Activity Log (Phase 8)\n *\n * Lightweight append-only telemetry for \"what TITAN did today\".\n * Drives real Facebook \"activity\" posts instead of fictional templates.\n *\n * Format: JSON Lines (~/.titan/activity-log.jsonl)\n * { \"t\": 1714141200000, \"event\": \"tool_call\", \"tool\": \"write_file\", \"session\": \"abc\" }\n * { \"t\": 1714141205000, \"event\": \"agent_spawn\", \"agent\": \"builder\", \"task\": \"fix bug\" }\n *\n * Rotation: keep last 1000 lines (≈ 50-100KB).\n */\nimport { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';\nimport { ACTIVITY_LOG_PATH } from '../utils/constants.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'ActivityLog';\nconst MAX_LINES = 1000;\n\nexport type ActivityEventType =\n | 'tool_call'\n | 'agent_spawn'\n | 'agent_complete'\n | 'file_edit'\n | 'web_search'\n | 'web_fetch'\n | 'eval_run'\n | 'goal_complete'\n | 'self_improve_proposal'\n | 'error_recovery'\n | 'milestone';\n\nexport interface ActivityEvent {\n t: number; // timestamp ms\n event: ActivityEventType;\n [key: string]: unknown;\n}\n\nexport interface ActivitySummary {\n periodHours: number;\n toolCalls: number;\n agentSpawns: number;\n agentCompletions: number;\n fileEdits: number;\n webSearches: number;\n webFetches: number;\n evalRuns: number;\n goalsCompleted: number;\n selfImproveProposals: number;\n errorRecoveries: number;\n highlights: string[];\n}\n\nlet _inMemoryBuffer: ActivityEvent[] = [];\nconst _bufferFlushMs = 5000;\nlet _bufferTimer: ReturnType<typeof setTimeout> | null = null;\n\n/** Append an event to the activity log (buffered + flushed async) */\nexport function logActivity(event: Omit<ActivityEvent, 't'>): void {\n // The string indexer on ActivityEvent confuses TS's spread narrowing —\n // it loses sight of `event` being a required field after the spread.\n // Cast through unknown is safe because logActivity's parameter type\n // already requires the `event` discriminator.\n const entry = { t: Date.now(), ...event } as unknown as ActivityEvent;\n _inMemoryBuffer.push(entry);\n scheduleFlush();\n}\n\nfunction scheduleFlush(): void {\n if (_bufferTimer) return;\n _bufferTimer = setTimeout(() => {\n _bufferTimer = null;\n flushBuffer();\n }, _bufferFlushMs);\n}\n\n/** Flush buffered events to disk. Exposed for tests and shutdown hooks. */\nexport function flushBuffer(): void {\n if (_inMemoryBuffer.length === 0) return;\n const lines = _inMemoryBuffer.map(e => JSON.stringify(e)).join('\\n') + '\\n';\n _inMemoryBuffer = [];\n try {\n appendFileSync(ACTIVITY_LOG_PATH, lines, 'utf-8');\n enforceRotation();\n } catch (e) {\n logger.error(COMPONENT, `Failed to write activity log: ${(e as Error).message}`);\n }\n}\n\nfunction enforceRotation(): void {\n try {\n if (!existsSync(ACTIVITY_LOG_PATH)) return;\n const data = readFileSync(ACTIVITY_LOG_PATH, 'utf-8');\n const lines = data.split('\\n').filter(l => l.trim());\n if (lines.length <= MAX_LINES) return;\n const keep = lines.slice(-MAX_LINES);\n writeFileSync(ACTIVITY_LOG_PATH, keep.join('\\n') + '\\n', 'utf-8');\n logger.debug(COMPONENT, `Rotated activity log to ${keep.length} lines`);\n } catch (e) {\n logger.warn(COMPONENT, `Rotation failed: ${(e as Error).message}`);\n }\n}\n\n/** Read all events from disk + buffer */\nexport function readActivityEvents(): ActivityEvent[] {\n const events: ActivityEvent[] = [];\n try {\n if (existsSync(ACTIVITY_LOG_PATH)) {\n const data = readFileSync(ACTIVITY_LOG_PATH, 'utf-8');\n for (const line of data.split('\\n')) {\n if (!line.trim()) continue;\n try { events.push(JSON.parse(line) as ActivityEvent); } catch { /* skip bad line */ }\n }\n }\n } catch (e) {\n logger.warn(COMPONENT, `Failed to read activity log: ${(e as Error).message}`);\n }\n return events.concat(_inMemoryBuffer);\n}\n\n/** Summarize activity over the last N hours */\nexport function getActivitySummary(periodHours = 24): ActivitySummary {\n const cutoff = Date.now() - periodHours * 60 * 60 * 1000;\n const events = readActivityEvents().filter(e => e.t >= cutoff);\n\n const summary: ActivitySummary = {\n periodHours,\n toolCalls: 0,\n agentSpawns: 0,\n agentCompletions: 0,\n fileEdits: 0,\n webSearches: 0,\n webFetches: 0,\n evalRuns: 0,\n goalsCompleted: 0,\n selfImproveProposals: 0,\n errorRecoveries: 0,\n highlights: [],\n };\n\n for (const e of events) {\n switch (e.event) {\n case 'tool_call': summary.toolCalls++; break;\n case 'agent_spawn': summary.agentSpawns++; break;\n case 'agent_complete': summary.agentCompletions++; break;\n case 'file_edit': summary.fileEdits++; break;\n case 'web_search': summary.webSearches++; break;\n case 'web_fetch': summary.webFetches++; break;\n case 'eval_run': summary.evalRuns++; break;\n case 'goal_complete': summary.goalsCompleted++; break;\n case 'self_improve_proposal': summary.selfImproveProposals++; break;\n case 'error_recovery': summary.errorRecoveries++; break;\n case 'milestone':\n if (e.description && typeof e.description === 'string') {\n summary.highlights.push(e.description);\n }\n break;\n }\n }\n\n // Auto-detect highlights: milestone thresholds\n if (summary.toolCalls > 0 && summary.toolCalls % 1000 === 0) {\n summary.highlights.push(`Hit ${summary.toolCalls.toLocaleString()} tool calls`);\n }\n if (summary.agentSpawns > 0 && summary.agentSpawns % 100 === 0) {\n summary.highlights.push(`Spawned ${summary.agentSpawns} agents today`);\n }\n\n return summary;\n}\n\n/** Check if there's anything worth posting about */\nexport function hasInterestingActivity(periodHours = 24): boolean {\n const s = getActivitySummary(periodHours);\n return s.toolCalls > 0 || s.agentSpawns > 0 || s.fileEdits > 0 || s.highlights.length > 0;\n}\n\n/** Format a short narrative from the summary for the LLM prompt */\nexport function formatActivityNarrative(summary: ActivitySummary): string {\n const parts: string[] = [];\n if (summary.agentSpawns > 0) parts.push(`spawned ${summary.agentSpawns} sub-agent${summary.agentSpawns === 1 ? '' : 's'}`);\n if (summary.agentCompletions > 0) parts.push(`completed ${summary.agentCompletions} agent task${summary.agentCompletions === 1 ? '' : 's'}`);\n if (summary.toolCalls > 0) parts.push(`made ${summary.toolCalls.toLocaleString()} tool call${summary.toolCalls === 1 ? '' : 's'}`);\n if (summary.fileEdits > 0) parts.push(`edited ${summary.fileEdits} file${summary.fileEdits === 1 ? '' : 's'}`);\n if (summary.webSearches > 0) parts.push(`ran ${summary.webSearches} web search${summary.webSearches === 1 ? '' : 'es'}`);\n if (summary.evalRuns > 0) parts.push(`ran ${summary.evalRuns} eval suite${summary.evalRuns === 1 ? '' : 's'}`);\n if (summary.goalsCompleted > 0) parts.push(`completed ${summary.goalsCompleted} goal${summary.goalsCompleted === 1 ? '' : 's'}`);\n if (summary.selfImproveProposals > 0) parts.push(`filed ${summary.selfImproveProposals} self-improvement proposal${summary.selfImproveProposals === 1 ? '' : 's'}`);\n if (summary.errorRecoveries > 0) parts.push(`recovered from ${summary.errorRecoveries} error${summary.errorRecoveries === 1 ? '' : 's'}`);\n\n if (parts.length === 0) return '';\n\n let narrative = `In the last ${summary.periodHours}h, TITAN ${parts.join(', ')}.`;\n if (summary.highlights.length > 0) {\n narrative += ` Highlights: ${summary.highlights.join('; ')}.`;\n }\n return narrative;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,154 @@
1
+ # TITAN Agent Orchestration Hierarchy
2
+
3
+ > **Audience:** anyone touching `src/agent/`. If you're about to add a new
4
+ > file or another way to spawn a sub-agent, read this first. Most of the
5
+ > time the answer is "use the existing path."
6
+
7
+ TITAN's agent layer has three distinct concerns. Keeping them separate
8
+ prevents the "I'll just add another orchestrator" cycle that produced
9
+ the v5.4.x cleanup pass.
10
+
11
+ ---
12
+
13
+ ## The three layers
14
+
15
+ ```
16
+ ┌─────────────────────────────────────────────────────────────┐
17
+ │ LAYER 1 — Conversation loop (one user, one turn) │
18
+ │ │
19
+ │ /api/message → multiAgent.routeMessage │
20
+ │ → agent.processMessage │
21
+ │ → agentLoop (THINK → ACT → RESPOND) │
22
+ │ │
23
+ │ Files: agent/loop/{agent,agentLoop,agentEvents, │
24
+ │ contextManager,contextCompressor,promptBudget}.ts │
25
+ └─────────────────────────────────────────────────────────────┘
26
+
27
+ │ needs parallel / specialized work?
28
+
29
+ ┌─────────────────────────────────────────────────────────────┐
30
+ │ LAYER 2 — Delegation (this turn spawns sub-agents) │
31
+ │ │
32
+ │ orchestrator.analyzeForDelegation │
33
+ │ → spawnSubAgent(template, task, ctx) ← CANONICAL │
34
+ │ hierarchicalPlanner.decomposeHierarchically │
35
+ │ → spawnSubAgent for each leaf │
36
+ │ │
37
+ │ Specialists: explorer, coder, browser, analyst, … │
38
+ │ │
39
+ │ Files: agent/delegation/{subAgent,orchestrator, │
40
+ │ specialists,specialistRouter, │
41
+ │ hierarchicalPlanner,structuredSpawn,swarm}.ts │
42
+ └─────────────────────────────────────────────────────────────┘
43
+
44
+ │ no user prompt at all? scheduled / autonomous?
45
+
46
+ ┌─────────────────────────────────────────────────────────────┐
47
+ │ LAYER 3 — Autonomy (no-user-prompt background work) │
48
+ │ │
49
+ │ driverScheduler (10s tick, concurrency cap = 5) │
50
+ │ │ │
51
+ │ ├─ goalDriver (per-goal state machine) │
52
+ │ ├─ missionDriver (multi-goal coordination) │
53
+ │ ├─ autopilot (fallback chain orchestration) │
54
+ │ └─ initiative (build-loop initiative engine) │
55
+ │ │
56
+ │ Soma drives → goalProposer → driverScheduler picks up │
57
+ │ │
58
+ │ Files: agent/autonomy/{driverScheduler,goalDriver, │
59
+ │ missionDriver,autopilot,initiative, │
60
+ │ driverAwareChat,heartbeatScheduler}.ts │
61
+ └─────────────────────────────────────────────────────────────┘
62
+
63
+ │ spans every layer
64
+
65
+ ┌─────────────────────────────────────────────────────────────┐
66
+ │ GOVERNANCE OVERLAY (runs alongside, not below) │
67
+ │ │
68
+ │ commandPost — atomic checkout, registry, ancestry │
69
+ │ budgetEnforcer — token + dollar caps per goal │
70
+ │ guardrails — input/tool/output content filters │
71
+ │ approvalClassifier — auto-approve vs ask-user │
72
+ │ │
73
+ │ Files: agent/governance/{commandPost,budgetEnforcer, │
74
+ │ guardrails,approvalClassifier,auditLog, │
75
+ │ auditStore,outputGuardrails}.ts │
76
+ └─────────────────────────────────────────────────────────────┘
77
+ ```
78
+
79
+ ---
80
+
81
+ ## When do I use which?
82
+
83
+ | You want to… | Layer | Use |
84
+ |---|---|---|
85
+ | Process a message from the user / a channel / voice | 1 | `agent.processMessage(msg, channel, userId)` |
86
+ | Spawn a fresh sub-agent for a parallel sub-task in the current turn | 2 | `subAgent.spawnSubAgent(template, task, ctx)` ← **always this** |
87
+ | Decompose a multi-step request into a planned tree | 2 | `hierarchicalPlanner.decomposeHierarchically(...)` then iterate `spawnSubAgent` per leaf |
88
+ | Decide *whether* to delegate at all | 2 | `orchestrator.analyzeForDelegation(message)` |
89
+ | Run a goal in the background until it completes | 3 | Create the goal via Command Post → `driverScheduler` picks it up automatically |
90
+ | Coordinate multiple goals into one outcome | 3 | `missionDriver.createMission({...})` |
91
+ | Add a budget / approval / audit gate | overlay | Hook into `commandPost`, `budgetEnforcer`, or `guardrails` — **don't add a new gate file**, extend an existing one |
92
+
93
+ ---
94
+
95
+ ## Anti-patterns (these are **deprecated** as of v5.4.x cleanup)
96
+
97
+ - ❌ `swarm.runSubAgent` — was a parallel implementation of `spawnSubAgent`. Re-exports through the canonical path; new call sites should use `spawnSubAgent` directly.
98
+ - ❌ `structuredSpawn.spawn` — was a third copy. Same re-export shim.
99
+ - ❌ Inline `chat({ systemPrompt: 'You are an explorer...', ... })` calls — bypasses the specialist registry, the governance overlay, and the trace bus. **Always go through `spawnSubAgent`.**
100
+ - ❌ A new file in `src/agent/` to "do something kind of like sub-agents but different" — talk to the architecture before adding. 99% of the time, an existing primitive covers it.
101
+
102
+ ---
103
+
104
+ ## Soma feedback loop
105
+
106
+ The autonomous layer has one input the others lack: **homeostatic drives**.
107
+
108
+ ```
109
+ driveTickWatcher (every N seconds)
110
+
111
+
112
+ organism/pressure.ts computes pressure per drive
113
+
114
+ ▼ if pressure > threshold
115
+ goalProposer emits a draft goal
116
+
117
+
118
+ commandPost.createIssue (governance entry point)
119
+
120
+
121
+ driverScheduler picks up the new goal next tick
122
+ ```
123
+
124
+ This is the part that's actually hard to copy. LangGraph / CrewAI / Mastra have nothing equivalent. If you're touching this loop, read `docs/ADR-001-soma.md` first.
125
+
126
+ ---
127
+
128
+ ## File-organization contract
129
+
130
+ Every file in `src/agent/` lives in one of six subdirectories:
131
+
132
+ | Directory | What goes here |
133
+ |---|---|
134
+ | `agent/loop/` | Per-turn conversation processing |
135
+ | `agent/delegation/` | Sub-agent spawn + specialist registry + hierarchical planning |
136
+ | `agent/autonomy/` | Background drivers, schedulers, mission/goal state machines |
137
+ | `agent/governance/` | Cross-cutting policy: budgets, approvals, audit, guardrails |
138
+ | `agent/self-mod/` | Self-improvement: GEPA, proposals, staging, shadow git |
139
+ | `agent/tooling/` | Tool execution: runner, search, sandbox, parallel exec |
140
+
141
+ When in doubt, check which **layer** above your concern belongs to and the
142
+ subdirectory follows.
143
+
144
+ ---
145
+
146
+ ## Testing the contract
147
+
148
+ `tests/architecture-contract.test.ts` (TODO: write) should grep `src/agent/` for:
149
+
150
+ - Any file outside the six subdirectories → fail
151
+ - Any inline `chat({ systemPrompt: 'You are a ...' })` outside `delegation/specialists.ts` → fail
152
+ - Any `runSubAgent` / `structuredSpawn` import that isn't the deprecated re-export shim → fail
153
+
154
+ That's how this contract stays a contract instead of a doc nobody reads.