@pentoshi/clai 3.11.3 → 3.11.5
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/runner.js +60 -16
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/stream-recovery.d.ts +15 -1
- package/dist/agent/stream-recovery.js +42 -2
- package/dist/agent/stream-recovery.js.map +1 -1
- package/dist/agent/tool-history.d.ts +6 -3
- package/dist/agent/tool-history.js +90 -11
- package/dist/agent/tool-history.js.map +1 -1
- package/dist/llm/agentrouter.js +2 -2
- package/dist/llm/agentrouter.js.map +1 -1
- package/dist/llm/bynara.js +2 -2
- package/dist/llm/bynara.js.map +1 -1
- package/dist/llm/http.d.ts +29 -3
- package/dist/llm/http.js +110 -25
- package/dist/llm/http.js.map +1 -1
- package/dist/llm/modal.js +7 -2
- package/dist/llm/modal.js.map +1 -1
- package/dist/llm/nvidia.js +6 -6
- package/dist/llm/nvidia.js.map +1 -1
- package/dist/llm/router.d.ts +1 -1
- package/dist/llm/router.js +59 -15
- package/dist/llm/router.js.map +1 -1
- package/dist/modes/ask.js +103 -32
- package/dist/modes/ask.js.map +1 -1
- package/dist/prompts/embedded.js +2 -2
- package/dist/prompts/embedded.js.map +1 -1
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/index.js +44 -4
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/system.agent.md +2 -1
- package/dist/prompts/system.ask.md +1 -0
- package/dist/safety/classifier.js +3 -0
- package/dist/safety/classifier.js.map +1 -1
- package/dist/tools/definitions.js +15 -0
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/image.d.ts +19 -1
- package/dist/tools/image.js +149 -1
- package/dist/tools/image.js.map +1 -1
- package/dist/tools/registry.js +13 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/tool-types.d.ts +8 -1
- package/dist/tui-v2/bootstrap/capabilities.d.ts +1 -0
- package/dist/tui-v2/bootstrap/capabilities.js +1 -1
- package/dist/tui-v2/bootstrap/capabilities.js.map +1 -1
- package/dist/tui-v2/components/transcript/transcript-view.js +155 -28
- package/dist/tui-v2/components/transcript/transcript-view.js.map +1 -1
- package/dist/tui-v2/components/transcript/use-transcript-selection.d.ts +2 -2
- package/dist/tui-v2/components/transcript/use-transcript-selection.js +7 -8
- package/dist/tui-v2/components/transcript/use-transcript-selection.js.map +1 -1
- package/dist/tui-v2/rendering/streaming-markdown.js +11 -2
- package/dist/tui-v2/rendering/streaming-markdown.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/ui/code-block.d.ts +40 -0
- package/dist/ui/code-block.js +380 -0
- package/dist/ui/code-block.js.map +1 -0
- package/dist/ui/markdown.js +55 -52
- package/dist/ui/markdown.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
|
@@ -3,7 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { completeWithProvider, streamWithProvider } from "../llm/router.js";
|
|
4
4
|
import { streamAlreadyEmitted } from "../llm/stream-progress.js";
|
|
5
5
|
import { classifyStreamFailure, planStreamRecovery, recordRecoveryAttempt, createStreamRecoveryState, resetStreamRecoveryState, } from "./stream-recovery.js";
|
|
6
|
-
import { resolveToolDialect } from "../llm/capabilities.js";
|
|
6
|
+
import { modelSupportsVision, resolveToolDialect } from "../llm/capabilities.js";
|
|
7
7
|
import { syntheticToolCallId, isTextOnlyModel, markTextOnlyModel, fromWireName, } from "../llm/tool-protocol.js";
|
|
8
8
|
import { sanitizeAssistantText } from "../ui/ansi-box.js";
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
@@ -38,7 +38,7 @@ function safeEngagementActionsForToolCall(call) {
|
|
|
38
38
|
}
|
|
39
39
|
import { availableToolNames, normalizeToolCall, runToolCall, BATCH_SAFE_TOOLS, } from "../tools/registry.js";
|
|
40
40
|
import { getToolDefinitions, getCompactToolDefinitions, RUNNER_META_TOOL_NAMES, } from "../tools/definitions.js";
|
|
41
|
-
import { appendAssistantWithTools, ensureUniqueToolCallIds, appendToolResult, assertValidToolProtocol, fillMissingToolResults, repairToolProtocol, } from "./tool-history.js";
|
|
41
|
+
import { appendAssistantWithTools, ensureUniqueToolCallIds, toolCallIdsInHistory, appendToolResult, assertValidToolProtocol, fillMissingToolResults, repairToolProtocol, } from "./tool-history.js";
|
|
42
42
|
import { formatViewportHint, registerViewport } from "../ui/output-pane.js";
|
|
43
43
|
import { compactMessagesWithSummary, shouldApplyAutoCompact, COMPACTION_MEMORY_PREFIX, PLAN_IMPLEMENT_MEMORY_PREFIX, isCompactionMemoryMessage, } from "./context-manager.js";
|
|
44
44
|
import { buildContextBreakdown, contextBreakdownAuditPayload, describeDominantContextBlock, toolSchemaHash, } from "./context-breakdown.js";
|
|
@@ -410,10 +410,20 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
410
410
|
const projectContext = await loadProjectContext();
|
|
411
411
|
const hasAttachedImages = Boolean(options.images?.length);
|
|
412
412
|
const imageOcrEnabled = shouldEnableImageOcr(prompt, hasAttachedImages, options.visionProven !== false);
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
//
|
|
416
|
-
|
|
413
|
+
const initialProvider = options.provider ?? config.defaultProvider;
|
|
414
|
+
const initialModel = options.model ?? config.defaultModel;
|
|
415
|
+
// image.view is different from optimistic user-attachment handling: once
|
|
416
|
+
// the tool succeeds, the model must actually receive and inspect its bytes.
|
|
417
|
+
// Offer it only with affirmative capability evidence for the active route.
|
|
418
|
+
const routeToolNames = (routeProvider, routeModel) => availableToolNames().filter((name) => {
|
|
419
|
+
if (name === "image.ocr")
|
|
420
|
+
return imageOcrEnabled;
|
|
421
|
+
if (name === "image.view") {
|
|
422
|
+
return modelSupportsVision(routeProvider, routeModel);
|
|
423
|
+
}
|
|
424
|
+
return true;
|
|
425
|
+
});
|
|
426
|
+
const toolNames = routeToolNames(initialProvider, initialModel);
|
|
417
427
|
// Build / scaffold / continuation turns must NEVER be diverted into a
|
|
418
428
|
// web.search for "current info". The /implement directive ("Execute it
|
|
419
429
|
// now…") and prompts like "create a react app" contain words such as
|
|
@@ -442,9 +452,9 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
442
452
|
!idleOrSocialPrompt &&
|
|
443
453
|
toolNames.includes("web.search") &&
|
|
444
454
|
requiresFreshWebSearch(prompt);
|
|
445
|
-
let provider =
|
|
455
|
+
let provider = initialProvider;
|
|
446
456
|
await ensureProviderConfigured(provider);
|
|
447
|
-
let model =
|
|
457
|
+
let model = initialModel;
|
|
448
458
|
// Some Groq free-tier models have a per-request/per-minute input budget
|
|
449
459
|
// below the normal agent prompt alone. Select a purpose-built compact
|
|
450
460
|
// instruction set before the request is made, rather than treating the
|
|
@@ -456,13 +466,16 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
456
466
|
return { dialect, native: dialect !== "none" };
|
|
457
467
|
};
|
|
458
468
|
let { dialect: toolDialect, native: nativeToolsActive } = resolveNativeTools(provider, model);
|
|
459
|
-
const selectToolDefs = (native, compact) => {
|
|
469
|
+
const selectToolDefs = (native, compact, routeProvider = provider, routeModel = model) => {
|
|
460
470
|
if (!native)
|
|
461
471
|
return undefined;
|
|
462
472
|
const base = compact
|
|
463
473
|
? getCompactToolDefinitions()
|
|
464
474
|
: getToolDefinitions();
|
|
465
|
-
const allow = new Set([
|
|
475
|
+
const allow = new Set([
|
|
476
|
+
...routeToolNames(routeProvider, routeModel),
|
|
477
|
+
...RUNNER_META_TOOL_NAMES,
|
|
478
|
+
]);
|
|
466
479
|
return base.filter((d) => allow.has(d.name));
|
|
467
480
|
};
|
|
468
481
|
let lastAnswer = "";
|
|
@@ -529,11 +542,13 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
529
542
|
// constitution (and, on Anthropic, the native tool schemas before it).
|
|
530
543
|
const buildStableSystemContent = (native) => {
|
|
531
544
|
const reliability = getReliabilityPolicy();
|
|
545
|
+
const visionAvailable = modelSupportsVision(provider, model);
|
|
532
546
|
return (useCompactSystemPrompt
|
|
533
547
|
? renderCompactAgentSystemPrompt
|
|
534
|
-
: renderAgentSystemPrompt)(
|
|
548
|
+
: renderAgentSystemPrompt)(routeToolNames(provider, model).join(", "), {
|
|
535
549
|
nativeTools: native,
|
|
536
550
|
stableEnvironment: true,
|
|
551
|
+
imageView: visionAvailable,
|
|
537
552
|
// E6: slim native constitution when API tool schemas are attached.
|
|
538
553
|
...(native ? { slimNative: reliability.slimNativePrompt } : {}),
|
|
539
554
|
});
|
|
@@ -892,6 +907,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
892
907
|
// reset on any successful stream so each failure episode gets a fresh
|
|
893
908
|
// budget and we only give up in the worst case.
|
|
894
909
|
let allowModelFallback = false;
|
|
910
|
+
let preferModelFallback = false;
|
|
895
911
|
const recoveryState = createStreamRecoveryState();
|
|
896
912
|
// Track tool calls truncated by the token limit so we can ask the model
|
|
897
913
|
// to retry in smaller pieces instead of leaking broken JSON as an answer.
|
|
@@ -2068,6 +2084,10 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2068
2084
|
},
|
|
2069
2085
|
confirmed: true,
|
|
2070
2086
|
userPrompt: prompt,
|
|
2087
|
+
// image.view needs the active route to check vision support and size
|
|
2088
|
+
// images to the provider's per-image budget.
|
|
2089
|
+
llmProvider: provider,
|
|
2090
|
+
llmModel: model,
|
|
2071
2091
|
sessionId: session.sessionId,
|
|
2072
2092
|
...(delegation?.taskId ? { taskId: delegation.taskId } : {}),
|
|
2073
2093
|
...(delegation ? { delegationId: delegation.id } : {}),
|
|
@@ -3158,6 +3178,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3158
3178
|
provider,
|
|
3159
3179
|
model,
|
|
3160
3180
|
allowModelFallback,
|
|
3181
|
+
preferModelFallback,
|
|
3161
3182
|
messages,
|
|
3162
3183
|
// Sampling is provider/model policy (llm/sampling.ts).
|
|
3163
3184
|
// Sending a fixed 0.2 here overrode it for every model.
|
|
@@ -3314,6 +3335,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3314
3335
|
// starts fresh (and we never give up while making progress).
|
|
3315
3336
|
resetStreamRecoveryState(recoveryState);
|
|
3316
3337
|
allowModelFallback = false;
|
|
3338
|
+
preferModelFallback = false;
|
|
3317
3339
|
}
|
|
3318
3340
|
catch (streamError) {
|
|
3319
3341
|
// User cancelled (double-Esc) — never try to recover, just stop.
|
|
@@ -3389,6 +3411,8 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3389
3411
|
retryWithoutThinking = true;
|
|
3390
3412
|
if (plan.allowModelFallback)
|
|
3391
3413
|
allowModelFallback = true;
|
|
3414
|
+
if (plan.preferModelFallback)
|
|
3415
|
+
preferModelFallback = true;
|
|
3392
3416
|
if (plan.forceCompact) {
|
|
3393
3417
|
await maybeAutoCompact(`stream-recovery:${failureKind}`, true);
|
|
3394
3418
|
}
|
|
@@ -3608,11 +3632,14 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3608
3632
|
writeNotice("info", `native tool call was truncated — salvaged ${lineCount} lines and wrote to ${salvaged.path}`, chalk.cyan(` ℹ native tool call was truncated — salvaged ${lineCount} lines to ${salvaged.path}\n`));
|
|
3609
3633
|
// Pair assistant tool_calls with synthetic results so the
|
|
3610
3634
|
// next turn is not orphaned, then nudge for append.
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3635
|
+
const salvageHistoryCalls = ensureUniqueToolCallIds(nativeToolCalls, toolCallIdsInHistory(messages));
|
|
3636
|
+
const salvagedCallIndex = nativeToolCalls.indexOf(writeTc);
|
|
3637
|
+
const salvagedCallId = salvageHistoryCalls[salvagedCallIndex]?.id ?? writeTc.id;
|
|
3638
|
+
appendAssistantWithTools(messages, assistantText.visible, salvageHistoryCalls, completion.reasoningBlock);
|
|
3639
|
+
for (const tc of salvageHistoryCalls) {
|
|
3640
|
+
appendToolResult(messages, tc.id, tc.id === salvagedCallId
|
|
3614
3641
|
? `Tool ${tc.name} result (exit=0, ok=true):\nSalvaged partial write: ${lineCount} lines to ${salvaged.path}`
|
|
3615
|
-
: `Tool ${tc.name} result (exit=1, ok=false):\nCancelled — sibling write was truncated and salvaged.`, tc.name, tc.id ===
|
|
3642
|
+
: `Tool ${tc.name} result (exit=1, ok=false):\nCancelled — sibling write was truncated and salvaged.`, tc.name, tc.id === salvagedCallId);
|
|
3616
3643
|
}
|
|
3617
3644
|
const priorBytes = writeResult.bytesOnDisk;
|
|
3618
3645
|
const salvagedToolName = salvaged.operation === "append"
|
|
@@ -4227,7 +4254,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4227
4254
|
// assistant toolCalls and role:tool results always share the same id.
|
|
4228
4255
|
// Mismatched ids make results look orphaned → repair injects placeholders
|
|
4229
4256
|
// → model thrash-retries tools that already succeeded in the UI.
|
|
4230
|
-
const historyNativeCalls = ensureUniqueToolCallIds(bound.map((b) => b.native));
|
|
4257
|
+
const historyNativeCalls = ensureUniqueToolCallIds(bound.map((b) => b.native), toolCallIdsInHistory(messages));
|
|
4231
4258
|
for (let i = 0; i < bound.length; i++) {
|
|
4232
4259
|
const fixed = historyNativeCalls[i];
|
|
4233
4260
|
bound[i] = {
|
|
@@ -4433,6 +4460,23 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4433
4460
|
content: toolContent,
|
|
4434
4461
|
});
|
|
4435
4462
|
}
|
|
4463
|
+
// image.view hands back real image bytes. Tool results are text-only
|
|
4464
|
+
// on every provider wire, and images are only serialized on user
|
|
4465
|
+
// turns, so the bytes ride a deferred internal user message that
|
|
4466
|
+
// lands after the assistant→tool group is closed — inserting it here
|
|
4467
|
+
// would orphan the remaining tool results.
|
|
4468
|
+
if (res.result.images?.length) {
|
|
4469
|
+
deferredPostToolMessages.push({
|
|
4470
|
+
role: "user",
|
|
4471
|
+
internal: true,
|
|
4472
|
+
content: `[${res.call.name}] The ${res.result.images.length === 1 ? "image" : `${res.result.images.length} images`} you asked to look at ` +
|
|
4473
|
+
`${res.result.images.length === 1 ? "is" : "are"} attached to this message` +
|
|
4474
|
+
`${res.result.images.length === 1 ? "" : ", in the order you requested them"}: ` +
|
|
4475
|
+
`${res.result.images.map((image) => image.path ?? "(unnamed)").join(", ")}. ` +
|
|
4476
|
+
"Judge them from the pixels and continue the task.",
|
|
4477
|
+
images: res.result.images,
|
|
4478
|
+
});
|
|
4479
|
+
}
|
|
4436
4480
|
// Reset retry counters — they track consecutive failures, not cumulative.
|
|
4437
4481
|
truncatedToolRetries = 0;
|
|
4438
4482
|
malformedFenceRetries = 0;
|