ai-runtime-engine 1.3.0 → 2.7.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 (138) hide show
  1. package/CHANGELOG.md +578 -0
  2. package/dist/agents/admit.d.ts +69 -0
  3. package/dist/agents/admit.js +129 -0
  4. package/dist/agents/definition.d.ts +36 -0
  5. package/dist/agents/definition.js +9 -0
  6. package/dist/agents/envelope.d.ts +53 -0
  7. package/dist/agents/envelope.js +68 -0
  8. package/dist/agents/finding.d.ts +79 -0
  9. package/dist/agents/finding.js +80 -0
  10. package/dist/agents/task.d.ts +60 -0
  11. package/dist/agents/task.js +32 -0
  12. package/dist/agents/worker.d.ts +68 -0
  13. package/dist/agents/worker.js +256 -0
  14. package/dist/capabilities/capability.d.ts +117 -0
  15. package/dist/capabilities/capability.js +66 -0
  16. package/dist/capabilities/registry.d.ts +139 -0
  17. package/dist/capabilities/registry.js +413 -0
  18. package/dist/capabilities/vocabulary.d.ts +32 -0
  19. package/dist/capabilities/vocabulary.js +34 -0
  20. package/dist/cli/cli.js +55 -4
  21. package/dist/cli/commands/cleanup.js +29 -27
  22. package/dist/cli/commands/doctor.d.ts +14 -0
  23. package/dist/cli/commands/doctor.js +38 -8
  24. package/dist/cli/commands/executions.js +34 -25
  25. package/dist/cli/commands/info.d.ts +1 -0
  26. package/dist/cli/commands/info.js +11 -9
  27. package/dist/cli/commands/init.js +19 -0
  28. package/dist/cli/commands/inspect.d.ts +40 -1
  29. package/dist/cli/commands/inspect.js +157 -2
  30. package/dist/cli/commands/mcp.d.ts +45 -0
  31. package/dist/cli/commands/mcp.js +148 -0
  32. package/dist/cli/commands/route.js +21 -0
  33. package/dist/cli/commands/run.d.ts +1 -0
  34. package/dist/cli/commands/run.js +21 -2
  35. package/dist/cli/commands/skills.d.ts +2 -0
  36. package/dist/cli/commands/skills.js +29 -7
  37. package/dist/cli/interactive/ansi.d.ts +41 -0
  38. package/dist/cli/interactive/ansi.js +43 -0
  39. package/dist/cli/interactive/complete.d.ts +10 -0
  40. package/dist/cli/interactive/complete.js +19 -0
  41. package/dist/cli/interactive/repl.d.ts +3 -0
  42. package/dist/cli/interactive/repl.js +91 -13
  43. package/dist/cli/interactive/session.d.ts +8 -0
  44. package/dist/cli/interactive/session.js +73 -2
  45. package/dist/cli/render.d.ts +7 -0
  46. package/dist/cli/render.js +10 -0
  47. package/dist/cli/runtimeSession.d.ts +11 -0
  48. package/dist/cli/runtimeSession.js +17 -0
  49. package/dist/config/defaults.d.ts +3 -1
  50. package/dist/config/defaults.js +2 -0
  51. package/dist/config/schema.d.ts +1 -0
  52. package/dist/config/schema.js +2 -2
  53. package/dist/context/lossVerifier.d.ts +24 -0
  54. package/dist/context/lossVerifier.js +45 -0
  55. package/dist/context/summarize.d.ts +19 -0
  56. package/dist/context/summarize.js +53 -0
  57. package/dist/core/fallback/fallback.d.ts +5 -0
  58. package/dist/core/fallback/fallback.js +3 -1
  59. package/dist/core/router/router.d.ts +3 -0
  60. package/dist/core/router/router.js +1 -0
  61. package/dist/executions/execution.d.ts +13 -2
  62. package/dist/generation/generateAdapter.d.ts +14 -0
  63. package/dist/generation/generateAdapter.js +38 -0
  64. package/dist/generation/generateSkill.d.ts +26 -0
  65. package/dist/generation/generateSkill.js +51 -0
  66. package/dist/index.d.ts +43 -4
  67. package/dist/index.js +26 -2
  68. package/dist/mcp/client.d.ts +70 -0
  69. package/dist/mcp/client.js +221 -0
  70. package/dist/mcp/manager.d.ts +151 -0
  71. package/dist/mcp/manager.js +493 -0
  72. package/dist/mcp/protocol.d.ts +216 -0
  73. package/dist/mcp/protocol.js +149 -0
  74. package/dist/mcp/toolAdapter.d.ts +44 -0
  75. package/dist/mcp/toolAdapter.js +94 -0
  76. package/dist/mcp/transport.d.ts +109 -0
  77. package/dist/mcp/transport.js +383 -0
  78. package/dist/memory/embedders/hash.d.ts +12 -0
  79. package/dist/memory/embedders/hash.js +31 -0
  80. package/dist/memory/embedders/http.d.ts +25 -0
  81. package/dist/memory/embedders/http.js +48 -0
  82. package/dist/memory/memory.d.ts +19 -2
  83. package/dist/memory/memory.js +75 -11
  84. package/dist/memory/semantic.d.ts +17 -0
  85. package/dist/memory/semantic.js +29 -0
  86. package/dist/orchestration/budget.d.ts +30 -0
  87. package/dist/orchestration/budget.js +40 -0
  88. package/dist/orchestration/executor.d.ts +39 -1
  89. package/dist/orchestration/executor.js +64 -4
  90. package/dist/orchestration/orchestrator.d.ts +29 -1
  91. package/dist/orchestration/orchestrator.js +89 -8
  92. package/dist/orchestration/plan.d.ts +15 -1
  93. package/dist/orchestration/plan.js +23 -4
  94. package/dist/orchestration/planner.d.ts +19 -1
  95. package/dist/orchestration/planner.js +25 -5
  96. package/dist/plugin/ai.d.ts +4 -0
  97. package/dist/plugin/ai.js +9 -0
  98. package/dist/runtime/config.js +50 -6
  99. package/dist/runtime/intent/aiClassifier.d.ts +19 -0
  100. package/dist/runtime/intent/aiClassifier.js +74 -0
  101. package/dist/runtime/models/modelProfile.d.ts +61 -0
  102. package/dist/runtime/models/modelProfile.js +139 -0
  103. package/dist/runtime/planning/deriveCapabilities.d.ts +95 -0
  104. package/dist/runtime/planning/deriveCapabilities.js +146 -0
  105. package/dist/runtime/policy.d.ts +10 -0
  106. package/dist/runtime/policy.js +9 -2
  107. package/dist/runtime/runtime.d.ts +173 -0
  108. package/dist/runtime/runtime.js +705 -50
  109. package/dist/runtime/types.d.ts +88 -2
  110. package/dist/skills/manifest.d.ts +3 -0
  111. package/dist/skills/manifest.js +24 -0
  112. package/dist/skills/registry.d.ts +16 -1
  113. package/dist/skills/registry.js +21 -1
  114. package/dist/skills/skill.d.ts +6 -1
  115. package/dist/store/area.d.ts +15 -1
  116. package/dist/store/area.js +19 -8
  117. package/dist/store/crypto.d.ts +21 -0
  118. package/dist/store/crypto.js +49 -0
  119. package/dist/store/paths.d.ts +5 -1
  120. package/dist/store/paths.js +6 -0
  121. package/dist/store/store.d.ts +15 -3
  122. package/dist/store/store.js +28 -7
  123. package/dist/telemetry/sinks/otlp.d.ts +31 -0
  124. package/dist/telemetry/sinks/otlp.js +76 -0
  125. package/dist/tools/builtins/filesystem.js +1 -0
  126. package/dist/tools/builtins/git.js +1 -0
  127. package/dist/tools/builtins/shell.js +1 -0
  128. package/dist/tools/permissions.d.ts +28 -0
  129. package/dist/tools/permissions.js +72 -0
  130. package/dist/tools/registry.d.ts +18 -2
  131. package/dist/tools/registry.js +22 -2
  132. package/dist/tools/tool.d.ts +4 -0
  133. package/dist/types.d.ts +5 -1
  134. package/dist/util/flatten.d.ts +11 -0
  135. package/dist/util/flatten.js +18 -0
  136. package/dist/util/semaphore.d.ts +19 -0
  137. package/dist/util/semaphore.js +60 -0
  138. package/package.json +24 -9
