@sigrun/sigrun 0.4.2 → 0.6.0

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 (68) hide show
  1. package/CHANGELOG.md +92 -1
  2. package/README.md +63 -33
  3. package/dist/commands/expand.js +37 -5
  4. package/dist/curation/attribution.js +135 -0
  5. package/dist/curation/deletion.js +32 -0
  6. package/dist/curation/gate.js +50 -0
  7. package/dist/curation/host.js +342 -0
  8. package/dist/curation/internal.js +54 -0
  9. package/dist/curation/names.js +5 -0
  10. package/dist/curation/overlays.js +325 -0
  11. package/dist/curation/paths.js +17 -0
  12. package/dist/curation/run-doc.js +64 -0
  13. package/dist/curation/runs.js +579 -0
  14. package/dist/curation/scan.js +30 -0
  15. package/dist/curation/skill-tool.js +308 -0
  16. package/dist/curation/store.js +444 -0
  17. package/dist/curation/targets.js +77 -0
  18. package/dist/curation/usage.js +124 -0
  19. package/dist/curation/wrap.js +212 -0
  20. package/dist/extension.d.ts +336 -1
  21. package/dist/harness/boot.js +236 -13
  22. package/dist/harness/builtins.js +19 -12
  23. package/dist/harness/event-bus.js +64 -0
  24. package/dist/harness/identity/primary-admin.js +19 -0
  25. package/dist/harness/loader.js +114 -1
  26. package/dist/harness/naming.js +6 -0
  27. package/dist/harness/packages.js +14 -9
  28. package/dist/harness/reserved-sections.js +4 -0
  29. package/dist/harness/seam-host.js +154 -26
  30. package/dist/harness/sections/context-files.js +6 -3
  31. package/dist/harness/sections/job-state.js +24 -0
  32. package/dist/harness/settings.js +2 -2
  33. package/dist/harness/tools.js +70 -0
  34. package/dist/harness/workspace.js +30 -3
  35. package/dist/jobs/fire.js +37 -14
  36. package/dist/jobs/index.js +34 -7
  37. package/dist/jobs/reconcile.js +9 -0
  38. package/dist/jobs/skills-refs.js +40 -0
  39. package/dist/jobs/state-dir.js +16 -0
  40. package/dist/jobs/transfer.js +14 -0
  41. package/dist/sandbox/policy.js +27 -4
  42. package/dist/serve.js +5 -3
  43. package/dist/server/blocks/answers.js +9 -3
  44. package/dist/server/conversation-ops.js +34 -3
  45. package/dist/server/helpers.js +1 -1
  46. package/dist/server/host-bridge.js +31 -1
  47. package/dist/server/notifications.js +108 -16
  48. package/dist/server/routes/jobs.js +31 -0
  49. package/dist/server/routes/meta.js +8 -3
  50. package/dist/server/routes/submissions.js +5 -0
  51. package/dist/server/routes/surfaces.js +3 -1
  52. package/dist/server/routes/users.js +7 -0
  53. package/dist/server/schema-fixture.js +168 -0
  54. package/dist/server/session-meta.js +12 -10
  55. package/dist/settings/core-sections.js +18 -0
  56. package/dist/settings/registry.js +12 -0
  57. package/dist/skills/index.js +88 -19
  58. package/node_modules/@sigrun/shared/CHANGELOG.md +6 -1
  59. package/node_modules/@sigrun/shared/dist/access.d.ts +3 -0
  60. package/node_modules/@sigrun/shared/dist/access.js +8 -0
  61. package/node_modules/@sigrun/shared/dist/session-meta.d.ts +15 -4
  62. package/node_modules/@sigrun/shared/dist/settings.d.ts +7 -5
  63. package/node_modules/@sigrun/shared/package.json +1 -1
  64. package/package.json +9 -7
  65. package/webui/dist/assets/index-BR0di0OG.css +1 -0
  66. package/webui/dist/assets/{index-DWmsAQl2.js → index-BzrA1T4g.js} +5 -5
  67. package/webui/dist/index.html +2 -2
  68. package/webui/dist/assets/index-h7zDY9vD.css +0 -1
@@ -29,10 +29,10 @@
29
29
  * other's fields (the mutation line serializes commits).
30
30
  *
