agent-sh 0.8.0 → 0.10.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 (106) hide show
  1. package/README.md +27 -43
  2. package/dist/agent/agent-loop.d.ts +69 -6
  3. package/dist/agent/agent-loop.js +954 -153
  4. package/dist/agent/conversation-state.d.ts +74 -21
  5. package/dist/agent/conversation-state.js +361 -150
  6. package/dist/agent/history-file.d.ts +13 -4
  7. package/dist/agent/history-file.js +110 -36
  8. package/dist/agent/nuclear-form.d.ts +28 -3
  9. package/dist/agent/nuclear-form.js +88 -6
  10. package/dist/agent/skills.d.ts +2 -4
  11. package/dist/agent/skills.js +10 -4
  12. package/dist/agent/subagent.d.ts +23 -0
  13. package/dist/agent/subagent.js +53 -11
  14. package/dist/agent/system-prompt.d.ts +37 -5
  15. package/dist/agent/system-prompt.js +100 -67
  16. package/dist/{token-budget.d.ts → agent/token-budget.d.ts} +5 -4
  17. package/dist/{token-budget.js → agent/token-budget.js} +15 -20
  18. package/dist/agent/tool-protocol.d.ts +105 -0
  19. package/dist/agent/tool-protocol.js +551 -0
  20. package/dist/agent/tools/bash.js +3 -3
  21. package/dist/agent/tools/edit-file.js +9 -6
  22. package/dist/agent/tools/glob.js +4 -2
  23. package/dist/agent/tools/grep.js +27 -3
  24. package/dist/agent/tools/ls.js +5 -6
  25. package/dist/agent/types.d.ts +22 -2
  26. package/dist/context-manager.d.ts +17 -0
  27. package/dist/context-manager.js +37 -4
  28. package/dist/core.d.ts +7 -7
  29. package/dist/core.js +99 -196
  30. package/dist/event-bus.d.ts +85 -2
  31. package/dist/event-bus.js +20 -1
  32. package/dist/executor.d.ts +4 -3
  33. package/dist/executor.js +18 -15
  34. package/dist/extension-loader.d.ts +5 -0
  35. package/dist/extension-loader.js +143 -19
  36. package/dist/extensions/agent-backend.d.ts +14 -0
  37. package/dist/extensions/agent-backend.js +188 -0
  38. package/dist/extensions/command-suggest.d.ts +3 -3
  39. package/dist/extensions/command-suggest.js +4 -3
  40. package/dist/extensions/index.d.ts +19 -0
  41. package/dist/extensions/index.js +24 -0
  42. package/dist/extensions/slash-commands.d.ts +1 -1
  43. package/dist/extensions/slash-commands.js +30 -10
  44. package/dist/extensions/tui-renderer.js +117 -113
  45. package/dist/index.js +39 -26
  46. package/dist/settings.d.ts +40 -3
  47. package/dist/settings.js +57 -10
  48. package/dist/{input-handler.d.ts → shell/input-handler.d.ts} +3 -2
  49. package/dist/{input-handler.js → shell/input-handler.js} +111 -85
  50. package/dist/{output-parser.d.ts → shell/output-parser.d.ts} +1 -1
  51. package/dist/{output-parser.js → shell/output-parser.js} +1 -1
  52. package/dist/{shell.d.ts → shell/shell.d.ts} +8 -2
  53. package/dist/{shell.js → shell/shell.js} +39 -8
  54. package/dist/types.d.ts +61 -10
  55. package/dist/utils/ansi.d.ts +5 -0
  56. package/dist/utils/ansi.js +1 -1
  57. package/dist/utils/compositor.d.ts +67 -0
  58. package/dist/utils/compositor.js +116 -0
  59. package/dist/utils/diff-renderer.d.ts +9 -0
  60. package/dist/utils/diff-renderer.js +312 -146
  61. package/dist/utils/diff.d.ts +21 -2
  62. package/dist/utils/diff.js +165 -89
  63. package/dist/utils/floating-panel.d.ts +2 -0
  64. package/dist/utils/floating-panel.js +30 -14
  65. package/dist/utils/handler-registry.d.ts +31 -10
  66. package/dist/utils/handler-registry.js +58 -16
  67. package/dist/utils/line-editor.d.ts +33 -3
  68. package/dist/utils/line-editor.js +221 -44
  69. package/dist/utils/markdown.d.ts +1 -0
  70. package/dist/utils/markdown.js +1 -1
  71. package/dist/utils/message-utils.d.ts +35 -0
  72. package/dist/utils/message-utils.js +75 -0
  73. package/dist/utils/terminal-buffer.d.ts +5 -1
  74. package/dist/utils/terminal-buffer.js +18 -2
  75. package/dist/utils/tool-display.d.ts +1 -1
  76. package/dist/utils/tool-display.js +4 -4
  77. package/dist/utils/tool-interactive.d.ts +12 -0
  78. package/dist/utils/tool-interactive.js +53 -0
  79. package/examples/extensions/ash-acp-bridge/README.md +39 -0
  80. package/examples/extensions/ash-acp-bridge/package.json +23 -0
  81. package/examples/extensions/ash-acp-bridge/src/index.ts +574 -0
  82. package/examples/extensions/ash-acp-bridge/tsconfig.json +14 -0
  83. package/examples/extensions/ash-mcp-bridge/README.md +72 -0
  84. package/examples/extensions/ash-mcp-bridge/index.ts +164 -0
  85. package/examples/extensions/ash-mcp-bridge/package.json +9 -0
  86. package/examples/extensions/claude-code-bridge/index.ts +198 -51
  87. package/examples/extensions/claude-code-bridge/package.json +1 -0
  88. package/examples/extensions/interactive-prompts.ts +98 -112
  89. package/examples/extensions/overlay-agent.ts +84 -38
  90. package/examples/extensions/peer-mesh.ts +565 -0
  91. package/examples/extensions/pi-bridge/index.ts +2 -2
  92. package/examples/extensions/questionnaire.ts +260 -0
  93. package/examples/extensions/subagents.ts +19 -4
  94. package/examples/extensions/terminal-buffer.ts +32 -53
  95. package/examples/extensions/tmux-pane.ts +307 -0
  96. package/examples/extensions/user-shell.ts +136 -0
  97. package/examples/extensions/web-access.ts +335 -0
  98. package/package.json +44 -2
  99. package/dist/agent/tools/display.d.ts +0 -13
  100. package/dist/agent/tools/display.js +0 -70
  101. package/dist/agent/tools/user-shell.d.ts +0 -13
  102. package/dist/agent/tools/user-shell.js +0 -87
  103. package/dist/extensions/overlay-agent.d.ts +0 -14
  104. package/dist/extensions/overlay-agent.js +0 -147
  105. package/dist/extensions/terminal-buffer.d.ts +0 -14
  106. package/dist/extensions/terminal-buffer.js +0 -125
