@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/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
@@ -12197,8 +12206,12 @@ external_exports.union([
12197
12206
  external_exports.array(external_exports.string()),
12198
12207
  external_exports.object({
12199
12208
  values: external_exports.array(external_exports.string()),
12209
+ labels: external_exports.array(external_exports.string().min(1)).optional(),
12200
12210
  message: external_exports.string().optional()
12201
- }).strict()
12211
+ }).strict().refine(
12212
+ (rule) => !rule.labels || rule.labels.length === rule.values.length,
12213
+ { message: "Enum labels must match enum values one-for-one" }
12214
+ )
12202
12215
  ]);
12203
12216
  external_exports.union([
12204
12217
  external_exports.boolean(),
@@ -12226,7 +12239,7 @@ var StateMachineStateSchema = external_exports.union([
12226
12239
  external_exports.string(),
12227
12240
  external_exports.object({
12228
12241
  name: external_exports.string().min(1),
12229
- label: external_exports.string().optional(),
12242
+ label: external_exports.string().min(1).optional(),
12230
12243
  description: external_exports.string().optional(),
12231
12244
  isFinal: external_exports.boolean().optional()
12232
12245
  }).strict()
@@ -12312,6 +12325,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
12312
12325
  summary: external_exports.string().optional()
12313
12326
  }).strict()
12314
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();
12315
12334
  var StateMachineTransitionSchema = external_exports.object({
12316
12335
  name: external_exports.string().min(1),
12317
12336
  from: external_exports.string().min(1),
@@ -12323,7 +12342,8 @@ var StateMachineTransitionSchema = external_exports.object({
12323
12342
  requirements: StateTransitionRequirementsSchema.optional(),
12324
12343
  permission: StateTransitionPermissionSchema.optional(),
12325
12344
  risk: external_exports.enum(["low", "medium", "high"]).optional(),
12326
- expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
12345
+ expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
12346
+ outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
12327
12347
  }).strict();
12328
12348
  external_exports.object({
12329
12349
  name: external_exports.string().min(1),
@@ -13004,6 +13024,289 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
13004
13024
  }
13005
13025
  });
13006
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
+
13007
13310
  // src/effect-runtime.ts
13008
13311
  function computeEffectKey(effect) {
13009
13312
  const attachedClass = effect.className?.trim();
@@ -13068,6 +13371,45 @@ function resolveInvocationMode(context) {
13068
13371
  }
13069
13372
  return "execute";
13070
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
+ }
13071
13413
  function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
13072
13414
  const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
13073
13415
  const configuredReverseHandler = behaviors.reverse?.handler?.trim();
@@ -13120,7 +13462,7 @@ function resolveHandlerForMode(effectMap, effect, request) {
13120
13462
  if (mode === "artifactOptions") {
13121
13463
  if (!effect.artifactOptionsHandler) {
13122
13464
  throw new Error(
13123
- `Artifact relationship options are not supported for ${request.effectKey}`
13465
+ `Artifact field options are not supported for ${request.effectKey}`
13124
13466
  );
13125
13467
  }
13126
13468
  return {
@@ -13139,22 +13481,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
13139
13481
  throw new Error(`Dry run is not supported for ${request.effectKey}`);
13140
13482
  }
13141
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
+ }
13142
13507
  if (effect.reverseHandler) {
13143
13508
  return { effect, mode, handler: effect.reverseHandler };
13144
13509
  }
13145
- const reverseEffect = resolveReverseEffect(
13146
- effectMap,
13147
- effect,
13148
- request,
13149
- behaviors
13150
- );
13151
- if (reverseEffect) {
13152
- return {
13153
- effect: reverseEffect,
13154
- mode,
13155
- handler: reverseEffect.reverseHandler || reverseEffect.handler
13156
- };
13157
- }
13158
13510
  throw new Error(
13159
13511
  `Reverse execution is not supported for ${request.effectKey}`
13160
13512
  );
@@ -13255,18 +13607,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
13255
13607
  throw new Error(`Effect handler not found: ${request.effectKey}`);
13256
13608
  }
13257
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
+ );
13258
13730
  const context = {
13259
13731
  ...request.context || {},
13732
+ ...idempotencyKey ? { idempotencyKey } : {},
13733
+ commit: commitContext,
13260
13734
  behaviors: normalizeEffectBehaviors(
13261
13735
  request.context?.behaviors || effect.metamodels || void 0
13262
13736
  ),
13263
13737
  invocation: {
13264
13738
  mode: resolved.mode,
13265
- sourceEffectKey: request.effectKey,
13266
- sourceEffectName: request.effectName,
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,
13267
13742
  ...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
13268
13743
  ...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
13269
- ...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 } : {}
13270
13746
  }
13271
13747
  };
13272
13748
  const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
@@ -13290,6 +13766,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
13290
13766
  handlerFailed = true;
13291
13767
  handlerError = error;
13292
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
+ }
13293
13779
  let feedbackError;
