@omercnet/paseo-context-mode 3.1.1-next.353.1 → 3.1.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.
package/index.server.ts CHANGED
@@ -45,7 +45,7 @@ export default function contribute(server: PluginServerContext) {
45
45
  server.before("agent.create", async ({ request }, { paseo }) => {
46
46
  const current = await settings.read();
47
47
  if (current.status !== "ready") return request;
48
- const binary = await resolveContextModeBinary(current.values, { paseo });
48
+ const binary = await resolveContextModeBinary(current.values);
49
49
  if (binary.state !== "found") return request;
50
50
  const providers = await readProviderOverrides(paseo);
51
51
  return injectContextModeOnCreate(request, current.values, binary.launch, { providers });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-context-mode",
3
- "version": "3.1.1-next.353.1",
3
+ "version": "3.1.1",
4
4
  "type": "module",
5
5
  "description": "Provider-aware Context Mode health, savings, and MCP activation for Paseo.",
6
6
  "license": "MIT",
package/server/actions.ts CHANGED
@@ -6,9 +6,8 @@ import {
6
6
  type DoctorReport,
7
7
  type DoctorStatusRow,
8
8
  } from "../shared/actions";
9
- import type { contextModeSettings } from "../shared/settings";
9
+ import type { ContextModeSettings, contextModeSettings } from "../shared/settings";
10
10
  import { type BinaryResolution, type LaunchDescriptor, resolveContextModeBinary } from "./binary";
11
- import type { ProviderConfigSource } from "./integration";
12
11
  import { contextModeEnvironmentFor, contextModePlatformFor } from "./knowledge";
13
12
  import {
14
13
  callContextModeTool,
@@ -29,7 +28,7 @@ type ActionToolCaller = (
29
28
 
30
29
  export interface ActionHandlerDependencies {
31
30
  osPlatform?: NodeJS.Platform;
32
- resolveBinary?: typeof resolveContextModeBinary;
31
+ resolveBinary?: (settings: ContextModeSettings) => Promise<BinaryResolution>;
33
32
  inspect?: (
34
33
  launch: LaunchDescriptor,
35
34
  dependencies?: McpProcessDependencies,
@@ -37,12 +36,10 @@ export interface ActionHandlerDependencies {
37
36
  callTool?: ActionToolCaller;
38
37
  }
39
38
 
40
- type ActionContext = { paseo?: ProviderConfigSource };
41
-
42
39
  export interface ContextModeActionHandlers {
43
- doctor(input: ContextModeActionInput, context?: ActionContext): Promise<DoctorReport>;
44
- install(input: ContextModeActionInput, context?: ActionContext): Promise<ContextModeAction>;
45
- upgrade(input: ContextModeActionInput, context?: ActionContext): Promise<ContextModeAction>;
40
+ doctor(input: ContextModeActionInput): Promise<DoctorReport>;
41
+ install(input: ContextModeActionInput): Promise<ContextModeAction>;
42
+ upgrade(input: ContextModeActionInput): Promise<ContextModeAction>;
46
43
  }
47
44
 
48
45
  export function parseContextModeDoctorOutput(rawOutput: string): DoctorStatusRow[] {
@@ -74,24 +71,19 @@ export function createContextModeActionHandlers(
74
71
  const callTool = dependencies.callTool ?? callContextModeTool;
75
72
  const osPlatform = dependencies.osPlatform ?? process.platform;
76
73
 
77
- async function resolvedBinary(
78
- paseo: ProviderConfigSource | undefined,
79
- ): Promise<Extract<BinaryResolution, { state: "found" }>> {
74
+ async function resolvedBinary(): Promise<Extract<BinaryResolution, { state: "found" }>> {
80
75
  const settings = await settingsHandle.read();
81
76
  if (settings.status !== "ready") {
82
77
  throw new Error(`Context Mode settings are invalid: ${settings.error}`.slice(0, 4_096));
83
78
  }
84
- const binary = await resolveBinary(settings.values, { paseo });
79
+ const binary = await resolveBinary(settings.values);
85
80
  if (binary.state === "missing") throw new Error(binary.message.slice(0, 4_096));
86
81
  return binary;
87
82
  }
88
83
 
89
- async function currentVersion(
90
- input: ContextModeActionInput,
91
- paseo: ProviderConfigSource | undefined,
92
- ): Promise<string | null> {
84
+ async function currentVersion(input: ContextModeActionInput): Promise<string | null> {
93
85
  try {
94
- const binary = await resolvedBinary(paseo);
86
+ const binary = await resolvedBinary();
95
87
  const inspection = await inspect(binary.launch, {
96
88
  env: contextModeEnvironmentFor(input.provider),
97
89
  });
@@ -102,8 +94,8 @@ export function createContextModeActionHandlers(
102
94
  }
103
95
 
104
96
  return {
105
- async doctor(input, context) {
106
- const binary = await resolvedBinary(context?.paseo);
97
+ async doctor(input) {
98
+ const binary = await resolvedBinary();
107
99
  const rawOutput = await callTool(
108
100
  binary.launch,
109
101
  "ctx_doctor",
@@ -118,7 +110,7 @@ export function createContextModeActionHandlers(
118
110
  rawOutput,
119
111
  };
120
112
  },
121
- async install(input, context) {
113
+ async install(input) {
122
114
  const platform = contextModePlatformFor(input.provider);
123
115
  return {
124
116
  program: osPlatform === "win32" ? "npm.cmd" : "npm",
@@ -129,15 +121,15 @@ export function createContextModeActionHandlers(
129
121
  "--no-audit",
130
122
  "--no-fund",
131
123
  ],
132
- currentVersion: await currentVersion(input, context?.paseo),
124
+ currentVersion: await currentVersion(input),
133
125
  targetVersion: CONTEXT_MODE_TARGET_VERSION,
134
126
  platform,
135
127
  requiresRestart: true,
136
128
  };
137
129
  },
138
- async upgrade(input, context) {
130
+ async upgrade(input) {
139
131
  const platform = contextModePlatformFor(input.provider);
140
- const binary = await resolvedBinary(context?.paseo);
132
+ const binary = await resolvedBinary();
141
133
  const inspection = await inspect(binary.launch, {
142
134
  env: contextModeEnvironmentFor(input.provider),
143
135
  });
package/server/binary.ts CHANGED
@@ -1,14 +1,12 @@
1
1
  import { constants } from "node:fs";
2
- import { access, readFile, realpath, stat } from "node:fs/promises";
2
+ import { access, realpath, stat } from "node:fs/promises";
3
3
  import { createRequire } from "node:module";
4
4
  import { join, posix, win32 } from "node:path";
5
5
  import type { ContextModeSettings } from "../shared";
6
- import type { ProviderConfigSource } from "./integration";
7
6
 
8
7
  export interface LaunchDescriptor {
9
8
  program: string;
10
9
  args: string[];
11
- env?: Record<string, string>;
12
10
  }
13
11
 
14
12
  export type BinaryResolution =
@@ -24,32 +22,9 @@ export interface BinaryResolutionDependencies {
24
22
  env?: NodeJS.ProcessEnv;
25
23
  platform?: NodeJS.Platform;
26
24
  nodePath?: string;
27
- electron?: boolean;
28
25
  canExecute?: (path: string) => Promise<boolean>;
29
26
  isFile?: (path: string) => Promise<boolean>;
30
- paseo?: ProviderConfigSource;
31
- }
32
-
33
- /**
34
- * Paseo evaluates the server bundle from the daemon's cwd with an empty `import.meta`, so the
35
- * bundled runtime is resolved from this plugin's directory in the daemon config instead.
36
- */
37
- async function findBundledCli(paseo: ProviderConfigSource | undefined): Promise<string | null> {
38
- const { config } = (await paseo?.config.get().catch(() => undefined)) ?? {};
39
- const plugins = config?.plugins;
40
- if (typeof plugins !== "object" || plugins === null) return null;
41
- for (const source of Object.values(plugins)) {
42
- const directory = (source as { path?: unknown } | null)?.path;
43
- if (typeof directory !== "string") continue;
44
- try {
45
- const manifest = JSON.parse(await readFile(join(directory, "paseo-plugin.json"), "utf8"));
46
- if (manifest?.id !== "context-mode") continue;
47
- return createRequire(join(directory, "package.json")).resolve("context-mode/cli");
48
- } catch {
49
- // Not this plugin, or its install has no bundled runtime.
50
- }
51
- }
52
- return null;
27
+ bundledCliPath?: () => string | null;
53
28
  }
54
29
 
55
30
  async function defaultIsFile(path: string): Promise<boolean> {
@@ -166,21 +141,25 @@ export async function resolveContextModeBinary(
166
141
  }
167
142
  }
168
143
 
169
- const bundledCli = await findBundledCli(dependencies.paseo);
170
- if (bundledCli) {
144
+ const bundledCandidates: string[] = [];
145
+ const injectedBundledPath = dependencies.bundledCliPath?.();
146
+ if (injectedBundledPath) bundledCandidates.push(injectedBundledPath);
147
+ if (!dependencies.bundledCliPath) {
148
+ bundledCandidates.push(join(process.cwd(), "node_modules", "context-mode", "cli.bundle.mjs"));
149
+ try {
150
+ bundledCandidates.push(createRequire(import.meta.url).resolve("context-mode/cli"));
151
+ } catch {
152
+ // The plugin bundle may run outside the dependency tree; the cwd candidate remains valid.
153
+ }
154
+ }
155
+ for (const bundledCli of new Set(bundledCandidates)) {
156
+ if (!(await isFile(bundledCli))) continue;
171
157
  const canonicalPath = await realpath(bundledCli).catch(() => bundledCli);
172
- // Under the desktop app `nodePath` is Electron. Paseo strips this variable from agent
173
- // environments, so an injected MCP server needs it to start as Node.
174
- const electron = dependencies.electron ?? process.versions.electron !== undefined;
175
158
  return {
176
159
  state: "found",
177
160
  path: canonicalPath,
178
161
  source: "bundled",
179
- launch: {
180
- program: nodePath,
181
- args: [canonicalPath],
182
- ...(electron ? { env: { ELECTRON_RUN_AS_NODE: "1" } } : {}),
183
- },
162
+ launch: { program: nodePath, args: [canonicalPath] },
184
163
  };
185
164
  }
186
165
 
@@ -42,7 +42,7 @@ function contextModeProbeOptions(env: NodeJS.ProcessEnv = process.env): ProbeOpt
42
42
 
43
43
  export interface ContextModeHandlerDependencies {
44
44
  now?: () => Date;
45
- resolveBinary?: typeof resolveContextModeBinary;
45
+ resolveBinary?: (settings: ContextModeSettings) => Promise<BinaryResolution>;
46
46
  inspect?: (launch: LaunchDescriptor, options: ProbeOptions) => Promise<McpInspection>;
47
47
  callTool?: (
48
48
  launch: LaunchDescriptor,
@@ -57,13 +57,14 @@ export interface ContextModeHandlerDependencies {
57
57
  ) => Promise<IntegrationAudit>;
58
58
  }
59
59
 
60
- type HandlerContext = { paseo?: ProviderConfigSource };
61
-
62
60
  export interface ContextModeHandlers {
63
- status(input: ContextModeRefreshInput, context?: HandlerContext): Promise<BinaryStatus>;
64
- doctor(input: ContextModeRefreshInput, context?: HandlerContext): Promise<CommandReport>;
65
- stats(input: ContextModeRefreshInput, context?: HandlerContext): Promise<CommandReport>;
66
- audit(input: ContextModeRefreshInput, context?: HandlerContext): Promise<IntegrationAudit>;
61
+ status(input: ContextModeRefreshInput): Promise<BinaryStatus>;
62
+ doctor(input: ContextModeRefreshInput): Promise<CommandReport>;
63
+ stats(input: ContextModeRefreshInput): Promise<CommandReport>;
64
+ audit(
65
+ input: ContextModeRefreshInput,
66
+ context?: { paseo?: ProviderConfigSource },
67
+ ): Promise<IntegrationAudit>;
67
68
  }
68
69
 
69
70
  interface CacheEntry<T> {
@@ -92,7 +93,7 @@ export function createContextModeHandlers(
92
93
  const cache = new Map<string, CacheEntry<BinaryStatus | CommandReport>>();
93
94
  const probe = contextModeProbeOptions();
94
95
 
95
- async function settingsAndBinary(paseo: ProviderConfigSource | undefined): Promise<
96
+ async function settingsAndBinary(): Promise<
96
97
  | {
97
98
  state: "ready";
98
99
  settings: ContextModeSettings;
@@ -114,7 +115,7 @@ export function createContextModeHandlers(
114
115
  },
115
116
  };
116
117
  }
117
- const binary = await resolveBinary(settings.values, { paseo });
118
+ const binary = await resolveBinary(settings.values);
118
119
  if (binary.state === "missing") {
119
120
  return {
120
121
  state: "unavailable",
@@ -158,11 +159,8 @@ export function createContextModeHandlers(
158
159
  return result;
159
160
  }
160
161
 
161
- async function status(
162
- { fresh }: ContextModeRefreshInput,
163
- context?: HandlerContext,
164
- ): Promise<BinaryStatus> {
165
- const resolved = await settingsAndBinary(context?.paseo);
162
+ async function status({ fresh }: ContextModeRefreshInput): Promise<BinaryStatus> {
163
+ const resolved = await settingsAndBinary();
166
164
  if (resolved.state === "unavailable") return resolved.report;
167
165
  const cacheKey = `status:${resolved.binary.source}:${resolved.binary.path}`;
168
166
  return cached(cacheKey, 10_000, fresh, async () => {
@@ -184,13 +182,8 @@ export function createContextModeHandlers(
184
182
  });
185
183
  }
186
184
 
187
- async function report(
188
- name: ToolName,
189
- ttlMs: number,
190
- fresh: boolean,
191
- context: HandlerContext | undefined,
192
- ): Promise<CommandReport> {
193
- const resolved = await settingsAndBinary(context?.paseo);
185
+ async function report(name: ToolName, ttlMs: number, fresh: boolean): Promise<CommandReport> {
186
+ const resolved = await settingsAndBinary();
194
187
  if (resolved.state === "unavailable") return resolved.report;
195
188
  return cached(`${name}:${resolved.binary.path}`, ttlMs, fresh, async () => {
196
189
  const checkedAt = now().toISOString();
@@ -209,9 +202,9 @@ export function createContextModeHandlers(
209
202
 
210
203
  async function audit(
211
204
  _input: ContextModeRefreshInput,
212
- context?: HandlerContext,
205
+ context?: { paseo?: ProviderConfigSource },
213
206
  ): Promise<IntegrationAudit> {
214
- const resolved = await settingsAndBinary(context?.paseo);
207
+ const resolved = await settingsAndBinary();
215
208
  if (resolved.state === "unavailable") {
216
209
  return {
217
210
  runtimePath: "",
@@ -234,8 +227,8 @@ export function createContextModeHandlers(
234
227
 
235
228
  return {
236
229
  status,
237
- doctor: ({ fresh }, context) => report("ctx_doctor", 10_000, fresh, context),
238
- stats: ({ fresh }, context) => report("ctx_stats", 5_000, fresh, context),
230
+ doctor: ({ fresh }) => report("ctx_doctor", 10_000, fresh),
231
+ stats: ({ fresh }) => report("ctx_stats", 5_000, fresh),
239
232
  audit,
240
233
  };
241
234
  }
@@ -63,7 +63,7 @@ export type ProviderOverrides = Readonly<
63
63
 
64
64
  /** Structural slice of the hook/RPC `paseo` API; avoids a client-package import. */
65
65
  export interface ProviderConfigSource {
66
- config: { get(): Promise<{ config: { providers?: unknown; plugins?: unknown } }> };
66
+ config: { get(): Promise<{ config: { providers?: unknown } }> };
67
67
  }
68
68
 
69
69
  /**
@@ -483,7 +483,7 @@ export async function injectContextModeOnCreate<T extends CreateRequest>(
483
483
  type: "stdio",
484
484
  command: launch.program,
485
485
  args: launch.args,
486
- env: { ...serverEnvironment(policy, storageRoot, request.env), ...launch.env },
486
+ env: serverEnvironment(policy, storageRoot, request.env),
487
487
  };
488
488
  return {
489
489
  ...request,
@@ -10,8 +10,8 @@ import type {
10
10
  PurgeKnowledgeInput,
11
11
  SearchKnowledgeInput,
12
12
  } from "../shared/knowledge";
13
- import type { contextModeSettings } from "../shared/settings";
14
- import { type LaunchDescriptor, resolveContextModeBinary } from "./binary";
13
+ import type { ContextModeSettings, contextModeSettings } from "../shared/settings";
14
+ import { type BinaryResolution, type LaunchDescriptor, resolveContextModeBinary } from "./binary";
15
15
  import {
16
16
  contextModeStorageRootFor,
17
17
  type ProviderConfigSource,
@@ -37,7 +37,7 @@ type KnowledgeToolCaller = (
37
37
 
38
38
  export interface KnowledgeHandlerDependencies {
39
39
  now?: () => Date;
40
- resolveBinary?: typeof resolveContextModeBinary;
40
+ resolveBinary?: (settings: ContextModeSettings) => Promise<BinaryResolution>;
41
41
  callTool?: KnowledgeToolCaller;
42
42
  }
43
43
 
@@ -99,14 +99,12 @@ export function createContextModeKnowledgeHandlers(
99
99
  const resolveBinary = dependencies.resolveBinary ?? resolveContextModeBinary;
100
100
  const callTool = dependencies.callTool ?? callContextModeTool;
101
101
 
102
- async function launchDescriptor(
103
- paseo: ProviderConfigSource | undefined,
104
- ): Promise<LaunchDescriptor> {
102
+ async function launchDescriptor(): Promise<LaunchDescriptor> {
105
103
  const settings = await settingsHandle.read();
106
104
  if (settings.status !== "ready") {
107
105
  throw new Error(`Context Mode settings are invalid: ${settings.error}`.slice(0, 4_096));
108
106
  }
109
- const binary = await resolveBinary(settings.values, { paseo });
107
+ const binary = await resolveBinary(settings.values);
110
108
  if (binary.state === "missing") throw new Error(binary.message.slice(0, 4_096));
111
109
  return binary.launch;
112
110
  }
@@ -119,7 +117,7 @@ export function createContextModeKnowledgeHandlers(
119
117
  context: KnowledgeContext | undefined,
120
118
  environmentOverrides: NodeJS.ProcessEnv = {},
121
119
  ): Promise<KnowledgeToolResult> {
122
- const launch = await launchDescriptor(context?.paseo);
120
+ const launch = await launchDescriptor();
123
121
  const providers = await readProviderOverrides(context?.paseo);
124
122
  const output = await callTool(launch, name, arguments_, {
125
123
  env: {
@@ -125,10 +125,7 @@ async function exchange(
125
125
  stdio: ["pipe", "pipe", "pipe"],
126
126
  windowsHide: true,
127
127
  cwd: dependencies.cwd,
128
- env:
129
- launch.env || dependencies.env
130
- ? { ...process.env, ...launch.env, ...dependencies.env }
131
- : undefined,
128
+ env: dependencies.env ? { ...process.env, ...dependencies.env } : undefined,
132
129
  });
133
130
  } catch (error) {
134
131
  throw new ProcessFailure(