@womp/spark-agent 5.6.1 → 5.7.1

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 (43) hide show
  1. package/README.md +424 -421
  2. package/dist/agent-run.d.ts +1 -1
  3. package/dist/agent.d.ts +2 -0
  4. package/dist/agent.js +3 -1
  5. package/dist/capabilities/geometry.js +14 -14
  6. package/dist/capabilities/materials.js +14 -14
  7. package/dist/capabilities/openscad-instructions.js +77 -77
  8. package/dist/capabilities/operations.js +28 -28
  9. package/dist/capabilities/scene.js +81 -81
  10. package/dist/capabilities/wompify.js +10 -10
  11. package/dist/client.d.ts +7 -0
  12. package/dist/client.js +14 -0
  13. package/dist/geometry/schemas/backend-schema.json +3393 -3393
  14. package/dist/geometry/schemas/flowir-dialect.json +1023 -1023
  15. package/dist/geometry.js +6 -6
  16. package/dist/goals.d.ts +43 -0
  17. package/dist/goals.js +37 -0
  18. package/dist/materials/recipes.js +266 -266
  19. package/dist/materials/reference.js +94 -94
  20. package/dist/materials/structure.js +10 -10
  21. package/dist/materials/surface-recipes.js +6 -6
  22. package/dist/prompt.js +74 -74
  23. package/dist/server/executor.d.ts +1 -1
  24. package/dist/server/executor.js +2 -0
  25. package/dist/server/express.js +18 -0
  26. package/dist/server/fixtures/materials/matte-blue.json +14 -14
  27. package/dist/server/fixtures/materials/polished-copper.json +14 -14
  28. package/dist/server/goal-evaluator.d.ts +29 -0
  29. package/dist/server/goal-evaluator.js +55 -0
  30. package/dist/server/index.d.ts +4 -0
  31. package/dist/server/index.js +4 -0
  32. package/dist/server/service.d.ts +17 -0
  33. package/dist/server/service.js +204 -5
  34. package/dist/server/sqlite-store.d.ts +2 -0
  35. package/dist/server/sqlite-store.js +45 -38
  36. package/dist/server/store.d.ts +2 -0
  37. package/dist/server/store.js +12 -3
  38. package/dist/server/types.d.ts +6 -0
  39. package/dist/ui.d.ts +7 -3
  40. package/dist/ui.js +3 -1
  41. package/dist/wompify.d.ts +11 -0
  42. package/dist/wompify.js +11 -2
  43. package/package.json +140 -140
