@alpacakit/agents 0.1.0-beta.39 → 0.2.0-beta.3

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 (134) hide show
  1. package/README.md +142 -1
  2. package/dist/agents/builtin.d.ts +2 -2
  3. package/dist/agents/builtin.d.ts.map +1 -1
  4. package/dist/agents/builtin.js +6 -4
  5. package/dist/agents/claude/cli.d.ts +133 -0
  6. package/dist/agents/claude/cli.d.ts.map +1 -0
  7. package/dist/agents/claude/cli.js +182 -0
  8. package/dist/agents/claude/host.d.ts +14 -0
  9. package/dist/agents/claude/host.d.ts.map +1 -0
  10. package/dist/agents/claude/host.js +319 -0
  11. package/dist/agents/{claude.d.ts → claude/index.d.ts} +11 -11
  12. package/dist/agents/claude/index.d.ts.map +1 -0
  13. package/dist/agents/claude/index.js +134 -0
  14. package/dist/agents/claude/session.d.ts +25 -0
  15. package/dist/agents/claude/session.d.ts.map +1 -0
  16. package/dist/agents/claude/session.js +321 -0
  17. package/dist/agents/codex/cli.d.ts +132 -0
  18. package/dist/agents/codex/cli.d.ts.map +1 -0
  19. package/dist/agents/codex/cli.js +110 -0
  20. package/dist/agents/codex/host.d.ts +14 -0
  21. package/dist/agents/codex/host.d.ts.map +1 -0
  22. package/dist/agents/codex/host.js +221 -0
  23. package/dist/agents/codex/index.d.ts +14 -0
  24. package/dist/agents/codex/index.d.ts.map +1 -0
  25. package/dist/agents/codex/index.js +127 -0
  26. package/dist/agents/codex/session.d.ts +21 -0
  27. package/dist/agents/codex/session.d.ts.map +1 -0
  28. package/dist/agents/codex/session.js +390 -0
  29. package/dist/agents/openai-compatible/fetch-engine.d.ts.map +1 -1
  30. package/dist/agents/openai-compatible/fetch-engine.js +3 -2
  31. package/dist/agents/openai-compatible/pi/engine.js +1 -1
  32. package/dist/agents/openai-compatible/pi/{session.d.ts → run.d.ts} +1 -1
  33. package/dist/agents/openai-compatible/pi/run.d.ts.map +1 -0
  34. package/dist/agents/openai-compatible/pi/sandbox.d.ts.map +1 -1
  35. package/dist/auth/login-probe.d.ts +6 -4
  36. package/dist/auth/login-probe.d.ts.map +1 -1
  37. package/dist/auth/login-probe.js +19 -13
  38. package/dist/auth/material.d.ts +1 -1
  39. package/dist/auth/material.d.ts.map +1 -1
  40. package/dist/codex/app-server.d.ts +50 -1
  41. package/dist/codex/app-server.d.ts.map +1 -1
  42. package/dist/codex/app-server.js +140 -66
  43. package/dist/codex/index.d.ts +3 -2
  44. package/dist/codex/index.d.ts.map +1 -1
  45. package/dist/codex/index.js +2 -1
  46. package/dist/codex/protocol.d.ts +101 -0
  47. package/dist/codex/protocol.d.ts.map +1 -1
  48. package/dist/codex/protocol.js +78 -0
  49. package/dist/config/agent-config-validation.d.ts +1 -1
  50. package/dist/config/agent-config-validation.d.ts.map +1 -1
  51. package/dist/config/agent-configs.d.ts.map +1 -1
  52. package/dist/config/agent-configs.js +3 -3
  53. package/dist/config/compose.d.ts.map +1 -1
  54. package/dist/config/config.d.ts +1 -1
  55. package/dist/config/config.d.ts.map +1 -1
  56. package/dist/config/index.d.ts +2 -2
  57. package/dist/config/index.d.ts.map +1 -1
  58. package/dist/config/index.js +1 -1
  59. package/dist/config/model.d.ts +0 -1
  60. package/dist/config/model.d.ts.map +1 -1
  61. package/dist/config/schema.d.ts.map +1 -1
  62. package/dist/config/schema.js +1 -1
  63. package/dist/config/store.d.ts.map +1 -1
  64. package/dist/config/store.js +6 -24
  65. package/dist/config/support.d.ts +0 -1
  66. package/dist/config/support.d.ts.map +1 -1
  67. package/dist/config/support.js +0 -9
  68. package/dist/core/cli-child.d.ts +56 -0
  69. package/dist/core/cli-child.d.ts.map +1 -0
  70. package/dist/core/cli-child.js +105 -0
  71. package/dist/core/fs.d.ts +1 -1
  72. package/dist/core/fs.d.ts.map +1 -1
  73. package/dist/core/fs.js +10 -2
  74. package/dist/core/node-ports.d.ts.map +1 -1
  75. package/dist/core/node-ports.js +1 -5
  76. package/dist/core/session.d.ts +104 -0
  77. package/dist/core/session.d.ts.map +1 -0
  78. package/dist/core/session.js +29 -0
  79. package/dist/fleet/store.d.ts.map +1 -1
  80. package/dist/fleet/store.js +4 -3
  81. package/dist/host/availability.d.ts +2 -1
  82. package/dist/host/availability.d.ts.map +1 -1
  83. package/dist/host/github-dir.d.ts.map +1 -1
  84. package/dist/host/github-dir.js +2 -1
  85. package/dist/host/mcp-list.d.ts.map +1 -1
  86. package/dist/host/mcp-list.js +5 -6
  87. package/dist/host/run-command.d.ts +2 -1
  88. package/dist/host/run-command.d.ts.map +1 -1
  89. package/dist/host/run-command.js +10 -19
  90. package/dist/host/session-files.d.ts +23 -0
  91. package/dist/host/session-files.d.ts.map +1 -0
  92. package/dist/host/session-files.js +92 -0
  93. package/dist/index.d.ts +9 -4
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +5 -2
  96. package/dist/internal/controlled-process/supervisor-runtime.js +27 -1
  97. package/dist/kernel/classify.d.ts +1 -2
  98. package/dist/kernel/classify.d.ts.map +1 -1
  99. package/dist/kernel/classify.js +1 -1
  100. package/dist/kernel/cli-agent.d.ts.map +1 -1
  101. package/dist/kernel/cli-agent.js +26 -85
  102. package/dist/kernel/cli-login.d.ts.map +1 -1
  103. package/dist/kernel/cli-login.js +9 -30
  104. package/dist/kernel/definition.d.ts +11 -2
  105. package/dist/kernel/definition.d.ts.map +1 -1
  106. package/dist/kernel/facets.d.ts +38 -1
  107. package/dist/kernel/facets.d.ts.map +1 -1
  108. package/dist/kernel/facets.js +20 -0
  109. package/dist/kernel/login.d.ts.map +1 -1
  110. package/dist/kernel/login.js +2 -1
  111. package/dist/kernel/options.d.ts +1 -1
  112. package/dist/kernel/options.d.ts.map +1 -1
  113. package/dist/kernel/run.d.ts.map +1 -1
  114. package/dist/kernel/run.js +2 -1
  115. package/dist/kernel/runtime.d.ts +10 -6
  116. package/dist/kernel/runtime.d.ts.map +1 -1
  117. package/dist/kernel/runtime.js +4 -1
  118. package/dist/kernel/session.d.ts +104 -0
  119. package/dist/kernel/session.d.ts.map +1 -0
  120. package/dist/kernel/session.js +314 -0
  121. package/dist/testing/fake-exec.d.ts +10 -5
  122. package/dist/testing/fake-exec.d.ts.map +1 -1
  123. package/dist/testing/fake-exec.js +17 -2
  124. package/dist/testing/index.d.ts +1 -1
  125. package/dist/testing/index.d.ts.map +1 -1
  126. package/dist/testing/index.js +1 -1
  127. package/package.json +2 -2
  128. package/dist/agents/claude.d.ts.map +0 -1
  129. package/dist/agents/claude.js +0 -543
  130. package/dist/agents/codex.d.ts +0 -19
  131. package/dist/agents/codex.d.ts.map +0 -1
  132. package/dist/agents/codex.js +0 -377
  133. package/dist/agents/openai-compatible/pi/session.d.ts.map +0 -1
  134. /package/dist/agents/openai-compatible/pi/{session.js → run.js} +0 -0
