@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.
Files changed (119) hide show
  1. package/.env.example +12 -3
  2. package/EXCEPTIONS.md +46 -0
  3. package/PLUGINS.md +454 -197
  4. package/SPEC.md +284 -93
  5. package/migrations/001_initial_schema.sql +57 -70
  6. package/package.json +16 -10
  7. package/service.js +1 -1
  8. package/src/agent/AgentLoop.js +254 -70
  9. package/src/agent/ContextAssembler.js +18 -4
  10. package/src/agent/KnownStore.js +156 -23
  11. package/src/agent/ProjectAgent.js +5 -4
  12. package/src/agent/ResponseHealer.js +21 -1
  13. package/src/agent/TurnExecutor.js +393 -115
  14. package/src/agent/XmlParser.js +92 -39
  15. package/src/agent/known_checks.sql +5 -4
  16. package/src/agent/known_queries.sql +4 -3
  17. package/src/agent/known_store.sql +45 -15
  18. package/src/agent/loops.sql +63 -0
  19. package/src/agent/runs.sql +7 -7
  20. package/src/agent/schemes.sql +5 -2
  21. package/src/agent/tokens.js +6 -21
  22. package/src/agent/turns.sql +13 -4
  23. package/src/hooks/Hooks.js +18 -0
  24. package/src/hooks/PluginContext.js +14 -10
  25. package/src/hooks/RummyContext.js +30 -10
  26. package/src/hooks/ToolRegistry.js +83 -19
  27. package/src/llm/LlmProvider.js +27 -8
  28. package/src/llm/OpenAiClient.js +20 -0
  29. package/src/llm/OpenRouterClient.js +24 -2
  30. package/src/llm/XaiClient.js +47 -2
  31. package/src/plugins/ask_user/README.md +4 -4
  32. package/src/plugins/ask_user/ask_user.js +8 -7
  33. package/src/plugins/ask_user/ask_userDoc.js +29 -0
  34. package/src/plugins/budget/BudgetGuard.js +74 -0
  35. package/src/plugins/budget/README.md +43 -0
  36. package/src/plugins/budget/budget.js +79 -0
  37. package/src/plugins/cp/README.md +5 -4
  38. package/src/plugins/cp/cp.js +16 -12
  39. package/src/plugins/cp/cpDoc.js +29 -0
  40. package/src/plugins/current/README.md +4 -4
  41. package/src/plugins/current/current.js +12 -10
  42. package/src/plugins/engine/engine.sql +5 -10
  43. package/src/plugins/engine/turn_context.sql +13 -13
  44. package/src/plugins/env/README.md +3 -4
  45. package/src/plugins/env/env.js +8 -7
  46. package/src/plugins/env/envDoc.js +29 -0
  47. package/src/plugins/file/README.md +9 -12
  48. package/src/plugins/file/file.js +34 -45
  49. package/src/plugins/get/README.md +2 -2
  50. package/src/plugins/get/get.js +28 -11
  51. package/src/plugins/get/getDoc.js +41 -0
  52. package/src/plugins/hedberg/docs.md +0 -9
  53. package/src/plugins/hedberg/hedberg.js +4 -6
  54. package/src/plugins/hedberg/matcher.js +1 -1
  55. package/src/plugins/hedberg/normalize.js +28 -0
  56. package/src/plugins/hedberg/patterns.js +31 -33
  57. package/src/plugins/hedberg/sed.js +17 -10
  58. package/src/plugins/helpers.js +2 -2
  59. package/src/plugins/index.js +93 -28
  60. package/src/plugins/instructions/README.md +6 -2
  61. package/src/plugins/instructions/instructions.js +21 -5
  62. package/src/plugins/instructions/preamble.md +9 -5
  63. package/src/plugins/known/README.md +10 -7
  64. package/src/plugins/known/known.js +33 -23
  65. package/src/plugins/known/knownDoc.js +33 -0
  66. package/src/plugins/mv/README.md +5 -4
  67. package/src/plugins/mv/mv.js +16 -12
  68. package/src/plugins/mv/mvDoc.js +31 -0
  69. package/src/plugins/persona/persona.js +78 -0
  70. package/src/plugins/previous/README.md +2 -2
  71. package/src/plugins/previous/previous.js +12 -8
  72. package/src/plugins/progress/progress.js +44 -12
  73. package/src/plugins/prompt/README.md +5 -5
  74. package/src/plugins/prompt/prompt.js +23 -19
  75. package/src/plugins/rm/README.md +4 -4
  76. package/src/plugins/rm/rm.js +29 -12
  77. package/src/plugins/rm/rmDoc.js +30 -0
  78. package/src/plugins/rpc/README.md +15 -28
  79. package/src/plugins/rpc/rpc.js +63 -107
  80. package/src/plugins/set/README.md +13 -12
  81. package/src/plugins/set/set.js +82 -21
  82. package/src/plugins/set/setDoc.js +45 -0
  83. package/src/plugins/sh/README.md +4 -4
  84. package/src/plugins/sh/sh.js +8 -7
  85. package/src/plugins/sh/shDoc.js +29 -0
  86. package/src/plugins/{skills/skills.js → skill/skill.js} +12 -54
  87. package/src/plugins/summarize/README.md +6 -5
  88. package/src/plugins/summarize/summarize.js +7 -6
  89. package/src/plugins/summarize/summarizeDoc.js +33 -0
  90. package/src/plugins/telemetry/telemetry.js +20 -8
  91. package/src/plugins/think/README.md +20 -0
  92. package/src/plugins/think/think.js +5 -0
  93. package/src/plugins/unknown/README.md +5 -5
  94. package/src/plugins/unknown/unknown.js +11 -8
  95. package/src/plugins/unknown/unknownDoc.js +31 -0
  96. package/src/plugins/update/README.md +3 -8
  97. package/src/plugins/update/update.js +7 -6
  98. package/src/plugins/update/updateDoc.js +33 -0
  99. package/src/server/ClientConnection.js +3 -5
  100. package/src/server/RpcRegistry.js +52 -4
  101. package/src/sql/v_model_context.sql +31 -39
  102. package/src/sql/v_run_log.sql +3 -3
  103. package/src/agent/prompt_queue.sql +0 -39
  104. package/src/plugins/ask_user/docs.md +0 -2
  105. package/src/plugins/cp/docs.md +0 -2
  106. package/src/plugins/env/docs.md +0 -2
  107. package/src/plugins/get/docs.md +0 -6
  108. package/src/plugins/known/docs.md +0 -3
  109. package/src/plugins/mv/docs.md +0 -2
  110. package/src/plugins/rm/docs.md +0 -4
  111. package/src/plugins/set/docs.md +0 -4
  112. package/src/plugins/sh/docs.md +0 -2
  113. package/src/plugins/skills/README.md +0 -25
  114. package/src/plugins/store/README.md +0 -20
  115. package/src/plugins/store/docs.md +0 -5
  116. package/src/plugins/store/store.js +0 -52
  117. package/src/plugins/summarize/docs.md +0 -4
  118. package/src/plugins/unknown/docs.md +0 -5
  119. package/src/plugins/update/docs.md +0 -4
