@agent-native/core 0.95.0 → 0.96.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 +35 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +13 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +18 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +16 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +17 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +16 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +17 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +15 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +16 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +11 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +12 -0
- package/corpus/core/docs/content/template-design.mdx +16 -0
- package/corpus/core/package.json +4 -2
- package/corpus/core/src/a2a/client.ts +11 -1
- package/corpus/core/src/agent/production-agent.ts +107 -27
- package/corpus/core/src/agent/thread-data-builder.ts +48 -22
- package/corpus/core/src/chat-threads/store.ts +20 -1
- package/corpus/core/src/cli/code-agent-executor.ts +1 -0
- package/corpus/core/src/cli/code-agent-runs.ts +14 -0
- package/corpus/core/src/client/AgentPanel.tsx +32 -25
- package/corpus/core/src/client/AssistantChat.tsx +286 -45
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +141 -90
- package/corpus/core/src/client/chat/ChatHistoryList.tsx +404 -0
- package/corpus/core/src/client/chat/index.ts +6 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +38 -4
- package/corpus/core/src/client/chat/use-reconnect-reader-owner.ts +28 -0
- package/corpus/core/src/client/code-agent-chat-adapter.ts +72 -4
- package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +52 -11
- package/corpus/core/src/client/composer/TiptapComposer.tsx +157 -20
- package/corpus/core/src/client/composer/VoiceButton.tsx +28 -0
- package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +5 -0
- package/corpus/core/src/client/conversation/code-agent-transcript.ts +4 -5
- package/corpus/core/src/client/guided-questions.tsx +11 -2
- package/corpus/core/src/client/index.ts +11 -0
- package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +6 -7
- package/corpus/core/src/client/sse-event-processor.ts +90 -21
- package/corpus/core/src/client/use-chat-threads.ts +10 -1
- package/corpus/core/src/client/voice-provider-status.ts +44 -0
- package/corpus/core/src/code-agents/transcript-normalizer.ts +76 -0
- package/corpus/core/src/connections/catalog.ts +2 -2
- package/corpus/core/src/deploy/build.ts +12 -8
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +35 -5
- package/corpus/core/src/integrations/adapters/slack.ts +74 -21
- package/corpus/core/src/integrations/catalog.ts +2 -2
- package/corpus/core/src/integrations/webhook-handler.ts +14 -0
- package/corpus/core/src/localization/default-messages.ts +6 -0
- package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
- package/corpus/core/src/observability/routes.ts +17 -0
- package/corpus/core/src/observability/store.ts +4 -1
- package/corpus/core/src/observability/traces.ts +41 -2
- package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +19 -1
- package/corpus/core/src/scripts/call-agent.ts +53 -3
- package/corpus/core/src/scripts/chat/search-chats.ts +11 -8
- package/corpus/core/src/secrets/register-framework-secrets.ts +1 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +41 -6
- package/corpus/core/src/server/auth.ts +1 -1
- package/corpus/core/src/server/core-routes-plugin.ts +15 -0
- package/corpus/core/src/server/credential-provider.ts +7 -3
- package/corpus/core/src/server/realtime-voice.ts +71 -24
- package/corpus/core/src/server/voice-providers-status.ts +4 -3
- package/corpus/core/src/shared/cache-control.ts +4 -4
- package/corpus/core/src/styles/agent-native.css +1 -0
- package/corpus/core/src/styles/chat-history-list.css +313 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/corpus/core/src/vite/client.ts +1 -0
- package/corpus/templates/analytics/AGENTS.md +7 -0
- package/corpus/templates/analytics/actions/bigquery.ts +26 -2
- package/corpus/templates/analytics/actions/data-source-status.ts +18 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +5 -4
- package/corpus/templates/analytics/app/global.css +10 -0
- package/corpus/templates/analytics/app/lib/data-sources.ts +5 -4
- package/corpus/templates/analytics/app/pages/Ask.tsx +25 -2
- package/corpus/templates/analytics/changelog/2026-07-10-analytics-answers-simple-time-bounded-metrics-without-unnece.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-analytics-chat-reliably-uses-connected-data-sources-across-s.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-chat-threads-now-stay-clear-of-top-actions-and-fade-smoothly.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-context-chips-can-now-be-selected-and-removed-one-at-a-time-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-slack-token-setup-marked-legacy.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-the-ask-composer-no-longer-carries-dashboard-or-panel-contex.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-the-ask-section-stays-highlighted-while-its-chat-history-is-.md +6 -0
- package/corpus/templates/analytics/server/lib/bigquery.ts +90 -14
- package/corpus/templates/analytics/server/lib/credential-keys.ts +6 -2
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +82 -7
- package/corpus/templates/brain/changelog/2026-07-10-slack-backfill-token-marked-legacy.md +6 -0
- package/corpus/templates/brain/server/register-secrets.ts +2 -2
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -1
- package/corpus/templates/clips/app/components/library/page-header.tsx +19 -3
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +16 -12
- package/corpus/templates/clips/changelog/2026-07-10-new-recording-appears-once-when-the-library-sidebar-is-open.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-slack-token-fallback-guidance.md +6 -0
- package/corpus/templates/clips/server/register-secrets.ts +1 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +0 -66
- package/corpus/templates/content/app/routes/_app.local-files.tsx +7 -47
- package/corpus/templates/content/changelog/2026-07-10-local-folders-now-wait-for-an-explicit-pull-before-content-r.md +6 -0
- package/corpus/templates/content/package.json +1 -1
- package/corpus/templates/design/actions/generate-design.ts +128 -31
- package/corpus/templates/design/actions/present-design-variants.ts +37 -9
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +9 -3
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +32 -10
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +2 -2
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -5
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
- package/corpus/templates/design/app/global.css +10 -0
- package/corpus/templates/design/app/hooks/use-question-flow.ts +9 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/i18n-data.ts +21 -11
- package/corpus/templates/design/changelog/2026-07-10-editor-panels-are-easier-to-distinguish-and-design-intake-qu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-interact-mode-now-stays-on-the-full-canvas-matches-edit-visu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-new-web-designs-open-at-desktop-size-and-adapt-across-mobile.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-tweaks-now-link-to-full-documentation.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/mail/app/i18n/ar-SA.ts +3 -3
- package/corpus/templates/mail/app/i18n/de-DE.ts +3 -3
- package/corpus/templates/mail/app/i18n/en-US.ts +4 -3
- package/corpus/templates/mail/app/i18n/es-ES.ts +3 -3
- package/corpus/templates/mail/app/i18n/fr-FR.ts +3 -3
- package/corpus/templates/mail/app/i18n/hi-IN.ts +3 -3
- package/corpus/templates/mail/app/i18n/ja-JP.ts +3 -3
- package/corpus/templates/mail/app/i18n/ko-KR.ts +3 -3
- package/corpus/templates/mail/app/i18n/pt-BR.ts +3 -3
- package/corpus/templates/mail/app/i18n/zh-CN.ts +3 -3
- package/corpus/templates/mail/app/i18n/zh-TW.ts +3 -3
- package/corpus/templates/mail/changelog/2026-07-10-slack-intake-marked-legacy.md +6 -0
- package/corpus/templates/mail/server/lib/env-config.ts +5 -4
- package/corpus/templates/plan/package.json +1 -1
- package/dist/a2a/client.d.ts +7 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +2 -1
- package/dist/a2a/client.js.map +1 -1
- package/dist/agent/production-agent.d.ts +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +83 -26
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +42 -23
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/chat-threads/store.d.ts +10 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +6 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +1 -0
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.d.ts +12 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +1 -0
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +3 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +14 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +207 -32
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +83 -10
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/chat/ChatHistoryList.d.ts +88 -0
- package/dist/client/chat/ChatHistoryList.d.ts.map +1 -0
- package/dist/client/chat/ChatHistoryList.js +83 -0
- package/dist/client/chat/ChatHistoryList.js.map +1 -0
- package/dist/client/chat/index.d.ts +1 -0
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -0
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +13 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +11 -3
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat/use-reconnect-reader-owner.d.ts +11 -0
- package/dist/client/chat/use-reconnect-reader-owner.d.ts.map +1 -0
- package/dist/client/chat/use-reconnect-reader-owner.js +21 -0
- package/dist/client/chat/use-reconnect-reader-owner.js.map +1 -0
- package/dist/client/code-agent-chat-adapter.d.ts +17 -1
- package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/code-agent-chat-adapter.js +47 -3
- package/dist/client/code-agent-chat-adapter.js.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts +11 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.js +5 -3
- package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +25 -2
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +96 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/VoiceButton.js +21 -1
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.js +5 -0
- package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/client/conversation/code-agent-transcript.d.ts +2 -0
- package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -1
- package/dist/client/conversation/code-agent-transcript.js +3 -5
- package/dist/client/conversation/code-agent-transcript.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +7 -2
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js +5 -7
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +67 -20
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +9 -1
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/voice-provider-status.d.ts +10 -0
- package/dist/client/voice-provider-status.d.ts.map +1 -0
- package/dist/client/voice-provider-status.js +32 -0
- package/dist/client/voice-provider-status.js.map +1 -0
- package/dist/code-agents/transcript-normalizer.d.ts +29 -0
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +55 -0
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +2 -2
- package/dist/connections/catalog.js +2 -2
- package/dist/connections/catalog.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +12 -8
- package/dist/deploy/build.js.map +1 -1
- package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +25 -5
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +55 -21
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/catalog.js +2 -2
- package/dist/integrations/catalog.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +11 -0
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +5 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/observability/hosted-model-experiment.d.ts +39 -0
- package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
- package/dist/observability/hosted-model-experiment.js +90 -0
- package/dist/observability/hosted-model-experiment.js.map +1 -0
- package/dist/observability/routes.d.ts.map +1 -1
- package/dist/observability/routes.js +16 -0
- package/dist/observability/routes.js.map +1 -1
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +4 -1
- package/dist/observability/store.js.map +1 -1
- package/dist/observability/traces.d.ts +6 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +28 -2
- package/dist/observability/traces.js.map +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +19 -1
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +43 -3
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/chat/search-chats.d.ts +1 -1
- package/dist/scripts/chat/search-chats.d.ts.map +1 -1
- package/dist/scripts/chat/search-chats.js +11 -8
- package/dist/scripts/chat/search-chats.js.map +1 -1
- package/dist/secrets/register-framework-secrets.js +1 -1
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +11 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +27 -6
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.js +1 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +3 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +13 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +5 -3
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +7 -3
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +55 -21
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +2 -3
- package/dist/server/voice-providers-status.js.map +1 -1
- package/dist/shared/cache-control.d.ts +6 -6
- package/dist/shared/cache-control.d.ts.map +1 -1
- package/dist/shared/cache-control.js +3 -3
- package/dist/shared/cache-control.js.map +1 -1
- package/dist/styles/agent-native.css +1 -0
- package/dist/styles/chat-history-list.css +313 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/locales/ar-SA/template-design.mdx +13 -0
- package/docs/content/locales/de-DE/template-design.mdx +18 -0
- package/docs/content/locales/es-ES/template-design.mdx +16 -0
- package/docs/content/locales/fr-FR/template-design.mdx +17 -0
- package/docs/content/locales/hi-IN/template-design.mdx +16 -0
- package/docs/content/locales/ja-JP/template-design.mdx +17 -0
- package/docs/content/locales/ko-KR/template-design.mdx +15 -0
- package/docs/content/locales/pt-BR/template-design.mdx +16 -0
- package/docs/content/locales/zh-CN/template-design.mdx +11 -0
- package/docs/content/locales/zh-TW/template-design.mdx +12 -0
- package/docs/content/template-design.mdx +16 -0
- package/package.json +4 -2
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
|
@@ -231,12 +231,6 @@ export type PreparingActionState = {
|
|
|
231
231
|
toolEntries?: Map<string, PreparingActionEntry>;
|
|
232
232
|
};
|
|
233
233
|
|
|
234
|
-
function formatProgressBytes(bytes: number): string {
|
|
235
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
236
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
237
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
234
|
function activityProgressBytes(ev: SSEEvent): number | undefined {
|
|
241
235
|
return typeof ev.progressBytes === "number" &&
|
|
242
236
|
Number.isFinite(ev.progressBytes) &&
|
|
@@ -281,7 +275,7 @@ function preparationActivityLabel(
|
|
|
281
275
|
if (progressBytes <= 0) {
|
|
282
276
|
return `Preparing ${action}...`;
|
|
283
277
|
}
|
|
284
|
-
return `Writing ${action}
|
|
278
|
+
return `Writing ${action}...`;
|
|
285
279
|
}
|
|
286
280
|
|
|
287
281
|
function visibleActivityLabel(ev: SSEEvent, tool?: string): string {
|
|
@@ -301,16 +295,8 @@ function findPendingToolCallIndex(
|
|
|
301
295
|
// calls can be in flight simultaneously, and name-only matching would
|
|
302
296
|
// attach a result to the wrong call.
|
|
303
297
|
if (toolCallId) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (
|
|
307
|
-
part.type === "tool-call" &&
|
|
308
|
-
part.toolCallId === toolCallId &&
|
|
309
|
-
part.result === undefined
|
|
310
|
-
) {
|
|
311
|
-
return i;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
298
|
+
const exactIndex = findPendingToolCallIndexById(content, toolCallId);
|
|
299
|
+
if (exactIndex >= 0) return exactIndex;
|
|
314
300
|
// Fall through to name-matching: the start event may have arrived before
|
|
315
301
|
// the server started emitting ids (e.g. older server build), so the
|
|
316
302
|
// stored toolCallId is the locally-generated "tc_N" value rather than the
|
|
@@ -329,6 +315,76 @@ function findPendingToolCallIndex(
|
|
|
329
315
|
return -1;
|
|
330
316
|
}
|
|
331
317
|
|
|
318
|
+
function findPendingToolCallIndexById(
|
|
319
|
+
content: ContentPart[],
|
|
320
|
+
toolCallId: string,
|
|
321
|
+
): number {
|
|
322
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
323
|
+
const part = content[i];
|
|
324
|
+
if (
|
|
325
|
+
part.type === "tool-call" &&
|
|
326
|
+
part.toolCallId === toolCallId &&
|
|
327
|
+
part.result === undefined
|
|
328
|
+
) {
|
|
329
|
+
return i;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return -1;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function findOldestPendingActivityToolCallIndex(
|
|
336
|
+
content: ContentPart[],
|
|
337
|
+
toolName: string,
|
|
338
|
+
): number {
|
|
339
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
340
|
+
const part = content[i];
|
|
341
|
+
if (
|
|
342
|
+
part.type === "tool-call" &&
|
|
343
|
+
part.toolName === toolName &&
|
|
344
|
+
part.activity === true &&
|
|
345
|
+
part.result === undefined
|
|
346
|
+
) {
|
|
347
|
+
return i;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return -1;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function findPendingActivityToolCallIndex(
|
|
354
|
+
content: ContentPart[],
|
|
355
|
+
toolName: string,
|
|
356
|
+
toolCallId?: string,
|
|
357
|
+
): number {
|
|
358
|
+
if (!toolCallId) {
|
|
359
|
+
return findPendingToolCallIndex(content, toolName);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Activity events emitted while the model is assembling tool input already
|
|
363
|
+
// carry the engine's stable call id. Match that id exactly so repeated
|
|
364
|
+
// progress for one call updates one placeholder while parallel same-name
|
|
365
|
+
// calls retain separate cards.
|
|
366
|
+
const exactIndex = findPendingToolCallIndexById(content, toolCallId);
|
|
367
|
+
if (exactIndex >= 0) return exactIndex;
|
|
368
|
+
|
|
369
|
+
// Older activity events may omit the id before a later progress event adds
|
|
370
|
+
// it. Upgrade one unambiguous reader-local placeholder instead of painting a
|
|
371
|
+
// second card for the same logical call.
|
|
372
|
+
const readerLocalCandidates: number[] = [];
|
|
373
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
374
|
+
const part = content[i];
|
|
375
|
+
if (
|
|
376
|
+
part.type === "tool-call" &&
|
|
377
|
+
part.toolName === toolName &&
|
|
378
|
+
part.activity === true &&
|
|
379
|
+
part.result === undefined &&
|
|
380
|
+
/^tc_\d+$/.test(part.toolCallId)
|
|
381
|
+
) {
|
|
382
|
+
readerLocalCandidates.push(i);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return readerLocalCandidates.length === 1 ? readerLocalCandidates[0] : -1;
|
|
386
|
+
}
|
|
387
|
+
|
|
332
388
|
function findCompletedToolCallIndex(
|
|
333
389
|
content: ContentPart[],
|
|
334
390
|
toolCallId?: string,
|
|
@@ -1000,7 +1056,17 @@ export function processEvent(
|
|
|
1000
1056
|
}
|
|
1001
1057
|
if (!tool) return { action: "continue" };
|
|
1002
1058
|
|
|
1003
|
-
const pendingToolCallIndex =
|
|
1059
|
+
const pendingToolCallIndex = findPendingActivityToolCallIndex(
|
|
1060
|
+
content,
|
|
1061
|
+
tool,
|
|
1062
|
+
ev.id,
|
|
1063
|
+
);
|
|
1064
|
+
if (pendingToolCallIndex >= 0 && ev.id) {
|
|
1065
|
+
const pending = content[pendingToolCallIndex];
|
|
1066
|
+
if (pending?.type === "tool-call" && pending.toolCallId !== ev.id) {
|
|
1067
|
+
content[pendingToolCallIndex] = { ...pending, toolCallId: ev.id };
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1004
1070
|
if (pendingToolCallIndex === -1) {
|
|
1005
1071
|
// Only surface a placeholder spinner when this tool has no card yet. A
|
|
1006
1072
|
// trailing activity heartbeat that arrives after the matching tool_done
|
|
@@ -1011,12 +1077,13 @@ export function processEvent(
|
|
|
1011
1077
|
(part) =>
|
|
1012
1078
|
part.type === "tool-call" &&
|
|
1013
1079
|
part.toolName === tool &&
|
|
1014
|
-
part.result !== undefined
|
|
1080
|
+
part.result !== undefined &&
|
|
1081
|
+
(!ev.id || part.toolCallId === ev.id),
|
|
1015
1082
|
);
|
|
1016
1083
|
if (!hasCompletedSameTool) {
|
|
1017
1084
|
content.push({
|
|
1018
1085
|
type: "tool-call",
|
|
1019
|
-
toolCallId: `tc_${++toolCallCounter.value}`,
|
|
1086
|
+
toolCallId: ev.id ?? `tc_${++toolCallCounter.value}`,
|
|
1020
1087
|
toolName: tool,
|
|
1021
1088
|
argsText: "",
|
|
1022
1089
|
args: {},
|
|
@@ -1054,7 +1121,9 @@ export function processEvent(
|
|
|
1054
1121
|
}
|
|
1055
1122
|
// Pass the server-assigned id so we upgrade the pending activity card
|
|
1056
1123
|
// using id-match when available (parallel same-name calls stay separate).
|
|
1057
|
-
const pendingToolCallIndex =
|
|
1124
|
+
const pendingToolCallIndex = ev.id
|
|
1125
|
+
? findPendingActivityToolCallIndex(content, tool, ev.id)
|
|
1126
|
+
: findOldestPendingActivityToolCallIndex(content, tool);
|
|
1058
1127
|
const pendingToolCall =
|
|
1059
1128
|
pendingToolCallIndex >= 0 ? content[pendingToolCallIndex] : undefined;
|
|
1060
1129
|
const pendingIsActivityPlaceholder =
|
|
@@ -449,8 +449,17 @@ export function useChatThreads(
|
|
|
449
449
|
// haven't shown up in the server list yet — the server only learns
|
|
450
450
|
// about a thread when the user actually sends a message and the
|
|
451
451
|
// agent run's `persistSubmittedUserMessage` writes the row.
|
|
452
|
+
//
|
|
453
|
+
// Archived threads are excluded here too: the server list omits
|
|
454
|
+
// archived threads by default, so a thread we archived this same
|
|
455
|
+
// session would otherwise look identical to a not-yet-synced
|
|
456
|
+
// optimistic thread (created this session, missing from `loaded`)
|
|
457
|
+
// and get preserved forever instead of disappearing once archived.
|
|
452
458
|
const optimisticOnly = prev.filter(
|
|
453
|
-
(t) =>
|
|
459
|
+
(t) =>
|
|
460
|
+
newlyCreatedRef.current.has(t.id) &&
|
|
461
|
+
!loadedIds.has(t.id) &&
|
|
462
|
+
!t.archivedAt,
|
|
454
463
|
);
|
|
455
464
|
// Reconcile each server thread against our local copy. If the local
|
|
456
465
|
// copy has a newer updatedAt or higher messageCount, keep those
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { agentNativePath } from "./api-path.js";
|
|
4
|
+
|
|
5
|
+
export interface VoiceProviderStatus {
|
|
6
|
+
builder: boolean;
|
|
7
|
+
openai: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getVoiceProviderStatus(): Promise<VoiceProviderStatus> {
|
|
11
|
+
const response = await fetch(
|
|
12
|
+
agentNativePath("/_agent-native/voice-providers/status"),
|
|
13
|
+
{ credentials: "same-origin" },
|
|
14
|
+
);
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`Voice provider status unavailable (${response.status})`);
|
|
17
|
+
}
|
|
18
|
+
const status = (await response.json()) as Partial<VoiceProviderStatus>;
|
|
19
|
+
return { builder: status.builder === true, openai: status.openai === true };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useVoiceProviderStatus() {
|
|
23
|
+
const [status, setStatus] = useState<VoiceProviderStatus | null>(null);
|
|
24
|
+
|
|
25
|
+
const refresh = useCallback(async () => {
|
|
26
|
+
try {
|
|
27
|
+
setStatus(await getVoiceProviderStatus());
|
|
28
|
+
} catch {
|
|
29
|
+
// Keep the last known status. The session route remains authoritative.
|
|
30
|
+
}
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
void refresh();
|
|
35
|
+
window.addEventListener("focus", refresh);
|
|
36
|
+
window.addEventListener("agent-engine:configured-changed", refresh);
|
|
37
|
+
return () => {
|
|
38
|
+
window.removeEventListener("focus", refresh);
|
|
39
|
+
window.removeEventListener("agent-engine:configured-changed", refresh);
|
|
40
|
+
};
|
|
41
|
+
}, [refresh]);
|
|
42
|
+
|
|
43
|
+
return { status, refresh };
|
|
44
|
+
}
|
|
@@ -61,6 +61,16 @@ export interface NormalizedCodeAgentToolEvent extends NormalizedCodeAgentTranscr
|
|
|
61
61
|
* it. Absent on old transcript events — UI must handle both cases.
|
|
62
62
|
*/
|
|
63
63
|
structuredMeta?: Record<string, unknown>;
|
|
64
|
+
/**
|
|
65
|
+
* Stable approval id extracted from the synthetic "Approval required..."
|
|
66
|
+
* bash result (see `requestCodeAgentApproval` in `cli/code-agent-executor.ts`)
|
|
67
|
+
* when this exact approval has not yet been resolved elsewhere in the
|
|
68
|
+
* transcript (approved / denied / allowlisted / forbidden). Consumers attach
|
|
69
|
+
* this as `approval: { approvalKey }` on the rendered tool-call content part
|
|
70
|
+
* so the shared `ApprovalAffordance` can render inline. Absent once a later
|
|
71
|
+
* transcript event records a resolution for this approval id.
|
|
72
|
+
*/
|
|
73
|
+
pendingApprovalKey?: string;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
export interface NormalizedCodeAgentStatusEvent extends NormalizedCodeAgentTranscriptBase {
|
|
@@ -70,6 +80,7 @@ export interface NormalizedCodeAgentStatusEvent extends NormalizedCodeAgentTrans
|
|
|
70
80
|
statusKind: CodeAgentTranscriptEvent["kind"];
|
|
71
81
|
status?: string;
|
|
72
82
|
phase?: string;
|
|
83
|
+
signal?: CodeAgentTranscriptEvent["signal"];
|
|
73
84
|
metadata?: Record<string, unknown>;
|
|
74
85
|
}
|
|
75
86
|
|
|
@@ -82,6 +93,35 @@ export interface NormalizedCodeAgentThinkingEvent extends NormalizedCodeAgentTra
|
|
|
82
93
|
text: string;
|
|
83
94
|
}
|
|
84
95
|
|
|
96
|
+
/** Structured signal value stamped on the "no LLM provider key" status event. */
|
|
97
|
+
export const CREDENTIAL_GAP_SIGNAL: NonNullable<
|
|
98
|
+
CodeAgentTranscriptEvent["signal"]
|
|
99
|
+
> = "credential-gap";
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Shared "credential gap" detection for code-agent transcript events and the
|
|
103
|
+
* normalized status items built from them. Prefers the structured `signal`
|
|
104
|
+
* field the executor stamps on the event (see `code-agent-executor.ts`); only
|
|
105
|
+
* falls back to matching the legacy hint text for transcripts persisted
|
|
106
|
+
* before the structured signal existed. Accepts either a raw
|
|
107
|
+
* `CodeAgentTranscriptEvent` (`message`) or a `NormalizedCodeAgentStatusEvent`
|
|
108
|
+
* (`text`), and any of the other UI-facing transcript event shapes that carry
|
|
109
|
+
* the same field names, so every consumer can share one implementation
|
|
110
|
+
* instead of re-implementing the regex.
|
|
111
|
+
*/
|
|
112
|
+
export function isCredentialGapCodeAgentEvent(event: {
|
|
113
|
+
signal?: string;
|
|
114
|
+
text?: string;
|
|
115
|
+
message?: string;
|
|
116
|
+
}): boolean {
|
|
117
|
+
if (event.signal === CREDENTIAL_GAP_SIGNAL) return true;
|
|
118
|
+
return isLegacyCredentialGapHintText(event.text ?? event.message ?? "");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isLegacyCredentialGapHintText(value: string): boolean {
|
|
122
|
+
return /No LLM provider key was found|Missing credentials/i.test(value);
|
|
123
|
+
}
|
|
124
|
+
|
|
85
125
|
export function normalizeCodeAgentTranscript(
|
|
86
126
|
events: readonly CodeAgentTranscriptEvent[],
|
|
87
127
|
): NormalizedCodeAgentTranscript {
|
|
@@ -148,6 +188,7 @@ export function normalizeCodeAgentTranscript(
|
|
|
148
188
|
hiddenEvents.sort(
|
|
149
189
|
(a, b) => (eventOrder.get(a.id) ?? 0) - (eventOrder.get(b.id) ?? 0),
|
|
150
190
|
);
|
|
191
|
+
applyPendingCodeAgentApprovalKeys(dedupedItems, events);
|
|
151
192
|
|
|
152
193
|
return {
|
|
153
194
|
items: dedupedItems,
|
|
@@ -156,6 +197,40 @@ export function normalizeCodeAgentTranscript(
|
|
|
156
197
|
};
|
|
157
198
|
}
|
|
158
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Stamp `pendingApprovalKey` onto completed bash tool events whose synthetic
|
|
202
|
+
* result carries an "Approval id: {id}" marker (see `requestCodeAgentApproval`
|
|
203
|
+
* in `cli/code-agent-executor.ts`), unless a later raw event already recorded
|
|
204
|
+
* a resolution for that same id (approved / denied / allowlisted-and-run /
|
|
205
|
+
* forbidden — all stamp `metadata.approvalId`).
|
|
206
|
+
*
|
|
207
|
+
* Resolution lookup scans the *raw* event stream rather than the normalized
|
|
208
|
+
* items: resolution status events are intentionally low-signal (they read as
|
|
209
|
+
* "status: running") and get folded into `hiddenEvents` by
|
|
210
|
+
* `isLowSignalLifecycleEvent`, so they would not otherwise be visible here.
|
|
211
|
+
*/
|
|
212
|
+
function applyPendingCodeAgentApprovalKeys(
|
|
213
|
+
items: NormalizedCodeAgentTranscriptItem[],
|
|
214
|
+
events: readonly CodeAgentTranscriptEvent[],
|
|
215
|
+
): void {
|
|
216
|
+
const resolvedApprovalIds = new Set<string>();
|
|
217
|
+
for (const event of events) {
|
|
218
|
+
const approvalId = stringMetadata(event.metadata, "approvalId");
|
|
219
|
+
if (approvalId) resolvedApprovalIds.add(approvalId);
|
|
220
|
+
}
|
|
221
|
+
for (const item of items) {
|
|
222
|
+
if (item.type !== "tool" || item.state !== "completed") continue;
|
|
223
|
+
if (typeof item.result !== "string") continue;
|
|
224
|
+
const match = CODE_AGENT_APPROVAL_ID_RESULT_PATTERN.exec(item.result);
|
|
225
|
+
const id = match?.[1];
|
|
226
|
+
if (id && !resolvedApprovalIds.has(id)) {
|
|
227
|
+
item.pendingApprovalKey = id;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const CODE_AGENT_APPROVAL_ID_RESULT_PATTERN = /Approval id:\s*(\S+)/;
|
|
233
|
+
|
|
159
234
|
function createUserTurn(
|
|
160
235
|
event: CodeAgentTranscriptEvent,
|
|
161
236
|
turnIndex: number,
|
|
@@ -253,6 +328,7 @@ function createStatusEvent(
|
|
|
253
328
|
statusKind: event.kind,
|
|
254
329
|
status: stringMetadata(metadata, "status"),
|
|
255
330
|
phase: stringMetadata(metadata, "phase"),
|
|
331
|
+
signal: event.signal,
|
|
256
332
|
metadata,
|
|
257
333
|
createdAt: event.createdAt,
|
|
258
334
|
updatedAt: event.createdAt,
|
|
@@ -66,9 +66,9 @@ export const WORKSPACE_CONNECTION_PROVIDERS = [
|
|
|
66
66
|
credentialKeys: [
|
|
67
67
|
{
|
|
68
68
|
key: "SLACK_BOT_TOKEN",
|
|
69
|
-
label: "Slack bot token",
|
|
69
|
+
label: "Slack bot token (legacy)",
|
|
70
70
|
description:
|
|
71
|
-
"
|
|
71
|
+
"Legacy single-workspace fallback. For new messaging automations, connect Slack from Settings → Messaging instead.",
|
|
72
72
|
required: true,
|
|
73
73
|
},
|
|
74
74
|
],
|
|
@@ -984,17 +984,21 @@ function isSsrHtmlOrDataResponse(headers, status, pathname) {
|
|
|
984
984
|
/**
|
|
985
985
|
* Apply the SSR cache policy to the response headers.
|
|
986
986
|
*
|
|
987
|
-
* SSR
|
|
988
|
-
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
* so it is safe to hard-cache one shared copy at the edge. Do NOT reintroduce
|
|
992
|
-
* per-user / cookie-based cache variation here (no private, no no-store, no
|
|
993
|
-
* "authenticated then don't cache" branch) — that makes every logged-in
|
|
994
|
-
* visitor's pages uncacheable. Per-user state is resolved client-side instead.
|
|
987
|
+
* Unannotated SSR HTML and React Router .data responses share a public
|
|
988
|
+
* stale-while-revalidate cache policy. Preserve an explicit route policy,
|
|
989
|
+
* though: token-gated and personalized routes can opt out with private or
|
|
990
|
+
* no-store policies.
|
|
995
991
|
*/
|
|
996
992
|
function applyDefaultSsrCacheHeader(headers, status, pathname) {
|
|
997
993
|
if (!isSsrHtmlOrDataResponse(headers, status, pathname)) return;
|
|
994
|
+
// React Router marks every .data response as no-cache, but those loader
|
|
995
|
+
// payloads are safe to cache at the shared edge. Preserve only explicit
|
|
996
|
+
// private/no-store policies, which token-gated and personalized routes use
|
|
997
|
+
// to opt out of shared caching.
|
|
998
|
+
const cacheControl = headers.get("cache-control")?.toLowerCase() ?? "";
|
|
999
|
+
if (cacheControl.includes("private") || cacheControl.includes("no-store")) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
998
1002
|
|
|
999
1003
|
headers.set("cache-control", DEFAULT_SSR_CACHE_CONTROL);
|
|
1000
1004
|
headers.set("cdn-cache-control", DEFAULT_SSR_CDN_CACHE_CONTROL);
|
|
@@ -4,6 +4,7 @@ import type { Task } from "../a2a/types.js";
|
|
|
4
4
|
import {
|
|
5
5
|
formatLlmCredentialErrorMessage,
|
|
6
6
|
isLlmCredentialError,
|
|
7
|
+
LLM_MISSING_CREDENTIALS_ERROR_CODE,
|
|
7
8
|
} from "../agent/engine/credential-errors.js";
|
|
8
9
|
import { withConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
9
10
|
import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
|
|
@@ -457,15 +458,44 @@ function formatContinuationFailureMessage(
|
|
|
457
458
|
continuation: A2AContinuation,
|
|
458
459
|
reason: string,
|
|
459
460
|
): string {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
461
|
+
const explicitCode = extractFailureCode(reason);
|
|
462
|
+
const diagnostics = formatContinuationFailureDiagnostics(
|
|
463
|
+
continuation,
|
|
464
|
+
reason,
|
|
465
|
+
);
|
|
466
|
+
if (isLlmCredentialError(reason, explicitCode)) {
|
|
467
|
+
return (
|
|
468
|
+
formatLlmCredentialErrorMessage({
|
|
469
|
+
agentName: continuation.agentName,
|
|
470
|
+
}) + diagnostics
|
|
471
|
+
);
|
|
464
472
|
}
|
|
465
473
|
|
|
466
474
|
return `The ${continuation.agentName} agent could not finish this request: ${sanitizeFailureReason(
|
|
467
475
|
reason,
|
|
468
|
-
)}`;
|
|
476
|
+
)}${diagnostics}`;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function formatContinuationFailureDiagnostics(
|
|
480
|
+
continuation: A2AContinuation,
|
|
481
|
+
reason: string,
|
|
482
|
+
): string {
|
|
483
|
+
return `\n\nError code: \`${continuationFailureCode(reason)}\`\nRequest ID: \`${continuation.integrationTaskId}\`\nContinuation ID: \`${continuation.id}\`\nDownstream task ID: \`${continuation.a2aTaskId}\``;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function continuationFailureCode(reason: string): string {
|
|
487
|
+
const explicitCode = extractFailureCode(reason);
|
|
488
|
+
if (explicitCode) return explicitCode;
|
|
489
|
+
if (isLlmCredentialError(reason, explicitCode)) {
|
|
490
|
+
return LLM_MISSING_CREDENTIALS_ERROR_CODE;
|
|
491
|
+
}
|
|
492
|
+
if (/\btimed out polling\b/i.test(reason)) return "a2a_remote_timeout";
|
|
493
|
+
return "a2a_downstream_error";
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function extractFailureCode(reason: string): string | null {
|
|
497
|
+
const match = /\bcode\s*[:=]\s*[`"']?([a-z][a-z0-9_]{0,79})\b/i.exec(reason);
|
|
498
|
+
return match?.[1]?.toLowerCase() ?? null;
|
|
469
499
|
}
|
|
470
500
|
|
|
471
501
|
function isRemoteWorkExpired(continuation: A2AContinuation): boolean {
|
|
@@ -37,6 +37,7 @@ const SLACK_API_TIMEOUT_MS = 10_000;
|
|
|
37
37
|
const SLACK_PERMALINK_TIMEOUT_MS = 1_000;
|
|
38
38
|
const SLACK_CONTEXT_MESSAGE_LIMIT = 15;
|
|
39
39
|
const SLACK_CONTEXT_TEXT_LIMIT = 2_000;
|
|
40
|
+
const SLACK_CALM_PROGRESS_THRESHOLD_SECONDS = 30;
|
|
40
41
|
|
|
41
42
|
export interface SlackAdapterOptions {
|
|
42
43
|
/** Resolve the bot token for the exact Slack installation. */
|
|
@@ -323,12 +324,10 @@ export function slackAdapter(
|
|
|
323
324
|
async postProcessingPlaceholder(
|
|
324
325
|
incoming: IncomingMessage,
|
|
325
326
|
): Promise<{ placeholderRef: string } | null> {
|
|
326
|
-
// No placeholder reply in the thread — Slack's native assistant
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
// We just set the native status and return null so sendResponse posts
|
|
331
|
-
// the final reply as a fresh message.
|
|
327
|
+
// No placeholder reply in the thread — Slack's native assistant status
|
|
328
|
+
// bar and the task stream are the loading affordance. Keep the status
|
|
329
|
+
// specific about intent instead of presenting the generic thinking
|
|
330
|
+
// state while the native plan is opening.
|
|
332
331
|
const token = await resolveBotToken(incoming);
|
|
333
332
|
if (!token) return null;
|
|
334
333
|
|
|
@@ -336,11 +335,16 @@ export function slackAdapter(
|
|
|
336
335
|
const threadTs = incoming.platformContext.threadTs as string;
|
|
337
336
|
if (!channelId || !threadTs) return null;
|
|
338
337
|
|
|
339
|
-
// Best-effort: flip the native Agent
|
|
338
|
+
// Best-effort: flip the native Agent status bar in the
|
|
340
339
|
// channel input area. Slack accepts chat:write for this method. The
|
|
341
340
|
// canonical manifest separately requests assistant:write for Agent View
|
|
342
341
|
// and app_context_changed events.
|
|
343
|
-
setSlackAssistantStatus(
|
|
342
|
+
setSlackAssistantStatus(
|
|
343
|
+
token,
|
|
344
|
+
channelId,
|
|
345
|
+
threadTs,
|
|
346
|
+
"I’m looking into this now…",
|
|
347
|
+
);
|
|
344
348
|
return null;
|
|
345
349
|
},
|
|
346
350
|
|
|
@@ -1266,6 +1270,42 @@ function shortTaskTitle(value: unknown): string {
|
|
|
1266
1270
|
return text.replace(/[-_]/g, " ").slice(0, 200);
|
|
1267
1271
|
}
|
|
1268
1272
|
|
|
1273
|
+
function delegatedTaskTitle(agent: unknown): string {
|
|
1274
|
+
return `Contact ${shortTaskTitle(agent)}`;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
function formatElapsedSeconds(elapsedSeconds: number): string {
|
|
1278
|
+
const totalSeconds = Math.max(0, Math.round(elapsedSeconds));
|
|
1279
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
1280
|
+
const seconds = totalSeconds % 60;
|
|
1281
|
+
return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
function formatProgressState(state: string): string {
|
|
1285
|
+
if (state === "submitted") return "Queued";
|
|
1286
|
+
if (state === "working") return "Working";
|
|
1287
|
+
return shortTaskTitle(state);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function delegatedProgressDetails(
|
|
1291
|
+
agent: unknown,
|
|
1292
|
+
state: string,
|
|
1293
|
+
elapsedSeconds: number,
|
|
1294
|
+
detail?: string,
|
|
1295
|
+
): string {
|
|
1296
|
+
const elapsed = formatElapsedSeconds(elapsedSeconds);
|
|
1297
|
+
const progress = `${formatProgressState(state)} · ${elapsed}`;
|
|
1298
|
+
const update = detail ? shortTaskTitle(detail) : "";
|
|
1299
|
+
|
|
1300
|
+
if (elapsedSeconds >= SLACK_CALM_PROGRESS_THRESHOLD_SECONDS) {
|
|
1301
|
+
const agentName = shortTaskTitle(agent);
|
|
1302
|
+
const context = update ? ` — ${update}.` : ".";
|
|
1303
|
+
return `${progress}${context} This is taking longer than usual, but ${agentName} is still working. I’ll post the result here.`;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
return update ? `${progress} — ${update}` : progress;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1269
1309
|
async function postSlackJson(
|
|
1270
1310
|
token: string,
|
|
1271
1311
|
method: string,
|
|
@@ -1315,18 +1355,18 @@ async function startSlackRunProgress(
|
|
|
1315
1355
|
...(incoming.tenantId ? { recipient_team_id: incoming.tenantId } : {}),
|
|
1316
1356
|
...(incoming.senderId ? { recipient_user_id: incoming.senderId } : {}),
|
|
1317
1357
|
task_display_mode: "plan",
|
|
1318
|
-
markdown_text: "
|
|
1358
|
+
markdown_text: "I’m looking into this for you.",
|
|
1319
1359
|
chunks: [
|
|
1320
1360
|
{
|
|
1321
1361
|
type: "plan_update",
|
|
1322
|
-
title: "
|
|
1362
|
+
title: "I’m looking into this for you",
|
|
1323
1363
|
},
|
|
1324
1364
|
{
|
|
1325
1365
|
type: "task_update",
|
|
1326
1366
|
id: "agent-native:context",
|
|
1327
|
-
title: "
|
|
1367
|
+
title: "Review the request",
|
|
1328
1368
|
status: "in_progress",
|
|
1329
|
-
details: "
|
|
1369
|
+
details: "Finding the information needed for an answer",
|
|
1330
1370
|
},
|
|
1331
1371
|
],
|
|
1332
1372
|
});
|
|
@@ -1373,7 +1413,7 @@ function createSlackRunProgress(
|
|
|
1373
1413
|
const toolTaskIds = new Map<string, string>();
|
|
1374
1414
|
const agentTaskIds = new Map<string, string>();
|
|
1375
1415
|
tasks.set("agent-native:context", {
|
|
1376
|
-
title: "
|
|
1416
|
+
title: "Review the request",
|
|
1377
1417
|
status: "in_progress",
|
|
1378
1418
|
});
|
|
1379
1419
|
let sequence = 0;
|
|
@@ -1540,9 +1580,19 @@ function createSlackRunProgress(
|
|
|
1540
1580
|
: event.status === "done"
|
|
1541
1581
|
? "complete"
|
|
1542
1582
|
: "error";
|
|
1543
|
-
const title =
|
|
1583
|
+
const title = delegatedTaskTitle(event.agent);
|
|
1544
1584
|
tasks.set(id, { title, status });
|
|
1545
|
-
await append({
|
|
1585
|
+
await append({
|
|
1586
|
+
type: "task_update",
|
|
1587
|
+
id,
|
|
1588
|
+
title,
|
|
1589
|
+
status,
|
|
1590
|
+
...(event.status === "start"
|
|
1591
|
+
? {
|
|
1592
|
+
details: `I’m contacting ${shortTaskTitle(event.agent)} for an answer.`,
|
|
1593
|
+
}
|
|
1594
|
+
: {}),
|
|
1595
|
+
});
|
|
1546
1596
|
} else if (event.type === "agent_call_progress") {
|
|
1547
1597
|
// A2A calls can stay healthy for minutes. Keep the same native Slack
|
|
1548
1598
|
// task card alive with each real downstream poll rather than creating
|
|
@@ -1550,10 +1600,13 @@ function createSlackRunProgress(
|
|
|
1550
1600
|
const id =
|
|
1551
1601
|
agentTaskIds.get(event.agent) ?? taskId("agent", event.agent);
|
|
1552
1602
|
agentTaskIds.set(event.agent, id);
|
|
1553
|
-
const title =
|
|
1554
|
-
const details =
|
|
1555
|
-
|
|
1556
|
-
|
|
1603
|
+
const title = delegatedTaskTitle(event.agent);
|
|
1604
|
+
const details = delegatedProgressDetails(
|
|
1605
|
+
event.agent,
|
|
1606
|
+
event.state,
|
|
1607
|
+
event.elapsedSeconds,
|
|
1608
|
+
event.detail,
|
|
1609
|
+
);
|
|
1557
1610
|
tasks.set(id, { title, status: "in_progress" });
|
|
1558
1611
|
await append({
|
|
1559
1612
|
type: "task_update",
|
|
@@ -1566,9 +1619,9 @@ function createSlackRunProgress(
|
|
|
1566
1619
|
await append({
|
|
1567
1620
|
type: "task_update",
|
|
1568
1621
|
id: "agent-native:context",
|
|
1569
|
-
title: "
|
|
1622
|
+
title: "Review the request",
|
|
1570
1623
|
status: "in_progress",
|
|
1571
|
-
details: shortTaskTitle(event.label)
|
|
1624
|
+
details: `Working · ${shortTaskTitle(event.label)}`,
|
|
1572
1625
|
});
|
|
1573
1626
|
}
|
|
1574
1627
|
},
|
|
@@ -137,10 +137,10 @@ const BUILT_IN_CHANNEL_CATALOG = [
|
|
|
137
137
|
credentialRequirements: [
|
|
138
138
|
{
|
|
139
139
|
key: "SLACK_BOT_TOKEN",
|
|
140
|
-
label: "Slack Bot Token",
|
|
140
|
+
label: "Slack Bot Token (legacy)",
|
|
141
141
|
required: false,
|
|
142
142
|
helpText:
|
|
143
|
-
"
|
|
143
|
+
"Legacy single-workspace fallback only. For new setups, install Slack from Settings → Messaging; managed OAuth stores each workspace token automatically.",
|
|
144
144
|
},
|
|
145
145
|
{
|
|
146
146
|
key: "SLACK_CLIENT_ID",
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
normalizeModelForEngine,
|
|
16
16
|
resolveEngine,
|
|
17
17
|
} from "../agent/engine/index.js";
|
|
18
|
+
import { resolveMainChatMaxOutputTokens } from "../agent/engine/output-tokens.js";
|
|
18
19
|
import { PROVIDER_TO_ENV } from "../agent/engine/provider-env-vars.js";
|
|
19
20
|
import type { AgentEngine, EngineMessage } from "../agent/engine/types.js";
|
|
20
21
|
import {
|
|
@@ -45,6 +46,7 @@ import {
|
|
|
45
46
|
readDeployCredentialEnv,
|
|
46
47
|
} from "../server/credential-provider.js";
|
|
47
48
|
import { runWithRequestContext } from "../server/request-context.js";
|
|
49
|
+
import { normalizeReasoningEffortForRequest } from "../shared/reasoning-effort.js";
|
|
48
50
|
import { A2A_CONTINUATION_QUEUED_MARKER } from "./a2a-continuation-marker.js";
|
|
49
51
|
import {
|
|
50
52
|
clearIntegrationAwaitingInput,
|
|
@@ -866,6 +868,18 @@ async function processIncomingMessage(
|
|
|
866
868
|
signal,
|
|
867
869
|
threadId,
|
|
868
870
|
approvedToolCalls: incoming.approvedToolCalls,
|
|
871
|
+
// Messaging integrations are interactive chat surfaces. They
|
|
872
|
+
// need the same initial completion headroom as web chat so
|
|
873
|
+
// reasoning cannot consume the small per-engine default and
|
|
874
|
+
// leave a user-facing Slack reply empty.
|
|
875
|
+
maxOutputTokens: resolveMainChatMaxOutputTokens(resolvedModel),
|
|
876
|
+
// Explicitly resolve the normal chat default so an empty-final
|
|
877
|
+
// retry can step its reasoning effort down rather than
|
|
878
|
+
// repeatedly letting the engine choose Medium.
|
|
879
|
+
reasoningEffort: normalizeReasoningEffortForRequest(
|
|
880
|
+
resolvedModel,
|
|
881
|
+
undefined,
|
|
882
|
+
),
|
|
869
883
|
});
|
|
870
884
|
return usage;
|
|
871
885
|
},
|