@primitive.ai/prim 0.1.0-alpha.8 → 0.1.0-alpha.80

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 (45) hide show
  1. package/README.md +295 -37
  2. package/SKILL.md +269 -0
  3. package/dist/chunk-2KW7DVFJ.js +1400 -0
  4. package/dist/chunk-3EEFL6NJ.js +529 -0
  5. package/dist/chunk-4A2XB2PC.js +310 -0
  6. package/dist/chunk-4GLVJLEQ.js +55 -0
  7. package/dist/chunk-BQUJ4AFJ.js +771 -0
  8. package/dist/chunk-CUE2LKLR.js +155 -0
  9. package/dist/chunk-EZXEWFDG.js +210 -0
  10. package/dist/chunk-FI3LIRCB.js +262 -0
  11. package/dist/chunk-KKP2NYZ3.js +505 -0
  12. package/dist/chunk-LEMX3IOG.js +79 -0
  13. package/dist/chunk-LMRYZL3D.js +209 -0
  14. package/dist/chunk-OUXH3UEP.js +247 -0
  15. package/dist/chunk-RO3F6JYV.js +126 -0
  16. package/dist/chunk-TJTVHDJ5.js +321 -0
  17. package/dist/chunk-WB7AUZ4C.js +52 -0
  18. package/dist/chunk-XGADQPCI.js +26 -0
  19. package/dist/chunk-YWO7O6QG.js +327 -0
  20. package/dist/chunk-Z7YPFWAZ.js +917 -0
  21. package/dist/chunk-ZOSNEO5N.js +77 -0
  22. package/dist/daemon/server.js +15329 -0
  23. package/dist/hook-runtime/daemon/server.js +15329 -0
  24. package/dist/hook-runtime/hooks/post-commit.js +1128 -0
  25. package/dist/hook-runtime/hooks/post-rewrite.js +1148 -0
  26. package/dist/hook-runtime/hooks/post-tool-use.js +7586 -0
  27. package/dist/hook-runtime/hooks/pre-commit.js +1314 -0
  28. package/dist/hook-runtime/hooks/pre-tool-use.js +7096 -0
  29. package/dist/hook-runtime/hooks/prim-hook.js +7691 -0
  30. package/dist/hook-runtime/hooks/session-end.js +495 -0
  31. package/dist/hook-runtime/hooks/session-start.js +11313 -0
  32. package/dist/hook-runtime/index.js +44686 -0
  33. package/dist/hook-runtime/statusline-main.js +765 -0
  34. package/dist/hooks/post-commit.js +179 -0
  35. package/dist/hooks/post-rewrite.js +197 -0
  36. package/dist/hooks/post-tool-use.js +250 -0
  37. package/dist/hooks/pre-commit.js +21 -74
  38. package/dist/hooks/pre-tool-use.js +345 -0
  39. package/dist/hooks/prim-hook.js +195 -0
  40. package/dist/hooks/session-end.js +70 -0
  41. package/dist/hooks/session-start.js +299 -0
  42. package/dist/index.js +20971 -519
  43. package/dist/statusline-main.js +765 -0
  44. package/package.json +35 -13
  45. package/dist/chunk-3APLWTLB.js +0 -165
