@possumtech/rummy 0.2.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.
- package/.env.example +55 -0
- package/LICENSE +21 -0
- package/PLUGINS.md +302 -0
- package/README.md +41 -0
- package/SPEC.md +524 -0
- package/lang/en.json +34 -0
- package/migrations/001_initial_schema.sql +226 -0
- package/package.json +54 -0
- package/service.js +143 -0
- package/src/agent/AgentLoop.js +553 -0
- package/src/agent/ContextAssembler.js +29 -0
- package/src/agent/KnownStore.js +254 -0
- package/src/agent/ProjectAgent.js +101 -0
- package/src/agent/ResponseHealer.js +134 -0
- package/src/agent/TurnExecutor.js +457 -0
- package/src/agent/XmlParser.js +247 -0
- package/src/agent/known_checks.sql +42 -0
- package/src/agent/known_queries.sql +80 -0
- package/src/agent/known_store.sql +161 -0
- package/src/agent/messages.js +17 -0
- package/src/agent/prompt_queue.sql +39 -0
- package/src/agent/runs.sql +114 -0
- package/src/agent/schemes.sql +3 -0
- package/src/agent/sessions.sql +51 -0
- package/src/agent/tokens.js +28 -0
- package/src/agent/turns.sql +36 -0
- package/src/hooks/HookRegistry.js +72 -0
- package/src/hooks/Hooks.js +115 -0
- package/src/hooks/PluginContext.js +116 -0
- package/src/hooks/RummyContext.js +181 -0
- package/src/hooks/ToolRegistry.js +83 -0
- package/src/llm/LlmProvider.js +107 -0
- package/src/llm/OllamaClient.js +88 -0
- package/src/llm/OpenAiClient.js +80 -0
- package/src/llm/OpenRouterClient.js +78 -0
- package/src/llm/XaiClient.js +113 -0
- package/src/plugins/ask_user/README.md +18 -0
- package/src/plugins/ask_user/ask_user.js +48 -0
- package/src/plugins/ask_user/docs.md +2 -0
- package/src/plugins/cp/README.md +18 -0
- package/src/plugins/cp/cp.js +55 -0
- package/src/plugins/cp/docs.md +2 -0
- package/src/plugins/current/README.md +14 -0
- package/src/plugins/current/current.js +48 -0
- package/src/plugins/engine/README.md +12 -0
- package/src/plugins/engine/engine.sql +18 -0
- package/src/plugins/engine/turn_context.sql +51 -0
- package/src/plugins/env/README.md +14 -0
- package/src/plugins/env/docs.md +2 -0
- package/src/plugins/env/env.js +32 -0
- package/src/plugins/file/README.md +25 -0
- package/src/plugins/file/file.js +85 -0
- package/src/plugins/get/README.md +19 -0
- package/src/plugins/get/docs.md +6 -0
- package/src/plugins/get/get.js +53 -0
- package/src/plugins/hedberg/README.md +72 -0
- package/src/plugins/hedberg/docs.md +9 -0
- package/src/plugins/hedberg/edits.js +65 -0
- package/src/plugins/hedberg/hedberg.js +89 -0
- package/src/plugins/hedberg/matcher.js +181 -0
- package/src/plugins/hedberg/normalize.js +41 -0
- package/src/plugins/hedberg/patterns.js +452 -0
- package/src/plugins/hedberg/sed.js +48 -0
- package/src/plugins/helpers.js +22 -0
- package/src/plugins/index.js +180 -0
- package/src/plugins/instructions/README.md +11 -0
- package/src/plugins/instructions/instructions.js +37 -0
- package/src/plugins/instructions/preamble.md +12 -0
- package/src/plugins/known/README.md +18 -0
- package/src/plugins/known/docs.md +3 -0
- package/src/plugins/known/known.js +57 -0
- package/src/plugins/mv/README.md +18 -0
- package/src/plugins/mv/docs.md +2 -0
- package/src/plugins/mv/mv.js +56 -0
- package/src/plugins/previous/README.md +15 -0
- package/src/plugins/previous/previous.js +50 -0
- package/src/plugins/progress/README.md +17 -0
- package/src/plugins/progress/progress.js +44 -0
- package/src/plugins/prompt/README.md +16 -0
- package/src/plugins/prompt/prompt.js +45 -0
- package/src/plugins/rm/README.md +18 -0
- package/src/plugins/rm/docs.md +4 -0
- package/src/plugins/rm/rm.js +51 -0
- package/src/plugins/rpc/README.md +45 -0
- package/src/plugins/rpc/rpc.js +587 -0
- package/src/plugins/set/README.md +32 -0
- package/src/plugins/set/docs.md +4 -0
- package/src/plugins/set/set.js +268 -0
- package/src/plugins/sh/README.md +18 -0
- package/src/plugins/sh/docs.md +2 -0
- package/src/plugins/sh/sh.js +32 -0
- package/src/plugins/skills/README.md +25 -0
- package/src/plugins/skills/skills.js +175 -0
- package/src/plugins/store/README.md +20 -0
- package/src/plugins/store/docs.md +5 -0
- package/src/plugins/store/store.js +52 -0
- package/src/plugins/summarize/README.md +18 -0
- package/src/plugins/summarize/docs.md +4 -0
- package/src/plugins/summarize/summarize.js +24 -0
- package/src/plugins/telemetry/README.md +19 -0
- package/src/plugins/telemetry/rpc_log.sql +28 -0
- package/src/plugins/telemetry/telemetry.js +186 -0
- package/src/plugins/unknown/README.md +23 -0
- package/src/plugins/unknown/docs.md +5 -0
- package/src/plugins/unknown/unknown.js +31 -0
- package/src/plugins/update/README.md +18 -0
- package/src/plugins/update/docs.md +4 -0
- package/src/plugins/update/update.js +24 -0
- package/src/server/ClientConnection.js +228 -0
- package/src/server/RpcRegistry.js +52 -0
- package/src/server/SocketServer.js +43 -0
- package/src/sql/file_constraints.sql +15 -0
- package/src/sql/functions/countTokens.js +7 -0
- package/src/sql/functions/hedmatch.js +8 -0
- package/src/sql/functions/hedreplace.js +8 -0
- package/src/sql/functions/hedsearch.js +8 -0
- package/src/sql/functions/schemeOf.js +7 -0
- package/src/sql/functions/slugify.js +6 -0
- package/src/sql/v_model_context.sql +101 -0
- package/src/sql/v_run_log.sql +23 -0
package/SPEC.md
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
# RUMMY: Architecture Specification
|
|
2
|
+
|
|
3
|
+
The authoritative reference for Rummy's design. The sacred prompt
|
|
4
|
+
The instructions plugin (`preamble.md` + tool docs) defines
|
|
5
|
+
model-facing behavior. This document defines everything else.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. Design Philosophy: Events & Filters
|
|
10
|
+
|
|
11
|
+
Rummy is a hooks-and-filters system. Every structural seam in the
|
|
12
|
+
pipeline is a hookable checkpoint. Plugins subscribe to events
|
|
13
|
+
(fire-and-forget side effects) and filters (transformation chains
|
|
14
|
+
that thread a value through subscribers in priority order).
|
|
15
|
+
|
|
16
|
+
**Every `<tag>` the model sees is a plugin.** The `<known>` section
|
|
17
|
+
of the system message is rendered by the known plugin. The `<progress>`
|
|
18
|
+
section is rendered by the progress plugin. The `<ask>` tag is rendered
|
|
19
|
+
by the prompt plugin. No monolithic assembler decides what goes where.
|
|
20
|
+
Each plugin filters for its own data from the shared row set, renders
|
|
21
|
+
its section, and returns.
|
|
22
|
+
|
|
23
|
+
**Plugins compose, they don't coordinate.** A plugin subscribes to a
|
|
24
|
+
filter at a priority. It receives the accumulator value, appends its
|
|
25
|
+
contribution, and returns. It doesn't know what other plugins exist.
|
|
26
|
+
Priority determines ordering. Lower numbers run first.
|
|
27
|
+
|
|
28
|
+
**The core is a filter chain invocation.** The TurnExecutor computes
|
|
29
|
+
`loopStartTurn` (one value from one row), then calls
|
|
30
|
+
`assembly.system.filter(instructions, ctx)` and
|
|
31
|
+
`assembly.user.filter("", ctx)`. Everything else is plugins.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. The Known Store
|
|
36
|
+
|
|
37
|
+
All model-facing state lives in `known_entries`. Files, knowledge, tool
|
|
38
|
+
results, skills, audit — everything is a keyed entry with a URI scheme,
|
|
39
|
+
body, attributes, and state.
|
|
40
|
+
|
|
41
|
+
### 1.1 Schema
|
|
42
|
+
|
|
43
|
+
```sql
|
|
44
|
+
known_entries (
|
|
45
|
+
id, run_id, turn, path, body, scheme, state, hash,
|
|
46
|
+
attributes, tokens, tokens_full, refs, write_count,
|
|
47
|
+
created_at, updated_at
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
| Column | Purpose |
|
|
52
|
+
|--------|---------|
|
|
53
|
+
| `path` | Entry identity. Bare paths (`src/app.js`) or URIs (`known://auth`) |
|
|
54
|
+
| `body` | Tag body text. File content, tool output, skill docs. |
|
|
55
|
+
| `attributes` | Tag attributes as JSON. Handler-private workspace. `CHECK (json_valid)` |
|
|
56
|
+
| `scheme` | Generated from path via `schemeOf()`. Drives dispatch and view routing |
|
|
57
|
+
| `state` | Lifecycle stage. Determines model visibility |
|
|
58
|
+
| `hash` | SHA-256 for file change detection |
|
|
59
|
+
| `tokens` | Context cost at current state |
|
|
60
|
+
| `tokens_full` | Cost of raw body at full fidelity |
|
|
61
|
+
| `turn` | Freshness — when was this entry last touched |
|
|
62
|
+
|
|
63
|
+
### 1.2 Schemes & States
|
|
64
|
+
|
|
65
|
+
Paths use URI scheme syntax. Bare paths (no `://`) are files.
|
|
66
|
+
|
|
67
|
+
**Files** (`scheme IS NULL`):
|
|
68
|
+
|
|
69
|
+
| State | Model sees |
|
|
70
|
+
|-------|-----------|
|
|
71
|
+
| `full` | File content in code fence |
|
|
72
|
+
| `index` | Path listed in File Index |
|
|
73
|
+
| `stored` | Invisible, retrievable via `<get>` |
|
|
74
|
+
|
|
75
|
+
**Knowledge** (`known://`, `unknown://`):
|
|
76
|
+
|
|
77
|
+
| State | Model sees |
|
|
78
|
+
|-------|-----------|
|
|
79
|
+
| `full` | Key — value in bullet list |
|
|
80
|
+
| `stored` | Key listed, no value |
|
|
81
|
+
|
|
82
|
+
**Tool results** (`set://`, `sh://`, `env://`, `rm://`, `ask_user://`,
|
|
83
|
+
`mv://`, `cp://`, `search://`, `get://`, `store://`):
|
|
84
|
+
|
|
85
|
+
All start at `full` state when recorded. Handlers set the final state:
|
|
86
|
+
`proposed`, `pass`, `rejected`, `error`, `pattern`, `read`, `stored`, `info`.
|
|
87
|
+
|
|
88
|
+
**Skills** (`skill://`): `full` or `stored`. Rendered in system message.
|
|
89
|
+
|
|
90
|
+
**Tools** (`tool://`): `full`, `model_visible = 0`. Internal plugin metadata.
|
|
91
|
+
|
|
92
|
+
**URLs** (`http://`, `https://`): `full`, `summary`, `stored`.
|
|
93
|
+
|
|
94
|
+
**Structural** (`summarize://`, `update://`): Status signals.
|
|
95
|
+
|
|
96
|
+
**Audit** (`system://`, `prompt://`, `ask://`, `act://`, `progress://`,
|
|
97
|
+
`reasoning://`, `model://`, `error://`, `user://`, `assistant://`,
|
|
98
|
+
`content://`): `info` state, `model_visible = 0` (hidden from model).
|
|
99
|
+
|
|
100
|
+
### 1.3 State Validation
|
|
101
|
+
|
|
102
|
+
The `schemes` table is a bootstrap registry — 30 rows of static config.
|
|
103
|
+
INSERT/UPDATE triggers validate state against `schemes.valid_states`.
|
|
104
|
+
Plugins cannot bypass this (circular dependency prevents schemes as entries).
|
|
105
|
+
|
|
106
|
+
### 1.4 UPSERT Semantics
|
|
107
|
+
|
|
108
|
+
INSERT OR REPLACE on `(run_id, path)`. Each write increments `write_count`.
|
|
109
|
+
Blank body is valid. Deletion uses `<rm>`, which removes the row entirely.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 2. Relational Tables
|
|
114
|
+
|
|
115
|
+
The K/V store is the memory. Relational tables are the skeleton.
|
|
116
|
+
|
|
117
|
+
```sql
|
|
118
|
+
projects (id, name UNIQUE, project_root, config_path, created_at)
|
|
119
|
+
models (id, alias UNIQUE, actual, context_length, created_at)
|
|
120
|
+
runs (id, project_id, parent_run_id, model, alias UNIQUE, status,
|
|
121
|
+
temperature, persona, context_limit, next_turn, created_at)
|
|
122
|
+
turns (id, run_id, sequence, prompt_tokens, completion_tokens,
|
|
123
|
+
total_tokens, cost, created_at)
|
|
124
|
+
|
|
125
|
+
file_constraints (id, project_id, pattern, visibility, created_at)
|
|
126
|
+
prompt_queue (id, run_id, mode, model, prompt, config, status, result)
|
|
127
|
+
rpc_log (id, project_id, method, rpc_id, params, result, error)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**No sessions.** Runs belong to projects. Any client that knows the project
|
|
131
|
+
name can access any run. Temperature, persona, and context_limit are per-run.
|
|
132
|
+
|
|
133
|
+
**Models** are bootstrapped from `RUMMY_MODEL_*` env vars at startup (upsert).
|
|
134
|
+
Clients can add/remove models at runtime via RPC. No default model — the
|
|
135
|
+
client picks for every run.
|
|
136
|
+
|
|
137
|
+
### 2.1 Run State Machine
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
queued → running → proposed → running → completed
|
|
141
|
+
→ completed
|
|
142
|
+
→ failed → running
|
|
143
|
+
→ aborted → running
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
All terminal states allow transition back to `running`. Runs are long-lived.
|
|
147
|
+
|
|
148
|
+
### 2.2 Prompt Queue
|
|
149
|
+
|
|
150
|
+
All prompts flow through `prompt_queue`. FIFO per run. One active at a time.
|
|
151
|
+
Abort stops the current prompt; pending prompts survive.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 3. Entry-Driven Dispatch
|
|
156
|
+
|
|
157
|
+
### 3.1 Unified API
|
|
158
|
+
|
|
159
|
+
Three callers, one interface. Each tier is a superset of the one below.
|
|
160
|
+
|
|
161
|
+
| Tier | Transport | Invocation shape |
|
|
162
|
+
|------|-----------|-----------------|
|
|
163
|
+
| Model | XML tags | `{ name: "rm", path: "file.txt" }` |
|
|
164
|
+
| Client | JSON-RPC | `{ method: "rm", params: { path: "file.txt" } }` |
|
|
165
|
+
| Plugin | PluginContext | `rummy.rm({ path: "file.txt" })` |
|
|
166
|
+
|
|
167
|
+
`name` (model) = `method` (client) = method name (plugin). The params
|
|
168
|
+
object is the same shape at every tier.
|
|
169
|
+
|
|
170
|
+
| Method | Model | Client | Plugin |
|
|
171
|
+
|--------|-------|--------|--------|
|
|
172
|
+
| `get`, `set`, `rm`, `mv`, `cp`, `sh`, `env`, `store` | ✓ | ✓ | ✓ |
|
|
173
|
+
| `known`, `unknown`, `ask_user`, `summarize`, `update` | ✓ | ✓ | ✓ |
|
|
174
|
+
| `ask`, `act`, `resolve`, `abort`, `startRun` | — | ✓ | ✓ |
|
|
175
|
+
| `getRuns`, `getModels`, `getEntries` | — | ✓ | ✓ |
|
|
176
|
+
| `on()`, `filter()`, db/store access | — | — | ✓ |
|
|
177
|
+
|
|
178
|
+
Model tier restrictions enforced by mode (ask removes act-only tools).
|
|
179
|
+
Client tier requires project init. Plugin tier has no restrictions.
|
|
180
|
+
|
|
181
|
+
### 3.2 Dispatch Path
|
|
182
|
+
|
|
183
|
+
All three tiers feed the same handler chain:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
Model: XmlParser → { name, path, ... } → #record() → dispatch(scheme, entry, rummy)
|
|
187
|
+
Client: JSON-RPC → { method, params } → #record() → dispatch(scheme, entry, rummy)
|
|
188
|
+
Plugin: rummy.rm({ path }) → #record() → dispatch(scheme, entry, rummy)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 3.3 Plugin Convention
|
|
192
|
+
|
|
193
|
+
A plugin is an instantiated class. The class name matches the file name.
|
|
194
|
+
The constructor receives `core` (a PluginContext) — the plugin's
|
|
195
|
+
complete interface with the system.
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
export default class Rm {
|
|
199
|
+
#core;
|
|
200
|
+
|
|
201
|
+
constructor(core) {
|
|
202
|
+
this.#core = core;
|
|
203
|
+
core.on("handler", this.handler.bind(this));
|
|
204
|
+
core.on("full", this.full.bind(this));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async handler(entry, rummy) {
|
|
208
|
+
// rummy here is per-turn RummyContext (not the startup PluginContext)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
full(entry) {
|
|
212
|
+
return `# rm ${entry.attributes.path}`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Two objects:**
|
|
218
|
+
- `this.#core` — PluginContext (startup). For registration: `on()`, `filter()`.
|
|
219
|
+
- `rummy` argument — RummyContext (per-turn). For runtime: tool verbs, queries.
|
|
220
|
+
|
|
221
|
+
**Plugin types:**
|
|
222
|
+
- **Tool plugins**: register `handler` + `full`/`summary`. Model-invokable.
|
|
223
|
+
- **Assembly plugins**: register `core.filter("assembly.system", ...)`. Own a packet tag.
|
|
224
|
+
- **Infrastructure plugins**: register `core.on("turn", ...)`. Background work.
|
|
225
|
+
|
|
226
|
+
A plugin can be multiple types. Known is a tool AND an assembly plugin.
|
|
227
|
+
|
|
228
|
+
### 3.4 Mode Enforcement
|
|
229
|
+
|
|
230
|
+
All tools are available by default. In ask mode, the core removes
|
|
231
|
+
act-only tools (`sh`, file-scheme `set`) from the tool list. This is
|
|
232
|
+
a core concern — plugins do not declare their modes.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 4. Message Structure
|
|
237
|
+
|
|
238
|
+
Two messages per turn. System = stable truth. User = active task.
|
|
239
|
+
|
|
240
|
+
### 4.1 Packet Structure
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
[system]
|
|
244
|
+
[instructions]
|
|
245
|
+
[sacred_prompt/]
|
|
246
|
+
[toolDescriptions/]
|
|
247
|
+
[persona/]
|
|
248
|
+
[skills/]
|
|
249
|
+
[/instructions]
|
|
250
|
+
<knowledge>
|
|
251
|
+
...entries sorted by fidelity (index, summary, full), then by scheme
|
|
252
|
+
</knowledge>
|
|
253
|
+
<previous>
|
|
254
|
+
(pre-loop user prompt, model responses, agent warnings, and tools used, in order)
|
|
255
|
+
</previous>
|
|
256
|
+
<unknowns></unknowns>
|
|
257
|
+
[/system]
|
|
258
|
+
[user]
|
|
259
|
+
<current>
|
|
260
|
+
(current loop model responses, agent warnings, and tools used, in order)
|
|
261
|
+
</current>
|
|
262
|
+
<progress>the above actions have been performed on this user prompt:</progress>
|
|
263
|
+
<ask tools="..." warn="...">user prompt</ask>
|
|
264
|
+
— OR —
|
|
265
|
+
<act tools="...">user prompt</act>
|
|
266
|
+
[/user]
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**System** contains everything the model needs to know.
|
|
270
|
+
**User** contains everything the model needs to do.
|
|
271
|
+
|
|
272
|
+
The `<ask>`/`<act>` tag is present on every turn — first turn and
|
|
273
|
+
continuations alike. The model always sees its task. The active prompt
|
|
274
|
+
is extracted from its chronological position and placed last for maximum
|
|
275
|
+
recency. `<progress>` bridges the gap, narrating the causal relationship
|
|
276
|
+
between `<current>` (the work) and the prompt (the cause).
|
|
277
|
+
|
|
278
|
+
### 4.2 Loops, Previous, and Current
|
|
279
|
+
|
|
280
|
+
A **loop** is one `ask` or `act` invocation and all its continuation
|
|
281
|
+
turns until summarize, fail, or abort.
|
|
282
|
+
|
|
283
|
+
**Previous** = all completed loops on this run. The user prompt, model
|
|
284
|
+
responses, tool results, agent warnings — the full chronicle in order.
|
|
285
|
+
Lives in the system message as established history. Omitted on the
|
|
286
|
+
first turn of the first loop.
|
|
287
|
+
|
|
288
|
+
**Current** = the active loop's work so far. Model responses, tool
|
|
289
|
+
results, agent warnings — in order. Does NOT include the user prompt
|
|
290
|
+
(one per loop, extracted to `<ask>`/`<act>`). Lives in the user
|
|
291
|
+
message as immediate context. Empty on the first turn of a loop.
|
|
292
|
+
|
|
293
|
+
When a new prompt arrives on an existing run, the prior loop's
|
|
294
|
+
`<current>` content plus its prompt move to `<previous>`. When a loop
|
|
295
|
+
continues (next turn), new results append to `<current>`.
|
|
296
|
+
|
|
297
|
+
### 4.3 Key Entries
|
|
298
|
+
|
|
299
|
+
| Path | Lifetime | Body | Attributes |
|
|
300
|
+
|------|----------|------|-----------|
|
|
301
|
+
| `instructions://system` | One per run (mutable) | Empty (projection builds from preamble + plugins) | `{ persona }` |
|
|
302
|
+
| `system://N` | Audit, one per turn | Full assembled system message | — |
|
|
303
|
+
| `user://N` | Audit, one per turn | Full assembled user message | — |
|
|
304
|
+
| `assistant://N` | Audit, one per turn | Model's raw response | — |
|
|
305
|
+
|
|
306
|
+
`instructions://system` is the only mutable entry in this group. The
|
|
307
|
+
framework auto-populates `toolDescriptions` from tool registrations
|
|
308
|
+
that include `docs`. The instructions projection assembles the final
|
|
309
|
+
text from body + attributes.
|
|
310
|
+
|
|
311
|
+
### 4.4 Materialization
|
|
312
|
+
|
|
313
|
+
Each turn:
|
|
314
|
+
|
|
315
|
+
1. Write `instructions://system` (empty body, attributes = { persona })
|
|
316
|
+
2. Run plugin hooks (`onTurn`) — plugins modify entries before the model sees them
|
|
317
|
+
3. Project `instructions://system` → instructions text
|
|
318
|
+
4. Query `v_model_context` VIEW → visible entries
|
|
319
|
+
5. Project each entry through its tool's `full`/`summary` projection
|
|
320
|
+
6. Insert projected rows into `turn_context`
|
|
321
|
+
7. Invoke `assembly.system` filter chain (instructions text as base):
|
|
322
|
+
- Known plugin (priority 100) → `<known>` section
|
|
323
|
+
- Previous plugin (priority 200) → `<previous>` section
|
|
324
|
+
- Unknown plugin (priority 300) → `<unknowns>` section
|
|
325
|
+
8. Invoke `assembly.user` filter chain (empty string as base):
|
|
326
|
+
- Current plugin (priority 100) → `<current>` section
|
|
327
|
+
- Progress plugin (priority 200) → `<progress>` section
|
|
328
|
+
- Prompt plugin (priority 300) → `<ask>`/`<act>` section
|
|
329
|
+
9. Store as `system://N` and `user://N` audit entries
|
|
330
|
+
|
|
331
|
+
The VIEW determines visibility. State IS fidelity:
|
|
332
|
+
- `full` → body visible
|
|
333
|
+
- `summary` → body visible
|
|
334
|
+
- `index` → path listed, no content
|
|
335
|
+
- `stored` → invisible
|
|
336
|
+
- `proposed` → invisible (pending client)
|
|
337
|
+
- `model_visible = 0` → invisible (audit, tool, instructions)
|
|
338
|
+
|
|
339
|
+
### 4.5 progress:// as Entry
|
|
340
|
+
|
|
341
|
+
The continuation prompt is a `progress://N` entry. Plugins can modify its
|
|
342
|
+
body before materialization.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 5. RPC Protocol
|
|
347
|
+
|
|
348
|
+
JSON-RPC 2.0 over WebSocket. `discover` returns the live catalog.
|
|
349
|
+
|
|
350
|
+
### 5.1 Methods
|
|
351
|
+
|
|
352
|
+
#### Protocol
|
|
353
|
+
|
|
354
|
+
| Method | Params |
|
|
355
|
+
|--------|--------|
|
|
356
|
+
| `ping` | — |
|
|
357
|
+
| `discover` | — |
|
|
358
|
+
| `init` | `{ name, projectRoot, configPath? }` |
|
|
359
|
+
|
|
360
|
+
#### Models
|
|
361
|
+
|
|
362
|
+
| Method | Params |
|
|
363
|
+
|--------|--------|
|
|
364
|
+
| `getModels` | `{ limit?, offset? }` |
|
|
365
|
+
| `addModel` | `{ alias, actual, contextLength? }` |
|
|
366
|
+
| `removeModel` | `{ alias }` |
|
|
367
|
+
|
|
368
|
+
#### Entry Operations (dispatched through handler chain)
|
|
369
|
+
|
|
370
|
+
| Method | Params |
|
|
371
|
+
|--------|--------|
|
|
372
|
+
| `read` | `{ path, run?, persist?, readonly? }` |
|
|
373
|
+
| `store` | `{ path, run?, persist?, ignore?, clear? }` |
|
|
374
|
+
| `write` | `{ run, path, body?, state?, attributes? }` |
|
|
375
|
+
| `delete` | `{ run, path }` |
|
|
376
|
+
| `getEntries` | `{ pattern?, body?, run?, limit?, offset? }` |
|
|
377
|
+
|
|
378
|
+
`persist` creates a project-level file constraint (operator privilege).
|
|
379
|
+
Without `persist`, operations dispatch through the handler chain.
|
|
380
|
+
|
|
381
|
+
#### Runs
|
|
382
|
+
|
|
383
|
+
| Method | Params |
|
|
384
|
+
|--------|--------|
|
|
385
|
+
| `startRun` | `{ model, temperature?, persona?, contextLimit? }` |
|
|
386
|
+
| `ask` | `{ prompt, model, run?, temperature?, persona?, contextLimit?, noContext?, fork? }` |
|
|
387
|
+
| `act` | `{ prompt, model, run?, temperature?, persona?, contextLimit?, noContext?, fork? }` |
|
|
388
|
+
| `run/resolve` | `{ run, resolution: { path, action, output? } }` |
|
|
389
|
+
| `run/abort` | `{ run }` |
|
|
390
|
+
| `run/rename` | `{ run, name }` |
|
|
391
|
+
| `run/inject` | `{ run, message }` |
|
|
392
|
+
| `run/config` | `{ run, temperature?, persona?, contextLimit?, model? }` |
|
|
393
|
+
|
|
394
|
+
`model` is required on `ask`, `act`, and `startRun`. No default.
|
|
395
|
+
|
|
396
|
+
#### Queries
|
|
397
|
+
|
|
398
|
+
| Method | Params |
|
|
399
|
+
|--------|--------|
|
|
400
|
+
| `getRuns` | `{ limit?, offset? }` |
|
|
401
|
+
| `getRun` | `{ run }` |
|
|
402
|
+
|
|
403
|
+
#### Skills & Personas
|
|
404
|
+
|
|
405
|
+
| Method | Params |
|
|
406
|
+
|--------|--------|
|
|
407
|
+
| `skill/add` | `{ run, name }` |
|
|
408
|
+
| `skill/remove` | `{ run, name }` |
|
|
409
|
+
| `getSkills` | `{ run }` |
|
|
410
|
+
| `listSkills` | — |
|
|
411
|
+
| `persona/set` | `{ run, name?, text? }` |
|
|
412
|
+
| `listPersonas` | — |
|
|
413
|
+
|
|
414
|
+
Skills loaded from `RUMMY_HOME/skills/{name}.md`. Personas from
|
|
415
|
+
`RUMMY_HOME/personas/{name}.md`.
|
|
416
|
+
|
|
417
|
+
### 5.2 Notifications
|
|
418
|
+
|
|
419
|
+
| Notification | Scoped by |
|
|
420
|
+
|-------------|-----------|
|
|
421
|
+
| `run/state` | projectId |
|
|
422
|
+
| `run/progress` | projectId |
|
|
423
|
+
| `ui/render` | projectId |
|
|
424
|
+
| `ui/notify` | projectId |
|
|
425
|
+
|
|
426
|
+
### 5.3 Resolution
|
|
427
|
+
|
|
428
|
+
| Resolution | Model signal | Outcome |
|
|
429
|
+
|-----------|-------------|---------|
|
|
430
|
+
| reject | any | `completed` — rejection stops the bus |
|
|
431
|
+
| accept | `<update>` | `running` — model has more work |
|
|
432
|
+
| accept | `<summarize>` | `completed` |
|
|
433
|
+
| accept | neither | `running` — healer decides |
|
|
434
|
+
| error | any | `running` — error state, model retries |
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 6. Plugin System
|
|
439
|
+
|
|
440
|
+
See [PLUGINS.md](PLUGINS.md) for the full plugin development guide,
|
|
441
|
+
including the RummyContext API, tool registration, handler chains,
|
|
442
|
+
projections, events, filters, and hedberg pattern library.
|
|
443
|
+
|
|
444
|
+
Each plugin has its own README at `src/plugins/{name}/README.md`.
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## 7. Hedberg Editing Syntax
|
|
449
|
+
|
|
450
|
+
The model picks its preferred edit format. The parser understands all of them:
|
|
451
|
+
|
|
452
|
+
1. Git merge conflict: `<<<<<<< SEARCH ... ======= ... >>>>>>> REPLACE`
|
|
453
|
+
2. Replace-only: `======= ... >>>>>>> REPLACE`
|
|
454
|
+
3. Unified diff: `@@ -1,3 +1,3 @@` with `-`/`+` lines
|
|
455
|
+
4. Sed syntax: `s/old/new/flags`
|
|
456
|
+
5. Claude XML: `<old_text>old</old_text><new_text>new</new_text>`
|
|
457
|
+
6. JSON body: `{"search": "old", "replace": "new"}` or `{search="old", replace="new"}`
|
|
458
|
+
7. XML attributes: `<set search="old" replace="new"/>`
|
|
459
|
+
8. Full replacement: anything else becomes the new content
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## 8. Response Healing
|
|
464
|
+
|
|
465
|
+
The server never throws on model output. Recovery order:
|
|
466
|
+
|
|
467
|
+
1. Can we recover? Extract the data and continue.
|
|
468
|
+
2. Can we warn? Log structured warnings.
|
|
469
|
+
3. Did our structure cause this? Check formatting, prompts.
|
|
470
|
+
4. Model drift is the LAST answer.
|
|
471
|
+
|
|
472
|
+
Termination protocol:
|
|
473
|
+
- `<summarize>` → run terminates
|
|
474
|
+
- `<update>` → run continues
|
|
475
|
+
- Both → summarize wins
|
|
476
|
+
- Neither + tools → stall counter
|
|
477
|
+
- Neither + plain text → healed to summarize
|
|
478
|
+
- Repeated commands → loop detection
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## 9. Testing
|
|
483
|
+
|
|
484
|
+
| Tier | Location | LLM? |
|
|
485
|
+
|------|----------|------|
|
|
486
|
+
| Unit | `src/**/*.test.js` | No |
|
|
487
|
+
| Integration | `test/integration/` | No |
|
|
488
|
+
| Live | `test/live/` | Yes |
|
|
489
|
+
| E2E | `test/e2e/` | Yes |
|
|
490
|
+
|
|
491
|
+
E2E tests must NEVER mock the LLM. Environment cascade:
|
|
492
|
+
`.env.example` → `.env` → `.env.test`. Always use `npm run test:*`.
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## 10. SQL Functions
|
|
497
|
+
|
|
498
|
+
| Function | Purpose |
|
|
499
|
+
|----------|---------|
|
|
500
|
+
| `schemeOf(path)` | Extract URI scheme |
|
|
501
|
+
| `countTokens(text)` | Token count (tiktoken o200k_base, `ceil(len/4)` fallback) |
|
|
502
|
+
| `hedmatch(pattern, string)` | Full-string pattern match (paths, equality) |
|
|
503
|
+
| `hedsearch(pattern, string)` | Substring pattern search (content filtering) |
|
|
504
|
+
| `hedreplace(pattern, replacement, string)` | Pattern-based replacement |
|
|
505
|
+
| `slugify(text)` | URI-encoded slug, max 80 chars |
|
|
506
|
+
|
|
507
|
+
See [PLUGINS.md](PLUGINS.md) for the hedberg pattern type reference.
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## 11. Configuration
|
|
512
|
+
|
|
513
|
+
```env
|
|
514
|
+
RUMMY_HOME=~/.rummy
|
|
515
|
+
RUMMY_MAX_TURNS=15
|
|
516
|
+
RUMMY_MAX_STALLS=3
|
|
517
|
+
RUMMY_MAX_REPETITIONS=3
|
|
518
|
+
RUMMY_RETENTION_DAYS=31
|
|
519
|
+
RUMMY_TEMPERATURE=0.7
|
|
520
|
+
RUMMY_DEBUG=false
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Model aliases: `RUMMY_MODEL_{alias}={provider/model}`. Seeded into
|
|
524
|
+
`models` table at startup.
|
package/lang/en.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"error.session_not_found": "Session '{sessionId}' not found.",
|
|
3
|
+
"error.project_not_found": "Project '{projectId}' not found.",
|
|
4
|
+
"error.run_not_found": "Run '{runId}' not found.",
|
|
5
|
+
"error.not_initialized": "Project not initialized.",
|
|
6
|
+
"error.method_not_found": "Method '{method}' not found.",
|
|
7
|
+
"error.rpc_timeout": "RPC '{method}' timed out after {timeout}ms.",
|
|
8
|
+
"error.run_name_invalid": "Name must match [a-z_]+.",
|
|
9
|
+
"error.run_name_taken": "Name '{name}' is already taken.",
|
|
10
|
+
"error.model_alias_unknown": "Unknown model alias '{alias}'. Define RUMMY_MODEL_{alias} in your environment.",
|
|
11
|
+
"error.openai_base_url_missing": "openai/ model requested but neither OPENAI_BASE_URL nor OPENAI_API_BASE is set.",
|
|
12
|
+
"error.openrouter_api_key_missing": "OpenRouter API key is missing. Please set OPENROUTER_API_KEY in your environment.",
|
|
13
|
+
"error.openrouter_auth": "OpenRouter Authentication Error: {status}. Please check your OPENROUTER_API_KEY.",
|
|
14
|
+
"error.openrouter_api": "OpenRouter API error: {status}",
|
|
15
|
+
"error.openrouter_catalog": "OpenRouter /models failed: {status}. Check your OPENROUTER_API_KEY.",
|
|
16
|
+
"error.openrouter_model_not_found": "Model '{model}' not found in OpenRouter catalog.",
|
|
17
|
+
"error.openrouter_no_context_length": "OpenRouter reports no context_length for model '{model}'.",
|
|
18
|
+
"error.ollama_api": "Ollama API error: {status}",
|
|
19
|
+
"error.ollama_show_failed": "Ollama /api/show failed: {status}. Is Ollama running at {baseUrl}?",
|
|
20
|
+
"error.ollama_no_context_length": "Ollama /api/show returned no context_length for model '{model}'.",
|
|
21
|
+
"error.ollama_unreachable": "Ollama /api/show unreachable after 3 attempts. Is Ollama running at {baseUrl}?",
|
|
22
|
+
"error.openai_api": "OpenAI-compatible API error: {status}",
|
|
23
|
+
"error.openai_models_failed": "OpenAI-compatible /v1/models failed: {status}. Is the server running at {baseUrl}?",
|
|
24
|
+
"error.openai_no_context_length": "OpenAI-compatible /v1/models returned no context size.",
|
|
25
|
+
"error.missing_summary": "missing required summary",
|
|
26
|
+
"error.unresolved_proposed": "Blocked: run has {count} unresolved proposed entries.",
|
|
27
|
+
"error.tool_already_registered": "Tool '{name}' already registered.",
|
|
28
|
+
"error.rpc_already_registered": "RPC method '{name}' already registered.",
|
|
29
|
+
"error.resolution_invalid": "Invalid resolution action: {action}. Use 'accept' or 'reject'.",
|
|
30
|
+
"error.xai_base_url_missing": "x.ai/ model requested but XAI_BASE_URL is not set.",
|
|
31
|
+
"error.xai_api_key_missing": "x.ai/ model requested but XAI_API_KEY is not set.",
|
|
32
|
+
"error.xai_auth": "xAI Authentication Error: {status}. Please check your XAI_API_KEY.",
|
|
33
|
+
"error.xai_api": "xAI API error: {status}"
|
|
34
|
+
}
|