greprag 5.46.0 → 5.47.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/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/init.js +20 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/reminder-registry.d.ts +20 -0
- package/dist/commands/reminder-registry.js +59 -0
- package/dist/commands/reminder-registry.js.map +1 -0
- package/dist/commands/reminder-types.d.ts +49 -0
- package/dist/commands/reminder-types.js +8 -0
- package/dist/commands/reminder-types.js.map +1 -0
- package/dist/hook.js +159 -27
- package/dist/hook.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,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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"}
|
package/dist/commands/init.js
CHANGED
|
@@ -1339,6 +1339,26 @@ function applySettings(settings, apiKey) {
|
|
|
1339
1339
|
else {
|
|
1340
1340
|
changes.push('UserPromptSubmit mail hook already configured (skipped)');
|
|
1341
1341
|
}
|
|
1342
|
+
// UserPromptSubmit (friction-reminder) — the active Mechanic's dynamic friction
|
|
1343
|
+
// reminder, BESIDE the arm directive. Each turn it reads the Stop-stashed state
|
|
1344
|
+
// bag (stress + turnCount) and, on a stress-driven tier (silent → ambient →
|
|
1345
|
+
// nudge → nag), injects a one-line forced-decision pointer to spawn the mechanic
|
|
1346
|
+
// reflex chip. Paired with the SessionStart announce (recap) — the no-orphan
|
|
1347
|
+
// invariant. Additive + fail-open (own hook, never breaks the arm/mail chain);
|
|
1348
|
+
// self-silences at rest and under `mechanic off`. docs/reminder-interrupt.md
|
|
1349
|
+
const frictionReminderHook = {
|
|
1350
|
+
matcher: '',
|
|
1351
|
+
hooks: [{ type: 'command', command: 'greprag-hook friction-reminder', timeout: 3000 }],
|
|
1352
|
+
};
|
|
1353
|
+
if (!hasGrepragHook(settings.hooks.UserPromptSubmit, 'friction-reminder')) {
|
|
1354
|
+
if (!settings.hooks.UserPromptSubmit)
|
|
1355
|
+
settings.hooks.UserPromptSubmit = [];
|
|
1356
|
+
settings.hooks.UserPromptSubmit.push(frictionReminderHook);
|
|
1357
|
+
changes.push('Added UserPromptSubmit hook (active-Mechanic friction reminder)');
|
|
1358
|
+
}
|
|
1359
|
+
else {
|
|
1360
|
+
changes.push('UserPromptSubmit friction-reminder hook already configured (skipped)');
|
|
1361
|
+
}
|
|
1342
1362
|
// Context governor (Chip C) — two local-only hooks, no API calls.
|
|
1343
1363
|
// Stop probe: reads the transcript TAIL, records live context size to
|
|
1344
1364
|
// ~/.greprag/context-state/<8hex>.json — read it with `greprag context status`.
|