@agent-native/core 0.227.0 → 0.228.0-nightly-20261111004142

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 (190) hide show
  1. package/dist/action.d.ts +6 -2
  2. package/dist/agent/engine/builder-engine.js +6 -1
  3. package/dist/agent/engine/tool-call-journal-seed.d.ts +2 -0
  4. package/dist/agent/engine/tool-call-journal-seed.js +14 -0
  5. package/dist/agent/harness/store.js +14 -0
  6. package/dist/agent/production-agent.js +257 -17
  7. package/dist/agent/reaped-run-thread.js +6 -0
  8. package/dist/agent/run-manager.js +6 -0
  9. package/dist/agent/tool-call-journal.d.ts +4 -0
  10. package/dist/agent/tool-call-journal.js +12 -0
  11. package/dist/agent/types.d.ts +7 -1
  12. package/dist/app-config/agent.d.ts +3 -0
  13. package/dist/app-config/agent.js +7 -0
  14. package/dist/app-config/schema.d.ts +3 -0
  15. package/dist/application-state/handlers.d.ts +3 -3
  16. package/dist/application-state/handlers.js +6 -1
  17. package/dist/application-state/index.d.ts +3 -2
  18. package/dist/application-state/index.js +3 -2
  19. package/dist/application-state/last-used-tab.d.ts +11 -0
  20. package/dist/application-state/last-used-tab.js +43 -0
  21. package/dist/application-state/script-helpers.d.ts +26 -0
  22. package/dist/application-state/script-helpers.js +83 -15
  23. package/dist/application-state/store.d.ts +8 -0
  24. package/dist/application-state/store.js +16 -0
  25. package/dist/cli/agent.js +7 -3
  26. package/dist/cli/code-agent-executor.d.ts +1 -0
  27. package/dist/cli/code-agent-executor.js +37 -2
  28. package/dist/cli/code.js +3 -1
  29. package/dist/client/agent-sidebar-state.d.ts +2 -0
  30. package/dist/client/browser-tab-use.d.ts +9 -0
  31. package/dist/client/browser-tab-use.js +112 -0
  32. package/dist/client/chat/runtime.js +3 -1
  33. package/dist/client/error-capture.js +33 -1
  34. package/dist/client/navigation/index.d.ts +1 -0
  35. package/dist/client/navigation/index.js +1 -0
  36. package/dist/client/resources/mcp-integration-logos.js +16 -0
  37. package/dist/client/review/use-review.d.ts +28 -0
  38. package/dist/client/review/use-review.js +41 -12
  39. package/dist/client/route-state.js +2 -0
  40. package/dist/client/sse-event-processor.d.ts +1 -0
  41. package/dist/client/sse-event-processor.js +7 -1
  42. package/dist/client/use-db-sync.d.ts +8 -1
  43. package/dist/client/use-db-sync.js +6 -5
  44. package/dist/coding-tools/run-code.js +12 -2
  45. package/dist/coding-tools/sandbox/executions-store.d.ts +1 -1
  46. package/dist/coding-tools/sandbox/executions-store.js +9 -3
  47. package/dist/collab/routes.d.ts +1 -1
  48. package/dist/data-programs/execute.js +1 -1
  49. package/dist/db/schema-snapshot.generated.js +17 -3
  50. package/dist/extensions/url-safety.d.ts +1 -3
  51. package/dist/extensions/url-safety.js +23 -23
  52. package/dist/file-upload/actions/upload-image.d.ts +5 -5
  53. package/dist/file-upload/index.d.ts +1 -1
  54. package/dist/file-upload/index.js +1 -1
  55. package/dist/file-upload/s3-client.d.ts +1 -0
  56. package/dist/file-upload/s3-client.js +70 -13
  57. package/dist/file-upload/s3.d.ts +9 -0
  58. package/dist/file-upload/s3.js +14 -1
  59. package/dist/framework-package-versions.json +8 -8
  60. package/dist/jobs/run-history.js +10 -3
  61. package/dist/jobs/scheduler.js +5 -36
  62. package/dist/localization/core-messages/supplemental/ar-SA.js +35 -0
  63. package/dist/localization/core-messages/supplemental/de-DE.js +35 -0
  64. package/dist/localization/core-messages/supplemental/en-US.js +35 -0
  65. package/dist/localization/core-messages/supplemental/es-ES.js +35 -0
  66. package/dist/localization/core-messages/supplemental/fr-FR.js +35 -0
  67. package/dist/localization/core-messages/supplemental/hi-IN.js +35 -0
  68. package/dist/localization/core-messages/supplemental/ja-JP.js +35 -0
  69. package/dist/localization/core-messages/supplemental/ko-KR.js +35 -0
  70. package/dist/localization/core-messages/supplemental/pt-BR.js +35 -0
  71. package/dist/localization/core-messages/supplemental/zh-CN.js +35 -0
  72. package/dist/localization/core-messages/supplemental/zh-TW.js +35 -0
  73. package/dist/localization/default-messages.d.ts +35 -0
  74. package/dist/localization/default-messages.js +35 -0
  75. package/dist/localization/mcp-settings-messages.d.ts +35 -0
  76. package/dist/mcp/actions/list-mcp-client-connections.d.ts +8 -0
  77. package/dist/mcp/actions/list-mcp-client-connections.js +39 -0
  78. package/dist/mcp/browser-tab-target.d.ts +16 -0
  79. package/dist/mcp/browser-tab-target.js +58 -0
  80. package/dist/mcp/build-server.d.ts +1 -0
  81. package/dist/mcp/build-server.js +38 -7
  82. package/dist/mcp/call-attribution.d.ts +4 -1
  83. package/dist/mcp/call-attribution.js +3 -1
  84. package/dist/mcp/client-connections.d.ts +6 -0
  85. package/dist/mcp/client-connections.js +91 -0
  86. package/dist/mcp/connect-route.js +12 -2
  87. package/dist/mcp/connect-store.d.ts +3 -0
  88. package/dist/mcp/connect-store.js +10 -1
  89. package/dist/mcp/direct-action-call.js +1 -0
  90. package/dist/mcp/oauth-store.d.ts +6 -0
  91. package/dist/mcp/oauth-store.js +23 -0
  92. package/dist/mcp/oauth-token.d.ts +1 -0
  93. package/dist/mcp/oauth-token.js +1 -0
  94. package/dist/mcp/server.js +9 -20
  95. package/dist/mcp-client/config.d.ts +2 -0
  96. package/dist/mcp-client/manager.js +23 -0
  97. package/dist/mcp-client/workspace-servers.js +42 -10
  98. package/dist/notifications/routes.d.ts +3 -3
  99. package/dist/org/handlers.d.ts +1 -1
  100. package/dist/org/handlers.js +21 -17
  101. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  102. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  103. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  104. package/dist/provider-api/index.js +17 -36
  105. package/dist/review/actions/create-review-comment.d.ts +7 -0
  106. package/dist/review/actions/create-review-comment.js +29 -3
  107. package/dist/review/actions/delete-review-comment.js +2 -2
  108. package/dist/review/actions/list-review-comments.d.ts +3 -0
  109. package/dist/review/actions/list-review-comments.js +41 -17
  110. package/dist/review/actions/react-to-review-comment.js +2 -2
  111. package/dist/review/actions/reply-review-comment.d.ts +4 -0
  112. package/dist/review/actions/reply-review-comment.js +15 -6
  113. package/dist/review/actions/update-review-comment-anchor.d.ts +6 -0
  114. package/dist/review/actions/update-review-comment-anchor.js +39 -10
  115. package/dist/review/actions/update-review-comment.d.ts +3 -0
  116. package/dist/review/actions/update-review-comment.js +28 -3
  117. package/dist/review/errors.d.ts +33 -0
  118. package/dist/review/errors.js +51 -0
  119. package/dist/review/index.d.ts +5 -4
  120. package/dist/review/index.js +3 -2
  121. package/dist/review/registry.d.ts +51 -1
  122. package/dist/review/registry.js +117 -0
  123. package/dist/review/store.d.ts +59 -14
  124. package/dist/review/store.js +622 -101
  125. package/dist/review/suggestions/actions.js +65 -10
  126. package/dist/review/suggestions/actor-kind.d.ts +5 -0
  127. package/dist/review/suggestions/actor-kind.js +20 -0
  128. package/dist/review/suggestions/store.js +16 -2
  129. package/dist/review/suggestions/types.d.ts +13 -0
  130. package/dist/review/types.d.ts +89 -0
  131. package/dist/scripts/db/dev-query-proxy.d.ts +2 -0
  132. package/dist/scripts/db/dev-query-proxy.js +4 -1
  133. package/dist/scripts/db/query.js +3 -0
  134. package/dist/scripts/dev/shell.js +4 -0
  135. package/dist/scripts/dev-session.d.ts +31 -0
  136. package/dist/scripts/dev-session.js +58 -0
  137. package/dist/scripts/resources/list.js +53 -4
  138. package/dist/scripts/runner.js +22 -4
  139. package/dist/search/query.d.ts +11 -5
  140. package/dist/search/query.js +52 -15
  141. package/dist/search/tokenize.d.ts +1 -0
  142. package/dist/search/tokenize.js +3 -0
  143. package/dist/secrets/api-keys.d.ts +4 -0
  144. package/dist/secrets/api-keys.js +5 -0
  145. package/dist/secrets/register.d.ts +2 -0
  146. package/dist/secrets/routes.d.ts +2 -2
  147. package/dist/secrets/routes.js +85 -6
  148. package/dist/secrets/storage.d.ts +2 -0
  149. package/dist/secrets/storage.js +34 -9
  150. package/dist/secrets/substitution.d.ts +3 -2
  151. package/dist/secrets/substitution.js +56 -7
  152. package/dist/server/action-discovery.js +5 -0
  153. package/dist/server/action-routes.js +8 -6
  154. package/dist/server/agent-chat/prompt-resources.js +117 -79
  155. package/dist/server/agent-chat/script-entries.js +15 -8
  156. package/dist/server/agent-chat-plugin.js +9 -4
  157. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  158. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  159. package/dist/server/agents-bundle.d.ts +3 -1
  160. package/dist/server/agents-bundle.js +4 -4
  161. package/dist/server/csrf.js +1 -0
  162. package/dist/server/dev-action-bridge.d.ts +2 -0
  163. package/dist/server/dev-action-bridge.js +34 -5
  164. package/dist/server/first-touch-route.d.ts +1 -1
  165. package/dist/server/http-response-telemetry.js +4 -0
  166. package/dist/server/loopback.js +3 -1
  167. package/dist/server/request-context.d.ts +26 -0
  168. package/dist/shared/app-state-routes.d.ts +7 -0
  169. package/dist/shared/app-state-routes.js +7 -0
  170. package/dist/shared/mcp-client-connections.d.ts +41 -0
  171. package/dist/shared/mcp-client-connections.js +18 -0
  172. package/dist/shared/mcp-connect-content.d.ts +18 -0
  173. package/dist/shared/mcp-connect-content.js +200 -172
  174. package/dist/shared/runtime-config.js +5 -1
  175. package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  176. package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  177. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
  178. package/dist/tracking/providers.d.ts +7 -0
  179. package/dist/tracking/providers.js +39 -17
  180. package/dist/triggers/condition-evaluator.d.ts +2 -0
  181. package/dist/triggers/condition-evaluator.js +93 -3
  182. package/dist/triggers/dispatcher.js +24 -6
  183. package/docs/AGENTS.md +16 -16
  184. package/docs/content/create-with-an-agent.mdx +10 -4
  185. package/docs/content/durable-resume.mdx +11 -2
  186. package/docs/content/toolkit-comments-review.mdx +54 -1
  187. package/package.json +5 -4
  188. package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
  189. package/src/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
  190. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
