@gaberrb/polypus 0.4.9 → 0.4.10
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/dist/index.js +79 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -142,6 +142,7 @@ var en = {
|
|
|
142
142
|
"run.reprompt": "\u21BB no tool call \u2014 reinforcing instructions (attempt {attempt})",
|
|
143
143
|
"run.autocorrect": "\u21BB tool failed \u2014 auto-correcting with extra context",
|
|
144
144
|
"run.cancelled": "\u25A0 cancelled",
|
|
145
|
+
"compaction.done": "context compacted: ~{before} \u2192 ~{after} tokens",
|
|
145
146
|
"run.jsonNeedsTask": "--json requires a task argument (headless mode has no interactive REPL).",
|
|
146
147
|
"review.approveAll": "approve all",
|
|
147
148
|
"review.reject": "reject",
|
|
@@ -401,6 +402,7 @@ var ptBR = {
|
|
|
401
402
|
"run.reprompt": "\u21BB nenhuma chamada de tool \u2014 refor\xE7ando instru\xE7\xF5es (tentativa {attempt})",
|
|
402
403
|
"run.autocorrect": "\u21BB tool falhou \u2014 autocorrigindo com contexto extra",
|
|
403
404
|
"run.cancelled": "\u25A0 cancelado",
|
|
405
|
+
"compaction.done": "contexto compactado: ~{before} \u2192 ~{after} tokens",
|
|
404
406
|
"run.jsonNeedsTask": "--json exige um argumento de tarefa (o modo headless n\xE3o tem REPL interativo).",
|
|
405
407
|
"review.approveAll": "aprovar tudo",
|
|
406
408
|
"review.reject": "rejeitar",
|
|
@@ -2092,6 +2094,56 @@ async function loadProjectInstructions(workspace) {
|
|
|
2092
2094
|
return void 0;
|
|
2093
2095
|
}
|
|
2094
2096
|
|
|
2097
|
+
// src/core/agent/compaction.ts
|
|
2098
|
+
function estimateTokens(messages) {
|
|
2099
|
+
let chars = 0;
|
|
2100
|
+
for (const m of messages) chars += m.content.length;
|
|
2101
|
+
return Math.ceil(chars / 4);
|
|
2102
|
+
}
|
|
2103
|
+
var RECENT_KEEP = 8;
|
|
2104
|
+
var MIN_TO_COMPACT = 4;
|
|
2105
|
+
var MAX_SUMMARY_INPUT = 4e4;
|
|
2106
|
+
function findSafeCut(messages, desiredKeep = RECENT_KEEP) {
|
|
2107
|
+
let cut = Math.max(1, messages.length - desiredKeep);
|
|
2108
|
+
while (cut < messages.length && (messages[cut].role === "tool" || messages[cut - 1]?.role === "assistant" && (messages[cut - 1].toolCalls?.length ?? 0) > 0)) {
|
|
2109
|
+
cut++;
|
|
2110
|
+
}
|
|
2111
|
+
return cut;
|
|
2112
|
+
}
|
|
2113
|
+
function serialize(messages) {
|
|
2114
|
+
const text2 = messages.map((m) => {
|
|
2115
|
+
const tools = m.toolCalls?.length ? ` [called: ${m.toolCalls.map((c) => c.name).join(", ")}]` : "";
|
|
2116
|
+
return `${m.role}${tools}: ${m.content}`;
|
|
2117
|
+
}).join("\n\n");
|
|
2118
|
+
return text2.length > MAX_SUMMARY_INPUT ? text2.slice(-MAX_SUMMARY_INPUT) : text2;
|
|
2119
|
+
}
|
|
2120
|
+
async function compactHistory(messages, agent, signal) {
|
|
2121
|
+
if (messages.length === 0) return messages;
|
|
2122
|
+
const system = messages[0].role === "system" ? messages[0] : void 0;
|
|
2123
|
+
const startIdx = system ? 1 : 0;
|
|
2124
|
+
const cut = findSafeCut(messages);
|
|
2125
|
+
if (cut >= messages.length) return messages;
|
|
2126
|
+
const middle = messages.slice(startIdx, cut);
|
|
2127
|
+
if (middle.length < MIN_TO_COMPACT) return messages;
|
|
2128
|
+
const tail = messages.slice(cut);
|
|
2129
|
+
const summary = await agent.provider.chat({
|
|
2130
|
+
messages: [
|
|
2131
|
+
{
|
|
2132
|
+
role: "system",
|
|
2133
|
+
content: "You compress a coding agent's conversation so it can continue with less context. Summarize the messages below into a concise but information-dense brief that preserves: the original task and goal, key decisions, files created/edited and why, important command/test outputs, and any remaining TODOs or open problems. Use terse bullet points. Do not invent details."
|
|
2134
|
+
},
|
|
2135
|
+
{ role: "user", content: serialize(middle) }
|
|
2136
|
+
],
|
|
2137
|
+
signal
|
|
2138
|
+
});
|
|
2139
|
+
const summaryMessage = {
|
|
2140
|
+
role: "user",
|
|
2141
|
+
content: `[Summary of earlier conversation, compacted to save context]
|
|
2142
|
+
${summary.content.trim()}`
|
|
2143
|
+
};
|
|
2144
|
+
return system ? [system, summaryMessage, ...tail] : [summaryMessage, ...tail];
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2095
2147
|
// src/core/agent/loop.ts
|
|
2096
2148
|
function looksLikeStall(text2) {
|
|
2097
2149
|
const lc = text2.toLowerCase();
|
|
@@ -2146,9 +2198,22 @@ async function runAgent(opts) {
|
|
|
2146
2198
|
const maxToolRetries = opts.maxToolRetries ?? 3;
|
|
2147
2199
|
const autoCorrect = opts.autoCorrect ?? true;
|
|
2148
2200
|
const usage2 = { promptTokens: 0, completionTokens: 0 };
|
|
2201
|
+
const compactThreshold = opts.compactThresholdTokens ?? 0;
|
|
2202
|
+
let lastPromptTokens = 0;
|
|
2149
2203
|
for (let step = 1; step <= maxSteps; step++) {
|
|
2150
2204
|
if (opts.signal?.aborted) return { finished: false, reason: "cancelled", steps: step - 1, messages, usage: usage2 };
|
|
2151
2205
|
events?.onStep?.(step);
|
|
2206
|
+
if (compactThreshold > 0) {
|
|
2207
|
+
const current = lastPromptTokens || estimateTokens(messages);
|
|
2208
|
+
if (current >= compactThreshold) {
|
|
2209
|
+
const compacted = await compactHistory(messages, agent, opts.signal);
|
|
2210
|
+
if (compacted.length < messages.length) {
|
|
2211
|
+
messages.splice(0, messages.length, ...compacted);
|
|
2212
|
+
lastPromptTokens = estimateTokens(messages);
|
|
2213
|
+
events?.onCompaction?.(current, lastPromptTokens);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2152
2217
|
let response;
|
|
2153
2218
|
try {
|
|
2154
2219
|
response = await agent.provider.chat({
|
|
@@ -2163,6 +2228,7 @@ async function runAgent(opts) {
|
|
|
2163
2228
|
}
|
|
2164
2229
|
usage2.promptTokens += response.usage?.promptTokens ?? 0;
|
|
2165
2230
|
usage2.completionTokens += response.usage?.completionTokens ?? 0;
|
|
2231
|
+
lastPromptTokens = response.usage?.promptTokens ?? estimateTokens(messages);
|
|
2166
2232
|
events?.onUsage?.(usage2);
|
|
2167
2233
|
const { toolCalls, text: text2 } = driver.parse(response);
|
|
2168
2234
|
messages.push(driver.assistantMessage(response, toolCalls));
|
|
@@ -2588,6 +2654,9 @@ function createJsonCollector() {
|
|
|
2588
2654
|
onReprompt(attempt) {
|
|
2589
2655
|
log.push({ type: "reprompt", attempt });
|
|
2590
2656
|
},
|
|
2657
|
+
onCompaction(before, after) {
|
|
2658
|
+
log.push({ type: "compaction", before, after });
|
|
2659
|
+
},
|
|
2591
2660
|
onUsage() {
|
|
2592
2661
|
}
|
|
2593
2662
|
};
|
|
@@ -3800,6 +3869,11 @@ var Spinner = class {
|
|
|
3800
3869
|
|
|
3801
3870
|
// src/cli/commands/run.ts
|
|
3802
3871
|
var MAX_VERIFY_FIXES = 3;
|
|
3872
|
+
function compactionThreshold() {
|
|
3873
|
+
if (process.env.POLYPUS_NO_COMPACT) return 0;
|
|
3874
|
+
const v = Number(process.env.POLYPUS_COMPACT_THRESHOLD);
|
|
3875
|
+
return Number.isFinite(v) && v > 0 ? v : 12e4;
|
|
3876
|
+
}
|
|
3803
3877
|
async function run(task, opts) {
|
|
3804
3878
|
let config = await loadConfig();
|
|
3805
3879
|
const workspace = process.cwd();
|
|
@@ -3925,6 +3999,7 @@ async function executeTask(task, resolved, workspace, session, json = false, ver
|
|
|
3925
3999
|
promptContext: { workspace, mode: session.mode, allow: session.allow },
|
|
3926
4000
|
history: session.history,
|
|
3927
4001
|
maxSteps: session.maxSteps,
|
|
4002
|
+
compactThresholdTokens: compactionThreshold(),
|
|
3928
4003
|
signal: controller.signal,
|
|
3929
4004
|
events
|
|
3930
4005
|
});
|
|
@@ -4108,6 +4183,10 @@ function renderEvents(spinner3) {
|
|
|
4108
4183
|
spinner3.stop();
|
|
4109
4184
|
console.log(pc8.yellow(" " + t("run.reprompt", { attempt })));
|
|
4110
4185
|
},
|
|
4186
|
+
onCompaction(before, after) {
|
|
4187
|
+
spinner3.stop();
|
|
4188
|
+
console.log(pc8.dim("\u21AF " + t("compaction.done", { before: fmtTokens(before), after: fmtTokens(after) })));
|
|
4189
|
+
},
|
|
4111
4190
|
onCorrection() {
|
|
4112
4191
|
spinner3.stop();
|
|
4113
4192
|
console.log(pc8.yellow(" \u21BB " + t("run.autocorrect")));
|