@@ -1,15 +1,66 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { discoverSkills } from "./skills.js";
3
+ import { fileURLToPath } from "node:url";
4
+ import { discoverProjectSkills } from "./skills.js";
5
+ /**
6
+ * Format skills for inline display in prompt.
7
+ * Shows name, description, and file path so the model can decide immediately
8
+ * whether to load a skill — no extra round-trip needed.
9
+ */
10
+ export function formatSkillsBlock(skills) {
11
+ if (skills.length === 0)
12
+ return "";
13
+ return "# Available Skills\n\n"
14
+ + "Load a skill's full content with read_file on its file path when needed.\n\n"
15
+ + skills.map(s => `- **${s.name}**: ${s.description}\n Path: ${s.filePath}`).join("\n\n");
16
+ }
17
+ // Resolve to the user's home-based config dir — user's standing instructions to the agent
18
+ import * as os from "node:os";
19
+ const GLOBAL_AGENTS_MD = path.join(os.homedir(), ".agent-sh", "AGENTS.md");
20
+ // ── File caches ─────────────────────────────────────────────────────
21
+ // Convention files (CLAUDE.md/AGENT.md) are walked synchronously from
22
+ // CWD to root on every query. In practice they almost never change,
23
+ // so a short TTL cache keyed by CWD avoids redundant filesystem walks.
24
+ // The 5-second TTL is short enough to pick up edits quickly but long
25
+ // enough to eliminate repeated walks within a multi-tool agent loop.
26
+ const CACHE_TTL_MS = 5_000;
27
+ /** TTL cache for convention files, keyed by resolved CWD. */
28
+ let conventionCache = null;
29
+ /** TTL cache for global AGENTS.md — changes extremely rarely. */
30
+ let agentsMdCache = null;
31
+ export function loadGlobalAgentsMd() {
32
+ const now = Date.now();
33
+ if (agentsMdCache && now < agentsMdCache.expiry) {
34
+ return agentsMdCache.result;
35
+ }
36
+ try {
37
+ const content = fs.readFileSync(GLOBAL_AGENTS_MD, "utf-8").trim();
38
+ const result = content || null;
39
+ agentsMdCache = { result, expiry: now + CACHE_TTL_MS };
40
+ return result;
41
+ }
42
+ catch {
43
+ agentsMdCache = { result: null, expiry: now + CACHE_TTL_MS };
44
+ return null;
45
+ }
46
+ }
47
+ /** Resolve the absolute path to agent-sh's own docs directory. */
48
+ const CODE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../");
4
49
  /** File names to scan for project conventions (checked in order). */
