pi-vault-mind 0.16.5 → 0.16.7
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 +34 -0
- package/README.md +10 -35
- package/dist/src/commands.js +2 -1
- package/dist/src/personalize.d.ts +3 -1
- package/dist/src/personalize.js +8 -2
- package/dist/src/server.js +12 -9
- package/dist/src/utils.d.ts +3 -0
- package/dist/src/utils.js +13 -0
- package/dist/test/personalize.test.js +63 -0
- package/dist/test/rest-vm.test.js +16 -9
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.7 / 0.6.10 — 2026-07-18
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Obsidian Markdown lifecycle.** Assistant Markdown renders through Obsidian's native renderer, renderer components unload on panel close or remount, stale asynchronous opens cannot remount a closed panel, and deferred message fills cannot hijack a replacement message placeholder.
|
|
8
|
+
- **Generated sandbox constants.** Root builds now format the sandbox extension-package catalog they regenerate, so the release build cannot invalidate the sandbox verification suite.
|
|
9
|
+
- **Repository-only documentation.** Removed the obsolete MkDocs configuration, GitHub Pages deployment workflow, publication badges, and generated-site instructions while retaining maintained Markdown documentation in the repository.
|
|
10
|
+
|
|
11
|
+
### Tests
|
|
12
|
+
|
|
13
|
+
- Added focused regressions for Markdown cleanup ownership, direct remount disposal, close-during-refresh cancellation, and deferred placeholder isolation.
|
|
14
|
+
|
|
15
|
+
## 0.6.9 — 2026-07-18
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **First-run personalization recovery.** The configured-vault onboarding card now retains its active personalization state until durable runtime status confirms completion, including when the prompt acknowledgement fails or a permission request is pending. The chat feed and composer remain correctly gated until then.
|
|
20
|
+
- **Portable model selection.** The shared panel contract now passes opaque model IDs end-to-end; the Obsidian adapter resolves each ID to Pi's provider/model RPC payload internally, preserving IDs that contain `/` or `:`.
|
|
21
|
+
|
|
22
|
+
### Tests
|
|
23
|
+
|
|
24
|
+
- Added focused regressions for acknowledgement failure, pending permission delivery, re-entry protection, and exact model-ID selection ordering.
|
|
25
|
+
|
|
26
|
+
## 0.16.6 / 0.6.8 — 2026-07-18
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **First-run personalization handoff.** Configured vaults now remain on a dedicated personalization card until `/vm personalize` durably completes, including the valid all-suggestions-rejected path. The chat feed and composer are revealed only after the persisted completion status is observed.
|
|
31
|
+
- **Live Pi model selection.** The first-run card now loads the active model and catalog from typed Pi RPC state, preserves provider and model ID for selection, and starts Pi only when a configured panel refresh needs that catalog.
|
|
32
|
+
|
|
33
|
+
### Tests
|
|
34
|
+
|
|
35
|
+
- Added regression coverage for first-run gating, model selection ordering, delayed personalization completion, and demand-driven Pi model loading.
|
|
36
|
+
|
|
3
37
|
## 0.16.5 / 0.6.7 — 2026-07-18
|
|
4
38
|
|
|
5
39
|
### Fixed
|
package/README.md
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/pi-vault-mind)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://github.com/mariozechner/pi)
|
|
6
|
-
[](https://kylebrodeur.github.io/pi-vault-mind/)
|
|
7
|
-
[](https://github.com/kylebrodeur/pi-vault-mind/actions/workflows/docs.yml)
|
|
8
6
|
|
|
9
7
|
Passive Obsidian vault extension for the [pi](https://github.com/mariozechner/pi) agent ecosystem. Watches `@agent` markers in your vault, dispatches forked subagents, and stores results in LanceDB with vector + FTS + graph. Multi-agent "Drop & Forget" workflow.
|
|
10
8
|
|
|
@@ -363,19 +361,16 @@ Edit `pi-vault-mind.config.json` to match your domain:
|
|
|
363
361
|
|
|
364
362
|
## Documentation
|
|
365
363
|
|
|
366
|
-
|
|
367
|
-
> The links below point to the source files in this repository.
|
|
364
|
+
Documentation is maintained directly in this repository.
|
|
368
365
|
|
|
369
366
|
### Getting started
|
|
370
367
|
|
|
371
368
|
| Doc | Description |
|
|
372
369
|
|---|---|
|
|
373
|
-
| [
|
|
374
|
-
| [
|
|
375
|
-
| [
|
|
376
|
-
| [
|
|
377
|
-
| [docs/getting-started/CLI_ONLY_WALKTHROUGH.md](docs/getting-started/CLI_ONLY_WALKTHROUGH.md) | Same setup + daily commands, entirely CLI — no Obsidian plugin required |
|
|
378
|
-
| [docs/getting-started/WALKTHROUGH_PROMPT.md](docs/getting-started/WALKTHROUGH_PROMPT.md) | Paste-into-pi guided setup with checkpoints between phases |
|
|
370
|
+
| [Install playbook](docs/_reference/reference_archive/getting-started/INSTALL.md) | Canonical install procedure for the extension, skills, Obsidian plugin, configuration, and external CLIs |
|
|
371
|
+
| [Getting started](docs/_reference/reference_archive/getting-started/GETTING_STARTED.md) | End-to-end setup and daily “drop and forget” workflow |
|
|
372
|
+
| [CLI-only walkthrough](docs/_reference/reference_archive/getting-started/CLI_ONLY_WALKTHROUGH.md) | Setup and daily commands without the Obsidian plugin |
|
|
373
|
+
| [ReturnVape walkthrough](docs/getting-started/WALKTHROUGH.md) | Current guided Obsidian test-vault procedure |
|
|
379
374
|
|
|
380
375
|
### Architecture & design
|
|
381
376
|
|
|
@@ -384,8 +379,6 @@ Edit `pi-vault-mind.config.json` to match your domain:
|
|
|
384
379
|
| [docs/architecture/AGENTS.md](docs/architecture/AGENTS.md) | Agent Roster and Multi-Agent Architecture ("Fork & Review" model) |
|
|
385
380
|
| [docs/architecture/EXTENSION_WIRING.md](docs/architecture/EXTENSION_WIRING.md) | Extension dependencies, runtime wiring, auto-install patterns |
|
|
386
381
|
| [docs/architecture/DISPATCHER_SPEC.md](docs/architecture/DISPATCHER_SPEC.md) | Technical spec for the passive file-watcher and subagent routing — incl. the "Fork & Dispatch" rationale and thread resume |
|
|
387
|
-
| [docs/architecture/AGENTS.md](docs/architecture/AGENTS.md) | Agent roster + the Miner's "Drop & Forget" ingestion pipeline |
|
|
388
|
-
| [docs/integrations/OBSIDIAN_SETUP.md](docs/integrations/OBSIDIAN_SETUP.md) | Recommended Obsidian vault structure, plugins, and CLI |
|
|
389
382
|
|
|
390
383
|
### Modal embedding service (local integration done)
|
|
391
384
|
|
|
@@ -401,8 +394,6 @@ full design.
|
|
|
401
394
|
| Doc | Description |
|
|
402
395
|
|---|---|
|
|
403
396
|
| [docs/integrations/MODAL_EMBEDDING.md](docs/integrations/MODAL_EMBEDDING.md) | Design of record: ADRs, HTTP contract, sync protocol, roadmap for the cloud embedding service |
|
|
404
|
-
| [docs/plans/modal-embedding-plan.md](docs/plans/modal-embedding-plan.md) | **Consolidated plan & next steps** — phases, owners, status, decision gate (in repo, not on the docs site) |
|
|
405
|
-
| [docs/plans/embedding-open-questions.md](docs/plans/embedding-open-questions.md) | Embedding-strategy decision log (decided + open questions) |
|
|
406
397
|
| [modal/](modal/) | The deployable Modal app: embedding service + bulk worker + sync + dataset generator |
|
|
407
398
|
| [eval/](eval/) | Retrieval eval harness + labeled benchmark datasets for picking the canonical model |
|
|
408
399
|
|
|
@@ -412,17 +403,10 @@ full design.
|
|
|
412
403
|
|---|---|
|
|
413
404
|
| [skills/vault-mind/SKILL.md](skills/vault-mind/SKILL.md) | The Manager skill — what pi auto-loads about this extension |
|
|
414
405
|
| [docs/CHANGELOG.md](docs/CHANGELOG.md) | Version history (rename from `pi-knowledge-store` to `pi-vault-mind` was v0.7.0) |
|
|
415
|
-
| [
|
|
416
|
-
| [
|
|
417
|
-
| [
|
|
418
|
-
| [
|
|
419
|
-
|
|
420
|
-
### Testing
|
|
421
|
-
|
|
422
|
-
| Doc | Description |
|
|
423
|
-
|---|---|
|
|
424
|
-
| [docs/testing/TESTING.md](docs/testing/TESTING.md) | Test plan for agent / human / HITL personas, regression suite |
|
|
425
|
-
| [docs/testing/E2E_MANUAL_TEST.md](docs/testing/E2E_MANUAL_TEST.md) | Manual end-to-end verification procedure (watcher → dispatch → vault) |
|
|
406
|
+
| [Tools reference](docs/_reference/reference_archive/tools.md) | Registered Vault Mind tools, parameters, and return shapes |
|
|
407
|
+
| [Commands reference](docs/_reference/reference_archive/commands.md) | Full `/vm` slash command tree |
|
|
408
|
+
| [Configuration reference](docs/_reference/reference_archive/configuration.md) | Complete `pi-vault-mind.config.json` schema |
|
|
409
|
+
| [Skill manifest](docs/_reference/reference_archive/skill.md) | Bundled skills and their trigger phrases |
|
|
426
410
|
|
|
427
411
|
### Development
|
|
428
412
|
|
|
@@ -430,22 +414,13 @@ full design.
|
|
|
430
414
|
|---|---|
|
|
431
415
|
| [docs/development/CONTRIBUTING.md](docs/development/CONTRIBUTING.md) | Dev setup, testing, and commit conventions |
|
|
432
416
|
| [docs/development/PUBLISHING.md](docs/development/PUBLISHING.md) | How to publish this extension to npm |
|
|
433
|
-
| [docs/development/FUTURE_WORK.md](docs/development/FUTURE_WORK.md) | Roadmap — codegraph integration, pagination, TUI rendering, etc. |
|
|
434
|
-
|
|
435
|
-
### Research
|
|
436
|
-
|
|
437
|
-
| Doc | Description |
|
|
438
|
-
|---|---|
|
|
439
|
-
| [docs/research/COMPETITOR_COMPARISON.md](docs/research/COMPETITOR_COMPARISON.md) | Tier 1/2/3 comparison vs. other Obsidian-LLM tools (22 competitors) |
|
|
440
|
-
| [docs/research/naming-decisions.md](docs/research/naming-decisions.md) | Historical record of the 2026-06-06 decision to name the project `pi-vault-mind` |
|
|
441
|
-
| [docs/research/obsidian-links-reviewed.csv](docs/research/obsidian-links-reviewed.csv) | Curated subset of starred Obsidian repos with adoption verdicts |
|
|
442
417
|
|
|
443
418
|
### Archive
|
|
444
419
|
|
|
445
420
|
| Doc | Description |
|
|
446
421
|
|---|---|
|
|
447
422
|
| [docs/_archive/](docs/_archive/) | Historical docs kept for context (e.g. the `pi-knowledge-store` → `pi-vault-mind` rename audit) |
|
|
448
|
-
| [docs/_archive/legacy-audit.md](docs/_archive/legacy-audit.md) | The 2026-06-08 legacy-terminology audit (139 findings, 13 blockers) and its resolution log. Resolved 2026-06-09
|
|
423
|
+
| [docs/_archive/legacy-audit.md](docs/_archive/legacy-audit.md) | The 2026-06-08 legacy-terminology audit (139 findings, 13 blockers) and its resolution log. Resolved 2026-06-09 and archived 2026-06-16. |
|
|
449
424
|
|
|
450
425
|
## Contributing
|
|
451
426
|
|
package/dist/src/commands.js
CHANGED
|
@@ -1760,7 +1760,8 @@ export const registerCommands = (pi) => {
|
|
|
1760
1760
|
case "remote":
|
|
1761
1761
|
return handleModal(rest, ctx, pi);
|
|
1762
1762
|
case "personalize":
|
|
1763
|
-
|
|
1763
|
+
await runPersonalize(ctx, pi);
|
|
1764
|
+
return;
|
|
1764
1765
|
case "setup":
|
|
1765
1766
|
return handleSetup(rest, ctx, pi);
|
|
1766
1767
|
case "token":
|
|
@@ -44,4 +44,6 @@ export declare const presentAndApplyDiff: (suggestions: SuggestionResult, ctx: E
|
|
|
44
44
|
rejected: string[];
|
|
45
45
|
}>;
|
|
46
46
|
/** Orchestrate /vm personalize: analyze, suggest, confirm, apply. */
|
|
47
|
-
export declare const runPersonalize: (ctx: ExtensionContext, pi: ExtensionAPI) => Promise<
|
|
47
|
+
export declare const runPersonalize: (ctx: ExtensionContext, pi: ExtensionAPI) => Promise<{
|
|
48
|
+
completed: boolean;
|
|
49
|
+
}>;
|
package/dist/src/personalize.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { getStatus } from "./lance.js";
|
|
5
|
-
import { expandHome, loadConfig, resolveAgentDir } from "./utils.js";
|
|
5
|
+
import { expandHome, getPersonalizedMarkerPath, loadConfig, resolveAgentDir } from "./utils.js";
|
|
6
6
|
/** Resolve the vault path: configured default vault wins, otherwise ctx.cwd. */
|
|
7
7
|
export const resolveVaultPath = (ctx) => {
|
|
8
8
|
const cfg = loadConfig(ctx.cwd);
|
|
@@ -359,8 +359,14 @@ export const runPersonalize = async (ctx, pi) => {
|
|
|
359
359
|
try {
|
|
360
360
|
const suggestions = await generateSuggestions(pi, profile, currentConfig);
|
|
361
361
|
await presentAndApplyDiff(suggestions, ctx, currentConfig);
|
|
362
|
+
const markerPath = getPersonalizedMarkerPath(vaultPath);
|
|
363
|
+
await fs.promises.mkdir(path.dirname(markerPath), { recursive: true });
|
|
364
|
+
await fs.promises.writeFile(markerPath, JSON.stringify({ completed: true, completedAt: new Date().toISOString() }, null, 2));
|
|
365
|
+
return { completed: true };
|
|
362
366
|
}
|
|
363
367
|
catch (err) {
|
|
364
|
-
|
|
368
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
369
|
+
ctx.ui.notify(`Personalization failed: ${message}`, "error");
|
|
370
|
+
return { completed: false };
|
|
365
371
|
}
|
|
366
372
|
};
|
package/dist/src/server.js
CHANGED
|
@@ -45,7 +45,7 @@ import { readModelProviders } from "./models.js";
|
|
|
45
45
|
import { scaffoldVaultConfig } from "./scaffold.js";
|
|
46
46
|
import { archiveSession, deleteSession, exportSession, getSession, listSessions, renameSession, resolveSessionsDir, searchSessions, } from "./session-search.js";
|
|
47
47
|
import { listAllTools } from "./tool-catalog.js";
|
|
48
|
-
import { ensureDir, expandHome, findConfig, getConfigPath, loadConfig, resolveVaultFile, resolveVaultMindPaths, shrinkHome, } from "./utils.js";
|
|
48
|
+
import { ensureDir, expandHome, findConfig, getConfigPath, isPersonalized, loadConfig, resolveVaultFile, resolveVaultMindPaths, shrinkHome, } from "./utils.js";
|
|
49
49
|
import { classifySearchMode, normalizeGraphResult, searchVaultNative } from "./vault-tools.js";
|
|
50
50
|
import { createManualDispatch, processQueue, scanFile, startWatcher, stopWatcher, } from "./watcher.js";
|
|
51
51
|
export function createServerState(port = 11435) {
|
|
@@ -1100,12 +1100,14 @@ function handleVmStatus(res, serverState) {
|
|
|
1100
1100
|
}
|
|
1101
1101
|
const cfg = loadConfig(vaultPath);
|
|
1102
1102
|
const configured = !!findConfig(vaultPath).project;
|
|
1103
|
+
const personalized = isPersonalized(vaultPath);
|
|
1103
1104
|
const model = cfg.vaultMind.embedding[EMBEDDING_FLAT_KEYS[2]] || "embeddinggemma";
|
|
1104
1105
|
const dim = cfg.vaultMind.embedding[EMBEDDING_FLAT_KEYS[4]] ?? 768;
|
|
1105
1106
|
res.writeHead(200);
|
|
1106
1107
|
res.end(JSON.stringify({
|
|
1107
1108
|
ok: true,
|
|
1108
1109
|
configured,
|
|
1110
|
+
personalized,
|
|
1109
1111
|
vaultPath,
|
|
1110
1112
|
watcher: ws?.running ?? false,
|
|
1111
1113
|
embedding: {
|
|
@@ -1635,30 +1637,32 @@ async function handleVmSearch(req, res, serverState, overrideBody) {
|
|
|
1635
1637
|
res.end(JSON.stringify({ error: `Unknown mode: ${mode}` }));
|
|
1636
1638
|
return;
|
|
1637
1639
|
}
|
|
1640
|
+
const sources = Array.isArray(body.sources) ? body.sources : ["vault"];
|
|
1641
|
+
const includeVault = sources.includes("vault");
|
|
1638
1642
|
try {
|
|
1639
|
-
let results;
|
|
1643
|
+
let results = [];
|
|
1640
1644
|
let relations;
|
|
1641
1645
|
let message;
|
|
1642
|
-
if (resolvedMode === "hybrid") {
|
|
1646
|
+
if (includeVault && resolvedMode === "hybrid") {
|
|
1643
1647
|
results = await searchHybridRanked(cfg.vaultMind.dataDir, collection, query, limit, cfg.vaultMind, identity);
|
|
1644
1648
|
}
|
|
1645
|
-
else if (resolvedMode === "semantic") {
|
|
1649
|
+
else if (includeVault && resolvedMode === "semantic") {
|
|
1646
1650
|
results = await searchHybrid(cfg.vaultMind.dataDir, collection, query, limit, cfg.vaultMind, identity);
|
|
1647
1651
|
}
|
|
1648
|
-
else if (resolvedMode === "fts") {
|
|
1652
|
+
else if (includeVault && resolvedMode === "fts") {
|
|
1649
1653
|
results = await searchFts(cfg.vaultMind.dataDir, collection, query, limit, cfg.vaultMind, identity);
|
|
1650
1654
|
}
|
|
1651
|
-
else if (resolvedMode === "graph") {
|
|
1655
|
+
else if (includeVault && resolvedMode === "graph") {
|
|
1652
1656
|
const depth = body.depth ?? 1;
|
|
1653
1657
|
const norm = normalizeGraphResult(await queryGraph(cfg.vaultMind.dataDir, cfg.vaultMind, entity, depth, identity));
|
|
1654
1658
|
results = norm.hits;
|
|
1655
1659
|
relations = norm.relations;
|
|
1656
1660
|
message = norm.message;
|
|
1657
1661
|
}
|
|
1658
|
-
else if (resolvedMode === "vault") {
|
|
1662
|
+
else if (includeVault && resolvedMode === "vault") {
|
|
1659
1663
|
results = await searchVaultNative(vaultPath, query, limit, body.tags);
|
|
1660
1664
|
}
|
|
1661
|
-
else {
|
|
1665
|
+
else if (includeVault) {
|
|
1662
1666
|
// mode === "full"
|
|
1663
1667
|
const depth = body.depth ?? 1;
|
|
1664
1668
|
const [hybrid, semantic, fts, graph, vault] = await Promise.all([
|
|
@@ -1707,7 +1711,6 @@ async function handleVmSearch(req, res, serverState, overrideBody) {
|
|
|
1707
1711
|
addResults(vault, "vault");
|
|
1708
1712
|
results = merged.slice(0, limit);
|
|
1709
1713
|
}
|
|
1710
|
-
const sources = Array.isArray(body.sources) ? body.sources : ["vault"];
|
|
1711
1714
|
if (sources.includes("sessions")) {
|
|
1712
1715
|
results = [...results, ...searchSessions(resolveSessionsDir(), query, limit)];
|
|
1713
1716
|
}
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ export declare const resolveAgentDir: (cwd: string) => string;
|
|
|
13
13
|
export declare const resolveVaultMindDir: (cwd: string) => string;
|
|
14
14
|
/** Active pi-vault-mind config: `<vault>/.vault-mind/vault-mind.config.json`. */
|
|
15
15
|
export declare const getConfigPath: (cwd: string) => string;
|
|
16
|
+
export declare const getPersonalizedMarkerPath: (cwd: string) => string;
|
|
17
|
+
/** Check if a vault has a valid personalization completion marker. */
|
|
18
|
+
export declare function isPersonalized(cwd: string): boolean;
|
|
16
19
|
/** Token env file: `<vault>/.vault-mind/vault-mind.env`. */
|
|
17
20
|
export declare const getTokenEnvPath: (cwd: string) => string;
|
|
18
21
|
export declare const EXT_ROOT: string;
|
package/dist/src/utils.js
CHANGED
|
@@ -18,6 +18,19 @@ export const resolveAgentDir = (cwd) => {
|
|
|
18
18
|
export const resolveVaultMindDir = (cwd) => path.join(cwd, VAULT_MIND_DIR);
|
|
19
19
|
/** Active pi-vault-mind config: `<vault>/.vault-mind/vault-mind.config.json`. */
|
|
20
20
|
export const getConfigPath = (cwd) => path.join(resolveVaultMindDir(cwd), "vault-mind.config.json");
|
|
21
|
+
export const getPersonalizedMarkerPath = (cwd) => path.join(resolveVaultMindDir(cwd), "personalized.json");
|
|
22
|
+
/** Check if a vault has a valid personalization completion marker. */
|
|
23
|
+
export function isPersonalized(cwd) {
|
|
24
|
+
const p = getPersonalizedMarkerPath(cwd);
|
|
25
|
+
try {
|
|
26
|
+
const content = fs.readFileSync(p, "utf8");
|
|
27
|
+
const marker = JSON.parse(content);
|
|
28
|
+
return marker && typeof marker === "object" && marker.completed === true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
21
34
|
/** Token env file: `<vault>/.vault-mind/vault-mind.env`. */
|
|
22
35
|
export const getTokenEnvPath = (cwd) => path.join(resolveVaultMindDir(cwd), "vault-mind.env");
|
|
23
36
|
export const EXT_ROOT = path.join(import.meta.dirname, "..", "..");
|
|
@@ -192,6 +192,69 @@ describe("personalize", () => {
|
|
|
192
192
|
// AGENTS.md was never created, so rejection means it still does not exist.
|
|
193
193
|
assert.equal(fs.existsSync(path.join(testDir, "AGENTS.md")), false);
|
|
194
194
|
});
|
|
195
|
+
it("persists a completion marker even when all suggestions are rejected", async () => {
|
|
196
|
+
const { runPersonalize } = await import("../src/personalize.js");
|
|
197
|
+
const pi = makePi(JSON.stringify({
|
|
198
|
+
commentary: "No changes needed.",
|
|
199
|
+
files: [
|
|
200
|
+
{ path: ".pi/agent/system.md", action: "create", content: "# System" },
|
|
201
|
+
{ path: "AGENTS.md", action: "create", content: "# Agents" },
|
|
202
|
+
],
|
|
203
|
+
}));
|
|
204
|
+
const ctx = makeCtx(testDir, [false, false]);
|
|
205
|
+
await runPersonalize(ctx, pi);
|
|
206
|
+
const markerPath = path.join(testDir, ".vault-mind", "personalized.json");
|
|
207
|
+
assert.ok(fs.existsSync(markerPath), "runPersonalize must write a durable completion marker after the flow finishes, even when the user rejects every actionable suggestion");
|
|
208
|
+
const marker = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
|
|
209
|
+
assert.strictEqual(marker.completed, true);
|
|
210
|
+
assert.ok(typeof marker.completedAt === "string");
|
|
211
|
+
});
|
|
212
|
+
it("persists a completion marker after approved suggestions", async () => {
|
|
213
|
+
const { runPersonalize } = await import("../src/personalize.js");
|
|
214
|
+
const pi = makePi(JSON.stringify({
|
|
215
|
+
commentary: "Updated tuning.",
|
|
216
|
+
files: [{ path: ".pi/agent/system.md", action: "create", content: "# New system" }],
|
|
217
|
+
}));
|
|
218
|
+
const ctx = makeCtx(testDir, [true]);
|
|
219
|
+
await runPersonalize(ctx, pi);
|
|
220
|
+
const markerPath = path.join(testDir, ".vault-mind", "personalized.json");
|
|
221
|
+
assert.ok(fs.existsSync(markerPath), "runPersonalize must write a durable completion marker after approved suggestions are applied");
|
|
222
|
+
const marker = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
|
|
223
|
+
assert.strictEqual(marker.completed, true);
|
|
224
|
+
assert.ok(typeof marker.completedAt === "string");
|
|
225
|
+
});
|
|
226
|
+
it("returns a successful result when all suggestions are rejected", async () => {
|
|
227
|
+
const { runPersonalize: raw } = await import("../src/personalize.js");
|
|
228
|
+
const runPersonalize = raw;
|
|
229
|
+
const pi = makePi(JSON.stringify({
|
|
230
|
+
commentary: "No changes needed.",
|
|
231
|
+
files: [
|
|
232
|
+
{ path: ".pi/agent/system.md", action: "create", content: "# System" },
|
|
233
|
+
{ path: "AGENTS.md", action: "create", content: "# Agents" },
|
|
234
|
+
],
|
|
235
|
+
}));
|
|
236
|
+
const ctx = makeCtx(testDir, [false, false]);
|
|
237
|
+
const result = await runPersonalize(ctx, pi);
|
|
238
|
+
assert.ok(result, "runPersonalize must return a result, not void");
|
|
239
|
+
assert.strictEqual(result.completed, true, "all-rejected personalization must resolve as a successful completion");
|
|
240
|
+
});
|
|
241
|
+
it("returns a failed result when suggestion generation fails", async () => {
|
|
242
|
+
const { runPersonalize: raw } = await import("../src/personalize.js");
|
|
243
|
+
const runPersonalize = raw;
|
|
244
|
+
const pi = makePi("not valid JSON");
|
|
245
|
+
const ctx = makeCtx(testDir, []);
|
|
246
|
+
const result = await runPersonalize(ctx, pi);
|
|
247
|
+
assert.ok(result, "runPersonalize must return a result, not void");
|
|
248
|
+
assert.strictEqual(result.completed, false, "a generation failure must resolve as a failed personalization, distinguishable from success");
|
|
249
|
+
});
|
|
250
|
+
it("does not persist a completion marker when suggestion generation fails", async () => {
|
|
251
|
+
const { runPersonalize } = await import("../src/personalize.js");
|
|
252
|
+
const pi = makePi("not valid JSON");
|
|
253
|
+
const ctx = makeCtx(testDir, []);
|
|
254
|
+
await runPersonalize(ctx, pi);
|
|
255
|
+
const markerPath = path.join(testDir, ".vault-mind", "personalized.json");
|
|
256
|
+
assert.strictEqual(fs.existsSync(markerPath), false, "runPersonalize must not leave a completion marker after a failed personalization");
|
|
257
|
+
});
|
|
195
258
|
it("presentAndApplyDiff creates missing directories for new files", async () => {
|
|
196
259
|
const { presentAndApplyDiff } = await import("../src/personalize.js");
|
|
197
260
|
const ctx = makeCtx(testDir, [true]);
|
|
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { after, before, describe, it } from "node:test";
|
|
6
6
|
import { _resetAuthCache } from "../src/auth.js";
|
|
7
|
+
import { resetConnection } from "../src/lance.js";
|
|
7
8
|
import { fakePi, fetchJson, makeVault, startTestServer, TEST_TOKEN, } from "./helpers/server.js";
|
|
8
9
|
const envVar = "PVM_API_TOKEN";
|
|
9
10
|
describe("REST vm routes", () => {
|
|
@@ -265,8 +266,8 @@ describe("REST vm routes", () => {
|
|
|
265
266
|
assert.ok(Array.isArray(d.sources), "sources should be echoed");
|
|
266
267
|
}
|
|
267
268
|
});
|
|
268
|
-
it("POST /vm/search with
|
|
269
|
-
// Seed a temp sessions dir
|
|
269
|
+
it("POST /vm/search with only sessions succeeds without an available vault index", async () => {
|
|
270
|
+
// Seed a temp sessions dir while the configured LanceDB index remains unavailable.
|
|
270
271
|
const tmpSessions = fs.mkdtempSync(path.join(os.tmpdir(), "pvm-rest-sessions-"));
|
|
271
272
|
const slugDir = path.join(tmpSessions, "--test--");
|
|
272
273
|
fs.mkdirSync(slugDir, { recursive: true });
|
|
@@ -284,18 +285,24 @@ describe("REST vm routes", () => {
|
|
|
284
285
|
const savedDir = process.env.PI_SESSIONS_DIR;
|
|
285
286
|
try {
|
|
286
287
|
process.env.PI_SESSIONS_DIR = tmpSessions;
|
|
288
|
+
resetConnection();
|
|
289
|
+
const vaultIndexPath = path.join(vaultPath, ".lancedb");
|
|
290
|
+
fs.rmSync(vaultIndexPath, { recursive: true, force: true });
|
|
291
|
+
assert.equal(fs.existsSync(vaultIndexPath), false, "the vault index fixture must be absent");
|
|
287
292
|
const { status, body } = await fetchJson(port, "POST", "/vm/search", {
|
|
288
|
-
body: { query: "searchtestquery", mode: "
|
|
293
|
+
body: { query: "searchtestquery", mode: "hybrid", sources: ["sessions"] },
|
|
289
294
|
token: TEST_TOKEN,
|
|
290
295
|
});
|
|
291
|
-
assert.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
296
|
+
assert.equal(status, 200, "sessions-only search must not initialize or query the vault index");
|
|
297
|
+
assert.equal(fs.existsSync(vaultIndexPath), false, "sessions-only search must leave the unavailable vault index untouched");
|
|
298
|
+
const d = body;
|
|
299
|
+
assert.deepEqual(d.sources, ["sessions"], "the response must preserve the isolated source");
|
|
300
|
+
const hits = d.hits;
|
|
301
|
+
assert.ok(hits.length > 0, "sessions-only search should return the seeded session hit");
|
|
302
|
+
assert.ok(hits.every((hit) => hit._source === "session"), "sessions-only search must not merge vault hits");
|
|
297
303
|
}
|
|
298
304
|
finally {
|
|
305
|
+
resetConnection();
|
|
299
306
|
if (savedDir === undefined)
|
|
300
307
|
delete process.env.PI_SESSIONS_DIR;
|
|
301
308
|
else
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-vault-mind",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.7",
|
|
4
4
|
"description": "Passive Obsidian vault extension for pi. Watches @agent markers, dispatches forked subagents (Miner, Broadcaster, Heavy-Lifter), stores in LanceDB with vector + FTS + graph. Multi-agent 'Drop & Forget' workflow for the pi agent ecosystem.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"scripts": {
|
|
65
65
|
"prepare": "husky",
|
|
66
66
|
"check": "biome check --write . && tsc --noEmit",
|
|
67
|
-
"generate-constants": "node scripts/generate-extension-packages-json.mjs && node scripts/generate-config-keys-json.mjs && biome check --write extension-packages.json config-keys.json packages/obsidian/src/extension-packages.json packages/obsidian/src/config-keys.json",
|
|
67
|
+
"generate-constants": "node scripts/generate-extension-packages-json.mjs && node scripts/generate-config-keys-json.mjs && biome check --write extension-packages.json config-keys.json packages/obsidian/src/extension-packages.json packages/obsidian/src/config-keys.json packages/obsidian-ui/src/extension-packages.json",
|
|
68
68
|
"build": "npm run generate-constants && tsc && tsc -p tsconfig.test.json",
|
|
69
69
|
"dev": "tsc --watch",
|
|
70
70
|
"test": "node --test \"dist/test/**/*.test.js\"",
|