pikiloom 0.4.37 → 0.4.39

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 (67) hide show
  1. package/dashboard/dist/assets/{AgentTab-DbFzaIyZ.js → AgentTab-Ds8D6yrl.js} +1 -1
  2. package/dashboard/dist/assets/{ConnectionModal-QNYOWHCU.js → ConnectionModal-Buj2d5L5.js} +1 -1
  3. package/dashboard/dist/assets/{DirBrowser-BGaKHjFT.js → DirBrowser-BTv7rH8E.js} +1 -1
  4. package/dashboard/dist/assets/{ExtensionsTab-D4Gv9b8z.js → ExtensionsTab-BnGwxS9U.js} +1 -1
  5. package/dashboard/dist/assets/{IMAccessTab-DxxoZd84.js → IMAccessTab-DjQnpOmF.js} +1 -1
  6. package/dashboard/dist/assets/{Modal-Nm2e93s5.js → Modal-DpuinsE3.js} +1 -1
  7. package/dashboard/dist/assets/{Modals-BwxZmU0U.js → Modals-Bs842H3n.js} +1 -1
  8. package/dashboard/dist/assets/{Select-SGlII0yx.js → Select-Dvia29HZ.js} +1 -1
  9. package/dashboard/dist/assets/SessionPanel-CVItEgcH.js +1 -0
  10. package/dashboard/dist/assets/{SystemTab-BF6lIAYM.js → SystemTab-BzPtwDxq.js} +1 -1
  11. package/dashboard/dist/assets/index-Bthwt6K_.css +1 -0
  12. package/dashboard/dist/assets/{index-DZiAiRNt.js → index-S0NmlDEH.js} +14 -14
  13. package/dashboard/dist/assets/{index-BP8R_bLT.js → index-yZ-iG1qk.js} +2 -2
  14. package/dashboard/dist/assets/{shared-S0kcs5yP.js → shared-p3kZpiD4.js} +1 -1
  15. package/dashboard/dist/index.html +2 -2
  16. package/dist/agent/kernel-bridge.js +207 -0
  17. package/dist/agent/mcp/capabilities.js +39 -0
  18. package/dist/agent/stream.js +19 -1
  19. package/dist/bot/bot.js +4 -13
  20. package/dist/cli/kernel-app.js +115 -0
  21. package/dist/cli/main.js +7 -0
  22. package/package.json +4 -2
  23. package/packages/kernel/README.md +305 -0
  24. package/packages/kernel/dist/contracts/driver.d.ts +95 -0
  25. package/packages/kernel/dist/contracts/driver.js +1 -0
  26. package/packages/kernel/dist/contracts/ports.d.ts +84 -0
  27. package/packages/kernel/dist/contracts/ports.js +1 -0
  28. package/packages/kernel/dist/contracts/surface.d.ts +92 -0
  29. package/packages/kernel/dist/contracts/surface.js +1 -0
  30. package/packages/kernel/dist/drivers/claude.d.ts +34 -0
  31. package/packages/kernel/dist/drivers/claude.js +500 -0
  32. package/packages/kernel/dist/drivers/codex.d.ts +24 -0
  33. package/packages/kernel/dist/drivers/codex.js +415 -0
  34. package/packages/kernel/dist/drivers/echo.d.ts +20 -0
  35. package/packages/kernel/dist/drivers/echo.js +61 -0
  36. package/packages/kernel/dist/drivers/gemini.d.ts +18 -0
  37. package/packages/kernel/dist/drivers/gemini.js +143 -0
  38. package/packages/kernel/dist/drivers/hermes.d.ts +14 -0
  39. package/packages/kernel/dist/drivers/hermes.js +194 -0
  40. package/packages/kernel/dist/drivers/index.d.ts +5 -0
  41. package/packages/kernel/dist/drivers/index.js +5 -0
  42. package/packages/kernel/dist/index.d.ts +18 -0
  43. package/packages/kernel/dist/index.js +29 -0
  44. package/packages/kernel/dist/ports/defaults.d.ts +59 -0
  45. package/packages/kernel/dist/ports/defaults.js +137 -0
  46. package/packages/kernel/dist/protocol/index.d.ts +309 -0
  47. package/packages/kernel/dist/protocol/index.js +59 -0
  48. package/packages/kernel/dist/runtime/hub.d.ts +68 -0
  49. package/packages/kernel/dist/runtime/hub.js +334 -0
  50. package/packages/kernel/dist/runtime/loom.d.ts +45 -0
  51. package/packages/kernel/dist/runtime/loom.js +72 -0
  52. package/packages/kernel/dist/runtime/pty.d.ts +23 -0
  53. package/packages/kernel/dist/runtime/pty.js +69 -0
  54. package/packages/kernel/dist/runtime/session-runner.d.ts +27 -0
  55. package/packages/kernel/dist/runtime/session-runner.js +210 -0
  56. package/packages/kernel/dist/runtime/tui.d.ts +8 -0
  57. package/packages/kernel/dist/runtime/tui.js +35 -0
  58. package/packages/kernel/dist/runtime/turn.d.ts +17 -0
  59. package/packages/kernel/dist/runtime/turn.js +16 -0
  60. package/packages/kernel/dist/surfaces/cli.d.ts +19 -0
  61. package/packages/kernel/dist/surfaces/cli.js +65 -0
  62. package/packages/kernel/dist/surfaces/index.d.ts +2 -0
  63. package/packages/kernel/dist/surfaces/index.js +2 -0
  64. package/packages/kernel/dist/surfaces/web.d.ts +39 -0
  65. package/packages/kernel/dist/surfaces/web.js +244 -0
  66. package/dashboard/dist/assets/SessionPanel-DYfSlreh.js +0 -1
  67. package/dashboard/dist/assets/index-CtS48Jn-.css +0 -1
