total-recall-brain 3.46.2 → 3.47.1
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.
- package/bin/total-recall.mjs +5 -0
- package/frontend/dist/assets/index-CpsWeV4X.css +1 -0
- package/frontend/dist/assets/{index-D1goIPjC.js → index-poecbHSl.js} +100 -100
- package/frontend/dist/brand/total-recall-lockup-transparent.png +0 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/sw.js +1 -1
- package/frontend/src/App.tsx +15 -81
- package/frontend/src/components/DashboardOverview.tsx +164 -0
- package/frontend/src/components/brand/BrandMark.tsx +8 -6
- package/frontend/src/components/plugins/PluginWorkspace.tsx +112 -57
- package/frontend/src/components/workspace/PluginHome.tsx +3 -3
- package/frontend/src/index.css +43 -11
- package/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +30 -5
- package/scaffold/.agent/skills/total-recall/plugins/auth/skills/auth/SKILL.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/home.js +494 -79
- package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +33 -8
- package/scaffold/.agent/skills/total-recall/plugins/automations/skills/automations/SKILL.md +29 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/dream-consolidation.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +31 -4
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/skills/code-quality/SKILL.md +27 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +17 -3
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +24 -4
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/skills/embeddings/SKILL.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/git/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +300 -58
- package/scaffold/.agent/skills/total-recall/plugins/git/skills/git/SKILL.md +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +5 -33
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/git-fleet-ops.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/git-publish.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +47 -14
- package/scaffold/.agent/skills/total-recall/plugins/mesh/skills/mesh/SKILL.md +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +28 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +7 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-selection.mjs +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +9 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +50 -6
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/session-controls.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +175 -17
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +242 -31
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/skills/openwiki/SKILL.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +41 -15
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +29 -22
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +20 -33
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/lint.mjs +42 -49
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/markdown-links.mjs +112 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +219 -65
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +21 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +51 -2
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +22 -5
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +169 -37
- package/scaffold/.agent/skills/total-recall/plugins/ssss/skills/ssss/SKILL.md +30 -0
- package/src/cli/context.mjs +6 -2
- package/src/cli/plugin/create.mjs +2 -1
- package/src/cli/plugin/monetization.mjs +66 -32
- package/src/cli/plugin/search.mjs +25 -3
- package/src/cli/preflight.mjs +40 -6
- package/src/core/context-compactor.mjs +2 -0
- package/src/core/gate-mode.mjs +13 -24
- package/src/core/plugin-distribution.mjs +35 -1
- package/src/core/preflight-gate.mjs +28 -8
- package/src/core/preflight-receipt.mjs +11 -0
- package/src/server/routes/vault.mjs +1 -0
- package/frontend/dist/assets/index-BRYE1jmv.css +0 -1
package/src/core/gate-mode.mjs
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Modes for the gate rules that steer work toward a plugin: raw commands a plugin owns, and
|
|
3
|
-
* routing.
|
|
4
|
-
* The preflight receipt is not one of these rules and has no mode.
|
|
3
|
+
* routing. Gates are permanently enforced (`enforce`): there is no `off` or `warn` bypass.
|
|
5
4
|
*
|
|
6
|
-
* Settings live in `config/gate.json` in a brain
|
|
7
|
-
*
|
|
8
|
-
* The repository's brain wins over the machine's. This runs on every tool call, so it reads the
|
|
9
|
-
* files directly, and an unreadable file or unknown value counts as unset: a bad settings file
|
|
10
|
-
* must never be what blocks work.
|
|
5
|
+
* Settings live in `config/gate.json` in a brain. Any legacy `off` or `warn` values in
|
|
6
|
+
* `gate.json` are ignored so enforcement cannot be disabled on disk.
|
|
11
7
|
*/
|
|
12
8
|
import fs from 'node:fs';
|
|
13
9
|
import path from 'node:path';
|
|
14
10
|
|
|
15
|
-
export const GATE_MODES = ['
|
|
16
|
-
export const DEFAULT_GATE_MODE = '
|
|
11
|
+
export const GATE_MODES = ['enforce'];
|
|
12
|
+
export const DEFAULT_GATE_MODE = 'enforce';
|
|
17
13
|
|
|
18
14
|
export const gateConfigPath = (brainDir) => path.join(brainDir, 'config', 'gate.json');
|
|
19
15
|
export const projectBrainDir = (root) => path.join(root, '.agent', 'skills', 'total-recall');
|
|
@@ -30,39 +26,32 @@ export function readGateLayers({ globalBrainDir, root }) {
|
|
|
30
26
|
return [readSettings(projectBrainDir(root)), readSettings(globalBrainDir)];
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
const known = (value) => (GATE_MODES.includes(value) ? value : undefined);
|
|
34
|
-
|
|
35
29
|
/**
|
|
36
30
|
* @param {object[]} layers from readGateLayers
|
|
37
31
|
* @param {'router'|'owned'} kind
|
|
38
32
|
* @param {string} [plugin] the owning plugin, for `owned`
|
|
39
33
|
*/
|
|
40
|
-
export function resolveGateMode(
|
|
41
|
-
for (const layer of layers) {
|
|
42
|
-
const specific = kind === 'router' ? known(layer.router) : known(layer.plugins?.[plugin]);
|
|
43
|
-
const mode = specific ?? known(layer.default);
|
|
44
|
-
if (mode) return mode;
|
|
45
|
-
}
|
|
34
|
+
export function resolveGateMode(_layers, _kind, _plugin) {
|
|
46
35
|
return DEFAULT_GATE_MODE;
|
|
47
36
|
}
|
|
48
37
|
|
|
49
38
|
/**
|
|
50
|
-
*
|
|
51
|
-
* that level follows the next layer again.
|
|
39
|
+
* Persist `enforce` mode. `off`, `warn`, and clearing are refused.
|
|
52
40
|
* @param {string} brainDir
|
|
53
41
|
* @param {{ mode: string|null, plugin?: string, router?: boolean }} change
|
|
54
42
|
*/
|
|
55
43
|
export function writeGateMode(brainDir, { mode, plugin, router }) {
|
|
44
|
+
if (mode !== 'enforce') {
|
|
45
|
+
throw new Error('Plugin and routing gates are permanently enforced and cannot be set to off, warn, or cleared.');
|
|
46
|
+
}
|
|
56
47
|
const settings = readSettings(brainDir);
|
|
57
48
|
if (router) {
|
|
58
|
-
|
|
49
|
+
settings.router = 'enforce';
|
|
59
50
|
} else if (plugin) {
|
|
60
51
|
settings.plugins = settings.plugins && typeof settings.plugins === 'object' ? settings.plugins : {};
|
|
61
|
-
|
|
62
|
-
} else if (mode === null) {
|
|
63
|
-
delete settings.default;
|
|
52
|
+
settings.plugins[plugin] = 'enforce';
|
|
64
53
|
} else {
|
|
65
|
-
settings.default =
|
|
54
|
+
settings.default = 'enforce';
|
|
66
55
|
}
|
|
67
56
|
const file = gateConfigPath(brainDir);
|
|
68
57
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -12,7 +12,41 @@ function describePluginWithShare(plugin, projectRoot) {
|
|
|
12
12
|
return result;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
async function
|
|
15
|
+
async function resolveStoreBundle(id, { storeUrl = process.env.TR_STORE_URL || 'http://127.0.0.1:3000', fetchImpl = fetch, licenseKey } = {}) {
|
|
16
|
+
const base = storeUrl.replace(/\/+$/, '');
|
|
17
|
+
const headers = {};
|
|
18
|
+
const resolvedKey = licenseKey || process.env[`${id.toUpperCase().replace(/-/g, '_')}_LICENSE_KEY`] || process.env.TR_LICENSE_KEY;
|
|
19
|
+
if (resolvedKey) headers['x-license-key'] = resolvedKey;
|
|
20
|
+
const listRes = await fetchImpl(`${base}/api/store/plugins?q=${encodeURIComponent(id)}`);
|
|
21
|
+
if (!listRes.ok) throw new Error(`Store catalog unavailable (HTTP ${listRes.status})`);
|
|
22
|
+
const listData = await listRes.json();
|
|
23
|
+
const entry = (listData.plugins || []).find((p) => p.id === id);
|
|
24
|
+
if (!entry) throw new Error(`Plugin '${id}' not found in Store catalog`);
|
|
25
|
+
const bundleRes = await fetchImpl(`${base}/api/store/plugins/${encodeURIComponent(id)}/bundle`, { headers });
|
|
26
|
+
if (!bundleRes.ok) {
|
|
27
|
+
let reason = `HTTP ${bundleRes.status}`;
|
|
28
|
+
try { const err = await bundleRes.json(); if (err?.error) reason = err.error; } catch {}
|
|
29
|
+
throw new Error(`Store install denied for '${id}': ${reason}`);
|
|
30
|
+
}
|
|
31
|
+
const bundle = Buffer.from(await bundleRes.arrayBuffer());
|
|
32
|
+
const sha256 = bundleRes.headers?.get?.('x-plugin-sha256') || entry.sha256;
|
|
33
|
+
return { bundle, sha256, sourceRecord: { kind: 'store', ref: `store:${id}`, store_url: base }, expectedId: id };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function resolveBundle(source, { peerDeps, publicDeps, storeDeps } = {}) {
|
|
37
|
+
if (typeof source === 'string' && source.startsWith('store:')) {
|
|
38
|
+
const id = source.slice('store:'.length).trim();
|
|
39
|
+
if (!id) throw new Error('Missing plugin id in store:<id>');
|
|
40
|
+
const { bundle, sha256, sourceRecord, expectedId } = await resolveStoreBundle(id, storeDeps);
|
|
41
|
+
const decoded = decodeBundle(bundle, sha256 ? { expectedSha256: sha256 } : {});
|
|
42
|
+
const entry = decoded.entries.find(e => e.path === 'plugin.json');
|
|
43
|
+
if (!entry) throw new Error('Store plugin bundle has no plugin.json');
|
|
44
|
+
const manifest = JSON.parse(entry.data.toString('utf8'));
|
|
45
|
+
const validation = validatePluginManifest(manifest);
|
|
46
|
+
if (!validation.valid) throw new Error(`Invalid plugin manifest: ${validation.errors.join('; ')}`);
|
|
47
|
+
if (manifest.id !== expectedId) throw new Error(`Requested '${expectedId}' but bundle contained '${manifest.id}'`);
|
|
48
|
+
return { manifest, entries: decoded.entries, sha256: decoded.sha256, source: sourceRecord };
|
|
49
|
+
}
|
|
16
50
|
const peerModule = typeof source === 'string' && source.startsWith('peer:')
|
|
17
51
|
? await import('./plugin-peers.mjs') : null;
|
|
18
52
|
const peer = peerModule?.parsePeerSource(source);
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
* (`gate.router`). Reading, searching and native subagents stay open, as does any
|
|
30
30
|
* plain Total Recall command, so routing can always run. A process the router
|
|
31
31
|
* started has already been routed. It warns once per receipt, not on every call.
|
|
32
|
+
* 8. With a receipt, once the router has selected a harness for this repository, a write from
|
|
33
|
+
* any other harness is denied, whatever the mode. Only a run the router started may write
|
|
34
|
+
* for it. A selection lasts as long as a receipt, and routing again replaces it. A call with
|
|
35
|
+
* no named harness is not judged by this rule, because it cannot be told apart.
|
|
32
36
|
* Rules 6 and 7 follow a mode (see gate-mode.mjs): `off`, `warn` (the default) or `enforce`.
|
|
33
37
|
* `enforce` blocks only while the owner passes its health check, so a broken plugin can
|
|
34
38
|
* never trap an agent. Warnings are statements of fact, because harnesses treat
|
|
@@ -154,6 +158,13 @@ export function ackDirectory(command, cwd) {
|
|
|
154
158
|
return target ? path.resolve(cwd || process.cwd(), target.replace(/^(["'])([\s\S]*)\1$/, '$2')) : cwd;
|
|
155
159
|
}
|
|
156
160
|
|
|
161
|
+
export function isBlockedGrep(command) {
|
|
162
|
+
const text = runnableText(command);
|
|
163
|
+
if (/(^|[\s;&|(])(grep|egrep|fgrep|rg|ripgrep|ag)(\s|$)/.test(text)) return true;
|
|
164
|
+
if (/(^|[\s;&|(])git\s+grep(\s|$)/.test(text)) return true;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
157
168
|
/** A repository-wide search used to answer a question, as the DSH `tr-first.sh` rule defined it. */
|
|
158
169
|
export function isRepoSweep(command) {
|
|
159
170
|
const c = String(command || '');
|
|
@@ -235,10 +246,7 @@ const staleness = (receipt) => (receipt.ageMs > receipt.limitMs
|
|
|
235
246
|
* What a rule that steers work toward a plugin does with a call it matched: nothing (null),
|
|
236
247
|
* a denial, or a warning to carry.
|
|
237
248
|
*/
|
|
238
|
-
function steer(deps, {
|
|
239
|
-
const mode = deps.mode ? deps.mode(kind, plugin) : DEFAULT_GATE_MODE;
|
|
240
|
-
if (mode === 'off') return null;
|
|
241
|
-
if (mode !== 'enforce') return { name, warning: note };
|
|
249
|
+
function steer(deps, { plugin, name, reason, note, use }) {
|
|
242
250
|
if (!deps.ownerHealthy || deps.ownerHealthy(plugin, use)) return deny(name, reason, use);
|
|
243
251
|
return { name, warning: `${note} The ${plugin} plugin failed its health check, so this call was not blocked.` };
|
|
244
252
|
}
|
|
@@ -287,7 +295,7 @@ export function whenGateFails(call, err) {
|
|
|
287
295
|
|
|
288
296
|
/**
|
|
289
297
|
* @param {{ tool: string, command?: string, path?: string, cwd?: string }} call
|
|
290
|
-
* @param {{ check: () => { ok: boolean, problems: string[], ageMs?: number|null, limitMs?: number }, recentConsult?: () => boolean, readablePaths?: RegExp[], ownedCommands?: () => { plugin: string, pattern: string, use: string }[], router?: () => ({ plugin: string, pattern: string, use: string } | null), routed?: (receipt: object) => boolean, routeWarned?: (receipt: object) => boolean, sessionAcknowledged?: (receipt: object) => boolean, sessionLapsed?: () => boolean, arrival?: () => (string | null), dispatched?: boolean, mode?: (kind: 'router'|'owned', plugin: string) => string, ownerHealthy?: (plugin: string, use: string) => boolean }} deps
|
|
298
|
+
* @param {{ check: () => { ok: boolean, problems: string[], ageMs?: number|null, limitMs?: number }, recentConsult?: () => boolean, readablePaths?: RegExp[], ownedCommands?: () => { plugin: string, pattern: string, use: string }[], router?: () => ({ plugin: string, pattern: string, use: string } | null), routed?: (receipt: object) => boolean, routeWarned?: (receipt: object) => boolean, sessionAcknowledged?: (receipt: object) => boolean, sessionLapsed?: () => boolean, arrival?: () => (string | null), dispatched?: boolean, harness?: string|null, routerRun?: boolean, selection?: (receipt: object) => ({ harness: string|null, at: number } | null), mode?: (kind: 'router'|'owned', plugin: string) => string, ownerHealthy?: (plugin: string, use: string) => boolean }} deps
|
|
291
299
|
*/
|
|
292
300
|
export function decide(call, deps) {
|
|
293
301
|
try {
|
|
@@ -313,14 +321,26 @@ export function decide(call, deps) {
|
|
|
313
321
|
: `this session has not acknowledged (directory: ${call.cwd || 'unknown'}). A receipt from another agent does not count.`;
|
|
314
322
|
return deny('receipt:session', `TR-PREFLIGHT gate: ${state} ${nextStep(call)}`);
|
|
315
323
|
}
|
|
316
|
-
if (tool === 'shell' &&
|
|
317
|
-
return deny('
|
|
324
|
+
if (tool === 'shell' && isBlockedGrep(call.command)) {
|
|
325
|
+
return deny('grep', 'TR-PREFLIGHT gate: `grep`/`rg`/`ag` is blocked. Use Total Recall OpenWiki (`total-recall openwiki` / `openwiki/`) or `total-recall recall "<topic>" --local` instead.',
|
|
326
|
+
'total-recall openwiki hubs --json');
|
|
327
|
+
}
|
|
328
|
+
if ((tool === 'search' || (tool === 'shell' && isRepoSweep(call.command))) && !(deps.recentConsult ? deps.recentConsult() : false)) {
|
|
329
|
+
return deny('sweep', 'TR-PREFLIGHT gate: do not sweep or search the repository before consulting Total Recall / OpenWiki. Run `total-recall openwiki hubs --json`, `total-recall recall "<topic>" --local`, or `total-recall help "<task>"` first.',
|
|
318
330
|
'total-recall help "<task>"');
|
|
319
331
|
}
|
|
332
|
+
const router = deps.router ? deps.router() : null;
|
|
333
|
+
if (tool === 'write' && deps.harness && !deps.routerRun) {
|
|
334
|
+
const chosen = deps.selection ? deps.selection(receipt) : null;
|
|
335
|
+
if (chosen?.harness && chosen.harness !== deps.harness) {
|
|
336
|
+
const by = router?.plugin || 'the router';
|
|
337
|
+
return deny('route:selected', `TR-PREFLIGHT gate: ${by} selected ${chosen.harness} for this task, so ${deps.harness} may not write here. Only a run ${by} started may write: hand the task over with its --execute form, or route it again.`,
|
|
338
|
+
router?.use || 'route the task again');
|
|
339
|
+
}
|
|
340
|
+
}
|
|
320
341
|
const notes = [];
|
|
321
342
|
const arrival = deps.arrival ? deps.arrival() : null;
|
|
322
343
|
if (arrival) notes.push({ name: 'arrival', warning: arrival });
|
|
323
|
-
const router = deps.router ? deps.router() : null;
|
|
324
344
|
if (router && !deps.dispatched) {
|
|
325
345
|
const open = ['read', 'search', 'web', 'agent'].includes(tool) || (tool === 'shell' && isPlainTotalRecall(call.command));
|
|
326
346
|
if (!open && !(deps.routed ? deps.routed(receipt.receipt) : false)) {
|
|
@@ -339,6 +339,17 @@ export function lastRouteWarnedMs(root, { home = os.homedir(), session = null }
|
|
|
339
339
|
return readStamp(routeWarnedPath(root, home, session));
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
const selectionPath = (root, home) => path.join(stateDir(home), scoped('route-selected', root));
|
|
343
|
+
|
|
344
|
+
/** The harness the router chose for work in this repository, or null when it chose none. */
|
|
345
|
+
export function markRouteSelection(root, harness, { home = os.homedir(), now = Date.now() } = {}) {
|
|
346
|
+
writeBookkeeping(selectionPath(root, home), (target) => fs.writeFileSync(target, JSON.stringify({ at: now, harness: harness || null, root }), { mode: 0o600 }));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function lastRouteSelection(root, { home = os.homedir() } = {}) {
|
|
350
|
+
return newestRecord(selectionPath(root, home));
|
|
351
|
+
}
|
|
352
|
+
|
|
342
353
|
const sessionAckPath = (session, home) => path.join(stateDir(home), `session-ack-${sessionKey(session)}.stamp`);
|
|
343
354
|
const delegatedPath = (root, home) => path.join(stateDir(home), scoped('delegated', root));
|
|
344
355
|
|
|
@@ -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-heading:"Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--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)}h1,h2,h3,h4,h5,h6{font-family:var(--font-heading);letter-spacing:-.025em;color:var(--text-primary);font-weight:700}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{--workspace-right-width:min(480px, calc(100vw - 48px));--workspace-bottom-height:min(520px, 66vh);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;position:fixed;inset:0 auto 0 0;transform:translate(-100%);box-shadow:4px 0 40px #00000040}.sidebar-nav{scrollbar-width:thin;flex-direction:column;flex:1;gap:2px;padding:0 10px;display:flex;overflow-y:auto}.sidebar.open{transform:translate(0)}.sidebar-brains{padding:0 12px 10px}.sidebar-brains .nav-section-label{padding-inline:0}.sidebar-memory{border-top:1px solid var(--border);margin-top:10px}.sidebar-memory summary{cursor:pointer}.sidebar-memory a{margin-inline-start:12px}.sidebar-saved-pages .nav-link{overflow-wrap:anywhere;min-height:44px}.sidebar-handle{z-index:101;border:1px solid var(--border);border-radius:0 var(--radius-md) var(--radius-md) 0;background:var(--bg-secondary);width:36px;min-height:48px;color:var(--text-primary);cursor:pointer;transition:inset-inline-start var(--transition-normal);place-items:center;display:grid;position:fixed;inset:45% auto auto 0}.sidebar-handle[aria-expanded=true]{inset-inline-start:var(--sidebar-width)}.workspace-main{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.workspace-toolbar{border-bottom:1px solid var(--border);justify-content:space-between;align-items:center;gap:12px;padding:8px clamp(12px,3vw,24px);display:flex}.workspace-logo{border-radius:var(--radius-sm)}.workspace-body{flex:1;min-height:0}.workspace-body[hidden]{display:none}.plugin-workspace-page{flex-direction:column;flex:1;gap:16px;width:min(100%,960px);margin-inline:auto;padding:clamp(16px,4vw,28px);display:flex;overflow:auto}.workspace-toolbar .btn{min-height:44px}.account-menu{z-index:80;position:relative}.account-menu>summary{cursor:pointer;list-style:none}.account-menu>summary::-webkit-details-marker{display:none}.account-menu-body{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);width:min(260px,100vw - 24px);max-height:calc(100dvh - 62px);padding:8px;display:grid;position:absolute;inset:calc(100% + 6px) 0 auto auto;overflow:auto}.account-menu-host,.account-menu-health{color:var(--text-secondary);border-top:1px solid var(--border);gap:6px;padding:10px 12px;font-size:12px;display:grid}.sidebar-scrim{z-index:90;background:#0000006b;position:fixed;inset:0}.workspace-edge-handle{z-index:101;border:1px solid var(--border);background:var(--bg-secondary);min-height:44px;color:var(--text-primary);font:600 12px var(--font-sans);cursor:pointer;transition:inset var(--transition-normal);justify-content:center;align-items:center;gap:5px;padding:0 10px;display:inline-flex;position:fixed}.workspace-edge-handle--right{border-radius:var(--radius-md) 0 0 var(--radius-md);inset:45% 0 auto auto}.workspace-edge-handle--right[aria-expanded=true]{right:var(--workspace-right-width)}.workspace-edge-handle--bottom{border-radius:var(--radius-md) var(--radius-md) 0 0;min-width:88px;inset:auto auto 0 50%;transform:translate(-50%)}.workspace-edge-handle--bottom[aria-expanded=true]{bottom:var(--workspace-bottom-height)}.workspace-edge-panel{z-index:100;visibility:hidden;background:var(--bg-primary);min-width:0;min-height:0;color:var(--text-primary);border:1px solid var(--border);transition:transform var(--transition-normal), visibility 0s var(--transition-normal);flex-direction:column;display:flex;position:fixed;overflow:hidden;box-shadow:0 12px 48px #00000073}.workspace-edge-panel--right{width:var(--workspace-right-width);inset:0 0 0 auto;transform:translate(100%)}.workspace-edge-panel--bottom{width:min(100%,1050px);height:var(--workspace-bottom-height);border-radius:var(--radius-lg) var(--radius-lg) 0 0;margin-inline:auto;inset:auto 0 0;transform:translateY(100%)}.workspace-edge-panel.open{visibility:visible;transition:transform var(--transition-normal);transform:translate(0)}.workspace-edge-header,.drawer-row{justify-content:space-between;align-items:center;gap:12px;display:flex}.workspace-edge-header{border-bottom:1px solid var(--border);flex:none;padding:10px 16px}.workspace-edge-header h2{margin:0;font-size:16px;font-weight:650}.workspace-edge-body{flex:1;min-height:0;padding:12px 16px 24px;overflow:auto}.drawer-section-tabs,.drawer-actions{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.drawer-section-tabs{margin-bottom:14px}.drawer-section-tabs [aria-pressed=true]{background:var(--accent-muted);color:var(--text-primary)}.drawer-stack{gap:12px;min-width:0;display:grid}.drawer-stack label{gap:5px;display:grid}.drawer-note{color:var(--text-secondary);font-size:13px}.drawer-error{color:var(--error,#f87171);font-size:13px}.drawer-fields{border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 12px 12px}.drawer-fields legend{color:var(--text-secondary);padding-inline:5px;font-size:12px}.drawer-shortcut-edit{grid-template-columns:minmax(0,1fr) minmax(0,2fr) auto;gap:8px;display:grid}.drawer-code,.drawer-context{font-family:var(--font-mono), monospace;white-space:pre-wrap;overflow-wrap:anywhere}.drawer-code{resize:vertical;width:100%}.drawer-html{border:1px solid var(--border);border-radius:var(--radius-sm);width:100%;height:clamp(160px,35vh,340px);display:block}.help-content{gap:10px;min-width:0;display:grid}.help-topic-picker{color:var(--text-secondary);gap:4px;font-size:13px;display:grid}.help-topic-description{color:var(--text-secondary);font-size:12px}.help-article{overflow-wrap:anywhere;min-width:0}@media (width<=540px){.app-layout{--workspace-right-width:calc(100vw - 48px);--workspace-bottom-height:min(72dvh, 600px)}.workspace-edge-handle--right{padding-inline:5px;font-size:11px}.workspace-edge-handle--right[aria-expanded=true]{font-size:0}.drawer-shortcut-edit{grid-template-columns:minmax(0,1fr)}}@media (prefers-reduced-motion:reduce){.workspace-edge-handle,.workspace-edge-panel,.workspace-edge-panel.open{transition:none}}.plugin-folder-toggle,.plugin-folder-item{text-align:start;cursor:pointer;background:0 0;width:100%;min-height:44px;font-family:inherit}.plugin-folder-chevron{color:var(--text-tertiary);margin-inline-start:auto}.plugin-origin{color:var(--text-tertiary);margin-inline-start:auto;font-size:10px}.plugin-folder-list{margin:0 0 0 16px;padding:0;list-style:none}#plugin-folder>.plugin-folder-item{padding-inline-start:28px}.plugin-folder-message,.plugin-muted{color:var(--text-secondary);font-size:13px}.plugin-folder-message{margin:4px 12px}.plugin-drawer-backdrop{z-index:1000;background:#0000008c;justify-content:flex-end;display:flex;position:fixed;inset:0}.plugin-drawer{background:var(--bg-secondary);border-inline-start:1px solid var(--border);flex-direction:column;gap:16px;width:min(100%,680px);padding:clamp(16px,4vw,24px);display:flex;overflow:auto}.plugin-home,.plugin-home-focus{gap:12px;min-width:0;display:grid}.plugin-home-focus label{gap:6px;display:grid}.plugin-home-operations{gap:2px;padding:0;list-style:none;display:grid}.plugin-home-operations li{border-bottom:1px solid var(--border);flex-wrap:wrap;align-items:center;column-gap:12px;padding:4px 0;display:flex}.plugin-home-operations span{color:var(--text-secondary)}.plugin-drawer-header,.plugin-drawer-actions,.plugin-detail-status dd{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.plugin-drawer-header{justify-content:space-between}.plugin-drawer .btn,.plugin-workspace-page .btn{min-height:44px}.plugin-detail-status{border-block:1px solid var(--border);grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:12px;padding-block:12px;display:grid}.plugin-detail-status dt{color:var(--text-secondary);font-size:12px}.plugin-detail-status dd{min-height:28px;margin:4px 0 0}.plugin-drawer-form{gap:12px;display:grid}.plugin-drawer-form label,.plugin-drawer>label{gap:6px;display:grid}.plugin-command-output{white-space:pre-wrap;overflow-wrap:anywhere;max-height:320px;overflow:auto}@media (prefers-reduced-motion:reduce){.sidebar,.sidebar-handle{transition:none}}.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{-webkit-backdrop-filter:blur(16px);border-radius:var(--radius-md);transition:all var(--transition-normal);background:#121a2ba6;border:1px solid #94a3b81f;padding:22px;box-shadow:0 4px 20px #00000040}.card:hover{background:#1c2740bf;border-color:#60a5fa59;transform:translateY(-1px);box-shadow:0 8px 30px #00000059}.card-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;display:grid}.btn{border-radius:var(--radius-md);min-height:44px;transition:all var(--transition-fast);letter-spacing:-.01em;cursor:pointer;border:1px solid #0000;justify-content:center;align-items:center;gap:8px;padding:0 18px;font-size:13.5px;font-weight:600;display:inline-flex}.btn-primary{color:#fff;background:linear-gradient(135deg,#3b82f6 0%,#2563eb 55%,#1d4ed8 100%);border-color:#ffffff1a;box-shadow:inset 0 1px #fff3,0 4px 14px #2563eb59}.btn-primary:hover{box-shadow:0 0 24px var(--accent-glow), 0 6px 20px #2563eb73;background:linear-gradient(135deg,#60a5fa 0%,#3b82f6 50%,#2563eb 100%);transform:translateY(-1px)}.btn-ghost{color:var(--text-secondary);background:#94a3b814;border:1px solid #94a3b824}.btn-ghost:hover{color:var(--text-primary);background:#94a3b829;border-color:#94a3b847}.btn-sm{border-radius:var(--radius-sm);min-height:36px;padding:0 12px;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:760px;padding:16px 20px;font-size:14px;line-height:1.65;animation:.18s cubic-bezier(.16,1,.3,1) msgIn;position:relative}@keyframes msgIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.message-user{color:#f8fafc;-webkit-backdrop-filter:blur(12px);background:#1e3a8a47;border:1px solid #3b82f659;border-bottom-right-radius:4px;align-self:flex-end;box-shadow:0 4px 18px #00000040}.message-assistant{-webkit-backdrop-filter:blur(12px);color:var(--text-primary);background:#121a2bb8;border:1px solid #94a3b81f;border-bottom-left-radius:4px;align-self:flex-start;box-shadow:0 6px 24px #0000004d}.message-assistant code{background:#070b14bf;border:1px solid #94a3b81a;border-radius:4px;padding:2px 6px;font-size:12.5px}.chat-input-bar{-webkit-backdrop-filter:blur(20px);background:#0c1220d9;border-top:1px solid #94a3b81a;padding:16px 32px 20px}.chat-input-wrapper{align-items:flex-end;gap:10px;max-width:820px;margin:0 auto;display:flex}.chat-input-wrapper textarea{resize:none;border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-sans);min-height:44px;max-height:160px;transition:all var(--transition-fast);background:#121a2ba6;border:1px solid #94a3b824;flex:1;padding:12px 16px;font-size:14px;line-height:1.5}.chat-input-wrapper textarea:focus{background:#121a2be6;border-color:#3b82f680;outline:none;box-shadow:0 0 0 3px #3b82f629}.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:.35;cursor:not-allowed;box-shadow:none;transform:none}.chat-send-btn svg{width:18px;height:18px}.memory-main{flex-direction:column;gap:14px;min-width:0;display:flex}.memory-header,.memory-tabs,.memory-toolbar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.page-header.memory-header{align-items:flex-end;margin-bottom:20px}.memory-tabs{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-secondary);gap:4px;padding:4px}.memory-toolbar input{flex:220px;min-width:0}.memory-toolbar>*{min-height:42px}.memory-list{flex-direction:column;flex:1;gap:10px;display:flex;overflow-y:auto}.memory-node-card{border-radius:var(--radius-md);cursor:pointer;transition:all var(--transition-fast);background:#121a2b99;border:1px solid #94a3b81f;padding:16px 18px;box-shadow:0 2px 8px #0003}.memory-node-card:hover{background:#1c2740b3;border-color:#60a5fa66;transform:translateY(-1px);box-shadow:0 4px 16px #0000004d}.memory-node-card.selected{background:#1e3a8a40;border-color:#3b82f68c;box-shadow:0 0 0 1px #3b82f64d,0 4px 20px #00000059}.memory-node-card h4{color:#f8fafc;margin-bottom:6px;font-size:14px;font-weight:600}.memory-node-card .meta{color:var(--text-tertiary);align-items:center;gap:14px;font-size:12px;display:flex}.memory-detail{-webkit-backdrop-filter:blur(20px);border-radius:var(--radius-lg);background:#0c1220cc;border:1px solid #94a3b824;padding:24px;overflow-y:auto;box-shadow:0 8px 32px #0006}.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){.page{padding:16px}.btn,.nav-link,.memory-toolbar select{touch-action:manipulation;min-height:44px}.chat-input-wrapper textarea,.memory-toolbar 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{-webkit-backdrop-filter:blur(16px);background:#0c1220b3;border-right:1px solid #94a3b81a;flex-direction:column;flex-shrink:0;width:260px;height:100%;display:flex;overflow:hidden}.sidebar-header{border-bottom:1px solid #94a3b81a;padding:14px 16px}.new-chat-btn{color:#93c5fd;border-radius:var(--radius-sm);width:100%;transition:all var(--transition-fast);cursor:pointer;background:linear-gradient(135deg,#3b82f638,#1e3a8a47);border:1px solid #3b82f659;justify-content:center;align-items:center;gap:8px;padding:10px 16px;font-size:13px;font-weight:600;display:flex}.new-chat-btn:hover{color:#fff;box-shadow:0 0 16px var(--accent-glow);background:linear-gradient(135deg,#3b82f659,#1e3a8a73);border-color:#60a5fa80}.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:#94a3b814}.thread-item.active{background:#3b82f61f;border-color:#3b82f64d}.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}}
|