@pat-lewczuk/cezar 0.1.2 → 0.1.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 (42) hide show
  1. package/README.md +249 -45
  2. package/dist/config.d.ts +8 -0
  3. package/dist/config.js +6 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/core/agent-runner.d.ts +44 -2
  6. package/dist/core/agent-runner.js +8 -2
  7. package/dist/core/agent-runner.js.map +1 -1
  8. package/dist/core/backend-detect.d.ts +6 -4
  9. package/dist/core/backend-detect.js +44 -4
  10. package/dist/core/backend-detect.js.map +1 -1
  11. package/dist/core/claude-cli-runner.d.ts +3 -25
  12. package/dist/core/claude-cli-runner.js +2 -19
  13. package/dist/core/claude-cli-runner.js.map +1 -1
  14. package/dist/core/codex-app-server-runner.d.ts +29 -0
  15. package/dist/core/codex-app-server-runner.js +452 -0
  16. package/dist/core/codex-app-server-runner.js.map +1 -0
  17. package/dist/core/ndjson.d.ts +6 -0
  18. package/dist/core/ndjson.js +23 -0
  19. package/dist/core/ndjson.js.map +1 -0
  20. package/dist/core/opencode-server-runner.d.ts +30 -0
  21. package/dist/core/opencode-server-runner.js +451 -0
  22. package/dist/core/opencode-server-runner.js.map +1 -0
  23. package/dist/core/runner-factory.d.ts +8 -0
  24. package/dist/core/runner-factory.js +22 -0
  25. package/dist/core/runner-factory.js.map +1 -0
  26. package/dist/planner.js +7 -3
  27. package/dist/planner.js.map +1 -1
  28. package/dist/runs/store.d.ts +5 -0
  29. package/dist/runs/store.js +3 -0
  30. package/dist/runs/store.js.map +1 -1
  31. package/dist/server/server.js +34 -10
  32. package/dist/server/server.js.map +1 -1
  33. package/dist/workflows/run.d.ts +3 -0
  34. package/dist/workflows/run.js +22 -7
  35. package/dist/workflows/run.js.map +1 -1
  36. package/dist/workflows/types.d.ts +16 -0
  37. package/dist/workflows/types.js +3 -1
  38. package/dist/workflows/types.js.map +1 -1
  39. package/package.json +1 -1
  40. package/web/app.js +145 -18
  41. package/web/index.html +1 -0
  42. package/web/style.css +28 -8