package/README.md CHANGED
@@ -150,6 +150,109 @@ Each recovery call performs one authenticated termination convergence. A
150
150
  typed `pending` result invokes no lifecycle callback; the host owns the
151
151
  cancellable retry cadence and calls `recover` again when appropriate.
152
152
 
153
+ ## Sessions: hold a conversation and talk to it (`agent.session`)
154
+
155
+ `run()` is one prompt, one process, one answer. A **Session** is the other
156
+ shape a host offers: one Claude (`claude -p --input-format stream-json`) or
157
+ Codex (`codex app-server` thread) process held open across turns, which a
158
+ UI or an orchestrator keeps talking to. `agent.session` is `null` when the
159
+ definition has no session facet; `claude()` and `codex()` provide one.
160
+
161
+ ```ts
162
+ const runtime = createAgentRuntime({ agents: [claude(), codex()], baseEnv: processEnv() });
163
+ const sessions = runtime.agent("codex").session!;
164
+
165
+ const session = await sessions.open({
166
+ workspace: "/work/repo",
167
+ permissions: {
168
+ approvalPolicy: "on-request",
169
+ approvalsReviewer: "user",
170
+ sandbox: { type: "read-only" },
171
+ },
172
+ });
173
+ void (async () => {
174
+ for await (const event of session.events) {
175
+ // turn_started / message / tool / approval_requested / turn_ended / exited
176
+ if (event.type === "approval_requested") await session.respond(event.requestId, "accept");
177
+ }
178
+ })();
179
+ const turn = await session.send("Summarize the failing tests."); // { turnId, outcome, text }
180
+ await session.steer("Also list the flaky ones."); // into the running turn
181
+ await session.interrupt(); // turn ends with outcome "interrupted"
182
+ await session.close(); // EOF, then signals; releases the host's lock
183
+
184
+ const facts = await sessions.inspect(session.id, { homeDir }); // { workspace, lastPermissions, owner, archived } | null
185
+ const again = await sessions.resume(session.id, { workspace: facts!.workspace, permissions: facts!.lastPermissions! });
186
+ // An archived Codex thread resumes only with `unarchive: true` (see below).
187
+ ```
188
+
189
+ `send()` starts one turn and resolves when the host ends it; exactly one
190
+ turn runs at a time (`send` needs idle — including while a Codex
191
+ `turn/start` is still unanswered — and `steer` / `interrupt` need a running
192
+ turn; anything else is a typed `invalid_input`). `Session.id` is the host's
193
+ own conversation id (Claude `session_id`, Codex thread id), so it is what
194
+ the host's files, `--resume`, and `thread/resume` know.
195
+
196
+ `approval_requested.decisions` is always `["accept", "decline"]`: Claude's
197
+ `can_use_tool` takes allow or deny, and Codex takes `decline` for every
198
+ request even when its own `availableDecisions` lists only `accept` plus
199
+ amendment / `cancel` variants. Requests belong to the turn that raised
200
+ them — once the turn ends, `respond` for them is an `invalid_input`; after
201
+ the session ended it is "session is closed" for both hosts.
202
+
203
+ Permissions are **per host**, never the neutral `Sandbox` of `run()`:
204
+ `ClaudeSessionPermissions { mode }` (spelled as the transcript's
205
+ `permissionMode`; the CLI flag spells `default` as `manual`, and
206
+ `bypassPermissions` also passes `--allow-dangerously-skip-permissions`;
207
+ every Claude session passes `--permission-prompt-tool stdio` so approval
208
+ prompts reach `events` instead of being auto-denied) and `CodexSessionPermissions
209
+ { approvalPolicy, approvalsReviewer, sandbox }` with `sandbox` being
210
+ `read-only`, `danger-full-access`, or `workspace-write { writableRoots,
211
+ networkAccess, excludeTmpdirEnvVar, excludeSlashTmp }` (sent as config.toml
212
+ overrides on `thread/start` / `thread/resume`). `resume` always passes them
213
+ explicitly: a Codex `thread/resume` without `sandbox` ran as
214
+ danger-full-access in the lab.
215
+
216
+ Facts hosts write that `inspect` reads (through `HostLocation.homeDir`, never
217
+ `os.homedir()`): Claude `~/.claude/projects/*/<id>.jsonl` (first `cwd`, last
218
+ `permissionMode`) and `~/.claude/sessions/<pid>.json` (`owner.pid`,
219
+ `owner.entrypoint`); Codex `~/.codex/sessions/YYYY/MM/DD/rollout-*-<id>.jsonl`
220
+ or `archived_sessions/` (`archived`), `session_meta.cwd`, last
221
+ `turn_context`, and `thread-writer-locks/<id>.lock` (`owner` with `pid: null`
222
+ — the lock does not say who). `inspect` reports what is recorded; it does not
223
+ probe whether a recorded pid is alive.
224
+
225
+ **Codex trust note.** `open()` with a writable sandbox (`workspace-write`,
226
+ `danger-full-access`) makes Codex append a `trust_level` entry for the
227
+ workspace to `~/.codex/config.toml` (observed with codex 0.153.4);
228
+ `read-only` does not. Surface that before offering a writable `open()`.
229
+ Whether `resume()` with a writable sandbox appends the same entry is
230
+ **unverified** (the lab only resumed with `read-only`).
231
+
232
+ **Codex archive note.** `resume()` of an archived thread fails by default
233
+ with the server's "is archived" message (`inspect(id).archived` tells you
234
+ beforehand). Pass `unarchive: true` to `thread/unarchive` it first — that
235
+ also moves the thread back into the ChatGPT app's active list, which is
236
+ why it is opt-in. Claude has no archive; the option is ignored there.
237
+
238
+ Design conditions this facet keeps (T60 v3):
239
+
240
+ - C1 — permissions are host-typed; the neutral `Sandbox` never learns `workspace-write`.
241
+ - C2 — a Session holds a plain `Exec` child with `keepStdinOpen`; it does not go through `ControlledExec`. If the embedding process restarts, `resume` is the recovery path.
242
+ - C3 — the app-server client never kills the process on a request timeout; only that request fails. `close()` is the one place the process is ended.
243
+ - C4 — every host-file read goes through `HostLocation.homeDir`.
244
+ - C5 — Claude approvals were verified in the lab (`--permission-prompt-tool stdio` → `control_request` / `can_use_tool` → `control_response` allow/deny), so `respond` is supported for both hosts.
245
+ - C6 — consumers pin an exact version of this package and upgrade libs first, then the consumer, when a host CLI changes its protocol.
246
+
247
+ Not supported (by choice, or unverified):
248
+
249
+ - Claude control requests other than `can_use_tool` are ignored (logged) — if the CLI ever waits on one, that turn hangs until `interrupt()`; Codex server requests other than `item/commandExecution/requestApproval` and `item/fileChange/requestApproval` are refused with JSON-RPC -32601 so a turn never hangs on them. Neither case has been observed in the lab.
250
+ - Claude's stream carries no turn id, so a `steer()` that lands after the host has already ended the turn is taken by the CLI as the next turn's prompt; the following `send()` then resolves with that orphan turn's result. Steer only while a `tool` event has shown the turn is still working.
251
+ - Decisions are `accept` / `decline` only — no `acceptForSession`, execpolicy amendments, or `cancel`; Codex `approvalPolicy` is `untrusted` / `on-request` / `never` (the granular object form and `externalSandbox` decode to `lastPermissions: null` in `inspect`).
252
+ - No per-turn limits or guards on a Session; use `interrupt()`. `open()` takes no `AbortSignal`; use `close()`.
253
+ - `claude({ customizations: "disabled" })` sessions pass `--safe-mode --strict-mcp-config` but keep session persistence (a Session is the recorded conversation).
254
+ - The Claude child needs `USER` in `baseEnv` (keychain lookup); with only `PATH` / `HOME` the CLI reports "Not logged in".
255
+
153
256
  ## Structured output
