agenthub-multiagent-mcp 1.12.0 → 1.13.0
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/dist/brain/backend.d.ts +12 -0
- package/dist/brain/backend.d.ts.map +1 -0
- package/dist/brain/backend.js +33 -0
- package/dist/brain/backend.js.map +1 -0
- package/dist/brain/jsonlBrain.d.ts +17 -0
- package/dist/brain/jsonlBrain.d.ts.map +1 -0
- package/dist/brain/jsonlBrain.js +50 -0
- package/dist/brain/jsonlBrain.js.map +1 -0
- package/dist/brain/memvidBrain.d.ts +28 -0
- package/dist/brain/memvidBrain.d.ts.map +1 -0
- package/dist/brain/memvidBrain.js +281 -0
- package/dist/brain/memvidBrain.js.map +1 -0
- package/dist/brain/memvidBrain.test.d.ts +8 -0
- package/dist/brain/memvidBrain.test.d.ts.map +1 -0
- package/dist/brain/memvidBrain.test.js +121 -0
- package/dist/brain/memvidBrain.test.js.map +1 -0
- package/dist/brain/migrate.d.ts +36 -0
- package/dist/brain/migrate.d.ts.map +1 -0
- package/dist/brain/migrate.js +174 -0
- package/dist/brain/migrate.js.map +1 -0
- package/dist/brain/migrate.test.d.ts +5 -0
- package/dist/brain/migrate.test.d.ts.map +1 -0
- package/dist/brain/migrate.test.js +139 -0
- package/dist/brain/migrate.test.js.map +1 -0
- package/dist/brain/types.d.ts +41 -0
- package/dist/brain/types.d.ts.map +1 -0
- package/dist/brain/types.js +5 -0
- package/dist/brain/types.js.map +1 -0
- package/dist/commands/resume.d.ts +61 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +145 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/resume.test.d.ts +2 -0
- package/dist/commands/resume.test.d.ts.map +1 -0
- package/dist/commands/resume.test.js +106 -0
- package/dist/commands/resume.test.js.map +1 -0
- package/dist/commands/setup-shell.d.ts +21 -0
- package/dist/commands/setup-shell.d.ts.map +1 -0
- package/dist/commands/setup-shell.js +173 -0
- package/dist/commands/setup-shell.js.map +1 -0
- package/dist/commands/setup-shell.test.d.ts +2 -0
- package/dist/commands/setup-shell.test.d.ts.map +1 -0
- package/dist/commands/setup-shell.test.js +169 -0
- package/dist/commands/setup-shell.test.js.map +1 -0
- package/dist/hooks/brainCapture.d.ts.map +1 -1
- package/dist/hooks/brainCapture.js +12 -4
- package/dist/hooks/brainCapture.js.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/setup.js +16 -4
- package/dist/setup.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +85 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +64 -2
- package/dist/tools/tools.test.js.map +1 -1
- package/package.json +3 -2
- package/scripts/migrate-smoke.ts +134 -0
- package/src/commands/resume.test.ts +127 -0
- package/src/commands/resume.ts +205 -0
- package/src/commands/setup-shell.test.ts +185 -0
- package/src/commands/setup-shell.ts +200 -0
- package/src/setup.ts +15 -4
- package/src/tools/index.ts +76 -0
- package/src/tools/tools.test.ts +119 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brain backend selector (§1.3 finish-agenthub-v1-backlog).
|
|
3
|
+
*
|
|
4
|
+
* `AGENTHUB_BRAIN_BACKEND=memvid` (default) → MemvidBrain (.mv2 files)
|
|
5
|
+
* `AGENTHUB_BRAIN_BACKEND=jsonl` → JsonlBrain (legacy .jsonl files)
|
|
6
|
+
*
|
|
7
|
+
* The selector is lazy + cached so tests can flip the env var before first use.
|
|
8
|
+
*/
|
|
9
|
+
import type { BrainBackend } from "./types.js";
|
|
10
|
+
export declare function getBrain(): BrainBackend;
|
|
11
|
+
export declare function resetBrainBackendCache(): void;
|
|
12
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/brain/backend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO/C,wBAAgB,QAAQ,IAAI,YAAY,CAevC;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brain backend selector (§1.3 finish-agenthub-v1-backlog).
|
|
3
|
+
*
|
|
4
|
+
* `AGENTHUB_BRAIN_BACKEND=memvid` (default) → MemvidBrain (.mv2 files)
|
|
5
|
+
* `AGENTHUB_BRAIN_BACKEND=jsonl` → JsonlBrain (legacy .jsonl files)
|
|
6
|
+
*
|
|
7
|
+
* The selector is lazy + cached so tests can flip the env var before first use.
|
|
8
|
+
*/
|
|
9
|
+
import { memvidBrain } from "./memvidBrain.js";
|
|
10
|
+
import { jsonlBrain } from "./jsonlBrain.js";
|
|
11
|
+
let cached = null;
|
|
12
|
+
let cachedBackendName = null;
|
|
13
|
+
export function getBrain() {
|
|
14
|
+
const requested = (process.env.AGENTHUB_BRAIN_BACKEND ?? "memvid").toLowerCase();
|
|
15
|
+
if (cached && cachedBackendName === requested)
|
|
16
|
+
return cached;
|
|
17
|
+
switch (requested) {
|
|
18
|
+
case "jsonl":
|
|
19
|
+
cached = jsonlBrain;
|
|
20
|
+
break;
|
|
21
|
+
case "memvid":
|
|
22
|
+
default:
|
|
23
|
+
cached = memvidBrain;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
cachedBackendName = requested;
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
export function resetBrainBackendCache() {
|
|
30
|
+
cached = null;
|
|
31
|
+
cachedBackendName = null;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/brain/backend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,IAAI,MAAM,GAAwB,IAAI,CAAC;AACvC,IAAI,iBAAiB,GAAkB,IAAI,CAAC;AAE5C,MAAM,UAAU,QAAQ;IACtB,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjF,IAAI,MAAM,IAAI,iBAAiB,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAE7D,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,OAAO;YACV,MAAM,GAAG,UAAU,CAAC;YACpB,MAAM;QACR,KAAK,QAAQ,CAAC;QACd;YACE,MAAM,GAAG,WAAW,CAAC;YACrB,MAAM;IACV,CAAC;IACD,iBAAiB,GAAG,SAAS,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,GAAG,IAAI,CAAC;IACd,iBAAiB,GAAG,IAAI,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL Brain — BrainBackend adapter over the existing localBrain.ts functions.
|
|
3
|
+
*
|
|
4
|
+
* Preserved as the fallback when AGENTHUB_BRAIN_BACKEND=jsonl, and as a
|
|
5
|
+
* disaster-recovery path if memvid proves unstable on a given platform.
|
|
6
|
+
*/
|
|
7
|
+
import type { BrainBackend, SessionInput, SessionRecord, SearchHit, BrainInfo } from "./types.js";
|
|
8
|
+
export declare class JsonlBrain implements BrainBackend {
|
|
9
|
+
getBrainPath(agentId: string): string;
|
|
10
|
+
appendSession(agentId: string, session: SessionInput): Promise<number>;
|
|
11
|
+
search(agentId: string, query: string, limit?: number): Promise<SearchHit[]>;
|
|
12
|
+
recall(agentId: string, sessionNumber: number): Promise<SessionRecord | null>;
|
|
13
|
+
getSessionCount(agentId: string): Promise<number>;
|
|
14
|
+
listBrains(): Promise<BrainInfo[]>;
|
|
15
|
+
}
|
|
16
|
+
export declare const jsonlBrain: JsonlBrain;
|
|
17
|
+
//# sourceMappingURL=jsonlBrain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonlBrain.d.ts","sourceRoot":"","sources":["../../src/brain/jsonlBrain.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAElG,qBAAa,UAAW,YAAW,YAAY;IAC7C,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAa/B,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAUtE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAIvE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAc7E,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIjD,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;CAGzC;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL Brain — BrainBackend adapter over the existing localBrain.ts functions.
|
|
3
|
+
*
|
|
4
|
+
* Preserved as the fallback when AGENTHUB_BRAIN_BACKEND=jsonl, and as a
|
|
5
|
+
* disaster-recovery path if memvid proves unstable on a given platform.
|
|
6
|
+
*/
|
|
7
|
+
import * as localBrain from "./localBrain.js";
|
|
8
|
+
export class JsonlBrain {
|
|
9
|
+
getBrainPath(agentId) {
|
|
10
|
+
// localBrain has a private getBrainPath; recover it via listBrains if needed.
|
|
11
|
+
// For now, we do not publicly expose localBrain's path helper; return a best-effort path.
|
|
12
|
+
const entry = localBrain.listBrains().find((b) => b.agentId === agentId);
|
|
13
|
+
if (entry)
|
|
14
|
+
return entry.path;
|
|
15
|
+
// Fallback: mirror localBrain's sanitization for callers like the migration
|
|
16
|
+
// script that need a target path before any session exists.
|
|
17
|
+
const safe = agentId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
18
|
+
const { join } = require("path");
|
|
19
|
+
const { homedir } = require("os");
|
|
20
|
+
return join(homedir(), ".claude", "brains", `${safe}.jsonl`);
|
|
21
|
+
}
|
|
22
|
+
async appendSession(agentId, session) {
|
|
23
|
+
return localBrain.appendSession(agentId, session.summary, session.content, session.files_changed, session.outcome);
|
|
24
|
+
}
|
|
25
|
+
async search(agentId, query, limit = 5) {
|
|
26
|
+
return localBrain.search(agentId, query, limit);
|
|
27
|
+
}
|
|
28
|
+
async recall(agentId, sessionNumber) {
|
|
29
|
+
const entry = localBrain.recall(agentId, sessionNumber);
|
|
30
|
+
if (!entry)
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
session_number: entry.session_number,
|
|
34
|
+
agent_id: entry.agent_id,
|
|
35
|
+
timestamp: entry.timestamp,
|
|
36
|
+
summary: entry.summary,
|
|
37
|
+
content: entry.content,
|
|
38
|
+
files_changed: entry.files_changed,
|
|
39
|
+
outcome: entry.outcome,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async getSessionCount(agentId) {
|
|
43
|
+
return localBrain.getSessionCount(agentId);
|
|
44
|
+
}
|
|
45
|
+
async listBrains() {
|
|
46
|
+
return localBrain.listBrains();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export const jsonlBrain = new JsonlBrain();
|
|
50
|
+
//# sourceMappingURL=jsonlBrain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonlBrain.js","sourceRoot":"","sources":["../../src/brain/jsonlBrain.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAG9C,MAAM,OAAO,UAAU;IACrB,YAAY,CAAC,OAAe;QAC1B,8EAA8E;QAC9E,0FAA0F;QAC1F,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;QACzE,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QAC7B,4EAA4E;QAC5E,4DAA4D;QAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QACrD,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,QAAQ,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAe,EAAE,OAAqB;QACxD,OAAO,UAAU,CAAC,aAAa,CAC7B,OAAO,EACP,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,aAAa,EACrB,OAAO,CAAC,OAAO,CAChB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,KAAa,EAAE,KAAK,GAAG,CAAC;QACpD,OAAO,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,aAAqB;QACjD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACxD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,OAAO;YACL,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,OAAe;QACnC,OAAO,UAAU,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,UAAU;QACd,OAAO,UAAU,CAAC,UAAU,EAAE,CAAC;IACjC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memvid Brain — .mv2-backed per-agent session store (§1.2 finish-agenthub-v1-backlog).
|
|
3
|
+
*
|
|
4
|
+
* Storage: ~/.claude/brains/<agent-id>.mv2
|
|
5
|
+
*
|
|
6
|
+
* Encoding conventions (session_number must survive a round-trip through memvid's
|
|
7
|
+
* document model, which stores metadata as inline "key: value" text lines):
|
|
8
|
+
* - title: `[session <N>] <summary>` — N is extractable via regex
|
|
9
|
+
* - text: full session content — indexed by BM25
|
|
10
|
+
* - metadata: { session_number, agent_id, timestamp, files_changed (JSON), outcome, summary }
|
|
11
|
+
* Memvid serializes each metadata field as a "key: value" line appended to the
|
|
12
|
+
* document's searchable body, so they round-trip through find().text.
|
|
13
|
+
*
|
|
14
|
+
* Search is lexical (BM25) by default — works everywhere including Windows.
|
|
15
|
+
* Semantic search is opt-in via AGENTHUB_BRAIN_EMBEDDINGS=openai|openrouter and the
|
|
16
|
+
* usual OpenAI-compatible env vars. Not wired in this file yet (future phase).
|
|
17
|
+
*/
|
|
18
|
+
import type { BrainBackend, SessionInput, SessionRecord, SearchHit, BrainInfo } from "./types.js";
|
|
19
|
+
export declare class MemvidBrain implements BrainBackend {
|
|
20
|
+
getBrainPath(agentId: string): string;
|
|
21
|
+
appendSession(agentId: string, session: SessionInput): Promise<number>;
|
|
22
|
+
search(agentId: string, query: string, limit?: number): Promise<SearchHit[]>;
|
|
23
|
+
recall(agentId: string, sessionNumber: number): Promise<SessionRecord | null>;
|
|
24
|
+
getSessionCount(agentId: string): Promise<number>;
|
|
25
|
+
listBrains(): Promise<BrainInfo[]>;
|
|
26
|
+
}
|
|
27
|
+
export declare const memvidBrain: MemvidBrain;
|
|
28
|
+
//# sourceMappingURL=memvidBrain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memvidBrain.d.ts","sourceRoot":"","sources":["../../src/brain/memvidBrain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAmJlG,qBAAa,WAAY,YAAW,YAAY;IAC9C,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI/B,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BtE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IA0BvE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAmB7E,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYjD,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;CAwBzC;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memvid Brain — .mv2-backed per-agent session store (§1.2 finish-agenthub-v1-backlog).
|
|
3
|
+
*
|
|
4
|
+
* Storage: ~/.claude/brains/<agent-id>.mv2
|
|
5
|
+
*
|
|
6
|
+
* Encoding conventions (session_number must survive a round-trip through memvid's
|
|
7
|
+
* document model, which stores metadata as inline "key: value" text lines):
|
|
8
|
+
* - title: `[session <N>] <summary>` — N is extractable via regex
|
|
9
|
+
* - text: full session content — indexed by BM25
|
|
10
|
+
* - metadata: { session_number, agent_id, timestamp, files_changed (JSON), outcome, summary }
|
|
11
|
+
* Memvid serializes each metadata field as a "key: value" line appended to the
|
|
12
|
+
* document's searchable body, so they round-trip through find().text.
|
|
13
|
+
*
|
|
14
|
+
* Search is lexical (BM25) by default — works everywhere including Windows.
|
|
15
|
+
* Semantic search is opt-in via AGENTHUB_BRAIN_EMBEDDINGS=openai|openrouter and the
|
|
16
|
+
* usual OpenAI-compatible env vars. Not wired in this file yet (future phase).
|
|
17
|
+
*/
|
|
18
|
+
import { use } from "@memvid/sdk";
|
|
19
|
+
import { existsSync, mkdirSync, readdirSync } from "fs";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
import { homedir } from "os";
|
|
22
|
+
const BRAIN_DIR_NAME = "brains";
|
|
23
|
+
const SESSION_TITLE_RE = /^\[session\s+(\d+)\]\s*(.*)$/i;
|
|
24
|
+
function getBrainsDir() {
|
|
25
|
+
return join(homedir(), ".claude", BRAIN_DIR_NAME);
|
|
26
|
+
}
|
|
27
|
+
function sanitizeAgentId(agentId) {
|
|
28
|
+
return agentId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
29
|
+
}
|
|
30
|
+
function ensureBrainsDir() {
|
|
31
|
+
const dir = getBrainsDir();
|
|
32
|
+
if (!existsSync(dir))
|
|
33
|
+
mkdirSync(dir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
function getBrainPath(agentId) {
|
|
36
|
+
return join(getBrainsDir(), `${sanitizeAgentId(agentId)}.mv2`);
|
|
37
|
+
}
|
|
38
|
+
async function openForWrite(path) {
|
|
39
|
+
return use("basic", path, { mode: "auto" });
|
|
40
|
+
}
|
|
41
|
+
async function openForRead(path) {
|
|
42
|
+
if (!existsSync(path))
|
|
43
|
+
return null;
|
|
44
|
+
return use("basic", path, { mode: "open", readOnly: true });
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Memvid treats `seal()` on a readOnly handle as a mutation and throws.
|
|
48
|
+
* We still need to release the file handle, but there is no dedicated close()
|
|
49
|
+
* for readOnly handles. Relying on GC is acceptable — the N-API layer releases
|
|
50
|
+
* the Rust resources when the JS wrapper is collected.
|
|
51
|
+
*/
|
|
52
|
+
async function closeRead(_mv) {
|
|
53
|
+
// no-op; see comment above
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parse `[session N] summary` title. Returns null if the title doesn't match.
|
|
57
|
+
*/
|
|
58
|
+
function parseTitle(title) {
|
|
59
|
+
if (!title)
|
|
60
|
+
return null;
|
|
61
|
+
const m = SESSION_TITLE_RE.exec(title.trim());
|
|
62
|
+
if (!m)
|
|
63
|
+
return null;
|
|
64
|
+
return { sessionNumber: Number(m[1]), summary: m[2] ?? "" };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Extract a metadata value from memvid's serialized hit text.
|
|
68
|
+
*
|
|
69
|
+
* Memvid appends metadata as `key: <JSON-encoded-value>` tokens to the document
|
|
70
|
+
* body, whitespace-separated (no newlines). Each value is a JSON string/array/
|
|
71
|
+
* object/number/bool/null.
|
|
72
|
+
*
|
|
73
|
+
* Returns the decoded JS value (string, array, etc.) or null if not present.
|
|
74
|
+
*/
|
|
75
|
+
function extractMetaValue(text, key) {
|
|
76
|
+
if (!text)
|
|
77
|
+
return null;
|
|
78
|
+
// Match `<whitespace or start><key>:<space>` then capture a JSON value.
|
|
79
|
+
// JSON values are: "..." | [...] | {...} | number | true/false/null | unquoted word.
|
|
80
|
+
const re = new RegExp(`(?:^|\\s)${key}:\\s*("(?:[^"\\\\]|\\\\.)*"|\\[[^\\]]*\\]|\\{[^}]*\\}|-?\\d+(?:\\.\\d+)?|true|false|null|[^\\s]+)`);
|
|
81
|
+
const m = re.exec(text);
|
|
82
|
+
if (!m)
|
|
83
|
+
return null;
|
|
84
|
+
const raw = m[1];
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(raw);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return raw;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function extractMetaString(text, key) {
|
|
93
|
+
const v = extractMetaValue(text, key);
|
|
94
|
+
return typeof v === "string" ? v : v != null ? String(v) : null;
|
|
95
|
+
}
|
|
96
|
+
function extractMetaStringArray(text, key) {
|
|
97
|
+
const v = extractMetaValue(text, key);
|
|
98
|
+
return Array.isArray(v) ? v.map(String) : undefined;
|
|
99
|
+
}
|
|
100
|
+
function hitToRecord(agentId, hit) {
|
|
101
|
+
const parsed = parseTitle(hit?.title);
|
|
102
|
+
if (!parsed)
|
|
103
|
+
return null;
|
|
104
|
+
const text = hit?.text ?? "";
|
|
105
|
+
const timestamp = extractMetaString(text, "timestamp") ?? new Date().toISOString();
|
|
106
|
+
const outcomeRaw = extractMetaString(text, "outcome");
|
|
107
|
+
const outcome = outcomeRaw && outcomeRaw.length > 0 ? outcomeRaw : undefined;
|
|
108
|
+
const filesChanged = extractMetaStringArray(text, "files_changed");
|
|
109
|
+
const content = stripMetadataTokens(text);
|
|
110
|
+
return {
|
|
111
|
+
session_number: parsed.sessionNumber,
|
|
112
|
+
agent_id: agentId,
|
|
113
|
+
timestamp,
|
|
114
|
+
summary: parsed.summary,
|
|
115
|
+
content,
|
|
116
|
+
files_changed: filesChanged && filesChanged.length > 0 ? filesChanged : undefined,
|
|
117
|
+
outcome,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Strip memvid's auto-appended `<key>: <JSON>` metadata tail from a hit's text
|
|
122
|
+
* to recover the original content. Cuts at the first known metadata key we see.
|
|
123
|
+
*/
|
|
124
|
+
function stripMetadataTokens(text) {
|
|
125
|
+
if (!text)
|
|
126
|
+
return "";
|
|
127
|
+
// Find the earliest occurrence of any known trailing-metadata sentinel.
|
|
128
|
+
const sentinels = [
|
|
129
|
+
/\btitle:\s/,
|
|
130
|
+
/\btags:\s/,
|
|
131
|
+
/\blabels:\s/,
|
|
132
|
+
/\bextractous_metadata:\s/,
|
|
133
|
+
/\bsession_number:\s/,
|
|
134
|
+
/\bagent_id:\s/,
|
|
135
|
+
/\btimestamp:\s/,
|
|
136
|
+
/\bfiles_changed:\s/,
|
|
137
|
+
/\boutcome:\s/,
|
|
138
|
+
/\bsummary:\s/,
|
|
139
|
+
];
|
|
140
|
+
let cutIdx = text.length;
|
|
141
|
+
for (const re of sentinels) {
|
|
142
|
+
const m = re.exec(text);
|
|
143
|
+
if (m && m.index < cutIdx)
|
|
144
|
+
cutIdx = m.index;
|
|
145
|
+
}
|
|
146
|
+
return text.slice(0, cutIdx).trim();
|
|
147
|
+
}
|
|
148
|
+
async function nextSessionNumber(agentId) {
|
|
149
|
+
const path = getBrainPath(agentId);
|
|
150
|
+
const mv = await openForRead(path);
|
|
151
|
+
if (!mv)
|
|
152
|
+
return 1;
|
|
153
|
+
try {
|
|
154
|
+
const stats = await mv.stats();
|
|
155
|
+
return (stats?.frame_count ?? 0) + 1;
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
await closeRead(mv);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export class MemvidBrain {
|
|
162
|
+
getBrainPath(agentId) {
|
|
163
|
+
return getBrainPath(agentId);
|
|
164
|
+
}
|
|
165
|
+
async appendSession(agentId, session) {
|
|
166
|
+
ensureBrainsDir();
|
|
167
|
+
const sessionNumber = session.session_number ?? (await nextSessionNumber(agentId));
|
|
168
|
+
const timestamp = session.timestamp ?? new Date().toISOString();
|
|
169
|
+
const path = getBrainPath(agentId);
|
|
170
|
+
const mv = await openForWrite(path);
|
|
171
|
+
try {
|
|
172
|
+
await mv.put({
|
|
173
|
+
title: `[session ${sessionNumber}] ${session.summary}`,
|
|
174
|
+
label: sanitizeAgentId(agentId),
|
|
175
|
+
text: session.content,
|
|
176
|
+
metadata: {
|
|
177
|
+
session_number: sessionNumber,
|
|
178
|
+
agent_id: agentId,
|
|
179
|
+
timestamp,
|
|
180
|
+
summary: session.summary,
|
|
181
|
+
files_changed: session.files_changed ?? [],
|
|
182
|
+
outcome: session.outcome ?? "",
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
await mv.seal();
|
|
188
|
+
}
|
|
189
|
+
return sessionNumber;
|
|
190
|
+
}
|
|
191
|
+
async search(agentId, query, limit = 5) {
|
|
192
|
+
const path = getBrainPath(agentId);
|
|
193
|
+
const mv = await openForRead(path);
|
|
194
|
+
if (!mv)
|
|
195
|
+
return [];
|
|
196
|
+
try {
|
|
197
|
+
const res = await mv.find(query, { k: limit, mode: "lex" });
|
|
198
|
+
const hits = [];
|
|
199
|
+
for (const rawHit of res.hits ?? []) {
|
|
200
|
+
const hit = rawHit;
|
|
201
|
+
const parsed = parseTitle(hit?.title);
|
|
202
|
+
if (!parsed)
|
|
203
|
+
continue;
|
|
204
|
+
const timestamp = extractMetaString(hit?.text, "timestamp") ?? "";
|
|
205
|
+
hits.push({
|
|
206
|
+
session_number: parsed.sessionNumber,
|
|
207
|
+
timestamp,
|
|
208
|
+
summary: parsed.summary,
|
|
209
|
+
snippet: hit?.snippet ?? "",
|
|
210
|
+
score: typeof hit?.score === "number" ? hit.score : 0,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return hits;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
await closeRead(mv);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async recall(agentId, sessionNumber) {
|
|
220
|
+
const path = getBrainPath(agentId);
|
|
221
|
+
const mv = await openForRead(path);
|
|
222
|
+
if (!mv)
|
|
223
|
+
return null;
|
|
224
|
+
try {
|
|
225
|
+
// Match the title prefix. `[session N]` is distinctive enough.
|
|
226
|
+
const res = await mv.find(`[session ${sessionNumber}]`, { k: 5, mode: "lex" });
|
|
227
|
+
for (const hit of res.hits ?? []) {
|
|
228
|
+
const parsed = parseTitle(hit?.title);
|
|
229
|
+
if (parsed?.sessionNumber === sessionNumber) {
|
|
230
|
+
return hitToRecord(agentId, hit);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
await closeRead(mv);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async getSessionCount(agentId) {
|
|
240
|
+
const path = getBrainPath(agentId);
|
|
241
|
+
const mv = await openForRead(path);
|
|
242
|
+
if (!mv)
|
|
243
|
+
return 0;
|
|
244
|
+
try {
|
|
245
|
+
const stats = await mv.stats();
|
|
246
|
+
return stats?.frame_count ?? 0;
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
await closeRead(mv);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async listBrains() {
|
|
253
|
+
const dir = getBrainsDir();
|
|
254
|
+
if (!existsSync(dir))
|
|
255
|
+
return [];
|
|
256
|
+
const result = [];
|
|
257
|
+
for (const file of readdirSync(dir)) {
|
|
258
|
+
if (!file.endsWith(".mv2"))
|
|
259
|
+
continue;
|
|
260
|
+
const agentId = file.slice(0, -".mv2".length);
|
|
261
|
+
const fullPath = join(dir, file);
|
|
262
|
+
let sessions = 0;
|
|
263
|
+
try {
|
|
264
|
+
const mv = await openForRead(fullPath);
|
|
265
|
+
if (mv) {
|
|
266
|
+
const stats = await mv.stats();
|
|
267
|
+
sessions = stats?.frame_count ?? 0;
|
|
268
|
+
await closeRead(mv);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// If a .mv2 is locked or corrupted, still list it with 0 sessions.
|
|
273
|
+
sessions = 0;
|
|
274
|
+
}
|
|
275
|
+
result.push({ agentId, sessions, path: fullPath });
|
|
276
|
+
}
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
export const memvidBrain = new MemvidBrain();
|
|
281
|
+
//# sourceMappingURL=memvidBrain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memvidBrain.js","sourceRoot":"","sources":["../../src/brain/memvidBrain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for MemvidBrain (§1.5 finish-agenthub-v1-backlog).
|
|
3
|
+
*
|
|
4
|
+
* Uses a per-test temp dir as the fake HOME, so `~/.claude/brains/` maps under
|
|
5
|
+
* that temp dir and real user data is never touched.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=memvidBrain.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memvidBrain.test.d.ts","sourceRoot":"","sources":["../../src/brain/memvidBrain.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for MemvidBrain (§1.5 finish-agenthub-v1-backlog).
|
|
3
|
+
*
|
|
4
|
+
* Uses a per-test temp dir as the fake HOME, so `~/.claude/brains/` maps under
|
|
5
|
+
* that temp dir and real user data is never touched.
|
|
6
|
+
*/
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
|
+
import { mkdtempSync, rmSync } from "fs";
|
|
9
|
+
import { tmpdir } from "os";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
let tempHome;
|
|
12
|
+
function setFakeHome(dir) {
|
|
13
|
+
// memvidBrain reads `os.homedir()` lazily — patch it via env + module-level mock.
|
|
14
|
+
if (process.platform === "win32") {
|
|
15
|
+
process.env.USERPROFILE = dir;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
process.env.HOME = dir;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Mock os.homedir before the modules under test are imported.
|
|
22
|
+
vi.mock("os", async () => {
|
|
23
|
+
const actual = await vi.importActual("os");
|
|
24
|
+
return {
|
|
25
|
+
...actual,
|
|
26
|
+
homedir: () => process.env.__TEST_HOME__ ?? actual.homedir(),
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
async function importMemvidBrain() {
|
|
30
|
+
// Import lazily so the os mock is applied before the module reads homedir.
|
|
31
|
+
const mod = await import("./memvidBrain.js");
|
|
32
|
+
return mod;
|
|
33
|
+
}
|
|
34
|
+
describe("MemvidBrain", () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
tempHome = mkdtempSync(join(tmpdir(), "memvid-brain-test-"));
|
|
37
|
+
process.env.__TEST_HOME__ = tempHome;
|
|
38
|
+
setFakeHome(tempHome);
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
try {
|
|
42
|
+
rmSync(tempHome, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// best effort
|
|
46
|
+
}
|
|
47
|
+
delete process.env.__TEST_HOME__;
|
|
48
|
+
});
|
|
49
|
+
it("round-trip: append, search, recall", async () => {
|
|
50
|
+
const { MemvidBrain } = await importMemvidBrain();
|
|
51
|
+
const brain = new MemvidBrain();
|
|
52
|
+
const agent = "test-agent-1";
|
|
53
|
+
const sessions = [
|
|
54
|
+
{ summary: "intel deploy", content: "Deployed intel-layer runs." },
|
|
55
|
+
{ summary: "org brain staging", content: "Shipped org brain models and store." },
|
|
56
|
+
{ summary: "FTS5 sanitizer", content: "Hardened FTS5 query sanitizer against punctuation." },
|
|
57
|
+
{ summary: "WS header auth", content: "Migrated WebSocket API key to X-API-Key header." },
|
|
58
|
+
{ summary: "openspec cleanup", content: "Archived 10 changes, reconciled tasks." },
|
|
59
|
+
];
|
|
60
|
+
const appended = [];
|
|
61
|
+
for (const s of sessions) {
|
|
62
|
+
const n = await brain.appendSession(agent, s);
|
|
63
|
+
appended.push(n);
|
|
64
|
+
}
|
|
65
|
+
expect(appended).toEqual([1, 2, 3, 4, 5]);
|
|
66
|
+
expect(await brain.getSessionCount(agent)).toBe(5);
|
|
67
|
+
});
|
|
68
|
+
it("search returns the expected session for a lexical query", async () => {
|
|
69
|
+
const { MemvidBrain } = await importMemvidBrain();
|
|
70
|
+
const brain = new MemvidBrain();
|
|
71
|
+
const agent = "test-agent-2";
|
|
72
|
+
await brain.appendSession(agent, { summary: "intel deploy", content: "Deployed intel-layer runs." });
|
|
73
|
+
await brain.appendSession(agent, {
|
|
74
|
+
summary: "FTS5 sanitizer",
|
|
75
|
+
content: "Hardened FTS5 query sanitizer against reserved tokens.",
|
|
76
|
+
});
|
|
77
|
+
await brain.appendSession(agent, { summary: "openspec", content: "Archived changes." });
|
|
78
|
+
const hits = await brain.search(agent, "FTS5 sanitizer", 5);
|
|
79
|
+
expect(hits.length).toBeGreaterThan(0);
|
|
80
|
+
expect(hits[0].summary).toBe("FTS5 sanitizer");
|
|
81
|
+
expect(hits[0].session_number).toBe(2);
|
|
82
|
+
});
|
|
83
|
+
it("recall returns full record for a known session number", async () => {
|
|
84
|
+
const { MemvidBrain } = await importMemvidBrain();
|
|
85
|
+
const brain = new MemvidBrain();
|
|
86
|
+
const agent = "test-agent-3";
|
|
87
|
+
await brain.appendSession(agent, { summary: "s1", content: "c1" });
|
|
88
|
+
const n = await brain.appendSession(agent, {
|
|
89
|
+
summary: "target",
|
|
90
|
+
content: "the content we want to recall",
|
|
91
|
+
files_changed: ["a.go", "b.ts"],
|
|
92
|
+
outcome: "success",
|
|
93
|
+
});
|
|
94
|
+
await brain.appendSession(agent, { summary: "s3", content: "c3" });
|
|
95
|
+
const recalled = await brain.recall(agent, n);
|
|
96
|
+
expect(recalled).not.toBeNull();
|
|
97
|
+
expect(recalled?.summary).toBe("target");
|
|
98
|
+
expect(recalled?.content).toBe("the content we want to recall");
|
|
99
|
+
expect(recalled?.outcome).toBe("success");
|
|
100
|
+
expect(recalled?.files_changed).toEqual(["a.go", "b.ts"]);
|
|
101
|
+
expect(recalled?.session_number).toBe(n);
|
|
102
|
+
});
|
|
103
|
+
it("empty brain: search returns [] without error", async () => {
|
|
104
|
+
const { MemvidBrain } = await importMemvidBrain();
|
|
105
|
+
const brain = new MemvidBrain();
|
|
106
|
+
const hits = await brain.search("never-registered-agent", "anything");
|
|
107
|
+
expect(hits).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
it("empty brain: recall returns null without error", async () => {
|
|
110
|
+
const { MemvidBrain } = await importMemvidBrain();
|
|
111
|
+
const brain = new MemvidBrain();
|
|
112
|
+
const record = await brain.recall("never-registered-agent", 1);
|
|
113
|
+
expect(record).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
it("empty brain: getSessionCount returns 0", async () => {
|
|
116
|
+
const { MemvidBrain } = await importMemvidBrain();
|
|
117
|
+
const brain = new MemvidBrain();
|
|
118
|
+
expect(await brain.getSessionCount("never-registered-agent")).toBe(0);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=memvidBrain.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memvidBrain.test.js","sourceRoot":"","sources":["../../src/brain/memvidBrain.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL → memvid migration (§1.4 finish-agenthub-v1-backlog).
|
|
3
|
+
*
|
|
4
|
+
* For each `~/.claude/brains/<agent>.jsonl`:
|
|
5
|
+
* 1. Skip if `<agent>.mv2` exists and is newer than the JSONL.
|
|
6
|
+
* 2. Read JSONL line-by-line; skip malformed lines (log + continue).
|
|
7
|
+
* 3. Write new entries to `<agent>.mv2.new` via MemvidBrain.appendSession.
|
|
8
|
+
* 4. Verify written session count matches parsed-JSONL count (hash-verify).
|
|
9
|
+
* 5. Atomic rename `.mv2.new` → `.mv2`.
|
|
10
|
+
* 6. Atomic rename `<agent>.jsonl` → `<agent>.jsonl.frozen.<ISO>`.
|
|
11
|
+
*
|
|
12
|
+
* If any step fails, the `.mv2.new` tempfile is left for inspection and the
|
|
13
|
+
* JSONL source is untouched — migration is re-runnable.
|
|
14
|
+
*/
|
|
15
|
+
export interface MigrationResult {
|
|
16
|
+
agentId: string;
|
|
17
|
+
skipped: boolean;
|
|
18
|
+
skipReason?: string;
|
|
19
|
+
sessionsRead: number;
|
|
20
|
+
sessionsWritten: number;
|
|
21
|
+
malformedLines: number;
|
|
22
|
+
mv2Path: string;
|
|
23
|
+
frozenPath?: string;
|
|
24
|
+
elapsedMs: number;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Migrate a single agent's JSONL brain to memvid. Idempotent.
|
|
29
|
+
*/
|
|
30
|
+
export declare function migrateAgent(agentId: string): Promise<MigrationResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Walk the brains dir, migrate every `<agent>.jsonl` that has no fresh `.mv2`.
|
|
33
|
+
* Called at MCP boot when backend=memvid.
|
|
34
|
+
*/
|
|
35
|
+
export declare function migrateAllPending(): Promise<MigrationResult[]>;
|
|
36
|
+
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/brain/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiBH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoCD;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAmH5E;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAWpE"}
|