agent-cli-runtime 0.1.0-alpha.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 (151) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/CONTRIBUTING.md +60 -0
  3. package/LICENSE +202 -0
  4. package/README.md +573 -0
  5. package/README.zh-CN.md +571 -0
  6. package/SECURITY.md +35 -0
  7. package/dist/adapters/adapter-types.d.ts +138 -0
  8. package/dist/adapters/adapter-types.js +2 -0
  9. package/dist/adapters/adapter-types.js.map +1 -0
  10. package/dist/adapters/claude.d.ts +2 -0
  11. package/dist/adapters/claude.js +97 -0
  12. package/dist/adapters/claude.js.map +1 -0
  13. package/dist/adapters/codex.d.ts +3 -0
  14. package/dist/adapters/codex.js +120 -0
  15. package/dist/adapters/codex.js.map +1 -0
  16. package/dist/adapters/opencode.d.ts +4 -0
  17. package/dist/adapters/opencode.js +111 -0
  18. package/dist/adapters/opencode.js.map +1 -0
  19. package/dist/adapters/registry.d.ts +9 -0
  20. package/dist/adapters/registry.js +23 -0
  21. package/dist/adapters/registry.js.map +1 -0
  22. package/dist/cli/main.d.ts +2 -0
  23. package/dist/cli/main.js +978 -0
  24. package/dist/cli/main.js.map +1 -0
  25. package/dist/core/async-queue.d.ts +10 -0
  26. package/dist/core/async-queue.js +49 -0
  27. package/dist/core/async-queue.js.map +1 -0
  28. package/dist/core/diagnostics.d.ts +20 -0
  29. package/dist/core/diagnostics.js +4 -0
  30. package/dist/core/diagnostics.js.map +1 -0
  31. package/dist/core/event-contract.d.ts +32 -0
  32. package/dist/core/event-contract.js +128 -0
  33. package/dist/core/event-contract.js.map +1 -0
  34. package/dist/core/events.d.ts +147 -0
  35. package/dist/core/events.js +4 -0
  36. package/dist/core/events.js.map +1 -0
  37. package/dist/core/ids.d.ts +1 -0
  38. package/dist/core/ids.js +5 -0
  39. package/dist/core/ids.js.map +1 -0
  40. package/dist/core/redaction.d.ts +4 -0
  41. package/dist/core/redaction.js +51 -0
  42. package/dist/core/redaction.js.map +1 -0
  43. package/dist/core/runtime.d.ts +41 -0
  44. package/dist/core/runtime.js +83 -0
  45. package/dist/core/runtime.js.map +1 -0
  46. package/dist/core/schema-contract.d.ts +55 -0
  47. package/dist/core/schema-contract.js +143 -0
  48. package/dist/core/schema-contract.js.map +1 -0
  49. package/dist/detection/detect.d.ts +14 -0
  50. package/dist/detection/detect.js +293 -0
  51. package/dist/detection/detect.js.map +1 -0
  52. package/dist/detection/env.d.ts +2 -0
  53. package/dist/detection/env.js +15 -0
  54. package/dist/detection/env.js.map +1 -0
  55. package/dist/detection/executable-resolution.d.ts +12 -0
  56. package/dist/detection/executable-resolution.js +50 -0
  57. package/dist/detection/executable-resolution.js.map +1 -0
  58. package/dist/detection/invocation.d.ts +9 -0
  59. package/dist/detection/invocation.js +22 -0
  60. package/dist/detection/invocation.js.map +1 -0
  61. package/dist/goals/goal-scheduler.d.ts +31 -0
  62. package/dist/goals/goal-scheduler.js +518 -0
  63. package/dist/goals/goal-scheduler.js.map +1 -0
  64. package/dist/goals/goal-store.d.ts +37 -0
  65. package/dist/goals/goal-store.js +300 -0
  66. package/dist/goals/goal-store.js.map +1 -0
  67. package/dist/goals/goal-types.d.ts +103 -0
  68. package/dist/goals/goal-types.js +2 -0
  69. package/dist/goals/goal-types.js.map +1 -0
  70. package/dist/goals/planner-prompts.d.ts +3 -0
  71. package/dist/goals/planner-prompts.js +26 -0
  72. package/dist/goals/planner-prompts.js.map +1 -0
  73. package/dist/goals/task-graph.d.ts +9 -0
  74. package/dist/goals/task-graph.js +229 -0
  75. package/dist/goals/task-graph.js.map +1 -0
  76. package/dist/goals/validation-runner.d.ts +7 -0
  77. package/dist/goals/validation-runner.js +63 -0
  78. package/dist/goals/validation-runner.js.map +1 -0
  79. package/dist/index.d.ts +11 -0
  80. package/dist/index.js +2 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/parsers/claude-stream-json.d.ts +11 -0
  83. package/dist/parsers/claude-stream-json.js +102 -0
  84. package/dist/parsers/claude-stream-json.js.map +1 -0
  85. package/dist/parsers/codex-json.d.ts +8 -0
  86. package/dist/parsers/codex-json.js +107 -0
  87. package/dist/parsers/codex-json.js.map +1 -0
  88. package/dist/parsers/line-buffer.d.ts +7 -0
  89. package/dist/parsers/line-buffer.js +28 -0
  90. package/dist/parsers/line-buffer.js.map +1 -0
  91. package/dist/parsers/opencode-json.d.ts +8 -0
  92. package/dist/parsers/opencode-json.js +72 -0
  93. package/dist/parsers/opencode-json.js.map +1 -0
  94. package/dist/parsers/plain-lines.d.ts +6 -0
  95. package/dist/parsers/plain-lines.js +9 -0
  96. package/dist/parsers/plain-lines.js.map +1 -0
  97. package/dist/public-types.d.ts +143 -0
  98. package/dist/public-types.js +2 -0
  99. package/dist/public-types.js.map +1 -0
  100. package/dist/runs/process-runner.d.ts +35 -0
  101. package/dist/runs/process-runner.js +97 -0
  102. package/dist/runs/process-runner.js.map +1 -0
  103. package/dist/runs/prompt-transport.d.ts +10 -0
  104. package/dist/runs/prompt-transport.js +43 -0
  105. package/dist/runs/prompt-transport.js.map +1 -0
  106. package/dist/runs/run-result.d.ts +9 -0
  107. package/dist/runs/run-result.js +22 -0
  108. package/dist/runs/run-result.js.map +1 -0
  109. package/dist/runs/run-scheduler.d.ts +25 -0
  110. package/dist/runs/run-scheduler.js +552 -0
  111. package/dist/runs/run-scheduler.js.map +1 -0
  112. package/dist/runs/run-store.d.ts +42 -0
  113. package/dist/runs/run-store.js +297 -0
  114. package/dist/runs/run-store.js.map +1 -0
  115. package/dist/runs/run-types.d.ts +59 -0
  116. package/dist/runs/run-types.js +2 -0
  117. package/dist/runs/run-types.js.map +1 -0
  118. package/dist/smoke/parser-samples.d.ts +17 -0
  119. package/dist/smoke/parser-samples.js +186 -0
  120. package/dist/smoke/parser-samples.js.map +1 -0
  121. package/dist/storage/file-storage.d.ts +35 -0
  122. package/dist/storage/file-storage.js +271 -0
  123. package/dist/storage/file-storage.js.map +1 -0
  124. package/dist/storage/jsonl-store.d.ts +9 -0
  125. package/dist/storage/jsonl-store.js +138 -0
  126. package/dist/storage/jsonl-store.js.map +1 -0
  127. package/dist/storage/manifest-validation.d.ts +11 -0
  128. package/dist/storage/manifest-validation.js +102 -0
  129. package/dist/storage/manifest-validation.js.map +1 -0
  130. package/dist/storage/storage-lease.d.ts +40 -0
  131. package/dist/storage/storage-lease.js +223 -0
  132. package/dist/storage/storage-lease.js.map +1 -0
  133. package/dist/storage/storage-types.d.ts +55 -0
  134. package/dist/storage/storage-types.js +2 -0
  135. package/dist/storage/storage-types.js.map +1 -0
  136. package/dist/storage/store-inspection.d.ts +28 -0
  137. package/dist/storage/store-inspection.js +941 -0
  138. package/dist/storage/store-inspection.js.map +1 -0
  139. package/docs/api-schema-contract.md +92 -0
  140. package/docs/compatibility.md +832 -0
  141. package/docs/daemon-ready-contract.md +283 -0
  142. package/docs/production-readiness.md +281 -0
  143. package/docs/release-checklist.md +257 -0
  144. package/docs/release-publish-runbook.md +201 -0
  145. package/docs/release-report.md +517 -0
  146. package/docs/ssot.md +1257 -0
  147. package/examples/cli-dogfood.md +113 -0
  148. package/examples/library-goal.js +94 -0
  149. package/examples/library-run.js +84 -0
  150. package/package.json +79 -0
  151. package/scripts/dogfood.mjs +243 -0
