@opengeni/worker-bundle 2.0.1 → 2.0.3

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.
Files changed (28) hide show
  1. package/dist/activities/agent-turn/failure-settlement.d.ts +1 -1
  2. package/dist/activities/agent-turn/history.d.ts +2 -0
  3. package/dist/activities/agent-turn/media-artifacts.d.ts +5 -3
  4. package/dist/activities/retained-screenshots.d.ts +7 -2
  5. package/dist/activities/sandbox-lease.d.ts +3 -4
  6. package/dist/{activities-control-QA6OLRQY.js → activities-control-IVTL723D.js} +204 -23
  7. package/dist/activities-control-IVTL723D.js.map +1 -0
  8. package/dist/{activities-turn-YVVKCGAK.js → activities-turn-EZEDZTEZ.js} +108 -33
  9. package/dist/activities-turn-EZEDZTEZ.js.map +1 -0
  10. package/dist/{chunk-M3NVMF2V.js → chunk-JMO5ZUW3.js} +14 -7
  11. package/dist/{chunk-M3NVMF2V.js.map → chunk-JMO5ZUW3.js.map} +1 -1
  12. package/dist/index.js +7 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/workflow-bundle.js +4 -1
  15. package/package.json +15 -15
  16. package/src/activities/agent-turn/errors.ts +8 -3
  17. package/src/activities/agent-turn/failure-settlement.ts +40 -8
  18. package/src/activities/agent-turn/history.ts +14 -1
  19. package/src/activities/agent-turn/media-artifacts.ts +2 -1
  20. package/src/activities/agent-turn/sandbox-runtime.ts +25 -9
  21. package/src/activities/agent-turn/stream-attempt.ts +13 -2
  22. package/src/activities/knowledge-indexing.ts +133 -6
  23. package/src/activities/retained-screenshots.ts +82 -11
  24. package/src/activities/sandbox-lease.ts +141 -21
  25. package/src/sandbox-resume.ts +6 -0
  26. package/src/workflows/activities.ts +14 -1
  27. package/dist/activities-control-QA6OLRQY.js.map +0 -1
  28. package/dist/activities-turn-YVVKCGAK.js.map +0 -1
@@ -86,7 +86,7 @@ import {
86
86
  withFirstPartyTools,
87
87
  wrapLazyTurnBoxWithRouting,
88
88
  wrapTurnBoxWithRouting
89
- } from "./chunk-M3NVMF2V.js";
89
+ } from "./chunk-JMO5ZUW3.js";
90
90
  import {
91
91
  StreamTimingMetrics,
92
92
  makeMachineOpObserver,
@@ -1445,11 +1445,13 @@ import {
1445
1445
  COMPUTER_SCREENSHOT_RETENTION_MS,
1446
1446
  COMPUTER_SCREENSHOT_WORKSPACE_QUOTA_BYTES,
1447
1447
  RetainedArtifactMetadataSchema,
1448
- retainedScreenshotReferenceFromFile
1448
+ retainedScreenshotReferenceFromFile,
1449
+ retainedSessionScreenshotKindFromObjectKey
1449
1450
  } from "@opengeni/contracts";
1450
1451
  import {
1451
1452
  RetainedScreenshotQuotaExceededError,
1452
1453
  getRetainedScreenshotArtifact,
1454
+ getRetainedScreenshotArtifactForToolCall,
1453
1455
  isDatabasePersistenceFailure,
1454
1456
  isSessionEventPersistenceError,
1455
1457
  prepareRetainedScreenshotArtifact,
@@ -1554,13 +1556,16 @@ function toolOutputContainsInlineImage(output) {
1554
1556
  return output.some((entry) => {
1555
1557
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) return false;
1556
1558
  const record7 = entry;
1557
- return record7.type === "input_image" && sdkImageSourceContainsInlineImage(record7.image ?? record7.image_url ?? record7.imageUrl) || record7.type === "image" && decodeInlineMcpImage(record7.data, record7.mimeType) !== null;
1559
+ return record7.type === "input_image" && sdkImageSourceContainsInlineImage(record7.image ?? record7.image_url ?? record7.imageUrl) || record7.type === "image" && isDeclaredInlineMcpImage(record7);
1558
1560
  });
1559
1561
  }
1560
1562
  if (!output || typeof output !== "object") return false;
1561
1563
  const record6 = output;
1562
1564
  if (Array.isArray(record6.content)) return toolOutputContainsInlineImage(record6.content);
1563
- return record6.type === "image" && sdkImageSourceContainsInlineImage(record6.image);
1565
+ return record6.type === "image" && (sdkImageSourceContainsInlineImage(record6.image) || isDeclaredInlineMcpImage(record6));
1566
+ }
1567
+ function isDeclaredInlineMcpImage(record6) {
1568
+ return typeof record6.data === "string" && typeof record6.mimeType === "string" && record6.mimeType.toLowerCase().startsWith("image/");
1564
1569
  }
