@agent-native/core 0.214.0 → 0.217.0

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 (196) hide show
  1. package/dist/action.js +54 -1
  2. package/dist/agent/actions/disconnect-chatgpt-subscription.js +5 -0
  3. package/dist/agent/actions/get-agentkit-capabilities.js +3 -3
  4. package/dist/agent/actions/manage-builder-connection.js +5 -0
  5. package/dist/agent/actions/manage-provider-key-policy.js +5 -0
  6. package/dist/agent/actions/manage-provider-models.js +5 -0
  7. package/dist/agent/actions/manage-service-providers.js +5 -0
  8. package/dist/agent/actions/select-chatgpt-subscription-account.js +5 -0
  9. package/dist/agent/context-xray/actions/context-evict.js +5 -0
  10. package/dist/agent/context-xray/actions/context-pin.js +5 -0
  11. package/dist/agent/context-xray/actions/context-report.js +5 -0
  12. package/dist/agent/context-xray/actions/context-restore.js +5 -0
  13. package/dist/agent/execution-fence.d.ts +1 -0
  14. package/dist/agent/execution-fence.js +5 -0
  15. package/dist/agent/production-agent.d.ts +35 -2
  16. package/dist/agent/production-agent.js +629 -32
  17. package/dist/agent/run-manager.d.ts +2 -0
  18. package/dist/agent/run-manager.js +397 -115
  19. package/dist/agent/run-slot-lock.d.ts +2 -0
  20. package/dist/agent/run-slot-lock.js +6 -0
  21. package/dist/agent/run-store.d.ts +5 -0
  22. package/dist/agent/run-store.js +423 -156
  23. package/dist/agent/thread-data-builder.js +27 -5
  24. package/dist/agent/types.d.ts +6 -12
  25. package/dist/agentkit/capabilities.d.ts +1 -1
  26. package/dist/app-config/analytics.js +1 -1
  27. package/dist/application-state/store.d.ts +5 -0
  28. package/dist/application-state/store.js +26 -0
  29. package/dist/automation/index.d.ts +1 -1
  30. package/dist/chat-threads/schema-migrations.js +17 -0
  31. package/dist/chat-threads/store.d.ts +21 -1
  32. package/dist/chat-threads/store.js +223 -41
  33. package/dist/cli/skills-content/turn-into-app-skill.d.ts +1 -1
  34. package/dist/cli/skills-content/turn-into-app-skill.js +20 -18
  35. package/dist/cli/templates-meta.js +14 -0
  36. package/dist/client/analytics-session.d.ts +7 -0
  37. package/dist/client/analytics-session.js +27 -1
  38. package/dist/client/analytics.js +276 -31
  39. package/dist/client/chat/agentkit-agent-native.js +79 -3
  40. package/dist/client/chat/agentkit-protocol.js +80 -42
  41. package/dist/client/chat/runtime.d.ts +5 -1
  42. package/dist/client/chat/runtime.js +46 -3
  43. package/dist/client/consent.d.ts +48 -0
  44. package/dist/client/consent.js +413 -0
  45. package/dist/client/onboarding/index.d.ts +1 -0
  46. package/dist/client/onboarding/index.js +1 -0
  47. package/dist/client/onboarding/use-onboarding-checklist.d.ts +23 -0
  48. package/dist/client/onboarding/use-onboarding-checklist.js +115 -0
  49. package/dist/client/review/use-review.d.ts +2 -0
  50. package/dist/client/review/use-review.js +28 -1
  51. package/dist/client/session-replay.d.ts +1 -1
  52. package/dist/client/session-replay.js +142 -3
  53. package/dist/client/sharing/useResourceAccessGate.d.ts +3 -2
  54. package/dist/client/sharing/useResourceAccessGate.js +9 -1
  55. package/dist/client/use-chat-thread-list.d.ts +2 -0
  56. package/dist/client/use-chat-thread-list.js +12 -2
  57. package/dist/client/use-chat-threads.d.ts +1 -0
  58. package/dist/client/use-chat-threads.js +2 -0
  59. package/dist/collab/awareness.d.ts +2 -2
  60. package/dist/connections/catalog.d.ts +5 -5
  61. package/dist/connections/catalog.js +22 -3
  62. package/dist/data-widgets/index.d.ts +36 -2
  63. package/dist/data-widgets/index.js +94 -6
  64. package/dist/db/migrations.js +4 -1
  65. package/dist/db/schema-snapshot.generated.js +8 -1
  66. package/dist/feature-flags/actions/set-feature-flag.js +5 -0
  67. package/dist/feature-flags/index.d.ts +1 -1
  68. package/dist/feature-flags/index.js +1 -1
  69. package/dist/feature-flags/store.d.ts +12 -0
  70. package/dist/feature-flags/store.js +79 -7
  71. package/dist/file-upload/actions/manage-file-storage.js +5 -0
  72. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  73. package/dist/file-upload/actions/upload-image.js +5 -0
  74. package/dist/history/actions/create-resource-version.js +5 -0
  75. package/dist/history/actions/restore-resource-version.js +5 -0
  76. package/dist/ingestion/index.d.ts +1 -1
  77. package/dist/ingestion/index.js +1 -1
  78. package/dist/ingestion/pptx.d.ts +11 -0
  79. package/dist/ingestion/pptx.js +81 -1
  80. package/dist/integrations/actions/manage-messaging-channel.js +5 -0
  81. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  82. package/dist/jobs/scheduler.js +17 -1
  83. package/dist/labs/actions/set-lab.js +5 -0
  84. package/dist/localization/actions/set-localization-preference.js +5 -0
  85. package/dist/localization/consent-banner-messages.d.ts +16 -0
  86. package/dist/localization/consent-banner-messages.js +149 -0
  87. package/dist/localization/core-messages.js +4 -0
  88. package/dist/localization/default-messages.d.ts +1 -0
  89. package/dist/localization/default-messages.js +2 -0
  90. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  91. package/dist/notifications/routes.d.ts +2 -2
  92. package/dist/observability/actions/save-observability-instruction-update.js +5 -0
  93. package/dist/observability/actions/save-observability-review-summary.js +5 -0
  94. package/dist/observability/routes.d.ts +6 -6
  95. package/dist/onboarding/index.d.ts +2 -1
  96. package/dist/onboarding/index.js +1 -0
  97. package/dist/onboarding/plugin.js +55 -46
  98. package/dist/onboarding/status.d.ts +15 -0
  99. package/dist/onboarding/status.js +59 -0
  100. package/dist/onboarding/types.d.ts +12 -0
  101. package/dist/onboarding/types.js +2 -1
  102. package/dist/org/workspace-app-access.js +19 -9
  103. package/dist/progress/routes.d.ts +1 -1
  104. package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
  105. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  106. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  107. package/dist/provider-api/index.js +9 -2
  108. package/dist/resources/handlers.d.ts +1 -1
  109. package/dist/review/actions/consume-review-feedback.js +5 -0
  110. package/dist/review/actions/create-review-comment.js +5 -0
  111. package/dist/review/actions/delete-review-comment.js +5 -0
  112. package/dist/review/actions/react-to-review-comment.js +5 -0
  113. package/dist/review/actions/reply-review-comment.js +5 -0
  114. package/dist/review/actions/resolve-review-thread.js +5 -0
  115. package/dist/review/actions/send-review-thread-to-agent.js +5 -0
  116. package/dist/review/actions/set-review-status.js +5 -0
  117. package/dist/review/actions/set-review-thread-muted.js +5 -0
  118. package/dist/review/actions/set-review-thread-unread.js +5 -0
  119. package/dist/review/actions/set-review-threads-unread.js +5 -0
  120. package/dist/review/actions/update-review-comment-anchor.js +5 -0
  121. package/dist/review/actions/update-review-comment.js +5 -0
  122. package/dist/review/suggestions/actions.js +60 -21
  123. package/dist/review/suggestions/store.d.ts +2 -0
  124. package/dist/review/suggestions/store.js +28 -0
  125. package/dist/secrets/actions/delete-api-key.js +5 -0
  126. package/dist/secrets/routes.d.ts +6 -6
  127. package/dist/server/agent-capabilities.js +13 -6
  128. package/dist/server/agent-chat/context-tools.js +63 -2
  129. package/dist/server/agent-chat-plugin.d.ts +2 -0
  130. package/dist/server/agent-chat-plugin.js +242 -54
  131. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  132. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  133. package/dist/server/analytics.d.ts +0 -1
  134. package/dist/server/analytics.js +81 -20
  135. package/dist/server/application-state-maintenance.d.ts +2 -0
  136. package/dist/server/application-state-maintenance.js +2 -0
  137. package/dist/server/attribution.d.ts +14 -0
  138. package/dist/server/attribution.js +62 -2
  139. package/dist/server/auth.js +7 -0
  140. package/dist/server/builder-api-auth.d.ts +7 -0
  141. package/dist/server/builder-api-auth.js +24 -0
  142. package/dist/server/builder-oauth.d.ts +5 -1
  143. package/dist/server/builder-oauth.js +5 -0
  144. package/dist/server/consent-route.d.ts +41 -0
  145. package/dist/server/consent-route.js +102 -0
  146. package/dist/server/core-routes-plugin.d.ts +1 -1
  147. package/dist/server/first-touch-route.d.ts +39 -0
  148. package/dist/server/first-touch-route.js +115 -0
  149. package/dist/server/index.d.ts +2 -1
  150. package/dist/server/index.js +2 -1
  151. package/dist/server/queued-submission-inbox-drain.d.ts +9 -0
  152. package/dist/server/queued-submission-inbox-drain.js +12 -0
  153. package/dist/server/request-origin.d.ts +6 -0
  154. package/dist/server/request-origin.js +12 -0
  155. package/dist/settings/org-settings.d.ts +2 -1
  156. package/dist/settings/org-settings.js +4 -1
  157. package/dist/settings/store.d.ts +8 -0
  158. package/dist/settings/store.js +14 -5
  159. package/dist/shared/consent.d.ts +33 -0
  160. package/dist/shared/consent.js +149 -0
  161. package/dist/shared/first-touch-persistence.d.ts +31 -0
  162. package/dist/shared/first-touch-persistence.js +53 -0
  163. package/dist/sharing/actions/set-resource-visibility.js +5 -0
  164. package/dist/sharing/actions/share-resource.js +5 -0
  165. package/dist/sharing/actions/unshare-resource.js +5 -0
  166. package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
  167. package/dist/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
  168. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
  169. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
  170. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
  171. package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
  172. package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
  173. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
  174. package/dist/triggers/actions/manage-automation.d.ts +4 -4
  175. package/dist/usage/actions/get-usage-metrics.js +27 -4
  176. package/dist/usage/actions/manage-usage-alert.js +5 -0
  177. package/dist/vite/client.js +8 -6
  178. package/dist/workspace-connections/actions/bulk-update-workspace-user-groups.js +5 -0
  179. package/dist/workspace-connections/actions/delete-workspace-user-group.js +5 -0
  180. package/dist/workspace-connections/actions/upsert-workspace-user-group.js +5 -0
  181. package/docs/content/actions-access-control.mdx +8 -8
  182. package/docs/content/actions-defining.mdx +9 -8
  183. package/docs/content/deployment-environment-variables.mdx +1 -1
  184. package/docs/content/environment-variables.mdx +1 -1
  185. package/docs/content/template-design-developers.mdx +9 -9
  186. package/docs/content/template-design-features.mdx +1 -3
  187. package/docs/content/turn-into-app.mdx +4 -0
  188. package/package.json +6 -3
  189. package/src/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
  190. package/src/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
  191. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
  192. package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
  193. package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
  194. package/src/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
  195. package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
  196. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
