total-recall-brain 3.41.4 → 3.41.6

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.
Files changed (147) hide show
  1. package/bin/total-recall.mjs +10 -6
  2. package/docs/reference/cli-reference.md +18 -0
  3. package/frontend/dist/assets/index-4tGQfTa2.js +5605 -0
  4. package/frontend/dist/assets/index-DtwsDyFM.css +1 -0
  5. package/frontend/dist/index.html +2 -2
  6. package/frontend/package.json +11 -0
  7. package/frontend/src/App.tsx +47 -122
  8. package/frontend/src/components/plugins/plugin-frame.ts +1 -0
  9. package/frontend/src/index.css +152 -86
  10. package/frontend/src/pages/HelpPage.tsx +13 -10
  11. package/frontend/src/pages/MemoryPage.tsx +28 -30
  12. package/frontend/src/pages/NetworkPage.tsx +3 -3
  13. package/frontend/src/pages/PluginsPage.tsx +6 -5
  14. package/frontend/src/pages/RulesPage.tsx +16 -12
  15. package/package.json +4 -3
  16. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +3114 -36
  17. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +4 -1
  18. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +3114 -36
  19. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +4 -1
  20. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +3121 -33
  21. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +4 -1
  22. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +3122 -34
  23. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +4 -1
  24. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +3127 -49
  25. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -2
  26. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +1 -1
  27. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +136 -7
  28. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +135 -26
  29. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +3121 -33
  30. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +5 -2
  31. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +228 -9
  32. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +24 -30
  33. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +29 -16
  34. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +73 -0
  35. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +182 -365
  36. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -0
  37. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +40 -14
  38. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +4 -4
  39. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/agy.mjs +187 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/claude.mjs +275 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cline.mjs +132 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/codex.mjs +187 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cursor.mjs +158 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/devin.mjs +157 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/dsh.mjs +183 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/grok.mjs +132 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +173 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +87 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/openclaw.mjs +173 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/pi.mjs +129 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate.mjs +12 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/json-reply.mjs +22 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +72 -70
  54. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-catalog.mjs +195 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/paths.mjs +11 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +36 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +169 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +136 -346
  59. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +1 -1
  60. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/ssss-store.mjs +20 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/telemetry.mjs +43 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +156 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +117 -35
  64. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +51 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +398 -57
  66. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +304 -46
  67. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +367 -41
  68. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +5 -2
  69. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +33 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +2 -2
  71. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +159 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +197 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/lint.mjs +153 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +261 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +151 -4
  76. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +114 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +96 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +899 -662
  79. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/openwiki.mjs +8987 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/mermaid-diagrams/SKILL.md +44 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/write-connector/SKILL.md +56 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3097 -81
  83. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +4 -1
  84. package/src/cli/agent.mjs +12 -1
  85. package/src/cli/compact.mjs +108 -0
  86. package/src/cli/connect.mjs +56 -1
  87. package/src/cli/context.mjs +55 -23
  88. package/src/cli/doctor.mjs +5 -2
  89. package/src/cli/forget.mjs +2 -0
  90. package/src/cli/harness.mjs +62 -50
  91. package/src/cli/memory-entry.mjs +2 -2
  92. package/src/cli/preflight.mjs +152 -16
  93. package/src/cli/release.mjs +386 -0
  94. package/src/cli/remember.mjs +19 -1
  95. package/src/cli/rules.mjs +54 -12
  96. package/src/cli/startup.mjs +1 -1
  97. package/src/cli/status.mjs +12 -2
  98. package/src/core/claude-hooks.mjs +41 -3
  99. package/src/core/cli-commands.mjs +2 -0
  100. package/src/core/cli-semantic-index.mjs +3 -2
  101. package/src/core/conflict-detector.mjs +23 -12
  102. package/src/core/context-compactor.mjs +145 -0
  103. package/src/core/context-policy.mjs +20 -0
  104. package/src/core/duplicate-guard.mjs +350 -0
  105. package/src/core/meta-harness.mjs +31 -11
  106. package/src/core/preflight-gate.mjs +2 -2
  107. package/src/core/preflight-receipt.mjs +29 -1
  108. package/src/core/scaffold-plugins.mjs +7 -0
  109. package/src/core/stdio-flush.mjs +23 -0
  110. package/src/core/surface.mjs +33 -10
  111. package/src/server/invoke-durability.mjs +39 -0
  112. package/src/server/invoke-write-child.mjs +25 -0
  113. package/src/server/routes/invoke.mjs +7 -0
  114. package/src/server/routes/plugins.mjs +2 -2
  115. package/frontend/dist/assets/index-BIQmqDKP.css +0 -1
  116. package/frontend/dist/assets/index-CRzjQLu7.js +0 -5631
  117. package/frontend/src/components/DaemonLogsTab.tsx +0 -54
  118. package/frontend/src/components/ResearchAgendaTab.tsx +0 -712
  119. package/frontend/src/components/TaskQueueTab.tsx +0 -193
  120. package/frontend/src/components/VoiceInput.tsx +0 -112
  121. package/frontend/src/pages/ChatPage.tsx +0 -1059
  122. package/frontend/src/pages/CollabPage.tsx +0 -470
  123. package/frontend/src/pages/DesignDocsPage.tsx +0 -621
  124. package/frontend/src/pages/FilesPage.tsx +0 -393
  125. package/frontend/src/pages/HealthPage.tsx +0 -359
  126. package/frontend/src/pages/InboxPage.tsx +0 -83
  127. package/frontend/src/pages/InstructionsPage.tsx +0 -583
  128. package/frontend/src/pages/IntegrationsPage.tsx +0 -761
  129. package/frontend/src/pages/MeshPage.css +0 -58
  130. package/frontend/src/pages/MeshPage.tsx +0 -1219
  131. package/frontend/src/pages/OkfPage.tsx +0 -535
  132. package/frontend/src/pages/SandboxPage.tsx +0 -113
  133. package/frontend/src/pages/SkillsPage.tsx +0 -1994
  134. package/frontend/src/pages/TasksPage.tsx +0 -427
  135. package/frontend/src/pages/UsagePage.tsx +0 -702
  136. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +0 -65
  137. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +0 -76
  138. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +0 -19
  139. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -266
  140. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +0 -446
  141. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +0 -152
  142. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +0 -71
  143. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +0 -36
  144. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +0 -35
  145. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +0 -175
  146. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +0 -32
  147. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +0 -575
@@ -23,7 +23,9 @@ export const HARNESS_SPECS = {
23
23
  category: 'frontier_reasoning',
24
24
  defaultFlags: ['--output-format', 'json', '-p'],
25
25
  execType: 'flag_last',
26
- description: 'Frontier reasoning and research on Google AI Ultra plan.'
26
+ description: 'Frontier reasoning and research on Google AI Ultra plan.',
27
+ enforcement: 'enforced',
28
+ enforcementEvidence: 'PreToolUse deny hook verified live'
27
29
  },