@@ -341,7 +341,8 @@ async function describeSandboxExecutionForOwner(executionId, context) {
341
341
  guidance: "Background executions are scoped to the user who started them. Retry from an authenticated agent run.",
342
342
  });
343
343
  }
344
- let row = await getSandboxExecutionForOwner(executionId, owner);
344
+ const orgId = context?.orgId ?? getRequestContext()?.orgId ?? null;
345
+ let row = await getSandboxExecutionForOwner(executionId, owner, orgId);
345
346
  if (!row) {
346
347
  return structuredRunCodeError({
347
348
  executionId,
@@ -397,7 +398,16 @@ async function describeSandboxExecutionForOwner(executionId, context) {
397
398
  }, null, 2);
398
399
  }
399
400
  await failExpiredSandboxExecution(row.id, `Executor lease expired after ${row.attemptCount} attempt(s); the execution environment was likely terminated before the code finished. Split the computation into smaller chunks or persist intermediate results (e.g. workspaceWrite) and run again.`, now);
400
- row = (await getSandboxExecutionForOwner(executionId, owner)) ?? row;
401
+ const refreshed = await getSandboxExecutionForOwner(executionId, owner, orgId);
402
+ if (!refreshed) {
403
+ return structuredRunCodeError({
404
+ executionId,
405
+ code: "execution_not_found",
406
+ message: `No background execution "${executionId}" exists for this user.`,
407
+ guidance: "Check the executionId and your current organization access.",
408
+ });
409
+ }
410
+ row = refreshed;
401
411
  }
402
412
  return formatTerminalSandboxExecution(row);
403
413
  }
