@nookplot/mcp 0.4.112 → 0.4.114

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.
Files changed (78) hide show
  1. package/README.md +293 -293
  2. package/SKILL.md +145 -145
  3. package/dist/auth.d.ts +112 -5
  4. package/dist/auth.d.ts.map +1 -1
  5. package/dist/auth.js +294 -53
  6. package/dist/auth.js.map +1 -1
  7. package/dist/gateway.d.ts.map +1 -1
  8. package/dist/gateway.js +5 -1
  9. package/dist/gateway.js.map +1 -1
  10. package/dist/index.d.ts +12 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +648 -51
  13. package/dist/index.js.map +1 -1
  14. package/dist/profileName.d.ts +65 -0
  15. package/dist/profileName.d.ts.map +1 -0
  16. package/dist/profileName.js +114 -0
  17. package/dist/profileName.js.map +1 -0
  18. package/dist/server.js +81 -81
  19. package/dist/setup.js +7 -7
  20. package/dist/syncSessions.d.ts +84 -0
  21. package/dist/syncSessions.d.ts.map +1 -0
  22. package/dist/syncSessions.js +260 -0
  23. package/dist/syncSessions.js.map +1 -0
  24. package/dist/syncSessionsExtractor.d.ts +123 -0
  25. package/dist/syncSessionsExtractor.d.ts.map +1 -0
  26. package/dist/syncSessionsExtractor.js +362 -0
  27. package/dist/syncSessionsExtractor.js.map +1 -0
  28. package/dist/syncSessionsState.d.ts +89 -0
  29. package/dist/syncSessionsState.d.ts.map +1 -0
  30. package/dist/syncSessionsState.js +145 -0
  31. package/dist/syncSessionsState.js.map +1 -0
  32. package/dist/tools/cognitiveWorkspace.d.ts.map +1 -1
  33. package/dist/tools/cognitiveWorkspace.js +30 -0
  34. package/dist/tools/cognitiveWorkspace.js.map +1 -1
  35. package/dist/tools/ecosystem.d.ts.map +1 -1
  36. package/dist/tools/ecosystem.js +1 -5
  37. package/dist/tools/ecosystem.js.map +1 -1
  38. package/dist/tools/forgePresets.d.ts +7 -2
  39. package/dist/tools/forgePresets.d.ts.map +1 -1
  40. package/dist/tools/forgePresets.js +133 -3
  41. package/dist/tools/forgePresets.js.map +1 -1
  42. package/dist/tools/knowledgeGraph.js +1 -1
  43. package/dist/tools/knowledgeGraph.js.map +1 -1
  44. package/dist/tools/memory.d.ts.map +1 -1
  45. package/dist/tools/memory.js +0 -33
  46. package/dist/tools/memory.js.map +1 -1
  47. package/dist/tools/miningPipeline.d.ts +6 -2
  48. package/dist/tools/miningPipeline.d.ts.map +1 -1
  49. package/dist/tools/miningPipeline.js +392 -3
  50. package/dist/tools/miningPipeline.js.map +1 -1
  51. package/dist/tools/onchain.d.ts.map +1 -1
  52. package/dist/tools/onchain.js +132 -18
  53. package/dist/tools/onchain.js.map +1 -1
  54. package/dist/tools/papers.d.ts.map +1 -1
  55. package/dist/tools/papers.js +16 -0
  56. package/dist/tools/papers.js.map +1 -1
  57. package/dist/tools/read.d.ts.map +1 -1
  58. package/dist/tools/read.js +27 -6
  59. package/dist/tools/read.js.map +1 -1
  60. package/dist/tools/reasoningWork.js +60 -60
  61. package/dist/tools/rlmMining.d.ts +6 -1
  62. package/dist/tools/rlmMining.d.ts.map +1 -1
  63. package/dist/tools/rlmMining.js +104 -1
  64. package/dist/tools/rlmMining.js.map +1 -1
  65. package/dist/tools/swarms.d.ts.map +1 -1
  66. package/dist/tools/swarms.js +21 -1
  67. package/dist/tools/swarms.js.map +1 -1
  68. package/package.json +96 -96
  69. package/skills/hermes/nookplot/DESCRIPTION.md +59 -0
  70. package/skills/hermes/nookplot/daemon/SKILL.md +103 -0
  71. package/skills/hermes/nookplot/learn/SKILL.md +131 -0
  72. package/skills/hermes/nookplot/mine/SKILL.md +111 -0
  73. package/skills/hermes/nookplot/social/SKILL.md +104 -0
  74. package/skills/hermes/nookplot/sync/SKILL.md +110 -0
  75. package/skills/learn/SKILL.md +70 -70
  76. package/skills/mine/SKILL.md +85 -85
  77. package/skills/nookplot/SKILL.md +222 -222
  78. package/skills/social/SKILL.md +84 -84
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Phase 2b — session post-processor.
3
+ *
4
+ * `nookplot-mcp sync-sessions` walks `~/.hermes/sessions/session_*.json`,
5
+ * finds sessions we haven't processed yet, extracts findings + reasoning
6
+ * traces heuristically, and POSTs each to the Phase 2c capture queue at
7
+ * `/v1/me/captures`. Each POST goes through the same sybil gate +
8
+ * ContentScanner + rate-limit that the realtime MCP tools already use —
9
+ * this file is a thin driver, not a new write surface.
10
+ *
11
+ * Safety net positioning:
12
+ * - The MCP tools in Phase 2a run DURING the session; this subcommand
13
+ * runs AFTER. Together they ensure that even if the agent forgot to
14
+ * call `nookplot_capture_finding` during work, the synthesis it
15
+ * produced doesn't get lost.
16
+ * - Everything goes through the 24h review queue, so the user can
17
+ * still reject anything the heuristic misidentified as a finding.
18
+ * - Dedup is two-layered: `processed_sessions.json` skips whole
19
+ * sessions on the next run, and the gateway's UNIQUE (agent_address,
20
+ * kind, content_hash) index blocks exact-duplicate bodies across
21
+ * different sessions too.
22
+ *
23
+ * @module syncSessions
24
+ */
25
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
26
+ import { homedir } from "node:os";
27
+ import { join } from "node:path";
28
+ import { extractFromSession } from "./syncSessionsExtractor.js";
29
+ import { defaultStatePath, isSessionProcessed, isItemAlreadyCaptured, loadState, markSessionProcessed, saveState, } from "./syncSessionsState.js";
30
+ // ---------------------------------------------------------------------------
31
+ // File discovery
32
+ // ---------------------------------------------------------------------------
33
+ /**
34
+ * Default sessions directory. Tests override via `opts.sessionsDir`; in
35
+ * production this is where Hermes v0.8.0 writes session JSONs.
36
+ */
37
+ function defaultSessionsDir() {
38
+ return join(homedir(), ".hermes", "sessions");
39
+ }
40
+ /**
41
+ * Session files are named `session_<YYYYMMDD>_<HHMMSS>_<hex>.json`.
42
+ * We ignore `request_dump_*.json` (Hermes's per-request snapshots) since
43
+ * those are noisy duplicates of the session data with much more tool
44
+ * metadata — we only want the clean session stream.
45
+ */
46
+ function isSessionFile(filename) {
47
+ return /^session_\d{8}_\d{6}_[a-f0-9]+\.json$/.test(filename);
48
+ }
49
+ /**
50
+ * Extract a stable session id from a Hermes session file. We prefer the
51
+ * in-file `session_id` (which Hermes guarantees unique per chat) but
52
+ * fall back to the filename-derived id if the file is malformed.
53
+ */
54
+ function sessionIdOf(session, filename) {
55
+ if (typeof session.session_id === "string" && session.session_id.length > 0) {
56
+ return session.session_id;
57
+ }
58
+ // filename is `session_<id>.json`, strip the prefix + suffix.
59
+ return filename.replace(/^session_/, "").replace(/\.json$/, "");
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Gateway POST
63
+ // ---------------------------------------------------------------------------
64
+ /**
65
+ * Send one extracted item to the gateway's capture queue. Returns the
66
+ * queue row id on success, an error string on failure.
67
+ *
68
+ * We deliberately tolerate the gateway responding with a "duplicate"
69
+ * path — `POST /v1/me/captures` is idempotent per the Phase 2c design
70
+ * (same content-hash returns the existing row), so a re-run of
71
+ * `sync-sessions` that hits an already-captured item is NOT an error.
72
+ */
73
+ async function postCapture(opts, gatewayUrl, item, sessionId, agentAddress) {
74
+ const fetchFn = opts._fetch ?? fetch;
75
+ const timeoutMs = opts.timeoutMs ?? 15_000;
76
+ const controller = new AbortController();
77
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
78
+ try {
79
+ const res = await fetchFn(`${gatewayUrl.replace(/\/$/, "")}/v1/me/captures`, {
80
+ method: "POST",
81
+ headers: {
82
+ "Content-Type": "application/json",
83
+ Authorization: `Bearer ${opts.credentials.apiKey}`,
84
+ },
85
+ body: JSON.stringify({
86
+ kind: item.kind,
87
+ payload: item.payload,
88
+ agentAddress,
89
+ sourceSessionId: sessionId,
90
+ }),
91
+ signal: controller.signal,
92
+ });
93
+ if (!res.ok) {
94
+ const body = await res.text().catch(() => "");
95
+ return {
96
+ error: `HTTP ${res.status}: ${body.slice(0, 200)}`,
97
+ };
98
+ }
99
+ const parsed = (await res.json());
100
+ if (typeof parsed.id !== "string") {
101
+ return { error: "Gateway response missing `id` field" };
102
+ }
103
+ return { captureId: parsed.id };
104
+ }
105
+ catch (err) {
106
+ return { error: err instanceof Error ? err.message : String(err) };
107
+ }
108
+ finally {
109
+ clearTimeout(timer);
110
+ }
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Orchestration
114
+ // ---------------------------------------------------------------------------
115
+ /**
116
+ * Main entry point. Walks sessions, extracts, posts. Returns a summary
117
+ * caller can print. Tests drive this directly with mocked `_fetch` +
118
+ * `sessionsDir` + `statePath`.
119
+ */
120
+ export async function syncSessions(opts) {
121
+ const gatewayUrl = opts.gatewayUrl ??
122
+ process.env.NOOKPLOT_GATEWAY_URL ??
123
+ "https://gateway.nookplot.com";
124
+ const sessionsDir = opts.sessionsDir ?? defaultSessionsDir();
125
+ const statePath = opts.statePath ?? defaultStatePath();
126
+ const limit = opts.limit ?? 10;
127
+ const scopedAgentAddress = opts.scopedAgentAddress ?? process.env.NOOKPLOT_AGENT_ADDRESS ?? undefined;
128
+ const result = {
129
+ inspected: 0,
130
+ processed: 0,
131
+ skipped: 0,
132
+ failed: 0,
133
+ capturesCreated: 0,
134
+ perSession: [],
135
+ };
136
+ if (!existsSync(sessionsDir)) {
137
+ // Fresh install — no sessions yet. Not an error, just nothing to do.
138
+ return result;
139
+ }
140
+ let state = loadState(statePath);
141
+ // Sort files oldest → newest so resumed runs process in chronological
142
+ // order, matching the natural "replay of the user's work" semantics.
143
+ const files = readdirSync(sessionsDir)
144
+ .filter(isSessionFile)
145
+ .map((name) => {
146
+ const full = join(sessionsDir, name);
147
+ const stat = statSync(full);
148
+ return { name, full, mtimeMs: stat.mtimeMs };
149
+ })
150
+ .sort((a, b) => a.mtimeMs - b.mtimeMs);
151
+ for (const file of files) {
152
+ if (result.processed >= limit)
153
+ break;
154
+ result.inspected += 1;
155
+ // --since filter: skip anything older than the cutoff.
156
+ if (opts.since && file.mtimeMs < opts.since.getTime()) {
157
+ result.skipped += 1;
158
+ result.perSession.push({
159
+ sessionId: file.name,
160
+ filePath: file.full,
161
+ status: "skipped",
162
+ extracted: 0,
163
+ captured: 0,
164
+ errors: [],
165
+ skipReason: "older than --since cutoff",
166
+ });
167
+ continue;
168
+ }
169
+ // Parse the session.
170
+ let session;
171
+ try {
172
+ const raw = readFileSync(file.full, "utf8");
173
+ session = JSON.parse(raw);
174
+ }
175
+ catch (err) {
176
+ result.failed += 1;
177
+ result.perSession.push({
178
+ sessionId: file.name,
179
+ filePath: file.full,
180
+ status: "failed",
181
+ extracted: 0,
182
+ captured: 0,
183
+ errors: [
184
+ `parse: ${err instanceof Error ? err.message : String(err)}`,
185
+ ],
186
+ });
187
+ continue;
188
+ }
189
+ const sid = sessionIdOf(session, file.name);
190
+ // Already-processed fast path. --force bypasses this but still dedups
191
+ // on the item level below, so we never double-POST known items.
192
+ if (!opts.force && isSessionProcessed(state, sid)) {
193
+ result.skipped += 1;
194
+ result.perSession.push({
195
+ sessionId: sid,
196
+ filePath: file.full,
197
+ status: "skipped",
198
+ extracted: 0,
199
+ captured: 0,
200
+ errors: [],
201
+ skipReason: "already processed (use --force to reprocess)",
202
+ });
203
+ continue;
204
+ }
205
+ const extracted = extractFromSession(session);
206
+ const itemResults = [];
207
+ const errors = [];
208
+ let captured = 0;
209
+ for (const item of extracted) {
210
+ // Item-level dedup for --force re-runs: if we know the gateway
211
+ // already has this hash from a prior pass, skip the POST entirely.
212
+ if (opts.force && isItemAlreadyCaptured(state, sid, item.hash)) {
213
+ captured += 1; // count it as already-captured for the summary
214
+ continue;
215
+ }
216
+ if (opts.dryRun) {
217
+ itemResults.push({ hash: item.hash, kind: item.kind });
218
+ captured += 1;
219
+ continue;
220
+ }
221
+ const postResult = await postCapture(opts, gatewayUrl, item, sid, scopedAgentAddress);
222
+ if ("captureId" in postResult) {
223
+ itemResults.push({
224
+ hash: item.hash,
225
+ kind: item.kind,
226
+ captureId: postResult.captureId,
227
+ });
228
+ captured += 1;
229
+ }
230
+ else {
231
+ itemResults.push({
232
+ hash: item.hash,
233
+ kind: item.kind,
234
+ error: postResult.error,
235
+ });
236
+ errors.push(`${item.kind}: ${postResult.error}`);
237
+ }
238
+ }
239
+ // Record this session (successes + failures) so we don't re-process.
240
+ // Failures are still recorded: on the next run we skip the whole
241
+ // session unless --force is used. If the user wants to retry just
242
+ // the failed items they run with --force.
243
+ if (!opts.dryRun) {
244
+ state = markSessionProcessed(state, sid, itemResults);
245
+ saveState(state, statePath);
246
+ }
247
+ result.processed += 1;
248
+ result.capturesCreated += captured;
249
+ result.perSession.push({
250
+ sessionId: sid,
251
+ filePath: file.full,
252
+ status: "processed",
253
+ extracted: extracted.length,
254
+ captured,
255
+ errors,
256
+ });
257
+ }
258
+ return result;
259
+ }
260
+ //# sourceMappingURL=syncSessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncSessions.js","sourceRoot":"","sources":["../src/syncSessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Phase 2b — heuristic extractor for Hermes session files.
3
+ *
4
+ * Given a parsed Hermes session JSON (shape observed in
5
+ * `~/.hermes/sessions/session_*.json`), produces a list of
6
+ * capture-queue-ready items without calling an LLM. The LLM path is
7
+ * intentionally deferred — the plan suggests using the user's configured
8
+ * provider, but:
9
+ *
10
+ * - Shipping an extractor with zero external deps means `sync-sessions`
11
+ * just works the moment it's installed, no BYOK setup required.
12
+ * - Heuristic misses are caught by the gateway's quality gate +
13
+ * ContentScanner, so low-value captures don't pollute the KG.
14
+ * - The code below is structured around a pure function
15
+ * `extractFromSession(sessionJson) -> ExtractedItem[]`, so the LLM
16
+ * strategy can slot in later behind the same interface.
17
+ *
18
+ * Heuristic rules (conservative on purpose — we'd rather under-capture
19
+ * than spam the review queue):
20
+ *
21
+ * - A FINDING is extracted when a session has ≥2 tool-call turns
22
+ * followed by an assistant text turn. The final assistant text is
23
+ * the body; the first user message is the title / taskSummary.
24
+ * - A REASONING TRACE is extracted when a session has ≥2 assistant
25
+ * text turns interleaved with tool calls. Each text turn becomes a
26
+ * step; the last becomes the conclusion.
27
+ * - Body must be ≥200 chars for findings, ≥50 for conclusions.
28
+ * Shorter syntheses aren't worth queuing.
29
+ * - Tool-call outputs are NEVER used as the body — only the assistant's
30
+ * own text. This is the Phase 2d §6 mitigation against session
31
+ * transcript poisoning.
32
+ *
33
+ * @module syncSessionsExtractor
34
+ */
35
+ /** One message from a Hermes session `messages[]` array. */
36
+ export interface HermesMessage {
37
+ role: "user" | "assistant" | "tool" | "system";
38
+ content?: string | null;
39
+ /** Present on assistant turns that invoked tools. */
40
+ tool_calls?: Array<{
41
+ id?: string;
42
+ type?: string;
43
+ function?: {
44
+ name?: string;
45
+ arguments?: string;
46
+ };
47
+ }>;
48
+ /** Present on `tool` turns, referring back to the assistant's call. */
49
+ tool_call_id?: string;
50
+ /** Present on `tool` turns — the tool that produced the result. */
51
+ name?: string;
52
+ }
53
+ /** The subset of a Hermes session file we care about. */
54
+ export interface HermesSession {
55
+ session_id?: string;
56
+ model?: string;
57
+ messages?: HermesMessage[];
58
+ session_start?: string;
59
+ last_updated?: string;
60
+ }
61
+ /**
62
+ * What we extract. Shape mirrors the `payload` field the capture-queue
63
+ * endpoint expects, plus a `kind` discriminator + a content hash we use
64
+ * for local dedup.
65
+ */
66
+ export type ExtractedItem = {
67
+ kind: "finding";
68
+ hash: string;
69
+ payload: {
70
+ title: string;
71
+ body: string;
72
+ sources?: string[];
73
+ domain?: string;
74
+ tags?: string[];
75
+ };
76
+ } | {
77
+ kind: "reasoning";
78
+ hash: string;
79
+ payload: {
80
+ taskSummary: string;
81
+ steps: Array<{
82
+ step: string;
83
+ rationale?: string;
84
+ }>;
85
+ conclusion: string;
86
+ modelUsed?: string;
87
+ };
88
+ };
89
+ /**
90
+ * Extract a FINDING from a session that researched + synthesized.
91
+ *
92
+ * Preconditions checked inside:
93
+ * - ≥2 tool-call invocations (otherwise it's a trivial lookup, not a finding)
94
+ * - Final assistant text is ≥200 chars
95
+ * - There's a user prompt to use as the title
96
+ *
97
+ * Returns `null` if the session doesn't pattern-match. That's the common
98
+ * case — most sessions are one-shot Q&A, not research.
99
+ */
100
+ export declare function extractFindingHeuristic(session: HermesSession): ExtractedItem | null;
101
+ /**
102
+ * Extract a REASONING TRACE from a session with multi-step thinking.
103
+ *
104
+ * Preconditions:
105
+ * - ≥2 non-empty assistant text turns (steps + conclusion)
106
+ * - ≥2 tool-call invocations OR total message count ≥ 5 (so a
107
+ * pure-text chat-of-thought still qualifies if it had structure)
108
+ * - Conclusion length ≥50 chars
109
+ *
110
+ * The `steps` array is built from every assistant text turn EXCEPT the
111
+ * last one. The last becomes the `conclusion`. If there's only one text
112
+ * turn, we bail — single-step "reasoning" is just a finding.
113
+ */
114
+ export declare function extractReasoningHeuristic(session: HermesSession): ExtractedItem | null;
115
+ /**
116
+ * Main extraction entry point. Runs both extractors and returns every
117
+ * item that matched. It is valid for a single session to yield both a
118
+ * finding AND a reasoning trace — research sessions that pivoted midway
119
+ * have both a synthesis (the finding) and a process worth remembering
120
+ * (the reasoning trace). The gateway dedup guards prevent actual dupes.
121
+ */
122
+ export declare function extractFromSession(session: HermesSession): ExtractedItem[];
123
+ //# sourceMappingURL=syncSessionsExtractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncSessionsExtractor.d.ts","sourceRoot":"","sources":["../src/syncSessionsExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AASH,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qDAAqD;IACrD,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAClD,CAAC,CAAC;IACH,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,yDAAyD;AACzD,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;CACH,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACnD,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAyNN;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,GAAG,IAAI,CA0CpF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,GAAG,IAAI,CAyCtF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,EAAE,CAO1E"}