greprag 5.46.0 → 5.48.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.
Files changed (38) hide show
  1. package/dist/commands/arm-reminder.d.ts +25 -0
  2. package/dist/commands/arm-reminder.js +65 -0
  3. package/dist/commands/arm-reminder.js.map +1 -0
  4. package/dist/commands/assistant-reminder.d.ts +9 -0
  5. package/dist/commands/assistant-reminder.js +20 -0
  6. package/dist/commands/assistant-reminder.js.map +1 -0
  7. package/dist/commands/checkpoint-reminder.d.ts +14 -0
  8. package/dist/commands/checkpoint-reminder.js +50 -0
  9. package/dist/commands/checkpoint-reminder.js.map +1 -0
  10. package/dist/commands/friction-reminder.d.ts +92 -0
  11. package/dist/commands/friction-reminder.js +147 -0
  12. package/dist/commands/friction-reminder.js.map +1 -0
  13. package/dist/commands/frontdesk-reminder.d.ts +23 -0
  14. package/dist/commands/frontdesk-reminder.js +40 -0
  15. package/dist/commands/frontdesk-reminder.js.map +1 -0
  16. package/dist/commands/inbox-watch-supervisor.d.ts +25 -0
  17. package/dist/commands/inbox-watch-supervisor.js +112 -5
  18. package/dist/commands/inbox-watch-supervisor.js.map +1 -1
  19. package/dist/commands/init.js +31 -0
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/memory-reflex.d.ts +115 -0
  22. package/dist/commands/memory-reflex.js +243 -0
  23. package/dist/commands/memory-reflex.js.map +1 -0
  24. package/dist/commands/reminder-registry.d.ts +24 -0
  25. package/dist/commands/reminder-registry.js +76 -0
  26. package/dist/commands/reminder-registry.js.map +1 -0
  27. package/dist/commands/reminder-types.d.ts +76 -0
  28. package/dist/commands/reminder-types.js +8 -0
  29. package/dist/commands/reminder-types.js.map +1 -0
  30. package/dist/commands/setup-reminder.d.ts +9 -0
  31. package/dist/commands/setup-reminder.js +20 -0
  32. package/dist/commands/setup-reminder.js.map +1 -0
  33. package/dist/hook.js +472 -120
  34. package/dist/hook.js.map +1 -1
  35. package/dist/opencode-plugin.js +68 -8
  36. package/dist/opencode-plugin.js.map +1 -1
  37. package/package.json +1 -1
  38. package/skill/templates/reflex-chip.md +58 -0
