@possumtech/rummy 2.1.0 → 2.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 +40 -15
- package/.xai.key +1 -0
- package/PLUGINS.md +169 -53
- package/README.md +38 -32
- package/SPEC.md +366 -179
- package/bin/digest.js +1097 -0
- package/biome/no-fallbacks.grit +2 -2
- package/gemini.key +1 -0
- package/lang/en.json +10 -1
- package/migrations/001_initial_schema.sql +9 -2
- package/package.json +19 -8
- package/service.js +1 -0
- package/src/agent/AgentLoop.js +76 -26
- package/src/agent/ContextAssembler.js +2 -0
- package/src/agent/Entries.js +238 -60
- package/src/agent/ProjectAgent.js +44 -0
- package/src/agent/TurnExecutor.js +99 -30
- package/src/agent/XmlParser.js +206 -111
- package/src/agent/errors.js +35 -0
- package/src/agent/known_queries.sql +1 -1
- package/src/agent/known_store.sql +3 -42
- package/src/agent/materializeContext.js +30 -1
- package/src/agent/runs.sql +8 -18
- package/src/agent/tokens.js +0 -1
- package/src/agent/turns.sql +1 -0
- package/src/hooks/Hooks.js +26 -0
- package/src/hooks/RummyContext.js +12 -1
- package/src/lib/hedberg/README.md +60 -0
- package/src/lib/hedberg/hedberg.js +60 -0
- package/src/lib/hedberg/marker.js +158 -0
- package/src/{plugins → lib}/hedberg/matcher.js +1 -2
- package/src/llm/LlmProvider.js +41 -3
- package/src/llm/openaiStream.js +17 -0
- package/src/plugins/ask_user/ask_user.js +12 -2
- package/src/plugins/ask_user/ask_userDoc.md +1 -5
- package/src/plugins/budget/README.md +29 -24
- package/src/plugins/budget/budget.js +166 -110
- package/src/plugins/cli/README.md +3 -4
- package/src/plugins/cli/cli.js +31 -5
- package/src/plugins/cloudflare/cloudflare.js +136 -0
- package/src/plugins/cp/cp.js +41 -4
- package/src/plugins/cp/cpDoc.md +5 -6
- package/src/plugins/engine/engine.sql +1 -1
- package/src/plugins/env/README.md +5 -4
- package/src/plugins/env/env.js +7 -4
- package/src/plugins/env/envDoc.md +7 -8
- package/src/plugins/error/error.js +56 -15
- package/src/plugins/file/README.md +12 -3
- package/src/plugins/file/file.js +2 -2
- package/src/plugins/get/get.js +59 -36
- package/src/plugins/get/getDoc.md +10 -34
- package/src/plugins/google/google.js +115 -0
- package/src/plugins/hedberg/hedberg.js +13 -56
- package/src/plugins/helpers.js +66 -12
- package/src/plugins/index.js +1 -2
- package/src/plugins/instructions/README.md +44 -47
- package/src/plugins/instructions/instructions-system.md +44 -0
- package/src/plugins/instructions/instructions-user.md +53 -0
- package/src/plugins/instructions/instructions.js +58 -189
- package/src/plugins/known/README.md +6 -7
- package/src/plugins/known/known.js +24 -30
- package/src/plugins/log/log.js +41 -32
- package/src/plugins/mv/mv.js +40 -1
- package/src/plugins/mv/mvDoc.md +1 -8
- package/src/plugins/ollama/ollama.js +4 -3
- package/src/plugins/openai/openai.js +4 -3
- package/src/plugins/openrouter/openrouter.js +14 -4
- package/src/plugins/persona/README.md +11 -13
- package/src/plugins/persona/default.md +29 -0
- package/src/plugins/persona/persona.js +10 -66
- package/src/plugins/policy/policy.js +23 -22
- package/src/plugins/prompt/README.md +37 -27
- package/src/plugins/prompt/prompt.js +13 -19
- package/src/plugins/rm/rm.js +18 -0
- package/src/plugins/rm/rmDoc.md +5 -6
- package/src/plugins/rpc/rpc.js +3 -3
- package/src/plugins/set/set.js +205 -323
- package/src/plugins/set/setDoc.md +47 -17
- package/src/plugins/sh/README.md +6 -5
- package/src/plugins/sh/sh.js +8 -5
- package/src/plugins/sh/shDoc.md +7 -8
- package/src/plugins/skill/README.md +37 -14
- package/src/plugins/skill/skill.js +200 -101
- package/src/plugins/skill/skillDoc.js +3 -0
- package/src/plugins/skill/skillDoc.md +9 -0
- package/src/plugins/stream/README.md +7 -6
- package/src/plugins/stream/finalize.js +100 -0
- package/src/plugins/stream/stream.js +13 -45
- package/src/plugins/telemetry/telemetry.js +27 -4
- package/src/plugins/think/think.js +2 -3
- package/src/plugins/think/thinkDoc.md +2 -4
- package/src/plugins/unknown/README.md +1 -1
- package/src/plugins/unknown/unknown.js +17 -19
- package/src/plugins/update/update.js +4 -51
- package/src/plugins/update/updateDoc.md +21 -6
- package/src/plugins/xai/xai.js +68 -102
- package/src/plugins/yolo/yolo.js +102 -75
- package/src/sql/functions/hedmatch.js +1 -1
- package/src/sql/functions/hedreplace.js +1 -1
- package/src/sql/functions/hedsearch.js +1 -1
- package/src/sql/functions/slugify.js +16 -2
- package/BENCH_ENVIRONMENT.md +0 -230
- package/CLIENT_INTERFACE.md +0 -396
- package/last_run.txt +0 -5617
- package/scriptify/ask_run.js +0 -77
- package/scriptify/cache_probe.js +0 -66
- package/scriptify/cache_probe_grok.js +0 -74
- package/src/agent/budget.js +0 -33
- package/src/agent/config.js +0 -38
- package/src/plugins/hedberg/README.md +0 -71
- package/src/plugins/hedberg/docs.md +0 -0
- package/src/plugins/hedberg/edits.js +0 -55
- package/src/plugins/hedberg/normalize.js +0 -17
- package/src/plugins/hedberg/sed.js +0 -49
- package/src/plugins/instructions/instructions.md +0 -34
- package/src/plugins/instructions/instructions_104.md +0 -8
- package/src/plugins/instructions/instructions_105.md +0 -39
- package/src/plugins/instructions/instructions_106.md +0 -22
- package/src/plugins/instructions/instructions_107.md +0 -17
- package/src/plugins/instructions/instructions_108.md +0 -0
- package/src/plugins/known/knownDoc.js +0 -3
- package/src/plugins/known/knownDoc.md +0 -8
- package/src/plugins/unknown/unknownDoc.js +0 -3
- package/src/plugins/unknown/unknownDoc.md +0 -11
- package/turns/cli_1777462658211/turn_001.txt +0 -772
- package/turns/cli_1777462658211/turn_002.txt +0 -606
- package/turns/cli_1777462658211/turn_003.txt +0 -667
- package/turns/cli_1777462658211/turn_004.txt +0 -297
- package/turns/cli_1777462658211/turn_005.txt +0 -301
- package/turns/cli_1777462658211/turn_006.txt +0 -262
- package/turns/cli_1777465095132/turn_001.txt +0 -715
- package/turns/cli_1777465095132/turn_002.txt +0 -236
- package/turns/cli_1777465095132/turn_003.txt +0 -287
- package/turns/cli_1777465095132/turn_004.txt +0 -694
- package/turns/cli_1777465095132/turn_005.txt +0 -422
- package/turns/cli_1777465095132/turn_006.txt +0 -365
- package/turns/cli_1777465095132/turn_007.txt +0 -885
- package/turns/cli_1777465095132/turn_008.txt +0 -1277
- package/turns/cli_1777465095132/turn_009.txt +0 -736
- /package/src/{plugins → lib}/hedberg/patterns.js +0 -0
package/.env.example
CHANGED
|
@@ -17,16 +17,25 @@ RUMMY_MMAP_MB=0
|
|
|
17
17
|
|
|
18
18
|
# Agent Loop Limits — per-loop cap (turns within a single loop).
|
|
19
19
|
# No per-run cap; a run can comprise many loops.
|
|
20
|
-
RUMMY_MAX_LOOP_TURNS=
|
|
20
|
+
RUMMY_MAX_LOOP_TURNS=999
|
|
21
21
|
# Hard cap on commands per turn — high by design. The real cost
|
|
22
22
|
# ceiling is the Token Budget; per-tool rate limits (e.g.
|
|
23
23
|
# RUMMY_WEB_SEARCH_MAX) bound the expensive tools individually.
|
|
24
24
|
RUMMY_MAX_COMMANDS=99
|
|
25
25
|
# Per-turn cap on <search>. Refusals strike via 429.
|
|
26
26
|
RUMMY_WEB_SEARCH_MAX=1
|
|
27
|
+
# Default candidate count per <search>. Brave caps at 20; the model can
|
|
28
|
+
# still narrow per-call via <search results="N">.
|
|
29
|
+
RUMMY_WEB_SEARCH_RESULTS=20
|
|
27
30
|
RUMMY_MAX_STRIKES=3
|
|
28
31
|
RUMMY_MIN_CYCLES=3
|
|
29
32
|
RUMMY_MAX_CYCLE_PERIOD=4
|
|
33
|
+
# Free turns in an FCRM admin stage (Decomposition or Demotion) before
|
|
34
|
+
# the stagnation strike fires. Turn N+1 in the same admin stage emits a
|
|
35
|
+
# "N+1 turns in current stage" reminder and contributes to the strike
|
|
36
|
+
# streak (3 strikes → MAX_STRIKES → abandon). Distillation and Deployment
|
|
37
|
+
# are exempt — those phases can grind for many turns on hard tasks.
|
|
38
|
+
RUMMY_STAGNATION_FREE_TURNS=3
|
|
30
39
|
|
|
31
40
|
# Hygiene
|
|
32
41
|
# Days to keep completed/aborted runs before purging
|
|
@@ -35,16 +44,25 @@ RUMMY_RETENTION_DAYS=31
|
|
|
35
44
|
# Timeouts (ms)
|
|
36
45
|
RUMMY_RPC_TIMEOUT=30000
|
|
37
46
|
RUMMY_FETCH_TIMEOUT=300000
|
|
47
|
+
RUMMY_WEB_FETCH_TIMEOUT=300000
|
|
38
48
|
# Test harness — how long AuditClient waits for a single ask/act to reach
|
|
39
49
|
# terminal status. Sized for full-context ingest on large-window models.
|
|
40
50
|
RUMMY_TEST_RUN_TIMEOUT=3600000
|
|
41
|
-
# rummy-cli watchdog — wall-clock budget for a one
|
|
42
|
-
# Overridable per invocation via --
|
|
43
|
-
|
|
51
|
+
# rummy-cli watchdog — wall-clock budget for a single loop (one ask/act
|
|
52
|
+
# CLI invocation). Overridable per invocation via --RUMMY_LOOP_TIMEOUT=<ms>.
|
|
53
|
+
RUMMY_LOOP_TIMEOUT=86400000
|
|
44
54
|
|
|
45
55
|
# Plugin module load watchdog.
|
|
46
56
|
RUMMY_PLUGINS_LOAD_TIMEOUT=10000
|
|
47
57
|
|
|
58
|
+
# Per-entry storage cap (bytes). Generous by design — rummy is a
|
|
59
|
+
# memory-resident workspace, not a chat buffer — but bounded so a
|
|
60
|
+
# pathological capture (e.g. 100 MB of vim escape codes from a single
|
|
61
|
+
# <sh>) becomes a healthy 413 strike instead of an unbounded write.
|
|
62
|
+
# Enforced at the SQLite layer (entries.body CHECK) and surfaced to
|
|
63
|
+
# the model as an error.log entry the strike system can act on.
|
|
64
|
+
RUMMY_ENTRY_SIZE_MAX=104857600
|
|
65
|
+
|
|
48
66
|
# LLM retry policy: time-bounded exponential backoff with full jitter.
|
|
49
67
|
# DEADLINE is total wall-clock budget for an LLM call across all retries.
|
|
50
68
|
# MAX_BACKOFF caps each inter-attempt sleep so a long deadline doesn't
|
|
@@ -55,9 +73,20 @@ RUMMY_LLM_MAX_BACKOFF=30000
|
|
|
55
73
|
# Debug
|
|
56
74
|
# RUMMY_DEBUG=true
|
|
57
75
|
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
|
|
76
|
+
# Reasoning request flag forwarded to the LLM provider's `think`
|
|
77
|
+
# parameter (gemma/llama.cpp: think=false skips server-side reasoning;
|
|
78
|
+
# ollama: same). 1 asks the provider to reason and surface reasoning
|
|
79
|
+
# content to rummy.
|
|
80
|
+
#
|
|
81
|
+
# Default 0 because forced reasoning on weak models (e.g. local gemma)
|
|
82
|
+
# burns the n_ctx ceiling on `<think>` blocks and triggers reasoning-
|
|
83
|
+
# runaway strikes. Opt in deliberately, per model.
|
|
84
|
+
#
|
|
85
|
+
# OpenRouter is intentionally orthogonal: rummy.web's openrouter plugin
|
|
86
|
+
# always sends `include_reasoning: true` (relay-level always-on
|
|
87
|
+
# telemetry — we pay the upstream cost regardless, so we keep the
|
|
88
|
+
# reasoning bytes). RUMMY_THINK does not apply there.
|
|
89
|
+
RUMMY_THINK=0
|
|
61
90
|
|
|
62
91
|
# Budget
|
|
63
92
|
# Fraction of context window used as ceiling. 0.9 = 90%, 10% reserved as headroom.
|
|
@@ -72,7 +101,7 @@ RUMMY_TOKEN_DIVISOR=2
|
|
|
72
101
|
|
|
73
102
|
# Model Behavior
|
|
74
103
|
# LLM temperature (0 = deterministic, 0.7 = creative). Client can override per-request.
|
|
75
|
-
RUMMY_TEMPERATURE=0.
|
|
104
|
+
RUMMY_TEMPERATURE=0.1
|
|
76
105
|
|
|
77
106
|
# Run Attribute Defaults
|
|
78
107
|
# Per-run attributes (passed in the run-creation set call) trump these.
|
|
@@ -97,7 +126,7 @@ RUMMY_X_TITLE=RUMMY
|
|
|
97
126
|
# OPENAI_BASE_URL="http://127.0.0.1:11434"
|
|
98
127
|
# OPENAI_API_KEY=
|
|
99
128
|
|
|
100
|
-
# XAI_BASE_URL="https://api.x.ai/v1
|
|
129
|
+
# XAI_BASE_URL="https://api.x.ai/v1"
|
|
101
130
|
# XAI_API_KEY=""
|
|
102
131
|
|
|
103
132
|
# Model Aliases (Optional)
|
|
@@ -108,7 +137,7 @@ RUMMY_X_TITLE=RUMMY
|
|
|
108
137
|
# RUMMY_MODEL_grok="xai/grok-4-1-fast-reasoning-latest"
|
|
109
138
|
# RUMMY_MODEL_opus="openrouter/anthropic/claude-opus-4.6"
|
|
110
139
|
# RUMMY_MODEL_gpro="openrouter/google/gemini-3.1-pro-preview"
|
|
111
|
-
# RUMMY_MODEL_gemma="openai/
|
|
140
|
+
# RUMMY_MODEL_gemma="openai/macher.gguf"
|
|
112
141
|
# RUMMY_MODEL_qwen="ollama/qwen:7b"
|
|
113
142
|
|
|
114
143
|
# Necessary for automated testing
|
|
@@ -116,11 +145,7 @@ RUMMY_X_TITLE=RUMMY
|
|
|
116
145
|
|
|
117
146
|
# Web Search
|
|
118
147
|
|
|
119
|
-
#
|
|
120
|
-
# RUMMY_SEARXNG_URL="http://127.0.0.1:8888"
|
|
121
|
-
|
|
122
|
-
# RUMMY_SEARCH="brave"
|
|
123
|
-
# BRAVE_API_KEY=""
|
|
148
|
+
# RUMMY_WEB_SEARXNG_URL="http://127.0.0.1:8888"
|
|
124
149
|
|
|
125
150
|
# External plugins: npm i -g <package>, then uncomment
|
|
126
151
|
# RUMMY_PLUGIN_WEB="@possumtech/rummy.web"
|
package/.xai.key
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export XAI_API_KEY="xai-oI0qbXtb0SUCLqgw3qeDPhIytrRs2YuPn3zGIV0l8XSygn1s5M1ZglxfbFHvCJUrIYrIbQvurCzkLpZA"
|
package/PLUGINS.md
CHANGED
|
@@ -182,7 +182,7 @@ is a superset of what's below.
|
|
|
182
182
|
| Event | Payload | Purpose |
|
|
183
183
|
|-------|---------|---------|
|
|
184
184
|
| `"handler"` | `(entry, rummy)` | Tool handler — called when model/client invokes this tool |
|
|
185
|
-
| `"visible"` | `(entry)` | Visible-visibility projection — body shown in `<
|
|
185
|
+
| `"visible"` | `(entry)` | Visible-visibility projection — body shown in `<visible>` (data) or `<log>` (logging) |
|
|
186
186
|
| `"summarized"` | `(entry)` | Summarized-visibility projection — path + summary only (body hidden) |
|
|
187
187
|
| `"turn.started"` | `({rummy, mode, prompt, loopIteration, isContinuation})` | Turn beginning — plugins write prompt/instructions entries |
|
|
188
188
|
| `"turn.response"` | `({rummy, turn, result, responseMessage, content, commands, ...})` | LLM responded — write audit entries, commit usage |
|
|
@@ -253,7 +253,7 @@ the message. Current `assembly.user` registrations:
|
|
|
253
253
|
|
|
254
254
|
| Priority | Block | Plugin | Mutates per turn? |
|
|
255
255
|
|---|---|---|---|
|
|
256
|
-
| 50 | `<
|
|
256
|
+
| 50 | `<summary>` | `known.js` | Slow — only on new entry |
|
|
257
257
|
| 75 | `<visible>` | `known.js` | Fast — on every promote/demote |
|
|
258
258
|
| 100 | `<log>` | `log.js` | Always — appends per action |
|
|
259
259
|
| 200 | `<unknowns>` | `unknown.js` | On unknown lifecycle |
|
|
@@ -266,7 +266,7 @@ and predictable rendering position):
|
|
|
266
266
|
|
|
267
267
|
| Range | Position | Use for |
|
|
268
268
|
|---|---|---|
|
|
269
|
-
| `0–49` | Top of user | Reserved (stable identity-tier blocks above `<
|
|
269
|
+
| `0–49` | Top of user | Reserved (stable identity-tier blocks above `<summary>`) |
|
|
270
270
|
| `50–99` | Codebase data surface | Don't add here — owned by `known.js` |
|
|
271
271
|
| `100–149` | History tier | Action history, timeline-style content |
|
|
272
272
|
| `150–199` | Open slot | Inter-history blocks (e.g. recent-decisions, tracked progress) |
|
|
@@ -378,10 +378,10 @@ full contract and the rationale.
|
|
|
378
378
|
|
|
379
379
|
Returns the string the model sees for this tool's entries at the
|
|
380
380
|
given visibility. Every tool MUST register `full`. `summary` is
|
|
381
|
-
optional — if unregistered, falls back to `attributes.
|
|
381
|
+
optional — if unregistered, falls back to `attributes.tags`
|
|
382
382
|
(model-authored keyword description) or empty string.
|
|
383
383
|
|
|
384
|
-
At summary visibility, `attributes.
|
|
384
|
+
At summary visibility, `attributes.tags` is prepended above the
|
|
385
385
|
plugin's summary output automatically by ToolRegistry.view().
|
|
386
386
|
|
|
387
387
|
## Two Objects {#plugins_two_objects}
|
|
@@ -424,6 +424,23 @@ instead.
|
|
|
424
424
|
| `rummy.getEntry(path)` | First matching entry or null |
|
|
425
425
|
| `rummy.getEntries(pattern, bodyFilter?)` | Array of matching entries |
|
|
426
426
|
| `rummy.setAttributes(path, attrs)` | Merge attributes via json_patch |
|
|
427
|
+
| `rummy.entries.logPath(runId, turn, action, target)` | Build a `log://turn_N/<action>/<slug>` path, slugified + collision-safe |
|
|
428
|
+
| `rummy.entries.slugPath(runId, scheme, content, summary?)` | Build a `<scheme>://<slug>` path, slugified + collision-safe |
|
|
429
|
+
|
|
430
|
+
#### Path conventions {#plugins_path_conventions}
|
|
431
|
+
|
|
432
|
+
Entry paths are bounded by a hard `length(path) <= 2048` DB
|
|
433
|
+
CHECK constraint. In normal use, paths stay well under ~100 chars
|
|
434
|
+
because plugins build them via `logPath` / `slugPath`, which run the
|
|
435
|
+
target through `slugify` (80-char cap, `/` preserved as separator,
|
|
436
|
+
URL-encoded per segment) and append an integer tie-breaker on
|
|
437
|
+
collision (e.g. `log://turn_3/set/src/app.js_2`).
|
|
438
|
+
|
|
439
|
+
Plugin authors should pass any model-supplied target straight
|
|
440
|
+
through these helpers instead of stitching paths from the model's
|
|
441
|
+
raw input. The helpers absorb arbitrary target length and exotic
|
|
442
|
+
character composition without the caller having to defend against
|
|
443
|
+
either. The 2048 limit is the outer wall, not the working budget.
|
|
427
444
|
|
|
428
445
|
### Properties {#plugins_rummy_properties}
|
|
429
446
|
|
|
@@ -460,23 +477,44 @@ handles all exclusions:
|
|
|
460
477
|
|
|
461
478
|
## Hedberg {#plugins_hedberg}
|
|
462
479
|
|
|
463
|
-
|
|
464
|
-
|
|
480
|
+
Hedberg has two faces. The implementation is a **library** at
|
|
481
|
+
`src/lib/hedberg/` — pattern matching, fuzzy literal replacement,
|
|
482
|
+
unified-diff generation. Internal plugins import these utilities
|
|
483
|
+
directly:
|
|
465
484
|
|
|
466
485
|
```js
|
|
467
|
-
|
|
468
|
-
|
|
486
|
+
import { hedmatch, hedsearch } from "../../lib/hedberg/patterns.js";
|
|
487
|
+
import Hedberg, { generatePatch } from "../../lib/hedberg/hedberg.js";
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
A thin **plugin shim** at `src/plugins/hedberg/` re-exposes the same
|
|
491
|
+
surface on `core.hooks.hedberg` for external plugins shipped in
|
|
492
|
+
separate packages (`rummy.repo`, `rummy.web`, etc.) that can't reach
|
|
493
|
+
into rummy/main's internals via direct import.
|
|
494
|
+
|
|
495
|
+
```js
|
|
496
|
+
const { match, search, replace, generatePatch } = core.hooks.hedberg;
|
|
469
497
|
```
|
|
470
498
|
|
|
471
499
|
| Method | Purpose |
|
|
472
500
|
|--------|---------|
|
|
473
501
|
| `match(pattern, string)` | Full-string pattern match (glob, regex, literal) |
|
|
474
502
|
| `search(pattern, string)` | Substring search |
|
|
475
|
-
| `replace(body, search, replacement
|
|
476
|
-
| `parseSed(input)` | Parse sed syntax (any delimiter) |
|
|
477
|
-
| `parseEdits(content)` | Detect edit format (merge conflict, udiff, sed) |
|
|
503
|
+
| `replace(body, search, replacement)` | Fuzzy literal replacement (whitespace-tolerant) |
|
|
478
504
|
| `generatePatch(path, old, new)` | Generate unified diff |
|
|
479
505
|
|
|
506
|
+
Edit-shape parsing for `<set>` bodies (the `<<:::IDENT...:::IDENT`
|
|
507
|
+
marker family — see SPEC.md "Edit Syntax") lives in
|
|
508
|
+
`src/lib/hedberg/marker.js` and is invoked by the XmlParser at
|
|
509
|
+
`<set>` resolution time. It's not on `core.hooks.hedberg` because no
|
|
510
|
+
external plugin needs to re-parse model output.
|
|
511
|
+
|
|
512
|
+
**The split is intentional.** `src/lib/` is for stateless utility
|
|
513
|
+
modules anyone in the project can import. `src/plugins/` is for
|
|
514
|
+
contracts exposed via the hook system. Hedberg is one of the few
|
|
515
|
+
modules that has both shapes — same code, two access paths, one for
|
|
516
|
+
internal consumers and one for cross-package consumers.
|
|
517
|
+
|
|
480
518
|
## Events & Filters {#plugins_events_overview}
|
|
481
519
|
|
|
482
520
|
**Events** are fire-and-forget. All handlers run. Return values ignored.
|
|
@@ -501,6 +539,7 @@ All hooks are async.
|
|
|
501
539
|
| `run.config` | filter | Before run config applied |
|
|
502
540
|
| `run.progress` | event | Transient turn activity (`thinking` / `processing` / `retrying`) |
|
|
503
541
|
| `run.state` | event | Turn conclusion, per-command incremental, or terminal run close — full state snapshot (status, history, unknowns, telemetry) |
|
|
542
|
+
| `turn.verdict` | filter | Post-turn decision: continue / abandon / strike. Filter chain — multiple plugins (strike streak, cycle detect, stagnation today; future voters can join) each transform a verdict object. Initial value `{ continue: true }`; final value drives the loop's continue/abandon decision. |
|
|
504
543
|
| `run.step.completed` | event | Turn verdict resolved (post-healer, pre-close) |
|
|
505
544
|
| `loop.completed` | event | Loop exit — fires from `finally`, guaranteed on every exit path |
|
|
506
545
|
| `ask.completed` | event | Ask-mode run finished |
|
|
@@ -510,32 +549,92 @@ All hooks are async.
|
|
|
510
549
|
|
|
511
550
|
### Turn Pipeline {#plugins_turn_pipeline}
|
|
512
551
|
|
|
513
|
-
Hooks fire in this order every turn:
|
|
552
|
+
Hooks fire in this order every turn. Type column legend:
|
|
553
|
+
**event** = fire-and-forget, all handlers run, no return value;
|
|
554
|
+
**filter** = chain transform, ordered by priority, return value carries forward;
|
|
555
|
+
**call** = direct named-method invocation on a specific plugin.
|
|
556
|
+
Exceptions for `call`-shaped hooks are documented under
|
|
557
|
+
[Architectural exceptions](#plugins_architectural_exceptions).
|
|
514
558
|
|
|
515
559
|
| # | Hook | Type | When |
|
|
516
560
|
|---|------|------|------|
|
|
517
561
|
| 1 | `turn.started` | event | Plugins write prompt/instructions entries |
|
|
518
|
-
| 2 | `
|
|
519
|
-
| 3 | `
|
|
520
|
-
| 4 | `assembly.
|
|
521
|
-
| 5 | `
|
|
522
|
-
| 6 | `
|
|
523
|
-
| 7 | `llm.
|
|
524
|
-
| 8 | `llm.
|
|
525
|
-
| 9 |
|
|
526
|
-
| 10 | `
|
|
527
|
-
| 11 | `
|
|
528
|
-
| 12 |
|
|
562
|
+
| 2 | `instructions.resolveSystemPrompt` | call ⚠ | System prompt assembly — single-owner exception (cache stability) |
|
|
563
|
+
| 3 | `context.materialized` | event | turn_context populated from v_model_context |
|
|
564
|
+
| 4 | `assembly.system` | filter | Build system message from entries (called from inside `materializeContext`) |
|
|
565
|
+
| 5 | `assembly.user` | filter | Build user message (prompt plugin adds `<prompt tokensFree tokenUsage>`) |
|
|
566
|
+
| 6 | `turn.beforeDispatch` | filter | Measure assembled tokens; if over and turn 1, demote prompt, re-materialize, re-check; still over → 413. Filter chain on the dispatch packet `{ messages, rows, contextSize, lastPromptTokens, assembledTokens, ok, overflow }`. Budget participates here; future plugins may trim, re-order, or annotate via the same surface. `ok=false` short-circuits dispatch. |
|
|
567
|
+
| 7 | `llm.messages` | filter | Transform messages before LLM call |
|
|
568
|
+
| 8 | `llm.request.started` | event | LLM call about to fire |
|
|
569
|
+
| 9 | (LLM completion call) | — | Direct provider call. Errors caught: ContextExceededError → 413; TimeoutError/AbortError → 504 strike (unless drain). |
|
|
570
|
+
| 10 | `llm.response` | filter | Transform raw LLM response |
|
|
571
|
+
| 11 | `llm.request.completed` | event | LLM call finished |
|
|
572
|
+
| 12 | (XML parse + parser-warning emission) | — | Synchronous; warnings emitted via `error.log` with `soft: true` — recoverable, no strike |
|
|
573
|
+
| 13 | `llm.reasoning` | filter | Layer plugin reasoning contributions onto API-provided seed (used by `<think>` plugin to merge content-channel thinking into reasoning_content) |
|
|
574
|
+
| 14 | `turn.response` | event | Plugins write audit entries (telemetry) |
|
|
575
|
+
| 15 | `entry.recording` | filter | Per command, during `#record()`. Returning an entry with `state: "failed"` (or `"cancelled"`) rejects it. |
|
|
576
|
+
| 16 | Per recorded entry (sequential, abort-on-failure): | | |
|
|
529
577
|
| | `tool.before` | event | Before handler dispatch |
|
|
530
|
-
| | `tools.dispatch` |
|
|
578
|
+
| | `tools.dispatch` | call (keyed) | Scheme's registered handler runs. Keyed dispatch is principled — multi-plugin contract by scheme name. |
|
|
531
579
|
| | `tool.after` | event | Handler finished |
|
|
532
580
|
| | `entry.created` | event | Entry written to store |
|
|
533
581
|
| | `run.state` | event | Incremental state push to connected clients |
|
|
534
582
|
| | `proposal.prepare` | event | This entry's dispatch may have created proposals (e.g. set → 202 revisions) |
|
|
535
583
|
| | `proposal.pending` | event | Per each materialized proposal — client is notified, dispatch awaits resolution |
|
|
536
|
-
|
|
|
537
|
-
|
|
|
538
|
-
|
|
|
584
|
+
| 17 | `turn.dispatched` | event | Post-dispatch cleanup. Budget subscribes for Turn Demotion (visibility=summarized on visible rows that overflow) + 413 `error://` emission via `hooks.error.log.emit`. Future plugins may subscribe for any post-dispatch concern. |
|
|
585
|
+
| 18 | `update.resolve` | call ⚠ | Update plugin classifies this turn's `<update>` (terminal/continuation, override-to-continuation if actions failed, heal from raw content if missing). Single-owner exception — synchronous return value (`{ summaryText, updateText }`) is load-bearing. |
|
|
586
|
+
| 19 | `turn.completed` | event | Turn fully resolved with final status |
|
|
587
|
+
|
|
588
|
+
**Legend:** ⚠ = load-bearing exception (kept by design, see below); ✗ = refactor candidate (ceremonial coupling).
|
|
589
|
+
|
|
590
|
+
### Architectural exceptions {#plugins_architectural_exceptions}
|
|
591
|
+
|
|
592
|
+
The plugin contract aims for **events for emit, filters for transform,
|
|
593
|
+
keyed dispatch for multi-plugin lookups by category**. Five points
|
|
594
|
+
intentionally deviate. They're documented here so they aren't
|
|
595
|
+
mistaken for ceremony and "fixed" in a way that breaks the
|
|
596
|
+
load-bearing reason.
|
|
597
|
+
|
|
598
|
+
**1. `instructions.resolveSystemPrompt(rummy)` — single-owner, cache-stable.**
|
|
599
|
+
The system prompt is deliberately not a filter chain. Multiple
|
|
600
|
+
participants would defeat prefix-cache reasoning ("Static base in
|
|
601
|
+
system, phase-specific in user," see AGENTS.md instruction
|
|
602
|
+
discipline). One plugin owns the surface; direct call enforces it.
|
|
603
|
+
|
|
604
|
+
**2. `update.resolve({ recorded, ... })` — single-owner with
|
|
605
|
+
synchronous return value.** Caller (`TurnExecutor`) needs
|
|
606
|
+
`{ summaryText, updateText }` back to drive the resolve callback.
|
|
607
|
+
Events emit but don't return; only the update plugin understands
|
|
608
|
+
terminal-vs-continuation status semantics. Filter-chain shape
|
|
609
|
+
would only have one element (still update), so the chain would be
|
|
610
|
+
ceremony.
|
|
611
|
+
|
|
612
|
+
**3. Static utility imports across plugins
|
|
613
|
+
(`Entries.scheme`, `Entries.normalizePath`, `countTokens`,
|
|
614
|
+
`stateToStatus`).** Pure stateless utilities. Routing through
|
|
615
|
+
hooks adds a ceremony layer for zero capability gain — these aren't
|
|
616
|
+
extension points; they're canonical implementations.
|
|
617
|
+
|
|
618
|
+
**4. Hedberg lib + thin plugin shim.** The library lives at
|
|
619
|
+
`src/lib/hedberg/` (pattern matching, sed parsing, merge handling).
|
|
620
|
+
A thin plugin shim at `src/plugins/hedberg/hedberg.js` re-exposes
|
|
621
|
+
the same surface on `core.hooks.hedberg` for external plugins
|
|
622
|
+
(rummy.repo, rummy.web) that can't reach into rummy/main's
|
|
623
|
+
internals via direct import. Internal plugins use direct imports
|
|
624
|
+
from `src/lib/hedberg/`; external plugins use the hook namespace.
|
|
625
|
+
See [Hedberg](#plugins_hedberg) for the API table.
|
|
626
|
+
|
|
627
|
+
**5. Transport plugins (`cli`, `rpc`).** These are *interface*
|
|
628
|
+
plugins, not action plugins. Their job is to bridge external
|
|
629
|
+
interfaces (stdin/stdout, WebSocket) to the agent. Direct imports
|
|
630
|
+
of `ProjectAgent` / `RummyContext` are what makes them transports;
|
|
631
|
+
fitting them into the action-plugin shape would require running
|
|
632
|
+
the agent over a back-channel to itself.
|
|
633
|
+
|
|
634
|
+
**Anything else that looks like a direct named call into a plugin
|
|
635
|
+
is a seam, not an exception** — see the ✗-marked entries in the
|
|
636
|
+
Turn Pipeline above. Refactor surface tracked in AGENTS.md "Now"
|
|
637
|
+
under Phase 2.
|
|
539
638
|
|
|
540
639
|
`entry.changed` fires asynchronously from mutation points — not
|
|
541
640
|
pipeline-ordered. Subscribe when you need to react to any entry
|
|
@@ -566,22 +665,41 @@ update, visibility change, state change, attribute update. Payload:
|
|
|
566
665
|
|
|
567
666
|
| Hook | Type | When |
|
|
568
667
|
|------|------|------|
|
|
569
|
-
| `
|
|
570
|
-
| `
|
|
668
|
+
| `turn.beforeDispatch` filter | subscriber | Pre-LLM ceiling check on the dispatch packet. On first-turn 413 → Prompt Demotion + re-check; sets `ok=false` + `overflow` to short-circuit dispatch. |
|
|
669
|
+
| `turn.dispatched` event | subscriber | Post-dispatch re-check. On 413 → Turn Demotion + 413 `error://` entry via `hooks.error.log.emit`. |
|
|
670
|
+
| `assembly.user` filter | subscriber | Renders `<budget>` table into the user message. |
|
|
571
671
|
|
|
572
672
|
The budget plugin measures tokens on the assembled messages — the
|
|
573
673
|
actual content being sent to the LLM. No estimates at the ceiling,
|
|
574
674
|
no SQL token sums. The assembled message IS the measurement. When
|
|
575
|
-
turn 2+ information is available,
|
|
576
|
-
API-reported token count (`turns.context_tokens` from the
|
|
577
|
-
turn) over re-measuring the assembled string.
|
|
675
|
+
turn 2+ information is available, the pre-LLM check prefers the
|
|
676
|
+
actual API-reported token count (`turns.context_tokens` from the
|
|
677
|
+
prior turn) over re-measuring the assembled string.
|
|
678
|
+
|
|
679
|
+
**Use of the assembler.** Budget calls the context assembler in two
|
|
680
|
+
spots — these are projections, not orchestration leaks:
|
|
681
|
+
|
|
682
|
+
- **Pre-LLM Prompt Demotion (`turn.beforeDispatch`)** — when the
|
|
683
|
+
first-turn packet overflows, budget demotes the prompt entry in
|
|
684
|
+
the DB, swaps `body` from `vBody` to `sBody` on the local prompt
|
|
685
|
+
row, and re-runs `ContextAssembler.assembleFromTurnContext` on
|
|
686
|
+
the modified rows. No `materializeContext` round-trip — the row
|
|
687
|
+
already carries both projections.
|
|
688
|
+
- **Post-dispatch projection (`turn.dispatched`)** — budget re-runs
|
|
689
|
+
`materializeContext` to project the *next* turn's packet
|
|
690
|
+
(entries written during dispatch need projection through
|
|
691
|
+
`hooks.tools.view`). If predicted next packet overflows, budget
|
|
692
|
+
demotes now so next turn's enforce isn't stuck with only the
|
|
693
|
+
prompt-demotion lever. Cost projection is the budget plugin's
|
|
694
|
+
job; the assembler is the measurement instrument.
|
|
578
695
|
|
|
579
696
|
**DB tokens vs assembled tokens:** The `tokens` column on `entries`
|
|
580
|
-
is strictly for DISPLAY — showing token costs
|
|
581
|
-
the model can reason about entry
|
|
582
|
-
decisions. Budget math uses only
|
|
583
|
-
These are two separate numbers that
|
|
584
|
-
|
|
697
|
+
is strictly for DISPLAY — showing token costs on entry tags in
|
|
698
|
+
`<summary>` / `<visible>` so the model can reason about entry
|
|
699
|
+
sizes. It is NEVER used for budget decisions. Budget math uses only
|
|
700
|
+
assembled message token counts. These are two separate numbers that
|
|
701
|
+
must never be conflated. See
|
|
702
|
+
[budget_enforcement](SPEC.md#budget_enforcement) for the three-measure table.
|
|
585
703
|
|
|
586
704
|
### Client Notifications {#plugins_client_notifications}
|
|
587
705
|
|
|
@@ -611,7 +729,7 @@ Every entry follows the same lifecycle regardless of origin:
|
|
|
611
729
|
|
|
612
730
|
Entries at `visibility = 'archived'` skip steps 4–6 (invisible to
|
|
613
731
|
model, discoverable via pattern search). Entries at `visibility =
|
|
614
|
-
'summarized'` render with `attributes.
|
|
732
|
+
'summarized'` render with `attributes.tags` (model-authored keyword
|
|
615
733
|
description) prepended above the plugin's `summarized` view output —
|
|
616
734
|
the body is hidden; promoting with `<get>` brings it back.
|
|
617
735
|
|
|
@@ -631,7 +749,7 @@ the projected body — they do NOT re-check `entry.visibility`.
|
|
|
631
749
|
| `file` (bare paths) | data | `entry.body` | `""` | Same as known |
|
|
632
750
|
|
|
633
751
|
Plugins providing only a `visible` hook fall back to
|
|
634
|
-
`attributes.
|
|
752
|
+
`attributes.tags` (model-authored keyword description) at summarized;
|
|
635
753
|
the renderer inserts it automatically. Plugins providing neither
|
|
636
754
|
default to empty body — the tag still renders with its attributes so
|
|
637
755
|
the model can pattern-match the path.
|
|
@@ -653,7 +771,7 @@ state: "proposed" (user decision pending)
|
|
|
653
771
|
|
|
654
772
|
1. On dispatch, create a **proposal entry** at `{scheme}://turn_N/{slug}`
|
|
655
773
|
with `state: "proposed"`, category=logging. Body empty;
|
|
656
|
-
`
|
|
774
|
+
`tags=command` attr.
|
|
657
775
|
2. On user accept (client sends `set { state: "resolved" }` on the
|
|
658
776
|
proposal path), `AgentLoop.resolve()` transitions the proposal
|
|
659
777
|
entry to `state: "resolved"` (it becomes the **log entry**) and
|
|
@@ -690,31 +808,31 @@ pure RPC plumbing shared across all streaming producers.
|
|
|
690
808
|
|--------|------|-------------|
|
|
691
809
|
| `get` | Core tool | Load file/entry into context |
|
|
692
810
|
| `set` | Core tool | Edit file/entry, visibility control |
|
|
693
|
-
| `known` | Core tool + Assembly | Save knowledge
|
|
811
|
+
| `known` | Core tool + Assembly | Save knowledge; renders `<summary>` (priority 50) and `<visible>` (priority 75) for all category=data entries |
|
|
694
812
|
| `rm` | Core tool | Delete permanently |
|
|
695
813
|
| `mv` | Core tool | Move entry |
|
|
696
814
|
| `cp` | Core tool | Copy entry |
|
|
697
815
|
| `sh` | Core tool | Shell command (act mode only). Streaming producer — see [plugins_streaming_entries](#plugins_streaming_entries) |
|
|
698
|
-
| `env` | Core tool | Exploratory command. Streaming producer — see
|
|
816
|
+
| `env` | Core tool | Exploratory command. Streaming producer — see [plugins_streaming_entries](#plugins_streaming_entries) |
|
|
699
817
|
| `stream` | Internal | Generic streaming-entry RPC (`stream`, `stream/completed`, `stream/aborted`, `stream/cancel`) for sh/env and future producers |
|
|
700
818
|
| `ask_user` | Core tool | Ask the user |
|
|
701
819
|
| `search` | Core tool | Web search (via external plugin) |
|
|
702
820
|
| `update` | Structural | Status report + lifecycle signal. `status="200\|204\|422"` terminates; `status="102"` continues. Exposes `hooks.update.resolve` for TurnExecutor. |
|
|
703
|
-
| `unknown` | Structural + Assembly | Register unknowns, render `<unknowns>` |
|
|
704
|
-
| `
|
|
705
|
-
| `
|
|
706
|
-
| `prompt` | Assembly | Render `<prompt mode="ask\|act" tokensFree="N" tokenUsage="M">` tag |
|
|
821
|
+
| `unknown` | Structural + Assembly | Register unknowns, render `<unknowns>` (priority 150) |
|
|
822
|
+
| `log` | Assembly | Render `<log>` (priority 100) — all logging-category entries plus pre-latest prompts |
|
|
823
|
+
| `prompt` | Assembly | Render `<prompt tokensFree="N" tokenUsage="M">` (priority 30, front of user message) |
|
|
707
824
|
| `hedberg` | Utility | Pattern matching, interpretation, normalization |
|
|
708
|
-
| `instructions` | Internal |
|
|
825
|
+
| `instructions` | Internal | System prompt assembly (`instructions-system.md` + `[%TOOLS%]` + `[%TOOLDOCS%]` + persona); renders `<instructions>` (priority 165) from `instructions-user.md`; exposes `hooks.instructions.resolveSystemPrompt` |
|
|
709
826
|
| `file` | Internal | File entry projections and constraints (`scheme IS NULL`) |
|
|
710
827
|
| `rpc` | Internal | RPC method registration + tool-fallback dispatch |
|
|
711
828
|
| `telemetry` | Internal | Audit entries, usage stats, reasoning_content |
|
|
712
|
-
| `budget` | Internal | Context ceiling enforcement: Prompt Demotion (pre-LLM first-turn 413) + Turn Demotion (post-dispatch).
|
|
829
|
+
| `budget` | Internal | Context ceiling enforcement: Prompt Demotion (pre-LLM first-turn 413) + Turn Demotion (post-dispatch). Subscribes to `turn.beforeDispatch` (filter) + `turn.dispatched` (event) + `assembly.user` (filter, priority 175 — renders `<budget>`). |
|
|
713
830
|
| `policy` | Internal | Ask-mode per-invocation rejections via `entry.recording` filter |
|
|
714
831
|
| `error` | Internal | `error.log` hook → `error://` entries |
|
|
715
832
|
| `think` | Tool | Private reasoning tag; contributes to `reasoning_content` via the `llm.reasoning` filter |
|
|
716
833
|
| `openai` / `ollama` / `xai` / `openrouter` | LLM provider | Register with `hooks.llm.providers`; handle `{prefix}/...` model aliases. Silently inert if their env isn't configured. |
|
|
717
|
-
| `persona`
|
|
834
|
+
| `persona` | Internal | Renders the persona body inside the system prompt; default at `persona/default.md`. Run-attribute `persona` overrides per run (1:1, immutable for the run's lifetime). |
|
|
835
|
+
| `skill` | Internal | `<skill path="..."/>` tag handler + `skill://` scheme. Walks file/folder/`.zip` (local or URL); registers content under `skill://<name>/...`. |
|
|
718
836
|
|
|
719
837
|
## External Plugins
|
|
720
838
|
|
|
@@ -806,9 +924,7 @@ dedicated verbs with 1:1 plugin-API equivalents.
|
|
|
806
924
|
| `file/constraint` | `{ pattern, visibility }` | Project-scoped: set overlay. `visibility ∈ {active, readonly, ignore}`. Patterns can be globs. `readonly` is enforced on `set://` accept in `AgentLoop.resolve()`. |
|
|
807
925
|
| `file/drop` | `{ pattern }` | Project-scoped: remove overlay row. |
|
|
808
926
|
| `getConstraints` | — | Project-scoped: returns `[{pattern, visibility}]`. |
|
|
809
|
-
| `
|
|
810
|
-
| `persona/set` / `listPersonas` | | Persona management |
|
|
811
|
-
| `stream` / `stream/completed` / `stream/aborted` / `stream/cancel` | | Streaming RPC (§8.1) |
|
|
927
|
+
| `stream` / `stream/completed` / `stream/aborted` / `stream/cancel` | | Streaming RPC — see [plugins_streaming_entries](#plugins_streaming_entries) |
|
|
812
928
|
|
|
813
929
|
**Why file constraints are typed RPCs and not `set` entries:** they
|
|
814
930
|
are project-scoped (no `run`), persist across runs, and `readonly`
|
package/README.md
CHANGED
|
@@ -1,63 +1,69 @@
|
|
|
1
|
-
# RUMMY:
|
|
1
|
+
# RUMMY: The General-Purpose Agent Kernel
|
|
2
2
|
|
|
3
|
-
Rummy is
|
|
3
|
+
Rummy is a headless, metacognitive relational architecture for LLM agents. It is designed to be integrated into real-world workflows—from IDEs and CLI tools to autonomous research pipelines—where project state is complex and accuracy is non-negotiable.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
While traditional agents "thrash" and fail under the weight of linear chat history, Rummy treats the LLM as a **program** executing on a **managed memory substrate**. This "Virtual Memory" architecture ensures that Rummy remains reliable in sessions that span hundreds of turns and tens of thousands of files.
|
|
6
|
+
|
|
7
|
+
## The Architecture: Virtual Memory for Tokens
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Rummy provides the memory hierarchy necessary to maintain high-fidelity reasoning over unlimited-turn sessions. This is not a benchmarking "harness," but a production-grade Operating System for AI agency:
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
* **L1 Cache (`visible`):** High-fidelity, character-perfect context. This is the active "Working Set" the model is reasoning with right now.
|
|
12
|
+
* **RAM (`summarized`):** Folksonomic metadata and searchable indices. This allows the model to know *what* information exists and how to address it without consuming the L1 token budget.
|
|
13
|
+
* **The Disk (`archived`):** Persistent SQLite storage. A relational substrate where every historical finding, raw source document, and prior tool result is safely indexed and searchable, ready to be "paged" back into Cache on demand.
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
## Key Features
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
### Headless & RPC-First
|
|
18
|
+
Rummy is a **headless service**. It exposes a JSON-RPC over WebSocket interface, allowing it to be embedded into any client (e.g., [rummy.nvim](https://github.com/possumtech/rummy.nvim)). The server manages the project state and the "Kernel" loop, while the client drives the UI and handles local proposal resolution.
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
### Extensible Plugin Architecture
|
|
21
|
+
Rummy is built for integration. Every `<tag>` the model sees is a plugin. Every URI scheme (`known://`, `unknown://`, `sh://`) is registered by its owner. Developers can drop custom logic into `src/plugins/` to add new tools, filters, or event hooks. See [PLUGINS.md](PLUGINS.md) for details.
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
### The Six Primitives
|
|
24
|
+
Every operation in Rummy reduces to one of six verbs over a single entry contract: `set` / `get` / `rm` / `mv` / `cp` / `update`. Tools (`<sh>`, `<search>`, `<known>`, `<unknown>`, …) are plugins that compose these primitives. Three actor surfaces — model XML tags, plugin RummyContext methods, JSON-RPC client calls — speak the same grammar at the store layer.
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
### The Model Owns Its Context
|
|
27
|
+
Visibility (`visible` / `summarized` / `archived`) is the model's exclusive lever. The engine never silently mutates an entry's visibility behind the model's back; the only enforcements that touch visibility (Turn Demotion at budget overflow, Prompt Demotion at context-exceeded) surface through `error://` so the model sees the trigger. No chat-waterfall horizon, no auto-prune — the model controls what it sees and what it doesn't.
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
### Apophatic Reasoning (The Rumsfeld Loop)
|
|
30
|
+
Rummy turns "Not Knowing" into a formal state to be processed. By mapping **Unknowns** (`unknown://`) into verified **Knowns** (`known://`), Rummy provides a transparent, auditable trail of how the agent arrived at its conclusion.
|
|
22
31
|
|
|
23
32
|
## Installation
|
|
24
33
|
|
|
25
|
-
Rummy loads configuration from exactly **one** directory per
|
|
26
|
-
invocation:
|
|
34
|
+
Rummy loads configuration from exactly **one** directory per invocation:
|
|
27
35
|
|
|
28
|
-
1. The
|
|
36
|
+
1. The current working directory (if it contains `.env.example`).
|
|
29
37
|
2. Otherwise, `${RUMMY_HOME}` (default `~/.rummy`).
|
|
30
38
|
|
|
31
|
-
`npm i -g @possumtech/rummy` runs a postinstall that seeds
|
|
32
|
-
`${RUMMY_HOME}/.env.example` from the package defaults, so the
|
|
33
|
-
out-of-the-box path works:
|
|
34
|
-
|
|
35
39
|
```bash
|
|
36
|
-
#
|
|
40
|
+
# Set your RUMMY_HOME
|
|
37
41
|
export RUMMY_HOME=~/.rummy
|
|
38
42
|
|
|
43
|
+
# Install globally
|
|
39
44
|
npm i -g @possumtech/rummy
|
|
40
|
-
|
|
45
|
+
|
|
46
|
+
# Configure your environment
|
|
47
|
+
$EDITOR ~/.rummy/.env.example # set model aliases and keys
|
|
41
48
|
rummy
|
|
42
49
|
```
|
|
43
50
|
|
|
44
|
-
Within the chosen directory, `.env.example` is the baseline and `.env`
|
|
45
|
-
(if present) overrides. Shell env beats both. The package's own
|
|
46
|
-
`.env.example` is **never** loaded at runtime — if neither the cwd nor
|
|
47
|
-
`${RUMMY_HOME}` has an `.env.example`, rummy crashes at startup. No
|
|
48
|
-
silent defaults.
|
|
49
|
-
|
|
50
51
|
## Usage
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
Start the service and connect your preferred client. The server defaults to port `3044`.
|
|
54
|
+
|
|
55
|
+
* **Official Client:** [rummy.nvim](https://github.com/possumtech/rummy.nvim) (Neovim interface)
|
|
56
|
+
* **In-process CLI:** `rummy-cli` (one-shot ask/act invocations against a project; see `src/plugins/cli/`)
|
|
57
|
+
* **Diagnostic Suite:** `test/tbench/` and `test/programbench/` (autonomous diagnostic and benchmarking harnesses)
|
|
53
58
|
|
|
54
59
|
## Documentation
|
|
55
60
|
|
|
56
61
|
| Document | Contents |
|
|
57
62
|
|----------|----------|
|
|
58
|
-
| [SPEC.md](SPEC.md) |
|
|
59
|
-
| [PLUGINS.md](PLUGINS.md) |
|
|
60
|
-
| [
|
|
63
|
+
| [SPEC.md](SPEC.md) | Technical Specification: K/V store, packet structure, dispatch path, and lifecycle contracts. |
|
|
64
|
+
| [PLUGINS.md](PLUGINS.md) | Extensibility: Hook registry, event filtering, and custom scheme registration. |
|
|
65
|
+
| [src/plugins/](src/plugins/**/README.md) | **Plugin Reference:** Internal documentation for each scheme and toolset. |
|
|
66
|
+
| [AGENTS.md](AGENTS.md) | Project roadmap, planning history, and architectural lessons. |
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
The
|
|
68
|
+
---
|
|
69
|
+
*Rummy: The Managed Operating System for AI Agency.*
|