akm-cli 0.9.1 → 0.9.2-alpha.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 (341) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/tasks/core/extract.yml +6 -5
  12. package/dist/assets/tasks/core/improve.yml +6 -5
  13. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  14. package/dist/assets/tasks/core/sync.yml +6 -5
  15. package/dist/assets/tasks/core/version-check.yml +6 -5
  16. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  17. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  21. package/dist/cli/confirm.js +2 -2
  22. package/dist/cli/parse-args.js +3 -24
  23. package/dist/cli/retired-commands.js +1 -1
  24. package/dist/cli/shared.js +2 -2
  25. package/dist/cli.js +11 -9
  26. package/dist/commands/agent/agent-dispatch.js +55 -89
  27. package/dist/commands/agent/contribute-cli.js +12 -45
  28. package/dist/commands/command/builtin-action.js +32 -0
  29. package/dist/commands/command/command-cli.js +99 -0
  30. package/dist/commands/command/command-execution.js +308 -0
  31. package/dist/commands/command/execution-source-loader.js +176 -0
  32. package/dist/commands/command/portable-template.js +60 -0
  33. package/dist/commands/config-cli.js +10 -4
  34. package/dist/commands/env/env.js +4 -2
  35. package/dist/commands/feedback-cli.js +1 -1
  36. package/dist/commands/health/checks.js +241 -29
  37. package/dist/commands/health/html-report.js +0 -14
  38. package/dist/commands/health/report-view-model.js +0 -1
  39. package/dist/commands/health/surfaces.js +6 -7
  40. package/dist/commands/health/types.js +0 -2
  41. package/dist/commands/health.js +63 -18
  42. package/dist/commands/improve/collapse-detector.js +2 -2
  43. package/dist/commands/improve/consolidate.js +251 -214
  44. package/dist/commands/improve/distill/promote-memory.js +71 -34
  45. package/dist/commands/improve/distill/quality-gate.js +17 -5
  46. package/dist/commands/improve/distill.js +232 -155
  47. package/dist/commands/improve/eligibility.js +112 -79
  48. package/dist/commands/improve/execution.js +57 -0
  49. package/dist/commands/improve/extract-cli.js +5 -5
  50. package/dist/commands/improve/extract.js +521 -342
  51. package/dist/commands/improve/improve-strategies.js +43 -14
  52. package/dist/commands/improve/improve.js +249 -29
  53. package/dist/commands/improve/loop-stages.js +11 -17
  54. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  55. package/dist/commands/improve/outcome-loop.js +22 -38
  56. package/dist/commands/improve/planner.js +134 -0
  57. package/dist/commands/improve/preparation.js +730 -409
  58. package/dist/commands/improve/reflect.js +386 -223
  59. package/dist/commands/improve/run-context.js +3 -4
  60. package/dist/commands/improve/salience.js +6 -58
  61. package/dist/commands/improve/session-asset.js +12 -12
  62. package/dist/commands/lint/index.js +101 -29
  63. package/dist/commands/migrate-cli.js +11 -69
  64. package/dist/commands/migration-tool.js +6 -9
  65. package/dist/commands/models-cli.js +27 -0
  66. package/dist/commands/proposal/drain.js +258 -186
  67. package/dist/commands/proposal/proposal-cli.js +32 -10
  68. package/dist/commands/proposal/proposal.js +2 -5
  69. package/dist/commands/proposal/propose.js +192 -172
  70. package/dist/commands/proposal/repository.js +54 -91
  71. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  72. package/dist/commands/read/curate.js +10 -0
  73. package/dist/commands/read/registry-search.js +25 -9
  74. package/dist/commands/read/remember-cli.js +14 -2
  75. package/dist/commands/read/search.js +10 -4
  76. package/dist/commands/read/show.js +139 -153
  77. package/dist/commands/registry-cli.js +16 -7
  78. package/dist/commands/remember.js +33 -18
  79. package/dist/commands/sources/add-cli.js +19 -178
  80. package/dist/commands/sources/bundle-cli.js +15 -3
  81. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  82. package/dist/commands/sources/info.js +2 -1
  83. package/dist/commands/sources/installed-stashes.js +835 -152
  84. package/dist/commands/sources/schema-repair.js +174 -95
  85. package/dist/commands/sources/self-update.js +30 -74
  86. package/dist/commands/sources/source-add.js +3 -5
  87. package/dist/commands/sources/sources-cli.js +2 -15
  88. package/dist/commands/sources/update-transaction.js +220 -0
  89. package/dist/commands/tasks/tasks-cli.js +3 -3
  90. package/dist/commands/tasks/tasks.js +736 -317
  91. package/dist/commands/workflow-cli.js +2 -2
  92. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  93. package/dist/core/adapter/adapters/akm-adapter.js +83 -34
  94. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  95. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  96. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  97. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  98. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  99. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  100. package/dist/core/adapter/adapters/index.js +0 -9
  101. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  102. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  103. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  104. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  105. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  106. package/dist/core/adapter/execution-source.js +308 -0
  107. package/dist/core/adapter/recognize-match.js +36 -13
  108. package/dist/core/adapter/registry.js +0 -9
  109. package/dist/core/asset/stash-meta.js +94 -4
  110. package/dist/core/common.js +6 -11
  111. package/dist/core/config/config-io.js +3 -3
  112. package/dist/core/config/config-schema.js +18 -40
  113. package/dist/core/config/config-sources.js +11 -21
  114. package/dist/core/config/config-walker.js +31 -13
  115. package/dist/core/config/config.js +21 -20
  116. package/dist/core/config/schema/engines.js +8 -7
  117. package/dist/core/config/schema/improve-processes.js +29 -5
  118. package/dist/core/config/schema/primitives.js +1 -23
  119. package/dist/core/config/schema/sources-bundles.js +13 -16
  120. package/dist/core/errors.js +2 -0
  121. package/dist/core/events.js +68 -32
  122. package/dist/core/extra-params.js +1 -0
  123. package/dist/core/improve-result.js +315 -0
  124. package/dist/core/lesson-lint.js +0 -6
  125. package/dist/core/maintenance-barrier.js +4 -4
  126. package/dist/core/network-policy.js +152 -0
  127. package/dist/core/paths.js +1 -1
  128. package/dist/core/recognition-util.js +4 -4
  129. package/dist/core/registry-url.js +456 -0
  130. package/dist/core/state/migrations.js +161 -47
  131. package/dist/core/state-db.js +453 -80
  132. package/dist/core/system-error.js +32 -0
  133. package/dist/core/time.js +2 -12
  134. package/dist/core/write-source.js +0 -18
  135. package/dist/execution/directory-identity.js +52 -0
  136. package/dist/execution/executable-identity.js +107 -0
  137. package/dist/execution/guarded-source.js +398 -0
  138. package/dist/execution/json.js +95 -0
  139. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  140. package/dist/execution/record.js +55 -0
  141. package/dist/execution/resolved-request.js +730 -0
  142. package/dist/execution/source.js +320 -0
  143. package/dist/indexer/bundle-identity-guard.js +5 -4
  144. package/dist/indexer/db/graph-db.js +33 -0
  145. package/dist/indexer/graph/graph-boost.js +3 -4
  146. package/dist/indexer/graph/graph-extraction.js +562 -373
  147. package/dist/indexer/index-written-assets.js +65 -34
  148. package/dist/indexer/indexer.js +443 -222
  149. package/dist/indexer/installations.js +6 -0
  150. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  151. package/dist/indexer/passes/memory-inference.js +227 -174
  152. package/dist/indexer/scan/doc-to-entry.js +7 -9
  153. package/dist/indexer/scan/drain-dir.js +51 -23
  154. package/dist/indexer/search/db-search.js +98 -22
  155. package/dist/indexer/search/search-fields.js +1 -1
  156. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  157. package/dist/indexer/search/search-source.js +7 -16
  158. package/dist/indexer/search/semantic-status.js +10 -1
  159. package/dist/indexer/usage/show-usage.js +105 -0
  160. package/dist/indexer/usage/usage-events.js +7 -2
  161. package/dist/indexer/walk/matchers.js +40 -10
  162. package/dist/indexer/walk/path-resolver.js +5 -2
  163. package/dist/indexer/walk/walker.js +20 -2
  164. package/dist/integrations/agent/builder-shared.js +3 -6
  165. package/dist/integrations/agent/conversation-fallback.js +16 -0
  166. package/dist/integrations/agent/engine-resolution.js +87 -87
  167. package/dist/integrations/agent/execution-cascade.js +566 -0
  168. package/dist/integrations/agent/execution-definitions.js +211 -0
  169. package/dist/integrations/agent/execution-lowering.js +811 -0
  170. package/dist/integrations/agent/execution-preparation.js +67 -0
  171. package/dist/integrations/agent/index.js +0 -2
  172. package/dist/integrations/agent/inline-execution.js +74 -0
  173. package/dist/integrations/agent/model-map.js +515 -0
  174. package/dist/integrations/agent/persona-fallback.js +30 -0
  175. package/dist/integrations/agent/request-lowering.js +186 -0
  176. package/dist/integrations/agent/runner-dispatch.js +230 -37
  177. package/dist/integrations/agent/runner.js +12 -83
  178. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  179. package/dist/integrations/harnesses/aider/index.js +0 -1
  180. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  181. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  182. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  183. package/dist/integrations/harnesses/claude/index.js +1 -5
  184. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  185. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  186. package/dist/integrations/harnesses/codex/index.js +0 -1
  187. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/copilot/index.js +0 -1
  189. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/gemini/index.js +0 -1
  191. package/dist/integrations/harnesses/index.js +4 -44
  192. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  193. package/dist/integrations/harnesses/opencode/index.js +0 -2
  194. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  195. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  196. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  197. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  198. package/dist/integrations/harnesses/openhands/index.js +0 -1
  199. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  200. package/dist/integrations/harnesses/pi/index.js +0 -1
  201. package/dist/integrations/harnesses/shared.js +0 -1
  202. package/dist/integrations/harnesses/types.js +1 -3
  203. package/dist/integrations/lockfile.js +82 -79
  204. package/dist/integrations/session-logs/index.js +6 -17
  205. package/dist/integrations/session-logs/provider-base.js +1 -29
  206. package/dist/llm/client.js +10 -5
  207. package/dist/llm/embedder.js +6 -7
  208. package/dist/llm/embedders/local.js +37 -88
  209. package/dist/llm/embedders/types.js +1 -1
  210. package/dist/llm/graph-extract.js +75 -50
  211. package/dist/llm/index-passes.js +43 -5
  212. package/dist/llm/memory-infer.js +8 -6
  213. package/dist/llm/metadata-enhance.js +5 -3
  214. package/dist/llm/structured-call.js +122 -25
  215. package/dist/output/format-exempt.js +1 -1
  216. package/dist/output/render-registry.js +0 -16
  217. package/dist/output/renderers.js +12 -7
  218. package/dist/output/shapes/curate.js +1 -0
  219. package/dist/output/shapes/helpers.js +10 -2
  220. package/dist/output/shapes/passthrough.js +2 -0
  221. package/dist/output/text/command-format.js +28 -32
  222. package/dist/output/text/health-format.js +1 -29
  223. package/dist/output/text/migrate.js +6 -56
  224. package/dist/output/text/proposal-format.js +16 -1
  225. package/dist/output/text/workflow-format.js +16 -0
  226. package/dist/registry/network.js +279 -0
  227. package/dist/registry/pinned-request-helper.js +247 -0
  228. package/dist/registry/pinned-transport.js +717 -0
  229. package/dist/registry/providers/skills-sh.js +18 -6
  230. package/dist/registry/providers/static-index.js +20 -7
  231. package/dist/registry/resolve.js +53 -28
  232. package/dist/scripts/akm-migrate-node.js +19122 -52218
  233. package/dist/scripts/akm-migrate.js +19064 -51567
  234. package/dist/setup/registry-stash-loader.js +64 -20
  235. package/dist/setup/semantic-assets.js +9 -34
  236. package/dist/setup/setup.js +12 -30
  237. package/dist/setup/source-identity.js +17 -0
  238. package/dist/setup/steps/sources.js +36 -15
  239. package/dist/setup/steps/tasks.js +39 -11
  240. package/dist/sources/providers/git-provider.js +3 -3
  241. package/dist/sources/providers/npm.js +2 -2
  242. package/dist/sources/providers/provider-utils.js +4 -3
  243. package/dist/sources/providers/website.js +11 -7
  244. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  245. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  246. package/dist/sources/website-url.js +73 -0
  247. package/dist/storage/engines/sqlite-migrations.js +81 -26
  248. package/dist/storage/managed-db.js +27 -24
  249. package/dist/storage/repositories/events-repository.js +3 -0
  250. package/dist/storage/repositories/index-connection.js +42 -10
  251. package/dist/storage/repositories/index-entries-repository.js +167 -207
  252. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  253. package/dist/storage/repositories/index-entry-schema.js +255 -0
  254. package/dist/storage/repositories/index-fts-repository.js +10 -10
  255. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  256. package/dist/storage/repositories/index-meta-repository.js +0 -11
  257. package/dist/storage/repositories/index-schema.js +71 -342
  258. package/dist/storage/repositories/index-utility-repository.js +12 -17
  259. package/dist/storage/repositories/index-vec-repository.js +1 -1
  260. package/dist/storage/repositories/proposals-repository.js +4 -127
  261. package/dist/storage/repositories/registry-cache.js +2 -1
  262. package/dist/storage/repositories/task-history-repository.js +20 -40
  263. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  264. package/dist/storage/sqlite-read-snapshot.js +148 -0
  265. package/dist/tasks/backends/cron.js +170 -42
  266. package/dist/tasks/backends/index.js +1 -1
  267. package/dist/tasks/backends/launchd.js +787 -202
  268. package/dist/tasks/backends/schtasks.js +282 -83
  269. package/dist/tasks/embedded.js +7 -7
  270. package/dist/tasks/frozen-script.js +50 -0
  271. package/dist/tasks/resolve-akm-bin.js +5 -1
  272. package/dist/tasks/runner.js +239 -251
  273. package/dist/tasks/runtime-v3.js +281 -0
  274. package/dist/tasks/scheduler-binding.js +272 -0
  275. package/dist/tasks/scheduler-invocation.js +57 -43
  276. package/dist/tasks/scheduler-sync.js +654 -0
  277. package/dist/tasks/source-v3.js +752 -0
  278. package/dist/tasks/standalone-script-entry.js +5 -0
  279. package/dist/tasks/task-id.js +29 -0
  280. package/dist/workflows/authoring/authoring.js +15 -32
  281. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  282. package/dist/workflows/exec/exec-unit.js +7 -28
  283. package/dist/workflows/exec/frozen-judge.js +57 -89
  284. package/dist/workflows/exec/lowering-notices.js +23 -0
  285. package/dist/workflows/exec/native-executor.js +301 -458
  286. package/dist/workflows/exec/param-secrets.js +4 -3
  287. package/dist/workflows/exec/run-workflow.js +26 -32
  288. package/dist/workflows/exec/step-work.js +105 -109
  289. package/dist/workflows/exec/unit-dispatch.js +103 -27
  290. package/dist/workflows/exec/unit-writer.js +3 -3
  291. package/dist/workflows/exec/worktree.js +2 -2
  292. package/dist/workflows/ir/compile.js +86 -72
  293. package/dist/workflows/ir/environment-v4.js +328 -0
  294. package/dist/workflows/ir/freeze-v4.js +122 -0
  295. package/dist/workflows/ir/plan-hash.js +13 -7
  296. package/dist/workflows/ir/schema-v4.js +525 -0
  297. package/dist/workflows/ir/schema.js +25 -284
  298. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  299. package/dist/workflows/parser.js +27 -24
  300. package/dist/workflows/program/schema.js +1 -2
  301. package/dist/workflows/renderer.js +42 -29
  302. package/dist/workflows/resource-limits.js +4 -5
  303. package/dist/workflows/runtime/agent-identity.js +11 -13
  304. package/dist/workflows/runtime/plan-classifier.js +8 -8
  305. package/dist/workflows/runtime/runs.js +27 -43
  306. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  307. package/dist/workflows/source-files.js +373 -0
  308. package/dist/workflows/source-ir/compile.js +196 -0
  309. package/dist/workflows/source-ir/github-yaml.js +577 -0
  310. package/dist/workflows/source-ir/ordering.js +38 -0
  311. package/dist/workflows/source-ir/program.js +50 -0
  312. package/dist/workflows/source-ir/result.js +26 -0
  313. package/dist/workflows/source-ir/schema.js +772 -0
  314. package/dist/workflows/source-ir/semantics.js +242 -0
  315. package/dist/workflows/source-ir/uses.js +14 -0
  316. package/docs/README.md +2 -0
  317. package/docs/migration/README.md +3 -1
  318. package/docs/migration/release-notes/0.9.2.md +55 -0
  319. package/docs/migration/release-notes/README.md +5 -0
  320. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  321. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  322. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  323. package/docs/reference/README.md +1 -0
  324. package/docs/reference/cli.md +230 -98
  325. package/docs/reference/configuration.md +153 -27
  326. package/docs/reference/data-and-telemetry.md +19 -1
  327. package/docs/reference/supported-formats.md +23 -3
  328. package/docs/reference/tasks.md +182 -0
  329. package/docs/reference/workflow-schema.md +91 -40
  330. package/docs/reference/workflows.md +33 -6
  331. package/package.json +10 -6
  332. package/schemas/akm-config.json +372 -216
  333. package/schemas/akm-task.json +324 -80
  334. package/schemas/akm-workflow.json +6 -9
  335. package/dist/core/migration-operation.js +0 -75
  336. package/dist/integrations/agent/model-aliases.js +0 -74
  337. package/dist/tasks/parser.js +0 -380
  338. package/dist/tasks/schema.js +0 -123
  339. package/dist/tasks/validator.js +0 -80
  340. package/dist/workflows/ir/freeze.js +0 -320
  341. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -0,0 +1,320 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { bundleRefToString, isBundleSlug, parseBundleRef } from "../core/asset/asset-ref.js";
