@opengeni/worker-bundle 2.0.1 → 2.0.3-canary.36079201993001

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 (31) 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/context-compaction.d.ts +10 -2
  5. package/dist/activities/retained-screenshots.d.ts +7 -2
  6. package/dist/activities/sandbox-lease.d.ts +3 -4
  7. package/dist/{activities-control-QA6OLRQY.js → activities-control-IVTL723D.js} +204 -23
  8. package/dist/activities-control-IVTL723D.js.map +1 -0
  9. package/dist/{activities-turn-YVVKCGAK.js → activities-turn-AJHDYNZU.js} +180 -58
  10. package/dist/activities-turn-AJHDYNZU.js.map +1 -0
  11. package/dist/{chunk-M3NVMF2V.js → chunk-JMO5ZUW3.js} +14 -7
  12. package/dist/{chunk-M3NVMF2V.js.map → chunk-JMO5ZUW3.js.map} +1 -1
  13. package/dist/index.js +7 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/workflow-bundle.js +4 -1
  16. package/package.json +19 -19
  17. package/src/activities/agent-turn/compaction-prep.ts +52 -16
  18. package/src/activities/agent-turn/errors.ts +8 -3
  19. package/src/activities/agent-turn/failure-settlement.ts +40 -8
  20. package/src/activities/agent-turn/history.ts +14 -1
  21. package/src/activities/agent-turn/media-artifacts.ts +2 -1
  22. package/src/activities/agent-turn/sandbox-runtime.ts +25 -9
  23. package/src/activities/agent-turn/stream-attempt.ts +13 -2
  24. package/src/activities/context-compaction.ts +49 -14
  25. package/src/activities/knowledge-indexing.ts +133 -6
  26. package/src/activities/retained-screenshots.ts +82 -11
  27. package/src/activities/sandbox-lease.ts +141 -21
  28. package/src/sandbox-resume.ts +6 -0
  29. package/src/workflows/activities.ts +14 -1
  30. package/dist/activities-control-QA6OLRQY.js.map +0 -1
  31. 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,
