@massu/core 1.15.7 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -973,6 +973,106 @@ var init_db_backup = __esm({
973
973
  }
974
974
  });
975
975
 
976
+ // src/rule-delivery.ts
977
+ function tableExists(db, table) {
978
+ const row = db.prepare("SELECT 1 AS ok FROM sqlite_master WHERE type='table' AND name=?").get(table);
979
+ return row !== void 0;
980
+ }
981
+ function columns(db, table) {
982
+ const rows = db.prepare(`PRAGMA table_info(${table})`).all();
983
+ return new Set(rows.map((r) => r.name));
984
+ }
985
+ function migrateRuleDelivery(db) {
986
+ const ADDITIONS = [
987
+ { name: "attempts", decl: "INTEGER NOT NULL DEFAULT 0" },
988
+ { name: "lease_token", decl: "TEXT" },
989
+ { name: "leased_at_ms", decl: "INTEGER" },
990
+ { name: "last_error", decl: "TEXT" }
991
+ ];
992
+ for (const table of ALL_OUTBOUND_TABLES) {
993
+ if (!tableExists(db, table)) continue;
994
+ const cols = columns(db, table);
995
+ for (const add of ADDITIONS) {
996
+ if (cols.has(add.name)) continue;
997
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${add.name} ${add.decl}`);
998
+ }
999
+ }
1000
+ }
1001
+ function capTelemetry(db, cap = TELEMETRY_OUTBOX_CAP) {
1002
+ if (!tableExists(db, TELEMETRY_OUTBOUND_TABLE)) return 0;
1003
+ const before = db.prepare(`SELECT COUNT(*) AS n FROM ${TELEMETRY_OUTBOUND_TABLE}`).get();
1004
+ if (before.n <= cap) return 0;
1005
+ const info = db.prepare(
1006
+ `DELETE FROM ${TELEMETRY_OUTBOUND_TABLE}
1007
+ WHERE id NOT IN (SELECT id FROM ${TELEMETRY_OUTBOUND_TABLE} ORDER BY id DESC LIMIT ?)`
1008
+ ).run(cap);
1009
+ const dropped = info.changes;
1010
+ if (dropped > 0) {
1011
+ process.stderr.write(
1012
+ `[massu] NOTE: dropped ${dropped} oldest promotion-funnel telemetry row(s) at the ${cap}-row cap. No learned rules were affected (rules are never dropped).
1013
+ `
1014
+ );
1015
+ recordAnalytics(db, EVENT_TELEMETRY_CAPPED, { dropped, cap });
1016
+ }
1017
+ return dropped;
1018
+ }
1019
+ function recordAnalytics(db, eventType, data) {
1020
+ try {
1021
+ db.prepare(
1022
+ `INSERT INTO analytics_events (event_type, event_data, created_at)
1023
+ VALUES (?, ?, datetime('now'))`
1024
+ ).run(eventType, JSON.stringify(data));
1025
+ } catch (err) {
1026
+ process.stderr.write(
1027
+ `[massu] WARNING: could not record '${eventType}' telemetry: ${err instanceof Error ? err.message : String(err)}
1028
+ `
1029
+ );
1030
+ }
1031
+ }
1032
+ var RULE_OUTBOUND_TABLES, TELEMETRY_OUTBOUND_TABLE, ALL_OUTBOUND_TABLES, TELEMETRY_OUTBOX_CAP, LEASE_TTL_MS, EVENT_TELEMETRY_CAPPED;
1033
+ var init_rule_delivery = __esm({
1034
+ "src/rule-delivery.ts"() {
1035
+ "use strict";
1036
+ RULE_OUTBOUND_TABLES = [
1037
+ "team_promotion_outbound",
1038
+ "team_revocation_outbound"
1039
+ ];
1040
+ TELEMETRY_OUTBOUND_TABLE = "rule_promotion_events_outbound";
1041
+ ALL_OUTBOUND_TABLES = [
1042
+ ...RULE_OUTBOUND_TABLES,
1043
+ TELEMETRY_OUTBOUND_TABLE
1044
+ ];
1045
+ TELEMETRY_OUTBOX_CAP = 2e4;
1046
+ LEASE_TTL_MS = 15 * 60 * 1e3;
1047
+ EVENT_TELEMETRY_CAPPED = "rule_telemetry_capped";
1048
+ }
1049
+ });
1050
+
1051
+ // src/rule-candidate-store.ts
1052
+ function ensureRuleCandidatesTable(db) {
1053
+ db.exec(`
1054
+ CREATE TABLE IF NOT EXISTS rule_candidates (
1055
+ prompt_hash TEXT PRIMARY KEY,
1056
+ status TEXT NOT NULL DEFAULT 'proposed'
1057
+ CHECK (status IN ('proposed','shown','promoted','dismissed')),
1058
+ origin TEXT NOT NULL DEFAULT 'local'
1059
+ CHECK (origin IN ('local','team','pack')),
1060
+ score REAL,
1061
+ destination TEXT,
1062
+ payload_json TEXT NOT NULL DEFAULT '{}',
1063
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1064
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1065
+ );
1066
+ CREATE INDEX IF NOT EXISTS idx_rule_candidates_status ON rule_candidates(status);
1067
+ CREATE INDEX IF NOT EXISTS idx_rule_candidates_created ON rule_candidates(created_at);
1068
+ `);
1069
+ }
1070
+ var init_rule_candidate_store = __esm({
1071
+ "src/rule-candidate-store.ts"() {
1072
+ "use strict";
1073
+ }
1074
+ });
1075
+
976
1076
  // src/hooks/lib/hook-failure-signal.ts
977
1077
  import { appendFileSync, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
978
1078
  import { join as join2, dirname as dirname3 } from "path";
@@ -1526,9 +1626,6 @@ __export(memory_db_exports, {
1526
1626
  createSession: () => createSession,
1527
1627
  deduplicateFailedAttempt: () => deduplicateFailedAttempt,
1528
1628
  dequeuePendingSync: () => dequeuePendingSync,
1529
- drainRulePromotionEvents: () => drainRulePromotionEvents,
1530
- drainTeamPromotions: () => drainTeamPromotions,
1531
- drainTeamRevocations: () => drainTeamRevocations,
1532
1629
  embedMissingObservations: () => embedMissingObservations,
1533
1630
  endSession: () => endSession,
1534
1631
  enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
@@ -2435,6 +2532,8 @@ function initMemorySchema(db) {
2435
2532
  `);
2436
2533
  migrateObservationEmbeddingChunks(db);
2437
2534
  migrateMemoryFilesFor4B(db);
2535
+ ensureRuleCandidatesTable(db);
2536
+ migrateRuleDelivery(db);
2438
2537
  }
2439
2538
  function enqueueSyncPayload(db, payload) {
2440
2539
  db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
@@ -2509,38 +2608,12 @@ function enqueueTeamPromotion(db, promo) {
2509
2608
  promo.review_attestation !== void 0 ? JSON.stringify(promo.review_attestation) : null
2510
2609
  );
2511
2610
  }
2512
- function drainTeamPromotions(db) {
2513
- const rows = db.prepare(`
2514
- SELECT prompt_hash, destination, draft_text, score, signals_json, content_hash, hardened, review_attestation_json
2515
- FROM team_promotion_outbound ORDER BY created_at ASC LIMIT 1000
2516
- `).all();
2517
- if (rows.length === 0) return [];
2518
- db.prepare("DELETE FROM team_promotion_outbound").run();
2519
- return rows.map((r) => ({
2520
- prompt_hash: r.prompt_hash,
2521
- destination: r.destination,
2522
- draft_text: r.draft_text,
2523
- score: r.score ?? void 0,
2524
- signals: safeJsonArray(r.signals_json),
2525
- content_hash: r.content_hash,
2526
- hardened: r.hardened === 1,
2527
- review_attestation: r.review_attestation_json ? safeJsonParse(r.review_attestation_json) : void 0
2528
- }));
2529
- }
2530
2611
  function enqueueTeamRevocation(db, promptHash) {
2531
2612
  db.prepare(`
2532
2613
  INSERT OR REPLACE INTO team_revocation_outbound (prompt_hash, created_at)
2533
2614
  VALUES (?, datetime('now'))
2534
2615
  `).run(promptHash);
2535
2616
  }
2536
- function drainTeamRevocations(db) {
2537
- const rows = db.prepare(
2538
- `SELECT prompt_hash FROM team_revocation_outbound ORDER BY created_at ASC LIMIT 1000`
2539
- ).all();
2540
- if (rows.length === 0) return [];
2541
- db.prepare("DELETE FROM team_revocation_outbound").run();
2542
- return rows.map((r) => r.prompt_hash);
2543
- }
2544
2617
  function getRecurrenceCountForPromptHash(db, promptHash) {
2545
2618
  try {
2546
2619
  const row = db.prepare(`
@@ -2568,30 +2641,14 @@ function enqueueRulePromotionEvent(db, ev) {
2568
2641
  JSON.stringify(ev.metadata ?? {}),
2569
2642
  ev.created_at
2570
2643
  );
2571
- db.prepare(`
2572
- DELETE FROM rule_promotion_events_outbound
2573
- WHERE id NOT IN (
2574
- SELECT id FROM rule_promotion_events_outbound ORDER BY id DESC LIMIT ?
2575
- )
2576
- `).run(FUNNEL_EVENT_OUTBOX_CAP);
2577
- } catch {
2644
+ capTelemetry(db, FUNNEL_EVENT_OUTBOX_CAP);
2645
+ } catch (err) {
2646
+ process.stderr.write(
2647
+ `[massu] WARNING: failed to record promotion-funnel event (${ev.event_type} / ${ev.prompt_hash}): ${err instanceof Error ? err.message : String(err)}
2648
+ `
2649
+ );
2578
2650
  }
2579
2651
  }
2580
- function drainRulePromotionEvents(db) {
2581
- const rows = db.prepare(`
2582
- SELECT id, prompt_hash, event_type, metadata_json, created_at
2583
- FROM rule_promotion_events_outbound ORDER BY id ASC LIMIT ?
2584
- `).all(FUNNEL_EVENT_DRAIN_LIMIT);
2585
- if (rows.length === 0) return [];
2586
- const maxId = rows[rows.length - 1].id;
2587
- db.prepare("DELETE FROM rule_promotion_events_outbound WHERE id <= ?").run(maxId);
2588
- return rows.map((r) => ({
2589
- prompt_hash: r.prompt_hash,
2590
- event_type: r.event_type,
2591
- created_at: r.created_at,
2592
- metadata: safeJsonParse(r.metadata_json) ?? {}
2593
- }));
2594
- }
2595
2652
  function recordTelemetry(db, eventType, data) {
2596
2653
  try {
2597
2654
  db.prepare(`
@@ -2601,28 +2658,55 @@ function recordTelemetry(db, eventType, data) {
2601
2658
  } catch {
2602
2659
  }
2603
2660
  }
2604
- function safeJsonArray(json) {
2605
- try {
2606
- const v = JSON.parse(json);
2607
- return Array.isArray(v) ? v : [];
2608
- } catch {
2609
- return [];
2610
- }
2611
- }
2612
- function safeJsonParse(json) {
2613
- try {
2614
- return JSON.parse(json);
2615
- } catch {
2616
- return void 0;
2661
+ function rescueLearningFromStalePayloads(db, stale) {
2662
+ let rescued = 0;
2663
+ for (const item of stale) {
2664
+ let payload;
2665
+ try {
2666
+ payload = JSON.parse(item.payload);
2667
+ } catch {
2668
+ continue;
2669
+ }
2670
+ try {
2671
+ const promotions = Array.isArray(payload.rule_promotions) ? payload.rule_promotions : [];
2672
+ for (const p of promotions) {
2673
+ enqueueTeamPromotion(db, p);
2674
+ rescued++;
2675
+ }
2676
+ const revocations = Array.isArray(payload.rule_revocations) ? payload.rule_revocations : [];
2677
+ for (const r of revocations) {
2678
+ if (typeof r?.prompt_hash === "string") {
2679
+ enqueueTeamRevocation(db, r.prompt_hash);
2680
+ rescued++;
2681
+ }
2682
+ }
2683
+ const events = Array.isArray(payload.rule_promotion_events) ? payload.rule_promotion_events : [];
2684
+ for (const e of events) {
2685
+ enqueueRulePromotionEvent(db, e);
2686
+ }
2687
+ } catch (err) {
2688
+ process.stderr.write(
2689
+ `[massu] WARNING: failed to rescue learned rules from give-up payload ${item.id}: ${err instanceof Error ? err.message : String(err)}
2690
+ `
2691
+ );
2692
+ }
2617
2693
  }
2694
+ return rescued;
2618
2695
  }
2619
2696
  function dequeuePendingSync(db, limit = 10) {
2620
2697
  const stale = db.prepare(
2621
- "SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
2698
+ "SELECT id, retry_count, last_error, payload FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
2622
2699
  ).all();
2623
2700
  if (stale.length > 0) {
2701
+ const rescued = rescueLearningFromStalePayloads(db, stale);
2624
2702
  const ids = stale.map((s) => s.id);
2625
2703
  db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
2704
+ if (rescued > 0) {
2705
+ process.stderr.write(
2706
+ `[massu] RESCUED ${rescued} learned rule(s) from ${stale.length} give-up payload(s) \u2014 they were re-queued for delivery, NOT discarded.
2707
+ `
2708
+ );
2709
+ }
2626
2710
  const lastErrors = [...new Set(stale.map((s) => s.last_error).filter(Boolean))];
2627
2711
  process.stderr.write(
2628
2712
  `[massu] WARNING: ${stale.length} cloud-sync queue item(s) discarded after 10+ retries. Likely cause: invalid API key or unreachable endpoint. Recent errors: ${lastErrors.slice(0, 3).join("; ") || "(none recorded)"}
@@ -3314,19 +3398,20 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
3314
3398
  }
3315
3399
  return bestMatch;
3316
3400
  }
3317
- var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_DRAIN_LIMIT, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
3401
+ var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
3318
3402
  var init_memory_db = __esm({
3319
3403
  "src/memory-db.ts"() {
3320
3404
  "use strict";
3321
3405
  init_config();
3322
3406
  init_memory_vector();
3323
3407
  init_db_backup();
3408
+ init_rule_delivery();
3409
+ init_rule_candidate_store();
3324
3410
  init_hook_failure_signal();
3325
3411
  init_memory_embed_sweep();
3326
3412
  MEMORY_SCHEMA_VERSION = 1;
3327
3413
  TOOL_COST_EVENTS_RETENTION_DAYS = 90;
3328
3414
  MAX_DRAFT_TEXT_LEN = 16384;
3329
- FUNNEL_EVENT_DRAIN_LIMIT = 5e3;
3330
3415
  FUNNEL_EVENT_OUTBOX_CAP = 2e4;
3331
3416
  _temporalColCache = /* @__PURE__ */ new WeakMap();
3332
3417
  CONSOLIDATION_LESSON_EVIDENCE = "consolidation:session-summary";
@@ -12714,10 +12799,11 @@ function verifyEd25519SignedEnvelope(key, payload) {
12714
12799
  }
12715
12800
 
12716
12801
  // src/security/license-pubkey.generated.ts
12717
- var LICENSE_PUBKEY_ED25519 = new Uint8Array([39, 136, 108, 146, 85, 233, 119, 252, 223, 226, 123, 155, 234, 168, 200, 150, 36, 249, 174, 6, 130, 146, 125, 196, 136, 224, 202, 150, 53, 228, 114, 15]);
12718
- var LICENSE_PUBKEY_FINGERPRINT_HEX = "18a63d64fdec9e5a368fc45feaa49bed6ced815967e582bc7b8af534f22a9475";
12802
+ var LICENSE_PUBKEY_ED25519 = new Uint8Array([254, 188, 221, 150, 111, 252, 154, 100, 253, 247, 113, 192, 144, 224, 127, 160, 202, 111, 75, 40, 35, 169, 108, 89, 252, 250, 255, 233, 250, 120, 47, 160]);
12803
+ var LICENSE_PUBKEY_FINGERPRINT_HEX = "18c0456789a70eeee28012719f04725f43f59e693f735227ff73f0475f2290e3";
12719
12804
  var KNOWN_LICENSE_PUBKEY_FINGERPRINTS = /* @__PURE__ */ new Set([
12720
- "18a63d64fdec9e5a368fc45feaa49bed6ced815967e582bc7b8af534f22a9475"
12805
+ "18a63d64fdec9e5a368fc45feaa49bed6ced815967e582bc7b8af534f22a9475",
12806
+ "18c0456789a70eeee28012719f04725f43f59e693f735227ff73f0475f2290e3"
12721
12807
  ]);
12722
12808
 
12723
12809
  // src/security/license-response-verifier.ts
@@ -12916,6 +13002,9 @@ init_memory_db();
12916
13002
  // src/rule-candidate-funnel.ts
12917
13003
  init_memory_db();
12918
13004
 
13005
+ // src/rule-candidate-applier.ts
13006
+ init_rule_candidate_store();
13007
+
12919
13008
  // src/rule-candidate-hardened.ts
12920
13009
  var TEAM_HARDENED_SHAREABLE_DESTINATIONS = [
12921
13010
  "pattern-scanner",
@@ -12988,10 +13077,11 @@ async function pullTeamPromotions(db, opts = {}) {
12988
13077
  headers: { Authorization: `Bearer ${apiKey}` },
12989
13078
  signal: AbortSignal.timeout(timeoutMs)
12990
13079
  });
12991
- if (!res.ok) return { ...ZERO };
13080
+ if (!res.ok) return failSync(db, `http_${res.status}`);
12992
13081
  envelope = await res.json();
12993
- } catch {
12994
- return { ...ZERO };
13082
+ } catch (err) {
13083
+ const reason = err instanceof Error && err.name === "TimeoutError" ? "timeout" : "network";
13084
+ return failSync(db, reason);
12995
13085
  }
12996
13086
  const verdict = verifyPromotionEnvelope(envelope);
12997
13087
  if (verdict.kind !== "valid") {
@@ -13177,6 +13267,14 @@ function emitDropTelemetry(db, eventType, data) {
13177
13267
  `
13178
13268
  );
13179
13269
  }
13270
+ function failSync(db, reason) {
13271
+ recordTelemetry(db, "team_promotion_sync_failed", { reason });
13272
+ process.stderr.write(
13273
+ `[massu] team-shared promotion pull FAILED (${reason}). This is NOT "nothing to sync" \u2014 the cloud was unreachable or rejected the request; promotions were NOT refreshed this run.
13274
+ `
13275
+ );
13276
+ return { ...ZERO, sync_error: reason };
13277
+ }
13180
13278
 
13181
13279
  // src/hooks/session-start.ts
13182
13280
  init_memory_file_ingest();