@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.mjs CHANGED
@@ -4278,8 +4278,8 @@ function normalizeFeedItems(values) {
4278
4278
  return { items: Object.freeze(items), error };
4279
4279
  }
4280
4280
  function normalizeTransients(values) {
4281
- const record2 = asRecord(values);
4282
- if (!record2) {
4281
+ const record3 = asRecord(values);
4282
+ if (!record3) {
4283
4283
  return {
4284
4284
  items: [],
4285
4285
  error: new Error("Canonical feed transientById is not an object.")
@@ -4288,7 +4288,7 @@ function normalizeTransients(values) {
4288
4288
  let error = null;
4289
4289
  const byId = /* @__PURE__ */ new Map();
4290
4290
  const idByOrdinal = /* @__PURE__ */ new Map();
4291
- for (const [key, value] of Object.entries(record2)) {
4291
+ for (const [key, value] of Object.entries(record3)) {
4292
4292
  const item = normalizeTransientFeedItem(value);
4293
4293
  if (!item || item.id !== key) {
4294
4294
  error ||= new Error("Canonical feed contains an invalid transient item.");
@@ -5161,11 +5161,11 @@ function operationId(prefix) {
5161
5161
  return randomUuid ? `${prefix}_${randomUuid}` : `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
5162
5162
  }
5163
5163
  function unwrapPublishedItem(value) {
5164
- const record2 = asRecord(value);
5165
- const directItems = Array.isArray(record2?.items) ? record2.items : [];
5166
- const durable = asRecord(record2?.durable);
5164
+ const record3 = asRecord(value);
5165
+ const directItems = Array.isArray(record3?.items) ? record3.items : [];
5166
+ const durable = asRecord(record3?.durable);
5167
5167
  const durableItems = Array.isArray(durable?.items) ? durable.items : [];
5168
- return record2?.item || directItems[0] || durableItems[0] || value;
5168
+ return record3?.item || directItems[0] || durableItems[0] || value;
5169
5169
  }
5170
5170
  function requireFeedbackItem(value) {
5171
5171
  const item = normalizeFeedItem(unwrapPublishedItem(value));
@@ -5287,9 +5287,9 @@ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 5;
5287
5287
  var DOCUMENT_RESYNC_CLOSE_CODE = 4e3;
5288
5288
  var DOCUMENT_RESYNC_CLOSE_REASON = "Document resync required";
5289
5289
  function documentVersion(document) {
5290
- const record2 = document;
5291
- const epochValue = record2.documentEpoch;
5292
- const revisionValue = record2.documentRevision;
5290
+ const record3 = document;
5291
+ const epochValue = record3.documentEpoch;
5292
+ const revisionValue = record3.documentRevision;
5293
5293
  const epoch = epochValue === void 0 ? 0 : epochValue;
5294
5294
  const revision = revisionValue === void 0 ? 0 : revisionValue;
5295
5295
  if (!Number.isSafeInteger(epoch) || Number(epoch) < 0 || !Number.isSafeInteger(revision) || Number(revision) < 0) {
@@ -5311,9 +5311,9 @@ function stableDocumentFingerprint(value) {
5311
5311
  if (Array.isArray(value)) {
5312
5312
  return `[${value.map(stableDocumentFingerprint).join(",")}]`;
5313
5313
  }
5314
- const record2 = value;
5315
- return `{${Object.keys(record2).sort().map(
5316
- (key) => `${JSON.stringify(key)}:${stableDocumentFingerprint(record2[key])}`
5314
+ const record3 = value;
5315
+ return `{${Object.keys(record3).sort().map(
5316
+ (key) => `${JSON.stringify(key)}:${stableDocumentFingerprint(record3[key])}`
5317
5317
  ).join(",")}}`;
5318
5318
  }
5319
5319
  function canonicalDocumentFingerprint(value) {
@@ -5323,9 +5323,9 @@ function canonicalDocumentFingerprint(value) {
5323
5323
  if (Array.isArray(input)) {
5324
5324
  return input.map((entry) => stripPresentation(entry, path));
5325
5325
  }
5326
- const record2 = input;
5326
+ const record3 = input;
5327
5327
  const output = {};
5328
- for (const [key, entry] of Object.entries(record2)) {
5328
+ for (const [key, entry] of Object.entries(record3)) {
5329
5329
  if (path.length === 1 && path[0] === "feed" && [
5330
5330
  "transientById",
5331
5331
  "feedbackUsageByJob",
@@ -6429,30 +6429,30 @@ function parseJsonPromptChoiceOption(option) {
6429
6429
  }
6430
6430
  function normalizePromptChoiceOption(option) {
6431
6431
  if (typeof option === "string") {
6432
- const record3 = parseJsonPromptChoiceOption(option);
6433
- if (!record3) {
6432
+ const record4 = parseJsonPromptChoiceOption(option);
6433
+ if (!record4) {
6434
6434
  return { value: option, label: option };
6435
6435
  }
6436
- const value2 = typeof record3.value === "string" ? record3.value : typeof record3.id === "string" ? record3.id : typeof record3.label === "string" ? record3.label : JSON.stringify(record3);
6436
+ const value2 = typeof record4.value === "string" ? record4.value : typeof record4.id === "string" ? record4.id : typeof record4.label === "string" ? record4.label : JSON.stringify(record4);
6437
6437
  return {
6438
6438
  value: value2,
6439
- label: typeof record3.label === "string" ? record3.label : value2,
6440
- description: typeof record3.description === "string" ? record3.description : void 0
6439
+ label: typeof record4.label === "string" ? record4.label : value2,
6440
+ description: typeof record4.description === "string" ? record4.description : void 0
6441
6441
  };
6442
6442
  }
6443
- const record2 = option;
6444
- if (!record2) {
6443
+ const record3 = option;
6444
+ if (!record3) {
6445
6445
  return { value: "", label: "" };
6446
6446
  }
6447
- const nestedJson = (typeof record2.value === "string" ? parseJsonPromptChoiceOption(record2.value) : null) || (typeof record2.label === "string" ? parseJsonPromptChoiceOption(record2.label) : null);
6447
+ const nestedJson = (typeof record3.value === "string" ? parseJsonPromptChoiceOption(record3.value) : null) || (typeof record3.label === "string" ? parseJsonPromptChoiceOption(record3.label) : null);
6448
6448
  if (nestedJson) {
6449
6449
  return normalizePromptChoiceOption(nestedJson);
6450
6450
  }
6451
- const value = typeof record2.value === "string" ? record2.value : typeof record2.label === "string" ? record2.label : JSON.stringify(record2);
6451
+ const value = typeof record3.value === "string" ? record3.value : typeof record3.label === "string" ? record3.label : JSON.stringify(record3);
6452
6452
  return {
6453
6453
  value,
6454
- label: typeof record2.label === "string" ? record2.label : value,
6455
- description: typeof record2.description === "string" ? record2.description : void 0
6454
+ label: typeof record3.label === "string" ? record3.label : value,
6455
+ description: typeof record3.description === "string" ? record3.description : void 0
6456
6456
  };
6457
6457
  }
6458
6458
  function scorePromptChoiceMatch(answer, answerTokens, option) {
@@ -7960,8 +7960,8 @@ function artifactRecordsById(liveDoc) {
7960
7960
  const byId = asRecord3(artifacts?.byId) || {};
7961
7961
  return Object.fromEntries(
7962
7962
  Object.entries(byId).map(([artifactId, value]) => {
7963
- const record2 = asRecord3(value);
7964
- return record2 ? [artifactId, record2] : null;
7963
+ const record3 = asRecord3(value);
7964
+ return record3 ? [artifactId, record3] : null;
7965
7965
  }).filter((entry) => Boolean(entry))
7966
7966
  );
7967
7967
  }
@@ -7994,8 +7994,8 @@ function latestInputEditSummary(metadata) {
7994
7994
  ].filter(Boolean);
7995
7995
  return `lastEdit=${source}${actor ? ` by ${actor}` : ""}${changed.length ? ` (${changed.join("; ")})` : ""}`;
7996
7996
  }
7997
- function artifactIssueSummary(record2) {
7998
- const validation = asRecord3(record2.validation);
7997
+ function artifactIssueSummary(record3) {
7998
+ const validation = asRecord3(record3.validation);
7999
7999
  if (!validation) return null;
8000
8000
  const issues = Array.isArray(validation.issues) ? validation.issues.map((issue) => asRecord3(issue)).filter((issue) => Boolean(issue)).slice(0, 3) : [];
8001
8001
  if (issues.length > 0) {
@@ -8042,18 +8042,18 @@ function artifactStatePathSummary(metadata) {
8042
8042
  ].filter(Boolean);
8043
8043
  return pieces.length ? pieces.join("; ") : null;
8044
8044
  }
8045
- function artifactSummaryLine(artifactId, record2) {
8046
- if (!record2) return `- ${artifactId}: unavailable in session artifact store`;
8047
- const label = trimString(record2.label) || artifactId;
8048
- const kind = asString(record2.kind) || "artifact";
8049
- const status = asString(record2.status) || "unknown";
8050
- const createdByJobId = asString(record2.createdByJobId);
8051
- const target = asRecord3(record2.target);
8052
- const metadata = asRecord3(record2.metadata) || {};
8053
- const subArtifactIds = Array.isArray(record2.subArtifactIds) ? record2.subArtifactIds.filter(
8045
+ function artifactSummaryLine(artifactId, record3) {
8046
+ if (!record3) return `- ${artifactId}: unavailable in session artifact store`;
8047
+ const label = trimString(record3.label) || artifactId;
8048
+ const kind = asString(record3.kind) || "artifact";
8049
+ const status = asString(record3.status) || "unknown";
8050
+ const createdByJobId = asString(record3.createdByJobId);
8051
+ const target = asRecord3(record3.target);
8052
+ const metadata = asRecord3(record3.metadata) || {};
8053
+ const subArtifactIds = Array.isArray(record3.subArtifactIds) ? record3.subArtifactIds.filter(
8054
8054
  (id) => typeof id === "string" && id.trim().length > 0
8055
8055
  ).slice(0, 8) : [];
8056
- const relationships = compactJson(record2.relationships, 220);
8056
+ const relationships = compactJson(record3.relationships, 220);
8057
8057
  const pieces = [
8058
8058
  `kind=${kind}`,
8059
8059
  `status=${status}`,
@@ -8061,7 +8061,7 @@ function artifactSummaryLine(artifactId, record2) {
8061
8061
  target ? `target=${asString(target.className) || "record"}:${asString(target.id) || "unknown"}${asString(target.label) ? ` (${asString(target.label)})` : ""}` : null,
8062
8062
  artifactStatePathSummary(metadata),
8063
8063
  artifactExecutionSummary(metadata),
8064
- artifactIssueSummary(record2),
8064
+ artifactIssueSummary(record3),
8065
8065
  latestInputEditSummary(metadata),
8066
8066
  subArtifactIds.length ? `subArtifacts=${subArtifactIds.join(",")}` : null,
8067
8067
  relationships ? `relationships=${relationships}` : null
@@ -13172,6 +13172,300 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
13172
13172
  }
13173
13173
  });
13174
13174
 
13175
+ // src/record-snapshot-projection.ts
13176
+ function stableValue(value) {
13177
+ if (Array.isArray(value)) return value.map(stableValue);
13178
+ if (value && typeof value === "object") {
13179
+ return Object.fromEntries(
13180
+ Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stableValue(item)])
13181
+ );
13182
+ }
13183
+ return value;
13184
+ }
13185
+ function stableJson(value) {
13186
+ return JSON.stringify(stableValue(value));
13187
+ }
13188
+ function recordKey(record3) {
13189
+ return `${record3.className}\0${record3.id}`;
13190
+ }
13191
+ function recordReference(className, id) {
13192
+ return { className, id };
13193
+ }
13194
+ function changedRecordSnapshots(beforeRecords, afterRecords) {
13195
+ const before = new Map(
13196
+ beforeRecords.map((record3) => [recordKey(record3), record3])
13197
+ );
13198
+ const after = new Map(
13199
+ afterRecords.map((record3) => [recordKey(record3), record3])
13200
+ );
13201
+ const changedKeys = /* @__PURE__ */ new Set();
13202
+ for (const key of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) {
13203
+ if (stableJson(before.get(key)) !== stableJson(after.get(key))) {
13204
+ changedKeys.add(key);
13205
+ }
13206
+ }
13207
+ return {
13208
+ beforeRecords: beforeRecords.filter(
13209
+ (record3) => changedKeys.has(recordKey(record3))
13210
+ ),
13211
+ afterRecords: afterRecords.filter(
13212
+ (record3) => changedKeys.has(recordKey(record3))
13213
+ )
13214
+ };
13215
+ }
13216
+ function relationshipTargets(manifest) {
13217
+ const targets = /* @__PURE__ */ new Map();
13218
+ for (const volume of manifest.volumes) {
13219
+ for (const operation of volume.operations) {
13220
+ const relationship = operation.defineRelationship;
13221
+ if (!relationship) continue;
13222
+ targets.set(`${relationship.left}\0${relationship.leftSubmodel}`, {
13223
+ className: relationship.right
13224
+ });
13225
+ targets.set(`${relationship.right}\0${relationship.rightSubmodel}`, {
13226
+ className: relationship.left
13227
+ });
13228
+ }
13229
+ }
13230
+ return targets;
13231
+ }
13232
+ function stateName(value) {
13233
+ return typeof value === "string" ? value : value.state;
13234
+ }
13235
+ function asIds(value) {
13236
+ return value === void 0 ? [] : Array.isArray(value) ? value : [value];
13237
+ }
13238
+ function changedObjectProjection(before, after) {
13239
+ const beforeFields = before?.fields || {};
13240
+ const afterFields = after.fields || {};
13241
+ const fields = { ...afterFields };
13242
+ for (const fieldName of Object.keys(beforeFields)) {
13243
+ if (!(fieldName in afterFields)) fields[fieldName] = null;
13244
+ }
13245
+ if (before && before.label === after.label && stableJson(beforeFields) === stableJson(afterFields)) {
13246
+ return null;
13247
+ }
13248
+ return {
13249
+ kind: "object",
13250
+ operation: before ? "updated" : "created",
13251
+ record: {
13252
+ className: after.className,
13253
+ id: after.id,
13254
+ ...after.label !== void 0 ? { label: after.label } : {},
13255
+ fields
13256
+ }
13257
+ };
13258
+ }
13259
+ function projectionChanges(input) {
13260
+ const before = new Map(
13261
+ input.beforeRecords.map((record3) => [recordKey(record3), record3])
13262
+ );
13263
+ const after = new Map(
13264
+ input.afterRecords.map((record3) => [recordKey(record3), record3])
13265
+ );
13266
+ const targets = relationshipTargets(input.manifest);
13267
+ const changes = [];
13268
+ for (const record3 of input.afterRecords) {
13269
+ const previous = before.get(recordKey(record3));
13270
+ const objectChange = changedObjectProjection(previous, record3);
13271
+ if (objectChange) changes.push(objectChange);
13272
+ const relationshipNames = /* @__PURE__ */ new Set([
13273
+ ...Object.keys(previous?.relationships || {}),
13274
+ ...Object.keys(record3.relationships || {})
13275
+ ]);
13276
+ for (const relationshipName of [...relationshipNames].sort()) {
13277
+ const target = targets.get(
13278
+ `${record3.className}\0${relationshipName}`
13279
+ );
13280
+ if (!target) {
13281
+ throw new Error(
13282
+ `No ontology relationship target is declared for ${record3.className}.${relationshipName}`
13283
+ );
13284
+ }
13285
+ const previousIds = new Set(
13286
+ asIds(previous?.relationships?.[relationshipName])
13287
+ );
13288
+ const currentIds = new Set(
13289
+ asIds(record3.relationships?.[relationshipName])
13290
+ );
13291
+ for (const id of [...previousIds].sort()) {
13292
+ if (currentIds.has(id)) continue;
13293
+ changes.push({
13294
+ kind: "relationship",
13295
+ operation: "disconnected",
13296
+ relationship: relationshipName,
13297
+ from: recordReference(record3.className, record3.id),
13298
+ to: recordReference(target.className, id)
13299
+ });
13300
+ }
13301
+ for (const id of [...currentIds].sort()) {
13302
+ if (previousIds.has(id)) continue;
13303
+ changes.push({
13304
+ kind: "relationship",
13305
+ operation: "connected",
13306
+ relationship: relationshipName,
13307
+ from: recordReference(record3.className, record3.id),
13308
+ to: recordReference(target.className, id)
13309
+ });
13310
+ }
13311
+ }
13312
+ const machines = /* @__PURE__ */ new Set([
13313
+ ...Object.keys(previous?.states || {}),
13314
+ ...Object.keys(record3.states || {})
13315
+ ]);
13316
+ for (const machine of [...machines].sort()) {
13317
+ const next = record3.states?.[machine];
13318
+ if (next === void 0) continue;
13319
+ const prior = previous?.states?.[machine];
13320
+ if (prior !== void 0 && stateName(prior) === stateName(next)) continue;
13321
+ if (input.transition && input.transition.className === record3.className && input.transition.objectId === record3.id && input.transition.machine === machine) {
13322
+ continue;
13323
+ }
13324
+ changes.push({
13325
+ kind: "state_observation",
13326
+ target: recordReference(record3.className, record3.id),
13327
+ machine,
13328
+ state: stateName(next)
13329
+ });
13330
+ }
13331
+ }
13332
+ for (const record3 of input.beforeRecords) {
13333
+ if (after.has(recordKey(record3))) continue;
13334
+ changes.push({
13335
+ kind: "object",
13336
+ operation: "deleted",
13337
+ target: recordReference(record3.className, record3.id)
13338
+ });
13339
+ }
13340
+ return changes;
13341
+ }
13342
+ function valueAtPath(value, path) {
13343
+ return path.split(".").filter(Boolean).reduce((current, segment) => {
13344
+ if (!current || typeof current !== "object" || Array.isArray(current)) {
13345
+ return void 0;
13346
+ }
13347
+ return current[segment];
13348
+ }, value);
13349
+ }
13350
+ function primaryTarget(input) {
13351
+ if (input.transition) {
13352
+ return recordReference(
13353
+ input.transition.className,
13354
+ input.transition.objectId
13355
+ );
13356
+ }
13357
+ const creates = input.effect.metamodels?.creates;
13358
+ if (creates) {
13359
+ const declaration = typeof creates === "string" ? { className: creates } : creates;
13360
+ const id = declaration.idPath ? valueAtPath(input.result, declaration.idPath) : void 0;
13361
+ if (typeof id === "string" && id) {
13362
+ return recordReference(declaration.className, id);
13363
+ }
13364
+ }
13365
+ if (input.effect.className && !input.effect.static) {
13366
+ const objectId = input.effectInput && typeof input.effectInput === "object" && !Array.isArray(input.effectInput) ? input.effectInput._objectId : void 0;
13367
+ if (typeof objectId === "string" && objectId) {
13368
+ return recordReference(input.effect.className, objectId);
13369
+ }
13370
+ }
13371
+ const created = input.changes.filter(
13372
+ (change) => change.kind === "object" && change.operation === "created"
13373
+ );
13374
+ const onlyCreated = created.length === 1 ? created[0] : void 0;
13375
+ return onlyCreated ? recordReference(onlyCreated.record.className, onlyCreated.record.id) : void 0;
13376
+ }
13377
+ function assertRecordSnapshotTransitionMapping(transition) {
13378
+ if (!transition)
13379
+ throw new Error("A transition commit has no authored transition context.");
13380
+ if (!transition.outcomes || !Object.keys(transition.outcomes).length) {
13381
+ throw new Error(
13382
+ "Automatic product-state mapping requires at least one declared outcome."
13383
+ );
13384
+ }
13385
+ const destinations = /* @__PURE__ */ new Map();
13386
+ for (const [key, outcome] of Object.entries(transition.outcomes)) {
13387
+ if (!outcome || typeof outcome.to !== "string" || !outcome.to) {
13388
+ throw new Error(
13389
+ `Automatic product-state mapping requires a destination for outcome ${key}.`
13390
+ );
13391
+ }
13392
+ const destination = outcome.to === "$current" ? transition.from : outcome.to;
13393
+ const previous = destinations.get(destination);
13394
+ if (previous !== void 0) {
13395
+ throw new Error(
13396
+ `Ambiguous product-state mapping for ${transition.machine}.${transition.transition}: outcomes ${previous} and ${key} both resolve to ${destination}; automatic mapping requires distinct outcome destinations.`
13397
+ );
13398
+ }
13399
+ destinations.set(destination, key);
13400
+ }
13401
+ }
13402
+ function transitionOutcome(transition, afterRecords) {
13403
+ assertRecordSnapshotTransitionMapping(transition);
13404
+ const target = afterRecords.find(
13405
+ (record3) => record3.className === transition.className && record3.id === transition.objectId
13406
+ );
13407
+ const observed = target?.states?.[transition.machine];
13408
+ if (!observed) {
13409
+ throw new Error(
13410
+ `The product result did not expose ${transition.className}:${transition.objectId}.${transition.machine}`
13411
+ );
13412
+ }
13413
+ const finalState = stateName(observed);
13414
+ const matches = Object.entries(transition.outcomes).filter(
13415
+ ([, outcome]) => outcome.to === finalState || outcome.to === "$current" && finalState === transition.from
13416
+ );
13417
+ if (matches.length !== 1) {
13418
+ throw new Error(
13419
+ `Product state ${finalState} maps to ${matches.length} authored outcomes for ${transition.machine}.${transition.transition}`
13420
+ );
13421
+ }
13422
+ const [key, declaration] = matches[0];
13423
+ return {
13424
+ key,
13425
+ ...declaration.disposition === "error" ? {
13426
+ error: {
13427
+ code: `product_outcome_${key}`,
13428
+ message: declaration.label || `The product completed in ${finalState} instead of continuing.`,
13429
+ retryable: false
13430
+ }
13431
+ } : {}
13432
+ };
13433
+ }
13434
+ function projectRecordSnapshotMutation(input) {
13435
+ const changes = projectionChanges({
13436
+ manifest: input.manifest,
13437
+ beforeRecords: input.source.beforeRecords,
13438
+ afterRecords: input.source.afterRecords,
13439
+ transition: input.transition
13440
+ });
13441
+ const target = primaryTarget({
13442
+ effect: input.effect,
13443
+ effectInput: input.effectInput,
13444
+ result: input.result,
13445
+ changes,
13446
+ transition: input.transition
13447
+ });
13448
+ const base = {
13449
+ source: {
13450
+ reference: input.source.reference,
13451
+ ...input.source.version ? { version: input.source.version } : {},
13452
+ ...input.source.versionScope ? { versionScope: input.source.versionScope } : {}
13453
+ },
13454
+ ...target ? { primaryTarget: target } : {},
13455
+ changes,
13456
+ safeSummary: {
13457
+ effect: input.effect.name,
13458
+ affectedChanges: changes.length
13459
+ }
13460
+ };
13461
+ if (input.commitKind === "effect") return base;
13462
+ return {
13463
+ ...base,
13464
+ outcome: transitionOutcome(input.transition, input.source.afterRecords)
13465
+ };
13466
+ }
13467
+ projectRecordSnapshotMutation.validateTransition = assertRecordSnapshotTransitionMapping;
13468
+
13175
13469
  // src/commit.ts
13176
13470
  var MAX_PROJECTION_CHANGES = 1e3;
13177
13471
  var MAX_SAFE_SUMMARY_KEYS = 32;
@@ -13265,18 +13559,18 @@ function validateScalarRecord(value, path) {
13265
13559
  }
13266
13560
  function validateProjectedRecord(value, path) {
13267
13561
  validateObjectReference(value, path);
13268
- const record2 = value;
13269
- if (record2.label !== void 0) {
13270
- if (typeof record2.label !== "string") {
13562
+ const record3 = value;
13563
+ if (record3.label !== void 0) {
13564
+ if (typeof record3.label !== "string") {
13271
13565
  throw new Error(`${path}.label must be a string`);
13272
13566
  }
13273
- if (record2.label.length > MAX_SAFE_TEXT_LENGTH) {
13567
+ if (record3.label.length > MAX_SAFE_TEXT_LENGTH) {
13274
13568
  throw new Error(
13275
13569
  `${path}.label exceeds ${MAX_SAFE_TEXT_LENGTH} characters`
13276
13570
  );
13277
13571
  }
13278
13572
  }
13279
- validateScalarRecord(record2.fields, `${path}.fields`);
13573
+ validateScalarRecord(record3.fields, `${path}.fields`);
13280
13574
  }
13281
13575
  function validateBoundedJson(value, path, depth = 0) {
13282
13576
  if (depth > 12) throw new Error(`${path} is nested too deeply`);
@@ -13780,6 +14074,11 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
13780
14074
  `Transition effect ${request.effectKey} has no resolved transition context; customer code was not invoked`
13781
14075
  );
13782
14076
  }
14077
+ if (commitRequired && declaration?.kind === "transition" && declaration.outcomeSelection === "product_state") {
14078
+ assertRecordSnapshotTransitionMapping(
14079
+ request.context?.invocation?.transition
14080
+ );
14081
+ }
13783
14082
  const idempotencyKey = commitRequired ? await resolveInvocationIdempotencyKey(request) : request.context?.idempotencyKey;
13784
14083
  let commitStarted = false;
13785
14084
  let commitPromise = null;
@@ -16343,7 +16642,7 @@ function stateTransitionSteps(artifact) {
16343
16642
  {
16344
16643
  index: typeof step.index === "number" ? step.index : fallbackIndex,
16345
16644
  transition,
16346
- label: text(step.label) || humanize(transition),
16645
+ label: text(transitionDescription?.label) || text(step.label) || humanize(transition),
16347
16646
  description: text(step.description) || text(record(step.expectedOutcome)?.summary) || null,
16348
16647
  status: text(step.status) || "pending",
16349
16648
  // State plans may contain bookkeeping transitions that are necessary
@@ -17025,17 +17324,17 @@ function planRecordObjectsChunks(records, batchSize) {
17025
17324
  }
17026
17325
  return plans;
17027
17326
  }
17028
- function preserveRecordObjectRealId(record2) {
17029
- const realId = record2.id.trim();
17327
+ function preserveRecordObjectRealId(record3) {
17328
+ const realId = record3.id.trim();
17030
17329
  if (!realId) {
17031
- return record2;
17330
+ return record3;
17032
17331
  }
17033
- const fields = record2.fields || {};
17332
+ const fields = record3.fields || {};
17034
17333
  if (typeof fields.real_id === "string" && fields.real_id.trim()) {
17035
- return record2;
17334
+ return record3;
17036
17335
  }
17037
17336
  return {
17038
- ...record2,
17337
+ ...record3,
17039
17338
  fields: {
17040
17339
  ...fields,
17041
17340
  real_id: realId
@@ -17256,13 +17555,15 @@ var Environment = class _Environment {
17256
17555
  _apiEndpoint;
17257
17556
  _tokenProvider;
17258
17557
  _beforeMutationSend;
17259
- constructor(granular, envData, apiKey, apiEndpoint, tokenProvider, beforeMutationSend) {
17558
+ _graphqlIdempotencyKey;
17559
+ constructor(granular, envData, apiKey, apiEndpoint, tokenProvider, beforeMutationSend, graphqlIdempotencyKey) {
17260
17560
  this.granular = granular;
17261
17561
  this.envData = envData;
17262
17562
  this._apiKey = apiKey;
17263
17563
  this._apiEndpoint = apiEndpoint;
17264
17564
  this._tokenProvider = tokenProvider;
17265
17565
  this._beforeMutationSend = beforeMutationSend;
17566
+ this._graphqlIdempotencyKey = graphqlIdempotencyKey;
17266
17567
  }
17267
17568
  /** The environment ID */
17268
17569
  get environmentId() {
@@ -17367,7 +17668,7 @@ var Environment = class _Environment {
17367
17668
  }
17368
17669
  get data() {
17369
17670
  return {
17370
- record: async (record2) => this.recordObject(record2),
17671
+ record: async (record3) => this.recordObject(record3),
17371
17672
  recordMany: async (records, options) => this.recordObjects(records, options),
17372
17673
  import: async (records, options) => this.enqueueRecordImport(records, options),
17373
17674
  listImports: async (status) => this.listRecordImports(status),
@@ -17805,11 +18106,13 @@ var Environment = class _Environment {
17805
18106
  if (this._beforeMutationSend && isGraphQLMutationDocument(query)) {
17806
18107
  this._beforeMutationSend?.();
17807
18108
  }
18109
+ const idempotencyKey = this._graphqlIdempotencyKey?.(query, variables);
17808
18110
  const response = await fetch(this._apiEndpoint, {
17809
18111
  method: "POST",
17810
18112
  headers: {
17811
18113
  "Content-Type": "application/json",
17812
- Authorization: `Bearer ${authToken}`
18114
+ Authorization: `Bearer ${authToken}`,
18115
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
17813
18116
  },
17814
18117
  body: JSON.stringify({
17815
18118
  environmentId: this.environmentId,
@@ -17864,6 +18167,11 @@ var Environment = class _Environment {
17864
18167
  classNames: options.classNames?.length ? options.classNames : []
17865
18168
  }
17866
18169
  );
18170
+ if (response.errors?.length) {
18171
+ throw new Error(
18172
+ `Record search failed: ${response.errors.map((error) => error.message).join("; ")}`
18173
+ );
18174
+ }
17867
18175
  const seen = /* @__PURE__ */ new Set();
17868
18176
  const results = (response.data?.record_search || []).flatMap((entry) => {
17869
18177
  const className = entry.className?.trim();
@@ -19378,6 +19686,7 @@ var Granular = class _Granular {
19378
19686
  onUnexpectedClose;
19379
19687
  onReconnectError;
19380
19688
  effectHostUrl;
19689
+ graphqlIdempotencyKey;
19381
19690
  debugHttp = process.env.GRANULAR_DEBUG_HTTP === "1";
19382
19691
  /** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
19383
19692
  sandboxEffects = /* @__PURE__ */ new Map();
@@ -19407,6 +19716,7 @@ var Granular = class _Granular {
19407
19716
  this.onUnexpectedClose = options.onUnexpectedClose;
19408
19717
  this.onReconnectError = options.onReconnectError;
19409
19718
  this.effectHostUrl = options.effectHostUrl;
19719
+ this.graphqlIdempotencyKey = options.graphqlIdempotencyKey;
19410
19720
  this.httpUrl = this.apiUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://").replace(/\/ws$/, "");
19411
19721
  }
19412
19722
  /**
@@ -20422,7 +20732,9 @@ var Granular = class _Granular {
20422
20732
  envData,
20423
20733
  this.apiKey,
20424
20734
  graphqlEndpoint,
20425
- this.tokenProvider
20735
+ this.tokenProvider,
20736
+ void 0,
20737
+ this.graphqlIdempotencyKey
20426
20738
  );
20427
20739
  }
20428
20740
  async bindWebSocketEnvironmentSession(environment, clientId, session, transportOptions = {}) {
@@ -21022,13 +21334,13 @@ var Granular = class _Granular {
21022
21334
  * Permission Profile management for sandboxes
21023
21335
  */
21024
21336
  get permissionProfiles() {
21025
- const profileSourceFromRecord = (record2) => {
21026
- const profile = record2.profile || record2.rules || {};
21337
+ const profileSourceFromRecord = (record3) => {
21338
+ const profile = record3.profile || record3.rules || {};
21027
21339
  return {
21028
21340
  ...profile,
21029
21341
  schemaVersion: profile.schemaVersion || 1,
21030
- name: profile.name || record2.name,
21031
- description: profile.description || record2.description
21342
+ name: profile.name || record3.name,
21343
+ description: profile.description || record3.description
21032
21344
  };
21033
21345
  };
21034
21346
  return {
@@ -21598,276 +21910,6 @@ async function pollEnvironmentSetup(input) {
21598
21910
  }
21599
21911
  }
21600
21912
 
21601
- // src/record-snapshot-projection.ts
21602
- function stableValue(value) {
21603
- if (Array.isArray(value)) return value.map(stableValue);
21604
- if (value && typeof value === "object") {
21605
- return Object.fromEntries(
21606
- Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stableValue(item)])
21607
- );
21608
- }
21609
- return value;
21610
- }
21611
- function stableJson(value) {
21612
- return JSON.stringify(stableValue(value));
21613
- }
21614
- function recordKey(record2) {
21615
- return `${record2.className}\0${record2.id}`;
21616
- }
21617
- function recordReference(className, id) {
21618
- return { className, id };
21619
- }
21620
- function changedRecordSnapshots(beforeRecords, afterRecords) {
21621
- const before = new Map(
21622
- beforeRecords.map((record2) => [recordKey(record2), record2])
21623
- );
21624
- const after = new Map(
21625
- afterRecords.map((record2) => [recordKey(record2), record2])
21626
- );
21627
- const changedKeys = /* @__PURE__ */ new Set();
21628
- for (const key of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) {
21629
- if (stableJson(before.get(key)) !== stableJson(after.get(key))) {
21630
- changedKeys.add(key);
21631
- }
21632
- }
21633
- return {
21634
- beforeRecords: beforeRecords.filter(
21635
- (record2) => changedKeys.has(recordKey(record2))
21636
- ),
21637
- afterRecords: afterRecords.filter(
21638
- (record2) => changedKeys.has(recordKey(record2))
21639
- )
21640
- };
21641
- }
21642
- function relationshipTargets(manifest) {
21643
- const targets = /* @__PURE__ */ new Map();
21644
- for (const volume of manifest.volumes) {
21645
- for (const operation of volume.operations) {
21646
- const relationship = operation.defineRelationship;
21647
- if (!relationship) continue;
21648
- targets.set(`${relationship.left}\0${relationship.leftSubmodel}`, {
21649
- className: relationship.right
21650
- });
21651
- targets.set(`${relationship.right}\0${relationship.rightSubmodel}`, {
21652
- className: relationship.left
21653
- });
21654
- }
21655
- }
21656
- return targets;
21657
- }
21658
- function stateName(value) {
21659
- return typeof value === "string" ? value : value.state;
21660
- }
21661
- function asIds(value) {
21662
- return value === void 0 ? [] : Array.isArray(value) ? value : [value];
21663
- }
21664
- function changedObjectProjection(before, after) {
21665
- const beforeFields = before?.fields || {};
21666
- const afterFields = after.fields || {};
21667
- const fields = { ...afterFields };
21668
- for (const fieldName of Object.keys(beforeFields)) {
21669
- if (!(fieldName in afterFields)) fields[fieldName] = null;
21670
- }
21671
- if (before && before.label === after.label && stableJson(beforeFields) === stableJson(afterFields)) {
21672
- return null;
21673
- }
21674
- return {
21675
- kind: "object",
21676
- operation: before ? "updated" : "created",
21677
- record: {
21678
- className: after.className,
21679
- id: after.id,
21680
- ...after.label !== void 0 ? { label: after.label } : {},
21681
- fields
21682
- }
21683
- };
21684
- }
21685
- function projectionChanges(input) {
21686
- const before = new Map(
21687
- input.beforeRecords.map((record2) => [recordKey(record2), record2])
21688
- );
21689
- const after = new Map(
21690
- input.afterRecords.map((record2) => [recordKey(record2), record2])
21691
- );
21692
- const targets = relationshipTargets(input.manifest);
21693
- const changes = [];
21694
- for (const record2 of input.afterRecords) {
21695
- const previous = before.get(recordKey(record2));
21696
- const objectChange = changedObjectProjection(previous, record2);
21697
- if (objectChange) changes.push(objectChange);
21698
- const relationshipNames = /* @__PURE__ */ new Set([
21699
- ...Object.keys(previous?.relationships || {}),
21700
- ...Object.keys(record2.relationships || {})
21701
- ]);
21702
- for (const relationshipName of [...relationshipNames].sort()) {
21703
- const target = targets.get(
21704
- `${record2.className}\0${relationshipName}`
21705
- );
21706
- if (!target) {
21707
- throw new Error(
21708
- `No ontology relationship target is declared for ${record2.className}.${relationshipName}`
21709
- );
21710
- }
21711
- const previousIds = new Set(
21712
- asIds(previous?.relationships?.[relationshipName])
21713
- );
21714
- const currentIds = new Set(
21715
- asIds(record2.relationships?.[relationshipName])
21716
- );
21717
- for (const id of [...previousIds].sort()) {
21718
- if (currentIds.has(id)) continue;
21719
- changes.push({
21720
- kind: "relationship",
21721
- operation: "disconnected",
21722
- relationship: relationshipName,
21723
- from: recordReference(record2.className, record2.id),
21724
- to: recordReference(target.className, id)
21725
- });
21726
- }
21727
- for (const id of [...currentIds].sort()) {
21728
- if (previousIds.has(id)) continue;
21729
- changes.push({
21730
- kind: "relationship",
21731
- operation: "connected",
21732
- relationship: relationshipName,
21733
- from: recordReference(record2.className, record2.id),
21734
- to: recordReference(target.className, id)
21735
- });
21736
- }
21737
- }
21738
- const machines = /* @__PURE__ */ new Set([
21739
- ...Object.keys(previous?.states || {}),
21740
- ...Object.keys(record2.states || {})
21741
- ]);
21742
- for (const machine of [...machines].sort()) {
21743
- const next = record2.states?.[machine];
21744
- if (next === void 0) continue;
21745
- const prior = previous?.states?.[machine];
21746
- if (prior !== void 0 && stateName(prior) === stateName(next)) continue;
21747
- if (input.transition && input.transition.className === record2.className && input.transition.objectId === record2.id && input.transition.machine === machine) {
21748
- continue;
21749
- }
21750
- changes.push({
21751
- kind: "state_observation",
21752
- target: recordReference(record2.className, record2.id),
21753
- machine,
21754
- state: stateName(next)
21755
- });
21756
- }
21757
- }
21758
- for (const record2 of input.beforeRecords) {
21759
- if (after.has(recordKey(record2))) continue;
21760
- changes.push({
21761
- kind: "object",
21762
- operation: "deleted",
21763
- target: recordReference(record2.className, record2.id)
21764
- });
21765
- }
21766
- return changes;
21767
- }
21768
- function valueAtPath(value, path) {
21769
- return path.split(".").filter(Boolean).reduce((current, segment) => {
21770
- if (!current || typeof current !== "object" || Array.isArray(current)) {
21771
- return void 0;
21772
- }
21773
- return current[segment];
21774
- }, value);
21775
- }
21776
- function primaryTarget(input) {
21777
- if (input.transition) {
21778
- return recordReference(
21779
- input.transition.className,
21780
- input.transition.objectId
21781
- );
21782
- }
21783
- const creates = input.effect.metamodels?.creates;
21784
- if (creates) {
21785
- const declaration = typeof creates === "string" ? { className: creates } : creates;
21786
- const id = declaration.idPath ? valueAtPath(input.result, declaration.idPath) : void 0;
21787
- if (typeof id === "string" && id) {
21788
- return recordReference(declaration.className, id);
21789
- }
21790
- }
21791
- if (input.effect.className && !input.effect.static) {
21792
- const objectId = input.effectInput && typeof input.effectInput === "object" && !Array.isArray(input.effectInput) ? input.effectInput._objectId : void 0;
21793
- if (typeof objectId === "string" && objectId) {
21794
- return recordReference(input.effect.className, objectId);
21795
- }
21796
- }
21797
- const created = input.changes.filter(
21798
- (change) => change.kind === "object" && change.operation === "created"
21799
- );
21800
- const onlyCreated = created.length === 1 ? created[0] : void 0;
21801
- return onlyCreated ? recordReference(onlyCreated.record.className, onlyCreated.record.id) : void 0;
21802
- }
21803
- function transitionOutcome(transition, afterRecords) {
21804
- if (!transition) {
21805
- throw new Error("A transition commit has no authored transition context.");
21806
- }
21807
- const target = afterRecords.find(
21808
- (record2) => record2.className === transition.className && record2.id === transition.objectId
21809
- );
21810
- const observed = target?.states?.[transition.machine];
21811
- if (!observed) {
21812
- throw new Error(
21813
- `The product result did not expose ${transition.className}:${transition.objectId}.${transition.machine}`
21814
- );
21815
- }
21816
- const finalState = stateName(observed);
21817
- const matches = Object.entries(transition.outcomes).filter(
21818
- ([, outcome]) => outcome.to === finalState || outcome.to === "$current" && finalState === transition.from
21819
- );
21820
- if (matches.length !== 1) {
21821
- throw new Error(
21822
- `Product state ${finalState} maps to ${matches.length} authored outcomes for ${transition.machine}.${transition.transition}`
21823
- );
21824
- }
21825
- const [key, declaration] = matches[0];
21826
- return {
21827
- key,
21828
- ...declaration.disposition === "error" ? {
21829
- error: {
21830
- code: `product_outcome_${key}`,
21831
- message: declaration.label || `The product completed in ${finalState} instead of continuing.`,
21832
- retryable: false
21833
- }
21834
- } : {}
21835
- };
21836
- }
21837
- function projectRecordSnapshotMutation(input) {
21838
- const changes = projectionChanges({
21839
- manifest: input.manifest,
21840
- beforeRecords: input.source.beforeRecords,
21841
- afterRecords: input.source.afterRecords,
21842
- transition: input.transition
21843
- });
21844
- const target = primaryTarget({
21845
- effect: input.effect,
21846
- effectInput: input.effectInput,
21847
- result: input.result,
21848
- changes,
21849
- transition: input.transition
21850
- });
21851
- const base = {
21852
- source: {
21853
- reference: input.source.reference,
21854
- ...input.source.version ? { version: input.source.version } : {},
21855
- ...input.source.versionScope ? { versionScope: input.source.versionScope } : {}
21856
- },
21857
- ...target ? { primaryTarget: target } : {},
21858
- changes,
21859
- safeSummary: {
21860
- effect: input.effect.name,
21861
- affectedChanges: changes.length
21862
- }
21863
- };
21864
- if (input.commitKind === "effect") return base;
21865
- return {
21866
- ...base,
21867
- outcome: transitionOutcome(input.transition, input.source.afterRecords)
21868
- };
21869
- }
21870
-
21871
21913
  // src/agent-harness-templates/action-presentation/0.1.0/manifest.json
21872
21914
  var manifest_default = {
21873
21915
  id: "action-presentation",
@@ -22023,29 +22065,29 @@ function requireRecord(value, context) {
22023
22065
  }
22024
22066
  return value;
22025
22067
  }
22026
- function requiredString(record2, key, context) {
22027
- const value = record2[key];
22068
+ function requiredString(record3, key, context) {
22069
+ const value = record3[key];
22028
22070
  if (typeof value !== "string" || !value.trim()) {
22029
22071
  throw new Error(`${context}.${key} must be a non-empty string.`);
22030
22072
  }
22031
22073
  return value;
22032
22074
  }
22033
- function requiredNumber(record2, key, context) {
22034
- const value = record2[key];
22075
+ function requiredNumber(record3, key, context) {
22076
+ const value = record3[key];
22035
22077
  if (typeof value !== "number" || !Number.isFinite(value)) {
22036
22078
  throw new Error(`${context}.${key} must be a finite number.`);
22037
22079
  }
22038
22080
  return value;
22039
22081
  }
22040
- function requiredStringArray(record2, key, context) {
22041
- const value = record2[key];
22082
+ function requiredStringArray(record3, key, context) {
22083
+ const value = record3[key];
22042
22084
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.trim())) {
22043
22085
  throw new Error(`${context}.${key} must be an array of non-empty strings.`);
22044
22086
  }
22045
22087
  return [...value];
22046
22088
  }
22047
- function requiredNumberRecord(record2, key, context) {
22048
- const value = requireRecord(record2[key], `${context}.${key}`);
22089
+ function requiredNumberRecord(record3, key, context) {
22090
+ const value = requireRecord(record3[key], `${context}.${key}`);
22049
22091
  const output = {};
22050
22092
  for (const [entryKey, entryValue] of Object.entries(value)) {
22051
22093
  if (typeof entryValue !== "number" || !Number.isFinite(entryValue) || entryValue < 0 || entryValue > 1) {
@@ -22058,22 +22100,22 @@ function requiredNumberRecord(record2, key, context) {
22058
22100
  return output;
22059
22101
  }
22060
22102
  function validateHarnessTemplateManifest(value, context = "HarnessTemplateManifest") {
22061
- const record2 = requireRecord(value, context);
22062
- const status = requiredString(record2, "status", context);
22103
+ const record3 = requireRecord(value, context);
22104
+ const status = requiredString(record3, "status", context);
22063
22105
  if (!HARNESS_TEMPLATE_STATUSES.includes(status)) {
22064
22106
  throw new Error(
22065
22107
  `${context}.status must be one of ${HARNESS_TEMPLATE_STATUSES.join(", ")}.`
22066
22108
  );
22067
22109
  }
22068
- const createdAt = requiredString(record2, "createdAt", context);
22110
+ const createdAt = requiredString(record3, "createdAt", context);
22069
22111
  if (Number.isNaN(Date.parse(createdAt))) {
22070
22112
  throw new Error(`${context}.createdAt must be an ISO timestamp.`);
22071
22113
  }
22072
22114
  const compatibility = requireRecord(
22073
- record2.compatibility,
22115
+ record3.compatibility,
22074
22116
  `${context}.compatibility`
22075
22117
  );
22076
- const evalGates = requireRecord(record2.evalGates, `${context}.evalGates`);
22118
+ const evalGates = requireRecord(record3.evalGates, `${context}.evalGates`);
22077
22119
  const maxRegressionPct = requiredNumber(
22078
22120
  evalGates,
22079
22121
  "maxRegressionPct",
@@ -22085,23 +22127,23 @@ function validateHarnessTemplateManifest(value, context = "HarnessTemplateManife
22085
22127
  );
22086
22128
  }
22087
22129
  return {
22088
- id: requiredString(record2, "id", context),
22089
- version: requiredString(record2, "version", context),
22130
+ id: requiredString(record3, "id", context),
22131
+ version: requiredString(record3, "version", context),
22090
22132
  status,
22091
- owner: requiredString(record2, "owner", context),
22133
+ owner: requiredString(record3, "owner", context),
22092
22134
  createdAt,
22093
- changelog: requiredString(record2, "changelog", context),
22094
- promptBuilder: requiredString(record2, "promptBuilder", context),
22095
- continuationBuilder: requiredString(record2, "continuationBuilder", context),
22135
+ changelog: requiredString(record3, "changelog", context),
22136
+ promptBuilder: requiredString(record3, "promptBuilder", context),
22137
+ continuationBuilder: requiredString(record3, "continuationBuilder", context),
22096
22138
  modelOutputInstruction: requiredString(
22097
- record2,
22139
+ record3,
22098
22140
  "modelOutputInstruction",
22099
22141
  context
22100
22142
  ),
22101
- codeReviewPolicy: requiredString(record2, "codeReviewPolicy", context),
22102
- defaultModel: requiredString(record2, "defaultModel", context),
22103
- modelMatrix: requiredStringArray(record2, "modelMatrix", context),
22104
- temperature: requiredNumber(record2, "temperature", context),
22143
+ codeReviewPolicy: requiredString(record3, "codeReviewPolicy", context),
22144
+ defaultModel: requiredString(record3, "defaultModel", context),
22145
+ modelMatrix: requiredStringArray(record3, "modelMatrix", context),
22146
+ temperature: requiredNumber(record3, "temperature", context),
22105
22147
  compatibility: {
22106
22148
  minSdkVersion: requiredString(
22107
22149
  compatibility,
@@ -22246,8 +22288,8 @@ function stableStringify(value) {
22246
22288
  if (Array.isArray(value)) {
22247
22289
  return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
22248
22290
  }
22249
- const record2 = value;
22250
- return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record2[key])}`).join(",")}}`;
22291
+ const record3 = value;
22292
+ return `{${Object.keys(record3).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record3[key])}`).join(",")}}`;
22251
22293
  }
22252
22294
  function renderConstBlock(name, value) {
22253
22295
  return `const ${name} = ${JSON.stringify(value, null, 2)} as const;`;
@@ -22843,9 +22885,9 @@ function getLatestClosure(liveDoc) {
22843
22885
  }
22844
22886
  const closures = [];
22845
22887
  for (const [closureId, value] of Object.entries(closuresById)) {
22846
- const record2 = asRecord4(value);
22847
- if (!record2) continue;
22848
- closures.push({ ...record2, closureId });
22888
+ const record3 = asRecord4(value);
22889
+ if (!record3) continue;
22890
+ closures.push({ ...record3, closureId });
22849
22891
  }
22850
22892
  closures.sort(
22851
22893
  (left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0)
@@ -22883,9 +22925,9 @@ function getJobRecords(liveDoc) {
22883
22925
  const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
22884
22926
  const jobs = [];
22885
22927
  for (const [jobId, value] of Object.entries(jobsById)) {
22886
- const record2 = asRecord4(value);
22887
- if (!record2) continue;
22888
- jobs.push({ ...record2, jobId });
22928
+ const record3 = asRecord4(value);
22929
+ if (!record3) continue;
22930
+ jobs.push({ ...record3, jobId });
22889
22931
  }
22890
22932
  jobs.sort(
22891
22933
  (left, right) => getJobTimestamp(right) - getJobTimestamp(left)
@@ -23071,8 +23113,8 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
23071
23113
  const prompts = asRecord4(asRecord4(job)?.prompts);
23072
23114
  if (!prompts) continue;
23073
23115
  for (const prompt of Object.values(prompts)) {
23074
- const record2 = asRecord4(prompt);
23075
- if (record2?.status === "open") return true;
23116
+ const record3 = asRecord4(prompt);
23117
+ if (record3?.status === "open") return true;
23076
23118
  }
23077
23119
  }
23078
23120
  return false;
@@ -23131,13 +23173,13 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
23131
23173
  title: typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision",
23132
23174
  status,
23133
23175
  candidates: status === "open" ? asArray(decision.candidates).slice(0, 5).map((candidate) => {
23134
- const record2 = asRecord4(candidate);
23135
- if (!record2) return null;
23176
+ const record3 = asRecord4(candidate);
23177
+ if (!record3) return null;
23136
23178
  return {
23137
- id: typeof record2.id === "string" ? record2.id : "unknown",
23138
- label: typeof record2.label === "string" && record2.label.trim() ? record2.label.trim() : null,
23139
- description: typeof record2.description === "string" && record2.description.trim() ? record2.description.trim() : null,
23140
- metadata: asRecord4(record2.metadata)
23179
+ id: typeof record3.id === "string" ? record3.id : "unknown",
23180
+ label: typeof record3.label === "string" && record3.label.trim() ? record3.label.trim() : null,
23181
+ description: typeof record3.description === "string" && record3.description.trim() ? record3.description.trim() : null,
23182
+ metadata: asRecord4(record3.metadata)
23141
23183
  };
23142
23184
  }).filter(Boolean) : [],
23143
23185
  selected: status === "open" ? null : {
@@ -23772,11 +23814,11 @@ function normalizeJsonSchema(value) {
23772
23814
  return asRecord4(value);
23773
23815
  }
23774
23816
  function jsonSchemaTypeName(schema) {
23775
- const record2 = normalizeJsonSchema(schema);
23776
- if (!record2) return "unknown";
23777
- const type = record2.type;
23778
- if (record2.format === "date-time") return "date-time";
23779
- if (record2.format === "date" || type === "date" || Array.isArray(type) && type.includes("date")) {
23817
+ const record3 = normalizeJsonSchema(schema);
23818
+ if (!record3) return "unknown";
23819
+ const type = record3.type;
23820
+ if (record3.format === "date-time") return "date-time";
23821
+ if (record3.format === "date" || type === "date" || Array.isArray(type) && type.includes("date")) {
23780
23822
  return "date";
23781
23823
  }
23782
23824
  if (typeof type === "string") {
@@ -23787,12 +23829,12 @@ function jsonSchemaTypeName(schema) {
23787
23829
  return "unknown";
23788
23830
  }
23789
23831
  function summarizeObjectSchema(schema) {
23790
- const record2 = normalizeJsonSchema(schema);
23791
- const properties = asRecord4(record2?.properties);
23832
+ const record3 = normalizeJsonSchema(schema);
23833
+ const properties = asRecord4(record3?.properties);
23792
23834
  if (!properties || Object.keys(properties).length === 0) {
23793
- return record2 ? "{}" : null;
23835
+ return record3 ? "{}" : null;
23794
23836
  }
23795
- const required = new Set(asArray(record2?.required));
23837
+ const required = new Set(asArray(record3?.required));
23796
23838
  const fields = Object.entries(properties).slice(0, 8).map(([name, property]) => {
23797
23839
  const marker = required.has(name) ? "*" : "?";
23798
23840
  return `${name}${marker}: ${jsonSchemaTypeName(property)}`;
@@ -24556,39 +24598,39 @@ function pushStringArray(target, value) {
24556
24598
  pushString(target, item);
24557
24599
  }
24558
24600
  }
24559
- function collectReferencesFromRecord(record2, refs) {
24601
+ function collectReferencesFromRecord(record3, refs) {
24560
24602
  for (const key of ENTRY_KEY_CANDIDATES)
24561
- pushString(refs.entryPaths, record2[key]);
24603
+ pushString(refs.entryPaths, record3[key]);
24562
24604
  for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
24563
- pushStringArray(refs.entryPaths, record2[key]);
24605
+ pushStringArray(refs.entryPaths, record3[key]);
24564
24606
  for (const key of LIST_KEY_CANDIDATES)
24565
- pushString(refs.listNames, record2[key]);
24607
+ pushString(refs.listNames, record3[key]);
24566
24608
  for (const key of LIST_ARRAY_KEY_CANDIDATES)
24567
- pushStringArray(refs.listNames, record2[key]);
24609
+ pushStringArray(refs.listNames, record3[key]);
24568
24610
  for (const key of VARIABLE_KEY_CANDIDATES)
24569
- pushString(refs.variableNames, record2[key]);
24611
+ pushString(refs.variableNames, record3[key]);
24570
24612
  for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
24571
- pushStringArray(refs.variableNames, record2[key]);
24613
+ pushStringArray(refs.variableNames, record3[key]);
24572
24614
  }
24573
- function stringValue(record2, keys) {
24615
+ function stringValue(record3, keys) {
24574
24616
  for (const key of keys) {
24575
- const value = record2[key];
24617
+ const value = record3[key];
24576
24618
  if (typeof value === "string" && value.trim()) {
24577
24619
  return value.trim();
24578
24620
  }
24579
24621
  }
24580
24622
  return null;
24581
24623
  }
24582
- function findEntryPathForRecord(record2, heap) {
24583
- const directPath = stringValue(record2, ["entryPath", "path"]);
24624
+ function findEntryPathForRecord(record3, heap) {
24625
+ const directPath = stringValue(record3, ["entryPath", "path"]);
24584
24626
  if (directPath && heap.entriesByPath?.[directPath]) {
24585
24627
  return directPath;
24586
24628
  }
24587
- const id = stringValue(record2, ["id", "_id", "recordId", "objectId"]);
24629
+ const id = stringValue(record3, ["id", "_id", "recordId", "objectId"]);
24588
24630
  if (!id) {
24589
24631
  return null;
24590
24632
  }
24591
- const className = stringValue(record2, [
24633
+ const className = stringValue(record3, [
24592
24634
  "className",
24593
24635
  "_className",
24594
24636
  "__className",
@@ -24622,17 +24664,17 @@ function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__
24622
24664
  }
24623
24665
  return;
24624
24666
  }
24625
- const record2 = asRecord5(value);
24626
- if (!record2) return;
24667
+ const record3 = asRecord5(value);
24668
+ if (!record3) return;
24627
24669
  seen.add(value);
24628
- const entryPath = findEntryPathForRecord(record2, heap);
24670
+ const entryPath = findEntryPathForRecord(record3, heap);
24629
24671
  if (entryPath) refs.entryPaths.add(entryPath);
24630
- collectReferencesFromRecord(record2, refs);
24672
+ collectReferencesFromRecord(record3, refs);
24631
24673
  for (const key of UI_CONTAINER_KEYS) {
24632
- const nested = asRecord5(record2[key]);
24674
+ const nested = asRecord5(record3[key]);
24633
24675
  if (nested) collectReferencesFromRecord(nested, refs);
24634
24676
  }
24635
- for (const nested of Object.values(record2).slice(0, 24)) {
24677
+ for (const nested of Object.values(record3).slice(0, 24)) {
24636
24678
  scanForHeapReferences(nested, heap, refs, depth + 1, seen);
24637
24679
  }
24638
24680
  }
@@ -24781,8 +24823,8 @@ var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
24781
24823
  function asRecord6(value) {
24782
24824
  return value && typeof value === "object" ? value : null;
24783
24825
  }
24784
- function numberField(record2, key) {
24785
- const value = record2?.[key];
24826
+ function numberField(record3, key) {
24827
+ const value = record3?.[key];
24786
24828
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
24787
24829
  }
24788
24830
  function microsPerMillion(usdPerMillion) {
@@ -24886,6 +24928,39 @@ function calculateOpenAITokenSpend(model, rawUsage) {
24886
24928
  };
24887
24929
  }
24888
24930
 
24931
+ // src/artifact-action-review.ts
24932
+ function artifactReviewedOperation(artifact, inputValues = artifact.inputValues || {}, relationships = artifact.relationships || {}) {
24933
+ const metadata = artifact.metadata || {};
24934
+ const statePlan = { ...record2(metadata.statePlan) };
24935
+ delete statePlan.approvedTransitions;
24936
+ delete statePlan.approvalDecisions;
24937
+ return {
24938
+ artifactId: artifact.artifactId,
24939
+ sessionId: artifact.sessionId || null,
24940
+ environmentId: artifact.environmentId || null,
24941
+ subjectId: artifact.subjectId || null,
24942
+ buildId: artifact.buildId || null,
24943
+ buildVersionNumber: artifact.buildVersionNumber ?? null,
24944
+ kind: artifact.kind,
24945
+ target: artifact.target || null,
24946
+ policy: artifact.policy ?? null,
24947
+ inputSchema: artifact.inputSchema || null,
24948
+ inputValues,
24949
+ relationships,
24950
+ effectKey: metadata.effectKey || metadata.toolName || null,
24951
+ effect: metadata.effect || null,
24952
+ statePlan,
24953
+ draftRecord: metadata.draftRecord || null,
24954
+ relationshipCreate: metadata.relationshipCreate || null,
24955
+ autonomy: metadata.autonomy || null,
24956
+ batch: metadata.batch || null,
24957
+ subArtifactIds: artifact.subArtifactIds || []
24958
+ };
24959
+ }
24960
+ function record2(value) {
24961
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
24962
+ }
24963
+
24889
24964
  // src/artifact-surface.ts
24890
24965
  function operationNode(workflow) {
24891
24966
  return getArtifactWorkflowNodes(workflow).find(
@@ -25096,6 +25171,6 @@ function selectArtifactSurface(artifact, context = {}) {
25096
25171
  };
25097
25172
  }
25098
25173
 
25099
- export { Environment, EnvironmentSession, GRANULAR_FEED_DIAGNOSTIC_EVENT, Granular, OPENAI_MODEL_PRICING_USD_PER_MILLION, OntologyHandle, Session, SessionFeedController, WSClient, assertArtifactWorkflow, buildContinuationInstruction, buildContinuationInstructionFromTemplate, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentFileBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentManualActionBlock, buildGranularAgentManualActionMemorySummary, buildGranularAgentReferentBlock, buildGranularAgentRuntimeImportsBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentSystemPromptFromTemplate, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildOpenAISpendEventId, buildSessionTranscript, buildSessionTranscriptFromFeedItems, calculateOpenAITokenSpend, canInspectArtifactWorkflowNode, canonicalizeCommitValue, changedRecordSnapshots, consumeGranularReasoningOnlyChunk, consumeGranularReasoningTraceChunk, createFeedPublisher, createHarnessVerifierSnapshot, defineEffect, defineProjection, defineStateMachine, emitFeedDiagnostic, emitFeedDiagnosticToDefaultSink, emptyFeedSnapshot, environmentSetupProgressFromError, environmentSetupProgressFromResponse, evaluateContinuation, evaluateValidationRule, extractPromptTokens, getArtifactWorkflowNavigationLeaves, getArtifactWorkflowNavigationTarget, getArtifactWorkflowNodes, getCurrentArtifactWorkflowNode, getCurrentClosureId, getDefaultHarnessTemplateId, getExclusivePromptTarget, getOpenAIModelPricing, hasCanonicalSessionFeedActivation, hasOpenPrompt, hashHarnessTemplateValue, invokeRegisteredEffect, isArtifactWorkflowNodeNavigableLeaf, isArtifactWorkflowNodeTerminal, isCanonicalSessionFeedDocument, isGraphQLMutationDocument, isLocalApiUrl, listHarnessTemplates, mergeFeedItemsBySequence, normalizeEffectBehaviors, normalizeFeedDiagnostic, normalizeFeedDiagnosticKind, normalizeFeedPage, normalizeOpenAIUsage, normalizePrompt, normalizePromptChoiceOption, normalizePromptText, normalizePromptType, orderTransientFeedItems, pollEnvironmentSetup, projectArtifactWorkflow, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectRecordSnapshotMutation, projectSessionFileSummary, projectWorkflowFocus, projectWorkflowSummary, readSessionFeedSnapshot, recordOpenAIUsageSpend, renderContinuationInstructionFromTemplate, renderGranularAgentSystemPromptFromTemplate, requireArtifactWorkflow, requireArtifactWorkflowResult, resolveApiUrl, resolveAuthTokenForApiUrl, resolveHarnessTemplate, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch, selectArtifactSurface, stripGranularReasoningTrace, toGranularHttpBase, validateHarnessTemplateManifest, validateProjectionResult, validationRuleFailureMessage };
25174
+ export { Environment, EnvironmentSession, GRANULAR_FEED_DIAGNOSTIC_EVENT, Granular, OPENAI_MODEL_PRICING_USD_PER_MILLION, OntologyHandle, Session, SessionFeedController, WSClient, artifactReviewedOperation, assertArtifactWorkflow, buildContinuationInstruction, buildContinuationInstructionFromTemplate, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentFileBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentManualActionBlock, buildGranularAgentManualActionMemorySummary, buildGranularAgentReferentBlock, buildGranularAgentRuntimeImportsBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentSystemPromptFromTemplate, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildOpenAISpendEventId, buildSessionTranscript, buildSessionTranscriptFromFeedItems, calculateOpenAITokenSpend, canInspectArtifactWorkflowNode, canonicalizeCommitValue, changedRecordSnapshots, consumeGranularReasoningOnlyChunk, consumeGranularReasoningTraceChunk, createFeedPublisher, createHarnessVerifierSnapshot, defineEffect, defineProjection, defineStateMachine, emitFeedDiagnostic, emitFeedDiagnosticToDefaultSink, emptyFeedSnapshot, environmentSetupProgressFromError, environmentSetupProgressFromResponse, evaluateContinuation, evaluateValidationRule, extractPromptTokens, getArtifactWorkflowNavigationLeaves, getArtifactWorkflowNavigationTarget, getArtifactWorkflowNodes, getCurrentArtifactWorkflowNode, getCurrentClosureId, getDefaultHarnessTemplateId, getExclusivePromptTarget, getOpenAIModelPricing, hasCanonicalSessionFeedActivation, hasOpenPrompt, hashHarnessTemplateValue, invokeRegisteredEffect, isArtifactWorkflowNodeNavigableLeaf, isArtifactWorkflowNodeTerminal, isCanonicalSessionFeedDocument, isGraphQLMutationDocument, isLocalApiUrl, listHarnessTemplates, mergeFeedItemsBySequence, normalizeEffectBehaviors, normalizeFeedDiagnostic, normalizeFeedDiagnosticKind, normalizeFeedPage, normalizeOpenAIUsage, normalizePrompt, normalizePromptChoiceOption, normalizePromptText, normalizePromptType, orderTransientFeedItems, pollEnvironmentSetup, projectArtifactWorkflow, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectRecordSnapshotMutation, projectSessionFileSummary, projectWorkflowFocus, projectWorkflowSummary, readSessionFeedSnapshot, recordOpenAIUsageSpend, renderContinuationInstructionFromTemplate, renderGranularAgentSystemPromptFromTemplate, requireArtifactWorkflow, requireArtifactWorkflowResult, resolveApiUrl, resolveAuthTokenForApiUrl, resolveHarnessTemplate, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch, selectArtifactSurface, stripGranularReasoningTrace, toGranularHttpBase, validateHarnessTemplateManifest, validateProjectionResult, validationRuleFailureMessage };
25100
25175
  //# sourceMappingURL=index.mjs.map
25101
25176
  //# sourceMappingURL=index.mjs.map