@pentoshi/clai 3.12.1 → 3.13.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/dist/agent/compaction-summary.d.ts +20 -12
- package/dist/agent/compaction-summary.js +72 -14
- package/dist/agent/compaction-summary.js.map +1 -1
- package/dist/agent/context-manager.d.ts +2 -0
- package/dist/agent/context-manager.js +64 -35
- package/dist/agent/context-manager.js.map +1 -1
- package/dist/agent/events.d.ts +1 -0
- package/dist/agent/runner.js +125 -97
- package/dist/agent/runner.js.map +1 -1
- package/dist/app/adapters/agent-event-adapter.js +1 -0
- package/dist/app/adapters/agent-event-adapter.js.map +1 -1
- package/dist/app/controllers/session-compact-helper.d.ts +3 -1
- package/dist/app/controllers/session-compact-helper.js +86 -42
- package/dist/app/controllers/session-compact-helper.js.map +1 -1
- package/dist/app/controllers/session-context-usage.js +0 -2
- package/dist/app/controllers/session-context-usage.js.map +1 -1
- package/dist/app/controllers/session-controller.js +3 -0
- package/dist/app/controllers/session-controller.js.map +1 -1
- package/dist/app/events/app-event.d.ts +1 -0
- package/dist/app/events/app-event.js.map +1 -1
- package/dist/tools/registry.js +33 -13
- package/dist/tools/registry.js.map +1 -1
- package/dist/tui/state.js +5 -5
- package/dist/tui/state.js.map +1 -1
- package/dist/tui-v2/components/status/status-line.d.ts +1 -0
- package/dist/tui-v2/components/status/status-line.js +62 -3
- package/dist/tui-v2/components/status/status-line.js.map +1 -1
- package/dist/tui-v2/rendering/incremental-strip.js +1 -1
- package/dist/tui-v2/rendering/incremental-strip.js.map +1 -1
- package/dist/tui-v2/rendering/strip-tool-surfaces.d.ts +0 -8
- package/dist/tui-v2/rendering/strip-tool-surfaces.js +31 -17
- package/dist/tui-v2/rendering/strip-tool-surfaces.js.map +1 -1
- package/dist/tui-v2/state/transcript-compaction.js +1 -4
- package/dist/tui-v2/state/transcript-compaction.js.map +1 -1
- package/dist/tui-v2/state/transcript-reducer.js +3 -1
- package/dist/tui-v2/state/transcript-reducer.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/package.json +1 -1
package/dist/agent/runner.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { streamWithProvider } from "../llm/router.js";
|
|
4
|
+
import { modelContextWindow } from "../llm/token-usage.js";
|
|
4
5
|
import { streamAlreadyEmitted } from "../llm/stream-progress.js";
|
|
5
6
|
import { classifyStreamFailure, planStreamRecovery, recordRecoveryAttempt, createStreamRecoveryState, resetStreamRecoveryState, } from "./stream-recovery.js";
|
|
6
7
|
import { modelSupportsVision, resolveToolDialect } from "../llm/capabilities.js";
|
|
@@ -55,7 +56,7 @@ import { analyzeTask, formatTaskAnalysisHint, isNarrowExplicitNmapOperation, } f
|
|
|
55
56
|
import { computeMaxIterations, computeStepBudget } from "./step-budget.js";
|
|
56
57
|
import { isScratchOnlyWrite } from "./scratch-write.js";
|
|
57
58
|
import { buildDurableEnvelope, WorkLedger, } from "./durable-envelope.js";
|
|
58
|
-
import { COMPACTION_SYSTEM_PROMPT, COMPACTION_MAX_COMPLETION_TOKENS, COMPACTION_MAP_MAX_COMPLETION_TOKENS, } from "./compaction-summary.js";
|
|
59
|
+
import { buildCompactionRetryPrompt, compactionSinglePassInputBudget, COMPACTION_SYSTEM_PROMPT, COMPACTION_MAX_COMPLETION_TOKENS, COMPACTION_MAP_MAX_COMPLETION_TOKENS, isCompactionCompletionTruncated, looksLikeIncompleteCompactionSummary, looksLikeTranscriptReplay, normalizeCompactionSummary, } from "./compaction-summary.js";
|
|
59
60
|
import { maybeAppendPlanModeReminder, PLAN_REMINDER_TOAST, } from "./plan-mode-reminders.js";
|
|
60
61
|
import { LoopGuard } from "./loop-guard.js";
|
|
61
62
|
import { appendInterruptedReasoning, interruptedReasoningBrief, isMeaningfulResumptionYield, } from "./interrupted-reasoning.js";
|
|
@@ -315,12 +316,21 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
315
316
|
process.stdout.write(chalk.dim(" ✦ Compacted Context · streaming Markdown\n\n"));
|
|
316
317
|
}
|
|
317
318
|
};
|
|
318
|
-
const writeCompactionDelta = (id, text) => {
|
|
319
|
-
if (!text)
|
|
319
|
+
const writeCompactionDelta = (id, text, replace = false) => {
|
|
320
|
+
if (!text && !replace)
|
|
320
321
|
return;
|
|
321
|
-
emit({
|
|
322
|
-
|
|
323
|
-
|
|
322
|
+
emit({
|
|
323
|
+
type: "compaction-delta",
|
|
324
|
+
id,
|
|
325
|
+
text,
|
|
326
|
+
...(replace ? { replace: true } : {}),
|
|
327
|
+
});
|
|
328
|
+
if (writesDirectly) {
|
|
329
|
+
if (replace)
|
|
330
|
+
process.stdout.write(chalk.dim("\n ↻ rewriting compacted context\n\n"));
|
|
331
|
+
if (text)
|
|
332
|
+
process.stdout.write(text);
|
|
333
|
+
}
|
|
324
334
|
};
|
|
325
335
|
const writeCompactionCompleted = (id, summary, beforeTokens, afterTokens) => {
|
|
326
336
|
emit({
|
|
@@ -2773,50 +2783,105 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2773
2783
|
const summarizeForCompaction = async (summaryPrompt, stage) => {
|
|
2774
2784
|
const streamFinalSummary = stage?.phase !== "map";
|
|
2775
2785
|
const compactionId = streamFinalSummary ? activeCompactionId : undefined;
|
|
2776
|
-
const
|
|
2777
|
-
?
|
|
2786
|
+
const maxTokens = stage?.phase === "map"
|
|
2787
|
+
? COMPACTION_MAP_MAX_COMPLETION_TOKENS
|
|
2788
|
+
: COMPACTION_MAX_COMPLETION_TOKENS;
|
|
2789
|
+
const sourceMessages = stage?.sourceMessages;
|
|
2790
|
+
const compactionTools = sourceMessages
|
|
2791
|
+
? selectToolDefs(nativeToolsActive, useCompactSystemPrompt)
|
|
2778
2792
|
: undefined;
|
|
2779
2793
|
const request = {
|
|
2780
2794
|
provider,
|
|
2781
2795
|
model,
|
|
2782
|
-
messages:
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2796
|
+
messages: sourceMessages
|
|
2797
|
+
? [
|
|
2798
|
+
...sourceMessages,
|
|
2799
|
+
{ role: "user", content: summaryPrompt },
|
|
2800
|
+
]
|
|
2801
|
+
: [
|
|
2802
|
+
{ role: "system", content: COMPACTION_SYSTEM_PROMPT },
|
|
2803
|
+
{ role: "user", content: summaryPrompt },
|
|
2804
|
+
],
|
|
2786
2805
|
temperature: 0.1,
|
|
2787
|
-
maxTokens
|
|
2788
|
-
? COMPACTION_MAP_MAX_COMPLETION_TOKENS
|
|
2789
|
-
: COMPACTION_MAX_COMPLETION_TOKENS,
|
|
2806
|
+
maxTokens,
|
|
2790
2807
|
thinking: { enabled: false, effort: "none" },
|
|
2791
2808
|
signal: options.signal,
|
|
2792
2809
|
allowModelFallback: true,
|
|
2810
|
+
...(compactionTools?.length
|
|
2811
|
+
? {
|
|
2812
|
+
tools: compactionTools,
|
|
2813
|
+
toolChoice: "none",
|
|
2814
|
+
}
|
|
2815
|
+
: {}),
|
|
2793
2816
|
};
|
|
2794
|
-
const
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
}
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2817
|
+
const runAttempt = async (attemptRequest, replace = false) => {
|
|
2818
|
+
if (compactionId && replace) {
|
|
2819
|
+
writeCompactionDelta(compactionId, "", true);
|
|
2820
|
+
}
|
|
2821
|
+
const parser = createThinkingStreamParser((text) => {
|
|
2822
|
+
if (compactionId)
|
|
2823
|
+
writeCompactionDelta(compactionId, text);
|
|
2824
|
+
}, undefined, { remember: false });
|
|
2825
|
+
const result = await streamWithProvider(attemptRequest, (token) => parser.push(token), { onStatus: () => undefined, maxRetries: 0 });
|
|
2826
|
+
parser.finish();
|
|
2827
|
+
return result;
|
|
2828
|
+
};
|
|
2829
|
+
const first = await runAttempt(request);
|
|
2830
|
+
let visible = normalizeCompactionSummary(stripThinking(first.text).visible);
|
|
2831
|
+
let retryReason;
|
|
2832
|
+
if (isCompactionCompletionTruncated(first, maxTokens)) {
|
|
2833
|
+
retryReason = "truncated";
|
|
2834
|
+
}
|
|
2835
|
+
else if (!visible) {
|
|
2836
|
+
retryReason = "reasoning-only";
|
|
2837
|
+
}
|
|
2838
|
+
else if (looksLikeTranscriptReplay(visible)) {
|
|
2839
|
+
retryReason = "replayed";
|
|
2840
|
+
}
|
|
2841
|
+
else if (looksLikeIncompleteCompactionSummary(visible)) {
|
|
2842
|
+
retryReason = "incomplete";
|
|
2843
|
+
}
|
|
2844
|
+
if (retryReason) {
|
|
2845
|
+
const retry = await runAttempt({
|
|
2846
|
+
...request,
|
|
2847
|
+
messages: sourceMessages
|
|
2848
|
+
? [
|
|
2849
|
+
...sourceMessages,
|
|
2850
|
+
{
|
|
2851
|
+
role: "user",
|
|
2852
|
+
content: buildCompactionRetryPrompt(summaryPrompt, retryReason),
|
|
2853
|
+
},
|
|
2854
|
+
]
|
|
2855
|
+
: [
|
|
2856
|
+
{
|
|
2857
|
+
role: "system",
|
|
2858
|
+
content: `${COMPACTION_SYSTEM_PROMPT}\nReturn only a complete continuation-memory summary. Do not include analysis, reasoning, or <think> tags.`,
|
|
2859
|
+
},
|
|
2860
|
+
{
|
|
2861
|
+
role: "user",
|
|
2862
|
+
content: buildCompactionRetryPrompt(summaryPrompt, retryReason),
|
|
2863
|
+
},
|
|
2864
|
+
],
|
|
2865
|
+
temperature: 0,
|
|
2866
|
+
maxTokens,
|
|
2867
|
+
thinking: { enabled: false, effort: "none" },
|
|
2868
|
+
allowModelFallback: true,
|
|
2869
|
+
}, true);
|
|
2870
|
+
if (isCompactionCompletionTruncated(retry, maxTokens)) {
|
|
2871
|
+
throw new Error("compaction failed: model hit the summary output limit twice — original context retained");
|
|
2872
|
+
}
|
|
2873
|
+
visible = normalizeCompactionSummary(stripThinking(retry.text).visible);
|
|
2874
|
+
if (!visible) {
|
|
2875
|
+
throw new Error("compaction failed: model returned an empty summary");
|
|
2876
|
+
}
|
|
2877
|
+
if (looksLikeTranscriptReplay(visible)) {
|
|
2878
|
+
throw new Error("compaction failed: model replayed the transcript twice — original context retained");
|
|
2879
|
+
}
|
|
2880
|
+
if (looksLikeIncompleteCompactionSummary(visible)) {
|
|
2881
|
+
throw new Error("compaction failed: model returned an incomplete summary twice — original context retained");
|
|
2882
|
+
}
|
|
2818
2883
|
}
|
|
2819
|
-
return
|
|
2884
|
+
return visible;
|
|
2820
2885
|
};
|
|
2821
2886
|
/**
|
|
2822
2887
|
* Estimate the complete next model request, including attached native-tool
|
|
@@ -2909,9 +2974,12 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2909
2974
|
activeCompactionId = compactionId;
|
|
2910
2975
|
writeCompactionStarted(compactionId, beforeTokens);
|
|
2911
2976
|
try {
|
|
2977
|
+
const compactionTools = selectToolDefs(nativeToolsActive, useCompactSystemPrompt);
|
|
2978
|
+
const compactionSchemaTokens = buildContextBreakdown([], compactionTools).estimatedTotalTokens;
|
|
2912
2979
|
const result = await compactMessagesWithSummary(messages, summarizeForCompaction, {
|
|
2913
2980
|
budgetTokens: 0,
|
|
2914
2981
|
keepRecent: AUTO_COMPACT_KEEP_RECENT,
|
|
2982
|
+
singlePassInputBudgetTokens: Math.max(0, compactionSinglePassInputBudget(contextLimitTokens ?? modelContextWindow(model, provider)) - compactionSchemaTokens),
|
|
2915
2983
|
...(durableEnvelope ? { durableEnvelope } : {}),
|
|
2916
2984
|
});
|
|
2917
2985
|
const summaryBody = result.messages.find((m) => isCompactionMemoryMessage(m))?.content ??
|
|
@@ -3098,6 +3166,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3098
3166
|
}, 10_000);
|
|
3099
3167
|
heartbeat.unref?.();
|
|
3100
3168
|
const deferredToolCalls = [];
|
|
3169
|
+
const streamedNativeCallNames = new Map();
|
|
3101
3170
|
const deltaParser = writesDirectly
|
|
3102
3171
|
? undefined
|
|
3103
3172
|
: createThinkingStreamParser((text) => emit({ type: "assistant-delta", text }), (text) => {
|
|
@@ -3208,64 +3277,22 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3208
3277
|
tools: turnTools,
|
|
3209
3278
|
toolChoice: "auto",
|
|
3210
3279
|
parallelToolCalls: true,
|
|
3211
|
-
// P2-3: emit tool cards as soon as the function name arrives.
|
|
3212
3280
|
onToolCallDelta: (delta) => {
|
|
3213
3281
|
if (!delta.name)
|
|
3214
3282
|
return;
|
|
3215
3283
|
const name = fromWireName(delta.name) ?? delta.name;
|
|
3216
|
-
|
|
3217
|
-
if (existing && existing.call.name !== "…") {
|
|
3218
|
-
if (delta.argumentsBytes &&
|
|
3219
|
-
delta.argumentsBytes >= 4096 &&
|
|
3220
|
-
!writesDirectly) {
|
|
3221
|
-
emit({
|
|
3222
|
-
type: "status",
|
|
3223
|
-
text: `${name} (${Math.round(delta.argumentsBytes / 1024)}KB args)`,
|
|
3224
|
-
});
|
|
3225
|
-
}
|
|
3226
|
-
return;
|
|
3227
|
-
}
|
|
3228
|
-
while (deferredToolCalls.length < delta.index) {
|
|
3229
|
-
const slot = deferredToolCalls.length;
|
|
3230
|
-
const placeholderId = `tool-${++nextToolEventId}`;
|
|
3231
|
-
callIds[slot] = placeholderId;
|
|
3232
|
-
deferredToolCalls.push({
|
|
3233
|
-
eventId: placeholderId,
|
|
3234
|
-
call: { name: "…", args: {} },
|
|
3235
|
-
rendered: "",
|
|
3236
|
-
shown: false,
|
|
3237
|
-
});
|
|
3238
|
-
}
|
|
3239
|
-
const call = normalizeToolCall({
|
|
3240
|
-
name,
|
|
3241
|
-
args: {},
|
|
3242
|
-
});
|
|
3243
|
-
const eventId = existing?.eventId ?? `tool-${++nextToolEventId}`;
|
|
3244
|
-
callIds[delta.index] = eventId;
|
|
3245
|
-
alreadyPrintedIds.add(eventId);
|
|
3246
|
-
const toolCallLine = chalk.cyan(` ▶ ${call.name}`) +
|
|
3247
|
-
chalk.gray(` ${formatToolArgs(call)}`);
|
|
3248
|
-
const entry = {
|
|
3249
|
-
eventId,
|
|
3250
|
-
call,
|
|
3251
|
-
rendered: styleToolChatter(call, toolCallLine) + "\n",
|
|
3252
|
-
shown: false,
|
|
3253
|
-
};
|
|
3254
|
-
if (deferredToolCalls.length === delta.index) {
|
|
3255
|
-
deferredToolCalls.push(entry);
|
|
3256
|
-
}
|
|
3257
|
-
else {
|
|
3258
|
-
deferredToolCalls[delta.index] = entry;
|
|
3259
|
-
}
|
|
3260
|
-
streamedCallsCount = Math.max(streamedCallsCount, deferredToolCalls.length);
|
|
3284
|
+
streamedNativeCallNames.set(delta.index, name);
|
|
3261
3285
|
if (!writesDirectly) {
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3286
|
+
emit({
|
|
3287
|
+
type: "status",
|
|
3288
|
+
text: delta.argumentsBytes && delta.argumentsBytes >= 4096
|
|
3289
|
+
? `${name} (${Math.round(delta.argumentsBytes / 1024)}KB args)`
|
|
3290
|
+
: name,
|
|
3291
|
+
});
|
|
3265
3292
|
}
|
|
3266
3293
|
else {
|
|
3267
3294
|
spinner.stop();
|
|
3268
|
-
spinner = startThinkingSpinner(`tool ${
|
|
3295
|
+
spinner = startThinkingSpinner(`tool ${name}…`, options.signal);
|
|
3269
3296
|
}
|
|
3270
3297
|
},
|
|
3271
3298
|
}
|
|
@@ -3283,7 +3310,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3283
3310
|
spinner.stop();
|
|
3284
3311
|
}
|
|
3285
3312
|
while (streamedCallsCount < parsedCalls.length) {
|
|
3286
|
-
const call = parsedCalls[streamedCallsCount];
|
|
3313
|
+
const call = normalizeToolCall(parsedCalls[streamedCallsCount]);
|
|
3287
3314
|
const eventId = `tool-${++nextToolEventId}`;
|
|
3288
3315
|
callIds[streamedCallsCount] = eventId;
|
|
3289
3316
|
alreadyPrintedIds.add(eventId);
|
|
@@ -3788,7 +3815,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3788
3815
|
continue;
|
|
3789
3816
|
}
|
|
3790
3817
|
const incompleteNativeStream = nativeToolCalls.length === 0 &&
|
|
3791
|
-
|
|
3818
|
+
streamedNativeCallNames.size > 0;
|
|
3792
3819
|
if (incompleteNativeStream) {
|
|
3793
3820
|
const reason = "The provider began this native tool call but never completed it. Nothing ran; reissue a complete call.";
|
|
3794
3821
|
for (const deferred of deferredToolCalls) {
|
|
@@ -4201,13 +4228,14 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4201
4228
|
let parsed = parseAllToolCalls(assistantText.visible || assistantText.thinkContent);
|
|
4202
4229
|
if (parsed.length === 0 && call)
|
|
4203
4230
|
parsed = [call];
|
|
4204
|
-
bound = parsed.map((
|
|
4231
|
+
bound = parsed.map((rawCall, index) => {
|
|
4232
|
+
const call = normalizeToolCall(rawCall);
|
|
4205
4233
|
const id = syntheticToolCallId(index);
|
|
4206
4234
|
return {
|
|
4207
4235
|
index,
|
|
4208
4236
|
id,
|
|
4209
|
-
call
|
|
4210
|
-
native: { id, name:
|
|
4237
|
+
call,
|
|
4238
|
+
native: { id, name: call.name, args: call.args },
|
|
4211
4239
|
};
|
|
4212
4240
|
});
|
|
4213
4241
|
}
|
|
@@ -4377,7 +4405,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4377
4405
|
for (const deferred of activeDeferredToolCalls.slice(0, allCalls.length)) {
|
|
4378
4406
|
if (!deferred.call.name || deferred.call.name === "…")
|
|
4379
4407
|
continue;
|
|
4380
|
-
if (!deferred.shown
|
|
4408
|
+
if (!deferred.shown) {
|
|
4381
4409
|
writeToolCall(deferred.eventId, deferred.call, deferred.rendered);
|
|
4382
4410
|
deferred.shown = true;
|
|
4383
4411
|
}
|