titan-agent 5.5.11 → 5.5.13

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.11";
4
+ const TITAN_VERSION = "5.5.13";
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.11';\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.13';\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":[]}
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, appendFileSync, existsSync } from "fs";
3
+ import { execSync } from "child_process";
4
+ import { join } from "path";
5
+ import { TITAN_HOME } from "./constants.js";
6
+ import { mkdirIfNotExists } from "./helpers.js";
7
+ import { logger } from "./logger.js";
8
+ const COMPONENT = "RestartTracker";
9
+ const RESTART_HISTORY_PATH = join(TITAN_HOME, "restart-history.jsonl");
10
+ function readSystemdNRestarts() {
11
+ if (process.platform !== "linux") return null;
12
+ try {
13
+ const out = execSync("systemctl show titan.service --property=NRestarts --value", {
14
+ timeout: 1e3,
15
+ stdio: ["ignore", "pipe", "ignore"]
16
+ }).toString().trim();
17
+ const n = parseInt(out, 10);
18
+ return Number.isFinite(n) ? n : null;
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+ let bootRecorded = false;
24
+ function recordBoot(version) {
25
+ if (bootRecorded) return;
26
+ bootRecorded = true;
27
+ try {
28
+ mkdirIfNotExists(TITAN_HOME);
29
+ const record = {
30
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
31
+ pid: process.pid,
32
+ version,
33
+ nRestartsAtBoot: readSystemdNRestarts() ?? void 0
34
+ };
35
+ appendFileSync(RESTART_HISTORY_PATH, JSON.stringify(record) + "\n");
36
+ } catch (err) {
37
+ logger.warn(COMPONENT, `recordBoot failed: ${err.message}`);
38
+ }
39
+ }
40
+ function readHistory() {
41
+ if (!existsSync(RESTART_HISTORY_PATH)) return [];
42
+ try {
43
+ const raw = readFileSync(RESTART_HISTORY_PATH, "utf8");
44
+ const out = [];
45
+ for (const line of raw.split("\n")) {
46
+ const trimmed = line.trim();
47
+ if (!trimmed) continue;
48
+ try {
49
+ out.push(JSON.parse(trimmed));
50
+ } catch {
51
+ }
52
+ }
53
+ return out;
54
+ } catch {
55
+ return [];
56
+ }
57
+ }
58
+ function getRestartStats() {
59
+ const history = readHistory();
60
+ const now = Date.now();
61
+ const myPid = process.pid;
62
+ const otherBoots = history.filter((r) => r.pid !== myPid);
63
+ const last60 = otherBoots.filter((r) => now - new Date(r.timestamp).getTime() <= 60 * 60 * 1e3);
64
+ const last10 = otherBoots.filter((r) => now - new Date(r.timestamp).getTime() <= 10 * 60 * 1e3);
65
+ const nRestartsSystemd = readSystemdNRestarts();
66
+ const previousBootAt = otherBoots.length > 0 ? otherBoots[otherBoots.length - 1].timestamp : null;
67
+ let severity = "ok";
68
+ let reason = null;
69
+ if (last10.length >= 2) {
70
+ severity = "critical";
71
+ reason = `${last10.length} restarts in last 10 minutes \u2014 gateway may be in a restart loop`;
72
+ } else if (last60.length >= 5) {
73
+ severity = "warning";
74
+ reason = `${last60.length} restarts in last hour \u2014 investigate cause`;
75
+ }
76
+ return {
77
+ totalBoots: history.length,
78
+ restartsLastHour: last60.length,
79
+ restartsLast10Min: last10.length,
80
+ nRestartsSystemd,
81
+ previousBootAt,
82
+ severity,
83
+ reason
84
+ };
85
+ }
86
+ export {
87
+ RESTART_HISTORY_PATH,
88
+ getRestartStats,
89
+ recordBoot
90
+ };
91
+ //# sourceMappingURL=restartTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/restartTracker.ts"],"sourcesContent":["/**\n * TITAN — Restart Tracker\n *\n * Persists gateway start timestamps to disk so we can compute restart rate\n * across systemd-managed restarts. Without this, a 5-minute restart loop is\n * invisible from inside any single process — each gateway boot starts with\n * a fresh `process.uptime()` and no memory of the previous boot.\n *\n * Hunt Finding (2026-05-07): A 5-min restart loop ran for ~3 days before we\n * caught it. NRestarts was sitting at 700+ in systemd while every individual\n * /api/health response looked fine.\n */\nimport { readFileSync, appendFileSync, existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { join } from 'path';\nimport { TITAN_HOME } from './constants.js';\nimport { mkdirIfNotExists } from './helpers.js';\nimport { logger } from './logger.js';\n\nconst COMPONENT = 'RestartTracker';\nexport const RESTART_HISTORY_PATH = join(TITAN_HOME, 'restart-history.jsonl');\n\ninterface RestartRecord {\n timestamp: string;\n pid: number;\n version: string;\n nRestartsAtBoot?: number;\n}\n\n/** Read systemd's NRestarts for this service. Returns null when not under systemd. */\nfunction readSystemdNRestarts(): number | null {\n if (process.platform !== 'linux') return null;\n try {\n const out = execSync('systemctl show titan.service --property=NRestarts --value', {\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore'],\n }).toString().trim();\n const n = parseInt(out, 10);\n return Number.isFinite(n) ? n : null;\n } catch {\n return null;\n }\n}\n\nlet bootRecorded = false;\n\n/** Record that this gateway just booted. Idempotent within a process. */\nexport function recordBoot(version: string): void {\n if (bootRecorded) return;\n bootRecorded = true;\n try {\n mkdirIfNotExists(TITAN_HOME);\n const record: RestartRecord = {\n timestamp: new Date().toISOString(),\n pid: process.pid,\n version,\n nRestartsAtBoot: readSystemdNRestarts() ?? undefined,\n };\n appendFileSync(RESTART_HISTORY_PATH, JSON.stringify(record) + '\\n');\n } catch (err) {\n logger.warn(COMPONENT, `recordBoot failed: ${(err as Error).message}`);\n }\n}\n\nfunction readHistory(): RestartRecord[] {\n if (!existsSync(RESTART_HISTORY_PATH)) return [];\n try {\n const raw = readFileSync(RESTART_HISTORY_PATH, 'utf8');\n const out: RestartRecord[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n out.push(JSON.parse(trimmed) as RestartRecord);\n } catch { /* skip malformed line */ }\n }\n return out;\n } catch {\n return [];\n }\n}\n\nexport interface RestartStats {\n /** Total recorded boots in history file. */\n totalBoots: number;\n /** Boots in the last 60 minutes (excluding the current one). */\n restartsLastHour: number;\n /** Boots in the last 10 minutes (excluding the current one). */\n restartsLast10Min: number;\n /** systemd's NRestarts counter, when available. */\n nRestartsSystemd: number | null;\n /** ISO timestamp of the prior boot (the one before this process). */\n previousBootAt: string | null;\n /** Severity assessment: ok if rate is sane, warning/critical otherwise. */\n severity: 'ok' | 'warning' | 'critical';\n /** Reason string when severity > ok. */\n reason: string | null;\n}\n\n/**\n * Compute restart stats. The current process counts as one entry in the\n * history file; we exclude it from \"restarts last X\" so a healthy boot\n * reports 0 restarts when it just started.\n */\nexport function getRestartStats(): RestartStats {\n const history = readHistory();\n const now = Date.now();\n const myPid = process.pid;\n\n // Anything from the current process counts as \"this boot\", not a restart.\n const otherBoots = history.filter(r => r.pid !== myPid);\n const last60 = otherBoots.filter(r => now - new Date(r.timestamp).getTime() <= 60 * 60 * 1000);\n const last10 = otherBoots.filter(r => now - new Date(r.timestamp).getTime() <= 10 * 60 * 1000);\n\n const nRestartsSystemd = readSystemdNRestarts();\n const previousBootAt = otherBoots.length > 0 ? otherBoots[otherBoots.length - 1].timestamp : null;\n\n let severity: 'ok' | 'warning' | 'critical' = 'ok';\n let reason: string | null = null;\n if (last10.length >= 2) {\n severity = 'critical';\n reason = `${last10.length} restarts in last 10 minutes — gateway may be in a restart loop`;\n } else if (last60.length >= 5) {\n severity = 'warning';\n reason = `${last60.length} restarts in last hour — investigate cause`;\n }\n\n return {\n totalBoots: history.length,\n restartsLastHour: last60.length,\n restartsLast10Min: last10.length,\n nRestartsSystemd,\n previousBootAt,\n severity,\n reason,\n };\n}\n"],"mappings":";AAYA,SAAS,cAAc,gBAAgB,kBAAkB;AACzD,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAC3B,SAAS,wBAAwB;AACjC,SAAS,cAAc;AAEvB,MAAM,YAAY;AACX,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AAU5E,SAAS,uBAAsC;AAC3C,MAAI,QAAQ,aAAa,QAAS,QAAO;AACzC,MAAI;AACA,UAAM,MAAM,SAAS,6DAA6D;AAAA,MAC9E,SAAS;AAAA,MACT,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACtC,CAAC,EAAE,SAAS,EAAE,KAAK;AACnB,UAAM,IAAI,SAAS,KAAK,EAAE;AAC1B,WAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,EACpC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,IAAI,eAAe;AAGZ,SAAS,WAAW,SAAuB;AAC9C,MAAI,aAAc;AAClB,iBAAe;AACf,MAAI;AACA,qBAAiB,UAAU;AAC3B,UAAM,SAAwB;AAAA,MAC1B,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,KAAK,QAAQ;AAAA,MACb;AAAA,MACA,iBAAiB,qBAAqB,KAAK;AAAA,IAC/C;AACA,mBAAe,sBAAsB,KAAK,UAAU,MAAM,IAAI,IAAI;AAAA,EACtE,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,sBAAuB,IAAc,OAAO,EAAE;AAAA,EACzE;AACJ;AAEA,SAAS,cAA+B;AACpC,MAAI,CAAC,WAAW,oBAAoB,EAAG,QAAO,CAAC;AAC/C,MAAI;AACA,UAAM,MAAM,aAAa,sBAAsB,MAAM;AACrD,UAAM,MAAuB,CAAC;AAC9B,eAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAChC,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,QAAS;AACd,UAAI;AACA,YAAI,KAAK,KAAK,MAAM,OAAO,CAAkB;AAAA,MACjD,QAAQ;AAAA,MAA4B;AAAA,IACxC;AACA,WAAO;AAAA,EACX,QAAQ;AACJ,WAAO,CAAC;AAAA,EACZ;AACJ;AAwBO,SAAS,kBAAgC;AAC5C,QAAM,UAAU,YAAY;AAC5B,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,QAAQ,QAAQ;AAGtB,QAAM,aAAa,QAAQ,OAAO,OAAK,EAAE,QAAQ,KAAK;AACtD,QAAM,SAAS,WAAW,OAAO,OAAK,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,KAAK,KAAK,GAAI;AAC7F,QAAM,SAAS,WAAW,OAAO,OAAK,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,KAAK,KAAK,GAAI;AAE7F,QAAM,mBAAmB,qBAAqB;AAC9C,QAAM,iBAAiB,WAAW,SAAS,IAAI,WAAW,WAAW,SAAS,CAAC,EAAE,YAAY;AAE7F,MAAI,WAA0C;AAC9C,MAAI,SAAwB;AAC5B,MAAI,OAAO,UAAU,GAAG;AACpB,eAAW;AACX,aAAS,GAAG,OAAO,MAAM;AAAA,EAC7B,WAAW,OAAO,UAAU,GAAG;AAC3B,eAAW;AACX,aAAS,GAAG,OAAO,MAAM;AAAA,EAC7B;AAEA,SAAO;AAAA,IACH,YAAY,QAAQ;AAAA,IACpB,kBAAkB,OAAO;AAAA,IACzB,mBAAmB,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "titan-agent",
3
- "version": "5.5.11",
3
+ "version": "5.5.13",
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": {