@zhivex-ai/harness 1.4.0 → 1.4.1

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 (117) hide show
  1. package/CHANGELOG.md +29 -10
  2. package/README.md +7 -5
  3. package/SUPPORT.md +2 -2
  4. package/contracts/client-protocol-v1.schema.json +593 -0
  5. package/contracts/engine-api.json +156 -0
  6. package/contracts/public-api.json +12 -0
  7. package/contracts/stable-api-signatures.json +33 -33
  8. package/dist/acp-cli.js +54 -20
  9. package/dist/audit/index.d.ts +3 -0
  10. package/dist/audit/index.js +482 -0
  11. package/dist/audit/pack.d.ts +72 -0
  12. package/dist/audit-pack-e4vaq7np.js +532 -0
  13. package/dist/cli/arguments.d.ts +3 -1
  14. package/dist/cli/audit-pack.d.ts +24 -0
  15. package/dist/cli/cli-credentials.d.ts +6 -0
  16. package/dist/cli/cli-profiles.d.ts +4 -2
  17. package/dist/cli/node-engine.d.ts +2 -0
  18. package/dist/{cli-2h37qyvg.js → cli-1cf4dcnj.js} +245 -642
  19. package/dist/{cli-entry-w0hterjs.js → cli-entry-3s63zn4w.js} +40 -13
  20. package/dist/cli-entry-7f0cen32.js +9990 -0
  21. package/dist/cli-entry-a890x3kq.js +10348 -0
  22. package/dist/{cli-entry-20w6nh0k.js → cli-entry-hdtfef27.js} +439 -19
  23. package/dist/cli-entry-rgs6bxmy.js +484 -0
  24. package/dist/cli.js +27 -2
  25. package/dist/client/json-contracts.d.ts +4 -0
  26. package/dist/client/protocol.d.ts +133 -0
  27. package/dist/client/task-control-host.d.ts +400 -0
  28. package/dist/client/task-projection-host.d.ts +12 -0
  29. package/dist/client/task-projection.d.ts +168 -0
  30. package/dist/compat/code-support.js +28 -22
  31. package/dist/{config-qt69hkqj.js → config-r9fdhce6.js} +3 -3
  32. package/dist/{config-j9624t6t.js → config-vyajmksy.js} +1 -1
  33. package/dist/engine/acp.js +15 -11
  34. package/dist/engine/client.js +14 -10
  35. package/dist/engine/desktop-providers-v1.d.ts +2 -0
  36. package/dist/engine/desktop-providers-v1.js +23 -3
  37. package/dist/engine/desktop-state-v1.js +11 -8
  38. package/dist/engine/index.d.ts +3 -0
  39. package/dist/engine/index.js +101 -554
  40. package/dist/engine/mcp-stdio-v1.js +8 -6
  41. package/dist/engine/models.js +76 -4
  42. package/dist/engine/protocol.d.ts +2 -0
  43. package/dist/engine/protocol.js +110 -0
  44. package/dist/engine/service.js +15 -11
  45. package/dist/hostile-repository-demo.js +1095 -505
  46. package/dist/{index-937cb7y8.js → index-02pqtbbg.js} +559 -33
  47. package/dist/{index-pp474137.js → index-0zs2dz26.js} +766 -931
  48. package/dist/{index-8zjmnyw5.js → index-1265j7t2.js} +73 -6
  49. package/dist/index-4fx5f3k5.js +269 -0
  50. package/dist/{index-nmjdwd4p.js → index-4gt045j0.js} +109 -1
  51. package/dist/{index-6dft2vev.js → index-6599mpmy.js} +5 -3
  52. package/dist/{index-tfzta544.js → index-74kkpyqk.js} +9 -7
  53. package/dist/{index-61gthdz6.js → index-8sm2a1wa.js} +1 -1
  54. package/dist/index-c39197wq.js +552 -0
  55. package/dist/{index-5jhgatga.js → index-crta5wzv.js} +7 -6
  56. package/dist/{index-m34q25jj.js → index-d9zkmhz3.js} +143 -824
  57. package/dist/{index-pdmevfxv.js → index-dtb2c6ds.js} +5 -259
  58. package/dist/index-f6mqa5gk.js +1535 -0
  59. package/dist/index-j6zxnc8m.js +490 -0
  60. package/dist/{index-rkbf7dqg.js → index-mv8sb1ns.js} +5 -3
  61. package/dist/{index-xqxeypt5.js → index-mzydbzp8.js} +76 -4
  62. package/dist/{index-wg1sd3at.js → index-qfsrrnz6.js} +1 -1
  63. package/dist/{index-m82cfspw.js → index-sk3cap35.js} +877 -963
  64. package/dist/index-sstgcsr0.js +511 -0
  65. package/dist/index-wm9xeq1n.js +329 -0
  66. package/dist/index-xdbkcccf.js +719 -0
  67. package/dist/{index-s2k2edts.js → index-zxg4ve4j.js} +41 -1548
  68. package/dist/index.d.ts +3 -0
  69. package/dist/index.js +93 -77
  70. package/dist/internal/desktop/providers.d.ts +2 -0
  71. package/dist/models/catalog.d.ts +2 -0
  72. package/dist/persistence/sqlite-database.d.ts +1 -0
  73. package/dist/providers/azure-openai.d.ts +14 -0
  74. package/dist/providers/bedrock.d.ts +23 -0
  75. package/dist/providers/providers.d.ts +1 -1
  76. package/dist/runtime/durable-review-group.d.ts +33 -0
  77. package/dist/runtime/harness.d.ts +11 -0
  78. package/dist/runtime/run-interruption.d.ts +7 -0
  79. package/dist/runtime/shared-budget.d.ts +30 -0
  80. package/dist/runtime/task-continuity.d.ts +300 -77
  81. package/dist/service-cli.js +15 -11
  82. package/dist/{service-client-cli-fn54ev6w.js → service-client-cli-eqrdc9zq.js} +400 -305
  83. package/dist/verify-historical-migrations.js +1095 -505
  84. package/dist/zhx.js +27 -2
  85. package/docs/AUDIT_PACK.md +99 -0
  86. package/docs/AZURE_OPENAI.md +53 -0
  87. package/docs/BEDROCK.md +56 -0
  88. package/docs/CLI.md +15 -0
  89. package/docs/CLIENT_PROTOCOL.md +178 -0
  90. package/docs/CODE_RELEASE.md +31 -1
  91. package/docs/CODE_WEB_TASKS.md +116 -0
  92. package/docs/CONTEXT_ENGINEERING.md +59 -0
  93. package/docs/CREDENTIALS.md +21 -0
  94. package/docs/DURABLE_OPERATIONS.md +93 -1
  95. package/docs/ENGINE_API.md +5 -2
  96. package/docs/EXTENSIBILITY.md +2 -2
  97. package/docs/FIRST_USE.md +6 -6
  98. package/docs/MINIMUM_TASK_AUTHORITY.md +3 -3
  99. package/docs/PROJECT_MEMORY.md +1 -1
  100. package/docs/README.md +6 -1
  101. package/docs/STABILITY.md +3 -1
  102. package/docs/SUPPORT_MATRIX.md +1 -1
  103. package/docs/SUPPORT_MATRIX_1_1.md +1 -1
  104. package/docs/USAGE.md +3 -1
  105. package/docs/VERTEX.md +2 -2
  106. package/docs/WEB_LIMITS.md +4 -3
  107. package/docs/adr/0002-isolated-mcp-stdio.md +6 -3
  108. package/docs/support-matrix-1.1.json +3 -1
  109. package/docs/support-matrix.json +6 -4
  110. package/evaluations/code-task-ui.json +20 -0
  111. package/evaluations/representative-assembly-matrix.json +10 -1
  112. package/evaluations/task-projection.json +10 -0
  113. package/evaluations/task-recovery-lab.json +60 -0
  114. package/examples/README.md +3 -3
  115. package/package.json +23 -13
  116. package/dist/cli-entry-4ngptr62.js +0 -19900
  117. package/dist/index-m49jx72n.js +0 -220
