@aexhq/sdk 0.13.6

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 (112) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +160 -0
  3. package/dist/_contracts/connection-ticket.d.ts +21 -0
  4. package/dist/_contracts/connection-ticket.js +49 -0
  5. package/dist/_contracts/event-envelope.d.ts +276 -0
  6. package/dist/_contracts/event-envelope.js +324 -0
  7. package/dist/_contracts/event-stream-client.d.ts +47 -0
  8. package/dist/_contracts/event-stream-client.js +141 -0
  9. package/dist/_contracts/http.d.ts +35 -0
  10. package/dist/_contracts/http.js +114 -0
  11. package/dist/_contracts/index.d.ts +28 -0
  12. package/dist/_contracts/index.js +29 -0
  13. package/dist/_contracts/managed-key.d.ts +74 -0
  14. package/dist/_contracts/managed-key.js +110 -0
  15. package/dist/_contracts/operations.d.ts +237 -0
  16. package/dist/_contracts/operations.js +632 -0
  17. package/dist/_contracts/provider-support.d.ts +220 -0
  18. package/dist/_contracts/provider-support.js +90 -0
  19. package/dist/_contracts/proxy-protocol.d.ts +257 -0
  20. package/dist/_contracts/proxy-protocol.js +234 -0
  21. package/dist/_contracts/proxy-validation.d.ts +19 -0
  22. package/dist/_contracts/proxy-validation.js +51 -0
  23. package/dist/_contracts/run-artifacts.d.ts +47 -0
  24. package/dist/_contracts/run-artifacts.js +101 -0
  25. package/dist/_contracts/run-config.d.ts +304 -0
  26. package/dist/_contracts/run-config.js +659 -0
  27. package/dist/_contracts/run-cost.d.ts +125 -0
  28. package/dist/_contracts/run-cost.js +616 -0
  29. package/dist/_contracts/run-custody.d.ts +226 -0
  30. package/dist/_contracts/run-custody.js +465 -0
  31. package/dist/_contracts/run-record.d.ts +127 -0
  32. package/dist/_contracts/run-record.js +177 -0
  33. package/dist/_contracts/run-retention.d.ts +213 -0
  34. package/dist/_contracts/run-retention.js +484 -0
  35. package/dist/_contracts/run-unit.d.ts +194 -0
  36. package/dist/_contracts/run-unit.js +215 -0
  37. package/dist/_contracts/runner-event.d.ts +114 -0
  38. package/dist/_contracts/runner-event.js +187 -0
  39. package/dist/_contracts/runtime-manifest.d.ts +106 -0
  40. package/dist/_contracts/runtime-manifest.js +98 -0
  41. package/dist/_contracts/runtime-security-profile.d.ts +27 -0
  42. package/dist/_contracts/runtime-security-profile.js +82 -0
  43. package/dist/_contracts/runtime-sizes.d.ts +144 -0
  44. package/dist/_contracts/runtime-sizes.js +136 -0
  45. package/dist/_contracts/runtime-types.d.ts +212 -0
  46. package/dist/_contracts/runtime-types.js +2 -0
  47. package/dist/_contracts/sdk-errors.d.ts +34 -0
  48. package/dist/_contracts/sdk-errors.js +52 -0
  49. package/dist/_contracts/sdk-secrets.d.ts +31 -0
  50. package/dist/_contracts/sdk-secrets.js +220 -0
  51. package/dist/_contracts/side-effect-audit.d.ts +129 -0
  52. package/dist/_contracts/side-effect-audit.js +494 -0
  53. package/dist/_contracts/sse.d.ts +74 -0
  54. package/dist/_contracts/sse.js +0 -0
  55. package/dist/_contracts/stable.d.ts +26 -0
  56. package/dist/_contracts/stable.js +44 -0
  57. package/dist/_contracts/status.d.ts +19 -0
  58. package/dist/_contracts/status.js +61 -0
  59. package/dist/_contracts/submission.d.ts +383 -0
  60. package/dist/_contracts/submission.js +1380 -0
  61. package/dist/agents-md.d.ts +46 -0
  62. package/dist/agents-md.js +83 -0
  63. package/dist/agents-md.js.map +1 -0
  64. package/dist/asset-upload.d.ts +66 -0
  65. package/dist/asset-upload.js +168 -0
  66. package/dist/asset-upload.js.map +1 -0
  67. package/dist/bundle.d.ts +33 -0
  68. package/dist/bundle.js +89 -0
  69. package/dist/bundle.js.map +1 -0
  70. package/dist/cli.mjs +4140 -0
  71. package/dist/cli.mjs.sha256 +1 -0
  72. package/dist/client.d.ts +460 -0
  73. package/dist/client.js +857 -0
  74. package/dist/client.js.map +1 -0
  75. package/dist/fetch-archive.d.ts +16 -0
  76. package/dist/fetch-archive.js +170 -0
  77. package/dist/fetch-archive.js.map +1 -0
  78. package/dist/file.d.ts +57 -0
  79. package/dist/file.js +153 -0
  80. package/dist/file.js.map +1 -0
  81. package/dist/index.d.ts +30 -0
  82. package/dist/index.js +34 -0
  83. package/dist/index.js.map +1 -0
  84. package/dist/mcp-server.d.ts +84 -0
  85. package/dist/mcp-server.js +114 -0
  86. package/dist/mcp-server.js.map +1 -0
  87. package/dist/node-fs.d.ts +12 -0
  88. package/dist/node-fs.js +44 -0
  89. package/dist/node-fs.js.map +1 -0
  90. package/dist/proxy-endpoint.d.ts +131 -0
  91. package/dist/proxy-endpoint.js +147 -0
  92. package/dist/proxy-endpoint.js.map +1 -0
  93. package/dist/skill.d.ts +117 -0
  94. package/dist/skill.js +169 -0
  95. package/dist/skill.js.map +1 -0
  96. package/dist/version.d.ts +9 -0
  97. package/dist/version.js +10 -0
  98. package/dist/version.js.map +1 -0
  99. package/docs/cleanup.md +38 -0
  100. package/docs/credentials.md +153 -0
  101. package/docs/events.md +76 -0
  102. package/docs/mcp.md +47 -0
  103. package/docs/outputs.md +157 -0
  104. package/docs/product-boundaries.md +57 -0
  105. package/docs/provider-runtime-capabilities.md +103 -0
  106. package/docs/quickstart.md +110 -0
  107. package/docs/release.md +99 -0
  108. package/docs/run-config.md +53 -0
  109. package/docs/run-record.md +39 -0
  110. package/docs/skills.md +139 -0
  111. package/docs/testing.md +29 -0
  112. package/package.json +47 -0
