@pentoshi/clai 3.11.7 → 3.11.8
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/finalize-gate.d.ts +57 -0
- package/dist/agent/finalize-gate.js +146 -0
- package/dist/agent/finalize-gate.js.map +1 -0
- package/dist/agent/runner.js +58 -174
- package/dist/agent/runner.js.map +1 -1
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/index.js +23 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure finalize-time gate: pick the recovery nudge that must replace a final
|
|
3
|
+
* answer, or nothing when the turn may finalize. No I/O, no budget mutation.
|
|
4
|
+
*/
|
|
5
|
+
import { type RecoveryAction, type RecoveryBudgets } from "./must-continue.js";
|
|
6
|
+
/** Task fields the gate reads. */
|
|
7
|
+
export interface FinalizeGatePlanTask {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
state: string;
|
|
11
|
+
responderOwned?: boolean | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Plain-data snapshot of the live plan, resolved once by the caller. */
|
|
14
|
+
export interface FinalizeGatePlan {
|
|
15
|
+
kind: string;
|
|
16
|
+
hasVerifiedRuntime: boolean;
|
|
17
|
+
tasks: readonly FinalizeGatePlanTask[];
|
|
18
|
+
}
|
|
19
|
+
/** Everything the finalize cascade reads, as data. */
|
|
20
|
+
export interface FinalizeGateInput {
|
|
21
|
+
cleaned: string;
|
|
22
|
+
recovery: RecoveryBudgets;
|
|
23
|
+
toolsAttached: boolean;
|
|
24
|
+
productiveSteps: number;
|
|
25
|
+
planApproved: boolean;
|
|
26
|
+
planHasOpenWork: boolean;
|
|
27
|
+
activePlanExists: boolean;
|
|
28
|
+
wantsAction: boolean;
|
|
29
|
+
narratedAction: boolean;
|
|
30
|
+
narratedWebAction: boolean;
|
|
31
|
+
isPlanMode: boolean;
|
|
32
|
+
buildLikeTurn: boolean;
|
|
33
|
+
pentestLikeTurn: boolean;
|
|
34
|
+
buildLike: boolean;
|
|
35
|
+
pentestLike: boolean;
|
|
36
|
+
pentestSession: boolean;
|
|
37
|
+
informationalQuery: boolean;
|
|
38
|
+
idleOrSocialPrompt: boolean;
|
|
39
|
+
freshWebSearchRequired: boolean;
|
|
40
|
+
freshnessGuardText: string;
|
|
41
|
+
sawFreshWebSearch: boolean;
|
|
42
|
+
sawPlanCreateOk: boolean;
|
|
43
|
+
sawFeatureImplWrite: boolean;
|
|
44
|
+
sawScaffoldOk: boolean;
|
|
45
|
+
sawLocalAppMaterialWork: boolean;
|
|
46
|
+
sawServerStart: boolean;
|
|
47
|
+
sawServerTail: boolean;
|
|
48
|
+
sawLocalHttpProbe: boolean;
|
|
49
|
+
sawFailedLocalHttpProbe: boolean;
|
|
50
|
+
sawActivePentestTest: boolean;
|
|
51
|
+
sawSuccessfulMutation: boolean;
|
|
52
|
+
featureAppAsk: boolean;
|
|
53
|
+
projectRoot: string | undefined;
|
|
54
|
+
plan: FinalizeGatePlan | undefined;
|
|
55
|
+
deferResponderReport: boolean;
|
|
56
|
+
}
|
|
57
|
+
export declare function chooseFinalizeRecovery(input: FinalizeGateInput): RecoveryAction | undefined;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure finalize-time gate: pick the recovery nudge that must replace a final
|
|
3
|
+
* answer, or nothing when the turn may finalize. No I/O, no budget mutation.
|
|
4
|
+
*/
|
|
5
|
+
import { budgetRemaining, freestyleClaimsAppReady, looksLikeShallowPentestReport, recoveryForErrorDiagnosis, recoveryForFailedProbe, recoveryForFreshness, recoveryForMissingFeature, recoveryForMissingPlan, recoveryForNarration, recoveryForPrematureComplete, recoveryForRuntimeVerify, recoveryForShallowPentest, } from "./must-continue.js";
|
|
6
|
+
import { looksLikeErrorDiagnosisWithFixIntent, looksLikePlanNarration, } from "./tool-call-parser.js";
|
|
7
|
+
export function chooseFinalizeRecovery(input) {
|
|
8
|
+
const { cleaned, recovery, toolsAttached, productiveSteps, plan } = input;
|
|
9
|
+
const planNarrated = (input.buildLikeTurn || input.pentestLikeTurn) &&
|
|
10
|
+
!input.activePlanExists &&
|
|
11
|
+
looksLikePlanNarration(cleaned);
|
|
12
|
+
const errorFixNarration = !input.sawSuccessfulMutation &&
|
|
13
|
+
looksLikeErrorDiagnosisWithFixIntent(cleaned);
|
|
14
|
+
const shouldRetryBeforeFinalizing = productiveSteps === 0 ||
|
|
15
|
+
planNarrated ||
|
|
16
|
+
((input.narratedAction || input.narratedWebAction) &&
|
|
17
|
+
!input.informationalQuery) ||
|
|
18
|
+
(input.planApproved &&
|
|
19
|
+
input.planHasOpenWork &&
|
|
20
|
+
(input.narratedAction || errorFixNarration)) ||
|
|
21
|
+
(input.planApproved && errorFixNarration) ||
|
|
22
|
+
(input.buildLikeTurn && errorFixNarration);
|
|
23
|
+
if (input.wantsAction &&
|
|
24
|
+
cleaned.trim().length > 0 &&
|
|
25
|
+
shouldRetryBeforeFinalizing) {
|
|
26
|
+
let action;
|
|
27
|
+
if (errorFixNarration && budgetRemaining(recovery, "errorFix")) {
|
|
28
|
+
action = recoveryForErrorDiagnosis(toolsAttached);
|
|
29
|
+
}
|
|
30
|
+
else if (budgetRemaining(recovery, "actionIntent") &&
|
|
31
|
+
input.planHasOpenWork &&
|
|
32
|
+
input.planApproved) {
|
|
33
|
+
action = recoveryForNarration(toolsAttached, "plan_open");
|
|
34
|
+
}
|
|
35
|
+
else if (budgetRemaining(recovery, "actionIntent") &&
|
|
36
|
+
input.pentestLikeTurn) {
|
|
37
|
+
action = recoveryForNarration(toolsAttached, "pentest");
|
|
38
|
+
}
|
|
39
|
+
else if (budgetRemaining(recovery, "actionIntent") &&
|
|
40
|
+
(input.freshWebSearchRequired || input.narratedWebAction)) {
|
|
41
|
+
action = recoveryForNarration(toolsAttached, "web");
|
|
42
|
+
}
|
|
43
|
+
else if (budgetRemaining(recovery, "actionIntent") &&
|
|
44
|
+
input.buildLikeTurn &&
|
|
45
|
+
(planNarrated || productiveSteps > 0)) {
|
|
46
|
+
action = recoveryForNarration(toolsAttached, "build_plan_prose");
|
|
47
|
+
}
|
|
48
|
+
else if (budgetRemaining(recovery, "actionIntent") &&
|
|
49
|
+
input.buildLikeTurn) {
|
|
50
|
+
action = recoveryForNarration(toolsAttached, "build");
|
|
51
|
+
}
|
|
52
|
+
else if (budgetRemaining(recovery, "actionIntent")) {
|
|
53
|
+
action = recoveryForNarration(toolsAttached, "generic");
|
|
54
|
+
}
|
|
55
|
+
if (action)
|
|
56
|
+
return action;
|
|
57
|
+
}
|
|
58
|
+
if (input.freshWebSearchRequired &&
|
|
59
|
+
!input.sawFreshWebSearch &&
|
|
60
|
+
budgetRemaining(recovery, "freshnessUsed")) {
|
|
61
|
+
return recoveryForFreshness(input.freshnessGuardText +
|
|
62
|
+
(toolsAttached
|
|
63
|
+
? " Call the web_search tool now."
|
|
64
|
+
: " Reply with ONLY a fenced ```tool block for web.search now."));
|
|
65
|
+
}
|
|
66
|
+
if (input.isPlanMode &&
|
|
67
|
+
!input.informationalQuery &&
|
|
68
|
+
!input.idleOrSocialPrompt &&
|
|
69
|
+
budgetRemaining(recovery, "forcePlan")) {
|
|
70
|
+
if (!plan && !input.sawPlanCreateOk) {
|
|
71
|
+
return recoveryForMissingPlan(toolsAttached);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (input.buildLike &&
|
|
75
|
+
!input.pentestLike &&
|
|
76
|
+
!input.pentestSession &&
|
|
77
|
+
input.planApproved &&
|
|
78
|
+
input.featureAppAsk &&
|
|
79
|
+
!input.sawFeatureImplWrite &&
|
|
80
|
+
(input.sawScaffoldOk || input.sawLocalAppMaterialWork) &&
|
|
81
|
+
productiveSteps > 0 &&
|
|
82
|
+
budgetRemaining(recovery, "featureImpl")) {
|
|
83
|
+
return recoveryForMissingFeature(input.projectRoot);
|
|
84
|
+
}
|
|
85
|
+
if (input.buildLike &&
|
|
86
|
+
!input.pentestLike &&
|
|
87
|
+
!input.pentestSession &&
|
|
88
|
+
budgetRemaining(recovery, "runtimeVerify") &&
|
|
89
|
+
(!input.featureAppAsk || input.sawFeatureImplWrite)) {
|
|
90
|
+
const planRuntimeOk = Boolean(plan && plan.hasVerifiedRuntime);
|
|
91
|
+
const sessionRuntimeOk = input.sawServerStart &&
|
|
92
|
+
(input.sawServerTail || input.sawLocalHttpProbe || planRuntimeOk);
|
|
93
|
+
if (!planRuntimeOk && !sessionRuntimeOk) {
|
|
94
|
+
const codingPlanFinished = Boolean(plan &&
|
|
95
|
+
input.planApproved &&
|
|
96
|
+
plan.kind !== "pentest" &&
|
|
97
|
+
plan.tasks.length > 0 &&
|
|
98
|
+
plan.tasks.every((task) => task.state === "done" || task.state === "skipped"));
|
|
99
|
+
const freestyleLocalAppDone = !input.planApproved &&
|
|
100
|
+
input.sawLocalAppMaterialWork &&
|
|
101
|
+
productiveSteps > 0 &&
|
|
102
|
+
freestyleClaimsAppReady(cleaned) &&
|
|
103
|
+
(input.projectRoot !== undefined ||
|
|
104
|
+
/\b(?:npm|pnpm|yarn|bun)\s+run\s+dev\b/i.test(cleaned) ||
|
|
105
|
+
/\bopen\s+http:\/\/localhost\b/i.test(cleaned));
|
|
106
|
+
if (codingPlanFinished || freestyleLocalAppDone) {
|
|
107
|
+
return recoveryForRuntimeVerify(input.projectRoot);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (input.buildLike &&
|
|
112
|
+
!input.pentestLike &&
|
|
113
|
+
!input.pentestSession &&
|
|
114
|
+
input.sawFailedLocalHttpProbe &&
|
|
115
|
+
!input.sawLocalHttpProbe &&
|
|
116
|
+
budgetRemaining(recovery, "failedProbe") &&
|
|
117
|
+
cleaned.trim().length > 0) {
|
|
118
|
+
return recoveryForFailedProbe();
|
|
119
|
+
}
|
|
120
|
+
if ((input.pentestLike || input.pentestSession) &&
|
|
121
|
+
budgetRemaining(recovery, "shallowPentest") &&
|
|
122
|
+
looksLikeShallowPentestReport(cleaned, {
|
|
123
|
+
productiveSteps,
|
|
124
|
+
sawActiveTest: input.sawActivePentestTest,
|
|
125
|
+
})) {
|
|
126
|
+
return recoveryForShallowPentest();
|
|
127
|
+
}
|
|
128
|
+
if (input.planApproved && budgetRemaining(recovery, "prematureComplete")) {
|
|
129
|
+
const unfinished = plan?.tasks.filter((task) => !task.responderOwned &&
|
|
130
|
+
(task.state === "pending" || task.state === "in_progress"));
|
|
131
|
+
if (plan &&
|
|
132
|
+
unfinished &&
|
|
133
|
+
unfinished.length > 0 &&
|
|
134
|
+
!input.deferResponderReport) {
|
|
135
|
+
const next = unfinished[0];
|
|
136
|
+
return recoveryForPrematureComplete({
|
|
137
|
+
unfinished,
|
|
138
|
+
next,
|
|
139
|
+
pentest: plan.kind === "pentest" || input.pentestSession,
|
|
140
|
+
errorFix: errorFixNarration,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=finalize-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finalize-gate.js","sourceRoot":"","sources":["../../src/agent/finalize-gate.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/runner.js
CHANGED
|
@@ -64,7 +64,7 @@ import { resolveRequestBudget } from "./request-budget.js";
|
|
|
64
64
|
import { loadPlan, mutatePlan, markTask, appendPlanTask, readyPlanTasks, foregroundRemaining, responderOpenTasks, isPlanTerminal, isPlanSuccessful, } from "../store/plan.js";
|
|
65
65
|
import { stat } from "node:fs/promises";
|
|
66
66
|
import { isOutsideWorkingDirectory, resolveFsToolPath, } from "../tools/fs.js";
|
|
67
|
-
import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, salvageTruncatedWrite, salvageTruncatedWriteFromNative, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeIdleOrSocialPrompt, looksLikeActionNarration, looksLikeWebActionNarration,
|
|
67
|
+
import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, salvageTruncatedWrite, salvageTruncatedWriteFromNative, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeIdleOrSocialPrompt, looksLikeActionNarration, looksLikeWebActionNarration, localHttpProbeIsFailure, localHttpProbeIsSuccess, requiresFreshWebSearch, freshnessGuardMessage, buildWorkflowDirective, narrowNmapOperationDirective, pentestWorkflowDirective, pentestNoLocalServerDirective, shouldDimToolChatter, looksLikePromptLeak, } from "./tool-call-parser.js";
|
|
68
68
|
import { createSessionPolicy, isPreApprovalAllowedTool, isPlanModeAllowedShellCommand, isPlanModeAllowedTool, isPlanApprovedByStatus, planHasOpenWork, isAbortError, shouldEnableImageOcr, } from "./session-policy.js";
|
|
69
69
|
import { saveToolOutput, summarizeOutput, formatToolContext, } from "./tool-output-formatting.js";
|
|
70
70
|
import { codingSessionFromContext, isProtocolPlaceholderOutput, progressPauseMode, } from "./progress-pause-policy.js";
|
|
@@ -75,7 +75,8 @@ import { absorbLooseWorkIntoLedger, applyDestinationCwd, canMarkTaskDone, hasLoc
|
|
|
75
75
|
import { buildSessionStateBlock, inferNextHint, upsertSessionStateMessage, } from "./session-state.js";
|
|
76
76
|
import { buildContinueOrientation, looksLikeContinueOrResumePrompt, } from "./continue-orient.js";
|
|
77
77
|
import { detectPackageManager } from "./workspace-orient.js";
|
|
78
|
-
import { budgetRemaining, consumeBudget, createRecoveryBudgets,
|
|
78
|
+
import { budgetRemaining, consumeBudget, createRecoveryBudgets, } from "./must-continue.js";
|
|
79
|
+
import { chooseFinalizeRecovery } from "./finalize-gate.js";
|
|
79
80
|
import { outOfScopeToolMessage, scopeContextMessage } from "./scope-context.js";
|
|
80
81
|
import { EngagementPolicyEngine, actionFromUrl, engagementActionsForToolCall, evaluateEngagementAction, } from "../safety/engagement-policy.js";
|
|
81
82
|
import { patchPlanMeta } from "../store/plan.js";
|
|
@@ -543,6 +544,9 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
543
544
|
// Request, project, workspace, recovery, scope, and plan state are appended
|
|
544
545
|
// later as system-marked turns so a changing byte cannot invalidate the
|
|
545
546
|
// constitution (and, on Anthropic, the native tool schemas before it).
|
|
547
|
+
// The red-team methodology block is ~940 tokens on every request. Attach it
|
|
548
|
+
// only when this turn is actually a remote-security engagement.
|
|
549
|
+
const pentestPromptTurn = pentestLikeTurn || activePlan?.kind === "pentest";
|
|
546
550
|
const buildStableSystemContent = (native) => {
|
|
547
551
|
const reliability = getReliabilityPolicy();
|
|
548
552
|
const visionAvailable = modelSupportsVision(provider, model);
|
|
@@ -554,6 +558,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
554
558
|
imageView: visionAvailable,
|
|
555
559
|
// E6: slim native constitution when API tool schemas are attached.
|
|
556
560
|
...(native ? { slimNative: reliability.slimNativePrompt } : {}),
|
|
561
|
+
...(pentestPromptTurn ? { pentest: true } : {}),
|
|
557
562
|
});
|
|
558
563
|
};
|
|
559
564
|
const systemSections = [renderRequestEnvironmentContext()];
|
|
@@ -4022,182 +4027,61 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4022
4027
|
"If analysis is incomplete, call only the bounded evidence tool needed now. If each result has been analyzed and is satisfactory, you MUST call job.read with its jobId or exact notificationId before giving a final response. job.read does not require an active plan; do not create or update a plan merely to acknowledge a result."));
|
|
4023
4028
|
continue;
|
|
4024
4029
|
}
|
|
4025
|
-
const
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
(
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
}
|
|
4071
|
-
else if (budgetRemaining(recovery, "actionIntent")) {
|
|
4072
|
-
action = recoveryForNarration(toolsAttached, "generic");
|
|
4073
|
-
}
|
|
4074
|
-
if (action) {
|
|
4075
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4076
|
-
commitAssistantRetry(assistantText.visible);
|
|
4077
|
-
messages.push(recoveryUserMessage(action.message));
|
|
4078
|
-
continue;
|
|
4079
|
-
}
|
|
4080
|
-
}
|
|
4081
|
-
if (freshWebSearchRequired &&
|
|
4082
|
-
!sawFreshWebSearch &&
|
|
4083
|
-
budgetRemaining(recovery, "freshnessUsed")) {
|
|
4084
|
-
const action = recoveryForFreshness(freshnessGuardMessage() +
|
|
4085
|
-
(toolsAttached
|
|
4086
|
-
? " Call the web_search tool now."
|
|
4087
|
-
: " Reply with ONLY a fenced ```tool block for web.search now."));
|
|
4088
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4089
|
-
commitAssistantRetry(assistantText.visible);
|
|
4090
|
-
messages.push(recoveryUserMessage(action.message));
|
|
4091
|
-
continue;
|
|
4092
|
-
}
|
|
4093
|
-
if (isPlanMode &&
|
|
4094
|
-
!informationalQuery &&
|
|
4095
|
-
!idleOrSocialPrompt &&
|
|
4096
|
-
budgetRemaining(recovery, "forcePlan")) {
|
|
4097
|
-
const planAtEnd = await loadPlan(session.sessionId).catch(() => undefined);
|
|
4098
|
-
if (!planAtEnd && !sawPlanCreateOk) {
|
|
4099
|
-
const action = recoveryForMissingPlan(toolsAttached);
|
|
4100
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4101
|
-
commitAssistantRetry(assistantText.visible);
|
|
4102
|
-
messages.push(recoveryUserMessage(action.message));
|
|
4103
|
-
continue;
|
|
4104
|
-
}
|
|
4105
|
-
}
|
|
4106
|
-
if (buildLike &&
|
|
4107
|
-
!pentestLike &&
|
|
4108
|
-
!pentestSession &&
|
|
4109
|
-
session.planApproved.value &&
|
|
4110
|
-
featureAppAsk &&
|
|
4111
|
-
!sawFeatureImplWrite &&
|
|
4112
|
-
(sawScaffoldOk || sawLocalAppMaterialWork) &&
|
|
4113
|
-
productiveSteps > 0 &&
|
|
4114
|
-
budgetRemaining(recovery, "featureImpl")) {
|
|
4115
|
-
const action = recoveryForMissingFeature(getActiveProjectRoot());
|
|
4116
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4117
|
-
commitAssistantRetry(assistantText.visible);
|
|
4118
|
-
messages.push(recoveryUserMessage(action.message));
|
|
4119
|
-
continue;
|
|
4120
|
-
}
|
|
4121
|
-
if (buildLike &&
|
|
4122
|
-
!pentestLike &&
|
|
4123
|
-
!pentestSession &&
|
|
4124
|
-
budgetRemaining(recovery, "runtimeVerify") &&
|
|
4125
|
-
(!featureAppAsk || sawFeatureImplWrite)) {
|
|
4126
|
-
const runtimePlan = await loadPlan(session.sessionId).catch(() => undefined);
|
|
4127
|
-
// Durable plan evidence or multi-signal proof this turn is enough
|
|
4128
|
-
const planRuntimeOk = Boolean(runtimePlan && planHasVerifiedRuntime(runtimePlan));
|
|
4129
|
-
const sessionRuntimeOk = sawServerStart &&
|
|
4130
|
-
(sawServerTail || sawLocalHttpProbe || planRuntimeOk);
|
|
4131
|
-
if (!planRuntimeOk && !sessionRuntimeOk) {
|
|
4132
|
-
const codingPlanFinished = Boolean(runtimePlan &&
|
|
4133
|
-
session.planApproved.value &&
|
|
4134
|
-
runtimePlan.kind !== "pentest" &&
|
|
4135
|
-
runtimePlan.tasks.length > 0 &&
|
|
4136
|
-
runtimePlan.tasks.every((task) => task.state === "done" || task.state === "skipped"));
|
|
4137
|
-
const freestyleLocalAppDone = !session.planApproved.value &&
|
|
4138
|
-
sawLocalAppMaterialWork &&
|
|
4139
|
-
productiveSteps > 0 &&
|
|
4140
|
-
freestyleClaimsAppReady(cleaned) &&
|
|
4141
|
-
(getActiveProjectRoot() !== undefined ||
|
|
4142
|
-
/\b(?:npm|pnpm|yarn|bun)\s+run\s+dev\b/i.test(cleaned) ||
|
|
4143
|
-
/\bopen\s+http:\/\/localhost\b/i.test(cleaned));
|
|
4144
|
-
if (codingPlanFinished || freestyleLocalAppDone) {
|
|
4145
|
-
const action = recoveryForRuntimeVerify(getActiveProjectRoot());
|
|
4146
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4147
|
-
commitAssistantRetry(assistantText.visible);
|
|
4148
|
-
messages.push(recoveryUserMessage(action.message));
|
|
4149
|
-
continue;
|
|
4030
|
+
const deferResponderReport = session.planApproved.value &&
|
|
4031
|
+
budgetRemaining(recovery, "prematureComplete")
|
|
4032
|
+
? shouldYieldForDeclaredResponderDependency(livePlanAtCompletion, jobManager.getRunningJobs(session.sessionId), jobManager.getPendingNotifications(session.sessionId), responderWakeNotificationId)
|
|
4033
|
+
: false;
|
|
4034
|
+
const finalizeRecovery = chooseFinalizeRecovery({
|
|
4035
|
+
cleaned,
|
|
4036
|
+
recovery,
|
|
4037
|
+
toolsAttached,
|
|
4038
|
+
productiveSteps,
|
|
4039
|
+
planApproved: session.planApproved.value,
|
|
4040
|
+
planHasOpenWork: planHasOpenWorkNow,
|
|
4041
|
+
activePlanExists: Boolean(activePlan),
|
|
4042
|
+
wantsAction,
|
|
4043
|
+
narratedAction,
|
|
4044
|
+
narratedWebAction,
|
|
4045
|
+
isPlanMode,
|
|
4046
|
+
buildLikeTurn,
|
|
4047
|
+
pentestLikeTurn,
|
|
4048
|
+
buildLike,
|
|
4049
|
+
pentestLike,
|
|
4050
|
+
pentestSession,
|
|
4051
|
+
informationalQuery,
|
|
4052
|
+
idleOrSocialPrompt,
|
|
4053
|
+
freshWebSearchRequired,
|
|
4054
|
+
freshnessGuardText: freshWebSearchRequired
|
|
4055
|
+
? freshnessGuardMessage()
|
|
4056
|
+
: "",
|
|
4057
|
+
sawFreshWebSearch,
|
|
4058
|
+
sawPlanCreateOk,
|
|
4059
|
+
sawFeatureImplWrite,
|
|
4060
|
+
sawScaffoldOk,
|
|
4061
|
+
sawLocalAppMaterialWork,
|
|
4062
|
+
sawServerStart,
|
|
4063
|
+
sawServerTail,
|
|
4064
|
+
sawLocalHttpProbe,
|
|
4065
|
+
sawFailedLocalHttpProbe,
|
|
4066
|
+
sawActivePentestTest,
|
|
4067
|
+
sawSuccessfulMutation,
|
|
4068
|
+
featureAppAsk,
|
|
4069
|
+
projectRoot: getActiveProjectRoot(),
|
|
4070
|
+
plan: livePlanAtCompletion
|
|
4071
|
+
? {
|
|
4072
|
+
kind: livePlanAtCompletion.kind,
|
|
4073
|
+
hasVerifiedRuntime: planHasVerifiedRuntime(livePlanAtCompletion),
|
|
4074
|
+
tasks: livePlanAtCompletion.tasks,
|
|
4150
4075
|
}
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
sawFailedLocalHttpProbe &&
|
|
4157
|
-
!sawLocalHttpProbe &&
|
|
4158
|
-
budgetRemaining(recovery, "failedProbe") &&
|
|
4159
|
-
cleaned.trim().length > 0) {
|
|
4160
|
-
const action = recoveryForFailedProbe();
|
|
4161
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4162
|
-
commitAssistantRetry(assistantText.visible);
|
|
4163
|
-
messages.push(recoveryUserMessage(action.message));
|
|
4164
|
-
continue;
|
|
4165
|
-
}
|
|
4166
|
-
if ((pentestLike || pentestSession) &&
|
|
4167
|
-
budgetRemaining(recovery, "shallowPentest") &&
|
|
4168
|
-
looksLikeShallowPentestReport(cleaned, {
|
|
4169
|
-
productiveSteps,
|
|
4170
|
-
sawActiveTest: sawActivePentestTest,
|
|
4171
|
-
})) {
|
|
4172
|
-
const action = recoveryForShallowPentest();
|
|
4173
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4076
|
+
: undefined,
|
|
4077
|
+
deferResponderReport,
|
|
4078
|
+
});
|
|
4079
|
+
if (finalizeRecovery) {
|
|
4080
|
+
consumeBudget(recovery, finalizeRecovery.budgetKey);
|
|
4174
4081
|
commitAssistantRetry(assistantText.visible);
|
|
4175
|
-
messages.push(recoveryUserMessage(
|
|
4082
|
+
messages.push(recoveryUserMessage(finalizeRecovery.message));
|
|
4176
4083
|
continue;
|
|
4177
4084
|
}
|
|
4178
|
-
if (session.planApproved.value &&
|
|
4179
|
-
budgetRemaining(recovery, "prematureComplete")) {
|
|
4180
|
-
const livePlan = await loadPlan(session.sessionId).catch(() => undefined);
|
|
4181
|
-
const unfinished = livePlan?.tasks.filter((task) => !task.responderOwned &&
|
|
4182
|
-
(task.state === "pending" || task.state === "in_progress"));
|
|
4183
|
-
const deferReport = shouldYieldForDeclaredResponderDependency(livePlan, jobManager.getRunningJobs(session.sessionId), jobManager.getPendingNotifications(session.sessionId), responderWakeNotificationId);
|
|
4184
|
-
if (livePlan &&
|
|
4185
|
-
unfinished &&
|
|
4186
|
-
unfinished.length > 0 &&
|
|
4187
|
-
!deferReport) {
|
|
4188
|
-
const next = unfinished[0];
|
|
4189
|
-
const action = recoveryForPrematureComplete({
|
|
4190
|
-
unfinished,
|
|
4191
|
-
next,
|
|
4192
|
-
pentest: livePlan.kind === "pentest" || pentestSession,
|
|
4193
|
-
errorFix: errorFixNarration,
|
|
4194
|
-
});
|
|
4195
|
-
consumeBudget(recovery, action.budgetKey);
|
|
4196
|
-
commitAssistantRetry(assistantText.visible);
|
|
4197
|
-
messages.push(recoveryUserMessage(action.message));
|
|
4198
|
-
continue;
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
4085
|
let outcomeStatus = "succeeded";
|
|
4202
4086
|
const remainingCriteria = [];
|
|
4203
4087
|
if (session.planApproved.value) {
|