@vellumai/assistant 0.12.5 → 0.12.6-dev.202609292116.41f7caa
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/.env.example +2 -0
- package/ARCHITECTURE.md +26 -3
- package/docs/automatic-done.md +32 -0
- package/docs/desktop-browser-cli.md +3 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -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/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/environments/src/shell.test.ts +13 -1
- package/node_modules/@vellumai/environments/src/shell.ts +22 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -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/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/status-contracts.test.ts +39 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +25 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/openapi.yaml +270 -10
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +76 -0
- package/src/__tests__/anthropic-provider.test.ts +2 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +7 -0
- package/src/__tests__/config-get-vision-flag.test.ts +1 -1
- package/src/__tests__/config-loader-validation-reset-notice.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +160 -154
- package/src/__tests__/conversation-evictor.test.ts +54 -0
- package/src/__tests__/conversation-pairing.test.ts +112 -0
- package/src/__tests__/document-append-idempotency.test.ts +13 -0
- package/src/__tests__/document-create-dedupe.test.ts +13 -0
- package/src/__tests__/document-find-replace.test.ts +13 -0
- package/src/__tests__/document-revision-history.test.ts +390 -0
- package/src/__tests__/document-revision.test.ts +425 -0
- package/src/__tests__/document-tool-security.test.ts +13 -0
- package/src/__tests__/document-update-default-surface.test.ts +13 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +270 -0
- package/src/__tests__/gateway-flag-listener.test.ts +59 -6
- package/src/__tests__/headless-browser-navigate.test.ts +20 -30
- package/src/__tests__/host-cu-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +14 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -2
- package/src/__tests__/identity-routes.test.ts +4 -3
- package/src/__tests__/list-messages-client-message-id.test.ts +43 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -4
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/notification-platform-adapter.test.ts +83 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
- package/src/__tests__/openai-provider.test.ts +38 -11
- package/src/__tests__/pricing.test.ts +11 -0
- package/src/__tests__/public-ingress-urls.test.ts +26 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +47 -0
- package/src/__tests__/skill-execute-input.test.ts +11 -0
- package/src/__tests__/tool-approval-handler.test.ts +4 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +26 -0
- package/src/__tests__/usage-attribution.test.ts +31 -0
- package/src/__tests__/workspace-migration-133-collapse-provider-connections.test.ts +3 -3
- package/src/__tests__/workspace-migration-160-repair-retired-fireworks-model-ids.test.ts +262 -0
- package/src/__tests__/workspace-migration-161-repair-retired-fireworks-glm-5p2-model-id.test.ts +233 -0
- package/src/acp/agent-process.ts +6 -1
- package/src/acp/client-handler.ts +6 -1
- package/src/agent/loop-unparseable-tool-retry.test.ts +333 -0
- package/src/agent/loop.ts +161 -17
- package/src/api/events/document-editor-show.ts +5 -0
- package/src/api/events/document-editor-update.ts +10 -0
- package/src/api/events/tool-result.ts +1 -0
- package/src/calls/__tests__/call-controller.test.ts +273 -1
- package/src/calls/__tests__/media-stream-server-integration.test.ts +40 -0
- package/src/calls/__tests__/media-stream-stt-session.test.ts +33 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +54 -0
- package/src/calls/call-constants.ts +10 -0
- package/src/calls/call-controller.ts +359 -25
- package/src/calls/channel-admission-reader.ts +15 -9
- package/src/calls/media-stream-server.ts +18 -0
- package/src/calls/media-stream-stt-session.ts +30 -9
- package/src/calls/twilio-provider.ts +14 -31
- package/src/calls/voice-session-bridge.ts +18 -1
- package/src/cli/commands/browser.ts +1 -2
- package/src/cli/lib/__tests__/plugin-artifact.test.ts +23 -0
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +2 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +16 -0
- package/src/cli/lib/bundled-marketplace.json +54 -2
- package/src/cli/lib/inspect-plugin.ts +15 -15
- package/src/cli/lib/list-installed-plugins.ts +29 -28
- package/src/cli/lib/plugin-artifact.ts +74 -63
- package/src/cli/lib/plugin-catalog-visibility.ts +1 -0
- package/src/cli/lib/plugin-details.ts +27 -42
- package/src/cli/lib/plugin-marketplace.ts +6 -8
- package/src/cli/lib/plugin-surfaces.ts +8 -7
- package/src/cli.ts +1 -3
- package/src/config/__tests__/chat-settings.test.ts +121 -0
- package/src/config/assistant-feature-flags.ts +28 -23
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +11 -11
- package/src/config/chat-settings.ts +100 -0
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/feature-flag-cache.ts +7 -25
- package/src/config/feature-flag-registry.json +9 -9
- package/src/config/loader.ts +5 -1
- package/src/config/profile-text-generation.ts +25 -21
- package/src/config/schemas/conversations.ts +14 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/notifications.ts +8 -1
- package/src/contacts/gateway-channel-read.ts +1 -1
- package/src/conversations/__tests__/auto-archive-activity.test.ts +136 -0
- package/src/conversations/__tests__/auto-archive.test.ts +304 -0
- package/src/conversations/auto-archive-activity.ts +61 -0
- package/src/conversations/auto-archive.ts +201 -0
- package/src/daemon/computer-use-observation.test.ts +81 -0
- package/src/daemon/computer-use-observation.ts +23 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -2
- package/src/daemon/conversation-agent-loop.ts +14 -6
- package/src/daemon/conversation-evictor.ts +50 -16
- package/src/daemon/conversation-messaging.ts +4 -0
- package/src/daemon/conversation-process.ts +15 -0
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/doordash-steps.ts +1 -3
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +43 -1
- package/src/daemon/handlers/__tests__/config-channels.test.ts +1 -1
- package/src/daemon/handlers/config-a2a.ts +75 -2
- package/src/daemon/handlers/shared.ts +13 -2
- package/src/daemon/host-cu-proxy.ts +34 -3
- package/src/daemon/lifecycle.ts +15 -2
- package/src/daemon/process-message.ts +5 -0
- package/src/daemon/shutdown-handlers.ts +3 -0
- package/src/daemon/turn-finalization.ts +4 -0
- package/src/desktop/desktop-browser-endpoint.ts +11 -2
- package/src/desktop/desktop-chrome-policy.test.ts +1 -0
- package/src/desktop/desktop-chrome-policy.ts +7 -1
- package/src/desktop/desktop-computer-use.test.ts +14 -4
- package/src/desktop/desktop-session-manager.ts +5 -3
- package/src/documents/document-revisions-store.ts +153 -0
- package/src/documents/document-store.ts +471 -133
- package/src/followups/followup-store.ts +0 -4
- package/src/i18n/__tests__/i18n.test.ts +25 -0
- package/src/i18n/assistant-text.ts +18 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/messages.ts +11 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +25 -2
- package/src/inbound/oauth-callback-url.ts +30 -1
- package/src/inbound/public-ingress-urls.ts +12 -2
- package/src/ipc/gateway-client.test.ts +40 -0
- package/src/ipc/gateway-client.ts +12 -29
- package/src/ipc/gateway-flag-listener.ts +7 -10
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +71 -0
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +83 -0
- package/src/live-voice/__tests__/protocol.test.ts +77 -0
- package/src/live-voice/__tests__/share-targets.test.ts +163 -0
- package/src/live-voice/live-voice-session.ts +23 -6
- package/src/live-voice/protocol.ts +22 -0
- package/src/live-voice/share-targets.ts +225 -0
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +144 -3
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -0
- package/src/mcp/client.ts +1 -19
- package/src/mcp/mcp-oauth-provider.ts +66 -49
- package/src/media/openrouter-image-service.ts +39 -22
- package/src/monitoring/__tests__/oom-inheritance-sweep.test.ts +86 -0
- package/src/monitoring/__tests__/oom-kill-reporter.test.ts +374 -0
- package/src/monitoring/control.ts +2 -1
- package/src/monitoring/oom-inheritance-sweep.ts +96 -0
- package/src/monitoring/oom-kill-reporter.ts +469 -0
- package/src/monitoring/resource-sampler.ts +30 -0
- package/src/notifications/README.md +53 -4
- package/src/notifications/__tests__/assistant-initiated-thread.test.ts +254 -0
- package/src/notifications/__tests__/broadcaster.test.ts +558 -6
- package/src/notifications/__tests__/completion-policy.test.ts +37 -0
- package/src/notifications/__tests__/from-me-alert-integration.test.ts +192 -0
- package/src/notifications/adapters/platform.ts +14 -0
- package/src/notifications/assistant-initiated-thread.ts +101 -0
- package/src/notifications/broadcaster.ts +94 -16
- package/src/notifications/chat-reply-policy.ts +11 -0
- package/src/notifications/completion-policy.ts +12 -2
- package/src/notifications/conversation-pairing.ts +15 -70
- package/src/notifications/emit-signal.ts +34 -3
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connect-orchestrator.ts +1 -2
- package/src/persistence/__tests__/conversation-auto-archive.test.ts +330 -0
- package/src/persistence/conversation-auto-archive.ts +170 -0
- package/src/persistence/conversation-crud.ts +2 -1
- package/src/persistence/conversation-queries.ts +15 -8
- package/src/persistence/embeddings/embed.test.ts +96 -0
- package/src/persistence/embeddings/embed.ts +20 -2
- package/src/persistence/embeddings/embedding-gemini.test.ts +39 -3
- package/src/persistence/embeddings/embedding-gemini.ts +39 -1
- package/src/persistence/embeddings/embedding-local.ts +6 -0
- package/src/persistence/embeddings/qdrant-manager.ts +5 -0
- package/src/persistence/job-utils.ts +10 -0
- package/src/persistence/migrations/384-add-document-revision.test.ts +83 -0
- package/src/persistence/migrations/384-add-document-revision.ts +21 -0
- package/src/persistence/migrations/385-create-document-revisions.test.ts +67 -0
- package/src/persistence/migrations/385-create-document-revisions.ts +28 -0
- package/src/persistence/migrations/386-conversations-last-reopened-at.ts +10 -0
- package/src/persistence/migrations/__tests__/386-conversations-last-reopened-at.test.ts +32 -0
- package/src/persistence/schema/conversations.ts +1 -0
- package/src/persistence/schema/documents.ts +20 -0
- package/src/persistence/steps.ts +6 -0
- package/src/platform/managed-speech.ts +42 -46
- package/src/plugin-api/vision-support.test.ts +21 -21
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +11 -6
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +1 -1
- package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
- package/src/providers/__tests__/unparseable-tool-args.test.ts +25 -0
- package/src/providers/anthropic/client.ts +1 -1
- package/src/providers/fireworks/client.test.ts +168 -0
- package/src/providers/fireworks/client.ts +24 -0
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/ionet/client.test.ts +143 -0
- package/src/providers/ionet/client.ts +37 -0
- package/src/providers/model-catalog.ts +141 -84
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +33 -2
- package/src/providers/retry.ts +56 -4
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +10 -2
- package/src/providers/unparseable-tool-args.ts +29 -5
- package/src/providers/usage-tracking.ts +5 -3
- package/src/runtime/assistant-event-hub.ts +23 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +0 -7
- package/src/runtime/auth/__tests__/route-policy.test.ts +1 -1
- package/src/runtime/channel-reply-delivery.ts +2 -0
- package/src/runtime/channel-retry-sweep.ts +1 -2
- package/src/runtime/http-types.ts +3 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +35 -10
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +48 -0
- package/src/runtime/migrations/vbundle-builder.ts +19 -3
- package/src/runtime/migrations/vbundle-streaming-importer.ts +14 -19
- package/src/runtime/migrations/vbundle-streaming-validator.ts +14 -7
- package/src/runtime/migrations/vbundle-validator.ts +54 -1
- package/src/runtime/pending-interactions.ts +20 -2
- package/src/runtime/routes/__tests__/contact-routes.test.ts +5 -3
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +36 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +375 -2
- package/src/runtime/routes/__tests__/inference-send-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +12 -0
- package/src/runtime/routes/contact-routes.ts +2 -2
- package/src/runtime/routes/conversation-list-routes.ts +10 -2
- package/src/runtime/routes/conversation-query-routes.ts +48 -10
- package/src/runtime/routes/conversation-routes.ts +7 -0
- package/src/runtime/routes/documents-routes.ts +185 -47
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -0
- package/src/runtime/routes/host-bash-routes.ts +1 -0
- package/src/runtime/routes/host-browser-routes.ts +15 -0
- package/src/runtime/routes/host-cu-routes.ts +4 -1
- package/src/runtime/routes/host-file-routes.ts +1 -0
- package/src/runtime/routes/host-proxy-result-binding.test.ts +83 -0
- package/src/runtime/routes/host-proxy-result-binding.ts +25 -0
- package/src/runtime/routes/host-transfer-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/integrations/a2a.ts +4 -1
- package/src/runtime/routes/migration-routes.ts +3 -1
- package/src/runtime/routes/notification-routes.ts +1 -2
- package/src/runtime/routes/ui-snapshot-routes.test.ts +1 -0
- package/src/runtime/routes/user-route-dispatcher.ts +5 -1
- package/src/runtime/verification-templates.ts +1 -2
- package/src/sequence/analytics.ts +0 -4
- package/src/sequence/engine.ts +1 -11
- package/src/sequence/guardrails.ts +1 -6
- package/src/sequence/importer.ts +0 -8
- package/src/sequence/store.ts +0 -10
- package/src/signals/user-message.ts +0 -6
- package/src/skills/catalog-install.ts +52 -23
- package/src/tools/browser/browser-execution.ts +20 -23
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +1 -3
- package/src/tools/document/document-tool.ts +13 -11
- package/src/tools/executor.ts +1 -2
- package/src/tools/host-terminal/host-shell.ts +4 -2
- package/src/tools/network/__tests__/web-search.test.ts +105 -0
- package/src/tools/network/web-fetch.ts +2 -4
- package/src/tools/network/web-search.ts +239 -12
- package/src/tools/shared/filesystem/format-diff.ts +1 -2
- package/src/tools/shared/shell-output.test.ts +42 -0
- package/src/tools/shared/shell-output.ts +27 -4
- package/src/tools/skills/execute.ts +10 -7
- package/src/tools/terminal/shell.ts +3 -3
- package/src/tools/tool-approval-handler.ts +9 -1
- package/src/tools/ui-surface/visual-validation.ts +1 -2
- package/src/usage/attribution.ts +18 -1
- package/src/util/__tests__/oom-priority.test.ts +55 -0
- package/src/util/__tests__/worker-memory.test.ts +16 -0
- package/src/util/debounce.ts +0 -4
- package/src/util/oom-priority.ts +79 -0
- package/src/util/platform.ts +0 -2
- package/src/util/plugin-manifest.ts +5 -7
- package/src/util/pricing.ts +25 -0
- package/src/util/worker-memory.ts +9 -3
- package/src/util/worker-process.ts +8 -0
- package/src/workspace/byok-default-profile-ensure.ts +11 -0
- package/src/workspace/hatched-date.ts +11 -4
- package/src/workspace/migrations/160-repair-retired-fireworks-model-ids.ts +209 -0
- package/src/workspace/migrations/161-repair-retired-fireworks-glm-5p2-model-id.ts +197 -0
- package/src/workspace/migrations/registry.ts +4 -0
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../config/loader.js";
|
|
14
14
|
import { seedInferenceProfiles } from "../config/seed-inference-profiles.js";
|
|
15
15
|
import { reconcileFlagGatedProfiles } from "../config/sync-gated-profiles.js";
|
|
16
|
+
import { startConversationAutoArchive } from "../conversations/auto-archive.js";
|
|
16
17
|
import { startCes } from "../credential-execution/ces-runtime.js";
|
|
17
18
|
import { refreshManagedConnectionCache } from "../credential-execution/managed-catalog.js";
|
|
18
19
|
import { startHeartbeatService } from "../heartbeat/heartbeat-service.js";
|
|
@@ -57,6 +58,7 @@ import { startScheduler } from "../schedule/scheduler.js";
|
|
|
57
58
|
import { getSubagentManager } from "../subagent/index.js";
|
|
58
59
|
import { startUsageTelemetryReporter } from "../telemetry/usage-telemetry-reporter.js";
|
|
59
60
|
import { getLogger, initLogger } from "../util/logger.js";
|
|
61
|
+
import { DAEMON_OOM_SCORE_ADJ, setOomScoreAdj } from "../util/oom-priority.js";
|
|
60
62
|
import {
|
|
61
63
|
ensureDataDir,
|
|
62
64
|
getDotEnvPath,
|
|
@@ -114,6 +116,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
114
116
|
// the event hub real clients subscribe to, so plugin-facing publishes made
|
|
115
117
|
// here fan out locally rather than routing to a daemon over IPC.
|
|
116
118
|
markCurrentProcessAsMainDaemon();
|
|
119
|
+
// Before the first spawn: every child inherits this value and resets its
|
|
120
|
+
// own, so the kernel OOM killer takes a tool or worker before the daemon.
|
|
121
|
+
const oomProtected = setOomScoreAdj(DAEMON_OOM_SCORE_ADJ);
|
|
117
122
|
|
|
118
123
|
const startupStartedAt = Date.now();
|
|
119
124
|
// dotenv loads before the first log call so the lazy root logger
|
|
@@ -121,7 +126,13 @@ export async function runDaemon(): Promise<void> {
|
|
|
121
126
|
// whatever was in the live environment at process spawn.
|
|
122
127
|
loadDotEnv();
|
|
123
128
|
validateEnv();
|
|
124
|
-
log.info(
|
|
129
|
+
log.info(
|
|
130
|
+
{
|
|
131
|
+
version: APP_VERSION,
|
|
132
|
+
oomScoreAdj: oomProtected ? DAEMON_OOM_SCORE_ADJ : undefined,
|
|
133
|
+
},
|
|
134
|
+
"Daemon starting",
|
|
135
|
+
);
|
|
125
136
|
|
|
126
137
|
// Signal handlers install before any blocking startup work — a boot that
|
|
127
138
|
// inherits a large WAL can spend minutes inside `initializeDb()`, and
|
|
@@ -860,13 +871,15 @@ export async function runDaemon(): Promise<void> {
|
|
|
860
871
|
// CES, which the startup sequence above just brought up. Fire-and-forget:
|
|
861
872
|
// the resumes run sequentially in the background while the daemon serves
|
|
862
873
|
// requests; per-conversation failures are logged inside.
|
|
874
|
+
let startupRecovery = Promise.resolve();
|
|
863
875
|
if (conversationsToResume.length > 0) {
|
|
864
876
|
log.info(
|
|
865
877
|
{ count: conversationsToResume.length },
|
|
866
878
|
"Resuming conversations interrupted by the previous process",
|
|
867
879
|
);
|
|
868
|
-
|
|
880
|
+
startupRecovery = resumeInterruptedConversations(conversationsToResume);
|
|
869
881
|
}
|
|
882
|
+
startConversationAutoArchive(startupRecovery);
|
|
870
883
|
|
|
871
884
|
log.info(
|
|
872
885
|
{
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
parseChannelId,
|
|
20
20
|
parseInterfaceId,
|
|
21
21
|
} from "../channels/types.js";
|
|
22
|
+
import type { SupportedLocale } from "../i18n/index.js";
|
|
22
23
|
import { resolveAttachmentsForPersist } from "../persistence/attachments-store.js";
|
|
23
24
|
import {
|
|
24
25
|
addMessage,
|
|
@@ -88,6 +89,8 @@ type ProcessMessageOptions = ConversationCreateOptions & {
|
|
|
88
89
|
sourceChannel?: string;
|
|
89
90
|
/** Originating interface (e.g. "cli", "web"). Defaults to "web". */
|
|
90
91
|
sourceInterface?: string;
|
|
92
|
+
/** Locale for daemon-authored text emitted during this turn. */
|
|
93
|
+
locale?: SupportedLocale;
|
|
91
94
|
/**
|
|
92
95
|
* Origin tag of the turn (the conversation's `TitleOrigin`, e.g.
|
|
93
96
|
* "memory_consolidation"), threaded from `runBackgroundJob`. Propagated
|
|
@@ -780,6 +783,7 @@ export async function processMessage(
|
|
|
780
783
|
onEvent: emitEvent,
|
|
781
784
|
isInteractive: options?.isInteractive ?? false,
|
|
782
785
|
isUserMessage: true,
|
|
786
|
+
locale: options?.locale,
|
|
783
787
|
...(options?.callSite ? { callSite: options.callSite } : {}),
|
|
784
788
|
...(options?.overrideProfile
|
|
785
789
|
? { overrideProfile: options.overrideProfile }
|
|
@@ -854,6 +858,7 @@ export async function processMessageInBackground(
|
|
|
854
858
|
onEvent: emitEvent,
|
|
855
859
|
isInteractive: options?.isInteractive ?? false,
|
|
856
860
|
isUserMessage: true,
|
|
861
|
+
locale: options?.locale,
|
|
857
862
|
...(options?.callSite ? { callSite: options.callSite } : {}),
|
|
858
863
|
...(options?.overrideProfile
|
|
859
864
|
? { overrideProfile: options.overrideProfile }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { disposeAcpSessionManager } from "../acp/index.js";
|
|
2
|
+
import { stopConversationAutoArchive } from "../conversations/auto-archive.js";
|
|
2
3
|
import { stopCes } from "../credential-execution/ces-runtime.js";
|
|
3
4
|
import { stopHeartbeatService } from "../heartbeat/heartbeat-service.js";
|
|
4
5
|
import { stopCliIpcServer } from "../ipc/assistant-server.js";
|
|
@@ -43,6 +44,7 @@ const log = getLogger("lifecycle");
|
|
|
43
44
|
* leaves a stale PID file or orphaned timers behind.
|
|
44
45
|
*/
|
|
45
46
|
function stopBackgroundServicesAndCleanupPidFile(): void {
|
|
47
|
+
stopConversationAutoArchive();
|
|
46
48
|
stopGatewayFlagListener();
|
|
47
49
|
stopDiskPressureGuardForLifecycle();
|
|
48
50
|
stopResourcePressureGuardForLifecycle();
|
|
@@ -63,6 +65,7 @@ async function shutdown(): Promise<void> {
|
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
65
67
|
shuttingDown = true;
|
|
68
|
+
stopConversationAutoArchive();
|
|
66
69
|
|
|
67
70
|
// Force exit if graceful shutdown takes too long.
|
|
68
71
|
// Set this BEFORE awaiting heartbeat stop so it covers all
|
|
@@ -23,6 +23,10 @@ const pendingFinalizations = new Map<
|
|
|
23
23
|
{ promise: Promise<void>; settle: () => void }
|
|
24
24
|
>();
|
|
25
25
|
|
|
26
|
+
export function hasPendingTurnFinalization(conversationId: string): boolean {
|
|
27
|
+
return pendingFinalizations.has(conversationId);
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
/**
|
|
27
31
|
* Open the barrier for a turn that is starting. Returns the closer, which the
|
|
28
32
|
* turn calls once everything the next turn must not overlap with is done.
|
|
@@ -2,9 +2,14 @@ import { readdir, readFile, readlink, realpath } from "node:fs/promises";
|
|
|
2
2
|
import { createServer } from "node:net";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
5
7
|
import { shouldRestoreDesktopChromeSession } from "./desktop-chrome-session.js";
|
|
6
8
|
|
|
7
9
|
export const DESKTOP_CHROME_WINDOW_CLASS = "vellum-desktop-chrome";
|
|
10
|
+
const DesktopVersionResponseSchema = z.object({
|
|
11
|
+
webSocketDebuggerUrl: z.unknown().optional(),
|
|
12
|
+
});
|
|
8
13
|
|
|
9
14
|
export async function allocateDesktopDebugPort(): Promise<number> {
|
|
10
15
|
const server = createServer();
|
|
@@ -119,9 +124,12 @@ export async function discoverDesktopBrowser(
|
|
|
119
124
|
if (body.length > 16_384) {
|
|
120
125
|
throw new Error("Desktop browser discovery exceeded its size limit");
|
|
121
126
|
}
|
|
122
|
-
const
|
|
127
|
+
const parsed = DesktopVersionResponseSchema.safeParse(JSON.parse(body));
|
|
123
128
|
await assertDesktopListener(pid, port);
|
|
124
|
-
return validateDesktopWebSocket(
|
|
129
|
+
return validateDesktopWebSocket(
|
|
130
|
+
parsed.success ? parsed.data.webSocketDebuggerUrl : undefined,
|
|
131
|
+
port,
|
|
132
|
+
);
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
export function desktopChromeArguments(
|
|
@@ -134,6 +142,7 @@ export function desktopChromeArguments(
|
|
|
134
142
|
`--class=${DESKTOP_CHROME_WINDOW_CLASS}`,
|
|
135
143
|
"--force-renderer-accessibility",
|
|
136
144
|
"--disable-dev-shm-usage",
|
|
145
|
+
`--disk-cache-size=${250 * 1024 * 1024}`,
|
|
137
146
|
"--disable-features=Prerender2",
|
|
138
147
|
...(shouldRestoreDesktopChromeSession(profileDir)
|
|
139
148
|
? ["--restore-last-session", "--hide-crash-restore-bubble"]
|
|
@@ -23,16 +23,22 @@ export function writeDesktopChromePolicy(policyDir: string): void {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
if (
|
|
26
|
+
policy.NetworkPredictionOptions === 2 &&
|
|
26
27
|
policy.CommandLineFlagSecurityWarningsEnabled === false &&
|
|
27
|
-
policy.PasswordManagerEnabled === false
|
|
28
|
+
policy.PasswordManagerEnabled === false &&
|
|
29
|
+
policy.HighEfficiencyModeEnabled === true &&
|
|
30
|
+
policy.MemorySaverModeSavings === 1
|
|
28
31
|
) {
|
|
29
32
|
return;
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
policy.NetworkPredictionOptions = 2;
|
|
32
36
|
// This suppresses command-line warnings; Chrome's sandbox stays unchanged.
|
|
33
37
|
policy.CommandLineFlagSecurityWarningsEnabled = false;
|
|
34
38
|
// Suppress save prompts while keeping previously saved passwords usable.
|
|
35
39
|
policy.PasswordManagerEnabled = false;
|
|
40
|
+
policy.HighEfficiencyModeEnabled = true;
|
|
41
|
+
policy.MemorySaverModeSavings = 1;
|
|
36
42
|
mkdirSync(policyDir, { recursive: true });
|
|
37
43
|
const staging = mkdtempSync(join(policyDir, ".vellum-desktop-"));
|
|
38
44
|
try {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import sharp from "sharp";
|
|
4
|
+
|
|
3
5
|
import { run as click } from "../config/bundled-skills/computer-use/tools/computer-use-click.js";
|
|
4
6
|
import * as configLoader from "../config/loader.js";
|
|
5
7
|
import { HostCuProxy } from "../daemon/host-cu-proxy.js";
|
|
@@ -14,12 +16,20 @@ import {
|
|
|
14
16
|
} from "./desktop-computer-use.js";
|
|
15
17
|
import * as sessionManager from "./desktop-session-manager.js";
|
|
16
18
|
|
|
19
|
+
const SCREENSHOT = (
|
|
20
|
+
await sharp({
|
|
21
|
+
create: { width: 1440, height: 810, channels: 3, background: "white" },
|
|
22
|
+
})
|
|
23
|
+
.jpeg()
|
|
24
|
+
.toBuffer()
|
|
25
|
+
).toString("base64");
|
|
26
|
+
|
|
17
27
|
function driver(userGuidance?: string) {
|
|
18
28
|
return {
|
|
19
29
|
input: mock(async (_args: string[], _signal?: AbortSignal) => {}),
|
|
20
30
|
capture: mock(async (_signal: AbortSignal) => ({
|
|
21
31
|
userGuidance,
|
|
22
|
-
screenshot:
|
|
32
|
+
screenshot: SCREENSHOT,
|
|
23
33
|
screenshotWidthPx: 1440,
|
|
24
34
|
screenshotHeightPx: 810,
|
|
25
35
|
screenWidthPt: 1440,
|
|
@@ -52,7 +62,7 @@ describe("virtual desktop computer use", () => {
|
|
|
52
62
|
signal(),
|
|
53
63
|
backend,
|
|
54
64
|
);
|
|
55
|
-
expect(result.screenshot).toBe(
|
|
65
|
+
expect(result.screenshot).toBe(SCREENSHOT);
|
|
56
66
|
}
|
|
57
67
|
expect(backend.input).not.toHaveBeenCalled();
|
|
58
68
|
expect(backend.capture).toHaveBeenCalledTimes(2);
|
|
@@ -161,7 +171,7 @@ describe("virtual desktop computer use", () => {
|
|
|
161
171
|
);
|
|
162
172
|
expect(backend.input).toHaveBeenCalledTimes(1);
|
|
163
173
|
expect(result.executionError).toBe("Input failed");
|
|
164
|
-
expect(result.screenshot).toBe(
|
|
174
|
+
expect(result.screenshot).toBe(SCREENSHOT);
|
|
165
175
|
});
|
|
166
176
|
|
|
167
177
|
test("done releases only its conversation without starting the desktop", async () => {
|
|
@@ -316,7 +326,7 @@ describe("virtual desktop computer use", () => {
|
|
|
316
326
|
source: {
|
|
317
327
|
type: "base64",
|
|
318
328
|
media_type: "image/jpeg",
|
|
319
|
-
data:
|
|
329
|
+
data: SCREENSHOT,
|
|
320
330
|
},
|
|
321
331
|
},
|
|
322
332
|
]);
|
|
@@ -15,6 +15,7 @@ import { getIsContainerized } from "../config/env-registry.js";
|
|
|
15
15
|
import { connectCdpWsTransport } from "../tools/browser/cdp-client/cdp-inspect/ws-transport.js";
|
|
16
16
|
import { terminateProcessTree } from "../util/host-process.js";
|
|
17
17
|
import { getLogger } from "../util/logger.js";
|
|
18
|
+
import { CHILD_OOM_SCORE_ADJ, withOomScoreAdj } from "../util/oom-priority.js";
|
|
18
19
|
import { getDataDir, getWorkspaceDir } from "../util/platform.js";
|
|
19
20
|
import { sleep } from "../util/retry.js";
|
|
20
21
|
import { DesktopBrowserClient } from "./desktop-browser-client.js";
|
|
@@ -46,7 +47,7 @@ const log = getLogger("desktop-session");
|
|
|
46
47
|
|
|
47
48
|
export const DESKTOP_VNC_PORT = 5999;
|
|
48
49
|
const DESKTOP_GEOMETRY = `${DESKTOP_WIDTH}x${DESKTOP_HEIGHT}`;
|
|
49
|
-
const DESKTOP_LINGER_MS =
|
|
50
|
+
const DESKTOP_LINGER_MS = 8 * 60 * 60_000;
|
|
50
51
|
const VNC_READY_DEADLINE_MS = 10_000;
|
|
51
52
|
const VNC_PROBE_INTERVAL_MS = 100;
|
|
52
53
|
const KILL_GRACE_MS = 2_000;
|
|
@@ -928,8 +929,9 @@ function spawnDetached(
|
|
|
928
929
|
request: DesktopSpawnRequest,
|
|
929
930
|
): DesktopChild {
|
|
930
931
|
// Each child leads its own process group so teardown can kill everything
|
|
931
|
-
// it forked (Chrome's renderers, openbox's autostart) in one signal.
|
|
932
|
-
|
|
932
|
+
// it forked (Chrome's renderers, openbox's autostart) in one signal. The
|
|
933
|
+
// OOM wrapper execs in place, so pid and process group are unchanged.
|
|
934
|
+
return Bun.spawn(withOomScoreAdj(request.cmd, CHILD_OOM_SCORE_ADJ), {
|
|
933
935
|
env: request.env,
|
|
934
936
|
detached: true,
|
|
935
937
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document revision history: snapshots of a document's title and body as they
|
|
3
|
+
* stood at a revision, taken just before a write replaces that state.
|
|
4
|
+
*
|
|
5
|
+
* Snapshot writes run inside the caller's transaction, next to the document
|
|
6
|
+
* write they precede, so a snapshot exists exactly when its write landed.
|
|
7
|
+
*/
|
|
8
|
+
import { rawAll, rawGet, rawRun } from "../persistence/raw-query.js";
|
|
9
|
+
|
|
10
|
+
/** Who made the write a snapshot was taken ahead of. */
|
|
11
|
+
export type DocumentRevisionAuthor = "user" | "assistant";
|
|
12
|
+
|
|
13
|
+
/** Snapshots kept per document; older ones are pruned on each insert. */
|
|
14
|
+
export const MAX_REVISIONS_PER_DOCUMENT = 100;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A user save snapshots the prior state at most this often per document,
|
|
18
|
+
* unless the latest snapshot preceded an assistant edit.
|
|
19
|
+
*/
|
|
20
|
+
export const USER_SNAPSHOT_INTERVAL_MS = 10 * 60 * 1000;
|
|
21
|
+
|
|
22
|
+
export interface DocumentRevisionSummary {
|
|
23
|
+
revision: number;
|
|
24
|
+
author: DocumentRevisionAuthor;
|
|
25
|
+
createdAt: number;
|
|
26
|
+
wordCount: number;
|
|
27
|
+
title: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DocumentRevisionRecord extends DocumentRevisionSummary {
|
|
31
|
+
content: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The document state a snapshot preserves. */
|
|
35
|
+
export interface DocumentSnapshotSource {
|
|
36
|
+
surfaceId: string;
|
|
37
|
+
revision: number;
|
|
38
|
+
title: string;
|
|
39
|
+
content: string;
|
|
40
|
+
wordCount: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface RevisionRow {
|
|
44
|
+
revision: number;
|
|
45
|
+
author: DocumentRevisionAuthor;
|
|
46
|
+
created_at: number;
|
|
47
|
+
word_count: number;
|
|
48
|
+
title: string;
|
|
49
|
+
content: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function mapRowToSummary(
|
|
53
|
+
row: Omit<RevisionRow, "content">,
|
|
54
|
+
): DocumentRevisionSummary {
|
|
55
|
+
return {
|
|
56
|
+
revision: row.revision,
|
|
57
|
+
author: row.author,
|
|
58
|
+
createdAt: row.created_at,
|
|
59
|
+
wordCount: row.word_count,
|
|
60
|
+
title: row.title,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Store `state` as the snapshot for its revision and prune the document's
|
|
66
|
+
* history to the newest {@link MAX_REVISIONS_PER_DOCUMENT}. A revision that is
|
|
67
|
+
* already snapshotted keeps its first snapshot.
|
|
68
|
+
*/
|
|
69
|
+
export function recordDocumentSnapshot(
|
|
70
|
+
state: DocumentSnapshotSource,
|
|
71
|
+
author: DocumentRevisionAuthor,
|
|
72
|
+
): void {
|
|
73
|
+
rawRun(
|
|
74
|
+
"documentRevisions:record",
|
|
75
|
+
/*sql*/ `INSERT OR IGNORE INTO document_revisions (surface_id, revision, title, content, word_count, author, created_at)
|
|
76
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
77
|
+
state.surfaceId,
|
|
78
|
+
state.revision,
|
|
79
|
+
state.title,
|
|
80
|
+
state.content,
|
|
81
|
+
state.wordCount,
|
|
82
|
+
author,
|
|
83
|
+
Date.now(),
|
|
84
|
+
);
|
|
85
|
+
rawRun(
|
|
86
|
+
"documentRevisions:prune",
|
|
87
|
+
/*sql*/ `DELETE FROM document_revisions
|
|
88
|
+
WHERE surface_id = ?
|
|
89
|
+
AND revision NOT IN (
|
|
90
|
+
SELECT revision FROM document_revisions
|
|
91
|
+
WHERE surface_id = ?
|
|
92
|
+
ORDER BY revision DESC
|
|
93
|
+
LIMIT ?
|
|
94
|
+
)`,
|
|
95
|
+
state.surfaceId,
|
|
96
|
+
state.surfaceId,
|
|
97
|
+
MAX_REVISIONS_PER_DOCUMENT,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Whether a user save should snapshot the state it replaces: when the document
|
|
103
|
+
* has no snapshot yet, when the latest one is older than
|
|
104
|
+
* {@link USER_SNAPSHOT_INTERVAL_MS}, or when the latest one preceded an
|
|
105
|
+
* assistant edit, so the assistant's result survives the user's next edits.
|
|
106
|
+
*/
|
|
107
|
+
export function shouldSnapshotBeforeUserSave(surfaceId: string): boolean {
|
|
108
|
+
const latest = rawGet<{ author: string; created_at: number }>(
|
|
109
|
+
"documentRevisions:latest",
|
|
110
|
+
/*sql*/ `SELECT author, created_at FROM document_revisions
|
|
111
|
+
WHERE surface_id = ?
|
|
112
|
+
ORDER BY revision DESC
|
|
113
|
+
LIMIT 1`,
|
|
114
|
+
surfaceId,
|
|
115
|
+
);
|
|
116
|
+
if (!latest) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return (
|
|
120
|
+
latest.author === "assistant" ||
|
|
121
|
+
Date.now() - latest.created_at >= USER_SNAPSHOT_INTERVAL_MS
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** A document's snapshots, newest first, without their bodies. */
|
|
126
|
+
export function listDocumentRevisions(
|
|
127
|
+
surfaceId: string,
|
|
128
|
+
): DocumentRevisionSummary[] {
|
|
129
|
+
return rawAll<Omit<RevisionRow, "content">>(
|
|
130
|
+
"documentRevisions:list",
|
|
131
|
+
/*sql*/ `SELECT revision, author, created_at, word_count, title
|
|
132
|
+
FROM document_revisions
|
|
133
|
+
WHERE surface_id = ?
|
|
134
|
+
ORDER BY revision DESC`,
|
|
135
|
+
surfaceId,
|
|
136
|
+
).map(mapRowToSummary);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** One snapshot with its body, or `null` when it does not exist. */
|
|
140
|
+
export function getDocumentRevision(
|
|
141
|
+
surfaceId: string,
|
|
142
|
+
revision: number,
|
|
143
|
+
): DocumentRevisionRecord | null {
|
|
144
|
+
const row = rawGet<RevisionRow>(
|
|
145
|
+
"documentRevisions:get",
|
|
146
|
+
/*sql*/ `SELECT revision, author, created_at, word_count, title, content
|
|
147
|
+
FROM document_revisions
|
|
148
|
+
WHERE surface_id = ? AND revision = ?`,
|
|
149
|
+
surfaceId,
|
|
150
|
+
revision,
|
|
151
|
+
);
|
|
152
|
+
return row ? { ...mapRowToSummary(row), content: row.content } : null;
|
|
153
|
+
}
|