@pentoshi/clai 2.0.36 → 2.0.38
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 +1 -0
- package/dist/agent/confirm-port.js +6 -1
- package/dist/agent/confirm-port.js.map +1 -1
- package/dist/agent/events.d.ts +5 -0
- package/dist/agent/loop-guard.js +8 -1
- package/dist/agent/loop-guard.js.map +1 -1
- package/dist/agent/progress.js +5 -1
- package/dist/agent/progress.js.map +1 -1
- package/dist/agent/runner.js +215 -31
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tool-call-parser.d.ts +11 -0
- package/dist/agent/tool-call-parser.js +151 -8
- package/dist/agent/tool-call-parser.js.map +1 -1
- package/dist/agent/tool-output-formatting.js +1 -0
- package/dist/agent/tool-output-formatting.js.map +1 -1
- package/dist/commands/update.js +1 -1
- package/dist/prompts/index.d.ts +8 -1
- package/dist/prompts/index.js +8 -4
- package/dist/prompts/index.js.map +1 -1
- package/dist/repl/slash-commands.js +5 -0
- package/dist/repl/slash-commands.js.map +1 -1
- package/dist/repl.js +43 -0
- package/dist/repl.js.map +1 -1
- package/dist/safety/classifier.js +3 -1
- package/dist/safety/classifier.js.map +1 -1
- package/dist/store/config.d.ts +2 -0
- package/dist/store/config.js +1 -0
- package/dist/store/config.js.map +1 -1
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +48 -0
- package/dist/tools/fs.js.map +1 -1
- package/dist/tools/registry.js +7 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tui/App.js +51 -4
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/ConfirmModal.d.ts +2 -1
- package/dist/tui/components/ConfirmModal.js +3 -1
- package/dist/tui/components/ConfirmModal.js.map +1 -1
- package/dist/tui/render-lines.d.ts +1 -0
- package/dist/tui/render-lines.js +4 -1
- package/dist/tui/render-lines.js.map +1 -1
- package/dist/tui/state.js +18 -1
- package/dist/tui/state.js.map +1 -1
- package/dist/ui/intro-card.d.ts +1 -0
- package/dist/ui/intro-card.js +8 -6
- package/dist/ui/intro-card.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -204,6 +204,7 @@ export OLLAMA_HOST=http://localhost:11434
|
|
|
204
204
|
| `/reset` | Clear all saved history |
|
|
205
205
|
| `/cwd <path>` | Change working directory |
|
|
206
206
|
| `/allow <tool>` | Whitelist a tool for the session |
|
|
207
|
+
| `/permissions [default\|allow-all]` | Toggle confirmation prompting (allow-all bypasses confirm gates except passwords) |
|
|
207
208
|
| `/plan` | View the current session plan (also `Ctrl+P`) |
|
|
208
209
|
| `/implement` | Approve the current plan and have clai execute it |
|
|
209
210
|
| `/discard` | Discard the current plan so later messages ignore it |
|
|
@@ -55,8 +55,11 @@ export const inquirerConfirmPort = {
|
|
|
55
55
|
export async function ensurePentestAuthorization(call, autoConfirm, session, confirmPort) {
|
|
56
56
|
if (!isPentestToolCall(call))
|
|
57
57
|
return true;
|
|
58
|
+
const config = getConfig();
|
|
59
|
+
if (config.permissions === "allow-all")
|
|
60
|
+
return true;
|
|
58
61
|
// Persistent auth (via `clai authorize-pentest AGREE`) wins.
|
|
59
|
-
if (
|
|
62
|
+
if (config.pentestAuthorized)
|
|
60
63
|
return true;
|
|
61
64
|
// Session auth flipped earlier in this session — no re-prompt.
|
|
62
65
|
if (session.pentestAuthorized.value)
|
|
@@ -75,6 +78,8 @@ export async function ensurePentestAuthorization(call, autoConfirm, session, con
|
|
|
75
78
|
}
|
|
76
79
|
export async function confirmToolExecution(call, autoConfirm, session, confirmPort) {
|
|
77
80
|
const config = getConfig();
|
|
81
|
+
if (config.permissions === "allow-all")
|
|
82
|
+
return true;
|
|
78
83
|
if (autoConfirm)
|
|
79
84
|
return true;
|
|
80
85
|
if (session.allow.has(call.name))
|
|
@@ -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;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAkBvD;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB;IACrC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO;IACjC,IAAI,CAAC;QACH,IAAI,CAAE,OAAO,CAAC,KAAiD,CAAC,KAAK,EAAE,CAAC;YACtE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAgB;IAC9C,KAAK,CAAC,WAAW,CAAC,IAAc;QAC9B,OAAO,OAAO,CAAC;YACb,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;YACrE,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,cAAc;QAClB,OAAO,OAAO,CAAC;YACb,OAAO,EAAE,KAAK,CAAC,GAAG,CAChB,0HAA0H,CAC3H;YACD,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,eAAe,CAAC,KAAa;QACjC,OAAO,OAAO,CAAC;YACb,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,KAAK,4BAA4B,CAAC;YAC7D,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,kBAAkB,CAAC,IAGxB;QACC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,OAAO,OAAO,CAAC;YACb,OAAO,EAAE,KAAK,CAAC,MAAM,CACnB,0BAA0B,KAAK,uBAAuB,CACvD;YACD,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,IAAc,EACd,WAAoB,EACpB,OAAsB,EACtB,WAAwB;IAExB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,6DAA6D;IAC7D,IAAI,
|
|
1
|
+
{"version":3,"file":"confirm-port.js","sourceRoot":"","sources":["../../src/agent/confirm-port.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/events.d.ts
CHANGED
package/dist/agent/loop-guard.js
CHANGED
|
@@ -75,6 +75,9 @@ export class LoopGuard {
|
|
|
75
75
|
* removes their results.
|
|
76
76
|
*/
|
|
77
77
|
shouldBlock(name, args) {
|
|
78
|
+
if (name === "task.update" || name === "plan.create") {
|
|
79
|
+
return { block: false };
|
|
80
|
+
}
|
|
78
81
|
const sig = this.canonicalize(name, args);
|
|
79
82
|
const count = this.signatureCount.get(sig) ?? 0;
|
|
80
83
|
if (count === 0)
|
|
@@ -85,7 +88,11 @@ export class LoopGuard {
|
|
|
85
88
|
// Mutating file tools deserve tool-appropriate wording. Telling a model
|
|
86
89
|
// that just wrote a file to "use the results you already have" is
|
|
87
90
|
// nonsensical and has caused models to assume the whole task is done.
|
|
88
|
-
const isWrite = name === "fs.write" ||
|
|
91
|
+
const isWrite = name === "fs.write" ||
|
|
92
|
+
name === "fs.writeMany" ||
|
|
93
|
+
name === "fs.edit" ||
|
|
94
|
+
name === "fs.replaceLines" ||
|
|
95
|
+
name === "fs.append";
|
|
89
96
|
// Read-only tools get a higher threshold — they may need re-calling
|
|
90
97
|
// after context compaction removes their earlier results.
|
|
91
98
|
const threshold = READ_ONLY_TOOLS.has(name) ? 3 : 2;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-guard.js","sourceRoot":"","sources":["../../src/agent/loop-guard.ts"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,SAAS;IACT,SAAS;IACT,WAAW;IACX,SAAS;IACT,aAAa;IACb,YAAY;IACZ,eAAe;IACf,WAAW;IACX,UAAU;CACX,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,OAAO,SAAS;IACZ,QAAQ,GAAkB,EAAE,CAAC;IAC7B,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,gBAAgB,GAAG,IAAI,GAAG,EAAmB,CAAC;IAEtD;;;;OAIG;IACH,YAAY,CAAC,IAAY,EAAE,IAA6B;QACtD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,8CAA8C;YAC9C,IAAI,IAAI,KAAK,YAAY,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC5E,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAC5C,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;QACD,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,aAAa,CACX,IAAY,EACZ,IAAY,EACZ,IAA6B,EAC7B,EAAW,EACX,QAA6B;QAE7B,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtE,wEAAwE;QACxE,yEAAyE;QACzE,qEAAqE;QACrE,kBAAkB;QAClB,IAAI,EAAE;YAAE,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;aACxC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClF,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CACT,IAAY,EACZ,IAA6B;QAE7B,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEhD,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAEzC,2DAA2D;QAC3D,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAEtE,wEAAwE;QACxE,kEAAkE;QAClE,sEAAsE;QACtE,MAAM,OAAO,GACX,IAAI,KAAK,UAAU,IAAI,
|
|
1
|
+
{"version":3,"file":"loop-guard.js","sourceRoot":"","sources":["../../src/agent/loop-guard.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/progress.js
CHANGED
|
@@ -67,7 +67,11 @@ export function evaluateProgress(plan, call, result) {
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
// File edit/write goals
|
|
70
|
-
if ((call.name === "fs.edit" ||
|
|
70
|
+
if ((call.name === "fs.edit" ||
|
|
71
|
+
call.name === "fs.replaceLines" ||
|
|
72
|
+
call.name === "fs.write" ||
|
|
73
|
+
call.name === "fs.append") &&
|
|
74
|
+
ok) {
|
|
71
75
|
return {
|
|
72
76
|
madeProgress: true,
|
|
73
77
|
goalSatisfied: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/agent/progress.ts"],"names":[],"mappings":"AAUA;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAA0B,EAC1B,IAAc,EACd,MAAkB;IAElB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IAErB,mDAAmD;IACnD,IACE,CAAC,EAAE;QACH,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YACnC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC,EAC/C,CAAC;QACD,OAAO;YACL,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;YACpB,cAAc,EAAE,IAAI;YACpB,MAAM,EAAE,8FAA8F;SACvG,CAAC;IACJ,CAAC;IAED,oBAAoB;IACpB,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAChD,OAAO;YACL,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;YACpB,cAAc,EAAE,IAAI;YACpB,MAAM,EAAE,yEAAyE;SAClF,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,IAAI,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,EAAE,CAAC;QAC5D,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YACpE,OAAO;gBACL,YAAY,EAAE,IAAI;gBAClB,aAAa,EAAE,IAAI;gBACnB,cAAc,EAAE,KAAK;gBACrB,MAAM,EAAE,6CAA6C;aACtD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,YAAY;IACZ,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAC/B,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC7B,OAAO;gBACL,YAAY,EAAE,IAAI;gBAClB,aAAa,EAAE,IAAI;gBACnB,cAAc,EAAE,KAAK;gBACrB,MAAM,EAAE,wBAAwB;aACjC,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAChC,OAAO;gBACL,YAAY,EAAE,IAAI;gBAClB,aAAa,EAAE,IAAI;gBACnB,cAAc,EAAE,KAAK;gBACrB,MAAM,EAAE,mCAAmC;aAC5C,CAAC;QACJ,CAAC;IACH,CAAC;IAED,cAAc;IACd,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC7D,OAAO;YACL,YAAY,EAAE,IAAI;YAClB,aAAa,EAAE,IAAI;YACnB,cAAc,EAAE,KAAK;YACrB,MAAM,EAAE,uBAAuB;SAChC,CAAC;IACJ,CAAC;IAED,wBAAwB;IACxB,
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/agent/progress.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/runner.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
3
|
+
import { join, relative, resolve } from "node:path";
|
|
4
4
|
import { streamWithProvider, completeWithProvider } from "../llm/router.js";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { jobManager } from "../tools/jobs.js";
|
|
7
|
-
import { renderAgentSystemPrompt, } from "../prompts/index.js";
|
|
7
|
+
import { renderAgentSystemPrompt, scratchDirFor, } from "../prompts/index.js";
|
|
8
8
|
import { getConfig } from "../store/config.js";
|
|
9
9
|
import { classifyToolCall, isPentestToolCall, scopeHint, scopeTargetForToolCall, } from "../safety/classifier.js";
|
|
10
10
|
import { availableToolNames, normalizeToolCall, runToolCall, BATCH_SAFE_TOOLS, } from "../tools/registry.js";
|
|
11
11
|
import { looksInteractiveStdin } from "../tools/shell.js";
|
|
12
12
|
import { formatViewportHint, registerViewport } from "../ui/output-pane.js";
|
|
13
|
-
import { compactMessagesWithSummary, estimateMessagesTokens, } from "./context-manager.js";
|
|
13
|
+
import { compactMessagesWithSummary, estimateMessagesTokens, COMPACTION_MEMORY_PREFIX, } from "./context-manager.js";
|
|
14
14
|
import { auditLog } from "../store/logs.js";
|
|
15
15
|
import { loadProjectContext } from "../store/project.js";
|
|
16
16
|
import { loadScope, isScopeActive, targetInScope } from "../store/scope.js";
|
|
@@ -23,7 +23,7 @@ import { analyzeTask } from "./task-analyzer.js";
|
|
|
23
23
|
import { LoopGuard } from "./loop-guard.js";
|
|
24
24
|
import { loadPlan } from "../store/plan.js";
|
|
25
25
|
import { pathInsideSandbox } from "../tools/fs.js";
|
|
26
|
-
import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeActionNarration, looksLikePlanNarration, requiresFreshWebSearch, freshnessGuardMessage, buildWorkflowDirective, shouldDimToolChatter, looksLikePromptLeak, } from "./tool-call-parser.js";
|
|
26
|
+
import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTruncatedToolCall, countToolFences, parseAllToolCalls, groupToolCallsForExecution, buildTurnHistory, collapseRepeatedText, textBeforeToolCall, formatToolArgs, looksLikePentestTask, looksLikeBuildTask, looksLikeInformationalQuery, looksLikeActionNarration, looksLikePlanNarration, requiresFreshWebSearch, freshnessGuardMessage, buildWorkflowDirective, pentestWorkflowDirective, shouldDimToolChatter, looksLikePromptLeak, } from "./tool-call-parser.js";
|
|
27
27
|
import { createSessionPolicy, isPreApprovalAllowedTool, isPlanApprovedByStatus, planHasOpenWork, isAbortError, shouldEnableImageOcr, } from "./session-policy.js";
|
|
28
28
|
import { saveToolOutput, summarizeOutput, formatToolContext, } from "./tool-output-formatting.js";
|
|
29
29
|
import { planContextMessage, handlePlanTool, } from "./plan-tool.js";
|
|
@@ -38,6 +38,78 @@ export { createSessionPolicy, isPreApprovalAllowedTool, isPlanApprovedByStatus,
|
|
|
38
38
|
export function styleToolChatter(call, text) {
|
|
39
39
|
return shouldDimToolChatter(call) ? chalk.dim(text) : text;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Tool names that may write into the project tree. Writes restricted to the
|
|
43
|
+
* per-project scratch directory (under tmpdir()/clai/<name>) are exempted
|
|
44
|
+
* from the active-plan and plan-approved gates so the model can use scratch
|
|
45
|
+
* space to build / inspect / stage work without first creating a plan.
|
|
46
|
+
*/
|
|
47
|
+
const SCRATCH_WRITABLE_TOOLS = new Set([
|
|
48
|
+
"fs.write",
|
|
49
|
+
"fs.writeMany",
|
|
50
|
+
"fs.edit",
|
|
51
|
+
"fs.replaceLines",
|
|
52
|
+
"fs.append",
|
|
53
|
+
"fs.delete",
|
|
54
|
+
]);
|
|
55
|
+
/**
|
|
56
|
+
* Expand `~` the same way `src/tools/fs.ts` does so callers can compare an
|
|
57
|
+
* already-expanded scratch path against paths supplied by the model.
|
|
58
|
+
*/
|
|
59
|
+
function expandHomeLocal(path) {
|
|
60
|
+
if (path === "~")
|
|
61
|
+
return homedir();
|
|
62
|
+
if (path.startsWith("~/") || path.startsWith("~\\")) {
|
|
63
|
+
return resolve(homedir(), path.slice(2));
|
|
64
|
+
}
|
|
65
|
+
return path;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Extract the target path(s) a write-shaped tool call would touch. Returns
|
|
69
|
+
* an empty array when the call has no resolvable path (so the caller can
|
|
70
|
+
* treat it as NOT scratch-only and fall through to the normal gates).
|
|
71
|
+
*/
|
|
72
|
+
function scratchWriteTargetPaths(call) {
|
|
73
|
+
if (call.name === "fs.writeMany") {
|
|
74
|
+
const files = call.args.files;
|
|
75
|
+
if (!Array.isArray(files))
|
|
76
|
+
return [];
|
|
77
|
+
const paths = [];
|
|
78
|
+
for (const entry of files) {
|
|
79
|
+
if (entry && typeof entry === "object" && "path" in entry) {
|
|
80
|
+
const p = entry.path;
|
|
81
|
+
if (typeof p === "string" && p.length > 0)
|
|
82
|
+
paths.push(p);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return paths;
|
|
86
|
+
}
|
|
87
|
+
const pathArg = call.args.path;
|
|
88
|
+
if (typeof pathArg !== "string" || pathArg.length === 0)
|
|
89
|
+
return [];
|
|
90
|
+
return [pathArg];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* True iff every target path this call would write is inside the resolved
|
|
94
|
+
* scratch directory. A path is considered inside when its `path.relative`
|
|
95
|
+
* against the scratch root is empty (the scratch root itself) or does not
|
|
96
|
+
* start with `..` (no parent traversal). Calls without a recognizable
|
|
97
|
+
* target path return false so they fall through to the normal gates.
|
|
98
|
+
*/
|
|
99
|
+
function isScratchOnlyWrite(call, scratchDir) {
|
|
100
|
+
if (!SCRATCH_WRITABLE_TOOLS.has(call.name))
|
|
101
|
+
return false;
|
|
102
|
+
const paths = scratchWriteTargetPaths(call);
|
|
103
|
+
if (paths.length === 0)
|
|
104
|
+
return false;
|
|
105
|
+
const resolvedScratch = resolve(scratchDir);
|
|
106
|
+
return paths.every((raw) => {
|
|
107
|
+
const expanded = expandHomeLocal(raw);
|
|
108
|
+
const resolved = resolve(expanded);
|
|
109
|
+
const rel = relative(resolvedScratch, resolved);
|
|
110
|
+
return rel === "" || (!rel.startsWith("..") && rel !== "..");
|
|
111
|
+
});
|
|
112
|
+
}
|
|
41
113
|
export async function runAgentLoop(prompt, options = {}) {
|
|
42
114
|
const writesDirectly = !options.onEvent;
|
|
43
115
|
const emit = (event) => options.onEvent?.(event);
|
|
@@ -114,6 +186,22 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
114
186
|
}
|
|
115
187
|
emit(event);
|
|
116
188
|
};
|
|
189
|
+
/** Strip a known prefix from a string, returning the remainder unchanged. */
|
|
190
|
+
const insertedText = (value, prefix) => value.startsWith(prefix) ? value.slice(prefix.length) : value;
|
|
191
|
+
/**
|
|
192
|
+
* Surface the compacted-context summary to both the TUI (via an event)
|
|
193
|
+
* and, when running with a direct stdout writer, as a rendered box.
|
|
194
|
+
* Token-count stats are always emitted for logs.
|
|
195
|
+
*/
|
|
196
|
+
const writeCompacted = (summary, beforeTokens, afterTokens) => {
|
|
197
|
+
emit({ type: "compacted", summary, beforeTokens, afterTokens });
|
|
198
|
+
if (writesDirectly) {
|
|
199
|
+
const header = chalk.dim(" \u2726 Compacted Context");
|
|
200
|
+
const footer = chalk.dim(` ~${beforeTokens.toLocaleString()} \u2192 ~${afterTokens.toLocaleString()} tokens`);
|
|
201
|
+
const body = summary ? renderMarkdown(summary) : "(empty summary)";
|
|
202
|
+
process.stdout.write(`${header}\n\n${body}\n${footer}\n`);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
117
205
|
// Points at the live message array so finishTurn can hand the full
|
|
118
206
|
// conversation back to the caller. Assigned once `messages` is built below;
|
|
119
207
|
// all later mutations are in-place so this reference stays current.
|
|
@@ -199,6 +287,15 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
199
287
|
if (buildLikeTurn && !activePlan && !informationalQuery) {
|
|
200
288
|
systemSections.push(buildWorkflowDirective());
|
|
201
289
|
}
|
|
290
|
+
// Pentest / security engagements need a different shape than a coding
|
|
291
|
+
// build: recon first, then a plan built from real findings, then
|
|
292
|
+
// incremental task additions as new attack surface appears. The
|
|
293
|
+
// directive is only injected before a plan exists; once a plan is in
|
|
294
|
+
// place (or being refined), the ACTIVE PLAN block already carries the
|
|
295
|
+
// current task state and recon-vs-active-tool guidance.
|
|
296
|
+
if (pentestLikeTurn && !activePlan && !informationalQuery) {
|
|
297
|
+
systemSections.push(pentestWorkflowDirective());
|
|
298
|
+
}
|
|
202
299
|
const fullSystemPrompt = systemSections.join("\n\n");
|
|
203
300
|
const userMessage = { role: "user", content: prompt };
|
|
204
301
|
if (options.images && options.images.length > 0) {
|
|
@@ -306,6 +403,7 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
306
403
|
let productiveSteps = 0;
|
|
307
404
|
let step = -1;
|
|
308
405
|
let nextToolEventId = 0;
|
|
406
|
+
const alreadyPrintedIds = new Set();
|
|
309
407
|
const promptMutex = {
|
|
310
408
|
promise: Promise.resolve(),
|
|
311
409
|
async acquire() {
|
|
@@ -320,6 +418,9 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
320
418
|
},
|
|
321
419
|
};
|
|
322
420
|
async function executeSingleTool(rawCall, toolEventId, parentSignal) {
|
|
421
|
+
// Resolved once per call so the scratch-only exemption can compare the
|
|
422
|
+
// model-supplied paths against the canonical per-project scratch root.
|
|
423
|
+
const scratchDir = scratchDirFor(safeCwd());
|
|
323
424
|
let call = normalizeToolCall(rawCall);
|
|
324
425
|
if (call.name === "image.ocr" && !imageOcrEnabled) {
|
|
325
426
|
writeNotice("info", "skipped OCR because the original image is attached to the vision model", chalk.dim(" ℹ skipped OCR — inspecting the attached image directly\n"));
|
|
@@ -333,7 +434,8 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
333
434
|
const isWrite = call.name === "fs.write" ||
|
|
334
435
|
call.name === "fs.writeMany" ||
|
|
335
436
|
call.name === "fs.edit" ||
|
|
336
|
-
call.name === "fs.replaceLines"
|
|
437
|
+
call.name === "fs.replaceLines" ||
|
|
438
|
+
call.name === "fs.append";
|
|
337
439
|
const reason = `${call.name} was already called with the same arguments — ${isWrite ? "moving on" : "forcing summary"}`;
|
|
338
440
|
writeNotice("warn", reason, chalk.yellow(` ⚠ ${reason}\n`));
|
|
339
441
|
const result = { ok: false, output: reason, exitCode: 1 };
|
|
@@ -355,10 +457,18 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
355
457
|
});
|
|
356
458
|
if (planResult.handled) {
|
|
357
459
|
loopGuard.recordAttempt(step, call.name, call.args, planResult.ok, 0);
|
|
460
|
+
if (!alreadyPrintedIds.has(toolEventId)) {
|
|
461
|
+
const toolCallLine = chalk.cyan(` ▶ ${call.name}`) + chalk.gray(` ${formatToolArgs(call)}`);
|
|
462
|
+
writeToolCall(toolEventId, call, styleToolChatter(call, toolCallLine) + "\n");
|
|
463
|
+
alreadyPrintedIds.add(toolEventId);
|
|
464
|
+
}
|
|
358
465
|
if (planResult.plan) {
|
|
359
466
|
writePlanUpdate(planResult.plan, planResult.display);
|
|
360
467
|
}
|
|
361
468
|
const result = { ok: planResult.ok, output: planResult.modelNote };
|
|
469
|
+
emitToolResult(toolEventId, result, planResult.modelNote);
|
|
470
|
+
const statusIcon = result.ok ? chalk.green(" ✓") : chalk.red(" ✗");
|
|
471
|
+
writeToolOutput(toolEventId, result.ok ? "ok\n" : "failed\n", statusIcon + "\n");
|
|
362
472
|
return {
|
|
363
473
|
ok: planResult.ok,
|
|
364
474
|
call,
|
|
@@ -374,19 +484,34 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
374
484
|
decision,
|
|
375
485
|
scope: isScopeActive(scope) ? (scope.name ?? "(unnamed)") : "(none)",
|
|
376
486
|
});
|
|
377
|
-
|
|
378
|
-
!
|
|
379
|
-
!
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
ok: false,
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
487
|
+
const isMutatingAction = (decision.level === "confirm" || decision.level === "block") &&
|
|
488
|
+
!isPreApprovalAllowedTool(call.name) &&
|
|
489
|
+
!isScratchOnlyWrite(call, scratchDir);
|
|
490
|
+
if (isMutatingAction) {
|
|
491
|
+
if (!activePlan) {
|
|
492
|
+
const reason = `No active plan — ${call.name} is blocked. You must first create a plan using plan.create before executing mutating actions.`;
|
|
493
|
+
writeNotice("warn", reason, chalk.yellow(` ⚠ ${reason}\n`));
|
|
494
|
+
const result = { ok: false, output: reason, exitCode: 1 };
|
|
495
|
+
return {
|
|
496
|
+
ok: false,
|
|
497
|
+
call,
|
|
498
|
+
result,
|
|
499
|
+
contextOutput: reason,
|
|
500
|
+
blockOrCancel: true,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
if (!session.planApproved.value) {
|
|
504
|
+
const reason = `plan awaiting approval — ${call.name} is blocked until you /implement (or /discard)`;
|
|
505
|
+
writeNotice("warn", reason, chalk.yellow(` ⚠ ${reason}\n`));
|
|
506
|
+
const result = { ok: false, output: reason, exitCode: 1 };
|
|
507
|
+
return {
|
|
508
|
+
ok: false,
|
|
509
|
+
call,
|
|
510
|
+
result,
|
|
511
|
+
contextOutput: reason,
|
|
512
|
+
blockOrCancel: true,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
390
515
|
}
|
|
391
516
|
// Task-scoped execution gate
|
|
392
517
|
// Once a plan is approved, every non-plan tool call must run while
|
|
@@ -423,8 +548,11 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
423
548
|
if (call.name === "web.search") {
|
|
424
549
|
sawFreshWebSearch = true;
|
|
425
550
|
}
|
|
426
|
-
|
|
427
|
-
|
|
551
|
+
if (!alreadyPrintedIds.has(toolEventId)) {
|
|
552
|
+
const toolCallLine = chalk.cyan(` ▶ ${call.name}`) + chalk.gray(` ${formatToolArgs(call)}`);
|
|
553
|
+
writeToolCall(toolEventId, call, styleToolChatter(call, toolCallLine) + "\n");
|
|
554
|
+
alreadyPrintedIds.add(toolEventId);
|
|
555
|
+
}
|
|
428
556
|
const scopeTarget = scopeTargetForToolCall(call);
|
|
429
557
|
if (scopeTarget &&
|
|
430
558
|
(!isScopeActive(scope) || !targetInScope(scopeTarget, scope))) {
|
|
@@ -499,7 +627,7 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
499
627
|
restoreInteractiveStdin();
|
|
500
628
|
if (!ok) {
|
|
501
629
|
const lastAnswer = "Cancelled.";
|
|
502
|
-
|
|
630
|
+
writeToolBlocked(toolEventId, call.name, lastAnswer, chalk.red(` ✗ cancelled`) + "\n");
|
|
503
631
|
const result = { ok: false, output: lastAnswer, exitCode: 1 };
|
|
504
632
|
return {
|
|
505
633
|
ok: false,
|
|
@@ -746,6 +874,16 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
746
874
|
estimatedTokens: afterTokens,
|
|
747
875
|
reason,
|
|
748
876
|
});
|
|
877
|
+
// Extract the inserted compaction memory so we can surface the
|
|
878
|
+
// summary itself (not just token-count stats). The summary lives in
|
|
879
|
+
// the first system message whose content begins with
|
|
880
|
+
// COMPACTION_MEMORY_PREFIX.
|
|
881
|
+
const insertedSummary = messages.find((m) => m.role === "system" &&
|
|
882
|
+
m.content.startsWith(COMPACTION_MEMORY_PREFIX))?.content ?? "";
|
|
883
|
+
const summaryText = insertedSummary.startsWith(`${COMPACTION_MEMORY_PREFIX}\n\n`)
|
|
884
|
+
? insertedText(insertedSummary, `${COMPACTION_MEMORY_PREFIX}\n\n`)
|
|
885
|
+
: insertedText(insertedSummary, COMPACTION_MEMORY_PREFIX);
|
|
886
|
+
writeCompacted(summaryText, beforeTokens, afterTokens);
|
|
749
887
|
writeNotice("info", `context auto-compacted to fit the window (~${beforeTokens.toLocaleString()} → ~${afterTokens.toLocaleString()} tokens)`, chalk.dim(` ℹ context auto-compacted (~${beforeTokens.toLocaleString()} → ~${afterTokens.toLocaleString()} tokens)\n`));
|
|
750
888
|
}
|
|
751
889
|
catch (error) {
|
|
@@ -836,7 +974,7 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
836
974
|
// A spinner gives the user feedback during long thinking phases on
|
|
837
975
|
// models like glm-5.1 / deepseek-v4-flash that stream reasoning first.
|
|
838
976
|
const streamLabel = step === 0 ? "waiting for model" : `step ${step + 1}`;
|
|
839
|
-
|
|
977
|
+
let spinner = writesDirectly
|
|
840
978
|
? startThinkingSpinner(streamLabel, options.signal)
|
|
841
979
|
: noopSpinner;
|
|
842
980
|
if (!writesDirectly) {
|
|
@@ -845,6 +983,10 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
845
983
|
let sawReasoning = false;
|
|
846
984
|
let inThinking = false;
|
|
847
985
|
let emittedThinkingStatus = false;
|
|
986
|
+
let generatedTokens = 0;
|
|
987
|
+
let accumulatedText = "";
|
|
988
|
+
const callIds = [];
|
|
989
|
+
let streamedCallsCount = 0;
|
|
848
990
|
const deltaParser = writesDirectly
|
|
849
991
|
? undefined
|
|
850
992
|
: createThinkingStreamParser((text) => emit({ type: "assistant-delta", text }), (text) => {
|
|
@@ -877,6 +1019,26 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
877
1019
|
thinking: config.thinking,
|
|
878
1020
|
}, (token) => {
|
|
879
1021
|
deltaParser?.push(token);
|
|
1022
|
+
generatedTokens += 1;
|
|
1023
|
+
accumulatedText += token;
|
|
1024
|
+
const parsedCalls = parseAllToolCalls(accumulatedText);
|
|
1025
|
+
if (parsedCalls.length > streamedCallsCount) {
|
|
1026
|
+
if (writesDirectly) {
|
|
1027
|
+
spinner.stop();
|
|
1028
|
+
}
|
|
1029
|
+
while (streamedCallsCount < parsedCalls.length) {
|
|
1030
|
+
const call = parsedCalls[streamedCallsCount];
|
|
1031
|
+
const eventId = `tool-${++nextToolEventId}`;
|
|
1032
|
+
callIds.push(eventId);
|
|
1033
|
+
alreadyPrintedIds.add(eventId);
|
|
1034
|
+
const toolCallLine = chalk.cyan(` ▶ ${call.name}`) + chalk.gray(` ${formatToolArgs(call)}`);
|
|
1035
|
+
writeToolCall(eventId, call, styleToolChatter(call, toolCallLine) + "\n");
|
|
1036
|
+
streamedCallsCount += 1;
|
|
1037
|
+
}
|
|
1038
|
+
if (writesDirectly) {
|
|
1039
|
+
spinner = startThinkingSpinner(`generating response (${generatedTokens} tokens)`, options.signal);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
880
1042
|
// Heuristic: <think>… markers and reasoning_content tokens flow
|
|
881
1043
|
// through onToken. Surface activity in the spinner so the screen
|
|
882
1044
|
// is never empty for minutes.
|
|
@@ -889,6 +1051,8 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
889
1051
|
}
|
|
890
1052
|
if (/<\/think>/i.test(token)) {
|
|
891
1053
|
inThinking = false;
|
|
1054
|
+
spinner.setLabel("generating response (0 tokens)");
|
|
1055
|
+
generatedTokens = 0;
|
|
892
1056
|
}
|
|
893
1057
|
// Only push reasoning tokens to the spinner preview. Visible
|
|
894
1058
|
// answer / tool-call tokens should NOT go through the dim
|
|
@@ -904,6 +1068,11 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
904
1068
|
spinner.bumpReasoning(approx);
|
|
905
1069
|
}
|
|
906
1070
|
}
|
|
1071
|
+
else {
|
|
1072
|
+
if (generatedTokens % 10 === 0) {
|
|
1073
|
+
spinner.setLabel(`generating response (${generatedTokens} tokens)`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
907
1076
|
}, (status) => {
|
|
908
1077
|
spinner.stop();
|
|
909
1078
|
writeStatus(status, chalk.dim(status));
|
|
@@ -1235,9 +1404,7 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
1235
1404
|
].filter(Boolean);
|
|
1236
1405
|
messages.push({
|
|
1237
1406
|
role: "user",
|
|
1238
|
-
content: "
|
|
1239
|
-
`Missing evidence: ${missing.join(", ")}. ` +
|
|
1240
|
-
"Run the missing checks now. Stop the job afterward unless the user explicitly asked to keep it running, and report whether it remains running truthfully.",
|
|
1407
|
+
content: "Run the missing checks now. Keep the dev server/job running in the background so that the user can interact with the live application, and print the localhost link. Report whether it remains running truthfully.",
|
|
1241
1408
|
});
|
|
1242
1409
|
continue;
|
|
1243
1410
|
}
|
|
@@ -1259,15 +1426,21 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
1259
1426
|
role: "assistant",
|
|
1260
1427
|
content: assistantText.visible,
|
|
1261
1428
|
});
|
|
1429
|
+
let instruction = `Resume now with the NEXT task ${next.id} ("${next.title}"): `;
|
|
1430
|
+
if (next.state === "pending") {
|
|
1431
|
+
instruction += `call task.update {taskId:"${next.id}", state:"in_progress"}, then do the real work with a tool call (fs.writeMany / shell.exec / shell.start), VERIFY it, and mark it done. `;
|
|
1432
|
+
}
|
|
1433
|
+
else {
|
|
1434
|
+
instruction += `do the real work with a tool call (fs.writeMany / shell.exec / shell.start) to complete it, VERIFY it, and mark it done (call task.update {taskId:"${next.id}", state:"done"}). `;
|
|
1435
|
+
}
|
|
1436
|
+
instruction += `Continue task by task until EVERY task is actually finished.`;
|
|
1262
1437
|
messages.push({
|
|
1263
1438
|
role: "user",
|
|
1264
1439
|
content: `You have NOT finished the approved plan: ${unfinished.length} task(s) remain ` +
|
|
1265
1440
|
`(${unfinished.map((t) => `[${t.id}] ${t.title}`).join("; ")}). ` +
|
|
1266
1441
|
`Do NOT claim the work is complete, that files were created, or that a server is running ` +
|
|
1267
1442
|
`unless a tool call actually succeeded and you saw the output. ` +
|
|
1268
|
-
|
|
1269
|
-
`then do the real work with a tool call (fs.writeMany / shell.exec / shell.start), VERIFY it, and mark it done. ` +
|
|
1270
|
-
`Continue task by task until EVERY task is actually finished.`,
|
|
1443
|
+
instruction,
|
|
1271
1444
|
});
|
|
1272
1445
|
continue;
|
|
1273
1446
|
}
|
|
@@ -1394,14 +1567,25 @@ export async function runAgentLoop(prompt, options = {}) {
|
|
|
1394
1567
|
if (aborted || blocked || failed || awaitingPlanApproval)
|
|
1395
1568
|
break;
|
|
1396
1569
|
if (group.length === 1) {
|
|
1397
|
-
const
|
|
1398
|
-
const
|
|
1570
|
+
const call = group[0];
|
|
1571
|
+
const idx = allCalls.indexOf(call);
|
|
1572
|
+
if (idx >= 0 && !callIds[idx]) {
|
|
1573
|
+
callIds[idx] = `tool-${++nextToolEventId}`;
|
|
1574
|
+
}
|
|
1575
|
+
const id = (idx >= 0 ? callIds[idx] : undefined) ?? `tool-${++nextToolEventId}`;
|
|
1576
|
+
const res = await executeSingleTool(call, id, options.signal || new AbortController().signal);
|
|
1399
1577
|
recordResult(res);
|
|
1400
1578
|
}
|
|
1401
1579
|
else {
|
|
1402
1580
|
// Concurrent group — assign ids in document order, then push their
|
|
1403
1581
|
// results in document order for a stable transcript.
|
|
1404
|
-
const ids = group.map(() =>
|
|
1582
|
+
const ids = group.map((c) => {
|
|
1583
|
+
const idx = allCalls.indexOf(c);
|
|
1584
|
+
if (idx >= 0 && !callIds[idx]) {
|
|
1585
|
+
callIds[idx] = `tool-${++nextToolEventId}`;
|
|
1586
|
+
}
|
|
1587
|
+
return (idx >= 0 ? callIds[idx] : undefined) ?? `tool-${++nextToolEventId}`;
|
|
1588
|
+
});
|
|
1405
1589
|
const results = await Promise.all(group.map((c, k) => executeSingleTool(c, ids[k], options.signal || new AbortController().signal)));
|
|
1406
1590
|
for (const res of results)
|
|
1407
1591
|
recordResult(res);
|