@granular-software/sdk 0.4.64 → 0.4.65
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/dist/agent-evals.d.mts +2 -2
- package/dist/agent-evals.d.ts +2 -2
- package/dist/agent-evals.js +679 -53
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +679 -53
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +679 -53
- package/dist/{client-IQeOoPnO.d.mts → client-S-z8bri1.d.mts} +47 -1
- package/dist/{client-BziqnDTl.d.ts → client-z6shQivt.d.ts} +47 -1
- package/dist/index.d.mts +72 -5
- package/dist/index.d.ts +72 -5
- package/dist/index.js +1024 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1018 -72
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-CDLmk-KW.d.mts → spend-CESUvrZC.d.mts} +227 -1
- package/dist/{spend-CDLmk-KW.d.ts → spend-CESUvrZC.d.ts} +227 -1
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6555,6 +6555,11 @@ var Session = class {
|
|
|
6555
6555
|
}
|
|
6556
6556
|
}
|
|
6557
6557
|
buildDirectedInvocationEffectContext(params, feedbackContext) {
|
|
6558
|
+
const commitUnavailable = async () => {
|
|
6559
|
+
throw new Error(
|
|
6560
|
+
"This directed browser tool invocation has no product commit transport"
|
|
6561
|
+
);
|
|
6562
|
+
};
|
|
6558
6563
|
return {
|
|
6559
6564
|
effectClientId: this.clientId,
|
|
6560
6565
|
sandboxId: params.sandboxId || "",
|
|
@@ -6567,6 +6572,10 @@ var Session = class {
|
|
|
6567
6572
|
userId: "",
|
|
6568
6573
|
subjectId: ""
|
|
6569
6574
|
},
|
|
6575
|
+
commit: {
|
|
6576
|
+
effect: commitUnavailable,
|
|
6577
|
+
transition: commitUnavailable
|
|
6578
|
+
},
|
|
6570
6579
|
...feedbackContext ? {
|
|
6571
6580
|
feedback: feedbackContext.feedback,
|
|
6572
6581
|
transientFeedback: feedbackContext.transientFeedback
|
|
@@ -12338,6 +12347,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
12338
12347
|
summary: external_exports.string().optional()
|
|
12339
12348
|
}).strict()
|
|
12340
12349
|
]);
|
|
12350
|
+
var StateTransitionOutcomeSchema = external_exports.object({
|
|
12351
|
+
label: external_exports.string().min(1).optional(),
|
|
12352
|
+
to: external_exports.string().min(1),
|
|
12353
|
+
primary: external_exports.boolean().optional(),
|
|
12354
|
+
disposition: external_exports.enum(["continue", "error"])
|
|
12355
|
+
}).strict();
|
|
12341
12356
|
var StateMachineTransitionSchema = external_exports.object({
|
|
12342
12357
|
name: external_exports.string().min(1),
|
|
12343
12358
|
from: external_exports.string().min(1),
|
|
@@ -12349,7 +12364,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12349
12364
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
12350
12365
|
permission: StateTransitionPermissionSchema.optional(),
|
|
12351
12366
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
12352
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
12367
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
12368
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
12353
12369
|
}).strict();
|
|
12354
12370
|
external_exports.object({
|
|
12355
12371
|
name: external_exports.string().min(1),
|
|
@@ -13030,6 +13046,289 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
13030
13046
|
}
|
|
13031
13047
|
});
|
|
13032
13048
|
|
|
13049
|
+
// src/commit.ts
|
|
13050
|
+
var MAX_PROJECTION_CHANGES = 1e3;
|
|
13051
|
+
var MAX_SAFE_SUMMARY_KEYS = 32;
|
|
13052
|
+
var MAX_SAFE_TEXT_LENGTH = 2e3;
|
|
13053
|
+
var definedTransitionMetadata = /* @__PURE__ */ new WeakMap();
|
|
13054
|
+
function getDefinedTransitionMetadata(transition) {
|
|
13055
|
+
return definedTransitionMetadata.get(transition);
|
|
13056
|
+
}
|
|
13057
|
+
function defineProjection(mapper) {
|
|
13058
|
+
return mapper;
|
|
13059
|
+
}
|
|
13060
|
+
function defineEffect(effect) {
|
|
13061
|
+
return effect;
|
|
13062
|
+
}
|
|
13063
|
+
function defineStateMachine(definition) {
|
|
13064
|
+
const stateNames = new Set(Object.keys(definition.states));
|
|
13065
|
+
for (const [transitionName, transition] of Object.entries(
|
|
13066
|
+
definition.transitions
|
|
13067
|
+
)) {
|
|
13068
|
+
if (!stateNames.has(transition.from)) {
|
|
13069
|
+
throw new Error(
|
|
13070
|
+
`Transition ${transitionName} starts at undeclared state ${transition.from}`
|
|
13071
|
+
);
|
|
13072
|
+
}
|
|
13073
|
+
if (transition.effect.commit?.kind !== "transition") {
|
|
13074
|
+
throw new Error(
|
|
13075
|
+
`Transition ${transitionName} must use a transition-commit effect`
|
|
13076
|
+
);
|
|
13077
|
+
}
|
|
13078
|
+
const outcomes = Object.entries(transition.outcomes);
|
|
13079
|
+
const primary = outcomes.filter(([, outcome]) => outcome.primary === true);
|
|
13080
|
+
if (primary.length !== 1) {
|
|
13081
|
+
throw new Error(
|
|
13082
|
+
`Transition ${transitionName} must declare exactly one primary outcome`
|
|
13083
|
+
);
|
|
13084
|
+
}
|
|
13085
|
+
for (const [outcomeKey, outcome] of outcomes) {
|
|
13086
|
+
if (outcome.to !== "$current" && !stateNames.has(outcome.to)) {
|
|
13087
|
+
throw new Error(
|
|
13088
|
+
`Transition ${transitionName} outcome ${outcomeKey} targets undeclared state ${outcome.to}`
|
|
13089
|
+
);
|
|
13090
|
+
}
|
|
13091
|
+
if (outcome.to === "$current" && outcome.disposition !== "error") {
|
|
13092
|
+
throw new Error(
|
|
13093
|
+
`Transition ${transitionName} outcome ${outcomeKey} may use $current only with error disposition`
|
|
13094
|
+
);
|
|
13095
|
+
}
|
|
13096
|
+
}
|
|
13097
|
+
}
|
|
13098
|
+
for (const [transitionName, transition] of Object.entries(
|
|
13099
|
+
definition.transitions
|
|
13100
|
+
)) {
|
|
13101
|
+
definedTransitionMetadata.set(transition, {
|
|
13102
|
+
machine: definition.name,
|
|
13103
|
+
transition: transitionName
|
|
13104
|
+
});
|
|
13105
|
+
}
|
|
13106
|
+
return definition;
|
|
13107
|
+
}
|
|
13108
|
+
function requireNonEmptyString(value, path) {
|
|
13109
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
13110
|
+
throw new Error(`${path} must be a non-empty string`);
|
|
13111
|
+
}
|
|
13112
|
+
return value.trim();
|
|
13113
|
+
}
|
|
13114
|
+
function validateObjectReference(value, path) {
|
|
13115
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13116
|
+
throw new Error(`${path} must be an object reference`);
|
|
13117
|
+
}
|
|
13118
|
+
const reference = value;
|
|
13119
|
+
requireNonEmptyString(reference.className, `${path}.className`);
|
|
13120
|
+
requireNonEmptyString(reference.id, `${path}.id`);
|
|
13121
|
+
if (reference.path !== void 0) {
|
|
13122
|
+
requireNonEmptyString(reference.path, `${path}.path`);
|
|
13123
|
+
}
|
|
13124
|
+
}
|
|
13125
|
+
function validateScalarRecord(value, path) {
|
|
13126
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
13127
|
+
throw new Error(`${path} must be an object of scalar values`);
|
|
13128
|
+
}
|
|
13129
|
+
for (const [key, item] of Object.entries(value)) {
|
|
13130
|
+
if (item !== null && typeof item !== "string" && typeof item !== "number" && typeof item !== "boolean") {
|
|
13131
|
+
throw new Error(
|
|
13132
|
+
`${path}.${key} must be a string, finite number, boolean, or null`
|
|
13133
|
+
);
|
|
13134
|
+
}
|
|
13135
|
+
if (typeof item === "number" && !Number.isFinite(item)) {
|
|
13136
|
+
throw new Error(`${path}.${key} must be finite`);
|
|
13137
|
+
}
|
|
13138
|
+
}
|
|
13139
|
+
}
|
|
13140
|
+
function validateProjectedRecord(value, path) {
|
|
13141
|
+
validateObjectReference(value, path);
|
|
13142
|
+
const record = value;
|
|
13143
|
+
if (record.label !== void 0) {
|
|
13144
|
+
if (typeof record.label !== "string") {
|
|
13145
|
+
throw new Error(`${path}.label must be a string`);
|
|
13146
|
+
}
|
|
13147
|
+
if (record.label.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13148
|
+
throw new Error(
|
|
13149
|
+
`${path}.label exceeds ${MAX_SAFE_TEXT_LENGTH} characters`
|
|
13150
|
+
);
|
|
13151
|
+
}
|
|
13152
|
+
}
|
|
13153
|
+
validateScalarRecord(record.fields, `${path}.fields`);
|
|
13154
|
+
}
|
|
13155
|
+
function validateBoundedJson(value, path, depth = 0) {
|
|
13156
|
+
if (depth > 12) throw new Error(`${path} is nested too deeply`);
|
|
13157
|
+
if (value === null || typeof value === "boolean") return;
|
|
13158
|
+
if (typeof value === "number") {
|
|
13159
|
+
if (!Number.isFinite(value)) throw new Error(`${path} must be finite`);
|
|
13160
|
+
return;
|
|
13161
|
+
}
|
|
13162
|
+
if (typeof value === "string") {
|
|
13163
|
+
if (value.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13164
|
+
throw new Error(`${path} exceeds ${MAX_SAFE_TEXT_LENGTH} characters`);
|
|
13165
|
+
}
|
|
13166
|
+
return;
|
|
13167
|
+
}
|
|
13168
|
+
if (Array.isArray(value)) {
|
|
13169
|
+
if (value.length > MAX_PROJECTION_CHANGES) {
|
|
13170
|
+
throw new Error(`${path} contains too many values`);
|
|
13171
|
+
}
|
|
13172
|
+
value.forEach(
|
|
13173
|
+
(item, index) => validateBoundedJson(item, `${path}[${index}]`, depth + 1)
|
|
13174
|
+
);
|
|
13175
|
+
return;
|
|
13176
|
+
}
|
|
13177
|
+
if (!value || typeof value !== "object") {
|
|
13178
|
+
throw new Error(`${path} contains an unsupported value`);
|
|
13179
|
+
}
|
|
13180
|
+
const entries = Object.entries(value);
|
|
13181
|
+
if (entries.length > 256) throw new Error(`${path} contains too many keys`);
|
|
13182
|
+
for (const [key, item] of entries) {
|
|
13183
|
+
if (/token|secret|password|authorization|cookie/i.test(key)) {
|
|
13184
|
+
throw new Error(`${path}.${key} is not allowed in a commit projection`);
|
|
13185
|
+
}
|
|
13186
|
+
validateBoundedJson(item, `${path}.${key}`, depth + 1);
|
|
13187
|
+
}
|
|
13188
|
+
}
|
|
13189
|
+
function validateProjectionResult(declaration, projection) {
|
|
13190
|
+
if (!projection || typeof projection !== "object") {
|
|
13191
|
+
throw new Error("Projection mapper must return an object");
|
|
13192
|
+
}
|
|
13193
|
+
requireNonEmptyString(
|
|
13194
|
+
projection.source?.reference,
|
|
13195
|
+
"projection.source.reference"
|
|
13196
|
+
);
|
|
13197
|
+
if (projection.source.version !== void 0) {
|
|
13198
|
+
requireNonEmptyString(
|
|
13199
|
+
projection.source.version,
|
|
13200
|
+
"projection.source.version"
|
|
13201
|
+
);
|
|
13202
|
+
}
|
|
13203
|
+
if (!Array.isArray(projection.changes)) {
|
|
13204
|
+
throw new Error("projection.changes must be an array");
|
|
13205
|
+
}
|
|
13206
|
+
if (projection.changes.length > MAX_PROJECTION_CHANGES) {
|
|
13207
|
+
throw new Error(
|
|
13208
|
+
`projection.changes exceeds the ${MAX_PROJECTION_CHANGES} change limit`
|
|
13209
|
+
);
|
|
13210
|
+
}
|
|
13211
|
+
if (projection.primaryTarget) {
|
|
13212
|
+
validateObjectReference(
|
|
13213
|
+
projection.primaryTarget,
|
|
13214
|
+
"projection.primaryTarget"
|
|
13215
|
+
);
|
|
13216
|
+
}
|
|
13217
|
+
if (projection.safeSummary) {
|
|
13218
|
+
const entries = Object.entries(projection.safeSummary);
|
|
13219
|
+
if (entries.length > MAX_SAFE_SUMMARY_KEYS) {
|
|
13220
|
+
throw new Error(
|
|
13221
|
+
`projection.safeSummary exceeds the ${MAX_SAFE_SUMMARY_KEYS} key limit`
|
|
13222
|
+
);
|
|
13223
|
+
}
|
|
13224
|
+
validateBoundedJson(projection.safeSummary, "projection.safeSummary");
|
|
13225
|
+
validateScalarRecord(projection.safeSummary, "projection.safeSummary");
|
|
13226
|
+
}
|
|
13227
|
+
projection.changes.forEach((change, index) => {
|
|
13228
|
+
const changePath = `projection.changes[${index}]`;
|
|
13229
|
+
validateBoundedJson(change, changePath);
|
|
13230
|
+
if (!change || typeof change !== "object" || Array.isArray(change)) {
|
|
13231
|
+
throw new Error(`${changePath} must be an object`);
|
|
13232
|
+
}
|
|
13233
|
+
const rawChange = change;
|
|
13234
|
+
const kind = requireNonEmptyString(rawChange.kind, `${changePath}.kind`);
|
|
13235
|
+
if (kind === "object") {
|
|
13236
|
+
const operation = requireNonEmptyString(
|
|
13237
|
+
rawChange.operation,
|
|
13238
|
+
`${changePath}.operation`
|
|
13239
|
+
);
|
|
13240
|
+
if (operation !== "created" && operation !== "updated" && operation !== "deleted") {
|
|
13241
|
+
throw new Error(
|
|
13242
|
+
`${changePath}.operation must be created, updated, or deleted`
|
|
13243
|
+
);
|
|
13244
|
+
}
|
|
13245
|
+
if (operation === "deleted") {
|
|
13246
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13247
|
+
} else {
|
|
13248
|
+
validateProjectedRecord(rawChange.record, `${changePath}.record`);
|
|
13249
|
+
}
|
|
13250
|
+
} else if (kind === "relationship") {
|
|
13251
|
+
const operation = requireNonEmptyString(
|
|
13252
|
+
rawChange.operation,
|
|
13253
|
+
`${changePath}.operation`
|
|
13254
|
+
);
|
|
13255
|
+
if (operation !== "connected" && operation !== "disconnected") {
|
|
13256
|
+
throw new Error(
|
|
13257
|
+
`${changePath}.operation must be connected or disconnected`
|
|
13258
|
+
);
|
|
13259
|
+
}
|
|
13260
|
+
requireNonEmptyString(
|
|
13261
|
+
rawChange.relationship,
|
|
13262
|
+
`${changePath}.relationship`
|
|
13263
|
+
);
|
|
13264
|
+
validateObjectReference(rawChange.from, `${changePath}.from`);
|
|
13265
|
+
validateObjectReference(rawChange.to, `${changePath}.to`);
|
|
13266
|
+
} else if (kind === "state_observation") {
|
|
13267
|
+
if (rawChange.operation !== void 0) {
|
|
13268
|
+
throw new Error(
|
|
13269
|
+
`${changePath}.operation is not valid for an observation`
|
|
13270
|
+
);
|
|
13271
|
+
}
|
|
13272
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13273
|
+
requireNonEmptyString(rawChange.machine, `${changePath}.machine`);
|
|
13274
|
+
requireNonEmptyString(rawChange.state, `${changePath}.state`);
|
|
13275
|
+
} else {
|
|
13276
|
+
throw new Error(`${changePath}.kind is unsupported: ${kind}`);
|
|
13277
|
+
}
|
|
13278
|
+
});
|
|
13279
|
+
const objectOperations = /* @__PURE__ */ new Map();
|
|
13280
|
+
for (const change of projection.changes) {
|
|
13281
|
+
if (change.kind !== "object") continue;
|
|
13282
|
+
const reference = change.operation === "deleted" ? change.target : change.record;
|
|
13283
|
+
const key = `${reference.className}\0${reference.id}`;
|
|
13284
|
+
const operations = objectOperations.get(key) || {
|
|
13285
|
+
deleted: false,
|
|
13286
|
+
upserted: false
|
|
13287
|
+
};
|
|
13288
|
+
if (change.operation === "deleted") operations.deleted = true;
|
|
13289
|
+
else operations.upserted = true;
|
|
13290
|
+
if (operations.deleted && operations.upserted) {
|
|
13291
|
+
throw new Error(
|
|
13292
|
+
`projection.changes cannot both delete and upsert ${reference.className}/${reference.id}; return only its canonical final state`
|
|
13293
|
+
);
|
|
13294
|
+
}
|
|
13295
|
+
objectOperations.set(key, operations);
|
|
13296
|
+
}
|
|
13297
|
+
const outcome = projection.outcome;
|
|
13298
|
+
if (declaration.kind === "transition") {
|
|
13299
|
+
if (!outcome) {
|
|
13300
|
+
throw new Error("A transition projection must return an outcome");
|
|
13301
|
+
}
|
|
13302
|
+
requireNonEmptyString(outcome.key, "projection.outcome.key");
|
|
13303
|
+
if (outcome.error) {
|
|
13304
|
+
validateBoundedJson(outcome.error, "projection.outcome.error");
|
|
13305
|
+
}
|
|
13306
|
+
} else if (projection.outcome !== void 0) {
|
|
13307
|
+
throw new Error("An effect projection cannot declare a transition outcome");
|
|
13308
|
+
}
|
|
13309
|
+
}
|
|
13310
|
+
function canonicalizeCommitValue(value) {
|
|
13311
|
+
const normalize = (current) => {
|
|
13312
|
+
if (current === null || typeof current === "boolean" || typeof current === "string") {
|
|
13313
|
+
return typeof current === "string" ? current.normalize("NFC") : current;
|
|
13314
|
+
}
|
|
13315
|
+
if (typeof current === "number") {
|
|
13316
|
+
if (!Number.isFinite(current)) {
|
|
13317
|
+
throw new Error("Cannot canonicalize a non-finite number");
|
|
13318
|
+
}
|
|
13319
|
+
return Object.is(current, -0) ? 0 : current;
|
|
13320
|
+
}
|
|
13321
|
+
if (Array.isArray(current)) return current.map(normalize);
|
|
13322
|
+
if (current && typeof current === "object") {
|
|
13323
|
+
return Object.fromEntries(
|
|
13324
|
+
Object.entries(current).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key.normalize("NFC"), normalize(item)])
|
|
13325
|
+
);
|
|
13326
|
+
}
|
|
13327
|
+
throw new Error(`Cannot canonicalize ${typeof current}`);
|
|
13328
|
+
};
|
|
13329
|
+
return JSON.stringify(normalize(value));
|
|
13330
|
+
}
|
|
13331
|
+
|
|
13033
13332
|
// src/effect-runtime.ts
|
|
13034
13333
|
function computeEffectKey(effect) {
|
|
13035
13334
|
const attachedClass = effect.className?.trim();
|
|
@@ -13094,6 +13393,45 @@ function resolveInvocationMode(context) {
|
|
|
13094
13393
|
}
|
|
13095
13394
|
return "execute";
|
|
13096
13395
|
}
|
|
13396
|
+
async function sha256Hex(value) {
|
|
13397
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
13398
|
+
"SHA-256",
|
|
13399
|
+
new TextEncoder().encode(value)
|
|
13400
|
+
);
|
|
13401
|
+
return Array.from(
|
|
13402
|
+
new Uint8Array(digest),
|
|
13403
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
13404
|
+
).join("");
|
|
13405
|
+
}
|
|
13406
|
+
async function resolveInvocationIdempotencyKey(request) {
|
|
13407
|
+
const supplied = request.context?.idempotencyKey?.trim();
|
|
13408
|
+
if (supplied) return supplied;
|
|
13409
|
+
const invocationId = request.context?.invocationId?.trim();
|
|
13410
|
+
if (!invocationId) {
|
|
13411
|
+
throw new Error(
|
|
13412
|
+
`Committed effect ${request.effectKey} requires an invocation id`
|
|
13413
|
+
);
|
|
13414
|
+
}
|
|
13415
|
+
const digest = await sha256Hex(
|
|
13416
|
+
canonicalizeCommitValue({
|
|
13417
|
+
sandboxId: request.context?.sandboxId || "",
|
|
13418
|
+
environmentId: request.context?.environmentId || "",
|
|
13419
|
+
effectKey: request.effectKey,
|
|
13420
|
+
invocationId,
|
|
13421
|
+
input: request.input
|
|
13422
|
+
})
|
|
13423
|
+
);
|
|
13424
|
+
return `gci_${digest}`;
|
|
13425
|
+
}
|
|
13426
|
+
function createUnavailableCommitContext(message) {
|
|
13427
|
+
const unavailable = async () => {
|
|
13428
|
+
throw new Error(message);
|
|
13429
|
+
};
|
|
13430
|
+
return {
|
|
13431
|
+
effect: unavailable,
|
|
13432
|
+
transition: unavailable
|
|
13433
|
+
};
|
|
13434
|
+
}
|
|
13097
13435
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
13098
13436
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
13099
13437
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -13165,22 +13503,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13165
13503
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
13166
13504
|
}
|
|
13167
13505
|
if (mode === "reverse") {
|
|
13506
|
+
const sourceEffectKey = request.context?.invocation?.sourceEffectKey?.trim();
|
|
13507
|
+
if (sourceEffectKey && sourceEffectKey !== request.effectKey) {
|
|
13508
|
+
return { effect, mode, handler: effect.handler };
|
|
13509
|
+
}
|
|
13510
|
+
const namedReverseHandler = request.context?.invocation?.reverseHandler?.trim() || behaviors.reverse?.handler?.trim();
|
|
13511
|
+
if (namedReverseHandler) {
|
|
13512
|
+
const reverseEffect = resolveReverseEffect(
|
|
13513
|
+
effectMap,
|
|
13514
|
+
effect,
|
|
13515
|
+
request,
|
|
13516
|
+
behaviors
|
|
13517
|
+
);
|
|
13518
|
+
if (reverseEffect) {
|
|
13519
|
+
return {
|
|
13520
|
+
effect: reverseEffect,
|
|
13521
|
+
mode,
|
|
13522
|
+
handler: reverseEffect.handler
|
|
13523
|
+
};
|
|
13524
|
+
}
|
|
13525
|
+
throw new Error(
|
|
13526
|
+
`Reverse effect ${namedReverseHandler} is not registered for ${request.effectKey}`
|
|
13527
|
+
);
|
|
13528
|
+
}
|
|
13168
13529
|
if (effect.reverseHandler) {
|
|
13169
13530
|
return { effect, mode, handler: effect.reverseHandler };
|
|
13170
13531
|
}
|
|
13171
|
-
const reverseEffect = resolveReverseEffect(
|
|
13172
|
-
effectMap,
|
|
13173
|
-
effect,
|
|
13174
|
-
request,
|
|
13175
|
-
behaviors
|
|
13176
|
-
);
|
|
13177
|
-
if (reverseEffect) {
|
|
13178
|
-
return {
|
|
13179
|
-
effect: reverseEffect,
|
|
13180
|
-
mode,
|
|
13181
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
13182
|
-
};
|
|
13183
|
-
}
|
|
13184
13532
|
throw new Error(
|
|
13185
13533
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
13186
13534
|
);
|
|
@@ -13281,18 +13629,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13281
13629
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
13282
13630
|
}
|
|
13283
13631
|
const resolved = resolveHandlerForMode(effectMap, effect, request);
|
|
13632
|
+
const isPreResolvedNamedReverse = resolved.mode === "reverse" && Boolean(request.context?.invocation?.sourceEffectKey?.trim()) && request.context?.invocation?.sourceEffectKey?.trim() !== request.effectKey;
|
|
13633
|
+
const resolvedEffectKey = computeEffectKey(resolved.effect);
|
|
13634
|
+
if (resolved.mode === "reverse" && resolved.effect !== effect && (effect.commit || resolved.effect.commit)) {
|
|
13635
|
+
throw new Error(
|
|
13636
|
+
`Committed named reverse effect ${resolvedEffectKey} must be reserved and dispatched using its own effect key; customer code was not invoked`
|
|
13637
|
+
);
|
|
13638
|
+
}
|
|
13639
|
+
const declaration = resolved.effect.commit;
|
|
13640
|
+
const commitTransport = options.commit;
|
|
13641
|
+
const commitRequired = Boolean(declaration) && (resolved.mode === "execute" || resolved.mode === "reverse");
|
|
13642
|
+
if (commitRequired && !commitTransport) {
|
|
13643
|
+
throw new Error(
|
|
13644
|
+
`Committed effect ${request.effectKey} has no durable commit transport; customer code was not invoked`
|
|
13645
|
+
);
|
|
13646
|
+
}
|
|
13647
|
+
if (commitRequired && declaration?.kind === "transition" && !request.context?.invocation?.transition) {
|
|
13648
|
+
throw new Error(
|
|
13649
|
+
`Transition effect ${request.effectKey} has no resolved transition context; customer code was not invoked`
|
|
13650
|
+
);
|
|
13651
|
+
}
|
|
13652
|
+
const idempotencyKey = commitRequired ? await resolveInvocationIdempotencyKey(request) : request.context?.idempotencyKey;
|
|
13653
|
+
let commitStarted = false;
|
|
13654
|
+
let commitPromise = null;
|
|
13655
|
+
const beginCommit = (requestedKind, productResult) => {
|
|
13656
|
+
if (!declaration || !commitRequired || !commitTransport) {
|
|
13657
|
+
return Promise.reject(
|
|
13658
|
+
new Error(
|
|
13659
|
+
`Effect ${request.effectKey} does not declare an active ${requestedKind} commit`
|
|
13660
|
+
)
|
|
13661
|
+
);
|
|
13662
|
+
}
|
|
13663
|
+
if (declaration.kind !== requestedKind) {
|
|
13664
|
+
return Promise.reject(
|
|
13665
|
+
new Error(
|
|
13666
|
+
`Effect ${request.effectKey} declares ${declaration.kind} commit, not ${requestedKind}`
|
|
13667
|
+
)
|
|
13668
|
+
);
|
|
13669
|
+
}
|
|
13670
|
+
if (commitStarted) {
|
|
13671
|
+
return Promise.reject(
|
|
13672
|
+
new Error(
|
|
13673
|
+
`Effect invocation ${request.context?.invocationId || request.effectKey} already emitted its commit`
|
|
13674
|
+
)
|
|
13675
|
+
);
|
|
13676
|
+
}
|
|
13677
|
+
commitStarted = true;
|
|
13678
|
+
commitPromise = (async () => {
|
|
13679
|
+
let projection;
|
|
13680
|
+
try {
|
|
13681
|
+
projection = declaration.project(productResult);
|
|
13682
|
+
validateProjectionResult(declaration, projection);
|
|
13683
|
+
} catch (error) {
|
|
13684
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13685
|
+
if (commitTransport.mappingFailed) {
|
|
13686
|
+
await commitTransport.mappingFailed({
|
|
13687
|
+
effectKey: request.effectKey,
|
|
13688
|
+
effectName: request.effectName,
|
|
13689
|
+
invocationId: request.context?.invocationId || "",
|
|
13690
|
+
idempotencyKey: idempotencyKey || "",
|
|
13691
|
+
environmentId: request.context?.environmentId || "",
|
|
13692
|
+
message
|
|
13693
|
+
});
|
|
13694
|
+
}
|
|
13695
|
+
throw new Error(
|
|
13696
|
+
`Product mutation may have succeeded, but its commit projection is invalid: ${message}`
|
|
13697
|
+
);
|
|
13698
|
+
}
|
|
13699
|
+
if (requestedKind === "transition") {
|
|
13700
|
+
const transition = request.context?.invocation?.transition;
|
|
13701
|
+
const outcome = projection.outcome;
|
|
13702
|
+
if (!transition || !outcome?.key) {
|
|
13703
|
+
throw new Error(
|
|
13704
|
+
`Transition effect ${request.effectKey} did not resolve an authored outcome`
|
|
13705
|
+
);
|
|
13706
|
+
}
|
|
13707
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
13708
|
+
transition.outcomes,
|
|
13709
|
+
outcome.key
|
|
13710
|
+
)) {
|
|
13711
|
+
throw new Error(
|
|
13712
|
+
`Transition ${transition.machine}.${transition.transition} does not declare outcome ${outcome.key}`
|
|
13713
|
+
);
|
|
13714
|
+
}
|
|
13715
|
+
}
|
|
13716
|
+
const invocationId = request.context?.invocationId || "";
|
|
13717
|
+
const environmentId = request.context?.environmentId || "";
|
|
13718
|
+
const sandboxId = request.context?.sandboxId || "";
|
|
13719
|
+
if (!invocationId || !environmentId || !sandboxId || !idempotencyKey) {
|
|
13720
|
+
throw new Error(
|
|
13721
|
+
`Committed effect ${request.effectKey} is missing its trusted invocation scope`
|
|
13722
|
+
);
|
|
13723
|
+
}
|
|
13724
|
+
const commitRequest = {
|
|
13725
|
+
kind: requestedKind,
|
|
13726
|
+
effectKey: request.effectKey,
|
|
13727
|
+
effectName: request.effectName,
|
|
13728
|
+
operationLabel: resolved.effect.label || resolved.effect.name,
|
|
13729
|
+
invocationId,
|
|
13730
|
+
idempotencyKey,
|
|
13731
|
+
sandboxId,
|
|
13732
|
+
environmentId,
|
|
13733
|
+
...request.context?.sessionId ? { sessionId: request.context.sessionId } : {},
|
|
13734
|
+
...request.context?.jobId ? { jobId: request.context.jobId } : {},
|
|
13735
|
+
...request.context?.buildId ? { buildId: request.context.buildId } : {},
|
|
13736
|
+
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13737
|
+
projection,
|
|
13738
|
+
...requestedKind === "transition" && request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13739
|
+
};
|
|
13740
|
+
const receipt = await commitTransport.persist(commitRequest);
|
|
13741
|
+
await options.commitAcknowledged?.(receipt);
|
|
13742
|
+
return receipt;
|
|
13743
|
+
})();
|
|
13744
|
+
return commitPromise;
|
|
13745
|
+
};
|
|
13746
|
+
const commitContext = commitRequired ? {
|
|
13747
|
+
effect: (productResult) => beginCommit("effect", productResult),
|
|
13748
|
+
transition: (productResult) => beginCommit("transition", productResult)
|
|
13749
|
+
} : createUnavailableCommitContext(
|
|
13750
|
+
`Effect ${request.effectKey} is not executing a declared product commit`
|
|
13751
|
+
);
|
|
13284
13752
|
const context = {
|
|
13285
13753
|
...request.context || {},
|
|
13754
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
13755
|
+
commit: commitContext,
|
|
13286
13756
|
behaviors: normalizeEffectBehaviors(
|
|
13287
13757
|
request.context?.behaviors || effect.metamodels || void 0
|
|
13288
13758
|
),
|
|
13289
13759
|
invocation: {
|
|
13290
13760
|
mode: resolved.mode,
|
|
13291
|
-
|
|
13292
|
-
|
|
13761
|
+
...commitRequired && declaration ? { commitKind: declaration.kind } : {},
|
|
13762
|
+
sourceEffectKey: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectKey || request.effectKey : request.effectKey,
|
|
13763
|
+
sourceEffectName: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectName || request.effectName : request.effectName,
|
|
13293
13764
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
13294
13765
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13295
|
-
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {}
|
|
13766
|
+
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {},
|
|
13767
|
+
...request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13296
13768
|
}
|
|
13297
13769
|
};
|
|
13298
13770
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -13316,6 +13788,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13316
13788
|
handlerFailed = true;
|
|
13317
13789
|
handlerError = error;
|
|
13318
13790
|
}
|
|
13791
|
+
let commitError;
|
|
13792
|
+
let commitFailed = false;
|
|
13793
|
+
if (commitPromise) {
|
|
13794
|
+
try {
|
|
13795
|
+
await commitPromise;
|
|
13796
|
+
} catch (error) {
|
|
13797
|
+
commitFailed = true;
|
|
13798
|
+
commitError = error;
|
|
13799
|
+
}
|
|
13800
|
+
}
|
|
13319
13801
|
let feedbackError;
|
|
13320
13802
|
let feedbackFailed = false;
|
|
13321
13803
|
if (feedbackContext) {
|
|
@@ -13329,9 +13811,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13329
13811
|
if (handlerFailed) {
|
|
13330
13812
|
throw handlerError;
|
|
13331
13813
|
}
|
|
13814
|
+
if (commitFailed) {
|
|
13815
|
+
throw commitError;
|
|
13816
|
+
}
|
|
13332
13817
|
if (feedbackFailed) {
|
|
13333
13818
|
throw feedbackError;
|
|
13334
13819
|
}
|
|
13820
|
+
if (commitRequired && !commitStarted) {
|
|
13821
|
+
throw new Error(
|
|
13822
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
13823
|
+
);
|
|
13824
|
+
}
|
|
13335
13825
|
return handlerResult;
|
|
13336
13826
|
}
|
|
13337
13827
|
|
|
@@ -13400,12 +13890,7 @@ function toRecordSearchResult(className, node) {
|
|
|
13400
13890
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
13401
13891
|
);
|
|
13402
13892
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
13403
|
-
const
|
|
13404
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path)) {
|
|
13405
|
-
return null;
|
|
13406
|
-
}
|
|
13407
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
13408
|
-
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
13893
|
+
const label = typeof node.label === "string" && node.label.trim() ? node.label.trim() : path || id;
|
|
13409
13894
|
return {
|
|
13410
13895
|
path,
|
|
13411
13896
|
className,
|
|
@@ -13415,30 +13900,6 @@ function toRecordSearchResult(className, node) {
|
|
|
13415
13900
|
fields
|
|
13416
13901
|
};
|
|
13417
13902
|
}
|
|
13418
|
-
function isPlaceholderRecordLabel(label, id, path) {
|
|
13419
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
13420
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path);
|
|
13421
|
-
}
|
|
13422
|
-
function displayLabelFromFields(fields) {
|
|
13423
|
-
const preferredFieldNames = [
|
|
13424
|
-
"name",
|
|
13425
|
-
"title",
|
|
13426
|
-
"label",
|
|
13427
|
-
"display_name",
|
|
13428
|
-
"file_name",
|
|
13429
|
-
"number",
|
|
13430
|
-
"code"
|
|
13431
|
-
];
|
|
13432
|
-
for (const preferred of preferredFieldNames) {
|
|
13433
|
-
const match = fields.find(
|
|
13434
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
13435
|
-
);
|
|
13436
|
-
if (typeof match?.value === "string") {
|
|
13437
|
-
return match.value.trim();
|
|
13438
|
-
}
|
|
13439
|
-
}
|
|
13440
|
-
return null;
|
|
13441
|
-
}
|
|
13442
13903
|
function normalizeRecordSearchText(value) {
|
|
13443
13904
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
13444
13905
|
}
|
|
@@ -14401,7 +14862,8 @@ function normalizeStateMachines(values) {
|
|
|
14401
14862
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
14402
14863
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
14403
14864
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
14404
|
-
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
14865
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json),
|
|
14866
|
+
outcomes: parseJsonRecord(transition?.outcomes) || parseJsonRecord(transition?.outcomes_json)
|
|
14405
14867
|
})).filter(
|
|
14406
14868
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
14407
14869
|
);
|
|
@@ -14494,6 +14956,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14494
14956
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
14495
14957
|
);
|
|
14496
14958
|
}
|
|
14959
|
+
if (transition.outcomes) {
|
|
14960
|
+
args.push(
|
|
14961
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
14962
|
+
);
|
|
14963
|
+
}
|
|
14497
14964
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
14498
14965
|
}
|
|
14499
14966
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -14556,7 +15023,7 @@ function buildMachineTypes(classSummary, machine) {
|
|
|
14556
15023
|
];
|
|
14557
15024
|
}
|
|
14558
15025
|
function buildMachineMethods(classSummary, machine) {
|
|
14559
|
-
const
|
|
15026
|
+
const stateName2 = stateTypeName(classSummary.name, machine.name);
|
|
14560
15027
|
const transitionName = transitionTypeName(classSummary.name, machine.name);
|
|
14561
15028
|
pathTypeName(classSummary.name, machine.name);
|
|
14562
15029
|
const docsPrefix = `${classSummary.name}.${machine.name}`;
|
|
@@ -14566,12 +15033,12 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14566
15033
|
docs: [`Get the current ${docsPrefix} state.`],
|
|
14567
15034
|
static: false,
|
|
14568
15035
|
params: [],
|
|
14569
|
-
returnType: `Promise<${
|
|
15036
|
+
returnType: `Promise<${stateName2} | null>`,
|
|
14570
15037
|
runtime: {
|
|
14571
15038
|
kind: "state_machine",
|
|
14572
15039
|
machineName: machine.name,
|
|
14573
15040
|
className: classSummary.name,
|
|
14574
|
-
stateTypeName:
|
|
15041
|
+
stateTypeName: stateName2,
|
|
14575
15042
|
transitionTypeName: transitionName,
|
|
14576
15043
|
operation: "get_current"
|
|
14577
15044
|
}
|
|
@@ -14582,13 +15049,13 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14582
15049
|
`Reach a ${docsPrefix} state through the shortest allowed transition path.`
|
|
14583
15050
|
],
|
|
14584
15051
|
static: false,
|
|
14585
|
-
params: [{ name: "target", type:
|
|
15052
|
+
params: [{ name: "target", type: stateName2 }],
|
|
14586
15053
|
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
14587
15054
|
runtime: {
|
|
14588
15055
|
kind: "state_machine",
|
|
14589
15056
|
machineName: machine.name,
|
|
14590
15057
|
className: classSummary.name,
|
|
14591
|
-
stateTypeName:
|
|
15058
|
+
stateTypeName: stateName2,
|
|
14592
15059
|
transitionTypeName: transitionName,
|
|
14593
15060
|
operation: "reach"
|
|
14594
15061
|
}
|
|
@@ -14603,7 +15070,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14603
15070
|
kind: "state_machine",
|
|
14604
15071
|
machineName: machine.name,
|
|
14605
15072
|
className: classSummary.name,
|
|
14606
|
-
stateTypeName:
|
|
15073
|
+
stateTypeName: stateName2,
|
|
14607
15074
|
transitionTypeName: transitionName,
|
|
14608
15075
|
operation: "list_transitions"
|
|
14609
15076
|
}
|
|
@@ -14613,12 +15080,12 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14613
15080
|
docs: [`List reachable states for ${docsPrefix} from the current state.`],
|
|
14614
15081
|
static: false,
|
|
14615
15082
|
params: [],
|
|
14616
|
-
returnType: `Promise<${
|
|
15083
|
+
returnType: `Promise<${stateName2}[]>`,
|
|
14617
15084
|
runtime: {
|
|
14618
15085
|
kind: "state_machine",
|
|
14619
15086
|
machineName: machine.name,
|
|
14620
15087
|
className: classSummary.name,
|
|
14621
|
-
stateTypeName:
|
|
15088
|
+
stateTypeName: stateName2,
|
|
14622
15089
|
transitionTypeName: transitionName,
|
|
14623
15090
|
operation: "list_reachable_states"
|
|
14624
15091
|
}
|
|
@@ -14633,7 +15100,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14633
15100
|
kind: "state_machine",
|
|
14634
15101
|
machineName: machine.name,
|
|
14635
15102
|
className: classSummary.name,
|
|
14636
|
-
stateTypeName:
|
|
15103
|
+
stateTypeName: stateName2,
|
|
14637
15104
|
transitionTypeName: transitionName,
|
|
14638
15105
|
operation: "is_final"
|
|
14639
15106
|
}
|
|
@@ -14644,13 +15111,13 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14644
15111
|
`List shortest transition paths from the current ${docsPrefix} state to a target state.`
|
|
14645
15112
|
],
|
|
14646
15113
|
static: false,
|
|
14647
|
-
params: [{ name: "target", type:
|
|
14648
|
-
returnType: `Promise<Array<{ states: ${
|
|
15114
|
+
params: [{ name: "target", type: stateName2 }],
|
|
15115
|
+
returnType: `Promise<Array<{ states: ${stateName2}[]; transitions: ${transitionName}[] }>>`,
|
|
14649
15116
|
runtime: {
|
|
14650
15117
|
kind: "state_machine",
|
|
14651
15118
|
machineName: machine.name,
|
|
14652
15119
|
className: classSummary.name,
|
|
14653
|
-
stateTypeName:
|
|
15120
|
+
stateTypeName: stateName2,
|
|
14654
15121
|
transitionTypeName: transitionName,
|
|
14655
15122
|
operation: "paths_to"
|
|
14656
15123
|
}
|
|
@@ -14674,7 +15141,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14674
15141
|
kind: "state_machine",
|
|
14675
15142
|
machineName: machine.name,
|
|
14676
15143
|
className: classSummary.name,
|
|
14677
|
-
stateTypeName:
|
|
15144
|
+
stateTypeName: stateName2,
|
|
14678
15145
|
transitionTypeName: transitionName,
|
|
14679
15146
|
operation: "reach",
|
|
14680
15147
|
targetState: stateNameValue,
|
|
@@ -14693,7 +15160,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14693
15160
|
kind: "state_machine",
|
|
14694
15161
|
machineName: machine.name,
|
|
14695
15162
|
className: classSummary.name,
|
|
14696
|
-
stateTypeName:
|
|
15163
|
+
stateTypeName: stateName2,
|
|
14697
15164
|
transitionTypeName: transitionName,
|
|
14698
15165
|
operation: "prepare_reach",
|
|
14699
15166
|
targetState: stateNameValue,
|
|
@@ -14706,7 +15173,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14706
15173
|
kind: "state_machine",
|
|
14707
15174
|
machineName: machine.name,
|
|
14708
15175
|
className: classSummary.name,
|
|
14709
|
-
stateTypeName:
|
|
15176
|
+
stateTypeName: stateName2,
|
|
14710
15177
|
transitionTypeName: transitionName,
|
|
14711
15178
|
operation: "prepare_create_reach",
|
|
14712
15179
|
targetState: stateNameValue,
|
|
@@ -14772,7 +15239,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14772
15239
|
name: String!
|
|
14773
15240
|
state_machine: StateMachine!
|
|
14774
15241
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
14775
|
-
add_transition(name: String!, from: String!, to: String!, label: String, description: String, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String): StateMachineMutation!
|
|
15242
|
+
add_transition(name: String!, from: String!, to: String!, label: String, description: String, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String, outcomes_json: String): StateMachineMutation!
|
|
14776
15243
|
activate_transition(name: String!): StateMachineMutation!
|
|
14777
15244
|
}
|
|
14778
15245
|
|
|
@@ -14789,6 +15256,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14789
15256
|
type StateMachineSnapshotMutation {
|
|
14790
15257
|
snapshot: StateMachineSnapshot!
|
|
14791
15258
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15259
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
14792
15260
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
14793
15261
|
}
|
|
14794
15262
|
|
|
@@ -14835,6 +15303,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14835
15303
|
permission_json: String
|
|
14836
15304
|
risk: String
|
|
14837
15305
|
expected_outcome_json: String
|
|
15306
|
+
outcomes_json: String
|
|
14838
15307
|
}
|
|
14839
15308
|
|
|
14840
15309
|
type StateMachinePath {
|
|
@@ -14845,6 +15314,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14845
15314
|
type StateMachineTransitionEvent {
|
|
14846
15315
|
sequence: Int!
|
|
14847
15316
|
occurred_at: Float!
|
|
15317
|
+
commit_id: String
|
|
15318
|
+
outcome: String
|
|
15319
|
+
source_version: String
|
|
15320
|
+
projected_from_mismatch: String
|
|
14848
15321
|
transition: StateMachineTransition!
|
|
14849
15322
|
from: StateMachineState!
|
|
14850
15323
|
to: StateMachineState!
|
|
@@ -14910,7 +15383,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14910
15383
|
requirements_json,
|
|
14911
15384
|
permission_json,
|
|
14912
15385
|
risk,
|
|
14913
|
-
expected_outcome_json
|
|
15386
|
+
expected_outcome_json,
|
|
15387
|
+
outcomes_json
|
|
14914
15388
|
}) => {
|
|
14915
15389
|
await run(
|
|
14916
15390
|
value.target.add_state_machine_transition(
|
|
@@ -14926,7 +15400,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14926
15400
|
requirementsJson: requirements_json,
|
|
14927
15401
|
permissionJson: permission_json,
|
|
14928
15402
|
risk,
|
|
14929
|
-
expectedOutcomeJson: expected_outcome_json
|
|
15403
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
15404
|
+
outcomesJson: outcomes_json
|
|
14930
15405
|
}
|
|
14931
15406
|
)
|
|
14932
15407
|
);
|
|
@@ -14947,6 +15422,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14947
15422
|
);
|
|
14948
15423
|
return value;
|
|
14949
15424
|
},
|
|
15425
|
+
commit_transition: async (value, { name, outcome, to, commit_id, source_version }) => {
|
|
15426
|
+
await run(
|
|
15427
|
+
value.target.commit_state_machine_transition(
|
|
15428
|
+
value.name,
|
|
15429
|
+
name,
|
|
15430
|
+
outcome,
|
|
15431
|
+
to,
|
|
15432
|
+
commit_id,
|
|
15433
|
+
source_version
|
|
15434
|
+
)
|
|
15435
|
+
);
|
|
15436
|
+
return value;
|
|
15437
|
+
},
|
|
14950
15438
|
observe_state: async (value, { state, force, source }) => {
|
|
14951
15439
|
await run(
|
|
14952
15440
|
value.target.observe_state_machine_state(
|
|
@@ -14976,7 +15464,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14976
15464
|
requirements_json: (value) => value.requirements_json || null,
|
|
14977
15465
|
permission_json: (value) => value.permission_json || null,
|
|
14978
15466
|
risk: (value) => value.risk || null,
|
|
14979
|
-
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
15467
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null,
|
|
15468
|
+
outcomes_json: (value) => value.outcomes_json || null
|
|
14980
15469
|
},
|
|
14981
15470
|
StateMachinePath: {
|
|
14982
15471
|
states: (value) => value.states,
|
|
@@ -14985,6 +15474,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14985
15474
|
StateMachineTransitionEvent: {
|
|
14986
15475
|
sequence: (value) => value.sequence,
|
|
14987
15476
|
occurred_at: (value) => value.occurred_at,
|
|
15477
|
+
commit_id: (value) => value.commit_id || null,
|
|
15478
|
+
outcome: (value) => value.outcome || null,
|
|
15479
|
+
source_version: (value) => value.source_version || null,
|
|
15480
|
+
projected_from_mismatch: (value) => value.projected_from_mismatch || null,
|
|
14988
15481
|
transition: (value) => value.transition,
|
|
14989
15482
|
from: (value) => value.from,
|
|
14990
15483
|
to: (value) => value.to
|
|
@@ -15058,6 +15551,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15058
15551
|
permission_json
|
|
15059
15552
|
risk
|
|
15060
15553
|
expected_outcome_json
|
|
15554
|
+
outcomes_json
|
|
15061
15555
|
}
|
|
15062
15556
|
}`
|
|
15063
15557
|
]
|
|
@@ -15785,6 +16279,133 @@ var Environment = class _Environment {
|
|
|
15785
16279
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
15786
16280
|
};
|
|
15787
16281
|
}
|
|
16282
|
+
/**
|
|
16283
|
+
* Acknowledge a declared product mutation that already happened outside a
|
|
16284
|
+
* Granular-run effect (for example, in a webhook consumer). These methods
|
|
16285
|
+
* run the declaration's pure projection mapper; they never call its handler.
|
|
16286
|
+
*/
|
|
16287
|
+
get commit() {
|
|
16288
|
+
return {
|
|
16289
|
+
effect: async (effect, productResult, options = {}) => this.persistExternalEffect(effect, productResult, options),
|
|
16290
|
+
transition: async (transition, productResult, options = {}) => this.persistExternalTransition(transition, productResult, options),
|
|
16291
|
+
get: async (commitId) => this.controlPlaneRequest(
|
|
16292
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16293
|
+
commitId
|
|
16294
|
+
)}`
|
|
16295
|
+
),
|
|
16296
|
+
retry: async (commitId) => this.controlPlaneRequest(
|
|
16297
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16298
|
+
commitId
|
|
16299
|
+
)}/retry`,
|
|
16300
|
+
{ method: "POST" }
|
|
16301
|
+
)
|
|
16302
|
+
};
|
|
16303
|
+
}
|
|
16304
|
+
/** Inspect or retry the agent synchronization of a product snapshot. */
|
|
16305
|
+
get observation() {
|
|
16306
|
+
return {
|
|
16307
|
+
get: async (observationId) => this.controlPlaneRequest(
|
|
16308
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16309
|
+
observationId
|
|
16310
|
+
)}`
|
|
16311
|
+
),
|
|
16312
|
+
retry: async (observationId) => this.controlPlaneRequest(
|
|
16313
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16314
|
+
observationId
|
|
16315
|
+
)}/retry`,
|
|
16316
|
+
{ method: "POST" }
|
|
16317
|
+
)
|
|
16318
|
+
};
|
|
16319
|
+
}
|
|
16320
|
+
async persistExternalEffect(effect, productResult, options) {
|
|
16321
|
+
const projection = effect.commit.project(productResult);
|
|
16322
|
+
validateProjectionResult(effect.commit, projection);
|
|
16323
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16324
|
+
return this.controlPlaneRequest(
|
|
16325
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16326
|
+
{
|
|
16327
|
+
method: "POST",
|
|
16328
|
+
body: JSON.stringify({
|
|
16329
|
+
kind: "effect",
|
|
16330
|
+
effectKey: computeEffectKey2(effect),
|
|
16331
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16332
|
+
projection
|
|
16333
|
+
})
|
|
16334
|
+
}
|
|
16335
|
+
);
|
|
16336
|
+
}
|
|
16337
|
+
async persistExternalTransition(transition, productResult, options) {
|
|
16338
|
+
const metadata = getDefinedTransitionMetadata(transition);
|
|
16339
|
+
if (!metadata) {
|
|
16340
|
+
throw new Error(
|
|
16341
|
+
"environment.commit.transition requires a transition returned by defineStateMachine"
|
|
16342
|
+
);
|
|
16343
|
+
}
|
|
16344
|
+
const projection = transition.effect.commit.project(productResult);
|
|
16345
|
+
validateProjectionResult(transition.effect.commit, projection);
|
|
16346
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16347
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
16348
|
+
transition.outcomes,
|
|
16349
|
+
projection.outcome.key
|
|
16350
|
+
)) {
|
|
16351
|
+
throw new Error(
|
|
16352
|
+
`Transition ${metadata.machine}.${metadata.transition} does not declare outcome ${projection.outcome.key}`
|
|
16353
|
+
);
|
|
16354
|
+
}
|
|
16355
|
+
if (!projection.primaryTarget) {
|
|
16356
|
+
throw new Error(
|
|
16357
|
+
"An external transition projection requires primaryTarget to identify the transitioned product record"
|
|
16358
|
+
);
|
|
16359
|
+
}
|
|
16360
|
+
return this.controlPlaneRequest(
|
|
16361
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16362
|
+
{
|
|
16363
|
+
method: "POST",
|
|
16364
|
+
body: JSON.stringify({
|
|
16365
|
+
kind: "transition",
|
|
16366
|
+
effectKey: computeEffectKey2(transition.effect),
|
|
16367
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16368
|
+
projection,
|
|
16369
|
+
transition: {
|
|
16370
|
+
className: projection.primaryTarget.className,
|
|
16371
|
+
objectId: projection.primaryTarget.id,
|
|
16372
|
+
...projection.primaryTarget.path ? { objectPath: projection.primaryTarget.path } : {},
|
|
16373
|
+
machine: metadata.machine,
|
|
16374
|
+
transition: metadata.transition,
|
|
16375
|
+
from: transition.from
|
|
16376
|
+
}
|
|
16377
|
+
})
|
|
16378
|
+
}
|
|
16379
|
+
);
|
|
16380
|
+
}
|
|
16381
|
+
requireExternalIdentity(sourceVersion, options) {
|
|
16382
|
+
if (!options.sourceEventId?.trim() && !sourceVersion?.trim()) {
|
|
16383
|
+
throw new Error(
|
|
16384
|
+
"An external commit requires sourceEventId or a source version from its projection mapper"
|
|
16385
|
+
);
|
|
16386
|
+
}
|
|
16387
|
+
}
|
|
16388
|
+
/**
|
|
16389
|
+
* Synchronize a versioned product snapshot without claiming an effect or
|
|
16390
|
+
* lifecycle transition. This records no transition history.
|
|
16391
|
+
*/
|
|
16392
|
+
async observe(mapper, productResult) {
|
|
16393
|
+
const declaration = { kind: "effect"};
|
|
16394
|
+
const projection = mapper(productResult);
|
|
16395
|
+
validateProjectionResult(declaration, projection);
|
|
16396
|
+
if (!projection.source.version?.trim()) {
|
|
16397
|
+
throw new Error(
|
|
16398
|
+
"environment.observe requires a monotonic source version or serialized adapter sequence"
|
|
16399
|
+
);
|
|
16400
|
+
}
|
|
16401
|
+
return this.controlPlaneRequest(
|
|
16402
|
+
`/control/environments/${this.environmentId}/observations`,
|
|
16403
|
+
{
|
|
16404
|
+
method: "POST",
|
|
16405
|
+
body: JSON.stringify({ projection })
|
|
16406
|
+
}
|
|
16407
|
+
);
|
|
16408
|
+
}
|
|
15788
16409
|
/**
|
|
15789
16410
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
15790
16411
|
* own the customer application's state machine.
|
|
@@ -15824,8 +16445,8 @@ var Environment = class _Environment {
|
|
|
15824
16445
|
* `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
|
|
15825
16446
|
*/
|
|
15826
16447
|
state(target) {
|
|
15827
|
-
const observe = async (machineName,
|
|
15828
|
-
const observedState = input.observedState || input.state ||
|
|
16448
|
+
const observe = async (machineName, stateName2, input = {}) => {
|
|
16449
|
+
const observedState = input.observedState || input.state || stateName2;
|
|
15829
16450
|
if (!observedState) {
|
|
15830
16451
|
throw new Error("State observation requires a target state");
|
|
15831
16452
|
}
|
|
@@ -15851,7 +16472,7 @@ var Environment = class _Environment {
|
|
|
15851
16472
|
{
|
|
15852
16473
|
get: (_machineTarget, stateProperty) => {
|
|
15853
16474
|
if (stateProperty === "to") {
|
|
15854
|
-
return (
|
|
16475
|
+
return (stateName2, input) => observe(machineProperty, stateName2, input || {});
|
|
15855
16476
|
}
|
|
15856
16477
|
if (typeof stateProperty !== "string") return void 0;
|
|
15857
16478
|
return (input) => observe(
|
|
@@ -17191,6 +17812,14 @@ var EnvironmentSession = class extends Session {
|
|
|
17191
17812
|
}
|
|
17192
17813
|
};
|
|
17193
17814
|
}
|
|
17815
|
+
get mutations() {
|
|
17816
|
+
return {
|
|
17817
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
17818
|
+
"/mutations",
|
|
17819
|
+
options
|
|
17820
|
+
)
|
|
17821
|
+
};
|
|
17822
|
+
}
|
|
17194
17823
|
get artifacts() {
|
|
17195
17824
|
return {
|
|
17196
17825
|
list: (options = {}) => {
|
|
@@ -18690,6 +19319,7 @@ var Granular = class _Granular {
|
|
|
18690
19319
|
const serialized = {
|
|
18691
19320
|
effectKey: computeEffectKey2(effect),
|
|
18692
19321
|
name: effect.name,
|
|
19322
|
+
...effect.label ? { label: effect.label } : {},
|
|
18693
19323
|
description: effect.description,
|
|
18694
19324
|
inputSchema: effect.inputSchema,
|
|
18695
19325
|
stability: effect.stability || "stable",
|
|
@@ -18713,6 +19343,9 @@ var Granular = class _Granular {
|
|
|
18713
19343
|
if (effect.metamodels !== void 0) {
|
|
18714
19344
|
serialized.metamodels = effect.metamodels;
|
|
18715
19345
|
}
|
|
19346
|
+
if (effect.commit !== void 0) {
|
|
19347
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
19348
|
+
}
|
|
18716
19349
|
return serialized;
|
|
18717
19350
|
}
|
|
18718
19351
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -18933,16 +19566,60 @@ var Granular = class _Granular {
|
|
|
18933
19566
|
};
|
|
18934
19567
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
18935
19568
|
const request = params;
|
|
18936
|
-
|
|
19569
|
+
let commitReceipt;
|
|
19570
|
+
const result = await invokeRegisteredEffect(
|
|
18937
19571
|
this.getSandboxEffectMap(sandboxId),
|
|
18938
19572
|
request,
|
|
18939
19573
|
{
|
|
19574
|
+
commitAcknowledged: (receipt) => {
|
|
19575
|
+
commitReceipt = receipt;
|
|
19576
|
+
},
|
|
19577
|
+
commit: {
|
|
19578
|
+
persist: (commitRequest) => this.request(
|
|
19579
|
+
`/control/environments/${encodeURIComponent(
|
|
19580
|
+
commitRequest.environmentId
|
|
19581
|
+
)}/commits`,
|
|
19582
|
+
{
|
|
19583
|
+
method: "POST",
|
|
19584
|
+
body: JSON.stringify({
|
|
19585
|
+
kind: commitRequest.kind,
|
|
19586
|
+
invocationId: commitRequest.invocationId,
|
|
19587
|
+
idempotencyKey: commitRequest.idempotencyKey,
|
|
19588
|
+
effectKey: commitRequest.effectKey,
|
|
19589
|
+
projection: commitRequest.projection
|
|
19590
|
+
})
|
|
19591
|
+
}
|
|
19592
|
+
),
|
|
19593
|
+
mappingFailed: (failure) => this.request(
|
|
19594
|
+
`/control/environments/${encodeURIComponent(
|
|
19595
|
+
failure.environmentId
|
|
19596
|
+
)}/commit-invocations/${encodeURIComponent(
|
|
19597
|
+
failure.invocationId
|
|
19598
|
+
)}`,
|
|
19599
|
+
{
|
|
19600
|
+
method: "PATCH",
|
|
19601
|
+
body: JSON.stringify({
|
|
19602
|
+
status: "mapping_failed",
|
|
19603
|
+
error: {
|
|
19604
|
+
code: "commit_projection_mapping_failed",
|
|
19605
|
+
message: failure.message,
|
|
19606
|
+
retryable: false
|
|
19607
|
+
}
|
|
19608
|
+
})
|
|
19609
|
+
}
|
|
19610
|
+
)
|
|
19611
|
+
},
|
|
18940
19612
|
feedback: {
|
|
18941
19613
|
invocationId: request.callId,
|
|
18942
19614
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
18943
19615
|
}
|
|
18944
19616
|
}
|
|
18945
19617
|
);
|
|
19618
|
+
return {
|
|
19619
|
+
__granularEffectInvocationResult: true,
|
|
19620
|
+
result,
|
|
19621
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
19622
|
+
};
|
|
18946
19623
|
});
|
|
18947
19624
|
wsClient.on("open", () => {
|
|
18948
19625
|
void this.synchronizeEffectHost(host).catch((error) => {
|
|
@@ -19514,6 +20191,275 @@ var Granular = class _Granular {
|
|
|
19514
20191
|
}
|
|
19515
20192
|
};
|
|
19516
20193
|
|
|
20194
|
+
// src/record-snapshot-projection.ts
|
|
20195
|
+
function stableValue(value) {
|
|
20196
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
20197
|
+
if (value && typeof value === "object") {
|
|
20198
|
+
return Object.fromEntries(
|
|
20199
|
+
Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stableValue(item)])
|
|
20200
|
+
);
|
|
20201
|
+
}
|
|
20202
|
+
return value;
|
|
20203
|
+
}
|
|
20204
|
+
function stableJson(value) {
|
|
20205
|
+
return JSON.stringify(stableValue(value));
|
|
20206
|
+
}
|
|
20207
|
+
function recordKey(record) {
|
|
20208
|
+
return `${record.className}\0${record.id}`;
|
|
20209
|
+
}
|
|
20210
|
+
function recordReference(className, id) {
|
|
20211
|
+
return { className, id };
|
|
20212
|
+
}
|
|
20213
|
+
function changedRecordSnapshots(beforeRecords, afterRecords) {
|
|
20214
|
+
const before = new Map(
|
|
20215
|
+
beforeRecords.map((record) => [recordKey(record), record])
|
|
20216
|
+
);
|
|
20217
|
+
const after = new Map(
|
|
20218
|
+
afterRecords.map((record) => [recordKey(record), record])
|
|
20219
|
+
);
|
|
20220
|
+
const changedKeys = /* @__PURE__ */ new Set();
|
|
20221
|
+
for (const key of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) {
|
|
20222
|
+
if (stableJson(before.get(key)) !== stableJson(after.get(key))) {
|
|
20223
|
+
changedKeys.add(key);
|
|
20224
|
+
}
|
|
20225
|
+
}
|
|
20226
|
+
return {
|
|
20227
|
+
beforeRecords: beforeRecords.filter(
|
|
20228
|
+
(record) => changedKeys.has(recordKey(record))
|
|
20229
|
+
),
|
|
20230
|
+
afterRecords: afterRecords.filter(
|
|
20231
|
+
(record) => changedKeys.has(recordKey(record))
|
|
20232
|
+
)
|
|
20233
|
+
};
|
|
20234
|
+
}
|
|
20235
|
+
function relationshipTargets(manifest) {
|
|
20236
|
+
const targets = /* @__PURE__ */ new Map();
|
|
20237
|
+
for (const volume of manifest.volumes) {
|
|
20238
|
+
for (const operation of volume.operations) {
|
|
20239
|
+
const relationship = operation.defineRelationship;
|
|
20240
|
+
if (!relationship) continue;
|
|
20241
|
+
targets.set(`${relationship.left}\0${relationship.leftSubmodel}`, {
|
|
20242
|
+
className: relationship.right
|
|
20243
|
+
});
|
|
20244
|
+
targets.set(`${relationship.right}\0${relationship.rightSubmodel}`, {
|
|
20245
|
+
className: relationship.left
|
|
20246
|
+
});
|
|
20247
|
+
}
|
|
20248
|
+
}
|
|
20249
|
+
return targets;
|
|
20250
|
+
}
|
|
20251
|
+
function stateName(value) {
|
|
20252
|
+
return typeof value === "string" ? value : value.state;
|
|
20253
|
+
}
|
|
20254
|
+
function asIds(value) {
|
|
20255
|
+
return value === void 0 ? [] : Array.isArray(value) ? value : [value];
|
|
20256
|
+
}
|
|
20257
|
+
function changedObjectProjection(before, after) {
|
|
20258
|
+
const beforeFields = before?.fields || {};
|
|
20259
|
+
const afterFields = after.fields || {};
|
|
20260
|
+
const fields = { ...afterFields };
|
|
20261
|
+
for (const fieldName of Object.keys(beforeFields)) {
|
|
20262
|
+
if (!(fieldName in afterFields)) fields[fieldName] = null;
|
|
20263
|
+
}
|
|
20264
|
+
if (before && before.label === after.label && stableJson(beforeFields) === stableJson(afterFields)) {
|
|
20265
|
+
return null;
|
|
20266
|
+
}
|
|
20267
|
+
return {
|
|
20268
|
+
kind: "object",
|
|
20269
|
+
operation: before ? "updated" : "created",
|
|
20270
|
+
record: {
|
|
20271
|
+
className: after.className,
|
|
20272
|
+
id: after.id,
|
|
20273
|
+
...after.label !== void 0 ? { label: after.label } : {},
|
|
20274
|
+
fields
|
|
20275
|
+
}
|
|
20276
|
+
};
|
|
20277
|
+
}
|
|
20278
|
+
function projectionChanges(input) {
|
|
20279
|
+
const before = new Map(
|
|
20280
|
+
input.beforeRecords.map((record) => [recordKey(record), record])
|
|
20281
|
+
);
|
|
20282
|
+
const after = new Map(
|
|
20283
|
+
input.afterRecords.map((record) => [recordKey(record), record])
|
|
20284
|
+
);
|
|
20285
|
+
const targets = relationshipTargets(input.manifest);
|
|
20286
|
+
const changes = [];
|
|
20287
|
+
for (const record of input.afterRecords) {
|
|
20288
|
+
const previous = before.get(recordKey(record));
|
|
20289
|
+
const objectChange = changedObjectProjection(previous, record);
|
|
20290
|
+
if (objectChange) changes.push(objectChange);
|
|
20291
|
+
const relationshipNames = /* @__PURE__ */ new Set([
|
|
20292
|
+
...Object.keys(previous?.relationships || {}),
|
|
20293
|
+
...Object.keys(record.relationships || {})
|
|
20294
|
+
]);
|
|
20295
|
+
for (const relationshipName of [...relationshipNames].sort()) {
|
|
20296
|
+
const target = targets.get(
|
|
20297
|
+
`${record.className}\0${relationshipName}`
|
|
20298
|
+
);
|
|
20299
|
+
if (!target) {
|
|
20300
|
+
throw new Error(
|
|
20301
|
+
`No ontology relationship target is declared for ${record.className}.${relationshipName}`
|
|
20302
|
+
);
|
|
20303
|
+
}
|
|
20304
|
+
const previousIds = new Set(
|
|
20305
|
+
asIds(previous?.relationships?.[relationshipName])
|
|
20306
|
+
);
|
|
20307
|
+
const currentIds = new Set(
|
|
20308
|
+
asIds(record.relationships?.[relationshipName])
|
|
20309
|
+
);
|
|
20310
|
+
for (const id of [...previousIds].sort()) {
|
|
20311
|
+
if (currentIds.has(id)) continue;
|
|
20312
|
+
changes.push({
|
|
20313
|
+
kind: "relationship",
|
|
20314
|
+
operation: "disconnected",
|
|
20315
|
+
relationship: relationshipName,
|
|
20316
|
+
from: recordReference(record.className, record.id),
|
|
20317
|
+
to: recordReference(target.className, id)
|
|
20318
|
+
});
|
|
20319
|
+
}
|
|
20320
|
+
for (const id of [...currentIds].sort()) {
|
|
20321
|
+
if (previousIds.has(id)) continue;
|
|
20322
|
+
changes.push({
|
|
20323
|
+
kind: "relationship",
|
|
20324
|
+
operation: "connected",
|
|
20325
|
+
relationship: relationshipName,
|
|
20326
|
+
from: recordReference(record.className, record.id),
|
|
20327
|
+
to: recordReference(target.className, id)
|
|
20328
|
+
});
|
|
20329
|
+
}
|
|
20330
|
+
}
|
|
20331
|
+
const machines = /* @__PURE__ */ new Set([
|
|
20332
|
+
...Object.keys(previous?.states || {}),
|
|
20333
|
+
...Object.keys(record.states || {})
|
|
20334
|
+
]);
|
|
20335
|
+
for (const machine of [...machines].sort()) {
|
|
20336
|
+
const next = record.states?.[machine];
|
|
20337
|
+
if (next === void 0) continue;
|
|
20338
|
+
const prior = previous?.states?.[machine];
|
|
20339
|
+
if (prior !== void 0 && stateName(prior) === stateName(next)) continue;
|
|
20340
|
+
if (input.transition && input.transition.className === record.className && input.transition.objectId === record.id && input.transition.machine === machine) {
|
|
20341
|
+
continue;
|
|
20342
|
+
}
|
|
20343
|
+
changes.push({
|
|
20344
|
+
kind: "state_observation",
|
|
20345
|
+
target: recordReference(record.className, record.id),
|
|
20346
|
+
machine,
|
|
20347
|
+
state: stateName(next)
|
|
20348
|
+
});
|
|
20349
|
+
}
|
|
20350
|
+
}
|
|
20351
|
+
for (const record of input.beforeRecords) {
|
|
20352
|
+
if (after.has(recordKey(record))) continue;
|
|
20353
|
+
changes.push({
|
|
20354
|
+
kind: "object",
|
|
20355
|
+
operation: "deleted",
|
|
20356
|
+
target: recordReference(record.className, record.id)
|
|
20357
|
+
});
|
|
20358
|
+
}
|
|
20359
|
+
return changes;
|
|
20360
|
+
}
|
|
20361
|
+
function valueAtPath(value, path) {
|
|
20362
|
+
return path.split(".").filter(Boolean).reduce((current, segment) => {
|
|
20363
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
20364
|
+
return void 0;
|
|
20365
|
+
}
|
|
20366
|
+
return current[segment];
|
|
20367
|
+
}, value);
|
|
20368
|
+
}
|
|
20369
|
+
function primaryTarget(input) {
|
|
20370
|
+
if (input.transition) {
|
|
20371
|
+
return recordReference(
|
|
20372
|
+
input.transition.className,
|
|
20373
|
+
input.transition.objectId
|
|
20374
|
+
);
|
|
20375
|
+
}
|
|
20376
|
+
const creates = input.effect.metamodels?.creates;
|
|
20377
|
+
if (creates) {
|
|
20378
|
+
const declaration = typeof creates === "string" ? { className: creates } : creates;
|
|
20379
|
+
const id = declaration.idPath ? valueAtPath(input.result, declaration.idPath) : void 0;
|
|
20380
|
+
if (typeof id === "string" && id) {
|
|
20381
|
+
return recordReference(declaration.className, id);
|
|
20382
|
+
}
|
|
20383
|
+
}
|
|
20384
|
+
if (input.effect.className && !input.effect.static) {
|
|
20385
|
+
const objectId = input.effectInput && typeof input.effectInput === "object" && !Array.isArray(input.effectInput) ? input.effectInput._objectId : void 0;
|
|
20386
|
+
if (typeof objectId === "string" && objectId) {
|
|
20387
|
+
return recordReference(input.effect.className, objectId);
|
|
20388
|
+
}
|
|
20389
|
+
}
|
|
20390
|
+
const created = input.changes.filter(
|
|
20391
|
+
(change) => change.kind === "object" && change.operation === "created"
|
|
20392
|
+
);
|
|
20393
|
+
const onlyCreated = created.length === 1 ? created[0] : void 0;
|
|
20394
|
+
return onlyCreated ? recordReference(onlyCreated.record.className, onlyCreated.record.id) : void 0;
|
|
20395
|
+
}
|
|
20396
|
+
function transitionOutcome(transition, afterRecords) {
|
|
20397
|
+
if (!transition) {
|
|
20398
|
+
throw new Error("A transition commit has no authored transition context.");
|
|
20399
|
+
}
|
|
20400
|
+
const target = afterRecords.find(
|
|
20401
|
+
(record) => record.className === transition.className && record.id === transition.objectId
|
|
20402
|
+
);
|
|
20403
|
+
const observed = target?.states?.[transition.machine];
|
|
20404
|
+
if (!observed) {
|
|
20405
|
+
throw new Error(
|
|
20406
|
+
`The product result did not expose ${transition.className}:${transition.objectId}.${transition.machine}`
|
|
20407
|
+
);
|
|
20408
|
+
}
|
|
20409
|
+
const finalState = stateName(observed);
|
|
20410
|
+
const matches = Object.entries(transition.outcomes).filter(
|
|
20411
|
+
([, outcome]) => outcome.to === finalState || outcome.to === "$current" && finalState === transition.from
|
|
20412
|
+
);
|
|
20413
|
+
if (matches.length !== 1) {
|
|
20414
|
+
throw new Error(
|
|
20415
|
+
`Product state ${finalState} maps to ${matches.length} authored outcomes for ${transition.machine}.${transition.transition}`
|
|
20416
|
+
);
|
|
20417
|
+
}
|
|
20418
|
+
const [key, declaration] = matches[0];
|
|
20419
|
+
return {
|
|
20420
|
+
key,
|
|
20421
|
+
...declaration.disposition === "error" ? {
|
|
20422
|
+
error: {
|
|
20423
|
+
code: `product_outcome_${key}`,
|
|
20424
|
+
message: declaration.label || `The product completed in ${finalState} instead of continuing.`,
|
|
20425
|
+
retryable: false
|
|
20426
|
+
}
|
|
20427
|
+
} : {}
|
|
20428
|
+
};
|
|
20429
|
+
}
|
|
20430
|
+
function projectRecordSnapshotMutation(input) {
|
|
20431
|
+
const changes = projectionChanges({
|
|
20432
|
+
manifest: input.manifest,
|
|
20433
|
+
beforeRecords: input.source.beforeRecords,
|
|
20434
|
+
afterRecords: input.source.afterRecords,
|
|
20435
|
+
transition: input.transition
|
|
20436
|
+
});
|
|
20437
|
+
const target = primaryTarget({
|
|
20438
|
+
effect: input.effect,
|
|
20439
|
+
effectInput: input.effectInput,
|
|
20440
|
+
result: input.result,
|
|
20441
|
+
changes,
|
|
20442
|
+
transition: input.transition
|
|
20443
|
+
});
|
|
20444
|
+
const base = {
|
|
20445
|
+
source: {
|
|
20446
|
+
reference: input.source.reference,
|
|
20447
|
+
...input.source.version ? { version: input.source.version } : {}
|
|
20448
|
+
},
|
|
20449
|
+
...target ? { primaryTarget: target } : {},
|
|
20450
|
+
changes,
|
|
20451
|
+
safeSummary: {
|
|
20452
|
+
effect: input.effect.name,
|
|
20453
|
+
affectedChanges: changes.length
|
|
20454
|
+
}
|
|
20455
|
+
};
|
|
20456
|
+
if (input.commitKind === "effect") return base;
|
|
20457
|
+
return {
|
|
20458
|
+
...base,
|
|
20459
|
+
outcome: transitionOutcome(input.transition, input.source.afterRecords)
|
|
20460
|
+
};
|
|
20461
|
+
}
|
|
20462
|
+
|
|
19517
20463
|
// src/agent-harness-templates/action-presentation/0.1.0/manifest.json
|
|
19518
20464
|
var manifest_default = {
|
|
19519
20465
|
id: "action-presentation",
|
|
@@ -22551,10 +23497,15 @@ exports.buildOpenAISpendEventId = buildOpenAISpendEventId;
|
|
|
22551
23497
|
exports.buildSessionTranscript = buildSessionTranscript;
|
|
22552
23498
|
exports.buildSessionTranscriptFromFeedItems = buildSessionTranscriptFromFeedItems;
|
|
22553
23499
|
exports.calculateOpenAITokenSpend = calculateOpenAITokenSpend;
|
|
23500
|
+
exports.canonicalizeCommitValue = canonicalizeCommitValue;
|
|
23501
|
+
exports.changedRecordSnapshots = changedRecordSnapshots;
|
|
22554
23502
|
exports.consumeGranularReasoningOnlyChunk = consumeGranularReasoningOnlyChunk;
|
|
22555
23503
|
exports.consumeGranularReasoningTraceChunk = consumeGranularReasoningTraceChunk;
|
|
22556
23504
|
exports.createFeedPublisher = createFeedPublisher;
|
|
22557
23505
|
exports.createHarnessVerifierSnapshot = createHarnessVerifierSnapshot;
|
|
23506
|
+
exports.defineEffect = defineEffect;
|
|
23507
|
+
exports.defineProjection = defineProjection;
|
|
23508
|
+
exports.defineStateMachine = defineStateMachine;
|
|
22558
23509
|
exports.emitFeedDiagnostic = emitFeedDiagnostic;
|
|
22559
23510
|
exports.emitFeedDiagnosticToDefaultSink = emitFeedDiagnosticToDefaultSink;
|
|
22560
23511
|
exports.emptyFeedSnapshot = emptyFeedSnapshot;
|
|
@@ -22587,6 +23538,7 @@ exports.projectConversationReferentFocus = projectConversationReferentFocus;
|
|
|
22587
23538
|
exports.projectConversationReferentSummary = projectConversationReferentSummary;
|
|
22588
23539
|
exports.projectHeapSummary = projectHeapSummary;
|
|
22589
23540
|
exports.projectLoopSummary = projectLoopSummary;
|
|
23541
|
+
exports.projectRecordSnapshotMutation = projectRecordSnapshotMutation;
|
|
22590
23542
|
exports.projectSessionFileSummary = projectSessionFileSummary;
|
|
22591
23543
|
exports.projectWorkflowFocus = projectWorkflowFocus;
|
|
22592
23544
|
exports.projectWorkflowSummary = projectWorkflowSummary;
|
|
@@ -22604,6 +23556,7 @@ exports.scorePromptChoiceMatch = scorePromptChoiceMatch;
|
|
|
22604
23556
|
exports.stripGranularReasoningTrace = stripGranularReasoningTrace;
|
|
22605
23557
|
exports.toGranularHttpBase = toGranularHttpBase;
|
|
22606
23558
|
exports.validateHarnessTemplateManifest = validateHarnessTemplateManifest;
|
|
23559
|
+
exports.validateProjectionResult = validateProjectionResult;
|
|
22607
23560
|
exports.validationRuleFailureMessage = validationRuleFailureMessage;
|
|
22608
23561
|
//# sourceMappingURL=index.js.map
|
|
22609
23562
|
//# sourceMappingURL=index.js.map
|