@agent-native/core 0.119.2 → 0.119.4
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 +19 -0
- package/corpus/core/package.json +3 -3
- package/corpus/core/src/agent/production-agent.ts +107 -125
- package/corpus/core/src/agent/run-manager.ts +45 -2
- package/corpus/core/src/agent/thread-data-builder.ts +85 -8
- package/corpus/core/src/cli/create.ts +44 -5
- package/corpus/core/src/client/AssistantChat.tsx +37 -9
- package/corpus/core/src/client/chat/message-components.tsx +9 -1
- package/corpus/core/src/client/chat/repo-helpers.ts +77 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +51 -15
- package/corpus/core/src/server/agent-chat/framework-prompts.ts +3 -1
- package/corpus/core/src/server/agent-chat/plugin-options.ts +9 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +12 -3
- package/corpus/core/src/templates/chat/.agents/skills/actions/SKILL.md +512 -0
- package/corpus/core/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +189 -0
- package/corpus/core/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +115 -0
- package/corpus/core/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
- package/corpus/core/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +89 -0
- package/corpus/core/src/templates/chat/.agents/skills/create-skill/SKILL.md +221 -0
- package/corpus/core/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +220 -0
- package/corpus/core/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +263 -0
- package/corpus/core/src/templates/chat/.agents/skills/feature-flags/SKILL.md +169 -0
- package/corpus/core/src/templates/chat/.agents/skills/frontend-design/SKILL.md +160 -0
- package/corpus/core/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +232 -0
- package/corpus/core/src/templates/chat/.agents/skills/security/SKILL.md +280 -0
- package/corpus/core/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +118 -0
- package/corpus/core/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +119 -0
- package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +100 -0
- package/corpus/core/src/templates/chat/.env.example +5 -0
- package/corpus/core/src/templates/chat/.ignore +0 -0
- package/corpus/core/src/templates/chat/.oxfmtrc.json +8 -0
- package/corpus/core/src/templates/chat/AGENTS.md +69 -0
- package/corpus/core/src/templates/chat/CHANGELOG.md +10 -0
- package/corpus/core/src/templates/chat/DEVELOPING.md +185 -0
- package/corpus/core/src/templates/chat/README.md +32 -0
- package/corpus/core/src/templates/chat/_gitignore +43 -0
- package/corpus/core/src/templates/chat/actions/hello.ts +13 -0
- package/corpus/core/src/templates/chat/actions/navigate.ts +41 -0
- package/corpus/core/src/templates/chat/actions/run.ts +2 -0
- package/corpus/core/src/templates/chat/actions/view-screen.ts +31 -0
- package/corpus/core/src/templates/chat/app/components/layout/Header.tsx +60 -0
- package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +185 -0
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +450 -0
- package/corpus/core/src/templates/chat/app/components/ui/button.tsx +1 -0
- package/corpus/core/src/templates/chat/app/components/ui/card.tsx +1 -0
- package/corpus/core/src/templates/chat/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/core/src/templates/chat/app/components/ui/input.tsx +1 -0
- package/corpus/core/src/templates/chat/app/components/ui/label.tsx +1 -0
- package/corpus/core/src/templates/chat/app/components/ui/sheet.tsx +1 -0
- package/corpus/core/src/templates/chat/app/components/ui/toolkit-provider.tsx +17 -0
- package/corpus/core/src/templates/chat/app/components/ui/tooltip.tsx +1 -0
- package/corpus/core/src/templates/chat/app/design-system.ts +3 -0
- package/corpus/core/src/templates/chat/app/entry.client.tsx +19 -0
- package/corpus/core/src/templates/chat/app/entry.server.tsx +10 -0
- package/corpus/core/src/templates/chat/app/global.css +93 -0
- package/corpus/core/src/templates/chat/app/hooks/use-navigation-state.ts +93 -0
- package/corpus/core/src/templates/chat/app/i18n/ar-SA.ts +68 -0
- package/corpus/core/src/templates/chat/app/i18n/de-DE.ts +71 -0
- package/corpus/core/src/templates/chat/app/i18n/en-US.ts +70 -0
- package/corpus/core/src/templates/chat/app/i18n/es-ES.ts +71 -0
- package/corpus/core/src/templates/chat/app/i18n/fr-FR.ts +71 -0
- package/corpus/core/src/templates/chat/app/i18n/hi-IN.ts +69 -0
- package/corpus/core/src/templates/chat/app/i18n/index.ts +34 -0
- package/corpus/core/src/templates/chat/app/i18n/ja-JP.ts +70 -0
- package/corpus/core/src/templates/chat/app/i18n/ko-KR.ts +70 -0
- package/corpus/core/src/templates/chat/app/i18n/pt-BR.ts +71 -0
- package/corpus/core/src/templates/chat/app/i18n/zh-CN.ts +66 -0
- package/corpus/core/src/templates/chat/app/i18n/zh-TW.ts +66 -0
- package/corpus/core/src/templates/chat/app/i18n-data.ts +556 -0
- package/corpus/core/src/templates/chat/app/lib/agent-page.tsx +49 -0
- package/corpus/core/src/templates/chat/app/lib/app-config.ts +11 -0
- package/corpus/core/src/templates/chat/app/lib/tab-id.ts +1 -0
- package/corpus/core/src/templates/chat/app/lib/utils.ts +1 -0
- package/corpus/core/src/templates/chat/app/root.tsx +173 -0
- package/corpus/core/src/templates/chat/app/routes/_index.tsx +94 -0
- package/corpus/core/src/templates/chat/app/routes/agent.tsx +24 -0
- package/corpus/core/src/templates/chat/app/routes/chat.$threadId.tsx +1 -0
- package/corpus/core/src/templates/chat/app/routes/database.tsx +17 -0
- package/corpus/core/src/templates/chat/app/routes/extensions.$id.$slug.tsx +2 -0
- package/corpus/core/src/templates/chat/app/routes/extensions.$id.tsx +11 -0
- package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +11 -0
- package/corpus/core/src/templates/chat/app/routes/extensions.tsx +5 -0
- package/corpus/core/src/templates/chat/app/routes/observability.tsx +19 -0
- package/corpus/core/src/templates/chat/app/routes/settings.tsx +89 -0
- package/corpus/core/src/templates/chat/app/routes/team.tsx +11 -0
- package/corpus/core/src/templates/chat/app/routes.ts +4 -0
- package/corpus/core/src/templates/chat/app/vite-env.d.ts +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
- package/corpus/core/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
- package/corpus/core/src/templates/chat/components.json +20 -0
- package/corpus/core/src/templates/chat/learnings.defaults.md +5 -0
- package/corpus/core/src/templates/chat/netlify.toml +10 -0
- package/corpus/core/src/templates/chat/package.json +92 -0
- package/corpus/core/src/templates/chat/public/agent-native-icon-dark.svg +10 -0
- package/corpus/core/src/templates/chat/public/agent-native-icon-light.svg +10 -0
- package/corpus/core/src/templates/chat/public/agent-native-logo-dark.svg +21 -0
- package/corpus/core/src/templates/chat/public/agent-native-logo-light.svg +21 -0
- package/corpus/core/src/templates/chat/public/favicon.svg +1 -0
- package/corpus/core/src/templates/chat/public/icon-180.svg +1 -0
- package/corpus/core/src/templates/chat/public/icon-192.svg +1 -0
- package/corpus/core/src/templates/chat/public/icon-512.svg +1 -0
- package/corpus/core/src/templates/chat/public/manifest.json +21 -0
- package/corpus/core/src/templates/chat/react-router.config.ts +7 -0
- package/corpus/core/src/templates/chat/server/middleware/auth.ts +15 -0
- package/corpus/core/src/templates/chat/server/plugins/agent-chat.ts +21 -0
- package/corpus/core/src/templates/chat/server/plugins/auth.ts +17 -0
- package/corpus/core/src/templates/chat/server/routes/[...page].get.ts +5 -0
- package/corpus/core/src/templates/chat/ssr-entry.ts +15 -0
- package/corpus/core/src/templates/chat/tsconfig.json +21 -0
- package/corpus/core/src/templates/chat/vite.config.ts +19 -0
- package/corpus/templates/analytics/AGENTS.md +19 -11
- package/corpus/templates/analytics/actions/list-data-dictionary.ts +3 -1
- package/corpus/templates/analytics/actions/save-data-dictionary-entry.ts +14 -0
- package/corpus/templates/analytics/actions/search-analytics-query-catalog.ts +41 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +13 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +12 -11
- package/corpus/templates/analytics/app/pages/adhoc/DashboardSkeleton.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +7 -1
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +8 -1
- package/corpus/templates/analytics/changelog/2026-07-22-fixed-dashboards-and-analyses-missing-a-copyable-share-link.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-chart-loading-placeholders-are-easier-to-see-in-da.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-routine-analytics-lookups-reuse-existing-query-definitions.md +6 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +1 -12
- package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +405 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -16
- package/corpus/templates/content/changelog/2026-07-23-database-toolbar-sort-and-filter-controls-open-reliably.md +6 -0
- package/corpus/templates/forms/app/lib/public-form-link.ts +14 -0
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +6 -14
- package/corpus/templates/forms/changelog/2026-07-23-published-form-links-can-be-copied-without-being-asked-to-re.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-23-scheduled-emails-now-send-only-once-when-multiple-requests-r.md +6 -0
- package/corpus/templates/mail/server/lib/jobs.ts +13 -4
- package/corpus/templates/mail/server/plugins/mail-jobs.ts +1 -1
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +7 -18
- package/corpus/templates/slides/app/lib/agent-visible-message.ts +28 -0
- package/corpus/templates/slides/app/pages/Index.tsx +6 -34
- package/corpus/templates/slides/changelog/2026-07-23-full-prompts-stay-visible-in-chat-when-creating-decks-or-add.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +13 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/editor/SharedRichEditor.tsx +23 -2
- package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +68 -2
- package/corpus/toolkit/src/editor/index.ts +1 -0
- package/corpus/toolkit/src/editor.css +75 -0
- package/corpus/toolkit/src/ui/button.tsx +3 -2
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +96 -115
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +26 -0
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +2 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +69 -9
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/cli/create.d.ts +2 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +43 -6
- package/dist/cli/create.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +32 -10
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +2 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +9 -3
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts +4 -0
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +61 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +2 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +10 -9
- package/dist/client/chat/tool-call-display.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/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +7 -7
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -1
- package/dist/server/agent-chat/framework-prompts.js +3 -1
- package/dist/server/agent-chat/framework-prompts.js.map +1 -1
- package/dist/server/agent-chat/plugin-options.d.ts +9 -0
- package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
- package/dist/server/agent-chat/plugin-options.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +11 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +4 -4
- package/src/agent/production-agent.ts +107 -125
- package/src/agent/run-manager.ts +45 -2
- package/src/agent/thread-data-builder.ts +85 -8
- package/src/cli/create.ts +44 -5
- package/src/client/AssistantChat.tsx +37 -9
- package/src/client/chat/message-components.tsx +9 -1
- package/src/client/chat/repo-helpers.ts +77 -0
- package/src/client/chat/tool-call-display.tsx +51 -15
- package/src/server/agent-chat/framework-prompts.ts +3 -1
- package/src/server/agent-chat/plugin-options.ts +9 -0
- package/src/server/agent-chat-plugin.ts +12 -3
- package/src/templates/chat/actions/hello.spec.ts +0 -23
- package/src/templates/chat/actions/navigate.spec.ts +0 -68
- package/src/templates/chat/actions/view-screen.spec.ts +0 -40
- package/src/templates/chat/app/lib/agent-page.spec.tsx +0 -61
|
@@ -27,11 +27,11 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
-
error?: undefined;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
key: string;
|
|
33
32
|
baseUrlKey?: string;
|
|
34
33
|
scope: AgentEngineApiKeyScope;
|
|
34
|
+
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
37
37
|
//# sourceMappingURL=agent-engine-api-key-route.d.ts.map
|
|
@@ -26,9 +26,9 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
-
error?: undefined;
|
|
30
29
|
token: string;
|
|
31
30
|
expiresAt: string;
|
|
32
31
|
ttlSeconds: number;
|
|
32
|
+
error?: undefined;
|
|
33
33
|
}>>;
|
|
34
34
|
//# sourceMappingURL=realtime-token.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.119.
|
|
3
|
+
"version": "0.119.4",
|
|
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": {
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
"highlight.js": "^11.11.1",
|
|
363
363
|
"i18next": "26.3.1",
|
|
364
364
|
"isbot": "^5",
|
|
365
|
-
"jiti": "^2.
|
|
365
|
+
"jiti": "^2.7.0",
|
|
366
366
|
"jose": "^6.2.2",
|
|
367
367
|
"linkedom": "0.18.12",
|
|
368
368
|
"lowlight": "^3.3.0",
|
|
@@ -370,7 +370,7 @@
|
|
|
370
370
|
"nanoid": "^5.1.9",
|
|
371
371
|
"next-themes": "^0.4.6",
|
|
372
372
|
"nf3": "0.3.17",
|
|
373
|
-
"nitro": "3.0.
|
|
373
|
+
"nitro": "3.0.260610-beta",
|
|
374
374
|
"p-limit": "^7.3.0",
|
|
375
375
|
"prettier": "^3.8.3",
|
|
376
376
|
"react-i18next": "17.0.8",
|
|
@@ -390,7 +390,7 @@
|
|
|
390
390
|
"yjs": "^13.6.31",
|
|
391
391
|
"zod": "^4.3.6",
|
|
392
392
|
"@agent-native/recap-cli": "0.5.1",
|
|
393
|
-
"@agent-native/toolkit": "^0.10.
|
|
393
|
+
"@agent-native/toolkit": "^0.10.4"
|
|
394
394
|
},
|
|
395
395
|
"devDependencies": {
|
|
396
396
|
"@ai-sdk/anthropic": "^3.0.71",
|
|
@@ -7746,18 +7746,6 @@ export function createProductionAgentHandler(
|
|
|
7746
7746
|
}).catch(() => {});
|
|
7747
7747
|
}
|
|
7748
7748
|
|
|
7749
|
-
// The background worker must AWAIT the run to completion before returning,
|
|
7750
|
-
// or Netlify freezes/kills the function the instant the handler returns and
|
|
7751
|
-
// the detached run dies mid-turn (mirrors the agent-teams processor, which
|
|
7752
|
-
// wraps startRun in `await new Promise(resolve => startRun(..., onComplete:
|
|
7753
|
-
// () => resolve()))`). We resolve this when the run's onComplete fires.
|
|
7754
|
-
let resolveBackgroundRunDone: (() => void) | null = null;
|
|
7755
|
-
const backgroundRunDone = isBackgroundWorker
|
|
7756
|
-
? new Promise<void>((resolve) => {
|
|
7757
|
-
resolveBackgroundRunDone = resolve;
|
|
7758
|
-
})
|
|
7759
|
-
: null;
|
|
7760
|
-
|
|
7761
7749
|
const baseHandleRunComplete =
|
|
7762
7750
|
options.onRunComplete || trackedProgressRunId
|
|
7763
7751
|
? async (run: ActiveRun) => {
|
|
@@ -7771,119 +7759,114 @@ export function createProductionAgentHandler(
|
|
|
7771
7759
|
}
|
|
7772
7760
|
: undefined;
|
|
7773
7761
|
|
|
7774
|
-
//
|
|
7775
|
-
// onRunComplete / tracked-progress callback configured.
|
|
7776
|
-
//
|
|
7777
|
-
// wrapper to fire the continuation dispatch below.
|
|
7762
|
+
// Install the completion callback for background continuation even when
|
|
7763
|
+
// there is no app onRunComplete / tracked-progress callback configured.
|
|
7764
|
+
// The foreground self-chain path uses the same continuation dispatch.
|
|
7778
7765
|
const handleRunComplete =
|
|
7779
7766
|
isBackgroundWorker || foregroundSelfChainEligible || baseHandleRunComplete
|
|
7780
7767
|
? async (run: ActiveRun) => {
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
errEvent
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
});
|
|
7884
|
-
}
|
|
7885
|
-
} finally {
|
|
7886
|
-
resolveBackgroundRunDone?.();
|
|
7768
|
+
// DIAGNOSTIC: a background worker that completed in an errored
|
|
7769
|
+
// state threw inside the loop. Record it (with the last error
|
|
7770
|
+
// event's message when available) so the failure cause is
|
|
7771
|
+
// readable from the client. Skipped for clean completions and for
|
|
7772
|
+
// recoverable soft-timeout boundaries (those chain a continuation
|
|
7773
|
+
// below, they did not "throw").
|
|
7774
|
+
if (
|
|
7775
|
+
isBackgroundWorker &&
|
|
7776
|
+
run.status === "errored" &&
|
|
7777
|
+
!willChainBackgroundContinuation(run)
|
|
7778
|
+
) {
|
|
7779
|
+
const errEvent = [...run.events]
|
|
7780
|
+
.reverse()
|
|
7781
|
+
.find((e) => e.event.type === "error")?.event as
|
|
7782
|
+
| { error?: string; errorCode?: string }
|
|
7783
|
+
| undefined;
|
|
7784
|
+
await recordRunDiagnostic(
|
|
7785
|
+
run.runId,
|
|
7786
|
+
RUN_DIAG_STAGE.workerThrew,
|
|
7787
|
+
errEvent?.errorCode || errEvent?.error
|
|
7788
|
+
? `${errEvent.errorCode ?? ""} ${errEvent.error ?? ""}`.trim()
|
|
7789
|
+
: "run ended in errored state",
|
|
7790
|
+
).catch(() => {});
|
|
7791
|
+
}
|
|
7792
|
+
// Persist the (partial) assistant turn to thread_data FIRST — the
|
|
7793
|
+
// server-driven continuation below rebuilds from it, so it must be
|
|
7794
|
+
// committed before we re-fire.
|
|
7795
|
+
await baseHandleRunComplete?.(run);
|
|
7796
|
+
|
|
7797
|
+
// Server-driven background→background continuation. If this chunk
|
|
7798
|
+
// hit its soft-timeout still unfinished (ended at an auto_continue
|
|
7799
|
+
// / loop_limit / recoverable boundary), chain the next chunk by
|
|
7800
|
+
// re-firing the `_process-run` self-dispatch with mode "continue"
|
|
7801
|
+
// (carried as internalContinuation + an incremented count),
|
|
7802
|
+
// instead of relying on the client to re-POST. Mirrors the
|
|
7803
|
+
// agent-teams `fireInternalDispatch({ body: { mode: "continue" }})`
|
|
7804
|
+
// chain. Bounded by MAX_BACKGROUND_RUN_CONTINUATIONS. Aborted /
|
|
7805
|
+
// user-stopped runs do NOT chain.
|
|
7806
|
+
// Self-chain server-side for EVERY durable worker, not only the
|
|
7807
|
+
// ones inside a `-background` function. Server-driven
|
|
7808
|
+
// continuation is the whole point of durable background: the run
|
|
7809
|
+
// must survive the client disconnecting (closed tab), so it
|
|
7810
|
+
// cannot depend on the browser re-POSTing `auto_continue`. A
|
|
7811
|
+
// worker on the regular ~60s function — a Netlify routing miss,
|
|
7812
|
+
// or a non-Netlify host (Vercel/Cloudflare/Render/Fly) that
|
|
7813
|
+
// never emits a `-background` function — checkpoints at the 40s
|
|
7814
|
+
// soft-timeout and self-dispatches the next 40s chunk; a worker
|
|
7815
|
+
// in a real `-background` function chains ~13-min chunks. Only
|
|
7816
|
+
// the per-chunk BUDGET differs by function type (gated by
|
|
7817
|
+
// `runsInBackgroundFunction` at the startRun call below); the
|
|
7818
|
+
// continuation itself must stay server-driven on both. (The
|
|
7819
|
+
// self-chain is only reachable when the initial dispatch already
|
|
7820
|
+
// succeeded — a dispatch fast-fail degrades to the inline
|
|
7821
|
+
// foreground fallback, which is not a worker and rides the
|
|
7822
|
+
// connected client's auto_continue instead.)
|
|
7823
|
+
if (willChainBackgroundContinuation(run)) {
|
|
7824
|
+
// Full handoff discipline lives in
|
|
7825
|
+
// `chainServerDrivenContinuation` (exported + unit-tested):
|
|
7826
|
+
// per-turn SQL run budget, successor row PRE-INSERTED before
|
|
7827
|
+
// the dispatch, fully awaited dispatch with retries, loud
|
|
7828
|
+
// diag/terminal marking on failure. Never throws.
|
|
7829
|
+
await chainServerDrivenContinuation({
|
|
7830
|
+
event,
|
|
7831
|
+
run,
|
|
7832
|
+
effectiveThreadId,
|
|
7833
|
+
effectiveTurnId,
|
|
7834
|
+
requestBody: body as unknown as Record<string, unknown>,
|
|
7835
|
+
backgroundContinuationCount,
|
|
7836
|
+
// Re-evaluate the durable gate rather than keying off
|
|
7837
|
+
// isBackgroundWorker: a successor chunk of a FOREGROUND
|
|
7838
|
+
// self-chain re-enters as a worker too, and must keep
|
|
7839
|
+
// chaining via the regular function — the Netlify
|
|
7840
|
+
// `-background` function is only emitted into the deploy
|
|
7841
|
+
// output when the durable flag is on.
|
|
7842
|
+
//
|
|
7843
|
+
// `&& !runsInBackgroundFunction`: a worker PROVEN to already
|
|
7844
|
+
// be executing inside the real `-background` function must
|
|
7845
|
+
// never dispatch back to that same function's own URL — a
|
|
7846
|
+
// background function invoking itself by URL from inside a
|
|
7847
|
+
// live invocation is a documented Netlify platform
|
|
7848
|
+
// limitation (404), unlike the initial foreground→background
|
|
7849
|
+
// dispatch or a worker that landed on the regular function
|
|
7850
|
+
// (a genuinely different function calling the background
|
|
7851
|
+
// one, which works). This only changes the dispatch TARGET
|
|
7852
|
+
// for that one proven-in-bg-function case — by this point
|
|
7853
|
+
// (with the in-process resumable-error resume above) it is
|
|
7854
|
+
// reached only when the chunk genuinely exhausted its
|
|
7855
|
+
// budget, so falling back to the regular `_process-run`
|
|
7856
|
+
// function (40s clamp) here is the correct, safe target.
|
|
7857
|
+
chainViaDurableBackground:
|
|
7858
|
+
isAgentChatDurableBackgroundEnabled({
|
|
7859
|
+
appOptIn: options.durableBackgroundRuns,
|
|
7860
|
+
}) && !runsInBackgroundFunction,
|
|
7861
|
+
// Only changes the retry BUDGET, never the dispatch target
|
|
7862
|
+
// above: a worker proven in a real background function has
|
|
7863
|
+
// minutes of remaining wall clock and no connected-client
|
|
7864
|
+
// fallback, so it must not be demoted to the foreground's
|
|
7865
|
+
// 2-attempt budget just because it was forced onto the
|
|
7866
|
+
// regular-function dispatch target. See
|
|
7867
|
+
// `resolveContinuationDispatchBudget`.
|
|
7868
|
+
workerProvenInBackgroundFunction: runsInBackgroundFunction,
|
|
7869
|
+
});
|
|
7887
7870
|
}
|
|
7888
7871
|
}
|
|
7889
7872
|
: undefined;
|
|
@@ -7970,7 +7953,7 @@ export function createProductionAgentHandler(
|
|
|
7970
7953
|
)
|
|
7971
7954
|
: null;
|
|
7972
7955
|
|
|
7973
|
-
startRun(
|
|
7956
|
+
const startedRun = startRun(
|
|
7974
7957
|
runId,
|
|
7975
7958
|
effectiveThreadId,
|
|
7976
7959
|
async (rawSend, signal) => {
|
|
@@ -8414,9 +8397,8 @@ export function createProductionAgentHandler(
|
|
|
8414
8397
|
// Background worker: await the run to completion so Netlify keeps the
|
|
8415
8398
|
// background function alive for the whole turn (the client is streaming the
|
|
8416
8399
|
// same events via the foreground POST's cross-isolate SQL subscription).
|
|
8417
|
-
// The onComplete wrapper above resolves `backgroundRunDone`.
|
|
8418
8400
|
if (isBackgroundWorker) {
|
|
8419
|
-
|
|
8401
|
+
await startedRun.finalized;
|
|
8420
8402
|
return { ok: true, runId };
|
|
8421
8403
|
}
|
|
8422
8404
|
|
package/src/agent/run-manager.ts
CHANGED
|
@@ -44,6 +44,15 @@ export interface ActiveRun {
|
|
|
44
44
|
startedAt: number;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export interface StartedRun extends ActiveRun {
|
|
48
|
+
/**
|
|
49
|
+
* Resolves after the terminal event and final SQL status have been persisted.
|
|
50
|
+
* Serverless workers must await this before returning or the runtime can
|
|
51
|
+
* freeze the isolate between onComplete and terminalization.
|
|
52
|
+
*/
|
|
53
|
+
finalized: Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
const activeRuns = new Map<string, ActiveRun>();
|
|
48
57
|
const threadToRun = new Map<string, string>();
|
|
49
58
|
|
|
@@ -447,7 +456,7 @@ export function startRun(
|
|
|
447
456
|
) => Promise<void>,
|
|
448
457
|
onComplete?: (run: ActiveRun) => void | Promise<void>,
|
|
449
458
|
options?: StartRunOptions,
|
|
450
|
-
):
|
|
459
|
+
): StartedRun {
|
|
451
460
|
// If there's already a run for this thread, abort it
|
|
452
461
|
const existingRunId = threadToRun.get(threadId);
|
|
453
462
|
if (existingRunId) {
|
|
@@ -456,7 +465,17 @@ export function startRun(
|
|
|
456
465
|
|
|
457
466
|
const abort = new AbortController();
|
|
458
467
|
let softTimedOut = false;
|
|
459
|
-
|
|
468
|
+
let resolveFinalized: () => void = () => {};
|
|
469
|
+
let rejectFinalized: (reason?: unknown) => void = () => {};
|
|
470
|
+
const finalized = new Promise<void>((resolve, reject) => {
|
|
471
|
+
resolveFinalized = resolve;
|
|
472
|
+
rejectFinalized = reject;
|
|
473
|
+
});
|
|
474
|
+
// Foreground callers do not await this promise, but terminal persistence
|
|
475
|
+
// failures must still be observable to background workers without creating
|
|
476
|
+
// an unhandled rejection in the foreground path.
|
|
477
|
+
void finalized.catch(() => {});
|
|
478
|
+
const run: StartedRun = {
|
|
460
479
|
runId,
|
|
461
480
|
threadId,
|
|
462
481
|
turnId: options?.turnId ?? runId,
|
|
@@ -465,6 +484,7 @@ export function startRun(
|
|
|
465
484
|
subscribers: new Set(),
|
|
466
485
|
abort,
|
|
467
486
|
startedAt: Date.now(),
|
|
487
|
+
finalized,
|
|
468
488
|
};
|
|
469
489
|
|
|
470
490
|
activeRuns.set(runId, run);
|
|
@@ -1039,6 +1059,21 @@ export function startRun(
|
|
|
1039
1059
|
"[run-manager] terminal event persistence error:",
|
|
1040
1060
|
err instanceof Error ? err.message : err,
|
|
1041
1061
|
);
|
|
1062
|
+
try {
|
|
1063
|
+
await insertRunEvent(
|
|
1064
|
+
runId,
|
|
1065
|
+
terminal.seq,
|
|
1066
|
+
JSON.stringify(terminal.event),
|
|
1067
|
+
);
|
|
1068
|
+
terminalPersistenceError = null;
|
|
1069
|
+
} catch (retryError) {
|
|
1070
|
+
terminalPersistenceError = retryError;
|
|
1071
|
+
captureRunError(retryError, "completion");
|
|
1072
|
+
console.error(
|
|
1073
|
+
"[run-manager] terminal event retry persistence error:",
|
|
1074
|
+
retryError instanceof Error ? retryError.message : retryError,
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1042
1077
|
}
|
|
1043
1078
|
}
|
|
1044
1079
|
}
|
|
@@ -1112,6 +1147,13 @@ export function startRun(
|
|
|
1112
1147
|
await setRunError(runId, errorCode ?? "unknown", errorDetail);
|
|
1113
1148
|
}
|
|
1114
1149
|
|
|
1150
|
+
if (terminalPersistenceError) {
|
|
1151
|
+
const reconciled = await reconcileTerminalRunFromEvents(runId).catch(
|
|
1152
|
+
() => false,
|
|
1153
|
+
);
|
|
1154
|
+
if (!reconciled) throw terminalPersistenceError;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1115
1157
|
// 6. Schedule in-memory cleanup + opportunistic old-run pruning.
|
|
1116
1158
|
setTimeout(() => {
|
|
1117
1159
|
activeRuns.delete(runId);
|
|
@@ -1124,6 +1166,7 @@ export function startRun(
|
|
|
1124
1166
|
resolveErroredRunRetentionMs(),
|
|
1125
1167
|
).catch(() => {});
|
|
1126
1168
|
});
|
|
1169
|
+
runPromise.then(resolveFinalized, rejectFinalized);
|
|
1127
1170
|
|
|
1128
1171
|
// On Cloudflare Workers, keep the isolate alive for this run
|
|
1129
1172
|
try {
|
|
@@ -35,6 +35,7 @@ interface BuildAssistantMessageOptions {
|
|
|
35
35
|
* overwriting the others.
|
|
36
36
|
*/
|
|
37
37
|
turnId?: string;
|
|
38
|
+
runDurationMs?: number;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
type AssistantMessage = NonNullable<ReturnType<typeof buildAssistantMessage>>;
|
|
@@ -43,6 +44,8 @@ type UserMessage = ReturnType<typeof buildUserMessage>;
|
|
|
43
44
|
const INTERRUPTED_TOOL_RESULT =
|
|
44
45
|
"Interrupted before this tool returned a result.";
|
|
45
46
|
|
|
47
|
+
export const ASSISTANT_RUN_DURATION_METADATA_KEY = "agentNativeRunDurationMs";
|
|
48
|
+
|
|
46
49
|
const MAX_STORED_ATTACHMENT_CHARS = 60_000;
|
|
47
50
|
/**
|
|
48
51
|
* When no file-upload provider is configured we fall back to storing base64
|
|
@@ -267,6 +270,13 @@ export function buildAssistantMessage(
|
|
|
267
270
|
const custom: Record<string, unknown> = {};
|
|
268
271
|
if (options.turnId) custom.turnId = options.turnId;
|
|
269
272
|
if (runId) custom.foldedRunIds = [runId];
|
|
273
|
+
if (
|
|
274
|
+
typeof options.runDurationMs === "number" &&
|
|
275
|
+
Number.isFinite(options.runDurationMs) &&
|
|
276
|
+
options.runDurationMs >= 0
|
|
277
|
+
) {
|
|
278
|
+
custom[ASSISTANT_RUN_DURATION_METADATA_KEY] = options.runDurationMs;
|
|
279
|
+
}
|
|
270
280
|
if (continued) custom.continued = true;
|
|
271
281
|
if (runError) {
|
|
272
282
|
custom.runError = {
|
|
@@ -462,6 +472,41 @@ function messagesMatch(a: any, b: any): boolean {
|
|
|
462
472
|
return messageIdentityKeys(a).some((key) => bKeys.has(key));
|
|
463
473
|
}
|
|
464
474
|
|
|
475
|
+
function preserveAssistantRunDuration(chosenEntry: any, otherEntry: any): any {
|
|
476
|
+
const chosen = getStoredMessage(chosenEntry);
|
|
477
|
+
const other = getStoredMessage(otherEntry);
|
|
478
|
+
if (chosen?.role !== "assistant" || other?.role !== "assistant") {
|
|
479
|
+
return chosenEntry;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const chosenCustom =
|
|
483
|
+
chosen.metadata?.custom && typeof chosen.metadata.custom === "object"
|
|
484
|
+
? (chosen.metadata.custom as Record<string, unknown>)
|
|
485
|
+
: {};
|
|
486
|
+
if (assistantRunDurationMs(chosenCustom) != null) return chosenEntry;
|
|
487
|
+
|
|
488
|
+
const otherCustom =
|
|
489
|
+
other.metadata?.custom && typeof other.metadata.custom === "object"
|
|
490
|
+
? (other.metadata.custom as Record<string, unknown>)
|
|
491
|
+
: {};
|
|
492
|
+
const durationMs = assistantRunDurationMs(otherCustom);
|
|
493
|
+
if (durationMs == null) return chosenEntry;
|
|
494
|
+
|
|
495
|
+
const nextMessage = {
|
|
496
|
+
...chosen,
|
|
497
|
+
metadata: {
|
|
498
|
+
...chosen.metadata,
|
|
499
|
+
custom: {
|
|
500
|
+
...chosenCustom,
|
|
501
|
+
[ASSISTANT_RUN_DURATION_METADATA_KEY]: durationMs,
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
return chosenEntry?.message === undefined
|
|
506
|
+
? nextMessage
|
|
507
|
+
: { ...chosenEntry, message: nextMessage };
|
|
508
|
+
}
|
|
509
|
+
|
|
465
510
|
function chooseMergedMessageEntry(existingEntry: any, incomingEntry: any): any {
|
|
466
511
|
const existing = getStoredMessage(existingEntry);
|
|
467
512
|
const incoming = getStoredMessage(incomingEntry);
|
|
@@ -479,12 +524,19 @@ function chooseMergedMessageEntry(existingEntry: any, incomingEntry: any): any {
|
|
|
479
524
|
) {
|
|
480
525
|
const existingWeight = assistantContentWeight(existing.content);
|
|
481
526
|
const incomingWeight = assistantContentWeight(incoming.content);
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
527
|
+
const chosen =
|
|
528
|
+
existingWeight > incomingWeight
|
|
529
|
+
? existingEntry
|
|
530
|
+
: incomingWeight > existingWeight
|
|
531
|
+
? incomingEntry
|
|
532
|
+
: isTerminalAssistantStatus(existing?.status) &&
|
|
533
|
+
!isTerminalAssistantStatus(incoming?.status)
|
|
534
|
+
? existingEntry
|
|
535
|
+
: incomingEntry;
|
|
536
|
+
return preserveAssistantRunDuration(
|
|
537
|
+
chosen,
|
|
538
|
+
chosen === existingEntry ? incomingEntry : existingEntry,
|
|
539
|
+
);
|
|
488
540
|
}
|
|
489
541
|
if (
|
|
490
542
|
existing?.role === "assistant" &&
|
|
@@ -492,9 +544,9 @@ function chooseMergedMessageEntry(existingEntry: any, incomingEntry: any): any {
|
|
|
492
544
|
isTerminalAssistantStatus(existing?.status) &&
|
|
493
545
|
!isTerminalAssistantStatus(incoming?.status)
|
|
494
546
|
) {
|
|
495
|
-
return existingEntry;
|
|
547
|
+
return preserveAssistantRunDuration(existingEntry, incomingEntry);
|
|
496
548
|
}
|
|
497
|
-
return incomingEntry;
|
|
549
|
+
return preserveAssistantRunDuration(incomingEntry, existingEntry);
|
|
498
550
|
}
|
|
499
551
|
|
|
500
552
|
function normalizeMessageEntry(
|
|
@@ -1397,6 +1449,17 @@ function foldedRunIdsOf(message: any): string[] {
|
|
|
1397
1449
|
: [];
|
|
1398
1450
|
}
|
|
1399
1451
|
|
|
1452
|
+
function assistantRunDurationMs(
|
|
1453
|
+
custom: Record<string, unknown>,
|
|
1454
|
+
): number | null {
|
|
1455
|
+
const durationMs = custom[ASSISTANT_RUN_DURATION_METADATA_KEY];
|
|
1456
|
+
return typeof durationMs === "number" &&
|
|
1457
|
+
Number.isFinite(durationMs) &&
|
|
1458
|
+
durationMs >= 0
|
|
1459
|
+
? durationMs
|
|
1460
|
+
: null;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1400
1463
|
/** Rough size of an assistant message's content, used only to pick the larger
|
|
1401
1464
|
* of two representations of the same chunk so a fold can never shrink. */
|
|
1402
1465
|
function assistantContentWeight(content: unknown): number {
|
|
@@ -1519,6 +1582,20 @@ export function foldAssistantTurn(
|
|
|
1519
1582
|
turnId,
|
|
1520
1583
|
foldedRunIds: mergedFolded,
|
|
1521
1584
|
};
|
|
1585
|
+
const existingDurationMs = assistantRunDurationMs(existingCustom);
|
|
1586
|
+
const incomingDurationMs = assistantRunDurationMs(incomingCustom);
|
|
1587
|
+
const mergedDurationMs = runAlreadyFolded
|
|
1588
|
+
? existingDurationMs == null
|
|
1589
|
+
? incomingDurationMs
|
|
1590
|
+
: incomingDurationMs == null
|
|
1591
|
+
? existingDurationMs
|
|
1592
|
+
: Math.max(existingDurationMs, incomingDurationMs)
|
|
1593
|
+
: existingDurationMs == null && incomingDurationMs == null
|
|
1594
|
+
? null
|
|
1595
|
+
: (existingDurationMs ?? 0) + (incomingDurationMs ?? 0);
|
|
1596
|
+
if (mergedDurationMs != null) {
|
|
1597
|
+
mergedCustom[ASSISTANT_RUN_DURATION_METADATA_KEY] = mergedDurationMs;
|
|
1598
|
+
}
|
|
1522
1599
|
// Only the freshest chunk decides whether the turn is still continuing.
|
|
1523
1600
|
if (incomingCustom.continued !== true) delete mergedCustom.continued;
|
|
1524
1601
|
|