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.
- package/dist/commands/arm-reminder.d.ts +25 -0
- package/dist/commands/arm-reminder.js +65 -0
- package/dist/commands/arm-reminder.js.map +1 -0
- package/dist/commands/assistant-reminder.d.ts +9 -0
- package/dist/commands/assistant-reminder.js +20 -0
- package/dist/commands/assistant-reminder.js.map +1 -0
- package/dist/commands/checkpoint-reminder.d.ts +14 -0
- package/dist/commands/checkpoint-reminder.js +50 -0
- package/dist/commands/checkpoint-reminder.js.map +1 -0
- package/dist/commands/friction-reminder.d.ts +92 -0
- package/dist/commands/friction-reminder.js +147 -0
- package/dist/commands/friction-reminder.js.map +1 -0
- package/dist/commands/frontdesk-reminder.d.ts +23 -0
- package/dist/commands/frontdesk-reminder.js +40 -0
- package/dist/commands/frontdesk-reminder.js.map +1 -0
- package/dist/commands/inbox-watch-supervisor.d.ts +25 -0
- package/dist/commands/inbox-watch-supervisor.js +112 -5
- package/dist/commands/inbox-watch-supervisor.js.map +1 -1
- package/dist/commands/init.js +31 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/memory-reflex.d.ts +115 -0
- package/dist/commands/memory-reflex.js +243 -0
- package/dist/commands/memory-reflex.js.map +1 -0
- package/dist/commands/reminder-registry.d.ts +24 -0
- package/dist/commands/reminder-registry.js +76 -0
- package/dist/commands/reminder-registry.js.map +1 -0
- package/dist/commands/reminder-types.d.ts +76 -0
- package/dist/commands/reminder-types.js +8 -0
- package/dist/commands/reminder-types.js.map +1 -0
- package/dist/commands/setup-reminder.d.ts +9 -0
- package/dist/commands/setup-reminder.js +20 -0
- package/dist/commands/setup-reminder.js.map +1 -0
- package/dist/hook.js +472 -120
- package/dist/hook.js.map +1 -1
- package/dist/opencode-plugin.js +68 -8
- package/dist/opencode-plugin.js.map +1 -1
- package/package.json +1 -1
- package/skill/templates/reflex-chip.md +58 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** watcherArm — the REFERENCE reminder-interrupt module (docs/reminder-interrupt.md
|
|
2
|
+
* §Registry spec). The working pattern the whole registry is modeled on: detect the
|
|
3
|
+
* watcher deficiency (unarmed) → fire while down → auto-silence when armed →
|
|
4
|
+
* self-heal (watcher dies → next turn re-detects). Pure over ReminderEnv; the hook
|
|
5
|
+
* supplies `armed` (isLocallyArmed) + `sessionUnread` (this session's own mail).
|
|
6
|
+
* adr: adr/session-id-awareness.md */
|
|
7
|
+
import { ReminderEnv, Detection, ReminderModule } from './reminder-types';
|
|
8
|
+
/** SessionStart announce — the heavy half, loaded once (+ re-fired on compact via the
|
|
9
|
+
* session-id hook). Leads with ARM NOW (not "here's how for later") so the agent arms
|
|
10
|
+
* immediately; states peers ARE reaching this session, and teaches the drop-anytime →
|
|
11
|
+
* MUST re-arm fact that licenses the per-turn reminder. Carries the deferred-Monitor
|
|
12
|
+
* call shape (ToolSearch select:Monitor + required description/timeout_ms) so the arm
|
|
13
|
+
* call succeeds unaided. Do NOT trim the ToolSearch clause — the deferral made it
|
|
14
|
+
* load-bearing (adr/session-id-awareness.md 2026-05-29). adr: adr/session-id-awareness.md */
|
|
15
|
+
export declare function buildArmAnnounce(short: string, ownerPid?: number | null, alias?: string | null, assistant?: boolean): string;
|
|
16
|
+
/** Detect the watcher deficiency. armed → silent (the auto-stop). Unarmed → nag when a
|
|
17
|
+
* peer has actually messaged this session (grounded + TRUE — nag only when
|
|
18
|
+
* sessionUnread>0, so it never decays into a cried-wolf banner), else nudge. */
|
|
19
|
+
export declare function armDetect(env: ReminderEnv): Detection;
|
|
20
|
+
/** Per-turn thin line. nag = a real peer message waits (true by construction); nudge =
|
|
21
|
+
* unarmed with no mail. The heavy how-to-arm lives in the SessionStart announce. */
|
|
22
|
+
export declare function buildArmReminder(d: Detection): string | null;
|
|
23
|
+
/** The reference module. detect = unarmed; announce = the urgent how-to; reminder = the
|
|
24
|
+
* thin line. Wired into the registry container (reminder-registry.ts). */
|
|
25
|
+
export declare const watcherArmModule: ReminderModule;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** watcherArm — the REFERENCE reminder-interrupt module (docs/reminder-interrupt.md
|
|
3
|
+
* §Registry spec). The working pattern the whole registry is modeled on: detect the
|
|
4
|
+
* watcher deficiency (unarmed) → fire while down → auto-silence when armed →
|
|
5
|
+
* self-heal (watcher dies → next turn re-detects). Pure over ReminderEnv; the hook
|
|
6
|
+
* supplies `armed` (isLocallyArmed) + `sessionUnread` (this session's own mail).
|
|
7
|
+
* adr: adr/session-id-awareness.md */
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.watcherArmModule = void 0;
|
|
10
|
+
exports.buildArmAnnounce = buildArmAnnounce;
|
|
11
|
+
exports.armDetect = armDetect;
|
|
12
|
+
exports.buildArmReminder = buildArmReminder;
|
|
13
|
+
const session_id_1 = require("../session-id");
|
|
14
|
+
/** SessionStart announce — the heavy half, loaded once (+ re-fired on compact via the
|
|
15
|
+
* session-id hook). Leads with ARM NOW (not "here's how for later") so the agent arms
|
|
16
|
+
* immediately; states peers ARE reaching this session, and teaches the drop-anytime →
|
|
17
|
+
* MUST re-arm fact that licenses the per-turn reminder. Carries the deferred-Monitor
|
|
18
|
+
* call shape (ToolSearch select:Monitor + required description/timeout_ms) so the arm
|
|
19
|
+
* call succeeds unaided. Do NOT trim the ToolSearch clause — the deferral made it
|
|
20
|
+
* load-bearing (adr/session-id-awareness.md 2026-05-29). adr: adr/session-id-awareness.md */
|
|
21
|
+
function buildArmAnnounce(short, ownerPid, alias = null, assistant = false) {
|
|
22
|
+
const handle = alias || '<handle>';
|
|
23
|
+
return (`[⚠ ARM YOUR INBOX WATCHER NOW. Peers are live in your mesh trying to coordinate `
|
|
24
|
+
+ `with this session, and the operator can DM you — but they reach you ONLY while armed. `
|
|
25
|
+
+ `Unarmed, you're invisible and their messages silently pile up. The watcher can DROP `
|
|
26
|
+
+ `ANYTIME (crash / reload / OOM) — every turn you're flagged unarmed you MUST re-arm, `
|
|
27
|
+
+ `or coordination breaks. `
|
|
28
|
+
+ `ARM: Monitor is a deferred tool — first load its schema (ToolSearch query `
|
|
29
|
+
+ `"select:Monitor"), then call Monitor with persistent:true, timeout_ms:3600000, `
|
|
30
|
+
+ `description:"greprag inbox ${short}", and command: `
|
|
31
|
+
+ `\`${(0, session_id_1.armMonitorCommand)(short, ownerPid, assistant)}\`. `
|
|
32
|
+
+ `Then drain what's waiting: \`greprag inbox\`. `
|
|
33
|
+
+ `Reply to you: ${handle}@greprag.com/${short}.]`);
|
|
34
|
+
}
|
|
35
|
+
/** Detect the watcher deficiency. armed → silent (the auto-stop). Unarmed → nag when a
|
|
36
|
+
* peer has actually messaged this session (grounded + TRUE — nag only when
|
|
37
|
+
* sessionUnread>0, so it never decays into a cried-wolf banner), else nudge. */
|
|
38
|
+
function armDetect(env) {
|
|
39
|
+
if (env.armed)
|
|
40
|
+
return { tier: 'silent' };
|
|
41
|
+
const unread = env.sessionUnread > 0 ? env.sessionUnread : 0;
|
|
42
|
+
return unread > 0
|
|
43
|
+
? { tier: 'nag', detail: { unread } }
|
|
44
|
+
: { tier: 'nudge', detail: { unread: 0 } };
|
|
45
|
+
}
|
|
46
|
+
/** Per-turn thin line. nag = a real peer message waits (true by construction); nudge =
|
|
47
|
+
* unarmed with no mail. The heavy how-to-arm lives in the SessionStart announce. */
|
|
48
|
+
function buildArmReminder(d) {
|
|
49
|
+
if (d.tier === 'silent')
|
|
50
|
+
return null;
|
|
51
|
+
const unread = Number((d.detail && d.detail.unread) || 0);
|
|
52
|
+
if (unread > 0) {
|
|
53
|
+
return `⚠ A peer messaged you (${unread} waiting) & you're UNARMED — arm + answer NOW: greprag inbox.`;
|
|
54
|
+
}
|
|
55
|
+
return `⚠ Watcher down — peers can't reach you. RE-ARM now.`;
|
|
56
|
+
}
|
|
57
|
+
/** The reference module. detect = unarmed; announce = the urgent how-to; reminder = the
|
|
58
|
+
* thin line. Wired into the registry container (reminder-registry.ts). */
|
|
59
|
+
exports.watcherArmModule = {
|
|
60
|
+
id: 'watcher-arm',
|
|
61
|
+
detect: armDetect,
|
|
62
|
+
announce: (env) => buildArmAnnounce(env.short, env.ownerPid, env.alias ?? null, env.assistant ?? false),
|
|
63
|
+
reminder: (d) => buildArmReminder(d),
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=arm-reminder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arm-reminder.js","sourceRoot":"","sources":["../../src/commands/arm-reminder.ts"],"names":[],"mappings":";AAAA;;;;;uCAKuC;;;AAYvC,4CAiBC;AAKD,8BAMC;AAID,4CAOC;AAhDD,8CAAkD;AAElD;;;;;;8FAM8F;AAC9F,SAAgB,gBAAgB,CAC9B,KAAa,EAAE,QAAwB,EAAE,QAAuB,IAAI,EAAE,SAAS,GAAG,KAAK;IAEvF,MAAM,MAAM,GAAG,KAAK,IAAI,UAAU,CAAC;IACnC,OAAO,CACL,kFAAkF;UAChF,wFAAwF;UACxF,sFAAsF;UACtF,sFAAsF;UACtF,0BAA0B;UAC1B,4EAA4E;UAC5E,iFAAiF;UACjF,8BAA8B,KAAK,kBAAkB;UACrD,KAAK,IAAA,8BAAiB,EAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM;UACxD,gDAAgD;UAChD,iBAAiB,MAAM,gBAAgB,KAAK,IAAI,CACnD,CAAC;AACJ,CAAC;AAED;;iFAEiF;AACjF,SAAgB,SAAS,CAAC,GAAgB;IACxC,IAAI,GAAG,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,OAAO,MAAM,GAAG,CAAC;QACf,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE;QACrC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;AAC/C,CAAC;AAED;qFACqF;AACrF,SAAgB,gBAAgB,CAAC,CAAY;IAC3C,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,IAAK,CAAC,CAAC,MAA8B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,OAAO,0BAA0B,MAAM,+DAA+D,CAAC;IACzG,CAAC;IACD,OAAO,qDAAqD,CAAC;AAC/D,CAAC;AAED;2EAC2E;AAC9D,QAAA,gBAAgB,GAAmB;IAC9C,EAAE,EAAE,aAAa;IACjB,MAAM,EAAE,SAAS;IACjB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,IAAI,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,KAAK,CAAC;IACvG,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;CACrC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** assistant-doctrine — the flagged-assistant-project doctrine auto-load as a
|
|
2
|
+
* reminder-interrupt module (docs/reminder-interrupt.md §Registry spec). announce-ONLY
|
|
3
|
+
* + pass-through: the doctrine text is PRECOMPUTED by the hook (buildAssistantDoctrineContext
|
|
4
|
+
* reads doctrine files — I/O that belongs in the hook). Fires ONLY for the assistant
|
|
5
|
+
* project — env.assistantDoctrine is null everywhere else, so a normal project sees
|
|
6
|
+
* nothing. adr: adr/assistant-role.md */
|
|
7
|
+
import { ReminderEnv, Detection, ReminderModule } from './reminder-types';
|
|
8
|
+
export declare function assistantDetect(env: ReminderEnv): Detection;
|
|
9
|
+
export declare const assistantDoctrineModule: ReminderModule;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** assistant-doctrine — the flagged-assistant-project doctrine auto-load as a
|
|
3
|
+
* reminder-interrupt module (docs/reminder-interrupt.md §Registry spec). announce-ONLY
|
|
4
|
+
* + pass-through: the doctrine text is PRECOMPUTED by the hook (buildAssistantDoctrineContext
|
|
5
|
+
* reads doctrine files — I/O that belongs in the hook). Fires ONLY for the assistant
|
|
6
|
+
* project — env.assistantDoctrine is null everywhere else, so a normal project sees
|
|
7
|
+
* nothing. adr: adr/assistant-role.md */
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.assistantDoctrineModule = void 0;
|
|
10
|
+
exports.assistantDetect = assistantDetect;
|
|
11
|
+
function assistantDetect(env) {
|
|
12
|
+
return env.assistantDoctrine ? { tier: 'ambient' } : { tier: 'silent' };
|
|
13
|
+
}
|
|
14
|
+
exports.assistantDoctrineModule = {
|
|
15
|
+
id: 'assistant-doctrine',
|
|
16
|
+
detect: assistantDetect,
|
|
17
|
+
announce: (env) => env.assistantDoctrine ?? null,
|
|
18
|
+
reminder: () => null,
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=assistant-reminder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-reminder.js","sourceRoot":"","sources":["../../src/commands/assistant-reminder.ts"],"names":[],"mappings":";AAAA;;;;;0CAK0C;;;AAI1C,0CAEC;AAFD,SAAgB,eAAe,CAAC,GAAgB;IAC9C,OAAO,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC1E,CAAC;AAEY,QAAA,uBAAuB,GAAmB;IACrD,EAAE,EAAE,oBAAoB;IACxB,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI;IAChD,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;CACrB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** checkpoint — the open-checkpoints SessionStart landmark as a reminder-interrupt
|
|
2
|
+
* module (docs/reminder-interrupt.md §Registry spec). announce-ONLY: checkpoints are
|
|
3
|
+
* operator-triggered landmarks surfaced once at session start, never per-turn traffic
|
|
4
|
+
* (reminder → null). `detect` gates on the hook-supplied list so the announce auto-
|
|
5
|
+
* silences when nothing is open. The PURE render (list → hint block) lives here — the
|
|
6
|
+
* hook owns only the fetch (I/O), keeping the module unit-testable. */
|
|
7
|
+
import { ReminderEnv, Detection, ReminderModule, OpenCheckpointSlim } from './reminder-types';
|
|
8
|
+
/** Format the open-checkpoints hint block: 3-most-recent rows + a single overflow line
|
|
9
|
+
* when there are more. Returns null on zero (the announce is then skipped). */
|
|
10
|
+
export declare function renderCheckpointHint(open: OpenCheckpointSlim[], projectName: string): string | null;
|
|
11
|
+
/** Open checkpoints present → ambient announce; none (or per-turn, where the list is
|
|
12
|
+
* absent) → silent. */
|
|
13
|
+
export declare function checkpointDetect(env: ReminderEnv): Detection;
|
|
14
|
+
export declare const checkpointModule: ReminderModule;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** checkpoint — the open-checkpoints SessionStart landmark as a reminder-interrupt
|
|
3
|
+
* module (docs/reminder-interrupt.md §Registry spec). announce-ONLY: checkpoints are
|
|
4
|
+
* operator-triggered landmarks surfaced once at session start, never per-turn traffic
|
|
5
|
+
* (reminder → null). `detect` gates on the hook-supplied list so the announce auto-
|
|
6
|
+
* silences when nothing is open. The PURE render (list → hint block) lives here — the
|
|
7
|
+
* hook owns only the fetch (I/O), keeping the module unit-testable. */
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.checkpointModule = void 0;
|
|
10
|
+
exports.renderCheckpointHint = renderCheckpointHint;
|
|
11
|
+
exports.checkpointDetect = checkpointDetect;
|
|
12
|
+
/** Render a YYYY-MM-DD date in UTC for the session-start hint. */
|
|
13
|
+
function fmtCheckpointDate(iso) {
|
|
14
|
+
return iso.slice(0, 10);
|
|
15
|
+
}
|
|
16
|
+
/** Format the open-checkpoints hint block: 3-most-recent rows + a single overflow line
|
|
17
|
+
* when there are more. Returns null on zero (the announce is then skipped). */
|
|
18
|
+
function renderCheckpointHint(open, projectName) {
|
|
19
|
+
if (open.length === 0)
|
|
20
|
+
return null;
|
|
21
|
+
// Server returns DESC by created_at; take the 3 most recent.
|
|
22
|
+
const top = open.slice(0, 3);
|
|
23
|
+
const overflow = open.length - top.length;
|
|
24
|
+
const lines = [];
|
|
25
|
+
lines.push(`[${open.length} checkpoint${open.length === 1 ? '' : 's'} open in ${projectName}:`);
|
|
26
|
+
for (const cp of top) {
|
|
27
|
+
lines.push(` · ${cp.nodeId} ${cp.title} (since ${fmtCheckpointDate(cp.createdAt)})`);
|
|
28
|
+
}
|
|
29
|
+
if (overflow > 0) {
|
|
30
|
+
lines.push(` · ${overflow} more — greprag checkpoint list]`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
lines.push(` View one: greprag checkpoint show <id>]`);
|
|
34
|
+
}
|
|
35
|
+
return lines.join('\n');
|
|
36
|
+
}
|
|
37
|
+
/** Open checkpoints present → ambient announce; none (or per-turn, where the list is
|
|
38
|
+
* absent) → silent. */
|
|
39
|
+
function checkpointDetect(env) {
|
|
40
|
+
return (env.openCheckpoints && env.openCheckpoints.length > 0)
|
|
41
|
+
? { tier: 'ambient' }
|
|
42
|
+
: { tier: 'silent' };
|
|
43
|
+
}
|
|
44
|
+
exports.checkpointModule = {
|
|
45
|
+
id: 'checkpoint',
|
|
46
|
+
detect: checkpointDetect,
|
|
47
|
+
announce: (env) => renderCheckpointHint(env.openCheckpoints ?? [], env.projectName ?? ''),
|
|
48
|
+
reminder: () => null,
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=checkpoint-reminder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-reminder.js","sourceRoot":"","sources":["../../src/commands/checkpoint-reminder.ts"],"names":[],"mappings":";AAAA;;;;;wEAKwE;;;AAWxE,oDAgBC;AAID,4CAIC;AA/BD,kEAAkE;AAClE,SAAS,iBAAiB,CAAC,GAAW;IACpC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED;gFACgF;AAChF,SAAgB,oBAAoB,CAAC,IAA0B,EAAE,WAAmB;IAClF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,6DAA6D;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,cAAc,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,WAAW,GAAG,CAAC,CAAC;IAChG,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,KAAK,WAAW,iBAAiB,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,OAAO,QAAQ,kCAAkC,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;wBACwB;AACxB,SAAgB,gBAAgB,CAAC,GAAgB;IAC/C,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;QAC5D,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;QACrB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzB,CAAC;AAEY,QAAA,gBAAgB,GAAmB;IAC9C,EAAE,EAAE,YAAY;IAChB,MAAM,EAAE,gBAAgB;IACxB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IACzF,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;CACrB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** friction-reminder — the active Mechanic as a reminder-interrupt registry module
|
|
2
|
+
* (docs/reminder-interrupt.md §Registry spec). The GRADED sibling of the watcher-arm
|
|
3
|
+
* reference module: its detector is stress-gated (silent at calm, escalating with the
|
|
4
|
+
* live stress level) rather than a binary resolvable deficiency.
|
|
5
|
+
*
|
|
6
|
+
* A reminder-interrupt MODULE is `{ id, detect, announce, reminder }` (reminder-types.ts):
|
|
7
|
+
* `detect(env)` reads the live state → a tier (`silent` = nothing to fix); a SessionStart
|
|
8
|
+
* `announce` loads the schema ONCE; a per-turn `reminder` fires a thin, grounded pointer
|
|
9
|
+
* back at it. The no-orphan invariant requires the PAIR. The container (reminder-registry.ts)
|
|
10
|
+
* wires every module; the hook (hook.ts) assembles the env and emits — see `injectReminders`.
|
|
11
|
+
*
|
|
12
|
+
* PURE: imports only the stress constants; returns strings + a tier + a tally. No fs, no
|
|
13
|
+
* network, no clock — the hook does ALL I/O. Keeping detect/tier a pure function is what
|
|
14
|
+
* makes the detect→silent transition (the auto-stop) unit-testable (the regression-lock). */
|
|
15
|
+
import { Tier, ReminderModule } from './reminder-types';
|
|
16
|
+
/** The firing-tier ladder (docs/reminder-interrupt.md §Firing tiers). `silent`
|
|
17
|
+
* is the off rung; the other three escalate wording intensity with the cost of
|
|
18
|
+
* inaction: ambient = soft pointer, nudge = imperative + reason, nag = LOUD
|
|
19
|
+
* forced-decision. */
|
|
20
|
+
export type FrictionTier = Tier;
|
|
21
|
+
/** Ambient (calm-state) cadence: one soft pointer every N turns. The low-rate
|
|
22
|
+
* trickle for a capability with no live bad-state to gate on. */
|
|
23
|
+
export declare const AMBIENT_EVERY_N = 5;
|
|
24
|
+
/** Decide the friction reminder's firing tier from the live state bag — PURE so
|
|
25
|
+
* the ladder is unit-testable (the regression-lock rung). DYNAMIC: urgency tracks
|
|
26
|
+
* the live stress level (docs/stress-trigger.md), not a hand-set knob.
|
|
27
|
+
*
|
|
28
|
+
* stress HIGH (2) → nag — LOUD forced-decision; fires while the live bad
|
|
29
|
+
* state holds and goes silent the instant it flips.
|
|
30
|
+
* stress ELEVATED (1) → nudge — imperative + reason, while elevated.
|
|
31
|
+
* stress CALM (0) → ambient every Nth turn (turnCount % AMBIENT_EVERY_N),
|
|
32
|
+
* else silent.
|
|
33
|
+
*
|
|
34
|
+
* Defensive: non-finite inputs collapse to 0 (calm / turn 0 → silent), so a
|
|
35
|
+
* missing or corrupt state bag can never escalate — fail-quiet by construction. */
|
|
36
|
+
export declare function frictionReminderTier(turnCount: number, stress: number): FrictionTier;
|
|
37
|
+
/** SessionStart announce — the mechanic-launch schema, loaded ONCE. The heavy
|
|
38
|
+
* half of the pair: it (1) TEACHES the method the per-turn reminder triggers, so
|
|
39
|
+
* the reminder stays a thin pointer, and (2) PRIMES adherence — the agent acts on
|
|
40
|
+
* a reminder for a capability it was *taught*, not a cold mid-session imperative
|
|
41
|
+
* (the no-orphan invariant, docs/reminder-interrupt.md). Written for an AGENT under
|
|
42
|
+
* token pressure per the doc's authoring rules: action-first, reason+method
|
|
43
|
+
* bundled, directive-framed, token-minimal. ~150 tokens; amortized over the
|
|
44
|
+
* session. */
|
|
45
|
+
export declare function buildMechanicAnnounce(): string;
|
|
46
|
+
/** Per-turn reminder line for a given tier — the thin pointer back at the announce
|
|
47
|
+
* schema; null on `silent`. Wording intensity scales with the tier (the doc):
|
|
48
|
+
* ambient = soft pointer, nudge = imperative + reason, nag = LOUD forced-decision.
|
|
49
|
+
* Every non-silent line is action-first, bundles reason+method, and ends on a
|
|
50
|
+
* forced binary (act → spawn / clear → continue) so it CONVERTS instead of becoming
|
|
51
|
+
* wallpaper (the fix-`16a29a8b` banner-blindness failure mode). Grounded to the
|
|
52
|
+
* live signal where there is one (nudge/nag name the stress). The act-now directive
|
|
53
|
+
* envelope (authoring rule #4) is applied by the hook, not here — this module owns
|
|
54
|
+
* the wording, the caller owns the framing. */
|
|
55
|
+
export declare function buildFrictionReminder(tier: FrictionTier): string | null;
|
|
56
|
+
/** Persisted fire-counter (docs/reminder-interrupt.md §Measurement). The
|
|
57
|
+
* DENOMINATOR of the adherence ratio: how many friction reminders were shown, by
|
|
58
|
+
* tier. The numerator (reflex chips actually spawned after a reminder) is the next
|
|
59
|
+
* measurement increment — see `reflexSpawns` (stamped by the spawn path when it
|
|
60
|
+
* lands). Read the file to compute the fire rate: `reflexSpawns / total`. */
|
|
61
|
+
export interface FrictionStats {
|
|
62
|
+
/** Total non-silent reminders shown. */
|
|
63
|
+
total: number;
|
|
64
|
+
/** Per-tier breakdown of `total`. */
|
|
65
|
+
byTier: {
|
|
66
|
+
ambient: number;
|
|
67
|
+
nudge: number;
|
|
68
|
+
nag: number;
|
|
69
|
+
};
|
|
70
|
+
/** Reflex chips observed spawned after a reminder (the adherence numerator).
|
|
71
|
+
* Bumped by the spawn path; reminders never touch it. */
|
|
72
|
+
reflexSpawns: number;
|
|
73
|
+
firstFiredAt?: string;
|
|
74
|
+
lastFiredAt?: string;
|
|
75
|
+
/** turnCount at the most recent fire — lets the reader compute a per-N-turn rate. */
|
|
76
|
+
lastTurnCount?: number;
|
|
77
|
+
}
|
|
78
|
+
/** Tally one reminder fire — PURE (the hook supplies the clock via `nowIso`, so
|
|
79
|
+
* this stays testable and resume-safe). A `silent` tier is NOT a fire: it returns
|
|
80
|
+
* the prior stats unchanged, so the denominator only ever counts shown reminders. */
|
|
81
|
+
export declare function tallyFrictionFire(prior: FrictionStats | null, tier: FrictionTier, turnCount: number, nowIso: string): FrictionStats;
|
|
82
|
+
/** Record an observed reflex-chip spawn — PURE; the adherence numerator. The spawn
|
|
83
|
+
* path calls this when it sees a chip preloaded from the reflex-chip template land
|
|
84
|
+
* after a reminder. Never decrements; idempotency is the caller's concern. */
|
|
85
|
+
export declare function tallyReflexSpawn(prior: FrictionStats | null): FrictionStats;
|
|
86
|
+
/** The active Mechanic, recast onto the detector model (docs/reminder-interrupt.md
|
|
87
|
+
* §Registry spec). `detect` is stress-gated — the friction signal IS the live
|
|
88
|
+
* deficiency; `announce` teaches the reflex-chip schema once; `reminder` is the thin
|
|
89
|
+
* per-turn line. Wired through the container (reminder-registry.ts). Unlike the
|
|
90
|
+
* watcher-arm reference module (binary, auto-silences when armed), this one is
|
|
91
|
+
* GRADED — silent at calm, escalating with the live stress level. */
|
|
92
|
+
export declare const mechanicFrictionModule: ReminderModule;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** friction-reminder — the active Mechanic as a reminder-interrupt registry module
|
|
3
|
+
* (docs/reminder-interrupt.md §Registry spec). The GRADED sibling of the watcher-arm
|
|
4
|
+
* reference module: its detector is stress-gated (silent at calm, escalating with the
|
|
5
|
+
* live stress level) rather than a binary resolvable deficiency.
|
|
6
|
+
*
|
|
7
|
+
* A reminder-interrupt MODULE is `{ id, detect, announce, reminder }` (reminder-types.ts):
|
|
8
|
+
* `detect(env)` reads the live state → a tier (`silent` = nothing to fix); a SessionStart
|
|
9
|
+
* `announce` loads the schema ONCE; a per-turn `reminder` fires a thin, grounded pointer
|
|
10
|
+
* back at it. The no-orphan invariant requires the PAIR. The container (reminder-registry.ts)
|
|
11
|
+
* wires every module; the hook (hook.ts) assembles the env and emits — see `injectReminders`.
|
|
12
|
+
*
|
|
13
|
+
* PURE: imports only the stress constants; returns strings + a tier + a tally. No fs, no
|
|
14
|
+
* network, no clock — the hook does ALL I/O. Keeping detect/tier a pure function is what
|
|
15
|
+
* makes the detect→silent transition (the auto-stop) unit-testable (the regression-lock). */
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.mechanicFrictionModule = exports.AMBIENT_EVERY_N = void 0;
|
|
18
|
+
exports.frictionReminderTier = frictionReminderTier;
|
|
19
|
+
exports.buildMechanicAnnounce = buildMechanicAnnounce;
|
|
20
|
+
exports.buildFrictionReminder = buildFrictionReminder;
|
|
21
|
+
exports.tallyFrictionFire = tallyFrictionFire;
|
|
22
|
+
exports.tallyReflexSpawn = tallyReflexSpawn;
|
|
23
|
+
const state_trigger_1 = require("./state-trigger");
|
|
24
|
+
/** Ambient (calm-state) cadence: one soft pointer every N turns. The low-rate
|
|
25
|
+
* trickle for a capability with no live bad-state to gate on. */
|
|
26
|
+
exports.AMBIENT_EVERY_N = 5;
|
|
27
|
+
/** Decide the friction reminder's firing tier from the live state bag — PURE so
|
|
28
|
+
* the ladder is unit-testable (the regression-lock rung). DYNAMIC: urgency tracks
|
|
29
|
+
* the live stress level (docs/stress-trigger.md), not a hand-set knob.
|
|
30
|
+
*
|
|
31
|
+
* stress HIGH (2) → nag — LOUD forced-decision; fires while the live bad
|
|
32
|
+
* state holds and goes silent the instant it flips.
|
|
33
|
+
* stress ELEVATED (1) → nudge — imperative + reason, while elevated.
|
|
34
|
+
* stress CALM (0) → ambient every Nth turn (turnCount % AMBIENT_EVERY_N),
|
|
35
|
+
* else silent.
|
|
36
|
+
*
|
|
37
|
+
* Defensive: non-finite inputs collapse to 0 (calm / turn 0 → silent), so a
|
|
38
|
+
* missing or corrupt state bag can never escalate — fail-quiet by construction. */
|
|
39
|
+
function frictionReminderTier(turnCount, stress) {
|
|
40
|
+
const s = Number.isFinite(stress) ? stress : 0;
|
|
41
|
+
const t = Number.isFinite(turnCount) ? turnCount : 0;
|
|
42
|
+
if (s >= state_trigger_1.STRESS_HIGH)
|
|
43
|
+
return 'nag';
|
|
44
|
+
if (s >= state_trigger_1.STRESS_ELEVATED)
|
|
45
|
+
return 'nudge';
|
|
46
|
+
if (t >= 1 && t % exports.AMBIENT_EVERY_N === 0)
|
|
47
|
+
return 'ambient';
|
|
48
|
+
return 'silent';
|
|
49
|
+
}
|
|
50
|
+
// ---------- Authored text (announce + per-turn reminder) ---------------------
|
|
51
|
+
/** The HOW string — the method every tier points at. Kept in one place so the
|
|
52
|
+
* announce and the per-turn line can never drift on the spawn mechanic. */
|
|
53
|
+
const SPAWN_METHOD = 'spawn a fire-and-forget reflex chip (spawn_task → packages/cli/skill/templates/reflex-chip.md)';
|
|
54
|
+
/** SessionStart announce — the mechanic-launch schema, loaded ONCE. The heavy
|
|
55
|
+
* half of the pair: it (1) TEACHES the method the per-turn reminder triggers, so
|
|
56
|
+
* the reminder stays a thin pointer, and (2) PRIMES adherence — the agent acts on
|
|
57
|
+
* a reminder for a capability it was *taught*, not a cold mid-session imperative
|
|
58
|
+
* (the no-orphan invariant, docs/reminder-interrupt.md). Written for an AGENT under
|
|
59
|
+
* token pressure per the doc's authoring rules: action-first, reason+method
|
|
60
|
+
* bundled, directive-framed, token-minimal. ~150 tokens; amortized over the
|
|
61
|
+
* session. */
|
|
62
|
+
function buildMechanicAnnounce() {
|
|
63
|
+
return [
|
|
64
|
+
'[Active Mechanic — keep the harness healthy IN-SESSION. Friction is a signal to fix now, not to defer.]',
|
|
65
|
+
'WHEN you hit friction this session — repeated yourself, fought a tool, got corrected ≥2× on the same thing, or rediscovered something already known — fix the PATTERN now; do not push through.',
|
|
66
|
+
`HOW: ${SPAWN_METHOD}. The chip is preloaded with the mechanic tools + live \`greprag mechanic status\`, so it orients instantly, lands a Tier-0 born-shadow repair, and ends — no gate to wait at.`,
|
|
67
|
+
'MODES: fix (land the repair) · log (`greprag fix log "<obs>"` when it is a mechanism for /mechanic later) · ask-human (the operator must decide).',
|
|
68
|
+
'WHY now: immediate-fix beats the backlog — the moment that produced the friction still holds the context the fix needs; deferred, that context is gone.',
|
|
69
|
+
'A per-turn reminder fires LOUD when live friction is detected — act on it, or say "clear" and continue.',
|
|
70
|
+
].join('\n');
|
|
71
|
+
}
|
|
72
|
+
/** Per-turn reminder line for a given tier — the thin pointer back at the announce
|
|
73
|
+
* schema; null on `silent`. Wording intensity scales with the tier (the doc):
|
|
74
|
+
* ambient = soft pointer, nudge = imperative + reason, nag = LOUD forced-decision.
|
|
75
|
+
* Every non-silent line is action-first, bundles reason+method, and ends on a
|
|
76
|
+
* forced binary (act → spawn / clear → continue) so it CONVERTS instead of becoming
|
|
77
|
+
* wallpaper (the fix-`16a29a8b` banner-blindness failure mode). Grounded to the
|
|
78
|
+
* live signal where there is one (nudge/nag name the stress). The act-now directive
|
|
79
|
+
* envelope (authoring rule #4) is applied by the hook, not here — this module owns
|
|
80
|
+
* the wording, the caller owns the framing. */
|
|
81
|
+
function buildFrictionReminder(tier) {
|
|
82
|
+
switch (tier) {
|
|
83
|
+
case 'nag':
|
|
84
|
+
return `⚠ FRICTION HIGH (live signal: repetition / errors / churn) — STOP pushing through; fix the PATTERN now → ${SPAWN_METHOD}. No friction? say "clear" + continue.`;
|
|
85
|
+
case 'nudge':
|
|
86
|
+
return `⚠ Friction rising — if you have repeated yourself or fought a tool, fix it now → ${SPAWN_METHOD}. Otherwise say "clear" + continue.`;
|
|
87
|
+
case 'ambient':
|
|
88
|
+
return `💡 Hit friction since the last check (repeated yourself, fought a tool, corrected ≥2×)? → ${SPAWN_METHOD}. If not, say "clear" + continue.`;
|
|
89
|
+
default:
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Normalize any prior-stats blob (possibly missing/partial/corrupt) into a fully
|
|
94
|
+
* populated FrictionStats. Pure, total. */
|
|
95
|
+
function normalizeStats(prior) {
|
|
96
|
+
const p = (prior && typeof prior === 'object') ? prior : {};
|
|
97
|
+
const bt = (p.byTier && typeof p.byTier === 'object') ? p.byTier : {};
|
|
98
|
+
return {
|
|
99
|
+
total: Number.isFinite(p.total) ? p.total : 0,
|
|
100
|
+
byTier: {
|
|
101
|
+
ambient: Number.isFinite(bt.ambient) ? bt.ambient : 0,
|
|
102
|
+
nudge: Number.isFinite(bt.nudge) ? bt.nudge : 0,
|
|
103
|
+
nag: Number.isFinite(bt.nag) ? bt.nag : 0,
|
|
104
|
+
},
|
|
105
|
+
reflexSpawns: Number.isFinite(p.reflexSpawns) ? p.reflexSpawns : 0,
|
|
106
|
+
firstFiredAt: p.firstFiredAt,
|
|
107
|
+
lastFiredAt: p.lastFiredAt,
|
|
108
|
+
lastTurnCount: p.lastTurnCount,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Tally one reminder fire — PURE (the hook supplies the clock via `nowIso`, so
|
|
112
|
+
* this stays testable and resume-safe). A `silent` tier is NOT a fire: it returns
|
|
113
|
+
* the prior stats unchanged, so the denominator only ever counts shown reminders. */
|
|
114
|
+
function tallyFrictionFire(prior, tier, turnCount, nowIso) {
|
|
115
|
+
const s = normalizeStats(prior);
|
|
116
|
+
if (tier === 'silent')
|
|
117
|
+
return s;
|
|
118
|
+
s.total += 1;
|
|
119
|
+
s.byTier[tier] += 1;
|
|
120
|
+
s.firstFiredAt = s.firstFiredAt || nowIso;
|
|
121
|
+
s.lastFiredAt = nowIso;
|
|
122
|
+
if (Number.isFinite(turnCount))
|
|
123
|
+
s.lastTurnCount = turnCount;
|
|
124
|
+
return s;
|
|
125
|
+
}
|
|
126
|
+
/** Record an observed reflex-chip spawn — PURE; the adherence numerator. The spawn
|
|
127
|
+
* path calls this when it sees a chip preloaded from the reflex-chip template land
|
|
128
|
+
* after a reminder. Never decrements; idempotency is the caller's concern. */
|
|
129
|
+
function tallyReflexSpawn(prior) {
|
|
130
|
+
const s = normalizeStats(prior);
|
|
131
|
+
s.reflexSpawns += 1;
|
|
132
|
+
return s;
|
|
133
|
+
}
|
|
134
|
+
// ---------- Registry module --------------------------------------------------
|
|
135
|
+
/** The active Mechanic, recast onto the detector model (docs/reminder-interrupt.md
|
|
136
|
+
* §Registry spec). `detect` is stress-gated — the friction signal IS the live
|
|
137
|
+
* deficiency; `announce` teaches the reflex-chip schema once; `reminder` is the thin
|
|
138
|
+
* per-turn line. Wired through the container (reminder-registry.ts). Unlike the
|
|
139
|
+
* watcher-arm reference module (binary, auto-silences when armed), this one is
|
|
140
|
+
* GRADED — silent at calm, escalating with the live stress level. */
|
|
141
|
+
exports.mechanicFrictionModule = {
|
|
142
|
+
id: 'mechanic-friction',
|
|
143
|
+
detect: (env) => ({ tier: frictionReminderTier(env.turnCount, env.stress) }),
|
|
144
|
+
announce: () => buildMechanicAnnounce(),
|
|
145
|
+
reminder: (d) => buildFrictionReminder(d.tier),
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=friction-reminder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"friction-reminder.js","sourceRoot":"","sources":["../../src/commands/friction-reminder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** front-desk — the shared tenant front-desk mailbox as a reminder-interrupt module
|
|
2
|
+
* (docs/reminder-interrupt.md §Registry spec). The module that uses BOTH halves of the
|
|
3
|
+
* pair: a SessionStart `announce` (discreet count of cold opens + inbound email waiting
|
|
4
|
+
* at the shared desk) AND a per-turn `reminder` (envelope-only "you've got mail from X"
|
|
5
|
+
* notice). This is the "announce + reminder often utilize each other" case the registry
|
|
6
|
+
* was consolidated for — one module, one mailbox, two surfaces.
|
|
7
|
+
*
|
|
8
|
+
* PURE: the hook owns the count fetch (frontDeskUnread) AND the notice's network + per-
|
|
9
|
+
* machine dedup (frontDeskNotice), passing both via env; the module only formats the
|
|
10
|
+
* count and routes the precomputed notice.
|
|
11
|
+
*
|
|
12
|
+
* Privacy — front-desk is the SHARED tenant mailbox (cold opens + inbound email addressed
|
|
13
|
+
* to nobody's session), so its count leaks nothing private. This is NOT the v5.6.1
|
|
14
|
+
* project-wide eavesdrop vector that betrayed a sibling session's session-targeted DMs.
|
|
15
|
+
* adr: adr/address-grammar.md (2026-06-10), docs/front-desk-switchboard.md §5 */
|
|
16
|
+
import { ReminderEnv, Detection, ReminderModule } from './reminder-types';
|
|
17
|
+
/** SessionStart count hint — the discreet door (count + how to read), never content.
|
|
18
|
+
* Null at zero so the announce is skipped. */
|
|
19
|
+
export declare function buildFrontDeskAnnounce(unread: number): string | null;
|
|
20
|
+
/** Either surface has something → ambient (soft, never a forced-decision directive); else
|
|
21
|
+
* silent. SessionStart sets frontDeskUnread; per-turn sets frontDeskNotice. */
|
|
22
|
+
export declare function frontDeskDetect(env: ReminderEnv): Detection;
|
|
23
|
+
export declare const frontDeskModule: ReminderModule;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** front-desk — the shared tenant front-desk mailbox as a reminder-interrupt module
|
|
3
|
+
* (docs/reminder-interrupt.md §Registry spec). The module that uses BOTH halves of the
|
|
4
|
+
* pair: a SessionStart `announce` (discreet count of cold opens + inbound email waiting
|
|
5
|
+
* at the shared desk) AND a per-turn `reminder` (envelope-only "you've got mail from X"
|
|
6
|
+
* notice). This is the "announce + reminder often utilize each other" case the registry
|
|
7
|
+
* was consolidated for — one module, one mailbox, two surfaces.
|
|
8
|
+
*
|
|
9
|
+
* PURE: the hook owns the count fetch (frontDeskUnread) AND the notice's network + per-
|
|
10
|
+
* machine dedup (frontDeskNotice), passing both via env; the module only formats the
|
|
11
|
+
* count and routes the precomputed notice.
|
|
12
|
+
*
|
|
13
|
+
* Privacy — front-desk is the SHARED tenant mailbox (cold opens + inbound email addressed
|
|
14
|
+
* to nobody's session), so its count leaks nothing private. This is NOT the v5.6.1
|
|
15
|
+
* project-wide eavesdrop vector that betrayed a sibling session's session-targeted DMs.
|
|
16
|
+
* adr: adr/address-grammar.md (2026-06-10), docs/front-desk-switchboard.md §5 */
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.frontDeskModule = void 0;
|
|
19
|
+
exports.buildFrontDeskAnnounce = buildFrontDeskAnnounce;
|
|
20
|
+
exports.frontDeskDetect = frontDeskDetect;
|
|
21
|
+
/** SessionStart count hint — the discreet door (count + how to read), never content.
|
|
22
|
+
* Null at zero so the announce is skipped. */
|
|
23
|
+
function buildFrontDeskAnnounce(unread) {
|
|
24
|
+
if (!unread || unread <= 0)
|
|
25
|
+
return null;
|
|
26
|
+
return `[📬 ${unread} message${unread === 1 ? '' : 's'} at the front desk — \`greprag inbox --front-desk\` to read]`;
|
|
27
|
+
}
|
|
28
|
+
/** Either surface has something → ambient (soft, never a forced-decision directive); else
|
|
29
|
+
* silent. SessionStart sets frontDeskUnread; per-turn sets frontDeskNotice. */
|
|
30
|
+
function frontDeskDetect(env) {
|
|
31
|
+
const unread = env.frontDeskUnread ?? 0;
|
|
32
|
+
return (unread > 0 || env.frontDeskNotice) ? { tier: 'ambient' } : { tier: 'silent' };
|
|
33
|
+
}
|
|
34
|
+
exports.frontDeskModule = {
|
|
35
|
+
id: 'front-desk',
|
|
36
|
+
detect: frontDeskDetect,
|
|
37
|
+
announce: (env) => buildFrontDeskAnnounce(env.frontDeskUnread ?? 0),
|
|
38
|
+
reminder: (_d, env) => env.frontDeskNotice ?? null,
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=frontdesk-reminder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontdesk-reminder.js","sourceRoot":"","sources":["../../src/commands/frontdesk-reminder.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;kFAckF;;;AAMlF,wDAGC;AAID,0CAGC;AAZD;+CAC+C;AAC/C,SAAgB,sBAAsB,CAAC,MAAc;IACnD,IAAI,CAAC,MAAM,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,OAAO,MAAM,WAAW,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,8DAA8D,CAAC;AACvH,CAAC;AAED;gFACgF;AAChF,SAAgB,eAAe,CAAC,GAAgB;IAC9C,MAAM,MAAM,GAAG,GAAG,CAAC,eAAe,IAAI,CAAC,CAAC;IACxC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACxF,CAAC;AAEY,QAAA,eAAe,GAAmB;IAC7C,EAAE,EAAE,YAAY;IAChB,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,CAAC;IACnE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI;CACnD,CAAC"}
|
|
@@ -35,7 +35,32 @@ export declare const FATAL_EXIT_CODE = 64;
|
|
|
35
35
|
* so it respawns; SIGINT/SIGTERM skip these and return via the
|
|
36
36
|
* normal abort path. */
|
|
37
37
|
export declare function installLastResortHandlers(): void;
|
|
38
|
+
type ChildExit = {
|
|
39
|
+
code: number | null;
|
|
40
|
+
signal: NodeJS.Signals | null;
|
|
41
|
+
err: Error | null;
|
|
42
|
+
};
|
|
43
|
+
/** Human-readable cause for a child death + whether it was an OS-level native
|
|
44
|
+
* crash. Turns "exit 3221226091" into "native crash 0xC000026B (DLL init failed —
|
|
45
|
+
* out of commit memory / window station gone)" so both the respawn log and the
|
|
46
|
+
* stand-down diagnostic are actionable instead of opaque digits. */
|
|
47
|
+
export declare function describeChildExit(exit: ChildExit): {
|
|
48
|
+
reason: string;
|
|
49
|
+
native: boolean;
|
|
50
|
+
};
|
|
51
|
+
/** Pure crash-loop decision (testable, mirrors capSurplusVerdict/gcVerdict).
|
|
52
|
+
* Given how long the just-exited child lived (`liveMs`) and the running
|
|
53
|
+
* fast-death `streak`, return the updated streak and whether the supervisor
|
|
54
|
+
* should STAND DOWN instead of respawning. A child that lived at least
|
|
55
|
+
* `lifetimeMs` did real work → streak resets to 0. Otherwise the streak grows
|
|
56
|
+
* and trips once it reaches `maxDeaths`. This is the whole breaker rule — no
|
|
57
|
+
* process state, just a count + a lifetime floor. */
|
|
58
|
+
export declare function crashLoopVerdict(liveMs: number, streak: number, lifetimeMs: number, maxDeaths: number): {
|
|
59
|
+
streak: number;
|
|
60
|
+
standDown: boolean;
|
|
61
|
+
};
|
|
38
62
|
/** Parent supervisor. Spawns the watch loop as a Node child, forwards
|
|
39
63
|
* stdio so Monitor sees each printed line as a notification verbatim,
|
|
40
64
|
* and respawns on any non-clean exit. */
|
|
41
65
|
export declare function runSupervisor(): Promise<void>;
|
|
66
|
+
export {};
|