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
@@ -5,6 +5,7 @@ import fs from "node:fs";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import { openDatabase } from "../../../storage/database.js";
8
+ import { openSqliteReadSnapshot } from "../../../storage/sqlite-read-snapshot.js";
8
9
  import { extractInlineRefMentions } from "../../session-logs/inline-refs.js";
9
10
  import { AbstractSessionLogProvider } from "../../session-logs/provider-base.js";
10
11
  function getOpenCodeBaseDir() {
@@ -14,21 +15,12 @@ function getOpenCodeBaseDir() {
14
15
  return path.join(os.homedir(), ".local", "share", "opencode");
15
16
  }
16
17
  /**
17
- * Opencode storage layouts:
18
+ * Opencode session storage:
18
19
  *
19
20
  * SQLite (current, observed 2026-06): `<base>/opencode.db` — a Drizzle-managed
20
21
  * database with `session` / `message` / `part` tables. Message text lives in
21
22
  * `part` rows (`data` JSON, `type: "text"`); `message.data` holds role/timing.
22
- * This is the layout current opencode builds write; it is preferred whenever
23
- * `opencode.db` exists.
24
- *
25
- * JSON files (legacy, observed 2026-05): `<base>/storage/session/<projectId>/
26
- * <sessionId>.json` (metadata) + `<base>/storage/message/<sessionId>/
27
- * <messageId>.json` (one per message). Read only when `opencode.db` is absent.
28
- *
29
- * Older builds wrote logs directly into `<base>/log/` and `<base>/*.log`;
30
- * those are still scanned by {@link OpenCodeProvider.readEvents} for
31
- * backward compatibility with the existing failure-pattern aggregator.
23
+ * This is the sole supported layout.
32
24
  */
33
25
  /** Filename of opencode's SQLite session store, relative to its base dir. */
34
26
  const OPENCODE_DB_FILENAME = "opencode.db";
@@ -36,153 +28,22 @@ export class OpenCodeProvider extends AbstractSessionLogProvider {
36
28
  name = "opencode";
37
29
  #baseDir = getOpenCodeBaseDir();
38
30
  availabilityRoot() {
39
- return this.#baseDir;
31
+ return this.#dbPath(this.#baseDir);
40
32
  }
41
33
  /** Absolute path to opencode's SQLite store under `base`. */
42
34
  #dbPath(base) {
43
35
  return path.join(base, OPENCODE_DB_FILENAME);
44
36
  }
45
- *readEvents(input) {
46
- // Legacy behavior: stream raw log lines from the top-level dir and `log/`
47
- // subdirectory. Kept to keep `getExecutionLogCandidates` working without
48
- // a coordinated change to its caller. New code should use
49
- // {@link listSessions} + {@link readSession} instead.
50
- const candidates = [this.#baseDir, path.join(this.#baseDir, "log")];
51
- for (const dir of candidates) {
52
- if (!fs.existsSync(dir))
53
- continue;
54
- try {
55
- for (const file of fs.readdirSync(dir)) {
56
- const full = path.join(dir, file);
57
- const stat = this.statSafe(full);
58
- if (!stat?.isFile())
59
- continue;
60
- if (stat.mtimeMs < input.sinceMs)
61
- continue;
62
- if (!file.endsWith(".json") && !file.endsWith(".jsonl") && !file.endsWith(".log"))
63
- continue;
64
- const content = fs.readFileSync(full, "utf8");
65
- const lines = content.includes("\n") ? content.split("\n") : [content];
66
- yield* this.logLineEvents({
67
- lines,
68
- filePath: full,
69
- fallbackTsMs: stat.mtimeMs,
70
- selectText: (entry) => entry?.content ?? entry?.message ?? entry?.text ?? "",
71
- selectSessionId: (entry) => entry?.sessionId,
72
- });
73
- }
74
- }
75
- catch {
76
- // unreadable dir — skip
77
- }
78
- }
79
- }
80
37
  listSessions(input = {}) {
81
38
  const base = input.location ?? this.#baseDir;
82
39
  const sinceMs = input.sinceMs ?? 0;
83
40
  const dbPath = this.#dbPath(base);
84
- if (fs.existsSync(dbPath))
85
- return this.#listSessionsFromDb(dbPath, sinceMs);
86
- const sessionRoot = path.join(base, "storage", "session");
87
- if (!fs.existsSync(sessionRoot))
41
+ if (!fs.existsSync(dbPath))
88
42
  return [];
89
- return this.listSessionsFromFiles({
90
- sinceMs,
91
- enumerate: () => this.#legacySessionFiles(sessionRoot),
92
- summarize: (filePath, stat) => this.#legacySessionSummary(filePath, stat),
93
- });
94
- }
95
- /** Legacy JSON layout: `<sessionRoot>/<projectId>/<sessionId>.json`. */
96
- *#legacySessionFiles(sessionRoot) {
97
- for (const projectId of fs.readdirSync(sessionRoot)) {
98
- const projectDir = path.join(sessionRoot, projectId);
99
- if (!this.statSafe(projectDir)?.isDirectory())
100
- continue;
101
- for (const file of fs.readdirSync(projectDir)) {
102
- if (!file.endsWith(".json"))
103
- continue;
104
- yield path.join(projectDir, file);
105
- }
106
- }
107
- }
108
- /** Summarize one legacy session metadata file; `undefined` when unreadable. */
109
- #legacySessionSummary(filePath, stat) {
110
- let meta;
111
- try {
112
- meta = JSON.parse(fs.readFileSync(filePath, "utf8"));
113
- }
114
- catch {
115
- return undefined;
116
- }
117
- const time = meta?.time ?? undefined;
118
- return this.sessionRef({
119
- sessionId: typeof meta?.id === "string" ? meta.id : path.basename(filePath, ".json"),
120
- filePath,
121
- startedAt: typeof time?.created === "number" ? time.created : stat.ctimeMs,
122
- endedAt: typeof time?.updated === "number" ? time.updated : stat.mtimeMs,
123
- // Fallback is the project directory name (the legacy layout's projectId).
124
- projectHint: typeof meta?.directory === "string" ? meta.directory : path.basename(path.dirname(filePath)),
125
- title: typeof meta?.title === "string" ? meta.title : undefined,
126
- });
43
+ return this.#listSessionsFromDb(dbPath, sinceMs, input.isolatedSnapshot ?? false);
127
44
  }
