@adcp/sdk 16.2.0 → 16.4.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/AGENTS.md +2 -0
- package/dist/lib/adapters/version/3.1/assignments.d.mts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.d.ts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.js +67 -0
- package/dist/lib/adapters/version/3.1/assignments.mjs +43 -0
- package/dist/lib/adapters/version/3.1/bidding.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.js +115 -0
- package/dist/lib/adapters/version/3.1/bidding.mjs +90 -0
- package/dist/lib/adapters/version/3.1/delivery.d.mts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.d.ts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.js +78 -0
- package/dist/lib/adapters/version/3.1/delivery.mjs +54 -0
- package/dist/lib/adapters/version/3.1/index.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/index.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/index.js +36 -0
- package/dist/lib/adapters/version/3.1/index.mjs +12 -0
- package/dist/lib/adapters/version/index.d.mts +59 -7
- package/dist/lib/adapters/version/index.d.ts +59 -7
- package/dist/lib/adapters/version/index.js +51 -2
- package/dist/lib/adapters/version/index.mjs +49 -1
- package/dist/lib/adapters/version/negotiate.d.mts +71 -0
- package/dist/lib/adapters/version/negotiate.d.ts +71 -0
- package/dist/lib/adapters/version/negotiate.js +101 -0
- package/dist/lib/adapters/version/negotiate.mjs +73 -0
- package/dist/lib/adapters/version/schema-projection.d.mts +33 -0
- package/dist/lib/adapters/version/schema-projection.d.ts +33 -0
- package/dist/lib/adapters/version/schema-projection.js +294 -0
- package/dist/lib/adapters/version/schema-projection.mjs +269 -0
- package/dist/lib/adapters/version/translate.d.mts +122 -0
- package/dist/lib/adapters/version/translate.d.ts +122 -0
- package/dist/lib/adapters/version/translate.js +169 -0
- package/dist/lib/adapters/version/translate.mjs +141 -0
- package/dist/lib/adapters/version/types.d.mts +51 -10
- package/dist/lib/adapters/version/types.d.ts +51 -10
- package/dist/lib/core/AgentClient.d.mts +1 -1
- package/dist/lib/core/AgentClient.d.ts +1 -1
- package/dist/lib/core/AgentClient.js +17 -1
- package/dist/lib/core/AgentClient.mjs +17 -1
- package/dist/lib/core/ConversationTypes.d.mts +7 -0
- package/dist/lib/core/ConversationTypes.d.ts +7 -0
- package/dist/lib/core/SingleAgentClient.d.mts +82 -0
- package/dist/lib/core/SingleAgentClient.d.ts +82 -0
- package/dist/lib/core/SingleAgentClient.js +336 -15
- package/dist/lib/core/SingleAgentClient.mjs +350 -17
- package/dist/lib/core/TaskExecutor.d.mts +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +1 -1
- package/dist/lib/core/TaskExecutor.js +182 -31
- package/dist/lib/core/TaskExecutor.mjs +183 -32
- package/dist/lib/core/account-reduction.d.mts +36 -0
- package/dist/lib/core/account-reduction.d.ts +36 -0
- package/dist/lib/core/account-reduction.js +127 -0
- package/dist/lib/core/account-reduction.mjs +98 -0
- package/dist/lib/core/buyer-account-registry.d.mts +52 -0
- package/dist/lib/core/buyer-account-registry.d.ts +52 -0
- package/dist/lib/core/buyer-account-registry.js +142 -6
- package/dist/lib/core/buyer-account-registry.mjs +142 -6
- package/dist/lib/errors/index.d.mts +34 -0
- package/dist/lib/errors/index.d.ts +34 -0
- package/dist/lib/errors/index.js +11 -0
- package/dist/lib/errors/index.mjs +10 -0
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.mjs +2 -0
- package/dist/lib/media-buy/action-contracts.js +4 -4
- package/dist/lib/media-buy/action-contracts.mjs +4 -4
- package/dist/lib/media-buy/action-types.d.mts +3 -2
- package/dist/lib/media-buy/action-types.d.ts +3 -2
- package/dist/lib/media-buy/compatibility.d.mts +1 -0
- package/dist/lib/media-buy/compatibility.d.ts +1 -0
- package/dist/lib/media-buy/compatibility.js +33 -10
- package/dist/lib/media-buy/compatibility.mjs +32 -14
- package/dist/lib/media-buy/update-fields.generated.d.mts +3 -2
- package/dist/lib/media-buy/update-fields.generated.d.ts +3 -2
- package/dist/lib/negotiation/buyer.js +11 -5
- package/dist/lib/negotiation/buyer.mjs +11 -5
- package/dist/lib/negotiation/types.d.mts +4 -2
- package/dist/lib/negotiation/types.d.ts +4 -2
- package/dist/lib/negotiation/verification.d.mts +15 -6
- package/dist/lib/negotiation/verification.d.ts +15 -6
- package/dist/lib/negotiation/verification.js +146 -27
- package/dist/lib/negotiation/verification.mjs +146 -28
- package/dist/lib/negotiation/version.d.mts +6 -0
- package/dist/lib/negotiation/version.d.ts +6 -0
- package/dist/lib/negotiation/version.js +60 -0
- package/dist/lib/negotiation/version.mjs +33 -0
- package/dist/lib/registry/types.generated.d.mts +1 -1
- package/dist/lib/registry/types.generated.d.ts +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +21 -0
- package/dist/lib/server/create-adcp-server.d.ts +21 -0
- package/dist/lib/server/create-adcp-server.js +98 -16
- package/dist/lib/server/create-adcp-server.mjs +84 -2
- package/dist/lib/server/error-arm-tools.d.mts +10 -10
- package/dist/lib/server/error-arm-tools.d.ts +10 -10
- package/dist/lib/server/error-arm-tools.js +22 -58
- package/dist/lib/server/error-arm-tools.mjs +30 -65
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/storage/interfaces.d.mts +2 -0
- package/dist/lib/storage/interfaces.d.ts +2 -0
- package/dist/lib/testing/compliance/comply.js +17 -0
- package/dist/lib/testing/compliance/comply.mjs +17 -0
- package/dist/lib/testing/index.d.mts +3 -0
- package/dist/lib/testing/index.d.ts +3 -0
- package/dist/lib/testing/index.js +44 -2
- package/dist/lib/testing/index.mjs +42 -0
- package/dist/lib/testing/storyboard/collection-selection.d.mts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.d.ts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.js +384 -0
- package/dist/lib/testing/storyboard/collection-selection.mjs +348 -0
- package/dist/lib/testing/storyboard/context.js +12 -6
- package/dist/lib/testing/storyboard/context.mjs +12 -6
- package/dist/lib/testing/storyboard/fixture-resolution.d.mts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.d.ts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.js +216 -12
- package/dist/lib/testing/storyboard/fixture-resolution.mjs +218 -12
- package/dist/lib/testing/storyboard/index.d.mts +9 -2
- package/dist/lib/testing/storyboard/index.d.ts +9 -2
- package/dist/lib/testing/storyboard/index.js +51 -2
- package/dist/lib/testing/storyboard/index.mjs +49 -1
- package/dist/lib/testing/storyboard/prepared-fixtures.d.mts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.d.ts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.js +434 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.mjs +389 -0
- package/dist/lib/testing/storyboard/protected-context.d.mts +127 -0
- package/dist/lib/testing/storyboard/protected-context.d.ts +127 -0
- package/dist/lib/testing/storyboard/protected-context.js +301 -0
- package/dist/lib/testing/storyboard/protected-context.mjs +267 -0
- package/dist/lib/testing/storyboard/rejection-hints.js +13 -4
- package/dist/lib/testing/storyboard/rejection-hints.mjs +13 -4
- package/dist/lib/testing/storyboard/runner.d.mts +24 -0
- package/dist/lib/testing/storyboard/runner.d.ts +24 -0
- package/dist/lib/testing/storyboard/runner.js +1061 -105
- package/dist/lib/testing/storyboard/runner.mjs +1075 -106
- package/dist/lib/testing/storyboard/seeding.d.mts +15 -1
- package/dist/lib/testing/storyboard/seeding.d.ts +15 -1
- package/dist/lib/testing/storyboard/seeding.js +272 -12
- package/dist/lib/testing/storyboard/seeding.mjs +274 -13
- package/dist/lib/testing/storyboard/types.d.mts +207 -10
- package/dist/lib/testing/storyboard/types.d.ts +207 -10
- package/dist/lib/testing/storyboard/types.js +1 -1
- package/dist/lib/testing/storyboard/types.mjs +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +95 -0
- package/dist/lib/testing/storyboard/validations.mjs +95 -0
- package/dist/lib/validation/bundled-schema-store.js +5 -0
- package/dist/lib/validation/bundled-schema-store.mjs +4 -0
- package/dist/lib/validation/schema-loader.js +19 -2
- package/dist/lib/validation/schema-loader.mjs +22 -3
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +850 -0
- package/docs/guides/PROPOSAL-TERMS-VERIFICATION.md +66 -0
- package/docs/llms.txt +4 -3
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var protected_context_exports = {};
|
|
20
|
+
__export(protected_context_exports, {
|
|
21
|
+
PROTECTED_CONTEXT_PREFIX: () => PROTECTED_CONTEXT_PREFIX,
|
|
22
|
+
ProtectedContextViolation: () => ProtectedContextViolation,
|
|
23
|
+
applyProtectedContextPatch: () => applyProtectedContextPatch,
|
|
24
|
+
assertWellFormedProtectedKey: () => assertWellFormedProtectedKey,
|
|
25
|
+
createProtectedContextWriter: () => createProtectedContextWriter,
|
|
26
|
+
detachProtectedValue: () => detachProtectedValue,
|
|
27
|
+
forwardProtectedContext: () => forwardProtectedContext,
|
|
28
|
+
freezeDeep: () => freezeDeep,
|
|
29
|
+
isProtectedContextKey: () => isProtectedContextKey,
|
|
30
|
+
listProtectedContextKeys: () => listProtectedContextKeys,
|
|
31
|
+
registerProtectedContext: () => registerProtectedContext
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(protected_context_exports);
|
|
34
|
+
const PROTECTED_CONTEXT_PREFIX = "__adcp_fixture_";
|
|
35
|
+
const PROTECTED_KEY_PATTERN = /^__adcp_fixture_[A-Za-z0-9_]+$/;
|
|
36
|
+
const PROTOTYPE_SENSITIVE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
37
|
+
function isProtectedContextKey(key) {
|
|
38
|
+
return key.startsWith(PROTECTED_CONTEXT_PREFIX);
|
|
39
|
+
}
|
|
40
|
+
class ProtectedContextViolation extends Error {
|
|
41
|
+
code;
|
|
42
|
+
keys;
|
|
43
|
+
constructor(code, keys, detail) {
|
|
44
|
+
super(detail);
|
|
45
|
+
this.name = "ProtectedContextViolation";
|
|
46
|
+
this.code = code;
|
|
47
|
+
this.keys = [...keys];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const writers = /* @__PURE__ */ new WeakSet();
|
|
51
|
+
function createProtectedContextWriter() {
|
|
52
|
+
const writer = Object.freeze({
|
|
53
|
+
kind: "adcp.storyboard.protected-context-writer"
|
|
54
|
+
});
|
|
55
|
+
writers.add(writer);
|
|
56
|
+
return writer;
|
|
57
|
+
}
|
|
58
|
+
function isTrustedWriter(writer) {
|
|
59
|
+
return writer !== void 0 && writers.has(writer);
|
|
60
|
+
}
|
|
61
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
62
|
+
function registerProtectedContext(context, writer) {
|
|
63
|
+
if (!isTrustedWriter(writer)) {
|
|
64
|
+
throw new ProtectedContextViolation(
|
|
65
|
+
"unauthorized_write",
|
|
66
|
+
[],
|
|
67
|
+
"registerProtectedContext requires a runner-owned protected-context capability"
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (!states.has(context)) states.set(context, { committed: /* @__PURE__ */ new Set(), writer });
|
|
71
|
+
}
|
|
72
|
+
function forwardProtectedContext(from, to) {
|
|
73
|
+
const state = states.get(from);
|
|
74
|
+
if (!state) return;
|
|
75
|
+
const existing = states.get(to);
|
|
76
|
+
if (existing) {
|
|
77
|
+
for (const key of state.committed) existing.committed.add(key);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
states.set(to, { committed: new Set(state.committed), writer: state.writer });
|
|
81
|
+
}
|
|
82
|
+
function listProtectedContextKeys(context) {
|
|
83
|
+
return [...states.get(context)?.committed ?? []].sort();
|
|
84
|
+
}
|
|
85
|
+
function markUnavailableEntries(markUnavailable) {
|
|
86
|
+
if (markUnavailable === void 0) return [];
|
|
87
|
+
if (Array.isArray(markUnavailable)) return markUnavailable.map((key) => [key, "unavailable"]);
|
|
88
|
+
return Object.entries(markUnavailable);
|
|
89
|
+
}
|
|
90
|
+
function applyProtectedContextPatch(context, patch, options) {
|
|
91
|
+
const state = states.get(context);
|
|
92
|
+
const trusted = isTrustedWriter(options.writer) && state !== void 0 && state.writer === options.writer;
|
|
93
|
+
const setEntries = Object.entries(patch.set ?? {});
|
|
94
|
+
const unsetKeys = patch.unset ?? [];
|
|
95
|
+
const provenanceEntries = Object.entries(patch.setProvenance ?? {});
|
|
96
|
+
const provenanceUnset = patch.unsetProvenance ?? [];
|
|
97
|
+
const unavailableEntries = markUnavailableEntries(patch.markUnavailable);
|
|
98
|
+
const clearUnavailable = patch.clearUnavailable ?? [];
|
|
99
|
+
const prototypeKeys = [
|
|
100
|
+
...setEntries.map(([key]) => key),
|
|
101
|
+
...unsetKeys,
|
|
102
|
+
...provenanceEntries.map(([key]) => key),
|
|
103
|
+
...provenanceUnset,
|
|
104
|
+
...unavailableEntries.map(([key]) => key),
|
|
105
|
+
...clearUnavailable
|
|
106
|
+
].filter((key) => PROTOTYPE_SENSITIVE_KEYS.has(key));
|
|
107
|
+
if (prototypeKeys.length > 0) {
|
|
108
|
+
throw new ProtectedContextViolation(
|
|
109
|
+
"reserved_key",
|
|
110
|
+
[...new Set(prototypeKeys)],
|
|
111
|
+
`${options.origin}: refused prototype-sensitive context key(s) ${[...new Set(prototypeKeys)].join(", ")}; these can rewrite the context's prototype and shadow reserved fixture keys. Rename them to anything other than __proto__, constructor or prototype.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (!trusted) {
|
|
115
|
+
const valueKeys = setEntries.map(([key]) => key).filter(isProtectedContextKey);
|
|
116
|
+
if (valueKeys.length > 0) {
|
|
117
|
+
throw new ProtectedContextViolation(
|
|
118
|
+
"unauthorized_write",
|
|
119
|
+
valueKeys,
|
|
120
|
+
`${options.origin}: cannot write reserved fixture-preparation context key(s) ${valueKeys.join(", ")}; the "${PROTECTED_CONTEXT_PREFIX}" namespace is owned by fixture preparation. Declare the value under fixture_resolution[].context_outputs instead, or rename the key.`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const deleteKeys = unsetKeys.filter(isProtectedContextKey);
|
|
124
|
+
if (deleteKeys.length > 0) {
|
|
125
|
+
throw new ProtectedContextViolation(
|
|
126
|
+
"unauthorized_delete",
|
|
127
|
+
deleteKeys,
|
|
128
|
+
`${options.origin}: cannot delete reserved fixture-preparation context key(s) ${deleteKeys.join(", ")}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const metadataKeys = [
|
|
132
|
+
...provenanceEntries.map(([key]) => key),
|
|
133
|
+
...provenanceUnset,
|
|
134
|
+
...unavailableEntries.map(([key]) => key),
|
|
135
|
+
...clearUnavailable
|
|
136
|
+
].filter(isProtectedContextKey);
|
|
137
|
+
if (metadataKeys.length > 0) {
|
|
138
|
+
throw new ProtectedContextViolation(
|
|
139
|
+
"unauthorized_metadata",
|
|
140
|
+
[...new Set(metadataKeys)],
|
|
141
|
+
`${options.origin}: cannot change provenance or availability metadata for reserved key(s) ${[...new Set(metadataKeys)].join(", ")}; suppressing a prepared capture is as damaging as overwriting it`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
const recommitted = [
|
|
146
|
+
...setEntries.map(([key]) => key),
|
|
147
|
+
...unsetKeys,
|
|
148
|
+
...provenanceUnset,
|
|
149
|
+
...unavailableEntries.map(([key]) => key)
|
|
150
|
+
].filter((key) => isProtectedContextKey(key) && state?.committed.has(key) === true);
|
|
151
|
+
if (recommitted.length > 0) {
|
|
152
|
+
throw new ProtectedContextViolation(
|
|
153
|
+
"already_committed",
|
|
154
|
+
recommitted,
|
|
155
|
+
`${options.origin}: reserved context key(s) ${recommitted.join(", ")} are already committed by fixture preparation and are immutable for the rest of the run`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
for (const [key] of setEntries) {
|
|
159
|
+
if (isProtectedContextKey(key)) assertWellFormedProtectedKey(key, options.origin);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const mutatedKeys = [.../* @__PURE__ */ new Set([...setEntries.map(([key]) => key), ...unsetKeys])];
|
|
163
|
+
if (!Object.isExtensible(context)) {
|
|
164
|
+
const unsetSet = new Set(unsetKeys);
|
|
165
|
+
const added = mutatedKeys.filter((key) => !Object.hasOwn(context, key) || unsetSet.has(key));
|
|
166
|
+
if (added.length > 0) {
|
|
167
|
+
throw new ProtectedContextViolation(
|
|
168
|
+
"uncloneable_value",
|
|
169
|
+
added,
|
|
170
|
+
`${options.origin}: the storyboard context is not extensible, so key(s) ${added.join(", ")} cannot be written`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const locked = mutatedKeys.filter((key) => {
|
|
175
|
+
const descriptor = Object.getOwnPropertyDescriptor(context, key);
|
|
176
|
+
return descriptor !== void 0 && descriptor.configurable === false;
|
|
177
|
+
});
|
|
178
|
+
if (locked.length > 0) {
|
|
179
|
+
throw new ProtectedContextViolation(
|
|
180
|
+
"uncloneable_value",
|
|
181
|
+
locked,
|
|
182
|
+
`${options.origin}: context key(s) ${locked.join(", ")} are non-configurable and cannot be rewritten or removed`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
186
|
+
for (const [key, value] of setEntries) {
|
|
187
|
+
if (!isProtectedContextKey(key)) continue;
|
|
188
|
+
let cloned;
|
|
189
|
+
try {
|
|
190
|
+
cloned = structuredClone(value);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
throw new ProtectedContextViolation(
|
|
193
|
+
"uncloneable_value",
|
|
194
|
+
[key],
|
|
195
|
+
`${options.origin}: reserved context key "${key}" holds a value that cannot be detached (${error instanceof Error ? error.message : String(error)}); captures must be plain JSON-shaped data`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
assertFreezable(cloned, key, options.origin);
|
|
199
|
+
snapshots.set(key, freezeDeep(cloned));
|
|
200
|
+
}
|
|
201
|
+
const provenanceSnapshots = /* @__PURE__ */ new Map();
|
|
202
|
+
for (const [key, entry] of provenanceEntries) {
|
|
203
|
+
if (!isProtectedContextKey(key)) continue;
|
|
204
|
+
let cloned;
|
|
205
|
+
try {
|
|
206
|
+
cloned = structuredClone(entry);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
throw new ProtectedContextViolation(
|
|
209
|
+
"uncloneable_value",
|
|
210
|
+
[key],
|
|
211
|
+
`${options.origin}: provenance for reserved context key "${key}" cannot be detached (${error instanceof Error ? error.message : String(error)}); capture provenance must be plain JSON-shaped data`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
assertFreezable(cloned, key, options.origin);
|
|
215
|
+
provenanceSnapshots.set(key, freezeDeep(cloned));
|
|
216
|
+
}
|
|
217
|
+
for (const key of unsetKeys) delete context[key];
|
|
218
|
+
for (const [key, value] of setEntries) {
|
|
219
|
+
Object.defineProperty(context, key, {
|
|
220
|
+
value: snapshots.has(key) ? snapshots.get(key) : value,
|
|
221
|
+
writable: true,
|
|
222
|
+
enumerable: true,
|
|
223
|
+
configurable: true
|
|
224
|
+
});
|
|
225
|
+
if (isProtectedContextKey(key)) state?.committed.add(key);
|
|
226
|
+
}
|
|
227
|
+
const sinks = options.sinks;
|
|
228
|
+
if (sinks?.provenance) {
|
|
229
|
+
for (const key of provenanceUnset) sinks.provenance.delete(key);
|
|
230
|
+
for (const [key, entry] of provenanceEntries) {
|
|
231
|
+
sinks.provenance.set(key, provenanceSnapshots.get(key) ?? entry);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (sinks?.unavailable) {
|
|
235
|
+
for (const key of clearUnavailable) sinks.unavailable.delete(key);
|
|
236
|
+
for (const [key, reason] of unavailableEntries) sinks.unavailable.set(key, reason);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function assertWellFormedProtectedKey(key, location) {
|
|
240
|
+
if (PROTOTYPE_SENSITIVE_KEYS.has(key)) {
|
|
241
|
+
throw new ProtectedContextViolation(
|
|
242
|
+
"invalid_key",
|
|
243
|
+
[key],
|
|
244
|
+
`${location}: "${key}" is a prototype-sensitive key; rename it to anything other than __proto__, constructor or prototype`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
if (!PROTECTED_KEY_PATTERN.test(key)) {
|
|
248
|
+
throw new ProtectedContextViolation(
|
|
249
|
+
"invalid_key",
|
|
250
|
+
[key],
|
|
251
|
+
`${location}: reserved capture key "${key}" must match ${PROTECTED_KEY_PATTERN.source} so whole-key $context.<key> substitution resolves it`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function assertFreezable(value, key, origin, seen = /* @__PURE__ */ new Set()) {
|
|
256
|
+
if (value === null || typeof value !== "object") return;
|
|
257
|
+
if (seen.has(value)) return;
|
|
258
|
+
seen.add(value);
|
|
259
|
+
if (Array.isArray(value)) {
|
|
260
|
+
for (const nested of value) assertFreezable(nested, key, origin, seen);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const prototype = Object.getPrototypeOf(value);
|
|
264
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
265
|
+
throw new ProtectedContextViolation(
|
|
266
|
+
"uncloneable_value",
|
|
267
|
+
[key],
|
|
268
|
+
`${origin}: reserved context key "${key}" holds a ${value.constructor?.name ?? "non-plain object"}, which Object.freeze cannot make immutable; captures must be plain JSON-shaped data`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
for (const nested of Reflect.ownKeys(value)) {
|
|
272
|
+
assertFreezable(value[nested], key, origin, seen);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function freezeDeep(value) {
|
|
276
|
+
if (value === null || typeof value !== "object") return value;
|
|
277
|
+
if (Object.isFrozen(value)) return value;
|
|
278
|
+
Object.freeze(value);
|
|
279
|
+
for (const nested of Reflect.ownKeys(value)) {
|
|
280
|
+
freezeDeep(value[nested]);
|
|
281
|
+
}
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
function detachProtectedValue(value) {
|
|
285
|
+
if (value === null || typeof value !== "object") return value;
|
|
286
|
+
return structuredClone(value);
|
|
287
|
+
}
|
|
288
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
289
|
+
0 && (module.exports = {
|
|
290
|
+
PROTECTED_CONTEXT_PREFIX,
|
|
291
|
+
ProtectedContextViolation,
|
|
292
|
+
applyProtectedContextPatch,
|
|
293
|
+
assertWellFormedProtectedKey,
|
|
294
|
+
createProtectedContextWriter,
|
|
295
|
+
detachProtectedValue,
|
|
296
|
+
forwardProtectedContext,
|
|
297
|
+
freezeDeep,
|
|
298
|
+
isProtectedContextKey,
|
|
299
|
+
listProtectedContextKeys,
|
|
300
|
+
registerProtectedContext
|
|
301
|
+
});
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
const PROTECTED_CONTEXT_PREFIX = "__adcp_fixture_";
|
|
2
|
+
const PROTECTED_KEY_PATTERN = /^__adcp_fixture_[A-Za-z0-9_]+$/;
|
|
3
|
+
const PROTOTYPE_SENSITIVE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
4
|
+
function isProtectedContextKey(key) {
|
|
5
|
+
return key.startsWith(PROTECTED_CONTEXT_PREFIX);
|
|
6
|
+
}
|
|
7
|
+
class ProtectedContextViolation extends Error {
|
|
8
|
+
code;
|
|
9
|
+
keys;
|
|
10
|
+
constructor(code, keys, detail) {
|
|
11
|
+
super(detail);
|
|
12
|
+
this.name = "ProtectedContextViolation";
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.keys = [...keys];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const writers = /* @__PURE__ */ new WeakSet();
|
|
18
|
+
function createProtectedContextWriter() {
|
|
19
|
+
const writer = Object.freeze({
|
|
20
|
+
kind: "adcp.storyboard.protected-context-writer"
|
|
21
|
+
});
|
|
22
|
+
writers.add(writer);
|
|
23
|
+
return writer;
|
|
24
|
+
}
|
|
25
|
+
function isTrustedWriter(writer) {
|
|
26
|
+
return writer !== void 0 && writers.has(writer);
|
|
27
|
+
}
|
|
28
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
29
|
+
function registerProtectedContext(context, writer) {
|
|
30
|
+
if (!isTrustedWriter(writer)) {
|
|
31
|
+
throw new ProtectedContextViolation(
|
|
32
|
+
"unauthorized_write",
|
|
33
|
+
[],
|
|
34
|
+
"registerProtectedContext requires a runner-owned protected-context capability"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (!states.has(context)) states.set(context, { committed: /* @__PURE__ */ new Set(), writer });
|
|
38
|
+
}
|
|
39
|
+
function forwardProtectedContext(from, to) {
|
|
40
|
+
const state = states.get(from);
|
|
41
|
+
if (!state) return;
|
|
42
|
+
const existing = states.get(to);
|
|
43
|
+
if (existing) {
|
|
44
|
+
for (const key of state.committed) existing.committed.add(key);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
states.set(to, { committed: new Set(state.committed), writer: state.writer });
|
|
48
|
+
}
|
|
49
|
+
function listProtectedContextKeys(context) {
|
|
50
|
+
return [...states.get(context)?.committed ?? []].sort();
|
|
51
|
+
}
|
|
52
|
+
function markUnavailableEntries(markUnavailable) {
|
|
53
|
+
if (markUnavailable === void 0) return [];
|
|
54
|
+
if (Array.isArray(markUnavailable)) return markUnavailable.map((key) => [key, "unavailable"]);
|
|
55
|
+
return Object.entries(markUnavailable);
|
|
56
|
+
}
|
|
57
|
+
function applyProtectedContextPatch(context, patch, options) {
|
|
58
|
+
const state = states.get(context);
|
|
59
|
+
const trusted = isTrustedWriter(options.writer) && state !== void 0 && state.writer === options.writer;
|
|
60
|
+
const setEntries = Object.entries(patch.set ?? {});
|
|
61
|
+
const unsetKeys = patch.unset ?? [];
|
|
62
|
+
const provenanceEntries = Object.entries(patch.setProvenance ?? {});
|
|
63
|
+
const provenanceUnset = patch.unsetProvenance ?? [];
|
|
64
|
+
const unavailableEntries = markUnavailableEntries(patch.markUnavailable);
|
|
65
|
+
const clearUnavailable = patch.clearUnavailable ?? [];
|
|
66
|
+
const prototypeKeys = [
|
|
67
|
+
...setEntries.map(([key]) => key),
|
|
68
|
+
...unsetKeys,
|
|
69
|
+
...provenanceEntries.map(([key]) => key),
|
|
70
|
+
...provenanceUnset,
|
|
71
|
+
...unavailableEntries.map(([key]) => key),
|
|
72
|
+
...clearUnavailable
|
|
73
|
+
].filter((key) => PROTOTYPE_SENSITIVE_KEYS.has(key));
|
|
74
|
+
if (prototypeKeys.length > 0) {
|
|
75
|
+
throw new ProtectedContextViolation(
|
|
76
|
+
"reserved_key",
|
|
77
|
+
[...new Set(prototypeKeys)],
|
|
78
|
+
`${options.origin}: refused prototype-sensitive context key(s) ${[...new Set(prototypeKeys)].join(", ")}; these can rewrite the context's prototype and shadow reserved fixture keys. Rename them to anything other than __proto__, constructor or prototype.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (!trusted) {
|
|
82
|
+
const valueKeys = setEntries.map(([key]) => key).filter(isProtectedContextKey);
|
|
83
|
+
if (valueKeys.length > 0) {
|
|
84
|
+
throw new ProtectedContextViolation(
|
|
85
|
+
"unauthorized_write",
|
|
86
|
+
valueKeys,
|
|
87
|
+
`${options.origin}: cannot write reserved fixture-preparation context key(s) ${valueKeys.join(", ")}; the "${PROTECTED_CONTEXT_PREFIX}" namespace is owned by fixture preparation. Declare the value under fixture_resolution[].context_outputs instead, or rename the key.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const deleteKeys = unsetKeys.filter(isProtectedContextKey);
|
|
91
|
+
if (deleteKeys.length > 0) {
|
|
92
|
+
throw new ProtectedContextViolation(
|
|
93
|
+
"unauthorized_delete",
|
|
94
|
+
deleteKeys,
|
|
95
|
+
`${options.origin}: cannot delete reserved fixture-preparation context key(s) ${deleteKeys.join(", ")}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const metadataKeys = [
|
|
99
|
+
...provenanceEntries.map(([key]) => key),
|
|
100
|
+
...provenanceUnset,
|
|
101
|
+
...unavailableEntries.map(([key]) => key),
|
|
102
|
+
...clearUnavailable
|
|
103
|
+
].filter(isProtectedContextKey);
|
|
104
|
+
if (metadataKeys.length > 0) {
|
|
105
|
+
throw new ProtectedContextViolation(
|
|
106
|
+
"unauthorized_metadata",
|
|
107
|
+
[...new Set(metadataKeys)],
|
|
108
|
+
`${options.origin}: cannot change provenance or availability metadata for reserved key(s) ${[...new Set(metadataKeys)].join(", ")}; suppressing a prepared capture is as damaging as overwriting it`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
const recommitted = [
|
|
113
|
+
...setEntries.map(([key]) => key),
|
|
114
|
+
...unsetKeys,
|
|
115
|
+
...provenanceUnset,
|
|
116
|
+
...unavailableEntries.map(([key]) => key)
|
|
117
|
+
].filter((key) => isProtectedContextKey(key) && state?.committed.has(key) === true);
|
|
118
|
+
if (recommitted.length > 0) {
|
|
119
|
+
throw new ProtectedContextViolation(
|
|
120
|
+
"already_committed",
|
|
121
|
+
recommitted,
|
|
122
|
+
`${options.origin}: reserved context key(s) ${recommitted.join(", ")} are already committed by fixture preparation and are immutable for the rest of the run`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
for (const [key] of setEntries) {
|
|
126
|
+
if (isProtectedContextKey(key)) assertWellFormedProtectedKey(key, options.origin);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const mutatedKeys = [.../* @__PURE__ */ new Set([...setEntries.map(([key]) => key), ...unsetKeys])];
|
|
130
|
+
if (!Object.isExtensible(context)) {
|
|
131
|
+
const unsetSet = new Set(unsetKeys);
|
|
132
|
+
const added = mutatedKeys.filter((key) => !Object.hasOwn(context, key) || unsetSet.has(key));
|
|
133
|
+
if (added.length > 0) {
|
|
134
|
+
throw new ProtectedContextViolation(
|
|
135
|
+
"uncloneable_value",
|
|
136
|
+
added,
|
|
137
|
+
`${options.origin}: the storyboard context is not extensible, so key(s) ${added.join(", ")} cannot be written`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const locked = mutatedKeys.filter((key) => {
|
|
142
|
+
const descriptor = Object.getOwnPropertyDescriptor(context, key);
|
|
143
|
+
return descriptor !== void 0 && descriptor.configurable === false;
|
|
144
|
+
});
|
|
145
|
+
if (locked.length > 0) {
|
|
146
|
+
throw new ProtectedContextViolation(
|
|
147
|
+
"uncloneable_value",
|
|
148
|
+
locked,
|
|
149
|
+
`${options.origin}: context key(s) ${locked.join(", ")} are non-configurable and cannot be rewritten or removed`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
153
|
+
for (const [key, value] of setEntries) {
|
|
154
|
+
if (!isProtectedContextKey(key)) continue;
|
|
155
|
+
let cloned;
|
|
156
|
+
try {
|
|
157
|
+
cloned = structuredClone(value);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
throw new ProtectedContextViolation(
|
|
160
|
+
"uncloneable_value",
|
|
161
|
+
[key],
|
|
162
|
+
`${options.origin}: reserved context key "${key}" holds a value that cannot be detached (${error instanceof Error ? error.message : String(error)}); captures must be plain JSON-shaped data`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
assertFreezable(cloned, key, options.origin);
|
|
166
|
+
snapshots.set(key, freezeDeep(cloned));
|
|
167
|
+
}
|
|
168
|
+
const provenanceSnapshots = /* @__PURE__ */ new Map();
|
|
169
|
+
for (const [key, entry] of provenanceEntries) {
|
|
170
|
+
if (!isProtectedContextKey(key)) continue;
|
|
171
|
+
let cloned;
|
|
172
|
+
try {
|
|
173
|
+
cloned = structuredClone(entry);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new ProtectedContextViolation(
|
|
176
|
+
"uncloneable_value",
|
|
177
|
+
[key],
|
|
178
|
+
`${options.origin}: provenance for reserved context key "${key}" cannot be detached (${error instanceof Error ? error.message : String(error)}); capture provenance must be plain JSON-shaped data`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
assertFreezable(cloned, key, options.origin);
|
|
182
|
+
provenanceSnapshots.set(key, freezeDeep(cloned));
|
|
183
|
+
}
|
|
184
|
+
for (const key of unsetKeys) delete context[key];
|
|
185
|
+
for (const [key, value] of setEntries) {
|
|
186
|
+
Object.defineProperty(context, key, {
|
|
187
|
+
value: snapshots.has(key) ? snapshots.get(key) : value,
|
|
188
|
+
writable: true,
|
|
189
|
+
enumerable: true,
|
|
190
|
+
configurable: true
|
|
191
|
+
});
|
|
192
|
+
if (isProtectedContextKey(key)) state?.committed.add(key);
|
|
193
|
+
}
|
|
194
|
+
const sinks = options.sinks;
|
|
195
|
+
if (sinks?.provenance) {
|
|
196
|
+
for (const key of provenanceUnset) sinks.provenance.delete(key);
|
|
197
|
+
for (const [key, entry] of provenanceEntries) {
|
|
198
|
+
sinks.provenance.set(key, provenanceSnapshots.get(key) ?? entry);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (sinks?.unavailable) {
|
|
202
|
+
for (const key of clearUnavailable) sinks.unavailable.delete(key);
|
|
203
|
+
for (const [key, reason] of unavailableEntries) sinks.unavailable.set(key, reason);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function assertWellFormedProtectedKey(key, location) {
|
|
207
|
+
if (PROTOTYPE_SENSITIVE_KEYS.has(key)) {
|
|
208
|
+
throw new ProtectedContextViolation(
|
|
209
|
+
"invalid_key",
|
|
210
|
+
[key],
|
|
211
|
+
`${location}: "${key}" is a prototype-sensitive key; rename it to anything other than __proto__, constructor or prototype`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
if (!PROTECTED_KEY_PATTERN.test(key)) {
|
|
215
|
+
throw new ProtectedContextViolation(
|
|
216
|
+
"invalid_key",
|
|
217
|
+
[key],
|
|
218
|
+
`${location}: reserved capture key "${key}" must match ${PROTECTED_KEY_PATTERN.source} so whole-key $context.<key> substitution resolves it`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function assertFreezable(value, key, origin, seen = /* @__PURE__ */ new Set()) {
|
|
223
|
+
if (value === null || typeof value !== "object") return;
|
|
224
|
+
if (seen.has(value)) return;
|
|
225
|
+
seen.add(value);
|
|
226
|
+
if (Array.isArray(value)) {
|
|
227
|
+
for (const nested of value) assertFreezable(nested, key, origin, seen);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const prototype = Object.getPrototypeOf(value);
|
|
231
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
232
|
+
throw new ProtectedContextViolation(
|
|
233
|
+
"uncloneable_value",
|
|
234
|
+
[key],
|
|
235
|
+
`${origin}: reserved context key "${key}" holds a ${value.constructor?.name ?? "non-plain object"}, which Object.freeze cannot make immutable; captures must be plain JSON-shaped data`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
for (const nested of Reflect.ownKeys(value)) {
|
|
239
|
+
assertFreezable(value[nested], key, origin, seen);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function freezeDeep(value) {
|
|
243
|
+
if (value === null || typeof value !== "object") return value;
|
|
244
|
+
if (Object.isFrozen(value)) return value;
|
|
245
|
+
Object.freeze(value);
|
|
246
|
+
for (const nested of Reflect.ownKeys(value)) {
|
|
247
|
+
freezeDeep(value[nested]);
|
|
248
|
+
}
|
|
249
|
+
return value;
|
|
250
|
+
}
|
|
251
|
+
function detachProtectedValue(value) {
|
|
252
|
+
if (value === null || typeof value !== "object") return value;
|
|
253
|
+
return structuredClone(value);
|
|
254
|
+
}
|
|
255
|
+
export {
|
|
256
|
+
PROTECTED_CONTEXT_PREFIX,
|
|
257
|
+
ProtectedContextViolation,
|
|
258
|
+
applyProtectedContextPatch,
|
|
259
|
+
assertWellFormedProtectedKey,
|
|
260
|
+
createProtectedContextWriter,
|
|
261
|
+
detachProtectedValue,
|
|
262
|
+
forwardProtectedContext,
|
|
263
|
+
freezeDeep,
|
|
264
|
+
isProtectedContextKey,
|
|
265
|
+
listProtectedContextKeys,
|
|
266
|
+
registerProtectedContext
|
|
267
|
+
};
|
|
@@ -128,7 +128,12 @@ function formatMessage(input) {
|
|
|
128
128
|
const { contextKey, entry, rejectedValue, acceptedValues, requestField, currentTask } = input;
|
|
129
129
|
const valueRepr = formatScalar(rejectedValue);
|
|
130
130
|
const fieldRepr = requestField ? `\`${requestField}: ${valueRepr}\`` : `\`${valueRepr}\``;
|
|
131
|
-
const sourceDetail = entry.source_kind === "
|
|
131
|
+
const sourceDetail = entry.source_kind === "fixture_preparation" ? (
|
|
132
|
+
// Naming a convention extractor here would point an implementor at
|
|
133
|
+
// machinery that was never involved; the useful pointer is the
|
|
134
|
+
// fixture handle whose pinned entity produced the value.
|
|
135
|
+
`pinned by fixture preparation${entry.fixture_handle ? ` for handle \`${entry.fixture_handle}\`` : ""}`
|
|
136
|
+
) : entry.source_kind === "context_outputs" && entry.response_path ? `set by step \`${entry.source_step_id}\` from response path \`${entry.response_path}\`` : entry.source_task ? `set by step \`${entry.source_step_id}\` (convention extractor for \`${entry.source_task}\`)` : `set by step \`${entry.source_step_id}\``;
|
|
132
137
|
const acceptedRepr = `[${acceptedValues.map(formatScalar).join(", ")}]`;
|
|
133
138
|
const closing = buildClosing(entry.source_task, currentTask);
|
|
134
139
|
return `Rejected ${fieldRepr} was extracted from \`$context.${contextKey}\` (${sourceDetail}). Seller's accepted values: ${acceptedRepr}. ` + closing;
|
|
@@ -142,14 +147,18 @@ function buildClosing(sourceTask, currentTask) {
|
|
|
142
147
|
}
|
|
143
148
|
return `Check that the seller's catalogs agree across tools.`;
|
|
144
149
|
}
|
|
150
|
+
const HINT_VALUE_MAX_CHARS = 200;
|
|
151
|
+
function truncateForHint(rendered) {
|
|
152
|
+
return rendered.length <= HINT_VALUE_MAX_CHARS ? rendered : `${rendered.slice(0, HINT_VALUE_MAX_CHARS)}\u2026 (${rendered.length} chars)`;
|
|
153
|
+
}
|
|
145
154
|
function formatScalar(value) {
|
|
146
|
-
if (typeof value === "string") return value;
|
|
155
|
+
if (typeof value === "string") return truncateForHint(value);
|
|
147
156
|
if (value === null) return "null";
|
|
148
157
|
if (value === void 0) return "undefined";
|
|
149
158
|
try {
|
|
150
|
-
return JSON.stringify(value);
|
|
159
|
+
return truncateForHint(JSON.stringify(value) ?? String(value));
|
|
151
160
|
} catch {
|
|
152
|
-
return String(value);
|
|
161
|
+
return truncateForHint(String(value));
|
|
153
162
|
}
|
|
154
163
|
}
|
|
155
164
|
function resolveErrorsList(data) {
|
|
@@ -105,7 +105,12 @@ function formatMessage(input) {
|
|
|
105
105
|
const { contextKey, entry, rejectedValue, acceptedValues, requestField, currentTask } = input;
|
|
106
106
|
const valueRepr = formatScalar(rejectedValue);
|
|
107
107
|
const fieldRepr = requestField ? `\`${requestField}: ${valueRepr}\`` : `\`${valueRepr}\``;
|
|
108
|
-
const sourceDetail = entry.source_kind === "
|
|
108
|
+
const sourceDetail = entry.source_kind === "fixture_preparation" ? (
|
|
109
|
+
// Naming a convention extractor here would point an implementor at
|
|
110
|
+
// machinery that was never involved; the useful pointer is the
|
|
111
|
+
// fixture handle whose pinned entity produced the value.
|
|
112
|
+
`pinned by fixture preparation${entry.fixture_handle ? ` for handle \`${entry.fixture_handle}\`` : ""}`
|
|
113
|
+
) : entry.source_kind === "context_outputs" && entry.response_path ? `set by step \`${entry.source_step_id}\` from response path \`${entry.response_path}\`` : entry.source_task ? `set by step \`${entry.source_step_id}\` (convention extractor for \`${entry.source_task}\`)` : `set by step \`${entry.source_step_id}\``;
|
|
109
114
|
const acceptedRepr = `[${acceptedValues.map(formatScalar).join(", ")}]`;
|
|
110
115
|
const closing = buildClosing(entry.source_task, currentTask);
|
|
111
116
|
return `Rejected ${fieldRepr} was extracted from \`$context.${contextKey}\` (${sourceDetail}). Seller's accepted values: ${acceptedRepr}. ` + closing;
|
|
@@ -119,14 +124,18 @@ function buildClosing(sourceTask, currentTask) {
|
|
|
119
124
|
}
|
|
120
125
|
return `Check that the seller's catalogs agree across tools.`;
|
|
121
126
|
}
|
|
127
|
+
const HINT_VALUE_MAX_CHARS = 200;
|
|
128
|
+
function truncateForHint(rendered) {
|
|
129
|
+
return rendered.length <= HINT_VALUE_MAX_CHARS ? rendered : `${rendered.slice(0, HINT_VALUE_MAX_CHARS)}\u2026 (${rendered.length} chars)`;
|
|
130
|
+
}
|
|
122
131
|
function formatScalar(value) {
|
|
123
|
-
if (typeof value === "string") return value;
|
|
132
|
+
if (typeof value === "string") return truncateForHint(value);
|
|
124
133
|
if (value === null) return "null";
|
|
125
134
|
if (value === void 0) return "undefined";
|
|
126
135
|
try {
|
|
127
|
-
return JSON.stringify(value);
|
|
136
|
+
return truncateForHint(JSON.stringify(value) ?? String(value));
|
|
128
137
|
} catch {
|
|
129
|
-
return String(value);
|
|
138
|
+
return truncateForHint(String(value));
|
|
130
139
|
}
|
|
131
140
|
}
|
|
132
141
|
function resolveErrorsList(data) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - runStoryboard(): run all phases/steps sequentially
|
|
6
6
|
* - runStoryboardStep(): run a single step (stateless, LLM-friendly)
|
|
7
7
|
*/
|
|
8
|
+
import { PreparedStoryboardFixtures } from './prepared-fixtures.mjs';
|
|
8
9
|
import type { RequiresCapabilityPredicate, Storyboard, StoryboardStep, StoryboardContext, StoryboardRunOptions, StoryboardResult, StoryboardPhaseResult, StoryboardStepResult, StoryboardStepPreview, StrictValidationSummary, SchemaValidationError } from './types.mjs';
|
|
9
10
|
import type { TestStepResult } from '../types.mjs';
|
|
10
11
|
export declare function applyStoryboardVersionOptions(storyboard: Storyboard, options: StoryboardRunOptions): StoryboardRunOptions;
|
|
@@ -161,6 +162,29 @@ export declare function signingCoverageUnverified(steps: readonly SigningCoverag
|
|
|
161
162
|
* called from `runStoryboard` when discovery throws.
|
|
162
163
|
*/
|
|
163
164
|
export declare function buildDiscoveryFailedResult(agentUrls: string[], storyboard: Storyboard, discoveryStep: TestStepResult): StoryboardResult;
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a storyboard's fixture graph once and return reusable, immutable
|
|
167
|
+
* prepared state.
|
|
168
|
+
*
|
|
169
|
+
* This is the public form of what `executeStoryboardPass` already does
|
|
170
|
+
* internally, promoted so the standalone step runner and hosted step routes
|
|
171
|
+
* stop falling back to literal authored handles. The returned state carries
|
|
172
|
+
* the pinned seller identities, the declaration-local captures from the
|
|
173
|
+
* SAME selected graph, the retained collection witnesses, the resolution
|
|
174
|
+
* evidence, any coverage gaps, and the terminal preparation disposition.
|
|
175
|
+
*
|
|
176
|
+
* The state is deliberately process-local: its trust is a live object
|
|
177
|
+
* identity, and its credential fingerprint is keyed by a per-process random
|
|
178
|
+
* HMAC key. It will not survive serialization, and it is not meant to — a
|
|
179
|
+
* frozen JSON snapshot carries no evidence that its grant is still live.
|
|
180
|
+
* Hosted multi-process reuse belongs behind an authenticated reference
|
|
181
|
+
* lookup with the authorizer hook. See `docs/guides/COLLECTION-FIXTURE-FOUNDATION.md`.
|
|
182
|
+
*
|
|
183
|
+
* A failed or unsatisfied preparation is returned, not thrown: the terminal
|
|
184
|
+
* disposition is part of the evidence and must be carried forward verbatim
|
|
185
|
+
* so a retry cannot hide it behind a freshly discovered alternative.
|
|
186
|
+
*/
|
|
187
|
+
export declare function prepareStoryboardFixtures(agentUrl: string | string[], storyboard: Storyboard, options?: StoryboardRunOptions): Promise<PreparedStoryboardFixtures>;
|
|
164
188
|
/**
|
|
165
189
|
* Walk every response_schema validation and aggregate the strict/lenient
|
|
166
190
|
* delta. Always returns a summary; `observable: false` signals "run had
|