greprag 5.49.10 → 5.49.11

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 (48) hide show
  1. package/dist/commands/arm-reminder.d.ts +10 -3
  2. package/dist/commands/arm-reminder.js +18 -6
  3. package/dist/commands/arm-reminder.js.map +1 -1
  4. package/dist/commands/collision-check.d.ts +1 -2
  5. package/dist/commands/collision-check.js +1 -2
  6. package/dist/commands/collision-check.js.map +1 -1
  7. package/dist/commands/inbox-drain.d.ts +9 -10
  8. package/dist/commands/inbox-drain.js +9 -10
  9. package/dist/commands/inbox-drain.js.map +1 -1
  10. package/dist/commands/inbox-primer-reminder.d.ts +10 -5
  11. package/dist/commands/inbox-primer-reminder.js +35 -21
  12. package/dist/commands/inbox-primer-reminder.js.map +1 -1
  13. package/dist/commands/poll-registry.d.ts +52 -9
  14. package/dist/commands/poll-registry.js +176 -9
  15. package/dist/commands/poll-registry.js.map +1 -1
  16. package/dist/commands/procedure.d.ts +15 -0
  17. package/dist/commands/procedure.js +167 -0
  18. package/dist/commands/procedure.js.map +1 -0
  19. package/dist/commands/reminder-registry.d.ts +5 -0
  20. package/dist/commands/reminder-registry.js +7 -3
  21. package/dist/commands/reminder-registry.js.map +1 -1
  22. package/dist/commands/reminder-types.d.ts +7 -5
  23. package/dist/hook.js +26 -46
  24. package/dist/hook.js.map +1 -1
  25. package/dist/index.js +4 -12
  26. package/dist/index.js.map +1 -1
  27. package/dist/procedure.d.ts +88 -0
  28. package/dist/procedure.js +269 -0
  29. package/dist/procedure.js.map +1 -0
  30. package/package.json +1 -1
  31. package/dist/commands/checkpoint-reminder.d.ts +0 -14
  32. package/dist/commands/checkpoint-reminder.js +0 -50
  33. package/dist/commands/checkpoint-reminder.js.map +0 -1
  34. package/dist/commands/frontdesk-reminder.d.ts +0 -23
  35. package/dist/commands/frontdesk-reminder.js +0 -43
  36. package/dist/commands/frontdesk-reminder.js.map +0 -1
  37. package/dist/commands/inbox-poll.d.ts +0 -47
  38. package/dist/commands/inbox-poll.js +0 -261
  39. package/dist/commands/inbox-poll.js.map +0 -1
  40. package/dist/commands/inbox-spool.d.ts +0 -27
  41. package/dist/commands/inbox-spool.js +0 -151
  42. package/dist/commands/inbox-spool.js.map +0 -1
  43. package/dist/commands/lore.d.ts +0 -35
  44. package/dist/commands/lore.js +0 -504
  45. package/dist/commands/lore.js.map +0 -1
  46. package/dist/front-desk-mail.d.ts +0 -50
  47. package/dist/front-desk-mail.js +0 -206
  48. package/dist/front-desk-mail.js.map +0 -1
