@agent-native/core 0.70.3 → 0.71.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 (79) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +13 -0
  3. package/corpus/core/docs/content/multi-app-workspace.md +2 -2
  4. package/corpus/core/docs/design/durable-agent-runs.md +458 -4
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/agent/durable-background.ts +192 -0
  7. package/corpus/core/src/agent/production-agent.ts +330 -13
  8. package/corpus/core/src/agent/run-manager.ts +66 -3
  9. package/corpus/core/src/agent/run-store.ts +129 -23
  10. package/corpus/core/src/agent/types.ts +20 -0
  11. package/corpus/core/src/client/AgentPanel.tsx +12 -9
  12. package/corpus/core/src/client/blocks/library/FileTreeBlock.tsx +72 -14
  13. package/corpus/core/src/deploy/build.ts +96 -0
  14. package/corpus/core/src/deploy/workspace-deploy.ts +121 -0
  15. package/corpus/core/src/mcp/build-server.ts +22 -13
  16. package/corpus/core/src/server/agent-chat-plugin.ts +133 -66
  17. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -0
  18. package/corpus/templates/analytics/AGENTS.md +8 -0
  19. package/corpus/templates/analytics/actions/compose-dashboard.ts +317 -0
  20. package/corpus/templates/analytics/changelog/2026-06-23-build-large-first-party-analytics-dashboards-in-one-fast-cal.md +6 -0
  21. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +574 -0
  22. package/corpus/templates/clips/changelog/2026-06-23-dragging-the-desktop-camera-bubble-now-glides-to-a-stop-at-t.md +6 -0
  23. package/corpus/templates/clips/chrome-extension/src/background.ts +130 -32
  24. package/corpus/templates/clips/chrome-extension/src/content-script.ts +163 -13
  25. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +195 -34
  26. package/corpus/templates/clips/chrome-extension/src/overlay.css +73 -23
  27. package/corpus/templates/clips/chrome-extension/src/overlay.ts +82 -13
  28. package/corpus/templates/clips/chrome-extension/src/popup.html +62 -5
  29. package/corpus/templates/clips/chrome-extension/src/popup.ts +290 -1
  30. package/corpus/templates/clips/chrome-extension/src/styles.css +34 -0
  31. package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +21 -16
  32. package/corpus/templates/clips/desktop/src/lib/recorder.ts +10 -17
  33. package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +73 -23
  34. package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +0 -6
  35. package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +56 -46
  36. package/corpus/templates/clips/desktop/src/styles.css +33 -5
  37. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +113 -0
  38. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -0
  39. package/dist/agent/durable-background.d.ts +60 -0
  40. package/dist/agent/durable-background.d.ts.map +1 -0
  41. package/dist/agent/durable-background.js +144 -0
  42. package/dist/agent/durable-background.js.map +1 -0
  43. package/dist/agent/production-agent.d.ts +20 -0
  44. package/dist/agent/production-agent.d.ts.map +1 -1
  45. package/dist/agent/production-agent.js +292 -14
  46. package/dist/agent/production-agent.js.map +1 -1
  47. package/dist/agent/run-manager.d.ts +48 -0
  48. package/dist/agent/run-manager.d.ts.map +1 -1
  49. package/dist/agent/run-manager.js +45 -3
  50. package/dist/agent/run-manager.js.map +1 -1
  51. package/dist/agent/run-store.d.ts +30 -1
  52. package/dist/agent/run-store.d.ts.map +1 -1
  53. package/dist/agent/run-store.js +124 -24
  54. package/dist/agent/run-store.js.map +1 -1
  55. package/dist/agent/types.d.ts +20 -0
  56. package/dist/agent/types.d.ts.map +1 -1
  57. package/dist/agent/types.js.map +1 -1
  58. package/dist/client/AgentPanel.d.ts.map +1 -1
  59. package/dist/client/AgentPanel.js +2 -2
  60. package/dist/client/AgentPanel.js.map +1 -1
  61. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
  62. package/dist/client/blocks/library/FileTreeBlock.js +37 -4
  63. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
  64. package/dist/deploy/build.d.ts +29 -0
  65. package/dist/deploy/build.d.ts.map +1 -1
  66. package/dist/deploy/build.js +85 -0
  67. package/dist/deploy/build.js.map +1 -1
  68. package/dist/deploy/workspace-deploy.d.ts.map +1 -1
  69. package/dist/deploy/workspace-deploy.js +108 -0
  70. package/dist/deploy/workspace-deploy.js.map +1 -1
  71. package/dist/mcp/build-server.d.ts.map +1 -1
  72. package/dist/mcp/build-server.js +23 -9
  73. package/dist/mcp/build-server.js.map +1 -1
  74. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  75. package/dist/server/agent-chat-plugin.js +68 -13
  76. package/dist/server/agent-chat-plugin.js.map +1 -1
  77. package/docs/content/multi-app-workspace.md +2 -2
  78. package/docs/design/durable-agent-runs.md +458 -4
  79. package/package.json +1 -1
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Durable background agent-chat runs (Netlify background functions).
3
+ *
4
+ * Off by default. When enabled, a long in-app agent-chat turn is dispatched
5
+ * into a Netlify *background* function (15-min budget) instead of completing
6
+ * synchronously under the ~40s soft-timeout. The foreground POST claims the
7
+ * run slot, inserts the run row, fires an HMAC-signed self-dispatch to
8
+ * `AGENT_CHAT_PROCESS_RUN_PATH`, and returns the existing SSE subscription so
9
+ * the client streams the same events (via the cross-isolate SQL-poll path)
10
+ * with no client change.
11
+ *
12
+ * This module owns ONLY the gating decision + shared constants so both the
13
+ * HTTP handler (`production-agent.ts`) and the processor route
14
+ * (`agent-chat-plugin.ts`) agree on when the path is active without a circular
15
+ * import. The actual run machinery is reused verbatim from run-manager /
16
+ * run-store / self-dispatch / internal-token.
17
+ *
18
+ * GUARDRAIL: when `isAgentChatDurableBackgroundEnabled()` returns false, the
19
+ * agent-chat handler must behave byte-for-byte like the current synchronous
20
+ * path. The flag is only ever true when ALL of these hold:
21
+ * 1. `AGENT_CHAT_DURABLE_BACKGROUND` env is a truthy value.
22
+ * 2. The runtime is hosted/serverless (local dev keeps the inline path so SSE
23
+ * stays a single live stream and no second function is needed).
24
+ * 3. `A2A_SECRET` is configured (the HMAC handoff is required to authenticate
25
+ * the background dispatch; without it the dispatch can't be trusted).
26
+ */
27
+ import { hasConfiguredA2ASecret, isA2AProductionRuntime, } from "../a2a/auth-policy.js";
28
+ import { extractBearerToken, verifyInternalToken, } from "../integrations/internal-token.js";
29
+ /**
30
+ * Framework route the background function actually runs — sibling to
31
+ * `AGENT_TEAM_PROCESS_RUN_PATH`. Reached *through* the Netlify background
32
+ * function, so it inherits the 15-min budget.
33
+ */
34
+ export const AGENT_CHAT_PROCESS_RUN_PATH = "/_agent-native/agent-chat/_process-run";
35
+ /** Env flag (off by default) that opts an app into durable background runs. */
36
+ export const AGENT_CHAT_DURABLE_BACKGROUND_ENV = "AGENT_CHAT_DURABLE_BACKGROUND";
37
+ /**
38
+ * Body field the foreground handler injects when self-dispatching to the
39
+ * background processor. Its presence is how the re-entered handler knows it is
40
+ * the background worker (run inline with the background soft-timeout; do NOT
41
+ * re-claim the slot or re-dispatch). Untrusted on its own — the route also
42
+ * verifies the HMAC token before invoking the handler.
43
+ */
44
+ export const AGENT_CHAT_BACKGROUND_RUN_FIELD = "__backgroundRun";
45
+ /**
46
+ * Mirror of run-manager's private `isHostedRuntime`. Kept in sync deliberately:
47
+ * the durable-background gate must agree with the soft-timeout regime about
48
+ * what "hosted" means.
49
+ */
50
+ export function isHostedRuntimeForDurableBackground() {
51
+ if (process.env.NETLIFY &&
52
+ process.env.NETLIFY !== "false" &&
53
+ process.env.NETLIFY_LOCAL !== "true") {
54
+ return true;
55
+ }
56
+ if (process.env.AWS_LAMBDA_FUNCTION_NAME &&
57
+ process.env.NETLIFY_LOCAL !== "true") {
58
+ return true;
59
+ }
60
+ return Boolean(process.env.CF_PAGES ||
61
+ process.env.VERCEL ||
62
+ process.env.VERCEL_ENV ||
63
+ process.env.RENDER ||
64
+ process.env.FLY_APP_NAME ||
65
+ process.env.K_SERVICE);
66
+ }
67
+ function isFlagEnabled() {
68
+ // Read the literal key (not `process.env[CONST]`) so guard:no-env-credentials
69
+ // can statically verify it against the allowlisted `AGENT_*` prefix. Keep this
70
+ // in sync with AGENT_CHAT_DURABLE_BACKGROUND_ENV.
71
+ const raw = process.env.AGENT_CHAT_DURABLE_BACKGROUND;
72
+ if (raw == null)
73
+ return false;
74
+ const normalized = raw.trim().toLowerCase();
75
+ return (normalized === "1" ||
76
+ normalized === "true" ||
77
+ normalized === "yes" ||
78
+ normalized === "on");
79
+ }
80
+ /**
81
+ * The single gate. True only when the flag is on AND the runtime is hosted AND
82
+ * A2A_SECRET is configured. False otherwise — and false means the current
83
+ * synchronous behavior is used, unchanged.
84
+ */
85
+ export function isAgentChatDurableBackgroundEnabled() {
86
+ return (isFlagEnabled() &&
87
+ isHostedRuntimeForDurableBackground() &&
88
+ hasConfiguredA2ASecret());
89
+ }
90
+ /**
91
+ * Pure, transport-agnostic core of the `_process-run` route: validate the body,
92
+ * authenticate the HMAC self-dispatch, and produce the body the re-entered
93
+ * agent-chat handler should run as the background worker.
94
+ *
95
+ * Auth policy mirrors the agent-teams processor exactly:
96
+ * - `A2A_SECRET` set → require a valid `verifyInternalToken(runId, token)`.
97
+ * - no secret but a production runtime → refuse (503) — never run unsigned in
98
+ * prod.
99
+ * - no secret + non-prod (local dev) → allow unsigned; the SQL atomic claim
100
+ * in the worker still prevents double-processing.
101
+ *
102
+ * Extracted from the route handler so the auth + marker-prep decision is unit
103
+ * testable without booting the whole Nitro plugin. The route only adds body
104
+ * reading and the final handler invocation around this.
105
+ */
106
+ export function prepareProcessRunRequest(body, authHeader) {
107
+ if (!body || typeof body !== "object") {
108
+ return { ok: false, status: 400, error: "Invalid request body" };
109
+ }
110
+ const record = body;
111
+ const marker = record[AGENT_CHAT_BACKGROUND_RUN_FIELD];
112
+ const runId = marker && typeof marker.runId === "string"
113
+ ? marker.runId
114
+ : typeof record.taskId === "string"
115
+ ? record.taskId
116
+ : "";
117
+ if (!runId) {
118
+ return { ok: false, status: 400, error: "runId required" };
119
+ }
120
+ if (hasConfiguredA2ASecret()) {
121
+ const token = extractBearerToken(authHeader);
122
+ if (!verifyInternalToken(runId, token ?? "")) {
123
+ return {
124
+ ok: false,
125
+ status: 401,
126
+ error: "Invalid or expired processor token",
127
+ };
128
+ }
129
+ }
130
+ else if (isA2AProductionRuntime()) {
131
+ return {
132
+ ok: false,
133
+ status: 503,
134
+ error: "Agent chat background processor not configured — set A2A_SECRET on this deployment.",
135
+ };
136
+ }
137
+ // Ensure the marker is present so the re-entered handler runs as the
138
+ // background worker (reuses runId/turnId, no re-claim, no re-dispatch).
139
+ if (!marker || typeof marker.runId !== "string") {
140
+ record[AGENT_CHAT_BACKGROUND_RUN_FIELD] = { runId };
141
+ }
142
+ return { ok: true, runId, body: record };
143
+ }
144
+ //# sourceMappingURL=durable-background.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-background.js","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mCAAmC,CAAC;AAE3C;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GACtC,wCAAwC,CAAC;AAE3C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,iCAAiC,GAC5C,+BAA+B,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,iBAAiB,CAAC;AAEjE;;;;GAIG;AACH,MAAM,UAAU,mCAAmC;IACjD,IACE,OAAO,CAAC,GAAG,CAAC,OAAO;QACnB,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,OAAO;QAC/B,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,EACpC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,OAAO,CAAC,GAAG,CAAC,wBAAwB;QACpC,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,EACpC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CACZ,OAAO,CAAC,GAAG,CAAC,QAAQ;QACpB,OAAO,CAAC,GAAG,CAAC,MAAM;QAClB,OAAO,CAAC,GAAG,CAAC,UAAU;QACtB,OAAO,CAAC,GAAG,CAAC,MAAM;QAClB,OAAO,CAAC,GAAG,CAAC,YAAY;QACxB,OAAO,CAAC,GAAG,CAAC,SAAS,CACtB,CAAC;AACJ,CAAC;AAED,SAAS,aAAa;IACpB,8EAA8E;IAC9E,+EAA+E;IAC/E,kDAAkD;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC;IACtD,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,OAAO,CACL,UAAU,KAAK,GAAG;QAClB,UAAU,KAAK,MAAM;QACrB,UAAU,KAAK,KAAK;QACpB,UAAU,KAAK,IAAI,CACpB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mCAAmC;IACjD,OAAO,CACL,aAAa,EAAE;QACf,mCAAmC,EAAE;QACrC,sBAAsB,EAAE,CACzB,CAAC;AACJ,CAAC;AAmBD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAa,EACb,UAA8B;IAE9B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;IACnE,CAAC;IACD,MAAM,MAAM,GAAG,IAA+B,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,+BAA+B,CAExC,CAAC;IACd,MAAM,KAAK,GACT,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;QACxC,CAAC,CAAC,MAAM,CAAC,KAAK;QACd,CAAC,CAAC,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;YACjC,CAAC,CAAE,MAAM,CAAC,MAAiB;YAC3B,CAAC,CAAC,EAAE,CAAC;IACX,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC7D,CAAC;IAED,IAAI,sBAAsB,EAAE,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;YAC7C,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,GAAG;gBACX,KAAK,EAAE,oCAAoC;aAC5C,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,sBAAsB,EAAE,EAAE,CAAC;QACpC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EACH,qFAAqF;SACxF,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,wEAAwE;IACxE,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,CAAC,+BAA+B,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC3C,CAAC","sourcesContent":["/**\n * Durable background agent-chat runs (Netlify background functions).\n *\n * Off by default. When enabled, a long in-app agent-chat turn is dispatched\n * into a Netlify *background* function (15-min budget) instead of completing\n * synchronously under the ~40s soft-timeout. The foreground POST claims the\n * run slot, inserts the run row, fires an HMAC-signed self-dispatch to\n * `AGENT_CHAT_PROCESS_RUN_PATH`, and returns the existing SSE subscription so\n * the client streams the same events (via the cross-isolate SQL-poll path)\n * with no client change.\n *\n * This module owns ONLY the gating decision + shared constants so both the\n * HTTP handler (`production-agent.ts`) and the processor route\n * (`agent-chat-plugin.ts`) agree on when the path is active without a circular\n * import. The actual run machinery is reused verbatim from run-manager /\n * run-store / self-dispatch / internal-token.\n *\n * GUARDRAIL: when `isAgentChatDurableBackgroundEnabled()` returns false, the\n * agent-chat handler must behave byte-for-byte like the current synchronous\n * path. The flag is only ever true when ALL of these hold:\n * 1. `AGENT_CHAT_DURABLE_BACKGROUND` env is a truthy value.\n * 2. The runtime is hosted/serverless (local dev keeps the inline path so SSE\n * stays a single live stream and no second function is needed).\n * 3. `A2A_SECRET` is configured (the HMAC handoff is required to authenticate\n * the background dispatch; without it the dispatch can't be trusted).\n */\nimport {\n hasConfiguredA2ASecret,\n isA2AProductionRuntime,\n} from \"../a2a/auth-policy.js\";\nimport {\n extractBearerToken,\n verifyInternalToken,\n} from \"../integrations/internal-token.js\";\n\n/**\n * Framework route the background function actually runs — sibling to\n * `AGENT_TEAM_PROCESS_RUN_PATH`. Reached *through* the Netlify background\n * function, so it inherits the 15-min budget.\n */\nexport const AGENT_CHAT_PROCESS_RUN_PATH =\n \"/_agent-native/agent-chat/_process-run\";\n\n/** Env flag (off by default) that opts an app into durable background runs. */\nexport const AGENT_CHAT_DURABLE_BACKGROUND_ENV =\n \"AGENT_CHAT_DURABLE_BACKGROUND\";\n\n/**\n * Body field the foreground handler injects when self-dispatching to the\n * background processor. Its presence is how the re-entered handler knows it is\n * the background worker (run inline with the background soft-timeout; do NOT\n * re-claim the slot or re-dispatch). Untrusted on its own — the route also\n * verifies the HMAC token before invoking the handler.\n */\nexport const AGENT_CHAT_BACKGROUND_RUN_FIELD = \"__backgroundRun\";\n\n/**\n * Mirror of run-manager's private `isHostedRuntime`. Kept in sync deliberately:\n * the durable-background gate must agree with the soft-timeout regime about\n * what \"hosted\" means.\n */\nexport function isHostedRuntimeForDurableBackground(): boolean {\n if (\n process.env.NETLIFY &&\n process.env.NETLIFY !== \"false\" &&\n process.env.NETLIFY_LOCAL !== \"true\"\n ) {\n return true;\n }\n if (\n process.env.AWS_LAMBDA_FUNCTION_NAME &&\n process.env.NETLIFY_LOCAL !== \"true\"\n ) {\n return true;\n }\n return Boolean(\n process.env.CF_PAGES ||\n process.env.VERCEL ||\n process.env.VERCEL_ENV ||\n process.env.RENDER ||\n process.env.FLY_APP_NAME ||\n process.env.K_SERVICE,\n );\n}\n\nfunction isFlagEnabled(): boolean {\n // Read the literal key (not `process.env[CONST]`) so guard:no-env-credentials\n // can statically verify it against the allowlisted `AGENT_*` prefix. Keep this\n // in sync with AGENT_CHAT_DURABLE_BACKGROUND_ENV.\n const raw = process.env.AGENT_CHAT_DURABLE_BACKGROUND;\n if (raw == null) return false;\n const normalized = raw.trim().toLowerCase();\n return (\n normalized === \"1\" ||\n normalized === \"true\" ||\n normalized === \"yes\" ||\n normalized === \"on\"\n );\n}\n\n/**\n * The single gate. True only when the flag is on AND the runtime is hosted AND\n * A2A_SECRET is configured. False otherwise — and false means the current\n * synchronous behavior is used, unchanged.\n */\nexport function isAgentChatDurableBackgroundEnabled(): boolean {\n return (\n isFlagEnabled() &&\n isHostedRuntimeForDurableBackground() &&\n hasConfiguredA2ASecret()\n );\n}\n\n/** Decision returned by `prepareProcessRunRequest`. */\nexport type ProcessRunPreparation =\n | {\n ok: true;\n /** The pre-claimed run id the background worker must reuse. */\n runId: string;\n /** Body to stash for the re-entered handler (marker guaranteed present). */\n body: Record<string, unknown>;\n }\n | {\n ok: false;\n /** HTTP status the route should return. */\n status: number;\n /** Error payload. */\n error: string;\n };\n\n/**\n * Pure, transport-agnostic core of the `_process-run` route: validate the body,\n * authenticate the HMAC self-dispatch, and produce the body the re-entered\n * agent-chat handler should run as the background worker.\n *\n * Auth policy mirrors the agent-teams processor exactly:\n * - `A2A_SECRET` set → require a valid `verifyInternalToken(runId, token)`.\n * - no secret but a production runtime → refuse (503) — never run unsigned in\n * prod.\n * - no secret + non-prod (local dev) → allow unsigned; the SQL atomic claim\n * in the worker still prevents double-processing.\n *\n * Extracted from the route handler so the auth + marker-prep decision is unit\n * testable without booting the whole Nitro plugin. The route only adds body\n * reading and the final handler invocation around this.\n */\nexport function prepareProcessRunRequest(\n body: unknown,\n authHeader: string | undefined,\n): ProcessRunPreparation {\n if (!body || typeof body !== \"object\") {\n return { ok: false, status: 400, error: \"Invalid request body\" };\n }\n const record = body as Record<string, unknown>;\n const marker = record[AGENT_CHAT_BACKGROUND_RUN_FIELD] as\n | { runId?: unknown }\n | undefined;\n const runId =\n marker && typeof marker.runId === \"string\"\n ? marker.runId\n : typeof record.taskId === \"string\"\n ? (record.taskId as string)\n : \"\";\n if (!runId) {\n return { ok: false, status: 400, error: \"runId required\" };\n }\n\n if (hasConfiguredA2ASecret()) {\n const token = extractBearerToken(authHeader);\n if (!verifyInternalToken(runId, token ?? \"\")) {\n return {\n ok: false,\n status: 401,\n error: \"Invalid or expired processor token\",\n };\n }\n } else if (isA2AProductionRuntime()) {\n return {\n ok: false,\n status: 503,\n error:\n \"Agent chat background processor not configured — set A2A_SECRET on this deployment.\",\n };\n }\n\n // Ensure the marker is present so the re-entered handler runs as the\n // background worker (reuses runId/turnId, no re-claim, no re-dispatch).\n if (!marker || typeof marker.runId !== \"string\") {\n record[AGENT_CHAT_BACKGROUND_RUN_FIELD] = { runId };\n }\n return { ok: true, runId, body: record };\n}\n"]}
@@ -375,6 +375,26 @@ export declare function runAgentLoop(opts: {
375
375
  */
376
376
  processors?: Processor[];
377
377
  }): Promise<AgentLoopUsage>;
378
+ /**
379
+ * Hard cap on server-driven background→background continuation chunks for a
380
+ * single logical turn. A `backgroundFunction` run gets a ~13-min soft timeout,
381
+ * so reaching this boundary at all is the rare exception (most turns finish in
382
+ * one chunk). The cap bounds a pathological turn that would otherwise chain
383
+ * background invocations forever, mirroring `MAX_AGENT_TEAM_CONTINUATIONS`.
384
+ */
385
+ export declare const MAX_BACKGROUND_RUN_CONTINUATIONS = 20;
386
+ /**
387
+ * Whether the background worker should self-fire the next server-driven
388
+ * continuation chunk. True only when this is a background worker run that ended
389
+ * at a recoverable soft-timeout boundary (not aborted/stopped) and the chain is
390
+ * still under its budget. Extracted so the decision is unit testable without
391
+ * booting the whole handler.
392
+ */
393
+ export declare function shouldChainBackgroundContinuation(opts: {
394
+ isBackgroundWorker: boolean;
395
+ run: ActiveRun;
396
+ continuationCount: number;
397
+ }): boolean;
378
398
  export declare function createProductionAgentHandler(options: ProductionAgentOptions): H3EventHandler;
379
399
  export { getActiveRunForThread, getActiveRunForThreadAsync, getRun, abortRun, subscribeToRun, };
380
400
  //# sourceMappingURL=production-agent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"production-agent.d.ts","sourceRoot":"","sources":["../../src/agent/production-agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"production-agent.d.ts","sourceRoot":"","sources":["../../src/agent/production-agent.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,8 @@ import { isDemoModeEnabled } from "../demo/config.js";
12
12
  import { redactDemoData, redactDemoString } from "../demo/redact.js";
13
13
  import { redactSensitiveFields, sanitizeToolErrorText, sanitizeToolErrorValue, } from "./tool-error-redaction.js";
14
14
  import { startRun, subscribeToRun, getActiveRunForThread, getActiveRunForThreadAsync, getRun, abortRun, tryClaimRunSlot, } from "./run-manager.js";
15
+ import { AGENT_CHAT_PROCESS_RUN_PATH, AGENT_CHAT_BACKGROUND_RUN_FIELD, isAgentChatDurableBackgroundEnabled, } from "./durable-background.js";
16
+ import { fireInternalDispatch } from "../server/self-dispatch.js";
15
17
  import { readBody } from "../server/h3-helpers.js";
16
18
  import { isReadOnlyShellCommand } from "../coding-tools/index.js";
17
19
  import { getRequestRunContext, ensureRequestRunContext, getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
@@ -22,7 +24,7 @@ import { createToolSearchEntry, TOOL_SEARCH_ACTION_NAME, } from "./tool-search.j
22
24
  import { getDefaultMaxIterations, normalizeMaxIterations, readAgentLoopSettings, } from "./loop-settings.js";
23
25
  import { isReasoningEffort, normalizeReasoningEffortForModel, } from "../shared/reasoning-effort.js";
24
26
  import { isAgentActionStopError } from "../action.js";
25
- import { writeLedgerEntry, readLedgerEntry, clearLedgerForThread, getCurrentTurnEventsForThread, } from "./run-store.js";
27
+ import { writeLedgerEntry, readLedgerEntry, clearLedgerForThread, getCurrentTurnEventsForThread, insertRun, updateRunHeartbeat, updateRunStatusIfRunning, claimBackgroundRun, } from "./run-store.js";
26
28
  import { classifyToolCallJournal, findCompletedJournalEntry, } from "./tool-call-journal.js";
27
29
  import { preUploadAttachments } from "../file-upload/pre-upload-attachments.js";
28
30
  import { extensionIdFromPathname } from "../extensions/path.js";
@@ -2675,6 +2677,27 @@ function endsAtInternalContinuationBoundary(run) {
2675
2677
  }
2676
2678
  return last.type === "error" && isRecoverableContinuationError(last);
2677
2679
  }
2680
+ /**
2681
+ * Hard cap on server-driven background→background continuation chunks for a
2682
+ * single logical turn. A `backgroundFunction` run gets a ~13-min soft timeout,
2683
+ * so reaching this boundary at all is the rare exception (most turns finish in
2684
+ * one chunk). The cap bounds a pathological turn that would otherwise chain
2685
+ * background invocations forever, mirroring `MAX_AGENT_TEAM_CONTINUATIONS`.
2686
+ */
2687
+ export const MAX_BACKGROUND_RUN_CONTINUATIONS = 20;
2688
+ /**
2689
+ * Whether the background worker should self-fire the next server-driven
2690
+ * continuation chunk. True only when this is a background worker run that ended
2691
+ * at a recoverable soft-timeout boundary (not aborted/stopped) and the chain is
2692
+ * still under its budget. Extracted so the decision is unit testable without
2693
+ * booting the whole handler.
2694
+ */
2695
+ export function shouldChainBackgroundContinuation(opts) {
2696
+ return (opts.isBackgroundWorker &&
2697
+ opts.run.status !== "aborted" &&
2698
+ endsAtInternalContinuationBoundary(opts.run) &&
2699
+ opts.continuationCount < MAX_BACKGROUND_RUN_CONTINUATIONS);
2700
+ }
2678
2701
  function progressStepFromAgentChatEvent(event) {
2679
2702
  switch (event.type) {
2680
2703
  case "activity":
@@ -2729,14 +2752,46 @@ export function createProductionAgentHandler(options) {
2729
2752
  return { error: "Method not allowed" };
2730
2753
  }
2731
2754
  let body;
2732
- try {
2733
- body = await readBody(event);
2755
+ // The durable-background `_process-run` route already consumed and verified
2756
+ // the request body (h3 v2's web Request body stream is single-use, so a
2757
+ // second readBody would fail). It stashes the verified+augmented body here
2758
+ // so this re-entered handler reads it instead of the spent stream.
2759
+ const preInjectedBody = event?.context
2760
+ ?.__agentChatBackgroundBody;
2761
+ if (preInjectedBody && typeof preInjectedBody === "object") {
2762
+ body = preInjectedBody;
2734
2763
  }
2735
- catch {
2736
- setResponseStatus(event, 400);
2737
- return { error: "Invalid request body" };
2764
+ else {
2765
+ try {
2766
+ body = await readBody(event);
2767
+ }
2768
+ catch {
2769
+ setResponseStatus(event, 400);
2770
+ return { error: "Invalid request body" };
2771
+ }
2738
2772
  }
2739
2773
  const { message, history = [], structuredHistory, references = [], threadId, attachments, displayMessage, internalContinuation, turnId: requestTurnId, model: requestModel, engine: requestEngine, effort: requestEffort, browserTabId, scope, trackInRunsTray, } = body;
2774
+ // Durable-background marker. Present ONLY when this handler was re-entered
2775
+ // as the Netlify background worker via the `_process-run` self-dispatch
2776
+ // (the route HMAC-verifies the dispatch before invoking us). When set, we
2777
+ // run the loop inline with the background soft-timeout, reusing the
2778
+ // pre-claimed runId/turnId — we must NOT re-claim the slot or re-dispatch.
2779
+ const backgroundRunMarker = body[AGENT_CHAT_BACKGROUND_RUN_FIELD] &&
2780
+ typeof body[AGENT_CHAT_BACKGROUND_RUN_FIELD] === "object" &&
2781
+ typeof body[AGENT_CHAT_BACKGROUND_RUN_FIELD].runId === "string"
2782
+ ? body[AGENT_CHAT_BACKGROUND_RUN_FIELD]
2783
+ : null;
2784
+ const isBackgroundWorker = backgroundRunMarker !== null;
2785
+ // How many server-driven background continuations have already chained into
2786
+ // this logical turn (0 on the first chunk). Used to bound the chain.
2787
+ const backgroundContinuationCount = isBackgroundWorker &&
2788
+ typeof backgroundRunMarker?.continuationCount === "number" &&
2789
+ Number.isFinite(backgroundRunMarker.continuationCount)
2790
+ ? Math.max(0, Math.floor(backgroundRunMarker.continuationCount))
2791
+ : 0;
2792
+ // The foreground POST decides whether to dispatch into a background
2793
+ // function. The background worker itself never re-dispatches.
2794
+ const dispatchToBackground = !isBackgroundWorker && isAgentChatDurableBackgroundEnabled();
2740
2795
  const requestBrowserTabId = normalizeBrowserTabId(browserTabId);
2741
2796
  const requestChatScope = normalizeChatScope(scope);
2742
2797
  const requestRunCtx = ensureRequestRunContext();
@@ -3154,7 +3209,11 @@ export function createProductionAgentHandler(options) {
3154
3209
  // different serverless isolates both see the correct state — a plain
3155
3210
  // read-then-act check races on multi-isolate deployments because both
3156
3211
  // reads see no running row before either insert commits.
3157
- if (threadId) {
3212
+ //
3213
+ // The background worker SKIPS this: the foreground POST already claimed the
3214
+ // slot and inserted the run row before dispatching, so re-claiming here
3215
+ // would falsely 409 against the row the foreground holds.
3216
+ if (threadId && !isBackgroundWorker) {
3158
3217
  const slot = await tryClaimRunSlot(threadId);
3159
3218
  if (!slot.claimed) {
3160
3219
  setResponseStatus(event, 409);
@@ -3164,17 +3223,62 @@ export function createProductionAgentHandler(options) {
3164
3223
  };
3165
3224
  }
3166
3225
  }
3167
- // Start agent loop in background via run-manager
3168
- const runId = generateRunId();
3226
+ // Start agent loop in background via run-manager. The background worker
3227
+ // reuses the runId carried in the marker (signed into the dispatch token):
3228
+ // - First background chunk (count 0): the foreground generated + INSERTED
3229
+ // this runId, so the event stream the client is already subscribed to is
3230
+ // the one we write to.
3231
+ // - Chained continuation chunk (count > 0): the prior chunk minted a FRESH
3232
+ // runId for this one (a reused runId would restart `startRun`'s in-memory
3233
+ // seq log at 0 and collide with the prior chunk's persisted seqs, which
3234
+ // insertRunEvent's ON CONFLICT would drop — making the continuation
3235
+ // invisible). A fresh runId on the SAME thread + SAME turnId folds onto
3236
+ // one assistant message and is surfaced by the existing
3237
+ // `/runs/active?threadId` reconnect path. The continuation worker inserts
3238
+ // its own background row below (the foreground only inserted chunk-0's).
3239
+ const isChainedBackgroundContinuation = isBackgroundWorker && backgroundContinuationCount > 0;
3240
+ const runId = backgroundRunMarker?.runId ?? generateRunId();
3169
3241
  const effectiveThreadId = threadId ?? runId;
3170
- const effectiveTurnId = typeof requestTurnId === "string" && requestTurnId.trim()
3171
- ? requestTurnId.trim()
3172
- : runId;
3242
+ const effectiveTurnId = typeof backgroundRunMarker?.turnId === "string" &&
3243
+ backgroundRunMarker.turnId.trim()
3244
+ ? backgroundRunMarker.turnId.trim()
3245
+ : typeof requestTurnId === "string" && requestTurnId.trim()
3246
+ ? requestTurnId.trim()
3247
+ : runId;
3173
3248
  const messageToPersist = typeof requestDisplayMessage === "string" &&
3174
3249
  requestDisplayMessage.trim().length > 0
3175
3250
  ? requestDisplayMessage
3176
3251
  : requestMessage;
3177
- if (options.onRunPrepared && !internalContinuation) {
3252
+ // Server-driven background continuation: when the background worker re-fired
3253
+ // itself at a soft-timeout boundary (a chained continuation chunk), rebuild
3254
+ // the conversation from the persisted thread_data so the next chunk resumes
3255
+ // from committed progress instead of restarting from the original user
3256
+ // message (which would re-do work — the re-hydration thrash the design doc
3257
+ // calls out). Mirrors the agent-teams continuation, which seeds from
3258
+ // thread_data + appends a continuation nudge. Falls back to the body-derived
3259
+ // `messages` if thread_data is empty/unreadable — a continuation that
3260
+ // restarts is worse than one that resumes, but both are correct.
3261
+ if (isChainedBackgroundContinuation && effectiveThreadId) {
3262
+ try {
3263
+ const { getThread } = await import("../chat-threads/store.js");
3264
+ const { threadDataToEngineMessages } = await import("./thread-data-builder.js");
3265
+ const priorThreadData = (await getThread(effectiveThreadId))
3266
+ ?.threadData;
3267
+ const resumed = threadDataToEngineMessages(priorThreadData);
3268
+ if (resumed.length > 0) {
3269
+ appendAgentLoopContinuation(resumed, "run_timeout");
3270
+ messages.length = 0;
3271
+ messages.push(...resumed);
3272
+ }
3273
+ }
3274
+ catch {
3275
+ // Keep the body-derived messages — never drop the run.
3276
+ }
3277
+ }
3278
+ // Persist the user's turn exactly once. The foreground POST does this
3279
+ // before dispatching; the background worker must NOT repeat it (it re-enters
3280
+ // with the same body, which would double-persist the user message).
3281
+ if (options.onRunPrepared && !internalContinuation && !isBackgroundWorker) {
3178
3282
  await options.onRunPrepared({
3179
3283
  runId,
3180
3284
  threadId,
@@ -3182,6 +3286,73 @@ export function createProductionAgentHandler(options) {
3182
3286
  attachments: requestAttachments,
3183
3287
  });
3184
3288
  }
3289
+ // ─── Durable-background dispatch decision ──────────────────────────────
3290
+ // Flag active (hosted + A2A_SECRET + AGENT_CHAT_DURABLE_BACKGROUND) and we
3291
+ // are the FOREGROUND POST: insert the run row (marked background), fire an
3292
+ // HMAC-signed self-dispatch into the Netlify background function (15-min
3293
+ // budget), and return the SSE subscription immediately. The client streams
3294
+ // the same events via the cross-isolate SQL-poll path with no client
3295
+ // change. With the flag OFF this whole branch is skipped and the inline
3296
+ // `startRun` path below runs exactly as before (byte-for-byte).
3297
+ if (dispatchToBackground) {
3298
+ try {
3299
+ // Insert the run row up front so /runs/active sees it immediately and
3300
+ // the slot stays held while the background function cold-starts. Mark
3301
+ // it background-dispatched so the stale reaper uses the wider window.
3302
+ await insertRun(runId, effectiveThreadId, effectiveTurnId, {
3303
+ dispatchMode: "background",
3304
+ });
3305
+ }
3306
+ catch (err) {
3307
+ // A duplicate-PK collision means the row already exists (ret­ried POST);
3308
+ // any other failure means we can't safely hand off — fall back to the
3309
+ // inline path rather than dropping the turn.
3310
+ console.error("[agent-chat] background insertRun failed; falling back to inline:", err instanceof Error ? err.message : err);
3311
+ }
3312
+ let dispatched = false;
3313
+ try {
3314
+ await fireInternalDispatch({
3315
+ event,
3316
+ path: AGENT_CHAT_PROCESS_RUN_PATH,
3317
+ taskId: runId,
3318
+ body: {
3319
+ ...body,
3320
+ // Carry the pre-claimed identity so the worker reuses this run.
3321
+ [AGENT_CHAT_BACKGROUND_RUN_FIELD]: {
3322
+ runId,
3323
+ turnId: effectiveTurnId,
3324
+ },
3325
+ },
3326
+ });
3327
+ dispatched = true;
3328
+ }
3329
+ catch (err) {
3330
+ console.error("[agent-chat] background dispatch failed; falling back to inline:", err instanceof Error ? err.message : err);
3331
+ }
3332
+ if (dispatched) {
3333
+ const stream = subscribeToRun(runId, 0);
3334
+ if (stream) {
3335
+ setResponseHeader(event, "Content-Type", "text/event-stream");
3336
+ setResponseHeader(event, "Cache-Control", "no-cache");
3337
+ setResponseHeader(event, "Connection", "keep-alive");
3338
+ setResponseHeader(event, "X-Run-Id", runId);
3339
+ return stream;
3340
+ }
3341
+ // Subscription failed even though dispatch landed — surface an error
3342
+ // rather than silently running inline (the background worker is already
3343
+ // processing this runId, so an inline second run would double-execute).
3344
+ setResponseStatus(event, 500);
3345
+ return { error: "Failed to subscribe to background run" };
3346
+ }
3347
+ // Dispatch failed before any worker could claim the run. Fail loud: flip
3348
+ // the row terminal so the held slot is released (and any reconnect sees a
3349
+ // terminal status instead of spinning), then 500 so the client can retry.
3350
+ // We do NOT silently fall through to inline here — that risks a later,
3351
+ // delayed background delivery double-executing the same runId.
3352
+ await updateRunStatusIfRunning(runId, "errored").catch(() => { });
3353
+ setResponseStatus(event, 500);
3354
+ return { error: "Failed to dispatch background run" };
3355
+ }
3185
3356
  const trackedProgressOwner = trackInRunsTray === true && ownerEmail ? ownerEmail : null;
3186
3357
  const trackedProgressRunId = trackedProgressOwner
3187
3358
  ? backgroundChatProgressRunId(effectiveTurnId)
@@ -3246,7 +3417,18 @@ export function createProductionAgentHandler(options) {
3246
3417
  metadata: trackedProgressMetadata ?? undefined,
3247
3418
  }).catch(() => { });
3248
3419
  }
3249
- const handleRunComplete = options.onRunComplete || trackedProgressRunId
3420
+ // The background worker must AWAIT the run to completion before returning,
3421
+ // or Netlify freezes/kills the function the instant the handler returns and
3422
+ // the detached run dies mid-turn (mirrors the agent-teams processor, which
3423
+ // wraps startRun in `await new Promise(resolve => startRun(..., onComplete:
3424
+ // () => resolve()))`). We resolve this when the run's onComplete fires.
3425
+ let resolveBackgroundRunDone = null;
3426
+ const backgroundRunDone = isBackgroundWorker
3427
+ ? new Promise((resolve) => {
3428
+ resolveBackgroundRunDone = resolve;
3429
+ })
3430
+ : null;
3431
+ const baseHandleRunComplete = options.onRunComplete || trackedProgressRunId
3250
3432
  ? async (run) => {
3251
3433
  try {
3252
3434
  await options.onRunComplete?.(run, threadId);
@@ -3258,6 +3440,89 @@ export function createProductionAgentHandler(options) {
3258
3440
  await completeTrackedProgressRun(run);
3259
3441
  }
3260
3442
  : undefined;
3443
+ // Wrap so the background worker is unblocked even when there is no app
3444
+ // onRunComplete / tracked-progress callback configured.
3445
+ const handleRunComplete = isBackgroundWorker || baseHandleRunComplete
3446
+ ? async (run) => {
3447
+ try {
3448
+ // Persist the (partial) assistant turn to thread_data FIRST — the
3449
+ // server-driven continuation below rebuilds from it, so it must be
3450
+ // committed before we re-fire.
3451
+ await baseHandleRunComplete?.(run);
3452
+ // Server-driven background→background continuation. If this chunk
3453
+ // hit its soft-timeout still unfinished (ended at an auto_continue
3454
+ // / loop_limit / recoverable boundary), chain the next chunk by
3455
+ // re-firing the `_process-run` self-dispatch with mode "continue"
3456
+ // (carried as internalContinuation + an incremented count),
3457
+ // instead of relying on the client to re-POST. Mirrors the
3458
+ // agent-teams `fireInternalDispatch({ body: { mode: "continue" }})`
3459
+ // chain. Bounded by MAX_BACKGROUND_RUN_CONTINUATIONS. Aborted /
3460
+ // user-stopped runs do NOT chain.
3461
+ if (shouldChainBackgroundContinuation({
3462
+ isBackgroundWorker,
3463
+ run,
3464
+ continuationCount: backgroundContinuationCount,
3465
+ })) {
3466
+ // Mint the next chunk's runId here and sign the dispatch token
3467
+ // over it, so the `_process-run` route's HMAC check and the
3468
+ // worker's run identity agree. Fresh runId (not this chunk's) so
3469
+ // its seq log starts clean; same turnId folds the assistant
3470
+ // message across chunks.
3471
+ const nextRunId = generateRunId();
3472
+ try {
3473
+ await fireInternalDispatch({
3474
+ event,
3475
+ path: AGENT_CHAT_PROCESS_RUN_PATH,
3476
+ taskId: nextRunId,
3477
+ body: {
3478
+ ...body,
3479
+ internalContinuation: true,
3480
+ [AGENT_CHAT_BACKGROUND_RUN_FIELD]: {
3481
+ runId: nextRunId,
3482
+ turnId: effectiveTurnId,
3483
+ continuationCount: backgroundContinuationCount + 1,
3484
+ },
3485
+ },
3486
+ });
3487
+ }
3488
+ catch (chainErr) {
3489
+ // Chain dispatch failed — fail loud so the held row goes
3490
+ // terminal instead of spinning. The reaper would also catch
3491
+ // it, but this is immediate and truthful.
3492
+ console.error("[agent-chat] background continuation dispatch failed:", chainErr instanceof Error ? chainErr.message : chainErr);
3493
+ await updateRunStatusIfRunning(runId, "errored").catch(() => { });
3494
+ }
3495
+ }
3496
+ }
3497
+ finally {
3498
+ resolveBackgroundRunDone?.();
3499
+ }
3500
+ }
3501
+ : undefined;
3502
+ // Background worker: claim the pre-inserted run idempotently before
3503
+ // executing. A duplicate Netlify delivery loses the claim and no-ops here,
3504
+ // so the run can never be double-executed. Bump the heartbeat immediately
3505
+ // on entry so a slow cold-start doesn't leave the row looking stale to the
3506
+ // reaper before startRun's 1.5s heartbeat timer takes over.
3507
+ if (isBackgroundWorker) {
3508
+ // A chained continuation chunk's runId was minted by the prior chunk and
3509
+ // never inserted, so insert its background row now (idempotently — a
3510
+ // duplicate Netlify delivery that already inserted it just PK-collides and
3511
+ // the claim below dedups). The first chunk's row was inserted by the
3512
+ // foreground, so skip the insert there.
3513
+ if (isChainedBackgroundContinuation) {
3514
+ await insertRun(runId, effectiveThreadId, effectiveTurnId, {
3515
+ dispatchMode: "background",
3516
+ }).catch(() => { });
3517
+ }
3518
+ const won = await claimBackgroundRun(runId);
3519
+ if (!won) {
3520
+ // Already claimed by an earlier delivery — return a benign ack so
3521
+ // Netlify doesn't retry a successful handoff.
3522
+ return { ok: true, skipped: "already-claimed" };
3523
+ }
3524
+ await updateRunHeartbeat(runId).catch(() => { });
3525
+ }
3261
3526
  startRun(runId, effectiveThreadId, async (rawSend, signal) => {
3262
3527
  const send = (event) => {
3263
3528
  rawSend(event);
@@ -3591,11 +3856,24 @@ export function createProductionAgentHandler(options) {
3591
3856
  }, handleRunComplete, {
3592
3857
  softTimeoutMs: options.runSoftTimeoutMs,
3593
3858
  useHostedSoftTimeoutDefault: true,
3859
+ // Inside the Netlify background function there is no ~60s wall, so lift
3860
+ // the soft-timeout clamp to ~13min for THIS run only. Foreground runs
3861
+ // never set this, so their 40s clamp is unchanged.
3862
+ backgroundFunction: isBackgroundWorker,
3594
3863
  // Fold continuation runs of one logical turn onto a single durable
3595
3864
  // assistant message. Falls back to the runId (turn == run) when the
3596
3865
  // client doesn't supply a turnId.
3597
3866
  turnId: effectiveTurnId,
3598
3867
  });
3868
+ // Background worker: await the run to completion so Netlify keeps the
3869
+ // background function alive for the whole turn (the client is streaming the
3870
+ // same events via the foreground POST's cross-isolate SQL subscription).
3871
+ // The onComplete wrapper above resolves `backgroundRunDone`.
3872
+ if (isBackgroundWorker) {
3873
+ if (backgroundRunDone)
3874
+ await backgroundRunDone;
3875
+ return { ok: true, runId };
3876
+ }
3599
3877
  // Subscribe to the run and stream events to the client
3600
3878
  const stream = subscribeToRun(runId, 0);
3601
3879
  if (!stream) {