@possumtech/rummy 0.5.0 → 2.0.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 +42 -5
- package/PLUGINS.md +389 -194
- package/README.md +25 -8
- package/SPEC.md +934 -373
- package/bin/demo.js +166 -0
- package/bin/rummy.js +9 -3
- package/biome/no-fallbacks.grit +50 -0
- package/lang/en.json +2 -2
- package/migrations/001_initial_schema.sql +88 -37
- package/package.json +13 -11
- package/scriptify/ask_run.js +77 -0
- package/service.js +50 -9
- package/src/agent/AgentLoop.js +476 -335
- package/src/agent/ContextAssembler.js +4 -4
- package/src/agent/Entries.js +676 -0
- package/src/agent/ProjectAgent.js +30 -18
- package/src/agent/TurnExecutor.js +232 -421
- package/src/agent/XmlParser.js +99 -33
- package/src/agent/budget.js +56 -0
- package/src/agent/errors.js +22 -0
- package/src/agent/httpStatus.js +39 -0
- package/src/agent/known_checks.sql +8 -4
- package/src/agent/known_queries.sql +9 -13
- package/src/agent/known_store.sql +280 -125
- package/src/agent/materializeContext.js +104 -0
- package/src/agent/runs.sql +29 -7
- package/src/agent/schemes.sql +14 -3
- package/src/agent/tokens.js +6 -0
- package/src/agent/turns.sql +9 -9
- package/src/hooks/HookRegistry.js +6 -5
- package/src/hooks/Hooks.js +44 -3
- package/src/hooks/PluginContext.js +29 -21
- package/src/{server → hooks}/RpcRegistry.js +2 -1
- package/src/hooks/RummyContext.js +139 -35
- package/src/hooks/ToolRegistry.js +21 -16
- package/src/llm/LlmProvider.js +66 -89
- package/src/llm/errors.js +21 -0
- package/src/llm/retry.js +63 -0
- package/src/plugins/ask_user/README.md +1 -1
- package/src/plugins/ask_user/ask_user.js +37 -12
- package/src/plugins/ask_user/ask_userDoc.js +2 -25
- package/src/plugins/ask_user/ask_userDoc.md +10 -0
- package/src/plugins/budget/README.md +27 -25
- package/src/plugins/budget/budget.js +306 -88
- package/src/plugins/cp/README.md +2 -2
- package/src/plugins/cp/cp.js +29 -11
- package/src/plugins/cp/cpDoc.js +2 -15
- package/src/plugins/cp/cpDoc.md +7 -0
- package/src/plugins/engine/README.md +2 -2
- package/src/plugins/engine/engine.sql +4 -4
- package/src/plugins/engine/turn_context.sql +10 -10
- package/src/plugins/env/README.md +20 -5
- package/src/plugins/env/env.js +45 -6
- package/src/plugins/env/envDoc.js +2 -23
- package/src/plugins/env/envDoc.md +13 -0
- package/src/plugins/error/README.md +16 -0
- package/src/plugins/error/error.js +151 -0
- package/src/plugins/file/README.md +6 -6
- package/src/plugins/file/file.js +15 -2
- package/src/plugins/get/README.md +1 -1
- package/src/plugins/get/get.js +103 -48
- package/src/plugins/get/getDoc.js +2 -32
- package/src/plugins/get/getDoc.md +36 -0
- package/src/plugins/hedberg/README.md +1 -2
- package/src/plugins/hedberg/hedberg.js +8 -4
- package/src/plugins/hedberg/matcher.js +16 -17
- package/src/plugins/hedberg/normalize.js +0 -48
- package/src/plugins/helpers.js +42 -2
- package/src/plugins/index.js +146 -123
- package/src/plugins/instructions/README.md +35 -9
- package/src/plugins/instructions/instructions.js +244 -9
- package/src/plugins/instructions/instructions.md +33 -0
- package/src/plugins/instructions/instructions_104.md +7 -0
- package/src/plugins/instructions/instructions_105.md +38 -0
- package/src/plugins/instructions/instructions_106.md +21 -0
- package/src/plugins/instructions/instructions_107.md +10 -0
- package/src/plugins/instructions/instructions_108.md +0 -0
- package/src/plugins/instructions/protocol.js +12 -0
- package/src/plugins/known/README.md +2 -2
- package/src/plugins/known/known.js +68 -36
- package/src/plugins/known/knownDoc.js +2 -17
- package/src/plugins/known/knownDoc.md +8 -0
- package/src/plugins/log/README.md +48 -0
- package/src/plugins/log/log.js +129 -0
- package/src/plugins/mv/README.md +2 -2
- package/src/plugins/mv/mv.js +55 -22
- package/src/plugins/mv/mvDoc.js +2 -18
- package/src/plugins/mv/mvDoc.md +10 -0
- package/src/plugins/ollama/README.md +15 -0
- package/src/{llm/OllamaClient.js → plugins/ollama/ollama.js} +40 -18
- package/src/plugins/openai/README.md +17 -0
- package/src/plugins/openai/openai.js +120 -0
- package/src/plugins/openrouter/README.md +27 -0
- package/src/plugins/openrouter/openrouter.js +121 -0
- package/src/plugins/persona/README.md +20 -0
- package/src/plugins/persona/persona.js +9 -16
- package/src/plugins/policy/README.md +21 -0
- package/src/plugins/policy/policy.js +29 -14
- package/src/plugins/prompt/README.md +1 -1
- package/src/plugins/prompt/prompt.js +64 -16
- package/src/plugins/rm/README.md +1 -1
- package/src/plugins/rm/rm.js +56 -12
- package/src/plugins/rm/rmDoc.js +2 -20
- package/src/plugins/rm/rmDoc.md +13 -0
- package/src/plugins/rpc/README.md +2 -2
- package/src/plugins/rpc/rpc.js +525 -296
- package/src/plugins/set/README.md +1 -1
- package/src/plugins/set/set.js +318 -75
- package/src/plugins/set/setDoc.js +2 -35
- package/src/plugins/set/setDoc.md +22 -0
- package/src/plugins/sh/README.md +28 -5
- package/src/plugins/sh/sh.js +50 -6
- package/src/plugins/sh/shDoc.js +2 -23
- package/src/plugins/sh/shDoc.md +13 -0
- package/src/plugins/skill/README.md +23 -0
- package/src/plugins/skill/skill.js +14 -18
- package/src/plugins/stream/README.md +101 -0
- package/src/plugins/stream/stream.js +290 -0
- package/src/plugins/telemetry/README.md +1 -1
- package/src/plugins/telemetry/telemetry.js +129 -80
- package/src/plugins/think/README.md +1 -1
- package/src/plugins/think/think.js +12 -0
- package/src/plugins/think/thinkDoc.js +2 -15
- package/src/plugins/think/thinkDoc.md +7 -0
- package/src/plugins/unknown/README.md +3 -3
- package/src/plugins/unknown/unknown.js +47 -19
- package/src/plugins/unknown/unknownDoc.js +2 -21
- package/src/plugins/unknown/unknownDoc.md +11 -0
- package/src/plugins/update/README.md +1 -1
- package/src/plugins/update/update.js +83 -5
- package/src/plugins/update/updateDoc.js +2 -30
- package/src/plugins/update/updateDoc.md +8 -0
- package/src/plugins/xai/README.md +23 -0
- package/src/{llm/XaiClient.js → plugins/xai/xai.js} +58 -37
- package/src/plugins/yolo/yolo.js +192 -0
- package/src/server/ClientConnection.js +64 -37
- package/src/server/SocketServer.js +23 -10
- package/src/server/protocol.js +11 -0
- package/src/sql/v_model_context.sql +27 -31
- package/src/sql/v_run_log.sql +9 -14
- package/EXCEPTIONS.md +0 -46
- package/FIDELITY_CONTRACT.md +0 -172
- package/src/agent/KnownStore.js +0 -337
- package/src/agent/ResponseHealer.js +0 -241
- package/src/llm/OpenAiClient.js +0 -100
- package/src/llm/OpenRouterClient.js +0 -100
- package/src/plugins/budget/recovery.js +0 -47
- package/src/plugins/instructions/preamble.md +0 -45
- package/src/plugins/performed/README.md +0 -15
- package/src/plugins/performed/performed.js +0 -45
- package/src/plugins/previous/README.md +0 -16
- package/src/plugins/previous/previous.js +0 -56
- package/src/plugins/progress/README.md +0 -16
- package/src/plugins/progress/progress.js +0 -43
- package/src/plugins/summarize/README.md +0 -19
- package/src/plugins/summarize/summarize.js +0 -32
- package/src/plugins/summarize/summarizeDoc.js +0 -27
|
@@ -1,196 +1,351 @@
|
|
|
1
|
-
-- PREP:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
-- PREP: upsert_entry
|
|
2
|
+
-- Content-layer upsert. Returns entry id for the subsequent run_view write.
|
|
3
|
+
-- Null :attributes on UPDATE path means "don't touch existing attributes"
|
|
4
|
+
-- — so UPDATE reads :attributes directly, not excluded.attributes (which
|
|
5
|
+
-- would have been coerced to '{}' by the VALUES clause).
|
|
6
|
+
INSERT INTO entries (
|
|
7
|
+
scope, path, body, attributes, hash, updated_at
|
|
5
8
|
)
|
|
6
9
|
VALUES (
|
|
7
|
-
:
|
|
8
|
-
,
|
|
9
|
-
, countTokens(:body)
|
|
10
|
-
, COALESCE(:updated_at, CURRENT_TIMESTAMP)
|
|
10
|
+
:scope, :path, :body, COALESCE(:attributes, '{}'), :hash
|
|
11
|
+
, CURRENT_TIMESTAMP
|
|
11
12
|
)
|
|
12
|
-
ON CONFLICT (
|
|
13
|
+
ON CONFLICT (scope, path) DO UPDATE SET
|
|
13
14
|
body = excluded.body
|
|
14
|
-
,
|
|
15
|
-
,
|
|
16
|
-
,
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
, attributes = COALESCE(:attributes, entries.attributes)
|
|
16
|
+
, hash = COALESCE(:hash, entries.hash)
|
|
17
|
+
, updated_at = CURRENT_TIMESTAMP
|
|
18
|
+
RETURNING id;
|
|
19
|
+
|
|
20
|
+
-- PREP: upsert_run_view
|
|
21
|
+
-- View-layer upsert. Called after upsert_entry with the returned entry id.
|
|
22
|
+
INSERT INTO run_views (
|
|
23
|
+
run_id, entry_id, loop_id, turn, state, outcome, visibility, updated_at
|
|
24
|
+
)
|
|
25
|
+
VALUES (
|
|
26
|
+
:run_id, :entry_id, :loop_id, :turn, :state, :outcome, :visibility
|
|
27
|
+
, CURRENT_TIMESTAMP
|
|
28
|
+
)
|
|
29
|
+
ON CONFLICT (run_id, entry_id) DO UPDATE SET
|
|
30
|
+
loop_id = excluded.loop_id
|
|
19
31
|
, turn = excluded.turn
|
|
20
|
-
,
|
|
21
|
-
,
|
|
22
|
-
,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
, state = excluded.state
|
|
33
|
+
, outcome = excluded.outcome
|
|
34
|
+
, visibility = excluded.visibility
|
|
35
|
+
, write_count = run_views.write_count + 1
|
|
36
|
+
, updated_at = CURRENT_TIMESTAMP;
|
|
37
|
+
|
|
38
|
+
-- Helper fragment: "the entry this run's view references at this path".
|
|
39
|
+
-- Every UPDATE/DELETE resolves its target this way so the logic is
|
|
40
|
+
-- correct whether the entry lives in the run's own scope or a shared one.
|
|
41
|
+
|
|
42
|
+
-- PREP: append_entry_body
|
|
43
|
+
-- Streaming entry body growth. Appends a chunk to the existing body.
|
|
44
|
+
UPDATE entries
|
|
45
|
+
SET
|
|
46
|
+
body = body || :chunk
|
|
47
|
+
, updated_at = CURRENT_TIMESTAMP
|
|
48
|
+
WHERE id = (
|
|
49
|
+
SELECT e.id FROM entries AS e
|
|
50
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
51
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
52
|
+
LIMIT 1
|
|
53
|
+
);
|
|
35
54
|
|
|
36
55
|
-- PREP: delete_known_entry
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
-- Removes the view only. Entry is left for future GC; may be shared.
|
|
57
|
+
DELETE FROM run_views
|
|
58
|
+
WHERE run_id = :run_id AND entry_id = (
|
|
59
|
+
SELECT e.id FROM entries AS e
|
|
60
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
61
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
62
|
+
LIMIT 1
|
|
63
|
+
);
|
|
39
64
|
|
|
40
65
|
-- PREP: delete_file_entries_by_pattern
|
|
41
|
-
DELETE FROM
|
|
42
|
-
WHERE run_id = :run_id AND
|
|
66
|
+
DELETE FROM run_views
|
|
67
|
+
WHERE run_id = :run_id AND entry_id IN (
|
|
68
|
+
SELECT e.id FROM entries AS e
|
|
69
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
70
|
+
WHERE
|
|
71
|
+
rv.run_id = :run_id
|
|
72
|
+
AND hedmatch(:pattern, e.path)
|
|
73
|
+
AND e.scheme IS NULL
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
-- PREP: resolve_known_entry_view
|
|
77
|
+
UPDATE run_views
|
|
78
|
+
SET
|
|
79
|
+
state = :state
|
|
80
|
+
, outcome = :outcome
|
|
81
|
+
, updated_at = CURRENT_TIMESTAMP
|
|
82
|
+
WHERE run_id = :run_id AND entry_id = (
|
|
83
|
+
SELECT e.id FROM entries AS e
|
|
84
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
85
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
86
|
+
LIMIT 1
|
|
87
|
+
);
|
|
43
88
|
|
|
44
|
-
-- PREP:
|
|
45
|
-
UPDATE
|
|
89
|
+
-- PREP: resolve_known_entry_body
|
|
90
|
+
UPDATE entries
|
|
46
91
|
SET
|
|
47
|
-
|
|
48
|
-
, body = :body
|
|
92
|
+
body = :body
|
|
49
93
|
, updated_at = CURRENT_TIMESTAMP
|
|
50
|
-
WHERE
|
|
94
|
+
WHERE id = (
|
|
95
|
+
SELECT e.id FROM entries AS e
|
|
96
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
97
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
98
|
+
LIMIT 1
|
|
99
|
+
);
|
|
51
100
|
|
|
52
|
-
-- PREP:
|
|
53
|
-
UPDATE
|
|
101
|
+
-- PREP: set_file_visibility
|
|
102
|
+
UPDATE run_views
|
|
54
103
|
SET
|
|
55
|
-
|
|
104
|
+
visibility = :visibility
|
|
56
105
|
, updated_at = CURRENT_TIMESTAMP
|
|
57
|
-
WHERE run_id = :run_id AND
|
|
106
|
+
WHERE run_id = :run_id AND entry_id IN (
|
|
107
|
+
SELECT e.id FROM entries AS e
|
|
108
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
109
|
+
WHERE
|
|
110
|
+
rv.run_id = :run_id
|
|
111
|
+
AND hedmatch(:pattern, e.path)
|
|
112
|
+
AND e.scheme IS NULL
|
|
113
|
+
);
|
|
58
114
|
|
|
59
115
|
-- PREP: promote_path
|
|
60
|
-
UPDATE
|
|
116
|
+
UPDATE run_views
|
|
61
117
|
SET
|
|
62
|
-
|
|
63
|
-
,
|
|
118
|
+
visibility = 'visible'
|
|
119
|
+
, state = 'resolved'
|
|
120
|
+
, outcome = NULL
|
|
64
121
|
, turn = :turn
|
|
65
122
|
, updated_at = CURRENT_TIMESTAMP
|
|
66
|
-
WHERE run_id = :run_id AND
|
|
123
|
+
WHERE run_id = :run_id AND entry_id = (
|
|
124
|
+
SELECT e.id FROM entries AS e
|
|
125
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
126
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
127
|
+
LIMIT 1
|
|
128
|
+
);
|
|
67
129
|
|
|
68
130
|
-- PREP: demote_path
|
|
69
|
-
UPDATE
|
|
131
|
+
UPDATE run_views
|
|
70
132
|
SET
|
|
71
|
-
|
|
133
|
+
visibility = 'archived'
|
|
72
134
|
, updated_at = CURRENT_TIMESTAMP
|
|
73
|
-
WHERE run_id = :run_id AND
|
|
135
|
+
WHERE run_id = :run_id AND entry_id = (
|
|
136
|
+
SELECT e.id FROM entries AS e
|
|
137
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
138
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
139
|
+
LIMIT 1
|
|
140
|
+
);
|
|
74
141
|
|
|
75
|
-
-- PREP:
|
|
76
|
-
|
|
77
|
-
UPDATE known_entries
|
|
142
|
+
-- PREP: set_visibility
|
|
143
|
+
UPDATE run_views
|
|
78
144
|
SET
|
|
79
|
-
|
|
145
|
+
visibility = :visibility
|
|
80
146
|
, updated_at = CURRENT_TIMESTAMP
|
|
81
|
-
WHERE run_id = :run_id AND
|
|
147
|
+
WHERE run_id = :run_id AND entry_id = (
|
|
148
|
+
SELECT e.id FROM entries AS e
|
|
149
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
150
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
151
|
+
LIMIT 1
|
|
152
|
+
);
|
|
82
153
|
|
|
83
154
|
-- PREP: get_entry_body
|
|
84
|
-
SELECT body
|
|
85
|
-
FROM
|
|
86
|
-
|
|
155
|
+
SELECT e.body AS body
|
|
156
|
+
FROM run_views AS rv
|
|
157
|
+
JOIN entries AS e ON e.id = rv.entry_id
|
|
158
|
+
WHERE rv.run_id = :run_id AND e.path = :path;
|
|
87
159
|
|
|
88
160
|
-- PREP: get_entry_state
|
|
89
|
-
SELECT
|
|
90
|
-
FROM
|
|
91
|
-
|
|
161
|
+
SELECT rv.state, rv.outcome, rv.visibility, e.scheme, rv.turn
|
|
162
|
+
FROM run_views AS rv
|
|
163
|
+
JOIN entries AS e ON e.id = rv.entry_id
|
|
164
|
+
WHERE rv.run_id = :run_id AND e.path = :path;
|
|
92
165
|
|
|
93
166
|
-- PREP: get_file_states_by_pattern
|
|
94
|
-
SELECT path,
|
|
95
|
-
FROM
|
|
96
|
-
|
|
97
|
-
|
|
167
|
+
SELECT e.path, rv.state, rv.outcome, rv.visibility, rv.turn
|
|
168
|
+
FROM run_views AS rv
|
|
169
|
+
JOIN entries AS e ON e.id = rv.entry_id
|
|
170
|
+
WHERE
|
|
171
|
+
rv.run_id = :run_id
|
|
172
|
+
AND hedmatch(:pattern, e.path)
|
|
173
|
+
AND e.scheme IS NULL
|
|
174
|
+
ORDER BY e.path;
|
|
98
175
|
|
|
99
176
|
-- PREP: update_entry_attributes
|
|
100
|
-
UPDATE
|
|
177
|
+
UPDATE entries
|
|
101
178
|
SET
|
|
102
179
|
attributes = json_patch(attributes, :attributes)
|
|
103
180
|
, updated_at = CURRENT_TIMESTAMP
|
|
104
|
-
WHERE
|
|
181
|
+
WHERE id = (
|
|
182
|
+
SELECT e.id FROM entries AS e
|
|
183
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
184
|
+
WHERE rv.run_id = :run_id AND e.path = :path
|
|
185
|
+
LIMIT 1
|
|
186
|
+
);
|
|
105
187
|
|
|
106
188
|
-- PREP: get_entry_attributes
|
|
107
|
-
SELECT attributes
|
|
108
|
-
FROM
|
|
109
|
-
|
|
189
|
+
SELECT e.attributes AS attributes
|
|
190
|
+
FROM run_views AS rv
|
|
191
|
+
JOIN entries AS e ON e.id = rv.entry_id
|
|
192
|
+
WHERE rv.run_id = :run_id AND e.path = :path;
|
|
110
193
|
|
|
111
194
|
-- PREP: promote_by_pattern
|
|
112
|
-
UPDATE
|
|
195
|
+
UPDATE run_views
|
|
113
196
|
SET
|
|
114
|
-
|
|
115
|
-
,
|
|
197
|
+
visibility = 'visible'
|
|
198
|
+
, state = 'resolved'
|
|
199
|
+
, outcome = NULL
|
|
116
200
|
, turn = :turn
|
|
117
201
|
, updated_at = CURRENT_TIMESTAMP
|
|
118
|
-
WHERE
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
202
|
+
WHERE run_id = :run_id AND entry_id IN (
|
|
203
|
+
SELECT e.id FROM entries AS e
|
|
204
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
205
|
+
WHERE
|
|
206
|
+
rv.run_id = :run_id
|
|
207
|
+
AND hedmatch(:path, e.path)
|
|
208
|
+
AND (:body IS NULL OR hedsearch(:body, e.body))
|
|
209
|
+
);
|
|
122
210
|
|
|
123
211
|
-- PREP: demote_by_pattern
|
|
124
|
-
UPDATE
|
|
212
|
+
UPDATE run_views
|
|
125
213
|
SET
|
|
126
|
-
|
|
214
|
+
visibility = 'archived'
|
|
127
215
|
, updated_at = CURRENT_TIMESTAMP
|
|
128
|
-
WHERE
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
216
|
+
WHERE run_id = :run_id AND entry_id IN (
|
|
217
|
+
SELECT e.id FROM entries AS e
|
|
218
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
219
|
+
WHERE
|
|
220
|
+
rv.run_id = :run_id
|
|
221
|
+
AND hedmatch(:path, e.path)
|
|
222
|
+
AND (:body IS NULL OR hedsearch(:body, e.body))
|
|
223
|
+
);
|
|
132
224
|
|
|
133
225
|
-- PREP: get_entries_by_pattern
|
|
134
|
-
|
|
135
|
-
|
|
226
|
+
-- Default excludes audit schemes (system://, reasoning://, model://, user://,
|
|
227
|
+
-- assistant://, content://, instructions://) so model-facing tools never leak
|
|
228
|
+
-- internal entries. Internal callers that need them pass include_audit_schemes=1.
|
|
229
|
+
SELECT
|
|
230
|
+
e.path, e.body, e.scheme, rv.state, rv.outcome, rv.visibility
|
|
231
|
+
, countTokens(e.body) AS tokens, e.attributes
|
|
232
|
+
FROM run_views AS rv
|
|
233
|
+
JOIN entries AS e ON e.id = rv.entry_id
|
|
234
|
+
JOIN schemes AS s ON s.name = COALESCE(e.scheme, 'file')
|
|
136
235
|
WHERE
|
|
137
|
-
run_id = :run_id
|
|
138
|
-
AND hedmatch(:path, path)
|
|
139
|
-
AND (:body IS NULL OR hedsearch(:body, body))
|
|
140
|
-
|
|
236
|
+
rv.run_id = :run_id
|
|
237
|
+
AND hedmatch(:path, e.path)
|
|
238
|
+
AND (:body IS NULL OR hedsearch(:body, e.body))
|
|
239
|
+
AND (:include_audit_schemes IS NOT NULL OR s.model_visible = 1)
|
|
240
|
+
ORDER BY e.path
|
|
141
241
|
LIMIT
|
|
142
242
|
COALESCE(:limit, -1)
|
|
143
|
-
OFFSET
|
|
144
|
-
COALESCE(:offset, 0);
|
|
243
|
+
OFFSET COALESCE(:offset, 0);
|
|
145
244
|
|
|
146
245
|
-- PREP: delete_entries_by_pattern
|
|
147
|
-
DELETE FROM
|
|
148
|
-
WHERE
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
246
|
+
DELETE FROM run_views
|
|
247
|
+
WHERE run_id = :run_id AND entry_id IN (
|
|
248
|
+
SELECT e.id FROM entries AS e
|
|
249
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
250
|
+
WHERE
|
|
251
|
+
rv.run_id = :run_id
|
|
252
|
+
AND hedmatch(:path, e.path)
|
|
253
|
+
AND (:body IS NULL OR hedsearch(:body, e.body))
|
|
254
|
+
);
|
|
152
255
|
|
|
153
256
|
-- PREP: update_body_by_pattern
|
|
154
|
-
UPDATE
|
|
257
|
+
UPDATE entries
|
|
155
258
|
SET
|
|
156
259
|
body = :new_body
|
|
157
|
-
, tokens = countTokens(:new_body)
|
|
158
|
-
, write_count = write_count + 1
|
|
159
260
|
, updated_at = CURRENT_TIMESTAMP
|
|
160
|
-
WHERE
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
261
|
+
WHERE id IN (
|
|
262
|
+
SELECT e.id FROM entries AS e
|
|
263
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
264
|
+
WHERE
|
|
265
|
+
rv.run_id = :run_id
|
|
266
|
+
AND hedmatch(:path, e.path)
|
|
267
|
+
AND (:body IS NULL OR hedsearch(:body, e.body))
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
-- PREP: bump_write_count_by_pattern
|
|
271
|
+
-- Companion to update_body_by_pattern. write_count lives on run_views.
|
|
272
|
+
UPDATE run_views
|
|
170
273
|
SET
|
|
171
|
-
|
|
274
|
+
write_count = write_count + 1
|
|
172
275
|
, updated_at = CURRENT_TIMESTAMP
|
|
173
|
-
WHERE run_id = :run_id AND
|
|
276
|
+
WHERE run_id = :run_id AND entry_id IN (
|
|
277
|
+
SELECT e.id FROM entries AS e
|
|
278
|
+
JOIN run_views AS rv ON rv.entry_id = e.id
|
|
279
|
+
WHERE
|
|
280
|
+
rv.run_id = :run_id
|
|
281
|
+
AND hedmatch(:path, e.path)
|
|
282
|
+
AND (:body IS NULL OR hedsearch(:body, e.body))
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
-- PREP: get_turn_demotion_targets
|
|
286
|
+
-- Rows that demote_turn_entries is about to flip. Return shape
|
|
287
|
+
-- matches the old RETURNING (path, tokens) for caller compatibility.
|
|
288
|
+
-- State filter: skip failed/cancelled entries (they're already not
|
|
289
|
+
-- contributing visible context — demoting them would be misleading).
|
|
290
|
+
-- Scheme filter: skip known/unknown — these are the model's deliverables,
|
|
291
|
+
-- not housekeeping. Auto-demoting just-created knowns punishes the
|
|
292
|
+
-- correct Distill+Demote pattern.
|
|
293
|
+
SELECT e.path, countTokens(e.body) AS tokens
|
|
294
|
+
FROM run_views AS rv
|
|
295
|
+
JOIN entries AS e ON e.id = rv.entry_id
|
|
296
|
+
WHERE
|
|
297
|
+
rv.run_id = :run_id
|
|
298
|
+
AND rv.turn = :turn
|
|
299
|
+
AND rv.visibility = 'visible'
|
|
300
|
+
AND rv.state NOT IN ('failed', 'cancelled')
|
|
301
|
+
AND e.scheme NOT IN ('known', 'unknown');
|
|
174
302
|
|
|
175
303
|
-- PREP: demote_turn_entries
|
|
176
|
-
--
|
|
177
|
-
--
|
|
178
|
-
--
|
|
179
|
-
--
|
|
180
|
-
--
|
|
181
|
-
|
|
182
|
-
UPDATE known_entries
|
|
304
|
+
-- View-layer only — visibility lives on run_views. State untouched.
|
|
305
|
+
-- Call get_turn_demotion_targets first if you need the list of what
|
|
306
|
+
-- was demoted (used by budget plugin for the overflow error body).
|
|
307
|
+
-- Scheme filter mirrors get_turn_demotion_targets — never demote the
|
|
308
|
+
-- model's deliverables (known/unknown) along with housekeeping.
|
|
309
|
+
UPDATE run_views
|
|
183
310
|
SET
|
|
184
|
-
|
|
185
|
-
, status = CASE
|
|
186
|
-
WHEN scheme IN ('set', 'rm', 'mv', 'cp') AND status = 200 THEN 200
|
|
187
|
-
ELSE 413
|
|
188
|
-
END
|
|
311
|
+
visibility = 'summarized'
|
|
189
312
|
, updated_at = CURRENT_TIMESTAMP
|
|
190
313
|
WHERE
|
|
191
314
|
run_id = :run_id
|
|
192
315
|
AND turn = :turn
|
|
193
|
-
AND
|
|
194
|
-
AND
|
|
195
|
-
|
|
316
|
+
AND visibility = 'visible'
|
|
317
|
+
AND state NOT IN ('failed', 'cancelled')
|
|
318
|
+
AND NOT EXISTS (
|
|
319
|
+
SELECT 1
|
|
320
|
+
FROM entries AS e
|
|
321
|
+
WHERE
|
|
322
|
+
e.id = run_views.entry_id
|
|
323
|
+
AND e.scheme IN ('known', 'unknown')
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
-- PREP: get_run_visible_targets
|
|
327
|
+
-- All visible entries across the run, oldest promotion first. Used by
|
|
328
|
+
-- budget postDispatch as the fallback demotion set when this-turn
|
|
329
|
+
-- demotion yields nothing but the packet still overflows (promotions
|
|
330
|
+
-- from prior turns the model forgot to demote themselves).
|
|
331
|
+
SELECT e.path, countTokens(e.body) AS tokens, rv.turn
|
|
332
|
+
FROM run_views AS rv
|
|
333
|
+
JOIN entries AS e ON e.id = rv.entry_id
|
|
334
|
+
WHERE
|
|
335
|
+
rv.run_id = :run_id
|
|
336
|
+
AND rv.visibility = 'visible'
|
|
337
|
+
AND rv.state NOT IN ('failed', 'cancelled')
|
|
338
|
+
ORDER BY rv.turn, e.id;
|
|
196
339
|
|
|
340
|
+
-- PREP: demote_run_visible
|
|
341
|
+
-- Broad cross-turn demotion. Separate prep from demote_turn_entries
|
|
342
|
+
-- so the caller's intent (surgical this-turn vs fallback all-visible)
|
|
343
|
+
-- stays explicit.
|
|
344
|
+
UPDATE run_views
|
|
345
|
+
SET
|
|
346
|
+
visibility = 'summarized'
|
|
347
|
+
, updated_at = CURRENT_TIMESTAMP
|
|
348
|
+
WHERE
|
|
349
|
+
run_id = :run_id
|
|
350
|
+
AND visibility = 'visible'
|
|
351
|
+
AND state NOT IN ('failed', 'cancelled');
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import ContextAssembler from "./ContextAssembler.js";
|
|
2
|
+
import { countLines, countTokens } from "./tokens.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rebuild turn_context from v_model_context, then assemble messages.
|
|
6
|
+
* Called at turn start and again by the budget plugin when it needs a
|
|
7
|
+
* fresh measurement after mutating visibility.
|
|
8
|
+
*/
|
|
9
|
+
export default async function materializeContext({
|
|
10
|
+
db,
|
|
11
|
+
hooks,
|
|
12
|
+
runId,
|
|
13
|
+
loopId,
|
|
14
|
+
turn,
|
|
15
|
+
systemPrompt,
|
|
16
|
+
mode,
|
|
17
|
+
toolSet,
|
|
18
|
+
contextSize,
|
|
19
|
+
demoted,
|
|
20
|
+
}) {
|
|
21
|
+
await db.clear_turn_context.run({ run_id: runId, turn });
|
|
22
|
+
const viewRows = await db.get_model_context.all({ run_id: runId });
|
|
23
|
+
// Per-entry token accounting (see SPEC @token_accounting): captured
|
|
24
|
+
// here while we still have the raw body, then merged onto rows after
|
|
25
|
+
// the read-back roundtrip through turn_context.
|
|
26
|
+
const tokenAccounting = new Map();
|
|
27
|
+
for (const row of viewRows) {
|
|
28
|
+
// schemeOf() yields NULL (or "") for bare file paths — translate
|
|
29
|
+
// to "file" so the view lookup finds the file scheme handler.
|
|
30
|
+
const scheme = row.scheme ? row.scheme : "file";
|
|
31
|
+
const attrs = row.attributes ? JSON.parse(row.attributes) : null;
|
|
32
|
+
// Log entries live at log://turn_N/action/slug. Dispatch projection
|
|
33
|
+
// to the action plugin's view (set, update, search, etc.) by
|
|
34
|
+
// extracting the action segment from the path.
|
|
35
|
+
let projectionKey = scheme;
|
|
36
|
+
if (scheme === "log") {
|
|
37
|
+
const m = row.path.match(/^log:\/\/turn_\d+\/([^/]+)\//);
|
|
38
|
+
if (m) projectionKey = m[1];
|
|
39
|
+
}
|
|
40
|
+
const baseEntry = {
|
|
41
|
+
path: row.path,
|
|
42
|
+
scheme,
|
|
43
|
+
body: row.body,
|
|
44
|
+
attributes: attrs,
|
|
45
|
+
category: row.category,
|
|
46
|
+
};
|
|
47
|
+
const visibleProjection = await hooks.tools.view(projectionKey, {
|
|
48
|
+
...baseEntry,
|
|
49
|
+
visibility: "visible",
|
|
50
|
+
});
|
|
51
|
+
const summarizedProjection = await hooks.tools.view(projectionKey, {
|
|
52
|
+
...baseEntry,
|
|
53
|
+
visibility: "summarized",
|
|
54
|
+
});
|
|
55
|
+
const vTokens = countTokens(visibleProjection);
|
|
56
|
+
const sTokens = countTokens(summarizedProjection);
|
|
57
|
+
const vLines = countLines(visibleProjection);
|
|
58
|
+
tokenAccounting.set(row.path, { vTokens, sTokens, vLines });
|
|
59
|
+
const projectedBody =
|
|
60
|
+
row.visibility === "visible" ? visibleProjection : summarizedProjection;
|
|
61
|
+
await db.insert_turn_context.run({
|
|
62
|
+
run_id: runId,
|
|
63
|
+
loop_id: loopId,
|
|
64
|
+
turn,
|
|
65
|
+
ordinal: row.ordinal,
|
|
66
|
+
path: row.path,
|
|
67
|
+
visibility: row.visibility,
|
|
68
|
+
state: row.state,
|
|
69
|
+
outcome: row.outcome,
|
|
70
|
+
body: projectedBody,
|
|
71
|
+
attributes: row.attributes,
|
|
72
|
+
category: row.category,
|
|
73
|
+
source_turn: row.turn,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const rows = await db.get_turn_context.all({ run_id: runId, turn });
|
|
77
|
+
for (const row of rows) {
|
|
78
|
+
const t = tokenAccounting.get(row.path);
|
|
79
|
+
if (!t) continue;
|
|
80
|
+
row.vTokens = t.vTokens;
|
|
81
|
+
row.sTokens = t.sTokens;
|
|
82
|
+
row.aTokens = t.vTokens - t.sTokens;
|
|
83
|
+
row.vLines = t.vLines;
|
|
84
|
+
}
|
|
85
|
+
const lastCtx = await db.get_last_context_tokens.get({ run_id: runId });
|
|
86
|
+
// First turn of a new run has no prior context.
|
|
87
|
+
let lastContextTokens = 0;
|
|
88
|
+
if (lastCtx) lastContextTokens = lastCtx.context_tokens;
|
|
89
|
+
|
|
90
|
+
const messages = await ContextAssembler.assembleFromTurnContext(
|
|
91
|
+
rows,
|
|
92
|
+
{
|
|
93
|
+
type: mode,
|
|
94
|
+
systemPrompt,
|
|
95
|
+
contextSize,
|
|
96
|
+
demoted,
|
|
97
|
+
toolSet,
|
|
98
|
+
lastContextTokens,
|
|
99
|
+
turn,
|
|
100
|
+
},
|
|
101
|
+
hooks,
|
|
102
|
+
);
|
|
103
|
+
return { rows, messages, lastContextTokens };
|
|
104
|
+
}
|
package/src/agent/runs.sql
CHANGED
|
@@ -44,7 +44,7 @@ SELECT
|
|
|
44
44
|
FROM known_entries AS ke
|
|
45
45
|
WHERE
|
|
46
46
|
ke.run_id = r.id
|
|
47
|
-
AND ke.
|
|
47
|
+
AND ke.path LIKE 'log://turn_%/update/%'
|
|
48
48
|
ORDER BY ke.id DESC
|
|
49
49
|
LIMIT 1
|
|
50
50
|
) AS summary
|
|
@@ -79,16 +79,38 @@ WHERE id = :run_id
|
|
|
79
79
|
RETURNING next_turn - 1 AS turn;
|
|
80
80
|
|
|
81
81
|
-- PREP: fork_known_entries
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
-- Cheap fork: copy only view rows. Entries stay shared between parent
|
|
83
|
+
-- and child. Child's subsequent writes diverge via upsert into a new
|
|
84
|
+
-- run-scoped entry.
|
|
85
|
+
INSERT INTO run_views (
|
|
86
|
+
run_id, entry_id, loop_id, turn, state, outcome, visibility
|
|
87
|
+
, write_count, refs
|
|
85
88
|
)
|
|
86
89
|
SELECT
|
|
87
|
-
:new_run_id, NULL, turn,
|
|
88
|
-
,
|
|
89
|
-
FROM
|
|
90
|
+
:new_run_id, entry_id, NULL, turn, state, outcome, visibility
|
|
91
|
+
, write_count, refs
|
|
92
|
+
FROM run_views
|
|
90
93
|
WHERE run_id = :parent_run_id;
|
|
91
94
|
|
|
95
|
+
-- PREP: archive_prior_prompt_artifacts
|
|
96
|
+
-- Multi-prompt sessions accumulate artifacts from prior prompt cycles
|
|
97
|
+
-- (consumed prompts, their per-turn logs). These pollute the validator's
|
|
98
|
+
-- prior-prompts check on subsequent Deployment landings. Archive all
|
|
99
|
+
-- prior prompt:// entries and prior-turn log:// entries when a new
|
|
100
|
+
-- prompt arrives. Knowns/unknowns/file entries are untouched — they
|
|
101
|
+
-- carry persistent knowledge across cycles. The loop_id IS NULL clause
|
|
102
|
+
-- catches forked-in views from a parent run (per fork_known_entries),
|
|
103
|
+
-- which represent prior cycles' artifacts inherited into a clean child.
|
|
104
|
+
UPDATE run_views
|
|
105
|
+
SET visibility = 'archived'
|
|
106
|
+
WHERE run_id = :run_id
|
|
107
|
+
AND visibility != 'archived'
|
|
108
|
+
AND (turn < :current_turn OR loop_id IS NULL)
|
|
109
|
+
AND entry_id IN (
|
|
110
|
+
SELECT id FROM entries
|
|
111
|
+
WHERE scheme IN ('prompt', 'log')
|
|
112
|
+
);
|
|
113
|
+
|
|
92
114
|
-- PREP: get_active_runs
|
|
93
115
|
SELECT r.id
|
|
94
116
|
FROM runs AS r
|
package/src/agent/schemes.sql
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
-- PREP: upsert_scheme
|
|
2
|
-
INSERT
|
|
3
|
-
VALUES (
|
|
2
|
+
INSERT INTO schemes (name, model_visible, category, default_scope, writable_by)
|
|
3
|
+
VALUES (
|
|
4
|
+
:name
|
|
5
|
+
, :model_visible
|
|
6
|
+
, :category
|
|
7
|
+
, COALESCE(:default_scope, 'run')
|
|
8
|
+
, COALESCE(:writable_by, '["model","plugin"]')
|
|
9
|
+
)
|
|
10
|
+
ON CONFLICT (name) DO UPDATE SET
|
|
11
|
+
model_visible = excluded.model_visible
|
|
12
|
+
, category = excluded.category
|
|
13
|
+
, default_scope = excluded.default_scope
|
|
14
|
+
, writable_by = excluded.writable_by;
|
|
4
15
|
|
|
5
16
|
-- PREP: get_all_schemes
|
|
6
|
-
SELECT name, model_visible, category FROM schemes;
|
|
17
|
+
SELECT name, model_visible, category, default_scope, writable_by FROM schemes;
|
package/src/agent/tokens.js
CHANGED
|
@@ -12,3 +12,9 @@ export function countTokens(text) {
|
|
|
12
12
|
if (!text) return 0;
|
|
13
13
|
return Math.ceil(text.length / DIVISOR);
|
|
14
14
|
}
|
|
15
|
+
|
|
16
|
+
export function countLines(text) {
|
|
17
|
+
if (!text) return 0;
|
|
18
|
+
const newlines = (text.match(/\n/g) || []).length;
|
|
19
|
+
return text.endsWith("\n") ? newlines : newlines + 1;
|
|
20
|
+
}
|