package/README.md ADDED
@@ -0,0 +1,573 @@
1
+ # Agent CLI Runtime
2
+
3
+ > A tiny local-first runtime for driving Codex CLI, Claude Code, OpenCode, and other coding-agent CLIs through one typed API.
4
+
5
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
6
+ [![Status: pre-alpha](https://img.shields.io/badge/status-pre--alpha-orange.svg)](#status)
7
+
8
+ [English](./README.md) | [简体中文](./README.zh-CN.md)
9
+
10
+ Agent CLI Runtime is the adapter layer you reach for when you do **not** want to build another coding agent.
11
+
12
+ Modern local coding agents already know how to plan, edit files, run tools, ask for permission, manage sessions, and talk to models. This project keeps those loops inside the user's installed CLI and gives product builders a small, dependable runtime around them:
13
+
14
+ - detect installed local coding agents;
15
+ - launch them in a chosen `cwd`;
16
+ - pass prompts through safe transports such as stdin;
17
+ - normalize streaming output into one event protocol;
18
+ - cancel, time out, diagnose, and classify runs;
19
+ - keep permissions and extra readable directories explicit.
20
+
21
+ ## Status
22
+
23
+ This repository is in **pre-alpha / developer preview**.
24
+
25
+ Release boundary:
26
+ - This is a P3-7 API / CLI schema freeze track, not a stable-contract or npm publication record.
27
+ - P3-11 keeps current-head release-candidate evidence outside the npm package under `.release-evidence/`; packaged docs only describe the stable workflow and human-gated alpha publish boundary.
28
+ - `createAgentRuntime` is the only runtime value export.
29
+ - No background daemon, no API server, no WAL, no database, and no remote runtime mode are included in this pre-alpha track.
30
+ - The package is intended as a local-first execution kernel for embedding in a daemon or product shell, not as a hosted control plane.
31
+
32
+ The API and CLI schema contract is [docs/api-schema-contract.md](./docs/api-schema-contract.md), the daemon-ready embedding contract is [docs/daemon-ready-contract.md](./docs/daemon-ready-contract.md), the SSOT is [docs/ssot.md](./docs/ssot.md), the release-candidate evidence entrypoint is [docs/release-report.md](./docs/release-report.md), and the future alpha publish runbook is [docs/release-publish-runbook.md](./docs/release-publish-runbook.md). The current implementation is a contract-hardening library-first Node.js/TypeScript implementation with memory-only default run and goal scheduling, optional durable local replay storage with crash/recovery health reporting, fault-injected consistency coverage, package-root API contract tests, tarball TypeScript consumer smoke, installed-package daemon embedding verification, compatibility profiles for the built-in CLIs, hardened planner/task-graph validation, versioned event/diagnostics/conformance/real-smoke/store/release-artifact contracts, redacted diagnostics, parser fixtures, local/remote release artifact verification, remote CI/artifact audit checks, alpha publish readiness docs, and thin local smoke/query CLI commands.
33
+
34
+ ## Why
35
+
36
+ Every serious coding-agent product eventually needs the same boring, sharp-edged runtime work:
37
+
38
+ | Problem | Runtime responsibility |
39
+ | --- | --- |
40
+ | Users have different CLIs installed | Detect Codex CLI, Claude Code, OpenCode, and future adapters |
41
+ | Each CLI has different flags | Hide argv construction behind adapter definitions |
42
+ | Long prompts break argv limits | Prefer stdin or prompt files by default |
43
+ | Streams have different schemas | Parse per-agent output into one `AgentEvent` stream |
44
+ | Headless runs can hang | Provide cancellation, timeout, inactivity, and exit classification |
45
+ | Permissions are easy to overgrant | Make `cwd`, `extraAllowedDirs`, and `permissionPolicy` explicit |
46
+
47
+ The goal is to make this layer boring enough that excellent tools can build on it.
48
+
49
+ ## What This Is Not
50
+
51
+ Agent CLI Runtime is not:
52
+
53
+ - an LLM provider router;
54
+ - a hosted cloud agent;
55
+ - a replacement for Codex CLI, Claude Code, or OpenCode;
56
+ - a web UI;
57
+ - a plugin marketplace;
58
+ - a custom `Read` / `Write` / `Edit` tool loop;
59
+ - a permission bypass wrapper.
60
+
61
+ The runtime delegates the agent loop. It normalizes execution, not intelligence.
62
+
63
+ ## API
64
+
65
+ ```ts
66
+ import { createAgentRuntime } from "agent-cli-runtime";
67
+
68
+ const runtime = createAgentRuntime();
69
+
70
+ const agents = await runtime.detect({
71
+ includeUnavailable: true,
72
+ });
73
+
74
+ const run = await runtime.run({
75
+ agentId: "codex",
76
+ cwd: "/path/to/project",
77
+ prompt: "Add a focused regression test for the failing parser case.",
78
+ permissionPolicy: "workspace-write",
79
+ });
80
+
81
+ for await (const event of run.events) {
82
+ if (event.type === "text_delta") process.stdout.write(event.text);
83
+ if (event.type === "tool_call") console.log("tool", event.name);
84
+ if (event.type === "error") console.error(event.code, event.message);
85
+ }
86
+ ```
87
+
88
+ Goals add a planner run before task execution:
89
+
90
+ ```ts
91
+ const goal = await runtime.createGoal({
92
+ cwd: "/path/to/project",
93
+ objective: "Implement a focused parser regression fix.",
94
+ defaultAgentId: "codex",
95
+ permissionPolicy: "workspace-write",
96
+ maxConcurrentTasks: 2,
97
+ retryPolicy: {
98
+ maxAttempts: 2,
99
+ retryableErrorCodes: ["AGENT_TIMEOUT", "AGENT_EXECUTION_FAILED"],
100
+ backoffMs: 500,
101
+ },
102
+ });
103
+
104
+ for await (const event of goal.events) {
105
+ if (event.type === "task_attempt_started") console.log(event.taskId, event.attemptId, event.runId);
106
+ if (event.type === "goal_finished") console.log(event.result);
107
+ }
108
+ ```
109
+
110
+ Goal scheduling uses a dependency-aware ready queue. A task can start only after all dependencies have succeeded. The conservative default is `maxConcurrentTasks: 1`, preserving serial dependency-order execution; set `maxConcurrentTasks` on `createGoal()` or `createAgentRuntime()` to allow independent ready tasks to run in parallel. `retryPolicy` defaults to `{ maxAttempts: 1 }`; only failures whose terminal error code is listed in `retryableErrorCodes` are retried. Cancellation and validation failures are not retried unless the caller explicitly includes their error code.
111
+
112
+ Planner output is validated before any task starts. The preferred planner response is strict JSON, but the runtime can extract one JSON object from a Markdown fenced block or short surrounding prose. Multiple JSON objects, malformed JSON, missing `tasks`, or invalid field types fail planning with `AGENT_TASK_GRAPH_INVALID` as a `scheduler_error` and the goal finishes as `failed`; they are not reported as task failures or adapter unavailability. Diagnostics are concise and do not echo oversized planner output.
113
+
114
+ Task graph schema:
115
+
116
+ ```json
117
+ {
118
+ "tasks": [
119
+ {
120
+ "id": "T001",
121
+ "title": "Short title",
122
+ "objective": "Self-contained task objective",
123
+ "dependencies": [],
124
+ "allowedFiles": ["src/example.ts"],
125
+ "validationCommands": ["npm test"],
126
+ "agentId": "codex",
127
+ "retryPolicy": {
128
+ "maxAttempts": 2,
129
+ "retryableErrorCodes": ["AGENT_TIMEOUT"],
130
+ "backoffMs": 250
131
+ }
132
+ }
133
+ ]
134
+ }
135
+ ```
136
+
137
+ `id`, `title`, `objective`, and every `dependencies` item must be strings. `dependencies`, `allowedFiles`, `validationCommands`, and `retryPolicy.retryableErrorCodes` must be string arrays when present. `agentId` must be a string when present. A task-level `retryPolicy` must include a positive integer `maxAttempts`, a string-array `retryableErrorCodes`, and a non-negative numeric `backoffMs`.
138
+
139
+ Task evidence records every attempt:
140
+
141
+ ```json
142
+ {
143
+ "runId": "run_latest",
144
+ "result": "success",
145
+ "attempts": [
146
+ {
147
+ "attemptId": "T001:attempt:1",
148
+ "runId": "run_1",
149
+ "startedAt": 1760000000000,
150
+ "finishedAt": 1760000001200,
151
+ "result": "failed",
152
+ "diagnostics": [{ "code": "AGENT_EXECUTION_FAILED", "message": "..." }]
153
+ },
154
+ {
155
+ "attemptId": "T001:attempt:2",
156
+ "runId": "run_2",
157
+ "startedAt": 1760000001800,
158
+ "finishedAt": 1760000002600,
159
+ "result": "success",
160
+ "diagnostics": []
161
+ }
162
+ ],
163
+ "validationCommands": [],
164
+ "summary": "Task T001 finished with success after 2 attempts."
165
+ }
166
+ ```
167
+
168
+ Persistence is opt-in. Without `storageDir`, runs and goals stay memory-only. With `storageDir`, manifests and replay events are written as auditable JSON files:
169
+
170
+ ```ts
171
+ const runtime = createAgentRuntime({
172
+ storageDir: ".agent-runtime",
173
+ storage: { durability: "fsync" }, // optional; default is "relaxed"
174
+ });
175
+
176
+ const runs = await runtime.listRuns({ status: "active" });
177
+ const runEvents = await runtime.replayRunEvents("run_123", { afterEventId: 10 });
178
+ const goals = await runtime.listGoals();
179
+ const goalEvents = await runtime.replayGoalEvents("goal_123");
180
+ ```
181
+
182
+ The public facade exposes:
183
+
184
+ - `createAgentRuntime(options?)`
185
+ - `runtime.detect(options?)`
186
+ - `runtime.detectStream(options?)`
187
+ - `runtime.run(request)`
188
+ - `runtime.createGoal(request)`
189
+ - `runtime.cancelRun(runId)`
190
+ - `runtime.cancelGoal(goalId)`
191
+ - `runtime.shutdown(reason?)`
192
+ - `runtime.getRun(runId)`
193
+ - `runtime.replayRunEvents(runId, { afterEventId? })`
194
+ - `runtime.listRuns({ status? })`
195
+ - `runtime.getGoal(goalId)`
196
+ - `runtime.replayGoalEvents(goalId, { afterEventId? })`
197
+ - `runtime.listGoals({ status? })`
198
+ - `runtime.inspectStore({ storageDir? })`
199
+ - `runtime.exportDiagnostics({ kind: "run", runId, storageDir? })`
200
+ - `runtime.exportDiagnostics({ kind: "goal", goalId, storageDir? })`
201
+ - `runtime.getAdapter(id)`
202
+
203
+ ### API Contract Boundary
204
+
205
+ For the pre-alpha release, the package root is intentionally small. It exports the `createAgentRuntime()` value plus the public TypeScript types needed to call it and consume its records:
206
+
207
+ - stable MVP surface: `AgentRuntime`, `RuntimeOptions`, `DetectOptions`, `DetectedAgent`, `RunRequest`, `RunHandle`, `RunRecord`, `RunStatus`, `CreateGoalRequest`, `GoalHandle`, `GoalRecord`, `GoalStatus`, `AgentEvent`, `SchedulerEvent`, `ReplayEvent`, `VersionedEventEnvelope`, `EventScope`, `EventTerminalContract`, `EventTerminalReason`, `RuntimeDiagnostic`, and `RuntimeErrorCode`;
208
+ - experimental extension surface: adapter-authoring types such as `AgentAdapterDef`, `BuildArgsInput`, `PromptTransport`, `StreamParser`, and `AdapterCompatibilityProfile`;
209
+ - not exported from the package root: built-in adapter values, parser helpers, executable-resolution helpers, stores, schedulers, and task-graph helpers.
210
+
211
+ The published tarball may contain internal `dist/` files because TypeScript declarations and the CLI need them, but only the package root import (`import { createAgentRuntime } from "agent-cli-runtime"`) is a documented API boundary.
212
+
213
+ `getAdapter(id)` and `RuntimeOptions.adapters` exist for adapter experimentation in pre-alpha. Treat them as extension points whose shape may still change before a stable release.
214
+
215
+ ## API Stability (pre-alpha / developer preview)
216
+
217
+ This release candidate is explicitly scoped:
218
+
219
+ - No stable API contract is guaranteed yet.
220
+ - Internal adapters/parsers/helpers are intentionally not exported from package root.
221
+ - No production promises around daemon APIs, WAL-backed storage, remote runtime mode, or distributed storage.
222
+ - CLI JSON schemas and failure taxonomy follow the pre-alpha versioning policy in [docs/api-schema-contract.md](./docs/api-schema-contract.md).
223
+
224
+ ## Installation
225
+
226
+ Install from npm:
227
+
228
+ ```bash
229
+ npm install agent-cli-runtime
230
+ ```
231
+
232
+ Use the CLI through `npx` without adding it to a project:
233
+
234
+ ```bash
235
+ npx --package agent-cli-runtime agent-runtime agents --json
236
+ npx --package agent-cli-runtime agent-runtime conformance --mode fixtures --json
237
+ ```
238
+
239
+ Use a local checkout:
240
+
241
+ ```bash
242
+ npm ci
243
+ npm run build
244
+ node ./dist/cli/main.js --help
245
+ npm run daemon:verify
246
+ npm run dogfood
247
+ ```
248
+
249
+ Quick library smoke after installation:
250
+
251
+ ```bash
252
+ node -e "import('agent-cli-runtime').then((m) => console.log(typeof m.createAgentRuntime))"
253
+ ```
254
+
255
+ Minimal TypeScript consumer:
256
+
257
+ ```ts
258
+ import {
259
+ createAgentRuntime,
260
+ type CreateGoalRequest,
261
+ type RunRequest,
262
+ } from "agent-cli-runtime";
263
+
264
+ const runtime = createAgentRuntime({ storageDir: "./.agent-runtime" });
265
+
266
+ const runRequest: RunRequest = {
267
+ agentId: "codex",
268
+ cwd: process.cwd(),
269
+ prompt: "Reply with a one-line status.",
270
+ };
271
+
272
+ const goalRequest: CreateGoalRequest = {
273
+ defaultAgentId: "codex",
274
+ cwd: process.cwd(),
275
+ objective: "Summarize this repository.",
276
+ };
277
+
278
+ void runRequest;
279
+ void goalRequest;
280
+ void runtime.shutdown();
281
+ ```
282
+
283
+ The daemon embedding gate installs the packed tarball into a temporary consumer, then executes fake-CLI detect/conformance, run, goal, replay, diagnostics, store inspection, shutdown, and reopen checks. The runtime safety gate uses the same installed-package boundary for repeated run/goal execution, slow event consumption, cancel/timeout churn, repeated shutdown, lease close, and reopen checks:
284
+
285
+ ```bash
286
+ npm run daemon:verify
287
+ npm run runtime:safety
288
+ ```
289
+
290
+ The broader release gate installs the packed tarball into a temporary TypeScript project, runs `tsc --noEmit`, and then executes fake-CLI library run/goal/replay/diagnostics smoke. See `npm run daemon:verify`, `npm run runtime:safety`, `npm run dogfood`, and [docs/release-checklist.md](./docs/release-checklist.md).
291
+
292
+ Required local agent CLIs are optional by scenario:
293
+
294
+ - `codex` for Codex CLI coverage.
295
+ - `claude` for Claude Code coverage.
296
+ - `opencode` / `opencode-cli` for OpenCode coverage.
297
+
298
+ Executable overrides:
299
+
300
+ ```bash
301
+ export CODEX_BIN=/absolute/path/to/codex
302
+ export CLAUDE_BIN=/absolute/path/to/claude
303
+ export OPENCODE_BIN=/absolute/path/to/opencode
304
+ ```
305
+
306
+ Codex configuration is inherited from the installed Codex CLI and process environment. The runtime does not log in, edit Codex config files, or add hidden permissions.
307
+
308
+ Claude Code can use its normal first-party setup or an Anthropic-compatible provider. Configure the provider through environment variables only; never write real token values into prompts, examples, fixtures, manifests, or committed docs:
309
+
310
+ ```bash
311
+ export ANTHROPIC_BASE_URL=<anthropic-compatible-base-url>
312
+ export ANTHROPIC_MODEL=<model-name>
313
+ export ANTHROPIC_DEFAULT_OPUS_MODEL=<model-name>
314
+ export ANTHROPIC_DEFAULT_SONNET_MODEL=<model-name>
315
+ export ANTHROPIC_DEFAULT_HAIKU_MODEL=<model-name>
316
+ export CLAUDE_CODE_SUBAGENT_MODEL=<model-name>
317
+ export CLAUDE_CODE_EFFORT_LEVEL=<effort>
318
+ # Set the auth token in the variable required by your provider or Claude Code setup,
319
+ # commonly ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY. Do not commit its value.
320
+ ```
321
+
322
+ OpenCode configuration is inherited from the installed OpenCode CLI. The runtime currently uses `opencode run --format json --dir <cwd>` and leaves explicit read-only/workspace-write flags in `needsVerification` until they are verified against real CLI evidence.
323
+
324
+ Proxy settings are inherited from the process environment:
325
+
326
+ ```bash
327
+ export HTTPS_PROXY=http://127.0.0.1:7897
328
+ export HTTP_PROXY=http://127.0.0.1:7897
329
+ ```
330
+
331
+ Use one of the quick verification command sets before release:
332
+
333
+ ```bash
334
+ npm run ci
335
+ npm run daemon:verify
336
+ npm run runtime:safety
337
+ npm run dogfood
338
+ npm run prepublish:check
339
+ node ./dist/cli/main.js conformance --mode fixtures --json
340
+ node ./dist/cli/main.js conformance --mode fake --json
341
+ node ./dist/cli/main.js conformance --mode real --agent all --json
342
+ node ./dist/cli/main.js smoke --mode real --agent codex --json
343
+ ```
344
+
345
+ `conformance --mode real` and `smoke --mode real` without `--allow-real-run` perform real local detection/profile certification only. They do not launch an authenticated agent run. A real run requires `--allow-real-run`; without `--cwd`, the runtime uses an isolated temporary cwd and requests read-only behavior. Treat `--allow-real-run` as an explicit local-account/network boundary.
346
+
347
+ CI uses a Node.js 20/22/24 matrix for typecheck, lint, tests, build, production dependency audit, package boundary checks, and `npm pack --dry-run`. A separate single-Node release-gates job runs `npm run daemon:verify`, `npm run runtime:safety`, and `npm run dogfood` so the full matrix does not launch redundant installed-package gates. The dogfood, CI, and prepublish paths share the same safety boundary: fixtures, fake CLIs, and real local detection/profile certification are allowed by default; authenticated real agent runs are not launched unless `--allow-real-run` is explicit.
348
+
349
+ For local release-candidate confidence, run `npm run prepublish:check`. It combines typecheck, lint, tests, build, daemon embedding verification, runtime safety verification, dogfood, production audit, package boundary checks, and a pack dry-run. The GitHub Actions `Release Candidate` workflow is manually triggered with `workflow_dispatch`, runs `npm ci`, `npm run ci`, `npm run dogfood`, and `npm run release:candidate -- --out-dir release-candidate`; the generated artifact set includes `agent-cli-runtime-tarball`, `agent-cli-runtime-pack-metadata`, `agent-cli-runtime-package-files`, `agent-cli-runtime-gate-evidence`, and `agent-cli-runtime-release-verification`. P3-10 pre-documentation release-candidate evidence is run `27945938663` for SHA `fdba3ebccb2e57a0ad295101028a2a3937a92204`, with all five downloaded artifacts passing `npm run release:verify -- --dir /tmp/agent-runtime-p3-10-current-head-remote-66VIhN/normalized` using `agent-cli-runtime.releaseVerification.v1`. Because release docs are included in the npm package, committing this evidence packet changes the package shasum; run `27945938663` must not be used as final post-documentation publish evidence. The human-gated publish packet stops at `npm publish --dry-run --ignore-scripts --tag alpha`; npm publish has not occurred, and a real publish requires a fresh release-candidate run after committing this packet. Historical P3-9 run `27943672095` only proves target SHA `65fac505ca3eb830a06d8656068cf4ed5f6dd46a`; historical P3-9 interim run `27942743285` only proves target SHA `a0299a7d81bb614661922bebc8c75496cf0a3d11` before the strict `fixtures?` package-boundary lock; historical P3-8 run `27940814340` only proves target SHA `eb8de0f9b1edfa3f94c35a50b31005c5d3c105d4`; historical P3-5 run `27932628093` only proves workflow head SHA `8d7bc2a19c626caa1ad5223acbcd35df34aff18e`. It does not publish and does not require an npm token.
350
+
351
+ To create a local release-candidate artifact set without publishing, run:
352
+
353
+ ```bash
354
+ npm run release:candidate -- --out-dir release-candidate
355
+ npm run release:verify -- --dir release-candidate
356
+ ```
357
+
358
+ `release:candidate` writes `npm-pack.json`, `package-files.txt`, `gate-evidence.json`, the tarball, and `release-verification.json` to the output directory. `release:verify` can also validate the same files after downloading GitHub Actions artifacts, including proof that `daemon:verify` and `runtime:safety` were recorded for the candidate.
359
+
360
+ The release evidence summary is [docs/release-report.md](./docs/release-report.md). The alpha publish decision runbook is [docs/release-publish-runbook.md](./docs/release-publish-runbook.md). `npm publish --dry-run --ignore-scripts --tag alpha` is documented there as a local manual dry-run check; it must not publish and is not required as a remote CI gate.
361
+
362
+ Runnable examples are in [examples/library-run.js](./examples/library-run.js), [examples/library-goal.js](./examples/library-goal.js), and [examples/cli-dogfood.md](./examples/cli-dogfood.md). The JavaScript examples create local fake CLIs and do not require real provider secrets.
363
+
364
+ ## CLI
365
+
366
+ ```bash
367
+ agent-runtime agents
368
+ agent-runtime conformance --mode fixtures --json
369
+ agent-runtime conformance --mode fake --json
370
+ agent-runtime conformance --mode real --agent all --json
371
+ agent-runtime smoke --mode real --agent codex --allow-real-run --expect-text <safe_text> --json
372
+ agent-runtime smoke --mode real --agent claude --allow-real-run --expect-text <safe_text> --json
373
+ agent-runtime smoke --mode real --agent opencode --allow-real-run --expect-text <safe_text> --json
374
+ agent-runtime smoke --mode detection --json
375
+ agent-runtime smoke --mode fixtures --json
376
+ agent-runtime run --agent codex --cwd . --prompt "fix the failing test"
377
+ agent-runtime goal --agent codex --cwd . --prompt "split this objective into tasks and execute them"
378
+ agent-runtime goal --agent codex --cwd . --prompt "run independent fixes" --max-concurrent-tasks 2 --max-attempts 2 --retryable-error-codes AGENT_TIMEOUT,AGENT_EXECUTION_FAILED
379
+ agent-runtime run --agent claude --cwd . --permission workspace-write --prompt-file task.md
380
+ agent-runtime run --agent codex --cwd . --prompt "fix the failing test" --json
381
+ agent-runtime run --agent codex --cwd . --prompt "fix the failing test" --stream jsonl --diagnostics
382
+ agent-runtime doctor
383
+ agent-runtime runs --storage-dir .agent-runtime --json
384
+ agent-runtime run-status run_123 --storage-dir .agent-runtime --json
385
+ agent-runtime replay-run run_123 --storage-dir .agent-runtime --after 10 --jsonl
386
+ agent-runtime goals --storage-dir .agent-runtime --json
387
+ agent-runtime goal-status goal_123 --storage-dir .agent-runtime --json
388
+ agent-runtime replay-goal goal_123 --storage-dir .agent-runtime --after 10 --jsonl
389
+ agent-runtime store-health --storage-dir .agent-runtime --json
390
+ agent-runtime store-lock --storage-dir .agent-runtime --json
391
+ agent-runtime store-repair --storage-dir .agent-runtime --dry-run --json
392
+ agent-runtime store-repair --storage-dir .agent-runtime --apply --json
393
+ agent-runtime diagnostics run run_123 --storage-dir .agent-runtime --json
394
+ agent-runtime diagnostics goal goal_123 --storage-dir .agent-runtime --json --out diagnostics-goal_123.json
395
+ agent-runtime smoke --mode real --agent codex --allow-real-run --prompt-file task.md --expect-text "expected reply" --timeout-ms 30000 --json --diagnostics
396
+ ```
397
+
398
+ The library API is primary. The CLI is a thin wrapper over the same runtime and supports `--json` plus `--stream jsonl` for run/goal event streams. For run/goal commands, `--json` prints the final run or goal record. `--stream jsonl --diagnostics` keeps the event stream and appends a redacted `run_summary` or `goal_summary` line after the terminal event.
399
+
400
+ `agent-runtime conformance` is the production gate wrapper. Its JSON output is versioned with `schemaVersion: "agent-runtime.conformance.v1"` and emits a stable per-adapter summary with `adapter`, `version`, `resolvedExecutable`, `auth`, `modelsSource`, `capabilities`, `argvProfile`, `promptTransport`, `parserMode`, `runClassification`, `expectedTextMatched`, `observedTextTail`, `cwdMutationChecked`, `cwdMutated`, `diagnosticsCount`, `diagnostics`, `skippedReason`, and `failureReason`.
401
+
402
+ - `--mode fixtures` checks parser contracts offline.
403
+ - `--mode fake` creates local fake CLIs and runs the real adapter argv/stdin/parser path offline.
404
+ - `--mode real` defaults to real local detection/profile certification without launching agent runs. A real run is launched only when `--allow-real-run` is explicit; otherwise runnable adapters report `runClassification: "real_run_skipped"` and `skippedReason: "real_run_not_allowed"`. `--agent all` keeps one adapter fail/skip isolated in the summary.
405
+
406
+ Real conformance diagnostics are designed for drift detection: unsupported tracked flags, unfamiliar help/version output, parser/stream failures, and unverified capabilities are reported as actionable diagnostics instead of guessed into the argv path. Unknown flags remain in `argvProfile.needsVerification`. Outputs are redacted for tokens, Bearer values, auth env assignments, prompts, observed text tails, and private absolute paths.
407
+
408
+ `agent-runtime smoke` has three modes:
409
+
410
+ - `--mode detection` runs local executable/model/auth detection only.
411
+ - `--mode fixtures` dry-runs built-in parser conformance fixtures for Codex, Claude, and OpenCode without launching real CLIs.
412
+ - `--mode real` defaults to detection/profile certification and returns `runClassification: "real_run_skipped"` unless `--allow-real-run` is explicit. With `--allow-real-run`, it launches one real non-mutating run for `--agent <id>` with runtime-requested read-only behavior. Without `--cwd`, it uses an isolated temp directory. The default prompt asks the agent to reply exactly with `agent-runtime <agent> smoke ok` without editing files, and `--expect-text <text>` can provide a different safe verifier. Expected text is required for success: if `--prompt` or `--prompt-file` is supplied without `--expect-text`, exit `0` and text output still classify as `unexpected_output`.
413
+
414
+ Recommended opt-in smoke evidence commands:
415
+
416
+ ```bash
417
+ node ./dist/cli/main.js smoke --mode real --agent codex --allow-real-run --expect-text <safe_text> --json
418
+ node ./dist/cli/main.js smoke --mode real --agent claude --allow-real-run --expect-text <safe_text> --json
419
+ node ./dist/cli/main.js smoke --mode real --agent opencode --allow-real-run --expect-text <safe_text> --json
420
+ ```
421
+
422
+ Real smoke JSON uses `schemaVersion: "agent-runtime.realSmoke.v1"` and a redacted `real_smoke_summary` with `adapter`, `version`, `auth`, `modelsSource`, `runClassification`, `expectedTextMatched`, truncated/redacted `observedTextTail`, `cwdMutationChecked`, `cwdMutated`, `diagnosticsCount`, `skippedReason`, and `failureReason`. It does not include prompt text, token values, private cwd, raw stdout/stderr, or the final run record. Failure and skip classifications include `auth_missing`, `unavailable_executable`, `unsupported_flag`, `unexpected_output`, `cwd_mutated`, `needs_verification`, and `real_run_skipped`.
423
+
424
+ The complete schema inventory and version bump policy for event envelopes, diagnostics, conformance, real smoke, store health/repair, CLI errors, release verification, and release gate evidence is maintained in [docs/api-schema-contract.md](./docs/api-schema-contract.md).
425
+
426
+ Disk storage layout is intentionally simple and tail-friendly:
427
+
428
+ ```text
429
+ .agent-runtime/
430
+ runtime.lock.json
431
+ runs/<runId>/manifest.json
432
+ runs/<runId>/events.jsonl
433
+ goals/<goalId>/manifest.json
434
+ goals/<goalId>/events.jsonl
435
+ ```
436
+
437
+ Public replay APIs remain source-compatible and return `ReplayEvent<T>` records: `{ "id": 1, "sequence": 1, "runId": "run_123", "timestamp": 123, "event": {...} }`, or the same shape with `goalId`. CLI JSONL output for `run --stream jsonl`, `goal --stream jsonl`, `replay-run --jsonl`, and `replay-goal --jsonl` uses the stable envelope `schemaVersion: "agent-runtime.event.v1"`:
438
+
439
+ ```json
440
+ {
441
+ "schemaVersion": "agent-runtime.event.v1",
442
+ "id": 1,
443
+ "sequence": 1,
444
+ "timestamp": 1760000000000,
445
+ "scope": { "kind": "run", "id": "run_123" },
446
+ "event": { "type": "run_finished", "result": "success", "timestamp": 1760000000000 },
447
+ "terminal": { "result": "success", "reason": "success" }
448
+ }
449
+ ```
450
+
451
+ `id` and `sequence` are monotonic per run or goal. Terminal reasons use one vocabulary: `success`, `failed`, `timeout`, `canceled`, `interrupted`, `validation_failed`, `execution_failed`, `unavailable`, `auth_missing`, and `task_graph_invalid`. `--stream jsonl --diagnostics` may append a redacted summary line after the event envelopes. The default durability is `relaxed`; `createAgentRuntime({ storageDir, storage: { durability: "fsync" } })` asks the store to `fdatasync`/`fsync` manifest temp files and event appends with graceful platform fallback diagnostics.
452
+
453
+ When `storageDir` is supplied, the runtime opens it in writer mode with a local single-writer lease stored in `runtime.lock.json`. The lock owner includes a generated `runtimeInstanceId`, `pid`, `startedAt`, and `heartbeatAt`; active run/goal manifests also record the current owner. A second writer runtime for the same `storageDir` is refused while the owner is live. If the existing owner is stale or closed, a new runtime may take over and records a redacted lease diagnostic. `runtime.shutdown(reason?)` cancels active runs/goals, waits briefly for terminal events, and marks the lease closed. This is a best-effort same-machine guard for embedded local runtimes; it is not a daemon coordination protocol, distributed lock, WAL, database transaction layer, or live process resume mechanism.
454
+
455
+ When a new writer runtime opens a `storageDir`, terminal runs/goals are readable immediately. Active records are recovered only when their recorded owner is missing, stale, or closed; those runs/goals are marked failed with an `AGENT_RUNTIME_INTERRUPTED` diagnostic/event so they never pretend to still be active after a process restart. Active records owned by another live runtime are left untouched and are visible through read-only inspection. A corrupt manifest or JSONL line is isolated to that record and reported through `AGENT_STORE_RECORD_CORRUPT` or `AGENT_EVENT_LOG_CORRUPT` diagnostics instead of failing runtime initialization. Corrupt manifests are not silently rewritten during load, so later health scans can still see the original damaged record.
456
+
457
+ `runs`, `goals`, `run-status`, `goal-status`, `replay-run`, `replay-goal`, `store-health`, `store-lock`, and `diagnostics` are read-only inspection paths for a supplied `storageDir`; they do not acquire the writer lease or interrupt active work. `store-lock` prints the current lock owner/status. `store-health` uses `schemaVersion: "agent-runtime.storeHealth.v1"` and scans the on-disk store without launching an agent. It reports lock status, active records with owner live/stale/closed state, run/goal totals, corrupt manifests, corrupt event logs, corrupt line counts, partial JSONL tail detection, retained event counts, last good event id/sequence, repair recommendations, interrupted historical records, storage-level sync/lease diagnostics, and consistency warnings. Middle corrupt JSONL lines are skipped so later valid records can still replay; partial tail records stop at the last known-good boundary. Terminal manifests without terminal events and non-terminal manifests with terminal events are reported as warnings; the runtime does not silently reconcile them.
458
+
459
+ `store-repair --json` defaults to the same non-destructive plan as `--dry-run`. Its output uses `schemaVersion: "agent-runtime.storeRepair.v1"`. Partial tails are reported as `truncate_partial_tail`; middle corrupt lines are reported as `isolate_corrupt_line`; terminal manifest/event mismatches are `manual_review` and are not auto-fixed. `--apply` is explicit, requires `--storage-dir`, refuses a live writer owner, holds the local store lease while writing, backs up each original event log under `repair-backups/<timestamp>/...`, and rewrites through temp-file-and-rename with best-effort fsync. If backup creation fails, the original event log is not rewritten. If rewrite fails after backup creation, the backup path is reported and the original log remains readable rather than becoming a partial rewrite. Successful apply records `AGENT_STORE_REPAIR_APPLIED`; failed apply records `AGENT_STORE_REPAIR_FAILED`, both redacted, so later health and diagnostics bundles can show repair evidence. Apply is conservative and idempotent; it is not a WAL, database transaction layer, daemon resume, or compaction service.
460
+
461
+ Diagnostics bundles are redacted JSON evidence packets for one run or goal. A bundle uses `schemaVersion: "agent-runtime.diagnostics.v1"` and includes the sanitized manifest, an event summary rather than full event payloads, `RuntimeDiagnostic[]` items, storage-level diagnostics, goal task attempt evidence when present, a supervisor summary with terminal reason and owner/lease status, and an environment-safe adapter summary. CLI usage failures emitted with `--json` use `schemaVersion: "agent-runtime.cliError.v1"`. `--out <file>` writes the bundle with a temp-file-and-rename atomic write. Bundles, health output, and JSON errors do not include raw corrupt JSONL lines, tokens, Bearer values, auth-token environment assignments, full environment dumps, prompts, or absolute private paths.
462
+
463
+ Production readiness scope is tracked in [docs/production-readiness.md](./docs/production-readiness.md). The local-first production target is single-machine, local CLI execution with explicit `storageDir`, a local single-writer lease, auditable redacted diagnostics, and no silent privilege escalation; daemon/API server, WAL, live resume/session attachment, distributed execution, UI/artifacts, telemetry, and database layers remain outside this package.
464
+
465
+ ## Configuration
466
+
467
+ Configuration is intentionally environment-first for the pre-alpha package. Use `CODEX_BIN`, `CLAUDE_BIN`, and `OPENCODE_BIN` to point at specific executables; pass proxy and provider variables through the parent process; keep tokens out of prompts and committed files.
468
+
469
+ See [docs/compatibility.md](./docs/compatibility.md) for the current real CLI smoke matrix.
470
+
471
+ ## Runtime Model
472
+
473
+ ```mermaid
474
+ flowchart LR
475
+ App["Your app or script"] --> Runtime["Agent CLI Runtime"]
476
+ Runtime --> Registry["Adapter registry"]
477
+ Registry --> Codex["Codex CLI"]
478
+ Registry --> Claude["Claude Code"]
479
+ Registry --> OpenCode["OpenCode"]
480
+ Codex --> Events["AgentEvent stream"]
481
+ Claude --> Events
482
+ OpenCode --> Events
483
+ Events --> App
484
+ ```
485
+
486
+ Each adapter owns only the details that truly vary by CLI:
487
+
488
+ - binary names and env overrides;
489
+ - version, auth, capability, and model probes;
490
+ - compatibility profile notes for verified and unverified invocation flags;
491
+ - argv construction;
492
+ - prompt transport;
493
+ - stream parser;
494
+ - permission-policy mapping.
495
+
496
+ The core runner owns process lifecycle, process-tree best-effort termination, diagnostics, cancellation, timeout, shutdown, redaction, and event delivery.
497
+
498
+ ## MVP Adapters
499
+
500
+ | Adapter | Target binary | Prompt transport | Stream strategy | MVP status |
501
+ | --- | --- | --- | --- | --- |
502
+ | Codex CLI | `codex` | stdin | `codex exec --json` | P1-6 real smoke requires expected text evidence and cwd mutation checks; timeout diagnostics classify local network/plugin startup stalls; transient reconnect events are parsed as status |
503
+ | Claude Code | `claude` | stdin JSONL | `stream-json` | P0-4 detection baseline recorded; local auth still missing |
504
+ | OpenCode | `opencode-cli`, `opencode` | stdin | JSON stream | P1-6 non-mutating isolated smoke is checked for expected text and cwd mutation; stdin prompt support is verified on local `opencode` 1.15.6, explicit read-only flags remain unverified |
505
+
506
+ Future adapters should be possible without changing the core runtime.
507
+
508
+ ## Event Protocol
509
+
510
+ The runtime exposes a small append-only event stream:
511
+
512
+ ```ts
513
+ type AgentEvent =
514
+ | { type: "run_started"; runId: string; agentId: string; cwd: string; model?: string; timestamp: number }
515
+ | { type: "status"; label: string; detail?: string; timestamp: number }
516
+ | { type: "text_delta"; text: string; timestamp: number }
517
+ | { type: "thinking_delta"; text: string; timestamp: number }
518
+ | { type: "tool_call"; id: string; name: string; input?: unknown; timestamp: number }
519
+ | { type: "tool_result"; id: string; output?: unknown; isError?: boolean; timestamp: number }
520
+ | { type: "file_event"; path: string; action: "created" | "updated" | "deleted" | "unknown"; timestamp: number }
521
+ | { type: "usage"; usage: RuntimeUsage; costUsd?: number; timestamp: number }
522
+ | { type: "error"; code: RuntimeErrorCode; message: string; retryable?: boolean; detail?: unknown; timestamp: number }
523
+ | { type: "run_finished"; result: "success" | "failed" | "cancelled"; exitCode?: number | null; signal?: string | null; timestamp: number };
524
+ ```
525
+
526
+ Goal scheduling wraps run events with `goal_started`, `task_created`, `task_started`, `task_attempt_started`, `run_event`, `task_attempt_finished`, `task_finished`, `goal_finished`, and `scheduler_error`.
527
+
528
+ Adapter-specific raw events can be logged for debugging, but the public API should stay stable and small.
529
+
530
+ ## Security Model
531
+
532
+ This project starts local processes on behalf of the caller. That is powerful and deserves explicit defaults.
533
+
534
+ - The runtime does not log in to agent CLIs.
535
+ - The runtime does not edit user CLI config files in MVP.
536
+ - Metadata probes run in a neutral temp directory, not in the user's project.
537
+ - Prompts should use stdin or prompt files before argv.
538
+ - `cwd` must be explicit.
539
+ - `extraAllowedDirs` must be explicit.
540
+ - Permission escalation must be explicit.
541
+ - Logs and diagnostics must redact secret-looking env values and tokens.
542
+ - Disk backed storage does not write secret-bearing environment maps. Diagnostics and validation stdout/stderr are redacted before they are written to manifests or events.
543
+ - A failed adapter must not collapse detection for other adapters.
544
+ - Detection probe diagnostics are classified as `not_installed`, `not_executable`, `auth_missing`, `network_error`, `unsupported_flag`, or `probe_failed`.
545
+ - JSON stream parsers ignore empty, warning, log, and non-JSON noise lines; user-visible text is emitted only from structured CLI text fields.
546
+ - Timeout diagnostics include sanitized argv/profile labels, parsed event counts, stdout/stderr tails, and actionable hints; prompts are still kept out of argv.
547
+ - Goal task `validationCommands` run in the task `cwd` after a successful agent run; failed validation marks the task and goal as failed.
548
+
549
+ The runtime should never grant more authority than the caller requested.
550
+
551
+ ## Relationship To Other Projects
552
+
553
+ Agent CLI Runtime is inspired by the adapter/runtime boundary in [OpenDesign](https://github.com/nexu-io/open-design) and the clarity of [OpenCode](https://github.com/anomalyco/opencode)'s open-source project presentation.
554
+
555
+ It is not affiliated with OpenDesign, OpenCode, Anthropic, OpenAI, or any supported CLI vendor.
556
+
557
+ ## Roadmap
558
+
559
+ - M0: SSOT, README, license, project skeleton. Done.
560
+ - M1: core process runner with fake CLI tests. Done.
561
+ - M2: Codex adapter MVP. Done.
562
+ - M3: Claude Code adapter MVP. Done.
563
+ - M4: OpenCode adapter MVP. Done.
564
+ - M5: CLI wrapper and `doctor` command. Done.
565
+ - M6: public package boundary, compatibility matrix, API/CLI contract freeze, contribution guide, and security policy. Completed for pre-alpha release candidate hardening.
566
+
567
+ ## Contributing
568
+
569
+ See [CONTRIBUTING.md](./CONTRIBUTING.md).
570
+
571
+ ## License
572
+
573
+ Apache License 2.0. See [LICENSE](./LICENSE).