5
+ import { cloneExecutionJson, cloneExecutionJsonObject, } from "./json.js";
6
+ import { assertSnapshotKeys, snapshotStrictRecord } from "./record.js";
7
+ export const EXECUTION_SOURCE_SCHEMA_VERSION = 1;
8
+ /** Current internal adapter identifiers are lowercase kebab-case registry keys. */
9
+ export const EXECUTION_ADAPTER_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
10
+ const CONTROL_OR_LINE_SEPARATOR_PATTERN = /[\p{Cc}\p{Zl}\p{Zp}]/u;
11
+ const FORMAT_CHARACTER_PATTERN = /\p{Cf}/u;
12
+ const WINDOWS_DRIVE_PREFIX_PATTERN = /^[A-Za-z]:/;
13
+ const RESERVED_EXTENSION_OWNERS = new Set(["__proto__", "constructor", "prototype", "tostring"]);
14
+ const renderedSourceBrand = Symbol("akm.adapter-rendered-execution-source");
15
+ const renderedSourceInstances = new WeakSet();
16
+ function requireRecord(value, path) {
17
+ return snapshotStrictRecord(value, path);
18
+ }
19
+ function assertOnlyKeys(value, allowed, path) {
20
+ assertSnapshotKeys(value, allowed, path);
21
+ }
22
+ function validateExtensionOwner(owner) {
23
+ const normalized = owner.toLowerCase();
24
+ if (!EXECUTION_ADAPTER_ID_PATTERN.test(owner) || RESERVED_EXTENSION_OWNERS.has(normalized)) {
25
+ throw new TypeError(`extension owner must be a canonical, non-reserved adapter identifier: ${owner}`);
26
+ }
27
+ }
28
+ function frozenNullPrototypeMap(entries) {
29
+ const out = Object.create(null);
30
+ for (const [key, value] of entries) {
31
+ Object.defineProperty(out, key, { value, enumerable: true, configurable: false, writable: false });
32
+ }
33
+ return Object.freeze(out);
34
+ }
35
+ function cloneExtensionEntry(value, index) {
36
+ const path = `extension entry ${index}`;
37
+ const cloned = cloneExecutionJson(value, path);
38
+ if (!Array.isArray(cloned) || cloned.length !== 2) {
39
+ throw new TypeError(`${path} must be a two-element [owner, values] array`);
40
+ }
41
+ const [owner, values] = cloned;
42
+ if (typeof owner !== "string")
43
+ throw new TypeError(`${path} owner must be a string`);
44
+ if (values === null || Array.isArray(values) || typeof values !== "object") {
45
+ throw new TypeError(`${path} values must be a JSON object`);
46
+ }
47
+ return [owner, values];
48
+ }
49
+ export function createAdapterExtensions(first, second, ...rest) {
50
+ const rawEntries = typeof first === "string"
51
+ ? [[first, second]]
52
+ : [first, ...(second === undefined ? [] : [second]), ...rest];
53
+ const seen = new Set();
54
+ const cloned = [];
55
+ for (const [index, rawEntry] of rawEntries.entries()) {
56
+ const [owner, values] = cloneExtensionEntry(rawEntry, index);
57
+ validateExtensionOwner(owner);
58
+ if (seen.has(owner))
59
+ throw new TypeError(`duplicate extension owner: ${owner}`);
60
+ seen.add(owner);
61
+ cloned.push([owner, cloneExecutionJsonObject(values, `extensions.${owner}`)]);
62
+ }
63
+ return frozenNullPrototypeMap(cloned);
64
+ }
65
+ export function cloneAdapterExtensions(value, path) {
66
+ const record = requireRecord(value, path);
67
+ const entries = Object.entries(record).map(([owner, fields]) => [owner, cloneExecutionJsonObject(fields, `${path}.${owner}`)]);
68
+ const [first, ...rest] = entries;
69
+ return first ? createAdapterExtensions(first, ...rest) : frozenNullPrototypeMap([]);
70
+ }
71
+ function requireCanonicalString(value, path) {
72
+ if (typeof value !== "string" ||
73
+ value.length === 0 ||
74
+ !isWellFormedUnicode(value) ||
75
+ value.normalize("NFC") !== value) {
76
+ throw new TypeError(`${path} must be a non-empty NFC string`);
77
+ }
78
+ if (hasUnsafeIdentityCharacter(value)) {
79
+ throw new TypeError(`${path} must not contain Unicode control or dangerous format characters`);
80
+ }
81
+ return value;
82
+ }
83
+ /**
84
+ * Validate a selector/layer identifier that may cross the durable execution
85
+ * boundary. Keep this shared with the cascade planner so resume cannot admit a
86
+ * selector the live planner would reject.
87
+ */
88
+ export function requireStableExecutionSelector(value, path) {
89
+ let selector;
90
+ try {
91
+ selector = requireCanonicalString(value, path);
92
+ }
93
+ catch (cause) {
94
+ throw new TypeError(`${path} must be a non-empty stable NFC identifier`, { cause });
95
+ }
96
+ if (selector.length > 512 || selector.trim() !== selector) {
97
+ throw new TypeError(`${path} must be a non-empty stable NFC identifier`);
98
+ }
99
+ return selector;
100
+ }
101
+ export function isPortableExecutionAgentSelector(value) {
102
+ return /^(?:[^/]+\/\/)?agents\/[^/]/.test(value);
103
+ }
104
+ export function executionPersonaMatchesSelector(selector, personaRef) {
105
+ return selector.includes("//") ? selector === personaRef : personaRef.endsWith(`//${selector}`);
106
+ }
107
+ function hasUnsafeIdentityCharacter(value) {
108
+ for (const character of value) {
109
+ if (CONTROL_OR_LINE_SEPARATOR_PATTERN.test(character))
110
+ return true;
111
+ // Join controls are meaningful in emoji and several writing systems. All
112
+ // other format controls are rejected because they can conceal identity.
113
+ if (FORMAT_CHARACTER_PATTERN.test(character) && character !== "\u200C" && character !== "\u200D")
114
+ return true;
115
+ }
116
+ return false;
117
+ }
118
+ function isWellFormedUnicode(value) {
119
+ for (let index = 0; index < value.length; index += 1) {
120
+ const code = value.charCodeAt(index);
121
+ if (code >= 0xd800 && code <= 0xdbff) {
122
+ const next = value.charCodeAt(index + 1);
123
+ if (!(next >= 0xdc00 && next <= 0xdfff))
124
+ return false;
125
+ index += 1;
126
+ }
127
+ else if (code >= 0xdc00 && code <= 0xdfff) {
128
+ return false;
129
+ }
130
+ }
131
+ return true;
132
+ }
133
+ function validateCanonicalIdentity(input, path) {
134
+ assertOnlyKeys(input, ["ref", "bundle", "adapter", "file", "hash"], path);
135
+ const ref = requireCanonicalString(input.ref, `${path}.ref`);
136
+ const bundle = requireCanonicalString(input.bundle, `${path}.bundle`);
137
+ const adapter = requireCanonicalString(input.adapter, `${path}.adapter`);
138
+ const file = requireCanonicalString(input.file, `${path}.file`);
139
+ const hash = requireCanonicalString(input.hash, `${path}.hash`);
140
+ let parsed;
141
+ try {
142
+ parsed = parseBundleRef(ref);
143
+ }
144
+ catch (cause) {
145
+ throw new TypeError(`${path}.ref is not a canonical bundle ref`, { cause });
146
+ }
147
+ if (parsed.bundle === undefined ||
148
+ parsed.bundle !== bundle ||
149
+ parsed.fragment !== undefined ||
150
+ !isBundleSlug(bundle) ||
151
+ bundleRefToString(parsed) !== ref) {
152
+ throw new TypeError(`${path}.ref must round-trip as the same fully-qualified bundle ref without a fragment`);
153
+ }
154
+ if (!EXECUTION_ADAPTER_ID_PATTERN.test(adapter)) {
155
+ throw new TypeError(`${path}.adapter must use the current lowercase kebab-case adapter identifier grammar`);
156
+ }
157
+ const segments = file.split("/");
158
+ if (file.startsWith("/") ||
159
+ WINDOWS_DRIVE_PREFIX_PATTERN.test(file) ||
160
+ file.includes("\\") ||
161
+ segments.some((segment) => !segment || segment === "." || segment === "..")) {
162
+ throw new TypeError(`${path}.file must be a normalized relative POSIX path`);
163
+ }
164
+ if (!/^[a-f0-9]{64}$/.test(hash))
165
+ throw new TypeError(`${path}.hash must be a SHA-256 hex digest`);
166
+ return { ref, bundle, adapter, file, hash };
167
+ }
168
+ /** Validate, clone, and freeze an identity produced by an adapter renderer. */
169
+ export function createExecutionSourceIdentity(input) {
170
+ return Object.freeze(validateCanonicalIdentity(requireRecord(input, "execution source identity"), "execution source identity"));
171
+ }
172
+ /** Validate and freeze a source identity read back from a frozen request. */
173
+ export function decodeExecutionSourceIdentity(value, path = "execution source identity") {
174
+ return Object.freeze(validateCanonicalIdentity(requireRecord(value, path), path));
175
+ }
176
+ export function cloneUnresolvedExecutionDefaults(input, path = "execution source defaults") {
177
+ const record = requireRecord(input, path);
178
+ assertOnlyKeys(record, [
179
+ "agent",
180
+ "engine",
181
+ "model",
182
+ "inference",
183
+ "outputSchema",
184
+ "tools",
185
+ "timeout",
186
+ "workspace",
187
+ "environment",
188
+ "runtime",
189
+ ], path);
190
+ const json = { ...cloneExecutionJsonObject(record, path) };
191
+ for (const key of ["agent", "engine", "model", "workspace"]) {
192
+ const value = json[key];
193
+ if (value !== undefined && value !== null && typeof value !== "string") {
194
+ throw new TypeError(`${path}.${key} must be a string or null`);
195
+ }
196
+ }
197
+ const timeout = json.timeout;
198
+ if (timeout !== undefined && timeout !== null && typeof timeout !== "string" && typeof timeout !== "number") {
199
+ throw new TypeError(`${path}.timeout must be a string, number, or null`);
200
+ }
201
+ for (const key of ["inference", "outputSchema", "runtime"]) {
202
+ const value = json[key];
203
+ if (value !== undefined && value !== null && (Array.isArray(value) || typeof value !== "object")) {
204
+ throw new TypeError(`${path}.${key} must be an object or null`);
205
+ }
206
+ }
207
+ const environment = json.environment;
208
+ if (environment !== undefined && environment !== null) {
209
+ if (Array.isArray(environment) || typeof environment !== "object") {
210
+ throw new TypeError(`${path}.environment must be an object or null`);
211
+ }
212
+ if (Object.values(environment).some((value) => typeof value !== "string")) {
213
+ throw new TypeError(`${path}.environment values must be strings`);
214
+ }
215
+ }
216
+ if (Object.hasOwn(json, "tools"))
217
+ json.tools = cloneToolSelection(json.tools, `${path}.tools`);
218
+ return Object.freeze(json);
219
+ }
220
+ export function cloneToolSelection(value, path = "tools") {
221
+ if (value === null || typeof value === "string")
222
+ return value;
223
+ const cloned = cloneExecutionJson(value, path);
224
+ if (Array.isArray(cloned)) {
225
+ if (cloned.some((tool) => typeof tool !== "string"))
226
+ throw new TypeError(`${path} array values must be strings`);
227
+ return cloned;
228
+ }
229
+ return cloneExecutionJsonObject(cloned, path);
230
+ }
231
+ export function createAdapterRenderedExecutionSource(input) {
232
+ const record = requireRecord(input, "adapter-rendered execution source");
233
+ assertOnlyKeys(record, ["kind", "content", "identity", "defaults", "extensions"], "adapter-rendered execution source");
234
+ const kind = record.kind;
235
+ const content = record.content;
236
+ if (kind !== "command" && kind !== "persona")
237
+ throw new TypeError("execution source kind is invalid");
238
+ if (typeof content !== "string")
239
+ throw new TypeError("execution source content must be a string");
240
+ const defaults = Object.hasOwn(record, "defaults") ? record.defaults : {};
241
+ if (defaults === undefined) {
242
+ throw new TypeError("execution source defaults must be omitted or be an object");
243
+ }
244
+ const source = {
245
+ schemaVersion: EXECUTION_SOURCE_SCHEMA_VERSION,
246
+ kind,
247
+ content,
248
+ defaults: cloneUnresolvedExecutionDefaults(defaults),
249
+ identity: createExecutionSourceIdentity(record.identity),
250
+ };
251
+ if (Object.hasOwn(record, "extensions")) {
252
+ source.extensions = cloneAdapterExtensions(record.extensions, "execution source extensions");
253
+ }
254
+ Object.defineProperty(source, renderedSourceBrand, { value: true, enumerable: false });
255
+ const frozen = Object.freeze(source);
256
+ renderedSourceInstances.add(frozen);
257
+ return frozen;
258
+ }
259
+ function isAdapterRenderedSource(value, kind) {
260
+ if (typeof value !== "object" || value === null)
261
+ return false;
262
+ if (!renderedSourceInstances.has(value))
263
+ return false;
264
+ if (Object.getPrototypeOf(value) !== Object.prototype || !Object.isFrozen(value))
265
+ return false;
266
+ if (!Object.hasOwn(value, renderedSourceBrand))
267
+ return false;
268
+ const required = new Set(["schemaVersion", "kind", "content", "defaults", "identity"]);
269
+ const optional = new Set(["extensions"]);
270
+ for (const key of Reflect.ownKeys(value)) {
271
+ if (key === renderedSourceBrand)
272
+ continue;
273
+ if (typeof key !== "string" || (!required.delete(key) && !optional.has(key)))
274
+ return false;
275
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
276
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable)
277
+ return false;
278
+ }
279
+ if (required.size > 0)
280
+ return false;
281
+ const brand = Object.getOwnPropertyDescriptor(value, renderedSourceBrand);
282
+ const source = value;
283
+ if (brand?.value !== true ||
284
+ brand.enumerable !== false ||
285
+ brand.configurable !== false ||
286
+ brand.writable !== false ||
287
+ source.schemaVersion !== EXECUTION_SOURCE_SCHEMA_VERSION ||
288
+ source.kind !== kind ||
289
+ typeof source.content !== "string" ||
290
+ typeof source.defaults !== "object" ||
291
+ source.defaults === null ||
292
+ !Object.isFrozen(source.defaults) ||
293
+ typeof source.identity !== "object" ||
294
+ source.identity === null ||
295
+ Object.getPrototypeOf(source.identity) !== Object.prototype ||
296
+ !Object.isFrozen(source.identity)) {
297
+ return false;
298
+ }
299
+ if (Object.hasOwn(source, "extensions")) {
300
+ if (typeof source.extensions !== "object" ||
301
+ source.extensions === null ||
302
+ Object.getPrototypeOf(source.extensions) !== null ||
303
+ !Object.isFrozen(source.extensions)) {
304
+ return false;
305
+ }
306
+ }
307
+ try {
308
+ decodeExecutionSourceIdentity(source.identity);
309
+ }
310
+ catch {
311
+ return false;
312
+ }
313
+ return true;
314
+ }
315
+ export function isAdapterRenderedCommandSource(value) {
316
+ return isAdapterRenderedSource(value, "command");
317
+ }
318
+ export function isAdapterRenderedPersonaSource(value) {
319
+ return isAdapterRenderedSource(value, "persona");
320
+ }
@@ -31,7 +31,7 @@ import fs from "node:fs";
31
31
  import { loadConfig } from "../core/config/config.js";
