@pentoshi/clai 2.0.27 → 2.0.29

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.
Files changed (112) hide show
  1. package/dist/agent/confirm-port.d.ts +30 -0
  2. package/dist/agent/confirm-port.js +89 -0
  3. package/dist/agent/confirm-port.js.map +1 -0
  4. package/dist/agent/context-manager.d.ts +10 -0
  5. package/dist/agent/context-manager.js +16 -2
  6. package/dist/agent/context-manager.js.map +1 -1
  7. package/dist/agent/plan-tool.d.ts +26 -0
  8. package/dist/agent/plan-tool.js +188 -0
  9. package/dist/agent/plan-tool.js.map +1 -0
  10. package/dist/agent/runner.d.ts +14 -165
  11. package/dist/agent/runner.js +37 -1585
  12. package/dist/agent/runner.js.map +1 -1
  13. package/dist/agent/session-policy.d.ts +19 -0
  14. package/dist/agent/session-policy.js +40 -0
  15. package/dist/agent/session-policy.js.map +1 -0
  16. package/dist/agent/stop-summary.d.ts +8 -0
  17. package/dist/agent/stop-summary.js +53 -0
  18. package/dist/agent/stop-summary.js.map +1 -0
  19. package/dist/agent/tool-call-parser.d.ts +155 -0
  20. package/dist/agent/tool-call-parser.js +1107 -0
  21. package/dist/agent/tool-call-parser.js.map +1 -0
  22. package/dist/agent/tool-output-formatting.d.ts +7 -0
  23. package/dist/agent/tool-output-formatting.js +127 -0
  24. package/dist/agent/tool-output-formatting.js.map +1 -0
  25. package/dist/commands/update.js +1 -1
  26. package/dist/modes/agent.d.ts +1 -0
  27. package/dist/modes/agent.js.map +1 -1
  28. package/dist/prompts/index.d.ts +2 -2
  29. package/dist/prompts/index.js +103 -59
  30. package/dist/prompts/index.js.map +1 -1
  31. package/dist/repl/prompt-line.d.ts +19 -0
  32. package/dist/repl/prompt-line.js +555 -0
  33. package/dist/repl/prompt-line.js.map +1 -0
  34. package/dist/repl/slash-commands.d.ts +24 -0
  35. package/dist/repl/slash-commands.js +317 -0
  36. package/dist/repl/slash-commands.js.map +1 -0
  37. package/dist/repl.d.ts +2 -25
  38. package/dist/repl.js +41 -898
  39. package/dist/repl.js.map +1 -1
  40. package/dist/safety/classifier.js +1 -1
  41. package/dist/safety/classifier.js.map +1 -1
  42. package/dist/store/keys.js +0 -4
  43. package/dist/store/keys.js.map +1 -1
  44. package/dist/store/plan.js +1 -1
  45. package/dist/store/plan.js.map +1 -1
  46. package/dist/tools/nmap-runner.d.ts +19 -0
  47. package/dist/tools/nmap-runner.js +177 -0
  48. package/dist/tools/nmap-runner.js.map +1 -0
  49. package/dist/tools/package-binary.d.ts +1 -0
  50. package/dist/tools/package-binary.js +30 -0
  51. package/dist/tools/package-binary.js.map +1 -0
  52. package/dist/tools/pdf.js +2 -2
  53. package/dist/tools/pdf.js.map +1 -1
  54. package/dist/tools/registry.d.ts +2 -11
  55. package/dist/tools/registry.js +3 -203
  56. package/dist/tools/registry.js.map +1 -1
  57. package/dist/tools/tool-types.d.ts +12 -0
  58. package/dist/tools/tool-types.js +2 -0
  59. package/dist/tools/tool-types.js.map +1 -0
  60. package/dist/tools/web/audit.d.ts +7 -29
  61. package/dist/tools/web/audit.js +7 -29
  62. package/dist/tools/web/audit.js.map +1 -1
  63. package/dist/tools/web/budget.d.ts +5 -28
  64. package/dist/tools/web/budget.js +5 -28
  65. package/dist/tools/web/budget.js.map +1 -1
  66. package/dist/tools/web/capture.d.ts +8 -35
  67. package/dist/tools/web/capture.js +8 -35
  68. package/dist/tools/web/capture.js.map +1 -1
  69. package/dist/tools/web/fetch-core.d.ts +6 -26
  70. package/dist/tools/web/fetch-core.js +6 -26
  71. package/dist/tools/web/fetch-core.js.map +1 -1
  72. package/dist/tools/web/readable.d.ts +7 -13
  73. package/dist/tools/web/readable.js +7 -13
  74. package/dist/tools/web/readable.js.map +1 -1
  75. package/dist/tools/web/search.d.ts +7 -20
  76. package/dist/tools/web/search.js +7 -20
  77. package/dist/tools/web/search.js.map +1 -1
  78. package/dist/tools/web/ssrf-guard.d.ts +8 -27
  79. package/dist/tools/web/ssrf-guard.js +8 -27
  80. package/dist/tools/web/ssrf-guard.js.map +1 -1
  81. package/dist/tui/App.js +19 -88
  82. package/dist/tui/App.js.map +1 -1
  83. package/dist/tui/hooks/useAgentRunner.js +17 -1
  84. package/dist/tui/hooks/useAgentRunner.js.map +1 -1
  85. package/dist/tui/key-intent.d.ts +36 -0
  86. package/dist/tui/key-intent.js +54 -0
  87. package/dist/tui/key-intent.js.map +1 -0
  88. package/dist/tui/render-lines.js +125 -37
  89. package/dist/tui/render-lines.js.map +1 -1
  90. package/dist/tui/state.js +2 -2
  91. package/dist/tui/state.js.map +1 -1
  92. package/dist/tui/text-format.d.ts +11 -0
  93. package/dist/tui/text-format.js +85 -0
  94. package/dist/tui/text-format.js.map +1 -0
  95. package/dist/ui/ansi-box.d.ts +7 -0
  96. package/dist/ui/ansi-box.js +18 -0
  97. package/dist/ui/ansi-box.js.map +1 -0
  98. package/dist/ui/banner.d.ts +0 -9
  99. package/dist/ui/banner.js +1 -43
  100. package/dist/ui/banner.js.map +1 -1
  101. package/dist/ui/intro-card.d.ts +10 -0
  102. package/dist/ui/intro-card.js +39 -0
  103. package/dist/ui/intro-card.js.map +1 -0
  104. package/dist/ui/markdown.js +1 -1
  105. package/dist/ui/markdown.js.map +1 -1
  106. package/dist/ui/output-pane.js.map +1 -1
  107. package/dist/ui/spinner.js +6 -17
  108. package/dist/ui/spinner.js.map +1 -1
  109. package/dist/ui/wordmark.d.ts +7 -0
  110. package/dist/ui/wordmark.js +42 -0
  111. package/dist/ui/wordmark.js.map +1 -0
  112. package/package.json +1 -1
