@pat-lewczuk/cezar 0.1.4 → 0.1.5
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.
- package/README.md +124 -37
- package/dist/config.d.ts +46 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-runner.d.ts +16 -0
- package/dist/core/agent-runner.js +9 -0
- package/dist/core/agent-runner.js.map +1 -1
- package/dist/core/claude-cli-runner.d.ts +6 -0
- package/dist/core/claude-cli-runner.js +27 -43
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/core/codex-app-server-runner.js +42 -5
- package/dist/core/codex-app-server-runner.js.map +1 -1
- package/dist/core/opencode-server-runner.js +42 -5
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/git-worktree.d.ts +21 -0
- package/dist/git-worktree.js +48 -4
- package/dist/git-worktree.js.map +1 -1
- package/dist/handoff.d.ts +1 -1
- package/dist/handoff.js +2 -0
- package/dist/handoff.js.map +1 -1
- package/dist/index.js +34 -4
- package/dist/index.js.map +1 -1
- package/dist/runs/store.d.ts +82 -1
- package/dist/runs/store.js +57 -5
- package/dist/runs/store.js.map +1 -1
- package/dist/server/git.js +12 -1
- package/dist/server/git.js.map +1 -1
- package/dist/server/github.d.ts +8 -29
- package/dist/server/github.js +7 -139
- package/dist/server/github.js.map +1 -1
- package/dist/server/pr.d.ts +5 -20
- package/dist/server/pr.js +4 -95
- package/dist/server/pr.js.map +1 -1
- package/dist/server/server.d.ts +30 -1
- package/dist/server/server.js +448 -19
- package/dist/server/server.js.map +1 -1
- package/dist/workflows/run.d.ts +90 -0
- package/dist/workflows/run.js +412 -29
- package/dist/workflows/run.js.map +1 -1
- package/dist/workflows/types.d.ts +16 -16
- package/package.json +44 -6
- package/scripts/check-pack.mjs +46 -0
- package/scripts/dev.mjs +74 -0
- package/scripts/mock-claude.mjs +54 -2
- package/scripts/test-process-usage.mjs +47 -0
- package/web/dist/assets/{toaster-BS2E1bhz.js → centered-state-zgEVNWsj.js} +1 -1
- package/web/dist/assets/compare-variants-ChHCcSQX.js +1 -0
- package/web/dist/assets/dialog-DDRjj-uK.js +1 -0
- package/web/dist/assets/{diff-view-CctnJHWe.js → diff-view-CWRO0GMe.js} +1 -1
- package/web/dist/assets/dist-id2CeyV-.js +1 -0
- package/web/dist/assets/git-toolbar-C4jo602L.js +1 -0
- package/web/dist/assets/github-BOIzQFwE.js +1 -0
- package/web/dist/assets/index-Cv1pQs9U.js +11 -0
- package/web/dist/assets/repo-git-BuyJiqna.js +1 -0
- package/web/dist/assets/{run-diff-C7mfxj7f.js → run-diff-BocG6LVd.js} +1 -1
- package/web/dist/assets/run-header-CDKw22ek.js +1 -0
- package/web/dist/assets/skill-detail-CcFFKmGO.js +1 -0
- package/web/dist/assets/skills-Bc_4U3yq.js +1 -0
- package/web/dist/assets/tab-link-C77EP3i6.js +1 -0
- package/web/dist/assets/task-changes-DsDgN8-x.js +1 -0
- package/web/dist/assets/task-commits-Dve6Fozc.js +1 -0
- package/web/dist/assets/{task-files-FOAAz2FB.js → task-files-BDMc_z0B.js} +1 -1
- package/web/dist/assets/{task-thread-Bncxe8Os.js → task-thread-CmRuPj9H.js} +2 -2
- package/web/dist/assets/use-desktop-CyEWsxSU.js +3 -0
- package/web/dist/assets/{workflows-Dg2wQg6i.js → workflows-J3z3kYic.js} +1 -1
- package/web/dist/index.html +5 -5
- package/web/app/node_modules/.vite/vitest/ca84d1343b96baa8137c943ed1860e522cacb238/results.json +0 -1
- package/web/app/src/components/app-shell-container.tsx +0 -76
- package/web/app.js +0 -3428
- package/web/dist/assets/compare-variants-D6fPL9cS.js +0 -1
- package/web/dist/assets/dialog-BEgEWFDS.js +0 -1
- package/web/dist/assets/dist-CW7M_Jhs.js +0 -1
- package/web/dist/assets/git-toolbar-D8OPPqBP.js +0 -1
- package/web/dist/assets/github-BYIHwIJB.js +0 -1
- package/web/dist/assets/index-9qG98Orw.js +0 -11
- package/web/dist/assets/repo-git-XTqlVOl2.js +0 -1
- package/web/dist/assets/run-header-CfVeY3uM.js +0 -1
- package/web/dist/assets/skill-detail-C9cniyId.js +0 -1
- package/web/dist/assets/skills-DXgY1uXJ.js +0 -1
- package/web/dist/assets/tab-link-phWEqSBy.js +0 -1
- package/web/dist/assets/task-changes-DYwnPnTN.js +0 -1
- package/web/dist/assets/task-commits-DZzxEzbn.js +0 -1
- package/web/dist/assets/use-desktop-eHm2iTXX.js +0 -3
- package/web/index.html +0 -108
- package/web/style.css +0 -1767
package/README.md
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
# cezar ⚡
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Parallel coding agents orchestrator** — a local cockpit for running and
|
|
6
|
+
tracking AI coding-agent tasks in your repo.
|
|
6
7
|
|
|
7
|
-
Type a task, pick a workflow
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Type a task, pick a workflow and an agent — **Claude Code, Codex or OpenCode,
|
|
9
|
+
or a mix of them per step** — and watch it work live: steps, tool calls,
|
|
10
|
+
tokens, diffs, in a browser cockpit that runs entirely on your machine.
|
|
11
|
+
Your CLI logins, your `gh`, your files. No accounts, no database, no cloud.
|
|
10
12
|
|
|
11
|
-
[What it solves](#what-it-solves) · [Who it's for](#who-its-for) · [Quick start](#quick-start) · [How it works](#how-it-works) · [Core concepts](#core-concepts) · [Cockpit tour](#cockpit-tour)
|
|
13
|
+
[A look inside](#a-look-inside) · [What it solves](#what-it-solves) · [Who it's for](#who-its-for) · [Quick start](#quick-start) · [How it works](#how-it-works) · [Core concepts](#core-concepts) · [Cockpit tour](#cockpit-tour) · [Agent backends](#coding-agent-backends)
|
|
12
14
|
|
|
13
15
|
[](#license)
|
|
14
16
|

|
|
@@ -29,6 +31,18 @@ That's the whole setup. If your `claude` CLI is logged in (Pro/Max) and `gh` is
|
|
|
29
31
|
authenticated, there is nothing else to configure. State lives in `.ai/cezar/`
|
|
30
32
|
inside your repo — plain JSON, NDJSON and Markdown you can `cat` and fix by hand.
|
|
31
33
|
|
|
34
|
+
## A look inside
|
|
35
|
+
|
|
36
|
+
Click any thumbnail for the full-size screenshot.
|
|
37
|
+
|
|
38
|
+
| Watch a run live | The review gate | Parallel variants |
|
|
39
|
+
|:--:|:--:|:--:|
|
|
40
|
+
| [](docs/screenshots/live-run.png) | [](docs/screenshots/review-gate.png) | [](docs/screenshots/variants-compare.png) |
|
|
41
|
+
| *Every step, tool call, token and screenshot — streamed as it happens.* | *Nothing auto-merges: inspect the diff, send notes into the same session, or push a draft PR.* | *Run a task ×2/×3 in isolated worktrees, compare the diffs, keep one.* |
|
|
42
|
+
| **Plan before you run** | **Workflow builder** | **GitHub, one click away** |
|
|
43
|
+
| [](docs/screenshots/plan-chain.png) | [](docs/screenshots/workflow-builder.png) | [](docs/screenshots/github-issues.png) |
|
|
44
|
+
| *Press **Plan first** and review the proposed chain — trim, reorder, then start.* | *Stitch skills and shell checks into a reusable YAML chain, no code.* | *Open issues and PRs via your `gh` — run the agent straight on an issue.* |
|
|
45
|
+
|
|
32
46
|
---
|
|
33
47
|
|
|
34
48
|
## What it solves
|
|
@@ -51,6 +65,10 @@ locally under *your* subscription, and a cockpit shows you exactly what it's doi
|
|
|
51
65
|
- **Losing a session when it fails.** Every run records its `claude` session id.
|
|
52
66
|
Take it over interactively in one click (`claude --resume <id>`), or continue it
|
|
53
67
|
in-process from the cockpit.
|
|
68
|
+
- **Locked into one agent vendor.** Most tools wed you to a single CLI. cezar
|
|
69
|
+
drives **Claude Code, Codex and OpenCode** through one runner seam — set a
|
|
70
|
+
default, pick a backend per task, or mix them inside one workflow (implement
|
|
71
|
+
with one agent, review with another). See [Agent backends](#coding-agent-backends).
|
|
54
72
|
- **Setup tax.** No wizard, no env vars, no schema. Skills are Markdown, workflows
|
|
55
73
|
are short YAML, and everything degrades: no `gh` → works without PRs, no network
|
|
56
74
|
→ local skills still load, no `.ai/skills` → the bare prompt still runs.
|
|
@@ -73,8 +91,10 @@ locally under *your* subscription, and a cockpit shows you exactly what it's doi
|
|
|
73
91
|
|
|
74
92
|
## Quick start
|
|
75
93
|
|
|
76
|
-
**Prerequisites:** Node 20+,
|
|
77
|
-
|
|
94
|
+
**Prerequisites:** Node 20+, at least one logged-in agent CLI — the
|
|
95
|
+
[`claude` CLI](https://github.com/anthropics/claude-code) (Pro/Max subscription),
|
|
96
|
+
the [`codex` CLI](https://github.com/openai/codex), or
|
|
97
|
+
[OpenCode](https://opencode.ai) — and, optionally, `git` and the `gh` CLI.
|
|
78
98
|
|
|
79
99
|
```bash
|
|
80
100
|
cd your-repo
|
|
@@ -91,8 +111,8 @@ npx cezar-cli init # scaffold .ai/cez
|
|
|
91
111
|
```
|
|
92
112
|
|
|
93
113
|
Both the `cezar` and `cez` commands are installed, so once it's on your PATH you
|
|
94
|
-
can run either. No API key is ever used — cezar shells out to
|
|
95
|
-
`claude`
|
|
114
|
+
can run either. No API key is ever used — cezar shells out to whichever agent
|
|
115
|
+
CLIs you are already logged into, `claude` by default.
|
|
96
116
|
|
|
97
117
|
> **Just kicking the tires?** Set `CEZ_DRY_RUN=1` to run against a bundled mock
|
|
98
118
|
> instead of the real CLI — the whole cockpit works with no `claude` login, so
|
|
@@ -103,8 +123,9 @@ can run either. No API key is ever used — cezar shells out to your logged-in
|
|
|
103
123
|
## How it works
|
|
104
124
|
|
|
105
125
|
You describe a task. cezar runs it as a **workflow** — an ordered list of agent
|
|
106
|
-
steps and shell checks — shelling out to your
|
|
107
|
-
|
|
126
|
+
steps and shell checks — shelling out to your locally installed agent CLI
|
|
127
|
+
(Claude Code by default; Codex and OpenCode are drop-in alternatives, per task
|
|
128
|
+
or per step). Each task gets its own git worktree; the cockpit streams every
|
|
108
129
|
event live and parks the run at a review gate when there's a diff to inspect.
|
|
109
130
|
|
|
110
131
|
```
|
|
@@ -117,15 +138,15 @@ event live and parks the run at a review gate when there's a diff to inspect.
|
|
|
117
138
|
│
|
|
118
139
|
▼
|
|
119
140
|
┌──────────────────────────────┐ ┌───────────────────────────────┐
|
|
120
|
-
│ git worktree per task │ │
|
|
121
|
-
│ (isolated branch, parallel) │◄───►│
|
|
122
|
-
└──────────────────────────────┘ │
|
|
141
|
+
│ git worktree per task │ │ agent CLI (your login) │
|
|
142
|
+
│ (isolated branch, parallel) │◄───►│ claude · codex · opencode │
|
|
143
|
+
└──────────────────────────────┘ │ default-deny · acceptEdits │
|
|
123
144
|
│ └───────────────────────────────┘
|
|
124
145
|
│ agent text · tool calls · tool results · tokens · cost
|
|
125
146
|
▼
|
|
126
147
|
┌─────────────┐ SSE (replay + live) ┌──────────────────────────┐
|
|
127
148
|
│ .ai/cezar/ │ ──────────────────────► │ cockpit localhost:4321 │
|
|
128
|
-
│ JSON·NDJSON │ │
|
|
149
|
+
│ JSON·NDJSON │ │ Tasks · Git · GitHub · │
|
|
129
150
|
│ ·Markdown │ │ Skills · Workflows │
|
|
130
151
|
└─────────────┘ └──────────────────────────┘
|
|
131
152
|
│
|
|
@@ -155,8 +176,8 @@ Three words, no jargon — **task**, **skill**, **chain**:
|
|
|
155
176
|
shape *how* the agent reasons without touching code.
|
|
156
177
|
- **Chains (workflows)** stitch steps into a pipeline: agent steps plus shell
|
|
157
178
|
checks, with bounded `onFail` retry loops. Write the YAML yourself, build one by
|
|
158
|
-
drag-ordering skills in the **Workflows** tab, or press **Plan** and let the
|
|
159
|
-
draft a chain for your task that you review, trim and start. The built-in
|
|
179
|
+
drag-ordering skills in the **Workflows** tab, or press **Plan first** and let the
|
|
180
|
+
AI draft a chain for your task that you review, trim and start. The built-in
|
|
160
181
|
`quick-task` (one agent step) works with zero setup.
|
|
161
182
|
|
|
162
183
|
Two moves that make the cockpit worth the browser tab:
|
|
@@ -172,19 +193,22 @@ Two moves that make the cockpit worth the browser tab:
|
|
|
172
193
|
|
|
173
194
|
## Cockpit tour
|
|
174
195
|
|
|
175
|
-
|
|
196
|
+
Seven views, one browser window, all live over Server-Sent Events:
|
|
176
197
|
|
|
177
|
-
|
|
|
198
|
+
| View | What's in it |
|
|
178
199
|
|---|---|
|
|
179
|
-
| **
|
|
200
|
+
| **Tasks** | Every task with its status, live event stream (agent text · tool calls · tool results · pasted/generated screenshots), tokens and cost. Continue, cancel, open in terminal (`claude --resume`), review the diff, or push a draft PR. |
|
|
180
201
|
| **Inbox** | Follow-ups an agent left behind (`todos.json`) — one click turns a suggestion into the next task, pre-wired to its suggested skill. |
|
|
181
|
-
| **
|
|
182
|
-
| **GitHub** | Open issues and PRs of the repo's origin, read through your logged-in `gh`.
|
|
202
|
+
| **Git** | Branch, working-tree status, diff vs HEAD, recent commits (click one for its inline patch + GitHub link), and the configurable base branch that worktrees fork from and PRs target. |
|
|
203
|
+
| **GitHub** | Open issues and PRs of the repo's origin, read through your logged-in `gh`. Hand an issue straight to the agent — pick a workflow and skills, one click runs it. |
|
|
183
204
|
| **Skills** | Local skills plus the team skills repo, with a rendered body + prompt preview. Refresh pulls the latest from the remote. |
|
|
184
205
|
| **Workflows** | Build a chain by drag-ordering skills, save it as portable YAML, import/export, or delete. Built-ins always come back. |
|
|
206
|
+
| **Settings** | Appearance (dark/light theme, accent, density), agent backends, notifications, and the skills catalog. |
|
|
185
207
|
|
|
186
|
-
The
|
|
187
|
-
|
|
208
|
+
The cockpit is a React app served pre-built from the package — `npx cezar-cli`
|
|
209
|
+
still means no build step and no dev server on your machine — with a dark/light
|
|
210
|
+
theme, a ⌘K command palette, and bookmarklets that launch a task straight from
|
|
211
|
+
a GitHub page.
|
|
188
212
|
|
|
189
213
|
---
|
|
190
214
|
|
|
@@ -201,6 +225,7 @@ steps:
|
|
|
201
225
|
prompt: "{{task}}"
|
|
202
226
|
skill: project-conventions # optional — from .ai/skills or .ai/cezar/skills
|
|
203
227
|
# model: opus # optional per-step model override
|
|
228
|
+
# runner: codex # optional per-step backend: claude · codex · opencode
|
|
204
229
|
# allowedTools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
205
230
|
- id: verify
|
|
206
231
|
name: Verify
|
|
@@ -226,11 +251,13 @@ skills: [reproduce, root-cause, implement, self-review]
|
|
|
226
251
|
|
|
227
252
|
## How it runs agents
|
|
228
253
|
|
|
229
|
-
cezar shells out to your locally installed, logged-in
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
(`--permission-mode acceptEdits`)
|
|
233
|
-
|
|
254
|
+
cezar shells out to your locally installed, logged-in agent CLI —
|
|
255
|
+
**your subscription, no API key**. With the default Claude Code backend that
|
|
256
|
+
means headless `stream-json` mode, tool access default-deny via
|
|
257
|
+
`--allowedTools`, and edits auto-accepted (`--permission-mode acceptEdits`)
|
|
258
|
+
inside the task's worktree. Codex and OpenCode are driven through their own
|
|
259
|
+
native protocols — see [Coding agent backends](#coding-agent-backends).
|
|
260
|
+
Nothing runs on a server you don't own.
|
|
234
261
|
|
|
235
262
|
Useful environment variables:
|
|
236
263
|
|
|
@@ -238,10 +265,62 @@ Useful environment variables:
|
|
|
238
265
|
|---|---|
|
|
239
266
|
| `CEZ_DRY_RUN=1` | Use the bundled mock instead of the real `claude` CLI — the entire cockpit works offline, for demos and development. |
|
|
240
267
|
| `CEZ_CLAUDE_BIN=/path/to/claude` | Override which `claude` binary is used. |
|
|
268
|
+
| `CEZ_CODEX_BIN=/path/to/codex` | Override which `codex` binary is used. |
|
|
269
|
+
| `CEZ_OPENCODE_BIN=/path/to/opencode` | Override which `opencode` binary is used. |
|
|
241
270
|
| `GITHUB_TOKEN` | Fallback for GitHub reads/PRs when `gh` isn't authenticated. |
|
|
242
271
|
|
|
243
272
|
---
|
|
244
273
|
|
|
274
|
+
## Coding agent backends
|
|
275
|
+
|
|
276
|
+
cezar is not married to one vendor. Every agent step runs through a single
|
|
277
|
+
`AgentRunner` seam with three built-in backends:
|
|
278
|
+
|
|
279
|
+
| Backend | CLI | How cezar drives it |
|
|
280
|
+
|---|---|---|
|
|
281
|
+
| **Claude Code** (default) | [`claude`](https://github.com/anthropics/claude-code) | Headless `stream-json` mode. |
|
|
282
|
+
| **Codex** | [`codex`](https://github.com/openai/codex) | `codex app-server` — JSON-RPC over stdio, the same transport the Codex IDE extensions use. |
|
|
283
|
+
| **OpenCode** | [`opencode`](https://opencode.ai) | `opencode serve` — a local HTTP server with an SSE event stream. |
|
|
284
|
+
|
|
285
|
+
On startup cezar probes which CLIs are installed and the cockpit only offers
|
|
286
|
+
the backends it found — install any one of the three and you're operational.
|
|
287
|
+
|
|
288
|
+
**Pick a backend at three levels** (most specific wins):
|
|
289
|
+
|
|
290
|
+
1. **Config default** — `"defaultRunner": "codex"` in `.ai/cezar/config.json`.
|
|
291
|
+
2. **Per task** — the backend picker next to the task box in the cockpit.
|
|
292
|
+
3. **Per workflow step** — `runner:` on any step in the YAML.
|
|
293
|
+
|
|
294
|
+
Per-step overrides are what make **mixed-agent strategies** a one-liner:
|
|
295
|
+
implement with one agent, review with another, and let a shell check referee:
|
|
296
|
+
|
|
297
|
+
```yaml
|
|
298
|
+
name: implement-and-cross-review
|
|
299
|
+
steps:
|
|
300
|
+
- id: implement
|
|
301
|
+
name: Implement
|
|
302
|
+
prompt: "{{task}}"
|
|
303
|
+
runner: codex # one vendor writes the code…
|
|
304
|
+
- id: review
|
|
305
|
+
name: Cross-review
|
|
306
|
+
prompt: "Review the diff produced for: {{task}}. Fix real issues only."
|
|
307
|
+
runner: claude # …another one reviews it
|
|
308
|
+
- id: verify
|
|
309
|
+
name: Verify
|
|
310
|
+
command: "npm test"
|
|
311
|
+
onFail: { retry: implement, max: 2 }
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Parallel variants (×2/×3) of one task share that task's backend — mixing
|
|
315
|
+
happens per task and per step, not inside a variant group.
|
|
316
|
+
|
|
317
|
+
The seam is deliberately small: a backend is one class implementing the
|
|
318
|
+
`AgentRunner` interface (`src/core/agent-runner.ts`) that turns a prompt into
|
|
319
|
+
a stream of normalized events. Other CLIs — pi, aider, whatever ships next —
|
|
320
|
+
can slot in the same way.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
245
324
|
## Configuration (optional)
|
|
246
325
|
|
|
247
326
|
Zero config is the default — everything below is opt-in via
|
|
@@ -252,8 +331,9 @@ never blocks startup):
|
|
|
252
331
|
{
|
|
253
332
|
"skillsRepos": [{ "repo": "open-mercato/skills", "ref": "main" }], // team skills; [] disables
|
|
254
333
|
"maxParallel": 2, // how many tasks may run at once (non-git dirs always run 1)
|
|
255
|
-
"
|
|
256
|
-
"
|
|
334
|
+
"defaultRunner": "claude", // agent backend: "claude" (default) · "codex" · "opencode"
|
|
335
|
+
"plannerModel": "sonnet", // model the "Plan first" button uses to draft chains
|
|
336
|
+
"baseBranch": "develop" // branch worktrees fork from + PRs target (also settable in the Git tab)
|
|
257
337
|
}
|
|
258
338
|
```
|
|
259
339
|
|
|
@@ -266,15 +346,22 @@ git-ignored automatically; your workflows and skills stay committable.
|
|
|
266
346
|
|
|
267
347
|
```bash
|
|
268
348
|
npm install
|
|
269
|
-
npm run dev #
|
|
270
|
-
npm run
|
|
271
|
-
npm run
|
|
349
|
+
npm run dev # server (API :4321) + Vite dev server, opens the cockpit in the browser
|
|
350
|
+
npm run dev:server # tsx src/index.ts — the API server alone
|
|
351
|
+
npm run dev:web # Vite dev server alone (proxies /api to :4321)
|
|
352
|
+
npm run build # tsc → dist/, vite build → web/dist/, then the pack gate
|
|
353
|
+
npm run typecheck # server + web (tsc --noEmit)
|
|
354
|
+
npm test # vitest — server + cockpit unit suites
|
|
355
|
+
npm run test:unit # node:test — fast core-module tests
|
|
356
|
+
npm run test:package # pack/install and exercise the built CLI
|
|
357
|
+
npm run test:e2e # real-browser cockpit suite (agent-browser)
|
|
272
358
|
```
|
|
273
359
|
|
|
274
360
|
The stack is deliberately small: **TypeScript** (strict, ESM), **Hono** + SSE for
|
|
275
|
-
the server, **Zod** at every boundary, **YAML** for workflows, and **
|
|
276
|
-
|
|
277
|
-
|
|
361
|
+
the server, **Zod** at every boundary, **YAML** for workflows, and a **React 19 +
|
|
362
|
+
Vite + Tailwind v4 + shadcn/ui** cockpit shipped pre-built in `web/dist/` — the
|
|
363
|
+
published package carries the built app, so `npx` users never run a bundler.
|
|
364
|
+
Every module is meant to be read in one sitting.
|
|
278
365
|
|
|
279
366
|
---
|
|
280
367
|
|
package/dist/config.d.ts
CHANGED
|
@@ -32,6 +32,13 @@ declare const configSchema: z.ZodObject<{
|
|
|
32
32
|
}>, "many">>;
|
|
33
33
|
/** How many tasks may run at once (spec 006). Non-git dirs always run 1. */
|
|
34
34
|
maxParallel: z.ZodDefault<z.ZodNumber>;
|
|
35
|
+
/**
|
|
36
|
+
* Per-task memory ceiling in MiB (whole process tree). When a running task's
|
|
37
|
+
* RSS crosses this the engine pauses it with a warning and lets the queue
|
|
38
|
+
* advance (#memory-guard). 0 / unset = no limit. `.catch(undefined)` keeps
|
|
39
|
+
* the key additive-safe: a bad value degrades to "no limit".
|
|
40
|
+
*/
|
|
41
|
+
memoryLimitMb: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
35
42
|
/**
|
|
36
43
|
* Which agent backend a task uses unless overridden per task (GUI) or per
|
|
37
44
|
* step (workflow). The GUI only offers runners actually installed; this is
|
|
@@ -45,6 +52,35 @@ declare const configSchema: z.ZodObject<{
|
|
|
45
52
|
* Unset = the branch currently checked out. Settable from the Repo tab.
|
|
46
53
|
*/
|
|
47
54
|
baseBranch: z.ZodOptional<z.ZodString>;
|
|
55
|
+
/**
|
|
56
|
+
* Default extra system prompt applied to every run's agent steps (claude:
|
|
57
|
+
* `--append-system-prompt`; codex/opencode: prepended to the opening user
|
|
58
|
+
* message — the AgentRunSpec seam handles the per-backend delivery).
|
|
59
|
+
* Settings is the single edit place; `POST /api/runs` can override it per
|
|
60
|
+
* run (`systemPrompt`). `.catch(undefined)` keeps the key additive-safe: a
|
|
61
|
+
* bad value degrades to unset without discarding the rest of the config.
|
|
62
|
+
*/
|
|
63
|
+
systemPrompt: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
64
|
+
/**
|
|
65
|
+
* Per-runner default model preset (Settings → Agents, redesign R6 1.5): the
|
|
66
|
+
* model id the composer preselects for that runner. Missing = auto (the
|
|
67
|
+
* runner decides). A capability (`model`), never a vendor config format.
|
|
68
|
+
* `.catch(undefined)` keeps the key additive-safe like `systemPrompt`: a
|
|
69
|
+
* bad value degrades to unset without discarding the rest of the config.
|
|
70
|
+
*/
|
|
71
|
+
defaultModels: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
72
|
+
claude: z.ZodOptional<z.ZodString>;
|
|
73
|
+
codex: z.ZodOptional<z.ZodString>;
|
|
74
|
+
opencode: z.ZodOptional<z.ZodString>;
|
|
75
|
+
}, "strip", z.ZodTypeAny, {
|
|
76
|
+
claude?: string | undefined;
|
|
77
|
+
codex?: string | undefined;
|
|
78
|
+
opencode?: string | undefined;
|
|
79
|
+
}, {
|
|
80
|
+
claude?: string | undefined;
|
|
81
|
+
codex?: string | undefined;
|
|
82
|
+
opencode?: string | undefined;
|
|
83
|
+
}>>>;
|
|
48
84
|
}, "strip", z.ZodTypeAny, {
|
|
49
85
|
skillsRepos: {
|
|
50
86
|
repo: string;
|
|
@@ -53,16 +89,26 @@ declare const configSchema: z.ZodObject<{
|
|
|
53
89
|
maxParallel: number;
|
|
54
90
|
defaultRunner: "claude" | "codex" | "opencode";
|
|
55
91
|
plannerModel: string;
|
|
92
|
+
memoryLimitMb?: number | undefined;
|
|
56
93
|
baseBranch?: string | undefined;
|
|
94
|
+
systemPrompt?: string | undefined;
|
|
95
|
+
defaultModels?: {
|
|
96
|
+
claude?: string | undefined;
|
|
97
|
+
codex?: string | undefined;
|
|
98
|
+
opencode?: string | undefined;
|
|
99
|
+
} | undefined;
|
|
57
100
|
}, {
|
|
58
101
|
skillsRepos?: {
|
|
59
102
|
repo: string;
|
|
60
103
|
ref?: string | undefined;
|
|
61
104
|
}[] | undefined;
|
|
62
105
|
maxParallel?: number | undefined;
|
|
106
|
+
memoryLimitMb?: unknown;
|
|
63
107
|
defaultRunner?: "claude" | "codex" | "opencode" | undefined;
|
|
64
108
|
plannerModel?: string | undefined;
|
|
65
109
|
baseBranch?: string | undefined;
|
|
110
|
+
systemPrompt?: unknown;
|
|
111
|
+
defaultModels?: unknown;
|
|
66
112
|
}>;
|
|
67
113
|
export type CezConfig = z.infer<typeof configSchema>;
|
|
68
114
|
/** Read `.ai/cezar/config.json` on demand — never cached, never throws. */
|
package/dist/config.js
CHANGED
|
@@ -19,6 +19,13 @@ const configSchema = z.object({
|
|
|
19
19
|
skillsRepos: z.array(skillsRepoSchema).default(DEFAULT_SKILLS_REPOS),
|
|
20
20
|
/** How many tasks may run at once (spec 006). Non-git dirs always run 1. */
|
|
21
21
|
maxParallel: z.number().int().min(1).max(16).default(2),
|
|
22
|
+
/**
|
|
23
|
+
* Per-task memory ceiling in MiB (whole process tree). When a running task's
|
|
24
|
+
* RSS crosses this the engine pauses it with a warning and lets the queue
|
|
25
|
+
* advance (#memory-guard). 0 / unset = no limit. `.catch(undefined)` keeps
|
|
26
|
+
* the key additive-safe: a bad value degrades to "no limit".
|
|
27
|
+
*/
|
|
28
|
+
memoryLimitMb: z.number().int().min(0).max(1_048_576).optional().catch(undefined),
|
|
22
29
|
/**
|
|
23
30
|
* Which agent backend a task uses unless overridden per task (GUI) or per
|
|
24
31
|
* step (workflow). The GUI only offers runners actually installed; this is
|
|
@@ -32,6 +39,30 @@ const configSchema = z.object({
|
|
|
32
39
|
* Unset = the branch currently checked out. Settable from the Repo tab.
|
|
33
40
|
*/
|
|
34
41
|
baseBranch: z.string().trim().min(1).optional(),
|
|
42
|
+
/**
|
|
43
|
+
* Default extra system prompt applied to every run's agent steps (claude:
|
|
44
|
+
* `--append-system-prompt`; codex/opencode: prepended to the opening user
|
|
45
|
+
* message — the AgentRunSpec seam handles the per-backend delivery).
|
|
46
|
+
* Settings is the single edit place; `POST /api/runs` can override it per
|
|
47
|
+
* run (`systemPrompt`). `.catch(undefined)` keeps the key additive-safe: a
|
|
48
|
+
* bad value degrades to unset without discarding the rest of the config.
|
|
49
|
+
*/
|
|
50
|
+
systemPrompt: z.string().trim().min(1).max(20_000).optional().catch(undefined),
|
|
51
|
+
/**
|
|
52
|
+
* Per-runner default model preset (Settings → Agents, redesign R6 1.5): the
|
|
53
|
+
* model id the composer preselects for that runner. Missing = auto (the
|
|
54
|
+
* runner decides). A capability (`model`), never a vendor config format.
|
|
55
|
+
* `.catch(undefined)` keeps the key additive-safe like `systemPrompt`: a
|
|
56
|
+
* bad value degrades to unset without discarding the rest of the config.
|
|
57
|
+
*/
|
|
58
|
+
defaultModels: z
|
|
59
|
+
.object({
|
|
60
|
+
claude: z.string().trim().min(1).max(200).optional(),
|
|
61
|
+
codex: z.string().trim().min(1).max(200).optional(),
|
|
62
|
+
opencode: z.string().trim().min(1).max(200).optional(),
|
|
63
|
+
})
|
|
64
|
+
.optional()
|
|
65
|
+
.catch(undefined),
|
|
35
66
|
});
|
|
36
67
|
/** Read `.ai/cezar/config.json` on demand — never cached, never throws. */
|
|
37
68
|
export async function loadConfig(repoRoot) {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,gFAAgF;IAChF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;CACvC,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,oBAAoB,GAAuB;IACtD,EAAE,IAAI,EAAE,qBAAqB,EAAE,GAAG,EAAE,MAAM,EAAE;CAC7C,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC;IACpE,4EAA4E;IAC5E,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACxE,2EAA2E;IAC3E,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACjD;;;OAGG;IACH,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - `codex` — `codex app-server`, JSON-RPC 2.0 (JSONL) over stdin/stdout;
|
|
11
11
|
* - `opencode` — `opencode serve`, HTTP + SSE.
|
|
12
12
|
*/
|
|
13
|
+
import type { UiEvent } from './ui-events.js';
|
|
13
14
|
/** `claude-cli` is the legacy id kept so old run records still parse. */
|
|
14
15
|
export type AgentBackend = 'claude' | 'codex' | 'opencode' | 'claude-cli';
|
|
15
16
|
/** The user-selectable runners (what config/GUI expose). */
|
|
@@ -47,6 +48,13 @@ export interface AgentRunSpec {
|
|
|
47
48
|
*/
|
|
48
49
|
resume?: boolean;
|
|
49
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Backends without a dedicated system-prompt channel (codex app-server,
|
|
53
|
+
* opencode serve) deliver `spec.systemPrompt` as a leading block of the
|
|
54
|
+
* opening user message — the documented per-backend mapping (spec §protocol
|
|
55
|
+
* v2: claude = `--append-system-prompt`, codex/opencode = prepended here).
|
|
56
|
+
*/
|
|
57
|
+
export declare function prependSystemPrompt(systemPrompt: string | undefined, userPrompt: string): string;
|
|
50
58
|
/** One content block of a user message — mirrors the Anthropic wire format
|
|
51
59
|
* so it can be written to the claude CLI's stdin verbatim. */
|
|
52
60
|
export type ContentBlock = {
|
|
@@ -123,6 +131,10 @@ export interface SessionOptions {
|
|
|
123
131
|
* behavior, used for non-interactive workflow steps). Interactive
|
|
124
132
|
* sessions omit this and control `end()` themselves. */
|
|
125
133
|
autoEndAfterFirstTurn?: boolean;
|
|
134
|
+
/** Protocol-v2 channel: receives the normalized `UiEvent` stream emitted
|
|
135
|
+
* ALONGSIDE the v1 `AgentEvent`s (additive — v1 keeps flowing unchanged).
|
|
136
|
+
* RunManager consumption lands in R2 step 2.1. */
|
|
137
|
+
onUiEvent?: (event: UiEvent) => void;
|
|
126
138
|
}
|
|
127
139
|
/**
|
|
128
140
|
* A live agent session over one spawned backend process. The process stays
|
|
@@ -133,6 +145,10 @@ export interface SessionOptions {
|
|
|
133
145
|
export interface AgentSession {
|
|
134
146
|
/** Resolves when the backend process exits — the session is fully over. */
|
|
135
147
|
result: Promise<AgentRunResult>;
|
|
148
|
+
/** OS pid of the spawned backend process — the root of the run's process
|
|
149
|
+
* tree (agents spawn Bash children under it). Absent when the spawn
|
|
150
|
+
* failed before a pid existed. Feeds live resource telemetry (#348). */
|
|
151
|
+
readonly pid?: number;
|
|
136
152
|
/** Write a user message into the live session. False when it is closed. */
|
|
137
153
|
sendMessage(content: ContentBlock[]): boolean;
|
|
138
154
|
/** Graceful close: end input, then a SIGTERM→SIGKILL watchdog. */
|
|
@@ -11,4 +11,13 @@
|
|
|
11
11
|
* - `opencode` — `opencode serve`, HTTP + SSE.
|
|
12
12
|
*/
|
|
13
13
|
export const RUNNER_IDS = ['claude', 'codex', 'opencode'];
|
|
14
|
+
/**
|
|
15
|
+
* Backends without a dedicated system-prompt channel (codex app-server,
|
|
16
|
+
* opencode serve) deliver `spec.systemPrompt` as a leading block of the
|
|
17
|
+
* opening user message — the documented per-backend mapping (spec §protocol
|
|
18
|
+
* v2: claude = `--append-system-prompt`, codex/opencode = prepended here).
|
|
19
|
+
*/
|
|
20
|
+
export function prependSystemPrompt(systemPrompt, userPrompt) {
|
|
21
|
+
return systemPrompt ? `${systemPrompt}\n\n---\n\n${userPrompt}` : userPrompt;
|
|
22
|
+
}
|
|
14
23
|
//# sourceMappingURL=agent-runner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-runner.js","sourceRoot":"","sources":["../../src/core/agent-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"agent-runner.js","sourceRoot":"","sources":["../../src/core/agent-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,MAAM,CAAC,MAAM,UAAU,GAAwB,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;AAmC/E;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAgC,EAAE,UAAkB;IACtF,OAAO,YAAY,CAAC,CAAC,CAAC,GAAG,YAAY,cAAc,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;AAC/E,CAAC"}
|
|
@@ -38,6 +38,12 @@ export declare class ClaudeCliRunner implements AgentRunner {
|
|
|
38
38
|
interrupt(): Promise<void>;
|
|
39
39
|
startSession(spec: AgentRunSpec, onEvent?: (event: AgentEvent) => void, opts?: SessionOptions): AgentSession;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Build the headless argv. `--input-format stream-json` reads user messages
|
|
43
|
+
* from stdin; `--output-format stream-json --verbose` gives per-event NDJSON;
|
|
44
|
+
* `--permission-mode acceptEdits` lets edits through without a TTY prompt.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildClaudeArgs(spec: AgentRunSpec): string[];
|
|
41
47
|
/**
|
|
42
48
|
* Map `allowedTools` onto claude's `--allowedTools` syntax. `Bash` with a
|
|
43
49
|
* `bashAllowlist` becomes one `Bash(<prefix>:*)` entry per allowed prefix;
|
|
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import { dirname, resolve as resolvePath } from 'node:path';
|
|
4
4
|
import { costWeightedTokens } from './usage.js';
|
|
5
5
|
import { readNdjson } from './ndjson.js';
|
|
6
|
+
import { claudeTurnStarted, createClaudeUiState, mapClaudeMessage, stringifyToolResultContent, toolResultImageBlocks, } from './claude-ui-mapper.js';
|
|
6
7
|
/** Default wall-clock cap for a single run before SIGTERM → SIGKILL.
|
|
7
8
|
* Interactive sessions pass `timeoutMs: 0` to disable it entirely. */
|
|
8
9
|
export const DEFAULT_RUN_TIMEOUT_MS = 30 * 60_000;
|
|
@@ -57,6 +58,24 @@ export class ClaudeCliRunner {
|
|
|
57
58
|
let autoEndTimer;
|
|
58
59
|
let eofTermTimer;
|
|
59
60
|
let eofKillTimer;
|
|
61
|
+
// Protocol v2 emission — additive alongside v1 (`onEvent` keeps flowing
|
|
62
|
+
// byte-identical); the channel is `opts.onUiEvent` (RunManager wiring
|
|
63
|
+
// lands in R2 step 2.1). The mapper never throws, but a defect in it
|
|
64
|
+
// must still never disturb the v1 stream — hence the belt-and-braces try.
|
|
65
|
+
let uiState = createClaudeUiState({ fallbackSessionId: spec.sessionId });
|
|
66
|
+
const emitUi = (map) => {
|
|
67
|
+
try {
|
|
68
|
+
const mapped = map(uiState);
|
|
69
|
+
uiState = mapped.state;
|
|
70
|
+
if (opts.onUiEvent) {
|
|
71
|
+
for (const event of mapped.events)
|
|
72
|
+
opts.onUiEvent(event);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// v2 mapping is best-effort; v1 consumers stay unaffected.
|
|
77
|
+
}
|
|
78
|
+
};
|
|
60
79
|
const sendMessage = (content) => {
|
|
61
80
|
if (!stdinOpen)
|
|
62
81
|
return false;
|
|
@@ -72,6 +91,8 @@ export class ClaudeCliRunner {
|
|
|
72
91
|
});
|
|
73
92
|
try {
|
|
74
93
|
child.stdin.write(`${line}\n`);
|
|
94
|
+
// Each user message written to stdin begins a turn (§7.1).
|
|
95
|
+
emitUi(claudeTurnStarted);
|
|
75
96
|
return true;
|
|
76
97
|
}
|
|
77
98
|
catch (err) {
|
|
@@ -151,6 +172,7 @@ export class ClaudeCliRunner {
|
|
|
151
172
|
onEvent?.({ type: 'note', message: `claude: skipped unparseable stream line: ${truncate(line)}` });
|
|
152
173
|
continue;
|
|
153
174
|
}
|
|
175
|
+
emitUi((state) => mapClaudeMessage(msg, state));
|
|
154
176
|
let delta = 0;
|
|
155
177
|
try {
|
|
156
178
|
delta = handleClaudeMessage(msg, { toolCalls, textChunks, onEvent });
|
|
@@ -224,6 +246,7 @@ export class ClaudeCliRunner {
|
|
|
224
246
|
sendMessage,
|
|
225
247
|
end,
|
|
226
248
|
interrupt,
|
|
249
|
+
pid: child.pid,
|
|
227
250
|
get open() {
|
|
228
251
|
return stdinOpen;
|
|
229
252
|
},
|
|
@@ -237,7 +260,7 @@ export class ClaudeCliRunner {
|
|
|
237
260
|
* from stdin; `--output-format stream-json --verbose` gives per-event NDJSON;
|
|
238
261
|
* `--permission-mode acceptEdits` lets edits through without a TTY prompt.
|
|
239
262
|
*/
|
|
240
|
-
function buildClaudeArgs(spec) {
|
|
263
|
+
export function buildClaudeArgs(spec) {
|
|
241
264
|
const args = [
|
|
242
265
|
'--input-format',
|
|
243
266
|
'stream-json',
|
|
@@ -325,12 +348,12 @@ function handleClaudeMessage(msg, ctx) {
|
|
|
325
348
|
ctx.onEvent?.({
|
|
326
349
|
type: 'tool-result',
|
|
327
350
|
toolCallId: b.tool_use_id,
|
|
328
|
-
result:
|
|
351
|
+
result: stringifyToolResultContent(b.content),
|
|
329
352
|
isError: b.is_error === true,
|
|
330
353
|
});
|
|
331
354
|
// Screenshots and other images inside the result get their own
|
|
332
355
|
// events — the text path above renders them as a placeholder.
|
|
333
|
-
for (const img of
|
|
356
|
+
for (const img of toolResultImageBlocks(b.content)) {
|
|
334
357
|
ctx.onEvent?.({ type: 'image', mediaType: img.media_type, data: img.data });
|
|
335
358
|
}
|
|
336
359
|
}
|
|
@@ -355,46 +378,7 @@ function handleClaudeMessage(msg, ctx) {
|
|
|
355
378
|
// system/init and anything else: nothing actionable.
|
|
356
379
|
return 0;
|
|
357
380
|
}
|
|
358
|
-
|
|
359
|
-
if (typeof content === 'string')
|
|
360
|
-
return content;
|
|
361
|
-
if (Array.isArray(content)) {
|
|
362
|
-
return content
|
|
363
|
-
.map((c) => {
|
|
364
|
-
const b = c;
|
|
365
|
-
if (b.type === 'text' && typeof b.text === 'string')
|
|
366
|
-
return b.text;
|
|
367
|
-
if (b.type === 'image')
|
|
368
|
-
return '[screenshot]'; // emitted as its own image event
|
|
369
|
-
try {
|
|
370
|
-
return JSON.stringify(b);
|
|
371
|
-
}
|
|
372
|
-
catch {
|
|
373
|
-
return String(b);
|
|
374
|
-
}
|
|
375
|
-
})
|
|
376
|
-
.join('\n');
|
|
377
|
-
}
|
|
378
|
-
try {
|
|
379
|
-
return JSON.stringify(content);
|
|
380
|
-
}
|
|
381
|
-
catch {
|
|
382
|
-
return String(content);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
/** base64 image blocks inside a tool_result's content, if any. */
|
|
386
|
-
function imageBlocks(content) {
|
|
387
|
-
if (!Array.isArray(content))
|
|
388
|
-
return [];
|
|
389
|
-
const out = [];
|
|
390
|
-
for (const c of content) {
|
|
391
|
-
const b = c;
|
|
392
|
-
if (b.type === 'image' && b.source?.type === 'base64' && b.source.media_type && b.source.data) {
|
|
393
|
-
out.push({ media_type: b.source.media_type, data: b.source.data });
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return out;
|
|
397
|
-
}
|
|
381
|
+
// stringify/image helpers moved to claude-ui-mapper.ts (shared by v1 and v2).
|
|
398
382
|
// ---- subprocess plumbing --------------------------------------------------
|
|
399
383
|
function waitForExit(child) {
|
|
400
384
|
if (child.exitCode != null)
|