@askexenow/exe-os 0.9.61 → 0.9.63

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 (103) hide show
  1. package/deploy/stack-manifests/v0.9.json +62 -13
  2. package/dist/bin/backfill-conversations.js +210 -2
  3. package/dist/bin/backfill-responses.js +210 -2
  4. package/dist/bin/backfill-vectors.js +47 -2
  5. package/dist/bin/cleanup-stale-review-tasks.js +210 -2
  6. package/dist/bin/cli.js +488 -25
  7. package/dist/bin/exe-agent-config.js +2 -2
  8. package/dist/bin/exe-agent.js +2 -2
  9. package/dist/bin/exe-assign.js +210 -2
  10. package/dist/bin/exe-boot.js +47 -2
  11. package/dist/bin/exe-call.js +2 -2
  12. package/dist/bin/exe-cloud.js +2 -2
  13. package/dist/bin/exe-dispatch.js +210 -2
  14. package/dist/bin/exe-doctor.js +2691 -1212
  15. package/dist/bin/exe-export-behaviors.js +210 -2
  16. package/dist/bin/exe-forget.js +264 -2
  17. package/dist/bin/exe-gateway.js +210 -2
  18. package/dist/bin/exe-heartbeat.js +212 -4
  19. package/dist/bin/exe-kill.js +210 -2
  20. package/dist/bin/exe-launch-agent.js +210 -2
  21. package/dist/bin/exe-link.js +47 -2
  22. package/dist/bin/exe-new-employee.js +113 -3
  23. package/dist/bin/exe-pending-messages.js +210 -2
  24. package/dist/bin/exe-pending-notifications.js +210 -2
  25. package/dist/bin/exe-pending-reviews.js +210 -2
  26. package/dist/bin/exe-rename.js +210 -2
  27. package/dist/bin/exe-review.js +210 -2
  28. package/dist/bin/exe-search.js +234 -3
  29. package/dist/bin/exe-session-cleanup.js +210 -2
  30. package/dist/bin/exe-settings.js +2 -2
  31. package/dist/bin/exe-start-codex.js +322 -4
  32. package/dist/bin/exe-start-opencode.js +336 -4
  33. package/dist/bin/exe-status.js +210 -2
  34. package/dist/bin/exe-team.js +210 -2
  35. package/dist/bin/git-sweep.js +210 -2
  36. package/dist/bin/graph-backfill.js +210 -2
  37. package/dist/bin/graph-export.js +210 -2
  38. package/dist/bin/install.js +113 -3
  39. package/dist/bin/intercom-check.js +210 -2
  40. package/dist/bin/scan-tasks.js +210 -2
  41. package/dist/bin/setup.js +47 -2
  42. package/dist/bin/shard-migrate.js +210 -2
  43. package/dist/bin/stack-update.js +158 -13
  44. package/dist/bin/update.js +2 -2
  45. package/dist/gateway/index.js +210 -2
  46. package/dist/hooks/bug-report-worker.js +321 -44
  47. package/dist/hooks/codex-stop-task-finalizer.js +210 -2
  48. package/dist/hooks/commit-complete.js +210 -2
  49. package/dist/hooks/error-recall.js +234 -3
  50. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  51. package/dist/hooks/ingest-worker.js +2 -2
  52. package/dist/hooks/ingest.js +210 -2
  53. package/dist/hooks/instructions-loaded.js +210 -2
  54. package/dist/hooks/notification.js +210 -2
  55. package/dist/hooks/post-compact.js +210 -2
  56. package/dist/hooks/post-tool-combined.js +234 -3
  57. package/dist/hooks/pre-compact.js +446 -111
  58. package/dist/hooks/pre-tool-use.js +210 -2
  59. package/dist/hooks/prompt-submit.js +234 -3
  60. package/dist/hooks/session-end.js +347 -124
  61. package/dist/hooks/session-start.js +236 -5
  62. package/dist/hooks/stop.js +210 -2
  63. package/dist/hooks/subagent-stop.js +210 -2
  64. package/dist/hooks/summary-worker.js +171 -74
  65. package/dist/index.js +210 -2
  66. package/dist/lib/agent-config.js +2 -2
  67. package/dist/lib/cloud-sync.js +47 -2
  68. package/dist/lib/config.js +2 -2
  69. package/dist/lib/consolidation.js +2 -2
  70. package/dist/lib/database.js +47 -2
  71. package/dist/lib/db-daemon-client.js +2 -2
  72. package/dist/lib/db.js +47 -2
  73. package/dist/lib/device-registry.js +47 -2
  74. package/dist/lib/embedder.js +2 -2
  75. package/dist/lib/employee-templates.js +2 -2
  76. package/dist/lib/employees.js +2 -2
  77. package/dist/lib/exe-daemon-client.js +2 -2
  78. package/dist/lib/exe-daemon.js +592 -10
  79. package/dist/lib/hybrid-search.js +234 -3
  80. package/dist/lib/identity.js +2 -2
  81. package/dist/lib/license.js +2 -2
  82. package/dist/lib/messaging.js +2 -2
  83. package/dist/lib/reminders.js +2 -2
  84. package/dist/lib/schedules.js +47 -2
  85. package/dist/lib/skill-learning.js +2 -2
  86. package/dist/lib/store.js +210 -2
  87. package/dist/lib/task-router.js +2 -2
  88. package/dist/lib/tasks.js +2 -2
  89. package/dist/lib/tmux-routing.js +2 -2
  90. package/dist/lib/token-spend.js +2 -2
  91. package/dist/mcp/server.js +608 -10
  92. package/dist/mcp/tools/complete-reminder.js +2 -2
  93. package/dist/mcp/tools/create-reminder.js +2 -2
  94. package/dist/mcp/tools/create-task.js +2 -2
  95. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  96. package/dist/mcp/tools/list-reminders.js +2 -2
  97. package/dist/mcp/tools/list-tasks.js +2 -2
  98. package/dist/mcp/tools/send-message.js +2 -2
  99. package/dist/mcp/tools/update-task.js +2 -2
  100. package/dist/runtime/index.js +210 -2
  101. package/dist/tui/App.js +210 -2
  102. package/package.json +3 -2
  103. package/stack.release.json +23 -7
