pikiloom 0.4.36 → 0.4.38

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 (50) hide show
  1. package/dist/agent/drivers/codex.js +20 -3
  2. package/dist/agent/index.js +2 -2
  3. package/dist/agent/kernel-bridge.js +206 -0
  4. package/dist/agent/stream.js +60 -2
  5. package/dist/cli/kernel-app.js +115 -0
  6. package/dist/cli/main.js +7 -0
  7. package/package.json +4 -2
  8. package/packages/kernel/README.md +107 -0
  9. package/packages/kernel/dist/contracts/driver.d.ts +95 -0
  10. package/packages/kernel/dist/contracts/driver.js +1 -0
  11. package/packages/kernel/dist/contracts/ports.d.ts +84 -0
  12. package/packages/kernel/dist/contracts/ports.js +1 -0
  13. package/packages/kernel/dist/contracts/surface.d.ts +75 -0
  14. package/packages/kernel/dist/contracts/surface.js +1 -0
  15. package/packages/kernel/dist/drivers/claude.d.ts +23 -0
  16. package/packages/kernel/dist/drivers/claude.js +453 -0
  17. package/packages/kernel/dist/drivers/codex.d.ts +14 -0
  18. package/packages/kernel/dist/drivers/codex.js +346 -0
  19. package/packages/kernel/dist/drivers/echo.d.ts +20 -0
  20. package/packages/kernel/dist/drivers/echo.js +61 -0
  21. package/packages/kernel/dist/drivers/gemini.d.ts +18 -0
  22. package/packages/kernel/dist/drivers/gemini.js +143 -0
  23. package/packages/kernel/dist/drivers/hermes.d.ts +14 -0
  24. package/packages/kernel/dist/drivers/hermes.js +194 -0
  25. package/packages/kernel/dist/drivers/index.d.ts +5 -0
  26. package/packages/kernel/dist/drivers/index.js +5 -0
  27. package/packages/kernel/dist/index.d.ts +18 -0
  28. package/packages/kernel/dist/index.js +29 -0
  29. package/packages/kernel/dist/ports/defaults.d.ts +59 -0
  30. package/packages/kernel/dist/ports/defaults.js +137 -0
  31. package/packages/kernel/dist/protocol/index.d.ts +309 -0
  32. package/packages/kernel/dist/protocol/index.js +59 -0
  33. package/packages/kernel/dist/runtime/hub.d.ts +65 -0
  34. package/packages/kernel/dist/runtime/hub.js +260 -0
  35. package/packages/kernel/dist/runtime/loom.d.ts +44 -0
  36. package/packages/kernel/dist/runtime/loom.js +69 -0
  37. package/packages/kernel/dist/runtime/pty.d.ts +23 -0
  38. package/packages/kernel/dist/runtime/pty.js +69 -0
  39. package/packages/kernel/dist/runtime/session-runner.d.ts +27 -0
  40. package/packages/kernel/dist/runtime/session-runner.js +210 -0
  41. package/packages/kernel/dist/runtime/tui.d.ts +8 -0
  42. package/packages/kernel/dist/runtime/tui.js +35 -0
  43. package/packages/kernel/dist/runtime/turn.d.ts +17 -0
  44. package/packages/kernel/dist/runtime/turn.js +16 -0
  45. package/packages/kernel/dist/surfaces/cli.d.ts +19 -0
  46. package/packages/kernel/dist/surfaces/cli.js +65 -0
  47. package/packages/kernel/dist/surfaces/index.d.ts +2 -0
  48. package/packages/kernel/dist/surfaces/index.js +2 -0
  49. package/packages/kernel/dist/surfaces/web.d.ts +39 -0
  50. package/packages/kernel/dist/surfaces/web.js +244 -0