1565
1570
  function decodeInlineMcpImage(data, mimeType) {
1566
1571
  if (typeof data !== "string" || typeof mimeType !== "string" || !["image/png", "image/jpeg", "image/webp"].includes(mimeType)) {
@@ -1798,7 +1803,9 @@ function validateRetainableSessionImage(input, limits = {}) {
1798
1803
  };
1799
1804
  }
1800
1805
  function retainedScreenshotIdentity(input) {
1801
- const settlementKey = createHash3("sha256").update("opengeni:computer-screenshot:v1\0").update(input.sessionId).update("\0").update(input.turnId).update("\0").update(input.attemptId).update("\0").update(input.toolCallId).update("\0").update(input.toolOutputId).digest("hex");
1806
+ const settlementKey = createHash3("sha256").update(
1807
+ input.kind === "browser_screenshot" ? "opengeni:browser-screenshot:v1\0" : "opengeni:computer-screenshot:v1\0"
1808
+ ).update(input.sessionId).update("\0").update(input.turnId).update("\0").update(input.attemptId).update("\0").update(input.toolCallId).update("\0").update(input.toolOutputId).digest("hex");
1802
1809
  const uuidBytes = Uint8Array.from(Buffer.from(settlementKey.slice(0, 32), "hex"));
1803
1810
  uuidBytes[6] = uuidBytes[6] & 15 | 128;
1804
1811
  uuidBytes[8] = uuidBytes[8] & 63 | 128;
@@ -1812,13 +1819,14 @@ function unavailableRetainedSessionImage(input) {
1812
1819
  const identity = retainedScreenshotIdentity(input);
1813
1820
  return unavailable(identity.artifactId, input.reason);
1814
1821
  }
1815
- async function retainComputerScreenshot(input) {
1822
+ async function retainSessionScreenshot(input) {
1816
1823
  const identity = retainedScreenshotIdentity({
1817
1824
  sessionId: input.sessionId,
1818
1825
  turnId: input.turnId,
1819
1826
  attemptId: input.attemptId,
1820
1827
  toolCallId: input.output.callId,
1821
- toolOutputId: input.output.toolOutputId
1828
+ toolOutputId: input.output.toolOutputId,
1829
+ kind: input.kind
1822
1830
  });
1823
1831
  let screenshot;
1824
1832
  try {
@@ -1837,7 +1845,8 @@ async function retainComputerScreenshot(input) {
1837
1845
  const retentionExpiresAt = new Date(
1838
1846
  now.getTime() + (input.retentionMs ?? COMPUTER_SCREENSHOT_RETENTION_MS)
1839
1847
  );
1840
- const objectKey = `workspaces/${input.workspaceId}/files/${identity.artifactId}/retained/session-image.${screenshot.extension}`;
1848
+ const objectName = input.kind === "browser_screenshot" ? "browser-screenshot" : "session-image";
1849
+ const objectKey = `workspaces/${input.workspaceId}/files/${identity.artifactId}/retained/${objectName}.${screenshot.extension}`;
1841
1850
  let prepared;
1842
1851
  try {
1843
1852
  prepared = (await prepareRetainedScreenshotArtifact(input.db, {
@@ -1924,6 +1933,7 @@ async function retainComputerScreenshot(input) {
1924
1933
  return unavailable(identity.artifactId, "pending");
1925
1934
  }
1926
1935
  }
1936
+ var retainComputerScreenshot = retainSessionScreenshot;
1927
1937
  function compactRetainedScreenshotHistory(history, receiptsByCallId) {
1928
1938
  if (receiptsByCallId.size === 0) return history;
1929
1939
  return history.map((item) => {
@@ -2005,6 +2015,20 @@ async function materializeRetainedScreenshotHistory(input) {
2005
2015
  }
2006
2016
  async function materializeRetainedScreenshotHistoryWithCache(input, cache) {
2007
2017
  const now = input.now ?? /* @__PURE__ */ new Date();
2018
+ const receiptForMarker = async (marker, callId) => {
2019
+ if (!isRetainedImageMarker(marker)) return null;
2020
+ const receipt = retainedReceipt(marker.artifact);
2021
+ if (receipt) return receipt;
2022
+ if (!callId) throw new Error("Retained screenshot receipt has no tool call identity");
2023
+ const artifact = await getRetainedScreenshotArtifactForToolCall(
2024
+ input.db,
2025
+ input.workspaceId,
2026
+ input.sessionId,
2027
+ callId
2028
+ );
2029
+ if (!artifact) throw new Error("Retained screenshot receipt cannot be recovered");
2030
+ return reference(artifact);
2031
+ };
2008
2032
  const dataUrlForReceipt = async (receipt) => {
2009
2033
  let dataUrl = cache.get(receipt.artifactId);
2010
2034
  if (!dataUrl) {
@@ -2045,15 +2069,17 @@ async function materializeRetainedScreenshotHistoryWithCache(input, cache) {
2045
2069
  }
2046
2070
  return dataUrl;
2047
2071
  };
2048
- const materializeEntry = async (entry) => {
2049
- const receipt = retainedReceiptFromImageContent(entry);
2072
+ const materializeEntry = async (entry, callId) => {
2073
+ const image = entry && typeof entry === "object" && !Array.isArray(entry) ? entry.image : null;
2074
+ const receipt = await receiptForMarker(image, callId);
2050
2075
  if (!receipt) return entry;
2051
2076
  const dataUrl = await dataUrlForReceipt(receipt);
2052
2077
  return { ...entry, image: dataUrl };
2053
2078
  };
2054
2079
  const materialized = [];
2055
2080
  for (const item of input.history) {
2056
- const directReceipt = retainedReceiptFromDirectOutput(item.output);
2081
+ const callId = historyCallId(item);
2082
+ const directReceipt = await receiptForMarker(item.output, callId);
2057
2083
  if (directReceipt) {
2058
2084
  materialized.push({
2059
2085
  ...item,
@@ -2070,7 +2096,7 @@ async function materializeRetainedScreenshotHistoryWithCache(input, cache) {
2070
2096
  let changed = false;
2071
2097
  const output = [];
2072
2098
  for (const entry of outputEntries) {
2073
- const next = await materializeEntry(entry);
2099
+ const next = await materializeEntry(entry, callId);
2074
2100
  changed ||= next !== entry;
2075
2101
  output.push(next);
2076
2102
  }
@@ -2124,12 +2150,15 @@ function reference(artifact) {
2124
2150
  if (!artifact.sessionId) {
2125
2151
  throw new Error(`Ready retained screenshot is detached: ${artifact.artifactId}`);
2126
2152
  }
2153
+ const kind = retainedSessionScreenshotKindFromObjectKey(artifact.file.objectKey);
2154
+ if (!kind) throw new Error(`Ready retained screenshot has unknown key: ${artifact.artifactId}`);
2127
2155
  const value = retainedScreenshotReferenceFromFile({
2128
2156
  ...artifact.file,
2129
2157
  sessionId: artifact.sessionId,
2130
2158
  width: artifact.width,
2131
2159
  height: artifact.height,
2132
- expiresAt: artifact.retentionExpiresAt.toISOString()
2160
+ expiresAt: artifact.retentionExpiresAt.toISOString(),
2161
+ kind
2133
2162
  });
2134
2163
  if (!value)
2135
2164
  throw new Error(`Ready retained screenshot receipt is invalid: ${artifact.artifactId}`);
@@ -2166,6 +2195,11 @@ function retainedReceiptFromImageContent(entry) {
2166
2195
  if (marker.type !== RETAINED_IMAGE_MARKER) return null;
2167
2196
  return retainedReceipt(marker.artifact);
2168
2197
  }
2198
+ function isRetainedImageMarker(value) {
2199
+ return Boolean(
2200
+ value && typeof value === "object" && !Array.isArray(value) && value.type === RETAINED_IMAGE_MARKER
2201
+ );
2202
+ }
2169
2203
  function retainedReceiptFromDirectOutput(output) {
2170
2204
  if (!output || typeof output !== "object" || Array.isArray(output)) return null;
2171
2205
  const marker = output;
@@ -2175,12 +2209,12 @@ function retainedReceiptFromDirectOutput(output) {
2175
2209
  function retainedReceipt(value) {
2176
2210
  const parsed = RetainedArtifactMetadataSchema.safeParse(value);
2177
2211
  if (!parsed.success) return null;
2178
- return parsed.data.available && parsed.data.kind !== "computer_screenshot" ? null : parsed.data;
2212
+ return parsed.data.available && parsed.data.kind !== "computer_screenshot" && parsed.data.kind !== "browser_screenshot" ? null : parsed.data;
2179
2213
  }
2180
2214
  function isInlineImageContent(entry) {
2181
2215
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) return false;
2182
2216
  const record6 = entry;
2183
- return record6.type === "input_image" && typeof record6.image === "string" && record6.image.startsWith("data:image/") || record6.type === "image" && decodeInlineMcpImage(record6.data, record6.mimeType) !== null;
2217
+ return record6.type === "input_image" && typeof record6.image === "string" && record6.image.startsWith("data:image/") || record6.type === "image" && isDeclaredInlineMcpImage(record6);
2184
2218
  }
2185
2219
  function historyCallId(item) {
2186
2220
  const value = item.callId ?? item.call_id;
@@ -2882,6 +2916,7 @@ var TurnMediaArtifacts = class {
2882
2916
  sandboxFileDownloadBackend;
2883
2917
  nativeImageGenerationRetention = null;
2884
2918
  retainedSessionImageCallIds = /* @__PURE__ */ new Set();
2919
+ retainedSessionImageKindsByCallId = /* @__PURE__ */ new Map();
2885
2920
  rememberGeneratedImageCreatedThisTurn = (receipt) => {
2886
2921
  this.generatedImageReceiptsCreatedThisTurn.set(receipt.artifact.artifactId, receipt);
2887
2922
  };
@@ -3217,7 +3252,10 @@ function pendingToolCallFromSdkEvent(event) {
3217
3252
  };
3218
3253
  }
3219
3254
  function toolCallProducesRetainableSessionImage(name) {
3220
- return name === "computer_screenshot" || name === "view_image" || name === "interaction__computer_observe";
3255
+ return name === "computer_screenshot" || name === "view_image" || name === "interaction__computer_observe" || retainableBrowserScreenshotToolCall(name);
3256
+ }
3257
+ function retainableBrowserScreenshotToolCall(name) {
3258
+ return name === "browser_screenshot" || name === "interaction__browser_screenshot" || name === "browser_observe" || name === "interaction__browser_observe" || name === "browser_act" || name === "interaction__browser_act";
3221
3259
  }
3222
3260
  function completedToolCallFromSdkEvent(event) {
3223
3261
  if (!event || typeof event !== "object") return null;
@@ -5110,7 +5148,8 @@ import {
5110
5148
  } from "@opengeni/db";
5111
5149
  import { publishDurableSessionEvents as publishDurableSessionEvents2 } from "@opengeni/events";
5112
5150
  import { maxTurnsExceededRunState } from "@opengeni/runtime";
5113
- import { CancelledFailure } from "@temporalio/activity";
5151
+ import { ApplicationFailure, CancelledFailure } from "@temporalio/activity";
5152
+ import { TurnExecutionPolicyDefinitionMismatchError } from "@opengeni/config";
5114
5153
  import {
5115
5154
  classifyCodexEncryptedArtifactRejection,
5116
5155
  classifyCodexUsageLimitError,
@@ -6853,10 +6892,11 @@ async function settleTurnFailure(deps) {
6853
6892
  return claimedResult({ status: "recovering" });
6854
6893
  }
6855
6894
  }
6856
- let failure = agentRunFailurePayload(error51, {
6895
+ const earlyDefinitionMismatch = error51 instanceof TurnExecutionPolicyDefinitionMismatchError && !attempt.modelRequestStarted && !eventing.turnStartedPublished && !!attempt.turnId && !!attempt.triggerEventId && attempt.executionGeneration > 0;
6896
+ let failure = earlyDefinitionMismatch ? { error: error51.message, code: error51.code, retryable: true } : agentRunFailurePayload(error51, {
6857
6897
  isCodexTurn: billingState.isCodexTurn
6858
6898
  });
6859
- if (failure.retryable && eventing.publish && attempt.turnId && eventing.turnStartedPublished) {
6899
+ if (attempt.turnId && (earlyDefinitionMismatch || failure.retryable && eventing.publish && eventing.turnStartedPublished)) {
6860
6900
  const nextProviderRecoveryCount = attempt.providerRecoveryCount + 1;
6861
6901
  const recoveryResult = providerRecoveryResult({
6862
6902
  failureCode: failure.code,
@@ -6865,8 +6905,10 @@ async function settleTurnFailure(deps) {
6865
6905
  });
6866
6906
  try {
6867
6907
  if (recoveryResult.status === "recovering") {
6868
- await flushRuntimeBatcher();
6869
- await historySink.reconcileConversationTruth({ requireDurable: true });
6908
+ if (!earlyDefinitionMismatch) {
6909
+ await flushRuntimeBatcher();
6910
+ await historySink.reconcileConversationTruth({ requireDurable: true });
6911
+ }
6870
6912
  const recovery = await requestSessionTurnRecovery(db, input.workspaceId, {
6871
6913
  sessionId: input.sessionId,
6872
6914
  turnId: attempt.turnId,
@@ -6894,6 +6936,16 @@ async function settleTurnFailure(deps) {
6894
6936
  return claimedResult(recoveryResult);
6895
6937
  }
6896
6938
  failure = providerRecoveryExhaustedFailure(failure, recoveryResult);
6939
+ if (earlyDefinitionMismatch) {
6940
+ control.activityStatus = "failed";
6941
+ control.turnMetricOutcome = "failed";
6942
+ control.activityError = error51;
6943
+ throw ApplicationFailure.create({
6944
+ message: `${error51.message}. Automatic same-turn configuration recovery exhausted after ${recoveryResult.providerRecoveryCount} retries.`,
6945
+ type: "TurnExecutionPolicyDefinitionMismatchError",
6946
+ nonRetryable: true
6947
+ });
6948
+ }
6897
6949
  } catch (recoveryError) {
6898
6950
  const escaped = recoveryResult.status === "recovering" ? escapedMcpTimeoutRecoveryFailure({
6899
6951
  failureCode: failure.code,
@@ -8695,6 +8747,12 @@ async function runTurnStreamAttempt(deps) {
8695
8747
  currentToolBatchCallIds.add(pendingToolCall.callId);
8696
8748
  if (toolCallProducesRetainableSessionImage(pendingToolCall.callName)) {
8697
8749
  media.retainedSessionImageCallIds.add(pendingToolCall.callId);
8750
+ if (retainableBrowserScreenshotToolCall(pendingToolCall.callName)) {
8751
+ media.retainedSessionImageKindsByCallId.set(
8752
+ pendingToolCall.callId,
8753
+ "browser_screenshot"
8754
+ );
8755
+ }
8698
8756
  }
8699
8757
  }
8700
8758
  const completedToolCall = completedToolCallFromSdkEvent(durableSdkEvent);
@@ -8708,7 +8766,8 @@ async function runTurnStreamAttempt(deps) {
8708
8766
  attemptId: input.attemptId,
8709
8767
  toolCallId: completedToolCall.callId,
8710
8768
  toolOutputId: completedToolCall.callId,
8711
- reason: "unsupported"
8769
+ reason: "unsupported",
8770
+ kind: media.retainedSessionImageKindsByCallId.get(completedToolCall.callId)
8712
8771
  });
8713
8772
  media.retainedScreenshotReceiptsByCallId.set(
8714
8773
  completedToolCall.callId,
@@ -8722,13 +8781,14 @@ async function runTurnStreamAttempt(deps) {
8722
8781
  attemptId: input.attemptId,
8723
8782
  toolCallId: typedScreenshot.callId,
8724
8783
  toolOutputId: typedScreenshot.toolOutputId,
8725
- reason: "pending"
8784
+ reason: "pending",
8785
+ kind: media.retainedSessionImageKindsByCallId.get(completedToolCall.callId)
8726
8786
  });
8727
8787
  media.retainedScreenshotReceiptsByCallId.set(
8728
8788
  completedToolCall.callId,
8729
8789
  retainedScreenshotMetadata
8730
8790
  );
8731
- retainedScreenshotMetadata = await retainComputerScreenshot({
8791
+ retainedScreenshotMetadata = await retainSessionScreenshot({
8732
8792
  db,
8733
8793
  objectStorage,
8734
8794
  accountId: input.accountId,
@@ -8736,7 +8796,8 @@ async function runTurnStreamAttempt(deps) {
8736
8796
  sessionId: input.sessionId,
8737
8797
  turnId: activeTurnId,
8738
8798
  attemptId: input.attemptId,
8739
- output: typedScreenshot
8799
+ output: typedScreenshot,
8800
+ kind: media.retainedSessionImageKindsByCallId.get(completedToolCall.callId)
8740
8801
  });
8741
8802
  media.retainedScreenshotReceiptsByCallId.set(
8742
8803
  completedToolCall.callId,
@@ -8855,6 +8916,7 @@ async function runTurnStreamAttempt(deps) {
8855
8916
  }
8856
8917
  for (const callId of stableToolCallIdsToClear) {
8857
8918
  media.retainedSessionImageCallIds.delete(callId);
8919
+ media.retainedSessionImageKindsByCallId.delete(callId);
8858
8920
  }
8859
8921
  }
8860
8922
  }
@@ -12004,7 +12066,7 @@ async function runPostAgentCompaction(deps) {
12004
12066
  import {
12005
12067
  advanceWorkspaceGeneration,
12006
12068
  verifyWorkspaceMutationSettlement,
12007
- heartbeatLeaseHolder,
12069
+ heartbeatLeaseHolderStatus,
12008
12070
  readLease as readLease2,
12009
12071
  accrueWarmSeconds,
12010
12072
  SandboxWorkspaceMutationFencedError
@@ -12312,21 +12374,29 @@ function createSandboxTurnRuntime(deps) {
12312
12374
  const heartbeatEpoch = sandbox.leaseEpoch;
12313
12375
  const heartbeatHolderId = sandboxState.sandboxHolderId;
12314
12376
  const heartbeatGroupId = sandboxState.sandboxGroupId;
12315
- const warmRate = sandboxWarmRateMicrosPerSecond(
12377
+ const warmRate = settings.sandboxWarmBillingMode === "usage_only" ? 0 : sandboxWarmRateMicrosPerSecond(
12316
12378
  settings,
12317
12379
  warmBackend ?? sandbox.established.backendId
12318
12380
  );
12319
12381
  sandboxState.leaseHeartbeatTimer = setInterval(() => {
12320
- void heartbeatLeaseHolder(db, {
12382
+ void heartbeatLeaseHolderStatus(db, {
12321
12383
  accountId: input.accountId,
12322
12384
  workspaceId: input.workspaceId,
12323
12385
  sandboxGroupId: heartbeatGroupId,
12324
12386
  kind: "turn",
12325
12387
  holderId: heartbeatHolderId,
12326
12388
  leaseTtlMs: settings.sandboxLeaseTtlMs,
12327
- expectedEpoch: heartbeatEpoch
12328
- }).then(async (alive) => {
12329
- if (alive) return;
12389
+ expectedEpoch: heartbeatEpoch,
12390
+ billingMode: settings.sandboxWarmBillingMode
12391
+ }).then(async (status) => {
12392
+ if (status.fence === "funding") {
12393
+ stopLeaseHeartbeat();
12394
+ sandboxRotationController.abort(
12395
+ new Error("Insufficient OpenGeni credits to extend paid sandbox compute")
12396
+ );
12397
+ return;
12398
+ }
12399
+ if (status.leaseExtended) return;
12330
12400
  const rotation = await beginRotationPreemption(sandbox, heartbeatEpoch, heartbeatGroupId);
12331
12401
  if (rotation === "not_rotating") {
12332
12402
  stopLeaseHeartbeat();
@@ -12338,8 +12408,13 @@ function createSandboxTurnRuntime(deps) {
12338
12408
  sandboxGroupId: heartbeatGroupId,
12339
12409
  expectedEpoch: heartbeatEpoch,
12340
12410
  warmRateMicrosPerSecond: warmRate,
12411
+ billingMode: settings.sandboxWarmBillingMode,
12341
12412
  subjectId: input.sessionId
12342
- }).then((result) => recordCreditMicros(observability, "usage", result.costMicros)).catch(() => void 0);
12413
+ }).then((result) => {
12414
+ if (settings.sandboxWarmBillingMode === "credits") {
12415
+ recordCreditMicros(observability, "usage", result.costMicros);
12416
+ }
12417
+ }).catch(() => void 0);
12343
12418
  const snapshotSession = sandboxState.setupBoxSession;
12344
12419
  const snapshotTurnId = attempt.turnId;
12345
12420
  if (snapshotSession && snapshotTurnId && shouldStartPeriodicWorkspaceSnapshot({
@@ -37533,4 +37608,4 @@ export {
37533
37608
  createTurnActivities,
37534
37609
  createTurnActivitiesFromServices
37535
37610
  };
37536
- //# sourceMappingURL=activities-turn-YVVKCGAK.js.map
37611
+ //# sourceMappingURL=activities-turn-EZEDZTEZ.js.map