package/CHANGELOG.md CHANGED
@@ -5,6 +5,567 @@ All notable changes to `ai-runtime` are documented here. The format follows
5
5
  Versioning](https://semver.org/). Development history and rationale live in
6
6
  [docs/DECISIONS.md](docs/DECISIONS.md) and [docs/PROGRESS.md](docs/PROGRESS.md).
7
7
 
8
+ ## [2.7.0] — 2026-09-04
9
+
10
+ Multi-agent core. A plan step can now **delegate to an agent** — a bounded sub-task with its own plan,
11
+ its own narrowed catalog and permissions, its own budget, and a structured report of what it found.
12
+ Additive: with `runtime.agents` absent every prompt, key, observation, and rendered line is
13
+ byte-identical to 2.6.0.
14
+
15
+ ### Added
16
+
17
+ - **Agent execution as a THIRD step kind** (`skill` | `tool` | `agent`), reusing the orchestrator that
18
+ already exists: waves, skip-on-failed-dependency, the concurrency semaphore, and the budget gate are
19
+ all kind-agnostic. There is no second orchestration engine.
20
+ - **`runtime.agents`** — `enabled`, per-task defaults (`maxToolCalls` 25, `maxDurationMs` 120s,
21
+ `maxInnerCalls` 3), and operator-authored `definitions`. A definition is a **ceiling**: it can remove
22
+ a tool but never add one, lower a cap but never raise it, drop a permission but never grant one, and
23
+ add a routing exclusion but never re-admit an excluded provider. `Runtime.registerAgent` does the same
24
+ for a library host.
25
+ - **Findings** — an agent reports `{ type, claim, subject?, verdict? }` with two numbers that are never
26
+ conflated: `executionCoverage` (deterministic — how much of its plan ran) and `confidence`
27
+ (evidence-based belief). Conflict resolution weighs only `confidence`; coverage filters and breaks
28
+ ties. Evidence entries carry the validation checks that back them.
29
+ - **Budget reservations** — an agent step reserves model calls in the pre-flight estimate and in the
30
+ executor's wave gate, through one shared per-step cost rule. A plan whose agents cannot fit refuses to
31
+ start with a truthful estimate instead of overrunning mid-run.
32
+ - **Envelope disclosure at the approval gate** — an agent step's plan line now shows what it may spend,
33
+ what it may touch, and what it is allowed to do, because approving a delegation blind is approving a
34
+ blank cheque.
35
+ - **Lifecycle events** `agent.task.started` / `agent.task.completed`, carrying ids and counts only.
36
+
37
+ ### Guarantees
38
+
39
+ - **One router.** The worker never constructs an `AI`. Inner model calls go through a
40
+ prototype-delegating metered facade over the parent's — which is also what lets it be handed to a
41
+ skill as `SkillContext.ai`, so it counts the calls a skill makes on its own that the parent budget
42
+ cannot see today. An agent's routing may only narrow the parent's.
43
+ - **Bounded, at depth 1, structurally.** The inner plan is generated with an empty agent list, so a
44
+ nested agent step is a validation error rather than a runtime check a refactor can bypass.
45
+ - **One enforcement point.** `narrowEnvelope` derives the inner catalog, the permission clamp, the caps,
46
+ and the reservation, and it has exactly one caller. A skill whose declared tools escape the envelope is
47
+ excluded there, because a skill's own `callTool` resolves off the Runtime registry with no allowlist.
48
+ - **Arbitrary agent output never becomes a Finding.** Candidates pass extract → output-contract validate
49
+ → Finding validate → persist, each with one rejection reason; a rejected candidate is an auditable
50
+ diagnostic and is never threaded into a prompt, an observation, or a result. A finding with no
51
+ evidence cannot claim belief, even with a skill-authored override.
52
+ - **Both prompt boundaries are fenced.** A model-authored step input reaches the inner planner inside an
53
+ untrusted-data fence; every definition- and source-controlled string is flattened and clamped.
54
+ - **No new status anywhere.** Every agent-task state projects onto the existing step and execution
55
+ vocabularies, and the table is encoded in code so it cannot drift from the docs. An interruption is
56
+ metadata on the record, not a state. There is no inner approval wait: a headless denial becomes a
57
+ first-class finding plus a fail-branch.
58
+ - **A cancelled branch is distinguishable from a skipped one**, and `pauseExecution`/`cancelExecution`
59
+ now abort agent work that is actually in flight rather than only flipping a stored status.
60
+
61
+ ### Fixed
62
+
63
+ - The resume-continue path built its own step runners with no agent runner, so a persisted plan
64
+ containing an agent step would have failed every one of those steps. There is now one runner source.
65
+ - `execStatus` is exhaustive: an unrecognized orchestration status was silently persisted as
66
+ `waiting_for_clarification` — resumable, answerable with a text answer, and wrong.
67
+ - The three copies of "a model call is a skill step" (estimate, wave gate, resume recomputation) are one
68
+ primitive. A third step kind with a non-unit cost made three copies untenable.
69
+
70
+ ### Notes
71
+
72
+ - 796 tests pass (was 742); the runtime dependency count is unchanged (4).
73
+ - Persistence, resume, and cancellation of agent tasks are Phase 3.5. 3.4's shapes are chosen so that
74
+ phase needs no breaking change: the task record is already self-identifying and JSON-serializable, and
75
+ the waiting/paused projections are declared but unreachable.
76
+
77
+ ## [2.6.0] — 2026-09-03
78
+
79
+ Capability-first planning and the MCP UX. The runtime can now answer **"what does this goal need, and
80
+ what is missing?"** before it plans — offline, for free — and tell you exactly what to grant. Additive:
81
+ with `runtime.capabilities.planning` off (the default) the planner prompt and every result field are
82
+ byte-identical to 2.5.1.
83
+
84
+ ### Added
85
+
86
+ - **`runtime.capabilities.planning`** (default off) — derives the capabilities a goal needs BEFORE
87
+ planning, resolves them against that run's permissions, and reports the gaps. Two rungs: an **offline
88
+ BM25 pass over the live registry** runs first (free, deterministic, and the common path), and a single
89
+ model call sits under it, consulted only when the offline pass finds nothing. The model's reply is
90
+ filtered against the closed candidate set, so no model-authored string can reach resolution, a prompt,
91
+ or your terminal.
92
+ - **A post-plan capability check** — maps each plan step's real provider to what it actually does and
93
+ resolves that. Zero model calls, and the only path that surfaces a genuine `permission` gap with a
94
+ concrete config key to grant.
95
+ - **The gap UX.** `RuntimeResult.capabilityGaps` has been populated since 2.4.0 but was rendered
96
+ nowhere. `ai-runtime run`, the REPL, and both `capabilities` surfaces now print one shared block:
97
+ ```
98
+ Capabilities this goal needs:
99
+ ✗ commit_changes permission the runtime is not allowed to do this
100
+ ✓ read_file available tool:filesystem
101
+ To fix:
102
+ • grant permissions.gitCommit in .ai-runtime/config.yaml
103
+ ```
104
+ - **`ai-runtime capabilities --actions "<goal>"`** and **`/capabilities <goal>`** — a free, offline
105
+ "can you do this?" pre-flight. No model call and no provider needed: it answers correctly with
106
+ `providers: []` and no network.
107
+ - **REPL `/mcp` and `/mcp/<id>`** — the MCP surface inside the interactive session, reusing the exact
108
+ formatters the `mcp` command prints (and never opening a second Runtime).
109
+ - **A `doctor` MCP section** — servers, state, tool counts, and grants, with any ungranted server named
110
+ explicitly. Read-only: `doctor` never connects, so states are as last recorded.
111
+
112
+ ### Guarantees
113
+
114
+ - **Advisory, never authorization.** Capability planning never blocks a run, adds no orchestration
115
+ status, and grants nothing — the tool's own permission check remains the authority. A gap is metadata
116
+ plus a line in the planner prompt telling the model not to plan around it.
117
+ - **No budget can be exceeded to answer a question about capabilities.** The model rung is skipped
118
+ entirely whenever a call budget is set, and on a dry run; `maxCalls` counts plan steps and cannot
119
+ express a pre-plan call, so declining is the fail-closed reading. Nothing rewrites a resolved policy.
120
+ - **One router.** The model rung goes through the same `AI.run` facade as everything else, on the
121
+ existing `chat` task — the task registry is identical whether the flag is on or off.
122
+ - **The goal is fenced as untrusted** in the derive prompt, and every candidate label is flattened and
123
+ clamped — so an MCP server's description cannot forge a row in it.
124
+ - **Resolution and execution read the same permissions.** A run's gaps are resolved against that run's
125
+ merged permissions (config + per-run override, with `mcp` merged by per-server minimum), through one
126
+ extracted helper, so what gets reported and what the executor will allow can never disagree.
127
+ - `MissingCapabilityReport` now has exactly **one** builder, shared by the public reporter and the run
128
+ path, so the two can no longer drift.
129
+
130
+ ### Review outcomes
131
+
132
+ The phase's adversarial review pass produced 24 findings. Two were refuted by measurement (a claimed
133
+ IDF collapse — a 306-capability registry still derives correctly, because the slate is relevance-ranked
134
+ first; and a claimed flag-off report difference — the divergence needs duplicate suggestions, which plan
135
+ validation cannot produce). The rest are fixed:
136
+
137
+ - **No over-grant advice.** A plan step's provider can do more than the step will (`tool:filesystem`
138
+ provides both `read_file` and `write_file`), and lexical derivation can match a sibling. A
139
+ `permission` gap now survives only when the goal actually derived that capability — telling an
140
+ operator to grant write access for a read is worse than saying nothing. Other gap reasons (unknown /
141
+ unavailable / disabled) are always reported, because those block the step whatever it intends.
142
+ - **The relevance cut is tuned by measurement against the real registry** (0.45 → 0.6): zero false
143
+ positives and zero misses across the goal suite, where 0.45 derived `write_file` for "read a
144
+ workspace file" and 0.65 lost `commit_changes` for "commit the fix and run the tests".
145
+ - **Every budget declines the model rung**, not just a call ceiling: a cost ceiling counts, and so does a
146
+ ceiling set in the config file (which deliberately does not flow into `policy.maxCalls`, because
147
+ threading it there would newly gate orchestration for every existing config).
148
+ - **An idempotent retry pays nothing.** Derivation now happens after the `requestId` dedup, so a retry
149
+ that returns the existing execution no longer spends a model call whose result is discarded.
150
+ - A capability id from an operator `aliases` key is clamped before it reaches the derive prompt (it is
151
+ the one id with no charset or length guarantee); `capabilitiesOf` refuses a sanitization-colliding raw
152
+ id rather than answering with the incumbent provider's capabilities; the MCP grant lookup is
153
+ own-property checked everywhere (a server legally named `constructor` could otherwise inherit a
154
+ grant); `capabilities --actions --json` stays JSON on every path; a goal passed without `--actions` is
155
+ honored rather than discarded; `/mcp <id>` and `/capabilities <goal>` accept both the slash and space
156
+ forms; and `doctor` reports whether servers are live instead of a hardcoded `false`.
157
+
158
+ ### Notes
159
+
160
+ - 741 tests pass (was 692); the runtime dependency count is unchanged (4).
161
+ - A latent test-double defect is fixed: the MCP `hang` fixture settled only via the caller's abort
162
+ signal, and `AbortSignal.timeout` is unref'd — so a process whose only pending work was that promise
163
+ could exit with an awaiting test unresolved, cancelling it and every test after it in the file. Seen
164
+ once on a CI runner, nine milliseconds after the previous test passed; the mechanism is reproducible in
165
+ four lines of Node. The hang path now carries a ref'd backstop and the behavior is pinned.
166
+
167
+ ## [2.5.1] — 2026-09-03
168
+
169
+ Fixes a leak that could hang a test run or a CLI invocation, found by Windows CI. No API changes.
170
+
171
+ ### Fixed
172
+
173
+ - **A stdio child is released even when the code that owns it fails.** An MCP stdio child holds the event
174
+ loop open, so a manager that was not closed — because an assertion or an error unwound past the close —
175
+ left the process unable to exit. This is what stalled Windows CI: a platform-specific assertion failed,
176
+ its manager was never closed, and the run reported nothing until it was cancelled. Every spawn-owning
177
+ path now releases in a `finally`.
178
+ - **A dying child's pipes can no longer raise an uncaught exception.** `stdin`/`stdout`/`stderr` each get
179
+ an error handler, because an EPIPE while writing into a process that just exited (or an ECONNRESET on a
180
+ Windows named pipe) is expected during teardown — unhandled, a stream `error` takes down the host.
181
+ - **Pipe handles are destroyed after the child exits**, so no lingering handle can keep the event loop
182
+ alive on a platform where a terminated process's stdio outlives it.
183
+ - **A flaky crypto test is deterministic** (present since 1.10.0, ~1.75% failure rate): it inspected
184
+ `body[10]` but substituted `body[0]`, so whenever that character already held the replacement it
185
+ tampered nothing and AES-GCM correctly did not throw. It now substitutes at the index it inspects,
186
+ asserts the ciphertext actually changed, and a companion test sweeps **every** position in the
187
+ envelope body.
188
+
189
+ ### Changed
190
+
191
+ - `npm test` (and each battery) now passes `--test-timeout=120000`, and the CI job carries
192
+ `timeout-minutes: 15`. A future hang fails fast with the offending test's name instead of consuming a
193
+ runner's whole time budget.
194
+ - The MCP stdio tests that inspect the process table now **skip on platforms that cannot be probed**
195
+ (the same shape as the existing symlink skip) — they assert POSIX process semantics that
196
+ `StdioTransport` deliberately does not use on Windows. Windows keeps three real spawn tests plus the
197
+ entire protocol, client, HTTP-transport, permission, and fencing suite.
198
+ `AI_RUNTIME_TEST_NO_PROCESS_PROBE=1` exercises the skipping path on a POSIX host.
199
+
200
+ ## [2.5.0] — 2026-09-03
201
+
202
+ MCP connectivity — tools from external **MCP servers** become ordinary Runtime tools. There is no second
203
+ execution path: a discovered MCP tool goes through the same `runTool` permission, approval, cancellation,
204
+ and redaction machinery as `filesystem` or `shell`. Additive: with no `mcp:` config the subsystem is
205
+ entirely inert.
206
+
207
+ ### Added
208
+
209
+ - **Zero-dependency MCP client** (`src/mcp/`) — JSON-RPC 2.0 over **stdio** (newline-framed, own process
210
+ group) or **streamable HTTP** (JSON or SSE bodies, `Mcp-Session-Id` carried, 404 ⇒ re-initialize), with
211
+ version negotiation, per-call timeouts, cooperative cancellation, and concurrent request multiplexing.
212
+ The runtime dependency count is unchanged (4).
213
+ - **`mcp:` config** — declare servers by transport with `command`/`args` or `url`, an optional
214
+ `tokenEnv` (an env-var **NAME**; a key-like value is rejected by the same guard `apiKeyEnv` uses), and
215
+ per-server `timeoutMs`.
216
+ - **`permissions.mcp.servers.<id>`** — `off` | `read` | `full` (`true` ≡ full, `false` ≡ off).
217
+ Deny-by-default: adding or connecting a server grants **nothing**. `read` admits only tools the server
218
+ declares read-only; anything else needs `full`.
219
+ - **`ai-runtime mcp`** — list servers with state, tool count, and grant; `mcp <id>` for detail and
220
+ redacted diagnostics; `mcp add|remove|enable|disable|test`. `mcp add` persists to the local **store**,
221
+ never to your config file.
222
+ - **Action capabilities for MCP tools** — each discovered tool provides `mcp:<server>.<tool>`, with
223
+ availability driven by the server's lifecycle (connected ⇒ available, cache-only ⇒ unknown,
224
+ unreachable ⇒ unavailable, disabled ⇒ disabled).
225
+ - **`Runtime.close()`** — releases MCP child processes. Wired into the one-shot CLI, `run`, and REPL exit,
226
+ with a synchronous process-exit net so a crash cannot orphan a server.
227
+
228
+ ### Guarantees
229
+
230
+ - **MCP metadata is untrusted at every rendering point, not just call output.** Tool names, descriptions,
231
+ and error text pass a discovery-time normalization gate (name charset validated, control characters
232
+ stripped, length clamped) — a server cannot forge a catalog row or smuggle instructions. An unsafe
233
+ declaration is rejected and never registered; call output is `wrapUntrusted`-fenced and redacted; a
234
+ tool error yields a fixed taxonomy with the server's text only as a fenced diagnostic.
235
+ - **Effects come from the live handshake, never a cache.** A cached `readOnlyHint` cannot lower the
236
+ permission gate; a cache-seeded tool is write-like until a live handshake re-registers it.
237
+ - **Permissions only narrow.** `mcp` is the first nested permission dimension: it merges by per-server
238
+ minimum, so a per-run override can revoke a grant but never widen one.
239
+ - **A `destructiveHint` tool always confirms**, even when its server is granted `full`.
240
+ - **An MCP tool never shadows an existing tool** — the collision is skipped and reported.
241
+ - Credentials are named, never valued: no token, URL, or spawn command reaches an error message, status,
242
+ diagnostic, warning, or the persisted store. Status reports the env-var **name** and whether it resolves.
243
+ - All persisted MCP state (servers, enable-state, tool caches) is schema-validated on read; invalid ⇒
244
+ discarded and reported, never trusted.
245
+ - Bounded by construction: 20 `tools/list` pages, 500 tools per server, a 30s default per-call timeout,
246
+ and a capped reconnect backoff. One server failing never affects another.
247
+ - A failed handshake releases its connection, so a stdio child whose `initialize` times out is closed
248
+ rather than left running; and removing one server never strips a sibling whose id shares its prefix.
249
+ - **A server's JSON Schema is prompt text too**, so it is deep-sanitized: every string inside it is
250
+ flattened and clamped, and its depth, node count, and key count are bounded — an unbounded schema
251
+ would otherwise inflate every planning call that declares the tool.
252
+ - **Deny-by-default holds for every key, not just configured ones.** The grant map is prototype-free, so
253
+ a lookup of `constructor` (or any unconfigured server) can only answer "no grant", and a value that is
254
+ not exactly `off`/`read`/`full` collapses to `off`.
255
+ - **A cached annotation can never relax a gate in either direction.** A stale `readOnlyHint` cannot lower
256
+ the permission gate and a stale `destructiveHint: false` cannot remove the confirmation; with no live
257
+ connection both gates fail closed. The adapter reads live annotations at call time, not a snapshot.
258
+ - **Capability resolution honors the MCP grant**, so an ungranted server's tools are a `permission` gap
259
+ instead of being planned against and then refused by the executor.
260
+ - The model-facing declaration path (`AI.run({ mcp: true })`) honors the same read/full split, emits
261
+ already-namespaced names, and is revoked when a server is disabled or removed.
262
+ - A live discovery is authoritative: a tool the server no longer offers is unregistered rather than
263
+ lingering as available. Re-enabling a server restores its tools.
264
+ - Correlation is response-only, so a server-initiated request can no longer resolve a pending call; an
265
+ already-aborted call never reaches the wire; abort listeners are released on completion.
266
+ - A dynamic server id is validated exactly like a config one, `--token-env` refuses a token value, and
267
+ the persisted `servers` file is validated against the same schema as the config file.
268
+ - Every Runtime-based CLI command releases its Runtime on every path (`withRuntime`), so no command can
269
+ leave a live child holding the process open. A missing stdio command fails immediately instead of
270
+ waiting out the request timeout.
271
+
272
+ ### Notes
273
+
274
+ - MCP connects **lazily**: a one-shot CLI command that cannot execute tools pays nothing. The REPL
275
+ connects eagerly, once, at session start.
276
+ - `mcp:` is a Runtime-config key. `AI.load()` (the plain router loader) still rejects it.
277
+ - Public surface: the config/status types, `mcpToolId`, `MCP_PROTOCOL_VERSION`, and
278
+ `clampMcpPermissions`. The client, transports, and manager internals stay private so the wire
279
+ implementation can change without a breaking release.
280
+
281
+ ## [2.4.0] — 2026-09-03
282
+
283
+ Action capabilities — the Runtime now reasons about **what it can do** (`read_file`, `run_tests`,
284
+ `read_repository`), independent of which tool or skill provides it. Additive: with no `capabilities`
285
+ config, planning and every existing surface behave exactly as 2.3.0.
286
+
287
+ ### Added
288
+
289
+ - **Action-capability layer** (`src/capabilities/`) — a first-class capability model distinct from the
290
+ router's model-capability system: a shipped curated vocabulary, per-provider availability + evidence,
291
+ and a fully deterministic 8-step resolution (availability → operator pin → evidence → source → id).
292
+ - **Declarations** — tools, skills, and skill manifests may declare `provides: [{ id, effects }]`;
293
+ anything undeclared still surfaces under its own namespace, so nothing is ever invisible.
294
+ - **`runtime.capabilities`** config — `catalog` (enrich the planner prompt with capabilities and an
295
+ explicit "unavailable, do not use" section), `aliases` (an operator claims a curated id for a
296
+ provider), and `pins` (an operator selects among eligible providers).
297
+ - **Structured capability gaps** — an unknown plan reference now also produces a machine-readable gap
298
+ (`RuntimeResult.capabilityGaps`) with actionable suggestions, instead of only a raw error string.
299
+ - **`ai-runtime capabilities --actions`** and the REPL **`/capabilities`** — see every action capability,
300
+ its providers, effects, availability, and evidence.
301
+
302
+ ### Guarantees
303
+
304
+ - **`effects` is advisory metadata, never authorization.** Actual operation permission is always enforced
305
+ by the selected Tool/Skill execution path.
306
+ - **Capability resolution never grants permission**, and a bare curated id can be claimed only by an
307
+ in-tree builtin or an operator config alias — third-party code is always namespaced to its own source.
308
+ - Source-controlled text is clamped before it reaches a model prompt.
309
+
310
+ ## [2.3.0] — 2026-09-01
311
+
312
+ Release automation — push a tag, and CI publishes. Repo tooling only; nothing in the published package
313
+ changes.
314
+
315
+ ### Added
316
+
317
+ - **`.github/workflows/release.yml`** — push `v<version>` to publish `ai-runtime-engine`, or `pack-v<version>`
318
+ to publish `ai-runtime-developer-skills`. Each release runs typecheck + tests + build and a **version-sync
319
+ guard** (the tag must equal `package.json`, `cli.ts`, and the CHANGELOG — it fails fast otherwise) before
320
+ `npm publish`. A `409 "previously staged"` is treated as done, not a failure. A manual `workflow_dispatch`
321
+ run performs a `--dry-run` to prove the pipeline without releasing.
322
+
323
+ ### Notes
324
+
325
+ - **One-time setup:** add an npm granular access token (publish permission, 2FA-bypass) as the repo secret
326
+ `NPM_TOKEN`. The manual publish flow documented in `docs/handbook/09-npm.md` remains a supported fallback.
327
+
328
+ ## [2.2.0] — 2026-09-01
329
+
330
+ A richer interactive terminal — color, tab-completion, history, and a live spinner. Zero new dependencies,
331
+ and all of it is gated on an interactive TTY, so piped/CI output is unchanged.
332
+
333
+ ### Added
334
+
335
+ - **Colored** banner and prompt (disabled automatically when not a TTY, when `NO_COLOR` is set, or on a
336
+ `dumb` terminal).
337
+ - **Slash-command tab-completion** — press Tab after `/` to complete a command (only bare commands, never
338
+ your natural-language input or command arguments).
339
+ - **Persistent command history** across REPL sessions (best-effort, stored under the project cache dir).
340
+ - An in-place **spinner** while a request is in flight.
341
+
342
+ ### Unchanged
343
+
344
+ - The REPL command surface (`ReplSession`) and all output redaction are exactly as before; nothing about
345
+ scripted/piped usage changes.
346
+
347
+ ## [2.1.0] — 2026-09-01
348
+
349
+ Generative seams — draft a skill from a goal, and let a model fill gaps in a provider adapter. Additive:
350
+ both emit **validated config/manifests, never code**, and the scaffolder **writes nothing without your
351
+ confirmation**.
352
+
353
+ ### Added
354
+
355
+ - **`ai-runtime skills --scaffold "<goal>"`** — a model drafts a `*.skill.yaml` manifest that composes your
356
+ registered tools. It is refused unless it passes shape validation, names only real tools, has a safe
357
+ kebab-case id, and compiles — then it is printed and **saved only after you confirm** (`--yes` to skip the
358
+ prompt) into `.ai-runtime/skills/`. API: `Runtime.scaffoldSkill(goal)` + `saveScaffoldedSkill(manifest)`,
359
+ and `generateSkillManifest`.
360
+ - **`suggestProviderConfig(analysis, ai)`** — an opt-in AI rung over `generateProviderConfig` that proposes a
361
+ model list when an OpenAPI spec doesn't enumerate one. The output is always the deterministic, **validated**
362
+ `ProviderConfig` — a bad suggestion can only propose model-id strings, never an unvalidated config.
363
+
364
+ ### Guarantees
365
+
366
+ - Nothing generated is executed as code — manifests are data parsed by `parseManifest`; adapter output is a
367
+ validated `ProviderConfig`. Goals and specs are fenced as untrusted input.
368
+ - The scaffolder never writes without confirmation, and a scaffolded id can never escape the skills directory.
369
+
370
+ ## [2.0.0] — 2026-09-01
371
+
372
+ **The ecosystem milestone.** A first-party developer skill pack ships alongside the engine. This is **not a
373
+ breaking change** — no engine code changed, the public API is fully backward compatible, and every 1.x
374
+ config keeps working. 2.0 marks the start of the pack ecosystem.
375
+
376
+ ### Added
377
+
378
+ - **`ai-runtime-developer-skills`** (published separately, `v1.0.0`) — a dependency-free developer skill pack
379
+ loaded via the existing `skills: { packages: [...] }` mechanism. Five skills, each fencing untrusted input
380
+ and **validating by evidence**:
381
+ - **`code-review`** — structured findings for a file or files (evidence: bytes read + ≥1 parsed finding).
382
+ - **`refactor`** — proposes a refactor toward a goal and **never writes** (returns `data.proposal`).
383
+ - **`migration-planner`** — an ordered migration plan for a manifest.
384
+ - **`dependency-analyzer`** — enumerates dependencies, cross-checked against the real manifest count.
385
+ - **`debugger`** — hypotheses + concrete next checks for an error.
386
+
387
+ ### Unchanged
388
+
389
+ - The `ai-runtime-engine` public API, config schema, and CLI are all backward compatible with 1.x. The pack
390
+ is **not** bundled into the engine; install it separately.
391
+
392
+ ## [1.11.0] — 2026-09-01
393
+
394
+ Scale & ops — per-tool/skill/provider concurrency caps and an OTLP telemetry exporter. Fully additive and
395
+ opt-in: with no `concurrency` config and no `otlp` sink, behavior is byte-identical to 1.10.
396
+
397
+ ### Added
398
+
399
+ - **`runtime.concurrency`** — cap how much runs at once: `maxParallelSteps` (orchestrator batch bound, a
400
+ config rung under the per-run override), `perTool` / `perSkill` (cap concurrent uses of a given tool/skill
401
+ within a wave), and `perProvider` (cap concurrent calls to a given provider across the router, under
402
+ fan-out). Backed by a small zero-dep `KeyedSemaphore`.
403
+ - **`telemetry.sink: 'otlp'`** (+ `endpoint`, optional `headersEnv`) — an OpenTelemetry-shaped exporter that
404
+ batches the routing/health metadata events and POSTs them to an OTLP/HTTP endpoint. Metadata only, redacted,
405
+ and **all IO is swallowed** so telemetry never fails a run. The auth header value is read from the env var
406
+ **named** by `headersEnv` (never a value in config).
407
+ - New public exports: `KeyedSemaphore` (`util/semaphore.ts`), `OtlpSink`, `FileSink`.
408
+
409
+ ### Guarantees
410
+
411
+ - No `concurrency` config ⇒ scheduling is unchanged; no `otlp` sink ⇒ the sink chain is unchanged.
412
+ - A capped tool/skill/provider still completes every step/call — the cap serializes, it never drops work.
413
+
414
+ ## [1.10.0] — 2026-09-01
415
+
416
+ Encryption at rest — transparently encrypt the local store with a key you control. Fully additive and
417
+ opt-in: with no `storage` config, the store stays plaintext exactly as before.
418
+
419
+ ### Added
420
+
421
+ - **`runtime.storage: { encrypt: true, keyEnv: "<ENV>" }`** — transparently **AES-256-GCM**-encrypts every
422
+ store file (memory, conversations, executions, artifacts, …). The key is read from the env var **named**
423
+ by `keyEnv` (never a value); a missing key with `encrypt:true` is a hard config error at load. Zero new
424
+ dependencies (`node:crypto`).
425
+ - New public exports: `makeCodec`, `deriveKey`, `encryptString`, `decryptString`, `ENVELOPE_PREFIX`,
426
+ `ContentCodec` (`store/crypto.ts`).
427
+
428
+ ### Guarantees
429
+
430
+ - **Mixed store** — records written before encryption still read after you enable it; new writes are
431
+ encrypted. A wrong key surfaces as store corruption (reported by the integrity check), never wrong data.
432
+ - **`meta.json` stays plaintext** so version/migration always works; stateless mode is unaffected.
433
+ - The key lives only in memory — never written to disk, logged, put in telemetry, or included in an error.
434
+ - Not configured ⇒ byte-identical to 1.9.
435
+
436
+ ## [1.9.0] — 2026-09-01
437
+
438
+ Organization memory scope — a shared memory store above user/project/repository, for facts that belong to
439
+ a whole team. Fully additive: with no `organization` configured, memory behaves exactly as 1.8.
440
+
441
+ ### Added
442
+
443
+ - **`runtime.organization: "<name>"`** — enables the new **`organization`** memory scope, a store *above*
444
+ user/project/repository. Its id is **name-derived**, so every project and repo that configures the same
445
+ organization name shares one org store; different names never share, and project/repo isolation is
446
+ unchanged. `remember({ scope: 'organization' })`, search, delete-cascade, conflict-supersession, temporal
447
+ purge, and the integrity check all span the org area **when configured**.
448
+ - New public export **`organizationId`** (`store/paths.ts`); `info` / `doctor` show the org store path when
449
+ configured.
450
+
451
+ ### Guarantees
452
+
453
+ - **Not configured ⇒ nothing changes** — the org area is never traversed, and behavior is byte-identical to 1.8.
454
+ - `remember({ scope: 'organization' })` with **no org configured throws a clear error** rather than silently
455
+ writing the fact to the project store.
456
+
457
+ ## [1.8.0] — 2026-09-01
458
+
459
+ Smarter context compiler — optional model-verified loss checking and abstractive summarization. Fully
460
+ additive and opt-in: with both flags unset, context compilation is byte-identical to 1.7.
461
+
462
+ ### Added
463
+
464
+ - **`runtime.context.verifyLoss: true`** — after the deterministic compile, a model checks whether anything
465
+ task-critical was dropped and **appends** a `model-loss-verify` check to the context validation. It
466
+ **never blocks**: any budget / parse / model failure records an `inconclusive` (ok) check and leaves the
467
+ deterministic validation result untouched. New public export **`verifyContextLoss`**.
468
+ - **`runtime.context.summarize: true`** — before compiling, over-budget **non-critical** blocks are
469
+ abstractively shortened by one model call each. `critical` blocks are never summarized, originals are
470
+ never mutated, a summary is used only if it actually shrinks the block, and any failure falls back to the
471
+ original (deterministic truncation then applies). New public export **`summarizeOverBudget`**.
472
+
473
+ ### Guarantees
474
+
475
+ - Neither feature runs on a **dry run** (a dry run still makes zero model calls).
476
+ - Both `wrapUntrusted`-fence the block/context text before it enters a prompt.
477
+ - The loss verifier compares against the **true pre-summary originals**, so it catches loss from
478
+ summarization as well as from compilation.
479
+
480
+ ## [1.7.0] — 2026-09-01
481
+
482
+ AI-assisted intent classification — a model picks the mode when the offline heuristic is unsure. Fully
483
+ additive and opt-in: with `intent.aiFallback` unset, mode detection is byte-identical to 1.6.
484
+
485
+ ### Added
486
+
487
+ - **`runtime.intent.aiFallback: true`** — when the deterministic heuristic classifier is *ambiguous*, the
488
+ Runtime consults **one** model call to choose the mode. A **confident** heuristic result pays **zero**
489
+ model cost (it is returned verbatim). Any model failure, unparseable output, or out-of-range mode degrades
490
+ back to the heuristic — the enhancement never breaks a run.
491
+ - New public export **`AIIntentClassifier`** (`runtime/intent/aiClassifier.ts`) — implements the existing
492
+ `IntentClassifier` seam; usable directly via `RuntimeOptions.classifier` too.
493
+
494
+ ### Guarantees
495
+
496
+ - **Precedence intact** — an explicit / config / env mode never reaches the classifier.
497
+ - **Security** — the user's request is `wrapUntrusted`-fenced before it enters the classifier prompt (a
498
+ request is data to classify, never instructions), and the chosen mode is validated against the known modes.
499
+
500
+ ## [1.6.0] — 2026-09-01
501
+
502
+ Semantic memory retrieval — rank durable facts by meaning, not just keywords. Fully additive: with no
503
+ `embedding` config, memory retrieval is byte-identical to 1.5 (BM25 keyword ranking).
504
+
505
+ ### Added
506
+
507
+ - **`runtime.embedding`** config — opt in to semantic retrieval: `{ provider: 'local' }` uses the zero-dep,
508
+ deterministic **`HashEmbedder`** (offline, no key); `{ provider: 'openai-compatible', baseUrl, apiKeyEnv,
509
+ model? }` calls a `POST {baseUrl}/embeddings` endpoint via **`HttpEmbedder`** (key by **env NAME** only,
510
+ never a value; the request URL is kept out of every error). Absent → BM25 exactly as before.
511
+ - **`MemoryStore.searchSemantic(query, opts)`** — the async vector path: the *same* filter stage as
512
+ `search()` (scope / confidence / temporal / supersession / domain), then **cosine-similarity** ranking
513
+ over embeddings. `Runtime` chat retrieval and the REPL **`/memory search`** use it automatically when an
514
+ embedder is configured. Candidate vectors are cached per fact text (per instance).
515
+ - New public exports: `cosineSimilarity`, `semanticRank` (`memory/semantic.ts`), `HashEmbedder`,
516
+ `HttpEmbedder`.
517
+
518
+ ### Guarantees
519
+
520
+ - **`search()` is unchanged** — still synchronous BM25, the frozen public API since 1.0.
521
+ - **Retrieval never breaks a run.** Any embedder problem — network failure, malformed response, an empty or
522
+ non-finite vector — **degrades to BM25**, never an error and never a silently-empty result.
523
+
524
+ ## [1.5.0] — 2026-09-01
525
+
526
+ Model routing profiles — route your own way with a `models.md` file. Additive: with no file, routing is
527
+ identical to 1.4.
528
+
529
+ ### Added
530
+
531
+ - **`models.md`** (workspace root or `.ai-runtime/models.md`) — a friendly per-mode / per-task mapping of
532
+ which model to use, resolved through the router's existing controls (no second router). Sections
533
+ `## By mode` and `## By task`; each entry is a **soft prefer** (default), a **hard pin** (`=provider:model`
534
+ or `pin:`), or a **strategy** keyword (`best`/`fastest`/`cheapest`/`highest-quality`/`local-only`/… or
535
+ `free` → cheapest). Example: *chat → free, plan → a strong model, execute → model X, a task → a specific
536
+ model.* Precedence: **explicit per-run > `models.md` > config > defaults**; a `## By task` entry wins over
537
+ `## By mode`; a prefer never re-admits an excluded model.
538
+ - **`ai-runtime models --profile`** prints the resolved profile; **`init`** scaffolds a commented
539
+ `models.md`; `runtime.modelProfile()` exposes it. `## By mode` steers Runtime modes; `## By task` steers
540
+ router tasks via `ai-runtime route <task>` (and any `AI.run({ task })` caller).
541
+
542
+ **Scope:** in chat mode and for route tasks, all three directive kinds apply; orchestration modes
543
+ (plan/execute/orchestrate/agent/debug) honor the **soft prefer** (a per-mode hard pin/strategy is not
544
+ applied there — the planner and skills pick their own models).
545
+
546
+ ## [1.4.0] — 2026-09-01
547
+
548
+ Budget-aware phased execution. When a task would need more model calls than your budget allows, it no
549
+ longer fails partway — it tells you, and lets you run it in phases. Additive: with no call budget set,
550
+ behavior is identical to 1.3.
551
+
552
+ ### Added
553
+
554
+ - **Budget-aware orchestration** — set a call budget with `AI_MAX_CALLS` (or per-run `maxCalls`). When a
555
+ plan's estimated model calls exceed it, the run **notifies instead of failing**: it reports the estimate
556
+ and waits, executing nothing, so you can either raise the budget and re-run, or run what fits with
557
+ `--partial`. A `--partial` run executes the phases that fit the budget, checkpoints, and **pauses
558
+ resumably** (`waiting_for_budget`, a first-class non-failure) — raise `AI_MAX_CALLS` and
559
+ `resume-execution <id>` to continue from where it stopped. A phase is a plan wave; a skill step counts as
560
+ one model call, tool steps are free.
561
+ - **`run --partial`**, the REPL **`/budget`** command (shows `AI_MAX_CALLS` / `AI_MAX_COST_USD`), and an
562
+ over-budget resume hint.
563
+
564
+ ### Fixed
565
+
566
+ - An **approval-gated** over-budget plan now respects the call budget on resume (it previously ran to
567
+ completion, ignoring the budget, because the approval gate was reached before the budget check).
568
+
8
569
  ## [1.3.0] — 2026-09-01