13294
13780
  let feedbackFailed = false;
13295
13781
  if (feedbackContext) {
@@ -13303,9 +13789,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
13303
13789
  if (handlerFailed) {
13304
13790
  throw handlerError;
13305
13791
  }
13792
+ if (commitFailed) {
13793
+ throw commitError;
13794
+ }
13306
13795
  if (feedbackFailed) {
13307
13796
  throw feedbackError;
13308
13797
  }
13798
+ if (commitRequired && !commitStarted) {
13799
+ throw new Error(
13800
+ `Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
13801
+ );
13802
+ }
13309
13803
  return handlerResult;
13310
13804
  }
13311
13805
 
@@ -13374,12 +13868,7 @@ function toRecordSearchResult(className, node) {
13374
13868
  (field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
13375
13869
  );
13376
13870
  const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
13377
- const rawLabel = typeof node.label === "string" && node.label.trim() ? node.label : "";
13378
- if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path)) {
13379
- return null;
13380
- }
13381
- const fallbackLabel = displayLabelFromFields(fields);
13382
- 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;
13383
13872
  return {
13384
13873
  path,
13385
13874
  className,
@@ -13389,30 +13878,6 @@ function toRecordSearchResult(className, node) {
13389
13878
  fields
13390
13879
  };
13391
13880
  }
13392
- function isPlaceholderRecordLabel(label, id, path) {
13393
- const normalizedLabel = normalizeGraphPathSegment(label);
13394
- return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path);
13395
- }
13396
- function displayLabelFromFields(fields) {
13397
- const preferredFieldNames = [
13398
- "name",
13399
- "title",
13400
- "label",
13401
- "display_name",
13402
- "file_name",
13403
- "number",
13404
- "code"
13405
- ];
13406
- for (const preferred of preferredFieldNames) {
13407
- const match = fields.find(
13408
- (field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
13409
- );
13410
- if (typeof match?.value === "string") {
13411
- return match.value.trim();
13412
- }
13413
- }
13414
- return null;
13415
- }
13416
13881
  function normalizeRecordSearchText(value) {
13417
13882
  return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
13418
13883
  }
@@ -13622,18 +14087,24 @@ function normalizeEnumInput(enumSpec) {
13622
14087
  (value) => typeof value === "string" && value.length > 0
13623
14088
  );
13624
14089
  if (values.length === 0) return null;
13625
- return config.message ? { values, message: config.message } : { values };
14090
+ const labels = Array.isArray(config.labels) && config.labels.length === values.length ? config.labels : values;
14091
+ return {
14092
+ values,
14093
+ labels,
14094
+ ...config.message ? { message: config.message } : {}
14095
+ };
13626
14096
  }
13627
14097
  function buildEnumFieldMutations(fieldPath, enumSpec) {
13628
14098
  const normalized = normalizeEnumInput(enumSpec);
13629
14099
  if (!normalized) return [];
13630
14100
  const messageArg = normalized.message ? `, message: ${JSON.stringify(normalized.message)}` : "";
14101
+ const labelsArg = normalized.labels ? `, labels: ${JSON.stringify(normalized.labels)}` : "";
13631
14102
  return [
13632
14103
  {
13633
14104
  label: `set enum on ${fieldPath}`,
13634
14105
  query: `mutation { at(path: ${JSON.stringify(fieldPath)}) { set_enum(values: ${JSON.stringify(
13635
14106
  normalized.values
13636
- )}${messageArg}) { values } } }`
14107
+ )}${labelsArg}${messageArg}) { values labels } } }`
13637
14108
  }
13638
14109
  ];
13639
14110
  }
@@ -13643,7 +14114,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
13643
14114
  fieldRows: [
13644
14115
  {
13645
14116
  key: "enum",
13646
- description: 'Allowed values. Accepts `["a", "b"]` or `{ "values": [...], "message": "..." }`.'
14117
+ description: 'Allowed values. Use `{ "values": [...], "labels": [...] }` for authored display labels; otherwise each unchanged value is its display fallback.'
13647
14118
  }
13648
14119
  ]
