@granular-software/sdk 0.4.63 → 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 +713 -65
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +713 -65
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +713 -65
- package/dist/{client-lk-dmdKR.d.mts → client-S-z8bri1.d.mts} +47 -1
- package/dist/{client-KqJIH7WE.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 +1058 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1052 -84
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-DtsHRNb5.d.mts → spend-CESUvrZC.d.mts} +239 -4
- package/dist/{spend-DtsHRNb5.d.ts → spend-CESUvrZC.d.ts} +239 -4
- 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
|
|
@@ -12219,8 +12228,12 @@ external_exports.union([
|
|
|
12219
12228
|
external_exports.array(external_exports.string()),
|
|
12220
12229
|
external_exports.object({
|
|
12221
12230
|
values: external_exports.array(external_exports.string()),
|
|
12231
|
+
labels: external_exports.array(external_exports.string().min(1)).optional(),
|
|
12222
12232
|
message: external_exports.string().optional()
|
|
12223
|
-
}).strict()
|
|
12233
|
+
}).strict().refine(
|
|
12234
|
+
(rule) => !rule.labels || rule.labels.length === rule.values.length,
|
|
12235
|
+
{ message: "Enum labels must match enum values one-for-one" }
|
|
12236
|
+
)
|
|
12224
12237
|
]);
|
|
12225
12238
|
external_exports.union([
|
|
12226
12239
|
external_exports.boolean(),
|
|
@@ -12248,7 +12261,7 @@ var StateMachineStateSchema = external_exports.union([
|
|
|
12248
12261
|
external_exports.string(),
|
|
12249
12262
|
external_exports.object({
|
|
12250
12263
|
name: external_exports.string().min(1),
|
|
12251
|
-
label: external_exports.string().optional(),
|
|
12264
|
+
label: external_exports.string().min(1).optional(),
|
|
12252
12265
|
description: external_exports.string().optional(),
|
|
12253
12266
|
isFinal: external_exports.boolean().optional()
|
|
12254
12267
|
}).strict()
|
|
@@ -12334,6 +12347,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
12334
12347
|
summary: external_exports.string().optional()
|
|
12335
12348
|
}).strict()
|
|
12336
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();
|
|
12337
12356
|
var StateMachineTransitionSchema = external_exports.object({
|
|
12338
12357
|
name: external_exports.string().min(1),
|
|
12339
12358
|
from: external_exports.string().min(1),
|
|
@@ -12345,7 +12364,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12345
12364
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
12346
12365
|
permission: StateTransitionPermissionSchema.optional(),
|
|
12347
12366
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
12348
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
12367
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
12368
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
12349
12369
|
}).strict();
|
|
12350
12370
|
external_exports.object({
|
|
12351
12371
|
name: external_exports.string().min(1),
|
|
@@ -13026,6 +13046,289 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
13026
13046
|
}
|
|
13027
13047
|
});
|
|
13028
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
|
+
|
|
13029
13332
|
// src/effect-runtime.ts
|
|
13030
13333
|
function computeEffectKey(effect) {
|
|
13031
13334
|
const attachedClass = effect.className?.trim();
|
|
@@ -13090,6 +13393,45 @@ function resolveInvocationMode(context) {
|
|
|
13090
13393
|
}
|
|
13091
13394
|
return "execute";
|
|
13092
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
|
+
}
|
|
13093
13435
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
13094
13436
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
13095
13437
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -13142,7 +13484,7 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13142
13484
|
if (mode === "artifactOptions") {
|
|
13143
13485
|
if (!effect.artifactOptionsHandler) {
|
|
13144
13486
|
throw new Error(
|
|
13145
|
-
`Artifact
|
|
13487
|
+
`Artifact field options are not supported for ${request.effectKey}`
|
|
13146
13488
|
);
|
|
13147
13489
|
}
|
|
13148
13490
|
return {
|
|
@@ -13161,22 +13503,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13161
13503
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
13162
13504
|
}
|
|
13163
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
|
+
}
|
|
13164
13529
|
if (effect.reverseHandler) {
|
|
13165
13530
|
return { effect, mode, handler: effect.reverseHandler };
|
|
13166
13531
|
}
|
|
13167
|
-
const reverseEffect = resolveReverseEffect(
|
|
13168
|
-
effectMap,
|
|
13169
|
-
effect,
|
|
13170
|
-
request,
|
|
13171
|
-
behaviors
|
|
13172
|
-
);
|
|
13173
|
-
if (reverseEffect) {
|
|
13174
|
-
return {
|
|
13175
|
-
effect: reverseEffect,
|
|
13176
|
-
mode,
|
|
13177
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
13178
|
-
};
|
|
13179
|
-
}
|
|
13180
13532
|
throw new Error(
|
|
13181
13533
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
13182
13534
|
);
|
|
@@ -13277,18 +13629,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13277
13629
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
13278
13630
|
}
|
|
13279
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
|
+
);
|
|
13280
13752
|
const context = {
|
|
13281
13753
|
...request.context || {},
|
|
13754
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
13755
|
+
commit: commitContext,
|
|
13282
13756
|
behaviors: normalizeEffectBehaviors(
|
|
13283
13757
|
request.context?.behaviors || effect.metamodels || void 0
|
|
13284
13758
|
),
|
|
13285
13759
|
invocation: {
|
|
13286
13760
|
mode: resolved.mode,
|
|
13287
|
-
|
|
13288
|
-
|
|
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,
|
|
13289
13764
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
13290
13765
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13291
|
-
...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 } : {}
|
|
13292
13768
|
}
|
|
13293
13769
|
};
|
|
13294
13770
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -13312,6 +13788,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13312
13788
|
handlerFailed = true;
|
|
13313
13789
|
handlerError = error;
|
|
13314
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
|
+
}
|
|
13315
13801
|
let feedbackError;
|
|
13316
13802
|
let feedbackFailed = false;
|
|
13317
13803
|
if (feedbackContext) {
|
|
@@ -13325,9 +13811,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13325
13811
|
if (handlerFailed) {
|
|
13326
13812
|
throw handlerError;
|
|
13327
13813
|
}
|
|
13814
|
+
if (commitFailed) {
|
|
13815
|
+
throw commitError;
|
|
13816
|
+
}
|
|
13328
13817
|
if (feedbackFailed) {
|
|
13329
13818
|
throw feedbackError;
|
|
13330
13819
|
}
|
|
13820
|
+
if (commitRequired && !commitStarted) {
|
|
13821
|
+
throw new Error(
|
|
13822
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
13823
|
+
);
|
|
13824
|
+
}
|
|
13331
13825
|
return handlerResult;
|
|
13332
13826
|
}
|
|
13333
13827
|
|
|
@@ -13396,12 +13890,7 @@ function toRecordSearchResult(className, node) {
|
|
|
13396
13890
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
13397
13891
|
);
|
|
13398
13892
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
13399
|
-
const
|
|
13400
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path)) {
|
|
13401
|
-
return null;
|
|
13402
|
-
}
|
|
13403
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
13404
|
-
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;
|
|
13405
13894
|
return {
|
|
13406
13895
|
path,
|
|
13407
13896
|
className,
|
|
@@ -13411,30 +13900,6 @@ function toRecordSearchResult(className, node) {
|
|
|
13411
13900
|
fields
|
|
13412
13901
|
};
|
|
13413
13902
|
}
|
|
13414
|
-
function isPlaceholderRecordLabel(label, id, path) {
|
|
13415
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
13416
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path);
|
|
13417
|
-
}
|
|
13418
|
-
function displayLabelFromFields(fields) {
|
|
13419
|
-
const preferredFieldNames = [
|
|
13420
|
-
"name",
|
|
13421
|
-
"title",
|
|
13422
|
-
"label",
|
|
13423
|
-
"display_name",
|
|
13424
|
-
"file_name",
|
|
13425
|
-
"number",
|
|
13426
|
-
"code"
|
|
13427
|
-
];
|
|
13428
|
-
for (const preferred of preferredFieldNames) {
|
|
13429
|
-
const match = fields.find(
|
|
13430
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
13431
|
-
);
|
|
13432
|
-
if (typeof match?.value === "string") {
|
|
13433
|
-
return match.value.trim();
|
|
13434
|
-
}
|
|
13435
|
-
}
|
|
13436
|
-
return null;
|
|
13437
|
-
}
|
|
13438
13903
|
function normalizeRecordSearchText(value) {
|
|
13439
13904
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
13440
13905
|
}
|
|
@@ -13644,18 +14109,24 @@ function normalizeEnumInput(enumSpec) {
|
|
|
13644
14109
|
(value) => typeof value === "string" && value.length > 0
|
|
13645
14110
|
);
|
|
13646
14111
|
if (values.length === 0) return null;
|
|
13647
|
-
|
|
14112
|
+
const labels = Array.isArray(config.labels) && config.labels.length === values.length ? config.labels : values;
|
|
14113
|
+
return {
|
|
14114
|
+
values,
|
|
14115
|
+
labels,
|
|
14116
|
+
...config.message ? { message: config.message } : {}
|
|
14117
|
+
};
|
|
13648
14118
|
}
|
|
13649
14119
|
function buildEnumFieldMutations(fieldPath, enumSpec) {
|
|
13650
14120
|
const normalized = normalizeEnumInput(enumSpec);
|
|
13651
14121
|
if (!normalized) return [];
|
|
13652
14122
|
const messageArg = normalized.message ? `, message: ${JSON.stringify(normalized.message)}` : "";
|
|
14123
|
+
const labelsArg = normalized.labels ? `, labels: ${JSON.stringify(normalized.labels)}` : "";
|
|
13653
14124
|
return [
|
|
13654
14125
|
{
|
|
13655
14126
|
label: `set enum on ${fieldPath}`,
|
|
13656
14127
|
query: `mutation { at(path: ${JSON.stringify(fieldPath)}) { set_enum(values: ${JSON.stringify(
|
|
13657
14128
|
normalized.values
|
|
13658
|
-
)}${messageArg}) { values } } }`
|
|
14129
|
+
)}${labelsArg}${messageArg}) { values labels } } }`
|
|
13659
14130
|
}
|
|
13660
14131
|
];
|
|
13661
14132
|
}
|
|
@@ -13665,7 +14136,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13665
14136
|
fieldRows: [
|
|
13666
14137
|
{
|
|
13667
14138
|
key: "enum",
|
|
13668
|
-
description: 'Allowed values.
|
|
14139
|
+
description: 'Allowed values. Use `{ "values": [...], "labels": [...] }` for authored display labels; otherwise each unchanged value is its display fallback.'
|
|
13669
14140
|
}
|
|
13670
14141
|
]
|
|
13671
14142
|
},
|
|
@@ -13675,6 +14146,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13675
14146
|
type EnumMetamodel {
|
|
13676
14147
|
model: Model!
|
|
13677
14148
|
values: [String!]!
|
|
14149
|
+
labels: [String!]!
|
|
13678
14150
|
message: String
|
|
13679
14151
|
}
|
|
13680
14152
|
|
|
@@ -13683,7 +14155,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13683
14155
|
}
|
|
13684
14156
|
|
|
13685
14157
|
extend type ModelMutation {
|
|
13686
|
-
set_enum(values: [String!]!, message: String): EnumMetamodel
|
|
14158
|
+
set_enum(values: [String!]!, labels: [String!], message: String): EnumMetamodel
|
|
13687
14159
|
}
|
|
13688
14160
|
`
|
|
13689
14161
|
],
|
|
@@ -13692,15 +14164,19 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13692
14164
|
EnumMetamodel: {
|
|
13693
14165
|
model: (value) => value.model,
|
|
13694
14166
|
values: (value) => value.values,
|
|
14167
|
+
labels: (value) => value.labels || [],
|
|
13695
14168
|
message: (value) => value.message || null
|
|
13696
14169
|
},
|
|
13697
14170
|
Model: {
|
|
13698
14171
|
enum_rule: async (ant) => await run(ant.enum_rule())
|
|
13699
14172
|
},
|
|
13700
14173
|
ModelMutation: {
|
|
13701
|
-
set_enum: async (ant, { values, message }) => {
|
|
13702
|
-
const
|
|
13703
|
-
|
|
14174
|
+
set_enum: async (ant, { values, labels, message }) => {
|
|
14175
|
+
const resolvedLabels = Array.isArray(labels) && labels.length === values.length ? labels : values;
|
|
14176
|
+
const model = await run(
|
|
14177
|
+
ant.set_enum(values, resolvedLabels, message)
|
|
14178
|
+
);
|
|
14179
|
+
return { model, values, labels: resolvedLabels, message };
|
|
13704
14180
|
}
|
|
13705
14181
|
}
|
|
13706
14182
|
};
|
|
@@ -13713,7 +14189,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13713
14189
|
},
|
|
13714
14190
|
summary: {
|
|
13715
14191
|
selections: {
|
|
13716
|
-
propertyFields: [`enum_rule { values message }`]
|
|
14192
|
+
propertyFields: [`enum_rule { values labels message }`]
|
|
13717
14193
|
},
|
|
13718
14194
|
readPropertySummary(rawProperty) {
|
|
13719
14195
|
const values = Array.isArray(rawProperty.enum_rule?.values) ? rawProperty.enum_rule.values.filter(
|
|
@@ -13721,8 +14197,15 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13721
14197
|
) : [];
|
|
13722
14198
|
if (values.length === 0) return { enumRule: null };
|
|
13723
14199
|
const message = typeof rawProperty.enum_rule?.message === "string" ? rawProperty.enum_rule.message : null;
|
|
14200
|
+
const labels = Array.isArray(rawProperty.enum_rule?.labels) ? rawProperty.enum_rule.labels.filter(
|
|
14201
|
+
(label) => typeof label === "string" && label.length > 0
|
|
14202
|
+
) : [];
|
|
13724
14203
|
return {
|
|
13725
|
-
enumRule:
|
|
14204
|
+
enumRule: {
|
|
14205
|
+
values,
|
|
14206
|
+
...labels.length === values.length ? { labels } : {},
|
|
14207
|
+
...message ? { message } : {}
|
|
14208
|
+
}
|
|
13726
14209
|
};
|
|
13727
14210
|
}
|
|
13728
14211
|
},
|
|
@@ -14379,7 +14862,8 @@ function normalizeStateMachines(values) {
|
|
|
14379
14862
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
14380
14863
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
14381
14864
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
14382
|
-
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)
|
|
14383
14867
|
})).filter(
|
|
14384
14868
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
14385
14869
|
);
|
|
@@ -14472,6 +14956,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14472
14956
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
14473
14957
|
);
|
|
14474
14958
|
}
|
|
14959
|
+
if (transition.outcomes) {
|
|
14960
|
+
args.push(
|
|
14961
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
14962
|
+
);
|
|
14963
|
+
}
|
|
14475
14964
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
14476
14965
|
}
|
|
14477
14966
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -14534,7 +15023,7 @@ function buildMachineTypes(classSummary, machine) {
|
|
|
14534
15023
|
];
|
|
14535
15024
|
}
|
|
14536
15025
|
function buildMachineMethods(classSummary, machine) {
|
|
14537
|
-
const
|
|
15026
|
+
const stateName2 = stateTypeName(classSummary.name, machine.name);
|
|
14538
15027
|
const transitionName = transitionTypeName(classSummary.name, machine.name);
|
|
14539
15028
|
pathTypeName(classSummary.name, machine.name);
|
|
14540
15029
|
const docsPrefix = `${classSummary.name}.${machine.name}`;
|
|
@@ -14544,12 +15033,12 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14544
15033
|
docs: [`Get the current ${docsPrefix} state.`],
|
|
14545
15034
|
static: false,
|
|
14546
15035
|
params: [],
|
|
14547
|
-
returnType: `Promise<${
|
|
15036
|
+
returnType: `Promise<${stateName2} | null>`,
|
|
14548
15037
|
runtime: {
|
|
14549
15038
|
kind: "state_machine",
|
|
14550
15039
|
machineName: machine.name,
|
|
14551
15040
|
className: classSummary.name,
|
|
14552
|
-
stateTypeName:
|
|
15041
|
+
stateTypeName: stateName2,
|
|
14553
15042
|
transitionTypeName: transitionName,
|
|
14554
15043
|
operation: "get_current"
|
|
14555
15044
|
}
|
|
@@ -14560,13 +15049,13 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14560
15049
|
`Reach a ${docsPrefix} state through the shortest allowed transition path.`
|
|
14561
15050
|
],
|
|
14562
15051
|
static: false,
|
|
14563
|
-
params: [{ name: "target", type:
|
|
15052
|
+
params: [{ name: "target", type: stateName2 }],
|
|
14564
15053
|
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
14565
15054
|
runtime: {
|
|
14566
15055
|
kind: "state_machine",
|
|
14567
15056
|
machineName: machine.name,
|
|
14568
15057
|
className: classSummary.name,
|
|
14569
|
-
stateTypeName:
|
|
15058
|
+
stateTypeName: stateName2,
|
|
14570
15059
|
transitionTypeName: transitionName,
|
|
14571
15060
|
operation: "reach"
|
|
14572
15061
|
}
|
|
@@ -14581,7 +15070,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14581
15070
|
kind: "state_machine",
|
|
14582
15071
|
machineName: machine.name,
|
|
14583
15072
|
className: classSummary.name,
|
|
14584
|
-
stateTypeName:
|
|
15073
|
+
stateTypeName: stateName2,
|
|
14585
15074
|
transitionTypeName: transitionName,
|
|
14586
15075
|
operation: "list_transitions"
|
|
14587
15076
|
}
|
|
@@ -14591,12 +15080,12 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14591
15080
|
docs: [`List reachable states for ${docsPrefix} from the current state.`],
|
|
14592
15081
|
static: false,
|
|
14593
15082
|
params: [],
|
|
14594
|
-
returnType: `Promise<${
|
|
15083
|
+
returnType: `Promise<${stateName2}[]>`,
|
|
14595
15084
|
runtime: {
|
|
14596
15085
|
kind: "state_machine",
|
|
14597
15086
|
machineName: machine.name,
|
|
14598
15087
|
className: classSummary.name,
|
|
14599
|
-
stateTypeName:
|
|
15088
|
+
stateTypeName: stateName2,
|
|
14600
15089
|
transitionTypeName: transitionName,
|
|
14601
15090
|
operation: "list_reachable_states"
|
|
14602
15091
|
}
|
|
@@ -14611,7 +15100,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14611
15100
|
kind: "state_machine",
|
|
14612
15101
|
machineName: machine.name,
|
|
14613
15102
|
className: classSummary.name,
|
|
14614
|
-
stateTypeName:
|
|
15103
|
+
stateTypeName: stateName2,
|
|
14615
15104
|
transitionTypeName: transitionName,
|
|
14616
15105
|
operation: "is_final"
|
|
14617
15106
|
}
|
|
@@ -14622,13 +15111,13 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14622
15111
|
`List shortest transition paths from the current ${docsPrefix} state to a target state.`
|
|
14623
15112
|
],
|
|
14624
15113
|
static: false,
|
|
14625
|
-
params: [{ name: "target", type:
|
|
14626
|
-
returnType: `Promise<Array<{ states: ${
|
|
15114
|
+
params: [{ name: "target", type: stateName2 }],
|
|
15115
|
+
returnType: `Promise<Array<{ states: ${stateName2}[]; transitions: ${transitionName}[] }>>`,
|
|
14627
15116
|
runtime: {
|
|
14628
15117
|
kind: "state_machine",
|
|
14629
15118
|
machineName: machine.name,
|
|
14630
15119
|
className: classSummary.name,
|
|
14631
|
-
stateTypeName:
|
|
15120
|
+
stateTypeName: stateName2,
|
|
14632
15121
|
transitionTypeName: transitionName,
|
|
14633
15122
|
operation: "paths_to"
|
|
14634
15123
|
}
|
|
@@ -14652,7 +15141,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14652
15141
|
kind: "state_machine",
|
|
14653
15142
|
machineName: machine.name,
|
|
14654
15143
|
className: classSummary.name,
|
|
14655
|
-
stateTypeName:
|
|
15144
|
+
stateTypeName: stateName2,
|
|
14656
15145
|
transitionTypeName: transitionName,
|
|
14657
15146
|
operation: "reach",
|
|
14658
15147
|
targetState: stateNameValue,
|
|
@@ -14671,7 +15160,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14671
15160
|
kind: "state_machine",
|
|
14672
15161
|
machineName: machine.name,
|
|
14673
15162
|
className: classSummary.name,
|
|
14674
|
-
stateTypeName:
|
|
15163
|
+
stateTypeName: stateName2,
|
|
14675
15164
|
transitionTypeName: transitionName,
|
|
14676
15165
|
operation: "prepare_reach",
|
|
14677
15166
|
targetState: stateNameValue,
|
|
@@ -14684,7 +15173,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
14684
15173
|
kind: "state_machine",
|
|
14685
15174
|
machineName: machine.name,
|
|
14686
15175
|
className: classSummary.name,
|
|
14687
|
-
stateTypeName:
|
|
15176
|
+
stateTypeName: stateName2,
|
|
14688
15177
|
transitionTypeName: transitionName,
|
|
14689
15178
|
operation: "prepare_create_reach",
|
|
14690
15179
|
targetState: stateNameValue,
|
|
@@ -14750,7 +15239,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14750
15239
|
name: String!
|
|
14751
15240
|
state_machine: StateMachine!
|
|
14752
15241
|
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!
|
|
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!
|
|
14754
15243
|
activate_transition(name: String!): StateMachineMutation!
|
|
14755
15244
|
}
|
|
14756
15245
|
|
|
@@ -14767,6 +15256,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14767
15256
|
type StateMachineSnapshotMutation {
|
|
14768
15257
|
snapshot: StateMachineSnapshot!
|
|
14769
15258
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15259
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
14770
15260
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
14771
15261
|
}
|
|
14772
15262
|
|
|
@@ -14813,6 +15303,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14813
15303
|
permission_json: String
|
|
14814
15304
|
risk: String
|
|
14815
15305
|
expected_outcome_json: String
|
|
15306
|
+
outcomes_json: String
|
|
14816
15307
|
}
|
|
14817
15308
|
|
|
14818
15309
|
type StateMachinePath {
|
|
@@ -14823,6 +15314,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14823
15314
|
type StateMachineTransitionEvent {
|
|
14824
15315
|
sequence: Int!
|
|
14825
15316
|
occurred_at: Float!
|
|
15317
|
+
commit_id: String
|
|
15318
|
+
outcome: String
|
|
15319
|
+
source_version: String
|
|
15320
|
+
projected_from_mismatch: String
|
|
14826
15321
|
transition: StateMachineTransition!
|
|
14827
15322
|
from: StateMachineState!
|
|
14828
15323
|
to: StateMachineState!
|
|
@@ -14888,7 +15383,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14888
15383
|
requirements_json,
|
|
14889
15384
|
permission_json,
|
|
14890
15385
|
risk,
|
|
14891
|
-
expected_outcome_json
|
|
15386
|
+
expected_outcome_json,
|
|
15387
|
+
outcomes_json
|
|
14892
15388
|
}) => {
|
|
14893
15389
|
await run(
|
|
14894
15390
|
value.target.add_state_machine_transition(
|
|
@@ -14904,7 +15400,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14904
15400
|
requirementsJson: requirements_json,
|
|
14905
15401
|
permissionJson: permission_json,
|
|
14906
15402
|
risk,
|
|
14907
|
-
expectedOutcomeJson: expected_outcome_json
|
|
15403
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
15404
|
+
outcomesJson: outcomes_json
|
|
14908
15405
|
}
|
|
14909
15406
|
)
|
|
14910
15407
|
);
|
|
@@ -14925,6 +15422,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14925
15422
|
);
|
|
14926
15423
|
return value;
|
|
14927
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
|
+
},
|
|
14928
15438
|
observe_state: async (value, { state, force, source }) => {
|
|
14929
15439
|
await run(
|
|
14930
15440
|
value.target.observe_state_machine_state(
|
|
@@ -14954,7 +15464,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14954
15464
|
requirements_json: (value) => value.requirements_json || null,
|
|
14955
15465
|
permission_json: (value) => value.permission_json || null,
|
|
14956
15466
|
risk: (value) => value.risk || null,
|
|
14957
|
-
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
|
|
14958
15469
|
},
|
|
14959
15470
|
StateMachinePath: {
|
|
14960
15471
|
states: (value) => value.states,
|
|
@@ -14963,6 +15474,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14963
15474
|
StateMachineTransitionEvent: {
|
|
14964
15475
|
sequence: (value) => value.sequence,
|
|
14965
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,
|
|
14966
15481
|
transition: (value) => value.transition,
|
|
14967
15482
|
from: (value) => value.from,
|
|
14968
15483
|
to: (value) => value.to
|
|
@@ -15036,6 +15551,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15036
15551
|
permission_json
|
|
15037
15552
|
risk
|
|
15038
15553
|
expected_outcome_json
|
|
15554
|
+
outcomes_json
|
|
15039
15555
|
}
|
|
15040
15556
|
}`
|
|
15041
15557
|
]
|
|
@@ -15763,6 +16279,133 @@ var Environment = class _Environment {
|
|
|
15763
16279
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
15764
16280
|
};
|
|
15765
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
|
+
}
|
|
15766
16409
|
/**
|
|
15767
16410
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
15768
16411
|
* own the customer application's state machine.
|
|
@@ -15802,8 +16445,8 @@ var Environment = class _Environment {
|
|
|
15802
16445
|
* `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
|
|
15803
16446
|
*/
|
|
15804
16447
|
state(target) {
|
|
15805
|
-
const observe = async (machineName,
|
|
15806
|
-
const observedState = input.observedState || input.state ||
|
|
16448
|
+
const observe = async (machineName, stateName2, input = {}) => {
|
|
16449
|
+
const observedState = input.observedState || input.state || stateName2;
|
|
15807
16450
|
if (!observedState) {
|
|
15808
16451
|
throw new Error("State observation requires a target state");
|
|
15809
16452
|
}
|
|
@@ -15829,7 +16472,7 @@ var Environment = class _Environment {
|
|
|
15829
16472
|
{
|
|
15830
16473
|
get: (_machineTarget, stateProperty) => {
|
|
15831
16474
|
if (stateProperty === "to") {
|
|
15832
|
-
return (
|
|
16475
|
+
return (stateName2, input) => observe(machineProperty, stateName2, input || {});
|
|
15833
16476
|
}
|
|
15834
16477
|
if (typeof stateProperty !== "string") return void 0;
|
|
15835
16478
|
return (input) => observe(
|
|
@@ -17169,6 +17812,14 @@ var EnvironmentSession = class extends Session {
|
|
|
17169
17812
|
}
|
|
17170
17813
|
};
|
|
17171
17814
|
}
|
|
17815
|
+
get mutations() {
|
|
17816
|
+
return {
|
|
17817
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
17818
|
+
"/mutations",
|
|
17819
|
+
options
|
|
17820
|
+
)
|
|
17821
|
+
};
|
|
17822
|
+
}
|
|
17172
17823
|
get artifacts() {
|
|
17173
17824
|
return {
|
|
17174
17825
|
list: (options = {}) => {
|
|
@@ -18668,6 +19319,7 @@ var Granular = class _Granular {
|
|
|
18668
19319
|
const serialized = {
|
|
18669
19320
|
effectKey: computeEffectKey2(effect),
|
|
18670
19321
|
name: effect.name,
|
|
19322
|
+
...effect.label ? { label: effect.label } : {},
|
|
18671
19323
|
description: effect.description,
|
|
18672
19324
|
inputSchema: effect.inputSchema,
|
|
18673
19325
|
stability: effect.stability || "stable",
|
|
@@ -18691,6 +19343,9 @@ var Granular = class _Granular {
|
|
|
18691
19343
|
if (effect.metamodels !== void 0) {
|
|
18692
19344
|
serialized.metamodels = effect.metamodels;
|
|
18693
19345
|
}
|
|
19346
|
+
if (effect.commit !== void 0) {
|
|
19347
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
19348
|
+
}
|
|
18694
19349
|
return serialized;
|
|
18695
19350
|
}
|
|
18696
19351
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -18911,16 +19566,60 @@ var Granular = class _Granular {
|
|
|
18911
19566
|
};
|
|
18912
19567
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
18913
19568
|
const request = params;
|
|
18914
|
-
|
|
19569
|
+
let commitReceipt;
|
|
19570
|
+
const result = await invokeRegisteredEffect(
|
|
18915
19571
|
this.getSandboxEffectMap(sandboxId),
|
|
18916
19572
|
request,
|
|
18917
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
|
+
},
|
|
18918
19612
|
feedback: {
|
|
18919
19613
|
invocationId: request.callId,
|
|
18920
19614
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
18921
19615
|
}
|
|
18922
19616
|
}
|
|
18923
19617
|
);
|
|
19618
|
+
return {
|
|
19619
|
+
__granularEffectInvocationResult: true,
|
|
19620
|
+
result,
|
|
19621
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
19622
|
+
};
|
|
18924
19623
|
});
|
|
18925
19624
|
wsClient.on("open", () => {
|
|
18926
19625
|
void this.synchronizeEffectHost(host).catch((error) => {
|
|
@@ -19492,6 +20191,275 @@ var Granular = class _Granular {
|
|
|
19492
20191
|
}
|
|
19493
20192
|
};
|
|
19494
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
|
+
|
|
19495
20463
|
// src/agent-harness-templates/action-presentation/0.1.0/manifest.json
|
|
19496
20464
|
var manifest_default = {
|
|
19497
20465
|
id: "action-presentation",
|
|
@@ -22529,10 +23497,15 @@ exports.buildOpenAISpendEventId = buildOpenAISpendEventId;
|
|
|
22529
23497
|
exports.buildSessionTranscript = buildSessionTranscript;
|
|
22530
23498
|
exports.buildSessionTranscriptFromFeedItems = buildSessionTranscriptFromFeedItems;
|
|
22531
23499
|
exports.calculateOpenAITokenSpend = calculateOpenAITokenSpend;
|
|
23500
|
+
exports.canonicalizeCommitValue = canonicalizeCommitValue;
|
|
23501
|
+
exports.changedRecordSnapshots = changedRecordSnapshots;
|
|
22532
23502
|
exports.consumeGranularReasoningOnlyChunk = consumeGranularReasoningOnlyChunk;
|
|
22533
23503
|
exports.consumeGranularReasoningTraceChunk = consumeGranularReasoningTraceChunk;
|
|
22534
23504
|
exports.createFeedPublisher = createFeedPublisher;
|
|
22535
23505
|
exports.createHarnessVerifierSnapshot = createHarnessVerifierSnapshot;
|
|
23506
|
+
exports.defineEffect = defineEffect;
|
|
23507
|
+
exports.defineProjection = defineProjection;
|
|
23508
|
+
exports.defineStateMachine = defineStateMachine;
|
|
22536
23509
|
exports.emitFeedDiagnostic = emitFeedDiagnostic;
|
|
22537
23510
|
exports.emitFeedDiagnosticToDefaultSink = emitFeedDiagnosticToDefaultSink;
|
|
22538
23511
|
exports.emptyFeedSnapshot = emptyFeedSnapshot;
|
|
@@ -22565,6 +23538,7 @@ exports.projectConversationReferentFocus = projectConversationReferentFocus;
|
|
|
22565
23538
|
exports.projectConversationReferentSummary = projectConversationReferentSummary;
|
|
22566
23539
|
exports.projectHeapSummary = projectHeapSummary;
|
|
22567
23540
|
exports.projectLoopSummary = projectLoopSummary;
|
|
23541
|
+
exports.projectRecordSnapshotMutation = projectRecordSnapshotMutation;
|
|
22568
23542
|
exports.projectSessionFileSummary = projectSessionFileSummary;
|
|
22569
23543
|
exports.projectWorkflowFocus = projectWorkflowFocus;
|
|
22570
23544
|
exports.projectWorkflowSummary = projectWorkflowSummary;
|
|
@@ -22582,6 +23556,7 @@ exports.scorePromptChoiceMatch = scorePromptChoiceMatch;
|
|
|
22582
23556
|
exports.stripGranularReasoningTrace = stripGranularReasoningTrace;
|
|
22583
23557
|
exports.toGranularHttpBase = toGranularHttpBase;
|
|
22584
23558
|
exports.validateHarnessTemplateManifest = validateHarnessTemplateManifest;
|
|
23559
|
+
exports.validateProjectionResult = validateProjectionResult;
|
|
22585
23560
|
exports.validationRuleFailureMessage = validationRuleFailureMessage;
|
|
22586
23561
|
//# sourceMappingURL=index.js.map
|
|
22587
23562
|
//# sourceMappingURL=index.js.map
|