@@ -0,0 +1,30 @@
1
+ import type { ToolCall } from "../types.js";
2
+ import type { SessionPolicy } from "./session-policy.js";
3
+ export interface ConfirmPort {
4
+ confirmTool(call: ToolCall): Promise<boolean>;
5
+ confirmPentest(): Promise<boolean>;
6
+ /** Ask the user whether to continue after hitting the step budget. */
7
+ confirmContinue?(steps: number): Promise<boolean>;
8
+ /**
9
+ * Ask whether to leave ask mode and run an action task in agent mode.
10
+ * Optional so existing ports keep working; ask-mode handoff falls back to a
11
+ * default "no" when a port doesn't implement it.
12
+ */
13
+ confirmAgentSwitch?(info: {
14
+ reason: string;
15
+ tools: string[];
16
+ }): Promise<boolean>;
17
+ }
18
+ /**
19
+ * Re-assert raw mode AND resume stdin after an inquirer prompt
20
+ * (confirm/password). inquirer's readline interface pauses stdin and
21
+ * switches it to cooked mode when it closes; if we only flip raw mode back
22
+ * on but leave stdin paused, no `keypress`/`data` events flow to the REPL's
23
+ * ESC/Ctrl+C abort handler — so a long-running tool launched right after a
24
+ * confirmation can no longer be aborted (the user had to kill the terminal).
25
+ * Calling resume() restores the event flow.
26
+ */
27
+ export declare function restoreInteractiveStdin(): void;
28
+ export declare const inquirerConfirmPort: ConfirmPort;
29
+ export declare function ensurePentestAuthorization(call: ToolCall, autoConfirm: boolean, session: SessionPolicy, confirmPort: ConfirmPort): Promise<boolean>;
30
+ export declare function confirmToolExecution(call: ToolCall, autoConfirm: boolean, session: SessionPolicy, confirmPort: ConfirmPort): Promise<boolean>;
@@ -0,0 +1,89 @@
1
+ import { confirm } from "@inquirer/prompts";
2
+ import chalk from "chalk";
3
+ import { getConfig } from "../store/config.js";
4
+ import { isPentestToolCall } from "../safety/classifier.js";
5
+ import { formatToolArgs } from "./tool-call-parser.js";
6
+ /**
7
+ * Re-assert raw mode AND resume stdin after an inquirer prompt
8
+ * (confirm/password). inquirer's readline interface pauses stdin and
9
+ * switches it to cooked mode when it closes; if we only flip raw mode back
10
+ * on but leave stdin paused, no `keypress`/`data` events flow to the REPL's
11
+ * ESC/Ctrl+C abort handler — so a long-running tool launched right after a
12
+ * confirmation can no longer be aborted (the user had to kill the terminal).
13
+ * Calling resume() restores the event flow.
14
+ */
15
+ export function restoreInteractiveStdin() {
16
+ if (!process.stdin.isTTY)
17
+ return;
18
+ try {
19
+ if (!process.stdin.isRaw) {
20
+ process.stdin.setRawMode(true);
21
+ }
22
+ process.stdin.resume();
23
+ }
24
+ catch {
25
+ /* ignore */
26
+ }
27
+ }
28
+ export const inquirerConfirmPort = {
29
+ async confirmTool(call) {
30
+ return confirm({
31
+ message: chalk.yellow(` run ${call.name}: ${formatToolArgs(call)}?`),
32
+ default: true,
33
+ });
34
+ },
35
+ async confirmPentest() {
36
+ return confirm({
37
+ message: chalk.red("clai only assists with security testing on systems you own or have written permission to test. Confirm for this session?"),
38
+ default: false,
39
+ });
40
+ },
41
+ async confirmContinue(steps) {
42
+ return confirm({
43
+ message: chalk.yellow(` ${steps} steps reached — continue?`),
44
+ default: true,
45
+ });
46
+ },
47
+ async confirmAgentSwitch(info) {
48
+ const tools = info.tools.length > 0 ? ` (${info.tools.join(", ")})` : "";
49
+ return confirm({
50
+ message: chalk.yellow(` this needs agent mode${tools} — switch and run it?`),
51
+ default: true,
52
+ });
53
+ },
54
+ };
55
+ export async function ensurePentestAuthorization(call, autoConfirm, session, confirmPort) {
56
+ if (!isPentestToolCall(call))
57
+ return true;
58
+ // Persistent auth (via `clai authorize-pentest AGREE`) wins.
59
+ if (getConfig().pentestAuthorized)
60
+ return true;
61
+ // Session auth flipped earlier in this session — no re-prompt.
62
+ if (session.pentestAuthorized.value)
63
+ return true;
64
+ if (autoConfirm) {
65
+ // -y is session-scoped only. We do NOT touch the persistent config so
66
+ // a one-shot `-y` cannot silently authorize later interactive runs.
67
+ session.pentestAuthorized.value = true;
68
+ return true;
69
+ }
70
+ const ok = await confirmPort.confirmPentest();
71
+ if (!ok)
72
+ return false;
73
+ session.pentestAuthorized.value = true;
74
+ return true;
75
+ }
76
+ export async function confirmToolExecution(call, autoConfirm, session, confirmPort) {
77
+ const config = getConfig();
78
+ if (autoConfirm)
79
+ return true;
80
+ if (session.allow.has(call.name))
81
+ return true;
82
+ // Persistent allowlist kept for backwards compat with users who set it
83
+ // through `clai config` directly, but `/allow` only mutates the session
84
+ // set so authorizations never leak across processes.
85
+ if (config.allowAlwaysTools.includes(call.name))
86
+ return true;
87
+ return confirmPort.confirmTool(call);
88
+ }
89
+ //# sourceMappingURL=confirm-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm-port.js","sourceRoot":"","sources":["../../src/agent/confirm-port.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,16 @@ export interface CompactResult {
22
22
  afterTokens: number;