@@ -1,50 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,23 +0,0 @@
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;
@@ -1,43 +0,0 @@
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
- // No SessionStart announce: the bare "N at the front desk" count was the wrong
38
- // schema (a number with no model). The inbox-primer teaches the front desk +
39
- // `greprag inbox --front-desk` instead. The per-turn envelope notice stays.
40
- announce: () => null,
41
- reminder: (_d, env) => env.frontDeskNotice ?? null,
42
- };
43
- //# sourceMappingURL=frontdesk-reminder.js.map
@@ -1 +0,0 @@
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,+EAA+E;IAC/E,6EAA6E;IAC7E,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;IACpB,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI;CACnD,CAAC"}
@@ -1,47 +0,0 @@
1
- /** greprag inbox poll — asyncRewake idle-liveness probe (the deep fix).
2
- *
3
- * THE GAP IT CLOSES: the Monitor watcher delivers + crash-respawns fine, but it
4
- * DIES on session reload / `/compact` and needs an AGENT TURN to re-arm — the
5
- * structural idle-death gap no version (incl. June 8) ever closed. asyncRewake
6
- * breaks that wall: a command hook with `async:true, asyncRewake:true` runs
7
- * DETACHED, SURVIVES TURN-END, and on `exit 2` the harness injects its stderr as
8
- * a system reminder — WAKING A FULLY IDLE SESSION with no turn. SessionStart
9
- * fires on resume/compact (the exact events that kill Monitor), so wiring this as
10
- * a SessionStart async+asyncRewake hook RE-ARMS the poll after every reload,
11
- * turn-free. adr: adr/monitor-resilience.md (2026-06-19 entry).
12
- *
13
- * HOW IT BEHAVES: a session-scoped SSE long-poll. It stays connected while idle
14
- * (that connected wait IS the idle-liveness). On the FIRST inbox message it
15
- * writes the message to STDERR and returns POLL_DELIVERED_EXIT (2) → the wake.
16
- * On a bounded max-lifetime ceiling, a 4xx, or abort it returns POLL_CLEAN_EXIT
17
- * (0) → no wake. A persisted cursor (`~/.greprag/polls/<short>.cursor`) makes
18
- * the next poll resume after the last seen id, so a message arriving between one
19
- * poll exiting and the next arming is REPLAYED, not lost.
20
- *
21
- * ALONGSIDE MONITOR, not instead of it: it registers in its OWN namespace
22
- * (`~/.greprag/polls/`, see poll-registry.ts) so it never gates Monitor arming.
23
- * When both are live a message may be delivered twice (Monitor event + poll
24
- * wake) — the accepted cost of a parallel safety net until a deliberate cutover.
25
- *
26
- * MUST be wired ASYNC. As a synchronous hook this would block the turn for the
27
- * whole ceiling. The arming hooks set `async:true, asyncRewake:true`. */
28
- /** Exit code that triggers the asyncRewake idle-session wake (stderr → reminder). */
29
- export declare const POLL_DELIVERED_EXIT = 2;
30
- /** Exit code for every non-wake terminal (ceiling / gate-skip / no key / 4xx). */
31
- export declare const POLL_CLEAN_EXIT = 0;
32
- export interface PollOptions {
33
- session: string;
34
- apiUrl: string;
35
- apiKey: string;
36
- maxMs?: number;
37
- gate?: boolean;
38
- json?: boolean;
39
- register?: boolean;
40
- signal?: AbortSignal;
41
- projectId?: string;
42
- }
43
- /** Run one idle-liveness poll. Returns the exit code the hook wrapper exits with:
44
- * POLL_DELIVERED_EXIT (2) on a message (→ asyncRewake wake) or POLL_CLEAN_EXIT
45
- * (0) on every other terminal. Returns (does NOT call process.exit) so it is
46
- * unit-testable; the `greprag-hook poll` wrapper exits with the returned code. */
47
- export declare function runInboxPoll(opts: PollOptions): Promise<number>;
@@ -1,261 +0,0 @@
1
- "use strict";
2
- /** greprag inbox poll — asyncRewake idle-liveness probe (the deep fix).
3
- *
4
- * THE GAP IT CLOSES: the Monitor watcher delivers + crash-respawns fine, but it
5
- * DIES on session reload / `/compact` and needs an AGENT TURN to re-arm — the
6
- * structural idle-death gap no version (incl. June 8) ever closed. asyncRewake
7
- * breaks that wall: a command hook with `async:true, asyncRewake:true` runs
8
- * DETACHED, SURVIVES TURN-END, and on `exit 2` the harness injects its stderr as
9
- * a system reminder — WAKING A FULLY IDLE SESSION with no turn. SessionStart
10
- * fires on resume/compact (the exact events that kill Monitor), so wiring this as
11
- * a SessionStart async+asyncRewake hook RE-ARMS the poll after every reload,
12
- * turn-free. adr: adr/monitor-resilience.md (2026-06-19 entry).
13
- *
14
- * HOW IT BEHAVES: a session-scoped SSE long-poll. It stays connected while idle
15
- * (that connected wait IS the idle-liveness). On the FIRST inbox message it
16
- * writes the message to STDERR and returns POLL_DELIVERED_EXIT (2) → the wake.
17
- * On a bounded max-lifetime ceiling, a 4xx, or abort it returns POLL_CLEAN_EXIT
18
- * (0) → no wake. A persisted cursor (`~/.greprag/polls/<short>.cursor`) makes
19
- * the next poll resume after the last seen id, so a message arriving between one
20
- * poll exiting and the next arming is REPLAYED, not lost.
21
- *
22
- * ALONGSIDE MONITOR, not instead of it: it registers in its OWN namespace
23
- * (`~/.greprag/polls/`, see poll-registry.ts) so it never gates Monitor arming.
24
- * When both are live a message may be delivered twice (Monitor event + poll
25
- * wake) — the accepted cost of a parallel safety net until a deliberate cutover.
26
- *
27
- * MUST be wired ASYNC. As a synchronous hook this would block the turn for the
28
- * whole ceiling. The arming hooks set `async:true, asyncRewake:true`. */
29
- Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.POLL_CLEAN_EXIT = exports.POLL_DELIVERED_EXIT = void 0;
31
- exports.runInboxPoll = runInboxPoll;
32
- const inbox_watch_1 = require("./inbox-watch");
33
- const session_id_1 = require("../session-id");
34
- const poll_registry_1 = require("./poll-registry");
35
- // Ingress-trigger bridge, Adapter B (ASYNC half) — an arrived message is the
36
- // canonical ingress event. On a message wake we run the shared eval over any
37
- // registered `source:"ingress"` rule (peer-C's auto-reply directive) and append
38
- // its inject ALONGSIDE the hand-wired peerHumanTag. MIGRATION DISCIPLINE: the
39
- // hand-wired tag stays until the registered rule is field-green, then it cuts
40
- // over. Off the hot path → a local cache read is fine. docs/ingress-trigger-bridge.md
41
- const ingress_trigger_1 = require("./ingress-trigger");
42
- /** Exit code that triggers the asyncRewake idle-session wake (stderr → reminder). */
43
- exports.POLL_DELIVERED_EXIT = 2;
44
- /** Exit code for every non-wake terminal (ceiling / gate-skip / no key / 4xx). */
45
- exports.POLL_CLEAN_EXIT = 0;
46
- const POLL_MAX_MS_DEFAULT = Number(process.env.GREPRAG_POLL_MAX_MS) || 20 * 60 * 1000;
47
- const IDLE_TIMEOUT_MS = 60_000; // 4 missed 15s heartbeats → reconnect
48
- const IDLE_CHECK_MS = 5_000;
49
- const RECONNECT_MS = 1_000;
50
- const LOG_PREFIX = '[greprag inbox poll]';
51
- /** Format the woken-session reminder. Self-contained + actionable: the agent
52
- * reads this as a system reminder, so it carries the sender, the body, and how
53
- * to reply / see more. */
54
- function formatWake(msg, short, json, projectId) {
55
- if (json)
56
- return `${LOG_PREFIX} ` + JSON.stringify(msg);
57
- const sender = msg.from.email || `${msg.from.tenant}@greprag.com`;
58
- const replyTo = msg.references?.reply_to || sender;
59
- const body = msg.body.length > 600 ? msg.body.slice(0, 600) + '…' : msg.body;
60
- const fromSess = (0, session_id_1.truncateSessionId)(msg.from.session_id ?? null);
61
- const origin = fromSess ? ` (session ${fromSess})` : '';
62
- // Classification tag (server-authoritative): from.session_id SET ⇒ PEER agent
63
- // session → reply directly, no human confirm; null ⇒ HUMAN (cold-open / email)
64
- // → summarize + confirm. Reply, NOT obey — peer-requested destructive actions
65
- // still pass the normal gates. adr: adr/monitor-resilience.md
66
- const tag = (0, session_id_1.peerHumanTag)(msg.from.session_id);
67
- // Adapter B: append any registered ingress directive (best-effort, fail-open).
68
- // Sits ALONGSIDE the hand-wired tag during migration; a registered peer-C rule
69
- // can refine/replace it without a code change. docs/ingress-trigger-bridge.md
70
- let ingress = '';
71
- if (projectId) {
72
- try {
73
- const res = (0, ingress_trigger_1.runIngressGate)((0, ingress_trigger_1.triggerFromMessage)(msg), projectId);
74
- if (res.directive)
75
- ingress = `${res.directive}\n`;
76
- }
77
- catch { /* ingress eval is best-effort — never block the wake */ }
78
- }
79
- return (`${LOG_PREFIX} 📬 inbox message for ${short} from ${sender}${origin}:\n`
80
- + `${tag}\n`
81
- + ingress
82
- + `${body}\n`
83
- + `— reply now (even mid-task): greprag send "…" --to ${replyTo} --from-session ${short}`
84
- + ` · see all: greprag inbox --session ${short}`);
85
- }
86
- function buildPollUrl(apiUrl, session, since) {
87
- const u = new URL(apiUrl.replace(/\/+$/, '') + '/v1/inbox/stream');
88
- u.searchParams.set('session', session);
89
- if (since)
90
- u.searchParams.set('since', since);
91
- return u.toString();
92
- }
93
- function sleep(ms, signal) {
94
- return new Promise((resolve) => {
95
- const t = setTimeout(resolve, ms);
96
- const onAbort = () => { clearTimeout(t); resolve(); };
97
- if (signal.aborted) {
98
- onAbort();
99
- return;
100
- }
101
- signal.addEventListener('abort', onAbort, { once: true });
102
- });
103
- }
104
- /** Read the SSE stream until the FIRST message (→ write stderr, persist cursor,
105
- * delivered:true), the stream ends/idles (delivered:false), a 4xx (fatal:true),
106
- * or abort. Composes the outer signal with an idle-timeout so a half-open socket
107
- * can't hang past IDLE_TIMEOUT_MS. */
108
- async function readUntilMessage(url, apiKey, outer, json, session, initialId, projectId) {
109
- const inner = new AbortController();
110
- const onOuter = () => inner.abort();
111
- if (outer.aborted)
112
- inner.abort();
113
- else
114
- outer.addEventListener('abort', onOuter, { once: true });
115
- let lastByteAt = Date.now();
116
- const idleTimer = setInterval(() => {
117
- if (Date.now() - lastByteAt > IDLE_TIMEOUT_MS)
118
- inner.abort();
119
- }, IDLE_CHECK_MS);
120
- let lastId = initialId;
121
- try {
122
- const res = await fetch(url, {
123
- headers: { Authorization: `Bearer ${apiKey}`, Accept: 'text/event-stream' },
124
- signal: inner.signal,
125
- });
126
- if (!res.ok) {
127
- const text = await res.text().catch(() => '');
128
- if (res.status >= 400 && res.status < 500) {
129
- process.stderr.write(`${LOG_PREFIX} HTTP ${res.status}: ${text.slice(0, 160)}\n`);
130
- return { delivered: false, lastId, fatal: true };
131
- }
132
- throw new Error(`HTTP ${res.status}`);
133
- }
134
- if (!res.body)
135
- throw new Error('no response body');
136
- const reader = res.body.getReader();
137
- const decoder = new TextDecoder();
138
- let buffer = '';
139
- try {
140
- while (true) {
141
- const { done, value } = await reader.read();
142
- if (done)
143
- return { delivered: false, lastId };
144
- lastByteAt = Date.now();
145
- buffer += decoder.decode(value, { stream: true });
146
- buffer = buffer.replace(/\r\n/g, '\n');
147
- let sep;
148
- while ((sep = buffer.indexOf('\n\n')) >= 0) {
149
- const block = buffer.slice(0, sep);
150
- buffer = buffer.slice(sep + 2);
151
- const ev = (0, inbox_watch_1.parseEventBlock)(block);
152
- if (!ev)
153
- continue;
154
- if (ev.id)
155
- lastId = ev.id;
156
- if (ev.event !== 'message')
157
- continue; // ignore open / replay-complete / error markers
158
- let msg;
159
- try {
160
- msg = JSON.parse(ev.data);
161
- }
162
- catch {
163
- continue;
164
- }
165
- // FIRST real message → wake. Persist the cursor BEFORE we exit so the
166
- // next poll resumes AFTER this id (no re-deliver); the agent reads any
167
- // burst remainder via `greprag inbox`, which the next poll also catches.
168
- if (ev.id)
169
- (0, poll_registry_1.writePollCursor)(session, ev.id);
170
- process.stderr.write(formatWake(msg, session, json, projectId) + '\n');
171
- return { delivered: true, lastId };
172
- }
173
- }
174
- }
175
- finally {
176
- try {
177
- reader.releaseLock();
178
- }
179
- catch { /* already released */ }
180
- }
181
- }
182
- finally {
183
- clearInterval(idleTimer);
184
- outer.removeEventListener('abort', onOuter);
185
- }
186
- }
187
- /** Run one idle-liveness poll. Returns the exit code the hook wrapper exits with:
188
- * POLL_DELIVERED_EXIT (2) on a message (→ asyncRewake wake) or POLL_CLEAN_EXIT
189
- * (0) on every other terminal. Returns (does NOT call process.exit) so it is
190
- * unit-testable; the `greprag-hook poll` wrapper exits with the returned code. */
191
- async function runInboxPoll(opts) {
192
- if (!opts.apiKey || !opts.session)
193
- return exports.POLL_CLEAN_EXIT; // unconfigured → silent clean
194
- if (opts.gate && (0, poll_registry_1.isPollArmed)(opts.session))
195
- return exports.POLL_CLEAN_EXIT; // re-arm gate: one live poll
196
- const register = opts.register !== false;
197
- if (register)
198
- (0, poll_registry_1.registerPoll)(opts.session, process.pid);
199
- const maxMs = opts.maxMs ?? POLL_MAX_MS_DEFAULT;
200
- const ac = new AbortController();
201
- const ceiling = setTimeout(() => ac.abort(), maxMs);
202
- if (typeof ceiling.unref === 'function')
203
- ceiling.unref();
204
- const onSignal = () => ac.abort();
205
- process.on('SIGINT', onSignal);
206
- process.on('SIGTERM', onSignal);
207
- if (opts.signal) {
208
- if (opts.signal.aborted)
209
- ac.abort();
210
- else
211
- opts.signal.addEventListener('abort', onSignal, { once: true });
212
- }
213
- let code = exports.POLL_CLEAN_EXIT;
214
- try {
215
- let cursor = (0, poll_registry_1.readPollCursor)(opts.session);
216
- while (!ac.signal.aborted) {
217
- const url = buildPollUrl(opts.apiUrl, opts.session, cursor);
218
- try {
219
- const r = await readUntilMessage(url, opts.apiKey, ac.signal, !!opts.json, opts.session, cursor, opts.projectId);
220
- if (r.delivered) {
221
- code = exports.POLL_DELIVERED_EXIT;
222
- break;
223
- }
224
- if (r.fatal) {
225
- // 4xx WITH a cursor = the stored `since` points to a pruned/deleted
226
- // message the server now rejects. Drop the cursor and retry live-tail
227
- // ONCE so a dead cursor can't permanently wedge this session's delivery.
228
- // A 4xx with NO cursor is a genuine fatal (bad key/session) → clean exit.
229
- if (cursor) {
230
- (0, poll_registry_1.clearPollCursor)(opts.session);
231
- cursor = null;
232
- if (ac.signal.aborted)
233
- break;
234
- await sleep(RECONNECT_MS, ac.signal);
235
- continue;
236
- }
237
- code = exports.POLL_CLEAN_EXIT;
238
- break; // genuine 4xx → clean, do not re-arm-spin
239
- }
240
- if (r.lastId)
241
- cursor = r.lastId;
242
- }
243
- catch (err) {
244
- const msg = err?.message || String(err);
245
- process.stderr.write(`${LOG_PREFIX} disconnect: ${msg} — reconnecting\n`);
246
- }
247
- if (ac.signal.aborted)
248
- break;
249
- await sleep(RECONNECT_MS, ac.signal);
250
- }
251
- }
252
- finally {
253
- clearTimeout(ceiling);
254
- process.off('SIGINT', onSignal);
255
- process.off('SIGTERM', onSignal);
256
- if (register)
257
- (0, poll_registry_1.deregisterPoll)(opts.session, process.pid);
258
- }
259
- return code;
260
- }
261
- //# sourceMappingURL=inbox-poll.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inbox-poll.js","sourceRoot":"","sources":["../../src/commands/inbox-poll.ts"],"names":[],"mappings":"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"}
@@ -1,27 +0,0 @@
1
- /** Local inbox spool — the delivery channel between the machine's desk-line and
2
- * a session's watcher.
3
- *
4
- * The delivery-cutover counterpart to the live-list relay: instead of each
5
- * session's watcher holding a cloud SSE socket to receive inbound (the path that
6
- * keeps the stale InboxDO whiteboard alive), the desk-line APPENDS each inbound
7
- * message to a per-session spool file here, and the watcher TAILS it locally. No
8
- * NAT crossing on the delivery hot path, no per-session cloud socket — the
9
- * watcher reads a file the desk-line wrote.
10
- *
11
- * Format: newline-delimited JSON (one message per line) at
12
- * ~/.greprag/inbox/<short>.ndjson. Bounded to the last SPOOL_MAX lines so a
13
- * long-lived session can't grow it without bound; the watcher dedups by message
14
- * id, so the bound never drops an unseen message under normal cadence.
15
- *
16
- * adr: adr/desk-line-relay.md */
17
- export declare function spoolPath(short: string): string;
18
- /** Append a message to a session's spool (desk-line write side). Trims to the
19
- * last SPOOL_MAX lines. Best-effort: a failed write is logged by the caller's
20
- * catch, never throws into the relay loop. */
21
- export declare function appendToSpool(short: string, message: unknown): void;
22
- /** Tail a session's spool, invoking `onMessage` for each appended JSON line.
23
- * Polls by byte offset (survives the file not existing yet). Fully
24
- * error-isolated: every parse/read failure is swallowed so a malformed spool
25
- * line can never crash the watcher's live SSE path. Returns when `signal`
26
- * aborts. adr: adr/desk-line-relay.md */
27
- export declare function tailSpool(short: string, onMessage: (msg: unknown) => void, signal: AbortSignal): Promise<void>;
@@ -1,151 +0,0 @@
1
- "use strict";
2
- /** Local inbox spool — the delivery channel between the machine's desk-line and
3
- * a session's watcher.
4
- *
5
- * The delivery-cutover counterpart to the live-list relay: instead of each
6
- * session's watcher holding a cloud SSE socket to receive inbound (the path that
7
- * keeps the stale InboxDO whiteboard alive), the desk-line APPENDS each inbound
8
- * message to a per-session spool file here, and the watcher TAILS it locally. No
9
- * NAT crossing on the delivery hot path, no per-session cloud socket — the
10
- * watcher reads a file the desk-line wrote.
11
- *
12
- * Format: newline-delimited JSON (one message per line) at
13
- * ~/.greprag/inbox/<short>.ndjson. Bounded to the last SPOOL_MAX lines so a
14
- * long-lived session can't grow it without bound; the watcher dedups by message
15
- * id, so the bound never drops an unseen message under normal cadence.
16
- *
17
- * adr: adr/desk-line-relay.md */
18
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
19
- if (k2 === undefined) k2 = k;
20
- var desc = Object.getOwnPropertyDescriptor(m, k);
21
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
22
- desc = { enumerable: true, get: function() { return m[k]; } };
23
- }
24
- Object.defineProperty(o, k2, desc);
25
- }) : (function(o, m, k, k2) {
26
- if (k2 === undefined) k2 = k;
27
- o[k2] = m[k];
28
- }));
29
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
- Object.defineProperty(o, "default", { enumerable: true, value: v });
31
- }) : function(o, v) {
32
- o["default"] = v;
33
- });
34
- var __importStar = (this && this.__importStar) || (function () {
35
- var ownKeys = function(o) {
36
- ownKeys = Object.getOwnPropertyNames || function (o) {
37
- var ar = [];
38
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
- return ar;
40
- };
41
- return ownKeys(o);
42
- };
43
- return function (mod) {
44
- if (mod && mod.__esModule) return mod;
45
- var result = {};
46
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
- __setModuleDefault(result, mod);
48
- return result;
49
- };
50
- })();
51
- Object.defineProperty(exports, "__esModule", { value: true });
52
- exports.spoolPath = spoolPath;
53
- exports.appendToSpool = appendToSpool;
54
- exports.tailSpool = tailSpool;
55
- const fs = __importStar(require("fs"));
56
- const path = __importStar(require("path"));
57
- const SPOOL_DIRNAME = 'inbox';
58
- const SPOOL_MAX = 200; // keep the last N messages per session
59
- const TAIL_POLL_MS = 400; // how often the watcher checks for appends
60
- function grepragHome() {
61
- const home = process.env.HOME || process.env.USERPROFILE || '';
62
- return path.join(home, '.greprag');
63
- }
64
- function spoolDir() { return path.join(grepragHome(), SPOOL_DIRNAME); }
65
- function spoolPath(short) { return path.join(spoolDir(), `${short}.ndjson`); }
66
- /** Append a message to a session's spool (desk-line write side). Trims to the
67
- * last SPOOL_MAX lines. Best-effort: a failed write is logged by the caller's
68
- * catch, never throws into the relay loop. */
69
- function appendToSpool(short, message) {
70
- const dir = spoolDir();
71
- fs.mkdirSync(dir, { recursive: true });
72
- const file = spoolPath(short);
73
- fs.appendFileSync(file, JSON.stringify(message) + '\n');
74
- // Trim if it grew past the cap (cheap: only re-read when the file is large).
75
- try {
76
- const stat = fs.statSync(file);
77
- if (stat.size > SPOOL_MAX * 4096) {
78
- const lines = fs.readFileSync(file, 'utf-8').split('\n').filter(Boolean);
79
- if (lines.length > SPOOL_MAX) {
80
- fs.writeFileSync(file, lines.slice(-SPOOL_MAX).join('\n') + '\n');
81
- }
82
- }
83
- }
84
- catch { /* trim is best-effort */ }
85
- }
86
- /** Tail a session's spool, invoking `onMessage` for each appended JSON line.
87
- * Polls by byte offset (survives the file not existing yet). Fully
88
- * error-isolated: every parse/read failure is swallowed so a malformed spool
89
- * line can never crash the watcher's live SSE path. Returns when `signal`
90
- * aborts. adr: adr/desk-line-relay.md */
91
- async function tailSpool(short, onMessage, signal) {
92
- const file = spoolPath(short);
93
- // Start at the current end of file — the desk-line only writes NEW inbound, and
94
- // the watcher already got history via the SSE replay / `greprag inbox`. (When
95
- // the SSE path is eventually removed, switch this to 0 to replay the spool.)
96
- let offset = 0;
97
- try {
98
- offset = fs.statSync(file).size;
99
- }
100
- catch {
101
- offset = 0;
102
- }
103
- let carry = '';
104
- while (!signal.aborted) {
105
- try {
106
- let size = 0;
107
- try {
108
- size = fs.statSync(file).size;
109
- }
110
- catch {
111
- size = 0;
112
- }
113
- if (size < offset) {
114
- offset = 0;
115
- carry = '';
116
- } // file was rotated/trimmed
117
- if (size > offset) {
118
- const fd = fs.openSync(file, 'r');
119
- try {
120
- const buf = Buffer.alloc(size - offset);
121
- fs.readSync(fd, buf, 0, buf.length, offset);
122
- offset = size;
123
- carry += buf.toString('utf-8');
124
- let nl;
125
- while ((nl = carry.indexOf('\n')) >= 0) {
126
- const line = carry.slice(0, nl).trim();
127
- carry = carry.slice(nl + 1);
128
- if (!line)
129
- continue;
130
- try {
131
- onMessage(JSON.parse(line));
132
- }
133
- catch { /* skip bad line */ }
134
- }
135
- }
136
- finally {
137
- fs.closeSync(fd);
138
- }
139
- }
140
- }
141
- catch { /* read cycle best-effort — never throw into the watcher */ }
142
- await sleep(TAIL_POLL_MS, signal);
143
- }
144
- }
145
- function sleep(ms, signal) {
146
- return new Promise(resolve => {
147
- const t = setTimeout(resolve, ms);
148
- signal.addEventListener('abort', () => { clearTimeout(t); resolve(); }, { once: true });
149
- });
150
- }
151
- //# sourceMappingURL=inbox-spool.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inbox-spool.js","sourceRoot":"","sources":["../../src/commands/inbox-spool.ts"],"names":[],"mappings":"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"}