@pentoshi/clai 3.11.7 → 3.11.9
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/README.md +15 -0
- package/dist/agent/confirm-port.d.ts +4 -0
- package/dist/agent/confirm-port.js +9 -1
- package/dist/agent/confirm-port.js.map +1 -1
- 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 +71 -179
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tool-call-parser.js +3 -0
- package/dist/agent/tool-call-parser.js.map +1 -1
- package/dist/app/adapters/current-interactive-sessions-adapter.d.ts +4 -0
- package/dist/app/adapters/current-interactive-sessions-adapter.js +12 -0
- package/dist/app/adapters/current-interactive-sessions-adapter.js.map +1 -0
- package/dist/app/controllers/cancel-all-result.d.ts +8 -0
- package/dist/app/controllers/cancel-all-result.js +32 -0
- package/dist/app/controllers/cancel-all-result.js.map +1 -0
- package/dist/app/controllers/session-controller.d.ts +3 -0
- package/dist/app/controllers/session-controller.js +22 -6
- package/dist/app/controllers/session-controller.js.map +1 -1
- package/dist/app/ports/interactive-sessions-port.d.ts +22 -0
- package/dist/app/ports/interactive-sessions-port.js +9 -0
- package/dist/app/ports/interactive-sessions-port.js.map +1 -0
- package/dist/index.js +24 -13
- package/dist/index.js.map +1 -1
- package/dist/interactive-session/artifact-writer.d.ts +54 -0
- package/dist/interactive-session/artifact-writer.js +161 -0
- package/dist/interactive-session/artifact-writer.js.map +1 -0
- package/dist/interactive-session/cleanup.d.ts +37 -0
- package/dist/interactive-session/cleanup.js +191 -0
- package/dist/interactive-session/cleanup.js.map +1 -0
- package/dist/interactive-session/config.d.ts +118 -0
- package/dist/interactive-session/config.js +141 -0
- package/dist/interactive-session/config.js.map +1 -0
- package/dist/interactive-session/input-policy.d.ts +26 -0
- package/dist/interactive-session/input-policy.js +70 -0
- package/dist/interactive-session/input-policy.js.map +1 -0
- package/dist/interactive-session/manager.d.ts +118 -0
- package/dist/interactive-session/manager.js +1214 -0
- package/dist/interactive-session/manager.js.map +1 -0
- package/dist/interactive-session/output-store.d.ts +81 -0
- package/dist/interactive-session/output-store.js +239 -0
- package/dist/interactive-session/output-store.js.map +1 -0
- package/dist/interactive-session/output-view.d.ts +20 -0
- package/dist/interactive-session/output-view.js +142 -0
- package/dist/interactive-session/output-view.js.map +1 -0
- package/dist/interactive-session/recovery-journal.d.ts +53 -0
- package/dist/interactive-session/recovery-journal.js +167 -0
- package/dist/interactive-session/recovery-journal.js.map +1 -0
- package/dist/interactive-session/registry.d.ts +59 -0
- package/dist/interactive-session/registry.js +170 -0
- package/dist/interactive-session/registry.js.map +1 -0
- package/dist/interactive-session/runtime.d.ts +107 -0
- package/dist/interactive-session/runtime.js +191 -0
- package/dist/interactive-session/runtime.js.map +1 -0
- package/dist/interactive-session/session-runtime.d.ts +53 -0
- package/dist/interactive-session/session-runtime.js +65 -0
- package/dist/interactive-session/session-runtime.js.map +1 -0
- package/dist/interactive-session/streaming-redactor.d.ts +53 -0
- package/dist/interactive-session/streaming-redactor.js +271 -0
- package/dist/interactive-session/streaming-redactor.js.map +1 -0
- package/dist/interactive-session/telemetry.d.ts +30 -0
- package/dist/interactive-session/telemetry.js +47 -0
- package/dist/interactive-session/telemetry.js.map +1 -0
- package/dist/interactive-session/transport-factory.d.ts +8 -0
- package/dist/interactive-session/transport-factory.js +16 -0
- package/dist/interactive-session/transport-factory.js.map +1 -0
- package/dist/interactive-session/transport-node-pty.d.ts +38 -0
- package/dist/interactive-session/transport-node-pty.js +390 -0
- package/dist/interactive-session/transport-node-pty.js.map +1 -0
- package/dist/interactive-session/transport-pipe.d.ts +9 -0
- package/dist/interactive-session/transport-pipe.js +267 -0
- package/dist/interactive-session/transport-pipe.js.map +1 -0
- package/dist/interactive-session/transport.d.ts +110 -0
- package/dist/interactive-session/transport.js +89 -0
- package/dist/interactive-session/transport.js.map +1 -0
- package/dist/interactive-session/types.d.ts +258 -0
- package/dist/interactive-session/types.js +191 -0
- package/dist/interactive-session/types.js.map +1 -0
- package/dist/os/process-identity.d.ts +46 -0
- package/dist/os/process-identity.js +147 -0
- package/dist/os/process-identity.js.map +1 -0
- package/dist/os/process-tree.d.ts +1 -0
- package/dist/os/process-tree.js +11 -0
- package/dist/os/process-tree.js.map +1 -1
- package/dist/prompts/embedded.js +1 -1
- package/dist/prompts/embedded.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/prompts/system.agent.md +3 -0
- package/dist/repl.js +16 -1
- package/dist/repl.js.map +1 -1
- package/dist/safety/classifier.d.ts +9 -0
- package/dist/safety/classifier.js +119 -18
- package/dist/safety/classifier.js.map +1 -1
- package/dist/safety/engagement-policy.d.ts +13 -0
- package/dist/safety/engagement-policy.js +129 -34
- package/dist/safety/engagement-policy.js.map +1 -1
- package/dist/tools/definitions.js +94 -0
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/interactive-session-tools.d.ts +11 -0
- package/dist/tools/interactive-session-tools.js +276 -0
- package/dist/tools/interactive-session-tools.js.map +1 -0
- package/dist/tools/registry.js +3 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tui-v2/bootstrap/composition-root.d.ts +3 -0
- package/dist/tui-v2/bootstrap/composition-root.js +3 -0
- package/dist/tui-v2/bootstrap/composition-root.js.map +1 -1
- package/dist/tui-v2/bootstrap/start-tui-v2.js +8 -0
- package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Two things make it practical for everyday use:
|
|
|
19
19
|
- **Scope-based pentesting.** Opt-in engagement scope with authorized/excluded targets, allowed phases, rate and concurrency ceilings, redirect and DNS-rebinding escape detection, and out-of-scope flagging — designed for authorized pentests and bug-bounty programs.
|
|
20
20
|
- **Real building & debugging.** Scaffolds apps, edits code surgically, installs packages, runs builds/tests, starts dev servers as background jobs, and probes them before reporting success.
|
|
21
21
|
- **Durable plans.** `plan.create` / `task.update` drive a live checklist that survives context compaction and reloads with `/history` — the agent works task-by-task and won't fake completion.
|
|
22
|
+
- **Persistent interactive terminals.** Conversation-owned PTY or pipe sessions keep REPLs such as Python, Metasploit, Meterpreter, database consoles, and debuggers open across model turns. The agent can send follow-up input, read incremental output, resize, interrupt, and close them without losing state.
|
|
22
23
|
- **Native + text tool calling.** Uses provider-native function calling where available, with a text-fence fallback (`toolCalling: auto|native|text`).
|
|
23
24
|
- **Safety gate you control.** Every action is classified safe / confirm / block; deletes always confirm with a preview; destructive patterns are blocked.
|
|
24
25
|
|
|
@@ -416,6 +417,20 @@ Drop a `.clai/context.md` in a repo and its contents are injected every turn —
|
|
|
416
417
|
|
|
417
418
|
---
|
|
418
419
|
|
|
420
|
+
## Interactive REPLs and terminals
|
|
421
|
+
|
|
422
|
+
Ask clai to use an interactive terminal whenever a program needs more than one input step. It keeps the process attached to the current conversation instead of treating it like a one-line shell command:
|
|
423
|
+
|
|
424
|
+
```text
|
|
425
|
+
Start a Python REPL, inspect the failing parser, make a small change, and show me the result.
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
The agent uses `terminal.start`, `terminal.send`, `terminal.read`, `terminal.status`, `terminal.list`, `terminal.resize`, and `terminal.close` internally. Sessions are isolated per conversation, capture output as cursor-addressed pages, redact prompted secret input before it reaches transcripts or artifacts, and reliably terminate their process trees on close. In plan mode, starting or sending terminal input waits for plan approval; read, status, list, and close operations remain available.
|
|
429
|
+
|
|
430
|
+
For authorized security work, interactive effects are re-evaluated against the active engagement scope at the time they are delivered. That includes the target, port, phase, and expiry accumulated from prior REPL commands, so a split sequence cannot bypass scope controls.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
419
434
|
## Configuration & privacy
|
|
420
435
|
|
|
421
436
|
```sh
|
|
@@ -29,6 +29,10 @@ export interface ConfirmPort {
|
|
|
29
29
|
*/
|
|
30
30
|
export declare function restoreInteractiveStdin(): void;
|
|
31
31
|
export declare const inquirerConfirmPort: ConfirmPort;
|
|
32
|
+
export declare function inquirerSecretRequester(request: {
|
|
33
|
+
title: string;
|
|
34
|
+
prompt: string;
|
|
35
|
+
}): Promise<string | undefined>;
|
|
32
36
|
export declare function ensurePentestAuthorization(call: ToolCall, autoConfirm: boolean, session: SessionPolicy, confirmPort: ConfirmPort): Promise<boolean>;
|
|
33
37
|
export declare function confirmToolExecution(call: ToolCall, autoConfirm: boolean, session: SessionPolicy, confirmPort: ConfirmPort, options?: {
|
|
34
38
|
forceConfirm?: boolean | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { confirm } from "@inquirer/prompts";
|
|
1
|
+
import { confirm, password } from "@inquirer/prompts";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { getConfig } from "../store/config.js";
|
|
4
4
|
import { isPentestToolCall } from "../safety/classifier.js";
|
|
@@ -58,6 +58,14 @@ export const inquirerConfirmPort = {
|
|
|
58
58
|
});
|
|
59
59
|
},
|
|
60
60
|
};
|
|
61
|
+
export async function inquirerSecretRequester(request) {
|
|
62
|
+
try {
|
|
63
|
+
return await password({ message: request.prompt || request.title });
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
restoreInteractiveStdin();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
61
69
|
export async function ensurePentestAuthorization(call, autoConfirm, session, confirmPort) {
|
|
62
70
|
if (!isPentestToolCall(call))
|
|
63
71
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm-port.js","sourceRoot":"","sources":["../../src/agent/confirm-port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"confirm-port.js","sourceRoot":"","sources":["../../src/agent/confirm-port.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,13 +75,14 @@ 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";
|
|
82
83
|
import { extractProjectRootFromPlan, extractProjectRootFromScaffold, extractProjectRootFromText, getActiveProjectRoot, setActiveProjectRootIfValid, } from "./project-root.js";
|
|
83
84
|
import { buildWorkspaceOrientation, discoverImmediateProjectRoots, guessProjectFolderName, isBareParentDirectory, isScaffoldCancelledOutput, scaffoldLooksMaterialized, scaffoldTargetConflictMessage, resolveScaffoldTargetPath, } from "./workspace-orient.js";
|
|
84
|
-
import { inquirerConfirmPort, restoreInteractiveStdin, ensurePentestAuthorization, confirmToolExecution, } from "./confirm-port.js";
|
|
85
|
+
import { inquirerConfirmPort, inquirerSecretRequester, restoreInteractiveStdin, ensurePentestAuthorization, confirmToolExecution, } from "./confirm-port.js";
|
|
85
86
|
import { buildRichStopSummary } from "./stop-summary.js";
|
|
86
87
|
import { composeAgentSystemPrompt } from "./prompt-composer.js";
|
|
87
88
|
import { createGovernorState, governProgress, } from "./evidence-governor.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()];
|
|
@@ -1633,9 +1638,17 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
1633
1638
|
if (isPlanMode &&
|
|
1634
1639
|
!session.planApproved.value &&
|
|
1635
1640
|
!isScratchOnlyWrite(call, scratchDir)) {
|
|
1636
|
-
const cmd =
|
|
1637
|
-
|
|
1638
|
-
|
|
1641
|
+
const cmd = call.name === "terminal.send"
|
|
1642
|
+
? typeof call.args.text === "string"
|
|
1643
|
+
? call.args.text
|
|
1644
|
+
: ""
|
|
1645
|
+
: typeof call.args.command === "string"
|
|
1646
|
+
? call.args.command
|
|
1647
|
+
: "";
|
|
1648
|
+
const shellBlocked = call.name === "terminal.start" ||
|
|
1649
|
+
call.name === "terminal.send" ||
|
|
1650
|
+
((call.name === "shell.exec" || call.name === "shell.start") &&
|
|
1651
|
+
!isPlanModeAllowedShellCommand(cmd));
|
|
1639
1652
|
const allowed = isPlanModeAllowedTool(call.name) && !shellBlocked;
|
|
1640
1653
|
if (!allowed) {
|
|
1641
1654
|
const reason = `plan mode — ${call.name} is blocked (gather-only). ` +
|
|
@@ -2078,7 +2091,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2078
2091
|
const runToolWithForcedSettle = () => {
|
|
2079
2092
|
const work = runToolCall(call, {
|
|
2080
2093
|
signal: toolAc.signal,
|
|
2081
|
-
requestSecret: options.requestSecret,
|
|
2094
|
+
requestSecret: options.requestSecret ?? inquirerSecretRequester,
|
|
2082
2095
|
onOutput: (chunk) => {
|
|
2083
2096
|
if (toolAc.signal.aborted)
|
|
2084
2097
|
return;
|
|
@@ -4022,182 +4035,61 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4022
4035
|
"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
4036
|
continue;
|
|
4024
4037
|
}
|
|
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;
|
|
4038
|
+
const deferResponderReport = session.planApproved.value &&
|
|
4039
|
+
budgetRemaining(recovery, "prematureComplete")
|
|
4040
|
+
? shouldYieldForDeclaredResponderDependency(livePlanAtCompletion, jobManager.getRunningJobs(session.sessionId), jobManager.getPendingNotifications(session.sessionId), responderWakeNotificationId)
|
|
4041
|
+
: false;
|
|
4042
|
+
const finalizeRecovery = chooseFinalizeRecovery({
|
|
4043
|
+
cleaned,
|
|
4044
|
+
recovery,
|
|
4045
|
+
toolsAttached,
|
|
4046
|
+
productiveSteps,
|
|
4047
|
+
planApproved: session.planApproved.value,
|
|
4048
|
+
planHasOpenWork: planHasOpenWorkNow,
|
|
4049
|
+
activePlanExists: Boolean(activePlan),
|
|
4050
|
+
wantsAction,
|
|
4051
|
+
narratedAction,
|
|
4052
|
+
narratedWebAction,
|
|
4053
|
+
isPlanMode,
|
|
4054
|
+
buildLikeTurn,
|
|
4055
|
+
pentestLikeTurn,
|
|
4056
|
+
buildLike,
|
|
4057
|
+
pentestLike,
|
|
4058
|
+
pentestSession,
|
|
4059
|
+
informationalQuery,
|
|
4060
|
+
idleOrSocialPrompt,
|
|
4061
|
+
freshWebSearchRequired,
|
|
4062
|
+
freshnessGuardText: freshWebSearchRequired
|
|
4063
|
+
? freshnessGuardMessage()
|
|
4064
|
+
: "",
|
|
4065
|
+
sawFreshWebSearch,
|
|
4066
|
+
sawPlanCreateOk,
|
|
4067
|
+
sawFeatureImplWrite,
|
|
4068
|
+
sawScaffoldOk,
|
|
4069
|
+
sawLocalAppMaterialWork,
|
|
4070
|
+
sawServerStart,
|
|
4071
|
+
sawServerTail,
|
|
4072
|
+
sawLocalHttpProbe,
|
|
4073
|
+
sawFailedLocalHttpProbe,
|
|
4074
|
+
sawActivePentestTest,
|
|
4075
|
+
sawSuccessfulMutation,
|
|
4076
|
+
featureAppAsk,
|
|
4077
|
+
projectRoot: getActiveProjectRoot(),
|
|
4078
|
+
plan: livePlanAtCompletion
|
|
4079
|
+
? {
|
|
4080
|
+
kind: livePlanAtCompletion.kind,
|
|
4081
|
+
hasVerifiedRuntime: planHasVerifiedRuntime(livePlanAtCompletion),
|
|
4082
|
+
tasks: livePlanAtCompletion.tasks,
|
|
4150
4083
|
}
|
|
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);
|
|
4084
|
+
: undefined,
|
|
4085
|
+
deferResponderReport,
|
|
4086
|
+
});
|
|
4087
|
+
if (finalizeRecovery) {
|
|
4088
|
+
consumeBudget(recovery, finalizeRecovery.budgetKey);
|
|
4174
4089
|
commitAssistantRetry(assistantText.visible);
|
|
4175
|
-
messages.push(recoveryUserMessage(
|
|
4090
|
+
messages.push(recoveryUserMessage(finalizeRecovery.message));
|
|
4176
4091
|
continue;
|
|
4177
4092
|
}
|
|
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
4093
|
let outcomeStatus = "succeeded";
|
|
4202
4094
|
const remainingCriteria = [];
|
|
4203
4095
|
if (session.planApproved.value) {
|