@@ -14,6 +14,8 @@ export declare const workflowStepSchema: z.ZodEffects<z.ZodObject<{
14
14
  prompt: z.ZodOptional<z.ZodString>;
15
15
  skill: z.ZodOptional<z.ZodString>;
16
16
  model: z.ZodOptional<z.ZodString>;
17
+ /** Per-step agent backend override (falls back to the task / config default). */
18
+ runner: z.ZodOptional<z.ZodEnum<["claude", "codex", "opencode"]>>;
17
19
  allowedTools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
18
20
  bashAllowlist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
19
21
  command: z.ZodOptional<z.ZodString>;
@@ -31,6 +33,7 @@ export declare const workflowStepSchema: z.ZodEffects<z.ZodObject<{
31
33
  id: string;
32
34
  name?: string | undefined;
33
35
  model?: string | undefined;
36
+ runner?: "claude" | "codex" | "opencode" | undefined;
34
37
  skill?: string | undefined;
35
38
  command?: string | undefined;
36
39
  prompt?: string | undefined;
@@ -44,6 +47,7 @@ export declare const workflowStepSchema: z.ZodEffects<z.ZodObject<{
44
47
  id: string;
45
48
  name?: string | undefined;
46
49
  model?: string | undefined;
50
+ runner?: "claude" | "codex" | "opencode" | undefined;
47
51
  skill?: string | undefined;
48
52
  command?: string | undefined;
49
53
  prompt?: string | undefined;
@@ -57,6 +61,7 @@ export declare const workflowStepSchema: z.ZodEffects<z.ZodObject<{
57
61
  id: string;
58
62
  name?: string | undefined;
59
63
  model?: string | undefined;
64
+ runner?: "claude" | "codex" | "opencode" | undefined;
60
65
  skill?: string | undefined;
61
66
  command?: string | undefined;
62
67
  prompt?: string | undefined;
@@ -70,6 +75,7 @@ export declare const workflowStepSchema: z.ZodEffects<z.ZodObject<{
70
75
  id: string;
71
76
  name?: string | undefined;
72
77
  model?: string | undefined;
78
+ runner?: "claude" | "codex" | "opencode" | undefined;
73
79
  skill?: string | undefined;
74
80
  command?: string | undefined;
75
81
  prompt?: string | undefined;
@@ -94,6 +100,8 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
94
100
  prompt: z.ZodOptional<z.ZodString>;
95
101
  skill: z.ZodOptional<z.ZodString>;
96
102
  model: z.ZodOptional<z.ZodString>;
103
+ /** Per-step agent backend override (falls back to the task / config default). */
104
+ runner: z.ZodOptional<z.ZodEnum<["claude", "codex", "opencode"]>>;
97
105
  allowedTools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
98
106
  bashAllowlist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
99
107
  command: z.ZodOptional<z.ZodString>;
@@ -111,6 +119,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
111
119
  id: string;
112
120
  name?: string | undefined;
113
121
  model?: string | undefined;
122
+ runner?: "claude" | "codex" | "opencode" | undefined;
114
123
  skill?: string | undefined;
115
124
  command?: string | undefined;
116
125
  prompt?: string | undefined;
@@ -124,6 +133,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
124
133
  id: string;
125
134
  name?: string | undefined;
126
135
  model?: string | undefined;
136
+ runner?: "claude" | "codex" | "opencode" | undefined;
127
137
  skill?: string | undefined;
128
138
  command?: string | undefined;
129
139
  prompt?: string | undefined;
@@ -137,6 +147,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
137
147
  id: string;
138
148
  name?: string | undefined;
139
149
  model?: string | undefined;
150
+ runner?: "claude" | "codex" | "opencode" | undefined;
140
151
  skill?: string | undefined;
141
152
  command?: string | undefined;
142
153
  prompt?: string | undefined;
@@ -150,6 +161,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
150
161
  id: string;
151
162
  name?: string | undefined;
152
163
  model?: string | undefined;
164
+ runner?: "claude" | "codex" | "opencode" | undefined;
153
165
  skill?: string | undefined;
154
166
  command?: string | undefined;
155
167
  prompt?: string | undefined;
@@ -167,6 +179,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
167
179
  id: string;
168
180
  name?: string | undefined;
169
181
  model?: string | undefined;
182
+ runner?: "claude" | "codex" | "opencode" | undefined;
170
183
  skill?: string | undefined;
171
184
  command?: string | undefined;
172
185
  prompt?: string | undefined;
@@ -185,6 +198,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
185
198
  id: string;
186
199
  name?: string | undefined;
187
200
  model?: string | undefined;
201
+ runner?: "claude" | "codex" | "opencode" | undefined;
188
202
  skill?: string | undefined;
189
203
  command?: string | undefined;
190
204
  prompt?: string | undefined;
@@ -203,6 +217,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
203
217
  id: string;
204
218
  name?: string | undefined;
205
219
  model?: string | undefined;
220
+ runner?: "claude" | "codex" | "opencode" | undefined;
206
221
  skill?: string | undefined;
207
222
  command?: string | undefined;
208
223
  prompt?: string | undefined;
@@ -221,6 +236,7 @@ export declare const workflowFileSchema: z.ZodEffects<z.ZodObject<{
221
236
  id: string;
222
237
  name?: string | undefined;
223
238
  model?: string | undefined;
239
+ runner?: "claude" | "codex" | "opencode" | undefined;
224
240
  skill?: string | undefined;
225
241
  command?: string | undefined;
226
242
  prompt?: string | undefined;
@@ -16,6 +16,8 @@ export const workflowStepSchema = z
16
16
  prompt: z.string().optional(),
17
17
  skill: z.string().optional(),
18
18
  model: z.string().optional(),
19
+ /** Per-step agent backend override (falls back to the task / config default). */
20
+ runner: z.enum(['claude', 'codex', 'opencode']).optional(),
19
21
  allowedTools: z.array(z.string()).optional(),
20
22
  bashAllowlist: z.array(z.string()).optional(),
21
23
  // check step
@@ -79,7 +81,7 @@ export function skillStackOf(steps) {
79
81
  return null;
80
82
  if (s.name !== undefined && s.name !== s.skill)
81
83
  return null;
82
- if (s.model || s.allowedTools || s.bashAllowlist || s.onFail)
84
+ if (s.model || s.runner || s.allowedTools || s.bashAllowlist || s.onFail)
83
85
  return null;
84
86
  skills.push(s.skill);
85
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workflows/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workflows/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pat-lewczuk/cezar",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Local cockpit for running and tracking AI agent tasks in your repo. Uses your logged-in `claude` CLI and `gh` — zero config, zero database.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/web/app.js CHANGED
@@ -16,7 +16,9 @@ const state = {
16
16
  taskSource: 'workflow', // 'workflow' (a chain) | 'skill' — what taskRef names
17
17
  taskRef: null, // selected chain/skill name (the "≡ fix-and-verify" pill)
18
18
  taskModel: '', // model override ('' = auto; the "⊞ auto" pill)
19
- modelsAvailable: false, // claude CLI detected → model menu lists Claude models
19
+ modelsAvailable: false, // agent CLI detected → model menu lists that runner's models
20
+ taskRunner: 'claude', // selected agent backend (the "⚙ claude" pill)
21
+ runnersAvailable: ['claude'], // runner ids detected on the host (from /api/health)
20
22
  srcMenuTab: 'workflow', // Workflows | Skills tab inside the source pill menu
21
23
  srcMenuQuery: '', // search box inside the source pill menu
22
24
  pendingImages: [], // [{mediaType, data, preview}] queued for the next message
@@ -437,6 +439,7 @@ const PILL_ICONS = {
437
439
  chain: 'M4 6h16M4 12h16M4 18h10', // ≡ list
438
440
  skill: 'M4 19.5A2.5 2.5 0 016.5 17H20M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z', // book
439
441
  model: 'M9 3v3M15 3v3M9 18v3M15 18v3M3 9h3M3 15h3M18 9h3M18 15h3M6 6h12v12H6z', // chip
442
+ runner: 'M12 2l9 5v10l-9 5-9-5V7l9-5zM12 2v20M3 7l9 5 9-5', // 3D box (backend/engine)
440
443
  };
441
444
 
442
445
  /* Small inline icons for the run-detail action bar (design: every action has
@@ -526,7 +529,33 @@ function renderTaskPills() {
526
529
  srcMenu,
527
530
  );
528
531
 
529
- const models = state.modelsAvailable ? CLAUDE_MODELS : CLAUDE_MODELS.slice(0, 1);
532
+ // Runner pill only shown when more than one backend is installed, so a
533
+ // single-runner (claude-only) host stays as simple as before.
534
+ const runnerPill = $('#runner-pill');
535
+ if (runnerPill) {
536
+ const runners = RUNNERS.filter((r) => state.runnersAvailable.includes(r.id));
537
+ if (runners.length > 1) {
538
+ runnerPill.hidden = false;
539
+ const selected = runners.find((r) => r.id === state.taskRunner) ?? runners[0];
540
+ const runnerMenu = runners
541
+ .map((r) =>
542
+ menuItemHtml(
543
+ `data-mi="runner" data-value="${esc(r.id)}"`,
544
+ r.label,
545
+ r.desc,
546
+ r.id === selected.id,
547
+ PILL_ICONS.runner,
548
+ ),
549
+ )
550
+ .join('');
551
+ runnerPill.innerHTML = pillHtml(PILL_ICONS.runner, selected.label, runnerMenu);
552
+ } else {
553
+ runnerPill.hidden = true;
554
+ }
555
+ }
556
+
557
+ const all = modelsForRunner(state.taskRunner);
558
+ const models = state.modelsAvailable ? all : all.slice(0, 1);
530
559
  const selectedModel = models.find((m) => m.id === state.taskModel) ?? models[0];
531
560
  const modelMenu = models
532
561
  .map((m) =>
@@ -564,25 +593,64 @@ function renderChrome(health) {
564
593
  )
565
594
  .join('');
566
595
 
567
- // Model pill fed by what's actually connected: claude CLI present → the
568
- // Claude catalog; otherwise only "auto".
569
- state.modelsAvailable = health.checks.some((c) => c.name === 'claude' && c.available);
596
+ // Which agent backends are installed which runners the pill offers.
597
+ const available = RUNNERS.map((r) => r.id).filter((id) =>
598
+ health.checks.some((c) => c.name === id && c.available),
599
+ );
600
+ state.runnersAvailable = available.length ? available : ['claude'];
601
+ // Preselect the configured default when it's installed, else the first
602
+ // available runner.
603
+ const preferred = health.defaultRunner;
604
+ if (preferred && state.runnersAvailable.includes(preferred)) {
605
+ state.taskRunner = preferred;
606
+ } else if (!state.runnersAvailable.includes(state.taskRunner)) {
607
+ state.taskRunner = state.runnersAvailable[0];
608
+ }
609
+ // Model pill is populated only when the selected runner's CLI is present;
610
+ // otherwise it collapses to "auto".
611
+ state.modelsAvailable = state.runnersAvailable.includes(state.taskRunner);
570
612
  renderTaskPills();
571
613
  }
572
614
 
573
- /* What `claude --model` accepts: "auto" (no flag each step decides), the
574
- stable tier aliases (they track the newest release), then current pinned
575
- versions for reproducible runs. */
576
- const CLAUDE_MODELS = [
577
- { id: '', label: 'auto', desc: 'Pick the best model per step' },
578
- { id: 'opus', label: 'opus', desc: 'Deep reasoning for hard tasks' },
579
- { id: 'sonnet', label: 'sonnet', desc: 'Fast and cheap' },
580
- { id: 'haiku', label: 'haiku', desc: 'Fastest — simple, scoped tasks' },
581
- { id: 'claude-opus-4-8', label: 'Opus 4.8', desc: 'Pinned version' },
582
- { id: 'claude-sonnet-5', label: 'Sonnet 5', desc: 'Pinned version' },
583
- { id: 'claude-haiku-4-5', label: 'Haiku 4.5', desc: 'Pinned version' },
615
+ /* The selectable agent backends. Only those actually installed on the host
616
+ (detected via /api/health checks) are offered in the pill. */
617
+ const RUNNERS = [
618
+ { id: 'claude', label: 'claude', desc: 'Claude Code CLI' },
619
+ { id: 'codex', label: 'codex', desc: 'OpenAI Codex (app-server)' },
620
+ { id: 'opencode', label: 'opencode', desc: 'OpenCode (serve)' },
584
621
  ];
585
622
 
623
+ /* Model presets per runner. `id: ''` is always "auto" — no model flag, the
624
+ runner decides. Claude takes tier aliases + pinned versions; Codex takes
625
+ gpt-*-codex ids; OpenCode takes `provider/model` ids. */
626
+ const MODELS_BY_RUNNER = {
627
+ claude: [
628
+ { id: '', label: 'auto', desc: 'Pick the best model per step' },
629
+ { id: 'opus', label: 'opus', desc: 'Deep reasoning for hard tasks' },
630
+ { id: 'sonnet', label: 'sonnet', desc: 'Fast and cheap' },
631
+ { id: 'haiku', label: 'haiku', desc: 'Fastest — simple, scoped tasks' },
632
+ { id: 'claude-opus-4-8', label: 'Opus 4.8', desc: 'Pinned version' },
633
+ { id: 'claude-sonnet-5', label: 'Sonnet 5', desc: 'Pinned version' },
634
+ { id: 'claude-haiku-4-5', label: 'Haiku 4.5', desc: 'Pinned version' },
635
+ ],
636
+ codex: [
637
+ { id: '', label: 'auto', desc: 'Use your Codex default model' },
638
+ { id: 'gpt-5.1-codex', label: 'gpt-5.1-codex', desc: 'Codex-tuned' },
639
+ { id: 'gpt-5.1-codex-mini', label: 'gpt-5.1-codex-mini', desc: 'Faster, cheaper' },
640
+ { id: 'gpt-5-codex', label: 'gpt-5-codex', desc: 'Previous generation' },
641
+ ],
642
+ opencode: [
643
+ { id: '', label: 'auto', desc: 'Use your OpenCode default model' },
644
+ { id: 'anthropic/claude-sonnet-4-5', label: 'claude-sonnet-4.5', desc: 'via Anthropic' },
645
+ { id: 'openai/gpt-5.1', label: 'gpt-5.1', desc: 'via OpenAI' },
646
+ { id: 'openai/gpt-5.1-codex', label: 'gpt-5.1-codex', desc: 'via OpenAI' },
647
+ ],
648
+ };
649
+
650
+ function modelsForRunner(runner) {
651
+ return MODELS_BY_RUNNER[runner] ?? MODELS_BY_RUNNER.claude;
652
+ }
653
+
586
654
  /* The global stream replays nothing after a drop (laptop sleep, server
587
655
  restart) — a missed `run` event would leave a stale status in the sidebar
588
656
  forever (seen live: a run resting at `review` still listed under Working).
@@ -720,6 +788,10 @@ function bindUi() {
720
788
  state.taskRef = item.dataset.value;
721
789
  } else if (item.dataset.mi === 'model') {
722
790
  state.taskModel = item.dataset.value;
791
+ } else if (item.dataset.mi === 'runner') {
792
+ // Switching backend invalidates the model list — reset to auto.
793
+ state.taskRunner = item.dataset.value;
794
+ state.taskModel = '';
723
795
  }
724
796
  closePillMenus();
725
797
  renderTaskPills();
@@ -864,6 +936,7 @@ function bindUi() {
864
936
  const body = {
865
937
  task: form.task.value.trim(),
866
938
  model: state.taskModel || undefined,
939
+ runner: state.runnersAvailable.length > 1 ? state.taskRunner : undefined,
867
940
  variants: state.variants > 1 ? state.variants : undefined,
868
941
  images: state.taskImages.length
869
942
  ? state.taskImages.map((i) => ({ mediaType: i.mediaType, data: i.data }))
@@ -1649,6 +1722,7 @@ function updateDetail(run) {
1649
1722
  run.costUsd ? esc(fmtCost(run.costUsd)) : null,
1650
1723
  `started ${esc(timeAgo(run.startedAt ?? run.createdAt))}`,
1651
1724
  run.finishedAt ? `finished ${esc(timeAgo(run.finishedAt))}` : null,
1725
+ run.runner && run.runner !== 'claude' ? `runner ${esc(run.runner)}` : null,
1652
1726
  run.model ? `model ${esc(run.model)}` : null,
1653
1727
  run.branch ? esc(run.branch) : null,
1654
1728
  prLink(run) || null,
@@ -1892,6 +1966,53 @@ function toolArg(input) {
1892
1966
  return null;
1893
1967
  }
1894
1968
 
1969
+ /* Claude-Code-style collapsing for tool streaks: consecutive tool calls /
1970
+ results stack in one group — the last few stay visible, older ones fold
1971
+ under a "\u25b8 N earlier tool calls" toggle. Any other event breaks the
1972
+ streak. Selecting another run clears the log, which disconnects the group —
1973
+ the isConnected check below then starts a fresh one. */
1974
+ const STREAK_TAIL = 3;
1975
+ let toolStreak = null; // { wrap, toggle, hiddenBox, tailBox }
1976
+
1977
+ function streakLabel(hiddenBox) {
1978
+ const n = hiddenBox.children.length;
1979
+ return `${hiddenBox.hidden ? '\u25b8' : '\u25be'} ${n} earlier tool call${n === 1 ? '' : 's'}`;
1980
+ }
1981
+
1982
+ function newToolStreak(inner) {
1983
+ const wrap = document.createElement('div');
1984
+ wrap.className = 'ev tool-streak';
1985
+ const toggle = document.createElement('button');
1986
+ toggle.type = 'button';
1987
+ toggle.className = 'streak-toggle';
1988
+ toggle.hidden = true;
1989
+ const hiddenBox = document.createElement('div');
1990
+ hiddenBox.className = 'streak-box';
1991
+ hiddenBox.hidden = true;
1992
+ const tailBox = document.createElement('div');
1993
+ tailBox.className = 'streak-box';
1994
+ toggle.addEventListener('click', () => {
1995
+ hiddenBox.hidden = !hiddenBox.hidden;
1996
+ toggle.textContent = streakLabel(hiddenBox);
1997
+ });
1998
+ wrap.append(toggle, hiddenBox, tailBox);
1999
+ inner.appendChild(wrap);
2000
+ return { wrap, toggle, hiddenBox, tailBox };
2001
+ }
2002
+
2003
+ function appendToStreak(inner, el) {
2004
+ if (!toolStreak || !toolStreak.wrap.isConnected) toolStreak = newToolStreak(inner);
2005
+ toolStreak.tailBox.appendChild(el);
2006
+ // Overflow rolls the oldest visible chip into the folded box (order kept).
2007
+ while (toolStreak.tailBox.children.length > STREAK_TAIL) {
2008
+ toolStreak.hiddenBox.appendChild(toolStreak.tailBox.firstElementChild);
2009
+ }
2010
+ if (toolStreak.hiddenBox.children.length > 0) {
2011
+ toolStreak.toggle.hidden = false;
2012
+ toolStreak.toggle.textContent = streakLabel(toolStreak.hiddenBox);
2013
+ }
2014
+ }
2015
+
1895
2016
  function appendLog(evt) {
1896
2017
  const inner = $('#log-inner');
1897
2018
  const log = $('#log');
@@ -1985,7 +2106,12 @@ function appendLog(evt) {
1985
2106
  el.textContent = JSON.stringify(evt);
1986
2107
  }
1987
2108
 
1988
- inner.appendChild(el);
2109
+ if (evt.type === 'tool-call' || evt.type === 'tool-result') {
2110
+ appendToStreak(inner, el);
2111
+ } else {
2112
+ toolStreak = null; // anything else breaks the streak
2113
+ inner.appendChild(el);
2114
+ }
1989
2115
  if (state.autoScroll) log.scrollTop = log.scrollHeight;
1990
2116
  }
1991
2117
 
@@ -2725,7 +2851,7 @@ function wbSkillStack(steps) {
2725
2851
  if (s.command || !s.skill) return null;
2726
2852
  if (s.prompt !== undefined && s.prompt !== '{{task}}') return null;
2727
2853
  if (s.name !== undefined && s.name !== s.skill) return null;
2728
- if (s.model || s.allowedTools || s.bashAllowlist || s.onFail) return null;
2854
+ if (s.model || s.runner || s.allowedTools || s.bashAllowlist || s.onFail) return null;
2729
2855
  skills.push(s.skill);
2730
2856
  }
2731
2857
  return skills.length ? skills : null;
@@ -2764,6 +2890,7 @@ function wbYamlText() {
2764
2890
  if (s.skill) lines.push(` skill: ${yamlScalar(s.skill)}`);
2765
2891
  if (s.prompt) lines.push(...yamlBlock('prompt', s.prompt, 4));
2766
2892
  if (s.model) lines.push(` model: ${yamlScalar(s.model)}`);
2893
+ if (s.runner) lines.push(` runner: ${yamlScalar(s.runner)}`);
2767
2894
  if (s.allowedTools) lines.push(` allowedTools: [${s.allowedTools.map(yamlScalar).join(', ')}]`);
2768
2895
  if (s.bashAllowlist) lines.push(` bashAllowlist: [${s.bashAllowlist.map(yamlScalar).join(', ')}]`);
2769
2896
  if (s.command) lines.push(...yamlBlock('command', s.command, 4));
package/web/index.html CHANGED
@@ -36,6 +36,7 @@
36
36
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"><path d="M12 3l2 5.5L19.5 10 14 12l-2 5.5L10 12l-5.5-2L10 8.5 12 3z"/></svg>
37
37
  Plan
38
38
  </button>
39
+ <div class="pill-select" id="runner-pill" data-pill="runner" hidden></div>
39
40
  <div class="pill-select" id="model-pill" data-pill="model"></div>
40
41
  <button type="submit" id="run-btn" class="tip-right" data-tip="Run the task (⌘↵)">
41
42
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M12 19V5M6 11l6-6 6 6"/></svg>
package/web/style.css CHANGED
@@ -229,8 +229,8 @@ summary::-webkit-details-marker { display: none; }
229
229
  /* ---- sidebar ---- */
230
230
 
231
231
  #sidebar {
232
- width: 288px;
233
- min-width: 288px;
232
+ width: 318px;
233
+ min-width: 318px;
234
234
  display: flex;
235
235
  flex-direction: column;
236
236
  min-height: 0;
@@ -291,11 +291,11 @@ summary::-webkit-details-marker { display: none; }
291
291
  #task-thumbs img { height: 38px; border-radius: 7px; border: 1px solid var(--line2); display: block; }
292
292
  #task-thumbs .thumb:hover img { border-color: var(--red); opacity: 0.7; }
293
293
  #new-task .row { display: flex; gap: 7px; align-items: center; }
294
+ #new-task .row.actions { flex-wrap: wrap; row-gap: 6px; }
294
295
 
295
296
  /* pill dropdowns — "≡ fix-and-verify ⌄" / "⊞ auto ⌄" */
296
297
  .pill-select { position: relative; min-width: 0; }
297
- #src-pill { flex: 0 1 auto; min-width: 0; }
298
- #model-pill { flex: 0 1 auto; min-width: 0; }
298
+ #src-pill, #model-pill, #runner-pill { flex: 0 1 auto; min-width: 0; }
299
299
  .pill-btn {
300
300
  display: inline-flex;
301
301
  align-items: center;
@@ -677,10 +677,10 @@ summary::-webkit-details-marker { display: none; }
677
677
  border-top: 1px solid var(--line);
678
678
  padding: 11px 16px;
679
679
  display: flex;
680
- align-items: center;
681
- gap: 12px;
680
+ align-items: flex-end;
681
+ gap: 8px;
682
682
  }
683
- #env-chips { display: flex; gap: 12px; min-width: 0; }
683
+ #env-chips { display: flex; flex-wrap: wrap; gap: 6px 9px; min-width: 0; flex: 1 1 auto; }
684
684
  .env-chip {
685
685
  display: flex;
686
686
  align-items: center;
@@ -694,6 +694,8 @@ summary::-webkit-details-marker { display: none; }
694
694
  .env-chip.bad .led { background: var(--red); }
695
695
  #theme-toggle {
696
696
  margin-left: auto;
697
+ align-self: flex-end;
698
+ flex: none;
697
699
  border: none;
698
700
  background: transparent;
699
701
  color: var(--text3);
@@ -906,6 +908,24 @@ main { flex: 1; min-width: 0; display: flex; min-height: 0; background: var(--bg
906
908
  white-space: nowrap;
907
909
  max-width: 46ch;
908
910
  }
911
+ /* collapsed tool streaks (Claude-Code style) */
912
+ .ev.tool-streak { min-width: 0; margin: 4px 0; }
913
+ .streak-box { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
914
+ .streak-box[hidden] { display: none; }
915
+ .streak-toggle {
916
+ display: inline-flex;
917
+ align-items: center;
918
+ border: none;
919
+ background: transparent;
920
+ color: var(--text3);
921
+ font-family: var(--mono);
922
+ font-size: 10.5px;
923
+ cursor: pointer;
924
+ padding: 3px 2px;
925
+ margin: 1px 0;
926
+ }
927
+ .streak-toggle:hover { color: var(--text2); }
928
+
909
929
  .ev.result { margin: 4px 0; }
910
930
  .ev.result summary { cursor: pointer; }
911
931
  .ev.result summary:hover { border-color: var(--line2); }
@@ -1739,7 +1759,7 @@ a.bm:hover { border-color: var(--accent-line); color: var(--accent); text-decora
1739
1759
 
1740
1760
  /* narrow screens: sidebar shrinks, splits stack */
1741
1761
  @media (max-width: 900px) {
1742
- #sidebar { width: 240px; min-width: 240px; }
1762
+ #sidebar { width: 258px; min-width: 258px; }
1743
1763
  .split { grid-template-columns: 1fr; grid-template-rows: minmax(0, 45%) minmax(0, 1fr); }
1744
1764
  .split-list { border-right: none; border-bottom: 1px solid var(--line); }
1745
1765
  .wb-grid { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 45%); }