@@ -0,0 +1,115 @@
1
+ /** memory-reflex — greprag episodic memory as a context-fill reflex (the first
2
+ * "greprag service as a reminder-interrupt module" beyond the harness's own arm/mechanic).
3
+ * docs/reminder-interrupt.md §Registry spec + the announce-featureset discussion (2026-06-20).
4
+ *
5
+ * THE IDEA: when the agent is about to answer without context it could have, surface the
6
+ * relevant project memory inline — seamlessly, no tool call. Three trigger layers feed ONE
7
+ * reminder (the hybrid): keyword (user signals recall) · self-report (agent flags its own
8
+ * gap) · nano (implicit gap). P1 = keyword (this file's `hasRecallIntent`) + the announce
9
+ * that teaches the capability; self-report (the marker below) + nano land next.
10
+ *
11
+ * SEARCH-THEN-INJECT, not nudge: a trigger fires a memory search in the hook; only a hit
12
+ * ABOVE the confidence gate is injected (top 1-2, capped). A miss = silence — so the search
13
+ * result is its own precision filter and the reflex never becomes wallpaper (the
14
+ * banner-blindness failure mode, fix `16a29a8b`). PURE module: the hook owns the search
15
+ * (I/O) + frames the hit via `buildMemoryInjection`, then routes the string through env —
16
+ * exactly the front-desk module's shape.
17
+ *
18
+ * EXCESSIVENESS is the watched risk (operator's call 2026-06-20): two filters bound it —
19
+ * high-precision keyword phrases (low fire rate) + the confidence gate (low inject rate) —
20
+ * and every fire is tallied (`MemoryReflexStats`) so /mechanic can monitor the rate and the
21
+ * inject/fire ratio, and auto-quiet if it turns noisy. adr: adr/memory-reflex.md */
22
+ import { ReminderModule } from './reminder-types';
23
+ /** Does this prompt signal a recall need? Pure, total — the hook gates the inline search
24
+ * on it. The query handed to the search is the prompt itself (the search is built for
25
+ * natural queries; fragile noun-extraction buys nothing). */
26
+ export declare function hasRecallIntent(prompt: string | null | undefined): boolean;
27
+ /** The marker the announce teaches the agent to emit when it notices its OWN context gap.
28
+ * Distinctive + greppable + cheap to type. The Stop-hook scan that consumes it (→ a
29
+ * next-turn pull) is P1b; defined here now so the announce + the regex share one source. */
30
+ export declare const RECALL_MARKER_RE: RegExp;
31
+ /** Extract the topic from a self-report marker in the agent's response (P1b — the Stop-scan).
32
+ * Returns the trimmed, capped topic, or null when no marker. Pure, total. */
33
+ export declare function extractRecallMarker(response: string | null | undefined): string | null;
34
+ /** Minimal hit shape the module needs — the hook maps the /v1/memory/query QueryNode onto
35
+ * this so the module stays decoupled from the search client. */
36
+ export interface MemoryHit {
37
+ content: string;
38
+ score: number;
39
+ confidence?: number | null;
40
+ shape?: string | null;
41
+ createdAt?: string | null;
42
+ projectName?: string | null;
43
+ }
44
+ /** Confidence gate — a hit must clear this to be injected. Below it, the reflex stays
45
+ * silent (the search result IS the precision filter). Reranked hits carry a calibrated
46
+ * `confidence` (0-1); unreranked hits fall back to a raw-score floor. */
47
+ export declare const MEMORY_CONFIDENCE_GATE = 0.5;
48
+ /** The hits that clear the gate, capped — the SINGLE source of "what gets shown", shared by the
49
+ * injection framing AND the efficacy capture, so the overlap is scored against exactly what
50
+ * was injected. */
51
+ export declare function selectGatedHits(hits: MemoryHit[]): MemoryHit[];
52
+ /** Raw content of the gated hits (no framing boilerplate) — stashed at inject-time so the
53
+ * Stop-hook overlap scores the response against the actual memory, not the frame. */
54
+ export declare function gatedHitText(hits: MemoryHit[]): string;
55
+ /** Frame the gated top hits into the inline injection block; null when nothing clears the
56
+ * gate (→ the module stays silent that turn). Tier is ambient (soft context, never a
57
+ * forced-decision directive). Tight + capped by design. */
58
+ export declare function buildMemoryInjection(hits: MemoryHit[]): string | null;
59
+ /** The business card, loaded once. Teaches BOTH the passive auto-surface AND (P1b, now live)
60
+ * the active self-report marker. Action-first, token-minimal, per the reminder-interrupt
61
+ * authoring rules. */
62
+ export declare function buildMemoryAnnounce(): string;
63
+ /** Did the agent's response reuse distinctive content from the injection? Pure, total. */
64
+ export declare function injectionWasUsed(hitText: string, prompt: string, response: string): boolean;
65
+ /** Per-trigger fire tally — the monitor reads this. `fires` = recall intent detected;
66
+ * `injected` = a hit cleared the gate and was surfaced; `silent` = fired but nothing cleared
67
+ * the gate (miss/timeout); `used` = the agent's response drew on an injection (the EFFICACY
68
+ * numerator, recorded at Stop). The two signals the monitor watches: a low injected/fires
69
+ * ratio = firing without finding (noise), and a low **used/injected** ratio = injecting
70
+ * without helping (the most important one — fires that never change behavior). Either,
71
+ * sustained → tighten or auto-quiet. */
72
+ export interface MemoryReflexStats {
73
+ fires: number;
74
+ injected: number;
75
+ silent: number;
76
+ used: number;
77
+ byTrigger: {
78
+ keyword: number;
79
+ selfReport: number;
80
+ flash: number;
81
+ };
82
+ firstFiredAt?: string;
83
+ lastFiredAt?: string;
84
+ lastTurnCount?: number;
85
+ }
86
+ export type MemoryTrigger = 'keyword' | 'selfReport' | 'flash';
87
+ /** Tally one fire — PURE (the hook supplies the clock). `didInject` splits the
88
+ * fire into injected vs silent so the monitor can read the inject/fire ratio. */
89
+ export declare function tallyMemoryFire(prior: MemoryReflexStats | null, trigger: MemoryTrigger, didInject: boolean, turnCount: number, nowIso: string): MemoryReflexStats;
90
+ /** Record that an injection was USED (the efficacy numerator) — PURE; bumped at Stop when the
91
+ * overlap proxy (or, later, the Flash-Lite judge) finds the response drew on the injection.
92
+ * Never decrements; `used ≤ injected` by construction of the call sites. */
93
+ export declare function tallyMemoryUsed(prior: MemoryReflexStats | null): MemoryReflexStats;
94
+ /** The server (the Flash-Lite judge, `@greprag/core` memory-efficacy) owns the
95
+ * auto-quiet THRESHOLD and ships its verdict on the /v1/memory/query response.
96
+ * The hook caches that boolean and honors it with a TTL — how long to trust a
97
+ * cached verdict before re-probing. While quiet the reflex skips the search, so
98
+ * no fresh verdict arrives and the cache ages; once stale the reflex fires again
99
+ * to re-probe and the server re-judges. That gives auto-quiet TEETH (a real
100
+ * silent window) while staying self-healing — a reflex that recovers is never
101
+ * permanently silenced (unlike the hard, manual mechanicKilled() switch). */
102
+ export declare const MEMORY_QUIET_CACHE_TTL_MS: number;
103
+ export interface MemoryQuietCache {
104
+ quiet: boolean;
105
+ cachedAt: string;
106
+ }
107
+ /** Does the locally-cached server verdict say "stay silent THIS turn"? PURE +
108
+ * total. Silent only when the cache is present, says quiet, and is still fresh.
109
+ * Fail-open everywhere else (missing / malformed / stale → NOT silent) so a bad
110
+ * cache can never permanently kill the reflex. */
111
+ export declare function quietCacheSaysSilent(cache: MemoryQuietCache | null | undefined, nowMs: number): boolean;
112
+ /** memory-reflex as a reminder-interrupt module. announce teaches memory once; reminder
113
+ * routes the hook-supplied injection (env.memoryHit); detect gates on its presence. PURE —
114
+ * the hook owns the keyword gate + the search + the framing + the tally. */
115
+ export declare const memoryReflexModule: ReminderModule;
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ /** memory-reflex — greprag episodic memory as a context-fill reflex (the first
3
+ * "greprag service as a reminder-interrupt module" beyond the harness's own arm/mechanic).
4
+ * docs/reminder-interrupt.md §Registry spec + the announce-featureset discussion (2026-06-20).
5
+ *
6
+ * THE IDEA: when the agent is about to answer without context it could have, surface the
7
+ * relevant project memory inline — seamlessly, no tool call. Three trigger layers feed ONE
8
+ * reminder (the hybrid): keyword (user signals recall) · self-report (agent flags its own
9
+ * gap) · nano (implicit gap). P1 = keyword (this file's `hasRecallIntent`) + the announce
10
+ * that teaches the capability; self-report (the marker below) + nano land next.
11
+ *
12
+ * SEARCH-THEN-INJECT, not nudge: a trigger fires a memory search in the hook; only a hit
13
+ * ABOVE the confidence gate is injected (top 1-2, capped). A miss = silence — so the search
14
+ * result is its own precision filter and the reflex never becomes wallpaper (the
15
+ * banner-blindness failure mode, fix `16a29a8b`). PURE module: the hook owns the search
16
+ * (I/O) + frames the hit via `buildMemoryInjection`, then routes the string through env —
17
+ * exactly the front-desk module's shape.
18
+ *
19
+ * EXCESSIVENESS is the watched risk (operator's call 2026-06-20): two filters bound it —
20
+ * high-precision keyword phrases (low fire rate) + the confidence gate (low inject rate) —
21
+ * and every fire is tallied (`MemoryReflexStats`) so /mechanic can monitor the rate and the
22
+ * inject/fire ratio, and auto-quiet if it turns noisy. adr: adr/memory-reflex.md */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.memoryReflexModule = exports.MEMORY_QUIET_CACHE_TTL_MS = exports.MEMORY_CONFIDENCE_GATE = exports.RECALL_MARKER_RE = void 0;
25
+ exports.hasRecallIntent = hasRecallIntent;
26
+ exports.extractRecallMarker = extractRecallMarker;
27
+ exports.selectGatedHits = selectGatedHits;
28
+ exports.gatedHitText = gatedHitText;
29
+ exports.buildMemoryInjection = buildMemoryInjection;
30
+ exports.buildMemoryAnnounce = buildMemoryAnnounce;
31
+ exports.injectionWasUsed = injectionWasUsed;
32
+ exports.tallyMemoryFire = tallyMemoryFire;
33
+ exports.tallyMemoryUsed = tallyMemoryUsed;
34
+ exports.quietCacheSaysSilent = quietCacheSaysSilent;
35
+ // ---------- Layer 1: keyword trigger (free, same-turn) -----------------------
36
+ /** High-precision recall-intent phrases — MULTI-WORD by design (a single word like
37
+ * "previously" over-fires). Each strongly implies the agent needs recalled project
38
+ * context it may not hold. Tuned conservatively; the /mechanic monitor reports the
39
+ * false-fire rate and this list is where we'd loosen/tighten. */
40
+ const RECALL_PHRASES = [
41
+ 'last time', 'we decided', 'we agreed', 'we chose', 'we discussed',
42
+ 'we talked about', 'you said', 'you mentioned', 'you told me',
43
+ 'remember when', 'remember that', 'what did we', 'what was our',
44
+ 'as we discussed', 'as i mentioned', 'earlier you', 'earlier we',
45
+ 'refresh my memory', 'catch me up', 'where did we leave',
46
+ 'the bug we', 'that bug we', 'the plan we', 'did we ever', 'how did we',
47
+ ];
48
+ const RECALL_RE = new RegExp(`\\b(${RECALL_PHRASES.join('|').replace(/ /g, '\\s+')})\\b`, 'i');
49
+ /** Does this prompt signal a recall need? Pure, total — the hook gates the inline search
50
+ * on it. The query handed to the search is the prompt itself (the search is built for
51
+ * natural queries; fragile noun-extraction buys nothing). */
52
+ function hasRecallIntent(prompt) {
53
+ return !!prompt && RECALL_RE.test(prompt);
54
+ }
55
+ // ---------- Layer 2: self-report marker (free, next-turn — scan lands in P1b) -
56
+ /** The marker the announce teaches the agent to emit when it notices its OWN context gap.
57
+ * Distinctive + greppable + cheap to type. The Stop-hook scan that consumes it (→ a
58
+ * next-turn pull) is P1b; defined here now so the announce + the regex share one source. */
59
+ exports.RECALL_MARKER_RE = /\[\[recall:\s*([^\]]+)\]\]/i;
60
+ /** Extract the topic from a self-report marker in the agent's response (P1b — the Stop-scan).
61
+ * Returns the trimmed, capped topic, or null when no marker. Pure, total. */
62
+ function extractRecallMarker(response) {
63
+ const m = exports.RECALL_MARKER_RE.exec(response || '');
64
+ if (!m)
65
+ return null;
66
+ const topic = (m[1] || '').trim();
67
+ return topic ? topic.slice(0, 200) : null;
68
+ }
69
+ /** Confidence gate — a hit must clear this to be injected. Below it, the reflex stays
70
+ * silent (the search result IS the precision filter). Reranked hits carry a calibrated
71
+ * `confidence` (0-1); unreranked hits fall back to a raw-score floor. */
72
+ exports.MEMORY_CONFIDENCE_GATE = 0.5;
73
+ const RAW_SCORE_FLOOR = 0.15;
74
+ /** Hard cap — never inject more than this many hits (context-bloat discipline; we just
75
+ * spent a session draining the heap). */
76
+ const MAX_INJECTED = 2;
77
+ /** Per-hit trim — keep each injected snippet tight. */
78
+ const HIT_TRIM = 280;
79
+ function passesGate(h) {
80
+ return typeof h.confidence === 'number'
81
+ ? h.confidence >= exports.MEMORY_CONFIDENCE_GATE
82
+ : h.score >= RAW_SCORE_FLOOR;
83
+ }
84
+ /** The hits that clear the gate, capped — the SINGLE source of "what gets shown", shared by the
85
+ * injection framing AND the efficacy capture, so the overlap is scored against exactly what
86
+ * was injected. */
87
+ function selectGatedHits(hits) {
88
+ return (hits || []).filter(passesGate).slice(0, MAX_INJECTED);
89
+ }
90
+ /** Raw content of the gated hits (no framing boilerplate) — stashed at inject-time so the
91
+ * Stop-hook overlap scores the response against the actual memory, not the frame. */
92
+ function gatedHitText(hits) {
93
+ return selectGatedHits(hits).map(h => h.content || '').join('\n');
94
+ }
95
+ function trim(s, n) {
96
+ const t = (s || '').replace(/\s+/g, ' ').trim();
97
+ return t.length > n ? t.slice(0, n - 1) + '…' : t;
98
+ }
99
+ /** Frame the gated top hits into the inline injection block; null when nothing clears the
100
+ * gate (→ the module stays silent that turn). Tier is ambient (soft context, never a
101
+ * forced-decision directive). Tight + capped by design. */
102
+ function buildMemoryInjection(hits) {
103
+ const passing = selectGatedHits(hits);
104
+ if (passing.length === 0)
105
+ return null;
106
+ const lines = ['[📁 greprag memory — possibly relevant, auto-surfaced (pull more: `greprag memory search`):'];
107
+ for (const h of passing) {
108
+ const meta = [h.shape, h.createdAt ? h.createdAt.slice(0, 10) : null, h.projectName]
109
+ .filter(Boolean).join(', ');
110
+ lines.push(` · ${trim(h.content, HIT_TRIM)}${meta ? ` (${meta})` : ''}`);
111
+ }
112
+ lines.push(']');
113
+ return lines.join('\n');
114
+ }
115
+ // ---------- SessionStart announce (teach the capability once) -----------------
116
+ /** The business card, loaded once. Teaches BOTH the passive auto-surface AND (P1b, now live)
117
+ * the active self-report marker. Action-first, token-minimal, per the reminder-interrupt
118
+ * authoring rules. */
119
+ function buildMemoryAnnounce() {
120
+ return [
121
+ '[greprag memory — this project has episodic memory (every past session, searchable).]',
122
+ 'When THIS turn leans on earlier work — a past decision, a bug you hit before, what was discussed — relevant memory is auto-surfaced inline; use it.',
123
+ 'When you notice you are MISSING context this project would hold and it did NOT surface, emit `[[recall: <topic>]]` in your reply — that topic is pulled in on your next turn.',
124
+ 'Or pull on demand any time: `greprag memory search "<topic>"` (`greprag memory recap` to catch up).',
125
+ ].join('\n');
126
+ }
127
+ // ---------- Efficacy: did the injection get USED (the overlap proxy) ----------
128
+ /** Free overlap proxy for "did the response draw on the injected memory" — the efficacy
129
+ * numerator's cheap first rung. (The Flash-Lite judge calibrates/replaces it later,
130
+ * server-side; greprag's judge model is Gemini Flash-Lite, never nano.) Distinctive CONTENT
131
+ * terms the injection introduced — in the hit, NOT already in the prompt, not stopwords /
132
+ * frame boilerplate — that reappear in the response. ≥ USED_MIN_TERMS reused → used.
133
+ * Imperfect (paraphrase escapes it, coincidence trips it) but a real aggregate signal at
134
+ * zero cost + zero latency. */
135
+ const USED_MIN_TERMS = 2;
136
+ const MIN_TERM_LEN = 4;
137
+ const TERM_STOPWORDS = new Set([
138
+ 'this', 'that', 'with', 'from', 'have', 'were', 'your', 'they', 'them', 'then', 'than',
139
+ 'will', 'would', 'could', 'should', 'about', 'there', 'their', 'what', 'when', 'which',
140
+ 'into', 'over', 'also', 'been', 'more', 'most', 'some', 'such', 'only', 'just', 'like',
141
+ 'here', 'where', 'these', 'those', 'them', 'because',
142
+ // injection-frame boilerplate — never count these as "used":
143
+ 'greprag', 'memory', 'relevant', 'surfaced', 'auto', 'session', 'project', 'search', 'recap', 'pull',
144
+ ]);
145
+ function contentTerms(text) {
146
+ const out = new Set();
147
+ for (const raw of (text || '').toLowerCase().split(/[^a-z0-9]+/)) {
148
+ if (raw.length >= MIN_TERM_LEN && !TERM_STOPWORDS.has(raw))
149
+ out.add(raw);
150
+ }
151
+ return out;
152
+ }
153
+ /** Did the agent's response reuse distinctive content from the injection? Pure, total. */
154
+ function injectionWasUsed(hitText, prompt, response) {
155
+ const hitTerms = contentTerms(hitText);
156
+ if (hitTerms.size === 0)
157
+ return false;
158
+ const promptTerms = contentTerms(prompt);
159
+ const respTerms = contentTerms(response);
160
+ let used = 0;
161
+ for (const t of hitTerms) {
162
+ if (promptTerms.has(t))
163
+ continue; // not distinctive — already in the prompt
164
+ if (respTerms.has(t) && ++used >= USED_MIN_TERMS)
165
+ return true;
166
+ }
167
+ return false;
168
+ }
169
+ function normalizeStats(prior) {
170
+ const p = (prior && typeof prior === 'object') ? prior : {};
171
+ const bt = (p.byTrigger && typeof p.byTrigger === 'object') ? p.byTrigger : {};
172
+ return {
173
+ fires: Number.isFinite(p.fires) ? p.fires : 0,
174
+ injected: Number.isFinite(p.injected) ? p.injected : 0,
175
+ silent: Number.isFinite(p.silent) ? p.silent : 0,
176
+ used: Number.isFinite(p.used) ? p.used : 0,
177
+ byTrigger: {
178
+ keyword: Number.isFinite(bt.keyword) ? bt.keyword : 0,
179
+ selfReport: Number.isFinite(bt.selfReport) ? bt.selfReport : 0,
180
+ flash: Number.isFinite(bt.flash) ? bt.flash : 0,
181
+ },
182
+ firstFiredAt: p.firstFiredAt,
183
+ lastFiredAt: p.lastFiredAt,
184
+ lastTurnCount: p.lastTurnCount,
185
+ };
186
+ }
187
+ /** Tally one fire — PURE (the hook supplies the clock). `didInject` splits the
188
+ * fire into injected vs silent so the monitor can read the inject/fire ratio. */
189
+ function tallyMemoryFire(prior, trigger, didInject, turnCount, nowIso) {
190
+ const s = normalizeStats(prior);
191
+ s.fires += 1;
192
+ if (didInject)
193
+ s.injected += 1;
194
+ else
195
+ s.silent += 1;
196
+ s.byTrigger[trigger] += 1;
197
+ s.firstFiredAt = s.firstFiredAt || nowIso;
198
+ s.lastFiredAt = nowIso;
199
+ if (Number.isFinite(turnCount))
200
+ s.lastTurnCount = turnCount;
201
+ return s;
202
+ }
203
+ /** Record that an injection was USED (the efficacy numerator) — PURE; bumped at Stop when the
204
+ * overlap proxy (or, later, the Flash-Lite judge) finds the response drew on the injection.
205
+ * Never decrements; `used ≤ injected` by construction of the call sites. */
206
+ function tallyMemoryUsed(prior) {
207
+ const s = normalizeStats(prior);
208
+ s.used += 1;
209
+ return s;
210
+ }
211
+ // ---------- Auto-quiet (server efficacy verdict, cached locally) --------------
212
+ /** The server (the Flash-Lite judge, `@greprag/core` memory-efficacy) owns the
213
+ * auto-quiet THRESHOLD and ships its verdict on the /v1/memory/query response.
214
+ * The hook caches that boolean and honors it with a TTL — how long to trust a
215
+ * cached verdict before re-probing. While quiet the reflex skips the search, so
216
+ * no fresh verdict arrives and the cache ages; once stale the reflex fires again
217
+ * to re-probe and the server re-judges. That gives auto-quiet TEETH (a real
218
+ * silent window) while staying self-healing — a reflex that recovers is never
219
+ * permanently silenced (unlike the hard, manual mechanicKilled() switch). */
220
+ exports.MEMORY_QUIET_CACHE_TTL_MS = 12 * 60 * 60 * 1000; // 12h
221
+ /** Does the locally-cached server verdict say "stay silent THIS turn"? PURE +
222
+ * total. Silent only when the cache is present, says quiet, and is still fresh.
223
+ * Fail-open everywhere else (missing / malformed / stale → NOT silent) so a bad
224
+ * cache can never permanently kill the reflex. */
225
+ function quietCacheSaysSilent(cache, nowMs) {
226
+ if (!cache || cache.quiet !== true || typeof cache.cachedAt !== 'string')
227
+ return false;
228
+ const t = Date.parse(cache.cachedAt);
229
+ if (!Number.isFinite(t))
230
+ return false;
231
+ return (nowMs - t) < exports.MEMORY_QUIET_CACHE_TTL_MS;
232
+ }
233
+ // ---------- The registry module ----------------------------------------------
234
+ /** memory-reflex as a reminder-interrupt module. announce teaches memory once; reminder
235
+ * routes the hook-supplied injection (env.memoryHit); detect gates on its presence. PURE —
236
+ * the hook owns the keyword gate + the search + the framing + the tally. */
237
+ exports.memoryReflexModule = {
238
+ id: 'memory-reflex',
239
+ detect: (env) => (env.memoryHit ? { tier: 'ambient' } : { tier: 'silent' }),
240
+ announce: () => buildMemoryAnnounce(),
241
+ reminder: (_d, env) => env.memoryHit ?? null,
242
+ };
243
+ //# sourceMappingURL=memory-reflex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-reflex.js","sourceRoot":"","sources":["../../src/commands/memory-reflex.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;qFAoBqF;;;AAuBrF,0CAEC;AAWD,kDAKC;AAmCD,0CAEC;AAID,oCAEC;AAUD,oDAWC;AAOD,kDAOC;AA+BD,4CAWC;AA6CD,0CAYC;AAKD,0CAIC;AAuBD,oDAKC;AA3PD,gFAAgF;AAEhF;;;kEAGkE;AAClE,MAAM,cAAc,GAAG;IACrB,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc;IAClE,iBAAiB,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa;IAC7D,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc;IAC/D,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY;IAChE,mBAAmB,EAAE,aAAa,EAAE,oBAAoB;IACxD,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY;CACxE,CAAC;AACF,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/F;;8DAE8D;AAC9D,SAAgB,eAAe,CAAC,MAAiC;IAC/D,OAAO,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,iFAAiF;AAEjF;;6FAE6F;AAChF,QAAA,gBAAgB,GAAG,6BAA6B,CAAC;AAE9D;8EAC8E;AAC9E,SAAgB,mBAAmB,CAAC,QAAmC;IACrE,MAAM,CAAC,GAAG,wBAAgB,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAeD;;0EAE0E;AAC7D,QAAA,sBAAsB,GAAG,GAAG,CAAC;AAC1C,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B;0CAC0C;AAC1C,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,uDAAuD;AACvD,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErB,SAAS,UAAU,CAAC,CAAY;IAC9B,OAAO,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QACrC,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,8BAAsB;QACxC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,eAAe,CAAC;AACjC,CAAC;AAED;;oBAEoB;AACpB,SAAgB,eAAe,CAAC,IAAiB;IAC/C,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAChE,CAAC;AAED;sFACsF;AACtF,SAAgB,YAAY,CAAC,IAAiB;IAC5C,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,IAAI,CAAC,CAAS,EAAE,CAAS;IAChC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;4DAE4D;AAC5D,SAAgB,oBAAoB,CAAC,IAAiB;IACpD,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,KAAK,GAAa,CAAC,6FAA6F,CAAC,CAAC;IACxH,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC;aACjF,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,iFAAiF;AAEjF;;uBAEuB;AACvB,SAAgB,mBAAmB;IACjC,OAAO;QACL,uFAAuF;QACvF,qJAAqJ;QACrJ,+KAA+K;QAC/K,qGAAqG;KACtG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,iFAAiF;AAEjF;;;;;;gCAMgC;AAChC,MAAM,cAAc,GAAG,CAAC,CAAC;AACzB,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IACtF,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IACtF,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IACtF,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS;IACpD,6DAA6D;IAC7D,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;CACrG,CAAC,CAAC;AAEH,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACjE,IAAI,GAAG,CAAC,MAAM,IAAI,YAAY,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0FAA0F;AAC1F,SAAgB,gBAAgB,CAAC,OAAe,EAAE,MAAc,EAAE,QAAgB;IAChF,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAS,CAAkB,0CAA0C;QAC7F,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,cAAc;YAAE,OAAO,IAAI,CAAC;IAChE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAwBD,SAAS,cAAc,CAAC,KAA2C;IACjE,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,EAAwB,CAAC;IACnF,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,EAAqC,CAAC;IACnH,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7C,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAChD,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1C,SAAS,EAAE;YACT,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACrD,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAC9D,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAChD;QACD,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,aAAa,EAAE,CAAC,CAAC,aAAa;KAC/B,CAAC;AACJ,CAAC;AAED;kFACkF;AAClF,SAAgB,eAAe,CAC7B,KAA+B,EAAE,OAAsB,EAAE,SAAkB,EAC3E,SAAiB,EAAE,MAAc;IAEjC,MAAM,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACb,IAAI,SAAS;QAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC;;QAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;IACnD,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,IAAI,MAAM,CAAC;IAC1C,CAAC,CAAC,WAAW,GAAG,MAAM,CAAC;IACvB,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,CAAC,CAAC,aAAa,GAAG,SAAS,CAAC;IAC5D,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;6EAE6E;AAC7E,SAAgB,eAAe,CAAC,KAA+B;IAC7D,MAAM,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IACZ,OAAO,CAAC,CAAC;AACX,CAAC;AAED,iFAAiF;AAEjF;;;;;;;8EAO8E;AACjE,QAAA,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAG,MAAM;AAOtE;;;mDAGmD;AACnD,SAAgB,oBAAoB,CAAC,KAA0C,EAAE,KAAa;IAC5F,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACvF,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,iCAAyB,CAAC;AACjD,CAAC;AAED,gFAAgF;AAEhF;;6EAE6E;AAChE,QAAA,kBAAkB,GAAmB;IAChD,EAAE,EAAE,eAAe;IACnB,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAc;IACxF,QAAQ,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE;IACrC,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI;CAC7C,CAAC"}
@@ -0,0 +1,24 @@
1
+ /** The reminder-interrupt registry CONTAINER (docs/reminder-interrupt.md §Registry
2
+ * spec). One loop over all modules: per turn, fire each module's thin reminder while
3
+ * its detector says deficient; at SessionStart, emit each announce. Stacking welcome.
4
+ * PURE over ReminderEnv — the hook assembles env (i/o) and emits the returned lines;
5
+ * a broken module never blocks a turn (fail-open per module). */
6
+ import { ReminderEnv, ReminderModule, Detection } from './reminder-types';
7
+ /** Registry order = display order. THE single agent-facing announce/reminder assembly:
8
+ * the hook does I/O → fills ReminderEnv → collectAnnounces (SessionStart) / collectReminders
9
+ * (per turn) render every module here in this order. Order preserves the historical recap
10
+ * preamble (setup-warning → front-desk → checkpoint → assistant-doctrine) and keeps the two
11
+ * loud, action-first modules last (watcher-arm → mechanic) so they read most-recent at
12
+ * SessionStart. Add a module here to wire it into BOTH surfaces at once. */
13
+ export declare const REGISTRY: ReminderModule[];
14
+ export interface FiredReminder {
15
+ id: string;
16
+ tier: Detection['tier'];
17
+ line: string;
18
+ }
19
+ /** Per-turn: every module's live reminder line (skipping silent), in registry order.
20
+ * Stacking — a turn may carry several. A detector or reminder that throws drops that
21
+ * module for the turn; it never blocks the others or the turn. */
22
+ export declare function collectReminders(env: ReminderEnv, registry?: ReminderModule[]): FiredReminder[];
23
+ /** SessionStart: every module's announce (skipping null), in registry order. */
24
+ export declare function collectAnnounces(env: ReminderEnv, registry?: ReminderModule[]): string[];
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /** The reminder-interrupt registry CONTAINER (docs/reminder-interrupt.md §Registry
3
+ * spec). One loop over all modules: per turn, fire each module's thin reminder while
4
+ * its detector says deficient; at SessionStart, emit each announce. Stacking welcome.
5
+ * PURE over ReminderEnv — the hook assembles env (i/o) and emits the returned lines;
6
+ * a broken module never blocks a turn (fail-open per module). */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.REGISTRY = void 0;
9
+ exports.collectReminders = collectReminders;
10
+ exports.collectAnnounces = collectAnnounces;
11
+ const setup_reminder_1 = require("./setup-reminder");
12
+ const frontdesk_reminder_1 = require("./frontdesk-reminder");
13
+ const checkpoint_reminder_1 = require("./checkpoint-reminder");
14
+ const assistant_reminder_1 = require("./assistant-reminder");
15
+ const memory_reflex_1 = require("./memory-reflex");
16
+ const arm_reminder_1 = require("./arm-reminder");
17
+ const friction_reminder_1 = require("./friction-reminder");
18
+ /** Registry order = display order. THE single agent-facing announce/reminder assembly:
19
+ * the hook does I/O → fills ReminderEnv → collectAnnounces (SessionStart) / collectReminders
20
+ * (per turn) render every module here in this order. Order preserves the historical recap
21
+ * preamble (setup-warning → front-desk → checkpoint → assistant-doctrine) and keeps the two
22
+ * loud, action-first modules last (watcher-arm → mechanic) so they read most-recent at
23
+ * SessionStart. Add a module here to wire it into BOTH surfaces at once. */
24
+ exports.REGISTRY = [
25
+ setup_reminder_1.setupWarningModule,
26
+ frontdesk_reminder_1.frontDeskModule,
27
+ checkpoint_reminder_1.checkpointModule,
28
+ assistant_reminder_1.assistantDoctrineModule,
29
+ memory_reflex_1.memoryReflexModule,
30
+ arm_reminder_1.watcherArmModule,
31
+ friction_reminder_1.mechanicFrictionModule,
32
+ ];
33
+ /** Per-turn: every module's live reminder line (skipping silent), in registry order.
34
+ * Stacking — a turn may carry several. A detector or reminder that throws drops that
35
+ * module for the turn; it never blocks the others or the turn. */
36
+ function collectReminders(env, registry = exports.REGISTRY) {
37
+ const out = [];
38
+ for (const m of registry) {
39
+ let d;
40
+ try {
41
+ d = m.detect(env);
42
+ }
43
+ catch {
44
+ continue;
45
+ }
46
+ if (!d || d.tier === 'silent')
47
+ continue;
48
+ let line = null;
49
+ try {
50
+ line = m.reminder(d, env);
51
+ }
52
+ catch {
53
+ continue;
54
+ }
55
+ if (line)
56
+ out.push({ id: m.id, tier: d.tier, line });
57
+ }
58
+ return out;
59
+ }
60
+ /** SessionStart: every module's announce (skipping null), in registry order. */
61
+ function collectAnnounces(env, registry = exports.REGISTRY) {
62
+ const out = [];
63
+ for (const m of registry) {
64
+ let a = null;
65
+ try {
66
+ a = m.announce(env);
67
+ }
68
+ catch {
69
+ continue;
70
+ }
71
+ if (a)
72
+ out.push(a);
73
+ }
74
+ return out;
75
+ }
76
+ //# sourceMappingURL=reminder-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reminder-registry.js","sourceRoot":"","sources":["../../src/commands/reminder-registry.ts"],"names":[],"mappings":";AAAA;;;;kEAIkE;;;AAoClE,4CAaC;AAGD,4CAUC;AA3DD,qDAAsD;AACtD,6DAAuD;AACvD,+DAAyD;AACzD,6DAA+D;AAC/D,mDAAqD;AACrD,iDAAkD;AAClD,2DAA6D;AAE7D;;;;;6EAK6E;AAChE,QAAA,QAAQ,GAAqB;IACxC,mCAAkB;IAClB,oCAAe;IACf,sCAAgB;IAChB,4CAAuB;IACvB,kCAAkB;IAClB,+BAAgB;IAChB,0CAAsB;CACvB,CAAC;AAQF;;mEAEmE;AACnE,SAAgB,gBAAgB,CAC9B,GAAgB,EAAE,WAA6B,gBAAQ;IAEvD,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAY,CAAC;QACjB,IAAI,CAAC;YAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QAC9C,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QACxC,IAAI,IAAI,GAAkB,IAAI,CAAC;QAC/B,IAAI,CAAC;YAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QACtD,IAAI,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gFAAgF;AAChF,SAAgB,gBAAgB,CAC9B,GAAgB,EAAE,WAA6B,gBAAQ;IAEvD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,GAAkB,IAAI,CAAC;QAC5B,IAAI,CAAC;YAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QAChD,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,76 @@
1
+ /** Shared types for the reminder-interrupt registry (docs/reminder-interrupt.md
2
+ * §Registry spec — the detector model). Modeled on the WORKING arm-check loop:
3
+ * detect a live external deficiency → fire while it holds → auto-silence the
4
+ * instant it resolves. `detect(env)` is the primitive (the arm-check's
5
+ * `isLocallyArmed`, generalized); a turn counter is not. */
6
+ /** Firing-tier ladder. `silent` = the deficiency is resolved (the auto-stop); the
7
+ * rest escalate wording intensity with the cost of inaction (ambient = soft
8
+ * pointer, nudge = imperative + reason, nag = LOUD forced-decision). */
9
+ export type Tier = 'silent' | 'ambient' | 'nudge' | 'nag';
10
+ /** The live-state bag the hook assembles ONCE per turn — ALL i/o lives in the hook
11
+ * so modules stay pure (and their detect→silent transition stays unit-testable).
12
+ * Grows as modules need new signals. */
13
+ export interface ReminderEnv {
14
+ /** 8-hex session id. */
15
+ short: string;
16
+ /** Turns elapsed this session (for any ambient-cadence module). */
17
+ turnCount: number;
18
+ /** Live behavioral stress (docs/stress-trigger.md): 0 calm / 1 elevated / 2 high. */
19
+ stress: number;
20
+ /** Live local watcher present? (isLocallyArmed) — the arm-check's signal. */
21
+ armed: boolean;
22
+ /** Unread messages addressed to THIS session (own mail — NOT the project-wide
23
+ * v5.6.1 eavesdrop count). */
24
+ sessionUnread: number;
25
+ /** Owning claude.exe PID — baked into the arm command. */
26
+ ownerPid?: number | null;
27
+ /** Identity handle (alias before @) for reply-address templating. */
28
+ alias?: string | null;
29
+ /** Assistant project → elevated watcher arm. */
30
+ assistant?: boolean;
31
+ /** Project display name (for the checkpoint announce). */
32
+ projectName?: string;
33
+ /** Precomputed identity setup/drift warning — the hook owns the anchor logic AND its
34
+ * rate-limit stamp (a side-effect), so this module half is a pass-through. */
35
+ setupWarning?: string | null;
36
+ /** Front-desk unread count (the SessionStart count announce). */
37
+ frontDeskUnread?: number;
38
+ /** Open checkpoints for this project (the SessionStart landmark announce). */
39
+ openCheckpoints?: OpenCheckpointSlim[];
40
+ /** Precomputed assistant-doctrine context — the hook owns the doctrine-file read, so
41
+ * this module half is a pass-through. Null for any non-assistant project. */
42
+ assistantDoctrine?: string | null;
43
+ /** Envelope-only "you've got mail" front-desk notice — the hook owns the network +
44
+ * per-machine dedup; the front-desk module routes it as its per-turn reminder. */
45
+ frontDeskNotice?: string | null;
46
+ /** Auto-surfaced episodic-memory injection — the hook owns the recall-intent gate +
47
+ * the search + the confidence gate + framing; the memory-reflex module routes the
48
+ * framed string as its per-turn reminder. Null = nothing cleared the gate this turn. */
49
+ memoryHit?: string | null;
50
+ }
51
+ /** Slim open-checkpoint row (the SessionStart landmark announce, checkpoint module).
52
+ * Lives here — not in checkpoint-reminder.ts — because ReminderEnv references it and a
53
+ * module file importing from here would otherwise close an import cycle. */
54
+ export interface OpenCheckpointSlim {
55
+ nodeId: string;
56
+ title: string;
57
+ createdAt: string;
58
+ }
59
+ /** A module's per-turn read of its deficiency. `tier:'silent'` = resolved → no fire. */
60
+ export interface Detection {
61
+ tier: Tier;
62
+ /** Optional grounding data the reminder line interpolates (e.g. unread count). */
63
+ detail?: Record<string, unknown>;
64
+ }
65
+ /** A reminder-interrupt registry module (docs/reminder-interrupt.md §Registry spec).
66
+ * PURE over ReminderEnv — the container/hook owns all i/o, so detect→silent is the
67
+ * unit-testable regression-lock. */
68
+ export interface ReminderModule {
69
+ id: string;
70
+ /** Read the live deficiency. The gate: `silent` ⇒ the module is quiet this turn. */
71
+ detect: (env: ReminderEnv) => Detection;
72
+ /** SessionStart schema, loaded once (teach method + why). null = nothing to announce. */
73
+ announce: (env: ReminderEnv) => string | null;
74
+ /** Per-turn thin line for a live detection; null when silent. */
75
+ reminder: (d: Detection, env: ReminderEnv) => string | null;
76
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /** Shared types for the reminder-interrupt registry (docs/reminder-interrupt.md
3
+ * §Registry spec — the detector model). Modeled on the WORKING arm-check loop:
4
+ * detect a live external deficiency → fire while it holds → auto-silence the
5
+ * instant it resolves. `detect(env)` is the primitive (the arm-check's
6
+ * `isLocallyArmed`, generalized); a turn counter is not. */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ //# sourceMappingURL=reminder-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reminder-types.js","sourceRoot":"","sources":["../../src/commands/reminder-types.ts"],"names":[],"mappings":";AAAA;;;;6DAI6D"}
@@ -0,0 +1,9 @@
1
+ /** setup-warning — the identity setup/drift warning as a reminder-interrupt module
2
+ * (docs/reminder-interrupt.md §Registry spec). announce-ONLY + pass-through: the warning
3
+ * string is PRECOMPUTED by the hook because deriving it stamps a weekly rate-limit
4
+ * (a side-effect) and reads the git-derived id — I/O that must stay in the hook. The
5
+ * module just routes the result and gates on its presence. nudge tier: identity drift
6
+ * silently scatters memory across UUIDs, so it is worth more than an ambient pointer. */
7
+ import { ReminderEnv, Detection, ReminderModule } from './reminder-types';
8
+ export declare function setupDetect(env: ReminderEnv): Detection;
9
+ export declare const setupWarningModule: ReminderModule;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /** setup-warning — the identity setup/drift warning as a reminder-interrupt module
3
+ * (docs/reminder-interrupt.md §Registry spec). announce-ONLY + pass-through: the warning
4
+ * string is PRECOMPUTED by the hook because deriving it stamps a weekly rate-limit
5
+ * (a side-effect) and reads the git-derived id — I/O that must stay in the hook. The
6
+ * module just routes the result and gates on its presence. nudge tier: identity drift
7
+ * silently scatters memory across UUIDs, so it is worth more than an ambient pointer. */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.setupWarningModule = void 0;
10
+ exports.setupDetect = setupDetect;
11
+ function setupDetect(env) {
12
+ return env.setupWarning ? { tier: 'nudge' } : { tier: 'silent' };
13
+ }
14
+ exports.setupWarningModule = {
15
+ id: 'setup-warning',
16
+ detect: setupDetect,
17
+ announce: (env) => env.setupWarning ?? null,
18
+ reminder: () => null,
19
+ };
20
+ //# sourceMappingURL=setup-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-reminder.js","sourceRoot":"","sources":["../../src/commands/setup-reminder.ts"],"names":[],"mappings":";AAAA;;;;;0FAK0F;;;AAI1F,kCAEC;AAFD,SAAgB,WAAW,CAAC,GAAgB;IAC1C,OAAO,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACnE,CAAC;AAEY,QAAA,kBAAkB,GAAmB;IAChD,EAAE,EAAE,eAAe;IACnB,MAAM,EAAE,WAAW;IACnB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI;IAC3C,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;CACrB,CAAC"}