@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.mjs
CHANGED
|
@@ -6533,6 +6533,11 @@ var Session = class {
|
|
|
6533
6533
|
}
|
|
6534
6534
|
}
|
|
6535
6535
|
buildDirectedInvocationEffectContext(params, feedbackContext) {
|
|
6536
|
+
const commitUnavailable = async () => {
|
|
6537
|
+
throw new Error(
|
|
6538
|
+
"This directed browser tool invocation has no product commit transport"
|
|
6539
|
+
);
|
|
6540
|
+
};
|
|
6536
6541
|
return {
|
|
6537
6542
|
effectClientId: this.clientId,
|
|
6538
6543
|
sandboxId: params.sandboxId || "",
|
|
@@ -6545,6 +6550,10 @@ var Session = class {
|
|
|
6545
6550
|
userId: "",
|
|
6546
6551
|
subjectId: ""
|
|
6547
6552
|
},
|
|
6553
|
+
commit: {
|
|
6554
|
+
effect: commitUnavailable,
|
|
6555
|
+
transition: commitUnavailable
|
|
6556
|
+
},
|
|
6548
6557
|
...feedbackContext ? {
|
|
6549
6558
|
feedback: feedbackContext.feedback,
|
|
6550
6559
|
transientFeedback: feedbackContext.transientFeedback
|
|
@@ -12316,6 +12325,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
12316
12325
|
summary: external_exports.string().optional()
|
|
12317
12326
|
}).strict()
|
|
12318
12327
|
]);
|
|
12328
|
+
var StateTransitionOutcomeSchema = external_exports.object({
|
|
12329
|
+
label: external_exports.string().min(1).optional(),
|
|
12330
|
+
to: external_exports.string().min(1),
|
|
12331
|
+
primary: external_exports.boolean().optional(),
|
|
12332
|
+
disposition: external_exports.enum(["continue", "error"])
|
|
12333
|
+
}).strict();
|
|
12319
12334
|
var StateMachineTransitionSchema = external_exports.object({
|
|
12320
12335
|
name: external_exports.string().min(1),
|
|
12321
12336
|
from: external_exports.string().min(1),
|
|
@@ -12327,7 +12342,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12327
12342
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
12328
12343
|
permission: StateTransitionPermissionSchema.optional(),
|
|
12329
12344
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
12330
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
12345
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
12346
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
12331
12347
|
}).strict();
|
|
12332
12348
|
external_exports.object({
|
|
12333
12349
|
name: external_exports.string().min(1),
|
|
@@ -13008,6 +13024,289 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
13008
13024
|
}
|
|
13009
13025
|
});
|
|
13010
13026
|
|
|
13027
|
+
// src/commit.ts
|
|
13028
|
+
var MAX_PROJECTION_CHANGES = 1e3;
|
|
13029
|
+
var MAX_SAFE_SUMMARY_KEYS = 32;
|
|
13030
|
+
var MAX_SAFE_TEXT_LENGTH = 2e3;
|
|
13031
|
+
var definedTransitionMetadata = /* @__PURE__ */ new WeakMap();
|
|
13032
|
+
function getDefinedTransitionMetadata(transition) {
|
|
13033
|
+
return definedTransitionMetadata.get(transition);
|
|
13034
|
+
}
|
|
13035
|
+
function defineProjection(mapper) {
|
|
13036
|
+
return mapper;
|
|
13037
|
+
}
|
|
13038
|
+
function defineEffect(effect) {
|
|
13039
|
+
return effect;
|
|
13040
|
+
}
|
|
13041
|
+
function defineStateMachine(definition) {
|
|
13042
|
+
const stateNames = new Set(Object.keys(definition.states));
|
|
13043
|
+
for (const [transitionName, transition] of Object.entries(
|
|
13044
|
+
definition.transitions
|
|
13045
|
+
)) {
|
|
13046
|
+
if (!stateNames.has(transition.from)) {
|
|
13047
|
+
throw new Error(
|
|
13048
|
+
`Transition ${transitionName} starts at undeclared state ${transition.from}`
|
|
13049
|
+
);
|
|
13050
|
+
}
|
|
13051
|
+
if (transition.effect.commit?.kind !== "transition") {
|
|
13052
|
+
throw new Error(
|
|
13053
|
+
`Transition ${transitionName} must use a transition-commit effect`
|
|
13054
|
+
);
|
|
13055
|
+
}
|
|
13056
|
+
const outcomes = Object.entries(transition.outcomes);
|
|
13057
|
+
const primary = outcomes.filter(([, outcome]) => outcome.primary === true);
|
|
13058
|
+
if (primary.length !== 1) {
|
|
13059
|
+
throw new Error(
|
|
13060
|
+
`Transition ${transitionName} must declare exactly one primary outcome`
|
|
13061
|
+
);
|
|
13062
|
+
}
|
|
13063
|
+
for (const [outcomeKey, outcome] of outcomes) {
|
|
13064
|
+
if (outcome.to !== "$current" && !stateNames.has(outcome.to)) {
|
|
13065
|
+
throw new Error(
|
|
13066
|
+
`Transition ${transitionName} outcome ${outcomeKey} targets undeclared state ${outcome.to}`
|
|
13067
|
+
);
|
|
13068
|
+
}
|
|
13069
|
+
if (outcome.to === "$current" && outcome.disposition !== "error") {
|
|
13070
|
+
throw new Error(
|
|
13071
|
+
`Transition ${transitionName} outcome ${outcomeKey} may use $current only with error disposition`
|
|
13072
|
+
);
|
|
13073
|
+
}
|
|
13074
|
+
}
|
|
13075
|
+
}
|
|
13076
|
+
for (const [transitionName, transition] of Object.entries(
|
|
13077
|
+
definition.transitions
|
|
13078
|
+
)) {
|
|
13079
|
+
definedTransitionMetadata.set(transition, {
|
|
13080
|
+
machine: definition.name,
|
|
13081
|
+
transition: transitionName
|
|
13082
|
+
});
|
|
13083
|
+
}
|
|
13084
|
+
return definition;
|
|
13085
|
+
}
|
|
13086
|
+
function requireNonEmptyString(value, path) {
|
|
13087
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
13088
|
+
throw new Error(`${path} must be a non-empty string`);
|
|
13089
|
+
}
|
|
13090
|
+
return value.trim();
|
|
13091
|
+
}
|
|
13092
|
+
function validateObjectReference(value, path) {
|
|
13093
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13094
|
+
throw new Error(`${path} must be an object reference`);
|
|
13095
|
+
}
|
|
13096
|
+
const reference = value;
|
|
13097
|
+
requireNonEmptyString(reference.className, `${path}.className`);
|
|
13098
|
+
requireNonEmptyString(reference.id, `${path}.id`);
|
|
13099
|
+
if (reference.path !== void 0) {
|
|
13100
|
+
requireNonEmptyString(reference.path, `${path}.path`);
|
|
13101
|
+
}
|
|
13102
|
+
}
|
|
13103
|
+
function validateScalarRecord(value, path) {
|
|
13104
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
13105
|
+
throw new Error(`${path} must be an object of scalar values`);
|
|
13106
|
+
}
|
|
13107
|
+
for (const [key, item] of Object.entries(value)) {
|
|
13108
|
+
if (item !== null && typeof item !== "string" && typeof item !== "number" && typeof item !== "boolean") {
|
|
13109
|
+
throw new Error(
|
|
13110
|
+
`${path}.${key} must be a string, finite number, boolean, or null`
|
|
13111
|
+
);
|
|
13112
|
+
}
|
|
13113
|
+
if (typeof item === "number" && !Number.isFinite(item)) {
|
|
13114
|
+
throw new Error(`${path}.${key} must be finite`);
|
|
13115
|
+
}
|
|
13116
|
+
}
|
|
13117
|
+
}
|
|
13118
|
+
function validateProjectedRecord(value, path) {
|
|
13119
|
+
validateObjectReference(value, path);
|
|
13120
|
+
const record = value;
|
|
13121
|
+
if (record.label !== void 0) {
|
|
13122
|
+
if (typeof record.label !== "string") {
|
|
13123
|
+
throw new Error(`${path}.label must be a string`);
|
|
13124
|
+
}
|
|
13125
|
+
if (record.label.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13126
|
+
throw new Error(
|
|
13127
|
+
`${path}.label exceeds ${MAX_SAFE_TEXT_LENGTH} characters`
|
|
13128
|
+
);
|
|
13129
|
+
}
|
|
13130
|
+
}
|
|
13131
|
+
validateScalarRecord(record.fields, `${path}.fields`);
|
|
13132
|
+
}
|
|
13133
|
+
function validateBoundedJson(value, path, depth = 0) {
|
|
13134
|
+
if (depth > 12) throw new Error(`${path} is nested too deeply`);
|
|
13135
|
+
if (value === null || typeof value === "boolean") return;
|
|
13136
|
+
if (typeof value === "number") {
|
|
13137
|
+
if (!Number.isFinite(value)) throw new Error(`${path} must be finite`);
|
|
13138
|
+
return;
|
|
13139
|
+
}
|
|
13140
|
+
if (typeof value === "string") {
|
|
13141
|
+
if (value.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13142
|
+
throw new Error(`${path} exceeds ${MAX_SAFE_TEXT_LENGTH} characters`);
|
|
13143
|
+
}
|
|
13144
|
+
return;
|
|
13145
|
+
}
|
|
13146
|
+
if (Array.isArray(value)) {
|
|
13147
|
+
if (value.length > MAX_PROJECTION_CHANGES) {
|
|
13148
|
+
throw new Error(`${path} contains too many values`);
|
|
13149
|
+
}
|
|
13150
|
+
value.forEach(
|
|
13151
|
+
(item, index) => validateBoundedJson(item, `${path}[${index}]`, depth + 1)
|
|
13152
|
+
);
|
|
13153
|
+
return;
|
|
13154
|
+
}
|
|
13155
|
+
if (!value || typeof value !== "object") {
|
|
13156
|
+
throw new Error(`${path} contains an unsupported value`);
|
|
13157
|
+
}
|
|
13158
|
+
const entries = Object.entries(value);
|
|
13159
|
+
if (entries.length > 256) throw new Error(`${path} contains too many keys`);
|
|
13160
|
+
for (const [key, item] of entries) {
|
|
13161
|
+
if (/token|secret|password|authorization|cookie/i.test(key)) {
|
|
13162
|
+
throw new Error(`${path}.${key} is not allowed in a commit projection`);
|
|
13163
|
+
}
|
|
13164
|
+
validateBoundedJson(item, `${path}.${key}`, depth + 1);
|
|
13165
|
+
}
|
|
13166
|
+
}
|
|
13167
|
+
function validateProjectionResult(declaration, projection) {
|
|
13168
|
+
if (!projection || typeof projection !== "object") {
|
|
13169
|
+
throw new Error("Projection mapper must return an object");
|
|
13170
|
+
}
|
|
13171
|
+
requireNonEmptyString(
|
|
13172
|
+
projection.source?.reference,
|
|
13173
|
+
"projection.source.reference"
|
|
13174
|
+
);
|
|
13175
|
+
if (projection.source.version !== void 0) {
|
|
13176
|
+
requireNonEmptyString(
|
|
13177
|
+
projection.source.version,
|
|
13178
|
+
"projection.source.version"
|
|
13179
|
+
);
|
|
13180
|
+
}
|
|
13181
|
+
if (!Array.isArray(projection.changes)) {
|
|
13182
|
+
throw new Error("projection.changes must be an array");
|
|
13183
|
+
}
|
|
13184
|
+
if (projection.changes.length > MAX_PROJECTION_CHANGES) {
|
|
13185
|
+
throw new Error(
|
|
13186
|
+
`projection.changes exceeds the ${MAX_PROJECTION_CHANGES} change limit`
|
|
13187
|
+
);
|
|
13188
|
+
}
|
|
13189
|
+
if (projection.primaryTarget) {
|
|
13190
|
+
validateObjectReference(
|
|
13191
|
+
projection.primaryTarget,
|
|
13192
|
+
"projection.primaryTarget"
|
|
13193
|
+
);
|
|
13194
|
+
}
|
|
13195
|
+
if (projection.safeSummary) {
|
|
13196
|
+
const entries = Object.entries(projection.safeSummary);
|
|
13197
|
+
if (entries.length > MAX_SAFE_SUMMARY_KEYS) {
|
|
13198
|
+
throw new Error(
|
|
13199
|
+
`projection.safeSummary exceeds the ${MAX_SAFE_SUMMARY_KEYS} key limit`
|
|
13200
|
+
);
|
|
13201
|
+
}
|
|
13202
|
+
validateBoundedJson(projection.safeSummary, "projection.safeSummary");
|
|
13203
|
+
validateScalarRecord(projection.safeSummary, "projection.safeSummary");
|
|
13204
|
+
}
|
|
13205
|
+
projection.changes.forEach((change, index) => {
|
|
13206
|
+
const changePath = `projection.changes[${index}]`;
|
|
13207
|
+
validateBoundedJson(change, changePath);
|
|
13208
|
+
if (!change || typeof change !== "object" || Array.isArray(change)) {
|
|
13209
|
+
throw new Error(`${changePath} must be an object`);
|
|
13210
|
+
}
|
|
13211
|
+
const rawChange = change;
|
|
13212
|
+
const kind = requireNonEmptyString(rawChange.kind, `${changePath}.kind`);
|
|
13213
|
+
if (kind === "object") {
|
|
13214
|
+
const operation = requireNonEmptyString(
|
|
13215
|
+
rawChange.operation,
|
|
13216
|
+
`${changePath}.operation`
|
|
13217
|
+
);
|
|
13218
|
+
if (operation !== "created" && operation !== "updated" && operation !== "deleted") {
|
|
13219
|
+
throw new Error(
|
|
13220
|
+
`${changePath}.operation must be created, updated, or deleted`
|
|
13221
|
+
);
|
|
13222
|
+
}
|
|
13223
|
+
if (operation === "deleted") {
|
|
13224
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13225
|
+
} else {
|
|
13226
|
+
validateProjectedRecord(rawChange.record, `${changePath}.record`);
|
|
13227
|
+
}
|
|
13228
|
+
} else if (kind === "relationship") {
|
|
13229
|
+
const operation = requireNonEmptyString(
|
|
13230
|
+
rawChange.operation,
|
|
13231
|
+
`${changePath}.operation`
|
|
13232
|
+
);
|
|
13233
|
+
if (operation !== "connected" && operation !== "disconnected") {
|
|
13234
|
+
throw new Error(
|
|
13235
|
+
`${changePath}.operation must be connected or disconnected`
|
|
13236
|
+
);
|
|
13237
|
+
}
|
|
13238
|
+
requireNonEmptyString(
|
|
13239
|
+
rawChange.relationship,
|
|
13240
|
+
`${changePath}.relationship`
|
|
13241
|
+
);
|
|
13242
|
+
validateObjectReference(rawChange.from, `${changePath}.from`);
|
|
13243
|
+
validateObjectReference(rawChange.to, `${changePath}.to`);
|
|
13244
|
+
} else if (kind === "state_observation") {
|
|
13245
|
+
if (rawChange.operation !== void 0) {
|
|
13246
|
+
throw new Error(
|
|
13247
|
+
`${changePath}.operation is not valid for an observation`
|
|
13248
|
+
);
|
|
13249
|
+
}
|
|
13250
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13251
|
+
requireNonEmptyString(rawChange.machine, `${changePath}.machine`);
|
|
13252
|
+
requireNonEmptyString(rawChange.state, `${changePath}.state`);
|
|
13253
|
+
} else {
|
|
13254
|
+
throw new Error(`${changePath}.kind is unsupported: ${kind}`);
|
|
13255
|
+
}
|
|
13256
|
+
});
|
|
13257
|
+
const objectOperations = /* @__PURE__ */ new Map();
|
|
13258
|
+
for (const change of projection.changes) {
|
|
13259
|
+
if (change.kind !== "object") continue;
|
|
13260
|
+
const reference = change.operation === "deleted" ? change.target : change.record;
|
|
13261
|
+
const key = `${reference.className}\0${reference.id}`;
|
|
13262
|
+
const operations = objectOperations.get(key) || {
|
|
13263
|
+
deleted: false,
|
|
13264
|
+
upserted: false
|
|
13265
|
+
};
|
|
13266
|
+
if (change.operation === "deleted") operations.deleted = true;
|
|
13267
|
+
else operations.upserted = true;
|
|
13268
|
+
if (operations.deleted && operations.upserted) {
|
|
13269
|
+
throw new Error(
|
|
13270
|
+
`projection.changes cannot both delete and upsert ${reference.className}/${reference.id}; return only its canonical final state`
|
|
13271
|
+
);
|
|
13272
|
+
}
|
|
13273
|
+
objectOperations.set(key, operations);
|
|
13274
|
+
}
|
|
13275
|
+
const outcome = projection.outcome;
|
|
13276
|
+
if (declaration.kind === "transition") {
|
|
13277
|
+
if (!outcome) {
|
|
13278
|
+
throw new Error("A transition projection must return an outcome");
|
|
13279
|
+
}
|
|
13280
|
+
requireNonEmptyString(outcome.key, "projection.outcome.key");
|
|
13281
|
+
if (outcome.error) {
|
|
13282
|
+
validateBoundedJson(outcome.error, "projection.outcome.error");
|
|
13283
|
+
}
|
|
13284
|
+
} else if (projection.outcome !== void 0) {
|
|
13285
|
+
throw new Error("An effect projection cannot declare a transition outcome");
|
|
13286
|
+
}
|
|
13287
|
+
}
|
|
13288
|
+
function canonicalizeCommitValue(value) {
|
|
13289
|
+
const normalize = (current) => {
|
|
13290
|
+
if (current === null || typeof current === "boolean" || typeof current === "string") {
|
|
13291
|
+
return typeof current === "string" ? current.normalize("NFC") : current;
|
|
13292
|
+
}
|
|
13293
|
+
if (typeof current === "number") {
|
|
13294
|
+
if (!Number.isFinite(current)) {
|
|
13295
|
+
throw new Error("Cannot canonicalize a non-finite number");
|
|
13296
|
+
}
|
|
13297
|
+
return Object.is(current, -0) ? 0 : current;
|
|
13298
|
+
}
|
|
13299
|
+
if (Array.isArray(current)) return current.map(normalize);
|
|
13300
|
+
if (current && typeof current === "object") {
|
|
13301
|
+
return Object.fromEntries(
|
|
13302
|
+
Object.entries(current).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key.normalize("NFC"), normalize(item)])
|
|
13303
|
+
);
|
|
13304
|
+
}
|
|
13305
|
+
throw new Error(`Cannot canonicalize ${typeof current}`);
|
|
13306
|
+
};
|
|
13307
|
+
return JSON.stringify(normalize(value));
|
|
13308
|
+
}
|
|
13309
|
+
|
|
13011
13310
|
// src/effect-runtime.ts
|
|
13012
13311
|
function computeEffectKey(effect) {
|
|
13013
13312
|
const attachedClass = effect.className?.trim();
|
|
@@ -13072,6 +13371,45 @@ function resolveInvocationMode(context) {
|
|
|
13072
13371
|
}
|
|
13073
13372
|
return "execute";
|
|
13074
13373
|
}
|
|
13374
|
+
async function sha256Hex(value) {
|
|
13375
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
13376
|
+
"SHA-256",
|
|
13377
|
+
new TextEncoder().encode(value)
|
|
13378
|
+
);
|
|
13379
|
+
return Array.from(
|
|
13380
|
+
new Uint8Array(digest),
|
|
13381
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
13382
|
+
).join("");
|
|
13383
|
+
}
|
|
13384
|
+
async function resolveInvocationIdempotencyKey(request) {
|
|
13385
|
+
const supplied = request.context?.idempotencyKey?.trim();
|
|
13386
|
+
if (supplied) return supplied;
|
|
13387
|
+
const invocationId = request.context?.invocationId?.trim();
|
|
13388
|
+
if (!invocationId) {
|
|
13389
|
+
throw new Error(
|
|
13390
|
+
`Committed effect ${request.effectKey} requires an invocation id`
|
|
13391
|
+
);
|
|
13392
|
+
}
|
|
13393
|
+
const digest = await sha256Hex(
|
|
13394
|
+
canonicalizeCommitValue({
|
|
13395
|
+
sandboxId: request.context?.sandboxId || "",
|
|
13396
|
+
environmentId: request.context?.environmentId || "",
|
|
13397
|
+
effectKey: request.effectKey,
|
|
13398
|
+
invocationId,
|
|
13399
|
+
input: request.input
|
|
13400
|
+
})
|
|
13401
|
+
);
|
|
13402
|
+
return `gci_${digest}`;
|
|
13403
|
+
}
|
|
13404
|
+
function createUnavailableCommitContext(message) {
|
|
13405
|
+
const unavailable = async () => {
|
|
13406
|
+
throw new Error(message);
|
|
13407
|
+
};
|
|
13408
|
+
return {
|
|
13409
|
+
effect: unavailable,
|
|
13410
|
+
transition: unavailable
|
|
13411
|
+
};
|
|
13412
|
+
}
|
|
13075
13413
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
13076
13414
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
13077
13415
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -13143,22 +13481,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13143
13481
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
13144
13482
|
}
|
|
13145
13483
|
if (mode === "reverse") {
|
|
13484
|
+
const sourceEffectKey = request.context?.invocation?.sourceEffectKey?.trim();
|
|
13485
|
+
if (sourceEffectKey && sourceEffectKey !== request.effectKey) {
|
|
13486
|
+
return { effect, mode, handler: effect.handler };
|
|
13487
|
+
}
|
|
13488
|
+
const namedReverseHandler = request.context?.invocation?.reverseHandler?.trim() || behaviors.reverse?.handler?.trim();
|
|
13489
|
+
if (namedReverseHandler) {
|
|
13490
|
+
const reverseEffect = resolveReverseEffect(
|
|
13491
|
+
effectMap,
|
|
13492
|
+
effect,
|
|
13493
|
+
request,
|
|
13494
|
+
behaviors
|
|
13495
|
+
);
|
|
13496
|
+
if (reverseEffect) {
|
|
13497
|
+
return {
|
|
13498
|
+
effect: reverseEffect,
|
|
13499
|
+
mode,
|
|
13500
|
+
handler: reverseEffect.handler
|
|
13501
|
+
};
|
|
13502
|
+
}
|
|
13503
|
+
throw new Error(
|
|
13504
|
+
`Reverse effect ${namedReverseHandler} is not registered for ${request.effectKey}`
|
|
13505
|
+
);
|
|
13506
|
+
}
|
|
13146
13507
|
if (effect.reverseHandler) {
|
|
13147
13508
|
return { effect, mode, handler: effect.reverseHandler };
|
|
13148
13509
|
}
|
|
13149
|
-
const reverseEffect = resolveReverseEffect(
|
|
13150
|
-
effectMap,
|
|
13151
|
-
effect,
|
|
13152
|
-
request,
|
|
13153
|
-
behaviors
|
|
13154
|
-
);
|
|
13155
|
-
if (reverseEffect) {
|
|
13156
|
-
return {
|
|
13157
|
-
effect: reverseEffect,
|
|
13158
|
-
mode,
|
|
13159
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
13160
|
-
};
|
|
13161
|
-
}
|
|
13162
13510
|
throw new Error(
|
|
13163
13511
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
13164
13512
|
);
|
|
@@ -13259,18 +13607,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13259
13607
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
13260
13608
|
}
|
|
13261
13609
|
const resolved = resolveHandlerForMode(effectMap, effect, request);
|
|
13610
|
+
const isPreResolvedNamedReverse = resolved.mode === "reverse" && Boolean(request.context?.invocation?.sourceEffectKey?.trim()) && request.context?.invocation?.sourceEffectKey?.trim() !== request.effectKey;
|
|
13611
|
+
const resolvedEffectKey = computeEffectKey(resolved.effect);
|
|
13612
|
+
if (resolved.mode === "reverse" && resolved.effect !== effect && (effect.commit || resolved.effect.commit)) {
|
|
13613
|
+
throw new Error(
|
|
13614
|
+
`Committed named reverse effect ${resolvedEffectKey} must be reserved and dispatched using its own effect key; customer code was not invoked`
|
|
13615
|
+
);
|
|
13616
|
+
}
|
|
13617
|
+
const declaration = resolved.effect.commit;
|
|
13618
|
+
const commitTransport = options.commit;
|
|
13619
|
+
const commitRequired = Boolean(declaration) && (resolved.mode === "execute" || resolved.mode === "reverse");
|
|
13620
|
+
if (commitRequired && !commitTransport) {
|
|
13621
|
+
throw new Error(
|
|
13622
|
+
`Committed effect ${request.effectKey} has no durable commit transport; customer code was not invoked`
|
|
13623
|
+
);
|
|
13624
|
+
}
|
|
13625
|
+
if (commitRequired && declaration?.kind === "transition" && !request.context?.invocation?.transition) {
|
|
13626
|
+
throw new Error(
|
|
13627
|
+
`Transition effect ${request.effectKey} has no resolved transition context; customer code was not invoked`
|
|
13628
|
+
);
|
|
13629
|
+
}
|
|
13630
|
+
const idempotencyKey = commitRequired ? await resolveInvocationIdempotencyKey(request) : request.context?.idempotencyKey;
|
|
13631
|
+
let commitStarted = false;
|
|
13632
|
+
let commitPromise = null;
|
|
13633
|
+
const beginCommit = (requestedKind, productResult) => {
|
|
13634
|
+
if (!declaration || !commitRequired || !commitTransport) {
|
|
13635
|
+
return Promise.reject(
|
|
13636
|
+
new Error(
|
|
13637
|
+
`Effect ${request.effectKey} does not declare an active ${requestedKind} commit`
|
|
13638
|
+
)
|
|
13639
|
+
);
|
|
13640
|
+
}
|
|
13641
|
+
if (declaration.kind !== requestedKind) {
|
|
13642
|
+
return Promise.reject(
|
|
13643
|
+
new Error(
|
|
13644
|
+
`Effect ${request.effectKey} declares ${declaration.kind} commit, not ${requestedKind}`
|
|
13645
|
+
)
|
|
13646
|
+
);
|
|
13647
|
+
}
|
|
13648
|
+
if (commitStarted) {
|
|
13649
|
+
return Promise.reject(
|
|
13650
|
+
new Error(
|
|
13651
|
+
`Effect invocation ${request.context?.invocationId || request.effectKey} already emitted its commit`
|
|
13652
|
+
)
|
|
13653
|
+
);
|
|
13654
|
+
}
|
|
13655
|
+
commitStarted = true;
|
|
13656
|
+
commitPromise = (async () => {
|
|
13657
|
+
let projection;
|
|
13658
|
+
try {
|
|
13659
|
+
projection = declaration.project(productResult);
|
|
13660
|
+
validateProjectionResult(declaration, projection);
|
|
13661
|
+
} catch (error) {
|
|
13662
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13663
|
+
if (commitTransport.mappingFailed) {
|
|
13664
|
+
await commitTransport.mappingFailed({
|
|
13665
|
+
effectKey: request.effectKey,
|
|
13666
|
+
effectName: request.effectName,
|
|
13667
|
+
invocationId: request.context?.invocationId || "",
|
|
13668
|
+
idempotencyKey: idempotencyKey || "",
|
|
13669
|
+
environmentId: request.context?.environmentId || "",
|
|
13670
|
+
message
|
|
13671
|
+
});
|
|
13672
|
+
}
|
|
13673
|
+
throw new Error(
|
|
13674
|
+
`Product mutation may have succeeded, but its commit projection is invalid: ${message}`
|
|
13675
|
+
);
|
|
13676
|
+
}
|
|
13677
|
+
if (requestedKind === "transition") {
|
|
13678
|
+
const transition = request.context?.invocation?.transition;
|
|
13679
|
+
const outcome = projection.outcome;
|
|
13680
|
+
if (!transition || !outcome?.key) {
|
|
13681
|
+
throw new Error(
|
|
13682
|
+
`Transition effect ${request.effectKey} did not resolve an authored outcome`
|
|
13683
|
+
);
|
|
13684
|
+
}
|
|
13685
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
13686
|
+
transition.outcomes,
|
|
13687
|
+
outcome.key
|
|
13688
|
+
)) {
|
|
13689
|
+
throw new Error(
|
|
13690
|
+
`Transition ${transition.machine}.${transition.transition} does not declare outcome ${outcome.key}`
|
|
13691
|
+
);
|
|
13692
|
+
}
|
|
13693
|
+
}
|
|
13694
|
+
const invocationId = request.context?.invocationId || "";
|
|
13695
|
+
const environmentId = request.context?.environmentId || "";
|
|
13696
|
+
const sandboxId = request.context?.sandboxId || "";
|
|
13697
|
+
if (!invocationId || !environmentId || !sandboxId || !idempotencyKey) {
|
|
13698
|
+
throw new Error(
|
|
13699
|
+
`Committed effect ${request.effectKey} is missing its trusted invocation scope`
|
|
13700
|
+
);
|
|
13701
|
+
}
|
|
13702
|
+
const commitRequest = {
|
|
13703
|
+
kind: requestedKind,
|
|
13704
|
+
effectKey: request.effectKey,
|
|
13705
|
+
effectName: request.effectName,
|
|
13706
|
+
operationLabel: resolved.effect.label || resolved.effect.name,
|
|
13707
|
+
invocationId,
|
|
13708
|
+
idempotencyKey,
|
|
13709
|
+
sandboxId,
|
|
13710
|
+
environmentId,
|
|
13711
|
+
...request.context?.sessionId ? { sessionId: request.context.sessionId } : {},
|
|
13712
|
+
...request.context?.jobId ? { jobId: request.context.jobId } : {},
|
|
13713
|
+
...request.context?.buildId ? { buildId: request.context.buildId } : {},
|
|
13714
|
+
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13715
|
+
projection,
|
|
13716
|
+
...requestedKind === "transition" && request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13717
|
+
};
|
|
13718
|
+
const receipt = await commitTransport.persist(commitRequest);
|
|
13719
|
+
await options.commitAcknowledged?.(receipt);
|
|
13720
|
+
return receipt;
|
|
13721
|
+
})();
|
|
13722
|
+
return commitPromise;
|
|
13723
|
+
};
|
|
13724
|
+
const commitContext = commitRequired ? {
|
|
13725
|
+
effect: (productResult) => beginCommit("effect", productResult),
|
|
13726
|
+
transition: (productResult) => beginCommit("transition", productResult)
|
|
13727
|
+
} : createUnavailableCommitContext(
|
|
13728
|
+
`Effect ${request.effectKey} is not executing a declared product commit`
|
|
13729
|
+
);
|
|
13262
13730
|
const context = {
|
|
13263
13731
|
...request.context || {},
|
|
13732
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
13733
|
+
commit: commitContext,
|
|
13264
13734
|
behaviors: normalizeEffectBehaviors(
|
|
13265
13735
|
request.context?.behaviors || effect.metamodels || void 0
|
|
13266
13736
|
),
|
|
13267
13737
|
invocation: {
|
|
13268
13738
|
mode: resolved.mode,
|
|
13269
|
-
|
|
13270
|
-
|
|
13739
|
+
...commitRequired && declaration ? { commitKind: declaration.kind } : {},
|
|
13740
|
+
sourceEffectKey: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectKey || request.effectKey : request.effectKey,
|
|
13741
|
+
sourceEffectName: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectName || request.effectName : request.effectName,
|
|
13271
13742
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
13272
13743
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13273
|
-
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {}
|
|
13744
|
+
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {},
|
|
13745
|
+
...request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13274
13746
|
}
|
|
13275
13747
|
};
|
|
13276
13748
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -13294,6 +13766,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13294
13766
|
handlerFailed = true;
|
|
13295
13767
|
handlerError = error;
|
|
13296
13768
|
}
|
|
13769
|
+
let commitError;
|
|
13770
|
+
let commitFailed = false;
|
|
13771
|
+
if (commitPromise) {
|
|
13772
|
+
try {
|
|
13773
|
+
await commitPromise;
|
|
13774
|
+
} catch (error) {
|
|
13775
|
+
commitFailed = true;
|
|
13776
|
+
commitError = error;
|
|
13777
|
+
}
|
|
13778
|
+
}
|
|
13297
13779
|
let feedbackError;
|
|
13298
13780
|
let feedbackFailed = false;
|
|
13299
13781
|
if (feedbackContext) {
|
|
@@ -13307,9 +13789,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13307
13789
|
if (handlerFailed) {
|
|
13308
13790
|
throw handlerError;
|
|
13309
13791
|
}
|
|
13792
|
+
if (commitFailed) {
|
|
13793
|
+
throw commitError;
|
|
13794
|
+
}
|
|
13310
13795
|
if (feedbackFailed) {
|
|
13311
13796
|
throw feedbackError;
|
|
13312
13797
|
}
|
|
13798
|
+
if (commitRequired && !commitStarted) {
|
|
13799
|
+
throw new Error(
|
|
13800
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
13801
|
+
);
|
|
13802
|
+
}
|
|
13313
13803
|
return handlerResult;
|
|
13314
13804
|
}
|
|
13315
13805
|
|
|
@@ -13378,12 +13868,7 @@ function toRecordSearchResult(className, node) {
|
|
|
13378
13868
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
13379
13869
|
);
|
|
13380
13870
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
13381
|
-
const
|
|
13382
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path)) {
|
|
13383
|
-
return null;
|
|
13384
|
-
}
|
|
13385
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
13386
|
-
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
13871
|
+
const label = typeof node.label === "string" && node.label.trim() ? node.label.trim() : path || id;
|
|
13387
13872
|
return {
|
|
13388
13873
|
path,
|
|
13389
13874
|
className,
|
|
@@ -13393,30 +13878,6 @@ function toRecordSearchResult(className, node) {
|
|
|
13393
13878
|
fields
|
|
13394
13879
|
};
|
|
13395
13880
|
}
|
|
13396
|
-
function isPlaceholderRecordLabel(label, id, path) {
|
|
13397
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
13398
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path);
|
|
13399
|
-
}
|
|
13400
|
-
function displayLabelFromFields(fields) {
|
|
13401
|
-
const preferredFieldNames = [
|
|
13402
|
-
"name",
|
|
13403
|
-
"title",
|
|
13404
|
-
"label",
|
|
13405
|
-
"display_name",
|
|
13406
|
-
"file_name",
|
|
13407
|
-
"number",
|
|
13408
|
-
"code"
|
|
13409
|
-
];
|
|
13410
|
-
for (const preferred of preferredFieldNames) {
|
|
13411
|
-
const match = fields.find(
|
|
13412
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
13413
|
-
);
|
|
13414
|
-
if (typeof match?.value === "string") {
|
|
13415
|
-
return match.value.trim();
|
|
13416
|
-
}
|
|
13417
|
-
}
|
|
13418
|
-
return null;
|
|
13419
|
-
}
|
|
13420
13881
|
function normalizeRecordSearchText(value) {
|
|
13421
13882
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
13422
13883
|
}
|
|
@@ -14379,7 +14840,8 @@ function normalizeStateMachines(values) {
|
|
|
14379
14840
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
14380
14841
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
14381
14842
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
14382
|
-
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
14843
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json),
|
|
14844
|
+
outcomes: parseJsonRecord(transition?.outcomes) || parseJsonRecord(transition?.outcomes_json)
|
|
14383
14845
|
})).filter(
|
|
14384
14846
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
14385
14847
|
);
|
|
@@ -14472,6 +14934,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14472
14934
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
14473
14935
|
);
|
|
14474
14936
|
}
|
|
14937
|
+
if (transition.outcomes) {
|
|
14938
|
+
args.push(
|
|
14939
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
14940
|
+
);
|
|
14941
|
+
}
|
|
14475
14942
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
14476
14943
|
}
|
|
14477
14944
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -14534,7 +15001,7 @@ function buildMachineTypes(classSummary, machine) {
|
|
|
14534
15001
|
];
|
|
14535
15002
|
}
|
|
14536
15003
|
function buildMachineMethods(classSummary, machine) {
|
|
14537
|
-
const
|
|
15004
|
+
const stateName2 = stateTypeName(classSummary.name, machine.name);
|
|
14538
15005
|
const transitionName = transitionTypeName(classSummary.name, machine.name);
|
|
14539
15006
|
pathTypeName(classSummary.name, machine.name);
|
|
14540
15007
|
const docsPrefix = `${classSummary.name}.${machine.name}`;
|
|
@@ -14544,12 +15011,12 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14544
15011
|
docs: [`Get the current ${docsPrefix} state.`],
|
|
14545
15012
|
static: false,
|
|
14546
15013
|
params: [],
|
|
14547
|
-
returnType: `Promise<${
|
|
15014
|
+
returnType: `Promise<${stateName2} | null>`,
|
|
14548
15015
|
runtime: {
|
|
14549
15016
|
kind: "state_machine",
|
|
14550
15017
|
machineName: machine.name,
|
|
14551
15018
|
className: classSummary.name,
|
|
14552
|
-
stateTypeName:
|
|
15019
|
+
stateTypeName: stateName2,
|
|
14553
15020
|
transitionTypeName: transitionName,
|
|
14554
15021
|
operation: "get_current"
|
|
14555
15022
|
}
|
|
@@ -14560,13 +15027,13 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14560
15027
|
`Reach a ${docsPrefix} state through the shortest allowed transition path.`
|
|
14561
15028
|
],
|
|
14562
15029
|
static: false,
|
|
14563
|
-
params: [{ name: "target", type:
|
|
15030
|
+
params: [{ name: "target", type: stateName2 }],
|
|
14564
15031
|
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
14565
15032
|
runtime: {
|
|
14566
15033
|
kind: "state_machine",
|
|
14567
15034
|
machineName: machine.name,
|
|
14568
15035
|
className: classSummary.name,
|
|
14569
|
-
stateTypeName:
|
|
15036
|
+
stateTypeName: stateName2,
|
|
14570
15037
|
transitionTypeName: transitionName,
|
|
14571
15038
|
operation: "reach"
|
|
14572
15039
|
}
|
|
@@ -14581,7 +15048,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14581
15048
|
kind: "state_machine",
|
|
14582
15049
|
machineName: machine.name,
|
|
14583
15050
|
className: classSummary.name,
|
|
14584
|
-
stateTypeName:
|
|
15051
|
+
stateTypeName: stateName2,
|
|
14585
15052
|
transitionTypeName: transitionName,
|
|
14586
15053
|
operation: "list_transitions"
|
|
14587
15054
|
}
|
|
@@ -14591,12 +15058,12 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14591
15058
|
docs: [`List reachable states for ${docsPrefix} from the current state.`],
|
|
14592
15059
|
static: false,
|
|
14593
15060
|
params: [],
|
|
14594
|
-
returnType: `Promise<${
|
|
15061
|
+
returnType: `Promise<${stateName2}[]>`,
|
|
14595
15062
|
runtime: {
|
|
14596
15063
|
kind: "state_machine",
|
|
14597
15064
|
machineName: machine.name,
|
|
14598
15065
|
className: classSummary.name,
|
|
14599
|
-
stateTypeName:
|
|
15066
|
+
stateTypeName: stateName2,
|
|
14600
15067
|
transitionTypeName: transitionName,
|
|
14601
15068
|
operation: "list_reachable_states"
|
|
14602
15069
|
}
|
|
@@ -14611,7 +15078,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14611
15078
|
kind: "state_machine",
|
|
14612
15079
|
machineName: machine.name,
|
|
14613
15080
|
className: classSummary.name,
|
|
14614
|
-
stateTypeName:
|
|
15081
|
+
stateTypeName: stateName2,
|
|
14615
15082
|
transitionTypeName: transitionName,
|
|
14616
15083
|
operation: "is_final"
|
|
14617
15084
|
}
|
|
@@ -14622,13 +15089,13 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14622
15089
|
`List shortest transition paths from the current ${docsPrefix} state to a target state.`
|
|
14623
15090
|
],
|
|
14624
15091
|
static: false,
|
|
14625
|
-
params: [{ name: "target", type:
|
|
14626
|
-
returnType: `Promise<Array<{ states: ${
|
|
15092
|
+
params: [{ name: "target", type: stateName2 }],
|
|
15093
|
+
returnType: `Promise<Array<{ states: ${stateName2}[]; transitions: ${transitionName}[] }>>`,
|
|
14627
15094
|
runtime: {
|
|
14628
15095
|
kind: "state_machine",
|
|
14629
15096
|
machineName: machine.name,
|
|
14630
15097
|
className: classSummary.name,
|
|
14631
|
-
stateTypeName:
|
|
15098
|
+
stateTypeName: stateName2,
|
|
14632
15099
|
transitionTypeName: transitionName,
|
|
14633
15100
|
operation: "paths_to"
|
|
14634
15101
|
}
|
|
@@ -14652,7 +15119,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14652
15119
|
kind: "state_machine",
|
|
14653
15120
|
machineName: machine.name,
|
|
14654
15121
|
className: classSummary.name,
|
|
14655
|
-
stateTypeName:
|
|
15122
|
+
stateTypeName: stateName2,
|
|
14656
15123
|
transitionTypeName: transitionName,
|
|
14657
15124
|
operation: "reach",
|
|
14658
15125
|
targetState: stateNameValue,
|
|
@@ -14671,7 +15138,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14671
15138
|
kind: "state_machine",
|
|
14672
15139
|
machineName: machine.name,
|
|
14673
15140
|
className: classSummary.name,
|
|
14674
|
-
stateTypeName:
|
|
15141
|
+
stateTypeName: stateName2,
|
|
14675
15142
|
transitionTypeName: transitionName,
|
|
14676
15143
|
operation: "prepare_reach",
|
|
14677
15144
|
targetState: stateNameValue,
|
|
@@ -14684,7 +15151,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14684
15151
|
kind: "state_machine",
|
|
14685
15152
|
machineName: machine.name,
|
|
14686
15153
|
className: classSummary.name,
|
|
14687
|
-
stateTypeName:
|
|
15154
|
+
stateTypeName: stateName2,
|
|
14688
15155
|
transitionTypeName: transitionName,
|
|
14689
15156
|
operation: "prepare_create_reach",
|
|
14690
15157
|
targetState: stateNameValue,
|
|
@@ -14750,7 +15217,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14750
15217
|
name: String!
|
|
14751
15218
|
state_machine: StateMachine!
|
|
14752
15219
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
14753
|
-
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!
|
|
15220
|
+
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!
|
|
14754
15221
|
activate_transition(name: String!): StateMachineMutation!
|
|
14755
15222
|
}
|
|
14756
15223
|
|
|
@@ -14767,6 +15234,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14767
15234
|
type StateMachineSnapshotMutation {
|
|
14768
15235
|
snapshot: StateMachineSnapshot!
|
|
14769
15236
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15237
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
14770
15238
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
14771
15239
|
}
|
|
14772
15240
|
|
|
@@ -14813,6 +15281,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14813
15281
|
permission_json: String
|
|
14814
15282
|
risk: String
|
|
14815
15283
|
expected_outcome_json: String
|
|
15284
|
+
outcomes_json: String
|
|
14816
15285
|
}
|
|
14817
15286
|
|
|
14818
15287
|
type StateMachinePath {
|
|
@@ -14823,6 +15292,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14823
15292
|
type StateMachineTransitionEvent {
|
|
14824
15293
|
sequence: Int!
|
|
14825
15294
|
occurred_at: Float!
|
|
15295
|
+
commit_id: String
|
|
15296
|
+
outcome: String
|
|
15297
|
+
source_version: String
|
|
15298
|
+
projected_from_mismatch: String
|
|
14826
15299
|
transition: StateMachineTransition!
|
|
14827
15300
|
from: StateMachineState!
|
|
14828
15301
|
to: StateMachineState!
|
|
@@ -14888,7 +15361,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14888
15361
|
requirements_json,
|
|
14889
15362
|
permission_json,
|
|
14890
15363
|
risk,
|
|
14891
|
-
expected_outcome_json
|
|
15364
|
+
expected_outcome_json,
|
|
15365
|
+
outcomes_json
|
|
14892
15366
|
}) => {
|
|
14893
15367
|
await run(
|
|
14894
15368
|
value.target.add_state_machine_transition(
|
|
@@ -14904,7 +15378,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14904
15378
|
requirementsJson: requirements_json,
|
|
14905
15379
|
permissionJson: permission_json,
|
|
14906
15380
|
risk,
|
|
14907
|
-
expectedOutcomeJson: expected_outcome_json
|
|
15381
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
15382
|
+
outcomesJson: outcomes_json
|
|
14908
15383
|
}
|
|
14909
15384
|
)
|
|
14910
15385
|
);
|
|
@@ -14925,6 +15400,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14925
15400
|
);
|
|
14926
15401
|
return value;
|
|
14927
15402
|
},
|
|
15403
|
+
commit_transition: async (value, { name, outcome, to, commit_id, source_version }) => {
|
|
15404
|
+
await run(
|
|
15405
|
+
value.target.commit_state_machine_transition(
|
|
15406
|
+
value.name,
|
|
15407
|
+
name,
|
|
15408
|
+
outcome,
|
|
15409
|
+
to,
|
|
15410
|
+
commit_id,
|
|
15411
|
+
source_version
|
|
15412
|
+
)
|
|
15413
|
+
);
|
|
15414
|
+
return value;
|
|
15415
|
+
},
|
|
14928
15416
|
observe_state: async (value, { state, force, source }) => {
|
|
14929
15417
|
await run(
|
|
14930
15418
|
value.target.observe_state_machine_state(
|
|
@@ -14954,7 +15442,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14954
15442
|
requirements_json: (value) => value.requirements_json || null,
|
|
14955
15443
|
permission_json: (value) => value.permission_json || null,
|
|
14956
15444
|
risk: (value) => value.risk || null,
|
|
14957
|
-
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
15445
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null,
|
|
15446
|
+
outcomes_json: (value) => value.outcomes_json || null
|
|
14958
15447
|
},
|
|
14959
15448
|
StateMachinePath: {
|
|
14960
15449
|
states: (value) => value.states,
|
|
@@ -14963,6 +15452,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14963
15452
|
StateMachineTransitionEvent: {
|
|
14964
15453
|
sequence: (value) => value.sequence,
|
|
14965
15454
|
occurred_at: (value) => value.occurred_at,
|
|
15455
|
+
commit_id: (value) => value.commit_id || null,
|
|
15456
|
+
outcome: (value) => value.outcome || null,
|
|
15457
|
+
source_version: (value) => value.source_version || null,
|
|
15458
|
+
projected_from_mismatch: (value) => value.projected_from_mismatch || null,
|
|
14966
15459
|
transition: (value) => value.transition,
|
|
14967
15460
|
from: (value) => value.from,
|
|
14968
15461
|
to: (value) => value.to
|
|
@@ -15036,6 +15529,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15036
15529
|
permission_json
|
|
15037
15530
|
risk
|
|
15038
15531
|
expected_outcome_json
|
|
15532
|
+
outcomes_json
|
|
15039
15533
|
}
|
|
15040
15534
|
}`
|
|
15041
15535
|
]
|
|
@@ -15763,6 +16257,133 @@ var Environment = class _Environment {
|
|
|
15763
16257
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
15764
16258
|
};
|
|
15765
16259
|
}
|
|
16260
|
+
/**
|
|
16261
|
+
* Acknowledge a declared product mutation that already happened outside a
|
|
16262
|
+
* Granular-run effect (for example, in a webhook consumer). These methods
|
|
16263
|
+
* run the declaration's pure projection mapper; they never call its handler.
|
|
16264
|
+
*/
|
|
16265
|
+
get commit() {
|
|
16266
|
+
return {
|
|
16267
|
+
effect: async (effect, productResult, options = {}) => this.persistExternalEffect(effect, productResult, options),
|
|
16268
|
+
transition: async (transition, productResult, options = {}) => this.persistExternalTransition(transition, productResult, options),
|
|
16269
|
+
get: async (commitId) => this.controlPlaneRequest(
|
|
16270
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16271
|
+
commitId
|
|
16272
|
+
)}`
|
|
16273
|
+
),
|
|
16274
|
+
retry: async (commitId) => this.controlPlaneRequest(
|
|
16275
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16276
|
+
commitId
|
|
16277
|
+
)}/retry`,
|
|
16278
|
+
{ method: "POST" }
|
|
16279
|
+
)
|
|
16280
|
+
};
|
|
16281
|
+
}
|
|
16282
|
+
/** Inspect or retry the agent synchronization of a product snapshot. */
|
|
16283
|
+
get observation() {
|
|
16284
|
+
return {
|
|
16285
|
+
get: async (observationId) => this.controlPlaneRequest(
|
|
16286
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16287
|
+
observationId
|
|
16288
|
+
)}`
|
|
16289
|
+
),
|
|
16290
|
+
retry: async (observationId) => this.controlPlaneRequest(
|
|
16291
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16292
|
+
observationId
|
|
16293
|
+
)}/retry`,
|
|
16294
|
+
{ method: "POST" }
|
|
16295
|
+
)
|
|
16296
|
+
};
|
|
16297
|
+
}
|
|
16298
|
+
async persistExternalEffect(effect, productResult, options) {
|
|
16299
|
+
const projection = effect.commit.project(productResult);
|
|
16300
|
+
validateProjectionResult(effect.commit, projection);
|
|
16301
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16302
|
+
return this.controlPlaneRequest(
|
|
16303
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16304
|
+
{
|
|
16305
|
+
method: "POST",
|
|
16306
|
+
body: JSON.stringify({
|
|
16307
|
+
kind: "effect",
|
|
16308
|
+
effectKey: computeEffectKey2(effect),
|
|
16309
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16310
|
+
projection
|
|
16311
|
+
})
|
|
16312
|
+
}
|
|
16313
|
+
);
|
|
16314
|
+
}
|
|
16315
|
+
async persistExternalTransition(transition, productResult, options) {
|
|
16316
|
+
const metadata = getDefinedTransitionMetadata(transition);
|
|
16317
|
+
if (!metadata) {
|
|
16318
|
+
throw new Error(
|
|
16319
|
+
"environment.commit.transition requires a transition returned by defineStateMachine"
|
|
16320
|
+
);
|
|
16321
|
+
}
|
|
16322
|
+
const projection = transition.effect.commit.project(productResult);
|
|
16323
|
+
validateProjectionResult(transition.effect.commit, projection);
|
|
16324
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16325
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
16326
|
+
transition.outcomes,
|
|
16327
|
+
projection.outcome.key
|
|
16328
|
+
)) {
|
|
16329
|
+
throw new Error(
|
|
16330
|
+
`Transition ${metadata.machine}.${metadata.transition} does not declare outcome ${projection.outcome.key}`
|
|
16331
|
+
);
|
|
16332
|
+
}
|
|
16333
|
+
if (!projection.primaryTarget) {
|
|
16334
|
+
throw new Error(
|
|
16335
|
+
"An external transition projection requires primaryTarget to identify the transitioned product record"
|
|
16336
|
+
);
|
|
16337
|
+
}
|
|
16338
|
+
return this.controlPlaneRequest(
|
|
16339
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16340
|
+
{
|
|
16341
|
+
method: "POST",
|
|
16342
|
+
body: JSON.stringify({
|
|
16343
|
+
kind: "transition",
|
|
16344
|
+
effectKey: computeEffectKey2(transition.effect),
|
|
16345
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16346
|
+
projection,
|
|
16347
|
+
transition: {
|
|
16348
|
+
className: projection.primaryTarget.className,
|
|
16349
|
+
objectId: projection.primaryTarget.id,
|
|
16350
|
+
...projection.primaryTarget.path ? { objectPath: projection.primaryTarget.path } : {},
|
|
16351
|
+
machine: metadata.machine,
|
|
16352
|
+
transition: metadata.transition,
|
|
16353
|
+
from: transition.from
|
|
16354
|
+
}
|
|
16355
|
+
})
|
|
16356
|
+
}
|
|
16357
|
+
);
|
|
16358
|
+
}
|
|
16359
|
+
requireExternalIdentity(sourceVersion, options) {
|
|
16360
|
+
if (!options.sourceEventId?.trim() && !sourceVersion?.trim()) {
|
|
16361
|
+
throw new Error(
|
|
16362
|
+
"An external commit requires sourceEventId or a source version from its projection mapper"
|
|
16363
|
+
);
|
|
16364
|
+
}
|
|
16365
|
+
}
|
|
16366
|
+
/**
|
|
16367
|
+
* Synchronize a versioned product snapshot without claiming an effect or
|
|
16368
|
+
* lifecycle transition. This records no transition history.
|
|
16369
|
+
*/
|
|
16370
|
+
async observe(mapper, productResult) {
|
|
16371
|
+
const declaration = { kind: "effect"};
|
|
16372
|
+
const projection = mapper(productResult);
|
|
16373
|
+
validateProjectionResult(declaration, projection);
|
|
16374
|
+
if (!projection.source.version?.trim()) {
|
|
16375
|
+
throw new Error(
|
|
16376
|
+
"environment.observe requires a monotonic source version or serialized adapter sequence"
|
|
16377
|
+
);
|
|
16378
|
+
}
|
|
16379
|
+
return this.controlPlaneRequest(
|
|
16380
|
+
`/control/environments/${this.environmentId}/observations`,
|
|
16381
|
+
{
|
|
16382
|
+
method: "POST",
|
|
16383
|
+
body: JSON.stringify({ projection })
|
|
16384
|
+
}
|
|
16385
|
+
);
|
|
16386
|
+
}
|
|
15766
16387
|
/**
|
|
15767
16388
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
15768
16389
|
* own the customer application's state machine.
|
|
@@ -15802,8 +16423,8 @@ var Environment = class _Environment {
|
|
|
15802
16423
|
* `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
|
|
15803
16424
|
*/
|
|
15804
16425
|
state(target) {
|
|
15805
|
-
const observe = async (machineName,
|
|
15806
|
-
const observedState = input.observedState || input.state ||
|
|
16426
|
+
const observe = async (machineName, stateName2, input = {}) => {
|
|
16427
|
+
const observedState = input.observedState || input.state || stateName2;
|
|
15807
16428
|
if (!observedState) {
|
|
15808
16429
|
throw new Error("State observation requires a target state");
|
|
15809
16430
|
}
|
|
@@ -15829,7 +16450,7 @@ var Environment = class _Environment {
|
|
|
15829
16450
|
{
|
|
15830
16451
|
get: (_machineTarget, stateProperty) => {
|
|
15831
16452
|
if (stateProperty === "to") {
|
|
15832
|
-
return (
|
|
16453
|
+
return (stateName2, input) => observe(machineProperty, stateName2, input || {});
|
|
15833
16454
|
}
|
|
15834
16455
|
if (typeof stateProperty !== "string") return void 0;
|
|
15835
16456
|
return (input) => observe(
|
|
@@ -17169,6 +17790,14 @@ var EnvironmentSession = class extends Session {
|
|
|
17169
17790
|
}
|
|
17170
17791
|
};
|
|
17171
17792
|
}
|
|
17793
|
+
get mutations() {
|
|
17794
|
+
return {
|
|
17795
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
17796
|
+
"/mutations",
|
|
17797
|
+
options
|
|
17798
|
+
)
|
|
17799
|
+
};
|
|
17800
|
+
}
|
|
17172
17801
|
get artifacts() {
|
|
17173
17802
|
return {
|
|
17174
17803
|
list: (options = {}) => {
|
|
@@ -18668,6 +19297,7 @@ var Granular = class _Granular {
|
|
|
18668
19297
|
const serialized = {
|
|
18669
19298
|
effectKey: computeEffectKey2(effect),
|
|
18670
19299
|
name: effect.name,
|
|
19300
|
+
...effect.label ? { label: effect.label } : {},
|
|
18671
19301
|
description: effect.description,
|
|
18672
19302
|
inputSchema: effect.inputSchema,
|
|
18673
19303
|
stability: effect.stability || "stable",
|
|
@@ -18691,6 +19321,9 @@ var Granular = class _Granular {
|
|
|
18691
19321
|
if (effect.metamodels !== void 0) {
|
|
18692
19322
|
serialized.metamodels = effect.metamodels;
|
|
18693
19323
|
}
|
|
19324
|
+
if (effect.commit !== void 0) {
|
|
19325
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
19326
|
+
}
|
|
18694
19327
|
return serialized;
|
|
18695
19328
|
}
|
|
18696
19329
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -18911,16 +19544,60 @@ var Granular = class _Granular {
|
|
|
18911
19544
|
};
|
|
18912
19545
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
18913
19546
|
const request = params;
|
|
18914
|
-
|
|
19547
|
+
let commitReceipt;
|
|
19548
|
+
const result = await invokeRegisteredEffect(
|
|
18915
19549
|
this.getSandboxEffectMap(sandboxId),
|
|
18916
19550
|
request,
|
|
18917
19551
|
{
|
|
19552
|
+
commitAcknowledged: (receipt) => {
|
|
19553
|
+
commitReceipt = receipt;
|
|
19554
|
+
},
|
|
19555
|
+
commit: {
|
|
19556
|
+
persist: (commitRequest) => this.request(
|
|
19557
|
+
`/control/environments/${encodeURIComponent(
|
|
19558
|
+
commitRequest.environmentId
|
|
19559
|
+
)}/commits`,
|
|
19560
|
+
{
|
|
19561
|
+
method: "POST",
|
|
19562
|
+
body: JSON.stringify({
|
|
19563
|
+
kind: commitRequest.kind,
|
|
19564
|
+
invocationId: commitRequest.invocationId,
|
|
19565
|
+
idempotencyKey: commitRequest.idempotencyKey,
|
|
19566
|
+
effectKey: commitRequest.effectKey,
|
|
19567
|
+
projection: commitRequest.projection
|
|
19568
|
+
})
|
|
19569
|
+
}
|
|
19570
|
+
),
|
|
19571
|
+
mappingFailed: (failure) => this.request(
|
|
19572
|
+
`/control/environments/${encodeURIComponent(
|
|
19573
|
+
failure.environmentId
|
|
19574
|
+
)}/commit-invocations/${encodeURIComponent(
|
|
19575
|
+
failure.invocationId
|
|
19576
|
+
)}`,
|
|
19577
|
+
{
|
|
19578
|
+
method: "PATCH",
|
|
19579
|
+
body: JSON.stringify({
|
|
19580
|
+
status: "mapping_failed",
|
|
19581
|
+
error: {
|
|
19582
|
+
code: "commit_projection_mapping_failed",
|
|
19583
|
+
message: failure.message,
|
|
19584
|
+
retryable: false
|
|
19585
|
+
}
|
|
19586
|
+
})
|
|
19587
|
+
}
|
|
19588
|
+
)
|
|
19589
|
+
},
|
|
18918
19590
|
feedback: {
|
|
18919
19591
|
invocationId: request.callId,
|
|
18920
19592
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
18921
19593
|
}
|
|
18922
19594
|
}
|
|
18923
19595
|
);
|
|
19596
|
+
return {
|
|
19597
|
+
__granularEffectInvocationResult: true,
|
|
19598
|
+
result,
|
|
19599
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
19600
|
+
};
|
|
18924
19601
|
});
|
|
18925
19602
|
wsClient.on("open", () => {
|
|
18926
19603
|
void this.synchronizeEffectHost(host).catch((error) => {
|
|
@@ -19492,6 +20169,275 @@ var Granular = class _Granular {
|
|
|
19492
20169
|
}
|
|
19493
20170
|
};
|
|
19494
20171
|
|
|
20172
|
+
// src/record-snapshot-projection.ts
|
|
20173
|
+
function stableValue(value) {
|
|
20174
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
20175
|
+
if (value && typeof value === "object") {
|
|
20176
|
+
return Object.fromEntries(
|
|
20177
|
+
Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stableValue(item)])
|
|
20178
|
+
);
|
|
20179
|
+
}
|
|
20180
|
+
return value;
|
|
20181
|
+
}
|
|
20182
|
+
function stableJson(value) {
|
|
20183
|
+
return JSON.stringify(stableValue(value));
|
|
20184
|
+
}
|
|
20185
|
+
function recordKey(record) {
|
|
20186
|
+
return `${record.className}\0${record.id}`;
|
|
20187
|
+
}
|
|
20188
|
+
function recordReference(className, id) {
|
|
20189
|
+
return { className, id };
|
|
20190
|
+
}
|
|
20191
|
+
function changedRecordSnapshots(beforeRecords, afterRecords) {
|
|
20192
|
+
const before = new Map(
|
|
20193
|
+
beforeRecords.map((record) => [recordKey(record), record])
|
|
20194
|
+
);
|
|
20195
|
+
const after = new Map(
|
|
20196
|
+
afterRecords.map((record) => [recordKey(record), record])
|
|
20197
|
+
);
|
|
20198
|
+
const changedKeys = /* @__PURE__ */ new Set();
|
|
20199
|
+
for (const key of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) {
|
|
20200
|
+
if (stableJson(before.get(key)) !== stableJson(after.get(key))) {
|
|
20201
|
+
changedKeys.add(key);
|
|
20202
|
+
}
|
|
20203
|
+
}
|
|
20204
|
+
return {
|
|
20205
|
+
beforeRecords: beforeRecords.filter(
|
|
20206
|
+
(record) => changedKeys.has(recordKey(record))
|
|
20207
|
+
),
|
|
20208
|
+
afterRecords: afterRecords.filter(
|
|
20209
|
+
(record) => changedKeys.has(recordKey(record))
|
|
20210
|
+
)
|
|
20211
|
+
};
|
|
20212
|
+
}
|
|
20213
|
+
function relationshipTargets(manifest) {
|
|
20214
|
+
const targets = /* @__PURE__ */ new Map();
|
|
20215
|
+
for (const volume of manifest.volumes) {
|
|
20216
|
+
for (const operation of volume.operations) {
|
|
20217
|
+
const relationship = operation.defineRelationship;
|
|
20218
|
+
if (!relationship) continue;
|
|
20219
|
+
targets.set(`${relationship.left}\0${relationship.leftSubmodel}`, {
|
|
20220
|
+
className: relationship.right
|
|
20221
|
+
});
|
|
20222
|
+
targets.set(`${relationship.right}\0${relationship.rightSubmodel}`, {
|
|
20223
|
+
className: relationship.left
|
|
20224
|
+
});
|
|
20225
|
+
}
|
|
20226
|
+
}
|
|
20227
|
+
return targets;
|
|
20228
|
+
}
|
|
20229
|
+
function stateName(value) {
|
|
20230
|
+
return typeof value === "string" ? value : value.state;
|
|
20231
|
+
}
|
|
20232
|
+
function asIds(value) {
|
|
20233
|
+
return value === void 0 ? [] : Array.isArray(value) ? value : [value];
|
|
20234
|
+
}
|
|
20235
|
+
function changedObjectProjection(before, after) {
|
|
20236
|
+
const beforeFields = before?.fields || {};
|
|
20237
|
+
const afterFields = after.fields || {};
|
|
20238
|
+
const fields = { ...afterFields };
|
|
20239
|
+
for (const fieldName of Object.keys(beforeFields)) {
|
|
20240
|
+
if (!(fieldName in afterFields)) fields[fieldName] = null;
|
|
20241
|
+
}
|
|
20242
|
+
if (before && before.label === after.label && stableJson(beforeFields) === stableJson(afterFields)) {
|
|
20243
|
+
return null;
|
|
20244
|
+
}
|
|
20245
|
+
return {
|
|
20246
|
+
kind: "object",
|
|
20247
|
+
operation: before ? "updated" : "created",
|
|
20248
|
+
record: {
|
|
20249
|
+
className: after.className,
|
|
20250
|
+
id: after.id,
|
|
20251
|
+
...after.label !== void 0 ? { label: after.label } : {},
|
|
20252
|
+
fields
|
|
20253
|
+
}
|
|
20254
|
+
};
|
|
20255
|
+
}
|
|
20256
|
+
function projectionChanges(input) {
|
|
20257
|
+
const before = new Map(
|
|
20258
|
+
input.beforeRecords.map((record) => [recordKey(record), record])
|
|
20259
|
+
);
|
|
20260
|
+
const after = new Map(
|
|
20261
|
+
input.afterRecords.map((record) => [recordKey(record), record])
|
|
20262
|
+
);
|
|
20263
|
+
const targets = relationshipTargets(input.manifest);
|
|
20264
|
+
const changes = [];
|
|
20265
|
+
for (const record of input.afterRecords) {
|
|
20266
|
+
const previous = before.get(recordKey(record));
|
|
20267
|
+
const objectChange = changedObjectProjection(previous, record);
|
|
20268
|
+
if (objectChange) changes.push(objectChange);
|
|
20269
|
+
const relationshipNames = /* @__PURE__ */ new Set([
|
|
20270
|
+
...Object.keys(previous?.relationships || {}),
|
|
20271
|
+
...Object.keys(record.relationships || {})
|
|
20272
|
+
]);
|
|
20273
|
+
for (const relationshipName of [...relationshipNames].sort()) {
|
|
20274
|
+
const target = targets.get(
|
|
20275
|
+
`${record.className}\0${relationshipName}`
|
|
20276
|
+
);
|
|
20277
|
+
if (!target) {
|
|
20278
|
+
throw new Error(
|
|
20279
|
+
`No ontology relationship target is declared for ${record.className}.${relationshipName}`
|
|
20280
|
+
);
|
|
20281
|
+
}
|
|
20282
|
+
const previousIds = new Set(
|
|
20283
|
+
asIds(previous?.relationships?.[relationshipName])
|
|
20284
|
+
);
|
|
20285
|
+
const currentIds = new Set(
|
|
20286
|
+
asIds(record.relationships?.[relationshipName])
|
|
20287
|
+
);
|
|
20288
|
+
for (const id of [...previousIds].sort()) {
|
|
20289
|
+
if (currentIds.has(id)) continue;
|
|
20290
|
+
changes.push({
|
|
20291
|
+
kind: "relationship",
|
|
20292
|
+
operation: "disconnected",
|
|
20293
|
+
relationship: relationshipName,
|
|
20294
|
+
from: recordReference(record.className, record.id),
|
|
20295
|
+
to: recordReference(target.className, id)
|
|
20296
|
+
});
|
|
20297
|
+
}
|
|
20298
|
+
for (const id of [...currentIds].sort()) {
|
|
20299
|
+
if (previousIds.has(id)) continue;
|
|
20300
|
+
changes.push({
|
|
20301
|
+
kind: "relationship",
|
|
20302
|
+
operation: "connected",
|
|
20303
|
+
relationship: relationshipName,
|
|
20304
|
+
from: recordReference(record.className, record.id),
|
|
20305
|
+
to: recordReference(target.className, id)
|
|
20306
|
+
});
|
|
20307
|
+
}
|
|
20308
|
+
}
|
|
20309
|
+
const machines = /* @__PURE__ */ new Set([
|
|
20310
|
+
...Object.keys(previous?.states || {}),
|
|
20311
|
+
...Object.keys(record.states || {})
|
|
20312
|
+
]);
|
|
20313
|
+
for (const machine of [...machines].sort()) {
|
|
20314
|
+
const next = record.states?.[machine];
|
|
20315
|
+
if (next === void 0) continue;
|
|
20316
|
+
const prior = previous?.states?.[machine];
|
|
20317
|
+
if (prior !== void 0 && stateName(prior) === stateName(next)) continue;
|
|
20318
|
+
if (input.transition && input.transition.className === record.className && input.transition.objectId === record.id && input.transition.machine === machine) {
|
|
20319
|
+
continue;
|
|
20320
|
+
}
|
|
20321
|
+
changes.push({
|
|
20322
|
+
kind: "state_observation",
|
|
20323
|
+
target: recordReference(record.className, record.id),
|
|
20324
|
+
machine,
|
|
20325
|
+
state: stateName(next)
|
|
20326
|
+
});
|
|
20327
|
+
}
|
|
20328
|
+
}
|
|
20329
|
+
for (const record of input.beforeRecords) {
|
|
20330
|
+
if (after.has(recordKey(record))) continue;
|
|
20331
|
+
changes.push({
|
|
20332
|
+
kind: "object",
|
|
20333
|
+
operation: "deleted",
|
|
20334
|
+
target: recordReference(record.className, record.id)
|
|
20335
|
+
});
|
|
20336
|
+
}
|
|
20337
|
+
return changes;
|
|
20338
|
+
}
|
|
20339
|
+
function valueAtPath(value, path) {
|
|
20340
|
+
return path.split(".").filter(Boolean).reduce((current, segment) => {
|
|
20341
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
20342
|
+
return void 0;
|
|
20343
|
+
}
|
|
20344
|
+
return current[segment];
|
|
20345
|
+
}, value);
|
|
20346
|
+
}
|
|
20347
|
+
function primaryTarget(input) {
|
|
20348
|
+
if (input.transition) {
|
|
20349
|
+
return recordReference(
|
|
20350
|
+
input.transition.className,
|
|
20351
|
+
input.transition.objectId
|
|
20352
|
+
);
|
|
20353
|
+
}
|
|
20354
|
+
const creates = input.effect.metamodels?.creates;
|
|
20355
|
+
if (creates) {
|
|
20356
|
+
const declaration = typeof creates === "string" ? { className: creates } : creates;
|
|
20357
|
+
const id = declaration.idPath ? valueAtPath(input.result, declaration.idPath) : void 0;
|
|
20358
|
+
if (typeof id === "string" && id) {
|
|
20359
|
+
return recordReference(declaration.className, id);
|
|
20360
|
+
}
|
|
20361
|
+
}
|
|
20362
|
+
if (input.effect.className && !input.effect.static) {
|
|
20363
|
+
const objectId = input.effectInput && typeof input.effectInput === "object" && !Array.isArray(input.effectInput) ? input.effectInput._objectId : void 0;
|
|
20364
|
+
if (typeof objectId === "string" && objectId) {
|
|
20365
|
+
return recordReference(input.effect.className, objectId);
|
|
20366
|
+
}
|
|
20367
|
+
}
|
|
20368
|
+
const created = input.changes.filter(
|
|
20369
|
+
(change) => change.kind === "object" && change.operation === "created"
|
|
20370
|
+
);
|
|
20371
|
+
const onlyCreated = created.length === 1 ? created[0] : void 0;
|
|
20372
|
+
return onlyCreated ? recordReference(onlyCreated.record.className, onlyCreated.record.id) : void 0;
|
|
20373
|
+
}
|
|
20374
|
+
function transitionOutcome(transition, afterRecords) {
|
|
20375
|
+
if (!transition) {
|
|
20376
|
+
throw new Error("A transition commit has no authored transition context.");
|
|
20377
|
+
}
|
|
20378
|
+
const target = afterRecords.find(
|
|
20379
|
+
(record) => record.className === transition.className && record.id === transition.objectId
|
|
20380
|
+
);
|
|
20381
|
+
const observed = target?.states?.[transition.machine];
|
|
20382
|
+
if (!observed) {
|
|
20383
|
+
throw new Error(
|
|
20384
|
+
`The product result did not expose ${transition.className}:${transition.objectId}.${transition.machine}`
|
|
20385
|
+
);
|
|
20386
|
+
}
|
|
20387
|
+
const finalState = stateName(observed);
|
|
20388
|
+
const matches = Object.entries(transition.outcomes).filter(
|
|
20389
|
+
([, outcome]) => outcome.to === finalState || outcome.to === "$current" && finalState === transition.from
|
|
20390
|
+
);
|
|
20391
|
+
if (matches.length !== 1) {
|
|
20392
|
+
throw new Error(
|
|
20393
|
+
`Product state ${finalState} maps to ${matches.length} authored outcomes for ${transition.machine}.${transition.transition}`
|
|
20394
|
+
);
|
|
20395
|
+
}
|
|
20396
|
+
const [key, declaration] = matches[0];
|
|
20397
|
+
return {
|
|
20398
|
+
key,
|
|
20399
|
+
...declaration.disposition === "error" ? {
|
|
20400
|
+
error: {
|
|
20401
|
+
code: `product_outcome_${key}`,
|
|
20402
|
+
message: declaration.label || `The product completed in ${finalState} instead of continuing.`,
|
|
20403
|
+
retryable: false
|
|
20404
|
+
}
|
|
20405
|
+
} : {}
|
|
20406
|
+
};
|
|
20407
|
+
}
|
|
20408
|
+
function projectRecordSnapshotMutation(input) {
|
|
20409
|
+
const changes = projectionChanges({
|
|
20410
|
+
manifest: input.manifest,
|
|
20411
|
+
beforeRecords: input.source.beforeRecords,
|
|
20412
|
+
afterRecords: input.source.afterRecords,
|
|
20413
|
+
transition: input.transition
|
|
20414
|
+
});
|
|
20415
|
+
const target = primaryTarget({
|
|
20416
|
+
effect: input.effect,
|
|
20417
|
+
effectInput: input.effectInput,
|
|
20418
|
+
result: input.result,
|
|
20419
|
+
changes,
|
|
20420
|
+
transition: input.transition
|
|
20421
|
+
});
|
|
20422
|
+
const base = {
|
|
20423
|
+
source: {
|
|
20424
|
+
reference: input.source.reference,
|
|
20425
|
+
...input.source.version ? { version: input.source.version } : {}
|
|
20426
|
+
},
|
|
20427
|
+
...target ? { primaryTarget: target } : {},
|
|
20428
|
+
changes,
|
|
20429
|
+
safeSummary: {
|
|
20430
|
+
effect: input.effect.name,
|
|
20431
|
+
affectedChanges: changes.length
|
|
20432
|
+
}
|
|
20433
|
+
};
|
|
20434
|
+
if (input.commitKind === "effect") return base;
|
|
20435
|
+
return {
|
|
20436
|
+
...base,
|
|
20437
|
+
outcome: transitionOutcome(input.transition, input.source.afterRecords)
|
|
20438
|
+
};
|
|
20439
|
+
}
|
|
20440
|
+
|
|
19495
20441
|
// src/agent-harness-templates/action-presentation/0.1.0/manifest.json
|
|
19496
20442
|
var manifest_default = {
|
|
19497
20443
|
id: "action-presentation",
|
|
@@ -22500,6 +23446,6 @@ function calculateOpenAITokenSpend(model, rawUsage) {
|
|
|
22500
23446
|
};
|
|
22501
23447
|
}
|
|
22502
23448
|
|
|
22503
|
-
export { Environment, EnvironmentSession, GRANULAR_FEED_DIAGNOSTIC_EVENT, Granular, OPENAI_MODEL_PRICING_USD_PER_MILLION, OntologyHandle, Session, SessionFeedController, WSClient, buildContinuationInstruction, buildContinuationInstructionFromTemplate, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentFileBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentManualActionBlock, buildGranularAgentManualActionMemorySummary, buildGranularAgentReferentBlock, buildGranularAgentRuntimeImportsBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentSystemPromptFromTemplate, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildOpenAISpendEventId, buildSessionTranscript, buildSessionTranscriptFromFeedItems, calculateOpenAITokenSpend, consumeGranularReasoningOnlyChunk, consumeGranularReasoningTraceChunk, createFeedPublisher, createHarnessVerifierSnapshot, emitFeedDiagnostic, emitFeedDiagnosticToDefaultSink, emptyFeedSnapshot, evaluateContinuation, evaluateValidationRule, extractPromptTokens, getCurrentClosureId, getDefaultHarnessTemplateId, getExclusivePromptTarget, getOpenAIModelPricing, hasCanonicalSessionFeedActivation, hasOpenPrompt, hashHarnessTemplateValue, invokeRegisteredEffect, isCanonicalSessionFeedDocument, isLocalApiUrl, listHarnessTemplates, mergeFeedItemsBySequence, normalizeEffectBehaviors, normalizeFeedDiagnostic, normalizeFeedDiagnosticKind, normalizeFeedPage, normalizeOpenAIUsage, normalizePrompt, normalizePromptChoiceOption, normalizePromptText, normalizePromptType, orderTransientFeedItems, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectSessionFileSummary, projectWorkflowFocus, projectWorkflowSummary, readSessionFeedSnapshot, recordOpenAIUsageSpend, renderContinuationInstructionFromTemplate, renderGranularAgentSystemPromptFromTemplate, resolveApiUrl, resolveAuthTokenForApiUrl, resolveHarnessTemplate, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch, stripGranularReasoningTrace, toGranularHttpBase, validateHarnessTemplateManifest, validationRuleFailureMessage };
|
|
23449
|
+
export { Environment, EnvironmentSession, GRANULAR_FEED_DIAGNOSTIC_EVENT, Granular, OPENAI_MODEL_PRICING_USD_PER_MILLION, OntologyHandle, Session, SessionFeedController, WSClient, buildContinuationInstruction, buildContinuationInstructionFromTemplate, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentFileBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentManualActionBlock, buildGranularAgentManualActionMemorySummary, buildGranularAgentReferentBlock, buildGranularAgentRuntimeImportsBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentSystemPromptFromTemplate, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildOpenAISpendEventId, buildSessionTranscript, buildSessionTranscriptFromFeedItems, calculateOpenAITokenSpend, canonicalizeCommitValue, changedRecordSnapshots, consumeGranularReasoningOnlyChunk, consumeGranularReasoningTraceChunk, createFeedPublisher, createHarnessVerifierSnapshot, defineEffect, defineProjection, defineStateMachine, emitFeedDiagnostic, emitFeedDiagnosticToDefaultSink, emptyFeedSnapshot, evaluateContinuation, evaluateValidationRule, extractPromptTokens, getCurrentClosureId, getDefaultHarnessTemplateId, getExclusivePromptTarget, getOpenAIModelPricing, hasCanonicalSessionFeedActivation, hasOpenPrompt, hashHarnessTemplateValue, invokeRegisteredEffect, isCanonicalSessionFeedDocument, isLocalApiUrl, listHarnessTemplates, mergeFeedItemsBySequence, normalizeEffectBehaviors, normalizeFeedDiagnostic, normalizeFeedDiagnosticKind, normalizeFeedPage, normalizeOpenAIUsage, normalizePrompt, normalizePromptChoiceOption, normalizePromptText, normalizePromptType, orderTransientFeedItems, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectRecordSnapshotMutation, projectSessionFileSummary, projectWorkflowFocus, projectWorkflowSummary, readSessionFeedSnapshot, recordOpenAIUsageSpend, renderContinuationInstructionFromTemplate, renderGranularAgentSystemPromptFromTemplate, resolveApiUrl, resolveAuthTokenForApiUrl, resolveHarnessTemplate, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch, stripGranularReasoningTrace, toGranularHttpBase, validateHarnessTemplateManifest, validateProjectionResult, validationRuleFailureMessage };
|
|
22504
23450
|
//# sourceMappingURL=index.mjs.map
|
|
22505
23451
|
//# sourceMappingURL=index.mjs.map
|