agent-cli-runtime 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/CONTRIBUTING.md +60 -0
  3. package/LICENSE +202 -0
  4. package/README.md +573 -0
  5. package/README.zh-CN.md +571 -0
  6. package/SECURITY.md +35 -0
  7. package/dist/adapters/adapter-types.d.ts +138 -0
  8. package/dist/adapters/adapter-types.js +2 -0
  9. package/dist/adapters/adapter-types.js.map +1 -0
  10. package/dist/adapters/claude.d.ts +2 -0
  11. package/dist/adapters/claude.js +97 -0
  12. package/dist/adapters/claude.js.map +1 -0
  13. package/dist/adapters/codex.d.ts +3 -0
  14. package/dist/adapters/codex.js +120 -0
  15. package/dist/adapters/codex.js.map +1 -0
  16. package/dist/adapters/opencode.d.ts +4 -0
  17. package/dist/adapters/opencode.js +111 -0
  18. package/dist/adapters/opencode.js.map +1 -0
  19. package/dist/adapters/registry.d.ts +9 -0
  20. package/dist/adapters/registry.js +23 -0
  21. package/dist/adapters/registry.js.map +1 -0
  22. package/dist/cli/main.d.ts +2 -0
  23. package/dist/cli/main.js +978 -0
  24. package/dist/cli/main.js.map +1 -0
  25. package/dist/core/async-queue.d.ts +10 -0
  26. package/dist/core/async-queue.js +49 -0
  27. package/dist/core/async-queue.js.map +1 -0
  28. package/dist/core/diagnostics.d.ts +20 -0
  29. package/dist/core/diagnostics.js +4 -0
  30. package/dist/core/diagnostics.js.map +1 -0
  31. package/dist/core/event-contract.d.ts +32 -0
  32. package/dist/core/event-contract.js +128 -0
  33. package/dist/core/event-contract.js.map +1 -0
  34. package/dist/core/events.d.ts +147 -0
  35. package/dist/core/events.js +4 -0
  36. package/dist/core/events.js.map +1 -0
  37. package/dist/core/ids.d.ts +1 -0
  38. package/dist/core/ids.js +5 -0
  39. package/dist/core/ids.js.map +1 -0
  40. package/dist/core/redaction.d.ts +4 -0
  41. package/dist/core/redaction.js +51 -0
  42. package/dist/core/redaction.js.map +1 -0
  43. package/dist/core/runtime.d.ts +41 -0
  44. package/dist/core/runtime.js +83 -0
  45. package/dist/core/runtime.js.map +1 -0
  46. package/dist/core/schema-contract.d.ts +55 -0
  47. package/dist/core/schema-contract.js +143 -0
  48. package/dist/core/schema-contract.js.map +1 -0
  49. package/dist/detection/detect.d.ts +14 -0
  50. package/dist/detection/detect.js +293 -0
  51. package/dist/detection/detect.js.map +1 -0
  52. package/dist/detection/env.d.ts +2 -0
  53. package/dist/detection/env.js +15 -0
  54. package/dist/detection/env.js.map +1 -0
  55. package/dist/detection/executable-resolution.d.ts +12 -0
  56. package/dist/detection/executable-resolution.js +50 -0
  57. package/dist/detection/executable-resolution.js.map +1 -0
  58. package/dist/detection/invocation.d.ts +9 -0
  59. package/dist/detection/invocation.js +22 -0
  60. package/dist/detection/invocation.js.map +1 -0
  61. package/dist/goals/goal-scheduler.d.ts +31 -0
  62. package/dist/goals/goal-scheduler.js +518 -0
  63. package/dist/goals/goal-scheduler.js.map +1 -0
  64. package/dist/goals/goal-store.d.ts +37 -0
  65. package/dist/goals/goal-store.js +300 -0
  66. package/dist/goals/goal-store.js.map +1 -0
  67. package/dist/goals/goal-types.d.ts +103 -0
  68. package/dist/goals/goal-types.js +2 -0
  69. package/dist/goals/goal-types.js.map +1 -0
  70. package/dist/goals/planner-prompts.d.ts +3 -0
  71. package/dist/goals/planner-prompts.js +26 -0
  72. package/dist/goals/planner-prompts.js.map +1 -0
  73. package/dist/goals/task-graph.d.ts +9 -0
  74. package/dist/goals/task-graph.js +229 -0
  75. package/dist/goals/task-graph.js.map +1 -0
  76. package/dist/goals/validation-runner.d.ts +7 -0
  77. package/dist/goals/validation-runner.js +63 -0
  78. package/dist/goals/validation-runner.js.map +1 -0
  79. package/dist/index.d.ts +11 -0
  80. package/dist/index.js +2 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/parsers/claude-stream-json.d.ts +11 -0
  83. package/dist/parsers/claude-stream-json.js +102 -0
  84. package/dist/parsers/claude-stream-json.js.map +1 -0
  85. package/dist/parsers/codex-json.d.ts +8 -0
  86. package/dist/parsers/codex-json.js +107 -0
  87. package/dist/parsers/codex-json.js.map +1 -0
  88. package/dist/parsers/line-buffer.d.ts +7 -0
  89. package/dist/parsers/line-buffer.js +28 -0
  90. package/dist/parsers/line-buffer.js.map +1 -0
  91. package/dist/parsers/opencode-json.d.ts +8 -0
  92. package/dist/parsers/opencode-json.js +72 -0
  93. package/dist/parsers/opencode-json.js.map +1 -0
  94. package/dist/parsers/plain-lines.d.ts +6 -0
  95. package/dist/parsers/plain-lines.js +9 -0
  96. package/dist/parsers/plain-lines.js.map +1 -0
  97. package/dist/public-types.d.ts +143 -0
  98. package/dist/public-types.js +2 -0
  99. package/dist/public-types.js.map +1 -0
  100. package/dist/runs/process-runner.d.ts +35 -0
  101. package/dist/runs/process-runner.js +97 -0
  102. package/dist/runs/process-runner.js.map +1 -0
  103. package/dist/runs/prompt-transport.d.ts +10 -0
  104. package/dist/runs/prompt-transport.js +43 -0
  105. package/dist/runs/prompt-transport.js.map +1 -0
  106. package/dist/runs/run-result.d.ts +9 -0
  107. package/dist/runs/run-result.js +22 -0
  108. package/dist/runs/run-result.js.map +1 -0
  109. package/dist/runs/run-scheduler.d.ts +25 -0
  110. package/dist/runs/run-scheduler.js +552 -0
  111. package/dist/runs/run-scheduler.js.map +1 -0
  112. package/dist/runs/run-store.d.ts +42 -0
  113. package/dist/runs/run-store.js +297 -0
  114. package/dist/runs/run-store.js.map +1 -0
  115. package/dist/runs/run-types.d.ts +59 -0
  116. package/dist/runs/run-types.js +2 -0
  117. package/dist/runs/run-types.js.map +1 -0
  118. package/dist/smoke/parser-samples.d.ts +17 -0
  119. package/dist/smoke/parser-samples.js +186 -0
  120. package/dist/smoke/parser-samples.js.map +1 -0
  121. package/dist/storage/file-storage.d.ts +35 -0
  122. package/dist/storage/file-storage.js +271 -0
  123. package/dist/storage/file-storage.js.map +1 -0
  124. package/dist/storage/jsonl-store.d.ts +9 -0
  125. package/dist/storage/jsonl-store.js +138 -0
  126. package/dist/storage/jsonl-store.js.map +1 -0
  127. package/dist/storage/manifest-validation.d.ts +11 -0
  128. package/dist/storage/manifest-validation.js +102 -0
  129. package/dist/storage/manifest-validation.js.map +1 -0
  130. package/dist/storage/storage-lease.d.ts +40 -0
  131. package/dist/storage/storage-lease.js +223 -0
  132. package/dist/storage/storage-lease.js.map +1 -0
  133. package/dist/storage/storage-types.d.ts +55 -0
  134. package/dist/storage/storage-types.js +2 -0
  135. package/dist/storage/storage-types.js.map +1 -0
  136. package/dist/storage/store-inspection.d.ts +28 -0
  137. package/dist/storage/store-inspection.js +941 -0
  138. package/dist/storage/store-inspection.js.map +1 -0
  139. package/docs/api-schema-contract.md +92 -0
  140. package/docs/compatibility.md +832 -0
  141. package/docs/daemon-ready-contract.md +283 -0
  142. package/docs/production-readiness.md +281 -0
  143. package/docs/release-checklist.md +257 -0
  144. package/docs/release-publish-runbook.md +201 -0
  145. package/docs/release-report.md +517 -0
  146. package/docs/ssot.md +1257 -0
  147. package/examples/cli-dogfood.md +113 -0
  148. package/examples/library-goal.js +94 -0
  149. package/examples/library-run.js +84 -0
  150. package/package.json +79 -0
  151. package/scripts/dogfood.mjs +243 -0
