@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
|
@@ -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/hooks/lib/hook-failure-signal.ts
|
|
954
1054
|
import { appendFileSync, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
|
|
955
1055
|
import { join as join2, dirname as dirname3 } from "path";
|
|
@@ -1503,9 +1603,6 @@ __export(memory_db_exports, {
|
|
|
1503
1603
|
createSession: () => createSession,
|
|
1504
1604
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
1505
1605
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
1506
|
-
drainRulePromotionEvents: () => drainRulePromotionEvents,
|
|
1507
|
-
drainTeamPromotions: () => drainTeamPromotions,
|
|
1508
|
-
drainTeamRevocations: () => drainTeamRevocations,
|
|
1509
1606
|
embedMissingObservations: () => embedMissingObservations,
|
|
1510
1607
|
endSession: () => endSession,
|
|
1511
1608
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -2412,6 +2509,8 @@ function initMemorySchema(db) {
|
|
|
2412
2509
|
`);
|
|
2413
2510
|
migrateObservationEmbeddingChunks(db);
|
|
2414
2511
|
migrateMemoryFilesFor4B(db);
|
|
2512
|
+
ensureRuleCandidatesTable(db);
|
|
2513
|
+
migrateRuleDelivery(db);
|
|
2415
2514
|
}
|
|
2416
2515
|
function enqueueSyncPayload(db, payload) {
|
|
2417
2516
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
@@ -2486,38 +2585,12 @@ function enqueueTeamPromotion(db, promo) {
|
|
|
2486
2585
|
promo.review_attestation !== void 0 ? JSON.stringify(promo.review_attestation) : null
|
|
2487
2586
|
);
|
|
2488
2587
|
}
|
|
2489
|
-
function drainTeamPromotions(db) {
|
|
2490
|
-
const rows = db.prepare(`
|
|
2491
|
-
SELECT prompt_hash, destination, draft_text, score, signals_json, content_hash, hardened, review_attestation_json
|
|
2492
|
-
FROM team_promotion_outbound ORDER BY created_at ASC LIMIT 1000
|
|
2493
|
-
`).all();
|
|
2494
|
-
if (rows.length === 0) return [];
|
|
2495
|
-
db.prepare("DELETE FROM team_promotion_outbound").run();
|
|
2496
|
-
return rows.map((r) => ({
|
|
2497
|
-
prompt_hash: r.prompt_hash,
|
|
2498
|
-
destination: r.destination,
|
|
2499
|
-
draft_text: r.draft_text,
|
|
2500
|
-
score: r.score ?? void 0,
|
|
2501
|
-
signals: safeJsonArray(r.signals_json),
|
|
2502
|
-
content_hash: r.content_hash,
|
|
2503
|
-
hardened: r.hardened === 1,
|
|
2504
|
-
review_attestation: r.review_attestation_json ? safeJsonParse(r.review_attestation_json) : void 0
|
|
2505
|
-
}));
|
|
2506
|
-
}
|
|
2507
2588
|
function enqueueTeamRevocation(db, promptHash) {
|
|
2508
2589
|
db.prepare(`
|
|
2509
2590
|
INSERT OR REPLACE INTO team_revocation_outbound (prompt_hash, created_at)
|
|
2510
2591
|
VALUES (?, datetime('now'))
|
|
2511
2592
|
`).run(promptHash);
|
|
2512
2593
|
}
|
|
2513
|
-
function drainTeamRevocations(db) {
|
|
2514
|
-
const rows = db.prepare(
|
|
2515
|
-
`SELECT prompt_hash FROM team_revocation_outbound ORDER BY created_at ASC LIMIT 1000`
|
|
2516
|
-
).all();
|
|
2517
|
-
if (rows.length === 0) return [];
|
|
2518
|
-
db.prepare("DELETE FROM team_revocation_outbound").run();
|
|
2519
|
-
return rows.map((r) => r.prompt_hash);
|
|
2520
|
-
}
|
|
2521
2594
|
function getRecurrenceCountForPromptHash(db, promptHash) {
|
|
2522
2595
|
try {
|
|
2523
2596
|
const row = db.prepare(`
|
|
@@ -2545,30 +2618,14 @@ function enqueueRulePromotionEvent(db, ev) {
|
|
|
2545
2618
|
JSON.stringify(ev.metadata ?? {}),
|
|
2546
2619
|
ev.created_at
|
|
2547
2620
|
);
|
|
2548
|
-
db
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
} catch {
|
|
2621
|
+
capTelemetry(db, FUNNEL_EVENT_OUTBOX_CAP);
|
|
2622
|
+
} catch (err) {
|
|
2623
|
+
process.stderr.write(
|
|
2624
|
+
`[massu] WARNING: failed to record promotion-funnel event (${ev.event_type} / ${ev.prompt_hash}): ${err instanceof Error ? err.message : String(err)}
|
|
2625
|
+
`
|
|
2626
|
+
);
|
|
2555
2627
|
}
|
|
2556
2628
|
}
|
|
2557
|
-
function drainRulePromotionEvents(db) {
|
|
2558
|
-
const rows = db.prepare(`
|
|
2559
|
-
SELECT id, prompt_hash, event_type, metadata_json, created_at
|
|
2560
|
-
FROM rule_promotion_events_outbound ORDER BY id ASC LIMIT ?
|
|
2561
|
-
`).all(FUNNEL_EVENT_DRAIN_LIMIT);
|
|
2562
|
-
if (rows.length === 0) return [];
|
|
2563
|
-
const maxId = rows[rows.length - 1].id;
|
|
2564
|
-
db.prepare("DELETE FROM rule_promotion_events_outbound WHERE id <= ?").run(maxId);
|
|
2565
|
-
return rows.map((r) => ({
|
|
2566
|
-
prompt_hash: r.prompt_hash,
|
|
2567
|
-
event_type: r.event_type,
|
|
2568
|
-
created_at: r.created_at,
|
|
2569
|
-
metadata: safeJsonParse(r.metadata_json) ?? {}
|
|
2570
|
-
}));
|
|
2571
|
-
}
|
|
2572
2629
|
function recordTelemetry(db, eventType, data) {
|
|
2573
2630
|
try {
|
|
2574
2631
|
db.prepare(`
|
|
@@ -2578,28 +2635,55 @@ function recordTelemetry(db, eventType, data) {
|
|
|
2578
2635
|
} catch {
|
|
2579
2636
|
}
|
|
2580
2637
|
}
|
|
2581
|
-
function
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2638
|
+
function rescueLearningFromStalePayloads(db, stale) {
|
|
2639
|
+
let rescued = 0;
|
|
2640
|
+
for (const item of stale) {
|
|
2641
|
+
let payload;
|
|
2642
|
+
try {
|
|
2643
|
+
payload = JSON.parse(item.payload);
|
|
2644
|
+
} catch {
|
|
2645
|
+
continue;
|
|
2646
|
+
}
|
|
2647
|
+
try {
|
|
2648
|
+
const promotions = Array.isArray(payload.rule_promotions) ? payload.rule_promotions : [];
|
|
2649
|
+
for (const p of promotions) {
|
|
2650
|
+
enqueueTeamPromotion(db, p);
|
|
2651
|
+
rescued++;
|
|
2652
|
+
}
|
|
2653
|
+
const revocations = Array.isArray(payload.rule_revocations) ? payload.rule_revocations : [];
|
|
2654
|
+
for (const r of revocations) {
|
|
2655
|
+
if (typeof r?.prompt_hash === "string") {
|
|
2656
|
+
enqueueTeamRevocation(db, r.prompt_hash);
|
|
2657
|
+
rescued++;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
const events = Array.isArray(payload.rule_promotion_events) ? payload.rule_promotion_events : [];
|
|
2661
|
+
for (const e of events) {
|
|
2662
|
+
enqueueRulePromotionEvent(db, e);
|
|
2663
|
+
}
|
|
2664
|
+
} catch (err) {
|
|
2665
|
+
process.stderr.write(
|
|
2666
|
+
`[massu] WARNING: failed to rescue learned rules from give-up payload ${item.id}: ${err instanceof Error ? err.message : String(err)}
|
|
2667
|
+
`
|
|
2668
|
+
);
|
|
2669
|
+
}
|
|
2594
2670
|
}
|
|
2671
|
+
return rescued;
|
|
2595
2672
|
}
|
|
2596
2673
|
function dequeuePendingSync(db, limit = 10) {
|
|
2597
2674
|
const stale = db.prepare(
|
|
2598
|
-
"SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2675
|
+
"SELECT id, retry_count, last_error, payload FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2599
2676
|
).all();
|
|
2600
2677
|
if (stale.length > 0) {
|
|
2678
|
+
const rescued = rescueLearningFromStalePayloads(db, stale);
|
|
2601
2679
|
const ids = stale.map((s) => s.id);
|
|
2602
2680
|
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
2681
|
+
if (rescued > 0) {
|
|
2682
|
+
process.stderr.write(
|
|
2683
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${stale.length} give-up payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
2684
|
+
`
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2603
2687
|
const lastErrors = [...new Set(stale.map((s) => s.last_error).filter(Boolean))];
|
|
2604
2688
|
process.stderr.write(
|
|
2605
2689
|
`[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)"}
|
|
@@ -3291,19 +3375,20 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
3291
3375
|
}
|
|
3292
3376
|
return bestMatch;
|
|
3293
3377
|
}
|
|
3294
|
-
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN,
|
|
3378
|
+
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;
|
|
3295
3379
|
var init_memory_db = __esm({
|
|
3296
3380
|
"src/memory-db.ts"() {
|
|
3297
3381
|
"use strict";
|
|
3298
3382
|
init_config();
|
|
3299
3383
|
init_memory_vector();
|
|
3300
3384
|
init_db_backup();
|
|
3385
|
+
init_rule_delivery();
|
|
3386
|
+
init_rule_candidate_store();
|
|
3301
3387
|
init_hook_failure_signal();
|
|
3302
3388
|
init_memory_embed_sweep();
|
|
3303
3389
|
MEMORY_SCHEMA_VERSION = 1;
|
|
3304
3390
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
3305
3391
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
3306
|
-
FUNNEL_EVENT_DRAIN_LIMIT = 5e3;
|
|
3307
3392
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
3308
3393
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
3309
3394
|
CONSOLIDATION_LESSON_EVIDENCE = "consolidation:session-summary";
|
|
@@ -956,6 +956,106 @@ var init_db_backup = __esm({
|
|
|
956
956
|
}
|
|
957
957
|
});
|
|
958
958
|
|
|
959
|
+
// src/rule-delivery.ts
|
|
960
|
+
function tableExists(db, table) {
|
|
961
|
+
const row = db.prepare("SELECT 1 AS ok FROM sqlite_master WHERE type='table' AND name=?").get(table);
|
|
962
|
+
return row !== void 0;
|
|
963
|
+
}
|
|
964
|
+
function columns(db, table) {
|
|
965
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
966
|
+
return new Set(rows.map((r) => r.name));
|
|
967
|
+
}
|
|
968
|
+
function migrateRuleDelivery(db) {
|
|
969
|
+
const ADDITIONS = [
|
|
970
|
+
{ name: "attempts", decl: "INTEGER NOT NULL DEFAULT 0" },
|
|
971
|
+
{ name: "lease_token", decl: "TEXT" },
|
|
972
|
+
{ name: "leased_at_ms", decl: "INTEGER" },
|
|
973
|
+
{ name: "last_error", decl: "TEXT" }
|
|
974
|
+
];
|
|
975
|
+
for (const table of ALL_OUTBOUND_TABLES) {
|
|
976
|
+
if (!tableExists(db, table)) continue;
|
|
977
|
+
const cols = columns(db, table);
|
|
978
|
+
for (const add of ADDITIONS) {
|
|
979
|
+
if (cols.has(add.name)) continue;
|
|
980
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${add.name} ${add.decl}`);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function capTelemetry(db, cap = TELEMETRY_OUTBOX_CAP) {
|
|
985
|
+
if (!tableExists(db, TELEMETRY_OUTBOUND_TABLE)) return 0;
|
|
986
|
+
const before = db.prepare(`SELECT COUNT(*) AS n FROM ${TELEMETRY_OUTBOUND_TABLE}`).get();
|
|
987
|
+
if (before.n <= cap) return 0;
|
|
988
|
+
const info = db.prepare(
|
|
989
|
+
`DELETE FROM ${TELEMETRY_OUTBOUND_TABLE}
|
|
990
|
+
WHERE id NOT IN (SELECT id FROM ${TELEMETRY_OUTBOUND_TABLE} ORDER BY id DESC LIMIT ?)`
|
|
991
|
+
).run(cap);
|
|
992
|
+
const dropped = info.changes;
|
|
993
|
+
if (dropped > 0) {
|
|
994
|
+
process.stderr.write(
|
|
995
|
+
`[massu] NOTE: dropped ${dropped} oldest promotion-funnel telemetry row(s) at the ${cap}-row cap. No learned rules were affected (rules are never dropped).
|
|
996
|
+
`
|
|
997
|
+
);
|
|
998
|
+
recordAnalytics(db, EVENT_TELEMETRY_CAPPED, { dropped, cap });
|
|
999
|
+
}
|
|
1000
|
+
return dropped;
|
|
1001
|
+
}
|
|
1002
|
+
function recordAnalytics(db, eventType, data) {
|
|
1003
|
+
try {
|
|
1004
|
+
db.prepare(
|
|
1005
|
+
`INSERT INTO analytics_events (event_type, event_data, created_at)
|
|
1006
|
+
VALUES (?, ?, datetime('now'))`
|
|
1007
|
+
).run(eventType, JSON.stringify(data));
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
process.stderr.write(
|
|
1010
|
+
`[massu] WARNING: could not record '${eventType}' telemetry: ${err instanceof Error ? err.message : String(err)}
|
|
1011
|
+
`
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
var RULE_OUTBOUND_TABLES, TELEMETRY_OUTBOUND_TABLE, ALL_OUTBOUND_TABLES, TELEMETRY_OUTBOX_CAP, LEASE_TTL_MS, EVENT_TELEMETRY_CAPPED;
|
|
1016
|
+
var init_rule_delivery = __esm({
|
|
1017
|
+
"src/rule-delivery.ts"() {
|
|
1018
|
+
"use strict";
|
|
1019
|
+
RULE_OUTBOUND_TABLES = [
|
|
1020
|
+
"team_promotion_outbound",
|
|
1021
|
+
"team_revocation_outbound"
|
|
1022
|
+
];
|
|
1023
|
+
TELEMETRY_OUTBOUND_TABLE = "rule_promotion_events_outbound";
|
|
1024
|
+
ALL_OUTBOUND_TABLES = [
|
|
1025
|
+
...RULE_OUTBOUND_TABLES,
|
|
1026
|
+
TELEMETRY_OUTBOUND_TABLE
|
|
1027
|
+
];
|
|
1028
|
+
TELEMETRY_OUTBOX_CAP = 2e4;
|
|
1029
|
+
LEASE_TTL_MS = 15 * 60 * 1e3;
|
|
1030
|
+
EVENT_TELEMETRY_CAPPED = "rule_telemetry_capped";
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
// src/rule-candidate-store.ts
|
|
1035
|
+
function ensureRuleCandidatesTable(db) {
|
|
1036
|
+
db.exec(`
|
|
1037
|
+
CREATE TABLE IF NOT EXISTS rule_candidates (
|
|
1038
|
+
prompt_hash TEXT PRIMARY KEY,
|
|
1039
|
+
status TEXT NOT NULL DEFAULT 'proposed'
|
|
1040
|
+
CHECK (status IN ('proposed','shown','promoted','dismissed')),
|
|
1041
|
+
origin TEXT NOT NULL DEFAULT 'local'
|
|
1042
|
+
CHECK (origin IN ('local','team','pack')),
|
|
1043
|
+
score REAL,
|
|
1044
|
+
destination TEXT,
|
|
1045
|
+
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
1046
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1047
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1048
|
+
);
|
|
1049
|
+
CREATE INDEX IF NOT EXISTS idx_rule_candidates_status ON rule_candidates(status);
|
|
1050
|
+
CREATE INDEX IF NOT EXISTS idx_rule_candidates_created ON rule_candidates(created_at);
|
|
1051
|
+
`);
|
|
1052
|
+
}
|
|
1053
|
+
var init_rule_candidate_store = __esm({
|
|
1054
|
+
"src/rule-candidate-store.ts"() {
|
|
1055
|
+
"use strict";
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
|
|
959
1059
|
// src/memory-embedder-tokenizer.ts
|
|
960
1060
|
import { readFileSync as readFileSync3 } from "fs";
|
|
961
1061
|
function loadVocab(vocabPath) {
|
|
@@ -1438,9 +1538,6 @@ __export(memory_db_exports, {
|
|
|
1438
1538
|
createSession: () => createSession,
|
|
1439
1539
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
1440
1540
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
1441
|
-
drainRulePromotionEvents: () => drainRulePromotionEvents,
|
|
1442
|
-
drainTeamPromotions: () => drainTeamPromotions,
|
|
1443
|
-
drainTeamRevocations: () => drainTeamRevocations,
|
|
1444
1541
|
embedMissingObservations: () => embedMissingObservations,
|
|
1445
1542
|
endSession: () => endSession,
|
|
1446
1543
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -2347,6 +2444,8 @@ function initMemorySchema(db) {
|
|
|
2347
2444
|
`);
|
|
2348
2445
|
migrateObservationEmbeddingChunks(db);
|
|
2349
2446
|
migrateMemoryFilesFor4B(db);
|
|
2447
|
+
ensureRuleCandidatesTable(db);
|
|
2448
|
+
migrateRuleDelivery(db);
|
|
2350
2449
|
}
|
|
2351
2450
|
function enqueueSyncPayload(db, payload) {
|
|
2352
2451
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
@@ -2421,38 +2520,12 @@ function enqueueTeamPromotion(db, promo) {
|
|
|
2421
2520
|
promo.review_attestation !== void 0 ? JSON.stringify(promo.review_attestation) : null
|
|
2422
2521
|
);
|
|
2423
2522
|
}
|
|
2424
|
-
function drainTeamPromotions(db) {
|
|
2425
|
-
const rows = db.prepare(`
|
|
2426
|
-
SELECT prompt_hash, destination, draft_text, score, signals_json, content_hash, hardened, review_attestation_json
|
|
2427
|
-
FROM team_promotion_outbound ORDER BY created_at ASC LIMIT 1000
|
|
2428
|
-
`).all();
|
|
2429
|
-
if (rows.length === 0) return [];
|
|
2430
|
-
db.prepare("DELETE FROM team_promotion_outbound").run();
|
|
2431
|
-
return rows.map((r) => ({
|
|
2432
|
-
prompt_hash: r.prompt_hash,
|
|
2433
|
-
destination: r.destination,
|
|
2434
|
-
draft_text: r.draft_text,
|
|
2435
|
-
score: r.score ?? void 0,
|
|
2436
|
-
signals: safeJsonArray(r.signals_json),
|
|
2437
|
-
content_hash: r.content_hash,
|
|
2438
|
-
hardened: r.hardened === 1,
|
|
2439
|
-
review_attestation: r.review_attestation_json ? safeJsonParse(r.review_attestation_json) : void 0
|
|
2440
|
-
}));
|
|
2441
|
-
}
|
|
2442
2523
|
function enqueueTeamRevocation(db, promptHash) {
|
|
2443
2524
|
db.prepare(`
|
|
2444
2525
|
INSERT OR REPLACE INTO team_revocation_outbound (prompt_hash, created_at)
|
|
2445
2526
|
VALUES (?, datetime('now'))
|
|
2446
2527
|
`).run(promptHash);
|
|
2447
2528
|
}
|
|
2448
|
-
function drainTeamRevocations(db) {
|
|
2449
|
-
const rows = db.prepare(
|
|
2450
|
-
`SELECT prompt_hash FROM team_revocation_outbound ORDER BY created_at ASC LIMIT 1000`
|
|
2451
|
-
).all();
|
|
2452
|
-
if (rows.length === 0) return [];
|
|
2453
|
-
db.prepare("DELETE FROM team_revocation_outbound").run();
|
|
2454
|
-
return rows.map((r) => r.prompt_hash);
|
|
2455
|
-
}
|
|
2456
2529
|
function getRecurrenceCountForPromptHash(db, promptHash) {
|
|
2457
2530
|
try {
|
|
2458
2531
|
const row = db.prepare(`
|
|
@@ -2480,30 +2553,14 @@ function enqueueRulePromotionEvent(db, ev) {
|
|
|
2480
2553
|
JSON.stringify(ev.metadata ?? {}),
|
|
2481
2554
|
ev.created_at
|
|
2482
2555
|
);
|
|
2483
|
-
db
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
} catch {
|
|
2556
|
+
capTelemetry(db, FUNNEL_EVENT_OUTBOX_CAP);
|
|
2557
|
+
} catch (err) {
|
|
2558
|
+
process.stderr.write(
|
|
2559
|
+
`[massu] WARNING: failed to record promotion-funnel event (${ev.event_type} / ${ev.prompt_hash}): ${err instanceof Error ? err.message : String(err)}
|
|
2560
|
+
`
|
|
2561
|
+
);
|
|
2490
2562
|
}
|
|
2491
2563
|
}
|
|
2492
|
-
function drainRulePromotionEvents(db) {
|
|
2493
|
-
const rows = db.prepare(`
|
|
2494
|
-
SELECT id, prompt_hash, event_type, metadata_json, created_at
|
|
2495
|
-
FROM rule_promotion_events_outbound ORDER BY id ASC LIMIT ?
|
|
2496
|
-
`).all(FUNNEL_EVENT_DRAIN_LIMIT);
|
|
2497
|
-
if (rows.length === 0) return [];
|
|
2498
|
-
const maxId = rows[rows.length - 1].id;
|
|
2499
|
-
db.prepare("DELETE FROM rule_promotion_events_outbound WHERE id <= ?").run(maxId);
|
|
2500
|
-
return rows.map((r) => ({
|
|
2501
|
-
prompt_hash: r.prompt_hash,
|
|
2502
|
-
event_type: r.event_type,
|
|
2503
|
-
created_at: r.created_at,
|
|
2504
|
-
metadata: safeJsonParse(r.metadata_json) ?? {}
|
|
2505
|
-
}));
|
|
2506
|
-
}
|
|
2507
2564
|
function recordTelemetry(db, eventType, data) {
|
|
2508
2565
|
try {
|
|
2509
2566
|
db.prepare(`
|
|
@@ -2513,28 +2570,55 @@ function recordTelemetry(db, eventType, data) {
|
|
|
2513
2570
|
} catch {
|
|
2514
2571
|
}
|
|
2515
2572
|
}
|
|
2516
|
-
function
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2573
|
+
function rescueLearningFromStalePayloads(db, stale) {
|
|
2574
|
+
let rescued = 0;
|
|
2575
|
+
for (const item of stale) {
|
|
2576
|
+
let payload;
|
|
2577
|
+
try {
|
|
2578
|
+
payload = JSON.parse(item.payload);
|
|
2579
|
+
} catch {
|
|
2580
|
+
continue;
|
|
2581
|
+
}
|
|
2582
|
+
try {
|
|
2583
|
+
const promotions = Array.isArray(payload.rule_promotions) ? payload.rule_promotions : [];
|
|
2584
|
+
for (const p of promotions) {
|
|
2585
|
+
enqueueTeamPromotion(db, p);
|
|
2586
|
+
rescued++;
|
|
2587
|
+
}
|
|
2588
|
+
const revocations = Array.isArray(payload.rule_revocations) ? payload.rule_revocations : [];
|
|
2589
|
+
for (const r of revocations) {
|
|
2590
|
+
if (typeof r?.prompt_hash === "string") {
|
|
2591
|
+
enqueueTeamRevocation(db, r.prompt_hash);
|
|
2592
|
+
rescued++;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
const events = Array.isArray(payload.rule_promotion_events) ? payload.rule_promotion_events : [];
|
|
2596
|
+
for (const e of events) {
|
|
2597
|
+
enqueueRulePromotionEvent(db, e);
|
|
2598
|
+
}
|
|
2599
|
+
} catch (err) {
|
|
2600
|
+
process.stderr.write(
|
|
2601
|
+
`[massu] WARNING: failed to rescue learned rules from give-up payload ${item.id}: ${err instanceof Error ? err.message : String(err)}
|
|
2602
|
+
`
|
|
2603
|
+
);
|
|
2604
|
+
}
|
|
2529
2605
|
}
|
|
2606
|
+
return rescued;
|
|
2530
2607
|
}
|
|
2531
2608
|
function dequeuePendingSync(db, limit = 10) {
|
|
2532
2609
|
const stale = db.prepare(
|
|
2533
|
-
"SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2610
|
+
"SELECT id, retry_count, last_error, payload FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
|
|
2534
2611
|
).all();
|
|
2535
2612
|
if (stale.length > 0) {
|
|
2613
|
+
const rescued = rescueLearningFromStalePayloads(db, stale);
|
|
2536
2614
|
const ids = stale.map((s) => s.id);
|
|
2537
2615
|
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
2616
|
+
if (rescued > 0) {
|
|
2617
|
+
process.stderr.write(
|
|
2618
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${stale.length} give-up payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
2619
|
+
`
|
|
2620
|
+
);
|
|
2621
|
+
}
|
|
2538
2622
|
const lastErrors = [...new Set(stale.map((s) => s.last_error).filter(Boolean))];
|
|
2539
2623
|
process.stderr.write(
|
|
2540
2624
|
`[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)"}
|
|
@@ -3226,19 +3310,20 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
3226
3310
|
}
|
|
3227
3311
|
return bestMatch;
|
|
3228
3312
|
}
|
|
3229
|
-
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN,
|
|
3313
|
+
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;
|
|
3230
3314
|
var init_memory_db = __esm({
|
|
3231
3315
|
"src/memory-db.ts"() {
|
|
3232
3316
|
"use strict";
|
|
3233
3317
|
init_config();
|
|
3234
3318
|
init_memory_vector();
|
|
3235
3319
|
init_db_backup();
|
|
3320
|
+
init_rule_delivery();
|
|
3321
|
+
init_rule_candidate_store();
|
|
3236
3322
|
init_hook_failure_signal();
|
|
3237
3323
|
init_memory_embed_sweep();
|
|
3238
3324
|
MEMORY_SCHEMA_VERSION = 1;
|
|
3239
3325
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
3240
3326
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
3241
|
-
FUNNEL_EVENT_DRAIN_LIMIT = 5e3;
|
|
3242
3327
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
3243
3328
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
3244
3329
|
CONSOLIDATION_LESSON_EVIDENCE = "consolidation:session-summary";
|