titan-agent 5.5.28 → 5.5.29

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,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.28";
4
+ const TITAN_VERSION = "5.5.29";
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.28';\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.29';\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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, readdirSync, statSync } from "fs";
3
3
  import { dirname } from "path";
4
4
  function mkdirIfNotExists(dirPath) {
5
5
  if (!existsSync(dirPath)) {
@@ -28,8 +28,7 @@ function atomicWriteFileSync(filePath, data) {
28
28
  renameSync(tmpPath, filePath);
29
29
  } catch (err) {
30
30
  try {
31
- const fs = require("fs");
32
- if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
31
+ if (existsSync(tmpPath)) unlinkSync(tmpPath);
33
32
  } catch {
34
33
  }
35
34
  throw err;
@@ -39,19 +38,19 @@ function sweepAtomicTmpOrphans(dir, maxAgeMs = 60 * 60 * 1e3) {
39
38
  let removed = 0;
40
39
  let bytes = 0;
41
40
  try {
42
- const fs = require("fs");
43
- if (!fs.existsSync(dir)) return { removed, bytes };
44
- const entries = fs.readdirSync(dir);
45
- const tmpRe = /\.tmp\.\d+\.[a-z0-9]+$/;
41
+ if (!existsSync(dir)) return { removed, bytes };
42
+ const entries = readdirSync(dir);
43
+ const tmpRe = /\.tmp(?:\.\d+\.[a-z0-9]+)?$/;
46
44
  const cutoff = Date.now() - maxAgeMs;
47
45
  for (const name of entries) {
48
46
  if (!tmpRe.test(name)) continue;
49
47
  const full = `${dir}/${name}`;
50
48
  try {
51
- const st = fs.statSync(full);
49
+ const st = statSync(full);
50
+ if (!st.isFile()) continue;
52
51
  if (st.mtimeMs < cutoff) {
53
52
  bytes += st.size;
54
- fs.unlinkSync(full);
53
+ unlinkSync(full);
55
54
  removed += 1;
56
55
  }
57
56
  } catch {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/helpers.ts"],"sourcesContent":["/**\n * TITAN Helper Utilities\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'fs';\nimport { dirname, join } from 'path';\n\n/** Ensure a directory exists, creating it recursively if needed */\nexport function mkdirIfNotExists(dirPath: string): void {\n if (!existsSync(dirPath)) {\n mkdirSync(dirPath, { recursive: true });\n }\n}\n\n// Backward-compatible alias — deprecated, use mkdirIfNotExists\nexport const ensureDir = mkdirIfNotExists;\n\n/** Safely read a JSON file, returning null on failure */\nexport function readJsonFile<T = unknown>(filePath: string): T | null {\n try {\n if (!existsSync(filePath)) return null;\n const content = readFileSync(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\n/** Safely write a JSON file, creating parent dirs if needed */\nexport function writeJsonFile(filePath: string, data: unknown): void {\n ensureDir(dirname(filePath));\n writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n/** Atomic file write — writes to temp file then renames to avoid corruption on crash */\nexport function atomicWriteFileSync(filePath: string, data: string | Buffer): void {\n ensureDir(dirname(filePath));\n const tmpPath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;\n try {\n writeFileSync(tmpPath, data, 'utf-8');\n renameSync(tmpPath, filePath);\n } catch (err) {\n // v5.5.28 FIX: clean up the tmp file on failure. Previously, if the\n // rename step crashed (or the process was killed between\n // writeFileSync and renameSync), the .tmp.<ts>.<rand> file would\n // be orphaned. Found 188MB of orphans on Titan PC during the\n // 2026-05-08 audit (3 vectors.json + test-history tmp files).\n try {\n // unlink only — synchronous import to avoid circulars\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('fs') as typeof import('fs');\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch { /* best-effort cleanup */ }\n throw err;\n }\n}\n\n/**\n * Sweep stale `.tmp.<ts>.<rand>` orphans from a directory. Files older than\n * `maxAgeMs` are removed. Called at gateway boot to recover from crashes\n * that happened between writeFileSync and renameSync. v5.5.28+.\n */\nexport function sweepAtomicTmpOrphans(dir: string, maxAgeMs = 60 * 60 * 1000): { removed: number; bytes: number } {\n let removed = 0;\n let bytes = 0;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('fs') as typeof import('fs');\n if (!fs.existsSync(dir)) return { removed, bytes };\n const entries = fs.readdirSync(dir);\n const tmpRe = /\\.tmp\\.\\d+\\.[a-z0-9]+$/;\n const cutoff = Date.now() - maxAgeMs;\n for (const name of entries) {\n if (!tmpRe.test(name)) continue;\n const full = `${dir}/${name}`;\n try {\n const st = fs.statSync(full);\n if (st.mtimeMs < cutoff) {\n bytes += st.size;\n fs.unlinkSync(full);\n removed += 1;\n }\n } catch { /* skip */ }\n }\n } catch { /* nothing to do */ }\n return { removed, bytes };\n}\n\n/** Atomic JSON file write — uses atomicWriteFileSync internally */\nexport function atomicWriteJsonFile(filePath: string, data: unknown): void {\n atomicWriteFileSync(filePath, JSON.stringify(data, null, 2));\n}\n\n/** Safe string truncation */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength - 3) + '...';\n}\n\n/** Sleep for a given number of milliseconds */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Generate a short ID */\nexport function shortId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\n/** Format bytes into human-readable string */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n}\n\n/** Format a duration in ms to human-readable */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n if (ms < 3600000) return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;\n return `${Math.floor(ms / 3600000)}h ${Math.floor((ms % 3600000) / 60000)}m`;\n}\n\n/** Fetch with retry logic and exponential backoff */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n config?: { maxRetries?: number; initialDelayMs?: number; retryableStatuses?: number[]; timeoutMs?: number }\n): Promise<Response> {\n const maxRetries = config?.maxRetries ?? 3;\n const initialDelay = config?.initialDelayMs ?? 1000;\n const retryable = new Set(config?.retryableStatuses ?? [429, 500, 502, 503]);\n const timeoutMs = config?.timeoutMs ?? 120_000; // 2 minute default timeout\n\n let lastError: Error | null = null;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n // Apply timeout if caller hasn't already set an AbortSignal\n const fetchOptions = { ...options };\n if (!fetchOptions.signal) {\n fetchOptions.signal = AbortSignal.timeout(timeoutMs);\n }\n const response = await fetch(url, fetchOptions);\n if (!retryable.has(response.status) || attempt === maxRetries) {\n return response;\n }\n // Respect Retry-After header (cap at 30s)\n const retryAfter = response.headers.get('Retry-After');\n const delayMs = retryAfter\n ? Math.min(parseInt(retryAfter, 10) * 1000 || initialDelay, 30000)\n : initialDelay * Math.pow(2, attempt);\n\n // Import logger dynamically to avoid circular deps\n const { default: logger } = await import('../utils/logger.js');\n logger.warn('Retry', `${response.status} from ${url} — retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`);\n // Hunt Finding #29 (2026-04-14): consume the intermediate response\n // body before the retry delay so its socket returns to the pool.\n // Without this, every retry-eligible response leaked its socket.\n await response.body?.cancel().catch(() => {});\n await new Promise(r => setTimeout(r, delayMs));\n } catch (err) {\n lastError = err as Error;\n if (attempt === maxRetries) break;\n const delayMs = initialDelay * Math.pow(2, attempt);\n await new Promise(r => setTimeout(r, delayMs));\n }\n }\n throw lastError || new Error('Request failed after retries');\n}\n\n/** Deep merge two objects */\nexport function deepMerge<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {\n const result = { ...target };\n for (const key of Object.keys(source) as Array<keyof T>) {\n const sourceVal = source[key];\n const targetVal = result[key];\n if (\n sourceVal &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n result[key] = deepMerge(\n targetVal as Record<string, unknown>,\n sourceVal as Record<string, unknown>,\n ) as T[keyof T];\n } else if (sourceVal !== undefined) {\n result[key] = sourceVal as T[keyof T];\n }\n }\n return result;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/utils/helpers.ts"],"sourcesContent":["/**\n * TITAN Helper Utilities\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, readdirSync, statSync } from 'fs';\nimport { dirname, join } from 'path';\n\n/** Ensure a directory exists, creating it recursively if needed */\nexport function mkdirIfNotExists(dirPath: string): void {\n if (!existsSync(dirPath)) {\n mkdirSync(dirPath, { recursive: true });\n }\n}\n\n// Backward-compatible alias — deprecated, use mkdirIfNotExists\nexport const ensureDir = mkdirIfNotExists;\n\n/** Safely read a JSON file, returning null on failure */\nexport function readJsonFile<T = unknown>(filePath: string): T | null {\n try {\n if (!existsSync(filePath)) return null;\n const content = readFileSync(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\n/** Safely write a JSON file, creating parent dirs if needed */\nexport function writeJsonFile(filePath: string, data: unknown): void {\n ensureDir(dirname(filePath));\n writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n/** Atomic file write — writes to temp file then renames to avoid corruption on crash */\nexport function atomicWriteFileSync(filePath: string, data: string | Buffer): void {\n ensureDir(dirname(filePath));\n const tmpPath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;\n try {\n writeFileSync(tmpPath, data, 'utf-8');\n renameSync(tmpPath, filePath);\n } catch (err) {\n // v5.5.28 FIX: clean up the tmp file on failure. Previously, if the\n // rename step crashed (or the process was killed between\n // writeFileSync and renameSync), the .tmp.<ts>.<rand> file would\n // be orphaned. Found 188MB of orphans on Titan PC during the\n // 2026-05-08 audit (3 vectors.json + test-history tmp files).\n try { if (existsSync(tmpPath)) unlinkSync(tmpPath); } catch { /* best-effort */ }\n throw err;\n }\n}\n\n/**\n * Sweep stale `.tmp.<ts>.<rand>` and bare `.tmp` orphans from a directory.\n * Files older than `maxAgeMs` are removed. Called at gateway boot to\n * recover from crashes that happened between writeFileSync and renameSync.\n * v5.5.28+ (v5.5.29 fixed the bug where `require('fs')` silently failed\n * inside ESM modules — now uses the module-level static imports).\n */\nexport function sweepAtomicTmpOrphans(dir: string, maxAgeMs = 60 * 60 * 1000): { removed: number; bytes: number } {\n let removed = 0;\n let bytes = 0;\n try {\n if (!existsSync(dir)) return { removed, bytes };\n const entries = readdirSync(dir);\n // Match both .tmp.<ts>.<rand> (atomic-write convention) and bare\n // .tmp (older callsite convention found on Titan PC).\n const tmpRe = /\\.tmp(?:\\.\\d+\\.[a-z0-9]+)?$/;\n const cutoff = Date.now() - maxAgeMs;\n for (const name of entries) {\n if (!tmpRe.test(name)) continue;\n const full = `${dir}/${name}`;\n try {\n const st = statSync(full);\n if (!st.isFile()) continue;\n if (st.mtimeMs < cutoff) {\n bytes += st.size;\n unlinkSync(full);\n removed += 1;\n }\n } catch { /* skip */ }\n }\n } catch { /* nothing to do */ }\n return { removed, bytes };\n}\n\n/** Atomic JSON file write — uses atomicWriteFileSync internally */\nexport function atomicWriteJsonFile(filePath: string, data: unknown): void {\n atomicWriteFileSync(filePath, JSON.stringify(data, null, 2));\n}\n\n/** Safe string truncation */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength - 3) + '...';\n}\n\n/** Sleep for a given number of milliseconds */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Generate a short ID */\nexport function shortId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\n/** Format bytes into human-readable string */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n}\n\n/** Format a duration in ms to human-readable */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n if (ms < 3600000) return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;\n return `${Math.floor(ms / 3600000)}h ${Math.floor((ms % 3600000) / 60000)}m`;\n}\n\n/** Fetch with retry logic and exponential backoff */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n config?: { maxRetries?: number; initialDelayMs?: number; retryableStatuses?: number[]; timeoutMs?: number }\n): Promise<Response> {\n const maxRetries = config?.maxRetries ?? 3;\n const initialDelay = config?.initialDelayMs ?? 1000;\n const retryable = new Set(config?.retryableStatuses ?? [429, 500, 502, 503]);\n const timeoutMs = config?.timeoutMs ?? 120_000; // 2 minute default timeout\n\n let lastError: Error | null = null;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n // Apply timeout if caller hasn't already set an AbortSignal\n const fetchOptions = { ...options };\n if (!fetchOptions.signal) {\n fetchOptions.signal = AbortSignal.timeout(timeoutMs);\n }\n const response = await fetch(url, fetchOptions);\n if (!retryable.has(response.status) || attempt === maxRetries) {\n return response;\n }\n // Respect Retry-After header (cap at 30s)\n const retryAfter = response.headers.get('Retry-After');\n const delayMs = retryAfter\n ? Math.min(parseInt(retryAfter, 10) * 1000 || initialDelay, 30000)\n : initialDelay * Math.pow(2, attempt);\n\n // Import logger dynamically to avoid circular deps\n const { default: logger } = await import('../utils/logger.js');\n logger.warn('Retry', `${response.status} from ${url} — retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`);\n // Hunt Finding #29 (2026-04-14): consume the intermediate response\n // body before the retry delay so its socket returns to the pool.\n // Without this, every retry-eligible response leaked its socket.\n await response.body?.cancel().catch(() => {});\n await new Promise(r => setTimeout(r, delayMs));\n } catch (err) {\n lastError = err as Error;\n if (attempt === maxRetries) break;\n const delayMs = initialDelay * Math.pow(2, attempt);\n await new Promise(r => setTimeout(r, delayMs));\n }\n }\n throw lastError || new Error('Request failed after retries');\n}\n\n/** Deep merge two objects */\nexport function deepMerge<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {\n const result = { ...target };\n for (const key of Object.keys(source) as Array<keyof T>) {\n const sourceVal = source[key];\n const targetVal = result[key];\n if (\n sourceVal &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n result[key] = deepMerge(\n targetVal as Record<string, unknown>,\n sourceVal as Record<string, unknown>,\n ) as T[keyof T];\n } else if (sourceVal !== undefined) {\n result[key] = sourceVal as T[keyof T];\n }\n }\n return result;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "titan-agent",
3
- "version": "5.5.28",
3
+ "version": "5.5.29",
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": {