@possumtech/rummy 0.5.0 → 2.0.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 (153) hide show
  1. package/.env.example +21 -5
  2. package/PLUGINS.md +389 -194
  3. package/README.md +25 -8
  4. package/SPEC.md +850 -373
  5. package/bin/demo.js +166 -0
  6. package/bin/rummy.js +9 -3
  7. package/biome/no-fallbacks.grit +50 -0
  8. package/lang/en.json +2 -2
  9. package/migrations/001_initial_schema.sql +88 -37
  10. package/package.json +6 -4
  11. package/service.js +50 -9
  12. package/src/agent/AgentLoop.js +460 -330
  13. package/src/agent/ContextAssembler.js +4 -4
  14. package/src/agent/Entries.js +655 -0
  15. package/src/agent/ProjectAgent.js +30 -18
  16. package/src/agent/TurnExecutor.js +229 -421
  17. package/src/agent/XmlParser.js +99 -33
  18. package/src/agent/budget.js +56 -0
  19. package/src/agent/errors.js +22 -0
  20. package/src/agent/httpStatus.js +39 -0
  21. package/src/agent/known_checks.sql +8 -4
  22. package/src/agent/known_queries.sql +9 -13
  23. package/src/agent/known_store.sql +275 -125
  24. package/src/agent/materializeContext.js +102 -0
  25. package/src/agent/runs.sql +10 -7
  26. package/src/agent/schemes.sql +14 -3
  27. package/src/agent/turns.sql +9 -9
  28. package/src/hooks/HookRegistry.js +6 -5
  29. package/src/hooks/Hooks.js +44 -3
  30. package/src/hooks/PluginContext.js +29 -21
  31. package/src/{server → hooks}/RpcRegistry.js +2 -1
  32. package/src/hooks/RummyContext.js +135 -35
  33. package/src/hooks/ToolRegistry.js +21 -16
  34. package/src/llm/LlmProvider.js +64 -90
  35. package/src/llm/errors.js +21 -0
  36. package/src/plugins/ask_user/README.md +1 -1
  37. package/src/plugins/ask_user/ask_user.js +37 -12
  38. package/src/plugins/ask_user/ask_userDoc.js +2 -25
  39. package/src/plugins/ask_user/ask_userDoc.md +10 -0
  40. package/src/plugins/budget/README.md +27 -25
  41. package/src/plugins/budget/budget.js +260 -88
  42. package/src/plugins/cp/README.md +2 -2
  43. package/src/plugins/cp/cp.js +29 -11
  44. package/src/plugins/cp/cpDoc.js +2 -15
  45. package/src/plugins/cp/cpDoc.md +7 -0
  46. package/src/plugins/engine/README.md +2 -2
  47. package/src/plugins/engine/engine.sql +4 -4
  48. package/src/plugins/engine/turn_context.sql +10 -10
  49. package/src/plugins/env/README.md +20 -5
  50. package/src/plugins/env/env.js +45 -6
  51. package/src/plugins/env/envDoc.js +2 -23
  52. package/src/plugins/env/envDoc.md +13 -0
  53. package/src/plugins/error/README.md +16 -0
  54. package/src/plugins/error/error.js +151 -0
  55. package/src/plugins/file/README.md +6 -6
  56. package/src/plugins/file/file.js +15 -2
  57. package/src/plugins/get/README.md +1 -1
  58. package/src/plugins/get/get.js +103 -48
  59. package/src/plugins/get/getDoc.js +2 -32
  60. package/src/plugins/get/getDoc.md +36 -0
  61. package/src/plugins/hedberg/README.md +1 -2
  62. package/src/plugins/hedberg/hedberg.js +8 -4
  63. package/src/plugins/hedberg/matcher.js +16 -17
  64. package/src/plugins/hedberg/normalize.js +0 -48
  65. package/src/plugins/helpers.js +42 -2
  66. package/src/plugins/index.js +146 -123
  67. package/src/plugins/instructions/README.md +35 -9
  68. package/src/plugins/instructions/instructions.js +122 -9
  69. package/src/plugins/instructions/instructions.md +25 -0
  70. package/src/plugins/instructions/instructions_104.md +7 -0
  71. package/src/plugins/instructions/instructions_105.md +46 -0
  72. package/src/plugins/instructions/instructions_106.md +0 -0
  73. package/src/plugins/instructions/instructions_107.md +0 -0
  74. package/src/plugins/instructions/instructions_108.md +8 -0
  75. package/src/plugins/instructions/protocol.js +12 -0
  76. package/src/plugins/known/README.md +2 -2
  77. package/src/plugins/known/known.js +67 -36
  78. package/src/plugins/known/knownDoc.js +2 -17
  79. package/src/plugins/known/knownDoc.md +8 -0
  80. package/src/plugins/log/README.md +48 -0
  81. package/src/plugins/log/log.js +109 -0
  82. package/src/plugins/mv/README.md +2 -2
  83. package/src/plugins/mv/mv.js +55 -22
  84. package/src/plugins/mv/mvDoc.js +2 -18
  85. package/src/plugins/mv/mvDoc.md +10 -0
  86. package/src/plugins/ollama/README.md +15 -0
  87. package/src/{llm/OllamaClient.js → plugins/ollama/ollama.js} +40 -18
  88. package/src/plugins/openai/README.md +17 -0
  89. package/src/plugins/openai/openai.js +120 -0
  90. package/src/plugins/openrouter/README.md +27 -0
  91. package/src/plugins/openrouter/openrouter.js +121 -0
  92. package/src/plugins/persona/README.md +20 -0
  93. package/src/plugins/persona/persona.js +9 -16
  94. package/src/plugins/policy/README.md +21 -0
  95. package/src/plugins/policy/policy.js +29 -14
  96. package/src/plugins/prompt/README.md +1 -1
  97. package/src/plugins/prompt/prompt.js +58 -16
  98. package/src/plugins/rm/README.md +1 -1
  99. package/src/plugins/rm/rm.js +56 -12
  100. package/src/plugins/rm/rmDoc.js +2 -20
  101. package/src/plugins/rm/rmDoc.md +13 -0
  102. package/src/plugins/rpc/README.md +2 -2
  103. package/src/plugins/rpc/rpc.js +515 -296
  104. package/src/plugins/set/README.md +1 -1
  105. package/src/plugins/set/set.js +318 -75
  106. package/src/plugins/set/setDoc.js +2 -35
  107. package/src/plugins/set/setDoc.md +22 -0
  108. package/src/plugins/sh/README.md +28 -5
  109. package/src/plugins/sh/sh.js +50 -6
  110. package/src/plugins/sh/shDoc.js +2 -23
  111. package/src/plugins/sh/shDoc.md +13 -0
  112. package/src/plugins/skill/README.md +23 -0
  113. package/src/plugins/skill/skill.js +14 -18
  114. package/src/plugins/stream/README.md +101 -0
  115. package/src/plugins/stream/stream.js +290 -0
  116. package/src/plugins/telemetry/README.md +1 -1
  117. package/src/plugins/telemetry/telemetry.js +129 -80
  118. package/src/plugins/think/README.md +1 -1
  119. package/src/plugins/think/think.js +12 -0
  120. package/src/plugins/think/thinkDoc.js +2 -15
  121. package/src/plugins/think/thinkDoc.md +7 -0
  122. package/src/plugins/unknown/README.md +3 -3
  123. package/src/plugins/unknown/unknown.js +47 -19
  124. package/src/plugins/unknown/unknownDoc.js +2 -21
  125. package/src/plugins/unknown/unknownDoc.md +11 -0
  126. package/src/plugins/update/README.md +1 -1
  127. package/src/plugins/update/update.js +67 -5
  128. package/src/plugins/update/updateDoc.js +2 -30
  129. package/src/plugins/update/updateDoc.md +8 -0
  130. package/src/plugins/xai/README.md +23 -0
  131. package/src/{llm/XaiClient.js → plugins/xai/xai.js} +58 -37
  132. package/src/server/ClientConnection.js +64 -37
  133. package/src/server/SocketServer.js +23 -10
  134. package/src/server/protocol.js +11 -0
  135. package/src/sql/v_model_context.sql +27 -31
  136. package/src/sql/v_run_log.sql +9 -14
  137. package/EXCEPTIONS.md +0 -46
  138. package/FIDELITY_CONTRACT.md +0 -172
  139. package/src/agent/KnownStore.js +0 -337
  140. package/src/agent/ResponseHealer.js +0 -241
  141. package/src/llm/OpenAiClient.js +0 -100
  142. package/src/llm/OpenRouterClient.js +0 -100
  143. package/src/plugins/budget/recovery.js +0 -47
  144. package/src/plugins/instructions/preamble.md +0 -45
  145. package/src/plugins/performed/README.md +0 -15
  146. package/src/plugins/performed/performed.js +0 -45
  147. package/src/plugins/previous/README.md +0 -16
  148. package/src/plugins/previous/previous.js +0 -56
  149. package/src/plugins/progress/README.md +0 -16
  150. package/src/plugins/progress/progress.js +0 -43
  151. package/src/plugins/summarize/README.md +0 -19
  152. package/src/plugins/summarize/summarize.js +0 -32
  153. package/src/plugins/summarize/summarizeDoc.js +0 -27
