@agent-native/core 0.119.4 → 0.119.6
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +1 -0
- package/corpus/core/src/agent/engine/index.ts +2 -0
- package/corpus/core/src/agent/engine/registry.ts +95 -2
- package/corpus/core/src/deploy/build.ts +144 -2
- package/corpus/core/src/deploy/workspace-deploy.ts +123 -1
- package/corpus/core/src/integrations/integration-durable-dispatch-config.ts +22 -0
- package/corpus/core/src/integrations/integration-durable-dispatch.ts +190 -0
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +175 -87
- package/corpus/core/src/integrations/pending-tasks-store.ts +114 -11
- package/corpus/core/src/integrations/plugin.ts +113 -60
- package/corpus/core/src/integrations/task-queue-stats.ts +68 -15
- package/corpus/core/src/integrations/webhook-handler.ts +21 -46
- package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +8 -0
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +5 -1
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +9 -1
- package/corpus/core/src/server/auth.ts +6 -0
- package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/corpus/core/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/corpus/templates/dispatch/changelog/2026-07-21-recover-stalled-slack-agent-work.md +6 -0
- package/corpus/templates/slides/actions/patch-deck.ts +2 -1
- package/corpus/templates/slides/changelog/2026-07-23-opening-or-refining-a-deck-no-longer-crashes-with-a-blank-er.md +6 -0
- package/corpus/templates/slides/shared/aspect-ratios.ts +4 -1
- package/corpus/templates/tasks/.oxfmtrc.json +8 -0
- package/corpus/templates/tasks/AGENTS.md +34 -41
- package/corpus/templates/tasks/README.md +13 -6
- package/corpus/templates/tasks/actions/bulk-delete-inbox-items.ts +10 -8
- package/corpus/templates/tasks/actions/bulk-delete-tasks.ts +10 -8
- package/corpus/templates/tasks/actions/bulk-mark-inbox-items-ready.ts +1 -1
- package/corpus/templates/tasks/actions/bulk-update-tasks.ts +12 -10
- package/corpus/templates/tasks/actions/create-inbox-item.ts +7 -4
- package/corpus/templates/tasks/actions/create-task.ts +2 -1
- package/corpus/templates/tasks/actions/delete-inbox-item.ts +6 -4
- package/corpus/templates/tasks/actions/delete-task.ts +1 -1
- package/corpus/templates/tasks/actions/list-inbox-items.ts +4 -2
- package/corpus/templates/tasks/actions/list-visible-task-fields.ts +3 -1
- package/corpus/templates/tasks/actions/mark-inbox-item-ready.ts +6 -4
- package/corpus/templates/tasks/actions/navigate.ts +16 -14
- package/corpus/templates/tasks/actions/reorder-inbox-items.ts +9 -7
- package/corpus/templates/tasks/actions/update-inbox-item.ts +6 -4
- package/corpus/templates/tasks/actions/update-task.ts +12 -9
- package/corpus/templates/tasks/actions/update-visible-task-fields.ts +10 -8
- package/corpus/templates/tasks/actions/view-screen.ts +1 -1
- package/corpus/templates/tasks/app/components/tasks/fields/TaskFieldsSidebar.tsx +15 -0
- package/corpus/templates/tasks/changelog/2026-07-23-task-detail-extensions.md +6 -0
- package/corpus/templates/tasks/drizzle.config.ts +3 -0
- package/corpus/templates/tasks/package.json +4 -1
- package/corpus/templates/tasks/server/db/schema.ts +0 -10
- package/corpus/templates/tasks/server/db/test-tasks-table.ts +0 -5
- package/corpus/templates/tasks/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/tasks/server/plugins/db.ts +43 -10
- package/corpus/templates/tasks/server/user-config/store.ts +42 -81
- package/corpus/templates/tasks/tsconfig.json +1 -2
- package/corpus/templates/tasks/vitest.config.ts +10 -1
- package/dist/agent/durable-background.d.ts +1 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +1 -0
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +32 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +75 -3
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/deploy/build.d.ts +3 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +106 -3
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/workspace-deploy.d.ts.map +1 -1
- package/dist/deploy/workspace-deploy.js +103 -2
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/integration-durable-dispatch-config.d.ts +7 -0
- package/dist/integrations/integration-durable-dispatch-config.d.ts.map +1 -0
- package/dist/integrations/integration-durable-dispatch-config.js +16 -0
- package/dist/integrations/integration-durable-dispatch-config.js.map +1 -0
- package/dist/integrations/integration-durable-dispatch.d.ts +23 -0
- package/dist/integrations/integration-durable-dispatch.d.ts.map +1 -0
- package/dist/integrations/integration-durable-dispatch.js +115 -0
- package/dist/integrations/integration-durable-dispatch.js.map +1 -0
- package/dist/integrations/pending-tasks-retry-job.d.ts +13 -1
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +133 -74
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts +14 -2
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +75 -11
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +88 -55
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/task-queue-stats.d.ts +15 -3
- package/dist/integrations/task-queue-stats.d.ts.map +1 -1
- package/dist/integrations/task-queue-stats.js +45 -15
- package/dist/integrations/task-queue-stats.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +18 -42
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +8 -2
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +5 -2
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +9 -2
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/package.json +1 -1
- package/src/agent/durable-background.ts +1 -0
- package/src/agent/engine/index.ts +2 -0
- package/src/agent/engine/registry.ts +95 -2
- package/src/deploy/build.ts +144 -2
- package/src/deploy/workspace-deploy.ts +123 -1
- package/src/integrations/integration-durable-dispatch-config.ts +22 -0
- package/src/integrations/integration-durable-dispatch.ts +190 -0
- package/src/integrations/pending-tasks-retry-job.ts +175 -87
- package/src/integrations/pending-tasks-store.ts +114 -11
- package/src/integrations/plugin.ts +113 -60
- package/src/integrations/task-queue-stats.ts +68 -15
- package/src/integrations/webhook-handler.ts +21 -46
- package/src/scripts/agent-engines/list-agent-engines.ts +8 -0
- package/src/scripts/agent-engines/manage-agent-engine.ts +5 -1
- package/src/scripts/agent-engines/set-agent-engine.ts +9 -1
- package/src/server/auth.ts +6 -0
- package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
- package/corpus/templates/tasks/.prettierrc +0 -5
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.119.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f3c3523: Add an opt-in, scoped Netlify background handoff and scheduled recovery sweep for durable messaging-integration tasks.
|
|
8
|
+
|
|
9
|
+
## 0.119.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 13eb9f7: Fix AI SDK provider engines being unusable on bundled serverless deploys, and stop rewriting custom model IDs on save/read.
|
|
14
|
+
- **Serverless package detection.** `isAgentEnginePackageInstalled` relied on `require.resolve`, which fails on bundled serverless runtimes (Vercel/Netlify via Nitro) where optional provider packages (`ai`, `@ai-sdk/*`) are inlined into the function bundle. Every engine-usability gate then rejected the AI SDK engines and the agent silently fell back to the native Anthropic engine. Package resolution now treats a resolve miss as available only when there is real evidence of a bundled runtime (Vercel/Netlify markers, or a bundle-output module path), and defers to the engine's own dynamic `import()` as the real gate. Generic container/Lambda/Cloud Run deploys that ship a real `node_modules` still surface genuine "package not installed" misses.
|
|
15
|
+
- **Custom model preservation.** `normalizeModelForEngine` replaced any unrecognized model ID with the engine default when the settings actions passed a static registry entry, so a custom OpenAI-compatible gateway model (e.g. an Ollama model) reverted to the OpenAI default on save and read. The set/list/app-default actions now resolve the OpenAI-compatible-endpoint capability (`resolveEnginePreservesCustomModels`) and pass it through, preserving custom IDs verbatim — including version-shaped IDs — while first-party OpenAI still normalizes unknown IDs to a supported model.
|
|
16
|
+
|
|
3
17
|
## 0.119.4
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.119.
|
|
3
|
+
"version": "0.119.6",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -85,6 +85,7 @@ export const AGENT_BACKGROUND_FUNCTION_URL_PATH = `/.netlify/functions/${AGENT_B
|
|
|
85
85
|
*/
|
|
86
86
|
export const AGENT_BACKGROUND_PROCESSOR_FIELD = "__agentNativeProcessor";
|
|
87
87
|
export const AGENT_BACKGROUND_PROCESSOR_A2A = "a2a";
|
|
88
|
+
export const AGENT_BACKGROUND_PROCESSOR_INTEGRATION = "integration";
|
|
88
89
|
export const AGENT_BACKGROUND_PROCESSOR_ROUTE = "route";
|
|
89
90
|
export const AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD =
|
|
90
91
|
"__agentNativeProcessorRoute";
|
|
@@ -25,6 +25,8 @@ export {
|
|
|
25
25
|
getConfiguredEngineNameForRequest,
|
|
26
26
|
getStoredModelForEngine,
|
|
27
27
|
normalizeModelForEngine,
|
|
28
|
+
resolveEnginePreservesCustomModels,
|
|
29
|
+
type NormalizeModelOptions,
|
|
28
30
|
detectEngineFromEnv,
|
|
29
31
|
detectEngineFromEnvForRequest,
|
|
30
32
|
detectEngineFromUserSecrets,
|
|
@@ -96,6 +96,42 @@ function packageNameFromInstallSpecifier(specifier: string): string | null {
|
|
|
96
96
|
return versionIndex === -1 ? trimmed : trimmed.slice(0, versionIndex);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* True only when there is positive evidence this module is executing from a
|
|
101
|
+
* bundled serverless function where optional dependencies were inlined into the
|
|
102
|
+
* bundle and are therefore NOT resolvable via `require.resolve` — even though
|
|
103
|
+
* the dynamic `import()` the engine uses to load them still works.
|
|
104
|
+
*
|
|
105
|
+
* Deliberately narrow. The Nitro Vercel/Netlify presets (which agent-native's
|
|
106
|
+
* own `deploy` command emits) inline optional peers and always set these env
|
|
107
|
+
* markers, so they are a reliable signal. Other serverless runtimes — a
|
|
108
|
+
* container on Cloud Run / Google Cloud Functions (`K_SERVICE` /
|
|
109
|
+
* `FUNCTION_TARGET`), or a plain AWS Lambda — commonly ship a real
|
|
110
|
+
* `node_modules` where `require.resolve` is authoritative; there a resolve miss
|
|
111
|
+
* means the package is genuinely absent and must NOT be masked. Those runtimes
|
|
112
|
+
* are still covered *when the code is actually bundled*, via the module-path
|
|
113
|
+
* check below, which stays false for a normal `node_modules` layout.
|
|
114
|
+
*/
|
|
115
|
+
function isBundledServerlessRuntime(): boolean {
|
|
116
|
+
const env = process.env;
|
|
117
|
+
// Nitro's Vercel/Netlify presets inline optional peers into the function
|
|
118
|
+
// bundle; these platforms always set these markers.
|
|
119
|
+
if (env.VERCEL || env.NETLIFY) return true;
|
|
120
|
+
// Otherwise require direct evidence that this module is running from inside a
|
|
121
|
+
// bundle output directory (Vercel's `/var/task`, Nitro's `.output/server`,
|
|
122
|
+
// inlined `_libs`). This is the real signal that `require.resolve` cannot be
|
|
123
|
+
// trusted; it stays false for normal `node_modules` layouts (dev, tests, and
|
|
124
|
+
// container/Lambda/Cloud Run deploys that ship their dependencies), so a
|
|
125
|
+
// genuine "package not installed" miss still surfaces there.
|
|
126
|
+
try {
|
|
127
|
+
return /[\\/](?:_libs|\.vercel|\.netlify|\.output)[\\/]|\/var\/task\//.test(
|
|
128
|
+
import.meta.url ?? "",
|
|
129
|
+
);
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
99
135
|
function canResolvePackage(packageName: string): boolean {
|
|
100
136
|
const cached = _packageAvailabilityCache.get(packageName);
|
|
101
137
|
if (cached !== undefined) return cached;
|
|
@@ -104,7 +140,15 @@ function canResolvePackage(packageName: string): boolean {
|
|
|
104
140
|
require.resolve(packageName);
|
|
105
141
|
available = true;
|
|
106
142
|
} catch {
|
|
107
|
-
|
|
143
|
+
// Bundled serverless runtimes (e.g. Nitro on Vercel/Netlify) inline optional
|
|
144
|
+
// provider packages into the function bundle, so require.resolve cannot find
|
|
145
|
+
// them even though the dynamic `import()` the engine actually uses to load
|
|
146
|
+
// them works. Treat them as available there and let the engine's own import
|
|
147
|
+
// be the real gate — it already fails with a clear "pnpm add …" message when
|
|
148
|
+
// the package is genuinely missing. Without this, every engine-usability
|
|
149
|
+
// gate rejects the AI-SDK engines at runtime and the agent silently falls
|
|
150
|
+
// back to the native Anthropic engine.
|
|
151
|
+
available = isBundledServerlessRuntime();
|
|
108
152
|
}
|
|
109
153
|
_packageAvailabilityCache.set(packageName, available);
|
|
110
154
|
return available;
|
|
@@ -179,17 +223,41 @@ function findLatestSupportedVersionMatch(
|
|
|
179
223
|
return best?.model;
|
|
180
224
|
}
|
|
181
225
|
|
|
226
|
+
export interface NormalizeModelOptions {
|
|
227
|
+
/**
|
|
228
|
+
* Force unrecognized (custom) model IDs to be kept verbatim, as if
|
|
229
|
+
* `engine.preserveCustomModels` were set on a live engine instance.
|
|
230
|
+
*
|
|
231
|
+
* The settings actions call `normalizeModelForEngine` with a static registry
|
|
232
|
+
* ENTRY, which never carries the runtime `preserveCustomModels` flag — that
|
|
233
|
+
* is only set on the engine INSTANCE created with an OpenAI-compatible
|
|
234
|
+
* `baseUrl`. They resolve the capability with
|
|
235
|
+
* {@link resolveEnginePreservesCustomModels} and pass it here so a gateway
|
|
236
|
+
* model (e.g. an Ollama `gemma4`) is not rewritten to the OpenAI default on
|
|
237
|
+
* save/read. First-party OpenAI (no gateway) leaves this unset, so an unknown
|
|
238
|
+
* or invalid model still normalizes to a supported one.
|
|
239
|
+
*/
|
|
240
|
+
preserveCustomModels?: boolean;
|
|
241
|
+
}
|
|
242
|
+
|
|
182
243
|
export function normalizeModelForEngine(
|
|
183
244
|
engine: Pick<
|
|
184
245
|
AgentEngine,
|
|
185
246
|
"name" | "defaultModel" | "supportedModels" | "preserveCustomModels"
|
|
186
247
|
>,
|
|
187
248
|
model: string | null | undefined,
|
|
249
|
+
options: NormalizeModelOptions = {},
|
|
188
250
|
): string {
|
|
189
251
|
const candidate = typeof model === "string" ? model.trim() : "";
|
|
190
252
|
if (!candidate) return engine.defaultModel;
|
|
191
253
|
|
|
192
|
-
|
|
254
|
+
// Preserve custom IDs verbatim BEFORE any catalog/version matching, so a
|
|
255
|
+
// version-shaped gateway model that happens to share a family with a
|
|
256
|
+
// built-in model (e.g. `gpt-5.4` on an OpenAI-compatible endpoint) is not
|
|
257
|
+
// rewritten to a catalog entry.
|
|
258
|
+
if (engine.preserveCustomModels || options.preserveCustomModels) {
|
|
259
|
+
return candidate;
|
|
260
|
+
}
|
|
193
261
|
|
|
194
262
|
if (engine.supportedModels.length === 0) return candidate;
|
|
195
263
|
|
|
@@ -203,6 +271,31 @@ export function normalizeModelForEngine(
|
|
|
203
271
|
);
|
|
204
272
|
}
|
|
205
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Whether models saved or read for this engine ENTRY should be preserved
|
|
276
|
+
* verbatim instead of normalized against the built-in catalog.
|
|
277
|
+
*
|
|
278
|
+
* `normalizeModelForEngine` honors a live engine's `preserveCustomModels`, but
|
|
279
|
+
* that flag is only set on an AI SDK engine INSTANCE when the OpenAI provider
|
|
280
|
+
* is pointed at an OpenAI-compatible gateway (a custom base URL — e.g. Ollama
|
|
281
|
+
* Cloud or LiteLLM), whose model IDs are not in the built-in OpenAI catalog.
|
|
282
|
+
* The static registry entry the settings actions pass to
|
|
283
|
+
* `normalizeModelForEngine` cannot carry that runtime flag, so this async
|
|
284
|
+
* helper reproduces the same decision — `ai-sdk:openai` AND a resolved base URL
|
|
285
|
+
* — from the request's stored/deploy config. First-party OpenAI (no gateway)
|
|
286
|
+
* returns false so an unknown/invalid model still normalizes to a supported one.
|
|
287
|
+
*/
|
|
288
|
+
export async function resolveEnginePreservesCustomModels(
|
|
289
|
+
entry: Pick<AgentEngineEntry, "name">,
|
|
290
|
+
): Promise<boolean> {
|
|
291
|
+
if (entry.name !== "ai-sdk:openai") return false;
|
|
292
|
+
try {
|
|
293
|
+
return Boolean(await resolveOpenAiBaseUrl());
|
|
294
|
+
} catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
206
299
|
function assertAgentEnginePackageInstalled(entry: AgentEngineEntry): void {
|
|
207
300
|
if (isAgentEnginePackageInstalled(entry)) return;
|
|
208
301
|
const installHint = entry.installPackage
|
|
@@ -24,10 +24,16 @@ import {
|
|
|
24
24
|
AGENT_BACKGROUND_FUNCTION_NAME,
|
|
25
25
|
AGENT_BACKGROUND_PROCESSOR_A2A,
|
|
26
26
|
AGENT_BACKGROUND_PROCESSOR_FIELD,
|
|
27
|
+
AGENT_BACKGROUND_PROCESSOR_INTEGRATION,
|
|
27
28
|
AGENT_BACKGROUND_PROCESSOR_ROUTE,
|
|
28
29
|
AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD,
|
|
29
30
|
AGENT_CHAT_PROCESS_RUN_PATH,
|
|
30
31
|
} from "../agent/durable-background.js";
|
|
32
|
+
import {
|
|
33
|
+
INTEGRATION_RETRY_SWEEP_PATH,
|
|
34
|
+
INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT,
|
|
35
|
+
isIntegrationDurableDispatchConfigured,
|
|
36
|
+
} from "../integrations/integration-durable-dispatch-config.js";
|
|
31
37
|
import { normalizeAppBasePath } from "../server/app-base-path.js";
|
|
32
38
|
import {
|
|
33
39
|
DEFAULT_SSR_CDN_CACHE_CONTROL,
|
|
@@ -2369,6 +2375,13 @@ export function isDurableBackgroundDeployEnabled(): boolean {
|
|
|
2369
2375
|
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
2370
2376
|
}
|
|
2371
2377
|
|
|
2378
|
+
export const NETLIFY_INTEGRATION_RECOVERY_FUNCTION_NAME =
|
|
2379
|
+
"server-integration-recovery";
|
|
2380
|
+
|
|
2381
|
+
export function isIntegrationDurableDispatchDeployEnabled(): boolean {
|
|
2382
|
+
return isIntegrationDurableDispatchConfigured();
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2372
2385
|
const NETLIFY_KEEP_WARM_FUNCTION_NAME = "agent-native-keep-warm";
|
|
2373
2386
|
|
|
2374
2387
|
/**
|
|
@@ -2536,10 +2549,16 @@ export function emitSingleTemplateNetlifyBackgroundFunction(
|
|
|
2536
2549
|
|
|
2537
2550
|
const processRunPath = JSON.stringify(AGENT_CHAT_PROCESS_RUN_PATH);
|
|
2538
2551
|
const a2aProcessTaskPath = JSON.stringify("/_agent-native/a2a/_process-task");
|
|
2552
|
+
const integrationProcessTaskPath = JSON.stringify(
|
|
2553
|
+
"/_agent-native/integrations/process-task",
|
|
2554
|
+
);
|
|
2539
2555
|
const backgroundProcessorField = JSON.stringify(
|
|
2540
2556
|
AGENT_BACKGROUND_PROCESSOR_FIELD,
|
|
2541
2557
|
);
|
|
2542
2558
|
const backgroundProcessorA2A = JSON.stringify(AGENT_BACKGROUND_PROCESSOR_A2A);
|
|
2559
|
+
const backgroundProcessorIntegration = JSON.stringify(
|
|
2560
|
+
AGENT_BACKGROUND_PROCESSOR_INTEGRATION,
|
|
2561
|
+
);
|
|
2543
2562
|
const backgroundProcessorRoute = JSON.stringify(
|
|
2544
2563
|
AGENT_BACKGROUND_PROCESSOR_ROUTE,
|
|
2545
2564
|
);
|
|
@@ -2558,8 +2577,10 @@ globalThis.__AGENT_NATIVE_BACKGROUND_RUNTIME__ = true;
|
|
|
2558
2577
|
// The framework route the Nitro router dispatches to (the _process-run plugin).
|
|
2559
2578
|
const PROCESS_RUN_PATH = ${processRunPath};
|
|
2560
2579
|
const A2A_PROCESS_TASK_PATH = ${a2aProcessTaskPath};
|
|
2580
|
+
const INTEGRATION_PROCESS_TASK_PATH = ${integrationProcessTaskPath};
|
|
2561
2581
|
const BACKGROUND_PROCESSOR_FIELD = ${backgroundProcessorField};
|
|
2562
2582
|
const BACKGROUND_PROCESSOR_A2A = ${backgroundProcessorA2A};
|
|
2583
|
+
const BACKGROUND_PROCESSOR_INTEGRATION = ${backgroundProcessorIntegration};
|
|
2563
2584
|
const BACKGROUND_PROCESSOR_ROUTE = ${backgroundProcessorRoute};
|
|
2564
2585
|
const BACKGROUND_PROCESSOR_ROUTE_FIELD = ${backgroundProcessorRouteField};
|
|
2565
2586
|
|
|
@@ -2570,6 +2591,12 @@ function processorPathFromBody(body) {
|
|
|
2570
2591
|
if (parsed?.[BACKGROUND_PROCESSOR_FIELD] === BACKGROUND_PROCESSOR_A2A) {
|
|
2571
2592
|
return A2A_PROCESS_TASK_PATH;
|
|
2572
2593
|
}
|
|
2594
|
+
if (
|
|
2595
|
+
parsed?.[BACKGROUND_PROCESSOR_FIELD] ===
|
|
2596
|
+
BACKGROUND_PROCESSOR_INTEGRATION
|
|
2597
|
+
) {
|
|
2598
|
+
return INTEGRATION_PROCESS_TASK_PATH;
|
|
2599
|
+
}
|
|
2573
2600
|
const route = parsed?.[BACKGROUND_PROCESSOR_ROUTE_FIELD];
|
|
2574
2601
|
if (
|
|
2575
2602
|
parsed?.[BACKGROUND_PROCESSOR_FIELD] === BACKGROUND_PROCESSOR_ROUTE &&
|
|
@@ -2658,6 +2685,78 @@ export const config = {
|
|
|
2658
2685
|
);
|
|
2659
2686
|
}
|
|
2660
2687
|
|
|
2688
|
+
export function emitSingleTemplateNetlifyIntegrationRecoveryFunction(
|
|
2689
|
+
projectCwd: string,
|
|
2690
|
+
): void {
|
|
2691
|
+
const internalDir = path.join(projectCwd, ".netlify", "functions-internal");
|
|
2692
|
+
const serverDir = path.join(internalDir, "server");
|
|
2693
|
+
if (!fs.existsSync(path.join(serverDir, "main.mjs"))) {
|
|
2694
|
+
console.warn(
|
|
2695
|
+
"[build] Integration recovery emit skipped: expected Nitro Netlify function " +
|
|
2696
|
+
"at .netlify/functions-internal/server/main.mjs was not found.",
|
|
2697
|
+
);
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
2700
|
+
const functionName = NETLIFY_INTEGRATION_RECOVERY_FUNCTION_NAME;
|
|
2701
|
+
const dest = path.join(internalDir, functionName);
|
|
2702
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
2703
|
+
copyDir(serverDir, dest);
|
|
2704
|
+
fs.rmSync(path.join(dest, "server.mjs"), { force: true });
|
|
2705
|
+
|
|
2706
|
+
const entry = `import { createHmac } from "node:crypto";
|
|
2707
|
+
|
|
2708
|
+
const SWEEP_PATH = ${JSON.stringify(INTEGRATION_RETRY_SWEEP_PATH)};
|
|
2709
|
+
const SWEEP_SUBJECT = ${JSON.stringify(INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT)};
|
|
2710
|
+
|
|
2711
|
+
function enabled() {
|
|
2712
|
+
const raw = process.env.AGENT_INTEGRATION_DURABLE_DISPATCH;
|
|
2713
|
+
if (!raw) return false;
|
|
2714
|
+
return ["1", "true", "yes", "on"].includes(raw.trim().toLowerCase());
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
function token(secret) {
|
|
2718
|
+
const timestamp = Date.now();
|
|
2719
|
+
const signature = createHmac("sha256", secret)
|
|
2720
|
+
.update(\`${INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT}:\${timestamp}\`)
|
|
2721
|
+
.digest("hex");
|
|
2722
|
+
return \`\${timestamp}.\${signature}\`;
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
let cachedHandler;
|
|
2726
|
+
|
|
2727
|
+
export default async function handler(request, context) {
|
|
2728
|
+
if (!enabled()) return new Response(null, { status: 204 });
|
|
2729
|
+
const secret = process.env.A2A_SECRET;
|
|
2730
|
+
if (!secret) {
|
|
2731
|
+
console.error("[integration-recovery] A2A_SECRET is required; sweep skipped");
|
|
2732
|
+
return new Response(null, { status: 204 });
|
|
2733
|
+
}
|
|
2734
|
+
cachedHandler ??= (await import("./main.mjs")).default;
|
|
2735
|
+
const url = new URL(request.url);
|
|
2736
|
+
url.pathname = SWEEP_PATH;
|
|
2737
|
+
const rewritten = new Request(url.toString(), {
|
|
2738
|
+
method: "POST",
|
|
2739
|
+
headers: {
|
|
2740
|
+
Authorization: \`Bearer \${token(secret)}\`,
|
|
2741
|
+
"Content-Type": "application/json",
|
|
2742
|
+
},
|
|
2743
|
+
body: JSON.stringify({ taskId: SWEEP_SUBJECT }),
|
|
2744
|
+
});
|
|
2745
|
+
return cachedHandler(rewritten, context);
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
export const config = {
|
|
2749
|
+
name: "integration pending-task recovery",
|
|
2750
|
+
generator: "agent-native build",
|
|
2751
|
+
schedule: "* * * * *",
|
|
2752
|
+
nodeBundler: "none",
|
|
2753
|
+
includedFiles: ["**"],
|
|
2754
|
+
preferStatic: false,
|
|
2755
|
+
};
|
|
2756
|
+
`;
|
|
2757
|
+
fs.writeFileSync(path.join(dest, `${functionName}.mjs`), entry);
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2661
2760
|
/**
|
|
2662
2761
|
* Nitro's Netlify preset can emit a harmful fallback rewrite to
|
|
2663
2762
|
* `/.netlify/functions/server`. With `config.path: "/*"`, that default URL is
|
|
@@ -2950,7 +3049,10 @@ export function assertSingleTemplateNetlifyBuildOutput(
|
|
|
2950
3049
|
);
|
|
2951
3050
|
}
|
|
2952
3051
|
|
|
2953
|
-
if (
|
|
3052
|
+
if (
|
|
3053
|
+
isDurableBackgroundDeployEnabled() ||
|
|
3054
|
+
isIntegrationDurableDispatchDeployEnabled()
|
|
3055
|
+
) {
|
|
2954
3056
|
const backgroundDir = path.join(
|
|
2955
3057
|
internalDir,
|
|
2956
3058
|
AGENT_BACKGROUND_FUNCTION_NAME,
|
|
@@ -2987,6 +3089,32 @@ export function assertSingleTemplateNetlifyBuildOutput(
|
|
|
2987
3089
|
}
|
|
2988
3090
|
}
|
|
2989
3091
|
|
|
3092
|
+
if (isIntegrationDurableDispatchDeployEnabled()) {
|
|
3093
|
+
const recoveryEntryPath = path.join(
|
|
3094
|
+
internalDir,
|
|
3095
|
+
NETLIFY_INTEGRATION_RECOVERY_FUNCTION_NAME,
|
|
3096
|
+
`${NETLIFY_INTEGRATION_RECOVERY_FUNCTION_NAME}.mjs`,
|
|
3097
|
+
);
|
|
3098
|
+
if (!fs.existsSync(recoveryEntryPath)) {
|
|
3099
|
+
failures.push(
|
|
3100
|
+
`integration durable dispatch is enabled but ${path.relative(
|
|
3101
|
+
projectCwd,
|
|
3102
|
+
recoveryEntryPath,
|
|
3103
|
+
)} was not emitted`,
|
|
3104
|
+
);
|
|
3105
|
+
} else {
|
|
3106
|
+
const recoveryEntry = fs.readFileSync(recoveryEntryPath, "utf-8");
|
|
3107
|
+
if (!/\bschedule\s*:\s*["']\* \* \* \* \*["']/.test(recoveryEntry)) {
|
|
3108
|
+
failures.push(
|
|
3109
|
+
`integration recovery entry ${path.relative(
|
|
3110
|
+
projectCwd,
|
|
3111
|
+
recoveryEntryPath,
|
|
3112
|
+
)} is missing the one-minute schedule`,
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
|
|
2990
3118
|
if (failures.length > 0) {
|
|
2991
3119
|
throw new Error(
|
|
2992
3120
|
"[deploy] Netlify deploy guard failed; refusing to publish an output " +
|
|
@@ -3638,7 +3766,10 @@ export default bundle;
|
|
|
3638
3766
|
// bundle, so the chat `_process-run` POST lands on Netlify's async (15-min)
|
|
3639
3767
|
// function. When not opted in this is a no-op and the single-function
|
|
3640
3768
|
// deploy is byte-for-byte unchanged.
|
|
3641
|
-
if (
|
|
3769
|
+
if (
|
|
3770
|
+
isDurableBackgroundDeployEnabled() ||
|
|
3771
|
+
isIntegrationDurableDispatchDeployEnabled()
|
|
3772
|
+
) {
|
|
3642
3773
|
try {
|
|
3643
3774
|
emitSingleTemplateNetlifyBackgroundFunction(cwd);
|
|
3644
3775
|
} catch (err) {
|
|
@@ -3649,6 +3780,17 @@ export default bundle;
|
|
|
3649
3780
|
}
|
|
3650
3781
|
}
|
|
3651
3782
|
|
|
3783
|
+
if (isIntegrationDurableDispatchDeployEnabled()) {
|
|
3784
|
+
try {
|
|
3785
|
+
emitSingleTemplateNetlifyIntegrationRecoveryFunction(cwd);
|
|
3786
|
+
} catch (err) {
|
|
3787
|
+
console.warn(
|
|
3788
|
+
"[build] Failed to emit integration recovery Netlify function (non-fatal):",
|
|
3789
|
+
err instanceof Error ? err.message : err,
|
|
3790
|
+
);
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3652
3794
|
writeSingleTemplateNetlifyRedirects(cwd);
|
|
3653
3795
|
assertSingleTemplateNetlifyBuildOutput(cwd);
|
|
3654
3796
|
}
|
|
@@ -21,10 +21,16 @@ import path from "path";
|
|
|
21
21
|
import {
|
|
22
22
|
AGENT_BACKGROUND_PROCESSOR_A2A,
|
|
23
23
|
AGENT_BACKGROUND_PROCESSOR_FIELD,
|
|
24
|
+
AGENT_BACKGROUND_PROCESSOR_INTEGRATION,
|
|
24
25
|
AGENT_BACKGROUND_PROCESSOR_ROUTE,
|
|
25
26
|
AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD,
|
|
26
27
|
AGENT_CHAT_PROCESS_RUN_PATH,
|
|
27
28
|
} from "../agent/durable-background.js";
|
|
29
|
+
import {
|
|
30
|
+
INTEGRATION_RETRY_SWEEP_PATH,
|
|
31
|
+
INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT,
|
|
32
|
+
isIntegrationDurableDispatchConfigured,
|
|
33
|
+
} from "../integrations/integration-durable-dispatch-config.js";
|
|
28
34
|
import { findWorkspaceRoot } from "../scripts/utils.js";
|
|
29
35
|
import {
|
|
30
36
|
DEFAULT_WORKSPACE_APP_AUDIENCE,
|
|
@@ -677,9 +683,19 @@ function copyNetlifyFunctionIntoWorkspace(
|
|
|
677
683
|
// Durable background agent runs (default-ON; opt out with a falsy
|
|
678
684
|
// AGENT_CHAT_DURABLE_BACKGROUND). Additive ONLY: when explicitly opted out
|
|
679
685
|
// this emits nothing and the single-function deploy is unchanged.
|
|
680
|
-
|
|
686
|
+
const integrationDurableDispatch =
|
|
687
|
+
app === "dispatch" && isIntegrationDurableDispatchConfigured();
|
|
688
|
+
if (isDurableBackgroundDeployEnabled() || integrationDurableDispatch) {
|
|
681
689
|
emitNetlifyBackgroundFunction(workspaceRoot, app, src, workspaceApps);
|
|
682
690
|
}
|
|
691
|
+
if (integrationDurableDispatch) {
|
|
692
|
+
emitNetlifyIntegrationRecoveryFunction(
|
|
693
|
+
workspaceRoot,
|
|
694
|
+
app,
|
|
695
|
+
src,
|
|
696
|
+
workspaceApps,
|
|
697
|
+
);
|
|
698
|
+
}
|
|
683
699
|
}
|
|
684
700
|
|
|
685
701
|
/**
|
|
@@ -753,6 +769,7 @@ function emitNetlifyBackgroundFunction(
|
|
|
753
769
|
// incoming pathname to `/<app>/_agent-native/agent-chat/_process-run`.
|
|
754
770
|
const processRunPath = `${basePath}${AGENT_CHAT_PROCESS_RUN_PATH}`;
|
|
755
771
|
const a2aProcessTaskPath = `${basePath}/_agent-native/a2a/_process-task`;
|
|
772
|
+
const integrationProcessTaskPath = `${basePath}/_agent-native/integrations/process-task`;
|
|
756
773
|
const server = `// Mark this isolate as the durable background runtime BEFORE the handler bundle
|
|
757
774
|
// is imported, so isInBackgroundFunctionRuntime() reliably returns true in this
|
|
758
775
|
// function (the deployed Lambda name is not guaranteed to end in -background). A
|
|
@@ -764,8 +781,10 @@ const basePath = ${JSON.stringify(basePath)};
|
|
|
764
781
|
// The base-path-prefixed framework route the Nitro router dispatches to.
|
|
765
782
|
const PROCESS_RUN_PATH = ${JSON.stringify(processRunPath)};
|
|
766
783
|
const A2A_PROCESS_TASK_PATH = ${JSON.stringify(a2aProcessTaskPath)};
|
|
784
|
+
const INTEGRATION_PROCESS_TASK_PATH = ${JSON.stringify(integrationProcessTaskPath)};
|
|
767
785
|
const BACKGROUND_PROCESSOR_FIELD = ${JSON.stringify(AGENT_BACKGROUND_PROCESSOR_FIELD)};
|
|
768
786
|
const BACKGROUND_PROCESSOR_A2A = ${JSON.stringify(AGENT_BACKGROUND_PROCESSOR_A2A)};
|
|
787
|
+
const BACKGROUND_PROCESSOR_INTEGRATION = ${JSON.stringify(AGENT_BACKGROUND_PROCESSOR_INTEGRATION)};
|
|
769
788
|
const BACKGROUND_PROCESSOR_ROUTE = ${JSON.stringify(AGENT_BACKGROUND_PROCESSOR_ROUTE)};
|
|
770
789
|
const BACKGROUND_PROCESSOR_ROUTE_FIELD = ${JSON.stringify(AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD)};
|
|
771
790
|
|
|
@@ -776,6 +795,12 @@ function processorPathFromBody(body) {
|
|
|
776
795
|
if (parsed?.[BACKGROUND_PROCESSOR_FIELD] === BACKGROUND_PROCESSOR_A2A) {
|
|
777
796
|
return A2A_PROCESS_TASK_PATH;
|
|
778
797
|
}
|
|
798
|
+
if (
|
|
799
|
+
parsed?.[BACKGROUND_PROCESSOR_FIELD] ===
|
|
800
|
+
BACKGROUND_PROCESSOR_INTEGRATION
|
|
801
|
+
) {
|
|
802
|
+
return INTEGRATION_PROCESS_TASK_PATH;
|
|
803
|
+
}
|
|
779
804
|
const route = parsed?.[BACKGROUND_PROCESSOR_ROUTE_FIELD];
|
|
780
805
|
if (
|
|
781
806
|
parsed?.[BACKGROUND_PROCESSOR_FIELD] === BACKGROUND_PROCESSOR_ROUTE &&
|
|
@@ -862,6 +887,103 @@ export const config = {
|
|
|
862
887
|
);
|
|
863
888
|
}
|
|
864
889
|
|
|
890
|
+
function emitNetlifyIntegrationRecoveryFunction(
|
|
891
|
+
workspaceRoot: string,
|
|
892
|
+
app: string,
|
|
893
|
+
srcServerDir: string,
|
|
894
|
+
workspaceApps: WorkspaceAppManifestEntry[],
|
|
895
|
+
): void {
|
|
896
|
+
const functionName = `${app}-integration-recovery`;
|
|
897
|
+
const dest = path.join(netlifyFunctionsDir(workspaceRoot), functionName);
|
|
898
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
899
|
+
copyDir(srcServerDir, dest);
|
|
900
|
+
fs.rmSync(path.join(dest, "server.mjs"), { force: true });
|
|
901
|
+
|
|
902
|
+
const basePath = `/${app}`;
|
|
903
|
+
const workspaceAppAudience = workspaceAppAudienceForApp(workspaceApps, app);
|
|
904
|
+
const workspaceAppRouteAccess = workspaceAppRouteAccessForApp(
|
|
905
|
+
workspaceApps,
|
|
906
|
+
app,
|
|
907
|
+
);
|
|
908
|
+
const sweepPath = `${basePath}${INTEGRATION_RETRY_SWEEP_PATH}`;
|
|
909
|
+
const entry = `import { createHmac } from "node:crypto";
|
|
910
|
+
|
|
911
|
+
const basePath = ${JSON.stringify(basePath)};
|
|
912
|
+
const SWEEP_PATH = ${JSON.stringify(sweepPath)};
|
|
913
|
+
const SWEEP_SUBJECT = ${JSON.stringify(INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT)};
|
|
914
|
+
|
|
915
|
+
function setBasePathEnv() {
|
|
916
|
+
const processRef = globalThis.process ??= { env: {} };
|
|
917
|
+
processRef.env ??= {};
|
|
918
|
+
Object.assign(processRef.env, {
|
|
919
|
+
AGENT_NATIVE_WORKSPACE: "1",
|
|
920
|
+
AGENT_NATIVE_WORKSPACE_APP_ID: ${JSON.stringify(app)},
|
|
921
|
+
APP_BASE_PATH: basePath,
|
|
922
|
+
AGENT_NATIVE_WORKSPACE_APP_AUDIENCE: ${JSON.stringify(workspaceAppAudience)},
|
|
923
|
+
AGENT_NATIVE_WORKSPACE_APP_PUBLIC_PATHS: ${JSON.stringify(JSON.stringify(workspaceAppRouteAccess.publicPaths))},
|
|
924
|
+
AGENT_NATIVE_WORKSPACE_APP_PROTECTED_PATHS: ${JSON.stringify(JSON.stringify(workspaceAppRouteAccess.protectedPaths))},
|
|
925
|
+
VITE_AGENT_NATIVE_WORKSPACE: "1",
|
|
926
|
+
VITE_AGENT_NATIVE_WORKSPACE_APP_ID: ${JSON.stringify(app)},
|
|
927
|
+
VITE_APP_BASE_PATH: basePath,
|
|
928
|
+
VITE_AGENT_NATIVE_WORKSPACE_APP_AUDIENCE: ${JSON.stringify(workspaceAppAudience)},
|
|
929
|
+
VITE_AGENT_NATIVE_WORKSPACE_APP_PUBLIC_PATHS: ${JSON.stringify(JSON.stringify(workspaceAppRouteAccess.publicPaths))},
|
|
930
|
+
VITE_AGENT_NATIVE_WORKSPACE_APP_PROTECTED_PATHS: ${JSON.stringify(JSON.stringify(workspaceAppRouteAccess.protectedPaths))},
|
|
931
|
+
VITE_AGENT_NATIVE_WORKSPACE_APPS_JSON: ${JSON.stringify(JSON.stringify(workspaceApps))},
|
|
932
|
+
${JSON.stringify(WORKSPACE_APPS_ENV_KEY)}: ${JSON.stringify(JSON.stringify(workspaceApps))},
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function enabled() {
|
|
937
|
+
const raw = process.env.AGENT_INTEGRATION_DURABLE_DISPATCH;
|
|
938
|
+
if (!raw) return false;
|
|
939
|
+
return ["1", "true", "yes", "on"].includes(raw.trim().toLowerCase());
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function token(secret) {
|
|
943
|
+
const timestamp = Date.now();
|
|
944
|
+
const signature = createHmac("sha256", secret)
|
|
945
|
+
.update(\`${INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT}:\${timestamp}\`)
|
|
946
|
+
.digest("hex");
|
|
947
|
+
return \`\${timestamp}.\${signature}\`;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
setBasePathEnv();
|
|
951
|
+
let cachedHandler;
|
|
952
|
+
|
|
953
|
+
export default async function handler(request, context) {
|
|
954
|
+
setBasePathEnv();
|
|
955
|
+
if (!enabled()) return new Response(null, { status: 204 });
|
|
956
|
+
const secret = process.env.A2A_SECRET;
|
|
957
|
+
if (!secret) {
|
|
958
|
+
console.error("[integration-recovery] A2A_SECRET is required; sweep skipped");
|
|
959
|
+
return new Response(null, { status: 204 });
|
|
960
|
+
}
|
|
961
|
+
cachedHandler ??= (await import("./main.mjs")).default;
|
|
962
|
+
const url = new URL(request.url);
|
|
963
|
+
url.pathname = SWEEP_PATH;
|
|
964
|
+
const rewritten = new Request(url.toString(), {
|
|
965
|
+
method: "POST",
|
|
966
|
+
headers: {
|
|
967
|
+
Authorization: \`Bearer \${token(secret)}\`,
|
|
968
|
+
"Content-Type": "application/json",
|
|
969
|
+
},
|
|
970
|
+
body: JSON.stringify({ taskId: SWEEP_SUBJECT }),
|
|
971
|
+
});
|
|
972
|
+
return cachedHandler(rewritten, context);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
export const config = {
|
|
976
|
+
name: ${JSON.stringify(`${app} integration pending-task recovery`)},
|
|
977
|
+
generator: "agent-native workspace deploy",
|
|
978
|
+
schedule: "* * * * *",
|
|
979
|
+
nodeBundler: "none",
|
|
980
|
+
includedFiles: ["**"],
|
|
981
|
+
preferStatic: false,
|
|
982
|
+
};
|
|
983
|
+
`;
|
|
984
|
+
fs.writeFileSync(path.join(dest, `${functionName}.mjs`), entry);
|
|
985
|
+
}
|
|
986
|
+
|
|
865
987
|
function patchNetlifyFunctionEntry(
|
|
866
988
|
functionDir: string,
|
|
867
989
|
app: string,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const INTEGRATION_DURABLE_DISPATCH_ENV =
|
|
2
|
+
"AGENT_INTEGRATION_DURABLE_DISPATCH";
|
|
3
|
+
export const INTEGRATION_DURABLE_DISPATCH_SCOPES_ENV =
|
|
4
|
+
"AGENT_INTEGRATION_DURABLE_DISPATCH_SCOPES";
|
|
5
|
+
export const INTEGRATION_PROCESS_TASK_PATH =
|
|
6
|
+
"/_agent-native/integrations/process-task";
|
|
7
|
+
export const INTEGRATION_RETRY_SWEEP_PATH =
|
|
8
|
+
"/_agent-native/integrations/retry-stuck-tasks";
|
|
9
|
+
export const INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT =
|
|
10
|
+
"integration-pending-tasks-sweep";
|
|
11
|
+
|
|
12
|
+
export function isIntegrationDurableDispatchConfigured(): boolean {
|
|
13
|
+
const value = process.env.AGENT_INTEGRATION_DURABLE_DISPATCH;
|
|
14
|
+
if (!value) return false;
|
|
15
|
+
const normalized = value.trim().toLowerCase();
|
|
16
|
+
return (
|
|
17
|
+
normalized === "1" ||
|
|
18
|
+
normalized === "true" ||
|
|
19
|
+
normalized === "yes" ||
|
|
20
|
+
normalized === "on"
|
|
21
|
+
);
|
|
22
|
+
}
|