@@ -7017,7 +7069,8 @@ import {
7017
7069
  import {
7018
7070
  EmptyCompactionSummaryError,
7019
7071
  REMOTE_COMPACTION_V2_IMPLEMENTATION,
7020
- SUMMARY_BUFFER_TOKENS,
7072
+ compactionSummaryOutputTokens,
7073
+ buildSummaryItem,
7021
7074
  buildCompactionReplacementHistory,
7022
7075
  buildRemoteV2ReplacementHistory,
7023
7076
  compactionThresholdTokens,
@@ -7032,7 +7085,7 @@ import {
7032
7085
  } from "@opengeni/runtime";
7033
7086
  import { contextInputBudgetTokens } from "@opengeni/config";
7034
7087
  async function maybeCompactContext(db, settings, scope, lastInputTokens, summarize = (s, m) => summarizeForCompaction(s, m, {
7035
- maxOutputTokens: SUMMARY_BUFFER_TOKENS
7088
+ maxOutputTokens: compactionSummaryOutputTokens(s.contextWindowTokens)
7036
7089
  }), options = {}) {
7037
7090
  if (options.codexCompactionMode === "remote_v2" && options.isCodexSubscriptionTurn !== true) {
7038
7091
  throw new Error(
@@ -7263,12 +7316,23 @@ async function compactContextPortable(db, settings, scope, canonicalItems, items
7263
7316
  const summarized = await summarizeWithCodexOverflowTrimming(summarize, settings, items);
7264
7317
  const summaryBody = summarized.summaryBody;
7265
7318
  const retainedTokens = await retentionTokenCounts(canonicalItems, projectForWire);
7319
+ const outputReserve = compactionSummaryOutputTokens(settings.contextWindowTokens);
7320
+ const structuralBudget = Math.min(
7321
+ contextInputBudgetTokens(settings) || settings.contextWindowTokens - outputReserve,
7322
+ settings.contextWindowTokens - outputReserve
7323
+ );
7324
+ const prefixTokens = Math.max(0, Math.ceil(summarize.estimatePrefixTokens?.() ?? 0));
7325
+ const summaryTokens = estimateTokens([buildSummaryItem(summaryBody)]);
7266
7326
  const replacementHistory = buildCompactionReplacementHistory(
7267
7327
  canonicalItems,
7268
7328
  summaryBody,
7269
- (item) => retainedTokens.get(item) ?? estimateTokens([item])
7329
+ (item) => retainedTokens.get(item) ?? estimateTokens([item]),
7330
+ Math.min(outputReserve, Math.max(0, structuralBudget - prefixTokens - summaryTokens))
7270
7331
  );
7271
7332
  const estimatedTokensAfter = estimateTokens(await projectForWire(replacementHistory));
7333
+ if (estimatedTokensAfter + prefixTokens > structuralBudget) {
7334
+ return await settleSkippedAfterStart(db, scope, options, "replacement_exceeds_model_budget");
7335
+ }
7272
7336
  const replacementFingerprint = compactionReplacementFingerprint(replacementHistory);
7273
7337
  const previousReplacementFingerprint = latestCompactionReplacementFingerprint(canonicalItems);
7274
7338
  const summaryIndex = replacementHistory.length - (readReasoningConfiguration(replacementHistory.at(-1) ?? {}) ? 2 : 1);
@@ -7321,14 +7385,27 @@ async function compactContextPortable(db, settings, scope, canonicalItems, items
7321
7385
  };
7322
7386
  }
7323
7387
  async function summarizeWithCodexOverflowTrimming(summarize, settings, activeHistory) {
7324
- const summaryAwareBudget = Math.max(0, settings.contextWindowTokens - SUMMARY_BUFFER_TOKENS);
7388
+ const summaryAwareBudget = Math.max(
7389
+ 0,
7390
+ settings.contextWindowTokens - compactionSummaryOutputTokens(settings.contextWindowTokens)
7391
+ );
7325
7392
  const configuredInputBudget = contextInputBudgetTokens(settings);
7326
7393
  const structuralBudget = Math.min(
7327
7394
  configuredInputBudget > 0 ? configuredInputBudget : summaryAwareBudget,
7328
7395
  summaryAwareBudget
7329
7396
  );
7330
- const initialBudget = Math.floor(structuralBudget * 0.85);
7397
+ const prefixTokens = Math.max(0, Math.ceil(summarize.estimatePrefixTokens?.() ?? 0));
7398
+ const initialBudget = Math.max(0, structuralBudget - prefixTokens);
7331
7399
  let preparation = prepareCompactionPromptInput(activeHistory, initialBudget);
7400
+ const requireHistory = () => {
7401
+ if (activeHistory.length > 0 && preparation.input.length === 1) {
7402
+ throw new EmptyCompactionSummaryError({
7403
+ stage: "portable_input_budget",
7404
+ reason: "no_history_fit"
7405
+ });
7406
+ }
7407
+ };
7408
+ requireHistory();
7332
7409
  try {
7333
7410
  return {
7334
7411
  summaryBody: await summarize(settings, preparation.input),
@@ -7338,9 +7415,10 @@ async function summarizeWithCodexOverflowTrimming(summarize, settings, activeHis
7338
7415
  } catch (error51) {
7339
7416
  if (!isContextWindowExceeded(error51)) throw error51;
7340
7417
  const retryBudget = Math.floor(
7341
- Math.min(initialBudget * 0.5, preparation.estimatedInputTokens * 0.5)
7418
+ Math.min(initialBudget * 0.4, preparation.estimatedInputTokens * 0.4)
7342
7419
  );
7343
7420
  preparation = prepareCompactionPromptInput(activeHistory, retryBudget);
7421
+ requireHistory();
7344
7422
  return {
7345
7423
  summaryBody: await summarize(settings, preparation.input),
7346
7424
  preparation,
@@ -8695,6 +8773,12 @@ async function runTurnStreamAttempt(deps) {
8695
8773
  currentToolBatchCallIds.add(pendingToolCall.callId);
8696
8774
  if (toolCallProducesRetainableSessionImage(pendingToolCall.callName)) {
8697
8775
  media.retainedSessionImageCallIds.add(pendingToolCall.callId);
8776
+ if (retainableBrowserScreenshotToolCall(pendingToolCall.callName)) {
8777
+ media.retainedSessionImageKindsByCallId.set(
8778
+ pendingToolCall.callId,
8779
+ "browser_screenshot"
8780
+ );
8781
+ }
8698
8782
  }
8699
8783
  }
8700
8784
  const completedToolCall = completedToolCallFromSdkEvent(durableSdkEvent);
@@ -8708,7 +8792,8 @@ async function runTurnStreamAttempt(deps) {
8708
8792
  attemptId: input.attemptId,
8709
8793
  toolCallId: completedToolCall.callId,
8710
8794
  toolOutputId: completedToolCall.callId,
8711
- reason: "unsupported"
8795
+ reason: "unsupported",
8796
+ kind: media.retainedSessionImageKindsByCallId.get(completedToolCall.callId)
8712
8797
  });
8713
8798
  media.retainedScreenshotReceiptsByCallId.set(
8714
8799
  completedToolCall.callId,
@@ -8722,13 +8807,14 @@ async function runTurnStreamAttempt(deps) {
8722
8807
  attemptId: input.attemptId,
8723
8808
  toolCallId: typedScreenshot.callId,
8724
8809
  toolOutputId: typedScreenshot.toolOutputId,
8725
- reason: "pending"
8810
+ reason: "pending",
8811
+ kind: media.retainedSessionImageKindsByCallId.get(completedToolCall.callId)
8726
8812
  });
8727
8813
  media.retainedScreenshotReceiptsByCallId.set(
8728
8814
  completedToolCall.callId,
8729
8815
  retainedScreenshotMetadata
8730
8816
  );
8731
- retainedScreenshotMetadata = await retainComputerScreenshot({
8817
+ retainedScreenshotMetadata = await retainSessionScreenshot({
8732
8818
  db,
8733
8819
  objectStorage,
8734
8820
  accountId: input.accountId,
@@ -8736,7 +8822,8 @@ async function runTurnStreamAttempt(deps) {
8736
8822
  sessionId: input.sessionId,
8737
8823
  turnId: activeTurnId,
8738
8824
  attemptId: input.attemptId,
8739
- output: typedScreenshot
8825
+ output: typedScreenshot,
8826
+ kind: media.retainedSessionImageKindsByCallId.get(completedToolCall.callId)
8740
8827
  });
8741
8828
  media.retainedScreenshotReceiptsByCallId.set(
8742
8829
  completedToolCall.callId,
@@ -8855,6 +8942,7 @@ async function runTurnStreamAttempt(deps) {
8855
8942
  }
8856
8943
  for (const callId of stableToolCallIdsToClear) {
8857
8944
  media.retainedSessionImageCallIds.delete(callId);
8945
+ media.retainedSessionImageKindsByCallId.delete(callId);
8858
8946
  }
8859
8947
  }
8860
8948
  }
@@ -11456,7 +11544,8 @@ import {
11456
11544
  compactionThresholdTokens as compactionThresholdTokens2,
11457
11545
  CompactionNeededError,
11458
11546
  compactionProviderRejection as compactionProviderRejection2,
11459
- SUMMARY_BUFFER_TOKENS as SUMMARY_BUFFER_TOKENS2
11547
+ estimateSerializedValueTokens,
11548
+ compactionSummaryOutputTokens as compactionSummaryOutputTokens2
11460
11549
  } from "@opengeni/runtime";
11461
11550
  async function prepareCompaction(deps) {
11462
11551
  const {
@@ -11495,6 +11584,11 @@ async function prepareCompaction(deps) {
11495
11584
  const remotePrefix = {
11496
11585
  agent: null
11497
11586
  };
11587
+ const portableResponsesNeedsAgentPrefix = resolvedModel?.provider.api === "responses" && !(billingState.isCodexTurn && session.codexCompactionMode === "remote_v2");
11588
+ const preparedPortableRequest = () => {
11589
+ if (!remotePrefix.agent) throw new Error("Compaction agent is unavailable");
11590
+ return preparedCompactionRequest(remotePrefix.agent);
11591
+ };
11498
11592
  const promptCacheKey = acceptsPromptCacheKeyForTurn(resolvedModel) ? input.sessionId : void 0;
11499
11593
  const compactionUsageState = createCompactionModelUsageEventState(claimedModelUsageSourceKeys);
11500
11594
  const recordCompactionUsage = async (usage) => {
@@ -11526,24 +11620,37 @@ async function prepareCompaction(deps) {
11526
11620
  contextContributions: eventing.companyBrainContextContributions
11527
11621
  });
11528
11622
  };
11529
- const compactionSummarizerFor = (systemInstructions) => resolvedModel ? (s, m) => withProviderRequestContext(
11530
- () => summarizeContextForCompaction(s, m, {
11531
- client: resolvedModel.client,
11532
- provider: resolvedModel.provider,
11533
- api: resolvedModel.provider.api,
11623
+ const compactionSummarizerFor = (systemInstructions) => {
11624
+ const summarize = resolvedModel ? (s, m) => withProviderRequestContext(
11625
+ () => summarizeContextForCompaction(s, m, {
11626
+ client: resolvedModel.client,
11627
+ provider: resolvedModel.provider,
11628
+ api: resolvedModel.provider.api,
11629
+ model: turnExecutionPolicy.upstreamModelId,
11630
+ maxOutputTokens: compactionSummaryOutputTokens2(s.contextWindowTokens),
11631
+ ...cancellationSignal ? { signal: cancellationSignal } : {},
11632
+ onUsage: recordCompactionUsage,
11633
+ ...systemInstructions ? { systemInstructions } : {},
11634
+ ...promptCacheKey ? { promptCacheKey } : {},
11635
+ ...portableResponsesNeedsAgentPrefix ? { preparedRequest: preparedPortableRequest() } : {}
11636
+ })
11637
+ ) : (s, m) => summarizeContextForCompaction(s, m, {
11534
11638
  model: turnExecutionPolicy.upstreamModelId,
11535
- maxOutputTokens: SUMMARY_BUFFER_TOKENS2,
11639
+ maxOutputTokens: compactionSummaryOutputTokens2(s.contextWindowTokens),
11640
+ ...cancellationSignal ? { signal: cancellationSignal } : {},
11536
11641
  onUsage: recordCompactionUsage,
11537
11642
  ...systemInstructions ? { systemInstructions } : {},
11538
11643
  ...promptCacheKey ? { promptCacheKey } : {}
11539
- })
11540
- ) : (s, m) => summarizeContextForCompaction(s, m, {
11541
- model: turnExecutionPolicy.upstreamModelId,
11542
- maxOutputTokens: SUMMARY_BUFFER_TOKENS2,
11543
- onUsage: recordCompactionUsage,
11544
- ...systemInstructions ? { systemInstructions } : {},
11545
- ...promptCacheKey ? { promptCacheKey } : {}
11546
- });
11644
+ });
11645
+ summarize.estimatePrefixTokens = () => {
11646
+ if (resolvedModel?.provider.api === "chat") {
11647
+ return estimateSerializedValueTokens(systemInstructions ?? "");
11648
+ }
11649
+ const prepared = portableResponsesNeedsAgentPrefix ? preparedPortableRequest() : null;
11650
+ return estimateSerializedValueTokens(prepared?.systemInstructions ?? systemInstructions ?? "") + (prepared ? estimateSerializedValueTokens(prepared.tools) : 0);
11651
+ };
11652
+ return summarize;
11653
+ };
11547
11654
  const remoteCompactionRequester = resolvedModel && billingState.isCodexTurn ? (s, m) => withCodexRemoteCompaction(async () => {
11548
11655
  if (!remotePrefix.agent) throw new Error("Compaction agent is unavailable");
11549
11656
  const preparedRequest = preparedCompactionRequest(remotePrefix.agent);
@@ -11606,7 +11713,7 @@ async function prepareCompaction(deps) {
11606
11713
  control.activityStatus = "idle";
11607
11714
  return claimedResult({ status: "idle" });
11608
11715
  };
11609
- if (compactionOnlyTurn && !remoteV2CompactionNeedsAgentPrefix) {
11716
+ if (compactionOnlyTurn && !remoteV2CompactionNeedsAgentPrefix && !portableResponsesNeedsAgentPrefix) {
11610
11717
  const compactionInstructions = appendWorkspaceMemory(
11611
11718
  appendSessionInstructions(
11612
11719
  appendWorkspaceGovernance(
@@ -11748,6 +11855,7 @@ async function runPostAgentCompaction(deps) {
11748
11855
  claimedResult,
11749
11856
  turn,
11750
11857
  session,
11858
+ resolvedModel,
11751
11859
  remotePrefix,
11752
11860
  remoteCompactionRequester,
11753
11861
  publishCompactionLiveEvents,
@@ -11761,16 +11869,17 @@ async function runPostAgentCompaction(deps) {
11761
11869
  agent
11762
11870
  } = deps;
11763
11871
  const agentInstructions = typeof agent.instructions === "string" ? agent.instructions : "";
11872
+ const preparedPortable = resolvedModel?.provider.api === "responses" && !(remoteCompactionRequester && session.codexCompactionMode === "remote_v2");
11764
11873
  const compactSummarizer = compactionSummarizerFor(
11765
11874
  agentInstructions.trim() ? agentInstructions : void 0
11766
11875
  );
11767
- if (remoteCompactionRequester) {
11876
+ if (remoteCompactionRequester || preparedPortable) {
11768
11877
  remotePrefix.agent = agent;
11769
11878
  }
11770
11879
  if (compactionOnlyTurn) {
11771
11880
  const requested = await isSessionCompactionRequested2(db, input.workspaceId, input.sessionId);
11772
11881
  let outcome = null;
11773
- if (requested && remoteCompactionRequester && session.codexCompactionMode === "remote_v2") {
11882
+ if (requested && (preparedPortable || remoteCompactionRequester && session.codexCompactionMode === "remote_v2")) {
11774
11883
  queuePreparedCompaction(
11775
11884
  agent,
11776
11885
  new CompactionNeededError({
@@ -11880,7 +11989,7 @@ async function runPostAgentCompaction(deps) {
11880
11989
  control.activityStatus = "idle";
11881
11990
  return { exit: claimedResult({ status: "idle" }) };
11882
11991
  }
11883
- if (remoteCompactionRequester && session.codexCompactionMode === "remote_v2") {
11992
+ if (preparedPortable || remoteCompactionRequester && session.codexCompactionMode === "remote_v2") {
11884
11993
  const forced = await isSessionCompactionRequested2(db, input.workspaceId, input.sessionId);
11885
11994
  const thresholdTokens = compactionThresholdTokens2(eventing.modelRunSettings);
11886
11995
  if (forced || (attempt.triggerType === "user.message" || attempt.triggerType === "system.update.delivered") && (session.lastInputTokens ?? 0) >= thresholdTokens) {
@@ -12004,7 +12113,7 @@ async function runPostAgentCompaction(deps) {
12004
12113
  import {
12005
12114
  advanceWorkspaceGeneration,
12006
12115
  verifyWorkspaceMutationSettlement,
12007
- heartbeatLeaseHolder,
12116
+ heartbeatLeaseHolderStatus,
12008
12117
  readLease as readLease2,
12009
12118
  accrueWarmSeconds,
12010
12119
  SandboxWorkspaceMutationFencedError
@@ -12312,21 +12421,29 @@ function createSandboxTurnRuntime(deps) {
12312
12421
  const heartbeatEpoch = sandbox.leaseEpoch;
12313
12422
  const heartbeatHolderId = sandboxState.sandboxHolderId;
12314
12423
  const heartbeatGroupId = sandboxState.sandboxGroupId;
12315
- const warmRate = sandboxWarmRateMicrosPerSecond(
12424
+ const warmRate = settings.sandboxWarmBillingMode === "usage_only" ? 0 : sandboxWarmRateMicrosPerSecond(
12316
12425
  settings,
12317
12426
  warmBackend ?? sandbox.established.backendId
12318
12427
  );
12319
12428
  sandboxState.leaseHeartbeatTimer = setInterval(() => {
12320
- void heartbeatLeaseHolder(db, {
12429
+ void heartbeatLeaseHolderStatus(db, {
12321
12430
  accountId: input.accountId,
12322
12431
  workspaceId: input.workspaceId,
12323
12432
  sandboxGroupId: heartbeatGroupId,
12324
12433
  kind: "turn",
12325
12434
  holderId: heartbeatHolderId,
12326
12435
  leaseTtlMs: settings.sandboxLeaseTtlMs,
12327
- expectedEpoch: heartbeatEpoch
12328
- }).then(async (alive) => {
12329
- if (alive) return;
12436
+ expectedEpoch: heartbeatEpoch,
12437
+ billingMode: settings.sandboxWarmBillingMode
12438
+ }).then(async (status) => {
12439
+ if (status.fence === "funding") {
12440
+ stopLeaseHeartbeat();
12441
+ sandboxRotationController.abort(
12442
+ new Error("Insufficient OpenGeni credits to extend paid sandbox compute")
12443
+ );
12444
+ return;
12445
+ }
12446
+ if (status.leaseExtended) return;
12330
12447
  const rotation = await beginRotationPreemption(sandbox, heartbeatEpoch, heartbeatGroupId);
12331
12448
  if (rotation === "not_rotating") {
12332
12449
  stopLeaseHeartbeat();
@@ -12338,8 +12455,13 @@ function createSandboxTurnRuntime(deps) {
12338
12455
  sandboxGroupId: heartbeatGroupId,
12339
12456
  expectedEpoch: heartbeatEpoch,
12340
12457
  warmRateMicrosPerSecond: warmRate,
12458
+ billingMode: settings.sandboxWarmBillingMode,
12341
12459
  subjectId: input.sessionId
12342
- }).then((result) => recordCreditMicros(observability, "usage", result.costMicros)).catch(() => void 0);
12460
+ }).then((result) => {
12461
+ if (settings.sandboxWarmBillingMode === "credits") {
12462
+ recordCreditMicros(observability, "usage", result.costMicros);
12463
+ }
12464
+ }).catch(() => void 0);
12343
12465
  const snapshotSession = sandboxState.setupBoxSession;
12344
12466
  const snapshotTurnId = attempt.turnId;
12345
12467
  if (snapshotSession && snapshotTurnId && shouldStartPeriodicWorkspaceSnapshot({
@@ -37533,4 +37655,4 @@ export {
37533
37655
  createTurnActivities,
37534
37656
  createTurnActivitiesFromServices
37535
37657
  };
37536
- //# sourceMappingURL=activities-turn-YVVKCGAK.js.map
37658
+ //# sourceMappingURL=activities-turn-AJHDYNZU.js.map