@massu/core 1.15.6 → 1.16.0
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/CHANGELOG.md +103 -0
- package/agents/massu-architecture-reviewer.md +20 -0
- package/agents/massu-blast-radius-analyzer.md +20 -0
- package/agents/massu-competitive-scorer.md +20 -0
- package/agents/massu-help-sync.md +20 -0
- package/agents/massu-migration-writer.md +20 -0
- package/agents/massu-output-scorer.md +20 -0
- package/agents/massu-pattern-reviewer.md +20 -0
- package/agents/massu-plan-auditor.md +20 -0
- package/agents/massu-schema-sync-verifier.md +20 -0
- package/agents/massu-security-reviewer.md +20 -0
- package/agents/massu-ux-reviewer.md +20 -0
- package/commands/_shared-preamble.md +21 -0
- package/commands/_shared-references/auto-learning-protocol.md +2 -0
- package/commands/_shared-references/blast-radius-protocol.md +2 -0
- package/commands/_shared-references/security-pre-screen.md +2 -0
- package/commands/_shared-references/test-first-protocol.md +2 -0
- package/commands/_shared-references/verification-table.md +2 -0
- package/commands/_verification-laws.md +131 -0
- package/commands/_verification-laws.md.sha256 +1 -0
- package/commands/massu-data/references/common-queries.md +2 -0
- package/commands/massu-data/references/table-guide.md +2 -0
- package/commands/massu-data.md +2 -0
- package/commands/massu-debug/references/auto-learning.md +2 -0
- package/commands/massu-debug/references/codegraph-tracing.md +2 -0
- package/commands/massu-debug/references/common-shortcuts.md +2 -0
- package/commands/massu-debug/references/investigation-phases.md +2 -0
- package/commands/massu-debug/references/report-format.md +2 -0
- package/commands/massu-deploy.python-docker.md +2 -0
- package/commands/massu-deploy.python-fly.md +2 -0
- package/commands/massu-deploy.python-launchd.md +2 -0
- package/commands/massu-deploy.python-systemd.md +2 -0
- package/commands/massu-full-audit.md +2 -0
- package/commands/massu-golden-path/references/approval-points.md +2 -0
- package/commands/massu-golden-path/references/phase-0-requirements.md +2 -0
- package/commands/massu-golden-path/references/phase-1-plan-creation.md +2 -0
- package/commands/massu-golden-path/references/phase-2-implementation.md +2 -0
- package/commands/massu-golden-path/references/phase-2.5-gap-analyzer.md +2 -0
- package/commands/massu-golden-path/references/phase-3-simplify.md +2 -0
- package/commands/massu-golden-path/references/phase-3.5-security-audit.md +2 -0
- package/commands/massu-golden-path/references/phase-4-commit.md +2 -0
- package/commands/massu-golden-path/references/phase-5-push.md +2 -0
- package/commands/massu-golden-path/references/phase-5.5-production-verify.md +2 -0
- package/commands/massu-golden-path/references/phase-6-completion.md +2 -0
- package/commands/massu-golden-path/references/qa-evaluator-spec.md +2 -0
- package/commands/massu-golden-path/references/sprint-contract-protocol.md +2 -0
- package/commands/massu-golden-path/references/vr-visual-calibration.md +2 -0
- package/commands/massu-guide.md +2 -0
- package/commands/massu-hooks.md +2 -0
- package/commands/massu-learning-audit.md +2 -0
- package/commands/massu-loop/references/auto-learning.md +2 -0
- package/commands/massu-loop/references/checkpoint-audit.md +2 -0
- package/commands/massu-loop/references/guardrails.md +2 -0
- package/commands/massu-loop/references/iteration-structure.md +2 -0
- package/commands/massu-loop/references/loop-controller.md +2 -0
- package/commands/massu-loop/references/plan-extraction.md +2 -0
- package/commands/massu-loop/references/vr-plan-spec.md +2 -0
- package/commands/massu-parity.md +2 -0
- package/commands/massu-push.md +2 -0
- package/commands/massu-scaffold-hook.md +2 -0
- package/commands/massu-scaffold-page.md +2 -0
- package/commands/massu-scaffold-page.swift.md +2 -0
- package/commands/massu-scaffold-router.md +2 -0
- package/commands/massu-scaffold-router.python-django.md +2 -0
- package/commands/massu-scaffold-router.python-fastapi.md +2 -0
- package/commands/massu-scaffold-router.python.md +2 -0
- package/dist/cli.js +2012 -787
- package/dist/hooks/auto-learning-pipeline.js +152 -67
- package/dist/hooks/classify-failure.js +152 -67
- package/dist/hooks/cost-tracker.js +152 -67
- package/dist/hooks/fix-detector.js +152 -67
- package/dist/hooks/incident-pipeline.js +152 -67
- package/dist/hooks/intent-suggester.js +152 -67
- package/dist/hooks/memory-recall.js +152 -67
- package/dist/hooks/post-edit-context.js +152 -67
- package/dist/hooks/post-tool-use.js +152 -67
- package/dist/hooks/pre-compact.js +152 -67
- package/dist/hooks/pre-delete-check.js +154 -69
- package/dist/hooks/pre-tool-use-gate.js +154 -69
- package/dist/hooks/quality-event.js +152 -67
- package/dist/hooks/rule-enforcement-pipeline.js +152 -67
- package/dist/hooks/security-gate.js +152 -67
- package/dist/hooks/session-end.js +346 -80
- package/dist/hooks/session-start.js +155 -67
- package/dist/hooks/user-prompt.js +222 -111
- package/package.json +4 -3
- package/patterns/build-patterns.md +2 -0
- package/patterns/component-patterns.md +2 -0
- package/patterns/display-patterns.md +2 -0
- package/patterns/form-patterns.md +2 -0
- package/patterns/integration-testing-checklist.md +2 -0
- package/patterns/security-patterns.md +2 -0
- package/patterns/testing-patterns.md +2 -0
- package/patterns/tool-routing.md +2 -0
- package/patterns/ui-patterns.md +2 -0
- package/protocols/plan-implementation.md +2 -0
- package/protocols/recovery.md +2 -0
- package/protocols/verification.md +2 -0
- package/reference/cr-rules-reference.md +2 -0
- package/reference/hook-execution-order.md +2 -0
- package/reference/lessons-learned.md +2 -0
- package/reference/patterns-quickref.md +2 -0
- package/reference/standards.md +2 -0
- package/reference/subagents-reference.md +2 -0
- package/reference/vr-verification-reference.md +2 -0
|
@@ -971,6 +971,106 @@ var init_db_backup = __esm({
|
|
|
971
971
|
}
|
|
972
972
|
});
|
|
973
973
|
|
|
974
|
+
// src/rule-delivery.ts
|
|
975
|
+
function tableExists(db, table) {
|
|
976
|
+
const row = db.prepare("SELECT 1 AS ok FROM sqlite_master WHERE type='table' AND name=?").get(table);
|
|
977
|
+
return row !== void 0;
|
|
978
|
+
}
|
|
979
|
+
function columns(db, table) {
|
|
980
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
981
|
+
return new Set(rows.map((r) => r.name));
|
|
982
|
+
}
|
|
983
|
+
function migrateRuleDelivery(db) {
|
|
984
|
+
const ADDITIONS = [
|
|
985
|
+
{ name: "attempts", decl: "INTEGER NOT NULL DEFAULT 0" },
|
|
986
|
+
{ name: "lease_token", decl: "TEXT" },
|
|
987
|
+
{ name: "leased_at_ms", decl: "INTEGER" },
|
|
988
|
+
{ name: "last_error", decl: "TEXT" }
|
|
989
|
+
];
|
|
990
|
+
for (const table of ALL_OUTBOUND_TABLES) {
|
|
991
|
+
if (!tableExists(db, table)) continue;
|
|
992
|
+
const cols = columns(db, table);
|
|
993
|
+
for (const add of ADDITIONS) {
|
|
994
|
+
if (cols.has(add.name)) continue;
|
|
995
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${add.name} ${add.decl}`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function capTelemetry(db, cap = TELEMETRY_OUTBOX_CAP) {
|
|
1000
|
+
if (!tableExists(db, TELEMETRY_OUTBOUND_TABLE)) return 0;
|
|
1001
|
+
const before = db.prepare(`SELECT COUNT(*) AS n FROM ${TELEMETRY_OUTBOUND_TABLE}`).get();
|
|
1002
|
+
if (before.n <= cap) return 0;
|
|
1003
|
+
const info = db.prepare(
|
|
1004
|
+
`DELETE FROM ${TELEMETRY_OUTBOUND_TABLE}
|
|
1005
|
+
WHERE id NOT IN (SELECT id FROM ${TELEMETRY_OUTBOUND_TABLE} ORDER BY id DESC LIMIT ?)`
|
|
1006
|
+
).run(cap);
|
|
1007
|
+
const dropped = info.changes;
|
|
1008
|
+
if (dropped > 0) {
|
|
1009
|
+
process.stderr.write(
|
|
1010
|
+
`[massu] NOTE: dropped ${dropped} oldest promotion-funnel telemetry row(s) at the ${cap}-row cap. No learned rules were affected (rules are never dropped).
|
|
1011
|
+
`
|
|
1012
|
+
);
|
|
1013
|
+
recordAnalytics(db, EVENT_TELEMETRY_CAPPED, { dropped, cap });
|
|
1014
|
+
}
|
|
1015
|
+
return dropped;
|
|
1016
|
+
}
|
|
1017
|
+
function recordAnalytics(db, eventType, data) {
|
|
1018
|
+
try {
|
|
1019
|
+
db.prepare(
|
|
1020
|
+
`INSERT INTO analytics_events (event_type, event_data, created_at)
|
|
1021
|
+
VALUES (?, ?, datetime('now'))`
|
|
1022
|
+
).run(eventType, JSON.stringify(data));
|
|
1023
|
+
} catch (err) {
|
|
1024
|
+
process.stderr.write(
|
|
1025
|
+
`[massu] WARNING: could not record '${eventType}' telemetry: ${err instanceof Error ? err.message : String(err)}
|
|
1026
|
+
`
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
var RULE_OUTBOUND_TABLES, TELEMETRY_OUTBOUND_TABLE, ALL_OUTBOUND_TABLES, TELEMETRY_OUTBOX_CAP, LEASE_TTL_MS, EVENT_TELEMETRY_CAPPED;
|
|
1031
|
+
var init_rule_delivery = __esm({
|
|
1032
|
+
"src/rule-delivery.ts"() {
|
|
1033
|
+
"use strict";
|
|
1034
|
+
RULE_OUTBOUND_TABLES = [
|
|
1035
|
+
"team_promotion_outbound",
|
|
1036
|
+
"team_revocation_outbound"
|
|
1037
|
+
];
|
|
1038
|
+
TELEMETRY_OUTBOUND_TABLE = "rule_promotion_events_outbound";
|
|
1039
|
+
ALL_OUTBOUND_TABLES = [
|
|
1040
|
+
...RULE_OUTBOUND_TABLES,
|
|
1041
|
+
TELEMETRY_OUTBOUND_TABLE
|
|
1042
|
+
];
|
|
1043
|
+
TELEMETRY_OUTBOX_CAP = 2e4;
|
|
1044
|
+
LEASE_TTL_MS = 15 * 60 * 1e3;
|
|
1045
|
+
EVENT_TELEMETRY_CAPPED = "rule_telemetry_capped";
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
// src/rule-candidate-store.ts
|
|
1050
|
+
function ensureRuleCandidatesTable(db) {
|
|
1051
|
+
db.exec(`
|
|
1052
|
+
CREATE TABLE IF NOT EXISTS rule_candidates (
|
|
1053
|
+
prompt_hash TEXT PRIMARY KEY,
|
|
1054
|
+
status TEXT NOT NULL DEFAULT 'proposed'
|
|
1055
|
+
CHECK (status IN ('proposed','shown','promoted','dismissed')),
|
|
1056
|
+
origin TEXT NOT NULL DEFAULT 'local'
|
|
1057
|
+
CHECK (origin IN ('local','team','pack')),
|
|
1058
|
+
score REAL,
|
|
1059
|
+
destination TEXT,
|
|
1060
|
+
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
1061
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1062
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1063
|
+
);
|
|
1064
|
+
CREATE INDEX IF NOT EXISTS idx_rule_candidates_status ON rule_candidates(status);
|
|
1065
|
+
CREATE INDEX IF NOT EXISTS idx_rule_candidates_created ON rule_candidates(created_at);
|
|
1066
|
+
`);
|
|
1067
|
+
}
|
|
1068
|
+
var init_rule_candidate_store = __esm({
|
|
1069
|
+
"src/rule-candidate-store.ts"() {
|
|
1070
|
+
"use strict";
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
|
|
974
1074
|
// src/hooks/lib/hook-failure-signal.ts
|
|
975
1075
|
import { appendFileSync, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
|
|
976
1076
|
import { join as join2, dirname as dirname3 } from "path";
|
|
@@ -1539,9 +1639,6 @@ __export(memory_db_exports, {
|
|
|
1539
1639
|
createSession: () => createSession,
|
|
1540
1640
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
1541
1641
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
1542
|
-
drainRulePromotionEvents: () => drainRulePromotionEvents,
|
|
1543
|
-
drainTeamPromotions: () => drainTeamPromotions,
|
|
1544
|
-
drainTeamRevocations: () => drainTeamRevocations,
|
|
1545
1642
|
embedMissingObservations: () => embedMissingObservations,
|
|
1546
1643
|
endSession: () => endSession,
|
|
1547
1644
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -2448,6 +2545,8 @@ function initMemorySchema(db) {
|
|
|
2448
2545
|
`);
|
|
2449
2546
|
migrateObservationEmbeddingChunks(db);
|
|
2450
2547
|
migrateMemoryFilesFor4B(db);
|
|
2548
|
+
ensureRuleCandidatesTable(db);
|
|
2549
|
+
migrateRuleDelivery(db);
|
|
2451
2550
|
}
|
|
2452
2551
|
function enqueueSyncPayload(db, payload) {
|
|
2453
2552
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
@@ -2522,38 +2621,12 @@ function enqueueTeamPromotion(db, promo) {
|
|
|
2522
2621
|
promo.review_attestation !== void 0 ? JSON.stringify(promo.review_attestation) : null
|
|
2523
2622
|
);
|
|
2524
2623
|
}
|
|
2525
|
-
function drainTeamPromotions(db) {
|
|
2526
|
-
const rows = db.prepare(`
|
|
2527
|
-
SELECT prompt_hash, destination, draft_text, score, signals_json, content_hash, hardened, review_attestation_json
|
|
2528
|
-
FROM team_promotion_outbound ORDER BY created_at ASC LIMIT 1000
|
|
2529
|
-
`).all();
|
|
2530
|
-
if (rows.length === 0) return [];
|
|
2531
|
-
db.prepare("DELETE FROM team_promotion_outbound").run();
|
|
2532
|
-
return rows.map((r) => ({
|
|
2533
|
-
prompt_hash: r.prompt_hash,
|
|
2534
|
-
destination: r.destination,
|
|
2535
|
-
draft_text: r.draft_text,
|
|
2536
|
-
score: r.score ?? void 0,
|
|
2537
|
-
signals: safeJsonArray(r.signals_json),
|
|
2538
|
-
content_hash: r.content_hash,
|
|
2539
|
-
hardened: r.hardened === 1,
|
|
2540
|
-
review_attestation: r.review_attestation_json ? safeJsonParse(r.review_attestation_json) : void 0
|
|
2541
|
-
}));
|
|
2542
|
-
}
|
|
2543
2624
|
function enqueueTeamRevocation(db, promptHash) {
|
|
2544
2625
|
db.prepare(`
|
|
2545
2626
|
INSERT OR REPLACE INTO team_revocation_outbound (prompt_hash, created_at)
|
|
2546
2627
|
VALUES (?, datetime('now'))
|
|
2547
2628
|
`).run(promptHash);
|
|
2548
2629
|
}
|
|
2549
|
-
function drainTeamRevocations(db) {
|
|
2550
|
-
const rows = db.prepare(
|
|
2551
|
-
`SELECT prompt_hash FROM team_revocation_outbound ORDER BY created_at ASC LIMIT 1000`
|
|
2552
|
-
).all();
|
|
2553
|
-
if (rows.length === 0) return [];
|
|
2554
|
-
db.prepare("DELETE FROM team_revocation_outbound").run();
|
|
2555
|
-
return rows.map((r) => r.prompt_hash);
|
|
2556
|
-
}
|
|
2557
2630
|
function getRecurrenceCountForPromptHash(db, promptHash) {
|
|
2558
2631
|
try {
|
|
2559
2632
|
const row = db.prepare(`
|
|
@@ -2581,30 +2654,14 @@ function enqueueRulePromotionEvent(db, ev) {
|
|
|
2581
2654
|
JSON.stringify(ev.metadata ?? {}),
|
|
2582
2655
|
ev.created_at
|
|
2583
2656
|
);
|
|
2584
|
-
db
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
} catch {
|
|
2657
|
+
capTelemetry(db, FUNNEL_EVENT_OUTBOX_CAP);
|
|
2658
|
+
} catch (err) {
|
|
2659
|
+
process.stderr.write(
|
|
2660
|
+
`[massu] WARNING: failed to record promotion-funnel event (${ev.event_type} / ${ev.prompt_hash}): ${err instanceof Error ? err.message : String(err)}
|
|
2661
|
+
`
|
|
2662
|
+
);
|
|
2591
2663
|
}
|
|
2592
2664
|
}
|
|
2593
|
-
function drainRulePromotionEvents(db) {
|
|
2594
|
-
const rows = db.prepare(`
|
|
2595
|
-
SELECT id, prompt_hash, event_type, metadata_json, created_at
|
|
2596
|
-
FROM rule_promotion_events_outbound ORDER BY id ASC LIMIT ?
|
|
2597
|
-
`).all(FUNNEL_EVENT_DRAIN_LIMIT);
|
|
2598
|
-
if (rows.length === 0) return [];
|
|
2599
|
-
const maxId = rows[rows.length - 1].id;
|
|
2600
|
-
db.prepare("DELETE FROM rule_promotion_events_outbound WHERE id <= ?").run(maxId);
|
|
2601
|
-
return rows.map((r) => ({
|
|
2602
|
-
prompt_hash: r.prompt_hash,
|
|
2603
|
-
event_type: r.event_type,
|
|
2604
|
-
created_at: r.created_at,
|
|
2605
|
-
metadata: safeJsonParse(r.metadata_json) ?? {}
|
|
2606
|
-
}));
|
|
2607
|
-
}
|
|
2608
2665
|
function recordTelemetry(db, eventType, data) {
|
|
2609
2666
|
try {
|
|
2610
2667
|
db.prepare(`
|
|
@@ -2614,28 +2671,55 @@ function recordTelemetry(db, eventType, data) {
|
|
|
2614
2671
|
} catch {
|
|
2615
2672
|
}
|
|
2616
2673
|
}
|
|
2617
|
-
function
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2674
|
+
function rescueLearningFromStalePayloads(db, stale) {
|
|
2675
|
+
let rescued = 0;
|
|
2676
|
+
for (const item of stale) {
|
|
2677
|
+
let payload;
|
|
2678
|
+
try {
|
|
2679
|
+
payload = JSON.parse(item.payload);
|
|
2680
|
+
} catch {
|
|
2681
|
+
continue;
|
|
2682
|
+
}
|
|
2683
|
+
try {
|
|
2684
|
+
const promotions = Array.isArray(payload.rule_promotions) ? payload.rule_promotions : [];
|
|
2685
|
+
for (const p of promotions) {
|
|
2686
|
+
enqueueTeamPromotion(db, p);
|
|
2687
|
+
rescued++;
|
|
2688
|
+
}
|
|
2689
|
+
const revocations = Array.isArray(payload.rule_revocations) ? payload.rule_revocations : [];
|
|
2690
|
+
for (const r of revocations) {
|
|
2691
|
+
if (typeof r?.prompt_hash === "string") {
|
|
2692
|
+
enqueueTeamRevocation(db, r.prompt_hash);
|
|
2693
|
+
rescued++;
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
const events = Array.isArray(payload.rule_promotion_events) ? payload.rule_promotion_events : [];
|
|
2697
|
+
for (const e of events) {
|
|
2698
|
+
enqueueRulePromotionEvent(db, e);
|
|
2699
|
+
}
|
|
2700
|
+
} catch (err) {
|
|
2701
|
+
process.stderr.write(
|
|
2702
|
+
`[massu] WARNING: failed to rescue learned rules from give-up payload ${item.id}: ${err instanceof Error ? err.message : String(err)}
|
|
2703
|
+
`
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2630
2706
|
}
|
|
2707
|
+
return rescued;
|
|
2631
2708
|
}
|
|
2632
2709
|
function dequeuePendingSync(db, limit = 10) {
|
|
2633
2710
|
const stale = db.prepare(
|
|
2634
|
-
"SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2711
|
+
"SELECT id, retry_count, last_error, payload FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2635
2712
|
).all();
|
|
2636
2713
|
if (stale.length > 0) {
|
|
2714
|
+
const rescued = rescueLearningFromStalePayloads(db, stale);
|
|
2637
2715
|
const ids = stale.map((s) => s.id);
|
|
2638
2716
|
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
2717
|
+
if (rescued > 0) {
|
|
2718
|
+
process.stderr.write(
|
|
2719
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${stale.length} give-up payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
2720
|
+
`
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2639
2723
|
const lastErrors = [...new Set(stale.map((s) => s.last_error).filter(Boolean))];
|
|
2640
2724
|
process.stderr.write(
|
|
2641
2725
|
`[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)"}
|
|
@@ -3327,19 +3411,20 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
3327
3411
|
}
|
|
3328
3412
|
return bestMatch;
|
|
3329
3413
|
}
|
|
3330
|
-
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN,
|
|
3414
|
+
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;
|
|
3331
3415
|
var init_memory_db = __esm({
|
|
3332
3416
|
"src/memory-db.ts"() {
|
|
3333
3417
|
"use strict";
|
|
3334
3418
|
init_config();
|
|
3335
3419
|
init_memory_vector();
|
|
3336
3420
|
init_db_backup();
|
|
3421
|
+
init_rule_delivery();
|
|
3422
|
+
init_rule_candidate_store();
|
|
3337
3423
|
init_hook_failure_signal();
|
|
3338
3424
|
init_memory_embed_sweep();
|
|
3339
3425
|
MEMORY_SCHEMA_VERSION = 1;
|
|
3340
3426
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
3341
3427
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
3342
|
-
FUNNEL_EVENT_DRAIN_LIMIT = 5e3;
|
|
3343
3428
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
3344
3429
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
3345
3430
|
CONSOLIDATION_LESSON_EVIDENCE = "consolidation:session-summary";
|
|
@@ -950,6 +950,106 @@ var init_db_backup = __esm({
|
|
|
950
950
|
}
|
|
951
951
|
});
|
|
952
952
|
|
|
953
|
+
// src/rule-delivery.ts
|
|
954
|
+
function tableExists(db, table) {
|
|
955
|
+
const row = db.prepare("SELECT 1 AS ok FROM sqlite_master WHERE type='table' AND name=?").get(table);
|
|
956
|
+
return row !== void 0;
|
|
957
|
+
}
|
|
958
|
+
function columns(db, table) {
|
|
959
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
960
|
+
return new Set(rows.map((r) => r.name));
|
|
961
|
+
}
|
|
962
|
+
function migrateRuleDelivery(db) {
|
|
963
|
+
const ADDITIONS = [
|
|
964
|
+
{ name: "attempts", decl: "INTEGER NOT NULL DEFAULT 0" },
|
|
965
|
+
{ name: "lease_token", decl: "TEXT" },
|
|
966
|
+
{ name: "leased_at_ms", decl: "INTEGER" },
|
|
967
|
+
{ name: "last_error", decl: "TEXT" }
|
|
968
|
+
];
|
|
969
|
+
for (const table of ALL_OUTBOUND_TABLES) {
|
|
970
|
+
if (!tableExists(db, table)) continue;
|
|
971
|
+
const cols = columns(db, table);
|
|
972
|
+
for (const add of ADDITIONS) {
|
|
973
|
+
if (cols.has(add.name)) continue;
|
|
974
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${add.name} ${add.decl}`);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function capTelemetry(db, cap = TELEMETRY_OUTBOX_CAP) {
|
|
979
|
+
if (!tableExists(db, TELEMETRY_OUTBOUND_TABLE)) return 0;
|
|
980
|
+
const before = db.prepare(`SELECT COUNT(*) AS n FROM ${TELEMETRY_OUTBOUND_TABLE}`).get();
|
|
981
|
+
if (before.n <= cap) return 0;
|
|
982
|
+
const info = db.prepare(
|
|
983
|
+
`DELETE FROM ${TELEMETRY_OUTBOUND_TABLE}
|
|
984
|
+
WHERE id NOT IN (SELECT id FROM ${TELEMETRY_OUTBOUND_TABLE} ORDER BY id DESC LIMIT ?)`
|
|
985
|
+
).run(cap);
|
|
986
|
+
const dropped = info.changes;
|
|
987
|
+
if (dropped > 0) {
|
|
988
|
+
process.stderr.write(
|
|
989
|
+
`[massu] NOTE: dropped ${dropped} oldest promotion-funnel telemetry row(s) at the ${cap}-row cap. No learned rules were affected (rules are never dropped).
|
|
990
|
+
`
|
|
991
|
+
);
|
|
992
|
+
recordAnalytics(db, EVENT_TELEMETRY_CAPPED, { dropped, cap });
|
|
993
|
+
}
|
|
994
|
+
return dropped;
|
|
995
|
+
}
|
|
996
|
+
function recordAnalytics(db, eventType, data) {
|
|
997
|
+
try {
|
|
998
|
+
db.prepare(
|
|
999
|
+
`INSERT INTO analytics_events (event_type, event_data, created_at)
|
|
1000
|
+
VALUES (?, ?, datetime('now'))`
|
|
1001
|
+
).run(eventType, JSON.stringify(data));
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
process.stderr.write(
|
|
1004
|
+
`[massu] WARNING: could not record '${eventType}' telemetry: ${err instanceof Error ? err.message : String(err)}
|
|
1005
|
+
`
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
var RULE_OUTBOUND_TABLES, TELEMETRY_OUTBOUND_TABLE, ALL_OUTBOUND_TABLES, TELEMETRY_OUTBOX_CAP, LEASE_TTL_MS, EVENT_TELEMETRY_CAPPED;
|
|
1010
|
+
var init_rule_delivery = __esm({
|
|
1011
|
+
"src/rule-delivery.ts"() {
|
|
1012
|
+
"use strict";
|
|
1013
|
+
RULE_OUTBOUND_TABLES = [
|
|
1014
|
+
"team_promotion_outbound",
|
|
1015
|
+
"team_revocation_outbound"
|
|
1016
|
+
];
|
|
1017
|
+
TELEMETRY_OUTBOUND_TABLE = "rule_promotion_events_outbound";
|
|
1018
|
+
ALL_OUTBOUND_TABLES = [
|
|
1019
|
+
...RULE_OUTBOUND_TABLES,
|
|
1020
|
+
TELEMETRY_OUTBOUND_TABLE
|
|
1021
|
+
];
|
|
1022
|
+
TELEMETRY_OUTBOX_CAP = 2e4;
|
|
1023
|
+
LEASE_TTL_MS = 15 * 60 * 1e3;
|
|
1024
|
+
EVENT_TELEMETRY_CAPPED = "rule_telemetry_capped";
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// src/rule-candidate-store.ts
|
|
1029
|
+
function ensureRuleCandidatesTable(db) {
|
|
1030
|
+
db.exec(`
|
|
1031
|
+
CREATE TABLE IF NOT EXISTS rule_candidates (
|
|
1032
|
+
prompt_hash TEXT PRIMARY KEY,
|
|
1033
|
+
status TEXT NOT NULL DEFAULT 'proposed'
|
|
1034
|
+
CHECK (status IN ('proposed','shown','promoted','dismissed')),
|
|
1035
|
+
origin TEXT NOT NULL DEFAULT 'local'
|
|
1036
|
+
CHECK (origin IN ('local','team','pack')),
|
|
1037
|
+
score REAL,
|
|
1038
|
+
destination TEXT,
|
|
1039
|
+
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
1040
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1041
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1042
|
+
);
|
|
1043
|
+
CREATE INDEX IF NOT EXISTS idx_rule_candidates_status ON rule_candidates(status);
|
|
1044
|
+
CREATE INDEX IF NOT EXISTS idx_rule_candidates_created ON rule_candidates(created_at);
|
|
1045
|
+
`);
|
|
1046
|
+
}
|
|
1047
|
+
var init_rule_candidate_store = __esm({
|
|
1048
|
+
"src/rule-candidate-store.ts"() {
|
|
1049
|
+
"use strict";
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
|
|
953
1053
|
// src/memory-embedder-tokenizer.ts
|
|
954
1054
|
import { readFileSync as readFileSync3 } from "fs";
|
|
955
1055
|
function loadVocab(vocabPath) {
|
|
@@ -1432,9 +1532,6 @@ __export(memory_db_exports, {
|
|
|
1432
1532
|
createSession: () => createSession,
|
|
1433
1533
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
1434
1534
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
1435
|
-
drainRulePromotionEvents: () => drainRulePromotionEvents,
|
|
1436
|
-
drainTeamPromotions: () => drainTeamPromotions,
|
|
1437
|
-
drainTeamRevocations: () => drainTeamRevocations,
|
|
1438
1535
|
embedMissingObservations: () => embedMissingObservations,
|
|
1439
1536
|
endSession: () => endSession,
|
|
1440
1537
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -2341,6 +2438,8 @@ function initMemorySchema(db) {
|
|
|
2341
2438
|
`);
|
|
2342
2439
|
migrateObservationEmbeddingChunks(db);
|
|
2343
2440
|
migrateMemoryFilesFor4B(db);
|
|
2441
|
+
ensureRuleCandidatesTable(db);
|
|
2442
|
+
migrateRuleDelivery(db);
|
|
2344
2443
|
}
|
|
2345
2444
|
function enqueueSyncPayload(db, payload) {
|
|
2346
2445
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
@@ -2415,38 +2514,12 @@ function enqueueTeamPromotion(db, promo) {
|
|
|
2415
2514
|
promo.review_attestation !== void 0 ? JSON.stringify(promo.review_attestation) : null
|
|
2416
2515
|
);
|
|
2417
2516
|
}
|
|
2418
|
-
function drainTeamPromotions(db) {
|
|
2419
|
-
const rows = db.prepare(`
|
|
2420
|
-
SELECT prompt_hash, destination, draft_text, score, signals_json, content_hash, hardened, review_attestation_json
|
|
2421
|
-
FROM team_promotion_outbound ORDER BY created_at ASC LIMIT 1000
|
|
2422
|
-
`).all();
|
|
2423
|
-
if (rows.length === 0) return [];
|
|
2424
|
-
db.prepare("DELETE FROM team_promotion_outbound").run();
|
|
2425
|
-
return rows.map((r) => ({
|
|
2426
|
-
prompt_hash: r.prompt_hash,
|
|
2427
|
-
destination: r.destination,
|
|
2428
|
-
draft_text: r.draft_text,
|
|
2429
|
-
score: r.score ?? void 0,
|
|
2430
|
-
signals: safeJsonArray(r.signals_json),
|
|
2431
|
-
content_hash: r.content_hash,
|
|
2432
|
-
hardened: r.hardened === 1,
|
|
2433
|
-
review_attestation: r.review_attestation_json ? safeJsonParse(r.review_attestation_json) : void 0
|
|
2434
|
-
}));
|
|
2435
|
-
}
|
|
2436
2517
|
function enqueueTeamRevocation(db, promptHash) {
|
|
2437
2518
|
db.prepare(`
|
|
2438
2519
|
INSERT OR REPLACE INTO team_revocation_outbound (prompt_hash, created_at)
|
|
2439
2520
|
VALUES (?, datetime('now'))
|
|
2440
2521
|
`).run(promptHash);
|
|
2441
2522
|
}
|
|
2442
|
-
function drainTeamRevocations(db) {
|
|
2443
|
-
const rows = db.prepare(
|
|
2444
|
-
`SELECT prompt_hash FROM team_revocation_outbound ORDER BY created_at ASC LIMIT 1000`
|
|
2445
|
-
).all();
|
|
2446
|
-
if (rows.length === 0) return [];
|
|
2447
|
-
db.prepare("DELETE FROM team_revocation_outbound").run();
|
|
2448
|
-
return rows.map((r) => r.prompt_hash);
|
|
2449
|
-
}
|
|
2450
2523
|
function getRecurrenceCountForPromptHash(db, promptHash) {
|
|
2451
2524
|
try {
|
|
2452
2525
|
const row = db.prepare(`
|
|
@@ -2474,30 +2547,14 @@ function enqueueRulePromotionEvent(db, ev) {
|
|
|
2474
2547
|
JSON.stringify(ev.metadata ?? {}),
|
|
2475
2548
|
ev.created_at
|
|
2476
2549
|
);
|
|
2477
|
-
db
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
} catch {
|
|
2550
|
+
capTelemetry(db, FUNNEL_EVENT_OUTBOX_CAP);
|
|
2551
|
+
} catch (err) {
|
|
2552
|
+
process.stderr.write(
|
|
2553
|
+
`[massu] WARNING: failed to record promotion-funnel event (${ev.event_type} / ${ev.prompt_hash}): ${err instanceof Error ? err.message : String(err)}
|
|
2554
|
+
`
|
|
2555
|
+
);
|
|
2484
2556
|
}
|
|
2485
2557
|
}
|
|
2486
|
-
function drainRulePromotionEvents(db) {
|
|
2487
|
-
const rows = db.prepare(`
|
|
2488
|
-
SELECT id, prompt_hash, event_type, metadata_json, created_at
|
|
2489
|
-
FROM rule_promotion_events_outbound ORDER BY id ASC LIMIT ?
|
|
2490
|
-
`).all(FUNNEL_EVENT_DRAIN_LIMIT);
|
|
2491
|
-
if (rows.length === 0) return [];
|
|
2492
|
-
const maxId = rows[rows.length - 1].id;
|
|
2493
|
-
db.prepare("DELETE FROM rule_promotion_events_outbound WHERE id <= ?").run(maxId);
|
|
2494
|
-
return rows.map((r) => ({
|
|
2495
|
-
prompt_hash: r.prompt_hash,
|
|
2496
|
-
event_type: r.event_type,
|
|
2497
|
-
created_at: r.created_at,
|
|
2498
|
-
metadata: safeJsonParse(r.metadata_json) ?? {}
|
|
2499
|
-
}));
|
|
2500
|
-
}
|
|
2501
2558
|
function recordTelemetry(db, eventType, data) {
|
|
2502
2559
|
try {
|
|
2503
2560
|
db.prepare(`
|
|
@@ -2507,28 +2564,55 @@ function recordTelemetry(db, eventType, data) {
|
|
|
2507
2564
|
} catch {
|
|
2508
2565
|
}
|
|
2509
2566
|
}
|
|
2510
|
-
function
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2567
|
+
function rescueLearningFromStalePayloads(db, stale) {
|
|
2568
|
+
let rescued = 0;
|
|
2569
|
+
for (const item of stale) {
|
|
2570
|
+
let payload;
|
|
2571
|
+
try {
|
|
2572
|
+
payload = JSON.parse(item.payload);
|
|
2573
|
+
} catch {
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
try {
|
|
2577
|
+
const promotions = Array.isArray(payload.rule_promotions) ? payload.rule_promotions : [];
|
|
2578
|
+
for (const p of promotions) {
|
|
2579
|
+
enqueueTeamPromotion(db, p);
|
|
2580
|
+
rescued++;
|
|
2581
|
+
}
|
|
2582
|
+
const revocations = Array.isArray(payload.rule_revocations) ? payload.rule_revocations : [];
|
|
2583
|
+
for (const r of revocations) {
|
|
2584
|
+
if (typeof r?.prompt_hash === "string") {
|
|
2585
|
+
enqueueTeamRevocation(db, r.prompt_hash);
|
|
2586
|
+
rescued++;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
const events = Array.isArray(payload.rule_promotion_events) ? payload.rule_promotion_events : [];
|
|
2590
|
+
for (const e of events) {
|
|
2591
|
+
enqueueRulePromotionEvent(db, e);
|
|
2592
|
+
}
|
|
2593
|
+
} catch (err) {
|
|
2594
|
+
process.stderr.write(
|
|
2595
|
+
`[massu] WARNING: failed to rescue learned rules from give-up payload ${item.id}: ${err instanceof Error ? err.message : String(err)}
|
|
2596
|
+
`
|
|
2597
|
+
);
|
|
2598
|
+
}
|
|
2523
2599
|
}
|
|
2600
|
+
return rescued;
|
|
2524
2601
|
}
|
|
2525
2602
|
function dequeuePendingSync(db, limit = 10) {
|
|
2526
2603
|
const stale = db.prepare(
|
|
2527
|
-
"SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2604
|
+
"SELECT id, retry_count, last_error, payload FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2528
2605
|
).all();
|
|
2529
2606
|
if (stale.length > 0) {
|
|
2607
|
+
const rescued = rescueLearningFromStalePayloads(db, stale);
|
|
2530
2608
|
const ids = stale.map((s) => s.id);
|
|
2531
2609
|
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
2610
|
+
if (rescued > 0) {
|
|
2611
|
+
process.stderr.write(
|
|
2612
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${stale.length} give-up payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
2613
|
+
`
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2532
2616
|
const lastErrors = [...new Set(stale.map((s) => s.last_error).filter(Boolean))];
|
|
2533
2617
|
process.stderr.write(
|
|
2534
2618
|
`[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)"}
|
|
@@ -3220,19 +3304,20 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
3220
3304
|
}
|
|
3221
3305
|
return bestMatch;
|
|
3222
3306
|
}
|
|
3223
|
-
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN,
|
|
3307
|
+
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;
|
|
3224
3308
|
var init_memory_db = __esm({
|
|
3225
3309
|
"src/memory-db.ts"() {
|
|
3226
3310
|
"use strict";
|
|
3227
3311
|
init_config();
|
|
3228
3312
|
init_memory_vector();
|
|
3229
3313
|
init_db_backup();
|
|
3314
|
+
init_rule_delivery();
|
|
3315
|
+
init_rule_candidate_store();
|
|
3230
3316
|
init_hook_failure_signal();
|
|
3231
3317
|
init_memory_embed_sweep();
|
|
3232
3318
|
MEMORY_SCHEMA_VERSION = 1;
|
|
3233
3319
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
3234
3320
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
3235
|
-
FUNNEL_EVENT_DRAIN_LIMIT = 5e3;
|
|
3236
3321
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
3237
3322
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
3238
3323
|
CONSOLIDATION_LESSON_EVIDENCE = "consolidation:session-summary";
|