@vellumai/assistant 0.11.7 → 0.11.8-staging.1
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/ARCHITECTURE.md +10 -10
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +1 -1
- package/docs/error-handling.md +1 -2
- package/docs/guardian-request-flow.md +51 -5
- package/docs/trusted-contact-access.md +13 -12
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-catalog/package.json +19 -0
- package/{src/avatar/character-components.ts → node_modules/@vellumai/avatar-catalog/src/catalog.ts} +12 -44
- package/node_modules/@vellumai/avatar-catalog/src/colors.ts +19 -0
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +25 -0
- package/node_modules/@vellumai/avatar-catalog/src/types.ts +47 -0
- package/node_modules/@vellumai/avatar-catalog/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +43 -12
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/test-path-guard.test.ts +89 -0
- package/node_modules/@vellumai/environments/src/test-path-guard.ts +113 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +8 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +62 -21
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +53 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +11 -5
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +1 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/openapi.yaml +481 -9
- package/package.json +3 -1
- package/scripts/sync-llm-catalog.ts +3 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +16 -0
- package/src/__tests__/agent-loop-compacted-history-transform.test.ts +304 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +3 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +3 -0
- package/src/__tests__/anthropic-provider.test.ts +56 -0
- package/src/__tests__/approval-cascade.test.ts +0 -13
- package/src/__tests__/attachments-store.test.ts +117 -6
- package/src/__tests__/attachments.test.ts +122 -0
- package/src/__tests__/bundled-asset.test.ts +22 -1
- package/src/__tests__/channel-approval.test.ts +14 -7
- package/src/__tests__/channel-approvals.test.ts +3 -3
- package/src/__tests__/channel-delivery-store.test.ts +58 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -0
- package/src/__tests__/channel-invite-transport.test.ts +1 -0
- package/src/__tests__/channel-readiness-slack-remote.test.ts +3 -3
- package/src/__tests__/compaction-events.test.ts +0 -12
- package/src/__tests__/compactor-call-site-logging.test.ts +7 -1
- package/src/__tests__/compactor-retained-image-validation.test.ts +15 -0
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/contacts-write.test.ts +141 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -12
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop.test.ts +95 -13
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -13
- package/src/__tests__/conversation-load-history-repair.test.ts +418 -0
- package/src/__tests__/conversation-media-retry.test.ts +33 -2
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -12
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -13
- package/src/__tests__/conversation-queue.test.ts +4 -13
- package/src/__tests__/conversation-retry-route.test.ts +5 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-sight-frames.test.ts +615 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -13
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -13
- package/src/__tests__/conversation-speed-override.test.ts +0 -12
- package/src/__tests__/conversation-store-ephemeral.test.ts +0 -12
- package/src/__tests__/conversation-summarize-route.test.ts +5 -0
- package/src/__tests__/conversation-summarize-up-to.test.ts +102 -0
- package/src/__tests__/conversation-wait-for-idle.test.ts +0 -12
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -17
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -17
- package/src/__tests__/credential-health-service.test.ts +45 -0
- package/src/__tests__/credential-record-retire.test.ts +256 -0
- package/src/__tests__/credential-record-write-through.test.ts +8 -4
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +0 -4
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +0 -4
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +0 -8
- package/src/__tests__/delete-propagation.test.ts +121 -3
- package/src/__tests__/evict-conversations-for-reload.test.ts +46 -6
- package/src/__tests__/file-ops-service.test.ts +8 -3
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/guardian-action-service-scope.test.ts +118 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +299 -5
- package/src/__tests__/guardian-expiry-notifier.test.ts +117 -13
- package/src/__tests__/guardian-gateway-sim.test.ts +119 -0
- package/src/__tests__/guardian-gateway-sim.ts +56 -20
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +6 -0
- package/src/__tests__/guardian-request-status-sync.test.ts +162 -0
- package/src/__tests__/guardian-wait-controller.test.ts +6 -0
- package/src/__tests__/headless-browser-mode.test.ts +27 -0
- package/src/__tests__/helpers/mock-conversation.ts +2 -0
- package/src/__tests__/list-messages-system-card.test.ts +70 -0
- package/src/__tests__/llm-catalog-parity.test.ts +2 -0
- package/src/__tests__/managed-profile-guard.test.ts +2 -2
- package/src/__tests__/media-resolve-image-validation.test.ts +106 -0
- package/src/__tests__/media-stream-server-integration.test.ts +6 -0
- package/src/__tests__/non-member-access-request.test.ts +12 -0
- package/src/__tests__/notification-discord-adapter.test.ts +76 -4
- package/src/__tests__/oauth-commands-routes.test.ts +21 -0
- package/src/__tests__/openai-provider.test.ts +63 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +114 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +63 -1
- package/src/__tests__/persist-unsendable-image.test.ts +19 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +2 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -1
- package/src/__tests__/process-message-display-content.test.ts +1 -1
- package/src/__tests__/processing-flag-persist-failure.test.ts +0 -5
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +0 -17
- package/src/__tests__/qdrant-manager.test.ts +6 -0
- package/src/__tests__/reaction-persistence.test.ts +58 -62
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +67 -0
- package/src/__tests__/server-history-render.test.ts +22 -0
- package/src/__tests__/sight-frame-retention.test.ts +362 -0
- package/src/__tests__/slack-reaction-approvals.test.ts +44 -24
- package/src/__tests__/slack-reaction-guardian-approval.test.ts +29 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +85 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +4 -4
- package/src/__tests__/terminal-tools.test.ts +9 -9
- package/src/__tests__/tool-result-spool.test.ts +5 -8
- package/src/__tests__/usage-attribution.test.ts +26 -0
- package/src/acp/__tests__/acp-auth-marker-store.test.ts +191 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +262 -0
- package/src/acp/__tests__/acp-connect-card-state.test.ts +17 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +10 -3
- package/src/acp/acp-auth-marker-store.ts +196 -0
- package/src/acp/acp-claude-oauth.ts +154 -4
- package/src/acp/acp-connect-card-state.ts +49 -8
- package/src/acp/agent-process.ts +1 -0
- package/src/acp/auto-install.ts +1 -0
- package/src/acp/client-handler.ts +1 -0
- package/src/acp/prepare-agent-env.ts +79 -1
- package/src/acp/resolve-agent.ts +12 -0
- package/src/acp/session-manager.test.ts +183 -0
- package/src/acp/session-manager.ts +110 -6
- package/src/acp/types.ts +17 -0
- package/src/agent/attachments.ts +7 -2
- package/src/agent/loop.ts +59 -1
- package/src/api/events/contact-form-closed.ts +32 -0
- package/src/api/events/contact-record-request.ts +63 -0
- package/src/api/events/contact-request.ts +10 -3
- package/src/api/index.ts +20 -0
- package/src/api/responses/conversation-message.ts +20 -0
- package/src/api/responses/home.ts +101 -0
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-card-withdrawal.ts +127 -30
- package/src/approvals/guardian-decision-primitive.ts +9 -0
- package/src/approvals/guardian-expiry-notifier.ts +22 -16
- package/src/approvals/guardian-request-status-sync.ts +98 -0
- package/src/avatar/svg-compositor.ts +1 -1
- package/src/avatar/traits-png-sync.ts +1 -1
- package/src/bundler/app-compiler.ts +1 -0
- package/src/bundler/compiler-tools.ts +1 -0
- package/src/bundler/package-resolver.ts +1 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +183 -3
- package/src/calls/guardian-action-sweep.ts +6 -1
- package/src/calls/voice-session-bridge.ts +46 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +13 -18
- package/src/channels/gateway-guardian-requests.ts +37 -18
- package/src/cli/__tests__/contacts-record-prompt-cli.test.ts +329 -0
- package/src/cli/commands/__tests__/browser.test.ts +65 -1
- package/src/cli/commands/browser.ts +30 -10
- package/src/cli/commands/contacts.help.ts +173 -11
- package/src/cli/commands/contacts.ts +343 -4
- package/src/cli/commands/db/refresh.ts +1 -0
- package/src/cli/commands/oauth/index.help.ts +6 -3
- package/src/cli/commands/oauth/request.test.ts +37 -0
- package/src/cli/commands/oauth/request.ts +7 -6
- package/src/cli/lib/bundled-marketplace.json +1 -1
- package/src/cli/lib/install-from-github.ts +20 -19
- package/src/cli/lib/install-plugin-dependencies.ts +3 -5
- package/src/cli/lib/merge-plugin-tree.ts +1 -0
- package/src/cli/lib/publish-plugin.ts +4 -1
- package/src/config/__tests__/balanced-model-experiment.test.ts +63 -31
- package/src/config/__tests__/default-profile-catalog.test.ts +2 -2
- package/src/config/balanced-model-experiment.ts +15 -9
- package/src/config/bundled-skills/contacts/SKILL.md +61 -18
- package/src/config/default-profile-catalog.ts +12 -11
- package/src/config/feature-flag-registry.json +22 -13
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +13 -0
- package/src/config/schemas/live-voice.ts +7 -0
- package/src/config/schemas/sight.ts +45 -0
- package/src/config/skills.ts +2 -1
- package/src/contacts/__tests__/mirror-reconciler.test.ts +352 -0
- package/src/contacts/contact-store.ts +47 -4
- package/src/contacts/contacts-write.ts +5 -2
- package/src/contacts/mirror-reconciler.ts +210 -0
- package/src/contacts/mirror-tombstones.ts +42 -0
- package/src/contacts/notify-contacts-changed.ts +11 -2
- package/src/context/compactor.ts +47 -19
- package/src/context/tool-result-spool.ts +21 -14
- package/src/conversations/message-consolidation.ts +2 -2
- package/src/credential-health/credential-health-service.ts +37 -6
- package/src/daemon/conversation-agent-loop-handlers.ts +19 -2
- package/src/daemon/conversation-agent-loop.ts +100 -1
- package/src/daemon/conversation-media-retry.ts +11 -6
- package/src/daemon/conversation-messaging.ts +293 -30
- package/src/daemon/conversation-runtime-assembly.ts +67 -0
- package/src/daemon/conversation-sight-frames.ts +187 -0
- package/src/daemon/conversation-store.ts +18 -28
- package/src/daemon/conversation-surfaces.ts +12 -2
- package/src/daemon/conversation-tool-setup.ts +39 -10
- package/src/daemon/conversation.ts +159 -16
- package/src/daemon/daemon-control.ts +6 -17
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +10 -11
- package/src/daemon/install-assistant-command.ts +1 -0
- package/src/daemon/lifecycle.ts +10 -13
- package/src/daemon/message-types/sync.ts +4 -0
- package/src/daemon/process-message.ts +2 -17
- package/src/daemon/reaction-history-render.test.ts +162 -0
- package/src/daemon/reaction-history-render.ts +115 -0
- package/src/daemon/reaction-record.test.ts +162 -0
- package/src/daemon/reaction-record.ts +83 -0
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/summarize-boundary.ts +12 -0
- package/src/daemon/video-thumbnail.ts +1 -1
- package/src/home/feed-types.ts +4 -0
- package/src/home/feed-writer.ts +25 -0
- package/src/live-voice/__tests__/live-voice-attach-frame.test.ts +679 -0
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +8 -5
- package/src/live-voice/__tests__/live-voice-photo.test.ts +637 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +787 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +347 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +109 -0
- package/src/live-voice/live-voice-photo.ts +420 -65
- package/src/live-voice/live-voice-session.ts +311 -27
- package/src/live-voice/protocol.ts +147 -11
- package/src/messaging/provider-message-metadata.ts +7 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +113 -1
- package/src/messaging/providers/channel-transport.ts +37 -0
- package/src/messaging/providers/discord/api.ts +4 -4
- package/src/messaging/providers/discord/send.test.ts +177 -2
- package/src/messaging/providers/discord/send.ts +197 -10
- package/src/messaging/providers/discord/transport.ts +10 -0
- package/src/messaging/providers/index.ts +42 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +129 -3
- package/src/messaging/providers/slack/__tests__/auth.test.ts +35 -0
- package/src/messaging/providers/slack/__tests__/message-content.test.ts +152 -0
- package/src/messaging/providers/slack/adapter.ts +112 -27
- package/src/messaging/providers/slack/api.test.ts +10 -0
- package/src/messaging/providers/slack/auth.ts +32 -6
- package/src/messaging/providers/slack/client.ts +0 -15
- package/src/messaging/providers/slack/message-content.ts +137 -0
- package/src/messaging/providers/slack/send.test.ts +43 -2
- package/src/messaging/providers/slack/send.ts +18 -5
- package/src/messaging/providers/slack/transport.ts +10 -0
- package/src/messaging/providers/slack/types.ts +21 -2
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -1
- package/src/messaging/providers/telegram-bot/send.test.ts +49 -2
- package/src/messaging/providers/telegram-bot/send.ts +41 -1
- package/src/messaging/providers/telegram-bot/transport.ts +10 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +13 -5
- package/src/messaging/reaction-envelopes.ts +79 -0
- package/src/monitoring/control.ts +2 -2
- package/src/monitoring/page-cache.ts +5 -1
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +360 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +8 -0
- package/src/notifications/adapters/discord.ts +62 -9
- package/src/notifications/conversation-pairing.ts +31 -4
- package/src/notifications/emit-signal.ts +8 -5
- package/src/notifications/guardian-delivery-recorder.ts +47 -6
- package/src/notifications/guardian-feed-projection.ts +492 -0
- package/src/notifications/home-feed-side-effect.ts +59 -11
- package/src/oauth/byo-connection.test.ts +22 -0
- package/src/oauth/byo-connection.ts +11 -5
- package/src/oauth/connection.ts +3 -2
- package/src/oauth/platform-connection.test.ts +35 -0
- package/src/oauth/platform-connection.ts +18 -13
- package/src/oauth/scope-utils.ts +48 -0
- package/src/oauth/seed-providers.ts +12 -0
- package/src/outbound-proxy/certs.ts +5 -4
- package/src/permissions/question-guardian-request.test.ts +7 -1
- package/src/persistence/attachments-store.ts +134 -31
- package/src/persistence/conversation-crud.ts +137 -4
- package/src/persistence/conversation-types.ts +109 -0
- package/src/persistence/db-connection.ts +3 -21
- package/src/persistence/delivery-crud.ts +42 -12
- package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +15 -2
- package/src/persistence/embeddings/embedding-local.ts +7 -112
- package/src/persistence/embeddings/qdrant-manager.ts +4 -1
- package/src/persistence/llm-request-log-store.ts +1 -1
- package/src/persistence/migrations/370-acp-session-history-auth-error-code.ts +36 -0
- package/src/persistence/migrations/371-acp-session-history-auth-error-credential.ts +40 -0
- package/src/persistence/migrations/372-create-acp-refused-credentials.ts +34 -0
- package/src/persistence/migrations/373-acp-auth-marker-index.ts +27 -0
- package/src/persistence/migrations/__tests__/370-acp-session-history-auth-error-code.test.ts +65 -0
- package/src/persistence/migrations/__tests__/371-acp-session-history-auth-error-credential.test.ts +68 -0
- package/src/persistence/migrations/__tests__/372-create-acp-refused-credentials.test.ts +64 -0
- package/src/persistence/migrations/__tests__/373-acp-auth-marker-index.test.ts +82 -0
- package/src/persistence/schema/acp.ts +25 -0
- package/src/persistence/steps.ts +8 -0
- package/src/platform/platform-patch-queue.test.ts +43 -41
- package/src/plugin-api/index.ts +12 -0
- package/src/plugins/defaults/compaction/window-manager.ts +2 -0
- package/src/plugins/defaults/image-recovery/recover.ts +12 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +166 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +3 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +3 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +53 -9
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +4 -7
- package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
- package/src/plugins/defaults/memory/v1/job-handlers/index-maintenance.ts +15 -1
- package/src/plugins/defaults/memory/worker-control.ts +1 -2
- package/src/providers/content-block-schema.ts +1 -0
- package/src/providers/fireworks/client.ts +9 -1
- package/src/providers/gemini/client.ts +22 -15
- package/src/providers/media-resolve.ts +24 -10
- package/src/providers/model-catalog.ts +91 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -5
- package/src/providers/retry.ts +16 -11
- package/src/providers/types.ts +56 -0
- package/src/routes/__tests__/control.test.ts +4 -3
- package/src/routes/control.ts +1 -1
- package/src/routes/route-host-client.ts +1 -1
- package/src/runtime/AGENTS.md +30 -12
- package/src/runtime/__tests__/agent-wake.test.ts +71 -6
- package/src/runtime/__tests__/guardian-form-registry.test.ts +242 -0
- package/src/runtime/access-request-helper.ts +15 -7
- package/src/runtime/agent-wake.ts +16 -4
- package/src/runtime/channel-approval-types.ts +8 -5
- package/src/runtime/channel-invite-transport.ts +2 -0
- package/src/runtime/channel-invite-transports/discord.ts +27 -0
- package/src/runtime/channel-readiness-service.ts +4 -4
- package/src/runtime/channel-reply-delivery.ts +72 -28
- package/src/runtime/guardian-action-service.ts +9 -5
- package/src/runtime/guardian-form-registry.ts +256 -0
- package/src/runtime/guardian-reply-router.ts +33 -14
- package/src/runtime/invite-instruction-generator.ts +14 -2
- package/src/runtime/migrations/vbundle-metadata-merge.ts +4 -7
- package/src/runtime/no-response.ts +13 -74
- package/src/runtime/routes/__tests__/acp-routes.test.ts +527 -0
- package/src/runtime/routes/__tests__/app-management-shared-apps.test.ts +74 -0
- package/src/runtime/routes/__tests__/contact-form-kinds.test.ts +22 -0
- package/src/runtime/routes/__tests__/contact-record-prompt-route.test.ts +303 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +6 -0
- package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +5 -6
- package/src/runtime/routes/acp-routes-list.test.ts +58 -0
- package/src/runtime/routes/acp-routes.ts +253 -47
- package/src/runtime/routes/app-management-routes.ts +80 -47
- package/src/runtime/routes/approval-routes.ts +9 -0
- package/src/runtime/routes/archive-utils.ts +6 -1
- package/src/runtime/routes/avatar-routes.ts +1 -1
- package/src/runtime/routes/channel-route-shared.ts +18 -20
- package/src/runtime/routes/contact-prompt-routes.ts +271 -105
- package/src/runtime/routes/conversation-routes.ts +41 -32
- package/src/runtime/routes/credential-prompt-routes.ts +10 -1
- package/src/runtime/routes/debug-bash-routes.ts +1 -0
- package/src/runtime/routes/guardian-expiry-sweep.ts +89 -31
- package/src/runtime/routes/guardian-form-routes.ts +115 -0
- package/src/runtime/routes/inbound-message-handler.ts +82 -29
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +6 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +50 -17
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +97 -94
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/channel.ts +39 -18
- package/src/runtime/routes/migration-routes.ts +1 -3
- package/src/runtime/routes/monitoring-routes.ts +0 -3
- package/src/runtime/routes/oauth-commands-routes.ts +18 -5
- package/src/runtime/routes/route-host-worker-routes.ts +1 -1
- package/src/runtime/routes/schedule-worker-routes.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +4 -0
- package/src/schedule/run-script.ts +1 -0
- package/src/schedule/scheduler.ts +1 -1
- package/src/schedule/worker-control.ts +2 -2
- package/src/security/oauth2.ts +2 -11
- package/src/security/secure-keys.ts +55 -8
- package/src/skills/catalog-install.ts +7 -4
- package/src/skills/clawhub.ts +1 -0
- package/src/skills/inline-command-runner.ts +1 -0
- package/src/stt/__tests__/room-noise-floor.test.ts +108 -0
- package/src/stt/room-noise-floor.ts +140 -0
- package/src/subagent/notify.ts +49 -3
- package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
- package/src/tools/acp/spawn.ts +2 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +9 -0
- package/src/tools/browser/browser-execution.ts +10 -43
- package/src/tools/browser/browser-manager.ts +1 -0
- package/src/tools/browser/browser-status-constants.ts +6 -5
- package/src/tools/browser/runtime-check.ts +2 -0
- package/src/tools/channel/react-to-message.test.ts +193 -0
- package/src/tools/channel/react-to-message.ts +159 -0
- package/src/tools/credentials/metadata-store.ts +115 -19
- package/src/tools/credentials/store.ts +0 -2
- package/src/tools/filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +11 -38
- package/src/tools/shared/filesystem/file-ops-service.ts +10 -7
- package/src/tools/shared/shell-output.test.ts +104 -0
- package/src/tools/shared/shell-output.ts +112 -39
- package/src/tools/skills/sandbox-runner.ts +1 -0
- package/src/tools/system/request-permission.test.ts +34 -0
- package/src/tools/system/request-permission.ts +29 -5
- package/src/tools/terminal/shell.test.ts +63 -0
- package/src/tools/terminal/shell.ts +12 -44
- package/src/tools/tool-input-schemas.ts +2 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/usage/attribution.ts +12 -4
- package/src/util/__tests__/worker-ownership.test.ts +77 -0
- package/src/util/__tests__/worker-process-command.test.ts +246 -1
- package/src/util/bun-runtime.ts +44 -13
- package/src/util/bundled-asset.ts +9 -1
- package/src/util/disk-usage.ts +1 -0
- package/src/util/file-use.ts +4 -1
- package/src/util/guardian-form-timeouts.ts +50 -0
- package/src/util/image-conversion.ts +1 -0
- package/src/util/oauth-request-body.test.ts +59 -0
- package/src/util/oauth-request-body.ts +56 -5
- package/src/util/platform.ts +69 -73
- package/src/util/process-table.ts +16 -5
- package/src/util/read-stdin.ts +5 -0
- package/src/util/spawn.ts +1 -0
- package/src/util/sqlite3-runtime.ts +25 -5
- package/src/util/worker-ownership.ts +193 -0
- package/src/util/worker-process.ts +375 -37
- package/src/workspace/git-service.ts +4 -12
- package/src/workspace/migrations/010-app-dir-rename.ts +1 -0
|
@@ -59,6 +59,12 @@ export interface CatalogModel {
|
|
|
59
59
|
* `adaptiveThinkingOnly`.
|
|
60
60
|
*/
|
|
61
61
|
adaptiveThinkingUnsupported?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Lowest thinking level the model accepts on the Gemini wire. `"low"`
|
|
64
|
+
* means `"minimal"` is not a valid request. Omit when `"minimal"` is
|
|
65
|
+
* accepted.
|
|
66
|
+
*/
|
|
67
|
+
thinkingFloor?: "minimal" | "low";
|
|
62
68
|
supportsCaching?: boolean;
|
|
63
69
|
supportsVision?: boolean;
|
|
64
70
|
/**
|
|
@@ -77,6 +83,16 @@ export interface CatalogModel {
|
|
|
77
83
|
* default.
|
|
78
84
|
*/
|
|
79
85
|
maxEffort?: "high" | "xhigh" | "max";
|
|
86
|
+
/**
|
|
87
|
+
* Wire `reasoning_effort` values this model's upstream accepts, for models
|
|
88
|
+
* whose accepted set is sparse rather than a contiguous range under
|
|
89
|
+
* `maxEffort` (e.g. GLM 5.3 accepts only low/high/max). After the
|
|
90
|
+
* `maxEffort` ceiling clamp, provider clients snap an unsupported value
|
|
91
|
+
* down to the nearest listed value ("none" is exempt: it is the explicit
|
|
92
|
+
* opt-out and keeps its own rejection handling). Daemon-only: not
|
|
93
|
+
* projected into the client catalog (see scripts/sync-llm-catalog.ts).
|
|
94
|
+
*/
|
|
95
|
+
supportedEfforts?: readonly ("low" | "medium" | "high" | "xhigh" | "max")[];
|
|
80
96
|
/**
|
|
81
97
|
* Daemon-only: when true, the direct-OpenAI Responses transport sends
|
|
82
98
|
* explicit prompt-cache breakpoints for this model (GPT-5.6+ semantics:
|
|
@@ -597,6 +613,22 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
597
613
|
linkLabel: "Open Google AI Studio",
|
|
598
614
|
},
|
|
599
615
|
models: [
|
|
616
|
+
{
|
|
617
|
+
id: "gemini-3.7-flash",
|
|
618
|
+
displayName: "Gemini 3.7 Flash",
|
|
619
|
+
contextWindowTokens: 1048576,
|
|
620
|
+
maxOutputTokens: 65536,
|
|
621
|
+
supportsThinking: true,
|
|
622
|
+
thinkingFloor: "low",
|
|
623
|
+
supportsCaching: true,
|
|
624
|
+
supportsVision: true,
|
|
625
|
+
supportsToolUse: true,
|
|
626
|
+
pricing: {
|
|
627
|
+
inputPer1mTokens: 1.5,
|
|
628
|
+
outputPer1mTokens: 7.5,
|
|
629
|
+
cacheReadPer1mTokens: 0.15,
|
|
630
|
+
},
|
|
631
|
+
},
|
|
600
632
|
{
|
|
601
633
|
id: "gemini-3.6-flash",
|
|
602
634
|
displayName: "Gemini 3.6 Flash",
|
|
@@ -649,6 +681,7 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
649
681
|
maxOutputTokens: 65536,
|
|
650
682
|
longContextPricingThresholdTokens: 200000,
|
|
651
683
|
supportsThinking: true,
|
|
684
|
+
thinkingFloor: "low",
|
|
652
685
|
supportsCaching: true,
|
|
653
686
|
supportsVision: true,
|
|
654
687
|
supportsToolUse: true,
|
|
@@ -673,6 +706,7 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
673
706
|
maxOutputTokens: 65536,
|
|
674
707
|
longContextPricingThresholdTokens: 200000,
|
|
675
708
|
supportsThinking: true,
|
|
709
|
+
thinkingFloor: "low",
|
|
676
710
|
supportsCaching: true,
|
|
677
711
|
supportsVision: true,
|
|
678
712
|
supportsToolUse: true,
|
|
@@ -893,6 +927,45 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
893
927
|
cacheReadPer1mTokens: 0.26,
|
|
894
928
|
},
|
|
895
929
|
},
|
|
930
|
+
{
|
|
931
|
+
id: "accounts/fireworks/models/glm-5p3",
|
|
932
|
+
displayName: "GLM 5.3",
|
|
933
|
+
contextWindowTokens: 1040000,
|
|
934
|
+
maxOutputTokens: 131072,
|
|
935
|
+
supportsThinking: true,
|
|
936
|
+
// GLM 5.3 reasoning is always on (reasoning_effort low/high/max);
|
|
937
|
+
// it cannot be disabled upstream.
|
|
938
|
+
adaptiveThinkingOnly: true,
|
|
939
|
+
supportsCaching: true,
|
|
940
|
+
supportsVision: false,
|
|
941
|
+
supportsToolUse: true,
|
|
942
|
+
maxEffort: "max",
|
|
943
|
+
supportedEfforts: ["low", "high", "max"],
|
|
944
|
+
pricing: {
|
|
945
|
+
inputPer1mTokens: 1.4,
|
|
946
|
+
outputPer1mTokens: 4.4,
|
|
947
|
+
cacheReadPer1mTokens: 0.26,
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
id: "accounts/fireworks/models/glm-5p3-flash",
|
|
952
|
+
displayName: "GLM 5.3 Flash",
|
|
953
|
+
contextWindowTokens: 1040000,
|
|
954
|
+
maxOutputTokens: 131072,
|
|
955
|
+
supportsThinking: true,
|
|
956
|
+
// Same always-on reasoning as GLM 5.3.
|
|
957
|
+
adaptiveThinkingOnly: true,
|
|
958
|
+
supportsCaching: true,
|
|
959
|
+
supportsVision: true,
|
|
960
|
+
supportsToolUse: true,
|
|
961
|
+
maxEffort: "max",
|
|
962
|
+
supportedEfforts: ["low", "high", "max"],
|
|
963
|
+
pricing: {
|
|
964
|
+
inputPer1mTokens: 0.15,
|
|
965
|
+
outputPer1mTokens: 0.5,
|
|
966
|
+
cacheReadPer1mTokens: 0.029,
|
|
967
|
+
},
|
|
968
|
+
},
|
|
896
969
|
// Kimi K2.5 (accounts/fireworks/models/kimi-k2p5) is intentionally
|
|
897
970
|
// absent: Fireworks serves it on-demand/dedicated only, so serverless
|
|
898
971
|
// chat/completions calls 404 ("not found, inaccessible, and/or not
|
|
@@ -2366,6 +2439,24 @@ export function modelEffortCeilings(
|
|
|
2366
2439
|
);
|
|
2367
2440
|
}
|
|
2368
2441
|
|
|
2442
|
+
/**
|
|
2443
|
+
* Per-model sparse `reasoning_effort` support for a provider, keyed by model
|
|
2444
|
+
* ID (same derivation pattern as {@link modelEffortCeilings}). Models without
|
|
2445
|
+
* `supportedEfforts` are absent and accept any value under their ceiling.
|
|
2446
|
+
*/
|
|
2447
|
+
export function modelSupportedEfforts(
|
|
2448
|
+
providerId: string,
|
|
2449
|
+
): ReadonlyMap<
|
|
2450
|
+
string,
|
|
2451
|
+
readonly ("low" | "medium" | "high" | "xhigh" | "max")[]
|
|
2452
|
+
> {
|
|
2453
|
+
return new Map(
|
|
2454
|
+
PROVIDER_CATALOG.find((p) => p.id === providerId)?.models.flatMap((m) =>
|
|
2455
|
+
m.supportedEfforts ? ([[m.id, m.supportedEfforts]] as const) : [],
|
|
2456
|
+
) ?? [],
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2369
2460
|
/**
|
|
2370
2461
|
* Return the catalog provider that owns a model ID, if known. When multiple
|
|
2371
2462
|
* providers list the same ID (e.g. OpenRouter and the Vercel AI Gateway share
|
|
@@ -225,6 +225,28 @@ export function clampReasoningEffort(
|
|
|
225
225
|
: value;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
/** Snap a wire effort onto a model's sparse accepted set: the nearest
|
|
229
|
+
* supported value at or below it, or the smallest supported value when the
|
|
230
|
+
* request sits below all of them. Models like GLM 5.3 accept only
|
|
231
|
+
* `low|high|max` and 4xx on the in-between tiers instead of rounding, so the
|
|
232
|
+
* client rounds for them. `"none"` is never snapped: it is the explicit
|
|
233
|
+
* opt-out and has its own rejection retry. */
|
|
234
|
+
export function snapReasoningEffortToSupported(
|
|
235
|
+
value: ReasoningEffortWire,
|
|
236
|
+
supported: readonly ReasoningEffortWire[],
|
|
237
|
+
): ReasoningEffortWire {
|
|
238
|
+
if (value === "none" || supported.includes(value)) {
|
|
239
|
+
return value;
|
|
240
|
+
}
|
|
241
|
+
const ranked = [...supported].sort(
|
|
242
|
+
(a, b) => REASONING_EFFORT_RANK[a] - REASONING_EFFORT_RANK[b],
|
|
243
|
+
);
|
|
244
|
+
const atOrBelow = ranked.filter(
|
|
245
|
+
(s) => REASONING_EFFORT_RANK[s] < REASONING_EFFORT_RANK[value],
|
|
246
|
+
);
|
|
247
|
+
return atOrBelow[atOrBelow.length - 1] ?? ranked[0] ?? value;
|
|
248
|
+
}
|
|
249
|
+
|
|
228
250
|
/** Human-readable text from an OpenAI-compatible error, including wrapped
|
|
229
251
|
* upstream detail (OpenRouter `metadata.raw`). Used by the one-shot
|
|
230
252
|
* compatibility retries so a generic SDK wrapper message cannot hide the
|
|
@@ -781,12 +803,16 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
781
803
|
? EFFORT_TO_REASONING_EFFORT[effort]
|
|
782
804
|
: undefined;
|
|
783
805
|
if (reasoningEffort && typeof nestedReasoningEffort !== "string") {
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
);
|
|
787
|
-
params.reasoning_effort = clampReasoningEffort(
|
|
806
|
+
const effortModel = modelOverride ?? this.model;
|
|
807
|
+
const clamped = clampReasoningEffort(
|
|
788
808
|
reasoningEffort,
|
|
789
|
-
|
|
809
|
+
this.resolveMaxReasoningEffort(effortModel),
|
|
810
|
+
);
|
|
811
|
+
const supported = this.resolveSupportedReasoningEfforts(effortModel);
|
|
812
|
+
params.reasoning_effort = (
|
|
813
|
+
supported
|
|
814
|
+
? snapReasoningEffortToSupported(clamped, supported)
|
|
815
|
+
: clamped
|
|
790
816
|
) as OpenAI.Chat.Completions.ChatCompletionCreateParams["reasoning_effort"];
|
|
791
817
|
}
|
|
792
818
|
|
|
@@ -1352,6 +1378,19 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
1352
1378
|
return this.maxReasoningEffort;
|
|
1353
1379
|
}
|
|
1354
1380
|
|
|
1381
|
+
/**
|
|
1382
|
+
* Per-model sparse `reasoning_effort` support. Subclasses return the
|
|
1383
|
+
* accepted values for models that 4xx on in-between tiers (e.g. GLM 5.3's
|
|
1384
|
+
* `low|high|max`); the outbound value is snapped onto that set after the
|
|
1385
|
+
* ceiling clamp (see {@link snapReasoningEffortToSupported}). Undefined
|
|
1386
|
+
* means any value under the ceiling is accepted.
|
|
1387
|
+
*/
|
|
1388
|
+
protected resolveSupportedReasoningEfforts(
|
|
1389
|
+
_model: string,
|
|
1390
|
+
): readonly ReasoningEffortWire[] | undefined {
|
|
1391
|
+
return undefined;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1355
1394
|
/** Convert neutral messages + system prompt to OpenAI message format. */
|
|
1356
1395
|
private async toOpenAIMessages(
|
|
1357
1396
|
messages: Message[],
|
package/src/providers/retry.ts
CHANGED
|
@@ -813,17 +813,15 @@ function normalizeSendMessageOptions(
|
|
|
813
813
|
}
|
|
814
814
|
}
|
|
815
815
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
//
|
|
824
|
-
//
|
|
825
|
-
// disabled thinking config for these models so they fall back to their
|
|
826
|
-
// always-on adaptive thinking; effort and other params are unaffected.
|
|
816
|
+
// Adaptive-thinking-only models (Claude Fable, Fireworks Kimi K3 / GLM 5.3)
|
|
817
|
+
// always reason and reject an explicit opt-out: Anthropic 400s
|
|
818
|
+
// `thinking: { type: "disabled" }`, and effort-encoding providers 4xx a
|
|
819
|
+
// generated `reasoning_effort: "none"`. Drop a disabled thinking config for
|
|
820
|
+
// these models BEFORE the opt-out is encoded as `effort: "none"` below, so
|
|
821
|
+
// they fall back to their always-on thinking with the user's chosen effort
|
|
822
|
+
// and other params unaffected. Profiles can retain a stale
|
|
823
|
+
// `thinking: { enabled: false }` after switching to such a model, so this
|
|
824
|
+
// state is reachable from the profile editor.
|
|
827
825
|
if (
|
|
828
826
|
typeof nextConfig.model === "string" &&
|
|
829
827
|
isAdaptiveThinkingOnlyModel(nextConfig.model) &&
|
|
@@ -832,6 +830,13 @@ function normalizeSendMessageOptions(
|
|
|
832
830
|
delete nextConfig.thinking;
|
|
833
831
|
}
|
|
834
832
|
|
|
833
|
+
if (
|
|
834
|
+
isThinkingConfigDisabled(nextConfig.thinking) &&
|
|
835
|
+
disabledThinkingForcesEffortNone(providerName, nextConfig.model)
|
|
836
|
+
) {
|
|
837
|
+
nextConfig.effort = "none";
|
|
838
|
+
}
|
|
839
|
+
|
|
835
840
|
// Pre-adaptive Claude models (Haiku 4.5, Opus 4.5, Sonnet 4.5) reject
|
|
836
841
|
// `thinking: { type: "adaptive" }` (Anthropic 400s the request), and Vellum
|
|
837
842
|
// never sends the legacy budget_tokens form. Drop an adaptive thinking
|
package/src/providers/types.ts
CHANGED
|
@@ -80,6 +80,19 @@ export type MediaSource = Base64MediaSource | WorkspaceRefMediaSource;
|
|
|
80
80
|
export interface ImageContent {
|
|
81
81
|
type: "image";
|
|
82
82
|
source: MediaSource;
|
|
83
|
+
/**
|
|
84
|
+
* Internal id linking a base64 image block to a row in the attachments table,
|
|
85
|
+
* the same correlation {@link FileContent._attachmentId} carries. A live turn
|
|
86
|
+
* sends its uploads inline while persisting them as references, so this is
|
|
87
|
+
* the only handle on the in-memory copy of an image whose stored form is a
|
|
88
|
+
* `workspace_ref`; camera-frame retention matches on it. Redundant once the
|
|
89
|
+
* block is a reference (use `source.attachmentId`).
|
|
90
|
+
*
|
|
91
|
+
* Never reaches a provider: every client builds its image payload out of
|
|
92
|
+
* `source` alone (see the `case "image"` arms of the Anthropic, Gemini, and
|
|
93
|
+
* both OpenAI clients), so top-level fields are dropped by construction.
|
|
94
|
+
*/
|
|
95
|
+
_attachmentId?: string;
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
export interface FileContent {
|
|
@@ -97,6 +110,49 @@ export interface FileContent {
|
|
|
97
110
|
_attachmentId?: string;
|
|
98
111
|
}
|
|
99
112
|
|
|
113
|
+
/**
|
|
114
|
+
* The `_attachmentId` property as a spreadable fragment, or nothing when there
|
|
115
|
+
* is no usable id.
|
|
116
|
+
*
|
|
117
|
+
* Every producer of a media block faces the same choice: it either knows the
|
|
118
|
+
* attachment row the block came from, or it does not, and the field has to be
|
|
119
|
+
* absent rather than present-and-empty in the second case. This helper is the
|
|
120
|
+
* one place that decision lives, so every producer answers it the same way.
|
|
121
|
+
* Callers pass whatever id they hold: the source block's own
|
|
122
|
+
* ({@link ImageContent._attachmentId}) when carrying it across a rebuild, or an
|
|
123
|
+
* upload's row id when stamping a fresh block.
|
|
124
|
+
*
|
|
125
|
+
* Empty counts as absent, matching the read side, which requires a non-empty
|
|
126
|
+
* string before it will treat the field as an id (see
|
|
127
|
+
* `daemon/handlers/shared.ts`).
|
|
128
|
+
*/
|
|
129
|
+
export function attachmentIdFragment(attachmentId: string | undefined): {
|
|
130
|
+
_attachmentId?: string;
|
|
131
|
+
} {
|
|
132
|
+
return attachmentId ? { _attachmentId: attachmentId } : {};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The attachment row a media block came from, whichever of the two shapes it
|
|
137
|
+
* is in, or undefined for a block that came from none (tool-generated and
|
|
138
|
+
* assistant-authored media).
|
|
139
|
+
*
|
|
140
|
+
* A reference names its row on `source.attachmentId`; an inline block has
|
|
141
|
+
* nowhere to put it but the top-level `_attachmentId`. Both are the same fact,
|
|
142
|
+
* so anything asking "which attachment is this?" has to read both or it goes
|
|
143
|
+
* blind on half the histories. Reading only `_attachmentId` in particular is
|
|
144
|
+
* the sharp edge: it is absent on every reference block, so a rebuild that
|
|
145
|
+
* flattens a reference to inline bytes drops the link unless it derives the id
|
|
146
|
+
* through here first.
|
|
147
|
+
*/
|
|
148
|
+
export function mediaBlockAttachmentId(
|
|
149
|
+
block: ImageContent | FileContent,
|
|
150
|
+
): string | undefined {
|
|
151
|
+
return block.source.type === "workspace_ref"
|
|
152
|
+
? block.source.attachmentId
|
|
153
|
+
: block._attachmentId;
|
|
154
|
+
}
|
|
155
|
+
|
|
100
156
|
export interface ToolUseContent {
|
|
101
157
|
type: "tool_use";
|
|
102
158
|
id: string;
|
|
@@ -14,7 +14,7 @@ import * as realLogger from "../../util/logger.js";
|
|
|
14
14
|
import * as realWorkerProcess from "../../util/worker-process.js";
|
|
15
15
|
|
|
16
16
|
let enabled = false;
|
|
17
|
-
let spawnArgs: Array<{ options?: {
|
|
17
|
+
let spawnArgs: Array<{ options?: { terminateOnTimeout?: boolean } }> = [];
|
|
18
18
|
let stopStatus: { status: "running" | "not_running"; pid?: number } = {
|
|
19
19
|
status: "not_running",
|
|
20
20
|
};
|
|
@@ -39,7 +39,9 @@ mock.module("../../util/logger.js", () => ({
|
|
|
39
39
|
|
|
40
40
|
mock.module("../../util/worker-process.js", () => ({
|
|
41
41
|
...realWorkerProcess,
|
|
42
|
-
spawnWorkerProcess: async (args: {
|
|
42
|
+
spawnWorkerProcess: async (args: {
|
|
43
|
+
options?: { terminateOnTimeout?: boolean };
|
|
44
|
+
}) => {
|
|
43
45
|
spawnArgs.push(args);
|
|
44
46
|
return { pid: 4242, alreadyRunning: false };
|
|
45
47
|
},
|
|
@@ -76,7 +78,6 @@ describe("startRouteHost", () => {
|
|
|
76
78
|
startRouteHost();
|
|
77
79
|
await Promise.resolve();
|
|
78
80
|
expect(spawnArgs).toHaveLength(1);
|
|
79
|
-
expect(spawnArgs[0].options?.detached).toBe(false);
|
|
80
81
|
});
|
|
81
82
|
});
|
|
82
83
|
|
package/src/routes/control.ts
CHANGED
|
@@ -101,7 +101,7 @@ export function startRouteHost(): void {
|
|
|
101
101
|
if (!isRouteHostEnabled()) {
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
|
-
void spawnRouteHostWorkerProcess(
|
|
104
|
+
void spawnRouteHostWorkerProcess()
|
|
105
105
|
.then((r) =>
|
|
106
106
|
log.info(
|
|
107
107
|
{ pid: r.pid, alreadyRunning: r.alreadyRunning },
|
|
@@ -153,7 +153,7 @@ export class RouteHostClient {
|
|
|
153
153
|
workerLabel: "Route host",
|
|
154
154
|
// Owned by the daemon (appears in its process tree, torn down with it);
|
|
155
155
|
// kill it if it hangs during startup so a failed spawn leaves nothing.
|
|
156
|
-
options: {
|
|
156
|
+
options: { terminateOnTimeout: true },
|
|
157
157
|
}));
|
|
158
158
|
} catch (err) {
|
|
159
159
|
// A failed bring-up (crash-on-startup, PID-file timeout) is a retryable
|
package/src/runtime/AGENTS.md
CHANGED
|
@@ -156,7 +156,7 @@ All CDP-backed browser tools (`browser_navigate`, `browser_snapshot`, `browser_s
|
|
|
156
156
|
|
|
157
157
|
**Auto-mode fallback logging**: In auto mode, fallback transitions are logged at `warn` level with structured metadata including the full candidate sequence and per-candidate failure reasons. This ensures fallback events are always observable in production logs.
|
|
158
158
|
|
|
159
|
-
**Test coverage:** Regression tests for `browser_mode` wiring live in `__tests__/headless-browser-mode.test.ts`.
|
|
159
|
+
**Test coverage:** Regression tests for `browser_mode` wiring live in `__tests__/headless-browser-mode.test.ts`. Route-wiring coverage for the macOS fallback path lives in `__tests__/conversation-routes-disk-view.test.ts`, which asserts a macOS turn carries its interface context rather than exercising the backends themselves. Unit tests for pinned candidate construction and failover live in `tools/browser/cdp-client/__tests__/factory.test.ts`. Browser status tests covering macOS host-browser diagnostics live in `tools/browser/__tests__/browser-status.test.ts`.
|
|
160
160
|
|
|
161
161
|
### Interactive requests on channels (approvals, questions)
|
|
162
162
|
|
|
@@ -190,18 +190,36 @@ the gateway's Channel Identity Vocabulary, which covers the wire side.
|
|
|
190
190
|
- **`slackMeta`** is the per-row key describing what a row is in its Slack
|
|
191
191
|
conversation: `channelTs` for the row's own id, `threadTs` for its thread,
|
|
192
192
|
`reaction.targetChannelTs` for the message a reaction was attached to, plus
|
|
193
|
-
Slack's own file markers and timezone labels.
|
|
194
|
-
through it
|
|
193
|
+
Slack's own file markers and timezone labels. Most channel-path readers
|
|
194
|
+
still go through it directly; the lookups in `persistence/delivery-crud.ts`
|
|
195
|
+
read through `readProviderMetadata` instead, which serves this envelope and
|
|
196
|
+
the neutral shape below from one call.
|
|
195
197
|
- **`providerMeta`** is the channel-neutral counterpart of that key
|
|
196
|
-
(`messaging/provider-message-metadata.ts`): `
|
|
197
|
-
`messageId`, `threadId`, `actorExternalId`,
|
|
198
|
-
`
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
rather than
|
|
204
|
-
|
|
198
|
+
(`messaging/provider-message-metadata.ts`): `source`,
|
|
199
|
+
`conversationExternalId`, `messageId`, `threadId`, `actorExternalId`,
|
|
200
|
+
`eventKind`, the `reaction` sub-key, and the `editedAt` / `deletedAt`
|
|
201
|
+
marks. Deliberately the same vocabulary the inbound wire uses in
|
|
202
|
+
`InboundEventBase`, so a channel that can describe itself at ingress can
|
|
203
|
+
describe its stored rows without a translation of its own. Writable by any
|
|
204
|
+
channel, including one this repo has no code for, which is why a new
|
|
205
|
+
channel belongs here rather than in a sixth key of its own.
|
|
206
|
+
|
|
207
|
+
Every channel except Slack writes it: a reaction row carries the whole
|
|
208
|
+
shape (`inbound-stages/reaction-intercept.ts`), an edit or a delete
|
|
209
|
+
stamps `editedAt` / `deletedAt` onto whatever the row already said about
|
|
210
|
+
itself through `mergeProviderMessageMetadata`
|
|
211
|
+
(`inbound-stages/edit-intercept.ts`, `inbound-message-handler.ts`), and an
|
|
212
|
+
outbound assistant reply is stamped with a partial envelope at reserve time
|
|
213
|
+
(`buildAssistantChannelMetadata`) whose `messageId` (and, for a reply
|
|
214
|
+
split into several posts, `additionalMessageIds`) the post-send
|
|
215
|
+
reconciliation in `channel-reply-delivery.ts` back-fills from the
|
|
216
|
+
transport's delivery results, which is what lets a later reaction on the
|
|
217
|
+
assistant's own post resolve back to its row. Slack
|
|
218
|
+
keeps writing `slackMeta`, and `readProviderMetadata` maps that envelope
|
|
219
|
+
onto this shape on read, so the channel-agnostic readers in
|
|
220
|
+
`persistence/delivery-crud.ts` (thread evidence, and finding the
|
|
221
|
+
conversation that holds a given provider message id) serve both without a
|
|
222
|
+
per-channel branch.
|
|
205
223
|
|
|
206
224
|
### Channel verification: gateway-owned
|
|
207
225
|
|
|
@@ -347,6 +347,7 @@ import {
|
|
|
347
347
|
deleteConversation,
|
|
348
348
|
setConversation,
|
|
349
349
|
} from "../../daemon/conversation-registry.js";
|
|
350
|
+
import { stripAgedSightFrames } from "../../daemon/conversation-sight-frames.js";
|
|
350
351
|
import { ContextOverflowError, type Message } from "../../providers/types.js";
|
|
351
352
|
import {
|
|
352
353
|
__resetWakeChainForTests,
|
|
@@ -521,6 +522,11 @@ function makeWakeConversation(options: {
|
|
|
521
522
|
return runBody(input, onEvent, options);
|
|
522
523
|
},
|
|
523
524
|
},
|
|
525
|
+
// The wake trims its own run input, the way the orchestrator's pre-run
|
|
526
|
+
// pass trims a normal turn's. Empty capture times leave the array
|
|
527
|
+
// untouched, which is what every case but the camera one wants.
|
|
528
|
+
trimAgedSightFrames: (msgs: Message[]) =>
|
|
529
|
+
stripAgedSightFrames(msgs, wakeSightFrameCaptureTimes).messages,
|
|
524
530
|
messages,
|
|
525
531
|
getMessages: () => messages,
|
|
526
532
|
isProcessing: () => processing,
|
|
@@ -605,8 +611,12 @@ function makeWakeConversation(options: {
|
|
|
605
611
|
return conversation as unknown as WakeConversation;
|
|
606
612
|
}
|
|
607
613
|
|
|
614
|
+
/** Tagged camera frames the fake conversation's trim resolves against. */
|
|
615
|
+
let wakeSightFrameCaptureTimes = new Map<string, number>();
|
|
616
|
+
|
|
608
617
|
beforeEach(() => {
|
|
609
618
|
__resetWakeChainForTests();
|
|
619
|
+
wakeSightFrameCaptureTimes = new Map();
|
|
610
620
|
wakeConvRegistry.clear();
|
|
611
621
|
recordRequestLogCalls.length = 0;
|
|
612
622
|
recordUsageCalls.length = 0;
|
|
@@ -934,6 +944,62 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
934
944
|
expect(conversation.personaOverrideSets).toEqual([]);
|
|
935
945
|
});
|
|
936
946
|
|
|
947
|
+
test("bounds camera frames in the run input it sends", async () => {
|
|
948
|
+
// The wake sends its run input itself and keeps the in-loop budget gate
|
|
949
|
+
// disabled, so neither the orchestrator's pre-run pass nor the loop's
|
|
950
|
+
// post-compaction transform ever sees this array. Without its own trim a
|
|
951
|
+
// long camera session would send every frame it captured.
|
|
952
|
+
wakeSightFrameCaptureTimes = new Map([
|
|
953
|
+
["f1", 1000],
|
|
954
|
+
["f2", 2000],
|
|
955
|
+
["f3", 3000],
|
|
956
|
+
["f4", 4000],
|
|
957
|
+
]);
|
|
958
|
+
const frame = (attachmentId: string): Message => ({
|
|
959
|
+
role: "user",
|
|
960
|
+
content: [
|
|
961
|
+
{
|
|
962
|
+
type: "image",
|
|
963
|
+
source: { type: "base64", media_type: "image/png", data: "AAAAAAAA" },
|
|
964
|
+
_attachmentId: attachmentId,
|
|
965
|
+
},
|
|
966
|
+
],
|
|
967
|
+
});
|
|
968
|
+
const conversation = makeWakeConversation({
|
|
969
|
+
baseline: [frame("f1"), frame("f2"), frame("f3"), frame("f4")],
|
|
970
|
+
scriptedAssistant: {
|
|
971
|
+
role: "assistant",
|
|
972
|
+
content: [{ type: "text", text: "" }],
|
|
973
|
+
},
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
await wakeAgentForOpportunity(
|
|
977
|
+
{
|
|
978
|
+
conversationId: conversation.conversationId,
|
|
979
|
+
hint: "look around",
|
|
980
|
+
source: "unit-test",
|
|
981
|
+
},
|
|
982
|
+
{ resolveTarget: async () => conversation },
|
|
983
|
+
);
|
|
984
|
+
|
|
985
|
+
const input = conversation.runCalls[0]!.input;
|
|
986
|
+
const images = input.flatMap((message) =>
|
|
987
|
+
message.content.filter((block) => block.type === "image"),
|
|
988
|
+
);
|
|
989
|
+
const stubs = input.flatMap((message) =>
|
|
990
|
+
message.content.filter(
|
|
991
|
+
(block) =>
|
|
992
|
+
block.type === "text" &&
|
|
993
|
+
block.text.startsWith("[Camera frame omitted from context:"),
|
|
994
|
+
),
|
|
995
|
+
);
|
|
996
|
+
expect(images).toHaveLength(2);
|
|
997
|
+
expect(stubs).toHaveLength(2);
|
|
998
|
+
// The trim replaces blocks in place, so the wake's tail accounting still
|
|
999
|
+
// indexes the same positions.
|
|
1000
|
+
expect(input.filter((m) => m.role === "user").length).toBeGreaterThan(0);
|
|
1001
|
+
});
|
|
1002
|
+
|
|
937
1003
|
test("silent no-op when agent produces no tool calls and no text", async () => {
|
|
938
1004
|
const conversation = makeWakeConversation({
|
|
939
1005
|
baseline: [
|
|
@@ -1092,10 +1158,10 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1092
1158
|
},
|
|
1093
1159
|
});
|
|
1094
1160
|
|
|
1095
|
-
// Mirror formatShellOutput on large output: ~20KB body + a
|
|
1096
|
-
//
|
|
1097
|
-
//
|
|
1098
|
-
const marker = '<output_truncated limit="20K"
|
|
1161
|
+
// Mirror formatShellOutput on large output: ~20KB body + a truncation
|
|
1162
|
+
// marker. The default tool_result budget would slice the marker off; the
|
|
1163
|
+
// caller passes a larger maxChars.
|
|
1164
|
+
const marker = '<output_truncated limit="20K" />';
|
|
1099
1165
|
const big = `${"x".repeat(20_000)}\n${marker}`;
|
|
1100
1166
|
|
|
1101
1167
|
await wakeAgentForOpportunity(
|
|
@@ -1116,8 +1182,7 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1116
1182
|
const text = (
|
|
1117
1183
|
conversation.persistedTailCalls[0]!.content as Array<{ text: string }>
|
|
1118
1184
|
)[0]!.text;
|
|
1119
|
-
// The trailing
|
|
1120
|
-
// fence is still well-formed.
|
|
1185
|
+
// The trailing truncation marker survives and the fence is well-formed.
|
|
1121
1186
|
expect(text).toContain(marker);
|
|
1122
1187
|
expect(text).toContain('<external_content source="tool_result">');
|
|
1123
1188
|
expect(text.endsWith("</background_event>")).toBe(true);
|