akm-cli 0.9.13 → 0.9.14

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.
@@ -9466,6 +9466,53 @@ function projectMarkdownContent(body, truncationInfo) {
9466
9466
  truncationInfo.truncated = text.length > MARKDOWN_CONTENT_MAX_CHARS;
9467
9467
  return truncateUnicodeSafe(text, MARKDOWN_CONTENT_MAX_CHARS);
9468
9468
  }
9469
+ function setMarkdownFragmentContent(entry, content) {
9470
+ markdownFragmentProjectionEntries.add(entry);
9471
+ if (content)
9472
+ markdownFragmentContentByEntry.set(entry, content);
9473
+ }
9474
+ function getMarkdownFragmentContent(entry) {
9475
+ return markdownFragmentContentByEntry.get(entry);
9476
+ }
9477
+ function hasMarkdownFragmentContent(entry) {
9478
+ return markdownFragmentProjectionEntries.has(entry);
9479
+ }
9480
+ function projectMarkdownFragmentContent(raw) {
9481
+ const lines = raw.split(/\r?\n/);
9482
+ const parsed = parseFrontmatter(raw);
9483
+ const start = parsed.frontmatter ? parsed.bodyStartLine - 1 : 0;
9484
+ const projected = lines.map(() => "");
9485
+ let fence;
9486
+ const htmlComment = { inComment: false };
9487
+ for (let index = start;index < lines.length; index++) {
9488
+ const rawLine = lines[index];
9489
+ if (fence) {
9490
+ if (isMarkdownFenceClosing(rawLine, fence))
9491
+ fence = undefined;
9492
+ continue;
9493
+ }
9494
+ if (!htmlComment.inComment) {
9495
+ const opening2 = parseMarkdownFenceOpening(rawLine);
9496
+ if (opening2) {
9497
+ fence = opening2;
9498
+ continue;
9499
+ }
9500
+ }
9501
+ let safe = stripMarkdownHtmlComments(rawLine, htmlComment);
9502
+ const opening = parseMarkdownFenceOpening(safe.trim());
9503
+ if (opening) {
9504
+ fence = opening;
9505
+ continue;
9506
+ }
9507
+ if (/^\s*\[[^\]]+\]:\s*\S+/.test(safe) || /^\s*<[^>]+>\s*$/.test(safe))
9508
+ continue;
9509
+ safe = stripMarkdownLinkDestinations(safe).replace(/<[^>]+>/g, " ");
9510
+ projected[index] = safe.replace(/[ \t]+$/g, "");
9511
+ }
9512
+ const text = projected.join(`
9513
+ `);
9514
+ return text.trim() ? text : undefined;
9515
+ }
9469
9516
  function applyPreContributorFields(entry, file, ctx, pkgMeta) {
9470
9517
  const ext = path19.extname(file).toLowerCase();
9471
9518
  if (pkgMeta) {
@@ -9486,7 +9533,9 @@ function applyPreContributorFields(entry, file, ctx, pkgMeta) {
9486
9533
  entry.parameters = fmParams;
9487
9534
  applyWikiFrontmatter(entry, parsed.data);
9488
9535
  applyProvenanceFrontmatter(entry, parsed.data);
9489
- if (entry.type !== "env" && entry.type !== "session" && !hasSessionMemoryMarker(parsed.data, parsed.content)) {
9536
+ const safeForFragments = entry.type !== "env" && entry.type !== "session" && !hasSessionMemoryMarker(parsed.data, parsed.content);
9537
+ setMarkdownFragmentContent(entry, safeForFragments ? projectMarkdownFragmentContent(content) : undefined);
9538
+ if (safeForFragments) {
9490
9539
  const truncationInfo = { truncated: false };
9491
9540
  const contentProjection = projectMarkdownContent(parsed.content, truncationInfo);
9492
9541
  if (contentProjection) {
@@ -9604,7 +9653,7 @@ function extractDirTagsFromName(name) {
9604
9653
  }
9605
9654
  return Array.from(tags);
9606
9655
  }
9607
- var SCOPE_KEYS, KNOWN_QUALITY_VALUES, warnedUnknownQualityValues, MARKDOWN_CONTENT_MAX_CHARS = 1e6, MAX_MARKDOWN_LINK_NESTING = 32;
9656
+ var SCOPE_KEYS, KNOWN_QUALITY_VALUES, warnedUnknownQualityValues, MARKDOWN_CONTENT_MAX_CHARS = 1e6, MAX_MARKDOWN_LINK_NESTING = 32, markdownFragmentContentByEntry, markdownFragmentProjectionEntries;
9608
9657
  var init_metadata = __esm(() => {
9609
9658
  init_asset_ref();
9610
9659
  init_frontmatter();
@@ -9613,6 +9662,8 @@ var init_metadata = __esm(() => {
9613
9662
  SCOPE_KEYS = ["user", "agent", "run", "channel"];
9614
9663
  KNOWN_QUALITY_VALUES = new Set(["generated", "curated", "enriched", "proposed"]);
9615
9664
  warnedUnknownQualityValues = new Set;
9665
+ markdownFragmentContentByEntry = new WeakMap;
9666
+ markdownFragmentProjectionEntries = new WeakSet;
9616
9667
  });
9617
9668
 
9618
9669
  // src/execution/record.ts
@@ -12186,6 +12237,9 @@ var init_schema2 = __esm(() => {
12186
12237
  });
12187
12238
 
12188
12239
  // src/core/asset/markdown.ts
12240
+ function markdownHeadingSlug(heading) {
12241
+ return heading.trim().toLowerCase().replace(/<[^>]*>/g, "").replace(/[^\p{L}\p{N}\s_-]+/gu, "-").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
12242
+ }
12189
12243
  function parseMarkdownToc(content) {
12190
12244
  const lines = content.split(/\r?\n/);
12191
12245
  const headings = [];
@@ -35620,7 +35674,7 @@ var require_libvips = __commonJS((exports, module) => {
35620
35674
  SPDX-License-Identifier: Apache-2.0
35621
35675
  */
35622
35676
  var { spawnSync: spawnSync6 } = __require("child_process");
35623
- var { createHash: createHash9 } = __require("crypto");
35677
+ var { createHash: createHash10 } = __require("crypto");
35624
35678
  var semverCoerce = require_coerce2();
35625
35679
  var semverGreaterThanOrEqualTo = require_gte2();
35626
35680
  var semverSatisfies = require_satisfies2();
@@ -35708,7 +35762,7 @@ var require_libvips = __commonJS((exports, module) => {
35708
35762
  }
35709
35763
  return false;
35710
35764
  };
35711
- var sha512 = (s) => createHash9("sha512").update(s).digest("hex");
35765
+ var sha512 = (s) => createHash10("sha512").update(s).digest("hex");
35712
35766
  var yarnLocator = () => {
35713
35767
  try {
35714
35768
  const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
@@ -50000,10 +50054,10 @@ var import_sharp, __dirname = "/home/runner/work/akm/akm/node_modules/@huggingfa
50000
50054
  if (cached) {
50001
50055
  return cached.text();
50002
50056
  }
50003
- const hash3 = await this._getLfsFileHash(url2);
50004
- if (hash3) {
50005
- await hashCache.put(url2, new Response(hash3));
50006
- return hash3;
50057
+ const hash4 = await this._getLfsFileHash(url2);
50058
+ if (hash4) {
50059
+ await hashCache.put(url2, new Response(hash4));
50060
+ return hash4;
50007
50061
  }
50008
50062
  return null;
50009
50063
  } catch {
@@ -78574,7 +78628,7 @@ function listTxnJournalsTolerant(predicate) {
78574
78628
  }
78575
78629
 
78576
78630
  // src/commands/proposal/repository.ts
78577
- import { createHash as createHash9, randomUUID as randomUUID6 } from "crypto";
78631
+ import { createHash as createHash10, randomUUID as randomUUID6 } from "crypto";
78578
78632
  import fs49 from "fs";
78579
78633
  init_dist();
78580
78634
  import path61 from "path";
@@ -79482,20 +79536,41 @@ function buildWorkflowAction(ref) {
79482
79536
  return `Start or resume execution with \`akm workflow run ${shellQuote(ref)}\`.`;
79483
79537
  }
79484
79538
  var TYPE_PRESENTATION = {
79485
- skill: { label: "Skill", renderer: "skill-md", action: (ref) => `akm show ${ref} -> follow the instructions` },
79539
+ skill: {
79540
+ label: "Skill",
79541
+ renderer: "skill-md",
79542
+ action: (ref) => `akm show ${ref} -> follow the instructions`,
79543
+ fragmentRef: false
79544
+ },
79486
79545
  command: {
79487
79546
  label: "Command",
79488
79547
  renderer: "command-md",
79489
- action: (ref) => `akm show ${ref} -> fill placeholders and dispatch`
79548
+ action: (ref) => `akm show ${ref} -> fill placeholders and dispatch`,
79549
+ fragmentRef: false
79550
+ },
79551
+ agent: {
79552
+ label: "Agent",
79553
+ renderer: "agent-md",
79554
+ action: (ref) => `akm show ${ref} -> dispatch with full prompt`,
79555
+ fragmentRef: false
79490
79556
  },
79491
- agent: { label: "Agent", renderer: "agent-md", action: (ref) => `akm show ${ref} -> dispatch with full prompt` },
79492
79557
  knowledge: {
79493
79558
  label: "Knowledge",
79494
79559
  renderer: "knowledge-md",
79495
79560
  action: (ref) => `akm show ${ref} -> read reference material`
79496
79561
  },
79497
- workflow: { label: "Workflow", renderer: "workflow-md", action: (ref) => buildWorkflowAction(ref) },
79498
- script: { label: "Script", renderer: "script-source", action: (ref) => `akm show ${ref} -> execute the run command` },
79562
+ workflow: {
79563
+ label: "Workflow",
79564
+ renderer: "workflow-md",
79565
+ action: (ref) => buildWorkflowAction(ref),
79566
+ fragmentRef: false
79567
+ },
79568
+ script: {
79569
+ label: "Script",
79570
+ renderer: "script-source",
79571
+ action: (ref) => `akm show ${ref} -> execute the run command`,
79572
+ fragmentRef: false
79573
+ },
79499
79574
  memory: { label: "Memory", renderer: "memory-md", action: (ref) => `akm show ${ref} -> recall context` },
79500
79575
  env: {
79501
79576
  label: "Env",
@@ -79515,7 +79590,8 @@ var TYPE_PRESENTATION = {
79515
79590
  task: {
79516
79591
  label: "Task",
79517
79592
  renderer: "task-yaml",
79518
- action: (ref) => `akm show ${ref} -> inspect; akm task run <id> -> run now; edit the file + akm task sync -> unschedule`
79593
+ action: (ref) => `akm show ${ref} -> inspect; akm task run <id> -> run now; edit the file + akm task sync -> unschedule`,
79594
+ fragmentRef: false
79519
79595
  },
79520
79596
  session: {
79521
79597
  label: "Session",
@@ -79530,7 +79606,8 @@ var TYPE_PRESENTATION = {
79530
79606
  instruction: {
79531
79607
  label: "Instruction",
79532
79608
  renderer: "knowledge-md",
79533
- action: (ref) => `akm show ${ref} -> read the project instructions`
79609
+ action: (ref) => `akm show ${ref} -> read the project instructions`,
79610
+ fragmentRef: false
79534
79611
  }
79535
79612
  };
79536
79613
  var DEFAULT_PRESENTATION = { label: "Asset" };
@@ -82456,6 +82533,8 @@ function indexDocumentFromEntry(entry, base3, rendererName) {
82456
82533
  doc.lessonStrength = entry.lessonStrength;
82457
82534
  if (entry.derivedFrom !== undefined)
82458
82535
  doc.derivedFrom = entry.derivedFrom;
82536
+ if (hasMarkdownFragmentContent(entry))
82537
+ setMarkdownFragmentContent(doc, getMarkdownFragmentContent(entry));
82459
82538
  return doc;
82460
82539
  }
82461
82540
  function conceptIdForRecognizedType(root, filePath, type) {
@@ -92673,7 +92752,7 @@ init_paths();
92673
92752
  init_warn();
92674
92753
 
92675
92754
  // src/storage/repositories/index-entry-schema.ts
92676
- var CANONICAL_INDEX_DB_VERSION = 22;
92755
+ var CANONICAL_INDEX_DB_VERSION = 23;
92677
92756
  var CANONICAL_ENTRY_SCHEMA_FINGERPRINT = {
92678
92757
  tableSql: "CREATE TABLE entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, item_ref TEXT NOT NULL UNIQUE, bundle_id TEXT NOT NULL, component_id TEXT NOT NULL, concept_id TEXT NOT NULL, adapter_id TEXT NOT NULL, type TEXT NOT NULL, file_path TEXT NOT NULL, content_hash TEXT, document_json TEXT NOT NULL, search_text TEXT NOT NULL, derived_from TEXT )",
92679
92758
  sqliteSequenceTable: true,
@@ -92823,7 +92902,12 @@ var CANONICAL_ENTRY_SCHEMA_FINGERPRINT = {
92823
92902
  { sequence: 1, cid: -1, name: null, descending: 0, collation: "BINARY", key: 0 }
92824
92903
  ]
92825
92904
  }
92826
- ]
92905
+ ],
92906
+ searchSurfaces: {
92907
+ entriesFtsSql: "CREATE VIRTUAL TABLE entries_fts USING fts5( entry_id UNINDEXED, name, description, tags, hints, content, tokenize='porter unicode61' )",
92908
+ fragmentSourceSql: "CREATE TABLE entry_fragments ( entry_id INTEGER PRIMARY KEY REFERENCES entries(id) ON DELETE CASCADE, safe_markdown TEXT NOT NULL )",
92909
+ fragmentsFtsSql: "CREATE VIRTUAL TABLE entry_fragments_fts USING fts5( entry_id UNINDEXED, fragment_id UNINDEXED, fragment_ordinal UNINDEXED, content, tokenize='porter unicode61' )"
92910
+ }
92827
92911
  };
92828
92912
  function sqlString(value) {
92829
92913
  return `'${value.replaceAll("'", "''")}'`;
@@ -92833,8 +92917,11 @@ function normalizeSchemaSql(value) {
92833
92917
  return null;
92834
92918
  return value.replace(/\s+/g, " ").trim();
92835
92919
  }
92920
+ function readNamedTableSql(db, name) {
92921
+ const row = db.prepare(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ${sqlString(name)}`).get();
92922
+ return normalizeSchemaSql(row?.sql);
92923
+ }
92836
92924
  function readEntrySchemaFingerprint(db) {
92837
- const tableRow = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'entries'").get();
92838
92925
  const sqliteSequenceTable = db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_sequence'").get() != null;
92839
92926
  const maxId = Number(db.prepare("SELECT COALESCE(MAX(id), 0) AS maxId FROM entries").get().maxId);
92840
92927
  const sequenceRow = sqliteSequenceTable ? db.prepare("SELECT seq FROM sqlite_sequence WHERE name = 'entries'").get() : undefined;
@@ -92863,11 +92950,16 @@ function readEntrySchemaFingerprint(db) {
92863
92950
  }))
92864
92951
  })).sort((left, right) => left.name.localeCompare(right.name));
92865
92952
  return {
92866
- tableSql: normalizeSchemaSql(tableRow?.sql),
92953
+ tableSql: readNamedTableSql(db, "entries"),
92867
92954
  sqliteSequenceTable,
92868
92955
  sqliteSequenceValid,
92869
92956
  columns,
92870
- indexes
92957
+ indexes,
92958
+ searchSurfaces: {
92959
+ entriesFtsSql: readNamedTableSql(db, "entries_fts"),
92960
+ fragmentSourceSql: readNamedTableSql(db, "entry_fragments"),
92961
+ fragmentsFtsSql: readNamedTableSql(db, "entry_fragments_fts")
92962
+ }
92871
92963
  };
92872
92964
  }
92873
92965
  function hasCanonicalEntrySchema(db) {
@@ -93070,20 +93162,30 @@ function openExistingDatabase(dbPath) {
93070
93162
  if (classifyPathAccess(resolvedPath).access === "absent") {
93071
93163
  throw new Error(`Index database not found at ${resolvedPath}. Run 'akm index' to build it.`);
93072
93164
  }
93073
- return openManagedDatabase({
93165
+ const db = openManagedDatabase({
93074
93166
  path: resolvedPath,
93075
- init: (db) => {
93076
- loadVecExtension(db);
93077
- warnIfNonCanonicalIndexGeneration(db, resolvedPath);
93167
+ init: (db2) => {
93168
+ loadVecExtension(db2);
93078
93169
  },
93079
93170
  create: false
93080
93171
  });
93172
+ try {
93173
+ assertCanonicalIndexGeneration(db, resolvedPath);
93174
+ return db;
93175
+ } catch (error2) {
93176
+ db.close();
93177
+ throw error2;
93178
+ }
93081
93179
  }
93082
- function warnIfNonCanonicalIndexGeneration(db, resolvedPath) {
93180
+ function assertCanonicalIndexGeneration(db, resolvedPath) {
93083
93181
  if (isCanonicalIndexGeneration(db))
93084
93182
  return;
93085
93183
  const classification = classifyIndexGeneration(db);
93086
- warnOnce(`index-read-noncanonical:${resolvedPath}`, `Index database at ${resolvedPath} does not match this akm's derived schema (stored generation ` + `${classification.storedVersion ?? "unknown"}; this binary understands ${CANONICAL_INDEX_DB_VERSION}). ` + "Reading it as-is; a query that needs a table or column this generation lacks will fail on its own. " + "Run 'akm index' to rebuild it for this binary.");
93184
+ const stored = classification.storedVersion ?? "unknown";
93185
+ if (classification.status === "newer") {
93186
+ throw new ConfigError(`Index database at ${resolvedPath} was built by a newer akm (stored generation ${stored}; ` + `this binary understands ${CANONICAL_INDEX_DB_VERSION}). Upgrade akm to use this index.`, "INDEX_SCHEMA_INCOMPATIBLE", "Upgrade akm to a version that understands this index generation.");
93187
+ }
93188
+ throw new ConfigError(`Index database at ${resolvedPath} is not usable with this akm's derived schema (stored generation ${stored}; ` + `this binary understands ${CANONICAL_INDEX_DB_VERSION}). Run 'akm index' to rebuild it.`, "INDEX_SCHEMA_INCOMPATIBLE", "Run `akm index` to rebuild the derived index from the currently materialized sources.");
93087
93189
  }
93088
93190
  function assertIndexPathReadable(resolvedPath) {
93089
93191
  const { access, code } = classifyPathAccess(resolvedPath);
@@ -93100,6 +93202,7 @@ import fs40 from "fs";
93100
93202
  init_asset_ref();
93101
93203
  init_resolve_ref();
93102
93204
  init_warn();
93205
+ init_metadata();
93103
93206
 
93104
93207
  // src/indexer/search/search-fields.ts
93105
93208
  function buildSearchFields(entry) {
@@ -93163,9 +93266,129 @@ function buildSearchText(entry) {
93163
93266
  // src/storage/repositories/index-entry-mapper.ts
93164
93267
  init_warn();
93165
93268
 
93269
+ // src/core/asset/markdown-fragments.ts
93270
+ init_markdown();
93271
+ import { createHash as createHash9 } from "crypto";
93272
+ var MARKDOWN_FRAGMENT_MAX_CHARS = 1600;
93273
+ var MARKDOWN_FRAGMENT_PREFIX = "akm-fragment-";
93274
+ function hash3(text) {
93275
+ return createHash9("sha256").update(text).digest("hex");
93276
+ }
93277
+ function uniqueSlugs(body) {
93278
+ const out = new Map;
93279
+ const seen = new Set;
93280
+ for (const heading of parseMarkdownToc(body).headings) {
93281
+ const base3 = markdownHeadingSlug(heading.text);
93282
+ if (!base3)
93283
+ continue;
93284
+ let slug = base3;
93285
+ for (let suffix = 1;seen.has(slug); suffix++)
93286
+ slug = `${base3}-${suffix}`;
93287
+ seen.add(slug);
93288
+ out.set(heading.line, slug);
93289
+ }
93290
+ return out;
93291
+ }
93292
+ function textOf(lines) {
93293
+ return lines.join(`
93294
+ `).trim();
93295
+ }
93296
+ function splitPiece(piece, maxChars) {
93297
+ if (textOf(piece.lines).length <= maxChars)
93298
+ return [piece];
93299
+ const pieces = [];
93300
+ let start = 0;
93301
+ while (start < piece.lines.length) {
93302
+ let end = start;
93303
+ let chars = 0;
93304
+ while (end < piece.lines.length) {
93305
+ const next2 = piece.lines[end];
93306
+ if (end === start && next2.length > maxChars)
93307
+ break;
93308
+ if (end > start && chars + next2.length + 1 > maxChars)
93309
+ break;
93310
+ chars += next2.length + (end > start ? 1 : 0);
93311
+ end++;
93312
+ }
93313
+ if (end === start) {
93314
+ const line = piece.lines[start];
93315
+ let offset = 0;
93316
+ while (offset < line.length) {
93317
+ let cut = Math.min(offset + maxChars, line.length);
93318
+ if (cut < line.length) {
93319
+ const space = line.lastIndexOf(" ", cut);
93320
+ if (space > offset + Math.floor(maxChars * 0.55))
93321
+ cut = space;
93322
+ }
93323
+ pieces.push({ lines: [line.slice(offset, cut).trim()], startLine: piece.startLine + start });
93324
+ offset = cut;
93325
+ while (line[offset] === " ")
93326
+ offset++;
93327
+ }
93328
+ start++;
93329
+ continue;
93330
+ }
93331
+ let preferred = -1;
93332
+ for (let i = start + 1;i < end; i++)
93333
+ if (!piece.lines[i].trim())
93334
+ preferred = i;
93335
+ if (preferred > start)
93336
+ end = preferred;
93337
+ pieces.push({ lines: piece.lines.slice(start, end), startLine: piece.startLine + start });
93338
+ start = end;
93339
+ while (start < piece.lines.length && !piece.lines[start].trim())
93340
+ start++;
93341
+ }
93342
+ return pieces.filter((candidate) => textOf(candidate.lines));
93343
+ }
93344
+ function splitMarkdownFragmentStats(body, maxChars = MARKDOWN_FRAGMENT_MAX_CHARS) {
93345
+ const lines = body.split(/\r?\n/);
93346
+ const headings = parseMarkdownToc(body).headings;
93347
+ const boundaries = [1, ...headings.map((heading) => heading.line), lines.length + 1].filter((line, index, all) => index === 0 || line !== all[index - 1]).sort((left, right) => left - right);
93348
+ const slugs = uniqueSlugs(body);
93349
+ const pieces = [];
93350
+ let sectionCount = 0;
93351
+ for (let i = 0;i < boundaries.length - 1; i++) {
93352
+ const startLine = boundaries[i];
93353
+ const end = boundaries[i + 1] - 1;
93354
+ const section = { lines: lines.slice(startLine - 1, end), startLine, headingSlug: slugs.get(startLine) };
93355
+ if (textOf(section.lines)) {
93356
+ sectionCount++;
93357
+ pieces.push(...splitPiece(section, maxChars));
93358
+ }
93359
+ }
93360
+ const piecesPerHeading = new Map;
93361
+ for (const piece of pieces) {
93362
+ if (piece.headingSlug)
93363
+ piecesPerHeading.set(piece.headingSlug, (piecesPerHeading.get(piece.headingSlug) ?? 0) + 1);
93364
+ }
93365
+ const fragments = pieces.map((piece, ordinal) => {
93366
+ const text = textOf(piece.lines);
93367
+ const contentLines = piece.lines.map((line, index) => ({ line, index })).filter(({ line }) => line.trim());
93368
+ const first = contentLines[0]?.index ?? 0;
93369
+ const last = contentLines.at(-1)?.index ?? 0;
93370
+ const digest = hash3(text);
93371
+ const unsplitHeading = piece.headingSlug && piecesPerHeading.get(piece.headingSlug) === 1;
93372
+ return {
93373
+ fragmentId: `${MARKDOWN_FRAGMENT_PREFIX}${ordinal + 1}-${digest.slice(0, 12)}`,
93374
+ ordinal,
93375
+ startLine: piece.startLine + first,
93376
+ endLine: piece.startLine + last,
93377
+ ...unsplitHeading ? { headingSlug: piece.headingSlug } : {},
93378
+ text,
93379
+ hash: digest
93380
+ };
93381
+ });
93382
+ return { fragments, hardSplitCount: Math.max(0, pieces.length - sectionCount) };
93383
+ }
93384
+ function splitMarkdownFragments(body, maxChars = MARKDOWN_FRAGMENT_MAX_CHARS) {
93385
+ return splitMarkdownFragmentStats(body, maxChars).fragments;
93386
+ }
93387
+
93166
93388
  // src/storage/repositories/index-fts-repository.ts
93167
93389
  init_warn();
93168
93390
  var INSERT_FTS_SQL = "INSERT INTO entries_fts (entry_id, name, description, tags, hints, content) VALUES (?, ?, ?, ?, ?, ?)";
93391
+ var INSERT_FRAGMENT_SQL = "INSERT INTO entry_fragments_fts (entry_id, fragment_id, fragment_ordinal, content) VALUES (?, ?, ?, ?)";
93169
93392
  var ftsMutationStatementsByDb = new WeakMap;
93170
93393
  function getFtsMutationStatements(db) {
93171
93394
  const existing = ftsMutationStatementsByDb.get(db);
@@ -93173,22 +93396,39 @@ function getFtsMutationStatements(db) {
93173
93396
  return existing;
93174
93397
  const statements = {
93175
93398
  deleteOne: db.prepare("DELETE FROM entries_fts WHERE entry_id = ?"),
93176
- insert: db.prepare(INSERT_FTS_SQL)
93399
+ insert: db.prepare(INSERT_FTS_SQL),
93400
+ deleteFragments: db.prepare("DELETE FROM entry_fragments_fts WHERE entry_id = ?"),
93401
+ upsertFragmentSource: db.prepare("INSERT INTO entry_fragments (entry_id, safe_markdown) VALUES (?, ?) ON CONFLICT(entry_id) DO UPDATE SET safe_markdown = excluded.safe_markdown"),
93402
+ deleteFragmentSource: db.prepare("DELETE FROM entry_fragments WHERE entry_id = ?"),
93403
+ insertFragment: db.prepare(INSERT_FRAGMENT_SQL)
93177
93404
  };
93178
93405
  ftsMutationStatementsByDb.set(db, statements);
93179
93406
  return statements;
93180
93407
  }
93181
- function replaceFtsEntry(db, entryId, entry) {
93408
+ function replaceFtsEntry(db, entryId, entry, fragmentContent) {
93182
93409
  const fields = buildSearchFields(entry);
93183
93410
  const statements = getFtsMutationStatements(db);
93184
93411
  statements.deleteOne.run(entryId);
93185
93412
  statements.insert.run(entryId, fields.name, fields.description, fields.tags, fields.hints, fields.content);
93413
+ if (fragmentContent === undefined) {
93414
+ return;
93415
+ }
93416
+ statements.deleteFragments.run(entryId);
93417
+ statements.deleteFragmentSource.run(entryId);
93418
+ if (!fragmentContent)
93419
+ return;
93420
+ statements.upsertFragmentSource.run(entryId, fragmentContent);
93421
+ for (const fragment of splitMarkdownFragments(fragmentContent)) {
93422
+ statements.insertFragment.run(entryId, fragment.fragmentId, fragment.ordinal, fragment.text.toLowerCase());
93423
+ }
93186
93424
  }
93187
93425
  function deleteFtsEntries(db, entryIds) {
93188
93426
  for (let i = 0;i < entryIds.length; i += SQLITE_CHUNK_SIZE) {
93189
93427
  const chunk = entryIds.slice(i, i + SQLITE_CHUNK_SIZE);
93190
93428
  const placeholders = chunk.map(() => "?").join(",");
93191
93429
  db.prepare(`DELETE FROM entries_fts WHERE entry_id IN (${placeholders})`).run(...chunk);
93430
+ db.prepare(`DELETE FROM entry_fragments_fts WHERE entry_id IN (${placeholders})`).run(...chunk);
93431
+ db.prepare(`DELETE FROM entry_fragments WHERE entry_id IN (${placeholders})`).run(...chunk);
93192
93432
  }
93193
93433
  }
93194
93434
 
@@ -93203,7 +93443,7 @@ function upsertEntry(db, filePath, entry, searchText, provenance, contentHash) {
93203
93443
  throw new Error("upsertEntry: item_ref not found after upsert");
93204
93444
  if (previous?.id === result.id && previous.search_text !== searchText)
93205
93445
  deleteEntryVectors(db, result.id);
93206
- replaceFtsEntry(db, result.id, entry);
93446
+ replaceFtsEntry(db, result.id, entry, hasMarkdownFragmentContent(entry) ? getMarkdownFragmentContent(entry) ?? null : undefined);
93207
93447
  return result.id;
93208
93448
  };
93209
93449
  return db.transaction(apply)();
@@ -94217,6 +94457,9 @@ function publishTargetedEmbeddingMeta(db, config) {
94217
94457
  setMeta(db, "hasEmbeddings", ready ? "1" : "0");
94218
94458
  }
94219
94459
 
94460
+ // src/indexer/index-written-assets.ts
94461
+ init_metadata();
94462
+
94220
94463
  // src/indexer/scan/drain-dir.ts
94221
94464
  import path58 from "path";
94222
94465
  init_common();
@@ -94279,52 +94522,56 @@ function workflowNameForSourcePath(sourceRoot, adapterId, sourcePath) {
94279
94522
  return;
94280
94523
  return ownedPath;
94281
94524
  }
94282
- function listWorkflowSourceFiles(sourceRoot, adapterId, name) {
94525
+ function resolveWorkflowSourceDomains(sourceRoot, adapterId, sourcePaths) {
94283
94526
  if (adapterId !== "akm" && adapterId !== "akm-workflow")
94284
94527
  return [];
94285
- const canonicalName = canonicalizeWorkflowName(normalizeName2(name));
94286
- if (!isSafeRelativeName(canonicalName)) {
94287
- throw new UsageError("Workflow ref resolves outside the bundle root.", "PATH_ESCAPE_VIOLATION");
94288
- }
94289
- let realRoot;
94290
94528
  const authoredRoot = path55.resolve(sourceRoot);
94529
+ let realRoot;
94291
94530
  try {
94292
94531
  realRoot = fs46.realpathSync(authoredRoot);
94293
94532
  } catch {
94294
94533
  return [];
94295
94534
  }
94296
- const ownershipRoot = adapterId === "akm" ? path55.join(authoredRoot, "workflows") : authoredRoot;
94297
- const parent = path55.join(ownershipRoot, path55.dirname(canonicalName));
94298
- if (!isWithinResolved(parent, authoredRoot)) {
94299
- throw new UsageError("Workflow ref resolves outside the bundle root.", "PATH_ESCAPE_VIOLATION");
94300
- }
94301
- let entries;
94302
- try {
94303
- entries = fs46.readdirSync(parent, { withFileTypes: true });
94304
- } catch {
94305
- return [];
94306
- }
94307
- const basename = path55.basename(canonicalName);
94308
- const candidates = [];
94309
- for (const entry of entries) {
94310
- if (!entry.isFile() && !entry.isSymbolicLink())
94535
+ const candidatesByName = new Map;
94536
+ const seenAuthoredPaths = new Set;
94537
+ for (const sourcePath of sourcePaths) {
94538
+ const normalizedSourcePath = path55.resolve(sourcePath);
94539
+ if (seenAuthoredPaths.has(normalizedSourcePath))
94540
+ continue;
94541
+ seenAuthoredPaths.add(normalizedSourcePath);
94542
+ const authoredName = workflowNameForSourcePath(authoredRoot, adapterId, normalizedSourcePath);
94543
+ if (authoredName === undefined)
94311
94544
  continue;
94312
- const extension = path55.extname(entry.name);
94545
+ const canonicalName = canonicalizeWorkflowName(authoredName);
94546
+ if (!isSafeRelativeName(canonicalName))
94547
+ continue;
94548
+ const extension = path55.extname(normalizedSourcePath);
94313
94549
  const lowerExtension = extension.toLowerCase();
94314
94550
  if (!WORKFLOW_EXTENSIONS.includes(lowerExtension))
94315
94551
  continue;
94316
- if (entry.name.slice(0, -extension.length) !== basename)
94317
- continue;
94318
- const candidatePath = path55.join(parent, entry.name);
94319
- candidates.push({
94320
- path: candidatePath,
94321
- relativePath: toPosix(path55.relative(authoredRoot, candidatePath)),
94552
+ const candidate = {
94553
+ path: normalizedSourcePath,
94554
+ relativePath: toPosix(path55.relative(authoredRoot, normalizedSourcePath)),
94322
94555
  lowerExtension,
94323
- extensionlessStem: entry.name.slice(0, -extension.length)
94556
+ extensionlessStem: path55.basename(normalizedSourcePath).slice(0, -extension.length)
94557
+ };
94558
+ const domain = candidatesByName.get(canonicalName) ?? [];
94559
+ domain.push(candidate);
94560
+ candidatesByName.set(canonicalName, domain);
94561
+ }
94562
+ const resolutions = [];
94563
+ for (const canonicalName of [...candidatesByName.keys()].sort(compareCodePoints)) {
94564
+ const candidates = candidatesByName.get(canonicalName) ?? [];
94565
+ candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
94566
+ const sources = inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
94567
+ const sourcePaths2 = candidates.map((candidate) => candidate.relativePath);
94568
+ resolutions.push({
94569
+ canonicalName,
94570
+ sourcePaths: sourcePaths2,
94571
+ source: pickWorkflowSource(adapterId, canonicalName, sources)
94324
94572
  });
94325
94573
  }
94326
- candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
94327
- return inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
94574
+ return resolutions;
94328
94575
  }
94329
94576
  function inspectWorkflowSourceDomain(candidates, canonicalName, realRoot) {
94330
94577
  const sources = [];
@@ -94398,14 +94645,6 @@ function inspectWorkflowSourceCandidate(candidate, canonicalName, realRoot) {
94398
94645
  }
94399
94646
  };
94400
94647
  }
94401
- function resolveUniqueWorkflowSource(sourceRoot, adapterId, name) {
94402
- const sources = listWorkflowSourceFiles(sourceRoot, adapterId, name);
94403
- const canonicalName = sources[0]?.canonicalName ?? canonicalizeWorkflowName(normalizeName2(name));
94404
- return pickWorkflowSource(adapterId, canonicalName, sources);
94405
- }
94406
- function normalizeName2(name) {
94407
- return name.replaceAll("\\", "/");
94408
- }
94409
94648
  function isSafeRelativeName(name) {
94410
94649
  return name.length > 0 && !path55.posix.isAbsolute(name) && name !== ".." && !name.startsWith("../") && path55.posix.normalize(name) === name;
94411
94650
  }
@@ -94420,6 +94659,7 @@ init_metadata();
94420
94659
  init_file_context();
94421
94660
 
94422
94661
  // src/indexer/scan/doc-to-entry.ts
94662
+ init_metadata();
94423
94663
  import path57 from "path";
94424
94664
  function indexDocumentToStashEntry(doc) {
94425
94665
  const dj = doc.documentJson ?? {};
@@ -94436,6 +94676,8 @@ function indexDocumentToStashEntry(doc) {
94436
94676
  entry.content = doc.content;
94437
94677
  if (doc.contentTruncated !== undefined)
94438
94678
  entry.contentTruncated = doc.contentTruncated;
94679
+ if (hasMarkdownFragmentContent(doc))
94680
+ setMarkdownFragmentContent(entry, getMarkdownFragmentContent(doc));
94439
94681
  if (doc.ownsPresentation !== undefined)
94440
94682
  entry.ownsPresentation = doc.ownsPresentation;
94441
94683
  if (doc.updated !== undefined)
@@ -94529,31 +94771,28 @@ function drainDirDocuments(adapter, component, fileContexts) {
94529
94771
  const conceptIdByFile = new Map;
94530
94772
  const rejectedPaths = new Set;
94531
94773
  const rejectedConceptIds = new Set;
94532
- const workflowLookups = new Map;
94533
- for (const file of fileContexts) {
94774
+ const workflowOwnerPathByCanonicalName = new Map(resolveWorkflowSourceDomains(component.root, adapter.id, fileContexts.map((file) => file.absPath)).filter((resolution) => resolution.source !== undefined).map((resolution) => [resolution.canonicalName, path58.resolve(resolution.source.path)]));
94775
+ const invalidWorkflowOwnerNames = new Set;
94776
+ const orderedFileContexts = [...fileContexts].sort((left, right) => {
94777
+ const leftName = workflowNameForSourcePath(component.root, adapter.id, left.absPath);
94778
+ const rightName = workflowNameForSourcePath(component.root, adapter.id, right.absPath);
94779
+ const leftOwner = leftName !== undefined && workflowOwnerPathByCanonicalName.get(canonicalizeWorkflowName(leftName)) === path58.resolve(left.absPath);
94780
+ const rightOwner = rightName !== undefined && workflowOwnerPathByCanonicalName.get(canonicalizeWorkflowName(rightName)) === path58.resolve(right.absPath);
94781
+ if (leftOwner !== rightOwner)
94782
+ return leftOwner ? -1 : 1;
94783
+ return compareCodePoints(left.absPath, right.absPath);
94784
+ });
94785
+ for (const file of orderedFileContexts) {
94786
+ if (rejectedPaths.has(file.absPath))
94787
+ continue;
94534
94788
  const workflowName = workflowNameForSourcePath(component.root, adapter.id, file.absPath);
94535
94789
  if (workflowName !== undefined) {
94536
94790
  const canonicalName = canonicalizeWorkflowName(workflowName);
94537
- if (!workflowLookups.has(canonicalName))
94538
- workflowLookups.set(canonicalName, workflowName);
94539
- }
94540
- }
94541
- for (const [canonicalName, workflowName] of [...workflowLookups].sort(([left], [right]) => compareCodePoints(left, right))) {
94542
- try {
94543
- resolveUniqueWorkflowSource(component.root, adapter.id, workflowName);
94544
- } catch (error2) {
94545
- if (!(error2 instanceof WorkflowSourceRejectionError))
94546
- throw error2;
94547
- rejectedConceptIds.add(adapter.id === "akm" ? `workflows/${canonicalName}` : canonicalName);
94548
- for (const relativePath of error2.sourcePaths) {
94549
- rejectedPaths.add(path58.join(component.root, relativePath));
94791
+ const ownerPath = workflowOwnerPathByCanonicalName.get(canonicalName);
94792
+ if (ownerPath !== undefined && ownerPath !== path58.resolve(file.absPath) && !invalidWorkflowOwnerNames.has(canonicalName)) {
94793
+ continue;
94550
94794
  }
94551
- warnings.push(error2.message);
94552
94795
  }
94553
- }
94554
- for (const file of fileContexts) {
94555
- if (rejectedPaths.has(file.absPath))
94556
- continue;
94557
94796
  const doc = adapter.recognize(component, file);
94558
94797
  if (doc === null)
94559
94798
  continue;
@@ -94565,6 +94804,8 @@ function drainDirDocuments(adapter, component, fileContexts) {
94565
94804
  const dropWarning = handleWorkflowDoc(doc, file, component.root);
94566
94805
  if (dropWarning !== null) {
94567
94806
  warnings.push(dropWarning);
94807
+ if (workflowName !== undefined)
94808
+ invalidWorkflowOwnerNames.add(canonicalizeWorkflowName(workflowName));
94568
94809
  continue;
94569
94810
  }
94570
94811
  if (doc.hash !== undefined)
@@ -94677,6 +94918,9 @@ async function indexWrittenAssets(stashDir, filePaths, options = {}) {
94677
94918
  let entryWithSize = entry;
94678
94919
  try {
94679
94920
  entryWithSize = { ...entry, fileSize: fs48.statSync(file).size };
94921
+ if (hasMarkdownFragmentContent(entry)) {
94922
+ setMarkdownFragmentContent(entryWithSize, getMarkdownFragmentContent(entry));
94923
+ }
94680
94924
  } catch {}
94681
94925
  const provenance = deriveEntryProvenance({ bundleId: component.id, componentId: component.id, adapterId: component.adapter }, entry.type, entry.name, conceptId);
94682
94926
  const supersededIds = db.prepare("SELECT id FROM entries WHERE file_path = ? AND item_ref <> ?").all(file, provenance.itemRef);
@@ -95536,7 +95780,7 @@ var PROPOSAL_TXN_PHASES = [
95536
95780
  "committed"
95537
95781
  ];
95538
95782
  function proposalHash(content) {
95539
- return createHash9("sha256").update(content).digest("hex");
95783
+ return createHash10("sha256").update(content).digest("hex");
95540
95784
  }
95541
95785
  function proposalFileHash(filePath) {
95542
95786
  return proposalHash(fs49.readFileSync(filePath));