@pentoshi/clai 4.11.4 → 4.11.7
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-executor.js +12 -0
- package/dist/agent/compaction-executor.js.map +1 -1
- package/dist/agent/context-manager.js +3 -7
- package/dist/agent/context-manager.js.map +1 -1
- package/dist/agent/message-slim.d.ts +2 -0
- package/dist/agent/message-slim.js +63 -3
- package/dist/agent/message-slim.js.map +1 -1
- package/dist/agent/request-accounting.js +1 -1
- package/dist/agent/request-accounting.js.map +1 -1
- package/dist/agent/runner.js +60 -29
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tool-call-parser.js +2 -1
- package/dist/agent/tool-call-parser.js.map +1 -1
- package/dist/agent/tool-history-projection.d.ts +7 -0
- package/dist/agent/tool-history-projection.js +455 -0
- package/dist/agent/tool-history-projection.js.map +1 -0
- package/dist/agent/tool-history.d.ts +7 -10
- package/dist/agent/tool-history.js +155 -151
- package/dist/agent/tool-history.js.map +1 -1
- package/dist/app/controllers/session-compact-helper.js +14 -7
- package/dist/app/controllers/session-compact-helper.js.map +1 -1
- package/dist/app/controllers/session-controller.js +3 -2
- package/dist/app/controllers/session-controller.js.map +1 -1
- package/dist/prompts/embedded.js +1 -1
- package/dist/prompts/embedded.js.map +1 -1
- package/dist/prompts/index.js +2 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/system.agent.md +2 -2
- package/dist/tools/registry.js +6 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tui-v2/bootstrap/resize-repaint.js +7 -1
- package/dist/tui-v2/bootstrap/resize-repaint.js.map +1 -1
- package/dist/tui-v2/bootstrap/start-tui-v2.js +6 -1
- package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
- package/dist/ui-core/bootstrap/theme-mode-cache.d.ts +3 -0
- package/dist/ui-core/bootstrap/theme-mode-cache.js +45 -0
- package/dist/ui-core/bootstrap/theme-mode-cache.js.map +1 -0
- 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
|
@@ -45,8 +45,8 @@ function safeEngagementActionsForToolCall(call) {
|
|
|
45
45
|
}
|
|
46
46
|
import { availableToolNames, normalizeToolCall, runToolCall, BATCH_SAFE_TOOLS, } from "../tools/registry.js";
|
|
47
47
|
import { getToolDefinitions, getCompactToolDefinitions, RUNNER_META_TOOL_NAMES, MCP_AGENT_TOOL_NAMES, mcpAgentToolNames, } from "../tools/definitions.js";
|
|
48
|
-
import { elidedStubReuseMessage, findElidedStubArg, } from "./message-slim.js";
|
|
49
|
-
import { appendAssistantWithTools, ensureUniqueToolCallIds, toolCallIdsInHistory, appendToolResult, assertValidToolProtocol, fillMissingToolResults, repairToolProtocol, } from "./tool-history.js";
|
|
48
|
+
import { elidedStubReuseMessage, findElidedStubArg, stripSupersededElidedArgs, } from "./message-slim.js";
|
|
49
|
+
import { appendAssistantWithTools, ensureUniqueToolCallIds, toolCallIdsInHistory, appendToolResult, assertValidToolProtocol, fillMissingToolResults, projectToolHistory, repairToolProtocol, } from "./tool-history.js";
|
|
50
50
|
import { legacyReasoningBlockFromArtifacts, reasoningArtifactsForPersistence, } from "../llm/reasoning-artifacts.js";
|
|
51
51
|
import { compactMessagesWithSummary, shouldApplyAutoCompact, COMPACTION_MEMORY_PREFIX, PLAN_IMPLEMENT_MEMORY_PREFIX, isCompactionMemoryMessage, } from "./context-manager.js";
|
|
52
52
|
import { buildContextBreakdown, contextBreakdownAuditPayload, describeDominantContextBlock, toolSchemaHash, } from "./context-breakdown.js";
|
|
@@ -1392,13 +1392,18 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
1392
1392
|
const reason = elidedStubReuseMessage(elidedStub.key);
|
|
1393
1393
|
return { reason, result: { ok: false, output: reason, exitCode: 1 } };
|
|
1394
1394
|
}
|
|
1395
|
+
const canonicalizeTurnCall = (rawCall) => {
|
|
1396
|
+
const normalized = normalizeToolCall(rawCall);
|
|
1397
|
+
const canonicalMcpName = mcpRuntime?.canonicalizeToolName(normalized.name);
|
|
1398
|
+
const named = canonicalMcpName && canonicalMcpName !== normalized.name
|
|
1399
|
+
? { ...normalized, name: canonicalMcpName }
|
|
1400
|
+
: normalized;
|
|
1401
|
+
const args = stripSupersededElidedArgs(named.args);
|
|
1402
|
+
return args === named.args ? named : { ...named, args };
|
|
1403
|
+
};
|
|
1395
1404
|
async function executeSingleTool(rawCall, toolEventId, parentSignal) {
|
|
1396
1405
|
const scratchDir = scratchDirFor(safeCwd());
|
|
1397
|
-
|
|
1398
|
-
const canonicalMcpName = mcpRuntime?.canonicalizeToolName(normalizedCall.name);
|
|
1399
|
-
let call = canonicalMcpName && canonicalMcpName !== normalizedCall.name
|
|
1400
|
-
? { ...normalizedCall, name: canonicalMcpName }
|
|
1401
|
-
: normalizedCall;
|
|
1406
|
+
let call = canonicalizeTurnCall(rawCall);
|
|
1402
1407
|
const emitVisibleSyntheticReceipt = (result, summary) => {
|
|
1403
1408
|
if (!alreadyPrintedIds.has(toolEventId)) {
|
|
1404
1409
|
writeToolCall(toolEventId, call);
|
|
@@ -1418,12 +1423,14 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
1418
1423
|
let engagementRecord;
|
|
1419
1424
|
const invalid = invalidToolCall(call);
|
|
1420
1425
|
if (invalid) {
|
|
1426
|
+
loopGuard.recordAttempt(step, call.name, call.args, false, invalid.result.exitCode, invalid.reason);
|
|
1421
1427
|
emitToolResult(toolEventId, invalid.result, invalid.reason);
|
|
1422
1428
|
return {
|
|
1423
1429
|
ok: false,
|
|
1424
1430
|
call,
|
|
1425
1431
|
result: invalid.result,
|
|
1426
1432
|
contextOutput: invalid.reason,
|
|
1433
|
+
suppressedRepeat: true,
|
|
1427
1434
|
};
|
|
1428
1435
|
}
|
|
1429
1436
|
if (call.name === "image.ocr" && !imageOcrEnabled) {
|
|
@@ -3011,6 +3018,9 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3011
3018
|
});
|
|
3012
3019
|
}
|
|
3013
3020
|
async function maybeAutoCompact(reason, force = false) {
|
|
3021
|
+
if (repairToolProtocol(messages) > 0) {
|
|
3022
|
+
lastSuccessfulRequestSnapshot = undefined;
|
|
3023
|
+
}
|
|
3014
3024
|
const beforeTokens = estimateNextRequestTokens(messages);
|
|
3015
3025
|
emit({
|
|
3016
3026
|
type: "context-estimate",
|
|
@@ -3053,7 +3063,10 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3053
3063
|
// single pass is forced (the raw estimate gate would otherwise reject a
|
|
3054
3064
|
// request that fits fine); when it does not, compaction falls back to
|
|
3055
3065
|
// the legacy transcript-rendered requests so it still succeeds.
|
|
3056
|
-
const
|
|
3066
|
+
const replayCandidate = lastSuccessfulRequestSnapshot;
|
|
3067
|
+
const replaySnapshot = replayCandidate && !projectToolHistory(replayCandidate.messages).changed
|
|
3068
|
+
? replayCandidate
|
|
3069
|
+
: undefined;
|
|
3057
3070
|
const replayPlan = replaySnapshot
|
|
3058
3071
|
? planCompactionReplay({
|
|
3059
3072
|
baseRequest: replaySnapshot,
|
|
@@ -3216,7 +3229,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3216
3229
|
let canonicalAssistantVisible = "";
|
|
3217
3230
|
let recoveredFromBareJson = false;
|
|
3218
3231
|
if (pendingCalls.length > 0) {
|
|
3219
|
-
call = pendingCalls.shift();
|
|
3232
|
+
call = canonicalizeTurnCall(pendingCalls.shift());
|
|
3220
3233
|
assistantText = { visible: "", thinkContent: "", hasThinking: false };
|
|
3221
3234
|
const batchStatus = ` ↳ continuing batch (${pendingCalls.length} more queued)\n`;
|
|
3222
3235
|
writeStatus(batchStatus);
|
|
@@ -3426,7 +3439,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3426
3439
|
const parsedCalls = parseAllToolCalls(accumulatedText);
|
|
3427
3440
|
if (parsedCalls.length > streamedCallsCount) {
|
|
3428
3441
|
while (streamedCallsCount < parsedCalls.length) {
|
|
3429
|
-
const call =
|
|
3442
|
+
const call = canonicalizeTurnCall(parsedCalls[streamedCallsCount]);
|
|
3430
3443
|
const eventId = `tool-${++nextToolEventId}`;
|
|
3431
3444
|
callIds[streamedCallsCount] = eventId;
|
|
3432
3445
|
alreadyPrintedIds.add(eventId);
|
|
@@ -3766,14 +3779,26 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3766
3779
|
lowYieldResumptions = 0;
|
|
3767
3780
|
};
|
|
3768
3781
|
// Native-first: prefer structured toolCalls from the provider.
|
|
3769
|
-
let nativeToolCalls = completion.toolCalls ?? []
|
|
3782
|
+
let nativeToolCalls = (completion.toolCalls ?? []).map((toolCall) => {
|
|
3783
|
+
if (toolCall.args?._parseError)
|
|
3784
|
+
return toolCall;
|
|
3785
|
+
const canonical = canonicalizeTurnCall({
|
|
3786
|
+
name: toolCall.name,
|
|
3787
|
+
args: toolCall.args,
|
|
3788
|
+
});
|
|
3789
|
+
return {
|
|
3790
|
+
...toolCall,
|
|
3791
|
+
name: canonical.name,
|
|
3792
|
+
args: canonical.args,
|
|
3793
|
+
};
|
|
3794
|
+
});
|
|
3770
3795
|
// Early UI cards: refresh args if stream deltas already opened cards;
|
|
3771
3796
|
// otherwise create cards now (non-streaming / name-after-done providers).
|
|
3772
3797
|
if (nativeToolCalls.length) {
|
|
3773
3798
|
if (deferredToolCalls.length === 0) {
|
|
3774
3799
|
for (let i = 0; i < nativeToolCalls.length; i += 1) {
|
|
3775
3800
|
const tc = nativeToolCalls[i];
|
|
3776
|
-
const normalized =
|
|
3801
|
+
const normalized = canonicalizeTurnCall({
|
|
3777
3802
|
name: tc.name,
|
|
3778
3803
|
args: tc.args,
|
|
3779
3804
|
});
|
|
@@ -3790,7 +3815,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3790
3815
|
else {
|
|
3791
3816
|
for (let i = 0; i < nativeToolCalls.length; i++) {
|
|
3792
3817
|
const tc = nativeToolCalls[i];
|
|
3793
|
-
const normalized =
|
|
3818
|
+
const normalized = canonicalizeTurnCall({
|
|
3794
3819
|
name: tc.name,
|
|
3795
3820
|
args: tc.args,
|
|
3796
3821
|
});
|
|
@@ -3826,7 +3851,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3826
3851
|
_raw: first.args._raw,
|
|
3827
3852
|
},
|
|
3828
3853
|
}
|
|
3829
|
-
:
|
|
3854
|
+
: canonicalizeTurnCall({ name: first.name, args: first.args });
|
|
3830
3855
|
}
|
|
3831
3856
|
else {
|
|
3832
3857
|
call = parseToolCall(assistantText.visible, {
|
|
@@ -3841,6 +3866,9 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3841
3866
|
}
|
|
3842
3867
|
}
|
|
3843
3868
|
}
|
|
3869
|
+
if (call && !call.args?.__nativeParseError) {
|
|
3870
|
+
call = canonicalizeTurnCall(call);
|
|
3871
|
+
}
|
|
3844
3872
|
if (looksLikePromptLeak(assistantText.visible)) {
|
|
3845
3873
|
if (call || nativeToolCalls.length) {
|
|
3846
3874
|
writeNotice("warn", "suppressed tool call from apparent prompt leak");
|
|
@@ -4182,16 +4210,15 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4182
4210
|
content: toolsAttached
|
|
4183
4211
|
? `Your ${salvagedToolName} tool call was cut off at the token limit, but the system salvaged the partial content and wrote ${lineCount} lines (file is now ${priorBytes} bytes) to ${salvaged.path}. ` +
|
|
4184
4212
|
`The file ends with: ${JSON.stringify(salvaged.lastLine)}\n\n` +
|
|
4185
|
-
`CONTINUE by calling fs.append now with path=${JSON.stringify(salvaged.path)}, expectedPriorBytes=${priorBytes}, and content set to ONLY the remaining
|
|
4213
|
+
`CONTINUE by calling fs.append now with path=${JSON.stringify(salvaged.path)}, expectedPriorBytes=${priorBytes}, and content set to ONLY the next remaining chunk not already on disk. Keep the chunk under 24,000 characters and wait for its receipt before sending another. Use the platform tool interface — no markdown fences.`
|
|
4186
4214
|
: `Your ${salvagedToolName} tool call was cut off at the token limit, but the system salvaged the partial content and wrote ${lineCount} lines (file is now ${priorBytes} bytes) to ${salvaged.path}. ` +
|
|
4187
4215
|
`The file ends with: ${JSON.stringify(salvaged.lastLine)}\n\n` +
|
|
4188
|
-
`CONTINUE with
|
|
4216
|
+
`CONTINUE with one fs.append chunk under 24,000 characters, then wait for its receipt before sending another:\n` +
|
|
4189
4217
|
'```tool\n{"name":"fs.append","args":{"path":' +
|
|
4190
4218
|
JSON.stringify(salvaged.path) +
|
|
4191
4219
|
',"expectedPriorBytes":' +
|
|
4192
4220
|
priorBytes +
|
|
4193
4221
|
',"content":"...ONLY the remaining content not already on disk..."}}\n```\n' +
|
|
4194
|
-
`expectedPriorBytes must match the receipt so append cannot double-write. ` +
|
|
4195
4222
|
`Do NOT re-read the full file; do NOT re-send content already saved.`,
|
|
4196
4223
|
});
|
|
4197
4224
|
continue;
|
|
@@ -4208,15 +4235,15 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4208
4235
|
role: "user",
|
|
4209
4236
|
content: toolsAttached
|
|
4210
4237
|
? "Your previous tool call was cut off before it finished — the JSON was incomplete, so NOTHING ran. " +
|
|
4211
|
-
"Prefer ONE complete fs.write when it fits. If the file is too large: (1) fs.write the first
|
|
4212
|
-
"(2) fs.append the rest with expectedPriorBytes from the write receipt, (3) repeat with
|
|
4238
|
+
"Prefer ONE complete fs.write when it fits. If the file is too large: (1) fs.write the first section under 24,000 characters, " +
|
|
4239
|
+
"(2) fs.append the rest with expectedPriorBytes from the write receipt, (3) repeat with chunks under 24,000 characters, waiting for each receipt. " +
|
|
4213
4240
|
"Keep reasoning SHORT and call the tool via the platform interface. Do NOT claim a file was written until a tool call succeeds."
|
|
4214
4241
|
: "Your previous tool call was cut off before it finished — the JSON was incomplete, so NOTHING ran. " +
|
|
4215
|
-
"Prefer ONE complete fs.write when it fits
|
|
4242
|
+
"Prefer ONE complete fs.write when it fits. " +
|
|
4216
4243
|
"If the file is too large for one call:\n" +
|
|
4217
|
-
"1. fs.write the first
|
|
4244
|
+
"1. fs.write the first section under 24,000 characters\n" +
|
|
4218
4245
|
"2. fs.append the rest with expectedPriorBytes from the write receipt\n" +
|
|
4219
|
-
"3.
|
|
4246
|
+
"3. Keep every append under 24,000 characters and wait for each receipt\n" +
|
|
4220
4247
|
"Keep reasoning SHORT — emit the ```tool block early. Do NOT claim a file was written until a tool call succeeds.",
|
|
4221
4248
|
});
|
|
4222
4249
|
continue;
|
|
@@ -4242,7 +4269,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4242
4269
|
content: `The system extracted and wrote ${lineCount} lines to ${salvaged.path} from your malformed tool call. ` +
|
|
4243
4270
|
`The file content ends at: "${salvaged.lastLine}"\n\n` +
|
|
4244
4271
|
`If the file is complete, proceed with the next step. ` +
|
|
4245
|
-
`If more content is needed, use
|
|
4272
|
+
`If more content is needed, use fs.append chunks under 24,000 characters with expectedPriorBytes from each receipt.`,
|
|
4246
4273
|
});
|
|
4247
4274
|
continue;
|
|
4248
4275
|
}
|
|
@@ -4261,13 +4288,13 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4261
4288
|
? "Your previous tool call JSON was INVALID, so NOTHING ran. " +
|
|
4262
4289
|
"Common causes: unescaped newlines/quotes, unbalanced braces, or content too large. " +
|
|
4263
4290
|
toolNudge(true) +
|
|
4264
|
-
" Prefer ONE complete fs.write when it fits; if cut off, continue with
|
|
4291
|
+
" Prefer ONE complete fs.write when it fits; if cut off, continue with fs.append chunks under 24,000 characters plus expectedPriorBytes. " +
|
|
4265
4292
|
"Do NOT claim any file was written until a tool call actually succeeds."
|
|
4266
4293
|
: "Your previous message contained a ```tool block, but its JSON was INVALID, so NOTHING ran. " +
|
|
4267
4294
|
"Common causes: unescaped newlines or quotes inside a string value, an extra or missing `}` / `]`, or content too large for the output window. " +
|
|
4268
4295
|
'Re-emit ONE valid ```tool block of the exact form {"name":"<tool>","args":{...}} with balanced braces. ' +
|
|
4269
4296
|
"IMPORTANT: Prefer ONE complete fs.write when it fits. Keep reasoning SHORT. " +
|
|
4270
|
-
"Only if the output window cuts you off, continue with
|
|
4297
|
+
"Only if the output window cuts you off, continue with fs.append chunks under 24,000 characters plus expectedPriorBytes. " +
|
|
4271
4298
|
"Do NOT claim any file was written until a tool call actually succeeds.",
|
|
4272
4299
|
});
|
|
4273
4300
|
continue;
|
|
@@ -4446,7 +4473,8 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4446
4473
|
let bound = [];
|
|
4447
4474
|
if (nativeToolCalls.length) {
|
|
4448
4475
|
bound = nativeToolCalls.map((tc, index) => {
|
|
4449
|
-
const
|
|
4476
|
+
const parseError = Boolean(tc.args?._parseError);
|
|
4477
|
+
const call = parseError
|
|
4450
4478
|
? {
|
|
4451
4479
|
name: tc.name || "unknown",
|
|
4452
4480
|
args: {
|
|
@@ -4454,8 +4482,11 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4454
4482
|
_raw: tc.args._raw,
|
|
4455
4483
|
},
|
|
4456
4484
|
}
|
|
4457
|
-
:
|
|
4458
|
-
|
|
4485
|
+
: canonicalizeTurnCall({ name: tc.name, args: tc.args });
|
|
4486
|
+
const native = parseError
|
|
4487
|
+
? tc
|
|
4488
|
+
: { ...tc, name: call.name, args: call.args };
|
|
4489
|
+
return { index, id: tc.id, call, native, wireId: tc.id };
|
|
4459
4490
|
});
|
|
4460
4491
|
}
|
|
4461
4492
|
else {
|
|
@@ -4463,7 +4494,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4463
4494
|
if (parsed.length === 0 && call)
|
|
4464
4495
|
parsed = [call];
|
|
4465
4496
|
bound = parsed.map((rawCall, index) => {
|
|
4466
|
-
const call =
|
|
4497
|
+
const call = canonicalizeTurnCall(rawCall);
|
|
4467
4498
|
const id = syntheticToolCallId(index);
|
|
4468
4499
|
return {
|
|
4469
4500
|
index,
|