@@ -0,0 +1,505 @@
1
+ import {
2
+ formatStatusline,
3
+ packageVersion
4
+ } from "./chunk-4A2XB2PC.js";
5
+ import {
6
+ decisionIngestionStatus,
7
+ gitToplevel,
8
+ repositoryBindingState
9
+ } from "./chunk-TJTVHDJ5.js";
10
+ import {
11
+ getSiteUrl,
12
+ isSessionEnded,
13
+ terminalSafeText,
14
+ withFileLock
15
+ } from "./chunk-Z7YPFWAZ.js";
16
+ import {
17
+ daemonPrincipalsMatch,
18
+ daemonRequest,
19
+ resolveDaemonPrincipal
20
+ } from "./chunk-CUE2LKLR.js";
21
+ import {
22
+ primConfigDirectory
23
+ } from "./chunk-3EEFL6NJ.js";
24
+
25
+ // src/hooks/codex-context.ts
26
+ import { createHash, randomBytes as randomBytes2 } from "crypto";
27
+ import {
28
+ chmodSync,
29
+ existsSync,
30
+ mkdirSync,
31
+ readFileSync,
32
+ readdirSync,
33
+ renameSync,
34
+ rmSync,
35
+ statSync,
36
+ unlinkSync,
37
+ writeFileSync
38
+ } from "fs";
39
+ import { join } from "path";
40
+
41
+ // src/daemon/decision-digest-cache.ts
42
+ import { randomBytes } from "crypto";
43
+ var DECISION_DIGEST_CACHE_LIMIT = 100;
44
+ var DECISION_DIGEST_CACHE_WINDOW = "24h";
45
+ var DECISION_DIGEST_CACHE_PATH = `/api/cli/decisions/recent?limit=${String(DECISION_DIGEST_CACHE_LIMIT)}&since=${DECISION_DIGEST_CACHE_WINDOW}`;
46
+ var DECISION_DRAFT_DIGEST_CACHE_PATH = `${DECISION_DIGEST_CACHE_PATH}&drafts=true`;
47
+ var SAFE_PRIVATE_DRAFT_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
48
+ function isActionablePrivateDraft(row) {
49
+ if (typeof row !== "object" || row === null || Array.isArray(row)) return false;
50
+ const candidate = row;
51
+ return typeof candidate.id === "string" && SAFE_PRIVATE_DRAFT_ID.test(candidate.id) && typeof candidate.intent === "string" && candidate.authorIsSelf === true && candidate.stage === "draft" && (candidate.intentKind === void 0 || candidate.intentKind === "change");
52
+ }
53
+
54
+ // src/hooks/codex-context.ts
55
+ var CODEX_CONTEXT_TIMEOUT_MS = 250;
56
+ var CODEX_DIGEST_LIMIT = DECISION_DIGEST_CACHE_LIMIT;
57
+ var CODEX_DIGEST_VISIBLE_CAP = 3;
58
+ var CODEX_DIGEST_OVERLAP_MS = 6e4;
59
+ var CODEX_DIGEST_MAX_SEEN_IDS = 128;
60
+ var CODEX_DRAFT_DIGEST_MAX_SEEN_IDS = 4096;
61
+ var CODEX_DIGEST_STATE_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
62
+ var CODEX_DIGEST_STATE_MAX_FILES = 256;
63
+ var STATE_VERSION = 1;
64
+ var STATE_DIRECTORY = ["codex", "decision-digests"];
65
+ var DRAFT_STATE_VERSION = 2;
66
+ var DRAFT_STATE_DIRECTORY = ["codex", "decision-draft-deliveries"];
67
+ var NO_CURSOR = -1;
68
+ function hasVisibleCodexMessage(output) {
69
+ if (typeof output !== "object" || output === null || Array.isArray(output)) return false;
70
+ const candidate = output;
71
+ return Boolean(
72
+ candidate.systemMessage?.trim() || candidate.hookSpecificOutput?.permissionDecisionReason?.trim() || candidate.hookSpecificOutput?.additionalContext?.trim()
73
+ );
74
+ }
75
+ function appendContext(existing, context) {
76
+ return existing ? `${existing}
77
+
78
+ ${context}` : context;
79
+ }
80
+ function appendCodexContext(output, context) {
81
+ if (!context) return output;
82
+ const candidate = output;
83
+ const next = { ...output };
84
+ if (candidate.systemMessage) {
85
+ next.systemMessage = appendContext(candidate.systemMessage, context);
86
+ } else if (candidate.hookSpecificOutput?.permissionDecisionReason) {
87
+ next.systemMessage = context;
88
+ } else if (candidate.hookSpecificOutput?.additionalContext) {
89
+ next.hookSpecificOutput = {
90
+ ...candidate.hookSpecificOutput,
91
+ additionalContext: appendContext(candidate.hookSpecificOutput.additionalContext, context)
92
+ };
93
+ } else {
94
+ next.systemMessage = context;
95
+ }
96
+ return next;
97
+ }
98
+ function stateRoot() {
99
+ return join(primConfigDirectory(), ...STATE_DIRECTORY);
100
+ }
101
+ function draftStateRoot() {
102
+ return join(primConfigDirectory(), ...DRAFT_STATE_DIRECTORY);
103
+ }
104
+ function workspaceFor(cwd) {
105
+ try {
106
+ return gitToplevel(cwd) ?? cwd;
107
+ } catch {
108
+ return cwd;
109
+ }
110
+ }
111
+ function statePath(args) {
112
+ const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}`;
113
+ const key = createHash("sha256").update(identity).digest("hex");
114
+ return join(stateRoot(), `${key}.json`);
115
+ }
116
+ function draftStatePath(args) {
117
+ const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}\0${args.principalId}\0${args.organizationId}\0${args.credentialFingerprint}`;
118
+ const key = createHash("sha256").update(identity).digest("hex");
119
+ return join(draftStateRoot(), `${key}.json`);
120
+ }
121
+ function parseState(value) {
122
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
123
+ const record = value;
124
+ if (record.version !== STATE_VERSION || typeof record.sessionId !== "string" || typeof record.siteUrl !== "string" || typeof record.workspace !== "string" || typeof record.startedAt !== "number" || typeof record.watermarkMs !== "number" || !Array.isArray(record.seenIds) || !record.seenIds.every((id) => typeof id === "string") || typeof record.updatedAt !== "number") {
125
+ return void 0;
126
+ }
127
+ return {
128
+ version: STATE_VERSION,
129
+ sessionId: record.sessionId,
130
+ siteUrl: record.siteUrl,
131
+ workspace: record.workspace,
132
+ startedAt: record.startedAt,
133
+ watermarkMs: record.watermarkMs,
134
+ seenIds: record.seenIds.slice(-CODEX_DIGEST_MAX_SEEN_IDS),
135
+ lastReport: typeof record.lastReport === "string" ? record.lastReport : void 0,
136
+ updatedAt: record.updatedAt
137
+ };
138
+ }
139
+ function parseDraftState(value) {
140
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
141
+ const record = value;
142
+ if (record.version !== DRAFT_STATE_VERSION || typeof record.sessionId !== "string" || typeof record.siteUrl !== "string" || typeof record.workspace !== "string" || typeof record.principalId !== "string" || typeof record.organizationId !== "string" || typeof record.credentialFingerprint !== "string" || !Array.isArray(record.seenDraftIds) || !record.seenDraftIds.every((id) => typeof id === "string") || typeof record.updatedAt !== "number") {
143
+ return void 0;
144
+ }
145
+ return {
146
+ version: DRAFT_STATE_VERSION,
147
+ sessionId: record.sessionId,
148
+ siteUrl: record.siteUrl,
149
+ workspace: record.workspace,
150
+ principalId: record.principalId,
151
+ organizationId: record.organizationId,
152
+ credentialFingerprint: record.credentialFingerprint,
153
+ seenDraftIds: record.seenDraftIds.slice(-CODEX_DRAFT_DIGEST_MAX_SEEN_IDS),
154
+ updatedAt: record.updatedAt
155
+ };
156
+ }
157
+ function readState(path) {
158
+ if (!existsSync(path)) return void 0;
159
+ try {
160
+ return parseState(JSON.parse(readFileSync(path, "utf8")));
161
+ } catch {
162
+ return void 0;
163
+ }
164
+ }
165
+ function readDraftState(path) {
166
+ if (!existsSync(path)) return void 0;
167
+ try {
168
+ return parseDraftState(JSON.parse(readFileSync(path, "utf8")));
169
+ } catch {
170
+ return void 0;
171
+ }
172
+ }
173
+ function writeStateFile(path, directory, state) {
174
+ mkdirSync(directory, { recursive: true, mode: 448 });
175
+ chmodSync(directory, 448);
176
+ const temporary = join(
177
+ directory,
178
+ `.${path.slice(directory.length + 1)}.tmp-${process.pid}-${randomBytes2(8).toString("hex")}`
179
+ );
180
+ try {
181
+ writeFileSync(temporary, `${JSON.stringify(state)}
182
+ `, {
183
+ encoding: "utf8",
184
+ mode: 384,
185
+ flag: "wx"
186
+ });
187
+ chmodSync(temporary, 384);
188
+ renameSync(temporary, path);
189
+ } finally {
190
+ try {
191
+ unlinkSync(temporary);
192
+ } catch {
193
+ }
194
+ }
195
+ }
196
+ function cleanupStateFiles(directory, now) {
197
+ if (!existsSync(directory)) return;
198
+ let names;
199
+ try {
200
+ names = readdirSync(directory);
201
+ } catch {
202
+ return;
203
+ }
204
+ const files = [];
205
+ const residue = [];
206
+ for (const name of names) {
207
+ const path = join(directory, name);
208
+ try {
209
+ const mtimeMs = statSync(path).mtimeMs;
210
+ if (name.endsWith(".json")) {
211
+ files.push({ path, mtimeMs });
212
+ } else if ((name.includes(".json.tmp-") || name.endsWith(".json.lock")) && now - mtimeMs > CODEX_DIGEST_STATE_RETENTION_MS) {
213
+ residue.push(path);
214
+ }
215
+ } catch {
216
+ }
217
+ }
218
+ const stale = files.filter((file) => now - file.mtimeMs > CODEX_DIGEST_STATE_RETENTION_MS);
219
+ const excess = files.filter((file) => !stale.some((candidate) => candidate.path === file.path)).sort((a, b) => a.mtimeMs - b.mtimeMs).slice(0, Math.max(0, files.length - CODEX_DIGEST_STATE_MAX_FILES));
220
+ for (const file of [...stale, ...excess]) {
221
+ try {
222
+ unlinkSync(file.path);
223
+ } catch {
224
+ }
225
+ }
226
+ for (const path of residue) {
227
+ try {
228
+ if (now - statSync(path).mtimeMs > CODEX_DIGEST_STATE_RETENTION_MS) {
229
+ rmSync(path, { recursive: true, force: true });
230
+ }
231
+ } catch {
232
+ }
233
+ }
234
+ }
235
+ function isChangeDecision(row) {
236
+ return row.intentKind === void 0 || row.intentKind === "change";
237
+ }
238
+ function safeInline(value, fallback) {
239
+ const clean = terminalSafeText(value ?? "").replace(/\s+/gu, " ").trim();
240
+ if (!clean) return fallback;
241
+ return clean.length <= 240 ? clean : `${clean.slice(0, 239)}\u2026`;
242
+ }
243
+ function renderDecisionDigest(rows, options = {}) {
244
+ if (rows.length === 0) return void 0;
245
+ const visible = rows.slice(0, CODEX_DIGEST_VISIBLE_CAP).map((row) => {
246
+ const author = safeInline(row.authorName, "(unknown)").replace(/[;;;‒-―−]/gu, "-");
247
+ const intent = safeInline(row.intent, "(untitled Decision)").replace(/[`“-‟″‶〝〞]/gu, "'");
248
+ return `${author} \u2014 \u201C${intent}\u201D`;
249
+ });
250
+ const overflow = rows.length > CODEX_DIGEST_VISIBLE_CAP ? ` +${String(rows.length - CODEX_DIGEST_VISIBLE_CAP)}${options.pageTruncated === true ? "+" : ""}` : "";
251
+ return `[prim] Decisions captured since last message: ${visible.join("; ")}${overflow}`;
252
+ }
253
+ function actionablePrivateDrafts(rows) {
254
+ const seen = /* @__PURE__ */ new Set();
255
+ const actionable = [];
256
+ for (const row of rows) {
257
+ if (!isActionablePrivateDraft(row) || seen.has(row.id)) continue;
258
+ seen.add(row.id);
259
+ actionable.push(row);
260
+ }
261
+ return actionable;
262
+ }
263
+ function renderDecisionDraftDigest(rows, options = {}) {
264
+ const eligible = actionablePrivateDrafts(rows);
265
+ if (eligible.length === 0) return void 0;
266
+ const visible = eligible.slice(0, CODEX_DIGEST_VISIBLE_CAP);
267
+ const lines = visible.flatMap((row) => {
268
+ const intent = safeInline(row.intent, "(untitled Decision)").replace(/[“-‟″‶〝〞]/gu, "'");
269
+ return [
270
+ `[prim] private Decision ${row.id} \xB7 stage: draft \xB7 \u201C${intent}\u201D`,
271
+ `[prim] Run \`prim decisions publish ${row.id}\` to share it with your team.`
272
+ ];
273
+ });
274
+ if (eligible.length > visible.length) {
275
+ const suffix = options.pageTruncated === true ? "+" : "";
276
+ lines.push(
277
+ `[prim] +${String(eligible.length - visible.length)}${suffix} private Decision drafts remain for later messages.`
278
+ );
279
+ }
280
+ return { message: lines.join("\n"), deliveredIds: visible.map((row) => row.id) };
281
+ }
282
+ function reauthSnapshot(sessionId) {
283
+ return {
284
+ pid: 0,
285
+ uptimeMs: 0,
286
+ sessionId,
287
+ healthy: false,
288
+ needsReauth: true
289
+ };
290
+ }
291
+ function mergeState(latest, args) {
292
+ const seen = new Set(latest?.seenIds ?? []);
293
+ for (const id of args.seenIds) seen.add(id);
294
+ return {
295
+ version: STATE_VERSION,
296
+ sessionId: args.sessionId,
297
+ siteUrl: args.siteUrl,
298
+ workspace: args.workspace,
299
+ startedAt: latest?.startedAt ?? args.startedAt,
300
+ // Advance only on an observed page (server classifiedAt time), never the
301
+ // client clock; an unavailable feed keeps the previous cursor — or the
302
+ // NO_CURSOR sentinel, so the startup backlog survives until the first
303
+ // successful fetch.
304
+ watermarkMs: args.feedAvailable ? Math.max(latest?.watermarkMs ?? NO_CURSOR, args.watermarkMs) : latest?.watermarkMs ?? NO_CURSOR,
305
+ seenIds: [...seen].slice(-CODEX_DIGEST_MAX_SEEN_IDS),
306
+ lastReport: args.report,
307
+ updatedAt: args.now
308
+ };
309
+ }
310
+ async function commitState(path, args) {
311
+ try {
312
+ await withFileLock(
313
+ `${path}.lock`,
314
+ () => {
315
+ const latest = readState(path);
316
+ writeStateFile(path, stateRoot(), mergeState(latest, args));
317
+ cleanupStateFiles(stateRoot(), args.now);
318
+ },
319
+ { timeoutMs: 50, pollMs: 10 }
320
+ );
321
+ } catch {
322
+ }
323
+ }
324
+ function mergeDraftState(latest, args) {
325
+ const seen = new Set(latest?.seenDraftIds ?? []);
326
+ for (const id of args.deliveredIds) seen.add(id);
327
+ return {
328
+ version: DRAFT_STATE_VERSION,
329
+ sessionId: args.sessionId,
330
+ siteUrl: args.siteUrl,
331
+ workspace: args.workspace,
332
+ principalId: args.principalId,
333
+ organizationId: args.organizationId,
334
+ credentialFingerprint: args.credentialFingerprint,
335
+ seenDraftIds: [...seen].slice(-CODEX_DRAFT_DIGEST_MAX_SEEN_IDS),
336
+ updatedAt: args.now
337
+ };
338
+ }
339
+ async function commitDraftState(path, args) {
340
+ try {
341
+ let next;
342
+ await withFileLock(
343
+ `${path}.lock`,
344
+ () => {
345
+ const latest = readDraftState(path);
346
+ next = mergeDraftState(latest, args);
347
+ writeStateFile(path, draftStateRoot(), next);
348
+ cleanupStateFiles(draftStateRoot(), args.now);
349
+ },
350
+ { timeoutMs: 50, pollMs: 10 }
351
+ );
352
+ return next;
353
+ } catch {
354
+ return void 0;
355
+ }
356
+ }
357
+ async function prepareCodexContext(options) {
358
+ const siteUrl = getSiteUrl();
359
+ const workspace = workspaceFor(options.cwd);
360
+ const path = statePath({ cwd: options.cwd, sessionId: options.sessionId, siteUrl });
361
+ const previous = readState(path);
362
+ const principal = resolveDaemonPrincipal();
363
+ const privatePath = principal ? draftStatePath({
364
+ cwd: options.cwd,
365
+ sessionId: options.sessionId,
366
+ siteUrl,
367
+ principalId: principal.principalId,
368
+ organizationId: principal.organizationId,
369
+ credentialFingerprint: principal.credentialFingerprint
370
+ }) : void 0;
371
+ const previousDraft = privatePath ? readDraftState(privatePath) : void 0;
372
+ const startup = options.startup === true;
373
+ const includeDigest = options.includeDigest !== false;
374
+ const startedAt = previous?.startedAt ?? Date.now();
375
+ const terminalAuth = isSessionEnded();
376
+ let snapshot = null;
377
+ let recent = null;
378
+ let recentDrafts = null;
379
+ if (terminalAuth) {
380
+ snapshot = reauthSnapshot(options.sessionId);
381
+ } else {
382
+ [snapshot, recent, recentDrafts] = await Promise.all([
383
+ daemonRequest(
384
+ "status_snapshot",
385
+ { callerEnv: siteUrl },
386
+ { timeoutMs: CODEX_CONTEXT_TIMEOUT_MS }
387
+ ),
388
+ includeDigest ? daemonRequest(
389
+ "decision_digest_snapshot",
390
+ { callerEnv: siteUrl },
391
+ { timeoutMs: CODEX_CONTEXT_TIMEOUT_MS }
392
+ ) : Promise.resolve(null),
393
+ includeDigest && principal !== void 0 ? daemonRequest(
394
+ "decision_draft_digest_snapshot",
395
+ { callerEnv: siteUrl },
396
+ { timeoutMs: CODEX_CONTEXT_TIMEOUT_MS }
397
+ ) : Promise.resolve(null)
398
+ ]);
399
+ if (principal !== void 0 && !daemonPrincipalsMatch(principal, resolveDaemonPrincipal())) {
400
+ snapshot = null;
401
+ recent = null;
402
+ recentDrafts = null;
403
+ }
404
+ }
405
+ let ingestionStatus;
406
+ const resolveIngestionStatus = () => {
407
+ ingestionStatus ??= decisionIngestionStatus(options.cwd);
408
+ return ingestionStatus;
409
+ };
410
+ const report = formatStatusline(packageVersion() ?? "0.0.0", snapshot, resolveIngestionStatus, {
411
+ includeIngestionWhenUnavailable: true,
412
+ plainLinks: true,
413
+ resolveRepositoryBindingState: () => resolveIngestionStatus() === "enabled" ? repositoryBindingState(options.cwd) : void 0
414
+ });
415
+ const reportChanged = startup || previous?.lastReport !== report;
416
+ let feedAvailable = false;
417
+ let freshRows = [];
418
+ let pageTruncated = false;
419
+ let pageWatermark = NO_CURSOR;
420
+ if (!terminalAuth && includeDigest && recent !== null) {
421
+ feedAvailable = recent.unavailable === void 0 && Array.isArray(recent.decisions);
422
+ if (feedAvailable) {
423
+ const seen = new Set(previous?.seenIds ?? []);
424
+ const lowerBound = previous !== void 0 && previous.watermarkMs >= 0 ? Math.max(0, previous.watermarkMs - CODEX_DIGEST_OVERLAP_MS) : 0;
425
+ freshRows = recent.decisions.filter(
426
+ (row) => isChangeDecision(row) && !seen.has(row.id) && typeof row.classifiedAt === "number" && row.classifiedAt >= lowerBound
427
+ );
428
+ pageTruncated = recent.decisions.length >= CODEX_DIGEST_LIMIT;
429
+ pageWatermark = recent.decisions.reduce(
430
+ (max, row) => typeof row.classifiedAt === "number" && row.classifiedAt > max ? row.classifiedAt : max,
431
+ NO_CURSOR
432
+ );
433
+ }
434
+ }
435
+ const digest = renderDecisionDigest(freshRows, { pageTruncated });
436
+ let draftDigest;
437
+ let draftPageToken;
438
+ let draftPageActionableIds = [];
439
+ if (!terminalAuth && includeDigest && privatePath !== void 0 && recentDrafts !== null && recentDrafts.unavailable === void 0 && Array.isArray(recentDrafts.decisions)) {
440
+ const allActionableDrafts = actionablePrivateDrafts(recentDrafts.decisions);
441
+ const seenDrafts = new Set(previousDraft?.seenDraftIds ?? []);
442
+ const freshDrafts = allActionableDrafts.filter((row) => !seenDrafts.has(row.id));
443
+ draftDigest = renderDecisionDraftDigest(freshDrafts, {
444
+ pageTruncated: recentDrafts.pageDone === false
445
+ });
446
+ draftPageToken = typeof recentDrafts.pageToken === "string" ? recentDrafts.pageToken : void 0;
447
+ draftPageActionableIds = allActionableDrafts.map((row) => row.id);
448
+ }
449
+ const combinedDigest = [digest, draftDigest?.message].filter((value) => value !== void 0).join("\n\n");
450
+ const context = [reportChanged ? report : void 0, combinedDigest || void 0].filter((value) => value !== void 0).join("\n\n") || void 0;
451
+ let acknowledged = false;
452
+ return {
453
+ context,
454
+ decisionDigest: combinedDigest || void 0,
455
+ feedAvailable,
456
+ acknowledge: async (handedOff) => {
457
+ if (!handedOff || acknowledged) return;
458
+ acknowledged = true;
459
+ await commitState(path, {
460
+ sessionId: options.sessionId,
461
+ siteUrl,
462
+ workspace,
463
+ startedAt,
464
+ watermarkMs: pageWatermark,
465
+ seenIds: freshRows.map((row) => row.id),
466
+ report,
467
+ feedAvailable,
468
+ now: Date.now()
469
+ });
470
+ if (privatePath === void 0 || principal === void 0 || draftPageToken === void 0) {
471
+ return;
472
+ }
473
+ let persistedDraft = previousDraft;
474
+ if (draftDigest?.deliveredIds.length) {
475
+ persistedDraft = await commitDraftState(privatePath, {
476
+ sessionId: options.sessionId,
477
+ siteUrl,
478
+ workspace,
479
+ principalId: principal.principalId,
480
+ organizationId: principal.organizationId,
481
+ credentialFingerprint: principal.credentialFingerprint,
482
+ deliveredIds: draftDigest.deliveredIds,
483
+ now: Date.now()
484
+ });
485
+ }
486
+ const seenDrafts = new Set(persistedDraft?.seenDraftIds ?? []);
487
+ const deliveredOnThisPage = draftPageActionableIds.filter((id) => seenDrafts.has(id));
488
+ await daemonRequest(
489
+ "decision_draft_digest_acknowledge",
490
+ {
491
+ callerEnv: siteUrl,
492
+ pageToken: draftPageToken,
493
+ deliveredIds: deliveredOnThisPage
494
+ },
495
+ { timeoutMs: CODEX_CONTEXT_TIMEOUT_MS }
496
+ );
497
+ }
498
+ };
499
+ }
500
+
501
+ export {
502
+ hasVisibleCodexMessage,
503
+ appendCodexContext,
504
+ prepareCodexContext
505
+ };
@@ -0,0 +1,79 @@
1
+ import {
2
+ terminalSafeLine
3
+ } from "./chunk-Z7YPFWAZ.js";
4
+
5
+ // src/lib/ansi.ts
6
+ var ANSI_CODES = {
7
+ yellow: "\x1B[33m",
8
+ magenta: "\x1B[35m",
9
+ blue: "\x1B[34m",
10
+ cyan: "\x1B[36m",
11
+ green: "\x1B[32m",
12
+ red: "\x1B[31m",
13
+ // 256-color approximation of the orange trigger marker (xterm 208).
14
+ orange: "\x1B[38;5;208m",
15
+ gray: "\x1B[90m"
16
+ };
17
+ var ANSI_RESET = "\x1B[0m";
18
+ var ANSI_DIM = "\x1B[2m";
19
+ var ANSI_BOLD = "\x1B[1m";
20
+ function supportsColor() {
21
+ if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") {
22
+ return false;
23
+ }
24
+ return process.stderr.isTTY === true;
25
+ }
26
+ function color(text, c) {
27
+ if (!supportsColor()) {
28
+ return text;
29
+ }
30
+ return `${ANSI_CODES[c]}${text}${ANSI_RESET}`;
31
+ }
32
+ function dim(text) {
33
+ if (!supportsColor()) {
34
+ return text;
35
+ }
36
+ return `${ANSI_DIM}${text}${ANSI_RESET}`;
37
+ }
38
+ function bold(text) {
39
+ if (!supportsColor()) {
40
+ return text;
41
+ }
42
+ return `${ANSI_BOLD}${text}${ANSI_RESET}`;
43
+ }
44
+ var AREA_COLORS = {
45
+ auth: "yellow",
46
+ data: "magenta",
47
+ mobile: "blue",
48
+ infra: "cyan",
49
+ ui: "green",
50
+ billing: "orange",
51
+ api: "blue",
52
+ docs: "gray",
53
+ testing: "gray"
54
+ };
55
+ function colorForArea(area) {
56
+ if (!area) {
57
+ return "gray";
58
+ }
59
+ return AREA_COLORS[area] ?? "gray";
60
+ }
61
+ var MAX_HEALTH_ERROR_LENGTH = 240;
62
+ function boundedHealthError(value) {
63
+ if (!value) return void 0;
64
+ const clean = terminalSafeLine(value);
65
+ if (!clean) return void 0;
66
+ return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
67
+ }
68
+ function stripAnsi(text) {
69
+ return text.replace(/\u001b\[[0-9;]*m/g, "");
70
+ }
71
+
72
+ export {
73
+ color,
74
+ dim,
75
+ bold,
76
+ colorForArea,
77
+ boundedHealthError,
78
+ stripAnsi
79
+ };