@@ -1,34 +1,31 @@
1
1
  -- INIT: create_v_model_context
2
2
  CREATE VIEW IF NOT EXISTS v_model_context AS
3
3
  WITH
4
- classified AS (
4
+ visible AS (
5
5
  SELECT
6
6
  ke.run_id
7
7
  , ke.id
8
8
  , ke.path
9
9
  , ke.body
10
10
  , ke.scheme
11
- , ke.state
11
+ , ke.status
12
+ , ke.fidelity
12
13
  , ke.turn
13
14
  , ke.updated_at
14
15
  , ke.attributes
15
16
  , ke.tokens AS tokens_full
16
17
  , CASE
17
- -- Proposed entries hidden until resolved
18
- WHEN ke.state = 'proposed' THEN NULL
18
+ -- Archived entries not in context
19
+ WHEN ke.fidelity = 'archive' THEN NULL
20
+ -- 202 Accepted (proposed) hidden until resolved
21
+ WHEN ke.status = 202 THEN NULL
19
22
  -- Audit schemes (model_visible = 0) hidden
20
23
  WHEN s.model_visible = 0 THEN NULL
21
- -- State IS fidelity for visible entries
22
- WHEN ke.state IN ('full', 'summary', 'index') THEN ke.state
23
- -- Stored entries hidden (retrievable via <read>)
24
- WHEN ke.state = 'stored' THEN NULL
25
- -- Result/structural states are visible at full fidelity
26
- WHEN ke.state IN ('pass', 'error', 'warn', 'pattern', 'read', 'info', 'summary') THEN 'full'
27
- ELSE NULL
28
- END AS fidelity
24
+ -- Everything else visible at its fidelity
25
+ ELSE ke.fidelity
26
+ END AS visible_fidelity
29
27
  FROM known_entries AS ke
30
28
  JOIN schemes AS s ON s.name = COALESCE(ke.scheme, 'file')
31
- WHERE ke.state NOT IN ('proposed')
32
29
  ),
33
30
  projected AS (
34
31
  SELECT
@@ -36,38 +33,37 @@ projected AS (
36
33
  , id
37
34
  , path
38
35
  , scheme
39
- , state
40
- , fidelity
36
+ , status
37
+ , visible_fidelity AS fidelity
41
38
  , turn
42
39
  , updated_at
43
40
  , attributes
44
41
  , CASE
45
- WHEN fidelity IN ('full', 'summary') THEN body
42
+ WHEN visible_fidelity IN ('full', 'summary') THEN body
46
43
  ELSE ''
47
44
  END AS body
45
+ -- Four roles: data, logging, unknown, prompt.
46
+ -- These are structural — see PluginContext.CATEGORIES.
47
+ -- 'tool' is internal (model_visible=0 in practice).
48
+ -- Default is 'logging' — plugins opt into 'data' explicitly.
48
49
  , CASE
49
- WHEN scheme IS NULL AND state IN ('full', 'summary') THEN 'file'
50
- WHEN scheme IS NULL THEN 'file_index'
51
- WHEN scheme IN ('http', 'https') AND state IN ('full', 'summary') THEN 'file'
52
- WHEN scheme IN ('http', 'https') THEN 'file_index'
53
- WHEN scheme IN ('known', 'skill') AND state = 'full' THEN 'known'
54
- WHEN scheme IN ('known', 'skill') THEN 'known_index'
50
+ WHEN scheme IS NULL THEN 'data'
51
+ WHEN scheme IN ('http', 'https') THEN 'data'
52
+ WHEN scheme IN ('known', 'skill') THEN 'data'
55
53
  WHEN scheme = 'unknown' THEN 'unknown'
56
- WHEN scheme IN ('ask', 'act', 'progress') THEN 'prompt'
57
- WHEN scheme = 'summarize' THEN 'structural'
58
- WHEN scheme = 'update' THEN 'structural'
54
+ WHEN scheme = 'prompt' THEN 'prompt'
59
55
  WHEN scheme = 'tool' THEN 'tool'
60
- ELSE 'result'
56
+ ELSE 'logging'
61
57
  END AS category
62
- FROM classified
63
- WHERE fidelity IS NOT NULL
58
+ FROM visible
59
+ WHERE visible_fidelity IS NOT NULL
64
60
  )
65
61
  SELECT
66
62
  run_id
67
63
  , path
68
64
  , scheme
69
65
  , fidelity
70
- , state
66
+ , status
71
67
  , body
72
68
  , attributes
73
69
  , category
@@ -77,18 +73,14 @@ SELECT
77
73
  ORDER BY
78
74
  CASE category
79
75
  WHEN 'tool' THEN 1
80
- WHEN 'known' THEN 2
81
- WHEN 'known_index' THEN 2
82
- WHEN 'file_index' THEN 2
83
- WHEN 'file' THEN 2
84
- WHEN 'result' THEN 3
85
- WHEN 'structural' THEN 4
86
- WHEN 'unknown' THEN 5
87
- WHEN 'prompt' THEN 6
88
- ELSE 6
76
+ WHEN 'data' THEN 2
77
+ WHEN 'logging' THEN 3
78
+ WHEN 'unknown' THEN 4
79
+ WHEN 'prompt' THEN 5
80
+ ELSE 5
89
81
  END
90
82
  , CASE scheme WHEN 'skill' THEN 0 ELSE 1 END
91
- , CASE state
83
+ , CASE fidelity
92
84
  WHEN 'index' THEN 0
93
85
  WHEN 'summary' THEN 1
94
86
  ELSE 2
@@ -4,8 +4,8 @@ SELECT
4
4
  ke.run_id
5
5
  , ke.path
6
6
  , ke.body
7
- , ke.state AS status
8
- , COALESCE(ke.scheme, ke.state) AS tool
7
+ , ke.status
8
+ , COALESCE(ke.scheme, 'file') AS tool
9
9
  , COALESCE(
10
10
  json_extract(ke.attributes, '$.command')
11
11
  , json_extract(ke.attributes, '$.file')
@@ -17,7 +17,7 @@ FROM known_entries AS ke
17
17
  JOIN schemes AS s ON s.name = COALESCE(ke.scheme, 'file')
18
18
  WHERE
19
19
  ke.scheme IS NOT NULL
20
- AND ke.state != 'proposed'
20
+ AND ke.status != 202
21
21
  AND s.category NOT IN ('knowledge', 'file')
22
22
  AND ke.scheme NOT IN ('system', 'reasoning', 'model', 'content')
23
23
  ORDER BY ke.id;
@@ -1,39 +0,0 @@
1
- -- PREP: enqueue_prompt
2
- INSERT INTO prompt_queue (run_id, mode, model, prompt, config)
3
- VALUES (:run_id, :mode, :model, :prompt, :config)
4
- RETURNING id;
5
-
6
- -- PREP: claim_next_prompt
7
- UPDATE prompt_queue
8
- SET status = 'active'
9
- WHERE
10
- id = (
11
- SELECT
12
- id
13
- FROM prompt_queue
14
- WHERE run_id = :run_id AND status = 'pending'
15
- ORDER BY id
16
- LIMIT 1
17
- )
18
- RETURNING id, run_id, mode, model, prompt, config;
19
-
20
- -- PREP: complete_prompt
21
- UPDATE prompt_queue
22
- SET status = 'completed', result = :result
23
- WHERE id = :id;
24
-
25
- -- PREP: abort_active_prompt
26
- UPDATE prompt_queue
27
- SET status = 'aborted'
28
- WHERE run_id = :run_id AND status = 'active';
29
-
30
- -- PREP: get_pending_prompts
31
- SELECT id, mode, model, prompt, status, created_at
32
- FROM prompt_queue
33
- WHERE run_id = :run_id AND status IN ('pending', 'active')
34
- ORDER BY id;
35
-
36
- -- PREP: reset_active_prompts
37
- UPDATE prompt_queue
38
- SET status = 'pending'
39
- WHERE status = 'active';
@@ -1,2 +0,0 @@
1
- ## <ask_user question="[Question?]">[option1; option2; ...]</ask_user>
2
- Example: <ask_user question="Which test framework?">Mocha; Jest; Node Native</ask_user>
@@ -1,2 +0,0 @@
1
- ## <cp path="[path/to/origin"]>[path/to/destination]</cp> - Copy a file or entry
2
- Example: <cp path="docs/example.txt">docs/example_copy.txt</cp>
@@ -1,2 +0,0 @@
1
- ## <env>[command]</env> - Run an exploratory shell command
2
- Example: <env>npm --version</env>
@@ -1,6 +0,0 @@
1
- ## <get>[path/to/file]</get> - Load a file or entry into context
2
- Example: <get>docs/example.txt</get>
3
- Example: <get>known://auth_flow</get>
4
- * Entries at state="index" or state="summary" are promoted to full content.
5
- * Use "known://" paths to recall stored information.
6
- * When irrelevant or resolved, use <store/> to remove from context.
@@ -1,3 +0,0 @@
1
- ## <known>[information]</known> - Save knowledge
2
- Example: <known>Donald Rumsfeld was born in 1932</known>
3
- Example: <known path="known://auth">OAuth2 PKCE</known>
@@ -1,2 +0,0 @@
1
- ## <mv path="[path/to/origin"]>[path/to/destination]</mv> - Move a file or entry
2
- Example: <mv path="known://active_user">known://inactive_user</mv>
@@ -1,4 +0,0 @@
1
- ## <rm path="[path/to/file]"/> - Remove a file or entry
2
- Example: <rm path="src/config.js"/>
3
- Example: <rm path="unknown://42"/>
4
- * <rm/> removes the file or entry from context and deletes it PERMANENTLY
@@ -1,4 +0,0 @@
1
- ## <set path="[path/to/file]">[edit]</set> - Edit a file or entry
2
- Example: <set path="src/config.js">s/base_url = http:\/\/localhost/base_url = http:\/\/0.0.0.0/g s/port = 3000/port = 8080/g</set>
3
- * All editing syntaxes supported: s/old/new/, {"search":"old","replace":"new"}, SEARCH/REPLACE blocks
4
- * Do not use <sh/> or <env/> to read, create, update, or delete files or entries
@@ -1,2 +0,0 @@
1
- ## <sh>[command]</sh> - Run a shell command with side effects
2
- Example: <sh>npm install</sh>
@@ -1,25 +0,0 @@
1
- # skills
2
-
3
- Manages skills and personas via RPC methods. Skills are stackable per-run entries; personas are exclusive per-run configuration.
4
-
5
- ## Registration
6
-
7
- - **No tool handler** — registers RPC methods on `hooks.rpc.registry`.
8
-
9
- ## RPC Methods
10
-
11
- ### Skills
12
- - `skill/add` — Load a skill from `config/skills/{name}.md` into the run as a `skill://` entry at full state.
13
- - `skill/remove` — Remove a skill entry from a run.
14
- - `getSkills` — List active skills on a run.
15
- - `listSkills` — List available skill files from disk.
16
-
17
- ### Personas
18
- - `persona/set` — Set persona on a run. Load from `config/personas/{name}.md` by name, pass raw text, or clear by omitting both.
19
- - `listPersonas` — List available persona files from disk.
20
-
21
- ## Behavior
22
-
23
- - Skills stack: multiple skills can be active on a run simultaneously as separate `skill://` entries.
24
- - Personas are exclusive: setting a persona replaces the previous one (stored as a run column, not an entry).
25
- - File paths resolve from `RUMMY_HOME` environment variable.
@@ -1,20 +0,0 @@
1
- # store
2
-
3
- Demotes entries from active context to stored (background) state.
4
-
5
- ## Registration
6
-
7
- - **Tool**: `store`
8
- - **Modes**: ask, act
9
- - **Category**: ask
10
- - **Handler**: Matches entries by pattern, demotes them via `demoteByPattern`, and records the result.
11
-
12
- ## Projection
13
-
14
- Shows `store {path}`.
15
-
16
- ## Behavior
17
-
18
- - Pattern queries (globs or body filters) produce a summary of matched paths.
19
- - Exact path queries report "{path} stored" or "{path} not found".
20
- - Stored entries remain in the database but are excluded from model context.
@@ -1,5 +0,0 @@
1
- ## <store path="[path/to/file]"/> - Store a file or entry
2
- Example: <store path="src/config.js"/>
3
- Example: <store path="unknown://42"/>
4
- * <store/> removes the file or entry from context, but does not delete it
5
- * A stored file or entry can be restored with <get/>
@@ -1,52 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { storePatternResult } from "../helpers.js";
3
-
4
- export default class Store {
5
- #core;
6
-
7
- constructor(core) {
8
- this.#core = core;
9
- core.registerScheme({ validStates: ["full", "stored", "pattern"] });
10
- core.on("handler", this.handler.bind(this));
11
- core.on("full", this.full.bind(this));
12
- core.on("summary", this.summary.bind(this));
13
- const docs = readFileSync(new URL("./docs.md", import.meta.url), "utf8");
14
- core.filter("instructions.toolDocs", async (content) =>
15
- content ? `${content}\n\n${docs}` : docs,
16
- );
17
- }
18
-
19
- async handler(entry, rummy) {
20
- const { entries: store, sequence: turn, runId } = rummy;
21
- const target = entry.attributes.path;
22
- const bodyFilter = entry.attributes.body || null;
23
- const isPattern = bodyFilter || target.includes("*");
24
- const matches = await store.getEntriesByPattern(runId, target, bodyFilter);
25
- await store.demoteByPattern(runId, target, bodyFilter);
26
-
27
- if (isPattern) {
28
- await storePatternResult(
29
- store,
30
- runId,
31
- turn,
32
- "store",
33
- target,
34
- bodyFilter,
35
- matches,
36
- );
37
- } else {
38
- const paths = matches.map((m) => m.path).join(", ");
39
- const body =
40
- matches.length > 0 ? `${paths} stored` : `${target} not found`;
41
- await store.upsert(runId, turn, entry.resultPath, body, "stored");
42
- }
43
- }
44
-
45
- full(entry) {
46
- return `# store ${entry.attributes.path || entry.path}`;
47
- }
48
-
49
- summary(entry) {
50
- return this.full(entry);
51
- }
52
- }
@@ -1,4 +0,0 @@
1
- ## <summarize>[Answer or summary]</summarize>
2
- * Describe the final state
3
- * ONLY use if done
4
- * Keep brief (<= 80 characters)
@@ -1,5 +0,0 @@
1
- ## <unknown>[what you need to learn]</unknown> - Track open questions
2
- Example: <unknown>contents of answer.txt</unknown>
3
- Example: <unknown>which database adapter is configured</unknown>
4
- * Use get, env, or ask_user to investigate unknowns
5
- * When irrelevant or resolved, use <rm/> to remove from context.
@@ -1,4 +0,0 @@
1
- ## <update>[Brief update]</update>
2
- * Describe the current state
3
- * DO NOT use if done
4
- * Keep brief (<= 80 characters)