@@ -53,7 +53,7 @@ export declare const sandboxExecutionsStore: import("../../db/store-registry.js"
53
53
  export declare function ensureTable(): Promise<void>;
54
54
  export declare function resetSandboxExecutionsStoreForTests(): void;
55
55
  export declare function createSandboxExecution(input: CreateSandboxExecutionInput): Promise<SandboxExecutionRow>;
56
- export declare function getSandboxExecutionForOwner(id: string, owner: string): Promise<SandboxExecutionRow | null>;
56
+ export declare function getSandboxExecutionForOwner(id: string, owner: string, orgId: string | null): Promise<SandboxExecutionRow | null>;
57
57
  export declare function getSandboxExecutionInternal(id: string): Promise<SandboxExecutionRow | null>;
58
58
  export declare function claimSandboxExecution(id: string, claimToken: string, leaseMs: number, now?: number): Promise<SandboxExecutionRow | null>;
59
59
  export declare function renewSandboxExecutionLease(id: string, claimToken: string, leaseMs: number, now?: number): Promise<boolean>;
@@ -38,6 +38,7 @@ import crypto from "node:crypto";
38
38
  import { getDbExec } from "../../db/client.js";
39
39
  import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../../db/ddl-guard.js";
40
40
  import { defineStore } from "../../db/store-registry.js";
41
+ import { isOrgMember } from "../../org/membership.js";
41
42
  export const SANDBOX_EXECUTION_DEFAULT_MAX_ATTEMPTS = 2;
42
43
  export const SANDBOX_EXECUTION_MAX_STORED_OUTPUT_CHARS = 200_000;
43
44
  const TABLE = "sandbox_executions";
@@ -218,16 +219,21 @@ export async function createSandboxExecution(input) {
218
219
  throw new Error(`sandbox execution ${id} vanished after insert`);
219
220
  return row;
220
221
  }
221
- export async function getSandboxExecutionForOwner(id, owner) {
222
+ export async function getSandboxExecutionForOwner(id, owner, orgId) {
222
223
  if (!id || !owner)
223
224
  return null;
224
225
  await ensureTable();
225
226
  const client = getDbExec();
226
227
  const result = await client.execute({
227
- sql: `SELECT * FROM ${TABLE} WHERE id = ? AND owner = ? LIMIT 1`,
228
- args: [id, owner],
228
+ sql: "SELECT * FROM sandbox_executions WHERE id = ? AND owner = ? AND org_id IS NOT DISTINCT FROM ? LIMIT 1",
229
+ args: [id, owner, orgId],
229
230
  });
230
231
  const raw = result.rows?.[0];
232
+ if (raw &&
233
+ orgId &&
234
+ !(await isOrgMember(orgId, owner, { requireOrganizationMetadata: true }))) {
235
+ return null;
236
+ }
231
237
  return raw ? rowFromDb(raw) : null;
232
238
  }
233
239
  export async function getSandboxExecutionInternal(id) {
@@ -26,9 +26,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
26
26
  ok: boolean;
27
27
  }>>;
28
28
  export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- text?: undefined;
30
29
  ok?: undefined;
31
30
  error: string;
31
+ text?: undefined;
32
32
  } | {
33
33
  error?: undefined;
34
34
  ok: boolean;
@@ -329,7 +329,7 @@ async function runBackgroundProgram(program, args, paramsHash, timeoutMs) {
329
329
  const backgroundTimeout = Math.max(timeoutMs, BACKGROUND_TIMEOUT_MS);
330
330
  const active = await getActiveRun(program.id, paramsHash);
331
331
  if (active?.executionId && args.ctx.userEmail) {
332
- const execution = await getSandboxExecutionForOwner(active.executionId, args.ctx.userEmail);
332
+ const execution = await getSandboxExecutionForOwner(active.executionId, args.ctx.userEmail, args.ctx.orgId ?? null);
333
333
  if (execution &&
334
334
  execution.status !== "queued" &&
335
335
  execution.status !== "running") {
@@ -38,8 +38,9 @@ export const SCHEMA_SNAPSHOT = {
38
38
  'CREATE TABLE "agent_native_embed_session_revocations" (\n "owner_hash" text NOT NULL,\n "revoked_before" bigint NOT NULL\n);',
39
39
  'CREATE TABLE "agent_native_embed_tickets" (\n "ticket_hash" text NOT NULL,\n "owner_email" text NOT NULL,\n "org_id" text,\n "target_path" text NOT NULL,\n "scope" text,\n "created_at" bigint NOT NULL,\n "expires_at" bigint NOT NULL,\n "consumed_at" bigint,\n "renewal_expires_at" bigint,\n "session_active_until" bigint,\n "audience_host" text,\n "audience_base_path" text\n);',
40
40
  'CREATE TABLE "agent_resource_versions" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "version_number" bigint NOT NULL,\n "created_at" text NOT NULL,\n "created_by" text,\n "actor_kind" text DEFAULT \'human\'::text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "title" text,\n "summary" text,\n "snapshot_json" text NOT NULL,\n "metadata_json" text\n);',
41
+ 'CREATE TABLE "agent_review_comment_links" (\n "provider" text NOT NULL,\n "external_kind" text NOT NULL,\n "external_id" text NOT NULL,\n "comment_id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "created_at" text NOT NULL\n);',
41
42
  'CREATE TABLE "agent_review_comment_reactions" (\n "comment_id" text NOT NULL,\n "actor_email" text NOT NULL,\n "reaction" text NOT NULL,\n "created_at" text NOT NULL\n);',
42
- 'CREATE TABLE "agent_review_comments" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "thread_id" text NOT NULL,\n "parent_comment_id" text,\n "target_id" text,\n "kind" text DEFAULT \'comment\'::text NOT NULL,\n "status" text DEFAULT \'open\'::text NOT NULL,\n "anchor_json" text,\n "body" text NOT NULL,\n "author_email" text,\n "author_name" text,\n "created_by" text DEFAULT \'human\'::text NOT NULL,\n "resolution_target" text,\n "reply_route_target" text DEFAULT \'legacy\'::text,\n "notification_completed_at" text,\n "mentions_json" text,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "resolved_by" text,\n "resolved_at" text,\n "consumed_at" text,\n "deleted_by" text,\n "deleted_at" text,\n "created_at" text NOT NULL,\n "updated_at" text NOT NULL,\n "metadata_json" text\n);',
43
+ 'CREATE TABLE "agent_review_comments" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "thread_id" text NOT NULL,\n "parent_comment_id" text,\n "target_id" text,\n "kind" text DEFAULT \'comment\'::text NOT NULL,\n "status" text DEFAULT \'open\'::text NOT NULL,\n "anchor_json" text,\n "body" text NOT NULL,\n "author_email" text,\n "author_name" text,\n "created_by" text DEFAULT \'human\'::text NOT NULL,\n "resolution_target" text,\n "reply_route_target" text DEFAULT \'legacy\'::text,\n "notification_completed_at" text,\n "mentions_json" text,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "resolved_by" text,\n "resolved_at" text,\n "consumed_at" text,\n "deleted_by" text,\n "deleted_at" text,\n "created_at" text NOT NULL,\n "updated_at" text NOT NULL,\n "metadata_json" text,\n "surface_id" text,\n "anchor_kind" text,\n "anchor_version" integer,\n "revision" integer DEFAULT 1 NOT NULL\n);',
43
44
  'CREATE TABLE "agent_review_notification_deliveries" (\n "comment_id" text NOT NULL,\n "recipient_email" text NOT NULL,\n "claim_token" text NOT NULL,\n "claimed_at" text NOT NULL,\n "sent_at" text\n);',
44
45
  'CREATE TABLE "agent_review_statuses" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "status" text NOT NULL,\n "note" text,\n "updated_by" text,\n "updated_at" text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "metadata_json" text\n);',
45
46
  'CREATE TABLE "agent_review_suggestion_amendments" (\n "idempotency_key" text NOT NULL,\n "suggestion_id" text NOT NULL,\n "revision" integer NOT NULL,\n "author_email" text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "request_json" text NOT NULL,\n "before_json" text NOT NULL,\n "after_json" text NOT NULL,\n "created_at" text NOT NULL\n);',
@@ -49,7 +50,7 @@ export const SCHEMA_SNAPSHOT = {
49
50
  'CREATE TABLE "agent_review_suggestion_proposal_creations" (\n "idempotency_key" text NOT NULL,\n "proposal_id" text NOT NULL,\n "author_email" text,\n "actor_kind" text NOT NULL,\n "request_hash" text NOT NULL,\n "suggestion_ids_json" text NOT NULL\n);',
50
51
  'CREATE TABLE "agent_review_suggestion_proposal_decisions" (\n "idempotency_key" text NOT NULL,\n "proposal_id" text NOT NULL,\n "reviewer" text,\n "decision" text NOT NULL,\n "request_json" text NOT NULL,\n "suggestion_ids_json" text NOT NULL,\n "created_at" text NOT NULL\n);',
51
52
  'CREATE TABLE "agent_review_suggestion_proposals" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "adapter_kind" text NOT NULL,\n "summary" text NOT NULL,\n "author_email" text,\n "actor_kind" text NOT NULL,\n "created_at" text NOT NULL\n);',
52
- 'CREATE TABLE "agent_review_suggestions" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "adapter_kind" text NOT NULL,\n "adapter_version" integer NOT NULL,\n "thread_id" text NOT NULL,\n "author_email" text,\n "actor_kind" text NOT NULL,\n "base_revision" text NOT NULL,\n "status" text DEFAULT \'pending\'::text NOT NULL,\n "summary" text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "created_at" text NOT NULL,\n "updated_at" text NOT NULL,\n "metadata_json" text,\n "revision" integer DEFAULT 1 NOT NULL,\n "proposal_id" text\n);',
53
+ 'CREATE TABLE "agent_review_suggestions" (\n "id" text NOT NULL,\n "resource_type" text NOT NULL,\n "resource_id" text NOT NULL,\n "adapter_kind" text NOT NULL,\n "adapter_version" integer NOT NULL,\n "thread_id" text NOT NULL,\n "author_email" text,\n "actor_kind" text NOT NULL,\n "base_revision" text NOT NULL,\n "status" text DEFAULT \'pending\'::text NOT NULL,\n "summary" text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "visibility" text DEFAULT \'private\'::text NOT NULL,\n "created_at" text NOT NULL,\n "updated_at" text NOT NULL,\n "metadata_json" text,\n "revision" integer DEFAULT 1 NOT NULL,\n "proposal_id" text,\n "author_source" text,\n "author_client" text\n);',
53
54
  'CREATE TABLE "agent_review_thread_preferences" (\n "thread_id" text NOT NULL,\n "user_email" text NOT NULL,\n "muted" integer DEFAULT 0 NOT NULL,\n "unread" integer DEFAULT 0 NOT NULL,\n "updated_at" text NOT NULL\n);',
54
55
  'CREATE TABLE "agent_run_events" (\n "run_id" text NOT NULL,\n "seq" bigint NOT NULL,\n "event_at" bigint,\n "event_data" text NOT NULL,\n "transcript_order" bigint\n);',
55
56
  'CREATE TABLE "agent_run_outcome_daily" (\n "day" text NOT NULL,\n "status" text NOT NULL,\n "terminal_reason" text DEFAULT \'\'::text NOT NULL,\n "run_count" bigint DEFAULT 0 NOT NULL\n);',
@@ -112,7 +113,7 @@ export const SCHEMA_SNAPSHOT = {
112
113
  'CREATE TABLE "invitation" (\n "id" text NOT NULL,\n "organization_id" text NOT NULL,\n "email" text NOT NULL,\n "role" text,\n "status" text DEFAULT \'pending\'::text NOT NULL,\n "expires_at" timestamp with time zone NOT NULL,\n "inviter_id" text NOT NULL,\n "created_at" timestamp with time zone NOT NULL,\n "updated_at" timestamp with time zone NOT NULL\n);',
113
114
  'CREATE TABLE "jwks" (\n "id" text NOT NULL,\n "public_key" text NOT NULL,\n "private_key" text NOT NULL,\n "created_at" timestamp with time zone NOT NULL,\n "expires_at" timestamp with time zone,\n "alg" text,\n "crv" text\n);',
114
115
  'CREATE TABLE "mcp_action_approvals" (\n "nonce" text NOT NULL,\n "caller_key" text NOT NULL,\n "action_name" text NOT NULL,\n "arguments_hash" text NOT NULL,\n "expires_at" bigint NOT NULL,\n "consumed_at" bigint,\n "owner_email" text,\n "org_id" text,\n "app_id" text,\n "call_arguments" text,\n "status" text DEFAULT \'pending\'::text NOT NULL,\n "prompt_answered_at" bigint,\n "approver_email" text,\n "approved_at" bigint,\n "denied_at" bigint,\n "created_at" bigint,\n "run_status" text,\n "run_settled_at" bigint,\n "oauth_client_id" text,\n "retry_safe" boolean DEFAULT false NOT NULL\n);',
115
- 'CREATE TABLE "mcp_connect_tokens" (\n "id" text NOT NULL,\n "jti" text NOT NULL,\n "owner_email" text NOT NULL,\n "org_id" text,\n "label" text,\n "kind" text DEFAULT \'personal\'::text NOT NULL,\n "service_name" text,\n "created_by" text,\n "created_at" bigint,\n "last_used_at" bigint,\n "revoked_at" bigint\n);',
116
+ 'CREATE TABLE "mcp_connect_tokens" (\n "id" text NOT NULL,\n "jti" text NOT NULL,\n "owner_email" text NOT NULL,\n "org_id" text,\n "label" text,\n "kind" text DEFAULT \'personal\'::text NOT NULL,\n "service_name" text,\n "created_by" text,\n "created_at" bigint,\n "last_used_at" bigint,\n "revoked_at" bigint,\n "client_name" text,\n "resource" text,\n "expires_at" bigint\n);',
116
117
  'CREATE TABLE "mcp_device_codes" (\n "device_code" text NOT NULL,\n "user_code" text NOT NULL,\n "owner_email" text,\n "org_id" text,\n "status" text DEFAULT \'pending\'::text NOT NULL,\n "token_jti" text,\n "catalog_scope" text,\n "created_at" bigint,\n "expires_at" bigint,\n "consumed_at" bigint,\n "client" text,\n "purpose" text\n);',
117
118
  'CREATE TABLE "mcp_oauth_clients" (\n "client_id" text NOT NULL,\n "client_name" text,\n "redirect_uris" text NOT NULL,\n "grant_types" text,\n "response_types" text,\n "token_endpoint_auth_method" text,\n "application_type" text,\n "created_at" bigint\n);',
118
119
  'CREATE TABLE "mcp_oauth_codes" (\n "code" text NOT NULL,\n "client_id" text NOT NULL,\n "redirect_uri" text NOT NULL,\n "code_challenge" text NOT NULL,\n "code_challenge_method" text NOT NULL,\n "owner_email" text NOT NULL,\n "issued_for_email" text,\n "org_id" text,\n "org_domain" text,\n "scope" text NOT NULL,\n "resource" text NOT NULL,\n "created_at" bigint,\n "expires_at" bigint,\n "consumed_at" bigint,\n "client_name" text\n);',
@@ -212,6 +213,7 @@ export const SCHEMA_SNAPSHOT = {
212
213
  'ALTER TABLE "agent_native_embed_session_revocations" ADD CONSTRAINT "agent_native_embed_session_revocations_pkey" PRIMARY KEY (owner_hash);',
213
214
  'ALTER TABLE "agent_native_embed_tickets" ADD CONSTRAINT "agent_native_embed_tickets_pkey" PRIMARY KEY (ticket_hash);',
214
215
  'ALTER TABLE "agent_resource_versions" ADD CONSTRAINT "agent_resource_versions_pkey" PRIMARY KEY (id);',
216
+ 'ALTER TABLE "agent_review_comment_links" ADD CONSTRAINT "agent_review_comment_links_pkey" PRIMARY KEY (provider, external_kind, external_id);',
215
217
  'ALTER TABLE "agent_review_comment_reactions" ADD CONSTRAINT "agent_review_comment_reactions_pkey" PRIMARY KEY (comment_id, actor_email, reaction);',
216
218
  'ALTER TABLE "agent_review_comments" ADD CONSTRAINT "agent_review_comments_pkey" PRIMARY KEY (id);',
217
219
  'ALTER TABLE "agent_review_notification_deliveries" ADD CONSTRAINT "agent_review_notification_deliveries_pkey" PRIMARY KEY (comment_id, recipient_email);',
@@ -432,6 +434,7 @@ export const SCHEMA_SNAPSHOT = {
432
434
  "CREATE INDEX idx_agent_resource_versions_org ON public.agent_resource_versions USING btree (org_id, created_at);",
433
435
  "CREATE INDEX idx_agent_resource_versions_owner ON public.agent_resource_versions USING btree (owner_email, created_at);",
434
436
  "CREATE UNIQUE INDEX idx_agent_resource_versions_resource_number ON public.agent_resource_versions USING btree (resource_type, resource_id, version_number);",
437
+ "CREATE INDEX idx_agent_review_comment_links_comment ON public.agent_review_comment_links USING btree (comment_id);",
435
438
  "CREATE INDEX agent_review_comments_access_owner_lower_idx ON public.agent_review_comments USING btree (lower(owner_email));",
436
439
  "CREATE INDEX idx_agent_review_comments_org ON public.agent_review_comments USING btree (org_id, created_at);",
437
440
  "CREATE INDEX idx_agent_review_comments_owner ON public.agent_review_comments USING btree (owner_email, created_at);",
@@ -551,6 +554,7 @@ export const SCHEMA_SNAPSHOT = {
551
554
  "CREATE UNIQUE INDEX idx_integration_budget_subject ON public.integration_usage_budgets USING btree (partition_key, subject_type, subject_id, period);",
552
555
  "CREATE INDEX idx_integration_reservation_budget ON public.integration_usage_reservations USING btree (budget_id, window_start, status);",
553
556
  "CREATE INDEX idx_mcp_action_approvals_call ON public.mcp_action_approvals USING btree (caller_key, action_name, arguments_hash);",
557
+ "CREATE INDEX mcp_oauth_refresh_activity_idx ON public.mcp_oauth_refresh_tokens USING btree (client_id, lower(owner_email), lower(issued_for_email), org_id, resource) WHERE (revoked_at IS NULL);",
554
558
  "CREATE INDEX idx_notifications_owner_unread ON public.notifications USING btree (owner, read_at);",
555
559
  "CREATE INDEX observational_memory_access_owner_lower_idx ON public.observational_memory USING btree (lower(owner_email));",
556
560
  "CREATE INDEX observational_memory_thread_owner_idx ON public.observational_memory USING btree (thread_id, owner_email);",
@@ -683,6 +687,7 @@ export const SCHEMA_SNAPSHOT = {
683
687
  "agent_native_embed_session_revocations",
684
688
  "agent_native_embed_tickets",
685
689
  "agent_resource_versions",
690
+ "agent_review_comment_links",
686
691
  "agent_review_comment_reactions",
687
692
  "agent_review_comments",
688
693
  "agent_review_notification_deliveries",
@@ -834,6 +839,11 @@ export const SCHEMA_SNAPSHOT = {
834
839
  "67a8c1a568d3e4b8",
835
840
  ],
836
841
  ["agent_harness_sessions", "agent-harness-follow-up-recovery", null],
842
+ [
843
+ "agent_harness_sessions",
844
+ "agent-harness-sessions-approval-and-expiry-columns",
845
+ null,
846
+ ],
837
847
  [
838
848
  "agent_harness_sessions",
839
849
  "agent-harness-sessions-approval-resolution",
@@ -1096,7 +1106,9 @@ export const SCHEMA_SNAPSHOT = {
1096
1106
  ["mcp_approvals", "baseline", null],
1097
1107
  ["mcp_approvals", "review-page-columns", "5219a7b9642c4847"],
1098
1108
  ["mcp_connect", "baseline", null],
1109
+ ["mcp_connect", "client-connection-metadata", "e3be9d6fc56a1427"],
1099
1110
  ["mcp_oauth", "baseline", null],
1111
+ ["mcp_oauth", "client-activity-lookup-index", "4b5d5edf48a0f71b"],
1100
1112
  ["notifications", "baseline", null],
1101
1113
  ["oauth_tokens", "baseline", null],
1102
1114
  ["oauth_tokens", "oauth-tokens-display-name-column", "ea7cbba8dff602f8"],
@@ -1179,6 +1191,8 @@ export const SCHEMA_SNAPSHOT = {
1179
1191
  ["resources", "visibility-expires-index", null],
1180
1192
  ["resources", "widen-bigint", null],
1181
1193
  ["review", "baseline", null],
1194
+ ["review", "storage-contract", "ed0e701a19ad1c15"],
1195
+ ["review_suggestions", "author-source-and-client", null],
1182
1196
  ["review_suggestions", "baseline", null],
1183
1197
  ["sandbox_executions", "baseline", null],
1184
1198
  [
@@ -12,9 +12,7 @@ export declare function createSsrfSafeDispatcher(allowedPrivateOrigins?: readonl
12
12
  * 1. Pre-flight DNS-aware private-address check (isBlockedExtensionUrlWithDns)
13
13
  * on the initial URL and on every redirect hop.
14
14
  * 2. A connect-time dispatcher that re-checks the resolved IP at TCP-connect
15
- * time (closes the DNS-rebinding TOCTOU) when the runtime supports the
16
- * Node dispatcher. Edge runtimes retain preflight DNS checks, literal and
17
- * rebinding-domain rejection, and per-hop redirect validation.
15
+ * time (closes the DNS-rebinding TOCTOU). If unavailable, the request fails.
18
16
  * 3. Manual redirect handling — a public URL cannot 30x-redirect into the
19
17
  * private network because each hop is re-validated before it is followed.
20
18
  *
@@ -136,12 +136,11 @@ export async function isBlockedExtensionUrlWithDns(url) {
136
136
  try {
137
137
  const { lookup } = await import("node:dns/promises");
138
138
  const records = await lookup(hostname, { all: true, verbatim: true });
139
- return records.some((record) => isPrivateHost(record.address));
139
+ return (records.length === 0 ||
140
+ records.some((record) => isPrivateHost(record.address)));
140
141
  }
141
142
  catch {
142
- // Some edge runtimes do not expose DNS lookup. Keep the deterministic
143
- // parser-based protections instead of failing every outbound request.
144
- return false;
143
+ return true;
145
144
  }
146
145
  }
147
146
  function normalizeLookupHostname(hostname) {
@@ -215,10 +214,7 @@ async function createSsrfSafeDispatcherUncached(allowedPrivateOrigins = [], dest
215
214
  dnsModule = await import("node:dns");
216
215
  }
217
216
  catch (error) {
218
- if (options.required) {
219
- throw new Error("SSRF protection is unavailable because the server dispatcher could not be loaded.", { cause: error });
220
- }
221
- return null;
217
+ throw new Error("SSRF protection is unavailable because the server dispatcher could not be loaded.", { cause: error });
222
218
  }
223
219
  const { Agent } = undici;
224
220
  const { lookup } = dnsModule;
@@ -230,10 +226,7 @@ async function createSsrfSafeDispatcherUncached(allowedPrivateOrigins = [], dest
230
226
  parsed.port || (parsed.protocol === "https:" ? "443" : "80");
231
227
  }
232
228
  if (!Agent || !lookup) {
233
- if (options.required) {
234
- throw new Error("SSRF protection is unavailable because the server dispatcher is incomplete.");
235
- }
236
- return null;
229
+ throw new Error("SSRF protection is unavailable because the server dispatcher is incomplete.");
237
230
  }
238
231
  return new Agent({
239
232
  connect: {
@@ -244,6 +237,11 @@ async function createSsrfSafeDispatcherUncached(allowedPrivateOrigins = [], dest
244
237
  const list = Array.isArray(addresses)
245
238
  ? addresses
246
239
  : [{ address: addresses, family: 4 }];
240
+ if (list.length === 0 || list.some((record) => !record.address)) {
241
+ const error = new Error(`Connect blocked: ${hostname} returned no DNS address`);
242
+ error.code = "EAI_BLOCKED";
243
+ return callback(error);
244
+ }
247
245
  for (const record of list) {
248
246
  const allowedOrigin = allowedPrivateOriginKeys.has(`${normalizeLookupHostname(hostname)}:${destinationPort}`);
249
247
  const loopback = record.address === "127.0.0.1" || record.address === "::1";
@@ -273,18 +271,23 @@ export async function createSsrfSafeDispatcher(allowedPrivateOrigins = [], desti
273
271
  !allowedPrivateOrigins.includes(allowedPrivateOrigin)) {
274
272
  throw new Error("SSRF protection requires an exact localhost origin for loopback-only dispatch.");
275
273
  }
276
- options = { ...options, required: true };
277
274
  }
278
275
  if (!allowedPrivateOrigin) {
279
276
  sharedSsrfDispatcher ??= createSsrfSafeDispatcherUncached();
280
- const dispatcher = await sharedSsrfDispatcher;
281
- if (dispatcher || !options.required)
277
+ try {
278
+ const dispatcher = await sharedSsrfDispatcher;
279
+ if (!dispatcher) {
280
+ throw new Error("SSRF protection is unavailable because the server dispatcher is empty.");
281
+ }
282
282
  return dispatcher;
283
- return createSsrfSafeDispatcherUncached([], undefined, options);
283
+ }
284
+ catch (error) {
285
+ sharedSsrfDispatcher = undefined;
286
+ throw error;
287
+ }
284
288
  }
285
289
  const cacheKey = JSON.stringify([
286
290
  allowedPrivateOrigin,
287
- options.required === true,
288
291
  options.loopbackOnly === true,
289
292
  ]);
290
293
  let dispatcher = privateSsrfDispatchers.get(cacheKey);
@@ -294,8 +297,8 @@ export async function createSsrfSafeDispatcher(allowedPrivateOrigins = [], desti
294
297
  }
295
298
  try {
296
299
  const resolved = await dispatcher;
297
- if (!resolved && privateSsrfDispatchers.get(cacheKey) === dispatcher) {
298
- privateSsrfDispatchers.delete(cacheKey);
300
+ if (!resolved) {
301
+ throw new Error("SSRF protection is unavailable because the server dispatcher is empty.");
299
302
  }
300
303
  return resolved;
301
304
  }
@@ -314,9 +317,7 @@ export async function createSsrfSafeDispatcher(allowedPrivateOrigins = [], desti
314
317
  * 1. Pre-flight DNS-aware private-address check (isBlockedExtensionUrlWithDns)
315
318
  * on the initial URL and on every redirect hop.
316
319
  * 2. A connect-time dispatcher that re-checks the resolved IP at TCP-connect
317
- * time (closes the DNS-rebinding TOCTOU) when the runtime supports the
318
- * Node dispatcher. Edge runtimes retain preflight DNS checks, literal and
319
- * rebinding-domain rejection, and per-hop redirect validation.
320
+ * time (closes the DNS-rebinding TOCTOU). If unavailable, the request fails.
320
321
  * 3. Manual redirect handling — a public URL cannot 30x-redirect into the
321
322
  * private network because each hop is re-validated before it is followed.
322
323
  *
@@ -379,7 +380,6 @@ export async function ssrfSafeFetch(url, init = {}, options = {}) {
379
380
  const dispatcher = await createSsrfSafeDispatcher(allowedLocalhostOrigin
380
381
  ? [allowedLocalhostOrigin]
381
382
  : options.allowedPrivateOrigins, currentUrl, {
382
- required: Boolean(allowedLocalhostOrigin || options.requireDispatcher),
383
383
  loopbackOnly: Boolean(allowedLocalhostOrigin),
384
384
  });
385
385
  if (dispatcher)
@@ -33,21 +33,21 @@ declare const _default: import("../../action.js").ActionDefinition<{
33
33
  idempotencyKey: string;
34
34
  deleted: boolean;
35
35
  } | {
36
+ provider?: undefined;
37
+ id?: undefined;
36
38
  error: string;
37
39
  url?: undefined;
38
- id?: undefined;
39
- provider?: undefined;
40
40
  configured?: undefined;
41
41
  } | {
42
- error?: undefined;
43
42
  url: string;
44
43
  id: string | undefined;
45
44
  provider: string;
45
+ error?: undefined;
46
46
  configured?: undefined;
47
47
  } | {
48
- url?: undefined;
49
- id?: undefined;
50
48
  provider?: undefined;
49
+ id?: undefined;
50
+ url?: undefined;
51
51
  error: string;
52
52
  configured: boolean;
53
53
  }>;
@@ -2,7 +2,7 @@ export type { FileUploadDeleteInput, FileUploadInput, FileUploadProvider, FileUp
2
2
  export { registerFileUploadProvider, unregisterFileUploadProvider, listFileUploadProviders, getActiveFileUploadProvider, getActiveFileUploadProviderForRequest, deleteUploadedFile, uploadFile, } from "./registry.js";
3
3
  export { builderFileUploadProvider } from "./builder.js";
4
4
  export { getBuilderUploadCredentialStatus } from "./status.js";
5
- export { ensureS3FileUploadProvider, s3FileUploadProvider } from "./s3.js";
5
+ export { ensureS3FileUploadProvider, S3RequestError, s3FileUploadProvider, } from "./s3.js";
6
6
  export { cancelS3ResponseBody, fetchS3WithTimeout, presignS3GetUrl, readS3ErrorText, rfc3986, s3ObjectUri, sha256Hex, signS3Request, signedS3Request, trustedDeploymentS3Origin, type PresignS3GetOptions, type S3ClientConfig, type S3Method, type SignedS3RequestOptions, } from "./s3-client.js";
7
7
  export { FILE_STORAGE_SECRET_KEYS, getFileStorageStatus, saveFileStorage, clearFileStorage, type FileStorageField, type FileStorageProviderId, type FileStorageStatus, type SaveFileStorageInput, } from "./storage-settings.js";
8
8
  export { preUploadImageAttachments, preUploadAttachments, isFileUploadProviderConfigured, type PreUploadAttachmentsResult, type PreUploadedImageAttachment, type PreUploadedFileAttachment, } from "./pre-upload-attachments.js";
@@ -1,7 +1,7 @@
1
1
  export { registerFileUploadProvider, unregisterFileUploadProvider, listFileUploadProviders, getActiveFileUploadProvider, getActiveFileUploadProviderForRequest, deleteUploadedFile, uploadFile, } from "./registry.js";
2
2
  export { builderFileUploadProvider } from "./builder.js";
3
3
  export { getBuilderUploadCredentialStatus } from "./status.js";
4
- export { ensureS3FileUploadProvider, s3FileUploadProvider } from "./s3.js";
4
+ export { ensureS3FileUploadProvider, S3RequestError, s3FileUploadProvider, } from "./s3.js";
5
5
  export { cancelS3ResponseBody, fetchS3WithTimeout, presignS3GetUrl, readS3ErrorText, rfc3986, s3ObjectUri, sha256Hex, signS3Request, signedS3Request, trustedDeploymentS3Origin, } from "./s3-client.js";
6
6
  export { FILE_STORAGE_SECRET_KEYS, getFileStorageStatus, saveFileStorage, clearFileStorage, } from "./storage-settings.js";
7
7
  export { preUploadImageAttachments, preUploadAttachments, isFileUploadProviderConfigured, } from "./pre-upload-attachments.js";
@@ -22,6 +22,7 @@ export interface SignedS3RequestOptions {
22
22
  /** An HTTP Range header value, such as `bytes=0-1023`. */
23
23
  range?: string | undefined;
24
24
  timeoutMs: number;
25
+ signal?: AbortSignal | undefined;
25
26
  /**
26
27
  * The deployment configured this endpoint and its keys (not a workspace),
27
28
  * so it may sit on a private network or use plain HTTP. Without it, only
@@ -88,6 +88,10 @@ function unrefTimer(timer) {
88
88
  */
89
89
  export async function fetchS3WithTimeout(url, init, timeoutMs, trustedPrivateOrigin) {
90
90
  const controller = new AbortController();
91
+ const signal = init.signal
92
+ ? AbortSignal.any([controller.signal, init.signal])
93
+ : controller.signal;
94
+ signal.throwIfAborted();
91
95
  const headersTimeoutMs = timeoutMs + uploadAllowanceMs(init.body);
92
96
  let timeout;
93
97
  const createTimeoutError = (elapsedMs) => {
@@ -108,11 +112,15 @@ export async function fetchS3WithTimeout(url, init, timeoutMs, trustedPrivateOri
108
112
  reject(timeoutError);
109
113
  }, headersTimeoutMs);
110
114
  });
115
+ let abortHeaders;
116
+ const abortPromise = new Promise((_, reject) => {
117
+ abortHeaders = () => reject(signal.reason);
118
+ signal.addEventListener("abort", abortHeaders, { once: true });
119
+ });
111
120
  try {
112
121
  const request = (async () => {
113
122
  const isPrivateDestination = await isBlockedExtensionUrlWithDns(url);
114
- if (controller.signal.aborted)
115
- throw controller.signal.reason;
123
+ signal.throwIfAborted();
116
124
  const allowedPrivateOrigins = isPrivateDestination &&
117
125
  trustedPrivateOrigin === new URL(url).origin &&
118
126
  trustedPrivateOrigin
@@ -123,15 +131,29 @@ export async function fetchS3WithTimeout(url, init, timeoutMs, trustedPrivateOri
123
131
  }
124
132
  return ssrfSafeFetch(url, {
125
133
  ...init,
126
- signal: controller.signal,
134
+ signal,
127
135
  }, {
128
136
  followRedirects: false,
129
137
  requireDispatcher: true,
130
138
  allowedPrivateOrigins,
131
139
  });
132
140
  })();
133
- const response = await Promise.race([request, timeoutPromise]);
141
+ void request.then((response) => {
142
+ if (signal.aborted)
143
+ void response.body?.cancel().catch(() => { });
144
+ }, () => { });
145
+ const response = await Promise.race([
146
+ request,
147
+ timeoutPromise,
148
+ abortPromise,
149
+ ]);
134
150
  clearTimeoutIfRunning();
151
+ if (abortHeaders)
152
+ signal.removeEventListener("abort", abortHeaders);
153
+ if (signal.aborted) {
154
+ await response.body?.cancel().catch(() => { });
155
+ throw signal.reason;
156
+ }
135
157
  if (!response.body) {
136
158
  return response;
137
159
  }
@@ -145,14 +167,20 @@ export async function fetchS3WithTimeout(url, init, timeoutMs, trustedPrivateOri
145
167
  idleTimeout = undefined;
146
168
  }
147
169
  };
148
- const timeOutBody = () => {
170
+ const abortBody = () => {
149
171
  if (bodySettled)
150
172
  return;
151
- const timeoutError = createTimeoutError(timeoutMs);
152
173
  bodySettled = true;
153
174
  clearIdleTimeout();
154
- controller.abort(timeoutError);
155
- bodyController?.error(timeoutError);
175
+ signal.removeEventListener("abort", abortBody);
176
+ bodyController?.error(signal.reason);
177
+ void reader.cancel(signal.reason).catch(() => { });
178
+ };
179
+ signal.addEventListener("abort", abortBody, { once: true });
180
+ const timeOutBody = () => {
181
+ if (bodySettled)
182
+ return;
183
+ controller.abort(createTimeoutError(timeoutMs));
156
184
  };
157
185
  const resetIdleTimeout = () => {
158
186
  clearIdleTimeout();
@@ -162,18 +190,22 @@ export async function fetchS3WithTimeout(url, init, timeoutMs, trustedPrivateOri
162
190
  const body = new ReadableStream({
163
191
  start(streamController) {
164
192
  bodyController = streamController;
165
- resetIdleTimeout();
193
+ if (signal.aborted)
194
+ abortBody();
195
+ else
196
+ resetIdleTimeout();
166
197
  },
167
198
  async pull(streamController) {
168
199
  try {
169
- controller.signal.throwIfAborted();
200
+ signal.throwIfAborted();
170
201
  const { done, value } = await reader.read();
171
202
  if (bodySettled)
172
203
  return;
173
- controller.signal.throwIfAborted();
204
+ signal.throwIfAborted();
174
205
  if (done) {
175
206
  bodySettled = true;
176
207
  clearIdleTimeout();
208
+ signal.removeEventListener("abort", abortBody);
177
209
  streamController.close();
178
210
  }
179
211
  else {
@@ -186,12 +218,14 @@ export async function fetchS3WithTimeout(url, init, timeoutMs, trustedPrivateOri
186
218
  return;
187
219
  bodySettled = true;
188
220
  clearIdleTimeout();
189
- streamController.error(controller.signal.aborted ? controller.signal.reason : error);
221
+ signal.removeEventListener("abort", abortBody);
222
+ streamController.error(signal.aborted ? signal.reason : error);
190
223
  }
191
224
  },
192
225
  async cancel(reason) {
193
226
  bodySettled = true;
194
227
  clearIdleTimeout();
228
+ signal.removeEventListener("abort", abortBody);
195
229
  await reader.cancel(reason);
196
230
  },
197
231
  }, { highWaterMark: 0 });
@@ -203,6 +237,8 @@ export async function fetchS3WithTimeout(url, init, timeoutMs, trustedPrivateOri
203
237
  }
204
238
  catch (err) {
205
239
  clearTimeoutIfRunning();
240
+ if (abortHeaders)
241
+ signal.removeEventListener("abort", abortHeaders);
206
242
  if (err instanceof Error && err.name === "TimeoutError") {
207
243
  throw createTimeoutError(headersTimeoutMs);
208
244
  }
@@ -352,12 +388,31 @@ export async function signS3Request(config, key, options) {
352
388
  headers,
353
389
  };
354
390
  }
391
+ async function signS3RequestWithSignal(config, key, options) {
392
+ const signal = options.signal;
393
+ if (!signal)
394
+ return signS3Request(config, key, options);
395
+ signal.throwIfAborted();
396
+ let abort;
397
+ const aborted = new Promise((_, reject) => {
398
+ abort = () => reject(signal.reason);
399
+ signal.addEventListener("abort", abort, { once: true });
400
+ });
401
+ try {
402
+ return await Promise.race([signS3Request(config, key, options), aborted]);
403
+ }
404
+ finally {
405
+ if (abort)
406
+ signal.removeEventListener("abort", abort);
407
+ }
408
+ }
355
409
  /**
356
410
  * Sends a SigV4-signed request through the SSRF guard. Requests carry signed
357
411
  * credentials and private bytes, so only the deployment's own endpoint and
358
412
  * keys may sit on a private network or use plain HTTP.
359
413
  */
360
414
  export async function signedS3Request(config, key, options) {
415
+ options.signal?.throwIfAborted();
361
416
  const trustedOrigin = options.trustedEndpoint
362
417
  ? new URL(config.endpoint).origin
363
418
  : usesDeploymentS3Keys(config)
@@ -366,10 +421,12 @@ export async function signedS3Request(config, key, options) {
366
421
  if (new URL(config.endpoint).protocol !== "https:" && !trustedOrigin) {
367
422
  throw new Error("Storage endpoints saved in settings must use HTTPS");
368
423
  }
369
- const { url, headers } = await signS3Request(config, key, options);
424
+ const { url, headers } = await signS3RequestWithSignal(config, key, options);
425
+ options.signal?.throwIfAborted();
370
426
  return fetchS3WithTimeout(url, {
371
427
  method: options.method,
372
428
  headers,
429
+ signal: options.signal,
373
430
  ...(options.body
374
431
  ? {
375
432
  body: options.body.buffer.slice(options.body.byteOffset, options.body.byteOffset + options.body.byteLength),
@@ -1,3 +1,12 @@
1
1
  import type { FileUploadProvider } from "./types.js";
2
+ /**
3
+ * A request the bucket answered with an error. `status` lets callers tell a
4
+ * refused bucket (401, 403, or 404 for a missing bucket) from an outage.
5
+ */
6
+ export declare class S3RequestError extends Error {
7
+ readonly provider = "s3";
8
+ readonly status: number;
9
+ constructor(message: string, status: number);
10
+ }
2
11
  export declare const s3FileUploadProvider: FileUploadProvider;
3
12
  export declare function ensureS3FileUploadProvider(): void;
@@ -103,6 +103,19 @@ async function readRequestConfig() {
103
103
  await prefetchSecrets(STORAGE_SECRET_KEYS);
104
104
  return (await readSavedConfig()) ?? readEnvConfig();
105
105
  }
106
+ /**
107
+ * A request the bucket answered with an error. `status` lets callers tell a
108
+ * refused bucket (401, 403, or 404 for a missing bucket) from an outage.
109
+ */
110
+ export class S3RequestError extends Error {
111
+ provider = "s3";
112
+ status;
113
+ constructor(message, status) {
114
+ super(message);
115
+ this.name = "S3RequestError";
116
+ this.status = status;
117
+ }
118
+ }
106
119
  const PUT_TIMEOUT_MS = 120_000;
107
120
  const DELETE_TIMEOUT_MS = 30_000;
108
121
  function encodeKey(key) {
@@ -117,7 +130,7 @@ async function putObject(config, key, data, contentType) {
117
130
  });
118
131
  if (!response.ok) {
119
132
  const detail = await readS3ErrorText(response);
120
- throw new Error(`S3 PutObject failed (${response.status}): ${detail || response.statusText}`);
133
+ throw new S3RequestError(`S3 PutObject failed (${response.status}): ${detail || response.statusText}`, response.status);
121
134
  }
122
135
  await cancelS3ResponseBody(response);
123
136
  return `${config.publicBaseUrl}/${encodeKey(key)}`;
@@ -1,17 +1,17 @@
1
1
  {
2
- "@agent-native/agentkit": "0.227.0",
2
+ "@agent-native/agentkit": "0.228.0-nightly-20261111004142",
3
3
  "@agent-native/code-agents-ui": "0.1.75",
4
- "@agent-native/core": "0.227.0",
5
- "@agent-native/core-corpus": "0.227.0",
4
+ "@agent-native/core": "0.228.0-nightly-20261111004142",
5
+ "@agent-native/core-corpus": "0.228.0-nightly-20261111004142",
6
6
  "@agent-native/creative-context": "0.8.69",
7
- "@agent-native/dispatch": "0.42.4",
7
+ "@agent-native/dispatch": "0.42.5-nightly-20261111004142",
8
8
  "@agent-native/embedding": "0.2.0",
9
9
  "@agent-native/migrate": "0.0.7",
10
10
  "@agent-native/otel": "0.1.36",
11
11
  "@agent-native/pinpoint": "0.1.112",
12
- "@agent-native/recap-cli": "0.5.100",
13
- "@agent-native/scheduling": "0.2.56",
12
+ "@agent-native/recap-cli": "0.5.101-nightly-20261111004142",
13
+ "@agent-native/scheduling": "0.2.57-nightly-20261111004142",
14
14
  "@agent-native/shared-app-config": "0.1.2",
15
- "@agent-native/skills": "0.4.4",
16
- "@agent-native/toolkit": "0.227.0"
15
+ "@agent-native/skills": "0.4.5-nightly-20261111004142",
16
+ "@agent-native/toolkit": "0.228.0-nightly-20261111004142"
17
17
  }