@@ -623,8 +623,8 @@ var init_config = __esm({
623
623
  rerankerAutoTrigger: {
624
624
  enabled: true,
625
625
  broadQueryMinCardinality: 5e4,
626
- fetchTopK: 150,
627
- returnTopK: 5
626
+ fetchTopK: 200,
627
+ returnTopK: 20
628
628
  }
629
629
  },
630
630
  graphRagEnabled: true,
@@ -3366,6 +3366,51 @@ async function ensureSchema() {
3366
3366
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3367
3367
  END;
3368
3368
  `);
3369
+ await client.executeMultiple(`
3370
+ CREATE TABLE IF NOT EXISTS memory_cards (
3371
+ id TEXT PRIMARY KEY,
3372
+ memory_id TEXT NOT NULL,
3373
+ agent_id TEXT NOT NULL,
3374
+ session_id TEXT NOT NULL,
3375
+ project_name TEXT,
3376
+ timestamp TEXT NOT NULL,
3377
+ card_type TEXT NOT NULL,
3378
+ subject TEXT,
3379
+ predicate TEXT,
3380
+ object TEXT,
3381
+ content TEXT NOT NULL,
3382
+ source_ref TEXT,
3383
+ confidence REAL DEFAULT 0.6,
3384
+ active INTEGER DEFAULT 1,
3385
+ created_at TEXT NOT NULL
3386
+ );
3387
+
3388
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3389
+ ON memory_cards(agent_id, active, timestamp);
3390
+
3391
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3392
+ ON memory_cards(memory_id);
3393
+
3394
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3395
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3396
+
3397
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3398
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3399
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3400
+ END;
3401
+
3402
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3403
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3404
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3405
+ END;
3406
+
3407
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3408
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3409
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3410
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3411
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3412
+ END;
3413
+ `);
3369
3414
  try {
3370
3415
  await client.execute({
3371
3416
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4668,6 +4713,164 @@ ${p.content}`).join("\n\n");
4668
4713
  }
4669
4714
  });
