@aiagentmem/agentmem 0.1.3
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/LICENSE +21 -0
- package/README.md +89 -0
- package/dist/cli.js +108 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agentmem
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# agentmem · Coding-Agent Session Collector
|
|
2
|
+
|
|
3
|
+
`@aiagentmem/agentmem` — read-only collector for local coding-agent sessions across 5 sources: capture on-device → redact → HMAC-sign → upload to your session cloud. **Zero runtime dependencies.**
|
|
4
|
+
|
|
5
|
+
**Supports:** Claude Code · Codex · Cursor · Gemini · OpenCode
|
|
6
|
+
|
|
7
|
+
### 🌐 Website: **https://www.agentmem.cloud**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What it is
|
|
12
|
+
|
|
13
|
+
`agentmem` is a **read-only** collector for your local coding-agent sessions (messages / reasoning occurrences / tool calls / tool results / tokens / cost). It **redacts on-device** (secrets, tokens, connection passwords, JWTs, emails…), **HMAC-SHA256 signs**, and uploads to your server's `/ingest`. A local WAL plus a persistence-watermark reconcile make it **end-to-end lossless**.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# no install (try it first)
|
|
19
|
+
npx -y @aiagentmem/agentmem doctor
|
|
20
|
+
# or global (command: agentmem)
|
|
21
|
+
npm i -g @aiagentmem/agentmem
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
agentmem login # device-authorization login (defaults to https://api.agentmem.cloud)
|
|
28
|
+
# self-hosted: point to your own server → agentmem login --endpoint https://your-server
|
|
29
|
+
agentmem install # inject Claude Code hook (+ Codex notify if detected)
|
|
30
|
+
# For Cursor / Gemini / OpenCode (no-hook sources):
|
|
31
|
+
agentmem daemon # continuous capture
|
|
32
|
+
agentmem service install # auto-start at login
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Capture channel per agent
|
|
36
|
+
|
|
37
|
+
| Agent | Channel | Notes |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| Claude Code | hook (event-driven) | `install` injects a Stop/SessionEnd hook into `~/.claude/settings.json`; no daemon, survives reboot |
|
|
40
|
+
| Codex | notify hook | `install` idempotently adds `notify` to `~/.codex/config.toml`; captures at turn end, no daemon |
|
|
41
|
+
| Cursor / Gemini / OpenCode | daemon (file watch) | needs a running `agentmem daemon`; use `service install` for auto-start |
|
|
42
|
+
|
|
43
|
+
## Commands
|
|
44
|
+
|
|
45
|
+
| Command | Description |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `daemon` | Continuous capture (watch + poll → local WAL → upload) |
|
|
48
|
+
| `flush [--transcript F\|--latest\|--session S\|--stdin]` | One-shot incremental capture + drain (hooks use `--stdin`) |
|
|
49
|
+
| `login --endpoint <url>` | Device-authorization login |
|
|
50
|
+
| `install [--local]` | Inject the Claude hook (+ Codex notify when detected) |
|
|
51
|
+
| `uninstall` | Remove our hook (incl. Codex notify) |
|
|
52
|
+
| `service <install\|uninstall\|status>` | Service / auto-start (launch daemon at login) |
|
|
53
|
+
| `status` | Local queue / cursor / credentials |
|
|
54
|
+
| `doctor` | One-shot health check |
|
|
55
|
+
| `upload --file <jsonl> [--dry-run]` | Manual full upload (debugging) |
|
|
56
|
+
| `mcp [--print-config]` | Team-memory MCP server (recall for Claude Code/Codex) |
|
|
57
|
+
| `replay-dead` | Requeue local dead-letter events |
|
|
58
|
+
| `codex-notify [<json>]` | Codex notify hook entry (usually invoked by Codex) |
|
|
59
|
+
| `update` | Upgrade to the latest version |
|
|
60
|
+
| `-v` / `--version` / `version` | Print version |
|
|
61
|
+
| `-h` / `-help` / `--help` | Show help |
|
|
62
|
+
|
|
63
|
+
## Service / auto-start (needed for daemon-based sources)
|
|
64
|
+
|
|
65
|
+
All user-level, no admin/sudo:
|
|
66
|
+
|
|
67
|
+
- **Windows** — `schtasks` logon task `agentmem-daemon`
|
|
68
|
+
- **macOS** — `launchd` LaunchAgent (`RunAtLoad` + `KeepAlive`)
|
|
69
|
+
- **Linux** — `systemd --user` unit; to persist without an active login run `loginctl enable-linger $USER` once
|
|
70
|
+
|
|
71
|
+
## Design
|
|
72
|
+
|
|
73
|
+
- **Read-only & non-intrusive** — never modifies agent files or workflows.
|
|
74
|
+
- **Client-side redaction** — masks secrets / tokens / connection passwords / JWTs / emails before upload, with a report (rules hit + counts) in `meta.redaction`. Best-effort, not a guarantee; the server can re-check.
|
|
75
|
+
- **Reasoning content not exposed** — only its occurrence and tokens are recorded.
|
|
76
|
+
- **Machine auth** — HMAC-SHA256; tenant/user identity is bound server-side by the credential, so the client never carries a tenant.
|
|
77
|
+
- **Idempotent** — `(client_id, session_id, seq)` is unique; duplicate uploads return `accepted=0`.
|
|
78
|
+
- **End-to-end lossless** — local WAL + server persistence watermark; entries drop only after the server confirms they are stored. Failed items can be re-queued via `replay-dead`.
|
|
79
|
+
- **Zero runtime dependencies** — run the compiled `dist` on Node ≥ 20, or the `.ts` source directly on Node ≥ 23.6 (native type-stripping).
|
|
80
|
+
|
|
81
|
+
## Environment variables
|
|
82
|
+
|
|
83
|
+
| Variable | Description |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `AGENTMEM_LANG` | `en` / `zh` to force the CLI & log language (default: OS locale) |
|
|
86
|
+
| `AGENTMEM_ENDPOINT` / `AGENTMEM_ACCESS_KEY` / `AGENTMEM_ACCESS_SECRET` | Credentials via environment (CI / headless); otherwise stored in `~/.agentmem/credentials.json` after `login` |
|
|
87
|
+
| `AGENTMEM_CLAUDE_DIR` / `AGENTMEM_CODEX_DIR` / `AGENTMEM_CURSOR_DIR` / `AGENTMEM_GEMINI_DIR` / `AGENTMEM_OPENCODE_DIR` / `AGENTMEM_EXTRA_DIRS` | Override each source's session directory |
|
|
88
|
+
|
|
89
|
+
Local state lives under `~/.agentmem/` (client id, credentials, WAL, cursors).
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{fileURLToPath as hr}from"node:url";import{existsSync as L}from"node:fs";import{spawnSync as wr}from"node:child_process";import{readFileSync as so,statSync as It,openSync as ro,readSync as io,closeSync as ao}from"node:fs";import{basename as co}from"node:path";var _t=4e3,lo=16*1024*1024;function Rt(e){return e.customTitle??e.aiTitle??null}function Ct(e){let t=co(e);return t.endsWith(".jsonl")?t.slice(0,-6):t}function uo(e){if(!e||typeof e!="object")return"";let t=e,n=t.file_path??t.notebook_path??t.filePath??t.path;if(typeof n=="string"&&n)return n.slice(0,300);let o=t.command??t.cmd??t.script;if(typeof o=="string"&&o)return o.slice(0,200);let s=t.pattern??t.query;return typeof s=="string"&&s?s.slice(0,120):""}function fo(e){if(typeof e=="string")return e.slice(0,300);if(Array.isArray(e)){let t=[];for(let n of e)n&&typeof n=="object"&&n.type==="text"&&t.push(String(n.text??""));return t.join(`
|
|
3
|
+
`).slice(0,300)}return""}function po(e){if(typeof e=="string")return e;if(Array.isArray(e)){let t=[];for(let n of e)n&&typeof n=="object"&&n.type==="text"&&t.push(String(n.text??""));return t.join(`
|
|
4
|
+
`)}return""}function mo(e,t){if(typeof e=="string")return{preview:e.slice(0,_t),raw:e};if(!Array.isArray(e))return{preview:"",raw:""};let n=[],o=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.type;if(r==="text"){let a=String(s.text??"");n.push(a),o.push(a)}else if(r==="thinking"||r==="redacted_thinking"){t.kind="reasoning";let a=String(s.thinking??"");a&&o.push(a)}else if(r==="tool_use"){t.kind="tool_call";let a=s.name??"unknown",i=s.input,c=uo(i);n.push(c?`[tool_call:${a}] ${c}`:`[tool_call:${a}]`);let l="";try{l=JSON.stringify(i??"")}catch{}o.push(`[tool_call:${a}] ${l}`)}else if(r==="tool_result"){t.kind="tool_result";let a=s.content;n.push(`[tool_result] ${fo(a)}`),o.push(`[tool_result] ${po(a)}`)}}return{preview:n.join(`
|
|
5
|
+
`).slice(0,_t).trim(),raw:o.join(`
|
|
6
|
+
`).trim()}}function At(e,t,n){let o=e.trim();if(!o)return null;let s;try{s=JSON.parse(o)}catch{return null}let r=s.type;if(r==="custom-title")return typeof s.customTitle=="string"&&(n.customTitle=s.customTitle),null;if(r==="ai-title")return typeof s.aiTitle=="string"&&(n.aiTitle=s.aiTitle),null;if(r!=="user"&&r!=="assistant")return null;let a=s.message;if(!a||typeof a!="object")return null;let i=a;typeof s.sessionId=="string"&&(n.sessionId=s.sessionId),typeof s.cwd=="string"&&(n.project=s.cwd);let c=r==="assistant"?"assistant":"user",l={kind:"message"},d=mo(i.content,l),p=i.usage&&typeof i.usage=="object"?i.usage:{},y=typeof i.model=="string"?i.model:null;return y&&(n.lastModel=y),{sessionId:n.sessionId,seq:t,role:c,eventKind:l.kind,model:c==="assistant"?y??n.lastModel:null,project:n.project,ts:typeof s.timestamp=="string"?s.timestamp:null,text:d.preview,rawText:d.raw,tokenIn:Number(p.input_tokens??0)||0,tokenOut:Number(p.output_tokens??0)||0,meta:{uuid:s.uuid??null,parentUuid:s.parentUuid??null,isSidechain:!!s.isSidechain,isMeta:!!s.isMeta,gitBranch:s.gitBranch??null,version:s.version??null,stopReason:i.stop_reason??null,cacheRead:Number(p.cache_read_input_tokens??0)||0,cacheCreation:Number(p.cache_creation_input_tokens??0)||0}}}function Nt(e){let t=so(e,"utf8").split(/\r?\n/),n={sessionId:Ct(e),project:null,lastModel:null,customTitle:null,aiTitle:null},o=[];for(let r=0;r<t.length;r++){let a=At(t[r],r,n);a&&o.push(a)}let s=Rt(n);if(s)for(let r of o)r.title=s;return{sessionId:n.sessionId,project:n.project,events:o}}function ye(e){let t=It(e);return`${t.dev}-${t.ino}`}function Tt(e,t,n,o){let s=It(e),r=s.size,a=`${s.dev}-${s.ino}`,i=t,c=n;i>r&&(i=0,c=0);let l={sessionId:o||Ct(e),project:null,lastModel:null,customTitle:null,aiTitle:null},d=[];if(r<=i)return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:i,lineNo:c,fileSize:r,hasMore:!1};let p=r-i,y=Math.min(p,lo),h=Buffer.alloc(y),m=ro(e,"r");try{io(m,h,0,y,i)}finally{ao(m)}let b=h.lastIndexOf(10);if(b<0){if(y<p){let v=i+y;return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:v,lineNo:c,fileSize:r,hasMore:v<r}}return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:i,lineNo:c,fileSize:r,hasMore:!1}}let S=h.subarray(0,b+1),k=S.toString("utf8").split(/\r?\n/);k.length&&k[k.length-1]===""&&k.pop();for(let v=0;v<k.length;v++){let J=At(k[v],c+v,l);J&&d.push(J)}let w=Rt(l);if(w)for(let v of d)v.title=w;let f=i+S.length;return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:f,lineNo:c+k.length,fileSize:r,hasMore:f<r}}import{readFileSync as go,statSync as yo,openSync as ho,readSync as wo,closeSync as ko}from"node:fs";import{basename as bo}from"node:path";var So=4e3,vo=16*1024*1024,xo=/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;function Eo(e){let t=xo.exec(e);if(t)return t[1];let n=bo(e);return n.endsWith(".jsonl")?n.slice(0,-6):n}var _o=(e,t=So)=>String(e??"").slice(0,t);function te(e,t,n,o,s,r){let a=String(s??"");return{sessionId:e.sessionId,seq:t,role:n,eventKind:o,model:n==="assistant"&&o==="message"?e.lastModel:null,project:e.project,ts:e.ts,text:_o(a),rawText:a,tokenIn:0,tokenOut:0,meta:{role:n,eventKind:o,agent:"codex",...r}}}function jt(e,t,n,o){let s=e.trim();if(!s)return;let r;try{r=JSON.parse(s)}catch{return}let a=r.type,i=r.payload&&typeof r.payload=="object"?r.payload:{};if(n.ts=typeof r.timestamp=="string"?r.timestamp:n.ts,a==="session_meta"){typeof i.id=="string"&&(n.sessionId=i.id),typeof i.cwd=="string"&&(n.project=i.cwd);return}if(a==="turn_context"){typeof i.model=="string"&&(n.lastModel=i.model),typeof i.cwd=="string"&&(n.project=i.cwd);return}if(a==="event_msg"){let c=i.type;if(c==="token_count"){let l=i.info&&typeof i.info=="object"?i.info:{},d=l.last_token_usage&&typeof l.last_token_usage=="object"?l.last_token_usage:{};n.lastAssistant&&(n.lastAssistant.tokenIn=Number(d.input_tokens??0)||0,n.lastAssistant.tokenOut=Number(d.output_tokens??0)||0);return}if(c==="user_message"){o.push(te(n,t,"user","message",String(i.message??""),{turnId:r.turn_id??null}));return}if(c==="agent_message"){let l=te(n,t,"assistant","message",String(i.message??""),{phase:i.phase??null});o.push(l),n.lastAssistant=l;return}return}if(a==="response_item"){let c=i.type;if(c==="reasoning"){o.push(te(n,t,"assistant","reasoning","",{encrypted:!0}));return}if(c==="function_call"){let l=i.name??"tool";o.push(te(n,t,"assistant","tool_call",`[tool_call:${l}] ${String(i.arguments??"")}`,{name:l,callId:i.call_id??null}));return}if(c==="function_call_output"){let l=typeof i.output=="string"?i.output:JSON.stringify(i.output??"");o.push(te(n,t,"tool","tool_result",`[tool_result] ${l}`,{callId:i.call_id??null}));return}if(c==="web_search_call"){let l=i.action&&typeof i.action=="object"?i.action:{};o.push(te(n,t,"assistant","tool_call",`[tool_call:web_search] ${String(l.query??"")}`,{name:"web_search"}));return}return}}function Mt(e,t){return{sessionId:t||Eo(e),project:null,lastModel:null,ts:null,lastAssistant:null}}function Ot(e){let t=go(e,"utf8").split(/\r?\n/),n=Mt(e),o=[];for(let s=0;s<t.length;s++)jt(t[s],s,n,o);return{sessionId:n.sessionId,project:n.project,events:o}}function $t(e,t,n,o){let s=yo(e),r=s.size,a=`${s.dev}-${s.ino}`,i=t,c=n;i>r&&(i=0,c=0);let l=Mt(e,o),d=[];if(r<=i)return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:i,lineNo:c,fileSize:r,hasMore:!1};let p=r-i,y=Math.min(p,vo),h=Buffer.alloc(y),m=ho(e,"r");try{wo(m,h,0,y,i)}finally{ko(m)}let b=h.lastIndexOf(10);if(b<0){if(y<p){let f=i+y;return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:f,lineNo:c,fileSize:r,hasMore:f<r}}return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:i,lineNo:c,fileSize:r,hasMore:!1}}let S=h.subarray(0,b+1),k=S.toString("utf8").split(/\r?\n/);k.length&&k[k.length-1]===""&&k.pop();for(let f=0;f<k.length;f++)jt(k[f],c+f,l,d);let w=i+S.length;return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:w,lineNo:c+k.length,fileSize:r,hasMore:w<r}}import{readFileSync as Io,statSync as Ro,openSync as Co,readSync as Ao,closeSync as No}from"node:fs";import{basename as To}from"node:path";var Dt=4e3,jo=16*1024*1024,Mo=/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;function Oo(e){let t=Mo.exec(e);if(t)return t[1];let n=To(e);return n.endsWith(".jsonl")?n.slice(0,-6):n}function $o(e){let t=e.split(/[\\/]/),n=t.lastIndexOf("agent-transcripts");if(n>0)return t[n-1]||null;let o=t.lastIndexOf("projects");return o>=0&&t[o+1]?t[o+1]:null}function Do(e){if(typeof e=="string")return e.slice(0,300);if(Array.isArray(e)){let t=[];for(let n of e)n&&typeof n=="object"&&n.type==="text"&&t.push(String(n.text??""));return t.join(`
|
|
7
|
+
`).slice(0,300)}return""}function Po(e){if(typeof e=="string")return e;if(Array.isArray(e)){let t=[];for(let n of e)n&&typeof n=="object"&&n.type==="text"&&t.push(String(n.text??""));return t.join(`
|
|
8
|
+
`)}return""}function Lo(e,t){if(typeof e=="string")return{preview:e.slice(0,Dt),raw:e};if(!Array.isArray(e))return{preview:"",raw:""};let n=[],o=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.type;if(r==="text"){let a=String(s.text??"");n.push(a),o.push(a)}else if(r==="thinking"||r==="redacted_thinking"){t.kind="reasoning";let a=String(s.thinking??"");a&&o.push(a)}else if(r==="tool_use"){t.kind="tool_call";let a=s.name??"unknown";n.push(`[tool_call:${a}]`);let i="";try{i=JSON.stringify(s.input??"")}catch{}o.push(`[tool_call:${a}] ${i}`)}else if(r==="tool_result"){t.kind="tool_result";let a=s.content;n.push(`[tool_result] ${Do(a)}`),o.push(`[tool_result] ${Po(a)}`)}}return{preview:n.join(`
|
|
9
|
+
`).slice(0,Dt).trim(),raw:o.join(`
|
|
10
|
+
`).trim()}}function Pt(e,t,n){let o=e.trim();if(!o)return null;let s;try{s=JSON.parse(o)}catch{return null}for(let m of["sessionId","conversationId","conversation_id"])typeof s[m]=="string"&&s[m]&&(n.sessionId=s[m]);for(let m of["cwd","workspaceRoot","workspace_root","projectRoot"])typeof s[m]=="string"&&s[m]&&(n.project=s[m]);let r=(typeof s.type=="string"?s.type:"")||(typeof s.role=="string"?s.role:"");if(r!=="user"&&r!=="assistant")return null;let a=r==="assistant"?"assistant":"user",i=s.message&&typeof s.message=="object"?s.message:null,c=i?i.content:s.content??s.text,l={kind:"message"},d=Lo(c,l);if(!d.preview&&!d.raw&&l.kind==="message")return null;let p=i&&i.usage&&typeof i.usage=="object"?i.usage:{},y=(i&&typeof i.model=="string"?i.model:null)??(typeof s.model=="string"?s.model:null);y&&(n.lastModel=y);let h=typeof s.timestamp=="string"?s.timestamp:typeof s.ts=="string"?s.ts:null;return{sessionId:n.sessionId,seq:t,role:a,eventKind:l.kind,model:a==="assistant"?y??n.lastModel:null,project:n.project,ts:h,text:d.preview,rawText:d.raw,tokenIn:Number(p.input_tokens??0)||0,tokenOut:Number(p.output_tokens??0)||0,meta:{role:a,eventKind:l.kind,agent:"cursor",workspaceSlug:n.workspaceSlug,uuid:s.uuid??s.id??null,stopReason:(i&&i.stop_reason)??null,cacheRead:Number(p.cache_read_input_tokens??0)||0,cacheCreation:Number(p.cache_creation_input_tokens??0)||0}}}function Lt(e,t){return{sessionId:t||Oo(e),project:null,workspaceSlug:$o(e),lastModel:null}}function Ft(e){let t=Io(e,"utf8").split(/\r?\n/),n=Lt(e),o=[];for(let s=0;s<t.length;s++){let r=Pt(t[s],s,n);r&&o.push(r)}return{sessionId:n.sessionId,project:n.project,events:o}}function zt(e,t,n,o){let s=Ro(e),r=s.size,a=`${s.dev}-${s.ino}`,i=t,c=n;i>r&&(i=0,c=0);let l=Lt(e,o),d=[];if(r<=i)return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:i,lineNo:c,fileSize:r,hasMore:!1};let p=r-i,y=Math.min(p,jo),h=Buffer.alloc(y),m=Co(e,"r");try{Ao(m,h,0,y,i)}finally{No(m)}let b=h.lastIndexOf(10);if(b<0){if(y<p){let f=i+y;return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:f,lineNo:c,fileSize:r,hasMore:f<r}}return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:i,lineNo:c,fileSize:r,hasMore:!1}}let S=h.subarray(0,b+1),k=S.toString("utf8").split(/\r?\n/);k.length&&k[k.length-1]===""&&k.pop();for(let f=0;f<k.length;f++){let v=Pt(k[f],c+f,l);v&&d.push(v)}let w=i+S.length;return{events:d,sessionId:l.sessionId,project:l.project,inode:a,byteOffset:w,lineNo:c+k.length,fileSize:r,hasMore:w<r}}import{readFileSync as Fo,statSync as zo}from"node:fs";import{basename as Ko}from"node:path";var tt=4e3;function et(e){let t=e.split(/[\\/]/).filter(Boolean),n=t.lastIndexOf("tmp");return n>=0&&t[n+1]?`gemini-${t[n+1]}`:`gemini-${Ko(e).replace(/\.json$/i,"")}`}function Uo(e){if(Array.isArray(e))return e.filter(t=>t&&typeof t=="object");if(e&&typeof e=="object"){let t=e;for(let n of["messages","history","chunks","entries","turns"])if(Array.isArray(t[n]))return t[n].filter(o=>o&&typeof o=="object")}return[]}function Go(e,t,n){let o=[],s=[];for(let r of e){if(!r||typeof r!="object")continue;let a=r;if(typeof a.text=="string")o.push(a.text),s.push(a.text);else if(a.functionCall&&typeof a.functionCall=="object"){t.kind="tool_call";let i=a.functionCall,c=i.name??"tool",l="";try{l=JSON.stringify(i.args??{})}catch{}o.push(`[tool_call:${c}]`),s.push(`[tool_call:${c}] ${l}`)}else if(a.functionResponse&&typeof a.functionResponse=="object"){t.kind="tool_result",n.role="tool";let i=a.functionResponse,c=i.name??"tool",l="";try{l=typeof i.response=="string"?i.response:JSON.stringify(i.response??{})}catch{}o.push(`[tool_result:${c}] ${l.slice(0,300)}`),s.push(`[tool_result:${c}] ${l}`)}}return{preview:o.join(`
|
|
11
|
+
`).slice(0,tt).trim(),raw:s.join(`
|
|
12
|
+
`).trim()}}function Wo(e,t,n){let o=e.content??e.text??e.message;if(typeof o=="string")return{preview:o.slice(0,tt),raw:o};if(Array.isArray(e.parts))return Go(e.parts,t,n);if(Array.isArray(o)){let s=[];for(let a of o)a&&typeof a=="object"&&a.type==="text"&&s.push(String(a.text??""));let r=s.join(`
|
|
13
|
+
`);return{preview:r.slice(0,tt),raw:r}}return{preview:"",raw:""}}function qo(e){let t=e.toLowerCase();return t==="user"||t==="human"?"user":t==="gemini"||t==="model"||t==="assistant"||t==="ai"?"assistant":t==="tool"||t==="function"||t==="function_response"||t==="tool_result"?"tool":t==="system"?"system":null}function Kt(e,t){let n;try{let a=Fo(e,"utf8").trim();if(!a)return{sessionId:t||et(e),project:null,events:[]};n=JSON.parse(a)}catch{return{sessionId:t||et(e),project:null,events:[]}}let o=Uo(n),s={sessionId:t||et(e),project:null,lastModel:null};if(n&&typeof n=="object"&&!Array.isArray(n)){let a=n;typeof a.sessionId=="string"&&a.sessionId&&(s.sessionId=a.sessionId);for(let i of["projectDir","cwd","project"])typeof a[i]=="string"&&a[i]&&(s.project=a[i])}let r=[];for(let a=0;a<o.length;a++){let i=o[a];for(let b of["sessionId","session_id"])typeof i[b]=="string"&&i[b]&&(s.sessionId=i[b]);for(let b of["projectDir","cwd","project"])typeof i[b]=="string"&&i[b]&&(s.project=i[b]);let c=(typeof i.type=="string"?i.type:"")||(typeof i.role=="string"?i.role:""),l=qo(c);if(!l)continue;let d={role:l},p={kind:"message"},y=Wo(i,p,d);if(!y.preview&&!y.raw)continue;let h=typeof i.model=="string"?i.model:null;h&&(s.lastModel=h);let m=typeof i.timestamp=="string"?i.timestamp:typeof i.ts=="string"?i.ts:null;r.push({sessionId:s.sessionId,seq:a,role:d.role,eventKind:p.kind,model:d.role==="assistant"&&p.kind==="message"?h??s.lastModel:null,project:s.project,ts:m,text:y.preview,rawText:y.raw,tokenIn:0,tokenOut:0,meta:{role:d.role,eventKind:p.kind,agent:"gemini",id:i.id??i.messageId??null}})}return{sessionId:s.sessionId,project:s.project,events:r}}function Ut(e){return Kt(e)}function Gt(e,t,n,o){let s=zo(e),r=s.size,a=`${s.dev}-${s.ino}`,i=n>0?n:0,c=Kt(e,o),l=c.events.filter(p=>p.seq>=i),d=c.events.length>0?c.events[c.events.length-1].seq+1:i;return{events:l,sessionId:c.sessionId,project:c.project,inode:a,byteOffset:r,lineNo:Math.max(i,d),fileSize:r,hasMore:!1}}import{readFileSync as Jo,readdirSync as Ho}from"node:fs";import{join as he,dirname as H,basename as B}from"node:path";var Bo=4e3,Xo=300;function ot(e){let t=H(e),n=H(t);if(B(n)==="message")return{storageRoot:H(n),sessionId:B(t)};let o=H(n);return B(o)==="part"?{storageRoot:H(o),sessionId:B(n)}:null}function Wt(e){try{let t=JSON.parse(Jo(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function qt(e){try{return Ho(e).filter(t=>t.toLowerCase().endsWith(".json")).sort()}catch{return[]}}function nt(e){let t=e.time;if(t&&typeof t=="object"){let o=t.created,s=Number(o);if(Number.isFinite(s))return s}let n=Number(e.created??e.timestamp);return Number.isFinite(n)?n:0}function Zo(e){let t=e.time;if(t&&typeof t=="object"){let n=t.completed,o=Number(n);if(Number.isFinite(o)&&o>0)return o}return null}function Yo(e){if(!e)return null;try{return new Date(e).toISOString()}catch{return null}}function Vo(e,t){let n=[],o=[],s=!1,r=!1,a=!1;for(let i of e){let c=String(i.type??"");if(c==="text"){let l=String(i.text??"");l&&(s=!0,n.push(l),o.push(l))}else if(c==="reasoning"){a=!0;let l=String(i.text??i.reasoning??"");l&&o.push(l)}else if(c==="tool"){r=!0;let l=String(i.tool??i.name??"tool"),d=i.state&&typeof i.state=="object"?i.state:{},p="",y="";try{p=typeof d.input=="string"?d.input:JSON.stringify(d.input??i.input??{})}catch{}try{y=typeof d.output=="string"?d.output:JSON.stringify(d.output??"")}catch{}n.push(`[tool:${l}] ${y.slice(0,Xo)}`),o.push(`[tool:${l}] input=${p}
|
|
14
|
+
output=${y}`)}}return t.kind=s?"message":r?"tool_call":a?"reasoning":"message",{preview:n.join(`
|
|
15
|
+
`).slice(0,Bo).trim(),raw:o.join(`
|
|
16
|
+
`).trim()}}function Qo(e,t,n,o){if(Array.isArray(o.parts))return o.parts.filter(a=>a&&typeof a=="object");let s=he(e,"part",t,n),r=[];for(let a of qt(s)){let i=Wt(he(s,a));i&&r.push(i)}return r}function es(e){let t=e.toLowerCase();return t==="assistant"||t==="model"||t==="ai"?"assistant":t==="tool"||t==="function"?"tool":t==="system"?"system":"user"}function st(e,t){let n=he(e,"message",t),s=qt(n).map(c=>c.replace(/\.json$/i,"")).map(c=>({id:c,data:Wt(he(n,`${c}.json`))})).filter(c=>c.data!==null);s.sort((c,l)=>nt(c.data)-nt(l.data)||c.id.localeCompare(l.id));let r=null,a=[],i=0;for(let c=0;c<s.length;c++){let l=s[c].data,d=es(String(l.role??"user")),p=c===s.length-1;if(!(d!=="assistant"||Zo(l)!==null||!p))break;let h={kind:"message",role:d},m=Qo(e,t,s[c].id,l),b=Vo(m,h),S=l.path&&typeof l.path=="object"?l.path:null,k=(S&&typeof S.cwd=="string"?S.cwd:null)??(typeof l.cwd=="string"?l.cwd:null)??(typeof l.directory=="string"?l.directory:null);k&&!r&&(r=k);let w=(typeof l.modelID=="string"?l.modelID:null)??(typeof l.model=="string"?l.model:null),f=l.tokens&&typeof l.tokens=="object"?l.tokens:{};i=c+1,!(!b.preview&&!b.raw)&&a.push({sessionId:t,seq:c,role:h.role,eventKind:h.kind,model:h.role==="assistant"&&h.kind==="message"?w:null,project:r,ts:Yo(nt(l)),text:b.preview,rawText:b.raw,tokenIn:Number(f.input??0)||0,tokenOut:Number(f.output??0)||0,meta:{role:h.role,eventKind:h.kind,agent:"opencode",messageId:s[c].id,providerID:l.providerID??null,modelID:w}})}return{sessionId:t,project:r,events:a,watermark:i}}function Jt(e){let t=e.toLowerCase().endsWith(".json")?e:he(e,"_.json"),n=ot(t);if(!n){let s=B(e),r=H(e);B(r)==="message"&&(n={storageRoot:H(r),sessionId:s})}if(!n)return{sessionId:B(e),project:null,events:[]};let o=st(n.storageRoot,n.sessionId);return{sessionId:o.sessionId,project:o.project,events:o.events}}var Ht=(e,t)=>{let n=process.env[e];return n===void 0?t:n==="1"||n.toLowerCase()==="true"},ts=[{name:"private_key_block",re:/-----BEGIN[ A-Z]*PRIVATE KEY-----[\s\S]*?-----END[ A-Z]*PRIVATE KEY-----/g,mask:"\xABPRIVATE_KEY\xBB"},{name:"aws_access_key",re:/\bAKIA[0-9A-Z]{16}\b/g,mask:"\xABAWS_KEY\xBB"},{name:"anthropic_key",re:/\bsk-ant-[A-Za-z0-9_\-]{20,}\b/g,mask:"\xABANTHROPIC_KEY\xBB"},{name:"openai_key",re:/\bsk-(?!ant-)[A-Za-z0-9_\-]{20,}\b/g,mask:"\xABOPENAI_KEY\xBB"},{name:"github_pat",re:/\bgh[pousr]_[A-Za-z0-9]{20,}\b/g,mask:"\xABGITHUB_PAT\xBB"},{name:"github_fine_pat",re:/\bgithub_pat_[A-Za-z0-9_]{22,}\b/g,mask:"\xABGITHUB_PAT\xBB"},{name:"gitlab_pat",re:/\bglpat-[A-Za-z0-9_\-]{16,}\b/g,mask:"\xABGITLAB_PAT\xBB"},{name:"sendgrid_key",re:/\bSG\.[A-Za-z0-9_\-]{16,}\.[A-Za-z0-9_\-]{16,}\b/g,mask:"\xABSENDGRID_KEY\xBB"},{name:"google_api_key",re:/\bAIza[0-9A-Za-z_\-]{35}\b/g,mask:"\xABGOOGLE_API_KEY\xBB"},{name:"slack_token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,mask:"\xABSLACK_TOKEN\xBB"},{name:"stripe_key",re:/\b(sk|rk)_(live|test)_[0-9A-Za-z]{16,}\b/g,mask:"\xABSTRIPE_KEY\xBB"},{name:"authorization_header",re:/\b(authorization)\b\s*[:=]\s*["']?(?:bearer|basic|digest|token)?\s*[A-Za-z0-9._\-+/=]{8,}/gi,mask:"$1: \xABREDACTED\xBB"},{name:"jwt",re:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g,mask:"\xABJWT\xBB"},{name:"bearer_token",re:/\b([Bb]earer)\s+[A-Za-z0-9._\-]{16,}\b/g,mask:"$1 \xABTOKEN\xBB"},{name:"db_url",re:/\b(postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^\s/:@]+:[^\s/@]+@[^\s]+/gi,mask:"$1://\xABREDACTED\xBB"},{name:"url_basic_auth",re:/\b(https?:\/\/[^\s:/@]+):[^\s:/@]+@/gi,mask:"$1:\xABREDACTED\xBB@"},{name:"npm_token",re:/\bnpm_[A-Za-z0-9]{36}\b/g,mask:"\xABNPM_TOKEN\xBB"},{name:"pypi_token",re:/\bpypi-[A-Za-z0-9_\-]{16,}/g,mask:"\xABPYPI_TOKEN\xBB"},{name:"hf_token",re:/\bhf_[A-Za-z0-9]{34,}\b/g,mask:"\xABHF_TOKEN\xBB"},{name:"google_oauth_refresh",re:/\b1\/\/[A-Za-z0-9_\-]{20,}/g,mask:"\xABGOOGLE_OAUTH_REFRESH\xBB"},{name:"gcp_sa_field",re:/("(?:private_key_id|client_email|private_key)"\s*:\s*)"(?:\\.|[^"\\])*"/gi,mask:'$1"\xABREDACTED\xBB"'},{name:"azure_sas_sig",re:/([?&]sig=)[A-Za-z0-9%]{10,}/gi,mask:"$1\xABREDACTED\xBB"},{name:"azure_account_key",re:/\b(AccountKey=)[A-Za-z0-9+/=]{10,}/gi,mask:"$1\xABREDACTED\xBB"},{name:"secret_kv",re:/\b([A-Za-z0-9_.\-]*(?:password|passwd|pwd|secret|token|api[_-]?key|apikey|access[_-]?key|client[_-]?secret|private[_-]?key|credential|passphrase)[A-Za-z0-9_.\-]*)\s*[=:]\s*["']?(?!«)[^\s"',;]{4,}/gi,mask:"$1=\xABREDACTED\xBB"},{name:"cn_idcard",re:/\b[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]\b/g,mask:"\xABIDCARD\xBB"},{name:"credit_card",re:/\b(?:\d{4}[ -]?){3}\d{4}\b/g,mask:"\xABCREDIT_CARD\xBB"},{name:"cn_phone",re:/(?<!\d)1[3-9]\d{9}(?!\d)/g,mask:"\xABPHONE\xBB"},{name:"email",re:/\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b/g,mask:"\xABEMAIL\xBB",enabled:()=>Ht("AGENTMEM_REDACT_EMAIL",!0)},{name:"private_ip",re:/\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b/g,mask:"\xABPRIVATE_IP\xBB",enabled:()=>Ht("AGENTMEM_REDACT_IP",!1)}];function we(e){let t=e??"",n=[],o={};for(let s of ts){if(s.enabled&&!s.enabled())continue;let r=t.match(s.re),a=r?r.length:0;a>0&&(t=t.replace(s.re,s.mask),n.push(s.name),o[s.name]=a)}return{text:t,rulesHit:n,counts:o}}function ns(e){return e?/[\u4e00-\u9fff]/.test(e)?"zh":"en":null}function ne(e){return e.split("\0").join("").replace(/\\u0000/gi,"")}function os(e){let t=ne(e).replace(/\s+/g," ").trim();return t?t.slice(0,200):null}var Bt=5*1024*1024,Xt=32*1024,ss=/^\s*<\/?(system-reminder|extremely_important|task-notification|local-command-(caveat|stdout|stderr)|command-(name|message|args)|bash-(input|stdout|stderr)|user-prompt-submit-hook|session-start-hook)\b/i;function rs(e){let t=(e??"").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi,"").replace(/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/gi,"").replace(/<EXTREMELY_IMPORTANT>[\s\S]*?<\/EXTREMELY_IMPORTANT>/gi,"").replace(/<task-notification>[\s\S]*?<\/task-notification>/gi,"").trim();if(!t||ss.test(t))return"";let n=t.slice(0,400);return/^Base directory for this skill:/i.test(n)||/SessionStart hook additional context/i.test(n)||/You have superpowers/i.test(n)||/\[SYSTEM NOTIFICATION - NOT USER INPUT\]/i.test(n)||/^Caveat: The messages below were generated by the user while running local commands/i.test(n)?"":t}function ke(e,t,n,o="claude-code"){let s=[],r={},a=0,i={};for(let c of e){let l=we(c.text);for(let m of l.rulesHit)r[m]=(r[m]??0)+l.counts[m],a+=l.counts[m];i[c.eventKind]=(i[c.eventKind]??0)+1;let d={role:c.role,eventKind:c.eventKind,...c.meta,redaction:{rulesHit:l.rulesHit,counts:l.counts}},p=c.meta.isSidechain?["sidechain"]:[],y=null;if(c.rawText&&c.rawText.length>c.text.length){let m=ne(we(c.rawText).text);m.length>Bt&&(m=m.slice(0,Bt)+`
|
|
17
|
+
\u2026[raw over 5MB truncated]`),y=m}let h=null;if(c.eventKind==="message"&&!c.meta.isMeta){let m=rs(ne(we(c.rawText??c.text).text));h=m?m.length>Xt?m.slice(0,Xt)+`
|
|
18
|
+
\u2026[content too long, truncated]`:m:null}s.push({clientId:t,agentType:o,surface:n,sessionId:c.sessionId,seq:c.seq,model:c.model,language:ns(l.text),project:c.project,title:c.title?ne(we(c.title).text):null,summary:os(l.text),content:h,tags:ne(JSON.stringify(p)),meta:ne(JSON.stringify(d)),ossKey:null,raw:y,tokenIn:c.tokenIn,tokenOut:c.tokenOut,ts:c.ts})}return{dtos:s,totalHits:a,ruleCounts:r,kindBreakdown:i}}import{hostname as Zt,homedir as oe}from"node:os";import{join as j,delimiter as is}from"node:path";import{readFileSync as as,writeFileSync as cs,mkdirSync as ls,existsSync as Yt}from"node:fs";import{randomUUID as us,createHash as ds}from"node:crypto";function se(){let e=j(oe(),".agentmem"),t=j(e,"client.json");try{if(Yt(t)){let o=JSON.parse(as(t,"utf8"));if(o&&typeof o.clientId=="string"&&o.clientId)return o.clientId}}catch{}let n=ds("sha256").update(`${Zt()}|${us()}`).digest("hex").slice(0,24);try{ls(e,{recursive:!0}),cs(t,JSON.stringify({clientId:n,host:Zt(),createdAt:new Date().toISOString()},null,2))}catch{}return n}function X(){return process.env.AGENTMEM_CLAUDE_DIR?process.env.AGENTMEM_CLAUDE_DIR:process.env.CLAUDE_CONFIG_DIR?j(process.env.CLAUDE_CONFIG_DIR,"projects"):j(oe(),".claude","projects")}function Z(){return process.env.AGENTMEM_CODEX_DIR?process.env.AGENTMEM_CODEX_DIR:process.env.CODEX_HOME?j(process.env.CODEX_HOME,"sessions"):j(oe(),".codex","sessions")}function re(){return process.env.AGENTMEM_CURSOR_DIR?process.env.AGENTMEM_CURSOR_DIR:j(oe(),".cursor","projects")}function ie(){return process.env.AGENTMEM_GEMINI_DIR?process.env.AGENTMEM_GEMINI_DIR:process.env.GEMINI_HOME?j(process.env.GEMINI_HOME,"tmp"):j(oe(),".gemini","tmp")}function ae(){return process.env.AGENTMEM_OPENCODE_DIR?process.env.AGENTMEM_OPENCODE_DIR:process.env.OPENCODE_DATA?j(process.env.OPENCODE_DATA,"storage"):process.env.XDG_DATA_HOME?j(process.env.XDG_DATA_HOME,"opencode","storage"):j(oe(),".local","share","opencode","storage")}function be(){let e=process.env.AGENTMEM_EXTRA_DIRS;if(!e)return[];let t=e.split(new RegExp(`[${is},]`)),n=new Set,o=[];for(let s of t){let r=s.trim();!r||n.has(r)||(n.add(r),Yt(r)&&o.push(r))}return o}import{createHmac as fs,randomUUID as ps}from"node:crypto";function F(e,t,n,o,s,r){let a=`${e}&${t}&${n}&${o}&${s}`;return fs("sha256",r).update(a,"utf8").digest("base64")}function z(){return ps().replace(/-/g,"")}function K(e){return Math.floor(e/1e3).toString()}var M="0.1.3",U=`agentmem/${M} (node ${process.versions.node}; ${process.platform})`;var u=e=>e;var Vt="/api/ingest/ingest",Y=class extends Error{},ce=class extends Error{},le=class extends Error{retryAfterMs;constructor(t,n=0){super(t),this.retryAfterMs=n}},Oe=0;function Qt(){return Oe}function ms(e){if(!e)return 0;let t=Number(e);if(Number.isFinite(t))return Math.max(0,t*1e3);let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,n-Date.now()):0}async function rt(e,t){let n=K(Date.now()),o=z(),s=F("POST",Vt,e.accessKey,n,o,e.accessSecret),r=new AbortController,a=setTimeout(()=>r.abort(),e.timeoutMs??1e4),i;try{i=await fetch(e.endpoint+Vt,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":U,accessKey:e.accessKey,timestamp:n,nonce:o,sign:s},body:JSON.stringify({events:t}),signal:r.signal})}catch(y){throw new Y(u(`Network/timeout: ${y instanceof Error?y.message:String(y)}`))}finally{clearTimeout(a)}let c=i.headers.get("date");if(c){let y=Date.parse(c);Number.isFinite(y)&&(Oe=y-Date.now())}if(i.status>=500)throw new Y(u(`Server error ${i.status}`));if(i.status===401||i.status===403){let y=Math.round(Oe/1e3);throw Math.abs(Oe)>12e4?new Y(u(`Auth failed ${i.status} (suspected clock drift ${y}s, exceeds 5-min signature window; will retry with backoff, please sync system clock)`)):new ce(u(`Auth failed ${i.status} (check credentials / clock drift ${y}s)`))}if(i.status===429)throw new le(u("Rate limited 429"),ms(i.headers.get("retry-after"))||6e4);let l=await i.text(),d;try{d=JSON.parse(l)}catch{throw new Y(u(`Non-JSON response (HTTP ${i.status}): ${l.slice(0,160)}`))}if(d.code===200)return d.result??{accepted:0};let p=d.message??"";throw/quota|exceed|limit/i.test(p)?new le(u(`Quota limited: ${p}`),5*6e4):new ce(u(`Ingest rejected code=${d.code} message=${p}`))}function gs(e){return new Promise(t=>setTimeout(t,e))}async function en(e,t,n=100,o=5){let s=0;for(let r=0;r<t.length;r+=n){let a=t.slice(r,r+n),i=0;for(;;)try{s+=(await rt(e,a)).accepted;break}catch(c){if(c instanceof Y&&i<o){i++;let l=500*2**(i-1);await gs(l+Math.floor(Math.random()*l));continue}throw c}}return s}import{readFileSync as Le,existsSync as Fe}from"node:fs";import{join as ze}from"node:path";import{homedir as ys}from"node:os";import{join as ue,dirname as nn}from"node:path";import{mkdirSync as it,writeFileSync as hs,readFileSync as on,renameSync as ws,existsSync as ks,openSync as sn,closeSync as rn,writeSync as an,fsyncSync as cn,statSync as ln,statfsSync as bs,rmSync as $e,copyFileSync as Ss,chmodSync as vs}from"node:fs";import{randomUUID as xs}from"node:crypto";import{execFileSync as tn}from"node:child_process";var C=ue(ys(),".agentmem");function O(e){return new Promise(t=>setTimeout(t,e))}function G(e){it(e,{recursive:!0})}function De(e){return G(C),ue(C,e)}function un(){let e=ue(C,"spool");return G(e),e}function de(){let e=ue(C,"logs");return G(e),e}function I(e,t){G(nn(e));let n=`${e}.tmp.${process.pid}.${xs().slice(0,8)}`,o=sn(n,"w");try{an(o,t),cn(o)}finally{rn(o)}ws(n,e)}function Pe(e,t){G(nn(e));let n=sn(e,"a");try{an(n,t),cn(n)}finally{rn(n)}}function Se(e){try{let t=bs(e);return Number(t.bavail)*Number(t.bsize)}catch{return-1}}function dn(e){try{return ln(e).size}catch{return 0}}function fn(e,t){I(e,t);try{if(process.platform==="win32"){tn("icacls",[e,"/inheritance:r"],{stdio:"ignore"});let n=process.env.USERNAME||process.env.USER;n&&tn("icacls",[e,"/grant:r",`${n}:F`],{stdio:"ignore"})}else vs(e,384)}catch{}}function V(e){if(!ks(e))return null;let t;try{t=on(e,"utf8")}catch{return null}if(t.charCodeAt(0)===65279&&(t=t.slice(1)),!t.trim())return null;try{return JSON.parse(t)}catch{try{Ss(e,`${e}.corrupt`)}catch{}return null}}async function W(e,t,n={}){let o=n.staleMs??3e4,s=n.retryMs??50,r=n.timeoutMs??15e3,a=Date.now();for(;;)try{it(e);break}catch{try{let i=ln(e);if(Date.now()-i.mtimeMs>o){$e(e,{recursive:!0,force:!0});continue}}catch{}if(Date.now()-a>r)throw new Error(u(`Lock acquisition timed out: ${e}`));await O(s)}try{return await t()}finally{try{$e(e,{recursive:!0,force:!0})}catch{}}}function Es(e){if(!e)return!1;try{return process.kill(e,0),!0}catch(t){return t?.code==="EPERM"}}function fe(e){try{let t=Number(on(ue(e,"pid"),"utf8").trim());return Es(t)}catch{return!1}}function pn(e){for(let t=0;t<2;t++)try{it(e);try{hs(ue(e,"pid"),String(process.pid))}catch{}return()=>{try{$e(e,{recursive:!0,force:!0})}catch{}}}catch{if(fe(e))return null;try{$e(e,{recursive:!0,force:!0});continue}catch{return null}}return null}function _s(e){return`${e.sessionId}:${e.seq}`}var $=class{outbox;ackedLog;deadFile;lock;map=new Map;acked=new Set;dead=new Set;constructor(){let t=un();this.outbox=ze(t,"outbox.jsonl"),this.ackedLog=ze(t,"acked.log"),this.deadFile=ze(t,"dead.jsonl"),this.lock=ze(t,"spool.lock"),this.load()}load(){if(Fe(this.ackedLog))for(let t of Le(this.ackedLog,"utf8").split(`
|
|
19
|
+
`)){let n=t.trim();n&&this.acked.add(n)}if(Fe(this.deadFile))for(let t of Le(this.deadFile,"utf8").split(`
|
|
20
|
+
`)){let n=t.trim();if(n)try{this.dead.add(JSON.parse(n).id)}catch{}}if(Fe(this.outbox))for(let t of Le(this.outbox,"utf8").split(`
|
|
21
|
+
`)){let n=t.trim();if(n)try{let o=JSON.parse(n);if(!o?.id||this.acked.has(o.id)||this.dead.has(o.id))continue;this.map.set(o.id,o)}catch{}}}async enqueue(t){let n=[];for(let o of t){let s=_s(o);this.map.has(s)||this.acked.has(s)||this.dead.has(s)||n.push({id:s,payload:o,enqueuedAt:Date.now(),attempts:0})}if(!n.length)return 0;await W(this.lock,()=>{Pe(this.outbox,n.map(o=>JSON.stringify(o)).join(`
|
|
22
|
+
`)+`
|
|
23
|
+
`)});for(let o of n)this.map.set(o.id,o);return n.length}pending(t,n=0){let o=Date.now(),s=[];for(let r of this.map.values())if(!(n>0&&r.sentAt&&o-r.sentAt<n)&&(s.push(r),s.length>=t))break;return s}markSent(t){let n=Date.now();for(let o of t){let s=this.map.get(o);s&&(s.sentAt=n)}}allEntries(){return[...this.map.values()]}async ack(t){let n=t.filter(o=>this.map.has(o));if(n.length){await W(this.lock,()=>{Pe(this.ackedLog,n.join(`
|
|
24
|
+
`)+`
|
|
25
|
+
`)});for(let o of n)this.acked.add(o),this.map.delete(o);this.acked.size>2e3&&this.acked.size>this.map.size*2&&await this.compact()}}async deadletter(t,n){let o=t.map(s=>this.map.get(s)).filter(s=>!!s);if(o.length){await W(this.lock,()=>{Pe(this.deadFile,o.map(s=>JSON.stringify({id:s.id,reason:n,at:Date.now(),payload:s.payload})).join(`
|
|
26
|
+
`)+`
|
|
27
|
+
`)});for(let s of o)this.dead.add(s.id),this.map.delete(s.id)}}async requeueDead(){if(!Fe(this.deadFile))return 0;let t=[],n=[];for(let s of Le(this.deadFile,"utf8").split(`
|
|
28
|
+
`)){let r=s.trim();if(r)try{let a=JSON.parse(r);a?.id&&a.payload&&(n.push(a.id),t.push(a.payload))}catch{}}for(let s of n)this.dead.delete(s);let o=t.length?await this.enqueue(t):0;return await W(this.lock,()=>{I(this.deadFile,"")}),o}markAttempt(t){for(let n of t){let o=this.map.get(n);o&&o.attempts++}}stats(){let t=0;if(this.map.size){let n=1/0;for(let o of this.map.values())o.enqueuedAt<n&&(n=o.enqueuedAt);t=n===1/0?0:Math.max(0,Date.now()-n)}return{pending:this.map.size,acked:this.acked.size,dead:this.dead.size,oldestMs:t,bytes:dn(this.outbox)}}async compact(){await W(this.lock,()=>{let t=[...this.map.values()].map(n=>JSON.stringify(n));I(this.outbox,t.length?t.join(`
|
|
29
|
+
`)+`
|
|
30
|
+
`:""),I(this.ackedLog,"")}),this.acked.clear()}};import{join as Is}from"node:path";var mn=()=>De("cursors.json"),Rs=()=>Is(C,"cursors.lock");function at(){return V(mn())??{}}function ct(e){return at()[e]??null}function gn(){return at()}async function lt(e){await W(Rs(),()=>{let t=at();t[e.sourcePath]={...e,updatedAt:Date.now()},I(mn(),JSON.stringify(t,null,2))})}var Cs=256;async function Ke(e,t,n,o,s,r,a){let i=ct(e),c=i?.byteOffset??0,l=i?.lineNo??0,d=i?.sessionId??"";try{let h=ye(e);i&&i.inode&&i.inode!==h&&(a.info(u("File rotated, rescanning"),{file:e}),c=0,l=0)}catch{}let p=0,y=0;for(let h=0;h<Cs;h++){let m;try{m=n(e,c,l,d||void 0)}catch(S){a.warn(u("Incremental parse failed, will retry next run"),{file:e,msg:S instanceof Error?S.message:String(S)});break}if(d=m.sessionId,m.events.length){let S=ke(m.events,o,s,t);p+=await r.enqueue(S.dtos),y+=m.events.length}await lt({sourcePath:e,agentType:t,sessionId:m.sessionId,inode:m.inode,byteOffset:m.byteOffset,lineNo:m.lineNo,fileSize:m.fileSize,updatedAt:Date.now()});let b=m.byteOffset>c;if(c=m.byteOffset,l=m.lineNo,!m.hasMore||!b)break}return p&&a.debug(u("Enqueued for collection"),{file:e,agent:t,events:y,enqueued:p}),{enqueued:p,events:y,sessionId:d}}function Q(e,t,n,o,s){return Ke(e,"claude-code",Tt,t,n,o,s)}function ee(e,t,n,o,s){return Ke(e,"codex",$t,t,n,o,s)}function ve(e,t,n,o,s){return Ke(e,"cursor",zt,t,n,o,s)}function xe(e,t,n,o,s){return Ke(e,"gemini",Gt,t,n,o,s)}var As="opencode:";async function Ns(e,t,n,o,s,r){let a=As+t,i=ct(a)?.lineNo??0,c;try{c=st(e,t)}catch(p){return r.warn(u("OpenCode assemble failed, will retry next run"),{sessionId:t,msg:p instanceof Error?p.message:String(p)}),{enqueued:0,events:0,sessionId:t}}let l=c.events.filter(p=>p.seq>=i),d=0;if(l.length){let p=ke(l,n,o,"opencode");d=await s.enqueue(p.dtos)}return await lt({sourcePath:a,agentType:"opencode",sessionId:t,inode:"",byteOffset:0,lineNo:Math.max(i,c.watermark),fileSize:0,updatedAt:Date.now()}),d&&r.debug(u("Enqueued for collection"),{session:t,agent:"opencode",events:l.length,enqueued:d}),{enqueued:d,events:l.length,sessionId:t}}function Ee(e,t,n,o,s){let r=ot(e);return r?Ns(r.storageRoot,r.sessionId,t,n,o,s):Promise.resolve({enqueued:0,events:0,sessionId:""})}var Ts=2*1024*1024,js=8;function Ms(e){try{return Buffer.byteLength(JSON.stringify(e),"utf8")}catch{return 0}}async function _e(e,t,n,o={}){let s=o.batchSize??100,r=o.maxWallMs??6e4,a=o.maxBackoffMs??3e5,i=Date.now(),c=0,l=0,d=0,p=500,y=(h,m)=>({sent:c,accepted:l,parked:h,parkedUntilMs:m,dead:d,remaining:t.stats().pending,driftMs:Qt()});for(;!(Date.now()-i>r);){let h=t.pending(s,15e3);if(!h.length)break;let m=[],b=0;for(let w of h){let f=Ms(w.payload);if(m.length>0&&b+f>Ts)break;m.push(w),b+=f}if(m.length===1&&m[0].attempts>=js){await t.deadletter([m[0].id],u(`Failed ${m[0].attempts} times in a row (suspected oversized/poison event, ${Math.round(b/1024)}KB), isolated to dead-letter`)),d+=1,n.error(u("Single event kept failing, isolated to dead-letter (avoids head-of-line blocking)"),{id:m[0].id,bytes:b});continue}let S=m.map(w=>w.id),k=Math.min(12e4,1e4+Math.floor(b/(200*1024))*1e3);try{let w=await rt({...e,timeoutMs:k},m.map(f=>f.payload));t.markSent(S),c+=S.length,l+=w.accepted,p=500,n.debug(u("Flush batch succeeded"),{sent:S.length,accepted:w.accepted})}catch(w){if(w instanceof le){let v=Date.now()+(w.retryAfterMs||3e5);return n.warn(u("Quota/rate limited, pausing upload (data safely cached)"),{msg:w.message,retryInMs:w.retryAfterMs}),y(!0,v)}if(w instanceof ce){await t.deadletter(S,w.message),d+=S.length,n.error(u("Batch not retriable, moved to dead-letter (manual replay possible)"),{count:S.length,msg:w.message});continue}t.markAttempt(S);let f=Math.min(p,a);if(n.warn(u("Upload failed, backing off for retry"),{waitMs:f,msg:w instanceof Error?w.message:String(w)}),Date.now()-i+f>r)break;await O(f+Math.floor(Math.random()*Math.min(p,1e3))),p=Math.min(p*2,a)}}return y(!1,0)}var ut=()=>De("credentials.json"),pe="https://api.agentmem.cloud";function N(){let e=process.env.AGENTMEM_ENDPOINT,t=process.env.AGENTMEM_ACCESS_KEY,n=process.env.AGENTMEM_ACCESS_SECRET,o=V(ut()),s=t||o?.accessKey,r=n||o?.accessSecret;return!s||!r?null:{endpoint:e||o?.endpoint||pe,accessKey:s,accessSecret:r}}function yn(e){fn(ut(),JSON.stringify({...e,savedAt:new Date().toISOString()},null,2))}function hn(){return ut()}import{appendFileSync as Os,statSync as bn,renameSync as $s,readdirSync as Ds,rmSync as Ps}from"node:fs";import{join as dt}from"node:path";var wn={debug:10,info:20,warn:30,error:40},Ls=5*1024*1024,Fs=14;function zs(){return new Date().toISOString().slice(0,10)}function Ks(e){try{let t=Date.now()-Fs*864e5;for(let n of Ds(e))if(!(!n.startsWith("agentmem-")||!n.includes(".log")))try{bn(dt(e,n)).mtimeMs<t&&Ps(dt(e,n),{force:!0})}catch{}}catch{}}var kn=!1;function Ie(e="agentmem",t="info",n=!0){let o=wn[t];kn||(kn=!0,Ks(de()));let s=(r,a,i)=>{if(wn[r]<o)return;let c={ts:new Date().toISOString(),level:r,scope:e,msg:a,...i??{}},l=JSON.stringify(c)+`
|
|
31
|
+
`;try{let d=dt(de(),`agentmem-${zs()}.log`);try{bn(d).size>Ls&&$s(d,d+".1")}catch{}Os(d,l)}catch{}n&&(r==="error"||r==="warn"?console.error:console.log)(`[${r}] ${a}${i?" "+JSON.stringify(i):""}`)};return{debug:(r,a)=>s("debug",r,a),info:(r,a)=>s("info",r,a),warn:(r,a)=>s("warn",r,a),error:(r,a)=>s("error",r,a)}}import{readdirSync as ft,statSync as Us}from"node:fs";import{join as Ue}from"node:path";function Sn(e,t){let n;try{n=ft(e,{withFileTypes:!0})}catch{return}for(let o of n){let s=Ue(e,o.name);o.isDirectory()?Sn(s,t):o.isFile()&&o.name.endsWith(".jsonl")&&t.push(s)}}function A(e){let t=[];return Sn(e,t),t}var Gs=new Set(["settings.json","oauth_creds.json","google_accounts.json","installation_id.json","user_id.json","package.json","mcp.json",".mcp.json"]);function vn(e,t){let n;try{n=ft(e,{withFileTypes:!0})}catch{return}for(let o of n){let s=Ue(e,o.name);o.isDirectory()?vn(s,t):o.isFile()&&o.name.toLowerCase().endsWith(".json")&&!Gs.has(o.name.toLowerCase())&&t.push(s)}}function me(e){let t=[];return vn(e,t),t}function xn(e,t){let n;try{n=ft(e,{withFileTypes:!0})}catch{return}for(let o of n){let s=Ue(e,o.name);o.isDirectory()?xn(s,t):o.isFile()&&o.name.toLowerCase().endsWith(".json")&&t.push(s)}}function ge(e){let t=[];return xn(Ue(e,"message"),t),t}function Ge(e){let t="",n=0;for(let o of A(e))try{let s=Us(o).mtimeMs;s>n&&(n=s,t=o)}catch{}return t}import{watch as qs,statSync as In}from"node:fs";import{hostname as Js}from"node:os";import{join as Rn}from"node:path";var En="/api/collector/heartbeat";async function pt(e,t,n,o,s){let r=K(Date.now()),a=z(),i=F("POST",En,e.accessKey,r,a,e.accessSecret),c=new AbortController,l=setTimeout(()=>c.abort(),e.timeoutMs??8e3);try{return(await fetch(e.endpoint+En,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":U,accessKey:e.accessKey,timestamp:r,nonce:a,sign:i},body:JSON.stringify({clientId:t,host:n,version:M,events24h:o,errors24h:s}),signal:c.signal})).ok}catch{return!1}finally{clearTimeout(l)}}var Re=class{buf=[];windowMs;constructor(t=24*36e5){this.windowMs=t}add(t){t>0&&this.buf.push({t:Date.now(),n:t})}sum(){let t=Date.now()-this.windowMs;this.buf=this.buf.filter(o=>o.t>=t);let n=0;for(let o of this.buf)n+=o.n;return n}};var _n="/api/ingest/watermark";async function Ws(e,t,n){let o=new Map;if(!n.length)return o;let s=K(Date.now()),r=z(),a=F("POST",_n,e.accessKey,s,r,e.accessSecret),i=new AbortController,c=setTimeout(()=>i.abort(),e.timeoutMs??1e4);try{let l=await fetch(e.endpoint+_n,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":U,accessKey:e.accessKey,timestamp:s,nonce:r,sign:a},body:JSON.stringify({clientId:t,sessionIds:n}),signal:i.signal});if(!l.ok)return o;let d=await l.json();for(let p of d?.result??[])o.set(p.sessionId,p.maxSeq)}catch{}finally{clearTimeout(c)}return o}async function Ce(e,t,n,o){let s=t.allEntries().filter(c=>c.sentAt);if(!s.length)return{acked:0,checked:0};let r=[...new Set(s.map(c=>c.payload.sessionId))],a=await Ws(e,n,r);if(!a.size)return{acked:0,checked:s.length};let i=[];for(let c of s){let l=a.get(c.payload.sessionId);l!==void 0&&c.payload.seq<=l&&i.push(c.id)}return i.length&&(await t.ack(i),o.debug(u("watermark ack (persistence confirmed)"),{acked:i.length})),{acked:i.length,checked:s.length}}async function Cn(e={}){let t=Ie("daemon"),n=pn(Rn(C,"daemon.lock"));if(!n){t.warn(u("daemon already running, exiting"));return}process.on("uncaughtException",g=>t.error(u("uncaughtException (ignored, process continues)"),{msg:g?.message??String(g)})),process.on("unhandledRejection",g=>t.error(u("unhandledRejection (ignored)"),{msg:g instanceof Error?g.message:String(g)}));let o=se(),s=Js(),r=e.surface??"cli",a=e.pollMs??3e3,i=e.watchdogMs??3e4,c=e.heartbeatMs??6e4,l=new $,d=N();d||t.warn(u("No credentials (run `agentmem login`); capturing to local spool, will backfill after authorization"));let p=g=>{let x=g.toLowerCase().split(/[\\/]/).pop()??"";return x.includes("rollout")||/^rollout-.*\.jsonl$/.test(x)},y=(g,x,E,D,P)=>(p(g)?ee:Q)(g,x,E,D,P),h=[{dir:X(),collect:Q},{dir:Z(),collect:ee},{dir:re(),collect:ve},{dir:ie(),collect:xe,list:me,watchExt:".json"},{dir:ae(),collect:Ee,list:ge,watchExt:".json",accept:g=>/[\\/]message[\\/]/.test(g)}];for(let g of be())h.push({dir:g,collect:y});let m=g=>(g.list??A)(g.dir),b=g=>{let x=g.toLowerCase(),E;for(let D of h)x.startsWith(D.dir.toLowerCase())&&(!E||D.dir.length>E.dir.length)&&(E=D);return E?E.collect:Q},S=new Re,k=new Re,w=new Map,f=new Map,v=!1,J=!1,Ze=0,je=async()=>{if(d??=N(),!(!d||v)&&!(Date.now()<Ze)){v=!0;try{let g=await _e(d,l,t,{maxWallMs:25e3});g.dead&&k.add(g.dead),g.parked&&(Ze=g.parkedUntilMs),(g.sent||g.parked||g.dead)&&t.info(u("flush"),{sent:g.sent,accepted:g.accepted,dead:g.dead,remaining:g.remaining}),Math.abs(g.driftMs)>12e4&&t.warn(u("Clock drift too large (5min signature window), sync NTP"),{driftSec:Math.round(g.driftMs/1e3)}),await Ce(d,l,o,t)}catch(g){k.add(1),t.error(u("drain error"),{msg:g instanceof Error?g.message:String(g)})}finally{v=!1}}},Ye=g=>{let x=w.get(g);x&&clearTimeout(x),w.set(g,setTimeout(()=>{w.delete(g),(async()=>{try{let E=await b(g)(g,o,r,l,t);E.events&&S.add(E.events),E.enqueued&&await je()}catch(E){k.add(1),t.error(u("capture error"),{file:g,msg:E instanceof Error?E.message:String(E)})}})()},200))};for(let g of h)for(let x of m(g)){try{f.set(x,In(x).mtimeMs)}catch{}Ye(x)}let Ve=[],vt=(g,x,E,D)=>{try{let P=qs(g,{recursive:!0},(Me,Et)=>{if(!Et)return;let Qe=Rn(g,Et.toString());Qe.endsWith(E)&&(!D||D(Qe))&&Ye(Qe)});P.on("error",Me=>{t.warn(u("watch error, re-arming later (polling fallback active)"),{dir:g,msg:Me instanceof Error?Me.message:String(Me)});try{P.close()}catch{}Ve[x]=void 0,J||setTimeout(()=>vt(g,x,E,D),2e3)}),Ve[x]=P}catch(P){t.warn(u("fs.watch unavailable, this source uses polling only"),{dir:g,msg:P instanceof Error?P.message:String(P)})}};h.forEach((g,x)=>vt(g.dir,x,g.watchExt??".jsonl",g.accept));let to=setInterval(()=>{for(let g of h)for(let x of m(g)){let E=0;try{E=In(x).mtimeMs}catch{continue}f.get(x)!==E&&(f.set(x,E),Ye(x))}},a),no=setInterval(()=>{let g=l.stats();t.debug(u("heartbeat"),{...g}),g.oldestMs>36e5&&t.warn(u("Backlog over 1 hour, check network/credentials/quota"),{pendingMin:Math.round(g.oldestMs/6e4),pending:g.pending});let x=Se(C);x>=0&&x<200*1024*1024&&t.warn(u("Free disk space below 200MB, may affect cache"),{freeMB:Math.round(x/1048576)}),je()},i),oo=setInterval(()=>{d??=N(),d&&pt(d,o,s,S.sum(),k.sum())},c),xt=async()=>{if(!J){J=!0,t.info(u("shutdown signal received, final drain")),clearInterval(to),clearInterval(no),clearInterval(oo);for(let g of Ve)try{g?.close()}catch{}for(let g of w.values())clearTimeout(g);Ze=0,await je(),n(),process.exit(0)}};for(process.on("SIGINT",()=>void xt()),process.on("SIGTERM",()=>void xt()),t.info(u("daemon started"),{sources:h.map(g=>g.dir),clientId:o,host:s,hasCreds:!!d}),d&&pt(d,o,s,0,0),await je();!J;)await O(1e3)}async function An(e){let t;try{t=await(await fetch(e+"/api/device/code",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"})).json()}catch(r){return{ok:!1,message:u(`Failed to connect to server: ${r instanceof Error?r.message:String(r)}`)}}let n=t.result;if(!n?.deviceCode||!n.userCode)return{ok:!1,message:u(`Failed to obtain device code: ${t.message??JSON.stringify(t).slice(0,160)}`)};console.log(`
|
|
32
|
+
========================================`),console.log(u(` Open in your browser: ${n.verificationUri}`)),console.log(u(` Enter the code: ${n.userCode}`)),console.log(u(" (This terminal will finish login automatically once authorized\u2026)")),console.log(`========================================
|
|
33
|
+
`);let o=(n.interval??5)*1e3,s=Date.now()+(n.expiresIn??600)*1e3;for(;Date.now()<s;){await O(o);let r;try{r=await(await fetch(e+"/api/device/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:n.deviceCode})})).json()}catch{continue}let a=r.result;if(a?.accessKey&&a.accessSecret)return yn({endpoint:e,accessKey:a.accessKey,accessSecret:a.accessSecret}),{ok:!0,message:u("Login succeeded; credentials saved securely (tenant/user bound by the server).")};if(a?.status==="expired")return{ok:!1,message:u("Authorization code expired; please run login again.")};if(a?.status==="denied")return{ok:!1,message:u("Authorization denied.")}}return{ok:!1,message:u("Timed out waiting for authorization; please retry.")}}import{homedir as Hs}from"node:os";import{join as Bs}from"node:path";var Tn="agentmem";function jn(){return Bs(Hs(),".claude","settings.json")}function Nn(e){return e.some(t=>Array.isArray(t.hooks)&&t.hooks.some(n=>typeof n.command=="string"&&n.command.includes(Tn)))}function mt(e="npx -y @aiagentmem/agentmem flush --stdin"){let t=e.replace(/flush\s+--stdin/,"recall-hook"),n=jn(),o=V(n)??{};o.hooks??={};let s=!1;for(let r of["Stop","SessionEnd"]){let a=o.hooks[r]??=[];Nn(a)||(a.push({hooks:[{type:"command",command:e,async:!0}]}),s=!0)}if(t!==e)for(let r of["SessionStart","UserPromptSubmit"]){let a=o.hooks[r]??=[];Nn(a)||(a.push({hooks:[{type:"command",command:t}]}),s=!0)}return s&&I(n,JSON.stringify(o,null,2)),{changed:s,path:n,command:e}}function Mn(){let e=jn(),t=V(e);if(!t?.hooks)return{changed:!1,path:e};let n=!1;for(let o of Object.keys(t.hooks)){let s=t.hooks[o];if(!Array.isArray(s))continue;let r=s.filter(a=>!(Array.isArray(a.hooks)&&a.hooks.some(i=>typeof i.command=="string"&&i.command.includes(Tn))));r.length!==s.length&&(t.hooks[o]=r,n=!0)}return n&&I(e,JSON.stringify(t,null,2)),{changed:n,path:e}}import{homedir as $n}from"node:os";import{join as We}from"node:path";import{existsSync as qe,readFileSync as Dn}from"node:fs";var Pn="agentmem",Ln="agentmem-codex-notify",On=`# ${Ln}: Codex session notification hook (maintained by agentmem install, do not edit by hand)`,gt='notify = ["npx", "-y", "@aiagentmem/agentmem", "codex-notify"]';function Je(){let e=process.env.CODEX_HOME||We($n(),".codex");return We(e,"config.toml")}function Fn(){let e=process.env.CODEX_HOME||We($n(),".codex");if(qe(e))return!0;let t=process.env.PATH||"",n=process.platform==="win32"?["codex.exe","codex.cmd","codex.bat","codex"]:["codex"];for(let o of t.split(process.platform==="win32"?";":":")){let s=o.trim();if(s)for(let r of n)try{if(qe(We(s,r)))return!0}catch{}}return!1}function zn(e){return e.endsWith(`
|
|
34
|
+
`)?e:e+`
|
|
35
|
+
`}function Xs(e){for(let t=0;t<e.length;t++)if(/^\s*\[/.test(e[t]))return t;return e.length}function Kn(){let e=Je(),t=gt;if(!qe(e))return I(e,On+`
|
|
36
|
+
`+gt+`
|
|
37
|
+
`),{changed:!0,path:e,conflict:!1,command:t};let o=Dn(e,"utf8").split(`
|
|
38
|
+
`),s=Xs(o);for(let i=0;i<s;i++){let c=o[i];if(!/^\s*#/.test(c)&&/^\s*notify\s*=/.test(c))return c.includes(Pn)?{changed:!1,path:e,conflict:!1,command:t}:{changed:!1,path:e,conflict:!0,command:t}}let r=[On,gt],a;return s<o.length?a=[...o.slice(0,s),...r,"",...o.slice(s)]:a=[...o,...r],I(e,zn(a.join(`
|
|
39
|
+
`))),{changed:!0,path:e,conflict:!1,command:t}}function Un(){let e=Je();if(!qe(e))return{changed:!1,path:e};let t=Dn(e,"utf8").split(`
|
|
40
|
+
`),n=[],o=!1;for(let s of t){if(/^\s*notify\s*=/.test(s)&&s.includes(Pn)){o=!0;continue}if(s.includes(Ln)){o=!0;continue}n.push(s)}return o&&I(e,zn(n.join(`
|
|
41
|
+
`))),{changed:o,path:e}}import{join as Xe}from"node:path";async function Zs(e,t,n,o){let s=K(Date.now()),r=z(),a=F("POST",t,e.accessKey,s,r,e.accessSecret),i=new AbortController,c=setTimeout(()=>i.abort(),o);try{let l=await fetch(e.endpoint+t,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":U,accessKey:e.accessKey,timestamp:s,nonce:r,sign:a},body:JSON.stringify(n??{}),signal:i.signal});return{status:l.status,text:await l.text()}}finally{clearTimeout(c)}}async function q(e,t,n=15e3){let o=N();if(!o)throw new Error(u("Not logged in: run `agentmem login` or set AGENTMEM_ENDPOINT/ACCESS_KEY/ACCESS_SECRET"));let s;for(let r=0;r<2;r++){r>0&&await new Promise(c=>setTimeout(c,400));let a;try{a=await Zs(o,e,t,n)}catch(c){s=c;continue}if(a.status>=500){s=new Error(`HTTP ${a.status}`);continue}let i;try{i=JSON.parse(a.text)}catch{throw new Error(`HTTP ${a.status}: ${a.text.slice(0,160)}`)}if(i.code!==200)throw new Error(i.message||`code ${i.code}`);return i.result}throw s instanceof Error?s:new Error("recall failed")}var Ys="2024-11-05",Vs=[{name:"recall_search",description:`Search relevant memory across the team's past coding sessions by keyword/question (scoped by your permissions), returning session memory cards. Use it when asking "how did we solve X before".`,inputSchema:{type:"object",properties:{query:{type:"string",description:"search term or question"},project:{type:"string",description:"optional: restrict to a project path"},limit:{type:"number",description:"number of results, default 8"}},required:["query"]}},{name:"recall_recent",description:`Get the team's most recent coding session memory cards (permission-scoped). Use it to understand "what everyone has been doing on this project lately".`,inputSchema:{type:"object",properties:{project:{type:"string",description:"optional: restrict to a project path"},limit:{type:"number"}}}},{name:"recall_context",description:`Get the team's recent-memory "context pack" (Markdown: key decisions/pitfalls/conventions + recent session summaries), suitable for injecting at the start of a task to understand the project's established conventions and pitfalls.`,inputSchema:{type:"object",properties:{project:{type:"string",description:"optional: restrict to a project path"},limit:{type:"number"}}}},{name:"save_memory",description:"Persist an important team memory (a key decision / a pitfall you hit / a project convention) to the cloud team-memory store for the whole team to recall later. Call it after making an important decision, resolving a pitfall, or establishing a convention.",inputSchema:{type:"object",properties:{text:{type:"string",description:"the memory (one self-contained decision/pitfall/convention)"},type:{type:"string",description:"decision|pitfall|convention|note, default note"},project:{type:"string",description:"optional: associated project path/cwd"}},required:["text"]}}];function Wn(e){process.stdout.write(JSON.stringify(e)+`
|
|
42
|
+
`)}function Ae(e,t){Wn({jsonrpc:"2.0",id:e,result:t})}function Gn(e){return!Array.isArray(e)||e.length===0?u("(no relevant memory found)"):e.map((t,n)=>{let o=t.title||t.sessionId,s=t.project?`(${t.project})`:"",r=String(t.summary||"").slice(0,300);return`${n+1}. ${o}${s} [source ${t.sessionId}]
|
|
43
|
+
${r}`}).join(`
|
|
44
|
+
`)}async function Qs(e,t){let n=t||{};if(e==="recall_search")return Gn(await q("/api/recall/search",{query:String(n.query??""),project:n.project,limit:n.limit}));if(e==="recall_recent")return Gn(await q("/api/recall/recent",{project:n.project,limit:n.limit}));if(e==="recall_context"){let o=await q("/api/recall/context",{project:n.project,limit:n.limit});return o&&o.markdown?String(o.markdown):u("(no team memory yet)")}if(e==="save_memory"){let o=await q("/api/recall/save",{text:String(n.text??""),type:n.type,project:n.project});return o&&o.message?String(o.message):o&&o.saved?u("\u2713 saved to team memory"):u("not saved")}throw new Error(`unknown tool: ${e}`)}async function er(e){let t=e?.id,n=e?.method,o=e?.params;if(!(n==="notifications/initialized"||n==="notifications/cancelled")){if(n==="initialize"){let s=o&&o.protocolVersion||Ys;Ae(t,{protocolVersion:s,capabilities:{tools:{}},serverInfo:{name:"agentmem-recall",version:M}});return}if(n==="ping"){Ae(t,{});return}if(n==="tools/list"){Ae(t,{tools:Vs});return}if(n==="tools/call"){try{let s=await Qs(o?.name,o?.arguments);Ae(t,{content:[{type:"text",text:s}]})}catch(s){Ae(t,{content:[{type:"text",text:u("Recall failed: ")+(s instanceof Error?s.message:String(s))}],isError:!0})}return}t!=null&&Wn({jsonrpc:"2.0",id:t,error:{code:-32601,message:`method not found: ${n}`}})}}async function qn(){process.stderr.write(u(`[agentmem mcp] team-memory MCP server started (stdio)
|
|
45
|
+
`)),process.stdin.setEncoding("utf8");let e="";for await(let t of process.stdin){e+=t;let n;for(;(n=e.indexOf(`
|
|
46
|
+
`))>=0;){let o=e.slice(0,n).trim();if(e=e.slice(n+1),!o)continue;let s;try{s=JSON.parse(o)}catch{continue}er(s).catch(r=>process.stderr.write("[agentmem mcp] "+(r instanceof Error?r.message:String(r))+`
|
|
47
|
+
`))}}}function Jn(){let e={mcpServers:{agentmem:{command:"agentmem",args:["mcp"]}}};process.stdout.write(u("# Claude Code: put into project .mcp.json (or `claude mcp add`)\n")),process.stdout.write(JSON.stringify(e,null,2)+`
|
|
48
|
+
|
|
49
|
+
`),process.stdout.write(u(`# Codex: add to ~/.codex/config.toml
|
|
50
|
+
`)),process.stdout.write(`[mcp_servers.agentmem]
|
|
51
|
+
command = "agentmem"
|
|
52
|
+
args = ["mcp"]
|
|
53
|
+
|
|
54
|
+
`),process.stdout.write(u('# Note: if agentmem is not installed globally, set command to npx with args ["-y","@aiagentmem/agentmem","mcp"]; ensure `agentmem login` first.\n'))}async function tr(){let e=[];process.stdin.setEncoding("utf8");for await(let t of process.stdin)e.push(t);return e.join("")}function Hn(e,t){let n=(t||"").trim();n&&process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:e,additionalContext:n}}))}function nr(e){return!Array.isArray(e)||e.length===0?"":e.map((t,n)=>`${n+1}. ${t.title||t.sessionId}: ${String(t.summary||"").slice(0,240)}`).join(`
|
|
55
|
+
`)}async function Bn(){let e={};try{e=JSON.parse(await tr())||{}}catch{}let t=e.hook_event_name||"SessionStart",n=e.cwd||void 0;try{if(t==="UserPromptSubmit"){let o=(e.prompt||"").trim();if(o.length<8)return;let s=await q("/api/recall/search",{query:o,project:n,limit:4},4e3),r=nr(s);r&&Hn(t,`[Team memory \xB7 history relevant to this prompt]
|
|
56
|
+
`+r)}else{let o=await q("/api/recall/context",{project:n,limit:8},5e3);o&&((o.sessionCount??0)>0||(o.factCount??0)>0)&&o.markdown&&Hn(t,String(o.markdown))}}catch{}}import{homedir as Xn}from"node:os";import{join as Ne,dirname as ht}from"node:path";import{fileURLToPath as or}from"node:url";import{existsSync as Te,rmSync as Zn}from"node:fs";import{execFileSync as sr}from"node:child_process";var _="agentmem-daemon",He="cloud.agentmem.daemon";function rr(){let e=or(import.meta.url),t=ht(e);for(let n of["cli.js","cli.ts"]){let o=Ne(t,n);if(Te(o))return o}return null}function wt(){let e=rr();if(e){let t=process.execPath;return{program:t,args:[e,"daemon"],display:`"${t}" "${e}" daemon`}}return{program:"npx",args:["-y","@aiagentmem/agentmem","daemon"],display:"npx -y @aiagentmem/agentmem daemon"}}function T(e,t){try{return{code:0,out:sr(e,t,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})??"",err:""}}catch(n){let o=n,s=String(o.stderr??"")||String(o.message??"")||String(o.code??"");return{code:o.status??1,out:String(o.stdout??""),err:s}}}function Be(e){return(e.err.trim()||e.out.trim()||"unknown error").split(`
|
|
57
|
+
`)[0]}function ir(){let e=wt(),t=T("schtasks",["/Create","/TN",_,"/TR",e.display,"/SC","ONLOGON","/F"]);return t.code!==0?{ok:!1,message:u(`Failed to create logon auto-start task: ${Be(t)}`),command:e.display}:{ok:!0,path:_,command:e.display,message:u(`Created logon auto-start task "${_}" (daemon runs at each logon)`)}}function ar(){return T("schtasks",["/Delete","/TN",_,"/F"]).code!==0?{ok:!0,path:_,message:u(`Auto-start task "${_}" not found; nothing to remove`)}:{ok:!0,path:_,message:u(`Removed logon auto-start task "${_}"`)}}function cr(){return T("schtasks",["/Query","/TN",_]).code!==0?{ok:!0,path:_,message:u(`Not installed (no auto-start task "${_}")`)}:{ok:!0,path:_,message:u(`Installed: logon auto-start task "${_}" exists`)}}function kt(){return Ne(Xn(),"Library","LaunchAgents",`${He}.plist`)}function yt(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function lr(e){let n=[e.program,...e.args].map(r=>` <string>${yt(r)}</string>`).join(`
|
|
58
|
+
`),o=Ne(de(),"daemon.out.log"),s=Ne(de(),"daemon.err.log");return`<?xml version="1.0" encoding="UTF-8"?>
|
|
59
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
60
|
+
<plist version="1.0">
|
|
61
|
+
<dict>
|
|
62
|
+
<key>Label</key>
|
|
63
|
+
<string>${He}</string>
|
|
64
|
+
<key>ProgramArguments</key>
|
|
65
|
+
<array>
|
|
66
|
+
${n}
|
|
67
|
+
</array>
|
|
68
|
+
<key>RunAtLoad</key>
|
|
69
|
+
<true/>
|
|
70
|
+
<key>KeepAlive</key>
|
|
71
|
+
<true/>
|
|
72
|
+
<key>StandardOutPath</key>
|
|
73
|
+
<string>${yt(o)}</string>
|
|
74
|
+
<key>StandardErrorPath</key>
|
|
75
|
+
<string>${yt(s)}</string>
|
|
76
|
+
</dict>
|
|
77
|
+
</plist>
|
|
78
|
+
`}function ur(){let e=wt(),t=kt();G(ht(t)),T("launchctl",["unload","-w",t]),I(t,lr(e));let n=T("launchctl",["load","-w",t]);return n.code!==0?{ok:!1,path:t,command:e.display,message:u(`Wrote plist but launchctl load failed: ${Be(n)}`)}:{ok:!0,path:t,command:e.display,message:u(`Installed and loaded LaunchAgent (daemon auto-starts at login) \u2192 ${t}`)}}function dr(){let e=kt();if(!Te(e))return{ok:!0,path:e,message:u(`LaunchAgent not found; nothing to remove \u2192 ${e}`)};T("launchctl",["unload","-w",e]);try{Zn(e,{force:!0})}catch{}return{ok:!0,path:e,message:u(`Unloaded and removed LaunchAgent \u2192 ${e}`)}}function fr(){let e=kt();if(!Te(e))return{ok:!0,path:e,message:u(`Not installed (no LaunchAgent \u2192 ${e})`)};let n=T("launchctl",["list",He]).code===0;return{ok:!0,path:e,message:n?u(`Installed and loaded: LaunchAgent "${He}" is running`):u(`Installed but not loaded (try: launchctl load -w ${e})`)}}function bt(){return Ne(Xn(),".config","systemd","user",`${_}.service`)}function pr(e){return`[Unit]
|
|
79
|
+
Description=agentmem session collector daemon
|
|
80
|
+
After=default.target
|
|
81
|
+
|
|
82
|
+
[Service]
|
|
83
|
+
Type=simple
|
|
84
|
+
ExecStart=${[e.program,...e.args].map(n=>`"${n}"`).join(" ")}
|
|
85
|
+
Restart=on-failure
|
|
86
|
+
RestartSec=5
|
|
87
|
+
|
|
88
|
+
[Install]
|
|
89
|
+
WantedBy=default.target
|
|
90
|
+
`}function mr(){let e=wt(),t=bt();G(ht(t)),I(t,pr(e));let n=T("systemctl",["--user","daemon-reload"]);if(n.code!==0)return{ok:!1,path:t,command:e.display,message:u(`Wrote unit but 'systemctl --user daemon-reload' failed: ${Be(n)}`)};let o=T("systemctl",["--user","enable","--now",_]);return o.code!==0?{ok:!1,path:t,command:e.display,message:u(`Wrote unit but 'enable --now' failed: ${Be(o)} (hint: for non-login sessions run 'loginctl enable-linger $USER')`)}:{ok:!0,path:t,command:e.display,message:u(`Installed and enabled systemd user unit "${_}" (daemon auto-starts at login) \u2192 ${t}`)}}function gr(){let e=bt();T("systemctl",["--user","disable","--now",_]);let t=!1;if(Te(e))try{Zn(e,{force:!0}),t=!0}catch{}return T("systemctl",["--user","daemon-reload"]),{ok:!0,path:e,message:t?u(`Disabled and removed systemd user unit "${_}" \u2192 ${e}`):u(`Unit file not found; attempted to disable "${_}" \u2192 ${e}`)}}function yr(){let e=bt();if(!Te(e))return{ok:!0,path:e,message:u(`Not installed (no systemd user unit \u2192 ${e})`)};let t=T("systemctl",["--user","is-enabled",_]).out.trim()||"unknown",n=T("systemctl",["--user","is-active",_]).out.trim()||"unknown";return{ok:!0,path:e,message:u(`Installed: unit "${_}" enabled=${t} active=${n} \u2192 ${e}`)}}function St(){return{ok:!1,message:u(`Unsupported platform: ${process.platform} (only win32/darwin/linux user-level auto-start)`)}}function Yn(){switch(process.platform){case"win32":return ir();case"darwin":return ur();case"linux":return mr();default:return St()}}function Vn(){switch(process.platform){case"win32":return ar();case"darwin":return dr();case"linux":return gr();default:return St()}}function Qn(){switch(process.platform){case"win32":return cr();case"darwin":return fr();case"linux":return yr();default:return St()}}function kr(e){let t={};for(let n=0;n<e.length;n++){let o=e[n];if(!o.startsWith("--"))continue;let s=o.slice(2),r=e[n+1];r===void 0||r.startsWith("--")?t[s]=!0:(t[s]=r,n++)}return t}var R=e=>typeof e=="string"?e:void 0;function br(e){let t=e.toLowerCase();return/[\\/]opencode[\\/]storage[\\/]/i.test(e)||/[\\/]storage[\\/](message|part)[\\/]/i.test(e)?"opencode":/[\\/]\.gemini[\\/]/i.test(e)||t.endsWith(".json")&&!t.endsWith(".jsonl")?"gemini":/[\\/]\.cursor[\\/]/i.test(e)?"cursor":/[\\/]\.codex[\\/]/i.test(e)?"codex":"claude-code"}function Sr(){return new Promise(e=>{if(process.stdin.isTTY){e("");return}let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",n=>t+=n),process.stdin.on("end",()=>e(t)),setTimeout(()=>e(t),1500)})}async function vr(e){let t=!!e.stdin;if(fe(Xe(C,"daemon.lock"))){t||console.log(u("daemon is running; it handles capture and upload (no flush needed)."));return}let n=Ie("flush","info",!t),o=se(),s=R(e.surface)||"cli",r=R(e.transcript),a=R(e.session);if(t){let f=await Sr();if(f)try{let v=JSON.parse(f);r??=v.transcript_path||v.transcriptPath||void 0,a??=v.session_id||v.sessionId||void 0}catch{}}let i=X(),c=Z(),l=re(),d=ie(),p=ae(),y=R(e.agent);!r&&e.latest&&(r=Ge(y==="cursor"?l:y==="codex"?c:i)),!r&&a&&(r=A(i).find(f=>f.endsWith(`${a}.jsonl`))||A(l).find(f=>f.endsWith(`${a}.jsonl`))||A(c).find(f=>f.includes(a))||me(d).find(f=>f.includes(a))||ge(p).find(f=>f.includes(a)));let h=new $,m=f=>f.toLowerCase().startsWith(p.toLowerCase())||/[\\/]storage[\\/](message|part)[\\/]/i.test(f),b=f=>{let v=f.toLowerCase();return y==="opencode"?Ee:y==="gemini"?xe:y==="cursor"?ve:y==="codex"?ee:y==="claude-code"?Q:m(f)?Ee:v.endsWith(".json")||v.startsWith(d.toLowerCase())||/[\\/]\.gemini[\\/]/i.test(f)?xe:v.startsWith(l.toLowerCase())||/[\\/]\.cursor[\\/]/i.test(f)?ve:v.startsWith(c.toLowerCase())||/[\\/]\.codex[\\/]/i.test(f)?ee:Q},S=f=>b(f)(f,o,s,h,n);if(r&&L(r))await S(r);else{for(let f of A(i))await S(f);for(let f of A(c))await S(f);for(let f of A(l))await S(f);for(let f of me(d))await S(f);for(let f of ge(p))await S(f)}let k=N();if(!k){n.warn(u("no credentials; cached to local spool, will auto-resend after `agentmem login`"));return}let w=await _e(k,h,n,{maxWallMs:2e4});for(let f=0;f<3&&h.stats().pending>0;f++)await O(2e3),await Ce(k,h,o,n);if(!t){let f=h.stats();console.log(u(`flush: sent ${w.sent} events (backend added ${w.accepted}), awaiting persist confirm ${f.pending}${w.parked?" [quota paused]":""}${w.dead?` dead ${w.dead}`:""}`))}}async function xr(){let t=await new $().requeueDead();console.log(u(`replay-dead: requeued ${t} dead-letter events; flush / daemon will upload them next.`))}function Er(e){let t=(e||"status").toLowerCase(),n=t==="install"?Yn():t==="uninstall"?Vn():t==="status"?Qn():void 0;n||(console.error(u("Usage: agentmem service <install|uninstall|status>")),process.exit(2)),console.log(`${n.ok?"\u2713":"\u2717"} ${n.message}`),n.command&&console.log(u(" Auto-start command:"),n.command),t==="install"&&n.ok&&console.log(u(" Note: run `agentmem login` first so the daemon can upload.")),n.ok||process.exit(1)}async function _r(e,t){if(fe(Xe(C,"daemon.lock")))return;let n=Ie("codex-notify","info",!1),o=se(),s=Z(),r;for(let d of t){let p=d.trim();if(p.startsWith("{")&&p.endsWith("}"))try{r=JSON.parse(p);break}catch{}}let a=R(e.transcript),i=R(e.session);if(r){let d=r.rollout_path??r.rolloutPath??r.rollout;!a&&typeof d=="string"&&L(d)&&(a=d),i??=r.session_id??r.sessionId??r.thread_id??r.threadId??r.conversation_id??r["turn-id"]??r.turn_id}!a&&i&&(a=A(s).find(d=>d.includes(i))),a||(a=Ge(s));let c=new $;a&&L(a)&&await ee(a,o,R(e.surface)||"cli",c,n);let l=N();if(!l){n.warn(u("no credentials; cached to local spool, will auto-resend after `agentmem login`"));return}await _e(l,c,n,{maxWallMs:15e3});for(let d=0;d<3&&c.stats().pending>0;d++)await O(2e3),await Ce(l,c,o,n)}function Ir(){let t=new $().stats(),n=N(),o=gn(),s=fe(Xe(C,"daemon.lock")),r=Se(C);console.log(u(`=== agentmem collector \xB7 status (v${M}) ===`)),console.log(u("Daemon:"),s?u("running \u2713"):u("not running (run `agentmem daemon` or use a hook)")),console.log(u("Credentials:"),n?u(`configured \u2192 ${n.endpoint}`):u(`not configured (defaults to ${pe}; run agentmem login[ --endpoint <url>])`)),console.log(u("Local queue:"),u(`pending ${t.pending} \xB7 acked ${t.acked} \xB7 dead ${t.dead} \xB7 backlog ${Math.round(t.oldestMs/6e4)}min \xB7 queue ${Math.round(t.bytes/1024)}KB`)),r>=0&&console.log(u("Disk free:"),`${Math.round(r/1048576)}MB`),console.log(u("Claude dir:"),X()),console.log(u("Codex dir :"),Z()),console.log(u("Cursor dir:"),re()),console.log(u("Gemini dir:"),ie()),console.log(u("OpenCode dir:"),ae());let a=be();a.length&&console.log(u("Extra dirs:"),a.join(", "));let i=Object.keys(o);console.log(u("Sources:"),u(`${i.length} transcripts`));for(let c of Object.values(o).slice(0,8))console.log(u(` \xB7 ${c.sessionId??"?"} line=${c.lineNo} byte=${c.byteOffset}`));console.log(u("Credentials file:"),hn())}function Rr(){let e=w=>w?"\u2713":"\u2717",t=(()=>{let[w,f]=process.versions.node.split(".").map(Number);return w>18||w===18&&f>=18})(),n=X(),o=A(n),s=Z(),r=A(s).length,a=re(),i=A(a).length,c=ie(),l=me(c).length,d=ae(),p=ge(d),y=new Set(p.map(w=>w.replace(/[\\/][^\\/]+$/,""))).size,h=N(),m=!0;try{new $}catch{m=!1}let b=Se(C),S=fe(Xe(C,"daemon.lock"));console.log(u(`=== agentmem collector \xB7 doctor (v${M}) ===`)),console.log(u(`${e(t)} Node ${process.versions.node} (release needs \u226518.18; running unbundled from source needs \u226523.6)`)),console.log(u(`${e(L(n))} Claude session dir: ${n} (found ${o.length} transcripts)`)),console.log(u(`${e(L(s))} Codex session dir: ${s} (found ${r} rollouts)`)),console.log(u(`${e(L(a))} Cursor session dir: ${a} (found ${i} transcripts)`)),console.log(u(`${e(L(c))} Gemini session dir: ${c} (found ${l} candidate .json)`)),console.log(u(`${e(L(d))} OpenCode storage dir: ${d} (found ${y} sessions)`));let k=be();if(k.length)for(let w of k)console.log(u(`${e(!0)} extra dir: ${w} (found ${A(w).length} files)`));else console.log(u(" extra dirs: none (set AGENTMEM_EXTRA_DIRS)"));console.log(u(`${e(m)} local spool writable`)),console.log(u(`${e(b<0||b>200*1024*1024)} disk free${b>=0?`: ${Math.round(b/1048576)}MB`:" (unknown)"}`)),console.log(u(`${e(!!h)} credentials configured${h?` \u2192 ${h.endpoint}`:` (not logged in; defaults to ${pe})`}`)),console.log(u(` daemon: ${S?"running":"not running"}`)),console.log(u(` local time: ${new Date().toISOString()} (signature has a 5-min window; clock must match server)`))}async function Cr(e){let t=R(e.file);!t&&e.latest&&(t=Ge(X())),t||(console.error(u("Usage: agentmem upload --file <transcript> [--agent claude-code|codex|cursor|gemini|opencode] [--latest] [--dry-run]")),process.exit(2));let n=R(e["client-id"])||se(),o=R(e.surface)||"cli",s=R(e.agent)||br(t),r=s==="codex"?Ot(t):s==="cursor"?Ft(t):s==="gemini"?Ut(t):s==="opencode"?Jt(t):Nt(t);e.max&&(r.events=r.events.slice(0,Number(R(e.max))));let a=ke(r.events,n,o,s);console.log(u("File:"),t),console.log("sessionId:",r.sessionId,u(" events:"),a.dtos.length,u(" redacted:"),a.totalHits,JSON.stringify(a.ruleCounts));let i=N(),c=R(e.endpoint)||i?.endpoint||pe,l=R(e.ak)||i?.accessKey,d=R(e.secret)||i?.accessSecret;if(!!e["dry-run"]||!(c&&l&&d)){console.log(u("[dry-run] not sent (needs --endpoint/--ak/--secret, or run agentmem login first)."));return}let y=await en({endpoint:c,accessKey:l,accessSecret:d},a.dtos);console.log(u(`Sent ${a.dtos.length} events, server added accepted=${y} (rest deduped idempotently).`))}function Ar(e){let t;e.local&&(t=`node "${hr(import.meta.url)}" flush --stdin`);let n=t?mt(t):mt();if(console.log(n.changed?u(`\u2713 injected capture hook \u2192 ${n.path}`):u(`hook already present, no change \u2192 ${n.path}`)),console.log(u(" Command:"),n.command),console.log(u(" After this, every Claude Code session end auto-captures and uploads; run `agentmem login` first to set credentials.")),Fn()){let o=Kn();o.conflict?console.log(u(`! Codex already has a non-agentmem notify (not overwritten) \u2192 ${o.path}; to enable, manually set: ${o.command}`)):(console.log(o.changed?u(`\u2713 injected Codex notify hook \u2192 ${o.path}`):u(`Codex notify already present, no change \u2192 ${o.path}`)),console.log(u(" After this, every Codex session end auto-captures and uploads (notify \u2192 codex-notify).")))}}function Nr(){console.log(u(`agentmem collector v${M} (node ${process.versions.node})`))}function Tr(){let e="@aiagentmem/agentmem";console.log(u(`Upgrading ${e} to latest\u2026`));let t=process.platform==="win32",n=wr(t?"npm.cmd":"npm",["install","-g",`${e}@latest`],{stdio:"inherit",shell:t});n.status===0?console.log(u("\u2713 Upgraded. Run `agentmem -v` to confirm.")):(console.log(u("\u2717 Upgrade failed (not published / offline / no permission). Manual: npm i -g @aiagentmem/agentmem@latest; or use npx (always latest).")),process.exit(n.status??1))}function eo(){console.log(u(`agentmem collector v${M}
|
|
91
|
+
Usage: agentmem <command> [options]
|
|
92
|
+
Options: -v / --version / version print version \xB7 -h / -help / --help show help
|
|
93
|
+
|
|
94
|
+
daemon continuous capture (Claude/Codex/Cursor/Gemini/OpenCode, watch+poll \u2192 local WAL \u2192 upload)
|
|
95
|
+
flush [--transcript F|--latest|--session S|--stdin]
|
|
96
|
+
one-shot incremental capture + drain (hook reads from stdin via --stdin)
|
|
97
|
+
codex-notify [<json>] Codex notify hook entry (capture latest/given rollout and durably upload)
|
|
98
|
+
replay-dead requeue local dead-letter events back to the outbox (flush/daemon uploads next)
|
|
99
|
+
login --endpoint <url> device-authorization login (no key pasting)
|
|
100
|
+
install [--local] idempotently inject ~/.claude/settings.json hook + Codex notify (when Codex detected)
|
|
101
|
+
uninstall remove our hook (incl. Codex notify)
|
|
102
|
+
service <install|uninstall|status> service/auto-start: launch daemon at login (Linux=systemd user / Win=Scheduled Task / mac=launchd)
|
|
103
|
+
status local queue/cursor/credentials
|
|
104
|
+
doctor one-shot health check
|
|
105
|
+
upload --file <jsonl> [--dry-run] manual full upload (debug)
|
|
106
|
+
mcp [--print-config] team-memory MCP server (recall for Claude Code/Codex)
|
|
107
|
+
update upgrade to latest (npm i -g @aiagentmem/agentmem@latest)
|
|
108
|
+
version | -v print current version`))}async function jr(){let e=process.argv.slice(2),t=(e[0]??"").toLowerCase();if(["-v","--version","version"].includes(t)){Nr();return}if(["-h","-help","--help","help"].includes(t)){eo();return}let n=e[0]&&!e[0].startsWith("--")?e[0]:"",o=kr(n?e.slice(1):e);switch(n){case"daemon":await Cn({surface:R(o.surface)||"cli"});break;case"flush":await vr(o);break;case"login":{let s=R(o.endpoint)||process.env.AGENTMEM_ENDPOINT||N()?.endpoint||pe,r=await An(s);console.log(r.ok?`\u2713 ${r.message}`:`\u2717 ${r.message}`),process.exit(r.ok?0:1);break}case"install":Ar(o);break;case"uninstall":{let s=Mn();console.log(s.changed?u(`\u2713 removed capture hook \u2192 ${s.path}`):u(`our hook not found \u2192 ${s.path}`));let r=Un();r.changed?console.log(u(`\u2713 removed Codex notify hook \u2192 ${r.path}`)):L(Je())&&console.log(u(`our Codex notify not found \u2192 ${r.path}`));break}case"replay-dead":await xr();break;case"service":Er(e[1]&&!e[1].startsWith("--")?e[1]:void 0);break;case"codex-notify":await _r(o,n?e.slice(1):e);break;case"status":Ir();break;case"doctor":Rr();break;case"upload":await Cr(o);break;case"mcp":o["print-config"]?Jn():await qn();break;case"recall-hook":await Bn();break;case"update":Tr();break;default:eo()}}jr().catch(e=>{console.error(u("Capture failed:"),e instanceof Error?e.message:e),process.exit(1)});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aiagentmem/agentmem",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Session collector for coding agents (Claude Code / Codex / Cursor / Gemini / OpenCode): read-only capture → on-device redaction → HMAC-signed upload. Local WAL, resumable, dead-letter replay. Zero runtime dependencies.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "aiagentmem",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"agent",
|
|
10
|
+
"ai",
|
|
11
|
+
"claude-code",
|
|
12
|
+
"codex",
|
|
13
|
+
"cursor",
|
|
14
|
+
"gemini",
|
|
15
|
+
"opencode",
|
|
16
|
+
"session",
|
|
17
|
+
"capture",
|
|
18
|
+
"observability",
|
|
19
|
+
"redaction",
|
|
20
|
+
"cli"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://agentmem.cloud",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/aiagentmem/agentmem.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/aiagentmem/agentmem/issues"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public",
|
|
32
|
+
"registry": "https://registry.npmjs.org"
|
|
33
|
+
},
|
|
34
|
+
"engines": { "node": ">=20.0.0" },
|
|
35
|
+
"bin": { "agentmem": "dist/cli.js" },
|
|
36
|
+
"main": "dist/cli.js",
|
|
37
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "npm run typecheck && node esbuild.mjs",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "node test/redact.test.ts",
|
|
42
|
+
"dev": "node src/cli.ts",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"typescript": "^5.7.2",
|
|
47
|
+
"@types/node": "^22.0.0",
|
|
48
|
+
"esbuild": "^0.25.0"
|
|
49
|
+
}
|
|
50
|
+
}
|