agent-sh 0.7.0 → 0.9.0

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 (86) hide show
  1. package/README.md +28 -33
  2. package/dist/agent/agent-loop.d.ts +31 -8
  3. package/dist/agent/agent-loop.js +277 -66
  4. package/dist/agent/conversation-state.d.ts +41 -9
  5. package/dist/agent/conversation-state.js +340 -17
  6. package/dist/agent/history-file.d.ts +36 -0
  7. package/dist/agent/history-file.js +167 -0
  8. package/dist/agent/nuclear-form.d.ts +41 -0
  9. package/dist/agent/nuclear-form.js +176 -0
  10. package/dist/agent/system-prompt.d.ts +4 -5
  11. package/dist/agent/system-prompt.js +16 -11
  12. package/dist/agent/token-budget.d.ts +13 -0
  13. package/dist/agent/token-budget.js +50 -0
  14. package/dist/agent/tool-protocol.d.ts +83 -0
  15. package/dist/agent/tool-protocol.js +386 -0
  16. package/dist/agent/tools/user-shell.js +4 -1
  17. package/dist/agent/types.d.ts +21 -1
  18. package/dist/context-manager.d.ts +0 -1
  19. package/dist/context-manager.js +5 -110
  20. package/dist/core.d.ts +7 -7
  21. package/dist/core.js +76 -180
  22. package/dist/event-bus.d.ts +40 -0
  23. package/dist/event-bus.js +20 -1
  24. package/dist/extension-loader.d.ts +5 -0
  25. package/dist/extension-loader.js +104 -17
  26. package/dist/extensions/agent-backend.d.ts +13 -0
  27. package/dist/extensions/agent-backend.js +167 -0
  28. package/dist/extensions/command-suggest.d.ts +3 -3
  29. package/dist/extensions/command-suggest.js +4 -3
  30. package/dist/extensions/index.d.ts +19 -0
  31. package/dist/extensions/index.js +25 -0
  32. package/dist/extensions/slash-commands.d.ts +1 -1
  33. package/dist/extensions/slash-commands.js +44 -1
  34. package/dist/extensions/terminal-buffer.d.ts +1 -1
  35. package/dist/extensions/terminal-buffer.js +22 -8
  36. package/dist/extensions/tui-renderer.js +177 -122
  37. package/dist/index.js +14 -20
  38. package/dist/settings.d.ts +25 -2
  39. package/dist/settings.js +25 -4
  40. package/dist/{input-handler.d.ts → shell/input-handler.d.ts} +1 -1
  41. package/dist/{input-handler.js → shell/input-handler.js} +60 -43
  42. package/dist/{output-parser.d.ts → shell/output-parser.d.ts} +1 -1
  43. package/dist/{output-parser.js → shell/output-parser.js} +1 -1
  44. package/dist/{shell.d.ts → shell/shell.d.ts} +8 -2
  45. package/dist/{shell.js → shell/shell.js} +24 -6
  46. package/dist/types.d.ts +49 -32
  47. package/dist/utils/ansi.d.ts +10 -0
  48. package/dist/utils/ansi.js +27 -0
  49. package/dist/utils/compositor.d.ts +62 -0
  50. package/dist/utils/compositor.js +88 -0
  51. package/dist/utils/diff-renderer.js +92 -4
  52. package/dist/utils/floating-panel.d.ts +34 -3
  53. package/dist/utils/floating-panel.js +315 -82
  54. package/dist/utils/handler-registry.d.ts +26 -10
  55. package/dist/utils/handler-registry.js +52 -16
  56. package/dist/utils/line-editor.d.ts +32 -3
  57. package/dist/utils/line-editor.js +218 -36
  58. package/dist/utils/markdown.d.ts +1 -0
  59. package/dist/utils/markdown.js +4 -4
  60. package/dist/utils/message-utils.d.ts +35 -0
  61. package/dist/utils/message-utils.js +75 -0
  62. package/dist/utils/terminal-buffer.d.ts +9 -1
  63. package/dist/utils/terminal-buffer.js +31 -2
  64. package/dist/utils/tool-display.d.ts +1 -0
  65. package/dist/utils/tool-display.js +1 -1
  66. package/dist/utils/tool-interactive.d.ts +12 -0
  67. package/dist/utils/tool-interactive.js +53 -0
  68. package/examples/extensions/ash-acp-bridge/README.md +39 -0
  69. package/examples/extensions/ash-acp-bridge/package.json +23 -0
  70. package/examples/extensions/ash-acp-bridge/src/index.ts +571 -0
  71. package/examples/extensions/ash-acp-bridge/tsconfig.json +14 -0
  72. package/examples/extensions/ash-mcp-bridge/README.md +72 -0
  73. package/examples/extensions/ash-mcp-bridge/index.ts +154 -0
  74. package/examples/extensions/ash-mcp-bridge/package.json +9 -0
  75. package/examples/extensions/claude-code-bridge/index.ts +77 -1
  76. package/examples/extensions/interactive-prompts.ts +82 -110
  77. package/examples/extensions/overlay-agent.ts +84 -38
  78. package/examples/extensions/peer-mesh.ts +450 -0
  79. package/examples/extensions/pi-bridge/index.ts +87 -2
  80. package/examples/extensions/questionnaire.ts +249 -0
  81. package/examples/extensions/tmux-pane.ts +307 -0
  82. package/examples/extensions/web-access.ts +327 -0
  83. package/package.json +9 -1
  84. package/dist/extensions/overlay-agent.d.ts +0 -11
  85. package/dist/extensions/overlay-agent.js +0 -43
  86. package/examples/extensions/terminal-buffer.ts +0 -184