@@ -0,0 +1,484 @@
1
+ import { isTerminalRunStatus } from "./status.js";
2
+ export const RUN_RETENTION_SCHEMA_VERSION = 1;
3
+ export const RUN_DELETION_MANIFEST_KIND = "aex.run_deletion_manifest.v1";
4
+ export const RUN_DELETION_JOB_KIND = "aex.run_deletion_job.v1";
5
+ export const RUN_DELETION_MANIFEST_CONTENT_TYPE = "application/json; charset=utf-8";
6
+ export const RUN_RETENTION_REDACTION_SCANNER_VERSION = 1;
7
+ export const RUN_DELETION_REASONS = ["manual_delete", "retention_gc"];
8
+ export const RUN_DELETION_MANIFEST_MODES = ["dry_run", "final"];
9
+ export const RUN_DELETION_CANDIDATE_STATUSES = ["selected", "blocked"];
10
+ export const RUN_DELETION_BLOCKERS = [
11
+ "non_terminal",
12
+ "already_deleted",
13
+ "concurrent_delete",
14
+ "retention_policy_disabled",
15
+ "unexpired",
16
+ "held",
17
+ "retention_exempt",
18
+ "unresolved_cleanup",
19
+ "unresolved_custody"
20
+ ];
21
+ export const RUN_DELETION_COUNT_CLASSES = [
22
+ "r2_objects",
23
+ "outputs",
24
+ "logs",
25
+ "events",
26
+ "assets",
27
+ "db_event_rows",
28
+ "db_output_rows",
29
+ "capture_failures",
30
+ "storage_samples",
31
+ "custody_manifests"
32
+ ];
33
+ export const RUN_DELETION_COUNT_STATUSES = ["counted", "not_counted", "partial", "failed"];
34
+ export const RUN_DELETION_JOB_STATUSES = [
35
+ "queued",
36
+ "planning",
37
+ "blocked",
38
+ "manifest_written",
39
+ "deleting",
40
+ "delete_failed",
41
+ "completed",
42
+ "failed"
43
+ ];
44
+ export const RUN_DELETION_PROOF_STATUSES = [
45
+ "not_started",
46
+ "running",
47
+ "completed",
48
+ "failed"
49
+ ];
50
+ export const RUN_DELETION_WRITE_STATUSES = ["not_written", "written", "write_failed"];
51
+ export const RUN_RETENTION_EXCLUDED_VALUE_CLASSES = [
52
+ "raw_paths",
53
+ "object_keys",
54
+ "filenames",
55
+ "object_sizes",
56
+ "hashes",
57
+ "provider_ids",
58
+ "vault_ids",
59
+ "resource_ids",
60
+ "resource_handles",
61
+ "signed_urls"
62
+ ];
63
+ export class RunRetentionValidationError extends Error {
64
+ code = "run_retention_contract_invalid";
65
+ constructor(message) {
66
+ super(message);
67
+ this.name = "RunRetentionValidationError";
68
+ }
69
+ }
70
+ export class RunRetentionRedactionError extends Error {
71
+ code = "run_retention_payload_not_public_safe";
72
+ findings;
73
+ constructor(findings) {
74
+ super(`run retention payload contains non-public data at ${formatFindingPaths(findings)}`);
75
+ this.name = "RunRetentionRedactionError";
76
+ this.findings = Object.freeze([...findings]);
77
+ }
78
+ }
79
+ export class FakeRunDeletionManifestObjectStore {
80
+ #objects = new Map();
81
+ async putRunDeletionManifestObject(object) {
82
+ assertPublicSafeRunRetentionPayload(object.manifest);
83
+ this.#objects.set(object.runId, cloneJson(object.manifest));
84
+ }
85
+ getByRunId(runId) {
86
+ return this.get(runId);
87
+ }
88
+ get(runId) {
89
+ const object = this.#objects.get(runId);
90
+ return object ? cloneJson(object) : undefined;
91
+ }
92
+ listRunIds() {
93
+ return Object.freeze([...this.#objects.keys()].sort());
94
+ }
95
+ }
96
+ export function createRunDeletionManifestWriter(store) {
97
+ return {
98
+ async writeRunDeletionManifest(input) {
99
+ return writeRunDeletionManifest(store, input);
100
+ }
101
+ };
102
+ }
103
+ export async function writeRunDeletionManifest(store, input) {
104
+ const manifest = buildRunDeletionManifest(input);
105
+ await store.putRunDeletionManifestObject({
106
+ runId: manifest.run.runId,
107
+ workspaceId: manifest.run.workspaceId,
108
+ contentType: RUN_DELETION_MANIFEST_CONTENT_TYPE,
109
+ manifest
110
+ });
111
+ return Object.freeze({
112
+ status: "written",
113
+ schemaVersion: RUN_RETENTION_SCHEMA_VERSION,
114
+ runId: manifest.run.runId,
115
+ workspaceId: manifest.run.workspaceId,
116
+ writtenAt: manifest.generatedAt,
117
+ mode: manifest.mode
118
+ });
119
+ }
120
+ export function buildRunRetentionPolicy(input = {}) {
121
+ if (input.automaticDeletion) {
122
+ if (input.retentionDays === undefined) {
123
+ throw new RunRetentionValidationError("automatic retention deletion requires an explicit positive retentionDays value");
124
+ }
125
+ return Object.freeze({
126
+ mode: "delete_after_days",
127
+ manualDelete: "enabled",
128
+ automaticDeletion: "enabled",
129
+ retentionDays: positiveInteger(input.retentionDays, "policy.retentionDays")
130
+ });
131
+ }
132
+ if (input.retentionDays !== undefined) {
133
+ throw new RunRetentionValidationError("retentionDays is not allowed when automatic retention deletion is disabled");
134
+ }
135
+ return Object.freeze({
136
+ mode: "retain_indefinitely",
137
+ manualDelete: "enabled",
138
+ automaticDeletion: "disabled"
139
+ });
140
+ }
141
+ export function evaluateRunDeletionCandidate(input) {
142
+ const now = assertTimestamp(input.now, "candidate.now");
143
+ const run = normalizeCandidateRun(input.run);
144
+ const policy = normalizePolicy(input.policy);
145
+ const blockers = [];
146
+ addRunBlockers(blockers, run, now);
147
+ let eligibleAt;
148
+ if (input.reason === "retention_gc") {
149
+ if (policy.mode !== "delete_after_days" || policy.retentionDays === undefined) {
150
+ blockers.push(blocker("retention_policy_disabled", now));
151
+ }
152
+ else if (run.terminalAt) {
153
+ eligibleAt = addDaysIso(run.terminalAt, policy.retentionDays);
154
+ if (Date.parse(now) < Date.parse(eligibleAt)) {
155
+ blockers.push(blocker("unexpired", now));
156
+ }
157
+ }
158
+ }
159
+ else {
160
+ eligibleAt = now;
161
+ }
162
+ return Object.freeze({
163
+ status: blockers.length === 0 ? "selected" : "blocked",
164
+ reason: input.reason,
165
+ evaluatedAt: now,
166
+ ...(eligibleAt ? { eligibleAt } : {}),
167
+ blockers: Object.freeze(blockers)
168
+ });
169
+ }
170
+ export function buildRunDeletionManifest(input) {
171
+ const candidate = input.candidate ??
172
+ evaluateRunDeletionCandidate({
173
+ run: input.run,
174
+ reason: input.request.reason,
175
+ now: input.generatedAt,
176
+ ...(input.policy ? { policy: input.policy } : {})
177
+ });
178
+ const normalizedCandidate = normalizeCandidate(candidate);
179
+ const run = normalizeManifestRun(input.run, normalizedCandidate.eligibleAt);
180
+ const summary = buildRunDeletionSummary(input.counts ?? [], normalizedCandidate);
181
+ const manifest = Object.freeze({
182
+ schemaVersion: RUN_RETENTION_SCHEMA_VERSION,
183
+ kind: RUN_DELETION_MANIFEST_KIND,
184
+ generatedAt: assertTimestamp(input.generatedAt, "manifest.generatedAt"),
185
+ mode: input.mode,
186
+ run,
187
+ request: normalizeRequest(input.request),
188
+ candidate: normalizedCandidate,
189
+ summary,
190
+ redaction: Object.freeze({
191
+ policy: "counts_status_timestamps_only",
192
+ scannerVersion: RUN_RETENTION_REDACTION_SCANNER_VERSION,
193
+ excludes: Object.freeze([...RUN_RETENTION_EXCLUDED_VALUE_CLASSES])
194
+ })
195
+ });
196
+ assertPublicSafeRunRetentionPayload(manifest);
197
+ return manifest;
198
+ }
199
+ export function assertRunDeletionOrder(proof) {
200
+ const manifest = normalizeManifestProof(proof.manifest);
201
+ const purge = normalizePurgeProof(proof.purge);
202
+ const purgeStarted = purge.status === "running" || purge.status === "completed" || purge.status === "failed";
203
+ if (purgeStarted && manifest.status !== "written") {
204
+ throw new RunRetentionValidationError("run deletion cannot purge assets before the deletion manifest is written");
205
+ }
206
+ if (purgeStarted && manifest.mode !== "final") {
207
+ throw new RunRetentionValidationError("run deletion cannot purge assets from a dry-run deletion manifest");
208
+ }
209
+ }
210
+ export function buildRunDeletionJob(input) {
211
+ assertRunDeletionOrder(input.order);
212
+ const job = Object.freeze({
213
+ schemaVersion: RUN_RETENTION_SCHEMA_VERSION,
214
+ kind: RUN_DELETION_JOB_KIND,
215
+ jobId: assertSafeIdentifier(input.jobId, "job.jobId"),
216
+ runId: assertSafeIdentifier(input.runId, "job.runId"),
217
+ workspaceId: assertSafeIdentifier(input.workspaceId, "job.workspaceId"),
218
+ reason: input.reason,
219
+ mode: input.mode,
220
+ status: input.status,
221
+ createdAt: assertTimestamp(input.createdAt, "job.createdAt"),
222
+ ...(input.updatedAt ? { updatedAt: assertTimestamp(input.updatedAt, "job.updatedAt") } : {}),
223
+ order: normalizeOrderProof(input.order),
224
+ ...(input.candidate ? { candidate: normalizeCandidate(input.candidate) } : {}),
225
+ ...(input.summary ? { summary: normalizeSummary(input.summary) } : {})
226
+ });
227
+ assertPublicSafeRunRetentionPayload(job);
228
+ return job;
229
+ }
230
+ export function scanRunRetentionPayloadForSensitiveValues(input) {
231
+ const findings = [];
232
+ visitRetentionValue(input, "$", findings);
233
+ return Object.freeze(findings);
234
+ }
235
+ export function assertPublicSafeRunRetentionPayload(input) {
236
+ const findings = scanRunRetentionPayloadForSensitiveValues(input);
237
+ if (findings.length > 0) {
238
+ throw new RunRetentionRedactionError(findings);
239
+ }
240
+ }
241
+ function normalizePolicy(input) {
242
+ if (!input) {
243
+ return buildRunRetentionPolicy();
244
+ }
245
+ if ("mode" in input) {
246
+ if (input.mode === "delete_after_days") {
247
+ if (input.retentionDays === undefined) {
248
+ throw new RunRetentionValidationError("delete_after_days retention policy requires an explicit retentionDays value");
249
+ }
250
+ return buildRunRetentionPolicy({
251
+ automaticDeletion: true,
252
+ retentionDays: input.retentionDays
253
+ });
254
+ }
255
+ if (input.retentionDays !== undefined) {
256
+ throw new RunRetentionValidationError("retain_indefinitely retention policy cannot include retentionDays");
257
+ }
258
+ return buildRunRetentionPolicy();
259
+ }
260
+ return buildRunRetentionPolicy(input);
261
+ }
262
+ function normalizeCandidateRun(input) {
263
+ return Object.freeze({
264
+ runId: assertSafeIdentifier(input.runId, "run.runId"),
265
+ workspaceId: assertSafeIdentifier(input.workspaceId, "run.workspaceId"),
266
+ status: assertSafeMetadataString(input.status, "run.status"),
267
+ ...(input.createdAt ? { createdAt: assertTimestamp(input.createdAt, "run.createdAt") } : {}),
268
+ ...(input.terminalAt ? { terminalAt: assertTimestamp(input.terminalAt, "run.terminalAt") } : {}),
269
+ ...(input.pendingDeleteAt ? { pendingDeleteAt: assertTimestamp(input.pendingDeleteAt, "run.pendingDeleteAt") } : {}),
270
+ ...(input.deletedAt ? { deletedAt: assertTimestamp(input.deletedAt, "run.deletedAt") } : {}),
271
+ ...(input.held !== undefined ? { held: input.held } : {}),
272
+ ...(input.retentionExempt !== undefined ? { retentionExempt: input.retentionExempt } : {}),
273
+ ...(input.unresolvedCleanup !== undefined ? { unresolvedCleanup: input.unresolvedCleanup } : {}),
274
+ ...(input.unresolvedCustody !== undefined ? { unresolvedCustody: input.unresolvedCustody } : {})
275
+ });
276
+ }
277
+ function normalizeManifestRun(input, eligibleAt) {
278
+ const run = normalizeCandidateRun(input);
279
+ return Object.freeze({
280
+ runId: run.runId,
281
+ workspaceId: run.workspaceId,
282
+ status: run.status,
283
+ ...(run.createdAt ? { createdAt: run.createdAt } : {}),
284
+ ...(run.terminalAt ? { terminalAt: run.terminalAt } : {}),
285
+ ...(eligibleAt ? { eligibleAt: assertTimestamp(eligibleAt, "run.eligibleAt") } : {}),
286
+ ...(run.pendingDeleteAt ? { pendingDeleteAt: run.pendingDeleteAt } : {}),
287
+ ...(run.deletedAt ? { deletedAt: run.deletedAt } : {})
288
+ });
289
+ }
290
+ function normalizeRequest(input) {
291
+ return Object.freeze({
292
+ reason: input.reason,
293
+ actorClass: input.actorClass
294
+ });
295
+ }
296
+ function normalizeCandidate(input) {
297
+ return Object.freeze({
298
+ status: input.status,
299
+ reason: input.reason,
300
+ evaluatedAt: assertTimestamp(input.evaluatedAt, "candidate.evaluatedAt"),
301
+ ...(input.eligibleAt ? { eligibleAt: assertTimestamp(input.eligibleAt, "candidate.eligibleAt") } : {}),
302
+ blockers: Object.freeze(input.blockers.map(normalizeBlocker))
303
+ });
304
+ }
305
+ function normalizeBlocker(input) {
306
+ return Object.freeze({
307
+ code: input.code,
308
+ observedAt: assertTimestamp(input.observedAt, "candidate.blocker.observedAt")
309
+ });
310
+ }
311
+ function normalizeCount(input) {
312
+ return Object.freeze({
313
+ class: assertSafeMetadataString(input.class, "summary.count.class"),
314
+ count: nonNegativeInteger(input.count, "summary.count.count"),
315
+ status: input.status,
316
+ ...(input.countedAt ? { countedAt: assertTimestamp(input.countedAt, "summary.count.countedAt") } : {}),
317
+ ...(input.errorClass ? { errorClass: assertSafeMetadataString(input.errorClass, "summary.count.errorClass") } : {})
318
+ });
319
+ }
320
+ function buildRunDeletionSummary(input, candidate) {
321
+ const counts = Object.freeze(input.map(normalizeCount));
322
+ return Object.freeze({
323
+ totalCount: counts.reduce((sum, item) => sum + item.count, 0),
324
+ failedCountClasses: counts.filter((item) => item.status === "failed").length,
325
+ partialCountClasses: counts.filter((item) => item.status === "partial").length,
326
+ blockerCount: candidate.blockers.length,
327
+ counts
328
+ });
329
+ }
330
+ function normalizeSummary(input) {
331
+ return Object.freeze({
332
+ totalCount: nonNegativeInteger(input.totalCount, "summary.totalCount"),
333
+ failedCountClasses: nonNegativeInteger(input.failedCountClasses, "summary.failedCountClasses"),
334
+ partialCountClasses: nonNegativeInteger(input.partialCountClasses, "summary.partialCountClasses"),
335
+ blockerCount: nonNegativeInteger(input.blockerCount, "summary.blockerCount"),
336
+ counts: Object.freeze(input.counts.map(normalizeCount))
337
+ });
338
+ }
339
+ function normalizeOrderProof(input) {
340
+ return Object.freeze({
341
+ manifest: normalizeManifestProof(input.manifest),
342
+ purge: normalizePurgeProof(input.purge)
343
+ });
344
+ }
345
+ function normalizeManifestProof(input) {
346
+ return Object.freeze({
347
+ status: input.status,
348
+ ...(input.mode ? { mode: input.mode } : {}),
349
+ ...(input.writtenAt ? { writtenAt: assertTimestamp(input.writtenAt, "proof.manifest.writtenAt") } : {})
350
+ });
351
+ }
352
+ function normalizePurgeProof(input) {
353
+ return Object.freeze({
354
+ status: input.status,
355
+ ...(input.startedAt ? { startedAt: assertTimestamp(input.startedAt, "proof.purge.startedAt") } : {}),
356
+ ...(input.completedAt ? { completedAt: assertTimestamp(input.completedAt, "proof.purge.completedAt") } : {}),
357
+ ...(input.deletedObjectCount !== undefined
358
+ ? { deletedObjectCount: nonNegativeInteger(input.deletedObjectCount, "proof.purge.deletedObjectCount") }
359
+ : {})
360
+ });
361
+ }
362
+ function addRunBlockers(blockers, run, observedAt) {
363
+ if (run.status === "deleted") {
364
+ blockers.push(blocker("already_deleted", observedAt));
365
+ }
366
+ else if (run.status === "pending_delete" || run.pendingDeleteAt) {
367
+ blockers.push(blocker("concurrent_delete", observedAt));
368
+ }
369
+ else if (!isTerminalStatusLike(run.status)) {
370
+ blockers.push(blocker("non_terminal", observedAt));
371
+ }
372
+ if (run.held) {
373
+ blockers.push(blocker("held", observedAt));
374
+ }
375
+ if (run.retentionExempt) {
376
+ blockers.push(blocker("retention_exempt", observedAt));
377
+ }
378
+ if (run.unresolvedCleanup) {
379
+ blockers.push(blocker("unresolved_cleanup", observedAt));
380
+ }
381
+ if (run.unresolvedCustody) {
382
+ blockers.push(blocker("unresolved_custody", observedAt));
383
+ }
384
+ }
385
+ function blocker(code, observedAt) {
386
+ return Object.freeze({ code, observedAt });
387
+ }
388
+ function isTerminalStatusLike(status) {
389
+ return isTerminalRunStatus(status);
390
+ }
391
+ function addDaysIso(timestamp, days) {
392
+ const start = Date.parse(assertTimestamp(timestamp, "candidate.run.terminalAt"));
393
+ const end = start + positiveInteger(days, "policy.retentionDays") * 24 * 60 * 60 * 1000;
394
+ return new Date(end).toISOString();
395
+ }
396
+ function visitRetentionValue(input, path, findings) {
397
+ if (typeof input === "string") {
398
+ scanStringValue(input, path, findings);
399
+ return;
400
+ }
401
+ if (Array.isArray(input)) {
402
+ input.forEach((value, index) => visitRetentionValue(value, `${path}[${index}]`, findings));
403
+ return;
404
+ }
405
+ if (!input || typeof input !== "object") {
406
+ return;
407
+ }
408
+ for (const [key, value] of Object.entries(input)) {
409
+ const childPath = `${path}.${key}`;
410
+ if (isForbiddenRetentionFieldName(key)) {
411
+ findings.push(Object.freeze({ path: childPath, reason: "forbidden_field_name" }));
412
+ }
413
+ visitRetentionValue(value, childPath, findings);
414
+ }
415
+ }
416
+ function scanStringValue(value, path, findings) {
417
+ for (const pattern of forbiddenStringPatterns) {
418
+ if (pattern.regex.test(value)) {
419
+ findings.push(Object.freeze({
420
+ path,
421
+ reason: pattern.reason,
422
+ valueLength: value.length
423
+ }));
424
+ }
425
+ }
426
+ }
427
+ const forbiddenStringPatterns = Object.freeze([
428
+ { reason: "signed_url", regex: /[?&](?:X-Amz-Signature|X-Amz-Credential|X-Amz-Algorithm|AWSAccessKeyId)=/i },
429
+ { reason: "r2_object_key", regex: /(^|[\s"'`])(?:runs|assets)\/[^?<#\s"'`]+/i },
430
+ { reason: "vault_id", regex: /\b(?:vault|vlt|secret)[_:-][A-Za-z0-9][A-Za-z0-9_-]{7,}\b/i },
431
+ {
432
+ reason: "private_resource_handle",
433
+ regex: /\b(?:machine|session|resource|handle|provider|asset)[_:-][A-Za-z0-9][A-Za-z0-9_-]{7,}\b/i
434
+ },
435
+ { reason: "hash_like_value", regex: /\b(?:sha256|hash)[:_-][A-Fa-f0-9]{16,}\b/ }
436
+ ]);
437
+ function isForbiddenRetentionFieldName(key) {
438
+ return /^(path|paths|objectKey|objectKeys|r2Key|r2Keys|fileName|filename|filenames|size|sizes|bytes|byteCount|hash|hashes|providerId|providerIds|vaultId|vaultIds|resourceId|resourceIds|handle|handles|signedUrl|signedUrls|url|urls)$/i.test(key);
439
+ }
440
+ function assertSafeIdentifier(value, field) {
441
+ assertNonEmptyString(value, field);
442
+ if (!/^[A-Za-z0-9._:-]+$/.test(value)) {
443
+ throw new RunRetentionValidationError(`run retention ${field} must be an opaque identifier without path separators`);
444
+ }
445
+ assertPublicSafeRunRetentionPayload(value);
446
+ return value;
447
+ }
448
+ function assertSafeMetadataString(value, field) {
449
+ assertNonEmptyString(value, field);
450
+ assertPublicSafeRunRetentionPayload(value);
451
+ return value;
452
+ }
453
+ function assertNonEmptyString(value, field) {
454
+ if (typeof value !== "string" || value.trim().length === 0) {
455
+ throw new RunRetentionValidationError(`run retention ${field} must be a non-empty string`);
456
+ }
457
+ }
458
+ function assertTimestamp(value, field) {
459
+ assertSafeMetadataString(value, field);
460
+ const ms = Date.parse(value);
461
+ if (!Number.isFinite(ms)) {
462
+ throw new RunRetentionValidationError(`run retention ${field} must be an ISO timestamp string`);
463
+ }
464
+ return value;
465
+ }
466
+ function nonNegativeInteger(value, field) {
467
+ if (!Number.isSafeInteger(value) || value < 0) {
468
+ throw new RunRetentionValidationError(`run retention ${field} must be a non-negative safe integer`);
469
+ }
470
+ return value;
471
+ }
472
+ function positiveInteger(value, field) {
473
+ if (!Number.isSafeInteger(value) || value <= 0) {
474
+ throw new RunRetentionValidationError(`run retention ${field} must be a positive safe integer`);
475
+ }
476
+ return value;
477
+ }
478
+ function formatFindingPaths(findings) {
479
+ return findings.map((finding) => `${finding.path} (${finding.reason})`).join(", ");
480
+ }
481
+ function cloneJson(value) {
482
+ return JSON.parse(JSON.stringify(value));
483
+ }
484
+ //# sourceMappingURL=run-retention.js.map
@@ -0,0 +1,194 @@
1
+ /**
2
+ * RunUnit — the self-contained read shape of a run.
3
+ *
4
+ * One canonical struct that captures every non-secret artifact persisted
5
+ * for a single run: parsed submission inputs, status/lifecycle, attempts,
6
+ * indexed events, raw-event Storage manifest, outputs (+ capture
7
+ * failures), proxy-call audit log, pinned workspace skills, provider
8
+ * built-in skills, and transient (Anthropic Files) skill records.
9
+ *
10
+ * Wire contract for `GET /api/runs/:runId`, the per-run archive's
11
+ * `run.json`/`submission.json`/`caps.json`, and the SDK/CLI
12
+ * `client.runs.get(runId)` return type.
13
+ *
14
+ * Immutability: every field here is read-only. Edit endpoints do not
15
+ * exist by design.
16
+ *
17
+ * Raw event payloads are not embedded in this struct. They live in
18
+ * private object storage as gzipped JSONL pages and are listed via
19
+ * `rawEventPages` (manifest only; bytes downloaded out-of-band so the
20
+ * detail response stays bounded). The archive zip carries the bytes.
21
+ */
22
+ import type { CleanupStatus } from "./status.js";
23
+ import type { JsonValue, PlatformSubmission, PlatformProxyEndpoint } from "./submission.js";
24
+ /**
25
+ * Parsed view of the legacy run snapshot jsonb. Stored shape is
26
+ * `{kind:"submission", submission}` written by the hosted API's
27
+ * server-side run creation for all runs.
28
+ */
29
+ export type RunUnitSubmission = RunUnitFlatSubmission;
30
+ export interface RunUnitFlatSubmission {
31
+ readonly kind: "submission";
32
+ readonly submission: PlatformSubmission;
33
+ }
34
+ export interface RunUnitAttempt {
35
+ readonly id: string;
36
+ readonly attemptNumber: number;
37
+ readonly status: string;
38
+ readonly providerSessionId?: string;
39
+ readonly errorClass?: string;
40
+ readonly errorCode?: string;
41
+ readonly errorMessage?: string;
42
+ readonly startedAt?: string;
43
+ readonly terminalAt?: string;
44
+ readonly createdAt: string;
45
+ }
46
+ /**
47
+ * Indexed event metadata (dedupe-key + summary). Raw payload bytes for
48
+ * each event are NOT here — they ride in `rawEventPages`. This struct
49
+ * stays small so the detail response is bounded.
50
+ */
51
+ export interface RunUnitEvent {
52
+ readonly id: string;
53
+ readonly attemptId?: string;
54
+ readonly providerEventId?: string;
55
+ readonly type: string;
56
+ readonly summary?: string;
57
+ readonly occurredAt?: string;
58
+ readonly processedAt: string;
59
+ readonly usageDelta?: Record<string, JsonValue>;
60
+ }
61
+ /**
62
+ * Inline slice of events plus an optional cursor for the tail. Most
63
+ * runs fit entirely inline; long-running ones overflow and the
64
+ * consumer paginates via `GET /api/runs/:runId/events?cursor=...`.
65
+ */
66
+ export interface RunUnitEventPage {
67
+ readonly entries: readonly RunUnitEvent[];
68
+ readonly totalCount: number;
69
+ readonly truncated: boolean;
70
+ readonly nextCursor?: string;
71
+ }
72
+ /**
73
+ * One gzipped JSONL page of raw provider events captured to Storage.
74
+ * Bytes are downloaded via signed URL or surfaced inside the per-run
75
+ * archive zip. `storagePath` is bucket-relative; the BFF turns it
76
+ * into a signed URL for clients.
77
+ */
78
+ export interface RunUnitRawEventPage {
79
+ readonly attempt: number;
80
+ readonly page: number;
81
+ readonly byteSize: number;
82
+ readonly eventCount: number;
83
+ readonly storagePath: string;
84
+ readonly contentEncoding: "gzip";
85
+ readonly createdAt: string;
86
+ }
87
+ export interface RunUnitOutput {
88
+ readonly id: string;
89
+ readonly fileName: string;
90
+ readonly byteSize: number;
91
+ readonly contentType?: string;
92
+ }
93
+ export interface RunUnitOutputCaptureFailure {
94
+ readonly id: string;
95
+ readonly providerFileId?: string;
96
+ readonly filename?: string;
97
+ readonly byteSize?: number;
98
+ readonly reason: string;
99
+ readonly errorMessage?: string;
100
+ readonly createdAt: string;
101
+ }
102
+ export interface RunUnitProxyCall {
103
+ readonly id: string;
104
+ readonly endpointName: string;
105
+ readonly method: string;
106
+ readonly requestPathRedacted: string | null;
107
+ readonly requestByteSize: number;
108
+ readonly responseStatus: number | null;
109
+ readonly responseByteSize: number;
110
+ readonly outcome: string;
111
+ readonly errorClass: string | null;
112
+ readonly startedAt: string;
113
+ readonly finishedAt: string | null;
114
+ readonly durationMs: number | null;
115
+ }
116
+ export interface RunUnitProxyCallPage {
117
+ readonly entries: readonly RunUnitProxyCall[];
118
+ readonly totalCount: number;
119
+ readonly truncated: boolean;
120
+ readonly nextCursor?: string;
121
+ }
122
+ /**
123
+ * Workspace skill bundle pinned at submission. `liveSkillId` is `null`
124
+ * when the corresponding `skill_bundles` row has been soft-deleted —
125
+ * the UI uses that to render a tombstoned link.
126
+ */
127
+ export interface RunUnitSkillSnapshot {
128
+ readonly skillId: string;
129
+ readonly name: string;
130
+ readonly hash: string;
131
+ readonly sizeBytes: number;
132
+ readonly fileCount: number;
133
+ readonly liveSkillId: string | null;
134
+ }
135
+ export interface RunUnitProviderSkill {
136
+ readonly vendor: string;
137
+ readonly skillId: string;
138
+ readonly version?: string;
139
+ }
140
+ export interface RunUnitInlineSkill {
141
+ readonly id: string;
142
+ readonly slotId: string;
143
+ readonly skillName: string;
144
+ readonly contentHash: string;
145
+ readonly anthropicFileId: string | null;
146
+ readonly status: string;
147
+ readonly createdAt: string;
148
+ readonly updatedAt: string;
149
+ }
150
+ export interface RunUnit {
151
+ readonly id: string;
152
+ readonly workspaceId: string;
153
+ readonly status: string;
154
+ readonly lifecyclePhase?: string;
155
+ readonly cleanupStatus: CleanupStatus;
156
+ readonly createdAt: string;
157
+ readonly updatedAt: string;
158
+ readonly startedAt?: string;
159
+ readonly terminalAt?: string;
160
+ readonly deletedAt?: string;
161
+ readonly attemptCount: number;
162
+ readonly submission: RunUnitSubmission;
163
+ readonly capsSnapshot?: Record<string, JsonValue>;
164
+ readonly proxyEndpointsSnapshot?: readonly PlatformProxyEndpoint[];
165
+ readonly attempts: readonly RunUnitAttempt[];
166
+ readonly events: RunUnitEventPage;
167
+ readonly rawEventPages: readonly RunUnitRawEventPage[];
168
+ readonly outputs: readonly RunUnitOutput[];
169
+ readonly outputCaptureFailures: readonly RunUnitOutputCaptureFailure[];
170
+ readonly costTelemetry?: import("./run-cost.js").RunCostTelemetry;
171
+ readonly proxyCalls: RunUnitProxyCallPage;
172
+ readonly skillSnapshots: readonly RunUnitSkillSnapshot[];
173
+ readonly providerSkills: readonly RunUnitProviderSkill[];
174
+ readonly inlineSkills: readonly RunUnitInlineSkill[];
175
+ /**
176
+ * Per-run, per-provider runtime manifest — derived from the validated
177
+ * submission + the chosen provider (`buildRuntimeManifest`). Tells
178
+ * SDK consumers where aex placed things in-container and what
179
+ * env vars the agent will see. Undefined on responses from BFFs
180
+ * that predate Phase 2 of the runtime-environment rollout.
181
+ */
182
+ readonly runtimeManifest?: import("./runtime-manifest.js").RuntimeManifest;
183
+ }
184
+ /**
185
+ * Parse a legacy run snapshot jsonb payload into the typed flat
186
+ * submission. Never throws on minor unknown keys so we can
187
+ * forward-compat with worker-side enrichment.
188
+ *
189
+ * Returns a typed shape even for malformed snapshots — the worst case
190
+ * is `{kind: "submission", submission: {model: "", ...}}` with empty
191
+ * defaults — because the dashboard must still render *something* for a
192
+ * buggy historical row rather than 500ing the whole detail page.
193
+ */
194
+ export declare function parseRunUnitSubmission(input: unknown): RunUnitSubmission;