4670
4715
 
4716
+ // src/lib/memory-cards.ts
4717
+ var memory_cards_exports = {};
4718
+ __export(memory_cards_exports, {
4719
+ extractMemoryCards: () => extractMemoryCards,
4720
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4721
+ searchMemoryCards: () => searchMemoryCards
4722
+ });
4723
+ import { createHash as createHash2 } from "crypto";
4724
+ function stableId(memoryId, type, content) {
4725
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4726
+ }
4727
+ function cleanText(text) {
4728
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4729
+ }
4730
+ function splitSentences(text) {
4731
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4732
+ }
4733
+ function inferCardType(sentence, toolName) {
4734
+ const lower = sentence.toLowerCase();
4735
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4736
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4737
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4738
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4739
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4740
+ return "fact";
4741
+ }
4742
+ function extractSubject(sentence, agentId) {
4743
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4744
+ return explicit?.[1] ?? agentId;
4745
+ }
4746
+ function predicateFor(type) {
4747
+ switch (type) {
4748
+ case "preference":
4749
+ return "prefers";
4750
+ case "belief_update":
4751
+ return "updated";
4752
+ case "decision":
4753
+ return "decided";
4754
+ case "event":
4755
+ return "happened";
4756
+ case "code":
4757
+ return "implemented";
4758
+ default:
4759
+ return "states";
4760
+ }
4761
+ }
4762
+ function extractMemoryCards(row) {
4763
+ const sentences = splitSentences(row.raw_text);
4764
+ const cards = [];
4765
+ for (const sentence of sentences) {
4766
+ const type = inferCardType(sentence, row.tool_name);
4767
+ const subject = extractSubject(sentence, row.agent_id);
4768
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4769
+ cards.push({
4770
+ id: stableId(row.id, type, content),
4771
+ memory_id: row.id,
4772
+ agent_id: row.agent_id,
4773
+ session_id: row.session_id,
4774
+ project_name: row.project_name ?? null,
4775
+ timestamp: row.timestamp,
4776
+ card_type: type,
4777
+ subject,
4778
+ predicate: predicateFor(type),
4779
+ object: content,
4780
+ content,
4781
+ source_ref: row.id,
4782
+ confidence: type === "fact" ? 0.55 : 0.65
4783
+ });
4784
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4785
+ }
4786
+ return cards;
4787
+ }
4788
+ async function insertMemoryCardsForBatch(rows) {
4789
+ const cards = rows.flatMap(extractMemoryCards);
4790
+ if (cards.length === 0) return 0;
4791
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4792
+ const client = getClient();
4793
+ const stmts = cards.map((card) => ({
4794
+ sql: `INSERT OR IGNORE INTO memory_cards
4795
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4796
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4797
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4798
+ args: [
4799
+ card.id,
4800
+ card.memory_id,
4801
+ card.agent_id,
4802
+ card.session_id,
4803
+ card.project_name,
4804
+ card.timestamp,
4805
+ card.card_type,
4806
+ card.subject,
4807
+ card.predicate,
4808
+ card.object,
4809
+ card.content,
4810
+ card.source_ref,
4811
+ card.confidence,
4812
+ now
4813
+ ]
4814
+ }));
4815
+ await client.batch(stmts, "write");
4816
+ return cards.length;
4817
+ }
4818
+ function buildMatchExpr(queryText) {
4819
+ const terms = queryText.toLowerCase().split(/\s+/).filter((t) => t.length >= 3).map((t) => t.replace(/[^a-z0-9_]/g, "")).filter((t) => t.length >= 3).slice(0, 12);
4820
+ if (terms.length === 0) return null;
4821
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4822
+ }
4823
+ async function searchMemoryCards(queryText, agentId, options) {
4824
+ const limit = options?.limit ?? 10;
4825
+ const matchExpr = buildMatchExpr(queryText);
4826
+ if (!matchExpr) return [];
4827
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4828
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4829
+ FROM memory_cards c
4830
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4831
+ WHERE memory_cards_fts MATCH ?
4832
+ AND c.agent_id = ?
4833
+ AND COALESCE(c.active, 1) = 1`;
4834
+ const args = [matchExpr, agentId];
4835
+ if (options?.projectName) {
4836
+ sql += ` AND c.project_name = ?`;
4837
+ args.push(options.projectName);
4838
+ }
4839
+ if (options?.since) {
4840
+ sql += ` AND c.timestamp >= ?`;
4841
+ args.push(options.since);
4842
+ }
4843
+ sql += ` ORDER BY rank LIMIT ?`;
4844
+ args.push(limit);
4845
+ const result = await getClient().execute({ sql, args });
4846
+ return result.rows.map((row) => ({
4847
+ id: `card:${String(row.id)}`,
4848
+ agent_id: String(row.agent_id),
4849
+ agent_role: "memory_card",
4850
+ session_id: String(row.session_id),
4851
+ timestamp: String(row.timestamp),
4852
+ tool_name: `memory_card:${String(row.card_type)}`,
4853
+ project_name: row.project_name == null ? "" : String(row.project_name),
4854
+ has_error: false,
4855
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4856
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4857
+ vector: [],
4858
+ importance: 6,
4859
+ status: "active",
4860
+ confidence: Number(row.confidence ?? 0.6),
4861
+ last_accessed: String(row.timestamp)
4862
+ }));
4863
+ }
4864
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4865
+ var init_memory_cards = __esm({
4866
+ "src/lib/memory-cards.ts"() {
4867
+ "use strict";
4868
+ init_database();
4869
+ MAX_CARDS_PER_MEMORY = 6;
4870
+ MAX_SENTENCE_CHARS = 360;
4871
+ }
4872
+ });
4873
+
4671
4874
  // src/lib/store.ts
4672
4875
  var store_exports = {};
4673
4876
  __export(store_exports, {
@@ -5006,6 +5209,11 @@ async function flushBatch() {
5006
5209
  const globalClient = getClient();
5007
5210
  const globalStmts = batch.map(buildStmt);
5008
5211
  await globalClient.batch(globalStmts, "write");
5212
+ try {
5213
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
5214
+ await insertMemoryCardsForBatch2(batch);
5215
+ } catch {
5216
+ }
5009
5217
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
5010
5218
  _pendingRecords.splice(0, batch.length);
5011
5219
  try {
@@ -312,8 +312,8 @@ var init_config = __esm({
312
312
  rerankerAutoTrigger: {
313
313
  enabled: true,
314
314
  broadQueryMinCardinality: 5e4,
315
- fetchTopK: 150,
316
- returnTopK: 5
315
+ fetchTopK: 200,
316
+ returnTopK: 20
317
317
  }
318
318
  },
319
319
  graphRagEnabled: true,
@@ -3121,6 +3121,51 @@ async function ensureSchema() {
3121
3121
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3122
3122
  END;
3123
3123
  `);
3124
+ await client.executeMultiple(`
3125
+ CREATE TABLE IF NOT EXISTS memory_cards (
3126
+ id TEXT PRIMARY KEY,
3127
+ memory_id TEXT NOT NULL,
3128
+ agent_id TEXT NOT NULL,
3129
+ session_id TEXT NOT NULL,
3130
+ project_name TEXT,
3131
+ timestamp TEXT NOT NULL,
3132
+ card_type TEXT NOT NULL,
3133
+ subject TEXT,
3134
+ predicate TEXT,
3135
+ object TEXT,
3136
+ content TEXT NOT NULL,
3137
+ source_ref TEXT,
3138
+ confidence REAL DEFAULT 0.6,
3139
+ active INTEGER DEFAULT 1,
3140
+ created_at TEXT NOT NULL
3141
+ );
3142
+
3143
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3144
+ ON memory_cards(agent_id, active, timestamp);
3145
+
3146
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3147
+ ON memory_cards(memory_id);
3148
+
3149
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3150
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3151
+
3152
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3153
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3154
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3155
+ END;
3156
+
3157
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3158
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3159
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3160
+ END;
3161
+
3162
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3163
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3164
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3165
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3166
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3167
+ END;
3168
+ `);
3124
3169
  try {
3125
3170
  await client.execute({
3126
3171
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4410,6 +4455,164 @@ ${p.content}`).join("\n\n");
4410
4455
  }
4411
4456
  });
4412
4457
 
4458
+ // src/lib/memory-cards.ts
4459
+ var memory_cards_exports = {};
4460
+ __export(memory_cards_exports, {
4461
+ extractMemoryCards: () => extractMemoryCards,
4462
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4463
+ searchMemoryCards: () => searchMemoryCards
4464
+ });
4465
+ import { createHash as createHash2 } from "crypto";
4466
+ function stableId(memoryId, type, content) {
4467
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4468
+ }
4469
+ function cleanText(text) {
4470
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4471
+ }
4472
+ function splitSentences(text) {
4473
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4474
+ }
4475
+ function inferCardType(sentence, toolName) {
4476
+ const lower = sentence.toLowerCase();
4477
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4478
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4479
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4480
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4481
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4482
+ return "fact";
4483
+ }
4484
+ function extractSubject(sentence, agentId) {
4485
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4486
+ return explicit?.[1] ?? agentId;
4487
+ }
4488
+ function predicateFor(type) {
4489
+ switch (type) {
4490
+ case "preference":
4491
+ return "prefers";
4492
+ case "belief_update":
4493
+ return "updated";
4494
+ case "decision":
4495
+ return "decided";
4496
+ case "event":
4497
+ return "happened";
4498
+ case "code":
4499
+ return "implemented";
4500
+ default:
4501
+ return "states";
4502
+ }
4503
+ }
4504
+ function extractMemoryCards(row) {
4505
+ const sentences = splitSentences(row.raw_text);
4506
+ const cards = [];
4507
+ for (const sentence of sentences) {
4508
+ const type = inferCardType(sentence, row.tool_name);
4509
+ const subject = extractSubject(sentence, row.agent_id);
4510
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4511
+ cards.push({
4512
+ id: stableId(row.id, type, content),
4513
+ memory_id: row.id,
4514
+ agent_id: row.agent_id,
4515
+ session_id: row.session_id,
4516
+ project_name: row.project_name ?? null,
4517
+ timestamp: row.timestamp,
4518
+ card_type: type,
4519
+ subject,
4520
+ predicate: predicateFor(type),
4521
+ object: content,
4522
+ content,
4523
+ source_ref: row.id,
4524
+ confidence: type === "fact" ? 0.55 : 0.65
4525
+ });
4526
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4527
+ }
4528
+ return cards;
4529
+ }
4530
+ async function insertMemoryCardsForBatch(rows) {
4531
+ const cards = rows.flatMap(extractMemoryCards);
4532
+ if (cards.length === 0) return 0;
4533
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4534
+ const client = getClient();
4535
+ const stmts = cards.map((card) => ({
4536
+ sql: `INSERT OR IGNORE INTO memory_cards
4537
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4538
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4539
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4540
+ args: [
4541
+ card.id,
4542
+ card.memory_id,
4543
+ card.agent_id,
4544
+ card.session_id,
4545
+ card.project_name,
4546
+ card.timestamp,
4547
+ card.card_type,
4548
+ card.subject,
4549
+ card.predicate,
4550
+ card.object,
4551
+ card.content,
4552
+ card.source_ref,
4553
+ card.confidence,
4554
+ now
4555
+ ]
4556
+ }));
4557
+ await client.batch(stmts, "write");
4558
+ return cards.length;
4559
+ }
4560
+ function buildMatchExpr(queryText) {
4561
+ const terms = queryText.toLowerCase().split(/\s+/).filter((t) => t.length >= 3).map((t) => t.replace(/[^a-z0-9_]/g, "")).filter((t) => t.length >= 3).slice(0, 12);
4562
+ if (terms.length === 0) return null;
4563
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4564
+ }
4565
+ async function searchMemoryCards(queryText, agentId, options) {
4566
+ const limit = options?.limit ?? 10;
4567
+ const matchExpr = buildMatchExpr(queryText);
4568
+ if (!matchExpr) return [];
4569
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4570
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4571
+ FROM memory_cards c
4572
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4573
+ WHERE memory_cards_fts MATCH ?
4574
+ AND c.agent_id = ?
4575
+ AND COALESCE(c.active, 1) = 1`;
4576
+ const args = [matchExpr, agentId];
4577
+ if (options?.projectName) {
4578
+ sql += ` AND c.project_name = ?`;
4579
+ args.push(options.projectName);
4580
+ }
4581
+ if (options?.since) {
4582
+ sql += ` AND c.timestamp >= ?`;
4583
+ args.push(options.since);
4584
+ }
4585
+ sql += ` ORDER BY rank LIMIT ?`;
4586
+ args.push(limit);
4587
+ const result = await getClient().execute({ sql, args });
4588
+ return result.rows.map((row) => ({
4589
+ id: `card:${String(row.id)}`,
4590
+ agent_id: String(row.agent_id),
4591
+ agent_role: "memory_card",
4592
+ session_id: String(row.session_id),
4593
+ timestamp: String(row.timestamp),
4594
+ tool_name: `memory_card:${String(row.card_type)}`,
4595
+ project_name: row.project_name == null ? "" : String(row.project_name),
4596
+ has_error: false,
4597
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4598
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4599
+ vector: [],
4600
+ importance: 6,
4601
+ status: "active",
4602
+ confidence: Number(row.confidence ?? 0.6),
4603
+ last_accessed: String(row.timestamp)
4604
+ }));
4605
+ }
4606
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4607
+ var init_memory_cards = __esm({
4608
+ "src/lib/memory-cards.ts"() {
4609
+ "use strict";
4610
+ init_database();
4611
+ MAX_CARDS_PER_MEMORY = 6;
4612
+ MAX_SENTENCE_CHARS = 360;
4613
+ }
4614
+ });
4615
+
4413
4616
  // src/lib/store.ts
4414
4617
  var store_exports = {};
4415
4618
  __export(store_exports, {
@@ -4748,6 +4951,11 @@ async function flushBatch() {
4748
4951
  const globalClient = getClient();
4749
4952
  const globalStmts = batch.map(buildStmt);
4750
4953
  await globalClient.batch(globalStmts, "write");
4954
+ try {
4955
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4956
+ await insertMemoryCardsForBatch2(batch);
4957
+ } catch {
4958
+ }
4751
4959
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4752
4960
  _pendingRecords.splice(0, batch.length);
4753
4961
  try {
@@ -8684,54 +8892,98 @@ async function fastDbInit() {
8684
8892
  // src/adapters/claude/hooks/bug-report-worker.ts
8685
8893
  init_database();
8686
8894
  init_tasks();
8687
- async function main() {
8688
- const toolName = process.env.BUG_TOOL_NAME ?? "unknown";
8689
- const errorText = process.env.BUG_ERROR_TEXT ?? "";
8690
- const toolInput = process.env.BUG_TOOL_INPUT ?? "{}";
8691
- const fingerprint = process.env.BUG_FINGERPRINT ?? "";
8692
- const agentId = process.env.BUG_AGENT_ID ?? "unknown";
8693
- const agentRole = process.env.BUG_AGENT_ROLE ?? "employee";
8694
- const projectName = process.env.BUG_PROJECT_NAME ?? "unknown";
8695
- await fastDbInit();
8696
- const fpPrefix = fingerprint.slice(0, 8);
8697
- const client = getClient();
8698
- const { loadEmployeesSync: loadEmployeesSync2, getEmployeeByRole: getEmployeeByRole2, getCoordinatorName: getCoordinatorName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
8699
- const employees = loadEmployeesSync2();
8700
- const coordinatorName = getCoordinatorName2(employees);
8701
- const ctoName = getEmployeeByRole2(employees, "CTO")?.name ?? coordinatorName;
8702
- const existing = await client.execute({
8703
- sql: `SELECT id FROM tasks
8704
- WHERE assigned_to = ?
8705
- AND status IN ('open', 'in_progress')
8706
- AND title LIKE '[auto-bug]%'
8707
- AND task_file LIKE ?
8708
- LIMIT 1`,
8709
- args: [ctoName, `%${fpPrefix}%`]
8895
+
8896
+ // src/lib/bug-intake.ts
8897
+ import { createHash as createHash3, randomUUID as randomUUID4 } from "crypto";
8898
+ var BUG_INTAKE_SCHEMA_VERSION = 1;
8899
+ function firstMeaningfulLine(text) {
8900
+ return text.split("\n").find((line) => line.trim().length > 0)?.trim().slice(0, 80) ?? "unknown error";
8901
+ }
8902
+ function stableFingerprint(input) {
8903
+ const basis = [
8904
+ input.source,
8905
+ input.toolName ?? "unknown",
8906
+ firstMeaningfulLine(input.errorText ?? ""),
8907
+ input.projectName ?? "unknown"
8908
+ ].join("|");
8909
+ return createHash3("sha256").update(basis).digest("hex").slice(0, 16);
8910
+ }
8911
+ function hashLicense(licenseKey) {
8912
+ if (!licenseKey) return void 0;
8913
+ return createHash3("sha256").update(licenseKey).digest("hex").slice(0, 16);
8914
+ }
8915
+ function buildBugIntake(input) {
8916
+ const toolName = input.toolName ?? "unknown";
8917
+ const errorText = input.errorText ?? "";
8918
+ const summary = firstMeaningfulLine(errorText);
8919
+ const fingerprint = input.fingerprint && input.fingerprint.trim().length > 0 ? input.fingerprint.trim() : stableFingerprint(input);
8920
+ return {
8921
+ schemaVersion: BUG_INTAKE_SCHEMA_VERSION,
8922
+ id: randomUUID4(),
8923
+ source: input.source,
8924
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
8925
+ fingerprint,
8926
+ severity: input.severity ?? "p1",
8927
+ title: `[auto-bug] ${toolName}: ${summary.slice(0, 60)}`,
8928
+ summary,
8929
+ reporterAgentId: input.reporterAgentId ?? "unknown",
8930
+ reporterAgentRole: input.reporterAgentRole ?? "employee",
8931
+ projectName: input.projectName ?? "unknown",
8932
+ toolName,
8933
+ errorText,
8934
+ toolInput: input.toolInput ?? "{}",
8935
+ runtime: input.runtime,
8936
+ repo: input.repo,
8937
+ licenseKeyHash: hashLicense(input.licenseKey),
8938
+ labels: ["auto-bug", input.source, toolName].filter(Boolean)
8939
+ };
8940
+ }
8941
+ function buildBugIntakeFromEnv(env = process.env) {
8942
+ return buildBugIntake({
8943
+ source: "hook",
8944
+ toolName: env.BUG_TOOL_NAME,
8945
+ errorText: env.BUG_ERROR_TEXT,
8946
+ toolInput: env.BUG_TOOL_INPUT,
8947
+ fingerprint: env.BUG_FINGERPRINT,
8948
+ reporterAgentId: env.BUG_AGENT_ID,
8949
+ reporterAgentRole: env.BUG_AGENT_ROLE,
8950
+ projectName: env.BUG_PROJECT_NAME,
8951
+ runtime: env.EXE_RUNTIME,
8952
+ repo: env.EXE_REPO,
8953
+ licenseKey: env.EXE_LICENSE_KEY
8710
8954
  });
8711
- if (existing.rows.length > 0) {
8712
- process.stderr.write(`[bug-report-worker] Duplicate found for fingerprint ${fingerprint}, skipping
8713
- `);
8714
- return;
8715
- }
8716
- const errorSummary = errorText.split("\n").find((line) => line.trim().length > 0)?.trim().slice(0, 60) ?? "unknown error";
8717
- const context = [
8955
+ }
8956
+ function formatBugIntakeTaskContext(record) {
8957
+ return [
8718
8958
  "## Auto-detected system bug",
8719
8959
  "",
8720
- `**Detected by:** ${agentId} (${agentRole})`,
8721
- `**Tool:** ${toolName}`,
8722
- `**Timestamp:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
8723
- `**Fingerprint:** ${fingerprint}`,
8960
+ `**Schema:** bug-intake/v${record.schemaVersion}`,
8961
+ `**Source:** ${record.source}`,
8962
+ `**Detected by:** ${record.reporterAgentId} (${record.reporterAgentRole})`,
8963
+ `**Tool:** ${record.toolName}`,
8964
+ `**Timestamp:** ${record.createdAt}`,
8965
+ `**Fingerprint:** ${record.fingerprint}`,
8966
+ `**Severity:** ${record.severity}`,
8967
+ record.runtime ? `**Runtime:** ${record.runtime}` : void 0,
8968
+ record.repo ? `**Repo:** ${record.repo}` : void 0,
8969
+ record.licenseKeyHash ? `**License hash:** ${record.licenseKeyHash}` : void 0,
8724
8970
  "",
8725
8971
  "## Error output",
8726
8972
  "",
8727
8973
  "```",
8728
- errorText.slice(0, 1e3),
8974
+ record.errorText.slice(0, 2e3),
8729
8975
  "```",
8730
8976
  "",
8731
8977
  "## Tool input (reproduction context)",
8732
8978
  "",
8733
8979
  "```json",
8734
- toolInput.slice(0, 500),
8980
+ record.toolInput.slice(0, 1e3),
8981
+ "```",
8982
+ "",
8983
+ "## Normalized intake JSON",
8984
+ "",
8985
+ "```json",
8986
+ JSON.stringify(record, null, 2),
8735
8987
  "```",
8736
8988
  "",
8737
8989
  "## Triage notes",
@@ -8739,19 +8991,44 @@ async function main() {
8739
8991
  "- Classification: system bug (auto-detected)",
8740
8992
  "- Review this error \u2014 fix if real, close if false positive",
8741
8993
  "- If false positive: add the error pattern to USER_ERROR_PATTERNS in error-detector.ts"
8742
- ].join("\n");
8994
+ ].filter((line) => line !== void 0).join("\n");
8995
+ }
8996
+
8997
+ // src/adapters/claude/hooks/bug-report-worker.ts
8998
+ async function main() {
8999
+ const intake = buildBugIntakeFromEnv(process.env);
9000
+ await fastDbInit();
9001
+ const client = getClient();
9002
+ const { loadEmployeesSync: loadEmployeesSync2, getEmployeeByRole: getEmployeeByRole2, getCoordinatorName: getCoordinatorName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
9003
+ const employees = loadEmployeesSync2();
9004
+ const coordinatorName = getCoordinatorName2(employees);
9005
+ const ctoName = getEmployeeByRole2(employees, "CTO")?.name ?? coordinatorName;
9006
+ const existing = await client.execute({
9007
+ sql: `SELECT id FROM tasks
9008
+ WHERE assigned_to = ?
9009
+ AND status IN ('open', 'in_progress')
9010
+ AND title LIKE '[auto-bug]%'
9011
+ AND (context LIKE ? OR task_file LIKE ?)
9012
+ LIMIT 1`,
9013
+ args: [ctoName, `%${intake.fingerprint}%`, `%${intake.fingerprint.slice(0, 8)}%`]
9014
+ });
9015
+ if (existing.rows.length > 0) {
9016
+ process.stderr.write(`[bug-report-worker] Duplicate found for fingerprint ${intake.fingerprint}, skipping
9017
+ `);
9018
+ return;
9019
+ }
8743
9020
  await createTask({
8744
- title: `[auto-bug] ${toolName}: ${errorSummary}`,
9021
+ title: intake.title,
8745
9022
  assignedTo: ctoName,
8746
9023
  assignedBy: "system",
8747
- projectName,
8748
- priority: "p1",
8749
- context,
9024
+ projectName: intake.projectName,
9025
+ priority: intake.severity,
9026
+ context: formatBugIntakeTaskContext(intake),
8750
9027
  baseDir: process.cwd(),
8751
9028
  skipDispatch: true,
8752
9029
  reviewer: coordinatorName
8753
9030
  });
8754
- process.stderr.write(`[bug-report-worker] Created auto-bug task for ${toolName}: ${errorSummary}
9031
+ process.stderr.write(`[bug-report-worker] Created auto-bug task for ${intake.toolName}: ${intake.summary}
8755
9032
  `);
8756
9033
  }
8757
9034
  main().catch((err) => {