23
23
  summarized: boolean;
24
24
  }
25
+ /**
26
+ * Content prefixes that mark a `role:"system"` message as compacted session
27
+ * memory (vs. the main system prompt or transient injected guidance). Exported
28
+ * so history-persistence can KEEP this memory when it drops other system
29
+ * messages — otherwise a resumed session that compacted mid-run would lose all
30
+ * summarized context.
31
+ */
32
+ export declare const COMPACTION_MEMORY_PREFIX = "Session memory from compacted earlier turns:";
33
+ export declare const MECHANICAL_MEMORY_PREFIX = "Earlier turns in this session, summarized";
34
+ export declare function isCompactionMemoryMessage(message: ChatMessage): boolean;
25
35
  /**
26
36
  * Replace older messages with a single condensed "memory" message while
27
37
  * preserving the system prompt and the most recent N messages.
@@ -18,6 +18,20 @@ export function estimateMessagesTokens(messages) {
18
18
  }
19
19
  const DEFAULT_BUDGET_TOKENS = 32_000;
20
20
  const DEFAULT_KEEP_RECENT = 12;
21
+ /**
22
+ * Content prefixes that mark a `role:"system"` message as compacted session
23
+ * memory (vs. the main system prompt or transient injected guidance). Exported
24
+ * so history-persistence can KEEP this memory when it drops other system
25
+ * messages — otherwise a resumed session that compacted mid-run would lose all
26
+ * summarized context.
27
+ */
28
+ export const COMPACTION_MEMORY_PREFIX = "Session memory from compacted earlier turns:";
29
+ export const MECHANICAL_MEMORY_PREFIX = "Earlier turns in this session, summarized";
30
+ export function isCompactionMemoryMessage(message) {
31
+ return (message.role === "system" &&
32
+ (message.content.startsWith(COMPACTION_MEMORY_PREFIX) ||
33
+ message.content.startsWith(MECHANICAL_MEMORY_PREFIX)));
34
+ }
21
35
  /**
22
36
  * Replace older messages with a single condensed "memory" message while
23
37
  * preserving the system prompt and the most recent N messages.
@@ -62,7 +76,7 @@ export function compactMessages(messages, options = {}) {
62
76
  }
63
77
  const memo = {
64
78
  role: "system",
65
- content: `Earlier turns in this session, summarized to fit the context budget. Full artifacts (when produced) are saved on disk and can be expanded with /output.\n\n` +
79
+ content: `${MECHANICAL_MEMORY_PREFIX} to fit the context budget. Full artifacts (when produced) are saved on disk and can be expanded with /output.\n\n` +
66
80
  bullets.join("\n"),
67
81
  };
68
82
  return [...head, memo, ...tail];
@@ -123,7 +137,7 @@ export async function compactMessagesWithSummary(messages, summarize, options =
123
137
  const head = start === 1 ? [messages[0]] : [];
124
138
  const compacted = [
125
139
  ...head,
126
- { role: "system", content: `Session memory from compacted earlier turns:\n\n${summary}` },
140
+ { role: "system", content: `${COMPACTION_MEMORY_PREFIX}\n\n${summary}` },
127
141
  ...messages.slice(tailStart),
128
142
  ];
129
143
  const afterTokens = estimateMessagesTokens(compacted);
@@ -1 +1 @@
1
- {"version":3,"file":"context-manager.js","sourceRoot":"","sources":["../../src/agent/context-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context-manager.js","sourceRoot":"","sources":["../../src/agent/context-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { type SessionPlan } from "../store/plan.js";
2
+ import type { LoopGuard } from "./loop-guard.js";
3
+ import type { SessionPolicy } from "./session-policy.js";
4
+ import type { ToolCall } from "../types.js";
5
+ /** Render the plan as a right-side pane on wide terminals, else inline. */
6
+ export declare function renderPlanForTerminal(plan: SessionPlan): string;
7
+ export interface PlanToolResult {
8
+ handled: boolean;
9
+ ok: boolean;
10
+ plan?: SessionPlan | undefined;
11
+ /** What to print to the user's terminal. */
12
+ display: string;
13
+ /** What to feed back to the model as the tool result. */
14
+ modelNote: string;
15
+ }
16
+ /** Build the system-context block describing the session's active plan. */
17
+ export declare function planContextMessage(plan: SessionPlan, approved: boolean): string;
18
+ /**
19
+ * Handle plan.create / task.update inline. These are session-scoped and
20
+ * persisted via the plan store so the user can view the plan (Ctrl+P) and
21
+ * the agent keeps it in context across the whole session.
22
+ */
23
+ export declare function handlePlanTool(call: ToolCall, session: SessionPolicy, ctx: {
24
+ loopGuard: LoopGuard;
25
+ step: number;
26
+ }): Promise<PlanToolResult>;
@@ -0,0 +1,188 @@
1
+ import chalk from "chalk";
2
+ import { createPlan, loadPlan, savePlan, markTask, } from "../store/plan.js";
3
+ import { renderPlanChecklist, renderPlanSidePane } from "../ui/plan-pane.js";
4
+ import { isLumpedSingleTask } from "./tool-call-parser.js";
5
+ /** Render the plan as a right-side pane on wide terminals, else inline. */
6
+ export function renderPlanForTerminal(plan) {
7
+ const cols = process.stdout.columns ?? 0;
8
+ const side = process.stdout.isTTY
9
+ ? renderPlanSidePane(plan, cols)
10
+ : undefined;
11
+ return side ?? renderPlanChecklist(plan);
12
+ }
13
+ /** Build the system-context block describing the session's active plan. */
14
+ export function planContextMessage(plan, approved) {
15
+ const lines = [];
16
+ lines.push(`ACTIVE PLAN for this session (goal: ${plan.goal}, status: ${plan.status}):`);
17
+ if (plan.detail.trim())
18
+ lines.push(plan.detail.trim());
19
+ lines.push("Tasks:");
20
+ plan.tasks.forEach((t, i) => {
21
+ lines.push(` ${i + 1}. [${t.id}] (${t.state}) ${t.title}`);
22
+ });
23
+ if (approved) {
24
+ const firstPending = plan.tasks.find((t) => t.state === "pending");
25
+ lines.push("The user APPROVED this plan. Execute it task by task NOW.");
26
+ if (firstPending) {
27
+ lines.push(`START WITH TASK ${firstPending.id} (${firstPending.title}). ` +
28
+ "Do NOT re-do tasks already marked done, and do NOT skip ahead to later tasks.");
29
+ }
30
+ lines.push("STRICT ORDER: call task.update {taskId, state:'in_progress'} → do the real work → " +
31
+ "call task.update {taskId, state:'done'} ONLY after the tool calls actually succeed. " +
32
+ "If a tool fails, mark the task 'failed' with a note, fix the problem, then retry. " +
33
+ "Do NOT mark a task done when its commands error out. " +
34
+ "Never claim something ran without a successful tool call.");
35
+ }
36
+ else {
37
+ lines.push("This plan is NOT yet approved, so you MUST NOT execute any of its tasks yet. " +
38
+ "Any new free-text message from the user right now is a PLAN REVISION, not approval — even if it " +
39
+ "sounds like an instruction (e.g. 'do not install new tools', 'use only X', 'also add Y', 'skip task 2'). " +
40
+ "Treat it as feedback: call plan.create AGAIN with the revised goal/detail/tasks to produce an updated " +
41
+ "plan, then STOP and wait. Do NOT call shell.exec, pkg.install, net.scan, tool.check, fs.write, or any " +
42
+ "other execution tool. The user will APPROVE with /implement, or CANCEL with /discard. Only after " +
43
+ "/implement may you begin executing.");
44
+ }
45
+ return lines.join("\n");
46
+ }
47
+ /**
48
+ * Handle plan.create / task.update inline. These are session-scoped and
49
+ * persisted via the plan store so the user can view the plan (Ctrl+P) and
50
+ * the agent keeps it in context across the whole session.
51
+ */
52
+ export async function handlePlanTool(call, session, ctx) {
53
+ void ctx;
54
+ if (call.name === "plan.create") {
55
+ const goal = typeof call.args.goal === "string" ? call.args.goal : "";
56
+ const detail = typeof call.args.detail === "string" ? call.args.detail : "";
57
+ const kind = typeof call.args.kind === "string" ? call.args.kind : "general";
58
+ const rawTasks = Array.isArray(call.args.tasks) ? call.args.tasks : [];
59
+ const taskTitles = rawTasks
60
+ .map((t) => (typeof t === "string" ? t : ""))
61
+ .filter(Boolean);
62
+ if (!goal || taskTitles.length === 0) {
63
+ return {
64
+ handled: true,
65
+ ok: false,
66
+ display: chalk.red(" ✗ plan.create needs a non-empty goal and at least one task title\n"),
67
+ modelNote: "plan.create failed: provide a string goal and a non-empty tasks array of step titles.",
68
+ };
69
+ }
70
+ // Reject a low-quality "everything in one step" plan. A single task that
71
+ // itself enumerates many files/actions (commas, "and", slashes) is a sign
72
+ // the model lumped the whole build into one checkbox — split it so the
73
+ // user gets a real, trackable checklist and the executor works step by step.
74
+ if (isLumpedSingleTask(taskTitles)) {
75
+ return {
76
+ handled: true,
77
+ ok: false,
78
+ display: chalk.red(" ✗ plan.create: that single task lumps the whole build into one step\n"),
79
+ modelNote: "plan.create rejected: you put everything into ONE task. Break it into 3-8 SEPARATE, " +
80
+ "ordered tasks — each a distinct action, e.g. 'scaffold package.json + vite config', " +
81
+ "'create index.html + entry (main.jsx)', 'build App + Post components', 'add posts data + styles', " +
82
+ "'install deps and run dev server to verify'. Call plan.create again with that tasks array.",
83
+ };
84
+ }
85
+ const plan = createPlan({
86
+ sessionId: session.sessionId,
87
+ goal,
88
+ detail,
89
+ taskTitles,
90
+ kind,
91
+ });
92
+ await savePlan(plan).catch(() => undefined);
93
+ // A freshly (re)created plan resets approval — the user must /implement.
94
+ session.planApproved.value = false;
95
+ const checklist = renderPlanForTerminal(plan);
96
+ const display = chalk.cyan(" ● planning\n") +
97
+ checklist +
98
+ "\n" +
99
+ chalk.dim(" ✦ plan created — press Ctrl+P to view it, /implement to approve and run it,\n" +
100
+ " or /discard to cancel it. Any other message refines this plan.\n");
101
+ return {
102
+ handled: true,
103
+ ok: true,
104
+ plan,
105
+ display,
106
+ modelNote: `Plan saved with ${plan.tasks.length} task(s). STOP here and wait — produce NO other tool calls now. ` +
107
+ "Do NOT start executing tasks until the user approves with /implement. " +
108
+ "If the user's next message gives feedback instead of /implement, that is a REVISION: call plan.create " +
109
+ "again with the updated plan and STOP again. The user may cancel the whole plan with /discard. " +
110
+ "Only after /implement do you begin, working task by task, calling task.update to mark each " +
111
+ "in_progress before and done after you finish it.",
112
+ };
113
+ }
114
+ // task.update
115
+ const plan = await loadPlan(session.sessionId).catch(() => undefined);
116
+ if (!plan) {
117
+ return {
118
+ handled: true,
119
+ ok: false,
120
+ display: chalk.red(" ✗ task.update: no active plan — call plan.create first\n"),
121
+ modelNote: "task.update failed: there is no active plan. Call plan.create first.",
122
+ };
123
+ }
124
+ const taskId = typeof call.args.taskId === "string" ? call.args.taskId : "";
125
+ const stateRaw = typeof call.args.state === "string" ? call.args.state : "";
126
+ const note = typeof call.args.note === "string" ? call.args.note : undefined;
127
+ const validStates = [
128
+ "pending",
129
+ "in_progress",
130
+ "done",
131
+ "failed",
132
+ "skipped",
133
+ ];
134
+ if (!validStates.includes(stateRaw)) {
135
+ return {
136
+ handled: true,
137
+ ok: false,
138
+ display: chalk.red(` ✗ task.update: state must be one of ${validStates.join(", ")}\n`),
139
+ modelNote: `task.update failed: state must be one of ${validStates.join(", ")}.`,
140
+ };
141
+ }
142
+ // Only one task may be in_progress at a time. This forces genuine
143
+ // task-by-task execution: the model must close (done/failed/skipped) the
144
+ // current task before opening the next one, instead of leaving a task
145
+ // "in_progress" as an umbrella while it quietly works through the rest
146
+ // of the plan underneath it.
147
+ if (stateRaw === "in_progress") {
148
+ const otherInProgress = plan.tasks.find((t) => t.id !== taskId && t.state === "in_progress");
149
+ if (otherInProgress) {
150
+ return {
151
+ handled: true,
152
+ ok: false,
153
+ display: chalk.red(` \u2717 task.update: task [${otherInProgress.id}] "${otherInProgress.title}" is still in_progress\n`),
154
+ modelNote: `task.update failed: task [${otherInProgress.id}] "${otherInProgress.title}" is still in_progress. ` +
155
+ "Finish it first \u2014 call task.update with state 'done' (or 'failed'/'skipped' with a note) " +
156
+ `for [${otherInProgress.id}] before starting [${taskId}].`,
157
+ };
158
+ }
159
+ }
160
+ const ok = markTask(plan, taskId, stateRaw, note);
161
+ if (!ok) {
162
+ const ids = plan.tasks.map((t) => t.id).join(", ");
163
+ return {
164
+ handled: true,
165
+ ok: false,
166
+ display: chalk.red(` ✗ task.update: unknown taskId "${taskId}" (have: ${ids})\n`),
167
+ modelNote: `task.update failed: unknown taskId. Valid ids: ${ids}.`,
168
+ };
169
+ }
170
+ if (plan.status === "draft" || plan.status === "approved") {
171
+ plan.status = "in_progress";
172
+ }
173
+ const allDone = plan.tasks.every((t) => t.state === "done" || t.state === "skipped" || t.state === "failed");
174
+ if (allDone)
175
+ plan.status = "completed";
176
+ await savePlan(plan).catch(() => undefined);
177
+ const checklist = renderPlanForTerminal(plan);
178
+ return {
179
+ handled: true,
180
+ ok: true,
181
+ plan,
182
+ display: checklist + "\n",
183
+ modelNote: allDone
184
+ ? "Task updated. ALL tasks are now finished. Verify the result and give your final summary."
185
+ : "Task updated. Continue with the next pending task.",
186
+ };
187
+ }
188
+ //# sourceMappingURL=plan-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-tool.js","sourceRoot":"","sources":["../../src/agent/plan-tool.ts"],"names":[],"mappings":"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"}
@@ -1,35 +1,11 @@
1
1
  import type { ChatMessage, ChatImage, ProviderId, ToolCall, ToolResult } from "../types.js";
