@agent-native/core 0.158.9 → 0.159.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +2 -2
  2. package/corpus/templates/clips/app/i18n/en-US.ts +7 -2
  3. package/corpus/templates/clips/app/routes/record.tsx +147 -5
  4. package/dist/a2a/auth-policy.d.ts +0 -6
  5. package/dist/a2a/auth-policy.js +2 -1
  6. package/dist/a2a/client.js +10 -12
  7. package/dist/agent/durable-background.js +3 -2
  8. package/dist/agent/engine/registry.js +5 -4
  9. package/dist/agent/run-manager.js +8 -20
  10. package/dist/app-config/a2a.d.ts +14 -0
  11. package/dist/app-config/a2a.js +42 -0
  12. package/dist/app-config/agent.d.ts +18 -0
  13. package/dist/app-config/agent.js +69 -0
  14. package/dist/app-config/app.d.ts +33 -0
  15. package/dist/app-config/app.js +100 -0
  16. package/dist/app-config/describe.d.ts +26 -0
  17. package/dist/app-config/describe.js +61 -0
  18. package/dist/app-config/env-layer.d.ts +37 -0
  19. package/dist/app-config/env-layer.js +136 -0
  20. package/dist/app-config/index.d.ts +2 -0
  21. package/dist/app-config/index.js +2 -0
  22. package/dist/app-config/integrations.d.ts +5 -0
  23. package/dist/app-config/integrations.js +11 -0
  24. package/dist/app-config/private-blob.d.ts +14 -0
  25. package/dist/app-config/private-blob.js +19 -0
  26. package/dist/app-config/schema.d.ts +55 -0
  27. package/dist/app-config/schema.js +31 -0
  28. package/dist/app-config/store.d.ts +42 -0
  29. package/dist/app-config/store.js +104 -0
  30. package/dist/app-config/workspace.d.ts +21 -0
  31. package/dist/app-config/workspace.js +35 -0
  32. package/dist/cli/agent.js +2 -1
  33. package/dist/cli/code-agent-executor.js +5 -4
  34. package/dist/client/analytics.d.ts +9 -0
  35. package/dist/client/frame.js +11 -1
  36. package/dist/collab/awareness.d.ts +2 -2
  37. package/dist/deploy/build.js +36 -0
  38. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  39. package/dist/integrations/a2a-continuation-processor.js +12 -9
  40. package/dist/integrations/adapters/email.js +2 -1
  41. package/dist/integrations/adapters/telegram.js +2 -1
  42. package/dist/integrations/adapters/whatsapp.js +2 -1
  43. package/dist/integrations/webhook-handler.d.ts +7 -2
  44. package/dist/integrations/webhook-handler.js +9 -31
  45. package/dist/observability/routes.d.ts +5 -5
  46. package/dist/onboarding/app-profile.js +2 -4
  47. package/dist/onboarding/default-steps.js +4 -4
  48. package/dist/private-blob/registry.d.ts +5 -0
  49. package/dist/private-blob/registry.js +19 -8
  50. package/dist/progress/routes.d.ts +1 -1
  51. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  52. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  53. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  54. package/dist/resources/handlers.d.ts +1 -1
  55. package/dist/scripts/agent-engines/list-agent-engines.js +4 -2
  56. package/dist/secrets/routes.d.ts +9 -9
  57. package/dist/server/agent-chat/action-filters-a2a.js +5 -4
  58. package/dist/server/agent-chat/run-code-tools.js +3 -4
  59. package/dist/server/agent-chat-plugin.js +4 -3
  60. package/dist/server/agent-discovery.js +7 -5
  61. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  62. package/dist/server/app-origin-config.d.ts +23 -0
  63. package/dist/server/app-origin-config.js +43 -0
  64. package/dist/server/auth.js +21 -1
  65. package/dist/server/core-routes-plugin.js +3 -3
  66. package/dist/server/credential-provider.js +7 -8
  67. package/dist/server/embed-session.js +2 -1
  68. package/dist/server/identity-sso.js +6 -2
  69. package/dist/server/index.d.ts +1 -0
  70. package/dist/server/index.js +1 -0
  71. package/dist/server/oauth-public-origin.js +10 -14
  72. package/dist/server/oauth-return-url.js +5 -9
  73. package/dist/server/self-dispatch.js +5 -2
  74. package/dist/server/ssr-handler.js +2 -0
  75. package/dist/server/workspace-provider-oauth.js +2 -4
  76. package/dist/sharing/actions/create-agent-resource-link.js +2 -2
  77. package/dist/usage/metrics-store.js +2 -1
  78. package/package.json +3 -3
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Workspace gateway.
4
+ *
5
+ * A workspace deploy puts one gateway in front of N apps, so these are a
6
+ * *different host* from this app's own URL — not another spelling of it.
7
+ * `deploy/workspace-deploy.ts` sets them on each app it launches.
8
+ *
9
+ * Each field lists the plain and `VITE_` spelling as aliases of one value. The
10
+ * prefix only ever meant "inline this into the browser bundle", which is a
11
+ * delivery question, not a second setting — the shell projection in
12
+ * `server/app-origin-config.ts` answers that now.
13
+ *
14
+ * Length-checked but not `.url()`-validated, for the same reason as `app.url`:
15
+ * a malformed value should fail where it is used, not take every config read
16
+ * in the process down with it.
17
+ */
18
+ export declare const workspaceConfig: z.ZodObject<{
19
+ gatewayUrl: z.ZodOptional<z.ZodString>;
20
+ oauthOrigin: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strip>;
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Workspace gateway.
4
+ *
5
+ * A workspace deploy puts one gateway in front of N apps, so these are a
6
+ * *different host* from this app's own URL — not another spelling of it.
7
+ * `deploy/workspace-deploy.ts` sets them on each app it launches.
8
+ *
9
+ * Each field lists the plain and `VITE_` spelling as aliases of one value. The
10
+ * prefix only ever meant "inline this into the browser bundle", which is a
11
+ * delivery question, not a second setting — the shell projection in
12
+ * `server/app-origin-config.ts` answers that now.
13
+ *
14
+ * Length-checked but not `.url()`-validated, for the same reason as `app.url`:
15
+ * a malformed value should fail where it is used, not take every config read
16
+ * in the process down with it.
17
+ */
18
+ export const workspaceConfig = z.object({
19
+ gatewayUrl: z
20
+ .string()
21
+ .min(1)
22
+ .optional()
23
+ .meta({
24
+ env: ["WORKSPACE_GATEWAY_URL", "VITE_WORKSPACE_GATEWAY_URL"],
25
+ doc: "URL of the workspace gateway fronting this app.",
26
+ }),
27
+ oauthOrigin: z
28
+ .string()
29
+ .min(1)
30
+ .optional()
31
+ .meta({
32
+ env: ["WORKSPACE_OAUTH_ORIGIN", "VITE_WORKSPACE_OAUTH_ORIGIN"],
33
+ doc: "Shared origin workspace apps complete OAuth against.",
34
+ }),
35
+ });
package/dist/cli/agent.js CHANGED
@@ -4,6 +4,7 @@ import { DEFAULT_MODEL } from "../agent/default-model.js";
4
4
  import { registerBuiltinEngines, resolveEngine, } from "../agent/engine/index.js";
5
5
  import { actionsToEngineTools, } from "../agent/production-agent.js";
6
6
  import { runAgentLoopDirectWithSoftTimeout } from "../agent/run-loop-with-resume.js";
7
+ import { getAppConfig } from "../app-config/index.js";
7
8
  import { createGitHubRepoToolEntries } from "../provider-api/github-repo.js";
8
9
  import { resolveDevUserEmail } from "../scripts/dev-session.js";
9
10
  import { loadEnv } from "../scripts/utils.js";
@@ -143,7 +144,7 @@ async function runLocalAgentLoop(parsed) {
143
144
  const localActions = await autoDiscoverActions("auto");
144
145
  const builtinActions = await createHeadlessBuiltinActions();
145
146
  const repoActions = createGitHubRepoToolEntries({
146
- appId: process.env.AGENT_NATIVE_APP_ID ?? process.env.APP_ID ?? "app",
147
+ appId: getAppConfig().app.id ?? "app",
147
148
  });
148
149
  const actions = { ...builtinActions, ...localActions, ...repoActions };
149
150
  const tools = actionsToEngineTools(actions);
@@ -7,6 +7,7 @@ import { PROVIDER_ENV_VARS } from "../agent/engine/provider-env-vars.js";
7
7
  import { DEFAULT_AGENT_MAX_ITERATIONS } from "../agent/loop-settings.js";
8
8
  import { actionsToEngineTools, runAgentLoop, } from "../agent/production-agent.js";
9
9
  import { createToolSearchEntry, TOOL_SEARCH_ACTION_NAME, } from "../agent/tool-search.js";
10
+ import { getAppConfig } from "../app-config/index.js";
10
11
  import { formatPromptWithAttachments, } from "../code-agents/prompt-attachments.js";
11
12
  import { createCodingToolRegistry, isReadOnlyShellCommand, runCodingCommand, truncateBashOutput, truncateCodingOutput, } from "../coding-tools/index.js";
12
13
  import { buildMergedConfig, McpClientManager, mcpToolsToActionEntries, } from "../mcp-client/index.js";
@@ -103,7 +104,7 @@ export async function executeCodeAgentRun(options) {
103
104
  // Fall back to AGENT_ENGINE here too, mirroring resolveExecutorEngine below.
104
105
  // Without it, `AGENT_ENGINE=codex-cli` skips this Codex branch and is handed
105
106
  // to resolveEngine (LLM providers only), which throws `Unknown engine`.
106
- const requestedEngine = normalizeRequestedEngine(metadataString(existing, "engine") ?? process.env.AGENT_ENGINE);
107
+ const requestedEngine = normalizeRequestedEngine(metadataString(existing, "engine") ?? getAppConfig().agent.engine);
107
108
  if (requestedEngine === CODEX_CLI_ENGINE_NAME) {
108
109
  return executeCodexCliRun({
109
110
  run: existing,
@@ -178,7 +179,7 @@ export async function executeCodeAgentRun(options) {
178
179
  }
179
180
  const modelCandidate = options.model ??
180
181
  metadataString(existing, "model") ??
181
- process.env.AGENT_MODEL ??
182
+ getAppConfig().agent.model ??
182
183
  (await getStoredModelForEngine(engine).catch(() => undefined)) ??
183
184
  engine.defaultModel;
184
185
  const model = normalizeModelForEngine(engine, modelCandidate);
@@ -1794,11 +1795,11 @@ async function resolveExecutorEngine(requestedEngine) {
1794
1795
  if (!hasAnyProviderCredential())
1795
1796
  return null;
1796
1797
  return resolveEngine({
1797
- engineOption: requestedEngine ?? process.env.AGENT_ENGINE,
1798
+ engineOption: requestedEngine ?? getAppConfig().agent.engine,
1798
1799
  });
1799
1800
  }
1800
1801
  function hasAnyProviderCredential() {
1801
- if (process.env.AGENT_ENGINE)
1802
+ if (getAppConfig().agent.engine)
1802
1803
  return true;
1803
1804
  if (PROVIDER_ENV_VARS.some((key) => Boolean(process.env[key])))
1804
1805
  return true;
@@ -9,6 +9,15 @@ declare global {
9
9
  interface Window {
10
10
  gtag?: (...args: any[]) => void;
11
11
  __AGENT_NATIVE_CONFIG__?: {
12
+ /**
13
+ * This app's origins, projected by server/app-origin-config.ts. These
14
+ * replace the `VITE_` mirrors of APP_URL / WORKSPACE_* — the prefix only
15
+ * ever answered "how does this reach the browser", which the shell
16
+ * answers better. Impersonal, so safe in the CDN-cached shell.
17
+ */
18
+ appUrl?: string;
19
+ workspaceGatewayUrl?: string;
20
+ workspaceOAuthOrigin?: string;
12
21
  sentryDsn?: string;
13
22
  sentryEnvironment?: string;
14
23
  /**
@@ -186,7 +186,17 @@ function runtimeEnvValue(name) {
186
186
  : undefined;
187
187
  }
188
188
  function workspaceOAuthOrigin() {
189
- const raw = runtimeEnvValue("VITE_WORKSPACE_OAUTH_ORIGIN") ||
189
+ // The shell carries the server's declared values, so it answers first. The
190
+ // env lookups below are the pre-shell fallback and are kept for deployments
191
+ // built before the projection existed: in a browser only the `VITE_`
192
+ // spellings can ever resolve, and on the server only the plain ones are
193
+ // guaranteed — which is why the chain lists both and why it is not the
194
+ // primary path any more.
195
+ const shell = typeof window !== "undefined" ? window.__AGENT_NATIVE_CONFIG__ : undefined;
196
+ const raw = shell?.workspaceOAuthOrigin ||
197
+ shell?.appUrl ||
198
+ shell?.workspaceGatewayUrl ||
199
+ runtimeEnvValue("VITE_WORKSPACE_OAUTH_ORIGIN") ||
190
200
  runtimeEnvValue("WORKSPACE_OAUTH_ORIGIN") ||
191
201
  runtimeEnvValue("VITE_APP_URL") ||
192
202
  runtimeEnvValue("APP_URL") ||
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  states: {
66
67
  clientId: number;
67
68
  state: string;
68
69
  }[];
69
- error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
+ error?: undefined;
80
81
  users: {
81
82
  clientId: number;
82
83
  lastSeen: number;
83
84
  }[];
84
- error?: undefined;
85
85
  }>>;
@@ -964,6 +964,41 @@ function getRealtimeClientConfigScript() {
964
964
  );
965
965
  }
966
966
 
967
+ function getAppOriginClientConfigScript() {
968
+ // MUST stay consistent with resolvePublicAppOriginConfig in
969
+ // server/app-origin-config.ts, and with the alias order declared on
970
+ // app.url / workspace.* in app-config (worker bundles a string copy; it
971
+ // can't import them). Impersonal values only — this ships into the
972
+ // CDN-cached shell.
973
+ const env = globalThis.process?.env || {};
974
+ const appUrl = firstNonEmpty(
975
+ env.APP_URL,
976
+ env.VITE_APP_URL,
977
+ env.BETTER_AUTH_URL,
978
+ env.VITE_BETTER_AUTH_URL,
979
+ );
980
+ const workspaceGatewayUrl = firstNonEmpty(
981
+ env.WORKSPACE_GATEWAY_URL,
982
+ env.VITE_WORKSPACE_GATEWAY_URL,
983
+ );
984
+ const workspaceOAuthOrigin = firstNonEmpty(
985
+ env.WORKSPACE_OAUTH_ORIGIN,
986
+ env.VITE_WORKSPACE_OAUTH_ORIGIN,
987
+ );
988
+ const config = {
989
+ ...(appUrl ? { appUrl } : {}),
990
+ ...(workspaceGatewayUrl ? { workspaceGatewayUrl } : {}),
991
+ ...(workspaceOAuthOrigin ? { workspaceOAuthOrigin } : {}),
992
+ };
993
+ if (Object.keys(config).length === 0) return null;
994
+ return (
995
+ '<script data-agent-native-app-origin-config>' +
996
+ 'window.__AGENT_NATIVE_CONFIG__=Object.assign({},window.__AGENT_NATIVE_CONFIG__,' +
997
+ JSON.stringify(config) +
998
+ ");</script>"
999
+ );
1000
+ }
1001
+
967
1002
  function injectHeadScript(html, script) {
968
1003
  if (!script) return html;
969
1004
  const headCloseIdx = html.indexOf("</head>");
@@ -1105,6 +1140,7 @@ async function rewriteMountedResponse(response, basePath, pathname, request) {
1105
1140
  getSentryClientConfigScript(),
1106
1141
  getPostHogClientConfigScript(),
1107
1142
  getRealtimeClientConfigScript(),
1143
+ getAppOriginClientConfigScript(),
1108
1144
  ]
1109
1145
  .filter(Boolean)
1110
1146
  .join("") || null;
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
+ error?: undefined;
20
21
  configured?: undefined;
21
22
  connectPath?: undefined;
22
23
  url: string;
23
24
  id: string;
24
25
  provider: string;
25
- error?: undefined;
26
26
  }>;
27
27
  export default _default;
@@ -3,8 +3,10 @@ import { A2AClient, signA2AToken } from "../a2a/client.js";
3
3
  import { formatLlmCredentialErrorMessage, isLlmCredentialError, LLM_MISSING_CREDENTIALS_ERROR_CODE, } from "../agent/engine/credential-errors.js";
4
4
  import { extractThreadMeta } from "../agent/thread-data-builder.js";
5
5
  import { getThread, updateThreadData } from "../chat-threads/store.js";
6
+ import { resolveArtifactBaseUrl } from "../server/agent-chat/action-filters-a2a.js";
6
7
  import { withConfiguredAppBasePath } from "../server/app-base-path.js";
7
8
  import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
9
+ import { resolveSelfDispatchBaseUrl } from "../server/self-dispatch.js";
8
10
  import { claimA2AContinuation, claimA2AContinuationDelivery, claimDueA2AContinuations, failA2AContinuationsForIntegrationTask, failA2AContinuation, finalizeA2ATerminalHistory, getA2AContinuation, getA2AContinuationTaskOutcome, hasOnlyLegacyFailedA2AContinuationsForIntegrationTask, hasPendingConfirmedA2ADeliveryForIntegrationTask, listRecoverableA2AIntegrationTasks, recoverDueA2AContinuationIds, recordA2ATerminalDeliveryReceipt, retainA2AUnconfirmedDeliveryClaim, rescheduleA2AContinuation, saveA2AVerifiedArtifactCheckpoint, } from "./a2a-continuations-store.js";
9
11
  import { completeIntegrationCampaignTaskAfterA2A, failDisabledIntegrationCampaignTask, failIntegrationCampaignTaskDeliveryContainment, getIntegrationCampaignForTask, } from "./integration-campaigns-store.js";
10
12
  import { dispatchPendingIntegrationTask, isIntegrationDurableDispatchEnabledForTask, } from "./integration-durable-dispatch.js";
@@ -37,12 +39,17 @@ function logA2AContinuationTransition(event, continuation) {
37
39
  });
38
40
  }
39
41
  export async function dispatchA2AContinuation(continuationId, webhookBaseUrl) {
42
+ // Self-dispatch: this POST has to land on the deployment that enqueued the
43
+ // continuation. It used to carry its own chain, which omitted
44
+ // DEPLOY_PRIME_URL — so a deploy preview dispatched to production — and
45
+ // silently fell back to localhost in production, where the request simply
46
+ // never arrives and the continuation is dropped with no error.
47
+ // `WEBHOOK_BASE_URL` stays ahead of it: this runs from a retry job with no
48
+ // inbound request, and a dev tunnel is reachable where the app's own address
49
+ // is not.
40
50
  const baseUrl = webhookBaseUrl ||
41
51
  process.env.WEBHOOK_BASE_URL ||
42
- process.env.APP_URL ||
43
- process.env.URL ||
44
- process.env.DEPLOY_URL ||
45
- `http://localhost:${process.env.PORT || 3000}`;
52
+ resolveSelfDispatchBaseUrl();
46
53
  const url = `${withConfiguredAppBasePath(baseUrl)}${PROCESSOR_PATH}`;
47
54
  const headers = {
48
55
  "Content-Type": "application/json",
@@ -897,11 +904,7 @@ function formatRecoverableArtifactFallbackText(text) {
897
904
  }
898
905
  function formatContinuationArtifactText(text, agentUrl) {
899
906
  const expandedText = expandRelativeUrls(text, agentUrl);
900
- return appendA2AArtifactLinks(expandedText, [{ tool: "call-agent", result: expandedText }], { baseUrl: resolveArtifactBaseUrl() });
901
- }
902
- function resolveArtifactBaseUrl() {
903
- const baseUrl = process.env.APP_URL || process.env.URL || process.env.DEPLOY_URL;
904
- return baseUrl ? withConfiguredAppBasePath(baseUrl) : undefined;
907
+ return appendA2AArtifactLinks(expandedText, [{ tool: "call-agent", result: expandedText }], { baseUrl: resolveArtifactBaseUrl(undefined) });
905
908
  }
906
909
  function expandRelativeUrls(text, agentUrl) {
907
910
  if (!text || !agentUrl)
@@ -1,5 +1,6 @@
1
1
  import { timingSafeEqual } from "node:crypto";
2
2
  import { getHeader, readRawBody as h3ReadRawBody } from "h3";
3
+ import { getAppConfig } from "../../app-config/index.js";
3
4
  import { getDbExec } from "../../db/client.js";
4
5
  import { resolveSecret } from "../../server/credential-provider.js";
5
6
  import { sendEmail, isEmailConfigured, getEmailProvider, } from "../../server/email.js";
@@ -30,7 +31,7 @@ function escapeLike(value) {
30
31
  * webhook security audit).
31
32
  */
32
33
  function shouldRefuseWhenSecretMissing() {
33
- if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === "1")
34
+ if (getAppConfig().integrations.allowUnverifiedWebhooks)
34
35
  return false;
35
36
  return process.env.NODE_ENV === "production";
36
37
  }
@@ -1,4 +1,5 @@
1
1
  import { getHeader } from "h3";
2
+ import { getAppConfig } from "../../app-config/index.js";
2
3
  import { resolveSecret } from "../../server/credential-provider.js";
3
4
  import { readBody } from "../../server/h3-helpers.js";
4
5
  /** Telegram's max message length */
@@ -16,7 +17,7 @@ let _telegramUnverifiedWarned = false;
16
17
  * messages (C2 in the webhook security audit).
17
18
  */
18
19
  function shouldRefuseWhenSecretMissing() {
19
- if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === "1")
20
+ if (getAppConfig().integrations.allowUnverifiedWebhooks)
20
21
  return false;
21
22
  return process.env.NODE_ENV === "production";
22
23
  }
@@ -1,4 +1,5 @@
1
1
  import { getQuery, getHeader, readRawBody as h3ReadRawBody } from "h3";
2
+ import { getAppConfig } from "../../app-config/index.js";
2
3
  import { resolveSecret } from "../../server/credential-provider.js";
3
4
  /** WhatsApp's max message length */
4
5
  const WHATSAPP_MAX_LENGTH = 4096;
@@ -15,7 +16,7 @@ let _whatsappUnverifiedWarned = false;
15
16
  * be verified (C2 in the webhook security audit).
16
17
  */
17
18
  function shouldRefuseWhenSecretMissing() {
18
- if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === "1")
19
+ if (getAppConfig().integrations.allowUnverifiedWebhooks)
19
20
  return false;
20
21
  return process.env.NODE_ENV === "production";
21
22
  }
@@ -109,8 +109,13 @@ export declare function handleWebhook(event: H3Event, options: WebhookHandlerOpt
109
109
  }>;
110
110
  /**
111
111
  * Resolve the base URL we should dispatch the processor request to.
112
- * Prefers explicit env vars (most reliable on serverless), falls back to the
113
- * inbound request's headers.
112
+ *
113
+ * This is self-dispatch — the request has to land on *this* deployment — so it
114
+ * shares one resolver with the rest of the framework rather than keeping its
115
+ * own copy. The copy that used to live here preferred `APP_URL` over the
116
+ * platform's own deploy URLs and did not consult `DEPLOY_PRIME_URL` at all, so
117
+ * on a Netlify deploy preview it dispatched integration work to production
118
+ * while agent background work correctly stayed on the preview.
114
119
  */
115
120
  export declare function resolveBaseUrl(event: H3Event): string;
116
121
  /**
@@ -12,10 +12,10 @@ import { buildAssistantMessage, extractThreadMeta, threadDataToEngineMessages, }
12
12
  import { attachToolSearch } from "../agent/tool-search.js";
13
13
  import { createThread, getThread, grantThreadUserShare, setThreadSourceIfMissing, } from "../chat-threads/store.js";
14
14
  import { updateThreadData } from "../chat-threads/store.js";
15
- import { isLocalDatabase } from "../db/client.js";
16
15
  import { getOrgA2ASecret, resolveOrgIdForEmail } from "../org/context.js";
17
16
  import { withConfiguredAppBasePath } from "../server/app-base-path.js";
18
17
  import { runWithRequestContext } from "../server/request-context.js";
18
+ import { resolveSelfDispatchBaseUrl } from "../server/self-dispatch.js";
19
19
  import { normalizeReasoningEffortForRequest } from "../shared/reasoning-effort.js";
20
20
  import { A2A_CONTINUATION_QUEUED_MARKER } from "./a2a-continuation-marker.js";
21
21
  import { reconcileTerminalA2AParentIfDisabled } from "./a2a-continuation-processor.js";
@@ -360,38 +360,16 @@ async function enqueueAndDispatch(event, incoming, options, handlerStartedAt = D
360
360
  }
361
361
  /**
362
362
  * Resolve the base URL we should dispatch the processor request to.
363
- * Prefers explicit env vars (most reliable on serverless), falls back to the
364
- * inbound request's headers.
363
+ *
364
+ * This is self-dispatch — the request has to land on *this* deployment — so it
365
+ * shares one resolver with the rest of the framework rather than keeping its
366
+ * own copy. The copy that used to live here preferred `APP_URL` over the
367
+ * platform's own deploy URLs and did not consult `DEPLOY_PRIME_URL` at all, so
368
+ * on a Netlify deploy preview it dispatched integration work to production
369
+ * while agent background work correctly stayed on the preview.
365
370
  */
366
371
  export function resolveBaseUrl(event) {
367
- const fromEnv = process.env.APP_URL ||
368
- process.env.URL ||
369
- process.env.DEPLOY_URL ||
370
- process.env.BETTER_AUTH_URL;
371
- if (fromEnv)
372
- return withConfiguredAppBasePath(fromEnv);
373
- if (process.env.NODE_ENV === "production" || !isLocalDatabase()) {
374
- throw new Error("Integration self-dispatch requires APP_URL, URL, DEPLOY_URL, or BETTER_AUTH_URL in production/shared deployments.");
375
- }
376
- try {
377
- const headers = event.node?.req?.headers ?? event.headers;
378
- const get = (name) => {
379
- if (!headers)
380
- return undefined;
381
- if (typeof headers.get === "function") {
382
- return headers.get(name) ?? undefined;
383
- }
384
- const lower = String(name).toLowerCase();
385
- const map = headers;
386
- return map[name] ?? map[lower];
387
- };
388
- const proto = get("x-forwarded-proto") || "http";
389
- const host = get("host") || `localhost:${process.env.PORT || 3000}`;
390
- return withConfiguredAppBasePath(`${proto}://${host}`);
391
- }
392
- catch {
393
- return withConfiguredAppBasePath(`http://localhost:${process.env.PORT || 3000}`);
394
- }
372
+ return resolveSelfDispatchBaseUrl(event);
395
373
  }
396
374
  /**
397
375
  * Run the actual agent loop for a previously-enqueued task. Called by the
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
+ error?: undefined;
45
+ ok?: undefined;
44
46
  summary: import("./types.js").TraceSummary;
45
47
  spans: import("./types.js").TraceSpan[];
46
48
  id?: undefined;
49
+ } | {
47
50
  error?: undefined;
48
51
  ok?: undefined;
49
- } | {
50
52
  summary?: undefined;
51
53
  spans?: undefined;
52
54
  id: string;
53
- error?: undefined;
54
- ok?: undefined;
55
55
  } | {
56
+ ok?: undefined;
56
57
  summary?: undefined;
57
58
  spans?: undefined;
58
59
  id?: undefined;
59
60
  error: any;
60
- ok?: undefined;
61
61
  } | {
62
+ error?: undefined;
62
63
  summary?: undefined;
63
64
  spans?: undefined;
64
65
  id?: undefined;
65
- error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
@@ -1,3 +1,4 @@
1
+ import { getAppConfig } from "../app-config/index.js";
1
2
  const LLM_CAPABILITY = {
2
3
  id: "llm",
3
4
  label: "AI model",
@@ -382,10 +383,7 @@ function normalizeAppId(value) {
382
383
  .replace(/^-|-$/g, "") || "app");
383
384
  }
384
385
  export function resolveOnboardingAppId(explicit) {
385
- return normalizeAppId(explicit ??
386
- process.env.AGENT_NATIVE_APP_ID ??
387
- process.env.APP_ID ??
388
- process.env.npm_package_name);
386
+ return normalizeAppId(explicit ?? getAppConfig().app.id ?? getAppConfig().app.packageName);
389
387
  }
390
388
  export function getOnboardingAppProfile(appId) {
391
389
  const resolvedId = resolveOnboardingAppId(appId);
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { PROVIDER_ENV_META, PROVIDER_ENV_VARS, } from "../agent/engine/provider-env-vars.js";
9
9
  import { detectEngineFromUserSecrets, isAgentEngineSettingConfigured, } from "../agent/engine/registry.js";
10
+ import { getAppConfig } from "../app-config/index.js";
10
11
  import { getActiveFileUploadProviderForRequest, registerFileUploadProvider, } from "../file-upload/registry.js";
11
12
  import { s3FileUploadProvider } from "../file-upload/s3.js";
12
13
  import { canUseDeployCredentialFallbackForRequest, readDeployCredentialEnv, resolveSecret, } from "../server/credential-provider.js";
@@ -323,10 +324,9 @@ const githubRepositoryStep = {
323
324
  try {
324
325
  const { resolveWorkspaceConnectionCredentialForApp } = await import("../workspace-connections/index.js");
325
326
  const result = await resolveWorkspaceConnectionCredentialForApp({
326
- appId: process.env.AGENT_NATIVE_APP_ID ||
327
- process.env.APP_ID ||
328
- process.env.npm_package_name ||
329
- "app",
327
+ // Deliberately not `resolveOnboardingAppId()` — that normalizes, and
328
+ // this id is matched against a stored workspace connection grant.
329
+ appId: getAppConfig().app.id ?? getAppConfig().app.packageName ?? "app",
330
330
  provider: "github",
331
331
  key: "GITHUB_TOKEN",
332
332
  userEmail,
@@ -3,6 +3,11 @@ export declare function registerPrivateBlobProvider(provider: PrivateBlobProvide
3
3
  export declare function unregisterPrivateBlobProvider(id: string): void;
4
4
  export declare function listPrivateBlobProviders(): PrivateBlobProvider[];
5
5
  export declare function getActivePrivateBlobProvider(): PrivateBlobProvider | null;
6
+ /**
7
+ * @deprecated Use `defineAppConfig({ privateBlob: { publicUploadFallback } })`
8
+ * instead. This writes the same value into the deprecated layer of the config
9
+ * ladder, so an explicit `defineAppConfig` call now wins over it.
10
+ */
6
11
  export declare function setPrivateBlobPublicUploadFallbackEnabled(enabled: boolean): void;
7
12
  export declare function putPrivateBlob(input: PrivateBlobPutInput): Promise<PrivateBlobHandle | null>;
8
13
  export declare function readPrivateBlob(handle: PrivateBlobHandle): Promise<PrivateBlobReadResult>;
@@ -1,13 +1,12 @@
1
1
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
2
+ import { getAppConfig } from "../app-config/index.js";
3
+ import { setAppConfigLayer } from "../app-config/store.js";
2
4
  import { uploadFile } from "../file-upload/index.js";
3
5
  import { decryptSecretValue, encryptSecretValue, getSecretEncryptionKey, } from "../secrets/crypto.js";
4
6
  const PUBLIC_UPLOAD_HANDLE_PREFIX = "public-upload:v1:";
5
7
  const PUBLIC_UPLOAD_READ_RETRY_DELAYS_MS = [100, 250, 500];
6
8
  const globals = globalThis;
7
9
  const providers = (globals.__agentNativePrivateBlobProviders ??= new Map());
8
- const publicUploadFallbackRef = (globals.__agentNativePrivateBlobPublicUploadFallback ??= {
9
- enabled: true,
10
- });
11
10
  function toBytes(data) {
12
11
  return data instanceof Uint8Array ? data : new Uint8Array(data);
13
12
  }
@@ -171,24 +170,36 @@ export function listPrivateBlobProviders() {
171
170
  return [...providers.values()];
172
171
  }
173
172
  export function getActivePrivateBlobProvider() {
173
+ const selected = getAppConfig().privateBlob.provider;
174
+ if (selected) {
175
+ const provider = providers.get(selected);
176
+ if (!provider) {
177
+ throw new Error(`Private blob provider "${selected}" is selected in app config but no provider with that id is registered`);
178
+ }
179
+ return provider;
180
+ }
174
181
  for (const provider of providers.values()) {
175
182
  if (provider.isConfigured())
176
183
  return provider;
177
184
  }
178
185
  return null;
179
186
  }
187
+ /**
188
+ * @deprecated Use `defineAppConfig({ privateBlob: { publicUploadFallback } })`
189
+ * instead. This writes the same value into the deprecated layer of the config
190
+ * ladder, so an explicit `defineAppConfig` call now wins over it.
191
+ */
180
192
  export function setPrivateBlobPublicUploadFallbackEnabled(enabled) {
181
- publicUploadFallbackRef.enabled = enabled;
193
+ setAppConfigLayer("legacy", {
194
+ privateBlob: { publicUploadFallback: enabled },
195
+ });
182
196
  }
183
197
  export async function putPrivateBlob(input) {
184
198
  const provider = getActivePrivateBlobProvider();
185
199
  if (provider)
186
200
  return provider.put(input);
187
- if (!publicUploadFallbackRef.enabled)
201
+ if (!getAppConfig().privateBlob.publicUploadFallback)
188
202
  return null;
189
- if (process.env.AGENT_NATIVE_PRIVATE_BLOB_PUBLIC_UPLOAD_FALLBACK === "0") {
190
- return null;
191
- }
192
203
  return putViaEncryptedPublicUpload(input);
193
204
  }
194
205
  export async function readPrivateBlob(handle) {
@@ -15,6 +15,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- ok: boolean;
19
18
  error?: undefined;
19
+ ok: boolean;
20
20
  }>>;
@@ -75,7 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- found?: undefined;
78
+ message?: undefined;
79
+ id?: undefined;
79
80
  deleted?: undefined;
80
81
  providers: {
81
82
  id: string;
@@ -88,46 +89,45 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
88
89
  }[];
89
90
  count: number;
90
91
  provider?: undefined;
92
+ found?: undefined;
91
93
  registered?: undefined;
92
- id?: undefined;
93
94
  label?: undefined;
94
- message?: undefined;
95
95
  } | {
96
+ message?: undefined;
97
+ count?: undefined;
98
+ id?: undefined;
96
99
  deleted?: undefined;
97
100
  providers?: undefined;
98
- count?: undefined;
99
101
  found: boolean;
100
102
  provider: import("../custom-registry.js").CustomProviderConfig;
101
103
  registered?: undefined;
102
- id?: undefined;
103
104
  label?: undefined;
104
- message?: undefined;
105
105
  } | {
106
+ message?: undefined;
107
+ count?: undefined;
106
108
  deleted?: undefined;
107
109
  providers?: undefined;
108
- count?: undefined;
109
110
  provider?: undefined;
110
111
  found: boolean;
111
112
  id: string;
112
113
  registered?: undefined;
113
114
  label?: undefined;
114
- message?: undefined;
115
115
  } | {
116
- found?: undefined;
117
- providers?: undefined;
116
+ message?: undefined;
118
117
  count?: undefined;
118
+ providers?: undefined;
119
119
  provider?: undefined;
120
+ found?: undefined;
120
121
  deleted: boolean;
121
122
  id: string;
122
123
  registered?: undefined;
123
124
  label?: undefined;
124
- message?: undefined;
125
125
  } | {
126
- found?: undefined;
126
+ count?: undefined;
127
127
  deleted?: undefined;
128
128
  providers?: undefined;
129
- count?: undefined;
130
129
  provider?: undefined;
130
+ found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;