codeep 1.3.42 → 2.0.1

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 (60) hide show
  1. package/README.md +208 -0
  2. package/dist/acp/commands.js +770 -7
  3. package/dist/acp/protocol.d.ts +11 -2
  4. package/dist/acp/server.js +179 -11
  5. package/dist/acp/session.d.ts +3 -0
  6. package/dist/acp/session.js +5 -0
  7. package/dist/api/index.js +39 -6
  8. package/dist/config/index.d.ts +13 -0
  9. package/dist/config/index.js +45 -0
  10. package/dist/config/providers.js +76 -1
  11. package/dist/renderer/App.d.ts +12 -0
  12. package/dist/renderer/App.js +109 -4
  13. package/dist/renderer/agentExecution.js +5 -0
  14. package/dist/renderer/commands.js +638 -2
  15. package/dist/renderer/components/Help.js +28 -0
  16. package/dist/renderer/components/Login.d.ts +1 -0
  17. package/dist/renderer/components/Login.js +24 -9
  18. package/dist/renderer/handlers.d.ts +11 -1
  19. package/dist/renderer/handlers.js +30 -0
  20. package/dist/renderer/main.js +73 -0
  21. package/dist/utils/agent.d.ts +17 -0
  22. package/dist/utils/agent.js +91 -7
  23. package/dist/utils/agentChat.d.ts +10 -2
  24. package/dist/utils/agentChat.js +48 -9
  25. package/dist/utils/agentStream.js +6 -2
  26. package/dist/utils/checkpoints.d.ts +93 -0
  27. package/dist/utils/checkpoints.js +205 -0
  28. package/dist/utils/context.d.ts +24 -0
  29. package/dist/utils/context.js +57 -0
  30. package/dist/utils/customCommands.d.ts +62 -0
  31. package/dist/utils/customCommands.js +201 -0
  32. package/dist/utils/hooks.d.ts +97 -0
  33. package/dist/utils/hooks.js +223 -0
  34. package/dist/utils/mcpClient.d.ts +229 -0
  35. package/dist/utils/mcpClient.js +497 -0
  36. package/dist/utils/mcpConfig.d.ts +55 -0
  37. package/dist/utils/mcpConfig.js +177 -0
  38. package/dist/utils/mcpMarketplace.d.ts +49 -0
  39. package/dist/utils/mcpMarketplace.js +175 -0
  40. package/dist/utils/mcpRegistry.d.ts +129 -0
  41. package/dist/utils/mcpRegistry.js +427 -0
  42. package/dist/utils/mcpSamplingBridge.d.ts +32 -0
  43. package/dist/utils/mcpSamplingBridge.js +88 -0
  44. package/dist/utils/mcpStreamableHttp.d.ts +65 -0
  45. package/dist/utils/mcpStreamableHttp.js +207 -0
  46. package/dist/utils/openrouterPrefs.d.ts +36 -0
  47. package/dist/utils/openrouterPrefs.js +83 -0
  48. package/dist/utils/skillBundles.d.ts +84 -0
  49. package/dist/utils/skillBundles.js +257 -0
  50. package/dist/utils/skillBundlesCloud.d.ts +69 -0
  51. package/dist/utils/skillBundlesCloud.js +202 -0
  52. package/dist/utils/tokenTracker.d.ts +14 -2
  53. package/dist/utils/tokenTracker.js +59 -41
  54. package/dist/utils/toolExecution.d.ts +17 -1
  55. package/dist/utils/toolExecution.js +184 -6
  56. package/dist/utils/tools.d.ts +22 -6
  57. package/dist/utils/tools.js +83 -8
  58. package/package.json +3 -2
  59. package/bin/codeep-macos-arm64 +0 -0
  60. package/bin/codeep-macos-x64 +0 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Project-local lifecycle hooks.