@@ -0,0 +1,511 @@
1
+ import {
2
+ fileDigestSchema
3
+ } from "./index-xdbkcccf.js";
4
+
5
+ // src/workspace/change-envelope.ts
6
+ import { createHash } from "node:crypto";
7
+ import { z } from "zod";
8
+ var CHANGE_ENVELOPE_SCHEMA_VERSION = 1;
9
+ var CHANGE_ENVELOPE_DIGEST_ALGORITHM = "sha256";
10
+ var MAX_CHANGE_ENVELOPE_CHECKS = 100;
11
+ var MAX_CHANGE_ENVELOPE_APPROVALS = 100;
12
+ var MAX_CHANGE_ENVELOPE_APPROVAL_SCOPES = 32;
13
+ var MAX_CHANGE_ENVELOPE_ATTESTATIONS = 32;
14
+ var timestampSchema = z.iso.datetime({ precision: 3 });
15
+ var boundedIdentifierSchema = z.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:@/-]*$/, "Identifier must contain only bounded non-secret token characters.");
16
+ var approvalScopeSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:/*-]*$/, "Approval scope must contain only bounded token characters.");
17
+ var attestationFormatSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9.+_-]*$/);
18
+ var mediaTypeSchema = z.string().min(3).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$/);
19
+ var addDuplicateIssues = (values, key, context, path) => {
20
+ const seen = new Set;
21
+ for (let index = 0;index < values.length; index += 1) {
22
+ const value = values[index];
23
+ if (value === undefined)
24
+ continue;
25
+ const identifier = key(value);
26
+ if (seen.has(identifier)) {
27
+ context.addIssue({
28
+ code: "custom",
29
+ path: [index, path],
30
+ message: `Duplicate ${path}: ${identifier}.`
31
+ });
32
+ }
33
+ seen.add(identifier);
34
+ }
35
+ };
36
+ var addCanonicalOrderIssue = (values, key, context, path) => {
37
+ for (let index = 1;index < values.length; index += 1) {
38
+ const previous = values[index - 1];
39
+ const current = values[index];
40
+ if (previous !== undefined && current !== undefined && key(previous) >= key(current)) {
41
+ context.addIssue({
42
+ code: "custom",
43
+ path: [index, path],
44
+ message: `${path} entries must be unique and in canonical ascending order.`
45
+ });
46
+ }
47
+ }
48
+ };
49
+ var approvalScopesInputSchema = z.array(approvalScopeSchema).min(1).max(MAX_CHANGE_ENVELOPE_APPROVAL_SCOPES).superRefine((scopes, context) => addDuplicateIssues(scopes, (scope) => scope, context, "scope"));
50
+ var canonicalApprovalScopesSchema = z.array(approvalScopeSchema).min(1).max(MAX_CHANGE_ENVELOPE_APPROVAL_SCOPES).superRefine((scopes, context) => addCanonicalOrderIssue(scopes, (scope) => scope, context, "scope"));
51
+ var changeEnvelopeCheckSchema = z.strictObject({
52
+ checkId: boundedIdentifierSchema,
53
+ status: z.enum(["passed", "failed", "skipped"]),
54
+ redacted: z.literal(true),
55
+ startedAt: timestampSchema,
56
+ completedAt: timestampSchema,
57
+ durationMs: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
58
+ exitCode: z.number().int().min(0).max(255).optional(),
59
+ redactedOutputDigest: fileDigestSchema.optional(),
60
+ runnerFingerprint: fileDigestSchema.optional()
61
+ }).superRefine((check, context) => {
62
+ const startedAt = Date.parse(check.startedAt);
63
+ const completedAt = Date.parse(check.completedAt);
64
+ if (completedAt < startedAt) {
65
+ context.addIssue({
66
+ code: "custom",
67
+ path: ["completedAt"],
68
+ message: "Check completion cannot precede its start."
69
+ });
70
+ }
71
+ if (check.status === "passed" && check.exitCode !== undefined && check.exitCode !== 0) {
72
+ context.addIssue({
73
+ code: "custom",
74
+ path: ["exitCode"],
75
+ message: "A passed check cannot have a non-zero exit code."
76
+ });
77
+ }
78
+ if (check.status === "failed" && check.exitCode === 0) {
79
+ context.addIssue({
80
+ code: "custom",
81
+ path: ["exitCode"],
82
+ message: "A failed check cannot have a zero exit code."
83
+ });
84
+ }
85
+ if (check.status === "skipped" && check.exitCode !== undefined) {
86
+ context.addIssue({
87
+ code: "custom",
88
+ path: ["exitCode"],
89
+ message: "A skipped check cannot have an exit code."
90
+ });
91
+ }
92
+ });
93
+ var checkInputListSchema = z.array(changeEnvelopeCheckSchema).min(1).max(MAX_CHANGE_ENVELOPE_CHECKS).superRefine((checks, context) => addDuplicateIssues(checks, (check) => check.checkId, context, "checkId"));
94
+ var canonicalCheckListSchema = z.array(changeEnvelopeCheckSchema).min(1).max(MAX_CHANGE_ENVELOPE_CHECKS).superRefine((checks, context) => addCanonicalOrderIssue(checks, (check) => check.checkId, context, "checkId"));
95
+ var changeEnvelopeApprovalSchema = z.strictObject({
96
+ kind: z.literal("approval-reference"),
97
+ approvalId: boundedIdentifierSchema,
98
+ approvalDigest: fileDigestSchema,
99
+ approverDigest: fileDigestSchema,
100
+ scopes: canonicalApprovalScopesSchema,
101
+ approvedAt: timestampSchema,
102
+ expiresAt: timestampSchema.optional()
103
+ }).superRefine((approval, context) => {
104
+ if (approval.expiresAt && Date.parse(approval.expiresAt) <= Date.parse(approval.approvedAt)) {
105
+ context.addIssue({
106
+ code: "custom",
107
+ path: ["expiresAt"],
108
+ message: "Approval expiry must be later than approval time."
109
+ });
110
+ }
111
+ });
112
+ var changeEnvelopeApprovalInputSchema = z.strictObject({
113
+ kind: z.literal("approval-reference"),
114
+ approvalId: boundedIdentifierSchema,
115
+ approvalDigest: fileDigestSchema,
116
+ approverDigest: fileDigestSchema,
117
+ scopes: approvalScopesInputSchema,
118
+ approvedAt: timestampSchema,
119
+ expiresAt: timestampSchema.optional()
120
+ }).superRefine((approval, context) => {
121
+ if (approval.expiresAt && Date.parse(approval.expiresAt) <= Date.parse(approval.approvedAt)) {
122
+ context.addIssue({
123
+ code: "custom",
124
+ path: ["expiresAt"],
125
+ message: "Approval expiry must be later than approval time."
126
+ });
127
+ }
128
+ });
129
+ var approvalInputListSchema = z.array(changeEnvelopeApprovalInputSchema).min(1).max(MAX_CHANGE_ENVELOPE_APPROVALS).superRefine((approvals, context) => addDuplicateIssues(approvals, (approval) => approval.approvalId, context, "approvalId"));
130
+ var canonicalApprovalListSchema = z.array(changeEnvelopeApprovalSchema).min(1).max(MAX_CHANGE_ENVELOPE_APPROVALS).superRefine((approvals, context) => addCanonicalOrderIssue(approvals, (approval) => approval.approvalId, context, "approvalId"));
131
+ var externalAttestationReferenceSchema = z.strictObject({
132
+ kind: z.literal("external-attestation-reference"),
133
+ attestationId: boundedIdentifierSchema,
134
+ format: attestationFormatSchema,
135
+ mediaType: mediaTypeSchema,
136
+ bundleDigest: fileDigestSchema
137
+ });
138
+ var externalAttestationInputListSchema = z.array(externalAttestationReferenceSchema).min(1).max(MAX_CHANGE_ENVELOPE_ATTESTATIONS).superRefine((attestations, context) => addDuplicateIssues(attestations, (attestation) => attestation.attestationId, context, "attestationId"));
139
+ var canonicalExternalAttestationListSchema = z.array(externalAttestationReferenceSchema).min(1).max(MAX_CHANGE_ENVELOPE_ATTESTATIONS).superRefine((attestations, context) => addCanonicalOrderIssue(attestations, (attestation) => attestation.attestationId, context, "attestationId"));
140
+ var changeEnvelopeBaseSchema = z.strictObject({
141
+ workspaceDigest: fileDigestSchema,
142
+ treeDigest: fileDigestSchema
143
+ });
144
+ var changeEnvelopePatchSchema = z.strictObject({
145
+ patchId: boundedIdentifierSchema,
146
+ patchDigest: fileDigestSchema
147
+ });
148
+ var changeEnvelopeFingerprintsSchema = z.strictObject({
149
+ harness: fileDigestSchema,
150
+ policy: fileDigestSchema,
151
+ environment: fileDigestSchema
152
+ });
153
+ var changeEnvelopeEvidenceSchema = z.strictObject({
154
+ schemaVersion: z.literal(CHANGE_ENVELOPE_SCHEMA_VERSION),
155
+ kind: z.literal("check-evidence-summary"),
156
+ digestAlgorithm: z.literal(CHANGE_ENVELOPE_DIGEST_ALGORITHM),
157
+ evidenceDigest: fileDigestSchema,
158
+ checks: canonicalCheckListSchema
159
+ });
160
+ var changeEnvelopeInputSchema = z.strictObject({
161
+ createdAt: timestampSchema,
162
+ expiresAt: timestampSchema.optional(),
163
+ base: changeEnvelopeBaseSchema,
164
+ patch: changeEnvelopePatchSchema,
165
+ fingerprints: changeEnvelopeFingerprintsSchema,
166
+ checks: checkInputListSchema,
167
+ approvals: approvalInputListSchema.optional(),
168
+ externalAttestations: externalAttestationInputListSchema.optional()
169
+ });
170
+ var changeEnvelopeSchema = z.strictObject({
171
+ schemaVersion: z.literal(CHANGE_ENVELOPE_SCHEMA_VERSION),
172
+ kind: z.literal("change-envelope"),
173
+ digestAlgorithm: z.literal(CHANGE_ENVELOPE_DIGEST_ALGORITHM),
174
+ envelopeId: fileDigestSchema,
175
+ createdAt: timestampSchema,
176
+ expiresAt: timestampSchema.optional(),
177
+ base: changeEnvelopeBaseSchema,
178
+ patch: changeEnvelopePatchSchema,
179
+ fingerprints: changeEnvelopeFingerprintsSchema,
180
+ evidence: changeEnvelopeEvidenceSchema,
181
+ approvals: canonicalApprovalListSchema.optional(),
182
+ externalAttestations: canonicalExternalAttestationListSchema.optional()
183
+ }).superRefine((envelope, context) => {
184
+ if (envelope.expiresAt && Date.parse(envelope.expiresAt) <= Date.parse(envelope.createdAt)) {
185
+ context.addIssue({
186
+ code: "custom",
187
+ path: ["expiresAt"],
188
+ message: "Envelope expiry must be later than creation time."
189
+ });
190
+ }
191
+ for (let index = 0;index < envelope.evidence.checks.length; index += 1) {
192
+ const check = envelope.evidence.checks[index];
193
+ if (check && Date.parse(check.completedAt) > Date.parse(envelope.createdAt)) {
194
+ context.addIssue({
195
+ code: "custom",
196
+ path: ["evidence", "checks", index, "completedAt"],
197
+ message: "Check completion cannot be later than envelope creation time."
198
+ });
199
+ }
200
+ }
201
+ for (let index = 0;index < (envelope.approvals?.length ?? 0); index += 1) {
202
+ const approval = envelope.approvals?.[index];
203
+ if (approval && Date.parse(approval.approvedAt) > Date.parse(envelope.createdAt)) {
204
+ context.addIssue({
205
+ code: "custom",
206
+ path: ["approvals", index, "approvedAt"],
207
+ message: "Approval time cannot be later than envelope creation time."
208
+ });
209
+ }
210
+ }
211
+ });
212
+ var canonicalJson = (value) => {
213
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
214
+ return JSON.stringify(value);
215
+ }
216
+ if (typeof value === "number") {
217
+ if (!Number.isFinite(value))
218
+ throw new TypeError("Canonical JSON cannot encode a non-finite number.");
219
+ return JSON.stringify(Object.is(value, -0) ? 0 : value);
220
+ }
221
+ if (Array.isArray(value)) {
222
+ return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
223
+ }
224
+ if (typeof value === "object") {
225
+ const record = value;
226
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
227
+ }
228
+ throw new TypeError(`Canonical JSON cannot encode ${typeof value}.`);
229
+ };
230
+ var digestCanonical = (value) => `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`;
231
+ var digestChangeEnvelopeArtifact = (value) => `sha256:${createHash("sha256").update(value).digest("hex")}`;
232
+ var evidencePayload = (checks) => ({
233
+ schemaVersion: CHANGE_ENVELOPE_SCHEMA_VERSION,
234
+ kind: "check-evidence-summary",
235
+ digestAlgorithm: CHANGE_ENVELOPE_DIGEST_ALGORITHM,
236
+ checks: [...checks]
237
+ });
238
+ var envelopePayload = (envelope) => {
239
+ const { envelopeId: _envelopeId, ...payload } = envelope;
240
+ return payload;
241
+ };
242
+ var sortBy = (values, key) => [...values].sort((left, right) => {
243
+ const leftKey = key(left);
244
+ const rightKey = key(right);
245
+ return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
246
+ });
247
+ var createChangeEnvelope = (input) => {
248
+ const parsed = changeEnvelopeInputSchema.parse(input);
249
+ const checks = sortBy(parsed.checks, (check) => check.checkId);
250
+ const evidenceWithoutDigest = evidencePayload(checks);
251
+ const evidence = {
252
+ ...evidenceWithoutDigest,
253
+ evidenceDigest: digestCanonical(evidenceWithoutDigest)
254
+ };
255
+ const approvals = parsed.approvals ? sortBy(parsed.approvals, (approval) => approval.approvalId).map((approval) => ({
256
+ ...approval,
257
+ scopes: [...approval.scopes].sort()
258
+ })) : undefined;
259
+ const externalAttestations = parsed.externalAttestations ? sortBy(parsed.externalAttestations, (attestation) => attestation.attestationId) : undefined;
260
+ const payload = {
261
+ schemaVersion: CHANGE_ENVELOPE_SCHEMA_VERSION,
262
+ kind: "change-envelope",
263
+ digestAlgorithm: CHANGE_ENVELOPE_DIGEST_ALGORITHM,
264
+ createdAt: parsed.createdAt,
265
+ ...parsed.expiresAt ? { expiresAt: parsed.expiresAt } : {},
266
+ base: parsed.base,
267
+ patch: parsed.patch,
268
+ fingerprints: parsed.fingerprints,
269
+ evidence,
270
+ ...approvals ? { approvals } : {},
271
+ ...externalAttestations ? { externalAttestations } : {}
272
+ };
273
+ return changeEnvelopeSchema.parse({
274
+ ...payload,
275
+ envelopeId: digestCanonical(payload)
276
+ });
277
+ };
278
+ var canonicalizeChangeEnvelope = (input) => canonicalJson(changeEnvelopeSchema.parse(input));
279
+ var computeChangeEnvelopeDigest = (input) => {
280
+ const parsed = changeEnvelopeSchema.parse(input);
281
+ return digestCanonical(envelopePayload(parsed));
282
+ };
283
+ var computeChangeEnvelopeEvidenceDigest = (input) => {
284
+ const parsed = changeEnvelopeEvidenceSchema.parse(input);
285
+ return digestCanonical(evidencePayload(parsed.checks));
286
+ };
287
+ var changeEnvelopePreconditionsSchema = z.strictObject({
288
+ envelopeId: fileDigestSchema.optional(),
289
+ baseWorkspaceDigest: fileDigestSchema.optional(),
290
+ baseTreeDigest: fileDigestSchema.optional(),
291
+ patchId: boundedIdentifierSchema.optional(),
292
+ patchDigest: fileDigestSchema.optional(),
293
+ harnessFingerprint: fileDigestSchema.optional(),
294
+ policyFingerprint: fileDigestSchema.optional(),
295
+ environmentFingerprint: fileDigestSchema.optional(),
296
+ evidenceDigest: fileDigestSchema.optional(),
297
+ requiredPassedChecks: z.array(boundedIdentifierSchema).max(MAX_CHANGE_ENVELOPE_CHECKS).optional(),
298
+ requiredApprovalDigests: z.array(fileDigestSchema).max(MAX_CHANGE_ENVELOPE_APPROVALS).optional(),
299
+ requiredApprovalScopes: z.array(approvalScopeSchema).max(MAX_CHANGE_ENVELOPE_APPROVAL_SCOPES).optional(),
300
+ requiredAttestationDigests: z.array(fileDigestSchema).max(MAX_CHANGE_ENVELOPE_ATTESTATIONS).optional()
301
+ }).superRefine((preconditions, context) => {
302
+ if (preconditions.requiredPassedChecks) {
303
+ addDuplicateIssues(preconditions.requiredPassedChecks, (checkId) => checkId, context, "requiredPassedCheck");
304
+ }
305
+ if (preconditions.requiredApprovalDigests) {
306
+ addDuplicateIssues(preconditions.requiredApprovalDigests, (digest) => digest, context, "requiredApprovalDigest");
307
+ }
308
+ if (preconditions.requiredApprovalScopes) {
309
+ addDuplicateIssues(preconditions.requiredApprovalScopes, (scope) => scope, context, "requiredApprovalScope");
310
+ }
311
+ if (preconditions.requiredAttestationDigests) {
312
+ addDuplicateIssues(preconditions.requiredAttestationDigests, (digest) => digest, context, "requiredAttestationDigest");
313
+ }
314
+ });
315
+ var verificationOptionsSchema = z.strictObject({
316
+ now: z.union([z.date(), z.number().int(), timestampSchema]).optional(),
317
+ preconditions: changeEnvelopePreconditionsSchema.optional()
318
+ });
319
+ var verificationTime = (value) => {
320
+ const date = value === undefined ? new Date : value instanceof Date ? value : new Date(value);
321
+ if (!Number.isFinite(date.getTime()))
322
+ throw new TypeError("Verification time must be a valid date.");
323
+ return date;
324
+ };
325
+ var verifyChangeEnvelope = (input, options = {}) => {
326
+ const parsedOptions = verificationOptionsSchema.parse(options);
327
+ const now = verificationTime(parsedOptions.now);
328
+ const checkedAt = now.toISOString();
329
+ const parsedEnvelope = changeEnvelopeSchema.safeParse(input);
330
+ if (!parsedEnvelope.success) {
331
+ return {
332
+ valid: false,
333
+ verificationScope: "integrity-expiration-and-preconditions-only",
334
+ issues: parsedEnvelope.error.issues.map((issue) => ({
335
+ code: "invalid-schema",
336
+ path: issue.path.map((segment) => typeof segment === "symbol" ? segment.description ?? "symbol" : segment),
337
+ message: issue.message
338
+ })),
339
+ integrity: { envelopeDigestValid: false, evidenceDigestValid: false },
340
+ expiration: { checkedAt, envelopeNotYetValid: false, envelopeExpired: false, expiredApprovalIds: [] },
341
+ checks: {
342
+ recorded: 0,
343
+ passed: [],
344
+ failed: [],
345
+ skipped: [],
346
+ requiredPassed: [...parsedOptions.preconditions?.requiredPassedChecks ?? []].sort(),
347
+ missingPassed: [...parsedOptions.preconditions?.requiredPassedChecks ?? []].sort()
348
+ },
349
+ approvals: {
350
+ recorded: 0,
351
+ requiredScopes: [...parsedOptions.preconditions?.requiredApprovalScopes ?? []].sort(),
352
+ missingScopes: [...parsedOptions.preconditions?.requiredApprovalScopes ?? []].sort(),
353
+ authenticity: "not-verified"
354
+ },
355
+ externalAttestations: {
356
+ referenced: 0,
357
+ requiredDigests: [...parsedOptions.preconditions?.requiredAttestationDigests ?? []].sort(),
358
+ missingDigests: [...parsedOptions.preconditions?.requiredAttestationDigests ?? []].sort(),
359
+ authenticity: "not-verified"
360
+ }
361
+ };
362
+ }
363
+ const envelope = parsedEnvelope.data;
364
+ const issues = [];
365
+ const computedEvidenceDigest = digestCanonical(evidencePayload(envelope.evidence.checks));
366
+ const evidenceDigestValid = computedEvidenceDigest === envelope.evidence.evidenceDigest;
367
+ if (!evidenceDigestValid) {
368
+ issues.push({
369
+ code: "evidence-digest-mismatch",
370
+ path: ["evidence", "evidenceDigest"],
371
+ message: "Evidence digest does not match the canonical redacted check summary.",
372
+ expected: computedEvidenceDigest,
373
+ actual: envelope.evidence.evidenceDigest
374
+ });
375
+ }
376
+ const computedEnvelopeDigest = digestCanonical(envelopePayload(envelope));
377
+ const envelopeDigestValid = computedEnvelopeDigest === envelope.envelopeId;
378
+ if (!envelopeDigestValid) {
379
+ issues.push({
380
+ code: "envelope-digest-mismatch",
381
+ path: ["envelopeId"],
382
+ message: "Envelope digest does not match its canonical payload.",
383
+ expected: computedEnvelopeDigest,
384
+ actual: envelope.envelopeId
385
+ });
386
+ }
387
+ const envelopeNotYetValid = now.getTime() < Date.parse(envelope.createdAt);
388
+ if (envelopeNotYetValid) {
389
+ issues.push({
390
+ code: "envelope-not-yet-valid",
391
+ path: ["createdAt"],
392
+ message: "Change envelope creation time is in the future."
393
+ });
394
+ }
395
+ const envelopeExpired = envelope.expiresAt !== undefined && now.getTime() >= Date.parse(envelope.expiresAt);
396
+ if (envelopeExpired) {
397
+ issues.push({
398
+ code: "envelope-expired",
399
+ path: ["expiresAt"],
400
+ message: "Change envelope has expired."
401
+ });
402
+ }
403
+ const expiredApprovalIds = (envelope.approvals ?? []).filter((approval) => approval.expiresAt !== undefined && now.getTime() >= Date.parse(approval.expiresAt)).map((approval) => approval.approvalId);
404
+ for (const approvalId of expiredApprovalIds) {
405
+ issues.push({
406
+ code: "approval-expired",
407
+ path: ["approvals", envelope.approvals?.findIndex((approval) => approval.approvalId === approvalId) ?? 0, "expiresAt"],
408
+ message: `Recorded approval has expired: ${approvalId}.`
409
+ });
410
+ }
411
+ const preconditions = parsedOptions.preconditions;
412
+ const comparePrecondition = (field, expected, actual, path) => {
413
+ if (expected !== undefined && expected !== actual) {
414
+ issues.push({
415
+ code: "precondition-mismatch",
416
+ path,
417
+ message: `Change envelope precondition does not match: ${field}.`,
418
+ expected,
419
+ actual
420
+ });
421
+ }
422
+ };
423
+ comparePrecondition("envelopeId", preconditions?.envelopeId, envelope.envelopeId, ["envelopeId"]);
424
+ comparePrecondition("baseWorkspaceDigest", preconditions?.baseWorkspaceDigest, envelope.base.workspaceDigest, ["base", "workspaceDigest"]);
425
+ comparePrecondition("baseTreeDigest", preconditions?.baseTreeDigest, envelope.base.treeDigest, ["base", "treeDigest"]);
426
+ comparePrecondition("patchId", preconditions?.patchId, envelope.patch.patchId, ["patch", "patchId"]);
427
+ comparePrecondition("patchDigest", preconditions?.patchDigest, envelope.patch.patchDigest, ["patch", "patchDigest"]);
428
+ comparePrecondition("harnessFingerprint", preconditions?.harnessFingerprint, envelope.fingerprints.harness, ["fingerprints", "harness"]);
429
+ comparePrecondition("policyFingerprint", preconditions?.policyFingerprint, envelope.fingerprints.policy, ["fingerprints", "policy"]);
430
+ comparePrecondition("environmentFingerprint", preconditions?.environmentFingerprint, envelope.fingerprints.environment, ["fingerprints", "environment"]);
431
+ comparePrecondition("evidenceDigest", preconditions?.evidenceDigest, envelope.evidence.evidenceDigest, ["evidence", "evidenceDigest"]);
432
+ const passedChecks = envelope.evidence.checks.filter((check) => check.status === "passed").map((check) => check.checkId);
433
+ const failedChecks = envelope.evidence.checks.filter((check) => check.status === "failed").map((check) => check.checkId);
434
+ const skippedChecks = envelope.evidence.checks.filter((check) => check.status === "skipped").map((check) => check.checkId);
435
+ const passedCheckSet = new Set(passedChecks);
436
+ const requiredPassed = [...preconditions?.requiredPassedChecks ?? []].sort();
437
+ const missingPassed = requiredPassed.filter((checkId) => !passedCheckSet.has(checkId));
438
+ for (const checkId of missingPassed) {
439
+ issues.push({
440
+ code: "missing-passed-check",
441
+ path: ["evidence", "checks"],
442
+ message: `Required passed check is not recorded: ${checkId}.`,
443
+ expected: checkId
444
+ });
445
+ }
446
+ const expiredApprovals = new Set(expiredApprovalIds);
447
+ const activeApprovals = (envelope.approvals ?? []).filter((approval) => !expiredApprovals.has(approval.approvalId));
448
+ const approvalDigests = new Set(activeApprovals.map((approval) => approval.approvalDigest));
449
+ for (const requiredDigest of preconditions?.requiredApprovalDigests ?? []) {
450
+ if (!approvalDigests.has(requiredDigest)) {
451
+ issues.push({
452
+ code: "missing-approval-digest",
453
+ path: ["approvals"],
454
+ message: `Required active approval digest is not recorded: ${requiredDigest}.`,
455
+ expected: requiredDigest
456
+ });
457
+ }
458
+ }
459
+ const approvedScopes = new Set(activeApprovals.flatMap((approval) => approval.scopes));
460
+ const requiredScopes = [...preconditions?.requiredApprovalScopes ?? []].sort();
461
+ const missingScopes = requiredScopes.filter((scope) => !approvedScopes.has(scope));
462
+ for (const scope of missingScopes) {
463
+ issues.push({
464
+ code: "missing-approval-scope",
465
+ path: ["approvals"],
466
+ message: `Required active approval scope is not recorded: ${scope}.`,
467
+ expected: scope
468
+ });
469
+ }
470
+ const attestationDigests = new Set((envelope.externalAttestations ?? []).map((attestation) => attestation.bundleDigest));
471
+ const requiredAttestationDigests = [...preconditions?.requiredAttestationDigests ?? []].sort();
472
+ const missingAttestationDigests = requiredAttestationDigests.filter((digest) => !attestationDigests.has(digest));
473
+ for (const requiredDigest of missingAttestationDigests) {
474
+ issues.push({
475
+ code: "missing-attestation-digest",
476
+ path: ["externalAttestations"],
477
+ message: `Required external attestation digest is not referenced: ${requiredDigest}.`,
478
+ expected: requiredDigest
479
+ });
480
+ }
481
+ return {
482
+ valid: issues.length === 0,
483
+ verificationScope: "integrity-expiration-and-preconditions-only",
484
+ envelope,
485
+ issues,
486
+ integrity: { envelopeDigestValid, evidenceDigestValid },
487
+ expiration: { checkedAt, envelopeNotYetValid, envelopeExpired, expiredApprovalIds },
488
+ checks: {
489
+ recorded: envelope.evidence.checks.length,
490
+ passed: passedChecks,
491
+ failed: failedChecks,
492
+ skipped: skippedChecks,
493
+ requiredPassed,
494
+ missingPassed
495
+ },
496
+ approvals: {
497
+ recorded: envelope.approvals?.length ?? 0,
498
+ requiredScopes,
499
+ missingScopes,
500
+ authenticity: "not-verified"
501
+ },
502
+ externalAttestations: {
503
+ referenced: envelope.externalAttestations?.length ?? 0,
504
+ requiredDigests: requiredAttestationDigests,
505
+ missingDigests: missingAttestationDigests,
506
+ authenticity: "not-verified"
507
+ }
508
+ };
509
+ };
510
+
511
+ export { CHANGE_ENVELOPE_SCHEMA_VERSION, CHANGE_ENVELOPE_DIGEST_ALGORITHM, MAX_CHANGE_ENVELOPE_CHECKS, MAX_CHANGE_ENVELOPE_APPROVALS, MAX_CHANGE_ENVELOPE_APPROVAL_SCOPES, MAX_CHANGE_ENVELOPE_ATTESTATIONS, changeEnvelopeCheckSchema, changeEnvelopeApprovalSchema, externalAttestationReferenceSchema, changeEnvelopeBaseSchema, changeEnvelopePatchSchema, changeEnvelopeFingerprintsSchema, changeEnvelopeEvidenceSchema, changeEnvelopeInputSchema, changeEnvelopeSchema, digestChangeEnvelopeArtifact, createChangeEnvelope, canonicalizeChangeEnvelope, computeChangeEnvelopeDigest, computeChangeEnvelopeEvidenceDigest, changeEnvelopePreconditionsSchema, verifyChangeEnvelope };