2
2
  import type { AgentEvent } from "./events.js";
3
- export interface SessionPolicy {
4
- /** Tools the user authorized once during this REPL session. Not persisted. */
5
- allow: Set<string>;
6
- /** Mutable flag so the runner can flip pentest auth for this session only. */
7
- pentestAuthorized: {
8
- value: boolean;
9
- };
10
- /** Stable id used to scope the session's plan/tasks in the plan store. */
11
- sessionId: string;
12
- /** When true, the agent must follow its approved plan (set by /implement). */
13
- planApproved: {
14
- value: boolean;
15
- };
16
- }
17
- export declare function createSessionPolicy(sessionId?: string): SessionPolicy;
18
- export interface ConfirmPort {
19
- confirmTool(call: ToolCall): Promise<boolean>;
20
- confirmPentest(): Promise<boolean>;
21
- /** Ask the user whether to continue after hitting the step budget. */
22
- confirmContinue?(steps: number): Promise<boolean>;
23
- /**
24
- * Ask whether to leave ask mode and run an action task in agent mode.
25
- * Optional so existing ports keep working; ask-mode handoff falls back to a
26
- * default "no" when a port doesn't implement it.
27
- */
28
- confirmAgentSwitch?(info: {
29
- reason: string;
30
- tools: string[];
31
- }): Promise<boolean>;
32
- }
3
+ import { type SessionPolicy } from "./session-policy.js";
4
+ import { type ConfirmPort } from "./confirm-port.js";
5
+ export * from "./tool-call-parser.js";
6
+ export { createSessionPolicy, isPreApprovalAllowedTool, shouldEnableImageOcr, type SessionPolicy, } from "./session-policy.js";
7
+ export { type ConfirmPort } from "./confirm-port.js";
8
+ export declare function styleToolChatter(call: ToolCall, text: string): string;
33
9
  export interface AgentRunOptions {
34
10
  provider?: ProviderId | undefined;
35
11
  model?: string | undefined;
@@ -41,6 +17,14 @@ export interface AgentRunOptions {
41
17
  onToolStart?: ((call: ToolCall) => void) | undefined;
42
18
  onToolResult?: ((call: ToolCall, result: ToolResult) => void) | undefined;
43
19
  onEvent?: ((event: AgentEvent) => void) | undefined;
20
+ /**
21
+ * Called when a turn ends with the FULL conversation for the turn — the user
22
+ * message, every assistant tool-call, every tool result, and the final
23
+ * answer (system prompts excluded). Callers persist this so a resumed
24
+ * session gives the model back what it actually did (commands, outputs,
25
+ * results), not just its prose answers.
26
+ */
27
+ onMessages?: ((messages: ChatMessage[]) => void) | undefined;
44
28
  confirm?: ConfirmPort | undefined;
45
29
  requestSecret?: ((request: {
46
30
  title: string;
@@ -48,139 +32,4 @@ export interface AgentRunOptions {
48
32
  }) => Promise<string | undefined>) | undefined;
49
33
  session?: SessionPolicy | undefined;
50
34
  }
51
- export declare function preprocessJson(raw: string): string;
52
- export interface ParseToolCallOptions {
53
- /**
54
- * When true, only formats that are explicitly tool-call delimited are
55
- * accepted: ```tool fenced JSON, <tool_call> XML, and the Kimi sentinel
56
- * token format. Loose formats (any fenced block, heading-prefix, trailing
57
- * JSON) are dropped — useful when models routinely emit JSON examples in
58
- * prose. Default is `false` so existing free-tier models keep working.
59
- */
60
- strict?: boolean | undefined;
61
- }
62
- export declare function parseToolCall(text: string, options?: ParseToolCallOptions): ToolCall | undefined;
63
- /**
64
- * When a model emits a bare args object with no {"name", "args"} wrapper and
65
- * no ```tool fence, infer which tool it MEANT from the argument keys so we
66
- * can run it directly instead of nudging the model to re-emit (the user
67
- * should not have to type "run"). Only unambiguous key signatures map to a
68
- * tool; genuinely ambiguous shapes (a lone `path` could be fs.read / fs.list
69
- * / pdf.read / image.ocr; a lone `target` could be whois / dns / scan) return
70
- * undefined so the caller falls back to a re-emit nudge. Inferred calls still
71
- * pass through the normal safety classifier + confirmation, so inference can
72
- * never bypass a confirm/block gate.
73
- */
74
- export declare function inferToolFromArgs(obj: Record<string, unknown>): string | undefined;
75
- /**
76
- * When a model means to call a tool but emits ONLY a bare JSON object —
77
- * either a proper {"name","args"} that the strict matchers missed, or a bare
78
- * args object like {"path":"file.pdf"} with the wrapper/fence dropped — this
79
- * recognizes it. Returns:
80
- * - { call } when the object is a complete {name, args} tool call, or
81
- * - { argsOnly: true } when it looks like a bare args object (so the caller
82
- * can nudge the model to re-emit a properly named, fenced tool call).
83
- * Returns undefined for anything that is plainly a normal prose/JSON answer.
84
- *
85
- * Also handles the case where a model emits prose followed by a non-`tool`
86
- * fenced code block (e.g. ```web\n{"url":"..."}\n```) that contains a bare
87
- * args object — the fence is scanned even when it's not the sole content.
88
- */
89
- export declare function recognizeBareToolJson(text: string): {
90
- call?: ToolCall;
91
- argsOnly?: boolean;
92
- } | undefined;
93
- /**
94
- * Detect an opened-but-unparseable tool call. This happens when the model's
95
- * output is truncated by the token limit mid-JSON: we see the ```tool fence
96
- * (or a bare {"name":"...","args" prefix) open, but parseToolCall returns
97
- * undefined because the JSON never closed. Without this, the broken block
98
- * leaks to the screen as a "final answer" and the requested action (e.g. a
99
- * multi-file fs.writeMany scaffold) silently never runs.
100
- */
101
- export declare function looksLikeTruncatedToolCall(text: string): boolean;
102
- /**
103
- * Count the number of ```tool fenced blocks in a message. Models sometimes
104
- * emit MULTIPLE tool calls in one response (e.g. fs.writeMany + npm install +
105
- * npm run dev). Only the FIRST is parsed and executed; the rest are silently
106
- * dropped and leak to the screen as code fences, while the model believes it
107
- * ran all of them — a major cause of "everything is done" fabrications. We
108
- * detect this so the runner can run the first and explicitly tell the model
109
- * the others did NOT run and must be re-sent one at a time.
110
- */
111
- export declare function countToolFences(text: string): number;
112
- /**
113
- * Parse EVERY explicitly-delimited tool call in a message, in document
114
- * order. Unlike parseToolCall (which returns only the first), this lets the
115
- * runner execute a batch the model emitted in one turn — e.g. the natural
116
- * "task.update in_progress → do the work → task.update done" sequence, or
117
- * several fs.write calls. Only the unambiguous, delimited formats are
118
- * collected (```tool fences, <tool_call> XML, and Kimi sentinel blocks) so a
119
- * worked example in prose is far less likely to be mistaken for a call.
120
- * The runner executes them sequentially and STOPS the batch on the first
121
- * failure so the model can react, mirroring how Claude Code batches reads
122
- * and edits but pauses when something breaks.
123
- */
124
- export declare function parseAllToolCalls(text: string): ToolCall[];
125
- /** Structural equality for two tool calls (name + canonical args JSON). */
126
- export declare function sameToolCall(a: ToolCall, b: ToolCall): boolean;
127
- /**
128
- * Partition a batch of tool calls (in document order) into execution groups.
129
- * A run of consecutive parallel-safe calls forms one group to be run
130
- * concurrently (bounded by maxGroupSize); every non-parallel-safe call is its
131
- * own single-element group, i.e. a sequential barrier. Because plan updates
132
- * and side-effecting tools are never parallel-safe, they always split the
133
- * batch — which keeps parallelism scoped within a single task and prevents
134
- * plan-state races and overlapping writes.
135
- */
136
- export declare function groupToolCallsForExecution(calls: ToolCall[], isParallelSafe: (call: ToolCall) => boolean, maxGroupSize?: number): ToolCall[][];
137
- /**
138
- * Collapse pathological repetition before a message is stored in history.
139
- * Some models degenerate into emitting the same short phrase hundreds of
140
- * times ("We need to wait.We need to wait.…"), which otherwise bloats the
141
- * context window and wastes tokens on every subsequent turn. We keep a few
142
- * copies and note the collapse so the meaning is preserved without the bulk.
143
- */
144
- export declare function collapseRepeatedText(text: string): string;
145
- export declare function formatToolArgs(call: ToolCall): string;
146
- /**
147
- * Detect pentest/security tasks that need the full step budget.
148
- * Mirrors looksLikeBuildTask but for security work.
149
- */
150
- export declare function looksLikePentestTask(prompt: string, history?: ChatMessage[] | undefined): boolean;
151
- /**
152
- * Decide whether this turn should get the build workflow (explore → plan →
153
- * implement) and a generous step budget. Looks at the current prompt first,
154
- * then falls back to recent USER turns so a terse follow-up inherits an
155
- * ongoing build — but NOT the agent's own (possibly mistaken) plan narration.
156
- */
157
- export declare function looksLikeBuildTask(prompt: string, history?: ChatMessage[] | undefined): boolean;
158
- /**
159
- * Detect a message that narrates an *upcoming* action ("let me explore the
160
- * directory", "I'll create the components") rather than an actual answer or
161
- * tool call. Used to catch models that describe intent but emit no tool call,
162
- * which would otherwise end the turn with nothing done. A real completion
163
- * summary (past tense, longer, or containing a code block) is NOT flagged.
164
- */
165
- export declare function looksLikeActionNarration(text: string): boolean;
166
- /**
167
- * Detect a message that narrates a PLAN as prose ("Goal: … Tasks: 1. … Please
168
- * approve the plan") instead of calling plan.create. Such a turn leaves no
169
- * real plan, so the user can't /implement it — we nudge the model to emit the
170
- * plan.create tool call instead.
171
- */
172
- export declare function looksLikePlanNarration(text: string): boolean;
173
- export declare function requiresFreshWebSearch(prompt: string): boolean;
174
- /**
175
- * Detect a low-quality "everything in one step" plan task. A single task that
176
- * itself enumerates many files/actions (multiple commas, an "and", several
177
- * slashes, or an overlong title) means the model lumped the whole build into
178
- * one checkbox instead of producing a real ordered checklist.
179
- */
180
- export declare function isLumpedSingleTask(taskTitles: string[]): boolean;
181
- export declare function shouldDimToolChatter(call: ToolCall): boolean;
182
- export declare function isPreApprovalAllowedTool(name: string): boolean;
183
- export declare function styleToolChatter(call: ToolCall, text: string): string;
184
- /** OCR is opt-in when real image pixels are already attached to the model. */
185
- export declare function shouldEnableImageOcr(prompt: string, hasAttachedImages: boolean): boolean;
186
35
  export declare function runAgentLoop(prompt: string, options?: AgentRunOptions): Promise<string>;