@groundnuty/macf-channel-server 0.2.10 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,76 @@
1
+ import type { Logger } from '@groundnuty/macf-core';
2
+ import { z } from 'zod';
3
+ export declare const CheckpointToMemoryInputSchema: {
4
+ readonly session_id: z.ZodString;
5
+ readonly transcript_path: z.ZodOptional<z.ZodString>;
6
+ readonly cwd: z.ZodString;
7
+ readonly trigger: z.ZodOptional<z.ZodEnum<{
8
+ manual: "manual";
9
+ auto: "auto";
10
+ }>>;
11
+ readonly summary: z.ZodOptional<z.ZodString>;
12
+ };
13
+ export declare const CheckpointToMemoryOutputSchema: {
14
+ readonly written: z.ZodBoolean;
15
+ readonly path: z.ZodOptional<z.ZodString>;
16
+ readonly deduplicated: z.ZodBoolean;
17
+ readonly reason: z.ZodOptional<z.ZodString>;
18
+ };
19
+ export interface CheckpointToMemoryDeps {
20
+ readonly selfAgentName: string;
21
+ readonly logger: Logger;
22
+ /**
23
+ * Override for `~/.claude/projects` location. Production code uses
24
+ * `os.homedir()`; tests inject a temp dir. Optional — when absent,
25
+ * defaults to `${HOME}/.claude/projects`.
26
+ */
27
+ readonly projectsRootOverride?: string;
28
+ /**
29
+ * Override for `new Date()` so dedup-naming is testable against
30
+ * fixed dates. Production: undefined (uses real clock). Test: returns
31
+ * a fixed Date.
32
+ */
33
+ readonly nowOverride?: () => Date;
34
+ }
35
+ export interface CheckpointToMemoryInput {
36
+ readonly session_id: string;
37
+ readonly transcript_path?: string;
38
+ readonly cwd: string;
39
+ readonly trigger?: 'manual' | 'auto';
40
+ readonly summary?: string;
41
+ }
42
+ export interface CheckpointToMemoryResult {
43
+ readonly written: boolean;
44
+ readonly path?: string;
45
+ readonly deduplicated: boolean;
46
+ readonly reason?: string;
47
+ }
48
+ /**
49
+ * Encode an absolute filesystem path into Claude Code's per-project
50
+ * directory naming scheme used under `~/.claude/projects/`.
51
+ *
52
+ * Empirical scheme (verified by listing existing project dirs in the
53
+ * env): every `/` is replaced with `-`. The resulting string is the
54
+ * directory name; no other normalization is applied (case + spaces
55
+ * preserved).
56
+ */
57
+ export declare function encodeProjectDir(cwd: string): string;
58
+ /**
59
+ * Resolve the canonical memory directory for a given project cwd.
60
+ * Pure function — does not touch the filesystem.
61
+ */
62
+ export declare function resolveMemoryDir(cwd: string, projectsRoot: string): string;
63
+ /**
64
+ * Tool body — resolves memory dir, dedups by session-id, writes file.
65
+ *
66
+ * Wrapped in OTel span `macf.tool.checkpoint_to_memory` (SpanKind.INTERNAL —
67
+ * pure local filesystem op, no outbound network) per DR-023 telemetry
68
+ * pattern. Span attributes mirror the input shape so checkpoint cadence
69
+ * + outcome are visible in Tempo: trigger, deduplicated, written.
70
+ *
71
+ * Non-blocking: any error path resolves with `written: false` + a
72
+ * `reason` string. Never throws to the caller. The caller (server.ts
73
+ * tool wrapper) returns `isError: false` so PreCompact proceeds.
74
+ */
75
+ export declare function checkpointToMemory(deps: CheckpointToMemoryDeps, input: CheckpointToMemoryInput): Promise<CheckpointToMemoryResult>;
76
+ //# sourceMappingURL=checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../src/checkpoint.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,6BAA6B;;;;;;;;;CAWhC,CAAC;AAEX,eAAO,MAAM,8BAA8B;;;;;CASjC,CAAC;AAEX,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE1E;AAwGD;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,sBAAsB,EAC5B,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,wBAAwB,CAAC,CAmInC"}
@@ -0,0 +1,322 @@
1
+ /**
2
+ * `checkpoint_to_memory` MCP tool implementation per macf#271 / DR-023 UC-3.
3
+ *
4
+ * Registered on the channel-server's MCP surface; called by Claude Code's
5
+ * plugin loader when the `PreCompact` hook fires (per
6
+ * `packages/macf/plugin/hooks/hooks.json`). The tool writes a structured
7
+ * session-handoff entry to the agent's per-project memory directory
8
+ * (`~/.claude/projects/<encoded-cwd>/memory/`) so the next session can
9
+ * read the entry via the standard MEMORY.md index lookup pattern.
10
+ *
11
+ * Hook event: `PreCompact` (NOT `Stop`).
12
+ *
13
+ * The original macf#271 issue body framed UC-3 as a `Stop` hook. That
14
+ * was wrong-cadence: per `feedback_stop_hook_fires_on_turn_end_not_session_exit.md`
15
+ * + Claude Code 2.1.118+ behaviour, `Stop` fires after every LLM turn
16
+ * completes, not at session exit / before compaction. `/exit` is a TUI-
17
+ * local command and bypasses Stop hooks entirely. The natural trigger
18
+ * for "synthesize before context loss" is `PreCompact` — fires before
19
+ * BOTH `/compact` (manual) and auto-compaction. Confirmed in macf#271
20
+ * comment exchange between code-agent + science-agent (2026-05-01);
21
+ * DR-023 §UC-3 amendment in this PR codifies the resolution.
22
+ *
23
+ * Failure semantics (per DR-023 §UC-3 + §"Failure-mode contract"):
24
+ *
25
+ * The hook layer is observational + non-blocking by default. Memory
26
+ * directory missing, file write failure, malformed input — all log a
27
+ * diagnostic event and return `{written: false, reason}` to the hook
28
+ * without raising. The caller (server.ts tool wrapper) returns
29
+ * `isError: false` regardless so the PreCompact event always proceeds.
30
+ * A failed checkpoint is recoverable: the operator can manually
31
+ * author a handoff entry post-compaction (the existing
32
+ * `synthesize-before-compaction` discipline).
33
+ *
34
+ * Critically: the hook MUST NOT block compaction even on failure.
35
+ * PreCompact CAN block (`{decision: "block", reason}`) but the design
36
+ * choice here is observational — checkpoint is best-effort, missing
37
+ * one is a lost paragraph, not a safety violation.
38
+ */
39
+ import { writeFile, mkdir, readdir, readFile } from 'node:fs/promises';
40
+ import { homedir } from 'node:os';
41
+ import { join } from 'node:path';
42
+ import { z } from 'zod';
43
+ import { context, SpanKind, SpanStatusCode, trace } from '@opentelemetry/api';
44
+ import { SpanNames, Attr, GenAiAttr } from './tracing.js';
45
+ export const CheckpointToMemoryInputSchema = {
46
+ session_id: z.string().min(1)
47
+ .describe('Claude Code session identifier (UUID). Substituted from PreCompact hook input ${session_id}.'),
48
+ transcript_path: z.string().optional()
49
+ .describe('Absolute path to session transcript (jsonl). Substituted from ${transcript_path}.'),
50
+ cwd: z.string().min(1)
51
+ .describe('Workspace directory the session is operating in. Substituted from ${cwd}. Used to resolve the per-project memory directory.'),
52
+ trigger: z.enum(['manual', 'auto']).optional()
53
+ .describe('What triggered compaction — "manual" (`/compact` invocation) or "auto" (token threshold). Substituted from PreCompact matcher.'),
54
+ summary: z.string().optional()
55
+ .describe('Optional human-authored synthesis text to embed in the checkpoint body. When omitted, a stub body is written with metadata only — the LLM can populate it post-compaction or operator can author manually.'),
56
+ };
57
+ export const CheckpointToMemoryOutputSchema = {
58
+ written: z.boolean()
59
+ .describe('True if the checkpoint file was successfully written to disk.'),
60
+ path: z.string().optional()
61
+ .describe('Absolute path of the written or updated checkpoint file. Present when `written=true`.'),
62
+ deduplicated: z.boolean()
63
+ .describe('True if a pre-existing checkpoint for this session_id was updated (rather than a new file created).'),
64
+ reason: z.string().optional()
65
+ .describe('Diagnostic reason when `written=false`. Present on failure paths only.'),
66
+ };
67
+ /**
68
+ * Encode an absolute filesystem path into Claude Code's per-project
69
+ * directory naming scheme used under `~/.claude/projects/`.
70
+ *
71
+ * Empirical scheme (verified by listing existing project dirs in the
72
+ * env): every `/` is replaced with `-`. The resulting string is the
73
+ * directory name; no other normalization is applied (case + spaces
74
+ * preserved).
75
+ */
76
+ export function encodeProjectDir(cwd) {
77
+ return cwd.replace(/\//g, '-');
78
+ }
79
+ /**
80
+ * Resolve the canonical memory directory for a given project cwd.
81
+ * Pure function — does not touch the filesystem.
82
+ */
83
+ export function resolveMemoryDir(cwd, projectsRoot) {
84
+ return join(projectsRoot, encodeProjectDir(cwd), 'memory');
85
+ }
86
+ /** Format YYYY_MM_DD per `project_session_handoff_<date>.md` convention. */
87
+ function formatDate(d) {
88
+ const yyyy = d.getUTCFullYear().toString().padStart(4, '0');
89
+ const mm = (d.getUTCMonth() + 1).toString().padStart(2, '0');
90
+ const dd = d.getUTCDate().toString().padStart(2, '0');
91
+ return `${yyyy}_${mm}_${dd}`;
92
+ }
93
+ /**
94
+ * Look for a pre-existing checkpoint with the same `originSessionId`
95
+ * in this memory directory. If found, return its absolute path so the
96
+ * caller can OVERWRITE it (deduplication: PreCompact may fire multiple
97
+ * times in a single session for sequential auto-compactions). If
98
+ * not found, return undefined.
99
+ *
100
+ * Scans only files matching the `project_session_handoff_*.md` glob
101
+ * to keep cost bounded (memory dirs accumulate ~50-100 entries; reading
102
+ * all of them on every PreCompact would be wasteful).
103
+ */
104
+ async function findExistingCheckpoint(memoryDir, sessionId) {
105
+ let entries;
106
+ try {
107
+ entries = await readdir(memoryDir);
108
+ }
109
+ catch {
110
+ return undefined;
111
+ }
112
+ for (const entry of entries) {
113
+ if (!entry.startsWith('project_session_handoff_') || !entry.endsWith('.md')) {
114
+ continue;
115
+ }
116
+ const fullPath = join(memoryDir, entry);
117
+ let body;
118
+ try {
119
+ body = await readFile(fullPath, 'utf8');
120
+ }
121
+ catch {
122
+ continue;
123
+ }
124
+ // Frontmatter is YAML at top of file: ---\n...\n---
125
+ // Look for `originSessionId: <sessionId>` line within first ~20
126
+ // lines (frontmatter is short by convention).
127
+ const head = body.split('\n', 25).join('\n');
128
+ if (head.includes(`originSessionId: ${sessionId}`)) {
129
+ return fullPath;
130
+ }
131
+ }
132
+ return undefined;
133
+ }
134
+ /**
135
+ * Build the checkpoint markdown body. Frontmatter follows the existing
136
+ * `project_session_handoff_*.md` convention (verified against actual
137
+ * memory entries: name, description, type=project, originSessionId).
138
+ *
139
+ * Body shape: when `summary` provided, embeds it directly. Without
140
+ * summary, writes a minimal-stub body so the file is well-formed +
141
+ * findable; future tools can append.
142
+ *
143
+ * Why ISO-8601 date in the description: matches the existing entries'
144
+ * pattern (e.g., "2026-04-30 session handoff — three release cycles
145
+ * (v0.2.5 → v0.2.7) ...") so the MEMORY.md index renders consistently
146
+ * if/when an entry for this checkpoint is added.
147
+ */
148
+ function buildCheckpointBody(input, agentName, date) {
149
+ const isoDate = date.toISOString();
150
+ const dateOnly = formatDate(date).replace(/_/g, '-');
151
+ const trigger = input.trigger ?? 'unknown';
152
+ const transcriptLine = input.transcript_path !== undefined
153
+ ? `\n- transcript: \`${input.transcript_path}\``
154
+ : '';
155
+ const description = `Auto-checkpoint written by ${agentName} on ${dateOnly} via PreCompact hook (trigger=${trigger}).`;
156
+ const bodyContent = input.summary !== undefined && input.summary.length > 0
157
+ ? input.summary
158
+ : '_Auto-checkpoint stub — body not populated by hook input. The next session reads this frontmatter for session-id correlation; populate the body manually if a synthesis paragraph is needed._';
159
+ return `---
160
+ name: ${dateOnly} session checkpoint (PreCompact auto-write)
161
+ description: ${description}
162
+ type: project
163
+ originSessionId: ${input.session_id}
164
+ ---
165
+ **Auto-checkpoint** written by \`${agentName}\` at \`${isoDate}\` via PreCompact hook (DR-023 UC-3).
166
+
167
+ ## Session metadata
168
+
169
+ - agent: \`${agentName}\`
170
+ - session_id: \`${input.session_id}\`
171
+ - cwd: \`${input.cwd}\`
172
+ - trigger: \`${trigger}\`${transcriptLine}
173
+
174
+ ## Body
175
+
176
+ ${bodyContent}
177
+ `;
178
+ }
179
+ /**
180
+ * Tool body — resolves memory dir, dedups by session-id, writes file.
181
+ *
182
+ * Wrapped in OTel span `macf.tool.checkpoint_to_memory` (SpanKind.INTERNAL —
183
+ * pure local filesystem op, no outbound network) per DR-023 telemetry
184
+ * pattern. Span attributes mirror the input shape so checkpoint cadence
185
+ * + outcome are visible in Tempo: trigger, deduplicated, written.
186
+ *
187
+ * Non-blocking: any error path resolves with `written: false` + a
188
+ * `reason` string. Never throws to the caller. The caller (server.ts
189
+ * tool wrapper) returns `isError: false` so PreCompact proceeds.
190
+ */
191
+ export async function checkpointToMemory(deps, input) {
192
+ const tracer = trace.getTracer('macf');
193
+ return tracer.startActiveSpan(SpanNames.ToolCheckpointToMemory, {
194
+ kind: SpanKind.INTERNAL,
195
+ attributes: {
196
+ [GenAiAttr.System]: 'macf',
197
+ [GenAiAttr.OperationName]: 'checkpoint_to_memory',
198
+ [Attr.CheckpointTrigger]: input.trigger ?? 'unknown',
199
+ },
200
+ }, async (span) => {
201
+ try {
202
+ const projectsRoot = deps.projectsRootOverride
203
+ ?? join(homedir(), '.claude', 'projects');
204
+ const memoryDir = resolveMemoryDir(input.cwd, projectsRoot);
205
+ // Ensure dir exists. mkdir recursive is idempotent.
206
+ try {
207
+ await mkdir(memoryDir, { recursive: true });
208
+ }
209
+ catch (err) {
210
+ const reason = `mkdir_failed: ${err instanceof Error ? err.message : String(err)}`;
211
+ deps.logger.warn('checkpoint_mkdir_failed', {
212
+ memory_dir: memoryDir,
213
+ error: reason,
214
+ });
215
+ span.setAttribute(Attr.CheckpointWritten, false);
216
+ span.setStatus({ code: SpanStatusCode.OK });
217
+ return { written: false, deduplicated: false, reason };
218
+ }
219
+ // Dedup: if an existing entry has originSessionId === input.session_id,
220
+ // overwrite it. Otherwise allocate a fresh path. Naming pattern matches
221
+ // existing convention `project_session_handoff_<YYYY_MM_DD>.md`; if
222
+ // multiple session-ids hit the same date, the second (and later) get
223
+ // a short-session-id suffix to avoid collision.
224
+ const existing = await findExistingCheckpoint(memoryDir, input.session_id);
225
+ const now = (deps.nowOverride ?? (() => new Date()))();
226
+ let targetPath;
227
+ let deduplicated;
228
+ if (existing !== undefined) {
229
+ targetPath = existing;
230
+ deduplicated = true;
231
+ }
232
+ else {
233
+ deduplicated = false;
234
+ const baseName = `project_session_handoff_${formatDate(now)}.md`;
235
+ const base = join(memoryDir, baseName);
236
+ // If `base` exists and belongs to a DIFFERENT session, suffix
237
+ // with the first 8 chars of the session-id so two sessions in
238
+ // a single calendar-date both land cleanly.
239
+ let collides = false;
240
+ try {
241
+ const existingBody = await readFile(base, 'utf8');
242
+ const head = existingBody.split('\n', 25).join('\n');
243
+ // Different session-id already at the canonical path
244
+ if (!head.includes(`originSessionId: ${input.session_id}`)) {
245
+ collides = true;
246
+ }
247
+ else {
248
+ // Same session-id; the readdir scan above should have caught
249
+ // this — fallthrough to overwrite (defensive: still a write).
250
+ targetPath = base;
251
+ const body = buildCheckpointBody(input, deps.selfAgentName, now);
252
+ await writeFile(targetPath, body, 'utf8');
253
+ span.setAttribute(Attr.CheckpointWritten, true);
254
+ span.setAttribute(Attr.CheckpointDeduplicated, true);
255
+ span.setStatus({ code: SpanStatusCode.OK });
256
+ deps.logger.info('checkpoint_written', {
257
+ path: targetPath,
258
+ session_id: input.session_id,
259
+ deduplicated: 'true',
260
+ });
261
+ return { written: true, path: targetPath, deduplicated: true };
262
+ }
263
+ }
264
+ catch {
265
+ // ENOENT or read error → no collision; use base.
266
+ collides = false;
267
+ }
268
+ if (collides) {
269
+ const shortSid = input.session_id.slice(0, 8);
270
+ targetPath = join(memoryDir, `project_session_handoff_${formatDate(now)}_${shortSid}.md`);
271
+ }
272
+ else {
273
+ targetPath = base;
274
+ }
275
+ }
276
+ const body = buildCheckpointBody(input, deps.selfAgentName, now);
277
+ try {
278
+ await writeFile(targetPath, body, 'utf8');
279
+ }
280
+ catch (err) {
281
+ const reason = `write_failed: ${err instanceof Error ? err.message : String(err)}`;
282
+ deps.logger.warn('checkpoint_write_failed', {
283
+ path: targetPath,
284
+ error: reason,
285
+ });
286
+ span.setAttribute(Attr.CheckpointWritten, false);
287
+ span.setStatus({ code: SpanStatusCode.OK });
288
+ return { written: false, deduplicated, reason };
289
+ }
290
+ span.setAttribute(Attr.CheckpointWritten, true);
291
+ span.setAttribute(Attr.CheckpointDeduplicated, deduplicated);
292
+ span.setStatus({ code: SpanStatusCode.OK });
293
+ deps.logger.info('checkpoint_written', {
294
+ path: targetPath,
295
+ session_id: input.session_id,
296
+ deduplicated: deduplicated ? 'true' : 'false',
297
+ });
298
+ return { written: true, path: targetPath, deduplicated };
299
+ }
300
+ catch (err) {
301
+ // Defense-in-depth: any unexpected throw inside the body still
302
+ // surfaces as a non-blocking outcome to the caller.
303
+ span.recordException(err);
304
+ span.setStatus({
305
+ code: SpanStatusCode.ERROR,
306
+ message: err instanceof Error ? err.message : String(err),
307
+ });
308
+ const reason = `unexpected_error: ${err instanceof Error ? err.message : String(err)}`;
309
+ deps.logger.error('checkpoint_unexpected_error', {
310
+ error: reason,
311
+ });
312
+ return { written: false, deduplicated: false, reason };
313
+ }
314
+ finally {
315
+ span.end();
316
+ }
317
+ });
318
+ }
319
+ // Suppress unused-import lint for `context` — kept for future
320
+ // trace-correlation use (parent-span handoff) symmetric with notify-peer.ts.
321
+ void context;
322
+ //# sourceMappingURL=checkpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../src/checkpoint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { GitHubVariablesClient, Logger } from '@groundnuty/macf-core';
2
+ import type { TokenRefresher } from './token-refresh.js';
3
+ export interface RefreshAwareClientDeps {
4
+ readonly pathPrefix: string;
5
+ readonly tokenRefresher: TokenRefresher;
6
+ readonly logger: Logger;
7
+ }
8
+ /**
9
+ * Wrap a path-prefix + token-refresher into a `GitHubVariablesClient`
10
+ * that auto-refreshes on 401. Drop-in replacement for the bare
11
+ * `createGitHubClient(pathPrefix, token)` call in `server.ts` —
12
+ * passes through the existing Registry.create chain unchanged.
13
+ */
14
+ export declare function createRefreshAwareClient(deps: RefreshAwareClientDeps): GitHubVariablesClient;
15
+ //# sourceMappingURL=refresh-aware-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-aware-client.d.ts","sourceRoot":"","sources":["../src/refresh-aware-client.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,sBAAsB,GAC3B,qBAAqB,CA+CvB"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Refresh-aware wrapper around `GitHubVariablesClient` (macf#317).
3
+ *
4
+ * Decorates a base client with two behaviors:
5
+ *
6
+ * 1. **Pre-call refresh** — every method invocation calls
7
+ * `tokenRefresher.getRefreshedToken()` to get a current token + builds
8
+ * a fresh inner client. The cache inside `tokenRefresher` keeps this
9
+ * cheap (no fresh mint within ~50 min of last refresh).
10
+ *
11
+ * 2. **401 retry with force-refresh** — if the inner call throws
12
+ * `GitHubApiError` with `status === 401`, the wrapper invokes
13
+ * `getRefreshedToken({ forceRefresh: true })` (bypasses cache),
14
+ * rebuilds the inner client, and retries the call once. A second 401
15
+ * bubbles up — the issue isn't expiry but a different auth problem
16
+ * (revoked App, deleted installation, etc).
17
+ *
18
+ * Why decorate at the GitHubVariablesClient layer (not the Registry layer):
19
+ * the Registry interface is project-business-logic (register/get/list/
20
+ * remove for agents). The token-stale failure surfaces at the lower
21
+ * (HTTP/auth) layer. Decorating at the lower layer keeps the failure-
22
+ * recovery surface small + uniform across all 4 Registry methods, plus
23
+ * the `/sign` flow's `varsClient`. One wrapper, all callers benefit.
24
+ *
25
+ * Note on closure-capture: the inner client built by `createGitHubClient`
26
+ * captures the token in its closure — there's no mutable token slot. So
27
+ * "rebuild the inner client when the token changes" is the cheapest +
28
+ * cleanest change. Builds are pure-function pricing (~microseconds);
29
+ * negligible vs the network round-trip.
30
+ */
31
+ import { createGitHubClient, GitHubApiError } from '@groundnuty/macf-core';
32
+ /**
33
+ * Wrap a path-prefix + token-refresher into a `GitHubVariablesClient`
34
+ * that auto-refreshes on 401. Drop-in replacement for the bare
35
+ * `createGitHubClient(pathPrefix, token)` call in `server.ts` —
36
+ * passes through the existing Registry.create chain unchanged.
37
+ */
38
+ export function createRefreshAwareClient(deps) {
39
+ const { pathPrefix, tokenRefresher, logger } = deps;
40
+ /**
41
+ * Per-call: get token (cached or fresh), build inner client, invoke fn.
42
+ * On 401, force-refresh + rebuild + retry once. Anything else propagates.
43
+ */
44
+ async function withRefresh(label, fn) {
45
+ const initialToken = await tokenRefresher.getRefreshedToken();
46
+ const inner = createGitHubClient(pathPrefix, initialToken);
47
+ try {
48
+ return await fn(inner);
49
+ }
50
+ catch (err) {
51
+ // GitHubApiError carries status; 401 means token-related auth
52
+ // failure (expired, revoked, missing perm). Other GitHubApiError
53
+ // statuses (404, 422, 5xx) aren't auth-related; let them through.
54
+ if (err instanceof GitHubApiError && err.status === 401) {
55
+ logger.warn('github_api_401_refreshing_and_retrying', {
56
+ method: label,
57
+ });
58
+ const freshToken = await tokenRefresher.getRefreshedToken({
59
+ forceRefresh: true,
60
+ });
61
+ const fresh = createGitHubClient(pathPrefix, freshToken);
62
+ return await fn(fresh);
63
+ }
64
+ throw err;
65
+ }
66
+ }
67
+ return {
68
+ async writeVariable(name, value) {
69
+ await withRefresh('writeVariable', (c) => c.writeVariable(name, value));
70
+ },
71
+ async readVariable(name) {
72
+ return withRefresh('readVariable', (c) => c.readVariable(name));
73
+ },
74
+ async listVariables() {
75
+ return withRefresh('listVariables', (c) => c.listVariables());
76
+ },
77
+ async deleteVariable(name) {
78
+ await withRefresh('deleteVariable', (c) => c.deleteVariable(name));
79
+ },
80
+ };
81
+ }
82
+ //# sourceMappingURL=refresh-aware-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-aware-client.js","sourceRoot":"","sources":["../src/refresh-aware-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAU3E;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAA4B;IAE5B,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAEpD;;;OAGG;IACH,KAAK,UAAU,WAAW,CACxB,KAAa,EACb,EAAgD;QAEhD,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,iBAAiB,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAC3D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,8DAA8D;YAC9D,iEAAiE;YACjE,kEAAkE;YAClE,IAAI,GAAG,YAAY,cAAc,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxD,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE;oBACpD,MAAM,EAAE,KAAK;iBACd,CAAC,CAAC;gBACH,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,iBAAiB,CAAC;oBACxD,YAAY,EAAE,IAAI;iBACnB,CAAC,CAAC;gBACH,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBACzD,OAAO,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;YACzB,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,KAAa;YAC7C,MAAM,WAAW,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC1E,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,IAAY;YAC7B,OAAO,WAAW,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,KAAK,CAAC,aAAa;YACjB,OAAO,WAAW,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,IAAY;YAC/B,MAAM,WAAW,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QACrE,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/server.js CHANGED
@@ -13,10 +13,11 @@ import { createLogger } from '@groundnuty/macf-core';
13
13
  import { createMcpChannel } from './mcp.js';
14
14
  import { createHealthState } from './health.js';
15
15
  import { createHttpsServer } from './https.js';
16
- import { createRegistryFromConfig } from '@groundnuty/macf-core';
16
+ import { createRegistry, createRegistryFromConfig } from '@groundnuty/macf-core';
17
17
  import { checkCollision, CollisionError } from './collision.js';
18
18
  import { registerShutdownHandler } from './shutdown.js';
19
- import { generateToken } from '@groundnuty/macf-core';
19
+ import { createTokenRefresher } from './token-refresh.js';
20
+ import { createRefreshAwareClient } from './refresh-aware-client.js';
20
21
  import { createChallenge, verifyAndConsumeChallenge } from '@groundnuty/macf-core';
21
22
  import { createChallengeStore } from '@groundnuty/macf-core';
22
23
  import { signCSR } from '@groundnuty/macf-core';
@@ -138,24 +139,83 @@ async function main() {
138
139
  });
