@omercnet/paseo-context-mode 1.2.3 → 1.2.4-next.236.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/README.md +3 -3
- package/client/context-mode-surface.tsx +16 -3
- package/client/knowledge-section.tsx +41 -22
- package/client/scoped-knowledge-section.tsx +6 -2
- package/client/screen-scope.ts +2 -17
- package/index.server.ts +11 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/server/analytics.ts +15 -3
- package/server/handlers.ts +20 -7
- package/server/integration.ts +110 -27
- package/server/knowledge.ts +70 -39
- package/server/storage-discovery.ts +14 -0
- package/shared/knowledge.ts +7 -4
package/README.md
CHANGED
|
@@ -22,18 +22,18 @@ 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
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
|
-
-
|
|
36
|
+
- Providers that Context Mode does not support, and missing agents or mismatched workspaces, require explicit scope selection.
|
|
37
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.
|
|
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.
|
|
@@ -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 :
|
|
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
|
|
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
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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 =
|
|
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({
|
|
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 ?
|
|
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
|
-
{
|
|
389
|
+
{note ? <Text style={styles.scopeNote}>{note}</Text> : null}
|
|
371
390
|
<View accessibilityRole="radiogroup" style={styles.choiceRow}>
|
|
372
|
-
{
|
|
373
|
-
const selected = provider ===
|
|
391
|
+
{providers.map((id) => {
|
|
392
|
+
const selected = provider === id;
|
|
374
393
|
return (
|
|
375
394
|
<Pressable
|
|
376
|
-
key={
|
|
377
|
-
accessibilityLabel={`${
|
|
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(
|
|
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
|
-
{
|
|
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: ${
|
|
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([
|
|
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
|
/>
|
package/client/screen-scope.ts
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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.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 {
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
const providers = await readProviderOverrides(paseo);
|
|
57
|
+
return injectContextModeEnvironment(request, current.values, { providers });
|
|
52
58
|
});
|
|
53
59
|
return () => {};
|
|
54
60
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omercnet/paseo-context-mode",
|
|
3
|
-
"version": "1.2.
|
|
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.
|
|
9
|
+
"version": "1.2.4",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"context-mode": "1.0.169"
|
|
12
12
|
}
|
package/package.json
CHANGED
package/server/analytics.ts
CHANGED
|
@@ -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({
|
|
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
|
-
): (
|
|
423
|
-
|
|
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
|
}
|
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
|
+
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
|
|
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 {
|
package/server/knowledge.ts
CHANGED
|
@@ -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 {
|
|
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(
|
|
42
|
-
|
|
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:
|
|
76
|
+
provider: string,
|
|
62
77
|
home = homedir(),
|
|
63
78
|
env: NodeJS.ProcessEnv = process.env,
|
|
79
|
+
providers: ProviderOverrides = {},
|
|
64
80
|
): NodeJS.ProcessEnv {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
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: {
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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(
|
|
173
|
-
|
|
174
|
-
|
|
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(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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);
|
package/shared/knowledge.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
110
|
+
provider: KnowledgeProviderSchema,
|
|
108
111
|
projectPath: absoluteLocalPath,
|
|
109
112
|
confirm: z.literal(true),
|
|
110
113
|
scope: z.literal("session"),
|