31
31
  * Server-owned fields (`autoName`, `autoNameAttempts`,
32
- * `lastActivityAt`, `jobId`, `deliver`) are written by host code only
33
- * (activity.ts, the A2 auto-namer, fire.ts's job stamp); they are NOT
34
- * patch surface — the strict validator keeps rejecting them as
35
- * `invalid_field`.
32
+ * `lastActivityAt`, `jobId`, `deliver`, `internal`) are written by host
33
+ * code only (activity.ts, the A2 auto-namer, fire.ts's job stamp, the
34
+ * curator's creating commit); they are NOT patch surface — the strict
35
+ * validator keeps rejecting them as `invalid_field`.
36
36
  */
37
37
  import { defineDoc } from "@earendil-works/pi-durable";
38
38
  import { isSharing } from "@sigrun/shared/access";
@@ -158,12 +158,14 @@ export function validateSessionMetaPatch(body) {
158
158
  case "source":
159
159
  case "owner":
160
160
  case "jobId":
161
- case "deliver": {
162
- // Server-only (spec §5.9 + gateway C7): the KEY itself is the
163
- // client error, whatever the value — even a `null` clear. The
164
- // job stamp fields move with the fire (fire.ts's creating
165
- // commit) and the delivery targets ride the job spec, never a
166
- // meta patch.
161
+ case "deliver":
162
+ case "internal": {
163
+ // Server-only (spec §5.9 + gateway C7 + curator P5): the KEY
164
+ // itself is the client error, whatever the value — even a `null`
165
+ // clear. The job stamp fields move with the fire (fire.ts's
166
+ // creating commit) and the delivery targets ride the job spec,
167
+ // never a meta patch; the internal-run marker moves with the
168
+ // curator's creating commit (markInternal) alone.
167
169
  return { error: "invalid_field", field: key };
168
170
  }
169
171
  case "sharing": {
@@ -496,6 +496,15 @@ export const CORE_SETTINGS_SECTIONS = [
496
496
  help: "The built-in tool-search extension — applies at restart",
497
497
  apply: applyOf("builtinExtensions.toolSearch"),
498
498
  },
499
+ {
500
+ type: "toggle",
501
+ // Absent means ON (the built-in set defaults enabled).
502
+ default: true,
503
+ name: "builtinExtensions.gateway",
504
+ label: "Gateway",
505
+ help: "The built-in channel gateway engine — applies at restart",
506
+ apply: applyOf("builtinExtensions.gateway"),
507
+ },
499
508
  {
500
509
  type: "toggle",
501
510
  default: true,
@@ -504,6 +513,15 @@ export const CORE_SETTINGS_SECTIONS = [
504
513
  help: "The built-in OIDC/OAuth2 login extension — applies at restart",
505
514
  apply: applyOf("builtinExtensions.oauth"),
506
515
  },
516
+ {
517
+ type: "toggle",
518
+ // Absent means ON (the built-in set defaults enabled).
519
+ default: true,
520
+ name: "builtinExtensions.curator",
521
+ label: "Curator",
522
+ help: "The built-in curator extension — applies at restart",
523
+ apply: applyOf("builtinExtensions.curator"),
524
+ },
507
525
  ],
508
526
  },
509
527
  ],
@@ -169,12 +169,24 @@ export const CORE_SETTING_KEYS = [
169
169
  section: "extensions",
170
170
  reason: "the built-in set resolves at boot (a disabled built-in never loads)",
171
171
  },
172
+ {
173
+ key: "builtinExtensions.gateway",
174
+ apply: "restart",
175
+ section: "extensions",
176
+ reason: "the built-in set resolves at boot (a disabled built-in never loads)",
177
+ },
172
178
  {
173
179
  key: "builtinExtensions.oauth",
174
180
  apply: "restart",
175
181
  section: "extensions",
176
182
  reason: "the built-in set resolves at boot (a disabled built-in never loads)",
177
183
  },
184
+ {
185
+ key: "builtinExtensions.curator",
186
+ apply: "restart",
187
+ section: "extensions",
188
+ reason: "the built-in set resolves at boot (a disabled built-in never loads)",
189
+ },
178
190
  {
179
191
  key: "autoEnableCodemode",
180
192
  apply: "live",
@@ -3,16 +3,20 @@
3
3
  * package.
4
4
  *
5
5
  * v1 discovered ONE root: `skills/<dir>/SKILL.md` in the conversation
6
- * workspace. W5 Task 4 (spec §9.4) grows discovery to THREE roots, with
7
- * first-wins precedence in this order:
6
+ * workspace. W5 Task 4 (spec §9.4) grew discovery to THREE roots, and
7
+ * curator A3 (P2) adds a fourth — the conversation OWNER's learned
8
+ * skills. First-wins precedence in this order:
8
9
  *
9
10
  * 1. the WORKSPACE root — `skills/` under the conversation workspace, read
10
11
  * through the conversation `ExecutionEnv` (the section via
11
12
  * `PromptInput.env`, the tool via `ToolExecutionApi.env`), so discovery
12
13
  * follows the resolved workspace;
13
- * 2. the USER root — `<configDir>/skills/` (a named config-root entry),
14
+ * 2. the LEARNED root — the owner's `<dataDir>/users/<owner>/skills/`,
15
+ * read from the host filesystem; only for OWNED conversations (an
16
+ * ownerless conversation has no learned root);
17
+ * 3. the USER root — `<configDir>/skills/` (a named config-root entry),
14
18
  * read from the host filesystem;
15
- * 3. PACKAGE roots — skill directories shipped by extension packages
19
+ * 4. PACKAGE roots — skill directories shipped by extension packages
16
20
  * (manifest `sigrun.skills`/`pi.skills` dirs and factory-result `skills`
17
21
  * dirs, collected by the loader in install order), read from the host
18
22
  * filesystem.
@@ -60,6 +64,21 @@ import { Type } from "@earendil-works/pi-ai";
60
64
  import { defineExtension, defineTool, section, } from "@earendil-works/pi-durable";
61
65
  import { readdirSync, readFileSync } from "node:fs";
62
66
  import { join } from "node:path";
67
+ import { overlayForSkill, resolveUpstream, withOverlay } from "../curation/overlays.js";
68
+ import { buildSkillManageTool } from "../curation/skill-tool.js";
69
+ import { SessionMetaDoc } from "../server/session-meta.js";
70
+ /**
71
+ * Resolve a conversation's owner through any `DocumentReader` (curator
72
+ * A3): the section's `PromptInput.read` and the tool's `ToolExecutionApi`
73
+ * both carry one. `undefined` for an ownerless conversation — and for a
74
+ * bare test double without the reader surface (no learned root then).
75
+ */
76
+ async function conversationOwner(read, conversationId, context) {
77
+ if (typeof read?.snapshot !== "function")
78
+ return undefined;
79
+ const meta = await read.snapshot(SessionMetaDoc, conversationId, context);
80
+ return meta?.owner;
81
+ }
63
82
  /** The section key the skills list renders under. */
64
83
  export const SKILLS_SECTION_KEY = "skills";
65
84
  /** Total rendered-section budget (spec §3.2: 4KB, notice included). */
@@ -68,8 +87,8 @@ const SKILLS_DIRECTORY = "skills";
68
87
  const SKILL_FILE = "SKILL.md";
69
88
  const FRONTMATTER_DELIMITER = "---";
70
89
  const SKILLS_SECTION_HEADER = "Workspace skills. Call the load_skill tool with a skill's name to read its full instructions; each entry shows the skill's path, so you can also read its SKILL.md directly.";
71
- /** The three discovery roots, in precedence order (§9.4). */
72
- const ROOT_PRECEDENCE = ["workspace", "user dir", "package"];
90
+ /** The discovery roots, in precedence order (§9.4 + curator P2). */
91
+ const ROOT_PRECEDENCE = ["workspace", "learned", "user dir", "package"];
73
92
  const encoder = new TextEncoder();
74
93
  /** UTF-8 byte length — the unit the section cap is measured in. */
75
94
  function byteLength(text) {
@@ -129,7 +148,7 @@ function parseSkillFile(text) {
129
148
  * rules with a log. `displayDir` is the path named in the logs and the
130
149
  * section entry; `fileLabel` names the file in skip logs.
131
150
  */
132
- function skillFromFile(text, displayDir, fileLabel) {
151
+ function skillFromFile(text, displayDir, fileLabel, root) {
133
152
  const parsed = parseSkillFile(text);
134
153
  if (parsed === undefined) {
135
154
  console.warn(`skills: skipping ${fileLabel} (the frontmatter block is required)`);
@@ -149,6 +168,7 @@ function skillFromFile(text, displayDir, fileLabel) {
149
168
  body: parsed.body,
150
169
  dir: displayDir,
151
170
  disableModelInvocation: parsed.disableModelInvocation,
171
+ root,
152
172
  };
153
173
  }
154
174
  /** Directory-name comparison (plain code-point order, locale-independent). */
@@ -179,7 +199,7 @@ async function readSkill(env, dir, context) {
179
199
  cause: read.error,
180
200
  });
181
201
  }
182
- return skillFromFile(read.value, `${SKILLS_DIRECTORY}/${dir.name}`, relative);
202
+ return skillFromFile(read.value, `${SKILLS_DIRECTORY}/${dir.name}`, relative, "workspace");
183
203
  }
184
204
  /**
185
205
  * Scan the workspace's skills through the conversation `ExecutionEnv`.
@@ -236,7 +256,7 @@ export async function scanSkills(env, context, opts) {
236
256
  * `disable-model-invocation: true` skills (the `listSkills` path); the
237
257
  * default hides them with a log, exactly like the workspace root.
238
258
  */
239
- function scanFsRoot(root, opts) {
259
+ export function scanFsRoot(root, rootLabel, opts) {
240
260
  let entries;
241
261
  try {
242
262
  entries = readdirSync(root, { withFileTypes: true });
@@ -263,7 +283,7 @@ function scanFsRoot(root, opts) {
263
283
  }
264
284
  throw new Error(`Failed to read ${file}: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
265
285
  }
266
- const skill = skillFromFile(text, join(root, entry.name), file);
286
+ const skill = skillFromFile(text, join(root, entry.name), file, rootLabel);
267
287
  if (skill === undefined)
268
288
  continue;
269
289
  if (skill.disableModelInvocation && opts?.includeHidden !== true) {
@@ -320,9 +340,15 @@ export async function discoverSkills(env, context, opts, options) {
320
340
  if (env !== undefined) {
321
341
  merge(await scanSkills(env, context, options), "workspace");
322
342
  }
323
- merge(scanFsRoot(opts.userDir, options), "user dir");
343
+ // The learned root (curator P2): the owner's own skills, between the
344
+ // workspace and the deployment's user dir. Ownerless → none.
345
+ const learnedRoot = opts.learnedRoot?.(options?.owner);
346
+ if (learnedRoot !== undefined) {
347
+ merge(scanFsRoot(learnedRoot, "learned", options), "learned");
348
+ }
349
+ merge(scanFsRoot(opts.userDir, "user dir", options), "user dir");
324
350
  for (const root of opts.packageRoots()) {
325
- merge(scanFsRoot(root.dir, options), "package", root.extension);
351
+ merge(scanFsRoot(root.dir, "package", options), "package", root.extension);
326
352
  }
327
353
  return [...byName.values()].map((entry) => entry.skill).sort(bySkillName);
328
354
  }
@@ -333,8 +359,11 @@ export async function discoverSkills(env, context, opts, options) {
333
359
  * them while the section and `load_skill` keep them hidden. Read LIVE
334
360
  * per call (the same posture as the section's per-render scan).
335
361
  */
336
- export async function listSkills(env, context, opts) {
337
- return discoverSkills(env, context, opts, { includeHidden: true });
362
+ export async function listSkills(env, context, opts, options) {
363
+ return discoverSkills(env, context, opts, {
364
+ includeHidden: true,
365
+ ...(options?.owner === undefined ? {} : { owner: options.owner }),
366
+ });
338
367
  }
339
368
  /**
340
369
  * Assemble the section text: the header plus one `- name: description
@@ -373,9 +402,10 @@ export function renderSkillList(skills, limitBytes) {
373
402
  // rendering — it says skills exist and how many were cut).
374
403
  return `(skill list truncated: ${lines.length} more not shown — call load_skill by name)`;
375
404
  }
376
- /** Section render: discover all three roots, list the winning skills. */
405
+ /** Section render: discover all roots, list the winning skills. */
377
406
  async function renderSkills(input, context, opts) {
378
- const skills = await discoverSkills(input.env, context, opts);
407
+ const owner = await conversationOwner(input.read, input.conversationId, context);
408
+ const skills = await discoverSkills(input.env, context, opts, { owner });
379
409
  return renderSkillList(skills, opts.sectionMaxBytes());
380
410
  }
381
411
  /** An explicit tool error: text content the model can read and recover from. */
@@ -393,7 +423,8 @@ function buildLoadSkillTool(opts) {
393
423
  }),
394
424
  }),
395
425
  async execute(args, api, context) {
396
- const skills = await discoverSkills(api.env, context, opts);
426
+ const owner = await conversationOwner(api, api.conversationId, context);
427
+ const skills = await discoverSkills(api.env, context, opts, { owner });
397
428
  const skill = skills.find((candidate) => candidate.name === args.name);
398
429
  if (skill === undefined) {
399
430
  if (skills.length === 0) {
@@ -402,7 +433,19 @@ function buildLoadSkillTool(opts) {
402
433
  const available = skills.map((candidate) => candidate.name).join(", ");
403
434
  return skillError(`load_skill: unknown skill "${args.name}"; available skills: ${available}`);
404
435
  }
405
- api.output(skill.body);
436
+ // Curator A5 (P2): an overlay for this owner rides on the upstream
437
+ // body — upstream, then the heading, then the overlay text. The
438
+ // section is unchanged (the skill lists once, plain).
439
+ const overlay = await overlayForSkill(opts, owner, skill, api.env);
440
+ api.output(overlay === undefined ? skill.body : withOverlay(skill.body, overlay));
441
+ // Curator A6 (P4): a learned-root skill load counts as usage.
442
+ if (skill.root === "learned" && owner !== undefined) {
443
+ const usage = opts.usage?.(owner);
444
+ if (usage !== undefined) {
445
+ usage.bump(skill.name, "load");
446
+ void usage.flush();
447
+ }
448
+ }
406
449
  return {};
407
450
  },
408
451
  });
@@ -419,6 +462,32 @@ export function createSkillsExtension(opts) {
419
462
  sections: [
420
463
  section(SKILLS_SECTION_KEY, (input, context) => renderSkills(input, context, opts)),
421
464
  ],
422
- tools: [buildLoadSkillTool(opts)],
465
+ tools: [
466
+ buildLoadSkillTool(opts),
467
+ // Curator A6 (P3): the skill_manage tool installs once the options
468
+ // carry the curation wiring (a skill-store seam) — the read actions
469
+ // for every owned conversation, the mutating actions for curator
470
+ // runs. The overlay actions resolve the CURRENT upstream through
471
+ // A5's machinery over the same discovery roots (the final review
472
+ // fix wave: hashing the learned root stored `""` for every overlay).
473
+ ...(opts.skillStore === undefined
474
+ ? []
475
+ : [
476
+ buildSkillManageTool({
477
+ storesOf: (user) => {
478
+ const store = opts.skillStore?.(user);
479
+ const overlays = opts.overlays?.(user);
480
+ return store === undefined || overlays === undefined
481
+ ? undefined
482
+ : { store, overlays };
483
+ },
484
+ upstreamOf: (key, env, ctx) => resolveUpstream(key, env, ctx, {
485
+ userDir: opts.userDir,
486
+ packageRoots: opts.packageRoots,
487
+ }),
488
+ workspaceOf: (env) => opts.workspaceRoot?.() ?? env?.cwd,
489
+ }),
490
+ ]),
491
+ ],
423
492
  });
424
493
  }
@@ -5,8 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.6.0] - 2026-11-02
9
9
 
10
+ Version-companion release for sigrun 0.6.0 (no shared changes).
11
+ ## [0.5.0] - 2026-11-02
12
+
13
+ Version-companion release for sigrun 0.5.0 (no shared changes).
10
14
  ## [0.4.2] - 2026-10-31
11
15
 
12
16
  ### Fixed
@@ -60,3 +64,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
60
64
  conversation/turn shaping, the channel wire types, session-meta,
61
65
  commands, interactions, ambient records, access, settings, views,
62
66
  view-delta, and jobs — the surfaces the webui, TUI, and server share.
67
+
@@ -9,6 +9,8 @@
9
9
  *
10
10
  * The rules, in order (first match wins):
11
11
  *
12
+ * 0. internal resource → none (curator P5: a conversation created for
13
+ * curator work is invisible to EVERY caller — the superuser included);
12
14
  * 1. superuser → write;
13
15
  * 2. ownerless resource → household write (everyone);
14
16
  * 3. user === owner → write;
@@ -48,6 +50,7 @@ export interface AccessPrincipal {
48
50
  export declare function accessOf(p: AccessPrincipal, r: {
49
51
  readonly owner?: string;
50
52
  readonly sharing?: Sharing;
53
+ readonly internal?: true;
51
54
  }): Access;
52
55
  /**
53
56
  * Type guard for the wire value: every scope shape checked strictly
@@ -9,6 +9,8 @@
9
9
  *
10
10
  * The rules, in order (first match wins):
11
11
  *
12
+ * 0. internal resource → none (curator P5: a conversation created for
13
+ * curator work is invisible to EVERY caller — the superuser included);
12
14
  * 1. superuser → write;
13
15
  * 2. ownerless resource → household write (everyone);
14
16
  * 3. user === owner → write;
@@ -26,6 +28,12 @@
26
28
  * module doc; the first matching rule wins.
27
29
  */
28
30
  export function accessOf(p, r) {
31
+ // 0. Internal (curator P5): invisible to everyone, the superuser
32
+ // included — BEFORE any other rule can widen access. The marker is
33
+ // server-owned (written once in the creating commit), so no caller,
34
+ // share, or role can ever open an internal resource.
35
+ if (r.internal === true)
36
+ return "none";
29
37
  // 1. The superuser always writes.
30
38
  if (p.superuser === true)
31
39
  return "write";
@@ -25,10 +25,10 @@ export type { Sharing } from "./access.js";
25
25
  * `invalid_field`.
26
26
  *
27
27
  * Server-owned fields (`autoName`, `autoNameAttempts`,
28
- * `lastActivityAt`, `jobId`, `deliver`) are written by host code only
29
- * (activity.ts, the A2 auto-namer, fire.ts's job stamp); they are NOT
30
- * patch surface — the strict validator keeps rejecting them as
31
- * `invalid_field`.
28
+ * `lastActivityAt`, `jobId`, `deliver`, `internal`) are written by host
29
+ * code only (activity.ts, the A2 auto-namer, fire.ts's job stamp, the
30
+ * curator's creating commit); they are NOT patch surface — the strict
31
+ * validator keeps rejecting them as `invalid_field`.
32
32
  */
33
33
  /** The per-conversation session metadata (spec §5). */
34
34
  export type SessionMeta = {
@@ -85,6 +85,17 @@ export type SessionMeta = {
85
85
  * strict validator rejects the key as `invalid_field`.
86
86
  */
87
87
  deliver?: string[];
88
+ /**
89
+ * Server-owned internal-run marker (curator P5, spec §6.1): true on a
90
+ * conversation created for curator work — hidden from every list and
91
+ * view, 404 on every `:id` route for every caller (superuser
92
+ * included), never named, never notified, never idle-clocked, and
93
+ * absent from every event surface. Written ONCE by the curator's
94
+ * creating commit (`markInternal`); the flag never changes after
95
+ * creation. Never patchable — the strict validator rejects the key
96
+ * as `invalid_field`.
97
+ */
98
+ internal?: true;
88
99
  /**
89
100
  * The §5.9 per-resource sharing state; absent = private (owner and
90
101
  * superuser only). Patchable by the OWNER or a superuser (403
@@ -303,11 +303,11 @@ export interface NotificationsSettings {
303
303
  readonly runSettled?: "unwatched" | "cron" | "always" | "never";
304
304
  }
305
305
  /**
306
- * The built-in extensions toggles: codemode, MCP, tool search and OAuth
307
- * ship WITH core. Each key defaults to `true`; `false`
308
- * removes the built-in entirely (absent from `/v1/extensions`, its config
309
- * ignored with one log line). RESTART-REQUIRED: the built-in set resolves
310
- * at boot, like `extensions`.
306
+ * The built-in extensions toggles: codemode, MCP, tool search, the
307
+ * gateway, OAuth and the curator ship WITH core. Each key defaults to
308
+ * `true`; `false` removes the built-in entirely (absent from
309
+ * `/v1/extensions`, its config ignored with one log line).
310
+ * RESTART-REQUIRED: the built-in set resolves at boot, like `extensions`.
311
311
  */
312
312
  export interface BuiltinExtensionsSettings {
313
313
  readonly codemode?: boolean;
@@ -317,6 +317,8 @@ export interface BuiltinExtensionsSettings {
317
317
  readonly gateway?: boolean;
318
318
  /** OAuth login providers (multi-user M3b), default true. */
319
319
  readonly oauth?: boolean;
320
+ /** Curator (Task B1): the per-user curation loops, default true. */
321
+ readonly curator?: boolean;
320
322
  }
321
323
  /**
322
324
  * The persisted shape: HarnessSettings pass-throughs + sigrun fields.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigrun/shared",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "description": "sigrun shared leaf package \u2014 theme tokens, turn/conversation shaping, and the channel wire types (pure leaf, zero dependencies)",
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sigrun/sigrun",
3
- "version": "0.4.2",
4
- "description": "Durable personal assistant on pi-durable",
3
+ "version": "0.6.0",
4
+ "description": "Self-hosted, multi-user personal-assistant server",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./dist/index.js",
@@ -45,11 +45,12 @@
45
45
  "@earendil-works/pi-tui": "^1.3.0",
46
46
  "@fastify/static": "^10.1.7",
47
47
  "@sigrun/ext-codemode": "0.1.2",
48
- "@sigrun/ext-gateway": "0.1.7",
48
+ "@sigrun/ext-curator": "0.1.0",
49
+ "@sigrun/ext-gateway": "0.1.10",
49
50
  "@sigrun/ext-mcp": "0.2.2",
50
51
  "@sigrun/ext-tool-search": "0.1.2",
51
52
  "@sigrun/oauth": "0.1.1",
52
- "@sigrun/shared": "0.4.2",
53
+ "@sigrun/shared": "0.6.0",
53
54
  "croner": "^10.0.1",
54
55
  "fastify": "^5.12.7",
55
56
  "semver": "^7.8.5",
@@ -60,11 +61,12 @@
60
61
  "@sigrun/shared"
61
62
  ],
62
63
  "optionalDependencies": {
63
- "@sigrun/sandbox-linux-arm64": "0.4.2",
64
- "@sigrun/sandbox-linux-x64": "0.4.2",
65
- "@sigrun/tui": "0.4.2"
64
+ "@sigrun/sandbox-linux-arm64": "0.6.0",
65
+ "@sigrun/sandbox-linux-x64": "0.6.0",
66
+ "@sigrun/tui": "0.6.0"
66
67
  },
67
68
  "devDependencies": {
69
+ "@sigrun/memory-markdown": "file:../sigrun-memory-markdown",
68
70
  "@types/node": "^26.6.4",
69
71
  "@types/semver": "^7.8.0",
70
72
  "fast-check": "^4.10.2",
@@ -0,0 +1 @@
1
+ .blk{min-width:0;margin:0}.blk-width{width:100%;container-type:inline-size}.blk-icon{vertical-align:-.15em;flex:none}.blk-unsupported{border:1px dashed var(--border);color:var(--muted);border-radius:8px;padding:.75rem 1rem;font-size:.9em}.blk-unsupported-note{color:var(--faint);margin:.25rem 0 0;font-size:.85em}.blk-stack{flex-direction:column;display:flex}.blk-columns{gap:1rem;display:flex}.blk-columns[data-stacked=true]{flex-direction:column}.blk-column{min-width:0}.blk-grid{gap:1rem;display:grid}.blk-grid-item[data-span=full]{grid-column:1/-1}.blk-section{border:1px solid var(--border);background:var(--surface);border-radius:10px;padding:.9rem 1.1rem}.blk-section-head{flex-wrap:wrap;align-items:baseline;gap:.25rem .75rem;margin-bottom:.6rem;display:flex}.blk-section-title{color:var(--text);font-size:.95em;font-weight:600}.blk-section-subtitle{color:var(--faint);font-size:.85em}.blk-section-toggle{color:inherit;cursor:pointer;font:inherit;background:0 0;border:0;align-items:center;gap:.4rem;padding:0;display:flex}.blk-section-caret{color:var(--faint);width:1em}.blk-markdown .md{font-size:.95em}.blk-fields dl{gap:.35rem;margin:0;display:grid}.blk-field-row{justify-content:space-between;gap:1rem;display:flex}.blk-field-row dt{color:var(--muted);flex:none}.blk-field-row dd{overflow-wrap:anywhere;text-align:end;gap:.4rem;margin:0;display:flex}.blk-stat{flex-direction:column;gap:.15rem;display:flex}.blk-stat[data-size=s]{font-size:.9em}.blk-stat[data-size=l]{font-size:1.15em}.blk-stat-top{color:var(--muted);align-items:center;gap:.4rem;font-size:.85em;display:flex}.blk-stat-value{font-variant-numeric:tabular-nums;font-size:1.6em;font-weight:650;line-height:1.2}.blk-stat[data-size=s] .blk-stat-value{font-size:1.3em}.blk-stat[data-size=l] .blk-stat-value{font-size:2em}.blk-stat-unit{color:var(--muted);font-size:.6em;font-weight:400}.blk-stat-caption{color:var(--faint);font-size:.8em}.blk-spark{color:var(--muted);width:100%;height:24px;margin-top:.25rem;display:block}.blk-callout{border-left:3px solid var(--muted);border-radius:0 8px 8px 0;padding:.6rem .9rem}.blk-callout[data-tone=accent]{border-left-color:var(--accent);background:var(--accent-bg)}.blk-callout[data-tone=good]{border-left-color:var(--ok);background:color-mix(in srgb, var(--ok) 10%, transparent)}.blk-callout[data-tone=warn]{border-left-color:var(--warn);background:color-mix(in srgb, var(--warn) 12%, transparent)}.blk-callout[data-tone=bad]{border-left-color:var(--danger);background:color-mix(in srgb, var(--danger) 10%, transparent)}.blk-callout[data-tone=info]{border-left-color:var(--muted);background:var(--code)}.blk-callout-title{font-weight:650}.blk-callout-body{margin-top:.25rem;font-size:.9em}.blk-image{border-radius:8px;max-width:100%;height:auto}.blk-image-fallback{border:1px dashed var(--border);color:var(--muted);border-radius:8px;padding:.75rem;font-size:.9em}.blk-diff-path{color:var(--faint);font-family:var(--mono);margin-bottom:.35rem;font-size:.8em}.blk-diff-panes{border-radius:8px;grid-template-columns:1fr 1fr;gap:.5rem;display:grid;overflow-x:auto}.blk-diff-panes pre{background:var(--code);font-family:var(--mono);white-space:pre-wrap;border-radius:6px;margin:0;padding:.6rem .75rem;font-size:.82em}.blk-diff-after{border:1px solid color-mix(in srgb, var(--ok) 35%, transparent)}.blk-diff-before{border:1px solid color-mix(in srgb, var(--danger) 35%, transparent)}@container (width<=40rem){.blk-diff-panes{grid-template-columns:1fr}}.blk-details summary{color:var(--muted);cursor:pointer;font-size:.9em}.blk-details-json{background:var(--code);font-family:var(--mono);white-space:pre-wrap;border-radius:6px;margin:.5rem 0 0;padding:.6rem .75rem;font-size:.82em}:root{--chart-1:var(--accent);--chart-2:#6fb3e0;--chart-3:#86c79b;--chart-4:#d9a35a;--chart-5:#b48ead;--chart-6:#e08f7a;--chart-7:#7fc7c7;--chart-8:#a9b665}[data-mode=light]{--chart-2:#3e78a8;--chart-3:#4a8a5f;--chart-4:#8a6116;--chart-5:#83619c;--chart-6:#b35c42;--chart-7:#3d8f8f;--chart-8:#6f7a2f}.blk-chart{flex-direction:column;gap:.35rem;min-width:0;display:flex}.blk-chart-head{flex-wrap:wrap;align-items:baseline;gap:.25rem .75rem;display:flex}.blk-chart-title{color:var(--muted);font-size:.85em}.blk-chart-legend{flex-wrap:wrap;gap:.25rem .9rem;margin-left:auto;display:flex}.blk-chart-legend[data-legend=below]{margin-left:0}.blk-chart-legend-item{color:var(--muted);align-items:center;gap:.35rem;font-size:.8em;display:inline-flex}.blk-chart-swatch{border-radius:2px;width:.9em;height:.6em;display:inline-block}.blk-chart-svg{width:100%;display:block}.blk-chart-grid{stroke:var(--border);stroke-dasharray:2 4;stroke-width:1px}.blk-chart-tick{fill:var(--faint);font-size:11px}.blk-chart-tick-line{stroke:var(--faint);stroke-width:1px}.blk-chart-y-caption{font-size:10px}.blk-chart-threshold{stroke-dasharray:6 4;stroke-width:1.5px}.blk-chart-threshold[data-tone=neutral]{stroke:var(--text)}.blk-chart-threshold[data-tone=accent]{stroke:var(--accent)}.blk-chart-threshold[data-tone=good]{stroke:var(--ok)}.blk-chart-threshold[data-tone=warn]{stroke:var(--warn)}.blk-chart-threshold[data-tone=bad]{stroke:var(--danger)}.blk-chart-threshold[data-tone=info]{stroke:var(--muted)}.blk-chart-threshold-label{font-size:10px}.blk-chart-threshold-label[data-tone=neutral]{fill:var(--text)}.blk-chart-threshold-label[data-tone=accent]{fill:var(--accent)}.blk-chart-threshold-label[data-tone=good]{fill:var(--ok)}.blk-chart-threshold-label[data-tone=warn]{fill:var(--warn)}.blk-chart-threshold-label[data-tone=bad]{fill:var(--danger)}.blk-chart-threshold-label[data-tone=info]{fill:var(--muted)}.blk-chart-spark-row{align-items:baseline;gap:.5rem;display:flex}.blk-chart-spark{flex:1;min-width:0;height:24px}.blk-chart-spark-name{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;flex:none;font-size:.8em;overflow:hidden}.blk-chart-spark-value{font-variant-numeric:tabular-nums;flex:none;font-size:.9em;font-weight:650}.blk-meter{flex-direction:column;gap:.3rem;display:flex}.blk-meter-head{justify-content:space-between;gap:.75rem;display:flex}.blk-meter-label{color:var(--muted);font-size:.85em}.blk-meter-value{font-variant-numeric:tabular-nums;font-size:.9em}.blk-meter-track{background:var(--code);border-radius:999px;width:100%;height:8px;overflow:hidden}.blk-meter-fill{background:var(--muted);border-radius:999px;height:100%;transition:width .2s}.blk-meter[data-tone=accent] .blk-meter-fill{background:var(--accent)}.blk-meter[data-tone=good] .blk-meter-fill{background:var(--ok)}.blk-meter[data-tone=warn] .blk-meter-fill{background:var(--warn)}.blk-meter[data-tone=bad] .blk-meter-fill{background:var(--danger)}.blk-meter[data-tone=info] .blk-meter-fill{background:var(--muted)}.blk-log-lines{background:var(--code);font-family:var(--mono);border-radius:8px;max-height:20rem;padding:.55rem .75rem;font-size:.82em;line-height:1.45;overflow-y:auto}.blk-log-line{gap:.6rem;display:flex}.blk-log-line[data-wrap=true] .blk-log-text{white-space:pre-wrap}.blk-log-time{color:var(--faint);flex:none;font-size:.9em}.blk-log-level{text-transform:uppercase;flex:none;min-width:3.5em;font-size:.85em;font-weight:650}.blk-log-text{white-space:pre}.blk-tree{flex-direction:column;display:flex}.blk-tree-row{cursor:pointer;border-radius:6px;align-items:center;gap:.35rem;padding:.15rem .25rem;display:flex}.blk-tree-row:hover{background:var(--code)}.blk-tree-toggle{color:var(--faint);cursor:pointer;font:inherit;background:0 0;border:0;flex:none;justify-content:center;align-items:center;width:1.1rem;padding:0;display:inline-flex}.blk-tree-toggle:disabled{cursor:default}.blk-tree-caret{transition:transform .15s}.blk-tree-caret[data-open=true]{transform:rotate(90deg)}.blk-tree-dot{width:3px;height:3px;display:inline-block}.blk-tree-label{overflow-wrap:anywhere}.blk-tree-children{flex-direction:column;display:flex}.blk-tree-pending{color:var(--faint);padding:.15rem .25rem}.blk-actions{flex-wrap:wrap;gap:.5rem;margin-top:.5rem;display:flex}.blk-action{background:var(--surface-2);border:1px solid var(--border);color:var(--text);cursor:pointer;font:inherit;border-radius:8px;align-items:center;gap:.4rem;padding:.3rem .7rem;font-size:.85em;display:inline-flex}.blk-action:hover{border-color:var(--accent)}.blk-confirm{background:var(--surface);border:1px solid var(--border);border-radius:10px;max-width:24rem;margin-top:.5rem;padding:.8rem 1rem}.blk-confirm[data-tone=bad]{border-color:color-mix(in srgb, var(--danger) 50%, var(--border))}.blk-confirm[data-tone=warn]{border-color:color-mix(in srgb, var(--warn) 50%, var(--border))}.blk-confirm-title{font-weight:650}.blk-confirm-body{color:var(--muted);margin-top:.3rem;font-size:.9em}.blk-confirm-row{justify-content:flex-end;gap:.5rem;margin-top:.7rem;display:flex}.blk-confirm-row button{background:var(--surface-2);border:1px solid var(--border);color:var(--text);cursor:pointer;font:inherit;border-radius:8px;padding:.3rem .8rem;font-size:.85em}.blk-confirm-row .blk-confirm-ok{background:var(--accent-bg);border-color:var(--accent)}.blk-card{background:var(--surface-2);border:1px solid var(--border);border-radius:10px;gap:.6rem;min-width:0;display:flex;position:relative;overflow:hidden}.blk-card-stripe{width:3px;position:absolute;top:0;bottom:0;left:0}.blk-card-image[data-layout=top]{aspect-ratio:16/9;object-fit:cover;width:100%;height:auto;display:block}.blk-card-image[data-layout=thumb]{object-fit:cover;border-radius:8px;align-self:flex-start;width:3rem;height:3rem;margin:.6rem 0 .6rem .6rem}.blk-card:has(.blk-card-image[data-layout=top]){flex-direction:column}.blk-card-main{flex-direction:column;flex:1;gap:.35rem;min-width:0;padding:.6rem .7rem;display:flex}.blk-card-head{align-items:flex-start;gap:.5rem;min-width:0;display:flex}.blk-card-icon{color:var(--muted);margin-top:.1rem;display:inline-flex}.blk-card-titles{flex:1;min-width:0}.blk-card-eyebrow{color:var(--muted);overflow-wrap:anywhere;font-size:.78em}.blk-card-title{overflow-wrap:anywhere;font-weight:600}.blk-card-subtitle{color:var(--muted);overflow-wrap:anywhere;text-overflow:ellipsis;white-space:nowrap;font-size:.88em;overflow:hidden}.blk-card-body{min-width:0;font-size:.9em}.blk-card-body[data-clamped]{-webkit-line-clamp:4;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.blk-card-badges,.blk-card-meta,.blk-card-metrics{flex-wrap:wrap;gap:.4rem;display:flex}.blk-card-badge{background:var(--accent-bg);border-radius:999px;padding:.05rem .5rem;font-size:.75em}.blk-card-badge[data-tone=good]{color:var(--ok)}.blk-card-badge[data-tone=warn]{color:var(--warn)}.blk-card-badge[data-tone=bad]{color:var(--danger)}.blk-card-badge[data-tone=neutral]{color:var(--text)}.blk-card-badge[data-tone=info]{color:var(--muted)}.blk-card-meta{color:var(--muted);font-size:.8em}.blk-card-meta-item{overflow-wrap:anywhere;align-items:center;gap:.25rem;display:inline-flex}.blk-card-metric{flex-direction:column;display:inline-flex}.blk-card-metric-value{font-weight:600}.blk-card-metric-label{color:var(--muted);font-size:.75em}.blk-card-deps{color:var(--muted);overflow-wrap:anywhere;font-size:.8em}.blk-card-status{border:1px solid var(--border);white-space:nowrap;border-radius:999px;align-items:center;gap:.3rem;padding:.05rem .5rem;font-size:.75em;display:inline-flex}.blk-card-status[data-tone=good]{color:var(--ok)}.blk-card-status[data-tone=warn]{color:var(--warn)}.blk-card-status[data-tone=bad]{color:var(--danger)}.blk-card-spinner{border:2px solid var(--border);border-top-color:currentColor;border-radius:50%;width:.7em;height:.7em;animation:.9s linear infinite blk-card-spin;display:inline-block}@keyframes blk-card-spin{to{transform:rotate(360deg)}}.blk-card-actions{justify-content:flex-end;align-items:center;gap:.4rem;display:flex}.blk-card-actions .blk-actions{justify-content:flex-start;margin-top:0}.blk-card-overflow{position:relative}.blk-card-overflow>summary{cursor:pointer;list-style:none}.blk-card-overflow[open]>.blk-actions{background:var(--surface-2);border:1px solid var(--border);z-index:5;border-radius:8px;flex-direction:column;padding:.4rem;position:absolute;right:0}.blk-collection{flex-direction:column;gap:.6rem;display:flex}.blk-list-rows{flex-direction:column;gap:.4rem;margin:0;padding:0;list-style:none;display:flex}.blk-list-line{align-items:flex-start;gap:.5rem;min-width:0;display:flex}.blk-list-card{flex:1;min-width:0}.blk-list-card[role=button]{cursor:pointer}.blk-list-number{color:var(--muted);text-align:end;min-width:1.2rem;margin-top:.7rem;font-size:.85em}.blk-list-nested{margin:.4rem 0 0;padding:0;list-style:none}.blk-list-group{flex-direction:column;gap:.4rem;display:flex}.blk-list-heading{border-bottom:1px solid var(--border);overflow-wrap:anywhere;padding-bottom:.2rem;font-weight:600}.blk-quiet-toggle{color:var(--accent);cursor:pointer;font:inherit;background:0 0;border:none;align-self:flex-start;padding:.2rem 0;font-size:.85em}.blk-select,.blk-check{margin-top:.9rem}.blk-bulk{flex-wrap:wrap;align-items:center;gap:.4rem;display:flex}.blk-bulk-action,.blk-bulk-submit{background:var(--surface-2);border:1px solid var(--border);color:var(--text);cursor:pointer;font:inherit;border-radius:8px;padding:.3rem .8rem;font-size:.85em}.blk-bulk-form{align-items:center;gap:.4rem;display:flex}.blk-bulk-form input{background:var(--surface-2);border:1px solid var(--border);color:var(--text);font:inherit;border-radius:8px;padding:.25rem .5rem}.blk-table-wrap{overflow-x:auto}.blk-table{border-collapse:collapse;width:100%}.blk-table th{border-bottom:1px solid var(--border);text-align:start;padding:.4rem .6rem;font-weight:600}.blk-table td{border-bottom:1px solid var(--border);overflow-wrap:anywhere;padding:.4rem .6rem}.blk-table-sort{color:var(--text);cursor:pointer;font:inherit;background:0 0;border:none;padding:0;font-weight:600}.blk-table-sort[data-dir]:after{content:" " attr(data-dir)}.blk-table-pager{justify-content:center;align-items:center;gap:.6rem;margin-top:.6rem;display:flex}.blk-table-pager button{background:var(--surface-2);border:1px solid var(--border);color:var(--text);cursor:pointer;font:inherit;border-radius:8px;padding:.3rem .8rem}.blk-table-pager button:disabled{opacity:.5}.blk-table-compact{flex-direction:column;gap:.6rem;display:flex}.blk-table-card{border:1px solid var(--border);border-radius:10px;overflow:hidden}.blk-table-high,.blk-table-low-fields{grid-template-columns:auto 1fr;gap:.2rem .8rem;padding:.4rem .7rem;display:grid}.blk-table-field{display:contents}.blk-table-field dt{color:var(--muted);font-size:.8em}.blk-table-field dd{overflow-wrap:anywhere;margin:0}.blk-table-expand{color:var(--accent);cursor:pointer;font:inherit;background:0 0;border:none;padding:.2rem .7rem;font-size:.85em}.blk-grid-items{gap:.6rem;display:grid}.blk-grid-display[data-overflow=scroll] .blk-grid-items{grid-template-columns:none;grid-auto-flow:column}.blk-grid-card{cursor:pointer;min-width:0}.blk-lanes-row{align-items:flex-start;gap:.8rem;display:flex;overflow-x:auto}.blk-lane{flex:1 1 0;min-width:14rem}.blk-lane-heading{padding:.2rem .4rem;font-weight:600}.blk-lane-card{cursor:pointer;min-width:0;margin:.3rem 0}.blk-lane-switcher{font:inherit;margin-block-end:.4rem}.blk-move-to{position:relative}.blk-move-to-target{font:inherit;display:block}.blk-calendar-grid{grid-template-columns:repeat(7,1fr);gap:.2rem;display:grid}.blk-calendar-day{border:1px solid color-mix(in srgb, currentColor 12%, transparent);min-height:3.5rem;padding:.2rem}.blk-calendar-daynum{opacity:.6;font-size:.75em}.blk-calendar-item{cursor:pointer;font-size:.85em;position:relative}.blk-calendar-resize{cursor:ew-resize;inset-inline-end:0;width:.6rem;height:.6rem;display:inline-block;position:absolute;bottom:0}.blk-agenda-row{cursor:pointer;gap:.6rem;padding:.2rem 0;display:flex}.blk-agenda-when{opacity:.7;white-space:nowrap;font-size:.8em}.blk-lane-card,.blk-calendar-item,.blk-calendar-resize{touch-action:none}.blk-form{gap:var(--sp-3,.75rem);flex-direction:column;display:flex}.blk-form[data-mode=inline]{flex-direction:row;align-items:flex-end}.blk-field{flex-direction:column;gap:.25rem;display:flex}.blk-field-label{font-weight:500}.blk-field-help{color:var(--fg-dim,#888);font-size:.875em}.blk-field-error{color:var(--bad,#d33);font-size:.875em}.blk-choice{gap:1rem;display:flex}.blk-choice[data-preview=beside]{flex-direction:row}.blk-choice[data-preview=below]{flex-direction:column}.blk-choice-options{flex-direction:column;gap:.25rem;display:flex}.blk-choice-option{align-items:baseline;gap:.5rem;display:flex}.blk-choice-description{color:var(--fg-dim,#888);font-size:.875em}.blk-choice-preview{min-width:12rem}.blk-secret{align-items:center;gap:.5rem;display:flex}.blk-secret-status{color:var(--fg-dim,#888);font-size:.875em}.blk-list-entry{align-items:flex-start;gap:.5rem;display:flex}.blk-daterange{flex-wrap:wrap;gap:.5rem;display:flex}.blk-stepper-progress{color:var(--fg-dim,#888);font-size:.875em}.blk-stepper-nav{gap:.5rem;display:flex}.blk-page{flex-direction:column;height:100%;min-height:0;display:flex}.blk-page-header{border-bottom:1px solid var(--border,#333);flex-wrap:wrap;align-items:center;gap:.75rem;padding:.75rem 1rem;display:flex}.blk-page-titles{flex:auto;min-width:0}.blk-page-title{margin:0;font-size:1.1rem}.blk-page-subtitle{color:var(--fg-subtle,#999);margin:0;font-size:.85rem}.blk-page-body{flex:auto;gap:1rem;min-height:0;padding:1rem;display:flex}.blk-page-sidebar{flex:0 0 16rem;overflow:auto}.blk-page-main{flex:auto;min-width:0;overflow:auto}.blk-page-detail{flex:0 0 24rem;overflow:auto}.blk-page-empty{text-align:center;color:var(--fg-subtle,#999);padding:3rem 1rem}.blk-page-empty-title{color:var(--fg,#ddd);font-size:1rem;font-weight:600}.blk-page-switcher{gap:.5rem;display:inline-flex}.blk-page-action{align-items:center;gap:.25rem;display:inline-flex}.blk-page-toggle{display:none}@container (width<=39.99rem){.blk-page-toggle{display:inline-flex}.blk-page-sidebar,.blk-page-detail{display:none}.blk-page-sidebar[data-open],.blk-page-detail[data-open]{background:var(--bg,#111);z-index:20;padding:1rem;display:block;position:absolute;inset:0;overflow:auto}.blk-page{position:relative}}.blk-surface{min-height:0}.blk-surface>.blk-surface-toast{position:fixed;bottom:1rem;right:1rem}.blk-chat-toast{margin:.25rem 0 0}:root{--bg:#090d16;--surface:#0f1524;--surface-2:#161e31;--border:#1f2942;--text:#e2e8f3;--muted:#8e9ab2;--faint:#5d6984;--ok:#5fd4a8;--code:#ffffff0f;--accent:#d6a24a;--accent-bg:#d6a24a24;--warn:#d9a35a;--danger:#e0707a;--serif:Georgia, "Iowan Old Style", serif;--sans:Inter, -apple-system, "Segoe UI", system-ui, sans-serif;--mono:"JetBrains Mono", ui-monospace, Menlo, monospace;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}[data-mode=light]{--warn:#8a6116;--danger:#b3363f;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}a{color:var(--accent)}[data-mode=light] a{color:var(--text);text-decoration:underline;-webkit-text-decoration-color:var(--accent);text-decoration-color:var(--accent)}*{box-sizing:border-box}html,body{height:100%;margin:0}body{font:14px/1.5 var(--sans);background:var(--bg);color:var(--text);overflow:hidden}#root{height:100%}.app-root{height:100%;display:flex;overflow:hidden}.app-boot,.app-refused{height:100%;color:var(--muted);text-align:center;flex-direction:column;justify-content:center;align-items:center;gap:.5rem;padding:2rem;display:flex}.app-refused h1{color:var(--danger);margin:0;font-size:1.1rem}.app-refused p{max-width:70ch;font-family:var(--mono);word-break:break-all;font-size:.8rem}.side-drawer{background:var(--bg);flex:none;min-width:0;display:flex}.rail{border-right:1px solid var(--border);background:var(--bg);flex-direction:column;flex:none;align-items:center;gap:4px;width:52px;padding:12px 0;display:flex}.rail-brand{width:30px;height:30px;color:var(--accent);border:1px solid var(--border);border-radius:8px;place-items:center;margin-bottom:14px;display:grid}.rail-brand svg{width:18px;height:18px}.rail-btn{width:36px;height:36px;color:var(--faint);cursor:pointer;background:0 0;border:none;border-radius:8px;place-items:center;padding:0;display:grid;position:relative}.rail-btn:hover,.rail-btn-active{color:var(--text);background:var(--surface-2)}.rail-btn-active:before{content:"";background:var(--accent);border-radius:2px;width:2px;position:absolute;top:9px;bottom:9px;left:-8px}.rail-btn svg{width:18px;height:18px}.rail-spacer{flex:1}.rail-health{background:var(--ok);width:8px;height:8px;box-shadow:0 0 0 3px color-mix(in srgb, var(--ok) 15%, transparent);border-radius:50%;flex:none;margin:8px 0 10px}.rail-health[data-health=degraded]{background:var(--warn);box-shadow:0 0 0 3px color-mix(in srgb, var(--warn) 18%, transparent)}.rail-user{text-overflow:ellipsis;white-space:nowrap;max-width:44px;color:var(--muted);text-align:center;flex:none;margin:6px 0 2px;font-size:.68rem;overflow:hidden}.session-list{background:var(--surface);border-right:1px solid var(--border);flex-direction:column;flex:none;width:264px;min-height:0;padding:.4rem;display:flex}.session-search-row{gap:8px;margin:4px 2px 6px;display:flex}.session-search{border:1px solid var(--border);background:var(--bg);min-width:0;height:32px;color:var(--text);font:inherit;border-radius:8px;flex:1;padding:0 10px;font-size:13px}.session-search::placeholder{color:var(--faint)}.session-search:focus-visible{border-color:var(--accent);outline:none}.session-new{border:1px solid var(--border);width:32px;height:32px;color:var(--text);cursor:pointer;background:0 0;border-radius:8px;flex:none;place-items:center;padding:0;display:grid}.session-new:hover,.session-new:focus-visible{background:var(--surface-2)}.session-new svg{width:16px;height:16px}.session-list ul{flex:1;margin:.25rem 0 0;padding:0;list-style:none;overflow:auto}.session-group{letter-spacing:.04em;color:var(--faint);padding:10px 16px 4px;font-size:11px}.session-item{min-height:32px;color:var(--muted);border-radius:6px;align-items:center;gap:6px;margin:1px 8px;padding:0 6px 0 10px;display:flex;position:relative}.session-item:hover,.session-item:focus-within{background:var(--surface-2);color:var(--text)}.session-item-active{background:var(--accent-bg);color:var(--text)}.session-fork-mark{width:14px;color:var(--faint);text-align:right;flex:none;font-size:12px}.session-select{text-align:left;min-width:0;color:inherit;cursor:pointer;font:inherit;background:0 0;border:none;flex:1;padding:6px 0;display:block}.session-name{white-space:nowrap;text-overflow:ellipsis;display:block;overflow:hidden}.session-age{color:var(--faint);flex:none;font-size:11px}.session-row-actions{flex:none;display:flex;position:relative}.session-menu-toggle{width:22px;height:22px;color:var(--faint);cursor:pointer;background:0 0;border:none;border-radius:5px;place-items:center;padding:0;font-size:13px;line-height:1;display:grid}.session-menu-toggle svg{width:14px;height:14px}.session-menu-toggle{opacity:0;transition:opacity 80ms}.session-item:hover .session-menu-toggle,.session-item:focus-within .session-menu-toggle{opacity:1}.session-item:hover .session-age,.session-item:focus-within .session-age{opacity:0}.session-menu-toggle:hover,.session-menu-toggle:focus-visible{color:var(--text);background:var(--bg)}.session-menu{z-index:30;border:1px solid var(--border);background:var(--surface-2);border-radius:8px;flex-direction:column;min-width:130px;padding:4px;display:flex;position:absolute;top:24px;right:0;box-shadow:0 8px 24px #0000004d}.session-menu button{color:var(--text);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:6px;padding:6px 10px;font-size:13px}.session-menu button:hover,.session-menu button:focus-visible{background:var(--accent-bg)}.session-rename-input{border:1px solid var(--accent);background:var(--bg);min-width:0;color:var(--text);font:inherit;border-radius:6px;flex:1;padding:3px 6px;font-size:13px}.session-empty{color:var(--faint);text-align:center;padding:16px;font-size:13px}.session-archived-toggle{width:calc(100% - 16px);color:var(--faint);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;margin:6px 8px 2px;padding:5px 10px;font-size:12px}.session-archived-toggle:hover,.session-archived-toggle:focus-visible{background:var(--surface-2);color:var(--muted)}.session-archived-toggle[aria-pressed=true]{color:var(--text)}.session-item-archived{opacity:.55}.side-error{color:var(--danger);margin:0;padding:6px 10px;font-size:12px}.main-column{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.main-body{flex:1;min-height:0;display:flex}.ext-surface{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.chat-header{border-bottom:1px solid var(--border);background:var(--bg);flex:none;align-items:center;gap:10px;min-width:0;height:48px;padding:0 16px 0 24px;display:flex}.chat-title{color:var(--text);cursor:text;white-space:nowrap;text-overflow:ellipsis;background:0 0;border:none;min-width:0;padding:0;font-family:inherit;font-size:14px;font-weight:600;overflow:hidden}.chat-title-input{font:inherit;color:var(--text);background:var(--surface);border:1px solid var(--accent);border-radius:6px;width:min(360px,60vw);min-width:0;max-width:100%;padding:2px 8px;font-size:14px;font-weight:600}.chat-header-spacer{flex:1;min-width:12px}.icon-btn{width:30px;height:30px;color:var(--muted);cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;place-items:center;padding:0;display:grid}.icon-btn:hover{background:var(--surface-2);color:var(--text)}.icon-btn[aria-pressed=true]{color:var(--text);background:var(--surface-2)}.icon-btn svg{width:16px;height:16px}.chat-menu-slot{display:inline-flex;position:relative}.chat-menu{z-index:60;background:var(--surface);border:1px solid var(--border);border-radius:8px;flex-direction:column;min-width:148px;padding:4px;display:flex;position:absolute;top:34px;right:0;box-shadow:0 10px 28px #00000059}.chat-menu button{color:var(--text);text-align:left;font:inherit;cursor:pointer;background:0 0;border:none;border-radius:6px;padding:6px 10px;font-size:13px}.chat-menu button:hover{background:var(--surface-2)}.shell-error{color:var(--danger);border-bottom:1px solid var(--border);margin:0;padding:.35rem 24px;font-size:12.5px}.chat-menu-toggle{display:none}.chat-column{background:var(--bg);flex-direction:column;flex:1;min-width:0;min-height:0;display:flex;position:relative}.chat-center{overscroll-behavior-x:none;flex:1;min-height:0;padding:28px 24px 8rem;overflow:hidden auto}.chat-fade{z-index:1;pointer-events:none;background:linear-gradient(to bottom, transparent 0%, color-mix(in srgb, var(--bg) 85%, transparent) 45%, var(--bg) 70%);-webkit-backdrop-filter:blur(1.5px);backdrop-filter:blur(1.5px);height:12rem;position:absolute;bottom:0;left:0;right:0;-webkit-mask-image:linear-gradient(#0000,#000 40%);mask-image:linear-gradient(#0000,#000 40%)}.chat-empty,.chat-loading{color:var(--faint);text-align:center}.turn-list{flex-direction:column;gap:1.1rem;width:100%;max-width:720px;margin:0 auto;display:flex}.turn{flex-direction:column;align-items:stretch;min-width:0;display:flex}.turn-user{align-items:flex-end}.turn-lineage{color:var(--faint);font:inherit;cursor:pointer;background:0 0;border:none;align-self:flex-start;padding:0;font-size:12px}.turn-lineage:hover,.turn-lineage:focus-visible{color:var(--muted);text-underline-offset:3px;text-decoration:underline}.entry-user{flex-direction:column;align-items:stretch;display:flex}.entry-user-message{overflow-wrap:anywhere;background:var(--surface-2);border:1px solid var(--border);border-radius:14px 14px 4px;max-width:min(60ch,100%);padding:9px 14px}.entry-user-message p{margin:.25rem 0}.entry-expanded{flex-direction:column;align-items:flex-end;gap:.3rem;display:flex}.entry-expanded-chip{border:1px solid var(--border);color:var(--muted);cursor:pointer;background:0 0;border-radius:6px;padding:.1rem .5rem;font-size:.75rem}.entry-expanded-chip:hover{color:var(--text)}.entry-expanded-text{border:1px dashed var(--border);max-width:min(78%,60ch);color:var(--muted);white-space:pre-wrap;word-break:break-word;border-radius:8px;align-self:flex-end;padding:6px 10px;font-size:.8rem}.entry-blocks{border:1px solid var(--border);border-radius:8px;margin:.5rem 0;padding:.5rem .75rem}.entry-blocks-title{color:var(--muted);margin-bottom:.25rem;font-size:.8rem}.entry-blocks-unknown{font-size:.8rem;font-style:italic}.entry-note summary{cursor:pointer;color:var(--muted);font-size:.8rem}.entry-note-text{color:var(--muted);white-space:pre-wrap;padding:6px 10px;font-size:.8rem}.entry-answer-chip{border:1px solid var(--border);color:var(--muted);border-radius:6px;margin-top:.3rem;padding:.1rem .5rem;font-size:.75rem;display:inline-block}.turn-prose{word-break:break-word;min-width:0}.md{min-width:0}.md-prose{font-family:var(--serif);font-size:15px;line-height:1.65}.md-bubble{font-size:14px;line-height:1.5}.md>:last-child{margin-bottom:0}.md p{margin:0 0 .75rem}.md-bubble p{margin:.25rem 0}.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{font-family:var(--sans);color:var(--text);margin:1.1rem 0 .5rem;line-height:1.3}.md h1{font-size:1.25rem}.md h2{font-size:1.15rem}.md h3{font-size:1.05rem}.md h4,.md h5,.md h6{font-size:1rem}.md ul,.md ol{margin:0 0 .75rem;padding-left:1.4rem}.md li{margin:.15rem 0}.md blockquote{border-left:2px solid var(--border);color:var(--muted);margin:0 0 .75rem;padding:.1rem 0 .1rem .75rem}.md hr{border:0;border-top:1px solid var(--border);margin:1rem 0}.md img{max-width:100%}.md code{font-family:var(--mono);background:var(--code);border-radius:4px;padding:.1em .35em;font-size:.88em}.md-tbl{border:1px solid var(--border);font-family:var(--sans);border-radius:10px;margin:.35rem 0 .9rem;font-size:13px;overflow:hidden}.md-tbl-bar{border-bottom:1px solid var(--border);background:var(--surface);color:var(--faint);align-items:center;gap:.5rem;padding:.35rem .6rem;font-size:12px;display:flex}.md-tbl-bar input{min-width:0;color:var(--text);font:12px var(--sans);background:0 0;border:none;outline:none;flex:1}.md-tbl-count{color:var(--faint);white-space:nowrap;margin-left:auto;font-size:12px}.md-tbl-scroll{overscroll-behavior-x:contain;overflow-x:auto}.md-tbl table{border-collapse:collapse;width:100%}.md-tbl th,.md-tbl td{word-break:normal;overflow-wrap:normal}.md-tbl th{text-align:left;color:var(--muted);border-bottom:1px solid var(--border);background:var(--surface);padding:7px 10px;font-weight:500}.md-tbl td{border-bottom:1px solid var(--border);color:var(--text);padding:7px 10px}.md-tbl tbody tr:last-child td{border-bottom:none}.md-code{border:1px solid var(--border);background:color-mix(in srgb, var(--surface) 55%, var(--bg));border-radius:10px;margin:.35rem 0 .9rem}.md-code-head{border-bottom:1px solid var(--border);font-family:var(--sans);color:var(--faint);justify-content:space-between;align-items:center;padding:.3rem .6rem;font-size:11px;display:flex}.md-code-lang{font-family:var(--mono)}.md-code-copy{color:var(--faint);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:5px;padding:1px 7px}.md-code-copy:hover,.md-code-copy:focus-visible{color:var(--text);background:var(--surface-2)}.md-code-copy[data-copied]{color:var(--ok)}.md-code pre{font:12.5px/1.55 var(--mono);color:var(--text);margin:0;padding:.6rem .75rem;overflow-x:auto}.md-code pre code{font:inherit;background:0 0;border-radius:0;padding:0}.entry-attachment{font-family:var(--mono);color:var(--muted);margin-left:.4rem;font-size:.75rem}.message-actions{opacity:0;align-items:center;gap:.15rem;min-height:24px;margin-top:2px;transition:opacity 80ms;display:flex}.message-actions[data-kind=user]{justify-content:flex-end}.turn:hover .message-actions,.turn:focus-within .message-actions{opacity:1}.message-action{color:var(--faint);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:5px;padding:2px 7px;font-size:12px}.message-action:hover,.message-action:focus-visible{color:var(--text);background:var(--surface-2)}.message-action[data-copied]{color:var(--ok)}.message-meta{color:var(--faint);font-family:var(--mono);white-space:nowrap;margin-left:auto;font-size:11px}.activity-line{flex-direction:column;align-items:flex-start;margin-top:.35rem;display:flex}.activity-toggle{color:var(--muted);font-family:var(--mono);cursor:pointer;background:0 0;border:none;align-items:center;gap:.45rem;padding:1px 0;font-size:11.5px;display:inline-flex}.activity-toggle:hover,.activity-toggle:focus-visible{color:var(--text)}.activity-failed{color:var(--danger)}.activity-body{border-left:2px solid var(--border);flex-direction:column;gap:.15rem;width:100%;margin-top:.25rem;padding-left:.85rem;display:flex}.activity-thinking{color:var(--faint);white-space:pre-wrap;margin:.1rem 0 .25rem;font-size:.82rem;font-style:italic}.activity-spinner,.tool-row-spinner{border:1.5px solid var(--border);border-top-color:var(--accent);border-radius:50%;flex:none;width:9px;height:9px;animation:.8s linear infinite activity-spin}@keyframes activity-spin{to{transform:rotate(360deg)}}.entry-system,.entry-unknown{color:var(--muted);font-size:.8rem}.turn-compaction{color:var(--faint);letter-spacing:.05em;align-items:center;gap:12px;font-size:11px;display:flex}.turn-compaction:before,.turn-compaction:after{content:"";border-top:1px solid var(--border);flex:1}.chat-working{color:var(--muted);align-items:center;gap:.3rem;padding:.4rem .2rem;font-size:.8rem;display:flex}.chat-working-dot{background:var(--muted);border-radius:50%;width:6px;height:6px;animation:1.2s ease-in-out infinite working-bounce}.chat-working-dot:nth-child(2){animation-delay:.15s}.chat-working-dot:nth-child(3){animation-delay:.3s}@keyframes working-bounce{0%,to{opacity:.25;transform:translateY(0)}50%{opacity:1;transform:translateY(-3px)}}.chat-working-label{margin-left:.25rem}.chat-compaction-failure{color:var(--danger);margin:.25rem 0;font-size:.8rem}.tool-row{flex-direction:column;min-width:0;display:flex}.tool-row-head{align-items:center;gap:.4rem;min-width:0;display:flex}.tool-row-toggle{min-width:0;color:var(--muted);font-family:var(--mono);text-align:left;cursor:pointer;background:0 0;border:none;flex:1;align-items:baseline;gap:.5rem;padding:1px 0;font-size:12px;display:flex}.tool-row-toggle:hover,.tool-row-toggle:focus-visible{color:var(--text)}.tool-row[data-error] .tool-row-toggle{color:var(--danger)}.tool-row-glyph{flex:none}.tool-row-glyph[data-glyph=ok]{color:var(--ok)}.tool-row-glyph[data-glyph=error]{color:var(--danger)}.tool-row-glyph[data-glyph=pending]{color:var(--faint)}.tool-row-name{flex:none}.tool-row-preview{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.tool-row-actions{opacity:0;flex:none;gap:.15rem;transition:opacity 80ms;display:flex}.tool-row:hover .tool-row-actions,.tool-row:focus-within .tool-row-actions{opacity:1}.tool-row-detail{flex-direction:column;gap:.35rem;margin:.25rem 0 .35rem;display:flex}.tool-row-args,.tool-row-output{background:var(--code);font-family:var(--mono);white-space:pre;border-radius:6px;max-height:240px;margin:0;padding:.45rem .6rem;font-size:12px;overflow:auto}.tool-row-more{color:var(--faint);font:inherit;cursor:pointer;background:0 0;border:none;align-self:flex-start;padding:0;font-size:12px}.tool-row-more:hover,.tool-row-more:focus-visible{color:var(--text);text-underline-offset:3px;text-decoration:underline}.tool-row-diff{background:var(--code);font-family:var(--mono);white-space:pre;border-radius:6px;max-height:240px;margin:0;padding:.45rem .6rem;font-size:12px;overflow:auto}.tool-row-diff .diff-line-add{color:var(--ok)}.tool-row-diff .diff-line-del{color:var(--danger)}.tool-row-diff .diff-line-ctx{color:var(--muted)}.composer-slot{z-index:2;background:var(--bg);padding:.75rem 24px 1.1rem;position:absolute;bottom:0;left:0;right:0}.composer{border:1px solid var(--border);background:var(--surface);border-radius:14px;flex-direction:column;gap:.55rem;max-width:720px;margin:0 auto;padding:12px;display:flex}.composer-input{resize:none;width:100%;color:var(--text);font:inherit;background:0 0;border:none;min-height:1.6rem;max-height:40vh;padding:.35rem .25rem .45rem;transition:height .12s ease-out;overflow-y:auto}.composer-input:focus-visible{outline:1px solid var(--border);outline-offset:3px;border-radius:6px}.composer-toolbar{flex-wrap:nowrap;align-items:center;gap:4px;min-width:0;display:flex}.composer-toolbar>*{flex-shrink:0}.composer-toolbar>.composer-picker-model{flex-shrink:1;min-width:0}.composer-picker-model .composer-pill{min-width:0;max-width:100%}.composer-pill-label{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}@media (width<=600px){.composer-pill-prefix{display:none}}.composer-toolbar-spacer{flex:1}.composer-pill{height:28px;color:var(--muted);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:7px;align-items:center;gap:6px;padding:0 9px;font-size:12.5px;display:inline-flex}.composer-pill:hover,.composer-pill[aria-expanded=true]{background:var(--surface-2);color:var(--text)}.composer-pill:disabled{opacity:.55;cursor:default}.composer-pill svg{width:15px;height:15px}.composer-pill-caret{color:var(--faint);font-size:10px}.composer-picker{min-width:0;display:inline-flex;position:relative}.composer-picker-menu{z-index:60;background:var(--surface);border:1px solid var(--border);border-radius:8px;flex-direction:column;gap:1px;min-width:210px;max-height:16rem;margin:0;padding:4px;list-style:none;display:flex;position:absolute;bottom:calc(100% + 6px);left:0;overflow:auto;box-shadow:0 10px 28px #00000059}.composer-picker-menu li{color:var(--text);cursor:pointer;white-space:nowrap;border-radius:6px;align-items:baseline;gap:8px;padding:6px 10px;font-size:13px;display:flex}.composer-picker-menu li[data-highlighted]{background:var(--accent-bg)}.composer-picker-check{color:var(--accent);font-size:12px}.composer-picker-id{color:var(--muted);font-family:var(--mono);margin-left:auto;padding-left:12px;font-size:11.5px}.composer-ring{color:var(--faint);justify-content:center;align-items:center;gap:6px;margin-right:6px;font-size:11.5px;display:inline-flex}.composer-ring-track{fill:none;stroke:var(--border);stroke-width:3px}.composer-ring-fill{fill:none;stroke:var(--accent);stroke-width:3px;stroke-linecap:round}.composer-send{background:var(--accent);width:30px;height:30px;color:var(--bg);cursor:pointer;border:0;border-radius:8px;flex:none;place-items:center;padding:0;display:grid}.composer-send svg{width:16px;height:16px}.composer-send.composer-send-disabled{background:var(--surface-2);color:var(--faint);cursor:default}.composer-queued{color:var(--warn);white-space:nowrap;font-size:12px}.composer-attach{padding:0 7px}.composer-attach[data-uploading]{opacity:.6;cursor:default}.composer-hint{text-align:center;max-width:720px;color:var(--faint);margin:6px auto 0;font-size:11px}.composer-attachments{flex-wrap:wrap;gap:.4rem;margin:0;padding:0;list-style:none;display:flex}.composer-attachment{border:1px solid var(--border);color:var(--text);border-radius:6px;align-items:center;gap:.35rem;padding:.15rem .5rem;font-size:.78rem;display:flex}.composer-attachment-remove{color:var(--muted);cursor:pointer;background:0 0;border:none;padding:0;font-size:.9rem}.right-panel{background:var(--surface);border-left:1px solid var(--border);flex-direction:column;flex:none;width:280px;min-height:0;display:flex}.right-panel-title{color:var(--faint);letter-spacing:.04em;margin:14px 10px 8px;font-size:11px;font-weight:500}.right-panel-body{flex-direction:column;flex:1;min-height:0;display:flex}.outline{flex-direction:column;flex:1;gap:1px;min-height:0;padding:0 .5rem .75rem;display:flex;overflow-y:auto}.outline-empty{color:var(--faint);margin:.25rem .75rem;font-size:12.5px}.outline-item{text-align:left;color:var(--muted);font:12.5px/1.35 var(--sans);cursor:pointer;background:0 0;border:none;border-left:2px solid #0000;border-radius:0 6px 6px 0;padding:6px 8px}.outline-text{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.outline-item[data-clamped=true] .outline-text{-webkit-mask-image:linear-gradient(#000 calc(100% - .7em),#0000);mask-image:linear-gradient(#000 calc(100% - .7em),#0000)}.outline-item:hover,.outline-item:focus-visible{background:var(--surface-2);color:var(--text)}.outline-item[aria-current=true]{color:var(--text);border-left-color:var(--accent)}.workspace-browser{flex-direction:column;height:100%;min-height:0;padding:.6rem .8rem;display:flex}.workspace-title{color:var(--faint);text-transform:uppercase;letter-spacing:.5px;align-items:center;margin:0 0 .6rem;font-size:12px;font-weight:600;display:flex}.workspace-close{color:var(--muted);cursor:pointer;text-transform:none;background:0 0;border:none;margin-left:auto;padding:0 .2rem;font-size:14px;display:none}.workspace-tree,.workspace-children{margin:0;padding:0;list-style:none}.workspace-tree{font-family:var(--mono);flex:1;min-height:0;font-size:12px;overflow:auto}.workspace-dir,.workspace-file{width:100%;color:inherit;text-align:left;cursor:pointer;font:inherit;background:0 0;border:none;border-radius:5px;padding:.2rem .3rem;display:block}.workspace-file{color:var(--muted)}.workspace-dir:hover,.workspace-file:hover{background:var(--surface-2)}.workspace-file:hover{color:var(--text)}.workspace-file[data-selected]{background:var(--surface-2);color:var(--text)}.file-preview{border-top:1px solid var(--border);background:var(--surface-2);max-height:40%;padding:.6rem .8rem;overflow:auto}.file-preview-path{color:var(--muted);word-break:break-all;margin:0 0 .4rem;font-size:.75rem}.file-preview-content{font-family:var(--mono);white-space:pre-wrap;word-break:break-word;margin:0;font-size:11px;overflow:auto}.file-preview-binary,.file-preview-loading,.workspace-empty,.workspace-loading{color:var(--faint);font-size:.8rem}.jobs-panel{flex-direction:column;height:100%;min-height:0;padding:.6rem .8rem;display:flex}.jobs-list{flex:1;min-height:0;margin:0;padding:0;list-style:none;overflow-y:auto}.job-row{border-bottom:1px solid var(--border);padding:.35rem 0}.job-summary{text-align:left;cursor:pointer;font:inherit;background:0 0;border:none;border-radius:5px;grid-template-columns:1fr;gap:1px;padding:2px 4px;display:grid}.job-summary:hover{background:var(--surface-2)}.job-name{color:var(--text);font-size:12.5px;font-weight:500}.job-schedule,.job-next,.job-last{color:var(--muted);font-size:11.5px;font-family:var(--mono)}.job-summary-side{justify-content:flex-end;align-items:center;gap:.4rem;padding:0 4px .25rem;display:flex}.job-conversation{color:var(--accent);font:11.5px var(--mono);cursor:pointer;background:0 0;border:none;padding:0;text-decoration:underline}.job-runnow{border:1px solid var(--border);color:var(--text);font:11px var(--sans);cursor:pointer;background:0 0;border-radius:5px;padding:1px 8px}.job-runnow:hover{border-color:var(--accent);color:var(--accent)}.job-detail{padding:.3rem .4rem .5rem}.job-section-title{color:var(--faint);text-transform:uppercase;letter-spacing:.5px;margin:.4rem 0 .25rem;font-size:10.5px;font-weight:600}.job-run-row,.job-revision-meta{font:11px var(--mono);color:var(--muted);flex-wrap:wrap;align-items:baseline;gap:.5rem;padding:1px 0;display:flex}.job-run-version,.job-revision-version{color:var(--faint)}.job-run-manual{border:1px solid var(--border);border-radius:4px;padding:0 4px;font-size:10px}.job-run-outcome[data-outcome=failed],.job-run-error{color:var(--danger)}.job-run-outcome[data-outcome=succeeded]{color:var(--ok)}.job-run-error{white-space:nowrap;text-overflow:ellipsis;max-width:100%;overflow:hidden}.job-revision-row{margin:.25rem 0}.job-revision-changedBy{color:var(--text)}.job-revision-row .interaction-diff{margin:.25rem 0}.job-empty{color:var(--faint);margin:.1rem 0;font-size:11.5px}.drawer-backdrop,.panel-backdrop{display:none}@media (width<=900px){.side-drawer{z-index:90;transition:transform .18s;position:fixed;top:0;bottom:0;left:0;transform:translate(-100%)}.side-drawer[data-open]{transform:translate(0);box-shadow:0 0 32px #00000059}.chat-menu-toggle{display:grid}.right-panel{z-index:90;width:min(320px,85vw);position:fixed;top:0;bottom:0;right:0;box-shadow:-12px 0 32px #00000073}.workspace-close{display:block}.drawer-backdrop,.panel-backdrop{z-index:85;background:#00000059;display:block;position:fixed;inset:0}.chat-center{padding:20px 16px 8rem}.chat-header{padding:0 12px 0 16px}.composer-slot{padding:.5rem 16px .9rem}.settings-layout{flex-direction:column;gap:.5rem;padding:.6rem 12px .6rem 16px}.settings-nav{flex-flow:row;gap:.2rem;width:auto;padding-left:0;overflow:auto hidden}.settings-nav .settings-nav-group{flex-direction:row;align-items:center;gap:.2rem}.settings-nav-item{white-space:nowrap;flex:none}.settings-section{padding:0 4px 1.5rem 0;overflow-y:auto}}.composer-notice{color:var(--muted);margin:.25rem 0;font-size:.8rem}.composer-command-menu{border:1px solid var(--border);background:var(--surface-2);border-radius:8px;max-height:12rem;margin:0;padding:.25rem 0;font-size:.8rem;list-style:none;overflow:auto}.composer-command-menu li{cursor:pointer;align-items:baseline;gap:.5rem;padding:.3rem .6rem;display:flex}.composer-command-menu li[data-highlighted]{background:var(--accent-bg)}.composer-command-name{font-weight:600}.composer-command-args{color:var(--muted);font-family:var(--mono)}.composer-command-description{color:var(--muted)}.settings-page{background:var(--bg);flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.settings-panel{flex-direction:column;flex:1;min-height:0;padding:0;display:flex}.settings-head{border-bottom:1px solid var(--border);background:var(--bg);flex:none;align-items:center;gap:.5rem;height:48px;padding:0 16px 0 24px;display:flex}.settings-title{letter-spacing:.2px;font-weight:650}.settings-head .spacer{flex:1}.settings-close{color:var(--muted);cursor:pointer;background:0 0;border:none;font-size:13px}.settings-body{flex-direction:column;gap:.625rem;min-width:0;max-width:100%;display:flex}.settings-layout{flex:1;gap:.75rem;min-width:0;min-height:0;padding:.75rem 1rem .75rem 0;display:flex}.settings-nav{flex-direction:column;flex:none;gap:.2rem;width:11rem;padding-left:.5rem;display:flex;overflow-y:auto}.settings-nav-item{text-align:left;color:var(--muted);cursor:pointer;background:0 0;border:none;border-radius:5px;padding:.3rem .6rem;font-size:13px}.settings-nav-item:hover{background:var(--surface-2)}.settings-nav-item[data-active]{background:var(--accent-bg);color:var(--text)}.settings-nav-group{flex-direction:column;gap:.2rem;display:flex}.settings-section{flex:1;min-width:0;padding:0 1rem 1.5rem .5rem;overflow-y:auto}.settings-field{border:none;flex-direction:column;gap:.25rem;margin:0;padding:0;display:flex}.settings-label{color:var(--muted);font-size:12px;font-family:var(--sans)}.settings-checkbox{flex-flow:wrap;align-items:center;gap:.4rem;font-size:13px}.settings-checkbox .settings-note{flex-basis:100%}.settings-tools{flex-flow:wrap;gap:.25rem .75rem;display:flex}.settings-tools .settings-note{flex-basis:100%;min-width:0}.settings-note{color:var(--faint);margin:0;font-size:11px}.settings-select-wrap{width:fit-content;max-width:100%;position:relative}.settings-select-wrap:after{content:"▾";color:var(--muted);pointer-events:none;font-size:11px;position:absolute;top:50%;right:.55rem;transform:translateY(-50%)}.settings-field select{appearance:none;background:var(--bg);color:var(--text);border:1px solid var(--border);font:inherit;cursor:pointer;border-radius:6px;max-width:100%;padding:.35rem 1.7rem .35rem .6rem;font-size:13px}.settings-mode-row{background:var(--bg);border:1px solid var(--border);border-radius:8px;gap:3px;width:fit-content;padding:3px;display:flex}.settings-mode{color:var(--muted);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:6px;padding:.25rem .85rem;font-size:13px}.settings-mode:hover{color:var(--text)}.settings-mode[aria-pressed=true]{background:var(--surface-2);color:var(--text);box-shadow:inset 0 0 0 1px var(--border)}.settings-palettes{flex-wrap:wrap;gap:8px;display:flex}.settings-palette{border:1px solid var(--border);cursor:pointer;border-radius:8px;justify-content:center;align-items:center;gap:5px;width:56px;height:34px;padding:0;display:flex}.settings-palette[aria-pressed=true]{outline:2px solid var(--accent);outline-offset:2px}.settings-swatch-dot{border-radius:50%;flex:none;width:8px;height:8px}.settings-accents{flex-wrap:wrap;gap:10px;display:flex}.settings-accent{border:1px solid var(--border);cursor:pointer;border-radius:50%;width:20px;height:20px;padding:0}.settings-accent[aria-pressed=true]{outline:2px solid var(--accent);outline-offset:2px}.settings-thinking{font-family:var(--mono);background:var(--bg);color:var(--text);border:1px solid var(--border);resize:vertical;box-sizing:border-box;border-radius:4px;width:100%;max-width:100%;padding:.4rem;font-size:12px}.settings-actions{align-items:center;gap:.625rem;display:flex}.settings-save{background:var(--accent);color:var(--bg);cursor:pointer;border:none;border-radius:6px;padding:.3rem .9rem;font-weight:600}.settings-save:disabled{opacity:.5;cursor:default}.interaction-cards,.ambient-panels{max-height:40vh;overflow-y:auto}.interaction-cards{flex-direction:column;gap:.5rem;display:flex}.interaction-card{border:1px solid var(--warn);background:color-mix(in srgb, var(--warn) 8%, var(--surface));border-radius:8px;max-width:720px;padding:.7rem .9rem}.interaction-card-title{align-items:center;gap:.5rem;margin-bottom:.35rem;display:flex}.interaction-kind{font-family:var(--mono);color:var(--muted);background:color-mix(in srgb, var(--warn) 14%, transparent);border-radius:4px;padding:.1rem .45rem;font-size:11px}.interaction-expiry{color:var(--muted);font-size:.75rem}.interaction-text{white-space:pre-wrap;margin:.35rem 0}.interaction-diff{grid-template-columns:1fr 1fr;gap:.5rem;margin:.5rem 0;display:grid}.interaction-diff pre{background:var(--surface);font-family:var(--mono);border-radius:6px;max-height:16rem;margin:0;padding:.5rem;font-size:12px;overflow:auto}.interaction-diff-before{color:var(--danger)}.interaction-diff-after{color:var(--ok)}.interaction-options,.interaction-fields{flex-direction:column;gap:.4rem;margin:.5rem 0;display:flex}.interaction-option{border:1px solid var(--border);cursor:pointer;border-radius:6px;grid-template-columns:auto 1fr;align-items:baseline;gap:.4rem;padding:.4rem .6rem;display:grid}.interaction-option input{align-self:center}.interaction-option-label{font-weight:500}.interaction-option-danger{border-color:color-mix(in srgb, var(--danger) 45%, var(--border))}.interaction-option-warn{border-color:color-mix(in srgb, var(--warn) 45%, var(--border))}.interaction-option-ok{border-color:color-mix(in srgb, var(--ok) 45%, var(--border))}.interaction-option-description{color:var(--muted);grid-column:2;font-size:.8rem}.interaction-field{flex-direction:column;gap:.25rem;display:flex}.interaction-field-label{color:var(--muted);font-size:.8rem}.interaction-field input{border:1px solid var(--border);background:var(--surface);color:var(--text);font:inherit;border-radius:6px;padding:.4rem .6rem}.interaction-submit{border:1px solid var(--border);background:var(--ok);color:var(--bg);cursor:pointer;border-radius:6px;align-self:flex-start;padding:.35rem .9rem;font-weight:600}.interaction-submit:disabled{opacity:.5;cursor:default}.interaction-details,.ambient-details{color:var(--muted);margin-top:.4rem;font-size:.75rem}.interaction-details pre,.ambient-details pre{background:var(--surface);border-radius:6px;max-height:12rem;margin:.3rem 0 0;padding:.5rem;overflow:auto}.ambient-panels{flex-direction:column;gap:.5rem;display:flex}.ambient-surface{border:1px solid var(--border);background:var(--surface);border-radius:8px;max-width:720px;padding:.5rem .7rem}.ambient-surface-title{color:var(--faint);justify-content:space-between;margin-bottom:.3rem;font-size:.72rem;display:flex}.ambient-key{font-family:var(--mono)}.ambient-line{font-family:var(--mono);white-space:pre-wrap;flex-wrap:wrap;gap:.75rem;font-size:12px;display:flex}.ambient-segment-right{margin-left:auto}.ambient-rows{flex-direction:column;gap:.2rem;margin:.25rem 0 0;padding:0;list-style:none;display:flex}.ambient-row{align-items:baseline;gap:.45rem;font-size:.85rem;display:flex}.ambient-row[data-status=running] .ambient-row-glyph{color:var(--warn)}.ambient-row[data-status=completed],.ambient-row[data-status=complete],.ambient-row[data-status=passed]{color:var(--muted);text-decoration:line-through;-webkit-text-decoration-color:color-mix(in srgb, var(--muted) 60%, transparent);text-decoration-color:color-mix(in srgb, var(--muted) 60%, transparent)}.ambient-row[data-status=failed],.ambient-row[data-status=blocked]{color:var(--danger)}.ambient-row-glyph{font-family:var(--mono);flex:none}.ambient-row-spinner{color:var(--muted);font-size:.78rem;font-style:italic}.ambient-row-detail{color:var(--muted);font-size:.8rem}.ambient-row-deps{color:var(--muted);font-family:var(--mono);font-size:.72rem}.ambient-metrics{color:var(--muted);font-family:var(--mono);margin-left:auto;font-size:.72rem}.ambient-quiet-summary{color:var(--faint);font-size:.75rem}.ambient-gauge{flex-direction:column;gap:.35rem;display:flex}.ambient-gauge-label{color:var(--muted);font-size:.8rem}.ambient-gauge-bar{background:color-mix(in srgb, var(--faint) 25%, transparent);border-radius:3px;height:6px;overflow:hidden}.ambient-gauge-fill{background:var(--ok);height:100%;transition:width .3s}.ambient-recent-output{background:color-mix(in srgb, var(--border) 40%, transparent);font-family:var(--mono);border-radius:6px;max-height:8rem;margin:0;padding:.4rem;font-size:11px;overflow:auto}.ambient-ledger{flex-direction:column;gap:.2rem;max-height:10rem;margin:.25rem 0 0;padding:0;list-style:none;display:flex;overflow:auto}.ambient-ledger-entry{align-items:baseline;gap:.45rem;font-size:.8rem;display:flex}.ambient-ledger-kind{font-family:var(--mono);color:var(--muted);flex:none;font-size:.7rem}.ambient-ledger-detail{color:var(--muted);font-size:.75rem}.auth-screen{place-items:center;height:100%;padding:1rem;display:grid;overflow:auto}.auth-card{background:var(--surface);border:1px solid var(--border);border-radius:12px;flex-direction:column;gap:.9rem;width:min(24rem,100%);padding:1.5rem 1.5rem 1.25rem;display:flex;box-shadow:0 12px 40px #00000040}.auth-card h1{font-size:1.15rem;font-family:var(--serif);margin:0}.auth-message{color:var(--muted);margin:0;font-size:.85rem}.auth-identity{font-family:var(--mono);color:var(--text);word-break:break-all;margin:0;font-size:.85rem}.auth-field{color:var(--muted);flex-direction:column;gap:.3rem;font-size:.8rem;display:flex}.auth-field input{font:inherit;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:.5rem .65rem}.auth-field input:focus-visible{outline:1px solid var(--accent);border-color:var(--accent)}.auth-optional{color:var(--faint);font-size:.72rem}.auth-submit{font:inherit;color:var(--text);background:var(--accent-bg);border:1px solid var(--accent);cursor:pointer;border-radius:8px;padding:.5rem .9rem}.auth-submit:disabled{opacity:.6;cursor:default}.auth-error{color:var(--danger);word-break:break-word;margin:0;font-size:.8rem}.share-overlay{z-index:60;background:#00000073;place-items:center;padding:1rem;display:grid;position:fixed;inset:0}.share-menu{background:var(--surface);border:1px solid var(--border);border-radius:12px;flex-direction:column;gap:.8rem;width:min(22rem,100%);padding:1rem 1.25rem 1.1rem;display:flex;box-shadow:0 12px 40px #00000040}.share-head{justify-content:space-between;align-items:baseline;gap:.5rem;display:flex}.share-head h2{font-size:1rem;font-family:var(--serif);margin:0}.share-close{font:inherit;color:var(--muted);cursor:pointer;background:0 0;border:none;padding:0;font-size:.75rem}.share-close:hover{color:var(--text)}.share-scopes{flex-direction:column;gap:.4rem;display:flex}.share-scope{font:inherit;text-align:left;color:var(--text);background:var(--bg);border:1px solid var(--border);cursor:pointer;border-radius:8px;flex-direction:column;gap:.1rem;padding:.45rem .65rem;display:flex}.share-scope[data-selected]{border-color:var(--accent);background:var(--accent-bg)}.share-scope-hint{color:var(--muted);font-size:.72rem}.share-level{gap:.4rem;display:flex}.share-level-btn{font:inherit;color:var(--muted);background:var(--bg);border:1px solid var(--border);cursor:pointer;border-radius:8px;flex:1;padding:.35rem .5rem;font-size:.8rem}.share-level-btn[data-selected]{color:var(--text);border-color:var(--accent);background:var(--accent-bg)}.share-field{color:var(--muted);flex-direction:column;gap:.3rem;font-size:.8rem;display:flex}.share-field input{font:inherit;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:.45rem .6rem}.share-warning{color:var(--warn);margin:0;font-size:.8rem}.settings-ext-list{gap:.75rem}.settings-ext{border:1px solid var(--border);border-radius:4px;margin:0;padding:.5rem .75rem .75rem}.settings-ext-name{padding:0 .35rem;font-weight:600}.settings-ext-meta{color:var(--muted);margin-left:.4rem;font-size:11px;font-weight:400}.settings-ext-settings{font-family:var(--mono);background:var(--bg);color:var(--text);border:1px solid var(--border);resize:vertical;box-sizing:border-box;border-radius:4px;width:100%;max-width:100%;padding:.4rem;font-size:12px}.settings-ext-row{flex-wrap:wrap;align-items:center;gap:.4rem;margin-top:.35rem;display:flex}.settings-ext-key{font-family:var(--mono);font-size:12px}.settings-ext-source{color:var(--muted);font-size:11px}.settings-ext-row input{font-family:var(--mono);background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:4px;min-width:8rem;padding:.25rem .4rem;font-size:12px}.token-reveal{border:1px solid color-mix(in srgb, var(--accent) 40%, var(--border))}.token-secret,.token-snippet{font-family:var(--mono);background:var(--bg);border:1px solid var(--border);word-break:break-all;-webkit-user-select:all;user-select:all;border-radius:4px;margin-top:.25rem;padding:.3rem .45rem;font-size:12px;display:block}.token-secret{color:var(--accent)}.settings-provider-list{gap:.75rem}.settings-provider{border:1px solid var(--border);border-radius:4px;margin:0;padding:.5rem .75rem .75rem}.settings-provider-name{flex-wrap:wrap;align-items:center;gap:.4rem;padding:0 .35rem;font-weight:600;display:flex}.settings-provider-status{font-size:10px;font-weight:600;font-family:var(--mono);border:1px solid var(--border);color:var(--muted);white-space:nowrap;border-radius:999px;padding:.05rem .4rem}.settings-provider-status[data-status=ready]{color:var(--ok);border-color:color-mix(in srgb, var(--ok) 40%, transparent)}.settings-provider-status[data-status=degraded],.settings-provider-status[data-status=invalid]{color:var(--danger);border-color:color-mix(in srgb, var(--danger) 40%, transparent)}.settings-provider-meta{color:var(--muted);font-size:11px;font-weight:400}.settings-provider-row{flex-wrap:wrap;align-items:center;gap:.4rem;margin-top:.35rem;display:flex}.settings-provider-row input{font-family:var(--mono);background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:4px;flex:10rem;min-width:10rem;max-width:22rem;padding:.25rem .4rem;font-size:12px}.turn-timeline{flex-direction:column;gap:.5rem;display:flex}.turn-timeline>*+*{border-top:1px solid color-mix(in srgb, var(--border) 55%, transparent);padding-top:.5rem}.timeline-thinking{opacity:.75;white-space:pre-wrap;font-style:italic}.turn-assistant[data-live=true] .turn-timeline{border-left:2px solid color-mix(in srgb, var(--border) 80%, transparent);padding-left:.6rem}.settings-version{color:var(--text-dim,color-mix(in srgb, var(--text) 55%, transparent));-webkit-user-select:text;user-select:text;margin:.8rem 0 0;font-size:.82rem}.gallery{height:100%;min-height:0;display:flex}.gallery-list{border-right:1px solid var(--border,#333);flex:0 0 14rem;padding:1rem;overflow:auto}.gallery-list ul{margin:0;padding:0;list-style:none}.gallery-list button{text-align:left;width:100%;padding:.25rem .5rem;display:block}.gallery-main{flex:auto;min-width:0;padding:1rem;overflow:auto}.gallery-hint{color:var(--fg-subtle,#999)}.gallery-sample{flex-direction:column;min-width:0;height:100%;display:flex}.gallery-sample>.blk-surface{flex:auto;min-height:0}