@@ -0,0 +1,305 @@
1
+ # @pikiloom/kernel
2
+
3
+ Turn heterogeneous coding agents (**Claude Code · Codex · Gemini · ACP/Hermes**) into one
4
+ **uniform, accumulating session snapshot + a small set of control verbs**, exposed over
5
+ **pluggable surfaces** (IM, Web, tunnel, raw terminal). Environmental concerns (storage,
6
+ credentials, tools, prompts) are **injected ports** with working defaults.
7
+
8
+ This is the reusable core that [pikiloom](https://github.com/xiaotonng/pikiloom) itself is
9
+ built on. Drop it into any project and stand up a "pikiloom-like" backend in a few lines —
10
+ **you never parse a CLI's output or learn each agent's wire format**; you read one
11
+ `UniversalSnapshot` and call `prompt / stop / steer / interact`.
12
+
13
+ ```bash
14
+ npm i @pikiloom/kernel
15
+ ```
16
+
17
+ - Runtime dep: `ws`. Optional: `node-pty` (only for raw-TUI passthrough). Node ≥ 20, ESM-only.
18
+ - TypeScript types ship in the package (`dist/**/*.d.ts`). For an LLM-oriented summary, see [`llms.txt`](./llms.txt).
19
+
20
+ ---
21
+
22
+ ## Mental model
23
+
24
+ ```
25
+ 上层 (you write): IM bindings · Web UI · Plugins ── implement Surface / Plugin
26
+ ▲ createLoom({ surfaces, plugins, ...ports })
27
+ @pikiloom/kernel: contracts/ Driver · Surface · LoomIO · Ports
28
+ (one import, runtime/ SessionRunner · Hub (multi-session) · snapshot accumulation · control verbs
29
+ modular inside) drivers/ Claude / Codex / Gemini / Hermes / Echo ← the agent axis (下层)
30
+ surfaces/ WebSurface (ws host) · CliSurface
31
+ protocol/ UniversalSnapshot + diff ← the wire vocabulary
32
+ ports/ SessionStore · ModelResolver · ToolProvider · ... (+ defaults)
33
+ ▲ spawns the external `claude` / `codex` / `gemini` CLIs (unchanged)
34
+ 下层 (unchanged): the agent binaries, native protocols
35
+ ```
36
+
37
+ **IM and Web are not two systems** — both are just `Surface`s over the same `LoomIO`.
38
+
39
+ ### Two rails, same driver registry
40
+
41
+ | Rail | Driver method | Output | Use for |
42
+ |------|---------------|--------|---------|
43
+ | **Structured** | `driver.run(input, ctx)` | streamed `UniversalSnapshot` (text, reasoning, tool activity, plan, usage) | IM, Web dashboards, any UI that renders structured turns |
44
+ | **Raw PTY** | `driver.tui(input)` | a real full-screen interactive process passed through a PTY | a local terminal app (`pikiloom code`-style); needs `node-pty` |
45
+
46
+ ---
47
+
48
+ ## Quick start
49
+
50
+ ### 1. One turn through one agent — `runTurn` (the bridge primitive)
51
+
52
+ The smallest unit. No persistence, no multi-session — just run a turn and get a streamed
53
+ snapshot + final result. This is exactly what an existing app maps onto its own UI.
54
+
55
+ ```ts
56
+ import { runTurn, ClaudeDriver } from '@pikiloom/kernel';
57
+
58
+ const { result, snapshot } = await runTurn(
59
+ new ClaudeDriver(),
60
+ { prompt: 'Summarize package.json in one line', workdir: process.cwd(), effort: 'high' },
61
+ {
62
+ onSnapshot: (s) => {
63
+ // fires on every event — render live:
64
+ // s.text accumulated assistant output
65
+ // s.reasoning accumulated thinking (when the model/auth exposes it)
66
+ // s.activity human-readable execution trail ("Read foo.ts", "Run shell: npm test")
67
+ // s.toolCalls structured tool calls [{ name, summary, status }]
68
+ // s.plan, s.usage, s.artifacts, s.interactions
69
+ process.stdout.write(`\r${s.activity?.split('\n').at(-1) ?? ''}`);
70
+ },
71
+ onSteer: (steer) => { /* call steer('extra prompt') mid-turn */ },
72
+ signal: undefined, // pass an AbortSignal to stop the turn
73
+ },
74
+ );
75
+ console.log(result.ok, result.text, result.sessionId);
76
+ ```
77
+
78
+ ### 2. A full multi-session backend — `createLoom`
79
+
80
+ Adds persistence, a session hub, per-session queueing, discovery, and surfaces. Every port
81
+ has a default, so this runs with zero wiring.
82
+
83
+ ```ts
84
+ import { createLoom, ClaudeDriver, CodexDriver, WebSurface } from '@pikiloom/kernel';
85
+
86
+ const loom = createLoom({
87
+ drivers: [new ClaudeDriver(), new CodexDriver()], // 下层 (unchanged binaries)
88
+ surfaces: [new WebSurface({ port: 8787 })], // 上层 (Web/tunnel; add your IM Surface here)
89
+ defaultAgent: 'claude',
90
+ // optional ports — override any one to swap storage / credentials / tools / prompts:
91
+ // sessionStore, modelResolver, toolProvider, systemPromptBuilder, catalog, interactionHandler
92
+ });
93
+ await loom.start();
94
+
95
+ // Drive it from anywhere via loom.io (LoomIO):
96
+ const { sessionKey, taskId } = await loom.io.prompt({ prompt: 'hello', agent: 'claude' });
97
+ const unsub = loom.io.subscribe((key, snapshot, patch, seq) => { /* render */ });
98
+ loom.io.steer(taskId, 'actually, do X instead');
99
+ loom.io.stop(sessionKey);
100
+ ```
101
+
102
+ To add an **IM channel**, implement `Surface.start(io)`: route inbound messages to
103
+ `io.prompt(...)` and render `io.subscribe(...)` snapshots back out. Nothing else changes.
104
+
105
+ ### 3. Raw TUI passthrough — `runTui` / `openTui`
106
+
107
+ ```ts
108
+ const loom = createLoom({ drivers: [new ClaudeDriver()], defaultAgent: 'claude' });
109
+ await loom.runTui({ agent: 'claude', workdir: process.cwd() }); // you're now in the real Claude TUI
110
+ ```
111
+
112
+ `runTui` does full stdin/stdout raw passthrough on the current terminal. `openTui` returns a
113
+ `PtyBridge` (`onData` / `write` / `resize` / `onExit`) so you can drive or tee it yourself.
114
+ Requires the optional `node-pty` dependency (`ptyAvailable()` reports availability).
115
+
116
+ ---
117
+
118
+ ## The data you read: `UniversalSnapshot`
119
+
120
+ One driver-agnostic shape for every agent. A surface renders *this* and never touches a
121
+ CLI's native format. It **accumulates** across a turn (text/reasoning append; structured
122
+ fields replace).
123
+
124
+ ```ts
125
+ interface UniversalSnapshot {
126
+ phase: 'idle' | 'queued' | 'streaming' | 'done';
127
+ taskId?: string | null;
128
+ sessionId?: string | null;
129
+ agent?: string | null;
130
+ model?: string | null;
131
+ effort?: string | null;
132
+ prompt?: string | null;
133
+
134
+ text?: string; // accumulated assistant output
135
+ reasoning?: string; // accumulated thinking (only when the model/auth streams it; see note)
136
+ activity?: string; // human-readable execution trail, one line per tool/subagent (kernel-derived)
137
+ plan?: UniversalPlan | null; // { explanation, steps:[{ text, status }] }
138
+ toolCalls?: UniversalToolCall[]; // structured: { id, name, summary, input?, result?, status }
139
+ subAgents?: UniversalSubAgent[]; // spawned sub-agents and their tools
140
+ usage?: UniversalUsage | null; // { inputTokens, outputTokens, cachedInputTokens, contextPercent, ... }
141
+ artifacts?: UniversalArtifact[]; // generated files/images { url|path, fileName, mime, kind }
142
+ interactions?: UniversalInteraction[]; // pending human-in-the-loop questions (answer via interact())
143
+ queued?: UniversalQueuedTask[]; // prompts waiting behind the running turn
144
+
145
+ error?: string | null;
146
+ incomplete?: boolean; // true if the turn was interrupted / errored
147
+ startedAt?: number;
148
+ updatedAt: number;
149
+ }
150
+ ```
151
+
152
+ Sub-shapes:
153
+
154
+ ```ts
155
+ interface UniversalToolCall { id: string; name: string; summary: string; input?: string | null; result?: string | null; status: 'running' | 'done' | 'failed'; }
156
+ interface UniversalPlan { explanation: string | null; steps: { text: string; status: 'pending' | 'inProgress' | 'completed' }[]; }
157
+ interface UniversalUsage { inputTokens: number | null; outputTokens: number | null; cachedInputTokens: number | null; contextUsedTokens?: number | null; contextPercent: number | null; providerName?: string | null; }
158
+ interface UniversalArtifact { url?: string; path?: string; fileName: string; mime?: string; kind: 'photo' | 'document'; caption?: string; }
159
+ interface UniversalInteraction { promptId: string; kind: 'user-input' | 'permission' | 'confirmation'; title: string; questions: { id: string; text: string; type?: 'text' | 'select'; choices?: { label: string; value?: string }[] }[]; }
160
+ ```
161
+
162
+ **Activity projection (kernel-owned).** Every driver emits *structured* tool calls; the
163
+ kernel's `SessionRunner` derives `snapshot.activity` from `toolCalls` + `subAgents` centrally
164
+ (`projectActivity`): one line per call with a status suffix — `summary` while running,
165
+ `summary done` / `summary -> detail` on success, `summary failed: detail` on error. So every
166
+ surface gets a readable execution trail for free, and rich UIs can still use `toolCalls`
167
+ directly. You implement this **nowhere** — it's a property of the snapshot.
168
+
169
+ **Reasoning note.** Plaintext thinking only appears when the agent actually streams it
170
+ (e.g. BYOK Anthropic API keys, Codex `item/reasoning`). Subscription/OAuth Claude withholds
171
+ plaintext extended-thinking (streams only an encrypted signature), so `reasoning` is empty
172
+ there — a platform behavior, not a kernel limitation. The Claude driver also captures
173
+ reasoning delivered as a complete block, not only as streamed deltas.
174
+
175
+ ### Streaming on the wire — `diffSnapshot` / `applySnapshotPatch`
176
+
177
+ `diffSnapshot(prev, next)` produces a compact `SnapshotPatch` (prefix-append for `text` /
178
+ `reasoning`; field replacement otherwise, with `undefined → null` so field-clears survive
179
+ `JSON.stringify`). `applySnapshotPatch(prev, patch)` reassembles it on the client. `WebSurface`
180
+ uses these; reuse them for your own transport.
181
+
182
+ ---
183
+
184
+ ## Control verbs (`LoomIO`)
185
+
186
+ ```ts
187
+ io.prompt({ prompt, agent?, sessionKey?, workdir?, model?, effort?, attachments? }) // → { sessionKey, taskId }
188
+ io.stop(sessionKey) // interrupt the running turn (queued tasks stay & promote)
189
+ io.steer(taskId, prompt, attachments?) // inject a message mid-turn (drivers with capabilities.steer)
190
+ io.interact(promptId, action, value?) // answer a pending interaction: 'select' | 'text' | 'skip' | 'cancel'
191
+ io.subscribe((sessionKey, snapshot, patch, seq) => …) // live snapshots
192
+ io.getSnapshot(sessionKey) · io.getHistory(sessionKey) · io.listSessions()
193
+ io.listAgentInfo() · io.listModels(agent) · io.listEffort(agent, model?) · io.listTools(agent, workdir?) · io.listSkills(agent, workdir?)
194
+ ```
195
+
196
+ Concurrent prompts to one session **queue** by default (`serialPerSession`, no clobber); `stop`
197
+ interrupts only the current turn and the next queued task promotes.
198
+
199
+ ---
200
+
201
+ ## Drivers (the agent axis)
202
+
203
+ | Driver | id | transport | steer | interact | resume | tui |
204
+ |--------|----|-----------|:-----:|:--------:|:------:|:---:|
205
+ | `ClaudeDriver` | `claude` | `claude` CLI, stream-json (+ `--effort`, partial messages) | ✓ | — | ✓ | ✓ |
206
+ | `CodexDriver` | `codex` | `codex app-server` JSON-RPC (HITL via `requestUserInput`) | ✓ | via askUser | ✓ | ✓ |
207
+ | `GeminiDriver` | `gemini` | `gemini --output-format stream-json` | — | — | ✓ | ✓ |
208
+ | `HermesDriver` | `hermes` | ACP `session/update` | — | — | ✓ | — |
209
+ | `EchoDriver` | `echo` | none (hermetic, in-process) | ✓ | ✓ | ✓ | ✓ |
210
+
211
+ Write your own by implementing `AgentDriver` and passing it to `createLoom({ drivers })` (or
212
+ `loom.registerDriver(...)`). A driver normalizes its agent into `DriverEvent`s:
213
+
214
+ ```ts
215
+ type DriverEvent =
216
+ | { type: 'session'; sessionId: string }
217
+ | { type: 'text'; delta: string }
218
+ | { type: 'reasoning'; delta: string }
219
+ | { type: 'tool'; call: UniversalToolCall } // emit on tool start AND completion (status update)
220
+ | { type: 'plan'; plan: UniversalPlan }
221
+ | { type: 'subagent'; subagent: UniversalSubAgent }
222
+ | { type: 'usage'; usage: Partial<UniversalUsage> }
223
+ | { type: 'artifact'; artifact: UniversalArtifact }
224
+ | { type: 'activity'; line: string }; // explicit activity line (drivers that don't emit tool events)
225
+
226
+ interface AgentDriver {
227
+ readonly id: string;
228
+ readonly capabilities?: { steer?: boolean; interact?: boolean; resume?: boolean; tui?: boolean };
229
+ run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>; // ctx: { signal, emit, askUser, registerSteer }
230
+ tui?(input: TuiInput): TuiSpec;
231
+ }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Surfaces & Ports
237
+
238
+ **Surfaces** (上层) bind to `LoomIO`. Built in: `WebSurface` (a WebSocket host speaking the
239
+ wire protocol — any pikichannel-style client connects), `CliSurface`. Implement `Surface` to
240
+ add an IM channel or your own UI.
241
+
242
+ **Ports** (side) — all optional, each with a default, so `createLoom()` runs with zero config:
243
+
244
+ | Port | Default | Swap to… |
245
+ |------|---------|----------|
246
+ | `SessionStore` | `FsSessionStore` (`~/.<ns>/sessions`) | your DB / transcript store |
247
+ | `ModelResolver` | `NullModelResolver` (native login) | BYOK credential/provider injection |
248
+ | `ToolProvider` | `NoopToolProvider` | per-session MCP servers |
249
+ | `SystemPromptBuilder` | `PassthroughSystemPromptBuilder` | your system/developer prompt |
250
+ | `Catalog` | `NoopCatalog` | model/effort/tool/skill discovery for composers |
251
+ | `InteractionHandler` | `DeferToTerminalInteractionHandler` | programmatic HITL answers (`AutoCancelInteractionHandler` for one-shots) |
252
+
253
+ **Plugins** are the registration unit for everything ONE capability adds to a session —
254
+ register many, composed deterministically (and dynamically via `loom.registerPlugin(...)`):
255
+
256
+ ```ts
257
+ interface Plugin {
258
+ id: string;
259
+ tools?(opts: { agent; workdir }): McpServerSpec[]; // MCP servers
260
+ promptFragment?(opts: { agent; workdir; isFirstTurn }): string | null; // how-to-use / behavior prompt
261
+ contributeSpawn?(opts: { agent; workdir; mode: 'run'|'tui'; sessionId?; model? }): SpawnContribution | null; // { env?, extraArgs?, configOverrides? }
262
+ decorateSnapshot?(snapshot): UniversalSnapshot;
263
+ }
264
+ ```
265
+
266
+ The kernel merges contributions per spawn — **never mutating global `process.env`** — in order
267
+ `[ModelResolver → ToolProvider.env → plugins (registration order)]`, so a plugin can override
268
+ the resolver (e.g. point an agent's `ANTHROPIC_BASE_URL` at a local proxy). `promptFragment`s are
269
+ appended to the `SystemPromptBuilder` base and delivered via each agent's native mechanism. This
270
+ is how a capability registers its tools **and** their usage prompt **and** any env/flags in one
271
+ place — and how a model-traffic interceptor injects a redirect on both the `run()` and `tui()`
272
+ rails without the kernel knowing anything about it. (The singular `ModelResolver` /
273
+ `SystemPromptBuilder` ports remain the one authoritative model-credential / base-prompt source;
274
+ plugins are the composable per-capability layer on top.)
275
+
276
+ ---
277
+
278
+ ## Exports
279
+
280
+ Main entry `@pikiloom/kernel` re-exports everything. Subpaths: `@pikiloom/kernel/drivers`,
281
+ `@pikiloom/kernel/surfaces`, `@pikiloom/kernel/protocol`.
282
+
283
+ - Runtime: `createLoom`, `Loom`, `Hub`, `SessionRunner`, `runTurn`, `PtyBridge`, `ptyAvailable`, `attachTui`
284
+ - Drivers: `EchoDriver`, `ClaudeDriver`, `CodexDriver`, `GeminiDriver`, `HermesDriver`
285
+ - Surfaces: `WebSurface`, `CliSurface`
286
+ - Ports/defaults: `FsSessionStore`, `NullModelResolver`, `NoopToolProvider`, `PassthroughSystemPromptBuilder`, `AutoCancelInteractionHandler`, `DeferToTerminalInteractionHandler`, `NoopCatalog`, `defaultBaseDir`
287
+ - Protocol: `UniversalSnapshot`, `diffSnapshot`, `applySnapshotPatch`, `emptySnapshot`, `PROTOCOL_VERSION`, all wire/`Client*`/`Server*` message types
288
+ - Types: `AgentDriver`, `AgentTurnInput`, `DriverContext`, `DriverEvent`, `DriverResult`, `LoomIO`, `PromptInput`, `Surface`, `Plugin`, `SpawnContribution`, `SessionStore`, `ModelResolver`, `ToolProvider`, `SystemPromptBuilder`, `InteractionHandler`, `Catalog`, …
289
+
290
+ ---
291
+
292
+ ## Verify
293
+
294
+ ```bash
295
+ npm run typecheck # tsc, clean
296
+ npm test # hermetic: snapshot diff, full lifecycle (stop/steer/interact), web ws flow, driver parsers
297
+ KERNEL_E2E_REAL=1 npm test # also drives the real `claude` CLI end-to-end
298
+ node examples/smoke.mjs # smoke against the compiled dist
299
+ ```
300
+
301
+ See `examples/` for a runnable web console, a Feishu/Lark terminal, and a Node smoke test.
302
+
303
+ ## License
304
+
305
+ MIT
@@ -0,0 +1,95 @@
1
+ import type { UniversalToolCall, UniversalPlan, UniversalUsage, UniversalInteraction, UniversalArtifact, UniversalSubAgent } from '../protocol/index.js';
2
+ export interface AgentTurnInput {
3
+ prompt: string;
4
+ attachments?: string[];
5
+ sessionId?: string | null;
6
+ workdir: string;
7
+ model?: string | null;
8
+ effort?: string | null;
9
+ systemPrompt?: string;
10
+ env?: Record<string, string>;
11
+ extraMcpServers?: McpServerSpec[];
12
+ mcpConfigPath?: string | null;
13
+ permissionMode?: string | null;
14
+ extraArgs?: string[];
15
+ configOverrides?: string[];
16
+ fullAccess?: boolean;
17
+ steerable?: boolean;
18
+ }
19
+ export interface McpServerSpec {
20
+ name: string;
21
+ type?: 'stdio' | 'http';
22
+ command?: string;
23
+ args?: string[];
24
+ env?: Record<string, string>;
25
+ url?: string;
26
+ headers?: Record<string, string>;
27
+ }
28
+ export type DriverEvent = {
29
+ type: 'session';
30
+ sessionId: string;
31
+ } | {
32
+ type: 'text';
33
+ delta: string;
34
+ } | {
35
+ type: 'reasoning';
36
+ delta: string;
37
+ } | {
38
+ type: 'tool';
39
+ call: UniversalToolCall;
40
+ } | {
41
+ type: 'plan';
42
+ plan: UniversalPlan;
43
+ } | {
44
+ type: 'subagent';
45
+ subagent: UniversalSubAgent;
46
+ } | {
47
+ type: 'usage';
48
+ usage: Partial<UniversalUsage>;
49
+ } | {
50
+ type: 'artifact';
51
+ artifact: UniversalArtifact;
52
+ } | {
53
+ type: 'activity';
54
+ line: string;
55
+ };
56
+ export type SteerFn = (prompt: string, attachments?: string[]) => Promise<boolean>;
57
+ export interface DriverContext {
58
+ signal: AbortSignal;
59
+ emit(event: DriverEvent): void;
60
+ askUser(interaction: UniversalInteraction): Promise<Record<string, string[]>>;
61
+ registerSteer(fn: SteerFn): void;
62
+ }
63
+ export interface DriverResult {
64
+ ok: boolean;
65
+ text: string;
66
+ reasoning?: string;
67
+ error?: string | null;
68
+ stopReason?: string | null;
69
+ sessionId?: string | null;
70
+ usage?: UniversalUsage | null;
71
+ }
72
+ export interface TuiInput {
73
+ workdir: string;
74
+ model?: string | null;
75
+ sessionId?: string | null;
76
+ env?: Record<string, string>;
77
+ extraArgs?: string[];
78
+ }
79
+ export interface TuiSpec {
80
+ command: string;
81
+ args: string[];
82
+ cwd: string;
83
+ env?: Record<string, string>;
84
+ }
85
+ export interface AgentDriver {
86
+ readonly id: string;
87
+ readonly capabilities?: {
88
+ steer?: boolean;
89
+ interact?: boolean;
90
+ resume?: boolean;
91
+ tui?: boolean;
92
+ };
93
+ run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>;
94
+ tui?(input: TuiInput): TuiSpec;
95
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,84 @@
1
+ import type { UniversalInteraction, UniversalSnapshot, ModelDescriptor, EffortOption, ToolDescriptor, SkillDescriptor } from '../protocol/index.js';
2
+ import type { McpServerSpec, DriverResult } from './driver.js';
3
+ export interface CoreSessionRecord {
4
+ agent: string;
5
+ sessionId: string;
6
+ nativeSessionId?: string | null;
7
+ workspacePath: string;
8
+ createdAt: string;
9
+ updatedAt: string;
10
+ title?: string | null;
11
+ model?: string | null;
12
+ effort?: string | null;
13
+ runState?: 'running' | 'completed' | 'incomplete';
14
+ runDetail?: string | null;
15
+ }
16
+ export interface SessionStore {
17
+ ensure(agent: string, opts: {
18
+ sessionId?: string | null;
19
+ title?: string | null;
20
+ workdir: string;
21
+ }): Promise<{
22
+ sessionId: string;
23
+ workspacePath: string;
24
+ }>;
25
+ get(agent: string, sessionId: string): Promise<CoreSessionRecord | null>;
26
+ save(record: CoreSessionRecord): Promise<void>;
27
+ list(agent: string, opts?: {
28
+ limit?: number;
29
+ }): Promise<CoreSessionRecord[]>;
30
+ recordResult(agent: string, sessionId: string, result: DriverResult): Promise<void>;
31
+ appendTurn?(agent: string, sessionId: string, turn: UniversalSnapshot): Promise<void>;
32
+ history?(agent: string, sessionId: string): Promise<UniversalSnapshot[]>;
33
+ }
34
+ export interface ModelInjection {
35
+ model?: string | null;
36
+ env?: Record<string, string>;
37
+ extraArgs?: string[];
38
+ configOverrides?: string[];
39
+ providerName?: string | null;
40
+ contextWindow?: number | null;
41
+ }
42
+ export interface ModelResolver {
43
+ resolve(agent: string, opts: {
44
+ model?: string | null;
45
+ profileId?: string | null;
46
+ }): Promise<ModelInjection | null>;
47
+ }
48
+ export interface ToolProvider {
49
+ provideForSession(opts: {
50
+ agent: string;
51
+ workdir: string;
52
+ workspacePath: string;
53
+ }): Promise<{
54
+ servers: McpServerSpec[];
55
+ env?: Record<string, string>;
56
+ }>;
57
+ }
58
+ export interface SystemPromptBuilder {
59
+ compose(opts: {
60
+ agent: string;
61
+ base?: string;
62
+ isFirstTurn: boolean;
63
+ }): string | undefined;
64
+ }
65
+ export interface InteractionHandler {
66
+ askUser(interaction: UniversalInteraction): Promise<Record<string, string[]> | null>;
67
+ }
68
+ export interface Catalog {
69
+ listModels(opts: {
70
+ agent: string;
71
+ }): Promise<ModelDescriptor[]>;
72
+ listEffort(opts: {
73
+ agent: string;
74
+ model?: string | null;
75
+ }): Promise<EffortOption[]>;
76
+ listTools(opts: {
77
+ agent: string;
78
+ workdir: string;
79
+ }): Promise<ToolDescriptor[]>;
80
+ listSkills(opts: {
81
+ agent: string;
82
+ workdir: string;
83
+ }): Promise<SkillDescriptor[]>;
84
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,92 @@
1
+ import type { UniversalSnapshot, SnapshotPatch, SessionMeta, AgentInfo, ModelDescriptor, EffortOption, ToolDescriptor, SkillDescriptor } from '../protocol/index.js';
2
+ import type { McpServerSpec } from './driver.js';
3
+ export interface PromptInput {
4
+ prompt: string;
5
+ agent?: string;
6
+ sessionKey?: string;
7
+ workdir?: string;
8
+ model?: string | null;
9
+ effort?: string | null;
10
+ attachments?: string[];
11
+ }
12
+ export interface LoomIO {
13
+ prompt(input: PromptInput): Promise<{
14
+ sessionKey: string;
15
+ taskId: string;
16
+ }>;
17
+ stop(sessionKey: string): boolean;
18
+ steer(taskId: string, prompt: string, attachments?: string[]): Promise<boolean>;
19
+ interact(promptId: string, action: 'select' | 'text' | 'skip' | 'cancel', value?: string): boolean;
20
+ subscribe(cb: (sessionKey: string, snapshot: UniversalSnapshot, patch: SnapshotPatch, seq: number) => void): () => void;
21
+ onSessionsChanged(cb: (sessions: SessionMeta[]) => void): () => void;
22
+ listSessions(): SessionMeta[];
23
+ getSnapshot(sessionKey: string): {
24
+ snapshot: UniversalSnapshot;
25
+ seq: number;
26
+ } | null;
27
+ getHistory(sessionKey: string): Promise<UniversalSnapshot[]>;
28
+ listAgents(): string[];
29
+ listAgentInfo(): AgentInfo[];
30
+ listModels(agent: string): Promise<ModelDescriptor[]>;
31
+ listEffort(agent: string, model?: string | null): Promise<EffortOption[]>;
32
+ listTools(agent: string, workdir?: string): Promise<ToolDescriptor[]>;
33
+ listSkills(agent: string, workdir?: string): Promise<SkillDescriptor[]>;
34
+ }
35
+ export interface SurfaceCapabilities {
36
+ editMessages?: boolean;
37
+ images?: boolean;
38
+ buttons?: boolean;
39
+ tunnel?: boolean;
40
+ }
41
+ export interface PtyHandle {
42
+ onData(cb: (data: string) => void): () => void;
43
+ write(data: string): void;
44
+ resize(cols: number, rows: number): void;
45
+ kill(signal?: string): void;
46
+ onExit(cb: (e: {
47
+ exitCode: number;
48
+ signal?: number;
49
+ }) => void): () => void;
50
+ readonly pid: number;
51
+ }
52
+ export interface TuiHost {
53
+ openTui(opts: {
54
+ agent?: string;
55
+ workdir?: string;
56
+ model?: string | null;
57
+ sessionId?: string | null;
58
+ cols?: number;
59
+ rows?: number;
60
+ }): Promise<PtyHandle>;
61
+ }
62
+ export interface Surface {
63
+ readonly id: string;
64
+ readonly capabilities?: SurfaceCapabilities;
65
+ start(io: LoomIO, host?: TuiHost): Promise<void>;
66
+ stop(): Promise<void>;
67
+ }
68
+ export interface SpawnContribution {
69
+ env?: Record<string, string>;
70
+ extraArgs?: string[];
71
+ configOverrides?: string[];
72
+ }
73
+ export interface Plugin {
74
+ readonly id: string;
75
+ tools?(opts: {
76
+ agent: string;
77
+ workdir: string;
78
+ }): McpServerSpec[] | Promise<McpServerSpec[]>;
79
+ promptFragment?(opts: {
80
+ agent: string;
81
+ workdir: string;
82
+ isFirstTurn: boolean;
83
+ }): string | null | undefined | Promise<string | null | undefined>;
84
+ contributeSpawn?(opts: {
85
+ agent: string;
86
+ workdir: string;
87
+ mode: 'run' | 'tui';
88
+ sessionId?: string | null;
89
+ model?: string | null;
90
+ }): SpawnContribution | null | undefined | Promise<SpawnContribution | null | undefined>;
91
+ decorateSnapshot?(snapshot: UniversalSnapshot): UniversalSnapshot;
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, DriverEvent, TuiInput, TuiSpec } from '../contracts/driver.js';
2
+ import type { UniversalUsage, UniversalPlan } from '../protocol/index.js';
3
+ export declare class ClaudeDriver implements AgentDriver {
4
+ private readonly bin;
5
+ readonly id = "claude";
6
+ readonly capabilities: {
7
+ steer: boolean;
8
+ interact: boolean;
9
+ resume: boolean;
10
+ tui: boolean;
11
+ };
12
+ constructor(bin?: string);
13
+ tui(input: TuiInput): TuiSpec;
14
+ run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>;
15
+ private usage;
16
+ }
17
+ export interface ClaudeUsageState {
18
+ input: number | null;
19
+ output: number | null;
20
+ cached: number | null;
21
+ cacheCreation?: number | null;
22
+ contextWindow?: number | null;
23
+ turnOutputTokensBase?: number | null;
24
+ }
25
+ export declare function claudeUsageOf(s: ClaudeUsageState): UniversalUsage;
26
+ export declare function claudeContextWindowFromModel(model: unknown): number | null;
27
+ export declare function claudeEffectiveContextWindow(advertised: number | null): number | null;
28
+ export declare function handleClaudeEvent(ev: any, s: any, emit: (e: DriverEvent) => void): void;
29
+ export declare function claudeUserMessage(text: string): string;
30
+ export declare function emitClaudeImages(blocks: any[], s: any, emit: (e: DriverEvent) => void): void;
31
+ export declare function todoWriteToPlan(input: any): UniversalPlan | null;
32
+ export declare function shortToolValue(value: unknown, max?: number): string;
33
+ export declare function summarizeToolUse(name: string, input: any): string;
34
+ export declare function firstResultLine(content: any): string;