pi-vault-mind 0.8.0 → 0.8.3
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/CHANGELOG.md +20 -0
- package/README.md +44 -44
- package/agents/broadcaster.agent.md +30 -0
- package/agents/heavy-lifter.agent.md +30 -0
- package/agents/manager.agent.md +29 -0
- package/agents/miner.agent.md +29 -0
- package/dist/src/agent-bus/Agent.d.ts +49 -0
- package/dist/src/agent-bus/Agent.js +97 -0
- package/dist/src/agent-bus/AgentLoader.d.ts +33 -0
- package/dist/src/agent-bus/AgentLoader.js +102 -0
- package/dist/src/agent-bus/AgentRegistry.d.ts +17 -0
- package/dist/src/agent-bus/AgentRegistry.js +20 -0
- package/dist/src/agent-bus/LLMProvider.d.ts +36 -0
- package/dist/src/agent-bus/LLMProvider.js +125 -0
- package/dist/src/agent-bus/MessageBus.d.ts +47 -0
- package/dist/src/agent-bus/MessageBus.js +81 -0
- package/dist/src/agent-bus/TaskQueue.d.ts +27 -0
- package/dist/src/agent-bus/TaskQueue.js +57 -0
- package/dist/src/agent-bus/index.d.ts +6 -0
- package/dist/src/agent-bus/index.js +6 -0
- package/dist/src/agents/BroadcasterAgent.d.ts +19 -0
- package/dist/src/agents/BroadcasterAgent.js +70 -0
- package/dist/src/agents/HeavyLifterAgent.d.ts +26 -0
- package/dist/src/agents/HeavyLifterAgent.js +73 -0
- package/dist/src/agents/ManagerAgent.d.ts +20 -0
- package/dist/src/agents/ManagerAgent.js +71 -0
- package/dist/src/agents/MinerAgent.d.ts +19 -0
- package/dist/src/agents/MinerAgent.js +70 -0
- package/dist/src/agents/index.d.ts +4 -0
- package/dist/src/agents/index.js +4 -0
- package/dist/src/auth.d.ts +49 -2
- package/dist/src/auth.js +149 -7
- package/dist/src/autosync.d.ts +1 -1
- package/dist/src/autosync.js +3 -3
- package/dist/src/bridge.d.ts +45 -0
- package/dist/src/bridge.js +189 -0
- package/dist/src/commands.d.ts +1 -1
- package/dist/src/commands.js +419 -295
- package/dist/src/context-capture.d.ts +22 -0
- package/dist/src/context-capture.js +41 -0
- package/dist/src/discover-schema.d.ts +1 -1
- package/dist/src/discover-schema.js +4 -4
- package/dist/src/embed-queue.d.ts +1 -1
- package/dist/src/embed-queue.js +1 -1
- package/dist/src/engine.d.ts +65 -0
- package/dist/src/engine.js +244 -0
- package/dist/src/events.js +1 -1
- package/dist/src/graph.d.ts +3 -3
- package/dist/src/identity-injector.d.ts +15 -0
- package/dist/src/identity-injector.js +102 -0
- package/dist/src/index.js +88 -49
- package/dist/src/intake.js +4 -4
- package/dist/src/lance.d.ts +11 -37
- package/dist/src/lance.js +183 -240
- package/dist/src/modal-client.d.ts +14 -4
- package/dist/src/modal-client.js +50 -34
- package/dist/src/modal-config.d.ts +26 -10
- package/dist/src/modal-config.js +36 -5
- package/dist/src/personalize.d.ts +47 -0
- package/dist/src/personalize.js +352 -0
- package/dist/src/server.d.ts +30 -4
- package/dist/src/server.js +146 -64
- package/dist/src/settings-ui.d.ts +3 -1
- package/dist/src/settings-ui.js +84 -77
- package/dist/src/status.d.ts +25 -0
- package/dist/src/status.js +49 -0
- package/dist/src/sync.d.ts +1 -1
- package/dist/src/sync.js +19 -19
- package/dist/src/tombstone.js +2 -2
- package/dist/src/tools.js +112 -130
- package/dist/src/types.d.ts +86 -18
- package/dist/src/types.js +7 -4
- package/dist/src/utils.d.ts +5 -1
- package/dist/src/utils.js +24 -8
- package/dist/src/vault-tools.d.ts +29 -0
- package/dist/src/vault-tools.js +314 -0
- package/dist/src/vault-writer.d.ts +4 -5
- package/dist/src/vault-writer.js +13 -13
- package/dist/src/vm-handlers.d.ts +15 -0
- package/dist/src/vm-handlers.js +469 -0
- package/dist/src/watcher.d.ts +18 -4
- package/dist/src/watcher.js +109 -98
- package/dist/test/auth.test.js +57 -7
- package/dist/test/bridge-identity.test.js +388 -0
- package/dist/test/discover-schema.test.js +2 -2
- package/dist/test/engine-identity.test.js +119 -0
- package/dist/test/index.test.js +5 -6
- package/dist/test/lance-modal.test.js +7 -7
- package/dist/test/modal-config.test.js +51 -29
- package/dist/test/personalize.test.js +212 -0
- package/dist/test/rest-queue.test.js +1 -1
- package/dist/test/rest-setup.test.js +21 -21
- package/dist/test/{rest-wiki.test.js → rest-vm.test.js} +27 -23
- package/dist/test/server-ws.test.js +4 -4
- package/dist/test/sync.test.js +2 -2
- package/dist/test/tombstone.test.js +1 -1
- package/dist/test/vault-tools.test.js +224 -0
- package/package.json +2 -1
- package/scripts/check-deps.sh +207 -0
- package/scripts/e2e-ws.mjs +4 -4
- package/scripts/modal-e2e-smoke.mjs +1 -1
- package/scripts/setup-test-config.sh +1 -5
- package/scripts/test-slash-commands.mjs +197 -0
- package/skills/.agents/broadcaster.agent.md +31 -0
- package/skills/.agents/heavy-lifter.agent.md +31 -0
- package/skills/.agents/manager.agent.md +30 -0
- package/skills/.agents/miner.agent.md +30 -0
- package/skills/vault-mind/SKILL.md +50 -50
- package/skills/vault-mind/references/tool-reference.md +25 -25
- package/skills/vault-mind-broadcaster/SKILL.md +1 -1
- package/skills/vault-mind-heavy-lifter/SKILL.md +3 -3
- package/skills/vault-mind-manager/SKILL.md +7 -7
- package/skills/vault-mind-miner/SKILL.md +11 -11
- package/skills/vault-mind-setup/SKILL.md +65 -265
- package/skills/vault-mind-setup/references/obsidian-vault-structure.md +6 -6
- package/skills/vault-mind-setup/references/pi-extension-wiring.md +24 -33
- package/skills/vault-mind-setup/references/troubleshooting-tree.md +25 -26
- package/dist/src/auto-index.d.ts +0 -47
- package/dist/src/auto-index.js +0 -122
|
@@ -13,13 +13,17 @@
|
|
|
13
13
|
* This client is the typed mirror of the server contract. The server (Agent A)
|
|
14
14
|
* owns it; additive changes here are mirrored in the server's `modal/web.py`.
|
|
15
15
|
* The local wiring lives in `src/lance.ts` (provider), `src/sync.ts`, and
|
|
16
|
-
* `/
|
|
16
|
+
* `/vm remote` commands (see docs/MODAL_EMBEDDING.md "Local integration").
|
|
17
17
|
*/
|
|
18
18
|
export interface ModalClientConfig {
|
|
19
19
|
/** Base URL of the deployed ASGI app (no trailing slash needed). */
|
|
20
20
|
baseUrl: string;
|
|
21
|
-
/**
|
|
22
|
-
apiToken
|
|
21
|
+
/** Fallback bearer token (legacy single token for all operations). */
|
|
22
|
+
apiToken?: string;
|
|
23
|
+
/** Read-scoped token for search/export operations. Falls back to `apiToken`. */
|
|
24
|
+
readToken?: string;
|
|
25
|
+
/** Write-scoped token for embed/reindex operations. Falls back to `apiToken`. */
|
|
26
|
+
writeToken?: string;
|
|
23
27
|
/** Per-request timeout in ms (default 120s — bulk submits can be large). */
|
|
24
28
|
timeoutMs?: number;
|
|
25
29
|
}
|
|
@@ -101,8 +105,12 @@ export interface ExportPage {
|
|
|
101
105
|
export declare class ModalEmbeddingClient {
|
|
102
106
|
private baseUrl;
|
|
103
107
|
private apiToken;
|
|
108
|
+
private readToken;
|
|
109
|
+
private writeToken;
|
|
104
110
|
private timeoutMs;
|
|
105
111
|
constructor(cfg: ModalClientConfig);
|
|
112
|
+
/** Return the bearer token to use for the requested operation scope. */
|
|
113
|
+
private tokenFor;
|
|
106
114
|
private request;
|
|
107
115
|
/** Liveness check; also returns the server's default model. */
|
|
108
116
|
health(): Promise<{
|
|
@@ -128,7 +136,7 @@ export declare class ModalEmbeddingClient {
|
|
|
128
136
|
}): Promise<JobSubmitResult>;
|
|
129
137
|
jobStatus(jobId: string): Promise<JobStatus>;
|
|
130
138
|
/** List recent jobs (newest first). Additive — surfaces GET /jobs so
|
|
131
|
-
* `/
|
|
139
|
+
* `/vm remote jobs` can list, not just poll a known id. (Agent B request #1.) */
|
|
132
140
|
listJobs(limit?: number): Promise<JobListResponse>;
|
|
133
141
|
/** Cooperatively cancel a running/queued job. The worker stops after its
|
|
134
142
|
* current batch and writes status=cancelled. */
|
|
@@ -147,6 +155,8 @@ export declare class ModalEmbeddingClient {
|
|
|
147
155
|
since?: number;
|
|
148
156
|
limit?: number;
|
|
149
157
|
}): Promise<ExportPage>;
|
|
158
|
+
/** Low-level binary request helper with scoped token selection. */
|
|
159
|
+
private requestBuffer;
|
|
150
160
|
/** Pull one page of rows with seq > since as an Arrow IPC stream.
|
|
151
161
|
* Vectors are always included (no include_vectors flag). The watermark /
|
|
152
162
|
* done / count come back as response headers (X-Next-Watermark, X-Done,
|
package/dist/src/modal-client.js
CHANGED
|
@@ -13,25 +13,36 @@
|
|
|
13
13
|
* This client is the typed mirror of the server contract. The server (Agent A)
|
|
14
14
|
* owns it; additive changes here are mirrored in the server's `modal/web.py`.
|
|
15
15
|
* The local wiring lives in `src/lance.ts` (provider), `src/sync.ts`, and
|
|
16
|
-
* `/
|
|
16
|
+
* `/vm remote` commands (see docs/MODAL_EMBEDDING.md "Local integration").
|
|
17
17
|
*/
|
|
18
18
|
export class ModalEmbeddingClient {
|
|
19
19
|
baseUrl;
|
|
20
20
|
apiToken;
|
|
21
|
+
readToken;
|
|
22
|
+
writeToken;
|
|
21
23
|
timeoutMs;
|
|
22
24
|
constructor(cfg) {
|
|
23
25
|
this.baseUrl = cfg.baseUrl.replace(/\/$/, "");
|
|
24
26
|
this.apiToken = cfg.apiToken;
|
|
27
|
+
this.readToken = cfg.readToken ?? cfg.apiToken;
|
|
28
|
+
this.writeToken = cfg.writeToken ?? cfg.apiToken;
|
|
25
29
|
this.timeoutMs = cfg.timeoutMs ?? 120_000;
|
|
26
30
|
}
|
|
27
|
-
|
|
31
|
+
/** Return the bearer token to use for the requested operation scope. */
|
|
32
|
+
tokenFor(operation) {
|
|
33
|
+
const token = operation === "read" ? this.readToken : this.writeToken;
|
|
34
|
+
if (!token)
|
|
35
|
+
throw new Error(`Modal ${operation} token not configured`);
|
|
36
|
+
return token;
|
|
37
|
+
}
|
|
38
|
+
async request(method, path, body, operation = "write") {
|
|
28
39
|
const controller = new AbortController();
|
|
29
40
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
30
41
|
try {
|
|
31
42
|
const resp = await fetch(`${this.baseUrl}${path}`, {
|
|
32
43
|
method,
|
|
33
44
|
headers: {
|
|
34
|
-
Authorization: `Bearer ${this.
|
|
45
|
+
Authorization: `Bearer ${this.tokenFor(operation)}`,
|
|
35
46
|
...(body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
36
47
|
},
|
|
37
48
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
@@ -49,17 +60,17 @@ export class ModalEmbeddingClient {
|
|
|
49
60
|
}
|
|
50
61
|
/** Liveness check; also returns the server's default model. */
|
|
51
62
|
health() {
|
|
52
|
-
return this.request("GET", "/health");
|
|
63
|
+
return this.request("GET", "/health", undefined, "read");
|
|
53
64
|
}
|
|
54
65
|
/** Registry of available embedders (public; no auth). Use native_dim to
|
|
55
66
|
* resolve a model's output dim up-front instead of waiting for the first
|
|
56
67
|
* /embed response. (Additive — Agent B request #2.) */
|
|
57
68
|
models() {
|
|
58
|
-
return this.request("GET", "/models");
|
|
69
|
+
return this.request("GET", "/models", undefined, "read");
|
|
59
70
|
}
|
|
60
71
|
/** Server-side store + compute stats (rows per namespace, index state, GPU). */
|
|
61
72
|
stats() {
|
|
62
|
-
return this.request("GET", "/stats");
|
|
73
|
+
return this.request("GET", "/stats", undefined, "read");
|
|
63
74
|
}
|
|
64
75
|
/** Embed text on demand. Use task="query" for search, "document" for storage. */
|
|
65
76
|
embed(texts, opts = {}) {
|
|
@@ -68,7 +79,7 @@ export class ModalEmbeddingClient {
|
|
|
68
79
|
model: opts.model,
|
|
69
80
|
dim: opts.dim,
|
|
70
81
|
task: opts.task ?? "query",
|
|
71
|
-
});
|
|
82
|
+
}, "write");
|
|
72
83
|
}
|
|
73
84
|
/** Submit a bulk embedding job; embeds + stores server-side. */
|
|
74
85
|
submitJob(collection, records, opts = {}) {
|
|
@@ -77,24 +88,24 @@ export class ModalEmbeddingClient {
|
|
|
77
88
|
records,
|
|
78
89
|
model: opts.model,
|
|
79
90
|
dim: opts.dim,
|
|
80
|
-
});
|
|
91
|
+
}, "write");
|
|
81
92
|
}
|
|
82
93
|
jobStatus(jobId) {
|
|
83
|
-
return this.request("GET", `/jobs/${encodeURIComponent(jobId)}
|
|
94
|
+
return this.request("GET", `/jobs/${encodeURIComponent(jobId)}`, undefined, "read");
|
|
84
95
|
}
|
|
85
96
|
/** List recent jobs (newest first). Additive — surfaces GET /jobs so
|
|
86
|
-
* `/
|
|
97
|
+
* `/vm remote jobs` can list, not just poll a known id. (Agent B request #1.) */
|
|
87
98
|
listJobs(limit) {
|
|
88
99
|
const p = new URLSearchParams();
|
|
89
100
|
if (limit != null)
|
|
90
101
|
p.set("limit", String(limit));
|
|
91
102
|
const qs = p.toString();
|
|
92
|
-
return this.request("GET", `/jobs${qs ? `?${qs}` : ""}
|
|
103
|
+
return this.request("GET", `/jobs${qs ? `?${qs}` : ""}`, undefined, "read");
|
|
93
104
|
}
|
|
94
105
|
/** Cooperatively cancel a running/queued job. The worker stops after its
|
|
95
106
|
* current batch and writes status=cancelled. */
|
|
96
107
|
cancelJob(jobId) {
|
|
97
|
-
return this.request("POST", `/jobs/${encodeURIComponent(jobId)}/cancel
|
|
108
|
+
return this.request("POST", `/jobs/${encodeURIComponent(jobId)}/cancel`, undefined, "write");
|
|
98
109
|
}
|
|
99
110
|
/** Poll a job until it reaches a terminal state. */
|
|
100
111
|
async waitForJob(jobId, pollMs = 2000) {
|
|
@@ -107,7 +118,7 @@ export class ModalEmbeddingClient {
|
|
|
107
118
|
}
|
|
108
119
|
/** List the collections/tables held in the server-side vector store. */
|
|
109
120
|
async syncCollections() {
|
|
110
|
-
const out = await this.request("GET", "/sync/collections");
|
|
121
|
+
const out = await this.request("GET", "/sync/collections", undefined, "read");
|
|
111
122
|
return out.collections;
|
|
112
123
|
}
|
|
113
124
|
/** Pull one page of rows with seq > since. Remember next_watermark. */
|
|
@@ -119,7 +130,27 @@ export class ModalEmbeddingClient {
|
|
|
119
130
|
p.set("dim", String(opts.dim));
|
|
120
131
|
p.set("since", String(opts.since ?? 0));
|
|
121
132
|
p.set("limit", String(opts.limit ?? 500));
|
|
122
|
-
return this.request("GET", `/sync/export?${p.toString()}
|
|
133
|
+
return this.request("GET", `/sync/export?${p.toString()}`, undefined, "read");
|
|
134
|
+
}
|
|
135
|
+
/** Low-level binary request helper with scoped token selection. */
|
|
136
|
+
async requestBuffer(method, path, operation = "read") {
|
|
137
|
+
const controller = new AbortController();
|
|
138
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
139
|
+
try {
|
|
140
|
+
const resp = await fetch(`${this.baseUrl}${path}`, {
|
|
141
|
+
method,
|
|
142
|
+
headers: { Authorization: `Bearer ${this.tokenFor(operation)}` },
|
|
143
|
+
signal: controller.signal,
|
|
144
|
+
});
|
|
145
|
+
if (!resp.ok) {
|
|
146
|
+
const detail = await resp.text().catch(() => "");
|
|
147
|
+
throw new Error(`Modal ${method} ${path} failed: ${resp.status} ${detail}`);
|
|
148
|
+
}
|
|
149
|
+
return resp;
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
}
|
|
123
154
|
}
|
|
124
155
|
/** Pull one page of rows with seq > since as an Arrow IPC stream.
|
|
125
156
|
* Vectors are always included (no include_vectors flag). The watermark /
|
|
@@ -135,26 +166,11 @@ export class ModalEmbeddingClient {
|
|
|
135
166
|
p.set("dim", String(opts.dim));
|
|
136
167
|
p.set("since", String(opts.since ?? 0));
|
|
137
168
|
p.set("limit", String(opts.limit ?? 500));
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
headers: { Authorization: `Bearer ${this.apiToken}` },
|
|
144
|
-
signal: controller.signal,
|
|
145
|
-
});
|
|
146
|
-
if (!resp.ok) {
|
|
147
|
-
const detail = await resp.text().catch(() => "");
|
|
148
|
-
throw new Error(`Modal GET /sync/export failed: ${resp.status} ${detail}`);
|
|
149
|
-
}
|
|
150
|
-
const nextWatermark = Number(resp.headers.get("X-Next-Watermark") ?? opts.since ?? 0);
|
|
151
|
-
const done = (resp.headers.get("X-Done") ?? "true") === "true";
|
|
152
|
-
const count = Number(resp.headers.get("X-Count") ?? 0);
|
|
153
|
-
return { data: await resp.arrayBuffer(), nextWatermark, done, count };
|
|
154
|
-
}
|
|
155
|
-
finally {
|
|
156
|
-
clearTimeout(timer);
|
|
157
|
-
}
|
|
169
|
+
const resp = await this.requestBuffer("GET", `/sync/export?${p.toString()}`, "read");
|
|
170
|
+
const nextWatermark = Number(resp.headers.get("X-Next-Watermark") ?? opts.since ?? 0);
|
|
171
|
+
const done = (resp.headers.get("X-Done") ?? "true") === "true";
|
|
172
|
+
const count = Number(resp.headers.get("X-Count") ?? 0);
|
|
173
|
+
return { data: await resp.arrayBuffer(), nextWatermark, done, count };
|
|
158
174
|
}
|
|
159
175
|
/**
|
|
160
176
|
* Drain every remaining page for a collection, invoking `onPage` for each.
|
|
@@ -9,9 +9,13 @@
|
|
|
9
9
|
* No HTTP is reimplemented here — `ModalEmbeddingClient` owns that.
|
|
10
10
|
*/
|
|
11
11
|
import { ModalEmbeddingClient } from "./modal-client.js";
|
|
12
|
-
import type {
|
|
12
|
+
import type { VaultMindConfig } from "./types.js";
|
|
13
13
|
/** Env var name for the Modal bearer token (preferred over config). */
|
|
14
14
|
export declare const MODAL_TOKEN_ENV = "PVM_API_TOKEN";
|
|
15
|
+
/** Env var name for the read-scoped Modal token (search/export). */
|
|
16
|
+
export declare const MODAL_READ_TOKEN_ENV = "PVM_MODAL_READ_TOKEN";
|
|
17
|
+
/** Env var name for the write-scoped Modal token (embed/reindex). */
|
|
18
|
+
export declare const MODAL_WRITE_TOKEN_ENV = "PVM_MODAL_WRITE_TOKEN";
|
|
15
19
|
/** Dotenv-style fallback path for the Modal token. */
|
|
16
20
|
export declare const MODAL_TOKEN_ENV_PATH: string;
|
|
17
21
|
/** Canonical deployed Modal app name. */
|
|
@@ -22,33 +26,45 @@ export declare const modalUrl: (workspace: string) => string;
|
|
|
22
26
|
* Resolve the Modal API token. Resolution order:
|
|
23
27
|
* 1. `PVM_API_TOKEN` env var
|
|
24
28
|
* 2. `~/.pi/agent/vault-mind.env`
|
|
25
|
-
* 3. `
|
|
29
|
+
* 3. `vaultMind.embedding.modal.apiToken` in config
|
|
30
|
+
*
|
|
31
|
+
* Never log the resolved token.
|
|
32
|
+
*/
|
|
33
|
+
export declare const resolveModalToken: (cfg: VaultMindConfig) => string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a scoped Modal token for the requested operation.
|
|
36
|
+
* Resolution order:
|
|
37
|
+
* 1. `PVM_MODAL_{READ|WRITE}_TOKEN` env var
|
|
38
|
+
* 2. `~/.pi/agent/vault-mind.env`
|
|
39
|
+
* 3. `vaultMind.embedding.modal.{readToken|writeToken}` in config
|
|
40
|
+
* 4. Fallback to the unscoped `apiToken` (env, dotenv, then config)
|
|
41
|
+
*
|
|
26
42
|
* Never log the resolved token.
|
|
27
43
|
*/
|
|
28
|
-
export declare const
|
|
44
|
+
export declare const getModalToken: (cfg: VaultMindConfig, operation: "read" | "write") => string | undefined;
|
|
29
45
|
/** Resolve the explicit baseUrl or derive it from workspace. */
|
|
30
|
-
export declare const resolveBaseUrl: (cfg:
|
|
46
|
+
export declare const resolveBaseUrl: (cfg: VaultMindConfig) => string | undefined;
|
|
31
47
|
/** Resolve the configured workspace slug, if any. */
|
|
32
|
-
export declare const resolveWorkspace: (cfg:
|
|
33
|
-
/** True when Modal is usable: a base URL (explicit or derived) and
|
|
34
|
-
export declare const isModalConfigured: (cfg:
|
|
48
|
+
export declare const resolveWorkspace: (cfg: VaultMindConfig) => string | undefined;
|
|
49
|
+
/** True when Modal is usable: a base URL (explicit or derived) and at least one resolvable scoped token are present. */
|
|
50
|
+
export declare const isModalConfigured: (cfg: VaultMindConfig) => boolean;
|
|
35
51
|
/**
|
|
36
52
|
* Build a `ModalEmbeddingClient` from config. Returns null when Modal is not
|
|
37
53
|
* configured (no base URL / token) so callers can degrade gracefully.
|
|
38
54
|
*/
|
|
39
|
-
export declare const createModalClient: (cfg:
|
|
55
|
+
export declare const createModalClient: (cfg: VaultMindConfig) => ModalEmbeddingClient | null;
|
|
40
56
|
/**
|
|
41
57
|
* The canonical embedder key for a collection. Per-collection overrides win;
|
|
42
58
|
* otherwise the global Modal config model; otherwise "embeddinggemma"
|
|
43
59
|
* (the eval-confirmed baseline — kept as the default *model name*, never as a
|
|
44
60
|
* hard-coded dimension).
|
|
45
61
|
*/
|
|
46
|
-
export declare const resolveModel: (cfg:
|
|
62
|
+
export declare const resolveModel: (cfg: VaultMindConfig, collection?: string) => string;
|
|
47
63
|
/**
|
|
48
64
|
* The configured output dimension for a collection, if known. Per-collection
|
|
49
65
|
* overrides win; otherwise the global Modal config `dim`. Returns undefined
|
|
50
66
|
* when unset — callers resolve the native dim lazily via `/models`.
|
|
51
67
|
*/
|
|
52
|
-
export declare const resolveDim: (cfg:
|
|
68
|
+
export declare const resolveDim: (cfg: VaultMindConfig, collection?: string) => number | undefined;
|
|
53
69
|
/** Namespaced local table name mirroring the server's ADR-3 scheme. */
|
|
54
70
|
export declare const namespacedTableName: (collection: string, model: string, dim: number) => string;
|
package/dist/src/modal-config.js
CHANGED
|
@@ -13,6 +13,10 @@ import * as path from "node:path";
|
|
|
13
13
|
import { ModalEmbeddingClient } from "./modal-client.js";
|
|
14
14
|
/** Env var name for the Modal bearer token (preferred over config). */
|
|
15
15
|
export const MODAL_TOKEN_ENV = "PVM_API_TOKEN";
|
|
16
|
+
/** Env var name for the read-scoped Modal token (search/export). */
|
|
17
|
+
export const MODAL_READ_TOKEN_ENV = "PVM_MODAL_READ_TOKEN";
|
|
18
|
+
/** Env var name for the write-scoped Modal token (embed/reindex). */
|
|
19
|
+
export const MODAL_WRITE_TOKEN_ENV = "PVM_MODAL_WRITE_TOKEN";
|
|
16
20
|
/** Dotenv-style fallback path for the Modal token. */
|
|
17
21
|
export const MODAL_TOKEN_ENV_PATH = path.join(process.env.HOME || process.env.USERPROFILE || "", ".pi", "agent", "vault-mind.env");
|
|
18
22
|
/** Canonical deployed Modal app name. */
|
|
@@ -48,7 +52,8 @@ export const modalUrl = (workspace) => `https://${workspace}--${MODAL_APP_NAME}-
|
|
|
48
52
|
* Resolve the Modal API token. Resolution order:
|
|
49
53
|
* 1. `PVM_API_TOKEN` env var
|
|
50
54
|
* 2. `~/.pi/agent/vault-mind.env`
|
|
51
|
-
* 3. `
|
|
55
|
+
* 3. `vaultMind.embedding.modal.apiToken` in config
|
|
56
|
+
*
|
|
52
57
|
* Never log the resolved token.
|
|
53
58
|
*/
|
|
54
59
|
export const resolveModalToken = (cfg) => {
|
|
@@ -59,6 +64,30 @@ export const resolveModalToken = (cfg) => {
|
|
|
59
64
|
return dotenv[MODAL_TOKEN_ENV];
|
|
60
65
|
return cfg.embedding.modal?.apiToken;
|
|
61
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* Resolve a scoped Modal token for the requested operation.
|
|
69
|
+
* Resolution order:
|
|
70
|
+
* 1. `PVM_MODAL_{READ|WRITE}_TOKEN` env var
|
|
71
|
+
* 2. `~/.pi/agent/vault-mind.env`
|
|
72
|
+
* 3. `vaultMind.embedding.modal.{readToken|writeToken}` in config
|
|
73
|
+
* 4. Fallback to the unscoped `apiToken` (env, dotenv, then config)
|
|
74
|
+
*
|
|
75
|
+
* Never log the resolved token.
|
|
76
|
+
*/
|
|
77
|
+
export const getModalToken = (cfg, operation) => {
|
|
78
|
+
const modal = cfg.embedding.modal;
|
|
79
|
+
const dotenv = readDotEnv(MODAL_TOKEN_ENV_PATH);
|
|
80
|
+
if (operation === "read") {
|
|
81
|
+
return (process.env[MODAL_READ_TOKEN_ENV] ||
|
|
82
|
+
dotenv[MODAL_READ_TOKEN_ENV] ||
|
|
83
|
+
modal?.readToken ||
|
|
84
|
+
resolveModalToken(cfg));
|
|
85
|
+
}
|
|
86
|
+
return (process.env[MODAL_WRITE_TOKEN_ENV] ||
|
|
87
|
+
dotenv[MODAL_WRITE_TOKEN_ENV] ||
|
|
88
|
+
modal?.writeToken ||
|
|
89
|
+
resolveModalToken(cfg));
|
|
90
|
+
};
|
|
62
91
|
/** Resolve the explicit baseUrl or derive it from workspace. */
|
|
63
92
|
export const resolveBaseUrl = (cfg) => {
|
|
64
93
|
const modal = cfg.embedding.modal;
|
|
@@ -70,8 +99,8 @@ export const resolveBaseUrl = (cfg) => {
|
|
|
70
99
|
};
|
|
71
100
|
/** Resolve the configured workspace slug, if any. */
|
|
72
101
|
export const resolveWorkspace = (cfg) => cfg.embedding.modal?.workspace;
|
|
73
|
-
/** True when Modal is usable: a base URL (explicit or derived) and
|
|
74
|
-
export const isModalConfigured = (cfg) => cfg.embedding.
|
|
102
|
+
/** True when Modal is usable: a base URL (explicit or derived) and at least one resolvable scoped token are present. */
|
|
103
|
+
export const isModalConfigured = (cfg) => !!cfg.embedding.remoteUrl && (!!getModalToken(cfg, "read") || !!getModalToken(cfg, "write"));
|
|
75
104
|
/**
|
|
76
105
|
* Build a `ModalEmbeddingClient` from config. Returns null when Modal is not
|
|
77
106
|
* configured (no base URL / token) so callers can degrade gracefully.
|
|
@@ -81,9 +110,11 @@ export const createModalClient = (cfg) => {
|
|
|
81
110
|
if (!baseUrl)
|
|
82
111
|
return null;
|
|
83
112
|
const apiToken = resolveModalToken(cfg);
|
|
84
|
-
|
|
113
|
+
const readToken = getModalToken(cfg, "read");
|
|
114
|
+
const writeToken = getModalToken(cfg, "write");
|
|
115
|
+
if (!apiToken && !readToken && !writeToken)
|
|
85
116
|
return null;
|
|
86
|
-
const clientCfg = { baseUrl, apiToken };
|
|
117
|
+
const clientCfg = { baseUrl, apiToken, readToken, writeToken };
|
|
87
118
|
return new ModalEmbeddingClient(clientCfg);
|
|
88
119
|
};
|
|
89
120
|
/**
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export interface VaultProfile {
|
|
3
|
+
vaultPath: string;
|
|
4
|
+
topFolders: Array<{
|
|
5
|
+
name: string;
|
|
6
|
+
noteCount: number;
|
|
7
|
+
}>;
|
|
8
|
+
totalNotes: number;
|
|
9
|
+
commonTags: Array<{
|
|
10
|
+
tag: string;
|
|
11
|
+
count: number;
|
|
12
|
+
}>;
|
|
13
|
+
domains: string[];
|
|
14
|
+
lancedbSummary?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ConfigFiles {
|
|
17
|
+
systemMd: string | null;
|
|
18
|
+
agentsMd: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface FileSuggestion {
|
|
21
|
+
path: string;
|
|
22
|
+
current: string | null;
|
|
23
|
+
proposed: string;
|
|
24
|
+
action: "create" | "update" | "skip";
|
|
25
|
+
}
|
|
26
|
+
export interface SuggestionResult {
|
|
27
|
+
commentary?: string;
|
|
28
|
+
files: FileSuggestion[];
|
|
29
|
+
}
|
|
30
|
+
/** Resolve the vault path: configured default vault wins, otherwise ctx.cwd. */
|
|
31
|
+
export declare const resolveVaultPath: (ctx: ExtensionContext) => string;
|
|
32
|
+
/** Load current agent configuration files if they exist. */
|
|
33
|
+
export declare const loadCurrentConfig: (vaultPath: string) => ConfigFiles;
|
|
34
|
+
/** Analyze the vault structure, tags, and LanceDB state. */
|
|
35
|
+
export declare const analyzeVaultProfile: (ctx: ExtensionContext) => Promise<VaultProfile>;
|
|
36
|
+
export declare const parseSuggestionJson: (text: string) => SuggestionResult;
|
|
37
|
+
/** Ask the current session model to suggest personalized config updates. */
|
|
38
|
+
export declare const generateSuggestions: (pi: ExtensionAPI, profile: VaultProfile, currentConfig: ConfigFiles) => Promise<SuggestionResult>;
|
|
39
|
+
/** Format old vs new as a markdown diff block. */
|
|
40
|
+
export declare const formatDiff: (current: string | null, proposed: string) => string;
|
|
41
|
+
/** Present each suggestion diff to the user and write approved files. */
|
|
42
|
+
export declare const presentAndApplyDiff: (suggestions: SuggestionResult, ctx: ExtensionContext, currentConfig: ConfigFiles) => Promise<{
|
|
43
|
+
approved: string[];
|
|
44
|
+
rejected: string[];
|
|
45
|
+
}>;
|
|
46
|
+
/** Orchestrate /vm personalize: analyze, suggest, confirm, apply. */
|
|
47
|
+
export declare const runPersonalize: (ctx: ExtensionContext, pi: ExtensionAPI) => Promise<void>;
|