@omercnet/paseo-context-mode 1.2.3 → 1.2.4-next.241.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/OVERVIEW.md CHANGED
@@ -1,4 +1,4 @@
1
- Context Mode connects Paseo agents to [Context Mode](https://github.com/mksglu/context-mode), a separate tool that runs as an MCP server and gives agents tools for indexing and searching project knowledge. The plugin bundles Context Mode 1.0.169 and adds a screen with health, savings, and a knowledge search and indexing panel. It supports the Claude, Codex, Copilot, Cursor, OpenCode, Pi, and OMP providers and leaves others unchanged. It needs Paseo 0.9 to 0.11 and Node.js 24 or later on the daemon host.
1
+ Context Mode connects Paseo agents to [Context Mode](https://github.com/mksglu/context-mode), a separate tool that runs as an MCP server and gives agents tools for indexing and searching project knowledge. The plugin bundles Context Mode 1.0.169 and adds a screen with health, savings, and a knowledge search and indexing panel. It supports the Claude, Codex, Copilot, Cursor, OpenCode, Pi, and OMP providers and leaves others unchanged. It needs Paseo `^0.11.0` and Node.js 24 or later on the daemon host.
2
2
 
3
3
  By default, creating a supported agent adds a Context Mode MCP server to it. Existing agents do not get the server, and opening or resuming a session only sets two environment variables that name the provider and its data folder, with a separate folder per provider. Turning off automatic activation stops the plugin adding these from then on, and it does not remove them from agents that already have them. A server you configured yourself is kept. By default the plugin also reads each provider's config files and skips its own addition when Context Mode is already registered there.
4
4
 
package/README.md CHANGED
@@ -22,19 +22,19 @@ These previews were captured from an isolated Paseo test daemon with Context Mod
22
22
 
23
23
  - Bundles Context Mode as a runtime dependency; a separately installed global executable remains optional.
24
24
  - Resolves a configured absolute executable first, then the daemon's `PATH`, then the bundled runtime.
25
- - Shows Context Mode version, structured diagnostics, provider activation, storage roots, lifetime savings, activity, categories, and indexed sources.
25
+ - Shows Context Mode version, structured diagnostics, provider activation, storage roots, lifetime savings, activity, categories, and indexed sources. Savings lists the built-in harness storage plus the storage of every provider configured in Paseo (including ones derived with `extends`), the latter under its Paseo provider id and the root its own `env` resolves to. `ctx_stats` and the Setup tab report the daemon's storage only.
26
26
  - Reads Context Mode 1.0.169 SQLite databases in query-only mode, honors active WAL state, isolates corrupt databases, and reports partial coverage instead of mutating schemas.
27
27
  - Injects the bundled stdio MCP server into new supported agents through Paseo's `agent.create` hook.
28
28
  - Injects provider identity and an existing provider-specific storage root on create, resume, refresh, and import.
29
29
  - Preserves existing MCP servers and explicit environment overrides.
30
30
  - Prefers detected native provider integrations to avoid duplicate `ctx_*` tools.
31
31
  - Reuses existing provider-specific databases. It does not merge session databases across providers because Context Mode intentionally isolates their session identities.
32
- - Provides provider- and project-scoped knowledge search, local-path indexing, URL fetch/index, indexed-source visibility, and an Insight link.
32
+ - Provides provider- and project-scoped knowledge search, local-path indexing, URL fetch/index, indexed-source visibility, and an Insight link. Knowledge covers the built-in providers and every configured provider that resolves to a Context Mode store, using that provider's own `env`.
33
33
  - Requires an explicit provider, project path, scope review, and second confirmation before purge. Purge remains delegated to Context Mode's own MCP contract.
34
34
  - Returns structured install and provider-specific upgrade argv for review; Paseo never executes those commands automatically.
35
- - On Paseo 0.11, registers the `context-mode` screen with URL-persisted `section`, `agentId`, and `workspaceId` params. Composer pills open Knowledge using the selected host's agent provider and working directory. URL params never carry paths or queries.
36
- - Custom or named providers that do not exactly match a Knowledge RPC provider require explicit provider selection. Missing agents or mismatched workspaces also require explicit scope selection.
37
- - Adds a live Healthy/Failing sidebar footer row on Paseo 0.11, reusing the screen's health data. Paseo 0.9/0.10 retain the static sidebar, Savings surface, and unscoped composer shortcut.
35
+ - Registers the `context-mode` screen with URL-persisted `section`, `agentId`, and `workspaceId` params. Composer pills open Knowledge using the selected host's agent provider and working directory. URL params never carry paths or queries.
36
+ - Providers that Context Mode does not support, and missing agents or mismatched workspaces, require explicit scope selection.
37
+ - Adds a live Healthy/Failing sidebar footer row, reusing the screen's health data.
38
38
 
39
39
  Context Mode currently couples sessions, stats, and indexed content under `CONTEXT_MODE_DIR`. Until upstream supports separate session and content roots, this plugin keeps storage provider-scoped instead of risking cross-provider session contamination.
40
40
 
@@ -57,7 +57,7 @@ Update an npm installation to the latest published release:
57
57
  paseo plugin update context-mode
58
58
  ```
59
59
 
60
- The npm install and update flow requires Paseo 0.9 or 0.10.1.
60
+ The npm install and update flow requires Paseo `^0.11.0` with npm available on the daemon host.
61
61
 
62
62
 
63
63
  ## Commands and limits
@@ -50,14 +50,10 @@ export function contributeContextModeComposerPills(client: PluginClientContext):
50
50
  behavior: {
51
51
  kind: "action",
52
52
  onPress() {
53
- if (typeof client.addScreen === "function" && typeof client.openScreen === "function") {
54
- client.openScreen({
55
- screenId: "context-mode",
56
- params: { section: "knowledge", agentId: agent.id, workspaceId: agent.workspaceId },
57
- });
58
- } else {
59
- client.openSurface("context-mode");
60
- }
53
+ client.openScreen({
54
+ screenId: "context-mode",
55
+ params: { section: "knowledge", agentId: agent.id, workspaceId: agent.workspaceId },
56
+ });
61
57
  },
62
58
  },
63
59
  },
@@ -13,7 +13,7 @@ import {
13
13
  } from "../shared";
14
14
  import { ActionsSection } from "./actions-section";
15
15
  import { AnalyticsDashboard } from "./analytics-dashboard";
16
- import { KnowledgeSection } from "./knowledge-section";
16
+ import { BUILTIN_KNOWLEDGE_PROVIDERS, KnowledgeSection } from "./knowledge-section";
17
17
  import { ScopedKnowledgeSection } from "./scoped-knowledge-section";
18
18
  import { parseScreenParams, type SurfaceTab } from "./screen-scope";
19
19
 
@@ -76,6 +76,10 @@ export function ContextModeSurface({
76
76
  refetchInterval: refreshInterval,
77
77
  enabled: readySettings !== null,
78
78
  });
79
+ const auditedProviders = integration.data?.providers.map(({ provider }) => provider) ?? [];
80
+ const knowledgeProviders = auditedProviders.length
81
+ ? auditedProviders
82
+ : BUILTIN_KNOWLEDGE_PROVIDERS;
79
83
  const refresh = useMutation({
80
84
  mutationFn: async () => {
81
85
  const nextStatus = await loadStatus({ fresh: true });
@@ -280,16 +284,25 @@ export function ContextModeSurface({
280
284
  />
281
285
  </View>
282
286
  <View style={[styles.tabPanel, activeTab === "knowledge" ? null : styles.tabPanelHidden]}>
283
- {activeTab !== "knowledge" ? null : screenParams.agentId || screenParams.workspaceId ? (
287
+ {activeTab !== "knowledge" ? null : integration.isLoading ? (
288
+ <Text style={{ color: theme.colors.foregroundMuted }}>Loading providers…</Text>
289
+ ) : screenParams.agentId || screenParams.workspaceId ? (
284
290
  <ScopedKnowledgeSection
285
291
  key={JSON.stringify([host.id, screenParams.agentId, screenParams.workspaceId])}
286
292
  params={screenParams}
287
293
  host={host}
288
294
  layout={layout}
289
295
  theme={theme}
296
+ providers={knowledgeProviders}
290
297
  />
291
298
  ) : (
292
- <KnowledgeSection key={host.id} host={host} layout={layout} theme={theme} />
299
+ <KnowledgeSection
300
+ key={host.id}
301
+ host={host}
302
+ layout={layout}
303
+ theme={theme}
304
+ providers={knowledgeProviders}
305
+ />
293
306
  )}
294
307
  </View>
295
308
  <View style={[styles.tabPanel, activeTab === "setup" ? null : styles.tabPanelHidden]}>
@@ -13,7 +13,6 @@ import {
13
13
  type ViewStyle,
14
14
  } from "react-native";
15
15
  import {
16
- type ContextModeProvider,
17
16
  fetchAndIndexContextMode,
18
17
  indexContextModePath,
19
18
  type KnowledgeToolResult,
@@ -22,22 +21,29 @@ import {
22
21
  } from "../shared/knowledge";
23
22
  import type { KnowledgeScope } from "./screen-scope";
24
23
 
25
- const PROVIDERS = [
26
- { value: "claude", label: "Claude" },
27
- { value: "codex", label: "Codex" },
28
- { value: "copilot", label: "Copilot" },
29
- { value: "cursor", label: "Cursor" },
30
- { value: "opencode", label: "OpenCode" },
31
- { value: "pi", label: "Pi" },
32
- { value: "omp", label: "OMP" },
33
- { value: "omp-plugin", label: "OMP plugin" },
34
- ] as const;
24
+ const PROVIDER_LABELS: Readonly<Record<string, string>> = {
25
+ claude: "Claude",
26
+ codex: "Codex",
27
+ copilot: "Copilot",
28
+ cursor: "Cursor",
29
+ opencode: "OpenCode",
30
+ pi: "Pi",
31
+ omp: "OMP",
32
+ "omp-plugin": "OMP plugin",
33
+ };
34
+
35
+ /** Used until the integration audit reports the configured providers. */
36
+ export const BUILTIN_KNOWLEDGE_PROVIDERS = Object.keys(PROVIDER_LABELS);
37
+
38
+ const providerLabel = (id: string) => PROVIDER_LABELS[id] ?? id;
35
39
 
36
40
  const INSIGHT_URL = "https://context-mode.com/insight";
37
- type Provider = ContextModeProvider;
41
+ type Provider = string;
38
42
  type PurgeScope = "project" | "session";
39
43
  export type KnowledgeSectionProps = Pick<PluginSurfaceProps, "theme" | "layout" | "host"> & {
40
44
  initialScope?: KnowledgeScope;
45
+ /** Paseo provider ids with a Context Mode store: the built-ins plus every configured provider. */
46
+ providers: readonly string[];
41
47
  };
42
48
 
43
49
  interface KnowledgeStyles {
@@ -238,7 +244,13 @@ function ResultPanel({
238
244
  );
239
245
  }
240
246
 
241
- export function KnowledgeSection({ theme, layout, host, initialScope }: KnowledgeSectionProps) {
247
+ export function KnowledgeSection({
248
+ theme,
249
+ layout,
250
+ host,
251
+ initialScope,
252
+ providers,
253
+ }: KnowledgeSectionProps) {
242
254
  const styles = useMemo(() => createStyles(theme, layout.compact), [layout.compact, theme]);
243
255
  const searchKnowledge = useRpc(searchContextModeKnowledge);
244
256
  const indexPath = useRpc(indexContextModePath);
@@ -250,8 +262,15 @@ export function KnowledgeSection({ theme, layout, host, initialScope }: Knowledg
250
262
  queryKey: ["context-mode", host.id, "analytics-dashboard"],
251
263
  });
252
264
 
265
+ const scopedProvider = initialScope?.provider ?? null;
266
+ const scopedProviderKnown = scopedProvider !== null && providers.includes(scopedProvider);
267
+ const note =
268
+ initialScope?.note ??
269
+ (scopedProvider && !scopedProviderKnown
270
+ ? `Provider ${scopedProvider} has no Context Mode store on this host. Select one explicitly.`
271
+ : undefined);
253
272
  const [provider, setProvider] = useState<Provider | null>(
254
- initialScope ? initialScope.provider : "claude",
273
+ initialScope ? (scopedProviderKnown ? scopedProvider : null) : "claude",
255
274
  );
256
275
  const [projectPath, setProjectPath] = useState(initialScope?.projectPath ?? "");
257
276
  const [query, setQuery] = useState("");
@@ -367,18 +386,18 @@ export function KnowledgeSection({ theme, layout, host, initialScope }: Knowledg
367
386
 
368
387
  <View style={styles.card}>
369
388
  <Text style={styles.fieldLabel}>Provider store</Text>
370
- {initialScope?.note ? <Text style={styles.scopeNote}>{initialScope.note}</Text> : null}
389
+ {note ? <Text style={styles.scopeNote}>{note}</Text> : null}
371
390
  <View accessibilityRole="radiogroup" style={styles.choiceRow}>
372
- {PROVIDERS.map((option) => {
373
- const selected = provider === option.value;
391
+ {providers.map((id) => {
392
+ const selected = provider === id;
374
393
  return (
375
394
  <Pressable
376
- key={option.value}
377
- accessibilityLabel={`${option.label} provider store`}
395
+ key={id}
396
+ accessibilityLabel={`${providerLabel(id)} provider store`}
378
397
  disabled={busy}
379
398
  accessibilityRole="radio"
380
399
  accessibilityState={{ checked: selected, disabled: busy }}
381
- onPress={() => changeProvider(option.value)}
400
+ onPress={() => changeProvider(id)}
382
401
  style={[
383
402
  styles.choice,
384
403
  selected ? styles.choiceSelected : null,
@@ -386,7 +405,7 @@ export function KnowledgeSection({ theme, layout, host, initialScope }: Knowledg
386
405
  ]}
387
406
  >
388
407
  <Text style={[styles.choiceText, selected ? styles.choiceTextSelected : null]}>
389
- {option.label}
408
+ {providerLabel(id)}
390
409
  </Text>
391
410
  </Pressable>
392
411
  );
@@ -406,7 +425,7 @@ export function KnowledgeSection({ theme, layout, host, initialScope }: Knowledg
406
425
  />
407
426
  <Text accessibilityLiveRegion="polite" style={styles.scopeNote}>
408
427
  {provider && projectPath.trim()
409
- ? `Current destination: ${PROVIDERS.find((item) => item.value === provider)?.label} storage for ${projectPath.trim()}.`
428
+ ? `Current destination: ${providerLabel(provider)} storage for ${projectPath.trim()}.`
410
429
  : "Select a provider and absolute project path. No storage destination is selected by default."}
411
430
  </Text>
412
431
  </View>
@@ -7,7 +7,7 @@ import { resolveKnowledgeScope, type ScreenParams } from "./screen-scope";
7
7
  export function ScopedKnowledgeSection({
8
8
  params,
9
9
  ...props
10
- }: PluginSurfaceProps & { params: ScreenParams }) {
10
+ }: PluginSurfaceProps & { params: ScreenParams; providers: readonly string[] }) {
11
11
  const paseo = usePaseo();
12
12
  const scope = useQuery({
13
13
  queryKey: [
@@ -40,7 +40,11 @@ export function ScopedKnowledgeSection({
40
40
  // Remount only when the resolved destination changes so stale form state never survives it.
41
41
  return (
42
42
  <KnowledgeSection
43
- key={JSON.stringify([scope.data.provider, scope.data.projectPath])}
43
+ key={JSON.stringify([
44
+ scope.data.provider,
45
+ scope.data.projectPath,
46
+ props.providers.includes(scope.data.provider ?? ""),
47
+ ])}
44
48
  {...props}
45
49
  initialScope={scope.data}
46
50
  />
@@ -1,11 +1,10 @@
1
1
  import type { PluginClientContext } from "@getpaseo/plugin/client";
2
- import { type ContextModeProvider, ContextModeProviderSchema } from "../shared/knowledge";
3
2
 
4
3
  type PaseoApi = PluginClientContext["paseo"];
5
4
 
6
5
  export type SurfaceTab = "savings" | "knowledge" | "setup";
7
6
  export type KnowledgeScope = {
8
- provider: ContextModeProvider | null;
7
+ provider: string | null;
9
8
  projectPath: string;
10
9
  note?: string;
11
10
  };
@@ -21,11 +20,6 @@ export function parseScreenParams(params: Record<string, string>): ScreenParams
21
20
  };
22
21
  }
23
22
 
24
- export function knowledgeProvider(provider: string): ContextModeProvider | null {
25
- const parsed = ContextModeProviderSchema.safeParse(provider);
26
- return parsed.success ? parsed.data : null;
27
- }
28
-
29
23
  export async function resolveKnowledgeScope(
30
24
  paseo: PaseoApi,
31
25
  params: ScreenParams,
@@ -39,16 +33,7 @@ export async function resolveKnowledgeScope(
39
33
  "Agent no longer belongs to this workspace. Select a provider and project explicitly.",
40
34
  );
41
35
  }
42
- const provider = knowledgeProvider(result.agent.provider);
43
- return {
44
- provider,
45
- projectPath: result.agent.cwd,
46
- ...(!provider
47
- ? {
48
- note: `Provider ${result.agent.provider} cannot be mapped to a knowledge store. Select one explicitly.`,
49
- }
50
- : {}),
51
- };
36
+ return { provider: result.agent.provider, projectPath: result.agent.cwd };
52
37
  }
53
38
  const workspace = params.workspaceId
54
39
  ? await paseo.workspaces.ref(params.workspaceId).refresh()
package/index.client.tsx CHANGED
@@ -1,26 +1,15 @@
1
- import type {
2
- PluginClientContext,
3
- PluginScreenProps,
4
- PluginSurfaceProps,
5
- } from "@getpaseo/plugin/client";
6
- import { SidebarRow } from "@getpaseo/plugin/client/ui";
1
+ import type { PluginClientContext, PluginScreenProps } from "@getpaseo/plugin/client";
7
2
  import { ContextModeFooter } from "./client/context-mode-footer";
8
3
  import { contributeContextModeComposerPills } from "./client/context-mode-pill";
9
4
  import { ContextModeSurface } from "./client/context-mode-surface";
10
5
  import { ContextModeSettingsScreen } from "./client/settings-screen";
11
6
 
12
7
  export default function contribute(client: PluginClientContext) {
13
- function Surface(props: PluginSurfaceProps) {
14
- return (
15
- <ContextModeSurface {...props} onOpenSettings={() => client.openSettings("context-mode")} />
16
- );
17
- }
18
8
  function Screen(props: PluginScreenProps) {
19
9
  return (
20
10
  <ContextModeSurface {...props} onOpenSettings={() => client.openSettings("context-mode")} />
21
11
  );
22
12
  }
23
- const screens = typeof client.addScreen === "function" && typeof client.openScreen === "function";
24
13
 
25
14
  const cleanups = [
26
15
  contributeContextModeComposerPills(client),
@@ -30,44 +19,20 @@ export default function contribute(client: PluginClientContext) {
30
19
  icon: "Settings",
31
20
  Component: ContextModeSettingsScreen,
32
21
  }),
33
- ...(screens
34
- ? [
35
- client.addScreen({ id: "context-mode", title: "Context Mode", Component: Screen }),
36
- ...(typeof client.addSidebarFooterItem === "function" && typeof SidebarRow === "function"
37
- ? [
38
- client.addSidebarFooterItem({
39
- id: "context-mode",
40
- title: "Context Mode",
41
- Component: ContextModeFooter,
42
- }),
43
- ]
44
- : [
45
- client.addSidebarItem({
46
- id: "context-mode",
47
- title: "Context Mode",
48
- icon: "Gauge",
49
- surface: "context-mode",
50
- }),
51
- ]),
52
- ]
53
- : [
54
- client.addSurface("context-mode", Surface),
55
- client.addSidebarItem({
56
- id: "context-mode",
57
- title: "Context Mode",
58
- icon: "Gauge",
59
- surface: "context-mode",
60
- }),
61
- ]),
22
+ client.addScreen({ id: "context-mode", title: "Context Mode", Component: Screen }),
23
+ client.addSidebarFooterItem({
24
+ id: "context-mode",
25
+ title: "Context Mode",
26
+ Component: ContextModeFooter,
27
+ }),
62
28
  client.addCommandCenterItem({
63
29
  id: "open-context-mode",
64
30
  title: "Open Context Mode",
65
31
  icon: "Gauge",
66
32
  keywords: ["context", "savings", "tokens", "knowledge", "search", "doctor", "health"],
67
33
  context: "global",
68
- onSelect({ openSurface, openScreen }) {
69
- if (screens && typeof openScreen === "function") openScreen({ screenId: "context-mode" });
70
- else openSurface("context-mode");
34
+ onSelect({ openScreen }) {
35
+ openScreen({ screenId: "context-mode" });
71
36
  },
72
37
  }),
73
38
  ];
package/index.server.ts CHANGED
@@ -3,7 +3,11 @@ import { createContextModeActionHandlers } from "./server/actions";
3
3
  import { createContextModeAnalyticsHandler } from "./server/analytics";
4
4
  import { resolveContextModeBinary } from "./server/binary";
5
5
  import { createContextModeHandlers } from "./server/handlers";
6
- import { injectContextModeEnvironment, injectContextModeOnCreate } from "./server/integration";
6
+ import {
7
+ injectContextModeEnvironment,
8
+ injectContextModeOnCreate,
9
+ readProviderOverrides,
10
+ } from "./server/integration";
7
11
  import { createContextModeKnowledgeHandlers } from "./server/knowledge";
8
12
  import {
9
13
  contextModeSettings,
@@ -38,17 +42,19 @@ export default function contribute(server: PluginServerContext) {
38
42
  server.handle(getContextModeDoctorReport, actionHandlers.doctor);
39
43
  server.handle(getContextModeInstallAction, actionHandlers.install);
40
44
  server.handle(getContextModeUpgradeAction, actionHandlers.upgrade);
41
- server.before("agent.create", async ({ request }) => {
45
+ server.before("agent.create", async ({ request }, { paseo }) => {
42
46
  const current = await settings.read();
43
47
  if (current.status !== "ready") return request;
44
48
  const binary = await resolveContextModeBinary(current.values);
45
49
  if (binary.state !== "found") return request;
46
- return injectContextModeOnCreate(request, current.values, binary.launch);
50
+ const providers = await readProviderOverrides(paseo);
51
+ return injectContextModeOnCreate(request, current.values, binary.launch, { providers });
47
52
  });
48
- server.before("agent.session_open", async ({ request }) => {
53
+ server.before("agent.session_open", async ({ request }, { paseo }) => {
49
54
  const current = await settings.read();
50
55
  if (current.status !== "ready") return request;
51
- return injectContextModeEnvironment(request, current.values);
56
+ const providers = await readProviderOverrides(paseo);
57
+ return injectContextModeEnvironment(request, current.values, { providers });
52
58
  });
53
59
  return () => {};
54
60
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-context-mode",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@omercnet/paseo-context-mode",
9
- "version": "1.2.3",
9
+ "version": "1.2.4",
10
10
  "dependencies": {
11
11
  "context-mode": "1.0.169"
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-context-mode",
3
- "version": "1.2.3",
3
+ "version": "1.2.4-next.241.1",
4
4
  "type": "module",
5
5
  "description": "Provider-aware Context Mode health, savings, and MCP activation for Paseo.",
6
6
  "license": "MIT",
package/paseo-plugin.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "id": "context-mode",
3
3
  "description": "Provider-aware Context Mode health, savings, and MCP activation for Paseo.",
4
- "requirements": { "paseo": "^0.9.0 || ^0.10.0 || ^0.11.0" }
4
+ "requirements": { "paseo": "^0.11.0" }
5
5
  }
@@ -16,6 +16,7 @@ import {
16
16
  readContextModeDatabase,
17
17
  type SessionDatabaseSnapshot,
18
18
  } from "./context-mode-db";
19
+ import { type ProviderConfigSource, readProviderOverrides } from "./integration";
19
20
  import {
20
21
  discoverContextModeStorageRoots,
21
22
  listRegularContextModeDatabases,
@@ -257,7 +258,11 @@ export async function collectContextModeAnalytics(
257
258
  const listDatabases = dependencies.listDatabases ?? listRegularContextModeDatabases;
258
259
  const readDatabase = dependencies.readDatabase ?? readContextModeDatabase;
259
260
  const warningCollector = new WarningCollector();
260
- const discovery = await discoverStorage({ home: dependencies.home, env: dependencies.env });
261
+ const discovery = await discoverStorage({
262
+ home: dependencies.home,
263
+ env: dependencies.env,
264
+ providers: dependencies.providers,
265
+ });
261
266
  for (const warning of discovery.warnings) warningCollector.add(warning);
262
267
 
263
268
  const total = createAggregate();
@@ -419,6 +424,13 @@ export async function collectContextModeAnalytics(
419
424
 
420
425
  export function createContextModeAnalyticsHandler(
421
426
  dependencies: ContextModeAnalyticsDependencies = {},
422
- ): (_input: Record<string, never>) => Promise<AnalyticsDashboard> {
423
- return async () => collectContextModeAnalytics(dependencies);
427
+ ): (
428
+ _input: Record<string, never>,
429
+ context?: { paseo?: ProviderConfigSource },
430
+ ) => Promise<AnalyticsDashboard> {
431
+ return async (_input, context) =>
432
+ collectContextModeAnalytics({
433
+ ...dependencies,
434
+ providers: dependencies.providers ?? (await readProviderOverrides(context?.paseo)),
435
+ });
424
436
  }
@@ -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 { createIntegrationAudit } from "./integration";
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(input: ContextModeRefreshInput): Promise<IntegrationAudit>;
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(_input: ContextModeRefreshInput): Promise<IntegrationAudit> {
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(resolved.settings, {
189
- path: resolved.binary.path,
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 {
@@ -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[provider as keyof typeof PROVIDER_POLICIES] ?? null;
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
+ export 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 context = providerContext(home, undefined, hostEnvironment);
326
- const nativeByPolicy = new Map<ProviderPolicy, Promise<boolean>>();
327
- const detectNative = (policy: ProviderPolicy) => {
328
- const existing = nativeByPolicy.get(policy);
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(policy, context, pathExists, fileContains);
331
- nativeByPolicy.set(policy, detected);
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 = providerPolicy(provider);
410
+ for (const provider of [...AUDITED_PROVIDERS, ...derived]) {
411
+ const { base, policy, env: providerEnv } = resolveProvider(provider, configured);
337
412
  if (!policy) continue;
338
- const storageRoot = contextModeStorageRootFor(provider, { home, env: hostEnvironment });
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 && settings.preferNativeIntegrations && (await detectNative(policy));
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 policy = providerPolicy(request.config.provider);
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 context = providerContext(home, request.config.cwd, {
387
- ...hostEnvironment,
388
- ...request.env,
389
- });
468
+ const env = { ...hostEnvironment, ...providerEnv, ...request.env };
390
469
  if (
391
470
  settings.preferNativeIntegrations &&
392
- (await nativeIntegrationDetected(policy, context, pathExists, fileContains))
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.provider, {
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 policy = providerPolicy(request.provider);
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(request.provider, {
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 {
@@ -12,7 +12,13 @@ import type {
12
12
  } from "../shared/knowledge";
13
13
  import type { ContextModeSettings, contextModeSettings } from "../shared/settings";
14
14
  import { type BinaryResolution, type LaunchDescriptor, resolveContextModeBinary } from "./binary";
15
- import { contextModeStorageRootFor } from "./integration";
15
+ import {
16
+ contextModeStorageRootFor,
17
+ type ProviderConfigSource,
18
+ type ProviderOverrides,
19
+ readProviderOverrides,
20
+ resolveProvider,
21
+ } from "./integration";
16
22
  import {
17
23
  type ContextModeToolName,
18
24
  callContextModeTool,
@@ -35,11 +41,16 @@ export interface KnowledgeHandlerDependencies {
35
41
  callTool?: KnowledgeToolCaller;
36
42
  }
37
43
 
44
+ type KnowledgeContext = { paseo?: ProviderConfigSource };
45
+
38
46
  export interface ContextModeKnowledgeHandlers {
39
- search(input: SearchKnowledgeInput): Promise<KnowledgeToolResult>;
40
- indexPath(input: IndexPathInput): Promise<KnowledgeToolResult>;
41
- fetchAndIndex(input: FetchAndIndexInput): Promise<KnowledgeToolResult>;
42
- purge(input: PurgeKnowledgeInput): Promise<KnowledgeToolResult>;
47
+ search(input: SearchKnowledgeInput, context?: KnowledgeContext): Promise<KnowledgeToolResult>;
48
+ indexPath(input: IndexPathInput, context?: KnowledgeContext): Promise<KnowledgeToolResult>;
49
+ fetchAndIndex(
50
+ input: FetchAndIndexInput,
51
+ context?: KnowledgeContext,
52
+ ): Promise<KnowledgeToolResult>;
53
+ purge(input: PurgeKnowledgeInput, context?: KnowledgeContext): Promise<KnowledgeToolResult>;
43
54
  }
44
55
 
45
56
  const PROVIDER_PLATFORMS: Readonly<Record<ContextModeProvider, ContextModePlatform>> = {
@@ -57,17 +68,21 @@ export function contextModePlatformFor(provider: ContextModeProvider): ContextMo
57
68
  return PROVIDER_PLATFORMS[provider];
58
69
  }
59
70
 
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
+ */
60
75
  export function contextModeEnvironmentFor(
61
- provider: ContextModeProvider,
76
+ provider: string,
62
77
  home = homedir(),
63
78
  env: NodeJS.ProcessEnv = process.env,
79
+ providers: ProviderOverrides = {},
64
80
  ): NodeJS.ProcessEnv {
65
- const storageRoot = contextModeStorageRootFor(provider, { home, env });
66
- if (!storageRoot) throw new Error(`Context Mode does not support provider ${provider}.`);
67
- return {
68
- CONTEXT_MODE_PLATFORM: contextModePlatformFor(provider),
69
- CONTEXT_MODE_DIR: storageRoot,
70
- };
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 };
71
86
  }
72
87
 
73
88
  function definedArguments(values: Record<string, unknown>): McpToolArguments {
@@ -95,42 +110,51 @@ export function createContextModeKnowledgeHandlers(
95
110
  }
96
111
 
97
112
  async function run(
98
- provider: ContextModeProvider,
113
+ provider: string,
99
114
  projectPath: string,
100
115
  name: ContextModeToolName,
101
116
  arguments_: McpToolArguments,
117
+ context: KnowledgeContext | undefined,
102
118
  environmentOverrides: NodeJS.ProcessEnv = {},
103
119
  ): Promise<KnowledgeToolResult> {
104
120
  const launch = await launchDescriptor();
121
+ const providers = await readProviderOverrides(context?.paseo);
105
122
  const output = await callTool(launch, name, arguments_, {
106
- env: { ...contextModeEnvironmentFor(provider), ...environmentOverrides },
123
+ env: {
124
+ ...contextModeEnvironmentFor(provider, homedir(), process.env, providers),
125
+ ...environmentOverrides,
126
+ },
107
127
  cwd: projectPath,
108
128
  });
109
129
  return { provider, output, completedAt: now().toISOString() };
110
130
  }
111
131
 
112
132
  return {
113
- search({ provider, projectPath, queries, limit, source, contentType, sort }) {
133
+ search({ provider, projectPath, queries, limit, source, contentType, sort }, context) {
114
134
  return run(
115
135
  provider,
116
136
  projectPath,
117
137
  "ctx_search",
118
138
  definedArguments({ queries, limit, source, contentType, sort }),
139
+ context,
119
140
  );
120
141
  },
121
- indexPath({
122
- provider,
123
- projectPath,
124
- path,
125
- source,
126
- include,
127
- exclude,
128
- maxDepth,
129
- maxFiles,
130
- extensions,
131
- respectGitignore,
132
- followSymlinks,
133
- }) {
142
+ indexPath(
143
+ {
144
+ provider,
145
+ projectPath,
146
+ path,
147
+ source,
148
+ include,
149
+ exclude,
150
+ maxDepth,
151
+ maxFiles,
152
+ extensions,
153
+ respectGitignore,
154
+ followSymlinks,
155
+ },
156
+ context,
157
+ ) {
134
158
  if (!isAbsolute(path)) {
135
159
  throw new Error("Knowledge indexing requires an absolute path on this host.");
136
160
  }
@@ -149,18 +173,20 @@ export function createContextModeKnowledgeHandlers(
149
173
  respectGitignore,
150
174
  followSymlinks,
151
175
  }),
176
+ context,
152
177
  );
153
178
  },
154
- fetchAndIndex({ provider, projectPath, url, source, force, ttl }) {
179
+ fetchAndIndex({ provider, projectPath, url, source, force, ttl }, context) {
155
180
  return run(
156
181
  provider,
157
182
  projectPath,
158
183
  "ctx_fetch_and_index",
159
184
  definedArguments({ url, source, force, ttl }),
185
+ context,
160
186
  { CTX_FETCH_STRICT: "1" },
161
187
  );
162
188
  },
163
- async purge(input) {
189
+ async purge(input, context) {
164
190
  const raw = input as PurgeKnowledgeInput & { sessionId?: string };
165
191
  if (raw.confirm !== true) {
166
192
  throw new Error("Purge requires exact confirmation with confirm: true.");
@@ -169,19 +195,24 @@ export function createContextModeKnowledgeHandlers(
169
195
  if (raw.sessionId !== undefined) {
170
196
  throw new Error("Purge requires exactly one scope.");
171
197
  }
172
- return await run(raw.provider, raw.projectPath, "ctx_purge", {
173
- confirm: true,
174
- scope: "project",
175
- });
198
+ return await run(
199
+ raw.provider,
200
+ raw.projectPath,
201
+ "ctx_purge",
202
+ { confirm: true, scope: "project" },
203
+ context,
204
+ );
176
205
  }
177
206
  if (raw.scope !== "session" || !raw.sessionId) {
178
207
  throw new Error("Session purge requires exactly one non-empty sessionId scope.");
179
208
  }
180
- return await run(raw.provider, raw.projectPath, "ctx_purge", {
181
- confirm: true,
182
- scope: "session",
183
- sessionId: raw.sessionId,
184
- });
209
+ return await run(
210
+ raw.provider,
211
+ raw.projectPath,
212
+ "ctx_purge",
213
+ { confirm: true, scope: "session", sessionId: raw.sessionId },
214
+ context,
215
+ );
185
216
  },
186
217
  };
187
218
  }
@@ -3,6 +3,7 @@ import { lstat, readdir, realpath } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { basename, isAbsolute, join, resolve } from "node:path";
5
5
  import type { AnalyticsWarning } from "../shared/analytics";
6
+ import { contextModeStorageRootFor, type ProviderOverrides, resolveProvider } from "./integration";
6
7
 
7
8
  const MAX_DATABASES_PER_DIRECTORY = 512;
8
9
  const MAX_LOCAL_DISCOVERY_WARNINGS = 10;
@@ -23,6 +24,8 @@ export interface StorageDiscoveryOptions {
23
24
  home?: string;
24
25
  env?: NodeJS.ProcessEnv;
25
26
  platform?: NodeJS.Platform;
27
+ /** Paseo `agents.providers`; each one contributes the storage its own env resolves to. */
28
+ providers?: ProviderOverrides;
26
29
  }
27
30
 
28
31
  interface ProviderLocation {
@@ -54,6 +57,7 @@ function providerLocations(
54
57
  env: NodeJS.ProcessEnv,
55
58
  warnings: AnalyticsWarning[],
56
59
  platform: NodeJS.Platform,
60
+ providers: ProviderOverrides,
57
61
  ): ProviderLocation[] {
58
62
  const configuredRoot = configuredDirectory(
59
63
  env.CONTEXT_MODE_DIR,
@@ -82,6 +86,14 @@ function providerLocations(
82
86
  : (configuredDirectory(env.XDG_CONFIG_HOME, home, "XDG_CONFIG_HOME", warnings) ??
83
87
  join(home, ".config"));
84
88
 
89
+ const configured = Object.keys(providers).flatMap((id) => {
90
+ const { base, policy, env: providerEnv } = resolveProvider(id, providers);
91
+ const root = policy
92
+ ? contextModeStorageRootFor(base, { home, env: { ...env, ...providerEnv } })
93
+ : null;
94
+ return root ? [{ provider: id, root }] : [];
95
+ });
96
+
85
97
  return [
86
98
  ...(configuredRoot ? [{ provider: "custom", root: configuredRoot }] : []),
87
99
  ...(configuredDataRoot
@@ -106,6 +118,7 @@ function providerLocations(
106
118
  provider: "jetbrains-copilot",
107
119
  root: join(home, ".config", "JetBrains", "context-mode"),
108
120
  },
121
+ ...configured,
109
122
  ];
110
123
  }
111
124
 
@@ -132,6 +145,7 @@ export async function discoverContextModeStorageRoots(
132
145
  env,
133
146
  warnings,
134
147
  options.platform ?? process.platform,
148
+ options.providers ?? {},
135
149
  )) {
136
150
  try {
137
151
  const info = await lstat(location.root);
@@ -27,6 +27,9 @@ export const ContextModePlatformSchema = z.enum([
27
27
  "omp",
28
28
  ]);
29
29
 
30
+ /** A Paseo provider id: a built-in or one configured in `agents.providers`. */
31
+ export const KnowledgeProviderSchema = z.string().trim().min(1).max(128);
32
+
30
33
  const boundedLabel = z.string().trim().min(1).max(MAX_LABEL_LENGTH);
31
34
  const boundedPattern = z.string().trim().min(1).max(256);
32
35
  const absoluteLocalPath = z
@@ -43,7 +46,7 @@ const absoluteLocalPath = z
43
46
  { message: "path must be an absolute local path" },
44
47
  );
45
48
  const providerScope = z
46
- .object({ provider: ContextModeProviderSchema, projectPath: absoluteLocalPath })
49
+ .object({ provider: KnowledgeProviderSchema, projectPath: absoluteLocalPath })
47
50
  .strict();
48
51
  const httpUrl = z
49
52
  .string()
@@ -55,7 +58,7 @@ const httpUrl = z
55
58
 
56
59
  export const KnowledgeToolResultSchema = z
57
60
  .object({
58
- provider: ContextModeProviderSchema,
61
+ provider: KnowledgeProviderSchema,
59
62
  output: z.string().max(MAX_OUTPUT_LENGTH),
60
63
  completedAt: z.string().datetime(),
61
64
  })
@@ -96,7 +99,7 @@ export const FetchAndIndexInputSchema = providerScope
96
99
 
97
100
  const projectPurge = z
98
101
  .object({
99
- provider: ContextModeProviderSchema,
102
+ provider: KnowledgeProviderSchema,
100
103
  projectPath: absoluteLocalPath,
101
104
  confirm: z.literal(true),
102
105
  scope: z.literal("project"),
@@ -104,7 +107,7 @@ const projectPurge = z
104
107
  .strict();
105
108
  const sessionPurge = z
106
109
  .object({
107
- provider: ContextModeProviderSchema,
110
+ provider: KnowledgeProviderSchema,
108
111
  projectPath: absoluteLocalPath,
109
112
  confirm: z.literal(true),
110
113
  scope: z.literal("session"),