128
45
  readSession(ref) {
129
- if (path.basename(ref.filePath) === OPENCODE_DB_FILENAME)
130
- return this.#readSessionFromDb(ref);
131
- let meta = {};
132
- try {
133
- meta = JSON.parse(fs.readFileSync(ref.filePath, "utf8"));
134
- }
135
- catch {
136
- // metadata missing — proceed with empty defaults
137
- }
138
- const time = meta.time ?? undefined;
139
- const startedAt = typeof time?.created === "number" ? time.created : undefined;
140
- const endedAt = typeof time?.updated === "number" ? time.updated : undefined;
141
- const title = typeof meta.title === "string" ? meta.title : undefined;
142
- const projectHint = typeof meta.directory === "string" ? meta.directory : undefined;
143
- const events = [];
144
- const inlineRefs = [];
145
- // Resolve message directory: <baseDir>/storage/message/<sessionId>/
146
- const inferredBase = this.#inferBaseFromSessionPath(ref.filePath) ?? this.#baseDir;
147
- const msgDir = path.join(inferredBase, "storage", "message", ref.sessionId);
148
- if (fs.existsSync(msgDir)) {
149
- try {
150
- const files = fs.readdirSync(msgDir).filter((f) => f.endsWith(".json"));
151
- for (const file of files) {
152
- const full = path.join(msgDir, file);
153
- let msg;
154
- try {
155
- msg = JSON.parse(fs.readFileSync(full, "utf8"));
156
- }
157
- catch {
158
- continue;
159
- }
160
- if (!msg)
161
- continue;
162
- const evt = this.#messageToEvent(msg, ref.sessionId, full);
163
- if (evt) {
164
- events.push(evt);
165
- inlineRefs.push(...extractInlineRefMentions(evt.text, evt.ts));
166
- }
167
- }
168
- }
169
- catch {
170
- // unreadable msg dir — skip
171
- }
172
- }
173
- events.sort((a, b) => (a.ts ?? 0) - (b.ts ?? 0));
174
- return {
175
- ref: this.sessionRef({
176
- sessionId: ref.sessionId,
177
- filePath: ref.filePath,
178
- startedAt,
179
- endedAt,
180
- projectHint,
181
- title,
182
- }),
183
- events,
184
- inlineRefs,
185
- };
46
+ return this.#readSessionFromDb(ref);
186
47
  }
