@granular-software/sdk 0.4.69 → 0.4.71

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/cli/index.js CHANGED
@@ -14179,6 +14179,7 @@ var StateMachineTransitionSchema = external_exports.object({
14179
14179
  to: external_exports.string().min(1),
14180
14180
  label: external_exports.string().optional(),
14181
14181
  description: external_exports.string().optional(),
14182
+ userVisible: external_exports.boolean().optional(),
14182
14183
  action: StateTransitionActionSchema.optional(),
14183
14184
  assignee: StateTransitionAssigneeSchema.optional(),
14184
14185
  requirements: StateTransitionRequirementsSchema.optional(),
@@ -14263,6 +14264,7 @@ var CreatesSchema = external_exports.union([
14263
14264
  idPath: external_exports.string().min(1).optional(),
14264
14265
  pathPath: external_exports.string().min(1).optional(),
14265
14266
  statePath: external_exports.string().min(1).optional(),
14267
+ completionState: external_exports.string().min(1).optional(),
14266
14268
  classStateHandle: external_exports.boolean().optional()
14267
14269
  }).strict()
14268
14270
  ]);
@@ -14612,6 +14614,7 @@ function normalizeCreationSummary(metamodels) {
14612
14614
  ...typeof raw.idPath === "string" && raw.idPath.trim() ? { idPath: raw.idPath.trim() } : {},
14613
14615
  ...typeof raw.pathPath === "string" && raw.pathPath.trim() ? { pathPath: raw.pathPath.trim() } : {},
14614
14616
  ...typeof raw.statePath === "string" && raw.statePath.trim() ? { statePath: raw.statePath.trim() } : {},
14617
+ ...typeof raw.completionState === "string" && raw.completionState.trim() ? { completionState: raw.completionState.trim() } : {},
14615
14618
  ...typeof raw.classStateHandle === "boolean" ? { classStateHandle: raw.classStateHandle } : {}
14616
14619
  };
14617
14620
  }
@@ -14737,7 +14740,7 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
14737
14740
  },
14738
14741
  {
14739
14742
  key: "creates",
14740
- description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, classStateHandle }`.'
14743
+ description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, completionState, classStateHandle }`.'
14741
14744
  }
14742
14745
  ]
14743
14746
  },