3
+ *
4
+ * User drops executable shell scripts in `.codeep/hooks/<event>.sh` and Codeep
5
+ * runs them at the relevant moment during a session. Generalises what the
6
+ * `agentAutoCommit` and `agentAutoVerify` config flags do — anything those
7
+ * can do, a `post_edit` hook can do too, without a code change in the CLI.
8
+ *
9
+ * Supported events:
10
+ *
11
+ * `pre_tool_call` — fires before every tool execution. NON-ZERO exit
12
+ * BLOCKS the tool call (the agent gets a clear error
13
+ * message). Use for policy enforcement: block writes
14
+ * to certain paths, refuse risky commands, etc.
15
+ *
16
+ * `post_edit` — fires after a successful write_file or edit_file.
17
+ * Exit code is ignored (auto-format / auto-lint:
18
+ * failure shouldn't block the agent). Receives the
19
+ * file path. Use for prettier/eslint/gofmt.
20
+ *
21
+ * `on_error` — fires when a tool call returns success=false.
22
+ * Exit code ignored. Use for centralised logging
23
+ * or alerting.
24
+ *
25
+ * `pre_commit` — fires before the /commit skill stages anything.
26
+ * NON-ZERO exit BLOCKS the commit. Use for test
27
+ * runs, lint gates, etc.
28
+ *
29
+ * Environment variables every hook receives:
30
+ * CODEEP_HOOK_EVENT - one of the event names above
31
+ * CODEEP_WORKSPACE - workspace root absolute path
32
+ * CODEEP_SESSION_ID - current Codeep session id
33
+ *
34
+ * Event-specific extras:
35
+ * pre_tool_call / on_error:
36
+ * CODEEP_HOOK_TOOL - tool name (read_file, write_file, …)
37
+ * CODEEP_HOOK_PARAMS - JSON-encoded tool parameters
38
+ * post_edit:
39
+ * CODEEP_HOOK_TOOL - 'write_file' | 'edit_file'
40
+ * CODEEP_HOOK_FILE - absolute path of the file that was edited
41
+ *
42
+ * Security note: hooks run arbitrary shell. They are project-scoped — a
43
+ * cloned repo with hostile hooks would execute its scripts on the user's
44
+ * machine the first time they trigger an agent tool call. The welcome
45
+ * banner warns when hooks exist (see `summarizeHooks`); we do not run
46
+ * hooks from `~/.codeep/hooks/` (global) for that reason.
47
+ */
48
+ export type HookEvent = 'pre_tool_call' | 'post_edit' | 'on_error' | 'pre_commit';
49
+ export declare const HOOK_EVENTS: readonly HookEvent[];
50
+ export interface HookContext {
51
+ event: HookEvent;
52
+ workspaceRoot: string;
53
+ sessionId?: string;
54
+ /** For pre_tool_call / on_error / post_edit */
55
+ toolName?: string;
56
+ /** For pre_tool_call / on_error */
57
+ toolParams?: Record<string, unknown>;
58
+ /** For post_edit */
59
+ filePath?: string;
60
+ }
61
+ export interface HookResult {
62
+ /** True if a hook script existed and was actually invoked. */
63
+ executed: boolean;
64
+ /** Exit code, or 0 if no hook was present. */
65
+ exitCode: number;
66
+ stdout: string;
67
+ stderr: string;
68
+ /** True if this hook event blocks downstream work AND the hook failed. */
69
+ blocked: boolean;
70
+ /** Path that was executed (useful for error messages). */
71
+ scriptPath?: string;
72
+ }
73
+ /**
74
+ * Execute the configured hook for an event, if any. Returns `executed: false`
75
+ * if no script exists. Caller is responsible for checking `blocked` and
76
+ * aborting the parent action when it's true.
77
+ */
78
+ export declare function runHook(ctx: HookContext, opts?: {
79
+ timeoutMs?: number;
80
+ }): HookResult;
81
+ /**
82
+ * Inspect a workspace and return which hook scripts are installed.
83
+ * Used by the welcome banner and `/hooks` slash command.
84
+ */
85
+ export declare function listInstalledHooks(workspaceRoot: string): {
86
+ event: HookEvent;
87
+ scriptPath: string;
88
+ }[];
89
+ /**
90
+ * Render an installed-hook list as Markdown for `/hooks` output.
91
+ */
92
+ export declare function formatHookList(hooks: ReturnType<typeof listInstalledHooks>): string;
93
+ /**
94
+ * Short one-line summary used in the welcome banner when hooks are present.
95
+ * Returns empty string if no hooks installed.
96
+ */
97
+ export declare function summarizeHooks(workspaceRoot: string): string;
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Project-local lifecycle hooks.
3
+ *
4
+ * User drops executable shell scripts in `.codeep/hooks/<event>.sh` and Codeep
5
+ * runs them at the relevant moment during a session. Generalises what the
6
+ * `agentAutoCommit` and `agentAutoVerify` config flags do — anything those
7
+ * can do, a `post_edit` hook can do too, without a code change in the CLI.
8
+ *
9
+ * Supported events:
10
+ *
11
+ * `pre_tool_call` — fires before every tool execution. NON-ZERO exit
12
+ * BLOCKS the tool call (the agent gets a clear error
13
+ * message). Use for policy enforcement: block writes
14
+ * to certain paths, refuse risky commands, etc.
15
+ *
16
+ * `post_edit` — fires after a successful write_file or edit_file.
17
+ * Exit code is ignored (auto-format / auto-lint:
18
+ * failure shouldn't block the agent). Receives the
19
+ * file path. Use for prettier/eslint/gofmt.
20
+ *
21
+ * `on_error` — fires when a tool call returns success=false.
22
+ * Exit code ignored. Use for centralised logging
23
+ * or alerting.
24
+ *
25
+ * `pre_commit` — fires before the /commit skill stages anything.
26
+ * NON-ZERO exit BLOCKS the commit. Use for test
27
+ * runs, lint gates, etc.
28
+ *
29
+ * Environment variables every hook receives:
30
+ * CODEEP_HOOK_EVENT - one of the event names above
31
+ * CODEEP_WORKSPACE - workspace root absolute path
32
+ * CODEEP_SESSION_ID - current Codeep session id
33
+ *
34
+ * Event-specific extras:
35
+ * pre_tool_call / on_error:
36
+ * CODEEP_HOOK_TOOL - tool name (read_file, write_file, …)
37
+ * CODEEP_HOOK_PARAMS - JSON-encoded tool parameters
38
+ * post_edit:
39
+ * CODEEP_HOOK_TOOL - 'write_file' | 'edit_file'
40
+ * CODEEP_HOOK_FILE - absolute path of the file that was edited
41
+ *
42
+ * Security note: hooks run arbitrary shell. They are project-scoped — a
43
+ * cloned repo with hostile hooks would execute its scripts on the user's
44
+ * machine the first time they trigger an agent tool call. The welcome
45
+ * banner warns when hooks exist (see `summarizeHooks`); we do not run
46
+ * hooks from `~/.codeep/hooks/` (global) for that reason.
47
+ */
48
+ import { existsSync, readdirSync, statSync, accessSync, constants } from 'fs';
49
+ import { join } from 'path';
50
+ import { spawnSync } from 'child_process';
51
+ export const HOOK_EVENTS = ['pre_tool_call', 'post_edit', 'on_error', 'pre_commit'];
52
+ /** Events whose non-zero exit aborts the action that triggered them. */
53
+ const BLOCKING_EVENTS = new Set(['pre_tool_call', 'pre_commit']);
54
+ const NOT_EXECUTED = { executed: false, exitCode: 0, stdout: '', stderr: '', blocked: false };
55
+ function getHooksDir(workspaceRoot) {
56
+ return join(workspaceRoot, '.codeep', 'hooks');
57
+ }
58
+ function findHookScript(workspaceRoot, event) {
59
+ const dir = getHooksDir(workspaceRoot);
60
+ if (!existsSync(dir))
61
+ return null;
62
+ // Allow `.sh` and bare (no-extension) executable; users sometimes prefer
63
+ // `.codeep/hooks/post_edit` over `post_edit.sh`. Try both.
64
+ for (const name of [`${event}.sh`, event]) {
65
+ const full = join(dir, name);
66
+ if (!existsSync(full))
67
+ continue;
68
+ try {
69
+ const stat = statSync(full);
70
+ if (!stat.isFile())
71
+ continue;
72
+ // Must be executable by the current user — otherwise we'd surprise
73
+ // them with a hook that silently does nothing.
74
+ accessSync(full, constants.X_OK);
75
+ return full;
76
+ }
77
+ catch {
78
+ // Not executable or unreadable — try next candidate.
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+ /**
84
+ * Execute the configured hook for an event, if any. Returns `executed: false`
85
+ * if no script exists. Caller is responsible for checking `blocked` and
86
+ * aborting the parent action when it's true.
87
+ */
88
+ export function runHook(ctx, opts = {}) {
89
+ const script = findHookScript(ctx.workspaceRoot, ctx.event);
90
+ if (!script)
91
+ return NOT_EXECUTED;
92
+ const env = {
93
+ ...process.env,
94
+ CODEEP_HOOK_EVENT: ctx.event,
95
+ CODEEP_WORKSPACE: ctx.workspaceRoot,
96
+ };
97
+ if (ctx.sessionId)
98
+ env.CODEEP_SESSION_ID = ctx.sessionId;
99
+ if (ctx.toolName)
100
+ env.CODEEP_HOOK_TOOL = ctx.toolName;
101
+ if (ctx.toolParams) {
102
+ // Cap env var size — most shells choke at ~128KB total env, and a
103
+ // `write_file` with a 100KB content field would single-handedly blow
104
+ // past that. We truncate the serialised JSON instead and append a
105
+ // marker so hook scripts can detect overflow if they care.
106
+ const PARAMS_CAP = 8192;
107
+ let serialized = JSON.stringify(ctx.toolParams);
108
+ if (serialized.length > PARAMS_CAP) {
109
+ serialized = serialized.slice(0, PARAMS_CAP) + '...[truncated]';
110
+ }
111
+ env.CODEEP_HOOK_PARAMS = serialized;
112
+ }
113
+ if (ctx.filePath)
114
+ env.CODEEP_HOOK_FILE = ctx.filePath;
115
+ // 30s ceiling so a runaway lint / test command can't wedge the agent
116
+ // loop. Configurable per-call so tests can use a tight timeout.
117
+ const timeout = opts.timeoutMs ?? 30_000;
118
+ let proc;
119
+ try {
120
+ proc = spawnSync(script, [], {
121
+ cwd: ctx.workspaceRoot,
122
+ env,
123
+ timeout,
124
+ encoding: 'utf-8',
125
+ stdio: ['ignore', 'pipe', 'pipe'],
126
+ });
127
+ }
128
+ catch (err) {
129
+ // Should be rare — spawnSync usually returns a result rather than throwing.
130
+ return {
131
+ executed: true,
132
+ exitCode: 1,
133
+ stdout: '',
134
+ stderr: `hook spawn failed: ${err.message}`,
135
+ blocked: BLOCKING_EVENTS.has(ctx.event),
136
+ scriptPath: script,
137
+ };
138
+ }
139
+ const exitCode = proc.status ?? (proc.error ? 1 : 0);
140
+ const stdout = proc.stdout ?? '';
141
+ const stderr = proc.stderr ?? '';
142
+ const blocked = BLOCKING_EVENTS.has(ctx.event) && exitCode !== 0;
143
+ return { executed: true, exitCode, stdout, stderr, blocked, scriptPath: script };
144
+ }
145
+ /**
146
+ * Inspect a workspace and return which hook scripts are installed.
147
+ * Used by the welcome banner and `/hooks` slash command.
148
+ */
149
+ export function listInstalledHooks(workspaceRoot) {
150
+ const dir = getHooksDir(workspaceRoot);
151
+ if (!existsSync(dir))
152
+ return [];
153
+ let entries;
154
+ try {
155
+ entries = readdirSync(dir);
156
+ }
157
+ catch {
158
+ return [];
159
+ }
160
+ // Build a set of which events have a script — try both `<event>.sh` and
161
+ // bare-name forms, mirroring findHookScript.
162
+ const seen = new Set();
163
+ const out = [];
164
+ for (const event of HOOK_EVENTS) {
165
+ for (const candidate of [`${event}.sh`, event]) {
166
+ if (!entries.includes(candidate))
167
+ continue;
168
+ const full = join(dir, candidate);
169
+ try {
170
+ if (!statSync(full).isFile())
171
+ continue;
172
+ accessSync(full, constants.X_OK);
173
+ if (seen.has(event))
174
+ break;
175
+ seen.add(event);
176
+ out.push({ event, scriptPath: full });
177
+ break;
178
+ }
179
+ catch {
180
+ // Skip unreadable / non-executable.
181
+ }
182
+ }
183
+ }
184
+ return out;
185
+ }
186
+ /**
187
+ * Render an installed-hook list as Markdown for `/hooks` output.
188
+ */
189
+ export function formatHookList(hooks) {
190
+ if (hooks.length === 0) {
191
+ return [
192
+ '_No hooks installed in this workspace._',
193
+ '',
194
+ 'Drop an executable script in `.codeep/hooks/<event>.sh` to enable one. Supported events:',
195
+ '',
196
+ ...HOOK_EVENTS.map(e => `- \`${e}\`${BLOCKING_EVENTS.has(e) ? ' *(non-zero exit blocks the action)*' : ''}`),
197
+ '',
198
+ 'Example — auto-format on save:',
199
+ '',
200
+ '```bash',
201
+ '#!/bin/bash',
202
+ '# .codeep/hooks/post_edit.sh',
203
+ 'prettier --write "$CODEEP_HOOK_FILE" 2>/dev/null',
204
+ '```',
205
+ ].join('\n');
206
+ }
207
+ const lines = ['## Installed hooks', ''];
208
+ for (const h of hooks) {
209
+ const tag = BLOCKING_EVENTS.has(h.event) ? ' *(blocking)*' : '';
210
+ lines.push(`- \`${h.event}\`${tag} — \`${h.scriptPath}\``);
211
+ }
212
+ return lines.join('\n');
213
+ }
214
+ /**
215
+ * Short one-line summary used in the welcome banner when hooks are present.
216
+ * Returns empty string if no hooks installed.
217
+ */
218
+ export function summarizeHooks(workspaceRoot) {
219
+ const hooks = listInstalledHooks(workspaceRoot);
220
+ if (hooks.length === 0)
221
+ return '';
222
+ return `${hooks.length} hook${hooks.length === 1 ? '' : 's'} active (${hooks.map(h => h.event).join(', ')})`;
223
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Minimal MCP (Model Context Protocol) stdio client.
3
+ *
4
+ * Each `McpClient` instance owns one child process running an MCP server
5
+ * (e.g. `npx @modelcontextprotocol/server-filesystem /some/path`). It speaks
6
+ * JSON-RPC 2.0 over stdio per the MCP spec, performs the
7
+ * initialize → tools/list handshake, and exposes a `callTool` method that
8
+ * agent tool dispatch routes through.
9
+ *
10
+ * Scope of this MVP:
11
+ * - initialize + tools/list discovery
12
+ * - tools/call forwarding
13
+ * - stop() kills the process and rejects in-flight requests
14
+ *
15
+ * NOT covered yet (defer to a future iteration):
16
+ * - resources / prompts / sampling MCP primitives
17
+ * - capability negotiation beyond "we want tools"
18
+ * - server-initiated requests (we ignore them)
19
+ * - reconnect on crash (process exit is fatal for that client)
20
+ */
21
+ import type { McpServer } from '../acp/protocol.js';
22
+ export interface McpTool {
23
+ name: string;
24
+ description?: string;
25
+ inputSchema?: Record<string, unknown>;
26
+ }
27
+ export interface McpResource {
28
+ uri: string;
29
+ name?: string;
30
+ description?: string;
31
+ mimeType?: string;
32
+ }
33
+ export interface McpResourceContent {
34
+ uri: string;
35
+ mimeType?: string;
36
+ /** Text payload — set when the server returns a text resource. */
37
+ text?: string;
38
+ /** Base64 blob payload — set when the server returns binary. */
39
+ blob?: string;
40
+ }
41
+ export interface McpPrompt {
42
+ name: string;
43
+ description?: string;
44
+ /** Argument metadata if the prompt is parameterised. */
45
+ arguments?: {
46
+ name: string;
47
+ description?: string;
48
+ required?: boolean;
49
+ }[];
50
+ }
51
+ export interface McpPromptMessage {
52
+ role: 'user' | 'assistant' | 'system';
53
+ content: {
54
+ type: string;
55
+ text?: string;
56
+ [k: string]: unknown;
57
+ };
58
+ }
59
+ /**
60
+ * Server-initiated `sampling/createMessage` request payload. MCP servers
61
+ * that opt into the `sampling` capability send this to ask the host LLM
62
+ * (Codeep, in our case) to generate a completion on their behalf.
63
+ */
64
+ export interface SamplingCreateMessageParams {
65
+ messages: {
66
+ role: 'user' | 'assistant';
67
+ content: {
68
+ type: 'text';
69
+ text: string;
70
+ } | {
71
+ type: 'image';
72
+ data: string;
73
+ mimeType: string;
74
+ };
75
+ }[];
76
+ modelPreferences?: {
77
+ hints?: {
78
+ name?: string;
79
+ }[];
80
+ costPriority?: number;
81
+ speedPriority?: number;
82
+ intelligencePriority?: number;
83
+ };
84
+ systemPrompt?: string;
85
+ includeContext?: 'none' | 'thisServer' | 'allServers';
86
+ temperature?: number;
87
+ maxTokens?: number;
88
+ stopSequences?: string[];
89
+ metadata?: Record<string, unknown>;
90
+ }
91
+ export interface SamplingCreateMessageResult {
92
+ role: 'assistant';
93
+ content: {
94
+ type: 'text';
95
+ text: string;
96
+ };
97
+ model: string;
98
+ stopReason?: 'endTurn' | 'stopSequence' | 'maxTokens';
99
+ }
100
+ export declare class McpClient {
101
+ readonly server: McpServer;
102
+ readonly clientOpts: {
103
+ workspaceRoot?: string;
104
+ onSamplingRequest?: (params: SamplingCreateMessageParams) => Promise<SamplingCreateMessageResult>;
105
+ };
106
+ /** Stdio transport state. Null when running over HTTP (or before start). */
107
+ private child;
108
+ /** HTTP transport state. Null when running over stdio. */
109
+ private http;
110
+ private pending;
111
+ private buffer;
112
+ private stopped;
113
+ private toolsCache;
114
+ /** True when this client is configured for the Streamable HTTP transport. */
115
+ private get isHttp();
116
+ /**
117
+ * Rolling-window record of recent crash times (ms epoch). Used by the
118
+ * auto-reconnect logic: too many crashes in a short window → give up
119
+ * instead of spinning indefinitely on a broken server.
120
+ */
121
+ private crashTimestamps;
122
+ /** Reconnect tuning — generous defaults, configurable via env if needed. */
123
+ private readonly MAX_RESTARTS;
124
+ private readonly RESTART_WINDOW_MS;
125
+ /** Has the agent loop been notified that this server is fully gone? */
126
+ private gaveUp;
127
+ /**
128
+ * Optional callback fired after a successful auto-restart. The registry
129
+ * uses this to drop its tools cache so the next `listTools()` re-queries
130
+ * (the server may expose a different tool set after restart).
131
+ */
132
+ onRestart?: () => void;
133
+ /**
134
+ * Optional callback fired when the client gives up after exceeding the
135
+ * restart budget. The registry uses this to surface a visible "MCP
136
+ * server died" error in /mcp.
137
+ */
138
+ onGaveUp?: (reason: string) => void;
139
+ /**
140
+ * Optional callback fired when the server sends a `notifications/*`
141
+ * indicating its catalog changed (tools, resources, prompts). The
142
+ * registry forwards this up so the agent loop can re-fetch on the next
143
+ * iteration.
144
+ */
145
+ onCatalogChanged?: (kind: 'tools' | 'resources' | 'prompts') => void;
146
+ /**
147
+ * @param server MCP server config (command, args, env, name).
148
+ * @param opts Optional client metadata.
149
+ * - `workspaceRoot` exposed to the server as a root via
150
+ * the `roots` capability so filesystem-style servers
151
+ * can scope their reads.
152
+ * - `onSamplingRequest` makes the client advertise the
153
+ * `sampling` capability and routes server-initiated
154
+ * `sampling/createMessage` to the host LLM.
155
+ */
156
+ constructor(server: McpServer, clientOpts?: {
157
+ workspaceRoot?: string;
158
+ onSamplingRequest?: (params: SamplingCreateMessageParams) => Promise<SamplingCreateMessageResult>;
159
+ });
160
+ /** Open the transport and perform the MCP handshake. */
161
+ start(opts?: {
162
+ initTimeoutMs?: number;
163
+ }): Promise<void>;
164
+ /** Discover tools the server exposes. Cached on first call. */
165
+ listTools(): Promise<McpTool[]>;
166
+ /**
167
+ * Discover resources the server exposes. Not all servers implement
168
+ * resources/list — those return a `-32601 Method not found`, which we
169
+ * surface as an empty array (callers can treat absence and emptiness
170
+ * the same way).
171
+ */
172
+ listResources(): Promise<McpResource[]>;
173
+ /** Read one resource by URI. */
174
+ readResource(uri: string): Promise<McpResourceContent[]>;
175
+ /** Discover prompt templates the server exposes (optional capability). */
176
+ listPrompts(): Promise<McpPrompt[]>;
177
+ /** Materialise a prompt template into its message sequence. */
178
+ getPrompt(name: string, args?: Record<string, unknown>): Promise<{
179
+ description?: string;
180
+ messages: McpPromptMessage[];
181
+ }>;
182
+ /** Invoke a tool on this server. */
183
+ callTool(name: string, args: Record<string, unknown>, opts?: {
184
+ timeoutMs?: number;
185
+ }): Promise<string>;
186
+ /**
187
+ * Attempt to spawn a fresh child process after a crash. Tries up to
188
+ * MAX_RESTARTS times within RESTART_WINDOW_MS, then gives up. After a
189
+ * successful restart, `toolsCache` is cleared so the next listTools()
190
+ * re-queries — the server may legitimately expose different tools after
191
+ * a code reload.
192
+ */
193
+ private attemptRestart;
194
+ /** Wire up data/exit/error listeners on the current child. Used by start() and attemptRestart(). */
195
+ private attachChildHandlers;
196
+ /** Tear down the transport (stdio child or HTTP stream) and reject pending requests. */
197
+ stop(): Promise<void>;
198
+ private handleStdout;
199
+ /**
200
+ * Handle a request from the MCP server (server-initiated JSON-RPC).
201
+ * Currently handled methods:
202
+ * - `roots/list` — return the workspace folder if provided
203
+ * - `sampling/createMessage` — delegate to the host LLM callback if
204
+ * one was wired into the constructor; otherwise -32601 (so a
205
+ * server that asks without us advertising the capability gets a
206
+ * clear "no" instead of a hang).
207
+ *
208
+ * Anything else replies with `-32601 Method not found` per JSON-RPC spec.
209
+ */
210
+ private handleServerRequest;
211
+ /** Serialise and send a JSON-RPC response over whichever transport is active. */
212
+ private writeResponse;
213
+ /**
214
+ * Single send path used by request/notify/writeResponse. Stdio just
215
+ * pipes the serialised frame + newline. HTTP POSTs the JSON body; the
216
+ * response (or any later SSE event) re-enters via `dispatchFrame`.
217
+ * Errors on the HTTP path reject pending request promises so the
218
+ * agent doesn't hang waiting on a frame that'll never come.
219
+ */
220
+ private writeFrame;
221
+ /**
222
+ * Common entry point for every incoming JSON-RPC frame, regardless of
223
+ * transport. Stdio's `handleStdout` parses lines and forwards each
224
+ * here; the HTTP transport calls this directly from its `onFrame`.
225
+ */
226
+ private dispatchFrame;
227
+ private request;
228
+ private notify;
229
+ }