@luckydraw/cumulus 0.31.65 → 0.31.66

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 (3) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +138 -13
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.31.66
4
+
5
+ - New README guide for building persistent web-app agents: gateway setup, front-end integration, and the security model.
6
+
3
7
  ## v0.31.65
4
8
 
5
9
  You've hit your weekly limit · resets Aug 6, 9pm (UTC)
package/README.md CHANGED
@@ -9,6 +9,7 @@ Originally a CLI wrapper for Claude, Cumulus has grown into a full gateway platf
9
9
  - **Gateway daemon** (`cumulus-gateway`) — HTTP + WebSocket server with per-thread conversations, streaming responses, and an admin API.
10
10
  - **Web chat widget** — embeddable `/chat` interface with voice mode, push notifications, file uploads with progress, and rich [blex block](https://www.npmjs.com/package/@luckydraw/blex) rendering (tables, forms, charts, kanban, diagrams).
11
11
  - **Channel adapters** — Slack and Discord bots, inbound email webhooks (Resend), and generic HTTP webhooks — all injecting into the same thread model.
12
+ - **Persistent web-app agents** — embed an agent into any web app that can _drive its UI_, answer questions about its data, and remember every conversation per visitor. See [Persistent web-app agents](#persistent-web-app-agents).
12
13
  - **Inter-agent messaging** — threads can talk to each other via `send_to_agent`, with support for CC/BCC visibility.
13
14
  - **Per-thread model selection** — Claude (via CLI) or any HuggingFace model (GLM-5, Kimi-K2.5, Qwen3, etc.) with tool calling.
14
15
  - **Scheduled triggers, email, push, media serving** — built-in MCP tools so agents can send emails, schedule themselves, notify you, and upload files.
@@ -207,6 +208,139 @@ cumulus-gateway rollback # restores the previous version
207
208
 
208
209
  The widget's top bar also shows an "Update available" indicator (with a manual ↻ check button) when a new version lands on npm.
209
210
 
211
+ ## Persistent web-app agents
212
+
213
+ Cumulus can embed a persistent agent into any web app — one that **drives the app's UI**, **answers questions about the app and its data**, and **remembers every conversation per visitor**. No fork of the gateway, no per-app backend beyond a static file server.
214
+
215
+ Requires `>= 0.31.41`.
216
+
217
+ ### How it works
218
+
219
+ ```
220
+ Browser tab (your app) cumulus gateway
221
+ ┌───────────────────────────────┐ ┌──────────────────────────────────┐
222
+ │ your app UI │ │ one thread per visitor: │
223
+ │ ├─ command registry │ wss │ myapp-<deviceId> │
224
+ │ │ (window.MyAppAgent) │ /bridge │ ├─ full history + RAG │
225
+ │ ├─ BridgeClient ────────────┼────────────▶│ ├─ per-thread config │
226
+ │ ├─ agent panel (chat UI) │ https │ │ (inherited from │
227
+ │ │ POST /api/thread/… ─────┼────────────▶│ │ myapp.config.json) │
228
+ │ └─ selection / right-click │ SSE │ └─ model turn per message │
229
+ │ feedback capture │ │ └─ MCP shim ──────────────┼──┐
230
+ └───────────────────────────────┘ └──────────────────────────────────┘ │
231
+ ▲ │
232
+ POST /bridge/call ◀───────────────────┘
233
+ (agent tool call → executes in the tab)
234
+ ```
235
+
236
+ Three moving parts:
237
+
238
+ 1. **The gateway** — owns history, RAG retrieval, and prompt assembly, and runs a model turn per message. One config block per app; no code changes.
239
+ 2. **Your front end** — registers a typed **command registry** (what the agent can see and do in the UI), mounts the **bridge client** (a WebSocket back to the gateway), and renders the agent panel.
240
+ 3. **An MCP shim** — a small stdio script the gateway spawns per turn. It fetches the app's command manifest and exposes each command as a model-callable tool; calls are forwarded to `POST /bridge/call`, which dispatches them into the live browser tab.
241
+
242
+ The loop that makes the agent "drive the app": the model calls a tool → shim → `POST /bridge/call` → gateway pushes `call` over the tab's WebSocket → the tab executes it through the app's own actions (so guards, routing, and notifications all still fire) → the result flows back to the model.
243
+
244
+ Because the manifest is re-fetched every turn, **shipping a new UI command makes it agent-callable with zero gateway or backend changes.**
245
+
246
+ ### Key concepts
247
+
248
+ | Concept | What it is |
249
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
250
+ | **Namespace** | A config block grouping an app's threads (`myapp-*`), scoping its API key, and carrying its per-app settings (proxy, MCP servers). |
251
+ | **Base thread** (`myapp`) | Your own management thread for the app, visible only to your admin key. Never used by visitors — but it _is_ the config template they inherit. |
252
+ | **Visitor threads** (`myapp-<id>`) | One per browser/device, minted client-side. Full persistent history + RAG each. Hidden from the default thread list. |
253
+ | **Bridge** | The gateway↔tab WebSocket: the tab registers its command manifest; the gateway dispatches agent tool calls into the tab. |
254
+ | **Command registry** | The app-side catalog of typed commands (`{ name, description, params, risk, execute }`) — one capability surface shared by the UI and the agent. |
255
+ | **Capability-by-name** | The security model: a scoped key can only touch threads in its namespace, can enumerate nothing, and the random thread name is the per-visitor secret. |
256
+
257
+ ### Gateway setup
258
+
259
+ One edit to `~/.cumulus/gateway.config.json` (annotated below; the real file is strict JSON):
260
+
261
+ ```jsonc
262
+ {
263
+ "bridge": { "enabled": true }, // global, default off — every bridge surface is inert until enabled
264
+
265
+ "namespaces": [
266
+ {
267
+ "name": "myapp", // covers threads matching myapp-*
268
+ "label": "My App",
269
+ "apiKeys": ["sk-myapp-<random>"], // the app's OWN key — mint a fresh one
270
+
271
+ // OPTIONAL: reverse-proxy selected paths to the app's backend through the
272
+ // gateway origin, so the front end needs only one origin.
273
+ "executorProxy": {
274
+ "origin": "http://127.0.0.1:8097",
275
+ "pathPrefixes": ["/state", "/journal"],
276
+ },
277
+
278
+ // OPTIONAL, but required for "drive the app": the MCP shim that turns the
279
+ // app's command manifest into model-callable tools. Spawned per turn, only
280
+ // for threads in this namespace. {thread} is substituted with the real
281
+ // thread name (myapp-<deviceId>) in both args and env.
282
+ "extraMcpServers": {
283
+ "myapp-tools": {
284
+ "command": "node",
285
+ "args": ["/path/to/myapp/mcp-shim.js"],
286
+ "env": {
287
+ "GATEWAY_URL": "http://127.0.0.1:8090",
288
+ "GATEWAY_API_KEY": "sk-myapp-<same-scoped-key>",
289
+ "BRIDGE_THREAD": "{thread}",
290
+ },
291
+ },
292
+ },
293
+ },
294
+ ],
295
+ }
296
+ ```
297
+
298
+ Then give visitor threads their persona and working directory via the base thread config, `~/.cumulus/threads/myapp.config.json`:
299
+
300
+ ```json
301
+ {
302
+ "projectDir": "/home/you/projects/myapp",
303
+ "model": "claude",
304
+ "effort": "high",
305
+ "alwaysInclude": ["docs/myapp-system-prompt.md"]
306
+ }
307
+ ```
308
+
309
+ A turn on `myapp-a3f8c2d1` with no config of its own inherits this by prefix-fallback. Writes stay exact, so a visitor session can never mutate the base. `alwaysInclude` is where the app's product knowledge and persona live.
310
+
311
+ ### Front-end integration
312
+
313
+ Your app ships four small pieces (all vanilla-JS-able, no framework required):
314
+
315
+ | Piece | Job |
316
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
317
+ | **Device thread id** | Mint `myapp-<random hex>` once, persist in `localStorage`. This _is_ the visitor's identity — use 16+ hex chars of entropy. |
318
+ | **Command registry** | Expose `window.MyAppAgent` with typed commands. Include a `describeView`-style command so the agent can read the current screen. |
319
+ | **Bridge mount** | Open `wss://<gateway>/bridge`, register the manifest, execute inbound `call` frames against the registry. |
320
+ | **Chat client** | `POST /api/thread/<thread>/message` and render the SSE stream. Mark risky commands so they route through a confirm step. |
321
+
322
+ Serve the gateway URL and scoped key to the browser at request time (e.g. `window.__APP_AGENT_CONFIG__` injected by your serving layer from an env var) — never commit the key into the repo.
323
+
324
+ ### Security model
325
+
326
+ The browser must hold a credential — the bridge sends its key inside a WebSocket frame, which no reverse proxy can inject. So the model is _confinement_, not concealment:
327
+
328
+ - A **scoped key** can read/write only `myapp-*` threads; everything else is `403`.
329
+ - A scoped key **enumerates nothing** — `/api/threads`, `/api/agents`, and the dashboard all return empty. Reaching another visitor's thread means guessing its random name.
330
+ - The **thread name is the per-visitor secret**, the same capability-URL pattern as content-hashed `/media/*` filenames, one notch stricter (unguessable _and_ key-gated).
331
+
332
+ Mint fresh device ids with at least 16 hex characters; 8 is too thin against a determined brute-forcer.
333
+
334
+ ### Checklist
335
+
336
+ 1. Enable `bridge` and add the namespace + scoped key to the gateway config; reload.
337
+ 2. Create the base thread config with `projectDir`, `model`, and the `alwaysInclude` system-prompt document.
338
+ 3. Verify the scoped key: in-namespace `200`, out-of-namespace `403`, `/api/threads` empty.
339
+ 4. Inject the gateway URL + scoped key into the page at serve time.
340
+ 5. Ship the device thread id, command registry, bridge mount, and chat client.
341
+ 6. Point the namespace's `extraMcpServers` shim at your manifest endpoint.
342
+ 7. End-to-end check: ask the agent a question about the current screen, then ask it to navigate.
343
+
210
344
  ## Classic CLI mode
211
345
 
212
346
  The original RLM chat loop still works. Great for quick terminal work without running the gateway.
@@ -291,28 +425,19 @@ All `/api/*` routes require `X-API-Key: <key>` (from `apiKeys[]`).
291
425
 
292
426
  WebSocket (`/chat/ws`) carries the same semantics with streaming, interjection, `inject`, and voice-mode audio frames.
293
427
 
294
- ## Development
428
+ ## Upgrading
295
429
 
296
430
  ```bash
297
- git clone https://github.com/soapko/cumulus
298
- cd cumulus
299
- npm install
300
- npm run build
301
- npm test # vitest
302
- npm run lint
303
- npm run type-check
431
+ npm install -g @luckydraw/cumulus@latest
432
+ cumulus-gateway reload # SIGHUP — drains active streams before restarting
304
433
  ```
305
434
 
306
- - **Build:** `dist/` compiled TypeScript plus static assets (widget HTML/CSS/JS, blex bundles).
307
- - **Tests:** vitest, 700+ tests covering agentic loop, retriever, adapters, scheduler, push.
308
- - **Deploy workflow:** bump version, `npm publish`, then `cumulus-gateway reload` on the host (SIGHUP drains active streams — see `docs/tasks/050-graceful-restart.md`).
435
+ Or use the built-in updater (`cumulus-gateway update` / `rollback`), which does the same thing and keeps the previous version for a one-command rollback. See [CHANGELOG.md](./CHANGELOG.md) for release notes.
309
436
 
310
437
  ## Background
311
438
 
312
439
  Cumulus implements the **Recursive Language Model** pattern: treat conversation history as an external environment the model queries programmatically, rather than stuffing everything into context. This enables reasoning over contexts 2+ orders of magnitude beyond the model's window, with graceful cost scaling.
313
440
 
314
- See `docs/` for task documents, ADRs, and implementation notes.
315
-
316
441
  ## License
317
442
 
318
443
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luckydraw/cumulus",
3
- "version": "0.31.65",
3
+ "version": "0.31.66",
4
4
  "description": "RLM-based CLI chat wrapper for Claude with external history context management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",