13649
14120
  },
@@ -13653,6 +14124,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
13653
14124
  type EnumMetamodel {
13654
14125
  model: Model!
13655
14126
  values: [String!]!
14127
+ labels: [String!]!
13656
14128
  message: String
13657
14129
  }
13658
14130
 
@@ -13661,7 +14133,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
13661
14133
  }
13662
14134
 
13663
14135
  extend type ModelMutation {
13664
- set_enum(values: [String!]!, message: String): EnumMetamodel
14136
+ set_enum(values: [String!]!, labels: [String!], message: String): EnumMetamodel
13665
14137
  }
13666
14138
  `
13667
14139
  ],
@@ -13670,15 +14142,19 @@ var enumMetamodelPackage = defineMetamodelPackage({
13670
14142
  EnumMetamodel: {
13671
14143
  model: (value) => value.model,
13672
14144
  values: (value) => value.values,
14145
+ labels: (value) => value.labels || [],
13673
14146
  message: (value) => value.message || null
13674
14147
  },
13675
14148
  Model: {
13676
14149
  enum_rule: async (ant) => await run(ant.enum_rule())
13677
14150
  },
13678
14151
  ModelMutation: {
13679
- set_enum: async (ant, { values, message }) => {
13680
- const model = await run(ant.set_enum(values, message));
13681
- return { model, values, message };
14152
+ set_enum: async (ant, { values, labels, message }) => {
14153
+ const resolvedLabels = Array.isArray(labels) && labels.length === values.length ? labels : values;
14154
+ const model = await run(
14155
+ ant.set_enum(values, resolvedLabels, message)
14156
+ );
14157
+ return { model, values, labels: resolvedLabels, message };
13682
14158
  }
13683
14159
  }
13684
14160
  };
@@ -13691,7 +14167,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
13691
14167
  },
13692
14168
  summary: {
13693
14169
  selections: {
13694
- propertyFields: [`enum_rule { values message }`]
14170
+ propertyFields: [`enum_rule { values labels message }`]
13695
14171
  },
13696
14172
  readPropertySummary(rawProperty) {
13697
14173
  const values = Array.isArray(rawProperty.enum_rule?.values) ? rawProperty.enum_rule.values.filter(
@@ -13699,8 +14175,15 @@ var enumMetamodelPackage = defineMetamodelPackage({
13699
14175
  ) : [];
13700
14176
  if (values.length === 0) return { enumRule: null };
13701
14177
  const message = typeof rawProperty.enum_rule?.message === "string" ? rawProperty.enum_rule.message : null;
14178
+ const labels = Array.isArray(rawProperty.enum_rule?.labels) ? rawProperty.enum_rule.labels.filter(
14179
+ (label) => typeof label === "string" && label.length > 0
14180
+ ) : [];
13702
14181
  return {
13703
- enumRule: message ? { values, message } : { values }
14182
+ enumRule: {
14183
+ values,
14184
+ ...labels.length === values.length ? { labels } : {},
14185
+ ...message ? { message } : {}
14186
+ }
13704
14187
  };
13705
14188
  }
13706
14189
  },
@@ -14357,7 +14840,8 @@ function normalizeStateMachines(values) {
14357
14840
  requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
14358
14841
  permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
14359
14842
  risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
14360
- 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)
14361
14845
  })).filter(
14362
14846
  (transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
14363
14847
  );
@@ -14450,6 +14934,11 @@ function transitionMetadataGraphqlArgs(transition) {
14450
14934
  `expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
14451
14935
  );
14452
14936
  }
