@peac/schema 0.10.9 → 0.10.11
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/LICENSE +1 -1
- package/dist/attestation-receipt.cjs +127 -0
- package/dist/attestation-receipt.cjs.map +1 -0
- package/dist/attestation-receipt.mjs +113 -0
- package/dist/attestation-receipt.mjs.map +1 -0
- package/dist/attribution.cjs +249 -0
- package/dist/attribution.cjs.map +1 -0
- package/dist/attribution.mjs +227 -0
- package/dist/attribution.mjs.map +1 -0
- package/dist/dispute.d.ts.map +1 -1
- package/dist/index.cjs +2818 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +2577 -0
- package/dist/index.mjs.map +1 -0
- package/dist/interaction.cjs +619 -0
- package/dist/interaction.cjs.map +1 -0
- package/dist/interaction.mjs +583 -0
- package/dist/interaction.mjs.map +1 -0
- package/dist/normalize.cjs +84 -0
- package/dist/normalize.cjs.map +1 -0
- package/dist/normalize.d.ts +15 -9
- package/dist/normalize.d.ts.map +1 -1
- package/dist/normalize.mjs +82 -0
- package/dist/normalize.mjs.map +1 -0
- package/dist/receipt-parser.cjs +333 -0
- package/dist/receipt-parser.cjs.map +1 -0
- package/dist/receipt-parser.mjs +331 -0
- package/dist/receipt-parser.mjs.map +1 -0
- package/dist/workflow.cjs +321 -0
- package/dist/workflow.cjs.map +1 -0
- package/dist/workflow.mjs +292 -0
- package/dist/workflow.mjs.map +1 -0
- package/package.json +50 -6
- package/dist/agent-identity.js +0 -357
- package/dist/agent-identity.js.map +0 -1
- package/dist/attestation-receipt.js +0 -249
- package/dist/attestation-receipt.js.map +0 -1
- package/dist/attribution.js +0 -444
- package/dist/attribution.js.map +0 -1
- package/dist/constants.js +0 -73
- package/dist/constants.js.map +0 -1
- package/dist/control.js +0 -9
- package/dist/control.js.map +0 -1
- package/dist/dispute.js +0 -832
- package/dist/dispute.js.map +0 -1
- package/dist/envelope.js +0 -9
- package/dist/envelope.js.map +0 -1
- package/dist/errors.js +0 -116
- package/dist/errors.js.map +0 -1
- package/dist/evidence.js +0 -8
- package/dist/evidence.js.map +0 -1
- package/dist/index.js +0 -283
- package/dist/index.js.map +0 -1
- package/dist/interaction.js +0 -918
- package/dist/interaction.js.map +0 -1
- package/dist/json.js +0 -267
- package/dist/json.js.map +0 -1
- package/dist/normalize.js +0 -103
- package/dist/normalize.js.map +0 -1
- package/dist/obligations.js +0 -337
- package/dist/obligations.js.map +0 -1
- package/dist/purpose.js +0 -296
- package/dist/purpose.js.map +0 -1
- package/dist/receipt-parser.js +0 -89
- package/dist/receipt-parser.js.map +0 -1
- package/dist/schemas.js +0 -7
- package/dist/schemas.js.map +0 -1
- package/dist/subject.js +0 -9
- package/dist/subject.js.map +0 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
- package/dist/validators.js +0 -421
- package/dist/validators.js.map +0 -1
- package/dist/version.js +0 -7
- package/dist/version.js.map +0 -1
- package/dist/workflow.js +0 -523
- package/dist/workflow.js.map +0 -1
package/dist/interaction.js
DELETED
|
@@ -1,918 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* PEAC Interaction Evidence Types (v0.10.7+)
|
|
4
|
-
*
|
|
5
|
-
* Interaction evidence captures what happened during agent execution.
|
|
6
|
-
* This is an extension type - stored at evidence.extensions["org.peacprotocol/interaction@0.1"]
|
|
7
|
-
*
|
|
8
|
-
* Design principles:
|
|
9
|
-
* - Wire-stable: Uses extensions mechanism (NOT a top-level evidence field)
|
|
10
|
-
* - Hash-only by default: Privacy-preserving content digests
|
|
11
|
-
* - Open kind registry: Not a closed enum, converges through convention
|
|
12
|
-
* - 1 receipt = 1 interaction: No batching (use bundles for that)
|
|
13
|
-
*
|
|
14
|
-
* @see docs/specs/INTERACTION-EVIDENCE.md
|
|
15
|
-
*/
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.InteractionEvidenceV01Schema = exports.KindSchema = exports.RefsSchema = exports.PolicyContextSchema = exports.ResultSchema = exports.ResourceTargetSchema = exports.ToolTargetSchema = exports.ExecutorSchema = exports.PayloadRefSchema = exports.DigestSchema = exports.DigestAlgSchema = exports.DIGEST_VALUE_PATTERN = exports.EXTENSION_KEY_PATTERN = exports.KIND_FORMAT_PATTERN = exports.INTERACTION_LIMITS = exports.RESERVED_KIND_PREFIXES = exports.WELL_KNOWN_KINDS = exports.POLICY_DECISIONS = exports.REDACTION_MODES = exports.RESULT_STATUSES = exports.DIGEST_SIZE_CONSTANTS = exports.CANONICAL_DIGEST_ALGS = exports.INTERACTION_EXTENSION_KEY = void 0;
|
|
18
|
-
exports.validateInteractionOrdered = validateInteractionOrdered;
|
|
19
|
-
exports.validateInteraction = validateInteraction;
|
|
20
|
-
exports.validateInteractionEvidence = validateInteractionEvidence;
|
|
21
|
-
exports.isValidInteractionEvidence = isValidInteractionEvidence;
|
|
22
|
-
exports.isWellKnownKind = isWellKnownKind;
|
|
23
|
-
exports.isReservedKindPrefix = isReservedKindPrefix;
|
|
24
|
-
exports.isDigestTruncated = isDigestTruncated;
|
|
25
|
-
exports.getInteraction = getInteraction;
|
|
26
|
-
exports.setInteraction = setInteraction;
|
|
27
|
-
exports.hasInteraction = hasInteraction;
|
|
28
|
-
exports.createReceiptView = createReceiptView;
|
|
29
|
-
exports.createInteractionEvidence = createInteractionEvidence;
|
|
30
|
-
const zod_1 = require("zod");
|
|
31
|
-
// ============================================================================
|
|
32
|
-
// Constants
|
|
33
|
-
// ============================================================================
|
|
34
|
-
/**
|
|
35
|
-
* Extension key for interaction evidence
|
|
36
|
-
* Used in evidence.extensions['org.peacprotocol/interaction@0.1']
|
|
37
|
-
*/
|
|
38
|
-
exports.INTERACTION_EXTENSION_KEY = 'org.peacprotocol/interaction@0.1';
|
|
39
|
-
/**
|
|
40
|
-
* Canonical digest algorithms (NORMATIVE)
|
|
41
|
-
*
|
|
42
|
-
* k = 1024 bytes (binary), m = 1024*1024 bytes (binary)
|
|
43
|
-
* NO case-insensitivity, NO regex matching - strict canonical set only.
|
|
44
|
-
*/
|
|
45
|
-
exports.CANONICAL_DIGEST_ALGS = [
|
|
46
|
-
'sha-256', // Full SHA-256
|
|
47
|
-
'sha-256:trunc-64k', // First 64KB (65536 bytes)
|
|
48
|
-
'sha-256:trunc-1m', // First 1MB (1048576 bytes)
|
|
49
|
-
];
|
|
50
|
-
/**
|
|
51
|
-
* Size constants for digest truncation (NORMATIVE)
|
|
52
|
-
*/
|
|
53
|
-
exports.DIGEST_SIZE_CONSTANTS = {
|
|
54
|
-
k: 1024, // 1 KB = 1024 bytes (binary)
|
|
55
|
-
m: 1024 * 1024, // 1 MB = 1048576 bytes (binary)
|
|
56
|
-
'trunc-64k': 65536, // 64 * 1024
|
|
57
|
-
'trunc-1m': 1048576, // 1024 * 1024
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Result status values for interaction outcomes
|
|
61
|
-
*/
|
|
62
|
-
exports.RESULT_STATUSES = ['ok', 'error', 'timeout', 'canceled'];
|
|
63
|
-
/**
|
|
64
|
-
* Redaction modes for payload references
|
|
65
|
-
*/
|
|
66
|
-
exports.REDACTION_MODES = ['hash_only', 'redacted', 'plaintext_allowlisted'];
|
|
67
|
-
/**
|
|
68
|
-
* Policy decision values
|
|
69
|
-
*/
|
|
70
|
-
exports.POLICY_DECISIONS = ['allow', 'deny', 'constrained'];
|
|
71
|
-
/**
|
|
72
|
-
* Well-known interaction kinds (informational, not normative)
|
|
73
|
-
*
|
|
74
|
-
* The kind field accepts any string matching the kind grammar.
|
|
75
|
-
* These well-known values are listed in the PEAC registries for interop.
|
|
76
|
-
* New kinds do NOT require protocol updates - just use the name.
|
|
77
|
-
*
|
|
78
|
-
* Custom kinds: use "custom:<reverse-dns>" or "<reverse-dns>:<token>"
|
|
79
|
-
*/
|
|
80
|
-
exports.WELL_KNOWN_KINDS = [
|
|
81
|
-
'tool.call',
|
|
82
|
-
'http.request',
|
|
83
|
-
'fs.read',
|
|
84
|
-
'fs.write',
|
|
85
|
-
'message',
|
|
86
|
-
];
|
|
87
|
-
/**
|
|
88
|
-
* Reserved kind prefixes (NORMATIVE)
|
|
89
|
-
*
|
|
90
|
-
* Kinds starting with these prefixes that are NOT in WELL_KNOWN_KINDS
|
|
91
|
-
* MUST be rejected to prevent namespace pollution.
|
|
92
|
-
*/
|
|
93
|
-
exports.RESERVED_KIND_PREFIXES = ['peac.', 'org.peacprotocol.'];
|
|
94
|
-
/**
|
|
95
|
-
* Interaction evidence limits (DoS protection)
|
|
96
|
-
*/
|
|
97
|
-
exports.INTERACTION_LIMITS = {
|
|
98
|
-
/** Maximum interaction ID length */
|
|
99
|
-
maxInteractionIdLength: 256,
|
|
100
|
-
/** Maximum kind length */
|
|
101
|
-
maxKindLength: 128,
|
|
102
|
-
/** Maximum platform name length */
|
|
103
|
-
maxPlatformLength: 64,
|
|
104
|
-
/** Maximum version string length */
|
|
105
|
-
maxVersionLength: 64,
|
|
106
|
-
/** Maximum plugin ID length */
|
|
107
|
-
maxPluginIdLength: 128,
|
|
108
|
-
/** Maximum tool name length */
|
|
109
|
-
maxToolNameLength: 256,
|
|
110
|
-
/** Maximum provider length */
|
|
111
|
-
maxProviderLength: 128,
|
|
112
|
-
/** Maximum URI length */
|
|
113
|
-
maxUriLength: 2048,
|
|
114
|
-
/** Maximum method length */
|
|
115
|
-
maxMethodLength: 16,
|
|
116
|
-
/** Maximum error code length */
|
|
117
|
-
maxErrorCodeLength: 128,
|
|
118
|
-
/** Maximum payment reference length */
|
|
119
|
-
maxPaymentReferenceLength: 256,
|
|
120
|
-
/** Maximum receipt RID length */
|
|
121
|
-
maxReceiptRidLength: 128,
|
|
122
|
-
};
|
|
123
|
-
// ============================================================================
|
|
124
|
-
// Shared Invariant Helpers (used by both schema and ordered validator)
|
|
125
|
-
// ============================================================================
|
|
126
|
-
/**
|
|
127
|
-
* Check if a kind requires a tool target (internal helper)
|
|
128
|
-
*/
|
|
129
|
-
function requiresTool(kind) {
|
|
130
|
-
return kind.startsWith('tool.');
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Check if a kind requires a resource target (internal helper)
|
|
134
|
-
*/
|
|
135
|
-
function requiresResource(kind) {
|
|
136
|
-
return kind.startsWith('http.') || kind.startsWith('fs.');
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Check if a kind uses a reserved prefix (internal helper)
|
|
140
|
-
*/
|
|
141
|
-
function usesReservedPrefix(kind) {
|
|
142
|
-
return exports.RESERVED_KIND_PREFIXES.some((prefix) => kind.startsWith(prefix));
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Check if a resource object is meaningful - has at least uri (internal helper)
|
|
146
|
-
* Empty resource objects {} are not valid targets.
|
|
147
|
-
*/
|
|
148
|
-
function hasMeaningfulResource(resource) {
|
|
149
|
-
if (!resource)
|
|
150
|
-
return false;
|
|
151
|
-
if (typeof resource.uri === 'string' && resource.uri.length > 0)
|
|
152
|
-
return true;
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
// ============================================================================
|
|
156
|
-
// Format Patterns
|
|
157
|
-
// ============================================================================
|
|
158
|
-
/**
|
|
159
|
-
* Kind format pattern
|
|
160
|
-
*
|
|
161
|
-
* Lowercase letters, digits, dots, underscores, colons, hyphens.
|
|
162
|
-
* Must start with a letter, end with letter or digit.
|
|
163
|
-
* Min 2 chars, max 128 chars.
|
|
164
|
-
*
|
|
165
|
-
* Examples: "tool.call", "http.request", "custom:com.example.foo"
|
|
166
|
-
*/
|
|
167
|
-
exports.KIND_FORMAT_PATTERN = /^[a-z][a-z0-9._:-]{0,126}[a-z0-9]$/;
|
|
168
|
-
/**
|
|
169
|
-
* Extension key format pattern (NORMATIVE)
|
|
170
|
-
*
|
|
171
|
-
* Reverse-DNS domain + '/' + key name + optional version
|
|
172
|
-
* Pattern: ^([a-z0-9-]+\.)+[a-z0-9-]+\/[a-z][a-z0-9._:-]{0,126}[a-z0-9](?:@[0-9]+(?:\.[0-9]+)*)?$
|
|
173
|
-
*
|
|
174
|
-
* Examples:
|
|
175
|
-
* - "com.example/foo"
|
|
176
|
-
* - "org.peacprotocol/interaction@0.1"
|
|
177
|
-
* - "io.vendor/custom-data"
|
|
178
|
-
*/
|
|
179
|
-
exports.EXTENSION_KEY_PATTERN = /^([a-z0-9-]+\.)+[a-z0-9-]+\/[a-z][a-z0-9._:-]{0,126}[a-z0-9](?:@[0-9]+(?:\.[0-9]+)*)?$/;
|
|
180
|
-
/**
|
|
181
|
-
* Digest value format (64 lowercase hex chars)
|
|
182
|
-
*/
|
|
183
|
-
exports.DIGEST_VALUE_PATTERN = /^[a-f0-9]{64}$/;
|
|
184
|
-
// ============================================================================
|
|
185
|
-
// Zod Schemas
|
|
186
|
-
// ============================================================================
|
|
187
|
-
/**
|
|
188
|
-
* Digest algorithm schema - strict canonical set
|
|
189
|
-
*/
|
|
190
|
-
exports.DigestAlgSchema = zod_1.z.enum(exports.CANONICAL_DIGEST_ALGS);
|
|
191
|
-
/**
|
|
192
|
-
* Digest schema for privacy-preserving content hashing
|
|
193
|
-
*/
|
|
194
|
-
exports.DigestSchema = zod_1.z
|
|
195
|
-
.object({
|
|
196
|
-
/** Algorithm: 'sha-256' (full) or 'sha-256:trunc-{size}' (truncated) */
|
|
197
|
-
alg: exports.DigestAlgSchema,
|
|
198
|
-
/** 64 lowercase hex chars (SHA-256 output) */
|
|
199
|
-
value: zod_1.z.string().regex(exports.DIGEST_VALUE_PATTERN, 'Must be 64 lowercase hex chars'),
|
|
200
|
-
/** Original byte length before any truncation (REQUIRED) */
|
|
201
|
-
bytes: zod_1.z.number().int().nonnegative(),
|
|
202
|
-
})
|
|
203
|
-
.strict();
|
|
204
|
-
/**
|
|
205
|
-
* Payload reference schema (on-wire, portable)
|
|
206
|
-
*/
|
|
207
|
-
exports.PayloadRefSchema = zod_1.z
|
|
208
|
-
.object({
|
|
209
|
-
/** Content digest */
|
|
210
|
-
digest: exports.DigestSchema,
|
|
211
|
-
/** Redaction mode */
|
|
212
|
-
redaction: zod_1.z.enum(exports.REDACTION_MODES),
|
|
213
|
-
})
|
|
214
|
-
.strict();
|
|
215
|
-
/**
|
|
216
|
-
* Executor identity schema (who ran this)
|
|
217
|
-
*/
|
|
218
|
-
exports.ExecutorSchema = zod_1.z
|
|
219
|
-
.object({
|
|
220
|
-
/** Platform identifier: 'openclaw', 'mcp', 'a2a', 'claude-code' */
|
|
221
|
-
platform: zod_1.z.string().min(1).max(exports.INTERACTION_LIMITS.maxPlatformLength),
|
|
222
|
-
/** Platform version */
|
|
223
|
-
version: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxVersionLength).optional(),
|
|
224
|
-
/** Plugin that captured this */
|
|
225
|
-
plugin_id: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxPluginIdLength).optional(),
|
|
226
|
-
/** Hash of plugin package (provenance) */
|
|
227
|
-
plugin_digest: exports.DigestSchema.optional(),
|
|
228
|
-
})
|
|
229
|
-
.strict();
|
|
230
|
-
/**
|
|
231
|
-
* Tool target schema (for tool.call kind)
|
|
232
|
-
*/
|
|
233
|
-
exports.ToolTargetSchema = zod_1.z
|
|
234
|
-
.object({
|
|
235
|
-
/** Tool name */
|
|
236
|
-
name: zod_1.z.string().min(1).max(exports.INTERACTION_LIMITS.maxToolNameLength),
|
|
237
|
-
/** Tool provider */
|
|
238
|
-
provider: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxProviderLength).optional(),
|
|
239
|
-
/** Tool version */
|
|
240
|
-
version: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxVersionLength).optional(),
|
|
241
|
-
})
|
|
242
|
-
.strict();
|
|
243
|
-
/**
|
|
244
|
-
* Resource target schema (for http/fs kinds)
|
|
245
|
-
*/
|
|
246
|
-
exports.ResourceTargetSchema = zod_1.z
|
|
247
|
-
.object({
|
|
248
|
-
/** Resource URI */
|
|
249
|
-
uri: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxUriLength).optional(),
|
|
250
|
-
/** HTTP method or operation */
|
|
251
|
-
method: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxMethodLength).optional(),
|
|
252
|
-
})
|
|
253
|
-
.strict();
|
|
254
|
-
/**
|
|
255
|
-
* Execution result schema
|
|
256
|
-
*/
|
|
257
|
-
exports.ResultSchema = zod_1.z
|
|
258
|
-
.object({
|
|
259
|
-
/** Result status */
|
|
260
|
-
status: zod_1.z.enum(exports.RESULT_STATUSES),
|
|
261
|
-
/** Error code (PEAC error code or namespaced) */
|
|
262
|
-
error_code: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxErrorCodeLength).optional(),
|
|
263
|
-
/** Whether the operation can be retried */
|
|
264
|
-
retryable: zod_1.z.boolean().optional(),
|
|
265
|
-
})
|
|
266
|
-
.strict();
|
|
267
|
-
/**
|
|
268
|
-
* Policy context schema (policy state at execution time)
|
|
269
|
-
*/
|
|
270
|
-
exports.PolicyContextSchema = zod_1.z
|
|
271
|
-
.object({
|
|
272
|
-
/** Policy decision */
|
|
273
|
-
decision: zod_1.z.enum(exports.POLICY_DECISIONS),
|
|
274
|
-
/** Whether sandbox mode was enabled */
|
|
275
|
-
sandbox_enabled: zod_1.z.boolean().optional(),
|
|
276
|
-
/** Whether elevated permissions were granted */
|
|
277
|
-
elevated: zod_1.z.boolean().optional(),
|
|
278
|
-
/** Hash of effective policy document */
|
|
279
|
-
effective_policy_digest: exports.DigestSchema.optional(),
|
|
280
|
-
})
|
|
281
|
-
.strict();
|
|
282
|
-
/**
|
|
283
|
-
* References schema (links to related evidence)
|
|
284
|
-
*/
|
|
285
|
-
exports.RefsSchema = zod_1.z
|
|
286
|
-
.object({
|
|
287
|
-
/** Links to evidence.payment.reference */
|
|
288
|
-
payment_reference: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxPaymentReferenceLength).optional(),
|
|
289
|
-
/** Correlation across receipts */
|
|
290
|
-
related_receipt_rid: zod_1.z.string().max(exports.INTERACTION_LIMITS.maxReceiptRidLength).optional(),
|
|
291
|
-
})
|
|
292
|
-
.strict();
|
|
293
|
-
/**
|
|
294
|
-
* Kind schema with format validation
|
|
295
|
-
*/
|
|
296
|
-
exports.KindSchema = zod_1.z
|
|
297
|
-
.string()
|
|
298
|
-
.min(2)
|
|
299
|
-
.max(exports.INTERACTION_LIMITS.maxKindLength)
|
|
300
|
-
.regex(exports.KIND_FORMAT_PATTERN, 'Invalid kind format');
|
|
301
|
-
/**
|
|
302
|
-
* Interaction evidence schema (base, without cross-field refinements)
|
|
303
|
-
*/
|
|
304
|
-
const InteractionEvidenceV01BaseSchema = zod_1.z
|
|
305
|
-
.object({
|
|
306
|
-
/** Stable ID for idempotency/dedupe (REQUIRED) */
|
|
307
|
-
interaction_id: zod_1.z.string().min(1).max(exports.INTERACTION_LIMITS.maxInteractionIdLength),
|
|
308
|
-
/** Event kind - open string, not closed enum (REQUIRED) */
|
|
309
|
-
kind: exports.KindSchema,
|
|
310
|
-
/** Executor identity (REQUIRED) */
|
|
311
|
-
executor: exports.ExecutorSchema,
|
|
312
|
-
/** Tool target (when kind is tool-related) */
|
|
313
|
-
tool: exports.ToolTargetSchema.optional(),
|
|
314
|
-
/** Resource target (when kind is http/fs-related) */
|
|
315
|
-
resource: exports.ResourceTargetSchema.optional(),
|
|
316
|
-
/** Input payload reference */
|
|
317
|
-
input: exports.PayloadRefSchema.optional(),
|
|
318
|
-
/** Output payload reference */
|
|
319
|
-
output: exports.PayloadRefSchema.optional(),
|
|
320
|
-
/** Start time (RFC 3339) (REQUIRED) */
|
|
321
|
-
started_at: zod_1.z.string().datetime(),
|
|
322
|
-
/** Completion time (RFC 3339) */
|
|
323
|
-
completed_at: zod_1.z.string().datetime().optional(),
|
|
324
|
-
/** Duration in milliseconds (OPTIONAL, non-normative) */
|
|
325
|
-
duration_ms: zod_1.z.number().int().nonnegative().optional(),
|
|
326
|
-
/** Execution outcome */
|
|
327
|
-
result: exports.ResultSchema.optional(),
|
|
328
|
-
/** Policy context at execution */
|
|
329
|
-
policy: exports.PolicyContextSchema.optional(),
|
|
330
|
-
/** References to related evidence */
|
|
331
|
-
refs: exports.RefsSchema.optional(),
|
|
332
|
-
/** Platform-specific extensions (MUST be namespaced) */
|
|
333
|
-
extensions: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
334
|
-
})
|
|
335
|
-
.strict();
|
|
336
|
-
/**
|
|
337
|
-
* Interaction evidence schema with invariant refinements
|
|
338
|
-
*
|
|
339
|
-
* ALL REJECT invariants are enforced here. This ensures that
|
|
340
|
-
* both direct schema validation and ordered validation produce
|
|
341
|
-
* consistent results.
|
|
342
|
-
*/
|
|
343
|
-
exports.InteractionEvidenceV01Schema = InteractionEvidenceV01BaseSchema.superRefine((ev, ctx) => {
|
|
344
|
-
// Invariant 1: completed_at >= started_at
|
|
345
|
-
if (ev.completed_at && ev.started_at) {
|
|
346
|
-
const startedAt = new Date(ev.started_at).getTime();
|
|
347
|
-
const completedAt = new Date(ev.completed_at).getTime();
|
|
348
|
-
if (completedAt < startedAt) {
|
|
349
|
-
ctx.addIssue({
|
|
350
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
351
|
-
message: 'completed_at must be >= started_at',
|
|
352
|
-
path: ['completed_at'],
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
// Invariant 2: output requires result.status
|
|
357
|
-
if (ev.output && !ev.result?.status) {
|
|
358
|
-
ctx.addIssue({
|
|
359
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
360
|
-
message: 'result.status is required when output is present',
|
|
361
|
-
path: ['result'],
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
// Invariant 3: error status requires error_code or NON-EMPTY extensions
|
|
365
|
-
// Empty extensions object {} is not valid detail
|
|
366
|
-
if (ev.result?.status === 'error') {
|
|
367
|
-
const hasErrorCode = Boolean(ev.result.error_code);
|
|
368
|
-
const hasNonEmptyExtensions = ev.extensions !== undefined && Object.keys(ev.extensions).length > 0;
|
|
369
|
-
if (!hasErrorCode && !hasNonEmptyExtensions) {
|
|
370
|
-
ctx.addIssue({
|
|
371
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
372
|
-
message: 'error_code or non-empty extensions required when status is error',
|
|
373
|
-
path: ['result', 'error_code'],
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
// Invariant 4: extension keys must be properly namespaced
|
|
378
|
-
if (ev.extensions) {
|
|
379
|
-
for (const key of Object.keys(ev.extensions)) {
|
|
380
|
-
if (!exports.EXTENSION_KEY_PATTERN.test(key)) {
|
|
381
|
-
ctx.addIssue({
|
|
382
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
383
|
-
message: `Invalid extension key format: ${key} (must be reverse-DNS/name[@version])`,
|
|
384
|
-
path: ['extensions', key],
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
// Invariant 5: reserved kind prefixes (REJECT rule)
|
|
390
|
-
// Kinds starting with peac.* or org.peacprotocol.* that are NOT in WELL_KNOWN_KINDS are rejected
|
|
391
|
-
if (usesReservedPrefix(ev.kind) &&
|
|
392
|
-
!exports.WELL_KNOWN_KINDS.includes(ev.kind)) {
|
|
393
|
-
ctx.addIssue({
|
|
394
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
395
|
-
message: `kind "${ev.kind}" uses reserved prefix`,
|
|
396
|
-
path: ['kind'],
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
// Invariant 6: target consistency (REJECT rule)
|
|
400
|
-
// tool.* kinds MUST have tool field with name
|
|
401
|
-
if (requiresTool(ev.kind) && !ev.tool) {
|
|
402
|
-
ctx.addIssue({
|
|
403
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
404
|
-
message: `kind "${ev.kind}" requires tool field`,
|
|
405
|
-
path: ['tool'],
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
// http.* and fs.* kinds MUST have resource field with meaningful content (at least uri)
|
|
409
|
-
if (requiresResource(ev.kind)) {
|
|
410
|
-
if (!ev.resource) {
|
|
411
|
-
ctx.addIssue({
|
|
412
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
413
|
-
message: `kind "${ev.kind}" requires resource field`,
|
|
414
|
-
path: ['resource'],
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
else if (!hasMeaningfulResource(ev.resource)) {
|
|
418
|
-
ctx.addIssue({
|
|
419
|
-
code: zod_1.z.ZodIssueCode.custom,
|
|
420
|
-
message: `kind "${ev.kind}" requires resource.uri to be non-empty`,
|
|
421
|
-
path: ['resource', 'uri'],
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
// ============================================================================
|
|
427
|
-
// Ordered Validation (Conformance)
|
|
428
|
-
// ============================================================================
|
|
429
|
-
/**
|
|
430
|
-
* Validate interaction evidence with explicit evaluation ordering.
|
|
431
|
-
*
|
|
432
|
-
* Returns canonical error codes per validation ordering.
|
|
433
|
-
* This function does NOT depend on Zod's internal validation ordering.
|
|
434
|
-
* Cross-language implementations MUST produce identical error_code values
|
|
435
|
-
* for the same invalid input.
|
|
436
|
-
*
|
|
437
|
-
* Validation order:
|
|
438
|
-
* 1. Required field presence
|
|
439
|
-
* 2. Required field format (interaction_id, kind, started_at)
|
|
440
|
-
* 3. Kind format and reserved prefix check
|
|
441
|
-
* 4. Executor field validation
|
|
442
|
-
* 5. Optional field format (completed_at, digests, etc.)
|
|
443
|
-
* 6. Cross-field invariants (timing, output-result, error-detail)
|
|
444
|
-
* 7. Extension key namespacing
|
|
445
|
-
* 8. Target consistency (kind prefix -> target field)
|
|
446
|
-
*
|
|
447
|
-
* @param input - Raw input to validate
|
|
448
|
-
* @returns Validation result with canonical error codes on failure
|
|
449
|
-
*/
|
|
450
|
-
function validateInteractionOrdered(input) {
|
|
451
|
-
const errors = [];
|
|
452
|
-
const warnings = [];
|
|
453
|
-
// Step 1: Type check (arrays are typeof 'object' but not valid input)
|
|
454
|
-
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
455
|
-
return {
|
|
456
|
-
valid: false,
|
|
457
|
-
errors: [
|
|
458
|
-
{
|
|
459
|
-
code: 'E_INTERACTION_INVALID_FORMAT',
|
|
460
|
-
message: 'Input must be an object',
|
|
461
|
-
},
|
|
462
|
-
],
|
|
463
|
-
warnings: [],
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
const obj = input;
|
|
467
|
-
// Step 2: Required field presence
|
|
468
|
-
if (typeof obj.interaction_id !== 'string' || obj.interaction_id.length === 0) {
|
|
469
|
-
errors.push({
|
|
470
|
-
code: 'E_INTERACTION_MISSING_ID',
|
|
471
|
-
message: 'interaction_id is required',
|
|
472
|
-
field: 'interaction_id',
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
else if (obj.interaction_id.length > exports.INTERACTION_LIMITS.maxInteractionIdLength) {
|
|
476
|
-
errors.push({
|
|
477
|
-
code: 'E_INTERACTION_INVALID_FORMAT',
|
|
478
|
-
message: `interaction_id exceeds max length (${exports.INTERACTION_LIMITS.maxInteractionIdLength})`,
|
|
479
|
-
field: 'interaction_id',
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
// Step 3: Kind format validation
|
|
483
|
-
// Empty string is semantically "missing", not "invalid format"
|
|
484
|
-
if (typeof obj.kind !== 'string' || obj.kind.length === 0) {
|
|
485
|
-
errors.push({
|
|
486
|
-
code: 'E_INTERACTION_MISSING_KIND',
|
|
487
|
-
message: 'kind is required',
|
|
488
|
-
field: 'kind',
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
else if (obj.kind.length < 2 || obj.kind.length > exports.INTERACTION_LIMITS.maxKindLength) {
|
|
492
|
-
errors.push({
|
|
493
|
-
code: 'E_INTERACTION_INVALID_KIND_FORMAT',
|
|
494
|
-
message: `kind must be 2-${exports.INTERACTION_LIMITS.maxKindLength} characters`,
|
|
495
|
-
field: 'kind',
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
else if (!exports.KIND_FORMAT_PATTERN.test(obj.kind)) {
|
|
499
|
-
errors.push({
|
|
500
|
-
code: 'E_INTERACTION_INVALID_KIND_FORMAT',
|
|
501
|
-
message: 'kind must match pattern: lowercase, start with letter, end with alphanumeric',
|
|
502
|
-
field: 'kind',
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
else {
|
|
506
|
-
// Check reserved prefixes
|
|
507
|
-
for (const prefix of exports.RESERVED_KIND_PREFIXES) {
|
|
508
|
-
if (obj.kind.startsWith(prefix) && !exports.WELL_KNOWN_KINDS.includes(obj.kind)) {
|
|
509
|
-
errors.push({
|
|
510
|
-
code: 'E_INTERACTION_KIND_RESERVED',
|
|
511
|
-
message: `kind "${obj.kind}" uses reserved prefix "${prefix}"`,
|
|
512
|
-
field: 'kind',
|
|
513
|
-
});
|
|
514
|
-
break;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
// Warn if not in well-known registry (but valid format)
|
|
518
|
-
if (errors.length === 0 &&
|
|
519
|
-
!exports.WELL_KNOWN_KINDS.includes(obj.kind)) {
|
|
520
|
-
warnings.push({
|
|
521
|
-
code: 'W_INTERACTION_KIND_UNREGISTERED',
|
|
522
|
-
message: `kind "${obj.kind}" is not in the well-known registry`,
|
|
523
|
-
field: 'kind',
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
// Step 4: started_at validation
|
|
528
|
-
// Invalid format is treated as "missing" because the value is unusable
|
|
529
|
-
// E_INTERACTION_INVALID_TIMING is reserved for relational errors (completed_at < started_at)
|
|
530
|
-
if (typeof obj.started_at !== 'string') {
|
|
531
|
-
errors.push({
|
|
532
|
-
code: 'E_INTERACTION_MISSING_STARTED_AT',
|
|
533
|
-
message: 'started_at is required',
|
|
534
|
-
field: 'started_at',
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
const startedAtDate = new Date(obj.started_at);
|
|
539
|
-
if (isNaN(startedAtDate.getTime())) {
|
|
540
|
-
errors.push({
|
|
541
|
-
code: 'E_INTERACTION_MISSING_STARTED_AT',
|
|
542
|
-
message: 'started_at must be a valid ISO 8601 datetime',
|
|
543
|
-
field: 'started_at',
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
// Step 5: Executor validation
|
|
548
|
-
if (typeof obj.executor !== 'object' || obj.executor === null) {
|
|
549
|
-
errors.push({
|
|
550
|
-
code: 'E_INTERACTION_MISSING_EXECUTOR',
|
|
551
|
-
message: 'executor is required',
|
|
552
|
-
field: 'executor',
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
const executor = obj.executor;
|
|
557
|
-
if (typeof executor.platform !== 'string' || executor.platform.length === 0) {
|
|
558
|
-
errors.push({
|
|
559
|
-
code: 'E_INTERACTION_MISSING_EXECUTOR',
|
|
560
|
-
message: 'executor.platform is required',
|
|
561
|
-
field: 'executor.platform',
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
else if (executor.platform.length > exports.INTERACTION_LIMITS.maxPlatformLength) {
|
|
565
|
-
errors.push({
|
|
566
|
-
code: 'E_INTERACTION_INVALID_FORMAT',
|
|
567
|
-
message: `executor.platform exceeds max length (${exports.INTERACTION_LIMITS.maxPlatformLength})`,
|
|
568
|
-
field: 'executor.platform',
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
// Return early if we have fatal errors from required fields
|
|
573
|
-
if (errors.length > 0) {
|
|
574
|
-
return { valid: false, errors, warnings };
|
|
575
|
-
}
|
|
576
|
-
// Step 6: Digest validation (optional fields)
|
|
577
|
-
const validateDigest = (digest, fieldPath) => {
|
|
578
|
-
const digestErrors = [];
|
|
579
|
-
if (typeof digest !== 'object' || digest === null) {
|
|
580
|
-
digestErrors.push({
|
|
581
|
-
code: 'E_INTERACTION_INVALID_DIGEST',
|
|
582
|
-
message: 'digest must be an object',
|
|
583
|
-
field: fieldPath,
|
|
584
|
-
});
|
|
585
|
-
return { errors: digestErrors, valid: false };
|
|
586
|
-
}
|
|
587
|
-
const d = digest;
|
|
588
|
-
if (!exports.CANONICAL_DIGEST_ALGS.includes(d.alg)) {
|
|
589
|
-
digestErrors.push({
|
|
590
|
-
code: 'E_INTERACTION_INVALID_DIGEST_ALG',
|
|
591
|
-
message: `digest.alg must be one of: ${exports.CANONICAL_DIGEST_ALGS.join(', ')}`,
|
|
592
|
-
field: `${fieldPath}.alg`,
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
if (typeof d.value !== 'string' || !exports.DIGEST_VALUE_PATTERN.test(d.value)) {
|
|
596
|
-
digestErrors.push({
|
|
597
|
-
code: 'E_INTERACTION_INVALID_DIGEST',
|
|
598
|
-
message: 'digest.value must be 64 lowercase hex chars',
|
|
599
|
-
field: `${fieldPath}.value`,
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
if (typeof d.bytes !== 'number' || !Number.isInteger(d.bytes) || d.bytes < 0) {
|
|
603
|
-
digestErrors.push({
|
|
604
|
-
code: 'E_INTERACTION_INVALID_DIGEST',
|
|
605
|
-
message: 'digest.bytes must be a non-negative integer',
|
|
606
|
-
field: `${fieldPath}.bytes`,
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
return { errors: digestErrors, valid: digestErrors.length === 0 };
|
|
610
|
-
};
|
|
611
|
-
// Validate input digest if present
|
|
612
|
-
if (obj.input !== undefined) {
|
|
613
|
-
const inputObj = obj.input;
|
|
614
|
-
if (inputObj.digest !== undefined) {
|
|
615
|
-
const result = validateDigest(inputObj.digest, 'input.digest');
|
|
616
|
-
errors.push(...result.errors);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
// Validate output digest if present
|
|
620
|
-
if (obj.output !== undefined) {
|
|
621
|
-
const outputObj = obj.output;
|
|
622
|
-
if (outputObj.digest !== undefined) {
|
|
623
|
-
const result = validateDigest(outputObj.digest, 'output.digest');
|
|
624
|
-
errors.push(...result.errors);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
// Step 7: Timing invariant (completed_at >= started_at)
|
|
628
|
-
if (typeof obj.completed_at === 'string' && typeof obj.started_at === 'string') {
|
|
629
|
-
const startedAt = new Date(obj.started_at).getTime();
|
|
630
|
-
const completedAt = new Date(obj.completed_at).getTime();
|
|
631
|
-
if (!isNaN(startedAt) && !isNaN(completedAt) && completedAt < startedAt) {
|
|
632
|
-
errors.push({
|
|
633
|
-
code: 'E_INTERACTION_INVALID_TIMING',
|
|
634
|
-
message: 'completed_at must be >= started_at',
|
|
635
|
-
field: 'completed_at',
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
// Step 8: Output requires result invariant
|
|
640
|
-
if (obj.output !== undefined) {
|
|
641
|
-
const result = obj.result;
|
|
642
|
-
if (!result?.status) {
|
|
643
|
-
errors.push({
|
|
644
|
-
code: 'E_INTERACTION_MISSING_RESULT',
|
|
645
|
-
message: 'result.status is required when output is present',
|
|
646
|
-
field: 'result',
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
// Step 9: Error status requires detail (error_code OR non-empty extensions)
|
|
651
|
-
// Empty extensions object {} is not valid detail
|
|
652
|
-
if (obj.result !== undefined) {
|
|
653
|
-
const result = obj.result;
|
|
654
|
-
if (result.status === 'error') {
|
|
655
|
-
const hasErrorCode = Boolean(result.error_code);
|
|
656
|
-
const hasNonEmptyExtensions = obj.extensions !== undefined &&
|
|
657
|
-
typeof obj.extensions === 'object' &&
|
|
658
|
-
obj.extensions !== null &&
|
|
659
|
-
Object.keys(obj.extensions).length > 0;
|
|
660
|
-
if (!hasErrorCode && !hasNonEmptyExtensions) {
|
|
661
|
-
errors.push({
|
|
662
|
-
code: 'E_INTERACTION_MISSING_ERROR_DETAIL',
|
|
663
|
-
message: 'error_code or non-empty extensions required when result.status is error',
|
|
664
|
-
field: 'result.error_code',
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
// Step 10: Extension key namespacing
|
|
670
|
-
if (obj.extensions !== undefined) {
|
|
671
|
-
if (typeof obj.extensions !== 'object' || obj.extensions === null) {
|
|
672
|
-
errors.push({
|
|
673
|
-
code: 'E_INTERACTION_INVALID_FORMAT',
|
|
674
|
-
message: 'extensions must be an object',
|
|
675
|
-
field: 'extensions',
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
else {
|
|
679
|
-
for (const key of Object.keys(obj.extensions)) {
|
|
680
|
-
if (!exports.EXTENSION_KEY_PATTERN.test(key)) {
|
|
681
|
-
errors.push({
|
|
682
|
-
code: 'E_INTERACTION_INVALID_EXTENSION_KEY',
|
|
683
|
-
message: `Invalid extension key format: "${key}" (must be reverse-DNS/name[@version])`,
|
|
684
|
-
field: `extensions.${key}`,
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
// Step 11: Target consistency (prefix-aware, using shared helpers)
|
|
691
|
-
const kind = obj.kind;
|
|
692
|
-
const hasTool = obj.tool !== undefined;
|
|
693
|
-
const hasResource = obj.resource !== undefined;
|
|
694
|
-
// tool.* kinds MUST have tool field
|
|
695
|
-
if (requiresTool(kind) && !hasTool) {
|
|
696
|
-
errors.push({
|
|
697
|
-
code: 'E_INTERACTION_MISSING_TARGET',
|
|
698
|
-
message: `kind "${kind}" requires tool field`,
|
|
699
|
-
field: 'tool',
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
// http.* and fs.* kinds MUST have resource field with meaningful content
|
|
703
|
-
if (requiresResource(kind)) {
|
|
704
|
-
if (!hasResource) {
|
|
705
|
-
errors.push({
|
|
706
|
-
code: 'E_INTERACTION_MISSING_TARGET',
|
|
707
|
-
message: `kind "${kind}" requires resource field`,
|
|
708
|
-
field: 'resource',
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
else if (!hasMeaningfulResource(obj.resource)) {
|
|
712
|
-
errors.push({
|
|
713
|
-
code: 'E_INTERACTION_MISSING_TARGET',
|
|
714
|
-
message: `kind "${kind}" requires resource.uri to be non-empty`,
|
|
715
|
-
field: 'resource.uri',
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
// Warn if neither target present (non-strict kinds like 'message')
|
|
720
|
-
if (!hasTool && !hasResource && errors.length === 0) {
|
|
721
|
-
warnings.push({
|
|
722
|
-
code: 'W_INTERACTION_MISSING_TARGET',
|
|
723
|
-
message: 'Neither tool nor resource field is present',
|
|
724
|
-
field: 'tool',
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
// Return early if we have errors
|
|
728
|
-
if (errors.length > 0) {
|
|
729
|
-
return { valid: false, errors, warnings };
|
|
730
|
-
}
|
|
731
|
-
// Final Zod validation for strict schema compliance
|
|
732
|
-
const zodResult = exports.InteractionEvidenceV01Schema.safeParse(input);
|
|
733
|
-
if (!zodResult.success) {
|
|
734
|
-
return {
|
|
735
|
-
valid: false,
|
|
736
|
-
errors: [
|
|
737
|
-
{
|
|
738
|
-
code: 'E_INTERACTION_INVALID_FORMAT',
|
|
739
|
-
message: zodResult.error.issues[0]?.message || 'Schema validation failed',
|
|
740
|
-
field: zodResult.error.issues[0]?.path.join('.'),
|
|
741
|
-
},
|
|
742
|
-
],
|
|
743
|
-
warnings,
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
return { valid: true, value: zodResult.data, warnings };
|
|
747
|
-
}
|
|
748
|
-
// ============================================================================
|
|
749
|
-
// Helper Functions
|
|
750
|
-
// ============================================================================
|
|
751
|
-
/**
|
|
752
|
-
* Validate interaction evidence (simple API for conformance harness)
|
|
753
|
-
*
|
|
754
|
-
* This is the recommended entry point for conformance testing.
|
|
755
|
-
* Returns a simple result matching the existing harness contract.
|
|
756
|
-
*
|
|
757
|
-
* ## Warnings Contract
|
|
758
|
-
*
|
|
759
|
-
* This compat API intentionally **omits warnings** from the result.
|
|
760
|
-
* Warnings are available via validateInteractionOrdered() for consumers
|
|
761
|
-
* who explicitly opt in. Stable consumers should:
|
|
762
|
-
* - Use this function for pass/fail validation
|
|
763
|
-
* - Use validateInteractionOrdered() only when warnings are needed
|
|
764
|
-
*
|
|
765
|
-
* This prevents breaking changes if warning codes are added/modified.
|
|
766
|
-
*
|
|
767
|
-
* @param input - Raw input to validate
|
|
768
|
-
* @returns Simple validation result with error_code on failure (no warnings)
|
|
769
|
-
*/
|
|
770
|
-
function validateInteraction(input) {
|
|
771
|
-
const result = validateInteractionOrdered(input);
|
|
772
|
-
if (result.valid) {
|
|
773
|
-
return { valid: true };
|
|
774
|
-
}
|
|
775
|
-
const firstError = result.errors[0];
|
|
776
|
-
return {
|
|
777
|
-
valid: false,
|
|
778
|
-
error_code: firstError?.code,
|
|
779
|
-
error_field: firstError?.field,
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* Validate interaction evidence (throwing)
|
|
784
|
-
*
|
|
785
|
-
* @param evidence - Object to validate
|
|
786
|
-
* @returns Validated InteractionEvidenceV01
|
|
787
|
-
* @throws ZodError if validation fails
|
|
788
|
-
*/
|
|
789
|
-
function validateInteractionEvidence(evidence) {
|
|
790
|
-
return exports.InteractionEvidenceV01Schema.parse(evidence);
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* Check if an object is valid interaction evidence (non-throwing)
|
|
794
|
-
*
|
|
795
|
-
* @param evidence - Object to check
|
|
796
|
-
* @returns True if valid InteractionEvidenceV01
|
|
797
|
-
*/
|
|
798
|
-
function isValidInteractionEvidence(evidence) {
|
|
799
|
-
return exports.InteractionEvidenceV01Schema.safeParse(evidence).success;
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* Check if a kind is in the well-known registry
|
|
803
|
-
*
|
|
804
|
-
* @param kind - Kind string to check
|
|
805
|
-
* @returns True if well-known
|
|
806
|
-
*/
|
|
807
|
-
function isWellKnownKind(kind) {
|
|
808
|
-
return exports.WELL_KNOWN_KINDS.includes(kind);
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Check if a kind uses a reserved prefix
|
|
812
|
-
*
|
|
813
|
-
* @param kind - Kind string to check
|
|
814
|
-
* @returns True if uses reserved prefix
|
|
815
|
-
*/
|
|
816
|
-
function isReservedKindPrefix(kind) {
|
|
817
|
-
return exports.RESERVED_KIND_PREFIXES.some((prefix) => kind.startsWith(prefix));
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* Check if a digest uses truncation
|
|
821
|
-
*
|
|
822
|
-
* @param digest - Digest to check
|
|
823
|
-
* @returns True if truncated
|
|
824
|
-
*/
|
|
825
|
-
function isDigestTruncated(digest) {
|
|
826
|
-
return digest.alg.startsWith('sha-256:trunc-');
|
|
827
|
-
}
|
|
828
|
-
// ============================================================================
|
|
829
|
-
// SDK Accessors
|
|
830
|
-
// ============================================================================
|
|
831
|
-
/**
|
|
832
|
-
* Get interaction evidence from a PEAC envelope
|
|
833
|
-
*
|
|
834
|
-
* @param receipt - PEAC envelope to read from
|
|
835
|
-
* @returns Interaction evidence if present, undefined otherwise
|
|
836
|
-
*/
|
|
837
|
-
function getInteraction(receipt) {
|
|
838
|
-
return receipt.evidence?.extensions?.[exports.INTERACTION_EXTENSION_KEY];
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Set interaction evidence on a PEAC envelope (mutates)
|
|
842
|
-
*
|
|
843
|
-
* @param receipt - PEAC envelope to modify
|
|
844
|
-
* @param interaction - Interaction evidence to set
|
|
845
|
-
*/
|
|
846
|
-
function setInteraction(receipt, interaction) {
|
|
847
|
-
if (!receipt.evidence) {
|
|
848
|
-
receipt.evidence = {};
|
|
849
|
-
}
|
|
850
|
-
if (!receipt.evidence.extensions) {
|
|
851
|
-
receipt.evidence.extensions = {};
|
|
852
|
-
}
|
|
853
|
-
receipt.evidence.extensions[exports.INTERACTION_EXTENSION_KEY] = interaction;
|
|
854
|
-
}
|
|
855
|
-
/**
|
|
856
|
-
* Check if a PEAC envelope has interaction evidence
|
|
857
|
-
*
|
|
858
|
-
* @param receipt - PEAC envelope to check
|
|
859
|
-
* @returns True if interaction evidence is present
|
|
860
|
-
*/
|
|
861
|
-
function hasInteraction(receipt) {
|
|
862
|
-
return receipt.evidence?.extensions?.[exports.INTERACTION_EXTENSION_KEY] !== undefined;
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Create a view over a PEAC envelope that provides first-class access
|
|
866
|
-
* to extension data without modifying the wire format.
|
|
867
|
-
*
|
|
868
|
-
* @param envelope - PEAC envelope to create view for
|
|
869
|
-
* @returns ReceiptView with convenient accessors
|
|
870
|
-
*
|
|
871
|
-
* @example
|
|
872
|
-
* const view = createReceiptView(envelope);
|
|
873
|
-
* if (view.interaction) {
|
|
874
|
-
* console.log(view.interaction.kind);
|
|
875
|
-
* }
|
|
876
|
-
*/
|
|
877
|
-
function createReceiptView(envelope) {
|
|
878
|
-
const interaction = getInteraction(envelope);
|
|
879
|
-
const workflow = envelope.auth?.extensions?.['org.peacprotocol/workflow'];
|
|
880
|
-
return {
|
|
881
|
-
envelope,
|
|
882
|
-
interaction,
|
|
883
|
-
interactions: interaction ? [interaction] : [],
|
|
884
|
-
workflow,
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* Create validated interaction evidence
|
|
889
|
-
*
|
|
890
|
-
* @param params - Interaction parameters
|
|
891
|
-
* @returns Validated InteractionEvidenceV01
|
|
892
|
-
* @throws ZodError if validation fails
|
|
893
|
-
*/
|
|
894
|
-
function createInteractionEvidence(params) {
|
|
895
|
-
const evidence = {
|
|
896
|
-
interaction_id: params.interaction_id,
|
|
897
|
-
kind: params.kind,
|
|
898
|
-
executor: {
|
|
899
|
-
platform: params.executor.platform,
|
|
900
|
-
...(params.executor.version && { version: params.executor.version }),
|
|
901
|
-
...(params.executor.plugin_id && { plugin_id: params.executor.plugin_id }),
|
|
902
|
-
...(params.executor.plugin_digest && { plugin_digest: params.executor.plugin_digest }),
|
|
903
|
-
},
|
|
904
|
-
...(params.tool && { tool: params.tool }),
|
|
905
|
-
...(params.resource && { resource: params.resource }),
|
|
906
|
-
...(params.input && { input: params.input }),
|
|
907
|
-
...(params.output && { output: params.output }),
|
|
908
|
-
started_at: params.started_at,
|
|
909
|
-
...(params.completed_at && { completed_at: params.completed_at }),
|
|
910
|
-
...(params.duration_ms !== undefined && { duration_ms: params.duration_ms }),
|
|
911
|
-
...(params.result && { result: params.result }),
|
|
912
|
-
...(params.policy && { policy: params.policy }),
|
|
913
|
-
...(params.refs && { refs: params.refs }),
|
|
914
|
-
...(params.extensions && { extensions: params.extensions }),
|
|
915
|
-
};
|
|
916
|
-
return validateInteractionEvidence(evidence);
|
|
917
|
-
}
|
|
918
|
-
//# sourceMappingURL=interaction.js.map
|