@possumtech/rummy 0.2.7 → 0.3.0
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.
- package/.env.example +12 -3
- package/EXCEPTIONS.md +46 -0
- package/PLUGINS.md +454 -197
- package/SPEC.md +284 -93
- package/migrations/001_initial_schema.sql +57 -70
- package/package.json +16 -10
- package/service.js +1 -1
- package/src/agent/AgentLoop.js +254 -70
- package/src/agent/ContextAssembler.js +18 -4
- package/src/agent/KnownStore.js +156 -23
- package/src/agent/ProjectAgent.js +5 -4
- package/src/agent/ResponseHealer.js +21 -1
- package/src/agent/TurnExecutor.js +393 -115
- package/src/agent/XmlParser.js +92 -39
- package/src/agent/known_checks.sql +5 -4
- package/src/agent/known_queries.sql +4 -3
- package/src/agent/known_store.sql +45 -15
- package/src/agent/loops.sql +63 -0
- package/src/agent/runs.sql +7 -7
- package/src/agent/schemes.sql +5 -2
- package/src/agent/tokens.js +6 -21
- package/src/agent/turns.sql +13 -4
- package/src/hooks/Hooks.js +18 -0
- package/src/hooks/PluginContext.js +14 -10
- package/src/hooks/RummyContext.js +30 -10
- package/src/hooks/ToolRegistry.js +83 -19
- package/src/llm/LlmProvider.js +27 -8
- package/src/llm/OpenAiClient.js +20 -0
- package/src/llm/OpenRouterClient.js +24 -2
- package/src/llm/XaiClient.js +47 -2
- package/src/plugins/ask_user/README.md +4 -4
- package/src/plugins/ask_user/ask_user.js +8 -7
- package/src/plugins/ask_user/ask_userDoc.js +29 -0
- package/src/plugins/budget/BudgetGuard.js +74 -0
- package/src/plugins/budget/README.md +43 -0
- package/src/plugins/budget/budget.js +79 -0
- package/src/plugins/cp/README.md +5 -4
- package/src/plugins/cp/cp.js +16 -12
- package/src/plugins/cp/cpDoc.js +29 -0
- package/src/plugins/current/README.md +4 -4
- package/src/plugins/current/current.js +12 -10
- package/src/plugins/engine/engine.sql +5 -10
- package/src/plugins/engine/turn_context.sql +13 -13
- package/src/plugins/env/README.md +3 -4
- package/src/plugins/env/env.js +8 -7
- package/src/plugins/env/envDoc.js +29 -0
- package/src/plugins/file/README.md +9 -12
- package/src/plugins/file/file.js +34 -45
- package/src/plugins/get/README.md +2 -2
- package/src/plugins/get/get.js +28 -11
- package/src/plugins/get/getDoc.js +41 -0
- package/src/plugins/hedberg/docs.md +0 -9
- package/src/plugins/hedberg/hedberg.js +4 -6
- package/src/plugins/hedberg/matcher.js +1 -1
- package/src/plugins/hedberg/normalize.js +28 -0
- package/src/plugins/hedberg/patterns.js +31 -33
- package/src/plugins/hedberg/sed.js +17 -10
- package/src/plugins/helpers.js +2 -2
- package/src/plugins/index.js +93 -28
- package/src/plugins/instructions/README.md +6 -2
- package/src/plugins/instructions/instructions.js +21 -5
- package/src/plugins/instructions/preamble.md +9 -5
- package/src/plugins/known/README.md +10 -7
- package/src/plugins/known/known.js +33 -23
- package/src/plugins/known/knownDoc.js +33 -0
- package/src/plugins/mv/README.md +5 -4
- package/src/plugins/mv/mv.js +16 -12
- package/src/plugins/mv/mvDoc.js +31 -0
- package/src/plugins/persona/persona.js +78 -0
- package/src/plugins/previous/README.md +2 -2
- package/src/plugins/previous/previous.js +12 -8
- package/src/plugins/progress/progress.js +44 -12
- package/src/plugins/prompt/README.md +5 -5
- package/src/plugins/prompt/prompt.js +23 -19
- package/src/plugins/rm/README.md +4 -4
- package/src/plugins/rm/rm.js +29 -12
- package/src/plugins/rm/rmDoc.js +30 -0
- package/src/plugins/rpc/README.md +15 -28
- package/src/plugins/rpc/rpc.js +63 -107
- package/src/plugins/set/README.md +13 -12
- package/src/plugins/set/set.js +82 -21
- package/src/plugins/set/setDoc.js +45 -0
- package/src/plugins/sh/README.md +4 -4
- package/src/plugins/sh/sh.js +8 -7
- package/src/plugins/sh/shDoc.js +29 -0
- package/src/plugins/{skills/skills.js → skill/skill.js} +12 -54
- package/src/plugins/summarize/README.md +6 -5
- package/src/plugins/summarize/summarize.js +7 -6
- package/src/plugins/summarize/summarizeDoc.js +33 -0
- package/src/plugins/telemetry/telemetry.js +20 -8
- package/src/plugins/think/README.md +20 -0
- package/src/plugins/think/think.js +5 -0
- package/src/plugins/unknown/README.md +5 -5
- package/src/plugins/unknown/unknown.js +11 -8
- package/src/plugins/unknown/unknownDoc.js +31 -0
- package/src/plugins/update/README.md +3 -8
- package/src/plugins/update/update.js +7 -6
- package/src/plugins/update/updateDoc.js +33 -0
- package/src/server/ClientConnection.js +3 -5
- package/src/server/RpcRegistry.js +52 -4
- package/src/sql/v_model_context.sql +31 -39
- package/src/sql/v_run_log.sql +3 -3
- package/src/agent/prompt_queue.sql +0 -39
- package/src/plugins/ask_user/docs.md +0 -2
- package/src/plugins/cp/docs.md +0 -2
- package/src/plugins/env/docs.md +0 -2
- package/src/plugins/get/docs.md +0 -6
- package/src/plugins/known/docs.md +0 -3
- package/src/plugins/mv/docs.md +0 -2
- package/src/plugins/rm/docs.md +0 -4
- package/src/plugins/set/docs.md +0 -4
- package/src/plugins/sh/docs.md +0 -2
- package/src/plugins/skills/README.md +0 -25
- package/src/plugins/store/README.md +0 -20
- package/src/plugins/store/docs.md +0 -5
- package/src/plugins/store/store.js +0 -52
- package/src/plugins/summarize/docs.md +0 -4
- package/src/plugins/unknown/docs.md +0 -5
- package/src/plugins/update/docs.md +0 -4
package/PLUGINS.md
CHANGED
|
@@ -1,11 +1,73 @@
|
|
|
1
1
|
# PLUGINS.md — Plugin Development Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Every `<tag>` the model sees is a plugin. Every scheme is registered by
|
|
4
|
+
its owner. Every operation — model, client, plugin — flows through the
|
|
5
|
+
same tool handler. No exceptions without documentation in EXCEPTIONS.md.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
## §0 Quickstart
|
|
8
|
+
|
|
9
|
+
A complete tool plugin in four parts: register, handle, render, document.
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
// src/plugins/ping/ping.js
|
|
13
|
+
import docs from "./pingDoc.js";
|
|
14
|
+
|
|
15
|
+
export default class Ping {
|
|
16
|
+
#core;
|
|
17
|
+
|
|
18
|
+
constructor(core) {
|
|
19
|
+
this.#core = core;
|
|
20
|
+
core.ensureTool();
|
|
21
|
+
core.registerScheme({ category: "logging" });
|
|
22
|
+
core.on("handler", this.handler.bind(this));
|
|
23
|
+
core.on("full", this.full.bind(this));
|
|
24
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
25
|
+
docsMap.ping = docs;
|
|
26
|
+
return docsMap;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async handler(entry, rummy) {
|
|
31
|
+
const now = new Date().toISOString();
|
|
32
|
+
await rummy.set({
|
|
33
|
+
path: entry.resultPath,
|
|
34
|
+
body: `pong ${now}`,
|
|
35
|
+
status: 200,
|
|
36
|
+
attributes: { path: entry.path },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
full(entry) {
|
|
41
|
+
return entry.body;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
// src/plugins/ping/pingDoc.js
|
|
48
|
+
const LINES = [
|
|
49
|
+
["## ping",
|
|
50
|
+
"Header — model sees this as the tool name"],
|
|
51
|
+
["<ping/>",
|
|
52
|
+
"Simplest invocation — no path, no body"],
|
|
53
|
+
["* Returns server timestamp",
|
|
54
|
+
"One-line description of what the tool does"],
|
|
55
|
+
];
|
|
56
|
+
export default LINES.map(([text]) => text).join("\n");
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Install external plugins via npm + env var:
|
|
60
|
+
|
|
61
|
+
```env
|
|
62
|
+
RUMMY_PLUGIN_PING=@myorg/rummy.ping
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## §1 Plugin Contract
|
|
66
|
+
|
|
67
|
+
A plugin is a directory under `src/plugins/` containing a `.js` file
|
|
68
|
+
that exports a default class. The class name matches the file name.
|
|
69
|
+
The constructor receives `core` (a PluginContext) — the plugin's
|
|
70
|
+
complete interface with the system.
|
|
9
71
|
|
|
10
72
|
```js
|
|
11
73
|
export default class MyTool {
|
|
@@ -13,8 +75,15 @@ export default class MyTool {
|
|
|
13
75
|
|
|
14
76
|
constructor(core) {
|
|
15
77
|
this.#core = core;
|
|
78
|
+
core.ensureTool();
|
|
79
|
+
core.registerScheme({ category: "logging" });
|
|
16
80
|
core.on("handler", this.handler.bind(this));
|
|
17
81
|
core.on("full", this.full.bind(this));
|
|
82
|
+
core.on("summary", this.summary.bind(this));
|
|
83
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
84
|
+
docsMap.mytool = docs;
|
|
85
|
+
return docsMap;
|
|
86
|
+
});
|
|
18
87
|
}
|
|
19
88
|
|
|
20
89
|
async handler(entry, rummy) {
|
|
@@ -22,13 +91,17 @@ export default class MyTool {
|
|
|
22
91
|
}
|
|
23
92
|
|
|
24
93
|
full(entry) {
|
|
25
|
-
|
|
26
|
-
|
|
94
|
+
return entry.body;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
summary(entry) {
|
|
98
|
+
return entry.body;
|
|
27
99
|
}
|
|
28
100
|
}
|
|
29
101
|
```
|
|
30
102
|
|
|
31
103
|
File naming: `src/plugins/mytool/mytool.js`. Class name = file name.
|
|
104
|
+
Tool docs: `src/plugins/mytool/mytoolDoc.js` (annotated line arrays).
|
|
32
105
|
|
|
33
106
|
External plugins install via npm and load via `RUMMY_PLUGIN_*` env vars:
|
|
34
107
|
|
|
@@ -37,11 +110,11 @@ RUMMY_PLUGIN_WEB=@possumtech/rummy.web
|
|
|
37
110
|
RUMMY_PLUGIN_REPO=@possumtech/rummy.repo
|
|
38
111
|
```
|
|
39
112
|
|
|
40
|
-
## Unified API
|
|
113
|
+
## §2 Unified API
|
|
41
114
|
|
|
42
|
-
The model, the client, and plugins all use the same interface. Each
|
|
43
|
-
is a superset of the one below. `name` (model) = `method` (client)
|
|
44
|
-
method name (plugin). The params shape is the same at every tier.
|
|
115
|
+
The model, the client, and plugins all use the same interface. Each
|
|
116
|
+
tier is a superset of the one below. `name` (model) = `method` (client)
|
|
117
|
+
= method name (plugin). The params shape is the same at every tier.
|
|
45
118
|
|
|
46
119
|
```
|
|
47
120
|
Model: <rm path="file.txt"/> → { name: "rm", path: "file.txt" }
|
|
@@ -49,37 +122,121 @@ Client: { method: "rm", params: { path: "file.txt" } }
|
|
|
49
122
|
Plugin: rummy.rm({ path: "file.txt" })
|
|
50
123
|
```
|
|
51
124
|
|
|
52
|
-
|
|
125
|
+
All three tiers go through the same tool handler. Budget enforcement
|
|
126
|
+
applies equally. A client `get` is subject to the same budget check
|
|
127
|
+
as a model `<get>`.
|
|
53
128
|
|
|
54
|
-
|
|
55
|
-
`core.filter()`. No static methods. No direct hook manipulation.
|
|
129
|
+
## §3 Registration
|
|
56
130
|
|
|
57
|
-
|
|
131
|
+
All registration happens in the constructor via `core.on()`,
|
|
132
|
+
`core.filter()`, `core.ensureTool()`, and `core.registerScheme()`.
|
|
58
133
|
|
|
59
|
-
|
|
60
|
-
|-------|---------|
|
|
61
|
-
| `"handler"` | Tool handler — called when model/client invokes this tool |
|
|
62
|
-
| `"full"` | Full projection — what the model sees at full fidelity |
|
|
63
|
-
| `"summary"` | Summary projection — condensed view under token pressure |
|
|
64
|
-
| `"docs"` | Tool documentation — included in model prompt |
|
|
65
|
-
| `"turn"` | Turn processor — runs before context materialization |
|
|
66
|
-
| `"entry.created"` | Entry created during dispatch |
|
|
67
|
-
| `"entry.changed"` | File entries changed on disk |
|
|
68
|
-
| Any `"dotted.name"` | Resolves to the matching hook in the hook tree |
|
|
134
|
+
### §3.1 core.ensureTool()
|
|
69
135
|
|
|
70
|
-
|
|
136
|
+
Declares this plugin as a model-facing tool. Required for the tool
|
|
137
|
+
to appear in the model's tool list. Called automatically by
|
|
138
|
+
`core.on("handler", ...)` but must be called explicitly for tools
|
|
139
|
+
without handlers (e.g., `summarize`, `update`, `unknown`).
|
|
71
140
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
| `"llm.messages"` | Transform final messages before LLM call |
|
|
77
|
-
| `"llm.response"` | Transform LLM response |
|
|
78
|
-
| Any `"dotted.name"` | Resolves to the matching filter in the hook tree |
|
|
141
|
+
### §3.2 core.registerScheme(config?)
|
|
142
|
+
|
|
143
|
+
Registers this plugin's scheme in the database. Called once in the
|
|
144
|
+
constructor.
|
|
79
145
|
|
|
80
|
-
|
|
146
|
+
```js
|
|
147
|
+
core.registerScheme({
|
|
148
|
+
modelVisible: 1, // 1 or 0 — appears in v_model_context
|
|
149
|
+
category: "logging", // "data", "logging", "unknown", "prompt"
|
|
150
|
+
});
|
|
151
|
+
```
|
|
81
152
|
|
|
82
|
-
|
|
153
|
+
All fields optional. `core.registerScheme()` with no args gives a
|
|
154
|
+
sensible result-type scheme.
|
|
155
|
+
|
|
156
|
+
### §3.3 core.on(event, callback, priority?)
|
|
157
|
+
|
|
158
|
+
| Event | Payload | Purpose |
|
|
159
|
+
|-------|---------|---------|
|
|
160
|
+
| `"handler"` | `(entry, rummy)` | Tool handler — called when model/client invokes this tool |
|
|
161
|
+
| `"full"` | `(entry)` | Full fidelity projection — what the model sees at full |
|
|
162
|
+
| `"summary"` | `(entry)` | Summary fidelity projection — what the model sees at summary |
|
|
163
|
+
| `"turn.started"` | `(ctx)` | Turn beginning — write prompt/progress/instructions entries |
|
|
164
|
+
| `"turn.response"` | `(result, rummy)` | LLM responded — write audit entries, commit usage |
|
|
165
|
+
| `"turn.proposing"` | `(rummy)` | All dispatches done — materialize file edit proposals |
|
|
166
|
+
| `"entry.created"` | `({ runId, path, scheme })` | Entry created during dispatch |
|
|
167
|
+
| `"entry.changed"` | `({ runId, path, changeType })` | Entry content, fidelity, or status modified |
|
|
168
|
+
| Any `"dotted.name"` | varies | Resolves to the matching hook in the hook tree |
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
// One-liner examples
|
|
172
|
+
core.on("handler", async (entry, rummy) => { /* tool logic */ });
|
|
173
|
+
core.on("full", (entry) => entry.body);
|
|
174
|
+
core.on("summary", (entry) => entry.body?.slice(0, 200));
|
|
175
|
+
core.on("turn.started", async (ctx) => { /* write entries */ });
|
|
176
|
+
core.on("turn.response", async (result, rummy) => { /* audit */ });
|
|
177
|
+
core.on("entry.changed", ({ runId, path, changeType }) => { /* react */ });
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### §3.4 core.filter(name, callback, priority?)
|
|
181
|
+
|
|
182
|
+
| Filter | Signature | Purpose |
|
|
183
|
+
|--------|-----------|---------|
|
|
184
|
+
| `"instructions.toolDocs"` | `(docsMap) → docsMap` | Add tool documentation (docsMap pattern) |
|
|
185
|
+
| `"assembly.system"` | `(content, ctx) → content` | Contribute to system message |
|
|
186
|
+
| `"assembly.user"` | `(content, ctx) → content` | Contribute to user message |
|
|
187
|
+
| `"llm.messages"` | `(messages) → messages` | Transform final messages before LLM call |
|
|
188
|
+
| `"llm.response"` | `(response) → response` | Transform LLM response |
|
|
189
|
+
| Any `"dotted.name"` | varies | Resolves to the matching filter in the hook tree |
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
// One-liner examples
|
|
193
|
+
core.filter("assembly.system", async (content, ctx) => {
|
|
194
|
+
return `${content}\n<mytag>${myData}</mytag>`;
|
|
195
|
+
}, 400);
|
|
196
|
+
core.filter("assembly.user", async (content, ctx) => {
|
|
197
|
+
return `${content}\n<status>${myStatus}</status>`;
|
|
198
|
+
}, 150);
|
|
199
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
200
|
+
docsMap.mytool = docs;
|
|
201
|
+
return docsMap;
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The `ctx` object passed to assembly filters:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
ctx = {
|
|
209
|
+
rows, // turn_context rows (materialized entries)
|
|
210
|
+
loopStartTurn, // First turn of current loop
|
|
211
|
+
type, // "ask" or "act"
|
|
212
|
+
tools, // Set of active tool names
|
|
213
|
+
contextSize, // Model context window size
|
|
214
|
+
lastContextTokens, // Assembled tokens from previous turn
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### §3.5 Tool Docs
|
|
219
|
+
|
|
220
|
+
Each tool plugin has a `*Doc.js` file with annotated line arrays.
|
|
221
|
+
Text goes to the model. Rationale stays in source. Registered via
|
|
222
|
+
the `instructions.toolDocs` filter using the docsMap pattern:
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
import docs from "./mytoolDoc.js";
|
|
226
|
+
|
|
227
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
228
|
+
docsMap.mytool = docs;
|
|
229
|
+
return docsMap;
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The instructions plugin filters by the active tool set — tools
|
|
234
|
+
excluded by mode or flags are automatically omitted from the docs.
|
|
235
|
+
|
|
236
|
+
### §3.6 handler(entry, rummy)
|
|
237
|
+
|
|
238
|
+
The handler receives the parsed command entry and a per-turn
|
|
239
|
+
RummyContext:
|
|
83
240
|
|
|
84
241
|
```js
|
|
85
242
|
entry = {
|
|
@@ -87,216 +244,316 @@ entry = {
|
|
|
87
244
|
path, // Entry path ("set://src/app.js")
|
|
88
245
|
body, // Tag body text
|
|
89
246
|
attributes, // Parsed tag attributes
|
|
90
|
-
state, // Current state
|
|
91
247
|
resultPath, // Where to write the result
|
|
92
248
|
}
|
|
93
249
|
```
|
|
94
250
|
|
|
95
|
-
Multiple handlers per scheme. Lower priority runs first. Return
|
|
96
|
-
to stop the chain.
|
|
251
|
+
Multiple handlers per scheme. Lower priority runs first. Return
|
|
252
|
+
`false` to stop the chain.
|
|
97
253
|
|
|
98
|
-
### full(entry) / summary(entry)
|
|
254
|
+
### §3.7 full(entry) / summary(entry)
|
|
99
255
|
|
|
100
|
-
Returns the string the model sees for this tool's entries at the
|
|
101
|
-
fidelity.
|
|
102
|
-
|
|
103
|
-
|
|
256
|
+
Returns the string the model sees for this tool's entries at the
|
|
257
|
+
given fidelity. Every tool MUST register `full`. `summary` is
|
|
258
|
+
optional — if unregistered, falls back to `attributes.summary`
|
|
259
|
+
(model-authored keyword description) or empty string.
|
|
104
260
|
|
|
105
|
-
|
|
261
|
+
At summary fidelity, `attributes.summary` is prepended above the
|
|
262
|
+
plugin's summary output automatically by ToolRegistry.view().
|
|
263
|
+
|
|
264
|
+
## §4 Two Objects
|
|
106
265
|
|
|
107
266
|
Plugins interact with two objects at different scopes:
|
|
108
267
|
|
|
109
|
-
**PluginContext** (`
|
|
110
|
-
Used for registration (`on()`, `filter()`)
|
|
111
|
-
|
|
112
|
-
|
|
268
|
+
**PluginContext** (`core`) — startup-scoped. Created once per plugin.
|
|
269
|
+
Used for registration (`on()`, `filter()`, `registerScheme()`,
|
|
270
|
+
`ensureTool()`). Available as `this.#core` throughout the plugin's
|
|
271
|
+
lifetime.
|
|
113
272
|
|
|
114
|
-
**RummyContext** (`rummy`
|
|
115
|
-
|
|
273
|
+
**RummyContext** (`rummy`) — turn-scoped. Passed to handlers per
|
|
274
|
+
invocation. Has tool verbs, per-turn state, database access.
|
|
116
275
|
|
|
117
|
-
### Tool Verbs (
|
|
276
|
+
### §4.1 Tool Verbs (on RummyContext)
|
|
118
277
|
|
|
119
278
|
| Method | Effect |
|
|
120
279
|
|--------|--------|
|
|
121
|
-
| `rummy.set({ path, body,
|
|
122
|
-
| `rummy.get({ path })` | Promote to full
|
|
123
|
-
| `rummy.store({ path })` | Demote to stored state |
|
|
280
|
+
| `rummy.set({ path, body, status, fidelity, attributes })` | Create/update entry |
|
|
281
|
+
| `rummy.get({ path })` | Promote to full fidelity |
|
|
124
282
|
| `rummy.rm({ path })` | Delete permanently |
|
|
125
283
|
| `rummy.mv({ path, to })` | Move entry |
|
|
126
284
|
| `rummy.cp({ path, to })` | Copy entry |
|
|
127
285
|
|
|
128
|
-
### Query Methods
|
|
286
|
+
### §4.2 Query Methods
|
|
129
287
|
|
|
130
288
|
| Method | Returns |
|
|
131
289
|
|--------|---------|
|
|
132
|
-
| `rummy.getEntry(path)` | Full entry object |
|
|
133
290
|
| `rummy.getBody(path)` | Body text or null |
|
|
134
|
-
| `rummy.getState(path)` |
|
|
291
|
+
| `rummy.getState(path)` | Status code or null |
|
|
135
292
|
| `rummy.getAttributes(path)` | Parsed attributes `{}` |
|
|
136
293
|
| `rummy.getEntries(pattern, body?)` | Array of matching entries |
|
|
137
294
|
|
|
138
|
-
### Properties
|
|
295
|
+
### §4.3 Properties
|
|
139
296
|
|
|
140
|
-
| Property | Type |
|
|
141
|
-
|
|
142
|
-
| `rummy.
|
|
143
|
-
| `rummy.
|
|
144
|
-
| `rummy.
|
|
145
|
-
| `rummy.
|
|
146
|
-
| `rummy.
|
|
147
|
-
| `rummy.
|
|
297
|
+
| Property | Type | Scope |
|
|
298
|
+
|----------|------|-------|
|
|
299
|
+
| `rummy.entries` | KnownStore instance | Both |
|
|
300
|
+
| `rummy.db` | Database | Both |
|
|
301
|
+
| `rummy.runId` | Current run ID | RummyContext |
|
|
302
|
+
| `rummy.projectId` | Current project ID | Both |
|
|
303
|
+
| `rummy.sequence` | Current turn number | RummyContext |
|
|
304
|
+
| `rummy.contextSize` | Model context window | RummyContext |
|
|
305
|
+
| `rummy.noRepo` | Skip filesystem scanning | RummyContext |
|
|
148
306
|
|
|
149
|
-
##
|
|
307
|
+
## §5 Tool Display Order
|
|
150
308
|
|
|
151
|
-
|
|
309
|
+
Tools are presented to the model in priority order:
|
|
310
|
+
gather → reason → act → communicate.
|
|
152
311
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// React to file changes
|
|
156
|
-
}, priority);
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
**Filters** transform data through a chain. Each handler receives the value
|
|
160
|
-
and context, returns the (possibly modified) value.
|
|
161
|
-
|
|
162
|
-
```js
|
|
163
|
-
hooks.llm.messages.addFilter(async (messages, context) => {
|
|
164
|
-
return [{ role: "system", content: "Extra" }, ...messages];
|
|
165
|
-
}, priority);
|
|
166
|
-
```
|
|
312
|
+
Defined in `ToolRegistry.TOOL_ORDER`. The `resolveForLoop(mode, flags)`
|
|
313
|
+
method handles all exclusions through one mechanism:
|
|
167
314
|
|
|
168
|
-
|
|
315
|
+
| Flag | Excludes |
|
|
316
|
+
|------|----------|
|
|
317
|
+
| `mode === "ask"` | `sh` |
|
|
318
|
+
| `noInteraction` | `ask_user` |
|
|
319
|
+
| `noWeb` | `search` |
|
|
169
320
|
|
|
170
|
-
|
|
321
|
+
## §6 Hedberg
|
|
171
322
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
| `project.init.started` | event | `{ projectName, projectRoot }` | Before project DB upsert |
|
|
175
|
-
| `project.init.completed` | event | `{ projectId, projectRoot, db }` | After project created |
|
|
323
|
+
The hedberg plugin exposes pattern matching and interpretation
|
|
324
|
+
utilities on `core.hooks.hedberg` for all plugins to use:
|
|
176
325
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
| `socket.message.raw` | filter | Raw buffer | Before JSON parse |
|
|
182
|
-
| `rpc.request` | filter | `{ method, params, id }` | Before handler lookup |
|
|
183
|
-
| `rpc.started` | event | `{ method, params, id, projectId }` | Before handler execution |
|
|
184
|
-
| `rpc.response.result` | filter | `result, { method, id }` | Before sending response |
|
|
185
|
-
| `rpc.completed` | event | `{ method, id, result }` | After response sent |
|
|
186
|
-
| `rpc.error` | event | `{ id, error }` | On handler error |
|
|
326
|
+
```js
|
|
327
|
+
const { match, search, replace, parseSed, parseEdits,
|
|
328
|
+
normalizeAttrs, generatePatch } = core.hooks.hedberg;
|
|
329
|
+
```
|
|
187
330
|
|
|
188
|
-
|
|
331
|
+
| Method | Purpose |
|
|
332
|
+
|--------|---------|
|
|
333
|
+
| `match(pattern, string)` | Full-string pattern match (glob, regex, literal) |
|
|
334
|
+
| `search(pattern, string)` | Substring search |
|
|
335
|
+
| `replace(body, search, replacement, opts?)` | Apply replacement |
|
|
336
|
+
| `parseSed(input)` | Parse sed syntax (any delimiter) |
|
|
337
|
+
| `parseEdits(content)` | Detect edit format (merge conflict, udiff, sed) |
|
|
338
|
+
| `normalizeAttrs(attrs)` | Heal model attribute names |
|
|
339
|
+
| `generatePatch(path, old, new)` | Generate unified diff |
|
|
189
340
|
|
|
190
|
-
|
|
191
|
-
|------|------|---------|------|
|
|
192
|
-
| `ask.started` | event | `{ projectId, model, prompt, run }` | Run requested in ask mode |
|
|
193
|
-
| `act.started` | event | `{ projectId, model, prompt, run }` | Run requested in act mode |
|
|
194
|
-
| `run.config` | filter | Config object, `{ projectId }` | Before run config applied |
|
|
195
|
-
| `run.progress` | event | `{ run, turn, status }` | Status change (thinking, processing) |
|
|
196
|
-
| `run.state` | event | `{ run, turn, status, summary, history, unknowns, proposed, telemetry }` | After each turn — full state snapshot |
|
|
197
|
-
| `run.step.completed` | event | `{ run, turn, flags }` | Turn resolved, no proposals pending |
|
|
198
|
-
| `ask.completed` | event | `{ projectId, run, status, turn }` | Ask run finished |
|
|
199
|
-
| `act.completed` | event | `{ projectId, run, status, turn }` | Act run finished |
|
|
341
|
+
## §7 Events & Filters
|
|
200
342
|
|
|
201
|
-
|
|
343
|
+
**Events** are fire-and-forget. All handlers run. Return values ignored.
|
|
344
|
+
**Filters** transform data through a chain. Lower priority runs first.
|
|
345
|
+
All hooks are async.
|
|
346
|
+
|
|
347
|
+
### §7.1 Project Lifecycle
|
|
348
|
+
|
|
349
|
+
| Hook | Type | When |
|
|
350
|
+
|------|------|------|
|
|
351
|
+
| `project.init.started` | event | Before project DB upsert |
|
|
352
|
+
| `project.init.completed` | event | After project created |
|
|
353
|
+
|
|
354
|
+
### §7.2 Run & Loop Lifecycle
|
|
355
|
+
|
|
356
|
+
| Hook | Type | When |
|
|
357
|
+
|------|------|------|
|
|
358
|
+
| `run.created` | event | Run just created in DB |
|
|
359
|
+
| `ask.started` | event | Run requested in ask mode |
|
|
360
|
+
| `act.started` | event | Run requested in act mode |
|
|
361
|
+
| `loop.started` | event | Loop execution beginning |
|
|
362
|
+
| `run.config` | filter | Before run config applied |
|
|
363
|
+
| `run.progress` | event | Status change (thinking, processing) |
|
|
364
|
+
| `run.state` | event | After each turn — full state snapshot |
|
|
365
|
+
| `run.step.completed` | event | Turn resolved, no proposals pending |
|
|
366
|
+
| `loop.completed` | event | Loop execution finished (any exit path) |
|
|
367
|
+
| `ask.completed` | event | Ask run finished |
|
|
368
|
+
| `act.completed` | event | Act run finished |
|
|
369
|
+
|
|
370
|
+
### §7.3 Turn Pipeline
|
|
202
371
|
|
|
203
372
|
Hooks fire in this order every turn:
|
|
204
373
|
|
|
205
|
-
|
|
|
206
|
-
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
| `assembly.system` | filter |
|
|
210
|
-
| `assembly.user` | filter |
|
|
211
|
-
| `
|
|
212
|
-
| `llm.
|
|
213
|
-
| `llm.
|
|
214
|
-
| `llm.
|
|
215
|
-
|
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
|
222
|
-
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
|
|
226
|
-
|
|
374
|
+
| # | Hook | Type | When |
|
|
375
|
+
|---|------|------|------|
|
|
376
|
+
| 1 | `turn.started` | event | Plugins write prompt/progress/instructions entries |
|
|
377
|
+
| 2 | `context.materialized` | event | turn_context populated from v_model_context |
|
|
378
|
+
| 3 | `assembly.system` | filter | Build system message from entries |
|
|
379
|
+
| 4 | `assembly.user` | filter | Build user message from entries |
|
|
380
|
+
| 5 | `budget.enforce` | hook | Measure assembled tokens, 413 if over |
|
|
381
|
+
| 6 | `llm.messages` | filter | Transform messages before LLM call |
|
|
382
|
+
| 7 | `llm.request.started` | event | LLM call about to fire |
|
|
383
|
+
| 8 | `llm.response` | filter | Transform raw LLM response |
|
|
384
|
+
| 9 | `llm.request.completed` | event | LLM call finished |
|
|
385
|
+
| 10 | `turn.response` | event | Plugins write audit entries |
|
|
386
|
+
| 11 | `entry.recording` | filter | Before each entry is stored (validate/transform) |
|
|
387
|
+
| 12 | `tool.before` | event | Before tool handler dispatch |
|
|
388
|
+
| 13 | Tool handler dispatch | — | Lifecycle always, actions sequential |
|
|
389
|
+
| 14 | `tool.after` | event | After tool handler dispatch |
|
|
390
|
+
| 15 | `entry.created` | event | After each new entry dispatched |
|
|
391
|
+
| 16 | `entry.changed` | event | After entry content, fidelity, or status modified |
|
|
392
|
+
| 17 | `turn.proposing` | event | All dispatches done — materialize proposals |
|
|
393
|
+
| 18 | `turn.completed` | event | Turn fully resolved with final status |
|
|
394
|
+
|
|
395
|
+
### §7.4 Entry Events
|
|
396
|
+
|
|
397
|
+
| Hook | Type | When |
|
|
398
|
+
|------|------|------|
|
|
399
|
+
| `entry.recording` | filter | Before entry stored. Return `{ status: 4xx }` to reject. |
|
|
400
|
+
| `entry.created` | event | New entry added during dispatch |
|
|
401
|
+
| `entry.changed` | event | Entry content, fidelity, or status modified |
|
|
402
|
+
|
|
403
|
+
`entry.recording` is a filter — plugins can validate, transform, or
|
|
404
|
+
reject entries before they hit the store. Payload:
|
|
405
|
+
`{ scheme, path, body, attributes, status }`. Return the object
|
|
406
|
+
(modified or not). Set `status >= 400` to reject.
|
|
407
|
+
|
|
408
|
+
`entry.changed` fires on any mutation to an existing entry — body
|
|
409
|
+
update, fidelity change, status change, attribute update. Payload:
|
|
410
|
+
`{ runId, path, changeType }`. Subscribers include the budget plugin
|
|
411
|
+
(remeasure context) and the repo plugin (detect file changes on disk).
|
|
412
|
+
|
|
413
|
+
### §7.5 Budget
|
|
414
|
+
|
|
415
|
+
| Hook | Type | When |
|
|
416
|
+
|------|------|------|
|
|
417
|
+
| `budget.enforce` | hook | After assembly, before LLM call. Returns 413 if over context limit. |
|
|
418
|
+
|
|
419
|
+
The budget plugin measures `countTokens()` on assembled messages —
|
|
420
|
+
the actual content being sent to the LLM. No estimates, no DB token
|
|
421
|
+
math. The assembled message IS the measurement.
|
|
422
|
+
|
|
423
|
+
**DB tokens vs assembled tokens:** The `tokens` column on entries is
|
|
424
|
+
strictly for DISPLAY — showing token counts in `<knowns>` tags so
|
|
425
|
+
the model can reason about entry sizes. It is NEVER used for budget
|
|
426
|
+
decisions. Budget math uses only assembled message token counts.
|
|
427
|
+
These are two separate numbers that must never be conflated.
|
|
428
|
+
|
|
429
|
+
### §7.6 Client Notifications
|
|
430
|
+
|
|
431
|
+
| Hook | Type | When |
|
|
432
|
+
|------|------|------|
|
|
433
|
+
| `ui.render` | event | Text for client display |
|
|
434
|
+
| `ui.notify` | event | Status notification |
|
|
435
|
+
|
|
436
|
+
## §8 Entry Lifecycle
|
|
437
|
+
|
|
438
|
+
Every entry follows the same lifecycle regardless of origin:
|
|
439
|
+
|
|
440
|
+
1. **Created** — `known_entries` row with scheme, path, body, status
|
|
441
|
+
2. **Dispatched** — tool handler chain executes
|
|
442
|
+
3. **Status set** — handler sets 200, 202, 400, 413, etc.
|
|
443
|
+
4. **Materialized** — `v_model_context` projects into `turn_context`
|
|
444
|
+
5. **Assembled** — filter chain renders into system/user messages
|
|
445
|
+
6. **Visible** — model sees the entry in its context
|
|
446
|
+
|
|
447
|
+
Entries at `archive` fidelity skip steps 4-6 (invisible to model).
|
|
448
|
+
Entries at `index` fidelity render as path-only tags (no body).
|
|
449
|
+
Entries at `summary` fidelity render with `attributes.summary`
|
|
450
|
+
prepended above the plugin's summary view output.
|
|
451
|
+
|
|
452
|
+
## §9 Bundled Plugins
|
|
227
453
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
454
|
+
| Plugin | Type | Description |
|
|
455
|
+
|--------|------|-------------|
|
|
456
|
+
| `get` | Core tool | Load file/entry into context |
|
|
457
|
+
| `set` | Core tool | Edit file/entry, fidelity control |
|
|
458
|
+
| `known` | Core tool + Assembly | Save knowledge, render `<knowns>` section |
|
|
459
|
+
| `rm` | Core tool | Delete permanently |
|
|
460
|
+
| `mv` | Core tool | Move entry |
|
|
461
|
+
| `cp` | Core tool | Copy entry |
|
|
462
|
+
| `sh` | Core tool | Shell command (act mode only) |
|
|
463
|
+
| `env` | Core tool | Exploratory command |
|
|
464
|
+
| `ask_user` | Core tool | Ask the user |
|
|
465
|
+
| `search` | Core tool | Web search (via external plugin) |
|
|
466
|
+
| `summarize` | Structural | Signal completion |
|
|
467
|
+
| `update` | Structural | Signal continued work |
|
|
468
|
+
| `unknown` | Structural + Assembly | Register unknowns, render `<unknowns>` |
|
|
469
|
+
| `previous` | Assembly | Render `<previous>` loop history |
|
|
470
|
+
| `current` | Assembly | Render `<current>` active loop work |
|
|
471
|
+
| `progress` | Assembly | Render `<progress>` telemetry + warnings |
|
|
472
|
+
| `prompt` | Assembly | Render `<prompt mode="ask|act">` tag |
|
|
473
|
+
| `hedberg` | Utility | Pattern matching, interpretation, normalization |
|
|
474
|
+
| `instructions` | Internal | Preamble + tool docs + persona assembly |
|
|
475
|
+
| `file` | Internal | File entry projections and constraints |
|
|
476
|
+
| `rpc` | Internal | RPC method registration |
|
|
477
|
+
| `telemetry` | Internal | Audit entries, usage stats, reasoning_content |
|
|
478
|
+
| `budget` | Internal | Context ceiling enforcement (413), panic mode, BudgetGuard |
|
|
479
|
+
| `think` | Internal | Model reasoning tag (`model_visible = 0`) |
|
|
480
|
+
| `mcp` | Core tool | Model Context Protocol server management |
|
|
481
|
+
|
|
482
|
+
Removed: `crunch` (dead code, replaced by model-owned context management),
|
|
483
|
+
`store` (merged into `set` fidelity attributes).
|
|
484
|
+
|
|
485
|
+
## §10 External Plugins
|
|
244
486
|
|
|
245
|
-
|
|
|
246
|
-
|
|
247
|
-
|
|
|
248
|
-
|
|
|
249
|
-
| `$.path` | JSONPath | `$.config.port` |
|
|
250
|
-
| `//element` | XPath | `//div[@class]` |
|
|
251
|
-
| `*glob*` | Glob | `src/**/*.js` |
|
|
252
|
-
| Everything else | Literal | `port = 3000` |
|
|
487
|
+
| Plugin | Package | Description |
|
|
488
|
+
|--------|---------|-------------|
|
|
489
|
+
| Repo | `@possumtech/rummy.repo` | Git-aware file scanning and symbol extraction |
|
|
490
|
+
| Web | `@possumtech/rummy.web` | Web search and URL fetching via searxng |
|
|
253
491
|
|
|
254
|
-
|
|
492
|
+
Loaded via `RUMMY_PLUGIN_*` env vars. External plugins have access
|
|
493
|
+
to the same PluginContext API as bundled plugins.
|
|
255
494
|
|
|
256
|
-
|
|
257
|
-
import { hedmatch, hedsearch, hedreplace } from "./sql/functions/hedberg.js";
|
|
495
|
+
## §11 RPC Methods
|
|
258
496
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
hedreplace(pattern, replacement, string) // → new string or null
|
|
262
|
-
```
|
|
497
|
+
Client-facing JSON-RPC 2.0 over WebSocket. All tool methods go through
|
|
498
|
+
the same handler chain as model commands.
|
|
263
499
|
|
|
264
|
-
|
|
500
|
+
### §11.1 Wire Format
|
|
265
501
|
|
|
266
|
-
|
|
502
|
+
```json
|
|
503
|
+
// Request
|
|
504
|
+
{ "jsonrpc": "2.0", "id": 1, "method": "get", "params": { "path": "src/app.js", "run": "my_run" } }
|
|
267
505
|
|
|
268
|
-
|
|
506
|
+
// Success response
|
|
507
|
+
{ "jsonrpc": "2.0", "id": 1, "result": { "path": "src/app.js", "status": 200 } }
|
|
269
508
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
| [`get`](src/plugins/get/) | Core tool | Load file/entry into context |
|
|
273
|
-
| [`set`](src/plugins/set/) | Core tool | Edit file/entry |
|
|
274
|
-
| [`known`](src/plugins/known/) | Core tool | Save knowledge, render `<known>` |
|
|
275
|
-
| [`store`](src/plugins/store/) | Core tool | Remove from context |
|
|
276
|
-
| [`rm`](src/plugins/rm/) | Core tool | Delete permanently |
|
|
277
|
-
| [`mv`](src/plugins/mv/) | Core tool | Move entry |
|
|
278
|
-
| [`cp`](src/plugins/cp/) | Core tool | Copy entry |
|
|
279
|
-
| [`sh`](src/plugins/sh/) | Core tool | Shell command |
|
|
280
|
-
| [`env`](src/plugins/env/) | Core tool | Exploratory command |
|
|
281
|
-
| [`ask_user`](src/plugins/ask_user/) | Core tool | Ask the user |
|
|
282
|
-
| [`summarize`](src/plugins/summarize/) | Structural | Signal completion |
|
|
283
|
-
| [`update`](src/plugins/update/) | Structural | Signal continued work |
|
|
284
|
-
| [`unknown`](src/plugins/unknown/) | Structural | Register unknowns, render `<unknowns>` |
|
|
285
|
-
| [`previous`](src/plugins/previous/) | Assembly | Render `<previous>` loop history |
|
|
286
|
-
| [`current`](src/plugins/current/) | Assembly | Render `<current>` active loop work |
|
|
287
|
-
| [`progress`](src/plugins/progress/) | Assembly | Render `<progress>` bridge text |
|
|
288
|
-
| [`prompt`](src/plugins/prompt/) | Assembly | Render `<ask>`/`<act>` prompt tag |
|
|
289
|
-
| [`instructions`](src/plugins/instructions/) | Internal | System prompt assembly |
|
|
290
|
-
| [`file`](src/plugins/file/) | Internal | File projections, constraints, scanning |
|
|
291
|
-
| [`rpc`](src/plugins/rpc/) | Internal | RPC method registration |
|
|
292
|
-
| [`skills`](src/plugins/skills/) | Internal | Skill/persona management |
|
|
293
|
-
| [`telemetry`](src/plugins/telemetry/) | Internal | Debug logging |
|
|
294
|
-
|
|
295
|
-
## External Plugins
|
|
509
|
+
// Error response
|
|
510
|
+
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32600, "message": "Missing required param: path" } }
|
|
296
511
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
| Repo | `@possumtech/rummy.repo` | Symbol extraction |
|
|
512
|
+
// Notification (server → client, no id)
|
|
513
|
+
{ "jsonrpc": "2.0", "method": "run/state", "params": { "run": "my_run", "status": 200 } }
|
|
514
|
+
```
|
|
301
515
|
|
|
302
|
-
|
|
516
|
+
### §11.2 Tool Methods (Unified API)
|
|
517
|
+
|
|
518
|
+
| Method | Params | Notes |
|
|
519
|
+
|--------|--------|-------|
|
|
520
|
+
| `get` | `{ path, run, persist?, readonly? }` | `persist` also sets file constraint |
|
|
521
|
+
| `set` | `{ path, body, run, attributes? }` | All entries go through handler chain |
|
|
522
|
+
| `rm` | `{ path, run }` | |
|
|
523
|
+
| `mv` | `{ path, to, run }` | |
|
|
524
|
+
| `cp` | `{ path, to, run }` | |
|
|
525
|
+
| `store` | `{ path, run?, persist?, ignore?, clear? }` | File constraints only — not a model tool |
|
|
526
|
+
| `getEntries` | `{ pattern?, body?, run?, limit?, offset? }` | Query entries |
|
|
527
|
+
|
|
528
|
+
### §11.3 Run Management
|
|
529
|
+
|
|
530
|
+
| Method | Params | Notes |
|
|
531
|
+
|--------|--------|-------|
|
|
532
|
+
| `startRun` | `{ model, temperature?, persona?, contextLimit? }` | Create run without prompt |
|
|
533
|
+
| `ask` | `{ model, prompt, run?, noInteraction?, noWeb?, noRepo? }` | |
|
|
534
|
+
| `act` | `{ model, prompt, run?, noInteraction?, noWeb?, noRepo? }` | |
|
|
535
|
+
| `run/resolve` | `{ run, resolution }` | Accept/reject proposals |
|
|
536
|
+
| `run/abort` | `{ run }` | Cancel active run |
|
|
537
|
+
| `run/config` | `{ run, contextLimit?, persona?, model? }` | Update run settings |
|
|
538
|
+
| `run/rename` | `{ run, name }` | Change run alias |
|
|
539
|
+
| `run/inject` | `{ run, message }` | Inject message into active turn |
|
|
540
|
+
|
|
541
|
+
### §11.4 Project Management
|
|
542
|
+
|
|
543
|
+
| Method | Params | Notes |
|
|
544
|
+
|--------|--------|-------|
|
|
545
|
+
| `init` | `{ name, projectRoot }` | Initialize project |
|
|
546
|
+
| `addModel` | `{ alias, actual, contextLength? }` | Register model |
|
|
547
|
+
| `removeModel` | `{ alias }` | Remove model |
|
|
548
|
+
| `getRuns` | `{ limit?, offset? }` | List runs |
|
|
549
|
+
| `getRun` | `{ run }` | Get single run details |
|
|
550
|
+
| `getModels` | `{}` | List models |
|
|
551
|
+
|
|
552
|
+
### §11.5 Notifications (server → client)
|
|
553
|
+
|
|
554
|
+
| Method | Payload |
|
|
555
|
+
|--------|---------|
|
|
556
|
+
| `run/state` | `{ run, status, turn, entries, ... }` |
|
|
557
|
+
| `run/progress` | `{ run, status }` |
|
|
558
|
+
| `ui/render` | `{ text }` |
|
|
559
|
+
| `ui/notify` | `{ message }` |
|