5
50
  const CONVENTION_FILES = ["CLAUDE.md", "AGENT.md"];
6
51
  /**
7
52
  * Scan from `dir` upward for project convention files.
8
53
  * Returns contents ordered root-first (general → specific).
54
+ * Results are cached for CACHE_TTL_MS, keyed by resolved directory.
9
55
  */
10
56
  function loadConventionFiles(dir) {
57
+ const cwd = path.resolve(dir);
58
+ const now = Date.now();
59
+ if (conventionCache && conventionCache.cwd === cwd && now < conventionCache.expiry) {
60
+ return conventionCache.result;
61
+ }
11
62
  const files = [];
12
- let current = path.resolve(dir);
63
+ let current = cwd;
13
64
  while (true) {
14
65
  for (const name of CONVENTION_FILES) {
15
66
  const candidate = path.join(current, name);
@@ -30,95 +81,77 @@ function loadConventionFiles(dir) {
30
81
  current = parent;
31
82
  }
32
83
  files.reverse();
33
- return files.map(f => `<!-- ${f.path} -->\n${f.content}`);
84
+ const result = files.map(f => `<!-- ${f.path} -->\n${f.content}`);
85
+ conventionCache = { cwd, result, expiry: now + CACHE_TTL_MS };
86
+ return result;
34
87
  }
35
88
  /**
36
89
  * Static system prompt — identical across all queries, cacheable.
37
90
  * Contains only identity and behavioral instructions.
38
91
  */
39
- export const STATIC_SYSTEM_PROMPT = `You are an AI coding assistant embedded in agent-sh, a terminal shell.
92
+ export const STATIC_SYSTEM_PROMPT = `You are an AI coding assistant running inside agent-sh, a terminal shell.
40
93
  You have access to the user's shell environment and can read, write, and execute code.
41
94
  You share the user's working directory, environment variables, and shell history.
95
+ agent-sh documentation is at ${path.join(CODE_DIR, "docs")} — start with README.md for an index. Read the docs when you need to understand how the runtime works.
42
96
 
43
97
  # Tool Decision Guide
44
-
45
- You have three categories of tools — choose based on who needs the output and
46
- whether the command has lasting effects:
47
-
48
- **Scratchpad tools** (bash, read_file, grep, glob, ls, edit_file, write_file):
98
+ bash, read_file, grep, glob, ls, edit_file, write_file::
49
99
  Use these to investigate, search, read, and modify files. Output is returned
50
100
  to you for reasoning — the user doesn't see it directly.
51
101
 
52
- **Display** (display):
53
- Use this to show output to the user in their terminal. The user sees the
54
- output directly, but it is NOT returned to you. Use when:
55
- - The user asks to see something (cat a file, git log, git diff, man page)
56
- - The output is for the user to read, not for you to process
57
-
58
- **Live shell** (user_shell):
59
- Use this to run complete, non-interactive commands in the user's real shell. Use for:
60
- - Commands that affect shell state (cd, export, source)
61
- - Installing packages, starting servers, running builds
62
- - Any command where the user wants real side effects
63
- - Set return_output=true only if you need to inspect the result
64
-
65
- **Terminal interaction** (terminal_read, terminal_keys):
66
- Use these to observe and interact with what is currently on the user's terminal screen.
67
- - terminal_read: see what the user sees (current screen contents, cursor position)
68
- - terminal_keys: send keystrokes as if the user typed them
69
- Use for: driving interactive programs (vim, htop, less, ssh, REPLs), answering questions
70
- about what's on screen, or typing at the shell prompt when a program is already running.
71
- Do NOT use user_shell to interact with an already-running program — use these instead.
72
-
73
- Default to scratchpad tools for your own investigation. Use display when the
74
- user is the intended audience. Use user_shell when the command has real effects.
75
- Use terminal_read/terminal_keys when interacting with what's already on screen.
76
-
77
- # Interactive Overlay Sessions
78
-
79
- When the dynamic context includes \`interactive-session: true\`, the user has summoned you
80
- via a hotkey overlay from inside their live terminal. They may be in the middle of using
81
- a program (vim, ssh, a REPL, etc.) or at a shell prompt. In this mode:
82
- - Start with terminal_read if you need to understand what's on screen.
83
- - Prefer terminal_keys to interact with whatever is currently running.
84
- - Use user_shell only for running new, standalone commands — not for interacting with
85
- what's already on screen.
86
- - Keep responses concise — the user is in the middle of a workflow.
102
+ Extensions may register additional tools — follow their instructions.
87
103
 
88
104
  # Tool Usage Guidelines
89
105
  - Use read_file before editing a file you haven't seen
90
106
  - Prefer edit_file over write_file for modifying existing files
91
107
  - Use grep/glob to find files before reading them
92
108
  - Keep bash commands focused; avoid long-running blocking commands
93
- - Always check command exit codes for errors`;
109
+ - Always check command exit codes for errors
110
+
111
+ # Preference Learning
112
+
113
+ Treat the user's past commands as standing preferences. Before acting, check shell history
114
+ and conversation context for recurring patterns — apply them proactively and do not wait to
115
+ be reminded.`;
94
116
  /**
95
- * Build the dynamic context injected as a user message before each query.
96
- * Contains everything that changes: tools, shell context, conventions, cwd.
97
- *
98
- * Runs through the "agent:dynamic-context" pipe so extensions can append.
117
+ * CWD-scoped static context: project conventions (CLAUDE.md / AGENT.md)
118
+ * and discovered skills. Stable for a given cwd — callers should cache
119
+ * on cwd identity rather than rebuilding per LLM iteration.
99
120
  */
100
- export function buildDynamicContext(tools, contextManager, shellBudgetTokens) {
121
+ export function buildStaticByCwd(cwd) {
101
122
  const sections = [];
102
- // Tools
103
- sections.push("# Available Tools\n" +
104
- tools.map((t) => `- ${t.name}: ${t.description}`).join("\n"));
105
- // Project conventions (CLAUDE.md / AGENT.md)
106
- const conventions = loadConventionFiles(contextManager.getCwd());
123
+ const conventions = loadConventionFiles(cwd);
107
124
  if (conventions.length > 0) {
108
125
  sections.push("# Project Conventions\n\n" + conventions.join("\n\n"));
109
126
  }
110
- // Skills hint
111
- const skills = discoverSkills(contextManager.getCwd());
112
- if (skills.length > 0) {
113
- sections.push(`You have access to ${skills.length} skill(s). Use the list_skills tool to see them, then read_file to load one.`);
127
+ const projectSkills = discoverProjectSkills(cwd);
128
+ const skillsBlock = formatSkillsBlock(projectSkills);
129
+ if (skillsBlock) {
130
+ sections.push(skillsBlock);
114
131
  }
115
- // Shell context — pass token budget converted to bytes (~4 chars/token)
116
- const shellBudgetBytes = shellBudgetTokens != null ? shellBudgetTokens * 4 : undefined;
117
- const shellContext = contextManager.getContext(shellBudgetBytes);
118
- if (shellContext) {
119
- sections.push(shellContext);
120
- }
121
- // Metadata
122
- sections.push(`Current date: ${new Date().toISOString().split("T")[0]}\nWorking directory: ${contextManager.getCwd()}`);
123
132
  return sections.join("\n\n");
124
133
  }
134
+ /**
135
+ * Per-iteration dynamic context: date, working directory, token usage.
136
+ * Rebuilt every LLM call. Extension advisors add more sections (budget,
137
+ * subagents, metacognitive signals, etc.) on top.
138
+ *
139
+ * Skills, AGENTS.md, and project conventions live in the system prompt
140
+ * (see `system-prompt:build` in agent-loop) so they enter the provider's
141
+ * prefix cache instead of being rebuilt and re-sent every turn.
142
+ *
143
+ * Shell context is likewise not injected here — it flows into the
144
+ * conversation as incremental <shell-events> messages (see
145
+ * AgentLoop.injectShellDelta) for the same reason.
146
+ */
147
+ export function buildDynamicContext(contextManager, tokenStatus) {
148
+ const envLines = [
149
+ `Current date: ${new Date().toISOString().split("T")[0]}`,
150
+ `Working directory: ${contextManager.getCwd()}`,
151
+ ];
152
+ const usedK = (tokenStatus.promptTokens / 1000).toFixed(1);
153
+ const maxK = (tokenStatus.contextWindow / 1000).toFixed(0);
154
+ const pct = Math.min(100, Math.round((tokenStatus.promptTokens / tokenStatus.contextWindow) * 100));
155
+ envLines.push(`Token usage: ${usedK}k/${maxK}k (${pct}%)`);
156
+ return `<environment>\n${envLines.join("\n")}\n</environment>`;
157
+ }
@@ -1,13 +1,14 @@
1
+ /** Response reserve — tokens reserved for the model's output. */
2
+ declare const RESPONSE_RESERVE = 8192;
3
+ /** Fallback when contextWindow is unknown. */
4
+ declare const DEFAULT_CONTEXT_WINDOW = 60000;
5
+ export { RESPONSE_RESERVE, DEFAULT_CONTEXT_WINDOW };
1
6
  export declare class TokenBudget {
2
7
  private contextWindow;
3
8
  private toolCount;
4
9
  constructor(contextWindow?: number, toolCount?: number);
5
10
  /** Update when model or tool set changes. */
6
11
  update(contextWindow?: number, toolCount?: number): void;
7
- /** Total tokens available for shell context + conversation content. */
8
- get contentBudget(): number;
9
12
  /** Token budget for the shell context stream. */
10
13
  get shellBudgetTokens(): number;
11
- /** Token budget for the conversation messages stream. */
12
- get conversationBudgetTokens(): number;
13
14
  }
@@ -1,21 +1,23 @@
1
1
  /**
2
- * Unified token budget manager.
2
+ * Token budget for shell context sizing.
3
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)
4
+ * Computes how much of the context window to allocate to shell history
5
+ * (user commands and outputs — situational awareness). The remaining
6
+ * space is for the conversation, system prompt, tools, and response.
7
7
  *
8
- * The budget accounts for fixed overhead (system prompt, tool definitions,
9
- * response reserve) and divides the remaining space by a configurable ratio.
8
+ * Shell context is sized loosely chars/4 accuracy is fine for this.
9
+ * Conversation and compaction decisions use API-grounded token counts
10
+ * (see ConversationState.estimatePromptTokens).
10
11
  */
11
- import { getSettings } from "./settings.js";
12
- /** Overhead estimates (tokens). */
12
+ import { getSettings } from "../settings.js";
13
13
  const SYSTEM_PROMPT_OVERHEAD = 800;
14
14
  const DYNAMIC_CONTEXT_OVERHEAD = 500; // conventions, metadata, skills list
15
15
  const TOKENS_PER_TOOL_DEFINITION = 50;
16
- const RESPONSE_RESERVE = 8192; // matches llm-client.ts default max_tokens
16
+ /** Response reserve tokens reserved for the model's output. */
17
+ const RESPONSE_RESERVE = 8192;
17
18
  /** Fallback when contextWindow is unknown. */
18
19
  const DEFAULT_CONTEXT_WINDOW = 60_000;
20
+ export { RESPONSE_RESERVE, DEFAULT_CONTEXT_WINDOW };
19
21
  export class TokenBudget {
20
22
  contextWindow;
21
23
  toolCount;
@@ -30,21 +32,14 @@ export class TokenBudget {
30
32
  if (toolCount != null)
31
33
  this.toolCount = toolCount;
32
34
  }
33
- /** Total tokens available for shell context + conversation content. */
34
- get contentBudget() {
35
+ /** Token budget for the shell context stream. */
36
+ get shellBudgetTokens() {
35
37
  const overhead = SYSTEM_PROMPT_OVERHEAD +
36
38
  DYNAMIC_CONTEXT_OVERHEAD +
37
39
  this.toolCount * TOKENS_PER_TOOL_DEFINITION +
38
40
  RESPONSE_RESERVE;
39
- return Math.max(0, this.contextWindow - overhead);
40
- }
41
- /** Token budget for the shell context stream. */
42
- get shellBudgetTokens() {
41
+ const contentBudget = Math.max(0, this.contextWindow - overhead);
43
42
  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;
43
+ return Math.floor(contentBudget * ratio);
49
44
  }
50
45
  }
@@ -0,0 +1,105 @@
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
+ * Extra tool definitions the protocol wants registered in the tool registry.
49
+ * Used by deferred-lookup mode to register its `load_tool` meta-tool.
50
+ * Default: none.
51
+ */
52
+ getProtocolTools?(): ToolDefinition[];
53
+ }
54
+ export declare class ApiToolProtocol implements ToolProtocol {
55
+ readonly mode: "api";
56
+ getApiTools(tools: ToolDefinition[]): ChatCompletionTool[] | undefined;
57
+ getToolPrompt(): string;
58
+ extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
59
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
60
+ recordAssistant(conv: ConversationState, text: string, toolCalls: PendingToolCall[]): void;
61
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
62
+ createStreamFilter(): null;
63
+ }
64
+ export declare class InlineToolProtocol implements ToolProtocol {
65
+ readonly mode: "inline";
66
+ private callCounter;
67
+ getApiTools(): undefined;
68
+ getToolPrompt(tools: ToolDefinition[]): string;
69
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
70
+ extractToolCalls(text: string, _streamedCalls: PendingToolCall[]): PendingToolCall[];
71
+ recordAssistant(conv: ConversationState, text: string, _toolCalls: PendingToolCall[]): void;
72
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
73
+ createStreamFilter(_toolNames: string[]): StreamFilter;
74
+ }
75
+ export declare class DeferredToolProtocol implements ToolProtocol {
76
+ readonly mode: "deferred";
77
+ private coreNames;
78
+ /** Cached extension tool schemas for arg validation. */
79
+ private extSchemas;
80
+ constructor(coreNames: string[]);
81
+ getApiTools(tools: ToolDefinition[]): ChatCompletionTool[] | undefined;
82
+ getToolPrompt(): string;
83
+ extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
84
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
85
+ recordAssistant(conv: ConversationState, text: string, toolCalls: PendingToolCall[]): void;
86
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
87
+ createStreamFilter(): null;
88
+ }
89
+ export declare class DeferredLookupProtocol implements ToolProtocol {
90
+ readonly mode: "deferred-lookup";
91
+ private coreNames;
92
+ private loadedExt;
93
+ /** Cache of the current tools list so load_tool's execute can find schemas. */
94
+ private toolsRef;
95
+ constructor(coreNames: string[]);
96
+ getApiTools(tools: ToolDefinition[]): ChatCompletionTool[] | undefined;
97
+ getToolPrompt(): string;
98
+ extractToolCalls(_text: string, streamedCalls: PendingToolCall[]): PendingToolCall[];
99
+ rewriteToolCall(tc: PendingToolCall): PendingToolCall;
100
+ recordAssistant(conv: ConversationState, text: string, toolCalls: PendingToolCall[]): void;
101
+ recordResults(conv: ConversationState, results: ToolResult[]): void;
102
+ createStreamFilter(): null;
103
+ getProtocolTools(): ToolDefinition[];
104
+ }
105
+ export declare function createToolProtocol(mode: "api" | "inline" | "deferred" | "deferred-lookup"): ToolProtocol;