@usecontextlayer/ctxs 0.5.10 → 0.5.11

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/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="03f63fdf-d723-5d19-8f56-4178bb2fbd0a")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e80413c9-3000-584e-9bcf-799375152efc")}catch(e){}}();
4
4
  import { createRequire } from "node:module";
5
5
  import * as Sentry from "@sentry/node";
6
6
  import * as fs$2 from "node:fs/promises";
@@ -65,7 +65,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
65
65
 
66
66
  //#endregion
67
67
  //#region package.json
68
- var version$2 = "0.5.10";
68
+ var version$2 = "0.5.11";
69
69
 
70
70
  //#endregion
71
71
  //#region sentry.ts
@@ -368,7 +368,7 @@ function pggitUrl(remote) {
368
368
  * (RepoRemote, gitHostBaseDir) -> Repo. The all-slashes repoId IS the path under the
369
369
  * base (`hostDir = <base>/<repoId>`) — one layout rule for every repo kind, and since
370
370
  * `workspace/…` and `claude/…` are disjoint subtrees, a claude home can never land
371
- * inside a content tree. The returned `hostDir` is
371
+ * inside a workspace repo. The returned `hostDir` is
372
372
  * realpath-canonical: we realpath the base (which must already exist) and join the
373
373
  * repoId, so macOS-dev symlinks (`/tmp -> /private/tmp`) are resolved up front. That
374
374
  * matters because claude derives its session bucket from the cwd STRING — host-side
@@ -757,16 +757,16 @@ var ClaudeRepo = class ClaudeRepo {
757
757
  /**
758
758
  * One-sandbox-per-session — absorbs the bridge's `activeSessions`/4409 guard. The
759
759
  * single-threaded event loop makes this check-and-set atomic at the WS upgrade.
760
- * On a watching home (config.watch), the first live session also starts the
761
- * convergence watcher after restore resolves.
760
+ * A pure guard it spawns NO git work: a clone started here would have no owner to
761
+ * join it when a boot fails before the caller awaits restore(), leaving a writer
762
+ * racing teardown. A watching restore-less home is watchable immediately, so its
763
+ * first live session arms the convergence watcher here; a restoring home becomes
764
+ * watchable only inside restore().
762
765
  */
763
766
  tryAcquireSession(sessionId) {
764
767
  if (this.activeSessions.has(sessionId)) return false;
765
768
  this.activeSessions.add(sessionId);
766
- const watch = this.config.watch;
767
- if (watch !== false && this.watchTimer === null) this.restore().then(() => {
768
- if (this.activeSessions.size > 0 && this.watchTimer === null) this.startWatch(watch);
769
- }, () => {});
769
+ if (!this.config.restore) this.maybeStartWatch();
770
770
  return true;
771
771
  }
772
772
  releaseSession(sessionId) {
@@ -788,7 +788,9 @@ var ClaudeRepo = class ClaudeRepo {
788
788
  if (this.watchTimer !== null && this.watchWakeAt - now > opened.wakeAfterMs) this.armWatch(watch, opened.wakeAfterMs);
789
789
  }
790
790
  /**
791
- * Clone-on-first-use into the host dir (slate resume). A no-op for a capture-only
791
+ * Clone-on-first-use into the host dir (slate resume), and the ONE initiator of that
792
+ * clone. The call is the ownership contract: whoever starts the clone awaits it, so
793
+ * it can never outlive the scope that started it. A no-op for a capture-only
792
794
  * home (`restore:false`) and after the first success — memoized so concurrent sessions
793
795
  * don't double-clone. Restore failure **propagates**: the caller blocks the connection
794
796
  * — no boot → no capture → the good repo is untouched. (Proceeding fresh would try to
@@ -796,6 +798,9 @@ var ClaudeRepo = class ClaudeRepo {
796
798
  * silent destruction, but restore-first stays the invariant:
797
799
  * a rejected capture is still a broken session.) The memo is cleared on failure so a
798
800
  * later connection retries rather than inheriting a permanent rejection.
801
+ * Every success — fresh clone or memoized warm reconnect — arms the watcher when a
802
+ * session is live: the watcher stops with the last release, so a reconnect to the
803
+ * kept-warm singleton must re-arm here or the whole session runs unconverged.
799
804
  */
800
805
  async restore() {
801
806
  if (!this.config.restore) return;
@@ -803,7 +808,8 @@ var ClaudeRepo = class ClaudeRepo {
803
808
  this.restorePromise = null;
804
809
  throw err;
805
810
  });
806
- return this.restorePromise;
811
+ await this.restorePromise;
812
+ this.maybeStartWatch();
807
813
  }
808
814
  /**
809
815
  * Does the (restored) home already hold this session's transcript? Drives `--resume` vs
@@ -858,6 +864,10 @@ var ClaudeRepo = class ClaudeRepo {
858
864
  }
859
865
  if (current !== CLAUDE_HOME_GITIGNORE) await writeFile(target, CLAUDE_HOME_GITIGNORE);
860
866
  }
867
+ maybeStartWatch() {
868
+ const watch = this.config.watch;
869
+ if (watch !== false && this.activeSessions.size > 0 && this.watchTimer === null) this.startWatch(watch);
870
+ }
861
871
  startWatch(config) {
862
872
  const opened = openHotWindow(Date.now(), config);
863
873
  this.watchState = opened.state;
@@ -14255,17 +14265,15 @@ function ctxAuthHeaders(tokenProvider) {
14255
14265
 
14256
14266
  //#endregion
14257
14267
  //#region ../slate-shared/dist/index.mjs
14258
- const WORKSPACE_DOCS_SUBDIR = "output";
14259
- const INBOX_CONTENT_TREE = {
14260
- "output/types/update.loader.js": `// The Update detail loader: the routed proposal with its frontmatter
14261
- // judgments and the markdown brief (read off the entry).
14262
- export default async ({ vault, path }) => {
14263
- const node = vault.get(path)
14264
- if (!node) {
14265
- throw new Error(\`update: no entry at "\${path}"\`)
14268
+ const INBOX_DOCS_TREE = {
14269
+ "docs/types/update.loader.js": `// types/update.loader.js
14270
+ export default async ({ docs, path }) => {
14271
+ const doc = docs.get(path)
14272
+ if (!doc) {
14273
+ throw new Error(\`update: no doc at "\${path}"\`)
14266
14274
  }
14267
14275
 
14268
- const item = vault.query({
14276
+ const item = docs.query({
14269
14277
  find: {
14270
14278
  scalar: {
14271
14279
  pattern: [
@@ -14283,13 +14291,13 @@ export default async ({ vault, path }) => {
14283
14291
  where: [["?u", "path", path]],
14284
14292
  })
14285
14293
  if (!item) {
14286
- throw new Error(\`update: node "\${path}" not in the query index\`)
14294
+ throw new Error(\`update: doc "\${path}" not in the query index\`)
14287
14295
  }
14288
14296
 
14289
- return { items: [{ ...item, body: node.body }] }
14297
+ return { items: [{ ...item, body: doc.body }] }
14290
14298
  }
14291
14299
  `,
14292
- "output/types/update.render-spec.json": `${JSON.stringify({ render: {
14300
+ "docs/types/update.render-spec.json": `${JSON.stringify({ render: {
14293
14301
  elements: {
14294
14302
  body: {
14295
14303
  props: { text: { $item: "body" } },
@@ -14367,20 +14375,20 @@ export default async ({ vault, path }) => {
14367
14375
  },
14368
14376
  root: "detail"
14369
14377
  } }, null, " ")}\n`,
14370
- "output/updates/baltic-archival-scripts-revision-07-13.md": "---\ntype: Update\nproposed_title: Baltic - Revised Archival Scripts 07/13\nlane: Baltic - Data Archival\nkind: release\noccurred_on: 2026-07-13\nlast_updated: 2026-07-14\n---\n# Baltic - Revised Archival Scripts 07/13\n\nAdd **Baltic - Revised Archival Scripts 07/13** to the **Baltic - Data Archival** lane.\n\nYou sent the revised archival scripts on 2026-07-13 and the run window moved to Saturday night. Confirm the dry-run output lands in the shared drive before the window opens.\n",
14371
- "output/updates/baltic-dlp-matrix-redlines-07-06.md": "---\ntype: Update\nproposed_title: Baltic - DLP Matrix Redlines 07/06\nlane: Baltic - Data Archival\nkind: feedback\noccurred_on: 2026-07-06\nlast_updated: 2026-07-07\n---\n# Baltic - DLP Matrix Redlines 07/06\n\nAdd **Baltic - DLP Matrix Redlines 07/06** to the **Baltic - Data Archival** lane.\n\nMarcus Reid redlined the DLP sensitivity matrix on 2026-07-06: two label tiers collapsed into one and the finance share is missing. He expects a revised matrix this week.\n",
14372
- "output/updates/cascade-discovery-session-priya-07-12.md": "---\ntype: Update\nproposed_title: Cascade - Discovery Session (Priya) 07/12\nneeds_lane: true\nkind: call\noccurred_on: 2026-07-12\nlast_updated: 2026-07-13\n---\n# Cascade - Discovery Session (Priya) 07/12\n\nAdd **Cascade - Discovery Session (Priya) 07/12** — no lane confidently matches, so pick one.\n\nPriya Raman walked through the intake flow on the 2026-07-12 call and left three action items: the duplicate-vendor report, the approval-chain diagram, and a follow-up with her ops lead.\n",
14373
- "output/updates/cascade-final-invoice-06-30.md": "---\ntype: Update\nproposed_title: Cascade - Final Invoice 06/30\nlane: Cascade - ERP Cutover\nkind: followup\noccurred_on: 2026-06-30\nlast_updated: 2026-07-01\n---\n# Cascade - Final Invoice 06/30\n\nAdd **Cascade - Final Invoice 06/30** to the **Cascade - ERP Cutover** lane.\n\nClose out the quarter: the final cutover invoice is drafted but unsent. Ship it and file the copy against the engagement folder.\n",
14374
- "output/updates/harbor-training-proposal-followup-07-08.md": "---\ntype: Update\nproposed_title: Harbor - Training Proposal Follow-up 07/08\nlane: Harbor - Compliance Training\nkind: followup\noccurred_on: 2026-07-08\nlast_updated: 2026-07-09\n---\n# Harbor - Training Proposal Follow-up 07/08\n\nAdd **Harbor - Training Proposal Follow-up 07/08** to the **Harbor - Compliance Training** lane.\n\nThe training proposal went out 2026-07-01 and Harbor has not responded. A one-line nudge to Elena Cho keeps the September session dates holdable.\n",
14375
- "output/updates/meridian-migration-batch-alignment-07-10.md": "---\ntype: Update\nproposed_title: Meridian - Migration Batch Alignment 07/10\nlane: Meridian - Billing Portal\nkind: call\noccurred_on: 2026-07-10\nlast_updated: 2026-07-10\n---\n# Meridian - Migration Batch Alignment 07/10\n\nAdd **Meridian - Migration Batch Alignment 07/10** to the **Meridian - Billing Portal** lane.\n\nOn the 2026-07-10 call you agreed to re-sequence batches 3 and 4 so the vendor tables land before invoice history. The revised batch plan is owed to their team.\n",
14376
- "output/updates/meridian-uat-currency-escalation-07-14.md": "---\ntype: Update\nproposed_title: Meridian - UAT Currency Escalation 07/14\nlane: Meridian - Billing Portal\nkind: feedback\noccurred_on: 2026-07-14\nlast_updated: 2026-07-14\n---\n# Meridian - UAT Currency Escalation 07/14\n\nAdd **Meridian - UAT Currency Escalation 07/14** to the **Meridian - Billing Portal** lane.\n\nDana Whitfield (Meridian AP lead) escalated the multi-currency UAT blocker on 2026-07-14: mixed-currency invoices still post at the wrong rate.[^1] She wants a fix date before Thursday's steering call.\n\n[^1]: ctx:///mail/messages?id=fixture-meridian-uat-0714\n",
14377
- "output/views/inbox.loader.js": `// Inbox rows shaped for the Table view: the client/short-title split derived
14378
+ "docs/updates/baltic-archival-scripts-revision-07-13.md": "---\ntype: Update\nproposed_title: Baltic - Revised Archival Scripts 07/13\nlane: Baltic - Data Archival\nkind: release\noccurred_on: 2026-07-13\nlast_updated: 2026-07-14\n---\n# Baltic - Revised Archival Scripts 07/13\n\nAdd **Baltic - Revised Archival Scripts 07/13** to the **Baltic - Data Archival** lane.\n\nYou sent the revised archival scripts on 2026-07-13 and the run window moved to Saturday night. Confirm the dry-run output lands in the shared drive before the window opens.\n",
14379
+ "docs/updates/baltic-dlp-matrix-redlines-07-06.md": "---\ntype: Update\nproposed_title: Baltic - DLP Matrix Redlines 07/06\nlane: Baltic - Data Archival\nkind: feedback\noccurred_on: 2026-07-06\nlast_updated: 2026-07-07\n---\n# Baltic - DLP Matrix Redlines 07/06\n\nAdd **Baltic - DLP Matrix Redlines 07/06** to the **Baltic - Data Archival** lane.\n\nMarcus Reid redlined the DLP sensitivity matrix on 2026-07-06: two label tiers collapsed into one and the finance share is missing. He expects a revised matrix this week.\n",
14380
+ "docs/updates/cascade-discovery-session-priya-07-12.md": "---\ntype: Update\nproposed_title: Cascade - Discovery Session (Priya) 07/12\nneeds_lane: true\nkind: call\noccurred_on: 2026-07-12\nlast_updated: 2026-07-13\n---\n# Cascade - Discovery Session (Priya) 07/12\n\nAdd **Cascade - Discovery Session (Priya) 07/12** — no lane confidently matches, so pick one.\n\nPriya Raman walked through the intake flow on the 2026-07-12 call and left three action items: the duplicate-vendor report, the approval-chain diagram, and a follow-up with her ops lead.\n",
14381
+ "docs/updates/cascade-final-invoice-06-30.md": "---\ntype: Update\nproposed_title: Cascade - Final Invoice 06/30\nlane: Cascade - ERP Cutover\nkind: followup\noccurred_on: 2026-06-30\nlast_updated: 2026-07-01\n---\n# Cascade - Final Invoice 06/30\n\nAdd **Cascade - Final Invoice 06/30** to the **Cascade - ERP Cutover** lane.\n\nClose out the quarter: the final cutover invoice is drafted but unsent. Ship it and file the copy against the engagement folder.\n",
14382
+ "docs/updates/harbor-training-proposal-followup-07-08.md": "---\ntype: Update\nproposed_title: Harbor - Training Proposal Follow-up 07/08\nlane: Harbor - Compliance Training\nkind: followup\noccurred_on: 2026-07-08\nlast_updated: 2026-07-09\n---\n# Harbor - Training Proposal Follow-up 07/08\n\nAdd **Harbor - Training Proposal Follow-up 07/08** to the **Harbor - Compliance Training** lane.\n\nThe training proposal went out 2026-07-01 and Harbor has not responded. A one-line nudge to Elena Cho keeps the September session dates holdable.\n",
14383
+ "docs/updates/meridian-migration-batch-alignment-07-10.md": "---\ntype: Update\nproposed_title: Meridian - Migration Batch Alignment 07/10\nlane: Meridian - Billing Portal\nkind: call\noccurred_on: 2026-07-10\nlast_updated: 2026-07-10\n---\n# Meridian - Migration Batch Alignment 07/10\n\nAdd **Meridian - Migration Batch Alignment 07/10** to the **Meridian - Billing Portal** lane.\n\nOn the 2026-07-10 call you agreed to re-sequence batches 3 and 4 so the vendor tables land before invoice history. The revised batch plan is owed to their team.\n",
14384
+ "docs/updates/meridian-uat-currency-escalation-07-14.md": "---\ntype: Update\nproposed_title: Meridian - UAT Currency Escalation 07/14\nlane: Meridian - Billing Portal\nkind: feedback\noccurred_on: 2026-07-14\nlast_updated: 2026-07-14\n---\n# Meridian - UAT Currency Escalation 07/14\n\nAdd **Meridian - UAT Currency Escalation 07/14** to the **Meridian - Billing Portal** lane.\n\nDana Whitfield (Meridian AP lead) escalated the multi-currency UAT blocker on 2026-07-14: mixed-currency invoices still post at the wrong rate.[^1] She wants a fix date before Thursday's steering call.\n\n[^1]: ctx:///mail/messages?id=fixture-meridian-uat-0714\n",
14385
+ "docs/views/inbox.loader.js": `// Inbox rows shaped for the Table view: the client/short-title split derived
14378
14386
  // from the proposed_title house style "<Client> - <thing>", and lane emitted
14379
14387
  // as its display value ("Needs a lane" when unresolved) so the spec's
14380
14388
  // variantOverride can mark it.
14381
- export default async ({ vault }) => {
14389
+ export default async ({ docs }) => {
14382
14390
  const sort = [{ direction: "desc", field: "occurred_on" }]
14383
- const rows = vault.query(
14391
+ const rows = docs.query(
14384
14392
  {
14385
14393
  find: {
14386
14394
  coll: {
@@ -14420,7 +14428,7 @@ export default async ({ vault }) => {
14420
14428
  return { items }
14421
14429
  }
14422
14430
  `,
14423
- "output/views/inbox.render-spec.json": `${JSON.stringify({ render: {
14431
+ "docs/views/inbox.render-spec.json": `${JSON.stringify({ render: {
14424
14432
  elements: {
14425
14433
  freshness: {
14426
14434
  props: {
@@ -14503,6 +14511,7 @@ export default async ({ vault }) => {
14503
14511
  root: "page"
14504
14512
  } }, null, " ")}\n`
14505
14513
  };
14514
+ const DOCS_SUBDIR = "docs";
14506
14515
  const segmentSchema = string$1().min(1, "a repoId segment must be non-empty").refine((s) => s !== "." && s !== ".." && !s.includes("/"), { message: "a repoId segment must not be '.'/'..' or contain '/'" });
14507
14516
  function segment(value, label) {
14508
14517
  const result = segmentSchema.safeParse(value);
@@ -56240,7 +56249,7 @@ function createScheduleApiClient(config) {
56240
56249
  }
56241
56250
  };
56242
56251
  }
56243
- const SERVER_VERSION = "0.5.10";
56252
+ const SERVER_VERSION = "0.5.11";
56244
56253
  const guidDescription = "GUID from the synced Postgres tables";
56245
56254
  const labelDescription = "Human-readable label rendered on the approval card";
56246
56255
  function scheduleApiFromRequest(requestInfo) {
@@ -81213,7 +81222,7 @@ var require_datascript = /* @__PURE__ */ __commonJSMin(((exports, module) => {
81213
81222
  }));
81214
81223
 
81215
81224
  //#endregion
81216
- //#region ../vault/dist/index.mjs
81225
+ //#region ../docs/dist/index.mjs
81217
81226
  var import_datascript = /* @__PURE__ */ __toESM(require_datascript(), 1);
81218
81227
  function toPosixPath(value) {
81219
81228
  return value.replaceAll("\\", "/");
@@ -81243,7 +81252,7 @@ function posixBasename(value) {
81243
81252
  function normalizeRelativePath(value) {
81244
81253
  const normalized = posixNormalize(toPosixPath(value).replace(/^\/+/, "").replace(/^\.\//, ""));
81245
81254
  if (normalized === ".") return "";
81246
- if (normalized === ".." || normalized.startsWith("../")) throw new Error(`Path escapes vault root: ${value}`);
81255
+ if (normalized === ".." || normalized.startsWith("../")) throw new Error(`Path escapes docs root: ${value}`);
81247
81256
  return normalized;
81248
81257
  }
81249
81258
  function ensureMarkdownPath(value) {
@@ -81319,13 +81328,13 @@ function extractMarkdownLinks(content, knownTargets = []) {
81319
81328
  function extractOutgoingLinks(content, knownTargets = []) {
81320
81329
  return uniqueSorted([...extractWikilinks(content), ...extractMarkdownLinks(content, knownTargets)]);
81321
81330
  }
81322
- function resolveWikilink(target, entries) {
81331
+ function resolveWikilink(target, docs) {
81323
81332
  const normalized = normalizeLinkTarget(target);
81324
81333
  const exactPath = ensureMarkdownPath(normalized);
81325
- const exact = entries.find((entry) => entry.path === exactPath);
81334
+ const exact = docs.find((doc) => doc.path === exactPath);
81326
81335
  if (exact) return exact;
81327
81336
  if (normalized.includes("/")) return null;
81328
- const matches = entries.filter((entry) => stemOf(entry.path) === normalized);
81337
+ const matches = docs.filter((doc) => stemOf(doc.path) === normalized);
81329
81338
  return matches.length === 1 ? matches[0] ?? null : null;
81330
81339
  }
81331
81340
  function unique(values) {
@@ -81334,16 +81343,16 @@ function unique(values) {
81334
81343
  function uniqueSorted(values) {
81335
81344
  return unique(values).sort((left, right) => left.localeCompare(right, void 0, { sensitivity: "base" }));
81336
81345
  }
81337
- function computeBacklinks(entries) {
81346
+ function computeBacklinks(docs) {
81338
81347
  const backlinks = /* @__PURE__ */ new Map();
81339
- for (const entry of entries) backlinks.set(normalizeLinkTarget(entry.path), backlinks.get(normalizeLinkTarget(entry.path)) ?? []);
81340
- for (const entry of entries) {
81341
- const targets = new Set([...entry.outgoingLinks, ...Object.values(entry.relationships).flat()]);
81348
+ for (const doc of docs) backlinks.set(normalizeLinkTarget(doc.path), backlinks.get(normalizeLinkTarget(doc.path)) ?? []);
81349
+ for (const doc of docs) {
81350
+ const targets = new Set([...doc.outgoingLinks, ...Object.values(doc.relationships).flat()]);
81342
81351
  for (const target of targets) {
81343
81352
  const normalized = normalizeLinkTarget(target);
81344
- addBacklink(backlinks, normalized, entry.path);
81345
- const resolved = resolveWikilink(normalized, entries);
81346
- if (resolved) addBacklink(backlinks, normalizeLinkTarget(resolved.path), entry.path);
81353
+ addBacklink(backlinks, normalized, doc.path);
81354
+ const resolved = resolveWikilink(normalized, docs);
81355
+ if (resolved) addBacklink(backlinks, normalizeLinkTarget(resolved.path), doc.path);
81347
81356
  }
81348
81357
  }
81349
81358
  for (const [target, sources] of backlinks) backlinks.set(target, sources.sort(comparePath));
@@ -81456,15 +81465,15 @@ function coerce(value) {
81456
81465
  value
81457
81466
  };
81458
81467
  }
81459
- function buildDb(entries) {
81468
+ function buildDb(docs) {
81460
81469
  const tempidByPath = /* @__PURE__ */ new Map();
81461
- entries.forEach((entry, index) => {
81462
- tempidByPath.set(entry.path, -(index + 1));
81470
+ docs.forEach((doc, index) => {
81471
+ tempidByPath.set(doc.path, -(index + 1));
81463
81472
  });
81464
81473
  const refEdges = /* @__PURE__ */ new Set();
81465
- for (const entry of entries) for (const edge of Object.keys(entry.relationships)) if (edge !== "type") refEdges.add(edge);
81474
+ for (const doc of docs) for (const edge of Object.keys(doc.relationships)) if (edge !== "type") refEdges.add(edge);
81466
81475
  const manyScalar = /* @__PURE__ */ new Set();
81467
- for (const entry of entries) for (const [key, value] of Object.entries(entry.frontmatter)) if (!RESERVED.has(key) && !refEdges.has(key) && Array.isArray(value) && value.every(isScalar)) manyScalar.add(key);
81476
+ for (const doc of docs) for (const [key, value] of Object.entries(doc.frontmatter)) if (!RESERVED.has(key) && !refEdges.has(key) && Array.isArray(value) && value.every(isScalar)) manyScalar.add(key);
81468
81477
  const schema = { path: { ":db/unique": ":db.unique/identity" } };
81469
81478
  for (const edge of refEdges) schema[edge] = {
81470
81479
  ":db/cardinality": ":db.cardinality/many",
@@ -81472,24 +81481,24 @@ function buildDb(entries) {
81472
81481
  };
81473
81482
  for (const attr of manyScalar) schema[attr] = { ":db/cardinality": ":db.cardinality/many" };
81474
81483
  const tx = [];
81475
- for (const entry of entries) {
81484
+ for (const doc of docs) {
81476
81485
  const datom = {
81477
- ":db/id": tempidByPath.get(entry.path),
81478
- path: entry.path,
81479
- title: entry.title
81486
+ ":db/id": tempidByPath.get(doc.path),
81487
+ path: doc.path,
81488
+ title: doc.title
81480
81489
  };
81481
- if (entry.type) datom.type = entry.type;
81482
- for (const [key, value] of Object.entries(entry.frontmatter)) {
81490
+ if (doc.type) datom.type = doc.type;
81491
+ for (const [key, value] of Object.entries(doc.frontmatter)) {
81483
81492
  if (RESERVED.has(key) || refEdges.has(key)) continue;
81484
81493
  const coerced = coerce(value);
81485
81494
  if (coerced.ok) datom[key] = coerced.value;
81486
81495
  }
81487
81496
  for (const edge of refEdges) {
81488
- const targets = entry.relationships[edge];
81497
+ const targets = doc.relationships[edge];
81489
81498
  if (!targets) continue;
81490
81499
  const refs = [];
81491
81500
  for (const target of targets) {
81492
- const resolved = resolveWikilink(target, entries);
81501
+ const resolved = resolveWikilink(target, docs);
81493
81502
  if (!resolved) continue;
81494
81503
  const tempid = tempidByPath.get(resolved.path);
81495
81504
  if (tempid !== void 0) refs.push(tempid);
@@ -81619,7 +81628,7 @@ function sortRows(rows, sort) {
81619
81628
  if (cmp !== 0) return spec.direction === "asc" ? cmp : -cmp;
81620
81629
  }
81621
81630
  return left.index - right.index;
81622
- }).map((entry) => entry.row);
81631
+ }).map((item) => item.row);
81623
81632
  }
81624
81633
  function getPath(row, field) {
81625
81634
  let current = row;
@@ -81629,7 +81638,7 @@ function getPath(row, field) {
81629
81638
  }
81630
81639
  return current;
81631
81640
  }
81632
- var Vault = class {
81641
+ var Docs = class {
81633
81642
  root;
81634
81643
  fs;
81635
81644
  parseConcurrency;
@@ -81650,9 +81659,9 @@ var Vault = class {
81650
81659
  const results = await mapConcurrent(walk.markdownFiles, this.parseConcurrency, async (relPath) => this.parseFile(relPath, knownPaths));
81651
81660
  const jsonResults = await mapConcurrent(walk.jsonFiles, this.parseConcurrency, async (relPath) => this.parseJsonFile(relPath));
81652
81661
  this.index = /* @__PURE__ */ new Map();
81653
- for (const result of results) if (result.kind === "entry") this.index.set(result.entry.path, result.entry);
81662
+ for (const result of results) if (result.kind === "doc") this.index.set(result.doc.path, result.doc);
81654
81663
  this.jsonIndex = /* @__PURE__ */ new Map();
81655
- for (const result of jsonResults) if (result.kind === "json") this.jsonIndex.set(result.document.path, result.document);
81664
+ for (const result of jsonResults) if (result.kind === "json") this.jsonIndex.set(result.jsonDoc.path, result.jsonDoc);
81656
81665
  this.backlinkIndex = computeBacklinks(this.all());
81657
81666
  this.queryDb = buildDb(this.all());
81658
81667
  }
@@ -81682,13 +81691,13 @@ var Vault = class {
81682
81691
  try {
81683
81692
  const stat = await this.fs.stat(this.abs(relPath));
81684
81693
  return {
81685
- entry: {
81694
+ doc: {
81686
81695
  ...parseMarkdown(await this.fs.readFile(this.abs(relPath), "utf8"), relPath, { knownPaths }),
81687
81696
  createdAt: stat.birthtimeMs,
81688
81697
  fileSize: stat.size,
81689
81698
  modifiedAt: stat.mtimeMs
81690
81699
  },
81691
- kind: "entry"
81700
+ kind: "doc"
81692
81701
  };
81693
81702
  } catch (error) {
81694
81703
  if (isNotFound(error)) return { kind: "missing" };
@@ -81701,7 +81710,7 @@ var Vault = class {
81701
81710
  const stat = await this.fs.stat(this.abs(relPath));
81702
81711
  const content = await this.fs.readFile(this.abs(relPath), "utf8");
81703
81712
  return {
81704
- document: {
81713
+ jsonDoc: {
81705
81714
  createdAt: stat.birthtimeMs,
81706
81715
  fileSize: stat.size,
81707
81716
  modifiedAt: stat.mtimeMs,
@@ -83706,7 +83715,7 @@ function osUsername() {
83706
83715
  }
83707
83716
 
83708
83717
  //#endregion
83709
- //#region ../slate-bridge/dist/goldens-Dt6RFRRi.mjs
83718
+ //#region ../slate-bridge/dist/goldens-BlwT0koM.mjs
83710
83719
  const nonEmptyStringSchema = string$1().trim().min(1);
83711
83720
  const portSchema = number$1().int().min(0).max(65535).default(7777);
83712
83721
  const envSchema = object$2({
@@ -83715,7 +83724,7 @@ const envSchema = object$2({
83715
83724
  CTX_PLATFORM_URL: url().default("http://127.0.0.1:3010").transform((url) => url.replace(/\/+$/, "")),
83716
83725
  CTX_WEB_URL: url().default("http://localhost:3000").transform((url) => url.replace(/\/+$/, "")),
83717
83726
  CTXS_BRIDGE_PORT: portSchema,
83718
- CTXS_CLAUDE_IMAGE: nonEmptyStringSchema.default("ghcr.io/usecontextlayer/ctx-sandbox:0.5.10"),
83727
+ CTXS_CLAUDE_IMAGE: nonEmptyStringSchema.default("ghcr.io/usecontextlayer/ctx-sandbox:0.5.11"),
83719
83728
  CTXS_CLAUDE_MODEL: nonEmptyStringSchema.default("opus"),
83720
83729
  CTXS_HOST_CLAUDE_JSON_PATH: nonEmptyStringSchema.default(() => path.join(os.homedir(), ".claude.json")),
83721
83730
  NODE_ENV: _enum([
@@ -83735,7 +83744,7 @@ function fetchBridgePlan(platformUrl, workspaceId, userToken) {
83735
83744
  }).plan.getBridgePlan({ workspaceId });
83736
83745
  }
83737
83746
  const CHAT_SYSTEM_PROMPT_GUEST_PATH = "/opt/ctx/chat-system-prompt.md";
83738
- const CHAT_SYSTEM_PROMPT = `Your working directory is the user's workspace. The synthesized documents live under \`./output/\` — that is the content the user sees and refers to in conversation. When the user mentions a document, project, or view, look in \`./output/\` first. Files under \`./output/\` are typed vault markdown — follow the \`vault-markdown-format\` skill when creating or editing them.
83747
+ const CHAT_SYSTEM_PROMPT = `Your working directory is the user's workspace. The synthesized documents live under \`./docs/\` — that is what the user sees and refers to in conversation. When the user mentions a document, project, or view, look in \`./docs/\` first. Files under \`./docs/\` are typed docs markdown — follow the \`docs-markdown-format\` skill when creating or editing them.
83739
83748
 
83740
83749
  You are working in the user's workspace: a directory of documents that is also a shared git repository. The synthesis engine, other sessions, and operators write to this same repository through its remote. Git is how your work persists and how everyone else learns about it — an edit that is not pushed does not exist yet.
83741
83750
 
@@ -84139,31 +84148,31 @@ const renderSpecSchema = strictObject({
84139
84148
  async function runWorkspaceView(opts) {
84140
84149
  if (!VIEW_NAME.test(opts.view)) throw new Error(`views/run: invalid view name "${opts.view}"`);
84141
84150
  return runSurfacePair({
84151
+ docs: opts.docs,
84142
84152
  missingLoader: `views/run: view "${opts.view}" has no loader (expected views/${opts.view}.loader.js)`,
84143
84153
  rawParams: opts.rawParams,
84144
84154
  resolveDsn: opts.resolveDsn,
84145
84155
  rootDir: opts.rootDir,
84146
- stem: `views/${opts.view}`,
84147
- vault: opts.vault
84156
+ stem: `views/${opts.view}`
84148
84157
  });
84149
84158
  }
84150
- async function runWorkspaceContent(opts) {
84151
- const node = opts.vault.get(opts.contentPath);
84152
- if (!node) throw new Error(`content/run: no entry at "${opts.contentPath}"`);
84153
- if (!node.type) throw new Error(`content/run: entry "${opts.contentPath}" has no type`);
84154
- const slug = kebabCase(node.type);
84159
+ async function runWorkspaceDoc(opts) {
84160
+ const doc = opts.docs.get(opts.docPath);
84161
+ if (!doc) throw new Error(`docs/run: no doc at "${opts.docPath}"`);
84162
+ if (!doc.type) throw new Error(`docs/run: doc "${opts.docPath}" has no type`);
84163
+ const slug = kebabCase(doc.type);
84155
84164
  return runSurfacePair({
84156
- missingLoader: `content/run: type "${node.type}" has no loader (expected types/${slug}.loader.js)`,
84157
- nodePath: node.path,
84165
+ docPath: doc.path,
84166
+ docs: opts.docs,
84167
+ missingLoader: `docs/run: type "${doc.type}" has no loader (expected types/${slug}.loader.js)`,
84158
84168
  rawParams: opts.rawParams,
84159
84169
  resolveDsn: opts.resolveDsn,
84160
84170
  rootDir: opts.rootDir,
84161
- stem: `types/${slug}`,
84162
- vault: opts.vault
84171
+ stem: `types/${slug}`
84163
84172
  });
84164
84173
  }
84165
84174
  async function runSurfacePair(opts) {
84166
- const base = path.join(opts.rootDir, WORKSPACE_DOCS_SUBDIR, opts.stem);
84175
+ const base = path.join(opts.rootDir, DOCS_SUBDIR, opts.stem);
84167
84176
  const loaderSource = await readLoaderSource(`${base}.loader.js`, opts.missingLoader);
84168
84177
  const def = renderSpecSchema.parse(JSON.parse(await fs$2.readFile(`${base}.render-spec.json`, "utf8")));
84169
84178
  const { defaults, effective } = resolveParams(def.params ?? {}, opts.rawParams);
@@ -84171,10 +84180,10 @@ async function runSurfacePair(opts) {
84171
84180
  try {
84172
84181
  return {
84173
84182
  data: await executeLoader(loaderSource, `${opts.stem}.loader.js`, {
84183
+ docs: docsFacade(opts.docs),
84174
84184
  params: effective,
84175
- ...opts.nodePath === void 0 ? {} : { path: opts.nodePath },
84176
- sql: sqlRunner.sql,
84177
- vault: vaultFacade(opts.vault)
84185
+ ...opts.docPath === void 0 ? {} : { path: opts.docPath },
84186
+ sql: sqlRunner.sql
84178
84187
  }),
84179
84188
  paramDefaults: defaults,
84180
84189
  params: effective,
@@ -84204,13 +84213,13 @@ function resolveParams(defs, raw) {
84204
84213
  effective
84205
84214
  };
84206
84215
  }
84207
- function vaultFacade(vault) {
84216
+ function docsFacade(docs) {
84208
84217
  return {
84209
- all: () => vault.all(),
84210
- backlinks: (target) => vault.backlinks(target),
84211
- get: (entryPath) => vault.get(entryPath),
84212
- getJson: (jsonPath) => vault.getJson(jsonPath),
84213
- query: (query, queryOpts) => vault.query(query, queryOpts)
84218
+ all: () => docs.all(),
84219
+ backlinks: (target) => docs.backlinks(target),
84220
+ get: (docPath) => docs.get(docPath),
84221
+ getJson: (jsonPath) => docs.getJson(jsonPath),
84222
+ query: (query, queryOpts) => docs.query(query, queryOpts)
84214
84223
  };
84215
84224
  }
84216
84225
  function createSqlRunner(resolveDsn) {
@@ -84245,16 +84254,16 @@ function marshalCapabilities(capabilities) {
84245
84254
  const marshalArg = (value) => value !== null && typeof value === "object" ? structuredClone(value) : value;
84246
84255
  const marshalFn = (fn) => (...args) => fn(...args.map(marshalArg));
84247
84256
  return {
84257
+ docs: {
84258
+ all: marshalFn(capabilities.docs.all),
84259
+ backlinks: marshalFn(capabilities.docs.backlinks),
84260
+ get: marshalFn(capabilities.docs.get),
84261
+ getJson: marshalFn(capabilities.docs.getJson),
84262
+ query: marshalFn(capabilities.docs.query)
84263
+ },
84248
84264
  params: capabilities.params,
84249
84265
  ...capabilities.path === void 0 ? {} : { path: capabilities.path },
84250
- sql: marshalFn(capabilities.sql),
84251
- vault: {
84252
- all: marshalFn(capabilities.vault.all),
84253
- backlinks: marshalFn(capabilities.vault.backlinks),
84254
- get: marshalFn(capabilities.vault.get),
84255
- getJson: marshalFn(capabilities.vault.getJson),
84256
- query: marshalFn(capabilities.vault.query)
84257
- }
84266
+ sql: marshalFn(capabilities.sql)
84258
84267
  };
84259
84268
  }
84260
84269
  async function executeLoader(source, filename, capabilities) {
@@ -84266,7 +84275,7 @@ async function executeLoader(source, filename, capabilities) {
84266
84275
  });
84267
84276
  new vm.Script(cjs, { filename }).runInContext(context, { timeout: EVAL_TIMEOUT_MS });
84268
84277
  const loader = module.exports;
84269
- if (typeof loader !== "function") throw new Error(`${filename}: expected \`export default async ({ vault, sql, ... }) => ...\``);
84278
+ if (typeof loader !== "function") throw new Error(`${filename}: expected \`export default async ({ docs, sql, ... }) => ...\``);
84270
84279
  const result = await loader(marshalCapabilities(capabilities));
84271
84280
  if (result === null || typeof result !== "object" || Array.isArray(result)) throw new Error(`${filename}: loader must return a JSON object (got ${Array.isArray(result) ? "an array" : typeof result})`);
84272
84281
  const data = JSON.parse(JSON.stringify(result));
@@ -84281,30 +84290,30 @@ function loaderConsole(filename) {
84281
84290
  warn: (...args) => console.warn(prefix, ...args)
84282
84291
  };
84283
84292
  }
84284
- var WorkspaceVaults = class {
84285
- vaults = /* @__PURE__ */ new Map();
84293
+ var WorkspaceDocsCache = class {
84294
+ docsByRootDir = /* @__PURE__ */ new Map();
84286
84295
  get(rootDir, head) {
84287
- const entry = this.vaults.get(rootDir);
84288
- if (entry && entry.head === head) return entry.vault;
84296
+ const cached = this.docsByRootDir.get(rootDir);
84297
+ if (cached && cached.head === head) return cached.docs;
84289
84298
  const created = (async () => {
84290
- const root = path.join(rootDir, WORKSPACE_DOCS_SUBDIR);
84299
+ const root = path.join(rootDir, DOCS_SUBDIR);
84291
84300
  await fs$2.mkdir(root, { recursive: true });
84292
- const vault = new Vault({
84301
+ const docs = new Docs({
84293
84302
  fs: fs$2,
84294
- onError: (error, context) => console.error(`[slate-bridge] vault ${rootDir} (${context}):`, error),
84303
+ onError: (error, context) => console.error(`[slate-bridge] docs ${rootDir} (${context}):`, error),
84295
84304
  root
84296
84305
  });
84297
- await vault.load();
84298
- return vault;
84306
+ await docs.load();
84307
+ return docs;
84299
84308
  })();
84300
84309
  const fresh = {
84301
- head,
84302
- vault: created
84310
+ docs: created,
84311
+ head
84303
84312
  };
84304
84313
  created.catch(() => {
84305
- if (this.vaults.get(rootDir) === fresh) this.vaults.delete(rootDir);
84314
+ if (this.docsByRootDir.get(rootDir) === fresh) this.docsByRootDir.delete(rootDir);
84306
84315
  });
84307
- this.vaults.set(rootDir, fresh);
84316
+ this.docsByRootDir.set(rootDir, fresh);
84308
84317
  return created;
84309
84318
  }
84310
84319
  };
@@ -84376,7 +84385,7 @@ async function createBridgeServer(opts) {
84376
84385
  });
84377
84386
  let boundPort = 0;
84378
84387
  const inflight = /* @__PURE__ */ new Set();
84379
- const vaults = new WorkspaceVaults();
84388
+ const docsCache = new WorkspaceDocsCache();
84380
84389
  const app = new Hono();
84381
84390
  const wss = new WebSocketServer({ noServer: true });
84382
84391
  app.use(cors());
@@ -84394,10 +84403,10 @@ async function createBridgeServer(opts) {
84394
84403
  });
84395
84404
  if (freshness === "held_diverged") noteDivergedCheckout(workspaceId);
84396
84405
  const result = await runWorkspaceView({
84406
+ docs: await docsCache.get(rootDir, head),
84397
84407
  rawParams,
84398
84408
  resolveDsn: () => resolveRenderDsn(platformUrl, workspaceId, userToken),
84399
84409
  rootDir,
84400
- vault: await vaults.get(rootDir, head),
84401
84410
  view
84402
84411
  });
84403
84412
  return c.json(result);
@@ -84405,7 +84414,7 @@ async function createBridgeServer(opts) {
84405
84414
  return c.text(err instanceof Error ? err.message : String(err), 400);
84406
84415
  }
84407
84416
  });
84408
- app.post("/bridge/workspaces/:workspaceId/content/run", renderAuth, async (c) => {
84417
+ app.post("/bridge/workspaces/:workspaceId/docs/run", renderAuth, async (c) => {
84409
84418
  const workspaceId = c.req.param("workspaceId") ?? "";
84410
84419
  const body = await c.req.json();
84411
84420
  if (typeof body.path !== "string" || body.path.length === 0) return c.text("body must carry a non-empty string `path`", 400);
@@ -84418,13 +84427,13 @@ async function createBridgeServer(opts) {
84418
84427
  token: userToken
84419
84428
  });
84420
84429
  if (freshness === "held_diverged") noteDivergedCheckout(workspaceId);
84421
- const vault = await vaults.get(rootDir, head);
84422
- const result = await runWorkspaceContent({
84423
- contentPath: body.path,
84430
+ const docs = await docsCache.get(rootDir, head);
84431
+ const result = await runWorkspaceDoc({
84432
+ docPath: body.path,
84433
+ docs,
84424
84434
  rawParams,
84425
84435
  resolveDsn: () => resolveRenderDsn(platformUrl, workspaceId, userToken),
84426
- rootDir,
84427
- vault
84436
+ rootDir
84428
84437
  });
84429
84438
  return c.json(result);
84430
84439
  } catch (err) {
@@ -88752,4 +88761,4 @@ runCli().catch((error) => {
88752
88761
  //#endregion
88753
88762
  export { createProgram, runCli };
88754
88763
  //# sourceMappingURL=cli.mjs.map
88755
- //# debugId=03f63fdf-d723-5d19-8f56-4178bb2fbd0a
88764
+ //# debugId=e80413c9-3000-584e-9bcf-799375152efc