@@ -1,5 +1,6 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { AGENT_NATIVE_SUBMISSION_EXPIRES_AT_METADATA_KEY, AGENT_NATIVE_SUBMISSION_TTL_MS_METADATA_KEY, MAX_AGENT_REQUEST_ATTACHMENT_DATA_CHARS, MAX_AGENT_REQUEST_ATTACHMENTS, } from "@agent-native/agentkit/protocol";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ import { AGENT_NATIVE_SUBMISSION_EXPIRES_AT_METADATA_KEY, AGENT_NATIVE_SUBMISSION_TTL_MS_METADATA_KEY, MAX_AGENT_REQUEST_ATTACHMENT_DATA_CHARS, parseQueueMessageInput, persistableFilePart, fingerprintAgentSubmissionAttachment, submissionAttachmentsMatch, } from "@agent-native/agentkit/protocol";
3
4
  import Ajv from "ajv";
4
5
  import Ajv2020 from "ajv/dist/2020.js";
5
6
  import { defineEventHandler, getHeader, setResponseHeader, setResponseStatus, getMethod, } from "h3";
@@ -22,6 +23,7 @@ import { describeAttachmentBytesVerdict, normalizeImageMediaType, reconcileImage
22
23
  import { formatBase64CharBudget, MAX_INLINE_FILE_BASE64_CHARS, MAX_INLINE_IMAGE_BASE64_CHARS, } from "../file-upload/inline-attachment-limits.js";
23
24
  import { claimOwnedAttachmentHydrationCandidate, createOwnedAttachmentHydrationBudget, describeOwnedImageReadFailure, hydrateOwnedImageUrl, } from "../file-upload/owned-attachment.js";
24
25
  import { preUploadAttachments } from "../file-upload/pre-upload-attachments.js";
26
+ import { uploadFile } from "../file-upload/registry.js";
25
27
  import { canonicalImageReferenceUrl } from "../file-upload/thread-image-history.js";
26
28
  import { derivedTool, plainJsonSnapshot, UninterpretedSchema, UnusableSchemaError, usableInputSchema, } from "../json-schema/unusable-schema.js";
27
29
  import { assertUsableSchema } from "../json-schema/usable-schema.js";
@@ -87,7 +89,7 @@ import { maybeCompactThread, buildObservationalContext, hasObservationalMemory,
87
89
  import { ProcessorChain, TripWire, toolCallsFromContent, } from "./processors.js";
88
90
  import { applyUncheckedDefaultModelReplacement } from "./provider-model-selection.js";
89
91
  import { startRun, subscribeToRun, replayCompletedTurn, getActiveRunForThread, getActiveRunForThreadAsync, getRun, abortRun, abortRunDurably, abortRunAndTurnDurably, abortRunAtSafeBoundaryDurably, abortTurnByRefDurably, abortTurnDurably, getSlotHoldingRunId, tryClaimRunSlot, isHostedRuntime, resolveRunSoftTimeoutMs, resolveRunToolTimeoutCeilingMs, endsAfterToolResultWithoutAssistantFinal, endsDuringActionPreparation, } from "./run-manager.js";
90
- import { writeLedgerEntry, readLedgerEntry, beginAgentExecutionStep, completeAgentExecutionStep, markAgentExecutionStepDispatched, markAgentExecutionStepUnknown, recordLateAgentExecutionStepOutcome, clearLedgerForThread, AgentTurnInitiatorMismatchError, AgentTurnInitiatorUnavailableError, insertRun, insertRunEvent, isTurnAborted, markRunAborted, updateRunHeartbeat, updateRunStatusIfRunning, updateRunTerminalStateIfRunning, setRunError, setRunTerminalReason, claimBackgroundRun, readBackgroundRunClaim, recordRunDiagnostic, countRunsForTurn, RUN_DIAG_STAGE, UNCLAIMED_BACKGROUND_RUN_GRACE_MS, turnRunLedgerExhausted, } from "./run-store.js";
92
+ import { writeLedgerEntry, readLedgerEntry, beginAgentExecutionStep, completeAgentExecutionStep, markAgentExecutionStepDispatched, markAgentExecutionStepUnknown, recordLateAgentExecutionStepOutcome, clearLedgerForThread, AgentTurnInitiatorMismatchError, AgentTurnInitiatorUnavailableError, insertRun, insertRunEvent, isRunAborted, isTurnAborted, markRunAborted, updateRunHeartbeat, updateRunStatusIfRunning, updateRunTerminalStateIfRunning, setRunError, setRunTerminalReason, claimBackgroundRun, readBackgroundRunClaim, recordRunDiagnostic, countRunsForTurn, RUN_DIAG_STAGE, UNCLAIMED_BACKGROUND_RUN_GRACE_MS, turnRunLedgerExhausted, } from "./run-store.js";
91
93
  import { buildCurrentTimeUserContext } from "./runtime-context.js";
92
94
  import { claimSetupResume, setupResumeRefusedRunId, } from "./setup-resume-claim.js";
93
95
  import { getStrictToolInputValidator } from "./strict-tool-input.js";
@@ -619,6 +621,34 @@ function runSlotBusy(event, activeRunId) {
619
621
  activeRunId,
620
622
  };
621
623
  }
624
+ function queuedSubmissionResponse(event, message, activeRunId) {
625
+ setResponseStatus(event, 202);
626
+ setResponseHeader(event, "Content-Type", "application/json");
627
+ return {
628
+ state: "queued",
629
+ message,
630
+ ...(activeRunId ? { activeRunId } : {}),
631
+ };
632
+ }
633
+ const QUEUED_REASONING_EFFORTS = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
634
+ function effectiveQueuedRunOptions(options, model, effort, mode) {
635
+ const effective = options ? { ...options } : undefined;
636
+ let result = effective;
637
+ if (typeof model === "string" && model.trim()) {
638
+ result ??= {};
639
+ result.model = model;
640
+ }
641
+ if (typeof effort === "string" &&
642
+ QUEUED_REASONING_EFFORTS.has(effort)) {
643
+ result ??= {};
644
+ result.reasoningEffort = effort;
645
+ }
646
+ if (mode !== undefined) {
647
+ result ??= {};
648
+ result.mode = mode;
649
+ }
650
+ return result;
651
+ }
622
652
  /**
623
653
  * The error a chat turn answers with when no model credential is usable. A
624
654
  * member whose org restricts personal API keys can't fix that by adding a key,
@@ -4606,6 +4636,31 @@ function notifyModelInputObserver(observer, messages) {
4606
4636
  // coercion-ok: observer failures cannot change agent execution.
4607
4637
  }
4608
4638
  }
4639
+ export function createRunSafeBoundaryGuards(input) {
4640
+ return {
4641
+ beforeModelDispatch: async (dispatch) => {
4642
+ await input.flushEvents();
4643
+ const interruptResult = await input.interruptRequestedAtBoundary();
4644
+ if (input.signal.aborted || interruptResult !== "none") {
4645
+ return { started: false };
4646
+ }
4647
+ return { started: true, value: dispatch() };
4648
+ },
4649
+ beforeWrite: async () => {
4650
+ await input.flushEvents();
4651
+ const interruptResult = await input.interruptRequestedAtBoundary();
4652
+ if (input.signal.aborted || interruptResult !== "none") {
4653
+ throw new Error("Run aborted at a safe boundary");
4654
+ }
4655
+ },
4656
+ };
4657
+ }
4658
+ export async function confirmQueuedRunInterrupt(input) {
4659
+ if (await input.abort())
4660
+ return "aborted";
4661
+ await input.defer();
4662
+ return "pending";
4663
+ }
4609
4664
  export async function runAgentLoop(opts) {
4610
4665
  const { engine, systemPrompt: providedSystemPrompt, tools: providedTools, availableTools: providedAvailableTools, messages, actions, send, signal, } = opts;
4611
4666
  // Every audit row this loop writes reads from this: each action's own, and
@@ -8084,15 +8139,118 @@ async function readTurnStartedAt(threadId, turnId) {
8084
8139
  const startedAt = Number(raw);
8085
8140
  return Number.isFinite(startedAt) && startedAt > 0 ? startedAt : null;
8086
8141
  }
8142
+ function requestAttachmentIndexes(submission, attachments) {
8143
+ const unmatched = new Set(attachments.map((_, index) => index));
8144
+ for (const identity of submission.attachments ?? []) {
8145
+ const match = [...unmatched].find((index) => fingerprintAgentSubmissionAttachment(attachments[index], "file") ===
8146
+ identity.fingerprint);
8147
+ if (match === undefined)
8148
+ return null;
8149
+ unmatched.delete(match);
8150
+ }
8151
+ const requestIndexes = new Set();
8152
+ for (const identity of submission.requestAttachments ?? []) {
8153
+ const match = [...unmatched].find((index) => attachments[index]?.type === "image" &&
8154
+ fingerprintAgentSubmissionAttachment(attachments[index], "image") ===
8155
+ identity.fingerprint);
8156
+ if (match === undefined)
8157
+ return null;
8158
+ unmatched.delete(match);
8159
+ requestIndexes.add(match);
8160
+ }
8161
+ return requestIndexes;
8162
+ }
8163
+ async function queueableAttachments(attachments, ownerEmail, requestAttachmentIndexes) {
8164
+ const files = [];
8165
+ const requestFiles = [];
8166
+ for (const [index, attachment] of attachments.entries()) {
8167
+ if (attachment.displayOnly === true)
8168
+ continue;
8169
+ if (attachment.type !== "image" &&
8170
+ attachment.type !== "file" &&
8171
+ attachment.type !== "document") {
8172
+ return null;
8173
+ }
8174
+ if (typeof attachment.name !== "string") {
8175
+ return null;
8176
+ }
8177
+ const record = attachment;
8178
+ const mediaType = typeof attachment.contentType === "string" && attachment.contentType
8179
+ ? attachment.contentType
8180
+ : typeof attachment.mediaType === "string"
8181
+ ? attachment.mediaType
8182
+ : undefined;
8183
+ const inlineData = typeof attachment.data === "string"
8184
+ ? attachment.data
8185
+ : typeof attachment.text === "string"
8186
+ ? `data:${mediaType ?? "text/plain"};base64,${Buffer.from(attachment.text, "utf8").toString("base64")}`
8187
+ : typeof attachment.url === "string" &&
8188
+ attachment.url.startsWith("data:")
8189
+ ? attachment.url
8190
+ : undefined;
8191
+ let part = persistableFilePart({
8192
+ type: "file",
8193
+ name: attachment.name,
8194
+ ...(mediaType ? { mediaType } : {}),
8195
+ ...(typeof attachment.url === "string" ? { url: attachment.url } : {}),
8196
+ ...(typeof record.fileId === "string"
8197
+ ? { fileId: record.fileId }
8198
+ : typeof record.id === "string"
8199
+ ? { fileId: record.id }
8200
+ : {}),
8201
+ });
8202
+ if (!part.fileId &&
8203
+ inlineData &&
8204
+ (!part.url || !record.uploadProvider || record.storageUploadFailed)) {
8205
+ const parsedData = parseBase64DataUrl(inlineData);
8206
+ if (parsedData) {
8207
+ const uploaded = await uploadFile({
8208
+ data: new Uint8Array(Buffer.from(parsedData.data, "base64")),
8209
+ filename: attachment.name,
8210
+ mimeType: mediaType ?? parsedData.mediaType,
8211
+ ownerEmail: ownerEmail ?? undefined,
8212
+ });
8213
+ if (uploaded) {
8214
+ attachment.url = uploaded.url;
8215
+ attachment.uploadProvider = uploaded.provider;
8216
+ part = persistableFilePart({
8217
+ type: "file",
8218
+ name: attachment.name,
8219
+ ...(mediaType ? { mediaType } : {}),
8220
+ url: uploaded.url,
8221
+ });
8222
+ }
8223
+ }
8224
+ }
8225
+ if (requestAttachmentIndexes.has(index)) {
8226
+ if (!part.url || !hasDurableAttachmentUrl(part.url))
8227
+ return null;
8228
+ const referenceUrl = attachment.referenceUrl;
8229
+ if (referenceUrl && !hasDurableAttachmentUrl(referenceUrl))
8230
+ return null;
8231
+ requestFiles.push({
8232
+ type: "image",
8233
+ name: attachment.name,
8234
+ ...(mediaType ? { contentType: mediaType } : {}),
8235
+ url: part.url,
8236
+ ...(referenceUrl ? { referenceUrl } : {}),
8237
+ });
8238
+ continue;
8239
+ }
8240
+ if (!part.url && !part.fileId)
8241
+ return null;
8242
+ files.push(part);
8243
+ }
8244
+ return { attachments: files, requestAttachments: requestFiles };
8245
+ }
8087
8246
  function queuedPromotionRecord(value) {
8088
8247
  return value && typeof value === "object" && !Array.isArray(value)
8089
8248
  ? value
8090
8249
  : null;
8091
8250
  }
8092
8251
  function validateQueuedPromotionRequestAttachments(value) {
8093
- if (!Array.isArray(value) || value.length > MAX_AGENT_REQUEST_ATTACHMENTS) {
8252
+ if (!Array.isArray(value))
8094
8253
  return null;
8095
- }
8096
8254
  let totalDataChars = 0;
8097
8255
  const attachments = [];
8098
8256
  for (const entry of value) {
@@ -8141,7 +8299,8 @@ function validateQueuedPromotionRequestAttachments(value) {
8141
8299
  }
8142
8300
  return attachments;
8143
8301
  }
8144
- async function readAdmittedQueuedMessagePromotion(opts) {
8302
+ /** @internal exported for unit tests only */
8303
+ export async function readAdmittedQueuedMessagePromotion(opts) {
8145
8304
  const messageId = typeof opts.messageId === "string" ? opts.messageId.trim() : "";
8146
8305
  const claimId = typeof opts.claimId === "string" ? opts.claimId.trim() : "";
8147
8306
  if (!opts.ownerEmail ||
@@ -8197,6 +8356,9 @@ async function readAdmittedQueuedMessagePromotion(opts) {
8197
8356
  const options = hasOwn(queuedMessage, "options")
8198
8357
  ? queuedMessage.options
8199
8358
  : undefined;
8359
+ const references = hasOwn(queuedMessage, "references")
8360
+ ? queuedMessage.references
8361
+ : undefined;
8200
8362
  if ((attachments !== undefined && !Array.isArray(attachments)) ||
8201
8363
  (Array.isArray(attachments) &&
8202
8364
  attachments.some((attachment) => {
@@ -8208,14 +8370,19 @@ async function readAdmittedQueuedMessagePromotion(opts) {
8208
8370
  (metadata !== undefined &&
8209
8371
  (!metadata || typeof metadata !== "object" || Array.isArray(metadata))) ||
8210
8372
  (options !== undefined &&
8211
- (!options || typeof options !== "object" || Array.isArray(options)))) {
8373
+ (!options || typeof options !== "object" || Array.isArray(options))) ||
8374
+ (references !== undefined && !Array.isArray(references))) {
8212
8375
  return null;
8213
8376
  }
8377
+ parseQueueMessageInput({ ...queuedMessage, threadId: opts.threadId }, "queuedMessagePromotion", { allowLegacyQueueCount: true });
8214
8378
  return {
8215
8379
  id: messageId,
8216
8380
  text: queuedMessage.text,
8217
8381
  ...(Array.isArray(attachments) ? { attachments } : {}),
8218
8382
  ...(requestAttachments ? { requestAttachments } : {}),
8383
+ ...(Array.isArray(references)
8384
+ ? { references: references }
8385
+ : {}),
8219
8386
  ...(metadata ? { metadata: metadata } : {}),
8220
8387
  ...(options ? { options: options } : {}),
8221
8388
  };
@@ -8302,6 +8469,7 @@ export function queuedPromotionAttachments(attachments, requestAttachments = [])
8302
8469
  : {}),
8303
8470
  ...(data ? { data } : {}),
8304
8471
  ...(imageUrl ? { url: imageUrl } : {}),
8472
+ ...(referenceUrl ? { referenceUrl } : {}),
8305
8473
  ...(fileId ? { id: fileId } : {}),
8306
8474
  };
8307
8475
  const replacementIndex = matchingIndex >= 0 && matchingIndex !== referenceIndex
@@ -8414,6 +8582,7 @@ export async function chainServerDrivenContinuation(opts) {
8414
8582
  recordRunDiagnostic: opts.deps?.recordRunDiagnostic ?? recordRunDiagnostic,
8415
8583
  markBackgroundContinuationChunkTerminal: opts.deps?.markBackgroundContinuationChunkTerminal ??
8416
8584
  markBackgroundContinuationChunkTerminal,
8585
+ isRunAborted: opts.deps?.isRunAborted ?? isRunAborted,
8417
8586
  generateRunId: opts.deps?.generateRunId ?? generateRunId,
8418
8587
  sleep: opts.deps?.sleep ??
8419
8588
  ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
@@ -8497,9 +8666,20 @@ export async function chainServerDrivenContinuation(opts) {
8497
8666
  delete continuationBody.approvedToolCalls;
8498
8667
  delete continuationBody.approvedToolCallAskIds;
8499
8668
  delete continuationBody[AGENT_CHAT_BACKGROUND_RUN_FIELD];
8669
+ const markRunsAbortedOrThrow = async (...runIds) => {
8670
+ const results = await Promise.allSettled(runIds.map(async (targetRunId) => {
8671
+ if (await d.markRunAborted(targetRunId, "user"))
8672
+ return true;
8673
+ return await d.isRunAborted(targetRunId);
8674
+ }));
8675
+ const failedRunIds = results.flatMap((result, index) => result.status === "fulfilled" && result.value ? [] : [runIds[index]]);
8676
+ if (failedRunIds.length > 0) {
8677
+ throw new Error(`Could not confirm durable abort for continuation run(s): ${failedRunIds.join(", ")}`);
8678
+ }
8679
+ };
8500
8680
  try {
8501
8681
  if (await d.isTurnAborted(effectiveThreadId, effectiveTurnId)) {
8502
- await d.markRunAborted(runId, "user").catch(() => { });
8682
+ await markRunsAbortedOrThrow(runId);
8503
8683
  return;
8504
8684
  }
8505
8685
  await d
@@ -8527,9 +8707,7 @@ export async function chainServerDrivenContinuation(opts) {
8527
8707
  console.error("[agent-chat] continuation insertRun failed; dispatching with inline body:", insertErr instanceof Error ? insertErr.message : insertErr);
8528
8708
  }
8529
8709
  if (await d.isTurnAborted(effectiveThreadId, effectiveTurnId)) {
8530
- if (nextRowInserted)
8531
- await d.markRunAborted(nextRunId, "user").catch(() => { });
8532
- await d.markRunAborted(runId, "user").catch(() => { });
8710
+ await markRunsAbortedOrThrow(...(nextRowInserted ? [nextRunId] : []), runId);
8533
8711
  return;
8534
8712
  }
8535
8713
  const continuationTerminalEvent = {
@@ -8544,6 +8722,11 @@ export async function chainServerDrivenContinuation(opts) {
8544
8722
  if (!terminalMarked) {
8545
8723
  throw new Error(`Could not release run ${runId} for continuation ${nextRunId}`);
8546
8724
  }
8725
+ if (await d.isTurnAborted(effectiveThreadId, effectiveTurnId)) {
8726
+ if (nextRowInserted)
8727
+ await markRunsAbortedOrThrow(nextRunId);
8728
+ return;
8729
+ }
8547
8730
  run.continuationTerminalEvent = continuationTerminalEvent;
8548
8731
  const dispatchBody = nextRowInserted
8549
8732
  ? {
@@ -8775,7 +8958,7 @@ export function createProductionAgentHandler(options) {
8775
8958
  return { error: "Invalid request body" };
8776
8959
  }
8777
8960
  }
8778
- const { message, history: submittedHistory = [], structuredHistory: submittedStructuredHistory, loadedSkillSlugs: requestedLoadedSkillSlugs, references: submittedReferences = [], threadId, attachments, displayMessage, parentId, queuedMessageId, queuedMessageClaimId, agentKitMessageId: requestedAgentKitMessageId, submissionPayload: requestedSubmissionPayload, internalContinuation: submittedInternalContinuation, autoContinueOfRunId: requestedAutoContinueOfRunId, continueOfRunId: requestedContinueOfRunId, turnId: requestTurnId, model: submittedModel, engine: submittedEngine, effort: submittedEffort, browserTabId, scope, harness: submittedHarness, trackInRunsTray, skipPendingSelectionContext, } = body;
8961
+ const { message, history: submittedHistory = [], structuredHistory: submittedStructuredHistory, loadedSkillSlugs: requestedLoadedSkillSlugs, references: submittedReferences = [], threadId, attachments, displayMessage, parentId, queuedMessageId, queuedMessageClaimId, agentKitMessageId: requestedAgentKitMessageId, submissionPayload: requestedSubmissionPayload, internalContinuation: submittedInternalContinuation, autoContinueOfRunId: requestedAutoContinueOfRunId, continueOfRunId: requestedContinueOfRunId, turnId: requestTurnId, model: submittedModel, engine: submittedEngine, effort: submittedEffort, options: submittedRunOptions, browserTabId, scope, harness: submittedHarness, trackInRunsTray, skipPendingSelectionContext, } = body;
8779
8962
  let requestHistory = submittedHistory;
8780
8963
  let requestStructuredHistory = submittedStructuredHistory;
8781
8964
  let requestReferences = submittedReferences;
@@ -8784,6 +8967,11 @@ export function createProductionAgentHandler(options) {
8784
8967
  let requestModel = submittedModel;
8785
8968
  let requestEngine = submittedEngine;
8786
8969
  let requestEffort = submittedEffort;
8970
+ let requestOptions = submittedRunOptions &&
8971
+ typeof submittedRunOptions === "object" &&
8972
+ !Array.isArray(submittedRunOptions)
8973
+ ? submittedRunOptions
8974
+ : undefined;
8787
8975
  let internalContinuation = submittedInternalContinuation;
8788
8976
  let requestHarness = submittedHarness;
8789
8977
  let requestSkipPendingSelectionContext = skipPendingSelectionContext;
@@ -8838,7 +9026,49 @@ export function createProductionAgentHandler(options) {
8838
9026
  setResponseStatus(event, 400);
8839
9027
  return { error: "submissionPayload does not match this message" };
8840
9028
  }
8841
- submissionPayload = candidate;
9029
+ let validatedSubmissionPayload;
9030
+ try {
9031
+ validatedSubmissionPayload = parseQueueMessageInput({
9032
+ threadId: candidate.threadId,
9033
+ id: candidate.id,
9034
+ text: candidate.text,
9035
+ submissionPayload: candidate,
9036
+ }, "submissionPayload").submissionPayload;
9037
+ }
9038
+ catch {
9039
+ setResponseStatus(event, 400);
9040
+ return { error: "Invalid submissionPayload" };
9041
+ }
9042
+ if (!validatedSubmissionPayload) {
9043
+ setResponseStatus(event, 400);
9044
+ return { error: "Invalid submissionPayload" };
9045
+ }
9046
+ submissionPayload = validatedSubmissionPayload;
9047
+ if (validatedSubmissionPayload.text !==
9048
+ (typeof message === "string" ? message : "") ||
9049
+ !isDeepStrictEqual(validatedSubmissionPayload.references ?? [], submittedReferences) ||
9050
+ !isDeepStrictEqual(validatedSubmissionPayload.options ?? {}, requestOptions ?? {})) {
9051
+ setResponseStatus(event, 400);
9052
+ return { error: "submissionPayload does not match this message" };
9053
+ }
9054
+ }
9055
+ let requestMode = body.mode === "plan" || requestOptions?.mode === "plan" ? "plan" : "act";
9056
+ requestOptions = effectiveQueuedRunOptions(requestOptions, requestModel, requestEffort, body.mode !== undefined || requestOptions?.mode !== undefined
9057
+ ? requestMode
9058
+ : undefined);
9059
+ if (submissionPayload) {
9060
+ const metadata = submissionPayload.metadata === undefined && requestEngine === undefined
9061
+ ? undefined
9062
+ : {
9063
+ ...submissionPayload.metadata,
9064
+ ...(requestEngine === undefined ? {} : { engine: requestEngine }),
9065
+ };
9066
+ submissionPayload = {
9067
+ ...submissionPayload,
9068
+ ...(requestOptions === undefined ? {} : { options: requestOptions }),
9069
+ ...(metadata === undefined ? {} : { metadata }),
9070
+ };
9071
+ requestMetadata = normalizeSubmissionDeadlineMetadata(submissionPayload.metadata);
8842
9072
  }
8843
9073
  const backgroundRunMarker = preInjectedBody &&
8844
9074
  body[AGENT_CHAT_BACKGROUND_RUN_FIELD] &&
@@ -8942,7 +9172,6 @@ export function createProductionAgentHandler(options) {
8942
9172
  requestRunCtx.chatScope = requestChatScope;
8943
9173
  requestRunCtx.isBackgroundWorker = isBackgroundWorker;
8944
9174
  }
8945
- let requestMode = body.mode === "plan" ? "plan" : "act";
8946
9175
  const hasMessageText = typeof message === "string" && message.trim().length > 0;
8947
9176
  let hasAttachments = Array.isArray(attachments) && attachments.length > 0;
8948
9177
  if (!hasMessageText && !hasAttachments) {
@@ -8951,6 +9180,19 @@ export function createProductionAgentHandler(options) {
8951
9180
  }
8952
9181
  let requestMessage = hasMessageText ? message : "Use the attached context.";
8953
9182
  let requestAttachments = Array.isArray(attachments) ? attachments : [];
9183
+ const submissionAttachmentsAreConsistent = submissionPayload !== undefined &&
9184
+ submissionAttachmentsMatch(submissionPayload, requestAttachments);
9185
+ if (submissionPayload && !submissionAttachmentsAreConsistent) {
9186
+ setResponseStatus(event, 400);
9187
+ return { error: "submissionPayload does not match this message" };
9188
+ }
9189
+ const submissionRequestAttachmentIndexes = submissionPayload
9190
+ ? requestAttachmentIndexes(submissionPayload, requestAttachments)
9191
+ : new Set();
9192
+ if (!submissionRequestAttachmentIndexes) {
9193
+ setResponseStatus(event, 400);
9194
+ return { error: "submissionPayload does not match this message" };
9195
+ }
8954
9196
  let attachmentHydrationBudget;
8955
9197
  let requestDisplayMessage = displayMessage;
8956
9198
  let requestContext = buildRecentUserRequestContext({
@@ -8969,6 +9211,211 @@ export function createProductionAgentHandler(options) {
8969
9211
  anonymous: runRequestContext?.agentRunAnonymous === true,
8970
9212
  }
8971
9213
  : undefined;
9214
+ const busySubmissionQueueEligible = Boolean(submissionPayload &&
9215
+ agentKitMessageId &&
9216
+ threadId &&
9217
+ ownerEmail &&
9218
+ !internalContinuation &&
9219
+ !continueOf &&
9220
+ !queuedMessageId &&
9221
+ !queuedMessageClaimId &&
9222
+ !isBackgroundWorker &&
9223
+ submittedInternalContinuation !== true &&
9224
+ typeof requestedAutoContinueOfRunId !== "string" &&
9225
+ typeof requestedContinueOfRunId !== "string" &&
9226
+ !hasOwn(body, "approvedToolCalls") &&
9227
+ !hasOwn(body, "approvedToolCallAskIds") &&
9228
+ !hasOwn(body, "resume"));
9229
+ const busySubmissionAttachmentsAreQueueable = requestAttachments.every((attachment) => attachment.displayOnly === true ||
9230
+ ((attachment.type === "image" ||
9231
+ attachment.type === "file" ||
9232
+ attachment.type === "document") &&
9233
+ typeof attachment.name === "string"));
9234
+ const tryAdmitBusySubmission = async () => {
9235
+ if (!submissionPayload ||
9236
+ !agentKitMessageId ||
9237
+ !threadId ||
9238
+ !ownerEmail ||
9239
+ internalContinuation ||
9240
+ continueOf ||
9241
+ queuedMessageId ||
9242
+ queuedMessageClaimId ||
9243
+ isBackgroundWorker ||
9244
+ submittedInternalContinuation === true ||
9245
+ typeof requestedAutoContinueOfRunId === "string" ||
9246
+ typeof requestedContinueOfRunId === "string" ||
9247
+ hasOwn(body, "approvedToolCalls") ||
9248
+ hasOwn(body, "approvedToolCallAskIds") ||
9249
+ hasOwn(body, "resume")) {
9250
+ return null;
9251
+ }
9252
+ const { resolveThreadAccess } = await import("../chat-threads/store.js");
9253
+ const accessibleThread = await resolveThreadAccess(ownerEmail, threadId, "editor", { orgId: getRequestOrgId() ?? undefined });
9254
+ if (!accessibleThread) {
9255
+ setResponseStatus(event, 404);
9256
+ return { error: "Thread not found" };
9257
+ }
9258
+ const attachmentsForQueue = await queueableAttachments(requestAttachments, ownerEmail, submissionRequestAttachmentIndexes);
9259
+ if (!attachmentsForQueue) {
9260
+ if (requestAttachments.some((attachment) => attachment.displayOnly !== true &&
9261
+ (attachment.type === "image" ||
9262
+ attachment.type === "file" ||
9263
+ attachment.type === "document"))) {
9264
+ setResponseStatus(event, 503);
9265
+ return {
9266
+ error: "The attachment could not be saved for later delivery",
9267
+ code: "queued_attachment_reference_unavailable",
9268
+ retryable: true,
9269
+ };
9270
+ }
9271
+ return null;
9272
+ }
9273
+ const queueText = typeof message === "string" && message.trim()
9274
+ ? message
9275
+ : "Use the attached context.";
9276
+ let parsedMessage;
9277
+ try {
9278
+ parsedMessage = parseQueueMessageInput({
9279
+ threadId,
9280
+ id: agentKitMessageId,
9281
+ text: queueText,
9282
+ ...(attachmentsForQueue.attachments.length
9283
+ ? { attachments: attachmentsForQueue.attachments }
9284
+ : {}),
9285
+ ...(attachmentsForQueue.requestAttachments.length
9286
+ ? { requestAttachments: attachmentsForQueue.requestAttachments }
9287
+ : {}),
9288
+ ...(submittedReferences.length
9289
+ ? { references: submittedReferences }
9290
+ : {}),
9291
+ ...(requestMetadata ? { metadata: requestMetadata } : {}),
9292
+ ...(requestOptions ? { options: requestOptions } : {}),
9293
+ ...(submissionPayload.interruptActiveRun === true
9294
+ ? { interruptActiveRun: true }
9295
+ : {}),
9296
+ }, "agentChatSubmission");
9297
+ }
9298
+ catch {
9299
+ setResponseStatus(event, 400);
9300
+ return {
9301
+ error: "This submission cannot be saved for later delivery",
9302
+ code: "queued_submission_invalid",
9303
+ retryable: false,
9304
+ };
9305
+ }
9306
+ if (!parsedMessage.text.trim()) {
9307
+ setResponseStatus(event, 400);
9308
+ return {
9309
+ error: "This submission cannot be saved for later delivery",
9310
+ code: "queued_submission_invalid",
9311
+ retryable: false,
9312
+ };
9313
+ }
9314
+ const { mutateThreadQueuedMessages, QueuedSubmissionAdmissionError } = await import("../chat-threads/store.js");
9315
+ const requestContext = getRequestContext();
9316
+ const actor = {
9317
+ email: requestContext?.userEmail ?? ownerEmail,
9318
+ ...(requestContext?.authUserId
9319
+ ? { authUserId: requestContext.authUserId }
9320
+ : {}),
9321
+ ...(requestContext?.identityAuthenticatedAtMs !== undefined
9322
+ ? {
9323
+ identityAuthenticatedAtMs: requestContext.identityAuthenticatedAtMs,
9324
+ }
9325
+ : {}),
9326
+ ...(requestContext?.userName ? { name: requestContext.userName } : {}),
9327
+ orgId: requestContext?.orgId ?? null,
9328
+ orgScope: requestContext?.orgScope ?? null,
9329
+ anonymous: requestContext?.agentRunAnonymous === true,
9330
+ };
9331
+ const queuedMessage = {
9332
+ ...parsedMessage,
9333
+ id: agentKitMessageId,
9334
+ threadId,
9335
+ createdAt: new Date().toISOString(),
9336
+ ...(attachmentsForQueue.attachments.length
9337
+ ? { attachments: attachmentsForQueue.attachments }
9338
+ : {}),
9339
+ ...(attachmentsForQueue.requestAttachments.length
9340
+ ? { requestAttachments: attachmentsForQueue.requestAttachments }
9341
+ : {}),
9342
+ };
9343
+ let result;
9344
+ try {
9345
+ result = await mutateThreadQueuedMessages(threadId, {
9346
+ type: "append",
9347
+ message: queuedMessage,
9348
+ hashInput: submissionPayload,
9349
+ }, { actor });
9350
+ }
9351
+ catch (error) {
9352
+ if (!(error instanceof QueuedSubmissionAdmissionError))
9353
+ throw error;
9354
+ setResponseStatus(event, 409);
9355
+ return {
9356
+ error: "This message ID is no longer available for submission",
9357
+ code: error.code,
9358
+ retryable: false,
9359
+ };
9360
+ }
9361
+ if (!result)
9362
+ return null;
9363
+ if (result.receiptUnavailable) {
9364
+ setResponseStatus(event, 503);
9365
+ return {
9366
+ error: "The queued submission receipt could not be saved",
9367
+ code: "queued_submission_receipt_unavailable",
9368
+ retryable: true,
9369
+ };
9370
+ }
9371
+ if (result.receiptConflict) {
9372
+ setResponseStatus(event, 409);
9373
+ return {
9374
+ error: "This message ID was already used for a different submission",
9375
+ code: "queued_message_id_conflict",
9376
+ retryable: false,
9377
+ };
9378
+ }
9379
+ if (result.submissionExpired) {
9380
+ setResponseStatus(event, 409);
9381
+ return {
9382
+ error: "This submission expired before it could be queued",
9383
+ code: "submission_expired",
9384
+ retryable: false,
9385
+ };
9386
+ }
9387
+ if (result.alreadySubmitted && result.submittedRunId) {
9388
+ const stream = subscribeToRun(result.submittedRunId, 0);
9389
+ if (!stream) {
9390
+ setResponseStatus(event, 503);
9391
+ return {
9392
+ error: "The accepted submission run is no longer replayable",
9393
+ code: "queued_message_run_unavailable",
9394
+ retryable: true,
9395
+ };
9396
+ }
9397
+ setResponseHeader(event, "Content-Type", "text/event-stream");
9398
+ setResponseHeader(event, "Cache-Control", "no-cache");
9399
+ setResponseHeader(event, "Connection", "keep-alive");
9400
+ setResponseHeader(event, "X-Run-Id", result.submittedRunId);
9401
+ setResponseHeader(event, "X-Dispatch-Mode", "replay");
9402
+ return stream;
9403
+ }
9404
+ if (!result.message) {
9405
+ setResponseStatus(event, 503);
9406
+ return {
9407
+ error: "The queued submission receipt could not be read",
9408
+ code: "queued_submission_receipt_unavailable",
9409
+ retryable: true,
9410
+ };
9411
+ }
9412
+ return queuedSubmissionResponse(event, {
9413
+ ...result.message,
9414
+ id: agentKitMessageId,
9415
+ threadId,
9416
+ createdAt: result.message.createdAt ?? queuedMessage.createdAt,
9417
+ }, await getSlotHoldingRunId(threadId));
9418
+ };
8972
9419
  if (submissionPayload &&
8973
9420
  ownerEmail &&
8974
9421
  threadId &&
@@ -8976,7 +9423,7 @@ export function createProductionAgentHandler(options) {
8976
9423
  !queuedMessageId &&
8977
9424
  !queuedMessageClaimId &&
8978
9425
  !isBackgroundWorker) {
8979
- const { getQueuedSubmissionReceipt, hashQueuedSubmissionPayload, resolveThreadAccess, } = await import("../chat-threads/store.js");
9426
+ const { getQueuedSubmissionReceipt, getQueuedSubmissionReceiptStatus, hashQueuedSubmissionPayload, resolveThreadAccess, } = await import("../chat-threads/store.js");
8980
9427
  const receipt = await getQueuedSubmissionReceipt(threadId, String(submissionPayload.id));
8981
9428
  if (receipt) {
8982
9429
  const accessibleThread = await resolveThreadAccess(ownerEmail, threadId, "editor", { orgId: getRequestOrgId() ?? undefined });
@@ -9002,8 +9449,23 @@ export function createProductionAgentHandler(options) {
9002
9449
  retryable: false,
9003
9450
  };
9004
9451
  }
9005
- if (receipt.state === "submitted") {
9006
- if (!receipt.submittedRunId) {
9452
+ let receiptState = receipt.state;
9453
+ let submittedRunId = receipt.submittedRunId;
9454
+ if (receipt.state === "queued") {
9455
+ const status = await getQueuedSubmissionReceiptStatus(threadId, receipt.messageId);
9456
+ if (!status) {
9457
+ setResponseStatus(event, 503);
9458
+ return {
9459
+ error: "The queued submission receipt could not be read",
9460
+ code: "queued_submission_receipt_unavailable",
9461
+ retryable: true,
9462
+ };
9463
+ }
9464
+ receiptState = status.state;
9465
+ submittedRunId = status.submittedRunId;
9466
+ }
9467
+ if (receiptState === "submitted") {
9468
+ if (!submittedRunId) {
9007
9469
  setResponseStatus(event, 503);
9008
9470
  return {
9009
9471
  error: "The accepted submission has no replayable run",
@@ -9011,7 +9473,7 @@ export function createProductionAgentHandler(options) {
9011
9473
  retryable: true,
9012
9474
  };
9013
9475
  }
9014
- const stream = subscribeToRun(receipt.submittedRunId, 0);
9476
+ const stream = subscribeToRun(submittedRunId, 0);
9015
9477
  if (!stream) {
9016
9478
  setResponseStatus(event, 503);
9017
9479
  return {
@@ -9023,12 +9485,26 @@ export function createProductionAgentHandler(options) {
9023
9485
  setResponseHeader(event, "Content-Type", "text/event-stream");
9024
9486
  setResponseHeader(event, "Cache-Control", "no-cache");
9025
9487
  setResponseHeader(event, "Connection", "keep-alive");
9026
- setResponseHeader(event, "X-Run-Id", receipt.submittedRunId);
9488
+ setResponseHeader(event, "X-Run-Id", submittedRunId);
9027
9489
  setResponseHeader(event, "X-Dispatch-Mode", "replay");
9028
9490
  return stream;
9029
9491
  }
9030
- if (receipt.state === "queued") {
9031
- return runSlotBusy(event, null);
9492
+ if (receiptState === "queued") {
9493
+ if (!receipt.message) {
9494
+ setResponseStatus(event, 503);
9495
+ return {
9496
+ error: "The queued submission receipt has no replayable message",
9497
+ code: "queued_submission_receipt_unavailable",
9498
+ retryable: true,
9499
+ };
9500
+ }
9501
+ return queuedSubmissionResponse(event, {
9502
+ ...receipt.message,
9503
+ id: receipt.messageId,
9504
+ threadId,
9505
+ createdAt: receipt.message.createdAt ??
9506
+ new Date(receipt.createdAt).toISOString(),
9507
+ }, await getSlotHoldingRunId(threadId));
9032
9508
  }
9033
9509
  setResponseStatus(event, 409);
9034
9510
  return {
@@ -9153,6 +9629,9 @@ export function createProductionAgentHandler(options) {
9153
9629
  retryable: false,
9154
9630
  };
9155
9631
  }
9632
+ const isAuthorizedApprovalContinuation = Boolean(submittedInternalContinuation === true &&
9633
+ requestedApprovedToolCallAskIds?.length &&
9634
+ resolvedApprovalTurnId);
9156
9635
  // The continuation marker is client-controlled. Defer setup admission only
9157
9636
  // for automatic successors when the named stop and turn can be checked
9158
9637
  // under the run-slot lock. A user's manual Continue starts new work and
@@ -9184,7 +9663,8 @@ export function createProductionAgentHandler(options) {
9184
9663
  requestDisplayMessage = admittedQueuedMessage.text;
9185
9664
  requestAttachments = queuedPromotionAttachments(admittedQueuedMessage.attachments ?? [], admittedQueuedMessage.requestAttachments);
9186
9665
  hasAttachments = requestAttachments.length > 0;
9187
- requestReferences = [];
9666
+ requestReferences = admittedQueuedMessage.references ?? [];
9667
+ requestOptions = promotionOptions;
9188
9668
  requestHistory = [];
9189
9669
  requestStructuredHistory = undefined;
9190
9670
  internalContinuation = false;
@@ -9217,6 +9697,7 @@ export function createProductionAgentHandler(options) {
9217
9697
  mutableBody.history = requestHistory;
9218
9698
  delete mutableBody.structuredHistory;
9219
9699
  mutableBody.metadata = requestMetadata;
9700
+ mutableBody.options = requestOptions;
9220
9701
  mutableBody.model = requestModel;
9221
9702
  if (requestEngine === undefined) {
9222
9703
  delete mutableBody.engine;
@@ -9231,7 +9712,6 @@ export function createProductionAgentHandler(options) {
9231
9712
  delete mutableBody.continueOfRunId;
9232
9713
  delete mutableBody.harness;
9233
9714
  delete mutableBody.parentId;
9234
- delete mutableBody.options;
9235
9715
  delete mutableBody.skipPendingSelectionContext;
9236
9716
  }
9237
9717
  const recordUnstartedTurn = async (failure) => {
@@ -9301,6 +9781,14 @@ export function createProductionAgentHandler(options) {
9301
9781
  }
9302
9782
  }
9303
9783
  setupMark("aiGate");
9784
+ if (hasOwn(body, "queuedMessageClaimId") && !admittedQueuedMessage) {
9785
+ setResponseStatus(event, 409);
9786
+ return {
9787
+ error: "This queued message can no longer be promoted",
9788
+ code: "queued_message_claim_invalid",
9789
+ retryable: false,
9790
+ };
9791
+ }
9304
9792
  const contextPrefetchDeadlineAt = Date.now() + 1_300;
9305
9793
  const preparedRequest = await options.prepareRequest?.({
9306
9794
  event,
@@ -9489,18 +9977,23 @@ export function createProductionAgentHandler(options) {
9489
9977
  }
9490
9978
  }
9491
9979
  workerStep("db_request_ctx");
9980
+ if (hasAttachments &&
9981
+ threadId &&
9982
+ !isBackgroundWorker &&
9983
+ requestAttachments.some((attachment) => attachment.displayOnly !== true &&
9984
+ (attachment.type === "image" ||
9985
+ attachment.type === "file" ||
9986
+ attachment.type === "document"))) {
9987
+ const activeRunId = await getSlotHoldingRunId(threadId);
9988
+ if (activeRunId &&
9989
+ (!busySubmissionQueueEligible || !busySubmissionAttachmentsAreQueueable)) {
9990
+ return runSlotBusy(event, activeRunId);
9991
+ }
9992
+ }
9492
9993
  workerStep("attach_start");
9493
9994
  if (hasAttachments &&
9494
9995
  requestAttachments.some((a) => a.displayOnly !== true &&
9495
9996
  (a.type === "image" || a.type === "file" || a.type === "document"))) {
9496
- // A busy thread refuses this message (409) and the client sends it again
9497
- // once the thread frees up; uploading first would store every
9498
- // attachment again on each refusal.
9499
- if (threadId && !isBackgroundWorker) {
9500
- const activeRunId = await getSlotHoldingRunId(threadId);
9501
- if (activeRunId)
9502
- return runSlotBusy(event, activeRunId);
9503
- }
9504
9997
  try {
9505
9998
  attachmentHydrationBudget ??= createOwnedAttachmentHydrationBudget();
9506
9999
  const preUpload = await preUploadAttachments({
@@ -9521,6 +10014,15 @@ export function createProductionAgentHandler(options) {
9521
10014
  ? `${requestMessage}\n\n${describeAttachmentPreUploadWarning()}`
9522
10015
  : describeAttachmentPreUploadWarning();
9523
10016
  }
10017
+ if (threadId && !isBackgroundWorker) {
10018
+ const activeRunId = await getSlotHoldingRunId(threadId);
10019
+ if (activeRunId) {
10020
+ const queued = await tryAdmitBusySubmission();
10021
+ if (queued !== null)
10022
+ return queued;
10023
+ return runSlotBusy(event, activeRunId);
10024
+ }
10025
+ }
9524
10026
  }
9525
10027
  const textAttachmentResourceMap = new Map();
9526
10028
  if (hasAttachments && threadId) {
@@ -10273,6 +10775,11 @@ export function createProductionAgentHandler(options) {
10273
10775
  }
10274
10776
  let slot;
10275
10777
  try {
10778
+ const runThreadId = threadId;
10779
+ const shouldPreserveQueuedOrder = Boolean(!isAuthorizedApprovalContinuation &&
10780
+ !queuedMessageId &&
10781
+ !queuedMessageClaimId &&
10782
+ !isBackgroundWorker);
10276
10783
  slot = await tryClaimRunSlot(threadId, runId, undefined, {
10277
10784
  turnId: effectiveTurnId,
10278
10785
  ...(turnInitiator ? { turnInitiator } : {}),
@@ -10288,6 +10795,14 @@ export function createProductionAgentHandler(options) {
10288
10795
  ? { dispatchPayload: durableDispatchPayload }
10289
10796
  : {}),
10290
10797
  ...(continueOf ? { continueOf } : {}),
10798
+ ...(shouldPreserveQueuedOrder
10799
+ ? {
10800
+ hasPendingQueuedSubmission: async (tx) => {
10801
+ const { hasPendingQueuedSubmissionReceipt } = await import("../chat-threads/store.js");
10802
+ return hasPendingQueuedSubmissionReceipt(tx, runThreadId);
10803
+ },
10804
+ }
10805
+ : {}),
10291
10806
  });
10292
10807
  }
10293
10808
  catch (error) {
@@ -10323,6 +10838,15 @@ export function createProductionAgentHandler(options) {
10323
10838
  }
10324
10839
  if (!slot.claimed) {
10325
10840
  await setupResumeClaim?.release();
10841
+ if (slot.queuedSubmissionPending) {
10842
+ const queued = await tryAdmitBusySubmission();
10843
+ if (queued !== null)
10844
+ return queued;
10845
+ return runSlotBusy(event, null);
10846
+ }
10847
+ const queued = await tryAdmitBusySubmission();
10848
+ if (queued !== null)
10849
+ return queued;
10326
10850
  return runSlotBusy(event, slot.activeRunId);
10327
10851
  }
10328
10852
  foregroundRunRowInserted = true;
@@ -10891,6 +11415,77 @@ export function createProductionAgentHandler(options) {
10891
11415
  rawSend(event);
10892
11416
  updateTrackedProgressFromEvent(event);
10893
11417
  };
11418
+ const interruptQueuedRunAtBoundary = async () => {
11419
+ if (!threadId)
11420
+ return "none";
11421
+ const { clearQueuedSubmissionInterruptRun, listQueuedSubmissionReceipts, mutateThreadQueuedMessages, recordQueuedSubmissionInterruptRun, retryQueuedSubmissionReceipt, } = await import("../chat-threads/store.js");
11422
+ const pending = await listQueuedSubmissionReceipts({
11423
+ limit: 25,
11424
+ threadId,
11425
+ interruptActiveRun: true,
11426
+ });
11427
+ for (const receipt of pending) {
11428
+ if (receipt.message?.interruptActiveRun !== true)
11429
+ continue;
11430
+ const claimId = randomUUID();
11431
+ let claimed = false;
11432
+ try {
11433
+ const claim = await mutateThreadQueuedMessages(threadId, {
11434
+ type: "claimFromInbox",
11435
+ receipt,
11436
+ claimId,
11437
+ });
11438
+ if (!claim?.claimedMessage)
11439
+ continue;
11440
+ claimed = true;
11441
+ if (receipt.interruptRunId &&
11442
+ receipt.interruptRunId !== runId) {
11443
+ const { getRunById } = await import("./run-store.js");
11444
+ const interruptedRun = await getRunById(receipt.interruptRunId);
11445
+ if (interruptedRun?.status === "running")
11446
+ continue;
11447
+ const cleared = await clearQueuedSubmissionInterruptRun({
11448
+ threadId,
11449
+ messageId: receipt.messageId,
11450
+ runId: receipt.interruptRunId,
11451
+ claimId,
11452
+ });
11453
+ if (!cleared)
11454
+ continue;
11455
+ }
11456
+ if (!(await recordQueuedSubmissionInterruptRun({
11457
+ threadId,
11458
+ messageId: receipt.messageId,
11459
+ runId,
11460
+ claimId,
11461
+ }))) {
11462
+ continue;
11463
+ }
11464
+ const aborted = await confirmQueuedRunInterrupt({
11465
+ abort: () => abortRunAtSafeBoundaryDurably(runId, "user"),
11466
+ defer: () => retryQueuedSubmissionReceipt(threadId, receipt.messageId, Date.now() + 10_000, { claimId }),
11467
+ });
11468
+ if (aborted === "pending")
11469
+ return "pending";
11470
+ }
11471
+ finally {
11472
+ if (claimed) {
11473
+ await mutateThreadQueuedMessages(threadId, {
11474
+ type: "release",
11475
+ messageId: receipt.messageId,
11476
+ claimId,
11477
+ });
11478
+ }
11479
+ }
11480
+ return "aborted";
11481
+ }
11482
+ return "none";
11483
+ };
11484
+ const { beforeModelDispatch, beforeWrite } = createRunSafeBoundaryGuards({
11485
+ signal,
11486
+ flushEvents: control.flushEvents,
11487
+ interruptRequestedAtBoundary: interruptQueuedRunAtBoundary,
11488
+ });
10894
11489
  if (selfChainBudget?.skipToBoundary) {
10895
11490
  await recordRunDiagnostic(runId, RUN_DIAG_STAGE.workerSetupStep, `self_chain_budget_exhausted elapsed=${Date.now() - setupT0}ms`).catch(() => { });
10896
11491
  send({ type: "auto_continue", reason: "run_timeout" });
@@ -10988,7 +11583,8 @@ export function createProductionAgentHandler(options) {
10988
11583
  threadId: effectiveThreadId,
10989
11584
  turnId: effectiveTurnId,
10990
11585
  durableStepIdForToolCall: namespacedDurableStepId,
10991
- beforeWrite: control.flushEvents,
11586
+ beforeModelDispatch,
11587
+ beforeWrite,
10992
11588
  unknownOutcomes,
10993
11589
  internalContinuation,
10994
11590
  ...(shouldRecoverCompletedExecutionSteps
@@ -11161,7 +11757,8 @@ export function createProductionAgentHandler(options) {
11161
11757
  : {}),
11162
11758
  systemSections: contextXraySystemSections,
11163
11759
  actions: requestActions,
11164
- beforeWrite: control.flushEvents,
11760
+ beforeModelDispatch,
11761
+ beforeWrite,
11165
11762
  unknownOutcomes,
11166
11763
  internalContinuation,
11167
11764
  send,