@@ -0,0 +1,941 @@
1
+ import { closeSync, existsSync, fdatasyncSync, fsyncSync, appendFileSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync, } from "node:fs";
2
+ import path from "node:path";
3
+ import { randomUUID } from "node:crypto";
4
+ import { terminalReasonFromDiagnosticCode, terminalReasonFromResult } from "../core/event-contract.js";
5
+ import { redactUnknown } from "../core/redaction.js";
6
+ import { readJsonl } from "./jsonl-store.js";
7
+ import { isRecord, validateGoalManifest, validateRunManifest } from "./manifest-validation.js";
8
+ import { inspectOwner, inspectStorageLock, StorageLease } from "./storage-lease.js";
9
+ import { isTerminal } from "../runs/run-store.js";
10
+ import { isTerminalGoal } from "../goals/goal-store.js";
11
+ export function inspectStoreDirectory(storageDir) {
12
+ const records = scanStore(storageDir);
13
+ const lock = inspectStorageLock(storageDir);
14
+ const storageDiagnostics = readStoreDiagnostics(storageDir);
15
+ const corruptManifests = records.flatMap((record) => record.manifestIssue ? [record.manifestIssue] : []);
16
+ const corruptEventLogs = records.flatMap((record) => record.eventIssues);
17
+ const partialTails = records.flatMap((record) => record.partialTail ? [record.partialTail] : []);
18
+ const activeRecords = records.flatMap((record) => activeRecord(record, storageDir));
19
+ const activeInterrupted = records.flatMap((record) => record.activeInterrupted ? [record.activeInterrupted] : []);
20
+ const warnings = records.flatMap((record) => record.warnings);
21
+ const health = {
22
+ schemaVersion: "agent-runtime.storeHealth.v1",
23
+ ok: corruptManifests.length === 0
24
+ && corruptEventLogs.length === 0
25
+ && warnings.length === 0
26
+ && activeInterrupted.length === 0
27
+ && lock.status !== "invalid"
28
+ && storageDiagnostics.every(isNonBlockingStorageDiagnostic),
29
+ storageDir,
30
+ checkedAt: Date.now(),
31
+ lock,
32
+ totals: {
33
+ runs: records.filter((record) => record.kind === "run").length,
34
+ goals: records.filter((record) => record.kind === "goal").length,
35
+ corruptEventLogLines: corruptEventLogs.reduce((sum, issue) => sum + (issue.corruptLineCount ?? 1), 0),
36
+ partialEventLogTails: partialTails.length,
37
+ activeRecords: activeRecords.length,
38
+ },
39
+ corruptManifests,
40
+ corruptEventLogs,
41
+ partialTails,
42
+ activeRecords,
43
+ activeInterrupted,
44
+ warnings,
45
+ storageDiagnostics,
46
+ diagnostics: summarizeDiagnostics(records, storageDiagnostics),
47
+ };
48
+ return redactUnknown(health);
49
+ }
50
+ export function inspectStoreLock(storageDir) {
51
+ return redactUnknown(inspectStorageLock(storageDir));
52
+ }
53
+ export function listStoredRuns(storageDir, options = {}) {
54
+ return scanStore(storageDir)
55
+ .filter((record) => record.kind === "run")
56
+ .flatMap((record) => {
57
+ const run = recordToRun(record);
58
+ return run ? [run] : [];
59
+ })
60
+ .filter((run) => {
61
+ if (!options.status)
62
+ return true;
63
+ if (options.status === "active")
64
+ return !isTerminal(run.status);
65
+ return run.status === options.status;
66
+ });
67
+ }
68
+ export function getStoredRun(storageDir, runId) {
69
+ const record = scanRecord(storageDir, "run", runId);
70
+ return recordToRun(record);
71
+ }
72
+ export function replayStoredRunEvents(storageDir, runId, afterEventId = 0) {
73
+ const record = scanRecord(storageDir, "run", runId);
74
+ return record.events
75
+ .filter((event) => event.id > afterEventId)
76
+ .sort(compareReplayEvents);
77
+ }
78
+ export function listStoredGoals(storageDir, options = {}) {
79
+ return scanStore(storageDir)
80
+ .filter((record) => record.kind === "goal")
81
+ .flatMap((record) => {
82
+ const goal = recordToGoal(record);
83
+ return goal ? [goal] : [];
84
+ })
85
+ .filter((goal) => {
86
+ if (!options.status)
87
+ return true;
88
+ if (options.status === "active")
89
+ return !isTerminalGoal(goal.status);
90
+ return goal.status === options.status;
91
+ });
92
+ }
93
+ export function getStoredGoal(storageDir, goalId) {
94
+ const record = scanRecord(storageDir, "goal", goalId);
95
+ return recordToGoal(record);
96
+ }
97
+ export function replayStoredGoalEvents(storageDir, goalId, afterEventId = 0) {
98
+ const record = scanRecord(storageDir, "goal", goalId);
99
+ return record.events
100
+ .filter((event) => event.id > afterEventId)
101
+ .sort(compareReplayEvents);
102
+ }
103
+ export function inspectStoreRepairDryRun(storageDir) {
104
+ return inspectStoreRepair(storageDir, { apply: false });
105
+ }
106
+ export function inspectStoreRepair(storageDir, options = {}) {
107
+ const health = inspectStoreDirectory(storageDir);
108
+ const dryRun = !options.apply;
109
+ const actions = [
110
+ ...health.partialTails.map((issue) => repairActionFromIssue(storageDir, issue, "truncate_partial_tail", dryRun)),
111
+ ...health.corruptEventLogs
112
+ .filter((issue) => !issue.partialTailDetected)
113
+ .map((issue) => repairActionFromIssue(storageDir, issue, issue.repairRecommendation === "isolate_corrupt_line" ? "isolate_corrupt_line" : "manual_review", dryRun)),
114
+ ...health.warnings.map((warning) => ({
115
+ kind: warning.kind,
116
+ id: warning.id,
117
+ file: warning.file,
118
+ action: "manual_review",
119
+ dryRun,
120
+ applied: false,
121
+ backupPath: null,
122
+ retainedEventCount: 0,
123
+ removedLineCount: 0,
124
+ truncatedBytes: 0,
125
+ reason: warning.message,
126
+ diagnostics: [{ code: warning.code, message: warning.message, retryable: false }],
127
+ })),
128
+ ];
129
+ let blockedReason;
130
+ const diagnostics = cloneSummary(health.diagnostics);
131
+ let lease;
132
+ if (options.apply) {
133
+ const liveActiveRecord = health.activeRecords.find((record) => record.ownerStatus === "live");
134
+ const hasRepairableActions = actions.some((action) => action.action === "truncate_partial_tail" || action.action === "isolate_corrupt_line");
135
+ if (health.lock.status === "live") {
136
+ blockedReason = "store has a live writer owner; repair apply is refused";
137
+ }
138
+ else if (liveActiveRecord) {
139
+ blockedReason = `${liveActiveRecord.kind} ${liveActiveRecord.id} has a live owner; repair apply is refused`;
140
+ }
141
+ else if (!hasRepairableActions) {
142
+ // Nothing to mutate; keep no-op/manual-review apply from touching the store.
143
+ }
144
+ else {
145
+ try {
146
+ lease = StorageLease.acquire(storageDir);
147
+ applyRepairActions(storageDir, actions, options.faults);
148
+ persistRepairDiagnostic(storageDir, actions);
149
+ }
150
+ catch (error) {
151
+ blockedReason = `repair apply failed or could not acquire exclusive store access: ${errorMessage(error)}`;
152
+ persistRepairFailureDiagnostic(storageDir, error);
153
+ }
154
+ finally {
155
+ lease?.close();
156
+ }
157
+ }
158
+ if (blockedReason) {
159
+ const code = /live (writer )?owner/u.test(blockedReason) ? "AGENT_STORE_REPAIR_REFUSED_LIVE_OWNER" : "AGENT_STORE_REPAIR_FAILED";
160
+ increment(diagnostics.byCode, code);
161
+ diagnostics.total += 1;
162
+ }
163
+ }
164
+ return redactUnknown({
165
+ schemaVersion: "agent-runtime.storeRepair.v1",
166
+ storageDir,
167
+ checkedAt: Date.now(),
168
+ dryRun,
169
+ applied: actions.some((action) => action.applied),
170
+ ok: !blockedReason && (dryRun ? actions.length === 0 : actions.every((action) => action.applied)),
171
+ blockedReason,
172
+ actions,
173
+ diagnostics,
174
+ });
175
+ }
176
+ export function exportDiagnosticsBundle(request, storageDir) {
177
+ const kind = request.kind;
178
+ const id = kind === "run" ? request.runId : request.goalId;
179
+ const record = scanRecord(storageDir, kind, id);
180
+ const storageDiagnostics = readStoreDiagnostics(storageDir);
181
+ const eventTypes = {};
182
+ for (const event of record.events) {
183
+ const type = eventType(event.event);
184
+ eventTypes[type] = (eventTypes[type] ?? 0) + 1;
185
+ }
186
+ const diagnostics = diagnosticsForRecord(record);
187
+ const manifest = record.manifest;
188
+ const bundle = {
189
+ schemaVersion: "agent-runtime.diagnostics.v1",
190
+ exportedAt: Date.now(),
191
+ storageDir,
192
+ subject: { kind, id },
193
+ manifest,
194
+ events: {
195
+ total: record.events.length,
196
+ retained: record.events.length,
197
+ firstEventId: record.events[0]?.id,
198
+ lastEventId: record.events.at(-1)?.id,
199
+ terminalEvent: hasTerminalEvent(kind, record.events),
200
+ eventTypes,
201
+ corrupt: record.eventIssue,
202
+ partialTail: record.partialTail,
203
+ },
204
+ diagnostics,
205
+ storageDiagnostics,
206
+ consistencyWarnings: record.warnings,
207
+ attemptEvidence: kind === "goal" ? attemptEvidenceFromGoalManifest(manifest) : undefined,
208
+ supervisorSummary: supervisorSummary(record),
209
+ adapterSummary: adapterSummary(record),
210
+ };
211
+ return redactUnknown(bundle);
212
+ }
213
+ export function atomicWriteJsonFile(file, value) {
214
+ const tmp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${randomUUID()}.tmp`);
215
+ writeFileSync(tmp, `${JSON.stringify(redactUnknown(value), null, 2)}\n`, "utf8");
216
+ renameSync(tmp, file);
217
+ }
218
+ function scanStore(storageDir) {
219
+ return [
220
+ ...scanKind(storageDir, "run"),
221
+ ...scanKind(storageDir, "goal"),
222
+ ];
223
+ }
224
+ function scanKind(storageDir, kind) {
225
+ const parent = path.join(storageDir, kind === "run" ? "runs" : "goals");
226
+ if (!existsSync(parent))
227
+ return [];
228
+ return readdirSync(parent, { withFileTypes: true })
229
+ .filter((entry) => entry.isDirectory())
230
+ .map((entry) => scanRecord(storageDir, kind, entry.name));
231
+ }
232
+ function scanRecord(storageDir, kind, id) {
233
+ const recordDir = path.join(storageDir, kind === "run" ? "runs" : "goals", id);
234
+ const manifestPath = path.join(recordDir, "manifest.json");
235
+ const eventsPath = path.join(recordDir, "events.jsonl");
236
+ const manifest = readManifest(storageDir, manifestPath, kind, id);
237
+ const events = readJsonl(eventsPath);
238
+ const eventIssues = events.issues.map((issue) => ({
239
+ kind,
240
+ id,
241
+ file: relativeFile(storageDir, eventsPath),
242
+ line: issue.line,
243
+ reason: issue.reason,
244
+ retainedEventCount: issue.retainedEventCount,
245
+ corruptLineCount: issue.corruptLineCount,
246
+ partialTailDetected: issue.partialTailDetected,
247
+ lastGoodEventId: issue.lastGoodEventId,
248
+ lastGoodSequence: issue.lastGoodSequence,
249
+ repairRecommendation: issue.repairRecommendation,
250
+ redactedTailPreview: issue.redactedTailPreview,
251
+ }));
252
+ const eventIssue = eventIssues[0];
253
+ const record = {
254
+ kind,
255
+ id,
256
+ manifest: manifest.value,
257
+ manifestIssue: manifest.issue,
258
+ events: events.records,
259
+ eventIssue,
260
+ eventIssues,
261
+ partialTail: eventIssues.find((issue) => issue.partialTailDetected),
262
+ warnings: [],
263
+ };
264
+ record.activeInterrupted = activeInterruptedIssue(record, storageDir, manifestPath);
265
+ record.warnings = consistencyWarnings(record, storageDir, manifestPath);
266
+ return record;
267
+ }
268
+ function readManifest(storageDir, file, kind, id) {
269
+ try {
270
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
271
+ const validated = kind === "run" ? validateRunManifest(parsed, id) : validateGoalManifest(parsed, id);
272
+ if (validated.error)
273
+ return { value: null, issue: manifestIssue(storageDir, file, kind, id, validated.error.message) };
274
+ return { value: validated.value };
275
+ }
276
+ catch (error) {
277
+ return {
278
+ value: null,
279
+ issue: manifestIssue(storageDir, file, kind, id, error instanceof Error ? error.message : String(error)),
280
+ };
281
+ }
282
+ }
283
+ function recordToRun(record) {
284
+ if (record.kind !== "run")
285
+ return null;
286
+ if (record.manifest)
287
+ return record.manifest;
288
+ if (!record.manifestIssue)
289
+ return null;
290
+ const now = Date.now();
291
+ return {
292
+ id: record.id,
293
+ agentId: "unknown",
294
+ cwd: "<unknown>",
295
+ status: "failed",
296
+ createdAt: now,
297
+ updatedAt: now,
298
+ exitCode: null,
299
+ signal: null,
300
+ error: record.manifestIssue.reason,
301
+ errorCode: "AGENT_STORE_RECORD_CORRUPT",
302
+ diagnostics: [{
303
+ code: "AGENT_STORE_RECORD_CORRUPT",
304
+ message: record.manifestIssue.reason,
305
+ retryable: false,
306
+ }],
307
+ };
308
+ }
309
+ function recordToGoal(record) {
310
+ if (record.kind !== "goal")
311
+ return null;
312
+ if (record.manifest)
313
+ return record.manifest;
314
+ if (!record.manifestIssue)
315
+ return null;
316
+ const now = Date.now();
317
+ return {
318
+ id: record.id,
319
+ cwd: "<unknown>",
320
+ objective: "<unknown>",
321
+ status: "failed",
322
+ result: "failed",
323
+ tasks: [],
324
+ diagnostics: [{
325
+ code: "AGENT_STORE_RECORD_CORRUPT",
326
+ message: record.manifestIssue.reason,
327
+ retryable: false,
328
+ }],
329
+ createdAt: now,
330
+ updatedAt: now,
331
+ };
332
+ }
333
+ function manifestIssue(storageDir, file, kind, id, reason) {
334
+ return {
335
+ kind,
336
+ id,
337
+ file: relativeFile(storageDir, file),
338
+ reason,
339
+ };
340
+ }
341
+ function consistencyWarnings(record, storageDir, manifestPath) {
342
+ if (!record.manifest)
343
+ return [];
344
+ const terminalManifest = isTerminalManifest(record.kind, record.manifest.status);
345
+ const terminalEvent = hasTerminalEvent(record.kind, record.events);
346
+ if (terminalManifest && !terminalEvent) {
347
+ return [{
348
+ kind: record.kind,
349
+ id: record.id,
350
+ code: "AGENT_STORE_TERMINAL_EVENT_MISSING",
351
+ message: "Terminal manifest has no matching terminal event in events.jsonl.",
352
+ file: relativeFile(storageDir, manifestPath),
353
+ }];
354
+ }
355
+ if (!terminalManifest && terminalEvent) {
356
+ return [{
357
+ kind: record.kind,
358
+ id: record.id,
359
+ code: "AGENT_STORE_TERMINAL_EVENT_MANIFEST_MISMATCH",
360
+ message: "Event log has a terminal event but manifest status is not terminal.",
361
+ file: relativeFile(storageDir, manifestPath),
362
+ }];
363
+ }
364
+ return [];
365
+ }
366
+ function activeInterruptedIssue(record, storageDir, manifestPath) {
367
+ if (!record.manifest)
368
+ return undefined;
369
+ const manifestDiagnostics = Array.isArray(record.manifest.diagnostics) ? record.manifest.diagnostics : [];
370
+ const hasInterruptedDiagnostic = manifestDiagnostics.some((item) => isRecord(item) && item.code === "AGENT_RUNTIME_INTERRUPTED")
371
+ || record.events.some((event) => eventDiagnosticCode(event.event) === "AGENT_RUNTIME_INTERRUPTED");
372
+ const owner = recordOwner(record.manifest);
373
+ const ownerInspection = inspectOwner(owner);
374
+ const activeNeedsRecovery = !isTerminalManifest(record.kind, record.manifest.status) && ownerInspection.status !== "live";
375
+ if (hasInterruptedDiagnostic || activeNeedsRecovery) {
376
+ return {
377
+ kind: record.kind,
378
+ id: record.id,
379
+ file: relativeFile(storageDir, manifestPath),
380
+ reason: hasInterruptedDiagnostic
381
+ ? "record was interrupted by runtime load and cannot be resumed"
382
+ : "record is non-terminal in storage and cannot be resumed by a new runtime",
383
+ };
384
+ }
385
+ return undefined;
386
+ }
387
+ function activeRecord(record, storageDir) {
388
+ if (!record.manifest || isTerminalManifest(record.kind, record.manifest.status))
389
+ return [];
390
+ const manifestPath = path.join(storageDir, record.kind === "run" ? "runs" : "goals", record.id, "manifest.json");
391
+ const ownerInspection = inspectOwner(recordOwner(record.manifest));
392
+ return [redactUnknown({
393
+ kind: record.kind,
394
+ id: record.id,
395
+ status: String(record.manifest.status),
396
+ file: relativeFile(storageDir, manifestPath),
397
+ ownerStatus: ownerInspection.status,
398
+ owner: ownerInspection.owner,
399
+ ownerAgeMs: ownerInspection.ageMs,
400
+ reason: ownerInspection.reason,
401
+ })];
402
+ }
403
+ function summarizeDiagnostics(records, storageDiagnostics = []) {
404
+ const byCode = {};
405
+ for (const diagnostic of storageDiagnostics)
406
+ increment(byCode, String(diagnostic.code));
407
+ for (const record of records) {
408
+ if (record.manifestIssue)
409
+ increment(byCode, "AGENT_STORE_RECORD_CORRUPT");
410
+ for (const _issue of record.eventIssues)
411
+ increment(byCode, "AGENT_EVENT_LOG_CORRUPT");
412
+ if (record.activeInterrupted)
413
+ increment(byCode, "AGENT_RUNTIME_INTERRUPTED");
414
+ for (const warning of record.warnings)
415
+ increment(byCode, warning.code);
416
+ for (const diagnostic of manifestDiagnostics(record.manifest))
417
+ increment(byCode, String(diagnostic.code));
418
+ for (const event of record.events) {
419
+ const code = eventDiagnosticCode(event.event);
420
+ if (code)
421
+ increment(byCode, code);
422
+ }
423
+ }
424
+ return {
425
+ total: Object.values(byCode).reduce((sum, count) => sum + count, 0),
426
+ byCode,
427
+ };
428
+ }
429
+ function readStoreDiagnostics(storageDir) {
430
+ const file = path.join(storageDir, "diagnostics.jsonl");
431
+ if (!existsSync(file))
432
+ return [];
433
+ const diagnostics = [];
434
+ const lines = readFileSync(file, "utf8").split(/\r?\n/u);
435
+ for (let index = 0; index < lines.length; index += 1) {
436
+ const line = lines[index];
437
+ if (!line.trim())
438
+ continue;
439
+ try {
440
+ const parsed = JSON.parse(line);
441
+ if (isRecord(parsed) && isRuntimeDiagnostic(parsed.diagnostic)) {
442
+ diagnostics.push(parsed.diagnostic);
443
+ }
444
+ else if (isRuntimeDiagnostic(parsed)) {
445
+ diagnostics.push(parsed);
446
+ }
447
+ else {
448
+ diagnostics.push({
449
+ code: "AGENT_STORE_RECORD_CORRUPT",
450
+ message: `diagnostics.jsonl:${index + 1} is not a runtime diagnostic`,
451
+ retryable: false,
452
+ });
453
+ }
454
+ }
455
+ catch (error) {
456
+ diagnostics.push({
457
+ code: "AGENT_STORE_RECORD_CORRUPT",
458
+ message: `diagnostics.jsonl:${index + 1} ${error instanceof Error ? error.message : String(error)}`,
459
+ retryable: false,
460
+ });
461
+ }
462
+ }
463
+ return diagnostics;
464
+ }
465
+ function isNonBlockingStorageDiagnostic(diagnostic) {
466
+ return diagnostic.code === "AGENT_STORE_REPAIR_APPLIED";
467
+ }
468
+ function diagnosticsForRecord(record) {
469
+ const diagnostics = [...manifestDiagnostics(record.manifest)];
470
+ if (record.manifestIssue)
471
+ diagnostics.push({ code: "AGENT_STORE_RECORD_CORRUPT", message: record.manifestIssue.reason, retryable: false });
472
+ for (const issue of record.eventIssues) {
473
+ diagnostics.push({ code: "AGENT_EVENT_LOG_CORRUPT", message: issue.reason, retryable: false });
474
+ }
475
+ for (const warning of record.warnings)
476
+ diagnostics.push({ code: warning.code, message: warning.message, retryable: false });
477
+ for (const event of record.events) {
478
+ const code = eventDiagnosticCode(event.event);
479
+ if (code)
480
+ diagnostics.push({ code, message: eventDiagnosticMessage(event.event), retryable: false });
481
+ }
482
+ return diagnostics;
483
+ }
484
+ function manifestDiagnostics(manifest) {
485
+ if (!manifest || !Array.isArray(manifest.diagnostics))
486
+ return [];
487
+ return manifest.diagnostics.filter(isRuntimeDiagnostic);
488
+ }
489
+ function attemptEvidenceFromGoalManifest(manifest) {
490
+ if (!manifest || !Array.isArray(manifest.tasks))
491
+ return [];
492
+ return manifest.tasks
493
+ .filter(isRecord)
494
+ .flatMap((task) => {
495
+ const evidence = isRecord(task.evidence) ? task.evidence : undefined;
496
+ const attempts = Array.isArray(evidence?.attempts) ? evidence.attempts : [];
497
+ return attempts.map((attempt) => ({
498
+ taskId: task.id,
499
+ title: task.title,
500
+ attempt,
501
+ }));
502
+ });
503
+ }
504
+ function adapterSummary(record) {
505
+ if (!record.manifest)
506
+ return { kind: record.kind, id: record.id, available: false };
507
+ if (record.kind === "run") {
508
+ const diagnostic = manifestDiagnostics(record.manifest).find((item) => item.promptTransport || item.streamFormat || item.parsedEventCount !== undefined || item.argv);
509
+ return {
510
+ kind: "run",
511
+ agentId: record.manifest.agentId,
512
+ status: record.manifest.status,
513
+ errorCode: record.manifest.errorCode,
514
+ exitCode: record.manifest.exitCode,
515
+ signal: record.manifest.signal,
516
+ argv: diagnostic?.argv,
517
+ promptTransport: diagnostic?.promptTransport,
518
+ streamFormat: diagnostic?.streamFormat,
519
+ parsedEventCount: diagnostic?.parsedEventCount,
520
+ actionableHints: diagnostic?.actionableHints,
521
+ };
522
+ }
523
+ const taskAgentIds = Array.isArray(record.manifest.tasks)
524
+ ? [...new Set(record.manifest.tasks.filter(isRecord).map((task) => task.agentId).filter((agentId) => typeof agentId === "string"))]
525
+ : [];
526
+ return {
527
+ kind: "goal",
528
+ status: record.manifest.status,
529
+ result: record.manifest.result,
530
+ taskAgentIds,
531
+ };
532
+ }
533
+ function supervisorSummary(record) {
534
+ const terminalEvents = record.events.filter((event) => record.kind === "run" ? event.event.type === "run_finished" : event.event.type === "goal_finished");
535
+ if (!record.manifest) {
536
+ return {
537
+ kind: record.kind,
538
+ id: record.id,
539
+ status: "unknown",
540
+ terminalEventCount: terminalEvents.length,
541
+ terminalReason: "manifest_unavailable",
542
+ };
543
+ }
544
+ const ownerInspection = inspectOwner(recordOwner(record.manifest));
545
+ const ownerSummary = {
546
+ ownerStatus: ownerInspection.status,
547
+ owner: ownerInspection.owner,
548
+ ownerAgeMs: ownerInspection.ageMs,
549
+ ownerReason: ownerInspection.reason,
550
+ };
551
+ if (record.kind === "run") {
552
+ return {
553
+ kind: "run",
554
+ id: record.id,
555
+ status: record.manifest.status,
556
+ result: runResultFromStatus(record.manifest.status),
557
+ errorCode: record.manifest.errorCode,
558
+ signal: record.manifest.signal,
559
+ terminalReason: terminalEventReason(terminalEvents) ?? terminalReason(record.manifest),
560
+ terminalEventCount: terminalEvents.length,
561
+ activeReloadRecovered: hasDiagnostic(record, "AGENT_RUNTIME_INTERRUPTED"),
562
+ lease: ownerSummary,
563
+ };
564
+ }
565
+ const tasks = Array.isArray(record.manifest.tasks) ? record.manifest.tasks.filter(isRecord) : [];
566
+ const taskStatusCounts = {};
567
+ for (const task of tasks) {
568
+ if (typeof task.status === "string")
569
+ increment(taskStatusCounts, task.status);
570
+ }
571
+ return {
572
+ kind: "goal",
573
+ id: record.id,
574
+ status: record.manifest.status,
575
+ result: record.manifest.result,
576
+ terminalReason: terminalEventReason(terminalEvents) ?? terminalReason(record.manifest),
577
+ terminalEventCount: terminalEvents.length,
578
+ activeReloadRecovered: hasDiagnostic(record, "AGENT_RUNTIME_INTERRUPTED"),
579
+ taskStatusCounts,
580
+ lease: ownerSummary,
581
+ };
582
+ }
583
+ function runResultFromStatus(status) {
584
+ if (status === "succeeded")
585
+ return "success";
586
+ if (status === "canceled")
587
+ return "cancelled";
588
+ if (status === "failed")
589
+ return "failed";
590
+ return undefined;
591
+ }
592
+ function terminalReason(manifest) {
593
+ const errorCode = typeof manifest.errorCode === "string" ? manifest.errorCode : undefined;
594
+ const signal = typeof manifest.signal === "string" ? manifest.signal : undefined;
595
+ if (manifest.status === "succeeded")
596
+ return terminalReasonFromResult("success", errorCode, signal);
597
+ if (manifest.status === "canceled")
598
+ return terminalReasonFromResult("cancelled", errorCode, signal);
599
+ if (manifest.status === "failed")
600
+ return terminalReasonFromResult("failed", errorCode, signal);
601
+ return "active";
602
+ }
603
+ function terminalEventReason(events) {
604
+ const event = events.at(-1)?.event;
605
+ if (event && "reason" in event && typeof event.reason === "string")
606
+ return event.reason;
607
+ return undefined;
608
+ }
609
+ function hasDiagnostic(record, code) {
610
+ return manifestDiagnostics(record.manifest).some((item) => item.code === code)
611
+ || record.events.some((event) => eventDiagnosticCode(event.event) === code);
612
+ }
613
+ function isTerminalManifest(kind, status) {
614
+ if (kind === "run")
615
+ return status === "succeeded" || status === "failed" || status === "canceled";
616
+ return status === "succeeded" || status === "failed" || status === "canceled";
617
+ }
618
+ function hasTerminalEvent(kind, events) {
619
+ return events.some((record) => kind === "run" ? record.event.type === "run_finished" : record.event.type === "goal_finished");
620
+ }
621
+ function eventType(event) {
622
+ return typeof event.type === "string" ? event.type : "unknown";
623
+ }
624
+ function eventDiagnosticCode(event) {
625
+ if ((event.type === "error" || event.type === "scheduler_error") && typeof event.code === "string")
626
+ return event.code;
627
+ return undefined;
628
+ }
629
+ function eventDiagnosticMessage(event) {
630
+ if ((event.type === "error" || event.type === "scheduler_error") && typeof event.message === "string")
631
+ return event.message;
632
+ return "Event diagnostic";
633
+ }
634
+ function increment(counts, code) {
635
+ counts[code] = (counts[code] ?? 0) + 1;
636
+ }
637
+ function recordOwner(manifest) {
638
+ const owner = manifest.owner;
639
+ if (!isRecord(owner))
640
+ return undefined;
641
+ if (typeof owner.runtimeInstanceId !== "string")
642
+ return undefined;
643
+ if (typeof owner.pid !== "number" || typeof owner.startedAt !== "number" || typeof owner.heartbeatAt !== "number")
644
+ return undefined;
645
+ return {
646
+ runtimeInstanceId: owner.runtimeInstanceId,
647
+ pid: owner.pid,
648
+ startedAt: owner.startedAt,
649
+ heartbeatAt: owner.heartbeatAt,
650
+ closedAt: typeof owner.closedAt === "number" ? owner.closedAt : undefined,
651
+ };
652
+ }
653
+ function repairActionFromIssue(storageDir, issue, action, dryRun) {
654
+ const planned = plannedRepairMetrics(storageDir, issue, action);
655
+ return {
656
+ kind: issue.kind,
657
+ id: issue.id,
658
+ file: issue.file,
659
+ action,
660
+ dryRun,
661
+ applied: false,
662
+ backupPath: null,
663
+ line: issue.line,
664
+ retainedEventCount: planned.retainedEventCount ?? issue.retainedEventCount ?? 0,
665
+ removedLineCount: planned.removedLineCount ?? issue.corruptLineCount ?? 0,
666
+ truncatedBytes: planned.truncatedBytes ?? 0,
667
+ lastGoodEventId: issue.lastGoodEventId,
668
+ lastGoodSequence: issue.lastGoodSequence,
669
+ reason: issue.reason,
670
+ redactedTailPreview: issue.redactedTailPreview,
671
+ diagnostics: [],
672
+ };
673
+ }
674
+ function plannedRepairMetrics(storageDir, issue, action) {
675
+ if (action === "manual_review")
676
+ return { retainedEventCount: issue.retainedEventCount ?? 0, removedLineCount: 0, truncatedBytes: 0 };
677
+ const absolute = path.join(storageDir, issue.file);
678
+ if (!isInside(storageDir, absolute) || !existsSync(absolute))
679
+ return {};
680
+ try {
681
+ const scan = scanJsonlForRepair(absolute);
682
+ const matched = issue.line === undefined ? undefined : scan.issues.find((candidate) => candidate.line === issue.line);
683
+ if (!matched)
684
+ return {};
685
+ return {
686
+ retainedEventCount: matched.retainedEventCount,
687
+ removedLineCount: 1,
688
+ truncatedBytes: matched.partialTail ? Math.max(0, Buffer.byteLength(scan.text) - Buffer.byteLength(scan.text.slice(0, matched.startOffset))) : 0,
689
+ };
690
+ }
691
+ catch {
692
+ return {};
693
+ }
694
+ }
695
+ function applyRepairActions(storageDir, actions, faults) {
696
+ const repairable = actions.filter((action) => action.action === "truncate_partial_tail" || action.action === "isolate_corrupt_line");
697
+ const byFile = new Map();
698
+ for (const action of repairable) {
699
+ const actionsForFile = byFile.get(action.file) ?? [];
700
+ actionsForFile.push(action);
701
+ byFile.set(action.file, actionsForFile);
702
+ }
703
+ const backupRoot = path.join("repair-backups", `${new Date().toISOString().replace(/[:.]/gu, "-")}-${randomUUID()}`);
704
+ for (const [relative, fileActions] of byFile) {
705
+ const absolute = path.join(storageDir, relative);
706
+ if (!isInside(storageDir, absolute) || !existsSync(absolute))
707
+ continue;
708
+ const scan = scanJsonlForRepair(absolute);
709
+ const issueLines = new Set(scan.issues.map((issue) => issue.line));
710
+ const matchingActions = fileActions.filter((action) => action.line !== undefined && issueLines.has(action.line));
711
+ if (matchingActions.length === 0)
712
+ continue;
713
+ const backupPath = path.join(backupRoot, relative);
714
+ const absoluteBackup = path.join(storageDir, backupPath);
715
+ const diagnostics = [];
716
+ mkdirSync(path.dirname(absoluteBackup), { recursive: true });
717
+ faults?.beforeBackupWrite?.(absoluteBackup);
718
+ atomicWriteTextFile(absoluteBackup, scan.text, diagnostics);
719
+ for (const action of matchingActions) {
720
+ action.backupPath = backupPath;
721
+ action.diagnostics = diagnostics;
722
+ }
723
+ const partialOnly = scan.issues.length === 1 && scan.issues[0]?.partialTail === true;
724
+ const repaired = partialOnly
725
+ ? scan.text.slice(0, scan.issues[0]?.startOffset ?? scan.text.length)
726
+ : scan.validLines.map((line) => `${JSON.stringify(JSON.parse(line.text))}\n`).join("");
727
+ faults?.beforeRepairRewrite?.(absolute);
728
+ atomicWriteTextFile(absolute, repaired, diagnostics);
729
+ const truncatedBytes = Math.max(0, Buffer.byteLength(scan.text) - Buffer.byteLength(repaired));
730
+ const removedLineCount = scan.issues.length;
731
+ const retainedEventCount = scan.validLines.length;
732
+ for (const action of matchingActions) {
733
+ action.applied = true;
734
+ action.retainedEventCount = retainedEventCount;
735
+ action.removedLineCount = matchingActions.length || removedLineCount;
736
+ action.truncatedBytes = action.action === "truncate_partial_tail" ? truncatedBytes : 0;
737
+ action.diagnostics = diagnostics;
738
+ }
739
+ }
740
+ }
741
+ function persistRepairFailureDiagnostic(storageDir, error) {
742
+ const diagnostic = redactUnknown({
743
+ timestamp: Date.now(),
744
+ diagnostic: {
745
+ code: "AGENT_STORE_REPAIR_FAILED",
746
+ message: `Store repair apply failed: ${errorMessage(error)}`,
747
+ retryable: false,
748
+ actionableHints: ["Inspect store-health output, keep repair-backups, and rerun store-repair after fixing the underlying filesystem error."],
749
+ },
750
+ });
751
+ try {
752
+ appendFileSync(path.join(storageDir, "diagnostics.jsonl"), `${JSON.stringify(diagnostic)}\n`, "utf8");
753
+ }
754
+ catch {
755
+ // Failure diagnostics are best-effort; the repair report still carries blockedReason.
756
+ }
757
+ }
758
+ function persistRepairDiagnostic(storageDir, actions) {
759
+ const appliedActions = actions.filter((action) => action.applied);
760
+ if (appliedActions.length === 0)
761
+ return;
762
+ const diagnostic = redactUnknown({
763
+ timestamp: Date.now(),
764
+ diagnostic: {
765
+ code: "AGENT_STORE_REPAIR_APPLIED",
766
+ message: `Store repair applied ${appliedActions.length} action(s).`,
767
+ retryable: false,
768
+ actionableHints: appliedActions.map((action) => `${action.kind}:${action.id} ${action.action} file=${action.file} backup=${action.backupPath ?? "<none>"} removed=${action.removedLineCount} truncatedBytes=${action.truncatedBytes}`),
769
+ },
770
+ });
771
+ try {
772
+ appendFileSync(path.join(storageDir, "diagnostics.jsonl"), `${JSON.stringify(diagnostic)}\n`, "utf8");
773
+ }
774
+ catch {
775
+ // Repair diagnostics are best-effort; the event-log repair has already completed.
776
+ }
777
+ }
778
+ function scanJsonlForRepair(file) {
779
+ const text = readFileSync(file, "utf8");
780
+ const lines = jsonlLines(text);
781
+ const lastNonEmptyLine = [...lines].reverse().find((line) => line.text.trim())?.line ?? -1;
782
+ const validLines = [];
783
+ const issues = [];
784
+ for (const line of lines) {
785
+ if (!line.text.trim())
786
+ continue;
787
+ try {
788
+ const parsed = JSON.parse(line.text);
789
+ if (!isReplayEvent(parsed)) {
790
+ issues.push(repairIssue(line, "line is not a replay event", validLines, false));
791
+ continue;
792
+ }
793
+ validLines.push(line);
794
+ }
795
+ catch (error) {
796
+ const reason = error instanceof Error ? error.message : String(error);
797
+ const partialTail = isPartialTailForRepair(text, line.line, lastNonEmptyLine, reason);
798
+ issues.push(repairIssue(line, reason, validLines, partialTail));
799
+ if (partialTail)
800
+ break;
801
+ }
802
+ }
803
+ return { text, validLines, issues };
804
+ }
805
+ function repairIssue(line, reason, validLines, partialTail) {
806
+ let lastGoodEventId;
807
+ let lastGoodSequence;
808
+ const lastGood = validLines.at(-1);
809
+ if (lastGood) {
810
+ try {
811
+ const parsed = JSON.parse(lastGood.text);
812
+ lastGoodEventId = typeof parsed.id === "number" ? parsed.id : undefined;
813
+ lastGoodSequence = typeof parsed.sequence === "number" ? parsed.sequence : lastGoodEventId;
814
+ }
815
+ catch {
816
+ // Last good line was already parsed above.
817
+ }
818
+ }
819
+ return {
820
+ line: line.line,
821
+ reason,
822
+ partialTail,
823
+ retainedEventCount: validLines.length,
824
+ lastGoodEventId,
825
+ lastGoodSequence,
826
+ redactedTailPreview: redactUnknown(line.text.slice(0, 256)),
827
+ startOffset: line.startOffset,
828
+ };
829
+ }
830
+ function jsonlLines(text) {
831
+ const lines = [];
832
+ let start = 0;
833
+ let line = 1;
834
+ while (start < text.length) {
835
+ const newline = text.indexOf("\n", start);
836
+ const endOffset = newline === -1 ? text.length : newline + 1;
837
+ const raw = text.slice(start, endOffset);
838
+ const lineText = raw.endsWith("\n") ? raw.slice(0, raw.endsWith("\r\n") ? -2 : -1) : raw;
839
+ lines.push({ line, startOffset: start, endOffset, text: lineText });
840
+ start = endOffset;
841
+ line += 1;
842
+ }
843
+ return lines;
844
+ }
845
+ function isPartialTailForRepair(text, line, lastNonEmptyLine, reason) {
846
+ return line === lastNonEmptyLine
847
+ && !/\r?\n$/u.test(text)
848
+ && /(end of JSON input|unterminated|string|property name|after|expected|unexpected)/iu.test(reason);
849
+ }
850
+ function isReplayEvent(value) {
851
+ if (!value || typeof value !== "object")
852
+ return false;
853
+ const record = value;
854
+ return typeof record.id === "number" && typeof record.timestamp === "number" && Boolean(record.event);
855
+ }
856
+ function atomicWriteTextFile(file, text, diagnostics) {
857
+ const tmp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${randomUUID()}.tmp`);
858
+ const fd = openSync(tmp, "w");
859
+ let renamed = false;
860
+ try {
861
+ writeSync(fd, text, undefined, "utf8");
862
+ syncFileDescriptor(fd, diagnostics);
863
+ }
864
+ finally {
865
+ closeSync(fd);
866
+ }
867
+ try {
868
+ renameSync(tmp, file);
869
+ renamed = true;
870
+ syncPath(path.dirname(file), diagnostics);
871
+ }
872
+ finally {
873
+ if (!renamed) {
874
+ try {
875
+ unlinkSync(tmp);
876
+ }
877
+ catch {
878
+ // Temp cleanup is best-effort; the original target was not replaced.
879
+ }
880
+ }
881
+ }
882
+ }
883
+ function syncFileDescriptor(fd, diagnostics) {
884
+ try {
885
+ fdatasyncSync(fd);
886
+ }
887
+ catch (fdatasyncError) {
888
+ try {
889
+ fsyncSync(fd);
890
+ }
891
+ catch (fsyncError) {
892
+ diagnostics.push({
893
+ code: "AGENT_STORAGE_SYNC_FALLBACK",
894
+ message: `repair file sync fallback: fdatasync failed (${errorMessage(fdatasyncError)}); fsync failed (${errorMessage(fsyncError)})`,
895
+ retryable: false,
896
+ });
897
+ }
898
+ }
899
+ }
900
+ function syncPath(targetPath, diagnostics) {
901
+ let fd;
902
+ try {
903
+ const stat = statSync(targetPath);
904
+ fd = openSync(stat.isDirectory() ? targetPath : path.dirname(targetPath), "r");
905
+ fsyncSync(fd);
906
+ }
907
+ catch (error) {
908
+ diagnostics.push({
909
+ code: "AGENT_STORAGE_SYNC_FALLBACK",
910
+ message: `repair directory sync skipped (${errorMessage(error)})`,
911
+ retryable: false,
912
+ });
913
+ }
914
+ finally {
915
+ if (fd !== undefined)
916
+ closeSync(fd);
917
+ }
918
+ }
919
+ function cloneSummary(summary) {
920
+ return {
921
+ total: summary.total,
922
+ byCode: { ...summary.byCode },
923
+ };
924
+ }
925
+ function isInside(parent, child) {
926
+ const relative = path.relative(parent, child);
927
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
928
+ }
929
+ function errorMessage(error) {
930
+ return error instanceof Error ? error.message : String(error);
931
+ }
932
+ function compareReplayEvents(left, right) {
933
+ return (left.sequence - right.sequence) || (left.id - right.id) || (left.timestamp - right.timestamp);
934
+ }
935
+ function relativeFile(storageDir, file) {
936
+ return path.relative(storageDir, file).split(path.sep).join("/");
937
+ }
938
+ function isRuntimeDiagnostic(value) {
939
+ return isRecord(value) && typeof value.code === "string" && typeof value.message === "string";
940
+ }
941
+ //# sourceMappingURL=store-inspection.js.map