metame-cli 1.5.19 → 1.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/index.js +157 -80
  2. package/package.json +2 -2
  3. package/scripts/bin/bootstrap-worktree.sh +20 -0
  4. package/scripts/core/audit.js +190 -0
  5. package/scripts/core/handoff.js +780 -0
  6. package/scripts/core/handoff.test.js +1074 -0
  7. package/scripts/core/memory-model.js +183 -0
  8. package/scripts/core/memory-model.test.js +486 -0
  9. package/scripts/core/reactive-paths.js +44 -0
  10. package/scripts/core/reactive-paths.test.js +35 -0
  11. package/scripts/core/reactive-prompt.js +51 -0
  12. package/scripts/core/reactive-prompt.test.js +88 -0
  13. package/scripts/core/reactive-signal.js +40 -0
  14. package/scripts/core/reactive-signal.test.js +88 -0
  15. package/scripts/core/thread-chat-id.js +52 -0
  16. package/scripts/core/thread-chat-id.test.js +113 -0
  17. package/scripts/daemon-bridges.js +92 -38
  18. package/scripts/daemon-claude-engine.js +373 -444
  19. package/scripts/daemon-command-router.js +82 -8
  20. package/scripts/daemon-engine-runtime.js +7 -10
  21. package/scripts/daemon-reactive-lifecycle.js +100 -33
  22. package/scripts/daemon-session-commands.js +133 -43
  23. package/scripts/daemon-session-store.js +300 -82
  24. package/scripts/daemon-team-dispatch.js +16 -16
  25. package/scripts/daemon.js +21 -175
  26. package/scripts/deploy-manifest.js +90 -0
  27. package/scripts/docs/maintenance-manual.md +14 -11
  28. package/scripts/docs/pointer-map.md +13 -4
  29. package/scripts/feishu-adapter.js +31 -27
  30. package/scripts/hooks/intent-engine.js +6 -3
  31. package/scripts/hooks/intent-memory-recall.js +1 -0
  32. package/scripts/hooks/intent-perpetual.js +1 -1
  33. package/scripts/memory-extract.js +5 -97
  34. package/scripts/memory-gc.js +35 -90
  35. package/scripts/memory-migrate-v2.js +304 -0
  36. package/scripts/memory-nightly-reflect.js +40 -41
  37. package/scripts/memory.js +340 -859
  38. package/scripts/migrate-reactive-paths.js +122 -0
  39. package/scripts/signal-capture.js +4 -0
  40. package/scripts/sync-plugin.js +56 -0
@@ -33,7 +33,7 @@ const MIN_SEARCH_COUNT = 3;
33
33
  const WINDOW_DAYS = 7;
34
34
  const MAX_FACTS = 20;
35
35
  const LOCK_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
36
- const EXCLUDED_RELATIONS = ['project_milestone', 'synthesized_insight', 'knowledge_capsule', 'bug_lesson'];
36
+ const EXCLUDED_KINDS = ['project_milestone', 'synthesized_insight', 'knowledge_capsule', 'bug_lesson'];
37
37
 
38
38
  // Ensure output directories exist at startup
39
39
  [MEMORY_DIR, DECISIONS_DIR, LESSONS_DIR, CAPSULES_DIR].forEach(d => fs.mkdirSync(d, { recursive: true }));