187
48
  /**
188
49
  * List sessions from the SQLite store. `filePath` on each summary is the
@@ -190,10 +51,15 @@ export class OpenCodeProvider extends AbstractSessionLogProvider {
190
51
  * Returns `[]` (never throws) when the DB is unreadable or lacks the expected
191
52
  * schema — callers treat a missing harness as "no sessions".
192
53
  */
193
- #listSessionsFromDb(dbPath, sinceMs) {
54
+ #listSessionsFromDb(dbPath, sinceMs, isolatedSnapshot) {
194
55
  let db;
195
56
  try {
196
- db = openDatabase(dbPath, { readonly: true, create: false });
57
+ const opened = isolatedSnapshot
58
+ ? openSqliteReadSnapshot(dbPath)
59
+ : openDatabase(dbPath, { readonly: true, create: false });
60
+ if (!opened)
61
+ return [];
62
+ db = opened;
197
63
  }
198
64
  catch {
199
65
  return [];
@@ -311,60 +177,4 @@ export class OpenCodeProvider extends AbstractSessionLogProvider {
311
177
  db.close();
312
178
  }
313
179
  }
314
- /**
315
- * Derive opencode base dir from a session metadata file path so a caller
316
- * passing a custom `--location` can still find the message dir.
317
- * Layout: `<base>/storage/session/<projectId>/<id>.json` → base.
318
- */
319
- #inferBaseFromSessionPath(filePath) {
320
- // Walk up: <id>.json → <projectId> → session → storage → <base>
321
- const dir = path.dirname(filePath);
322
- const parts = dir.split(path.sep);
323
- if (parts.length < 3)
324
- return undefined;
325
- const last = parts[parts.length - 1];
326
- const sndLast = parts[parts.length - 2];
327
- const thirdLast = parts[parts.length - 3];
328
- if (sndLast !== "session" || thirdLast !== "storage" || !last)
329
- return undefined;
330
- return parts.slice(0, parts.length - 3).join(path.sep);
331
- }
332
- #messageToEvent(msg, sessionId, filePath) {
333
- const time = msg.time ?? undefined;
334
- const ts = typeof time?.created === "number" ? time.created : typeof msg.timestamp === "number" ? msg.timestamp : 0;
335
- const role = typeof msg.role === "string" ? msg.role : "unknown";
336
- // Opencode message bodies live in summary.title / summary.diffs[].before/after /
337
- // parts (referenced from storage/part/<msg-id>/). For listing+extraction
338
- // purposes the summary block is sufficient — it's what the platform itself
339
- // surfaces as the message preview.
340
- const summary = msg.summary;
341
- const parts = [];
342
- if (typeof summary?.title === "string")
343
- parts.push(summary.title);
344
- if (Array.isArray(summary?.parts)) {
345
- for (const p of summary.parts) {
346
- if (typeof p === "string")
347
- parts.push(p);
348
- else if (p && typeof p === "object") {
349
- const text = p.text;
350
- if (typeof text === "string")
351
- parts.push(text);
352
- }
353
- }
354
- }
355
- // content field for some opencode versions
356
- if (typeof msg.content === "string")
357
- parts.push(msg.content);
358
- const text = parts.join("\n").trim();
359
- if (text.length < 1)
360
- return undefined;
361
- return {
362
- harness: this.name,
363
- text,
364
- ts: ts || undefined,
365
- sessionId,
366
- role,
367
- filePath,
368
- };
369
- }
370
180
  }
