ai-runtime-engine 1.1.0

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 (269) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/LICENSE +15 -0
  3. package/README.md +489 -0
  4. package/dist/artifacts/artifacts.d.ts +28 -0
  5. package/dist/artifacts/artifacts.js +46 -0
  6. package/dist/benchmark/benchmark.d.ts +23 -0
  7. package/dist/benchmark/benchmark.js +40 -0
  8. package/dist/cli/cli.d.ts +6 -0
  9. package/dist/cli/cli.js +161 -0
  10. package/dist/cli/commands/cleanup.d.ts +47 -0
  11. package/dist/cli/commands/cleanup.js +96 -0
  12. package/dist/cli/commands/config.d.ts +8 -0
  13. package/dist/cli/commands/config.js +28 -0
  14. package/dist/cli/commands/doctor.d.ts +57 -0
  15. package/dist/cli/commands/doctor.js +86 -0
  16. package/dist/cli/commands/executions.d.ts +9 -0
  17. package/dist/cli/commands/executions.js +25 -0
  18. package/dist/cli/commands/info.d.ts +43 -0
  19. package/dist/cli/commands/info.js +53 -0
  20. package/dist/cli/commands/init.d.ts +5 -0
  21. package/dist/cli/commands/init.js +75 -0
  22. package/dist/cli/commands/inspect.d.ts +16 -0
  23. package/dist/cli/commands/inspect.js +60 -0
  24. package/dist/cli/commands/phase2.d.ts +22 -0
  25. package/dist/cli/commands/phase2.js +83 -0
  26. package/dist/cli/commands/route.d.ts +14 -0
  27. package/dist/cli/commands/route.js +49 -0
  28. package/dist/cli/commands/run.d.ts +11 -0
  29. package/dist/cli/commands/run.js +37 -0
  30. package/dist/cli/commands/setup.d.ts +34 -0
  31. package/dist/cli/commands/setup.js +104 -0
  32. package/dist/cli/commands/skills.d.ts +28 -0
  33. package/dist/cli/commands/skills.js +48 -0
  34. package/dist/cli/commands/test.d.ts +7 -0
  35. package/dist/cli/commands/test.js +29 -0
  36. package/dist/cli/context.d.ts +12 -0
  37. package/dist/cli/context.js +16 -0
  38. package/dist/cli/interactive/repl.d.ts +6 -0
  39. package/dist/cli/interactive/repl.js +45 -0
  40. package/dist/cli/interactive/session.d.ts +36 -0
  41. package/dist/cli/interactive/session.js +356 -0
  42. package/dist/cli/prompt.d.ts +6 -0
  43. package/dist/cli/prompt.js +18 -0
  44. package/dist/cli/render.d.ts +7 -0
  45. package/dist/cli/render.js +14 -0
  46. package/dist/comparison/analysis.d.ts +46 -0
  47. package/dist/comparison/analysis.js +177 -0
  48. package/dist/comparison/comparator.d.ts +46 -0
  49. package/dist/comparison/comparator.js +270 -0
  50. package/dist/comparison/comparison.d.ts +140 -0
  51. package/dist/comparison/comparison.js +9 -0
  52. package/dist/comparison/render.d.ts +7 -0
  53. package/dist/comparison/render.js +66 -0
  54. package/dist/config/defaults.d.ts +52 -0
  55. package/dist/config/defaults.js +56 -0
  56. package/dist/config/load.d.ts +17 -0
  57. package/dist/config/load.js +50 -0
  58. package/dist/config/providerDefaults.d.ts +17 -0
  59. package/dist/config/providerDefaults.js +61 -0
  60. package/dist/config/schema.d.ts +9 -0
  61. package/dist/config/schema.js +78 -0
  62. package/dist/context/budget.d.ts +13 -0
  63. package/dist/context/budget.js +17 -0
  64. package/dist/context/compiler.d.ts +61 -0
  65. package/dist/context/compiler.js +125 -0
  66. package/dist/context/tokens.d.ts +19 -0
  67. package/dist/context/tokens.js +38 -0
  68. package/dist/conversations/conversations.d.ts +38 -0
  69. package/dist/conversations/conversations.js +64 -0
  70. package/dist/core/capabilities/evidence.d.ts +40 -0
  71. package/dist/core/capabilities/evidence.js +102 -0
  72. package/dist/core/capabilities/overlay.d.ts +15 -0
  73. package/dist/core/capabilities/overlay.js +0 -0
  74. package/dist/core/capabilities/taxonomy.d.ts +19 -0
  75. package/dist/core/capabilities/taxonomy.js +25 -0
  76. package/dist/core/fallback/errors.d.ts +30 -0
  77. package/dist/core/fallback/errors.js +80 -0
  78. package/dist/core/fallback/fallback.d.ts +40 -0
  79. package/dist/core/fallback/fallback.js +82 -0
  80. package/dist/core/fallback/retryPolicy.d.ts +11 -0
  81. package/dist/core/fallback/retryPolicy.js +14 -0
  82. package/dist/core/health/health.d.ts +3 -0
  83. package/dist/core/health/health.js +5 -0
  84. package/dist/core/health/monitor.d.ts +23 -0
  85. package/dist/core/health/monitor.js +82 -0
  86. package/dist/core/policies/budget.d.ts +19 -0
  87. package/dist/core/policies/budget.js +37 -0
  88. package/dist/core/registry/builtinTasks.d.ts +8 -0
  89. package/dist/core/registry/builtinTasks.js +54 -0
  90. package/dist/core/registry/registry.d.ts +18 -0
  91. package/dist/core/registry/registry.js +33 -0
  92. package/dist/core/registry/taskRegistry.d.ts +15 -0
  93. package/dist/core/registry/taskRegistry.js +30 -0
  94. package/dist/core/router/confidence.d.ts +7 -0
  95. package/dist/core/router/confidence.js +20 -0
  96. package/dist/core/router/dimensions.d.ts +16 -0
  97. package/dist/core/router/dimensions.js +60 -0
  98. package/dist/core/router/executor.d.ts +16 -0
  99. package/dist/core/router/executor.js +25 -0
  100. package/dist/core/router/filter.d.ts +34 -0
  101. package/dist/core/router/filter.js +113 -0
  102. package/dist/core/router/normalize.d.ts +30 -0
  103. package/dist/core/router/normalize.js +119 -0
  104. package/dist/core/router/request.d.ts +4 -0
  105. package/dist/core/router/request.js +21 -0
  106. package/dist/core/router/router.d.ts +32 -0
  107. package/dist/core/router/router.js +195 -0
  108. package/dist/core/router/routingPrefs.d.ts +11 -0
  109. package/dist/core/router/routingPrefs.js +30 -0
  110. package/dist/core/router/scorer.d.ts +19 -0
  111. package/dist/core/router/scorer.js +50 -0
  112. package/dist/core/router/weights.d.ts +9 -0
  113. package/dist/core/router/weights.js +31 -0
  114. package/dist/core/validation/validator.d.ts +16 -0
  115. package/dist/core/validation/validator.js +33 -0
  116. package/dist/discovery/modelCatalog.d.ts +28 -0
  117. package/dist/discovery/modelCatalog.js +105 -0
  118. package/dist/discovery/openapi.d.ts +25 -0
  119. package/dist/discovery/openapi.js +76 -0
  120. package/dist/executions/checkpoint.d.ts +26 -0
  121. package/dist/executions/checkpoint.js +114 -0
  122. package/dist/executions/execution.d.ts +51 -0
  123. package/dist/executions/execution.js +8 -0
  124. package/dist/executions/store.d.ts +52 -0
  125. package/dist/executions/store.js +124 -0
  126. package/dist/generation/generateAdapter.d.ts +17 -0
  127. package/dist/generation/generateAdapter.js +30 -0
  128. package/dist/index.d.ts +147 -0
  129. package/dist/index.js +107 -0
  130. package/dist/learning/feedback.d.ts +9 -0
  131. package/dist/learning/feedback.js +18 -0
  132. package/dist/learning/learningStore.d.ts +68 -0
  133. package/dist/learning/learningStore.js +138 -0
  134. package/dist/learning/performanceStore.d.ts +27 -0
  135. package/dist/learning/performanceStore.js +0 -0
  136. package/dist/marketplace/presets.d.ts +24 -0
  137. package/dist/marketplace/presets.js +52 -0
  138. package/dist/mcp/mcp.d.ts +31 -0
  139. package/dist/mcp/mcp.js +54 -0
  140. package/dist/memory/bm25.d.ts +16 -0
  141. package/dist/memory/bm25.js +56 -0
  142. package/dist/memory/classifier.d.ts +14 -0
  143. package/dist/memory/classifier.js +17 -0
  144. package/dist/memory/memory.d.ts +80 -0
  145. package/dist/memory/memory.js +191 -0
  146. package/dist/orchestration/executor.d.ts +35 -0
  147. package/dist/orchestration/executor.js +65 -0
  148. package/dist/orchestration/orchestrator.d.ts +42 -0
  149. package/dist/orchestration/orchestrator.js +63 -0
  150. package/dist/orchestration/plan.d.ts +37 -0
  151. package/dist/orchestration/plan.js +70 -0
  152. package/dist/orchestration/planner.d.ts +29 -0
  153. package/dist/orchestration/planner.js +69 -0
  154. package/dist/plugin/ai.d.ts +82 -0
  155. package/dist/plugin/ai.js +167 -0
  156. package/dist/probing/probe.d.ts +25 -0
  157. package/dist/probing/probe.js +63 -0
  158. package/dist/providers/factory.d.ts +18 -0
  159. package/dist/providers/factory.js +54 -0
  160. package/dist/providers/httpClient.d.ts +34 -0
  161. package/dist/providers/httpClient.js +80 -0
  162. package/dist/providers/httpProvider.d.ts +49 -0
  163. package/dist/providers/httpProvider.js +135 -0
  164. package/dist/providers/mock/demo.d.ts +13 -0
  165. package/dist/providers/mock/demo.js +58 -0
  166. package/dist/providers/mock/mockProvider.d.ts +35 -0
  167. package/dist/providers/mock/mockProvider.js +121 -0
  168. package/dist/providers/mock/scenarios.d.ts +44 -0
  169. package/dist/providers/mock/scenarios.js +30 -0
  170. package/dist/providers/provider.d.ts +26 -0
  171. package/dist/providers/provider.js +11 -0
  172. package/dist/providers/wire/anthropicWire.d.ts +6 -0
  173. package/dist/providers/wire/anthropicWire.js +83 -0
  174. package/dist/providers/wire/openaiWire.d.ts +7 -0
  175. package/dist/providers/wire/openaiWire.js +81 -0
  176. package/dist/providers/wire/registry.d.ts +8 -0
  177. package/dist/providers/wire/registry.js +20 -0
  178. package/dist/providers/wire/types.d.ts +39 -0
  179. package/dist/providers/wire/types.js +24 -0
  180. package/dist/runtime/config.d.ts +31 -0
  181. package/dist/runtime/config.js +121 -0
  182. package/dist/runtime/context.d.ts +34 -0
  183. package/dist/runtime/context.js +11 -0
  184. package/dist/runtime/events.d.ts +99 -0
  185. package/dist/runtime/events.js +82 -0
  186. package/dist/runtime/host.d.ts +27 -0
  187. package/dist/runtime/host.js +7 -0
  188. package/dist/runtime/intent/classifier.d.ts +30 -0
  189. package/dist/runtime/intent/classifier.js +60 -0
  190. package/dist/runtime/intent/signals.d.ts +19 -0
  191. package/dist/runtime/intent/signals.js +46 -0
  192. package/dist/runtime/modes/availability.d.ts +11 -0
  193. package/dist/runtime/modes/availability.js +17 -0
  194. package/dist/runtime/modes/chat.d.ts +18 -0
  195. package/dist/runtime/modes/chat.js +67 -0
  196. package/dist/runtime/modes/modeResolver.d.ts +43 -0
  197. package/dist/runtime/modes/modeResolver.js +78 -0
  198. package/dist/runtime/policy.d.ts +72 -0
  199. package/dist/runtime/policy.js +59 -0
  200. package/dist/runtime/providerView.d.ts +62 -0
  201. package/dist/runtime/providerView.js +105 -0
  202. package/dist/runtime/routing.d.ts +26 -0
  203. package/dist/runtime/routing.js +65 -0
  204. package/dist/runtime/runtime.d.ts +191 -0
  205. package/dist/runtime/runtime.js +718 -0
  206. package/dist/runtime/types.d.ts +153 -0
  207. package/dist/runtime/types.js +9 -0
  208. package/dist/runtime/workspace/detectors.d.ts +15 -0
  209. package/dist/runtime/workspace/detectors.js +57 -0
  210. package/dist/runtime/workspace/workspace.d.ts +29 -0
  211. package/dist/runtime/workspace/workspace.js +116 -0
  212. package/dist/security/credentials.d.ts +26 -0
  213. package/dist/security/credentials.js +34 -0
  214. package/dist/security/redact.d.ts +16 -0
  215. package/dist/security/redact.js +57 -0
  216. package/dist/skills/builtins/fileAnalyzer.d.ts +7 -0
  217. package/dist/skills/builtins/fileAnalyzer.js +47 -0
  218. package/dist/skills/builtins/repositoryAnalyzer.d.ts +6 -0
  219. package/dist/skills/builtins/repositoryAnalyzer.js +47 -0
  220. package/dist/skills/discovery.d.ts +61 -0
  221. package/dist/skills/discovery.js +211 -0
  222. package/dist/skills/manifest.d.ts +30 -0
  223. package/dist/skills/manifest.js +75 -0
  224. package/dist/skills/registry.d.ts +15 -0
  225. package/dist/skills/registry.js +22 -0
  226. package/dist/skills/skill.d.ts +64 -0
  227. package/dist/skills/skill.js +8 -0
  228. package/dist/store/area.d.ts +54 -0
  229. package/dist/store/area.js +164 -0
  230. package/dist/store/paths.d.ts +15 -0
  231. package/dist/store/paths.js +48 -0
  232. package/dist/store/store.d.ts +59 -0
  233. package/dist/store/store.js +140 -0
  234. package/dist/telemetry/sinks/file.d.ts +12 -0
  235. package/dist/telemetry/sinks/file.js +28 -0
  236. package/dist/telemetry/telemetry.d.ts +36 -0
  237. package/dist/telemetry/telemetry.js +63 -0
  238. package/dist/tools/builtins/filesystem.d.ts +7 -0
  239. package/dist/tools/builtins/filesystem.js +53 -0
  240. package/dist/tools/builtins/git.d.ts +10 -0
  241. package/dist/tools/builtins/git.js +66 -0
  242. package/dist/tools/builtins/shell.d.ts +17 -0
  243. package/dist/tools/builtins/shell.js +91 -0
  244. package/dist/tools/jail.d.ts +12 -0
  245. package/dist/tools/jail.js +98 -0
  246. package/dist/tools/permissions.d.ts +25 -0
  247. package/dist/tools/permissions.js +24 -0
  248. package/dist/tools/registry.d.ts +10 -0
  249. package/dist/tools/registry.js +20 -0
  250. package/dist/tools/runner.d.ts +23 -0
  251. package/dist/tools/runner.js +64 -0
  252. package/dist/tools/tool.d.ts +53 -0
  253. package/dist/tools/tool.js +24 -0
  254. package/dist/tools/untrusted.d.ts +13 -0
  255. package/dist/tools/untrusted.js +30 -0
  256. package/dist/types.d.ts +460 -0
  257. package/dist/types.js +12 -0
  258. package/dist/util/clock.d.ts +6 -0
  259. package/dist/util/clock.js +4 -0
  260. package/dist/util/extractJson.d.ts +8 -0
  261. package/dist/util/extractJson.js +54 -0
  262. package/dist/verification/verify.d.ts +26 -0
  263. package/dist/verification/verify.js +67 -0
  264. package/docs/GUIDE.md +358 -0
  265. package/docs/README.md +21 -0
  266. package/docs/architecture.md +78 -0
  267. package/docs/router.md +376 -0
  268. package/docs/security.md +55 -0
  269. package/package.json +67 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ All notable changes to `ai-runtime` are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/), and the project adheres to [Semantic
5
+ Versioning](https://semver.org/). Development history and rationale live in
6
+ [docs/DECISIONS.md](docs/DECISIONS.md) and [docs/PROGRESS.md](docs/PROGRESS.md).
7
+
8
+ ## [1.1.0] — 2026-08-29
9
+
10
+ Closes out the two items 1.0 deferred. Additive and backward compatible.
11
+
12
+ ### Added
13
+
14
+ - **npm skill packs** — a skill pack is any npm package that exports `skills: Skill[]`. Name installed
15
+ packs under `skills.packages` in config and the Runtime loads them from `node_modules` as trusted
16
+ dependencies (not workspace-jailed, unlike local `.ai-runtime/skills/` files). Ships an example pack,
17
+ `packages/example-skills` (`ai-runtime-example-skills`), and turns the repo into an npm workspace.
18
+ - **Windows CI** — GitHub Actions now runs the typecheck/test/build matrix on `windows-latest` as well as
19
+ `ubuntu-latest` (`fail-fast: false`). Symlink-specific tests skip cleanly where symlink creation needs
20
+ elevation; a multi-hop symlink-chain jail test was added.
21
+
22
+ ## [1.0.0] — 2026-08-29
23
+
24
+ First stable release. `ai-runtime` grew from a provider-agnostic AI **router** into a full **AI Runtime &
25
+ Orchestration Platform** — the `Runtime` facade sits on top of the unchanged router (every model call
26
+ still goes through `AI.run()`). The router's public API (`AI`, `AI.run()`, config, providers, tasks, CLI
27
+ commands) is preserved and backward compatible.
28
+
29
+ ### Added — the Runtime
30
+
31
+ - **Modes** — `auto | chat | plan | execute | orchestrate | agent | compare | debug`, with
32
+ per-run > config > env > detection precedence; a mode is never overridden, and unavailable modes
33
+ degrade to chat with a notice.
34
+ - **Interactive terminal** — bare `ai-runtime` opens a REPL (natural language + slash commands); a
35
+ one-shot `run` for CI. Typed lifecycle events + a host contract.
36
+ - **Conversations & memory** — verbatim conversation history (recoverable, secret-scrubbed), and durable
37
+ scoped memory with local BM25 retrieval, conflict supersession, and cascading deletion. Stateless mode
38
+ for CI/ephemeral use; monorepo-isolated local state under `~/.ai-runtime/`.
39
+ - **Context compiler & token budgets** — budget-aware context assembly with retention levels, redundancy
40
+ removal, artifact references, and loss validation.
41
+ - **Tools, permissions & skills** — deny-by-default permissions, a workspace path jail, allowlist-first
42
+ shell, git gating, and an untrusted-data boundary; versioned skills that validate by evidence.
43
+ - **Planning & orchestration** — structured plans limited to registered skills/tools, an evidence-based
44
+ DAG executor, a replanning orchestrator, plan approval, and dry-run (zero mutations).
45
+ - **Persistent executions & resume** — executions persisted before they start, a best-effort ownership
46
+ lease with heartbeat, checkpoints, `requestId` idempotency, and drift-aware resume/reconciliation.
47
+ - **Comparison** — fan a task across models; agreement / differences / contradictions / missing info with
48
+ **evidence-first** ranking (a supplied validator beats a judge model).
49
+ - **Exclude / prefer routing** — a hard exclude filter (visible in the trace) and a soft prefer nudge,
50
+ resolved from env + config + per-run.
51
+ - **Learning & feedback** — provider/skill/workflow/plan outcome learning and `/feedback`, feeding only
52
+ soft provider preferences; a safety test proves learning can never override a hard gate.
53
+ - **Local skill discovery** — auto-load manifests and module skills from `.ai-runtime/skills/`, discover
54
+ more across the workspace (report-only), all path-jailed.
55
+ - **Bootstrap & maintenance** — `setup` (ask-before-mutation), `info`, an extended `doctor --json`, and
56
+ `cleanup` (opt-in retention + cache + integrity).
57
+
58
+ ### Security
59
+
60
+ - Untrusted-data boundary; workspace path jail (tested against `..`, absolute paths, and symlinks);
61
+ allowlist-first shell with a destructive denylist; secrets named by env var only and redacted at every
62
+ egress. Every phase shipped an adversarial security review; the confirmed findings — including path-jail
63
+ escapes and resume/lease concurrency issues — were fixed and regression-tested.
64
+
65
+ ### Engineering
66
+
67
+ - TypeScript, ESM, Node ≥ 22; offline-deterministic test suite; typecheck + full suite + build green at
68
+ every phase gate. GitHub Actions CI runs typecheck, tests, and build on every push and PR.
69
+
70
+ ## [0.1.0] — 2026-08-26
71
+
72
+ Initial release: the provider-agnostic AI **router** — capability-based routing, filtering, seven-dimension
73
+ scoring, evidence validation, fallback, health tracking, learning-based scoring, multi-model verification,
74
+ budgets, MCP tools, OpenAPI-based adapter generation, and the `AI` class + CLI.
75
+
76
+ [1.1.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.1.0
77
+ [1.0.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.0.0
78
+ [0.1.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 pavan
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,489 @@
1
+ # ai-runtime
2
+
3
+ **A provider-agnostic AI runtime for building and running AI features — without locking yourself to one vendor.**
4
+
5
+ You point it at whatever AI providers you have (cloud APIs, a local model, or an internal endpoint). It
6
+ routes each request to the best available model, and gives you a whole runtime on top of that: modes,
7
+ an interactive terminal, memory, skills, tools, planning, orchestration, comparison, and learning.
8
+
9
+ ```ts
10
+ import { Runtime } from 'ai-runtime-engine';
11
+
12
+ const runtime = await Runtime.load(); // reads your config + .env
13
+ const result = await runtime.run({ input: 'Fix the failing tests' });
14
+ console.log(result.response?.text);
15
+ ```
16
+
17
+ > **The idea:** providers are replaceable; capabilities are durable. You never have to know *which*
18
+ > provider handled a request — you describe what you want, and the runtime picks the right model and runs it.
19
+
20
+ > 📘 **New here?** Start with the **[hands-on Guide](docs/GUIDE.md)** — install to first run, with runnable
21
+ > examples you can try offline (no API key needed).
22
+
23
+ ---
24
+
25
+ ## Contents
26
+
27
+ - [What it is](#what-it-is)
28
+ - [Requirements](#requirements)
29
+ - [Install](#install)
30
+ - [Quick start](#quick-start)
31
+ - [How it works](#how-it-works)
32
+ - [How to use it](#how-to-use-it) — modes, terminal, memory, skills, orchestration, comparison, learning
33
+ - [Configuration](#configuration)
34
+ - [What you need to do](#what-you-need-to-do) — a getting-started checklist
35
+ - [CLI commands](#cli-commands)
36
+ - [Package & distribution](#package--distribution)
37
+ - [Security & privacy](#security--privacy)
38
+ - [Troubleshooting](#troubleshooting)
39
+ - [Further reading](#further-reading)
40
+ - [Development](#development)
41
+
42
+ ---
43
+
44
+ ## What it is
45
+
46
+ Hardcoding one AI provider means vendor lock-in, availability risk, and painful migrations — and different
47
+ providers are better at different things (reasoning, speed, cost, privacy), while any of them can be
48
+ rate-limited, out of quota, or down.
49
+
50
+ `ai-runtime` separates **provider support** from **provider availability**. Configure whatever you have
51
+ once; then, on every request, the runtime:
52
+
53
+ 1. figures out **what kind of work** it is (a quick answer, a plan, a multi-step task, a comparison…),
54
+ 2. **routes** it to the best model you have that can actually do it (by capability, health, privacy, cost),
55
+ 3. **executes** through one normalized interface and **validates** the result,
56
+ 4. **falls back** to the next-best model if something fails, and
57
+ 5. returns a clean result with a full trace of how it decided.
58
+
59
+ Around that router sits the **Runtime**: an interactive terminal, conversation history, durable memory,
60
+ context building, a skill/tool system with permissions, planning and orchestration, resumable long-running
61
+ tasks, model comparison, and learning from outcomes. Everything runs over native `fetch` — **no provider
62
+ SDKs**.
63
+
64
+ ---
65
+
66
+ ## Requirements
67
+
68
+ - **Node.js 22 or newer.**
69
+ - Git is optional (used to detect your workspace).
70
+ - At least one AI provider — a cloud API key, or a local model via [Ollama](https://ollama.com) (no key needed).
71
+
72
+ ---
73
+
74
+ ## Install
75
+
76
+ The npm package is **`ai-runtime-engine`**. Installing it gives you both a **library** and the
77
+ **`ai-runtime`** command-line tool (the command name is `ai-runtime` — a package name need not match its
78
+ command name).
79
+
80
+ ```bash
81
+ # in your project (library + local CLI via `npx ai-runtime`)
82
+ npm install ai-runtime-engine
83
+
84
+ # or install globally to get the `ai-runtime` command on your PATH
85
+ npm install -g ai-runtime-engine
86
+ ```
87
+
88
+ Requires Node 22+. Use it as a library:
89
+
90
+ ```ts
91
+ import { Runtime, AI } from 'ai-runtime-engine';
92
+ ```
93
+
94
+ > **Naming:** the **product** is *AI Runtime*, the **npm package** is `ai-runtime-engine`, and the **CLI
95
+ > command** is `ai-runtime`. The main API is `Runtime`; the lower-level router is `AI`.
96
+
97
+ ---
98
+
99
+ ## Quick start
100
+
101
+ ### Option A — the terminal (fastest)
102
+
103
+ ```bash
104
+ npm install -g ai-runtime-engine # (or use `npx ai-runtime …` from a project that installed it)
105
+
106
+ ai-runtime setup # detects your workspace, generates config + .env.example (asks before each write)
107
+ # → add a provider key to .env, OR install Ollama for a local, no-key model
108
+ ai-runtime doctor # check that a provider is configured and reachable
109
+ ai-runtime # open the interactive terminal — just type what you want
110
+ ```
111
+
112
+ ### Option B — in code
113
+
114
+ ```ts
115
+ import { Runtime } from 'ai-runtime-engine';
116
+
117
+ const runtime = await Runtime.load({ workspace: process.cwd() });
118
+ const r = await runtime.run({ input: 'Summarize what this repo does' });
119
+ console.log(r.response?.text);
120
+ ```
121
+
122
+ ### Try it with no keys (offline)
123
+
124
+ Use the built-in mock provider to see everything work without any credentials — great for tests and demos:
125
+
126
+ ```ts
127
+ import { Runtime, MockProvider, makeModel } from 'ai-runtime-engine';
128
+
129
+ const runtime = new Runtime();
130
+ runtime.ai.registerProvider(new MockProvider({
131
+ id: 'demo',
132
+ models: [makeModel({ providerId: 'demo', id: 'demo-1', caps: { intelligence: ['reasoning'], output: ['text'] } })],
133
+ }));
134
+ const r = await runtime.run({ input: 'hello' });
135
+ console.log(r.response?.text, r.routing?.selected); // includes the full routing decision
136
+ ```
137
+
138
+ ---
139
+
140
+ ## How it works
141
+
142
+ A request goes through one vendor-agnostic path. You provide the capabilities (providers, tools, skills,
143
+ a workspace); the runtime decides *how* to use them.
144
+
145
+ ```
146
+ runtime.run({ input })
147
+
148
+ ▼ resolve MODE (auto-detected, or set by you): chat · plan · execute · orchestrate · agent · compare · debug
149
+
150
+ ▼ build the model context under a token budget (workspace summary, memory, your request)
151
+
152
+ ▼ route to the best model ──► the ONE router: filter (capability/health/privacy/cost) → score → pick
153
+ │ → execute → validate → fall back on failure
154
+
155
+ ▼ for multi-step work: plan → run tools/skills as a DAG (succeed a step only on evidence) → replan on failure
156
+
157
+ ▼ result { ok, response, routing trace, + plan / execution / comparison / memory as relevant }
158
+ ```
159
+
160
+ Two principles hold everywhere:
161
+
162
+ - **One router.** Every model call — chat, planning, comparison, a skill's own call — goes through the same
163
+ router. There's never a second, hidden selection engine.
164
+ - **Evidence over claims.** A step "succeeds" only when there's proof (a tool returned ok, a validator
165
+ passed) — never because a model said "done."
166
+
167
+ For the engine's internals (scoring dimensions, strategies, provider contracts, the `AI` API), see
168
+ [docs/router.md](docs/router.md).
169
+
170
+ ---
171
+
172
+ ## How to use it
173
+
174
+ Load the runtime once, then `run()` requests. It resolves a **mode** and dispatches.
175
+
176
+ ```ts
177
+ const runtime = await Runtime.load({ workspace: process.cwd() });
178
+ runtime.ai.registerProvider(myProvider); // register providers the same way as the router
179
+ const r = await runtime.run({ input: 'Explain this stack trace: …' });
180
+ ```
181
+
182
+ ### Modes
183
+
184
+ Every run resolves to one of eight modes. **`auto`** (the default) detects intent; you can force any mode
185
+ per run (`{ mode: 'plan' }`), in config (`runtime.defaultMode`), or via `AI_DEFAULT_MODE`. Precedence:
186
+ per-run > config > env > detection — an explicit mode is never overridden.
187
+
188
+ | Mode | What it does |
189
+ |---|---|
190
+ | `chat` | A single conversational answer (memory- and workspace-aware). |
191
+ | `plan` | Produce a structured plan; execute nothing. |
192
+ | `execute` | Run a planned sequence once. |
193
+ | `orchestrate` | Plan → execute → observe → replan until done or a limit is hit. |
194
+ | `agent` | A longer autonomous loop with hard stops (iterations, budget, permissions). |
195
+ | `debug` | Evidence-first investigation (gather → hypotheses → validate → conclude). |
196
+ | `compare` | Fan the same task across models and analyze the results. |
197
+
198
+ Mode is *what kind of work*; **autonomy** is *how much independent action* — `chat`/`plan` need no approval,
199
+ `execute`/`orchestrate` ask before acting, `agent` runs autonomously within limits.
200
+
201
+ ### Interactive terminal
202
+
203
+ Bare `ai-runtime` opens a REPL: type natural language, or use slash commands to inspect and steer —
204
+ `/mode`, `/status`, `/info`, `/providers`, `/models`, `/skills [discover]`, `/tools`, `/permissions`,
205
+ `/memory`, `/conversations`, `/resume`, `/executions`, `/compare`, `/feedback`, `/learning`, `/doctor`,
206
+ `/cleanup`, `/dry-run`. For CI, `ai-runtime run "<input>" [--mode m] [--dry-run]` is the one-shot equivalent.
207
+
208
+ ### Conversations & memory
209
+
210
+ Conversations are saved verbatim (recoverable, with secrets scrubbed). Memory is durable, scoped facts —
211
+ say "remember that we use Postgres" and it's kept, retrieved when relevant, and superseded when it changes.
212
+ Turn persistence off entirely with `Runtime.load({ persistence: 'disabled' })` (for CI/ephemeral use).
213
+
214
+ ### Skills & tools (and your own)
215
+
216
+ **Tools** are concrete operations — `filesystem`, `shell`, and `git` are built in; add your own. They're
217
+ **deny-by-default**: reads are allowed and jailed to your workspace, but writes, shell, git commit/push,
218
+ and network are off until you grant them in config.
219
+
220
+ **Skills** are reusable capabilities that compose tools. Beyond the built-ins, the runtime **loads your
221
+ own local skills**:
222
+
223
+ - Drop a manifest (`*.skill.yaml`) or a JS module (`*.skill.mjs`) into **`.ai-runtime/skills/`** — auto-loaded.
224
+ - `ai-runtime skills --discover` scans the rest of your repo and **reports** other skill files without
225
+ loading them (you enable one deliberately).
226
+ - Install a **skill pack** (any npm package that exports `skills`) and name it under `skills.packages`.
227
+
228
+ ```yaml
229
+ # .ai-runtime/skills/read-changelog.skill.yaml — a declarative skill (composes tools, no code)
230
+ id: read-changelog
231
+ version: 1.0.0
232
+ name: Read changelog
233
+ description: read CHANGELOG.md
234
+ steps:
235
+ - tool: filesystem
236
+ input: { op: read, path: CHANGELOG.md }
237
+ ```
238
+
239
+ ### Planning, orchestration & dry-run
240
+
241
+ For multi-step work, the runtime plans (using only your registered skills/tools), runs the plan as a DAG,
242
+ succeeds a step only on evidence, and replans on failure. **Dry-run** (`run --dry-run`, `/dry-run`) shows
243
+ the plan and what it *would* touch — with zero changes made.
244
+
245
+ ### Persistent executions & resume
246
+
247
+ Longer runs are saved before they start and can be resumed. Resume never blindly continues: it re-checks
248
+ your workspace (git state, touched files, config) and replans if anything drifted. `/executions`,
249
+ `/resume-execution <id>`, `/pause`, `/cancel`.
250
+
251
+ ### Comparison
252
+
253
+ `compare` mode (or `runtime.compare(...)`) runs the same task across models and shows where they agree,
254
+ differ, and contradict each other. If you supply a way to *check* each answer (e.g. run the tests), the one
255
+ that passes wins — evidence beats opinion.
256
+
257
+ ### Steering which models get used
258
+
259
+ - **Exclude = a hard filter.** An excluded provider/model is **never** selected (it still shows in the
260
+ routing trace as `⊘ excluded by user configuration`).
261
+ - **Prefer = a soft preference.** A preferred model is nudged up, but a preferred-but-incapable/unhealthy
262
+ one still loses.
263
+ - **Neither configured = normal routing** across all eligible models.
264
+
265
+ Set them in config `routing:`, per run (`runtime.run({ input, routing: {…} })`), or via environment
266
+ variables — they combine, so an exclusion set anywhere always holds:
267
+
268
+ ```bash
269
+ AI_EXCLUDE_PROVIDERS=ollama
270
+ AI_EXCLUDE_MODELS=gemini:model-a,groq:model-b # provider:model, or a bare model id
271
+ AI_PREFER_PROVIDERS=gemini
272
+ AI_PREFER_MODELS=groq:model-c
273
+ ```
274
+
275
+ **Learning** — the runtime learns which providers/skills work from real outcomes and your `/feedback`, and
276
+ turns that into *soft* preferences only. **Learning can never override an exclusion** (nor a privacy rule,
277
+ a pin, a required capability, or the budget) — a guarantee proven by a dedicated test.
278
+
279
+ ---
280
+
281
+ ## Configuration
282
+
283
+ Three places, and only these:
284
+
285
+ | Path | What | In git? |
286
+ |---|---|---|
287
+ | `.ai-runtime/config.yaml` | Project config — runtime settings + providers (env-var **names** only). Falls back to root `ai-runtime.yaml`. | ✅ commit it |
288
+ | `.env` | The secret **values** for the env vars your config names. | ❌ gitignored |
289
+ | `~/.ai-runtime/` | Local state — conversations, memory, executions, cache, learning. Override with `AI_RUNTIME_HOME`. | ❌ never in git |
290
+
291
+ A minimal config:
292
+
293
+ ```yaml
294
+ # .ai-runtime/config.yaml
295
+ runtime:
296
+ defaultMode: auto
297
+ providers:
298
+ - id: gemini
299
+ kind: gemini
300
+ apiKeyEnv: GEMINI_API_KEY # the NAME of the env var — never the value
301
+ - id: ollama # local, no key required
302
+ kind: ollama
303
+ ```
304
+
305
+ **Provider API keys** go in `.env` — the config names the variable, the value lives here. Each provider
306
+ kind has a default variable name (override with `apiKeyEnv`): `GEMINI_API_KEY`, `GROQ_API_KEY`,
307
+ `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY` (Ollama needs none).
308
+
309
+ **Optional runtime variables** — all optional; unset means the built-in default:
310
+
311
+ | Variable | Effect |
312
+ |---|---|
313
+ | `AI_DEFAULT_MODE` | Default mode when a run doesn't set one (`auto` by default). |
314
+ | `AI_DEFAULT_STRATEGY` | Default routing strategy. |
315
+ | `AI_CONTEXT_MAX_TOKENS` | Token budget for the compiled model context. |
316
+ | `AI_MAX_COST_USD` · `AI_MAX_CALLS` | Per-run spend / call guardrails. |
317
+ | `AI_MEMORY_ENABLED` | Set `false` to disable memory. |
318
+ | `AI_LEARNING_ENABLED` | Set `false` to disable outcome learning. |
319
+ | `AI_EXCLUDE_PROVIDERS` · `AI_EXCLUDE_MODELS` | Hard-exclude providers / models (see [steering](#steering-which-models-get-used)). |
320
+ | `AI_PREFER_PROVIDERS` · `AI_PREFER_MODELS` | Soft-prefer providers / models. |
321
+ | `AI_CONVERSATION_RETENTION_DAYS` · `AI_EXECUTION_RETENTION_DAYS` · `AI_ARTIFACT_RETENTION_DAYS` | Retention for `cleanup` (unset = keep everything). |
322
+ | `AI_RUNTIME_HOME` | Override the `~/.ai-runtime/` state directory. |
323
+ | `AI_TELEMETRY_ENABLED` | *Reserved — not yet honored by the runtime.* Telemetry is metadata-only and set in config. |
324
+
325
+ `ai-runtime setup` writes the config and `.env.example` for you (asking first), and `ai-runtime info` shows
326
+ everything resolved. For the full router config (routing strategies, scoring weights, privacy posture,
327
+ telemetry, self-declared providers), see [docs/router.md](docs/router.md).
328
+
329
+ ### Providers
330
+
331
+ If no provider is configured, `ai-runtime setup` and `ai-runtime doctor` walk you through adding one.
332
+ Recommended starters:
333
+
334
+ | Provider | Type | Key | Where to get it |
335
+ |---|---|---|---|
336
+ | **Google Gemini** | Cloud API | `GEMINI_API_KEY` | https://aistudio.google.com/apikey |
337
+ | **Groq** | Cloud API | `GROQ_API_KEY` | https://console.groq.com/keys |
338
+ | **OpenRouter** | Cloud API (aggregator) | `OPENROUTER_API_KEY` | https://openrouter.ai/keys |
339
+ | **Ollama** | Local (no key) | — | https://ollama.com |
340
+
341
+ Cloud providers need an API key; **Ollama** runs models locally with no key. Availability, models, and any
342
+ free/paid tiers are set by each provider — check their site for current terms.
343
+
344
+ ---
345
+
346
+ ## What you need to do
347
+
348
+ A getting-started checklist:
349
+
350
+ 1. **Install** — Node 22+, then `npm install -g ai-runtime-engine` (or add it to a project). Verify with
351
+ `ai-runtime --help`.
352
+ 2. **Scaffold** — run `ai-runtime setup`. It creates `.ai-runtime/config.yaml`, `.env.example`, and
353
+ `.gitignore` entries (asking before each write).
354
+ 3. **Add a provider** — either put a real key in `.env` (e.g. `GEMINI_API_KEY=…`), or install
355
+ [Ollama](https://ollama.com) for a local model with no key.
356
+ 4. **Check it** — `ai-runtime doctor` confirms a provider is configured and reachable.
357
+ 5. **Use it** — `ai-runtime` (terminal), `ai-runtime run "…"` (one-shot), or `Runtime.load().run(…)` (code).
358
+ 6. **Go further (optional)** — add skills to `.ai-runtime/skills/`, grant write/shell permissions under
359
+ `permissions:` when you want the runtime to act, and set `routing:` to steer model selection.
360
+
361
+ ---
362
+
363
+ ## CLI commands
364
+
365
+ The commands you'll use day to day:
366
+
367
+ | Command | What it does |
368
+ |---|---|
369
+ | `ai-runtime` | Open the interactive terminal. |
370
+ | `ai-runtime setup` | Detect the workspace and scaffold config (asks before writing). |
371
+ | `ai-runtime run "<input>" [--mode m] [--dry-run] [--json]` | Run one request (one-shot, for scripts/CI). |
372
+ | `ai-runtime info [--json]` | Read-only summary: workspace, providers, skills, tools, memory, storage. |
373
+ | `ai-runtime doctor [--json]` | Diagnose config, `.env` (by name), providers, models, store, permissions, git. |
374
+ | `ai-runtime skills [--discover]` | List available skills; `--discover` reports skill files elsewhere in the repo. |
375
+ | `ai-runtime cleanup [--dry-run] [--yes]` | Enforce retention, drop the cache, validate the store. |
376
+ | `ai-runtime executions` · `resume-execution <id>` | List and resume persisted executions. |
377
+
378
+ The engine also ships lower-level commands (`route`, `providers`, `models`, `capabilities`, `probe`,
379
+ `discover`, `benchmark`, `presets`, `telemetry`) — see [docs/router.md](docs/router.md). Run any command
380
+ with `--help` for its flags.
381
+
382
+ ### Inside the interactive terminal
383
+
384
+ Type a request in plain language, or use a slash command to inspect and steer. The commands that exist
385
+ today:
386
+
387
+ ```
388
+ /help show all commands
389
+ /status /info workspace, mode, provider count · full runtime summary
390
+ /mode [name] show or set the mode (auto|chat|plan|…)
391
+ /providers list providers + access state
392
+ /providers/all models list every provider's models
393
+ /providers/<id> [models|capabilities|health] inspect one provider
394
+ /providers/setup how to add a provider (keys / Ollama)
395
+ /models list all known models
396
+ /skills [discover] /tools /permissions
397
+ /memory [list|search <q>|delete <id>|clear]
398
+ /conversations /resume <id>
399
+ /executions /resume-execution <id> /pause <id> /cancel <id>
400
+ /compare <request> compare across all providers
401
+ /feedback <text> /learning record feedback · show what was learned
402
+ /dry-run toggle dry-run (plan only, no changes)
403
+ /doctor /cleanup /config diagnostics · retention preview · resolved config
404
+ /clear /exit
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Package & distribution
410
+
411
+ | | |
412
+ |---|---|
413
+ | **Product** | AI Runtime |
414
+ | **npm package** | `ai-runtime-engine` |
415
+ | **CLI command** | `ai-runtime` |
416
+ | **Source repository** | private |
417
+ | **Project config** | `.ai-runtime/config.yaml` (committed) |
418
+ | **Secrets** | `.env` (gitignored) |
419
+ | **Runtime state** | `~/.ai-runtime/` (never committed) |
420
+
421
+ The public npm package contains the **distributed runtime code** (compiled `dist/`), this README, and the
422
+ changelog. It **does not** contain any of your data — no API keys, `.env`, memory, conversations, execution
423
+ state, local indexes, cache, or other runtime state; those live only on your machine under `~/.ai-runtime/`
424
+ and `.env`, and are never part of the package.
425
+
426
+ The **source repository is private**; the npm package is public. Note that a public npm package still ships
427
+ runnable code — publishing to npm distributes the compiled implementation to anyone who installs it. It
428
+ does not hide the implementation from package consumers; keeping the *repository* private only keeps the
429
+ source history and development materials private.
430
+
431
+ ---
432
+
433
+ ## Security & privacy
434
+
435
+ - Everything the runtime reads through tools, files, the web, or MCP is treated as **data, never
436
+ instructions** — it can't change permissions, obtain secrets, run commands, or bypass approvals.
437
+ - Filesystem access is **jailed to your workspace** (tested against `..`, absolute paths, and symlinks).
438
+ Shell is allowlist-first with a destructive-command denylist; writes, commit/push, and network are
439
+ each gated and off by default.
440
+ - **Secrets are named by env var only** in config; an inline key is rejected at load, and every value is
441
+ redacted at every egress — including the terminal and logs.
442
+ - **Privacy:** high-sensitivity input is kept off cloud providers unless you explicitly allow it.
443
+
444
+ Full details in [docs/security.md](docs/security.md).
445
+
446
+ ---
447
+
448
+ ## Troubleshooting
449
+
450
+ - **"No compatible AI provider is available"** — run `ai-runtime doctor`: it shows which provider keys are
451
+ set (by name), which providers authenticated, and how many models are usable. Add a key to `.env`, or a
452
+ provider to config; with none, `ai-runtime setup` lists free/local options (Ollama needs no key).
453
+ - **A provider is never chosen** — check `doctor` for its health and `/providers` for its access state; a
454
+ provider you excluded shows as `⊘ excluded by user configuration` in the routing trace.
455
+ - **Writes or shell are refused** — that's deny-by-default; grant them under `permissions:` in config
456
+ (destructive shell always asks). Use `--dry-run` to preview what a run would touch.
457
+ - **A local skill isn't loading** — `ai-runtime skills --discover` shows what was found and any load errors;
458
+ module skills load only from `.ai-runtime/skills/` or a configured `skills.paths`.
459
+ - **Nothing persists** — you may be in stateless mode (`persistence: 'disabled'`) or have `AI_*_ENABLED`
460
+ toggles off; `ai-runtime info` shows storage status.
461
+
462
+ ---
463
+
464
+ ## Further reading
465
+
466
+ - **[docs/GUIDE.md](docs/GUIDE.md)** — a hands-on, study-friendly guide: install → first run → how it works
467
+ → worked examples, runnable offline.
468
+ - **[docs/architecture.md](docs/architecture.md)** — how the two layers fit together, the one-router
469
+ invariant, and the lifecycle of a request.
470
+ - **[docs/router.md](docs/router.md)** — the router engine reference: config keys, strategies, scoring,
471
+ providers, the `AI` API, and advanced features.
472
+ - **[docs/security.md](docs/security.md)** — the full security model.
473
+ - **[CHANGELOG.md](CHANGELOG.md)** — version history.
474
+
475
+ ---
476
+
477
+ ## Development
478
+
479
+ TypeScript, ESM, Node ≥ 22. The test suite is fully offline and deterministic.
480
+
481
+ ```bash
482
+ npm install
483
+ npm run typecheck # tsc --noEmit (strict)
484
+ npm test # node:test — offline, no keys, no network
485
+ npm run build # tsc → dist/ (ESM + .d.ts)
486
+ npm run cli -- info # run the CLI from source
487
+ ```
488
+
489
+ CI (GitHub Actions) runs typecheck, tests, and build on Linux and Windows for every push and pull request.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Artifact store — the content owner behind ArtifactRef. Large outputs (traces, diffs, logs, reports)
3
+ * are written here once and passed around the runtime as compact references; the context compiler
4
+ * decides if/when to inline the content. Backed by a store Area (checksummed, atomic). No-op when the
5
+ * store is disabled (stateless mode).
6
+ */
7
+ import type { Clock } from '../util/clock.js';
8
+ import type { Area } from '../store/area.js';
9
+ import type { ArtifactRef } from '../runtime/types.js';
10
+ export interface PutArtifactInput {
11
+ type: string;
12
+ source: string;
13
+ content: string;
14
+ metadata?: Record<string, unknown>;
15
+ }
16
+ export declare class ArtifactStore {
17
+ private readonly area;
18
+ private readonly clock;
19
+ private counter;
20
+ constructor(area: Area, clock?: Clock);
21
+ get enabled(): boolean;
22
+ /** Store content and return a reference. The content lives here; callers pass the ref around. */
23
+ put(input: PutArtifactInput): ArtifactRef;
24
+ /** Resolve a reference back to its content (undefined if missing/corrupt). */
25
+ resolve(ref: ArtifactRef): string | undefined;
26
+ list(): string[];
27
+ delete(id: string): void;
28
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Artifact store — the content owner behind ArtifactRef. Large outputs (traces, diffs, logs, reports)
3
+ * are written here once and passed around the runtime as compact references; the context compiler
4
+ * decides if/when to inline the content. Backed by a store Area (checksummed, atomic). No-op when the
5
+ * store is disabled (stateless mode).
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ import { systemClock } from '../util/clock.js';
9
+ export class ArtifactStore {
10
+ area;
11
+ clock;
12
+ counter = 0;
13
+ constructor(area, clock = systemClock) {
14
+ this.area = area;
15
+ this.clock = clock;
16
+ }
17
+ get enabled() {
18
+ return this.area.enabled;
19
+ }
20
+ /** Store content and return a reference. The content lives here; callers pass the ref around. */
21
+ put(input) {
22
+ const id = `art_${this.clock.now().toString(36)}_${(this.counter += 1).toString(36)}`;
23
+ const checksum = createHash('sha256').update(input.content).digest('hex');
24
+ this.area.writeJson(id, { content: input.content, type: input.type, source: input.source, checksum, ...(input.metadata ? { metadata: input.metadata } : {}) });
25
+ return {
26
+ id,
27
+ type: input.type,
28
+ source: input.source,
29
+ contentRef: id,
30
+ checksum,
31
+ size: input.content.length,
32
+ ...(input.metadata ? { metadata: input.metadata } : {}),
33
+ };
34
+ }
35
+ /** Resolve a reference back to its content (undefined if missing/corrupt). */
36
+ resolve(ref) {
37
+ const rec = this.area.tryReadJson(ref.contentRef);
38
+ return rec?.content;
39
+ }
40
+ list() {
41
+ return this.area.listIds();
42
+ }
43
+ delete(id) {
44
+ this.area.remove(id);
45
+ }
46
+ }