akm-cli 0.9.12 → 0.9.14-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/assets/workflows/workflow-template.md +4 -0
  3. package/dist/commands/improve/eligibility.js +27 -15
  4. package/dist/commands/improve/improve.js +1 -0
  5. package/dist/commands/lint/base-linter.js +10 -0
  6. package/dist/commands/proposal/drain.js +48 -6
  7. package/dist/commands/proposal/proposal-cli.js +1 -0
  8. package/dist/commands/read/curate.js +3 -2
  9. package/dist/commands/read/show.js +26 -9
  10. package/dist/core/adapter/adapters/akm-adapter.js +5 -1
  11. package/dist/core/asset/markdown-fragments.js +146 -0
  12. package/dist/core/config/config-walker.js +7 -3
  13. package/dist/core/config/config.js +21 -12
  14. package/dist/core/config/schema/primitives.js +8 -2
  15. package/dist/core/errors.js +2 -0
  16. package/dist/core/lexical-score.js +25 -0
  17. package/dist/core/type-presentation.js +36 -4
  18. package/dist/indexer/index-written-assets.js +4 -0
  19. package/dist/indexer/indexer.js +5 -2
  20. package/dist/indexer/passes/metadata.js +64 -1
  21. package/dist/indexer/scan/doc-to-entry.js +3 -0
  22. package/dist/indexer/scan/drain-dir.js +33 -22
  23. package/dist/indexer/search/db-search.js +72 -14
  24. package/dist/indexer/search/name-match.js +35 -0
  25. package/dist/indexer/search/ranking-contributors.js +15 -12
  26. package/dist/indexer/search/ranking.js +42 -18
  27. package/dist/indexer/usage/show-usage.js +14 -2
  28. package/dist/llm/client.js +12 -8
  29. package/dist/llm/embedders/remote.js +3 -2
  30. package/dist/llm/graph-extract.js +18 -67
  31. package/dist/output/shapes.js +46 -1
  32. package/dist/output/text/proposal-format.js +5 -0
  33. package/dist/scripts/akm-migrate-node.js +648 -253
  34. package/dist/scripts/akm-migrate.js +648 -253
  35. package/dist/storage/repositories/index-connection.js +23 -8
  36. package/dist/storage/repositories/index-entries-repository.js +3 -2
  37. package/dist/storage/repositories/index-entry-schema.js +43 -3
  38. package/dist/storage/repositories/index-fts-repository.js +160 -14
  39. package/dist/storage/repositories/index-schema.js +8 -18
  40. package/dist/storage/repositories/workflow-runs-repository.js +118 -10
  41. package/dist/workflows/exec/run-workflow.js +1 -1
  42. package/dist/workflows/exec/step-work.js +41 -0
  43. package/dist/workflows/parser.js +1 -1
  44. package/dist/workflows/runtime/runs.js +29 -5
  45. package/docs/migration/release-notes/0.9.14.md +26 -0
  46. package/docs/migration/release-notes/README.md +2 -0
  47. package/docs/reference/cli.md +18 -0
  48. package/package.json +1 -1
