@crewx/memory 0.1.23-rc.191 → 0.1.23-rc.193
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 +1 -1
- package/SKILL.md +64 -64
- package/dist/cli.js +1 -1
- package/dist/src/engine.js +7 -7
- package/dist/src/summarizer-failure.d.ts +8 -0
- package/dist/src/summarizer-failure.js +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
package/SKILL.md
CHANGED
|
@@ -12,7 +12,7 @@ A long-term memory management skill based on markdown + frontmatter.
|
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
|
-
Included in `packages/built-in/memory`, used via the `
|
|
15
|
+
Included in `packages/built-in/memory`, used via the `crewx memory` command.
|
|
16
16
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
@@ -34,11 +34,11 @@ Shorthand aliases for frequently used commands. They work identically to the ori
|
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
# The following two commands are identical
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
crewx memory get agent_id ABC123
|
|
38
|
+
crewx memory read agent_id ABC123
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
crewx memory index agent_id
|
|
41
|
+
crewx memory list agent_id
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
## Directory Structure
|
|
@@ -59,7 +59,7 @@ memory/
|
|
|
59
59
|
### Save
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
|
|
62
|
+
crewx memory save <agent_id> "<summary>" [category] [--topic=xxx] [--tags=a,b,c] [--body="detailed content"]
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
**Options:**
|
|
@@ -78,18 +78,18 @@ npx memory save <agent_id> "<summary>" [category] [--topic=xxx] [--tags=a,b,c] [
|
|
|
78
78
|
**Examples:**
|
|
79
79
|
```bash
|
|
80
80
|
# [BAD] Bad example - no details
|
|
81
|
-
|
|
81
|
+
crewx memory save cto "Gemini 3.0 Flash moment" decision --topic=crewx-strategy
|
|
82
82
|
|
|
83
83
|
# [GOOD] Good example - details included
|
|
84
|
-
|
|
84
|
+
crewx memory save cto "Gemini 3.0 Flash moment - strategic turning point" decision --topic=crewx-strategy --tags=gemini,pricing --body="SWE-bench 78% dominates Claude 3.5 Sonnet (49%). Price is 1/6. CrewX strategy validated -- orchestration is the key moat."
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
### Index Query
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
crewx memory index <agent_id> # Medium-term memory (topic summaries only)
|
|
91
|
+
crewx memory index <agent_id> --recent # Recent entry list
|
|
92
|
+
crewx memory index <agent_id> --full # Full view
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
**Drill-down pattern:** `index` -> drill into needed topic with `topic` -> drill into needed entry with `get`
|
|
@@ -97,19 +97,19 @@ npx memory index <agent_id> --full # Full view
|
|
|
97
97
|
### Topic Drill-down
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
|
|
100
|
+
crewx memory topic <agent_id> <topic_name>
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
### Recent Memories
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
|
-
|
|
106
|
+
crewx memory recent <agent_id> [days=30]
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
### Keyword Search (Fast)
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
|
-
|
|
112
|
+
crewx memory find <agent_id> "<keyword>"
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
Grep-based string matching. Fast and free.
|
|
@@ -117,7 +117,7 @@ Grep-based string matching. Fast and free.
|
|
|
117
117
|
### Semantic Search (AI)
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
|
-
|
|
120
|
+
crewx memory search <agent_id> "<query>"
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
Semantic search powered by **Gemini 2.0 Flash**. Understands synonyms and context.
|
|
@@ -128,22 +128,22 @@ Semantic search powered by **Gemini 2.0 Flash**. Understands synonyms and contex
|
|
|
128
128
|
**Examples:**
|
|
129
129
|
```bash
|
|
130
130
|
# Keyword search (fast)
|
|
131
|
-
|
|
131
|
+
crewx memory find crewx_dev_lead "agent"
|
|
132
132
|
|
|
133
133
|
# Semantic search (AI)
|
|
134
|
-
|
|
134
|
+
crewx memory search crewx_dev_lead "things to watch out for when assigning tasks"
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
### Detail View (Drill-down)
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
|
|
140
|
+
crewx memory get <agent_id> <memory_id>
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
### Update Memory
|
|
144
144
|
|
|
145
145
|
```bash
|
|
146
|
-
|
|
146
|
+
crewx memory update <agent_id> <memory_id> [options]
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
**Options:**
|
|
@@ -158,16 +158,16 @@ npx memory update <agent_id> <memory_id> [options]
|
|
|
158
158
|
**Examples:**
|
|
159
159
|
```bash
|
|
160
160
|
# Update summary and detailed content
|
|
161
|
-
|
|
161
|
+
crewx memory update cto VkY9X6 --summary="Updated summary" --body="Updated detailed content"
|
|
162
162
|
|
|
163
163
|
# Change topic only
|
|
164
|
-
|
|
164
|
+
crewx memory update cto VkY9X6 --topic=new-topic
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
### Delete Memory
|
|
168
168
|
|
|
169
169
|
```bash
|
|
170
|
-
|
|
170
|
+
crewx memory delete <agent_id> <memory_id> [--force]
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
- Without `--force`, only a confirmation message is displayed
|
|
@@ -176,16 +176,16 @@ npx memory delete <agent_id> <memory_id> [--force]
|
|
|
176
176
|
**Examples:**
|
|
177
177
|
```bash
|
|
178
178
|
# Deletion confirmation
|
|
179
|
-
|
|
179
|
+
crewx memory delete cto VkY9X6
|
|
180
180
|
|
|
181
181
|
# Actual deletion
|
|
182
|
-
|
|
182
|
+
crewx memory delete cto VkY9X6 --force
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
### Merge Memories
|
|
186
186
|
|
|
187
187
|
```bash
|
|
188
|
-
|
|
188
|
+
crewx memory merge <agent_id> <memory_id1> <memory_id2> [--summary="merge summary"]
|
|
189
189
|
```
|
|
190
190
|
|
|
191
191
|
- Merges two memories into one
|
|
@@ -196,16 +196,16 @@ npx memory merge <agent_id> <memory_id1> <memory_id2> [--summary="merge summary"
|
|
|
196
196
|
**Examples:**
|
|
197
197
|
```bash
|
|
198
198
|
# Merge two memories
|
|
199
|
-
|
|
199
|
+
crewx memory merge cto abc123 def456
|
|
200
200
|
|
|
201
201
|
# Merge with custom summary
|
|
202
|
-
|
|
202
|
+
crewx memory merge cto abc123 def456 --summary="VLM fine-tuning comprehensive summary"
|
|
203
203
|
```
|
|
204
204
|
|
|
205
205
|
### Generate Summary (summarize)
|
|
206
206
|
|
|
207
207
|
```bash
|
|
208
|
-
|
|
208
|
+
crewx memory summarize <agent_id> [--force]
|
|
209
209
|
```
|
|
210
210
|
|
|
211
211
|
Memory summarization using the MemGAS incremental summarization approach.
|
|
@@ -229,10 +229,10 @@ Memory summarization using the MemGAS incremental summarization approach.
|
|
|
229
229
|
**Examples:**
|
|
230
230
|
```bash
|
|
231
231
|
# Incremental summary (reflects new entries only)
|
|
232
|
-
|
|
232
|
+
crewx memory summarize ui_devlead
|
|
233
233
|
|
|
234
234
|
# Force full regeneration
|
|
235
|
-
|
|
235
|
+
crewx memory summarize ui_devlead --force
|
|
236
236
|
```
|
|
237
237
|
|
|
238
238
|
### Generate Summary (summarize-dirty)
|
|
@@ -241,12 +241,12 @@ npx memory summarize ui_devlead --force
|
|
|
241
241
|
Cron checks and executes every 1 minute.
|
|
242
242
|
|
|
243
243
|
```bash
|
|
244
|
-
|
|
244
|
+
crewx memory summarize-dirty
|
|
245
245
|
```
|
|
246
246
|
|
|
247
247
|
**Cron registration example:**
|
|
248
248
|
```bash
|
|
249
|
-
|
|
249
|
+
crewx cron add "*/1 * * * *" "crewx memory summarize-dirty" --mode command --name "memory-debounce"
|
|
250
250
|
```
|
|
251
251
|
|
|
252
252
|
**summary.md format:**
|
|
@@ -271,7 +271,7 @@ CLI shell environment detection performance improvement...
|
|
|
271
271
|
### Filename Migration (migrate)
|
|
272
272
|
|
|
273
273
|
```bash
|
|
274
|
-
|
|
274
|
+
crewx memory migrate <agent_id>
|
|
275
275
|
```
|
|
276
276
|
|
|
277
277
|
Batch converts existing slug filenames (`2026-02-16-slug-name.md`) to ID-based filenames (`QOSIOs.md`).
|
|
@@ -282,7 +282,7 @@ Batch converts existing slug filenames (`2026-02-16-slug-name.md`) to ID-based f
|
|
|
282
282
|
|
|
283
283
|
**Example:**
|
|
284
284
|
```bash
|
|
285
|
-
|
|
285
|
+
crewx memory migrate ui_devlead
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
## Frontmatter Schema
|
|
@@ -307,12 +307,12 @@ summary: "One-line summary"
|
|
|
307
307
|
## At Conversation Start (Required!)
|
|
308
308
|
```bash
|
|
309
309
|
# Load medium-term memory (topic summaries + drill-down guide)
|
|
310
|
-
|
|
310
|
+
crewx memory index {{agent_id}}
|
|
311
311
|
|
|
312
312
|
# Drill down as needed
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
crewx memory index {{agent_id}} --recent # Recent list
|
|
314
|
+
crewx memory topic {{agent_id}} <topic> # Topic details
|
|
315
|
+
crewx memory get {{agent_id}} <id> # Individual memory
|
|
316
316
|
```
|
|
317
317
|
|
|
318
318
|
## Memory Save Triggers
|
|
@@ -336,16 +336,16 @@ npx memory get {{agent_id}} <id> # Individual memory
|
|
|
336
336
|
**Examples:**
|
|
337
337
|
```bash
|
|
338
338
|
# Add to existing topic
|
|
339
|
-
|
|
339
|
+
crewx memory save {{agent_id}} "VLM learning rate adjustment" decision --topic=vlm-finetuning
|
|
340
340
|
|
|
341
341
|
# Create new topic
|
|
342
|
-
|
|
342
|
+
crewx memory save {{agent_id}} "New feature planning" decision --topic=new-feature-x
|
|
343
343
|
```
|
|
344
344
|
|
|
345
345
|
## Save Command (--body required!)
|
|
346
346
|
```bash
|
|
347
347
|
# WARNING: Always save with --body included!
|
|
348
|
-
|
|
348
|
+
crewx memory save {{agent_id}} "<summary>" <category> --topic=<topic> --body="<detailed content>"
|
|
349
349
|
```
|
|
350
350
|
|
|
351
351
|
**--body writing guide:**
|
|
@@ -383,7 +383,7 @@ Manages associations between memories as a graph.
|
|
|
383
383
|
### Build Graph
|
|
384
384
|
|
|
385
385
|
```bash
|
|
386
|
-
|
|
386
|
+
crewx memory mindmap build <agent_id>
|
|
387
387
|
```
|
|
388
388
|
|
|
389
389
|
Rule-based connection generation:
|
|
@@ -408,13 +408,13 @@ Rule-based connection generation:
|
|
|
408
408
|
**[BAD] Bad example (no connections)**
|
|
409
409
|
```bash
|
|
410
410
|
# topic=general, category=general -> 0 connections
|
|
411
|
-
|
|
411
|
+
crewx memory save agent "Discord settings removal" general
|
|
412
412
|
```
|
|
413
413
|
|
|
414
414
|
**[GOOD] Good example (strong connections)**
|
|
415
415
|
```bash
|
|
416
416
|
# topic + category + tags -> maximum connections
|
|
417
|
-
|
|
417
|
+
crewx memory save agent "Discord settings removal decision" decision \
|
|
418
418
|
--topic=discord \
|
|
419
419
|
--tags=settings,cpo-decision,oauth \
|
|
420
420
|
--body="CPO meeting result..."
|
|
@@ -428,7 +428,7 @@ npx memory save agent "Discord settings removal decision" decision \
|
|
|
428
428
|
### View Graph Summary
|
|
429
429
|
|
|
430
430
|
```bash
|
|
431
|
-
|
|
431
|
+
crewx memory mindmap show <agent_id>
|
|
432
432
|
```
|
|
433
433
|
|
|
434
434
|
Displays topic-based clusters, hub nodes (memories with many connections), etc.
|
|
@@ -437,9 +437,9 @@ Default output is in **Mermaid format**, which can be visualized as a graph in V
|
|
|
437
437
|
|
|
438
438
|
**Period filter options:**
|
|
439
439
|
```bash
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
440
|
+
crewx memory mindmap show <agent_id> --period=stm # 24h
|
|
441
|
+
crewx memory mindmap show <agent_id> --period=mtm # 7d
|
|
442
|
+
crewx memory mindmap show <agent_id> --period=ltm # 30d
|
|
443
443
|
```
|
|
444
444
|
|
|
445
445
|
### Export as PNG Image
|
|
@@ -448,13 +448,13 @@ Convert Mermaid output to PNG images for visual inspection.
|
|
|
448
448
|
|
|
449
449
|
```bash
|
|
450
450
|
# 1. Save mermaid code to file
|
|
451
|
-
|
|
451
|
+
crewx memory mindmap show <agent_id> | sed 's/^```mermaid$//' | sed 's/^```$//' > /tmp/graph.mmd
|
|
452
452
|
|
|
453
453
|
# 2. Convert to PNG (runs without installation via npx)
|
|
454
454
|
npx @mermaid-js/mermaid-cli -i /tmp/graph.mmd -o graph.png -w 2400 -H 1600 -b white
|
|
455
455
|
|
|
456
456
|
# Period filter + PNG (e.g., last 24 hours only)
|
|
457
|
-
|
|
457
|
+
crewx memory mindmap show <agent_id> --period=stm | sed 's/^```mermaid$//' | sed 's/^```$//' | grep -v '^(' > /tmp/graph-stm.mmd
|
|
458
458
|
npx @mermaid-js/mermaid-cli -i /tmp/graph-stm.mmd -o graph-stm.png -w 1600 -H 900 -b white
|
|
459
459
|
```
|
|
460
460
|
|
|
@@ -463,7 +463,7 @@ npx @mermaid-js/mermaid-cli -i /tmp/graph-stm.mmd -o graph-stm.png -w 1600 -H 90
|
|
|
463
463
|
### Query Related Memories
|
|
464
464
|
|
|
465
465
|
```bash
|
|
466
|
-
|
|
466
|
+
crewx memory mindmap related <agent_id> <memory_id>
|
|
467
467
|
```
|
|
468
468
|
|
|
469
469
|
Displays memories connected to a specific memory, sorted by weight.
|
|
@@ -472,16 +472,16 @@ Displays memories connected to a specific memory, sorted by weight.
|
|
|
472
472
|
|
|
473
473
|
```bash
|
|
474
474
|
# Add connection
|
|
475
|
-
|
|
475
|
+
crewx memory mindmap add <agent_id> <from_id> <to_id> [type]
|
|
476
476
|
|
|
477
477
|
# Remove connection
|
|
478
|
-
|
|
478
|
+
crewx memory mindmap remove <agent_id> <from_id> <to_id>
|
|
479
479
|
```
|
|
480
480
|
|
|
481
481
|
### Visualization (HTML) - Shared Graph
|
|
482
482
|
|
|
483
483
|
```bash
|
|
484
|
-
|
|
484
|
+
crewx memory mindmap html
|
|
485
485
|
```
|
|
486
486
|
|
|
487
487
|
Generates a **shared visualization combining connection graphs of all agents** in one place.
|
|
@@ -499,22 +499,22 @@ Generates a **shared visualization combining connection graphs of all agents** i
|
|
|
499
499
|
**Usage:**
|
|
500
500
|
```bash
|
|
501
501
|
# 1. Generate shared HTML (no agent_id needed)
|
|
502
|
-
|
|
502
|
+
crewx memory mindmap html
|
|
503
503
|
|
|
504
504
|
# 2. Open in browser
|
|
505
|
-
|
|
505
|
+
crewx memory mindmap open
|
|
506
506
|
```
|
|
507
507
|
|
|
508
508
|
**Example:**
|
|
509
509
|
```bash
|
|
510
510
|
# Generate integrated visualization of all agent graphs
|
|
511
|
-
|
|
511
|
+
crewx memory mindmap html
|
|
512
512
|
```
|
|
513
513
|
|
|
514
514
|
### Open in Browser
|
|
515
515
|
|
|
516
516
|
```bash
|
|
517
|
-
|
|
517
|
+
crewx memory mindmap open [agent_id]
|
|
518
518
|
```
|
|
519
519
|
|
|
520
520
|
Opens a specific agent's graph directly in the browser.
|
|
@@ -522,17 +522,17 @@ Opens a specific agent's graph directly in the browser.
|
|
|
522
522
|
**Examples:**
|
|
523
523
|
```bash
|
|
524
524
|
# Open CTO graph
|
|
525
|
-
|
|
525
|
+
crewx memory mindmap open cto
|
|
526
526
|
|
|
527
527
|
# Open default (all agents)
|
|
528
|
-
|
|
528
|
+
crewx memory mindmap open
|
|
529
529
|
```
|
|
530
530
|
|
|
531
531
|
### Storage Location
|
|
532
532
|
|
|
533
533
|
```
|
|
534
534
|
memory/{agent_id}/graph.json # Graph data
|
|
535
|
-
memory/mindmap.html # Shared visualization HTML (generated by
|
|
535
|
+
memory/mindmap.html # Shared visualization HTML (generated by crewx memory mindmap html)
|
|
536
536
|
```
|
|
537
537
|
|
|
538
538
|
---
|
|
@@ -585,7 +585,7 @@ settings:
|
|
|
585
585
|
By default, `crewx.yaml` is read from the project root. To use a different path, set the `CREWX_CONFIG` environment variable:
|
|
586
586
|
|
|
587
587
|
```bash
|
|
588
|
-
CREWX_CONFIG=/path/to/custom-crewx.yaml
|
|
588
|
+
CREWX_CONFIG=/path/to/custom-crewx.yaml crewx memory index my_agent
|
|
589
589
|
```
|
|
590
590
|
|
|
591
591
|
### Error handling
|
|
@@ -608,7 +608,7 @@ settings:
|
|
|
608
608
|
Or override via environment variable (e.g., in CI/CD):
|
|
609
609
|
|
|
610
610
|
```bash
|
|
611
|
-
MEMORY_SUMMARY_DAYS=14
|
|
611
|
+
MEMORY_SUMMARY_DAYS=14 crewx memory summarize my_agent
|
|
612
612
|
```
|
|
613
613
|
|
|
614
614
|
---
|
|
@@ -616,5 +616,5 @@ MEMORY_SUMMARY_DAYS=14 npx memory summarize my_agent
|
|
|
616
616
|
## v1 -> v2 Migration
|
|
617
617
|
|
|
618
618
|
```bash
|
|
619
|
-
|
|
619
|
+
crewx memory migrate <agent_id>
|
|
620
620
|
```
|
package/dist/cli.js
CHANGED
|
@@ -33,4 +33,4 @@ Examples:
|
|
|
33
33
|
memory get myagent PWN3qt
|
|
34
34
|
|
|
35
35
|
For detailed usage guide: memory usage
|
|
36
|
-
`)}const j={read:"get",rm:"delete",ls:"recent"};const A=new Set(["help","force","verbose","full","important","no-important","json","all","version"]);function M(s){const r={positional:[],options:{}};for(let i=0;i<s.length;i++){const e=s[i];if(e==="-h"){r.options["help"]=true}else if(e.startsWith("--")){const t=e.indexOf("=");if(t!==-1){const n=e.slice(2,t);const o=e.slice(t+1);r.options[n]=o}else{const n=e.slice(2);if(!A.has(n)){const o=s[i+1];if(o!==void 0&&!o.startsWith("--")&&o!=="-h"){r.options[n]=o;i++}else{r.options[n]=true}}else{r.options[n]=true}}}else{r.positional.push(e)}}return r}async function b(s){const r=s??process.argv.slice(2);const{positional:i,options:e}=M(r);const[t,n,...o]=i;if(e.version){console.log(_);return}if(e.help){h();return}if(t==="usage"){O();return}if(t==="help"){h();return}if(!t){h();return}const g=j[t]??t;if(g==="mindmap"){const l=process.env["MEMORY_DATA_DIR"]??f.resolve(process.cwd(),"memory");const m=new S.MindmapEngine(l);const u=n;const c=o[0];const d=o.slice(1);if(!u){console.log("Usage:
|
|
36
|
+
`)}const j={read:"get",rm:"delete",ls:"recent"};const A=new Set(["help","force","verbose","full","important","no-important","json","all","version"]);function M(s){const r={positional:[],options:{}};for(let i=0;i<s.length;i++){const e=s[i];if(e==="-h"){r.options["help"]=true}else if(e.startsWith("--")){const t=e.indexOf("=");if(t!==-1){const n=e.slice(2,t);const o=e.slice(t+1);r.options[n]=o}else{const n=e.slice(2);if(!A.has(n)){const o=s[i+1];if(o!==void 0&&!o.startsWith("--")&&o!=="-h"){r.options[n]=o;i++}else{r.options[n]=true}}else{r.options[n]=true}}}else{r.positional.push(e)}}return r}async function b(s){const r=s??process.argv.slice(2);const{positional:i,options:e}=M(r);const[t,n,...o]=i;if(e.version){console.log(_);return}if(e.help){h();return}if(t==="usage"){O();return}if(t==="help"){h();return}if(!t){h();return}const g=j[t]??t;if(g==="mindmap"){const l=process.env["MEMORY_DATA_DIR"]??f.resolve(process.cwd(),"memory");const m=new S.MindmapEngine(l);const u=n;const c=o[0];const d=o.slice(1);if(!u){console.log("Usage: crewx memory mindmap <subcommand> [agent_id] [args]");console.log("");console.log("Subcommands:");console.log(" build <agent_id> - Build graph");console.log(" show <agent_id> [--period=stm|mtm|ltm] [--format=mermaid|text] - Graph summary");console.log(" related <agent_id> <memory_id> - View related memories");console.log(" add <agent_id> <from_id> <to_id> [type] - Add manual relation");console.log(" remove <agent_id> <from_id> <to_id> - Remove relation");console.log(" html - Generate shared HTML");console.log(" open [agent_id] - Open in browser");process.exit(1)}switch(u){case"build":if(!c){console.log("Error: agent_id is required");process.exit(1)}m.build(c,e);break;case"show":if(!c){console.log("Error: agent_id is required");process.exit(1)}m.show(c,{period:e.period,format:e.format});break;case"related":if(!c||!d?.[0]){console.log("Error: agent_id and memory_id are required");process.exit(1)}m.related(c,d[0]);break;case"add":if(!c||!d?.[0]||!d?.[1]){console.log("Error: agent_id, from_id and to_id are required");process.exit(1)}m.add(c,d[0],d[1],d[2]);break;case"remove":if(!c||!d?.[0]||!d?.[1]){console.log("Error: agent_id, from_id and to_id are required");process.exit(1)}m.remove(c,d[0],d[1]);break;case"html":m.html();break;case"open":m.open(c);break;default:console.log(`Unknown mindmap subcommand: ${u}`);process.exit(1)}return}const a=new k.MemoryEngine;if(g==="summarize-dirty"){a.summarizeDirty();return}if(!n){console.log("Error: agent_id is required");process.exit(1)}switch(g){case"save":{const l=o[0];if(!l){console.log("Error: summary is required");process.exit(1)}let m;if(o[1]&&x.CATEGORIES.includes(o[1])){m=o[1]}else{m="general";if(o[1]&&!e.tags){e.tags=o[1]}}if(o[2]&&!e.body){e.body=o[2]}if(!e.body&&!process.stdin.isTTY){const u=q();let c;try{c=await Promise.race([u.body,new Promise(d=>setTimeout(()=>d(""),100))])}finally{u.release()}if(c){e.body=c}}a.save(n,l,m,e);break}case"index":a.index(n,e);break;case"list":a.list(n,e);break;case"stats":a.stats(n);break;case"tags":if(o[0]){a.topic(n,o[0])}else{a.tags(n)}break;case"recent":a.recent(n,parseInt(o[0]??"",10)||30);break;case"find":if(!o[0]){console.log("Error: keyword is required");process.exit(1)}a.find(n,o[0]);break;case"get":if(!o[0]){console.log("Error: memory_id is required");process.exit(1)}a.get(n,o[0]);break;case"search":if(!o[0]){console.log("Error: query is required");process.exit(1)}a.search(n,o.join(" "));break;case"update":if(!o[0]){console.log("Error: memory_id is required");process.exit(1)}a.update(n,o[0],e);break;case"delete":if(!o[0]){console.log("Error: memory_id is required");process.exit(1)}a.delete(n,o[0],e);break;case"merge":if(!o[0]||!o[1]){console.log("Error: two memory_ids are required");console.log('Usage: node cli.ts merge <agent_id> <id1> <id2> [--summary="merge summary"]');process.exit(1)}a.merge(n,o[0],o[1],e);break;case"summarize":a.summarize(n,e);break;default:console.log(`Unknown command: ${t}`);process.exit(1)}}if(require.main===module){E().then(s=>{const r=s?{skillVersion:s.getOwnSkillVersion(__dirname)}:{};return s?s.run("crewx/memory",()=>b(),r):b()}).catch(s=>{console.error(s.message);process.exit(1)})}
|
package/dist/src/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var v=exports&&exports.__importDefault||function($){return $&&$.__esModule?$:{"default":$}};Object.defineProperty(exports,"__esModule",{value:true});exports.CATEGORIES=exports.MemoryEngine=exports.SUMMARIZE_LOCK_STALE_MS=exports.SUMMARIZER_FAILURE_THRESHOLD=void 0;exports.getMemoryConfig=
|
|
1
|
+
"use strict";var v=exports&&exports.__importDefault||function($){return $&&$.__esModule?$:{"default":$}};Object.defineProperty(exports,"__esModule",{value:true});exports.CATEGORIES=exports.MemoryEngine=exports.SUMMARIZE_LOCK_STALE_MS=exports.SUMMARIZER_FAILURE_THRESHOLD=void 0;exports.getMemoryConfig=P;exports.createMemoryEngine=Z;const l=v(require("fs"));const D=v(require("path"));const N=require("child_process");const B=require("nanoid");const H=v(require("js-yaml"));const T=require("./parser");const j=require("./summarizer-failure");const _=require("@crewx/knowledge-core");const z=require("@crewx/sdk");exports.SUMMARIZER_FAILURE_THRESHOLD=3;const U=".summarizer-health.json";const M=50;function q(){const $=(0,z.resolveCrewxExecutable)({env:process.env,cwd:process.cwd()});if(!$.ok){const n=new Error((0,z.formatCrewxExecutableFailure)($));Object.assign(n,{code:"CLI_UNRESOLVABLE"});throw n}const e=(0,z.createCrewxChildInvocation)($,{baseEnv:process.env});const t=(0,z.resolveWindowsSpawnProgram)({command:e.command,env:e.env,cwd:process.cwd(),packageName:"crewx",allowShellFallback:true});const r=(0,z.materializeWindowsSpawnProgram)(t,e.args);return{command:r.command,args:r.argv,env:e.env,shell:r.shell,windowsHide:r.windowsHide}}function k($,e){const t=q();return(0,N.execFileSync)(t.command,[...t.args,...$],{...e,env:t.env,...t.shell===void 0?{}:{shell:t.shell},...t.windowsHide===void 0?{}:{windowsHide:t.windowsHide}})}exports.SUMMARIZE_LOCK_STALE_MS=10*60*1e3;function O($){const e=$.match(/📄 Response:\s*─+\s*([\s\S]*?)(?=📁 Working Directory|$)/);if(e?.[1]){const t=e[1].trim();const r=t.split("\n")[0]?.trim()??"";if(!r.startsWith("{")){return t}return I(t)}return I($)}function I($){const e=$.split("\n").filter(o=>o.trim().startsWith("{"));let t="";let r=false;for(const o of e){try{const i=JSON.parse(o);if(i.type==="message"&&i.role==="assistant"){r=true;if(i.content)t+=i.content}}catch(i){}}if(r)return t.trim()||null;const n=$.trim();if(n.length===0)return null;const s=n.split("\n")[0]?.trim()??"";if(s.startsWith("{"))return null;return n}function W(){try{return(0,N.execSync)("git rev-parse --show-toplevel",{encoding:"utf8"}).trim()}catch{return process.cwd()}}function P(){try{const $=process.env.CREWX_CONFIG??D.default.join(W(),"crewx.yaml");const e=l.default.readFileSync($,"utf8");const t=H.default.load(e);const r=t?.settings;return r?.memory??{}}catch{return{}}}class L{constructor(e={}){this.searchIndexCache=new Map;const t=P();this.dataDir=e.dataDir??process.env["MEMORY_DATA_DIR"]??D.default.resolve(process.cwd(),"memory");this.summaryDays=e.summaryDays??(t.summary_days!=null?Number(t.summary_days):void 0)??(parseInt(process.env["MEMORY_SUMMARY_DAYS"]??"",10)||7);this.recentDays=e.recentDays??(t.recent_days!=null?Number(t.recent_days):void 0)??(parseInt(process.env["MEMORY_RECENT_DAYS"]??"",10)||30);this.shortTermHours=e.shortTermHours??(t.short_term_hours!=null?Number(t.short_term_hours):void 0)??24;this.summarizerAgent=e.summarizerAgent??t.summarizer_agent??"@memory_summarizer";this.searcherAgent=e.searcherAgent??t.searcher_agent??"@memory_searcher";this.summarizerTimeout=e.summarizerTimeout??(t.summarizer_timeout!=null?Number(t.summarizer_timeout):void 0)??48e4;this.searcherTimeout=e.searcherTimeout??(t.searcher_timeout!=null?Number(t.searcher_timeout):void 0)??3e5}getAgentDir(e){return D.default.join(this.dataDir,e)}getEntriesDir(e){return D.default.join(this.getAgentDir(e),"entries")}getEntryFilesFingerprint(e){const t=this.getEntriesDir(e);if(!l.default.existsSync(t))return{files:[],fingerprint:""};const r=l.default.readdirSync(t).filter(s=>s.endsWith(".md")).sort();const n=r.map(s=>{const o=l.default.statSync(D.default.join(t,s));return`${s}\0${o.size}\0${o.mtimeMs}`}).join("");return{files:r,fingerprint:n}}buildSearchIndex(e,t){const r=this.getEntriesDir(e);const n=t.map(s=>{const o=l.default.readFileSync(D.default.join(r,s),"utf-8");const{data:i,content:d}=(0,T.parseFrontmatter)(o);return{...i,file:s,body:d}});return(0,_.createSearchIndex)(n,{summary:3,tags:2,body:1})}getSearchIndex(e){const t=this.getEntryFilesFingerprint(e);const r=this.searchIndexCache.get(e);if(r?.fingerprint===t.fingerprint){return{files:t.files,index:r.index}}const n=this.buildSearchIndex(e,t.files);this.searchIndexCache.set(e,{fingerprint:t.fingerprint,index:n});return{files:t.files,index:n}}invalidateSearchIndex(e){this.searchIndexCache.delete(e)}createFindActionContext(e,t,r){return{commandId:"memory.find",params:{agentId:e,query:t},resultTags:[...new Set(r.flatMap(n=>n.tags??[]))].slice(0,3),resultIds:r.slice(0,3).map(n=>n.id),resultCount:r.length}}getSummaryPath(e){return D.default.join(this.getAgentDir(e),"summary.md")}getDirtySummaryPath(e){return D.default.join(this.getAgentDir(e),".dirty-summary")}getSummarizeLockPath(){return D.default.join(this.dataDir,".summarize.lock")}ensureDir(e){if(!l.default.existsSync(e)){l.default.mkdirSync(e,{recursive:true})}}generateId(){return(0,B.nanoid)(6)}getToday(){return new Date().toISOString().slice(0,10)}getLocalISO8601(){const e=new Date;const t=-e.getTimezoneOffset();const r=t>=0?"+":"-";const n=String(Math.floor(Math.abs(t)/60)).padStart(2,"0");const s=String(Math.abs(t)%60).padStart(2,"0");const o=e.getFullYear();const i=String(e.getMonth()+1).padStart(2,"0");const d=String(e.getDate()).padStart(2,"0");const f=String(e.getHours()).padStart(2,"0");const a=String(e.getMinutes()).padStart(2,"0");const c=String(e.getSeconds()).padStart(2,"0");return`${o}-${i}-${d}T${f}:${a}:${c}${r}${n}:${s}`}getPrimaryTag(e){return e.tags?.[0]??"general"}toEntryTimestamp(e){if(e["created_at"])return String(e["created_at"]);const t=e["date"];const r=t&&typeof t==="object"&&typeof t.toISOString==="function"?t.toISOString().slice(0,10):String(t??"");if(r.includes("T"))return r;return r+"T09:00:00+09:00"}markSummaryDirty(e){this.ensureDir(this.getAgentDir(e));l.default.writeFileSync(this.getDirtySummaryPath(e),String(Date.now()),"utf-8")}getSummarizerHealthPath(){return D.default.join(this.dataDir,U)}readSummarizerHealth(){try{const e=l.default.readFileSync(this.getSummarizerHealthPath(),"utf-8");const t=JSON.parse(e);return{consecutiveFailures:typeof t.consecutiveFailures==="number"?t.consecutiveFailures:0,notified:Boolean(t.notified),lastError:t.lastError}}catch{return{consecutiveFailures:0,notified:false}}}writeSummarizerHealth(e){this.ensureDir(this.dataDir);l.default.writeFileSync(this.getSummarizerHealthPath(),JSON.stringify(e),"utf-8")}recordSummarizerFailure(e){const t=this.readSummarizerHealth();t.consecutiveFailures+=1;t.lastError=e.code||e.message?e:void 0;if(t.consecutiveFailures>=exports.SUMMARIZER_FAILURE_THRESHOLD&&!t.notified){this.notifySummarizerDegraded(t.consecutiveFailures,t.lastError);t.notified=true}this.writeSummarizerHealth(t)}recordSummarizerSuccess(){const e=this.readSummarizerHealth();if(e.consecutiveFailures===0&&!e.notified)return;this.writeSummarizerHealth({consecutiveFailures:0,notified:false})}notifySummarizerDegraded(e,t){try{k(["notify","AI memory summarization keeps failing \u2014 falling back to mechanical summaries","--level=warning","--body=callSummarizer(agent="+this.summarizerAgent+") failed "+e+" times in a row (last error: "+(0,j.formatSummarizerLastError)(t)+"), so summarization switched to the fallback (mechanical) path."],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:15e3})}catch(r){const n=r;console.error(`[memory] failed to send summarizer-health notify: ${n.message}`)}}loadAllEntries(e){const t=this.getEntriesDir(e);if(!l.default.existsSync(t))return[];const r=l.default.readdirSync(t).filter(s=>s.endsWith(".md"));const n=[];for(const s of r){const o=l.default.readFileSync(D.default.join(t,s),"utf-8");const{data:i,content:d}=(0,T.parseFrontmatter)(o);const f=d.replace(/^#[^\n]*\n*/m,"").trim();const a=Boolean(f)&&f!=="(Add details here)";const c=i;n.push({...c,file:s,hasBody:a})}n.sort((s,o)=>this.toEntryTimestamp(o).localeCompare(this.toEntryTimestamp(s)));return n}findEntryById(e,t){const r=this.getEntriesDir(e);if(!l.default.existsSync(r))return null;const n=l.default.readdirSync(r).filter(s=>s.endsWith(".md"));for(const s of n){const o=D.default.join(r,s);const i=l.default.readFileSync(o,"utf-8");const{data:d,content:f}=(0,T.parseFrontmatter)(i);if(d.id===t){return{file:s,filePath:o,data:d,content:f}}}return null}save(e,t,r="general",n={}){this.ensureDir(this.getEntriesDir(e));const s=this.generateId();const o=this.getLocalISO8601();const i=Array.isArray(n.tags)?n.tags:n.tags?n.tags.split(",").map(h=>h.trim()):[];const d=(0,T.stringifyFrontmatter)({id:s,type:"memory",date:o,category:r,tags:i,summary:t,...n.important?{important:true}:{}});const f=`${s}.md`;const a=D.default.join(this.getEntriesDir(e),f);const c=n.body??"(Add details here)";const m=`${d}
|
|
2
2
|
|
|
3
3
|
# ${t}
|
|
4
4
|
|
|
@@ -30,7 +30,7 @@ ${c}
|
|
|
30
30
|
`;if(d>0)i+=`- Older than ${this.recentDays} days: ${d} items
|
|
31
31
|
`;return i}topic(e,t){const r=this.loadAllEntries(e);const n=r.filter(s=>this.getPrimaryTag(s)===t);if(n.length===0){console.log(`No memories found for topic '${t}'.`);return}console.log(`## \u{1F4C2} Topic: ${t} (${n.length} items)
|
|
32
32
|
`);for(const s of n){console.log(`### [${s.date}] ${s.summary}`);console.log(`- File: data/${e}/entries/${s.file}`);console.log(`- Category: ${s.category??"-"}`);console.log(`- Tags: ${(s.tags??[]).join(", ")||"-"}`);console.log("")}}recent(e,t=30){const r=this.getRecentEntries(e,t);if(r.length===0){console.log(`No memories found in the last ${t} days.`);return}console.log(`## \u{1F525} Last ${t} days (${r.length} items)
|
|
33
|
-
`);for(const n of r){const s=n.hasBody?" \u{1F4C4}":"";const o=String(n.date).includes("T")?String(n.date).slice(0,16).replace("T"," "):n.date;console.log(`[${n.id}] [${o}] [${n.category??"-"}] ${n.summary}${s}`)}}getRecentEntries(e,t=30,r={}){const n=this.loadAllEntries(e);const s=new Date(Date.now()-t*24*60*60*1e3).toISOString().slice(0,10);const o=n.filter(i=>this.resolveRecentDate(e,i).slice(0,10)>=s);if(!r.includeBody)return o;return o.map(i=>({...i,body:this.readEntryBody(e,i.file)}))}resolveRecentDate(e,t){const r=this.toDateString(t.updated);if(r)return r;const n=this.toDateString(t["created_at"]);if(n)return n;const s=this.toDateString(t.date);if(s)return s;try{const o=D.default.join(this.getEntriesDir(e),t.file);return l.default.statSync(o).mtime.toISOString()}catch{return""}}toDateString(e){if(e&&typeof e==="object"&&typeof e.toISOString==="function"){return e.toISOString()}return typeof e==="string"?e:""}readEntryBody(e,t){try{const r=l.default.readFileSync(D.default.join(this.getEntriesDir(e),t),"utf-8");const{content:n}=(0,T.parseFrontmatter)(r);return n.replace(/^#[^\n]*\n*/m,"").trim()}catch{return""}}find(e,t){const{files:r,index:n}=this.getSearchIndex(e);if(r.length===0){console.log("No memories found.");return}const s=n.search(t,
|
|
33
|
+
`);for(const n of r){const s=n.hasBody?" \u{1F4C4}":"";const o=String(n.date).includes("T")?String(n.date).slice(0,16).replace("T"," "):n.date;console.log(`[${n.id}] [${o}] [${n.category??"-"}] ${n.summary}${s}`)}}getRecentEntries(e,t=30,r={}){const n=this.loadAllEntries(e);const s=new Date(Date.now()-t*24*60*60*1e3).toISOString().slice(0,10);const o=n.filter(i=>this.resolveRecentDate(e,i).slice(0,10)>=s);if(!r.includeBody)return o;return o.map(i=>({...i,body:this.readEntryBody(e,i.file)}))}resolveRecentDate(e,t){const r=this.toDateString(t.updated);if(r)return r;const n=this.toDateString(t["created_at"]);if(n)return n;const s=this.toDateString(t.date);if(s)return s;try{const o=D.default.join(this.getEntriesDir(e),t.file);return l.default.statSync(o).mtime.toISOString()}catch{return""}}toDateString(e){if(e&&typeof e==="object"&&typeof e.toISOString==="function"){return e.toISOString()}return typeof e==="string"?e:""}readEntryBody(e,t){try{const r=l.default.readFileSync(D.default.join(this.getEntriesDir(e),t),"utf-8");const{content:n}=(0,T.parseFrontmatter)(r);return n.replace(/^#[^\n]*\n*/m,"").trim()}catch{return""}}find(e,t){const{files:r,index:n}=this.getSearchIndex(e);if(r.length===0){console.log("No memories found.");return}const s=n.search(t,M+1);const o=s.length>M;const i=s.slice(0,M);if(i.length===0){console.log(`No related memories found for '${t}'.`);return}const d=o?`, top ${M}`:"";console.log(`## \u{1F50D} Search results: "${t}" (${i.length} items${d}, sorted by BM25 score)
|
|
34
34
|
`);for(const m of i){const u=m._score!=null?` [${m._score}pts]`:"";console.log(`[${m.id}] [${m.date}] [${this.getPrimaryTag(m)}]${u} ${m.summary}`)}const f=this.createFindActionContext(e,t,i);const a=(0,_.generateNextActions)(f);const c=(0,_.formatActions)(a);if(c)console.log(c)}list(e,t={}){const r=this.loadAllEntries(e);let n=r;if(t.category){n=n.filter(a=>a.category===t.category)}if(t.tag){const a=t.tag.split(",").map(c=>c.trim());n=n.filter(c=>a.every(m=>(c.tags??[]).includes(m)))}if(t.json){const a=this.getEntriesDir(e);const c=n.map(m=>{let u="";if(m.file){try{const y=l.default.readFileSync(D.default.join(a,m.file),"utf-8");const{content:g}=(0,T.parseFrontmatter)(y);const h=g.replace(/^#[^\n]*\n*/m,"").trim();if(h!=="(Add details here)")u=h}catch(y){}}return{id:m.id,title:m.summary??"",category:m.category??"general",tags:m.tags??[],created_at:m.date??"",content:u}});console.log(JSON.stringify(c,null,2));return}if(n.length===0){const a=[];if(t.category)a.push(`category=${t.category}`);if(t.tag)a.push(`tag=${t.tag}`);console.log(`No memories match the filters. (${a.join(", ")||"all"})`);return}console.log(`## \u{1F4CB} List: ${e} (${n.length} items)
|
|
35
35
|
`);for(const a of n){console.log(`[${a.id}] [${a.date}] [${this.getPrimaryTag(a)}] ${a.summary}`)}const s=[...new Set(n.flatMap(a=>a.tags??[]))].slice(0,3);const o=n.slice(0,3).map(a=>a.id);const i={commandId:"memory.list",params:{agentId:e,...t.category?{category:t.category}:{}},resultTags:s,resultIds:o,resultCount:n.length};const d=(0,_.generateNextActions)(i);const f=(0,_.formatActions)(d);if(f)console.log(f)}stats(e){const t=this.loadAllEntries(e);if(t.length===0){console.log("No memories found.");return}const r={};for(const a of t){const c=a.category??"general";r[c]=(r[c]??0)+1}const n={};for(const a of t){for(const c of a.tags??[]){n[c]=(n[c]??0)+1}}console.log(`# ${e} Stats (${t.length} items)
|
|
36
36
|
`);console.log(`## By category`);for(const[a,c]of Object.entries(r).sort((m,u)=>u[1]-m[1])){console.log(` ${a}: ${c} items`)}console.log(`
|
|
@@ -66,7 +66,7 @@ ${a}
|
|
|
66
66
|
`;for(const c of o)n+=`- [${c.date}] ${c.summary}
|
|
67
67
|
`;for(const c of o)n+=`\u2192 [entries/${c.file}](entries/${c.file})
|
|
68
68
|
`;n+=`
|
|
69
|
-
`}return n}callSummarizer(e){try{const t=k(["q",this.summarizerAgent],{encoding:"utf-8",timeout:this.summarizerTimeout,stdio:["pipe","pipe","pipe"],input:e});const r=
|
|
69
|
+
`}return n}callSummarizer(e){try{const t=k(["q",this.summarizerAgent],{encoding:"utf-8",timeout:this.summarizerTimeout,stdio:["pipe","pipe","pipe"],input:e});const r=O(t);this.recordSummarizerSuccess();return r}catch(t){const r=t;const n=r.code==="ETIMEDOUT"||r.killed?`timed out after ${this.summarizerTimeout}ms`:r.message;console.error(`[memory] callSummarizer failed (agent=${this.summarizerAgent}), falling back to raw entry list: ${n}`);this.recordSummarizerFailure((0,j.describeSummarizerFailure)(r,this.summarizerTimeout));return null}}generateSummary(e,t={}){const r=this.loadAllEntries(e);if(r.length===0)return{status:"empty"};const n=new Date(Date.now()-this.summaryDays*24*60*60*1e3).toISOString().slice(0,10);const s=t.force?r:r.filter(S=>S.date>=n);if(s.length===0)return{status:"empty",entryCount:r.length};const o=this.getSummaryPath(e);const i=l.default.existsSync(o)?(0,T.parseFrontmatter)(l.default.readFileSync(o,"utf-8")):null;const d=i?.data?.last_entry_id??null;const f=i?.content.trim()??null;const a=(S,A)=>{if(!A)return S;const x=S.findIndex(p=>p.id===A);if(x===-1)return S;return S.slice(0,x)};const c=a(s,d);if(c.length===0&&!t.force){return{status:"up-to-date",entryCount:r.length}}const m=t.force?s:c;const u=[...m].reverse().map(S=>{if(S.file){try{const A=l.default.readFileSync(D.default.join(this.getEntriesDir(e),S.file),"utf-8");const{content:x}=(0,T.parseFrontmatter)(A);const p=x.replace(/^#[^\n]*\n*/m,"").trim();return{...S,body:p}}catch(A){}}return S});const y=[...new Set(s.map(S=>this.getPrimaryTag(S)))].sort();const g=(S,A,x)=>{let p=`You are a memory summarizer for an AI agent.
|
|
70
70
|
|
|
71
71
|
`;if(S){p+=`## Existing summary:
|
|
72
72
|
${S}
|
|
@@ -83,7 +83,7 @@ ${S}
|
|
|
83
83
|
`;p+=`- If there's an existing summary, merge new information into it
|
|
84
84
|
`;p+=`- Topics to cover: ${x.join(", ")}
|
|
85
85
|
`;p+=`- Output ONLY the markdown body (topic sections), no frontmatter or top-level heading
|
|
86
|
-
`;return p};const h=g(t.force?null:f,u,y);const w=this.callSummarizer(h);const E=s[0]?.id;if(!w){const S=s.map(p=>{if(p.file){try{const b=l.default.readFileSync(D.default.join(this.getEntriesDir(e),p.file),"utf-8");const{content:C}=(0,T.parseFrontmatter)(b);const
|
|
86
|
+
`;return p};const h=g(t.force?null:f,u,y);const w=this.callSummarizer(h);const E=s[0]?.id;if(!w){const S=s.map(p=>{if(p.file){try{const b=l.default.readFileSync(D.default.join(this.getEntriesDir(e),p.file),"utf-8");const{content:C}=(0,T.parseFrontmatter)(b);const R=C.replace(/^#[^\n]*\n*/m,"").trim();return{...p,body:R}}catch(b){}}return p});const A=this.buildFallbackSummary(S);const x=(0,T.stringifyFrontmatter)({last_entry_id:E,last_updated:this.getToday(),entry_count:r.length,summary_days:this.summaryDays,summarizer:"fallback"});l.default.writeFileSync(o,`${x}
|
|
87
87
|
|
|
88
88
|
${A}`,"utf-8");return{status:"fallback",entryCount:r.length,newEntries:m.length}}const F=(0,T.stringifyFrontmatter)({last_entry_id:E,last_updated:this.getToday(),entry_count:r.length,summary_days:this.summaryDays,summarizer:"memory_summarizer"});l.default.writeFileSync(o,`${F}
|
|
89
89
|
|
|
@@ -91,9 +91,9 @@ ${w}
|
|
|
91
91
|
`,"utf-8");return{status:"updated",entryCount:r.length,newEntries:m.length}}summarize(e,t={}){const r=this.loadAllEntries(e);if(r.length===0){console.log("No memories found.");return}console.log(`\u{1F4DD} Starting summary generation (${r.length} items entries)
|
|
92
92
|
`);const n=this.generateSummary(e,{force:t.force});if(n.status==="up-to-date"){console.log("\u2705 summary.md is up to date.")}else if(n.status==="fallback"){console.log(`\u{1F4CB} Fallback summary generated (AI call failed)`)}else if(n.status==="updated"){console.log(`\u{1F916} Summary complete (${n.newEntries} items applied)`)}console.log(` File: data/${e}/summary.md`);console.log(` entries: ${n.entryCount} items`)}isSummarizeLockStale(e){let t;try{t=l.default.readFileSync(e,"utf-8")}catch{return true}const r=t.match(/^pid=(\d+)/m);const n=r?Number(r[1]):NaN;if(Number.isFinite(n)){try{process.kill(n,0);return false}catch(s){const o=s;if(o.code==="ESRCH")return true}}try{const s=l.default.statSync(e);return Date.now()-s.mtimeMs>exports.SUMMARIZE_LOCK_STALE_MS}catch{return true}}acquireSummarizeLock(){const e=this.getSummarizeLockPath();const t=`pid=${process.pid}
|
|
93
93
|
mtime=${new Date().toISOString()}
|
|
94
|
-
`;try{const r=l.default.openSync(e,"wx");l.default.writeSync(r,t);l.default.closeSync(r);return true}catch(r){const n=r;if(n.code!=="EEXIST")throw n}if(!this.isSummarizeLockStale(e)){return false}l.default.writeFileSync(e,t,"utf-8");return true}releaseSummarizeLock(){try{l.default.unlinkSync(this.getSummarizeLockPath())}catch{}}summarizeDirty(){if(!l.default.existsSync(this.dataDir)){console.log(`\u{1F4A1} Register debounce cron:`);console.log(`
|
|
94
|
+
`;try{const r=l.default.openSync(e,"wx");l.default.writeSync(r,t);l.default.closeSync(r);return true}catch(r){const n=r;if(n.code!=="EEXIST")throw n}if(!this.isSummarizeLockStale(e)){return false}l.default.writeFileSync(e,t,"utf-8");return true}releaseSummarizeLock(){try{l.default.unlinkSync(this.getSummarizeLockPath())}catch{}}summarizeDirty(){if(!l.default.existsSync(this.dataDir)){console.log(`\u{1F4A1} Register debounce cron:`);console.log(`crewx cron add "*/1 * * * *" "crewx memory summarize-dirty" --mode command --name "memory-debounce"`);return}if(!this.acquireSummarizeLock()){console.log(`\u23ED\uFE0F summarize-dirty is already running (lock held) \u2014 skipping this run.`);return}try{const e=l.default.readdirSync(this.dataDir,{withFileTypes:true}).filter(t=>t.isDirectory()).map(t=>t.name);for(const t of e){const r=this.getDirtySummaryPath(t);if(!l.default.existsSync(r))continue;try{const n=this.generateSummary(t);if(n.status==="updated"){console.log(`\u{1F916} ${t} summary.md updated (${n.newEntries} items applied)`)}else if(n.status==="fallback"){console.log(`\u{1F4CB} ${t} Fallback summary generated (AI call failed)`)}else if(n.status==="up-to-date"){console.log(`\u2705 ${t} summary.md is up to date.`)}else if(n.status==="empty"){console.log(`\u2139\uFE0F ${t} has no memories to summarize.`)}l.default.unlinkSync(r)}catch(n){console.error(`\u274C ${t} summary.md update failed: ${n.message}`)}}}finally{this.releaseSummarizeLock()}console.log(`\u{1F4A1} Register debounce cron:`);console.log(`crewx cron add "*/1 * * * *" "crewx memory summarize-dirty" --mode command --name "memory-debounce"`)}search(e,t){const r=this.loadAllEntries(e);if(r.length===0){console.log("No memories found.");return}const n=r.map(o=>`[${o.id}] [${o.date}] [${this.getPrimaryTag(o)}] ${o.summary}`).join("\n");const s=`Memory list:
|
|
95
95
|
${n}
|
|
96
96
|
|
|
97
97
|
Question: "${t}"`;try{console.log(`\u{1F50D} "${t}" searching... (${this.searcherAgent})
|
|
98
|
-
`);const o=k(["q",`${this.searcherAgent} ${s}`],{encoding:"utf-8",timeout:this.searcherTimeout,stdio:["pipe","pipe","pipe"]});const i=
|
|
99
|
-
`);console.log(i)}else{console.log(o.trim())}}catch(o){const i=o;if(i.code==="ETIMEDOUT"||i.killed){console.log("Search failed: timed out (max 5 minutes)")}else{console.log("Search failed:",i.message)}console.log("\n\u{1F4A1} Tip: try keyword search with the find command.")}}}exports.MemoryEngine=
|
|
98
|
+
`);const o=k(["q",`${this.searcherAgent} ${s}`],{encoding:"utf-8",timeout:this.searcherTimeout,stdio:["pipe","pipe","pipe"]});const i=O(o);if(i){console.log(`## \u{1F9E0} Semantic search results
|
|
99
|
+
`);console.log(i)}else{console.log(o.trim())}}catch(o){const i=o;if(i.code==="ETIMEDOUT"||i.killed){console.log("Search failed: timed out (max 5 minutes)")}else{console.log("Search failed:",i.message)}console.log("\n\u{1F4A1} Tip: try keyword search with the find command.")}}}exports.MemoryEngine=L;function Z($){return new L($)}var Y=require("./types");Object.defineProperty(exports,"CATEGORIES",{enumerable:true,get:function(){return Y.CATEGORIES}});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const SUMMARIZER_CAUSE_MESSAGE_MAX_CHARS = 200;
|
|
2
|
+
export interface SummarizerFailureCause {
|
|
3
|
+
code?: string;
|
|
4
|
+
message?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function describeSummarizerFailure(error: unknown, timeoutMs: number): SummarizerFailureCause;
|
|
7
|
+
export declare function formatSummarizerLastError(cause: SummarizerFailureCause | undefined): string;
|
|
8
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.SUMMARIZER_CAUSE_MESSAGE_MAX_CHARS=void 0;exports.describeSummarizerFailure=a;exports.formatSummarizerLastError=u;const c=require("@crewx/sdk");exports.SUMMARIZER_CAUSE_MESSAGE_MAX_CHARS=200;function a(e,t){const n=typeof e==="object"&&e!==null?e:{};const r=m(n,t);if(!r)return{};const s={};const i=(0,c.classifyTaskError)({status:"failed",error:r}).errorCode;if(i)s.code=i;const o=d((0,c.redactTechnicalMessage)(r)??"");if(o)s.message=o;return s}function u(e){if(e?.code&&e.message)return`${e.code} \u2014 ${e.message}`;return e?.code??e?.message??"unknown"}function m(e,t){if(e.code==="ETIMEDOUT"||e.killed===true)return`timed out after ${t}ms`;const n=String(e.stderr??"").split(/\r?\n/).map(r=>r.trim()).filter(r=>r.length>0&&!r.startsWith("[crewx] "));if(n.length>0)return n[n.length-1];return typeof e.code==="string"?String(e.message??""):void 0}function d(e){const t=e.replace(/\s+/g," ").trim();return Array.from(t).slice(0,exports.SUMMARIZER_CAUSE_MESSAGE_MAX_CHARS).join("")}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewx/memory",
|
|
3
|
-
"version": "0.1.23-rc.
|
|
3
|
+
"version": "0.1.23-rc.193",
|
|
4
4
|
"description": "Memory engine for CrewX - markdown + frontmatter based long-term memory",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/src/engine.js",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"gray-matter": "^4.0.3",
|
|
37
37
|
"js-yaml": "^4.1.0",
|
|
38
38
|
"nanoid": "^3.3.11",
|
|
39
|
-
"@crewx/sdk": "0.9.0-rc.
|
|
40
|
-
"@crewx/
|
|
41
|
-
"@crewx/
|
|
39
|
+
"@crewx/sdk": "0.9.0-rc.176",
|
|
40
|
+
"@crewx/knowledge-core": "0.1.17-rc.186",
|
|
41
|
+
"@crewx/shared": "0.0.6-rc.91"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/js-yaml": "^4.0.9",
|