package/README.md CHANGED
@@ -1,412 +1,412 @@
1
- # @womp/spark-agent
2
-
3
- Portable AI agent for Womp applications and Kakapo scenes with model-authored JavaScript.
4
-
5
- Spark Agent uses AI SDK 7 `UIMessage` and `UIMessageChunk` as its public chat protocol. Generated
6
- JavaScript uses the host's executor and capabilities. Native browser execution and isolated QuickJS
7
- execution use the same model loop. The host owns conversations, artifacts, authorization, and connections.
8
-
9
- ## Requirements
10
-
11
- ### Migrating to 5.0
12
-
13
- - Replace scene capability `read`/`edit` with `access(callback)`. Inspect the current draft
14
- through `scene.nodes({ ids, name, type, parentId, offset, limit })`, not a `nodes` array.
15
- Results default to 100, with a maximum page size of 1,000.
16
- - Update custom scene hosts to `SceneHost.access` and SDK adapters to `acquireAccess`.
17
- `onCommitted` receives the acknowledged scene; keep host access suspended until synchronization succeeds.
18
- - Remove scene revision arguments from Geometry/OpenSCAD writes. Current parameters and overrides
19
- are prepared inside protected access. Hosts must still enforce connection/project ownership.
20
- - Screenshot calls without options capture the current native viewport. Explicit `camera`
21
- position/target, projection, `horizontalFovDegrees` and `orthographicWidth` are supported;
22
- remove `targetId` and `view` presets. Camera distances use current project units.
23
- - Scene health is explicitly requested through `scene.health()`, not automatically after each edit.
24
- - This release requires Kakapo SDK 0.7.0. Native and QuickJS executors now allow 100,000 host calls
25
- per execution. Receipts, cancellation, timeout limits and uncertain-outcome handling remain enforced.
26
-
27
- ### Runtime
28
-
29
- - Node.js 22.13 or newer
30
- - AI SDK 7
31
- - `@ai-sdk/react` 4 for React clients
32
-
33
- ```bash
34
- npm install @womp/spark-agent
35
- ```
36
-
37
- ## Run a prompt from the command line
38
-
39
- With the Spark server already running:
40
-
41
- ```sh
42
- yarn agent-run --prompt "Make a cube" --model gpt-5.6-luna --reasoning max
43
- ```
44
-
45
- Use `--conversation <id>` to continue a thread and `--output <new-directory>` to save
46
- `result.json`, `transcript.json` and original run artifacts under `artifacts/`.
47
- For local runs, use the git-ignored `.spark/` directory, with a fresh subdirectory per run:
48
-
49
- ```sh
50
- yarn agent-run --prompt "Make rainbow-colored boxes" --output .spark/agent-runs/rainbow-boxes
51
- yarn agent-run --prompt "Model this object using both views" --image front.png --image side.jpg --output .spark/agent-runs/reference-model
52
- ```
53
-
54
- Repeat `--image <path>` for local PNG, JPEG, WebP or GIF references, in argument order.
55
- The required prompt and all images are sent in one user message. Files, format signatures,
56
- and the full JSON request size (including base64 overhead) are checked before contacting the server.
57
- The CLI uses the server's default 25 MiB request limit; a custom server or provider may impose
58
- stricter limits. Images are never resized, and remote URLs are not accepted.
59
- With `--output`, original bytes are saved under `inputs/`; `input.json` and the result's `input`
60
- field list ordered original filenames, MIME types and relative paths. The submitted message in
61
- `transcript.json` uses those paths instead of inline data. Generated artifacts stay in `artifacts/`.
62
-
63
- Without output, artifacts remain server references. The CLI never clears the scene or
64
- takes extra screenshots. Defaults are `gpt-6-astra`, `high`, and
65
- `--api http://127.0.0.1:8787/agent`; unsupported settings are rejected.
66
-
67
- Progress goes to stderr; one JSON result goes to stdout. Use `yarn --silent agent-run`
68
- or the installed `agent-run` executable when parsing stdout (Yarn otherwise adds banners).
69
- Usage comes from the provider, with per-step USD estimates; missing usage is not estimated.
70
- Exit codes: 0 completed, 1 run/transport/export failure, 2 invalid arguments,
71
- 3 requires user action (continue in the UI), 130 interrupted. Interrupts attempt to cancel
72
- only this invocation's run; a failed cancellation is reported and the run may still be active.
73
- Output directories must not already exist. Artifact manifests include IDs, media types,
74
- server URLs and relative paths; failed downloads retain their references and report errors.
75
- Legacy inline base64 uploads in transcripts are replaced with `[inline data omitted]`.
76
-
77
- ## Mesh Wompify
78
-
79
- Swan imports the reference mesh with Disjoint off, then requests reconstruction
80
- through the shared `mesh-wompify` and `scene-editing` skills. Tier and request
81
- helpers are exported from `@womp/spark-agent/ui`. Server mode requires Crow to
82
- install the updated package and be redeployed; a local Swan package link is insufficient.
83
-
84
- ## Package entry points
85
-
86
- - `@womp/spark-agent` — browser-safe model loop, two tools, persistence hooks, and shared contracts
87
- - `@womp/spark-agent/browser` — native JavaScript executor and local chat transport
88
- - `@womp/spark-agent/capabilities` — capability composition and skill contracts
89
- - `@womp/spark-agent/kakapo` — direct Kakapo runtime and scene capability
90
- - `@womp/spark-agent/quickjs` — optional isolated JavaScript executor
91
- - `@womp/spark-agent/ui` — browser-safe UI message types, schemas, and model catalog
92
- - `@womp/spark-agent/client` — `SparkChatTransport` and HTTP client types
93
- - `@womp/spark-agent/server` — run service, store interfaces, executor, and development stores
94
- - `@womp/spark-agent/express` — mountable Express 5 router
95
-
96
- The root, `/browser`, `/ui`, `/capabilities`, and `/client` entry points do not import QuickJS,
97
- Kakapo, Express, or React at runtime.
98
-
99
- ## Running in a host application
100
-
101
- `runSparkAgent` accepts a configured model, model messages, an executor, a capability composition,
102
- and an artifact resolver. It exposes exactly `read_skill` and `execute_javascript` to the model.
103
- Skills carry instructions, declarations, and JavaScript examples. The native executor runs in the
104
- page realm; it is not a sandbox and cannot interrupt a synchronous infinite loop.
105
-
106
- Compose only supported capabilities with `composeCapabilities({ scene, account, ... })`. Each
107
- capability supplies its skills and `createApi(context)`; the returned API is bound under `womp`.
108
- Every invocation receives a cancellation signal and an awaited `recordReceipt` hook. Capabilities
109
- own their transactions and must check cancellation at their commit boundary.
110
-
111
- For a real user interaction, call `context.waitForUser(label, async signal => ...)` outside mutation
112
- locks. The run stays active with `requires_action` progress and resumes the same invocation after
113
- input. Native and QuickJS executors exclude this wait from their execution budget; service work
114
- continues to consume time, and QuickJS guest computation still counts even if an interaction was
115
- not awaited. Abort settles the wait and late answers cannot revive it. The UI host must also reject
116
- competing requests for a shared dialog and match callbacks to the original interaction identity.
117
- Custom executors must honor the optional host-owned `userWait` argument to `execute`.
118
-
119
- Persist the user message before starting a run. Supply `persistence.beforeToolCall`,
120
- `persistence.recordReceipt`, and `persistence.afterToolCall` for awaited storage barriers.
121
- Supply `persistence.saveMessage` to save the canonical UI message at each model-step boundary and
122
- at termination, including reasoning and provider metadata. Spark awaits each checkpoint before the
123
- next model request. Consume the run's stream while awaiting its result; the result settles after
124
- the terminal checkpoint. A host run owner must keep consuming when a view detaches.
125
- A failed barrier aborts the loop. The default model-step limit is 100; a limit returns
126
- `status: "incomplete"`. A host must not automatically restart to bypass that limit.
127
- A generation that ends on any other non-`stop` reason (`length`, `content-filter`, `error`,
128
- `other`) rejects the result and ends the run `failed`: its error names the unified reason and the
129
- provider's raw one, and its diagnostics carry the unified reason as `code`.
130
-
131
- Spark does not estimate or limit input tokens or automatically shorten conversation history.
132
- The provider enforces its context limit; rejections follow the normal provider-error path.
133
-
134
- Screenshot and file APIs return serializable artifact references. `emitImage` and `emitFile` resolve
135
- them through the host's artifact resolver. Keep references resolvable when reloading history, and
136
- pass `createSparkTools(...)` to AI SDK's `convertToModelMessages` to reconstruct multimodal results.
137
- `emitImage(artifact, { inspect: false })` displays an image without adding its bytes to model input,
138
- including restored history. Inspection is enabled by default.
139
- Kakapo skill declarations are generated from its actual typed API during the build and packaged as
140
- JavaScript strings; they do not need filesystem or Markdown loading in the browser.
141
-
142
- ## Local frontend
143
-
144
- `LocalSparkChatTransport` accepts a host callback returning a `SparkAgentRun` from `runSparkAgent`.
145
- The callback receives the standard AI SDK chat request, including its messages and abort signal.
146
- It persists the initiating message, resolves the model and capabilities, and starts the shared loop.
147
- Keep ownership of the active run outside the React renderer. Local browser runs end when the page
148
- closes; `reconnectToStream` returns `null`.
149
-
150
- ## Remote frontend
151
-
152
- ```tsx
153
- import { useChat } from "@ai-sdk/react";
154
- import { SparkChatTransport } from "@womp/spark-agent/client";
155
- import {
156
- sparkDataPartSchemas,
157
- sparkMessageMetadataSchema,
158
- type SparkUIMessage,
159
- } from "@womp/spark-agent/ui";
160
-
161
- const transport = new SparkChatTransport({ api: "/agent" });
162
-
163
- const chat = useChat<SparkUIMessage>({
164
- id: conversationId,
165
- messages: initialMessages,
166
- transport,
167
- resume: true,
168
- messageMetadataSchema: sparkMessageMetadataSchema,
169
- dataPartSchemas: sparkDataPartSchemas,
170
- });
171
- ```
172
-
173
- Render `message.parts`. Standard AI SDK parts represent text, reasoning, files, tool inputs, tool
174
- outputs, approvals, and step boundaries. Spark adds typed `data-console`, `data-artifact`,
175
- `data-run-status`, `data-step-metrics`, and `data-execution-receipt` parts. Receipts are emitted before
176
- their storage barrier, keeping known effects visible even when saving the tool result fails.
177
- Hosts can consume `data-tool-input-label` once the tool's label field is complete, while the
178
- remaining input is still streaming.
179
-
180
- An explicit Stop action must cancel the durable run before stopping local observation:
181
-
182
- ```ts
183
- await transport.cancelRun(conversationId);
184
- await chat.stop();
185
- ```
186
-
187
- Closing or reloading the page only detaches the SSE subscriber. It does not cancel the run.
188
-
189
- ## HTTP and SSE
190
-
191
- `SparkChatTransport` hides the durable run lifecycle from `useChat`:
192
-
193
- ```text
194
- POST /agent/conversations/:conversationId/messages
195
- PATCH /agent/conversations/:conversationId/settings
196
- GET /agent/runs/:runId/stream?after=0
197
- POST /agent/runs/:runId/cancel
198
- ```
199
-
200
- Conversation settings contain `modelId` and `reasoningEffort`. The standalone UI saves selector
201
- changes immediately through `transport.saveConversationSettings(id, settings)` and restores them
202
- per thread. Older threads fall back to their last recorded model (or the default) and medium effort;
203
- unsupported choices fall back to supported settings. Assistant footers show the model recorded on
204
- that turn, independently of the selector.
205
-
206
- Custom stores must implement `saveConversationSettings` as a settings-only update, and preserve
207
- stored settings during `saveConversation` so streaming history saves cannot overwrite preferences.
208
- The built-in memory and SQLite stores implement this contract.
209
-
210
- The SSE response contains ordered persistence envelopes around standard AI SDK chunks:
211
-
212
- ```ts
213
- type StoredSparkChunk = {
214
- sequence: number;
215
- conversationId: string;
216
- runId: string;
217
- createdAt: string;
218
- chunk: SparkUIMessageChunk;
219
- };
220
- ```
221
-
222
- The transport unwraps each envelope and returns the contained `UIMessageChunk` to `useChat`.
223
- Active assistant messages are replayed from sequence zero when reconnecting because `useChat`
224
- reconstructs the in-progress message from the resumed stream.
225
-
226
- ## Embedding in an Express backend
227
-
228
- Spark does not call `listen()` when imported. The host owns its HTTP server and mounts the router:
229
-
230
- ```ts
231
- import { createSparkAgentRouter } from "@womp/spark-agent/express";
232
- import { SparkAgentService, createSparkRunExecutor } from "@womp/spark-agent/server";
233
-
234
- const service = new SparkAgentService({
235
- store,
236
- executeRun: createSparkRunExecutor({
237
- resolveModel,
238
- createHost,
239
- }),
240
- });
241
-
242
- app.use(
243
- "/agent",
244
- userAccess,
245
- createSparkAgentRouter({
246
- service,
247
- artifactStore,
248
- resolveOwnerId: (request) => request.user.id,
249
- }),
250
- );
251
- ```
252
-
253
- `MemorySparkAgentStore` and `MemorySparkArtifactStore` are development implementations. Production
254
- hosts must supply durable implementations backed by their database/object storage and must preserve
255
- the ordering contract of `appendRunChunk()`.
256
-
257
- `SparkAgentServiceOptions.endOrphanedRuns` defaults to `false`. Enable it only when one process
258
- owns the store: a message or Stop then ends unfinished runs not held by that process. On a shared
259
- store, enabling it could end another process's live run.
260
-
261
- Custom stores can implement `releaseActiveRun(conversationId, runId, updatedAt)` to clear the active
262
- run only if its ID still matches. Memory and SQLite stores implement it. Without it, the service
263
- serializes read-and-save release against its own submissions.
264
-
265
- ## Standalone development server
266
-
267
- The package includes a local server bound to `127.0.0.1:8787`:
268
-
269
- ```bash
270
- spark-agent
271
- ```
272
-
273
- Configure the process with:
274
-
275
- - `OPENAI_API_KEY`
276
- - `ANTHROPIC_API_KEY`
277
- - `GEMINI_API_KEY`
278
- - `KAKAPO_URL` (defaults to `ws://127.0.0.1:5502`)
279
- - `PORT` (defaults to `8787`)
280
- - `SPARK_STORAGE_PATH` (defaults to `.spark/spark.sqlite`)
281
- - `SPARK_SCENE_IDENTITY` (optional stable identity for the scene behind `KAKAPO_URL`)
282
-
283
- The CLI automatically loads `.env` from its working directory when the file exists. Copy
284
- `.env.example` to `.env`, then add the provider key you intend to use. Existing process environment
285
- variables take precedence over values loaded from the file.
286
-
287
- For repository development, `yarn dev` starts the Express server and Vite demo together. Open
288
- `http://127.0.0.1:5173`.
289
-
290
- Shared SDK scene adapters can omit `operations` to use direct edit/save with
291
- per-invocation outcomes and conversation receipts. This path does not persist a scene
292
- journal, recover uncertain edits, or reuse previous operation results. Validation,
293
- settlement, and the host's save policy still apply; geometry/OpenSCAD refund repair
294
- attempts only after confirmed rollback. Hosts that supply `operations` retain their
295
- existing journal and recovery behavior.
296
-
297
- Standalone scene edits use the existing `scene_state_patch` command followed by `save_scene` after
298
- the patch is acknowledged. The durable operation journal records the separate mutation and save
299
- outcomes. A failed or unknown save stops that operation with an explicit error, retaining any
300
- applied work without replay. The two commands are not an atomic transaction, and a renderer save
301
- acknowledgement proves only renderer-local history, not durable backend storage.
302
-
303
- The standalone server persists conversations, run streams, and screenshot bytes in SQLite. Only
304
- one local process can execute against a database. After a terminated process is replaced, startup
305
- preserves recorded output and marks abandoned queued/running runs incomplete without replaying
306
- scene edits. Pending approvals remain pending; startup does not resume abandoned runs.
307
- Node 22 reports an experimental warning for its built-in SQLite module.
308
-
309
- Set `SPARK_SCENE_IDENTITY` only when the host can verify that the value names the same durable
310
- scene across renderer reconnects. Without it, Spark uses a process-scoped identity and does not
311
- authorize interrupted scene mutations to continue after a server restart.
312
-
313
- `createStandaloneSparkAgentApp(options)` is asynchronous. Await it before listening, and call
314
- `await app.dispose()` on shutdown to settle active runs and release storage ownership.
315
-
316
- When using a sibling `kakapo-sdk` dependency, run `yarn build` in `../kakapo-sdk` before installing
317
- Spark dependencies. Replace local dependencies with registry versions before publishing.
318
-
319
- For the explicit fictional account profile, use `yarn dev:demo-account` or
320
- `spark-agent --profile demo-account`. It adds the `account` skill and `womp.account.read`
321
- beside the real local Kakapo scene API. Profile reads identify themselves as fictional fixtures;
322
- credits, billing, orders, and other account services report unavailable. The default composition
323
- omits account entirely. Neither profile supplies fake generation jobs, imports, or billing permission.
324
- Embedded development servers can select `developmentProfile: "demo-account"` explicitly.
325
-
326
- The same profile supplies two actual local material resources: `Development: Matte Blue` and
327
- `Development: Polished Copper`. They are bundled JSON files under `src/server/fixtures/materials`
328
- and imported through the normal SDK draft, not simulated scene effects. In `scene.edit`, await
329
- `api.materialImport(name)`, then assign its numeric ID. The default profile has no library service.
330
-
331
- ## Run cost estimates
332
-
333
- Prompt caching is enabled for every listed model: Claude Opus 5 and Fable 5 use
334
- automatic 5-minute caching, and GPT-6 Astra uses implicit caching with a 30-minute TTL.
335
- The providers manage the cache as message history grows across tool steps; no local
336
- response cache is used. Cache hits require matching prefixes, sufficient prompt length,
337
- and an unexpired entry. Cache writes can cost more than uncached input, while subsequent
338
- cache reads are cheaper. The UI displays actual reported reads/writes, not assumed savings.
339
- See [Claude caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)
340
- and [OpenAI caching](https://developers.openai.com/api/docs/guides/prompt-caching).
341
- Restart the server after changing these settings; existing runs keep their original configuration.
342
-
343
- The chat displays estimated API token cost in USD, updated after every completed model step.
344
- Hover or focus the amount to inspect uncached input, cache reads/writes, and output costs. Output usage
345
- already includes reasoning tokens. Image input is included in the provider's input token count.
346
-
347
- `src/pricing.ts` contains standard direct API rates verified on September 9, 2026:
348
-
349
- | Model | Input / 1M | Cache read / 1M | Cache write / 1M | Output / 1M |
350
- | -------------- | ---------- | --------------- | --------------------- | ----------- |
351
- | GPT-6 Astra | $10 | $1 | $12.50 | $50 |
352
- | Claude Opus 5 | $5 | $0.50 | $6.25 (5m), $10 (1h) | $25 |
353
- | Claude Fable 5 | $10 | $1 | $12.50 (5m), $20 (1h) | $50 |
354
-
355
- Astra requests above 272,000 input tokens use 2x input/cache rates and 1.5x output rates.
356
- This threshold is applied to each request before summing run costs. Claude's full context uses
357
- standard rates. Sources: [Astra pricing](https://developers.openai.com/api/docs/models/gpt-6-astra),
358
- [Claude pricing](https://platform.claude.com/docs/en/about-claude/pricing).
359
-
360
- Each `data-step-metrics` part stores a `cost` snapshot with the model, rate date, source, token
361
- categories, rates, and amounts. The UI sums those stored amounts, so reconnection does not double
362
- count steps and future price changes do not reprice recorded runs. Update the rate table and its
363
- verification date when provider pricing changes.
364
-
365
- The unfinished step is excluded until its usage arrives. Cancelled/failed runs show a partial
366
- estimate. Missing usage, unknown models, missing cache TTLs, or provider sub-inferences without
367
- separate attribution are marked unavailable, never silently priced as zero. Historical runs
368
- without step pricing remain unavailable. Estimates exclude taxes, negotiated discounts, and scene
369
- hosting; the provider bill is authoritative. Spark requests OpenAI's standard service tier.
370
-
371
- ## Execution security
372
-
373
- The standalone host uses a fresh QuickJS runtime for each JavaScript execution. It has no Node.js
374
- or browser globals and receives only the composed capability APIs. Imports, filesystem access,
375
- network APIs, and host environment variables are not exposed. QuickJS limits memory, stack size,
376
- execution time, source size, host calls, and data crossing the host bridge. Browser hosts can instead
377
- choose native page execution, with the page-global access and interruption limitations described above.
378
- Both executors allow 100,000 host calls per execution by default; the counter resets for each
379
- execution. QuickJS supports overriding this through `maxHostCalls`. Cancellation, timeouts,
380
- and the other execution limits still apply.
381
-
382
- QuickJS isolates generated code from the JavaScript host API; it is not an operating-system security
383
- boundary. Multi-tenant deployments must additionally isolate workers, authorize every Kakapo
384
- session and tool call, enforce quotas, and avoid passing privileged functions through the runtime
385
- bridge.
386
-
387
- ## Validation
388
-
389
- ```bash
390
- yarn build
391
- yarn typecheck
392
- yarn test
393
- yarn web:build
394
- ```
395
-
396
- ## Release procedure
397
-
1
+ # @womp/spark-agent
2
+
3
+ Portable AI agent for Womp applications and Kakapo scenes with model-authored JavaScript.
4
+
5
+ Spark Agent uses AI SDK 7 `UIMessage` and `UIMessageChunk` as its public chat protocol. Generated
6
+ JavaScript uses the host's executor and capabilities. Native browser execution and isolated QuickJS
7
+ execution use the same model loop. The host owns conversations, artifacts, authorization, and connections.
8
+
9
+ ## Requirements
10
+
11
+ ### Migrating to 5.0
12
+
13
+ - Replace scene capability `read`/`edit` with `access(callback)`. Inspect the current draft
14
+ through `scene.nodes({ ids, name, type, parentId, offset, limit })`, not a `nodes` array.
15
+ Results default to 100, with a maximum page size of 1,000.
16
+ - Update custom scene hosts to `SceneHost.access` and SDK adapters to `acquireAccess`.
17
+ `onCommitted` receives the acknowledged scene; keep host access suspended until synchronization succeeds.
18
+ - Remove scene revision arguments from Geometry/OpenSCAD writes. Current parameters and overrides
19
+ are prepared inside protected access. Hosts must still enforce connection/project ownership.
20
+ - Screenshot calls without options capture the current native viewport. Explicit `camera`
21
+ position/target, projection, `horizontalFovDegrees` and `orthographicWidth` are supported;
22
+ remove `targetId` and `view` presets. Camera distances use current project units.
23
+ - Scene health is explicitly requested through `scene.health()`, not automatically after each edit.
24
+ - This release requires Kakapo SDK 0.7.0. Native and QuickJS executors now allow 100,000 host calls
25
+ per execution. Receipts, cancellation, timeout limits and uncertain-outcome handling remain enforced.
26
+
27
+ ### Runtime
28
+
29
+ - Node.js 22.13 or newer
30
+ - AI SDK 7
31
+ - `@ai-sdk/react` 4 for React clients
32
+
33
+ ```bash
34
+ npm install @womp/spark-agent
35
+ ```
36
+
37
+ ## Run a prompt from the command line
38
+
39
+ With the Spark server already running:
40
+
41
+ ```sh
42
+ yarn agent-run --prompt "Make a cube" --model gpt-5.6-luna --reasoning max
43
+ ```
44
+
45
+ Use `--conversation <id>` to continue a thread and `--output <new-directory>` to save
46
+ `result.json`, `transcript.json` and original run artifacts under `artifacts/`.
47
+ For local runs, use the git-ignored `.spark/` directory, with a fresh subdirectory per run:
48
+
49
+ ```sh
50
+ yarn agent-run --prompt "Make rainbow-colored boxes" --output .spark/agent-runs/rainbow-boxes
51
+ yarn agent-run --prompt "Model this object using both views" --image front.png --image side.jpg --output .spark/agent-runs/reference-model
52
+ ```
53
+
54
+ Repeat `--image <path>` for local PNG, JPEG, WebP or GIF references, in argument order.
55
+ The required prompt and all images are sent in one user message. Files, format signatures,
56
+ and the full JSON request size (including base64 overhead) are checked before contacting the server.
57
+ The CLI uses the server's default 25 MiB request limit; a custom server or provider may impose
58
+ stricter limits. Images are never resized, and remote URLs are not accepted.
59
+ With `--output`, original bytes are saved under `inputs/`; `input.json` and the result's `input`
60
+ field list ordered original filenames, MIME types and relative paths. The submitted message in
61
+ `transcript.json` uses those paths instead of inline data. Generated artifacts stay in `artifacts/`.
62
+
63
+ Without output, artifacts remain server references. The CLI never clears the scene or
64
+ takes extra screenshots. Defaults are `gpt-6-astra`, `high`, and
65
+ `--api http://127.0.0.1:8787/agent`; unsupported settings are rejected.
66
+
67
+ Progress goes to stderr; one JSON result goes to stdout. Use `yarn --silent agent-run`
68
+ or the installed `agent-run` executable when parsing stdout (Yarn otherwise adds banners).
69
+ Usage comes from the provider, with per-step USD estimates; missing usage is not estimated.
70
+ Exit codes: 0 completed, 1 run/transport/export failure, 2 invalid arguments,
71
+ 3 requires user action (continue in the UI), 130 interrupted. Interrupts attempt to cancel
72
+ only this invocation's run; a failed cancellation is reported and the run may still be active.
73
+ Output directories must not already exist. Artifact manifests include IDs, media types,
74
+ server URLs and relative paths; failed downloads retain their references and report errors.
75
+ Legacy inline base64 uploads in transcripts are replaced with `[inline data omitted]`.
76
+
77
+ ## Mesh Wompify
78
+
79
+ Swan imports the reference mesh with Disjoint off, then requests reconstruction
80
+ through the shared `mesh-wompify` and `scene-editing` skills. Tier and request
81
+ helpers are exported from `@womp/spark-agent/ui`. Server mode requires Crow to
82
+ install the updated package and be redeployed; a local Swan package link is insufficient.
83
+
84
+ ## Package entry points
85
+
86
+ - `@womp/spark-agent` — browser-safe model loop, two tools, persistence hooks, and shared contracts
87
+ - `@womp/spark-agent/browser` — native JavaScript executor and local chat transport
88
+ - `@womp/spark-agent/capabilities` — capability composition and skill contracts
89
+ - `@womp/spark-agent/kakapo` — direct Kakapo runtime and scene capability
90
+ - `@womp/spark-agent/quickjs` — optional isolated JavaScript executor
91
+ - `@womp/spark-agent/ui` — browser-safe UI message types, schemas, and model catalog
92
+ - `@womp/spark-agent/client` — `SparkChatTransport` and HTTP client types
93
+ - `@womp/spark-agent/server` — run service, store interfaces, executor, and development stores
94
+ - `@womp/spark-agent/express` — mountable Express 5 router
95
+
96
+ The root, `/browser`, `/ui`, `/capabilities`, and `/client` entry points do not import QuickJS,
97
+ Kakapo, Express, or React at runtime.
98
+
99
+ ## Running in a host application
100
+
101
+ `runSparkAgent` accepts a configured model, model messages, an executor, a capability composition,
102
+ and an artifact resolver. It exposes exactly `read_skill` and `execute_javascript` to the model.
103
+ Skills carry instructions, declarations, and JavaScript examples. The native executor runs in the
104
+ page realm; it is not a sandbox and cannot interrupt a synchronous infinite loop.
105
+
106
+ Compose only supported capabilities with `composeCapabilities({ scene, account, ... })`. Each
107
+ capability supplies its skills and `createApi(context)`; the returned API is bound under `womp`.
108
+ Every invocation receives a cancellation signal and an awaited `recordReceipt` hook. Capabilities
109
+ own their transactions and must check cancellation at their commit boundary.
110
+
111
+ For a real user interaction, call `context.waitForUser(label, async signal => ...)` outside mutation
112
+ locks. The run stays active with `requires_action` progress and resumes the same invocation after
113
+ input. Native and QuickJS executors exclude this wait from their execution budget; service work
114
+ continues to consume time, and QuickJS guest computation still counts even if an interaction was
115
+ not awaited. Abort settles the wait and late answers cannot revive it. The UI host must also reject
116
+ competing requests for a shared dialog and match callbacks to the original interaction identity.
117
+ Custom executors must honor the optional host-owned `userWait` argument to `execute`.
118
+
119
+ Persist the user message before starting a run. Supply `persistence.beforeToolCall`,
120
+ `persistence.recordReceipt`, and `persistence.afterToolCall` for awaited storage barriers.
121
+ Supply `persistence.saveMessage` to save the canonical UI message at each model-step boundary and
122
+ at termination, including reasoning and provider metadata. Spark awaits each checkpoint before the
123
+ next model request. Consume the run's stream while awaiting its result; the result settles after
124
+ the terminal checkpoint. A host run owner must keep consuming when a view detaches.
125
+ A failed barrier aborts the loop. The default model-step limit is 100; a limit returns
126
+ `status: "incomplete"`. A host must not automatically restart to bypass that limit.
127
+ A generation that ends on any other non-`stop` reason (`length`, `content-filter`, `error`,
128
+ `other`) rejects the result and ends the run `failed`: its error names the unified reason and the
129
+ provider's raw one, and its diagnostics carry the unified reason as `code`.
130
+
131
+ Spark does not estimate or limit input tokens or automatically shorten conversation history.
132
+ The provider enforces its context limit; rejections follow the normal provider-error path.
133
+
134
+ Screenshot and file APIs return serializable artifact references. `emitImage` and `emitFile` resolve
135
+ them through the host's artifact resolver. Keep references resolvable when reloading history, and
136
+ pass `createSparkTools(...)` to AI SDK's `convertToModelMessages` to reconstruct multimodal results.
137
+ `emitImage(artifact, { inspect: false })` displays an image without adding its bytes to model input,
138
+ including restored history. Inspection is enabled by default.
139
+ Kakapo skill declarations are generated from its actual typed API during the build and packaged as
140
+ JavaScript strings; they do not need filesystem or Markdown loading in the browser.
141
+
142
+ ## Local frontend
143
+
144
+ `LocalSparkChatTransport` accepts a host callback returning a `SparkAgentRun` from `runSparkAgent`.
145
+ The callback receives the standard AI SDK chat request, including its messages and abort signal.
146
+ It persists the initiating message, resolves the model and capabilities, and starts the shared loop.
147
+ Keep ownership of the active run outside the React renderer. Local browser runs end when the page
148
+ closes; `reconnectToStream` returns `null`.
149
+
150
+ ## Remote frontend
151
+
152
+ ```tsx
153
+ import { useChat } from "@ai-sdk/react";
154
+ import { SparkChatTransport } from "@womp/spark-agent/client";
155
+ import {
156
+ sparkDataPartSchemas,
157
+ sparkMessageMetadataSchema,
158
+ type SparkUIMessage,
159
+ } from "@womp/spark-agent/ui";
160
+
161
+ const transport = new SparkChatTransport({ api: "/agent" });
162
+
163
+ const chat = useChat<SparkUIMessage>({
164
+ id: conversationId,
165
+ messages: initialMessages,
166
+ transport,
167
+ resume: true,
168
+ messageMetadataSchema: sparkMessageMetadataSchema,
169
+ dataPartSchemas: sparkDataPartSchemas,
170
+ });
171
+ ```
172
+
173
+ Render `message.parts`. Standard AI SDK parts represent text, reasoning, files, tool inputs, tool
174
+ outputs, approvals, and step boundaries. Spark adds typed `data-console`, `data-artifact`,
175
+ `data-run-status`, `data-step-metrics`, and `data-execution-receipt` parts. Receipts are emitted before
176
+ their storage barrier, keeping known effects visible even when saving the tool result fails.
177
+ Hosts can consume `data-tool-input-label` once the tool's label field is complete, while the
178
+ remaining input is still streaming.
179
+
180
+ An explicit Stop action must cancel the durable run before stopping local observation:
181
+
182
+ ```ts
183
+ await transport.cancelRun(conversationId);
184
+ await chat.stop();
185
+ ```
186
+
187
+ Closing or reloading the page only detaches the SSE subscriber. It does not cancel the run.
188
+
189
+ ## HTTP and SSE
190
+
191
+ `SparkChatTransport` hides the durable run lifecycle from `useChat`:
192
+
193
+ ```text
194
+ POST /agent/conversations/:conversationId/messages
195
+ PATCH /agent/conversations/:conversationId/settings
196
+ GET /agent/runs/:runId/stream?after=0
197
+ POST /agent/runs/:runId/cancel
198
+ ```
199
+
200
+ Conversation settings contain `modelId` and `reasoningEffort`. The standalone UI saves selector
201
+ changes immediately through `transport.saveConversationSettings(id, settings)` and restores them
202
+ per thread. Older threads fall back to their last recorded model (or the default) and medium effort;
203
+ unsupported choices fall back to supported settings. Assistant footers show the model recorded on
204
+ that turn, independently of the selector.
205
+
206
+ Custom stores must implement `saveConversationSettings` as a settings-only update, and preserve
207
+ stored settings during `saveConversation` so streaming history saves cannot overwrite preferences.
208
+ The built-in memory and SQLite stores implement this contract.
209
+
210
+ The SSE response contains ordered persistence envelopes around standard AI SDK chunks:
211
+
212
+ ```ts
213
+ type StoredSparkChunk = {
214
+ sequence: number;
215
+ conversationId: string;
216
+ runId: string;
217
+ createdAt: string;
218
+ chunk: SparkUIMessageChunk;
219
+ };
220
+ ```
221
+
222
+ The transport unwraps each envelope and returns the contained `UIMessageChunk` to `useChat`.
223
+ Active assistant messages are replayed from sequence zero when reconnecting because `useChat`
224
+ reconstructs the in-progress message from the resumed stream.
225
+
226
+ ## Embedding in an Express backend
227
+
228
+ Spark does not call `listen()` when imported. The host owns its HTTP server and mounts the router:
229
+
230
+ ```ts
231
+ import { createSparkAgentRouter } from "@womp/spark-agent/express";
232
+ import { SparkAgentService, createSparkRunExecutor } from "@womp/spark-agent/server";
233
+
234
+ const service = new SparkAgentService({
235
+ store,
236
+ executeRun: createSparkRunExecutor({
237
+ resolveModel,
238
+ createHost,
239
+ }),
240
+ });
241
+
242
+ app.use(
243
+ "/agent",
244
+ userAccess,
245
+ createSparkAgentRouter({
246
+ service,
247
+ artifactStore,
248
+ resolveOwnerId: (request) => request.user.id,
249
+ }),
250
+ );
251
+ ```
252
+
253
+ `MemorySparkAgentStore` and `MemorySparkArtifactStore` are development implementations. Production
254
+ hosts must supply durable implementations backed by their database/object storage and must preserve
255
+ the ordering contract of `appendRunChunk()`.
256
+
257
+ `SparkAgentServiceOptions.endOrphanedRuns` defaults to `false`. Enable it only when one process
258
+ owns the store: a message or Stop then ends unfinished runs not held by that process. On a shared
259
+ store, enabling it could end another process's live run.
260
+
261
+ Custom stores can implement `releaseActiveRun(conversationId, runId, updatedAt)` to clear the active
262
+ run only if its ID still matches. Memory and SQLite stores implement it. Without it, the service
263
+ serializes read-and-save release against its own submissions.
264
+
265
+ ## Standalone development server
266
+
267
+ The package includes a local server bound to `127.0.0.1:8787`:
268
+
269
+ ```bash
270
+ spark-agent
271
+ ```
272
+
273
+ Configure the process with:
274
+
275
+ - `OPENAI_API_KEY`
276
+ - `ANTHROPIC_API_KEY`
277
+ - `GEMINI_API_KEY`
278
+ - `KAKAPO_URL` (defaults to `ws://127.0.0.1:5502`)
279
+ - `PORT` (defaults to `8787`)
280
+ - `SPARK_STORAGE_PATH` (defaults to `.spark/spark.sqlite`)
281
+ - `SPARK_SCENE_IDENTITY` (optional stable identity for the scene behind `KAKAPO_URL`)
282
+
283
+ The CLI automatically loads `.env` from its working directory when the file exists. Copy
284
+ `.env.example` to `.env`, then add the provider key you intend to use. Existing process environment
285
+ variables take precedence over values loaded from the file.
286
+
287
+ For repository development, `yarn dev` starts the Express server and Vite demo together. Open
288
+ `http://127.0.0.1:5173`.
289
+
290
+ Shared SDK scene adapters can omit `operations` to use direct edit/save with
291
+ per-invocation outcomes and conversation receipts. This path does not persist a scene
292
+ journal, recover uncertain edits, or reuse previous operation results. Validation,
293
+ settlement, and the host's save policy still apply; geometry/OpenSCAD refund repair
294
+ attempts only after confirmed rollback. Hosts that supply `operations` retain their
295
+ existing journal and recovery behavior.
296
+
297
+ Standalone scene edits use the existing `scene_state_patch` command followed by `save_scene` after
298
+ the patch is acknowledged. The durable operation journal records the separate mutation and save
299
+ outcomes. A failed or unknown save stops that operation with an explicit error, retaining any
300
+ applied work without replay. The two commands are not an atomic transaction, and a renderer save
301
+ acknowledgement proves only renderer-local history, not durable backend storage.
302
+
303
+ The standalone server persists conversations, run streams, and screenshot bytes in SQLite. Only
304
+ one local process can execute against a database. After a terminated process is replaced, startup
305
+ preserves recorded output and marks abandoned queued/running runs incomplete without replaying
306
+ scene edits. Pending approvals remain pending; startup does not resume abandoned runs.
307
+ Node 22 reports an experimental warning for its built-in SQLite module.
308
+
309
+ Set `SPARK_SCENE_IDENTITY` only when the host can verify that the value names the same durable
310
+ scene across renderer reconnects. Without it, Spark uses a process-scoped identity and does not
311
+ authorize interrupted scene mutations to continue after a server restart.
312
+
313
+ `createStandaloneSparkAgentApp(options)` is asynchronous. Await it before listening, and call
314
+ `await app.dispose()` on shutdown to settle active runs and release storage ownership.
315
+
316
+ When using a sibling `kakapo-sdk` dependency, run `yarn build` in `../kakapo-sdk` before installing
317
+ Spark dependencies. Replace local dependencies with registry versions before publishing.
318
+
319
+ For the explicit fictional account profile, use `yarn dev:demo-account` or
320
+ `spark-agent --profile demo-account`. It adds the `account` skill and `womp.account.read`
321
+ beside the real local Kakapo scene API. Profile reads identify themselves as fictional fixtures;
322
+ credits, billing, orders, and other account services report unavailable. The default composition
323
+ omits account entirely. Neither profile supplies fake generation jobs, imports, or billing permission.
324
+ Embedded development servers can select `developmentProfile: "demo-account"` explicitly.
325
+
326
+ The same profile supplies two actual local material resources: `Development: Matte Blue` and
327
+ `Development: Polished Copper`. They are bundled JSON files under `src/server/fixtures/materials`
328
+ and imported through the normal SDK draft, not simulated scene effects. In `scene.edit`, await
329
+ `api.materialImport(name)`, then assign its numeric ID. The default profile has no library service.
330
+
331
+ ## Run cost estimates
332
+
333
+ Prompt caching is enabled for every listed model: Claude Opus 5 and Fable 5 use
334
+ automatic 5-minute caching, and GPT-6 Astra uses implicit caching with a 30-minute TTL.
335
+ The providers manage the cache as message history grows across tool steps; no local
336
+ response cache is used. Cache hits require matching prefixes, sufficient prompt length,
337
+ and an unexpired entry. Cache writes can cost more than uncached input, while subsequent
338
+ cache reads are cheaper. The UI displays actual reported reads/writes, not assumed savings.
339
+ See [Claude caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)
340
+ and [OpenAI caching](https://developers.openai.com/api/docs/guides/prompt-caching).
341
+ Restart the server after changing these settings; existing runs keep their original configuration.
342
+
343
+ The chat displays estimated API token cost in USD, updated after every completed model step.
344
+ Hover or focus the amount to inspect uncached input, cache reads/writes, and output costs. Output usage
345
+ already includes reasoning tokens. Image input is included in the provider's input token count.
346
+
347
+ `src/pricing.ts` contains standard direct API rates verified on September 9, 2026:
348
+
349
+ | Model | Input / 1M | Cache read / 1M | Cache write / 1M | Output / 1M |
350
+ | -------------- | ---------- | --------------- | --------------------- | ----------- |
351
+ | GPT-6 Astra | $10 | $1 | $12.50 | $50 |
352
+ | Claude Opus 5 | $5 | $0.50 | $6.25 (5m), $10 (1h) | $25 |
353
+ | Claude Fable 5 | $10 | $1 | $12.50 (5m), $20 (1h) | $50 |
354
+
355
+ Astra requests above 272,000 input tokens use 2x input/cache rates and 1.5x output rates.
356
+ This threshold is applied to each request before summing run costs. Claude's full context uses
357
+ standard rates. Sources: [Astra pricing](https://developers.openai.com/api/docs/models/gpt-6-astra),
358
+ [Claude pricing](https://platform.claude.com/docs/en/about-claude/pricing).
359
+
360
+ Each `data-step-metrics` part stores a `cost` snapshot with the model, rate date, source, token
361
+ categories, rates, and amounts. The UI sums those stored amounts, so reconnection does not double
362
+ count steps and future price changes do not reprice recorded runs. Update the rate table and its
363
+ verification date when provider pricing changes.
364
+
365
+ The unfinished step is excluded until its usage arrives. Cancelled/failed runs show a partial
366
+ estimate. Missing usage, unknown models, missing cache TTLs, or provider sub-inferences without
367
+ separate attribution are marked unavailable, never silently priced as zero. Historical runs
368
+ without step pricing remain unavailable. Estimates exclude taxes, negotiated discounts, and scene
369
+ hosting; the provider bill is authoritative. Spark requests OpenAI's standard service tier.
370
+
371
+ ## Execution security
372
+
373
+ The standalone host uses a fresh QuickJS runtime for each JavaScript execution. It has no Node.js
374
+ or browser globals and receives only the composed capability APIs. Imports, filesystem access,
375
+ network APIs, and host environment variables are not exposed. QuickJS limits memory, stack size,
376
+ execution time, source size, host calls, and data crossing the host bridge. Browser hosts can instead
377
+ choose native page execution, with the page-global access and interruption limitations described above.
378
+ Both executors allow 100,000 host calls per execution by default; the counter resets for each
379
+ execution. QuickJS supports overriding this through `maxHostCalls`. Cancellation, timeouts,
380
+ and the other execution limits still apply.
381
+
382
+ QuickJS isolates generated code from the JavaScript host API; it is not an operating-system security
383
+ boundary. Multi-tenant deployments must additionally isolate workers, authorize every Kakapo
384
+ session and tool call, enforce quotas, and avoid passing privileged functions through the runtime
385
+ bridge.
386
+
387
+ ## Validation
388
+
389
+ ```bash
390
+ yarn build
391
+ yarn typecheck
392
+ yarn test
393
+ yarn web:build
394
+ ```
395
+
396
+ ## Release procedure
397
+
398
398
  Pushes to `main` run `.github/workflows/publish.yml`. Publication is driven by
399
399
  `package.json`, not Git tags: already-published versions skip; unpublished stable versions
400
400
  newer than npm's `latest` run release checks and publish automatically.
401
-
402
- Versions follow **MAJOR.MINOR.PATCH** (for example, `4.4.0`):
403
-
404
- - **Major:** breaking changes.
405
- - **Minor:** backward-compatible new features.
406
- - **Patch:** backward-compatible bug fixes.
407
-
408
- ### Publishing
409
-
401
+
402
+ Versions follow **MAJOR.MINOR.PATCH** (for example, `4.4.0`):
403
+
404
+ - **Major:** breaking changes.
405
+ - **Minor:** backward-compatible new features.
406
+ - **Patch:** backward-compatible bug fixes.
407
+
408
+ ### Publishing
409
+
410
410
  1. Configure npm's trusted publisher once for `@womp/spark-agent`: organization `wompxyz`,
411
411
  repository `spark-agent`, workflow filename `publish.yml`, no environment, and allow direct
412
412
  `npm publish`. GitHub-hosted runners use OIDC; no publishing token is required.
@@ -414,23 +414,26 @@ Versions follow **MAJOR.MINOR.PATCH** (for example, `4.4.0`):
414
414
  the release commit to `main`. Prereleases/build metadata are not published by this workflow.
415
415
  3. CI installs the frozen Yarn lockfile, runs typechecks, tests and the UI build, then checks
416
416
  package entry files and imports every exported module from a fresh tarball installation.
417
- It rechecks npm before publishing that same archive and verifies registry integrity and `latest`.
417
+ It rechecks npm before publishing that same archive and verifies registry integrity and `latest`,
418
+ allowing up to five minutes of polling waits for npm processing and metadata propagation.
419
+ A visible integrity mismatch fails immediately; missing metadata or a lagging `latest` fails
420
+ if it does not converge within the polling limit.
418
421
  Releases are serialized; newer pending pushes may supersede older pending runs.
419
422
  Registry failures stop publication rather than being treated as missing versions.
420
423
  4. If publication reports an uncertain result, inspect npm before rerunning the failed workflow.
421
424
  Published versions cannot be replaced; corrections need a new version. No tags are required.
422
425
  5. In the consuming application, use `yarn add @womp/spark-agent@<version> --exact`. Commit its
423
- manifest and lockfile together, and verify a clean frozen-lockfile installation and production
424
- build. Remove the temporary archive dependency. Application deployment and merge requirements
425
- remain separate from package publication.
426
-
427
- ## Layout
428
-
429
- - `src/agent.ts` — AI SDK stream and Spark tool orchestration
430
- - `src/ui.ts` — browser-safe protocol types and validation schemas
431
- - `src/client.ts` — durable `ChatTransport` implementation
432
- - `src/server/` — service, storage contracts, Express adapter, artifacts, and standalone composition
433
- - `src/runtime.ts` — Kakapo scene connections and edit transactions
434
- - `src/quickjs-executor.ts` — isolated JavaScript execution and host bridge
435
- - `src/womp-scene-api.ts` — Womp-compatible Kakapo scene API
436
- - `web/src/App.tsx` — reference `useChat<SparkUIMessage>` application
426
+ manifest and lockfile together, and verify a clean frozen-lockfile installation and production
427
+ build. Remove the temporary archive dependency. Application deployment and merge requirements
428
+ remain separate from package publication.
429
+
430
+ ## Layout
431
+
432
+ - `src/agent.ts` — AI SDK stream and Spark tool orchestration
433
+ - `src/ui.ts` — browser-safe protocol types and validation schemas
434
+ - `src/client.ts` — durable `ChatTransport` implementation
435
+ - `src/server/` — service, storage contracts, Express adapter, artifacts, and standalone composition
436
+ - `src/runtime.ts` — Kakapo scene connections and edit transactions
437
+ - `src/quickjs-executor.ts` — isolated JavaScript execution and host bridge
438
+ - `src/womp-scene-api.ts` — Womp-compatible Kakapo scene API
439
+ - `web/src/App.tsx` — reference `useChat<SparkUIMessage>` application