@@ -7043,7 +7043,8 @@ var init_errors = __esm(() => {
7043
7043
  TEST_ISOLATION_MISSING: "Under bun test, when AKM_BUNDLE_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
7044
7044
  UNSAFE_STASH_DIR: "Choose a path inside your home directory (e.g. ~/akm) or another empty workspace. The bundle directory cannot be the filesystem root, your home directory itself, or a sensitive system path like /etc, /var, ~/.config, or ~/.ssh.",
7045
7045
  UNKNOWN_IMPROVE_STRATEGY: "Pass one of the listed strategy names to `--strategy`, or define it under `improve.strategies`. Names are case-sensitive.",
7046
- EXECUTION_NOT_AUTHORIZED: "Change the selected tools or update the machine/user execution policy, then retry."
7046
+ EXECUTION_NOT_AUTHORIZED: "Change the selected tools or update the machine/user execution policy, then retry.",
7047
+ SECRET_REFERENCE_UNRESOLVED: "Check the secret exists (`akm secret list`) and the name after `secret://` matches, or run `akm secret set <name> <value>` to store it."
7047
7048
  };
7048
7049
  COMPOSITION_INVALID_MULTI_JOB_HINT = "AKM workflows support exactly one job per source, with no needs: between jobs. Split the extra job(s) into " + "their own workflow file, and compose them with uses: workflows/<ref> instead.";
7049
7050
  USAGE_HINTS = {
@@ -7065,7 +7066,8 @@ var init_errors = __esm(() => {
7065
7066
  INPUT_BINDING_INVALID: "Check the step's with: keys against the target's declared inputs.",
7066
7067
  TASK_TARGET_UNSUPPORTED: "Task definitions support command, script, workflow, and shell (run:) targets; akm/command is layered by callers.",
7067
7068
  WORKFLOW_IR_VERSION_UNSUPPORTED: "Abandon the run with `akm workflow abandon <id>`, then start it again from the workflow source — a frozen plan this akm cannot execute is not re-executable in place.",
7068
- WORKFLOW_OUTPUT_INVALID: "Check each `outputs:` entry's `from:` against the step artifact it names, and its `schema:` against the value that step actually promotes."
7069
+ WORKFLOW_OUTPUT_INVALID: "Check each `outputs:` entry's `from:` against the step artifact it names, and its `schema:` against the value that step actually promotes.",
7070
+ RUN_LEASE_HELD: "Wait for the named engine invocation to finish or for the lease to expire, then retry. `akm workflow status <id>` shows the current lease."
7069
7071
  };
7070
7072
  NOT_FOUND_HINTS = {
7071
7073
  ASSET_NOT_FOUND: "Run `akm search <query>` or `akm index` to refresh the index.",
@@ -8389,6 +8391,17 @@ function parseRefInput(raw) {
8389
8391
  }
8390
8392
  return { type: parts.type, name: parts.name, origin: ref.bundle };
8391
8393
  }
8394
+ function isFullRefInput(raw) {
8395
+ const trimmed = raw.trim();
8396
+ if (!trimmed)
8397
+ return false;
8398
+ try {
8399
+ const parsed = parseBundleRef(trimmed);
8400
+ return parsed.bundle !== undefined || typeNameFromConceptId(parsed.conceptId) !== undefined;
8401
+ } catch {
8402
+ return false;
8403
+ }
8404
+ }
8392
8405
  var init_resolve_ref = __esm(() => {
8393
8406
  init_errors();
8394
8407
  init_asset_placement();
@@ -9454,6 +9467,53 @@ function projectMarkdownContent(body, truncationInfo) {
9454
9467
  truncationInfo.truncated = text.length > MARKDOWN_CONTENT_MAX_CHARS;
9455
9468
  return truncateUnicodeSafe(text, MARKDOWN_CONTENT_MAX_CHARS);
9456
9469
  }
9470
+ function setMarkdownFragmentContent(entry, content) {
9471
+ markdownFragmentProjectionEntries.add(entry);
9472
+ if (content)
9473
+ markdownFragmentContentByEntry.set(entry, content);
9474
+ }
9475
+ function getMarkdownFragmentContent(entry) {
9476
+ return markdownFragmentContentByEntry.get(entry);
9477
+ }
9478
+ function hasMarkdownFragmentContent(entry) {
9479
+ return markdownFragmentProjectionEntries.has(entry);
9480
+ }
9481
+ function projectMarkdownFragmentContent(raw) {
9482
+ const lines = raw.split(/\r?\n/);
9483
+ const parsed = parseFrontmatter(raw);
9484
+ const start = parsed.frontmatter ? parsed.bodyStartLine - 1 : 0;
9485
+ const projected = lines.map(() => "");
9486
+ let fence;
9487
+ const htmlComment = { inComment: false };
9488
+ for (let index = start;index < lines.length; index++) {
9489
+ const rawLine = lines[index];
9490
+ if (fence) {
9491
+ if (isMarkdownFenceClosing(rawLine, fence))
9492
+ fence = undefined;
9493
+ continue;
9494
+ }
9495
+ if (!htmlComment.inComment) {
9496
+ const opening2 = parseMarkdownFenceOpening(rawLine);
9497
+ if (opening2) {
9498
+ fence = opening2;
9499
+ continue;
9500
+ }
9501
+ }
9502
+ let safe = stripMarkdownHtmlComments(rawLine, htmlComment);
9503
+ const opening = parseMarkdownFenceOpening(safe.trim());
9504
+ if (opening) {
9505
+ fence = opening;
9506
+ continue;
9507
+ }
9508
+ if (/^\s*\[[^\]]+\]:\s*\S+/.test(safe) || /^\s*<[^>]+>\s*$/.test(safe))
9509
+ continue;
9510
+ safe = stripMarkdownLinkDestinations(safe).replace(/<[^>]+>/g, " ");
9511
+ projected[index] = safe.replace(/[ \t]+$/g, "");
9512
+ }
9513
+ const text = projected.join(`
9514
+ `);
9515
+ return text.trim() ? text : undefined;
9516
+ }
9457
9517
  function applyPreContributorFields(entry, file, ctx, pkgMeta) {
9458
9518
  const ext = path19.extname(file).toLowerCase();
9459
9519
  if (pkgMeta) {
@@ -9474,7 +9534,9 @@ function applyPreContributorFields(entry, file, ctx, pkgMeta) {
9474
9534
  entry.parameters = fmParams;
9475
9535
  applyWikiFrontmatter(entry, parsed.data);
9476
9536
  applyProvenanceFrontmatter(entry, parsed.data);
9477
- if (entry.type !== "env" && entry.type !== "session" && !hasSessionMemoryMarker(parsed.data, parsed.content)) {
9537
+ const safeForFragments = entry.type !== "env" && entry.type !== "session" && !hasSessionMemoryMarker(parsed.data, parsed.content);
9538
+ setMarkdownFragmentContent(entry, safeForFragments ? projectMarkdownFragmentContent(content) : undefined);
9539
+ if (safeForFragments) {
9478
9540
  const truncationInfo = { truncated: false };
9479
9541
  const contentProjection = projectMarkdownContent(parsed.content, truncationInfo);
9480
9542
  if (contentProjection) {
@@ -9592,7 +9654,7 @@ function extractDirTagsFromName(name) {
9592
9654
  }
9593
9655
  return Array.from(tags);
9594
9656
  }
9595
- var SCOPE_KEYS, KNOWN_QUALITY_VALUES, warnedUnknownQualityValues, MARKDOWN_CONTENT_MAX_CHARS = 1e6, MAX_MARKDOWN_LINK_NESTING = 32;
9657
+ var SCOPE_KEYS, KNOWN_QUALITY_VALUES, warnedUnknownQualityValues, MARKDOWN_CONTENT_MAX_CHARS = 1e6, MAX_MARKDOWN_LINK_NESTING = 32, markdownFragmentContentByEntry, markdownFragmentProjectionEntries;
9596
9658
  var init_metadata = __esm(() => {
9597
9659
  init_asset_ref();
9598
9660
  init_frontmatter();
@@ -9601,6 +9663,8 @@ var init_metadata = __esm(() => {
9601
9663
  SCOPE_KEYS = ["user", "agent", "run", "channel"];
9602
9664
  KNOWN_QUALITY_VALUES = new Set(["generated", "curated", "enriched", "proposed"]);
9603
9665
  warnedUnknownQualityValues = new Set;
9666
+ markdownFragmentContentByEntry = new WeakMap;
9667
+ markdownFragmentProjectionEntries = new WeakSet;
9604
9668
  });
9605
9669
 
9606
9670
  // src/execution/record.ts
@@ -12174,6 +12238,9 @@ var init_schema2 = __esm(() => {
12174
12238
  });
12175
12239
 
12176
12240
  // src/core/asset/markdown.ts
12241
+ function markdownHeadingSlug(heading) {
12242
+ return heading.trim().toLowerCase().replace(/<[^>]*>/g, "").replace(/[^\p{L}\p{N}\s_-]+/gu, "-").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
12243
+ }
12177
12244
  function parseMarkdownToc(content) {
12178
12245
  const lines = content.split(/\r?\n/);
12179
12246
  const headings = [];
@@ -12415,7 +12482,7 @@ function bindStepSections(headings, lines, bodyStartLine, totalLines, path24, de
12415
12482
  if (!declaredIds.has(h.text)) {
12416
12483
  errors3.push({
12417
12484
  line: h.line,
12418
- message: `Unexpected level-2 heading "## ${h.text}" on line ${h.line} — no step "${h.text}" is declared in frontmatter "steps:". Level-2 headings must exactly match a declared step id.`
12485
+ message: `Unexpected level-2 heading "## ${h.text}" on line ${h.line} — no step "${h.text}" is declared in frontmatter "steps:". Level-2 headings must exactly match a declared step id. To document something that is not a step, use a level-3 heading.`
12419
12486
  });
12420
12487
  continue;
12421
12488
  }
@@ -36279,7 +36346,7 @@ var require_libvips = __commonJS((exports, module) => {
36279
36346
  SPDX-License-Identifier: Apache-2.0
36280
36347
  */
36281
36348
  var { spawnSync: spawnSync6 } = __require("node:child_process");
36282
- var { createHash: createHash9 } = __require("node:crypto");
36349
+ var { createHash: createHash10 } = __require("node:crypto");
36283
36350
  var semverCoerce = require_coerce2();
36284
36351
  var semverGreaterThanOrEqualTo = require_gte2();
36285
36352
  var semverSatisfies = require_satisfies2();
@@ -36367,7 +36434,7 @@ var require_libvips = __commonJS((exports, module) => {
36367
36434
  }
36368
36435
  return false;
36369
36436
  };
36370
- var sha512 = (s) => createHash9("sha512").update(s).digest("hex");
36437
+ var sha512 = (s) => createHash10("sha512").update(s).digest("hex");
36371
36438
  var yarnLocator = () => {
36372
36439
  try {
36373
36440
  const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
@@ -50659,10 +50726,10 @@ var import_sharp, __dirname = "/home/runner/work/akm/akm/node_modules/@huggingfa
50659
50726
  if (cached) {
50660
50727
  return cached.text();
50661
50728
  }
50662
- const hash3 = await this._getLfsFileHash(url2);
50663
- if (hash3) {
50664
- await hashCache.put(url2, new Response(hash3));
50665
- return hash3;
50729
+ const hash4 = await this._getLfsFileHash(url2);
50730
+ if (hash4) {
50731
+ await hashCache.put(url2, new Response(hash4));
50732
+ return hash4;
50666
50733
  }
50667
50734
  return null;
50668
50735
  } catch {
@@ -68759,15 +68826,15 @@ ${this.boa_token}${this.audio_token.repeat(this._compute_audio_num_tokens(audio_
68759
68826
  });
68760
68827
 
68761
68828
  // src/indexer/walk/file-context.ts
68762
- import fs45 from "node:fs";
68763
- import path55 from "node:path";
68829
+ import fs47 from "node:fs";
68830
+ import path56 from "node:path";
68764
68831
  function buildFileContext(stashRoot, absPath) {
68765
- const relPath = toPosix(path55.relative(stashRoot, absPath));
68766
- const ext = path55.extname(absPath).toLowerCase();
68767
- const fileName = path55.basename(absPath);
68768
- const parentDirAbs = path55.dirname(absPath);
68769
- const parentDir = path55.basename(parentDirAbs);
68770
- const relDir = toPosix(path55.dirname(relPath));
68832
+ const relPath = toPosix(path56.relative(stashRoot, absPath));
68833
+ const ext = path56.extname(absPath).toLowerCase();
68834
+ const fileName = path56.basename(absPath);
68835
+ const parentDirAbs = path56.dirname(absPath);
68836
+ const parentDir = path56.basename(parentDirAbs);
68837
+ const relDir = toPosix(path56.dirname(relPath));
68771
68838
  const ancestorDirs = relDir === "." ? [] : relDir.split("/").filter((seg) => seg.length > 0);
68772
68839
  let cachedContent;
68773
68840
  let cachedFrontmatter;
@@ -68784,7 +68851,7 @@ function buildFileContext(stashRoot, absPath) {
68784
68851
  stashRoot,
68785
68852
  content() {
68786
68853
  if (cachedContent === undefined) {
68787
- cachedContent = fs45.readFileSync(absPath, "utf8");
68854
+ cachedContent = fs47.readFileSync(absPath, "utf8");
68788
68855
  }
68789
68856
  return cachedContent;
68790
68857
  },
@@ -68799,7 +68866,7 @@ function buildFileContext(stashRoot, absPath) {
68799
68866
  },
68800
68867
  stat() {
68801
68868
  if (cachedStat === undefined) {
68802
- cachedStat = fs45.statSync(absPath);
68869
+ cachedStat = fs47.statSync(absPath);
68803
68870
  }
68804
68871
  return cachedStat;
68805
68872
  }
@@ -70922,6 +70989,7 @@ function formatProposalDrainPlain(r) {
70922
70989
  const deferred = Array.isArray(r.deferred) ? r.deferred : [];
70923
70990
  const skippedByCap = Array.isArray(r.skippedByCap) ? r.skippedByCap : [];
70924
70991
  const staged = Array.isArray(r.staged) ? r.staged : [];
70992
+ const failed = Array.isArray(r.failed) ? r.failed : [];
70925
70993
  const prefix = r.dryRun === true ? "[dry-run] " : "";
70926
70994
  const lines = [
70927
70995
  `${prefix}Drained proposal queue (strategy=${String(r.strategy ?? "?")}, policy=${policy}, applyMode=${applyMode})`,
@@ -70929,11 +70997,15 @@ function formatProposalDrainPlain(r) {
70929
70997
  ` rejected: ${rejected.length}`,
70930
70998
  ` deferred: ${deferred.length}`,
70931
70999
  ` skippedByCap: ${skippedByCap.length}`,
70932
- ` staged: ${staged.length}`
71000
+ ` staged: ${staged.length}`,
71001
+ ` failed: ${failed.length}`
70933
71002
  ];
70934
71003
  for (const d of deferred) {
70935
71004
  lines.push(` - ${String(d.id ?? "?")} (${String(d.reason ?? "?")})`);
70936
71005
  }
71006
+ for (const f of failed) {
71007
+ lines.push(` ! ${String(f.id ?? "?")} (${String(f.reason ?? "?")}): ${String(f.detail ?? "?")}`);
71008
+ }
70937
71009
  appendLoweringNotices(lines, r);
70938
71010
  return lines.join(`
70939
71011
  `).trimEnd();
@@ -76193,12 +76265,16 @@ var httpUrl = exports_external.string().refine((v) => v.startsWith("http://") ||
76193
76265
  });
76194
76266
  var ENGINE_NAME_PATTERN = new RegExp(ENGINE_NAME_PATTERN_SOURCE);
76195
76267
  var ENV_REFERENCE_PATTERN = /^\$[A-Za-z_][A-Za-z0-9_]*$|^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/;
76268
+ var SECRET_STORE_REFERENCE_PATTERN = /^secret:\/\/(.+)$/;
76196
76269
  var engineName = exports_external.string().max(63).regex(ENGINE_NAME_PATTERN, "names must be lowercase kebab-case and must not begin with reserved akm-");
76270
+ function isApiKeyReference(value) {
76271
+ return ENV_REFERENCE_PATTERN.test(value) || SECRET_STORE_REFERENCE_PATTERN.test(value);
76272
+ }
76197
76273
  function symbolicOrWarnApiKey(label) {
76198
76274
  return exports_external.string().superRefine((value) => {
76199
- if (ENV_REFERENCE_PATTERN.test(value))
76275
+ if (isApiKeyReference(value))
76200
76276
  return;
76201
- warnOnce(`config:literal-api-key:${label}`, `A ${label} in config.json is a literal API key, not a $VAR/\${VAR} reference; using it as configured. Prefer \`akm config set ...apiKey '$VAR'\` (with the corresponding env var set) — see docs/reference/data-and-telemetry.md.`);
76277
+ warnOnce(`config:literal-api-key:${label}`, `A ${label} in config.json is a literal API key, not a $VAR/\${VAR}/secret:// reference; using it as configured. Prefer \`akm config set ...apiKey '$VAR'\` (with the corresponding env var set) or \`secret://<name>\` (with \`akm secret set <name> ...\`) — see docs/reference/data-and-telemetry.md.`);
76202
76278
  });
76203
76279
  }
76204
76280
  var chatCompletionsEndpoint = exports_external.string().superRefine((value, ctx) => {
@@ -77366,11 +77442,19 @@ function getSources(config) {
77366
77442
  function loadConfig() {
77367
77443
  return loadUserConfig();
77368
77444
  }
77369
- function resolveSecret(value) {
77445
+ function resolveSecret(value, resolveFromStore) {
77370
77446
  if (value === undefined)
77371
77447
  return;
77372
77448
  if (typeof value !== "string")
77373
77449
  return value;
77450
+ const storeRef = SECRET_STORE_REFERENCE_PATTERN.exec(value)?.[1];
77451
+ if (storeRef !== undefined) {
77452
+ const resolved = resolveFromStore?.(storeRef) ?? null;
77453
+ if (resolved === null) {
77454
+ throw new ConfigError(`Secret store reference "${value}" did not resolve to a stored value.`, "SECRET_REFERENCE_UNRESOLVED");
77455
+ }
77456
+ return resolved;
77457
+ }
77374
77458
  if (!value.includes("$"))
77375
77459
  return value;
77376
77460
  return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, braced, bare) => {
@@ -79254,10 +79338,10 @@ function listTxnJournalsTolerant(predicate) {
79254
79338
  }
79255
79339
 
79256
79340
  // src/commands/proposal/repository.ts
79257
- import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
79258
- import fs47 from "node:fs";
79341
+ import { createHash as createHash10, randomUUID as randomUUID6 } from "node:crypto";
79342
+ import fs49 from "node:fs";
79259
79343
  init_dist();
79260
- import path60 from "node:path";
79344
+ import path61 from "node:path";
79261
79345
 
79262
79346
  // src/core/adapter/adapters/agent-skills-adapter.ts
79263
79347
  init_frontmatter();
@@ -80162,20 +80246,41 @@ function buildWorkflowAction(ref) {
80162
80246
  return `Start or resume execution with \`akm workflow run ${shellQuote(ref)}\`.`;
80163
80247
  }
80164
80248
  var TYPE_PRESENTATION = {
80165
- skill: { label: "Skill", renderer: "skill-md", action: (ref) => `akm show ${ref} -> follow the instructions` },
80249
+ skill: {
80250
+ label: "Skill",
80251
+ renderer: "skill-md",
80252
+ action: (ref) => `akm show ${ref} -> follow the instructions`,
80253
+ fragmentRef: false
80254
+ },
80166
80255
  command: {
80167
80256
  label: "Command",
80168
80257
  renderer: "command-md",
80169
- action: (ref) => `akm show ${ref} -> fill placeholders and dispatch`
80258
+ action: (ref) => `akm show ${ref} -> fill placeholders and dispatch`,
80259
+ fragmentRef: false
80260
+ },
80261
+ agent: {
80262
+ label: "Agent",
80263
+ renderer: "agent-md",
80264
+ action: (ref) => `akm show ${ref} -> dispatch with full prompt`,
80265
+ fragmentRef: false
80170
80266
  },
80171
- agent: { label: "Agent", renderer: "agent-md", action: (ref) => `akm show ${ref} -> dispatch with full prompt` },
80172
80267
  knowledge: {
80173
80268
  label: "Knowledge",
80174
80269
  renderer: "knowledge-md",
80175
80270
  action: (ref) => `akm show ${ref} -> read reference material`
80176
80271
  },
80177
- workflow: { label: "Workflow", renderer: "workflow-md", action: (ref) => buildWorkflowAction(ref) },
80178
- script: { label: "Script", renderer: "script-source", action: (ref) => `akm show ${ref} -> execute the run command` },
80272
+ workflow: {
80273
+ label: "Workflow",
80274
+ renderer: "workflow-md",
80275
+ action: (ref) => buildWorkflowAction(ref),
80276
+ fragmentRef: false
80277
+ },
80278
+ script: {
80279
+ label: "Script",
80280
+ renderer: "script-source",
80281
+ action: (ref) => `akm show ${ref} -> execute the run command`,
80282
+ fragmentRef: false
80283
+ },
80179
80284
  memory: { label: "Memory", renderer: "memory-md", action: (ref) => `akm show ${ref} -> recall context` },
80180
80285
  env: {
80181
80286
  label: "Env",
@@ -80195,7 +80300,8 @@ var TYPE_PRESENTATION = {
80195
80300
  task: {
80196
80301
  label: "Task",
80197
80302
  renderer: "task-yaml",
80198
- action: (ref) => `akm show ${ref} -> inspect; akm task run <id> -> run now; edit the file + akm task sync -> unschedule`
80303
+ action: (ref) => `akm show ${ref} -> inspect; akm task run <id> -> run now; edit the file + akm task sync -> unschedule`,
80304
+ fragmentRef: false
80199
80305
  },
80200
80306
  session: {
80201
80307
  label: "Session",
@@ -80210,7 +80316,8 @@ var TYPE_PRESENTATION = {
80210
80316
  instruction: {
80211
80317
  label: "Instruction",
80212
80318
  renderer: "knowledge-md",
80213
- action: (ref) => `akm show ${ref} -> read the project instructions`
80319
+ action: (ref) => `akm show ${ref} -> read the project instructions`,
80320
+ fragmentRef: false
80214
80321
  }
80215
80322
  };
80216
80323
  var DEFAULT_PRESENTATION = { label: "Asset" };
@@ -83136,6 +83243,8 @@ function indexDocumentFromEntry(entry, base3, rendererName) {
83136
83243
  doc.lessonStrength = entry.lessonStrength;
83137
83244
  if (entry.derivedFrom !== undefined)
83138
83245
  doc.derivedFrom = entry.derivedFrom;
83246
+ if (hasMarkdownFragmentContent(entry))
83247
+ setMarkdownFragmentContent(doc, getMarkdownFragmentContent(entry));
83139
83248
  return doc;
83140
83249
  }
83141
83250
  function conceptIdForRecognizedType(root, filePath, type) {
@@ -92674,8 +92783,8 @@ init_warn();
92674
92783
  init_write_provenance();
92675
92784
 
92676
92785
  // src/indexer/index-written-assets.ts
92677
- import fs46 from "node:fs";
92678
- import path58 from "node:path";
92786
+ import fs48 from "node:fs";
92787
+ import path59 from "node:path";
92679
92788
  init_errors();
92680
92789
  init_paths();
92681
92790
  init_warn();
@@ -92686,7 +92795,7 @@ init_paths();
92686
92795
  init_warn();
92687
92796
 
92688
92797
  // src/storage/repositories/index-entry-schema.ts
92689
- var CANONICAL_INDEX_DB_VERSION = 22;
92798
+ var CANONICAL_INDEX_DB_VERSION = 23;
92690
92799
  var CANONICAL_ENTRY_SCHEMA_FINGERPRINT = {
92691
92800
  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 )",
92692
92801
  sqliteSequenceTable: true,
@@ -92836,7 +92945,12 @@ var CANONICAL_ENTRY_SCHEMA_FINGERPRINT = {
92836
92945
  { sequence: 1, cid: -1, name: null, descending: 0, collation: "BINARY", key: 0 }
92837
92946
  ]
92838
92947
  }
92839
- ]
92948
+ ],
92949
+ searchSurfaces: {
92950
+ entriesFtsSql: "CREATE VIRTUAL TABLE entries_fts USING fts5( entry_id UNINDEXED, name, description, tags, hints, content, tokenize='porter unicode61' )",
92951
+ fragmentSourceSql: "CREATE TABLE entry_fragments ( entry_id INTEGER PRIMARY KEY REFERENCES entries(id) ON DELETE CASCADE, safe_markdown TEXT NOT NULL )",
92952
+ fragmentsFtsSql: "CREATE VIRTUAL TABLE entry_fragments_fts USING fts5( entry_id UNINDEXED, fragment_id UNINDEXED, fragment_ordinal UNINDEXED, content, tokenize='porter unicode61' )"
92953
+ }
92840
92954
  };
92841
92955
  function sqlString(value) {
92842
92956
  return `'${value.replaceAll("'", "''")}'`;
@@ -92846,8 +92960,11 @@ function normalizeSchemaSql(value) {
92846
92960
  return null;
92847
92961
  return value.replace(/\s+/g, " ").trim();
92848
92962
  }
92963
+ function readNamedTableSql(db, name) {
92964
+ const row = db.prepare(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ${sqlString(name)}`).get();
92965
+ return normalizeSchemaSql(row?.sql);
92966
+ }
92849
92967
  function readEntrySchemaFingerprint(db) {
92850
- const tableRow = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'entries'").get();
92851
92968
  const sqliteSequenceTable = db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_sequence'").get() != null;
92852
92969
  const maxId = Number(db.prepare("SELECT COALESCE(MAX(id), 0) AS maxId FROM entries").get().maxId);
92853
92970
  const sequenceRow = sqliteSequenceTable ? db.prepare("SELECT seq FROM sqlite_sequence WHERE name = 'entries'").get() : undefined;
@@ -92876,11 +92993,16 @@ function readEntrySchemaFingerprint(db) {
92876
92993
  }))
92877
92994
  })).sort((left, right) => left.name.localeCompare(right.name));
92878
92995
  return {
92879
- tableSql: normalizeSchemaSql(tableRow?.sql),
92996
+ tableSql: readNamedTableSql(db, "entries"),
92880
92997
  sqliteSequenceTable,
92881
92998
  sqliteSequenceValid,
92882
92999
  columns,
92883
- indexes
93000
+ indexes,
93001
+ searchSurfaces: {
93002
+ entriesFtsSql: readNamedTableSql(db, "entries_fts"),
93003
+ fragmentSourceSql: readNamedTableSql(db, "entry_fragments"),
93004
+ fragmentsFtsSql: readNamedTableSql(db, "entry_fragments_fts")
93005
+ }
92884
93006
  };
92885
93007
  }
92886
93008
  function hasCanonicalEntrySchema(db) {
@@ -93083,20 +93205,30 @@ function openExistingDatabase(dbPath) {
93083
93205
  if (classifyPathAccess(resolvedPath).access === "absent") {
93084
93206
  throw new Error(`Index database not found at ${resolvedPath}. Run 'akm index' to build it.`);
93085
93207
  }
93086
- return openManagedDatabase({
93208
+ const db = openManagedDatabase({
93087
93209
  path: resolvedPath,
93088
- init: (db) => {
93089
- loadVecExtension(db);
93090
- warnIfNonCanonicalIndexGeneration(db, resolvedPath);
93210
+ init: (db2) => {
93211
+ loadVecExtension(db2);
93091
93212
  },
93092
93213
  create: false
93093
93214
  });
93215
+ try {
93216
+ assertCanonicalIndexGeneration(db, resolvedPath);
93217
+ return db;
93218
+ } catch (error2) {
93219
+ db.close();
93220
+ throw error2;
93221
+ }
93094
93222
  }
93095
- function warnIfNonCanonicalIndexGeneration(db, resolvedPath) {
93223
+ function assertCanonicalIndexGeneration(db, resolvedPath) {
93096
93224
  if (isCanonicalIndexGeneration(db))
93097
93225
  return;
93098
93226
  const classification = classifyIndexGeneration(db);
93099
- 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.");
93227
+ const stored = classification.storedVersion ?? "unknown";
93228
+ if (classification.status === "newer") {
93229
+ 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.");
93230
+ }
93231
+ 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.");
93100
93232
  }
93101
93233
  function assertIndexPathReadable(resolvedPath) {
93102
93234
  const { access, code } = classifyPathAccess(resolvedPath);
@@ -93113,6 +93245,7 @@ import fs40 from "node:fs";
93113
93245
  init_asset_ref();
93114
93246
  init_resolve_ref();
93115
93247
  init_warn();
93248
+ init_metadata();
93116
93249
 
93117
93250
  // src/indexer/search/search-fields.ts
93118
93251
  function buildSearchFields(entry) {
@@ -93176,9 +93309,129 @@ function buildSearchText(entry) {
93176
93309
  // src/storage/repositories/index-entry-mapper.ts
93177
93310
  init_warn();
93178
93311
 
93312
+ // src/core/asset/markdown-fragments.ts
93313
+ init_markdown();
93314
+ import { createHash as createHash9 } from "node:crypto";
93315
+ var MARKDOWN_FRAGMENT_MAX_CHARS = 1600;
93316
+ var MARKDOWN_FRAGMENT_PREFIX = "akm-fragment-";
93317
+ function hash3(text) {
93318
+ return createHash9("sha256").update(text).digest("hex");
93319
+ }
93320
+ function uniqueSlugs(body) {
93321
+ const out = new Map;
93322
+ const seen = new Set;
93323
+ for (const heading of parseMarkdownToc(body).headings) {
93324
+ const base3 = markdownHeadingSlug(heading.text);
93325
+ if (!base3)
93326
+ continue;
93327
+ let slug = base3;
93328
+ for (let suffix = 1;seen.has(slug); suffix++)
93329
+ slug = `${base3}-${suffix}`;
93330
+ seen.add(slug);
93331
+ out.set(heading.line, slug);
93332
+ }
93333
+ return out;
93334
+ }
93335
+ function textOf(lines) {
93336
+ return lines.join(`
93337
+ `).trim();
93338
+ }
93339
+ function splitPiece(piece, maxChars) {
93340
+ if (textOf(piece.lines).length <= maxChars)
93341
+ return [piece];
93342
+ const pieces = [];
93343
+ let start = 0;
93344
+ while (start < piece.lines.length) {
93345
+ let end = start;
93346
+ let chars = 0;
93347
+ while (end < piece.lines.length) {
93348
+ const next = piece.lines[end];
93349
+ if (end === start && next.length > maxChars)
93350
+ break;
93351
+ if (end > start && chars + next.length + 1 > maxChars)
93352
+ break;
93353
+ chars += next.length + (end > start ? 1 : 0);
93354
+ end++;
93355
+ }
93356
+ if (end === start) {
93357
+ const line = piece.lines[start];
93358
+ let offset = 0;
93359
+ while (offset < line.length) {
93360
+ let cut = Math.min(offset + maxChars, line.length);
93361
+ if (cut < line.length) {
93362
+ const space = line.lastIndexOf(" ", cut);
93363
+ if (space > offset + Math.floor(maxChars * 0.55))
93364
+ cut = space;
93365
+ }
93366
+ pieces.push({ lines: [line.slice(offset, cut).trim()], startLine: piece.startLine + start });
93367
+ offset = cut;
93368
+ while (line[offset] === " ")
93369
+ offset++;
93370
+ }
93371
+ start++;
93372
+ continue;
93373
+ }
93374
+ let preferred = -1;
93375
+ for (let i = start + 1;i < end; i++)
93376
+ if (!piece.lines[i].trim())
93377
+ preferred = i;
93378
+ if (preferred > start)
93379
+ end = preferred;
93380
+ pieces.push({ lines: piece.lines.slice(start, end), startLine: piece.startLine + start });
93381
+ start = end;
93382
+ while (start < piece.lines.length && !piece.lines[start].trim())
93383
+ start++;
93384
+ }
93385
+ return pieces.filter((candidate) => textOf(candidate.lines));
93386
+ }
93387
+ function splitMarkdownFragmentStats(body, maxChars = MARKDOWN_FRAGMENT_MAX_CHARS) {
93388
+ const lines = body.split(/\r?\n/);
93389
+ const headings = parseMarkdownToc(body).headings;
93390
+ 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);
93391
+ const slugs = uniqueSlugs(body);
93392
+ const pieces = [];
93393
+ let sectionCount = 0;
93394
+ for (let i = 0;i < boundaries.length - 1; i++) {
93395
+ const startLine = boundaries[i];
93396
+ const end = boundaries[i + 1] - 1;
93397
+ const section = { lines: lines.slice(startLine - 1, end), startLine, headingSlug: slugs.get(startLine) };
93398
+ if (textOf(section.lines)) {
93399
+ sectionCount++;
93400
+ pieces.push(...splitPiece(section, maxChars));
93401
+ }
93402
+ }
93403
+ const piecesPerHeading = new Map;
93404
+ for (const piece of pieces) {
93405
+ if (piece.headingSlug)
93406
+ piecesPerHeading.set(piece.headingSlug, (piecesPerHeading.get(piece.headingSlug) ?? 0) + 1);
93407
+ }
93408
+ const fragments = pieces.map((piece, ordinal) => {
93409
+ const text = textOf(piece.lines);
93410
+ const contentLines = piece.lines.map((line, index) => ({ line, index })).filter(({ line }) => line.trim());
93411
+ const first = contentLines[0]?.index ?? 0;
93412
+ const last = contentLines.at(-1)?.index ?? 0;
93413
+ const digest = hash3(text);
93414
+ const unsplitHeading = piece.headingSlug && piecesPerHeading.get(piece.headingSlug) === 1;
93415
+ return {
93416
+ fragmentId: `${MARKDOWN_FRAGMENT_PREFIX}${ordinal + 1}-${digest.slice(0, 12)}`,
93417
+ ordinal,
93418
+ startLine: piece.startLine + first,
93419
+ endLine: piece.startLine + last,
93420
+ ...unsplitHeading ? { headingSlug: piece.headingSlug } : {},
93421
+ text,
93422
+ hash: digest
93423
+ };
93424
+ });
93425
+ return { fragments, hardSplitCount: Math.max(0, pieces.length - sectionCount) };
93426
+ }
93427
+ function splitMarkdownFragments(body, maxChars = MARKDOWN_FRAGMENT_MAX_CHARS) {
93428
+ return splitMarkdownFragmentStats(body, maxChars).fragments;
93429
+ }
93430
+
93179
93431
  // src/storage/repositories/index-fts-repository.ts
93180
93432
  init_warn();
93181
93433
  var INSERT_FTS_SQL = "INSERT INTO entries_fts (entry_id, name, description, tags, hints, content) VALUES (?, ?, ?, ?, ?, ?)";
93434
+ var INSERT_FRAGMENT_SQL = "INSERT INTO entry_fragments_fts (entry_id, fragment_id, fragment_ordinal, content) VALUES (?, ?, ?, ?)";
93182
93435
  var ftsMutationStatementsByDb = new WeakMap;
93183
93436
  function getFtsMutationStatements(db) {
93184
93437
  const existing = ftsMutationStatementsByDb.get(db);
@@ -93186,22 +93439,39 @@ function getFtsMutationStatements(db) {
93186
93439
  return existing;
93187
93440
  const statements = {
93188
93441
  deleteOne: db.prepare("DELETE FROM entries_fts WHERE entry_id = ?"),
93189
- insert: db.prepare(INSERT_FTS_SQL)
93442
+ insert: db.prepare(INSERT_FTS_SQL),
93443
+ deleteFragments: db.prepare("DELETE FROM entry_fragments_fts WHERE entry_id = ?"),
93444
+ upsertFragmentSource: db.prepare("INSERT INTO entry_fragments (entry_id, safe_markdown) VALUES (?, ?) ON CONFLICT(entry_id) DO UPDATE SET safe_markdown = excluded.safe_markdown"),
93445
+ deleteFragmentSource: db.prepare("DELETE FROM entry_fragments WHERE entry_id = ?"),
93446
+ insertFragment: db.prepare(INSERT_FRAGMENT_SQL)
93190
93447
  };
93191
93448
  ftsMutationStatementsByDb.set(db, statements);
93192
93449
  return statements;
93193
93450
  }
93194
- function replaceFtsEntry(db, entryId, entry) {
93451
+ function replaceFtsEntry(db, entryId, entry, fragmentContent) {
93195
93452
  const fields = buildSearchFields(entry);
93196
93453
  const statements = getFtsMutationStatements(db);
93197
93454
  statements.deleteOne.run(entryId);
93198
93455
  statements.insert.run(entryId, fields.name, fields.description, fields.tags, fields.hints, fields.content);
93456
+ if (fragmentContent === undefined) {
93457
+ return;
93458
+ }
93459
+ statements.deleteFragments.run(entryId);
93460
+ statements.deleteFragmentSource.run(entryId);
93461
+ if (!fragmentContent)
93462
+ return;
93463
+ statements.upsertFragmentSource.run(entryId, fragmentContent);
93464
+ for (const fragment of splitMarkdownFragments(fragmentContent)) {
93465
+ statements.insertFragment.run(entryId, fragment.fragmentId, fragment.ordinal, fragment.text.toLowerCase());
93466
+ }
93199
93467
  }
93200
93468
  function deleteFtsEntries(db, entryIds) {
93201
93469
  for (let i = 0;i < entryIds.length; i += SQLITE_CHUNK_SIZE) {
93202
93470
  const chunk = entryIds.slice(i, i + SQLITE_CHUNK_SIZE);
93203
93471
  const placeholders = chunk.map(() => "?").join(",");
93204
93472
  db.prepare(`DELETE FROM entries_fts WHERE entry_id IN (${placeholders})`).run(...chunk);
93473
+ db.prepare(`DELETE FROM entry_fragments_fts WHERE entry_id IN (${placeholders})`).run(...chunk);
93474
+ db.prepare(`DELETE FROM entry_fragments WHERE entry_id IN (${placeholders})`).run(...chunk);
93205
93475
  }
93206
93476
  }
93207
93477
 
@@ -93216,7 +93486,7 @@ function upsertEntry(db, filePath, entry, searchText, provenance, contentHash) {
93216
93486
  throw new Error("upsertEntry: item_ref not found after upsert");
93217
93487
  if (previous?.id === result.id && previous.search_text !== searchText)
93218
93488
  deleteEntryVectors(db, result.id);
93219
- replaceFtsEntry(db, result.id, entry);
93489
+ replaceFtsEntry(db, result.id, entry, hasMarkdownFragmentContent(entry) ? getMarkdownFragmentContent(entry) ?? null : undefined);
93220
93490
  return result.id;
93221
93491
  };
93222
93492
  return db.transaction(apply)();
@@ -93712,6 +93982,127 @@ function redactSensitiveText(text, sensitiveValues) {
93712
93982
 
93713
93983
  // src/llm/embedders/remote.ts
93714
93984
  init_warn();
93985
+
93986
+ // src/sources/snapshot-fetchers/secret-seam.ts
93987
+ import fs45 from "node:fs";
93988
+
93989
+ // src/core/env-secret-ref.ts
93990
+ import fs43 from "node:fs";
93991
+ import path54 from "node:path";
93992
+
93993
+ // src/registry/origin-resolve.ts
93994
+ init_asset_ref();
93995
+ function resolveSourcesForOrigin(origin, allSources) {
93996
+ if (!origin)
93997
+ return allSources;
93998
+ const installations = deriveInstallations(allSources);
93999
+ return allSources.filter((_, index) => installations[index]?.id === origin);
94000
+ }
94001
+
94002
+ // src/core/asset/asset-create.ts
94003
+ init_errors();
94004
+ function normalizeCreateSubPath(subPath) {
94005
+ if (subPath === undefined)
94006
+ return "";
94007
+ const trimmed = subPath.trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
94008
+ if (!trimmed)
94009
+ return "";
94010
+ if (trimmed.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
94011
+ throw new UsageError("--path must be a relative directory without '.' or '..' segments.");
94012
+ }
94013
+ return trimmed;
94014
+ }
94015
+ function assertFlatAssetName(name) {
94016
+ if (name?.replace(/\\/g, "/").replace(/\.md$/i, "").includes("/")) {
94017
+ throw new UsageError("Asset --name must be a flat name without '/'. Use --path to choose a subdirectory " + "(e.g. --path personal --name grocery-list).");
94018
+ }
94019
+ }
94020
+ function combineCreatePath(subPath, baseName) {
94021
+ return subPath ? `${subPath}/${baseName}` : baseName;
94022
+ }
94023
+
94024
+ // src/core/env-secret-ref.ts
94025
+ init_asset_placement();
94026
+ init_resolve_ref();
94027
+ init_common();
94028
+ init_errors();
94029
+ function assertNotRemovedVaultRef(ref) {
94030
+ const boundary = ref.indexOf("//");
94031
+ const bare = boundary >= 0 ? ref.slice(boundary + 2) : ref;
94032
+ if (/^vault[:/]/.test(bare.trim())) {
94033
+ throw new UsageError("The `vault` asset type was removed in 0.9.0 — use `env/` (whole .env config) or `secrets/` (a single value).", "INVALID_FLAG_VALUE");
94034
+ }
94035
+ }
94036
+ function assertNotColonRef(ref, aliases, replacement) {
94037
+ const boundary = ref.indexOf("//");
94038
+ const bare = (boundary >= 0 ? ref.slice(boundary + 2) : ref).trim();
94039
+ const colon = bare.indexOf(":");
94040
+ if (colon <= 0)
94041
+ return;
94042
+ const head = bare.slice(0, colon).toLowerCase();
94043
+ if (!aliases.includes(head))
94044
+ return;
94045
+ const name = bare.slice(colon + 1);
94046
+ throw new UsageError(`The \`${head}:\` ref spelling was removed in 0.9.0 — use the slash form instead: \`${replacement}${name}\`.`, "INVALID_FLAG_VALUE");
94047
+ }
94048
+ function findEnvSource(origin, type, name) {
94049
+ const sources = resolveSourceEntries(undefined, loadConfig());
94050
+ if (sources.length === 0) {
94051
+ throw new UsageError("No bundles configured. Run `akm bundle create` to create your working bundle.");
94052
+ }
94053
+ const candidates = origin ? resolveSourcesForOrigin(origin, sources) : sources;
94054
+ const typeDir = type === "env" ? "env" : "secrets";
94055
+ const member = candidates.find((source) => fs43.existsSync(assetPathForName(type, path54.join(source.path, typeDir), name)));
94056
+ if (member)
94057
+ return member;
94058
+ if (!origin) {
94059
+ const fallback = candidates[0];
94060
+ if (fallback)
94061
+ return fallback;
94062
+ throw new UsageError("No bundles configured. Run `akm bundle create` to create your working bundle.");
94063
+ }
94064
+ const named = candidates[0];
94065
+ if (!named) {
94066
+ throw new NotFoundError(`Source not found for origin: ${origin}`);
94067
+ }
94068
+ return named;
94069
+ }
94070
+ function parseSecretRef(ref) {
94071
+ assertNotRemovedVaultRef(ref);
94072
+ assertNotColonRef(ref, ["secret", "secrets"], "secrets/");
94073
+ return parseRefInput(isFullRefInput(ref) ? ref : `secrets/${ref}`);
94074
+ }
94075
+ function resolveSecretPath(ref, create) {
94076
+ const parsed = parseSecretRef(ref);
94077
+ if (parsed.type !== "secret") {
94078
+ throw new UsageError(`Expected a secret ref (secrets/<name>); got "${ref}".`);
94079
+ }
94080
+ if (create) {
94081
+ assertFlatAssetName(parsed.name);
94082
+ parsed.name = combineCreatePath(normalizeCreateSubPath(create.subPath), parsed.name);
94083
+ }
94084
+ const source = findEnvSource(parsed.origin, "secret", parsed.name);
94085
+ const typeRoot = path54.join(source.path, "secrets");
94086
+ const absPath = assetPathForName("secret", typeRoot, parsed.name);
94087
+ if (!isWithin(absPath, typeRoot)) {
94088
+ throw new UsageError(`Secret name "${parsed.name}" escapes the secrets directory.`);
94089
+ }
94090
+ return { name: parsed.name, absPath, source };
94091
+ }
94092
+
94093
+ // src/sources/snapshot-fetchers/secret-seam.ts
94094
+ function resolveSecretFromStore(ref) {
94095
+ try {
94096
+ const { absPath } = resolveSecretPath(ref);
94097
+ if (!fs45.existsSync(absPath))
94098
+ return null;
94099
+ return fs45.readFileSync(absPath, "utf8").trim() || null;
94100
+ } catch {
94101
+ return null;
94102
+ }
94103
+ }
94104
+
94105
+ // src/llm/embedders/remote.ts
93715
94106
  var DEFAULT_REMOTE_BATCH_SIZE = 100;
93716
94107
  var DEFAULT_TOKEN_BUDGET = 8000;
93717
94108
  function estimateTokenCount(text) {
@@ -93867,14 +94258,14 @@ class RemoteEmbedder {
93867
94258
  }
93868
94259
  buildHeaders() {
93869
94260
  const headers = { "Content-Type": "application/json" };
93870
- const resolvedKey = resolveSecret(this.config.apiKey);
94261
+ const resolvedKey = resolveSecret(this.config.apiKey, resolveSecretFromStore);
93871
94262
  if (resolvedKey) {
93872
94263
  headers.Authorization = `Bearer ${resolvedKey}`;
93873
94264
  }
93874
94265
  return headers;
93875
94266
  }
93876
94267
  safeErrorBody(body) {
93877
- const resolvedKey = resolveSecret(this.config.apiKey);
94268
+ const resolvedKey = resolveSecret(this.config.apiKey, resolveSecretFromStore);
93878
94269
  return redactSensitiveText(redactErrorBody(body), resolvedKey ? [resolvedKey] : []);
93879
94270
  }
93880
94271
  }
@@ -94109,8 +94500,11 @@ function publishTargetedEmbeddingMeta(db, config) {
94109
94500
  setMeta(db, "hasEmbeddings", ready ? "1" : "0");
94110
94501
  }
94111
94502
 
94503
+ // src/indexer/index-written-assets.ts
94504
+ init_metadata();
94505
+
94112
94506
  // src/indexer/scan/drain-dir.ts
94113
- import path57 from "node:path";
94507
+ import path58 from "node:path";
94114
94508
  init_common();
94115
94509
  init_recognition_util();
94116
94510
 
@@ -94119,8 +94513,8 @@ init_common();
94119
94513
  init_errors();
94120
94514
  init_recognition_util();
94121
94515
  init_warn();
94122
- import fs43 from "node:fs";
94123
- import path54 from "node:path";
94516
+ import fs46 from "node:fs";
94517
+ import path55 from "node:path";
94124
94518
 
94125
94519
  class WorkflowSourceRejectionError extends UsageError {
94126
94520
  sourcePaths;
@@ -94160,63 +94554,67 @@ class WorkflowSourcePathIdentityError extends WorkflowSourceRejectionError {
94160
94554
  function workflowNameForSourcePath(sourceRoot, adapterId, sourcePath) {
94161
94555
  if (adapterId !== "akm" && adapterId !== "akm-workflow")
94162
94556
  return;
94163
- const relativePath = toPosix(path54.relative(path54.resolve(sourceRoot), path54.resolve(sourcePath)));
94557
+ const relativePath = toPosix(path55.relative(path55.resolve(sourceRoot), path55.resolve(sourcePath)));
94164
94558
  if (!isSafeRelativeName(relativePath))
94165
94559
  return;
94166
94560
  const ownedPath = adapterId === "akm" ? relativePath.replace(/^workflows\//, "") : relativePath;
94167
94561
  if (adapterId === "akm" && ownedPath === relativePath)
94168
94562
  return;
94169
- const extension = path54.posix.extname(ownedPath);
94563
+ const extension = path55.posix.extname(ownedPath);
94170
94564
  if (!WORKFLOW_EXTENSIONS.includes(extension.toLowerCase()))
94171
94565
  return;
94172
94566
  return ownedPath;
94173
94567
  }
94174
- function listWorkflowSourceFiles(sourceRoot, adapterId, name) {
94568
+ function resolveWorkflowSourceDomains(sourceRoot, adapterId, sourcePaths) {
94175
94569
  if (adapterId !== "akm" && adapterId !== "akm-workflow")
94176
94570
  return [];
94177
- const canonicalName = canonicalizeWorkflowName(normalizeName2(name));
94178
- if (!isSafeRelativeName(canonicalName)) {
94179
- throw new UsageError("Workflow ref resolves outside the bundle root.", "PATH_ESCAPE_VIOLATION");
94180
- }
94571
+ const authoredRoot = path55.resolve(sourceRoot);
94181
94572
  let realRoot;
94182
- const authoredRoot = path54.resolve(sourceRoot);
94183
94573
  try {
94184
- realRoot = fs43.realpathSync(authoredRoot);
94574
+ realRoot = fs46.realpathSync(authoredRoot);
94185
94575
  } catch {
94186
94576
  return [];
94187
94577
  }
94188
- const ownershipRoot = adapterId === "akm" ? path54.join(authoredRoot, "workflows") : authoredRoot;
94189
- const parent = path54.join(ownershipRoot, path54.dirname(canonicalName));
94190
- if (!isWithinResolved(parent, authoredRoot)) {
94191
- throw new UsageError("Workflow ref resolves outside the bundle root.", "PATH_ESCAPE_VIOLATION");
94192
- }
94193
- let entries;
94194
- try {
94195
- entries = fs43.readdirSync(parent, { withFileTypes: true });
94196
- } catch {
94197
- return [];
94198
- }
94199
- const basename = path54.basename(canonicalName);
94200
- const candidates = [];
94201
- for (const entry of entries) {
94202
- if (!entry.isFile() && !entry.isSymbolicLink())
94578
+ const candidatesByName = new Map;
94579
+ const seenAuthoredPaths = new Set;
94580
+ for (const sourcePath of sourcePaths) {
94581
+ const normalizedSourcePath = path55.resolve(sourcePath);
94582
+ if (seenAuthoredPaths.has(normalizedSourcePath))
94203
94583
  continue;
94204
- const extension = path54.extname(entry.name);
94584
+ seenAuthoredPaths.add(normalizedSourcePath);
94585
+ const authoredName = workflowNameForSourcePath(authoredRoot, adapterId, normalizedSourcePath);
94586
+ if (authoredName === undefined)
94587
+ continue;
94588
+ const canonicalName = canonicalizeWorkflowName(authoredName);
94589
+ if (!isSafeRelativeName(canonicalName))
94590
+ continue;
94591
+ const extension = path55.extname(normalizedSourcePath);
94205
94592
  const lowerExtension = extension.toLowerCase();
94206
94593
  if (!WORKFLOW_EXTENSIONS.includes(lowerExtension))
94207
94594
  continue;
94208
- if (entry.name.slice(0, -extension.length) !== basename)
94209
- continue;
94210
- const candidatePath = path54.join(parent, entry.name);
94211
- candidates.push({
94212
- path: candidatePath,
94213
- relativePath: toPosix(path54.relative(authoredRoot, candidatePath)),
94595
+ const candidate = {
94596
+ path: normalizedSourcePath,
94597
+ relativePath: toPosix(path55.relative(authoredRoot, normalizedSourcePath)),
94214
94598
  lowerExtension,
94215
- extensionlessStem: entry.name.slice(0, -extension.length)
94599
+ extensionlessStem: path55.basename(normalizedSourcePath).slice(0, -extension.length)
94600
+ };
94601
+ const domain = candidatesByName.get(canonicalName) ?? [];
94602
+ domain.push(candidate);
94603
+ candidatesByName.set(canonicalName, domain);
94604
+ }
94605
+ const resolutions = [];
94606
+ for (const canonicalName of [...candidatesByName.keys()].sort(compareCodePoints)) {
94607
+ const candidates = candidatesByName.get(canonicalName) ?? [];
94608
+ candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
94609
+ const sources = inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
94610
+ const sourcePaths2 = candidates.map((candidate) => candidate.relativePath);
94611
+ resolutions.push({
94612
+ canonicalName,
94613
+ sourcePaths: sourcePaths2,
94614
+ source: pickWorkflowSource(adapterId, canonicalName, sources)
94216
94615
  });
94217
94616
  }
94218
- candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
94219
- return inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
94617
+ return resolutions;
94220
94618
  }
94221
94619
  function inspectWorkflowSourceDomain(candidates, canonicalName, realRoot) {
94222
94620
  const sources = [];
@@ -94245,7 +94643,7 @@ function inspectWorkflowSourceCandidate(candidate, canonicalName, realRoot) {
94245
94643
  const issues = [];
94246
94644
  let authoredStat;
94247
94645
  try {
94248
- authoredStat = fs43.lstatSync(candidate.path);
94646
+ authoredStat = fs46.lstatSync(candidate.path);
94249
94647
  } catch {
94250
94648
  issues.push(new WorkflowSourceLinkResolutionError(candidate.relativePath));
94251
94649
  return { issues };
@@ -94257,21 +94655,21 @@ function inspectWorkflowSourceCandidate(candidate, canonicalName, realRoot) {
94257
94655
  }
94258
94656
  let realPath;
94259
94657
  try {
94260
- realPath = fs43.realpathSync(candidate.path);
94658
+ realPath = fs46.realpathSync(candidate.path);
94261
94659
  } catch {
94262
94660
  issues.push(new WorkflowSourceLinkResolutionError(candidate.relativePath));
94263
94661
  return { issues };
94264
94662
  }
94265
- const targetPath = toPosix(path54.relative(realRoot, realPath));
94663
+ const targetPath = toPosix(path55.relative(realRoot, realPath));
94266
94664
  const contained2 = isWithinResolved(realPath, realRoot);
94267
94665
  if (!contained2)
94268
94666
  issues.push(new WorkflowSourcePathIdentityError(candidate.relativePath, targetPath));
94269
- if (isLink && path54.extname(realPath).toLowerCase() !== candidate.lowerExtension) {
94667
+ if (isLink && path55.extname(realPath).toLowerCase() !== candidate.lowerExtension) {
94270
94668
  issues.push(new WorkflowSourceLinkIdentityError(candidate.relativePath, targetPath));
94271
94669
  }
94272
94670
  if (contained2) {
94273
94671
  try {
94274
- if (!fs43.statSync(realPath).isFile())
94672
+ if (!fs46.statSync(realPath).isFile())
94275
94673
  issues.push(new WorkflowSourceLinkResolutionError(candidate.relativePath));
94276
94674
  } catch {
94277
94675
  issues.push(new WorkflowSourceLinkResolutionError(candidate.relativePath));
@@ -94290,20 +94688,12 @@ function inspectWorkflowSourceCandidate(candidate, canonicalName, realRoot) {
94290
94688
  }
94291
94689
  };
94292
94690
  }
94293
- function resolveUniqueWorkflowSource(sourceRoot, adapterId, name) {
94294
- const sources = listWorkflowSourceFiles(sourceRoot, adapterId, name);
94295
- const canonicalName = sources[0]?.canonicalName ?? canonicalizeWorkflowName(normalizeName2(name));
94296
- return pickWorkflowSource(adapterId, canonicalName, sources);
94297
- }
94298
- function normalizeName2(name) {
94299
- return name.replaceAll("\\", "/");
94300
- }
94301
94691
  function isSafeRelativeName(name) {
94302
- return name.length > 0 && !path54.posix.isAbsolute(name) && name !== ".." && !name.startsWith("../") && path54.posix.normalize(name) === name;
94692
+ return name.length > 0 && !path55.posix.isAbsolute(name) && name !== ".." && !name.startsWith("../") && path55.posix.normalize(name) === name;
94303
94693
  }
94304
94694
  function isWithinResolved(candidate, root) {
94305
- const relative = path54.relative(root, path54.resolve(candidate));
94306
- return relative === "" || !relative.startsWith("..") && !path54.isAbsolute(relative);
94695
+ const relative = path55.relative(root, path55.resolve(candidate));
94696
+ return relative === "" || !relative.startsWith("..") && !path55.isAbsolute(relative);
94307
94697
  }
94308
94698
 
94309
94699
  // src/indexer/scan/drain-dir.ts
@@ -94312,13 +94702,14 @@ init_metadata();
94312
94702
  init_file_context();
94313
94703
 
94314
94704
  // src/indexer/scan/doc-to-entry.ts
94315
- import path56 from "node:path";
94705
+ init_metadata();
94706
+ import path57 from "node:path";
94316
94707
  function indexDocumentToStashEntry(doc) {
94317
94708
  const dj = doc.documentJson ?? {};
94318
94709
  const entry = {
94319
94710
  name: doc.name,
94320
94711
  type: doc.type,
94321
- filename: path56.basename(doc.path ?? "")
94712
+ filename: path57.basename(doc.path ?? "")
94322
94713
  };
94323
94714
  if (doc.description !== undefined)
94324
94715
  entry.description = doc.description;
@@ -94328,6 +94719,8 @@ function indexDocumentToStashEntry(doc) {
94328
94719
  entry.content = doc.content;
94329
94720
  if (doc.contentTruncated !== undefined)
94330
94721
  entry.contentTruncated = doc.contentTruncated;
94722
+ if (hasMarkdownFragmentContent(doc))
94723
+ setMarkdownFragmentContent(entry, getMarkdownFragmentContent(doc));
94331
94724
  if (doc.ownsPresentation !== undefined)
94332
94725
  entry.ownsPresentation = doc.ownsPresentation;
94333
94726
  if (doc.updated !== undefined)
@@ -94421,31 +94814,28 @@ function drainDirDocuments(adapter, component, fileContexts) {
94421
94814
  const conceptIdByFile = new Map;
94422
94815
  const rejectedPaths = new Set;
94423
94816
  const rejectedConceptIds = new Set;
94424
- const workflowLookups = new Map;
94425
- for (const file of fileContexts) {
94817
+ 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)]));
94818
+ const invalidWorkflowOwnerNames = new Set;
94819
+ const orderedFileContexts = [...fileContexts].sort((left, right) => {
94820
+ const leftName = workflowNameForSourcePath(component.root, adapter.id, left.absPath);
94821
+ const rightName = workflowNameForSourcePath(component.root, adapter.id, right.absPath);
94822
+ const leftOwner = leftName !== undefined && workflowOwnerPathByCanonicalName.get(canonicalizeWorkflowName(leftName)) === path58.resolve(left.absPath);
94823
+ const rightOwner = rightName !== undefined && workflowOwnerPathByCanonicalName.get(canonicalizeWorkflowName(rightName)) === path58.resolve(right.absPath);
94824
+ if (leftOwner !== rightOwner)
94825
+ return leftOwner ? -1 : 1;
94826
+ return compareCodePoints(left.absPath, right.absPath);
94827
+ });
94828
+ for (const file of orderedFileContexts) {
94829
+ if (rejectedPaths.has(file.absPath))
94830
+ continue;
94426
94831
  const workflowName = workflowNameForSourcePath(component.root, adapter.id, file.absPath);
94427
94832
  if (workflowName !== undefined) {
94428
94833
  const canonicalName = canonicalizeWorkflowName(workflowName);
94429
- if (!workflowLookups.has(canonicalName))
94430
- workflowLookups.set(canonicalName, workflowName);
94431
- }
94432
- }
94433
- for (const [canonicalName, workflowName] of [...workflowLookups].sort(([left], [right]) => compareCodePoints(left, right))) {
94434
- try {
94435
- resolveUniqueWorkflowSource(component.root, adapter.id, workflowName);
94436
- } catch (error2) {
94437
- if (!(error2 instanceof WorkflowSourceRejectionError))
94438
- throw error2;
94439
- rejectedConceptIds.add(adapter.id === "akm" ? `workflows/${canonicalName}` : canonicalName);
94440
- for (const relativePath of error2.sourcePaths) {
94441
- rejectedPaths.add(path57.join(component.root, relativePath));
94834
+ const ownerPath = workflowOwnerPathByCanonicalName.get(canonicalName);
94835
+ if (ownerPath !== undefined && ownerPath !== path58.resolve(file.absPath) && !invalidWorkflowOwnerNames.has(canonicalName)) {
94836
+ continue;
94442
94837
  }
94443
- warnings.push(error2.message);
94444
94838
  }
94445
- }
94446
- for (const file of fileContexts) {
94447
- if (rejectedPaths.has(file.absPath))
94448
- continue;
94449
94839
  const doc = adapter.recognize(component, file);
94450
94840
  if (doc === null)
94451
94841
  continue;
@@ -94457,6 +94847,8 @@ function drainDirDocuments(adapter, component, fileContexts) {
94457
94847
  const dropWarning = handleWorkflowDoc(doc, file, component.root);
94458
94848
  if (dropWarning !== null) {
94459
94849
  warnings.push(dropWarning);
94850
+ if (workflowName !== undefined)
94851
+ invalidWorkflowOwnerNames.add(canonicalizeWorkflowName(workflowName));
94460
94852
  continue;
94461
94853
  }
94462
94854
  if (doc.hash !== undefined)
@@ -94500,7 +94892,7 @@ async function indexWrittenAssets(stashDir, filePaths, options = {}) {
94500
94892
  if (isPathAbsent(dbPath))
94501
94893
  return true;
94502
94894
  const files = filePaths.filter((f) => {
94503
- const rel = path58.relative(stashDir, f);
94895
+ const rel = path59.relative(stashDir, f);
94504
94896
  return !rel.split(/[\\/]+/).some((segment) => segment.startsWith("."));
94505
94897
  });
94506
94898
  if (files.length === 0)
@@ -94514,10 +94906,10 @@ async function indexWrittenAssets(stashDir, filePaths, options = {}) {
94514
94906
  const unindexable = new Set;
94515
94907
  const rejectedConceptIds = new Set;
94516
94908
  for (const file of files) {
94517
- if (!fs46.existsSync(file)) {
94909
+ if (!fs48.existsSync(file)) {
94518
94910
  let authoredDanglingSymlink = false;
94519
94911
  try {
94520
- authoredDanglingSymlink = fs46.lstatSync(file).isSymbolicLink();
94912
+ authoredDanglingSymlink = fs48.lstatSync(file).isSymbolicLink();
94521
94913
  } catch {}
94522
94914
  if (!authoredDanglingSymlink) {
94523
94915
  unindexable.add(file);
@@ -94568,7 +94960,10 @@ async function indexWrittenAssets(stashDir, filePaths, options = {}) {
94568
94960
  for (const { file, entry, conceptId, contentHash } of pairs) {
94569
94961
  let entryWithSize = entry;
94570
94962
  try {
94571
- entryWithSize = { ...entry, fileSize: fs46.statSync(file).size };
94963
+ entryWithSize = { ...entry, fileSize: fs48.statSync(file).size };
94964
+ if (hasMarkdownFragmentContent(entry)) {
94965
+ setMarkdownFragmentContent(entryWithSize, getMarkdownFragmentContent(entry));
94966
+ }
94572
94967
  } catch {}
94573
94968
  const provenance = deriveEntryProvenance({ bundleId: component.id, componentId: component.id, adapterId: component.adapter }, entry.type, entry.name, conceptId);
94574
94969
  const supersededIds = db.prepare("SELECT id FROM entries WHERE file_path = ? AND item_ref <> ?").all(file, provenance.itemRef);
@@ -94601,7 +94996,7 @@ async function indexWrittenAssets(stashDir, filePaths, options = {}) {
94601
94996
  init_asset_placement();
94602
94997
  init_asset_ref();
94603
94998
  init_warn();
94604
- import path59 from "node:path";
94999
+ import path60 from "node:path";
94605
95000
  function changesToStored(changes) {
94606
95001
  return changes.map((c, i) => ({
94607
95002
  path: c.path,
@@ -94654,10 +95049,10 @@ function currentProposalTarget(value) {
94654
95049
  if (typeof value !== "object" || value === null)
94655
95050
  throw new Error("Proposal metadata has an invalid proposedTarget.");
94656
95051
  const target = value;
94657
- if (typeof target.source !== "string" || !isBundleSlug(target.source) || typeof target.root !== "string" || !path59.isAbsolute(target.root)) {
95052
+ if (typeof target.source !== "string" || !isBundleSlug(target.source) || typeof target.root !== "string" || !path60.isAbsolute(target.root)) {
94658
95053
  throw new Error("Proposal metadata has an invalid proposedTarget.");
94659
95054
  }
94660
- return { source: target.source, root: path59.resolve(target.root) };
95055
+ return { source: target.source, root: path60.resolve(target.root) };
94661
95056
  }
94662
95057
  function invalidPresentField(name) {
94663
95058
  throw new Error(`Proposal metadata has an invalid ${name}.`);
@@ -94697,7 +95092,7 @@ function validatePresentMetadata(meta) {
94697
95092
  }
94698
95093
  if (Object.hasOwn(meta, "acceptedTarget")) {
94699
95094
  const target = meta.acceptedTarget;
94700
- if (typeof target !== "object" || target === null || typeof target.source !== "string" || !isBundleSlug(target.source) || typeof target.root !== "string" || !path59.isAbsolute(target.root) || typeof target.path !== "string" || !path59.isAbsolute(target.path) || typeof target.contentHash !== "string") {
95095
+ if (typeof target !== "object" || target === null || typeof target.source !== "string" || !isBundleSlug(target.source) || typeof target.root !== "string" || !path60.isAbsolute(target.root) || typeof target.path !== "string" || !path60.isAbsolute(target.path) || typeof target.contentHash !== "string") {
94701
95096
  invalidPresentField("acceptedTarget");
94702
95097
  }
94703
95098
  }
@@ -95428,15 +95823,15 @@ var PROPOSAL_TXN_PHASES = [
95428
95823
  "committed"
95429
95824
  ];
95430
95825
  function proposalHash(content) {
95431
- return createHash9("sha256").update(content).digest("hex");
95826
+ return createHash10("sha256").update(content).digest("hex");
95432
95827
  }
95433
95828
  function proposalFileHash(filePath) {
95434
- return proposalHash(fs47.readFileSync(filePath));
95829
+ return proposalHash(fs49.readFileSync(filePath));
95435
95830
  }
95436
95831
  function sameProposalFile(left, right) {
95437
95832
  try {
95438
- const leftStat = fs47.statSync(left);
95439
- const rightStat = fs47.statSync(right);
95833
+ const leftStat = fs49.statSync(left);
95834
+ const rightStat = fs49.statSync(right);
95440
95835
  return leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
95441
95836
  } catch {
95442
95837
  return false;
@@ -95445,20 +95840,20 @@ function sameProposalFile(left, right) {
95445
95840
  function cleanupProposalPublication(p) {
95446
95841
  for (const filePath of [p.publishPath, p.displacedPath]) {
95447
95842
  try {
95448
- fs47.rmSync(filePath, { force: true });
95843
+ fs49.rmSync(filePath, { force: true });
95449
95844
  } catch (error2) {
95450
95845
  warn(`[proposals] transaction publication cleanup failed at ${filePath}: ${error2 instanceof Error ? error2.message : String(error2)}`);
95451
95846
  }
95452
95847
  }
95453
- fsyncTxnDir(path60.dirname(p.assetPath));
95848
+ fsyncTxnDir(path61.dirname(p.assetPath));
95454
95849
  }
95455
95850
  function rollbackPreparedProposalTransaction(txn) {
95456
95851
  const p = txn.journal.payload;
95457
- const currentHash = fs47.existsSync(p.assetPath) ? proposalFileHash(p.assetPath) : null;
95458
- if (!fs47.existsSync(p.displacedPath)) {
95852
+ const currentHash = fs49.existsSync(p.assetPath) ? proposalFileHash(p.assetPath) : null;
95853
+ if (!fs49.existsSync(p.displacedPath)) {
95459
95854
  if (p.originalHash === null) {
95460
95855
  if (currentHash === p.publishedHash && sameProposalFile(p.assetPath, p.publishPath)) {
95461
- fs47.unlinkSync(p.assetPath);
95856
+ fs49.unlinkSync(p.assetPath);
95462
95857
  recordWrittenPath(p.assetPath);
95463
95858
  } else if (currentHash !== null) {
95464
95859
  throw new Error(`Cannot roll back proposal transaction: target was created externally.`);
@@ -95470,30 +95865,30 @@ function rollbackPreparedProposalTransaction(txn) {
95470
95865
  return;
95471
95866
  }
95472
95867
  if (currentHash === p.publishedHash) {
95473
- fs47.unlinkSync(p.assetPath);
95868
+ fs49.unlinkSync(p.assetPath);
95474
95869
  recordWrittenPath(p.assetPath);
95475
95870
  } else if (currentHash !== null && currentHash !== p.originalHash) {
95476
95871
  throw new Error(`Cannot roll back proposal transaction: ${p.assetPath} diverged.`);
95477
95872
  }
95478
- if (fs47.existsSync(p.displacedPath)) {
95479
- if (fs47.existsSync(p.assetPath)) {
95873
+ if (fs49.existsSync(p.displacedPath)) {
95874
+ if (fs49.existsSync(p.assetPath)) {
95480
95875
  throw new Error(`Cannot restore proposal backup: ${p.assetPath} is occupied.`);
95481
95876
  }
95482
- fs47.linkSync(p.displacedPath, p.assetPath);
95877
+ fs49.linkSync(p.displacedPath, p.assetPath);
95483
95878
  recordWrittenPath(p.assetPath);
95484
95879
  }
95485
95880
  cleanupProposalPublication(p);
95486
95881
  }
95487
95882
  function validatePublishedProposal(p) {
95488
- if (!fs47.existsSync(p.assetPath) || proposalFileHash(p.assetPath) !== p.publishedHash) {
95883
+ if (!fs49.existsSync(p.assetPath) || proposalFileHash(p.assetPath) !== p.publishedHash) {
95489
95884
  throw new Error(`Cannot recover proposal ${p.proposalId}: published asset diverged.`);
95490
95885
  }
95491
95886
  }
95492
95887
  function persistProposalTransactionState(txn, proposal, ctx) {
95493
95888
  const p = txn.journal.payload;
95494
95889
  const decidedAt = txn.journal.decidedAt;
95495
- const backupContent = p.backupPath ? fs47.readFileSync(p.backupPath, "utf8") : undefined;
95496
- const publishedContent = fs47.readFileSync(p.contentPath, "utf8");
95890
+ const backupContent = p.backupPath ? fs49.readFileSync(p.backupPath, "utf8") : undefined;
95891
+ const publishedContent = fs49.readFileSync(p.contentPath, "utf8");
95497
95892
  return withProposalsDb(p.stashDir, ctx, (db) => withImmediateTransaction(db, () => {
95498
95893
  const current = requireProposal(db, p.stashDir, p.proposalId);
95499
95894
  if (p.operation === "accept") {
@@ -95511,7 +95906,7 @@ function persistProposalTransactionState(txn, proposal, ctx) {
95511
95906
  payload: { ...proposal.payload, content: publishedContent },
95512
95907
  changes: [
95513
95908
  {
95514
- path: path60.relative(txn.journal.root, p.assetPath),
95909
+ path: path61.relative(txn.journal.root, p.assetPath),
95515
95910
  op: p.originalHash === null ? "create" : "update",
95516
95911
  after: publishedContent
95517
95912
  }
@@ -95582,7 +95977,7 @@ async function finalizeProposalTransaction(txn, target, proposal, ctx) {
95582
95977
  cleanupProposalPublication(p);
95583
95978
  if (txn.journal.phase === "asset-published") {
95584
95979
  const commitRoot = target.source.repoPath ?? target.source.path;
95585
- const commitPath = path60.relative(commitRoot, p.assetPath).replaceAll(path60.sep, "/");
95980
+ const commitPath = path61.relative(commitRoot, p.assetPath).replaceAll(path61.sep, "/");
95586
95981
  publishWriteTargetTransaction(target, p.gitPublication, {
95587
95982
  transactionId: txn.journal.transactionId,
95588
95983
  message: `${p.operation === "accept" ? "Update" : "Revert"} ${p.ref}`,
@@ -95619,8 +96014,8 @@ async function finalizeProposalTransaction(txn, target, proposal, ctx) {
95619
96014
  function fenceProposalTxnJournal(journal, txnDir, root) {
95620
96015
  const p = journal.payload;
95621
96016
  const refIdentity = proposalRefIdentity(p.ref);
95622
- if (!["accept", "revert"].includes(p.operation) || !p.targetSource || !p.targetKind || refIdentity?.bundle === undefined || !isWithin(p.assetPath, root) || ![p.contentPath, p.backupPath].filter((candidate) => candidate !== null).every((candidate) => isWithin(candidate, txnDir)) || ![p.publishPath, p.displacedPath].every((candidate) => isWithin(candidate, root) && path60.dirname(candidate) === path60.dirname(p.assetPath))) {
95623
- throw new Error(`Refusing unsafe proposal transaction journal at ${path60.join(txnDir, "journal.json")}.`);
96017
+ if (!["accept", "revert"].includes(p.operation) || !p.targetSource || !p.targetKind || refIdentity?.bundle === undefined || !isWithin(p.assetPath, root) || ![p.contentPath, p.backupPath].filter((candidate) => candidate !== null).every((candidate) => isWithin(candidate, txnDir)) || ![p.publishPath, p.displacedPath].every((candidate) => isWithin(candidate, root) && path61.dirname(candidate) === path61.dirname(p.assetPath))) {
96018
+ throw new Error(`Refusing unsafe proposal transaction journal at ${path61.join(txnDir, "journal.json")}.`);
95624
96019
  }
95625
96020
  }
95626
96021
  function resolveProposalRecoveryTarget(config, journal) {
@@ -95720,8 +96115,8 @@ async function recoverStaleTxns(stashDir) {
95720
96115
  }
95721
96116
 
95722
96117
  // scripts/akm-migrate/migrate/writer-relocation.ts
95723
- import fs48 from "node:fs";
95724
- import path61 from "node:path";
96118
+ import fs50 from "node:fs";
96119
+ import path62 from "node:path";
95725
96120
  init_paths();
95726
96121
  function relocationSpecs(stashDir) {
95727
96122
  return [
@@ -95741,7 +96136,7 @@ function mutexSiblingName(lockName) {
95741
96136
  function fileCountIfExists(dir) {
95742
96137
  let entries;
95743
96138
  try {
95744
- entries = fs48.readdirSync(dir, { withFileTypes: true });
96139
+ entries = fs50.readdirSync(dir, { withFileTypes: true });
95745
96140
  } catch {
95746
96141
  return;
95747
96142
  }
@@ -95749,7 +96144,7 @@ function fileCountIfExists(dir) {
95749
96144
  }
95750
96145
  function statFileIfExists(filePath) {
95751
96146
  try {
95752
- const stat = fs48.statSync(filePath);
96147
+ const stat = fs50.statSync(filePath);
95753
96148
  return stat.isFile() ? stat : undefined;
95754
96149
  } catch {
95755
96150
  return;
@@ -95759,8 +96154,8 @@ function classifyLockArtifacts(akmDir) {
95759
96154
  const removable = [];
95760
96155
  const skipped = [];
95761
96156
  for (const lockName of LOCK_NAMES) {
95762
- const lockPath = path61.join(akmDir, lockName);
95763
- const mutexPath = path61.join(akmDir, mutexSiblingName(lockName));
96157
+ const lockPath = path62.join(akmDir, lockName);
96158
+ const mutexPath = path62.join(akmDir, mutexSiblingName(lockName));
95764
96159
  const lockStat = statFileIfExists(lockPath);
95765
96160
  const mutexStat = statFileIfExists(mutexPath);
95766
96161
  if (!lockStat) {
@@ -95786,11 +96181,11 @@ function classifyLockArtifacts(akmDir) {
95786
96181
  return { removable, skipped };
95787
96182
  }
95788
96183
  function findWriterRelocationEntries(stashDir) {
95789
- const akmDir = path61.join(stashDir, ".akm");
96184
+ const akmDir = path62.join(stashDir, ".akm");
95790
96185
  const directories = [];
95791
96186
  for (const spec of relocationSpecs(stashDir)) {
95792
96187
  const relativeParts = Array.isArray(spec.oldRelative) ? spec.oldRelative : [spec.oldRelative];
95793
- const oldPath = path61.join(akmDir, ...relativeParts);
96188
+ const oldPath = path62.join(akmDir, ...relativeParts);
95794
96189
  const fileCount = fileCountIfExists(oldPath);
95795
96190
  if (fileCount === undefined || fileCount === 0)
95796
96191
  continue;
@@ -95801,36 +96196,36 @@ function findWriterRelocationEntries(stashDir) {
95801
96196
  }
95802
96197
  function moveFile(oldFilePath, newFilePath) {
95803
96198
  try {
95804
- fs48.renameSync(oldFilePath, newFilePath);
96199
+ fs50.renameSync(oldFilePath, newFilePath);
95805
96200
  } catch (error2) {
95806
96201
  if (error2.code !== "EXDEV")
95807
96202
  throw error2;
95808
- fs48.copyFileSync(oldFilePath, newFilePath);
95809
- fs48.rmSync(oldFilePath, { force: true });
96203
+ fs50.copyFileSync(oldFilePath, newFilePath);
96204
+ fs50.rmSync(oldFilePath, { force: true });
95810
96205
  }
95811
96206
  }
95812
96207
  function moveDirectoryContents(entry) {
95813
96208
  const errors3 = [];
95814
96209
  let moved = 0;
95815
- fs48.mkdirSync(entry.newPath, { recursive: true });
96210
+ fs50.mkdirSync(entry.newPath, { recursive: true });
95816
96211
  let names;
95817
96212
  try {
95818
- names = fs48.readdirSync(entry.oldPath).sort();
96213
+ names = fs50.readdirSync(entry.oldPath).sort();
95819
96214
  } catch {
95820
96215
  return { key: entry.key, oldPath: entry.oldPath, newPath: entry.newPath, moved: 0, errors: [] };
95821
96216
  }
95822
96217
  for (const name of names) {
95823
- const oldFilePath = path61.join(entry.oldPath, name);
95824
- const newFilePath = path61.join(entry.newPath, name);
96218
+ const oldFilePath = path62.join(entry.oldPath, name);
96219
+ const newFilePath = path62.join(entry.newPath, name);
95825
96220
  let oldStat;
95826
96221
  try {
95827
- oldStat = fs48.lstatSync(oldFilePath);
96222
+ oldStat = fs50.lstatSync(oldFilePath);
95828
96223
  } catch {
95829
96224
  continue;
95830
96225
  }
95831
96226
  if (!oldStat.isFile())
95832
96227
  continue;
95833
- if (fs48.existsSync(newFilePath))
96228
+ if (fs50.existsSync(newFilePath))
95834
96229
  continue;
95835
96230
  try {
95836
96231
  moveFile(oldFilePath, newFilePath);
@@ -95843,13 +96238,13 @@ function moveDirectoryContents(entry) {
95843
96238
  }
95844
96239
  function removeIfEmptyDir(dir) {
95845
96240
  try {
95846
- if (fs48.readdirSync(dir).length === 0)
95847
- fs48.rmdirSync(dir);
96241
+ if (fs50.readdirSync(dir).length === 0)
96242
+ fs50.rmdirSync(dir);
95848
96243
  } catch {}
95849
96244
  }
95850
96245
  function removeLockArtifact(entry) {
95851
96246
  try {
95852
- fs48.rmSync(entry.path, { force: true });
96247
+ fs50.rmSync(entry.path, { force: true });
95853
96248
  return { path: entry.path, removed: true };
95854
96249
  } catch (error2) {
95855
96250
  return { path: entry.path, removed: false, error: error2 instanceof Error ? error2.message : String(error2) };
@@ -95861,36 +96256,36 @@ function applyWriterRelocation(stashDir) {
95861
96256
  const lockResults = lockArtifacts.map(removeLockArtifact);
95862
96257
  for (const spec of relocationSpecs(stashDir)) {
95863
96258
  const relativeParts = Array.isArray(spec.oldRelative) ? spec.oldRelative : [spec.oldRelative];
95864
- removeIfEmptyDir(path61.join(stashDir, ".akm", ...relativeParts));
96259
+ removeIfEmptyDir(path62.join(stashDir, ".akm", ...relativeParts));
95865
96260
  }
95866
96261
  return { directories: directoryResults, lockArtifacts: lockResults, skippedLocks };
95867
96262
  }
95868
96263
 
95869
96264
  // scripts/akm-migrate/task-migrate.ts
95870
96265
  import { randomUUID as randomUUID7 } from "node:crypto";
95871
- import fs53 from "node:fs";
96266
+ import fs56 from "node:fs";
95872
96267
  import os5 from "node:os";
95873
- import path64 from "node:path";
96268
+ import path65 from "node:path";
95874
96269
  init_errors();
95875
96270
  init_paths();
95876
96271
 
95877
96272
  // scripts/akm-migrate/migrate/task-files-to-v3.ts
95878
96273
  init_errors();
95879
96274
  import crypto6 from "node:crypto";
95880
- import fs50 from "node:fs";
95881
- import path62 from "node:path";
96275
+ import fs53 from "node:fs";
96276
+ import path63 from "node:path";
95882
96277
 
95883
96278
  // scripts/akm-migrate/migrate/durable-fs.ts
95884
- import fs49 from "node:fs";
96279
+ import fs51 from "node:fs";
95885
96280
  function fsyncDirectoryPortable(directory) {
95886
96281
  if (process.platform === "win32")
95887
96282
  return;
95888
96283
  try {
95889
- const fd = fs49.openSync(directory, "r");
96284
+ const fd = fs51.openSync(directory, "r");
95890
96285
  try {
95891
- fs49.fsyncSync(fd);
96286
+ fs51.fsyncSync(fd);
95892
96287
  } finally {
95893
- fs49.closeSync(fd);
96288
+ fs51.closeSync(fd);
95894
96289
  }
95895
96290
  } catch (cause) {
95896
96291
  const code = cause.code;
@@ -95904,25 +96299,25 @@ function migrationError(detail) {
95904
96299
  return new ConfigError(`Task migration to v3 failed: ${detail}`, "INVALID_CONFIG_FILE");
95905
96300
  }
95906
96301
  function contained2(root, candidate) {
95907
- const relative = path62.relative(root, candidate);
95908
- return relative === "" || !relative.startsWith("..") && !path62.isAbsolute(relative);
96302
+ const relative = path63.relative(root, candidate);
96303
+ return relative === "" || !relative.startsWith("..") && !path63.isAbsolute(relative);
95909
96304
  }
95910
96305
  function realDirectory(filePath) {
95911
- const stat = fs50.lstatSync(filePath);
96306
+ const stat = fs53.lstatSync(filePath);
95912
96307
  if (stat.isSymbolicLink() || !stat.isDirectory())
95913
96308
  throw migrationError(`${filePath} must be a real directory.`);
95914
- return fs50.realpathSync(filePath);
96309
+ return fs53.realpathSync(filePath);
95915
96310
  }
95916
96311
  function snapshot(filePath) {
95917
- const stat = fs50.lstatSync(filePath);
96312
+ const stat = fs53.lstatSync(filePath);
95918
96313
  if (stat.isSymbolicLink() || !stat.isFile())
95919
96314
  throw migrationError(`${filePath} must be a real file.`);
95920
- const bytes = fs50.readFileSync(filePath);
96315
+ const bytes = fs53.readFileSync(filePath);
95921
96316
  return Object.freeze({ bytes, mode: stat.mode & 511 });
95922
96317
  }
95923
96318
  function writable(filePath) {
95924
96319
  try {
95925
- fs50.accessSync(filePath, fs50.constants.W_OK);
96320
+ fs53.accessSync(filePath, fs53.constants.W_OK);
95926
96321
  return true;
95927
96322
  } catch {
95928
96323
  return false;
@@ -95935,12 +96330,12 @@ function walkTasks(root, tasksDir, out) {
95935
96330
  throw migrationError(`${root.root} resolves outside bundle ${root.bundleId}.`);
95936
96331
  }
95937
96332
  const visit2 = (directory) => {
95938
- const physicalDirectory = fs50.realpathSync(directory);
96333
+ const physicalDirectory = fs53.realpathSync(directory);
95939
96334
  if (!contained2(physicalRoot, physicalDirectory)) {
95940
96335
  throw migrationError(`${directory} resolves outside bundle ${root.bundleId}.`);
95941
96336
  }
95942
- for (const entry of fs50.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
95943
- const candidate = path62.join(directory, entry.name);
96337
+ for (const entry of fs53.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
96338
+ const candidate = path63.join(directory, entry.name);
95944
96339
  if (entry.isSymbolicLink())
95945
96340
  throw migrationError(`task migration does not follow symbolic link ${candidate}.`);
95946
96341
  if (entry.isDirectory()) {
@@ -95950,7 +96345,7 @@ function walkTasks(root, tasksDir, out) {
95950
96345
  if (!entry.isFile() || !entry.name.endsWith(".yml"))
95951
96346
  continue;
95952
96347
  const current = snapshot(candidate);
95953
- const parent = path62.dirname(candidate);
96348
+ const parent = path63.dirname(candidate);
95954
96349
  out.push({
95955
96350
  filePath: candidate,
95956
96351
  bytes: current.bytes,
@@ -95966,9 +96361,9 @@ function walkTasks(root, tasksDir, out) {
95966
96361
  function inspectTaskToV3Files(roots) {
95967
96362
  const files = [];
95968
96363
  for (const root of [...roots].sort((a, b) => a.bundleId.localeCompare(b.bundleId))) {
95969
- const tasksDir = root.layout === "akm-task" ? root.root : path62.join(root.root, "tasks");
96364
+ const tasksDir = root.layout === "akm-task" ? root.root : path63.join(root.root, "tasks");
95970
96365
  try {
95971
- const stat = fs50.lstatSync(tasksDir);
96366
+ const stat = fs53.lstatSync(tasksDir);
95972
96367
  if (stat.isSymbolicLink())
95973
96368
  throw migrationError(`task migration does not follow symbolic link ${tasksDir}.`);
95974
96369
  if (!stat.isDirectory())
@@ -95983,33 +96378,33 @@ function inspectTaskToV3Files(roots) {
95983
96378
  return files.sort((a, b) => a.filePath.localeCompare(b.filePath));
95984
96379
  }
95985
96380
  function hashPath(filePath) {
95986
- return crypto6.createHash("sha256").update(path62.resolve(filePath)).digest("hex").slice(0, 16);
96381
+ return crypto6.createHash("sha256").update(path63.resolve(filePath)).digest("hex").slice(0, 16);
95987
96382
  }
95988
96383
  function taskMigrationBackupPath(backupRoot, filePath) {
95989
- return path62.join(backupRoot, "files", `${hashPath(filePath)}-${path62.basename(filePath)}`);
96384
+ return path63.join(backupRoot, "files", `${hashPath(filePath)}-${path63.basename(filePath)}`);
95990
96385
  }
95991
96386
  function writeDurable(filePath, bytes, mode, exclusive = false) {
95992
- fs50.mkdirSync(path62.dirname(filePath), { recursive: true });
96387
+ fs53.mkdirSync(path63.dirname(filePath), { recursive: true });
95993
96388
  const flags = exclusive ? "wx" : "w";
95994
- const fd = fs50.openSync(filePath, flags, mode);
96389
+ const fd = fs53.openSync(filePath, flags, mode);
95995
96390
  try {
95996
- fs50.writeFileSync(fd, bytes);
95997
- fs50.fsyncSync(fd);
96391
+ fs53.writeFileSync(fd, bytes);
96392
+ fs53.fsyncSync(fd);
95998
96393
  } finally {
95999
- fs50.closeSync(fd);
96394
+ fs53.closeSync(fd);
96000
96395
  }
96001
- fs50.chmodSync(filePath, mode);
96002
- fsyncDirectoryPortable(path62.dirname(filePath));
96396
+ fs53.chmodSync(filePath, mode);
96397
+ fsyncDirectoryPortable(path63.dirname(filePath));
96003
96398
  }
96004
96399
  function replaceAtomically(filePath, bytes, mode) {
96005
- const temporary = path62.join(path62.dirname(filePath), `.${path62.basename(filePath)}.migrate-${crypto6.randomUUID()}`);
96400
+ const temporary = path63.join(path63.dirname(filePath), `.${path63.basename(filePath)}.migrate-${crypto6.randomUUID()}`);
96006
96401
  try {
96007
96402
  writeDurable(temporary, bytes, mode, true);
96008
- fs50.renameSync(temporary, filePath);
96009
- fsyncDirectoryPortable(path62.dirname(filePath));
96403
+ fs53.renameSync(temporary, filePath);
96404
+ fsyncDirectoryPortable(path63.dirname(filePath));
96010
96405
  } finally {
96011
96406
  try {
96012
- fs50.unlinkSync(temporary);
96407
+ fs53.unlinkSync(temporary);
96013
96408
  } catch (cause) {
96014
96409
  if (cause.code !== "ENOENT")
96015
96410
  throw cause;
@@ -96048,7 +96443,7 @@ function applyTaskToV3MigrationPlan(plan, options) {
96048
96443
  const current = snapshot(change.filePath);
96049
96444
  if (!current.bytes.equals(change.after))
96050
96445
  continue;
96051
- replaceAtomically(change.filePath, fs50.readFileSync(taskMigrationBackupPath(options.backupRoot, change.filePath)), change.mode);
96446
+ replaceAtomically(change.filePath, fs53.readFileSync(taskMigrationBackupPath(options.backupRoot, change.filePath)), change.mode);
96052
96447
  }
96053
96448
  throw cause;
96054
96449
  }
@@ -96058,31 +96453,31 @@ function applyTaskToV3MigrationPlan(plan, options) {
96058
96453
  // scripts/akm-migrate/migrate/task-files-to-v4.ts
96059
96454
  init_errors();
96060
96455
  import crypto7 from "node:crypto";
96061
- import fs51 from "node:fs";
96062
- import path63 from "node:path";
96456
+ import fs55 from "node:fs";
96457
+ import path64 from "node:path";
96063
96458
  function migrationError2(detail) {
96064
96459
  return new ConfigError(`Task migration to v4 failed: ${detail}`, "INVALID_CONFIG_FILE");
96065
96460
  }
96066
96461
  function contained3(root, candidate) {
96067
- const relative = path63.relative(root, candidate);
96068
- return relative === "" || !relative.startsWith("..") && !path63.isAbsolute(relative);
96462
+ const relative = path64.relative(root, candidate);
96463
+ return relative === "" || !relative.startsWith("..") && !path64.isAbsolute(relative);
96069
96464
  }
96070
96465
  function realDirectory2(filePath) {
96071
- const stat = fs51.lstatSync(filePath);
96466
+ const stat = fs55.lstatSync(filePath);
96072
96467
  if (stat.isSymbolicLink() || !stat.isDirectory())
96073
96468
  throw migrationError2(`${filePath} must be a real directory.`);
96074
- return fs51.realpathSync(filePath);
96469
+ return fs55.realpathSync(filePath);
96075
96470
  }
96076
96471
  function snapshot2(filePath) {
96077
- const stat = fs51.lstatSync(filePath);
96472
+ const stat = fs55.lstatSync(filePath);
96078
96473
  if (stat.isSymbolicLink() || !stat.isFile())
96079
96474
  throw migrationError2(`${filePath} must be a real file.`);
96080
- const bytes = fs51.readFileSync(filePath);
96475
+ const bytes = fs55.readFileSync(filePath);
96081
96476
  return Object.freeze({ bytes, mode: stat.mode & 511 });
96082
96477
  }
96083
96478
  function writable2(filePath) {
96084
96479
  try {
96085
- fs51.accessSync(filePath, fs51.constants.W_OK);
96480
+ fs55.accessSync(filePath, fs55.constants.W_OK);
96086
96481
  return true;
96087
96482
  } catch {
96088
96483
  return false;
@@ -96095,12 +96490,12 @@ function walkTasks2(root, tasksDir, out) {
96095
96490
  throw migrationError2(`${root.root} resolves outside bundle ${root.bundleId}.`);
96096
96491
  }
96097
96492
  const visit2 = (directory) => {
96098
- const physicalDirectory = fs51.realpathSync(directory);
96493
+ const physicalDirectory = fs55.realpathSync(directory);
96099
96494
  if (!contained3(physicalRoot, physicalDirectory)) {
96100
96495
  throw migrationError2(`${directory} resolves outside bundle ${root.bundleId}.`);
96101
96496
  }
96102
- for (const entry of fs51.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
96103
- const candidate = path63.join(directory, entry.name);
96497
+ for (const entry of fs55.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
96498
+ const candidate = path64.join(directory, entry.name);
96104
96499
  if (entry.isSymbolicLink())
96105
96500
  throw migrationError2(`task migration does not follow symbolic link ${candidate}.`);
96106
96501
  if (entry.isDirectory()) {
@@ -96110,7 +96505,7 @@ function walkTasks2(root, tasksDir, out) {
96110
96505
  if (!entry.isFile() || !entry.name.endsWith(".yml"))
96111
96506
  continue;
96112
96507
  const current = snapshot2(candidate);
96113
- const parent = path63.dirname(candidate);
96508
+ const parent = path64.dirname(candidate);
96114
96509
  out.push({
96115
96510
  filePath: candidate,
96116
96511
  bytes: current.bytes,
@@ -96126,9 +96521,9 @@ function walkTasks2(root, tasksDir, out) {
96126
96521
  function inspectTaskToV4Files(roots) {
96127
96522
  const files = [];
96128
96523
  for (const root of [...roots].sort((a, b) => a.bundleId.localeCompare(b.bundleId))) {
96129
- const tasksDir = root.layout === "akm-task" ? root.root : path63.join(root.root, "tasks");
96524
+ const tasksDir = root.layout === "akm-task" ? root.root : path64.join(root.root, "tasks");
96130
96525
  try {
96131
- const stat = fs51.lstatSync(tasksDir);
96526
+ const stat = fs55.lstatSync(tasksDir);
96132
96527
  if (stat.isSymbolicLink())
96133
96528
  throw migrationError2(`task migration does not follow symbolic link ${tasksDir}.`);
96134
96529
  if (!stat.isDirectory())
@@ -96143,33 +96538,33 @@ function inspectTaskToV4Files(roots) {
96143
96538
  return files.sort((a, b) => a.filePath.localeCompare(b.filePath));
96144
96539
  }
96145
96540
  function hashPath2(filePath) {
96146
- return crypto7.createHash("sha256").update(path63.resolve(filePath)).digest("hex").slice(0, 16);
96541
+ return crypto7.createHash("sha256").update(path64.resolve(filePath)).digest("hex").slice(0, 16);
96147
96542
  }
96148
96543
  function taskMigrationBackupPathV4(backupRoot, filePath) {
96149
- return path63.join(backupRoot, "files", `${hashPath2(filePath)}-${path63.basename(filePath)}`);
96544
+ return path64.join(backupRoot, "files", `${hashPath2(filePath)}-${path64.basename(filePath)}`);
96150
96545
  }
96151
96546
  function writeDurable2(filePath, bytes, mode, exclusive = false) {
96152
- fs51.mkdirSync(path63.dirname(filePath), { recursive: true });
96547
+ fs55.mkdirSync(path64.dirname(filePath), { recursive: true });
96153
96548
  const flags = exclusive ? "wx" : "w";
96154
- const fd = fs51.openSync(filePath, flags, mode);
96549
+ const fd = fs55.openSync(filePath, flags, mode);
96155
96550
  try {
96156
- fs51.writeFileSync(fd, bytes);
96157
- fs51.fsyncSync(fd);
96551
+ fs55.writeFileSync(fd, bytes);
96552
+ fs55.fsyncSync(fd);
96158
96553
  } finally {
96159
- fs51.closeSync(fd);
96554
+ fs55.closeSync(fd);
96160
96555
  }
96161
- fs51.chmodSync(filePath, mode);
96162
- fsyncDirectoryPortable(path63.dirname(filePath));
96556
+ fs55.chmodSync(filePath, mode);
96557
+ fsyncDirectoryPortable(path64.dirname(filePath));
96163
96558
  }
96164
96559
  function replaceAtomically2(filePath, bytes, mode) {
96165
- const temporary = path63.join(path63.dirname(filePath), `.${path63.basename(filePath)}.migrate-${crypto7.randomUUID()}`);
96560
+ const temporary = path64.join(path64.dirname(filePath), `.${path64.basename(filePath)}.migrate-${crypto7.randomUUID()}`);
96166
96561
  try {
96167
96562
  writeDurable2(temporary, bytes, mode, true);
96168
- fs51.renameSync(temporary, filePath);
96169
- fsyncDirectoryPortable(path63.dirname(filePath));
96563
+ fs55.renameSync(temporary, filePath);
96564
+ fsyncDirectoryPortable(path64.dirname(filePath));
96170
96565
  } finally {
96171
96566
  try {
96172
- fs51.unlinkSync(temporary);
96567
+ fs55.unlinkSync(temporary);
96173
96568
  } catch (cause) {
96174
96569
  if (cause.code !== "ENOENT")
96175
96570
  throw cause;
@@ -96208,7 +96603,7 @@ function applyTaskToV4MigrationPlan(plan, options) {
96208
96603
  const current = snapshot2(change.filePath);
96209
96604
  if (!current.bytes.equals(change.after))
96210
96605
  continue;
96211
- replaceAtomically2(change.filePath, fs51.readFileSync(taskMigrationBackupPathV4(options.backupRoot, change.filePath)), change.mode);
96606
+ replaceAtomically2(change.filePath, fs55.readFileSync(taskMigrationBackupPathV4(options.backupRoot, change.filePath)), change.mode);
96212
96607
  }
96213
96608
  throw cause;
96214
96609
  }
@@ -96220,12 +96615,12 @@ function expandTilde(value) {
96220
96615
  if (value === "~")
96221
96616
  return os5.homedir();
96222
96617
  if (value.startsWith("~/") || value.startsWith("~\\"))
96223
- return path64.join(os5.homedir(), value.slice(2));
96618
+ return path65.join(os5.homedir(), value.slice(2));
96224
96619
  return value;
96225
96620
  }
96226
96621
  function existingDirectory(target) {
96227
96622
  try {
96228
- return fs53.statSync(target).isDirectory();
96623
+ return fs56.statSync(target).isDirectory();
96229
96624
  } catch (cause) {
96230
96625
  if (cause.code === "ENOENT")
96231
96626
  return false;
@@ -96246,21 +96641,21 @@ function taskRoots(config, resolutionBase = process.cwd()) {
96246
96641
  const source = sources.get(bundleId);
96247
96642
  if (!source)
96248
96643
  continue;
96249
- const configuredRoot = source.type === "filesystem" && source.path ? path64.resolve(resolutionBase, expandTilde(source.path)) : lockContentRootFor(bundleId, source.type);
96644
+ const configuredRoot = source.type === "filesystem" && source.path ? path65.resolve(resolutionBase, expandTilde(source.path)) : lockContentRootFor(bundleId, source.type);
96250
96645
  if (!configuredRoot || !existingDirectory(configuredRoot))
96251
96646
  continue;
96252
- const bundleRoot = path64.resolve(configuredRoot);
96647
+ const bundleRoot = path65.resolve(configuredRoot);
96253
96648
  const component = bundleComponentConfig(bundle);
96254
- const componentRoot = path64.resolve(bundleRoot, component?.root ?? ".");
96255
- const relative = path64.relative(bundleRoot, componentRoot);
96256
- if (relative === ".." || relative.startsWith(`..${path64.sep}`) || path64.isAbsolute(relative)) {
96649
+ const componentRoot = path65.resolve(bundleRoot, component?.root ?? ".");
96650
+ const relative = path65.relative(bundleRoot, componentRoot);
96651
+ if (relative === ".." || relative.startsWith(`..${path65.sep}`) || path65.isAbsolute(relative)) {
96257
96652
  throw new ConfigError(`Task migration component root ${componentRoot} escapes bundle ${bundleId} at ${bundleRoot}.`, "INVALID_CONFIG_FILE");
96258
96653
  }
96259
96654
  if (!existingDirectory(componentRoot))
96260
96655
  continue;
96261
96656
  const adapter = component?.adapter ?? detectAdapterId(componentRoot, "");
96262
96657
  if (!component?.adapter && adapter === "") {
96263
- const flatTasks = fs53.readdirSync(componentRoot, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".yml")).map((entry) => entry.name).sort();
96658
+ const flatTasks = fs56.readdirSync(componentRoot, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".yml")).map((entry) => entry.name).sort();
96264
96659
  if (flatTasks.length > 0) {
96265
96660
  throw new ConfigError(`Task migration cannot classify top-level task file(s) ${flatTasks.join(", ")} in bundle ${bundleId}; configure adapter "akm-task" or move them under tasks/.`, "INVALID_CONFIG_FILE");
96266
96661
  }
@@ -96332,8 +96727,8 @@ function applyTaskV3Migration() {
96332
96727
  const before = inspectCurrentTaskPlan();
96333
96728
  if (before.result.taskV3Migration.changed === 0)
96334
96729
  return before.result;
96335
- const backupRoot = path64.join(getDataDir(), "backups", "task-v3");
96336
- const backupPath = path64.join(backupRoot, `${Date.now()}-${randomUUID7()}`);
96730
+ const backupRoot = path65.join(getDataDir(), "backups", "task-v3");
96731
+ const backupPath = path65.join(backupRoot, `${Date.now()}-${randomUUID7()}`);
96337
96732
  const applied = applyTaskToV3MigrationPlan(before.plan, { backupRoot: backupPath });
96338
96733
  const after = inspectCurrentTaskPlan().result;
96339
96734
  if (after.taskV3Migration.changed > 0) {
@@ -96389,8 +96784,8 @@ function applyTaskV4Migration() {
96389
96784
  const before = inspectCurrentTaskV4Plan();
96390
96785
  if (before.result.taskV4Migration.changed === 0)
96391
96786
  return before.result;
96392
- const backupRoot = path64.join(getDataDir(), "backups", "task-v4");
96393
- const backupPath = path64.join(backupRoot, `${Date.now()}-${randomUUID7()}`);
96787
+ const backupRoot = path65.join(getDataDir(), "backups", "task-v4");
96788
+ const backupPath = path65.join(backupRoot, `${Date.now()}-${randomUUID7()}`);
96394
96789
  const applied = applyTaskToV4MigrationPlan(before.plan, { backupRoot: backupPath });
96395
96790
  const after = inspectCurrentTaskV4Plan().result;
96396
96791
  if (after.taskV4Migration.changed > 0) {