@@ -21,6 +21,7 @@
21
21
  * Importing the descriptor from this leaf keeps the registry a config-leaf and
22
22
  * breaks the cycle.
23
23
  */
24
+ import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
24
25
  import { caps } from "../shared.js";
25
26
  import { BaseHarness } from "../types.js";
26
27
  /**
@@ -31,7 +32,6 @@ import { BaseHarness } from "../types.js";
31
32
  export class OpencodeSdkHarness extends BaseHarness {
32
33
  id = "opencode-sdk";
33
34
  displayName = "OpenCode SDK";
34
- aliases = [];
35
35
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
36
36
  // Embedded-SDK dispatch on this machine ⇒ local-runner (the matrix's
37
37
  // "local (sdk/cli)" row, SDK half).
@@ -39,6 +39,17 @@ export class OpencodeSdkHarness extends BaseHarness {
39
39
  // `session.prompt` returns structured SDK events/messages; akm extracts the
40
40
  // final message then validates against the node schema ⇒ native-json tier.
41
41
  structuredOutput = "native-json";
42
+ executionLowerer = {
43
+ platform: "opencode-sdk",
44
+ personaChannel: "native",
45
+ lower: createAgentRequestLowerer({
46
+ adapter: "opencode-sdk",
47
+ personaChannel: "native",
48
+ nativeAgentSelector: true,
49
+ tools: "sdk",
50
+ outputSchema: false,
51
+ }),
52
+ };
42
53
  // No flag-shaped resume: session reuse is programmatic — the SDK session id is
43
54
  // stored opportunistically on the unit row and passed back to
44
55
  // `session.prompt`, not replayed via a CLI flag.
@@ -93,7 +93,6 @@
93
93
  import { spawn } from "node:child_process";
94
94
  import { createHash } from "node:crypto";
95
95
  import { DEFAULT_AGENT_TIMEOUT_MS } from "../../agent/config.js";
96
- import { resolveModel } from "../../agent/model-aliases.js";
97
96
  // Server registry — one server per complete server-material signature. Caller
98
97
  // deadlines race the shared promise independently; they never become startup
99
98
  // configuration inherited by later callers.
@@ -234,22 +233,13 @@ function toolsToSdkAllowlist(tools) {
234
233
  }
235
234
  /**
236
235
  * Assemble the OpenCode SDK server config from the profile + LLM fallback.
237
- * Pure and exported for tests. `profile.model` is resolved through the model
238
- * alias tables (platform key `"opencode-sdk"`) so config aliases like
239
- * `"model": "fast"` work on the SDK path the same way they do for CLI
240
- * builders. Note there is no built-in alias column for `opencode-sdk` —
241
- * built-in opus/sonnet/haiku strings are CLI-provider-qualified and would
242
- * collide with the `akm-custom/` provider prefixing below, so only profile
243
- * and config-root alias tables apply here.
236
+ * Pure and exported for tests. `profile.model` is already exact because model
237
+ * aliases resolve once before harness lowering.
244
238
  */
