@vellumai/assistant 0.6.0 → 0.6.2
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 +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +42 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/openapi.yaml +539 -4
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/checker.test.ts +138 -172
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/context-overflow-approval.test.ts +5 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +257 -100
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +63 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +0 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +16 -29
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +4 -27
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/transport-hints-queue.test.ts +77 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/agent/loop.ts +6 -29
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/channels/types.ts +5 -0
- package/src/cli/__tests__/run-assistant-command.ts +56 -0
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
- package/src/cli/commands/oauth/connect.ts +25 -5
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +11 -2
- package/src/cli.ts +1 -120
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
- package/src/config/bundled-skills/gmail/SKILL.md +13 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +85 -3
- package/src/daemon/context-overflow-approval.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +179 -65
- package/src/daemon/conversation-attachments.ts +0 -1
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +30 -8
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +359 -308
- package/src/daemon/conversation-surfaces.ts +65 -0
- package/src/daemon/conversation-tool-setup.ts +44 -17
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +19 -3
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +5 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +70 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +220 -158
- package/src/daemon/message-types/conversations.ts +29 -6
- package/src/daemon/message-types/messages.ts +9 -2
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +18 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +87 -10
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/daemon/transport-hints.ts +33 -0
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +151 -117
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +66 -6
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/embedding-local.ts +1 -1
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -17
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/platform-connection.test.ts +2 -2
- package/src/oauth/seed-providers.ts +1 -0
- package/src/permissions/checker.ts +15 -4
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/prompter.ts +0 -2
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +76 -162
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +30 -9
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/assistant-event-hub.ts +22 -0
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +270 -44
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +41 -278
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -18
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/secret-detection-handler.ts +0 -1
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +3 -5
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -3
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +85 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -24,6 +24,13 @@ function flag(name: string): boolean {
|
|
|
24
24
|
return raw === "true" || raw === "1";
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function int(name: string): number | undefined {
|
|
28
|
+
const raw = str(name);
|
|
29
|
+
if (raw === undefined) return undefined;
|
|
30
|
+
const n = parseInt(raw, 10);
|
|
31
|
+
return Number.isFinite(n) ? n : undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
// ── Registry ─────────────────────────────────────────────────────────────────
|
|
28
35
|
// Each entry documents the env var name, type, default, and purpose.
|
|
29
36
|
|
|
@@ -74,6 +81,57 @@ export function getWorkspaceDirOverride(): string | undefined {
|
|
|
74
81
|
return str("VELLUM_WORKSPACE_DIR");
|
|
75
82
|
}
|
|
76
83
|
|
|
84
|
+
// ── Profiler env vars ───────────────────────────────────────────────────
|
|
85
|
+
// These are injected by the platform when running a managed assistant in
|
|
86
|
+
// profiler mode. The runtime uses them to locate, scope, and budget-limit
|
|
87
|
+
// profiler output on the workspace volume.
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* VELLUM_PROFILER_RUN_ID — string, default: undefined
|
|
91
|
+
* Unique identifier for the current profiler run. When set, the profiler
|
|
92
|
+
* run store treats this run as "active" and will never prune its directory.
|
|
93
|
+
*/
|
|
94
|
+
export function getProfilerRunId(): string | undefined {
|
|
95
|
+
return str("VELLUM_PROFILER_RUN_ID");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* VELLUM_PROFILER_MODE — string, default: undefined
|
|
100
|
+
* The profiling mode to activate (e.g. "cpu", "heap", "cpu+heap").
|
|
101
|
+
* When unset, profiling is disabled.
|
|
102
|
+
*/
|
|
103
|
+
export function getProfilerMode(): string | undefined {
|
|
104
|
+
return str("VELLUM_PROFILER_MODE");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* VELLUM_PROFILER_MAX_BYTES — integer, default: undefined
|
|
109
|
+
* Maximum total bytes retained across all profiler runs (including active).
|
|
110
|
+
* The startup sweep prunes oldest completed runs to stay within budget.
|
|
111
|
+
*/
|
|
112
|
+
export function getProfilerMaxBytes(): number | undefined {
|
|
113
|
+
return int("VELLUM_PROFILER_MAX_BYTES");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* VELLUM_PROFILER_MAX_RUNS — integer, default: undefined
|
|
118
|
+
* Maximum number of completed profiler runs retained on disk.
|
|
119
|
+
* The startup sweep prunes oldest completed runs to stay within budget.
|
|
120
|
+
*/
|
|
121
|
+
export function getProfilerMaxRuns(): number | undefined {
|
|
122
|
+
return int("VELLUM_PROFILER_MAX_RUNS");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* VELLUM_PROFILER_MIN_FREE_MB — integer, default: undefined
|
|
127
|
+
* Minimum free disk space (in megabytes) that must remain after profiler
|
|
128
|
+
* runs are accounted for. The startup sweep prunes oldest completed runs
|
|
129
|
+
* until at least this much free space is available.
|
|
130
|
+
*/
|
|
131
|
+
export function getProfilerMinFreeMb(): number | undefined {
|
|
132
|
+
return int("VELLUM_PROFILER_MIN_FREE_MB");
|
|
133
|
+
}
|
|
134
|
+
|
|
77
135
|
// ── Known env var names ──────────────────────────────────────────────────────
|
|
78
136
|
|
|
79
137
|
/**
|
|
@@ -97,6 +155,11 @@ const KNOWN_VELLUM_VARS = new Set([
|
|
|
97
155
|
"VELLUM_HOOK_SETTINGS",
|
|
98
156
|
"VELLUM_LOCKFILE_DIR",
|
|
99
157
|
"VELLUM_PLATFORM_URL",
|
|
158
|
+
"VELLUM_PROFILER_MAX_BYTES",
|
|
159
|
+
"VELLUM_PROFILER_MAX_RUNS",
|
|
160
|
+
"VELLUM_PROFILER_MIN_FREE_MB",
|
|
161
|
+
"VELLUM_PROFILER_MODE",
|
|
162
|
+
"VELLUM_PROFILER_RUN_ID",
|
|
100
163
|
"VELLUM_ROOT_DIR",
|
|
101
164
|
"VELLUM_SSH_USER",
|
|
102
165
|
"VELLUM_UNSAFE_AUTH_BYPASS",
|
|
@@ -121,6 +121,14 @@
|
|
|
121
121
|
"description": "Show the Billing tab in Settings when signed in, displaying credits balance and top-up",
|
|
122
122
|
"defaultEnabled": true
|
|
123
123
|
},
|
|
124
|
+
{
|
|
125
|
+
"id": "referral-codes",
|
|
126
|
+
"scope": "macos",
|
|
127
|
+
"key": "referral-codes",
|
|
128
|
+
"label": "Referral Codes",
|
|
129
|
+
"description": "Surface the Earn Credits referral entry points (sidebar drawer row and Billing tab button) that open the referral modal",
|
|
130
|
+
"defaultEnabled": true
|
|
131
|
+
},
|
|
124
132
|
{
|
|
125
133
|
"id": "managed-sign-in",
|
|
126
134
|
"scope": "macos",
|
|
@@ -199,7 +207,7 @@
|
|
|
199
207
|
"key": "show-thinking-blocks",
|
|
200
208
|
"label": "Show Thinking Blocks",
|
|
201
209
|
"description": "Display the assistant's thinking/reasoning inline in chat messages as collapsible blocks",
|
|
202
|
-
"defaultEnabled":
|
|
210
|
+
"defaultEnabled": true
|
|
203
211
|
},
|
|
204
212
|
{
|
|
205
213
|
"id": "inline-skill-commands",
|
|
@@ -272,6 +280,14 @@
|
|
|
272
280
|
"label": "Teleport",
|
|
273
281
|
"description": "Enable teleport UI in General settings for moving assistants between hosting environments",
|
|
274
282
|
"defaultEnabled": false
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"id": "permission-controls-v2",
|
|
286
|
+
"scope": "assistant",
|
|
287
|
+
"key": "permission-controls-v2",
|
|
288
|
+
"label": "Permission Controls V2",
|
|
289
|
+
"description": "Replace risk-level permission system with two independent controls: 'Ask before acting' (LLM behavior toggle) and 'Host access' (system-enforced gate)",
|
|
290
|
+
"defaultEnabled": false
|
|
275
291
|
}
|
|
276
292
|
]
|
|
277
293
|
}
|
package/src/config/schema.ts
CHANGED
|
@@ -3,6 +3,11 @@ import { z } from "zod";
|
|
|
3
3
|
import { getDataDir } from "../util/platform.js";
|
|
4
4
|
|
|
5
5
|
// Re-export all domain schemas
|
|
6
|
+
export type { PermissionMode } from "../permissions/permission-mode.js";
|
|
7
|
+
export {
|
|
8
|
+
DEFAULT_PERMISSION_MODE,
|
|
9
|
+
PermissionModeSchema,
|
|
10
|
+
} from "../permissions/permission-mode.js";
|
|
6
11
|
export type { AcpAgentConfig, AcpConfig } from "./acp-schema.js";
|
|
7
12
|
export { AcpAgentConfigSchema, AcpConfigSchema } from "./acp-schema.js";
|
|
8
13
|
export type {
|
|
@@ -143,6 +148,7 @@ export type {
|
|
|
143
148
|
export {
|
|
144
149
|
PermissionsConfigSchema,
|
|
145
150
|
SecretDetectionConfigSchema,
|
|
151
|
+
VALID_PERMISSIONS_MODES,
|
|
146
152
|
} from "./schemas/security.js";
|
|
147
153
|
export type {
|
|
148
154
|
ImageGenerationService,
|
|
@@ -197,6 +203,7 @@ import {
|
|
|
197
203
|
WhatsAppConfigSchema,
|
|
198
204
|
} from "./schemas/channels.js";
|
|
199
205
|
import { ElevenLabsConfigSchema } from "./schemas/elevenlabs.js";
|
|
206
|
+
import { FilingConfigSchema } from "./schemas/filing.js";
|
|
200
207
|
import { FishAudioConfigSchema } from "./schemas/fish-audio.js";
|
|
201
208
|
import { HeartbeatConfigSchema } from "./schemas/heartbeat.js";
|
|
202
209
|
import {
|
|
@@ -272,6 +279,7 @@ export const AssistantConfigSchema = z
|
|
|
272
279
|
.describe(
|
|
273
280
|
"Custom pricing overrides for specific provider/model combinations",
|
|
274
281
|
),
|
|
282
|
+
filing: FilingConfigSchema.default(FilingConfigSchema.parse({})),
|
|
275
283
|
heartbeat: HeartbeatConfigSchema.default(HeartbeatConfigSchema.parse({})),
|
|
276
284
|
journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
|
|
277
285
|
mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { SpeedSchema } from "./inference.js";
|
|
4
|
+
|
|
5
|
+
export const FilingConfigSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
enabled: z
|
|
8
|
+
.boolean({ error: "filing.enabled must be a boolean" })
|
|
9
|
+
.default(true)
|
|
10
|
+
.describe(
|
|
11
|
+
"Whether periodic PKB filing is enabled — processes buffer.md into topic files and reviews knowledge base organization",
|
|
12
|
+
),
|
|
13
|
+
intervalMs: z
|
|
14
|
+
.number({ error: "filing.intervalMs must be a number" })
|
|
15
|
+
.int("filing.intervalMs must be an integer")
|
|
16
|
+
.positive("filing.intervalMs must be a positive integer")
|
|
17
|
+
.default(4 * 3_600_000)
|
|
18
|
+
.describe("Time between filing runs in milliseconds"),
|
|
19
|
+
speed: SpeedSchema.default("standard").describe(
|
|
20
|
+
"Inference speed mode for filing conversations",
|
|
21
|
+
),
|
|
22
|
+
activeHoursStart: z
|
|
23
|
+
.number({ error: "filing.activeHoursStart must be a number" })
|
|
24
|
+
.int("filing.activeHoursStart must be an integer")
|
|
25
|
+
.min(0, "filing.activeHoursStart must be >= 0")
|
|
26
|
+
.max(23, "filing.activeHoursStart must be <= 23")
|
|
27
|
+
.default(8)
|
|
28
|
+
.describe("Hour of the day (0-23) when filing runs begin"),
|
|
29
|
+
activeHoursEnd: z
|
|
30
|
+
.number({ error: "filing.activeHoursEnd must be a number" })
|
|
31
|
+
.int("filing.activeHoursEnd must be an integer")
|
|
32
|
+
.min(0, "filing.activeHoursEnd must be >= 0")
|
|
33
|
+
.max(23, "filing.activeHoursEnd must be <= 23")
|
|
34
|
+
.default(22)
|
|
35
|
+
.describe("Hour of the day (0-23) when filing runs stop"),
|
|
36
|
+
})
|
|
37
|
+
.describe(
|
|
38
|
+
"Periodic PKB (personal knowledge base) filing — processes the buffer into topic files and maintains knowledge organization",
|
|
39
|
+
)
|
|
40
|
+
.superRefine((config, ctx) => {
|
|
41
|
+
if (config.activeHoursStart === config.activeHoursEnd) {
|
|
42
|
+
ctx.addIssue({
|
|
43
|
+
code: z.ZodIssueCode.custom,
|
|
44
|
+
path: ["activeHoursEnd"],
|
|
45
|
+
message:
|
|
46
|
+
"filing.activeHoursStart and filing.activeHoursEnd must not be equal (would create an empty window)",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export type FilingConfig = z.infer<typeof FilingConfigSchema>;
|
|
@@ -12,7 +12,7 @@ export const HeartbeatConfigSchema = z
|
|
|
12
12
|
.number({ error: "heartbeat.intervalMs must be a number" })
|
|
13
13
|
.int("heartbeat.intervalMs must be an integer")
|
|
14
14
|
.positive("heartbeat.intervalMs must be a positive integer")
|
|
15
|
-
.default(3_600_000)
|
|
15
|
+
.default(6 * 3_600_000)
|
|
16
16
|
.describe("Time between heartbeat checks in milliseconds"),
|
|
17
17
|
speed: SpeedSchema.default("standard").describe(
|
|
18
18
|
"Inference speed mode for heartbeat conversations — defaults to standard to avoid inheriting the global fast mode multiplier",
|
|
@@ -22,35 +22,38 @@ export const HeartbeatConfigSchema = z
|
|
|
22
22
|
.int("heartbeat.activeHoursStart must be an integer")
|
|
23
23
|
.min(0, "heartbeat.activeHoursStart must be >= 0")
|
|
24
24
|
.max(23, "heartbeat.activeHoursStart must be <= 23")
|
|
25
|
-
.
|
|
25
|
+
.nullable()
|
|
26
|
+
.default(8)
|
|
26
27
|
.describe(
|
|
27
|
-
"Hour of the day (0-23) when heartbeat checks begin
|
|
28
|
+
"Hour of the day (0-23) when heartbeat checks begin, or null to disable active hours restriction",
|
|
28
29
|
),
|
|
29
30
|
activeHoursEnd: z
|
|
30
31
|
.number({ error: "heartbeat.activeHoursEnd must be a number" })
|
|
31
32
|
.int("heartbeat.activeHoursEnd must be an integer")
|
|
32
33
|
.min(0, "heartbeat.activeHoursEnd must be >= 0")
|
|
33
34
|
.max(23, "heartbeat.activeHoursEnd must be <= 23")
|
|
34
|
-
.
|
|
35
|
+
.nullable()
|
|
36
|
+
.default(22)
|
|
35
37
|
.describe(
|
|
36
|
-
"Hour of the day (0-23) when heartbeat checks stop
|
|
38
|
+
"Hour of the day (0-23) when heartbeat checks stop, or null to disable active hours restriction",
|
|
37
39
|
),
|
|
38
40
|
})
|
|
39
41
|
.describe("Periodic heartbeat configuration for health monitoring")
|
|
40
42
|
.superRefine((config, ctx) => {
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
43
|
+
const startNull = config.activeHoursStart == null;
|
|
44
|
+
const endNull = config.activeHoursEnd == null;
|
|
45
|
+
if (startNull !== endNull) {
|
|
44
46
|
ctx.addIssue({
|
|
45
47
|
code: z.ZodIssueCode.custom,
|
|
46
|
-
path: [
|
|
48
|
+
path: [startNull ? "activeHoursStart" : "activeHoursEnd"],
|
|
47
49
|
message:
|
|
48
|
-
"heartbeat.activeHoursStart and heartbeat.activeHoursEnd must both be set or both be
|
|
50
|
+
"heartbeat.activeHoursStart and heartbeat.activeHoursEnd must both be set or both be null",
|
|
49
51
|
});
|
|
52
|
+
return;
|
|
50
53
|
}
|
|
51
54
|
if (
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
config.activeHoursStart != null &&
|
|
56
|
+
config.activeHoursEnd != null &&
|
|
54
57
|
config.activeHoursStart === config.activeHoursEnd
|
|
55
58
|
) {
|
|
56
59
|
ctx.addIssue({
|
|
@@ -72,6 +72,18 @@ export const MemoryCleanupConfigSchema = z
|
|
|
72
72
|
.describe(
|
|
73
73
|
"Number of days to retain conversation data before cleanup (0 disables pruning)",
|
|
74
74
|
),
|
|
75
|
+
llmRequestLogRetentionMs: z
|
|
76
|
+
.number({
|
|
77
|
+
error: "memory.cleanup.llmRequestLogRetentionMs must be a number",
|
|
78
|
+
})
|
|
79
|
+
.int("memory.cleanup.llmRequestLogRetentionMs must be an integer")
|
|
80
|
+
.nonnegative(
|
|
81
|
+
"memory.cleanup.llmRequestLogRetentionMs must be non-negative",
|
|
82
|
+
)
|
|
83
|
+
.default(7 * 24 * 60 * 60 * 1000)
|
|
84
|
+
.describe(
|
|
85
|
+
"Retention period for LLM request/response logs in milliseconds (0 disables pruning)",
|
|
86
|
+
),
|
|
75
87
|
})
|
|
76
88
|
.describe("Automatic memory cleanup and garbage collection settings");
|
|
77
89
|
|
|
@@ -79,6 +79,20 @@ export const PermissionsConfigSchema = z
|
|
|
79
79
|
.describe(
|
|
80
80
|
"Permission mode — 'strict' requires explicit approval for all operations, 'workspace' allows operations within the workspace",
|
|
81
81
|
),
|
|
82
|
+
askBeforeActing: z
|
|
83
|
+
.boolean({
|
|
84
|
+
error: "permissions.askBeforeActing must be a boolean",
|
|
85
|
+
})
|
|
86
|
+
.default(true)
|
|
87
|
+
.describe("Whether the assistant should check in before taking actions"),
|
|
88
|
+
hostAccess: z
|
|
89
|
+
.boolean({
|
|
90
|
+
error: "permissions.hostAccess must be a boolean",
|
|
91
|
+
})
|
|
92
|
+
.default(false)
|
|
93
|
+
.describe(
|
|
94
|
+
"Whether the assistant can execute commands on the host machine without prompting",
|
|
95
|
+
),
|
|
82
96
|
})
|
|
83
97
|
.describe("Permission enforcement mode for tool operations");
|
|
84
98
|
|
|
@@ -56,6 +56,11 @@ export const OutlookOAuthServiceSchema = BaseServiceSchema.extend({
|
|
|
56
56
|
});
|
|
57
57
|
export type OutlookOAuthService = z.infer<typeof OutlookOAuthServiceSchema>;
|
|
58
58
|
|
|
59
|
+
export const LinearOAuthServiceSchema = BaseServiceSchema.extend({
|
|
60
|
+
mode: ServiceModeSchema.default("your-own"),
|
|
61
|
+
});
|
|
62
|
+
export type LinearOAuthService = z.infer<typeof LinearOAuthServiceSchema>;
|
|
63
|
+
|
|
59
64
|
export const ServicesSchema = z.object({
|
|
60
65
|
inference: InferenceServiceSchema.default(InferenceServiceSchema.parse({})),
|
|
61
66
|
"image-generation": ImageGenerationServiceSchema.default(
|
|
@@ -70,5 +75,8 @@ export const ServicesSchema = z.object({
|
|
|
70
75
|
"outlook-oauth": OutlookOAuthServiceSchema.default(
|
|
71
76
|
OutlookOAuthServiceSchema.parse({}),
|
|
72
77
|
),
|
|
78
|
+
"linear-oauth": LinearOAuthServiceSchema.default(
|
|
79
|
+
LinearOAuthServiceSchema.parse({}),
|
|
80
|
+
),
|
|
73
81
|
});
|
|
74
82
|
export type Services = z.infer<typeof ServicesSchema>;
|
|
@@ -220,7 +220,6 @@ export async function bridgeCesApproval(
|
|
|
220
220
|
[], // No allowlist options — CES manages its own grant patterns
|
|
221
221
|
[], // No scope options — CES manages scope internally
|
|
222
222
|
undefined, // No file diff
|
|
223
|
-
undefined, // Not sandboxed
|
|
224
223
|
options?.conversationId,
|
|
225
224
|
"host", // CES operations target the host
|
|
226
225
|
false, // Persistent decisions are managed by CES, not trust.json
|
|
@@ -130,16 +130,12 @@ export async function fetchManagedCatalog(): Promise<FetchManagedCatalogResult>
|
|
|
130
130
|
|
|
131
131
|
return { ok: true, descriptors };
|
|
132
132
|
} catch (err) {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
/Api-Key\s+\S+/gi,
|
|
136
|
-
"Api-Key [REDACTED]",
|
|
137
|
-
);
|
|
138
|
-
log.warn(`Failed to fetch managed CES catalog: ${safeMessage}`);
|
|
133
|
+
const errorName = err instanceof Error ? err.constructor.name : "Unknown";
|
|
134
|
+
log.warn(`Failed to fetch managed CES catalog (${errorName})`);
|
|
139
135
|
return {
|
|
140
136
|
ok: false,
|
|
141
137
|
descriptors: [],
|
|
142
|
-
error: `Failed to fetch managed CES catalog
|
|
138
|
+
error: `Failed to fetch managed CES catalog (${errorName})`,
|
|
143
139
|
};
|
|
144
140
|
}
|
|
145
141
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem watcher for app source directories.
|
|
3
|
+
*
|
|
4
|
+
* Watches the apps root directory recursively using fs.watch (FSEvents on
|
|
5
|
+
* macOS). When a source file changes, debounces per app ID and calls the
|
|
6
|
+
* provided callback so the server can recompile + refresh surfaces.
|
|
7
|
+
*
|
|
8
|
+
* This catches ALL modification sources (file_edit, file_write, bash, etc.)
|
|
9
|
+
* without relying on individual tool hooks.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, type FSWatcher, watch } from "node:fs";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
getAppsDir,
|
|
16
|
+
resolveAppIdByDirName,
|
|
17
|
+
} from "../memory/app-store.js";
|
|
18
|
+
import { DebouncerMap } from "../util/debounce.js";
|
|
19
|
+
import { getLogger } from "../util/logger.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("app-source-watcher");
|
|
22
|
+
|
|
23
|
+
const APP_REFRESH_DEBOUNCE_MS = 500;
|
|
24
|
+
|
|
25
|
+
export type AppSourceChangeCallback = (appId: string) => void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve app ID from a relative path within the apps directory.
|
|
29
|
+
* Returns null if the path is not an app source file (e.g. dist/, records/).
|
|
30
|
+
*/
|
|
31
|
+
function resolveAppIdFromRelPath(relPath: string): string | null {
|
|
32
|
+
const slashIdx = relPath.indexOf("/");
|
|
33
|
+
if (slashIdx === -1) return null; // file directly in apps/ (e.g. .json definition)
|
|
34
|
+
|
|
35
|
+
const dirName = relPath.slice(0, slashIdx);
|
|
36
|
+
const innerPath = relPath.slice(slashIdx + 1);
|
|
37
|
+
|
|
38
|
+
// Skip non-source directories (include bare directory names for fs.watch events)
|
|
39
|
+
if (
|
|
40
|
+
innerPath === "records" || innerPath.startsWith("records/") ||
|
|
41
|
+
innerPath === "dist" || innerPath.startsWith("dist/")
|
|
42
|
+
) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return resolveAppIdByDirName(dirName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class AppSourceWatcher {
|
|
50
|
+
private watcher: FSWatcher | null = null;
|
|
51
|
+
private debouncer = new DebouncerMap({
|
|
52
|
+
defaultDelayMs: APP_REFRESH_DEBOUNCE_MS,
|
|
53
|
+
maxEntries: 50,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
start(onChange: AppSourceChangeCallback): void {
|
|
57
|
+
let appsDir: string;
|
|
58
|
+
try {
|
|
59
|
+
appsDir = getAppsDir();
|
|
60
|
+
} catch {
|
|
61
|
+
log.warn("Could not resolve apps directory; app source watching disabled");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!existsSync(appsDir)) {
|
|
66
|
+
log.info("Apps directory does not exist yet; skipping source watcher");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
this.watcher = watch(appsDir, { recursive: true }, (_eventType, filename) => {
|
|
72
|
+
if (!filename) return;
|
|
73
|
+
|
|
74
|
+
const appId = resolveAppIdFromRelPath(filename);
|
|
75
|
+
if (!appId) return;
|
|
76
|
+
|
|
77
|
+
this.debouncer.schedule(`app:${appId}`, () => {
|
|
78
|
+
onChange(appId);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
} catch (err) {
|
|
82
|
+
log.warn({ err }, "Failed to watch apps directory; source watching disabled");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
stop(): void {
|
|
87
|
+
this.debouncer.cancelAll();
|
|
88
|
+
if (this.watcher) {
|
|
89
|
+
this.watcher.close();
|
|
90
|
+
this.watcher = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -28,7 +28,10 @@ import { handleUserMessageSignal } from "../signals/user-message.js";
|
|
|
28
28
|
import { DebouncerMap } from "../util/debounce.js";
|
|
29
29
|
import { getLogger } from "../util/logger.js";
|
|
30
30
|
import {
|
|
31
|
+
AVATAR_IMAGE_FILENAME,
|
|
32
|
+
getAvatarDir,
|
|
31
33
|
getSignalsDir,
|
|
34
|
+
getSoundsDir,
|
|
32
35
|
getWorkspaceDir,
|
|
33
36
|
getWorkspaceSkillsDir,
|
|
34
37
|
} from "../util/platform.js";
|
|
@@ -110,7 +113,14 @@ export class ConfigWatcher {
|
|
|
110
113
|
* files change and conversations need to be evicted for reload.
|
|
111
114
|
* `onIdentityChanged` is called when IDENTITY.md changes on disk.
|
|
112
115
|
*/
|
|
113
|
-
start(
|
|
116
|
+
start(
|
|
117
|
+
onConversationEvict: () => void,
|
|
118
|
+
onIdentityChanged?: () => void,
|
|
119
|
+
onSoundsConfigChanged?: () => void,
|
|
120
|
+
onAvatarChanged?: () => void,
|
|
121
|
+
onConfigChanged?: () => void,
|
|
122
|
+
onFeatureFlagsChanged?: () => void,
|
|
123
|
+
): void {
|
|
114
124
|
const workspaceDir = getWorkspaceDir();
|
|
115
125
|
|
|
116
126
|
const workspaceHandlers: Record<string, () => void> = {
|
|
@@ -122,6 +132,7 @@ export class ConfigWatcher {
|
|
|
122
132
|
const changed = await this.refreshConfigFromSources();
|
|
123
133
|
if (changed) {
|
|
124
134
|
onConversationEvict();
|
|
135
|
+
onConfigChanged?.();
|
|
125
136
|
const newConfig = getConfig();
|
|
126
137
|
const newMcpFingerprint = JSON.stringify(newConfig.mcp ?? {});
|
|
127
138
|
if (newMcpFingerprint !== prevMcpFingerprint) {
|
|
@@ -175,7 +186,14 @@ export class ConfigWatcher {
|
|
|
175
186
|
"workspace directory for config/prompt changes",
|
|
176
187
|
);
|
|
177
188
|
|
|
178
|
-
|
|
189
|
+
if (onSoundsConfigChanged) {
|
|
190
|
+
this.startSoundsWatcher(onSoundsConfigChanged);
|
|
191
|
+
}
|
|
192
|
+
if (onAvatarChanged) {
|
|
193
|
+
this.startAvatarWatcher(onAvatarChanged);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this.startFeatureFlagsWatcher(onFeatureFlagsChanged);
|
|
179
197
|
this.startSignalsWatcher();
|
|
180
198
|
this.startSkillsWatchers(onConversationEvict);
|
|
181
199
|
}
|
|
@@ -188,7 +206,70 @@ export class ConfigWatcher {
|
|
|
188
206
|
this.watchers = [];
|
|
189
207
|
}
|
|
190
208
|
|
|
191
|
-
private
|
|
209
|
+
private startSoundsWatcher(onSoundsConfigChanged: () => void): void {
|
|
210
|
+
const soundsDir = getSoundsDir();
|
|
211
|
+
try {
|
|
212
|
+
if (!existsSync(soundsDir)) {
|
|
213
|
+
mkdirSync(soundsDir, { recursive: true });
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
// If we can't create it, watching will also fail — handled below.
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const watcher = watch(soundsDir, (_eventType, filename) => {
|
|
221
|
+
if (!filename) return;
|
|
222
|
+
this.debounceTimers.schedule("file:sounds", () => {
|
|
223
|
+
log.info(
|
|
224
|
+
{ file: String(filename) },
|
|
225
|
+
"Sounds directory changed, notifying clients",
|
|
226
|
+
);
|
|
227
|
+
onSoundsConfigChanged();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
this.watchers.push(watcher);
|
|
231
|
+
log.info({ dir: soundsDir }, "Watching sounds directory for changes");
|
|
232
|
+
} catch (err) {
|
|
233
|
+
log.warn(
|
|
234
|
+
{ err, dir: soundsDir },
|
|
235
|
+
"Failed to watch sounds directory. Sound config changes will require a restart.",
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private startAvatarWatcher(onAvatarChanged: () => void): void {
|
|
241
|
+
const avatarDir = getAvatarDir();
|
|
242
|
+
try {
|
|
243
|
+
if (!existsSync(avatarDir)) {
|
|
244
|
+
mkdirSync(avatarDir, { recursive: true });
|
|
245
|
+
}
|
|
246
|
+
} catch {
|
|
247
|
+
// If we can't create it, watching will also fail — handled below.
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const watcher = watch(avatarDir, (_eventType, filename) => {
|
|
252
|
+
if (!filename) return;
|
|
253
|
+
if (String(filename) !== AVATAR_IMAGE_FILENAME) return;
|
|
254
|
+
this.debounceTimers.schedule("file:avatar", () => {
|
|
255
|
+
log.info(
|
|
256
|
+
{ file: String(filename) },
|
|
257
|
+
"Avatar image changed, notifying clients",
|
|
258
|
+
);
|
|
259
|
+
onAvatarChanged();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
this.watchers.push(watcher);
|
|
263
|
+
log.info({ dir: avatarDir }, "Watching avatar directory for changes");
|
|
264
|
+
} catch (err) {
|
|
265
|
+
log.warn(
|
|
266
|
+
{ err, dir: avatarDir },
|
|
267
|
+
"Failed to watch avatar directory. Avatar changes will require a restart.",
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private startFeatureFlagsWatcher(onFeatureFlagsChanged?: () => void): void {
|
|
192
273
|
const protectedDir = process.env.GATEWAY_SECURITY_DIR
|
|
193
274
|
? process.env.GATEWAY_SECURITY_DIR
|
|
194
275
|
: join(homedir(), ".vellum", "protected");
|
|
@@ -219,6 +300,7 @@ export class ConfigWatcher {
|
|
|
219
300
|
"Feature flags file changed, invalidating cache",
|
|
220
301
|
);
|
|
221
302
|
clearFeatureFlagOverridesCache();
|
|
303
|
+
onFeatureFlagsChanged?.();
|
|
222
304
|
},
|
|
223
305
|
500,
|
|
224
306
|
);
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
} from "../memory/llm-request-log-store.js";
|
|
28
28
|
import { backfillMemoryRecallLogMessageId } from "../memory/memory-recall-log-store.js";
|
|
29
29
|
import type { ContentBlock, ImageContent } from "../providers/types.js";
|
|
30
|
+
import { ProviderError } from "../util/errors.js";
|
|
30
31
|
import { getLogger } from "../util/logger.js";
|
|
31
32
|
import type { DirectiveRequest } from "./assistant-attachments.js";
|
|
32
33
|
import {
|
|
@@ -630,6 +631,25 @@ export function handleError(
|
|
|
630
631
|
state.orderingErrorDetected = true;
|
|
631
632
|
state.deferredOrderingError = event.error.message;
|
|
632
633
|
} else {
|
|
634
|
+
if (classified.errorCategory === "provider_api_error") {
|
|
635
|
+
log.error(
|
|
636
|
+
{
|
|
637
|
+
conversationId: deps.ctx.conversationId,
|
|
638
|
+
errorCode: classified.code,
|
|
639
|
+
errorCategory: classified.errorCategory,
|
|
640
|
+
statusCode:
|
|
641
|
+
event.error instanceof ProviderError
|
|
642
|
+
? event.error.statusCode
|
|
643
|
+
: undefined,
|
|
644
|
+
provider:
|
|
645
|
+
event.error instanceof ProviderError
|
|
646
|
+
? event.error.provider
|
|
647
|
+
: undefined,
|
|
648
|
+
errorMessage: event.error.message,
|
|
649
|
+
},
|
|
650
|
+
"Provider rejected request with unclassified 4xx error",
|
|
651
|
+
);
|
|
652
|
+
}
|
|
633
653
|
deps.onEvent(
|
|
634
654
|
buildConversationErrorMessage(deps.ctx.conversationId, classified),
|
|
635
655
|
);
|