@omercnet/paseo-context-mode 3.1.3 → 3.1.4-next.434.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.
@@ -2,49 +2,42 @@ import { homedir } from "node:os";
2
2
  import { isAbsolute } from "node:path";
3
3
  import type { PluginSettings } from "@getpaseo/plugin/server";
4
4
  import type {
5
- ContextModePlatform,
6
- ContextModeProvider,
7
5
  FetchAndIndexInput,
8
6
  IndexPathInput,
9
7
  KnowledgeToolResult,
10
8
  PurgeKnowledgeInput,
9
+ ResumeSessionInput,
11
10
  SearchKnowledgeInput,
11
+ SearchKnowledgeResult,
12
+ SearchPage,
12
13
  } from "../shared/knowledge";
14
+ import { IN_PROCESS_PROVIDERS, PANEL_PLATFORM } from "../shared/providers";
13
15
  import type { contextModeSettings } from "../shared/settings";
14
16
  import { type LaunchDescriptor, resolveContextModeBinary } from "./binary";
15
- import {
16
- contextModeStorageRootFor,
17
- type ProviderConfigSource,
18
- type ProviderOverrides,
19
- readProviderOverrides,
20
- resolveProvider,
21
- } from "./integration";
17
+ import { type ProviderConfigSource, readProviderOverrides, resolveProvider } from "./integration";
22
18
  import {
23
19
  type ContextModeToolName,
24
20
  callContextModeTool,
25
- type McpProcessDependencies,
21
+ type McpJsonValue,
26
22
  type McpToolArguments,
27
23
  } from "./mcp-process";
24
+ import { resolveContextModeDataDir } from "./storage-discovery";
28
25
 
29
26
  type SettingsHandle = PluginSettings<typeof contextModeSettings.schema>;
30
27
 
31
- type KnowledgeToolCaller = (
32
- launch: LaunchDescriptor,
33
- name: ContextModeToolName,
34
- arguments_: McpToolArguments,
35
- dependencies: McpProcessDependencies,
36
- ) => Promise<string>;
37
-
38
28
  export interface KnowledgeHandlerDependencies {
39
29
  now?: () => Date;
30
+ env?: NodeJS.ProcessEnv;
31
+ home?: string;
40
32
  resolveBinary?: typeof resolveContextModeBinary;
41
- callTool?: KnowledgeToolCaller;
33
+ callTool?: typeof callContextModeTool;
42
34
  }
43
35
 
44
36
  type KnowledgeContext = { paseo?: ProviderConfigSource };
45
37
 