32
32
  import { getDbPath } from "../core/paths.js";
33
33
  import { warn } from "../core/warn.js";
34
- import { openDatabase } from "../storage/database.js";
34
+ import { closeDatabase, openReadonlyExistingDatabase } from "../storage/repositories/index-connection.js";
35
35
  let guardSettled = false;
36
36
  /** TEST-ONLY: re-arm the once-per-process guard between cases. */
37
37
  export function resetBundleIdentityGuardForTests() {
@@ -43,8 +43,8 @@ function indexBundlePrefixes(dbPath) {
43
43
  return undefined;
44
44
  let db;
45
45
  try {
46
- db = openDatabase(dbPath, { readonly: true });
47
- if (!db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='entries'").get())
46
+ db = openReadonlyExistingDatabase(dbPath);
47
+ if (!db)
48
48
  return undefined;
49
49
  return db
50
50
  .prepare("SELECT DISTINCT bundle_id AS b FROM entries WHERE bundle_id IS NOT NULL AND bundle_id != ''")
@@ -54,7 +54,8 @@ function indexBundlePrefixes(dbPath) {
54
54
  return undefined;
55
55
  }
56
56
  finally {
57
- db?.close();
57
+ if (db)
58
+ closeDatabase(db);
58
59
  }
59
60
  }
60
61
  /**
@@ -219,6 +219,39 @@ export function drainExtractionQueue(db, stashRoot, limit) {
219
219
  return [];
220
220
  }
221
221
  }
222
+ /** Read queued graph work without claiming or deleting it. */
223
+ export function peekExtractionQueue(db, stashRoot, limit) {
224
+ try {
225
+ const rows = db
226
+ .prepare(`SELECT file_path, body_hash, priority
227
+ FROM graph_extraction_queue
228
+ WHERE stash_root = ?
229
+ ORDER BY priority DESC, queued_at ASC
230
+ LIMIT ?`)
231
+ .all(stashRoot, limit);
232
+ return rows.map((row) => ({ filePath: row.file_path, bodyHash: row.body_hash, priority: row.priority }));
233
+ }
234
+ catch (err) {
235
+ rethrowIfTestIsolationError(err);
236
+ return [];
237
+ }
238
+ }
239
+ /**
240
+ * Acknowledge the exact queued revision that was classified and handled.
241
+ * A concurrent re-enqueue with a new body hash therefore survives.
242
+ */
243
+ export function acknowledgeExtractionQueueEntry(db, stashRoot, filePath, bodyHash) {
244
+ try {
245
+ const result = db
246
+ .prepare("DELETE FROM graph_extraction_queue WHERE stash_root = ? AND file_path = ? AND body_hash = ?")
247
+ .run(stashRoot, filePath, bodyHash);
248
+ return result.changes > 0;
249
+ }
250
+ catch (err) {
251
+ rethrowIfTestIsolationError(err);
252
+ return false;
253
+ }
254
+ }
222
255
  /**
223
256
  * Scoped loader — graph_files rows without entities/relations. Used for
224
257
  * orphan detection and entity overview commands.
@@ -250,7 +250,7 @@ export function collectGraphRelatedHit(context, filePath) {
250
250
  * idx_graph_files_path guarantees one graph_files row per path).
251
251
  *
252
252
  * The returned `ref` field carries the canonical indexed `concept_id`, never a
253
- * value re-derived from presentation fields or `entry_key`. It is undefined
253
+ * value re-derived from presentation fields. It is undefined
254
254
  * when the graph row has no matching entry with current indexed provenance.
255
255
  */
256
256
  export function listRelatedPathsForFile(stashRoot, filePath, limit = 5, db) {
@@ -348,9 +348,8 @@ export function listRelatedPathsForFile(stashRoot, filePath, limit = 5, db) {
348
348
  try {
349
349
  const entryRows = db
350
350
  .prepare(`SELECT file_path, concept_id FROM entries
351
- WHERE file_path IN (${placeholders}) AND stash_dir = ?
352
- AND item_ref IS NOT NULL AND concept_id IS NOT NULL`)
353
- .all(...candidatePaths, stashRoot);
351
+ WHERE file_path IN (${placeholders})`)
352
+ .all(...candidatePaths);
354
353
  for (const row of entryRows) {
355
354
  refByPath.set(row.file_path, row.concept_id);
356
355
  }