154
257
 
155
258
  Any Standard Schema validator (zod v4 etc.) works. Repair happens within
@@ -303,7 +406,45 @@ installed skills and MCP servers remains in progress.
303
406
  | `/config/xdg` | XDG Base Directory config-root preset (general apps) |
304
407
  | `/config/extension` | custom agent-config kinds (`defineAgentConfigType`) |
305
408
  | `/testing` | `fakeExec`, `fakeClock`, `fakeAgent`, `fakeSecretResolver` — no subprocess ever spawns |
306
- | `/codex` | codex app-server JSON-RPC client (advanced) |
409
+ | `/codex` | codex app-server JSON-RPC client: typed requests, `incoming` notifications / server requests, `respond` (advanced) |
410
+
411
+ ## Changes in 0.2.0-beta.3
412
+
413
+ - `run()` with `sandbox: { mode: "read-only" }` passes
414
+ `--permission-mode manual` to claude (was `default`), and `run()` with
415
+ `mode: "full"` passes `--allow-dangerously-skip-permissions` next to
416
+ `bypassPermissions`, as sessions already did. One function
417
+ (`permissionModeArgs`) now decides how a mode reaches the CLI for run and
418
+ session alike; `manual` is the spelling `claude --help` (2.1.270) lists —
419
+ `default` is not among its choices — and the allow flag is what makes
420
+ `bypassPermissions` an option at all.
421
+ - `agent.auth.check` for a home-dir material answers `unknown` when the
422
+ probe cannot start (no binary on PATH, spawn fault) instead of throwing —
423
+ the same policy the availability probe already had.
424
+ - Internal structure (no API change): the facts of a CLI child process
425
+ (PATH resolution, output tails, redacted failure evidence, signal
426
+ escalation) live once in `core/cli-child.ts`; each built-in host spells
427
+ its vocabulary once in `agents/<host>/cli.ts`; the kernel drives every
428
+ Session (`kernel/session.ts`) and the host adapters only encode and
429
+ decode. `SessionEvent.turnId` is the host's turn id when it has one
430
+ (Codex) and a per-session counter otherwise (Claude).
431
+
432
+ ## Breaking changes in 0.2.0
433
+
434
+ - `Agent.session` (and `AgentDefinition.session`) are new; `AgentDefinition`
435
+ and `Agent` gained a session-permissions type parameter, and `AgentRuntime`
436
+ a second parameter for the definition list so `runtime.agent(id).session`
437
+ is typed per agent (defaults keep existing annotations compiling).
438
+ `claude()` / `codex()` return definitions typed with their permission
439
+ shapes.
440
+ - `@alpacakit/agents/codex`: `CodexAppServer` gained `incoming`, `respond`,
441
+ `exit`, and `stderrTail`; a request timeout no longer kills the server
442
+ process; `close()` ends stdin and waits for the child to exit, escalating
443
+ to SIGTERM after 5 s and SIGKILL after another 5 s. Host-plane calls
444
+ (`agent.skills.list`, `agent.mcp.list` for codex) therefore take up to
445
+ 10 s longer than before when the server hangs, instead of being killed at
446
+ once. `thread/*` and `turn/*` methods are registered.
447
+ - `run()` behavior is unchanged.
307
448
 
