open-mem 0.4.2 → 0.5.1
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/README.md +53 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/daemon/manager.d.ts +22 -0
- package/dist/daemon/manager.d.ts.map +1 -0
- package/dist/daemon/pid.d.ts +6 -0
- package/dist/daemon/pid.d.ts.map +1 -0
- package/dist/daemon/worker.d.ts +22 -0
- package/dist/daemon/worker.d.ts.map +1 -0
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +332 -0
- package/dist/dashboard/assets/index-9JxqY10c.css +1 -0
- package/dist/dashboard/assets/index-DWbZtwHB.js +60 -0
- package/dist/dashboard/dist/assets/index-9JxqY10c.css +1 -0
- package/dist/dashboard/dist/assets/index-CTwrdVhA.js +60 -0
- package/dist/dashboard/dist/assets/index-DWbZtwHB.js +60 -0
- package/dist/dashboard/dist/index.html +19 -0
- package/dist/dashboard/index.html +19 -0
- package/dist/db/database.d.ts +4 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/observations.d.ts +13 -0
- package/dist/db/observations.d.ts.map +1 -1
- package/dist/db/schema.d.ts +7 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/events/bus.d.ts +21 -0
- package/dist/events/bus.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -50
- package/dist/mcp.js +38 -26
- package/dist/queue/processor.d.ts +6 -0
- package/dist/queue/processor.d.ts.map +1 -1
- package/dist/search/hybrid.d.ts +1 -0
- package/dist/search/hybrid.d.ts.map +1 -1
- package/dist/servers/http-server.d.ts +25 -0
- package/dist/servers/http-server.d.ts.map +1 -0
- package/dist/servers/sse-broadcaster.d.ts +27 -0
- package/dist/servers/sse-broadcaster.d.ts.map +1 -0
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -8
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ Without any provider configured, open-mem still works — it falls back to a bas
|
|
|
77
77
|
- 🔒 **Privacy controls** with `<private>` tag support
|
|
78
78
|
- 🛠️ **Six custom tools**: mem-search, mem-save, mem-timeline, mem-recall, mem-export, mem-import
|
|
79
79
|
- 🌐 **MCP server mode** — expose memory tools to any MCP-compatible AI client
|
|
80
|
+
- 📊 **Web dashboard** — browse observations, sessions, search, and stats in a local web UI
|
|
80
81
|
- 🌳 **Git worktree support** — shared memory across all worktrees
|
|
81
82
|
- 📂 **AGENTS.md generation** — auto-generated folder-level context on session end
|
|
82
83
|
- 📦 **Import/export** — portable JSON for backup and transfer between machines
|
|
@@ -268,6 +269,54 @@ Or add it to your MCP client config:
|
|
|
268
269
|
|
|
269
270
|
The server communicates over stdin/stdout using JSON-RPC 2.0 and exposes: `mem-search`, `mem-save`, `mem-timeline`, `mem-recall`.
|
|
270
271
|
|
|
272
|
+
## Web Dashboard
|
|
273
|
+
|
|
274
|
+
open-mem includes an optional web dashboard for browsing observations, sessions, search results, and memory statistics in your browser.
|
|
275
|
+
|
|
276
|
+
### Enable the Dashboard
|
|
277
|
+
|
|
278
|
+
Set the environment variable before starting OpenCode:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
export OPEN_MEM_DASHBOARD=true
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
When enabled, open-mem starts a local HTTP server and logs the URL:
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
[open-mem] Dashboard available at http://127.0.0.1:3737
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Open that URL in your browser. The dashboard is read-only and bound to `127.0.0.1` (localhost only).
|
|
291
|
+
|
|
292
|
+
### Dashboard Pages
|
|
293
|
+
|
|
294
|
+
| Page | Description |
|
|
295
|
+
|------|-------------|
|
|
296
|
+
| **Timeline** | Real-time observation feed across all sessions. Supports type filtering and infinite scroll. New observations appear live via Server-Sent Events. |
|
|
297
|
+
| **Sessions** | Browse past coding sessions. Expand a session to see its observations, key facts, and files involved. |
|
|
298
|
+
| **Search** | Full-text and semantic search across all observations. Uses hybrid FTS5 + vector search when embeddings are configured. |
|
|
299
|
+
| **Stats** | Memory usage analytics — total observations, sessions, token savings from compression, and a type breakdown chart. |
|
|
300
|
+
| **Settings** | Read-only view of the current open-mem configuration (API keys are redacted). |
|
|
301
|
+
|
|
302
|
+
### Port Configuration
|
|
303
|
+
|
|
304
|
+
The dashboard defaults to port `3737`. If that port is busy, it automatically tries the next 9 ports (up to `3746`). To set a specific port:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
export OPEN_MEM_DASHBOARD_PORT=4000
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Building the Dashboard (Development)
|
|
311
|
+
|
|
312
|
+
If you're developing open-mem from source, the dashboard must be built before it can be served:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
bun run build:dashboard
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
This compiles the React app in `dashboard/` and copies the output to `dist/dashboard/`. The full `bun run build` also includes this step. Published npm packages ship with the dashboard pre-built.
|
|
319
|
+
|
|
271
320
|
## Configuration
|
|
272
321
|
|
|
273
322
|
open-mem works out of the box with zero configuration. All settings can be customized via environment variables:
|
|
@@ -293,6 +342,8 @@ open-mem works out of the box with zero configuration. All settings can be custo
|
|
|
293
342
|
| `OPEN_MEM_MAX_OBSERVATIONS` | `50` | Maximum observations to consider for context |
|
|
294
343
|
| `OPEN_MEM_FOLDER_CONTEXT` | `true` | Set to `false` to disable AGENTS.md generation |
|
|
295
344
|
| `OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH` | `5` | Max folder depth for AGENTS.md generation |
|
|
345
|
+
| `OPEN_MEM_DASHBOARD` | `false` | Set to `true` to enable the web dashboard |
|
|
346
|
+
| `OPEN_MEM_DASHBOARD_PORT` | `3737` | Dashboard HTTP port (tries up to +9 if busy) |
|
|
296
347
|
|
|
297
348
|
<details>
|
|
298
349
|
<summary><strong>Programmatic Configuration Reference</strong></summary>
|
|
@@ -320,6 +371,8 @@ If you need to configure open-mem programmatically (e.g. for testing or custom i
|
|
|
320
371
|
| `logLevel` | string | `warn` | Log level: `debug`, `info`, `warn`, `error` |
|
|
321
372
|
| `folderContextEnabled` | boolean | `true` | Auto-generate AGENTS.md in active folders |
|
|
322
373
|
| `folderContextMaxDepth` | number | `5` | Max folder depth from project root |
|
|
374
|
+
| `dashboardEnabled` | boolean | `false` | Enable the web dashboard |
|
|
375
|
+
| `dashboardPort` | number | `3737` | Dashboard HTTP port |
|
|
323
376
|
|
|
324
377
|
</details>
|
|
325
378
|
|
package/dist/config.d.ts
CHANGED
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAC;AAsH9D,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa5D;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAC5B,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAChC,aAAa,CAoDf;AAMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAuB9D;AAMD,8CAA8C;AAC9C,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAED,2CAA2C;AAC3C,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface DaemonManagerConfig {
|
|
2
|
+
dbPath: string;
|
|
3
|
+
projectPath: string;
|
|
4
|
+
daemonScript: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class DaemonManager {
|
|
7
|
+
private readonly pidPath;
|
|
8
|
+
private readonly projectPath;
|
|
9
|
+
private readonly daemonScript;
|
|
10
|
+
private subprocess;
|
|
11
|
+
constructor(config: DaemonManagerConfig);
|
|
12
|
+
start(): boolean;
|
|
13
|
+
signal(message: string): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
isRunning(): boolean;
|
|
16
|
+
getStatus(): {
|
|
17
|
+
running: boolean;
|
|
18
|
+
pid: number | null;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/daemon/manager.ts"],"names":[],"mappings":"AAMA,UAAU,mBAAmB;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,UAAU,CAA6C;gBAEnD,MAAM,EAAE,mBAAmB;IAMvC,KAAK,IAAI,OAAO;IA4BhB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ7B,IAAI,IAAI,IAAI;IAaZ,SAAS,IAAI,OAAO;IAQpB,SAAS,IAAI;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAUrD"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function writePid(pidPath: string): void;
|
|
2
|
+
export declare function readPid(pidPath: string): number | null;
|
|
3
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
4
|
+
export declare function removePid(pidPath: string): void;
|
|
5
|
+
export declare function getPidPath(dbPath: string): string;
|
|
6
|
+
//# sourceMappingURL=pid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pid.d.ts","sourceRoot":"","sources":["../../src/daemon/pid.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAO9C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUtD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWnD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAM/C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMjD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface BatchProcessor {
|
|
2
|
+
processBatch(): Promise<number>;
|
|
3
|
+
}
|
|
4
|
+
export interface DaemonWorkerOptions {
|
|
5
|
+
queueProcessor: BatchProcessor;
|
|
6
|
+
pollIntervalMs: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class DaemonWorker {
|
|
9
|
+
private queueProcessor;
|
|
10
|
+
private pollIntervalMs;
|
|
11
|
+
private timer;
|
|
12
|
+
private lastActiveAt;
|
|
13
|
+
private processing;
|
|
14
|
+
constructor(options: DaemonWorkerOptions);
|
|
15
|
+
start(): void;
|
|
16
|
+
stop(): void;
|
|
17
|
+
get isRunning(): boolean;
|
|
18
|
+
get idleMs(): number;
|
|
19
|
+
get shouldAutoExit(): boolean;
|
|
20
|
+
handleMessage(message: unknown): void;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/daemon/worker.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC9B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IACnC,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;CACvB;AAQD,qBAAa,YAAY;IACxB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,mBAAmB;IAKxC,KAAK,IAAI,IAAI;IAoBb,IAAI,IAAI,IAAI;IAOZ,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAkBrC"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":""}
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
var z=import.meta.require;import{parseArgs as T0}from"util";import{generateText as V0}from"ai";var Q0=new Set(["decision","bugfix","feature","refactor","discovery","change"]);function W(J,X){let Z=new RegExp(`<${X}[^>]*>([\\s\\S]*?)</${X}>`,"i"),$=J.match(Z);return $?$[1].trim():""}function G(J,X){let Z=new RegExp(`<${X}[^>]*>([\\s\\S]*?)</${X}>`,"gi"),$=[];for(let Q of J.matchAll(Z)){let H=Q[1].trim();if(H)$.push(H)}return $}function I(J){let X=W(J,"observation");if(!X)return null;let Z=W(X,"type").toLowerCase(),$=Q0.has(Z)?Z:"discovery",Q=W(X,"title")||"Untitled observation",H=W(X,"subtitle"),V=W(X,"narrative"),Y=G(W(X,"facts"),"fact"),K=G(W(X,"concepts"),"concept"),U=G(W(X,"files_read"),"file"),A=G(W(X,"files_modified"),"file");return{type:$,title:Q,subtitle:H,facts:Y,narrative:V,concepts:K,filesRead:U,filesModified:A}}function h(J){let X=W(J,"session_summary");if(!X)return null;let Z=W(X,"summary")||"No summary available",$=G(W(X,"key_decisions"),"decision"),Q=G(W(X,"files_modified"),"file"),H=G(W(X,"concepts"),"concept"),V=W(X,"request")||void 0,Y=W(X,"investigated")||void 0,K=W(X,"learned")||void 0,U=W(X,"completed")||void 0,A=W(X,"next_steps")||void 0;return{summary:Z,keyDecisions:$,filesModified:Q,concepts:H,request:V,investigated:Y,learned:K,completed:U,nextSteps:A}}function F(J){return Math.ceil(J.length/4)}function f(J,X,Z){let $=Z?`<session_context>
|
|
4
|
+
${Z}
|
|
5
|
+
</session_context>
|
|
6
|
+
|
|
7
|
+
`:"";return`<task>
|
|
8
|
+
Analyze the following tool output and extract a structured observation.
|
|
9
|
+
</task>
|
|
10
|
+
|
|
11
|
+
<tool_name>${J}</tool_name>
|
|
12
|
+
|
|
13
|
+
<tool_output>
|
|
14
|
+
${X}
|
|
15
|
+
</tool_output>
|
|
16
|
+
|
|
17
|
+
${$}<instructions>
|
|
18
|
+
Extract a structured observation from the tool output. Determine the most appropriate type and provide a concise but informative summary.
|
|
19
|
+
|
|
20
|
+
When extracting concepts, prefer established vocabulary where appropriate:
|
|
21
|
+
- how-it-works: Technical mechanisms and behaviors
|
|
22
|
+
- why-it-exists: Rationale and motivations
|
|
23
|
+
- what-changed: Modifications and their effects
|
|
24
|
+
- problem-solution: Issues encountered and how they were resolved
|
|
25
|
+
- gotcha: Surprising behaviors, edge cases, or pitfalls
|
|
26
|
+
- pattern: Recurring design patterns or approaches
|
|
27
|
+
- trade-off: Deliberate compromises between competing concerns
|
|
28
|
+
You may also use any domain-specific concepts relevant to the observation.
|
|
29
|
+
|
|
30
|
+
Respond with EXACTLY this XML format:
|
|
31
|
+
<observation>
|
|
32
|
+
<type>decision|bugfix|feature|refactor|discovery|change</type>
|
|
33
|
+
<title>Brief descriptive title (max 80 chars)</title>
|
|
34
|
+
<subtitle>One-line elaboration</subtitle>
|
|
35
|
+
<facts>
|
|
36
|
+
<fact>Specific factual detail 1</fact>
|
|
37
|
+
<fact>Specific factual detail 2</fact>
|
|
38
|
+
</facts>
|
|
39
|
+
<narrative>2-3 sentence narrative explaining what happened and why it matters</narrative>
|
|
40
|
+
<concepts>
|
|
41
|
+
<concept>relevant-concept-1</concept>
|
|
42
|
+
<concept>relevant-concept-2</concept>
|
|
43
|
+
</concepts>
|
|
44
|
+
<files_read>
|
|
45
|
+
<file>path/to/file/read</file>
|
|
46
|
+
</files_read>
|
|
47
|
+
<files_modified>
|
|
48
|
+
<file>path/to/file/modified</file>
|
|
49
|
+
</files_modified>
|
|
50
|
+
</observation>
|
|
51
|
+
</instructions>`}function v(J,X){let Z=J.map(($,Q)=>` <obs index="${Q+1}" type="${$.type}">
|
|
52
|
+
<title>${$.title}</title>
|
|
53
|
+
<narrative>${$.narrative}</narrative>
|
|
54
|
+
</obs>`).join(`
|
|
55
|
+
`);return`<task>
|
|
56
|
+
Summarize the following coding session based on its observations.
|
|
57
|
+
</task>
|
|
58
|
+
|
|
59
|
+
<session_id>${X}</session_id>
|
|
60
|
+
|
|
61
|
+
<observations>
|
|
62
|
+
${Z}
|
|
63
|
+
</observations>
|
|
64
|
+
|
|
65
|
+
<instructions>
|
|
66
|
+
Create a concise session summary. Focus on key decisions, outcomes, and patterns.
|
|
67
|
+
|
|
68
|
+
Respond with EXACTLY this XML format:
|
|
69
|
+
<session_summary>
|
|
70
|
+
<request>What the user asked for (1-2 sentences)</request>
|
|
71
|
+
<investigated>What was explored or researched</investigated>
|
|
72
|
+
<learned>Key discoveries and insights</learned>
|
|
73
|
+
<completed>What was accomplished</completed>
|
|
74
|
+
<next_steps>What to do next (if any)</next_steps>
|
|
75
|
+
<summary>2-4 sentence summary of the entire session</summary>
|
|
76
|
+
<key_decisions>
|
|
77
|
+
<decision>Important decision made during session</decision>
|
|
78
|
+
</key_decisions>
|
|
79
|
+
<files_modified>
|
|
80
|
+
<file>path/to/modified/file</file>
|
|
81
|
+
</files_modified>
|
|
82
|
+
<concepts>
|
|
83
|
+
<concept>key-concept</concept>
|
|
84
|
+
</concepts>
|
|
85
|
+
</session_summary>
|
|
86
|
+
</instructions>`}var Y0={"claude-sonnet-4-20250514":"us.anthropic.claude-sonnet-4-20250514-v1:0","claude-opus-4-20250514":"us.anthropic.claude-opus-4-20250514-v1:0","claude-3-5-sonnet-20241022":"us.anthropic.claude-3-5-sonnet-20241022-v2:0","claude-3-5-haiku-20241022":"us.anthropic.claude-3-5-haiku-20241022-v1:0","claude-3-haiku-20240307":"anthropic.claude-3-haiku-20240307-v1:0"};function H0(J){if(J.includes("."))return J;return Y0[J]||`us.anthropic.${J}-v1:0`}function N(J){switch(J.provider){case"anthropic":{let{createAnthropic:X}=z("@ai-sdk/anthropic");return X({apiKey:J.apiKey})(J.model)}case"bedrock":{let{createAmazonBedrock:X}=z("@ai-sdk/amazon-bedrock");return X()(H0(J.model))}case"openai":{let{createOpenAI:X}=z("@ai-sdk/openai");return X({apiKey:J.apiKey})(J.model)}case"google":{let{createGoogleGenerativeAI:X}=z("@ai-sdk/google");return X({apiKey:J.apiKey})(J.model)}default:throw Error(`Unknown provider: ${J.provider}. Supported: anthropic, bedrock, openai, google`)}}function w(J){try{switch(J.provider){case"google":{let{createGoogleGenerativeAI:X}=z("@ai-sdk/google");return X({apiKey:J.apiKey}).embedding("text-embedding-004")}case"openai":{let{createOpenAI:X}=z("@ai-sdk/openai");return X({apiKey:J.apiKey}).embedding("text-embedding-3-small")}case"bedrock":{let{createAmazonBedrock:X}=z("@ai-sdk/amazon-bedrock");return X().embedding("amazon.titan-embed-text-v2:0")}case"anthropic":return null;default:return null}}catch{return null}}var W0={"gemini-2.5-flash-lite":10,"gemini-2.5-flash":10,"gemini-2.5-pro":5,"gemini-2.0-flash":15,"gemini-2.0-flash-lite":30,"gemini-3-flash":5},u=0;async function M(J,X){if(!X)return;let Z=W0[J]||5,$=Math.ceil(60000/Z)+100,H=Date.now()-u;if(H<$){let V=$-H;await new Promise((Y)=>setTimeout(Y,V))}u=Date.now()}class _{model;config;_generate=V0;constructor(J){this.config=J,this.model=null;let X=J.provider!=="bedrock";if(J.compressionEnabled&&(!X||J.apiKey))try{this.model=N({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}static MAX_INPUT_LENGTH=50000;async compress(J,X,Z){if(!this.config.compressionEnabled||!this.model)return null;if(X.length<this.config.minOutputLength)return null;let $=F(X),Q=X.length>_.MAX_INPUT_LENGTH?`${X.substring(0,_.MAX_INPUT_LENGTH)}
|
|
87
|
+
|
|
88
|
+
[... truncated ...]`:X,H=f(J,Q,Z),V=2;for(let Y=0;Y<=V;Y++)try{if(this.config.provider==="google")await M(this.config.model,this.config.rateLimitingEnabled);let{text:K}=await this._generate({model:this.model,maxOutputTokens:this.config.maxTokensPerCompression,prompt:H}),U=I(K);if(U)U.discoveryTokens=$;return U}catch(K){if(z0(K)&&Y<V){let U=2**Y*1000;await b(U);continue}return null}return null}async compressBatch(J){let X=new Map;for(let Z=0;Z<J.length;Z++){let $=J[Z],Q=await this.compress($.toolName,$.toolOutput,$.sessionContext);if(X.set($.callId,Q),Z<J.length-1)await b(200)}return X}createFallbackObservation(J,X){let Z=U0(X),$=K0[J]??"discovery";return{type:$,title:`${J} execution`,subtitle:X.substring(0,100).replace(/\n/g," "),facts:[],narrative:`Tool ${J} was executed. Output length: ${X.length} chars.`,concepts:[],filesRead:$==="discovery"?Z:[],filesModified:$==="change"?Z:[],discoveryTokens:F(X)}}async isAvailable(){if(!this.model)return!1;try{return await this._generate({model:this.model,maxOutputTokens:10,prompt:"ping"}),!0}catch{return!1}}}var K0={Read:"discovery",Write:"change",Edit:"change",Bash:"change",Glob:"discovery",Grep:"discovery"},B0=/(?:^|\s)((?:\.\/|\/|src\/|tests\/|lib\/)\S+\.\w+)/gm;function U0(J){let X=[];for(let Z of J.matchAll(B0))X.push(Z[1]);return[...new Set(X)]}function z0(J){if(typeof J!=="object"||J===null)return!1;let X=J,Z=X.status;if(Z===429||Z===500||Z===503)return!0;let $=X.error;if(typeof $==="object"&&$!==null&&$.type==="overloaded_error")return!0;return!1}function b(J){return new Promise((X)=>setTimeout(X,J))}import{generateText as G0}from"ai";class C{model;config;_generate=G0;constructor(J){this.config=J,this.model=null;let X=J.provider!=="bedrock";if(J.compressionEnabled&&(!X||J.apiKey))try{this.model=N({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}async summarize(J,X){if(X.length===0)return null;if(!this.config.compressionEnabled||!this.model)return this.createFallbackSummary(X);let Z=v(X.map(($)=>({type:$.type,title:$.title,narrative:$.narrative})),J);try{if(this.config.provider==="google")await M(this.config.model,this.config.rateLimitingEnabled);let{text:$}=await this._generate({model:this.model,maxOutputTokens:this.config.maxTokensPerCompression,prompt:Z}),Q=h($);if(!Q)return this.createFallbackSummary(X);return Q}catch{return this.createFallbackSummary(X)}}createFallbackSummary(J){let X=new Set,Z=new Set,$=[];for(let Y of J){for(let K of Y.filesModified)X.add(K);for(let K of Y.concepts)Z.add(K);if(Y.type==="decision")$.push(Y.title)}let Q=new Map;for(let Y of J)Q.set(Y.type,(Q.get(Y.type)??0)+1);let H=Array.from(Q.entries()).map(([Y,K])=>`${K} ${Y}${K>1?"s":""}`).join(", "),V=Array.from(Z).slice(0,5).join(", ");return{summary:`Session with ${J.length} observations: ${H}. Files modified: ${X.size}. Key concepts: ${V}.`,keyDecisions:$.slice(0,5),filesModified:Array.from(X),concepts:Array.from(Z)}}shouldSummarize(J){return J>=2}}var F0={dbPath:".open-mem/memory.db",provider:"google",apiKey:void 0,model:"gemini-2.5-flash-lite",maxTokensPerCompression:1024,compressionEnabled:!0,contextInjectionEnabled:!0,maxContextTokens:4000,batchSize:5,batchIntervalMs:30000,ignoredTools:[],minOutputLength:50,maxIndexEntries:20,sensitivePatterns:[],retentionDays:90,maxDatabaseSizeMb:500,logLevel:"warn",contextShowTokenCosts:!0,contextObservationTypes:"all",contextFullObservationCount:3,maxObservations:50,contextShowLastSummary:!0,rateLimitingEnabled:!0,folderContextEnabled:!0,folderContextMaxDepth:5,daemonEnabled:!1,dashboardEnabled:!1,dashboardPort:3737,embeddingDimension:void 0};function L0(){let J={};if(process.env.OPEN_MEM_DB_PATH)J.dbPath=process.env.OPEN_MEM_DB_PATH;if(process.env.OPEN_MEM_PROVIDER)J.provider=process.env.OPEN_MEM_PROVIDER;if(process.env.OPEN_MEM_MODEL)J.model=process.env.OPEN_MEM_MODEL;if(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS)J.maxContextTokens=Number.parseInt(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS,10);if(process.env.OPEN_MEM_COMPRESSION==="false")J.compressionEnabled=!1;if(process.env.OPEN_MEM_CONTEXT_INJECTION==="false")J.contextInjectionEnabled=!1;if(process.env.OPEN_MEM_IGNORED_TOOLS)J.ignoredTools=process.env.OPEN_MEM_IGNORED_TOOLS.split(",").map((X)=>X.trim());if(process.env.OPEN_MEM_BATCH_SIZE)J.batchSize=Number.parseInt(process.env.OPEN_MEM_BATCH_SIZE,10);if(process.env.OPEN_MEM_RETENTION_DAYS)J.retentionDays=Number.parseInt(process.env.OPEN_MEM_RETENTION_DAYS,10);if(process.env.OPEN_MEM_LOG_LEVEL)J.logLevel=process.env.OPEN_MEM_LOG_LEVEL;if(process.env.OPEN_MEM_CONTEXT_SHOW_TOKEN_COSTS==="false")J.contextShowTokenCosts=!1;if(process.env.OPEN_MEM_CONTEXT_TYPES)J.contextObservationTypes=process.env.OPEN_MEM_CONTEXT_TYPES==="all"?"all":process.env.OPEN_MEM_CONTEXT_TYPES.split(",").map((X)=>X.trim());if(process.env.OPEN_MEM_CONTEXT_FULL_COUNT)J.contextFullObservationCount=Number.parseInt(process.env.OPEN_MEM_CONTEXT_FULL_COUNT,10);if(process.env.OPEN_MEM_MAX_OBSERVATIONS)J.maxObservations=Number.parseInt(process.env.OPEN_MEM_MAX_OBSERVATIONS,10);if(process.env.OPEN_MEM_CONTEXT_SHOW_LAST_SUMMARY==="false")J.contextShowLastSummary=!1;if(process.env.OPEN_MEM_RATE_LIMITING==="false")J.rateLimitingEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT==="false")J.folderContextEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH)J.folderContextMaxDepth=Number.parseInt(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH,10);if(process.env.OPEN_MEM_DAEMON==="true")J.daemonEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD==="true")J.dashboardEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD_PORT)J.dashboardPort=Number.parseInt(process.env.OPEN_MEM_DASHBOARD_PORT,10);if(process.env.OPEN_MEM_EMBEDDING_DIMENSION)J.embeddingDimension=Number.parseInt(process.env.OPEN_MEM_EMBEDDING_DIMENSION,10);return J}function _0(J){switch(J){case"google":return 768;case"openai":return 1536;case"bedrock":return 1024;case"anthropic":return 0;default:return 768}}function g(J,X){let Z=L0(),$={...F0,...Z,...X};if(!$.dbPath.startsWith("/"))$.dbPath=`${J}/${$.dbPath}`;if(!process.env.OPEN_MEM_PROVIDER&&!X?.provider){if(process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY)$.provider="google";else if(process.env.ANTHROPIC_API_KEY)$.provider="anthropic";else if(process.env.AWS_BEARER_TOKEN_BEDROCK||process.env.AWS_ACCESS_KEY_ID||process.env.AWS_PROFILE)$.provider="bedrock"}if(!$.apiKey)switch($.provider){case"google":$.apiKey=process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY;break;case"anthropic":$.apiKey=process.env.ANTHROPIC_API_KEY;break;case"openai":$.apiKey=process.env.OPENAI_API_KEY;break;case"bedrock":break}if($.embeddingDimension===void 0)$.embeddingDimension=_0($.provider);return $}import{existsSync as WJ,mkdirSync as N0,readFileSync as VJ,unlinkSync as M0,writeFileSync as E0}from"fs";function p(J){let X=J.lastIndexOf("/");if(X>0){let Z=J.substring(0,X);N0(Z,{recursive:!0})}E0(J,String(process.pid),"utf-8")}function l(J){try{M0(J)}catch{}}function m(J){let X=J.lastIndexOf("/");if(X>=0)return`${J.substring(0,X)}/worker.pid`;return"worker.pid"}class j{queueProcessor;pollIntervalMs;timer=null;lastActiveAt=Date.now();processing=!1;constructor(J){this.queueProcessor=J.queueProcessor,this.pollIntervalMs=J.pollIntervalMs}start(){if(this.timer)return;this.lastActiveAt=Date.now(),this.timer=setInterval(async()=>{if(this.processing)return;this.processing=!0;try{if(await this.queueProcessor.processBatch()>0)this.lastActiveAt=Date.now()}catch{}finally{this.processing=!1}},this.pollIntervalMs)}stop(){if(this.timer)clearInterval(this.timer),this.timer=null}get isRunning(){return this.timer!==null}get idleMs(){return Date.now()-this.lastActiveAt}get shouldAutoExit(){return this.idleMs>=60000&&!process.send}handleMessage(J){if(J==="SHUTDOWN")this.stop();else if(J==="PROCESS_NOW"){if(!this.processing)this.processing=!0,this.queueProcessor.processBatch().then((X)=>{if(X>0)this.lastActiveAt=Date.now()}).catch(()=>{}).finally(()=>{this.processing=!1})}}}import{Database as c}from"bun:sqlite";import{existsSync as S,mkdirSync as A0,unlinkSync as d}from"fs";import*as n from"sqlite-vec";class E{db;dbPath;_hasVectorExtension=!1;static enableExtensionSupport(){let J=["/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib","/usr/local/opt/sqlite/lib/libsqlite3.dylib"];for(let X of J)try{if(S(X))return c.setCustomSQLite(X),!0}catch{return!1}return!1}constructor(J){this.dbPath=J,this.db=this.open(J),this.configure()}open(J){let X=J.lastIndexOf("/");if(X>0){let Z=J.substring(0,X);A0(Z,{recursive:!0})}return new c(J,{create:!0})}configure(){try{this.applyPragmas(),this.loadExtensions()}catch(J){console.warn("[open-mem] Database configure failed, attempting recovery by removing WAL/SHM files:",J.message);try{this.db.close()}catch{}this.deleteSidecarFiles();try{this.db=this.open(this.dbPath),this.applyPragmas(),this.loadExtensions(),console.warn("[open-mem] Recovery successful after removing WAL/SHM files");return}catch(X){console.warn("[open-mem] WAL/SHM cleanup insufficient, recreating database from scratch:",X.message);try{this.db.close()}catch{}this.deleteDatabaseFiles();try{this.db=this.open(this.dbPath),this.applyPragmas(),this.loadExtensions(),console.warn("[open-mem] Recovery successful after full database recreation");return}catch(Z){throw console.warn("[open-mem] All recovery attempts failed, filesystem may be broken:",Z.message),J}}}}applyPragmas(){this.db.exec("PRAGMA journal_mode = WAL"),this.db.exec("PRAGMA synchronous = NORMAL"),this.db.exec("PRAGMA foreign_keys = ON"),this.db.exec("PRAGMA busy_timeout = 5000")}loadExtensions(){try{n.load(this.db),this._hasVectorExtension=!0}catch{this._hasVectorExtension=!1}}get hasVectorExtension(){return this._hasVectorExtension}deleteSidecarFiles(){for(let J of["-wal","-shm"]){let X=this.dbPath+J;try{if(S(X))d(X)}catch{}}}deleteDatabaseFiles(){this.deleteSidecarFiles();try{if(S(this.dbPath))d(this.dbPath)}catch{}}ensureMigrationTable(){this.db.exec(`
|
|
89
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
90
|
+
version INTEGER PRIMARY KEY,
|
|
91
|
+
name TEXT NOT NULL,
|
|
92
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
93
|
+
)
|
|
94
|
+
`)}migrate(J){this.ensureMigrationTable();let X=this.db.query("SELECT version FROM _migrations ORDER BY version").all(),Z=new Set(X.map((Q)=>Q.version)),$=J.filter((Q)=>!Z.has(Q.version)).sort((Q,H)=>Q.version-H.version);for(let Q of $)this.db.transaction(()=>{this.db.exec(Q.up),this.db.query("INSERT INTO _migrations (version, name) VALUES ($version, $name)").run({$version:Q.version,$name:Q.name})})()}run(J,X){let Z=this.db.query(J);if(X)Z.run(...X);else Z.run()}get(J,X){let Z=this.db.query(J);return X?Z.get(...X):Z.get()}all(J,X){let Z=this.db.query(J);return X?Z.all(...X):Z.all()}exec(J){this.db.exec(J)}transaction(J){return this.db.transaction(J)()}close(){this.db.close()}get isOpen(){try{return this.db.query("SELECT 1").get(),!0}catch{return!1}}get raw(){return this.db}}function i(J){return new E(J)}import{randomUUID as C0}from"crypto";class q{db;constructor(J){this.db=J}create(J){let X=C0(),Z=new Date().toISOString(),$=J.discoveryTokens??0;return this.db.run(`INSERT INTO observations
|
|
95
|
+
(id, session_id, type, title, subtitle, facts, narrative,
|
|
96
|
+
concepts, files_read, files_modified, raw_tool_output,
|
|
97
|
+
tool_name, created_at, token_count, discovery_tokens)
|
|
98
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[X,J.sessionId,J.type,J.title,J.subtitle,JSON.stringify(J.facts),J.narrative,JSON.stringify(J.concepts),JSON.stringify(J.filesRead),JSON.stringify(J.filesModified),J.rawToolOutput,J.toolName,Z,J.tokenCount,$]),{...J,id:X,createdAt:Z,discoveryTokens:$}}importObservation(J){this.db.run(`INSERT INTO observations
|
|
99
|
+
(id, session_id, type, title, subtitle, facts, narrative,
|
|
100
|
+
concepts, files_read, files_modified, raw_tool_output,
|
|
101
|
+
tool_name, created_at, token_count, discovery_tokens)
|
|
102
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[J.id,J.sessionId,J.type,J.title,J.subtitle,JSON.stringify(J.facts),J.narrative,JSON.stringify(J.concepts),JSON.stringify(J.filesRead),JSON.stringify(J.filesModified),J.rawToolOutput,J.toolName,J.createdAt,J.tokenCount,J.discoveryTokens??0])}getById(J){let X=this.db.get("SELECT * FROM observations WHERE id = ?",[J]);return X?this.mapRow(X):null}getBySession(J){return this.db.all("SELECT * FROM observations WHERE session_id = ? ORDER BY created_at ASC",[J]).map((X)=>this.mapRow(X))}getCount(J){if(J)return this.db.get("SELECT COUNT(*) as count FROM observations WHERE session_id = ?",[J])?.count??0;return this.db.get("SELECT COUNT(*) as count FROM observations")?.count??0}getIndex(J,X=20){return this.db.all(`SELECT o.id, o.session_id, o.type, o.title, o.token_count, o.discovery_tokens, o.created_at
|
|
103
|
+
FROM observations o
|
|
104
|
+
JOIN sessions s ON o.session_id = s.id
|
|
105
|
+
WHERE s.project_path = ?
|
|
106
|
+
ORDER BY o.created_at DESC
|
|
107
|
+
LIMIT ?`,[J,X]).map((Z)=>({id:Z.id,sessionId:Z.session_id,type:Z.type,title:Z.title,tokenCount:Z.token_count,discoveryTokens:Z.discovery_tokens??0,createdAt:Z.created_at}))}search(J){let X=`
|
|
108
|
+
SELECT o.*, rank
|
|
109
|
+
FROM observations o
|
|
110
|
+
JOIN observations_fts fts ON o._rowid = fts.rowid
|
|
111
|
+
WHERE observations_fts MATCH ?
|
|
112
|
+
`,Z=[J.query];if(J.sessionId)X+=" AND o.session_id = ?",Z.push(J.sessionId);if(J.type)X+=" AND o.type = ?",Z.push(J.type);return X+=" ORDER BY rank LIMIT ? OFFSET ?",Z.push(J.limit??10),Z.push(J.offset??0),this.db.all(X,Z).map(($)=>({observation:this.mapRow($),rank:$.rank,snippet:$.title}))}searchByConcept(J,X=10){return this.db.all(`SELECT o.*
|
|
113
|
+
FROM observations o
|
|
114
|
+
JOIN observations_fts fts ON o._rowid = fts.rowid
|
|
115
|
+
WHERE observations_fts MATCH ?
|
|
116
|
+
ORDER BY rank
|
|
117
|
+
LIMIT ?`,[`concepts:${J}`,X]).map((Z)=>this.mapRow(Z))}searchByFile(J,X=10){return this.db.all(`SELECT o.*
|
|
118
|
+
FROM observations o
|
|
119
|
+
JOIN observations_fts fts ON o._rowid = fts.rowid
|
|
120
|
+
WHERE observations_fts MATCH ?
|
|
121
|
+
ORDER BY rank
|
|
122
|
+
LIMIT ?`,[`files_read:"${J.replace(/"/g,'""')}" OR files_modified:"${J.replace(/"/g,'""')}"`,X]).map((Z)=>this.mapRow(Z))}setEmbedding(J,X){this.db.run("UPDATE observations SET embedding = ? WHERE id = ?",[JSON.stringify(X),J])}getWithEmbeddings(J,X){return this.db.all(`SELECT o.id, o.embedding, o.title
|
|
123
|
+
FROM observations o
|
|
124
|
+
JOIN sessions s ON o.session_id = s.id
|
|
125
|
+
WHERE s.project_path = ? AND o.embedding IS NOT NULL
|
|
126
|
+
ORDER BY o.created_at DESC
|
|
127
|
+
LIMIT ?`,[J,X]).map((Z)=>{try{return{id:Z.id,embedding:JSON.parse(Z.embedding),title:Z.title}}catch{return null}}).filter((Z)=>Z!==null)}insertVecEmbedding(J,X){let Z=new Float32Array(X);this.db.run("BEGIN");try{this.db.run("DELETE FROM observation_embeddings WHERE observation_id = ?",[J]),this.db.run("INSERT INTO observation_embeddings (observation_id, embedding) VALUES (?, ?)",[J,Z]),this.db.run("COMMIT")}catch($){throw this.db.run("ROLLBACK"),$}}migrateExistingEmbeddings(J){let X=this.db.all("SELECT id, embedding FROM observations WHERE embedding IS NOT NULL"),Z=0,$=0;for(let Q of X)try{let H=JSON.parse(Q.embedding);if(!Array.isArray(H)||H.length!==J){$++;continue}this.insertVecEmbedding(Q.id,H),Z++}catch{$++}return{migrated:Z,skipped:$}}getVecEmbeddingMatches(J,X){try{let Z=new Float32Array(J);return this.db.all(`SELECT observation_id, distance
|
|
128
|
+
FROM observation_embeddings
|
|
129
|
+
WHERE embedding MATCH ? AND k = ?`,[Z,X]).map(($)=>({observationId:$.observation_id,distance:$.distance}))}catch{return[]}}searchVecSubset(J,X,Z){if(X.length===0)return[];try{let $=new Float32Array(J),Q=Math.max(Z*5,X.length),H=this.db.all(`SELECT observation_id, distance
|
|
130
|
+
FROM observation_embeddings
|
|
131
|
+
WHERE embedding MATCH ? AND k = ?`,[$,Q]),V=new Set(X);return H.filter((Y)=>V.has(Y.observation_id)).slice(0,Z).map((Y)=>({observationId:Y.observation_id,distance:Y.distance}))}catch{return[]}}mapRow(J){return{id:J.id,sessionId:J.session_id,type:J.type,title:J.title,subtitle:J.subtitle,facts:JSON.parse(J.facts),narrative:J.narrative,concepts:JSON.parse(J.concepts),filesRead:JSON.parse(J.files_read),filesModified:JSON.parse(J.files_modified),rawToolOutput:J.raw_tool_output,toolName:J.tool_name,createdAt:J.created_at,tokenCount:J.token_count,discoveryTokens:J.discovery_tokens??0}}}import{randomUUID as j0}from"crypto";class O{db;constructor(J){this.db=J}create(J){let X=j0(),Z=new Date().toISOString();return this.db.run(`INSERT INTO pending_messages
|
|
132
|
+
(id, session_id, tool_name, tool_output, call_id, created_at)
|
|
133
|
+
VALUES (?, ?, ?, ?, ?, ?)`,[X,J.sessionId,J.toolName,J.toolOutput,J.callId,Z]),{...J,id:X,createdAt:Z,status:"pending",retryCount:0,error:null}}getPending(J=10){return this.db.all("SELECT * FROM pending_messages WHERE status = 'pending' ORDER BY created_at ASC LIMIT ?",[J]).map((X)=>this.mapRow(X))}getByStatus(J){return this.db.all("SELECT * FROM pending_messages WHERE status = ? ORDER BY created_at ASC",[J]).map((X)=>this.mapRow(X))}markProcessing(J){this.db.run("UPDATE pending_messages SET status = 'processing' WHERE id = ?",[J])}markCompleted(J){this.db.run("UPDATE pending_messages SET status = 'completed' WHERE id = ?",[J])}markFailed(J,X){this.db.run("UPDATE pending_messages SET status = 'failed', error = ?, retry_count = retry_count + 1 WHERE id = ?",[X,J])}resetStale(J=5){return this.db.all(`UPDATE pending_messages SET status = 'pending'
|
|
134
|
+
WHERE status = 'processing'
|
|
135
|
+
AND created_at < datetime('now', ? || ' minutes')
|
|
136
|
+
RETURNING id`,[`-${J}`]).length}mapRow(J){return{id:J.id,sessionId:J.session_id,toolName:J.tool_name,toolOutput:J.tool_output,callId:J.call_id,createdAt:J.created_at,status:J.status,retryCount:J.retry_count,error:J.error??null}}}var S0=[{version:1,name:"create-core-tables",up:`
|
|
137
|
+
-- Sessions table
|
|
138
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
139
|
+
_rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
140
|
+
id TEXT UNIQUE NOT NULL,
|
|
141
|
+
project_path TEXT NOT NULL,
|
|
142
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
143
|
+
ended_at TEXT,
|
|
144
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
145
|
+
CHECK (status IN ('active', 'idle', 'completed')),
|
|
146
|
+
observation_count INTEGER NOT NULL DEFAULT 0,
|
|
147
|
+
summary_id TEXT
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
151
|
+
ON sessions(project_path);
|
|
152
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_status
|
|
153
|
+
ON sessions(status);
|
|
154
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_started
|
|
155
|
+
ON sessions(started_at DESC);
|
|
156
|
+
|
|
157
|
+
-- Observations table
|
|
158
|
+
CREATE TABLE IF NOT EXISTS observations (
|
|
159
|
+
_rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
160
|
+
id TEXT UNIQUE NOT NULL,
|
|
161
|
+
session_id TEXT NOT NULL,
|
|
162
|
+
type TEXT NOT NULL
|
|
163
|
+
CHECK (type IN ('decision','bugfix','feature','refactor','discovery','change')),
|
|
164
|
+
title TEXT NOT NULL,
|
|
165
|
+
subtitle TEXT NOT NULL DEFAULT '',
|
|
166
|
+
facts TEXT NOT NULL DEFAULT '[]',
|
|
167
|
+
narrative TEXT NOT NULL DEFAULT '',
|
|
168
|
+
concepts TEXT NOT NULL DEFAULT '[]',
|
|
169
|
+
files_read TEXT NOT NULL DEFAULT '[]',
|
|
170
|
+
files_modified TEXT NOT NULL DEFAULT '[]',
|
|
171
|
+
raw_tool_output TEXT NOT NULL,
|
|
172
|
+
tool_name TEXT NOT NULL,
|
|
173
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
174
|
+
token_count INTEGER NOT NULL DEFAULT 0,
|
|
175
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
CREATE INDEX IF NOT EXISTS idx_observations_session
|
|
179
|
+
ON observations(session_id);
|
|
180
|
+
CREATE INDEX IF NOT EXISTS idx_observations_type
|
|
181
|
+
ON observations(type);
|
|
182
|
+
CREATE INDEX IF NOT EXISTS idx_observations_created
|
|
183
|
+
ON observations(created_at DESC);
|
|
184
|
+
|
|
185
|
+
-- Session summaries table
|
|
186
|
+
CREATE TABLE IF NOT EXISTS session_summaries (
|
|
187
|
+
_rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
188
|
+
id TEXT UNIQUE NOT NULL,
|
|
189
|
+
session_id TEXT NOT NULL UNIQUE,
|
|
190
|
+
summary TEXT NOT NULL,
|
|
191
|
+
key_decisions TEXT NOT NULL DEFAULT '[]',
|
|
192
|
+
files_modified TEXT NOT NULL DEFAULT '[]',
|
|
193
|
+
concepts TEXT NOT NULL DEFAULT '[]',
|
|
194
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
195
|
+
token_count INTEGER NOT NULL DEFAULT 0,
|
|
196
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
-- Pending messages (queue persistence)
|
|
200
|
+
CREATE TABLE IF NOT EXISTS pending_messages (
|
|
201
|
+
_rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
202
|
+
id TEXT UNIQUE NOT NULL,
|
|
203
|
+
session_id TEXT NOT NULL,
|
|
204
|
+
tool_name TEXT NOT NULL,
|
|
205
|
+
tool_output TEXT NOT NULL,
|
|
206
|
+
call_id TEXT NOT NULL,
|
|
207
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
208
|
+
status TEXT NOT NULL DEFAULT 'pending'
|
|
209
|
+
CHECK (status IN ('pending','processing','completed','failed')),
|
|
210
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
211
|
+
error TEXT,
|
|
212
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
CREATE INDEX IF NOT EXISTS idx_pending_status
|
|
216
|
+
ON pending_messages(status);
|
|
217
|
+
CREATE INDEX IF NOT EXISTS idx_pending_session
|
|
218
|
+
ON pending_messages(session_id);
|
|
219
|
+
`},{version:2,name:"create-fts5-tables",up:`
|
|
220
|
+
-- FTS5 for observations (title, subtitle, narrative, facts, concepts, files)
|
|
221
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
|
|
222
|
+
title,
|
|
223
|
+
subtitle,
|
|
224
|
+
narrative,
|
|
225
|
+
facts,
|
|
226
|
+
concepts,
|
|
227
|
+
files_read,
|
|
228
|
+
files_modified,
|
|
229
|
+
content=observations,
|
|
230
|
+
content_rowid=_rowid,
|
|
231
|
+
tokenize='porter unicode61'
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
-- Triggers to keep FTS5 in sync with observations table
|
|
235
|
+
CREATE TRIGGER observations_ai AFTER INSERT ON observations BEGIN
|
|
236
|
+
INSERT INTO observations_fts(
|
|
237
|
+
rowid, title, subtitle, narrative, facts, concepts,
|
|
238
|
+
files_read, files_modified
|
|
239
|
+
)
|
|
240
|
+
VALUES (
|
|
241
|
+
new._rowid, new.title, new.subtitle, new.narrative,
|
|
242
|
+
new.facts, new.concepts, new.files_read, new.files_modified
|
|
243
|
+
);
|
|
244
|
+
END;
|
|
245
|
+
|
|
246
|
+
CREATE TRIGGER observations_ad AFTER DELETE ON observations BEGIN
|
|
247
|
+
INSERT INTO observations_fts(
|
|
248
|
+
observations_fts, rowid, title, subtitle, narrative,
|
|
249
|
+
facts, concepts, files_read, files_modified
|
|
250
|
+
)
|
|
251
|
+
VALUES (
|
|
252
|
+
'delete', old._rowid, old.title, old.subtitle, old.narrative,
|
|
253
|
+
old.facts, old.concepts, old.files_read, old.files_modified
|
|
254
|
+
);
|
|
255
|
+
END;
|
|
256
|
+
|
|
257
|
+
CREATE TRIGGER observations_au AFTER UPDATE ON observations BEGIN
|
|
258
|
+
INSERT INTO observations_fts(
|
|
259
|
+
observations_fts, rowid, title, subtitle, narrative,
|
|
260
|
+
facts, concepts, files_read, files_modified
|
|
261
|
+
)
|
|
262
|
+
VALUES (
|
|
263
|
+
'delete', old._rowid, old.title, old.subtitle, old.narrative,
|
|
264
|
+
old.facts, old.concepts, old.files_read, old.files_modified
|
|
265
|
+
);
|
|
266
|
+
INSERT INTO observations_fts(
|
|
267
|
+
rowid, title, subtitle, narrative, facts, concepts,
|
|
268
|
+
files_read, files_modified
|
|
269
|
+
)
|
|
270
|
+
VALUES (
|
|
271
|
+
new._rowid, new.title, new.subtitle, new.narrative,
|
|
272
|
+
new.facts, new.concepts, new.files_read, new.files_modified
|
|
273
|
+
);
|
|
274
|
+
END;
|
|
275
|
+
|
|
276
|
+
-- FTS5 for session summaries
|
|
277
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS summaries_fts USING fts5(
|
|
278
|
+
summary,
|
|
279
|
+
key_decisions,
|
|
280
|
+
concepts,
|
|
281
|
+
content=session_summaries,
|
|
282
|
+
content_rowid=_rowid,
|
|
283
|
+
tokenize='porter unicode61'
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
CREATE TRIGGER summaries_ai AFTER INSERT ON session_summaries BEGIN
|
|
287
|
+
INSERT INTO summaries_fts(rowid, summary, key_decisions, concepts)
|
|
288
|
+
VALUES (new._rowid, new.summary, new.key_decisions, new.concepts);
|
|
289
|
+
END;
|
|
290
|
+
|
|
291
|
+
CREATE TRIGGER summaries_ad AFTER DELETE ON session_summaries BEGIN
|
|
292
|
+
INSERT INTO summaries_fts(
|
|
293
|
+
summaries_fts, rowid, summary, key_decisions, concepts
|
|
294
|
+
)
|
|
295
|
+
VALUES (
|
|
296
|
+
'delete', old._rowid, old.summary, old.key_decisions, old.concepts
|
|
297
|
+
);
|
|
298
|
+
END;
|
|
299
|
+
`},{version:3,name:"add-structured-summary-columns",up:`
|
|
300
|
+
ALTER TABLE session_summaries ADD COLUMN request TEXT NOT NULL DEFAULT '';
|
|
301
|
+
ALTER TABLE session_summaries ADD COLUMN investigated TEXT NOT NULL DEFAULT '';
|
|
302
|
+
ALTER TABLE session_summaries ADD COLUMN learned TEXT NOT NULL DEFAULT '';
|
|
303
|
+
ALTER TABLE session_summaries ADD COLUMN completed TEXT NOT NULL DEFAULT '';
|
|
304
|
+
ALTER TABLE session_summaries ADD COLUMN next_steps TEXT NOT NULL DEFAULT '';
|
|
305
|
+
`},{version:4,name:"add-discovery-tokens",up:`
|
|
306
|
+
ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER NOT NULL DEFAULT 0;
|
|
307
|
+
`},{version:5,name:"add-embedding-column",up:`
|
|
308
|
+
ALTER TABLE observations ADD COLUMN embedding TEXT;
|
|
309
|
+
`},{version:6,name:"create-embedding-meta-table",up:`
|
|
310
|
+
CREATE TABLE IF NOT EXISTS _embedding_meta (
|
|
311
|
+
key TEXT PRIMARY KEY,
|
|
312
|
+
value TEXT NOT NULL
|
|
313
|
+
);
|
|
314
|
+
`}];function s(J,X){if(J.migrate(S0),X?.hasVectorExtension&&X?.embeddingDimension&&X.embeddingDimension>0)q0(J,X.embeddingDimension)}function q0(J,X){if(J.get("SELECT name FROM sqlite_master WHERE type='table' AND name='observation_embeddings'")){let $=J.get("SELECT value FROM _embedding_meta WHERE key = 'dimension'");if($&&Number($.value)!==X){console.warn(`[open-mem] vec0 table exists with dimension ${$.value}, but config specifies ${X}. Drop observation_embeddings to re-create with new dimension.`);return}}else J.exec(`CREATE VIRTUAL TABLE observation_embeddings USING vec0(
|
|
315
|
+
observation_id TEXT PRIMARY KEY,
|
|
316
|
+
embedding float[${X}] distance_metric=cosine
|
|
317
|
+
)`);J.run("INSERT OR REPLACE INTO _embedding_meta (key, value) VALUES (?, ?)",["dimension",String(X)])}class D{db;constructor(J){this.db=J}create(J,X){let Z=new Date().toISOString();return this.db.run(`INSERT INTO sessions (id, project_path, started_at, status)
|
|
318
|
+
VALUES (?, ?, ?, 'active')`,[J,X,Z]),this.getById(J)}getOrCreate(J,X){let Z=this.getById(J);if(Z)return Z;return this.create(J,X)}getById(J){let X=this.db.get("SELECT * FROM sessions WHERE id = ?",[J]);return X?this.mapRow(X):null}getRecent(J,X=10){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC LIMIT ?",[J,X]).map((Z)=>this.mapRow(Z))}getAll(J){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC",[J]).map((X)=>this.mapRow(X))}getActive(){return this.db.all("SELECT * FROM sessions WHERE status = 'active' ORDER BY started_at DESC").map((J)=>this.mapRow(J))}updateStatus(J,X){this.db.run("UPDATE sessions SET status = ? WHERE id = ?",[X,J])}markCompleted(J){this.db.run("UPDATE sessions SET status = 'completed', ended_at = datetime('now') WHERE id = ?",[J])}incrementObservationCount(J){this.db.run("UPDATE sessions SET observation_count = observation_count + 1 WHERE id = ?",[J])}setSummary(J,X){this.db.run("UPDATE sessions SET summary_id = ? WHERE id = ?",[X,J])}mapRow(J){return{id:J.id,projectPath:J.project_path,startedAt:J.started_at,endedAt:J.ended_at??null,status:J.status,observationCount:J.observation_count,summaryId:J.summary_id??null}}}import{randomUUID as O0}from"crypto";class R{db;constructor(J){this.db=J}create(J){let X=O0(),Z=new Date().toISOString();return this.db.run(`INSERT INTO session_summaries
|
|
319
|
+
(id, session_id, summary, key_decisions, files_modified,
|
|
320
|
+
concepts, created_at, token_count,
|
|
321
|
+
request, investigated, learned, completed, next_steps)
|
|
322
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[X,J.sessionId,J.summary,JSON.stringify(J.keyDecisions),JSON.stringify(J.filesModified),JSON.stringify(J.concepts),Z,J.tokenCount,J.request??"",J.investigated??"",J.learned??"",J.completed??"",J.nextSteps??""]),{...J,id:X,createdAt:Z}}importSummary(J){this.db.run(`INSERT INTO session_summaries
|
|
323
|
+
(id, session_id, summary, key_decisions, files_modified,
|
|
324
|
+
concepts, created_at, token_count,
|
|
325
|
+
request, investigated, learned, completed, next_steps)
|
|
326
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[J.id,J.sessionId,J.summary,JSON.stringify(J.keyDecisions),JSON.stringify(J.filesModified),JSON.stringify(J.concepts),J.createdAt,J.tokenCount,J.request??"",J.investigated??"",J.learned??"",J.completed??"",J.nextSteps??""])}getBySessionId(J){let X=this.db.get("SELECT * FROM session_summaries WHERE session_id = ?",[J]);return X?this.mapRow(X):null}getRecent(J=10){return this.db.all("SELECT * FROM session_summaries ORDER BY created_at DESC LIMIT ?",[J]).map((X)=>this.mapRow(X))}search(J,X=10){return this.db.all(`SELECT ss.*
|
|
327
|
+
FROM session_summaries ss
|
|
328
|
+
JOIN summaries_fts fts ON ss._rowid = fts.rowid
|
|
329
|
+
WHERE summaries_fts MATCH ?
|
|
330
|
+
ORDER BY rank
|
|
331
|
+
LIMIT ?`,[J,X]).map((Z)=>this.mapRow(Z))}mapRow(J){return{id:J.id,sessionId:J.session_id,summary:J.summary,keyDecisions:JSON.parse(J.key_decisions),filesModified:JSON.parse(J.files_modified),concepts:JSON.parse(J.concepts),createdAt:J.created_at,tokenCount:J.token_count,request:J.request||void 0,investigated:J.investigated||void 0,learned:J.learned||void 0,completed:J.completed||void 0,nextSteps:J.next_steps||void 0}}}import{embed as D0}from"ai";async function r(J,X){try{let{embedding:Z}=await D0({model:J,value:X});return Z}catch{return null}}function a(J){let X=[J.title,J.narrative];if(J.concepts.length>0)X.push(J.concepts.join(", "));return X.join(`
|
|
332
|
+
`)}class k{config;compressor;summarizer;pendingRepo;observationRepo;sessionRepo;summaryRepo;embeddingModel;processing=!1;timer=null;mode="in-process";onEnqueue=null;constructor(J,X,Z,$,Q,H,V,Y=null){this.config=J;this.compressor=X;this.summarizer=Z;this.pendingRepo=$;this.observationRepo=Q;this.sessionRepo=H;this.summaryRepo=V;this.embeddingModel=Y}setMode(J){if(this.mode=J,J==="enqueue-only")this.stop()}getMode(){return this.mode}setOnEnqueue(J){this.onEnqueue=J}enqueue(J,X,Z,$){if(this.pendingRepo.create({sessionId:J,toolName:X,toolOutput:Z,callId:$}),this.mode==="enqueue-only")this.onEnqueue?.()}async processBatch(){if(this.mode==="enqueue-only")return 0;if(this.processing)return 0;this.processing=!0;let J=0;try{this.pendingRepo.resetStale(5);let X=this.pendingRepo.getPending(this.config.batchSize);if(X.length===0)return 0;for(let Z of X)try{this.pendingRepo.markProcessing(Z.id);let Q=await this.compressor.compress(Z.toolName,Z.toolOutput)??this.compressor.createFallbackObservation(Z.toolName,Z.toolOutput),H=this.observationRepo.create({sessionId:Z.sessionId,type:Q.type,title:Q.title,subtitle:Q.subtitle,facts:Q.facts,narrative:Q.narrative,concepts:Q.concepts,filesRead:Q.filesRead,filesModified:Q.filesModified,rawToolOutput:Z.toolOutput,toolName:Z.toolName,tokenCount:F(`${Q.title} ${Q.narrative} ${Q.facts.join(" ")}`),discoveryTokens:Q.discoveryTokens??F(Z.toolOutput)});if(this.embeddingModel)try{let V=a({title:H.title,narrative:H.narrative,concepts:H.concepts}),Y=await r(this.embeddingModel,V);if(Y)this.observationRepo.setEmbedding(H.id,Y)}catch{}this.sessionRepo.incrementObservationCount(Z.sessionId),this.pendingRepo.markCompleted(Z.id),J++}catch($){this.pendingRepo.markFailed(Z.id,String($))}return J}finally{this.processing=!1}}async summarizeSession(J){let X=this.observationRepo.getBySession(J);if(!this.summarizer.shouldSummarize(X.length))return;if(this.summaryRepo.getBySessionId(J))return;let $=await this.summarizer.summarize(J,X);if(!$)return;let Q=this.summaryRepo.create({sessionId:J,summary:$.summary,keyDecisions:$.keyDecisions,filesModified:$.filesModified,concepts:$.concepts,tokenCount:F($.summary)});this.sessionRepo.setSummary(J,Q.id)}start(){if(this.mode==="enqueue-only")return;if(this.timer)return;this.timer=setInterval(async()=>{try{await this.processBatch()}catch{}},this.config.batchIntervalMs)}stop(){if(this.timer)clearInterval(this.timer),this.timer=null}get isRunning(){return this.timer!==null}get isProcessing(){return this.processing}getStats(){return{pending:this.pendingRepo.getPending(1000).length,processing:this.processing}}}import{spawnSync as t}from"child_process";import{dirname as R0,resolve as e}from"path";function k0(J){try{let X=t("git",["rev-parse","--git-common-dir"],{cwd:J,encoding:"utf-8",timeout:5000});if(X.status!==0||!X.stdout)return null;let Z=X.stdout.trim();if(Z===".git")return null;let $=t("git",["rev-parse","--git-dir"],{cwd:J,encoding:"utf-8",timeout:5000});if($.status!==0||!$.stdout)return null;let Q=$.stdout.trim(),H=e(J,Z),V=e(J,Q);if(H===V)return null;let Y=R0(H);if(Y===H||Y==="/")return null;return Y}catch{return null}}function o(J){return k0(J)??J}var x0=5000,{values:T}=T0({options:{project:{type:"string",short:"p"},"poll-interval":{type:"string"}},strict:!1}),Z0=typeof T.project==="string"?T.project:null;if(!Z0)console.error("Usage: open-mem-daemon --project <path> [--poll-interval <ms>]"),process.exit(1);var J0=T["poll-interval"],x=typeof J0==="string"?Number.parseInt(J0,10):x0;if(Number.isNaN(x)||x<100)console.error("--poll-interval must be a number >= 100"),process.exit(1);var P0=o(Z0),B=g(P0);E.enableExtensionSupport();var L=i(B.dbPath);s(L,{hasVectorExtension:L.hasVectorExtension,embeddingDimension:B.embeddingDimension});var y0=new O(L),I0=new q(L),h0=new D(L),f0=new R(L),v0=new _(B),w0=new C(B),u0=B.provider!=="bedrock",b0=B.compressionEnabled&&(!u0||B.apiKey)?w({provider:B.provider,model:B.model,apiKey:B.apiKey}):null,g0=new k(B,v0,w0,y0,I0,h0,f0,b0),$0=m(B.dbPath);p($0);var P=new j({queueProcessor:g0,pollIntervalMs:x});if(process.send)process.on("message",(J)=>{P.handleMessage(J)});var X0=!1,y=()=>{if(X0)return;X0=!0,P.stop(),l($0),L.close()};process.on("SIGTERM",()=>{y(),process.exit(0)});process.on("SIGINT",()=>{y(),process.exit(0)});process.on("beforeExit",y);P.start();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:"DM Sans",ui-sans-serif,system-ui,sans-serif;--font-serif:"Instrument Serif",ui-serif,Georgia,serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:#fef2f2;--color-red-200:#fecaca;--color-red-400:#f87171;--color-red-500:#ef4444;--color-red-700:#b91c1c;--color-amber-50:#fffbeb;--color-amber-100:#fef3c7;--color-amber-200:#fde68a;--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:#fbbf24;--color-amber-500:#f59e0b;--color-amber-600:#d97706;--color-amber-700:#b45309;--color-emerald-50:#ecfdf5;--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:#a7f3d0;--color-emerald-400:#34d399;--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:#047857;--color-sky-50:#f0f9ff;--color-sky-200:#bae6fd;--color-sky-400:#38bdf8;--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-sky-700:#0369a1;--color-violet-50:#f5f3ff;--color-violet-200:#ddd6fe;--color-violet-400:#a78bfa;--color-violet-500:oklch(60.6% .25 292.717);--color-violet-600:oklch(54.1% .281 293.009);--color-violet-700:#6d28d9;--color-stone-50:#fafaf9;--color-stone-100:#f5f5f4;--color-stone-200:#e7e5e4;--color-stone-300:#d6d3d1;--color-stone-400:#a8a29e;--color-stone-500:#78716c;--color-stone-600:#57534e;--color-stone-700:#44403c;--color-stone-800:#292524;--color-stone-900:#1c1917;--color-stone-950:#0c0a09;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--leading-snug:1.375;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-1\/2{top:50%}.top-3{top:calc(var(--spacing)*3)}.right-2\.5{right:calc(var(--spacing)*2.5)}.right-3{right:calc(var(--spacing)*3)}.left-0{left:calc(var(--spacing)*0)}.left-4{left:calc(var(--spacing)*4)}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-2\.5{margin-top:calc(var(--spacing)*2.5)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-6{margin-top:calc(var(--spacing)*6)}.mr-3{margin-right:calc(var(--spacing)*3)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-1\.5{margin-bottom:calc(var(--spacing)*1.5)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-1\.5{margin-left:calc(var(--spacing)*1.5)}.ml-9{margin-left:calc(var(--spacing)*9)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.flex{display:flex}.grid{display:grid}.inline-flex{display:inline-flex}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-14{height:calc(var(--spacing)*14)}.h-16{height:calc(var(--spacing)*16)}.h-full{height:100%}.h-screen{height:100vh}.w-1{width:calc(var(--spacing)*1)}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing)*3)}.w-3\/4{width:75%}.w-4{width:calc(var(--spacing)*4)}.w-4\/5{width:80%}.w-5{width:calc(var(--spacing)*5)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-14{width:calc(var(--spacing)*14)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-28{width:calc(var(--spacing)*28)}.w-32{width:calc(var(--spacing)*32)}.w-36{width:calc(var(--spacing)*36)}.w-64{width:calc(var(--spacing)*64)}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-180{rotate:180deg}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-2\.5{gap:calc(var(--spacing)*2.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{column-gap:calc(var(--spacing)*3)}.gap-y-1{row-gap:calc(var(--spacing)*1)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-stone-50>:not(:last-child)){border-color:var(--color-stone-50)}:where(.divide-stone-100>:not(:last-child)){border-color:var(--color-stone-100)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-200{border-color:var(--color-amber-200)}.border-red-200{border-color:var(--color-red-200)}.border-stone-100{border-color:var(--color-stone-100)}.border-stone-200{border-color:var(--color-stone-200)}.border-stone-200\/80{border-color:#e7e5e4cc}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/80{border-color:color-mix(in oklab,var(--color-stone-200)80%,transparent)}}.border-stone-300{border-color:var(--color-stone-300)}.border-stone-800{border-color:var(--color-stone-800)}.border-t-amber-500{border-top-color:var(--color-amber-500)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/10{background-color:#f59e0b1a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-amber-500\/20{background-color:#f59e0b33}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/20{background-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.bg-black\/40{background-color:color-mix(in oklab,var(--color-black)40%,transparent)}}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/10{background-color:color-mix(in oklab,var(--color-emerald-500)10%,transparent)}}.bg-red-50{background-color:var(--color-red-50)}.bg-red-400{background-color:var(--color-red-400)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-400{background-color:var(--color-sky-400)}.bg-sky-500\/10{background-color:#00a5ef1a}@supports (color:color-mix(in lab,red,red)){.bg-sky-500\/10{background-color:color-mix(in oklab,var(--color-sky-500)10%,transparent)}}.bg-stone-50{background-color:var(--color-stone-50)}.bg-stone-50\/50{background-color:#fafaf980}@supports (color:color-mix(in lab,red,red)){.bg-stone-50\/50{background-color:color-mix(in oklab,var(--color-stone-50)50%,transparent)}}.bg-stone-100{background-color:var(--color-stone-100)}.bg-stone-200{background-color:var(--color-stone-200)}.bg-stone-300{background-color:var(--color-stone-300)}.bg-stone-400{background-color:var(--color-stone-400)}.bg-stone-900{background-color:var(--color-stone-900)}.bg-violet-50{background-color:var(--color-violet-50)}.bg-violet-400{background-color:var(--color-violet-400)}.bg-violet-500\/10{background-color:#8d54ff1a}@supports (color:color-mix(in lab,red,red)){.bg-violet-500\/10{background-color:color-mix(in oklab,var(--color-violet-500)10%,transparent)}}.bg-white{background-color:var(--color-white)}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-stone-50\/80{--tw-gradient-from:#fafaf9cc}@supports (color:color-mix(in lab,red,red)){.from-stone-50\/80{--tw-gradient-from:color-mix(in oklab,var(--color-stone-50)80%,transparent)}}.from-stone-50\/80{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-8{padding-inline:calc(var(--spacing)*8)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-3\.5{padding-block:calc(var(--spacing)*3.5)}.py-4{padding-block:calc(var(--spacing)*4)}.py-8{padding-block:calc(var(--spacing)*8)}.py-16{padding-block:calc(var(--spacing)*16)}.py-20{padding-block:calc(var(--spacing)*20)}.pt-2{padding-top:calc(var(--spacing)*2)}.pr-9{padding-right:calc(var(--spacing)*9)}.pr-10{padding-right:calc(var(--spacing)*10)}.pb-8{padding-bottom:calc(var(--spacing)*8)}.pl-3{padding-left:calc(var(--spacing)*3)}.pl-8{padding-left:calc(var(--spacing)*8)}.pl-12{padding-left:calc(var(--spacing)*12)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-serif{font-family:var(--font-serif)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-amber-400{color:var(--color-amber-400)}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-red-500{color:var(--color-red-500)}.text-red-700{color:var(--color-red-700)}.text-sky-600{color:var(--color-sky-600)}.text-sky-700{color:var(--color-sky-700)}.text-stone-100{color:var(--color-stone-100)}.text-stone-300{color:var(--color-stone-300)}.text-stone-400{color:var(--color-stone-400)}.text-stone-500{color:var(--color-stone-500)}.text-stone-600{color:var(--color-stone-600)}.text-stone-700{color:var(--color-stone-700)}.text-stone-800{color:var(--color-stone-800)}.text-stone-900{color:var(--color-stone-900)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-\[3px\]{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-amber-500\/5{--tw-shadow-color:#f59e0b0d}@supports (color:color-mix(in lab,red,red)){.shadow-amber-500\/5{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-amber-500)5%,transparent)var(--tw-shadow-alpha),transparent)}}.ring-amber-200{--tw-ring-color:var(--color-amber-200)}.ring-amber-200\/60{--tw-ring-color:#fde68a99}@supports (color:color-mix(in lab,red,red)){.ring-amber-200\/60{--tw-ring-color:color-mix(in oklab,var(--color-amber-200)60%,transparent)}}.ring-emerald-200{--tw-ring-color:var(--color-emerald-200)}.ring-red-200{--tw-ring-color:var(--color-red-200)}.ring-sky-200{--tw-ring-color:var(--color-sky-200)}.ring-stone-200{--tw-ring-color:var(--color-stone-200)}.ring-stone-200\/60{--tw-ring-color:#e7e5e499}@supports (color:color-mix(in lab,red,red)){.ring-stone-200\/60{--tw-ring-color:color-mix(in oklab,var(--color-stone-200)60%,transparent)}}.ring-violet-200{--tw-ring-color:var(--color-violet-200)}.ring-white{--tw-ring-color:var(--color-white)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.ring-inset{--tw-ring-inset:inset}@media(hover:hover){.group-hover\:text-stone-500:is(:where(.group):hover *){color:var(--color-stone-500)}.group-hover\:text-stone-950:is(:where(.group):hover *){color:var(--color-stone-950)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-stone-400::placeholder{color:var(--color-stone-400)}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:border-stone-300:hover{border-color:var(--color-stone-300)}.hover\:bg-stone-50:hover{background-color:var(--color-stone-50)}.hover\:bg-stone-100:hover{background-color:var(--color-stone-100)}.hover\:bg-stone-800:hover{background-color:var(--color-stone-800)}.hover\:text-stone-200:hover{color:var(--color-stone-200)}.hover\:text-stone-500:hover{color:var(--color-stone-500)}.hover\:text-stone-700:hover{color:var(--color-stone-700)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-stone-200\/50:hover{--tw-shadow-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.hover\:shadow-stone-200\/50:hover{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-stone-200)50%,transparent)var(--tw-shadow-alpha),transparent)}}.hover\:ring-stone-300\/80:hover{--tw-ring-color:#d6d3d1cc}@supports (color:color-mix(in lab,red,red)){.hover\:ring-stone-300\/80:hover{--tw-ring-color:color-mix(in oklab,var(--color-stone-300)80%,transparent)}}}.focus\:border-amber-300:focus{border-color:var(--color-amber-300)}.focus\:border-amber-400:focus{border-color:var(--color-amber-400)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-amber-200\/50:focus{--tw-ring-color:#fde68a80}@supports (color:color-mix(in lab,red,red)){.focus\:ring-amber-200\/50:focus{--tw-ring-color:color-mix(in oklab,var(--color-amber-200)50%,transparent)}}.focus\:ring-amber-400\/20:focus{--tw-ring-color:#fbbf2433}@supports (color:color-mix(in lab,red,red)){.focus\:ring-amber-400\/20:focus{--tw-ring-color:color-mix(in oklab,var(--color-amber-400)20%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:scale-\[0\.98\]:active{scale:.98}@media(min-width:40rem){.sm\:w-44{width:calc(var(--spacing)*44)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}.sm\:justify-between{justify-content:space-between}}@media(min-width:64rem){.lg\:static{position:static}.lg\:hidden{display:none}.lg\:translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.lg\:p-8{padding:calc(var(--spacing)*8)}}}body{font-family:var(--font-sans);background-color:var(--color-stone-50);color:var(--color-stone-900);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar-grain{position:relative}.sidebar-grain:after{content:"";opacity:.03;pointer-events:none;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");position:absolute;top:0;right:0;bottom:0;left:0}@keyframes timeline-fade-in{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.timeline-item-new{animation:.4s ease-out timeline-fade-in}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}
|