@@ -0,0 +1,453 @@
1
+ import { spawn } from 'node:child_process';
2
+ // Real driver: shells the local `claude` CLI in stream-json mode and normalizes its
3
+ // events into kernel DriverEvents. Faithful to pikiloom's claude.ts event shapes
4
+ // (system / stream_event{message_start,content_block_delta,message_delta} / assistant / result),
5
+ // but fully self-contained. Proves "下层 Claude 不变".
6
+ export class ClaudeDriver {
7
+ bin;
8
+ id = 'claude';
9
+ capabilities = { steer: true, interact: false, resume: true, tui: true };
10
+ constructor(bin = 'claude') {
11
+ this.bin = bin;
12
+ }
13
+ // Interactive Claude Code TUI (no -p): the kernel spawns this in a PTY and passes
14
+ // the terminal through. Model/resume/BYOK-env come from the kernel's resolution.
15
+ tui(input) {
16
+ const args = [];
17
+ if (input.model)
18
+ args.push('--model', input.model);
19
+ if (input.sessionId)
20
+ args.push('--resume', input.sessionId);
21
+ if (input.extraArgs?.length)
22
+ args.push(...input.extraArgs);
23
+ return { command: this.bin, args, cwd: input.workdir, env: input.env };
24
+ }
25
+ run(input, ctx) {
26
+ const steerable = !!input.steerable;
27
+ const args = ['-p', '--verbose', '--output-format', 'stream-json', '--include-partial-messages'];
28
+ if (input.model)
29
+ args.push('--model', input.model);
30
+ if (input.effort)
31
+ args.push('--effort', input.effort === 'ultra' ? 'max' : input.effort); // request extended thinking (ultra is a display-only alias for max)
32
+ if (input.sessionId)
33
+ args.push('--resume', input.sessionId);
34
+ if (input.systemPrompt)
35
+ args.push('--append-system-prompt', input.systemPrompt);
36
+ if (input.mcpConfigPath)
37
+ args.push('--mcp-config', input.mcpConfigPath);
38
+ if (input.permissionMode)
39
+ args.push('--permission-mode', input.permissionMode); // parity: keep bypass/accept-edits on the kernel path
40
+ if (steerable)
41
+ args.push('--input-format', 'stream-json', '--replay-user-messages'); // parity: mid-turn steer
42
+ if (input.extraArgs?.length)
43
+ args.push(...input.extraArgs);
44
+ const state = {
45
+ text: '', reasoning: '', streamedText: false, streamedReasoning: false,
46
+ sessionId: null, model: null,
47
+ stopReason: null, error: null,
48
+ input: null, output: null, cached: null,
49
+ subAgents: new Map(),
50
+ tools: new Map(),
51
+ };
52
+ return new Promise((resolve) => {
53
+ let child;
54
+ let settled = false;
55
+ const finish = (r) => {
56
+ if (settled)
57
+ return;
58
+ settled = true;
59
+ try {
60
+ child?.stdin?.end();
61
+ }
62
+ catch { /* ignore */ }
63
+ if (steerable) {
64
+ try {
65
+ child?.kill('SIGTERM');
66
+ }
67
+ catch { /* ignore */ }
68
+ } // replay mode keeps the process alive past the turn
69
+ resolve(r);
70
+ };
71
+ try {
72
+ child = spawn(this.bin, args, { cwd: input.workdir, env: { ...process.env, ...(input.env || {}) }, stdio: ['pipe', 'pipe', 'pipe'] });
73
+ }
74
+ catch (err) {
75
+ finish({ ok: false, text: '', error: `spawn failed: ${err?.message || err}`, stopReason: 'error' });
76
+ return;
77
+ }
78
+ const onAbort = () => { try {
79
+ child.kill('SIGTERM');
80
+ }
81
+ catch { /* ignore */ } };
82
+ if (ctx.signal.aborted)
83
+ onAbort();
84
+ else
85
+ ctx.signal.addEventListener('abort', onAbort, { once: true });
86
+ if (steerable) {
87
+ ctx.registerSteer(async (prompt) => {
88
+ try {
89
+ child.stdin.write(claudeUserMessage(prompt) + '\n');
90
+ return true;
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ });
96
+ }
97
+ const usageOf = () => this.usage(state);
98
+ let buf = '';
99
+ let stderr = '';
100
+ child.stdout.on('data', (chunk) => {
101
+ buf += chunk.toString('utf8');
102
+ const lines = buf.split('\n');
103
+ buf = lines.pop() || '';
104
+ for (const line of lines) {
105
+ const trimmed = line.trim();
106
+ if (!trimmed)
107
+ continue;
108
+ let ev;
109
+ try {
110
+ ev = JSON.parse(trimmed);
111
+ }
112
+ catch {
113
+ continue;
114
+ }
115
+ handleClaudeEvent(ev, state, ctx.emit);
116
+ if (steerable && ev.type === 'result') {
117
+ // In replay mode the turn ends on `result` but the process lingers; settle now.
118
+ finish({ ok: !state.error, text: state.text, reasoning: state.reasoning || undefined, error: state.error, stopReason: state.stopReason, sessionId: state.sessionId, usage: usageOf() });
119
+ }
120
+ }
121
+ });
122
+ child.stderr.on('data', (c) => { stderr += c.toString('utf8'); });
123
+ child.on('error', (err) => finish({ ok: false, text: state.text, error: `claude spawn error: ${err.message}`, stopReason: 'error' }));
124
+ child.on('close', (code) => {
125
+ if (ctx.signal.aborted) {
126
+ finish({ ok: false, text: state.text, reasoning: state.reasoning, error: 'Interrupted by user.', stopReason: 'interrupted', sessionId: state.sessionId, usage: usageOf() });
127
+ return;
128
+ }
129
+ const ok = !state.error && code === 0;
130
+ finish({ ok, text: state.text, reasoning: state.reasoning || undefined, error: state.error || (ok ? null : `claude exited ${code}${stderr ? `: ${stderr.slice(0, 300)}` : ''}`), stopReason: state.stopReason, sessionId: state.sessionId, usage: usageOf() });
131
+ });
132
+ try {
133
+ if (steerable)
134
+ child.stdin.write(claudeUserMessage(input.prompt) + '\n'); // keep stdin open for steering
135
+ else {
136
+ child.stdin.write(input.prompt);
137
+ child.stdin.end();
138
+ }
139
+ }
140
+ catch { /* ignore */ }
141
+ });
142
+ }
143
+ usage(s) {
144
+ return { inputTokens: s.input, outputTokens: s.output, cachedInputTokens: s.cached, contextPercent: null };
145
+ }
146
+ }
147
+ // Parse one claude stream-json event into kernel DriverEvents (pure + exported for
148
+ // hermetic testing). Faithful to pikiloom's claudeParse shapes.
149
+ export function handleClaudeEvent(ev, s, emit) {
150
+ const t = ev.type || '';
151
+ // Child events of a spawned sub-agent are tagged with parent_tool_use_id; route their
152
+ // tool_uses into that sub-agent rather than the main turn (mirrors pikiloom).
153
+ const parentId = (typeof ev.parent_tool_use_id === 'string' && ev.parent_tool_use_id) ? ev.parent_tool_use_id : null;
154
+ if (parentId) {
155
+ const sub = s.subAgents?.get?.(parentId);
156
+ if (sub) {
157
+ if (t === 'assistant') {
158
+ for (const b of (ev.message?.content || [])) {
159
+ if (b?.type !== 'tool_use')
160
+ continue;
161
+ sub.tools.push({ id: String(b.id || ''), name: String(b.name || 'Tool'), summary: String(b.name || 'Tool') });
162
+ }
163
+ const m = ev.message?.model;
164
+ if (typeof m === 'string' && m.trim())
165
+ sub.model = m;
166
+ emit({ type: 'subagent', subagent: { ...sub, tools: [...sub.tools] } });
167
+ }
168
+ else if (t === 'system' && typeof ev.model === 'string' && ev.model.trim()) {
169
+ sub.model = ev.model;
170
+ emit({ type: 'subagent', subagent: { ...sub, tools: [...sub.tools] } });
171
+ }
172
+ }
173
+ return;
174
+ }
175
+ if (t === 'system') {
176
+ if (ev.session_id && ev.session_id !== s.sessionId) {
177
+ s.sessionId = ev.session_id;
178
+ emit({ type: 'session', sessionId: ev.session_id });
179
+ }
180
+ s.model = ev.model ?? s.model;
181
+ return;
182
+ }
183
+ if (t === 'stream_event') {
184
+ const inner = ev.event || {};
185
+ if (inner.type === 'message_start') {
186
+ const u = inner.message?.usage;
187
+ if (u) {
188
+ s.input = u.input_tokens ?? s.input;
189
+ s.cached = u.cache_read_input_tokens ?? s.cached;
190
+ }
191
+ }
192
+ else if (inner.type === 'content_block_delta') {
193
+ const d = inner.delta || {};
194
+ if (d.type === 'text_delta' && d.text) {
195
+ s.text += d.text;
196
+ s.streamedText = true;
197
+ emit({ type: 'text', delta: d.text });
198
+ }
199
+ else if (d.type === 'thinking_delta' && d.thinking) {
200
+ s.reasoning += d.thinking;
201
+ s.streamedReasoning = true;
202
+ emit({ type: 'reasoning', delta: d.thinking });
203
+ }
204
+ }
205
+ else if (inner.type === 'message_delta') {
206
+ const u = inner.usage;
207
+ if (u) {
208
+ if (u.output_tokens != null)
209
+ s.output = u.output_tokens;
210
+ if (u.input_tokens != null)
211
+ s.input = u.input_tokens;
212
+ if (u.cache_read_input_tokens != null)
213
+ s.cached = u.cache_read_input_tokens;
214
+ emit({ type: 'usage', usage: { inputTokens: s.input, outputTokens: s.output, cachedInputTokens: s.cached, contextPercent: null } });
215
+ }
216
+ if (inner.delta?.stop_reason)
217
+ s.stopReason = inner.delta.stop_reason;
218
+ }
219
+ if (ev.session_id && ev.session_id !== s.sessionId) {
220
+ s.sessionId = ev.session_id;
221
+ emit({ type: 'session', sessionId: ev.session_id });
222
+ }
223
+ return;
224
+ }
225
+ if (t === 'assistant') {
226
+ const contents = ev.message?.content || [];
227
+ for (const b of contents) {
228
+ if (b?.type !== 'tool_use')
229
+ continue;
230
+ const id = String(b.id || '');
231
+ const name = String(b.name || 'Tool');
232
+ if (name === 'TodoWrite') {
233
+ const plan = todoWriteToPlan(b.input);
234
+ if (plan)
235
+ emit({ type: 'plan', plan });
236
+ continue;
237
+ }
238
+ if (name === 'Task' || name === 'Agent') {
239
+ const input = b.input || {};
240
+ const sub = {
241
+ id,
242
+ kind: typeof input.subagent_type === 'string' ? input.subagent_type : null,
243
+ description: typeof input.description === 'string' ? input.description : null,
244
+ model: null, tools: [], status: 'running',
245
+ };
246
+ (s.subAgents ||= new Map()).set(id, sub);
247
+ emit({ type: 'subagent', subagent: { ...sub, tools: [] } });
248
+ continue;
249
+ }
250
+ const summary = summarizeToolUse(name, b.input);
251
+ (s.tools ||= new Map()).set(id, { name, summary });
252
+ emit({ type: 'tool', call: { id, name, summary, input: shortToolValue(toolInputDetail(name, b.input), 200) || null, status: 'running' } });
253
+ }
254
+ emitClaudeImages(contents, s, emit);
255
+ // Reasoning fallback: when thinking is NOT streamed as thinking_delta (claude can deliver
256
+ // it as a complete `thinking` block in the assistant message instead), capture it here.
257
+ // Mirrors the legacy driver — without this the kernel path silently drops all thinking.
258
+ if (!s.streamedReasoning) {
259
+ const th = contents.filter((b) => b?.type === 'thinking').map((b) => b.thinking || '').filter(Boolean).join('\n\n');
260
+ if (th) {
261
+ const delta = s.reasoning ? '\n\n' + th : th;
262
+ s.reasoning += delta;
263
+ emit({ type: 'reasoning', delta });
264
+ }
265
+ }
266
+ if (!s.streamedText) {
267
+ const tx = contents.filter((b) => b?.type === 'text').map((b) => b.text || '').join('\n\n');
268
+ if (tx) {
269
+ s.text = tx;
270
+ emit({ type: 'text', delta: tx });
271
+ }
272
+ }
273
+ return;
274
+ }
275
+ if (t === 'user') {
276
+ // Tool results: surface generated images as artifacts AND close out the tool call
277
+ // (status done/failed + a result detail) so toolCalls is a faithful structured SSOT
278
+ // and the runtime's activity projection can render the execution trail.
279
+ const contents = ev.message?.content || [];
280
+ for (const b of contents) {
281
+ if (b?.type !== 'tool_result')
282
+ continue;
283
+ emitClaudeImages(b.content || [], s, emit);
284
+ const id = String(b.tool_use_id || '').trim();
285
+ const tool = id ? s.tools?.get(id) : undefined;
286
+ if (!tool)
287
+ continue;
288
+ const isError = !!b.is_error;
289
+ // File-shaped tools have no useful result detail (mirrors pikiloom): just mark done.
290
+ const fileTool = tool.name === 'Read' || tool.name === 'Edit' || tool.name === 'Write' || tool.name === 'TodoWrite';
291
+ const detail = (isError || !fileTool) ? firstResultLine(b.content) : null;
292
+ emit({ type: 'tool', call: { id, name: tool.name, summary: tool.summary, status: isError ? 'failed' : 'done', result: detail || null } });
293
+ }
294
+ return;
295
+ }
296
+ if (t === 'result') {
297
+ if (ev.session_id)
298
+ s.sessionId = ev.session_id;
299
+ if (ev.is_error && Array.isArray(ev.errors) && ev.errors.length)
300
+ s.error = ev.errors.join('; ');
301
+ if (ev.result && !s.text.trim()) {
302
+ s.text = ev.result;
303
+ }
304
+ if (ev.stop_reason && !s.stopReason)
305
+ s.stopReason = ev.stop_reason;
306
+ const u = ev.usage;
307
+ if (u) {
308
+ if (s.input == null && u.input_tokens != null)
309
+ s.input = u.input_tokens;
310
+ if (s.output == null && u.output_tokens != null)
311
+ s.output = u.output_tokens;
312
+ const cached = u.cache_read_input_tokens ?? u.cached_input_tokens;
313
+ if (s.cached == null && cached != null)
314
+ s.cached = cached;
315
+ }
316
+ return;
317
+ }
318
+ }
319
+ // A stream-json user message (for --input-format stream-json; used to send the prompt and
320
+ // to inject mid-turn steer messages while stdin stays open).
321
+ export function claudeUserMessage(text) {
322
+ return JSON.stringify({ type: 'user', message: { role: 'user', content: [{ type: 'text', text }] } });
323
+ }
324
+ // Surface base64 image content blocks as artifacts (data URLs), deduped per turn.
325
+ export function emitClaudeImages(blocks, s, emit) {
326
+ if (!Array.isArray(blocks))
327
+ return;
328
+ s.seenImages ||= new Set();
329
+ let n = 0;
330
+ for (const b of blocks) {
331
+ if (b?.type !== 'image' || b?.source?.type !== 'base64' || typeof b.source.data !== 'string')
332
+ continue;
333
+ const mime = String(b.source.media_type || 'image/png');
334
+ const key = `${mime}:${b.source.data.length}:${b.source.data.slice(0, 32)}`;
335
+ if (s.seenImages.has(key))
336
+ continue;
337
+ s.seenImages.add(key);
338
+ const ext = mime.split('/')[1] || 'png';
339
+ emit({ type: 'artifact', artifact: { url: `data:${mime};base64,${b.source.data}`, fileName: `image-${++n}.${ext}`, mime, kind: 'photo' } });
340
+ }
341
+ }
342
+ // Claude's TodoWrite tool input -> a normalized UniversalPlan (ported from pikiloom).
343
+ export function todoWriteToPlan(input) {
344
+ if (!input || typeof input !== 'object')
345
+ return null;
346
+ const rawTodos = Array.isArray(input.todos) ? input.todos : [];
347
+ const steps = [];
348
+ for (const todo of rawTodos) {
349
+ if (!todo || typeof todo !== 'object')
350
+ continue;
351
+ const text = typeof todo.content === 'string' ? todo.content.trim() : '';
352
+ if (!text)
353
+ continue;
354
+ const raw = typeof todo.status === 'string' ? todo.status : 'pending';
355
+ const status = raw === 'completed' ? 'completed' : raw === 'in_progress' ? 'inProgress' : 'pending';
356
+ steps.push({ text, status });
357
+ }
358
+ return steps.length ? { explanation: null, steps } : null;
359
+ }
360
+ // ── Tool-call summarization (ported from pikiloom's summarizeClaudeToolUse) ──────────
361
+ // Turns a Claude tool_use {name,input} into a one-line human summary. The runtime's
362
+ // activity projector joins these into snapshot.activity; the structured form lives in
363
+ // toolCalls. Kept driver-local: knowing Claude's tool input shapes is the driver's job.
364
+ export function shortToolValue(value, max = 140) {
365
+ if (value == null)
366
+ return '';
367
+ const text = (typeof value === 'string' ? value : String(value)).replace(/\s+/g, ' ').trim();
368
+ if (text.length <= max)
369
+ return text;
370
+ return text.slice(0, Math.max(0, max - 1)).trimEnd() + '…';
371
+ }
372
+ function toolInputDetail(name, input) {
373
+ const i = input || {};
374
+ return i.command || i.file_path || i.path || i.pattern || i.query || i.url || i.description || '';
375
+ }
376
+ export function summarizeToolUse(name, input) {
377
+ const tool = String(name || '').trim() || 'Tool';
378
+ const i = input || {};
379
+ const description = shortToolValue(i.description, 120);
380
+ switch (tool) {
381
+ case 'Read': {
382
+ const t = shortToolValue(i.file_path || i.path);
383
+ return t ? `Read ${t}` : 'Read file';
384
+ }
385
+ case 'Edit': {
386
+ const t = shortToolValue(i.file_path || i.path);
387
+ return t ? `Edit ${t}` : 'Edit file';
388
+ }
389
+ case 'Write': {
390
+ const t = shortToolValue(i.file_path || i.path);
391
+ return t ? `Write ${t}` : 'Write file';
392
+ }
393
+ case 'Glob': {
394
+ const p = shortToolValue(i.pattern || i.glob, 120);
395
+ return p ? `List files: ${p}` : 'List files';
396
+ }
397
+ case 'Grep': {
398
+ const p = shortToolValue(i.pattern || i.query, 120);
399
+ return p ? `Search text: ${p}` : 'Search text';
400
+ }
401
+ case 'WebFetch': {
402
+ const u = shortToolValue(i.url, 120);
403
+ return u ? `Fetch ${u}` : 'Fetch web page';
404
+ }
405
+ case 'WebSearch': {
406
+ const q = shortToolValue(i.query, 120);
407
+ return q ? `Search web: ${q}` : 'Search web';
408
+ }
409
+ case 'TodoWrite': return 'Update plan';
410
+ case 'Task': {
411
+ const p = shortToolValue(i.description || i.prompt, 120);
412
+ return p ? `Run task: ${p}` : 'Run task';
413
+ }
414
+ case 'Bash': {
415
+ if (description)
416
+ return `Run shell: ${description}`;
417
+ const c = shortToolValue(i.command, 120);
418
+ return c ? `Run shell: ${c}` : 'Run shell command';
419
+ }
420
+ default: {
421
+ const mcpMatch = tool.match(/^mcp__[^_]+__(.+)$/);
422
+ const bare = mcpMatch ? mcpMatch[1] : tool;
423
+ if (bare === 'im_send_file') {
424
+ const p = shortToolValue(i.path, 120);
425
+ return p ? `Send file: ${p}` : 'Send file';
426
+ }
427
+ if (bare === 'im_list_files')
428
+ return 'List workspace files';
429
+ if (bare === 'im_ask_user') {
430
+ const q = shortToolValue(i.question, 120);
431
+ return q ? `Ask user: ${q}` : 'Ask user';
432
+ }
433
+ if (description)
434
+ return `${tool}: ${description}`;
435
+ const d = shortToolValue(toolInputDetail(tool, i), 120);
436
+ return d ? `${tool}: ${d}` : tool;
437
+ }
438
+ }
439
+ }
440
+ // First non-empty line of a tool_result content (string | block[]), for the "summary -> detail" form.
441
+ export function firstResultLine(content) {
442
+ let text = '';
443
+ if (typeof content === 'string')
444
+ text = content;
445
+ else if (Array.isArray(content))
446
+ text = content.map((b) => (typeof b === 'string' ? b : b?.type === 'text' ? b.text || '' : '')).join('\n');
447
+ for (const line of text.split('\n')) {
448
+ const t = line.trim();
449
+ if (t)
450
+ return shortToolValue(t, 120);
451
+ }
452
+ return '';
453
+ }
@@ -0,0 +1,14 @@
1
+ import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, TuiInput, TuiSpec } from '../contracts/driver.js';
2
+ export declare class CodexDriver implements AgentDriver {
3
+ private readonly bin;
4
+ readonly id = "codex";
5
+ readonly capabilities: {
6
+ steer: boolean;
7
+ interact: boolean;
8
+ resume: boolean;
9
+ tui: boolean;
10
+ };
11
+ constructor(bin?: string);
12
+ run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>;
13
+ tui(input: TuiInput): TuiSpec;
14
+ }