@omercnet/paseo-context-mode 1.2.2 → 1.2.3-next.233.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 +11 -5
- package/npm-shrinkwrap.json +1758 -0
- package/package.json +8 -7
- package/server/handlers.ts +20 -7
- package/server/integration.ts +110 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omercnet/paseo-context-mode",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3-next.233.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Provider-aware Context Mode health, savings, and MCP activation for Paseo.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,10 +38,11 @@
|
|
|
38
38
|
"server",
|
|
39
39
|
"shared",
|
|
40
40
|
"paseo-plugin.json",
|
|
41
|
-
"OVERVIEW.md"
|
|
41
|
+
"OVERVIEW.md",
|
|
42
|
+
"npm-shrinkwrap.json"
|
|
42
43
|
],
|
|
43
44
|
"scripts": {
|
|
44
|
-
"check": "biome check .",
|
|
45
|
+
"check": "biome check . && node ../scripts/shrinkwrap.mjs --check .",
|
|
45
46
|
"check:write": "biome check --write .",
|
|
46
47
|
"test": "vitest run",
|
|
47
48
|
"test:unit": "vitest run",
|
|
@@ -51,10 +52,10 @@
|
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@biomejs/biome": "^2.5.10",
|
|
54
|
-
"@getpaseo/cli": "0.11.
|
|
55
|
-
"@getpaseo/client": "0.11.
|
|
56
|
-
"@getpaseo/plugin": "0.11.
|
|
57
|
-
"@getpaseo/protocol": "0.11.
|
|
55
|
+
"@getpaseo/cli": "0.11.1",
|
|
56
|
+
"@getpaseo/client": "0.11.1",
|
|
57
|
+
"@getpaseo/plugin": "0.11.1",
|
|
58
|
+
"@getpaseo/protocol": "0.11.1",
|
|
58
59
|
"@tanstack/react-query": "^5.102.3",
|
|
59
60
|
"@types/node": "^24.10.1",
|
|
60
61
|
"@types/react": "^19.2.18",
|
package/server/handlers.ts
CHANGED
|
@@ -8,7 +8,12 @@ import type {
|
|
|
8
8
|
IntegrationAudit,
|
|
9
9
|
} from "../shared";
|
|
10
10
|
import { type BinaryResolution, type LaunchDescriptor, resolveContextModeBinary } from "./binary";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
createIntegrationAudit,
|
|
13
|
+
type IntegrationDependencies,
|
|
14
|
+
type ProviderConfigSource,
|
|
15
|
+
readProviderOverrides,
|
|
16
|
+
} from "./integration";
|
|
12
17
|
import {
|
|
13
18
|
callContextModeTool,
|
|
14
19
|
inspectContextMode,
|
|
@@ -28,6 +33,7 @@ export interface ContextModeHandlerDependencies {
|
|
|
28
33
|
createAudit?: (
|
|
29
34
|
settings: ContextModeSettings,
|
|
30
35
|
runtime: { path: string; version: string | null },
|
|
36
|
+
dependencies?: IntegrationDependencies,
|
|
31
37
|
) => Promise<IntegrationAudit>;
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -35,7 +41,10 @@ export interface ContextModeHandlers {
|
|
|
35
41
|
status(input: ContextModeRefreshInput): Promise<BinaryStatus>;
|
|
36
42
|
doctor(input: ContextModeRefreshInput): Promise<CommandReport>;
|
|
37
43
|
stats(input: ContextModeRefreshInput): Promise<CommandReport>;
|
|
38
|
-
audit(
|
|
44
|
+
audit(
|
|
45
|
+
input: ContextModeRefreshInput,
|
|
46
|
+
context?: { paseo?: ProviderConfigSource },
|
|
47
|
+
): Promise<IntegrationAudit>;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
interface CacheEntry<T> {
|
|
@@ -170,7 +179,10 @@ export function createContextModeHandlers(
|
|
|
170
179
|
});
|
|
171
180
|
}
|
|
172
181
|
|
|
173
|
-
async function audit(
|
|
182
|
+
async function audit(
|
|
183
|
+
_input: ContextModeRefreshInput,
|
|
184
|
+
context?: { paseo?: ProviderConfigSource },
|
|
185
|
+
): Promise<IntegrationAudit> {
|
|
174
186
|
const resolved = await settingsAndBinary();
|
|
175
187
|
if (resolved.state === "unavailable") {
|
|
176
188
|
return {
|
|
@@ -185,10 +197,11 @@ export function createContextModeHandlers(
|
|
|
185
197
|
version: null,
|
|
186
198
|
tools: [],
|
|
187
199
|
}));
|
|
188
|
-
return buildAudit(
|
|
189
|
-
|
|
190
|
-
version: inspection.version,
|
|
191
|
-
|
|
200
|
+
return buildAudit(
|
|
201
|
+
resolved.settings,
|
|
202
|
+
{ path: resolved.binary.path, version: inspection.version },
|
|
203
|
+
{ providers: await readProviderOverrides(context?.paseo) },
|
|
204
|
+
);
|
|
192
205
|
}
|
|
193
206
|
|
|
194
207
|
return {
|
package/server/integration.ts
CHANGED
|
@@ -54,6 +54,52 @@ export interface IntegrationDependencies {
|
|
|
54
54
|
fileContains?: (path: string, text: string) => Promise<boolean>;
|
|
55
55
|
now?: () => Date;
|
|
56
56
|
env?: NodeJS.ProcessEnv;
|
|
57
|
+
providers?: ProviderOverrides;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type ProviderOverrides = Readonly<
|
|
61
|
+
Record<string, { extends?: string; env?: Readonly<Record<string, string>> }>
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
/** Structural slice of the hook/RPC `paseo` API; avoids a client-package import. */
|
|
65
|
+
export interface ProviderConfigSource {
|
|
66
|
+
config: { get(): Promise<{ config: { providers?: unknown } }> };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reads `agents.providers` from the daemon. Paseo applies a provider's own `env` and `extends`
|
|
71
|
+
* after the `agent.create` hook runs, so the hooks must look them up to find the right storage.
|
|
72
|
+
* Failures degrade to "no overrides" (canonical provider behavior).
|
|
73
|
+
*/
|
|
74
|
+
export async function readProviderOverrides(
|
|
75
|
+
paseo: ProviderConfigSource | undefined,
|
|
76
|
+
): Promise<ProviderOverrides> {
|
|
77
|
+
try {
|
|
78
|
+
const { config } = (await paseo?.config.get()) ?? {};
|
|
79
|
+
const entries: Array<[string, { extends?: string; env?: Record<string, string> }]> = [];
|
|
80
|
+
for (const [id, value] of Object.entries(config?.providers ?? {})) {
|
|
81
|
+
if (typeof value !== "object" || value === null) continue;
|
|
82
|
+
const { extends: parent, env } = value as { extends?: unknown; env?: unknown };
|
|
83
|
+
entries.push([
|
|
84
|
+
id,
|
|
85
|
+
{
|
|
86
|
+
...(typeof parent === "string" ? { extends: parent } : {}),
|
|
87
|
+
...(typeof env === "object" && env !== null && !Array.isArray(env)
|
|
88
|
+
? {
|
|
89
|
+
env: Object.fromEntries(
|
|
90
|
+
Object.entries(env).filter(
|
|
91
|
+
(entry): entry is [string, string] => typeof entry[1] === "string",
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
}
|
|
95
|
+
: {}),
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
return Object.fromEntries(entries);
|
|
100
|
+
} catch {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
57
103
|
}
|
|
58
104
|
|
|
59
105
|
export interface ContextModeStorageOptions {
|
|
@@ -241,7 +287,23 @@ const PROVIDER_POLICIES: Readonly<Record<Exclude<AuditedProvider, "omp-plugin">,
|
|
|
241
287
|
|
|
242
288
|
function providerPolicy(provider: string): ProviderPolicy | null {
|
|
243
289
|
if (provider === "omp-plugin" || provider.startsWith("omp-plugin-")) return OMP_POLICY;
|
|
244
|
-
return PROVIDER_POLICIES
|
|
290
|
+
return Object.hasOwn(PROVIDER_POLICIES, provider)
|
|
291
|
+
? PROVIDER_POLICIES[provider as keyof typeof PROVIDER_POLICIES]
|
|
292
|
+
: null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Mirrors Paseo's single-level `extends`: a derived provider uses its parent's policy, and its
|
|
297
|
+
* effective env is the parent's override env overlaid with its own.
|
|
298
|
+
*/
|
|
299
|
+
function resolveProvider(
|
|
300
|
+
id: string,
|
|
301
|
+
providers: ProviderOverrides = {},
|
|
302
|
+
): { base: string; policy: ProviderPolicy | null; env: Record<string, string> } {
|
|
303
|
+
const own = Object.hasOwn(providers, id) ? providers[id] : undefined;
|
|
304
|
+
const base = own?.extends ?? id;
|
|
305
|
+
const parent = base !== id && Object.hasOwn(providers, base) ? providers[base] : undefined;
|
|
306
|
+
return { base, policy: providerPolicy(base), env: { ...parent?.env, ...own?.env } };
|
|
245
307
|
}
|
|
246
308
|
|
|
247
309
|
async function defaultPathExists(path: string): Promise<boolean> {
|
|
@@ -322,24 +384,40 @@ export async function createIntegrationAudit(
|
|
|
322
384
|
const pathExists = dependencies.pathExists ?? defaultPathExists;
|
|
323
385
|
const fileContains = dependencies.fileContains ?? defaultFileContains;
|
|
324
386
|
const hostEnvironment = dependencies.env ?? process.env;
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
387
|
+
const nativeByPolicy = new Map<string, Promise<boolean>>();
|
|
388
|
+
const detectNative = (
|
|
389
|
+
policy: ProviderPolicy,
|
|
390
|
+
env: Readonly<Record<string, string | undefined>>,
|
|
391
|
+
key: string,
|
|
392
|
+
) => {
|
|
393
|
+
const cacheKey = `${policy.platform}\0${key}`;
|
|
394
|
+
const existing = nativeByPolicy.get(cacheKey);
|
|
329
395
|
if (existing) return existing;
|
|
330
|
-
const detected = nativeIntegrationDetected(
|
|
331
|
-
|
|
396
|
+
const detected = nativeIntegrationDetected(
|
|
397
|
+
policy,
|
|
398
|
+
providerContext(home, undefined, env),
|
|
399
|
+
pathExists,
|
|
400
|
+
fileContains,
|
|
401
|
+
);
|
|
402
|
+
nativeByPolicy.set(cacheKey, detected);
|
|
332
403
|
return detected;
|
|
333
404
|
};
|
|
405
|
+
const configured = dependencies.providers ?? {};
|
|
406
|
+
const derived = Object.keys(configured).filter(
|
|
407
|
+
(id) => !(AUDITED_PROVIDERS as readonly string[]).includes(id),
|
|
408
|
+
);
|
|
334
409
|
const providers: ProviderIntegration[] = [];
|
|
335
|
-
for (const provider of AUDITED_PROVIDERS) {
|
|
336
|
-
const policy =
|
|
410
|
+
for (const provider of [...AUDITED_PROVIDERS, ...derived]) {
|
|
411
|
+
const { base, policy, env: providerEnv } = resolveProvider(provider, configured);
|
|
337
412
|
if (!policy) continue;
|
|
338
|
-
const
|
|
413
|
+
const env = { ...hostEnvironment, ...providerEnv };
|
|
414
|
+
const storageRoot = contextModeStorageRootFor(base, { home, env });
|
|
339
415
|
if (!storageRoot) continue;
|
|
340
416
|
const reusesExistingStorage = await pathExists(storageRoot);
|
|
341
417
|
const native =
|
|
342
|
-
settings.autoInject &&
|
|
418
|
+
settings.autoInject &&
|
|
419
|
+
settings.preferNativeIntegrations &&
|
|
420
|
+
(await detectNative(policy, env, JSON.stringify(providerEnv)));
|
|
343
421
|
const activation = !settings.autoInject ? "disabled" : native ? "native" : "mcp";
|
|
344
422
|
providers.push({
|
|
345
423
|
provider,
|
|
@@ -377,34 +455,35 @@ export async function injectContextModeOnCreate<T extends CreateRequest>(
|
|
|
377
455
|
) {
|
|
378
456
|
return request;
|
|
379
457
|
}
|
|
380
|
-
const
|
|
458
|
+
const {
|
|
459
|
+
base,
|
|
460
|
+
policy,
|
|
461
|
+
env: providerEnv,
|
|
462
|
+
} = resolveProvider(request.config.provider, dependencies.providers);
|
|
381
463
|
if (!policy) return request;
|
|
382
464
|
const home = dependencies.home ?? homedir();
|
|
383
465
|
const hostEnvironment = dependencies.env ?? process.env;
|
|
384
466
|
const pathExists = dependencies.pathExists ?? defaultPathExists;
|
|
385
467
|
const fileContains = dependencies.fileContains ?? defaultFileContains;
|
|
386
|
-
const
|
|
387
|
-
...hostEnvironment,
|
|
388
|
-
...request.env,
|
|
389
|
-
});
|
|
468
|
+
const env = { ...hostEnvironment, ...providerEnv, ...request.env };
|
|
390
469
|
if (
|
|
391
470
|
settings.preferNativeIntegrations &&
|
|
392
|
-
(await nativeIntegrationDetected(
|
|
471
|
+
(await nativeIntegrationDetected(
|
|
472
|
+
policy,
|
|
473
|
+
providerContext(home, request.config.cwd, env),
|
|
474
|
+
pathExists,
|
|
475
|
+
fileContains,
|
|
476
|
+
))
|
|
393
477
|
) {
|
|
394
478
|
return request;
|
|
395
479
|
}
|
|
396
|
-
const storageRoot = contextModeStorageRootFor(request.config.
|
|
397
|
-
home,
|
|
398
|
-
cwd: request.config.cwd,
|
|
399
|
-
env: { ...hostEnvironment, ...request.env },
|
|
400
|
-
});
|
|
480
|
+
const storageRoot = contextModeStorageRootFor(base, { home, cwd: request.config.cwd, env });
|
|
401
481
|
if (!storageRoot) return request;
|
|
402
|
-
const env = serverEnvironment(policy, storageRoot, request.env);
|
|
403
482
|
const server: McpServer = {
|
|
404
483
|
type: "stdio",
|
|
405
484
|
command: launch.program,
|
|
406
485
|
args: launch.args,
|
|
407
|
-
env,
|
|
486
|
+
env: serverEnvironment(policy, storageRoot, request.env),
|
|
408
487
|
};
|
|
409
488
|
return {
|
|
410
489
|
...request,
|
|
@@ -421,14 +500,18 @@ export async function injectContextModeEnvironment<T extends SessionOpenRequest>
|
|
|
421
500
|
dependencies: IntegrationDependencies = {},
|
|
422
501
|
): Promise<T> {
|
|
423
502
|
if (!settings.autoInject) return request;
|
|
424
|
-
const
|
|
503
|
+
const {
|
|
504
|
+
base,
|
|
505
|
+
policy,
|
|
506
|
+
env: providerEnv,
|
|
507
|
+
} = resolveProvider(request.provider, dependencies.providers);
|
|
425
508
|
if (!policy) return request;
|
|
426
509
|
const home = dependencies.home ?? homedir();
|
|
427
510
|
const hostEnvironment = dependencies.env ?? process.env;
|
|
428
|
-
const storageRoot = contextModeStorageRootFor(
|
|
511
|
+
const storageRoot = contextModeStorageRootFor(base, {
|
|
429
512
|
home,
|
|
430
513
|
cwd: request.cwd,
|
|
431
|
-
env: { ...hostEnvironment, ...request.env },
|
|
514
|
+
env: { ...hostEnvironment, ...providerEnv, ...request.env },
|
|
432
515
|
});
|
|
433
516
|
if (!storageRoot) return request;
|
|
434
517
|
return {
|