14937
+ if (transition.outcomes) {
14938
+ args.push(
14939
+ `outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
14940
+ );
14941
+ }
14453
14942
  return args.length > 0 ? `, ${args.join(", ")}` : "";
14454
14943
  }
14455
14944
  function buildStateMachineModelMutations(modelPath, machines) {
@@ -14512,7 +15001,7 @@ function buildMachineTypes(classSummary, machine) {
14512
15001
  ];
14513
15002
  }
14514
15003
  function buildMachineMethods(classSummary, machine) {
14515
- const stateName = stateTypeName(classSummary.name, machine.name);
15004
+ const stateName2 = stateTypeName(classSummary.name, machine.name);
14516
15005
  const transitionName = transitionTypeName(classSummary.name, machine.name);
14517
15006
  pathTypeName(classSummary.name, machine.name);
14518
15007
  const docsPrefix = `${classSummary.name}.${machine.name}`;
@@ -14522,12 +15011,12 @@ function buildMachineMethods(classSummary, machine) {
14522
15011
  docs: [`Get the current ${docsPrefix} state.`],
14523
15012
  static: false,
14524
15013
  params: [],
14525
- returnType: `Promise<${stateName} | null>`,
15014
+ returnType: `Promise<${stateName2} | null>`,
14526
15015
  runtime: {
14527
15016
  kind: "state_machine",
14528
15017
  machineName: machine.name,
14529
15018
  className: classSummary.name,
14530
- stateTypeName: stateName,
15019
+ stateTypeName: stateName2,
14531
15020
  transitionTypeName: transitionName,
14532
15021
  operation: "get_current"
14533
15022
  }
@@ -14538,13 +15027,13 @@ function buildMachineMethods(classSummary, machine) {
14538
15027
  `Reach a ${docsPrefix} state through the shortest allowed transition path.`
14539
15028
  ],
14540
15029
  static: false,
14541
- params: [{ name: "target", type: stateName }],
15030
+ params: [{ name: "target", type: stateName2 }],
14542
15031
  returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
14543
15032
  runtime: {
14544
15033
  kind: "state_machine",
14545
15034
  machineName: machine.name,
14546
15035
  className: classSummary.name,
14547
- stateTypeName: stateName,
15036
+ stateTypeName: stateName2,
14548
15037
  transitionTypeName: transitionName,
14549
15038
  operation: "reach"
14550
15039
  }
@@ -14559,7 +15048,7 @@ function buildMachineMethods(classSummary, machine) {
14559
15048
  kind: "state_machine",
14560
15049
  machineName: machine.name,
14561
15050
  className: classSummary.name,
14562
- stateTypeName: stateName,
15051
+ stateTypeName: stateName2,
14563
15052
  transitionTypeName: transitionName,
14564
15053
  operation: "list_transitions"
14565
15054
  }
@@ -14569,12 +15058,12 @@ function buildMachineMethods(classSummary, machine) {
14569
15058
  docs: [`List reachable states for ${docsPrefix} from the current state.`],
14570
15059
  static: false,
14571
15060
  params: [],
14572
- returnType: `Promise<${stateName}[]>`,
15061
+ returnType: `Promise<${stateName2}[]>`,
14573
15062
  runtime: {
14574
15063
  kind: "state_machine",
14575
15064
  machineName: machine.name,
14576
15065
  className: classSummary.name,
14577
- stateTypeName: stateName,
15066
+ stateTypeName: stateName2,
14578
15067
  transitionTypeName: transitionName,
14579
15068
  operation: "list_reachable_states"
14580
15069
  }
@@ -14589,7 +15078,7 @@ function buildMachineMethods(classSummary, machine) {
14589
15078
  kind: "state_machine",
14590
15079
  machineName: machine.name,
14591
15080
  className: classSummary.name,
14592
- stateTypeName: stateName,
15081
+ stateTypeName: stateName2,
14593
15082
  transitionTypeName: transitionName,
14594
15083
  operation: "is_final"
14595
15084
  }
@@ -14600,13 +15089,13 @@ function buildMachineMethods(classSummary, machine) {
14600
15089
  `List shortest transition paths from the current ${docsPrefix} state to a target state.`
14601
15090
  ],
14602
15091
  static: false,
14603
- params: [{ name: "target", type: stateName }],
14604
- returnType: `Promise<Array<{ states: ${stateName}[]; transitions: ${transitionName}[] }>>`,
15092
+ params: [{ name: "target", type: stateName2 }],
15093
+ returnType: `Promise<Array<{ states: ${stateName2}[]; transitions: ${transitionName}[] }>>`,
14605
15094
  runtime: {
14606
15095
  kind: "state_machine",
14607
15096
  machineName: machine.name,
14608
15097
  className: classSummary.name,
14609
- stateTypeName: stateName,
15098
+ stateTypeName: stateName2,
14610
15099
  transitionTypeName: transitionName,
14611
15100
  operation: "paths_to"
14612
15101
  }
@@ -14630,7 +15119,7 @@ function buildMachineMethods(classSummary, machine) {
14630
15119
  kind: "state_machine",
14631
15120
  machineName: machine.name,
14632
15121
  className: classSummary.name,
14633
- stateTypeName: stateName,
15122
+ stateTypeName: stateName2,
14634
15123
  transitionTypeName: transitionName,
14635
15124
  operation: "reach",
14636
15125
  targetState: stateNameValue,
@@ -14649,7 +15138,7 @@ function buildMachineMethods(classSummary, machine) {
14649
15138
  kind: "state_machine",
14650
15139
  machineName: machine.name,
14651
15140
  className: classSummary.name,
14652
- stateTypeName: stateName,
15141
+ stateTypeName: stateName2,
14653
15142
  transitionTypeName: transitionName,
14654
15143
  operation: "prepare_reach",
14655
15144
  targetState: stateNameValue,
@@ -14662,7 +15151,7 @@ function buildMachineMethods(classSummary, machine) {
14662
15151
  kind: "state_machine",
14663
15152
  machineName: machine.name,
14664
15153
  className: classSummary.name,
14665
- stateTypeName: stateName,
15154
+ stateTypeName: stateName2,
14666
15155
  transitionTypeName: transitionName,
14667
15156
  operation: "prepare_create_reach",
14668
15157
  targetState: stateNameValue,
@@ -14728,7 +15217,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14728
15217
  name: String!
14729
15218
  state_machine: StateMachine!
14730
15219
  add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
14731
- 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!
14732
15221
  activate_transition(name: String!): StateMachineMutation!
14733
15222
  }
14734
15223
 
@@ -14745,6 +15234,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14745
15234
  type StateMachineSnapshotMutation {
14746
15235
  snapshot: StateMachineSnapshot!
14747
15236
  activate_transition(name: String!): StateMachineSnapshotMutation!
15237
+ commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
14748
15238
  observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
14749
15239
  }
14750
15240
 
@@ -14791,6 +15281,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14791
15281
  permission_json: String
14792
15282
  risk: String
14793
15283
  expected_outcome_json: String
15284
+ outcomes_json: String
14794
15285
  }
14795
15286
 
14796
15287
  type StateMachinePath {
@@ -14801,6 +15292,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14801
15292
  type StateMachineTransitionEvent {
14802
15293
  sequence: Int!
14803
15294
  occurred_at: Float!
15295
+ commit_id: String
15296
+ outcome: String
15297
+ source_version: String
15298
+ projected_from_mismatch: String
14804
15299
  transition: StateMachineTransition!
14805
15300
  from: StateMachineState!
14806
15301
  to: StateMachineState!
@@ -14866,7 +15361,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14866
15361
  requirements_json,
14867
15362
  permission_json,
14868
15363
  risk,
14869
- expected_outcome_json
15364
+ expected_outcome_json,
15365
+ outcomes_json
14870
15366
  }) => {
14871
15367
  await run(
14872
15368
  value.target.add_state_machine_transition(
@@ -14882,7 +15378,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14882
15378
  requirementsJson: requirements_json,
14883
15379
  permissionJson: permission_json,
14884
15380
  risk,
14885
- expectedOutcomeJson: expected_outcome_json
15381
+ expectedOutcomeJson: expected_outcome_json,
15382
+ outcomesJson: outcomes_json
14886
15383
  }
14887
15384
  )
14888
15385
  );
@@ -14903,6 +15400,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14903
15400
  );
14904
15401
  return value;
14905
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
+ },
14906
15416
  observe_state: async (value, { state, force, source }) => {
14907
15417
  await run(
14908
15418
  value.target.observe_state_machine_state(
@@ -14932,7 +15442,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14932
15442
  requirements_json: (value) => value.requirements_json || null,
14933
15443
  permission_json: (value) => value.permission_json || null,
14934
15444
  risk: (value) => value.risk || null,
14935
- 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
14936
15447
  },
14937
15448
  StateMachinePath: {
14938
15449
  states: (value) => value.states,
@@ -14941,6 +15452,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14941
15452
  StateMachineTransitionEvent: {
14942
15453
  sequence: (value) => value.sequence,
14943
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,
14944
15459
  transition: (value) => value.transition,
14945
15460
  from: (value) => value.from,
14946
15461
  to: (value) => value.to
@@ -15014,6 +15529,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
15014
15529
  permission_json
15015
15530
  risk
15016
15531
  expected_outcome_json
15532
+ outcomes_json
15017
15533
  }
15018
15534
  }`
15019
15535
  ]
@@ -15741,6 +16257,133 @@ var Environment = class _Environment {
15741
16257
  getAwaitingCount: async () => this.getAwaitingRecordCount()
15742
16258
  };
15743
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
+ }
15744
16387
  /**
15745
16388
  * Mirror product-owned workflow state into Granular without making Granular
15746
16389
  * own the customer application's state machine.
@@ -15780,8 +16423,8 @@ var Environment = class _Environment {
15780
16423
  * `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
15781
16424
  */
