@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.
- package/dist/action.d.ts +6 -2
- package/dist/agent/engine/builder-engine.js +6 -1
- package/dist/agent/engine/tool-call-journal-seed.d.ts +2 -0
- package/dist/agent/engine/tool-call-journal-seed.js +14 -0
- package/dist/agent/harness/store.js +14 -0
- package/dist/agent/production-agent.js +257 -17
- package/dist/agent/reaped-run-thread.js +6 -0
- package/dist/agent/run-manager.js +6 -0
- package/dist/agent/tool-call-journal.d.ts +4 -0
- package/dist/agent/tool-call-journal.js +12 -0
- package/dist/agent/types.d.ts +7 -1
- package/dist/app-config/agent.d.ts +3 -0
- package/dist/app-config/agent.js +7 -0
- package/dist/app-config/schema.d.ts +3 -0
- package/dist/application-state/handlers.d.ts +3 -3
- package/dist/application-state/handlers.js +6 -1
- package/dist/application-state/index.d.ts +3 -2
- package/dist/application-state/index.js +3 -2
- package/dist/application-state/last-used-tab.d.ts +11 -0
- package/dist/application-state/last-used-tab.js +43 -0
- package/dist/application-state/script-helpers.d.ts +26 -0
- package/dist/application-state/script-helpers.js +83 -15
- package/dist/application-state/store.d.ts +8 -0
- package/dist/application-state/store.js +16 -0
- package/dist/cli/agent.js +7 -3
- package/dist/cli/code-agent-executor.d.ts +1 -0
- package/dist/cli/code-agent-executor.js +37 -2
- package/dist/cli/code.js +3 -1
- package/dist/client/agent-sidebar-state.d.ts +2 -0
- package/dist/client/browser-tab-use.d.ts +9 -0
- package/dist/client/browser-tab-use.js +112 -0
- package/dist/client/chat/runtime.js +3 -1
- package/dist/client/error-capture.js +33 -1
- package/dist/client/navigation/index.d.ts +1 -0
- package/dist/client/navigation/index.js +1 -0
- package/dist/client/resources/mcp-integration-logos.js +16 -0
- package/dist/client/review/use-review.d.ts +28 -0
- package/dist/client/review/use-review.js +41 -12
- package/dist/client/route-state.js +2 -0
- package/dist/client/sse-event-processor.d.ts +1 -0
- package/dist/client/sse-event-processor.js +7 -1
- package/dist/client/use-db-sync.d.ts +8 -1
- package/dist/client/use-db-sync.js +6 -5
- package/dist/coding-tools/run-code.js +12 -2
- package/dist/coding-tools/sandbox/executions-store.d.ts +1 -1
- package/dist/coding-tools/sandbox/executions-store.js +9 -3
- package/dist/collab/routes.d.ts +1 -1
- package/dist/data-programs/execute.js +1 -1
- package/dist/db/schema-snapshot.generated.js +17 -3
- package/dist/extensions/url-safety.d.ts +1 -3
- package/dist/extensions/url-safety.js +23 -23
- package/dist/file-upload/actions/upload-image.d.ts +5 -5
- package/dist/file-upload/index.d.ts +1 -1
- package/dist/file-upload/index.js +1 -1
- package/dist/file-upload/s3-client.d.ts +1 -0
- package/dist/file-upload/s3-client.js +70 -13
- package/dist/file-upload/s3.d.ts +9 -0
- package/dist/file-upload/s3.js +14 -1
- package/dist/framework-package-versions.json +8 -8
- package/dist/jobs/run-history.js +10 -3
- package/dist/jobs/scheduler.js +5 -36
- package/dist/localization/core-messages/supplemental/ar-SA.js +35 -0
- package/dist/localization/core-messages/supplemental/de-DE.js +35 -0
- package/dist/localization/core-messages/supplemental/en-US.js +35 -0
- package/dist/localization/core-messages/supplemental/es-ES.js +35 -0
- package/dist/localization/core-messages/supplemental/fr-FR.js +35 -0
- package/dist/localization/core-messages/supplemental/hi-IN.js +35 -0
- package/dist/localization/core-messages/supplemental/ja-JP.js +35 -0
- package/dist/localization/core-messages/supplemental/ko-KR.js +35 -0
- package/dist/localization/core-messages/supplemental/pt-BR.js +35 -0
- package/dist/localization/core-messages/supplemental/zh-CN.js +35 -0
- package/dist/localization/core-messages/supplemental/zh-TW.js +35 -0
- package/dist/localization/default-messages.d.ts +35 -0
- package/dist/localization/default-messages.js +35 -0
- package/dist/localization/mcp-settings-messages.d.ts +35 -0
- package/dist/mcp/actions/list-mcp-client-connections.d.ts +8 -0
- package/dist/mcp/actions/list-mcp-client-connections.js +39 -0
- package/dist/mcp/browser-tab-target.d.ts +16 -0
- package/dist/mcp/browser-tab-target.js +58 -0
- package/dist/mcp/build-server.d.ts +1 -0
- package/dist/mcp/build-server.js +38 -7
- package/dist/mcp/call-attribution.d.ts +4 -1
- package/dist/mcp/call-attribution.js +3 -1
- package/dist/mcp/client-connections.d.ts +6 -0
- package/dist/mcp/client-connections.js +91 -0
- package/dist/mcp/connect-route.js +12 -2
- package/dist/mcp/connect-store.d.ts +3 -0
- package/dist/mcp/connect-store.js +10 -1
- package/dist/mcp/direct-action-call.js +1 -0
- package/dist/mcp/oauth-store.d.ts +6 -0
- package/dist/mcp/oauth-store.js +23 -0
- package/dist/mcp/oauth-token.d.ts +1 -0
- package/dist/mcp/oauth-token.js +1 -0
- package/dist/mcp/server.js +9 -20
- package/dist/mcp-client/config.d.ts +2 -0
- package/dist/mcp-client/manager.js +23 -0
- package/dist/mcp-client/workspace-servers.js +42 -10
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/org/handlers.d.ts +1 -1
- package/dist/org/handlers.js +21 -17
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/provider-api/index.js +17 -36
- package/dist/review/actions/create-review-comment.d.ts +7 -0
- package/dist/review/actions/create-review-comment.js +29 -3
- package/dist/review/actions/delete-review-comment.js +2 -2
- package/dist/review/actions/list-review-comments.d.ts +3 -0
- package/dist/review/actions/list-review-comments.js +41 -17
- package/dist/review/actions/react-to-review-comment.js +2 -2
- package/dist/review/actions/reply-review-comment.d.ts +4 -0
- package/dist/review/actions/reply-review-comment.js +15 -6
- package/dist/review/actions/update-review-comment-anchor.d.ts +6 -0
- package/dist/review/actions/update-review-comment-anchor.js +39 -10
- package/dist/review/actions/update-review-comment.d.ts +3 -0
- package/dist/review/actions/update-review-comment.js +28 -3
- package/dist/review/errors.d.ts +33 -0
- package/dist/review/errors.js +51 -0
- package/dist/review/index.d.ts +5 -4
- package/dist/review/index.js +3 -2
- package/dist/review/registry.d.ts +51 -1
- package/dist/review/registry.js +117 -0
- package/dist/review/store.d.ts +59 -14
- package/dist/review/store.js +622 -101
- package/dist/review/suggestions/actions.js +65 -10
- package/dist/review/suggestions/actor-kind.d.ts +5 -0
- package/dist/review/suggestions/actor-kind.js +20 -0
- package/dist/review/suggestions/store.js +16 -2
- package/dist/review/suggestions/types.d.ts +13 -0
- package/dist/review/types.d.ts +89 -0
- package/dist/scripts/db/dev-query-proxy.d.ts +2 -0
- package/dist/scripts/db/dev-query-proxy.js +4 -1
- package/dist/scripts/db/query.js +3 -0
- package/dist/scripts/dev/shell.js +4 -0
- package/dist/scripts/dev-session.d.ts +31 -0
- package/dist/scripts/dev-session.js +58 -0
- package/dist/scripts/resources/list.js +53 -4
- package/dist/scripts/runner.js +22 -4
- package/dist/search/query.d.ts +11 -5
- package/dist/search/query.js +52 -15
- package/dist/search/tokenize.d.ts +1 -0
- package/dist/search/tokenize.js +3 -0
- package/dist/secrets/api-keys.d.ts +4 -0
- package/dist/secrets/api-keys.js +5 -0
- package/dist/secrets/register.d.ts +2 -0
- package/dist/secrets/routes.d.ts +2 -2
- package/dist/secrets/routes.js +85 -6
- package/dist/secrets/storage.d.ts +2 -0
- package/dist/secrets/storage.js +34 -9
- package/dist/secrets/substitution.d.ts +3 -2
- package/dist/secrets/substitution.js +56 -7
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/action-routes.js +8 -6
- package/dist/server/agent-chat/prompt-resources.js +117 -79
- package/dist/server/agent-chat/script-entries.js +15 -8
- package/dist/server/agent-chat-plugin.js +9 -4
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/agents-bundle.d.ts +3 -1
- package/dist/server/agents-bundle.js +4 -4
- package/dist/server/csrf.js +1 -0
- package/dist/server/dev-action-bridge.d.ts +2 -0
- package/dist/server/dev-action-bridge.js +34 -5
- package/dist/server/first-touch-route.d.ts +1 -1
- package/dist/server/http-response-telemetry.js +4 -0
- package/dist/server/loopback.js +3 -1
- package/dist/server/request-context.d.ts +26 -0
- package/dist/shared/app-state-routes.d.ts +7 -0
- package/dist/shared/app-state-routes.js +7 -0
- package/dist/shared/mcp-client-connections.d.ts +41 -0
- package/dist/shared/mcp-client-connections.js +18 -0
- package/dist/shared/mcp-connect-content.d.ts +18 -0
- package/dist/shared/mcp-connect-content.js +200 -172
- package/dist/shared/runtime-config.js +5 -1
- package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
- package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
- package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +13 -0
- package/dist/tracking/providers.d.ts +7 -0
- package/dist/tracking/providers.js +39 -17
- package/dist/triggers/condition-evaluator.d.ts +2 -0
- package/dist/triggers/condition-evaluator.js +93 -3
- package/dist/triggers/dispatcher.js +24 -6
- package/docs/AGENTS.md +16 -16
- package/docs/content/create-with-an-agent.mdx +10 -4
- package/docs/content/durable-resume.mdx +11 -2
- package/docs/content/toolkit-comments-review.mdx +54 -1
- package/package.json +5 -4
- package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +13 -0
- package/src/templates/default/.agents/skills/context-awareness/SKILL.md +13 -0
- 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
|
-
|
|
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
|
-
|
|
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:
|
|
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) {
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -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)
|
|
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.
|
|
139
|
+
return (records.length === 0 ||
|
|
140
|
+
records.some((record) => isPrivateHost(record.address)));
|
|
140
141
|
}
|
|
141
142
|
catch {
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
281
|
-
|
|
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
|
-
|
|
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
|
|
298
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
|
134
|
+
signal,
|
|
127
135
|
}, {
|
|
128
136
|
followRedirects: false,
|
|
129
137
|
requireDispatcher: true,
|
|
130
138
|
allowedPrivateOrigins,
|
|
131
139
|
});
|
|
132
140
|
})();
|
|
133
|
-
|
|
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
|
|
170
|
+
const abortBody = () => {
|
|
149
171
|
if (bodySettled)
|
|
150
172
|
return;
|
|
151
|
-
const timeoutError = createTimeoutError(timeoutMs);
|
|
152
173
|
bodySettled = true;
|
|
153
174
|
clearIdleTimeout();
|
|
154
|
-
|
|
155
|
-
bodyController?.error(
|
|
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
|
-
|
|
193
|
+
if (signal.aborted)
|
|
194
|
+
abortBody();
|
|
195
|
+
else
|
|
196
|
+
resetIdleTimeout();
|
|
166
197
|
},
|
|
167
198
|
async pull(streamController) {
|
|
168
199
|
try {
|
|
169
|
-
|
|
200
|
+
signal.throwIfAborted();
|
|
170
201
|
const { done, value } = await reader.read();
|
|
171
202
|
if (bodySettled)
|
|
172
203
|
return;
|
|
173
|
-
|
|
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
|
-
|
|
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
|
|
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),
|
package/dist/file-upload/s3.d.ts
CHANGED
|
@@ -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;
|
package/dist/file-upload/s3.js
CHANGED
|
@@ -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
|
|
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.
|
|
2
|
+
"@agent-native/agentkit": "0.228.0-nightly-20261111004142",
|
|
3
3
|
"@agent-native/code-agents-ui": "0.1.75",
|
|
4
|
-
"@agent-native/core": "0.
|
|
5
|
-
"@agent-native/core-corpus": "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.
|
|
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.
|
|
13
|
-
"@agent-native/scheduling": "0.2.
|
|
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.
|
|
16
|
-
"@agent-native/toolkit": "0.
|
|
15
|
+
"@agent-native/skills": "0.4.5-nightly-20261111004142",
|
|
16
|
+
"@agent-native/toolkit": "0.228.0-nightly-20261111004142"
|
|
17
17
|
}
|