@possumtech/rummy 2.0.0 → 2.1.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.
- package/.env.example +31 -5
- package/BENCH_ENVIRONMENT.md +230 -0
- package/CLIENT_INTERFACE.md +396 -0
- package/PLUGINS.md +93 -1
- package/SPEC.md +389 -28
- package/bin/postinstall.js +2 -2
- package/bin/rummy.js +2 -2
- package/last_run.txt +5617 -0
- package/migrations/001_initial_schema.sql +2 -1
- package/package.json +13 -9
- package/scriptify/ask_run.js +77 -0
- package/scriptify/cache_probe.js +66 -0
- package/scriptify/cache_probe_grok.js +74 -0
- package/service.js +22 -11
- package/src/agent/AgentLoop.js +62 -157
- package/src/agent/ContextAssembler.js +2 -9
- package/src/agent/Entries.js +54 -98
- package/src/agent/ProjectAgent.js +4 -11
- package/src/agent/TurnExecutor.js +48 -83
- package/src/agent/XmlParser.js +247 -273
- package/src/agent/budget.js +5 -28
- package/src/agent/config.js +38 -0
- package/src/agent/errors.js +7 -13
- package/src/agent/httpStatus.js +1 -19
- package/src/agent/known_queries.sql +1 -1
- package/src/agent/known_store.sql +12 -2
- package/src/agent/materializeContext.js +15 -18
- package/src/agent/pathEncode.js +5 -0
- package/src/agent/rummyHome.js +9 -0
- package/src/agent/runs.sql +37 -0
- package/src/agent/tokens.js +7 -7
- package/src/hooks/HookRegistry.js +1 -16
- package/src/hooks/Hooks.js +8 -33
- package/src/hooks/PluginContext.js +3 -21
- package/src/hooks/RpcRegistry.js +1 -4
- package/src/hooks/RummyContext.js +6 -16
- package/src/hooks/ToolRegistry.js +5 -15
- package/src/llm/LlmProvider.js +41 -33
- package/src/llm/errors.js +41 -4
- package/src/llm/openaiStream.js +125 -0
- package/src/llm/retry.js +109 -0
- package/src/plugins/budget/budget.js +55 -76
- package/src/plugins/cli/README.md +87 -0
- package/src/plugins/cli/bin.js +61 -0
- package/src/plugins/cli/cli.js +120 -0
- package/src/plugins/env/README.md +2 -1
- package/src/plugins/env/env.js +4 -6
- package/src/plugins/env/envDoc.md +2 -2
- package/src/plugins/error/error.js +23 -23
- package/src/plugins/file/file.js +2 -22
- package/src/plugins/get/get.js +12 -34
- package/src/plugins/get/getDoc.md +8 -6
- package/src/plugins/hedberg/edits.js +1 -11
- package/src/plugins/hedberg/hedberg.js +3 -26
- package/src/plugins/hedberg/normalize.js +1 -5
- package/src/plugins/hedberg/patterns.js +4 -15
- package/src/plugins/hedberg/sed.js +1 -7
- package/src/plugins/helpers.js +28 -20
- package/src/plugins/index.js +25 -41
- package/src/plugins/instructions/README.md +18 -0
- package/src/plugins/instructions/instructions.js +97 -38
- package/src/plugins/instructions/instructions.md +24 -15
- package/src/plugins/instructions/instructions_104.md +5 -4
- package/src/plugins/instructions/instructions_105.md +29 -36
- package/src/plugins/instructions/instructions_106.md +22 -0
- package/src/plugins/instructions/instructions_107.md +17 -0
- package/src/plugins/instructions/instructions_108.md +0 -8
- package/src/plugins/known/README.md +26 -6
- package/src/plugins/known/known.js +37 -34
- package/src/plugins/log/README.md +2 -2
- package/src/plugins/log/log.js +27 -34
- package/src/plugins/ollama/ollama.js +50 -66
- package/src/plugins/openai/openai.js +26 -44
- package/src/plugins/openrouter/openrouter.js +28 -52
- package/src/plugins/policy/README.md +8 -2
- package/src/plugins/policy/policy.js +8 -21
- package/src/plugins/prompt/README.md +22 -0
- package/src/plugins/prompt/prompt.js +14 -16
- package/src/plugins/rm/rm.js +5 -2
- package/src/plugins/rm/rmDoc.md +4 -4
- package/src/plugins/rpc/README.md +2 -1
- package/src/plugins/rpc/rpc.js +62 -48
- package/src/plugins/set/README.md +5 -1
- package/src/plugins/set/set.js +23 -33
- package/src/plugins/set/setDoc.md +1 -1
- package/src/plugins/sh/README.md +2 -1
- package/src/plugins/sh/sh.js +5 -11
- package/src/plugins/sh/shDoc.md +2 -2
- package/src/plugins/stream/README.md +6 -5
- package/src/plugins/stream/stream.js +6 -35
- package/src/plugins/telemetry/telemetry.js +26 -19
- package/src/plugins/think/think.js +4 -7
- package/src/plugins/unknown/unknown.js +8 -13
- package/src/plugins/update/update.js +42 -25
- package/src/plugins/update/updateDoc.md +3 -3
- package/src/plugins/xai/xai.js +30 -20
- package/src/plugins/yolo/yolo.js +159 -0
- package/src/server/ClientConnection.js +17 -47
- package/src/server/SocketServer.js +14 -14
- package/src/server/protocol.js +1 -10
- package/src/sql/functions/slugify.js +5 -7
- package/src/sql/v_model_context.sql +4 -11
- package/turns/cli_1777462658211/turn_001.txt +772 -0
- package/turns/cli_1777462658211/turn_002.txt +606 -0
- package/turns/cli_1777462658211/turn_003.txt +667 -0
- package/turns/cli_1777462658211/turn_004.txt +297 -0
- package/turns/cli_1777462658211/turn_005.txt +301 -0
- package/turns/cli_1777462658211/turn_006.txt +262 -0
- package/turns/cli_1777465095132/turn_001.txt +715 -0
- package/turns/cli_1777465095132/turn_002.txt +236 -0
- package/turns/cli_1777465095132/turn_003.txt +287 -0
- package/turns/cli_1777465095132/turn_004.txt +694 -0
- package/turns/cli_1777465095132/turn_005.txt +422 -0
- package/turns/cli_1777465095132/turn_006.txt +365 -0
- package/turns/cli_1777465095132/turn_007.txt +885 -0
- package/turns/cli_1777465095132/turn_008.txt +1277 -0
- package/turns/cli_1777465095132/turn_009.txt +736 -0
package/src/agent/AgentLoop.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { computeBudget } from "./budget.js";
|
|
2
1
|
import msg from "./messages.js";
|
|
3
2
|
|
|
4
3
|
const HTTP_TO_RUN_STATE = {
|
|
@@ -31,20 +30,13 @@ export default class AgentLoop {
|
|
|
31
30
|
if (active) active.controller.abort();
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
* Abort every in-flight run and wait for each drain to settle.
|
|
36
|
-
* Called from server close / client teardown so the process can
|
|
37
|
-
* exit cleanly instead of leaving detached kickoff Promises
|
|
38
|
-
* pinning the event loop.
|
|
39
|
-
*/
|
|
33
|
+
// Abort all in-flight runs and drain; rejections were already surfaced to original awaiters.
|
|
40
34
|
async abortAll() {
|
|
41
35
|
const promises = [];
|
|
42
36
|
for (const { controller, promise } of this.#activeRuns.values()) {
|
|
43
37
|
controller.abort();
|
|
44
38
|
promises.push(promise);
|
|
45
39
|
}
|
|
46
|
-
// allSettled: drain waits for every run to finish; rejections are
|
|
47
|
-
// already surfaced to whoever awaited the original run() call.
|
|
48
40
|
await Promise.allSettled(promises);
|
|
49
41
|
}
|
|
50
42
|
|
|
@@ -56,6 +48,24 @@ export default class AgentLoop {
|
|
|
56
48
|
return `Turn ${turn}/${maxTurns}`;
|
|
57
49
|
}
|
|
58
50
|
|
|
51
|
+
async #emitCompleted(hook, projectId, runId, out) {
|
|
52
|
+
const s = await this.#db.get_run_summary.get({ id: runId });
|
|
53
|
+
await hook.completed.emit({
|
|
54
|
+
projectId,
|
|
55
|
+
...out,
|
|
56
|
+
model: s.model,
|
|
57
|
+
turns: s.turns,
|
|
58
|
+
cost: s.cost,
|
|
59
|
+
tokens: {
|
|
60
|
+
prompt: s.prompt_tokens,
|
|
61
|
+
cached: s.cached_tokens,
|
|
62
|
+
completion: s.completion_tokens,
|
|
63
|
+
reasoning: s.reasoning_tokens,
|
|
64
|
+
total: s.total_tokens,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
59
69
|
async #setRunStatus(runId, alias, httpStatus) {
|
|
60
70
|
await this.#db.update_run_status.run({ id: runId, status: httpStatus });
|
|
61
71
|
const state = HTTP_TO_RUN_STATE[httpStatus];
|
|
@@ -68,88 +78,6 @@ export default class AgentLoop {
|
|
|
68
78
|
});
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
async #emitRunState({
|
|
72
|
-
projectId,
|
|
73
|
-
runId,
|
|
74
|
-
alias,
|
|
75
|
-
turn,
|
|
76
|
-
status,
|
|
77
|
-
contextSize,
|
|
78
|
-
result = null,
|
|
79
|
-
}) {
|
|
80
|
-
if (!contextSize) throw new Error("#emitRunState: contextSize is required");
|
|
81
|
-
const runUsage = await this.#db.get_run_usage.get({ run_id: runId });
|
|
82
|
-
const history = await this.#entries.getLog(runId);
|
|
83
|
-
const unknowns = await this.#entries.getUnknowns(runId);
|
|
84
|
-
const latestSummary = history
|
|
85
|
-
.filter((e) => {
|
|
86
|
-
// Updates are under the unified log namespace at
|
|
87
|
-
// log://turn_N/update/<slug>. Match by path pattern rather
|
|
88
|
-
// than scheme (scheme is now "log" for all log entries).
|
|
89
|
-
if (!/^log:\/\/turn_\d+\/update\//.test(e.path)) return false;
|
|
90
|
-
const attrs =
|
|
91
|
-
typeof e.attributes === "string"
|
|
92
|
-
? JSON.parse(e.attributes)
|
|
93
|
-
: e.attributes;
|
|
94
|
-
return attrs?.status === 200;
|
|
95
|
-
})
|
|
96
|
-
.at(-1);
|
|
97
|
-
|
|
98
|
-
// Always emit complete telemetry. When we don't have a fresh turn
|
|
99
|
-
// result (abort/max-turns/crash), read the last turn's context
|
|
100
|
-
// tokens from the DB instead. Both code paths compute a real
|
|
101
|
-
// budget from real data — never undefined, never invented.
|
|
102
|
-
const rows = await this.#db.get_turn_context.all({
|
|
103
|
-
run_id: runId,
|
|
104
|
-
turn,
|
|
105
|
-
});
|
|
106
|
-
let totalTokens;
|
|
107
|
-
if (result) {
|
|
108
|
-
totalTokens = result.assembledTokens;
|
|
109
|
-
} else {
|
|
110
|
-
// No fresh turn result — this happens on abort/max-turns/crash
|
|
111
|
-
// emits that fire before any turn executed, or after a turn
|
|
112
|
-
// that never produced tokens. Read the last turn's assembled
|
|
113
|
-
// context_tokens from the DB; absent means no turn ran yet
|
|
114
|
-
// (zero is the truth, not a fallback).
|
|
115
|
-
const lastCtx = await this.#db.get_last_context_tokens.get({
|
|
116
|
-
run_id: runId,
|
|
117
|
-
});
|
|
118
|
-
totalTokens = lastCtx ? lastCtx.context_tokens : 0;
|
|
119
|
-
}
|
|
120
|
-
const budget = computeBudget({ rows, contextSize, totalTokens });
|
|
121
|
-
|
|
122
|
-
await this.#hooks.run.state.emit({
|
|
123
|
-
projectId,
|
|
124
|
-
run: alias,
|
|
125
|
-
turn,
|
|
126
|
-
status,
|
|
127
|
-
summary: latestSummary?.body,
|
|
128
|
-
history,
|
|
129
|
-
unknowns: unknowns.map((u) => ({ path: u.path, body: u.body })),
|
|
130
|
-
telemetry: {
|
|
131
|
-
modelAlias: result?.modelAlias,
|
|
132
|
-
model: result?.model,
|
|
133
|
-
temperature: result?.temperature,
|
|
134
|
-
context_size: contextSize,
|
|
135
|
-
context_tokens: totalTokens,
|
|
136
|
-
ceiling: budget.ceiling,
|
|
137
|
-
token_usage: budget.tokenUsage,
|
|
138
|
-
tokens_free: budget.tokensFree,
|
|
139
|
-
prompt_tokens: runUsage.prompt_tokens,
|
|
140
|
-
cached_tokens: runUsage.cached_tokens,
|
|
141
|
-
completion_tokens: runUsage.completion_tokens,
|
|
142
|
-
reasoning_tokens: runUsage.reasoning_tokens,
|
|
143
|
-
total_tokens: runUsage.total_tokens,
|
|
144
|
-
cost: runUsage.cost,
|
|
145
|
-
context_distribution: await this.#db.get_turn_distribution.all({
|
|
146
|
-
run_id: runId,
|
|
147
|
-
turn,
|
|
148
|
-
}),
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
81
|
async #writeRunEntry(
|
|
154
82
|
runId,
|
|
155
83
|
alias,
|
|
@@ -227,7 +155,6 @@ export default class AgentLoop {
|
|
|
227
155
|
const existing = this.#activeRuns.get(existingRun.id);
|
|
228
156
|
if (existing) existing.controller.abort();
|
|
229
157
|
|
|
230
|
-
// Clean up stale proposals from interrupted runs
|
|
231
158
|
const unresolved = await this.#entries.getUnresolved(existingRun.id);
|
|
232
159
|
for (const u of unresolved) {
|
|
233
160
|
await this.#entries.set({
|
|
@@ -240,7 +167,6 @@ export default class AgentLoop {
|
|
|
240
167
|
}
|
|
241
168
|
return { runId: existingRun.id, alias: existingRun.alias };
|
|
242
169
|
}
|
|
243
|
-
// Client-specified alias for a brand-new run — accept it verbatim.
|
|
244
170
|
}
|
|
245
171
|
|
|
246
172
|
const alias = run ? run : await this.#generateAlias(requestedModel);
|
|
@@ -287,10 +213,13 @@ export default class AgentLoop {
|
|
|
287
213
|
if (!project)
|
|
288
214
|
throw new Error(msg("error.project_not_found", { projectId }));
|
|
289
215
|
|
|
290
|
-
const noRepo = options?.noRepo ===
|
|
291
|
-
const noInteraction =
|
|
292
|
-
|
|
293
|
-
const
|
|
216
|
+
const noRepo = options?.noRepo ?? process.env.RUMMY_NO_REPO === "1";
|
|
217
|
+
const noInteraction =
|
|
218
|
+
options?.noInteraction ?? process.env.RUMMY_NO_INTERACTION === "1";
|
|
219
|
+
const noWeb = options?.noWeb ?? process.env.RUMMY_NO_WEB === "1";
|
|
220
|
+
const noProposals =
|
|
221
|
+
options?.noProposals ?? process.env.RUMMY_NO_PROPOSALS === "1";
|
|
222
|
+
const yolo = options?.yolo ?? process.env.RUMMY_YOLO === "1";
|
|
294
223
|
const requestedModel = model;
|
|
295
224
|
|
|
296
225
|
const runInfo = await this.ensureRun(
|
|
@@ -314,6 +243,7 @@ export default class AgentLoop {
|
|
|
314
243
|
noInteraction,
|
|
315
244
|
noWeb,
|
|
316
245
|
noProposals,
|
|
246
|
+
yolo,
|
|
317
247
|
temperature: options?.temperature,
|
|
318
248
|
}),
|
|
319
249
|
});
|
|
@@ -322,8 +252,7 @@ export default class AgentLoop {
|
|
|
322
252
|
return { run: currentAlias, status: 100 };
|
|
323
253
|
}
|
|
324
254
|
|
|
325
|
-
//
|
|
326
|
-
// reach both — abort the controller, await the Promise's drain.
|
|
255
|
+
// Pair controller + Promise so abortAll can both signal and await drain.
|
|
327
256
|
const controller = new AbortController();
|
|
328
257
|
const promise = this.#drainQueue(
|
|
329
258
|
currentRunId,
|
|
@@ -367,6 +296,7 @@ export default class AgentLoop {
|
|
|
367
296
|
noInteraction = false,
|
|
368
297
|
noWeb = false,
|
|
369
298
|
noProposals = false,
|
|
299
|
+
yolo = false,
|
|
370
300
|
} = loopConfig;
|
|
371
301
|
|
|
372
302
|
let result;
|
|
@@ -384,6 +314,7 @@ export default class AgentLoop {
|
|
|
384
314
|
noInteraction,
|
|
385
315
|
noWeb,
|
|
386
316
|
noProposals,
|
|
317
|
+
yolo,
|
|
387
318
|
options: { ...options, temperature: loopConfig.temperature },
|
|
388
319
|
hook,
|
|
389
320
|
signal: controller.signal,
|
|
@@ -448,6 +379,7 @@ export default class AgentLoop {
|
|
|
448
379
|
noInteraction,
|
|
449
380
|
noWeb,
|
|
450
381
|
noProposals,
|
|
382
|
+
yolo,
|
|
451
383
|
options,
|
|
452
384
|
hook,
|
|
453
385
|
signal,
|
|
@@ -470,7 +402,7 @@ export default class AgentLoop {
|
|
|
470
402
|
});
|
|
471
403
|
|
|
472
404
|
let loopIteration = 0;
|
|
473
|
-
const
|
|
405
|
+
const MAX_LOOP_TURNS = Number(process.env.RUMMY_MAX_LOOP_TURNS);
|
|
474
406
|
|
|
475
407
|
await this.#hooks.loop.started.emit({
|
|
476
408
|
runId: currentRunId,
|
|
@@ -480,31 +412,23 @@ export default class AgentLoop {
|
|
|
480
412
|
});
|
|
481
413
|
|
|
482
414
|
try {
|
|
483
|
-
while (loopIteration <
|
|
415
|
+
while (loopIteration < MAX_LOOP_TURNS) {
|
|
484
416
|
if (signal.aborted) {
|
|
485
417
|
console.error(
|
|
486
418
|
`[LOOP] ${currentAlias} iter=${loopIteration} ABORT via signal`,
|
|
487
419
|
);
|
|
488
420
|
await this.#setRunStatus(currentRunId, currentAlias, 499);
|
|
489
|
-
await this.#emitRunState({
|
|
490
|
-
projectId,
|
|
491
|
-
runId: currentRunId,
|
|
492
|
-
alias: currentAlias,
|
|
493
|
-
turn: loopIteration,
|
|
494
|
-
status: 499,
|
|
495
|
-
contextSize,
|
|
496
|
-
});
|
|
497
421
|
const out = {
|
|
498
422
|
run: currentAlias,
|
|
499
423
|
status: 499,
|
|
500
424
|
turn: loopIteration,
|
|
501
425
|
};
|
|
502
|
-
await hook
|
|
426
|
+
await this.#emitCompleted(hook, projectId, currentRunId, out);
|
|
503
427
|
return out;
|
|
504
428
|
}
|
|
505
429
|
loopIteration++;
|
|
506
430
|
console.error(
|
|
507
|
-
`[LOOP] ${currentAlias} iter=${loopIteration} ENTER (max=${
|
|
431
|
+
`[LOOP] ${currentAlias} iter=${loopIteration} ENTER (max=${MAX_LOOP_TURNS})`,
|
|
508
432
|
);
|
|
509
433
|
|
|
510
434
|
let turnPrompt;
|
|
@@ -513,7 +437,7 @@ export default class AgentLoop {
|
|
|
513
437
|
} else {
|
|
514
438
|
turnPrompt = this.#buildContinuationPrompt(
|
|
515
439
|
loopIteration,
|
|
516
|
-
|
|
440
|
+
MAX_LOOP_TURNS,
|
|
517
441
|
);
|
|
518
442
|
}
|
|
519
443
|
|
|
@@ -534,6 +458,7 @@ export default class AgentLoop {
|
|
|
534
458
|
noWeb,
|
|
535
459
|
noInteraction,
|
|
536
460
|
noProposals,
|
|
461
|
+
yolo,
|
|
537
462
|
toolSet,
|
|
538
463
|
contextSize,
|
|
539
464
|
options: { ...options, isContinuation: loopIteration > 1 },
|
|
@@ -557,15 +482,6 @@ export default class AgentLoop {
|
|
|
557
482
|
`[LOOP] ${currentAlias} iter=${loopIteration} verdict: continue=${verdict.continue} status=${vStatus} reason=${vReason}`,
|
|
558
483
|
);
|
|
559
484
|
|
|
560
|
-
await this.#emitRunState({
|
|
561
|
-
projectId,
|
|
562
|
-
runId: currentRunId,
|
|
563
|
-
alias: currentAlias,
|
|
564
|
-
turn: result.turn,
|
|
565
|
-
status: verdict.continue ? 102 : verdict.status,
|
|
566
|
-
contextSize,
|
|
567
|
-
result,
|
|
568
|
-
});
|
|
569
485
|
await this.#hooks.run.step.completed.emit({
|
|
570
486
|
projectId,
|
|
571
487
|
run: currentAlias,
|
|
@@ -592,41 +508,24 @@ export default class AgentLoop {
|
|
|
592
508
|
turn: result.turn,
|
|
593
509
|
reason: verdict.reason,
|
|
594
510
|
};
|
|
595
|
-
await hook
|
|
511
|
+
await this.#emitCompleted(hook, projectId, currentRunId, out);
|
|
596
512
|
return out;
|
|
597
513
|
}
|
|
598
514
|
|
|
599
|
-
// MAX_TURNS exhaustion without a terminal update is abandonment.
|
|
600
515
|
console.error(
|
|
601
|
-
`[LOOP] ${currentAlias} hit
|
|
516
|
+
`[LOOP] ${currentAlias} hit MAX_LOOP_TURNS=${MAX_LOOP_TURNS} — abandoning at 499`,
|
|
602
517
|
);
|
|
603
518
|
await this.#setRunStatus(currentRunId, currentAlias, 499);
|
|
604
|
-
await this.#emitRunState({
|
|
605
|
-
projectId,
|
|
606
|
-
runId: currentRunId,
|
|
607
|
-
alias: currentAlias,
|
|
608
|
-
turn: loopIteration,
|
|
609
|
-
status: 499,
|
|
610
|
-
contextSize,
|
|
611
|
-
});
|
|
612
519
|
const out = {
|
|
613
520
|
run: currentAlias,
|
|
614
521
|
status: 499,
|
|
615
522
|
turn: loopIteration,
|
|
616
523
|
};
|
|
617
|
-
await hook
|
|
524
|
+
await this.#emitCompleted(hook, projectId, currentRunId, out);
|
|
618
525
|
return out;
|
|
619
526
|
} catch (err) {
|
|
620
527
|
const status = signal.aborted ? 499 : 500;
|
|
621
528
|
await this.#setRunStatus(currentRunId, currentAlias, status);
|
|
622
|
-
await this.#emitRunState({
|
|
623
|
-
projectId,
|
|
624
|
-
runId: currentRunId,
|
|
625
|
-
alias: currentAlias,
|
|
626
|
-
turn: loopIteration,
|
|
627
|
-
status,
|
|
628
|
-
contextSize,
|
|
629
|
-
});
|
|
630
529
|
if (status === 500) {
|
|
631
530
|
await this.#hooks.error.log.emit({
|
|
632
531
|
store: this.#entries,
|
|
@@ -638,7 +537,7 @@ export default class AgentLoop {
|
|
|
638
537
|
}
|
|
639
538
|
const out = { run: currentAlias, status, turn: loopIteration };
|
|
640
539
|
if (status === 500) out.error = err.message;
|
|
641
|
-
await hook
|
|
540
|
+
await this.#emitCompleted(hook, projectId, currentRunId, out);
|
|
642
541
|
return out;
|
|
643
542
|
} finally {
|
|
644
543
|
await this.#hooks.loop.completed.emit({
|
|
@@ -678,11 +577,7 @@ export default class AgentLoop {
|
|
|
678
577
|
db: this.#db,
|
|
679
578
|
entries: this.#entries,
|
|
680
579
|
});
|
|
681
|
-
//
|
|
682
|
-
// client's dispatch handler doesn't mistake a successful
|
|
683
|
-
// resolve's HTTP-style 200 ack for a terminal run status and
|
|
684
|
-
// prematurely close the document. Real terminal state comes
|
|
685
|
-
// from the run/state notification at end-of-turn.
|
|
580
|
+
// Return current run status (not 200) so client doesn't close on resolve ack.
|
|
686
581
|
return { run: runAlias, status: runRow.status };
|
|
687
582
|
}
|
|
688
583
|
|
|
@@ -702,8 +597,7 @@ export default class AgentLoop {
|
|
|
702
597
|
entries: this.#entries,
|
|
703
598
|
};
|
|
704
599
|
|
|
705
|
-
//
|
|
706
|
-
// First veto wins: state=failed with plugin-supplied outcome + body.
|
|
600
|
+
// First plugin veto wins via proposal.accepting (e.g. readonly).
|
|
707
601
|
if (action === "accept") {
|
|
708
602
|
const veto = await this.#hooks.proposal.accepting.filter(null, ctx);
|
|
709
603
|
if (veto?.allow === false) {
|
|
@@ -718,9 +612,7 @@ export default class AgentLoop {
|
|
|
718
612
|
}
|
|
719
613
|
}
|
|
720
614
|
|
|
721
|
-
//
|
|
722
|
-
// override via proposal.content (e.g. set prefers the existing
|
|
723
|
-
// proposed body from the log entry).
|
|
615
|
+
// proposal.content override lets plugins prefer the proposed body (e.g. set).
|
|
724
616
|
const defaultBody = output ? output : "";
|
|
725
617
|
const resolvedBody = await this.#hooks.proposal.content.filter(
|
|
726
618
|
defaultBody,
|
|
@@ -745,13 +637,11 @@ export default class AgentLoop {
|
|
|
745
637
|
: this.#hooks.proposal.rejected;
|
|
746
638
|
await event.emit({ ...ctx, resolvedBody });
|
|
747
639
|
|
|
748
|
-
//
|
|
749
|
-
// (102 mid-run) rather than a hardcoded 200 so the nvim client
|
|
750
|
-
// doesn't treat the RPC ack as a terminal signal.
|
|
640
|
+
// Return current run status (not 200) so client doesn't close on resolve ack.
|
|
751
641
|
return { run: runAlias, status: runRow.status };
|
|
752
642
|
}
|
|
753
643
|
|
|
754
|
-
async inject(runAlias, message, mode) {
|
|
644
|
+
async inject(runAlias, message, mode, options = {}) {
|
|
755
645
|
if (mode !== "ask" && mode !== "act") {
|
|
756
646
|
throw new Error(
|
|
757
647
|
`inject: mode is required and must be "ask" or "act" (got ${JSON.stringify(mode)})`,
|
|
@@ -761,6 +651,14 @@ export default class AgentLoop {
|
|
|
761
651
|
if (!runRow)
|
|
762
652
|
throw new Error(msg("error.run_not_found", { runId: runAlias }));
|
|
763
653
|
|
|
654
|
+
const noRepo = options?.noRepo ?? process.env.RUMMY_NO_REPO === "1";
|
|
655
|
+
const noInteraction =
|
|
656
|
+
options?.noInteraction ?? process.env.RUMMY_NO_INTERACTION === "1";
|
|
657
|
+
const noWeb = options?.noWeb ?? process.env.RUMMY_NO_WEB === "1";
|
|
658
|
+
const noProposals =
|
|
659
|
+
options?.noProposals ?? process.env.RUMMY_NO_PROPOSALS === "1";
|
|
660
|
+
const yolo = options?.yolo ?? process.env.RUMMY_YOLO === "1";
|
|
661
|
+
|
|
764
662
|
const nextTurn = runRow.next_turn;
|
|
765
663
|
|
|
766
664
|
await this.#entries.set({
|
|
@@ -784,7 +682,14 @@ export default class AgentLoop {
|
|
|
784
682
|
mode,
|
|
785
683
|
model: runRow.model,
|
|
786
684
|
prompt: message,
|
|
787
|
-
config:
|
|
685
|
+
config: JSON.stringify({
|
|
686
|
+
noRepo,
|
|
687
|
+
noInteraction,
|
|
688
|
+
noWeb,
|
|
689
|
+
noProposals,
|
|
690
|
+
yolo,
|
|
691
|
+
temperature: options?.temperature,
|
|
692
|
+
}),
|
|
788
693
|
});
|
|
789
694
|
|
|
790
695
|
const projectId = runRow.project_id;
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Thin orchestrator. Computes loopStartTurn from the rows,
|
|
3
|
-
* then invokes assembly.system and assembly.user filter chains.
|
|
4
|
-
* All rendering logic lives in plugins.
|
|
5
|
-
*/
|
|
1
|
+
// Orchestrates assembly.system / assembly.user filter chains; plugins do all rendering.
|
|
6
2
|
export default class ContextAssembler {
|
|
7
3
|
static async assembleFromTurnContext(
|
|
8
4
|
rows,
|
|
@@ -10,15 +6,13 @@ export default class ContextAssembler {
|
|
|
10
6
|
type = "ask",
|
|
11
7
|
systemPrompt = "",
|
|
12
8
|
contextSize = 0,
|
|
13
|
-
demoted = [],
|
|
14
9
|
toolSet = null,
|
|
15
10
|
lastContextTokens = 0,
|
|
16
11
|
turn = 1,
|
|
17
12
|
} = {},
|
|
18
13
|
hooks,
|
|
19
14
|
) {
|
|
20
|
-
//
|
|
21
|
-
// the prompt plugin's turn.started handler has run.
|
|
15
|
+
// Loop boundary from active prompt; absent on turn 1 before prompt plugin's turn.started.
|
|
22
16
|
const promptEntry = rows.findLast(
|
|
23
17
|
(r) => r.category === "prompt" && r.scheme === "prompt",
|
|
24
18
|
);
|
|
@@ -31,7 +25,6 @@ export default class ContextAssembler {
|
|
|
31
25
|
type,
|
|
32
26
|
contextSize,
|
|
33
27
|
lastContextTokens,
|
|
34
|
-
demoted,
|
|
35
28
|
toolSet,
|
|
36
29
|
turn,
|
|
37
30
|
};
|