@@ -15532,6 +15535,7 @@ function normalizeStateMachines(values) {
15532
15535
  to: String(transition?.to?.name || ""),
15533
15536
  label: typeof transition?.label === "string" ? transition.label : null,
15534
15537
  description: typeof transition?.description === "string" ? transition.description : null,
15538
+ userVisible: typeof transition?.user_visible === "boolean" ? transition.user_visible : typeof transition?.userVisible === "boolean" ? transition.userVisible : null,
15535
15539
  action: parseJsonRecord(transition?.action) || parseJsonRecord(transition?.action_json),
15536
15540
  assignee: parseJsonRecord(transition?.assignee) || parseJsonRecord(transition?.assignee_json),
15537
15541
  requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
@@ -15603,6 +15607,9 @@ function transitionMetadataGraphqlArgs(transition) {
15603
15607
  if (typeof transition.description === "string") {
15604
15608
  args.push(`description: ${JSON.stringify(transition.description)}`);
15605
15609
  }
15610
+ if (typeof transition.userVisible === "boolean") {
15611
+ args.push(`user_visible: ${transition.userVisible}`);
15612
+ }
15606
15613
  if (transition.action) {
15607
15614
  args.push(
15608
15615
  `action_json: ${JSON.stringify(JSON.stringify(transition.action))}`
@@ -15914,7 +15921,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
15914
15921
  name: String!
15915
15922
  state_machine: StateMachine!
15916
15923
  add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
15917
- add_transition(name: String!, from: String!, to: String!, label: String, description: String, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String, outcomes_json: String): StateMachineMutation!
15924
+ add_transition(name: String!, from: String!, to: String!, label: String, description: String, user_visible: Boolean, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String, outcomes_json: String): StateMachineMutation!
15918
15925
  activate_transition(name: String!): StateMachineMutation!
15919
15926
  }
15920
15927
 
@@ -15972,6 +15979,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
15972
15979
  to: StateMachineState!
15973
15980
  label: String
15974
15981
  description: String
15982
+ user_visible: Boolean
15975
15983
  action_json: String
15976
15984
  assignee_json: String
15977
15985
  requirements_json: String
@@ -16053,6 +16061,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
16053
16061
  to,
16054
16062
  label,
16055
16063
  description,
16064
+ user_visible,
16056
16065
  action_json,
16057
16066
  assignee_json,
16058
16067
  requirements_json,
@@ -16070,6 +16079,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
16070
16079
  {
16071
16080
  label,
16072
16081
  description,
16082
+ userVisible: user_visible,
16073
16083
  actionJson: action_json,
16074
16084
  assigneeJson: assignee_json,
16075
16085
  requirementsJson: requirements_json,
@@ -16134,6 +16144,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
16134
16144
  to: (value) => value.to_state || { name: value.to, is_final: false },
16135
16145
  label: (value) => value.label || null,
16136
16146
  description: (value) => value.description || null,
16147
+ user_visible: (value) => value.user_visible ?? null,
16137
16148
  action_json: (value) => value.action_json || null,
16138
16149
  assignee_json: (value) => value.assignee_json || null,
16139
16150
  requirements_json: (value) => value.requirements_json || null,
@@ -16220,6 +16231,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
16220
16231
  to { name }
16221
16232
  label
16222
16233
  description
16234
+ user_visible
16223
16235
  action_json
16224
16236
  assignee_json
16225
16237
  requirements_json
@@ -21006,6 +21018,38 @@ function stableDocumentFingerprint(value) {
21006
21018
  (key) => `${JSON.stringify(key)}:${stableDocumentFingerprint(record[key])}`
21007
21019
  ).join(",")}}`;
21008
21020
  }
21021
+ function canonicalDocumentFingerprint(value) {
21022
+ const stripPresentation = (input, path7) => {
21023
+ if (input === null || typeof input !== "object") return input;
21024
+ if (input instanceof Date || input instanceof Uint8Array) return input;
21025
+ if (Array.isArray(input)) {
21026
+ return input.map((entry) => stripPresentation(entry, path7));
21027
+ }
21028
+ const record = input;
21029
+ const output = {};
21030
+ for (const [key, entry] of Object.entries(record)) {
21031
+ if (path7.length === 1 && path7[0] === "feed" && [
21032
+ "transientById",
21033
+ "feedbackUsageByJob",
21034
+ "revision",
21035
+ "lastTransientOrdinal"
21036
+ ].includes(key)) {
21037
+ continue;
21038
+ }
21039
+ if (path7.length === 3 && path7[0] === "jobs" && path7[1] === "byId" && [
21040
+ "actionTrace",
21041
+ "actionSummary",
21042
+ "actionSummaryText",
21043
+ "lastActionAt"
21044
+ ].includes(key)) {
21045
+ continue;
21046
+ }
21047
+ output[key] = stripPresentation(entry, [...path7, key]);
21048
+ }
21049
+ return output;
21050
+ };
21051
+ return stableDocumentFingerprint(stripPresentation(value, []));
21052
+ }
21009
21053
  function debugWs(...args) {
21010
21054
  if (DEBUG_WS) {
21011
21055
  console.log(...args);
@@ -21323,6 +21367,7 @@ var WSClient = class {
21323
21367
  }
21324
21368
  this.lastInboundActivityAt = Date.now();
21325
21369
  this.reconnectAttempts = 0;
21370
+ this.connectPromise = null;
21326
21371
  this.emit("open", {});
21327
21372
  finish();
21328
21373
  };
@@ -21485,7 +21530,7 @@ var WSClient = class {
21485
21530
  this.canonicalDocumentActivated = true;
21486
21531
  }
21487
21532
  }
21488
- canAcceptDocumentReplacement(replacement, strictlyNewer) {
21533
+ canAcceptDocumentReplacement(replacement, strictlyNewer, options = {}) {
21489
21534
  const replacementCanonical = isCanonicalSessionFeedDocument(replacement);
21490
21535
  const currentCanonical = isCanonicalSessionFeedDocument(this.doc);
21491
21536
  if (replacementCanonical) {
@@ -21534,7 +21579,7 @@ var WSClient = class {
21534
21579
  debugWs(`[Granular DEBUG] Rejected session snapshot: ${canonicalError}`);
21535
21580
  return false;
21536
21581
  }
21537
- if (this.canonicalDocumentActivated && incoming.epoch === current.epoch && incoming.revision === current.revision && !readSessionFeedSnapshot(this.doc).error && stableDocumentFingerprint(Automerge__namespace.toJS(replacement)) !== stableDocumentFingerprint(Automerge__namespace.toJS(this.doc))) {
21582
+ if (this.canonicalDocumentActivated && incoming.epoch === current.epoch && incoming.revision === current.revision && !readSessionFeedSnapshot(this.doc).error && (options.allowEqualRevisionPresentation ? canonicalDocumentFingerprint(Automerge__namespace.toJS(replacement)) : stableDocumentFingerprint(Automerge__namespace.toJS(replacement))) !== (options.allowEqualRevisionPresentation ? canonicalDocumentFingerprint(Automerge__namespace.toJS(this.doc)) : stableDocumentFingerprint(Automerge__namespace.toJS(this.doc)))) {
21538
21583
  debugWs(
21539
21584
  "[Granular DEBUG] Rejected divergent canonical snapshot at the accepted document version."
21540
21585
  );
@@ -21656,6 +21701,7 @@ var WSClient = class {
21656
21701
  reconnectScheduled: false
21657
21702
  };
21658
21703
  this.ws = null;
21704
+ this.connectPromise = null;
21659
21705
  this.emit("close", info2);
21660
21706
  if (this.reconnectTimer) {
21661
21707
  clearTimeout(this.reconnectTimer);
@@ -21693,13 +21739,89 @@ var WSClient = class {
21693
21739
  );
21694
21740
  return;
21695
21741
  }
21742
+ if ("type" in message && message.type === "job_action_trace") {
21743
+ const update = message;
21744
+ if (typeof update.jobId !== "string" || update.jobId.length === 0 || !Number.isSafeInteger(update.documentEpoch) || update.documentEpoch < 0 || !Number.isSafeInteger(update.documentRevision) || update.documentRevision < 0 || !Array.isArray(update.actionTrace) || update.actionTrace.length > 128 || !update.actionTrace.every(
21745
+ (event) => event && typeof event === "object" && typeof event.id === "string" && typeof event.timestamp === "number" && Number.isFinite(event.timestamp)
21746
+ ) || !Array.isArray(update.actionSummary) || update.actionSummary.length > 16 || !update.actionSummary.every((line) => typeof line === "string") || typeof update.actionSummaryText !== "string" || update.actionSummaryText.length > 8e3) {
21747
+ console.warn("[Granular] Ignoring invalid job_action_trace message");
21748
+ return;
21749
+ }
21750
+ const currentVersion = documentVersion(this.doc);
21751
+ if (update.documentEpoch < currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision < currentVersion.revision) {
21752
+ return;
21753
+ }
21754
+ if (update.documentEpoch > currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision > currentVersion.revision) {
21755
+ return;
21756
+ }
21757
+ const current = Automerge__namespace.toJS(this.doc);
21758
+ const job2 = current.jobs?.byId?.[update.jobId];
21759
+ if (job2 && typeof job2 === "object" && !Array.isArray(job2)) {
21760
+ job2.actionTrace = update.actionTrace;
21761
+ job2.actionSummary = update.actionSummary;
21762
+ job2.actionSummaryText = update.actionSummaryText;
21763
+ const lastEvent = update.actionTrace[update.actionTrace.length - 1];
21764
+ if (lastEvent && typeof lastEvent.timestamp === "number") {
21765
+ job2.lastActionAt = lastEvent.timestamp;
21766
+ }
21767
+ this.doc = Automerge__namespace.from(current);
21768
+ this.emit("job_action_trace", update);
21769
+ this.emit("sync", this.doc);
21770
+ }
21771
+ return;
21772
+ }
21773
+ if ("type" in message && message.type === "feed_transient_patch") {
21774
+ const update = message;
21775
+ if (!Number.isSafeInteger(update.documentEpoch) || update.documentEpoch < 0 || !Number.isSafeInteger(update.documentRevision) || update.documentRevision < 0 || !Number.isSafeInteger(update.feedRevision) || update.feedRevision < 0 || !Number.isSafeInteger(update.lastTransientOrdinal) || update.lastTransientOrdinal < 0 || !Array.isArray(update.changes) || update.changes.length > 128 || !update.changes.every((change) => {
21776
+ if (!change || typeof change !== "object" || typeof change.transientId !== "string" || change.transientId.length === 0) {
21777
+ return false;
21778
+ }
21779
+ if (change.removed === true) return change.item === void 0;
21780
+ const item = change.item;
21781
+ return Boolean(
21782
+ item && typeof item === "object" && !Array.isArray(item) && item.id === change.transientId
21783
+ );
21784
+ })) {
21785
+ console.warn("[Granular] Ignoring invalid feed_transient_patch");
21786
+ return;
21787
+ }
21788
+ const currentVersion = documentVersion(this.doc);
21789
+ if (update.documentEpoch > currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision > currentVersion.revision || update.documentEpoch < currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision < currentVersion.revision) {
21790
+ return;
21791
+ }
21792
+ const current = Automerge__namespace.toJS(this.doc);
21793
+ const feed = current.feed;
21794
+ if (!feed || typeof feed !== "object" || Array.isArray(feed)) return;
21795
+ if (Number.isSafeInteger(feed.revision) && feed.revision > update.feedRevision) {
21796
+ return;
21797
+ }
21798
+ if (Number.isSafeInteger(feed.lastTransientOrdinal) && feed.lastTransientOrdinal > update.lastTransientOrdinal) {
21799
+ return;
21800
+ }
21801
+ if (!feed.transientById || typeof feed.transientById !== "object") {
21802
+ feed.transientById = {};
21803
+ }
21804
+ for (const change of update.changes) {
21805
+ if (change.removed === true) {
21806
+ delete feed.transientById[change.transientId];
21807
+ } else {
21808
+ feed.transientById[change.transientId] = change.item;
21809
+ }
21810
+ }
21811
+ feed.revision = update.feedRevision;
21812
+ feed.lastTransientOrdinal = update.lastTransientOrdinal;
21813
+ this.doc = Automerge__namespace.from(current);
21814
+ this.emit("feed_transient_patch", update);
21815
+ this.emit("sync", this.doc);
21816
+ return;
21817
+ }
21696
21818
  if ("type" in message && message.type === "snapshot_reset") {
21697
21819
  const resetMessage = message;
21698
21820
  try {
21699
- if (!Number.isSafeInteger(resetMessage.documentEpoch) || resetMessage.documentEpoch <= 0 || !Number.isSafeInteger(resetMessage.documentRevision) || resetMessage.documentRevision < 0 || !Array.isArray(resetMessage.data)) {
21821
+ if (!Number.isSafeInteger(resetMessage.documentEpoch) || resetMessage.documentEpoch <= 0 || !Number.isSafeInteger(resetMessage.documentRevision) || resetMessage.documentRevision < 0 || !Array.isArray(resetMessage.data) && (!resetMessage.document || typeof resetMessage.document !== "object" || Array.isArray(resetMessage.document))) {
21700
21822
  throw new Error("snapshot_reset metadata is invalid");
21701
21823
  }
21702
- const replacement = Automerge__namespace.load(
21824
+ const replacement = resetMessage.document ? Automerge__namespace.from(resetMessage.document) : Automerge__namespace.load(
21703
21825
  this.decodeDocumentBytes(
21704
21826
  resetMessage.data,
21705
21827
  "Automerge snapshot_reset"
@@ -21713,7 +21835,10 @@ var WSClient = class {
21713
21835
  "snapshot_reset metadata does not match the saved document"
21714
21836
  );
21715
21837
  }
21716
- if (!this.canAcceptDocumentReplacement(replacement, true)) {
21838
+ const isJsonReplacement = Boolean(resetMessage.document);
21839
+ if (!this.canAcceptDocumentReplacement(replacement, !isJsonReplacement, {
21840
+ allowEqualRevisionPresentation: isJsonReplacement
21841
+ })) {
21717
21842
  if (this.documentRepairRequired) {
21718
21843
  this.requireDocumentResync(
21719
21844
  "Stale Automerge snapshot_reset during document repair",
@@ -23907,6 +24032,11 @@ function validateProjectionResult(declaration, projection) {
23907
24032
  "projection.source.version"
23908
24033
  );
23909
24034
  }
24035
+ if (projection.source.versionScope !== void 0 && projection.source.versionScope !== "global" && projection.source.versionScope !== "target") {
24036
+ throw new Error(
24037
+ "projection.source.versionScope must be 'global' or 'target'"
24038
+ );
24039
+ }
23910
24040
  if (!Array.isArray(projection.changes)) {
23911
24041
  throw new Error("projection.changes must be an array");
23912
24042
  }
@@ -24823,6 +24953,104 @@ function buildEffectMetamodelMutations(toolPath, spec) {
24823
24953
  );
24824
24954
  }
24825
24955
 
24956
+ // src/artifact-workflow.ts
24957
+ var TERMINAL_WORKFLOW_STEP_STATUSES = /* @__PURE__ */ new Set([
24958
+ "completed",
24959
+ "skipped",
24960
+ "canceled"
24961
+ ]);
24962
+ function isArtifactWorkflowNodeTerminal(nodeOrStatus) {
24963
+ const status = typeof nodeOrStatus === "string" ? nodeOrStatus : nodeOrStatus.status;
24964
+ return TERMINAL_WORKFLOW_STEP_STATUSES.has(status);
24965
+ }
24966
+ function isNavigationOnlyOperation(workflow, node) {
24967
+ const hasExplicitTransitionLeaf = getArtifactWorkflowNodes(workflow).some(
24968
+ (candidate) => candidate.childIds.length === 0 && candidate.kind === "transition"
24969
+ );
24970
+ return hasExplicitTransitionLeaf && node.childIds.length === 0 && node.kind === "operation" && // A terminal operation is redundant once authored state-path leaves are
24971
+ // available. While it is still delivering/synchronizing, however, it is
24972
+ // the only honest representation of the work still in progress.
24973
+ isArtifactWorkflowNodeTerminal(node);
24974
+ }
24975
+ function isArtifactWorkflowNodeNavigableLeaf(workflow, node) {
24976
+ return node.childIds.length === 0 && !isNavigationOnlyOperation(workflow, node);
24977
+ }
24978
+ function isArtifactWorkflowNodeActionable(node) {
24979
+ return node.availability === "available" && ["current", "needs_input", "working", "blocked", "failed"].includes(
24980
+ node.status
24981
+ );
24982
+ }
24983
+ function getArtifactWorkflowNodes(workflow) {
24984
+ const visit = (id) => {
24985
+ const node = workflow.nodesById[id];
24986
+ return node ? [node, ...node.childIds.flatMap(visit)] : [];
24987
+ };
24988
+ return workflow.rootNodeIds.flatMap(visit);
24989
+ }
24990
+ function assertArtifactWorkflow(workflow) {
24991
+ if (workflow.schemaVersion !== 1)
24992
+ throw new Error("Unsupported artifact workflow schema");
24993
+ if (!Number.isSafeInteger(workflow.revision) || workflow.revision < 1) {
24994
+ throw new Error("Artifact workflow revision must be a positive integer");
24995
+ }
24996
+ const visited = /* @__PURE__ */ new Set();
24997
+ const visiting = /* @__PURE__ */ new Set();
24998
+ const nodes = [];
24999
+ const visit = (id, expectedParentId) => {
25000
+ const node = workflow.nodesById[id];
25001
+ if (!node) throw new Error(`Artifact workflow node ${id} does not exist`);
25002
+ if (visiting.has(id)) throw new Error("Artifact workflow contains a cycle");
25003
+ if (visited.has(id))
25004
+ throw new Error("Artifact workflow contains a duplicate node reference");
25005
+ if (node.parentId !== expectedParentId) {
25006
+ throw new Error(`Artifact workflow node ${id} has an invalid parent`);
25007
+ }
25008
+ visiting.add(id);
25009
+ nodes.push(node);
25010
+ node.childIds.forEach((childId) => visit(childId, id));
25011
+ visiting.delete(id);
25012
+ visited.add(id);
25013
+ };
25014
+ workflow.rootNodeIds.forEach((id) => visit(id, null));
25015
+ if (visited.size !== Object.keys(workflow.nodesById).length) {
25016
+ throw new Error("Artifact workflow contains unreachable nodes");
25017
+ }
25018
+ if (workflow.currentNodeId && !workflow.nodesById[workflow.currentNodeId]) {
25019
+ throw new Error("Artifact workflow current node does not exist");
25020
+ }
25021
+ const currentNode = workflow.currentNodeId ? workflow.nodesById[workflow.currentNodeId] : null;
25022
+ if (currentNode && currentNode.childIds.length > 0) {
25023
+ throw new Error("Artifact workflow current node must be actionable leaf");
25024
+ }
25025
+ const actionable = nodes.filter(
25026
+ (node) => isArtifactWorkflowNodeNavigableLeaf(workflow, node) && isArtifactWorkflowNodeActionable(node)
25027
+ );
25028
+ if (actionable.length > 1)
25029
+ throw new Error("Artifact workflow has multiple current nodes");
25030
+ if (actionable.length === 1 && actionable[0].id !== workflow.currentNodeId) {
25031
+ throw new Error("Artifact workflow current node does not own its action");
25032
+ }
25033
+ if (workflow.currentNodeId && !actionable.length && !isArtifactWorkflowNodeTerminal(currentNode)) {
25034
+ throw new Error("Artifact workflow current node is not actionable");
25035
+ }
25036
+ }
25037
+ function requireArtifactWorkflow(artifact) {
25038
+ if (!artifact.workflow) {
25039
+ throw new Error(
25040
+ `Artifact ${artifact.artifactId} has no canonical workflow`
25041
+ );
25042
+ }
25043
+ assertArtifactWorkflow(artifact.workflow);
25044
+ if (artifact.workflow.artifactId !== artifact.artifactId) {
25045
+ throw new Error("Artifact workflow belongs to a different artifact");
25046
+ }
25047
+ return artifact;
25048
+ }
25049
+ function requireArtifactWorkflowResult(result) {
25050
+ requireArtifactWorkflow(result.artifact);
25051
+ return result;
25052
+ }
25053
+
24826
25054
  // src/client.ts
24827
25055
  var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
24828
25056
  var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
@@ -26666,6 +26894,7 @@ var EnvironmentSession = class extends Session {
26666
26894
  sessionDataHeaders;
26667
26895
  beforeMutationSend;
26668
26896
  heartbeatTimer = null;
26897
+ heartbeatProbeInFlight = false;
26669
26898
  /** The last known graph container status, updated by checkReadiness(). */
26670
26899
  graphContainerStatus = null;
26671
26900
  constructor(client, environment, clientId, options = {}) {
@@ -26685,16 +26914,34 @@ var EnvironmentSession = class extends Session {
26685
26914
  * an application RPC may be delayed by Durable Object work even while the
26686
26915
  * underlying WebSocket is healthy.
26687
26916
  */
26688
- startHeartbeat() {
26917
+ startHeartbeat(options = {}) {
26689
26918
  if (this.heartbeatTimer) return;
26690
- this.heartbeatTimer = setInterval(() => {
26691
- if (!this.client.isTransportOpen()) return;
26692
- if (this.client.hasRecentInboundActivity(3e4)) return;
26693
- if (this.client.hasRecentlyPendingRpcPeriod(6e4)) return;
26694
- this.client.reportTransportFailure(
26695
- new Error("No server activity received for 30 seconds")
26696
- );
26697
- }, 5e3);
26919
+ const probeServer = options.probeServer === true;
26920
+ this.heartbeatTimer = setInterval(
26921
+ () => {
26922
+ if (!this.client.isTransportOpen()) return;
26923
+ if (probeServer) {
26924
+ if (this.heartbeatProbeInFlight) return;
26925
+ if (this.client.hasPendingRpcRequests()) return;
26926
+ if (this.client.hasRecentInboundActivity(1e4)) return;
26927
+ this.heartbeatProbeInFlight = true;
26928
+ void this.client.call("client.heartbeat", {}).catch((error2) => {
26929
+ if (this.client.isTransportOpen()) {
26930
+ this.client.reportTransportFailure(error2);
26931
+ }
26932
+ }).finally(() => {
26933
+ this.heartbeatProbeInFlight = false;
26934
+ });
26935
+ return;
26936
+ }
26937
+ if (this.client.hasRecentInboundActivity(3e4)) return;
26938
+ if (this.client.hasRecentlyPendingRpcPeriod(6e4)) return;
26939
+ this.client.reportTransportFailure(
26940
+ new Error("No server activity received for 30 seconds")
26941
+ );
26942
+ },
26943
+ probeServer ? 1e4 : 5e3
26944
+ );
26698
26945
  this.heartbeatTimer.unref?.();
26699
26946
  }
26700
26947
  stopHeartbeat() {
@@ -26702,10 +26949,15 @@ var EnvironmentSession = class extends Session {
26702
26949
  clearInterval(this.heartbeatTimer);
26703
26950
  this.heartbeatTimer = null;
26704
26951
  }
26952
+ this.heartbeatProbeInFlight = false;
26705
26953
  }
26706
26954
  async hello() {
26707
26955
  const result = await super.hello();
26708
- this.startHeartbeat();
26956
+ if (result.features?.serverHeartbeat === false) {
26957
+ this.startHeartbeat({ probeServer: true });
26958
+ } else {
26959
+ this.startHeartbeat();
26960
+ }
26709
26961
  return result;
26710
26962
  }
26711
26963
  get environmentId() {
@@ -26891,7 +27143,10 @@ var EnvironmentSession = class extends Session {
26891
27143
  queryOptions.targetId = options.target.id;
26892
27144
  delete queryOptions.target;
26893
27145
  }
26894
- return this.sessionDataRequest("/artifacts", queryOptions);
27146
+ return this.sessionDataRequest("/artifacts", queryOptions).then((page) => ({
27147
+ ...page,
27148
+ items: page.items.map(requireArtifactWorkflow)
27149
+ }));
26895
27150
  },
26896
27151
  listForLatestJob: (options = {}) => this.artifacts.list({
26897
27152
  ...options,
@@ -26904,7 +27159,7 @@ var EnvironmentSession = class extends Session {
26904
27159
  // `cache: no-store`; this also bypasses intermediaries that ignore
26905
27160
  // the Fetch cache directive for an otherwise identical GET URL.
26906
27161
  { _granularLiveRead: Date.now() }
26907
- ),
27162
+ ).then(requireArtifactWorkflow),
26908
27163
  create: (artifact) => this.sessionDataRequest(
26909
27164
  "/artifacts",
26910
27165
  void 0,
@@ -26912,7 +27167,7 @@ var EnvironmentSession = class extends Session {
26912
27167
  method: "POST",
26913
27168
  body: artifact
26914
27169
  }
26915
- ),
27170
+ ).then(requireArtifactWorkflow),
26916
27171
  acceptSuggestion: (sequence) => this.sessionDataRequest(
26917
27172
  "/artifacts/suggestions/accept",
26918
27173
  void 0,
@@ -26920,7 +27175,7 @@ var EnvironmentSession = class extends Session {
26920
27175
  method: "POST",
26921
27176
  body: { sequence }
26922
27177
  }
26923
- ),
27178
+ ).then(requireArtifactWorkflow),
26924
27179
  updateInputs: (artifactId, patch) => this.sessionDataRequest(
26925
27180
  `/artifacts/${encodeURIComponent(artifactId)}`,
26926
27181
  void 0,
@@ -26928,7 +27183,7 @@ var EnvironmentSession = class extends Session {
26928
27183
  method: "PATCH",
26929
27184
  body: patch
26930
27185
  }
26931
- ),
27186
+ ).then(requireArtifactWorkflow),
26932
27187
  relationshipOptions: (artifactId, input) => this.sessionDataRequest(
26933
27188
  `/artifacts/${encodeURIComponent(artifactId)}/relationship-options`,
26934
27189
  void 0,
@@ -26938,27 +27193,27 @@ var EnvironmentSession = class extends Session {
26938
27193
  `/artifacts/${encodeURIComponent(artifactId)}/relationship-create`,
26939
27194
  void 0,
26940
27195
  { method: "POST", body: input }
26941
- ),
27196
+ ).then(requireArtifactWorkflow),
26942
27197
  validate: (artifactId) => this.sessionDataRequest(
26943
27198
  `/artifacts/${encodeURIComponent(artifactId)}/validate`,
26944
27199
  void 0,
26945
27200
  { method: "POST" }
26946
- ),
27201
+ ).then(requireArtifactWorkflowResult),
26947
27202
  execute: (artifactId, options) => this.sessionDataRequest(
26948
27203
  `/artifacts/${encodeURIComponent(artifactId)}/execute`,
26949
27204
  void 0,
26950
27205
  { method: "POST", body: options }
26951
- ),
27206
+ ).then(requireArtifactWorkflowResult),
26952
27207
  approve: (artifactId, options) => this.sessionDataRequest(
26953
27208
  `/artifacts/${encodeURIComponent(artifactId)}/approve`,
26954
27209
  void 0,
26955
27210
  { method: "POST", body: options }
26956
- ),
27211
+ ).then(requireArtifactWorkflowResult),
26957
27212
  cancel: (artifactId) => this.sessionDataRequest(
26958
27213
  `/artifacts/${encodeURIComponent(artifactId)}/cancel`,
26959
27214
  void 0,
26960
27215
  { method: "POST" }
26961
- )
27216
+ ).then(requireArtifactWorkflow)
26962
27217
  };
26963
27218
  }
26964
27219
  get manualActions() {
@@ -1,4 +1,4 @@
1
- import { dh as WSClientOptions, a7 as SessionFeedApi, bb as GranularQuotaProgress, T as ToolWithHandler, q as PublishToolsResult, ch as Job, r as ToolHandler, I as InstanceToolHandler, ck as UserMessageInput, cl as UserMessageAppendResult, c4 as EffectInfo, n as Prompt, c3 as ToolInfo, c6 as EffectsChangedEvent, c5 as ToolsChangedEvent, D as DomainState, aa as FeedListTransport, aT as GranularOptions, dO as EnvironmentImporter, aW as RecordUserOptions, aV as User, aY as OpenEnvironmentOptions, E as EnvironmentData, aS as AccessTokenProvider, bl as BuildPolicy, dL as EnvironmentSetupSummary, b2 as ConversationSessionListOptions, b0 as ConversationSessionInfo, b3 as ConversationSessionListResponse, a$ as CreateSessionOptions, h as RecordObjectOptions, dy as RecordObjectResult, dA as RecordObjectsOptions, dH as RecordImport, dD as RecordImportStatus, dG as RecordImportItem, a as EnvironmentRecordImportSummary, cf as EnvironmentFeedbackRecord, m as SessionHeapSnapshot, d5 as SessionDocumentResult, cm as AssistantReplyPublicationInput, cn as AssistantReplyPublicationResult, d6 as SessionCollectionListOptions, d8 as SessionCollectionListResult, cq as SessionTimelineEvent, d7 as SessionJobListOptions, c_ as SessionJobRecord, bP as SessionMutationView, cB as SessionArtifactListOptions, cA as SessionArtifactRecord, cD as SessionArtifactRelationshipOptionsInput, cF as SessionArtifactRelationshipOptionsResult, cG as SessionArtifactRelationshipCreateInput, cC as SessionArtifactValidationResult, cI as SessionArtifactExecutionOptions, cH as SessionArtifactExecutionResult, cJ as SessionArtifactApprovalOptions, cT as RecordManualActionInput, cV as ManualActionRecordResult, cW as ManualActionListOptions, cU as ManualActionOccurrence, cY as ManualActionSuggestionOptions, cX as ManualActionSuggestion, cM as ArtifactApprovalTaskListOptions, cL as ArtifactApprovalTask, cN as ArtifactApprovalDecisionInput, cO as ArtifactApprovalDecisionResult, cu as SessionFileRecord, cZ as SessionFileUploadOptions, S as SessionHeapEntry, k as SessionHeapList, d1 as SessionHeapVariable, o as SessionTranscriptEntry, eg as GraphQLResult, d3 as RecordSearchOptions, d2 as RecordSearchResult, d4 as RecordMentionInput, dr as DefineRelationshipOptions, dq as RelationshipInfo, dp as ModelRef, i as ManifestContent, dv as EnvironmentStateUpdateInput, dt as EnvironmentStateTarget, dx as EnvironmentStateProxy, dC as RecordImportOptions, dd as UserEnvironmentStateOptions, dc as UserEnvironmentState, de as MarkUserEnvironmentReadOptions, d as ProjectionMapper, bI as EffectProjectionResult, bN as ExternalCommitOptions, C as CommitReceipt, bJ as TransitionProjectionResult, bO as ObservationReceipt, aZ as AdoptEnvironmentOptions, a_ as ConnectOptions, dJ as RunEnvironmentImporterOptions, aF as OpenAIUsageSpendEvent, aE as GranularSpendContext, aH as RecordOpenAIUsageSpendResult, be as SandboxListResponse, bc as Sandbox, bd as CreateSandboxData, ei as DeleteResponse, bg as PermissionProfile, bh as CreatePermissionProfileData, bo as CreateEnvironmentData, ej as StreamEvent, ek as StreamSubscription, el as StreamStats, aX as Subject, bk as AssignmentListResponse } from './spend-B-IQmp7F.js';
1
+ import { du as WSClientOptions, ab as SessionFeedApi, bf as GranularQuotaProgress, T as ToolWithHandler, t as PublishToolsResult, cl as Job, u as ToolHandler, I as InstanceToolHandler, co as UserMessageInput, cp as UserMessageAppendResult, c8 as EffectInfo, n as Prompt, c7 as ToolInfo, ca as EffectsChangedEvent, c9 as ToolsChangedEvent, D as DomainState, ae as FeedListTransport, aX as GranularOptions, e0 as EnvironmentImporter, a_ as RecordUserOptions, aZ as User, b0 as OpenEnvironmentOptions, E as EnvironmentData, aW as AccessTokenProvider, bp as BuildPolicy, dZ as EnvironmentSetupSummary, b6 as ConversationSessionListOptions, b4 as ConversationSessionInfo, b7 as ConversationSessionListResponse, b3 as CreateSessionOptions, h as RecordObjectOptions, dM as RecordObjectResult, dO as RecordObjectsOptions, dV as RecordImport, dR as RecordImportStatus, dU as RecordImportItem, a as EnvironmentRecordImportSummary, cj as EnvironmentFeedbackRecord, m as SessionHeapSnapshot, dh as SessionDocumentResult, cq as AssistantReplyPublicationInput, cr as AssistantReplyPublicationResult, di as SessionCollectionListOptions, dk as SessionCollectionListResult, cu as SessionTimelineEvent, dj as SessionJobListOptions, da as SessionJobRecord, bT as SessionMutationView, cN as SessionArtifactListOptions, r as SessionArtifactRecord, cP as SessionArtifactRelationshipOptionsInput, cR as SessionArtifactRelationshipOptionsResult, cS as SessionArtifactRelationshipCreateInput, cO as SessionArtifactValidationResult, cU as SessionArtifactExecutionOptions, cT as SessionArtifactExecutionResult, cV as SessionArtifactApprovalOptions, d3 as RecordManualActionInput, d5 as ManualActionRecordResult, d6 as ManualActionListOptions, d4 as ManualActionOccurrence, d8 as ManualActionSuggestionOptions, d7 as ManualActionSuggestion, cY as ArtifactApprovalTaskListOptions, cX as ArtifactApprovalTask, cZ as ArtifactApprovalDecisionInput, c_ as ArtifactApprovalDecisionResult, cy as SessionFileRecord, d9 as SessionFileUploadOptions, S as SessionHeapEntry, k as SessionHeapList, dd as SessionHeapVariable, o as SessionTranscriptEntry, eu as GraphQLResult, df as RecordSearchOptions, de as RecordSearchResult, dg as RecordMentionInput, dF as DefineRelationshipOptions, dE as RelationshipInfo, dD as ModelRef, i as ManifestContent, dJ as EnvironmentStateUpdateInput, dH as EnvironmentStateTarget, dL as EnvironmentStateProxy, dQ as RecordImportOptions, dq as UserEnvironmentStateOptions, dp as UserEnvironmentState, dr as MarkUserEnvironmentReadOptions, d as ProjectionMapper, bM as EffectProjectionResult, bR as ExternalCommitOptions, C as CommitReceipt, bN as TransitionProjectionResult, bS as ObservationReceipt, b1 as AdoptEnvironmentOptions, b2 as ConnectOptions, dX as RunEnvironmentImporterOptions, aJ as OpenAIUsageSpendEvent, aI as GranularSpendContext, aL as RecordOpenAIUsageSpendResult, bi as SandboxListResponse, bg as Sandbox, bh as CreateSandboxData, ew as DeleteResponse, bk as PermissionProfile, bl as CreatePermissionProfileData, bs as CreateEnvironmentData, ex as StreamEvent, ey as StreamSubscription, ez as StreamStats, a$ as Subject, bo as AssignmentListResponse } from './spend-lJDtuAr6.js';
2
2
  import * as Automerge from '@automerge/automerge';
3
3
  import { Doc } from '@automerge/automerge/slim';
4
4
 
@@ -194,6 +194,12 @@ declare class Session {
194
194
  ok: boolean;
195
195
  environmentId?: string;
196
196
  docId?: string;
197
+ features?: {
198
+ /** Whether the server sends application-level heartbeat frames. */
199
+ serverHeartbeat?: boolean;
200
+ /** Whether the server expects a hibernatable WebSocket lifecycle. */
201
+ hibernatingWebSocket?: boolean;
202
+ };
197
203
  graphContainerStatus?: {
198
204
  lastKeepAliveAt: number;
199
205
  status: "warming" | "hot" | "unknown";
@@ -794,6 +800,7 @@ declare class EnvironmentSession extends Session {
794
800
  private readonly sessionDataHeaders;
795
801
  private readonly beforeMutationSend?;
796
802
  private heartbeatTimer;
803
+ private heartbeatProbeInFlight;
797
804
  /** The last known graph container status, updated by checkReadiness(). */
798
805
  graphContainerStatus: {
799
806
  lastKeepAliveAt: number;
@@ -824,6 +831,10 @@ declare class EnvironmentSession extends Session {
824
831
  ok: boolean;
825
832
  environmentId?: string;
826
833
  docId?: string;
834
+ features?: {
835
+ serverHeartbeat?: boolean;
836
+ hibernatingWebSocket?: boolean;
837
+ };
827
838
  graphContainerStatus?: {
828
839
  lastKeepAliveAt: number;
829
840
  status: "warming" | "hot" | "unknown";
@@ -888,13 +899,22 @@ declare class EnvironmentSession extends Session {
888
899
  get mutations(): {
889
900
  list: (options?: {
890
901
  limit?: number;
902
+ compact?: boolean;
891
903
  }) => Promise<{
892
904
  items: SessionMutationView[];
893
905
  }>;
894
906
  };
895
907
  get artifacts(): {
896
- list: (options?: SessionArtifactListOptions) => Promise<SessionCollectionListResult<SessionArtifactRecord>>;
897
- listForLatestJob: (options?: Omit<SessionArtifactListOptions, "jobId" | "latestJob">) => Promise<SessionCollectionListResult<SessionArtifactRecord>>;
908
+ list: (options?: SessionArtifactListOptions) => Promise<{
909
+ items: SessionArtifactRecord[];
910
+ nextCursor: string | null;
911
+ totalCount: number;
912
+ }>;
913
+ listForLatestJob: (options?: Omit<SessionArtifactListOptions, "jobId" | "latestJob">) => Promise<{
914
+ items: SessionArtifactRecord[];
915
+ nextCursor: string | null;
916
+ totalCount: number;
917
+ }>;
898
918
  get: (artifactId: string) => Promise<SessionArtifactRecord>;
899
919
  create: (artifact: Partial<SessionArtifactRecord> & {
900
920
  label: string;