9
570
 
10
571
  Token-by-token streaming. Additive and backward compatible — every new field is optional and, with
@@ -130,6 +691,23 @@ Initial release: the provider-agnostic AI **router** — capability-based routin
130
691
  scoring, evidence validation, fallback, health tracking, learning-based scoring, multi-model verification,
131
692
  budgets, MCP tools, OpenAPI-based adapter generation, and the `AI` class + CLI.
132
693
 
694
+ [2.7.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.7.0
695
+ [2.6.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.6.0
696
+ [2.5.1]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.5.1
697
+ [2.5.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.5.0
698
+ [2.4.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.4.0
699
+ [2.3.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.3.0
700
+ [2.2.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.2.0
701
+ [2.1.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.1.0
702
+ [2.0.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v2.0.0
703
+ [1.11.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.11.0
704
+ [1.10.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.10.0
705
+ [1.9.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.9.0
706
+ [1.8.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.8.0
707
+ [1.7.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.7.0
708
+ [1.6.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.6.0
709
+ [1.5.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.5.0
710
+ [1.4.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.4.0
133
711
  [1.3.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.3.0
134
712
  [1.2.0]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.2.0
135
713
  [1.1.1]: https://github.com/pavankhandelwal21/ai-runtime/releases/tag/v1.1.1