15782
16425
  state(target) {
15783
- const observe = async (machineName, stateName, input = {}) => {
15784
- const observedState = input.observedState || input.state || stateName;
16426
+ const observe = async (machineName, stateName2, input = {}) => {
16427
+ const observedState = input.observedState || input.state || stateName2;
15785
16428
  if (!observedState) {
15786
16429
  throw new Error("State observation requires a target state");
15787
16430
  }
@@ -15807,7 +16450,7 @@ var Environment = class _Environment {
15807
16450
  {
15808
16451
  get: (_machineTarget, stateProperty) => {
15809
16452
  if (stateProperty === "to") {
15810
- return (stateName, input) => observe(machineProperty, stateName, input || {});
16453
+ return (stateName2, input) => observe(machineProperty, stateName2, input || {});
15811
16454
  }
15812
16455
  if (typeof stateProperty !== "string") return void 0;
15813
16456
  return (input) => observe(
@@ -17147,6 +17790,14 @@ var EnvironmentSession = class extends Session {
17147
17790
  }
17148
17791
  };
17149
17792
  }
17793
+ get mutations() {
17794
+ return {
17795
+ list: (options = {}) => this.sessionDataRequest(
17796
+ "/mutations",
17797
+ options
17798
+ )
17799
+ };
17800
+ }
17150
17801
  get artifacts() {
17151
17802
  return {
17152
17803
  list: (options = {}) => {
@@ -18646,6 +19297,7 @@ var Granular = class _Granular {
18646
19297
  const serialized = {
18647
19298
  effectKey: computeEffectKey2(effect),
18648
19299
  name: effect.name,
19300
+ ...effect.label ? { label: effect.label } : {},
18649
19301
  description: effect.description,
18650
19302
  inputSchema: effect.inputSchema,
18651
19303
  stability: effect.stability || "stable",
@@ -18669,6 +19321,9 @@ var Granular = class _Granular {
18669
19321
  if (effect.metamodels !== void 0) {
18670
19322
  serialized.metamodels = effect.metamodels;
18671
19323
  }
19324
+ if (effect.commit !== void 0) {
19325
+ serialized.commit = { kind: effect.commit.kind };
19326
+ }
18672
19327
  return serialized;
18673
19328
  }
18674
19329
  async publishSandboxEffectCatalog(host) {
@@ -18889,16 +19544,60 @@ var Granular = class _Granular {
18889
19544
  };
18890
19545
  wsClient.registerRpcHandler("effect.invoke", async (params) => {
18891
19546
  const request = params;
18892
- return invokeRegisteredEffect(
19547
+ let commitReceipt;
19548
+ const result = await invokeRegisteredEffect(
18893
19549
  this.getSandboxEffectMap(sandboxId),
18894
19550
  request,
18895
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
+ },
18896
19590
  feedback: {
18897
19591
  invocationId: request.callId,
18898
19592
  publish: (method, publishParams) => wsClient.call(method, publishParams)
18899
19593
  }
18900
19594
  }
18901
19595
  );
19596
+ return {
19597
+ __granularEffectInvocationResult: true,
19598
+ result,
19599
+ ...commitReceipt ? { commit: commitReceipt } : {}
19600
+ };
18902
19601
  });
18903
19602
  wsClient.on("open", () => {
18904
19603
  void this.synchronizeEffectHost(host).catch((error) => {
@@ -19470,6 +20169,275 @@ var Granular = class _Granular {
19470
20169
  }
19471
20170
  };
19472
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
+
19473
20441
  // src/agent-harness-templates/action-presentation/0.1.0/manifest.json
19474
20442
  var manifest_default = {
19475
20443
  id: "action-presentation",
@@ -22478,6 +23446,6 @@ function calculateOpenAITokenSpend(model, rawUsage) {
22478
23446
  };
22479
23447
  }
22480
23448
 
22481
- 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 };
22482
23450
  //# sourceMappingURL=index.mjs.map
22483
23451
  //# sourceMappingURL=index.mjs.map