308
449
  ## Status
309
450
 
@@ -4,8 +4,8 @@
4
4
  * the config layer reuses `BUILTIN_AGENT_ID` for its agent-config kinds —
5
5
  * so "codex"/"claude" is spelled once, not once per layer.
6
6
  */
7
- import { claude } from "./claude.js";
8
- import { codex } from "./codex.js";
7
+ import { claude } from "./claude/index.js";
8
+ import { codex } from "./codex/index.js";
9
9
  export declare const BUILTIN_AGENT_ID: {
10
10
  readonly codex: "codex";
11
11
  readonly claude: "claude";
@@ -1 +1 @@
1
- {"version":3,"file":"builtin.d.ts","sourceRoot":"","sources":["../../src/agents/builtin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AAEX,MAAM,MAAM,cAAc,GACxB,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAE3D,wEAAwE;AACxE,eAAO,MAAM,qBAAqB;;;CAA6B,CAAC"}
1
+ {"version":3,"file":"builtin.d.ts","sourceRoot":"","sources":["../../src/agents/builtin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AAEX,MAAM,MAAM,cAAc,GACxB,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAE3D,wEAAwE;AACxE,eAAO,MAAM,qBAAqB;;;CAA6B,CAAC"}
@@ -4,11 +4,13 @@
4
4
  * the config layer reuses `BUILTIN_AGENT_ID` for its agent-config kinds —
5
5
  * so "codex"/"claude" is spelled once, not once per layer.
6
6
  */
7
- import { claude } from "./claude.js";
8
- import { codex } from "./codex.js";
7
+ import { CLAUDE } from "./claude/cli.js";
8
+ import { claude } from "./claude/index.js";
9
+ import { CODEX } from "./codex/cli.js";
10
+ import { codex } from "./codex/index.js";
9
11
  export const BUILTIN_AGENT_ID = {
10
- codex: "codex",
11
- claude: "claude",
12
+ codex: CODEX.id,
13
+ claude: CLAUDE.id,
12
14
  };
13
15
  /** Keyed record, not a branch: adding a built-in is adding an entry. */
14
16
  export const builtinAgentFactories = { claude, codex };
@@ -0,0 +1,133 @@
1
+ /**
2
+ * The Claude CLI vocabulary, spelled once: command and flags, the
3
+ * permission-mode words and how the flag spells them, the stream-json
4
+ * record types and fields, and the host's own file names. The run adapter
5
+ * (`index.ts`), the session adapter (`session.ts`), and the host plane
6
+ * (`host.ts`) all read from here and never respell a word.
7
+ */
8
+ import { type UnknownRecord } from "../../core/json.js";
9
+ import type { AgentEvent } from "../../core/run.js";
10
+ import type { CliResultCapture } from "../../kernel/cli-agent.js";
11
+ export declare const CLAUDE_CONFIG_DIR_ENV_VAR = "CLAUDE_CONFIG_DIR";
12
+ export declare const CLAUDE_API_KEY_ENV_VAR = "ANTHROPIC_API_KEY";
13
+ /** Spelled as the transcript's `permissionMode` records it. */
14
+ export declare const CLAUDE_PERMISSION_MODE: {
15
+ readonly default: "default";
16
+ readonly acceptEdits: "acceptEdits";
17
+ readonly auto: "auto";
18
+ readonly bypassPermissions: "bypassPermissions";
19
+ readonly dontAsk: "dontAsk";
20
+ readonly plan: "plan";
21
+ };
22
+ export type ClaudePermissionMode = (typeof CLAUDE_PERMISSION_MODE)[keyof typeof CLAUDE_PERMISSION_MODE];
23
+ export type ClaudeSessionPermissions = {
24
+ readonly mode: ClaudePermissionMode;
25
+ };
26
+ export declare const CLAUDE: {
27
+ readonly id: "claude";
28
+ readonly command: "claude";
29
+ readonly cli: {
30
+ readonly prompt: "-p";
31
+ readonly model: "--model";
32
+ readonly effort: "--effort";
33
+ readonly outputFormat: "--output-format";
34
+ readonly inputFormat: "--input-format";
35
+ readonly streamJson: "stream-json";
36
+ readonly permissionMode: "--permission-mode";
37
+ /** The flag spells the transcript's `default` mode `manual` (claude 2.1.270 `--help`). */
38
+ readonly manualPermissionMode: "manual";
39
+ readonly allowDangerouslySkipPermissions: "--allow-dangerously-skip-permissions";
40
+ readonly permissionPromptTool: "--permission-prompt-tool";
41
+ readonly stdio: "stdio";
42
+ readonly resume: "--resume";
43
+ readonly sessionId: "--session-id";
44
+ readonly verbose: "--verbose";
45
+ readonly tools: "--tools";
46
+ readonly allowedTools: "--allowedTools";
47
+ readonly safeMode: "--safe-mode";
48
+ readonly strictMcpConfig: "--strict-mcp-config";
49
+ readonly noSessionPersistence: "--no-session-persistence";
50
+ };
51
+ readonly auth: {
52
+ readonly statusArgs: readonly ["auth", "status", "--json"];
53
+ readonly loginArgs: readonly ["auth", "login", "--claudeai"];
54
+ };
55
+ readonly mcp: {
56
+ readonly addJson: "add-json";
57
+ readonly mcp: "mcp";
58
+ readonly remove: "remove";
59
+ readonly scope: "--scope";
60
+ };
61
+ readonly plugin: {
62
+ readonly plugin: "plugin";
63
+ readonly install: "install";
64
+ };
65
+ readonly json: {
66
+ readonly field: {
67
+ readonly type: "type";
68
+ readonly message: "message";
69
+ readonly role: "role";
70
+ readonly content: "content";
71
+ readonly text: "text";
72
+ readonly name: "name";
73
+ readonly usage: "usage";
74
+ readonly result: "result";
75
+ readonly inputTokens: "input_tokens";
76
+ readonly outputTokens: "output_tokens";
77
+ readonly totalCost: "total_cost_usd";
78
+ readonly request: "request";
79
+ readonly requestId: "request_id";
80
+ readonly response: "response";
81
+ readonly subtype: "subtype";
82
+ readonly error: "error";
83
+ readonly toolName: "tool_name";
84
+ readonly description: "description";
85
+ readonly input: "input";
86
+ readonly isError: "is_error";
87
+ readonly behavior: "behavior";
88
+ readonly updatedInput: "updatedInput";
89
+ };
90
+ readonly type: {
91
+ readonly user: "user";
92
+ readonly assistant: "assistant";
93
+ readonly result: "result";
94
+ readonly text: "text";
95
+ readonly toolUse: "tool_use";
96
+ readonly controlRequest: "control_request";
97
+ readonly controlResponse: "control_response";
98
+ };
99
+ readonly subtype: {
100
+ readonly interrupt: "interrupt";
101
+ readonly canUseTool: "can_use_tool";
102
+ readonly success: "success";
103
+ readonly error: "error";
104
+ };
105
+ readonly behavior: {
106
+ readonly allow: "allow";
107
+ readonly deny: "deny";
108
+ };
109
+ };
110
+ readonly files: {
111
+ readonly root: ".claude";
112
+ readonly projects: "projects";
113
+ readonly sessions: "sessions";
114
+ readonly transcriptExtension: ".jsonl";
115
+ readonly registryExtension: ".json";
116
+ readonly cwd: "cwd";
117
+ readonly permissionMode: "permissionMode";
118
+ readonly sessionId: "sessionId";
119
+ readonly pid: "pid";
120
+ readonly entrypoint: "entrypoint";
121
+ };
122
+ };
123
+ /**
124
+ * The one rule for how a permission mode reaches the CLI: the flag spells
125
+ * `default` as `manual`, and `bypassPermissions` is only an option once
126
+ * `--allow-dangerously-skip-permissions` enables it (`claude --help`).
127
+ */
128
+ export declare function permissionModeArgs(mode: ClaudePermissionMode): string[];
129
+ export declare function parseClaudeLine(line: string): AgentEvent[];
130
+ /** Events carried by one already-parsed stream-json record (`assistant` only). */
131
+ export declare function parseClaudeRecord(record: UnknownRecord): AgentEvent[];
132
+ export declare function parseClaudeResult(line: string): CliResultCapture | null;
133
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/agents/claude/cli.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,mBAAmB,CAAC;AAE9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAC7D,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB;;;;;;;CAOzB,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,OAAO,sBAAsB,CAAC,CAAC;AAEvE,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,MAAM;;;;;;;;;;;QAWf,0FAA0F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkFpF,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAUvE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAG1D;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,EAAE,CAgCrE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAkBvE"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * The Claude CLI vocabulary, spelled once: command and flags, the
3
+ * permission-mode words and how the flag spells them, the stream-json
4
+ * record types and fields, and the host's own file names. The run adapter
5
+ * (`index.ts`), the session adapter (`session.ts`), and the host plane
6
+ * (`host.ts`) all read from here and never respell a word.
7
+ */
8
+ import { readJsonRecord, readNumber, readRecord, readString, readUnknownRecord, } from "../../core/json.js";
9
+ import { AGENT_EVENT_TYPE } from "../../core/run.js";
10
+ export const CLAUDE_CONFIG_DIR_ENV_VAR = "CLAUDE_CONFIG_DIR";
11
+ export const CLAUDE_API_KEY_ENV_VAR = "ANTHROPIC_API_KEY";
12
+ /** Spelled as the transcript's `permissionMode` records it. */
13
+ export const CLAUDE_PERMISSION_MODE = {
14
+ default: "default",
15
+ acceptEdits: "acceptEdits",
16
+ auto: "auto",
17
+ bypassPermissions: "bypassPermissions",
18
+ dontAsk: "dontAsk",
19
+ plan: "plan",
20
+ };
21
+ export const CLAUDE = {
22
+ id: "claude",
23
+ command: "claude",
24
+ cli: {
25
+ prompt: "-p",
26
+ model: "--model",
27
+ effort: "--effort",
28
+ outputFormat: "--output-format",
29
+ inputFormat: "--input-format",
30
+ streamJson: "stream-json",
31
+ permissionMode: "--permission-mode",
32
+ /** The flag spells the transcript's `default` mode `manual` (claude 2.1.270 `--help`). */
33
+ manualPermissionMode: "manual",
34
+ allowDangerouslySkipPermissions: "--allow-dangerously-skip-permissions",
35
+ permissionPromptTool: "--permission-prompt-tool",
36
+ stdio: "stdio",
37
+ resume: "--resume",
38
+ sessionId: "--session-id",
39
+ verbose: "--verbose",
40
+ tools: "--tools",
41
+ allowedTools: "--allowedTools",
42
+ safeMode: "--safe-mode",
43
+ strictMcpConfig: "--strict-mcp-config",
44
+ noSessionPersistence: "--no-session-persistence",
45
+ },
46
+ auth: {
47
+ statusArgs: ["auth", "status", "--json"],
48
+ loginArgs: ["auth", "login", "--claudeai"],
49
+ },
50
+ mcp: {
51
+ addJson: "add-json",
52
+ mcp: "mcp",
53
+ remove: "remove",
54
+ scope: "--scope",
55
+ },
56
+ plugin: {
57
+ plugin: "plugin",
58
+ install: "install",
59
+ },
60
+ json: {
61
+ field: {
62
+ type: "type",
63
+ message: "message",
64
+ role: "role",
65
+ content: "content",
66
+ text: "text",
67
+ name: "name",
68
+ usage: "usage",
69
+ result: "result",
70
+ inputTokens: "input_tokens",
71
+ outputTokens: "output_tokens",
72
+ totalCost: "total_cost_usd",
73
+ request: "request",
74
+ requestId: "request_id",
75
+ response: "response",
76
+ subtype: "subtype",
77
+ error: "error",
78
+ toolName: "tool_name",
79
+ description: "description",
80
+ input: "input",
81
+ isError: "is_error",
82
+ behavior: "behavior",
83
+ updatedInput: "updatedInput",
84
+ },
85
+ type: {
86
+ user: "user",
87
+ assistant: "assistant",
88
+ result: "result",
89
+ text: "text",
90
+ toolUse: "tool_use",
91
+ controlRequest: "control_request",
92
+ controlResponse: "control_response",
93
+ },
94
+ subtype: {
95
+ interrupt: "interrupt",
96
+ canUseTool: "can_use_tool",
97
+ success: "success",
98
+ error: "error",
99
+ },
100
+ behavior: { allow: "allow", deny: "deny" },
101
+ },
102
+ files: {
103
+ root: ".claude",
104
+ projects: "projects",
105
+ sessions: "sessions",
106
+ transcriptExtension: ".jsonl",
107
+ registryExtension: ".json",
108
+ cwd: "cwd",
109
+ permissionMode: "permissionMode",
110
+ sessionId: "sessionId",
111
+ pid: "pid",
112
+ entrypoint: "entrypoint",
113
+ },
114
+ };
115
+ /**
116
+ * The one rule for how a permission mode reaches the CLI: the flag spells
117
+ * `default` as `manual`, and `bypassPermissions` is only an option once
118
+ * `--allow-dangerously-skip-permissions` enables it (`claude --help`).
119
+ */
120
+ export function permissionModeArgs(mode) {
121
+ return [
122
+ CLAUDE.cli.permissionMode,
123
+ mode === CLAUDE_PERMISSION_MODE.default
124
+ ? CLAUDE.cli.manualPermissionMode
125
+ : mode,
126
+ ...(mode === CLAUDE_PERMISSION_MODE.bypassPermissions
127
+ ? [CLAUDE.cli.allowDangerouslySkipPermissions]
128
+ : []),
129
+ ];
130
+ }
131
+ export function parseClaudeLine(line) {
132
+ const record = readJsonRecord(line);
133
+ return record === null ? [] : parseClaudeRecord(record);
134
+ }
135
+ /** Events carried by one already-parsed stream-json record (`assistant` only). */
136
+ export function parseClaudeRecord(record) {
137
+ if (readString(record, CLAUDE.json.field.type) !== CLAUDE.json.type.assistant) {
138
+ return [];
139
+ }
140
+ const message = readRecord(record, CLAUDE.json.field.message);
141
+ const content = message?.[CLAUDE.json.field.content];
142
+ if (!Array.isArray(content)) {
143
+ return [{ type: AGENT_EVENT_TYPE.turn }];
144
+ }
145
+ const events = [{ type: AGENT_EVENT_TYPE.turn }];
146
+ for (const block of content) {
147
+ const blockRecord = readUnknownRecord(block);
148
+ if (blockRecord === null) {
149
+ continue;
150
+ }
151
+ const blockType = readString(blockRecord, CLAUDE.json.field.type);
152
+ if (blockType === CLAUDE.json.type.text) {
153
+ const text = readString(blockRecord, CLAUDE.json.field.text);
154
+ if (text !== null) {
155
+ events.push({ type: AGENT_EVENT_TYPE.message, text });
156
+ }
157
+ }
158
+ if (blockType === CLAUDE.json.type.toolUse) {
159
+ const name = readString(blockRecord, CLAUDE.json.field.name);
160
+ if (name !== null) {
161
+ events.push({ type: AGENT_EVENT_TYPE.tool, name });
162
+ }
163
+ }
164
+ }
165
+ return events;
166
+ }
167
+ export function parseClaudeResult(line) {
168
+ const record = readJsonRecord(line);
169
+ if (record === null ||
170
+ readString(record, CLAUDE.json.field.type) !== CLAUDE.json.type.result) {
171
+ return null;
172
+ }
173
+ const usageRecord = readRecord(record, CLAUDE.json.field.usage);
174
+ const usage = usageRecord === null
175
+ ? null
176
+ : {
177
+ inputTokens: readNumber(usageRecord, CLAUDE.json.field.inputTokens),
178
+ outputTokens: readNumber(usageRecord, CLAUDE.json.field.outputTokens),
179
+ costUsd: readNumber(record, CLAUDE.json.field.totalCost),
180
+ };
181
+ return { text: readString(record, CLAUDE.json.field.result), usage };
182
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The claude host plane: auth materials and sign-in, installed skills
3
+ * (`~/.claude/skills`, project `.claude/skills`, plugins), and MCP
4
+ * registration through the CLI. Outside a run and a session; every path
5
+ * comes from `HostContext.location`, never `os.homedir()`.
6
+ */
7
+ import { type LoginCapability } from "../../auth/login.js";
8
+ import type { MaterialHandlers } from "../../auth/material.js";
9
+ import { type McpCapability, type SkillsCapability } from "../../host/capabilities.js";
10
+ export declare const claudeAuth: MaterialHandlers;
11
+ export declare const claudeLogin: LoginCapability;
12
+ export declare const claudeSkills: SkillsCapability;
13
+ export declare const claudeMcp: McpCapability;
14
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../../src/agents/claude/host.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAOhF,OAAO,EAML,KAAK,aAAa,EAKlB,KAAK,gBAAgB,EAEtB,MAAM,4BAA4B,CAAC;AA2WpC,eAAO,MAAM,UAAU,EAAE,gBAQxB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,eAIzB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,gBAO1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,aAMvB,CAAC"}