@vellumai/assistant 0.12.1 → 0.12.2-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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The directory walk behind GET /v1/workspace/tree.
|
|
3
|
+
*
|
|
4
|
+
* One directory by default. With `recursive`, the whole subtree in one
|
|
5
|
+
* response, so a client can search or expand without a request per folder.
|
|
6
|
+
* The walk yields the event loop between directories and is bounded by an
|
|
7
|
+
* entry cap and a deadline; it does not descend into the runtime-state
|
|
8
|
+
* directories the workspace's own gitignore rules name.
|
|
9
|
+
*/
|
|
10
|
+
import type { Dirent, Stats } from "node:fs";
|
|
11
|
+
import { readdir, stat } from "node:fs/promises";
|
|
12
|
+
import { join, sep } from "node:path";
|
|
13
|
+
|
|
14
|
+
import ignore from "ignore";
|
|
15
|
+
|
|
16
|
+
import { WORKSPACE_GITIGNORE_RULES } from "../../workspace/git-service.js";
|
|
17
|
+
import type { WorkspaceEntry } from "./workspace-utils.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Entries a recursive listing returns before it stops descending and
|
|
21
|
+
* reports `truncated`. The root's own listing is always whole, so the
|
|
22
|
+
* answer is never worse than a non-recursive one.
|
|
23
|
+
*/
|
|
24
|
+
export const MAX_RECURSIVE_ENTRIES = 20_000;
|
|
25
|
+
|
|
26
|
+
/** Wall-clock budget for one recursive walk. */
|
|
27
|
+
export const MAX_RECURSIVE_WALK_MS = 3_000;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Directories the recursive walk lists but does not enter: whatever the
|
|
31
|
+
* workspace's own gitignore rules exclude (databases, caches, dependency
|
|
32
|
+
* trees, logs), which is the one maintained statement of what in a
|
|
33
|
+
* workspace is runtime state rather than the user's files. Files those rules
|
|
34
|
+
* match are still listed; only descent is decided here.
|
|
35
|
+
*/
|
|
36
|
+
const descentRules = ignore().add(WORKSPACE_GITIGNORE_RULES);
|
|
37
|
+
|
|
38
|
+
/** git never lists its own metadata directory, and neither does this walk. */
|
|
39
|
+
const GIT_DIR = ".git";
|
|
40
|
+
|
|
41
|
+
function isDescentSkipped(relativeDirPath: string, name: string): boolean {
|
|
42
|
+
return name === GIT_DIR || descentRules.ignores(`${relativeDirPath}/`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface WorkspaceTreeWalk {
|
|
46
|
+
entries: WorkspaceEntry[];
|
|
47
|
+
/** The entry cap or the deadline stopped a recursive walk early. */
|
|
48
|
+
truncated: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Workspace-relative directories the walk listed but did not enter, for
|
|
51
|
+
* any reason: a gitignore rule, a symlink, or a directory that could not
|
|
52
|
+
* be read. A client that wants their contents asks for them one at a time.
|
|
53
|
+
*/
|
|
54
|
+
skipped: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface WorkspaceTreeWalkOptions {
|
|
58
|
+
/** Absolute directory to list. */
|
|
59
|
+
rootPath: string;
|
|
60
|
+
/** Absolute workspace root; entry paths are relative to it. */
|
|
61
|
+
workspaceDir: string;
|
|
62
|
+
showHidden: boolean;
|
|
63
|
+
recursive: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Per-directory size for directory entries in a non-recursive listing.
|
|
66
|
+
* Ignored when `recursive`, where directories report `size: null`.
|
|
67
|
+
*/
|
|
68
|
+
directorySize?: (absPath: string) => number | null;
|
|
69
|
+
maxEntries?: number;
|
|
70
|
+
maxWalkMs?: number;
|
|
71
|
+
/** Clock for the deadline. Tests hand in a scripted one. */
|
|
72
|
+
now?: () => number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function byDirectoriesThenName(a: WorkspaceEntry, b: WorkspaceEntry): number {
|
|
76
|
+
if (a.type !== b.type) {
|
|
77
|
+
return a.type === "directory" ? -1 : 1;
|
|
78
|
+
}
|
|
79
|
+
return a.name.localeCompare(b.name);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface ListedDirectory {
|
|
83
|
+
entries: WorkspaceEntry[];
|
|
84
|
+
/** Directory entries in listing order, with whether each is a symlink. */
|
|
85
|
+
subdirectories: Array<{
|
|
86
|
+
absPath: string;
|
|
87
|
+
relativePath: string;
|
|
88
|
+
name: string;
|
|
89
|
+
isSymlink: boolean;
|
|
90
|
+
}>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* What a nested directory may cost a recursive walk. The listing stops
|
|
95
|
+
* before any stat work when the directory has more entries than the
|
|
96
|
+
* response has room for, and between stats once the deadline passes, so a
|
|
97
|
+
* single wide or slow directory cannot carry the walk past its bound.
|
|
98
|
+
*/
|
|
99
|
+
interface ListingBudget {
|
|
100
|
+
room: number;
|
|
101
|
+
deadline: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* One directory's entries, sorted directories first then by name. Types
|
|
106
|
+
* follow `stat`, so a symlinked directory reads as a directory; the caller
|
|
107
|
+
* decides whether to enter it. Returns `null` when a budget stops the
|
|
108
|
+
* listing, in which case none of it is reported.
|
|
109
|
+
*/
|
|
110
|
+
async function listDirectory(
|
|
111
|
+
absDir: string,
|
|
112
|
+
{
|
|
113
|
+
workspaceDir,
|
|
114
|
+
showHidden,
|
|
115
|
+
directorySize,
|
|
116
|
+
now = Date.now,
|
|
117
|
+
}: WorkspaceTreeWalkOptions,
|
|
118
|
+
budget?: ListingBudget,
|
|
119
|
+
): Promise<ListedDirectory | null> {
|
|
120
|
+
const dirents: Dirent[] = (
|
|
121
|
+
await readdir(absDir, { withFileTypes: true })
|
|
122
|
+
).filter((dirent) => showHidden || !dirent.name.startsWith("."));
|
|
123
|
+
if (budget && dirents.length > budget.room) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const entries: WorkspaceEntry[] = [];
|
|
127
|
+
const subdirectories: ListedDirectory["subdirectories"] = [];
|
|
128
|
+
|
|
129
|
+
for (const dirent of dirents) {
|
|
130
|
+
if (budget && now() > budget.deadline) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const absPath = join(absDir, dirent.name);
|
|
134
|
+
let stats: Stats;
|
|
135
|
+
try {
|
|
136
|
+
stats = await stat(absPath);
|
|
137
|
+
} catch {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const isDir = stats.isDirectory();
|
|
141
|
+
// Wire paths use "/" on every host; clients key expansion and
|
|
142
|
+
// ancestry on them and never see the daemon's own separator.
|
|
143
|
+
const relativePath = absPath
|
|
144
|
+
.slice(workspaceDir.length + 1)
|
|
145
|
+
.split(sep)
|
|
146
|
+
.join("/");
|
|
147
|
+
entries.push({
|
|
148
|
+
name: dirent.name,
|
|
149
|
+
path: relativePath,
|
|
150
|
+
type: isDir ? "directory" : "file",
|
|
151
|
+
size: isDir ? (directorySize?.(absPath) ?? null) : stats.size,
|
|
152
|
+
mimeType: isDir ? null : Bun.file(absPath).type,
|
|
153
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
154
|
+
});
|
|
155
|
+
if (isDir) {
|
|
156
|
+
subdirectories.push({
|
|
157
|
+
absPath,
|
|
158
|
+
relativePath,
|
|
159
|
+
name: dirent.name,
|
|
160
|
+
isSymlink: dirent.isSymbolicLink(),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
entries.sort(byDirectoriesThenName);
|
|
166
|
+
subdirectories.sort((a, b) => a.name.localeCompare(b.name));
|
|
167
|
+
return { entries, subdirectories };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* List `rootPath`. A recursive walk is depth-first with each directory's
|
|
172
|
+
* entries in listing order and never part of a directory, so grouping the
|
|
173
|
+
* result by parent path gives every directory's listing exactly as a
|
|
174
|
+
* non-recursive request would, or nothing for it at all.
|
|
175
|
+
*
|
|
176
|
+
* Symlinked directories are listed but never entered, so a link cannot make
|
|
177
|
+
* the walk cycle or leave the workspace; they are reported in `skipped`, as
|
|
178
|
+
* is a directory found mid-walk that cannot be read. The root's own read
|
|
179
|
+
* error is the caller's to report.
|
|
180
|
+
*/
|
|
181
|
+
export async function walkWorkspaceTree(
|
|
182
|
+
options: WorkspaceTreeWalkOptions,
|
|
183
|
+
): Promise<WorkspaceTreeWalk> {
|
|
184
|
+
const {
|
|
185
|
+
rootPath,
|
|
186
|
+
recursive,
|
|
187
|
+
maxEntries = MAX_RECURSIVE_ENTRIES,
|
|
188
|
+
maxWalkMs = MAX_RECURSIVE_WALK_MS,
|
|
189
|
+
now = Date.now,
|
|
190
|
+
} = options;
|
|
191
|
+
// A recursive listing carries every file, so a client sums directory
|
|
192
|
+
// sizes itself and no per-directory sizer runs.
|
|
193
|
+
const listOptions = recursive
|
|
194
|
+
? { ...options, directorySize: undefined }
|
|
195
|
+
: options;
|
|
196
|
+
// The root is listed without a budget: its listing is always whole.
|
|
197
|
+
const root = (await listDirectory(rootPath, listOptions))!;
|
|
198
|
+
if (!recursive) {
|
|
199
|
+
return { entries: root.entries, truncated: false, skipped: [] };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const deadline = now() + maxWalkMs;
|
|
203
|
+
const entries: WorkspaceEntry[] = [...root.entries];
|
|
204
|
+
const skipped: string[] = [];
|
|
205
|
+
// Reversed so the stack pops subdirectories in listing order.
|
|
206
|
+
const stack = root.subdirectories.slice().reverse();
|
|
207
|
+
|
|
208
|
+
while (stack.length > 0) {
|
|
209
|
+
const dir = stack.pop()!;
|
|
210
|
+
if (dir.isSymlink || isDescentSkipped(dir.relativePath, dir.name)) {
|
|
211
|
+
skipped.push(dir.relativePath);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (entries.length >= maxEntries || now() > deadline) {
|
|
215
|
+
return { entries, truncated: true, skipped };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let listed: ListedDirectory | null;
|
|
219
|
+
try {
|
|
220
|
+
listed = await listDirectory(dir.absPath, listOptions, {
|
|
221
|
+
room: maxEntries - entries.length,
|
|
222
|
+
deadline,
|
|
223
|
+
});
|
|
224
|
+
} catch {
|
|
225
|
+
skipped.push(dir.relativePath);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
// A directory is carried whole or not at all, so a client grouping the
|
|
229
|
+
// result by parent never mistakes part of a listing for all of it.
|
|
230
|
+
if (listed === null) {
|
|
231
|
+
return { entries, truncated: true, skipped };
|
|
232
|
+
}
|
|
233
|
+
entries.push(...listed.entries);
|
|
234
|
+
for (let i = listed.subdirectories.length - 1; i >= 0; i--) {
|
|
235
|
+
stack.push(listed.subdirectories[i]);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { entries, truncated: false, skipped };
|
|
240
|
+
}
|
|
@@ -8,9 +8,26 @@ import {
|
|
|
8
8
|
} from "node:fs";
|
|
9
9
|
import { dirname, resolve, sep } from "node:path";
|
|
10
10
|
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
11
13
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
12
14
|
import { BadRequestError, ConflictError } from "./errors.js";
|
|
13
15
|
|
|
16
|
+
/**
|
|
17
|
+
* One workspace entry as the tree endpoint reports it. `size` is null for a
|
|
18
|
+
* directory whose recursive size was not computed.
|
|
19
|
+
*/
|
|
20
|
+
export const workspaceEntrySchema = z.object({
|
|
21
|
+
name: z.string(),
|
|
22
|
+
path: z.string(),
|
|
23
|
+
type: z.enum(["file", "directory"]),
|
|
24
|
+
size: z.number().nullable(),
|
|
25
|
+
mimeType: z.string().nullable(),
|
|
26
|
+
modifiedAt: z.string(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export type WorkspaceEntry = z.infer<typeof workspaceEntrySchema>;
|
|
30
|
+
|
|
14
31
|
/**
|
|
15
32
|
* Resolves a user-provided relative path to an absolute path within the workspace.
|
|
16
33
|
* Returns the resolved absolute path, or undefined if the path escapes the workspace root.
|
|
@@ -159,3 +159,31 @@ describe("serializeConversationSummary · enabledPlugins", () => {
|
|
|
159
159
|
expect("enabledPlugins" in summary).toBe(false);
|
|
160
160
|
});
|
|
161
161
|
});
|
|
162
|
+
|
|
163
|
+
describe("serializeConversationSummary · title keys", () => {
|
|
164
|
+
test("resolves a stored title key for the locale", () => {
|
|
165
|
+
const summary = serializeConversationSummary({
|
|
166
|
+
conversation: makeConversationRow({
|
|
167
|
+
title: "conversation.title.generating",
|
|
168
|
+
}),
|
|
169
|
+
displayMeta: { displayOrder: null, isPinned: false, groupId: null },
|
|
170
|
+
parentCache: new Map(),
|
|
171
|
+
isProcessing: false,
|
|
172
|
+
locale: "es",
|
|
173
|
+
});
|
|
174
|
+
expect(summary.title).toBe("Generando título...");
|
|
175
|
+
expect("titleState" in summary).toBe(false);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("passes a stored display string through", () => {
|
|
179
|
+
const summary = serialize(
|
|
180
|
+
makeConversationRow({ title: "Untitled Conversation" }),
|
|
181
|
+
);
|
|
182
|
+
expect(summary.title).toBe("Untitled Conversation");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("passes a custom title through", () => {
|
|
186
|
+
const summary = serialize(makeConversationRow({ title: "Weekly planning" }));
|
|
187
|
+
expect(summary.title).toBe("Weekly planning");
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import { parseChannelId } from "../../channels/types.js";
|
|
10
10
|
import { normalizeConversationType } from "../../daemon/message-types/shared.js";
|
|
11
|
+
import {
|
|
12
|
+
resolveConversationTitle,
|
|
13
|
+
type SupportedLocale,
|
|
14
|
+
} from "../../i18n/index.js";
|
|
11
15
|
import { buildChannelBindingMetadata } from "../../messaging/channel-binding-metadata.js";
|
|
12
16
|
import {
|
|
13
17
|
type AttentionState,
|
|
@@ -78,6 +82,7 @@ interface ForkLineage {
|
|
|
78
82
|
function buildForkLineage(
|
|
79
83
|
conversation: ConversationRow,
|
|
80
84
|
parentCache: Map<string, ConversationRow | null>,
|
|
85
|
+
locale?: SupportedLocale,
|
|
81
86
|
): ForkLineage {
|
|
82
87
|
const parentConversationId = conversation.forkParentConversationId;
|
|
83
88
|
const parentMessageId = conversation.forkParentMessageId;
|
|
@@ -101,7 +106,7 @@ function buildForkLineage(
|
|
|
101
106
|
forkParent: {
|
|
102
107
|
conversationId: parentConversationId,
|
|
103
108
|
messageId: parentMessageId,
|
|
104
|
-
title: parentConversation.title
|
|
109
|
+
title: resolveConversationTitle(parentConversation.title, locale),
|
|
105
110
|
},
|
|
106
111
|
};
|
|
107
112
|
}
|
|
@@ -178,6 +183,11 @@ export function serializeConversationSummary(params: {
|
|
|
178
183
|
* with no daemon-store coupling.
|
|
179
184
|
*/
|
|
180
185
|
isProcessing: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Locale used to resolve stored title keys. Omitted locale is English
|
|
188
|
+
* inside {@link resolveConversationTitle}.
|
|
189
|
+
*/
|
|
190
|
+
locale?: SupportedLocale;
|
|
181
191
|
}) {
|
|
182
192
|
const {
|
|
183
193
|
conversation,
|
|
@@ -186,14 +196,15 @@ export function serializeConversationSummary(params: {
|
|
|
186
196
|
displayMeta,
|
|
187
197
|
parentCache,
|
|
188
198
|
isProcessing,
|
|
199
|
+
locale,
|
|
189
200
|
} = params;
|
|
190
201
|
const originChannel = parseChannelId(conversation.originChannel);
|
|
191
202
|
const assistantAttention = buildAssistantAttention(attentionState);
|
|
192
|
-
const forkLineage = buildForkLineage(conversation, parentCache);
|
|
203
|
+
const forkLineage = buildForkLineage(conversation, parentCache, locale);
|
|
193
204
|
|
|
194
205
|
return {
|
|
195
206
|
id: conversation.id,
|
|
196
|
-
title: conversation.title
|
|
207
|
+
title: resolveConversationTitle(conversation.title, locale),
|
|
197
208
|
createdAt: conversation.createdAt,
|
|
198
209
|
updatedAt: conversation.updatedAt,
|
|
199
210
|
lastMessageAt: conversation.lastMessageAt,
|
|
@@ -253,6 +264,7 @@ export function serializeConversationSummary(params: {
|
|
|
253
264
|
*/
|
|
254
265
|
export function buildConversationDetailResponse(
|
|
255
266
|
conversationId: string,
|
|
267
|
+
locale?: SupportedLocale,
|
|
256
268
|
): { conversation: ReturnType<typeof serializeConversationSummary> } | null {
|
|
257
269
|
const conversation = getConversation(conversationId);
|
|
258
270
|
if (!conversation || conversation.conversationType === "private") {
|
|
@@ -274,6 +286,7 @@ export function buildConversationDetailResponse(
|
|
|
274
286
|
// Checks in-memory flag first (hot path), falls back to the
|
|
275
287
|
// persisted `processing_started_at` column for cold conversations.
|
|
276
288
|
isProcessing: isConversationProcessing(conversation.id),
|
|
289
|
+
locale,
|
|
277
290
|
}),
|
|
278
291
|
};
|
|
279
292
|
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
conversationMetadataSyncTag,
|
|
5
5
|
SYNC_TAGS,
|
|
6
6
|
} from "../../daemon/message-types/sync.js";
|
|
7
|
+
import { resolveConversationTitle } from "../../i18n/index.js";
|
|
7
8
|
import { syncAvatarToPlatform } from "../../platform/sync-avatar.js";
|
|
8
9
|
import { getAvatarImagePath } from "../../util/platform.js";
|
|
9
10
|
import { broadcastMessage } from "../assistant-event-hub.js";
|
|
@@ -257,7 +258,7 @@ export function publishConversationTitleChanged(
|
|
|
257
258
|
{
|
|
258
259
|
type: "conversation_title_updated",
|
|
259
260
|
conversationId,
|
|
260
|
-
title,
|
|
261
|
+
title: resolveConversationTitle(title),
|
|
261
262
|
},
|
|
262
263
|
conversationId,
|
|
263
264
|
);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "../util/host-process.js";
|
|
6
6
|
import { getLogger } from "../util/logger.js";
|
|
7
7
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
8
|
+
import { safeStringSlice } from "../util/unicode.js";
|
|
8
9
|
|
|
9
10
|
const log = getLogger("run-script");
|
|
10
11
|
|
|
@@ -200,7 +201,7 @@ function truncate(text: string): string {
|
|
|
200
201
|
if (text.length <= MAX_OUTPUT_BYTES) {
|
|
201
202
|
return text;
|
|
202
203
|
}
|
|
203
|
-
return text
|
|
204
|
+
return safeStringSlice(text, 0, MAX_OUTPUT_BYTES) + "\n... (truncated)";
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
/**
|
|
@@ -23,6 +23,7 @@ import { publishSchedulesChanged } from "../runtime/sync/resource-sync-events.js
|
|
|
23
23
|
import { UserError } from "../util/errors.js";
|
|
24
24
|
import { getLogger } from "../util/logger.js";
|
|
25
25
|
import { withSqliteRetry } from "../util/sqlite-retry.js";
|
|
26
|
+
import { safeStringSlice } from "../util/unicode.js";
|
|
26
27
|
import {
|
|
27
28
|
hasOwnerDeferProvenance,
|
|
28
29
|
isDeferSchedule,
|
|
@@ -1511,8 +1512,14 @@ async function finishScheduleRunRow(
|
|
|
1511
1512
|
status: result.status,
|
|
1512
1513
|
finishedAt: now,
|
|
1513
1514
|
durationMs,
|
|
1514
|
-
output:
|
|
1515
|
-
|
|
1515
|
+
output:
|
|
1516
|
+
result.output === undefined
|
|
1517
|
+
? null
|
|
1518
|
+
: safeStringSlice(result.output, 0, 10_000),
|
|
1519
|
+
error:
|
|
1520
|
+
result.error === undefined
|
|
1521
|
+
? null
|
|
1522
|
+
: safeStringSlice(result.error, 0, 2000),
|
|
1516
1523
|
})
|
|
1517
1524
|
.where(eq(scheduleRuns.id, runId))
|
|
1518
1525
|
.run(),
|
package/src/schedule/worker.ts
CHANGED
|
@@ -195,7 +195,7 @@ async function main(): Promise<void> {
|
|
|
195
195
|
//
|
|
196
196
|
// Bounded by MCP_STARTUP_GRACE_MS: unreachable servers must not hold the
|
|
197
197
|
// schedules that do not use them. The connect runs on past the deadline.
|
|
198
|
-
const mcpStartup = startConfiguredMcpServers(
|
|
198
|
+
const mcpStartup = startConfiguredMcpServers();
|
|
199
199
|
await Promise.race([
|
|
200
200
|
mcpStartup,
|
|
201
201
|
// Unref'd so a connect that beats the deadline leaves nothing pending.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { afterEach, describe, expect, spyOn, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { CredentialRecord } from "@vellumai/service-contracts/credential-rpc";
|
|
4
|
+
|
|
5
|
+
import * as envRegistry from "../../config/env-registry.js";
|
|
6
|
+
import {
|
|
7
|
+
CesHttpRecordBackend,
|
|
8
|
+
createCesHttpRecordBackendIfConfigured,
|
|
9
|
+
} from "../ces-http-record-backend.js";
|
|
10
|
+
|
|
11
|
+
const ORIGINAL_URL = process.env.CES_CREDENTIAL_URL;
|
|
12
|
+
const ORIGINAL_TOKEN = process.env.CES_SERVICE_TOKEN;
|
|
13
|
+
|
|
14
|
+
function restoreCesHttpEnv(): void {
|
|
15
|
+
if (ORIGINAL_URL === undefined) {
|
|
16
|
+
delete process.env.CES_CREDENTIAL_URL;
|
|
17
|
+
} else {
|
|
18
|
+
process.env.CES_CREDENTIAL_URL = ORIGINAL_URL;
|
|
19
|
+
}
|
|
20
|
+
if (ORIGINAL_TOKEN === undefined) {
|
|
21
|
+
delete process.env.CES_SERVICE_TOKEN;
|
|
22
|
+
} else {
|
|
23
|
+
process.env.CES_SERVICE_TOKEN = ORIGINAL_TOKEN;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function fakeHttpClient(options: {
|
|
28
|
+
listRecords: () => Promise<{
|
|
29
|
+
records: Array<{ account: string; record: CredentialRecord }>;
|
|
30
|
+
unreachable: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
}) {
|
|
33
|
+
return {
|
|
34
|
+
getRecord: async () => ({ record: undefined, unreachable: false }),
|
|
35
|
+
setRecord: async () => false,
|
|
36
|
+
deleteRecord: async () => "error" as const,
|
|
37
|
+
listRecords: options.listRecords,
|
|
38
|
+
bulkSetRecords: async (records: Array<{ account: string }>) =>
|
|
39
|
+
records.map((entry) => ({ account: entry.account, ok: false })),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("CES HTTP credential record backend", () => {
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
restoreCesHttpEnv();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("factory returns null when the assistant is not containerized", () => {
|
|
49
|
+
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(false);
|
|
50
|
+
process.env.CES_CREDENTIAL_URL = "http://ces-container:8090";
|
|
51
|
+
process.env.CES_SERVICE_TOKEN = "token-123";
|
|
52
|
+
try {
|
|
53
|
+
expect(createCesHttpRecordBackendIfConfigured()).toBeNull();
|
|
54
|
+
} finally {
|
|
55
|
+
spy.mockRestore();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("factory returns null when CES_CREDENTIAL_URL is unset", () => {
|
|
60
|
+
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(true);
|
|
61
|
+
delete process.env.CES_CREDENTIAL_URL;
|
|
62
|
+
process.env.CES_SERVICE_TOKEN = "token-123";
|
|
63
|
+
try {
|
|
64
|
+
expect(createCesHttpRecordBackendIfConfigured()).toBeNull();
|
|
65
|
+
} finally {
|
|
66
|
+
spy.mockRestore();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("factory returns null when CES_SERVICE_TOKEN is unset", () => {
|
|
71
|
+
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(true);
|
|
72
|
+
process.env.CES_CREDENTIAL_URL = "http://ces-container:8090";
|
|
73
|
+
delete process.env.CES_SERVICE_TOKEN;
|
|
74
|
+
try {
|
|
75
|
+
expect(createCesHttpRecordBackendIfConfigured()).toBeNull();
|
|
76
|
+
} finally {
|
|
77
|
+
spy.mockRestore();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("factory returns a backend when containerized with CES HTTP env", () => {
|
|
82
|
+
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(true);
|
|
83
|
+
process.env.CES_CREDENTIAL_URL = "http://ces-container:8090";
|
|
84
|
+
process.env.CES_SERVICE_TOKEN = "token-123";
|
|
85
|
+
try {
|
|
86
|
+
const backend = createCesHttpRecordBackendIfConfigured();
|
|
87
|
+
expect(backend?.name).toBe("ces-http");
|
|
88
|
+
expect(backend?.isAvailable()).toBe(true);
|
|
89
|
+
} finally {
|
|
90
|
+
spy.mockRestore();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("list returns records when HTTP reports a reachable catalog", async () => {
|
|
95
|
+
const record: CredentialRecord = {
|
|
96
|
+
credentialId: "cred-http",
|
|
97
|
+
service: "github",
|
|
98
|
+
field: "token",
|
|
99
|
+
allowedTools: ["bash"],
|
|
100
|
+
allowedDomains: [],
|
|
101
|
+
createdAt: 1,
|
|
102
|
+
updatedAt: 2,
|
|
103
|
+
};
|
|
104
|
+
const backend = new CesHttpRecordBackend(
|
|
105
|
+
fakeHttpClient({
|
|
106
|
+
listRecords: async () => ({
|
|
107
|
+
records: [{ account: "github:token", record }],
|
|
108
|
+
unreachable: false,
|
|
109
|
+
}),
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const listed = await backend.list();
|
|
114
|
+
expect(listed).toEqual([{ account: "github:token", record }]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("list returns an empty catalog when HTTP is reachable and empty", async () => {
|
|
118
|
+
const backend = new CesHttpRecordBackend(
|
|
119
|
+
fakeHttpClient({
|
|
120
|
+
listRecords: async () => ({ records: [], unreachable: false }),
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const listed = await backend.list();
|
|
125
|
+
expect(listed).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("list returns null when HTTP reports the vault unreachable", async () => {
|
|
129
|
+
const backend = new CesHttpRecordBackend(
|
|
130
|
+
fakeHttpClient({
|
|
131
|
+
listRecords: async () => ({ records: [], unreachable: true }),
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(await backend.list()).toBeNull();
|
|
136
|
+
});
|
|
137
|
+
});
|