You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f9577fd
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: AGENTS.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,6 +41,7 @@ Ahead-of-time build artifacts that live under `src/` - the shadow-root styleshee
41
41
## Conventions
42
42
43
43
- RPC functions must use `defineRpcFunction`; always namespace IDs `devframes:plugin:<slug>:<fn-name>` (matching the plugin's `@devframes/plugin-<slug>` package name).
44
+
- **No magic event names — use the centralized event maps.** Every event, broadcast, shared-state key, and channel name lives in one of two source-of-truth maps: `DEVFRAME_EVENTS` (`packages/devframe/src/events.ts`, re-exported from `devframe/constants`) for the core runtime, and `HUB_EVENTS` (`packages/hub/src/events.ts`, re-exported from `@devframes/hub/constants`) for the hub. Reference `DEVFRAME_EVENTS.*` / `HUB_EVENTS.*` at call sites (`.events.emit`/`.on`, `rpc.broadcast({ method })`, `sharedState.get(key)`, `defineHubRpcFunction({ name })`, `rpc.call`) instead of re-typing a string literal. The two maps and the [`docs/guide/events.md`](docs/guide/events.md) Events Reference are kept in lockstep: adding, renaming, or removing a name means editing the map **and** that page in the same change — every name in the maps appears in the tables, and vice versa. The only literals left are unavoidable type-position keys (the `EventEmitter<…>` maps in `types/*` and the `DevframeRpcClientFunctions`/`DevframeRpcServerFunctions` augmentations), which mirror the maps; a package that deliberately avoids a hub dependency (e.g. `@devframes/plugin-terminals`, which models the hub bridge structurally) keeps a local literal rather than importing `HUB_EVENTS`.
44
45
-**Stay validator-neutral.**`devframe` and every `@devframes/*` package must not introduce a preferred schema validator dependency - no `valibot`, `zod`, `arktype`, etc. in their runtime `dependencies`. `args`/`returns`/flag schemas are typed against [Standard Schema](https://standardschema.dev/) (`@standard-schema/spec`, types-only); first-party code that needs to author a schema uses the built-in zero-dep `devframe/utils/simple-schema` builder (deliberately minimal - not a general validator). JSON-schema conversion uses each schema's own Standard JSON Schema converter (`~standard.jsonSchema`, implemented by e.g. zod 4) when present and degrades to a permissive object otherwise - no converter library and no vendor dependency is required. Docs, by contrast, should point *users* at a real validator for their own integrations - recommend **valibot** (lightest) or **zod** (worth reusing if they already pull it via the JSON-render or MCP integrations).
45
46
- Shared state via `devframe/utils/shared-state`; keep values serializable.
46
47
- Utility imports use the package-path form `devframe/utils/*`, never relative `../utils/*`.
Devframe carries change notifications across a few distinct channels. What separates them is **direction and reach**: an in-process event bus that never leaves the node process, server RPC methods a client calls, and server-pushed broadcasts and shared state a client reads.
8
+
9
+
Two naming prefixes mark the wire surface: `hub:` for hub-layer server RPC (client → server actions), and `devframe:` for the client-facing devframe protocol (broadcasts, shared state, and streams pushed server → client). The internal event bus mirrors the same plural subsystem vocabulary (`docks`, `terminals`, `messages`, `commands`), so each internal event lines up with its wire counterpart — `docks:activate` fans out to `devframe:docks:activate`.
10
+
11
+
Every name on this page has one home in code: the [`HUB_EVENTS`](https://github.com/devframes/devframe/blob/main/packages/hub/src/events.ts) map (`@devframes/hub/constants`) backs the hub tables, and the [`DEVFRAME_EVENTS`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/events.ts) map (`devframe/constants`) backs the core tables. Call sites reference `HUB_EVENTS.*` / `DEVFRAME_EVENTS.*` rather than re-typing a literal, and this page and those maps move together — changing one without the other is a bug.
12
+
13
+
## Hub events
14
+
15
+
### Internal node event bus
16
+
17
+
Each subsystem host emits on `ctx.<subsystem>.events`. These fire and are consumed **inside the same node process** — chiefly by `createHubContext`, which fans them out onto the wire. They never cross to the browser.
18
+
19
+
| Event | Emitted by | Consumed by | Payload |
20
+
|---|---|---|---|
21
+
|`docks:entry:updated`|`DocksHost.register` / `update`| context → `devframe:docks` shared state |`DevframeDockUserEntry`|
|`commands:registered` / `commands:unregistered`|`CommandsHost` register / update / unregister | context → `devframe:commands` shared state | entry / id |
26
+
27
+
The `docks:entry:updated` and `terminals:session:updated` middle nouns (`entry`, `session`) name the specific record type; the messages and commands subsystems imply their record in the subsystem name, so they carry the verb directly.
28
+
29
+
### Server RPC methods — client → server
30
+
31
+
A connected client (any mounted iframe or panel, on its own RPC client) calls these; the hub node handles them. Carry the `hub:` prefix.
32
+
33
+
| Method | Signature | Purpose |
34
+
|---|---|---|
35
+
|`hub:docks:activate`|`({ dockId, params? }) => void`| Ask the viewer to switch its active dock — see [Deep Linking](./deep-linking). |
36
+
|`hub:commands:execute`|`(id, ...args) => unknown`| Invoke a registered server command by id. |
37
+
|`hub:messages:add`|`(input) => DevframeMessageEntry`| Add a message to the feed (marked `from: 'browser'`). |
38
+
|`hub:messages:update`|`(id, patch) => DevframeMessageEntry \| undefined`| Patch a message by id. |
39
+
|`hub:messages:remove`|`(id) => void`| Remove a message by id. |
40
+
|`hub:messages:clear`|`() => void`| Remove every message. |
41
+
|`hub:terminals:write`|`(id, data) => void`| Send input to an interactive PTY session. |
|`hub:terminals:terminate`|`(id) => void`| Kill a session's process, keeping it registered. |
44
+
|`hub:terminals:restart`|`(id) => void`| Re-run a session's command in place. |
45
+
|`hub:terminals:remove`|`(id) => void`| Kill a session's process and drop it from the registry. |
46
+
47
+
### Broadcasts & shared state — server → client
48
+
49
+
The server pushes these; a hub-aware client reads or subscribes. Carry the `devframe:` prefix. A UI subscribes to broadcasts via `rpc.client.register(...)`; the [client host](./client-context) registers the `devframe:docks:activate` handler for you.
50
+
51
+
| Name | Kind | Carries |
52
+
|---|---|---|
53
+
|`devframe:docks:activate`| broadcast | Live "switch active dock" request — the client host calls its local `switchEntry`. |
|`devframe:user-settings`| shared state | Persisted per-workspace hub settings (`DevframeDocksUserSettings`). |
60
+
|`devframe:terminals`| streaming channel | Live terminal output stream, keyed by session id. |
61
+
62
+
The [`devframe:docks:active`](./shared-state) mirror pairs with the `devframe:docks:activate` broadcast: the broadcast reaches docks already on screen, while the mirror lets a dock that mounts *because* of the switch converge on the same request instead of missing it.
63
+
64
+
## Core devframe events
65
+
66
+
The core `devframe` runtime (below the hub) carries its own notification channels — the agent host's change events, the client connection lifecycle, and the server-pushed broadcasts that power shared state and streaming. These are backed by `DEVFRAME_EVENTS` (`devframe/constants`).
67
+
68
+
This map covers notifications only. The request/response RPC endpoints of the shared-state, streaming, and auth-handshake protocols (`devframe:rpc:server-state:*`, `devframe:streaming:subscribe`, `anonymous:devframe:auth`, …) are defined at their handlers and typed in `types/rpc-augments.ts` — they aren't events.
69
+
70
+
### Node host bus
71
+
72
+
Emitted on `ctx.agent.events` as the agent-exposed tool/resource surface changes; protocol adapters (e.g. the MCP server) subscribe to re-publish their manifest.
73
+
74
+
| Event | Emitted by | Payload |
75
+
|---|---|---|
76
+
|`agent:manifest:changed`| any tool/resource/provider change | — |
77
+
|`agent:tool:registered` / `agent:tool:unregistered`|`registerTool` / `unregisterTool`|`AgentTool` / id |
78
+
|`agent:resource:registered` / `agent:resource:unregistered`|`registerResource` / `unregisterResource`|`AgentResource` / id |
79
+
80
+
### Client connection events
81
+
82
+
Emitted on the RPC client's `rpc.events` emitter (`RpcClientEvents`) for a UI to track connection lifecycle and surface errors.
|`rpc:error`| An RPC call rejected (`error`, `method`). |
88
+
|`connection:status`| Connection status changed (`status`, `previous`). |
89
+
|`connection:error`| A connection-level error (WebSocket errored, or trust refused). |
90
+
91
+
### Broadcasts — server → client
92
+
93
+
Pushed from the server to subscribed clients over the `devframe:` protocol. Wired by the framework's own hosts; not registered manually.
94
+
95
+
| Name | Carries |
96
+
|---|---|
97
+
|`devframe:auth:revoked`| This connection's bearer token was revoked; the client drops to untrusted. |
98
+
|`devframe:rpc:client-state:updated`| Full shared-state snapshot for a key. |
99
+
|`devframe:rpc:client-state:patch`| Incremental shared-state patch for a key. |
100
+
|`devframe:streaming:chunk`| A streaming chunk for a subscribed channel/id. |
101
+
|`devframe:streaming:end`| A streaming terminator (optionally an error). |
102
+
|`devframe:streaming:upload-cancel`| Server-side cancel of an in-flight upload. |
103
+
104
+
Plus one `postMessage` channel, `devframe:remote-assets-error`, that the remote-assets fallback page posts to `window.parent` so an embedding viewer can replace the bare 502 page with its own UI.
|`hub:docks:activate` RPC |`({ dockId, params? }) => void`| Switch the active dock from any client. |
277
277
278
-
Plus broadcast notifications (`devframe:docks:activate`, `devframe:terminals:updated`, `devframe:messages:updated`) that a UI can subscribe to via `rpc.client.register(...)`. The client host registers the `devframe:docks:activate` handler for you.
278
+
Plus broadcast notifications (`devframe:docks:activate`, `devframe:terminals:updated`, `devframe:messages:updated`) that a UI can subscribe to via `rpc.client.register(...)`. The client host registers the `devframe:docks:activate` handler for you. The [Events Reference](./events) tables every channel across all four subsystems.
0 commit comments