@mneme-ai/core 2.19.99 → 2.20.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.
@@ -0,0 +1,212 @@
1
+ /**
2
+ * v2.20.0 — TIME BRIDGE.
3
+ *
4
+ * "Past-you ANNOTATES the future; future-you's AI listens automatically."
5
+ *
6
+ * The moat is NOT crypto signatures (A2A v1.0 just commodified those).
7
+ * The real moat is:
8
+ * (1) decision corpus captured WITHOUT manual effort (auto-inscription
9
+ * on every Mneme verb, via SUPER NOVA observer)
10
+ * (2) "default temporal layer" position in the AI-agent stack
11
+ * (3) format-longevity commitment: TIME BRIDGE FORMAT v1 — stable
12
+ * for 20+ years, never breaks
13
+ *
14
+ * Seven world-class innovations that compose into a system AI agents
15
+ * cannot live without:
16
+ *
17
+ * 1. FUTURE-READABLE PROVENANCE (FRP)
18
+ * Decisions carry future-applicability hints — not "I decided X"
19
+ * but "if you touch this in 6 months and {condition}, here's why
20
+ * X mattered." Structurally prospective, not descriptive.
21
+ *
22
+ * 2. DRIFT-AWARE SURFACE (DAS)
23
+ * When past reasoning surfaces, the surface mechanism quantifies
24
+ * how the codebase has drifted since then. Stale constraints get
25
+ * DOWNGRADED automatically; freshly-relevant ones get UPGRADED.
26
+ *
27
+ * 3. CONSTRAINT RESURRECTION
28
+ * When past-self refused a pattern + AI today is about to attempt
29
+ * it, Resurrection structurally requires a signed override note.
30
+ * AI cannot silently regress past decisions.
31
+ *
32
+ * 4. ECHO-CHAMBER KILLER
33
+ * Today's plan contradicts past-self? TIME BRIDGE surfaces BOTH
34
+ * and forces a "reversal note" signed by present-self for
35
+ * future-self to read. Structured dialogue across time.
36
+ *
37
+ * 5. SPOTLIGHT AUTO-TUNING
38
+ * Relevance scoring adapts to which past warnings the user actually
39
+ * heeded (success signal) vs ignored (failure signal). The bridge
40
+ * LEARNS what's signal vs noise per user — no manual tuning.
41
+ *
42
+ * 6. WAKE-WORD PREDICATES (the killer)
43
+ * Past-self can record a decision with a wake predicate — "wake
44
+ * me when {condition} happens" — that fires automatically when
45
+ * the condition is later detected. Time-delayed guidance no other
46
+ * product ships.
47
+ *
48
+ * 7. GENERATIONAL CONSTRAINT TREE
49
+ * Decisions visible as a TREE of overrides — child decisions
50
+ * inherit / override parent. AI can read the full evolution of
51
+ * a constraint, not just its current value.
52
+ *
53
+ * Every Mneme verb fired through SUPER NOVA auto-inscribes here.
54
+ * No manual effort. No "remember to save." The corpus accumulates
55
+ * as a side-effect of using Mneme normally.
56
+ */
57
+ /** Format version — guaranteed stable for 20+ years. */
58
+ export declare const FORMAT_VERSION: 1;
59
+ export type InscriptionKind = "decision" | "refusal" | "constraint" | "warning" | "annotation";
60
+ export interface FutureApplicability {
61
+ /** Plain-English description of when this matters in the future. */
62
+ appliesWhen: string;
63
+ /** Relevance signals the relevance-matcher will look for. */
64
+ signals?: {
65
+ files?: string[];
66
+ symbols?: string[];
67
+ keywords?: string[];
68
+ tags?: string[];
69
+ afterDate?: string;
70
+ };
71
+ /** Initial relevance weight 0..1; tuned by Spotlight as outcomes accumulate. */
72
+ initialWeight?: number;
73
+ }
74
+ export interface WakePredicate {
75
+ /** Plain-English description of the wake condition. */
76
+ description: string;
77
+ /** Machine-checkable predicate. */
78
+ trigger: {
79
+ kind: "file-touched" | "symbol-mentioned" | "tag-fired" | "date-reached" | "external";
80
+ /** For file-touched / symbol-mentioned. */
81
+ pattern?: string;
82
+ /** For date-reached. */
83
+ iso?: string;
84
+ /** For external — caller invokes manually with this id. */
85
+ externalId?: string;
86
+ };
87
+ /** Has this predicate fired already? */
88
+ fired?: boolean;
89
+ /** When it fired. */
90
+ firedAt?: string;
91
+ }
92
+ export interface Inscription {
93
+ /** Format-stable schema version. */
94
+ v: 1;
95
+ /** Globally-unique id. */
96
+ id: string;
97
+ /** ISO timestamp when written. */
98
+ ts: string;
99
+ /** Author identity (free text — could be a vendor id or human name). */
100
+ author: string;
101
+ kind: InscriptionKind;
102
+ /** One-line summary the receiving AI sees in its context. */
103
+ headline: string;
104
+ /** Full reasoning the AI may read in detail. */
105
+ reasoning: string;
106
+ /** Future-applicability hint — the magic field. */
107
+ fra: FutureApplicability;
108
+ /** Optional wake predicates that fire automatically. */
109
+ wakes?: WakePredicate[];
110
+ /** Parent inscription id (for the Generational Tree). */
111
+ parentId?: string;
112
+ /** Tags for retrieval. */
113
+ tags: string[];
114
+ /** HMAC sig over canonical payload. */
115
+ sig: string;
116
+ }
117
+ export interface SurfaceMatch {
118
+ inscription: Inscription;
119
+ /** 0..1 relevance score after Spotlight + DAS. */
120
+ score: number;
121
+ /** Why it was surfaced. */
122
+ reasons: string[];
123
+ /** How much the codebase has drifted since the inscription (0..1). */
124
+ driftScore: number;
125
+ }
126
+ export interface OverrideRecord {
127
+ v: 1;
128
+ ts: string;
129
+ overrider: string;
130
+ /** Inscription being overridden. */
131
+ inscriptionId: string;
132
+ /** Plain-English reason for the override (forced via the structured prompt). */
133
+ reason: string;
134
+ sig: string;
135
+ }
136
+ export interface InscribeOptions {
137
+ author: string;
138
+ kind: InscriptionKind;
139
+ headline: string;
140
+ reasoning: string;
141
+ fra: FutureApplicability;
142
+ wakes?: WakePredicate[];
143
+ parentId?: string;
144
+ tags?: string[];
145
+ }
146
+ /** Write a new inscription. HMAC-signed; format-stable; auditable. */
147
+ export declare function inscribe(repoRoot: string, opts: InscribeOptions): Promise<Inscription>;
148
+ interface SurfaceContext {
149
+ /** File currently being touched. */
150
+ file?: string;
151
+ /** Symbols / function names involved. */
152
+ symbols?: string[];
153
+ /** Free text the AI is about to commit / write / propose. */
154
+ text?: string;
155
+ /** Tags the caller knows about. */
156
+ tags?: string[];
157
+ }
158
+ export interface SurfaceOptions extends SurfaceContext {
159
+ /** Minimum relevance threshold. Default 0.4. */
160
+ threshold?: number;
161
+ /** Max items to return. Default 5. */
162
+ topK?: number;
163
+ }
164
+ /** The headline verb: given the current context (file / symbols / text /
165
+ * tags), return the most-relevant past inscriptions with full
166
+ * Drift-Aware Surface + Spotlight Auto-Tune applied. */
167
+ export declare function surface(repoRoot: string, opts: SurfaceOptions): Promise<SurfaceMatch[]>;
168
+ export interface ResurrectionVerdict {
169
+ /** True iff plan contradicts a still-relevant past constraint. */
170
+ blocked: boolean;
171
+ /** The contradicting inscription(s). */
172
+ contradicts: SurfaceMatch[];
173
+ /** Text the AI must include in its override note to proceed. */
174
+ requiredOverride: string;
175
+ }
176
+ /** Check whether the AI's proposed plan contradicts any past
177
+ * constraint/refusal. If so, block + return required override text. */
178
+ export declare function resurrect(repoRoot: string, planText: string, ctx?: SurfaceContext): Promise<ResurrectionVerdict>;
179
+ /** Record an override of a past inscription. Signed for future audit. */
180
+ export declare function recordOverride(repoRoot: string, opts: {
181
+ overrider: string;
182
+ inscriptionId: string;
183
+ reason: string;
184
+ }): Promise<OverrideRecord>;
185
+ export declare function markHeeded(repoRoot: string, inscriptionId: string): void;
186
+ export declare function markIgnored(repoRoot: string, inscriptionId: string): void;
187
+ export interface WakeFiring {
188
+ inscription: Inscription;
189
+ predicate: WakePredicate;
190
+ firedAt: string;
191
+ }
192
+ /** Check all pending wake predicates against current repo state.
193
+ * Returns the inscriptions whose predicates fired. The daemon calls
194
+ * this periodically; CLI calls it ad-hoc. */
195
+ export declare function fireWatchers(repoRoot: string, ctx?: SurfaceContext): Promise<WakeFiring[]>;
196
+ export interface TreeNode {
197
+ inscription: Inscription;
198
+ children: TreeNode[];
199
+ }
200
+ /** Build the override-lineage tree for a given inscription id. Walks
201
+ * parent links upward + downward. */
202
+ export declare function tree(repoRoot: string, rootId: string): TreeNode | null;
203
+ /** Install a SUPER NOVA observer that auto-inscribes select Mneme verbs
204
+ * as decisions. Caller can opt-in once (idempotent). */
205
+ export declare function enableAutoInscription(opts: {
206
+ repoRoot: string;
207
+ author: string;
208
+ }): () => void;
209
+ export declare function formatSurfaceMatches(matches: SurfaceMatch[]): string;
210
+ export declare function formatResurrectionVerdict(v: ResurrectionVerdict): string;
211
+ export {};
212
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/time_bridge/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,467 @@
1
+ /**
2
+ * v2.20.0 — TIME BRIDGE.
3
+ *
4
+ * "Past-you ANNOTATES the future; future-you's AI listens automatically."
5
+ *
6
+ * The moat is NOT crypto signatures (A2A v1.0 just commodified those).
7
+ * The real moat is:
8
+ * (1) decision corpus captured WITHOUT manual effort (auto-inscription
9
+ * on every Mneme verb, via SUPER NOVA observer)
10
+ * (2) "default temporal layer" position in the AI-agent stack
11
+ * (3) format-longevity commitment: TIME BRIDGE FORMAT v1 — stable
12
+ * for 20+ years, never breaks
13
+ *
14
+ * Seven world-class innovations that compose into a system AI agents
15
+ * cannot live without:
16
+ *
17
+ * 1. FUTURE-READABLE PROVENANCE (FRP)
18
+ * Decisions carry future-applicability hints — not "I decided X"
19
+ * but "if you touch this in 6 months and {condition}, here's why
20
+ * X mattered." Structurally prospective, not descriptive.
21
+ *
22
+ * 2. DRIFT-AWARE SURFACE (DAS)
23
+ * When past reasoning surfaces, the surface mechanism quantifies
24
+ * how the codebase has drifted since then. Stale constraints get
25
+ * DOWNGRADED automatically; freshly-relevant ones get UPGRADED.
26
+ *
27
+ * 3. CONSTRAINT RESURRECTION
28
+ * When past-self refused a pattern + AI today is about to attempt
29
+ * it, Resurrection structurally requires a signed override note.
30
+ * AI cannot silently regress past decisions.
31
+ *
32
+ * 4. ECHO-CHAMBER KILLER
33
+ * Today's plan contradicts past-self? TIME BRIDGE surfaces BOTH
34
+ * and forces a "reversal note" signed by present-self for
35
+ * future-self to read. Structured dialogue across time.
36
+ *
37
+ * 5. SPOTLIGHT AUTO-TUNING
38
+ * Relevance scoring adapts to which past warnings the user actually
39
+ * heeded (success signal) vs ignored (failure signal). The bridge
40
+ * LEARNS what's signal vs noise per user — no manual tuning.
41
+ *
42
+ * 6. WAKE-WORD PREDICATES (the killer)
43
+ * Past-self can record a decision with a wake predicate — "wake
44
+ * me when {condition} happens" — that fires automatically when
45
+ * the condition is later detected. Time-delayed guidance no other
46
+ * product ships.
47
+ *
48
+ * 7. GENERATIONAL CONSTRAINT TREE
49
+ * Decisions visible as a TREE of overrides — child decisions
50
+ * inherit / override parent. AI can read the full evolution of
51
+ * a constraint, not just its current value.
52
+ *
53
+ * Every Mneme verb fired through SUPER NOVA auto-inscribes here.
54
+ * No manual effort. No "remember to save." The corpus accumulates
55
+ * as a side-effect of using Mneme normally.
56
+ */
57
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync, statSync } from "node:fs";
58
+ import { join } from "node:path";
59
+ import { createHmac, randomBytes, createHash } from "node:crypto";
60
+ import { registerObserver, withSuperNova } from "../super_nova/index.js";
61
+ const DIR = ".mneme/time_bridge";
62
+ const INSCRIPTIONS = "inscriptions.jsonl";
63
+ const WATCHERS = "watchers.jsonl";
64
+ const SURFACES = "surfaces.jsonl";
65
+ const OVERRIDES = "overrides.jsonl";
66
+ const TUNING = "tuning.json";
67
+ const KEY = "time_bridge.key";
68
+ /** Format version — guaranteed stable for 20+ years. */
69
+ export const FORMAT_VERSION = 1;
70
+ // ─── STORAGE ───────────────────────────────────────────────────────────
71
+ function ensureDir(repoRoot) {
72
+ const d = join(repoRoot, DIR);
73
+ if (!existsSync(d))
74
+ mkdirSync(d, { recursive: true });
75
+ return d;
76
+ }
77
+ function ensureKey(repoRoot) {
78
+ const d = ensureDir(repoRoot);
79
+ const p = join(d, KEY);
80
+ if (existsSync(p))
81
+ return readFileSync(p, "utf8").trim();
82
+ const k = randomBytes(32).toString("base64url");
83
+ writeFileSync(p, k, "utf8");
84
+ return k;
85
+ }
86
+ function signInscription(i, key) {
87
+ const payload = `${i.v}|${i.id}|${i.ts}|${i.author}|${i.kind}|${i.headline}|${i.reasoning}|${JSON.stringify(i.fra)}|${JSON.stringify(i.wakes ?? [])}|${i.parentId ?? ""}|${i.tags.join(",")}`;
88
+ return createHmac("sha256", key).update(payload).digest("base64url").slice(0, 22);
89
+ }
90
+ function loadInscriptions(repoRoot) {
91
+ const p = join(repoRoot, DIR, INSCRIPTIONS);
92
+ if (!existsSync(p))
93
+ return [];
94
+ try {
95
+ return readFileSync(p, "utf8").trim().split("\n").map((l) => { try {
96
+ return JSON.parse(l);
97
+ }
98
+ catch {
99
+ return null;
100
+ } }).filter((r) => !!r);
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ }
106
+ function loadTuning(repoRoot) {
107
+ const p = join(repoRoot, DIR, TUNING);
108
+ if (!existsSync(p))
109
+ return { v: 1, perInscription: {} };
110
+ try {
111
+ return JSON.parse(readFileSync(p, "utf8"));
112
+ }
113
+ catch {
114
+ return { v: 1, perInscription: {} };
115
+ }
116
+ }
117
+ function saveTuning(repoRoot, t) {
118
+ writeFileSync(join(ensureDir(repoRoot), TUNING), JSON.stringify(t, null, 2), "utf8");
119
+ }
120
+ /** Write a new inscription. HMAC-signed; format-stable; auditable. */
121
+ export async function inscribe(repoRoot, opts) {
122
+ return withSuperNova({ verb: "mneme.time_bridge.inscribe", surface: "lib", repoRoot, vendor: opts.author }, async () => {
123
+ const key = ensureKey(repoRoot);
124
+ const ts = new Date().toISOString();
125
+ const id = "ins_" + createHash("sha256").update(`${ts}|${opts.headline}|${opts.author}|${randomBytes(4).toString("hex")}`).digest("hex").slice(0, 16);
126
+ const payload = {
127
+ v: FORMAT_VERSION,
128
+ id, ts, author: opts.author,
129
+ kind: opts.kind, headline: opts.headline, reasoning: opts.reasoning,
130
+ fra: opts.fra, wakes: opts.wakes, parentId: opts.parentId,
131
+ tags: opts.tags ?? [],
132
+ };
133
+ const sig = signInscription(payload, key);
134
+ const inscription = { ...payload, sig };
135
+ appendFileSync(join(ensureDir(repoRoot), INSCRIPTIONS), JSON.stringify(inscription) + "\n", "utf8");
136
+ return inscription;
137
+ }, { tags: ["time-bridge", "inscribe"] });
138
+ }
139
+ /** Quantify how relevant an inscription is to the current context. */
140
+ function scoreRelevance(ins, ctx) {
141
+ const reasons = [];
142
+ let score = ins.fra.initialWeight ?? 0.5;
143
+ const sig = ins.fra.signals ?? {};
144
+ // Date gate.
145
+ if (sig.afterDate && new Date(sig.afterDate).getTime() > Date.now()) {
146
+ return { score: 0, reasons: ["before afterDate gate"] };
147
+ }
148
+ // File match.
149
+ if (sig.files && ctx.file) {
150
+ for (const f of sig.files) {
151
+ if (ctx.file.includes(f)) {
152
+ score += 0.3;
153
+ reasons.push(`file matches "${f}"`);
154
+ break;
155
+ }
156
+ }
157
+ }
158
+ // Symbol match.
159
+ if (sig.symbols && ctx.symbols) {
160
+ for (const s of sig.symbols) {
161
+ if (ctx.symbols.includes(s)) {
162
+ score += 0.25;
163
+ reasons.push(`symbol matches "${s}"`);
164
+ break;
165
+ }
166
+ }
167
+ }
168
+ // Keyword match.
169
+ if (sig.keywords && ctx.text) {
170
+ const lower = ctx.text.toLowerCase();
171
+ for (const k of sig.keywords) {
172
+ if (lower.includes(k.toLowerCase())) {
173
+ score += 0.2;
174
+ reasons.push(`keyword matches "${k}"`);
175
+ break;
176
+ }
177
+ }
178
+ }
179
+ // Tag overlap.
180
+ if (sig.tags && ctx.tags) {
181
+ const overlap = sig.tags.filter((t) => ctx.tags.includes(t)).length;
182
+ if (overlap > 0) {
183
+ score += Math.min(0.3, overlap * 0.1);
184
+ reasons.push(`${overlap} tag(s) match`);
185
+ }
186
+ }
187
+ return { score: Math.min(1, score), reasons };
188
+ }
189
+ /** Estimate how much the codebase has drifted from the inscription
190
+ * time. Pure heuristic: file mtime delta + (later: git diff hunk
191
+ * count for the involved files). */
192
+ function driftScore(repoRoot, ins) {
193
+ const sig = ins.fra.signals;
194
+ if (!sig?.files || sig.files.length === 0)
195
+ return 0;
196
+ let totalDays = 0;
197
+ let count = 0;
198
+ const insTime = new Date(ins.ts).getTime();
199
+ for (const f of sig.files) {
200
+ const full = join(repoRoot, f);
201
+ if (!existsSync(full)) {
202
+ totalDays += 365;
203
+ count++;
204
+ continue;
205
+ } // file deleted = max drift
206
+ try {
207
+ const st = statSync(full);
208
+ const days = Math.max(0, (st.mtimeMs - insTime) / (1000 * 60 * 60 * 24));
209
+ totalDays += days;
210
+ count++;
211
+ }
212
+ catch { /* */ }
213
+ }
214
+ if (count === 0)
215
+ return 0;
216
+ // Normalise to [0..1] — drift saturates around 1 year.
217
+ return Math.min(1, totalDays / count / 365);
218
+ }
219
+ /** Apply Spotlight auto-tuning multiplier based on past heeded/ignored counts. */
220
+ function applyTuning(score, ins, t) {
221
+ const e = t.perInscription[ins.id];
222
+ if (!e)
223
+ return score;
224
+ const total = e.heededCount + e.ignoredCount;
225
+ if (total < 3)
226
+ return score;
227
+ const heedRate = e.heededCount / total;
228
+ // Heeded a lot → boost relevance; ignored a lot → suppress.
229
+ const mult = 0.5 + heedRate; // 0.5 (always ignored) ... 1.5 (always heeded)
230
+ return Math.min(1, score * mult);
231
+ }
232
+ /** The headline verb: given the current context (file / symbols / text /
233
+ * tags), return the most-relevant past inscriptions with full
234
+ * Drift-Aware Surface + Spotlight Auto-Tune applied. */
235
+ export async function surface(repoRoot, opts) {
236
+ return withSuperNova({ verb: "mneme.time_bridge.surface", surface: "lib", repoRoot, vendor: "mneme" }, async () => {
237
+ const inscriptions = loadInscriptions(repoRoot);
238
+ const tuning = loadTuning(repoRoot);
239
+ const threshold = opts.threshold ?? 0.4;
240
+ const topK = opts.topK ?? 5;
241
+ const matches = [];
242
+ for (const ins of inscriptions) {
243
+ const { score: raw, reasons } = scoreRelevance(ins, opts);
244
+ if (raw <= 0)
245
+ continue;
246
+ const tuned = applyTuning(raw, ins, tuning);
247
+ const drift = driftScore(repoRoot, ins);
248
+ // Drift downgrades stale constraints; freshly-relevant ones unchanged.
249
+ const final = Math.max(0, tuned - drift * 0.3);
250
+ if (final >= threshold) {
251
+ matches.push({ inscription: ins, score: Number(final.toFixed(3)), reasons, driftScore: Number(drift.toFixed(3)) });
252
+ }
253
+ }
254
+ matches.sort((a, b) => b.score - a.score);
255
+ const out = matches.slice(0, topK);
256
+ // Record the surface event for audit (and so the user can later
257
+ // mark which ones were heeded).
258
+ try {
259
+ for (const m of out) {
260
+ appendFileSync(join(ensureDir(repoRoot), SURFACES), JSON.stringify({
261
+ ts: new Date().toISOString(),
262
+ inscriptionId: m.inscription.id,
263
+ score: m.score, driftScore: m.driftScore,
264
+ }) + "\n", "utf8");
265
+ }
266
+ }
267
+ catch { /* */ }
268
+ return out;
269
+ }, { tags: ["time-bridge", "surface"] });
270
+ }
271
+ /** Check whether the AI's proposed plan contradicts any past
272
+ * constraint/refusal. If so, block + return required override text. */
273
+ export async function resurrect(repoRoot, planText, ctx = {}) {
274
+ return withSuperNova({ verb: "mneme.time_bridge.resurrect", surface: "lib", repoRoot, vendor: "mneme" }, async () => {
275
+ const matches = await surface(repoRoot, { ...ctx, text: planText, threshold: 0.5, topK: 10 });
276
+ // Constraint/refusal kinds with high relevance → block.
277
+ const contradicting = matches.filter((m) => (m.inscription.kind === "constraint" || m.inscription.kind === "refusal") && m.score >= 0.5);
278
+ if (contradicting.length === 0) {
279
+ return { blocked: false, contradicts: [], requiredOverride: "" };
280
+ }
281
+ const ids = contradicting.map((c) => c.inscription.id).join(", ");
282
+ const requiredOverride = [
283
+ `You are attempting an action that contradicts past constraint(s): ${ids}.`,
284
+ `To proceed, you MUST write an override note signed with your identity, citing each inscription id and explaining WHY you are reversing it.`,
285
+ `Format:`,
286
+ ` # TIME BRIDGE OVERRIDE`,
287
+ ` overriding: ${ids}`,
288
+ ` by: <your-agent-id>`,
289
+ ` reason: <one-paragraph-plain-English>`,
290
+ ` newConstraint: <what replaces the old one, if anything>`,
291
+ ``,
292
+ `Without this override, the action is refused.`,
293
+ ].join("\n");
294
+ return { blocked: true, contradicts: contradicting, requiredOverride };
295
+ }, { tags: ["time-bridge", "resurrect"] });
296
+ }
297
+ /** Record an override of a past inscription. Signed for future audit. */
298
+ export async function recordOverride(repoRoot, opts) {
299
+ return withSuperNova({ verb: "mneme.time_bridge.override", surface: "lib", repoRoot, vendor: opts.overrider }, async () => {
300
+ const key = ensureKey(repoRoot);
301
+ const ts = new Date().toISOString();
302
+ const payload = `${ts}|${opts.overrider}|${opts.inscriptionId}|${opts.reason}`;
303
+ const sig = createHmac("sha256", key).update(payload).digest("base64url").slice(0, 22);
304
+ const rec = { v: 1, ts, overrider: opts.overrider, inscriptionId: opts.inscriptionId, reason: opts.reason, sig };
305
+ appendFileSync(join(ensureDir(repoRoot), OVERRIDES), JSON.stringify(rec) + "\n", "utf8");
306
+ return rec;
307
+ }, { tags: ["time-bridge", "override"] });
308
+ }
309
+ // ─── 5. SPOTLIGHT TUNING (heed / ignore feedback) ──────────────────────
310
+ export function markHeeded(repoRoot, inscriptionId) {
311
+ const t = loadTuning(repoRoot);
312
+ const e = t.perInscription[inscriptionId] ?? { heededCount: 0, ignoredCount: 0, lastSeen: new Date().toISOString() };
313
+ e.heededCount++;
314
+ e.lastSeen = new Date().toISOString();
315
+ t.perInscription[inscriptionId] = e;
316
+ saveTuning(repoRoot, t);
317
+ }
318
+ export function markIgnored(repoRoot, inscriptionId) {
319
+ const t = loadTuning(repoRoot);
320
+ const e = t.perInscription[inscriptionId] ?? { heededCount: 0, ignoredCount: 0, lastSeen: new Date().toISOString() };
321
+ e.ignoredCount++;
322
+ e.lastSeen = new Date().toISOString();
323
+ t.perInscription[inscriptionId] = e;
324
+ saveTuning(repoRoot, t);
325
+ }
326
+ /** Check all pending wake predicates against current repo state.
327
+ * Returns the inscriptions whose predicates fired. The daemon calls
328
+ * this periodically; CLI calls it ad-hoc. */
329
+ export async function fireWatchers(repoRoot, ctx = {}) {
330
+ return withSuperNova({ verb: "mneme.time_bridge.fire_watchers", surface: "lib", repoRoot, vendor: "mneme" }, async () => {
331
+ const inscriptions = loadInscriptions(repoRoot);
332
+ const firings = [];
333
+ const now = new Date().toISOString();
334
+ for (const ins of inscriptions) {
335
+ if (!ins.wakes || ins.wakes.length === 0)
336
+ continue;
337
+ for (const w of ins.wakes) {
338
+ if (w.fired)
339
+ continue;
340
+ let fired = false;
341
+ if (w.trigger.kind === "date-reached" && w.trigger.iso) {
342
+ if (new Date(w.trigger.iso).getTime() <= Date.now())
343
+ fired = true;
344
+ }
345
+ else if (w.trigger.kind === "file-touched" && w.trigger.pattern && ctx.file) {
346
+ if (ctx.file.includes(w.trigger.pattern))
347
+ fired = true;
348
+ }
349
+ else if (w.trigger.kind === "symbol-mentioned" && w.trigger.pattern && ctx.symbols) {
350
+ if (ctx.symbols.includes(w.trigger.pattern))
351
+ fired = true;
352
+ }
353
+ if (fired) {
354
+ w.fired = true;
355
+ w.firedAt = now;
356
+ firings.push({ inscription: ins, predicate: w, firedAt: now });
357
+ }
358
+ }
359
+ }
360
+ // Persist the updated `fired` flags by rewriting the inscriptions
361
+ // file with the modified rows. Cheap because file is jsonl + small.
362
+ try {
363
+ writeFileSync(join(ensureDir(repoRoot), INSCRIPTIONS), inscriptions.map((i) => JSON.stringify(i)).join("\n") + "\n", "utf8");
364
+ }
365
+ catch { /* */ }
366
+ return firings;
367
+ }, { tags: ["time-bridge", "fire-watchers"] });
368
+ }
369
+ /** Build the override-lineage tree for a given inscription id. Walks
370
+ * parent links upward + downward. */
371
+ export function tree(repoRoot, rootId) {
372
+ const all = loadInscriptions(repoRoot);
373
+ const byId = new Map(all.map((i) => [i.id, i]));
374
+ const root = byId.get(rootId);
375
+ if (!root)
376
+ return null;
377
+ const byParent = new Map();
378
+ for (const i of all) {
379
+ if (i.parentId) {
380
+ const arr = byParent.get(i.parentId) ?? [];
381
+ arr.push(i);
382
+ byParent.set(i.parentId, arr);
383
+ }
384
+ }
385
+ function build(ins) {
386
+ return { inscription: ins, children: (byParent.get(ins.id) ?? []).map(build) };
387
+ }
388
+ return build(root);
389
+ }
390
+ // ─── AUTO-INSCRIPTION via SUPER NOVA OBSERVER ──────────────────────────
391
+ let autoObserverInstalled = false;
392
+ /** Install a SUPER NOVA observer that auto-inscribes select Mneme verbs
393
+ * as decisions. Caller can opt-in once (idempotent). */
394
+ export function enableAutoInscription(opts) {
395
+ if (autoObserverInstalled)
396
+ return () => { };
397
+ autoObserverInstalled = true;
398
+ return registerObserver({
399
+ id: "time_bridge_auto_inscriber",
400
+ phases: ["after"],
401
+ onPhase: (_phase, ctx, outcome) => {
402
+ // Only inscribe meaningful, infrequent verbs — avoid spam.
403
+ const noteworthy = /\.(swarm|govtech|cert\.mint|chronicle|apostille\.mint|guardrail\.consent|intern\.(start|graduate)|dream\.run)/;
404
+ if (!noteworthy.test(ctx.verb))
405
+ return;
406
+ const root = ctx.repoRoot ?? opts.repoRoot;
407
+ if (!root)
408
+ return;
409
+ try {
410
+ // Synchronous-friendly write via inscribe sans super-nova wrapper
411
+ // (we're already inside the observer — wrapping again would loop).
412
+ const key = ensureKey(root);
413
+ const ts = new Date().toISOString();
414
+ const id = "ins_" + createHash("sha256").update(`${ts}|${ctx.verb}|${randomBytes(4).toString("hex")}`).digest("hex").slice(0, 16);
415
+ const payload = {
416
+ v: FORMAT_VERSION, id, ts, author: ctx.vendor ?? opts.author,
417
+ kind: "decision",
418
+ headline: `${ctx.verb} ${outcome?.ok ? "✓" : "✗"} (auto-inscribed)`,
419
+ reasoning: `Auto-inscription: ${ctx.verb} fired with ${outcome?.durationMs ?? "?"}ms latency. ${outcome?.ok ? "Succeeded." : "Failed: " + (outcome?.errorMessage ?? "unknown")}`,
420
+ fra: { appliesWhen: `Future invocations of ${ctx.verb} in similar context.`, signals: { tags: [ctx.verb] } },
421
+ tags: ["auto", ctx.verb, ctx.surface],
422
+ };
423
+ const sig = signInscription(payload, key);
424
+ appendFileSync(join(ensureDir(root), INSCRIPTIONS), JSON.stringify({ ...payload, sig }) + "\n", "utf8");
425
+ }
426
+ catch { /* auto-inscription is best-effort; never break the host call */ }
427
+ },
428
+ });
429
+ }
430
+ // ─── HUMAN-READABLE FORMATTER ──────────────────────────────────────────
431
+ export function formatSurfaceMatches(matches) {
432
+ if (matches.length === 0)
433
+ return "🕰 TIME BRIDGE — no relevant past inscriptions found for this context.";
434
+ const lines = [];
435
+ lines.push("🕰 TIME BRIDGE — relevant past inscriptions");
436
+ lines.push("");
437
+ for (const m of matches) {
438
+ const i = m.inscription;
439
+ const driftWarn = m.driftScore > 0.5 ? " ⚠ stale" : "";
440
+ lines.push(` 📜 ${i.kind.toUpperCase()} score=${(m.score * 100).toFixed(0)}% drift=${(m.driftScore * 100).toFixed(0)}%${driftWarn}`);
441
+ lines.push(` ${i.headline}`);
442
+ lines.push(` authored by ${i.author} on ${i.ts.slice(0, 10)} (id: ${i.id})`);
443
+ lines.push(` applies when: ${i.fra.appliesWhen}`);
444
+ if (m.reasons.length > 0)
445
+ lines.push(` matched because: ${m.reasons.join("; ")}`);
446
+ lines.push("");
447
+ }
448
+ return lines.join("\n");
449
+ }
450
+ export function formatResurrectionVerdict(v) {
451
+ if (!v.blocked)
452
+ return "🕰 TIME BRIDGE — no past constraints contradict this plan. Proceed.";
453
+ const lines = [];
454
+ lines.push("🕰 TIME BRIDGE — RESURRECTION");
455
+ lines.push("");
456
+ lines.push(" ⛔ Plan contradicts the following past constraints/refusals:");
457
+ for (const c of v.contradicts) {
458
+ lines.push(` • ${c.inscription.id} (${c.inscription.kind}, score ${(c.score * 100).toFixed(0)}%)`);
459
+ lines.push(` ${c.inscription.headline}`);
460
+ }
461
+ lines.push("");
462
+ lines.push(" Required action to proceed:");
463
+ for (const ln of v.requiredOverride.split("\n"))
464
+ lines.push(` ${ln}`);
465
+ return lines.join("\n");
466
+ }
467
+ //# sourceMappingURL=index.js.map