@agentplat/runtime 0.3.0-beta.2 → 0.3.0-beta.3
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/README.md +63 -0
- package/dist/adapter-bridge.d.ts +14 -0
- package/dist/adapter-bridge.d.ts.map +1 -0
- package/dist/adapter-bridge.js +267 -0
- package/dist/adapter-bridge.js.map +1 -0
- package/dist/adapter-contracts.d.ts +351 -0
- package/dist/adapter-contracts.d.ts.map +1 -0
- package/dist/adapter-contracts.js +2 -0
- package/dist/adapter-contracts.js.map +1 -0
- package/dist/adapter-errors.d.ts +6 -0
- package/dist/adapter-errors.d.ts.map +1 -0
- package/dist/adapter-errors.js +9 -0
- package/dist/adapter-errors.js.map +1 -0
- package/dist/adapter-registry.d.ts +25 -0
- package/dist/adapter-registry.d.ts.map +1 -0
- package/dist/adapter-registry.js +116 -0
- package/dist/adapter-registry.js.map +1 -0
- package/dist/adapter-runtime.d.ts +41 -0
- package/dist/adapter-runtime.d.ts.map +1 -0
- package/dist/adapter-runtime.js +591 -0
- package/dist/adapter-runtime.js.map +1 -0
- package/dist/adapter-store.d.ts +9 -0
- package/dist/adapter-store.d.ts.map +1 -0
- package/dist/adapter-store.js +26 -0
- package/dist/adapter-store.js.map +1 -0
- package/dist/adapter-validation.d.ts +39 -0
- package/dist/adapter-validation.d.ts.map +1 -0
- package/dist/adapter-validation.js +853 -0
- package/dist/adapter-validation.js.map +1 -0
- package/dist/adapter.d.ts +9 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +9 -0
- package/dist/adapter.js.map +1 -0
- package/dist/cognitive-adapter.d.ts +259 -0
- package/dist/cognitive-adapter.d.ts.map +1 -0
- package/dist/cognitive-adapter.js +813 -0
- package/dist/cognitive-adapter.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +13 -5
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
import { PortableAgentErrorV1 } from "./adapter-errors.js";
|
|
2
|
+
const AGENT_KINDS = new Set([
|
|
3
|
+
"language_model",
|
|
4
|
+
"vision_language_model",
|
|
5
|
+
"vision_language_action",
|
|
6
|
+
"policy",
|
|
7
|
+
"symbolic",
|
|
8
|
+
"hybrid",
|
|
9
|
+
"custom",
|
|
10
|
+
]);
|
|
11
|
+
const MODALITIES = new Set([
|
|
12
|
+
"text",
|
|
13
|
+
"image",
|
|
14
|
+
"audio",
|
|
15
|
+
"video",
|
|
16
|
+
"structured",
|
|
17
|
+
"sensor",
|
|
18
|
+
"action",
|
|
19
|
+
]);
|
|
20
|
+
const INTERACTION_MODES = new Set([
|
|
21
|
+
"invoke",
|
|
22
|
+
"stream",
|
|
23
|
+
"observe_act",
|
|
24
|
+
]);
|
|
25
|
+
const CONTROL_POINTS = new Set([
|
|
26
|
+
"pre_step",
|
|
27
|
+
"post_output",
|
|
28
|
+
"pre_action",
|
|
29
|
+
]);
|
|
30
|
+
const SOURCE_ZONES = new Set([
|
|
31
|
+
"operator_trusted",
|
|
32
|
+
"objective_trusted",
|
|
33
|
+
"local_trusted",
|
|
34
|
+
"environment_untrusted",
|
|
35
|
+
"peer_untrusted",
|
|
36
|
+
"tool_untrusted",
|
|
37
|
+
"provider_untrusted",
|
|
38
|
+
]);
|
|
39
|
+
const SESSION_STATUSES = new Set(["active", "paused", "closed", "failed"]);
|
|
40
|
+
const STEP_STATUSES = new Set(["completed", "refused", "paused", "failed"]);
|
|
41
|
+
export function normalizeAdapterManifestV1(input) {
|
|
42
|
+
exactKeys(input, [
|
|
43
|
+
"schemaVersion",
|
|
44
|
+
"adapterId",
|
|
45
|
+
"adapterVersion",
|
|
46
|
+
"implementationId",
|
|
47
|
+
"agentKinds",
|
|
48
|
+
"inputModalities",
|
|
49
|
+
"outputModalities",
|
|
50
|
+
"interactionModes",
|
|
51
|
+
"controlPoints",
|
|
52
|
+
"supportsCancellation",
|
|
53
|
+
"supportsCheckpoint",
|
|
54
|
+
"supportsRestore",
|
|
55
|
+
"maximumObservationBytes",
|
|
56
|
+
"maximumOutputBytes",
|
|
57
|
+
"maximumActionBytes",
|
|
58
|
+
"maximumStepsPerSession",
|
|
59
|
+
], "adapter manifest");
|
|
60
|
+
if (input.schemaVersion !== 1)
|
|
61
|
+
invalid("adapter schemaVersion is invalid");
|
|
62
|
+
const supportsCheckpoint = boolean(input.supportsCheckpoint, "supportsCheckpoint");
|
|
63
|
+
const supportsRestore = boolean(input.supportsRestore, "supportsRestore");
|
|
64
|
+
if (supportsRestore && !supportsCheckpoint) {
|
|
65
|
+
invalid("restore support requires checkpoint support");
|
|
66
|
+
}
|
|
67
|
+
return cloneAndFreeze({
|
|
68
|
+
schemaVersion: 1,
|
|
69
|
+
adapterId: identifier(input.adapterId, "adapterId"),
|
|
70
|
+
adapterVersion: token(input.adapterVersion, "adapterVersion", 128),
|
|
71
|
+
implementationId: identifier(input.implementationId, "implementationId"),
|
|
72
|
+
agentKinds: enumArray(input.agentKinds, AGENT_KINDS, "agentKinds", 16, true),
|
|
73
|
+
inputModalities: enumArray(input.inputModalities, MODALITIES, "inputModalities", 16, true),
|
|
74
|
+
outputModalities: enumArray(input.outputModalities, MODALITIES, "outputModalities", 16, true),
|
|
75
|
+
interactionModes: enumArray(input.interactionModes, INTERACTION_MODES, "interactionModes", 8, true),
|
|
76
|
+
controlPoints: enumArray(input.controlPoints, CONTROL_POINTS, "controlPoints", 8, false),
|
|
77
|
+
supportsCancellation: boolean(input.supportsCancellation, "supportsCancellation"),
|
|
78
|
+
supportsCheckpoint,
|
|
79
|
+
supportsRestore,
|
|
80
|
+
maximumObservationBytes: positiveInteger(input.maximumObservationBytes, "maximumObservationBytes", 67_108_864),
|
|
81
|
+
maximumOutputBytes: positiveInteger(input.maximumOutputBytes, "maximumOutputBytes", 67_108_864),
|
|
82
|
+
maximumActionBytes: positiveInteger(input.maximumActionBytes, "maximumActionBytes", 67_108_864),
|
|
83
|
+
maximumStepsPerSession: positiveInteger(input.maximumStepsPerSession, "maximumStepsPerSession", 1_000_000),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export function normalizeAdapterRequirementsV1(input) {
|
|
87
|
+
if (!isPlainRecord(input))
|
|
88
|
+
invalid("adapter requirements are required");
|
|
89
|
+
const keys = Object.keys(input).sort(compareAscii);
|
|
90
|
+
const allowed = [
|
|
91
|
+
"agentKinds",
|
|
92
|
+
"controlPoints",
|
|
93
|
+
"inputModalities",
|
|
94
|
+
"interactionMode",
|
|
95
|
+
"outputModalities",
|
|
96
|
+
"requireCancellation",
|
|
97
|
+
"requireCheckpoint",
|
|
98
|
+
"requireRestore",
|
|
99
|
+
].sort(compareAscii);
|
|
100
|
+
if (keys.some((key) => !allowed.includes(key))) {
|
|
101
|
+
invalid("adapter requirements contain unknown fields");
|
|
102
|
+
}
|
|
103
|
+
if (!INTERACTION_MODES.has(input.interactionMode)) {
|
|
104
|
+
invalid("requirements.interactionMode is invalid");
|
|
105
|
+
}
|
|
106
|
+
return cloneAndFreeze({
|
|
107
|
+
...(input.agentKinds === undefined
|
|
108
|
+
? {}
|
|
109
|
+
: {
|
|
110
|
+
agentKinds: enumArray(input.agentKinds, AGENT_KINDS, "requirements.agentKinds", 16, true),
|
|
111
|
+
}),
|
|
112
|
+
inputModalities: enumArray(input.inputModalities, MODALITIES, "requirements.inputModalities", 16, false),
|
|
113
|
+
outputModalities: enumArray(input.outputModalities, MODALITIES, "requirements.outputModalities", 16, false),
|
|
114
|
+
interactionMode: input.interactionMode,
|
|
115
|
+
controlPoints: enumArray(input.controlPoints, CONTROL_POINTS, "requirements.controlPoints", 8, false),
|
|
116
|
+
...(input.requireCancellation === undefined
|
|
117
|
+
? {}
|
|
118
|
+
: {
|
|
119
|
+
requireCancellation: boolean(input.requireCancellation, "requirements.requireCancellation"),
|
|
120
|
+
}),
|
|
121
|
+
...(input.requireCheckpoint === undefined
|
|
122
|
+
? {}
|
|
123
|
+
: {
|
|
124
|
+
requireCheckpoint: boolean(input.requireCheckpoint, "requirements.requireCheckpoint"),
|
|
125
|
+
}),
|
|
126
|
+
...(input.requireRestore === undefined
|
|
127
|
+
? {}
|
|
128
|
+
: {
|
|
129
|
+
requireRestore: boolean(input.requireRestore, "requirements.requireRestore"),
|
|
130
|
+
}),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function normalizeRoleBindingV1(input) {
|
|
134
|
+
exactKeys(input, [
|
|
135
|
+
"schemaVersion",
|
|
136
|
+
"roleBindingId",
|
|
137
|
+
"roleRevision",
|
|
138
|
+
"predecessorRoleBindingId",
|
|
139
|
+
"objectiveId",
|
|
140
|
+
"roleKey",
|
|
141
|
+
"instructions",
|
|
142
|
+
"constraints",
|
|
143
|
+
"validFromLogicalMs",
|
|
144
|
+
"validUntilLogicalMs",
|
|
145
|
+
], "role binding");
|
|
146
|
+
if (input.schemaVersion !== 1)
|
|
147
|
+
invalid("role schemaVersion is invalid");
|
|
148
|
+
const roleRevision = positiveInteger(input.roleRevision, "roleRevision", Number.MAX_SAFE_INTEGER);
|
|
149
|
+
const predecessorRoleBindingId = nullableIdentifier(input.predecessorRoleBindingId, "predecessorRoleBindingId");
|
|
150
|
+
if ((roleRevision === 1 && predecessorRoleBindingId !== null) ||
|
|
151
|
+
(roleRevision > 1 && predecessorRoleBindingId === null)) {
|
|
152
|
+
invalid("role predecessor is inconsistent with its revision");
|
|
153
|
+
}
|
|
154
|
+
const validFromLogicalMs = safeInteger(input.validFromLogicalMs, "validFromLogicalMs", 0);
|
|
155
|
+
const validUntilLogicalMs = safeInteger(input.validUntilLogicalMs, "validUntilLogicalMs", 1);
|
|
156
|
+
if (validUntilLogicalMs <= validFromLogicalMs) {
|
|
157
|
+
invalid("role validity interval is invalid");
|
|
158
|
+
}
|
|
159
|
+
if (!Array.isArray(input.instructions) || input.instructions.length > 128) {
|
|
160
|
+
invalid("role instructions must be a bounded array");
|
|
161
|
+
}
|
|
162
|
+
const instructions = Object.freeze(input.instructions.map((value, index) => text(value, `instructions[${index}]`, 8_192)));
|
|
163
|
+
return cloneAndFreeze({
|
|
164
|
+
schemaVersion: 1,
|
|
165
|
+
roleBindingId: identifier(input.roleBindingId, "roleBindingId"),
|
|
166
|
+
roleRevision,
|
|
167
|
+
predecessorRoleBindingId,
|
|
168
|
+
objectiveId: identifier(input.objectiveId, "objectiveId"),
|
|
169
|
+
roleKey: token(input.roleKey, "roleKey", 256),
|
|
170
|
+
instructions,
|
|
171
|
+
constraints: normalizeJsonObject(input.constraints, "role.constraints"),
|
|
172
|
+
validFromLogicalMs,
|
|
173
|
+
validUntilLogicalMs,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
export function normalizeObservationV1(input) {
|
|
177
|
+
exactKeys(input, [
|
|
178
|
+
"schemaVersion",
|
|
179
|
+
"observationId",
|
|
180
|
+
"sourceZone",
|
|
181
|
+
"sourceId",
|
|
182
|
+
"modality",
|
|
183
|
+
"content",
|
|
184
|
+
"contentReference",
|
|
185
|
+
"provenance",
|
|
186
|
+
"observedAtLogicalMs",
|
|
187
|
+
], "observation");
|
|
188
|
+
if (input.schemaVersion !== 1)
|
|
189
|
+
invalid("observation schemaVersion is invalid");
|
|
190
|
+
if (!SOURCE_ZONES.has(input.sourceZone))
|
|
191
|
+
invalid("sourceZone is invalid");
|
|
192
|
+
if (!MODALITIES.has(input.modality))
|
|
193
|
+
invalid("observation modality is invalid");
|
|
194
|
+
const content = input.content === null
|
|
195
|
+
? null
|
|
196
|
+
: normalizeJson(input.content, "observation.content");
|
|
197
|
+
const contentReference = input.contentReference === null
|
|
198
|
+
? null
|
|
199
|
+
: normalizeContentReference(input.contentReference, "contentReference");
|
|
200
|
+
if ((content === null) === (contentReference === null)) {
|
|
201
|
+
invalid("observation requires exactly one content representation");
|
|
202
|
+
}
|
|
203
|
+
return cloneAndFreeze({
|
|
204
|
+
schemaVersion: 1,
|
|
205
|
+
observationId: identifier(input.observationId, "observationId"),
|
|
206
|
+
sourceZone: input.sourceZone,
|
|
207
|
+
sourceId: identifier(input.sourceId, "sourceId"),
|
|
208
|
+
modality: input.modality,
|
|
209
|
+
content,
|
|
210
|
+
contentReference,
|
|
211
|
+
provenance: normalizeMetadata(input.provenance, "observation.provenance"),
|
|
212
|
+
observedAtLogicalMs: safeInteger(input.observedAtLogicalMs, "observedAtLogicalMs", 0),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
export function normalizeStepRequestV1(input, manifest) {
|
|
216
|
+
exactKeys(input, [
|
|
217
|
+
"schemaVersion",
|
|
218
|
+
"stepId",
|
|
219
|
+
"expectedSessionRevision",
|
|
220
|
+
"interactionMode",
|
|
221
|
+
"observations",
|
|
222
|
+
"input",
|
|
223
|
+
"requestedOutputModalities",
|
|
224
|
+
"logicalTimeMs",
|
|
225
|
+
], "step request");
|
|
226
|
+
if (input.schemaVersion !== 1)
|
|
227
|
+
invalid("step schemaVersion is invalid");
|
|
228
|
+
if (!INTERACTION_MODES.has(input.interactionMode)) {
|
|
229
|
+
invalid("step interactionMode is invalid");
|
|
230
|
+
}
|
|
231
|
+
if (!Array.isArray(input.observations) || input.observations.length > 4_096) {
|
|
232
|
+
invalid("step observations must be a bounded array");
|
|
233
|
+
}
|
|
234
|
+
const observations = input.observations.map(normalizeObservationV1);
|
|
235
|
+
const observationIds = observations.map(({ observationId }) => observationId);
|
|
236
|
+
if (new Set(observationIds).size !== observationIds.length) {
|
|
237
|
+
invalid("step observations contain duplicate IDs");
|
|
238
|
+
}
|
|
239
|
+
const requestedOutputModalities = enumArray(input.requestedOutputModalities, MODALITIES, "requestedOutputModalities", 16, true);
|
|
240
|
+
const stepInput = input.input === null
|
|
241
|
+
? null
|
|
242
|
+
: normalizeJsonObject(input.input, "step.input");
|
|
243
|
+
if (manifest) {
|
|
244
|
+
if (!manifest.interactionModes.includes(input.interactionMode)) {
|
|
245
|
+
invalid("adapter does not support the requested interaction mode");
|
|
246
|
+
}
|
|
247
|
+
for (const observation of observations) {
|
|
248
|
+
if (!manifest.inputModalities.includes(observation.modality)) {
|
|
249
|
+
invalid(`adapter does not accept ${observation.modality} observations`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
for (const modality of requestedOutputModalities) {
|
|
253
|
+
if (!manifest.outputModalities.includes(modality)) {
|
|
254
|
+
invalid(`adapter does not produce ${modality} output`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (jsonByteLength(normalizeJson({ observations, input: stepInput }, "step observation envelope")) +
|
|
258
|
+
observations.reduce((total, observation) => total + (observation.contentReference?.byteLength ?? 0), 0) >
|
|
259
|
+
manifest.maximumObservationBytes) {
|
|
260
|
+
invalid("step observations exceed the adapter byte limit");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return cloneAndFreeze({
|
|
264
|
+
schemaVersion: 1,
|
|
265
|
+
stepId: identifier(input.stepId, "stepId"),
|
|
266
|
+
expectedSessionRevision: safeInteger(input.expectedSessionRevision, "expectedSessionRevision", 0),
|
|
267
|
+
interactionMode: input.interactionMode,
|
|
268
|
+
observations,
|
|
269
|
+
input: stepInput,
|
|
270
|
+
requestedOutputModalities,
|
|
271
|
+
logicalTimeMs: safeInteger(input.logicalTimeMs, "logicalTimeMs", 0),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
export function normalizeStepResultV1(input, binding) {
|
|
275
|
+
exactKeys(input, [
|
|
276
|
+
"schemaVersion",
|
|
277
|
+
"sessionId",
|
|
278
|
+
"stepId",
|
|
279
|
+
"stepSequence",
|
|
280
|
+
"status",
|
|
281
|
+
"outputs",
|
|
282
|
+
"actionProposals",
|
|
283
|
+
"checkpoint",
|
|
284
|
+
"reasonCode",
|
|
285
|
+
"metadata",
|
|
286
|
+
], "step result");
|
|
287
|
+
if (input.schemaVersion !== 1 ||
|
|
288
|
+
input.sessionId !== binding.sessionId ||
|
|
289
|
+
input.stepId !== binding.stepId ||
|
|
290
|
+
input.stepSequence !== binding.stepSequence ||
|
|
291
|
+
!STEP_STATUSES.has(input.status)) {
|
|
292
|
+
invalid("step result binding is invalid");
|
|
293
|
+
}
|
|
294
|
+
if (!Array.isArray(input.outputs) || input.outputs.length > 4_096) {
|
|
295
|
+
invalid("step outputs must be a bounded array");
|
|
296
|
+
}
|
|
297
|
+
if (!Array.isArray(input.actionProposals) ||
|
|
298
|
+
input.actionProposals.length > 4_096) {
|
|
299
|
+
invalid("step action proposals must be a bounded array");
|
|
300
|
+
}
|
|
301
|
+
const outputs = input.outputs.map((value) => normalizeOutputV1(value, binding.manifest));
|
|
302
|
+
const actionProposals = input.actionProposals.map((value) => normalizeActionProposalV1(value, binding.manifest));
|
|
303
|
+
uniqueIds(outputs.map(({ outputId }) => outputId), "output");
|
|
304
|
+
uniqueIds(actionProposals.map(({ actionId }) => actionId), "action");
|
|
305
|
+
if (jsonByteLength(outputs) +
|
|
306
|
+
outputs.reduce((total, output) => total + (output.contentReference?.byteLength ?? 0), 0) >
|
|
307
|
+
binding.manifest.maximumOutputBytes) {
|
|
308
|
+
invalid("agent outputs exceed the adapter byte limit");
|
|
309
|
+
}
|
|
310
|
+
if (jsonByteLength(actionProposals) >
|
|
311
|
+
binding.manifest.maximumActionBytes) {
|
|
312
|
+
invalid("agent actions exceed the adapter byte limit");
|
|
313
|
+
}
|
|
314
|
+
const checkpoint = input.checkpoint === null
|
|
315
|
+
? null
|
|
316
|
+
: normalizeCheckpointV1(input.checkpoint, {
|
|
317
|
+
sessionId: binding.sessionId,
|
|
318
|
+
manifest: binding.manifest,
|
|
319
|
+
maximumSequence: binding.stepSequence,
|
|
320
|
+
});
|
|
321
|
+
if (checkpoint !== null && !binding.manifest.supportsCheckpoint) {
|
|
322
|
+
invalid("adapter returned an undeclared checkpoint");
|
|
323
|
+
}
|
|
324
|
+
if (input.status === "completed" &&
|
|
325
|
+
outputs.length === 0 &&
|
|
326
|
+
actionProposals.length === 0) {
|
|
327
|
+
invalid("completed step result is empty");
|
|
328
|
+
}
|
|
329
|
+
if (input.status !== "completed" &&
|
|
330
|
+
(input.reasonCode === null || input.reasonCode.length === 0)) {
|
|
331
|
+
invalid("non-completed step result requires a reasonCode");
|
|
332
|
+
}
|
|
333
|
+
return cloneAndFreeze({
|
|
334
|
+
schemaVersion: 1,
|
|
335
|
+
sessionId: binding.sessionId,
|
|
336
|
+
stepId: binding.stepId,
|
|
337
|
+
stepSequence: binding.stepSequence,
|
|
338
|
+
status: input.status,
|
|
339
|
+
outputs,
|
|
340
|
+
actionProposals,
|
|
341
|
+
checkpoint,
|
|
342
|
+
reasonCode: input.reasonCode === null
|
|
343
|
+
? null
|
|
344
|
+
: token(input.reasonCode, "reasonCode", 256),
|
|
345
|
+
metadata: normalizeMetadata(input.metadata, "step.metadata"),
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
export function normalizeCheckpointV1(input, binding) {
|
|
349
|
+
exactKeys(input, [
|
|
350
|
+
"schemaVersion",
|
|
351
|
+
"checkpointId",
|
|
352
|
+
"sessionId",
|
|
353
|
+
"adapterId",
|
|
354
|
+
"adapterVersion",
|
|
355
|
+
"implementationId",
|
|
356
|
+
"throughStepSequence",
|
|
357
|
+
"stateReference",
|
|
358
|
+
"stateDigest",
|
|
359
|
+
"createdAt",
|
|
360
|
+
], "checkpoint");
|
|
361
|
+
if (input.schemaVersion !== 1 ||
|
|
362
|
+
input.sessionId !== binding.sessionId ||
|
|
363
|
+
input.adapterId !== binding.manifest.adapterId ||
|
|
364
|
+
input.adapterVersion !== binding.manifest.adapterVersion ||
|
|
365
|
+
input.implementationId !== binding.manifest.implementationId) {
|
|
366
|
+
invalid("checkpoint binding is invalid");
|
|
367
|
+
}
|
|
368
|
+
const sequence = safeInteger(input.throughStepSequence, "throughStepSequence", 0);
|
|
369
|
+
if (sequence > binding.maximumSequence) {
|
|
370
|
+
invalid("checkpoint sequence is ahead of session state");
|
|
371
|
+
}
|
|
372
|
+
return cloneAndFreeze({
|
|
373
|
+
schemaVersion: 1,
|
|
374
|
+
checkpointId: identifier(input.checkpointId, "checkpointId"),
|
|
375
|
+
sessionId: binding.sessionId,
|
|
376
|
+
adapterId: binding.manifest.adapterId,
|
|
377
|
+
adapterVersion: binding.manifest.adapterVersion,
|
|
378
|
+
implementationId: binding.manifest.implementationId,
|
|
379
|
+
throughStepSequence: sequence,
|
|
380
|
+
stateReference: text(input.stateReference, "stateReference", 4_096),
|
|
381
|
+
stateDigest: token(input.stateDigest, "stateDigest", 256),
|
|
382
|
+
createdAt: timestamp(input.createdAt, "checkpoint.createdAt"),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
export function normalizeCheckpointTransferV1(input, options = {}) {
|
|
386
|
+
exactKeys(input, [
|
|
387
|
+
"schemaVersion",
|
|
388
|
+
"contentClass",
|
|
389
|
+
"tenantId",
|
|
390
|
+
"objectiveId",
|
|
391
|
+
"sourceSessionId",
|
|
392
|
+
"sourceAgentId",
|
|
393
|
+
"sourceSessionRevision",
|
|
394
|
+
"roleBindingId",
|
|
395
|
+
"adapterId",
|
|
396
|
+
"adapterVersion",
|
|
397
|
+
"implementationId",
|
|
398
|
+
"checkpoint",
|
|
399
|
+
"state",
|
|
400
|
+
"exportedAt",
|
|
401
|
+
], "checkpoint transfer");
|
|
402
|
+
if (input.schemaVersion !== 1 ||
|
|
403
|
+
input.contentClass !== "portable_application_state") {
|
|
404
|
+
invalid("checkpoint transfer header is invalid");
|
|
405
|
+
}
|
|
406
|
+
const adapterId = identifier(input.adapterId, "transfer.adapterId");
|
|
407
|
+
const adapterVersion = token(input.adapterVersion, "transfer.adapterVersion", 128);
|
|
408
|
+
const implementationId = identifier(input.implementationId, "transfer.implementationId");
|
|
409
|
+
const sourceSessionId = identifier(input.sourceSessionId, "transfer.sourceSessionId");
|
|
410
|
+
const checkpoint = normalizeCheckpointV1(input.checkpoint, {
|
|
411
|
+
sessionId: sourceSessionId,
|
|
412
|
+
manifest: {
|
|
413
|
+
schemaVersion: 1,
|
|
414
|
+
adapterId,
|
|
415
|
+
adapterVersion,
|
|
416
|
+
implementationId,
|
|
417
|
+
agentKinds: ["custom"],
|
|
418
|
+
inputModalities: ["structured"],
|
|
419
|
+
outputModalities: ["structured"],
|
|
420
|
+
interactionModes: ["invoke"],
|
|
421
|
+
controlPoints: ["pre_step"],
|
|
422
|
+
supportsCancellation: true,
|
|
423
|
+
supportsCheckpoint: true,
|
|
424
|
+
supportsRestore: true,
|
|
425
|
+
maximumObservationBytes: 1,
|
|
426
|
+
maximumOutputBytes: 1,
|
|
427
|
+
maximumActionBytes: 1,
|
|
428
|
+
maximumStepsPerSession: Number.MAX_SAFE_INTEGER,
|
|
429
|
+
},
|
|
430
|
+
maximumSequence: Number.MAX_SAFE_INTEGER,
|
|
431
|
+
});
|
|
432
|
+
const state = normalizeJson(input.state, "checkpoint transfer state");
|
|
433
|
+
const maximum = options.maximumStateBytes ?? 16 * 1_024 * 1_024;
|
|
434
|
+
if (!Number.isSafeInteger(maximum) ||
|
|
435
|
+
maximum < 1_024 ||
|
|
436
|
+
maximum > 64 * 1_024 * 1_024 ||
|
|
437
|
+
jsonByteLength(state) > maximum) {
|
|
438
|
+
invalid("checkpoint transfer state exceeds its byte limit");
|
|
439
|
+
}
|
|
440
|
+
return cloneAndFreeze({
|
|
441
|
+
schemaVersion: 1,
|
|
442
|
+
contentClass: "portable_application_state",
|
|
443
|
+
tenantId: identifier(input.tenantId, "transfer.tenantId"),
|
|
444
|
+
objectiveId: identifier(input.objectiveId, "transfer.objectiveId"),
|
|
445
|
+
sourceSessionId,
|
|
446
|
+
sourceAgentId: identifier(input.sourceAgentId, "transfer.sourceAgentId"),
|
|
447
|
+
sourceSessionRevision: safeInteger(input.sourceSessionRevision, "transfer.sourceSessionRevision", 0),
|
|
448
|
+
roleBindingId: identifier(input.roleBindingId, "transfer.roleBindingId"),
|
|
449
|
+
adapterId,
|
|
450
|
+
adapterVersion,
|
|
451
|
+
implementationId,
|
|
452
|
+
checkpoint,
|
|
453
|
+
state,
|
|
454
|
+
exportedAt: timestamp(input.exportedAt, "transfer.exportedAt"),
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
export function normalizeControlDecisionV1(input) {
|
|
458
|
+
exactKeys(input, ["disposition", "reasonCode"], "control decision");
|
|
459
|
+
if (!["allow", "deny", "abstain", "escalate"].includes(input.disposition)) {
|
|
460
|
+
invalid("control disposition is invalid");
|
|
461
|
+
}
|
|
462
|
+
return Object.freeze({
|
|
463
|
+
disposition: input.disposition,
|
|
464
|
+
reasonCode: token(input.reasonCode, "control.reasonCode", 256),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
export function assertStoredPortableSessionV1(input, binding) {
|
|
468
|
+
try {
|
|
469
|
+
exactKeys(input, [
|
|
470
|
+
"schemaVersion",
|
|
471
|
+
"sessionId",
|
|
472
|
+
"tenantId",
|
|
473
|
+
"agentId",
|
|
474
|
+
"objectiveId",
|
|
475
|
+
"manifest",
|
|
476
|
+
"controlBinding",
|
|
477
|
+
"role",
|
|
478
|
+
"status",
|
|
479
|
+
"revision",
|
|
480
|
+
"nextStepSequence",
|
|
481
|
+
"stepRecords",
|
|
482
|
+
"checkpoint",
|
|
483
|
+
"metadata",
|
|
484
|
+
"createdAt",
|
|
485
|
+
"updatedAt",
|
|
486
|
+
"closedAt",
|
|
487
|
+
], "stored session");
|
|
488
|
+
if (input.schemaVersion !== 1 || !SESSION_STATUSES.has(input.status)) {
|
|
489
|
+
stateInvalid("stored session status is invalid");
|
|
490
|
+
}
|
|
491
|
+
identifier(input.sessionId, "stored.sessionId");
|
|
492
|
+
identifier(input.tenantId, "stored.tenantId");
|
|
493
|
+
identifier(input.agentId, "stored.agentId");
|
|
494
|
+
identifier(input.objectiveId, "stored.objectiveId");
|
|
495
|
+
const manifest = normalizeAdapterManifestV1(input.manifest);
|
|
496
|
+
const role = normalizeRoleBindingV1(input.role);
|
|
497
|
+
if (role.objectiveId !== input.objectiveId) {
|
|
498
|
+
stateInvalid("stored role objective is invalid");
|
|
499
|
+
}
|
|
500
|
+
exactKeys(input.controlBinding, ["controlId", "controlVersion", "implementationId"], "stored control binding");
|
|
501
|
+
const controlBinding = {
|
|
502
|
+
controlId: identifier(input.controlBinding.controlId, "controlId"),
|
|
503
|
+
controlVersion: positiveInteger(input.controlBinding.controlVersion, "controlVersion", Number.MAX_SAFE_INTEGER),
|
|
504
|
+
implementationId: identifier(input.controlBinding.implementationId, "control.implementationId"),
|
|
505
|
+
};
|
|
506
|
+
if (binding?.sessionId !== undefined &&
|
|
507
|
+
binding.sessionId !== input.sessionId) {
|
|
508
|
+
stateInvalid("stored session ID does not match");
|
|
509
|
+
}
|
|
510
|
+
if (binding?.manifest !== undefined &&
|
|
511
|
+
!sameJson(binding.manifest, manifest)) {
|
|
512
|
+
stateInvalid("stored adapter manifest does not match");
|
|
513
|
+
}
|
|
514
|
+
if (binding?.control !== undefined &&
|
|
515
|
+
!sameJson(binding.control, controlBinding)) {
|
|
516
|
+
stateInvalid("stored control deployment does not match");
|
|
517
|
+
}
|
|
518
|
+
const revision = safeInteger(input.revision, "stored.revision", 0);
|
|
519
|
+
const nextStepSequence = positiveInteger(input.nextStepSequence, "stored.nextStepSequence", manifest.maximumStepsPerSession + 1);
|
|
520
|
+
if (!Array.isArray(input.stepRecords) ||
|
|
521
|
+
input.stepRecords.length > manifest.maximumStepsPerSession) {
|
|
522
|
+
stateInvalid("stored step records are invalid");
|
|
523
|
+
}
|
|
524
|
+
const seenSteps = new Set();
|
|
525
|
+
const stepRecords = input.stepRecords.map((record, index) => {
|
|
526
|
+
exactKeys(record, [
|
|
527
|
+
"schemaVersion",
|
|
528
|
+
"stepId",
|
|
529
|
+
"stepSequence",
|
|
530
|
+
"roleBindingId",
|
|
531
|
+
"roleRevision",
|
|
532
|
+
"interactionMode",
|
|
533
|
+
"status",
|
|
534
|
+
"request",
|
|
535
|
+
"result",
|
|
536
|
+
"startedAt",
|
|
537
|
+
"completedAt",
|
|
538
|
+
], "stored step record");
|
|
539
|
+
if (record.schemaVersion !== 1 ||
|
|
540
|
+
record.stepSequence !== index + 1 ||
|
|
541
|
+
seenSteps.has(record.stepId) ||
|
|
542
|
+
!INTERACTION_MODES.has(record.interactionMode) ||
|
|
543
|
+
!STEP_STATUSES.has(record.status)) {
|
|
544
|
+
stateInvalid("stored step sequence is invalid");
|
|
545
|
+
}
|
|
546
|
+
seenSteps.add(identifier(record.stepId, "stored.stepId"));
|
|
547
|
+
identifier(record.roleBindingId, "stored.roleBindingId");
|
|
548
|
+
positiveInteger(record.roleRevision, "stored.roleRevision", Number.MAX_SAFE_INTEGER);
|
|
549
|
+
const request = normalizeStepRequestV1(record.request, manifest);
|
|
550
|
+
if (request.stepId !== record.stepId ||
|
|
551
|
+
request.interactionMode !== record.interactionMode) {
|
|
552
|
+
stateInvalid("stored step request is inconsistent");
|
|
553
|
+
}
|
|
554
|
+
const result = normalizeStepResultV1(record.result, {
|
|
555
|
+
sessionId: input.sessionId,
|
|
556
|
+
stepId: record.stepId,
|
|
557
|
+
stepSequence: record.stepSequence,
|
|
558
|
+
manifest,
|
|
559
|
+
});
|
|
560
|
+
if (result.status !== record.status) {
|
|
561
|
+
stateInvalid("stored step result status is inconsistent");
|
|
562
|
+
}
|
|
563
|
+
return cloneAndFreeze({
|
|
564
|
+
...record,
|
|
565
|
+
request,
|
|
566
|
+
result,
|
|
567
|
+
startedAt: timestamp(record.startedAt, "stored.startedAt"),
|
|
568
|
+
completedAt: timestamp(record.completedAt, "stored.completedAt"),
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
if (nextStepSequence !== stepRecords.length + 1) {
|
|
572
|
+
stateInvalid("stored next step sequence is inconsistent");
|
|
573
|
+
}
|
|
574
|
+
const checkpoint = input.checkpoint === null
|
|
575
|
+
? null
|
|
576
|
+
: normalizeCheckpointV1(input.checkpoint, {
|
|
577
|
+
sessionId: input.sessionId,
|
|
578
|
+
manifest,
|
|
579
|
+
maximumSequence: nextStepSequence - 1,
|
|
580
|
+
});
|
|
581
|
+
const closedAt = input.closedAt === null
|
|
582
|
+
? null
|
|
583
|
+
: timestamp(input.closedAt, "stored.closedAt");
|
|
584
|
+
if ((input.status === "closed" && closedAt === null) ||
|
|
585
|
+
(input.status !== "closed" && closedAt !== null)) {
|
|
586
|
+
stateInvalid("stored closed state is inconsistent");
|
|
587
|
+
}
|
|
588
|
+
return cloneAndFreeze({
|
|
589
|
+
...input,
|
|
590
|
+
manifest,
|
|
591
|
+
controlBinding,
|
|
592
|
+
role,
|
|
593
|
+
revision,
|
|
594
|
+
nextStepSequence,
|
|
595
|
+
stepRecords,
|
|
596
|
+
checkpoint,
|
|
597
|
+
metadata: normalizeMetadata(input.metadata, "stored.metadata"),
|
|
598
|
+
createdAt: timestamp(input.createdAt, "stored.createdAt"),
|
|
599
|
+
updatedAt: timestamp(input.updatedAt, "stored.updatedAt"),
|
|
600
|
+
closedAt,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
if (error instanceof PortableAgentErrorV1 &&
|
|
605
|
+
error.code === "STATE_INVALID") {
|
|
606
|
+
throw error;
|
|
607
|
+
}
|
|
608
|
+
throw new PortableAgentErrorV1("STATE_INVALID", "stored portable agent session is invalid");
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
export function normalizeMetadata(input, label) {
|
|
612
|
+
return normalizeJsonObject(input, label);
|
|
613
|
+
}
|
|
614
|
+
export function normalizeJsonObject(input, label) {
|
|
615
|
+
const normalized = normalizeJson(input, label);
|
|
616
|
+
if (!isPlainRecord(normalized))
|
|
617
|
+
invalid(`${label} must be a JSON object`);
|
|
618
|
+
return normalized;
|
|
619
|
+
}
|
|
620
|
+
export function normalizeJson(input, label) {
|
|
621
|
+
let nodes = 0;
|
|
622
|
+
const visit = (value, path, depth) => {
|
|
623
|
+
nodes += 1;
|
|
624
|
+
if (nodes > 100_000 || depth > 32)
|
|
625
|
+
invalid(`${label} exceeds JSON limits`);
|
|
626
|
+
if (value === null ||
|
|
627
|
+
typeof value === "string" ||
|
|
628
|
+
typeof value === "boolean") {
|
|
629
|
+
return value;
|
|
630
|
+
}
|
|
631
|
+
if (typeof value === "number") {
|
|
632
|
+
if (!Number.isFinite(value))
|
|
633
|
+
invalid(`${path} is not finite`);
|
|
634
|
+
return value;
|
|
635
|
+
}
|
|
636
|
+
if (Array.isArray(value)) {
|
|
637
|
+
if (value.length > 100_000)
|
|
638
|
+
invalid(`${path} is too large`);
|
|
639
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
640
|
+
if (!Object.prototype.hasOwnProperty.call(value, index)) {
|
|
641
|
+
invalid(`${path} cannot contain sparse entries`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return value.map((item, index) => visit(item, `${path}[${index}]`, depth + 1));
|
|
645
|
+
}
|
|
646
|
+
if (!isPlainRecord(value))
|
|
647
|
+
invalid(`${path} must contain plain JSON data`);
|
|
648
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
649
|
+
invalid(`${path} cannot contain symbol keys`);
|
|
650
|
+
}
|
|
651
|
+
const output = Object.create(null);
|
|
652
|
+
for (const key of Object.getOwnPropertyNames(value).sort(compareAscii)) {
|
|
653
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
654
|
+
if (!descriptor || !descriptor.enumerable || !("value" in descriptor)) {
|
|
655
|
+
invalid(`${path}.${key} must be enumerable data`);
|
|
656
|
+
}
|
|
657
|
+
output[key] = visit(descriptor.value, `${path}.${key}`, depth + 1);
|
|
658
|
+
}
|
|
659
|
+
return output;
|
|
660
|
+
};
|
|
661
|
+
return deepFreeze(visit(input, label, 0));
|
|
662
|
+
}
|
|
663
|
+
export function jsonByteLength(input) {
|
|
664
|
+
return new TextEncoder().encode(JSON.stringify(input)).byteLength;
|
|
665
|
+
}
|
|
666
|
+
export function cloneAndFreeze(input) {
|
|
667
|
+
return deepFreeze(structuredClone(input));
|
|
668
|
+
}
|
|
669
|
+
export function identifier(input, label) {
|
|
670
|
+
if (typeof input !== "string" ||
|
|
671
|
+
input.length === 0 ||
|
|
672
|
+
input.length > 256 ||
|
|
673
|
+
input.trim() !== input ||
|
|
674
|
+
/[\u0000-\u001f\u007f]/u.test(input)) {
|
|
675
|
+
invalid(`${label} must be a non-empty bounded identifier`);
|
|
676
|
+
}
|
|
677
|
+
return input;
|
|
678
|
+
}
|
|
679
|
+
export function timestamp(input, label) {
|
|
680
|
+
if (typeof input !== "string" ||
|
|
681
|
+
!Number.isFinite(Date.parse(input)) ||
|
|
682
|
+
new Date(input).toISOString() !== input) {
|
|
683
|
+
invalid(`${label} must be a canonical ISO timestamp`);
|
|
684
|
+
}
|
|
685
|
+
return input;
|
|
686
|
+
}
|
|
687
|
+
function normalizeOutputV1(input, manifest) {
|
|
688
|
+
exactKeys(input, [
|
|
689
|
+
"schemaVersion",
|
|
690
|
+
"outputId",
|
|
691
|
+
"modality",
|
|
692
|
+
"content",
|
|
693
|
+
"contentReference",
|
|
694
|
+
"metadata",
|
|
695
|
+
], "agent output");
|
|
696
|
+
if (input.schemaVersion !== 1 || !MODALITIES.has(input.modality)) {
|
|
697
|
+
invalid("agent output is invalid");
|
|
698
|
+
}
|
|
699
|
+
if (!manifest.outputModalities.includes(input.modality)) {
|
|
700
|
+
invalid("adapter returned an undeclared output modality");
|
|
701
|
+
}
|
|
702
|
+
const content = input.content === null
|
|
703
|
+
? null
|
|
704
|
+
: normalizeJson(input.content, "output.content");
|
|
705
|
+
const contentReference = input.contentReference === null
|
|
706
|
+
? null
|
|
707
|
+
: normalizeContentReference(input.contentReference, "output.contentReference");
|
|
708
|
+
if ((content === null) === (contentReference === null)) {
|
|
709
|
+
invalid("output requires exactly one content representation");
|
|
710
|
+
}
|
|
711
|
+
const output = cloneAndFreeze({
|
|
712
|
+
schemaVersion: 1,
|
|
713
|
+
outputId: identifier(input.outputId, "outputId"),
|
|
714
|
+
modality: input.modality,
|
|
715
|
+
content,
|
|
716
|
+
contentReference,
|
|
717
|
+
metadata: normalizeMetadata(input.metadata, "output.metadata"),
|
|
718
|
+
});
|
|
719
|
+
if (jsonByteLength(output) > manifest.maximumOutputBytes) {
|
|
720
|
+
invalid("agent output exceeds the adapter byte limit");
|
|
721
|
+
}
|
|
722
|
+
return output;
|
|
723
|
+
}
|
|
724
|
+
function normalizeActionProposalV1(input, manifest) {
|
|
725
|
+
exactKeys(input, [
|
|
726
|
+
"schemaVersion",
|
|
727
|
+
"actionId",
|
|
728
|
+
"actionClass",
|
|
729
|
+
"input",
|
|
730
|
+
"riskClass",
|
|
731
|
+
"metadata",
|
|
732
|
+
], "action proposal");
|
|
733
|
+
if (input.schemaVersion !== 1 ||
|
|
734
|
+
!["low", "moderate", "high"].includes(input.riskClass)) {
|
|
735
|
+
invalid("action proposal is invalid");
|
|
736
|
+
}
|
|
737
|
+
const action = cloneAndFreeze({
|
|
738
|
+
schemaVersion: 1,
|
|
739
|
+
actionId: identifier(input.actionId, "actionId"),
|
|
740
|
+
actionClass: token(input.actionClass, "actionClass", 256),
|
|
741
|
+
input: normalizeJsonObject(input.input, "action.input"),
|
|
742
|
+
riskClass: input.riskClass,
|
|
743
|
+
metadata: normalizeMetadata(input.metadata, "action.metadata"),
|
|
744
|
+
});
|
|
745
|
+
if (jsonByteLength(action) > manifest.maximumActionBytes) {
|
|
746
|
+
invalid("action proposal exceeds the adapter byte limit");
|
|
747
|
+
}
|
|
748
|
+
return action;
|
|
749
|
+
}
|
|
750
|
+
function normalizeContentReference(input, label) {
|
|
751
|
+
exactKeys(input, ["uri", "mediaType", "byteLength", "contentDigest"], label);
|
|
752
|
+
const value = input;
|
|
753
|
+
return cloneAndFreeze({
|
|
754
|
+
uri: text(value.uri, `${label}.uri`, 4_096),
|
|
755
|
+
mediaType: token(value.mediaType, `${label}.mediaType`, 256),
|
|
756
|
+
byteLength: safeInteger(value.byteLength, `${label}.byteLength`, 0),
|
|
757
|
+
contentDigest: token(value.contentDigest, `${label}.contentDigest`, 256),
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
function exactKeys(input, keys, label) {
|
|
761
|
+
if (!isPlainRecord(input))
|
|
762
|
+
invalid(`${label} must be an object`);
|
|
763
|
+
const actual = Object.keys(input).sort(compareAscii);
|
|
764
|
+
const expected = [...keys].sort(compareAscii);
|
|
765
|
+
if (actual.length !== expected.length ||
|
|
766
|
+
actual.some((key, index) => key !== expected[index])) {
|
|
767
|
+
invalid(`${label} fields are invalid`);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function enumArray(input, allowed, label, maximum, requireNonEmpty) {
|
|
771
|
+
if (!Array.isArray(input) ||
|
|
772
|
+
input.length > maximum ||
|
|
773
|
+
(requireNonEmpty && input.length === 0) ||
|
|
774
|
+
input.some((value) => typeof value !== "string" || !allowed.has(value))) {
|
|
775
|
+
invalid(`${label} is invalid`);
|
|
776
|
+
}
|
|
777
|
+
const result = [...new Set(input)].sort(compareAscii);
|
|
778
|
+
if (result.length !== input.length)
|
|
779
|
+
invalid(`${label} contains duplicates`);
|
|
780
|
+
return Object.freeze(result);
|
|
781
|
+
}
|
|
782
|
+
function uniqueIds(ids, label) {
|
|
783
|
+
if (new Set(ids).size !== ids.length)
|
|
784
|
+
invalid(`${label} IDs are duplicated`);
|
|
785
|
+
}
|
|
786
|
+
function nullableIdentifier(input, label) {
|
|
787
|
+
return input === null ? null : identifier(input, label);
|
|
788
|
+
}
|
|
789
|
+
function token(input, label, maximum) {
|
|
790
|
+
if (typeof input !== "string" ||
|
|
791
|
+
input.length === 0 ||
|
|
792
|
+
input.length > maximum ||
|
|
793
|
+
input.trim() !== input ||
|
|
794
|
+
/[\u0000-\u001f\u007f]/u.test(input)) {
|
|
795
|
+
invalid(`${label} must be bounded text`);
|
|
796
|
+
}
|
|
797
|
+
return input;
|
|
798
|
+
}
|
|
799
|
+
function text(input, label, maximum) {
|
|
800
|
+
if (typeof input !== "string" ||
|
|
801
|
+
input.length === 0 ||
|
|
802
|
+
input.length > maximum ||
|
|
803
|
+
input.trim() !== input ||
|
|
804
|
+
input.includes("\u0000")) {
|
|
805
|
+
invalid(`${label} must be bounded text`);
|
|
806
|
+
}
|
|
807
|
+
return input;
|
|
808
|
+
}
|
|
809
|
+
function boolean(input, label) {
|
|
810
|
+
if (typeof input !== "boolean")
|
|
811
|
+
invalid(`${label} must be boolean`);
|
|
812
|
+
return input;
|
|
813
|
+
}
|
|
814
|
+
function positiveInteger(input, label, maximum) {
|
|
815
|
+
return safeInteger(input, label, 1, maximum);
|
|
816
|
+
}
|
|
817
|
+
function safeInteger(input, label, minimum, maximum = Number.MAX_SAFE_INTEGER) {
|
|
818
|
+
if (!Number.isSafeInteger(input) ||
|
|
819
|
+
input < minimum ||
|
|
820
|
+
input > maximum) {
|
|
821
|
+
invalid(`${label} must be a safe integer from ${minimum} through ${maximum}`);
|
|
822
|
+
}
|
|
823
|
+
return input;
|
|
824
|
+
}
|
|
825
|
+
function isPlainRecord(input) {
|
|
826
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
827
|
+
return false;
|
|
828
|
+
const prototype = Object.getPrototypeOf(input);
|
|
829
|
+
return prototype === Object.prototype || prototype === null;
|
|
830
|
+
}
|
|
831
|
+
function sameJson(left, right) {
|
|
832
|
+
return (JSON.stringify(normalizeJson(left, "left")) ===
|
|
833
|
+
JSON.stringify(normalizeJson(right, "right")));
|
|
834
|
+
}
|
|
835
|
+
function compareAscii(left, right) {
|
|
836
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
837
|
+
}
|
|
838
|
+
function deepFreeze(input) {
|
|
839
|
+
if (input && typeof input === "object" && !Object.isFrozen(input)) {
|
|
840
|
+
Object.freeze(input);
|
|
841
|
+
for (const key of Object.getOwnPropertyNames(input)) {
|
|
842
|
+
deepFreeze(input[key]);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return input;
|
|
846
|
+
}
|
|
847
|
+
function invalid(message) {
|
|
848
|
+
throw new PortableAgentErrorV1("VALIDATION_ERROR", message);
|
|
849
|
+
}
|
|
850
|
+
function stateInvalid(message) {
|
|
851
|
+
throw new PortableAgentErrorV1("STATE_INVALID", message);
|
|
852
|
+
}
|
|
853
|
+
//# sourceMappingURL=adapter-validation.js.map
|