@pentoshi/clai 3.11.31 → 3.11.33
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.js +1 -1
- package/dist/agent/compaction-summary.js.map +1 -1
- package/dist/agent/continue-orient.js +1 -1
- package/dist/agent/continue-orient.js.map +1 -1
- package/dist/agent/finalize-gate.d.ts +0 -3
- package/dist/agent/finalize-gate.js +2 -10
- package/dist/agent/finalize-gate.js.map +1 -1
- package/dist/agent/must-continue.d.ts +1 -3
- package/dist/agent/must-continue.js +0 -16
- package/dist/agent/must-continue.js.map +1 -1
- package/dist/agent/plan-tool.js +1 -1
- package/dist/agent/plan-tool.js.map +1 -1
- package/dist/agent/runner.js +23 -113
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/task-analyzer.js +1 -1
- package/dist/agent/task-analyzer.js.map +1 -1
- package/dist/agent/tool-call-parser.d.ts +2 -4
- package/dist/agent/tool-call-parser.js +47 -67
- package/dist/agent/tool-call-parser.js.map +1 -1
- package/dist/agent/workspace-orient.js +1 -1
- package/dist/agent/workspace-orient.js.map +1 -1
- package/dist/prompts/embedded.js +1 -1
- package/dist/prompts/embedded.js.map +1 -1
- package/dist/prompts/index.js +13 -6
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/system.agent.md +6 -4
- package/dist/tui-v2/rendering/strip-tool-surfaces.js +7 -1
- package/dist/tui-v2/rendering/strip-tool-surfaces.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
|
@@ -63,14 +63,14 @@ import { CompactionAttemptLedger, compactionAttemptKey, } from "./compaction-att
|
|
|
63
63
|
import { loadPlan, mutatePlan, markTask, appendPlanTask, readyPlanTasks, foregroundRemaining, responderOpenTasks, isPlanTerminal, isPlanSuccessful, } from "../store/plan.js";
|
|
64
64
|
import { stat } from "node:fs/promises";
|
|
65
65
|
import { isOutsideWorkingDirectory, resolveFsToolPath, } from "../tools/fs.js";
|
|
66
|
-
import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, salvageTruncatedWrite, salvageTruncatedWriteFromNative, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeIdleOrSocialPrompt, looksLikeActionNarration, looksLikeWebActionNarration, localHttpProbeIsFailure, localHttpProbeIsSuccess,
|
|
66
|
+
import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, salvageTruncatedWrite, salvageTruncatedWriteFromNative, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeIdleOrSocialPrompt, looksLikeActionNarration, looksLikeWebActionNarration, localHttpProbeIsFailure, localHttpProbeIsSuccess, buildWorkflowDirective, narrowNmapOperationDirective, pentestWorkflowDirective, pentestNoLocalServerDirective, shouldDimToolChatter, looksLikePromptLeak, } from "./tool-call-parser.js";
|
|
67
67
|
import { createSessionPolicy, isPreApprovalAllowedTool, isPlanModeAllowedShellCommand, isPlanModeAllowedTool, isPlanApprovedByStatus, planHasOpenWork, isAbortError, shouldEnableImageOcr, } from "./session-policy.js";
|
|
68
68
|
import { saveToolOutput, summarizeOutput, formatToolContext, } from "./tool-output-formatting.js";
|
|
69
69
|
import { codingSessionFromContext, isProtocolPlaceholderOutput, progressPauseMode, } from "./progress-pause-policy.js";
|
|
70
70
|
import { renderPlanForTerminal, planContextMessage, upsertPlanContextMessage, handlePlanTool, resolvePlanTaskId, } from "./plan-tool.js";
|
|
71
71
|
import { readDeclaredParentTaskId, resolveResponderParent, isExplicitResponderDelegation, delegationTaskTitle, } from "./responder-parent.js";
|
|
72
72
|
import { readTaskUpdateArgs, distinctAdvancingTaskIds, isSimultaneousTaskAdvance, batchUpdateSignature, buildMultiUpdateReminder, buildMultiOpenRejection, multiUpdateToast, multiOpenToast, openingTaskIds, } from "./task-sync.js";
|
|
73
|
-
import { absorbLooseWorkIntoLedger, applyDestinationCwd, canMarkTaskDone, hasLocalRuntimeProof, hasRemoteWorkProof, isDevServerCall, isEvidenceWorkTool, isFeatureImplementationCall, isPackageInstallCommand, isPlanPreflightTool, isPortListeningOutput, isReadOnlyReconTool, isRemoteActiveTestCall,
|
|
73
|
+
import { absorbLooseWorkIntoLedger, applyDestinationCwd, canMarkTaskDone, hasLocalRuntimeProof, hasRemoteWorkProof, isDevServerCall, isEvidenceWorkTool, isFeatureImplementationCall, isPackageInstallCommand, isPlanPreflightTool, isPortListeningOutput, isReadOnlyReconTool, isRemoteActiveTestCall, isRemoteReconToolCall, isScaffoldCreateCommand, isServerReadyOutput, ledgerFromTaskEvidence, pickPendingTaskForToolCall, recordTaskWorkSuccess, resolveUserDestinationHint, taskEvidenceFromLedger, TOOL_ABORT_GRACE_MS, toolHardBudgetMs, toolStallBudgetMs, userAskedForFeatureApp, } from "./task-evidence.js";
|
|
74
74
|
import { buildSessionStateBlock, inferNextHint, upsertSessionStateMessage, } from "./session-state.js";
|
|
75
75
|
import { buildContinueOrientation, looksLikeContinueOrResumePrompt, } from "./continue-orient.js";
|
|
76
76
|
import { detectPackageManager } from "./workspace-orient.js";
|
|
@@ -442,19 +442,12 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
442
442
|
// brand-new plan (the exact failure where "what do u know till now"
|
|
443
443
|
// triggered explore→plan and created an unrelated "Enhance clai" plan).
|
|
444
444
|
const informationalQuery = looksLikeInformationalQuery(prompt);
|
|
445
|
-
// Greetings / thanks / short acks must never force tools
|
|
446
|
-
// freshness retries — a false "act don't narrate" path burned tokens on
|
|
447
|
-
// web.search recovery loops after a simple "hi".
|
|
445
|
+
// Greetings / thanks / short acks must never force tools or plans.
|
|
448
446
|
const idleOrSocialPrompt = looksLikeIdleOrSocialPrompt(prompt);
|
|
449
447
|
suppressOutcomeDiagnostics =
|
|
450
448
|
informationalQuery ||
|
|
451
449
|
idleOrSocialPrompt ||
|
|
452
450
|
looksLikeContinueOrResumePrompt(prompt);
|
|
453
|
-
const freshWebSearchRequired = !buildLikeTurn &&
|
|
454
|
-
!pentestLikeTurn &&
|
|
455
|
-
!idleOrSocialPrompt &&
|
|
456
|
-
toolNames.includes("web.search") &&
|
|
457
|
-
requiresFreshWebSearch(prompt);
|
|
458
451
|
let provider = initialProvider;
|
|
459
452
|
await ensureProviderConfigured(provider);
|
|
460
453
|
let model = initialModel;
|
|
@@ -608,9 +601,6 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
608
601
|
orientInput.candidateProject = candidate;
|
|
609
602
|
systemSections.push(buildWorkspaceOrientation(orientInput));
|
|
610
603
|
}
|
|
611
|
-
if (freshWebSearchRequired) {
|
|
612
|
-
systemSections.push(freshnessGuardMessage());
|
|
613
|
-
}
|
|
614
604
|
// The live plan is mutable state: it is injected once as a keyed request
|
|
615
605
|
// suffix (upsertPlanContextMessage) instead of being frozen into the stable
|
|
616
606
|
// system prefix, so the model never sees a stale and a fresh plan together.
|
|
@@ -719,7 +709,6 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
719
709
|
content.startsWith("ACTIVE PROJECT ROOT:") ||
|
|
720
710
|
content.startsWith("USER DESTINATION:") ||
|
|
721
711
|
content.startsWith("WORKSPACE STATUS") ||
|
|
722
|
-
content.startsWith("Freshness guard for this turn:") ||
|
|
723
712
|
content.includes("MODE") ||
|
|
724
713
|
content.includes("OUTCOME"),
|
|
725
714
|
}));
|
|
@@ -939,7 +928,6 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
939
928
|
// (e.g. malformed extra/missing braces that are NOT simple truncation). We
|
|
940
929
|
// retry instead of leaking the raw block as the final answer.
|
|
941
930
|
let malformedFenceRetries = 0;
|
|
942
|
-
let sawFreshWebSearch = false;
|
|
943
931
|
const recovery = createRecoveryBudgets();
|
|
944
932
|
let sawServerStart = false;
|
|
945
933
|
let sawPlanCreateOk = false;
|
|
@@ -1011,70 +999,6 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
1011
999
|
remoteWorkVerified: planHasVerifiedRemoteWork(plan),
|
|
1012
1000
|
});
|
|
1013
1001
|
};
|
|
1014
|
-
const reconcileOpenTaskBeforeFinalizing = async () => {
|
|
1015
|
-
let plan = await loadPlan(session.sessionId).catch(() => undefined);
|
|
1016
|
-
const open = plan?.tasks.find((task) => task.state === "in_progress" && !task.responderOwned);
|
|
1017
|
-
if (!plan || !open)
|
|
1018
|
-
return plan;
|
|
1019
|
-
const gate = completionGateForTask(plan, open.id);
|
|
1020
|
-
if (!gate.ok)
|
|
1021
|
-
return plan;
|
|
1022
|
-
const reconciledTaskIds = [open.id];
|
|
1023
|
-
const working = plan;
|
|
1024
|
-
markTask(working, open.id, "done", "Completion reconciled from verified task evidence.");
|
|
1025
|
-
while (true) {
|
|
1026
|
-
const observation = readyPlanTasks(working).find((task) => isRuntimeObservationTask(task.title) ||
|
|
1027
|
-
(working.kind === "pentest" && isRemoteObservationTask(task.title)));
|
|
1028
|
-
if (!observation)
|
|
1029
|
-
break;
|
|
1030
|
-
const observationGate = completionGateForTask(working, observation.id);
|
|
1031
|
-
if (!observationGate.ok)
|
|
1032
|
-
break;
|
|
1033
|
-
markTask(working, observation.id, "done", working.kind === "pentest"
|
|
1034
|
-
? "Satisfied by verified remote evidence from the preceding task."
|
|
1035
|
-
: "Satisfied by the verified runtime evidence from the preceding task.");
|
|
1036
|
-
reconciledTaskIds.push(observation.id);
|
|
1037
|
-
}
|
|
1038
|
-
if (working.status === "draft" || working.status === "approved") {
|
|
1039
|
-
working.status = "in_progress";
|
|
1040
|
-
}
|
|
1041
|
-
if (isPlanTerminal(working)) {
|
|
1042
|
-
working.status = isPlanSuccessful(working) ? "completed" : "abandoned";
|
|
1043
|
-
}
|
|
1044
|
-
// Replay the same reconciliation as a reducer so a concurrent
|
|
1045
|
-
// responder settlement is preserved instead of overwritten.
|
|
1046
|
-
const reconciledNotes = new Map(reconciledTaskIds.map((id) => [
|
|
1047
|
-
id,
|
|
1048
|
-
working.tasks.find((task) => task.id === id)?.note,
|
|
1049
|
-
]));
|
|
1050
|
-
const reconcileResult = await mutatePlan(session.sessionId, (draft) => {
|
|
1051
|
-
let changed = false;
|
|
1052
|
-
for (const [id, note] of reconciledNotes) {
|
|
1053
|
-
const task = draft.tasks.find((candidate) => candidate.id === id);
|
|
1054
|
-
if (!task || task.state === "done")
|
|
1055
|
-
continue;
|
|
1056
|
-
task.state = "done";
|
|
1057
|
-
if (note !== undefined)
|
|
1058
|
-
task.note = note;
|
|
1059
|
-
changed = true;
|
|
1060
|
-
}
|
|
1061
|
-
if (draft.status === "draft" || draft.status === "approved") {
|
|
1062
|
-
draft.status = "in_progress";
|
|
1063
|
-
changed = true;
|
|
1064
|
-
}
|
|
1065
|
-
if (isPlanTerminal(draft)) {
|
|
1066
|
-
draft.status = isPlanSuccessful(draft) ? "completed" : "abandoned";
|
|
1067
|
-
changed = true;
|
|
1068
|
-
}
|
|
1069
|
-
return changed;
|
|
1070
|
-
}).catch(() => undefined);
|
|
1071
|
-
if (reconcileResult?.ok && reconcileResult.plan)
|
|
1072
|
-
plan = reconcileResult.plan;
|
|
1073
|
-
writePlanUpdate(plan, renderPlanForTerminal(plan) + "\n");
|
|
1074
|
-
writeNotice("info", `reconciled ${reconciledTaskIds.map((id) => `[${id}]`).join(", ")} from verified evidence`, chalk.dim(` ℹ reconciled ${reconciledTaskIds.map((id) => `[${id}]`).join(", ")} from verified evidence — no duplicate verification\n`));
|
|
1075
|
-
taskWorkLedger = null;
|
|
1076
|
-
return plan;
|
|
1077
|
-
};
|
|
1078
1002
|
async function persistProjectRootOnPlan(root) {
|
|
1079
1003
|
const pm = detectPackageManager(root);
|
|
1080
1004
|
// Metadata patches go through the transactional boundary so a
|
|
@@ -1806,9 +1730,6 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
1806
1730
|
};
|
|
1807
1731
|
}
|
|
1808
1732
|
}
|
|
1809
|
-
if (call.name === "web.search") {
|
|
1810
|
-
sawFreshWebSearch = true;
|
|
1811
|
-
}
|
|
1812
1733
|
if (!alreadyPrintedIds.has(toolEventId)) {
|
|
1813
1734
|
const toolCallLine = chalk.cyan(` ▶ ${call.name}`) + chalk.gray(` ${formatToolArgs(call)}`);
|
|
1814
1735
|
writeToolCall(toolEventId, call, styleToolChatter(call, toolCallLine) + "\n");
|
|
@@ -3274,9 +3195,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3274
3195
|
...(toolsAttached
|
|
3275
3196
|
? {
|
|
3276
3197
|
tools: turnTools,
|
|
3277
|
-
toolChoice:
|
|
3278
|
-
? { type: "function", name: "web.search" }
|
|
3279
|
-
: "auto",
|
|
3198
|
+
toolChoice: "auto",
|
|
3280
3199
|
parallelToolCalls: true,
|
|
3281
3200
|
// P2-3: emit tool cards as soon as the function name arrives.
|
|
3282
3201
|
onToolCallDelta: (delta) => {
|
|
@@ -3709,12 +3628,15 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3709
3628
|
}
|
|
3710
3629
|
if (nativeToolCalls.length) {
|
|
3711
3630
|
const first = nativeToolCalls[0];
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3631
|
+
call = first.args?._parseError
|
|
3632
|
+
? {
|
|
3633
|
+
name: first.name || "unknown",
|
|
3634
|
+
args: {
|
|
3635
|
+
__nativeParseError: true,
|
|
3636
|
+
_raw: first.args._raw,
|
|
3637
|
+
},
|
|
3638
|
+
}
|
|
3639
|
+
: normalizeToolCall({ name: first.name, args: first.args });
|
|
3718
3640
|
}
|
|
3719
3641
|
else {
|
|
3720
3642
|
call = parseToolCall(assistantText.visible, {
|
|
@@ -3821,19 +3743,15 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3821
3743
|
retryWithoutThinking = true;
|
|
3822
3744
|
commitAssistantRetry(assistantText.visible);
|
|
3823
3745
|
// Keep nudges SHORT — cheap models lose the key instruction in long text.
|
|
3824
|
-
const buildNudge =
|
|
3746
|
+
const buildNudge = isPlanMode && !activePlan
|
|
3825
3747
|
? toolsAttached
|
|
3826
|
-
? "No visible output.
|
|
3827
|
-
: "No visible output.
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
: toolsAttached
|
|
3834
|
-
? "No visible output. " + toolNudge(true)
|
|
3835
|
-
: "No visible output. Emit a ```tool block or give your final answer. " +
|
|
3836
|
-
"Do NOT hide tool calls in <think> tags — put them in the visible response.";
|
|
3748
|
+
? "No visible output. In plan mode: gather context or call plan.create when ready (do not only describe the plan)."
|
|
3749
|
+
: "No visible output. In plan mode: emit a ```tool block for research/recon or plan.create. " +
|
|
3750
|
+
"Do NOT hide tool calls in <think> tags — put them in the visible response."
|
|
3751
|
+
: toolsAttached
|
|
3752
|
+
? "No visible output. " + toolNudge(true)
|
|
3753
|
+
: "No visible output. Emit a ```tool block or give your final answer. " +
|
|
3754
|
+
"Do NOT hide tool calls in <think> tags — put them in the visible response.";
|
|
3837
3755
|
messages.push(recoveryUserMessage(buildNudge));
|
|
3838
3756
|
continue;
|
|
3839
3757
|
}
|
|
@@ -4029,15 +3947,12 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4029
3947
|
const cleaned = collapseRepeatedText(stripSentinelTokens(canonicalAssistantVisible));
|
|
4030
3948
|
const narratedAction = looksLikeActionNarration(cleaned);
|
|
4031
3949
|
const narratedWebAction = looksLikeWebActionNarration(cleaned);
|
|
4032
|
-
const
|
|
4033
|
-
const livePlanAtCompletion = reconciledPlanAtCompletion ??
|
|
4034
|
-
(await loadPlan(session.sessionId).catch(() => undefined));
|
|
3950
|
+
const livePlanAtCompletion = await loadPlan(session.sessionId).catch(() => undefined);
|
|
4035
3951
|
const planStatusAtCompletion = livePlanAtCompletion?.status ?? activePlan?.status;
|
|
4036
3952
|
const completedPlanDuringThisTurn = activePlan?.status !== "completed" &&
|
|
4037
3953
|
planStatusAtCompletion === "completed";
|
|
4038
3954
|
const planHasOpenWorkNow = planHasOpenWork(planStatusAtCompletion);
|
|
4039
|
-
const userExpectsWork =
|
|
4040
|
-
(planHasOpenWorkNow && session.planApproved.value) ||
|
|
3955
|
+
const userExpectsWork = (planHasOpenWorkNow && session.planApproved.value) ||
|
|
4041
3956
|
(!informationalQuery &&
|
|
4042
3957
|
!idleOrSocialPrompt &&
|
|
4043
3958
|
(buildLikeTurn || pentestLikeTurn));
|
|
@@ -4105,11 +4020,6 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4105
4020
|
pentestSession,
|
|
4106
4021
|
informationalQuery,
|
|
4107
4022
|
idleOrSocialPrompt,
|
|
4108
|
-
freshWebSearchRequired,
|
|
4109
|
-
freshnessGuardText: freshWebSearchRequired
|
|
4110
|
-
? freshnessGuardMessage()
|
|
4111
|
-
: "",
|
|
4112
|
-
sawFreshWebSearch,
|
|
4113
4023
|
sawPlanCreateOk,
|
|
4114
4024
|
sawFeatureImplWrite,
|
|
4115
4025
|
sawScaffoldOk,
|