@possumtech/rummy 0.2.6 → 0.2.8
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 +2 -3
- package/PLUGINS.md +105 -82
- package/bin/rummy.js +9 -2
- package/migrations/001_initial_schema.sql +53 -68
- package/package.json +4 -6
- package/service.js +2 -2
- package/src/agent/AgentLoop.js +91 -58
- package/src/agent/ContextAssembler.js +2 -2
- package/src/agent/KnownStore.js +30 -11
- package/src/agent/ProjectAgent.js +1 -3
- package/src/agent/TurnExecutor.js +119 -31
- package/src/agent/XmlParser.js +20 -0
- package/src/agent/known_checks.sql +5 -4
- package/src/agent/known_queries.sql +4 -3
- package/src/agent/known_store.sql +29 -15
- package/src/agent/loops.sql +63 -0
- package/src/agent/runs.sql +7 -7
- package/src/agent/schemes.sql +2 -2
- package/src/agent/turns.sql +3 -3
- package/src/hooks/PluginContext.js +1 -10
- package/src/hooks/RummyContext.js +16 -8
- package/src/plugins/ask_user/ask_user.js +3 -2
- package/src/plugins/cp/cp.js +7 -7
- package/src/plugins/current/current.js +3 -4
- package/src/plugins/engine/engine.sql +5 -3
- package/src/plugins/engine/turn_context.sql +9 -4
- package/src/plugins/env/docs.md +2 -0
- package/src/plugins/env/env.js +3 -2
- package/src/plugins/file/file.js +9 -19
- package/src/plugins/get/docs.md +7 -3
- package/src/plugins/get/get.js +22 -6
- package/src/plugins/hedberg/docs.md +0 -9
- package/src/plugins/hedberg/hedberg.js +2 -5
- package/src/plugins/hedberg/matcher.js +1 -1
- package/src/plugins/hedberg/patterns.js +6 -6
- package/src/plugins/helpers.js +2 -2
- package/src/plugins/index.js +28 -15
- package/src/plugins/instructions/instructions.js +1 -1
- package/src/plugins/known/known.js +9 -11
- package/src/plugins/mv/mv.js +7 -7
- package/src/plugins/previous/previous.js +6 -5
- package/src/plugins/progress/progress.js +6 -0
- package/src/plugins/prompt/prompt.js +9 -10
- package/src/plugins/rm/docs.md +3 -1
- package/src/plugins/rm/rm.js +24 -7
- package/src/plugins/rpc/rpc.js +33 -42
- package/src/plugins/set/docs.md +3 -1
- package/src/plugins/set/set.js +22 -16
- package/src/plugins/sh/sh.js +3 -2
- package/src/plugins/skills/skills.js +3 -4
- package/src/plugins/store/docs.md +2 -1
- package/src/plugins/store/store.js +14 -3
- package/src/plugins/summarize/summarize.js +1 -1
- package/src/plugins/telemetry/telemetry.js +17 -7
- package/src/plugins/unknown/unknown.js +3 -2
- package/src/plugins/update/update.js +1 -1
- package/src/server/ClientConnection.js +3 -5
- package/src/sql/v_model_context.sql +20 -23
- package/src/sql/v_run_log.sql +3 -3
- package/src/agent/prompt_queue.sql +0 -39
|
@@ -25,6 +25,7 @@ export default class TurnExecutor {
|
|
|
25
25
|
projectId,
|
|
26
26
|
currentRunId,
|
|
27
27
|
currentAlias,
|
|
28
|
+
currentLoopId,
|
|
28
29
|
requestedModel,
|
|
29
30
|
loopPrompt,
|
|
30
31
|
noContext,
|
|
@@ -36,6 +37,7 @@ export default class TurnExecutor {
|
|
|
36
37
|
|
|
37
38
|
const turnRow = await this.#db.create_turn.get({
|
|
38
39
|
run_id: currentRunId,
|
|
40
|
+
loop_id: currentLoopId,
|
|
39
41
|
sequence: turn,
|
|
40
42
|
});
|
|
41
43
|
|
|
@@ -67,6 +69,7 @@ export default class TurnExecutor {
|
|
|
67
69
|
type: mode,
|
|
68
70
|
sequence: turn,
|
|
69
71
|
runId: currentRunId,
|
|
72
|
+
loopId: currentLoopId,
|
|
70
73
|
turnId: turnRow.id,
|
|
71
74
|
noContext,
|
|
72
75
|
contextSize,
|
|
@@ -123,11 +126,12 @@ export default class TurnExecutor {
|
|
|
123
126
|
|
|
124
127
|
await this.#db.insert_turn_context.run({
|
|
125
128
|
run_id: currentRunId,
|
|
129
|
+
loop_id: currentLoopId,
|
|
126
130
|
turn,
|
|
127
131
|
ordinal: row.ordinal,
|
|
128
132
|
path: row.path,
|
|
129
133
|
fidelity: row.fidelity,
|
|
130
|
-
|
|
134
|
+
status: row.status,
|
|
131
135
|
body: projectedBody ?? "",
|
|
132
136
|
tokens: countTokens(projectedBody ?? ""),
|
|
133
137
|
attributes: row.attributes,
|
|
@@ -136,6 +140,8 @@ export default class TurnExecutor {
|
|
|
136
140
|
});
|
|
137
141
|
}
|
|
138
142
|
|
|
143
|
+
const demoted = [];
|
|
144
|
+
|
|
139
145
|
await this.#hooks.run.progress.emit({
|
|
140
146
|
projectId,
|
|
141
147
|
run: currentAlias,
|
|
@@ -144,20 +150,96 @@ export default class TurnExecutor {
|
|
|
144
150
|
});
|
|
145
151
|
|
|
146
152
|
// Assemble messages from projected system prompt + materialized turn_context
|
|
147
|
-
|
|
153
|
+
let rows = await this.#db.get_turn_context.all({
|
|
148
154
|
run_id: currentRunId,
|
|
149
155
|
turn,
|
|
150
156
|
});
|
|
151
|
-
|
|
157
|
+
let messages = await ContextAssembler.assembleFromTurnContext(
|
|
152
158
|
rows,
|
|
153
159
|
{
|
|
154
160
|
type: mode,
|
|
155
161
|
systemPrompt,
|
|
156
162
|
contextSize,
|
|
163
|
+
demoted,
|
|
157
164
|
},
|
|
158
165
|
this.#hooks,
|
|
159
166
|
);
|
|
160
167
|
|
|
168
|
+
// Budget check on assembled messages (includes system prompt)
|
|
169
|
+
if (contextSize && demoted.length === 0) {
|
|
170
|
+
const assembledTokens = messages.reduce(
|
|
171
|
+
(sum, m) => sum + countTokens(m.content || ""),
|
|
172
|
+
0,
|
|
173
|
+
);
|
|
174
|
+
const ceiling = contextSize * 0.95;
|
|
175
|
+
if (assembledTokens > ceiling) {
|
|
176
|
+
const candidates = rows
|
|
177
|
+
.filter(
|
|
178
|
+
(r) =>
|
|
179
|
+
r.fidelity === "full" &&
|
|
180
|
+
r.tokens > 0 &&
|
|
181
|
+
(r.category === "file" || r.category === "known"),
|
|
182
|
+
)
|
|
183
|
+
.toSorted((a, b) => a.source_turn - b.source_turn);
|
|
184
|
+
|
|
185
|
+
let excess = assembledTokens - ceiling;
|
|
186
|
+
for (const entry of candidates) {
|
|
187
|
+
if (excess <= 0) break;
|
|
188
|
+
await this.#knownStore.setFidelity(
|
|
189
|
+
currentRunId,
|
|
190
|
+
entry.path,
|
|
191
|
+
"summary",
|
|
192
|
+
);
|
|
193
|
+
excess -= entry.tokens;
|
|
194
|
+
demoted.push(entry.path);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (demoted.length > 0) {
|
|
198
|
+
await this.#db.clear_turn_context.run({ run_id: currentRunId, turn });
|
|
199
|
+
const freshViewRows = await this.#db.get_model_context.all({
|
|
200
|
+
run_id: currentRunId,
|
|
201
|
+
});
|
|
202
|
+
for (const row of freshViewRows) {
|
|
203
|
+
const scheme = row.scheme || "file";
|
|
204
|
+
const projectedBody = await this.#hooks.tools.view(scheme, {
|
|
205
|
+
path: row.path,
|
|
206
|
+
scheme,
|
|
207
|
+
body: row.body,
|
|
208
|
+
attributes: row.attributes ? JSON.parse(row.attributes) : null,
|
|
209
|
+
fidelity: row.fidelity,
|
|
210
|
+
category: row.category,
|
|
211
|
+
});
|
|
212
|
+
await this.#db.insert_turn_context.run({
|
|
213
|
+
run_id: currentRunId,
|
|
214
|
+
loop_id: currentLoopId,
|
|
215
|
+
turn,
|
|
216
|
+
ordinal: row.ordinal,
|
|
217
|
+
path: row.path,
|
|
218
|
+
fidelity: row.fidelity,
|
|
219
|
+
status: row.status,
|
|
220
|
+
body: projectedBody ?? "",
|
|
221
|
+
tokens: countTokens(projectedBody ?? ""),
|
|
222
|
+
attributes: row.attributes,
|
|
223
|
+
category: row.category,
|
|
224
|
+
source_turn: row.turn,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
rows = await this.#db.get_turn_context.all({
|
|
228
|
+
run_id: currentRunId,
|
|
229
|
+
turn,
|
|
230
|
+
});
|
|
231
|
+
messages = await ContextAssembler.assembleFromTurnContext(
|
|
232
|
+
rows,
|
|
233
|
+
{ type: mode, systemPrompt, contextSize, demoted },
|
|
234
|
+
this.#hooks,
|
|
235
|
+
);
|
|
236
|
+
console.warn(
|
|
237
|
+
`[RUMMY] Budget exceeded: demoted ${demoted.length} entries to fit ${contextSize} token limit`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
161
243
|
const filteredMessages = await this.#hooks.llm.messages.filter(messages, {
|
|
162
244
|
model: requestedModel,
|
|
163
245
|
projectId,
|
|
@@ -217,7 +299,13 @@ export default class TurnExecutor {
|
|
|
217
299
|
let updateText = null;
|
|
218
300
|
|
|
219
301
|
for (const cmd of commands) {
|
|
220
|
-
const entry = await this.#record(
|
|
302
|
+
const entry = await this.#record(
|
|
303
|
+
currentRunId,
|
|
304
|
+
currentLoopId,
|
|
305
|
+
turn,
|
|
306
|
+
mode,
|
|
307
|
+
cmd,
|
|
308
|
+
);
|
|
221
309
|
if (!entry) continue;
|
|
222
310
|
|
|
223
311
|
if (entry.scheme === "summarize") summaryText = entry.body;
|
|
@@ -249,7 +337,8 @@ export default class TurnExecutor {
|
|
|
249
337
|
turn,
|
|
250
338
|
summaryPath,
|
|
251
339
|
summaryText,
|
|
252
|
-
|
|
340
|
+
200,
|
|
341
|
+
{ loopId: currentLoopId },
|
|
253
342
|
);
|
|
254
343
|
} else if (updateText) {
|
|
255
344
|
const updatePath = await this.#knownStore.slugPath(
|
|
@@ -262,14 +351,14 @@ export default class TurnExecutor {
|
|
|
262
351
|
turn,
|
|
263
352
|
updatePath,
|
|
264
353
|
updateText,
|
|
265
|
-
|
|
354
|
+
200,
|
|
355
|
+
{ loopId: currentLoopId },
|
|
266
356
|
);
|
|
267
357
|
}
|
|
268
358
|
|
|
269
359
|
// --- PHASE 2: DISPATCH ---
|
|
270
360
|
// Handlers perform side effects: promote, demote, patch, propose.
|
|
271
361
|
|
|
272
|
-
let hasErrors = false;
|
|
273
362
|
for (const entry of recorded) {
|
|
274
363
|
await this.#hooks.tools.dispatch(entry.scheme, entry, rummy);
|
|
275
364
|
await this.#hooks.entry.created.emit(entry);
|
|
@@ -278,13 +367,16 @@ export default class TurnExecutor {
|
|
|
278
367
|
// Materialize proposals (e.g. file plugin applies accumulated revisions)
|
|
279
368
|
await this.#hooks.turn.proposing.emit({ rummy, recorded });
|
|
280
369
|
|
|
281
|
-
// Check if any dispatched entries ended in error state
|
|
370
|
+
// Check if any dispatched entries ended in error or proposed state
|
|
371
|
+
let hasErrors = false;
|
|
372
|
+
let hasProposed = false;
|
|
282
373
|
for (const entry of recorded) {
|
|
283
374
|
const row = await this.#db.get_entry_state.get({
|
|
284
375
|
run_id: currentRunId,
|
|
285
376
|
path: entry.resultPath || entry.path,
|
|
286
377
|
});
|
|
287
|
-
if (row?.
|
|
378
|
+
if (row?.status >= 400) hasErrors = true;
|
|
379
|
+
if (row?.status === 202) hasProposed = true;
|
|
288
380
|
}
|
|
289
381
|
|
|
290
382
|
// Errors override summarize — the model thinks it's done but it's not
|
|
@@ -293,6 +385,12 @@ export default class TurnExecutor {
|
|
|
293
385
|
updateText = "Tool errors detected — retry or investigate.";
|
|
294
386
|
}
|
|
295
387
|
|
|
388
|
+
// Proposals override summarize — outcome unknown until user resolves
|
|
389
|
+
if (hasProposed && summaryText) {
|
|
390
|
+
summaryText = null;
|
|
391
|
+
updateText = "Awaiting approval for proposed changes.";
|
|
392
|
+
}
|
|
393
|
+
|
|
296
394
|
// --- Classify for return value ---
|
|
297
395
|
|
|
298
396
|
const actionCalls = recorded.filter((e) =>
|
|
@@ -343,7 +441,7 @@ export default class TurnExecutor {
|
|
|
343
441
|
* Record a parsed command as a known_entries row.
|
|
344
442
|
* Returns the recorded entry descriptor, or null if rejected/skipped.
|
|
345
443
|
*/
|
|
346
|
-
async #record(runId, turn, mode, cmd) {
|
|
444
|
+
async #record(runId, loopId, turn, mode, cmd) {
|
|
347
445
|
// Mode enforcement — reject prohibited commands in ask mode
|
|
348
446
|
if (mode === "ask") {
|
|
349
447
|
if (cmd.name === "sh") {
|
|
@@ -391,7 +489,9 @@ export default class TurnExecutor {
|
|
|
391
489
|
"unknown",
|
|
392
490
|
cmd.body,
|
|
393
491
|
);
|
|
394
|
-
await this.#knownStore.upsert(runId, turn, unknownPath, cmd.body,
|
|
492
|
+
await this.#knownStore.upsert(runId, turn, unknownPath, cmd.body, 200, {
|
|
493
|
+
loopId,
|
|
494
|
+
});
|
|
395
495
|
return {
|
|
396
496
|
scheme,
|
|
397
497
|
path: unknownPath,
|
|
@@ -401,14 +501,8 @@ export default class TurnExecutor {
|
|
|
401
501
|
};
|
|
402
502
|
}
|
|
403
503
|
|
|
404
|
-
// Normalize path — encode spaces in scheme:// paths
|
|
405
504
|
const rawTarget = cmd.path || cmd.command || cmd.question || "";
|
|
406
|
-
const target = rawTarget
|
|
407
|
-
? rawTarget.replace(
|
|
408
|
-
/:\/\/(.*)$/,
|
|
409
|
-
(_, rest) => `://${encodeURIComponent(decodeURIComponent(rest))}`,
|
|
410
|
-
)
|
|
411
|
-
: rawTarget;
|
|
505
|
+
const target = rawTarget;
|
|
412
506
|
const resultPath = await this.#knownStore.dedup(runId, scheme, target);
|
|
413
507
|
|
|
414
508
|
// Pass parsed command fields through as attributes
|
|
@@ -420,7 +514,9 @@ export default class TurnExecutor {
|
|
|
420
514
|
if (!cmd.body) return null;
|
|
421
515
|
const knownPath =
|
|
422
516
|
cmd.path || (await this.#knownStore.slugPath(runId, "known", cmd.body));
|
|
423
|
-
await this.#knownStore.upsert(runId, turn, knownPath, cmd.body,
|
|
517
|
+
await this.#knownStore.upsert(runId, turn, knownPath, cmd.body, 200, {
|
|
518
|
+
loopId,
|
|
519
|
+
});
|
|
424
520
|
return {
|
|
425
521
|
scheme: "known",
|
|
426
522
|
path: knownPath,
|
|
@@ -430,11 +526,11 @@ export default class TurnExecutor {
|
|
|
430
526
|
};
|
|
431
527
|
}
|
|
432
528
|
|
|
433
|
-
// Record the entry
|
|
529
|
+
// Record the entry — 200 OK, handlers change status during dispatch
|
|
434
530
|
const body = cmd.body || cmd.command || cmd.question || "";
|
|
435
|
-
|
|
436
|
-
await this.#knownStore.upsert(runId, turn, resultPath, body, state, {
|
|
531
|
+
await this.#knownStore.upsert(runId, turn, resultPath, body, 200, {
|
|
437
532
|
attributes,
|
|
533
|
+
loopId,
|
|
438
534
|
});
|
|
439
535
|
|
|
440
536
|
return {
|
|
@@ -442,16 +538,8 @@ export default class TurnExecutor {
|
|
|
442
538
|
path: resultPath,
|
|
443
539
|
body,
|
|
444
540
|
attributes,
|
|
445
|
-
|
|
541
|
+
status: 200,
|
|
446
542
|
resultPath,
|
|
447
543
|
};
|
|
448
544
|
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Initial state for a recorded command entry.
|
|
452
|
-
* All entries start at "full". Handlers change state during dispatch.
|
|
453
|
-
*/
|
|
454
|
-
#initialState(_scheme) {
|
|
455
|
-
return "full";
|
|
456
|
-
}
|
|
457
545
|
}
|
package/src/agent/XmlParser.js
CHANGED
|
@@ -190,6 +190,16 @@ export default class XmlParser {
|
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// Known tool opened while another is still open — close the old one.
|
|
194
|
+
if (current) {
|
|
195
|
+
warnings.push(
|
|
196
|
+
`Unclosed <${current.name}> before <${name}> — recovered`,
|
|
197
|
+
);
|
|
198
|
+
commands.push(
|
|
199
|
+
resolveCommand(current.name, current.attrs, current.rawBody),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
193
203
|
current = { name, attrs, rawBody: "" };
|
|
194
204
|
},
|
|
195
205
|
|
|
@@ -210,6 +220,16 @@ export default class XmlParser {
|
|
|
210
220
|
resolveCommand(current.name, current.attrs, current.rawBody),
|
|
211
221
|
);
|
|
212
222
|
current = null;
|
|
223
|
+
} else if (current && ALL_TOOLS.has(name)) {
|
|
224
|
+
// Mismatched close tag for a known tool — close current tag,
|
|
225
|
+
// don't swallow subsequent commands as body text.
|
|
226
|
+
warnings.push(
|
|
227
|
+
`Mismatched </${name}> closing <${current.name}> — recovered`,
|
|
228
|
+
);
|
|
229
|
+
commands.push(
|
|
230
|
+
resolveCommand(current.name, current.attrs, current.rawBody),
|
|
231
|
+
);
|
|
232
|
+
current = null;
|
|
213
233
|
} else if (current) {
|
|
214
234
|
current.rawBody += `</${name}>`;
|
|
215
235
|
} else if (isImplied && ALL_TOOLS.has(name)) {
|
|
@@ -17,25 +17,26 @@ SELECT path, body, attributes, turn
|
|
|
17
17
|
FROM known_entries
|
|
18
18
|
WHERE
|
|
19
19
|
run_id = :run_id
|
|
20
|
-
AND
|
|
20
|
+
AND status = 202;
|
|
21
21
|
|
|
22
22
|
-- PREP: has_rejections
|
|
23
23
|
SELECT COUNT(*) AS count
|
|
24
24
|
FROM known_entries
|
|
25
25
|
WHERE
|
|
26
26
|
run_id = :run_id
|
|
27
|
-
AND
|
|
27
|
+
AND loop_id = :loop_id
|
|
28
|
+
AND status = 403;
|
|
28
29
|
|
|
29
30
|
-- PREP: has_accepted_actions
|
|
30
31
|
SELECT COUNT(*) AS count
|
|
31
32
|
FROM known_entries
|
|
32
33
|
WHERE
|
|
33
34
|
run_id = :run_id
|
|
34
|
-
AND
|
|
35
|
+
AND status = 200
|
|
35
36
|
AND scheme IN ('set', 'sh', 'rm', 'mv', 'cp');
|
|
36
37
|
|
|
37
38
|
-- PREP: get_file_entries
|
|
38
|
-
SELECT path,
|
|
39
|
+
SELECT path, status, fidelity, hash, updated_at
|
|
39
40
|
FROM known_entries
|
|
40
41
|
WHERE
|
|
41
42
|
run_id = :run_id
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
-- PREP: get_known_entries
|
|
2
|
-
SELECT path, scheme,
|
|
2
|
+
SELECT path, scheme, status, fidelity, body, turn, hash, attributes
|
|
3
3
|
FROM known_entries
|
|
4
4
|
WHERE run_id = :run_id
|
|
5
5
|
ORDER BY path;
|
|
@@ -18,7 +18,7 @@ WHERE
|
|
|
18
18
|
ORDER BY id;
|
|
19
19
|
|
|
20
20
|
-- PREP: get_turn_audit
|
|
21
|
-
SELECT path, scheme,
|
|
21
|
+
SELECT path, scheme, status, fidelity, turn, body, attributes
|
|
22
22
|
FROM known_entries
|
|
23
23
|
WHERE
|
|
24
24
|
run_id = :run_id
|
|
@@ -57,12 +57,13 @@ SELECT body
|
|
|
57
57
|
FROM known_entries
|
|
58
58
|
WHERE
|
|
59
59
|
run_id = :run_id
|
|
60
|
+
AND loop_id = :loop_id
|
|
60
61
|
AND scheme = 'summarize'
|
|
61
62
|
ORDER BY id DESC
|
|
62
63
|
LIMIT 1;
|
|
63
64
|
|
|
64
65
|
-- PREP: get_history
|
|
65
|
-
SELECT ke.path, ke.
|
|
66
|
+
SELECT ke.path, ke.status, ke.body, ke.attributes, ke.turn
|
|
66
67
|
FROM known_entries AS ke
|
|
67
68
|
JOIN schemes AS s ON s.name = COALESCE(ke.scheme, 'file')
|
|
68
69
|
WHERE
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
-- PREP: upsert_known_entry
|
|
2
2
|
INSERT INTO known_entries (
|
|
3
|
-
run_id, turn, path, body,
|
|
4
|
-
, tokens, tokens_full, updated_at
|
|
3
|
+
run_id, loop_id, turn, path, body, status, fidelity, hash
|
|
4
|
+
, attributes, tokens, tokens_full, updated_at
|
|
5
5
|
)
|
|
6
6
|
VALUES (
|
|
7
|
-
:run_id, :turn, :path, :body, :
|
|
7
|
+
:run_id, :loop_id, :turn, :path, :body, :status, :fidelity, :hash
|
|
8
|
+
, COALESCE(:attributes, '{}')
|
|
8
9
|
, countTokens(:body)
|
|
9
10
|
, countTokens(:body)
|
|
10
11
|
, COALESCE(:updated_at, CURRENT_TIMESTAMP)
|
|
11
12
|
)
|
|
12
13
|
ON CONFLICT (run_id, path) DO UPDATE SET
|
|
13
14
|
body = excluded.body
|
|
14
|
-
,
|
|
15
|
+
, status = excluded.status
|
|
16
|
+
, fidelity = excluded.fidelity
|
|
15
17
|
, hash = COALESCE(excluded.hash, known_entries.hash)
|
|
16
18
|
, attributes = COALESCE(excluded.attributes, known_entries.attributes)
|
|
19
|
+
, loop_id = excluded.loop_id
|
|
17
20
|
, turn = excluded.turn
|
|
18
21
|
, tokens = countTokens(excluded.body)
|
|
19
22
|
, tokens_full = countTokens(excluded.body)
|
|
@@ -43,17 +46,17 @@ WHERE run_id = :run_id AND hedmatch(:pattern, path) AND scheme IS NULL;
|
|
|
43
46
|
-- PREP: resolve_known_entry
|
|
44
47
|
UPDATE known_entries
|
|
45
48
|
SET
|
|
46
|
-
|
|
49
|
+
status = :status
|
|
47
50
|
, body = :body
|
|
48
51
|
, updated_at = CURRENT_TIMESTAMP
|
|
49
52
|
WHERE run_id = :run_id AND path = :path;
|
|
50
53
|
|
|
51
|
-
-- PREP:
|
|
54
|
+
-- PREP: set_file_fidelity
|
|
52
55
|
UPDATE known_entries
|
|
53
56
|
SET
|
|
54
|
-
|
|
57
|
+
fidelity = :fidelity
|
|
55
58
|
, tokens = CASE
|
|
56
|
-
WHEN :
|
|
59
|
+
WHEN :fidelity = 'summary' THEN countTokens(body)
|
|
57
60
|
ELSE tokens_full
|
|
58
61
|
END
|
|
59
62
|
, updated_at = CURRENT_TIMESTAMP
|
|
@@ -62,7 +65,7 @@ WHERE run_id = :run_id AND hedmatch(:pattern, path) AND scheme IS NULL;
|
|
|
62
65
|
-- PREP: promote_path
|
|
63
66
|
UPDATE known_entries
|
|
64
67
|
SET
|
|
65
|
-
|
|
68
|
+
fidelity = 'full'
|
|
66
69
|
, turn = :turn
|
|
67
70
|
, tokens = tokens_full
|
|
68
71
|
, updated_at = CURRENT_TIMESTAMP
|
|
@@ -71,23 +74,34 @@ WHERE run_id = :run_id AND path = :path;
|
|
|
71
74
|
-- PREP: demote_path
|
|
72
75
|
UPDATE known_entries
|
|
73
76
|
SET
|
|
74
|
-
|
|
77
|
+
fidelity = 'stored'
|
|
75
78
|
, tokens = 0
|
|
76
79
|
, updated_at = CURRENT_TIMESTAMP
|
|
77
80
|
WHERE run_id = :run_id AND path = :path;
|
|
78
81
|
|
|
82
|
+
-- PREP: set_fidelity
|
|
83
|
+
UPDATE known_entries
|
|
84
|
+
SET
|
|
85
|
+
fidelity = :fidelity
|
|
86
|
+
, tokens = CASE
|
|
87
|
+
WHEN :fidelity = 'stored' THEN 0
|
|
88
|
+
ELSE countTokens(body)
|
|
89
|
+
END
|
|
90
|
+
, updated_at = CURRENT_TIMESTAMP
|
|
91
|
+
WHERE run_id = :run_id AND path = :path;
|
|
92
|
+
|
|
79
93
|
-- PREP: get_entry_body
|
|
80
94
|
SELECT body
|
|
81
95
|
FROM known_entries
|
|
82
96
|
WHERE run_id = :run_id AND path = :path;
|
|
83
97
|
|
|
84
98
|
-- PREP: get_entry_state
|
|
85
|
-
SELECT
|
|
99
|
+
SELECT status, fidelity, scheme, turn
|
|
86
100
|
FROM known_entries
|
|
87
101
|
WHERE run_id = :run_id AND path = :path;
|
|
88
102
|
|
|
89
103
|
-- PREP: get_file_states_by_pattern
|
|
90
|
-
SELECT path,
|
|
104
|
+
SELECT path, status, fidelity, turn
|
|
91
105
|
FROM known_entries
|
|
92
106
|
WHERE run_id = :run_id AND hedmatch(:pattern, path) AND scheme IS NULL
|
|
93
107
|
ORDER BY path;
|
|
@@ -107,7 +121,7 @@ WHERE run_id = :run_id AND path = :path;
|
|
|
107
121
|
-- PREP: promote_by_pattern
|
|
108
122
|
UPDATE known_entries
|
|
109
123
|
SET
|
|
110
|
-
|
|
124
|
+
fidelity = 'full'
|
|
111
125
|
, turn = :turn
|
|
112
126
|
, tokens = tokens_full
|
|
113
127
|
, updated_at = CURRENT_TIMESTAMP
|
|
@@ -119,7 +133,7 @@ WHERE
|
|
|
119
133
|
-- PREP: demote_by_pattern
|
|
120
134
|
UPDATE known_entries
|
|
121
135
|
SET
|
|
122
|
-
|
|
136
|
+
fidelity = 'stored'
|
|
123
137
|
, tokens = 0
|
|
124
138
|
, updated_at = CURRENT_TIMESTAMP
|
|
125
139
|
WHERE
|
|
@@ -128,7 +142,7 @@ WHERE
|
|
|
128
142
|
AND (:body IS NULL OR hedsearch(:body, body));
|
|
129
143
|
|
|
130
144
|
-- PREP: get_entries_by_pattern
|
|
131
|
-
SELECT path, body, scheme,
|
|
145
|
+
SELECT path, body, scheme, status, fidelity, tokens_full, attributes
|
|
132
146
|
FROM known_entries
|
|
133
147
|
WHERE
|
|
134
148
|
run_id = :run_id
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- PREP: enqueue_loop
|
|
2
|
+
INSERT INTO loops (run_id, sequence, mode, model, prompt, config)
|
|
3
|
+
VALUES (:run_id, :sequence, :mode, :model, :prompt, :config)
|
|
4
|
+
RETURNING id, sequence;
|
|
5
|
+
|
|
6
|
+
-- PREP: next_loop
|
|
7
|
+
UPDATE runs
|
|
8
|
+
SET next_loop = next_loop + 1
|
|
9
|
+
WHERE id = :run_id
|
|
10
|
+
RETURNING next_loop - 1 AS sequence;
|
|
11
|
+
|
|
12
|
+
-- PREP: claim_next_loop
|
|
13
|
+
UPDATE loops
|
|
14
|
+
SET status = 102
|
|
15
|
+
WHERE
|
|
16
|
+
id = (
|
|
17
|
+
SELECT
|
|
18
|
+
id
|
|
19
|
+
FROM loops
|
|
20
|
+
WHERE run_id = :run_id AND status = 100
|
|
21
|
+
ORDER BY id
|
|
22
|
+
LIMIT 1
|
|
23
|
+
)
|
|
24
|
+
RETURNING id, run_id, sequence, mode, model, prompt, config;
|
|
25
|
+
|
|
26
|
+
-- PREP: complete_loop
|
|
27
|
+
UPDATE loops
|
|
28
|
+
SET status = :status, result = :result
|
|
29
|
+
WHERE id = :id;
|
|
30
|
+
|
|
31
|
+
-- PREP: abort_active_loop
|
|
32
|
+
UPDATE loops
|
|
33
|
+
SET status = 499
|
|
34
|
+
WHERE run_id = :run_id AND status = 102;
|
|
35
|
+
|
|
36
|
+
-- PREP: get_pending_loops
|
|
37
|
+
SELECT id, sequence, mode, model, prompt, status, created_at
|
|
38
|
+
FROM loops
|
|
39
|
+
WHERE run_id = :run_id AND status IN (100, 102)
|
|
40
|
+
ORDER BY id;
|
|
41
|
+
|
|
42
|
+
-- PREP: reset_active_loops
|
|
43
|
+
UPDATE loops
|
|
44
|
+
SET status = 100
|
|
45
|
+
WHERE status = 102;
|
|
46
|
+
|
|
47
|
+
-- PREP: get_current_loop
|
|
48
|
+
SELECT id, sequence, mode, model, prompt, status
|
|
49
|
+
FROM loops
|
|
50
|
+
WHERE run_id = :run_id AND status = 102
|
|
51
|
+
LIMIT 1;
|
|
52
|
+
|
|
53
|
+
-- PREP: get_loop_by_id
|
|
54
|
+
SELECT id, run_id, sequence, mode, model, prompt, status, config
|
|
55
|
+
FROM loops
|
|
56
|
+
WHERE id = :id;
|
|
57
|
+
|
|
58
|
+
-- PREP: get_latest_completed_loop
|
|
59
|
+
SELECT id, sequence, mode, status
|
|
60
|
+
FROM loops
|
|
61
|
+
WHERE run_id = :run_id AND status IN (200, 500)
|
|
62
|
+
ORDER BY id DESC
|
|
63
|
+
LIMIT 1;
|
package/src/agent/runs.sql
CHANGED
|
@@ -22,14 +22,14 @@ RETURNING id;
|
|
|
22
22
|
-- PREP: get_run_by_alias
|
|
23
23
|
SELECT
|
|
24
24
|
id, project_id, parent_run_id, model, status, alias
|
|
25
|
-
, temperature, persona, context_limit, next_turn, created_at
|
|
25
|
+
, temperature, persona, context_limit, next_turn, next_loop, created_at
|
|
26
26
|
FROM runs
|
|
27
27
|
WHERE alias = :alias;
|
|
28
28
|
|
|
29
29
|
-- PREP: get_run_by_id
|
|
30
30
|
SELECT
|
|
31
31
|
id, project_id, parent_run_id, model, status, alias
|
|
32
|
-
, temperature, persona, context_limit, next_turn, created_at
|
|
32
|
+
, temperature, persona, context_limit, next_turn, next_loop, created_at
|
|
33
33
|
FROM runs
|
|
34
34
|
WHERE id = :id;
|
|
35
35
|
|
|
@@ -80,11 +80,11 @@ RETURNING next_turn - 1 AS turn;
|
|
|
80
80
|
|
|
81
81
|
-- PREP: fork_known_entries
|
|
82
82
|
INSERT INTO known_entries (
|
|
83
|
-
run_id, turn, path, body,
|
|
83
|
+
run_id, loop_id, turn, path, body, status, fidelity
|
|
84
84
|
, hash, attributes, tokens, tokens_full, refs, write_count
|
|
85
85
|
)
|
|
86
86
|
SELECT
|
|
87
|
-
:new_run_id, turn, path, body,
|
|
87
|
+
:new_run_id, NULL, turn, path, body, status, fidelity
|
|
88
88
|
, hash, attributes, tokens, tokens_full, refs, write_count
|
|
89
89
|
FROM known_entries
|
|
90
90
|
WHERE run_id = :parent_run_id;
|
|
@@ -94,7 +94,7 @@ SELECT r.id
|
|
|
94
94
|
FROM runs AS r
|
|
95
95
|
WHERE
|
|
96
96
|
r.project_id = :project_id
|
|
97
|
-
AND r.status IN (
|
|
97
|
+
AND r.status IN (100, 102, 202);
|
|
98
98
|
|
|
99
99
|
-- PREP: get_latest_run
|
|
100
100
|
SELECT r.id
|
|
@@ -110,5 +110,5 @@ WHERE r.project_id = :project_id;
|
|
|
110
110
|
|
|
111
111
|
-- PREP: abort_stuck_runs
|
|
112
112
|
UPDATE runs
|
|
113
|
-
SET status =
|
|
114
|
-
WHERE status IN (
|
|
113
|
+
SET status = 499
|
|
114
|
+
WHERE status IN (100, 102);
|
package/src/agent/schemes.sql
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
-- PREP: upsert_scheme
|
|
2
|
-
INSERT OR REPLACE INTO schemes (name,
|
|
3
|
-
VALUES (:name, :
|
|
2
|
+
INSERT OR REPLACE INTO schemes (name, model_visible, category)
|
|
3
|
+
VALUES (:name, :model_visible, :category);
|
package/src/agent/turns.sql
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
-- PREP: create_turn
|
|
2
|
-
INSERT INTO turns (run_id, sequence)
|
|
3
|
-
VALUES (:run_id, :sequence)
|
|
2
|
+
INSERT INTO turns (run_id, loop_id, sequence)
|
|
3
|
+
VALUES (:run_id, :loop_id, :sequence)
|
|
4
4
|
RETURNING id, sequence;
|
|
5
5
|
|
|
6
6
|
-- PREP: update_turn_stats
|
|
@@ -26,7 +26,7 @@ FROM turns
|
|
|
26
26
|
WHERE run_id = :run_id;
|
|
27
27
|
|
|
28
28
|
-- PREP: get_run_log
|
|
29
|
-
SELECT ke.path, ke.
|
|
29
|
+
SELECT ke.path, ke.status, ke.body, ke.attributes
|
|
30
30
|
FROM known_entries AS ke
|
|
31
31
|
JOIN schemes AS s ON s.name = COALESCE(ke.scheme, 'file')
|
|
32
32
|
WHERE
|
|
@@ -48,18 +48,10 @@ export default class PluginContext {
|
|
|
48
48
|
return this.#schemes;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
registerScheme({
|
|
52
|
-
name,
|
|
53
|
-
fidelity = "full",
|
|
54
|
-
modelVisible = 1,
|
|
55
|
-
validStates = ["full", "proposed", "pass", "rejected", "error"],
|
|
56
|
-
category = "result",
|
|
57
|
-
} = {}) {
|
|
51
|
+
registerScheme({ name, modelVisible = 1, category = "result" } = {}) {
|
|
58
52
|
this.#schemes.push({
|
|
59
53
|
name: name || this.#name,
|
|
60
|
-
fidelity,
|
|
61
54
|
model_visible: modelVisible,
|
|
62
|
-
valid_states: JSON.stringify(validStates),
|
|
63
55
|
category,
|
|
64
56
|
});
|
|
65
57
|
}
|
|
@@ -78,7 +70,6 @@ export default class PluginContext {
|
|
|
78
70
|
return;
|
|
79
71
|
}
|
|
80
72
|
if (event === "full" || event === "summary") {
|
|
81
|
-
this.#hooks.tools.ensureTool(this.#name);
|
|
82
73
|
this.#hooks.tools.onView(this.#name, callback, event);
|
|
83
74
|
return;
|
|
84
75
|
}
|