@@ -109,19 +109,18 @@ function writeReflectLog(record) {
109
109
  * Returns array of plain objects.
110
110
  */
111
111
  function queryHotFacts(db, windowDays = WINDOW_DAYS) {
112
- const relationPlaceholders = EXCLUDED_RELATIONS.map(() => '?').join(', ');
112
+ const kindPlaceholders = EXCLUDED_KINDS.map(() => '?').join(', ');
113
113
  const stmt = db.prepare(`
114
- SELECT id, entity, relation, value, confidence, search_count, created_at
115
- FROM facts
114
+ SELECT id, title, kind, content, confidence, search_count, created_at
115
+ FROM memory_items
116
116
  WHERE search_count >= ${MIN_SEARCH_COUNT}
117
117
  AND created_at >= datetime('now', '-${windowDays} days')
118
- AND superseded_by IS NULL
119
- AND (conflict_status IS NULL OR conflict_status = 'OK')
120
- AND relation NOT IN (${relationPlaceholders})
118
+ AND state = 'active'
119
+ AND kind NOT IN (${kindPlaceholders})
121
120
  ORDER BY search_count DESC, created_at DESC
122
121
  LIMIT ${MAX_FACTS}
123
122
  `);
124
- return stmt.all(...EXCLUDED_RELATIONS);
123
+ return stmt.all(...EXCLUDED_KINDS);
125
124
  }
126
125
 
127
126
  /**
@@ -194,7 +193,7 @@ function entityPrefix(entity) {
194
193
  function collectCapsuleGroups(facts, minGroupSize = 3) {
195
194
  const groups = new Map();
196
195
  for (const fact of Array.isArray(facts) ? facts : []) {
197
- const prefix = entityPrefix(fact && fact.entity);
196
+ const prefix = entityPrefix(fact && fact.title);
198
197
  if (!prefix) continue;
199
198
  if (!groups.has(prefix)) groups.set(prefix, []);
200
199
  groups.get(prefix).push(fact);
@@ -304,9 +303,9 @@ async function run() {
304
303
 
305
304
  const factsJson = JSON.stringify(
306
305
  hotFacts.map(f => ({
307
- entity: f.entity,
308
- relation: f.relation,
309
- value: f.value,
306
+ title: f.title,
307
+ kind: f.kind,
308
+ content: f.content,
310
309
  confidence: f.confidence,
311
310
  search_count: f.search_count,
312
311
  })),
@@ -406,9 +405,9 @@ Rules:
406
405
 
407
406
  const sourceItems = factsForGroup ? factsForGroup.items : group.items;
408
407
  const groupFacts = sourceItems.map(f => ({
409
- entity: f.entity,
410
- relation: f.relation,
411
- value: f.value,
408
+ title: f.title,
409
+ kind: f.kind,
410
+ content: f.content,
412
411
  search_count: f.search_count,
413
412
  }));
414
413
  const capsulePrompt = playbookExists
@@ -504,15 +503,15 @@ entity_prefix: ${group.prefix}
504
503
  }
505
504
 
506
505
  // ── Conflict Resolution ──────────────────────────────────────────────
507
- // Query CONFLICT facts grouped by entity+relation, ask Haiku to pick winner.
508
- // Loser is marked superseded_by winner; winner restored to OK.
506
+ // Query CONFLICT memory_items grouped by title+kind, ask Haiku to pick winner.
507
+ // Loser is archived with supersedes_id; winner restored to active.
509
508
  let conflictsResolved = 0;
510
509
  try {
511
510
  const conflictGroups = db.prepare(`
512
- SELECT entity, relation, COUNT(*) as cnt
513
- FROM facts
514
- WHERE conflict_status = 'CONFLICT' AND superseded_by IS NULL
515
- GROUP BY entity, relation
511
+ SELECT title, kind, COUNT(*) as cnt
512
+ FROM memory_items
513
+ WHERE state = 'conflict'
514
+ GROUP BY title, kind
516
515
  HAVING cnt >= 2
517
516
  ORDER BY cnt DESC
518
517
  LIMIT 10
@@ -525,23 +524,23 @@ entity_prefix: ${group.prefix}
525
524
  const allConflicts = [];
526
525
  for (const g of conflictGroups) {
527
526
  const rows = db.prepare(`
528
- SELECT id, entity, relation, value, confidence, created_at
529
- FROM facts
530
- WHERE entity = ? AND relation = ? AND conflict_status = 'CONFLICT' AND superseded_by IS NULL
527
+ SELECT id, title, kind, content, confidence, created_at
528
+ FROM memory_items
529
+ WHERE title = ? AND kind = ? AND state = 'conflict'
531
530
  ORDER BY created_at DESC
532
- `).all(g.entity, g.relation);
533
- if (rows.length >= 2) allConflicts.push({ entity: g.entity, relation: g.relation, facts: rows });
531
+ `).all(g.title, g.kind);
532
+ if (rows.length >= 2) allConflicts.push({ title: g.title, kind: g.kind, facts: rows });
534
533
  }
535
534
 
536
535
  if (allConflicts.length > 0) {
537
536
  // Limit to 5 groups to avoid truncating serialized JSON
538
537
  const conflictInput = allConflicts.slice(0, 5).map(g => ({
539
- entity: g.entity,
540
- relation: g.relation,
541
- candidates: g.facts.slice(0, 5).map(f => ({ id: f.id, value: f.value.slice(0, 150), confidence: f.confidence, created_at: f.created_at })),
538
+ title: g.title,
539
+ kind: g.kind,
540
+ candidates: g.facts.slice(0, 5).map(f => ({ id: f.id, content: f.content.slice(0, 150), confidence: f.confidence, created_at: f.created_at })),
542
541
  }));
543
542
 
544
- const resolvePrompt = `你是知识库冲突调解员。以下是同一 entity+relation 下的冲突事实,请选出每组最准确的一条保留。
543
+ const resolvePrompt = `你是知识库冲突调解员。以下是同一 title+kind 下的冲突记忆条目,请选出每组最准确的一条保留。
545
544
 
546
545
  冲突组(JSON):
547
546
  ${JSON.stringify(conflictInput, null, 2)}
@@ -549,15 +548,15 @@ ${JSON.stringify(conflictInput, null, 2)}
549
548
  输出 JSON 数组,每个元素对应一组冲突的裁决:
550
549
  [
551
550
  {
552
- "entity": "...",
553
- "relation": "...",
554
- "winner_id": "保留的fact id",
551
+ "title": "...",
552
+ "kind": "...",
553
+ "winner_id": "保留的item id",
555
554
  "reason": "一句话理由"
556
555
  }
557
556
  ]
558
557
 
559
558
  规则:
560
- - 优先选最新(created_at)且 confidence=high
559
+ - 优先选最新(created_at)且 confidence 高的
561
560
  - 如果旧条目更准确具体,选旧的
562
561
  - 只输出 JSON 数组`;
563
562
 
@@ -569,9 +568,9 @@ ${JSON.stringify(conflictInput, null, 2)}
569
568
  const verdicts = parseJsonFromLlm(resolveRaw);
570
569
  if (Array.isArray(verdicts)) {
571
570
  for (const v of verdicts) {
572
- if (!v || !v.winner_id || !v.entity || !v.relation) continue;
571
+ if (!v || !v.winner_id || !v.title || !v.kind) continue;
573
572
  // Validate winner exists in our conflict set
574
- const group = allConflicts.find(g => g.entity === v.entity && g.relation === v.relation);
573
+ const group = allConflicts.find(g => g.title === v.title && g.kind === v.kind);
575
574
  if (!group) continue;
576
575
  const winnerExists = group.facts.some(f => f.id === v.winner_id);
577
576
  if (!winnerExists) continue;
@@ -579,16 +578,16 @@ ${JSON.stringify(conflictInput, null, 2)}
579
578
  const loserIds = group.facts.filter(f => f.id !== v.winner_id).map(f => f.id);
580
579
  if (loserIds.length === 0) continue;
581
580
 
582
- // Mark losers as superseded
581
+ // Mark losers as archived (superseded by winner)
583
582
  const placeholders = loserIds.map(() => '?').join(',');
584
583
  db.prepare(
585
- `UPDATE facts SET superseded_by = ?, conflict_status = 'OK', updated_at = datetime('now')
584
+ `UPDATE memory_items SET supersedes_id = ?, state = 'archived', updated_at = datetime('now')
586
585
  WHERE id IN (${placeholders})`
587
586
  ).run(v.winner_id, ...loserIds);
588
587
 
589
- // Restore winner
588
+ // Restore winner to active
590
589
  db.prepare(
591
- `UPDATE facts SET conflict_status = 'OK', updated_at = datetime('now') WHERE id = ?`
590
+ `UPDATE memory_items SET state = 'active', updated_at = datetime('now') WHERE id = ?`
592
591
  ).run(v.winner_id);
593
592
 
594
593
  conflictsResolved += loserIds.length;
@@ -652,6 +651,6 @@ module.exports = {
652
651
  collectCapsuleGroups,
653
652
  entityPrefix,
654
653
  parseJsonFromLlm,
655
- EXCLUDED_RELATIONS,
654
+ EXCLUDED_KINDS,
656
655
  },
657
656
  };