@owrede/vault-memory 2.0.0-rc.1 → 2.0.0-rc.5
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 +1304 -0
- package/dist/cli.js +706 -264
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -760,6 +760,52 @@ var init_suppress_contract_write = __esm({
|
|
|
760
760
|
}
|
|
761
761
|
});
|
|
762
762
|
|
|
763
|
+
// src/plugin-tools/source-tools.ts
|
|
764
|
+
import { z as z8 } from "zod";
|
|
765
|
+
async function refreshHandler(args2, deps) {
|
|
766
|
+
const info = await deps.refresh(args2.name);
|
|
767
|
+
if (info === void 0) {
|
|
768
|
+
return { ok: false, name: args2.name, error: `unknown source: ${args2.name}` };
|
|
769
|
+
}
|
|
770
|
+
const result = {
|
|
771
|
+
ok: true,
|
|
772
|
+
name: args2.name,
|
|
773
|
+
status: info.status,
|
|
774
|
+
tool_count: info.tools.length
|
|
775
|
+
};
|
|
776
|
+
if (info.error !== void 0) result.error = info.error;
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
779
|
+
async function unsetHandler(args2, deps) {
|
|
780
|
+
const removed = deps.remove(args2.name);
|
|
781
|
+
return { ok: true, name: args2.name, removed };
|
|
782
|
+
}
|
|
783
|
+
var RefreshSourceArgs, refreshSourceTool, UnsetMcpClientArgs, unsetMcpClientTool;
|
|
784
|
+
var init_source_tools = __esm({
|
|
785
|
+
"src/plugin-tools/source-tools.ts"() {
|
|
786
|
+
"use strict";
|
|
787
|
+
init_esm_shims();
|
|
788
|
+
RefreshSourceArgs = z8.object({
|
|
789
|
+
name: z8.string().min(1).describe("Peer-MCP source name to refresh (re-poll tools/list).")
|
|
790
|
+
});
|
|
791
|
+
refreshSourceTool = {
|
|
792
|
+
name: "refresh_source",
|
|
793
|
+
description: "Re-poll tools/list against a live peer-MCP source and refresh its cached tool list. Returns the updated status (connected/unavailable/unreachable) and tool_count. SOURCES-REGISTRY \xA76.3.",
|
|
794
|
+
inputSchema: RefreshSourceArgs,
|
|
795
|
+
handler: refreshHandler
|
|
796
|
+
};
|
|
797
|
+
UnsetMcpClientArgs = z8.object({
|
|
798
|
+
name: z8.string().min(1).describe("Peer-MCP source name to disconnect + drop from the registry.")
|
|
799
|
+
});
|
|
800
|
+
unsetMcpClientTool = {
|
|
801
|
+
name: "unset_mcp_client",
|
|
802
|
+
description: "Disconnect a live peer-MCP source and drop it from the running registry. Idempotent (removed:false when the name is unknown). Affects the running process only \u2014 pair with set_mcp_client({name, remove:true}) to also delete the persisted config entry. SOURCES-REGISTRY \xA76.2.",
|
|
803
|
+
inputSchema: UnsetMcpClientArgs,
|
|
804
|
+
handler: unsetHandler
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
763
809
|
// src/plugin-tools/index.ts
|
|
764
810
|
function ok(data) {
|
|
765
811
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
@@ -921,6 +967,54 @@ function syncPluginTools(server, registered, opts) {
|
|
|
921
967
|
}
|
|
922
968
|
}
|
|
923
969
|
)
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
name: "refresh_source",
|
|
973
|
+
reg: () => server.registerTool(
|
|
974
|
+
refreshSourceTool.name,
|
|
975
|
+
{
|
|
976
|
+
description: refreshSourceTool.description,
|
|
977
|
+
inputSchema: refreshSourceTool.inputSchema.shape
|
|
978
|
+
},
|
|
979
|
+
async (args2) => {
|
|
980
|
+
try {
|
|
981
|
+
const validated = refreshSourceTool.inputSchema.parse(
|
|
982
|
+
args2
|
|
983
|
+
);
|
|
984
|
+
const result = await refreshSourceTool.handler(
|
|
985
|
+
validated,
|
|
986
|
+
opts.sourceRegistry
|
|
987
|
+
);
|
|
988
|
+
return ok(result);
|
|
989
|
+
} catch (err) {
|
|
990
|
+
return errorResponse(err instanceof Error ? err.message : String(err));
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
)
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
name: "unset_mcp_client",
|
|
997
|
+
reg: () => server.registerTool(
|
|
998
|
+
unsetMcpClientTool.name,
|
|
999
|
+
{
|
|
1000
|
+
description: unsetMcpClientTool.description,
|
|
1001
|
+
inputSchema: unsetMcpClientTool.inputSchema.shape
|
|
1002
|
+
},
|
|
1003
|
+
async (args2) => {
|
|
1004
|
+
try {
|
|
1005
|
+
const validated = unsetMcpClientTool.inputSchema.parse(
|
|
1006
|
+
args2
|
|
1007
|
+
);
|
|
1008
|
+
const result = await unsetMcpClientTool.handler(
|
|
1009
|
+
validated,
|
|
1010
|
+
opts.sourceRegistry
|
|
1011
|
+
);
|
|
1012
|
+
return ok(result);
|
|
1013
|
+
} catch (err) {
|
|
1014
|
+
return errorResponse(err instanceof Error ? err.message : String(err));
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
)
|
|
924
1018
|
}
|
|
925
1019
|
];
|
|
926
1020
|
for (const { name, reg } of adds) {
|
|
@@ -941,12 +1035,14 @@ var init_plugin_tools = __esm({
|
|
|
941
1035
|
init_get_runtime_stats();
|
|
942
1036
|
init_trigger_reindex();
|
|
943
1037
|
init_suppress_contract_write();
|
|
1038
|
+
init_source_tools();
|
|
944
1039
|
init_set_runtime_config();
|
|
945
1040
|
init_resolve_secret();
|
|
946
1041
|
init_set_mcp_client();
|
|
947
1042
|
init_get_runtime_stats();
|
|
948
1043
|
init_trigger_reindex();
|
|
949
1044
|
init_suppress_contract_write();
|
|
1045
|
+
init_source_tools();
|
|
950
1046
|
init_runtime_config();
|
|
951
1047
|
PLUGIN_TOOL_NAMES = [
|
|
952
1048
|
"set_runtime_config",
|
|
@@ -954,7 +1050,10 @@ var init_plugin_tools = __esm({
|
|
|
954
1050
|
"set_mcp_client",
|
|
955
1051
|
"get_runtime_stats",
|
|
956
1052
|
"trigger_reindex",
|
|
957
|
-
"suppress_contract_write"
|
|
1053
|
+
"suppress_contract_write",
|
|
1054
|
+
// SOURCES-REGISTRY.md §6 (Stage 2) — live-registry source management.
|
|
1055
|
+
"refresh_source",
|
|
1056
|
+
"unset_mcp_client"
|
|
958
1057
|
];
|
|
959
1058
|
}
|
|
960
1059
|
});
|
|
@@ -1207,13 +1306,16 @@ function backfillSectionsFromChunks(db) {
|
|
|
1207
1306
|
"SELECT * FROM chunks WHERE note_id = ? ORDER BY id ASC"
|
|
1208
1307
|
);
|
|
1209
1308
|
const insertSection = db.prepare(`
|
|
1210
|
-
INSERT INTO sections
|
|
1309
|
+
INSERT OR IGNORE INTO sections
|
|
1211
1310
|
(note_id, anchor, heading_path, heading_text, level,
|
|
1212
1311
|
parent_id, ord, chunk_id_first, chunk_id_last, created_at)
|
|
1213
1312
|
VALUES
|
|
1214
1313
|
(@note_id, @anchor, @heading_path, @heading_text, @level,
|
|
1215
1314
|
@parent_id, @ord, @chunk_id_first, @chunk_id_last, @created_at)
|
|
1216
1315
|
`);
|
|
1316
|
+
const lookupExistingSection = db.prepare(
|
|
1317
|
+
"SELECT id FROM sections WHERE note_id = ? AND anchor = ?"
|
|
1318
|
+
);
|
|
1217
1319
|
let backfilled = 0;
|
|
1218
1320
|
const now = Date.now();
|
|
1219
1321
|
for (const note of notesRows) {
|
|
@@ -1249,7 +1351,12 @@ function backfillSectionsFromChunks(db) {
|
|
|
1249
1351
|
created_at: now
|
|
1250
1352
|
};
|
|
1251
1353
|
const info = insertSection.run(row);
|
|
1252
|
-
|
|
1354
|
+
if (info.changes > 0) {
|
|
1355
|
+
insertedIds.push(Number(info.lastInsertRowid));
|
|
1356
|
+
} else {
|
|
1357
|
+
const existing2 = lookupExistingSection.get(note.id, s.anchor);
|
|
1358
|
+
insertedIds.push(existing2 ? Number(existing2.id) : null);
|
|
1359
|
+
}
|
|
1253
1360
|
}
|
|
1254
1361
|
backfilled++;
|
|
1255
1362
|
}
|
|
@@ -3517,7 +3624,7 @@ var init_retry = __esm({
|
|
|
3517
3624
|
});
|
|
3518
3625
|
|
|
3519
3626
|
// src/ollama/client.ts
|
|
3520
|
-
import { z as
|
|
3627
|
+
import { z as z9 } from "zod";
|
|
3521
3628
|
function isRetryable(err) {
|
|
3522
3629
|
if (err instanceof OllamaHttpError) {
|
|
3523
3630
|
return err.status >= 500 && err.status < 600;
|
|
@@ -3540,26 +3647,26 @@ var init_client = __esm({
|
|
|
3540
3647
|
DEFAULT_BATCH_SIZE = 10;
|
|
3541
3648
|
DEFAULT_TIMEOUT_MS = 3e4;
|
|
3542
3649
|
DEFAULT_RETRIES = 3;
|
|
3543
|
-
EmbedResponseSchema =
|
|
3544
|
-
embeddings:
|
|
3545
|
-
model:
|
|
3650
|
+
EmbedResponseSchema = z9.object({
|
|
3651
|
+
embeddings: z9.array(z9.array(z9.number())),
|
|
3652
|
+
model: z9.string().optional()
|
|
3546
3653
|
});
|
|
3547
|
-
TagsResponseSchema =
|
|
3548
|
-
models:
|
|
3549
|
-
|
|
3550
|
-
name:
|
|
3654
|
+
TagsResponseSchema = z9.object({
|
|
3655
|
+
models: z9.array(
|
|
3656
|
+
z9.object({
|
|
3657
|
+
name: z9.string()
|
|
3551
3658
|
})
|
|
3552
3659
|
)
|
|
3553
3660
|
});
|
|
3554
|
-
ChatResponseSchema =
|
|
3555
|
-
model:
|
|
3556
|
-
message:
|
|
3557
|
-
role:
|
|
3558
|
-
content:
|
|
3661
|
+
ChatResponseSchema = z9.object({
|
|
3662
|
+
model: z9.string(),
|
|
3663
|
+
message: z9.object({
|
|
3664
|
+
role: z9.literal("assistant"),
|
|
3665
|
+
content: z9.string()
|
|
3559
3666
|
}),
|
|
3560
|
-
done:
|
|
3561
|
-
total_duration:
|
|
3562
|
-
eval_count:
|
|
3667
|
+
done: z9.boolean().optional(),
|
|
3668
|
+
total_duration: z9.number().optional(),
|
|
3669
|
+
eval_count: z9.number().optional()
|
|
3563
3670
|
});
|
|
3564
3671
|
OllamaHttpError = class extends Error {
|
|
3565
3672
|
status;
|
|
@@ -5426,7 +5533,16 @@ var init_obsidian_fs = __esm({
|
|
|
5426
5533
|
if (since !== void 0 && mtime < since) continue;
|
|
5427
5534
|
const body = await fs4.readFile(abs, "utf-8");
|
|
5428
5535
|
const hash = computeBodyHash(body);
|
|
5429
|
-
|
|
5536
|
+
let id;
|
|
5537
|
+
try {
|
|
5538
|
+
id = this.pathToDocId(rel);
|
|
5539
|
+
} catch (err) {
|
|
5540
|
+
console.error(
|
|
5541
|
+
`[obsidian-fs:${this.vault.name}] skipping un-addressable file ${JSON.stringify(rel)}: ${err instanceof Error ? err.message : String(err)}`
|
|
5542
|
+
);
|
|
5543
|
+
continue;
|
|
5544
|
+
}
|
|
5545
|
+
yield { id, mtime, hash };
|
|
5430
5546
|
yielded++;
|
|
5431
5547
|
}
|
|
5432
5548
|
}
|
|
@@ -7387,7 +7503,7 @@ var init_validator = __esm({
|
|
|
7387
7503
|
});
|
|
7388
7504
|
|
|
7389
7505
|
// src/memory/contract/default-v1.ts
|
|
7390
|
-
import { z as
|
|
7506
|
+
import { z as z10 } from "zod";
|
|
7391
7507
|
var requiredKeys, baseShape, DEFAULT_MEMORY_V1;
|
|
7392
7508
|
var init_default_v1 = __esm({
|
|
7393
7509
|
"src/memory/contract/default-v1.ts"() {
|
|
@@ -7402,15 +7518,15 @@ var init_default_v1 = __esm({
|
|
|
7402
7518
|
"superseded_by",
|
|
7403
7519
|
"type"
|
|
7404
7520
|
];
|
|
7405
|
-
baseShape =
|
|
7406
|
-
source:
|
|
7407
|
-
confidence:
|
|
7408
|
-
evidence:
|
|
7409
|
-
status:
|
|
7410
|
-
observed_at:
|
|
7411
|
-
superseded_by:
|
|
7412
|
-
type:
|
|
7413
|
-
superseded_reason:
|
|
7521
|
+
baseShape = z10.object({
|
|
7522
|
+
source: z10.enum(["agent", "user", "imported"]),
|
|
7523
|
+
confidence: z10.enum(["direct", "inferred", "uncertain"]),
|
|
7524
|
+
evidence: z10.array(z10.string()),
|
|
7525
|
+
status: z10.enum(["active", "superseded", "archived"]).default("active"),
|
|
7526
|
+
observed_at: z10.string().datetime({ offset: true }),
|
|
7527
|
+
superseded_by: z10.string().nullable().default(null),
|
|
7528
|
+
type: z10.string().min(1),
|
|
7529
|
+
superseded_reason: z10.string().optional()
|
|
7414
7530
|
}).passthrough().superRefine((data, ctx) => {
|
|
7415
7531
|
if (data.status === "superseded") {
|
|
7416
7532
|
if (data.superseded_by === null || data.superseded_by === void 0) {
|
|
@@ -7443,7 +7559,7 @@ var init_default_v1 = __esm({
|
|
|
7443
7559
|
});
|
|
7444
7560
|
|
|
7445
7561
|
// src/memory/contract/default-brief-v1.ts
|
|
7446
|
-
import { z as
|
|
7562
|
+
import { z as z11 } from "zod";
|
|
7447
7563
|
var requiredKeys2, baseShape2, DEFAULT_BRIEF_V1;
|
|
7448
7564
|
var init_default_brief_v1 = __esm({
|
|
7449
7565
|
"src/memory/contract/default-brief-v1.ts"() {
|
|
@@ -7465,29 +7581,29 @@ var init_default_brief_v1 = __esm({
|
|
|
7465
7581
|
"compiled_at",
|
|
7466
7582
|
"source_hashes"
|
|
7467
7583
|
];
|
|
7468
|
-
baseShape2 =
|
|
7584
|
+
baseShape2 = z11.object({
|
|
7469
7585
|
// ── Base shape inherited from default-v1 ────────────────────────
|
|
7470
|
-
source:
|
|
7471
|
-
confidence:
|
|
7472
|
-
evidence:
|
|
7586
|
+
source: z11.enum(["agent", "user", "imported"]),
|
|
7587
|
+
confidence: z11.enum(["direct", "inferred", "uncertain"]),
|
|
7588
|
+
evidence: z11.array(z11.string()),
|
|
7473
7589
|
// ── Status enum WIDENED for briefs: + "stale" ──────────────────
|
|
7474
|
-
status:
|
|
7475
|
-
observed_at:
|
|
7476
|
-
superseded_by:
|
|
7477
|
-
type:
|
|
7478
|
-
superseded_reason:
|
|
7590
|
+
status: z11.enum(["active", "stale", "superseded", "archived"]).default("active"),
|
|
7591
|
+
observed_at: z11.string().datetime({ offset: true }),
|
|
7592
|
+
superseded_by: z11.string().nullable().default(null),
|
|
7593
|
+
type: z11.string().min(1),
|
|
7594
|
+
superseded_reason: z11.string().optional(),
|
|
7479
7595
|
// ── Brief-specific properties (D-11 brief shape) ───────────────
|
|
7480
|
-
target:
|
|
7596
|
+
target: z11.string().min(1),
|
|
7481
7597
|
/**
|
|
7482
7598
|
* Brief purpose — free text but bounded at 500 chars so
|
|
7483
7599
|
* `list_briefs` stays scannable. Lower bound `min(1)` matches
|
|
7484
7600
|
* BRF-03 "no empty purpose".
|
|
7485
7601
|
*/
|
|
7486
|
-
purpose:
|
|
7602
|
+
purpose: z11.string().min(1).max(500),
|
|
7487
7603
|
/** DocId list of all sources the brief was compiled from. */
|
|
7488
|
-
compiled_from:
|
|
7604
|
+
compiled_from: z11.array(z11.string()).min(1),
|
|
7489
7605
|
/** ISO-8601 datetime with offset (mirrors observed_at). */
|
|
7490
|
-
compiled_at:
|
|
7606
|
+
compiled_at: z11.string().datetime({ offset: true }),
|
|
7491
7607
|
/**
|
|
7492
7608
|
* Record<ChunkId, BriefSourceHash> — staleness contract. The map
|
|
7493
7609
|
* key is the public ChunkId (`<DocId>#chunk-<7-hex>`); the value
|
|
@@ -7495,12 +7611,12 @@ var init_default_brief_v1 = __esm({
|
|
|
7495
7611
|
* the cross-field invariant below only REQUIRES it on stale; the
|
|
7496
7612
|
* validator still rejects `status: "stale"` writes that omit it.
|
|
7497
7613
|
*/
|
|
7498
|
-
source_hashes:
|
|
7614
|
+
source_hashes: z11.record(z11.string(), z11.string()).optional(),
|
|
7499
7615
|
/**
|
|
7500
7616
|
* Daemon-computed list of source DocIds whose hashes have
|
|
7501
7617
|
* diverged. Populated when `status` flips to `"stale"`.
|
|
7502
7618
|
*/
|
|
7503
|
-
changed_sources:
|
|
7619
|
+
changed_sources: z11.array(z11.string()).optional()
|
|
7504
7620
|
}).passthrough().superRefine((data, ctx) => {
|
|
7505
7621
|
if (data.status === "superseded") {
|
|
7506
7622
|
if (data.superseded_by === null || data.superseded_by === void 0) {
|
|
@@ -7570,36 +7686,36 @@ var init_contract_yaml_read = __esm({
|
|
|
7570
7686
|
});
|
|
7571
7687
|
|
|
7572
7688
|
// src/memory/contract/schema.ts
|
|
7573
|
-
import { z as
|
|
7689
|
+
import { z as z12 } from "zod";
|
|
7574
7690
|
var PropertyRuleSchema, CrossFieldRuleSchema, MemoryContractYamlSchema;
|
|
7575
7691
|
var init_schema2 = __esm({
|
|
7576
7692
|
"src/memory/contract/schema.ts"() {
|
|
7577
7693
|
"use strict";
|
|
7578
7694
|
init_esm_shims();
|
|
7579
|
-
PropertyRuleSchema =
|
|
7580
|
-
type:
|
|
7581
|
-
allowed:
|
|
7582
|
-
default:
|
|
7583
|
-
items:
|
|
7584
|
-
min_length:
|
|
7695
|
+
PropertyRuleSchema = z12.object({
|
|
7696
|
+
type: z12.enum(["string", "datetime", "array", "doc_id", "number", "boolean", "reference", "date"]),
|
|
7697
|
+
allowed: z12.array(z12.string()).optional(),
|
|
7698
|
+
default: z12.unknown().optional(),
|
|
7699
|
+
items: z12.object({ type: z12.string() }).optional(),
|
|
7700
|
+
min_length: z12.number().optional(),
|
|
7585
7701
|
/** When true, the property accepts `null` as a sentinel value (in
|
|
7586
7702
|
* addition to whatever `type` says). Used for required-but-null-by-
|
|
7587
7703
|
* default properties like `superseded_by` on active observations. */
|
|
7588
|
-
nullable:
|
|
7704
|
+
nullable: z12.boolean().optional()
|
|
7589
7705
|
});
|
|
7590
|
-
CrossFieldRuleSchema =
|
|
7591
|
-
when:
|
|
7592
|
-
require:
|
|
7706
|
+
CrossFieldRuleSchema = z12.object({
|
|
7707
|
+
when: z12.string(),
|
|
7708
|
+
require: z12.string()
|
|
7593
7709
|
});
|
|
7594
|
-
MemoryContractYamlSchema =
|
|
7595
|
-
name:
|
|
7596
|
-
version:
|
|
7597
|
-
required_properties:
|
|
7598
|
-
optional_properties:
|
|
7599
|
-
cross_field_rules:
|
|
7600
|
-
naming:
|
|
7601
|
-
strategy:
|
|
7602
|
-
pattern:
|
|
7710
|
+
MemoryContractYamlSchema = z12.object({
|
|
7711
|
+
name: z12.string().min(1),
|
|
7712
|
+
version: z12.string().default("1.0"),
|
|
7713
|
+
required_properties: z12.record(z12.string(), PropertyRuleSchema),
|
|
7714
|
+
optional_properties: z12.record(z12.string(), PropertyRuleSchema).default({}),
|
|
7715
|
+
cross_field_rules: z12.array(CrossFieldRuleSchema).default([]),
|
|
7716
|
+
naming: z12.object({
|
|
7717
|
+
strategy: z12.enum(["caller-provided", "date-slug", "adapter-assigned"]),
|
|
7718
|
+
pattern: z12.string().optional()
|
|
7603
7719
|
})
|
|
7604
7720
|
});
|
|
7605
7721
|
}
|
|
@@ -7607,7 +7723,7 @@ var init_schema2 = __esm({
|
|
|
7607
7723
|
|
|
7608
7724
|
// src/memory/contract/loader.ts
|
|
7609
7725
|
import { parse as parseYaml } from "yaml";
|
|
7610
|
-
import { z as
|
|
7726
|
+
import { z as z13 } from "zod";
|
|
7611
7727
|
function __cacheContract(name, contract) {
|
|
7612
7728
|
contractCache.set(name, contract);
|
|
7613
7729
|
}
|
|
@@ -9205,7 +9321,7 @@ var init_memory_stats = __esm({
|
|
|
9205
9321
|
});
|
|
9206
9322
|
|
|
9207
9323
|
// src/memory/resources/index.ts
|
|
9208
|
-
var RESOURCE_URI_LIST_SINKS, RESOURCE_URI_MEMORY_STATS, RESOURCE_URI_LIST_BRIEFS, RESOURCE_URI_LIST_CONTRACTS, RESOURCE_URI_LIST_CONTRACT_VERBS, RESOURCE_URI_VAULTS, RESOURCE_URI_MODELS, RESOURCE_URI_RECENT, RESOURCE_URI_STATS, RESOURCE_URI_BACKLINKS;
|
|
9324
|
+
var RESOURCE_URI_LIST_SINKS, RESOURCE_URI_MEMORY_STATS, RESOURCE_URI_LIST_BRIEFS, RESOURCE_URI_LIST_CONTRACTS, RESOURCE_URI_LIST_CONTRACT_VERBS, RESOURCE_URI_SOURCES, RESOURCE_URI_VAULTS, RESOURCE_URI_MODELS, RESOURCE_URI_RECENT, RESOURCE_URI_STATS, RESOURCE_URI_BACKLINKS;
|
|
9209
9325
|
var init_resources = __esm({
|
|
9210
9326
|
"src/memory/resources/index.ts"() {
|
|
9211
9327
|
"use strict";
|
|
@@ -9217,6 +9333,7 @@ var init_resources = __esm({
|
|
|
9217
9333
|
RESOURCE_URI_LIST_BRIEFS = "vault-memory://briefs";
|
|
9218
9334
|
RESOURCE_URI_LIST_CONTRACTS = "vault-memory://contracts";
|
|
9219
9335
|
RESOURCE_URI_LIST_CONTRACT_VERBS = "vault-memory://contract-verbs";
|
|
9336
|
+
RESOURCE_URI_SOURCES = "vault-memory://sources";
|
|
9220
9337
|
RESOURCE_URI_VAULTS = "vault-memory://vaults";
|
|
9221
9338
|
RESOURCE_URI_MODELS = "vault-memory://models";
|
|
9222
9339
|
RESOURCE_URI_RECENT = "vault-memory://recent";
|
|
@@ -9278,6 +9395,25 @@ var init_resource_registry = __esm({
|
|
|
9278
9395
|
description: "List baseline assembly verbs + custom (mcp://) verbs in use, with invocation_count + last_seen aggregated from contract_audit (D-A2b). Baseline verbs are constant per ADR-006 \xA7Decision 3.",
|
|
9279
9396
|
mimeType: "application/json"
|
|
9280
9397
|
},
|
|
9398
|
+
// ─── SOURCES-REGISTRY.md §5 (Stage 2) — peer-MCP source discovery ───────
|
|
9399
|
+
{
|
|
9400
|
+
name: "sources",
|
|
9401
|
+
uriTemplate: "vault-memory://sources",
|
|
9402
|
+
description: "List peer MCP servers vault-memory connects to, with per-source status (connected/unavailable/unreachable), tool_count, and last_refreshed. vault-memory itself is not included. SOURCES-REGISTRY \xA75.1.",
|
|
9403
|
+
mimeType: "application/json"
|
|
9404
|
+
},
|
|
9405
|
+
{
|
|
9406
|
+
name: "source-tools",
|
|
9407
|
+
uriTemplate: "vault-memory://sources/{name}/tools",
|
|
9408
|
+
description: "List the cached tools/list for one peer MCP source. Empty when the source is not connected. SOURCES-REGISTRY \xA75.2.",
|
|
9409
|
+
mimeType: "application/json"
|
|
9410
|
+
},
|
|
9411
|
+
{
|
|
9412
|
+
name: "source-tool",
|
|
9413
|
+
uriTemplate: "vault-memory://sources/{name}/tools/{tool}",
|
|
9414
|
+
description: "Read a single tool's schema from one peer MCP source, inlined from the cached tools/list. SOURCES-REGISTRY \xA75.3.",
|
|
9415
|
+
mimeType: "application/json"
|
|
9416
|
+
},
|
|
9281
9417
|
// ─── Phase 8 (Plan 08-05 / REL-08) — promoted from v1 tools ─────────────
|
|
9282
9418
|
{
|
|
9283
9419
|
name: "vaults",
|
|
@@ -11789,11 +11925,11 @@ var init_obsidian_fs3 = __esm({
|
|
|
11789
11925
|
});
|
|
11790
11926
|
|
|
11791
11927
|
// src/tool-registry.ts
|
|
11792
|
-
import { z as
|
|
11928
|
+
import { z as z14 } from "zod";
|
|
11793
11929
|
function buildToolSchema(name) {
|
|
11794
11930
|
const builder = SCHEMA_BUILDERS[name];
|
|
11795
11931
|
if (builder) return builder();
|
|
11796
|
-
return
|
|
11932
|
+
return z14.object(TOOL_SCHEMAS[name]);
|
|
11797
11933
|
}
|
|
11798
11934
|
var TOOLS, DOC_ID_PATTERN2, PredicateSchema, TOOL_SCHEMAS, SCHEMA_BUILDERS;
|
|
11799
11935
|
var init_tool_registry = __esm({
|
|
@@ -12666,243 +12802,243 @@ var init_tool_registry = __esm({
|
|
|
12666
12802
|
}
|
|
12667
12803
|
];
|
|
12668
12804
|
DOC_ID_PATTERN2 = /^[a-z][a-z0-9-]*:\/\/[^/]+\/.+$/;
|
|
12669
|
-
PredicateSchema =
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12805
|
+
PredicateSchema = z14.union([
|
|
12806
|
+
z14.string(),
|
|
12807
|
+
z14.number(),
|
|
12808
|
+
z14.boolean(),
|
|
12809
|
+
z14.null(),
|
|
12810
|
+
z14.object({ $in: z14.array(z14.union([z14.string(), z14.number(), z14.boolean(), z14.null()])) }),
|
|
12811
|
+
z14.object({ $exists: z14.boolean() }),
|
|
12812
|
+
z14.object({ $contains: z14.union([z14.string(), z14.number(), z14.boolean(), z14.null()]) })
|
|
12677
12813
|
]);
|
|
12678
12814
|
TOOL_SCHEMAS = {
|
|
12679
12815
|
list_vaults: {},
|
|
12680
12816
|
read_note: {
|
|
12681
|
-
vault:
|
|
12682
|
-
path:
|
|
12817
|
+
vault: z14.string(),
|
|
12818
|
+
path: z14.string()
|
|
12683
12819
|
},
|
|
12684
12820
|
search_semantic: {
|
|
12685
|
-
query:
|
|
12686
|
-
vaults:
|
|
12687
|
-
top_k:
|
|
12688
|
-
exclude_paths:
|
|
12821
|
+
query: z14.string().min(1),
|
|
12822
|
+
vaults: z14.array(z14.string()).optional(),
|
|
12823
|
+
top_k: z14.number().int().positive().max(100).optional().default(10),
|
|
12824
|
+
exclude_paths: z14.array(z14.string()).optional()
|
|
12689
12825
|
},
|
|
12690
12826
|
search_text: {
|
|
12691
|
-
query:
|
|
12692
|
-
vaults:
|
|
12693
|
-
top_k:
|
|
12694
|
-
exclude_paths:
|
|
12827
|
+
query: z14.string().min(1),
|
|
12828
|
+
vaults: z14.array(z14.string()).optional(),
|
|
12829
|
+
top_k: z14.number().int().positive().max(100).optional().default(10),
|
|
12830
|
+
exclude_paths: z14.array(z14.string()).optional()
|
|
12695
12831
|
},
|
|
12696
12832
|
search_hybrid: {
|
|
12697
|
-
query:
|
|
12698
|
-
vaults:
|
|
12699
|
-
top_k:
|
|
12700
|
-
rrf_k:
|
|
12701
|
-
exclude_paths:
|
|
12702
|
-
rerank:
|
|
12833
|
+
query: z14.string().min(1),
|
|
12834
|
+
vaults: z14.array(z14.string()).optional(),
|
|
12835
|
+
top_k: z14.number().int().positive().max(100).optional().default(10),
|
|
12836
|
+
rrf_k: z14.number().int().positive().max(1e3).optional().default(60),
|
|
12837
|
+
exclude_paths: z14.array(z14.string()).optional(),
|
|
12838
|
+
rerank: z14.boolean().optional().default(false),
|
|
12703
12839
|
// Phase 3 / 03-05 additive params — D-07, D-08, ASM-07, ASM-08.
|
|
12704
12840
|
// All `.optional()` with defaults that vanish when unset, so v1
|
|
12705
12841
|
// callers see no behavior change.
|
|
12706
|
-
recency_weight:
|
|
12707
|
-
authority_weight:
|
|
12708
|
-
half_life_days:
|
|
12709
|
-
include_superseded:
|
|
12842
|
+
recency_weight: z14.number().optional().default(0),
|
|
12843
|
+
authority_weight: z14.number().optional().default(0),
|
|
12844
|
+
half_life_days: z14.number().positive().optional().default(30),
|
|
12845
|
+
include_superseded: z14.boolean().optional().default(false),
|
|
12710
12846
|
// ── Phase 4 / 04-04 / GRA-03 (D-15): additive auto-expansion ──
|
|
12711
12847
|
// Nested under a single optional `expand` object per D-15. When
|
|
12712
12848
|
// omitted, hybridSearch behavior is byte-identical to v1 (the
|
|
12713
12849
|
// guard `if (opts.expand && opts.expandDeps && ...)` at the end of
|
|
12714
12850
|
// `src/search/hybrid.ts` short-circuits entirely). The literal-
|
|
12715
12851
|
// union for `hops` enforces the D-05 hop cap at the boundary.
|
|
12716
|
-
expand:
|
|
12717
|
-
hops:
|
|
12718
|
-
direction:
|
|
12719
|
-
edge_types:
|
|
12852
|
+
expand: z14.object({
|
|
12853
|
+
hops: z14.union([z14.literal(1), z14.literal(2)]),
|
|
12854
|
+
direction: z14.enum(["forward", "backward", "both"]).optional(),
|
|
12855
|
+
edge_types: z14.array(z14.enum(["wikilink", "mention", "frontmatter-ref", "hyperlink"])).optional()
|
|
12720
12856
|
}).optional()
|
|
12721
12857
|
},
|
|
12722
12858
|
list_backlinks: {
|
|
12723
|
-
vault:
|
|
12724
|
-
path:
|
|
12859
|
+
vault: z14.string(),
|
|
12860
|
+
path: z14.string()
|
|
12725
12861
|
},
|
|
12726
12862
|
list_forward_links: {
|
|
12727
|
-
vault:
|
|
12728
|
-
path:
|
|
12729
|
-
include_broken:
|
|
12863
|
+
vault: z14.string(),
|
|
12864
|
+
path: z14.string(),
|
|
12865
|
+
include_broken: z14.boolean().optional().default(true)
|
|
12730
12866
|
},
|
|
12731
12867
|
find_broken_links: {
|
|
12732
|
-
vault:
|
|
12868
|
+
vault: z14.string()
|
|
12733
12869
|
},
|
|
12734
12870
|
query_frontmatter: {
|
|
12735
|
-
vault:
|
|
12736
|
-
where:
|
|
12737
|
-
limit:
|
|
12871
|
+
vault: z14.string(),
|
|
12872
|
+
where: z14.record(z14.string(), PredicateSchema),
|
|
12873
|
+
limit: z14.number().int().positive().max(1e3).optional().default(100)
|
|
12738
12874
|
},
|
|
12739
12875
|
write_note: {
|
|
12740
|
-
vault:
|
|
12741
|
-
path:
|
|
12742
|
-
content:
|
|
12743
|
-
frontmatter:
|
|
12744
|
-
expected_hash:
|
|
12745
|
-
client_id:
|
|
12876
|
+
vault: z14.string(),
|
|
12877
|
+
path: z14.string(),
|
|
12878
|
+
content: z14.string(),
|
|
12879
|
+
frontmatter: z14.record(z14.string(), z14.unknown()).nullable().optional(),
|
|
12880
|
+
expected_hash: z14.string().optional(),
|
|
12881
|
+
client_id: z14.string().optional()
|
|
12746
12882
|
},
|
|
12747
12883
|
update_frontmatter: {
|
|
12748
|
-
vault:
|
|
12749
|
-
path:
|
|
12750
|
-
merge:
|
|
12751
|
-
expected_hash:
|
|
12752
|
-
client_id:
|
|
12884
|
+
vault: z14.string(),
|
|
12885
|
+
path: z14.string(),
|
|
12886
|
+
merge: z14.record(z14.string(), z14.unknown()),
|
|
12887
|
+
expected_hash: z14.string().optional(),
|
|
12888
|
+
client_id: z14.string().optional()
|
|
12753
12889
|
},
|
|
12754
12890
|
delete_note: {
|
|
12755
|
-
vault:
|
|
12756
|
-
path:
|
|
12757
|
-
expected_hash:
|
|
12758
|
-
client_id:
|
|
12891
|
+
vault: z14.string(),
|
|
12892
|
+
path: z14.string(),
|
|
12893
|
+
expected_hash: z14.string(),
|
|
12894
|
+
client_id: z14.string().optional()
|
|
12759
12895
|
},
|
|
12760
12896
|
audit_log: {
|
|
12761
|
-
vault:
|
|
12762
|
-
note_path:
|
|
12763
|
-
op:
|
|
12764
|
-
since:
|
|
12765
|
-
limit:
|
|
12897
|
+
vault: z14.string(),
|
|
12898
|
+
note_path: z14.string().optional(),
|
|
12899
|
+
op: z14.enum(["create", "update", "delete"]).optional(),
|
|
12900
|
+
since: z14.number().int().nonnegative().optional(),
|
|
12901
|
+
limit: z14.number().int().positive().max(1e3).optional().default(50),
|
|
12766
12902
|
// Plan 02-06 (MEM-08): additive optional filter. The MCP tool's
|
|
12767
12903
|
// `description` string is INTENTIONALLY unchanged — Phase 1 byte-identity
|
|
12768
12904
|
// is preserved. New capability is documented in docs/tools/audit_log.md.
|
|
12769
|
-
is_memory_sink_write:
|
|
12905
|
+
is_memory_sink_write: z14.boolean().optional()
|
|
12770
12906
|
},
|
|
12771
12907
|
list_models: {
|
|
12772
|
-
vault:
|
|
12908
|
+
vault: z14.string()
|
|
12773
12909
|
},
|
|
12774
12910
|
start_shadow_index: {
|
|
12775
|
-
vault:
|
|
12776
|
-
model:
|
|
12777
|
-
batch_size:
|
|
12911
|
+
vault: z14.string(),
|
|
12912
|
+
model: z14.string().min(1),
|
|
12913
|
+
batch_size: z14.number().int().positive().max(256).optional()
|
|
12778
12914
|
},
|
|
12779
12915
|
switch_active_model: {
|
|
12780
|
-
vault:
|
|
12781
|
-
model_name:
|
|
12916
|
+
vault: z14.string(),
|
|
12917
|
+
model_name: z14.string().min(1)
|
|
12782
12918
|
},
|
|
12783
12919
|
vacuum_embeddings: {
|
|
12784
|
-
vault:
|
|
12920
|
+
vault: z14.string()
|
|
12785
12921
|
},
|
|
12786
12922
|
index_runs: {
|
|
12787
|
-
vault:
|
|
12788
|
-
limit:
|
|
12923
|
+
vault: z14.string(),
|
|
12924
|
+
limit: z14.number().int().positive().max(200).optional().default(20)
|
|
12789
12925
|
},
|
|
12790
12926
|
search: {
|
|
12791
|
-
query:
|
|
12792
|
-
limit:
|
|
12927
|
+
query: z14.string().min(1),
|
|
12928
|
+
limit: z14.number().int().positive().max(50).optional().default(10)
|
|
12793
12929
|
},
|
|
12794
12930
|
fetch: {
|
|
12795
|
-
id:
|
|
12931
|
+
id: z14.string().min(1)
|
|
12796
12932
|
},
|
|
12797
12933
|
vault_stats: {
|
|
12798
|
-
vault:
|
|
12934
|
+
vault: z14.string().optional()
|
|
12799
12935
|
},
|
|
12800
12936
|
recent_notes: {
|
|
12801
|
-
vault:
|
|
12802
|
-
limit:
|
|
12803
|
-
since:
|
|
12937
|
+
vault: z14.string().optional(),
|
|
12938
|
+
limit: z14.number().int().positive().max(200).optional().default(20),
|
|
12939
|
+
since: z14.number().int().nonnegative().optional()
|
|
12804
12940
|
},
|
|
12805
12941
|
suggest_frontmatter: {
|
|
12806
|
-
vault:
|
|
12807
|
-
path:
|
|
12808
|
-
content:
|
|
12809
|
-
title:
|
|
12810
|
-
folder_hint:
|
|
12942
|
+
vault: z14.string(),
|
|
12943
|
+
path: z14.string().optional(),
|
|
12944
|
+
content: z14.string().optional(),
|
|
12945
|
+
title: z14.string().optional(),
|
|
12946
|
+
folder_hint: z14.string().optional()
|
|
12811
12947
|
},
|
|
12812
12948
|
// ── Phase 2 memory tools (Plan 02-04) ───────────────────────────────────
|
|
12813
12949
|
record_observation: {
|
|
12814
|
-
vault:
|
|
12815
|
-
claim:
|
|
12816
|
-
evidence:
|
|
12817
|
-
confidence:
|
|
12818
|
-
type:
|
|
12950
|
+
vault: z14.string().min(1).describe("Vault name (registered in [vaults] config block)"),
|
|
12951
|
+
claim: z14.string().min(1).describe("Short natural-language statement of the observation (becomes title + body)"),
|
|
12952
|
+
evidence: z14.array(z14.string()).describe("DocIds or quoted source spans supporting the claim; empty array allowed"),
|
|
12953
|
+
confidence: z14.enum(["direct", "inferred", "uncertain"]).describe("How the agent arrived at this claim"),
|
|
12954
|
+
type: z14.string().min(1).describe(
|
|
12819
12955
|
"Observation type per the sink contract (e.g. 'observation', 'hypothesis', 'decision')"
|
|
12820
12956
|
),
|
|
12821
|
-
sink:
|
|
12957
|
+
sink: z14.string().min(1).optional().describe(
|
|
12822
12958
|
"Memory sink name OR full obsidian-fs://\u2026 handle. Defaults to the vault's default sink."
|
|
12823
12959
|
),
|
|
12824
|
-
properties:
|
|
12960
|
+
properties: z14.record(z14.string(), z14.unknown()).optional().describe(
|
|
12825
12961
|
"Escape-hatch: contract-allowed extra properties; merged AFTER sugar args (caller wins)"
|
|
12826
12962
|
)
|
|
12827
12963
|
},
|
|
12828
12964
|
supersede: {
|
|
12829
|
-
doc_id:
|
|
12830
|
-
replacement_doc_id:
|
|
12831
|
-
reason:
|
|
12965
|
+
doc_id: z14.string().regex(DOC_ID_PATTERN2).describe("DocId of the document being superseded"),
|
|
12966
|
+
replacement_doc_id: z14.string().regex(DOC_ID_PATTERN2).describe("DocId of the replacement document"),
|
|
12967
|
+
reason: z14.string().min(1).describe("Why the old document is being retired; written to superseded_reason")
|
|
12832
12968
|
},
|
|
12833
12969
|
// ── Phase 5 brief tools (Plan 05-02 / BRF-03, BRF-04) ───────────────────
|
|
12834
12970
|
compile_brief: {
|
|
12835
|
-
vault:
|
|
12836
|
-
target:
|
|
12837
|
-
source_doc_ids:
|
|
12838
|
-
purpose:
|
|
12839
|
-
max_tokens:
|
|
12840
|
-
prepared_text:
|
|
12841
|
-
sink:
|
|
12971
|
+
vault: z14.string().min(1).describe("Vault name (registered in [vaults] config block)"),
|
|
12972
|
+
target: z14.string().min(1).describe("Stable cross-version handle for the brief (e.g. 'atlas-q3')"),
|
|
12973
|
+
source_doc_ids: z14.array(z14.string().regex(DOC_ID_PATTERN2)).min(1).max(50).describe("DocIds the brief is compiled from; deduped, capped at 50 (D-03)"),
|
|
12974
|
+
purpose: z14.string().min(1).max(500).describe("Free-form purpose; bounded so list_briefs stays scannable"),
|
|
12975
|
+
max_tokens: z14.number().int().positive().optional().default(2e3).describe("Hint for the LLM ladder; default 2000"),
|
|
12976
|
+
prepared_text: z14.string().min(1).optional().describe("D-10 tier 3 fallback when no LLM is reachable \u2014 verbatim body to stitch in"),
|
|
12977
|
+
sink: z14.string().min(1).optional().describe("Override the default `_memory/_briefs` sink")
|
|
12842
12978
|
},
|
|
12843
12979
|
get_brief: {
|
|
12844
|
-
vault:
|
|
12845
|
-
target:
|
|
12846
|
-
max_age_days:
|
|
12847
|
-
allow_stale:
|
|
12980
|
+
vault: z14.string().min(1).describe("Vault name (registered in [vaults] config block)"),
|
|
12981
|
+
target: z14.string().min(1).describe("Stable cross-version handle for the brief"),
|
|
12982
|
+
max_age_days: z14.number().int().nonnegative().optional().describe("Reject briefs older than this many days unless allow_stale=true"),
|
|
12983
|
+
allow_stale: z14.boolean().optional().default(false).describe(
|
|
12848
12984
|
"When true, return briefs flagged stale or too_old with annotation rather than null"
|
|
12849
12985
|
)
|
|
12850
12986
|
},
|
|
12851
12987
|
// ── Phase 3 assembly tools (Plan 03-02 / ASM-02) ────────────────────────
|
|
12852
12988
|
get_outline: {
|
|
12853
|
-
doc_id:
|
|
12854
|
-
vaults:
|
|
12989
|
+
doc_id: z14.string().regex(DOC_ID_PATTERN2).describe("Opaque DocId (obsidian-fs://<vault>/<path>) of the document"),
|
|
12990
|
+
vaults: z14.array(z14.string().min(1)).optional().describe("Optional vault filter; usually omitted (the DocId names a vault)")
|
|
12855
12991
|
},
|
|
12856
12992
|
// ── Phase 3 assembly tools (Plan 03-03) ─────────────────────────────────
|
|
12857
12993
|
search_sections: {
|
|
12858
|
-
query:
|
|
12859
|
-
limit:
|
|
12860
|
-
vaults:
|
|
12994
|
+
query: z14.string().min(1),
|
|
12995
|
+
limit: z14.number().int().positive().max(50).optional().default(10),
|
|
12996
|
+
vaults: z14.array(z14.string().min(1)).optional(),
|
|
12861
12997
|
// Forward-compat with slice 03-05's authority/staleness rescore.
|
|
12862
12998
|
// Accepted today; ignored by the controller until 03-05 wires the
|
|
12863
12999
|
// forwarding inside hybridSearch. See 03-03-DEVIATIONS.md.
|
|
12864
|
-
recency_weight:
|
|
12865
|
-
authority_weight:
|
|
12866
|
-
include_superseded:
|
|
13000
|
+
recency_weight: z14.number().min(0).optional().default(0),
|
|
13001
|
+
authority_weight: z14.number().min(0).optional().default(0),
|
|
13002
|
+
include_superseded: z14.boolean().optional().default(false)
|
|
12867
13003
|
},
|
|
12868
13004
|
// ── Phase 2 memory tools (Plan 02-05) ───────────────────────────────────
|
|
12869
13005
|
recall: {
|
|
12870
|
-
query:
|
|
12871
|
-
min_confidence:
|
|
13006
|
+
query: z14.string().min(1).describe("Natural-language query; routes through hybrid (semantic + BM25) search"),
|
|
13007
|
+
min_confidence: z14.enum(["direct", "inferred", "uncertain"]).optional().describe(
|
|
12872
13008
|
"Exclude docs whose confidence ordinal is lower than this (direct=3, inferred=2, uncertain=1)"
|
|
12873
13009
|
),
|
|
12874
|
-
types:
|
|
12875
|
-
max_age_days:
|
|
12876
|
-
sink:
|
|
13010
|
+
types: z14.array(z14.string().min(1)).optional().describe("Restrict to docs whose `type` property is in this set"),
|
|
13011
|
+
max_age_days: z14.number().int().positive().optional().describe("Exclude docs whose `observed_at` is older than this many days"),
|
|
13012
|
+
sink: z14.string().min(1).optional().describe(
|
|
12877
13013
|
"Memory sink name OR full obsidian-fs://\u2026 handle. Defaults to all configured sinks."
|
|
12878
13014
|
),
|
|
12879
|
-
limit:
|
|
12880
|
-
vaults:
|
|
13015
|
+
limit: z14.number().int().positive().max(200).optional().describe("Maximum results AFTER filter+sort; default 20"),
|
|
13016
|
+
vaults: z14.array(z14.string().min(1)).optional().describe("Restrict to these vault names; defaults to all configured")
|
|
12881
13017
|
},
|
|
12882
13018
|
// ── Phase 3 assembly tools (Plan 03-04 / ASM-01) ────────────────────────
|
|
12883
13019
|
get_document_bundle: {
|
|
12884
|
-
doc_id:
|
|
13020
|
+
doc_id: z14.string().regex(DOC_ID_PATTERN2).describe("Opaque DocId (obsidian-fs://<vault>/<path>) of the anchor document"),
|
|
12885
13021
|
// v2.0.0 accepts only depth:1. The literal pin guarantees Zod
|
|
12886
13022
|
// rejects any other value at the boundary so the controller does
|
|
12887
13023
|
// not need to clamp. Phase 4 may widen additively (z.union of
|
|
12888
13024
|
// literals, or `z.number().int().min(1).max(2)`).
|
|
12889
|
-
depth:
|
|
12890
|
-
vaults:
|
|
13025
|
+
depth: z14.literal(1).optional().default(1).describe("Link-walk depth. v2.0.0: only 1 (one-hop). Phase 4 may widen."),
|
|
13026
|
+
vaults: z14.array(z14.string().min(1)).optional().describe("Optional vault filter; usually omitted (the DocId names a vault)")
|
|
12891
13027
|
},
|
|
12892
13028
|
// ── Phase 4 graph tools (Plan 04-03 / GRA-01) ───────────────────────────
|
|
12893
13029
|
expand: {
|
|
12894
|
-
seed_doc_ids:
|
|
13030
|
+
seed_doc_ids: z14.array(z14.string().regex(DOC_ID_PATTERN2)).min(1).describe(
|
|
12895
13031
|
"1+ opaque DocIds (e.g. obsidian-fs://<vault>/<path>) \u2014 seeds of the BFS."
|
|
12896
13032
|
),
|
|
12897
13033
|
// Hops hard-capped at 2 (D-05) via Zod literal union — `hops: 3`
|
|
12898
13034
|
// is rejected at the boundary; the controller does not clamp.
|
|
12899
|
-
hops:
|
|
12900
|
-
direction:
|
|
12901
|
-
edge_types:
|
|
12902
|
-
filter_properties:
|
|
13035
|
+
hops: z14.union([z14.literal(1), z14.literal(2)]).describe("Hop cap (1 or 2). v2.0.0 hard-caps at 2."),
|
|
13036
|
+
direction: z14.enum(["forward", "backward", "both"]).optional().default("both").describe("Edge traversal direction; default 'both'."),
|
|
13037
|
+
edge_types: z14.array(z14.enum(["wikilink", "mention", "frontmatter-ref", "hyperlink"])).optional().describe("Optional filter on edge types; default = all four types."),
|
|
13038
|
+
filter_properties: z14.record(z14.string(), z14.unknown()).optional().describe(
|
|
12903
13039
|
"Strict-equality predicate on document properties (e.g. {type: 'Project'})."
|
|
12904
13040
|
),
|
|
12905
|
-
include_superseded:
|
|
13041
|
+
include_superseded: z14.boolean().optional().default(false).describe(
|
|
12906
13042
|
"When false (default), docs whose properties.status === 'superseded' are dropped."
|
|
12907
13043
|
)
|
|
12908
13044
|
},
|
|
@@ -12916,50 +13052,50 @@ var init_tool_registry = __esm({
|
|
|
12916
13052
|
// works. The runtime path goes through `buildToolSchema("cluster")`
|
|
12917
13053
|
// which calls the SCHEMA_BUILDERS entry.
|
|
12918
13054
|
cluster: {
|
|
12919
|
-
query:
|
|
12920
|
-
seed_doc_ids:
|
|
13055
|
+
query: z14.string().min(1).optional(),
|
|
13056
|
+
seed_doc_ids: z14.array(z14.string().regex(DOC_ID_PATTERN2)).min(1).optional(),
|
|
12921
13057
|
// CR-02: `vault` scopes the `query` path on multi-vault setups so
|
|
12922
13058
|
// search_hybrid is not silently restricted to whichever vault
|
|
12923
13059
|
// sorts first in VaultManager insertion order. Optional at the
|
|
12924
13060
|
// schema layer; the runtime cluster() entry enforces the
|
|
12925
13061
|
// multi-vault-without-vault error.
|
|
12926
|
-
vault:
|
|
12927
|
-
method:
|
|
12928
|
-
query_top_k:
|
|
12929
|
-
force:
|
|
13062
|
+
vault: z14.string().min(1).optional(),
|
|
13063
|
+
method: z14.literal("edge-community"),
|
|
13064
|
+
query_top_k: z14.number().int().positive().max(200).optional().default(50),
|
|
13065
|
+
force: z14.boolean().optional().default(false)
|
|
12930
13066
|
},
|
|
12931
13067
|
// ── Phase 3 assembly tools (Plan 03-06) ─────────────────────────────────
|
|
12932
13068
|
assemble_dossier: {
|
|
12933
|
-
type:
|
|
13069
|
+
type: z14.string().min(1).describe(
|
|
12934
13070
|
"Exact-match value for properties.type on the anchor document (D-03 \u2014 no fuzzy match)"
|
|
12935
13071
|
),
|
|
12936
|
-
key:
|
|
13072
|
+
key: z14.string().min(1).describe(
|
|
12937
13073
|
"Candidate key \u2014 matches the document's title OR any entry in properties.aliases (D-04)"
|
|
12938
13074
|
),
|
|
12939
|
-
vaults:
|
|
13075
|
+
vaults: z14.array(z14.string().min(1)).optional().describe("Restrict to these vault names; defaults to all configured")
|
|
12940
13076
|
},
|
|
12941
13077
|
// ── Phase 6 task-contract DSL (Plan 06-02 / D-A1 escape valve) ─────────
|
|
12942
13078
|
register_contracts_as_tools: {
|
|
12943
|
-
vault:
|
|
13079
|
+
vault: z14.string().min(1).optional().describe("Vault name; omit to apply to all vaults")
|
|
12944
13080
|
},
|
|
12945
13081
|
// ── Phase 6 task-contract DSL (Plan 06-03 / CON-05, Q-DESCRIBE) ────────
|
|
12946
13082
|
describe_contract: {
|
|
12947
|
-
name:
|
|
12948
|
-
vault:
|
|
13083
|
+
name: z14.string().min(1).describe("Registered contract name (see register_contracts_as_tools)"),
|
|
13084
|
+
vault: z14.string().min(1).optional().describe("Vault name; omit on single-vault setups")
|
|
12949
13085
|
},
|
|
12950
13086
|
// ── Phase 6 task-contract DSL (Plan 06-03 / CON-06) ────────────────────
|
|
12951
13087
|
instantiate_contract: {
|
|
12952
|
-
name:
|
|
12953
|
-
inputs:
|
|
12954
|
-
source_overrides:
|
|
12955
|
-
sink_overrides:
|
|
13088
|
+
name: z14.string().min(1).describe("Registered contract name"),
|
|
13089
|
+
inputs: z14.record(z14.string(), z14.unknown()).optional().default({}).describe("Contract inputs; validated against the contract's inputZodSchema"),
|
|
13090
|
+
source_overrides: z14.record(z14.string(), z14.string()).optional().describe("Override declared source handles by handle name"),
|
|
13091
|
+
sink_overrides: z14.record(z14.string(), z14.string()).optional().describe(
|
|
12956
13092
|
"Override declared sink handles by handle name. Targets MUST resolve through MemorySinkRegistry (D-A4c)."
|
|
12957
13093
|
),
|
|
12958
|
-
vault:
|
|
13094
|
+
vault: z14.string().min(1).optional().describe("Vault name; omit on single-vault setups")
|
|
12959
13095
|
}
|
|
12960
13096
|
};
|
|
12961
13097
|
SCHEMA_BUILDERS = {
|
|
12962
|
-
suggest_frontmatter: () =>
|
|
13098
|
+
suggest_frontmatter: () => z14.object(TOOL_SCHEMAS.suggest_frontmatter).refine((v) => v.path !== void 0 || v.content !== void 0, {
|
|
12963
13099
|
message: "suggest_frontmatter requires either `path` or `content`"
|
|
12964
13100
|
}),
|
|
12965
13101
|
// Plan 04-05 / D-15a — EXACTLY ONE of `query` or `seed_doc_ids` must
|
|
@@ -12968,7 +13104,7 @@ var init_tool_registry = __esm({
|
|
|
12968
13104
|
// so this Zod refinement is the early-rejection gate at the MCP
|
|
12969
13105
|
// boundary (cluster's internal validator handles the same case for
|
|
12970
13106
|
// direct callers that bypass Zod).
|
|
12971
|
-
cluster: () =>
|
|
13107
|
+
cluster: () => z14.object(TOOL_SCHEMAS.cluster).refine(
|
|
12972
13108
|
(v) => v.query !== void 0 && v.seed_doc_ids === void 0 || v.query === void 0 && v.seed_doc_ids !== void 0,
|
|
12973
13109
|
{
|
|
12974
13110
|
message: "cluster requires EXACTLY ONE of `query` or `seed_doc_ids` (D-15a mutual exclusion)"
|
|
@@ -13063,7 +13199,7 @@ var init_json_schema_ref = __esm({
|
|
|
13063
13199
|
});
|
|
13064
13200
|
|
|
13065
13201
|
// src/contracts/input-schema.ts
|
|
13066
|
-
import { z as
|
|
13202
|
+
import { z as z15 } from "zod";
|
|
13067
13203
|
function buildInputSchema(yamlInputs, required = []) {
|
|
13068
13204
|
const resolvedProperties = resolveRefs(yamlInputs);
|
|
13069
13205
|
const jsonSchema = {
|
|
@@ -13072,7 +13208,7 @@ function buildInputSchema(yamlInputs, required = []) {
|
|
|
13072
13208
|
required,
|
|
13073
13209
|
additionalProperties: false
|
|
13074
13210
|
};
|
|
13075
|
-
const zodSchema =
|
|
13211
|
+
const zodSchema = z15.fromJSONSchema(
|
|
13076
13212
|
jsonSchema
|
|
13077
13213
|
);
|
|
13078
13214
|
return { zodSchema, jsonSchema };
|
|
@@ -13158,7 +13294,7 @@ var init_audit4 = __esm({
|
|
|
13158
13294
|
});
|
|
13159
13295
|
|
|
13160
13296
|
// src/contracts/schema.ts
|
|
13161
|
-
import { z as
|
|
13297
|
+
import { z as z16 } from "zod";
|
|
13162
13298
|
var BASELINE_VERBS, MCP_VERB_RE, VerbSchema, StepSchema, HandleDeclSchema, WriteBackSchema, ContractFileSchema;
|
|
13163
13299
|
var init_schema4 = __esm({
|
|
13164
13300
|
"src/contracts/schema.ts"() {
|
|
@@ -13178,40 +13314,40 @@ var init_schema4 = __esm({
|
|
|
13178
13314
|
"read_note"
|
|
13179
13315
|
];
|
|
13180
13316
|
MCP_VERB_RE = /^mcp:\/\/[a-z][a-z0-9_-]*\/[a-z][a-z0-9_-]*$/;
|
|
13181
|
-
VerbSchema =
|
|
13182
|
-
|
|
13183
|
-
|
|
13317
|
+
VerbSchema = z16.union([
|
|
13318
|
+
z16.enum([...BASELINE_VERBS, "literal"]),
|
|
13319
|
+
z16.string().regex(MCP_VERB_RE)
|
|
13184
13320
|
]);
|
|
13185
|
-
StepSchema =
|
|
13186
|
-
as:
|
|
13321
|
+
StepSchema = z16.object({
|
|
13322
|
+
as: z16.string().min(1).regex(/^[a-z_][a-z0-9_]*$/, "alias must be snake_case").describe("D-A2c \u2014 unique snake_case alias for this step's output"),
|
|
13187
13323
|
verb: VerbSchema.describe(
|
|
13188
13324
|
"Closed enum + literal + mcp:// extension (D-A2a / C-1)"
|
|
13189
13325
|
),
|
|
13190
|
-
args:
|
|
13191
|
-
value:
|
|
13326
|
+
args: z16.record(z16.string(), z16.unknown()).optional(),
|
|
13327
|
+
value: z16.unknown().optional()
|
|
13192
13328
|
}).describe("One step in an assembly: array");
|
|
13193
|
-
HandleDeclSchema =
|
|
13194
|
-
handle:
|
|
13195
|
-
required:
|
|
13329
|
+
HandleDeclSchema = z16.object({
|
|
13330
|
+
handle: z16.string().min(1),
|
|
13331
|
+
required: z16.boolean().default(true)
|
|
13196
13332
|
}).describe("Source or sink handle declaration (D-A4a)");
|
|
13197
|
-
WriteBackSchema =
|
|
13198
|
-
sink:
|
|
13199
|
-
document_kind:
|
|
13200
|
-
properties:
|
|
13201
|
-
body_from:
|
|
13333
|
+
WriteBackSchema = z16.object({
|
|
13334
|
+
sink: z16.string().min(1).describe("Template expression OR literal sink handle"),
|
|
13335
|
+
document_kind: z16.enum(["brief", "observation", "custom"]),
|
|
13336
|
+
properties: z16.record(z16.string(), z16.unknown()).default({}),
|
|
13337
|
+
body_from: z16.string().min(1).describe("Template expression that resolves to the body string")
|
|
13202
13338
|
}).describe(
|
|
13203
13339
|
"DeliveryAdapter.write chokepoint \u2014 only ground-truth DocId source (C-3)"
|
|
13204
13340
|
);
|
|
13205
|
-
ContractFileSchema =
|
|
13206
|
-
version:
|
|
13207
|
-
name:
|
|
13208
|
-
description:
|
|
13209
|
-
inputs:
|
|
13210
|
-
required:
|
|
13211
|
-
sources:
|
|
13212
|
-
sinks:
|
|
13213
|
-
assembly:
|
|
13214
|
-
output_shape:
|
|
13341
|
+
ContractFileSchema = z16.object({
|
|
13342
|
+
version: z16.literal(1).describe("v2.0.0 supports version 1 only; v2.x may extend additively"),
|
|
13343
|
+
name: z16.string().min(1).regex(/^[a-z][a-z0-9-]*$/, "name must be kebab-case").describe("Contract name \u2014 used by instantiate_contract and slugify"),
|
|
13344
|
+
description: z16.string().default(""),
|
|
13345
|
+
inputs: z16.record(z16.string(), z16.unknown()).default({}),
|
|
13346
|
+
required: z16.array(z16.string()).default([]),
|
|
13347
|
+
sources: z16.record(z16.string(), HandleDeclSchema).default({}),
|
|
13348
|
+
sinks: z16.record(z16.string(), HandleDeclSchema).default({}),
|
|
13349
|
+
assembly: z16.array(StepSchema).min(1, "assembly must contain at least one step"),
|
|
13350
|
+
output_shape: z16.unknown().optional(),
|
|
13215
13351
|
write_back: WriteBackSchema.optional()
|
|
13216
13352
|
}).superRefine((data, ctx) => {
|
|
13217
13353
|
const aliases = /* @__PURE__ */ new Set();
|
|
@@ -13563,6 +13699,9 @@ var init_templates = __esm({
|
|
|
13563
13699
|
// src/contracts/mcp-clients.ts
|
|
13564
13700
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
13565
13701
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
13702
|
+
function nowSeconds() {
|
|
13703
|
+
return Math.floor(Date.now() / 1e3);
|
|
13704
|
+
}
|
|
13566
13705
|
function wrapAvailable(client, transport) {
|
|
13567
13706
|
return {
|
|
13568
13707
|
available: true,
|
|
@@ -13584,6 +13723,27 @@ function wrapAvailable(client, transport) {
|
|
|
13584
13723
|
}
|
|
13585
13724
|
return res;
|
|
13586
13725
|
},
|
|
13726
|
+
async listTools() {
|
|
13727
|
+
if (typeof client.listTools !== "function") return [];
|
|
13728
|
+
const res = await client.listTools();
|
|
13729
|
+
const tools = res.tools;
|
|
13730
|
+
if (!Array.isArray(tools)) return [];
|
|
13731
|
+
const out = [];
|
|
13732
|
+
for (const t of tools) {
|
|
13733
|
+
if (!t || typeof t !== "object") continue;
|
|
13734
|
+
const name = t.name;
|
|
13735
|
+
if (typeof name !== "string") continue;
|
|
13736
|
+
const tool = { name };
|
|
13737
|
+
const description = t.description;
|
|
13738
|
+
if (typeof description === "string") tool.description = description;
|
|
13739
|
+
const inputSchema = t.inputSchema;
|
|
13740
|
+
if (inputSchema && typeof inputSchema === "object") {
|
|
13741
|
+
tool.inputSchema = inputSchema;
|
|
13742
|
+
}
|
|
13743
|
+
out.push(tool);
|
|
13744
|
+
}
|
|
13745
|
+
return out;
|
|
13746
|
+
},
|
|
13587
13747
|
[Symbol.dispose]() {
|
|
13588
13748
|
transport.close();
|
|
13589
13749
|
}
|
|
@@ -13595,6 +13755,9 @@ function wrapUnavailable() {
|
|
|
13595
13755
|
async callTool() {
|
|
13596
13756
|
throw new Error("peer-MCP client unavailable");
|
|
13597
13757
|
},
|
|
13758
|
+
async listTools() {
|
|
13759
|
+
throw new Error("peer-MCP client unavailable");
|
|
13760
|
+
},
|
|
13598
13761
|
[Symbol.dispose]() {
|
|
13599
13762
|
}
|
|
13600
13763
|
};
|
|
@@ -13605,49 +13768,155 @@ var init_mcp_clients = __esm({
|
|
|
13605
13768
|
"use strict";
|
|
13606
13769
|
init_esm_shims();
|
|
13607
13770
|
PeerMcpRegistry = class {
|
|
13608
|
-
|
|
13771
|
+
entries = /* @__PURE__ */ new Map();
|
|
13609
13772
|
clientFactory;
|
|
13610
13773
|
constructor(clientFactory) {
|
|
13611
13774
|
this.clientFactory = clientFactory;
|
|
13612
13775
|
}
|
|
13613
13776
|
get size() {
|
|
13614
|
-
return this.
|
|
13777
|
+
return this.entries.size;
|
|
13615
13778
|
}
|
|
13616
13779
|
/**
|
|
13617
13780
|
* Boot every `[contracts.mcp_clients.<name>]` entry. Failures are
|
|
13618
13781
|
* non-fatal: the name is recorded as unavailable and a WARN line is
|
|
13619
13782
|
* written to stderr. Returns when all attempts have settled.
|
|
13783
|
+
*
|
|
13784
|
+
* On a successful connect we prime the tools cache via tools/list. A
|
|
13785
|
+
* tools/list failure does NOT mark the source unavailable — the
|
|
13786
|
+
* connection is live and callTool may still work — but the status
|
|
13787
|
+
* becomes "unreachable" so the UI can prompt a retry.
|
|
13620
13788
|
*/
|
|
13621
13789
|
async start(configs) {
|
|
13622
13790
|
for (const [name, cfg] of Object.entries(configs)) {
|
|
13791
|
+
await this.connectAndStore(name, cfg);
|
|
13792
|
+
}
|
|
13793
|
+
}
|
|
13794
|
+
get(name) {
|
|
13795
|
+
return this.entries.get(name)?.client;
|
|
13796
|
+
}
|
|
13797
|
+
/** All registered source names, in insertion order. */
|
|
13798
|
+
names() {
|
|
13799
|
+
return Array.from(this.entries.keys());
|
|
13800
|
+
}
|
|
13801
|
+
/** Cached metadata projection for one source; undefined if unknown. */
|
|
13802
|
+
getInfo(name) {
|
|
13803
|
+
const e = this.entries.get(name);
|
|
13804
|
+
if (e === void 0) return void 0;
|
|
13805
|
+
return {
|
|
13806
|
+
status: e.status,
|
|
13807
|
+
tools: e.tools,
|
|
13808
|
+
lastRefreshed: e.lastRefreshed,
|
|
13809
|
+
...e.error !== void 0 ? { error: e.error } : {}
|
|
13810
|
+
};
|
|
13811
|
+
}
|
|
13812
|
+
/**
|
|
13813
|
+
* Register a new source at runtime: spawn + connect, then prime the
|
|
13814
|
+
* tools cache. Replaces any existing entry of the same name (the old
|
|
13815
|
+
* client is disposed first). Returns the resulting info projection.
|
|
13816
|
+
*/
|
|
13817
|
+
async add(name, cfg) {
|
|
13818
|
+
const existing = this.entries.get(name);
|
|
13819
|
+
if (existing !== void 0) {
|
|
13623
13820
|
try {
|
|
13624
|
-
|
|
13625
|
-
|
|
13626
|
-
} catch (err) {
|
|
13627
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
13628
|
-
process.stderr.write(
|
|
13629
|
-
`[contracts] peer-MCP client '${name}' failed to start: ${msg}
|
|
13630
|
-
`
|
|
13631
|
-
);
|
|
13632
|
-
this.clients.set(name, wrapUnavailable());
|
|
13821
|
+
existing.client[Symbol.dispose]();
|
|
13822
|
+
} catch {
|
|
13633
13823
|
}
|
|
13634
13824
|
}
|
|
13825
|
+
await this.connectAndStore(name, cfg);
|
|
13826
|
+
return this.getInfo(name);
|
|
13635
13827
|
}
|
|
13636
|
-
|
|
13637
|
-
|
|
13828
|
+
/**
|
|
13829
|
+
* Dispose a source and drop it from the registry. Idempotent —
|
|
13830
|
+
* removing an unknown name is a no-op that returns false.
|
|
13831
|
+
*/
|
|
13832
|
+
remove(name) {
|
|
13833
|
+
const e = this.entries.get(name);
|
|
13834
|
+
if (e === void 0) return false;
|
|
13835
|
+
try {
|
|
13836
|
+
e.client[Symbol.dispose]();
|
|
13837
|
+
} catch (err) {
|
|
13838
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13839
|
+
process.stderr.write(`[contracts] peer-MCP dispose error: ${msg}
|
|
13840
|
+
`);
|
|
13841
|
+
}
|
|
13842
|
+
this.entries.delete(name);
|
|
13843
|
+
return true;
|
|
13844
|
+
}
|
|
13845
|
+
/**
|
|
13846
|
+
* Re-issue tools/list against the live client and refresh the cache.
|
|
13847
|
+
* Returns the updated info, or undefined if the name is unknown.
|
|
13848
|
+
*
|
|
13849
|
+
* If the client is currently unavailable this only updates the error;
|
|
13850
|
+
* re-spawning a failed source requires `add(name, cfg)` with the
|
|
13851
|
+
* config (the registry does not retain configs).
|
|
13852
|
+
*/
|
|
13853
|
+
async refresh(name) {
|
|
13854
|
+
const e = this.entries.get(name);
|
|
13855
|
+
if (e === void 0) return void 0;
|
|
13856
|
+
if (!e.client.available) {
|
|
13857
|
+
e.status = "unavailable";
|
|
13858
|
+
return this.getInfo(name);
|
|
13859
|
+
}
|
|
13860
|
+
await this.primeTools(e);
|
|
13861
|
+
return this.getInfo(name);
|
|
13638
13862
|
}
|
|
13639
13863
|
/** Dispose every client and clear the internal map. Idempotent. */
|
|
13640
13864
|
async shutdown() {
|
|
13641
|
-
for (const
|
|
13865
|
+
for (const e of this.entries.values()) {
|
|
13642
13866
|
try {
|
|
13643
|
-
|
|
13867
|
+
e.client[Symbol.dispose]();
|
|
13644
13868
|
} catch (err) {
|
|
13645
13869
|
const msg = err instanceof Error ? err.message : String(err);
|
|
13646
13870
|
process.stderr.write(`[contracts] peer-MCP dispose error: ${msg}
|
|
13647
13871
|
`);
|
|
13648
13872
|
}
|
|
13649
13873
|
}
|
|
13650
|
-
this.
|
|
13874
|
+
this.entries.clear();
|
|
13875
|
+
}
|
|
13876
|
+
// ─── internals ─────────────────────────────────────────────────────────
|
|
13877
|
+
/** Connect (factory or default), store the entry, prime tools cache. */
|
|
13878
|
+
async connectAndStore(name, cfg) {
|
|
13879
|
+
try {
|
|
13880
|
+
const { client, transport } = this.clientFactory ? await this.clientFactory(cfg) : await this.defaultConnect(cfg);
|
|
13881
|
+
const entry = {
|
|
13882
|
+
client: wrapAvailable(client, transport),
|
|
13883
|
+
status: "connected",
|
|
13884
|
+
tools: [],
|
|
13885
|
+
lastRefreshed: null
|
|
13886
|
+
};
|
|
13887
|
+
this.entries.set(name, entry);
|
|
13888
|
+
await this.primeTools(entry);
|
|
13889
|
+
} catch (err) {
|
|
13890
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13891
|
+
process.stderr.write(
|
|
13892
|
+
`[contracts] peer-MCP client '${name}' failed to start: ${msg}
|
|
13893
|
+
`
|
|
13894
|
+
);
|
|
13895
|
+
this.entries.set(name, {
|
|
13896
|
+
client: wrapUnavailable(),
|
|
13897
|
+
status: "unavailable",
|
|
13898
|
+
tools: [],
|
|
13899
|
+
lastRefreshed: null,
|
|
13900
|
+
error: msg
|
|
13901
|
+
});
|
|
13902
|
+
}
|
|
13903
|
+
}
|
|
13904
|
+
/**
|
|
13905
|
+
* Call tools/list and update the entry's cache + status. A failure
|
|
13906
|
+
* keeps the connection (status → "unreachable") rather than tearing it
|
|
13907
|
+
* down — the source is reachable for callTool even if discovery failed.
|
|
13908
|
+
*/
|
|
13909
|
+
async primeTools(entry) {
|
|
13910
|
+
try {
|
|
13911
|
+
const tools = await entry.client.listTools();
|
|
13912
|
+
entry.tools = tools;
|
|
13913
|
+
entry.lastRefreshed = nowSeconds();
|
|
13914
|
+
entry.status = "connected";
|
|
13915
|
+
delete entry.error;
|
|
13916
|
+
} catch (err) {
|
|
13917
|
+
entry.status = "unreachable";
|
|
13918
|
+
entry.error = err instanceof Error ? err.message : String(err);
|
|
13919
|
+
}
|
|
13651
13920
|
}
|
|
13652
13921
|
async defaultConnect(cfg) {
|
|
13653
13922
|
const transport = new StdioClientTransport({
|
|
@@ -13752,7 +14021,7 @@ var init_verbs = __esm({
|
|
|
13752
14021
|
});
|
|
13753
14022
|
|
|
13754
14023
|
// src/contracts/instantiate.ts
|
|
13755
|
-
import { z as
|
|
14024
|
+
import { z as z17 } from "zod";
|
|
13756
14025
|
async function instantiateContract(deps, args2) {
|
|
13757
14026
|
const parsed = deps.registry.get(args2.name);
|
|
13758
14027
|
if (!parsed) return { ok: false, reason: "unknown_contract", name: args2.name };
|
|
@@ -13900,7 +14169,7 @@ async function instantiateContract(deps, args2) {
|
|
|
13900
14169
|
};
|
|
13901
14170
|
if (parsed.output_shape) {
|
|
13902
14171
|
try {
|
|
13903
|
-
const outputSchema =
|
|
14172
|
+
const outputSchema = z17.fromJSONSchema(
|
|
13904
14173
|
parsed.output_shape
|
|
13905
14174
|
);
|
|
13906
14175
|
const check = outputSchema.safeParse(bundle);
|
|
@@ -13988,6 +14257,12 @@ var init_instantiate = __esm({
|
|
|
13988
14257
|
});
|
|
13989
14258
|
|
|
13990
14259
|
// src/contracts/describe.ts
|
|
14260
|
+
function glossFor(verb) {
|
|
14261
|
+
if (VERB_GLOSS[verb]) return VERB_GLOSS[verb];
|
|
14262
|
+
if (verb === "literal") return "Use a fixed inline value";
|
|
14263
|
+
if (verb.startsWith("mcp://")) return `Call an external tool (${verb})`;
|
|
14264
|
+
return verb;
|
|
14265
|
+
}
|
|
13991
14266
|
function describeContract(deps, args2) {
|
|
13992
14267
|
const parsed = deps.registry.get(args2.name);
|
|
13993
14268
|
if (!parsed) return { ok: false, reason: "unknown_contract", name: args2.name };
|
|
@@ -14037,7 +14312,9 @@ function renderSummary(parsed) {
|
|
|
14037
14312
|
lines.push("## Assembly");
|
|
14038
14313
|
parsed.assembly.forEach((step, i) => {
|
|
14039
14314
|
const argsRender = step.args ? `(${Object.keys(step.args).join(", ")})` : "()";
|
|
14040
|
-
lines.push(
|
|
14315
|
+
lines.push(
|
|
14316
|
+
`${i + 1}. **${step.as}** \u2014 ${glossFor(step.verb)} _(\`${step.verb}${argsRender}\`)_`
|
|
14317
|
+
);
|
|
14041
14318
|
});
|
|
14042
14319
|
lines.push("");
|
|
14043
14320
|
}
|
|
@@ -14061,10 +14338,24 @@ function renderSummary(parsed) {
|
|
|
14061
14338
|
}
|
|
14062
14339
|
return lines.join("\n").trim() + "\n";
|
|
14063
14340
|
}
|
|
14341
|
+
var VERB_GLOSS;
|
|
14064
14342
|
var init_describe = __esm({
|
|
14065
14343
|
"src/contracts/describe.ts"() {
|
|
14066
14344
|
"use strict";
|
|
14067
14345
|
init_esm_shims();
|
|
14346
|
+
VERB_GLOSS = {
|
|
14347
|
+
read_note: "Read a note's content",
|
|
14348
|
+
search_hybrid: "Search the vault (semantic + keyword)",
|
|
14349
|
+
search_sections: "Search for matching sections within notes",
|
|
14350
|
+
query_frontmatter: "Find notes by their properties (frontmatter)",
|
|
14351
|
+
expand: "Gather notes linked to the starting note (follow the graph)",
|
|
14352
|
+
cluster: "Group the gathered notes into related communities",
|
|
14353
|
+
recall: "Recall earlier agent observations from memory",
|
|
14354
|
+
compile_brief: "Compile the gathered notes into a brief",
|
|
14355
|
+
get_brief: "Fetch an already-compiled brief",
|
|
14356
|
+
list_backlinks: "List notes that link back to this one",
|
|
14357
|
+
get_outline: "Read a note's heading outline"
|
|
14358
|
+
};
|
|
14068
14359
|
}
|
|
14069
14360
|
});
|
|
14070
14361
|
|
|
@@ -14135,6 +14426,59 @@ var init_resources3 = __esm({
|
|
|
14135
14426
|
}
|
|
14136
14427
|
});
|
|
14137
14428
|
|
|
14429
|
+
// src/contracts/sources-resources.ts
|
|
14430
|
+
function readListSources(reg, configMeta) {
|
|
14431
|
+
const sources = [];
|
|
14432
|
+
for (const name of reg.names()) {
|
|
14433
|
+
const info = reg.getInfo(name);
|
|
14434
|
+
if (info === void 0) continue;
|
|
14435
|
+
const meta = configMeta[name];
|
|
14436
|
+
const entry = {
|
|
14437
|
+
name,
|
|
14438
|
+
transport: "stdio",
|
|
14439
|
+
command: meta?.command ?? "",
|
|
14440
|
+
args: meta?.args ?? [],
|
|
14441
|
+
status: info.status,
|
|
14442
|
+
tool_count: info.tools.length,
|
|
14443
|
+
last_refreshed: info.lastRefreshed
|
|
14444
|
+
};
|
|
14445
|
+
if (info.error !== void 0) entry.error = info.error;
|
|
14446
|
+
sources.push(entry);
|
|
14447
|
+
}
|
|
14448
|
+
return { sources };
|
|
14449
|
+
}
|
|
14450
|
+
function readSourceTools(reg, name) {
|
|
14451
|
+
const info = reg.getInfo(name);
|
|
14452
|
+
if (info === void 0) {
|
|
14453
|
+
return { error: `unknown source: ${name}` };
|
|
14454
|
+
}
|
|
14455
|
+
const out = {
|
|
14456
|
+
name,
|
|
14457
|
+
status: info.status,
|
|
14458
|
+
last_refreshed: info.lastRefreshed,
|
|
14459
|
+
tools: info.tools
|
|
14460
|
+
};
|
|
14461
|
+
if (info.error !== void 0) out.error = info.error;
|
|
14462
|
+
return out;
|
|
14463
|
+
}
|
|
14464
|
+
function readSourceTool(reg, name, toolName) {
|
|
14465
|
+
const info = reg.getInfo(name);
|
|
14466
|
+
if (info === void 0) {
|
|
14467
|
+
return { found: false, error: `unknown source: ${name}` };
|
|
14468
|
+
}
|
|
14469
|
+
const tool = info.tools.find((t) => t.name === toolName);
|
|
14470
|
+
if (tool === void 0) {
|
|
14471
|
+
return { found: false, error: `unknown tool: ${name}/${toolName}` };
|
|
14472
|
+
}
|
|
14473
|
+
return { found: true, name, tool };
|
|
14474
|
+
}
|
|
14475
|
+
var init_sources_resources = __esm({
|
|
14476
|
+
"src/contracts/sources-resources.ts"() {
|
|
14477
|
+
"use strict";
|
|
14478
|
+
init_esm_shims();
|
|
14479
|
+
}
|
|
14480
|
+
});
|
|
14481
|
+
|
|
14138
14482
|
// src/contracts/index.ts
|
|
14139
14483
|
var init_contracts = __esm({
|
|
14140
14484
|
"src/contracts/index.ts"() {
|
|
@@ -14157,6 +14501,7 @@ var init_contracts = __esm({
|
|
|
14157
14501
|
init_instantiate();
|
|
14158
14502
|
init_describe();
|
|
14159
14503
|
init_resources3();
|
|
14504
|
+
init_sources_resources();
|
|
14160
14505
|
}
|
|
14161
14506
|
});
|
|
14162
14507
|
|
|
@@ -14351,6 +14696,20 @@ async function serve(options = {}) {
|
|
|
14351
14696
|
process.on("SIGTERM", () => {
|
|
14352
14697
|
void shutdown().finally(() => process.exit(0));
|
|
14353
14698
|
});
|
|
14699
|
+
let stdinClosing = false;
|
|
14700
|
+
const onStdinClose = (reason) => {
|
|
14701
|
+
if (stdinClosing) return;
|
|
14702
|
+
stdinClosing = true;
|
|
14703
|
+
process.stderr.write(
|
|
14704
|
+
`[vault-memory] stdin ${reason} \u2014 parent process gone; shutting down.
|
|
14705
|
+
`
|
|
14706
|
+
);
|
|
14707
|
+
setTimeout(() => {
|
|
14708
|
+
void shutdown().finally(() => process.exit(0));
|
|
14709
|
+
}, 500);
|
|
14710
|
+
};
|
|
14711
|
+
process.stdin.on("end", () => onStdinClose("end"));
|
|
14712
|
+
process.stdin.on("close", () => onStdinClose("close"));
|
|
14354
14713
|
const server = new McpServer(
|
|
14355
14714
|
{ name: "vault-memory", version: VERSION },
|
|
14356
14715
|
// Plan 02-06 (MEM-09): advertise `resources` capability so MCP clients
|
|
@@ -15396,6 +15755,86 @@ async function serve(options = {}) {
|
|
|
15396
15755
|
};
|
|
15397
15756
|
}
|
|
15398
15757
|
);
|
|
15758
|
+
const sourceConfigMeta = () => {
|
|
15759
|
+
const out = {};
|
|
15760
|
+
for (const [name, cfg] of Object.entries(config.contracts.mcp_clients)) {
|
|
15761
|
+
out[name] = { command: cfg.command, args: cfg.args ?? [] };
|
|
15762
|
+
}
|
|
15763
|
+
return out;
|
|
15764
|
+
};
|
|
15765
|
+
server.registerResource(
|
|
15766
|
+
"sources",
|
|
15767
|
+
RESOURCE_URI_SOURCES,
|
|
15768
|
+
{
|
|
15769
|
+
title: "Peer MCP sources",
|
|
15770
|
+
description: "List peer MCP servers vault-memory connects to, with per-source status (connected/unavailable/unreachable), tool_count, and last_refreshed. vault-memory itself is not included. SOURCES-REGISTRY \xA75.1.",
|
|
15771
|
+
mimeType: "application/json"
|
|
15772
|
+
},
|
|
15773
|
+
async (uri) => ({
|
|
15774
|
+
contents: [
|
|
15775
|
+
{
|
|
15776
|
+
uri: uri.href,
|
|
15777
|
+
mimeType: "application/json",
|
|
15778
|
+
text: JSON.stringify(
|
|
15779
|
+
readListSources(peerMcpRegistry, sourceConfigMeta()),
|
|
15780
|
+
null,
|
|
15781
|
+
2
|
|
15782
|
+
)
|
|
15783
|
+
}
|
|
15784
|
+
]
|
|
15785
|
+
})
|
|
15786
|
+
);
|
|
15787
|
+
server.registerResource(
|
|
15788
|
+
"source-tools",
|
|
15789
|
+
new ResourceTemplate(`${RESOURCE_URI_SOURCES}/{name}/tools`, {
|
|
15790
|
+
list: void 0
|
|
15791
|
+
}),
|
|
15792
|
+
{
|
|
15793
|
+
title: "Peer MCP source tools",
|
|
15794
|
+
description: "List the cached tools/list for one peer MCP source. Empty when the source is not connected. SOURCES-REGISTRY \xA75.2.",
|
|
15795
|
+
mimeType: "application/json"
|
|
15796
|
+
},
|
|
15797
|
+
async (uri, variables) => {
|
|
15798
|
+
const name = String(variables.name ?? "");
|
|
15799
|
+
return {
|
|
15800
|
+
contents: [
|
|
15801
|
+
{
|
|
15802
|
+
uri: uri.href,
|
|
15803
|
+
mimeType: "application/json",
|
|
15804
|
+
text: JSON.stringify(readSourceTools(peerMcpRegistry, name), null, 2)
|
|
15805
|
+
}
|
|
15806
|
+
]
|
|
15807
|
+
};
|
|
15808
|
+
}
|
|
15809
|
+
);
|
|
15810
|
+
server.registerResource(
|
|
15811
|
+
"source-tool",
|
|
15812
|
+
new ResourceTemplate(`${RESOURCE_URI_SOURCES}/{name}/tools/{tool}`, {
|
|
15813
|
+
list: void 0
|
|
15814
|
+
}),
|
|
15815
|
+
{
|
|
15816
|
+
title: "Peer MCP source tool",
|
|
15817
|
+
description: "Read a single tool's schema from one peer MCP source, inlined from the cached tools/list (no extra peer call). SOURCES-REGISTRY \xA75.3.",
|
|
15818
|
+
mimeType: "application/json"
|
|
15819
|
+
},
|
|
15820
|
+
async (uri, variables) => {
|
|
15821
|
+
const name = String(variables.name ?? "");
|
|
15822
|
+
const tool = String(variables.tool ?? "");
|
|
15823
|
+
return {
|
|
15824
|
+
contents: [
|
|
15825
|
+
{
|
|
15826
|
+
uri: uri.href,
|
|
15827
|
+
mimeType: "application/json",
|
|
15828
|
+
text: JSON.stringify(
|
|
15829
|
+
readSourceTool(peerMcpRegistry, name, tool),
|
|
15830
|
+
null,
|
|
15831
|
+
2
|
|
15832
|
+
)
|
|
15833
|
+
}
|
|
15834
|
+
]
|
|
15835
|
+
};
|
|
15836
|
+
}
|
|
15837
|
+
);
|
|
15399
15838
|
const rel08Vaults = RESOURCES.find((r) => r.name === "vaults");
|
|
15400
15839
|
const rel08Models = RESOURCES.find((r) => r.name === "models");
|
|
15401
15840
|
const rel08Recent = RESOURCES.find((r) => r.name === "recent");
|
|
@@ -15708,6 +16147,9 @@ async function serve(options = {}) {
|
|
|
15708
16147
|
// sees, so the plugin's `suppress_contract_write` call and the
|
|
15709
16148
|
// change-feed handler observe the same entries.
|
|
15710
16149
|
suppression,
|
|
16150
|
+
// SOURCES-REGISTRY.md §6 (Stage 2) — live registry for refresh_source
|
|
16151
|
+
// + unset_mcp_client. The singleton booted above.
|
|
16152
|
+
sourceRegistry: peerMcpRegistry,
|
|
15711
16153
|
notifier: (notification) => {
|
|
15712
16154
|
server.server.notification(notification);
|
|
15713
16155
|
}
|