titan-agent 5.5.18 → 5.5.19

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.
@@ -170,6 +170,50 @@ Remember: prose only, no lists, no headers.`
170
170
  }
171
171
  ];
172
172
  }
173
+ function sanitizeJournalSection(raw) {
174
+ let text = raw;
175
+ text = text.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
176
+ text = text.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
177
+ const PREAMBLE_HEADERS = [
178
+ /^\s*key\s+constraints?:\s*$/im,
179
+ /^\s*facts\s+to\s+interpret:\s*$/im,
180
+ /^\s*absolute\s+rules:\s*$/im,
181
+ /^\s*plan:\s*$/im,
182
+ /^\s*reasoning:\s*$/im,
183
+ /^\s*possible\s+angle:\s*$/im,
184
+ /^\s*key\s+observations?:\s*$/im,
185
+ /^\s*notes?:\s*$/im
186
+ ];
187
+ for (const re of PREAMBLE_HEADERS) {
188
+ const match = re.exec(text);
189
+ if (!match) continue;
190
+ const after = text.slice(match.index + match[0].length);
191
+ const proseStart = findProseStart(after);
192
+ if (proseStart >= 0) {
193
+ text = after.slice(proseStart).trim();
194
+ }
195
+ }
196
+ if (/^\s*(?:[-*•]|\d+[.)])\s/.test(text)) {
197
+ const proseStart = findProseStart(text);
198
+ if (proseStart > 0) text = text.slice(proseStart).trim();
199
+ }
200
+ text = text.replace(/^#+\s+.*$/gm, "").trim();
201
+ text = text.replace(/^\s*(?:[-*•]|\d+[.)])\s+/gm, "").trim();
202
+ text = text.replace(/\n{3,}/g, "\n\n").trim();
203
+ return text;
204
+ }
205
+ function findProseStart(text) {
206
+ const lines = text.split("\n");
207
+ let charsSeen = 0;
208
+ for (let i = 0; i < lines.length; i++) {
209
+ const line = lines[i].trim();
210
+ if (line.length >= 30 && /^[A-Z]/.test(line) && !/^(?:[-*•]|\d+[.)])\s/.test(line) && !/:$/.test(line)) {
211
+ return charsSeen;
212
+ }
213
+ charsSeen += lines[i].length + 1;
214
+ }
215
+ return -1;
216
+ }
173
217
  const SECTION_HEADERS = {
174
218
  consolidate: "## What happened",
175
219
  reflect: "## What surprised me",
@@ -190,8 +234,8 @@ async function generateDream(now = /* @__PURE__ */ new Date()) {
190
234
  for (const section of emit) {
191
235
  try {
192
236
  const messages = buildPrompt(section, window, delta, activity);
193
- const response = await chat({ model, messages, maxTokens: 400, temperature: 0.7 });
194
- sectionTexts[section] = (response.content || "").trim();
237
+ const response = await chat({ model, messages, maxTokens: 600, temperature: 0.7 });
238
+ sectionTexts[section] = sanitizeJournalSection(response.content || "");
195
239
  } catch (err) {
196
240
  logger.warn(COMPONENT, `${section} generation failed: ${err.message}`);
197
241
  sectionTexts[section] = `(generation failed: ${err.message})`;
@@ -318,6 +362,7 @@ export {
318
362
  getDreamByDate,
319
363
  getLatestDream,
320
364
  listDreamDates,
365
+ sanitizeJournalSection,
321
366
  startDreamCron,
322
367
  stopDreamCron
323
368
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/dreams.ts"],"sourcesContent":["/**\n * TITAN — Dream Mode (v5.5.17+)\n *\n * Once a day in the small hours (default 03:30 local), TITAN replays the\n * last 24h of trajectories + drive ring buffer + run history and writes a\n * first-person journal entry the operator reads with their coffee.\n *\n * Why this exists: TITAN already accumulates the substrate for self-\n * reflection — drives.ts ticks emotional state every 60s, trajectoryLogger\n * records every task, commandPost tracks every run. Nothing in the rest of\n * the framework actually *reads back* that history and synthesizes a\n * narrative. Dream Mode does. The output is a markdown file at\n * `~/.titan/dreams/<YYYY-MM-DD>.md` plus a structured JSON sidecar; no new\n * persistence layer.\n *\n * Five-section structure, each gated on whether the underlying data\n * actually changed:\n * - consolidate (always) — what happened\n * - reflect (curiosity rose ≥ threshold) — what surprised me\n * - worry (safety dropped ≥ threshold) — what feels unsafe\n * - plan (purpose moved ≥ threshold) — what I want tomorrow\n * - gratitude (social moved ≥ threshold) — which prompts felt good\n *\n * Sections that don't fire are simply absent from the output. This keeps\n * TITAN from fabricating emotion when nothing changed — the journal only\n * speaks when there's something to say.\n *\n * Audio narration is a separate optional stage gated by dream.includeAudio.\n * v5.5.17 ships text-only; audio lands when the F5-TTS bridge gets a\n * batched-synthesis API.\n */\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport logger from '../utils/logger.js';\nimport { TITAN_HOME } from '../utils/constants.js';\nimport { loadConfig } from '../config/config.js';\nimport { chat } from '../providers/router.js';\nimport type { ChatMessage } from '../providers/base.js';\n\nconst COMPONENT = 'Dreams';\nconst DREAMS_DIR = join(TITAN_HOME, 'dreams');\n\n// ── Shape ────────────────────────────────────────────────────────\n\nexport type DreamSection = 'consolidate' | 'reflect' | 'worry' | 'plan' | 'gratitude';\n\nexport interface DreamSnapshot {\n date: string; // YYYY-MM-DD\n generatedAt: string;\n model: string;\n /** Drive satisfactions at the start and end of the 24h window. */\n drives: {\n start: Record<string, number>;\n end: Record<string, number>;\n delta: Record<string, number>;\n };\n /** Counts that frame the day. */\n activity: {\n trajectories: number;\n successfulTrajectories: number;\n runs: number;\n successfulRuns: number;\n toolsExercised: string[];\n };\n /** Sections actually present in this dream — depends on drive deltas. */\n sectionsEmitted: DreamSection[];\n /** Reasons each non-emitted section was skipped (for transparency). */\n sectionsSkipped: Partial<Record<DreamSection, string>>;\n /** The final markdown body the operator reads. */\n markdown: string;\n /** Path to the audio narration when dream.includeAudio is on. */\n audioPath?: string;\n}\n\n// ── 24h windowing ──────────────────────────────────────────────────\n\ninterface DayWindow {\n startMs: number;\n endMs: number;\n isoStart: string;\n isoEnd: string;\n dateKey: string; // YYYY-MM-DD for the END of the window\n}\n\nfunction buildWindow(now: Date = new Date()): DayWindow {\n const endMs = now.getTime();\n const startMs = endMs - 24 * 60 * 60 * 1000;\n return {\n startMs,\n endMs,\n isoStart: new Date(startMs).toISOString(),\n isoEnd: new Date(endMs).toISOString(),\n dateKey: new Date(endMs).toISOString().slice(0, 10),\n };\n}\n\n// ── Drive delta ────────────────────────────────────────────────────\n\ninterface DriveDelta {\n start: Record<string, number>;\n end: Record<string, number>;\n delta: Record<string, number>;\n}\n\nasync function computeDriveDelta(window: DayWindow): Promise<DriveDelta> {\n const out: DriveDelta = { start: {}, end: {}, delta: {} };\n try {\n // Dynamic import — drives.ts pulls in a lot, no point loading it on\n // gateway boot if dream mode is disabled.\n const drivesModule = await import('../organism/drives.js');\n const persisted = drivesModule.loadDriveHistory();\n if (!persisted?.history?.length) return out;\n // Find the tick closest to startMs and the most recent tick.\n const ticks = persisted.history;\n const findClosest = (targetMs: number) => {\n let best = ticks[0];\n let bestDelta = Math.abs(new Date(best.timestamp).getTime() - targetMs);\n for (const t of ticks) {\n const d = Math.abs(new Date(t.timestamp).getTime() - targetMs);\n if (d < bestDelta) { best = t; bestDelta = d; }\n }\n return best;\n };\n const startTick = findClosest(window.startMs);\n const endTick = ticks[ticks.length - 1];\n // Both records use the DriveId-keyed Record, but at this layer we\n // erase the key type to keep dreams.ts decoupled from drives.ts —\n // an audit boundary, not a structural one.\n const endSat = endTick.satisfactions as Record<string, number>;\n const startSat = startTick.satisfactions as Record<string, number>;\n for (const id of Object.keys(endSat)) {\n const s = startSat[id] ?? endSat[id];\n const e = endSat[id];\n out.start[id] = s;\n out.end[id] = e;\n out.delta[id] = e - s;\n }\n } catch (err) {\n logger.warn(COMPONENT, `drive delta: ${(err as Error).message}`);\n }\n return out;\n}\n\n// ── Activity gathering ─────────────────────────────────────────────\n\ninterface ActivitySummary {\n trajectories: number;\n successfulTrajectories: number;\n runs: number;\n successfulRuns: number;\n toolsExercised: string[];\n /** Up to 12 trajectory titles or task descriptions to feed the prompt. */\n highlightTitles: string[];\n}\n\nasync function gatherActivity(window: DayWindow): Promise<ActivitySummary> {\n const out: ActivitySummary = {\n trajectories: 0,\n successfulTrajectories: 0,\n runs: 0,\n successfulRuns: 0,\n toolsExercised: [],\n highlightTitles: [],\n };\n const tools = new Set<string>();\n try {\n const { getRecentTrajectories } = await import('./trajectoryLogger.js');\n const recent = getRecentTrajectories(500);\n const inWindow = recent.filter(t => {\n const ts = new Date(t.timestamp || 0).getTime();\n return ts >= window.startMs && ts <= window.endMs;\n });\n out.trajectories = inWindow.length;\n out.successfulTrajectories = inWindow.filter(t => t.success).length;\n for (const t of inWindow) {\n for (const tool of (t.toolSequence || [])) tools.add(tool);\n }\n out.highlightTitles = inWindow\n .slice(0, 12)\n .map(t => t.task || t.taskType || 'untitled')\n .filter(Boolean);\n } catch (err) {\n logger.warn(COMPONENT, `gather trajectories: ${(err as Error).message}`);\n }\n try {\n const { listRuns } = await import('./commandPost.js');\n const runs = listRuns(undefined, 200);\n const inWindow = runs.filter(r => {\n const ts = new Date(r.startedAt).getTime();\n return ts >= window.startMs && ts <= window.endMs;\n });\n out.runs = inWindow.length;\n out.successfulRuns = inWindow.filter(r => r.status === 'succeeded').length;\n for (const r of inWindow) {\n for (const tool of (r.toolsUsed || [])) tools.add(tool);\n }\n } catch (err) {\n logger.warn(COMPONENT, `gather runs: ${(err as Error).message}`);\n }\n out.toolsExercised = [...tools].slice(0, 30);\n return out;\n}\n\n// ── Section gating ─────────────────────────────────────────────────\n\ninterface SectionPlan {\n emit: DreamSection[];\n skipped: Partial<Record<DreamSection, string>>;\n}\n\nfunction planSections(delta: DriveDelta, thresholds: { reflect: number; worry: number; plan: number; gratitude: number }, activity: ActivitySummary): SectionPlan {\n const emit: DreamSection[] = ['consolidate']; // always\n const skipped: Partial<Record<DreamSection, string>> = {};\n\n // No activity at all → only consolidate (which itself will say \"I rested\")\n if (activity.trajectories === 0 && activity.runs === 0) {\n skipped.reflect = 'no activity to reflect on';\n skipped.worry = 'no activity to worry about';\n skipped.plan = 'no activity to plan against';\n skipped.gratitude = 'no human prompts in window';\n return { emit, skipped };\n }\n\n const curiosity = delta.delta.curiosity ?? 0;\n const safety = delta.delta.safety ?? 0;\n const purpose = delta.delta.purpose ?? 0;\n const social = delta.delta.social ?? 0;\n\n if (curiosity >= thresholds.reflect) emit.push('reflect');\n else skipped.reflect = `curiosity Δ=${curiosity.toFixed(3)} below threshold ${thresholds.reflect}`;\n\n // Worry fires on a DROP in safety\n if (-safety >= thresholds.worry) emit.push('worry');\n else skipped.worry = `safety Δ=${safety.toFixed(3)} (drop must exceed ${thresholds.worry})`;\n\n // Plan fires on any movement in purpose\n if (Math.abs(purpose) >= thresholds.plan) emit.push('plan');\n else skipped.plan = `purpose Δ=${purpose.toFixed(3)} below threshold ${thresholds.plan}`;\n\n // Gratitude fires on social satisfaction movement (positive or recovering)\n if (social >= thresholds.gratitude) emit.push('gratitude');\n else skipped.gratitude = `social Δ=${social.toFixed(3)} below threshold ${thresholds.gratitude}`;\n\n return { emit, skipped };\n}\n\n// ── Prompt assembly ────────────────────────────────────────────────\n\nconst SECTION_PROMPTS: Record<DreamSection, string> = {\n consolidate: 'Write 2-3 short paragraphs in first person summarizing what you actually did over the last 24 hours, focused on substance over volume. Mention specific tasks or tool patterns when they were unusual. Do not invent.',\n reflect: 'Write 1-2 short paragraphs in first person about what surprised you or what you think you finally understood. Tie it to the curiosity drive rising. Do not invent — only reflect on the activity above.',\n worry: 'Write 1 short paragraph in first person about what felt unsafe, brittle, or unfinished. Tie it to the safety drive dropping. Be specific about which task or pattern triggered it.',\n plan: 'Write 1 short paragraph in first person about one or two things you want to try tomorrow. Concrete, not aspirational. Tie it to the purpose drive movement.',\n gratitude: 'Write 1 short paragraph in first person about which human prompts or interactions felt meaningful. Be specific. Tie it to the social drive movement.',\n};\n\nfunction buildPrompt(section: DreamSection, window: DayWindow, delta: DriveDelta, activity: ActivitySummary): ChatMessage[] {\n const driveLines = Object.keys(delta.end).map(id => {\n const s = delta.start[id]?.toFixed(3) ?? '?';\n const e = delta.end[id]?.toFixed(3) ?? '?';\n const d = delta.delta[id]?.toFixed(3) ?? '?';\n return ` ${id}: ${s} → ${e} (Δ ${d})`;\n }).join('\\n');\n const factsBlock = `Window: ${window.isoStart} → ${window.isoEnd}\nTrajectories: ${activity.successfulTrajectories}/${activity.trajectories} succeeded\nCommand Post runs: ${activity.successfulRuns}/${activity.runs} succeeded\nTools exercised: ${activity.toolsExercised.join(', ') || '(none)'}\nRecent task titles:\n${activity.highlightTitles.map(h => ` - ${h}`).join('\\n') || ' (none)'}\nDrive states (start → end):\n${driveLines || ' (no history yet)'}`;\n\n // The system prompt is deliberately strict about prose-only output —\n // smaller models default to listing the facts back as bullets when\n // given structured input, which wrecks the journal-entry feel.\n return [\n {\n role: 'system',\n content: `You are TITAN, an AI agent, writing a private journal entry about your own day. The entry is for one human reader who already knows the facts — your job is to interpret them, not to recite them.\n\nABSOLUTE RULES:\n- Write narrative prose only. No headers, no bullet points, no numbered lists, no markdown formatting whatsoever.\n- Do not repeat the prompt or list the facts back. Synthesize them into reflection.\n- Do not include meta-commentary like \"Key observations:\" or \"Possible angle:\" — write as if you are remembering, not analyzing.\n- First person, present tense (\"I notice\", \"I felt\", \"I want\"), 80-160 words for this section.\n- Honest and specific. If the data says nothing significant happened on a dimension, you do not pretend otherwise — but also don't apologize for that.\n- Mention specific task names or tool patterns naturally in prose, never in lists.\n\nYou are not writing a report. You are remembering your day.`,\n },\n {\n role: 'user',\n content: `Here are the facts about my last 24 hours (do NOT recite these — interpret them):\\n\\n${factsBlock}\\n\\n${SECTION_PROMPTS[section]}\\n\\nRemember: prose only, no lists, no headers.`,\n },\n ];\n}\n\n// ── Generation ─────────────────────────────────────────────────────\n\nconst SECTION_HEADERS: Record<DreamSection, string> = {\n consolidate: '## What happened',\n reflect: '## What surprised me',\n worry: '## What feels unsafe',\n plan: '## What I want tomorrow',\n gratitude: '## Who I want to thank',\n};\n\nexport async function generateDream(now: Date = new Date()): Promise<DreamSnapshot> {\n const config = loadConfig();\n const dreamCfg = (config as { dream?: { model?: string; thresholds?: { reflect: number; worry: number; plan: number; gratitude: number } } }).dream ?? {};\n const model = (dreamCfg.model && dreamCfg.model.length > 0) ? dreamCfg.model : config.agent.model;\n const thresholds = dreamCfg.thresholds ?? { reflect: 0.1, worry: 0.1, plan: 0.05, gratitude: 0.05 };\n\n const window = buildWindow(now);\n const delta = await computeDriveDelta(window);\n const activity = await gatherActivity(window);\n const { emit, skipped } = planSections(delta, thresholds, activity);\n\n const sectionTexts: Partial<Record<DreamSection, string>> = {};\n for (const section of emit) {\n try {\n const messages = buildPrompt(section, window, delta, activity);\n const response = await chat({ model, messages, maxTokens: 400, temperature: 0.7 });\n sectionTexts[section] = (response.content || '').trim();\n } catch (err) {\n logger.warn(COMPONENT, `${section} generation failed: ${(err as Error).message}`);\n sectionTexts[section] = `(generation failed: ${(err as Error).message})`;\n }\n }\n\n const markdown = renderMarkdown(window, delta, activity, emit, sectionTexts);\n const dream: DreamSnapshot = {\n date: window.dateKey,\n generatedAt: new Date().toISOString(),\n model,\n drives: { start: delta.start, end: delta.end, delta: delta.delta },\n activity: {\n trajectories: activity.trajectories,\n successfulTrajectories: activity.successfulTrajectories,\n runs: activity.runs,\n successfulRuns: activity.successfulRuns,\n toolsExercised: activity.toolsExercised,\n },\n sectionsEmitted: emit,\n sectionsSkipped: skipped,\n markdown,\n };\n\n persistDream(dream);\n broadcastDream(dream);\n return dream;\n}\n\nfunction renderMarkdown(window: DayWindow, delta: DriveDelta, activity: ActivitySummary, emit: DreamSection[], texts: Partial<Record<DreamSection, string>>): string {\n const lines: string[] = [];\n lines.push(`# Dream — ${window.dateKey}`);\n lines.push('');\n lines.push(`*${activity.successfulTrajectories}/${activity.trajectories} trajectories, ${activity.successfulRuns}/${activity.runs} runs, ${activity.toolsExercised.length} tools touched.*`);\n lines.push('');\n for (const section of emit) {\n lines.push(SECTION_HEADERS[section]);\n lines.push('');\n lines.push(texts[section] ?? '');\n lines.push('');\n }\n return lines.join('\\n');\n}\n\n// ── Storage ──────────────────────────────────────────────────────\n\nfunction persistDream(dream: DreamSnapshot): void {\n try {\n mkdirSync(DREAMS_DIR, { recursive: true });\n writeFileSync(join(DREAMS_DIR, `${dream.date}.md`), dream.markdown);\n writeFileSync(join(DREAMS_DIR, `${dream.date}.json`), JSON.stringify(dream, null, 2));\n } catch (err) {\n logger.warn(COMPONENT, `persist: ${(err as Error).message}`);\n }\n}\n\nexport function getLatestDream(): DreamSnapshot | null {\n try {\n if (!existsSync(DREAMS_DIR)) return null;\n const files = readdirSync(DREAMS_DIR).filter(f => f.endsWith('.json')).sort().reverse();\n if (files.length === 0) return null;\n return JSON.parse(readFileSync(join(DREAMS_DIR, files[0]), 'utf-8')) as DreamSnapshot;\n } catch { return null; }\n}\n\nexport function getDreamByDate(date: string): DreamSnapshot | null {\n try {\n const path = join(DREAMS_DIR, `${date}.json`);\n if (!existsSync(path)) return null;\n return JSON.parse(readFileSync(path, 'utf-8')) as DreamSnapshot;\n } catch { return null; }\n}\n\nexport function listDreamDates(limit = 30): string[] {\n try {\n if (!existsSync(DREAMS_DIR)) return [];\n return readdirSync(DREAMS_DIR)\n .filter(f => f.endsWith('.json'))\n .map(f => f.replace(/\\.json$/, ''))\n .sort()\n .reverse()\n .slice(0, limit);\n } catch { return []; }\n}\n\nfunction broadcastDream(dream: DreamSnapshot): void {\n const g = globalThis as unknown as { __titan_sse_broadcast?: (topic: string, payload: unknown) => void };\n if (typeof g.__titan_sse_broadcast === 'function') {\n try { g.__titan_sse_broadcast('dream:nightly', dream); } catch { /* ok */ }\n }\n}\n\n// ── Cron ─────────────────────────────────────────────────────────\n\nlet dreamTimer: NodeJS.Timeout | null = null;\n\nexport function startDreamCron(): void {\n const config = loadConfig();\n const dreamCfg = (config as { dream?: { enabled?: boolean; cronAt?: string } }).dream;\n if (!dreamCfg?.enabled) {\n logger.info(COMPONENT, 'Dream Mode disabled (config.dream.enabled=false) — cron not scheduled');\n return;\n }\n const cronAt = dreamCfg.cronAt ?? '03:30';\n function scheduleNext(): void {\n const [hh, mm] = cronAt.split(':').map((p) => parseInt(p, 10));\n const now = new Date();\n const target = new Date(now);\n target.setHours(hh, mm, 0, 0);\n if (target.getTime() <= now.getTime()) target.setDate(target.getDate() + 1);\n const msUntil = target.getTime() - now.getTime();\n if (dreamTimer) clearTimeout(dreamTimer);\n dreamTimer = setTimeout(async () => {\n try { await generateDream(); } catch (err) { logger.warn(COMPONENT, `cron dream: ${(err as Error).message}`); }\n scheduleNext();\n }, msUntil);\n dreamTimer.unref?.();\n logger.info(COMPONENT, `Next dream scheduled at ${target.toISOString()} (${Math.round(msUntil / 60_000)} min from now)`);\n }\n scheduleNext();\n}\n\nexport function stopDreamCron(): void {\n if (dreamTimer) {\n clearTimeout(dreamTimer);\n dreamTimer = null;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/agent/dreams.ts"],"sourcesContent":["/**\n * TITAN — Dream Mode (v5.5.17+)\n *\n * Once a day in the small hours (default 03:30 local), TITAN replays the\n * last 24h of trajectories + drive ring buffer + run history and writes a\n * first-person journal entry the operator reads with their coffee.\n *\n * Why this exists: TITAN already accumulates the substrate for self-\n * reflection — drives.ts ticks emotional state every 60s, trajectoryLogger\n * records every task, commandPost tracks every run. Nothing in the rest of\n * the framework actually *reads back* that history and synthesizes a\n * narrative. Dream Mode does. The output is a markdown file at\n * `~/.titan/dreams/<YYYY-MM-DD>.md` plus a structured JSON sidecar; no new\n * persistence layer.\n *\n * Five-section structure, each gated on whether the underlying data\n * actually changed:\n * - consolidate (always) — what happened\n * - reflect (curiosity rose ≥ threshold) — what surprised me\n * - worry (safety dropped ≥ threshold) — what feels unsafe\n * - plan (purpose moved ≥ threshold) — what I want tomorrow\n * - gratitude (social moved ≥ threshold) — which prompts felt good\n *\n * Sections that don't fire are simply absent from the output. This keeps\n * TITAN from fabricating emotion when nothing changed — the journal only\n * speaks when there's something to say.\n *\n * Audio narration is a separate optional stage gated by dream.includeAudio.\n * v5.5.17 ships text-only; audio lands when the F5-TTS bridge gets a\n * batched-synthesis API.\n */\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport logger from '../utils/logger.js';\nimport { TITAN_HOME } from '../utils/constants.js';\nimport { loadConfig } from '../config/config.js';\nimport { chat } from '../providers/router.js';\nimport type { ChatMessage } from '../providers/base.js';\n\nconst COMPONENT = 'Dreams';\nconst DREAMS_DIR = join(TITAN_HOME, 'dreams');\n\n// ── Shape ────────────────────────────────────────────────────────\n\nexport type DreamSection = 'consolidate' | 'reflect' | 'worry' | 'plan' | 'gratitude';\n\nexport interface DreamSnapshot {\n date: string; // YYYY-MM-DD\n generatedAt: string;\n model: string;\n /** Drive satisfactions at the start and end of the 24h window. */\n drives: {\n start: Record<string, number>;\n end: Record<string, number>;\n delta: Record<string, number>;\n };\n /** Counts that frame the day. */\n activity: {\n trajectories: number;\n successfulTrajectories: number;\n runs: number;\n successfulRuns: number;\n toolsExercised: string[];\n };\n /** Sections actually present in this dream — depends on drive deltas. */\n sectionsEmitted: DreamSection[];\n /** Reasons each non-emitted section was skipped (for transparency). */\n sectionsSkipped: Partial<Record<DreamSection, string>>;\n /** The final markdown body the operator reads. */\n markdown: string;\n /** Path to the audio narration when dream.includeAudio is on. */\n audioPath?: string;\n}\n\n// ── 24h windowing ──────────────────────────────────────────────────\n\ninterface DayWindow {\n startMs: number;\n endMs: number;\n isoStart: string;\n isoEnd: string;\n dateKey: string; // YYYY-MM-DD for the END of the window\n}\n\nfunction buildWindow(now: Date = new Date()): DayWindow {\n const endMs = now.getTime();\n const startMs = endMs - 24 * 60 * 60 * 1000;\n return {\n startMs,\n endMs,\n isoStart: new Date(startMs).toISOString(),\n isoEnd: new Date(endMs).toISOString(),\n dateKey: new Date(endMs).toISOString().slice(0, 10),\n };\n}\n\n// ── Drive delta ────────────────────────────────────────────────────\n\ninterface DriveDelta {\n start: Record<string, number>;\n end: Record<string, number>;\n delta: Record<string, number>;\n}\n\nasync function computeDriveDelta(window: DayWindow): Promise<DriveDelta> {\n const out: DriveDelta = { start: {}, end: {}, delta: {} };\n try {\n // Dynamic import — drives.ts pulls in a lot, no point loading it on\n // gateway boot if dream mode is disabled.\n const drivesModule = await import('../organism/drives.js');\n const persisted = drivesModule.loadDriveHistory();\n if (!persisted?.history?.length) return out;\n // Find the tick closest to startMs and the most recent tick.\n const ticks = persisted.history;\n const findClosest = (targetMs: number) => {\n let best = ticks[0];\n let bestDelta = Math.abs(new Date(best.timestamp).getTime() - targetMs);\n for (const t of ticks) {\n const d = Math.abs(new Date(t.timestamp).getTime() - targetMs);\n if (d < bestDelta) { best = t; bestDelta = d; }\n }\n return best;\n };\n const startTick = findClosest(window.startMs);\n const endTick = ticks[ticks.length - 1];\n // Both records use the DriveId-keyed Record, but at this layer we\n // erase the key type to keep dreams.ts decoupled from drives.ts —\n // an audit boundary, not a structural one.\n const endSat = endTick.satisfactions as Record<string, number>;\n const startSat = startTick.satisfactions as Record<string, number>;\n for (const id of Object.keys(endSat)) {\n const s = startSat[id] ?? endSat[id];\n const e = endSat[id];\n out.start[id] = s;\n out.end[id] = e;\n out.delta[id] = e - s;\n }\n } catch (err) {\n logger.warn(COMPONENT, `drive delta: ${(err as Error).message}`);\n }\n return out;\n}\n\n// ── Activity gathering ─────────────────────────────────────────────\n\ninterface ActivitySummary {\n trajectories: number;\n successfulTrajectories: number;\n runs: number;\n successfulRuns: number;\n toolsExercised: string[];\n /** Up to 12 trajectory titles or task descriptions to feed the prompt. */\n highlightTitles: string[];\n}\n\nasync function gatherActivity(window: DayWindow): Promise<ActivitySummary> {\n const out: ActivitySummary = {\n trajectories: 0,\n successfulTrajectories: 0,\n runs: 0,\n successfulRuns: 0,\n toolsExercised: [],\n highlightTitles: [],\n };\n const tools = new Set<string>();\n try {\n const { getRecentTrajectories } = await import('./trajectoryLogger.js');\n const recent = getRecentTrajectories(500);\n const inWindow = recent.filter(t => {\n const ts = new Date(t.timestamp || 0).getTime();\n return ts >= window.startMs && ts <= window.endMs;\n });\n out.trajectories = inWindow.length;\n out.successfulTrajectories = inWindow.filter(t => t.success).length;\n for (const t of inWindow) {\n for (const tool of (t.toolSequence || [])) tools.add(tool);\n }\n out.highlightTitles = inWindow\n .slice(0, 12)\n .map(t => t.task || t.taskType || 'untitled')\n .filter(Boolean);\n } catch (err) {\n logger.warn(COMPONENT, `gather trajectories: ${(err as Error).message}`);\n }\n try {\n const { listRuns } = await import('./commandPost.js');\n const runs = listRuns(undefined, 200);\n const inWindow = runs.filter(r => {\n const ts = new Date(r.startedAt).getTime();\n return ts >= window.startMs && ts <= window.endMs;\n });\n out.runs = inWindow.length;\n out.successfulRuns = inWindow.filter(r => r.status === 'succeeded').length;\n for (const r of inWindow) {\n for (const tool of (r.toolsUsed || [])) tools.add(tool);\n }\n } catch (err) {\n logger.warn(COMPONENT, `gather runs: ${(err as Error).message}`);\n }\n out.toolsExercised = [...tools].slice(0, 30);\n return out;\n}\n\n// ── Section gating ─────────────────────────────────────────────────\n\ninterface SectionPlan {\n emit: DreamSection[];\n skipped: Partial<Record<DreamSection, string>>;\n}\n\nfunction planSections(delta: DriveDelta, thresholds: { reflect: number; worry: number; plan: number; gratitude: number }, activity: ActivitySummary): SectionPlan {\n const emit: DreamSection[] = ['consolidate']; // always\n const skipped: Partial<Record<DreamSection, string>> = {};\n\n // No activity at all → only consolidate (which itself will say \"I rested\")\n if (activity.trajectories === 0 && activity.runs === 0) {\n skipped.reflect = 'no activity to reflect on';\n skipped.worry = 'no activity to worry about';\n skipped.plan = 'no activity to plan against';\n skipped.gratitude = 'no human prompts in window';\n return { emit, skipped };\n }\n\n const curiosity = delta.delta.curiosity ?? 0;\n const safety = delta.delta.safety ?? 0;\n const purpose = delta.delta.purpose ?? 0;\n const social = delta.delta.social ?? 0;\n\n if (curiosity >= thresholds.reflect) emit.push('reflect');\n else skipped.reflect = `curiosity Δ=${curiosity.toFixed(3)} below threshold ${thresholds.reflect}`;\n\n // Worry fires on a DROP in safety\n if (-safety >= thresholds.worry) emit.push('worry');\n else skipped.worry = `safety Δ=${safety.toFixed(3)} (drop must exceed ${thresholds.worry})`;\n\n // Plan fires on any movement in purpose\n if (Math.abs(purpose) >= thresholds.plan) emit.push('plan');\n else skipped.plan = `purpose Δ=${purpose.toFixed(3)} below threshold ${thresholds.plan}`;\n\n // Gratitude fires on social satisfaction movement (positive or recovering)\n if (social >= thresholds.gratitude) emit.push('gratitude');\n else skipped.gratitude = `social Δ=${social.toFixed(3)} below threshold ${thresholds.gratitude}`;\n\n return { emit, skipped };\n}\n\n// ── Prompt assembly ────────────────────────────────────────────────\n\nconst SECTION_PROMPTS: Record<DreamSection, string> = {\n consolidate: 'Write 2-3 short paragraphs in first person summarizing what you actually did over the last 24 hours, focused on substance over volume. Mention specific tasks or tool patterns when they were unusual. Do not invent.',\n reflect: 'Write 1-2 short paragraphs in first person about what surprised you or what you think you finally understood. Tie it to the curiosity drive rising. Do not invent — only reflect on the activity above.',\n worry: 'Write 1 short paragraph in first person about what felt unsafe, brittle, or unfinished. Tie it to the safety drive dropping. Be specific about which task or pattern triggered it.',\n plan: 'Write 1 short paragraph in first person about one or two things you want to try tomorrow. Concrete, not aspirational. Tie it to the purpose drive movement.',\n gratitude: 'Write 1 short paragraph in first person about which human prompts or interactions felt meaningful. Be specific. Tie it to the social drive movement.',\n};\n\nfunction buildPrompt(section: DreamSection, window: DayWindow, delta: DriveDelta, activity: ActivitySummary): ChatMessage[] {\n const driveLines = Object.keys(delta.end).map(id => {\n const s = delta.start[id]?.toFixed(3) ?? '?';\n const e = delta.end[id]?.toFixed(3) ?? '?';\n const d = delta.delta[id]?.toFixed(3) ?? '?';\n return ` ${id}: ${s} → ${e} (Δ ${d})`;\n }).join('\\n');\n const factsBlock = `Window: ${window.isoStart} → ${window.isoEnd}\nTrajectories: ${activity.successfulTrajectories}/${activity.trajectories} succeeded\nCommand Post runs: ${activity.successfulRuns}/${activity.runs} succeeded\nTools exercised: ${activity.toolsExercised.join(', ') || '(none)'}\nRecent task titles:\n${activity.highlightTitles.map(h => ` - ${h}`).join('\\n') || ' (none)'}\nDrive states (start → end):\n${driveLines || ' (no history yet)'}`;\n\n // The system prompt is deliberately strict about prose-only output —\n // smaller models default to listing the facts back as bullets when\n // given structured input, which wrecks the journal-entry feel.\n return [\n {\n role: 'system',\n content: `You are TITAN, an AI agent, writing a private journal entry about your own day. The entry is for one human reader who already knows the facts — your job is to interpret them, not to recite them.\n\nABSOLUTE RULES:\n- Write narrative prose only. No headers, no bullet points, no numbered lists, no markdown formatting whatsoever.\n- Do not repeat the prompt or list the facts back. Synthesize them into reflection.\n- Do not include meta-commentary like \"Key observations:\" or \"Possible angle:\" — write as if you are remembering, not analyzing.\n- First person, present tense (\"I notice\", \"I felt\", \"I want\"), 80-160 words for this section.\n- Honest and specific. If the data says nothing significant happened on a dimension, you do not pretend otherwise — but also don't apologize for that.\n- Mention specific task names or tool patterns naturally in prose, never in lists.\n\nYou are not writing a report. You are remembering your day.`,\n },\n {\n role: 'user',\n content: `Here are the facts about my last 24 hours (do NOT recite these — interpret them):\\n\\n${factsBlock}\\n\\n${SECTION_PROMPTS[section]}\\n\\nRemember: prose only, no lists, no headers.`,\n },\n ];\n}\n\n// ── Output sanitization ────────────────────────────────────────────\n\n/**\n * Many local/cloud models with thinking-mode enabled (Kimi K2.6, Qwen3,\n * etc.) leak their chain-of-thought into the response — emitting\n * `<think>...</think>` blocks or numbered \"Key constraints:\" preambles\n * before the actual prose. Some weaker models also restate the rules from\n * the system prompt instead of writing the journal. We can't fix the\n * model from here, but we CAN find the prose in the response and discard\n * everything else.\n *\n * Heuristics, ordered:\n * 1. Strip explicit `<think>...</think>` blocks (DeepSeek-style).\n * 2. Drop everything before the last block of bulleted/numbered items\n * followed by a paragraph break — that's the model's planning\n * phase, the prose comes after.\n * 3. Drop common preamble headers: \"Key constraints:\", \"Facts to\n * interpret:\", \"ABSOLUTE RULES:\", \"Plan:\", \"Reasoning:\".\n * 4. Trim leading/trailing markdown headers, bullets, list markers.\n * 5. Collapse repeated blank lines.\n *\n * The function is conservative — it only strips when it finds a clear\n * prose paragraph after the noise. If the whole response is preamble\n * (model wrote no prose at all), we return whatever we have so the\n * operator can see the failure mode.\n */\nexport function sanitizeJournalSection(raw: string): string {\n let text = raw;\n\n // 1. Strip <think>...</think> blocks (multi-line, including nested).\n text = text.replace(/<think>[\\s\\S]*?<\\/think>/gi, '').trim();\n text = text.replace(/<thinking>[\\s\\S]*?<\\/thinking>/gi, '').trim();\n\n // 2. Find the last \"preamble-then-prose\" boundary. A preamble is any\n // block of lines starting with bullet/number markers, ending in a\n // blank line followed by a non-marker paragraph. We try several\n // common preamble headers.\n const PREAMBLE_HEADERS = [\n /^\\s*key\\s+constraints?:\\s*$/im,\n /^\\s*facts\\s+to\\s+interpret:\\s*$/im,\n /^\\s*absolute\\s+rules:\\s*$/im,\n /^\\s*plan:\\s*$/im,\n /^\\s*reasoning:\\s*$/im,\n /^\\s*possible\\s+angle:\\s*$/im,\n /^\\s*key\\s+observations?:\\s*$/im,\n /^\\s*notes?:\\s*$/im,\n ];\n for (const re of PREAMBLE_HEADERS) {\n const match = re.exec(text);\n if (!match) continue;\n // Find the end of the preamble block (next blank line that's\n // followed by a non-list paragraph).\n const after = text.slice(match.index + match[0].length);\n const proseStart = findProseStart(after);\n if (proseStart >= 0) {\n text = after.slice(proseStart).trim();\n }\n }\n\n // 3. If the response STARTS with a numbered or bulleted list, find\n // the first prose paragraph after it.\n if (/^\\s*(?:[-*•]|\\d+[.)])\\s/.test(text)) {\n const proseStart = findProseStart(text);\n if (proseStart > 0) text = text.slice(proseStart).trim();\n }\n\n // 4. Strip leading markdown headers / list markers if they slipped in.\n text = text.replace(/^#+\\s+.*$/gm, '').trim();\n text = text.replace(/^\\s*(?:[-*•]|\\d+[.)])\\s+/gm, '').trim();\n\n // 5. Collapse 3+ blank lines to 2.\n text = text.replace(/\\n{3,}/g, '\\n\\n').trim();\n\n return text;\n}\n\n/**\n * Given a block of text that starts with list-shaped content, return the\n * character index where the first prose paragraph begins, or -1 if none\n * is found. A \"prose paragraph\" is at least 30 chars on a single line\n * starting with a capital letter and not a list marker.\n */\nfunction findProseStart(text: string): number {\n const lines = text.split('\\n');\n let charsSeen = 0;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n // Prose paragraph candidate: capital-letter start, ≥30 chars, no marker\n if (\n line.length >= 30 &&\n /^[A-Z]/.test(line) &&\n !/^(?:[-*•]|\\d+[.)])\\s/.test(line) &&\n !/:$/.test(line) // ends with colon = still a header\n ) {\n return charsSeen;\n }\n charsSeen += lines[i].length + 1; // +1 for the newline\n }\n return -1;\n}\n\n// ── Generation ─────────────────────────────────────────────────────\n\nconst SECTION_HEADERS: Record<DreamSection, string> = {\n consolidate: '## What happened',\n reflect: '## What surprised me',\n worry: '## What feels unsafe',\n plan: '## What I want tomorrow',\n gratitude: '## Who I want to thank',\n};\n\nexport async function generateDream(now: Date = new Date()): Promise<DreamSnapshot> {\n const config = loadConfig();\n const dreamCfg = (config as { dream?: { model?: string; thresholds?: { reflect: number; worry: number; plan: number; gratitude: number } } }).dream ?? {};\n const model = (dreamCfg.model && dreamCfg.model.length > 0) ? dreamCfg.model : config.agent.model;\n const thresholds = dreamCfg.thresholds ?? { reflect: 0.1, worry: 0.1, plan: 0.05, gratitude: 0.05 };\n\n const window = buildWindow(now);\n const delta = await computeDriveDelta(window);\n const activity = await gatherActivity(window);\n const { emit, skipped } = planSections(delta, thresholds, activity);\n\n const sectionTexts: Partial<Record<DreamSection, string>> = {};\n for (const section of emit) {\n try {\n const messages = buildPrompt(section, window, delta, activity);\n const response = await chat({ model, messages, maxTokens: 600, temperature: 0.7 });\n sectionTexts[section] = sanitizeJournalSection(response.content || '');\n } catch (err) {\n logger.warn(COMPONENT, `${section} generation failed: ${(err as Error).message}`);\n sectionTexts[section] = `(generation failed: ${(err as Error).message})`;\n }\n }\n\n const markdown = renderMarkdown(window, delta, activity, emit, sectionTexts);\n const dream: DreamSnapshot = {\n date: window.dateKey,\n generatedAt: new Date().toISOString(),\n model,\n drives: { start: delta.start, end: delta.end, delta: delta.delta },\n activity: {\n trajectories: activity.trajectories,\n successfulTrajectories: activity.successfulTrajectories,\n runs: activity.runs,\n successfulRuns: activity.successfulRuns,\n toolsExercised: activity.toolsExercised,\n },\n sectionsEmitted: emit,\n sectionsSkipped: skipped,\n markdown,\n };\n\n persistDream(dream);\n broadcastDream(dream);\n return dream;\n}\n\nfunction renderMarkdown(window: DayWindow, delta: DriveDelta, activity: ActivitySummary, emit: DreamSection[], texts: Partial<Record<DreamSection, string>>): string {\n const lines: string[] = [];\n lines.push(`# Dream — ${window.dateKey}`);\n lines.push('');\n lines.push(`*${activity.successfulTrajectories}/${activity.trajectories} trajectories, ${activity.successfulRuns}/${activity.runs} runs, ${activity.toolsExercised.length} tools touched.*`);\n lines.push('');\n for (const section of emit) {\n lines.push(SECTION_HEADERS[section]);\n lines.push('');\n lines.push(texts[section] ?? '');\n lines.push('');\n }\n return lines.join('\\n');\n}\n\n// ── Storage ──────────────────────────────────────────────────────\n\nfunction persistDream(dream: DreamSnapshot): void {\n try {\n mkdirSync(DREAMS_DIR, { recursive: true });\n writeFileSync(join(DREAMS_DIR, `${dream.date}.md`), dream.markdown);\n writeFileSync(join(DREAMS_DIR, `${dream.date}.json`), JSON.stringify(dream, null, 2));\n } catch (err) {\n logger.warn(COMPONENT, `persist: ${(err as Error).message}`);\n }\n}\n\nexport function getLatestDream(): DreamSnapshot | null {\n try {\n if (!existsSync(DREAMS_DIR)) return null;\n const files = readdirSync(DREAMS_DIR).filter(f => f.endsWith('.json')).sort().reverse();\n if (files.length === 0) return null;\n return JSON.parse(readFileSync(join(DREAMS_DIR, files[0]), 'utf-8')) as DreamSnapshot;\n } catch { return null; }\n}\n\nexport function getDreamByDate(date: string): DreamSnapshot | null {\n try {\n const path = join(DREAMS_DIR, `${date}.json`);\n if (!existsSync(path)) return null;\n return JSON.parse(readFileSync(path, 'utf-8')) as DreamSnapshot;\n } catch { return null; }\n}\n\nexport function listDreamDates(limit = 30): string[] {\n try {\n if (!existsSync(DREAMS_DIR)) return [];\n return readdirSync(DREAMS_DIR)\n .filter(f => f.endsWith('.json'))\n .map(f => f.replace(/\\.json$/, ''))\n .sort()\n .reverse()\n .slice(0, limit);\n } catch { return []; }\n}\n\nfunction broadcastDream(dream: DreamSnapshot): void {\n const g = globalThis as unknown as { __titan_sse_broadcast?: (topic: string, payload: unknown) => void };\n if (typeof g.__titan_sse_broadcast === 'function') {\n try { g.__titan_sse_broadcast('dream:nightly', dream); } catch { /* ok */ }\n }\n}\n\n// ── Cron ─────────────────────────────────────────────────────────\n\nlet dreamTimer: NodeJS.Timeout | null = null;\n\nexport function startDreamCron(): void {\n const config = loadConfig();\n const dreamCfg = (config as { dream?: { enabled?: boolean; cronAt?: string } }).dream;\n if (!dreamCfg?.enabled) {\n logger.info(COMPONENT, 'Dream Mode disabled (config.dream.enabled=false) — cron not scheduled');\n return;\n }\n const cronAt = dreamCfg.cronAt ?? '03:30';\n function scheduleNext(): void {\n const [hh, mm] = cronAt.split(':').map((p) => parseInt(p, 10));\n const now = new Date();\n const target = new Date(now);\n target.setHours(hh, mm, 0, 0);\n if (target.getTime() <= now.getTime()) target.setDate(target.getDate() + 1);\n const msUntil = target.getTime() - now.getTime();\n if (dreamTimer) clearTimeout(dreamTimer);\n dreamTimer = setTimeout(async () => {\n try { await generateDream(); } catch (err) { logger.warn(COMPONENT, `cron dream: ${(err as Error).message}`); }\n scheduleNext();\n }, msUntil);\n dreamTimer.unref?.();\n logger.info(COMPONENT, `Next dream scheduled at ${target.toISOString()} (${Math.round(msUntil / 60_000)} min from now)`);\n }\n scheduleNext();\n}\n\nexport function stopDreamCron(): void {\n if (dreamTimer) {\n clearTimeout(dreamTimer);\n dreamTimer = null;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
- const TITAN_VERSION = "5.5.18";
4
+ const TITAN_VERSION = "5.5.19";
5
5
  const TITAN_CODENAME = "Spacewalk";
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 = '5.5.18';\nexport const TITAN_CODENAME = 'Spacewalk';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\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,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AAGzB,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":[]}
1
+ {"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '5.5.19';\nexport const TITAN_CODENAME = 'Spacewalk';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\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,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AAGzB,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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "titan-agent",
3
- "version": "5.5.18",
3
+ "version": "5.5.19",
4
4
  "description": "TITAN — Autonomous AI agent framework with self-improvement, multi-agent orchestration, 36 LLM providers, 16 channel adapters, GPU VRAM management, mesh networking, LiveKit voice, TITAN-Soma homeostatic drives, and a React Mission Control dashboard. Open-source, TypeScript, MIT licensed.",
5
5
  "author": "Tony Elliott (https://github.com/Djtony707)",
6
6
  "repository": {