245
239
  export function buildSdkConfig(profile, llmConfig) {
246
240
  const endpoint = llmConfig?.endpoint;
247
241
  const apiKey = llmConfig?.apiKey;
248
- const profileModel = profile.model
249
- ? profile.modelIsExact
250
- ? profile.model
251
- : resolveModel(profile.model, "opencode-sdk", profile.modelAliases, profile.globalModelAliases)
252
- : undefined;
242
+ const profileModel = profile.model;
253
243
  const model = profileModel ?? llmConfig?.model;
254
244
  const sdkConfig = {};
255
245
  if (model)
@@ -279,25 +269,28 @@ function serverRegistryKey(profile, env) {
279
269
  .update(JSON.stringify(canonicalize(material)))
280
270
  .digest("hex");
281
271
  }
272
+ const OPENCODE_SDK_SERVER_ENV_NAMES = [
273
+ "HOME",
274
+ "PATH",
275
+ "USER",
276
+ "LANG",
277
+ "LC_ALL",
278
+ "TERM",
279
+ "TMPDIR",
280
+ "SYSTEMROOT",
281
+ "COMSPEC",
282
+ "PATHEXT",
283
+ "WINDIR",
284
+ "TEMP",
285
+ "TMP",
286
+ ];
287
+ /** @internal Exact environment allowlist used to start the OpenCode SDK server. */
288
+ export function opencodeSdkServerEnvironmentNames(profile) {
289
+ return [...new Set([...OPENCODE_SDK_SERVER_ENV_NAMES, ...(profile.envPassthrough ?? [])])];
290
+ }
282
291
  function buildServerEnv(profile, config, bindings, envSource) {
283
292
  const env = {};
284
- const inheritedNames = new Set([
285
- "HOME",
286
- "PATH",
287
- "USER",
288
- "LANG",
289
- "LC_ALL",
290
- "TERM",
291
- "TMPDIR",
292
- "SYSTEMROOT",
293
- "COMSPEC",
294
- "PATHEXT",
295
- "WINDIR",
296
- "TEMP",
297
- "TMP",
298
- ...(profile.envPassthrough ?? []),
299
- ]);
300
- for (const key of inheritedNames) {
293
+ for (const key of opencodeSdkServerEnvironmentNames(profile)) {
301
294
  const value = envSource[key];
302
295
  if (value !== undefined)
303
296
  env[key] = value;
@@ -688,6 +681,17 @@ async function deleteSessionBestEffort(client, sessionId, query, setTimeoutFn, c
688
681
  return `OpenCode session cleanup failed: ${errorText(err)}`;
689
682
  }
690
683
  }
684
+ function abortedBeforeSdkStart(profile) {
685
+ return {
686
+ ok: false,
687
+ stdout: "",
688
+ stderr: "",
689
+ durationMs: 0,
690
+ exitCode: null,
691
+ reason: "aborted",
692
+ error: `opencode-sdk agent "${profile.name}" not started: caller signal already aborted`,
693
+ };
694
+ }
691
695
  export async function runOpencodeSdk(profile, prompt, opts = {}, llmConfig) {
692
696
  const start = Date.now();
693
697
  const timeoutMs = opts.timeoutMs !== undefined ? opts.timeoutMs : DEFAULT_AGENT_TIMEOUT_MS;
@@ -695,17 +699,8 @@ export async function runOpencodeSdk(profile, prompt, opts = {}, llmConfig) {
695
699
  const remainingTimeoutMs = () => (deadline === null ? null : Math.max(0, deadline - Date.now()));
696
700
  const setTimeoutImpl = opts.setTimeoutFn ?? setTimeout;
697
701
  const clearTimeoutImpl = opts.clearTimeoutFn ?? clearTimeout;
698
- if (opts.signal?.aborted) {
699
- return {
700
- ok: false,
701
- stdout: "",
702
- stderr: "",
703
- durationMs: 0,
704
- exitCode: null,
705
- reason: "aborted",
706
- error: `opencode-sdk agent "${profile.name}" not started: caller signal already aborted`,
707
- };
708
- }
702
+ if (opts.signal?.aborted)
703
+ return abortedBeforeSdkStart(profile);
709
704
  let client;
710
705
  if (_testServer) {
711
706
  client = _testServer.client;
@@ -831,14 +826,17 @@ export async function runOpencodeSdk(profile, prompt, opts = {}, llmConfig) {
831
826
  error: "Failed to create OpenCode session",
832
827
  };
833
828
  }
834
- // #564 bug fixes (1) + (2): forward systemPrompt and tools from the abstract
829
+ // Forward the exact native agent selector, systemPrompt, and tools from the abstract
835
830
  // dispatch request. Both were previously accepted on AgentDispatchRequest but
836
831
  // silently dropped on the SDK path, so SDK-mode dispatch ignored agent-asset
837
832
  // system prompts and tool policies entirely (the CLI path honours both).
838
833
  const dispatch = opts.dispatch;
834
+ const agent = dispatch?.agent;
839
835
  const system = dispatch?.systemPrompt;
840
836
  const tools = toolsToSdkAllowlist(dispatch?.tools);
841
837
  const body = { parts: [{ type: "text", text: prompt }] };
838
+ if (agent)
839
+ body.agent = agent;
842
840
  if (system)
843
841
  body.system = system;
844
842
  if (tools)
@@ -72,6 +72,7 @@
72
72
  * (`./index.ts`).
73
73
  */
74
74
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
75
+ import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
75
76
  /** Canonical harness/platform id used for model-alias resolution. */
76
77
  export const OPENHANDS_PLATFORM = "openhands";
77
78
  /**
@@ -106,6 +107,13 @@ function buildTaskPayload(req) {
106
107
  */
107
108
  export const openhandsBuilder = {
108
109
  platform: OPENHANDS_PLATFORM,
110
+ personaChannel: "prompt",
111
+ lower: createAgentRequestLowerer({
112
+ adapter: OPENHANDS_PLATFORM,
113
+ personaChannel: "prompt",
114
+ tools: "none",
115
+ outputSchema: true,
116
+ }),
109
117
  build(profile, req) {
110
118
  assertNotFlag(req.systemPrompt, "systemPrompt");
111
119
  assertNotFlag(req.model, "model");
@@ -26,7 +26,6 @@ export { openhandsResultExtractor } from "./result-extractor.js";
26
26
  export class OpenhandsHarness extends BaseHarness {
27
27
  id = "openhands";
28
28
  displayName = "OpenHands";
29
- aliases = [];
30
29
  agentBuilder = openhandsBuilder;
31
30
  resultExtractor = openhandsResultExtractor;
32
31
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
@@ -51,6 +51,7 @@
51
51
  * (`./index.ts`).
52
52
  */
53
53
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
54
+ import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
54
55
  /** Canonical harness/platform id used for model-alias resolution. */
55
56
  export const PI_PLATFORM = "pi";
56
57
  /**
@@ -72,6 +73,13 @@ function buildPromptPayload(req) {
72
73
  */
73
74
  export const piBuilder = {
74
75
  platform: PI_PLATFORM,
76
+ personaChannel: "native",
77
+ lower: createAgentRequestLowerer({
78
+ adapter: PI_PLATFORM,
79
+ personaChannel: "native",
80
+ tools: "none",
81
+ outputSchema: true,
82
+ }),
75
83
  build(profile, req) {
76
84
  assertNotFlag(req.systemPrompt, "systemPrompt");
77
85
  assertNotFlag(req.model, "model");
@@ -26,7 +26,6 @@ export { piResultExtractor } from "./result-extractor.js";
26
26
  export class PiHarness extends BaseHarness {
27
27
  id = "pi";
28
28
  displayName = "Pi";
29
- aliases = [];
30
29
  agentBuilder = piBuilder;
31
30
  resultExtractor = piResultExtractor;
32
31
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
@@ -7,7 +7,6 @@ export function caps(c) {
7
7
  agentDispatch: false,
8
8
  detection: false,
9
9
  configImport: false,
10
- runtimeIdentity: false,
11
10
  ...c,
12
11
  };
13
12
  }
@@ -44,11 +44,9 @@ export function isSessionLogHarness(h) {
44
44
  * (Claude, OpenCode) declare their own required `sessionLogProvider`.
45
45
  */
46
46
  export class BaseHarness {
47
- runtimeId;
48
47
  setupDetectionDir;
49
48
  agentBuilder;
50
- pattern;
51
- structuredOutput;
49
+ executionLowerer;
52
50
  identityEnv;
53
51
  presenceEnv;
54
52
  resultExtractor;