@agent-native/core 0.131.7 → 0.132.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 +2 -2
- package/corpus/core/CHANGELOG.md +123 -0
- package/corpus/core/docs/content/durable-background-runs.mdx +15 -6
- package/corpus/core/docs/design/durable-agent-runs.md +15 -11
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/activity.ts +127 -10
- package/corpus/core/src/a2a/index.ts +3 -0
- package/corpus/core/src/agent/durable-background.ts +38 -28
- package/corpus/core/src/agent/harness/background.ts +12 -2
- package/corpus/core/src/agent/production-agent.ts +18 -15
- package/corpus/core/src/agent/run-store.ts +45 -3
- package/corpus/core/src/application-state/handlers.ts +45 -0
- package/corpus/core/src/application-state/index.ts +3 -0
- package/corpus/core/src/application-state/store.ts +38 -34
- package/corpus/core/src/audit/redact.ts +104 -18
- package/corpus/core/src/cli/create.ts +115 -16
- package/corpus/core/src/client/AgentPanel.tsx +2 -7
- package/corpus/core/src/client/AssistantChat.tsx +10 -2
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
- package/corpus/core/src/client/RunStuckBanner.tsx +29 -6
- package/corpus/core/src/client/agent-chat.ts +4 -9
- package/corpus/core/src/client/application-state.ts +115 -12
- package/corpus/core/src/client/dynamic-suggestions.ts +4 -12
- package/corpus/core/src/client/frame-protocol.ts +5 -1
- package/corpus/core/src/client/guided-questions.tsx +12 -23
- package/corpus/core/src/db/client.ts +148 -28
- package/corpus/core/src/db/create-get-db.ts +39 -6
- package/corpus/core/src/db/runtime-diagnostics.ts +41 -6
- package/corpus/core/src/deploy/build.ts +26 -33
- package/corpus/core/src/feature-flags/store.ts +10 -5
- package/corpus/core/src/integrations/a2a-continuations-store.ts +11 -0
- package/corpus/core/src/integrations/config-store.ts +13 -0
- package/corpus/core/src/integrations/google-docs-poller.ts +26 -2
- package/corpus/core/src/mcp-client/routes.ts +33 -2
- package/corpus/core/src/onboarding/plugin.ts +31 -25
- package/corpus/core/src/org/accept-pending.ts +2 -0
- package/corpus/core/src/org/auto-join-domain.ts +2 -0
- package/corpus/core/src/org/context.ts +35 -10
- package/corpus/core/src/org/handlers.ts +6 -0
- package/corpus/core/src/org/request-org-cache.ts +71 -0
- package/corpus/core/src/resources/store.ts +10 -9
- package/corpus/core/src/scripts/runner.ts +6 -3
- package/corpus/core/src/secrets/storage.ts +97 -5
- package/corpus/core/src/server/action-change.ts +33 -0
- package/corpus/core/src/server/action-routes.ts +11 -9
- package/corpus/core/src/server/agent-teams.ts +10 -2
- package/corpus/core/src/server/better-auth-instance.ts +39 -12
- package/corpus/core/src/server/core-routes-plugin.ts +19 -2
- package/corpus/core/src/server/credential-provider.ts +57 -14
- package/corpus/core/src/server/http-response-telemetry.ts +1 -0
- package/corpus/core/src/server/poll.ts +95 -57
- package/corpus/core/src/server/voice-providers-status.ts +19 -4
- package/corpus/core/src/workspace-files/tool.ts +18 -11
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
- package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +299 -0
- package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +34 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +82 -0
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +2 -2
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +40 -34
- package/corpus/templates/analytics/app/hooks/use-user-pref.ts +3 -3
- package/corpus/templates/analytics/app/i18n-data.ts +40 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +18 -10
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +14 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +1 -0
- package/corpus/templates/analytics/app/routes/chart.tsx +3 -0
- package/corpus/templates/analytics/changelog/2026-07-30-analytics-dashboards-now-expose-native-funnel-heatmap-callou.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-30-analytics-now-ships-reusable-native-v2-dashboard-manifests-w.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +81 -1
- package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +457 -0
- package/corpus/templates/analytics/server/lib/report-chart-svg.ts +73 -2
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/analytics/shared/dashboard-funnel.ts +83 -0
- package/corpus/templates/clips/actions/generate-workflow.ts +6 -4
- package/corpus/templates/clips/actions/reconcile-workflow-generation.ts +257 -0
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +166 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-07-27-stopped-agent-runs-no-longer-leave-generated-workflow-cards-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-30-workflow-generation-now-stops-retrying-after-repeated-failur.md +6 -0
- package/corpus/templates/clips/shared/workflow.ts +5 -0
- package/corpus/templates/content/changelog/2026-07-30-long-running-delegated-requests-now-continue-reliably.md +6 -0
- package/corpus/templates/content/netlify.toml +3 -0
- package/corpus/templates/content/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -1
- package/corpus/templates/slides/actions/patch-deck.ts +25 -1
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +17 -4
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +9 -1
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +11 -3
- package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +39 -11
- package/corpus/templates/slides/app/pages/Index.tsx +2 -2
- package/corpus/templates/slides/changelog/2026-07-30-slide-previews-keep-their-aspect-ratio-isolate-embedded-styl.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slides-no-longer-lose-decks-while-organization-access-is-loa.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +8 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/composer/runtime-adapters.tsx +16 -11
- package/dist/a2a/activity.d.ts +22 -4
- package/dist/a2a/activity.d.ts.map +1 -1
- package/dist/a2a/activity.js +97 -11
- package/dist/a2a/activity.js.map +1 -1
- package/dist/a2a/index.d.ts +1 -1
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +1 -1
- package/dist/a2a/index.js.map +1 -1
- package/dist/agent/durable-background.d.ts +13 -8
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +36 -28
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/harness/background.d.ts.map +1 -1
- package/dist/agent/harness/background.js +12 -2
- package/dist/agent/harness/background.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +18 -15
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts +2 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +44 -3
- package/dist/agent/run-store.js.map +1 -1
- package/dist/application-state/handlers.d.ts +15 -0
- package/dist/application-state/handlers.d.ts.map +1 -1
- package/dist/application-state/handlers.js +42 -2
- package/dist/application-state/handlers.js.map +1 -1
- package/dist/application-state/index.d.ts +2 -2
- package/dist/application-state/index.d.ts.map +1 -1
- package/dist/application-state/index.js +2 -2
- package/dist/application-state/index.js.map +1 -1
- package/dist/application-state/store.d.ts +11 -0
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +37 -40
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/redact.d.ts +25 -2
- package/dist/audit/redact.d.ts.map +1 -1
- package/dist/audit/redact.js +85 -21
- package/dist/audit/redact.js.map +1 -1
- package/dist/cli/create.d.ts +8 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +98 -16
- package/dist/cli/create.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -7
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +10 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +11 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +20 -7
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +4 -6
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/application-state.d.ts +11 -0
- package/dist/client/application-state.d.ts.map +1 -1
- package/dist/client/application-state.js +87 -9
- package/dist/client/application-state.js.map +1 -1
- package/dist/client/dynamic-suggestions.d.ts.map +1 -1
- package/dist/client/dynamic-suggestions.js +4 -13
- package/dist/client/dynamic-suggestions.js.map +1 -1
- package/dist/client/frame-protocol.d.ts +1 -0
- package/dist/client/frame-protocol.d.ts.map +1 -1
- package/dist/client/frame-protocol.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +6 -22
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.d.ts +57 -10
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +122 -31
- package/dist/db/client.js.map +1 -1
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +31 -7
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/db/runtime-diagnostics.d.ts +1 -0
- package/dist/db/runtime-diagnostics.d.ts.map +1 -1
- package/dist/db/runtime-diagnostics.js +31 -6
- package/dist/db/runtime-diagnostics.js.map +1 -1
- package/dist/deploy/build.d.ts +5 -20
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +18 -30
- package/dist/deploy/build.js.map +1 -1
- package/dist/feature-flags/store.d.ts.map +1 -1
- package/dist/feature-flags/store.js +10 -5
- package/dist/feature-flags/store.js.map +1 -1
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +12 -0
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/config-store.d.ts +1 -0
- package/dist/integrations/config-store.d.ts.map +1 -1
- package/dist/integrations/config-store.js +11 -0
- package/dist/integrations/config-store.js.map +1 -1
- package/dist/integrations/google-docs-poller.d.ts.map +1 -1
- package/dist/integrations/google-docs-poller.js +19 -2
- package/dist/integrations/google-docs-poller.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +30 -2
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/onboarding/plugin.d.ts.map +1 -1
- package/dist/onboarding/plugin.js +12 -8
- package/dist/onboarding/plugin.js.map +1 -1
- package/dist/org/accept-pending.d.ts.map +1 -1
- package/dist/org/accept-pending.js +2 -0
- package/dist/org/accept-pending.js.map +1 -1
- package/dist/org/auto-join-domain.d.ts.map +1 -1
- package/dist/org/auto-join-domain.js +2 -0
- package/dist/org/auto-join-domain.js.map +1 -1
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +29 -9
- package/dist/org/context.js.map +1 -1
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +5 -0
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/request-org-cache.d.ts +16 -0
- package/dist/org/request-org-cache.d.ts.map +1 -0
- package/dist/org/request-org-cache.js +62 -0
- package/dist/org/request-org-cache.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +11 -9
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +3 -3
- package/dist/scripts/runner.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +82 -5
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/action-change.d.ts +20 -0
- package/dist/server/action-change.d.ts.map +1 -1
- package/dist/server/action-change.js +29 -0
- package/dist/server/action-change.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +7 -8
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +10 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +31 -14
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +18 -4
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +16 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +49 -13
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/http-response-telemetry.d.ts.map +1 -1
- package/dist/server/http-response-telemetry.js +1 -0
- package/dist/server/http-response-telemetry.js.map +1 -1
- package/dist/server/poll.d.ts +6 -0
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +84 -47
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +12 -3
- package/dist/server/voice-providers-status.js.map +1 -1
- package/dist/workspace-files/tool.d.ts.map +1 -1
- package/dist/workspace-files/tool.js +18 -11
- package/dist/workspace-files/tool.js.map +1 -1
- package/docs/content/durable-background-runs.mdx +15 -6
- package/docs/design/durable-agent-runs.md +15 -11
- package/package.json +3 -3
- package/src/a2a/activity.ts +127 -10
- package/src/a2a/index.ts +3 -0
- package/src/agent/durable-background.ts +38 -28
- package/src/agent/harness/background.ts +12 -2
- package/src/agent/production-agent.ts +18 -15
- package/src/agent/run-store.ts +45 -3
- package/src/application-state/handlers.ts +45 -0
- package/src/application-state/index.ts +3 -0
- package/src/application-state/store.ts +38 -34
- package/src/audit/redact.ts +104 -18
- package/src/cli/create.ts +115 -16
- package/src/client/AgentPanel.tsx +2 -7
- package/src/client/AssistantChat.tsx +10 -2
- package/src/client/MultiTabAssistantChat.tsx +3 -0
- package/src/client/RunStuckBanner.tsx +29 -6
- package/src/client/agent-chat.ts +4 -9
- package/src/client/application-state.ts +115 -12
- package/src/client/dynamic-suggestions.ts +4 -12
- package/src/client/frame-protocol.ts +5 -1
- package/src/client/guided-questions.tsx +12 -23
- package/src/db/client.ts +148 -28
- package/src/db/create-get-db.ts +39 -6
- package/src/db/runtime-diagnostics.ts +41 -6
- package/src/deploy/build.ts +26 -33
- package/src/feature-flags/store.ts +10 -5
- package/src/integrations/a2a-continuations-store.ts +11 -0
- package/src/integrations/config-store.ts +13 -0
- package/src/integrations/google-docs-poller.ts +26 -2
- package/src/mcp-client/routes.ts +33 -2
- package/src/onboarding/plugin.ts +31 -25
- package/src/org/accept-pending.ts +2 -0
- package/src/org/auto-join-domain.ts +2 -0
- package/src/org/context.ts +35 -10
- package/src/org/handlers.ts +6 -0
- package/src/org/request-org-cache.ts +71 -0
- package/src/resources/store.ts +10 -9
- package/src/scripts/runner.ts +6 -3
- package/src/secrets/storage.ts +97 -5
- package/src/server/action-change.ts +33 -0
- package/src/server/action-routes.ts +11 -9
- package/src/server/agent-teams.ts +10 -2
- package/src/server/better-auth-instance.ts +39 -12
- package/src/server/core-routes-plugin.ts +19 -2
- package/src/server/credential-provider.ts +57 -14
- package/src/server/http-response-telemetry.ts +1 -0
- package/src/server/poll.ts +95 -57
- package/src/server/voice-providers-status.ts +19 -4
- package/src/workspace-files/tool.ts +18 -11
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,128 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.132.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 89e5910: Enable durable agent-chat background runs by default for deployed Netlify apps, with an explicit opt-out.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 89e5910: Batch application-state reads. `GET /_agent-native/application-state?keys=a,b,c`
|
|
12
|
+
returns many keys in one request, and `readClientAppState` coalesces reads
|
|
13
|
+
issued in the same tick into that single call, so a page load no longer pays one
|
|
14
|
+
HTTP round trip and one full identity resolution per key. The response reports
|
|
15
|
+
absent keys in `missing` rather than as `null` values, keeping "never written"
|
|
16
|
+
distinguishable from "written as null or empty"; the single-key routes are
|
|
17
|
+
unchanged.
|
|
18
|
+
- 89e5910: Cut database round trips on the status endpoints a page load hits repeatedly.
|
|
19
|
+
- Added `prefetchSecrets(keys)`, which warms the per-request secret memo with
|
|
20
|
+
one batched read per scope instead of one read per key per scope, and used it
|
|
21
|
+
in `/_agent-native/env-status` (48 single-key `app_secrets` selects per request
|
|
22
|
+
→ 4 batched) and `/_agent-native/voice-providers/status` (19 → 4).
|
|
23
|
+
- The change marker after an action now honours a per-call Plan-mode `effect`
|
|
24
|
+
before the action-level `readOnly` flag. A status poll shaped as a mutating
|
|
25
|
+
action — `manage-agent-engine` with `action: "list"`, polled every few seconds
|
|
26
|
+
— no longer bumps the `"action"` change version, so queries keyed on it stop
|
|
27
|
+
refetching on an idle page.
|
|
28
|
+
- The default database schema health probe runs its table checks in parallel and
|
|
29
|
+
memoizes a clean result for a few seconds. A probe reporting a missing table or
|
|
30
|
+
an unreadable database is never memoized.
|
|
31
|
+
|
|
32
|
+
- 89e5910: Record tool arguments and result summaries in delegated-run traces. The A2A
|
|
33
|
+
agent-activity snapshot and the agent-team / harness background transcripts now
|
|
34
|
+
carry each tool call's arguments and a result preview in redacted, size-capped
|
|
35
|
+
form, so a delegated run that loops on the same tool is diagnosable from what
|
|
36
|
+
was recorded instead of needing a fresh repro. Captures reuse the audit
|
|
37
|
+
redaction helper (credential-looking keys and values become `[redacted]`),
|
|
38
|
+
oversized values keep an explicit `…(N more chars)` / `_auditTruncated` marker,
|
|
39
|
+
and a shared per-snapshot payload budget keeps the activity part inside its
|
|
40
|
+
wire limit.
|
|
41
|
+
- 89e5910: Share one Postgres connection pool per URL across the whole process instead of
|
|
42
|
+
building a separate one for the `getDbExec` singleton, Better Auth, and every
|
|
43
|
+
`createGetDb` store. Against a remote database that removed five redundant
|
|
44
|
+
first-connect round trips per process, and it let the pool cap rise so a
|
|
45
|
+
request's concurrent reads no longer serialize behind a single connection.
|
|
46
|
+
Secret reads are now memoized per request, keyed on scope and scope id, so the
|
|
47
|
+
user/org/workspace credential waterfall is not re-walked on every lookup.
|
|
48
|
+
Onboarding step status, resource inheritance layers, and feature-flag rules now
|
|
49
|
+
resolve their independent reads concurrently.
|
|
50
|
+
- 89e5910: Give delegated agent turns their app's actions as native tools in dev, so asking
|
|
51
|
+
a sibling app a question actually returns an answer.
|
|
52
|
+
|
|
53
|
+
In dev the interactive surface deliberately omits template actions from the tool
|
|
54
|
+
registry and lets the agent reach them through `bash`, which sidesteps the
|
|
55
|
+
degenerate empty-object tool call some models emit for complex schemas. That is
|
|
56
|
+
a reasonable trade for a person, who sees the bad call and rephrases. It is the
|
|
57
|
+
wrong trade for a delegated turn. An A2A caller, or an external host calling
|
|
58
|
+
`ask_app` over MCP, has nobody to intervene: with no native action the receiving
|
|
59
|
+
agent shells out, the call runs long, and the caller records "Interrupted before
|
|
60
|
+
this tool returned a result" — after which callers commonly fall back to
|
|
61
|
+
composing their own queries against a schema they do not own.
|
|
62
|
+
|
|
63
|
+
Both delegated surfaces now keep template actions native even in dev. A rejected
|
|
64
|
+
`{}` call returns a schema error the model can correct on its next step, which
|
|
65
|
+
is strictly better than a shell loop no caller can see or recover from.
|
|
66
|
+
|
|
67
|
+
- 89e5910: Stop paying for background sweeps and no-change polls that find nothing.
|
|
68
|
+
|
|
69
|
+
A workspace runs one server per app, so every recurring sweep multiplies by app
|
|
70
|
+
count. Several of them queried unconditionally: the 20-second unclaimed-run
|
|
71
|
+
sweep issued two `agent_runs` scans back to back, the A2A continuation retry ran
|
|
72
|
+
two blind `UPDATE`s before ever checking whether anything was due, the MCP config
|
|
73
|
+
refresh scanned the whole settings table every minute to diff a signature that
|
|
74
|
+
had not moved since boot, and the Google Docs poller re-read its config every 30
|
|
75
|
+
seconds even on deployments where the integration was never enabled. On local
|
|
76
|
+
SQLite that was free; on a remote or metered database each one is a network round
|
|
77
|
+
trip, forever, per app.
|
|
78
|
+
|
|
79
|
+
Each of those now leads with a cheap existence probe or an in-process change
|
|
80
|
+
signal, so the idle case costs one round trip instead of several and the work
|
|
81
|
+
still runs the moment there is any. The poll route gets the same treatment: its
|
|
82
|
+
legacy watermark scan read `application_state` four separate times per check,
|
|
83
|
+
and now reads `MAX(updated_at)` once and only fans out when that advances — a
|
|
84
|
+
cost that repeated per app per connected client.
|
|
85
|
+
|
|
86
|
+
Detection latency is unchanged: every probe is a strict superset of the predicate
|
|
87
|
+
it guards, and the negative caches are all narrower than the staleness window
|
|
88
|
+
they sit in front of.
|
|
89
|
+
|
|
90
|
+
- 89e5910: Cut one database round trip from every authenticated request. The membership
|
|
91
|
+
and `active-org-id` reads behind org resolution now overlap instead of queueing,
|
|
92
|
+
and `resolveOrgIdForEmail` memoizes its `org_members` read per request (keyed on
|
|
93
|
+
the AsyncLocalStorage request context and the email) so credential lookups,
|
|
94
|
+
agent runs, A2A, MCP, and adapter-authenticated action calls — none of which
|
|
95
|
+
carry an h3 event — stop each paying their own lookup.
|
|
96
|
+
- 89e5910: Refresh derived database consumers after pool recycling, redact embedded
|
|
97
|
+
credentials from captured tool results, preserve multiline A2A previews, and
|
|
98
|
+
retry MCP configuration refreshes after transient failures.
|
|
99
|
+
- Updated dependencies [89e5910]
|
|
100
|
+
- @agent-native/toolkit@0.12.1
|
|
101
|
+
|
|
102
|
+
## 0.131.9
|
|
103
|
+
|
|
104
|
+
### Patch Changes
|
|
105
|
+
|
|
106
|
+
- d80a9c9: Report workspace files as truncated only when more content exists beyond the requested read page, and normalize paging arguments to integer boundaries.
|
|
107
|
+
- 3c538e4: Preserve application-state database read failures and distinguish explicit stops or exhausted reconnect failures from recoverable chat handoffs.
|
|
108
|
+
|
|
109
|
+
## 0.131.8
|
|
110
|
+
|
|
111
|
+
### Patch Changes
|
|
112
|
+
|
|
113
|
+
- c7ec59d: `create .` now scaffolds into the current directory and takes the project name
|
|
114
|
+
from the folder's basename, matching `create-react-app` / `npm init`. Previously
|
|
115
|
+
`.` was rejected as an invalid name. The current directory must be empty apart
|
|
116
|
+
from benign VCS/editor files (`.git`, `.gitignore`, `LICENSE`, `README.md`, …)
|
|
117
|
+
so an existing project is never merged over.
|
|
118
|
+
|
|
119
|
+
The scaffold is built in a private staging directory and only the files that
|
|
120
|
+
don't already exist are copied in, so a mid-scaffold failure can never delete
|
|
121
|
+
the current directory (including `.git`) and pre-existing files like
|
|
122
|
+
`README.md` and `.gitignore` are preserved. When the current directory is
|
|
123
|
+
already a git repo, `create .` skips `git init`/commit so it never writes an
|
|
124
|
+
unexpected commit into the user's history.
|
|
125
|
+
|
|
3
126
|
## 0.131.7
|
|
4
127
|
|
|
5
128
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Durable Background Runs"
|
|
3
|
-
description: "Long agent-chat turns can run on a separate 15-minute Netlify background function instead of the synchronous request, so multi-step work finishes server-side without the client babysitting continuations.
|
|
3
|
+
description: "Long agent-chat turns can run on a separate 15-minute Netlify background function instead of the synchronous request, so multi-step work finishes server-side without the client babysitting continuations. Enabled by default on deployed Netlify apps with an explicit opt-out; a foreground circuit-breaker keeps chat working even if the worker never starts."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Durable Background Runs
|
|
@@ -10,7 +10,8 @@ description: "Long agent-chat turns can run on a separate 15-minute Netlify back
|
|
|
10
10
|
**Who is this for:** anyone deploying an agent-native app who wants long,
|
|
11
11
|
multi-step agent turns to complete server-side, and anyone wondering what the
|
|
12
12
|
`AGENT_CHAT_DURABLE_BACKGROUND` flag and the second Netlify function do. This
|
|
13
|
-
is
|
|
13
|
+
is enabled by default on deployed Netlify apps and entirely inside the
|
|
14
|
+
framework — there is no app code to write.
|
|
14
15
|
|
|
15
16
|
</Callout>
|
|
16
17
|
|
|
@@ -108,14 +109,17 @@ normal API calls always run synchronously on `server`.
|
|
|
108
109
|
The foreground chat POST decides **per turn**. It dispatches to the background
|
|
109
110
|
function only when all three hold:
|
|
110
111
|
|
|
111
|
-
-
|
|
112
|
+
- the app is deployed on Netlify and `AGENT_CHAT_DURABLE_BACKGROUND` is not
|
|
113
|
+
explicitly falsy (`false` / `0` / `no` / `off`), or a non-Netlify hosted app
|
|
114
|
+
explicitly opts in with a truthy value (`true` / `1` / `yes` / `on`),
|
|
112
115
|
- the app is running hosted (on the platform, not local dev), and
|
|
113
116
|
- `A2A_SECRET` is set (the dispatch is an HMAC-signed self-call; without the
|
|
114
117
|
secret it cannot be signed, so the turn stays inline).
|
|
115
118
|
|
|
116
|
-
If
|
|
119
|
+
If the app is local, `A2A_SECRET` is missing, or the flag is explicitly
|
|
120
|
+
disabled, the turn runs inline exactly as before. The setting is read at
|
|
117
121
|
**both build time** (to decide whether to emit the background function) and
|
|
118
|
-
**runtime** (to decide whether to dispatch), so
|
|
122
|
+
**runtime** (to decide whether to dispatch), so changing it requires a redeploy.
|
|
119
123
|
|
|
120
124
|
## The lifecycle and the circuit-breaker {#lifecycle}
|
|
121
125
|
|
|
@@ -190,7 +194,12 @@ one long invocation, not purely additive.
|
|
|
190
194
|
|
|
191
195
|
<Callout id="doc-block-gjoj2d" tone="info">
|
|
192
196
|
|
|
193
|
-
Durable runs are
|
|
197
|
+
Durable runs are enabled by default for deployed Netlify apps. Set
|
|
198
|
+
`AGENT_CHAT_DURABLE_BACKGROUND=false` to opt out, or set it to `true` on another
|
|
199
|
+
host. `A2A_SECRET` is still required. Redeploy after changing the setting, and
|
|
200
|
+
verify a turn actually used the worker by checking that its run reaches
|
|
201
|
+
`dispatch_mode=background-processing` rather than falling back to inline
|
|
202
|
+
recovery.
|
|
194
203
|
|
|
195
204
|
</Callout>
|
|
196
205
|
|
|
@@ -200,7 +200,8 @@ itself checkpoint).
|
|
|
200
200
|
|
|
201
201
|
> Status: **Phase 0 shipped. Phase 1 + Phase 2 implemented** (the host-agnostic
|
|
202
202
|
> durable-background worker and the Netlify 15-min `-background` optimization),
|
|
203
|
-
>
|
|
203
|
+
> default-on for deployed Netlify apps with an explicit opt-out via
|
|
204
|
+
> `AGENT_CHAT_DURABLE_BACKGROUND=false`. Internal per-step
|
|
204
205
|
> checkpointing (the Option A core of Phase 1) is recommended-not-yet-built; the
|
|
205
206
|
> worker today gets its durability from the long single invocation plus
|
|
206
207
|
> server-chained continuations rather than per-step idempotent checkpoints. See
|
|
@@ -251,13 +252,14 @@ checkpointed and durable runs remove the ceiling itself.
|
|
|
251
252
|
|
|
252
253
|
# Final layered architecture
|
|
253
254
|
|
|
254
|
-
Status: implemented (
|
|
255
|
+
Status: implemented (default-on for deployed Netlify, explicit opt-out)
|
|
255
256
|
Owner: core / run-manager + deploy
|
|
256
257
|
|
|
257
|
-
Durable background runs ship as **two layers**.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
258
|
+
Durable background runs ship as **two layers**. Deployed Netlify apps enable the
|
|
259
|
+
path by default and can opt out with an explicit falsy
|
|
260
|
+
`AGENT_CHAT_DURABLE_BACKGROUND` value. Other hosted platforms retain explicit
|
|
261
|
+
opt-in. When off, the agent-chat run path and deploy output are byte-for-byte
|
|
262
|
+
the pre-existing synchronous behavior.
|
|
261
263
|
|
|
262
264
|
### Layer 1 (portable baseline) — host-agnostic durable execution
|
|
263
265
|
|
|
@@ -600,8 +602,9 @@ Already strong; make the new claim match:
|
|
|
600
602
|
|
|
601
603
|
> Status: \*\*Slices 0–1 implemented and the Slice-3 background-aware stale window
|
|
602
604
|
>
|
|
603
|
-
> - background→background continuation chaining are implemented\*\*,
|
|
604
|
-
>
|
|
605
|
+
> - background→background continuation chaining are implemented\*\*, default-on
|
|
606
|
+
> for deployed Netlify apps with `AGENT_CHAT_DURABLE_BACKGROUND=false` as the
|
|
607
|
+
> opt-out. The host-agnostic baseline
|
|
605
608
|
> (Layer 1) carries the run on any host; the Netlify `-background` emit (Layer 2)
|
|
606
609
|
> is the deploy-time optimization. Slice 2's richer reconnect-first client UX and
|
|
607
610
|
> the internal per-step checkpointing (Option A) remain follow-ups; Slice 4
|
|
@@ -618,9 +621,10 @@ just writes a run event. Dispatch to it from a temporary test route via
|
|
|
618
621
|
stub writes an event to `agent_run_events` from the background function, visible
|
|
619
622
|
via `GET /runs/:id/events`. This de-risks the only genuinely new infra.
|
|
620
623
|
|
|
621
|
-
**Slice 1 — route the real chat loop through the background function
|
|
622
|
-
|
|
623
|
-
|
|
624
|
+
**Slice 1 — route the real chat loop through the background function.** On
|
|
625
|
+
deployed Netlify it is enabled by default; `AGENT_CHAT_DURABLE_BACKGROUND=false`
|
|
626
|
+
opts out. Other hosted platforms require explicit opt-in, and all hosts require
|
|
627
|
+
`A2A_SECRET`: the foreground handler inserts the run row, dispatches, returns the SSE
|
|
624
628
|
stream from `subscribeToRun`. The background `_process-run` claims the run and
|
|
625
629
|
calls the same `startRun` + `runAgentLoop` the inline path uses, with
|
|
626
630
|
`softTimeoutMs ≈ 13min` (new `backgroundFunction` option in
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.132.0",
|
|
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": {
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import { sanitizeToolErrorText } from "../agent/tool-error-redaction.js";
|
|
2
2
|
import type { AgentChatEvent } from "../agent/types.js";
|
|
3
|
+
import { redactArgsToJson, redactTextToSummary } from "../audit/redact.js";
|
|
3
4
|
import type { DataPart, Part } from "./types.js";
|
|
4
5
|
|
|
5
6
|
export const A2A_AGENT_ACTIVITY_KIND = "agent-native/agent-activity";
|
|
6
7
|
export const A2A_AGENT_ACTIVITY_VERSION = 1;
|
|
7
8
|
export const MAX_A2A_ACTIVITY_REASONING_CHARS = 32_768;
|
|
8
9
|
export const MAX_A2A_ACTIVITY_RESPONSE_CHARS = 32_768;
|
|
9
|
-
export const MAX_A2A_ACTIVITY_TOTAL_CHARS = 98_304;
|
|
10
10
|
export const MAX_A2A_ACTIVITY_REASONING_SEGMENTS = 128;
|
|
11
11
|
export const MAX_A2A_ACTIVITY_TOOL_CALLS = 64;
|
|
12
12
|
export const MAX_A2A_ACTIVITY_TOOL_NAME_CHARS = 96;
|
|
13
13
|
export const MAX_A2A_ACTIVITY_TOOL_ID_CHARS = 128;
|
|
14
|
+
export const MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS = 1024;
|
|
15
|
+
export const MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS = 512;
|
|
16
|
+
/**
|
|
17
|
+
* Shared ceiling for every recorded tool input/result across the snapshot. The
|
|
18
|
+
* per-call caps alone allow 64 × 1536 chars, which would push a busy run past
|
|
19
|
+
* `MAX_A2A_ACTIVITY_TOTAL_CHARS` and make `parseA2AAgentActivityPart` reject
|
|
20
|
+
* the whole snapshot — trading missing arguments for a missing trace.
|
|
21
|
+
*/
|
|
22
|
+
export const MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS = 16_384;
|
|
23
|
+
export const MAX_A2A_ACTIVITY_TOTAL_CHARS =
|
|
24
|
+
98_304 + MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS;
|
|
14
25
|
|
|
15
26
|
export type A2AAgentActivityPhase =
|
|
16
27
|
| "reasoning"
|
|
@@ -25,6 +36,13 @@ export interface A2AAgentActivityToolCall {
|
|
|
25
36
|
name: string;
|
|
26
37
|
id?: string;
|
|
27
38
|
status: A2AAgentActivityToolStatus;
|
|
39
|
+
/**
|
|
40
|
+
* Redacted, size-capped JSON of the call's arguments. Absent when the call
|
|
41
|
+
* had none, or when the snapshot's shared payload budget was already spent.
|
|
42
|
+
*/
|
|
43
|
+
input?: string;
|
|
44
|
+
/** Redacted, size-capped head of the tool result (`tool_done` only). */
|
|
45
|
+
result?: string;
|
|
28
46
|
}
|
|
29
47
|
|
|
30
48
|
export interface A2AAgentActivitySnapshot extends Record<string, unknown> {
|
|
@@ -68,9 +86,11 @@ export function createA2AAgentActivityState(
|
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
/**
|
|
71
|
-
* Converts internal loop events into a bounded activity snapshot.
|
|
72
|
-
*
|
|
73
|
-
* results
|
|
89
|
+
* Converts internal loop events into a bounded activity snapshot. Reasoning
|
|
90
|
+
* text is carried verbatim (it was already emitted to the local chat); tool
|
|
91
|
+
* arguments and results are carried only in redacted, size-capped form so the
|
|
92
|
+
* delegated run stays diagnosable without the snapshot becoming a secondary
|
|
93
|
+
* store of secrets or payloads.
|
|
74
94
|
*/
|
|
75
95
|
export function applyA2AAgentActivityEvent(
|
|
76
96
|
state: A2AAgentActivityState,
|
|
@@ -91,14 +111,26 @@ export function applyA2AAgentActivityEvent(
|
|
|
91
111
|
);
|
|
92
112
|
changed = true;
|
|
93
113
|
break;
|
|
94
|
-
case "tool_start":
|
|
114
|
+
case "tool_start": {
|
|
95
115
|
next.activePhase = "tool";
|
|
96
|
-
|
|
116
|
+
const input = captureToolInput(
|
|
117
|
+
event.input,
|
|
118
|
+
remainingPayloadBudget(next.toolCalls),
|
|
119
|
+
);
|
|
120
|
+
next.toolCalls = appendToolCall(next.toolCalls, {
|
|
121
|
+
...toolFromEvent(event),
|
|
122
|
+
...(input ? { input } : {}),
|
|
123
|
+
});
|
|
97
124
|
changed = true;
|
|
98
125
|
break;
|
|
126
|
+
}
|
|
99
127
|
case "tool_done":
|
|
100
128
|
next.activePhase = "tool";
|
|
101
|
-
next.toolCalls = settleToolCall(
|
|
129
|
+
next.toolCalls = settleToolCall(
|
|
130
|
+
next.toolCalls,
|
|
131
|
+
toolFromEvent(event),
|
|
132
|
+
event,
|
|
133
|
+
);
|
|
102
134
|
changed = true;
|
|
103
135
|
break;
|
|
104
136
|
case "text":
|
|
@@ -203,6 +235,49 @@ function toolFromEvent(
|
|
|
203
235
|
};
|
|
204
236
|
}
|
|
205
237
|
|
|
238
|
+
function payloadChars(tool: A2AAgentActivityToolCall): number {
|
|
239
|
+
return (tool.input?.length ?? 0) + (tool.result?.length ?? 0);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function remainingPayloadBudget(
|
|
243
|
+
current: A2AAgentActivityToolCall[],
|
|
244
|
+
excludeIndex = -1,
|
|
245
|
+
): number {
|
|
246
|
+
const used = current.reduce(
|
|
247
|
+
(total, tool, index) =>
|
|
248
|
+
index === excludeIndex ? total : total + payloadChars(tool),
|
|
249
|
+
0,
|
|
250
|
+
);
|
|
251
|
+
return Math.max(0, MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS - used);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Redacted, capped JSON of the arguments, or `undefined` when there is nothing
|
|
256
|
+
* to record or the shared budget cannot fit a whole (still-parseable) capture.
|
|
257
|
+
* A partial JSON string would be worse than none — the reader could not tell a
|
|
258
|
+
* clipped object from a different one.
|
|
259
|
+
*/
|
|
260
|
+
function captureToolInput(input: unknown, budget: number): string | undefined {
|
|
261
|
+
const cap = Math.min(MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS, budget);
|
|
262
|
+
if (cap < MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS / 8) return undefined;
|
|
263
|
+
const json = redactArgsToJson(input, { maxJson: cap, maxString: 256 });
|
|
264
|
+
// JSON.stringify escapes control characters, so the result is already safe
|
|
265
|
+
// for the snapshot without a `sanitizeText` pass — which would rewrite
|
|
266
|
+
// `"token":"[redacted]"` into unparseable JSON.
|
|
267
|
+
if (!json || json === "{}" || json.length > cap) return undefined;
|
|
268
|
+
return json;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function captureToolResult(
|
|
272
|
+
result: string | undefined,
|
|
273
|
+
budget: number,
|
|
274
|
+
): string | undefined {
|
|
275
|
+
const cap = Math.min(MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS, budget);
|
|
276
|
+
if (!result || cap <= 0) return undefined;
|
|
277
|
+
const summary = redactTextToSummary(result, cap);
|
|
278
|
+
return summary ? sanitizeText(summary, cap) || undefined : undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
206
281
|
function appendBoundedSegment(
|
|
207
282
|
current: string[],
|
|
208
283
|
addition: string,
|
|
@@ -244,6 +319,7 @@ function appendToolCall(
|
|
|
244
319
|
function settleToolCall(
|
|
245
320
|
current: A2AAgentActivityToolCall[],
|
|
246
321
|
tool: A2AAgentActivityToolCall,
|
|
322
|
+
event: Extract<AgentChatEvent, { type: "tool_done" }>,
|
|
247
323
|
): A2AAgentActivityToolCall[] {
|
|
248
324
|
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
249
325
|
const existing = current[index];
|
|
@@ -251,12 +327,32 @@ function settleToolCall(
|
|
|
251
327
|
(tool.id && tool.id === existing.id) ||
|
|
252
328
|
(!tool.id && tool.name === existing.name)
|
|
253
329
|
) {
|
|
330
|
+
// The matching `tool_start` already paid for its input; reuse it rather
|
|
331
|
+
// than re-capturing (and re-charging) the same arguments.
|
|
332
|
+
let budget = remainingPayloadBudget(current, index);
|
|
333
|
+
const input =
|
|
334
|
+
existing.input ?? captureToolInput(event.input, budget) ?? undefined;
|
|
335
|
+
budget -= input?.length ?? 0;
|
|
336
|
+
const result = captureToolResult(event.result, budget);
|
|
337
|
+
const settled: A2AAgentActivityToolCall = {
|
|
338
|
+
...tool,
|
|
339
|
+
...(input ? { input } : {}),
|
|
340
|
+
...(result ? { result } : {}),
|
|
341
|
+
};
|
|
254
342
|
return current.map((value, itemIndex) =>
|
|
255
|
-
itemIndex === index ?
|
|
343
|
+
itemIndex === index ? settled : value,
|
|
256
344
|
);
|
|
257
345
|
}
|
|
258
346
|
}
|
|
259
|
-
|
|
347
|
+
let budget = remainingPayloadBudget(current);
|
|
348
|
+
const input = captureToolInput(event.input, budget);
|
|
349
|
+
budget -= input?.length ?? 0;
|
|
350
|
+
const result = captureToolResult(event.result, budget);
|
|
351
|
+
return appendToolCall(current, {
|
|
352
|
+
...tool,
|
|
353
|
+
...(input ? { input } : {}),
|
|
354
|
+
...(result ? { result } : {}),
|
|
355
|
+
});
|
|
260
356
|
}
|
|
261
357
|
|
|
262
358
|
function sanitizeText(value: string, maxChars: number): string {
|
|
@@ -320,12 +416,31 @@ function isSafeToolCall(value: unknown): value is A2AAgentActivityToolCall {
|
|
|
320
416
|
if (!isRecord(value) || !isSafeToolName(value.name)) return false;
|
|
321
417
|
return (
|
|
322
418
|
(value.id === undefined || isSafeToolId(value.id)) &&
|
|
419
|
+
(value.input === undefined ||
|
|
420
|
+
isSafeCapturedText(value.input, MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS)) &&
|
|
421
|
+
(value.result === undefined ||
|
|
422
|
+
isSafeCapturedText(value.result, MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS)) &&
|
|
323
423
|
(value.status === "running" ||
|
|
324
424
|
value.status === "completed" ||
|
|
325
425
|
value.status === "failed")
|
|
326
426
|
);
|
|
327
427
|
}
|
|
328
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Captured input/result are producer-side redacted strings. Unlike reasoning
|
|
431
|
+
* text they are NOT re-sanitized on read: `sanitizeToolErrorText` rewrites
|
|
432
|
+
* `"token":"…"` into unparseable JSON, so a round-trip equality check would
|
|
433
|
+
* reject every legitimate capture. Bound the length and reject raw control
|
|
434
|
+
* characters instead.
|
|
435
|
+
*/
|
|
436
|
+
function isSafeCapturedText(value: unknown, maxChars: number): value is string {
|
|
437
|
+
return (
|
|
438
|
+
typeof value === "string" &&
|
|
439
|
+
value.length <= maxChars &&
|
|
440
|
+
!/[\u0000-\u0008\u000b-\u001f\u007f]/.test(value)
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
329
444
|
function isSafeText(value: unknown, maxChars: number): value is string {
|
|
330
445
|
return (
|
|
331
446
|
typeof value === "string" &&
|
|
@@ -365,7 +480,9 @@ function activityCharacterCount(data: Record<string, unknown>): number {
|
|
|
365
480
|
return (
|
|
366
481
|
total +
|
|
367
482
|
(typeof tool.name === "string" ? tool.name.length : 0) +
|
|
368
|
-
(typeof tool.id === "string" ? tool.id.length : 0)
|
|
483
|
+
(typeof tool.id === "string" ? tool.id.length : 0) +
|
|
484
|
+
(typeof tool.input === "string" ? tool.input.length : 0) +
|
|
485
|
+
(typeof tool.result === "string" ? tool.result.length : 0)
|
|
369
486
|
);
|
|
370
487
|
}, 0)
|
|
371
488
|
: 0;
|
|
@@ -11,7 +11,10 @@ export {
|
|
|
11
11
|
MAX_A2A_ACTIVITY_TOOL_CALLS,
|
|
12
12
|
MAX_A2A_ACTIVITY_TOTAL_CHARS,
|
|
13
13
|
MAX_A2A_ACTIVITY_TOOL_ID_CHARS,
|
|
14
|
+
MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS,
|
|
14
15
|
MAX_A2A_ACTIVITY_TOOL_NAME_CHARS,
|
|
16
|
+
MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS,
|
|
17
|
+
MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS,
|
|
15
18
|
applyA2AAgentActivityEvent,
|
|
16
19
|
buildA2AAgentActivityPart,
|
|
17
20
|
buildA2AAgentActivitySnapshot,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Durable background agent-chat runs (Netlify background functions).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Enabled by default on deployed Netlify apps. When enabled, a long in-app
|
|
5
|
+
* agent-chat turn is dispatched
|
|
5
6
|
* into a Netlify *background* function (15-min budget) instead of completing
|
|
6
7
|
* synchronously under the ~40s soft-timeout. The foreground POST claims the
|
|
7
8
|
* run slot, inserts the run row, fires an HMAC-signed self-dispatch to
|
|
@@ -18,18 +19,17 @@
|
|
|
18
19
|
* GUARDRAIL: when `isAgentChatDurableBackgroundEnabled()` returns false, the
|
|
19
20
|
* agent-chat handler must behave byte-for-byte like the current synchronous
|
|
20
21
|
* path. The gate is true only when ALL of these hold:
|
|
21
|
-
* 1.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* 1. The runtime is a deployed Netlify app and
|
|
23
|
+
* `AGENT_CHAT_DURABLE_BACKGROUND` is not explicitly disabled, or the
|
|
24
|
+
* existing env/app opt-in path is used on another hosted platform. Netlify
|
|
25
|
+
* deploys emit the background function by default; `false`, `0`, `no`, or
|
|
26
|
+
* `off` disables it.
|
|
26
27
|
* 2. The runtime is hosted/serverless (local dev keeps the inline path so SSE
|
|
27
28
|
* stays a single live stream and no second function is needed).
|
|
28
29
|
* 3. `A2A_SECRET` is configured (the HMAC handoff is required to authenticate
|
|
29
30
|
* the background dispatch; without it the dispatch can't be trusted).
|
|
30
31
|
*
|
|
31
|
-
*
|
|
32
|
-
* proven. And even when enabled, a *dispatch failure degrades to an inline run*:
|
|
32
|
+
* Even when enabled, a *dispatch failure degrades to an inline run*:
|
|
33
33
|
* if the self-dispatch self-POST can't be delivered (fast connection error or
|
|
34
34
|
* fast non-2xx), the foreground handler runs the turn synchronously instead of
|
|
35
35
|
* erroring (see `production-agent.ts` — the inline fallback claims the run row
|
|
@@ -120,7 +120,7 @@ function isNetlifyHostedRuntimeForDispatch(): boolean {
|
|
|
120
120
|
// exposes AWS runtime variables, so keep the function-name fallback for older
|
|
121
121
|
// deploys. Without either check a modern Netlify Function silently selects the
|
|
122
122
|
// portable framework route even though the emitted background function exists.
|
|
123
|
-
if (process.env.SITE_ID) return true; // guard:allow-env-credential
|
|
123
|
+
if (process.env.SITE_ID) return true; // guard:allow-env-credential - Netlify's read-only public site identifier is a runtime host marker, not a user credential.
|
|
124
124
|
// Non-Netlify AWS falls back inline if the /.netlify/functions dispatch
|
|
125
125
|
// fast-fails.
|
|
126
126
|
return Boolean(process.env.AWS_LAMBDA_FUNCTION_NAME);
|
|
@@ -186,8 +186,9 @@ export function dispatchPathTargetsNetlifyBackgroundFunction(
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
* Env flag for durable background runs.
|
|
190
|
-
*
|
|
189
|
+
* Env flag for durable background runs. On Netlify, unset means enabled and an
|
|
190
|
+
* explicit falsy value opts out. On other hosted platforms, apps still opt in
|
|
191
|
+
* with an explicit truthy value (`true`/`1`/`yes`/`on`).
|
|
191
192
|
*/
|
|
192
193
|
export const AGENT_CHAT_DURABLE_BACKGROUND_ENV =
|
|
193
194
|
"AGENT_CHAT_DURABLE_BACKGROUND";
|
|
@@ -233,6 +234,20 @@ export function isHostedRuntimeForDurableBackground(): boolean {
|
|
|
233
234
|
);
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Netlify is the only host with the emitted 15-minute background function.
|
|
239
|
+
* Keep the framework-wide default scoped to deployed Netlify rather than
|
|
240
|
+
* silently enabling it on other hosted runtimes that may not have that worker.
|
|
241
|
+
*/
|
|
242
|
+
export function isNetlifyHostedRuntimeForDurableBackground(): boolean {
|
|
243
|
+
if (process.env.NETLIFY_LOCAL === "true") return false;
|
|
244
|
+
if (process.env.NETLIFY === "false") return false;
|
|
245
|
+
return Boolean(
|
|
246
|
+
(process.env.NETLIFY && process.env.NETLIFY !== "false") ||
|
|
247
|
+
process.env.SITE_ID, // guard:allow-env-credential - Netlify's read-only public site identifier is a runtime host marker, not a user credential.
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
236
251
|
/**
|
|
237
252
|
* True when THIS process is actually executing inside a Netlify *background*
|
|
238
253
|
* function (the long, 15-min-budget async function whose deployed name ends in
|
|
@@ -353,16 +368,10 @@ export function isDurableBackgroundFlagEnabled(): boolean {
|
|
|
353
368
|
// can statically verify it against the allowlisted `AGENT_*` prefix. Keep this
|
|
354
369
|
// in sync with AGENT_CHAT_DURABLE_BACKGROUND_ENV.
|
|
355
370
|
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
// not reliably baked into a given deploy. So an unset/empty/unknown flag means
|
|
361
|
-
// OFF; an app opts IN only with an explicit truthy value
|
|
362
|
-
// (AGENT_CHAT_DURABLE_BACKGROUND=true). This still composes with the hosted +
|
|
363
|
-
// A2A_SECRET gates below. Flip back to default-on only after the 15-min
|
|
364
|
-
// background-function worker is verified live in production (see the
|
|
365
|
-
// project_durable_bg_prod_verified memory).
|
|
371
|
+
// This parses only the explicit opt-in signal. Netlify's default-on behavior
|
|
372
|
+
// is composed separately in isAgentChatDurableBackgroundEnabled, while other
|
|
373
|
+
// hosted runtimes still require this truthy value. Empty and unknown values
|
|
374
|
+
// remain false so an explicit host opt-in cannot be inferred accidentally.
|
|
366
375
|
const raw = process.env.AGENT_CHAT_DURABLE_BACKGROUND;
|
|
367
376
|
if (raw == null) return false;
|
|
368
377
|
const normalized = raw.trim().toLowerCase();
|
|
@@ -387,12 +396,10 @@ export function isDurableBackgroundFlagExplicitlyDisabled(): boolean {
|
|
|
387
396
|
}
|
|
388
397
|
|
|
389
398
|
/**
|
|
390
|
-
* The single gate.
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
* current synchronous behavior is used unchanged.
|
|
394
|
-
* opt-ins deliberately require the env flag too because that flag controls
|
|
395
|
-
* whether the `server-agent-background` function exists in the deploy output.
|
|
399
|
+
* The single gate. On deployed Netlify, durable runs are enabled unless the env
|
|
400
|
+
* flag is explicitly falsy. Other hosted runtimes retain the explicit env/app
|
|
401
|
+
* opt-in path. In every case the runtime must be hosted and have A2A_SECRET.
|
|
402
|
+
* False means the current synchronous behavior is used unchanged.
|
|
396
403
|
*/
|
|
397
404
|
export function isAgentChatDurableBackgroundEnabled(options?: {
|
|
398
405
|
appOptIn?: boolean;
|
|
@@ -404,12 +411,15 @@ export function isAgentChatDurableBackgroundEnabled(options?: {
|
|
|
404
411
|
// prevent.
|
|
405
412
|
if (options?.appOptIn === false) return false;
|
|
406
413
|
const envOptIn = isDurableBackgroundFlagEnabled();
|
|
414
|
+
const netlifyDefaultOptIn =
|
|
415
|
+
isNetlifyHostedRuntimeForDurableBackground() &&
|
|
416
|
+
!isDurableBackgroundFlagExplicitlyDisabled();
|
|
407
417
|
const workspaceAppOptIn =
|
|
408
418
|
options?.appOptIn === true &&
|
|
409
419
|
!isDurableBackgroundFlagExplicitlyDisabled() &&
|
|
410
420
|
resolveWorkspaceBackgroundFunctionUrlPath() !== null;
|
|
411
421
|
return (
|
|
412
|
-
(envOptIn || workspaceAppOptIn) &&
|
|
422
|
+
(envOptIn || netlifyDefaultOptIn || workspaceAppOptIn) &&
|
|
413
423
|
isHostedRuntimeForDurableBackground() &&
|
|
414
424
|
hasConfiguredA2ASecret()
|
|
415
425
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { redactArgsToValue, redactTextToSummary } from "../../audit/redact.js";
|
|
1
2
|
import type {
|
|
2
3
|
BackgroundAgentControlInput,
|
|
3
4
|
BackgroundAgentControlResult,
|
|
@@ -233,13 +234,22 @@ function summarizeAgentChatEvent(event: AgentChatEvent): {
|
|
|
233
234
|
return {
|
|
234
235
|
kind: "status",
|
|
235
236
|
message: `Running ${event.tool}`,
|
|
236
|
-
metadata: {
|
|
237
|
+
metadata: {
|
|
238
|
+
type: "tool_start",
|
|
239
|
+
tool: event.tool,
|
|
240
|
+
input: redactArgsToValue(event.input),
|
|
241
|
+
},
|
|
237
242
|
};
|
|
238
243
|
case "tool_done":
|
|
239
244
|
return {
|
|
240
245
|
kind: "artifact",
|
|
241
246
|
message: event.result,
|
|
242
|
-
metadata: {
|
|
247
|
+
metadata: {
|
|
248
|
+
type: "tool_done",
|
|
249
|
+
tool: event.tool,
|
|
250
|
+
input: redactArgsToValue(event.input),
|
|
251
|
+
result: redactTextToSummary(event.result),
|
|
252
|
+
},
|
|
243
253
|
};
|
|
244
254
|
case "error":
|
|
245
255
|
return {
|