@zhivex-ai/harness 1.4.0 → 1.5.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.
- package/CHANGELOG.md +32 -10
- package/README.md +7 -5
- package/SUPPORT.md +2 -2
- package/contracts/client-protocol-v1.schema.json +593 -0
- package/contracts/engine-api.json +156 -0
- package/contracts/public-api.json +12 -0
- package/contracts/stable-api-signatures.json +36 -36
- package/dist/acp-cli.js +54 -20
- package/dist/approvals/approval-diff.d.ts +2 -1
- package/dist/approvals/approval-preview.d.ts +1 -1
- package/dist/audit/index.d.ts +3 -0
- package/dist/audit/index.js +482 -0
- package/dist/audit/pack.d.ts +72 -0
- package/dist/audit-pack-kvw7ng34.js +532 -0
- package/dist/cli/arguments.d.ts +3 -1
- package/dist/cli/audit-pack.d.ts +24 -0
- package/dist/cli/cli-credentials.d.ts +6 -0
- package/dist/cli/cli-profiles.d.ts +4 -2
- package/dist/cli/node-engine.d.ts +2 -0
- package/dist/cli-entry-75j3r49b.js +484 -0
- package/dist/cli-entry-928p8h41.js +10067 -0
- package/dist/cli-entry-h2yc9x37.js +10350 -0
- package/dist/{cli-entry-20w6nh0k.js → cli-entry-hdtfef27.js} +439 -19
- package/dist/{cli-entry-w0hterjs.js → cli-entry-q8bgkn9m.js} +40 -13
- package/dist/{cli-2h37qyvg.js → cli-vyrytn3m.js} +253 -644
- package/dist/cli.js +27 -2
- package/dist/client/json-contracts.d.ts +4 -0
- package/dist/client/protocol.d.ts +133 -0
- package/dist/client/task-control-host.d.ts +400 -0
- package/dist/client/task-projection-host.d.ts +12 -0
- package/dist/client/task-projection.d.ts +168 -0
- package/dist/compat/code-support.js +28 -22
- package/dist/{config-qt69hkqj.js → config-r9fdhce6.js} +3 -3
- package/dist/{config-j9624t6t.js → config-vyajmksy.js} +1 -1
- package/dist/engine/acp.js +15 -11
- package/dist/engine/client.js +14 -10
- package/dist/engine/desktop-providers-v1.d.ts +2 -0
- package/dist/engine/desktop-providers-v1.js +23 -3
- package/dist/engine/desktop-state-v1.js +11 -8
- package/dist/engine/index.d.ts +3 -0
- package/dist/engine/index.js +101 -554
- package/dist/engine/mcp-stdio-v1.js +8 -6
- package/dist/engine/models.js +76 -4
- package/dist/engine/protocol.d.ts +2 -0
- package/dist/engine/protocol.js +110 -0
- package/dist/engine/service.js +15 -11
- package/dist/hostile-repository-demo.js +1232 -564
- package/dist/{index-8zjmnyw5.js → index-1265j7t2.js} +73 -6
- package/dist/{index-nmjdwd4p.js → index-4gt045j0.js} +109 -1
- package/dist/{index-pdmevfxv.js → index-5zbq9ztj.js} +5 -259
- package/dist/{index-6dft2vev.js → index-63gbp0cz.js} +5 -3
- package/dist/index-6dxrbnxm.js +719 -0
- package/dist/{index-pp474137.js → index-6fp7kdpx.js} +769 -933
- package/dist/{index-61gthdz6.js → index-8sm2a1wa.js} +1 -1
- package/dist/{index-rkbf7dqg.js → index-96ezrvg2.js} +57 -13
- package/dist/index-bt2p8x7r.js +329 -0
- package/dist/index-c39197wq.js +552 -0
- package/dist/{index-m82cfspw.js → index-eps3c3g6.js} +877 -963
- package/dist/{index-s2k2edts.js → index-fqqz1yt8.js} +110 -1544
- package/dist/{index-m34q25jj.js → index-ggstwr0v.js} +192 -838
- package/dist/index-jpdvn09j.js +490 -0
- package/dist/{index-xqxeypt5.js → index-mzydbzp8.js} +76 -4
- package/dist/index-pq3kz1r8.js +269 -0
- package/dist/{index-wg1sd3at.js → index-qfsrrnz6.js} +1 -1
- package/dist/{index-937cb7y8.js → index-vc7yxz50.js} +561 -35
- package/dist/{index-5jhgatga.js → index-vvzc5ezz.js} +7 -6
- package/dist/index-wwthv10a.js +1507 -0
- package/dist/{index-tfzta544.js → index-xfedewpt.js} +17 -9
- package/dist/index-yx52bt4a.js +511 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +93 -77
- package/dist/integrations/mcp-stdio-tools.d.ts +4 -1
- package/dist/internal/desktop/providers.d.ts +2 -0
- package/dist/models/catalog.d.ts +2 -0
- package/dist/persistence/sqlite-database.d.ts +1 -0
- package/dist/providers/azure-openai.d.ts +14 -0
- package/dist/providers/bedrock.d.ts +23 -0
- package/dist/providers/providers.d.ts +1 -1
- package/dist/runtime/budget-ledger-scope.d.ts +14 -0
- package/dist/runtime/durable-review-group.d.ts +33 -0
- package/dist/runtime/harness.d.ts +11 -0
- package/dist/runtime/run-interruption.d.ts +7 -0
- package/dist/runtime/shared-budget.d.ts +30 -0
- package/dist/runtime/task-continuity.d.ts +300 -77
- package/dist/service-cli.js +15 -11
- package/dist/{service-client-cli-fn54ev6w.js → service-client-cli-v6qptg9x.js} +400 -305
- package/dist/tools/workspace.d.ts +2 -2
- package/dist/verify-historical-migrations.js +1237 -566
- package/dist/workspace/edit-contracts.d.ts +2 -2
- package/dist/workspace/path-policy.d.ts +2 -1
- package/dist/workspace/workspace.d.ts +13 -0
- package/dist/zhx.js +27 -2
- package/docs/AUDIT_PACK.md +99 -0
- package/docs/AZURE_OPENAI.md +53 -0
- package/docs/BEDROCK.md +56 -0
- package/docs/CLI.md +15 -0
- package/docs/CLIENT_PROTOCOL.md +178 -0
- package/docs/CODE_RELEASE.md +59 -7
- package/docs/CODE_WEB_TASKS.md +116 -0
- package/docs/CONTEXT_ENGINEERING.md +59 -0
- package/docs/CREDENTIALS.md +21 -0
- package/docs/DURABLE_OPERATIONS.md +93 -1
- package/docs/ENGINE_API.md +5 -2
- package/docs/EXTENSIBILITY.md +8 -4
- package/docs/FIRST_USE.md +6 -6
- package/docs/MINIMUM_TASK_AUTHORITY.md +3 -3
- package/docs/PROJECT_MEMORY.md +1 -1
- package/docs/PUBLIC_SECURITY.md +14 -1
- package/docs/README.md +6 -1
- package/docs/REPOSITORY_EDITING.md +1 -1
- package/docs/STABILITY.md +3 -1
- package/docs/SUPPORT_MATRIX.md +1 -1
- package/docs/SUPPORT_MATRIX_1_1.md +1 -1
- package/docs/TOOL_POLICY.md +4 -1
- package/docs/USAGE.md +3 -1
- package/docs/VERTEX.md +2 -2
- package/docs/WEB_LIMITS.md +4 -3
- package/docs/adr/0002-isolated-mcp-stdio.md +7 -4
- package/docs/support-matrix-1.1.json +3 -1
- package/docs/support-matrix.json +6 -4
- package/evaluations/code-task-ui.json +20 -0
- package/evaluations/representative-assembly-matrix.json +19 -1
- package/evaluations/task-projection.json +10 -0
- package/evaluations/task-recovery-lab.json +60 -0
- package/examples/README.md +3 -3
- package/package.json +23 -13
- package/dist/cli-entry-4ngptr62.js +0 -19900
- package/dist/index-m49jx72n.js +0 -220
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
workspaceCheckpointSchema,
|
|
3
3
|
workspaceRestoreOperationSchema
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-63gbp0cz.js";
|
|
5
5
|
import {
|
|
6
6
|
HARNESS_OPERATIONS_SCHEMA_VERSION,
|
|
7
|
-
HARNESS_SESSION_SCHEMA_VERSION,
|
|
8
7
|
HARNESS_SQLITE_FILE,
|
|
9
|
-
assertTaskBudgetBackupLinks,
|
|
10
8
|
decodeProjectMemory,
|
|
11
|
-
openCliSessionStore,
|
|
12
9
|
openHarnessPersistence,
|
|
13
10
|
projectMemoryRecordKey,
|
|
14
|
-
sqliteAdapter
|
|
11
|
+
sqliteAdapter
|
|
12
|
+
} from "./index-wwthv10a.js";
|
|
13
|
+
import {
|
|
14
|
+
BUDGET_LEDGER_NAMESPACE,
|
|
15
|
+
BUDGET_OWNER_NAMESPACE_KEY,
|
|
16
|
+
HARNESS_SESSION_SCHEMA_VERSION,
|
|
17
|
+
assertTaskBudgetBackupLinks,
|
|
18
|
+
budgetLedgerNamespace,
|
|
19
|
+
openCliSessionStore,
|
|
15
20
|
validateStateDirectory
|
|
16
|
-
} from "./index-
|
|
21
|
+
} from "./index-fqqz1yt8.js";
|
|
17
22
|
import {
|
|
18
23
|
SqliteDatabase,
|
|
19
24
|
readRegularFileChunksNoFollow
|
|
20
|
-
} from "./index-
|
|
25
|
+
} from "./index-6dxrbnxm.js";
|
|
21
26
|
import {
|
|
22
27
|
HarnessStateConflictError,
|
|
23
28
|
HarnessWorkspaceError
|
|
@@ -396,7 +401,8 @@ var coordinatorIds = (runs) => new Set(runs.flatMap(({ state }) => {
|
|
|
396
401
|
var validateBudgetLedger = (config, run, identities) => {
|
|
397
402
|
const state = validatedRunState(run.state, `Budget ledger ${run.key}`);
|
|
398
403
|
const identity = state.metadata?.budgetIdentity;
|
|
399
|
-
|
|
404
|
+
const ownerNamespace = config.scope.namespace;
|
|
405
|
+
if (!ownerNamespace || state.scope?.namespace !== budgetLedgerNamespace(ownerNamespace) || state.metadata?.[BUDGET_OWNER_NAMESPACE_KEY] !== ownerNamespace || state.scope?.tenantId !== config.scope.tenantId || (state.scope?.userId ?? undefined) !== (config.scope.userId ?? undefined) || state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true || typeof identity !== "string" || !identities.has(identity) || !/^budget_[a-f0-9]{64}$/.test(state.runId) || !sdkRunKeyMatches(run.key, state.scope, state.runId)) {
|
|
400
406
|
throw new HarnessStateConflictError("Budget ledger is not bound to an exported run and durable scope.");
|
|
401
407
|
}
|
|
402
408
|
assertTerminalState(state, `Budget ledger ${run.key}`);
|
|
@@ -530,7 +536,9 @@ var readPayload = async (config, recordedWorkspace, accessLease) => {
|
|
|
530
536
|
const metadata = state.metadata;
|
|
531
537
|
if (typeof metadata?.budgetIdentity !== "string" || !identities.has(metadata.budgetIdentity)) {
|
|
532
538
|
const scope = state.scope;
|
|
533
|
-
|
|
539
|
+
const sameTenant = scope?.tenantId === config.scope.tenantId && (scope?.userId ?? undefined) === (config.scope.userId ?? undefined);
|
|
540
|
+
const boundNamespace = config.scope.namespace ? budgetLedgerNamespace(config.scope.namespace) : undefined;
|
|
541
|
+
if (sameTenant && (scope?.namespace === BUDGET_LEDGER_NAMESPACE || scope?.namespace === boundNamespace)) {
|
|
534
542
|
throw new HarnessStateConflictError("An unlinked shared budget ledger cannot be safely assigned to this backup scope; retain the original database.");
|
|
535
543
|
}
|
|
536
544
|
return [];
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fileDigestSchema
|
|
3
|
+
} from "./index-6dxrbnxm.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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -60,3 +60,6 @@ export { serveAcpStdio } from "./client/acp-stdio.js";
|
|
|
60
60
|
export { openWorkspaceCheckpointStore } from "./persistence/workspace-checkpoints.js";
|
|
61
61
|
export type { WorkspaceCheckpoint, WorkspaceRestoreOperation } from "./persistence/workspace-checkpoints.js";
|
|
62
62
|
export type { HarnessMcpResourceClient } from "./integrations/mcp.js";
|
|
63
|
+
export { runHarnessDurableReviewGroup, inspectHarnessReviewGroup, cancelHarnessReviewGroup } from "./runtime/durable-review-group.js";
|
|
64
|
+
export type { HarnessDurableReviewGroupResult } from "./runtime/durable-review-group.js";
|
|
65
|
+
export type { HarnessSharedBudgetOptions } from "./runtime/shared-budget.js";
|