@agent-native/core 0.73.0 → 0.75.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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +67 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +51 -5
- package/corpus/core/src/agent/production-agent.ts +92 -12
- package/corpus/core/src/client/ConnectBuilderCard.tsx +7 -7
- package/corpus/core/src/client/NewWorkspaceAppFlow.tsx +1 -1
- package/corpus/core/src/client/blocks/library/AnnotatedCodeBlock.tsx +33 -18
- package/corpus/core/src/client/blocks/library/annotation-rail.tsx +3 -4
- package/corpus/core/src/client/components/CodeRequiredDialog.tsx +5 -8
- package/corpus/core/src/deploy/build.ts +21 -9
- package/corpus/core/src/deploy/workspace-deploy.ts +12 -6
- package/corpus/core/src/server/agent-chat-plugin.ts +7 -7
- package/corpus/core/src/server/self-dispatch.ts +8 -1
- package/corpus/templates/analytics/actions/compose-dashboard.ts +34 -0
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +51 -3
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +31 -8
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-filters-now-apply-consistently-to-first-party-traf.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +97 -28
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +57 -10
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +786 -323
- package/corpus/templates/clips/changelog/2026-06-24-settings-now-puts-what-s-new-in-a-compact-sidebar-prioritize.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-24-annotated-code-callouts-now-use-numbered-gutter-markers-that.md +6 -0
- package/dist/agent/durable-background.d.ts +21 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +48 -5
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +88 -13
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +6 -6
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +1 -1
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +14 -10
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +3 -4
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +3 -4
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
- package/dist/client/components/CodeRequiredDialog.js +2 -1
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/deploy/build.d.ts +13 -2
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +21 -9
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/workspace-deploy.js +12 -6
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/server/agent-chat-plugin.js +7 -7
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/self-dispatch.d.ts.map +1 -1
- package/dist/server/self-dispatch.js +5 -1
- package/dist/server/self-dispatch.js.map +1 -1
- package/package.json +1 -1
|
@@ -23,6 +23,27 @@ export declare const AGENT_CHAT_BACKGROUND_RUN_FIELD = "__backgroundRun";
|
|
|
23
23
|
* what "hosted" means.
|
|
24
24
|
*/
|
|
25
25
|
export declare function isHostedRuntimeForDurableBackground(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* True when THIS process is actually executing inside a Netlify *background*
|
|
28
|
+
* function (the long, 15-min-budget async function whose deployed name ends in
|
|
29
|
+
* `-background`). Netlify runs functions on AWS Lambda and sets
|
|
30
|
+
* `AWS_LAMBDA_FUNCTION_NAME` to the function's name, so a `-background` suffix is
|
|
31
|
+
* the runtime proof that the ~60s synchronous wall does NOT apply here.
|
|
32
|
+
*
|
|
33
|
+
* This is the SAFETY GUARD for the soft-timeout regime. The `_process-run`
|
|
34
|
+
* self-dispatch worker (`isBackgroundWorker`) is NOT enough on its own: if the
|
|
35
|
+
* `-background` function was never emitted (deploy gate off, or Netlify routed
|
|
36
|
+
* the path to the synchronous function), the self-POST lands on the regular
|
|
37
|
+
* ~60s `server` function. A worker there MUST use the 40s soft-timeout and
|
|
38
|
+
* checkpoint before the 60s wall — using the ~13min budget would overshoot the
|
|
39
|
+
* hard wall and get killed at 60s, then re-dispatch/resume in a wasteful loop.
|
|
40
|
+
* So the 13-min budget is taken ONLY when this returns true.
|
|
41
|
+
*
|
|
42
|
+
* Falls back to the explicit `AGENT_CHAT_FORCE_BACKGROUND_RUNTIME` env (truthy)
|
|
43
|
+
* for hosts that don't expose a `-background`-suffixed function name but where
|
|
44
|
+
* the operator has confirmed a long async budget. Off by default.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isInBackgroundFunctionRuntime(): boolean;
|
|
26
47
|
/**
|
|
27
48
|
* The single gate. True when the flag is not explicitly disabled (default-on)
|
|
28
49
|
* AND the runtime is hosted AND A2A_SECRET is configured. False otherwise — and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-background.d.ts","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"durable-background.d.ts","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"AA6CA;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,2CACE,CAAC;AAE3C;;;GAGG;AACH,eAAO,MAAM,iCAAiC,kCACb,CAAC;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAsB7D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAcvD;AAyBD;;;;;;GAMG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAM7D;AAED,uDAAuD;AACvD,MAAM,MAAM,qBAAqB,GAC7B;IACE,EAAE,EAAE,IAAI,CAAC;IACT,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,qBAAqB,CA0CvB"}
|
|
@@ -19,12 +19,20 @@
|
|
|
19
19
|
* agent-chat handler must behave byte-for-byte like the current synchronous
|
|
20
20
|
* path. The gate is true only when ALL of these hold:
|
|
21
21
|
* 1. `AGENT_CHAT_DURABLE_BACKGROUND` env is not explicitly disabled. It is
|
|
22
|
-
* DEFAULT-ON: unset/empty counts as enabled; set it to a falsy
|
|
23
|
-
* (`false`/`0`/`no`/`off`) to opt a specific app back out.
|
|
22
|
+
* DEFAULT-ON: unset/empty/unknown counts as enabled; set it to a falsy
|
|
23
|
+
* value (`false`/`0`/`no`/`off`) to opt a specific app back out.
|
|
24
24
|
* 2. The runtime is hosted/serverless (local dev keeps the inline path so SSE
|
|
25
25
|
* stays a single live stream and no second function is needed).
|
|
26
26
|
* 3. `A2A_SECRET` is configured (the HMAC handoff is required to authenticate
|
|
27
27
|
* the background dispatch; without it the dispatch can't be trusted).
|
|
28
|
+
*
|
|
29
|
+
* Default-on is safe because a *dispatch failure degrades to an inline run*: if
|
|
30
|
+
* the self-dispatch self-POST can't be delivered (fast connection error or
|
|
31
|
+
* fast non-2xx), the foreground handler runs the turn synchronously instead of
|
|
32
|
+
* erroring (see `production-agent.ts` — the inline fallback claims the run row
|
|
33
|
+
* atomically so a delayed delivery can never double-execute). So an app where
|
|
34
|
+
* durable dispatch happens to fail still gets a working chat, just without the
|
|
35
|
+
* 15-min budget.
|
|
28
36
|
*/
|
|
29
37
|
import { hasConfiguredA2ASecret, isA2AProductionRuntime, } from "../a2a/auth-policy.js";
|
|
30
38
|
import { extractBearerToken, verifyInternalToken, } from "../integrations/internal-token.js";
|
|
@@ -69,16 +77,51 @@ export function isHostedRuntimeForDurableBackground() {
|
|
|
69
77
|
process.env.FLY_APP_NAME ||
|
|
70
78
|
process.env.K_SERVICE);
|
|
71
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* True when THIS process is actually executing inside a Netlify *background*
|
|
82
|
+
* function (the long, 15-min-budget async function whose deployed name ends in
|
|
83
|
+
* `-background`). Netlify runs functions on AWS Lambda and sets
|
|
84
|
+
* `AWS_LAMBDA_FUNCTION_NAME` to the function's name, so a `-background` suffix is
|
|
85
|
+
* the runtime proof that the ~60s synchronous wall does NOT apply here.
|
|
86
|
+
*
|
|
87
|
+
* This is the SAFETY GUARD for the soft-timeout regime. The `_process-run`
|
|
88
|
+
* self-dispatch worker (`isBackgroundWorker`) is NOT enough on its own: if the
|
|
89
|
+
* `-background` function was never emitted (deploy gate off, or Netlify routed
|
|
90
|
+
* the path to the synchronous function), the self-POST lands on the regular
|
|
91
|
+
* ~60s `server` function. A worker there MUST use the 40s soft-timeout and
|
|
92
|
+
* checkpoint before the 60s wall — using the ~13min budget would overshoot the
|
|
93
|
+
* hard wall and get killed at 60s, then re-dispatch/resume in a wasteful loop.
|
|
94
|
+
* So the 13-min budget is taken ONLY when this returns true.
|
|
95
|
+
*
|
|
96
|
+
* Falls back to the explicit `AGENT_CHAT_FORCE_BACKGROUND_RUNTIME` env (truthy)
|
|
97
|
+
* for hosts that don't expose a `-background`-suffixed function name but where
|
|
98
|
+
* the operator has confirmed a long async budget. Off by default.
|
|
99
|
+
*/
|
|
100
|
+
export function isInBackgroundFunctionRuntime() {
|
|
101
|
+
const lambdaName = process.env.AWS_LAMBDA_FUNCTION_NAME;
|
|
102
|
+
if (typeof lambdaName === "string" &&
|
|
103
|
+
lambdaName.toLowerCase().endsWith("-background")) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const forced = process.env.AGENT_CHAT_FORCE_BACKGROUND_RUNTIME;
|
|
107
|
+
if (forced != null) {
|
|
108
|
+
const v = forced.trim().toLowerCase();
|
|
109
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
72
113
|
function isFlagEnabled() {
|
|
73
114
|
// Read the literal key (not `process.env[CONST]`) so guard:no-env-credentials
|
|
74
115
|
// can statically verify it against the allowlisted `AGENT_*` prefix. Keep this
|
|
75
116
|
// in sync with AGENT_CHAT_DURABLE_BACKGROUND_ENV.
|
|
76
117
|
//
|
|
77
118
|
// DEFAULT-ON: durable background runs are the desired behavior for every
|
|
78
|
-
// hosted app
|
|
79
|
-
//
|
|
80
|
-
// only with an explicit falsy value. This still composes with the hosted +
|
|
119
|
+
// hosted app. So an unset/empty/unknown flag means ON; an app opts OUT only
|
|
120
|
+
// with an explicit falsy value. This still composes with the hosted +
|
|
81
121
|
// A2A_SECRET gates below, so non-hosted / unconfigured apps stay synchronous.
|
|
122
|
+
// Safety net: a failed dispatch degrades to a synchronous inline run (see
|
|
123
|
+
// production-agent.ts), so default-on cannot break chat even if the
|
|
124
|
+
// self-dispatch can't be delivered on a given app.
|
|
82
125
|
const raw = process.env.AGENT_CHAT_DURABLE_BACKGROUND;
|
|
83
126
|
if (raw == null)
|
|
84
127
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-background.js","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"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","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 gate is true only when ALL of these hold:\n * 1. `AGENT_CHAT_DURABLE_BACKGROUND` env is not explicitly disabled. It is\n * DEFAULT-ON: unset/empty counts as enabled; set it to a falsy value\n * (`false`/`0`/`no`/`off`) to opt a specific app back out.\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/**\n * Env flag for durable background runs. DEFAULT-ON: unset means enabled; an app\n * opts OUT with an explicit falsy value (`false`/`0`/`no`/`off`).\n */\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 //\n // DEFAULT-ON: durable background runs are the desired behavior for every\n // hosted app (verified in prod — long multi-step runs complete past the 40s\n // soft-timeout with no thrash). So an unset flag means ON; an app opts OUT\n // only with an explicit falsy value. This still composes with the hosted +\n // A2A_SECRET gates below, so non-hosted / unconfigured apps stay synchronous.\n const raw = process.env.AGENT_CHAT_DURABLE_BACKGROUND;\n if (raw == null) return true;\n const normalized = raw.trim().toLowerCase();\n return !(\n normalized === \"0\" ||\n normalized === \"false\" ||\n normalized === \"no\" ||\n normalized === \"off\"\n );\n}\n\n/**\n * The single gate. True when the flag is not explicitly disabled (default-on)\n * AND the runtime is hosted AND A2A_SECRET is configured. False otherwise — and\n * false means the current synchronous behavior is used, unchanged. So a local /\n * non-hosted / unconfigured app stays synchronous even with the flag defaulting\n * on; durable only engages where the runtime actually supports it.\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"]}
|
|
1
|
+
{"version":3,"file":"durable-background.js","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"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","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 gate is true only when ALL of these hold:\n * 1. `AGENT_CHAT_DURABLE_BACKGROUND` env is not explicitly disabled. It is\n * DEFAULT-ON: unset/empty/unknown counts as enabled; set it to a falsy\n * value (`false`/`0`/`no`/`off`) to opt a specific app back out.\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 *\n * Default-on is safe because a *dispatch failure degrades to an inline run*: if\n * the self-dispatch self-POST can't be delivered (fast connection error or\n * fast non-2xx), the foreground handler runs the turn synchronously instead of\n * erroring (see `production-agent.ts` — the inline fallback claims the run row\n * atomically so a delayed delivery can never double-execute). So an app where\n * durable dispatch happens to fail still gets a working chat, just without the\n * 15-min budget.\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/**\n * Env flag for durable background runs. DEFAULT-ON: unset means enabled; an app\n * opts OUT with an explicit falsy value (`false`/`0`/`no`/`off`).\n */\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\n/**\n * True when THIS process is actually executing inside a Netlify *background*\n * function (the long, 15-min-budget async function whose deployed name ends in\n * `-background`). Netlify runs functions on AWS Lambda and sets\n * `AWS_LAMBDA_FUNCTION_NAME` to the function's name, so a `-background` suffix is\n * the runtime proof that the ~60s synchronous wall does NOT apply here.\n *\n * This is the SAFETY GUARD for the soft-timeout regime. The `_process-run`\n * self-dispatch worker (`isBackgroundWorker`) is NOT enough on its own: if the\n * `-background` function was never emitted (deploy gate off, or Netlify routed\n * the path to the synchronous function), the self-POST lands on the regular\n * ~60s `server` function. A worker there MUST use the 40s soft-timeout and\n * checkpoint before the 60s wall — using the ~13min budget would overshoot the\n * hard wall and get killed at 60s, then re-dispatch/resume in a wasteful loop.\n * So the 13-min budget is taken ONLY when this returns true.\n *\n * Falls back to the explicit `AGENT_CHAT_FORCE_BACKGROUND_RUNTIME` env (truthy)\n * for hosts that don't expose a `-background`-suffixed function name but where\n * the operator has confirmed a long async budget. Off by default.\n */\nexport function isInBackgroundFunctionRuntime(): boolean {\n const lambdaName = process.env.AWS_LAMBDA_FUNCTION_NAME;\n if (\n typeof lambdaName === \"string\" &&\n lambdaName.toLowerCase().endsWith(\"-background\")\n ) {\n return true;\n }\n const forced = process.env.AGENT_CHAT_FORCE_BACKGROUND_RUNTIME;\n if (forced != null) {\n const v = forced.trim().toLowerCase();\n return v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\";\n }\n return false;\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 //\n // DEFAULT-ON: durable background runs are the desired behavior for every\n // hosted app. So an unset/empty/unknown flag means ON; an app opts OUT only\n // with an explicit falsy value. This still composes with the hosted +\n // A2A_SECRET gates below, so non-hosted / unconfigured apps stay synchronous.\n // Safety net: a failed dispatch degrades to a synchronous inline run (see\n // production-agent.ts), so default-on cannot break chat even if the\n // self-dispatch can't be delivered on a given app.\n const raw = process.env.AGENT_CHAT_DURABLE_BACKGROUND;\n if (raw == null) return true;\n const normalized = raw.trim().toLowerCase();\n return !(\n normalized === \"0\" ||\n normalized === \"false\" ||\n normalized === \"no\" ||\n normalized === \"off\"\n );\n}\n\n/**\n * The single gate. True when the flag is not explicitly disabled (default-on)\n * AND the runtime is hosted AND A2A_SECRET is configured. False otherwise — and\n * false means the current synchronous behavior is used, unchanged. So a local /\n * non-hosted / unconfigured app stays synchronous even with the flag defaulting\n * on; durable only engages where the runtime actually supports it.\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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"production-agent.d.ts","sourceRoot":"","sources":["../../src/agent/production-agent.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,IAAI,CAAC;AACzD,OAAO,KAAK,EACV,UAAU,EAEV,mBAAmB,EAEnB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,aAAa,EACb,iBAAiB,EAGlB,MAAM,mBAAmB,CAAC;AAmB3B,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAShE,OAAO,EAEL,cAAc,EACd,qBAAqB,EACrB,0BAA0B,EAC1B,MAAM,EACN,QAAQ,EAET,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"production-agent.d.ts","sourceRoot":"","sources":["../../src/agent/production-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ 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";
|
|
15
|
+
import { AGENT_CHAT_PROCESS_RUN_PATH, AGENT_CHAT_BACKGROUND_RUN_FIELD, isAgentChatDurableBackgroundEnabled, isInBackgroundFunctionRuntime, } from "./durable-background.js";
|
|
16
16
|
import { fireInternalDispatch } from "../server/self-dispatch.js";
|
|
17
17
|
import { readBody } from "../server/h3-helpers.js";
|
|
18
18
|
import { isReadOnlyShellCommand } from "../coding-tools/index.js";
|
|
@@ -2810,6 +2810,12 @@ export function createProductionAgentHandler(options) {
|
|
|
2810
2810
|
? body[AGENT_CHAT_BACKGROUND_RUN_FIELD]
|
|
2811
2811
|
: null;
|
|
2812
2812
|
const isBackgroundWorker = backgroundRunMarker !== null;
|
|
2813
|
+
// Whether this worker is REALLY executing inside a 15-min Netlify
|
|
2814
|
+
// `-background` function (proven by the runtime function name), not merely a
|
|
2815
|
+
// `_process-run` re-entry that may have landed on the ~60s synchronous
|
|
2816
|
+
// function. Only a true value unlocks the ~13-min soft-timeout budget; a
|
|
2817
|
+
// worker on the 60s function keeps the 40s clamp and checkpoints cleanly.
|
|
2818
|
+
const runsInBackgroundFunction = isBackgroundWorker && isInBackgroundFunctionRuntime();
|
|
2813
2819
|
// How many server-driven background continuations have already chained into
|
|
2814
2820
|
// this logical turn (0 on the first chunk). Used to bound the chain.
|
|
2815
2821
|
const backgroundContinuationCount = isBackgroundWorker &&
|
|
@@ -3323,6 +3329,7 @@ export function createProductionAgentHandler(options) {
|
|
|
3323
3329
|
// change. With the flag OFF this whole branch is skipped and the inline
|
|
3324
3330
|
// `startRun` path below runs exactly as before (byte-for-byte).
|
|
3325
3331
|
if (dispatchToBackground) {
|
|
3332
|
+
let backgroundRowInserted = false;
|
|
3326
3333
|
try {
|
|
3327
3334
|
// Insert the run row up front so /runs/active sees it immediately and
|
|
3328
3335
|
// the slot stays held while the background function cold-starts. Mark
|
|
@@ -3330,6 +3337,7 @@ export function createProductionAgentHandler(options) {
|
|
|
3330
3337
|
await insertRun(runId, effectiveThreadId, effectiveTurnId, {
|
|
3331
3338
|
dispatchMode: "background",
|
|
3332
3339
|
});
|
|
3340
|
+
backgroundRowInserted = true;
|
|
3333
3341
|
}
|
|
3334
3342
|
catch (err) {
|
|
3335
3343
|
// A duplicate-PK collision means the row already exists (retried POST);
|
|
@@ -3372,14 +3380,56 @@ export function createProductionAgentHandler(options) {
|
|
|
3372
3380
|
setResponseStatus(event, 500);
|
|
3373
3381
|
return { error: "Failed to subscribe to background run" };
|
|
3374
3382
|
}
|
|
3375
|
-
// Dispatch failed
|
|
3376
|
-
//
|
|
3377
|
-
//
|
|
3378
|
-
//
|
|
3379
|
-
//
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
+
// ─── Dispatch failed → degrade to a normal synchronous (inline) run ────
|
|
3384
|
+
// `fireInternalDispatch` throws ONLY when the self-POST failed *fast*
|
|
3385
|
+
// (rejected the connection, or returned a non-2xx within the ~250ms settle
|
|
3386
|
+
// race). The `_process-run` route verifies the HMAC token and validates
|
|
3387
|
+
// the body BEFORE it ever reaches the SQL atomic claim, so a fast throw
|
|
3388
|
+
// means NO background worker claimed this run — there is nothing to
|
|
3389
|
+
// double-execute. Rather than break the chat with "Failed to dispatch
|
|
3390
|
+
// background run", we run the turn inline (the same synchronous path the
|
|
3391
|
+
// flag-off branch below takes), reusing the already-inserted run row.
|
|
3392
|
+
//
|
|
3393
|
+
// Safety against a (very improbable) delayed background delivery: claim the
|
|
3394
|
+
// run atomically here via `claimBackgroundRun`, which flips the row's
|
|
3395
|
+
// dispatch_mode `background → background-processing` in one conditional
|
|
3396
|
+
// UPDATE. If a delayed dispatch DID land and a worker already won the
|
|
3397
|
+
// claim, our claim returns false and we must NOT run inline (that would
|
|
3398
|
+
// double-execute) — fall back to subscribing to the worker's run instead.
|
|
3399
|
+
// The SQL atomic claim is the single source of truth for ownership, so at
|
|
3400
|
+
// most one of {inline fallback, background worker} ever executes this run.
|
|
3401
|
+
if (backgroundRowInserted) {
|
|
3402
|
+
let claimedInline = false;
|
|
3403
|
+
try {
|
|
3404
|
+
claimedInline = await claimBackgroundRun(runId);
|
|
3405
|
+
}
|
|
3406
|
+
catch (err) {
|
|
3407
|
+
console.error("[agent-chat] inline-fallback claim failed:", err instanceof Error ? err.message : err);
|
|
3408
|
+
}
|
|
3409
|
+
if (!claimedInline) {
|
|
3410
|
+
// A background worker already owns this run (a delayed delivery landed
|
|
3411
|
+
// after our fast throw). Stream its events instead of running a second
|
|
3412
|
+
// copy. If we somehow can't subscribe, surface an error rather than
|
|
3413
|
+
// risk a double-run.
|
|
3414
|
+
const stream = subscribeToRun(runId, 0);
|
|
3415
|
+
if (stream) {
|
|
3416
|
+
setResponseHeader(event, "Content-Type", "text/event-stream");
|
|
3417
|
+
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
3418
|
+
setResponseHeader(event, "Connection", "keep-alive");
|
|
3419
|
+
setResponseHeader(event, "X-Run-Id", runId);
|
|
3420
|
+
return stream;
|
|
3421
|
+
}
|
|
3422
|
+
await updateRunStatusIfRunning(runId, "errored").catch(() => { });
|
|
3423
|
+
setResponseStatus(event, 500);
|
|
3424
|
+
return { error: "Failed to dispatch background run" };
|
|
3425
|
+
}
|
|
3426
|
+
// We own the run. `startRun` (below) calls `insertRun` again, but its
|
|
3427
|
+
// duplicate-PK collision is swallowed (`insertRun(...).catch(() => {})`),
|
|
3428
|
+
// so the existing `background-processing` row is reused — no double row.
|
|
3429
|
+
}
|
|
3430
|
+
// Fall through to the inline `startRun` path below (the same one the
|
|
3431
|
+
// flag-off branch uses). If the row was never inserted, `startRun` inserts
|
|
3432
|
+
// it fresh; if it was, the claim above made us the sole owner.
|
|
3383
3433
|
}
|
|
3384
3434
|
const trackedProgressOwner = trackInRunsTray === true && ownerEmail ? ownerEmail : null;
|
|
3385
3435
|
const trackedProgressRunId = trackedProgressOwner
|
|
@@ -3487,6 +3537,23 @@ export function createProductionAgentHandler(options) {
|
|
|
3487
3537
|
// chain. Bounded by MAX_BACKGROUND_RUN_CONTINUATIONS. Aborted /
|
|
3488
3538
|
// user-stopped runs do NOT chain.
|
|
3489
3539
|
if (shouldChainBackgroundContinuation({
|
|
3540
|
+
// Self-chain server-side for EVERY durable worker, not only the
|
|
3541
|
+
// ones inside a `-background` function. Server-driven
|
|
3542
|
+
// continuation is the whole point of durable background: the run
|
|
3543
|
+
// must survive the client disconnecting (closed tab), so it
|
|
3544
|
+
// cannot depend on the browser re-POSTing `auto_continue`. A
|
|
3545
|
+
// worker on the regular ~60s function — a Netlify routing miss,
|
|
3546
|
+
// or a non-Netlify host (Vercel/Cloudflare/Render/Fly) that
|
|
3547
|
+
// never emits a `-background` function — checkpoints at the 40s
|
|
3548
|
+
// soft-timeout and self-dispatches the next 40s chunk; a worker
|
|
3549
|
+
// in a real `-background` function chains ~13-min chunks. Only
|
|
3550
|
+
// the per-chunk BUDGET differs by function type (gated by
|
|
3551
|
+
// `runsInBackgroundFunction` at the startRun call below); the
|
|
3552
|
+
// continuation itself must stay server-driven on both. (The
|
|
3553
|
+
// self-chain is only reachable when the initial dispatch already
|
|
3554
|
+
// succeeded — a dispatch fast-fail degrades to the inline
|
|
3555
|
+
// foreground fallback, which is not a worker and rides the
|
|
3556
|
+
// connected client's auto_continue instead.)
|
|
3490
3557
|
isBackgroundWorker,
|
|
3491
3558
|
run,
|
|
3492
3559
|
continuationCount: backgroundContinuationCount,
|
|
@@ -3884,10 +3951,18 @@ export function createProductionAgentHandler(options) {
|
|
|
3884
3951
|
}, handleRunComplete, {
|
|
3885
3952
|
softTimeoutMs: options.runSoftTimeoutMs,
|
|
3886
3953
|
useHostedSoftTimeoutDefault: true,
|
|
3887
|
-
//
|
|
3888
|
-
//
|
|
3889
|
-
//
|
|
3890
|
-
|
|
3954
|
+
// Lift the soft-timeout clamp to ~13min ONLY when this run is actually
|
|
3955
|
+
// executing inside a real Netlify `-background` function (15-min budget,
|
|
3956
|
+
// no ~60s wall). Being the `_process-run` worker (`isBackgroundWorker`)
|
|
3957
|
+
// is NOT sufficient: if the `-background` function wasn't emitted, or
|
|
3958
|
+
// Netlify routed the self-POST to the synchronous function, the worker
|
|
3959
|
+
// landed on the regular ~60s `server` function — there it MUST keep the
|
|
3960
|
+
// 40s clamp and checkpoint before the wall, or it overshoots the 60s
|
|
3961
|
+
// hard kill and re-dispatches in a loop. `runsInBackgroundFunction`
|
|
3962
|
+
// gates the 13-min budget on the proven runtime, not merely on "I'm the
|
|
3963
|
+
// worker." Foreground runs never set this, so their 40s clamp is
|
|
3964
|
+
// unchanged.
|
|
3965
|
+
backgroundFunction: runsInBackgroundFunction,
|
|
3891
3966
|
// Fold continuation runs of one logical turn onto a single durable
|
|
3892
3967
|
// assistant message. Falls back to the runId (turn == run) when the
|
|
3893
3968
|
// client doesn't supply a turnId.
|