@agent-native/core 0.132.1 → 0.133.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 +198 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/correlation.ts +11 -1
- package/corpus/core/src/a2a/types.ts +11 -3
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +33 -46
- package/corpus/core/src/agent/engine/builder-engine.ts +22 -10
- package/corpus/core/src/agent/engine/error-detail.ts +184 -3
- package/corpus/core/src/agent/engine/failure-taxonomy.ts +123 -0
- package/corpus/core/src/agent/engine/index.ts +8 -0
- package/corpus/core/src/agent/engine/registry.ts +92 -8
- package/corpus/core/src/agent/engine/translate-ai-sdk.ts +17 -2
- package/corpus/core/src/agent/production-agent.ts +13 -33
- package/corpus/core/src/agent/run-loop-with-resume.ts +1 -1
- package/corpus/core/src/agent/run-manager.ts +43 -14
- package/corpus/core/src/agent/run-store.ts +30 -3
- package/corpus/core/src/agent/thread-data-builder.ts +36 -4
- package/corpus/core/src/chat-threads/store.ts +42 -0
- package/corpus/core/src/cli/templates-meta.ts +13 -0
- package/corpus/core/src/cli/workspace-dev.ts +1 -0
- package/corpus/core/src/client/error-format.ts +12 -0
- package/corpus/core/src/client/use-chat-threads.ts +76 -16
- package/corpus/core/src/credentials/index.ts +51 -12
- package/corpus/core/src/extensions/url-safety.ts +46 -12
- package/corpus/core/src/integrations/adapters/slack.ts +32 -10
- package/corpus/core/src/integrations/google-docs-poller.ts +12 -1
- package/corpus/core/src/integrations/installations-store.ts +30 -0
- package/corpus/core/src/integrations/plugin.ts +5 -5
- package/corpus/core/src/integrations/types.ts +7 -0
- package/corpus/core/src/integrations/webhook-handler.ts +24 -1
- package/corpus/core/src/jobs/background-automation-runner.ts +38 -1
- package/corpus/core/src/provider-api/index.ts +8 -2
- package/corpus/core/src/scripts/call-agent.ts +6 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +46 -10
- package/corpus/core/src/server/agent-discovery.ts +2 -0
- package/corpus/core/src/server/builder-design-systems.ts +21 -0
- package/corpus/core/src/server/credential-provider.ts +51 -12
- package/corpus/core/src/server/index.ts +1 -0
- package/corpus/core/src/server/onboarding-html.ts +37 -2
- package/corpus/core/src/shared/reasoning-effort.ts +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +6 -2
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +39 -19
- package/corpus/templates/assets/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +30 -3
- package/corpus/templates/brain/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +54 -25
- package/corpus/templates/calendar/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +30 -3
- package/corpus/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +37 -21
- package/corpus/templates/clips/changelog/2026-07-30-meeting-microphone-transcription-works-reliably-from-the-fir.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +1 -1
- package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +13 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +25 -132
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +34 -17
- package/corpus/templates/content/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -2
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +5 -0
- package/corpus/templates/design/actions/create-design-system.ts +1 -0
- package/corpus/templates/design/actions/set-default-design-system.ts +34 -26
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +5 -3
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +23 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +3 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +21 -4
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -7
- package/corpus/templates/design/app/i18n-data.ts +39 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +229 -12
- package/corpus/templates/design/app/pages/DesignSystems.tsx +10 -4
- package/corpus/templates/design/changelog/2026-07-30-fixed-the-keyboard-shortcuts-panel-not-opening-with-ctrl-shi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/corpus/templates/design/changelog/2026-07-31-design-system-indexing-keeps-your-chosen-name-when-you-uploa.md +6 -0
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +19 -4
- package/corpus/templates/dispatch/AGENTS.md +4 -0
- package/corpus/templates/factory/.agents/skills/actions/SKILL.md +562 -0
- package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +198 -0
- package/corpus/templates/factory/.agents/skills/agent-native-docs/SKILL.md +115 -0
- package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +182 -0
- package/corpus/templates/factory/.agents/skills/capture-learnings/SKILL.md +89 -0
- package/corpus/templates/factory/.agents/skills/create-skill/SKILL.md +221 -0
- package/corpus/templates/factory/.agents/skills/customizing-agent-native/SKILL.md +220 -0
- package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +263 -0
- package/corpus/templates/factory/.agents/skills/factory-graphs/SKILL.md +52 -0
- package/corpus/templates/factory/.agents/skills/feature-flags/SKILL.md +169 -0
- package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +174 -0
- package/corpus/templates/factory/.agents/skills/real-time-sync/SKILL.md +232 -0
- package/corpus/templates/factory/.agents/skills/security/SKILL.md +324 -0
- package/corpus/templates/factory/.agents/skills/self-modifying-code/SKILL.md +119 -0
- package/corpus/templates/factory/.agents/skills/shadcn-ui/SKILL.md +123 -0
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +232 -0
- package/corpus/templates/factory/.agents/skills/storing-data/SKILL.md +178 -0
- package/corpus/templates/factory/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
- package/corpus/templates/factory/.env.example +24 -0
- package/corpus/templates/factory/.ignore +0 -0
- package/corpus/templates/factory/.oxfmtrc.json +8 -0
- package/corpus/templates/factory/AGENTS.md +90 -0
- package/corpus/templates/factory/CHANGELOG.md +10 -0
- package/corpus/templates/factory/DEVELOPING.md +185 -0
- package/corpus/templates/factory/README.md +64 -0
- package/corpus/templates/factory/_gitignore +42 -0
- package/corpus/templates/factory/actions/add-factory-comment.ts +55 -0
- package/corpus/templates/factory/actions/approve-factory-item.ts +214 -0
- package/corpus/templates/factory/actions/babysit-pull-request.ts +126 -0
- package/corpus/templates/factory/actions/evaluate-triage-item.ts +172 -0
- package/corpus/templates/factory/actions/get-factory-graph.ts +62 -0
- package/corpus/templates/factory/actions/get-triage-config.ts +42 -0
- package/corpus/templates/factory/actions/get-triage-item.ts +74 -0
- package/corpus/templates/factory/actions/hello.ts +13 -0
- package/corpus/templates/factory/actions/ingest-github-observation.ts +100 -0
- package/corpus/templates/factory/actions/list-factories.ts +40 -0
- package/corpus/templates/factory/actions/list-factory-comments.ts +40 -0
- package/corpus/templates/factory/actions/list-triage-items.ts +85 -0
- package/corpus/templates/factory/actions/list-triage-rules.ts +44 -0
- package/corpus/templates/factory/actions/navigate.ts +41 -0
- package/corpus/templates/factory/actions/poll-slack-channel.ts +118 -0
- package/corpus/templates/factory/actions/reconcile-triage-run.ts +179 -0
- package/corpus/templates/factory/actions/record-triage-feedback.ts +58 -0
- package/corpus/templates/factory/actions/run.ts +2 -0
- package/corpus/templates/factory/actions/save-factory-graph.ts +129 -0
- package/corpus/templates/factory/actions/save-triage-config.ts +67 -0
- package/corpus/templates/factory/actions/save-triage-rule.ts +89 -0
- package/corpus/templates/factory/actions/suggest-factory-rules.ts +70 -0
- package/corpus/templates/factory/actions/view-screen.ts +73 -0
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +441 -0
- package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +388 -0
- package/corpus/templates/factory/app/components/layout/Header.tsx +60 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +190 -0
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +538 -0
- package/corpus/templates/factory/app/components/triage/triage-status-pill.tsx +28 -0
- package/corpus/templates/factory/app/components/ui/button.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/card.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/input.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/label.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/toolkit-provider.tsx +17 -0
- package/corpus/templates/factory/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/factory/app/design-system.ts +3 -0
- package/corpus/templates/factory/app/entry.client.tsx +22 -0
- package/corpus/templates/factory/app/entry.server.tsx +10 -0
- package/corpus/templates/factory/app/global.css +93 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +113 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +211 -0
- package/corpus/templates/factory/app/i18n/index.ts +34 -0
- package/corpus/templates/factory/app/i18n-data.ts +556 -0
- package/corpus/templates/factory/app/lib/agent-page.tsx +49 -0
- package/corpus/templates/factory/app/lib/app-config.ts +11 -0
- package/corpus/templates/factory/app/lib/tab-id.ts +1 -0
- package/corpus/templates/factory/app/lib/utils.ts +1 -0
- package/corpus/templates/factory/app/root.tsx +175 -0
- package/corpus/templates/factory/app/routes/_index.tsx +94 -0
- package/corpus/templates/factory/app/routes/agent.tsx +24 -0
- package/corpus/templates/factory/app/routes/chat.$threadId.tsx +1 -0
- package/corpus/templates/factory/app/routes/database.tsx +17 -0
- package/corpus/templates/factory/app/routes/extensions.$id.$slug.tsx +2 -0
- package/corpus/templates/factory/app/routes/extensions.$id.tsx +11 -0
- package/corpus/templates/factory/app/routes/extensions._index.tsx +11 -0
- package/corpus/templates/factory/app/routes/extensions.tsx +5 -0
- package/corpus/templates/factory/app/routes/factory.tsx +1003 -0
- package/corpus/templates/factory/app/routes/observability.tsx +19 -0
- package/corpus/templates/factory/app/routes/settings.tsx +89 -0
- package/corpus/templates/factory/app/routes/team.tsx +11 -0
- package/corpus/templates/factory/app/routes.ts +4 -0
- package/corpus/templates/factory/app/vite-env.d.ts +6 -0
- package/corpus/templates/factory/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/factory/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/factory/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
- package/corpus/templates/factory/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/factory/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/factory/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-31-added-an-inspectable-observe-only-factory-queue-for-slack-.md +6 -0
- package/corpus/templates/factory/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/corpus/templates/factory/components.json +20 -0
- package/corpus/templates/factory/data/sync-config.json +1 -0
- package/corpus/templates/factory/learnings.defaults.md +5 -0
- package/corpus/templates/factory/netlify.toml +10 -0
- package/corpus/templates/factory/package.json +105 -0
- package/corpus/templates/factory/public/agent-native-icon-dark.svg +10 -0
- package/corpus/templates/factory/public/agent-native-icon-light.svg +10 -0
- package/corpus/templates/factory/public/agent-native-logo-dark.svg +21 -0
- package/corpus/templates/factory/public/agent-native-logo-light.svg +21 -0
- package/corpus/templates/factory/public/favicon.svg +1 -0
- package/corpus/templates/factory/public/icon-180.svg +1 -0
- package/corpus/templates/factory/public/icon-192.svg +1 -0
- package/corpus/templates/factory/public/icon-512.svg +1 -0
- package/corpus/templates/factory/public/manifest.json +21 -0
- package/corpus/templates/factory/react-router.config.ts +7 -0
- package/corpus/templates/factory/server/connectors/credentials.ts +129 -0
- package/corpus/templates/factory/server/connectors/slack.ts +129 -0
- package/corpus/templates/factory/server/db/index.ts +6 -0
- package/corpus/templates/factory/server/db/schema.ts +193 -0
- package/corpus/templates/factory/server/factory-graph/contracts.ts +212 -0
- package/corpus/templates/factory/server/factory-graph/store.ts +121 -0
- package/corpus/templates/factory/server/lib/require-workspace-member.ts +55 -0
- package/corpus/templates/factory/server/middleware/auth.ts +15 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +58 -0
- package/corpus/templates/factory/server/plugins/auth.ts +14 -0
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +247 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +116 -0
- package/corpus/templates/factory/server/plugins/integrations.ts +46 -0
- package/corpus/templates/factory/server/routes/[...page].get.ts +5 -0
- package/corpus/templates/factory/server/routes/_agent-native/factory/builder-callback.post.ts +116 -0
- package/corpus/templates/factory/server/triage/ai-services-git.ts +197 -0
- package/corpus/templates/factory/server/triage/builder-executor.ts +118 -0
- package/corpus/templates/factory/server/triage/contracts.ts +164 -0
- package/corpus/templates/factory/server/triage/github-ingestion.ts +35 -0
- package/corpus/templates/factory/server/triage/guards.ts +121 -0
- package/corpus/templates/factory/server/triage/ids.ts +27 -0
- package/corpus/templates/factory/server/triage/pr-babysit.ts +100 -0
- package/corpus/templates/factory/server/triage/pr-monitor.ts +220 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +55 -0
- package/corpus/templates/factory/server/triage/slack-poller.ts +152 -0
- package/corpus/templates/factory/ssr-entry.ts +15 -0
- package/corpus/templates/factory/tsconfig.json +21 -0
- package/corpus/templates/factory/vite.config.ts +19 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +55 -5
- package/corpus/templates/forms/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +6 -2
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +21 -6
- package/corpus/templates/plan/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +12 -0
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -0
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +11 -0
- package/corpus/templates/slides/AGENTS.md +5 -6
- package/corpus/templates/slides/actions/_await-fit-check.ts +20 -10
- package/corpus/templates/slides/actions/add-slide.ts +17 -4
- package/corpus/templates/slides/actions/create-design-system.ts +16 -4
- package/corpus/templates/slides/actions/delete-design-system.ts +18 -2
- package/corpus/templates/slides/actions/get-layout-overflows.ts +88 -0
- package/corpus/templates/slides/actions/import-file.ts +1 -0
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +8 -0
- package/corpus/templates/slides/actions/patch-deck.ts +48 -16
- package/corpus/templates/slides/actions/set-default-design-system.ts +49 -19
- package/corpus/templates/slides/actions/update-slide.ts +13 -3
- package/corpus/templates/slides/actions/view-screen.ts +87 -10
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +155 -28
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +21 -17
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +849 -341
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +102 -2
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +13 -16
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +60 -12
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +11 -1
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +66 -19
- package/corpus/templates/slides/app/i18n/en-US.ts +23 -0
- package/corpus/templates/slides/app/lib/normalize-slide-padding.ts +8 -15
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -3
- package/corpus/templates/slides/app/pages/DesignSystems.tsx +4 -3
- package/corpus/templates/slides/app/pages/Index.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-07-31-design-system-details-now-show-their-source-and-indexing-sta.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-31-design-system-indexing-keeps-your-chosen-name-when-you-uploa.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-31-design-system-setup-keeps-focus-rings-visible-and-uses-a-sim.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-31-skip-prompt-is-now-available-in-the-new-deck-prompt-header.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-31-slides-now-detect-real-text-and-box-overflow-across-the-deck.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
- package/corpus/templates/slides/server/handlers/design-systems.ts +1 -0
- package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +1 -0
- package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +23 -4
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/slides/server/plugins/creative-context.ts +16 -3
- package/corpus/templates/slides/server/workspace-defaults.ts +5 -1
- package/corpus/templates/slides/shared/slide-fit.ts +26 -0
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +30 -3
- package/corpus/templates/tasks/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/dist/a2a/correlation.d.ts +4 -1
- package/dist/a2a/correlation.d.ts.map +1 -1
- package/dist/a2a/correlation.js +8 -1
- package/dist/a2a/correlation.js.map +1 -1
- package/dist/a2a/types.d.ts +11 -3
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +27 -41
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +18 -8
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/error-detail.d.ts +61 -0
- package/dist/agent/engine/error-detail.d.ts.map +1 -1
- package/dist/agent/engine/error-detail.js +145 -3
- package/dist/agent/engine/error-detail.js.map +1 -1
- package/dist/agent/engine/failure-taxonomy.d.ts +24 -0
- package/dist/agent/engine/failure-taxonomy.d.ts.map +1 -0
- package/dist/agent/engine/failure-taxonomy.js +88 -0
- package/dist/agent/engine/failure-taxonomy.js.map +1 -0
- package/dist/agent/engine/index.d.ts +2 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +2 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +16 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +71 -6
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.js +14 -2
- package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -24
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.js +1 -1
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +39 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +10 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +20 -3
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +28 -4
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/chat-threads/store.d.ts +13 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +35 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +13 -0
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/cli/workspace-dev.d.ts.map +1 -1
- package/dist/cli/workspace-dev.js +1 -0
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +10 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +66 -16
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/credentials/index.d.ts +9 -2
- package/dist/credentials/index.d.ts.map +1 -1
- package/dist/credentials/index.js +50 -8
- package/dist/credentials/index.js.map +1 -1
- package/dist/extensions/url-safety.d.ts.map +1 -1
- package/dist/extensions/url-safety.js +38 -12
- package/dist/extensions/url-safety.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +25 -8
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/google-docs-poller.d.ts.map +1 -1
- package/dist/integrations/google-docs-poller.js +13 -1
- package/dist/integrations/google-docs-poller.js.map +1 -1
- package/dist/integrations/installations-store.d.ts +10 -0
- package/dist/integrations/installations-store.d.ts.map +1 -1
- package/dist/integrations/installations-store.js +24 -0
- package/dist/integrations/installations-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +6 -5
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/types.d.ts +7 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +16 -1
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/jobs/background-automation-runner.d.ts.map +1 -1
- package/dist/jobs/background-automation-runner.js +35 -2
- package/dist/jobs/background-automation-runner.js.map +1 -1
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/index.js +7 -3
- package/dist/provider-api/index.js.map +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +6 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +38 -9
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts +2 -0
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts +4 -1
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +11 -1
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +49 -12
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +37 -2
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/reasoning-effort.d.ts +1 -0
- package/dist/shared/reasoning-effort.d.ts.map +1 -1
- package/dist/shared/reasoning-effort.js +1 -1
- package/dist/shared/reasoning-effort.js.map +1 -1
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +30 -3
- package/dist/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
- package/package.json +1 -1
- package/src/a2a/correlation.ts +11 -1
- package/src/a2a/types.ts +11 -3
- package/src/agent/engine/ai-sdk-engine.ts +33 -46
- package/src/agent/engine/builder-engine.ts +22 -10
- package/src/agent/engine/error-detail.ts +184 -3
- package/src/agent/engine/failure-taxonomy.ts +123 -0
- package/src/agent/engine/index.ts +8 -0
- package/src/agent/engine/registry.ts +92 -8
- package/src/agent/engine/translate-ai-sdk.ts +17 -2
- package/src/agent/production-agent.ts +13 -33
- package/src/agent/run-loop-with-resume.ts +1 -1
- package/src/agent/run-manager.ts +43 -14
- package/src/agent/run-store.ts +30 -3
- package/src/agent/thread-data-builder.ts +36 -4
- package/src/chat-threads/store.ts +42 -0
- package/src/cli/templates-meta.ts +13 -0
- package/src/cli/workspace-dev.ts +1 -0
- package/src/client/error-format.ts +12 -0
- package/src/client/use-chat-threads.ts +76 -16
- package/src/credentials/index.ts +51 -12
- package/src/extensions/url-safety.ts +46 -12
- package/src/integrations/adapters/slack.ts +32 -10
- package/src/integrations/google-docs-poller.ts +12 -1
- package/src/integrations/installations-store.ts +30 -0
- package/src/integrations/plugin.ts +5 -5
- package/src/integrations/types.ts +7 -0
- package/src/integrations/webhook-handler.ts +24 -1
- package/src/jobs/background-automation-runner.ts +38 -1
- package/src/provider-api/index.ts +8 -2
- package/src/scripts/call-agent.ts +6 -0
- package/src/server/agent-chat-plugin.ts +46 -10
- package/src/server/agent-discovery.ts +2 -0
- package/src/server/builder-design-systems.ts +21 -0
- package/src/server/credential-provider.ts +51 -12
- package/src/server/index.ts +1 -0
- package/src/server/onboarding-html.ts +37 -2
- package/src/shared/reasoning-effort.ts +1 -1
- package/src/templates/chat/app/components/layout/Sidebar.tsx +30 -3
- package/src/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
- package/corpus/templates/clips/desktop/src/lib/media-device-selection.ts +0 -157
|
@@ -4,6 +4,11 @@ import {
|
|
|
4
4
|
LLM_MISSING_CREDENTIALS_ERROR_CODE,
|
|
5
5
|
LLM_MISSING_CREDENTIALS_MESSAGE,
|
|
6
6
|
} from "./engine/credential-errors.js";
|
|
7
|
+
import {
|
|
8
|
+
classifyTerminalErrorCode,
|
|
9
|
+
describeErrorWithCauses,
|
|
10
|
+
isProviderConnectionError,
|
|
11
|
+
} from "./engine/error-detail.js";
|
|
7
12
|
import { EngineError } from "./engine/types.js";
|
|
8
13
|
import {
|
|
9
14
|
insertRun,
|
|
@@ -341,23 +346,16 @@ function getRunErrorMessage(err: unknown): string {
|
|
|
341
346
|
return "Unknown error";
|
|
342
347
|
}
|
|
343
348
|
|
|
344
|
-
function isProviderConnectionErrorMessage(message: string): boolean {
|
|
345
|
-
const normalized = message.trim().toLowerCase();
|
|
346
|
-
return (
|
|
347
|
-
normalized === "connection error." ||
|
|
348
|
-
normalized.includes("cannot connect to api:")
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
349
|
function getRunErrorCode(err: unknown): string | undefined {
|
|
353
350
|
if (err instanceof EngineError) {
|
|
354
351
|
if (err.errorCode) return err.errorCode;
|
|
355
352
|
if (err.statusCode === 429) return PROVIDER_RATE_LIMITED_ERROR_CODE;
|
|
356
353
|
}
|
|
357
|
-
if (
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
354
|
+
if (isProviderConnectionError(err)) return PROVIDER_NETWORK_ERROR_CODE;
|
|
355
|
+
// The code rides the error EVENT to the client, which decides recovery from
|
|
356
|
+
// it — so an uncoded transport failure has to be classified here too, not
|
|
357
|
+
// only when the run row is persisted.
|
|
358
|
+
return classifyTerminalErrorCode(describeErrorWithCauses(err));
|
|
361
359
|
}
|
|
362
360
|
|
|
363
361
|
function getEngineRunErrorDetails(err: EngineError): string | undefined {
|
|
@@ -376,7 +374,7 @@ function shouldCaptureRunError(err: unknown): boolean {
|
|
|
376
374
|
}
|
|
377
375
|
if (!(err instanceof Error)) return true;
|
|
378
376
|
if (/^40[13] status code\b/i.test(err.message)) return false;
|
|
379
|
-
if (
|
|
377
|
+
if (isProviderConnectionError(err)) return false;
|
|
380
378
|
if (!errorCode) return true;
|
|
381
379
|
const normalizedCode = errorCode.toLowerCase();
|
|
382
380
|
return (
|
|
@@ -984,6 +982,13 @@ export function startRun(
|
|
|
984
982
|
// false-stale-reap zombie scenario where the reaper flipped the row while
|
|
985
983
|
// this isolate was briefly unable to heartbeat (DB latency / GC pause).
|
|
986
984
|
let lastAbortCheck = Date.now() - 3000;
|
|
985
|
+
// A read failure here used to be indistinguishable from "not aborted" —
|
|
986
|
+
// exactly the coerced-to-false pattern that lets a real Stop go unseen for
|
|
987
|
+
// the rest of the run. Count consecutive failures like the heartbeat-write
|
|
988
|
+
// handler above; past the same threshold, fail closed (self-abort with a
|
|
989
|
+
// reason outside TURN_ENDING_ABORT_REASONS/RECOVERABLE_ABORT_REASONS, so it
|
|
990
|
+
// surfaces as a typed error) instead of silently retrying forever.
|
|
991
|
+
let consecutiveAbortCheckFailures = 0;
|
|
987
992
|
const checkSqlAbort = () => {
|
|
988
993
|
const now = Date.now();
|
|
989
994
|
if (now - lastAbortCheck < 3000) return;
|
|
@@ -1004,7 +1009,26 @@ export function startRun(
|
|
|
1004
1009
|
}
|
|
1005
1010
|
}
|
|
1006
1011
|
})
|
|
1007
|
-
.
|
|
1012
|
+
.then(() => {
|
|
1013
|
+
consecutiveAbortCheckFailures = 0;
|
|
1014
|
+
})
|
|
1015
|
+
.catch((error) => {
|
|
1016
|
+
consecutiveAbortCheckFailures += 1;
|
|
1017
|
+
if (consecutiveAbortCheckFailures >= 3) {
|
|
1018
|
+
captureError(error, {
|
|
1019
|
+
route: "/_agent-native/agent-chat",
|
|
1020
|
+
tags: {
|
|
1021
|
+
source: "agent-run-manager",
|
|
1022
|
+
phase: "abort-check",
|
|
1023
|
+
consecutiveFailures: String(consecutiveAbortCheckFailures),
|
|
1024
|
+
},
|
|
1025
|
+
extra: { runId, threadId },
|
|
1026
|
+
});
|
|
1027
|
+
if (!abort.signal.aborted) {
|
|
1028
|
+
abortInMemoryRun(run, "abort_check_unavailable");
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1008
1032
|
};
|
|
1009
1033
|
|
|
1010
1034
|
// Heartbeat: bump heartbeat_at every 1.5s so watchers can detect a dead
|
|
@@ -1456,6 +1480,11 @@ export function startRun(
|
|
|
1456
1480
|
? completionError.message
|
|
1457
1481
|
: String(completionError));
|
|
1458
1482
|
}
|
|
1483
|
+
// An engine that emitted an error event without a code has NOT told us
|
|
1484
|
+
// the failure is unclassifiable — it told us nothing. Recover the code
|
|
1485
|
+
// from the message before falling back to "unknown", which the client
|
|
1486
|
+
// reads as "do not attempt recovery".
|
|
1487
|
+
errorCode ??= classifyTerminalErrorCode(errorDetail);
|
|
1459
1488
|
runTerminalErrorCode = errorCode ?? "unknown";
|
|
1460
1489
|
runTerminalErrorDetail = errorDetail;
|
|
1461
1490
|
await setRunError(runId, errorCode ?? "unknown", errorDetail);
|
|
@@ -946,6 +946,16 @@ export interface UnclaimedBackgroundRunRow {
|
|
|
946
946
|
* pre-inserted — independent of any liveness bump a redispatch attempt
|
|
947
947
|
* makes along the way. */
|
|
948
948
|
startedAt: number;
|
|
949
|
+
/**
|
|
950
|
+
* Whether the row still carries the `dispatch_payload` a redispatched worker
|
|
951
|
+
* would rehydrate its request body from. Eligibility for this sweep does NOT
|
|
952
|
+
* imply it: a background row can reach the grace window having never had a
|
|
953
|
+
* payload at all. Redispatching one of those asserts `payloadRef: true` to a
|
|
954
|
+
* worker that then cannot rehydrate, so it kills the run as
|
|
955
|
+
* `dispatch_payload_missing` — a reason that reads like data loss for what is
|
|
956
|
+
* really an un-redispatchable handoff.
|
|
957
|
+
*/
|
|
958
|
+
hasDispatchPayload: boolean;
|
|
949
959
|
}
|
|
950
960
|
|
|
951
961
|
/**
|
|
@@ -965,7 +975,10 @@ export async function listUnclaimedBackgroundRunRows(): Promise<
|
|
|
965
975
|
const { rows } = await client.execute({
|
|
966
976
|
// CAST keeps the ms-epoch param 64-bit on Postgres (see
|
|
967
977
|
// backgroundAwareStaleCutoffSql for the int4-inference failure mode).
|
|
968
|
-
|
|
978
|
+
// Report payload presence rather than filtering on it: a payload-less row
|
|
979
|
+
// must still be VISIBLE to the sweep so the slow pass can reap it. Filtering
|
|
980
|
+
// it out here would leave it `running` forever.
|
|
981
|
+
sql: `SELECT id, started_at, (dispatch_payload IS NOT NULL) AS has_dispatch_payload FROM agent_runs
|
|
969
982
|
WHERE status = 'running'
|
|
970
983
|
AND dispatch_mode = 'background'
|
|
971
984
|
AND COALESCE(heartbeat_at, started_at) < (CAST(? AS BIGINT) - ${UNCLAIMED_BACKGROUND_RUN_GRACE_MS})`,
|
|
@@ -975,11 +988,15 @@ export async function listUnclaimedBackgroundRunRows(): Promise<
|
|
|
975
988
|
for (const row of rows ?? []) {
|
|
976
989
|
const id = (row as { id?: unknown }).id;
|
|
977
990
|
const startedAt = (row as { started_at?: unknown }).started_at;
|
|
991
|
+
const hasPayload = (row as { has_dispatch_payload?: unknown })
|
|
992
|
+
.has_dispatch_payload;
|
|
978
993
|
if (typeof id === "string" && id) {
|
|
979
994
|
result.push({
|
|
980
995
|
id,
|
|
981
996
|
startedAt:
|
|
982
997
|
typeof startedAt === "number" ? startedAt : Number(startedAt) || 0,
|
|
998
|
+
// SQLite returns 1/0 where Postgres returns a boolean.
|
|
999
|
+
hasDispatchPayload: hasPayload === true || hasPayload === 1,
|
|
983
1000
|
});
|
|
984
1001
|
}
|
|
985
1002
|
}
|
|
@@ -1147,10 +1164,20 @@ export async function setRunTerminalReason(
|
|
|
1147
1164
|
await ensureRunTables();
|
|
1148
1165
|
const client = getDbExec();
|
|
1149
1166
|
const reason = terminalReason.slice(0, 200);
|
|
1167
|
+
// Write-once for a row that is already terminal. Three writers in three
|
|
1168
|
+
// isolates race on this column — the mid-run checkpoint, the run-manager's
|
|
1169
|
+
// finalization, and the background worker's failure path — with no ordering
|
|
1170
|
+
// between them, and last-writer-wins let a late checkpoint relabel a row
|
|
1171
|
+
// another isolate had already finalized. That produced impossible rows
|
|
1172
|
+
// (status='errored' carrying a continuation reason, no error_code, no
|
|
1173
|
+
// terminal event) and misattributed 130 production runs to a failure mode
|
|
1174
|
+
// they never hit. A row still `running` has no honest reason yet, so it
|
|
1175
|
+
// stays writable; once one is recorded on a terminal row, it stands.
|
|
1176
|
+
const guard = `AND (status = 'running' OR terminal_reason IS NULL OR terminal_reason = '')`;
|
|
1150
1177
|
await client.execute({
|
|
1151
1178
|
sql: isContinuationTerminalReason(reason)
|
|
1152
|
-
? `UPDATE agent_runs SET terminal_reason = ?, status = CASE WHEN status = 'completed' THEN 'truncated' ELSE status END WHERE id =
|
|
1153
|
-
: `UPDATE agent_runs SET terminal_reason = ? WHERE id =
|
|
1179
|
+
? `UPDATE agent_runs SET terminal_reason = ?, status = CASE WHEN status = 'completed' THEN 'truncated' ELSE status END WHERE id = ? ${guard}`
|
|
1180
|
+
: `UPDATE agent_runs SET terminal_reason = ? WHERE id = ? ${guard}`,
|
|
1154
1181
|
args: [reason, runId],
|
|
1155
1182
|
});
|
|
1156
1183
|
} catch {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ActionChatUIConfig } from "../action-ui.js";
|
|
2
|
+
import {
|
|
3
|
+
formatChatErrorText,
|
|
4
|
+
normalizeChatError,
|
|
5
|
+
} from "../client/error-format.js";
|
|
2
6
|
import {
|
|
3
7
|
isCredentialGapCodeAgentEvent,
|
|
4
8
|
normalizeCodeAgentTranscript,
|
|
@@ -140,12 +144,29 @@ export function buildAssistantMessage(
|
|
|
140
144
|
}
|
|
141
145
|
};
|
|
142
146
|
|
|
147
|
+
// Index of the last event that is not a `clear`. Everything after it is a
|
|
148
|
+
// trailing run of clears with no successor chunk to re-emit what they wipe.
|
|
149
|
+
let lastNonClearIndex = events.length - 1;
|
|
150
|
+
while (
|
|
151
|
+
lastNonClearIndex >= 0 &&
|
|
152
|
+
events[lastNonClearIndex]?.event.type === "clear"
|
|
153
|
+
) {
|
|
154
|
+
lastNonClearIndex -= 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
143
157
|
for (const [index, { event }] of events.entries()) {
|
|
144
158
|
if (event.type === "clear") {
|
|
145
159
|
// A live stream always follows `clear` with the chunk that re-emits the
|
|
146
160
|
// wiped content. A rebuild has no successor, so applying a TRAILING
|
|
147
161
|
// clear can only destroy the transcript permanently.
|
|
148
|
-
|
|
162
|
+
//
|
|
163
|
+
// The whole trailing RUN has to be skipped, not just the final element:
|
|
164
|
+
// each failed engine attempt emits one `clear`, so three failed attempts
|
|
165
|
+
// in a row is the common shape, and skipping only the last still applied
|
|
166
|
+
// the other two. When the run made no tool calls that emptied `content`
|
|
167
|
+
// entirely and this builder returned null — the user's message was left
|
|
168
|
+
// with no assistant reply at all.
|
|
169
|
+
if (index > lastNonClearIndex) continue;
|
|
149
170
|
clearAssistantDraftContent(content);
|
|
150
171
|
continue;
|
|
151
172
|
}
|
|
@@ -248,13 +269,24 @@ export function buildAssistantMessage(
|
|
|
248
269
|
if (event.errorCode === "run_timeout" && event.recoverable) {
|
|
249
270
|
continue;
|
|
250
271
|
}
|
|
272
|
+
// Mirror the live client (client/sse-event-processor.ts): route the raw
|
|
273
|
+
// provider/engine string through the same friendly-copy layer before it
|
|
274
|
+
// ever becomes persisted chat text, and keep the raw text only in
|
|
275
|
+
// `details`. Without this, a rebuild (background run, reconnect, poller,
|
|
276
|
+
// webhook turn) dumps whatever the provider sent — a JSON error body, an
|
|
277
|
+
// SSL handshake failure — straight into the user-visible transcript.
|
|
278
|
+
const normalized = normalizeChatError(event.error, event.errorCode);
|
|
251
279
|
runError = {
|
|
252
|
-
message:
|
|
280
|
+
message: normalized.message,
|
|
253
281
|
...(event.errorCode ? { errorCode: event.errorCode } : {}),
|
|
254
|
-
...(event.details
|
|
282
|
+
...((event.details ?? normalized.details)
|
|
283
|
+
? { details: event.details ?? normalized.details }
|
|
284
|
+
: {}),
|
|
255
285
|
...(event.recoverable ? { recoverable: event.recoverable } : {}),
|
|
256
286
|
};
|
|
257
|
-
appendText(
|
|
287
|
+
appendText(
|
|
288
|
+
`${content.length > 0 ? "\n\n" : ""}${formatChatErrorText(event.error, event.upgradeUrl, event.errorCode)}`,
|
|
289
|
+
);
|
|
258
290
|
continue;
|
|
259
291
|
}
|
|
260
292
|
|
|
@@ -766,6 +766,48 @@ export async function searchThreads(
|
|
|
766
766
|
.filter((r): r is ChatThreadSummary => r !== null);
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
+
/**
|
|
770
|
+
* Scope a thread should carry after a run inside a resource: adopt when it has
|
|
771
|
+
* none, otherwise keep what it has. An unscoped thread reads as general, and a
|
|
772
|
+
* general chat renders inside every resource — so never retag, never clear.
|
|
773
|
+
*/
|
|
774
|
+
export function resolveRunThreadScope(
|
|
775
|
+
existing: ChatThreadScope | null,
|
|
776
|
+
incoming: ChatThreadScope | null | undefined,
|
|
777
|
+
): ChatThreadScope | null {
|
|
778
|
+
if (existing) return existing;
|
|
779
|
+
return incoming ?? null;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Claim an unscoped thread for `scope`, returning the scope it actually ends up
|
|
784
|
+
* with. `withThreadDataLock` only serializes one process, so two workers can
|
|
785
|
+
* both read the same unscoped row; the `scope_type IS NULL` guard makes the
|
|
786
|
+
* first writer win and the loser reports the winner instead of retagging.
|
|
787
|
+
*/
|
|
788
|
+
export async function adoptThreadScopeIfUnscoped(
|
|
789
|
+
id: string,
|
|
790
|
+
scope: ChatThreadScope,
|
|
791
|
+
): Promise<ChatThreadScope | null> {
|
|
792
|
+
await ensureTable();
|
|
793
|
+
const client = getDbExec();
|
|
794
|
+
const result = await client.execute({
|
|
795
|
+
sql: `UPDATE chat_threads SET scope_type = ?, scope_id = ?, scope_label = ?, updated_at = ? WHERE id = ? AND scope_type IS NULL`,
|
|
796
|
+
args: [
|
|
797
|
+
scope.type,
|
|
798
|
+
scope.id,
|
|
799
|
+
scope.label ?? null,
|
|
800
|
+
Math.max(Date.now(), 1),
|
|
801
|
+
id,
|
|
802
|
+
],
|
|
803
|
+
});
|
|
804
|
+
if (result.rowsAffected > 0) {
|
|
805
|
+
emitChatThreadChange(id);
|
|
806
|
+
return scope;
|
|
807
|
+
}
|
|
808
|
+
return (await getThread(id))?.scope ?? null;
|
|
809
|
+
}
|
|
810
|
+
|
|
769
811
|
/**
|
|
770
812
|
* Detach or rebind a chat's scope. Used by the UI's "Detach from <resource>"
|
|
771
813
|
* action and by templates that need to retag a chat after a rename. Pass
|
|
@@ -254,6 +254,19 @@ export const TEMPLATES: TemplateMeta[] = [
|
|
|
254
254
|
hidden: true,
|
|
255
255
|
defaultMode: "dev",
|
|
256
256
|
},
|
|
257
|
+
{
|
|
258
|
+
name: "factory",
|
|
259
|
+
label: "Factory",
|
|
260
|
+
hint: "Build agent factories with gates you control",
|
|
261
|
+
icon: "Users",
|
|
262
|
+
color: "#7C3AED",
|
|
263
|
+
colorRgb: "124 58 237",
|
|
264
|
+
devPort: 8108,
|
|
265
|
+
prodUrl: "https://agent-native-factory.netlify.app",
|
|
266
|
+
hidden: true,
|
|
267
|
+
defaultMode: "dev",
|
|
268
|
+
core: false,
|
|
269
|
+
},
|
|
257
270
|
];
|
|
258
271
|
|
|
259
272
|
/** Return templates visible in user-facing pickers (excludes hidden). */
|
|
@@ -712,6 +712,7 @@ export async function runWorkspaceDev(
|
|
|
712
712
|
name: workspaceApp.name,
|
|
713
713
|
description: workspaceApp.description,
|
|
714
714
|
path: `/${workspaceApp.id}`,
|
|
715
|
+
port: workspaceApp.port,
|
|
715
716
|
audience: workspaceApp.audience,
|
|
716
717
|
publicPaths: workspaceApp.publicPaths,
|
|
717
718
|
protectedPaths: workspaceApp.protectedPaths,
|
|
@@ -95,6 +95,7 @@ function isProviderAuthenticationError(
|
|
|
95
95
|
/\b(?:http\s*)?401\b.*\b(?:status|unauthorized|authentication|auth|no body)\b/i.test(
|
|
96
96
|
text,
|
|
97
97
|
) ||
|
|
98
|
+
lower.includes("missing authentication header") ||
|
|
98
99
|
lower.includes("invalid x-api-key") ||
|
|
99
100
|
lower.includes("invalid api key") ||
|
|
100
101
|
lower.includes("incorrect api key") ||
|
|
@@ -141,6 +142,17 @@ export function normalizeChatError(
|
|
|
141
142
|
};
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
// A model/parameter combination this provider will never accept. Retrying is
|
|
146
|
+
// pointless and the raw sentence names an API surface the reader has no way
|
|
147
|
+
// to act on, so say what they can actually change.
|
|
148
|
+
if (code === "provider_config_error") {
|
|
149
|
+
return {
|
|
150
|
+
message:
|
|
151
|
+
"This model can't use tools with the current settings. Switch models in Settings, then retry.",
|
|
152
|
+
details: text,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
144
156
|
if (isProviderRateLimit(text, errorCode)) {
|
|
145
157
|
return {
|
|
146
158
|
message:
|
|
@@ -91,6 +91,25 @@ async function fetchThreadListPage(
|
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Look up one thread the list page did not carry. Distinguishes the three states
|
|
96
|
+
* the caller must not collapse: the thread (found), `null` (the server denies it
|
|
97
|
+
* exists), and `undefined` (unreachable — nothing was learned).
|
|
98
|
+
*/
|
|
99
|
+
async function fetchThreadById(
|
|
100
|
+
apiUrl: string,
|
|
101
|
+
id: string,
|
|
102
|
+
): Promise<ChatThreadSummary | null | undefined> {
|
|
103
|
+
try {
|
|
104
|
+
const res = await fetch(`${apiUrl}/threads/${encodeURIComponent(id)}`);
|
|
105
|
+
if (res.status === 404) return null;
|
|
106
|
+
if (!res.ok) return undefined;
|
|
107
|
+
return (await res.json()) as ChatThreadSummary;
|
|
108
|
+
} catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
94
113
|
function emitThreadsUpdated() {
|
|
95
114
|
if (typeof window === "undefined") return;
|
|
96
115
|
window.dispatchEvent(new CustomEvent(THREADS_UPDATED_EVENT));
|
|
@@ -411,6 +430,17 @@ export function useChatThreads(
|
|
|
411
430
|
} catch {
|
|
412
431
|
nextActiveThreadId = null;
|
|
413
432
|
}
|
|
433
|
+
// Only a known mismatch disqualifies the pointer — an unresolved scope
|
|
434
|
+
// must not be read as "belongs here".
|
|
435
|
+
if (nextActiveThreadId) {
|
|
436
|
+
const savedScope = readKnownThreadScope(nextActiveThreadId);
|
|
437
|
+
if (
|
|
438
|
+
savedScope !== undefined &&
|
|
439
|
+
!threadCanStayVisibleInScope(savedScope, scopeRef.current)
|
|
440
|
+
) {
|
|
441
|
+
nextActiveThreadId = null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
414
444
|
if (!nextActiveThreadId && autoCreate) {
|
|
415
445
|
nextActiveThreadId = createLocalThreadId();
|
|
416
446
|
newlyCreatedRef.current.add(nextActiveThreadId);
|
|
@@ -583,7 +613,7 @@ export function useChatThreads(
|
|
|
583
613
|
|
|
584
614
|
(async () => {
|
|
585
615
|
const loadedThreads = await fetchThreads();
|
|
586
|
-
const
|
|
616
|
+
const restoredId = activeThreadIdRef.current;
|
|
587
617
|
if (loadedThreads === undefined) {
|
|
588
618
|
// Thread-list fetch failed. Do not reclassify a saved id as a new
|
|
589
619
|
// optimistic tab; AssistantChat should still get a chance to restore
|
|
@@ -591,8 +621,40 @@ export function useChatThreads(
|
|
|
591
621
|
setIsLoading(false);
|
|
592
622
|
return;
|
|
593
623
|
}
|
|
624
|
+
// Exempts route-owned threads (the URL names what the user asked for) and
|
|
625
|
+
// ids this client generated, which have never reached the server.
|
|
626
|
+
const lookupRestored = Boolean(
|
|
627
|
+
restoredId &&
|
|
628
|
+
!routeControlsActiveThread &&
|
|
629
|
+
!newlyCreatedRef.current.has(restoredId),
|
|
630
|
+
);
|
|
631
|
+
const restoredOnPage = restoredId
|
|
632
|
+
? loadedThreads.find((t) => t.id === restoredId)
|
|
633
|
+
: undefined;
|
|
634
|
+
// One page, so absence from it is not absence from the server — this is what
|
|
635
|
+
// separates an older real thread from the ghost tab reclassified below.
|
|
636
|
+
const restoredThread =
|
|
637
|
+
lookupRestored && !restoredOnPage
|
|
638
|
+
? await fetchThreadById(apiUrl, restoredId!)
|
|
639
|
+
: restoredOnPage;
|
|
640
|
+
if (restoredThread === undefined && lookupRestored && !restoredOnPage) {
|
|
641
|
+
// Lookup unreachable. Reclassifying now would stamp this thread with the
|
|
642
|
+
// current scope on a guess; leave it untouched for the next mount.
|
|
643
|
+
setIsLoading(false);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const restoredBelongsElsewhere = Boolean(
|
|
647
|
+
restoredThread &&
|
|
648
|
+
!threadCanStayVisibleInScope(
|
|
649
|
+
restoredThread.scope ?? null,
|
|
650
|
+
scopeRef.current,
|
|
651
|
+
),
|
|
652
|
+
);
|
|
653
|
+
if (restoredBelongsElsewhere) setActiveThreadId(null);
|
|
654
|
+
const savedId = restoredBelongsElsewhere ? null : restoredId;
|
|
594
655
|
const loadedHasSavedId = Boolean(
|
|
595
|
-
savedId &&
|
|
656
|
+
savedId &&
|
|
657
|
+
(restoredThread || loadedThreads.some((t) => t.id === savedId)),
|
|
596
658
|
);
|
|
597
659
|
const savedIdCameFromRoute =
|
|
598
660
|
Boolean(savedId) &&
|
|
@@ -646,6 +708,7 @@ export function useChatThreads(
|
|
|
646
708
|
setIsLoading(false);
|
|
647
709
|
})();
|
|
648
710
|
}, [
|
|
711
|
+
apiUrl,
|
|
649
712
|
fetchThreads,
|
|
650
713
|
addOptimisticThread,
|
|
651
714
|
autoCreate,
|
|
@@ -948,12 +1011,9 @@ export function useChatThreads(
|
|
|
948
1011
|
[apiUrl, clearUserRenamedThread, createThread],
|
|
949
1012
|
);
|
|
950
1013
|
|
|
951
|
-
//
|
|
952
|
-
//
|
|
953
|
-
//
|
|
954
|
-
// detachThread / fetchThreads — saveThreadData just mirrors it on
|
|
955
|
-
// every save so the server eventually catches up after
|
|
956
|
-
// persistSubmittedUserMessage creates the row sans scope.
|
|
1014
|
+
// Reads scope through refs so this callback survives every setThreads. Scope
|
|
1015
|
+
// rides only on creation: a periodic save must never move an existing thread
|
|
1016
|
+
// between resources, however stale this client's guess is.
|
|
957
1017
|
const saveThreadData = useCallback(
|
|
958
1018
|
async (
|
|
959
1019
|
id: string,
|
|
@@ -968,7 +1028,7 @@ export function useChatThreads(
|
|
|
968
1028
|
try {
|
|
969
1029
|
const { titleSource, ...threadDataPayload } = data;
|
|
970
1030
|
const localThread = threadsRef.current.find((t) => t.id === id);
|
|
971
|
-
const
|
|
1031
|
+
const knownScope = readKnownThreadScope(id) ?? null;
|
|
972
1032
|
const preserveUserTitle = userRenamedThreadIdsRef.current.has(id);
|
|
973
1033
|
const title = nextThreadTitle(
|
|
974
1034
|
localThread?.title,
|
|
@@ -977,11 +1037,7 @@ export function useChatThreads(
|
|
|
977
1037
|
titleSource,
|
|
978
1038
|
{ preserveUserTitle },
|
|
979
1039
|
);
|
|
980
|
-
const payload = {
|
|
981
|
-
...threadDataPayload,
|
|
982
|
-
title,
|
|
983
|
-
scope: localScope,
|
|
984
|
-
};
|
|
1040
|
+
const payload = { ...threadDataPayload, title };
|
|
985
1041
|
let response = await fetch(
|
|
986
1042
|
`${apiUrl}/threads/${encodeURIComponent(id)}`,
|
|
987
1043
|
{
|
|
@@ -997,7 +1053,11 @@ export function useChatThreads(
|
|
|
997
1053
|
const created = await fetch(`${apiUrl}/threads`, {
|
|
998
1054
|
method: "POST",
|
|
999
1055
|
headers: { "Content-Type": "application/json" },
|
|
1000
|
-
body: JSON.stringify({
|
|
1056
|
+
body: JSON.stringify({
|
|
1057
|
+
id,
|
|
1058
|
+
title,
|
|
1059
|
+
...(knownScope ? { scope: knownScope } : {}),
|
|
1060
|
+
}),
|
|
1001
1061
|
});
|
|
1002
1062
|
if (!created.ok) return;
|
|
1003
1063
|
response = await fetch(
|
|
@@ -1059,7 +1119,7 @@ export function useChatThreads(
|
|
|
1059
1119
|
});
|
|
1060
1120
|
} catch {}
|
|
1061
1121
|
},
|
|
1062
|
-
[apiUrl],
|
|
1122
|
+
[apiUrl, readKnownThreadScope],
|
|
1063
1123
|
);
|
|
1064
1124
|
|
|
1065
1125
|
const generateTitle = useCallback(
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
isEncryptedSecretValue,
|
|
6
6
|
} from "../secrets/crypto.js";
|
|
7
7
|
import { readAppSecret, type SecretRef } from "../secrets/storage.js";
|
|
8
|
+
import { assertCredentialStoreReadable } from "../server/credential-provider.js";
|
|
8
9
|
import { getSetting, putSetting, deleteSetting } from "../settings/store.js";
|
|
9
10
|
|
|
10
11
|
const SETTING_PREFIX = "credential:";
|
|
@@ -76,6 +77,33 @@ export async function resolveCredentialForScope(
|
|
|
76
77
|
return readCredentialSetting(userCredentialSettingKey(ctx.userEmail, key));
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
/**
|
|
81
|
+
* `ctx.orgId` when the caller supplied one, otherwise the org resolved from
|
|
82
|
+
* `ctx.userEmail`'s membership. Interactive requests always supply `orgId`
|
|
83
|
+
* (session/`getOrgContext` backfill it); CLI runs, cron jobs, and any other
|
|
84
|
+
* caller built straight from `getCredentialContext()` outside a request event
|
|
85
|
+
* do not, and previously fell through as "no active org" — invisibly skipping
|
|
86
|
+
* every org-scoped credential a signed-in session could see. Mirrors the
|
|
87
|
+
* fallback already proven in `resolveSecretDetailed`
|
|
88
|
+
* (server/credential-provider.ts).
|
|
89
|
+
*/
|
|
90
|
+
async function resolveEffectiveOrgId(
|
|
91
|
+
ctx: CredentialContext,
|
|
92
|
+
): Promise<{ orgId: string | null; lookupFailed: boolean; cause?: unknown }> {
|
|
93
|
+
if (ctx.orgId) return { orgId: ctx.orgId, lookupFailed: false };
|
|
94
|
+
try {
|
|
95
|
+
const { resolveOrgIdForEmail } = await import("../org/context.js");
|
|
96
|
+
return {
|
|
97
|
+
orgId: await resolveOrgIdForEmail(ctx.userEmail),
|
|
98
|
+
lookupFailed: false,
|
|
99
|
+
};
|
|
100
|
+
} catch (cause) {
|
|
101
|
+
// Membership was unreadable, not merely absent — must not collapse to
|
|
102
|
+
// "caller has no org", which would silently hide every org-scoped row.
|
|
103
|
+
return { orgId: null, lookupFailed: true, cause };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
79
107
|
/**
|
|
80
108
|
* Resolve a credential across the encrypted app_secrets store and the legacy
|
|
81
109
|
* settings-backed credential store. User overrides win, followed by the
|
|
@@ -92,7 +120,12 @@ export async function resolveCredentialForScope(
|
|
|
92
120
|
* 5. org-scoped legacy settings credential
|
|
93
121
|
* 6. solo workspace-scoped app_secrets (`solo:<email>`)
|
|
94
122
|
*
|
|
95
|
-
* Steps 3-5
|
|
123
|
+
* Steps 3-5 use `ctx.orgId` when given, else the org resolved from
|
|
124
|
+
* `ctx.userEmail` (see `resolveEffectiveOrgId`), and are skipped only when the
|
|
125
|
+
* caller truly has no org. A membership lookup that could not be read throws
|
|
126
|
+
* `CredentialStoreUnavailableError` instead of silently reporting the
|
|
127
|
+
* credential as unset — "the store didn't answer" and "nothing is saved" are
|
|
128
|
+
* different outcomes callers must not conflate.
|
|
96
129
|
*/
|
|
97
130
|
export async function resolveCredential(
|
|
98
131
|
key: string,
|
|
@@ -109,19 +142,20 @@ export async function resolveCredential(
|
|
|
109
142
|
});
|
|
110
143
|
if (userSetting) return userSetting;
|
|
111
144
|
|
|
112
|
-
|
|
113
|
-
|
|
145
|
+
const orgLookup = await resolveEffectiveOrgId(ctx);
|
|
146
|
+
assertCredentialStoreReadable(orgLookup);
|
|
147
|
+
const { orgId } = orgLookup;
|
|
148
|
+
|
|
149
|
+
if (orgId) {
|
|
150
|
+
const orgSecret = await readScopedAppSecret(key, "org", orgId);
|
|
114
151
|
if (orgSecret) return orgSecret;
|
|
115
152
|
|
|
116
|
-
const workspaceSecret = await readScopedAppSecret(
|
|
117
|
-
key,
|
|
118
|
-
"workspace",
|
|
119
|
-
ctx.orgId,
|
|
120
|
-
);
|
|
153
|
+
const workspaceSecret = await readScopedAppSecret(key, "workspace", orgId);
|
|
121
154
|
if (workspaceSecret) return workspaceSecret;
|
|
122
155
|
|
|
123
156
|
const orgSetting = await resolveCredentialForScope(key, {
|
|
124
157
|
...ctx,
|
|
158
|
+
orgId,
|
|
125
159
|
scope: "org",
|
|
126
160
|
});
|
|
127
161
|
if (orgSetting) return orgSetting;
|
|
@@ -156,7 +190,9 @@ export async function resolveCredential(
|
|
|
156
190
|
* org the caller already belongs to), and never return the owning account, how
|
|
157
191
|
* many rows matched, or any part of the value. Without an active org there is
|
|
158
192
|
* no boundary to bound the probe to, so it declines to answer rather than
|
|
159
|
-
* revealing that some other tenant holds a key of the same name
|
|
193
|
+
* revealing that some other tenant holds a key of the same name — this
|
|
194
|
+
* includes callers whose `ctx.orgId` is unset AND whose org could not be
|
|
195
|
+
* resolved from `ctx.userEmail` (see `resolveEffectiveOrgId`).
|
|
160
196
|
*
|
|
161
197
|
* Returns null when nothing safe and useful can be said.
|
|
162
198
|
*/
|
|
@@ -164,10 +200,13 @@ export async function describeCredentialScopeGap(
|
|
|
164
200
|
keys: readonly string[],
|
|
165
201
|
ctx: CredentialContext,
|
|
166
202
|
): Promise<string | null> {
|
|
167
|
-
if (!ctx?.userEmail
|
|
203
|
+
if (!ctx?.userEmail) return null;
|
|
204
|
+
const orgLookup = await resolveEffectiveOrgId(ctx);
|
|
205
|
+
if (orgLookup.lookupFailed || !orgLookup.orgId) return null;
|
|
206
|
+
const scopedCtx: CredentialContext = { ...ctx, orgId: orgLookup.orgId };
|
|
168
207
|
|
|
169
208
|
for (const key of keys) {
|
|
170
|
-
if (await hasForeignPersonalCredentialInOrg(key,
|
|
209
|
+
if (await hasForeignPersonalCredentialInOrg(key, scopedCtx)) {
|
|
171
210
|
return (
|
|
172
211
|
`A "${key}" key is saved in this workspace with Personal scope. ` +
|
|
173
212
|
`Personal keys are readable only by their own owner's signed-in sessions, ` +
|
|
@@ -177,7 +216,7 @@ export async function describeCredentialScopeGap(
|
|
|
177
216
|
);
|
|
178
217
|
}
|
|
179
218
|
|
|
180
|
-
const holder = await findMemberOrgHoldingCredential(key,
|
|
219
|
+
const holder = await findMemberOrgHoldingCredential(key, scopedCtx);
|
|
181
220
|
if (holder) {
|
|
182
221
|
return (
|
|
183
222
|
`A "${key}" key is saved in the ${holder} organization, but this ` +
|