@granular-software/sdk 0.4.72 → 0.4.74

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.js CHANGED
@@ -4300,8 +4300,8 @@ function normalizeFeedItems(values) {
4300
4300
  return { items: Object.freeze(items), error };
4301
4301
  }
4302
4302
  function normalizeTransients(values) {
4303
- const record2 = asRecord(values);
4304
- if (!record2) {
4303
+ const record3 = asRecord(values);
4304
+ if (!record3) {
4305
4305
  return {
4306
4306
  items: [],
4307
4307
  error: new Error("Canonical feed transientById is not an object.")
@@ -4310,7 +4310,7 @@ function normalizeTransients(values) {
4310
4310
  let error = null;
4311
4311
  const byId = /* @__PURE__ */ new Map();
4312
4312
  const idByOrdinal = /* @__PURE__ */ new Map();
4313
- for (const [key, value] of Object.entries(record2)) {
4313
+ for (const [key, value] of Object.entries(record3)) {
4314
4314
  const item = normalizeTransientFeedItem(value);
4315
4315
  if (!item || item.id !== key) {
4316
4316
  error ||= new Error("Canonical feed contains an invalid transient item.");
@@ -5183,11 +5183,11 @@ function operationId(prefix) {
5183
5183
  return randomUuid ? `${prefix}_${randomUuid}` : `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
5184
5184
  }
5185
5185
  function unwrapPublishedItem(value) {
5186
- const record2 = asRecord(value);
5187
- const directItems = Array.isArray(record2?.items) ? record2.items : [];
5188
- const durable = asRecord(record2?.durable);
5186
+ const record3 = asRecord(value);
5187
+ const directItems = Array.isArray(record3?.items) ? record3.items : [];
5188
+ const durable = asRecord(record3?.durable);
5189
5189
  const durableItems = Array.isArray(durable?.items) ? durable.items : [];
5190
- return record2?.item || directItems[0] || durableItems[0] || value;
5190
+ return record3?.item || directItems[0] || durableItems[0] || value;
5191
5191
  }
5192
5192
  function requireFeedbackItem(value) {
5193
5193
  const item = normalizeFeedItem(unwrapPublishedItem(value));
@@ -5309,9 +5309,9 @@ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 5;
5309
5309
  var DOCUMENT_RESYNC_CLOSE_CODE = 4e3;
5310
5310
  var DOCUMENT_RESYNC_CLOSE_REASON = "Document resync required";
5311
5311
  function documentVersion(document) {
5312
- const record2 = document;
5313
- const epochValue = record2.documentEpoch;
5314
- const revisionValue = record2.documentRevision;
5312
+ const record3 = document;
5313
+ const epochValue = record3.documentEpoch;
5314
+ const revisionValue = record3.documentRevision;
5315
5315
  const epoch = epochValue === void 0 ? 0 : epochValue;
5316
5316
  const revision = revisionValue === void 0 ? 0 : revisionValue;
5317
5317
  if (!Number.isSafeInteger(epoch) || Number(epoch) < 0 || !Number.isSafeInteger(revision) || Number(revision) < 0) {
@@ -5333,9 +5333,9 @@ function stableDocumentFingerprint(value) {
5333
5333
  if (Array.isArray(value)) {
5334
5334
  return `[${value.map(stableDocumentFingerprint).join(",")}]`;
5335
5335
  }
5336
- const record2 = value;
5337
- return `{${Object.keys(record2).sort().map(
5338
- (key) => `${JSON.stringify(key)}:${stableDocumentFingerprint(record2[key])}`
5336
+ const record3 = value;
5337
+ return `{${Object.keys(record3).sort().map(
5338
+ (key) => `${JSON.stringify(key)}:${stableDocumentFingerprint(record3[key])}`
5339
5339
  ).join(",")}}`;
5340
5340
  }
5341
5341
  function canonicalDocumentFingerprint(value) {
@@ -5345,9 +5345,9 @@ function canonicalDocumentFingerprint(value) {
5345
5345
  if (Array.isArray(input)) {
5346
5346
  return input.map((entry) => stripPresentation(entry, path));
5347
5347
  }
5348
- const record2 = input;
5348
+ const record3 = input;
5349
5349
  const output = {};
5350
- for (const [key, entry] of Object.entries(record2)) {
5350
+ for (const [key, entry] of Object.entries(record3)) {
5351
5351
  if (path.length === 1 && path[0] === "feed" && [
5352
5352
  "transientById",
5353
5353
  "feedbackUsageByJob",
@@ -6451,30 +6451,30 @@ function parseJsonPromptChoiceOption(option) {
6451
6451
  }
6452
6452
  function normalizePromptChoiceOption(option) {
6453
6453
  if (typeof option === "string") {
6454
- const record3 = parseJsonPromptChoiceOption(option);
6455
- if (!record3) {
6454
+ const record4 = parseJsonPromptChoiceOption(option);
6455
+ if (!record4) {
6456
6456
  return { value: option, label: option };
6457
6457
  }
6458
- const value2 = typeof record3.value === "string" ? record3.value : typeof record3.id === "string" ? record3.id : typeof record3.label === "string" ? record3.label : JSON.stringify(record3);
6458
+ const value2 = typeof record4.value === "string" ? record4.value : typeof record4.id === "string" ? record4.id : typeof record4.label === "string" ? record4.label : JSON.stringify(record4);
6459
6459
  return {
6460
6460
  value: value2,
6461
- label: typeof record3.label === "string" ? record3.label : value2,
6462
- description: typeof record3.description === "string" ? record3.description : void 0
6461
+ label: typeof record4.label === "string" ? record4.label : value2,
6462
+ description: typeof record4.description === "string" ? record4.description : void 0
6463
6463
  };
6464
6464
  }
6465
- const record2 = option;
6466
- if (!record2) {
6465
+ const record3 = option;
6466
+ if (!record3) {
6467
6467
  return { value: "", label: "" };
6468
6468
  }
6469
- const nestedJson = (typeof record2.value === "string" ? parseJsonPromptChoiceOption(record2.value) : null) || (typeof record2.label === "string" ? parseJsonPromptChoiceOption(record2.label) : null);
6469
+ const nestedJson = (typeof record3.value === "string" ? parseJsonPromptChoiceOption(record3.value) : null) || (typeof record3.label === "string" ? parseJsonPromptChoiceOption(record3.label) : null);
6470
6470
  if (nestedJson) {
6471
6471
  return normalizePromptChoiceOption(nestedJson);
6472
6472
  }
6473
- const value = typeof record2.value === "string" ? record2.value : typeof record2.label === "string" ? record2.label : JSON.stringify(record2);
6473
+ const value = typeof record3.value === "string" ? record3.value : typeof record3.label === "string" ? record3.label : JSON.stringify(record3);
6474
6474
  return {
6475
6475
  value,
6476
- label: typeof record2.label === "string" ? record2.label : value,
6477
- description: typeof record2.description === "string" ? record2.description : void 0
6476
+ label: typeof record3.label === "string" ? record3.label : value,
6477
+ description: typeof record3.description === "string" ? record3.description : void 0
6478
6478
  };
6479
6479
  }
6480
6480
  function scorePromptChoiceMatch(answer, answerTokens, option) {
@@ -7982,8 +7982,8 @@ function artifactRecordsById(liveDoc) {
7982
7982
  const byId = asRecord3(artifacts?.byId) || {};
7983
7983
  return Object.fromEntries(
7984
7984
  Object.entries(byId).map(([artifactId, value]) => {
7985
- const record2 = asRecord3(value);
7986
- return record2 ? [artifactId, record2] : null;
7985
+ const record3 = asRecord3(value);
7986
+ return record3 ? [artifactId, record3] : null;
7987
7987
  }).filter((entry) => Boolean(entry))
7988
7988
  );
7989
7989
  }
@@ -8016,8 +8016,8 @@ function latestInputEditSummary(metadata) {
8016
8016
  ].filter(Boolean);
8017
8017
  return `lastEdit=${source}${actor ? ` by ${actor}` : ""}${changed.length ? ` (${changed.join("; ")})` : ""}`;
8018
8018
  }
8019
- function artifactIssueSummary(record2) {
8020
- const validation = asRecord3(record2.validation);
8019
+ function artifactIssueSummary(record3) {
8020
+ const validation = asRecord3(record3.validation);
8021
8021
  if (!validation) return null;
8022
8022
  const issues = Array.isArray(validation.issues) ? validation.issues.map((issue) => asRecord3(issue)).filter((issue) => Boolean(issue)).slice(0, 3) : [];
8023
8023
  if (issues.length > 0) {
@@ -8064,18 +8064,18 @@ function artifactStatePathSummary(metadata) {
8064
8064
  ].filter(Boolean);
8065
8065
  return pieces.length ? pieces.join("; ") : null;
8066
8066
  }
8067
- function artifactSummaryLine(artifactId, record2) {
8068
- if (!record2) return `- ${artifactId}: unavailable in session artifact store`;
8069
- const label = trimString(record2.label) || artifactId;
8070
- const kind = asString(record2.kind) || "artifact";
8071
- const status = asString(record2.status) || "unknown";
8072
- const createdByJobId = asString(record2.createdByJobId);
8073
- const target = asRecord3(record2.target);
8074
- const metadata = asRecord3(record2.metadata) || {};
8075
- const subArtifactIds = Array.isArray(record2.subArtifactIds) ? record2.subArtifactIds.filter(
8067
+ function artifactSummaryLine(artifactId, record3) {
8068
+ if (!record3) return `- ${artifactId}: unavailable in session artifact store`;
8069
+ const label = trimString(record3.label) || artifactId;
8070
+ const kind = asString(record3.kind) || "artifact";
8071
+ const status = asString(record3.status) || "unknown";
8072
+ const createdByJobId = asString(record3.createdByJobId);
8073
+ const target = asRecord3(record3.target);
8074
+ const metadata = asRecord3(record3.metadata) || {};
8075
+ const subArtifactIds = Array.isArray(record3.subArtifactIds) ? record3.subArtifactIds.filter(
8076
8076
  (id) => typeof id === "string" && id.trim().length > 0
8077
8077
  ).slice(0, 8) : [];
8078
- const relationships = compactJson(record2.relationships, 220);
8078
+ const relationships = compactJson(record3.relationships, 220);
8079
8079
  const pieces = [
8080
8080
  `kind=${kind}`,
8081
8081
  `status=${status}`,
@@ -8083,7 +8083,7 @@ function artifactSummaryLine(artifactId, record2) {
8083
8083
  target ? `target=${asString(target.className) || "record"}:${asString(target.id) || "unknown"}${asString(target.label) ? ` (${asString(target.label)})` : ""}` : null,
8084
8084
  artifactStatePathSummary(metadata),
8085
8085
  artifactExecutionSummary(metadata),
8086
- artifactIssueSummary(record2),
8086
+ artifactIssueSummary(record3),
8087
8087
  latestInputEditSummary(metadata),
8088
8088
  subArtifactIds.length ? `subArtifacts=${subArtifactIds.join(",")}` : null,
8089
8089
  relationships ? `relationships=${relationships}` : null
@@ -13194,6 +13194,300 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
13194
13194
  }
13195
13195
  });
13196
13196
 
13197
+ // src/record-snapshot-projection.ts
13198
+ function stableValue(value) {
13199
+ if (Array.isArray(value)) return value.map(stableValue);
13200
+ if (value && typeof value === "object") {
13201
+ return Object.fromEntries(
13202
+ Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stableValue(item)])
13203
+ );
13204
+ }
13205
+ return value;
13206
+ }
13207
+ function stableJson(value) {
13208
+ return JSON.stringify(stableValue(value));
13209
+ }
13210
+ function recordKey(record3) {
13211
+ return `${record3.className}\0${record3.id}`;
13212
+ }
13213
+ function recordReference(className, id) {
13214
+ return { className, id };
13215
+ }
13216
+ function changedRecordSnapshots(beforeRecords, afterRecords) {
13217
+ const before = new Map(
13218
+ beforeRecords.map((record3) => [recordKey(record3), record3])
13219
+ );
13220
+ const after = new Map(
13221
+ afterRecords.map((record3) => [recordKey(record3), record3])
13222
+ );
13223
+ const changedKeys = /* @__PURE__ */ new Set();
13224
+ for (const key of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) {
13225
+ if (stableJson(before.get(key)) !== stableJson(after.get(key))) {
13226
+ changedKeys.add(key);
13227
+ }
13228
+ }
13229
+ return {
13230
+ beforeRecords: beforeRecords.filter(
13231
+ (record3) => changedKeys.has(recordKey(record3))
13232
+ ),
13233
+ afterRecords: afterRecords.filter(
13234
+ (record3) => changedKeys.has(recordKey(record3))
13235
+ )
13236
+ };
13237
+ }
13238
+ function relationshipTargets(manifest) {
13239
+ const targets = /* @__PURE__ */ new Map();
13240
+ for (const volume of manifest.volumes) {
13241
+ for (const operation of volume.operations) {
13242
+ const relationship = operation.defineRelationship;
13243
+ if (!relationship) continue;
13244
+ targets.set(`${relationship.left}\0${relationship.leftSubmodel}`, {
13245
+ className: relationship.right
13246
+ });
13247
+ targets.set(`${relationship.right}\0${relationship.rightSubmodel}`, {
13248
+ className: relationship.left
13249
+ });
13250
+ }
13251
+ }
13252
+ return targets;
13253
+ }
13254
+ function stateName(value) {
13255
+ return typeof value === "string" ? value : value.state;
13256
+ }
13257
+ function asIds(value) {
13258
+ return value === void 0 ? [] : Array.isArray(value) ? value : [value];
13259
+ }
13260
+ function changedObjectProjection(before, after) {
13261
+ const beforeFields = before?.fields || {};
13262
+ const afterFields = after.fields || {};
13263
+ const fields = { ...afterFields };
13264
+ for (const fieldName of Object.keys(beforeFields)) {
13265
+ if (!(fieldName in afterFields)) fields[fieldName] = null;
13266
+ }
13267
+ if (before && before.label === after.label && stableJson(beforeFields) === stableJson(afterFields)) {
13268
+ return null;
13269
+ }
13270
+ return {
13271
+ kind: "object",
13272
+ operation: before ? "updated" : "created",
13273
+ record: {
13274
+ className: after.className,
13275
+ id: after.id,
13276
+ ...after.label !== void 0 ? { label: after.label } : {},
13277
+ fields
13278
+ }
13279
+ };
13280
+ }
13281
+ function projectionChanges(input) {
13282
+ const before = new Map(
13283
+ input.beforeRecords.map((record3) => [recordKey(record3), record3])
13284
+ );
13285
+ const after = new Map(
13286
+ input.afterRecords.map((record3) => [recordKey(record3), record3])
13287
+ );
13288
+ const targets = relationshipTargets(input.manifest);
13289
+ const changes = [];
13290
+ for (const record3 of input.afterRecords) {
13291
+ const previous = before.get(recordKey(record3));
13292
+ const objectChange = changedObjectProjection(previous, record3);
13293
+ if (objectChange) changes.push(objectChange);
13294
+ const relationshipNames = /* @__PURE__ */ new Set([
13295
+ ...Object.keys(previous?.relationships || {}),
13296
+ ...Object.keys(record3.relationships || {})
13297
+ ]);
13298
+ for (const relationshipName of [...relationshipNames].sort()) {
13299
+ const target = targets.get(
13300
+ `${record3.className}\0${relationshipName}`
13301
+ );
13302
+ if (!target) {
13303
+ throw new Error(
13304
+ `No ontology relationship target is declared for ${record3.className}.${relationshipName}`
13305
+ );
13306
+ }
13307
+ const previousIds = new Set(
13308
+ asIds(previous?.relationships?.[relationshipName])
13309
+ );
13310
+ const currentIds = new Set(
13311
+ asIds(record3.relationships?.[relationshipName])
13312
+ );
13313
+ for (const id of [...previousIds].sort()) {
13314
+ if (currentIds.has(id)) continue;
13315
+ changes.push({
13316
+ kind: "relationship",
13317
+ operation: "disconnected",
13318
+ relationship: relationshipName,
13319
+ from: recordReference(record3.className, record3.id),
13320
+ to: recordReference(target.className, id)
13321
+ });
13322
+ }
13323
+ for (const id of [...currentIds].sort()) {
13324
+ if (previousIds.has(id)) continue;
13325
+ changes.push({
13326
+ kind: "relationship",
13327
+ operation: "connected",
13328
+ relationship: relationshipName,
13329
+ from: recordReference(record3.className, record3.id),
13330
+ to: recordReference(target.className, id)
13331
+ });
13332
+ }
13333
+ }
13334
+ const machines = /* @__PURE__ */ new Set([
13335
+ ...Object.keys(previous?.states || {}),
13336
+ ...Object.keys(record3.states || {})
13337
+ ]);
13338
+ for (const machine of [...machines].sort()) {
13339
+ const next = record3.states?.[machine];
13340
+ if (next === void 0) continue;
13341
+ const prior = previous?.states?.[machine];
13342
+ if (prior !== void 0 && stateName(prior) === stateName(next)) continue;
13343
+ if (input.transition && input.transition.className === record3.className && input.transition.objectId === record3.id && input.transition.machine === machine) {
13344
+ continue;
13345
+ }
13346
+ changes.push({
13347
+ kind: "state_observation",
13348
+ target: recordReference(record3.className, record3.id),
13349
+ machine,
13350
+ state: stateName(next)
13351
+ });
13352
+ }
13353
+ }
13354
+ for (const record3 of input.beforeRecords) {
13355
+ if (after.has(recordKey(record3))) continue;
13356
+ changes.push({
13357
+ kind: "object",
13358
+ operation: "deleted",
13359
+ target: recordReference(record3.className, record3.id)
13360
+ });
13361
+ }
13362
+ return changes;
13363
+ }
13364
+ function valueAtPath(value, path) {
13365
+ return path.split(".").filter(Boolean).reduce((current, segment) => {
13366
+ if (!current || typeof current !== "object" || Array.isArray(current)) {
13367
+ return void 0;
13368
+ }
13369
+ return current[segment];
13370
+ }, value);
13371
+ }
13372
+ function primaryTarget(input) {
13373
+ if (input.transition) {
13374
+ return recordReference(
13375
+ input.transition.className,
13376
+ input.transition.objectId
13377
+ );
13378
+ }
13379
+ const creates = input.effect.metamodels?.creates;
13380
+ if (creates) {
13381
+ const declaration = typeof creates === "string" ? { className: creates } : creates;
13382
+ const id = declaration.idPath ? valueAtPath(input.result, declaration.idPath) : void 0;
13383
+ if (typeof id === "string" && id) {
13384
+ return recordReference(declaration.className, id);
13385
+ }
13386
+ }
13387
+ if (input.effect.className && !input.effect.static) {
13388
+ const objectId = input.effectInput && typeof input.effectInput === "object" && !Array.isArray(input.effectInput) ? input.effectInput._objectId : void 0;
13389
+ if (typeof objectId === "string" && objectId) {
13390
+ return recordReference(input.effect.className, objectId);
13391
+ }
13392
+ }
13393
+ const created = input.changes.filter(
13394
+ (change) => change.kind === "object" && change.operation === "created"
13395
+ );
13396
+ const onlyCreated = created.length === 1 ? created[0] : void 0;
13397
+ return onlyCreated ? recordReference(onlyCreated.record.className, onlyCreated.record.id) : void 0;
13398
+ }
13399
+ function assertRecordSnapshotTransitionMapping(transition) {
13400
+ if (!transition)
13401
+ throw new Error("A transition commit has no authored transition context.");
13402
+ if (!transition.outcomes || !Object.keys(transition.outcomes).length) {
13403
+ throw new Error(
13404
+ "Automatic product-state mapping requires at least one declared outcome."
13405
+ );
13406
+ }
13407
+ const destinations = /* @__PURE__ */ new Map();
13408
+ for (const [key, outcome] of Object.entries(transition.outcomes)) {
13409
+ if (!outcome || typeof outcome.to !== "string" || !outcome.to) {
13410
+ throw new Error(
13411
+ `Automatic product-state mapping requires a destination for outcome ${key}.`
13412
+ );
13413
+ }
13414
+ const destination = outcome.to === "$current" ? transition.from : outcome.to;
13415
+ const previous = destinations.get(destination);
13416
+ if (previous !== void 0) {
13417
+ throw new Error(
13418
+ `Ambiguous product-state mapping for ${transition.machine}.${transition.transition}: outcomes ${previous} and ${key} both resolve to ${destination}; automatic mapping requires distinct outcome destinations.`
13419
+ );
13420
+ }
13421
+ destinations.set(destination, key);
13422
+ }
13423
+ }
13424
+ function transitionOutcome(transition, afterRecords) {
13425
+ assertRecordSnapshotTransitionMapping(transition);
13426
+ const target = afterRecords.find(
13427
+ (record3) => record3.className === transition.className && record3.id === transition.objectId
13428
+ );
13429
+ const observed = target?.states?.[transition.machine];
13430
+ if (!observed) {
13431
+ throw new Error(
13432
+ `The product result did not expose ${transition.className}:${transition.objectId}.${transition.machine}`
13433
+ );
13434
+ }
13435
+ const finalState = stateName(observed);
13436
+ const matches = Object.entries(transition.outcomes).filter(
13437
+ ([, outcome]) => outcome.to === finalState || outcome.to === "$current" && finalState === transition.from
13438
+ );
13439
+ if (matches.length !== 1) {
13440
+ throw new Error(
13441
+ `Product state ${finalState} maps to ${matches.length} authored outcomes for ${transition.machine}.${transition.transition}`
13442
+ );
13443
+ }
13444
+ const [key, declaration] = matches[0];
13445
+ return {
13446
+ key,
13447
+ ...declaration.disposition === "error" ? {
13448
+ error: {
13449
+ code: `product_outcome_${key}`,
13450
+ message: declaration.label || `The product completed in ${finalState} instead of continuing.`,
13451
+ retryable: false
13452
+ }
13453
+ } : {}
13454
+ };
13455
+ }
13456
+ function projectRecordSnapshotMutation(input) {
13457
+ const changes = projectionChanges({
13458
+ manifest: input.manifest,
13459
+ beforeRecords: input.source.beforeRecords,
13460
+ afterRecords: input.source.afterRecords,
13461
+ transition: input.transition
13462
+ });
13463
+ const target = primaryTarget({
13464
+ effect: input.effect,
13465
+ effectInput: input.effectInput,
13466
+ result: input.result,
13467
+ changes,
13468
+ transition: input.transition
13469
+ });
13470
+ const base = {
13471
+ source: {
13472
+ reference: input.source.reference,
13473
+ ...input.source.version ? { version: input.source.version } : {},
13474
+ ...input.source.versionScope ? { versionScope: input.source.versionScope } : {}
13475
+ },
13476
+ ...target ? { primaryTarget: target } : {},
13477
+ changes,
13478
+ safeSummary: {
13479
+ effect: input.effect.name,
13480
+ affectedChanges: changes.length
13481
+ }
13482
+ };
13483
+ if (input.commitKind === "effect") return base;
13484
+ return {
13485
+ ...base,
13486
+ outcome: transitionOutcome(input.transition, input.source.afterRecords)
13487
+ };
13488
+ }
13489
+ projectRecordSnapshotMutation.validateTransition = assertRecordSnapshotTransitionMapping;
13490
+
13197
13491
  // src/commit.ts
13198
13492
  var MAX_PROJECTION_CHANGES = 1e3;
13199
13493
  var MAX_SAFE_SUMMARY_KEYS = 32;
@@ -13287,18 +13581,18 @@ function validateScalarRecord(value, path) {
13287
13581
  }
13288
13582
  function validateProjectedRecord(value, path) {
13289
13583
  validateObjectReference(value, path);
13290
- const record2 = value;
13291
- if (record2.label !== void 0) {
13292
- if (typeof record2.label !== "string") {
13584
+ const record3 = value;
13585
+ if (record3.label !== void 0) {
13586
+ if (typeof record3.label !== "string") {
13293
13587
  throw new Error(`${path}.label must be a string`);
13294
13588
  }
13295
- if (record2.label.length > MAX_SAFE_TEXT_LENGTH) {
13589
+ if (record3.label.length > MAX_SAFE_TEXT_LENGTH) {
13296
13590
  throw new Error(
13297
13591
  `${path}.label exceeds ${MAX_SAFE_TEXT_LENGTH} characters`
13298
13592
  );
13299
13593
  }
13300
13594
  }
13301
- validateScalarRecord(record2.fields, `${path}.fields`);
13595
+ validateScalarRecord(record3.fields, `${path}.fields`);
13302
13596
  }
13303
13597
  function validateBoundedJson(value, path, depth = 0) {
13304
13598
  if (depth > 12) throw new Error(`${path} is nested too deeply`);
@@ -13802,6 +14096,11 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
13802
14096
  `Transition effect ${request.effectKey} has no resolved transition context; customer code was not invoked`
13803
14097
  );
13804
14098
  }
14099
+ if (commitRequired && declaration?.kind === "transition" && declaration.outcomeSelection === "product_state") {
14100
+ assertRecordSnapshotTransitionMapping(
14101
+ request.context?.invocation?.transition
14102
+ );
14103
+ }
13805
14104
  const idempotencyKey = commitRequired ? await resolveInvocationIdempotencyKey(request) : request.context?.idempotencyKey;
13806
14105
  let commitStarted = false;
13807
14106
  let commitPromise = null;
@@ -16365,7 +16664,7 @@ function stateTransitionSteps(artifact) {
16365
16664
  {
16366
16665
  index: typeof step.index === "number" ? step.index : fallbackIndex,
16367
16666
  transition,
16368
- label: text(step.label) || humanize(transition),
16667
+ label: text(transitionDescription?.label) || text(step.label) || humanize(transition),
16369
16668
  description: text(step.description) || text(record(step.expectedOutcome)?.summary) || null,
16370
16669
  status: text(step.status) || "pending",
16371
16670
  // State plans may contain bookkeeping transitions that are necessary
@@ -17047,17 +17346,17 @@ function planRecordObjectsChunks(records, batchSize) {
17047
17346
  }
17048
17347
  return plans;
17049
17348
  }
17050
- function preserveRecordObjectRealId(record2) {
17051
- const realId = record2.id.trim();
17349
+ function preserveRecordObjectRealId(record3) {
17350
+ const realId = record3.id.trim();
17052
17351
  if (!realId) {
17053
- return record2;
17352
+ return record3;
17054
17353
  }
17055
- const fields = record2.fields || {};
17354
+ const fields = record3.fields || {};
17056
17355
  if (typeof fields.real_id === "string" && fields.real_id.trim()) {
17057
- return record2;
17356
+ return record3;
17058
17357
  }
17059
17358
  return {
17060
- ...record2,
17359
+ ...record3,
17061
17360
  fields: {
17062
17361
  ...fields,
17063
17362
  real_id: realId
@@ -17278,13 +17577,15 @@ var Environment = class _Environment {
17278
17577
  _apiEndpoint;
17279
17578
  _tokenProvider;
17280
17579
  _beforeMutationSend;
17281
- constructor(granular, envData, apiKey, apiEndpoint, tokenProvider, beforeMutationSend) {
17580
+ _graphqlIdempotencyKey;
17581
+ constructor(granular, envData, apiKey, apiEndpoint, tokenProvider, beforeMutationSend, graphqlIdempotencyKey) {
17282
17582
  this.granular = granular;
17283
17583
  this.envData = envData;
17284
17584
  this._apiKey = apiKey;
17285
17585
  this._apiEndpoint = apiEndpoint;
17286
17586
  this._tokenProvider = tokenProvider;
17287
17587
  this._beforeMutationSend = beforeMutationSend;
17588
+ this._graphqlIdempotencyKey = graphqlIdempotencyKey;
17288
17589
  }
17289
17590
  /** The environment ID */
17290
17591
  get environmentId() {
@@ -17389,7 +17690,7 @@ var Environment = class _Environment {
17389
17690
  }
17390
17691
  get data() {
17391
17692
  return {
17392
- record: async (record2) => this.recordObject(record2),
17693
+ record: async (record3) => this.recordObject(record3),
17393
17694
  recordMany: async (records, options) => this.recordObjects(records, options),
17394
17695
  import: async (records, options) => this.enqueueRecordImport(records, options),
17395
17696
  listImports: async (status) => this.listRecordImports(status),
@@ -17827,11 +18128,13 @@ var Environment = class _Environment {
17827
18128
  if (this._beforeMutationSend && isGraphQLMutationDocument(query)) {
17828
18129
  this._beforeMutationSend?.();
17829
18130
  }
18131
+ const idempotencyKey = this._graphqlIdempotencyKey?.(query, variables);
17830
18132
  const response = await fetch(this._apiEndpoint, {
17831
18133
  method: "POST",
17832
18134
  headers: {
17833
18135
  "Content-Type": "application/json",
17834
- Authorization: `Bearer ${authToken}`
18136
+ Authorization: `Bearer ${authToken}`,
18137
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
17835
18138
  },
17836
18139
  body: JSON.stringify({
17837
18140
  environmentId: this.environmentId,
@@ -17886,6 +18189,11 @@ var Environment = class _Environment {
17886
18189
  classNames: options.classNames?.length ? options.classNames : []
17887
18190
  }
17888
18191
  );
18192
+ if (response.errors?.length) {
18193
+ throw new Error(
18194
+ `Record search failed: ${response.errors.map((error) => error.message).join("; ")}`
18195
+ );
18196
+ }
17889
18197
  const seen = /* @__PURE__ */ new Set();
17890
18198
  const results = (response.data?.record_search || []).flatMap((entry) => {
17891
18199
  const className = entry.className?.trim();
@@ -19400,6 +19708,7 @@ var Granular = class _Granular {
19400
19708
  onUnexpectedClose;
19401
19709
  onReconnectError;
19402
19710
  effectHostUrl;
19711
+ graphqlIdempotencyKey;
19403
19712
  debugHttp = process.env.GRANULAR_DEBUG_HTTP === "1";
19404
19713
  /** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
19405
19714
  sandboxEffects = /* @__PURE__ */ new Map();
@@ -19429,6 +19738,7 @@ var Granular = class _Granular {
19429
19738
  this.onUnexpectedClose = options.onUnexpectedClose;
19430
19739
  this.onReconnectError = options.onReconnectError;
19431
19740
  this.effectHostUrl = options.effectHostUrl;
19741
+ this.graphqlIdempotencyKey = options.graphqlIdempotencyKey;
19432
19742
  this.httpUrl = this.apiUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://").replace(/\/ws$/, "");
19433
19743
  }
19434
19744
  /**
@@ -20444,7 +20754,9 @@ var Granular = class _Granular {
20444
20754
  envData,
20445
20755
  this.apiKey,
20446
20756
  graphqlEndpoint,
20447
- this.tokenProvider
20757
+ this.tokenProvider,
20758
+ void 0,
20759
+ this.graphqlIdempotencyKey
20448
20760
  );
20449
20761
  }
20450
20762
  async bindWebSocketEnvironmentSession(environment, clientId, session, transportOptions = {}) {
@@ -21044,13 +21356,13 @@ var Granular = class _Granular {
21044
21356
  * Permission Profile management for sandboxes
21045
21357
  */
21046
21358
  get permissionProfiles() {
21047
- const profileSourceFromRecord = (record2) => {
21048
- const profile = record2.profile || record2.rules || {};
21359
+ const profileSourceFromRecord = (record3) => {
21360
+ const profile = record3.profile || record3.rules || {};
21049
21361
  return {
21050
21362
  ...profile,
21051
21363
  schemaVersion: profile.schemaVersion || 1,
21052
- name: profile.name || record2.name,
21053
- description: profile.description || record2.description
21364
+ name: profile.name || record3.name,
21365
+ description: profile.description || record3.description
21054
21366
  };
21055
21367
  };
21056
21368
  return {
@@ -21620,276 +21932,6 @@ async function pollEnvironmentSetup(input) {
21620
21932
  }
21621
21933
  }
21622
21934
 
21623
- // src/record-snapshot-projection.ts
21624
- function stableValue(value) {
21625
- if (Array.isArray(value)) return value.map(stableValue);
21626
- if (value && typeof value === "object") {
21627
- return Object.fromEntries(
21628
- Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stableValue(item)])
21629
- );
21630
- }
21631
- return value;
21632
- }
21633
- function stableJson(value) {
21634
- return JSON.stringify(stableValue(value));
21635
- }
21636
- function recordKey(record2) {
21637
- return `${record2.className}\0${record2.id}`;
21638
- }
21639
- function recordReference(className, id) {
21640
- return { className, id };
21641
- }
21642
- function changedRecordSnapshots(beforeRecords, afterRecords) {
21643
- const before = new Map(
21644
- beforeRecords.map((record2) => [recordKey(record2), record2])
21645
- );
21646
- const after = new Map(
21647
- afterRecords.map((record2) => [recordKey(record2), record2])
21648
- );
21649
- const changedKeys = /* @__PURE__ */ new Set();
21650
- for (const key of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) {
21651
- if (stableJson(before.get(key)) !== stableJson(after.get(key))) {
21652
- changedKeys.add(key);
21653
- }
21654
- }
21655
- return {
21656
- beforeRecords: beforeRecords.filter(
21657
- (record2) => changedKeys.has(recordKey(record2))
21658
- ),
21659
- afterRecords: afterRecords.filter(
21660
- (record2) => changedKeys.has(recordKey(record2))
21661
- )
21662
- };
21663
- }
21664
- function relationshipTargets(manifest) {
21665
- const targets = /* @__PURE__ */ new Map();
21666
- for (const volume of manifest.volumes) {
21667
- for (const operation of volume.operations) {
21668
- const relationship = operation.defineRelationship;
21669
- if (!relationship) continue;
21670
- targets.set(`${relationship.left}\0${relationship.leftSubmodel}`, {
21671
- className: relationship.right
21672
- });
21673
- targets.set(`${relationship.right}\0${relationship.rightSubmodel}`, {
21674
- className: relationship.left
21675
- });
21676
- }
21677
- }
21678
- return targets;
21679
- }
21680
- function stateName(value) {
21681
- return typeof value === "string" ? value : value.state;
21682
- }
21683
- function asIds(value) {
21684
- return value === void 0 ? [] : Array.isArray(value) ? value : [value];
21685
- }
21686
- function changedObjectProjection(before, after) {
21687
- const beforeFields = before?.fields || {};
21688
- const afterFields = after.fields || {};
21689
- const fields = { ...afterFields };
21690
- for (const fieldName of Object.keys(beforeFields)) {
21691
- if (!(fieldName in afterFields)) fields[fieldName] = null;
21692
- }
21693
- if (before && before.label === after.label && stableJson(beforeFields) === stableJson(afterFields)) {
21694
- return null;
21695
- }
21696
- return {
21697
- kind: "object",
21698
- operation: before ? "updated" : "created",
21699
- record: {
21700
- className: after.className,
21701
- id: after.id,
21702
- ...after.label !== void 0 ? { label: after.label } : {},
21703
- fields
21704
- }
21705
- };
21706
- }
21707
- function projectionChanges(input) {
21708
- const before = new Map(
21709
- input.beforeRecords.map((record2) => [recordKey(record2), record2])
21710
- );
21711
- const after = new Map(
21712
- input.afterRecords.map((record2) => [recordKey(record2), record2])
21713
- );
21714
- const targets = relationshipTargets(input.manifest);
21715
- const changes = [];
21716
- for (const record2 of input.afterRecords) {
21717
- const previous = before.get(recordKey(record2));
21718
- const objectChange = changedObjectProjection(previous, record2);
21719
- if (objectChange) changes.push(objectChange);
21720
- const relationshipNames = /* @__PURE__ */ new Set([
21721
- ...Object.keys(previous?.relationships || {}),
21722
- ...Object.keys(record2.relationships || {})
21723
- ]);
21724
- for (const relationshipName of [...relationshipNames].sort()) {
21725
- const target = targets.get(
21726
- `${record2.className}\0${relationshipName}`
21727
- );
21728
- if (!target) {
21729
- throw new Error(
21730
- `No ontology relationship target is declared for ${record2.className}.${relationshipName}`
21731
- );
21732
- }
21733
- const previousIds = new Set(
21734
- asIds(previous?.relationships?.[relationshipName])
21735
- );
21736
- const currentIds = new Set(
21737
- asIds(record2.relationships?.[relationshipName])
21738
- );
21739
- for (const id of [...previousIds].sort()) {
21740
- if (currentIds.has(id)) continue;
21741
- changes.push({
21742
- kind: "relationship",
21743
- operation: "disconnected",
21744
- relationship: relationshipName,
21745
- from: recordReference(record2.className, record2.id),
21746
- to: recordReference(target.className, id)
21747
- });
21748
- }
21749
- for (const id of [...currentIds].sort()) {
21750
- if (previousIds.has(id)) continue;
21751
- changes.push({
21752
- kind: "relationship",
21753
- operation: "connected",
21754
- relationship: relationshipName,
21755
- from: recordReference(record2.className, record2.id),
21756
- to: recordReference(target.className, id)
21757
- });
21758
- }
21759
- }
21760
- const machines = /* @__PURE__ */ new Set([
21761
- ...Object.keys(previous?.states || {}),
21762
- ...Object.keys(record2.states || {})
21763
- ]);
21764
- for (const machine of [...machines].sort()) {
21765
- const next = record2.states?.[machine];
21766
- if (next === void 0) continue;
21767
- const prior = previous?.states?.[machine];
21768
- if (prior !== void 0 && stateName(prior) === stateName(next)) continue;
21769
- if (input.transition && input.transition.className === record2.className && input.transition.objectId === record2.id && input.transition.machine === machine) {
21770
- continue;
21771
- }
21772
- changes.push({
21773
- kind: "state_observation",
21774
- target: recordReference(record2.className, record2.id),
21775
- machine,
21776
- state: stateName(next)
21777
- });
21778
- }
21779
- }
21780
- for (const record2 of input.beforeRecords) {
21781
- if (after.has(recordKey(record2))) continue;
21782
- changes.push({
21783
- kind: "object",
21784
- operation: "deleted",
21785
- target: recordReference(record2.className, record2.id)
21786
- });
21787
- }
21788
- return changes;
21789
- }
21790
- function valueAtPath(value, path) {
21791
- return path.split(".").filter(Boolean).reduce((current, segment) => {
21792
- if (!current || typeof current !== "object" || Array.isArray(current)) {
21793
- return void 0;
21794
- }
21795
- return current[segment];
21796
- }, value);
21797
- }
21798
- function primaryTarget(input) {
21799
- if (input.transition) {
21800
- return recordReference(
21801
- input.transition.className,
21802
- input.transition.objectId
21803
- );
21804
- }
21805
- const creates = input.effect.metamodels?.creates;
21806
- if (creates) {
21807
- const declaration = typeof creates === "string" ? { className: creates } : creates;
21808
- const id = declaration.idPath ? valueAtPath(input.result, declaration.idPath) : void 0;
21809
- if (typeof id === "string" && id) {
21810
- return recordReference(declaration.className, id);
21811
- }
21812
- }
21813
- if (input.effect.className && !input.effect.static) {
21814
- const objectId = input.effectInput && typeof input.effectInput === "object" && !Array.isArray(input.effectInput) ? input.effectInput._objectId : void 0;
21815
- if (typeof objectId === "string" && objectId) {
21816
- return recordReference(input.effect.className, objectId);
21817
- }
21818
- }
21819
- const created = input.changes.filter(
21820
- (change) => change.kind === "object" && change.operation === "created"
21821
- );
21822
- const onlyCreated = created.length === 1 ? created[0] : void 0;
21823
- return onlyCreated ? recordReference(onlyCreated.record.className, onlyCreated.record.id) : void 0;
21824
- }
21825
- function transitionOutcome(transition, afterRecords) {
21826
- if (!transition) {
21827
- throw new Error("A transition commit has no authored transition context.");
21828
- }
21829
- const target = afterRecords.find(
21830
- (record2) => record2.className === transition.className && record2.id === transition.objectId
21831
- );
21832
- const observed = target?.states?.[transition.machine];
21833
- if (!observed) {
21834
- throw new Error(
21835
- `The product result did not expose ${transition.className}:${transition.objectId}.${transition.machine}`
21836
- );
21837
- }
21838
- const finalState = stateName(observed);
21839
- const matches = Object.entries(transition.outcomes).filter(
21840
- ([, outcome]) => outcome.to === finalState || outcome.to === "$current" && finalState === transition.from
21841
- );
21842
- if (matches.length !== 1) {
21843
- throw new Error(
21844
- `Product state ${finalState} maps to ${matches.length} authored outcomes for ${transition.machine}.${transition.transition}`
21845
- );
21846
- }
21847
- const [key, declaration] = matches[0];
21848
- return {
21849
- key,
21850
- ...declaration.disposition === "error" ? {
21851
- error: {
21852
- code: `product_outcome_${key}`,
21853
- message: declaration.label || `The product completed in ${finalState} instead of continuing.`,
21854
- retryable: false
21855
- }
21856
- } : {}
21857
- };
21858
- }
21859
- function projectRecordSnapshotMutation(input) {
21860
- const changes = projectionChanges({
21861
- manifest: input.manifest,
21862
- beforeRecords: input.source.beforeRecords,
21863
- afterRecords: input.source.afterRecords,
21864
- transition: input.transition
21865
- });
21866
- const target = primaryTarget({
21867
- effect: input.effect,
21868
- effectInput: input.effectInput,
21869
- result: input.result,
21870
- changes,
21871
- transition: input.transition
21872
- });
21873
- const base = {
21874
- source: {
21875
- reference: input.source.reference,
21876
- ...input.source.version ? { version: input.source.version } : {},
21877
- ...input.source.versionScope ? { versionScope: input.source.versionScope } : {}
21878
- },
21879
- ...target ? { primaryTarget: target } : {},
21880
- changes,
21881
- safeSummary: {
21882
- effect: input.effect.name,
21883
- affectedChanges: changes.length
21884
- }
21885
- };
21886
- if (input.commitKind === "effect") return base;
21887
- return {
21888
- ...base,
21889
- outcome: transitionOutcome(input.transition, input.source.afterRecords)
21890
- };
21891
- }
21892
-
21893
21935
  // src/agent-harness-templates/action-presentation/0.1.0/manifest.json
21894
21936
  var manifest_default = {
21895
21937
  id: "action-presentation",
@@ -22045,29 +22087,29 @@ function requireRecord(value, context) {
22045
22087
  }
22046
22088
  return value;
22047
22089
  }
22048
- function requiredString(record2, key, context) {
22049
- const value = record2[key];
22090
+ function requiredString(record3, key, context) {
22091
+ const value = record3[key];
22050
22092
  if (typeof value !== "string" || !value.trim()) {
22051
22093
  throw new Error(`${context}.${key} must be a non-empty string.`);
22052
22094
  }
22053
22095
  return value;
22054
22096
  }
22055
- function requiredNumber(record2, key, context) {
22056
- const value = record2[key];
22097
+ function requiredNumber(record3, key, context) {
22098
+ const value = record3[key];
22057
22099
  if (typeof value !== "number" || !Number.isFinite(value)) {
22058
22100
  throw new Error(`${context}.${key} must be a finite number.`);
22059
22101
  }
22060
22102
  return value;
22061
22103
  }
22062
- function requiredStringArray(record2, key, context) {
22063
- const value = record2[key];
22104
+ function requiredStringArray(record3, key, context) {
22105
+ const value = record3[key];
22064
22106
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.trim())) {
22065
22107
  throw new Error(`${context}.${key} must be an array of non-empty strings.`);
22066
22108
  }
22067
22109
  return [...value];
22068
22110
  }
22069
- function requiredNumberRecord(record2, key, context) {
22070
- const value = requireRecord(record2[key], `${context}.${key}`);
22111
+ function requiredNumberRecord(record3, key, context) {
22112
+ const value = requireRecord(record3[key], `${context}.${key}`);
22071
22113
  const output = {};
22072
22114
  for (const [entryKey, entryValue] of Object.entries(value)) {
22073
22115
  if (typeof entryValue !== "number" || !Number.isFinite(entryValue) || entryValue < 0 || entryValue > 1) {
@@ -22080,22 +22122,22 @@ function requiredNumberRecord(record2, key, context) {
22080
22122
  return output;
22081
22123
  }
22082
22124
  function validateHarnessTemplateManifest(value, context = "HarnessTemplateManifest") {
22083
- const record2 = requireRecord(value, context);
22084
- const status = requiredString(record2, "status", context);
22125
+ const record3 = requireRecord(value, context);
22126
+ const status = requiredString(record3, "status", context);
22085
22127
  if (!HARNESS_TEMPLATE_STATUSES.includes(status)) {
22086
22128
  throw new Error(
22087
22129
  `${context}.status must be one of ${HARNESS_TEMPLATE_STATUSES.join(", ")}.`
22088
22130
  );
22089
22131
  }
22090
- const createdAt = requiredString(record2, "createdAt", context);
22132
+ const createdAt = requiredString(record3, "createdAt", context);
22091
22133
  if (Number.isNaN(Date.parse(createdAt))) {
22092
22134
  throw new Error(`${context}.createdAt must be an ISO timestamp.`);
22093
22135
  }
22094
22136
  const compatibility = requireRecord(
22095
- record2.compatibility,
22137
+ record3.compatibility,
22096
22138
  `${context}.compatibility`
22097
22139
  );
22098
- const evalGates = requireRecord(record2.evalGates, `${context}.evalGates`);
22140
+ const evalGates = requireRecord(record3.evalGates, `${context}.evalGates`);
22099
22141
  const maxRegressionPct = requiredNumber(
22100
22142
  evalGates,
22101
22143
  "maxRegressionPct",
@@ -22107,23 +22149,23 @@ function validateHarnessTemplateManifest(value, context = "HarnessTemplateManife
22107
22149
  );
22108
22150
  }
22109
22151
  return {
22110
- id: requiredString(record2, "id", context),
22111
- version: requiredString(record2, "version", context),
22152
+ id: requiredString(record3, "id", context),
22153
+ version: requiredString(record3, "version", context),
22112
22154
  status,
22113
- owner: requiredString(record2, "owner", context),
22155
+ owner: requiredString(record3, "owner", context),
22114
22156
  createdAt,
22115
- changelog: requiredString(record2, "changelog", context),
22116
- promptBuilder: requiredString(record2, "promptBuilder", context),
22117
- continuationBuilder: requiredString(record2, "continuationBuilder", context),
22157
+ changelog: requiredString(record3, "changelog", context),
22158
+ promptBuilder: requiredString(record3, "promptBuilder", context),
22159
+ continuationBuilder: requiredString(record3, "continuationBuilder", context),
22118
22160
  modelOutputInstruction: requiredString(
22119
- record2,
22161
+ record3,
22120
22162
  "modelOutputInstruction",
22121
22163
  context
22122
22164
  ),
22123
- codeReviewPolicy: requiredString(record2, "codeReviewPolicy", context),
22124
- defaultModel: requiredString(record2, "defaultModel", context),
22125
- modelMatrix: requiredStringArray(record2, "modelMatrix", context),
22126
- temperature: requiredNumber(record2, "temperature", context),
22165
+ codeReviewPolicy: requiredString(record3, "codeReviewPolicy", context),
22166
+ defaultModel: requiredString(record3, "defaultModel", context),
22167
+ modelMatrix: requiredStringArray(record3, "modelMatrix", context),
22168
+ temperature: requiredNumber(record3, "temperature", context),
22127
22169
  compatibility: {
22128
22170
  minSdkVersion: requiredString(
22129
22171
  compatibility,
@@ -22268,8 +22310,8 @@ function stableStringify(value) {
22268
22310
  if (Array.isArray(value)) {
22269
22311
  return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
22270
22312
  }
22271
- const record2 = value;
22272
- return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record2[key])}`).join(",")}}`;
22313
+ const record3 = value;
22314
+ return `{${Object.keys(record3).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record3[key])}`).join(",")}}`;
22273
22315
  }
22274
22316
  function renderConstBlock(name, value) {
22275
22317
  return `const ${name} = ${JSON.stringify(value, null, 2)} as const;`;
@@ -22865,9 +22907,9 @@ function getLatestClosure(liveDoc) {
22865
22907
  }
22866
22908
  const closures = [];
22867
22909
  for (const [closureId, value] of Object.entries(closuresById)) {
22868
- const record2 = asRecord4(value);
22869
- if (!record2) continue;
22870
- closures.push({ ...record2, closureId });
22910
+ const record3 = asRecord4(value);
22911
+ if (!record3) continue;
22912
+ closures.push({ ...record3, closureId });
22871
22913
  }
22872
22914
  closures.sort(
22873
22915
  (left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0)
@@ -22905,9 +22947,9 @@ function getJobRecords(liveDoc) {
22905
22947
  const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
22906
22948
  const jobs = [];
22907
22949
  for (const [jobId, value] of Object.entries(jobsById)) {
22908
- const record2 = asRecord4(value);
22909
- if (!record2) continue;
22910
- jobs.push({ ...record2, jobId });
22950
+ const record3 = asRecord4(value);
22951
+ if (!record3) continue;
22952
+ jobs.push({ ...record3, jobId });
22911
22953
  }
22912
22954
  jobs.sort(
22913
22955
  (left, right) => getJobTimestamp(right) - getJobTimestamp(left)
@@ -23093,8 +23135,8 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
23093
23135
  const prompts = asRecord4(asRecord4(job)?.prompts);
23094
23136
  if (!prompts) continue;
23095
23137
  for (const prompt of Object.values(prompts)) {
23096
- const record2 = asRecord4(prompt);
23097
- if (record2?.status === "open") return true;
23138
+ const record3 = asRecord4(prompt);
23139
+ if (record3?.status === "open") return true;
23098
23140
  }
23099
23141
  }
23100
23142
  return false;
@@ -23153,13 +23195,13 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
23153
23195
  title: typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision",
23154
23196
  status,
23155
23197
  candidates: status === "open" ? asArray(decision.candidates).slice(0, 5).map((candidate) => {
23156
- const record2 = asRecord4(candidate);
23157
- if (!record2) return null;
23198
+ const record3 = asRecord4(candidate);
23199
+ if (!record3) return null;
23158
23200
  return {
23159
- id: typeof record2.id === "string" ? record2.id : "unknown",
23160
- label: typeof record2.label === "string" && record2.label.trim() ? record2.label.trim() : null,
23161
- description: typeof record2.description === "string" && record2.description.trim() ? record2.description.trim() : null,
23162
- metadata: asRecord4(record2.metadata)
23201
+ id: typeof record3.id === "string" ? record3.id : "unknown",
23202
+ label: typeof record3.label === "string" && record3.label.trim() ? record3.label.trim() : null,
23203
+ description: typeof record3.description === "string" && record3.description.trim() ? record3.description.trim() : null,
23204
+ metadata: asRecord4(record3.metadata)
23163
23205
  };
23164
23206
  }).filter(Boolean) : [],
23165
23207
  selected: status === "open" ? null : {
@@ -23794,11 +23836,11 @@ function normalizeJsonSchema(value) {
23794
23836
  return asRecord4(value);
23795
23837
  }
23796
23838
  function jsonSchemaTypeName(schema) {
23797
- const record2 = normalizeJsonSchema(schema);
23798
- if (!record2) return "unknown";
23799
- const type = record2.type;
23800
- if (record2.format === "date-time") return "date-time";
23801
- if (record2.format === "date" || type === "date" || Array.isArray(type) && type.includes("date")) {
23839
+ const record3 = normalizeJsonSchema(schema);
23840
+ if (!record3) return "unknown";
23841
+ const type = record3.type;
23842
+ if (record3.format === "date-time") return "date-time";
23843
+ if (record3.format === "date" || type === "date" || Array.isArray(type) && type.includes("date")) {
23802
23844
  return "date";
23803
23845
  }
23804
23846
  if (typeof type === "string") {
@@ -23809,12 +23851,12 @@ function jsonSchemaTypeName(schema) {
23809
23851
  return "unknown";
23810
23852
  }
23811
23853
  function summarizeObjectSchema(schema) {
23812
- const record2 = normalizeJsonSchema(schema);
23813
- const properties = asRecord4(record2?.properties);
23854
+ const record3 = normalizeJsonSchema(schema);
23855
+ const properties = asRecord4(record3?.properties);
23814
23856
  if (!properties || Object.keys(properties).length === 0) {
23815
- return record2 ? "{}" : null;
23857
+ return record3 ? "{}" : null;
23816
23858
  }
23817
- const required = new Set(asArray(record2?.required));
23859
+ const required = new Set(asArray(record3?.required));
23818
23860
  const fields = Object.entries(properties).slice(0, 8).map(([name, property]) => {
23819
23861
  const marker = required.has(name) ? "*" : "?";
23820
23862
  return `${name}${marker}: ${jsonSchemaTypeName(property)}`;
@@ -24578,39 +24620,39 @@ function pushStringArray(target, value) {
24578
24620
  pushString(target, item);
24579
24621
  }
24580
24622
  }
24581
- function collectReferencesFromRecord(record2, refs) {
24623
+ function collectReferencesFromRecord(record3, refs) {
24582
24624
  for (const key of ENTRY_KEY_CANDIDATES)
24583
- pushString(refs.entryPaths, record2[key]);
24625
+ pushString(refs.entryPaths, record3[key]);
24584
24626
  for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
24585
- pushStringArray(refs.entryPaths, record2[key]);
24627
+ pushStringArray(refs.entryPaths, record3[key]);
24586
24628
  for (const key of LIST_KEY_CANDIDATES)
24587
- pushString(refs.listNames, record2[key]);
24629
+ pushString(refs.listNames, record3[key]);
24588
24630
  for (const key of LIST_ARRAY_KEY_CANDIDATES)
24589
- pushStringArray(refs.listNames, record2[key]);
24631
+ pushStringArray(refs.listNames, record3[key]);
24590
24632
  for (const key of VARIABLE_KEY_CANDIDATES)
24591
- pushString(refs.variableNames, record2[key]);
24633
+ pushString(refs.variableNames, record3[key]);
24592
24634
  for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
24593
- pushStringArray(refs.variableNames, record2[key]);
24635
+ pushStringArray(refs.variableNames, record3[key]);
24594
24636
  }
24595
- function stringValue(record2, keys) {
24637
+ function stringValue(record3, keys) {
24596
24638
  for (const key of keys) {
24597
- const value = record2[key];
24639
+ const value = record3[key];
24598
24640
  if (typeof value === "string" && value.trim()) {
24599
24641
  return value.trim();
24600
24642
  }
24601
24643
  }
24602
24644
  return null;
24603
24645
  }
24604
- function findEntryPathForRecord(record2, heap) {
24605
- const directPath = stringValue(record2, ["entryPath", "path"]);
24646
+ function findEntryPathForRecord(record3, heap) {
24647
+ const directPath = stringValue(record3, ["entryPath", "path"]);
24606
24648
  if (directPath && heap.entriesByPath?.[directPath]) {
24607
24649
  return directPath;
24608
24650
  }
24609
- const id = stringValue(record2, ["id", "_id", "recordId", "objectId"]);
24651
+ const id = stringValue(record3, ["id", "_id", "recordId", "objectId"]);
24610
24652
  if (!id) {
24611
24653
  return null;
24612
24654
  }
24613
- const className = stringValue(record2, [
24655
+ const className = stringValue(record3, [
24614
24656
  "className",
24615
24657
  "_className",
24616
24658
  "__className",
@@ -24644,17 +24686,17 @@ function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__
24644
24686
  }
24645
24687
  return;
24646
24688
  }
24647
- const record2 = asRecord5(value);
24648
- if (!record2) return;
24689
+ const record3 = asRecord5(value);
24690
+ if (!record3) return;
24649
24691
  seen.add(value);
24650
- const entryPath = findEntryPathForRecord(record2, heap);
24692
+ const entryPath = findEntryPathForRecord(record3, heap);
24651
24693
  if (entryPath) refs.entryPaths.add(entryPath);
24652
- collectReferencesFromRecord(record2, refs);
24694
+ collectReferencesFromRecord(record3, refs);
24653
24695
  for (const key of UI_CONTAINER_KEYS) {
24654
- const nested = asRecord5(record2[key]);
24696
+ const nested = asRecord5(record3[key]);
24655
24697
  if (nested) collectReferencesFromRecord(nested, refs);
24656
24698
  }
24657
- for (const nested of Object.values(record2).slice(0, 24)) {
24699
+ for (const nested of Object.values(record3).slice(0, 24)) {
24658
24700
  scanForHeapReferences(nested, heap, refs, depth + 1, seen);
24659
24701
  }
24660
24702
  }
@@ -24803,8 +24845,8 @@ var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
24803
24845
  function asRecord6(value) {
24804
24846
  return value && typeof value === "object" ? value : null;
24805
24847
  }
24806
- function numberField(record2, key) {
24807
- const value = record2?.[key];
24848
+ function numberField(record3, key) {
24849
+ const value = record3?.[key];
24808
24850
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
24809
24851
  }
24810
24852
  function microsPerMillion(usdPerMillion) {
@@ -24908,6 +24950,39 @@ function calculateOpenAITokenSpend(model, rawUsage) {
24908
24950
  };
24909
24951
  }
24910
24952
 
24953
+ // src/artifact-action-review.ts
24954
+ function artifactReviewedOperation(artifact, inputValues = artifact.inputValues || {}, relationships = artifact.relationships || {}) {
24955
+ const metadata = artifact.metadata || {};
24956
+ const statePlan = { ...record2(metadata.statePlan) };
24957
+ delete statePlan.approvedTransitions;
24958
+ delete statePlan.approvalDecisions;
24959
+ return {
24960
+ artifactId: artifact.artifactId,
24961
+ sessionId: artifact.sessionId || null,
24962
+ environmentId: artifact.environmentId || null,
24963
+ subjectId: artifact.subjectId || null,
24964
+ buildId: artifact.buildId || null,
24965
+ buildVersionNumber: artifact.buildVersionNumber ?? null,
24966
+ kind: artifact.kind,
24967
+ target: artifact.target || null,
24968
+ policy: artifact.policy ?? null,
24969
+ inputSchema: artifact.inputSchema || null,
24970
+ inputValues,
24971
+ relationships,
24972
+ effectKey: metadata.effectKey || metadata.toolName || null,
24973
+ effect: metadata.effect || null,
24974
+ statePlan,
24975
+ draftRecord: metadata.draftRecord || null,
24976
+ relationshipCreate: metadata.relationshipCreate || null,
24977
+ autonomy: metadata.autonomy || null,
24978
+ batch: metadata.batch || null,
24979
+ subArtifactIds: artifact.subArtifactIds || []
24980
+ };
24981
+ }
24982
+ function record2(value) {
24983
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
24984
+ }
24985
+
24911
24986
  // src/artifact-surface.ts
24912
24987
  function operationNode(workflow) {
24913
24988
  return getArtifactWorkflowNodes(workflow).find(
@@ -25127,6 +25202,7 @@ exports.OntologyHandle = OntologyHandle;
25127
25202
  exports.Session = Session;
25128
25203
  exports.SessionFeedController = SessionFeedController;
25129
25204
  exports.WSClient = WSClient;
25205
+ exports.artifactReviewedOperation = artifactReviewedOperation;
25130
25206
  exports.assertArtifactWorkflow = assertArtifactWorkflow;
25131
25207
  exports.buildContinuationInstruction = buildContinuationInstruction;
25132
25208
  exports.buildContinuationInstructionFromTemplate = buildContinuationInstructionFromTemplate;