@@ -1,172 +0,0 @@
1
- # Fidelity Contract — Observed State vs Intended
2
-
3
- ## Observed Behavior (traced from test/mab/results/2026-04-14T15-13-55-950Z/last_run.txt, turn 24)
4
-
5
- ### Flow
6
-
7
- ```
8
- Model emits tool
9
-
10
- Tool handler stores body in known_entries.body (raw, as model wrote it)
11
-
12
- Next turn: TurnExecutor materializes context
13
-
14
- For each row: hooks.tools.view(scheme, entry) → plugin's view hook returns projected body
15
-
16
- Projected body stored in turn_context.body with fidelity-projected token count
17
-
18
- Assembly phase: section renderers (knowns, unknowns, previous, performed) pull from ctx.rows (which has projected body) and render tags
19
-
20
- Model sees the assembled <knowns>, <previous>, etc. sections in the system prompt
21
- ```
22
-
23
- ### Fidelity Values (from code)
24
-
25
- - **full**: fully shown
26
- - **summary**: "compact" shown — but WHAT "compact" means varies per plugin
27
- - **archive**: excluded by `v_model_context` SQL before reaching any renderer (clean)
28
-
29
- ## Three Breaks in the Intended Contract
30
-
31
- ### Break 1 — Plugins disagree on what summary means
32
-
33
- Every plugin that registers view hooks decides what body to project per fidelity. Observed:
34
-
35
- | Plugin | full() | summary() |
36
- |--------|--------|-----------|
37
- | known | `# known ${path}\n${body}` | **same as full** (wrong) |
38
- | prompt | `body` | **500-char truncation + marker** (correct) |
39
- | budget | `body` | `body` (ok — budget is naturally short) |
40
- | skill | `body` | `body` (inherited default) |
41
- | unknown | varies — needs audit | needs audit |
42
- | others | needs audit | needs audit |
43
-
44
- The `known` plugin's `summary()` returning the full body is a direct contract violation. The summary view should return a compact representation of the entry, not the same full body.
45
-
46
- ### Break 2 — Renderers re-apply fidelity logic
47
-
48
- Two renderers currently re-check entry fidelity and override the plugin's projection:
49
-
50
- **`known.js` `renderKnownTag`** (lines 111-115):
51
- ```js
52
- if (entry.fidelity === "archive") return "";
53
- if (entry.fidelity === "summary") {
54
- return `<${tag} path="${entry.path}"...${summary}${fidelity}${tokens}${flag}/>`;
55
- }
56
- return `<${tag} path="${entry.path}"...${summary}${fidelity}${tokens}${flag}>${entry.body}</${tag}>`;
57
- ```
58
-
59
- This ignores entry.body at summary fidelity and renders self-closing. It's a workaround for known.summary() returning the wrong content. Belt over broken suspenders.
60
-
61
- **`previous.js` `renderToolTag`** (my edit this session):
62
- ```js
63
- if (entry.fidelity === "full") {
64
- return `<${entry.scheme} ${attrs}>${body}</${entry.scheme}>`;
65
- }
66
- // summary: self-closing with summary attr
67
- ```
68
-
69
- I added this fidelity re-check when I should have trusted the plugin's projected body. Same mistake as known, added today.
70
-
71
- ### Break 3 — Model writes scheme headers into body
72
-
73
- Every known/update/unknown entry in the DB has a body that starts with `# known known://path\n`, `# update\n`, or `# unknown\n`. The model writes this because the examples in the system prompt render tags with the body prefixed by `# ${scheme} ${path}\n`.
74
-
75
- Then the plugin's `full()` hook prepends ANOTHER `# ${scheme} ${path}\n` when projecting. Result: duplicate headers in the rendered output.
76
-
77
- Observed in turn 16 update body: `"# update\n# update\nDocuments 20-22 indexed and archived."`
78
-
79
- And in unknown paths: the slug-generation for pathless unknowns takes the body including the `# unknown\n` prefix, resulting in URL-encoded paths like:
80
- ```
81
- unknown://%23%20unknown%0ADocument%2023%20is%20missing%20from%20the%20prompt.
82
- ```
83
-
84
- ## The Intended Contract
85
-
86
- Based on the user's stated philosophy ("surface problems, don't solve them; plugin decides, renderer renders"):
87
-
88
- ### Layer 1 — Plugin decides per fidelity
89
-
90
- Each plugin registers view hooks that return the body content for each fidelity value:
91
-
92
- ```js
93
- core.hooks.tools.onView("known", (entry) => entry.body, "full");
94
- core.hooks.tools.onView("known", (entry) => "", "summary");
95
- ```
96
-
97
- At archive, no view hook is called (v_model_context excludes them).
98
-
99
- ### Layer 2 — Renderer shows the projected body
100
-
101
- Renderers take the projected body from `ctx.rows[].body`:
102
- - If non-empty, wrap in tag with body
103
- - If empty, render self-closing tag
104
-
105
- Renderers do NOT re-check entry.fidelity. They trust the plugin's projection.
106
-
107
- ### Layer 3 — Tag attributes always present
108
-
109
- Tag attributes visible in both full and summary rendering:
110
- - `path` — always
111
- - `summary` — if present in entry.attributes.summary
112
- - `turn` — if source_turn is set
113
- - `status` — if status is set
114
- - `fidelity` — always (the value itself)
115
- - `tokens` — always (full-cost value, unchanged by fidelity per `set_fidelity` SQL)
116
-
117
- ### Per-plugin view decisions (revised)
118
-
119
- | Plugin | Category | Full body | Summary body | Notes |
120
- |--------|----------|-----------|--------------|-------|
121
- | known | data | `entry.body` (no `# known` prefix) | `""` | Tag's summary attr carries the keywords |
122
- | unknown | unknown | `entry.body` | `""` | Same pattern as known/skill — summary attr carries the label |
123
- | prompt | prompt | `entry.body` | 500-char truncation with `[truncated...]` | Current behavior is correct |
124
- | budget | logging | `entry.body` | `entry.body` | Feedback signal — always full |
125
- | update | logging | `entry.body` | `entry.body` | Already 80-char capped |
126
- | summarize | logging | `entry.body` | `entry.body` | Already 80-char capped |
127
- | get | logging | result body | `""` | Just the action tag at summary |
128
- | set, rm, cp, mv | logging | result body | `""` | Just the action tag at summary |
129
- | env, sh | logging | output | `""` | Just the action tag at summary |
130
- | search | logging | results | `""` | Just the action tag at summary |
131
- | skill | data | `entry.body` | `""` | Same as known |
132
- | file | data | `entry.body` | `""` | Same as known |
133
- | http, https | data | — | — | **Move to rummy.web plugin** — not in core |
134
-
135
- ## The Body-Header Problem
136
-
137
- Separate from fidelity: the model writes `# scheme path` into the body because examples show that shape. Plugin view hooks then prepend another header.
138
-
139
- **Rule**: `# scheme` prefix belongs only in **logging** scheme outputs (tool execution results where the prefix identifies the log entry type). Non-logging schemes (known, unknown, prompt, data entries) should have no body prefix — tag attributes identify the entry.
140
-
141
- **What to remove**:
142
- - `known.js` `full()`: remove `# known ${entry.path}\n` prefix — just return `entry.body`
143
- - `unknown.js` `full()`: remove any `# unknown\n` prefix
144
- - Tooldoc examples for known/unknown that show bodies starting with `# scheme path` — remove so model stops copying
145
-
146
- **What to keep**:
147
- - Logging plugins (update, summarize, budget, get, set, etc.) may keep `# scheme` prefixes if present — they're describing tool execution results.
148
-
149
- ## Test Plan
150
-
151
- To enforce the contract:
152
-
153
- 1. **Per-plugin unit tests**: Each plugin with fidelity-sensitive views tests `full(entry)` and `summary(entry)` return the expected content.
154
- 2. **Renderer tests**: Each section renderer (knowns, previous, performed, unknowns) tests that it trusts `entry.body` without re-checking fidelity.
155
- 3. **Integration test**: Load a DB with entries at each fidelity, assemble context, verify:
156
- - Archive entries absent from any section
157
- - Summary entries visible as compact tags
158
- - Full entries visible with body
159
- - No double headers in bodies
160
- 4. **Contract lint**: Grep for `entry.fidelity ===` in renderer files — should have zero matches.
161
-
162
- ## Deliverable Order
163
-
164
- Before touching code, this document should be reviewed. Once aligned, the fix order would be:
165
-
166
- 1. Fix plugin view hooks to return correct body per fidelity
167
- 2. Remove fidelity re-checks from renderers
168
- 3. Remove the `# scheme path` header prepending (plugin-side) and examples (tooldoc-side)
169
- 4. Write tests per the plan above
170
- 5. Regenerate a sample context packet to confirm clean output
171
-
172
- No silent interventions. No belt-and-suspenders logic. Plugin projects, renderer renders, model sees honest representation.
@@ -1,337 +0,0 @@
1
- import slugify from "../sql/functions/slugify.js";
2
-
3
- export default class KnownStore {
4
- #db;
5
- #onChanged;
6
- #schemes = new Map();
7
- #seq = 0;
8
- #pendingResolutions = new Map();
9
-
10
- constructor(db, { onChanged } = {}) {
11
- this.#db = db;
12
- this.#onChanged = onChanged || null;
13
- }
14
-
15
- async loadSchemes(db) {
16
- const rows = await (db || this.#db).get_all_schemes.all();
17
- this.#schemes.clear();
18
- for (const row of rows) {
19
- this.#schemes.set(row.name, row);
20
- }
21
- }
22
-
23
- #emitChanged(runId, path, changeType) {
24
- if (this.#onChanged) this.#onChanged({ runId, path, changeType });
25
- }
26
-
27
- static scheme(path) {
28
- const idx = path.indexOf("://");
29
- return idx > 0 ? path.slice(0, idx) : null;
30
- }
31
-
32
- static normalizePath(path) {
33
- if (!path?.includes("://")) return path;
34
- const sep = path.indexOf("://");
35
- const scheme = path.slice(0, sep).toLowerCase();
36
- const rest = path.slice(sep + 3);
37
- try {
38
- // Decode first (idempotent), then encode — but preserve slashes
39
- const decoded = decodeURIComponent(rest);
40
- return `${scheme}://${decoded.split("/").map(encodeURIComponent).join("/")}`;
41
- } catch {
42
- return `${scheme}://${rest.split("/").map(encodeURIComponent).join("/")}`;
43
- }
44
- }
45
-
46
- async nextTurn(runId) {
47
- const row = await this.#db.next_turn.get({ run_id: runId });
48
- return row.turn;
49
- }
50
-
51
- async dedup(runId, scheme, target, turn) {
52
- const encodedTarget = encodeURIComponent(target);
53
- const turnPrefix = turn ? `turn_${turn}/` : "";
54
- const candidate = `${scheme}://${turnPrefix}${encodedTarget}`;
55
- const existing = await this.#db.get_entry_body.get({
56
- run_id: runId,
57
- path: candidate,
58
- });
59
- if (!existing) return candidate;
60
- return `${candidate}_${++this.#seq}`;
61
- }
62
-
63
- async slugPath(runId, scheme, content, summary) {
64
- const source = summary || content || "";
65
- const base = slugify(source);
66
- const prefix = `${scheme}://`;
67
-
68
- if (!base) return `${prefix}${++this.#seq}`;
69
-
70
- const candidate = `${prefix}${base}`;
71
- const existing = await this.#db.get_entry_body.get({
72
- run_id: runId,
73
- path: candidate,
74
- });
75
- if (!existing) return candidate;
76
-
77
- return `${prefix}${base}_${++this.#seq}`;
78
- }
79
-
80
- async upsert(
81
- runId,
82
- turn,
83
- path,
84
- body,
85
- status,
86
- {
87
- fidelity = "promoted",
88
- attributes = null,
89
- hash = null,
90
- updatedAt = null,
91
- loopId = null,
92
- } = {},
93
- ) {
94
- const normalized = KnownStore.normalizePath(path);
95
- await this.#db.upsert_known_entry.run({
96
- run_id: runId,
97
- loop_id: loopId,
98
- turn,
99
- path: normalized,
100
- body,
101
- status,
102
- fidelity,
103
- hash,
104
- attributes: attributes ? JSON.stringify(attributes) : null,
105
- updated_at: updatedAt,
106
- });
107
- this.#emitChanged(runId, normalized, "upsert");
108
- }
109
-
110
- async promote(runId, path, turn) {
111
- const normalized = KnownStore.normalizePath(path);
112
- await this.#db.promote_path.run({
113
- run_id: runId,
114
- path: normalized,
115
- turn,
116
- });
117
- this.#emitChanged(runId, normalized, "promote");
118
- }
119
-
120
- async setFileFidelity(runId, pattern, fidelity) {
121
- const result = await this.#db.set_file_fidelity.run({
122
- run_id: runId,
123
- pattern,
124
- fidelity,
125
- });
126
- if (result.changes === 0) {
127
- await this.upsert(runId, 0, pattern, "", 200, { fidelity });
128
- }
129
- this.#emitChanged(runId, pattern, "fidelity");
130
- }
131
-
132
- async setFidelity(runId, path, fidelity) {
133
- const normalized = KnownStore.normalizePath(path);
134
- await this.#db.set_fidelity.run({
135
- run_id: runId,
136
- path: normalized,
137
- fidelity,
138
- });
139
- this.#emitChanged(runId, normalized, "fidelity");
140
- }
141
-
142
- async demote(runId, path) {
143
- const normalized = KnownStore.normalizePath(path);
144
- await this.#db.demote_path.run({
145
- run_id: runId,
146
- path: normalized,
147
- });
148
- this.#emitChanged(runId, normalized, "demote");
149
- }
150
-
151
- async remove(runId, path) {
152
- const normalized = KnownStore.normalizePath(path);
153
- await this.#db.delete_known_entry.run({
154
- run_id: runId,
155
- path: normalized,
156
- });
157
- this.#emitChanged(runId, normalized, "remove");
158
- }
159
-
160
- async removeFilesByPattern(runId, pattern) {
161
- await this.#db.delete_file_entries_by_pattern.run({
162
- run_id: runId,
163
- pattern,
164
- });
165
- this.#emitChanged(runId, pattern, "remove");
166
- }
167
-
168
- static #bodyPattern(body) {
169
- return body || null;
170
- }
171
-
172
- async promoteByPattern(runId, path, body, turn) {
173
- await this.#db.promote_by_pattern.run({
174
- run_id: runId,
175
- path,
176
- body: KnownStore.#bodyPattern(body),
177
- turn,
178
- });
179
- this.#emitChanged(runId, path, "promote");
180
- }
181
-
182
- async demoteByPattern(runId, path, body) {
183
- await this.#db.demote_by_pattern.run({
184
- run_id: runId,
185
- path,
186
- body: KnownStore.#bodyPattern(body),
187
- });
188
- this.#emitChanged(runId, path, "demote");
189
- }
190
-
191
- async getEntriesByPattern(runId, path, body, { limit, offset } = {}) {
192
- return this.#db.get_entries_by_pattern.all({
193
- run_id: runId,
194
- path,
195
- body: KnownStore.#bodyPattern(body),
196
- limit: limit ?? null,
197
- offset: offset ?? null,
198
- });
199
- }
200
-
201
- async deleteByPattern(runId, path, body) {
202
- await this.#db.delete_entries_by_pattern.run({
203
- run_id: runId,
204
- path,
205
- body: KnownStore.#bodyPattern(body),
206
- });
207
- this.#emitChanged(runId, path, "remove");
208
- }
209
-
210
- async updateBodyByPattern(runId, path, body, newBody) {
211
- await this.#db.update_body_by_pattern.run({
212
- run_id: runId,
213
- path,
214
- body: KnownStore.#bodyPattern(body),
215
- new_body: newBody,
216
- });
217
- this.#emitChanged(runId, path, "body");
218
- }
219
-
220
- async resolve(runId, path, status, body) {
221
- const normalized = KnownStore.normalizePath(path);
222
- await this.#db.resolve_known_entry.run({
223
- run_id: runId,
224
- path: normalized,
225
- status,
226
- body,
227
- });
228
- this.#emitChanged(runId, normalized, "resolve");
229
- const key = `${runId}:${normalized}`;
230
- const resolver = this.#pendingResolutions.get(key);
231
- if (resolver) {
232
- this.#pendingResolutions.delete(key);
233
- resolver();
234
- }
235
- }
236
-
237
- waitForResolution(runId, path) {
238
- const normalized = KnownStore.normalizePath(path);
239
- const key = `${runId}:${normalized}`;
240
- return new Promise((resolve) => {
241
- this.#pendingResolutions.set(key, resolve);
242
- });
243
- }
244
-
245
- async restoreSummarizedPrompts(runId) {
246
- await this.#db.restore_summarized_prompts.run({ run_id: runId });
247
- this.#emitChanged(runId, "prompt://batch", "fidelity");
248
- }
249
-
250
- async getLog(runId) {
251
- return this.#db.get_results.all({ run_id: runId });
252
- }
253
-
254
- async getEntries(runId) {
255
- return this.#db.get_known_entries.all({ run_id: runId });
256
- }
257
-
258
- async getFileEntries(runId) {
259
- return this.#db.get_file_entries.all({ run_id: runId });
260
- }
261
-
262
- async getFileStatesByPattern(runId, pattern) {
263
- return this.#db.get_file_states_by_pattern.all({ run_id: runId, pattern });
264
- }
265
-
266
- async hasRejections(runId, loopId) {
267
- const row = await this.#db.has_rejections.get({
268
- run_id: runId,
269
- loop_id: loopId,
270
- });
271
- return row.count > 0;
272
- }
273
-
274
- async hasAcceptedActions(runId) {
275
- const row = await this.#db.has_accepted_actions.get({ run_id: runId });
276
- return row.count > 0;
277
- }
278
-
279
- async getUnresolved(runId) {
280
- return this.#db.get_unresolved.all({ run_id: runId });
281
- }
282
-
283
- async countUnknowns(runId) {
284
- const row = await this.#db.count_unknowns.get({ run_id: runId });
285
- return row.count;
286
- }
287
-
288
- async getUnknownValues(runId) {
289
- const rows = await this.#db.get_unknown_values.all({ run_id: runId });
290
- return new Set(rows.map((r) => r.body));
291
- }
292
-
293
- async getBody(runId, path) {
294
- const row = await this.#db.get_entry_body.get({
295
- run_id: runId,
296
- path: KnownStore.normalizePath(path),
297
- });
298
- return row?.body ?? null;
299
- }
300
-
301
- async setAttributes(runId, path, attrs) {
302
- const normalized = KnownStore.normalizePath(path);
303
- await this.#db.update_entry_attributes.run({
304
- run_id: runId,
305
- path: normalized,
306
- attributes: JSON.stringify(attrs),
307
- });
308
- this.#emitChanged(runId, normalized, "attributes");
309
- }
310
-
311
- async getState(runId, path) {
312
- return this.#db.get_entry_state.get({
313
- run_id: runId,
314
- path: KnownStore.normalizePath(path),
315
- });
316
- }
317
-
318
- async getAttributes(runId, path) {
319
- const row = await this.#db.get_entry_attributes.get({
320
- run_id: runId,
321
- path: KnownStore.normalizePath(path),
322
- });
323
- return row?.attributes ? JSON.parse(row.attributes) : null;
324
- }
325
-
326
- async getTurnAudit(runId, turn) {
327
- return this.#db.get_turn_audit.all({ run_id: runId, turn });
328
- }
329
-
330
- static toolFromPath(path) {
331
- return KnownStore.scheme(path);
332
- }
333
-
334
- static isSystemPath(path) {
335
- return path.includes("://");
336
- }
337
- }