@pentoshi/clai 3.7.4 → 3.7.6
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 +54 -19
- package/dist/agent/confirm-port.js +3 -2
- package/dist/agent/confirm-port.js.map +1 -1
- package/dist/agent/runner.js +22 -44
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tool-call-parser.js +7 -0
- package/dist/agent/tool-call-parser.js.map +1 -1
- package/dist/commands/update.js +1 -1
- package/dist/prompts/embedded.js +2 -2
- package/dist/prompts/embedded.js.map +1 -1
- package/dist/prompts/index.js +2 -2
- package/dist/prompts/system.agent.md +2 -2
- package/dist/prompts/system.ask.md +1 -1
- package/dist/safety/classifier.js +2 -138
- package/dist/safety/classifier.js.map +1 -1
- package/dist/safety/patterns.d.ts +7 -7
- package/dist/safety/patterns.js +10 -11
- package/dist/safety/patterns.js.map +1 -1
- package/dist/tools/batch-fail-policy.d.ts +55 -0
- package/dist/tools/batch-fail-policy.js +261 -0
- package/dist/tools/batch-fail-policy.js.map +1 -0
- package/dist/tools/definitions.js +82 -3
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/fs.js +3 -10
- package/dist/tools/fs.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -0
- package/dist/tools/registry.js +263 -34
- package/dist/tools/registry.js.map +1 -1
- package/dist/tui-v2/bootstrap/overlay-ports.d.ts +5 -0
- package/dist/tui-v2/bootstrap/overlay-ports.js +63 -1
- package/dist/tui-v2/bootstrap/overlay-ports.js.map +1 -1
- package/dist/tui-v2/components/modal/confirm-modal.d.ts +2 -0
- package/dist/tui-v2/components/modal/confirm-modal.js +10 -3
- package/dist/tui-v2/components/modal/confirm-modal.js.map +1 -1
- package/dist/tui-v2/components/overlay/overlay-host.js +1 -1
- package/dist/tui-v2/components/overlay/overlay-host.js.map +1 -1
- package/dist/tui-v2/controllers/overlay-controller.d.ts +8 -1
- package/dist/tui-v2/controllers/overlay-controller.js +5 -3
- package/dist/tui-v2/controllers/overlay-controller.js.map +1 -1
- package/dist/tui-v2/rendering/batch-sections.d.ts +3 -0
- package/dist/tui-v2/rendering/batch-sections.js +40 -17
- package/dist/tui-v2/rendering/batch-sections.js.map +1 -1
- package/dist/tui-v2/rendering/file-diff-view.d.ts +2 -0
- package/dist/tui-v2/rendering/file-diff-view.js +61 -19
- package/dist/tui-v2/rendering/file-diff-view.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,6 +117,7 @@ Inside a session:
|
|
|
117
117
|
| **Network** | `net.scan` (nmap wrapper, SYN with privilege / TCP fallback), `net.context`, `net.pingSweep`, `pentest.recon` (whois + dig + top ports) |
|
|
118
118
|
| **HTTP evidence** | `http.fetch` — status, headers, cookies, TLS, body for **raw protocol / pentest** work (not casual page reading) |
|
|
119
119
|
| **Web reading / OSINT** | `web.search`, `web.fetch` (readable pages), plus shell for specialized CLIs |
|
|
120
|
+
| **Batch recon** | `tool.batch` — up to 20 lookups in one call; optional `on_fail` fail-fast or selective cancel when dependents need a prerequisite |
|
|
120
121
|
| **Discovery** | `tool.check`, `pkg.install`, `wordlist.find` — install only what is missing; locate wordlists per OS (no Kali-only path guesses) |
|
|
121
122
|
| **DNS / ownership** | `dns.lookup`, `whois.lookup` for narrow questions |
|
|
122
123
|
| **Shell** | Full toolbox: `nmap`, `ffuf`, `gobuster`, `feroxbuster`, `sqlmap`, `hydra`, `nikto`, `masscan`, `nuclei`, `tshark`, … via `shell.exec` |
|
|
@@ -156,18 +157,19 @@ Default posture is **non-destructive proof**. Escalate impact only when you ask
|
|
|
156
157
|
- Full plan + notes pager (`Ctrl+P` / `/plan`)
|
|
157
158
|
- Approve with `/implement`, revise in chat, cancel with `/discard`
|
|
158
159
|
- Plans **survive context compaction** and **reload with `/history`**
|
|
160
|
+
- Agent mode: working checklist + evidence-before-done; plan mode: roadmap you approve before execution
|
|
159
161
|
|
|
160
162
|
---
|
|
161
163
|
|
|
162
164
|
## Terminal UI (operator console)
|
|
163
165
|
|
|
164
|
-
Full-screen
|
|
166
|
+
Full-screen OpenTUI console by default: streaming chat, nested tool cards (including `tool.batch` sub-sections), file diffs, plan pane, pickers, history, secure key prompts. Falls back to a classic line REPL if the terminal cannot host the UI.
|
|
165
167
|
|
|
166
168
|
| Action | How |
|
|
167
169
|
|--------|-----|
|
|
168
170
|
| Send | `Enter` |
|
|
169
171
|
| Newline | `Shift+Enter` |
|
|
170
|
-
| Abort turn | `Esc` |
|
|
172
|
+
| Abort turn | `Esc` / `Ctrl+C` (cancels in-flight tools cleanly) |
|
|
171
173
|
| Expand thinking | `Ctrl+T` (clickable on status strip) |
|
|
172
174
|
| Expand tool / compacted output | `Ctrl+O` |
|
|
173
175
|
| Plan pane | `Ctrl+H` |
|
|
@@ -176,9 +178,9 @@ Full-screen UI by default: streaming chat, tool cards, plan pane, pickers, histo
|
|
|
176
178
|
| Commands / files | `/` · `@` |
|
|
177
179
|
| Exit | `Ctrl+C` twice |
|
|
178
180
|
|
|
179
|
-
Tool cards show **command/input** clearly and keep long scan tails in **OUTPUT** (expand or open pager). Compaction cards preserve engagement memory without dropping the plan.
|
|
181
|
+
Tool cards show **command/input** clearly and keep long scan tails in **OUTPUT** (expand or open pager). File writes show a **diff preview**. Deletes always ask for confirmation (`y`/`n`); press **`v`** to preview the path before confirming. Compaction cards preserve engagement memory without dropping the plan.
|
|
180
182
|
|
|
181
|
-
**`/history`** restores full sessions — prompts,
|
|
183
|
+
**`/history`** restores full sessions — prompts, tool results, findings context, and the matching plan when present (including after abort / autosave).
|
|
182
184
|
|
|
183
185
|
---
|
|
184
186
|
|
|
@@ -198,14 +200,18 @@ Coding and general sysadmin work use the same agent (scaffold, debug, packages)
|
|
|
198
200
|
## Features (summary)
|
|
199
201
|
|
|
200
202
|
- **Pentest-first agent loop** — recon-before-plan, stack-aware enum, evidence-backed findings
|
|
201
|
-
- **Durable plans** — `plan.create` / `task.update`, side pane, approve/refine/discard
|
|
202
|
-
- **
|
|
203
|
-
-
|
|
203
|
+
- **Durable plans** — `plan.create` / `task.update`, side pane, approve/refine/discard; agent vs plan task workflows
|
|
204
|
+
- **Parallel multi-tool turns** — independent reads run together; failures do **not** cancel siblings by default
|
|
205
|
+
- **`tool.batch` fail policy** — opt-in `on_fail=cancel_pending` or selective `cancel_on_fail` / rules when later work depends on earlier success
|
|
206
|
+
- **Native + text tool calling** — `toolCalling: auto|native|text`
|
|
207
|
+
- **11+ LLM providers** with streaming (free tiers + local Ollama)
|
|
208
|
+
- **Safety gate** + pentest authorization + optional engagement scope (deletes always confirm with preview)
|
|
204
209
|
- **OS-aware** installs and wordlist discovery (macOS / Linux / Windows)
|
|
205
210
|
- **Context compaction** (auto + `/compact`) that keeps the plan alive
|
|
206
|
-
- **Session history** with full transcript restore
|
|
211
|
+
- **Session history** with full transcript + plan restore
|
|
207
212
|
- **Background jobs** for long scanners and listeners
|
|
208
213
|
- **Web OSINT** — `web.search` / `web.fetch` alongside raw `http.fetch`
|
|
214
|
+
- **Stall / cancel robustness** — tool heartbeats, hard deadlines, clean Esc abort with results recorded
|
|
209
215
|
|
|
210
216
|
---
|
|
211
217
|
|
|
@@ -265,15 +271,44 @@ CLI mirrors: `clai authorize-pentest`, `clai scope add`, `clai doctor` (missing
|
|
|
265
271
|
|
|
266
272
|
| Tool | Role in engagements |
|
|
267
273
|
|------|---------------------|
|
|
268
|
-
| `shell.exec` / `shell.start` | nmap, ffuf, sqlmap, hydra, custom PoCs, listeners |
|
|
269
|
-
| `net.scan` · `net.context` · `pentest.recon` | Host/port/service discovery |
|
|
274
|
+
| `shell.exec` / `shell.start` · `shell.jobs` / `tail` / `stop` | nmap, ffuf, sqlmap, hydra, custom PoCs, listeners, background jobs |
|
|
275
|
+
| `net.scan` · `net.context` · `net.pingSweep` · `pentest.recon` | Host/port/service discovery |
|
|
270
276
|
| `http.fetch` | Raw HTTP/TLS evidence |
|
|
271
277
|
| `web.search` · `web.fetch` | OSINT / docs (readable), not raw exploit traffic |
|
|
272
278
|
| `dns.lookup` · `whois.lookup` | Narrow DNS / ownership |
|
|
279
|
+
| `tool.batch` | Fan-out up to 20 tools; `concurrency` 1–6; `on_fail` continue (default) / cancel_pending / rules; per-call `cancel_on_fail` |
|
|
273
280
|
| `tool.check` · `pkg.install` · `wordlist.find` | Tooling readiness |
|
|
274
|
-
| `fs
|
|
275
|
-
| `plan.create` · `task.update` | Engagement checklist |
|
|
281
|
+
| `fs.read` · `fs.list` · `fs.search` · `fs.write` · `fs.writeMany` · `fs.edit` · `fs.replaceLines` · `fs.append` · `fs.delete` | Files (sandboxed roots; delete always confirms + optional preview) |
|
|
282
|
+
| `plan.create` · `task.update` | Engagement checklist / working tasks |
|
|
276
283
|
| `sysinfo` · `image.ocr` · `pdf.read` | Host context, report/screenshot OCR |
|
|
284
|
+
| `agent.handoff` | Ask mode → offer agent mode when the user wants action, not explanation |
|
|
285
|
+
|
|
286
|
+
### `tool.batch` fail policy (opt-in)
|
|
287
|
+
|
|
288
|
+
Default is **continue** — one failed lookup never kills the rest (best for recon).
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
// Fail-fast: stop remaining calls after the first failure
|
|
292
|
+
{"name":"tool.batch","args":{
|
|
293
|
+
"on_fail":"cancel_pending",
|
|
294
|
+
"calls":[
|
|
295
|
+
{"name":"net.scan","args":{"target":"lab.example"}},
|
|
296
|
+
{"name":"http.fetch","args":{"url":"https://lab.example/"}}
|
|
297
|
+
]
|
|
298
|
+
}}
|
|
299
|
+
|
|
300
|
+
// Selective: if scan fails, cancel only fuzz (dns still runs)
|
|
301
|
+
{"name":"tool.batch","args":{
|
|
302
|
+
"calls":[
|
|
303
|
+
{"id":"dns","name":"dns.lookup","args":{"target":"lab.example"}},
|
|
304
|
+
{"id":"scan","name":"net.scan","args":{"target":"lab.example"},
|
|
305
|
+
"cancel_on_fail":["fuzz"]},
|
|
306
|
+
{"id":"fuzz","name":"shell.exec","args":{"command":"ffuf …"}}
|
|
307
|
+
]
|
|
308
|
+
}}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Top-level multi-tool messages (several separate tool blocks) never cancel siblings; use `tool.batch` when you need a fail policy.
|
|
277
312
|
|
|
278
313
|
### Search providers (OSINT)
|
|
279
314
|
|
|
@@ -334,10 +369,10 @@ Node.js ≥ 20.
|
|
|
334
369
|
Tag-driven CI (`.github/workflows/release.yml`): tests → multi-platform binaries → GitHub Release → npm `@pentoshi/clai` → Homebrew tap.
|
|
335
370
|
|
|
336
371
|
```sh
|
|
337
|
-
npm version
|
|
372
|
+
npm version 3.7.6 --no-git-tag-version
|
|
338
373
|
# bump FALLBACK_VERSION / manifests as needed
|
|
339
|
-
git commit -am "
|
|
340
|
-
git tag -a
|
|
374
|
+
git commit -am "v3.7.6" && git push origin main
|
|
375
|
+
git tag -a v3.7.6 -m "clai v3.7.6" && git push origin v3.7.6
|
|
341
376
|
```
|
|
342
377
|
|
|
343
378
|
Secrets: `NPM_TOKEN`, `TAP_GITHUB_TOKEN`. Optional: `NPM_PROVENANCE=true`.
|
|
@@ -352,13 +387,13 @@ clai/
|
|
|
352
387
|
│ ├─ index.ts # CLI entry
|
|
353
388
|
│ ├─ modes/ # ask · agent
|
|
354
389
|
│ ├─ agent/ # loop, plans, compaction, tool parsing
|
|
355
|
-
│ ├─ llm/ # providers + streaming
|
|
356
|
-
│ ├─ tools/ # shell, net, http, web, fs, pentest, …
|
|
390
|
+
│ ├─ llm/ # providers + streaming + native tools
|
|
391
|
+
│ ├─ tools/ # shell, net, http, web, fs, batch, pentest, …
|
|
357
392
|
│ ├─ safety/ # classifier + patterns
|
|
358
393
|
│ ├─ store/ # config, history, keys, plans, scope, logs
|
|
359
|
-
│ ├─ tui/
|
|
394
|
+
│ ├─ tui-v2/ # full-screen OpenTUI (primary)
|
|
360
395
|
│ ├─ app/ # session, commands, events
|
|
361
|
-
│ └─ prompts/ # agent methodology (incl. pentest)
|
|
396
|
+
│ └─ prompts/ # agent methodology (incl. pentest; embedded for bun)
|
|
362
397
|
├─ bin/clai.mjs
|
|
363
398
|
├─ install/ · manifests/
|
|
364
399
|
└─ package.json
|
|
@@ -77,8 +77,9 @@ export async function ensurePentestAuthorization(call, autoConfirm, session, con
|
|
|
77
77
|
return true;
|
|
78
78
|
}
|
|
79
79
|
export async function confirmToolExecution(call, autoConfirm, session, confirmPort, options) {
|
|
80
|
-
//
|
|
81
|
-
|
|
80
|
+
// fs.delete + outside-cwd mutates always prompt — even under allow-all / -y.
|
|
81
|
+
// Deletion is irreversible; never auto-approve.
|
|
82
|
+
if (options?.forceConfirm || call.name === "fs.delete") {
|
|
82
83
|
return confirmPort.confirmTool(call);
|
|
83
84
|
}
|
|
84
85
|
const config = getConfig();
|
|
@@ -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,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,WAAW,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACpD,6DAA6D;IAC7D,IAAI,MAAM,CAAC,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAC1C,+DAA+D;IAC/D,IAAI,OAAO,CAAC,iBAAiB,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAEjD,IAAI,WAAW,EAAE,CAAC;QAChB,sEAAsE;QACtE,oEAAoE;QACpE,OAAO,CAAC,iBAAiB,CAAC,KAAK,GAAG,IAAI,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,cAAc,EAAE,CAAC;IAC9C,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IACtB,OAAO,CAAC,iBAAiB,CAAC,KAAK,GAAG,IAAI,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAc,EACd,WAAoB,EACpB,OAAsB,EACtB,WAAwB,EACxB,OAAgD;IAEhD,
|
|
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,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,WAAW,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACpD,6DAA6D;IAC7D,IAAI,MAAM,CAAC,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAC1C,+DAA+D;IAC/D,IAAI,OAAO,CAAC,iBAAiB,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAEjD,IAAI,WAAW,EAAE,CAAC;QAChB,sEAAsE;QACtE,oEAAoE;QACpE,OAAO,CAAC,iBAAiB,CAAC,KAAK,GAAG,IAAI,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,cAAc,EAAE,CAAC;IAC9C,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IACtB,OAAO,CAAC,iBAAiB,CAAC,KAAK,GAAG,IAAI,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAc,EACd,WAAoB,EACpB,OAAsB,EACtB,WAAwB,EACxB,OAAgD;IAEhD,6EAA6E;IAC7E,gDAAgD;IAChD,IAAI,OAAO,EAAE,YAAY,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACvD,OAAO,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,WAAW,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,WAAW;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,uEAAuE;IACvE,wEAAwE;IACxE,qDAAqD;IACrD,IAAI,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7D,OAAO,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC"}
|
package/dist/agent/runner.js
CHANGED
|
@@ -34,7 +34,7 @@ import { stripSentinelTokens, parseToolCall, recognizeBareToolJson, looksLikeTru
|
|
|
34
34
|
import { createSessionPolicy, isPreApprovalAllowedTool, isPlanModeAllowedShellCommand, isPlanModeAllowedTool, isPlanApprovedByStatus, planHasOpenWork, isAbortError, shouldEnableImageOcr, } from "./session-policy.js";
|
|
35
35
|
import { saveToolOutput, summarizeOutput, formatToolContext, } from "./tool-output-formatting.js";
|
|
36
36
|
import { renderPlanForTerminal, planContextMessage, handlePlanTool, resolvePlanTaskId, } from "./plan-tool.js";
|
|
37
|
-
import { absorbLooseWorkIntoLedger, applyDestinationCwd, canMarkTaskDone, hasLocalRuntimeProof, hasRemoteWorkProof,
|
|
37
|
+
import { absorbLooseWorkIntoLedger, applyDestinationCwd, canMarkTaskDone, hasLocalRuntimeProof, hasRemoteWorkProof, isDevServerCall, isEvidenceWorkTool, isFeatureImplementationCall, isPackageInstallCommand, isPlanPreflightTool, isPortListeningOutput, isReadOnlyReconTool, isRemoteActiveTestCall, isRemoteObservationTask, isRemoteReconToolCall, isRuntimeObservationTask, isScaffoldCreateCommand, isServerReadyOutput, ledgerFromTaskEvidence, pickPendingTaskForToolCall, recordTaskWorkSuccess, resolveUserDestinationHint, taskEvidenceFromLedger, TOOL_ABORT_GRACE_MS, toolHardBudgetMs, toolStallBudgetMs, userAskedForFeatureApp, } from "./task-evidence.js";
|
|
38
38
|
import { buildSessionStateBlock, inferNextHint, upsertSessionStateMessage, } from "./session-state.js";
|
|
39
39
|
import { detectPackageManager } from "./workspace-orient.js";
|
|
40
40
|
import { budgetRemaining, consumeBudget, createRecoveryBudgets, freestyleClaimsAppReady, looksLikeShallowPentestReport, recoveryForErrorDiagnosis, recoveryForFailedProbe, recoveryForFreshness, recoveryForMissingFeature, recoveryForMissingPlan, recoveryForNarration, recoveryForPrematureComplete, recoveryForRuntimeVerify, recoveryForShallowPentest, } from "./must-continue.js";
|
|
@@ -2861,7 +2861,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2861
2861
|
// Notice BEFORE tool cards so the transcript reads:
|
|
2862
2862
|
// thinking → response → "N tool calls…" → tool cards (not tools then info).
|
|
2863
2863
|
if (allCalls.length > 1) {
|
|
2864
|
-
writeNotice("info", `${allCalls.length} tool calls in this message —
|
|
2864
|
+
writeNotice("info", `${allCalls.length} tool calls in this message — read-only in parallel, writes in order (failures do not cancel siblings)`, chalk.dim(` ℹ ${allCalls.length} tool calls — parallel reads, ordered writes; failures continue\n`));
|
|
2865
2865
|
}
|
|
2866
2866
|
for (const deferred of activeDeferredToolCalls.slice(0, allCalls.length)) {
|
|
2867
2867
|
if (!deferred.call.name || deferred.call.name === "…")
|
|
@@ -2897,24 +2897,19 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2897
2897
|
return false;
|
|
2898
2898
|
}
|
|
2899
2899
|
};
|
|
2900
|
-
/** Tools whose failure must NOT cancel sibling calls in this turn. */
|
|
2901
|
-
const shouldSoftFailTool = (name) => {
|
|
2902
|
-
if (isBatchSoftFailTool(name))
|
|
2903
|
-
return true;
|
|
2904
|
-
if (BATCH_SAFE_TOOLS.has(name))
|
|
2905
|
-
return true;
|
|
2906
|
-
return false;
|
|
2907
|
-
};
|
|
2908
2900
|
// Recon waves often emit 6–10 lookups; 4 forced a second sequential wave.
|
|
2909
2901
|
const PARALLEL_LIMIT = 8;
|
|
2910
2902
|
let aborted = false;
|
|
2911
|
-
let blocked = false;
|
|
2912
|
-
let blockedResult = null;
|
|
2913
|
-
let failed = false;
|
|
2914
2903
|
let awaitingPlanApproval = false;
|
|
2915
2904
|
/** Native tool_call ids that already have a role:tool history entry. */
|
|
2916
2905
|
const recordedNativeIds = new Set();
|
|
2917
|
-
|
|
2906
|
+
/**
|
|
2907
|
+
* Record a tool result into history. Failures / user declines are
|
|
2908
|
+
* always returned to the model — we never cancel later siblings or
|
|
2909
|
+
* force-end the turn as "blocked" because one tool failed. Only an
|
|
2910
|
+
* explicit user abort stops remaining calls.
|
|
2911
|
+
*/
|
|
2912
|
+
const recordResult = (boundCall, res) => {
|
|
2918
2913
|
recordedNativeIds.add(boundCall.id);
|
|
2919
2914
|
const toolContent = `Tool ${res.call.name} result (exit=${res.result.exitCode ?? 0}, ok=${res.result.ok}):\n${res.contextOutput}`;
|
|
2920
2915
|
if (historyNativeCalls.length) {
|
|
@@ -3021,22 +3016,14 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3021
3016
|
session.planApproved.value = true;
|
|
3022
3017
|
}
|
|
3023
3018
|
}
|
|
3019
|
+
// User Esc/Ctrl+C only — never cancel siblings because a delete failed
|
|
3020
|
+
// or a confirm was declined; the model must see every tool result.
|
|
3024
3021
|
if (res.lastAnswer === "Aborted.")
|
|
3025
3022
|
aborted = true;
|
|
3026
|
-
else if (res.blockOrCancel) {
|
|
3027
|
-
blocked = true;
|
|
3028
|
-
blockedResult = res;
|
|
3029
|
-
}
|
|
3030
|
-
else if (!res.ok && !continueAfterFailure)
|
|
3031
|
-
failed = true;
|
|
3032
3023
|
};
|
|
3033
3024
|
const groups = groupToolCallsForExecution(allCalls, isParallelSafe, PARALLEL_LIMIT);
|
|
3034
3025
|
for (const group of groups) {
|
|
3035
|
-
if (aborted ||
|
|
3036
|
-
blocked ||
|
|
3037
|
-
failed ||
|
|
3038
|
-
awaitingPlanApproval ||
|
|
3039
|
-
governorPauseReason)
|
|
3026
|
+
if (aborted || awaitingPlanApproval || governorPauseReason)
|
|
3040
3027
|
break;
|
|
3041
3028
|
if (group.length === 1) {
|
|
3042
3029
|
const call = group[0];
|
|
@@ -3048,8 +3035,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3048
3035
|
}
|
|
3049
3036
|
const id = callIds[bc.index];
|
|
3050
3037
|
const res = await executeSingleTool(call, id, options.signal || new AbortController().signal);
|
|
3051
|
-
|
|
3052
|
-
recordResult(bc, res, softFail);
|
|
3038
|
+
recordResult(bc, res);
|
|
3053
3039
|
}
|
|
3054
3040
|
else {
|
|
3055
3041
|
// Concurrent group — BoundCall via Map; record in document order.
|
|
@@ -3067,11 +3053,12 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3067
3053
|
}
|
|
3068
3054
|
const results = await Promise.all(groupBound.map((bc, k) => executeSingleTool(bc.call, uiIds[k], options.signal || new AbortController().signal)));
|
|
3069
3055
|
for (let k = 0; k < results.length; k += 1) {
|
|
3070
|
-
recordResult(groupBound[k], results[k]
|
|
3056
|
+
recordResult(groupBound[k], results[k]);
|
|
3071
3057
|
}
|
|
3072
3058
|
}
|
|
3073
3059
|
}
|
|
3074
3060
|
// Cards still "running" get a terminal UI result; history always pairs.
|
|
3061
|
+
// Only abort / plan-gate / governor leave calls un-run now.
|
|
3075
3062
|
for (let i = 0; i < toRun.length; i += 1) {
|
|
3076
3063
|
const bc = toRun[i];
|
|
3077
3064
|
if (recordedNativeIds.has(bc.id))
|
|
@@ -3082,15 +3069,11 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3082
3069
|
const uiId = callIds[i];
|
|
3083
3070
|
const reason = aborted
|
|
3084
3071
|
? "Cancelled — turn aborted before this call ran."
|
|
3085
|
-
:
|
|
3086
|
-
? "
|
|
3087
|
-
:
|
|
3088
|
-
?
|
|
3089
|
-
:
|
|
3090
|
-
? `Deferred — progress governor paused execution: ${governorPauseReason}`
|
|
3091
|
-
: failed
|
|
3092
|
-
? "Cancelled — earlier tool in this batch failed."
|
|
3093
|
-
: "Cancelled — not executed.";
|
|
3072
|
+
: awaitingPlanApproval
|
|
3073
|
+
? "Deferred — waiting for plan approval."
|
|
3074
|
+
: governorPauseReason
|
|
3075
|
+
? `Deferred — progress governor paused execution: ${governorPauseReason}`
|
|
3076
|
+
: "Cancelled — not executed.";
|
|
3094
3077
|
const result = {
|
|
3095
3078
|
ok: false,
|
|
3096
3079
|
output: reason,
|
|
@@ -3138,13 +3121,8 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3138
3121
|
writeAbort();
|
|
3139
3122
|
return finishTurn(lastAnswer, productiveSteps, "aborted");
|
|
3140
3123
|
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
outcomeState.outcome.status = "blocked";
|
|
3144
|
-
await saveOutcomeState(outcomeState);
|
|
3145
|
-
moveTurn("blocked", lastAnswer);
|
|
3146
|
-
return finishTurn(lastAnswer, productiveSteps, "blocked");
|
|
3147
|
-
}
|
|
3124
|
+
// Confirm declines / tool failures already have role:tool results —
|
|
3125
|
+
// continue the agent loop so the model can adapt (do not force "blocked").
|
|
3148
3126
|
await maybeAutoCompact("post-tool-token-budget");
|
|
3149
3127
|
if (options.onMessages) {
|
|
3150
3128
|
try {
|