28
30
  claude: {
29
31
  id: 'claude',
@@ -38,7 +40,9 @@ export const HARNESS_SPECS = {
38
40
  // Headless subscription auth (`claude setup-token`), read from the secret
39
41
  // store when the caller's environment does not already carry it.
40
42
  authSecrets: ['CLAUDE_CODE_OAUTH_TOKEN'],
41
- description: 'Deep codebase editing, refactoring, and Unix command execution.'
43
+ description: 'Deep codebase editing, refactoring, and Unix command execution.',
44
+ enforcement: 'enforced',
45
+ enforcementEvidence: 'PreToolUse deny hook verified live'
42
46
  },
43
47
  codex: {
44
48
  id: 'codex',
@@ -47,7 +51,9 @@ export const HARNESS_SPECS = {
47
51
  category: 'code_synthesis',
48
52
  defaultFlags: ['exec', '--sandbox', 'workspace-write', '--json', '--skip-git-repo-check'],
49
53
  execType: 'subcommand',
50
- description: 'Autonomous program synthesis and sandbox execution.'
54
+ description: 'Autonomous program synthesis and sandbox execution.',
55
+ enforcement: 'enforced',
56
+ enforcementEvidence: 'hooks.json PreToolUse block hook verified live'
51
57
  },
52
58
  ollama: {
53
59
  id: 'ollama',
@@ -56,7 +62,9 @@ export const HARNESS_SPECS = {
56
62
  category: 'local_neural',
57
63
  defaultFlags: ['run', process.env.TR_OLLAMA_MODEL || 'gemma4:latest'],
58
64
  execType: 'pipe_stdin',
59
- description: 'Local neural inference, offline reasoning, zero API cost.'
65
+ description: 'Local neural inference, offline reasoning, zero API cost.',
66
+ enforcement: 'dispatch-only',
67
+ enforcementEvidence: 'No tool hooks; preflight checked before dispatch'
60
68
  },
61
69
  grok: {
62
70
  id: 'grok',
@@ -65,7 +73,9 @@ export const HARNESS_SPECS = {
65
73
  category: 'code_engineering',
66
74
  defaultFlags: ['-p'],
67
75
  execType: 'flag_last',
68
- description: 'Autonomous coding, repo analysis, and shell execution via xAI Grok.'
76
+ description: 'Autonomous coding, repo analysis, and shell execution via xAI Grok.',
77
+ enforcement: 'enforced (unverified live)',
78
+ enforcementEvidence: 'PreToolUse hook adapter specification verified offline'
69
79
  },
70
80
  hermes: {
71
81
  id: 'hermes',
@@ -74,7 +84,9 @@ export const HARNESS_SPECS = {
74
84
  category: 'autonomous_agent',
75
85
  defaultFlags: ['chat', '-p'],
76
86
  execType: 'flag_last',
77
- description: 'Supervised messaging gateway and multi-channel autonomous agent runtime.'
87
+ description: 'Supervised messaging gateway and multi-channel autonomous agent runtime.',
88
+ enforcement: 'enforced',
89
+ enforcementEvidence: 'pre_tool_call block hook verified live'
78
90
  },
79
91
  dsh: {
80
92
  id: 'dsh',
@@ -83,7 +95,9 @@ export const HARNESS_SPECS = {
83
95
  category: 'reasoning_engine',
84
96
  defaultFlags: ['-p'],
85
97
  execType: 'flag_last',
86
- description: 'High-throughput DeepSeek reasoning and tool execution harness.'
98
+ description: 'High-throughput DeepSeek reasoning and tool execution harness.',
99
+ enforcement: 'enforced',
100
+ enforcementEvidence: 'PreToolUse deny hook verified live'
87
101
  },
88
102
  openclaw: {
89
103
  id: 'openclaw',
@@ -92,7 +106,9 @@ export const HARNESS_SPECS = {
92
106
  category: 'multichannel_bridge',
93
107
  defaultFlags: ['status', '--json'],
94
108
  execType: 'flag_last',
95
- description: 'Multi-channel messaging bridge (WhatsApp, Telegram, Discord, Slack) and agent execution.'
109
+ description: 'Multi-channel messaging bridge (WhatsApp, Telegram, Discord, Slack) and agent execution.',
110
+ enforcement: 'enforced',
111
+ enforcementEvidence: 'before_tool_call block hook verified live'
96
112
  }
97
113
  };
98
114
 
@@ -567,6 +583,7 @@ export async function queryHarnessUsage() {
567
583
  const reports = [];
568
584
 
569
585
  for (const h of harnesses) {
586
+ const spec = HARNESS_SPECS[h.id];
570
587
  if (!h.available) {
571
588
  reports.push({
572
589
  id: h.id,
@@ -582,7 +599,9 @@ export async function queryHarnessUsage() {
582
599
  plan: '—',
583
600
  rolling5hRemainingPct: '—',
584
601
  weeklyRemainingPct: '—',
585
- quotaRemaining: 'not installed'
602
+ quotaRemaining: 'not installed',
603
+ enforcement: spec?.enforcement || 'advisory',
604
+ enforcementEvidence: spec?.enforcementEvidence || null
586
605
  });
587
606
  continue;
588
607
  }
@@ -610,7 +629,6 @@ export async function queryHarnessUsage() {
610
629
  const acc = JSON.parse(fs.readFileSync(accPath, 'utf8'));
611
630
  if (acc.active) identity = acc.active;
612
631
  }
613
- plan = 'Google AI Ultra / Pro';
614
632
  } catch {}
615
633
  } else if (h.id === 'claude') {
616
634
  try {
@@ -722,7 +740,9 @@ export async function queryHarnessUsage() {
722
740
  plan,
723
741
  rolling5hRemainingPct,
724
742
  weeklyRemainingPct,
725
- quotaRemaining
743
+ quotaRemaining,
744
+ enforcement: spec?.enforcement || 'advisory',
745
+ enforcementEvidence: spec?.enforcementEvidence || null
726
746
  });
727
747
  }
728
748
 
@@ -6,7 +6,7 @@
6
6
  * Rules, in order:
7
7
  * 1. Tools that only display or discover (`meta`, `ui`) are always open.
8
8
  * 2. Reads of skill files are open: reading them is how the preflight is earned.
9
- * 3. A single plain Total Recall preflight, context, recall or help command is
9
+ * 3. A single plain Total Recall preflight, context, compact, recall, rules or help command is
10
10
  * open, so the gate can always be satisfied or repaired. Chaining, piping,
11
11
  * substitution and redirects are not plain, so the exemption cannot carry
12
12
  * other work and a capsule cannot be truncated through a pipe.
@@ -21,7 +21,7 @@ export const TOOL_CLASSES = ['shell', 'read', 'write', 'search', 'web', 'mcp', '
21
21
  export const SWEEP_CONSULT_MS = 15 * 60 * 1000;
22
22
 
23
23
  const SKILL_PATH = /\/\.(agent|claude)\/skills\//;
24
- const SATISFYING = /^(cd\s+\S+\s*&&\s*)?(npx\s+)?(total-recall|node\s+\S*bin\/total-recall\.mjs)\s+(tr-preflight|preflight|context|recall|help|--help)(\s+[^;&|`<>$\n]*)?$/;
24
+ const SATISFYING = /^(cd\s+\S+\s*&&\s*)?(npx\s+)?(total-recall|node\s+\S*bin\/total-recall\.mjs)\s+(tr-preflight|preflight|context|compact|recall|rules|help|--help)(\s+[^;&|`<>$\n]*)?$/;
25
25
 
26
26
  /** True when the whole command is one plain Total Recall invocation that satisfies or repairs the gate. */
27
27
  export function isSatisfyingCommand(command) {
@@ -98,7 +98,7 @@ export function acknowledge({ root, task = '', actions = 'read,edit,test', maxAg
98
98
  const context = run(['context', task || '(no task given)', '--action', actions], root);
99
99
  if (!/ready:true/.test(context.out)) {
100
100
  return { ok: false, problems: ['total-recall context did not return ready:true',
101
- context.code === 2 ? 'exit 2: capsule overflow; raise --budget or curate applicability, then retry' : `context exit=${context.code ?? '?'}`] };
101
+ context.code === 2 ? 'exit 2: capsule overflow; the budget cannot hold every rule even compacted, so retry with the required_budget it reports' : `context exit=${context.code ?? '?'}`] };
102
102
  }
103
103
  const version = (context.out.match(/version:([0-9a-f]+)/) || [])[1] || null;
104
104
  const rules = run(['rules', 'verify'], root);
@@ -121,3 +121,31 @@ export function touchConsult(root, { now = Date.now(), home = os.homedir() } = {
121
121
  export function lastConsultMs(root, { home = os.homedir() } = {}) {
122
122
  try { return Number(fs.readFileSync(consultPath(root, home), 'utf8')) || 0; } catch { return 0; }
123
123
  }
124
+
125
+ export const decisionsLogPath = (home = os.homedir()) => path.join(stateDir(home), 'decisions.jsonl');
126
+
127
+ /** Append a real gate decision to the persistent decisions log (ENFORCE-R5). */
128
+ export function recordDecision(entry, { home = os.homedir(), now = Date.now() } = {}) {
129
+ try {
130
+ const file = decisionsLogPath(home);
131
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
132
+ const record = { at: new Date(now).toISOString(), ...entry };
133
+ fs.appendFileSync(file, `${JSON.stringify(record)}\n`, { mode: 0o600 });
134
+ return record;
135
+ } catch { return null; }
136
+ }
137
+
138
+ /** Read recent gate decisions for compliance reporting. */
139
+ export function readDecisions({ home = os.homedir(), limit = 1000 } = {}) {
140
+ try {
141
+ const file = decisionsLogPath(home);
142
+ if (!fs.existsSync(file)) return [];
143
+ const lines = fs.readFileSync(file, 'utf8').trim().split('\n').filter(Boolean);
144
+ const start = Math.max(0, lines.length - limit);
145
+ const records = [];
146
+ for (let i = start; i < lines.length; i++) {
147
+ try { records.push(JSON.parse(lines[i])); } catch {}
148
+ }
149
+ return records;
150
+ } catch { return []; }
151
+ }
@@ -84,6 +84,13 @@ export function syncScaffoldPlugins({ root, ids, write = false, run = spawnSync
84
84
  };
85
85
  walk(target);
86
86
  }
87
+ // The release gate reads the pin, so a stale pin is drift even when every copied file matches its source.
88
+ const lockFile = path.join(root, 'scripts', 'scaffold-plugins.json');
89
+ if (!write && fs.existsSync(lockFile)) {
90
+ const pinned = JSON.parse(fs.readFileSync(lockFile, 'utf8'));
91
+ const stale = [...new Set([...Object.keys(pinned), ...Object.keys(lock)])].some(key => pinned[key] !== lock[key]);
92
+ if (stale) changes.push({ file: 'scripts/scaffold-plugins.json', change: 'stale pin' });
93
+ }
87
94
  if (write) fs.writeFileSync(path.join(root, 'scripts', 'scaffold-plugins.json'), `${JSON.stringify(Object.fromEntries(Object.entries(lock).sort()), null, 2)}\n`);
88
95
  return { changes, files: Object.keys(lock).length };
89
96
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Exit once everything already written to stdout and stderr has left the process.
3
+ *
4
+ * process.exit() drops whatever is still queued. On macOS a pipe write is
5
+ * asynchronous and the pipe takes 64 KiB at once, so `write(big); process.exit()`
6
+ * loses the rest with exit status 0, while a redirect to a file looks fine.
7
+ *
8
+ * The returned promise never settles, so `await exitAfterFlush(code)` stops the
9
+ * caller the way process.exit() does. A reader that has gone away (`| head`) is
10
+ * not an error here: the unwritten output has nowhere to go.
11
+ */
12
+ function drained(stream) {
13
+ return new Promise((resolve) => {
14
+ stream.on('error', resolve);
15
+ if (stream.destroyed || !stream.writable || stream.writableLength === 0) return resolve();
16
+ stream.write('', resolve);
17
+ });
18
+ }
19
+
20
+ export function exitAfterFlush(code = process.exitCode ?? 0) {
21
+ Promise.all([drained(process.stdout), drained(process.stderr)]).then(() => process.exit(code));
22
+ return new Promise(() => {});
23
+ }
@@ -390,17 +390,18 @@ export function buildInstalledPluginsSummary(projectRoot = process.cwd()) {
390
390
  const plugins = discoverPlugins(projectRoot).filter((p) => p.valid);
391
391
  if (!plugins.length) return '';
392
392
  const lines = [
393
- '## Installed Plugins & Capabilities\n',
394
- 'Use Total Recall and plugins first. Discover operations with `total-recall plugin library --json`, inspect the owner CLI help, then use its CLI.\n',
393
+ '## Mandatory Plugin Enforcement Table (Installed Plugins)\n',
394
+ 'All tasks within these domains MUST use their owning Total Recall plugin CLI (`total-recall <namespace>`). Hand-rolling curl, ad-hoc scripts, raw bash, direct ssh, or manual file edits to bypass these plugins is a strict protocol violation. Discover operations with `total-recall plugin library --json` or run `total-recall <namespace> --help`.\n',
395
395
  'Namespaces below run as `total-recall <namespace> [command]`; declarations do not prove provider readiness.\n'
396
396
  ];
397
- if (plugins.some(plugin => plugin.id === 'composable-cli')) lines.push('Composed commands: `total-recall composable-cli library --json`.\n');
397
+ if (plugins.some(plugin => plugin.id === 'composable-cli')) {
398
+ lines.push('Composed commands: run `total-recall composable-cli library --json` or `total-recall <command> --help`.\n');
399
+ }
398
400
  for (const p of plugins) {
399
401
  const cmd = p.manifest.cli?.command || p.id;
400
- const desc = (p.manifest.description || 'Plugin capability').replace(/\s+/g, ' ').slice(0, 30).trimEnd();
401
- const ops = (p.manifest.cli?.subcommands || []).map(sub => sub.name).filter(Boolean);
402
- const shown = ops.join(' | ');
403
- lines.push(`- **${p.id}** v${p.manifest.version || 'unknown'} · ${pluginActivation(p, { projectRoot }).state} · \`total-recall ${cmd}\` ${shown ? `[${shown}]` : '(no operations)'}: ${desc}`);
402
+ const desc = (p.manifest.description || 'Plugin capability').replace(/\s+/g, ' ').trim();
403
+ const state = pluginActivation(p, { projectRoot }).state;
404
+ lines.push(`- **${p.id}** v${p.manifest.version || 'unknown'} · ${state} · \`total-recall ${cmd}\`: ${desc}`);
404
405
  }
405
406
  return lines.join('\n') + '\n';
406
407
  } catch {
@@ -418,6 +419,19 @@ export async function buildRulesBlock(skillsDir, nodes = [], {
418
419
  const contributions = [];
419
420
  const counts = category => rules.filter(n => n.category === category).length;
420
421
  const commands = buildCommandsSection(listSurfaceCommands(commandDirsFor(skillsDir)));
422
+
423
+ // Tier 1 Absolute Invariants & Top Operational Directives lead lines 1-50 so they are never truncated
424
+ const tier1Rules = rules.filter(r => r.priority === 'absolute' || r.immutable === true || r.slug === 'invariants-8cd098fd' || r.slug === 'invariants-dc116371');
425
+ const otherRules = rules.filter(r => !tier1Rules.includes(r));
426
+
427
+ for (const node of tier1Rules) {
428
+ contributions.push({
429
+ id: `${node._layer || 'project'}:${node.slug}`,
430
+ required: true,
431
+ text: node._directive ? `[${node.slug}] ${node._directive}` : `### ${node.title || node.slug} [${node.slug}]\n\n${node.body || node.content || ''}`
432
+ });
433
+ }
434
+
421
435
  const header = bootstrap ? `${CLI_QUICK_REFERENCE}\n${buildInstalledPluginsSummary(root)}\n${commands}\n# 🔴 MANDATORY FIRST STEP: TOTAL RECALL CLI & TASK ROUTING
422
436
 
423
437
  1. RUN TOTAL RECALL CLI HELP FIRST:
@@ -427,7 +441,7 @@ Inspect all core capabilities, installed plugins, and commands.
427
441
 
428
442
  2. RUN TOTAL RECALL CONTEXT BEFORE ACTING:
429
443
  \`total-recall context "task description" --action <actions>\`
430
- Read the complete returned capsule and require ready:true. Unknown rule applicability is conservatively required. If it exits 2, stop: the required set exceeds its budget; inspect overflow and explicitly raise the budget or curate applicability before action.
444
+ Read the returned capsule and require ready:true. Rules of unknown applicability are required. Over budget it compacts; every listed rule still applies: expand one with \`total-recall compact "task" --expand <slug>\` before relying on it. If it exits 2, retry with the required_budget it reports.
431
445
 
432
446
  Total Recall is the agent's working memory, toolset, and single source of truth across all decisions. Refresh at every changed task, action or project, and after a memory/skill edit. Do not infer applicability from semantic similarity. CLI retrieval is local and works without the server. Skills have compact entrypoints; load only relevant references. Canonical vault writes use the validated CLI.
433
447
 
@@ -435,13 +449,22 @@ DO NOT SKIP THIS. Run Total Recall CLI help and context first before doing anyth
435
449
 
436
450
  ` :
437
451
  `## Active Rules: ${counts('invariants')} invariants, ${counts('preferences')} preferences, ${counts('anti-patterns')} corrections`;
452
+
438
453
  contributions.push({ id: 'routing', text: header, required: true });
454
+
439
455
  const pluginsSummary = buildInstalledPluginsSummary(root);
440
456
  if (pluginsSummary && !bootstrap) {
441
457
  contributions.push({ id: 'installed-plugins', text: pluginsSummary, required: true });
442
458
  }
443
- for (const node of rules) contributions.push({ id: `${node._layer || 'project'}:${node.slug}`, required: true,
444
- text: node._directive ? `[${node.slug}] ${node._directive}` : `### ${node.title || node.slug} [${node.slug}]\n\n${node.body || node.content || ''}` });
459
+
460
+ for (const node of otherRules) {
461
+ contributions.push({
462
+ id: `${node._layer || 'project'}:${node.slug}`,
463
+ required: true,
464
+ text: node._directive ? `[${node.slug}] ${node._directive}` : `### ${node.title || node.slug} [${node.slug}]\n\n${node.body || node.content || ''}`
465
+ });
466
+ }
467
+
445
468
  if (!bootstrap) contributions.push({ id: 'commands', text: commands });
446
469
  if (!bootstrap) {
447
470
  if (skillsDir) {
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The restricted write child runs under Node's permission model, which disables
3
+ * fsync. Durability is therefore the unconfined parent's job: before a write is
4
+ * reported as successful, it flushes every file the child changed and the
5
+ * directories that hold them.
6
+ */
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+
10
+ // File timestamps can round down, so a change made at the start of the run still counts.
11
+ const MTIME_SLACK_MS = 2000;
12
+
13
+ function flush(target) {
14
+ const fd = fs.openSync(target, 'r');
15
+ try { fs.fsyncSync(fd); } finally { fs.closeSync(fd); }
16
+ }
17
+
18
+ /** Flush files under `roots` modified since `sinceMs`, then their directories, and return how many files were flushed. */
19
+ export function syncChangedFiles(roots, sinceMs) {
20
+ let files = 0;
21
+ const walk = dir => {
22
+ let changed = false;
23
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
24
+ if (entry.isSymbolicLink()) continue;
25
+ const target = path.join(dir, entry.name);
26
+ if (entry.isDirectory()) {
27
+ if (walk(target)) changed = true;
28
+ } else if (entry.isFile() && fs.statSync(target).mtimeMs >= sinceMs - MTIME_SLACK_MS) {
29
+ flush(target);
30
+ files++;
31
+ changed = true;
32
+ }
33
+ }
34
+ if (changed) flush(dir);
35
+ return changed;
36
+ };
37
+ for (const root of roots) if (fs.existsSync(root)) walk(root);
38
+ return files;
39
+ }
@@ -1,4 +1,29 @@
1
1
  /** Structured trusted mutation entrypoint. Never accepts executable code. */
2
+ import fs from 'node:fs';
3
+
4
+ // Node.js Permission Model disables fsync/fsyncSync APIs, so this child cannot
5
+ // flush its own writes. The unconfined parent flushes them before reporting
6
+ // success (invoke-durability.mjs); without that step these writes would not survive a crash.
7
+ if (fs.fsyncSync) {
8
+ const origFsyncSync = fs.fsyncSync;
9
+ fs.fsyncSync = (fd) => {
10
+ try { return origFsyncSync(fd); }
11
+ catch (err) {
12
+ if (err.message && err.message.includes('fsync API is disabled when Permission Model is enabled')) return;
13
+ throw err;
14
+ }
15
+ };
16
+ }
17
+ if (fs.fsync) {
18
+ const origFsync = fs.fsync;
19
+ fs.fsync = (fd, cb) => {
20
+ origFsync(fd, (err) => {
21
+ if (err && err.message && err.message.includes('fsync API is disabled when Permission Model is enabled')) return cb?.(null);
22
+ return cb?.(err);
23
+ });
24
+ };
25
+ }
26
+
2
27
  import { createConfinedFact } from '../core/confined-memory.mjs';
3
28
 
4
29
  try {
@@ -15,6 +15,7 @@ import { composedCommandDirectories } from '../../core/composed-command-resoluti
15
15
  import { invokeAuthority, authorizeInvoke, operationAllowed } from '../invoke-policy.mjs';
16
16
  import { restrictedBrainTargets } from '../invoke-targets.mjs';
17
17
  import { INVOKE_LIMITS, InvokeError, executeInvokeChild, requireSuccessfulRun, invokeEnvironment, absoluteTarget } from '../invoke-execution.mjs';
18
+ import { syncChangedFiles } from '../invoke-durability.mjs';
18
19
 
19
20
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
20
21
  // Share across router instances in this server; do not race projection refreshes.
@@ -214,6 +215,7 @@ const HANDLERS = {
214
215
  const { category, title, content, tags, scope, timeoutMs } = params;
215
216
  if (context.authority.mode === 'restricted') {
216
217
  const brain = context.selectedBrain;
218
+ const startedAt = Date.now();
217
219
  const run = await context.run({ entry: path.join(root, 'src/server/invoke-write-child.mjs'),
218
220
  cwd: root, env: invokeEnvironment(false), timeoutMs, nodeArgs: ['--max-old-space-size=128', '--permission',
219
221
  `--allow-fs-read=${path.join(root, 'src')}`, `--allow-fs-read=${path.join(root, 'node_modules')}`,
@@ -231,6 +233,11 @@ const HANDLERS = {
231
233
  result || { committed: 'unknown', indexed: false });
232
234
  }
233
235
  if (!result?.committed || !result?.indexed) throw new InvokeError(502, 'invalid-owner-output', 'Mutation returned invalid output.', { committed: 'unknown', indexed: false });
236
+ try {
237
+ syncChangedFiles([path.join(brain, 'memory-vault'), path.join(brain, 'memory-derived')], startedAt);
238
+ } catch {
239
+ throw new InvokeError(502, 'durability-unconfirmed', 'Fact was written but could not be flushed to disk; check it before retrying.', result);
240
+ }
234
241
  return result;
235
242
  }
236
243
  if (!content || typeof content !== 'string') throw new Error('ssss.write needs content.');
@@ -35,7 +35,7 @@ const projectRoot = () => process.cwd();
35
35
  router.get('/api/plugins/execution-targets', requireAuth, requireScope('config:read'), async (req, res) => {
36
36
  try {
37
37
  const { loadPluginModule } = await import('../../core/plugin-runtime.mjs');
38
- const owner = await loadPluginModule('cli-agents', 'command-executor', { activeRequired: true });
38
+ const owner = await loadPluginModule('meta-harness', 'command-executor', { activeRequired: true });
39
39
  const result = req.query.node ? await owner.inspectNode(String(req.query.node)) : await owner.listTargets();
40
40
  res.json({ success: true, ...result });
41
41
  } catch (err) { badRequest(res, err.message); }
@@ -338,7 +338,7 @@ router.post("/api/plugins/:id/run", requireAuth, (req, res, next) => {
338
338
  if (stdin !== undefined) return badRequest(res, 'Credential operations require direct local execution');
339
339
  if (!pluginActivation(plugin, { projectRoot: projectRoot() }).enabled && !['on', 'off', 'status', 'health', 'jobs', '--help', 'help'].includes(subcommand)) return badRequest(res, `Plugin ${plugin.id} is inactive`);
340
340
  const { loadPluginModule } = await import('../../core/plugin-runtime.mjs');
341
- const owner = await loadPluginModule('cli-agents', 'command-executor', { activeRequired: true });
341
+ const owner = await loadPluginModule('meta-harness', 'command-executor', { activeRequired: true });
342
342
  const result = await owner.executeCommand({ plugin, subcommand, args, execution, projectRoot: projectRoot() });
343
343
  return res.json({ success: result.ok, pluginId: plugin.id, ...result });
344
344
  }
@@ -1 +0,0 @@
1
- :root{--bg-primary:#070b14;--bg-secondary:#0c1220;--bg-tertiary:#121a2b;--bg-elevated:#172033;--bg-hover:#1c2740;--bg-active:#243352;--accent:#3b82f6;--accent-deep:#1e3a8a;--accent-glow:#3b82f647;--accent-hover:#60a5fa;--accent-muted:#3b82f624;--accent-soft:#93c5fd1f;--purple-primary:var(--accent);--purple-border:var(--border-accent);--success:#34d399;--success-muted:#34d3991f;--warning:#fbbf24;--warning-muted:#fbbf241f;--error:#f87171;--error-muted:#f871711f;--text-primary:#f1f5f9;--text-secondary:#94a3b8;--text-tertiary:#64748b;--text-inverse:#070b14;--border:#94a3b81a;--border-hover:#94a3b838;--border-accent:#3b82f659;--glass:#0c1220b8;--glass-border:#94a3b81f;--mesh-1:#1e3a8a59;--mesh-2:#3b82f61f;--mesh-3:#0f172ae6;--font-sans:"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:"JetBrains Mono", "SF Mono", ui-monospace, monospace;--sidebar-width:252px;--radius-sm:8px;--radius-md:12px;--radius-lg:18px;--radius-xl:24px;--shadow-sm:0 1px 2px #00000059;--shadow-md:0 8px 24px #00000059;--shadow-lg:0 24px 64px #00000073;--shadow-glow:0 0 40px #3b82f62e;--transition-fast:.14s cubic-bezier(.23, 1, .32, 1);--transition-normal:.22s cubic-bezier(.23, 1, .32, 1)}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{background:var(--bg-primary);height:100%;color:var(--text-primary);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizelegibility;font-size:14px;line-height:1.6}body{background:radial-gradient(1200px 600px at 10% -10%, var(--mesh-1), transparent 55%), radial-gradient(900px 500px at 100% 0%, var(--mesh-2), transparent 50%), radial-gradient(800px 400px at 50% 100%, #1e3a8a33, transparent 60%), var(--bg-primary)}a{color:var(--accent);text-decoration:none}a:hover{color:var(--accent-hover)}button{font-family:var(--font-sans);cursor:pointer;border:none;outline:none}code,pre{font-family:var(--font-mono)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--bg-hover);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text-tertiary)}.app-layout{height:100vh;display:flex;position:relative;overflow:hidden}.sidebar{width:var(--sidebar-width);min-width:var(--sidebar-width);background:var(--glass);-webkit-backdrop-filter:blur(20px)saturate(1.4);border-right:1px solid var(--glass-border);transition:transform var(--transition-normal);z-index:100;flex-direction:column;padding:14px 0 12px;display:flex;box-shadow:4px 0 40px #00000040}.sidebar-brand{border-bottom:1px solid var(--border);flex-direction:column;gap:10px;margin-bottom:10px;padding:4px 16px 18px;display:flex}.sidebar-brand-row{align-items:center;gap:12px;display:flex}.sidebar-brand h1{color:var(--text-primary);letter-spacing:-.03em;font-size:15px;font-weight:700;line-height:1.2}.sidebar-brand span{color:var(--text-tertiary);letter-spacing:.02em;font-size:11px;font-weight:500}.sidebar-brand-lockup{background:linear-gradient(#f8fafc 0%,#e8eef9 100%);border-radius:12px;width:100%;padding:10px 12px;overflow:hidden;box-shadow:inset 0 1px #ffffffb3,0 8px 20px #0f172a59,0 0 0 1px #94a3b833}.sidebar-brand-lockup img{object-fit:contain;width:100%;height:auto;display:block}.sidebar-nav{scrollbar-width:thin;flex-direction:column;flex:1;gap:2px;padding:0 10px;display:flex;overflow-y:auto}.nav-section-label{text-transform:uppercase;letter-spacing:.08em;color:var(--text-tertiary);opacity:.85;padding:12px 12px 6px;font-size:10px;font-weight:600}.nav-link{color:var(--text-secondary);transition:all var(--transition-fast);border:1px solid #0000;border-radius:10px;align-items:center;gap:10px;padding:9px 12px;font-size:13px;font-weight:500;text-decoration:none;display:flex;position:relative}.nav-link:hover{color:var(--text-primary);background:#94a3b814}.nav-link.active{color:#dbeafe;border-color:var(--border-accent);background:linear-gradient(135deg,#3b82f62e,#1e3a8a38);box-shadow:0 0 0 1px #3b82f614,inset 0 1px #ffffff0a}.nav-link.active:before{content:"";background:linear-gradient(#60a5fa,#2563eb);border-radius:0 3px 3px 0;width:3px;position:absolute;top:20%;bottom:20%;left:0;box-shadow:0 0 12px #3b82f699}.nav-link svg{opacity:.72;flex-shrink:0;width:18px;height:18px}.nav-link.active svg{opacity:1;color:#93c5fd}.nav-link:hover svg{opacity:.95}.sidebar-footer{border-top:1px solid var(--border);background:linear-gradient(#0000,#0f172a66);margin-top:8px;padding:12px 14px}.main-content{background:0 0;flex-direction:column;flex:1;min-width:0;display:flex;overflow:hidden}.page{flex:1;padding:28px 32px 40px;overflow-y:auto}.page-header{margin-bottom:28px}.page-header h1{letter-spacing:-.03em;background:linear-gradient(135deg,#f8fafc 0%,#93c5fd 100%);-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text;font-size:24px;font-weight:700}.page-header p{color:var(--text-secondary);max-width:56ch;margin-top:6px;font-size:13.5px}.card{border:1px solid var(--border);border-radius:var(--radius-md);transition:border-color var(--transition-normal), box-shadow var(--transition-normal), transform var(--transition-normal);box-shadow:var(--shadow-sm);background:linear-gradient(165deg,#172033e6,#0c1220f2);padding:20px}.card:hover{border-color:var(--border-hover);box-shadow:var(--shadow-md)}.card-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;display:grid}.btn{transition:all var(--transition-fast);letter-spacing:-.01em;border-radius:10px;align-items:center;gap:6px;padding:9px 16px;font-size:13px;font-weight:600;display:inline-flex}.btn-primary{color:#fff;background:linear-gradient(135deg,#3b82f6 0%,#2563eb 55%,#1d4ed8 100%);box-shadow:inset 0 1px #ffffff26,0 8px 20px #2563eb47}.btn-primary:hover{box-shadow:0 0 28px var(--accent-glow), 0 8px 24px #2563eb59;background:linear-gradient(135deg,#60a5fa 0%,#3b82f6 50%,#2563eb 100%);transform:translateY(-1px)}.btn-ghost{color:var(--text-secondary);border:1px solid var(--border);background:#94a3b80f}.btn-ghost:hover{color:var(--text-primary);border-color:var(--border-hover);background:#94a3b81f}.btn-sm{padding:6px 11px;font-size:12px}.alert{border-radius:var(--radius-sm);border:1px solid #0000;padding:12px 16px;font-size:13px;font-weight:500}.alert-error{background:var(--error-muted);color:var(--error);border-color:#f8717140}.alert-success{background:var(--success-muted);color:var(--success);border-color:#34d39940}.alert-warning{background:var(--warning-muted);color:var(--warning);border-color:#fbbf2440}.input,.select{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text-primary);font-family:var(--font-sans);transition:border-color var(--transition-fast), box-shadow var(--transition-fast);outline:none;padding:9px 12px;font-size:13px}.input:focus,.select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-muted)}.input::placeholder{color:var(--text-tertiary)}.select{cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");background-position:right 10px center;background-repeat:no-repeat;padding-right:32px}.data-table{border-collapse:collapse;width:100%}.data-table th{text-align:left;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.04em;border-bottom:1px solid var(--border);background:var(--bg-secondary);padding:12px 16px;font-size:12px;font-weight:500}.data-table td{border-bottom:1px solid var(--border);color:var(--text-primary);padding:14px 16px;font-size:13px}.data-table tbody tr{transition:background var(--transition-fast)}.data-table tbody tr:hover{background:var(--bg-hover)}.data-table tr.is-leader td{background:var(--accent-soft)}.page-container{max-width:1200px;margin:0 auto;padding:32px}.page-header{justify-content:space-between;align-items:center;margin-bottom:32px;display:flex}.page-header h1{color:var(--text-primary);font-size:24px;font-weight:600}.badge{letter-spacing:.03em;border-radius:4px;align-items:center;padding:3px 8px;font-size:11px;font-weight:600;display:inline-flex}.badge-online,.badge-active{background:var(--success-muted);color:var(--success)}.badge-offline,.badge-inactive{background:var(--error-muted);color:var(--error)}.badge-leader{color:var(--accent-hover);background:#3b82f626}.badge-follower{background:var(--bg-hover);color:var(--text-secondary)}.surface-premium{border:1px solid var(--glass-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-md), inset 0 1px 0 #ffffff0a;background:linear-gradient(165deg,#172033eb,#0c1220f5)}.login-shell{justify-content:center;align-items:center;min-height:100vh;padding:24px;display:flex;position:relative;overflow:hidden}.login-shell:before{content:"";pointer-events:none;background:radial-gradient(50% 40% at 20% 20%,#2563eb47,#0000 55%),radial-gradient(45% 35% at 80% 70%,#1e3a8a59,#0000 50%),radial-gradient(30% 25%,#60a5fa14,#0000 60%);animation:18s ease-in-out infinite alternate meshDrift;position:absolute;inset:-20%}@keyframes meshDrift{0%{transform:translate(0,0)scale(1)}to{transform:translate(-2%,2%)scale(1.05)}}.login-card{z-index:1;border-radius:var(--radius-xl);-webkit-backdrop-filter:blur(24px);backdrop-filter:blur(24px);background:linear-gradient(165deg,#121a2beb,#080c16f5);border:1px solid #94a3b824;flex-direction:column;gap:28px;width:min(400px,100%);padding:40px 36px;display:flex;position:relative;box-shadow:0 0 0 1px #3b82f614,0 32px 80px #0000008c,0 0 80px #2563eb1f}.onboarding-shell{max-width:680px;margin:0 auto;padding:36px 22px 72px}.onboarding-progress{background:#94a3b81a;border-radius:99px;height:6px;overflow:hidden;box-shadow:inset 0 1px 2px #0000004d}.onboarding-progress>div{background:linear-gradient(90deg,#1e40af,#3b82f6,#93c5fd);border-radius:99px;height:100%;transition:width .3s cubic-bezier(.23,1,.32,1);box-shadow:0 0 16px #3b82f680}.loop-card{border:1px solid var(--border-accent);border-radius:var(--radius-md);font-family:var(--font-mono);color:#cbd5e1;background:linear-gradient(145deg,#1e3a8a40,#0c1220cc);padding:18px 20px;font-size:12.5px;line-height:1.85;box-shadow:inset 0 1px #ffffff0a}.badge{text-transform:uppercase;letter-spacing:.04em;border-radius:99px;align-items:center;gap:4px;padding:3px 8px;font-size:11px;font-weight:600;display:inline-flex}.badge-success{background:var(--success-muted);color:var(--success)}.badge-warning{background:var(--warning-muted);color:var(--warning)}.badge-error{background:var(--error-muted);color:var(--error)}.badge-accent{background:var(--accent-muted);color:var(--accent)}.chat-container{flex-direction:column;flex:1;height:calc(100vh - 1px);display:flex;overflow:hidden}.chat-messages{flex-direction:column;flex:1;gap:16px;padding:24px 32px;display:flex;overflow-y:auto}.chat-empty{color:var(--text-tertiary);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:12px;display:flex}.chat-empty svg{opacity:.3;width:48px;height:48px}.chat-empty h2{color:var(--text-secondary);font-size:18px;font-weight:600}.chat-empty p{font-size:13px}.message{border-radius:var(--radius-md);white-space:pre-wrap;word-break:break-word;max-width:720px;padding:14px 18px;font-size:14px;line-height:1.7;animation:.2s msgIn}@keyframes msgIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.message-user{color:#fff;background:linear-gradient(135deg,#3b82f6 0%,#2563eb 100%);border-bottom-right-radius:4px;align-self:flex-end;box-shadow:0 8px 24px #2563eb38}.message-assistant{border:1px solid var(--border);box-shadow:var(--shadow-sm);background:linear-gradient(165deg,#172033f2,#0c1220fa);border-bottom-left-radius:4px;align-self:flex-start}.message-assistant code{background:var(--bg-primary);border-radius:4px;padding:1px 5px;font-size:12.5px}.chat-input-bar{border-top:1px solid var(--border);background:var(--bg-secondary);padding:16px 32px 20px}.chat-input-wrapper{align-items:flex-end;gap:8px;max-width:800px;margin:0 auto;display:flex}.chat-input-wrapper textarea{resize:none;background:var(--bg-tertiary);border:1px solid var(--border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-sans);min-height:44px;max-height:160px;transition:border-color var(--transition-fast);flex:1;padding:12px 16px;font-size:14px;line-height:1.5}.chat-input-wrapper textarea:focus{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-glow);outline:none}.chat-input-wrapper textarea::placeholder{color:var(--text-tertiary)}.chat-send-btn{border-radius:var(--radius-md);color:#fff;width:44px;height:44px;transition:all var(--transition-fast);background:linear-gradient(135deg,#3b82f6,#1d4ed8);flex-shrink:0;justify-content:center;align-items:center;display:flex;box-shadow:0 4px 14px #2563eb59}.chat-send-btn:hover{box-shadow:0 0 24px var(--accent-glow);background:linear-gradient(135deg,#60a5fa,#2563eb);transform:translateY(-1px)}.chat-send-btn:disabled{opacity:.4;cursor:not-allowed;box-shadow:none}.chat-send-btn svg{width:18px;height:18px}.memory-layout{grid-template-columns:220px 1fr;gap:20px;height:calc(100vh - 120px);display:grid}.memory-sidebar{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);padding:12px;overflow-y:auto}.memory-sidebar h3{text-transform:uppercase;letter-spacing:.06em;color:var(--text-tertiary);padding:8px 8px 6px;font-size:11px}.category-btn{border-radius:var(--radius-sm);width:100%;color:var(--text-secondary);text-align:left;transition:all var(--transition-fast);background:0 0;justify-content:space-between;align-items:center;padding:8px 10px;font-size:13px;display:flex}.category-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.category-btn.active{background:var(--accent-muted);color:var(--accent)}.category-count{background:var(--bg-tertiary);color:var(--text-tertiary);border-radius:99px;padding:1px 6px;font-size:11px}.memory-main{flex-direction:column;gap:12px;display:flex;overflow:hidden}.memory-search{gap:8px;display:flex}.memory-search input{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);color:var(--text-primary);font-size:13px;font-family:var(--font-sans);transition:border-color var(--transition-fast);flex:1;padding:10px 14px}.memory-search input:focus{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-glow);outline:none}.memory-search input::placeholder{color:var(--text-tertiary)}.memory-list{flex-direction:column;flex:1;gap:8px;display:flex;overflow-y:auto}.memory-node-card{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);cursor:pointer;transition:all var(--transition-fast);padding:14px 16px}.memory-node-card:hover{border-color:var(--accent);background:var(--bg-tertiary)}.memory-node-card.selected{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent-glow)}.memory-node-card h4{margin-bottom:4px;font-size:14px;font-weight:600}.memory-node-card .meta{color:var(--text-tertiary);gap:12px;font-size:12px;display:flex}.memory-detail{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);padding:24px;overflow-y:auto}.memory-detail h2{margin-bottom:12px;font-size:18px}.memory-detail pre{background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius-sm);white-space:pre-wrap;color:var(--text-secondary);padding:16px;font-size:12px;line-height:1.7;overflow-x:auto}.memory-detail .frontmatter{background:var(--bg-tertiary);border-radius:var(--radius-sm);grid-template-columns:auto 1fr;gap:4px 16px;margin-bottom:16px;padding:12px 16px;font-size:13px;display:grid}.frontmatter dt{color:var(--text-tertiary);font-weight:500}.frontmatter dd{color:var(--text-primary)}.health-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:24px;display:grid}.stat-card{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);text-align:center;transition:border-color var(--transition-normal);padding:20px}.stat-card:hover{border-color:var(--border-hover)}.stat-card .value{letter-spacing:-.02em;font-size:28px;font-weight:700}.stat-card .label{color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.04em;margin-top:4px;font-size:12px}.stat-card.healthy .value{color:var(--success)}.stat-card.warning .value{color:var(--warning)}.stat-card.error .value{color:var(--error)}.health-log{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px}.health-log h3{margin-bottom:12px;font-size:14px;font-weight:600}.health-log table{border-collapse:collapse;width:100%;font-size:13px}.health-log th{text-align:left;color:var(--text-tertiary);border-bottom:1px solid var(--border);padding:8px 12px;font-weight:500}.health-log td{border-bottom:1px solid var(--border);color:var(--text-secondary);padding:8px 12px}.sandbox-layout{grid-template-rows:1fr auto 1fr;gap:0;height:calc(100vh - 120px);display:grid}.sandbox-editor{flex-direction:column;display:flex;overflow:hidden}.sandbox-editor-header{background:var(--bg-tertiary);border:1px solid var(--border);border-radius:var(--radius-md) var(--radius-md) 0 0;color:var(--text-tertiary);justify-content:space-between;align-items:center;padding:8px 12px;font-size:12px;display:flex}.sandbox-editor textarea{resize:none;background:var(--bg-secondary);border:1px solid var(--border);border-radius:0 0 var(--radius-md) var(--radius-md);width:100%;color:var(--text-primary);font-family:var(--font-mono);tab-size:2;border-top:none;flex:1;padding:16px;font-size:13px;line-height:1.6}.sandbox-editor textarea:focus{border-color:var(--accent);outline:none}.sandbox-toolbar{justify-content:center;align-items:center;padding:8px;display:flex}.sandbox-output{background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius-md);font-family:var(--font-mono);color:var(--success);white-space:pre-wrap;padding:16px;font-size:13px;line-height:1.6;overflow-y:auto}.sandbox-output.error{color:var(--error)}.pulse{background:var(--success);border-radius:50%;width:8px;height:8px;animation:2s infinite pulseAnim;display:inline-block}@keyframes pulseAnim{0%,to{box-shadow:0 0 #00cec966}50%{box-shadow:0 0 0 6px #00cec900}}.skeleton{background:linear-gradient(90deg, var(--bg-tertiary) 25%, var(--bg-hover) 50%, var(--bg-tertiary) 75%);border-radius:var(--radius-sm);background-size:200% 100%;animation:1.5s infinite shimmer}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (width<=768px){.sidebar{position:fixed;top:0;bottom:0;left:0;transform:translate(-100%);box-shadow:0 0 40px #00000080}.sidebar.open{transform:translate(0)}.memory-layout{grid-template-columns:1fr;height:auto}.page{padding:16px}.btn,.nav-link,.category-btn{touch-action:manipulation;min-height:44px}.chat-input-wrapper textarea,.memory-search input,.sandbox-editor textarea{font-size:16px}.chat-input-bar{padding:12px 16px 16px}}.chat-layout{width:100%;height:100%;display:flex;overflow:hidden}.chat-threads-sidebar{background:var(--bg-secondary);border-right:1px solid var(--border);flex-direction:column;flex-shrink:0;width:260px;height:100%;display:flex;overflow:hidden}.sidebar-header{border-bottom:1px solid var(--border);padding:16px}.new-chat-btn{background:var(--accent);color:#fff;border-radius:var(--radius-sm);width:100%;transition:all var(--transition-fast);cursor:pointer;border:none;justify-content:center;align-items:center;gap:8px;padding:10px 16px;font-size:13px;font-weight:500;display:flex}.new-chat-btn:hover{background:var(--accent-hover);box-shadow:0 0 16px var(--accent-glow)}.thread-list{flex-direction:column;flex:1;gap:6px;padding:16px 12px;display:flex;overflow-y:auto}.thread-item{border-radius:var(--radius-sm);cursor:pointer;transition:all var(--transition-fast);border:1px solid #0000;justify-content:space-between;align-items:center;padding:10px 12px;display:flex;position:relative}.thread-item:hover{background:var(--bg-hover)}.thread-item.active{border-color:var(--border);background:#ffffff0d}.thread-info{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.thread-title{color:var(--text-primary);white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:500;overflow:hidden}.thread-meta{color:var(--text-tertiary);align-items:center;gap:8px;font-size:11px;display:flex}.thread-delete-btn{color:var(--text-tertiary);cursor:pointer;opacity:0;transition:all var(--transition-fast);background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;padding:4px;display:flex}.thread-item:hover .thread-delete-btn{opacity:1}.thread-delete-btn:hover{color:var(--error);background:var(--error-muted)}.chat-suggestions-container{width:100%;max-width:900px;margin-top:32px;animation:.4s ease-out fadeIn}.chat-suggestions-container h3{text-transform:uppercase;letter-spacing:.08em;color:var(--text-tertiary);text-align:center;margin-bottom:16px;font-size:12px}.chat-suggestions-deck{grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;width:100%;display:grid}.suggestion-card{-webkit-backdrop-filter:blur(12px);border-radius:var(--radius-md);cursor:pointer;text-align:left;transition:all var(--transition-normal);background:#12121a73;border:1px solid #ffffff0d;gap:14px;padding:16px;display:flex;position:relative;overflow:hidden}.suggestion-card:before{content:"";opacity:.8;width:4px;height:100%;transition:width var(--transition-fast) ease;position:absolute;top:0;left:0}.suggestion-card.card-fact:before{background:var(--success)}.suggestion-card.card-concept:before{background:var(--accent)}.suggestion-card.card-question:before{background:var(--warning)}.suggestion-card:hover{background:#1a1a2699;border-color:#ffffff1f;transform:translateY(-2px);box-shadow:0 8px 24px #0003}.suggestion-card:hover:before{width:6px}.suggestion-card.card-fact:hover{border-color:#00cec940;box-shadow:0 8px 24px #00cec90f}.suggestion-card.card-concept:hover{border-color:#6c5ce740;box-shadow:0 8px 24px #6c5ce70f}.suggestion-card.card-question:hover{border-color:#fdcb6e40;box-shadow:0 8px 24px #fdcb6e0f}.suggestion-card-icon{-webkit-user-select:none;user-select:none;align-items:flex-start;padding-top:2px;font-size:22px;display:flex}.suggestion-card-content{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.suggestion-card-content h4{color:var(--text-primary);font-size:13.5px;font-weight:600;line-height:1.4}.suggestion-card-content p{color:var(--text-secondary);font-size:12.5px;line-height:1.5}.suggestion-card-nodes{flex-wrap:wrap;gap:6px;margin-top:8px;display:flex}.suggestion-node-pill{background:var(--bg-tertiary);border:1px solid var(--border);color:var(--text-secondary);white-space:nowrap;text-overflow:ellipsis;border-radius:4px;max-width:140px;padding:2px 6px;font-size:10px;font-weight:500;overflow:hidden}.grounding-pills{border:1px solid var(--border);border-radius:var(--radius-md) var(--radius-md) 0 0;background:#12121a66;border-bottom:none;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 12px;display:flex}.grounding-pill{background:var(--accent-muted);color:var(--text-primary);transition:all var(--transition-fast);border:1px solid #6c5ce74d;border-radius:99px;align-items:center;gap:6px;padding:4px 10px;font-size:12px;font-weight:500;display:inline-flex}.grounding-pill:hover{background:#6c5ce733;border-color:#6c5ce780}.grounding-pill svg{color:var(--accent);flex-shrink:0}.grounding-pill-title{text-overflow:ellipsis;white-space:nowrap;max-width:150px;overflow:hidden}.grounding-pill-remove{color:var(--text-tertiary);cursor:pointer;transition:color var(--transition-fast);background:0 0;border:none;justify-content:center;align-items:center;padding:0 2px;font-size:14px;line-height:1;display:inline-flex}.grounding-pill-remove:hover{color:var(--error)}.grounding-count-badge{background:var(--accent);color:#fff;border:1.5px solid var(--bg-secondary);min-width:16px;height:16px;box-shadow:0 0 8px var(--accent-glow);border-radius:50%;justify-content:center;align-items:center;font-size:9px;font-weight:700;animation:.2s cubic-bezier(.175,.885,.32,1.275) popIn;display:flex;position:absolute;top:-4px;right:-4px}.node-selector-popover{-webkit-backdrop-filter:blur(20px);border-radius:var(--radius-lg);z-index:50;background:#12121ad9;border:1px solid #ffffff14;flex-direction:column;width:320px;max-height:380px;animation:.25s cubic-bezier(.16,1,.3,1) slideUp;display:flex;position:absolute;bottom:calc(100% + 12px);left:0;overflow:hidden;box-shadow:0 12px 32px #0006}.node-selector-header{border-bottom:1px solid var(--border);flex-direction:column;gap:8px;padding:14px 16px 10px;display:flex}.node-selector-header h4{color:var(--text-primary);letter-spacing:.02em;font-size:13px;font-weight:600}.node-search-input{background:var(--bg-tertiary);border:1px solid var(--border);border-radius:var(--radius-sm);width:100%;color:var(--text-primary);font-size:12px;font-family:var(--font-sans);transition:all var(--transition-fast);padding:6px 10px}.node-search-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-glow);outline:none}.node-selector-list{flex-direction:column;flex:1;gap:2px;padding:8px;display:flex;overflow-y:auto}.node-selector-empty{text-align:center;color:var(--text-tertiary);padding:24px;font-size:12px}.node-selector-item{border-radius:var(--radius-sm);cursor:pointer;transition:all var(--transition-fast);align-items:center;gap:10px;padding:8px 12px;display:flex}.node-selector-item:hover{background:var(--bg-hover)}.node-selector-item.selected{background:var(--accent-muted)}.node-selector-item-checkbox{border:1px solid var(--text-tertiary);width:16px;height:16px;color:var(--accent);transition:all var(--transition-fast);background:var(--bg-tertiary);border-radius:3px;flex-shrink:0;justify-content:center;align-items:center;font-size:11px;font-weight:700;display:flex}.node-selector-item.selected .node-selector-item-checkbox{background:var(--accent);border-color:var(--accent);color:#fff}.node-selector-item-info{flex-direction:column;flex:1;min-width:0;display:flex}.node-selector-item-title{color:var(--text-primary);white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:500;overflow:hidden}.node-selector-item-meta{color:var(--text-tertiary);text-transform:capitalize;font-size:11px}.animate-fade-in{animation:.2s ease-out fadeIn}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes popIn{0%{opacity:0;transform:scale(.6)}to{opacity:1;transform:scale(1)}}.graph-3d-wrapper{-webkit-user-select:none;user-select:none;background:radial-gradient(circle,#0a0d16 0%,#020408 100%);width:100%;height:100%;position:relative;overflow:hidden}.node-detail-drawer{-webkit-backdrop-filter:blur(25px);border-radius:var(--radius-lg);z-index:100;width:350px;color:var(--text-primary);background:#0a0f1ebf;border:1px solid #ffffff14;flex-direction:column;display:flex;position:absolute;top:20px;bottom:20px;right:20px;overflow:hidden;box-shadow:0 16px 48px #00000080,inset 0 0 0 1px #ffffff0d}.animate-slide-in{animation:.4s cubic-bezier(.16,1,.3,1) forwards slideInRight}@keyframes slideInRight{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}.drawer-header{border-bottom:1px solid #ffffff0f;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.drawer-badge{text-transform:uppercase;letter-spacing:.05em;border-radius:4px;padding:4px 8px;font-size:10px;font-weight:700}.drawer-badge.badge-memory{color:#c084fc;background:#8b5cf626;border:1px solid #8b5cf64d}.drawer-badge.badge-thread{color:#22d3ee;background:#06b6d426;border:1px solid #06b6d44d}.drawer-badge.badge-research{color:#fbbf24;background:#f59e0b26;border:1px solid #f59e0b4d}.drawer-close-btn{color:var(--text-tertiary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:4px;font-size:20px;transition:color .15s;display:flex}.drawer-close-btn:hover{color:#fff}.drawer-body{flex-direction:column;flex:1;gap:12px;padding:20px;display:flex;overflow-y:auto}.drawer-title{color:#fff;margin:0;font-size:18px;font-weight:700;line-height:1.35}.drawer-subtitle{color:var(--text-tertiary);margin-top:-4px;font-size:11px}.drawer-status{background:#ffffff08;border:1px solid #ffffff0a;border-radius:6px;align-items:center;gap:8px;width:fit-content;padding:6px 12px;font-size:12px;display:flex}.drawer-status .label{color:var(--text-tertiary)}.drawer-status .value{text-transform:capitalize;font-weight:600}.drawer-status .value.status-completed,.drawer-status .value.status-done,.drawer-status .value.status-active{color:#10b981}.drawer-status .value.status-in-progress{color:#f59e0b}.drawer-status .value.status-pending{color:#3b82f6}.drawer-excerpt{color:var(--text-secondary);white-space:pre-wrap;margin-top:8px;font-size:13.5px;line-height:1.6}.drawer-footer{background:#00000026;border-top:1px solid #ffffff0f;padding:16px 20px}.drawer-action-btn{border-radius:var(--radius-sm);cursor:pointer;width:100%;transition:all var(--transition-fast);border:none;justify-content:center;align-items:center;padding:10px 16px;font-size:13px;font-weight:600;display:flex}.drawer-action-btn.btn-cyan{color:#fff;background:#06b6d4}.drawer-action-btn.btn-cyan:hover{background:#0891b2;box-shadow:0 0 16px #06b6d466}.drawer-action-btn.btn-purple{color:#fff;background:#8b5cf6}.drawer-action-btn.btn-purple:hover{background:#7c3aed;box-shadow:0 0 16px #8b5cf666}@media (width<=768px){.node-detail-drawer{width:auto;height:300px;bottom:20px;left:20px}}.chat-messages:has(.graph-3d-wrapper){padding:0;overflow:hidden}.chat-header{-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border);z-index:40;background:#12121aeb;flex-shrink:0;justify-content:space-between;align-items:center;padding:12px 24px;display:flex;position:relative;overflow:visible}.chat-header-title{align-items:center;gap:10px;display:flex}.chat-header-title h2{color:var(--text-primary);letter-spacing:-.01em;font-size:15px;font-weight:600}.chat-header-model-selector{align-items:center;gap:8px;display:flex}.chat-header-model-selector span.selector-label{color:var(--text-tertiary);font-size:12px;font-weight:500}.model-selector-dropdown-wrapper{position:relative}.model-selector-dropdown-btn{border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text-secondary);transition:all var(--transition-fast);cursor:pointer;background:#ffffff0a;align-items:center;gap:8px;padding:6px 12px;font-size:13px;font-weight:500;display:flex}.model-selector-dropdown-btn:hover{border-color:var(--border-hover);color:var(--text-primary);background:#ffffff14}.model-selector-dropdown-btn.active{border-color:var(--accent);color:var(--text-primary);background:var(--accent-muted)}.model-selector-menu{-webkit-backdrop-filter:blur(20px);border:1px solid var(--border);border-radius:var(--radius-md);z-index:200;pointer-events:auto;background:#12121afa;flex-direction:column;gap:2px;width:240px;margin-top:6px;padding:6px;animation:.15s ease-out popoverIn;display:flex;position:absolute;top:100%;right:0;box-shadow:0 12px 32px #0000008c}@keyframes popoverIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.model-selector-item{border-radius:var(--radius-sm);cursor:pointer;transition:all var(--transition-fast);text-align:left;justify-content:space-between;align-items:center;padding:8px 12px;display:flex}.model-selector-item:hover{background:var(--bg-hover)}.model-selector-item.selected{background:var(--accent-muted)}.model-selector-item-info{flex-direction:column;gap:2px;display:flex}.model-selector-item-name{color:var(--text-primary);font-size:13px;font-weight:500}.model-selector-item.selected .model-selector-item-name{color:var(--accent)}.model-selector-item-desc{color:var(--text-tertiary);font-size:11px}.model-selector-item-badge{background:var(--bg-tertiary);color:var(--text-tertiary);text-transform:uppercase;border-radius:99px;padding:2px 6px;font-size:9px;font-weight:600}.model-selector-item.selected .model-selector-item-badge{background:var(--accent-muted);color:var(--accent)}@media (width<=768px){button,a,[role=button]{min-width:44px;min-height:44px}.chat-input-area{bottom:0;bottom:env(safe-area-inset-bottom,0);position:sticky}.app-container{padding-left:env(safe-area-inset-left,0);padding-right:env(safe-area-inset-right,0);padding-bottom:env(safe-area-inset-bottom,0)}}@media (display-mode:standalone){body{padding-top:env(safe-area-inset-top,0)}}.network-page{animation:.35s ease-out page-fade-in}@keyframes page-fade-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.stat-grid{grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:32px;display:grid}.stat-card{text-align:center;flex-direction:column;align-items:center;padding:24px 16px;display:flex}.stat-value{color:var(--text-primary);font-variant-numeric:tabular-nums;font-size:32px;font-weight:700;line-height:1.2;transition:color .3s}.stat-value.error{color:var(--error)}.stat-value.success{color:var(--success)}.stat-value.accent{color:var(--accent)}.stat-label{color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.06em;margin-top:8px;font-size:12px;font-weight:500}.network-sections{grid-template-columns:1fr 1fr;gap:24px;margin-bottom:32px;display:grid}.network-section-full{margin-bottom:32px}.section-title{color:var(--text-primary);align-items:center;gap:8px;margin-bottom:16px;font-size:15px;font-weight:600;display:flex}.blocklist-items{max-height:240px;margin:0 0 12px;padding:0;list-style:none;overflow-y:auto}.blocklist-item{border-bottom:1px solid var(--border);font-family:var(--font-mono);color:var(--text-primary);transition:background var(--transition-fast);justify-content:space-between;align-items:center;padding:8px 12px;font-size:13px;display:flex}.blocklist-item:hover{background:var(--bg-hover)}.blocklist-item:last-child{border-bottom:none}.blocklist-remove{color:var(--error);opacity:.6;transition:opacity var(--transition-fast), background var(--transition-fast);background:0 0;border-radius:4px;padding:4px 8px;font-size:12px}.blocklist-remove:hover{opacity:1;background:var(--error-muted)}.blocklist-add{gap:8px;display:flex}.blocklist-add .input{flex:1}.limits-form{flex-direction:column;gap:12px;display:flex}.limits-row{justify-content:space-between;align-items:center;gap:12px;display:flex}.limits-label{color:var(--text-secondary);flex-shrink:0;font-size:13px}.limits-input{text-align:right;width:100px}.limits-actions{justify-content:flex-end;margin-top:8px;display:flex}.audit-filters{flex-wrap:wrap;gap:12px;margin-bottom:16px;display:flex}.audit-filters .input,.audit-filters .select{flex:1;min-width:140px}.audit-status-badge{border-radius:3px;align-items:center;padding:2px 6px;font-size:11px;font-weight:600;display:inline-flex}.audit-status-badge.success{background:var(--success-muted);color:var(--success)}.audit-status-badge.error{background:var(--error-muted);color:var(--error)}.audit-status-badge.timeout{background:var(--warning-muted);color:var(--warning)}.toggle-row{align-items:center;gap:12px;margin-bottom:16px;display:flex}.toggle-switch{background:var(--bg-hover);cursor:pointer;width:44px;height:24px;transition:background var(--transition-fast);border:1px solid var(--border);border-radius:12px;flex-shrink:0;position:relative}.toggle-switch.active{background:var(--accent);border-color:var(--accent)}.toggle-switch:after{content:"";width:18px;height:18px;transition:transform var(--transition-fast);background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;box-shadow:0 1px 3px #0003}.toggle-switch.active:after{transform:translate(20px)}.toggle-label{color:var(--text-secondary);font-size:13px}.refresh-controls{align-items:center;gap:8px;display:flex}.refresh-controls .select{width:auto;padding:6px 28px 6px 10px;font-size:12px}.empty-state{text-align:center;color:var(--text-tertiary);padding:32px 16px;font-size:13px;font-style:italic}@media (width<=768px){.stat-grid,.network-sections{grid-template-columns:1fr}}.webhooks-page{animation:.35s ease-out page-fade-in}.wizard-modal{width:100%;max-width:600px}.wizard-steps{justify-content:space-between;margin-bottom:24px;display:flex;position:relative}.wizard-steps:before{content:"";z-index:0;background:#ffffff1a;height:2px;position:absolute;top:12px;left:0;right:0}.wizard-step{background:var(--bg-surface);color:var(--text-muted);z-index:1;border:2px solid #ffffff1a;border-radius:50%;justify-content:center;align-items:center;width:28px;height:28px;font-size:14px;font-weight:600;transition:all .3s;display:flex}.wizard-step.active{background:var(--primary);color:#fff;border-color:var(--primary);box-shadow:0 0 12px #6366f180}.wizard-step.completed{background:var(--success);color:#fff;border-color:var(--success)}.json-viewer{color:#a5b4fc;background:#0003;border:1px solid #ffffff0d;border-radius:8px;margin-top:8px;padding:12px;font-family:Fira Code,monospace;font-size:13px;overflow-x:auto}.json-toggle{color:var(--primary);cursor:pointer;background:0 0;border:none;margin-top:4px;padding:0;font-size:12px}.json-toggle:hover{text-decoration:underline}.webhook-stats{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:16px;margin-bottom:24px;display:grid}.stat-box{text-align:center;background:#ffffff08;border:1px solid #ffffff0d;border-radius:12px;padding:16px}.stat-box .label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;font-size:12px}.stat-box .value{color:var(--text-primary);font-size:24px;font-weight:700}.rotate-btn{color:var(--text-primary);cursor:pointer;background:#ffffff0d;border:1px solid #ffffff1a;border-radius:4px;padding:4px 8px;font-size:12px;transition:all .2s}.rotate-btn:hover{background:#ffffff1a}.notifications-page{animation:.35s ease-out notif-fade-in}@keyframes notif-fade-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.notif-rules-card{padding:0;overflow:hidden}.notif-rules-actions{gap:6px;display:flex}.priority-badge{text-transform:uppercase;letter-spacing:.04em;border-radius:4px;align-items:center;padding:2px 8px;font-size:11px;font-weight:600;display:inline-flex}.priority-badge.critical{background:var(--error-muted);color:var(--error)}.priority-badge.high{background:var(--warning-muted);color:var(--warning)}.priority-badge.low{background:var(--success-muted);color:var(--success)}.notif-toggle{background:var(--bg-hover);cursor:pointer;width:40px;height:22px;transition:background var(--transition-fast);border:1px solid var(--border);border-radius:11px;flex-shrink:0;position:relative}.notif-toggle.active{background:var(--accent);border-color:var(--accent)}.notif-toggle:after{content:"";width:16px;height:16px;transition:transform var(--transition-fast);background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;box-shadow:0 1px 3px #0003}.notif-toggle.active:after{transform:translate(18px)}.notif-history-header{justify-content:space-between;align-items:center;margin-bottom:16px;display:flex}.notif-history-header h2{color:var(--text-primary);font-size:15px;font-weight:600}.notif-history-list{flex-direction:column;gap:10px;max-height:420px;display:flex;overflow-y:auto}.notif-history-entry{background:var(--bg-hover);border-radius:var(--radius-sm);transition:background var(--transition-fast);justify-content:space-between;align-items:flex-start;padding:14px 16px;display:flex}.notif-history-entry:hover{background:var(--bg-secondary)}.notif-entry-content{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.notif-entry-title{color:var(--text-primary);font-size:14px;font-weight:500}.notif-entry-message{color:var(--text-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:13px;overflow:hidden}.notif-entry-meta{flex-direction:column;flex-shrink:0;align-items:flex-end;gap:4px;margin-left:16px;display:flex}.notif-entry-time{color:var(--text-tertiary);white-space:nowrap;font-size:12px}.notif-status-badge{border-radius:3px;align-items:center;padding:2px 6px;font-size:11px;font-weight:600;display:inline-flex}.notif-status-badge.delivered{background:var(--success-muted);color:var(--success)}.notif-status-badge.failed{background:var(--error-muted);color:var(--error)}.notif-quiet-hours-row{align-items:center;gap:10px;display:flex}.notif-quiet-hours-label{color:var(--text-secondary);cursor:pointer;font-size:13px}.notif-empty{text-align:center;color:var(--text-tertiary);padding:32px 16px;font-size:13px;font-style:italic}@media (width<=768px){.notif-history-entry{flex-direction:column;gap:8px}.notif-entry-meta{flex-direction:row;align-items:center;margin-left:0}}