@@ -0,0 +1,176 @@
1
+ // ── Tool classification ───────────────────────────────────────────
2
+ /** Read-only tools whose results are dropped at Tier 1→2 (agent can re-read). */
3
+ export const READ_ONLY_TOOLS = new Set([
4
+ "read_file", "grep", "glob", "ls", "search",
5
+ ]);
6
+ /** State-changing tools whose summaries are kept in nuclear memory. */
7
+ export const WRITE_TOOLS = new Set([
8
+ "write_file", "edit_file", "write", "edit", "patch",
9
+ ]);
10
+ // ── Nuclear entry generation ──────────────────────────────────────
11
+ /**
12
+ * Generate nuclear entries from a logical turn (a sequence of messages
13
+ * starting with a user message, followed by assistant + tool messages).
14
+ */
15
+ export function toNuclearEntries(messages, startSeq, instanceId) {
16
+ const entries = [];
17
+ let seq = startSeq;
18
+ const ts = Date.now();
19
+ for (const msg of messages) {
20
+ if (msg.role === "user") {
21
+ const text = typeof msg.content === "string" ? msg.content : "";
22
+ // Skip compaction markers
23
+ if (text.startsWith("["))
24
+ continue;
25
+ entries.push({
26
+ seq: seq++, ts, iid: instanceId,
27
+ kind: "user",
28
+ sum: `user: "${truncate(text, 80)}"`,
29
+ });
30
+ }
31
+ else if (msg.role === "assistant") {
32
+ // Process tool calls
33
+ if ("tool_calls" in msg && msg.tool_calls) {
34
+ for (const tc of msg.tool_calls) {
35
+ if (!("function" in tc))
36
+ continue;
37
+ const name = tc.function.name;
38
+ let args = {};
39
+ try {
40
+ args = JSON.parse(tc.function.arguments);
41
+ }
42
+ catch { }
43
+ // Store the tool call — we'll enrich it when we see the result
44
+ entries.push({
45
+ seq: seq++, ts, iid: instanceId,
46
+ kind: "tool",
47
+ tool: name,
48
+ sum: summarizeToolCall(name, args),
49
+ });
50
+ }
51
+ }
52
+ else if (typeof msg.content === "string" && msg.content) {
53
+ entries.push({
54
+ seq: seq++, ts, iid: instanceId,
55
+ kind: "agent",
56
+ sum: `agent: "${truncate(msg.content, 60)}"`,
57
+ });
58
+ }
59
+ }
60
+ else if (msg.role === "tool") {
61
+ // Enrich the most recent tool entry with result info
62
+ const content = typeof msg.content === "string" ? msg.content : "";
63
+ const lastTool = findLastTool(entries);
64
+ if (lastTool) {
65
+ const isError = content.startsWith("Error:");
66
+ if (isError) {
67
+ lastTool.kind = "error";
68
+ lastTool.sum = `error: ${lastTool.tool} ${truncate(content.slice(7).trim(), 80)}`;
69
+ }
70
+ else {
71
+ lastTool.sum = enrichWithResult(lastTool.tool ?? "", lastTool.sum, content);
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return entries;
77
+ }
78
+ // ── Formatting ────────────────────────────────────────────────────
79
+ /** Format a nuclear entry as a display line (for in-context injection). */
80
+ export function formatNuclearLine(entry) {
81
+ const d = new Date(entry.ts);
82
+ const pad = (n) => String(n).padStart(2, "0");
83
+ // ISO-ish compact: 2026-04-13 14:05
84
+ const stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
85
+ return `#${entry.seq} [${stamp}] ${entry.sum}`;
86
+ }
87
+ // ── Serialization (JSONL for history file) ────────────────────────
88
+ /** Serialize a nuclear entry to a JSONL line. */
89
+ export function serializeEntry(entry) {
90
+ return JSON.stringify(entry);
91
+ }
92
+ /** Deserialize a JSONL line to a nuclear entry. Returns null on parse failure. */
93
+ export function deserializeEntry(line) {
94
+ try {
95
+ const obj = JSON.parse(line);
96
+ if (typeof obj.seq === "number" && typeof obj.sum === "string") {
97
+ return obj;
98
+ }
99
+ return null;
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ // ── Classification helpers ────────────────────────────────────────
106
+ /** Check if a nuclear entry represents a read-only action (should be dropped). */
107
+ export function isReadOnly(entry) {
108
+ return entry.kind === "tool" && entry.tool != null && READ_ONLY_TOOLS.has(entry.tool);
109
+ }
110
+ // ── Internal helpers ──────────────────────────────────────────────
111
+ function truncate(text, maxLen) {
112
+ const oneLine = text.replace(/\n/g, " ").trim();
113
+ return oneLine.length > maxLen ? oneLine.slice(0, maxLen) + "..." : oneLine;
114
+ }
115
+ function findLastTool(entries) {
116
+ for (let i = entries.length - 1; i >= 0; i--) {
117
+ if (entries[i].kind === "tool")
118
+ return entries[i];
119
+ }
120
+ return undefined;
121
+ }
122
+ function summarizeToolCall(name, args) {
123
+ switch (name) {
124
+ case "bash":
125
+ return `bash: ${truncate(String(args.command ?? ""), 60)}`;
126
+ case "user_shell":
127
+ return `user_shell: ${truncate(String(args.command ?? ""), 60)}`;
128
+ case "edit_file":
129
+ return `edit_file ${args.path ?? ""}`;
130
+ case "write_file":
131
+ case "write":
132
+ return `write_file ${args.path ?? args.file_path ?? ""}`;
133
+ case "read_file":
134
+ return `read_file ${args.path ?? args.file_path ?? ""}`;
135
+ case "grep":
136
+ return `grep "${truncate(String(args.pattern ?? ""), 30)}"`;
137
+ case "glob":
138
+ return `glob ${args.pattern ?? ""}`;
139
+ case "ls":
140
+ return `ls ${args.path ?? "."}`;
141
+ case "display":
142
+ return `display: ${truncate(String(args.command ?? ""), 60)}`;
143
+ default:
144
+ return `${name}`;
145
+ }
146
+ }
147
+ function enrichWithResult(toolName, summary, result) {
148
+ const lines = result.split("\n");
149
+ const lineCount = lines.length;
150
+ switch (toolName) {
151
+ case "bash":
152
+ case "user_shell": {
153
+ // Extract exit code from result if present
154
+ const exitMatch = result.match(/exit code[:\s]*(\d+)/i) ?? result.match(/exit\s+(\d+)/);
155
+ const exitCode = exitMatch ? exitMatch[1] : "0";
156
+ return `${summary} (exit ${exitCode}, ${lineCount} lines)`;
157
+ }
158
+ case "edit_file":
159
+ case "edit": {
160
+ // Try to extract +/- counts from result
161
+ const addMatch = result.match(/\+(\d+)/);
162
+ const delMatch = result.match(/-(\d+)/);
163
+ if (addMatch || delMatch) {
164
+ return `${summary} (+${addMatch?.[1] ?? 0}/-${delMatch?.[1] ?? 0})`;
165
+ }
166
+ return `${summary} (edited)`;
167
+ }
168
+ case "write_file":
169
+ case "write": {
170
+ const created = result.toLowerCase().includes("created") ? "created" : "written";
171
+ return `${summary} (${created}, ${lineCount} lines)`;
172
+ }
173
+ default:
174
+ return `${summary} (${lineCount} lines)`;
175
+ }
176
+ }
@@ -1,14 +1,13 @@
1
- import type { ToolDefinition } from "./types.js";
2
1
  import type { ContextManager } from "../context-manager.js";
3
2
  /**
4
3
  * Static system prompt — identical across all queries, cacheable.
5
4
  * Contains only identity and behavioral instructions.
6
5
  */
7
- export declare const STATIC_SYSTEM_PROMPT = "You are an AI coding assistant embedded in agent-sh, a terminal shell.\nYou have access to the user's shell environment and can read, write, and execute code.\nYou share the user's working directory, environment variables, and shell history.\n\n# Tool Decision Guide\n\nYou have three categories of tools \u2014 choose based on who needs the output and\nwhether the command has lasting effects:\n\n**Scratchpad tools** (bash, read_file, grep, glob, ls, edit_file, write_file):\nUse these to investigate, search, read, and modify files. Output is returned\nto you for reasoning \u2014 the user doesn't see it directly.\n\n**Display** (display):\nUse this to show output to the user in their terminal. The user sees the\noutput directly, but it is NOT returned to you. Use when:\n- The user asks to see something (cat a file, git log, git diff, man page)\n- The output is for the user to read, not for you to process\n\n**Live shell** (user_shell):\nUse this to run commands with lasting effects in the user's real shell. Use for:\n- Commands that affect shell state (cd, export, source)\n- Installing packages, starting servers, running builds\n- Any command where the user wants real side effects\n- Set return_output=true only if you need to inspect the result\n\nDefault to scratchpad tools for your own investigation. Use display when the\nuser is the intended audience. Use user_shell when the command has real effects.\n\n# Tool Usage Guidelines\n- Use read_file before editing a file you haven't seen\n- Prefer edit_file over write_file for modifying existing files\n- Use grep/glob to find files before reading them\n- Keep bash commands focused; avoid long-running blocking commands\n- Always check command exit codes for errors";
6
+ export declare const STATIC_SYSTEM_PROMPT: string;
8
7
  /**
9
8
  * Build the dynamic context — injected as a user message before each query.
10
- * Contains everything that changes: tools, shell context, conventions, cwd.
9
+ * Contains everything that changes: shell context, conventions, cwd.
11
10
  *
12
- * Runs through the "agent:dynamic-context" pipe so extensions can append.
11
+ * Runs through the "dynamic-context:build" handler so extensions can advise.
13
12
  */
14
- export declare function buildDynamicContext(tools: ToolDefinition[], contextManager: ContextManager): string;
13
+ export declare function buildDynamicContext(contextManager: ContextManager, shellBudgetTokens?: number): string;
@@ -1,6 +1,9 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
  import { discoverSkills } from "./skills.js";
5
+ /** Resolve the absolute path to agent-sh's own docs directory. */
6
+ const DOCS_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../docs");
4
7
  /** File names to scan for project conventions (checked in order). */
5
8
  const CONVENTION_FILES = ["CLAUDE.md", "AGENT.md"];
6
9
  /**
@@ -36,7 +39,7 @@ function loadConventionFiles(dir) {
36
39
  * Static system prompt — identical across all queries, cacheable.
37
40
  * Contains only identity and behavioral instructions.
38
41
  */
39
- export const STATIC_SYSTEM_PROMPT = `You are an AI coding assistant embedded in agent-sh, a terminal shell.
42
+ export const STATIC_SYSTEM_PROMPT = `You are ash, an AI coding assistant embedded in agent-sh, a terminal shell.
40
43
  You have access to the user's shell environment and can read, write, and execute code.
41
44
  You share the user's working directory, environment variables, and shell history.
42
45
 
@@ -56,7 +59,7 @@ output directly, but it is NOT returned to you. Use when:
56
59
  - The output is for the user to read, not for you to process
57
60
 
58
61
  **Live shell** (user_shell):
59
- Use this to run commands with lasting effects in the user's real shell. Use for:
62
+ Use this to run complete, non-interactive commands in the user's real shell. Use for:
60
63
  - Commands that affect shell state (cd, export, source)
61
64
  - Installing packages, starting servers, running builds
62
65
  - Any command where the user wants real side effects
@@ -70,18 +73,19 @@ user is the intended audience. Use user_shell when the command has real effects.
70
73
  - Prefer edit_file over write_file for modifying existing files
71
74
  - Use grep/glob to find files before reading them
72
75
  - Keep bash commands focused; avoid long-running blocking commands
73
- - Always check command exit codes for errors`;
76
+ - Always check command exit codes for errors
77
+
78
+ # Documentation
79
+ agent-sh documentation is available in: ${DOCS_DIR}
80
+ Use read_file on ${DOCS_DIR}/README.md for an index of all docs.`;
74
81
  /**
75
82
  * Build the dynamic context — injected as a user message before each query.
76
- * Contains everything that changes: tools, shell context, conventions, cwd.
83
+ * Contains everything that changes: shell context, conventions, cwd.
77
84
  *
78
- * Runs through the "agent:dynamic-context" pipe so extensions can append.
85
+ * Runs through the "dynamic-context:build" handler so extensions can advise.
79
86
  */
80
- export function buildDynamicContext(tools, contextManager) {
87
+ export function buildDynamicContext(contextManager, shellBudgetTokens) {
81
88
  const sections = [];
82
- // Tools
83
- sections.push("# Available Tools\n" +
84
- tools.map((t) => `- ${t.name}: ${t.description}`).join("\n"));
85
89
  // Project conventions (CLAUDE.md / AGENT.md)
86
90
  const conventions = loadConventionFiles(contextManager.getCwd());
87
91
  if (conventions.length > 0) {
@@ -92,8 +96,9 @@ export function buildDynamicContext(tools, contextManager) {
92
96
  if (skills.length > 0) {
93
97
  sections.push(`You have access to ${skills.length} skill(s). Use the list_skills tool to see them, then read_file to load one.`);
94
98
  }
95
- // Shell context
96
- const shellContext = contextManager.getContext();
99
+ // Shell context — pass token budget converted to bytes (~4 chars/token)
100
+ const shellBudgetBytes = shellBudgetTokens != null ? shellBudgetTokens * 4 : undefined;
101
+ const shellContext = contextManager.getContext(shellBudgetBytes);
97
102
  if (shellContext) {
98
103
  sections.push(shellContext);
99
104
  }
@@ -0,0 +1,13 @@
1
+ export declare class TokenBudget {
2
+ private contextWindow;
3
+ private toolCount;
4
+ constructor(contextWindow?: number, toolCount?: number);
5
+ /** Update when model or tool set changes. */
6
+ update(contextWindow?: number, toolCount?: number): void;
7
+ /** Total tokens available for shell context + conversation content. */
8
+ get contentBudget(): number;
9
+ /** Token budget for the shell context stream. */
10
+ get shellBudgetTokens(): number;
11
+ /** Token budget for the conversation messages stream. */
12
+ get conversationBudgetTokens(): number;
13
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Unified token budget manager.
3
+ *
4
+ * Splits a model's context window between two streams:
5
+ * - Shell context (user shell commands and outputs — situational awareness)
6
+ * - Conversation (agent messages and tool results — task continuity)
7
+ *
8
+ * The budget accounts for fixed overhead (system prompt, tool definitions,
9
+ * response reserve) and divides the remaining space by a configurable ratio.
10
+ */
11
+ import { getSettings } from "../settings.js";
12
+ /** Overhead estimates (tokens). */
13
+ const SYSTEM_PROMPT_OVERHEAD = 800;
14
+ const DYNAMIC_CONTEXT_OVERHEAD = 500; // conventions, metadata, skills list
15
+ const TOKENS_PER_TOOL_DEFINITION = 50;
16
+ const RESPONSE_RESERVE = 8192; // matches llm-client.ts default max_tokens
17
+ /** Fallback when contextWindow is unknown. */
18
+ const DEFAULT_CONTEXT_WINDOW = 60_000;
19
+ export class TokenBudget {
20
+ contextWindow;
21
+ toolCount;
22
+ constructor(contextWindow, toolCount = 0) {
23
+ this.contextWindow = contextWindow ?? DEFAULT_CONTEXT_WINDOW;
24
+ this.toolCount = toolCount;
25
+ }
26
+ /** Update when model or tool set changes. */
27
+ update(contextWindow, toolCount) {
28
+ if (contextWindow != null)
29
+ this.contextWindow = contextWindow;
30
+ if (toolCount != null)
31
+ this.toolCount = toolCount;
32
+ }
33
+ /** Total tokens available for shell context + conversation content. */
34
+ get contentBudget() {
35
+ const overhead = SYSTEM_PROMPT_OVERHEAD +
36
+ DYNAMIC_CONTEXT_OVERHEAD +
37
+ this.toolCount * TOKENS_PER_TOOL_DEFINITION +
38
+ RESPONSE_RESERVE;
39
+ return Math.max(0, this.contextWindow - overhead);
40
+ }
41
+ /** Token budget for the shell context stream. */
42
+ get shellBudgetTokens() {
43
+ const ratio = getSettings().shellContextRatio;
44
+ return Math.floor(this.contentBudget * ratio);
45
+ }
46
+ /** Token budget for the conversation messages stream. */
47
+ get conversationBudgetTokens() {
48
+ return this.contentBudget - this.shellBudgetTokens;
49
+ }
50
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ToolProtocol — abstracts how tools are presented to the LLM and how
3
+ * tool calls are parsed from responses.
4
+ *
5
+ * Two modes:
6
+ * "api" — tools sent via OpenAI tools param, parsed from delta.tool_calls
7
+ * "inline" — tools described as text, tool calls are JSON code blocks
8
+ *
9
+ * The agent loop uses this interface uniformly so the rest of the code
10
+ * doesn't need to know which mode is active.
11
+ */
12
+ import type { ChatCompletionTool } from "../utils/llm-client.js";
13
+ import type { ToolDefinition } from "./types.js";
14
+ import type { ConversationState } from "./conversation-state.js";
15
+ export interface PendingToolCall {
16
+ id: string;
17
+ name: string;
18
+ argumentsJson: string;
19
+ }
20
+ export interface ToolResult {
21
+ callId: string;
22
+ toolName: string;
23
+ content: string;
24
+ isError: boolean;
25
+ }
26
+ /** Streaming filter — strips tool calls from display output. */
27
+ export interface StreamFilter {
28
+ feed(chunk: string): string;
29
+ flush(): string;
30
+ }
31
+ export interface ToolProtocol {
32
+ readonly mode: string;
33
+ /** Tools to pass in the API request's `tools` parameter. undefined = omit. */
34
+ getApiTools(tools: ToolDefinition[]): ChatCompletionTool[] | undefined;
35
+ /** Extra text for dynamic context (tool catalog for inline mode). */
36
+ getToolPrompt(tools: ToolDefinition[]): string;
37
+ /** Extract tool calls from a completed response. */
38
+ extractToolCalls(responseText: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
39
+ /** Rewrite a tool call before execution (e.g., unwrap meta-tool). */
40
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
41
+ /** Record the assistant turn in conversation state. */
42
+ recordAssistant(conv: ConversationState, text: string, toolCalls: PendingToolCall[]): void;
43
+ /** Record all tool results for a batch as conversation messages. */
44
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
45
+ /** Create a stream filter for stripping tool calls from display. null = pass-through. */
46
+ createStreamFilter(toolNames: string[]): StreamFilter | null;
47
+ }
48
+ export declare class ApiToolProtocol implements ToolProtocol {
49
+ readonly mode: "api";
50
+ getApiTools(tools: ToolDefinition[]): ChatCompletionTool[] | undefined;
51
+ getToolPrompt(): string;
52
+ extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
53
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
54
+ recordAssistant(conv: ConversationState, text: string, toolCalls: PendingToolCall[]): void;
55
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
56
+ createStreamFilter(): null;
57
+ }
58
+ export declare class InlineToolProtocol implements ToolProtocol {
59
+ readonly mode: "inline";
60
+ private callCounter;
61
+ getApiTools(): undefined;
62
+ getToolPrompt(tools: ToolDefinition[]): string;
63
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
64
+ extractToolCalls(text: string, _streamedCalls: PendingToolCall[]): PendingToolCall[];
65
+ recordAssistant(conv: ConversationState, text: string, _toolCalls: PendingToolCall[]): void;
66
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
67
+ createStreamFilter(_toolNames: string[]): StreamFilter;
68
+ }
69
+ export declare class DeferredToolProtocol implements ToolProtocol {
70
+ readonly mode: "deferred";
71
+ private coreNames;
72
+ /** Cached extension tool schemas for arg validation. */
73
+ private extSchemas;
74
+ constructor(coreNames: string[]);
75
+ getApiTools(tools: ToolDefinition[]): ChatCompletionTool[] | undefined;
76
+ getToolPrompt(): string;
77
+ extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
78
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
79
+ recordAssistant(conv: ConversationState, text: string, toolCalls: PendingToolCall[]): void;
80
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
81
+ createStreamFilter(): null;
82
+ }
83
+ export declare function createToolProtocol(mode: "api" | "inline" | "deferred"): ToolProtocol;