139
140
  }
140
141
  };
141
- // P2: Generate token early needed for /sign endpoint and registry
142
- const token = await generateToken();
143
- const registry = createRegistryFromConfig(config.registry, config.project, token);
144
- const { createGitHubClient } = await import('@groundnuty/macf-core');
145
- // Build the variables client for the /sign challenge flow
146
- let signPathPrefix;
147
- switch (config.registry.type) {
148
- case 'org':
149
- signPathPrefix = `/orgs/${config.registry.org}`;
150
- break;
151
- case 'profile':
152
- signPathPrefix = `/repos/${config.registry.user}/${config.registry.user}`;
153
- break;
154
- case 'repo':
155
- signPathPrefix = `/repos/${config.registry.owner}/${config.registry.repo}`;
156
- break;
142
+ // P2: Build the project registry + (GitHub-mode only) /sign varsClient.
143
+ //
144
+ // Pre-macf#317 this section also called `await generateToken()` to
145
+ // mint a static token + pass it into `createRegistryFromConfig` +
146
+ // `createGitHubClient`. The token-refresh wrapper now handles minting
147
+ // lazily (first call to `tokenRefresher.getRefreshedToken()`) — we
148
+ // don't pre-mint here because the refresh-aware client mints on first
149
+ // use anyway, and pre-minting wouldn't improve startup signal.
150
+ //
151
+ // DR-024 / macf#322: local-registry mode dispatches via
152
+ // `createRegistryFromConfig` (which routes 'local' to LocalRegistryClient)
153
+ // instead of building a refresh-aware GitHub Variables client. The
154
+ // `/sign` challenge-response endpoint is structurally inactive in
155
+ // local mode — operators pre-share the CA via filesystem perms, so
156
+ // there is no challenge to verify. `onSign` returns a 503 with a
157
+ // diagnostic body pointing at the local-mode trust model (DR-024
158
+ // §"/sign endpoint disabled in local mode" — Return 404 with diagnostic
159
+ // body strategy chosen so peers that mistakenly try challenge-response
160
+ // get a clear error rather than a connection-refused).
161
+ const isLocalRegistry = config.registry.type === 'local';
162
+ // macf#317: in-runner token refresh. The refresher caches the current
163
+ // token in-process; on each call it returns cached if age < 50min,
164
+ // else mints fresh via macf-gh-token.sh. On 401 from a downstream API
165
+ // call, the refresh-aware client retries once with forceRefresh: true.
166
+ // This closes the >1hr-session expiry gap (silent-fallback Instance 1
167
+ // expiry sub-case) — the cv-architect 401 at 67min uptime witnessed
168
+ // 2026-05-01 was the motivating incident.
169
+ // No-op in local mode (no token to mint) — but constructed unconditionally
170
+ // because subsequent code paths take the refresher reference; the
171
+ // refresher itself only fires on first `getRefreshedToken()` call.
172
+ const tokenRefresher = createTokenRefresher({ logger });
173
+ let registry;
174
+ let varsClient;
175
+ if (isLocalRegistry) {
176
+ // DR-024 §"Decision rule for future PRs" 2: factory dispatch on
177
+ // `registry.type`. The empty token argument is unused for local
178
+ // (LocalRegistryClient ignores it) — kept positional for call-surface
179
+ // symmetry across all four variants.
180
+ registry = createRegistryFromConfig(config.registry, config.project, '');
181
+ // varsClient stays undefined; /sign is structurally inactive.
182
+ }
183
+ else {
184
+ // TypeScript narrowed `config.registry.type` to `repo|org|profile`
185
+ // by virtue of the `isLocalRegistry` check above. The exhaustive
186
+ // switch over the narrowed union still fails the build if a fifth
187
+ // GitHub-backed variant is ever added — same coverage as the
188
+ // pre-DR-024 form.
189
+ let signPathPrefix;
190
+ switch (config.registry.type) {
191
+ case 'org':
192
+ signPathPrefix = `/orgs/${config.registry.org}`;
193
+ break;
194
+ case 'profile':
195
+ signPathPrefix = `/repos/${config.registry.user}/${config.registry.user}`;
196
+ break;
197
+ case 'repo':
198
+ signPathPrefix = `/repos/${config.registry.owner}/${config.registry.repo}`;
199
+ break;
200
+ }
201
+ // Project-registry path prefix differs from /sign prefix only in the
202
+ // org case (registry uses /orgs/<org> too — same shape). Re-derive
203
+ // here for clarity even though it's identical to signPathPrefix.
204
+ const registryClient = createRefreshAwareClient({
205
+ pathPrefix: signPathPrefix,
206
+ tokenRefresher,
207
+ logger,
208
+ });
209
+ registry = createRegistry(registryClient, config.project);
210
+ // Build the variables client for the /sign challenge flow with the
211
+ // same refresh-aware wrapping. Stop hook + /sign both 401 after the
212
+ // 1-hour token TTL absent this fix.
213
+ varsClient = createRefreshAwareClient({
214
+ pathPrefix: signPathPrefix,
215
+ tokenRefresher,
216
+ logger,
217
+ });
157
218
  }
158
- const varsClient = createGitHubClient(signPathPrefix, token);
159
219
  // In-memory challenge store (DR-010, #80). Process-local; server restart
160
220
  // between step 1 and step 2 of a flow invalidates outstanding challenges.
161
221
  const challengeStore = createChallengeStore();
@@ -163,6 +223,16 @@ async function main() {
163
223
  // Step 1: allocate challenge, return id + instruction (no registry write).
164
224
  // Step 2: verify challenge_id + registry-observed value, sign CSR.
165
225
  const onSign = async (request) => {
226
+ // DR-024 §"/sign endpoint disabled in local mode": local-registry
227
+ // mode has no GitHub-mediated identity proof. Reject with a clear
228
+ // 404 + diagnostic body so peers that mistakenly hit /sign see why
229
+ // it's not part of the trust path here.
230
+ if (varsClient === undefined) {
231
+ throw new HttpError(404, '/sign is disabled in local-registry mode (DR-024). ' +
232
+ 'Local mode uses pre-shared CA via filesystem permissions; ' +
233
+ 'there is no challenge-response trust path.');
234
+ }
235
+ const sharedVarsClient = varsClient;
166
236
  // Try to load CA key — if not available, this agent can't sign.
167
237
  let ca;
168
238
  try {
@@ -194,7 +264,7 @@ async function main() {
194
264
  agentName: request.agent_name,
195
265
  challengeId: request.challenge_id,
196
266
  store: challengeStore,
197
- client: varsClient,
267
+ client: sharedVarsClient,
198
268
  });
199
269
  if (result === 'mismatch') {
200
270
  // Generic error — do not leak which check failed (no oracle for
@@ -259,6 +329,42 @@ async function main() {
259
329
  isError: result.peers_attempted > 0 && result.peers_delivered === 0,
260
330
  };
261
331
  });
332
+ // macf#271 / DR-023 UC-3: register checkpoint_to_memory MCP tool on
333
+ // the same MCP channel. Hook event is PreCompact (NOT Stop, despite
334
+ // the issue's original framing — see DR-023 §UC-3 amendment + the
335
+ // checkpoint.ts file-header for the reframe rationale). Tool writes
336
+ // a session-handoff file to the agent's per-project memory directory
337
+ // under `~/.claude/projects/<encoded-cwd>/memory/`. Failure-mode is
338
+ // observational + non-blocking: any error path returns
339
+ // `{written: false, reason}` and `isError: false` so PreCompact
340
+ // proceeds (a missed checkpoint is recoverable; blocking compaction
341
+ // is not).
342
+ const { checkpointToMemory, CheckpointToMemoryInputSchema, CheckpointToMemoryOutputSchema, } = await import('./checkpoint.js');
343
+ const checkpointDeps = {
344
+ selfAgentName: config.agentName,
345
+ logger,
346
+ };
347
+ mcp.mcp.registerTool('checkpoint_to_memory', {
348
+ description: 'Write a session-handoff checkpoint to the agent\'s per-project ' +
349
+ 'memory directory. Invoked by the PreCompact hook (DR-023 UC-3) before context ' +
350
+ 'compaction so the next session can read structured handoff state via the ' +
351
+ 'MEMORY.md index pattern. Failure-mode is observational + non-blocking — write ' +
352
+ 'failures log + return `{written: false, reason}` to the hook without raising; ' +
353
+ 'compaction always proceeds.',
354
+ inputSchema: CheckpointToMemoryInputSchema,
355
+ outputSchema: CheckpointToMemoryOutputSchema,
356
+ }, async (input) => {
357
+ const result = await checkpointToMemory(checkpointDeps, input);
358
+ return {
359
+ content: [{ type: 'text', text: JSON.stringify(result) }],
360
+ structuredContent: { ...result },
361
+ // Per DR-023 §UC-3: PreCompact is best-effort. Even on
362
+ // write-failure, surface isError:false so compaction proceeds.
363
+ // The `reason` field in structured output is sufficient signal
364
+ // for LLM self-correction in subsequent turns.
365
+ isError: false,
366
+ };
367
+ });
262
368
  // P1: Connect MCP channel
263
369
  await mcp.connect();
264
370
  // P1: Bind port
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { Logger } from '@groundnuty/macf-core';
2
+ /**
3
+ * Token cache TTL — 50 minutes. 10-minute safety margin under the 1-hour
4
+ * installation-token TTL (GitHub App contract). Calls within this window
5
+ * return the cached token; calls past this window mint fresh.
6
+ *
7
+ * Exposed as a const so tests can assert the value without re-deriving.
8
+ */
9
+ export declare const REFRESH_AGE_MS: number;
10
+ export interface TokenRefresherDeps {
11
+ /** Optional override for the canonical helper-script path; tests inject. */
12
+ readonly tokenScriptPath?: string;
13
+ /** Optional clock injection for tests. */
14
+ readonly clock?: {
15
+ readonly now: () => number;
16
+ };
17
+ /** Optional execFile injection for tests (avoids spawning real shell). */
18
+ readonly exec?: (cmd: string, args: readonly string[], opts?: {
19
+ readonly encoding?: 'utf-8';
20
+ }) => Promise<{
21
+ readonly stdout: string;
22
+ readonly stderr: string;
23
+ }>;
24
+ /** Logger for diagnostic output. */
25
+ readonly logger: Logger;
26
+ }
27
+ export interface GetRefreshedTokenOptions {
28
+ /** Force a fresh mint regardless of cache age (used on 401 retry). */
29
+ readonly forceRefresh?: boolean;
30
+ }
31
+ export interface TokenRefresher {
32
+ /**
33
+ * Returns a current token, minting fresh if the cache is older than
34
+ * `REFRESH_AGE_MS` or `forceRefresh: true`. Throws on mint failure;
35
+ * never returns a stale token silently.
36
+ */
37
+ readonly getRefreshedToken: (opts?: GetRefreshedTokenOptions) => Promise<string>;
38
+ }
39
+ /**
40
+ * Create a token refresher with an in-process cache. Caller invokes
41
+ * `getRefreshedToken()` per-API-call; the cache returns the same token
42
+ * for ~50 min then mints fresh. On 401, caller invokes with
43
+ * `forceRefresh: true` to bypass cache.
44
+ */
45
+ export declare function createTokenRefresher(deps: TokenRefresherDeps): TokenRefresher;
46
+ //# sourceMappingURL=token-refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-refresh.d.ts","sourceRoot":"","sources":["../src/token-refresh.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAIpD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,QAAiB,CAAC;AAE7C,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAA;KAAE,CAAC;IAChD,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,CAAC,EAAE,CACd,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KACnC,OAAO,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,sEAAsE;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,EAAE,CAC1B,IAAI,CAAC,EAAE,wBAAwB,KAC5B,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB;AAsHD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CAyC7E"}
@@ -0,0 +1,202 @@
1
+ /**
2
+ * In-runner GH_TOKEN refresh helper (macf#317).
3
+ *
4
+ * Bot installation tokens have a **1-hour TTL by design** (GitHub App contract).
5
+ * `claude.sh` mints a fresh token at session start + exports `GH_TOKEN`; the
6
+ * channel-server inherits that token via `process.env`. Without an in-runner
7
+ * refresh, every gh-API-using handler 401s after ~60 minutes of session
8
+ * uptime. Witnessed 2026-05-01 ~14:30Z on cv-architect's Stop hook at
9
+ * ~67min uptime — the operator-witnessed incident motivating this module.
10
+ *
11
+ * Architecture: an in-process token cache. Holds {token, mintedAt}; on
12
+ * every `getRefreshedToken()` call, returns cached if `age < REFRESH_AGE_MS`
13
+ * (50min — 10min safety margin under 1hr TTL); otherwise mints fresh via
14
+ * the canonical `macf-gh-token.sh` helper. On a 401 from a downstream API
15
+ * call, the caller re-invokes with `forceRefresh: true` to bypass cache.
16
+ *
17
+ * Critical: refreshed tokens stay **in-process**. We never `process.env.GH_TOKEN
18
+ * = ...` or pass tokens to child processes via env mutation — a stale
19
+ * `GH_TOKEN` env var elsewhere is a separate hazard surface. The wrapper-
20
+ * around-GitHub-client pattern (refresh-aware-client.ts) holds the fresh
21
+ * token in closure, never leaks it.
22
+ *
23
+ * Failure semantics: refresh failures (clock drift, missing key, rotated
24
+ * key, deleted App) throw with diagnostic. We do NOT silently fall back
25
+ * to the stale env-var token — that would extend the silent-fallback class
26
+ * (Instance 1, gh-token attribution traps) by re-introducing a quiet path
27
+ * where ops continue under wrong identity. Loud failure preserves the
28
+ * fail-loud invariant.
29
+ *
30
+ * Sister-shape reference: macf-testbed#135 (closed/deferred — sweep-
31
+ * harness-side refresh between iterations). Same hazard class, different
32
+ * surface; this module is the channel-server-side response.
33
+ *
34
+ * Helper-script discoverability: in-runner refresh requires the canonical
35
+ * `macf-gh-token.sh` to be invokable. Resolution order:
36
+ * 1. Caller-injected `tokenScriptPath` (testing override)
37
+ * 2. `$MACF_WORKSPACE_DIR/.claude/scripts/macf-gh-token.sh` (canonical
38
+ * consumer-fleet path; set by claude.sh per macf#161)
39
+ *
40
+ * Falls back to `generateToken()` from macf-core if the helper script isn't
41
+ * resolvable (substrate / non-init'd workspaces). The core helper handles
42
+ * the same APP_ID/INSTALL_ID/KEY_PATH env path; only difference is fail-
43
+ * loud diagnostics live in the shell wrapper.
44
+ *
45
+ * SAFETY NOTE: this module uses `execFile` (NOT `exec`), so arguments are
46
+ * passed as an argv array — no shell interpretation, no injection surface
47
+ * for the operator-controlled APP_ID/INSTALL_ID/KEY_PATH values.
48
+ */
49
+ import { execFile } from 'node:child_process';
50
+ import { promisify } from 'node:util';
51
+ import { existsSync } from 'node:fs';
52
+ import { join } from 'node:path';
53
+ import { generateToken } from '@groundnuty/macf-core';
54
+ const execFileAsync = promisify(execFile);
55
+ /**
56
+ * Token cache TTL — 50 minutes. 10-minute safety margin under the 1-hour
57
+ * installation-token TTL (GitHub App contract). Calls within this window
58
+ * return the cached token; calls past this window mint fresh.
59
+ *
60
+ * Exposed as a const so tests can assert the value without re-deriving.
61
+ */
62
+ export const REFRESH_AGE_MS = 50 * 60 * 1000;
63
+ /**
64
+ * Locate the canonical token helper script. Returns the resolved path or
65
+ * `null` if not findable; caller falls back to `generateToken()`.
66
+ */
67
+ function resolveScriptPath(override) {
68
+ if (override !== undefined)
69
+ return override;
70
+ const workspaceDir = process.env['MACF_WORKSPACE_DIR'];
71
+ if (workspaceDir === undefined || workspaceDir === '')
72
+ return null;
73
+ const candidate = join(workspaceDir, '.claude', 'scripts', 'macf-gh-token.sh');
74
+ return existsSync(candidate) ? candidate : null;
75
+ }
76
+ /**
77
+ * Validate token shape — must be a non-empty `ghs_*` installation token.
78
+ * Mirrors the canonical fail-loud prefix check from `macf-gh-token.sh`.
79
+ * Defensive against scenarios where the helper exits 0 but produces empty
80
+ * stdout (shouldn't happen — the helper has its own check — but this is
81
+ * the result-invariant assertion at the channel-server boundary).
82
+ */
83
+ function assertTokenShape(token) {
84
+ if (token === '') {
85
+ throw new Error('Token refresh failed: empty token returned');
86
+ }
87
+ if (!token.startsWith('ghs_')) {
88
+ const prefix = token.slice(0, 4);
89
+ throw new Error(`Token refresh failed: bad prefix '${prefix}' — expected 'ghs_' (installation token). ` +
90
+ 'Refusing to use non-installation token to avoid mis-attribution.');
91
+ }
92
+ }
93
+ /**
94
+ * Mint a fresh token. Tries the canonical shell helper first (fail-loud
95
+ * with diagnostics) and falls back to `generateToken()` from core.
96
+ */
97
+ async function mintToken(deps) {
98
+ const scriptPath = resolveScriptPath(deps.tokenScriptPath);
99
+ if (scriptPath !== null) {
100
+ const appId = process.env['APP_ID'];
101
+ const installId = process.env['INSTALL_ID'];
102
+ const keyPath = process.env['KEY_PATH'];
103
+ if (appId === undefined || appId === '' ||
104
+ installId === undefined || installId === '' ||
105
+ keyPath === undefined || keyPath === '') {
106
+ throw new Error('Token refresh failed: APP_ID/INSTALL_ID/KEY_PATH env vars required ' +
107
+ 'for in-runner refresh; channel-server inherits these from claude.sh. ' +
108
+ 'If unset, the claude.sh launcher did not set them — check workspace ' +
109
+ 'config (.claude/settings.local.json) and macf doctor.');
110
+ }
111
+ const runExec = deps.exec ?? execFileAsync;
112
+ try {
113
+ const { stdout } = await runExec(scriptPath, ['--app-id', appId, '--install-id', installId, '--key', keyPath], { encoding: 'utf-8' });
114
+ const token = stdout.trim();
115
+ assertTokenShape(token);
116
+ return token;
117
+ }
118
+ catch (err) {
119
+ const msg = err instanceof Error ? err.message : String(err);
120
+ throw new Error(`Token refresh failed via ${scriptPath}: ${msg}. ` +
121
+ 'Common causes: clock drift (timedatectl status), wrong/rotated key, ' +
122
+ 'or wrong APP_ID/INSTALL_ID. Channel-server WILL NOT silently fall ' +
123
+ 'back to the stale inherited GH_TOKEN — fail loud per silent-fallback-' +
124
+ 'hazards.md Instance 1 (expiry sub-case).', { cause: err });
125
+ }
126
+ }
127
+ // Fallback: core's generateToken (no helper-script available — substrate
128
+ // / non-init'd workspaces). Same APP_ID/INSTALL_ID/KEY_PATH env path,
129
+ // less verbose diagnostics.
130
+ try {
131
+ // generateToken's precedence is GH_TOKEN env > TokenSource > env vars.
132
+ // When GH_TOKEN is set (typical), generateToken returns it as-is —
133
+ // which is the very stale token we're trying to refresh. To force a
134
+ // fresh mint, we'd need to either (a) clear GH_TOKEN before the call
135
+ // or (b) call into the gh CLI directly. Operators in this fallback
136
+ // path (no MACF_WORKSPACE_DIR + no helper script) get the env-var
137
+ // token unchanged; expiry hazard remains. The forceRefresh-on-401
138
+ // retry path still attempts a fresh mint — but if the env var keeps
139
+ // winning, the retry returns the same stale token. Documented for
140
+ // operators: in-runner refresh requires either the helper script or
141
+ // unsetting GH_TOKEN at claude-sh level. Substrate / non-init'd
142
+ // workspaces should ensure GH_TOKEN is not set OR ship the helper
143
+ // script.
144
+ const token = await generateToken();
145
+ assertTokenShape(token);
146
+ return token;
147
+ }
148
+ catch (err) {
149
+ const msg = err instanceof Error ? err.message : String(err);
150
+ throw new Error(`Token refresh failed via generateToken fallback: ${msg}. ` +
151
+ 'No MACF_WORKSPACE_DIR set + no APP_ID/INSTALL_ID/KEY_PATH env vars. ' +
152
+ 'In-runner refresh is not viable in this configuration.', { cause: err });
153
+ }
154
+ }
155
+ /**
156
+ * Create a token refresher with an in-process cache. Caller invokes
157
+ * `getRefreshedToken()` per-API-call; the cache returns the same token
158
+ * for ~50 min then mints fresh. On 401, caller invokes with
159
+ * `forceRefresh: true` to bypass cache.
160
+ */
161
+ export function createTokenRefresher(deps) {
162
+ const clock = deps.clock ?? { now: () => Date.now() };
163
+ let cachedToken = null;
164
+ let mintedAt = 0;
165
+ let inFlight = null;
166
+ async function mintAndCache() {
167
+ // De-duplicate concurrent refresh attempts: if one mint is already
168
+ // in-flight, return the same promise. Prevents N concurrent gh-token-
169
+ // generate spawns when N concurrent /notify handlers all hit the
170
+ // refresh boundary simultaneously.
171
+ if (inFlight !== null)
172
+ return inFlight;
173
+ inFlight = (async () => {
174
+ try {
175
+ const token = await mintToken(deps);
176
+ cachedToken = token;
177
+ mintedAt = clock.now();
178
+ deps.logger.info('token_refreshed', {
179
+ age_ms: '0',
180
+ source: deps.tokenScriptPath ?? 'workspace_helper',
181
+ });
182
+ return token;
183
+ }
184
+ finally {
185
+ inFlight = null;
186
+ }
187
+ })();
188
+ return inFlight;
189
+ }
190
+ return {
191
+ async getRefreshedToken(opts) {
192
+ const force = opts?.forceRefresh === true;
193
+ const now = clock.now();
194
+ const age = now - mintedAt;
195
+ if (!force && cachedToken !== null && age < REFRESH_AGE_MS) {
196
+ return cachedToken;
197
+ }
198
+ return mintAndCache();
199
+ },
200
+ };
201
+ }
202
+ //# sourceMappingURL=token-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-refresh.js","sourceRoot":"","sources":["../src/token-refresh.ts"],"names":[],"mappings":"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"}
package/dist/tracing.d.ts CHANGED
@@ -13,6 +13,7 @@ export declare const SpanNames: {
13
13
  readonly CertsVerifyChallenge: "macf.certs.verify_challenge";
14
14
  readonly CertsSign: "macf.certs.sign";
15
15
  readonly ToolNotifyPeer: "macf.tool.notify_peer";
16
+ readonly ToolCheckpointToMemory: "macf.tool.checkpoint_to_memory";
16
17
  };
17
18
  /** MACF-specific attribute keys (not covered by OTEL semconv). */
18
19
  export declare const Attr: {
@@ -26,6 +27,9 @@ export declare const Attr: {
26
27
  readonly NotifyEvent: "macf.notify.event";
27
28
  readonly PeersAttempted: "macf.notify.peers_attempted";
28
29
  readonly PeersDelivered: "macf.notify.peers_delivered";
30
+ readonly CheckpointTrigger: "macf.checkpoint.trigger";
31
+ readonly CheckpointWritten: "macf.checkpoint.written";
32
+ readonly CheckpointDeduplicated: "macf.checkpoint.deduplicated";
29
33
  };
30
34
  /** GenAI semconv keys (experimental in v1.36+). */
31
35
  export declare const GenAiAttr: {
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,gEAAgE;AAChE,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,uDAAuD;AACvD,eAAO,MAAM,SAAS;;;;;;;;;;CAeZ,CAAC;AAEX,kEAAkE;AAClE,eAAO,MAAM,IAAI;;;;;;;;;;;CAeP,CAAC;AAEX,mDAAmD;AACnD,eAAO,MAAM,SAAS;;;;;CAKZ,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CA0B9E"}
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,gEAAgE;AAChE,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,uDAAuD;AACvD,eAAO,MAAM,SAAS;;;;;;;;;;;CAmBZ,CAAC;AAEX,kEAAkE;AAClE,eAAO,MAAM,IAAI;;;;;;;;;;;;;;CAqBP,CAAC;AAEX,mDAAmD;AACnD,eAAO,MAAM,SAAS;;;;;CAKZ,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CA0B9E"}
package/dist/tracing.js CHANGED
@@ -38,6 +38,10 @@ export const SpanNames = {
38
38
  // parent-child relationship to receiver's NotifyReceived span via
39
39
  // W3C traceparent propagation.
40
40
  ToolNotifyPeer: 'macf.tool.notify_peer',
41
+ // macf#271: PreCompact-driven checkpoint_to_memory span. INTERNAL-kind
42
+ // (purely local filesystem write). Attributes: trigger (manual|auto),
43
+ // written (bool), deduplicated (bool). DR-023 §UC-3 telemetry pattern.
44
+ ToolCheckpointToMemory: 'macf.tool.checkpoint_to_memory',
41
45
  };
42
46
  /** MACF-specific attribute keys (not covered by OTEL semconv). */
43
47
  export const Attr = {
@@ -55,6 +59,12 @@ export const Attr = {
55
59
  NotifyEvent: 'macf.notify.event',
56
60
  PeersAttempted: 'macf.notify.peers_attempted',
57
61
  PeersDelivered: 'macf.notify.peers_delivered',
62
+ // macf#271: checkpoint_to_memory span attributes. Surface PreCompact
63
+ // trigger source (manual / auto) + outcome (written, deduplicated)
64
+ // for DR-023 UC-3 telemetry.
65
+ CheckpointTrigger: 'macf.checkpoint.trigger',
66
+ CheckpointWritten: 'macf.checkpoint.written',
67
+ CheckpointDeduplicated: 'macf.checkpoint.deduplicated',
58
68
  };
59
69
  /** GenAI semconv keys (experimental in v1.36+). */
60
70
  export const GenAiAttr = {
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAI3C,gEAAgE;AAChE,MAAM,UAAU,SAAS;IACvB,OAAO,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,uDAAuD;AACvD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,cAAc,EAAE,6BAA6B;IAC7C,OAAO,EAAE,sBAAsB;IAC/B,eAAe,EAAE,sBAAsB;IACvC,qBAAqB,EAAE,6BAA6B;IACpD,OAAO,EAAE,eAAe;IACxB,eAAe,EAAE,wBAAwB;IACzC,oBAAoB,EAAE,6BAA6B;IACnD,SAAS,EAAE,iBAAiB;IAC5B,kEAAkE;IAClE,+DAA+D;IAC/D,8DAA8D;IAC9D,kEAAkE;IAClE,+BAA+B;IAC/B,cAAc,EAAE,uBAAuB;CAC/B,CAAC;AAEX,kEAAkE;AAClE,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,UAAU,EAAE,kBAAkB;IAC9B,WAAW,EAAE,mBAAmB;IAChC,SAAS,EAAE,iBAAiB;IAC5B,QAAQ,EAAE,gBAAgB;IAC1B,UAAU,EAAE,kBAAkB;IAC9B,WAAW,EAAE,mBAAmB;IAChC,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,gCAAgC;IAChC,YAAY,EAAE,oBAAoB;IAClC,WAAW,EAAE,mBAAmB;IAChC,cAAc,EAAE,6BAA6B;IAC7C,cAAc,EAAE,6BAA6B;CACrC,CAAC;AAEX,mDAAmD;AACnD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,SAAS,EAAE,mBAAmB;IAC9B,OAAO,EAAE,iBAAiB;IAC1B,aAAa,EAAE,uBAAuB;IACtC,MAAM,EAAE,eAAe;CACf,CAAC;AAEX;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAA2B;IACpE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,cAAc,CAAC;QACxB,KAAK,cAAc;YACjB,OAAO,SAAS,CAAC;QACnB,KAAK,eAAe,CAAC;QACrB,KAAK,eAAe;YAClB,OAAO,QAAQ,CAAC;QAClB,kEAAkE;QAClE,oEAAoE;QACpE,oEAAoE;QACpE,sEAAsE;QACtE,8DAA8D;QAC9D,KAAK,mBAAmB;YACtB,OAAO,aAAa,CAAC;QACvB,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,gEAAgE;QAChE,mEAAmE;QACnE,kEAAkE;QAClE,+CAA+C;QAC/C,KAAK,iBAAiB;YACpB,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAI3C,gEAAgE;AAChE,MAAM,UAAU,SAAS;IACvB,OAAO,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,uDAAuD;AACvD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,cAAc,EAAE,6BAA6B;IAC7C,OAAO,EAAE,sBAAsB;IAC/B,eAAe,EAAE,sBAAsB;IACvC,qBAAqB,EAAE,6BAA6B;IACpD,OAAO,EAAE,eAAe;IACxB,eAAe,EAAE,wBAAwB;IACzC,oBAAoB,EAAE,6BAA6B;IACnD,SAAS,EAAE,iBAAiB;IAC5B,kEAAkE;IAClE,+DAA+D;IAC/D,8DAA8D;IAC9D,kEAAkE;IAClE,+BAA+B;IAC/B,cAAc,EAAE,uBAAuB;IACvC,uEAAuE;IACvE,sEAAsE;IACtE,uEAAuE;IACvE,sBAAsB,EAAE,gCAAgC;CAChD,CAAC;AAEX,kEAAkE;AAClE,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,UAAU,EAAE,kBAAkB;IAC9B,WAAW,EAAE,mBAAmB;IAChC,SAAS,EAAE,iBAAiB;IAC5B,QAAQ,EAAE,gBAAgB;IAC1B,UAAU,EAAE,kBAAkB;IAC9B,WAAW,EAAE,mBAAmB;IAChC,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,gCAAgC;IAChC,YAAY,EAAE,oBAAoB;IAClC,WAAW,EAAE,mBAAmB;IAChC,cAAc,EAAE,6BAA6B;IAC7C,cAAc,EAAE,6BAA6B;IAC7C,qEAAqE;IACrE,mEAAmE;IACnE,6BAA6B;IAC7B,iBAAiB,EAAE,yBAAyB;IAC5C,iBAAiB,EAAE,yBAAyB;IAC5C,sBAAsB,EAAE,8BAA8B;CAC9C,CAAC;AAEX,mDAAmD;AACnD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,SAAS,EAAE,mBAAmB;IAC9B,OAAO,EAAE,iBAAiB;IAC1B,aAAa,EAAE,uBAAuB;IACtC,MAAM,EAAE,eAAe;CACf,CAAC;AAEX;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAA2B;IACpE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,cAAc,CAAC;QACxB,KAAK,cAAc;YACjB,OAAO,SAAS,CAAC;QACnB,KAAK,eAAe,CAAC;QACrB,KAAK,eAAe;YAClB,OAAO,QAAQ,CAAC;QAClB,kEAAkE;QAClE,oEAAoE;QACpE,oEAAoE;QACpE,sEAAsE;QACtE,8DAA8D;QAC9D,KAAK,mBAAmB;YACtB,OAAO,aAAa,CAAC;QACvB,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,gEAAgE;QAChE,mEAAmE;QACnE,kEAAkE;QAClE,+CAA+C;QAC/C,KAAK,iBAAiB;YACpB,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundnuty/macf-channel-server",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "MCP channel server for the Multi-Agent Coordination Framework. HTTPS + mTLS endpoint agents connect to for inter-agent messaging (notify), CI-completion routing, and /sign certificate issuance. Invoked by Claude Code's plugin via `npx -y @groundnuty/macf-channel-server` per DR-022.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,7 +32,7 @@
32
32
  "test:e2e": "vitest run --config vitest.e2e.config.ts"
33
33
  },
34
34
  "dependencies": {
35
- "@groundnuty/macf-core": "0.2.10",
35
+ "@groundnuty/macf-core": "0.2.12",
36
36
  "@modelcontextprotocol/sdk": "~1.29.0",
37
37
  "@opentelemetry/api": "1.9.1",
38
38
  "@opentelemetry/exporter-metrics-otlp-proto": "0.215.0",