46
38
  export interface ContextModeKnowledgeHandlers {
47
- search(input: SearchKnowledgeInput, context?: KnowledgeContext): Promise<KnowledgeToolResult>;
39
+ search(input: SearchKnowledgeInput, context?: KnowledgeContext): Promise<SearchKnowledgeResult>;
40
+ resume(input: ResumeSessionInput, context?: KnowledgeContext): Promise<KnowledgeToolResult>;
48
41
  indexPath(input: IndexPathInput, context?: KnowledgeContext): Promise<KnowledgeToolResult>;
49
42
  fetchAndIndex(
50
43
  input: FetchAndIndexInput,
@@ -53,42 +46,26 @@ export interface ContextModeKnowledgeHandlers {
53
46
  purge(input: PurgeKnowledgeInput, context?: KnowledgeContext): Promise<KnowledgeToolResult>;
54
47
  }
55
48
 
56
- const PROVIDER_PLATFORMS: Readonly<Record<ContextModeProvider, ContextModePlatform>> = {
57
- claude: "claude-code",
58
- codex: "codex",
59
- copilot: "copilot-cli",
60
- cursor: "cursor",
61
- opencode: "opencode",
62
- pi: "pi",
63
- omp: "omp",
64
- "omp-plugin": "omp",
65
- };
66
-
67
- export function contextModePlatformFor(provider: ContextModeProvider): ContextModePlatform {
68
- return PROVIDER_PLATFORMS[provider];
69
- }
49
+ /** Search replies end with `More: ctx_search({..., offset:N})`, or `more: ... after:MS` for timeline. */
50
+ const NEXT_PAGE_LINE = /^[Mm]ore: ctx_search\(\{.*\b(offset|after):(\d+)\}\)\s*$/gm;
70
51
 
71
- /**
72
- * `provider` is a Paseo provider id: a built-in, or a configured one whose own `extends` and env
73
- * decide the storage root (`providers` is `agents.providers`).
74
- */
75
- export function contextModeEnvironmentFor(
76
- provider: string,
77
- home = homedir(),
78
- env: NodeJS.ProcessEnv = process.env,
79
- providers: ProviderOverrides = {},
80
- ): NodeJS.ProcessEnv {
81
- const { base, policy, env: providerEnv } = resolveProvider(provider, providers);
82
- const storageRoot = contextModeStorageRootFor(base, { home, env: { ...env, ...providerEnv } });
83
- if (!policy || !storageRoot)
84
- throw new Error(`Context Mode does not support provider ${provider}.`);
85
- return { CONTEXT_MODE_PLATFORM: policy.platform, CONTEXT_MODE_DIR: storageRoot };
52
+ /** The cursor of the last footer; the UI searches one query at a time, so there is one. */
53
+ function nextSearchPage(output: string): SearchPage | null {
54
+ const last = [...output.matchAll(NEXT_PAGE_LINE)].at(-1);
55
+ if (!last) return null;
56
+ const cursor = Number(last[2]);
57
+ return last[1] === "after" ? { after: cursor } : { offset: cursor };
86
58
  }
87
59
 
88
- function definedArguments(values: Record<string, unknown>): McpToolArguments {
89
- return Object.fromEntries(
90
- Object.entries(values).filter(([, value]) => value !== undefined),
91
- ) as McpToolArguments;
60
+ function purgeArguments(input: PurgeKnowledgeInput): McpToolArguments {
61
+ switch (input.scope) {
62
+ case "project":
63
+ return { project: true };
64
+ case "session":
65
+ return { session: input.sessionId };
66
+ case "source":
67
+ return { source: input.source };
68
+ }
92
69
  }
93
70
 
94
71
  export function createContextModeKnowledgeHandlers(
@@ -96,6 +73,8 @@ export function createContextModeKnowledgeHandlers(
96
73
  dependencies: KnowledgeHandlerDependencies = {},
97
74
  ): ContextModeKnowledgeHandlers {
98
75
  const now = dependencies.now ?? (() => new Date());
76
+ const env = dependencies.env ?? process.env;
77
+ const home = dependencies.home ?? homedir();
99
78
  const resolveBinary = dependencies.resolveBinary ?? resolveContextModeBinary;
100
79
  const callTool = dependencies.callTool ?? callContextModeTool;
101
80
 
@@ -117,104 +96,53 @@ export function createContextModeKnowledgeHandlers(
117
96
  name: ContextModeToolName,
118
97
  arguments_: McpToolArguments,
119
98
  context: KnowledgeContext | undefined,
120
- environmentOverrides: NodeJS.ProcessEnv = {},
121
99
  ): Promise<KnowledgeToolResult> {
100
+ // A configured provider inherits its parent's agent id; its own env may move the data dir.
101
+ const {
102
+ base,
103
+ platform,
104
+ env: providerEnv,
105
+ } = resolveProvider(provider, await readProviderOverrides(context?.paseo));
106
+ const inProcess = IN_PROCESS_PROVIDERS.some((entry) => entry.provider === base);
107
+ if (platform === null && !inProcess) {
108
+ throw new Error(`Context Mode does not support provider ${provider}.`);
109
+ }
122
110
  const launch = await launchDescriptor(context?.paseo);
123
- const providers = await readProviderOverrides(context?.paseo);
124
- const output = await callTool(launch, name, arguments_, {
125
- env: {
126
- ...contextModeEnvironmentFor(provider, homedir(), process.env, providers),
127
- ...environmentOverrides,
111
+ const output = await callTool(
112
+ launch,
113
+ name,
114
+ arguments_,
115
+ {},
116
+ {
117
+ platform: platform ?? PANEL_PLATFORM,
118
+ cwd: projectPath,
119
+ dataDir: resolveContextModeDataDir({ ...env, ...providerEnv }, home),
128
120
  },
129
- cwd: projectPath,
130
- });
121
+ );
131
122
  return { provider, output, completedAt: now().toISOString() };
132
123
  }
133
124
 
134
125
  return {
135
- search({ provider, projectPath, queries, limit, source, contentType, sort }, context) {
136
- return run(
137
- provider,
138
- projectPath,
139
- "ctx_search",
140
- definedArguments({ queries, limit, source, contentType, sort }),
141
- context,
142
- );
126
+ async search({ provider, projectPath, queries, scope, source, sort, page }, context) {
127
+ const arguments_: Record<string, McpJsonValue> = { queries, scope, sort, ...page };
128
+ if (source !== undefined) arguments_.source = source;
129
+ const result = await run(provider, projectPath, "ctx_search", arguments_, context);
130
+ return { ...result, next: nextSearchPage(result.output) };
143
131
  },
144
- indexPath(
145
- {
146
- provider,
147
- projectPath,
148
- path,
149
- source,
150
- include,
151
- exclude,
152
- maxDepth,
153
- maxFiles,
154
- extensions,
155
- respectGitignore,
156
- followSymlinks,
157
- },
158
- context,
159
- ) {
132
+ resume({ provider, projectPath, sessionId }, context) {
133
+ return run(provider, projectPath, "ctx_resume", { session: sessionId }, context);
134
+ },
135
+ indexPath({ provider, projectPath, path }, context) {
160
136
  if (!isAbsolute(path)) {
161
137
  throw new Error("Knowledge indexing requires an absolute path on this host.");
162
138
  }
163
- return run(
164
- provider,
165
- projectPath,
166
- "ctx_index",
167
- definedArguments({
168
- path,
169
- source,
170
- include,
171
- exclude,
172
- maxDepth,
173
- maxFiles,
174
- extensions,
175
- respectGitignore,
176
- followSymlinks,
177
- }),
178
- context,
179
- );
139
+ return run(provider, projectPath, "ctx_index", { path }, context);
180
140
  },
181
- fetchAndIndex({ provider, projectPath, url, source, force, ttl }, context) {
182
- return run(
183
- provider,
184
- projectPath,
185
- "ctx_fetch_and_index",
186
- definedArguments({ url, source, force, ttl }),
187
- context,
188
- { CTX_FETCH_STRICT: "1" },
189
- );
141
+ fetchAndIndex({ provider, projectPath, url }, context) {
142
+ return run(provider, projectPath, "ctx_index", { url }, context);
190
143
  },
191
- async purge(input, context) {
192
- const raw = input as PurgeKnowledgeInput & { sessionId?: string };
193
- if (raw.confirm !== true) {
194
- throw new Error("Purge requires exact confirmation with confirm: true.");
195
- }
196
- if (raw.scope === "project") {
197
- if (raw.sessionId !== undefined) {
198
- throw new Error("Purge requires exactly one scope.");
199
- }
200
- return await run(
201
- raw.provider,
202
- raw.projectPath,
203
- "ctx_purge",
204
- { confirm: true, scope: "project" },
205
- context,
206
- );
207
- }
208
- if (raw.scope !== "session" || !raw.sessionId) {
209
- throw new Error("Session purge requires exactly one non-empty sessionId scope.");
210
- }
211
- return await run(
212
- raw.provider,
213
- raw.projectPath,
214
- "ctx_purge",
215
- { confirm: true, scope: "session", sessionId: raw.sessionId },
216
- context,
217
- );
144
+ purge(input, context) {
145
+ return run(input.provider, input.projectPath, "ctx_purge", purgeArguments(input), context);
218
146
  },
219
147
  };
220
148
  }
@@ -1,5 +1,7 @@
1
1
  import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
2
- import type { LaunchDescriptor } from "./binary";
2
+ import { z } from "zod";
3
+ import { type ContextModePlatform, PANEL_PLATFORM } from "../shared/providers";
4
+ import { type LaunchDescriptor, readContextModeVersion } from "./binary";
3
5
 
4
6
  export const PROCESS_OUTPUT_LIMIT = 192 * 1_024;
5
7
  export const PROCESS_TIMEOUT_MS = 12_000;
@@ -33,9 +35,15 @@ export interface McpProcessDependencies {
33
35
  spawnProcess?: typeof spawn;
34
36
  timeoutMs?: number;
35
37
  outputLimit?: number;
36
- env?: NodeJS.ProcessEnv;
37
- cwd?: string;
38
38
  terminationGraceMs?: number;
39
+ readVersion?: (launch: LaunchDescriptor) => Promise<string | null>;
40
+ }
41
+
42
+ /** `--agent` label, project cwd (Context Mode keys its store by cwd) and optional data dir. */
43
+ export interface ContextModeProcessOptions {
44
+ platform: ContextModePlatform;
45
+ cwd?: string;
46
+ dataDir?: string;
39
47
  }
40
48
 
41
49
  export type McpJsonValue =
@@ -48,87 +56,107 @@ export type McpJsonValue =
48
56
 
49
57
  export type McpToolArguments = Readonly<Record<string, McpJsonValue>>;
50
58
 
51
- export type ContextModeToolName =
52
- | "ctx_doctor"
53
- | "ctx_stats"
54
- | "ctx_upgrade"
55
- | "ctx_search"
56
- | "ctx_index"
57
- | "ctx_fetch_and_index"
58
- | "ctx_purge";
59
-
60
- export const CONTEXT_MODE_TOOL_TIMEOUT_MS: Readonly<Record<ContextModeToolName, number>> = {
61
- ctx_doctor: 15_000,
62
- ctx_stats: 15_000,
63
- ctx_search: 20_000,
59
+ export const CONTEXT_MODE_TOOL_TIMEOUT_MS = {
64
60
  ctx_index: 120_000,
65
- ctx_fetch_and_index: 45_000,
61
+ ctx_search: 20_000,
62
+ ctx_resume: 20_000,
63
+ ctx_doctor: 30_000,
66
64
  ctx_purge: 120_000,
67
- ctx_upgrade: 120_000,
68
- };
65
+ } as const;
66
+
67
+ /** The tools the plugin calls; a runtime missing any of them (Context Mode 1.x) is unsupported. */
68
+ export type ContextModeToolName = keyof typeof CONTEXT_MODE_TOOL_TIMEOUT_MS;
69
+
70
+ /** Every plugin-initiated call shares this journal session, so it never shows up as agent work. */
71
+ export const PLUGIN_SESSION_ID = "paseo-context-mode";
72
+
73
+ const CORE_TOOLS = Object.keys(CONTEXT_MODE_TOOL_TIMEOUT_MS);
69
74
 
70
- type JsonRpcResponse = {
71
- id?: number;
72
- result?: unknown;
73
- error?: { code?: number; message?: string };
74
- };
75
+ const DEFAULT_OPTIONS: ContextModeProcessOptions = { platform: PANEL_PLATFORM };
76
+
77
+ /** Command line that runs the stdio MCP server for one agent. */
78
+ export function serverArgs(
79
+ launch: LaunchDescriptor,
80
+ platform: ContextModePlatform,
81
+ ): { command: string; args: string[] } {
82
+ return { command: launch.program, args: [...launch.args, "server", "--agent", platform] };
83
+ }
84
+
85
+ const JsonRpcResponseSchema = z.looseObject({
86
+ id: z.number().optional(),
87
+ result: z.unknown().optional(),
88
+ error: z.looseObject({ code: z.number().optional(), message: z.string().optional() }).optional(),
89
+ });
90
+
91
+ type JsonRpcResponse = z.infer<typeof JsonRpcResponseSchema>;
92
+
93
+ interface McpRequest {
94
+ method: "tools/list" | "tools/call";
95
+ params: Record<string, McpJsonValue>;
96
+ }
75
97
 
76
98
  function safeMessage(value: unknown): string {
77
99
  if (value instanceof Error && value.message) return value.message;
78
100
  return String(value);
79
101
  }
80
102
 
103
+ const ToolResultSchema = z.looseObject({
104
+ content: z.array(z.looseObject({ type: z.string().optional(), text: z.unknown() })).optional(),
105
+ structuredContent: z.unknown().optional(),
106
+ isError: z.boolean().optional(),
107
+ });
108
+
81
109
  function extractToolOutput(result: unknown): string {
82
- if (!result || typeof result !== "object") {
110
+ const parsed = ToolResultSchema.safeParse(result);
111
+ if (!parsed.success) {
83
112
  throw new ProcessFailure("protocol-error", "Context Mode returned an invalid tool result.");
84
113
  }
85
- const value = result as {
86
- content?: Array<{ type?: string; text?: unknown }>;
87
- structuredContent?: unknown;
88
- isError?: boolean;
89
- };
90
- const text = value.content
91
- ?.filter((item) => item?.type === "text" && typeof item.text === "string")
92
- .map((item) => item.text as string)
114
+ const { content = [], structuredContent, isError } = parsed.data;
115
+ const text = content
116
+ .flatMap((item) => (item.type === "text" && typeof item.text === "string" ? [item.text] : []))
93
117
  .join("\n");
94
- const output =
95
- text && text.trim().length > 0
96
- ? text
97
- : value.structuredContent === undefined
98
- ? ""
99
- : JSON.stringify(value.structuredContent, null, 2);
100
- if (value.isError) {
118
+ let output = text;
119
+ if (text.trim().length === 0) {
120
+ output = structuredContent === undefined ? "" : JSON.stringify(structuredContent, null, 2);
121
+ }
122
+ if (isError) {
101
123
  throw new ProcessFailure("command-failed", output || "Context Mode reported a tool error.");
102
124
  }
103
125
  return output;
104
126
  }
105
127
 
106
- async function exchange(
128
+ /** One MCP session: initialize, send `request`, and parse its result. */
129
+ async function exchange<T>(
107
130
  launch: LaunchDescriptor,
108
- operation:
109
- | { kind: "inspect" }
110
- | { kind: "tool"; name: ContextModeToolName; arguments: McpToolArguments },
131
+ request: McpRequest,
132
+ parseResult: (result: unknown) => T,
133
+ defaultTimeoutMs: number,
111
134
  dependencies: McpProcessDependencies,
112
- ): Promise<McpInspection | string> {
135
+ options: ContextModeProcessOptions,
136
+ ): Promise<T> {
113
137
  const spawnProcess = dependencies.spawnProcess ?? spawn;
114
- const timeoutMs =
115
- dependencies.timeoutMs ??
116
- (operation.kind === "tool" ? CONTEXT_MODE_TOOL_TIMEOUT_MS[operation.name] : PROCESS_TIMEOUT_MS);
138
+ const timeoutMs = dependencies.timeoutMs ?? defaultTimeoutMs;
117
139
  const terminationGraceMs = dependencies.terminationGraceMs ?? PROCESS_TERMINATION_GRACE_MS;
118
140
  const outputLimit = dependencies.outputLimit ?? PROCESS_OUTPUT_LIMIT;
119
141
 
120
- const { promise, resolve, reject } = Promise.withResolvers<McpInspection | string>();
142
+ const { promise, resolve, reject } = Promise.withResolvers<T>();
143
+ const { command, args } = serverArgs(launch, options.platform);
144
+ const env = {
145
+ ...launch.env,
146
+ ...(options.dataDir ? { CONTEXT_MODE_DATA_DIR: options.dataDir } : {}),
147
+ // Claude Code resolves its project from this variable before cwd.
148
+ ...(options.platform === "claude-code" && options.cwd
149
+ ? { CLAUDE_PROJECT_DIR: options.cwd }
150
+ : {}),
151
+ };
121
152
  let child: ChildProcessWithoutNullStreams;
122
153
  try {
123
- child = spawnProcess(launch.program, launch.args, {
154
+ child = spawnProcess(command, args, {
124
155
  shell: false,
125
156
  stdio: ["pipe", "pipe", "pipe"],
126
157
  windowsHide: true,
127
- cwd: dependencies.cwd,
128
- env:
129
- launch.env || dependencies.env
130
- ? { ...process.env, ...launch.env, ...dependencies.env }
131
- : undefined,
158
+ cwd: options.cwd,
159
+ env: Object.keys(env).length > 0 ? { ...process.env, ...env } : undefined,
132
160
  });
133
161
  } catch (error) {
134
162
  throw new ProcessFailure(
@@ -142,7 +170,6 @@ async function exchange(
142
170
  let stderrBytes = 0;
143
171
  let lineBuffer = "";
144
172
  let stderr = "";
145
- let version: string | null = null;
146
173
 
147
174
  const finish = (callback: () => void) => {
148
175
  if (settled) return;
@@ -263,7 +290,7 @@ async function exchange(
263
290
  if (!line.trim()) continue;
264
291
  let response: JsonRpcResponse;
265
292
  try {
266
- response = JSON.parse(line) as JsonRpcResponse;
293
+ response = JsonRpcResponseSchema.parse(JSON.parse(line));
267
294
  } catch {
268
295
  fail(
269
296
  new ProcessFailure("protocol-error", "Context Mode emitted invalid MCP protocol output."),
@@ -280,20 +307,8 @@ async function exchange(
280
307
  );
281
308
  return;
282
309
  }
283
- const result = response.result as { serverInfo?: { version?: unknown } } | undefined;
284
- version =
285
- typeof result?.serverInfo?.version === "string" ? result.serverInfo.version : null;
286
310
  writeMessage({ jsonrpc: "2.0", method: "notifications/initialized" });
287
- writeMessage(
288
- operation.kind === "inspect"
289
- ? { jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }
290
- : {
291
- jsonrpc: "2.0",
292
- id: 2,
293
- method: "tools/call",
294
- params: { name: operation.name, arguments: operation.arguments },
295
- },
296
- );
311
+ writeMessage({ jsonrpc: "2.0", id: 2, ...request });
297
312
  continue;
298
313
  }
299
314
  if (response.id !== 2) continue;
@@ -304,17 +319,9 @@ async function exchange(
304
319
  fail(new ProcessFailure(unsupported ? "unsupported" : "command-failed", message));
305
320
  return;
306
321
  }
307
- if (operation.kind === "inspect") {
308
- const result = response.result as { tools?: Array<{ name?: unknown }> } | undefined;
309
- const tools = (result?.tools ?? [])
310
- .map((tool) => tool.name)
311
- .filter((name): name is string => typeof name === "string");
312
- finish(() => resolve({ version, tools }));
313
- return;
314
- }
315
322
  try {
316
- const output = extractToolOutput(response.result);
317
- finish(() => resolve(output));
323
+ const value = parseResult(response.result);
324
+ finish(() => resolve(value));
318
325
  } catch (error) {
319
326
  fail(
320
327
  error instanceof ProcessFailure
@@ -339,22 +346,64 @@ async function exchange(
339
346
  return await promise;
340
347
  }
341
348
 
349
+ const ToolListSchema = z.looseObject({
350
+ tools: z.array(z.looseObject({ name: z.string() })).default([]),
351
+ });
352
+
353
+ function extractToolNames(result: unknown): string[] {
354
+ const parsed = ToolListSchema.safeParse(result ?? {});
355
+ if (!parsed.success) {
356
+ throw new ProcessFailure("protocol-error", "Context Mode returned an invalid tool list.");
357
+ }
358
+ return parsed.data.tools.map(({ name }) => name);
359
+ }
360
+
342
361
  export async function inspectContextMode(
343
362
  launch: LaunchDescriptor,
344
363
  dependencies: McpProcessDependencies = {},
364
+ options: ContextModeProcessOptions = DEFAULT_OPTIONS,
345
365
  ): Promise<McpInspection> {
346
- return (await exchange(launch, { kind: "inspect" }, dependencies)) as McpInspection;
366
+ const [tools, version] = await Promise.all([
367
+ exchange(
368
+ launch,
369
+ { method: "tools/list", params: {} },
370
+ extractToolNames,
371
+ PROCESS_TIMEOUT_MS,
372
+ dependencies,
373
+ options,
374
+ ),
375
+ (dependencies.readVersion ?? readContextModeVersion)(launch),
376
+ ]);
377
+ const missing = CORE_TOOLS.filter((tool) => !tools.includes(tool));
378
+ if (missing.length > 0) {
379
+ const doctorOnly = tools.length === 1 && tools[0] === "ctx_doctor";
380
+ const reason = doctorOnly
381
+ ? "Context Mode needs Node.js 24.15 or later with node:sqlite; run ctx_doctor for details."
382
+ : "Context Mode 2.x is required; clear the configured binary path to use the bundled runtime.";
383
+ throw new ProcessFailure(
384
+ "unsupported",
385
+ `Context Mode ${version ?? "(unknown version)"} lacks ${missing.join(", ")}. ${reason}`,
386
+ );
387
+ }
388
+ return { version, tools };
347
389
  }
348
390
 
349
391
  export async function callContextModeTool(
350
392
  launch: LaunchDescriptor,
351
393
  name: ContextModeToolName,
352
- arguments_: McpToolArguments = {},
353
- dependencies: McpProcessDependencies = {},
394
+ arguments_: McpToolArguments,
395
+ dependencies: McpProcessDependencies,
396
+ options: ContextModeProcessOptions,
354
397
  ): Promise<string> {
355
- return (await exchange(
398
+ return exchange(
356
399
  launch,
357
- { kind: "tool", name, arguments: arguments_ },
400
+ {
401
+ method: "tools/call",
402
+ params: { name, arguments: arguments_, _meta: { sessionId: PLUGIN_SESSION_ID } },
403
+ },
404
+ extractToolOutput,
405
+ CONTEXT_MODE_TOOL_TIMEOUT_MS[name],
358
406
  dependencies,
359
- )) as string;
407
+ options,
408
+ );
360
409
  }