@vellumai/assistant 0.4.53 → 0.4.55
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/bun.lock +62 -349
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/keychain-broker.md +94 -29
- package/docs/architecture/security.md +2 -2
- package/knip.json +7 -29
- package/package.json +2 -9
- package/src/__tests__/agent-loop.test.ts +1 -1
- package/src/__tests__/app-git-history.test.ts +0 -2
- package/src/__tests__/app-git-service.test.ts +1 -6
- package/src/__tests__/approval-cascade.test.ts +0 -1
- package/src/__tests__/avatar-e2e.test.ts +0 -1
- package/src/__tests__/browser-fill-credential.test.ts +1 -6
- package/src/__tests__/call-domain.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +4 -4
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +13 -11
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -1
- package/src/__tests__/claude-code-tool-profiles.test.ts +1 -2
- package/src/__tests__/config-loader-backfill.test.ts +0 -3
- package/src/__tests__/config-schema.test.ts +3 -9
- package/src/__tests__/config-watcher.test.ts +11 -3
- package/src/__tests__/credential-broker-browser-fill.test.ts +27 -24
- package/src/__tests__/credential-broker-server-use.test.ts +60 -24
- package/src/__tests__/credential-security-e2e.test.ts +1 -6
- package/src/__tests__/credential-security-invariants.test.ts +13 -8
- package/src/__tests__/credential-vault-unit.test.ts +28 -12
- package/src/__tests__/credential-vault.test.ts +40 -28
- package/src/__tests__/credentials-cli.test.ts +1 -21
- package/src/__tests__/email-invite-adapter.test.ts +0 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +3 -3
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -79
- package/src/__tests__/gateway-only-enforcement.test.ts +1 -21
- package/src/__tests__/guardian-action-conversation-turn.test.ts +8 -8
- package/src/__tests__/guardian-action-late-reply.test.ts +13 -14
- package/src/__tests__/guardian-action-store.test.ts +0 -57
- package/src/__tests__/guardian-outbound-http.test.ts +1 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -3
- package/src/__tests__/hooks-blocking.test.ts +1 -1
- package/src/__tests__/hooks-config.test.ts +5 -29
- package/src/__tests__/hooks-discovery.test.ts +1 -1
- package/src/__tests__/hooks-integration.test.ts +1 -1
- package/src/__tests__/hooks-manager.test.ts +1 -1
- package/src/__tests__/hooks-runner.test.ts +1 -23
- package/src/__tests__/hooks-settings.test.ts +1 -1
- package/src/__tests__/hooks-templates.test.ts +1 -1
- package/src/__tests__/integration-status.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +0 -3
- package/src/__tests__/list-messages-attachments.test.ts +4 -4
- package/src/__tests__/llm-usage-store.test.ts +50 -0
- package/src/__tests__/managed-proxy-context.test.ts +41 -41
- package/src/__tests__/media-generate-image.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -6
- package/src/__tests__/memory-regressions.experimental.test.ts +4 -4
- package/src/__tests__/memory-regressions.test.ts +27 -27
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -4
- package/src/__tests__/notification-decision-fallback.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +1 -4
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/openai-provider.test.ts +7 -7
- package/src/__tests__/platform.test.ts +14 -4
- package/src/__tests__/pricing.test.ts +0 -223
- package/src/__tests__/provider-commit-message-generator.test.ts +1 -4
- package/src/__tests__/provider-fail-open-selection.test.ts +58 -54
- package/src/__tests__/provider-managed-proxy-integration.test.ts +63 -63
- package/src/__tests__/provider-registry-ollama.test.ts +3 -3
- package/src/__tests__/public-ingress-urls.test.ts +1 -1
- package/src/__tests__/registry.test.ts +3 -103
- package/src/__tests__/script-proxy-injection-runtime.test.ts +2 -7
- package/src/__tests__/secret-onetime-send.test.ts +1 -6
- package/src/__tests__/secret-routes-managed-proxy.test.ts +6 -13
- package/src/__tests__/secure-keys.test.ts +241 -229
- package/src/__tests__/session-abort-tool-results.test.ts +0 -1
- package/src/__tests__/session-confirmation-signals.test.ts +0 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -7
- package/src/__tests__/session-pre-run-repair.test.ts +0 -1
- package/src/__tests__/session-provider-retry-repair.test.ts +0 -1
- package/src/__tests__/session-queue.test.ts +2 -4
- package/src/__tests__/session-slash-known.test.ts +0 -1
- package/src/__tests__/session-slash-queue.test.ts +0 -1
- package/src/__tests__/session-slash-unknown.test.ts +0 -1
- package/src/__tests__/session-workspace-injection.test.ts +0 -1
- package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -7
- package/src/__tests__/swarm-recursion.test.ts +0 -1
- package/src/__tests__/swarm-session-integration.test.ts +0 -1
- package/src/__tests__/swarm-tool.test.ts +0 -1
- package/src/__tests__/task-compiler.test.ts +1 -1
- package/src/__tests__/test-support/browser-skill-harness.ts +0 -18
- package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -23
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trust-store.test.ts +3 -82
- package/src/__tests__/twilio-config.test.ts +0 -1
- package/src/__tests__/twilio-provider.test.ts +0 -5
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/usage-cache-backfill-migration.test.ts +10 -10
- package/src/calls/guardian-question-copy.ts +1 -1
- package/src/cli/commands/bash.ts +3 -0
- package/src/cli/commands/doctor.ts +10 -34
- package/src/cli/commands/memory.ts +3 -5
- package/src/cli/commands/sessions.ts +1 -1
- package/src/cli/commands/usage.ts +359 -0
- package/src/cli/http-client.ts +22 -12
- package/src/cli/program.ts +2 -0
- package/src/cli/reference.ts +1 -0
- package/src/cli.ts +251 -181
- package/src/config/assistant-feature-flags.ts +0 -7
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +0 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -1
- package/src/config/bundled-skills/sequences/SKILL.md +0 -1
- package/src/config/env.ts +13 -0
- package/src/config/feature-flag-registry.json +9 -41
- package/src/config/schemas/security.ts +1 -2
- package/src/config/skills.ts +1 -1
- package/src/contacts/contact-store.ts +0 -50
- package/src/daemon/approved-devices-store.ts +0 -44
- package/src/daemon/classifier.ts +1 -1
- package/src/daemon/config-watcher.ts +14 -8
- package/src/daemon/handlers/config-model.ts +1 -1
- package/src/daemon/handlers/sessions.ts +4 -116
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/lifecycle.ts +13 -15
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +20 -3
- package/src/daemon/session-slash.ts +2 -2
- package/src/daemon/shutdown-handlers.ts +15 -0
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/guardrails.ts +1 -1
- package/src/email/service.ts +0 -5
- package/src/hooks/templates.ts +1 -1
- package/src/media/app-icon-generator.ts +2 -2
- package/src/media/avatar-router.ts +2 -2
- package/src/media/gemini-image-service.ts +5 -5
- package/src/memory/admin.ts +2 -2
- package/src/memory/app-git-service.ts +0 -7
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-title-service.ts +2 -2
- package/src/memory/embedding-backend.ts +30 -26
- package/src/memory/external-conversation-store.ts +0 -30
- package/src/memory/guardian-action-store.ts +0 -31
- package/src/memory/guardian-approvals.ts +1 -56
- package/src/memory/indexer.ts +4 -3
- package/src/memory/items-extractor.ts +1 -1
- package/src/memory/job-handlers/backfill.ts +5 -2
- package/src/memory/job-handlers/index-maintenance.ts +2 -2
- package/src/memory/job-handlers/media-processing.ts +2 -2
- package/src/memory/job-handlers/summarization.ts +1 -1
- package/src/memory/job-utils.ts +1 -2
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/llm-usage-store.ts +57 -11
- package/src/memory/media-store.ts +4 -535
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +2 -2
- package/src/memory/migrations/110-channel-guardian.ts +0 -1
- package/src/memory/published-pages-store.ts +0 -83
- package/src/memory/qdrant-circuit-breaker.ts +0 -8
- package/src/memory/retriever.ts +1 -1
- package/src/memory/schema/calls.ts +0 -67
- package/src/memory/search/semantic.ts +1 -8
- package/src/memory/shared-app-links-store.ts +0 -15
- package/src/messaging/registry.ts +0 -5
- package/src/messaging/style-analyzer.ts +1 -1
- package/src/notifications/copy-composer.ts +5 -13
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/deliveries-store.ts +0 -39
- package/src/notifications/guardian-question-mode.ts +6 -10
- package/src/notifications/preference-extractor.ts +1 -1
- package/src/oauth/byo-connection.test.ts +29 -20
- package/src/oauth/provider-behaviors.ts +1 -1
- package/src/permissions/checker.ts +1 -1
- package/src/permissions/shell-identity.ts +0 -5
- package/src/permissions/trust-store.ts +0 -37
- package/src/prompts/system-prompt.ts +4 -4
- package/src/prompts/templates/SOUL.md +1 -1
- package/src/providers/managed-proxy/constants.ts +8 -10
- package/src/providers/managed-proxy/context.ts +14 -9
- package/src/providers/provider-send-message.ts +4 -52
- package/src/providers/registry.ts +16 -50
- package/src/runtime/actor-token-store.ts +0 -23
- package/src/runtime/auth/__tests__/guard-tests.test.ts +64 -0
- package/src/runtime/http-router.ts +5 -1
- package/src/runtime/http-server.ts +101 -4
- package/src/runtime/invite-instruction-generator.ts +25 -51
- package/src/runtime/invite-service.ts +0 -20
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/brain-graph-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +32 -11
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +2 -2
- package/src/runtime/routes/documents-routes.ts +3 -3
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -20
- package/src/runtime/routes/guardian-refresh-routes.ts +0 -20
- package/src/runtime/routes/secret-routes.ts +4 -4
- package/src/runtime/routes/session-management-routes.ts +27 -0
- package/src/runtime/routes/trust-rules-routes.ts +1 -1
- package/src/security/credential-backend.ts +148 -0
- package/src/security/oauth2.ts +1 -1
- package/src/security/secret-allowlist.ts +1 -1
- package/src/security/secure-keys.ts +98 -160
- package/src/security/token-manager.ts +0 -7
- package/src/sequence/guardrails.ts +0 -4
- package/src/sequence/store.ts +1 -20
- package/src/sequence/types.ts +1 -36
- package/src/signals/bash.ts +33 -0
- package/src/signals/cancel.ts +69 -0
- package/src/signals/conversation-undo.ts +127 -0
- package/src/signals/trust-rule.ts +174 -0
- package/src/skills/clawhub.ts +5 -5
- package/src/skills/managed-store.ts +4 -4
- package/src/subagent/manager.ts +8 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +366 -0
- package/src/telemetry/usage-telemetry-reporter.ts +181 -0
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/credentials/vault.ts +8 -4
- package/src/tools/memory/handlers.test.ts +24 -26
- package/src/tools/memory/handlers.ts +1 -13
- package/src/tools/registry.ts +5 -100
- package/src/tools/terminal/parser.ts +34 -4
- package/src/tools/tool-manifest.ts +0 -10
- package/src/usage/actors.ts +0 -12
- package/src/util/canonicalize-identity.ts +0 -9
- package/src/util/errors.ts +0 -3
- package/src/util/platform.ts +24 -7
- package/src/util/pricing.ts +0 -38
- package/src/watcher/constants.ts +0 -7
- package/src/watcher/providers/linear.ts +1 -1
- package/src/work-items/work-item-store.ts +4 -4
- package/src/workspace/commit-message-provider.ts +1 -1
- package/src/workspace/git-service.ts +44 -1
- package/src/workspace/provider-commit-message-generator.ts +1 -1
- package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
- package/src/browser-extension-relay/client.ts +0 -155
- package/src/contacts/index.ts +0 -18
- package/src/daemon/tls-certs.ts +0 -270
- package/src/errors.ts +0 -41
- package/src/events/index.ts +0 -18
- package/src/followups/index.ts +0 -10
- package/src/playbooks/index.ts +0 -10
- package/src/runtime/auth/index.ts +0 -44
- package/src/tasks/candidate-store.ts +0 -95
- package/src/tools/browser/api-map.ts +0 -313
- package/src/tools/browser/auto-navigate.ts +0 -469
- package/src/tools/browser/headless-browser.ts +0 -590
- package/src/tools/browser/recording-store.ts +0 -75
- package/src/tools/computer-use/registry.ts +0 -21
- package/src/tools/tasks/index.ts +0 -27
package/src/tools/registry.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { RiskLevel } from "../permissions/types.js";
|
|
2
1
|
import type { ToolDefinition } from "../providers/types.js";
|
|
3
2
|
import { getLogger } from "../util/logger.js";
|
|
4
3
|
import { coreAppProxyTools } from "./apps/definitions.js";
|
|
@@ -8,7 +7,7 @@ import { hostFileEditTool } from "./host-filesystem/edit.js";
|
|
|
8
7
|
import { hostFileReadTool } from "./host-filesystem/read.js";
|
|
9
8
|
import { hostFileWriteTool } from "./host-filesystem/write.js";
|
|
10
9
|
import { hostShellTool } from "./host-terminal/host-shell.js";
|
|
11
|
-
import type { Tool
|
|
10
|
+
import type { Tool } from "./types.js";
|
|
12
11
|
import { allUiSurfaceTools } from "./ui-surface/definitions.js";
|
|
13
12
|
import { registerUiSurfaceTools } from "./ui-surface/registry.js";
|
|
14
13
|
|
|
@@ -26,68 +25,6 @@ let coreToolsSnapshot: Map<string, Tool> | null = null;
|
|
|
26
25
|
// Tools are only removed from the global registry when this drops to 0.
|
|
27
26
|
const skillRefCount = new Map<string, number>();
|
|
28
27
|
|
|
29
|
-
export interface LazyToolDescriptor {
|
|
30
|
-
name: string;
|
|
31
|
-
description: string;
|
|
32
|
-
category: string;
|
|
33
|
-
defaultRiskLevel: RiskLevel;
|
|
34
|
-
definition: ToolDefinition;
|
|
35
|
-
loader: () => Promise<Tool>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* A tool wrapper that exposes metadata eagerly but defers module loading
|
|
40
|
-
* and execute() initialization until the tool is first invoked.
|
|
41
|
-
*/
|
|
42
|
-
class LazyTool implements Tool {
|
|
43
|
-
name: string;
|
|
44
|
-
description: string;
|
|
45
|
-
category: string;
|
|
46
|
-
defaultRiskLevel: RiskLevel;
|
|
47
|
-
private definition: ToolDefinition;
|
|
48
|
-
private loader: () => Promise<Tool>;
|
|
49
|
-
private resolvedTool: Tool | null = null;
|
|
50
|
-
private loadPromise: Promise<Tool> | null = null;
|
|
51
|
-
|
|
52
|
-
constructor(descriptor: LazyToolDescriptor) {
|
|
53
|
-
this.name = descriptor.name;
|
|
54
|
-
this.description = descriptor.description;
|
|
55
|
-
this.category = descriptor.category;
|
|
56
|
-
this.defaultRiskLevel = descriptor.defaultRiskLevel;
|
|
57
|
-
this.definition = descriptor.definition;
|
|
58
|
-
this.loader = descriptor.loader;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
getDefinition(): ToolDefinition {
|
|
62
|
-
return this.definition;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async execute(
|
|
66
|
-
input: Record<string, unknown>,
|
|
67
|
-
context: ToolContext,
|
|
68
|
-
): Promise<ToolExecutionResult> {
|
|
69
|
-
if (!this.resolvedTool) {
|
|
70
|
-
if (!this.loadPromise) {
|
|
71
|
-
// Assign loadPromise synchronously before the async loader begins,
|
|
72
|
-
// so concurrent callers see the guard immediately.
|
|
73
|
-
const promise = this.loader()
|
|
74
|
-
.then((tool) => {
|
|
75
|
-
this.resolvedTool = tool;
|
|
76
|
-
log.info({ name: this.name }, "Lazy tool loaded");
|
|
77
|
-
return tool;
|
|
78
|
-
})
|
|
79
|
-
.catch((err) => {
|
|
80
|
-
this.loadPromise = null;
|
|
81
|
-
throw err;
|
|
82
|
-
});
|
|
83
|
-
this.loadPromise = promise;
|
|
84
|
-
}
|
|
85
|
-
await this.loadPromise;
|
|
86
|
-
}
|
|
87
|
-
return this.resolvedTool!.execute(input, context);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
28
|
export function registerTool(tool: Tool): void {
|
|
92
29
|
const existing = tools.get(tool.name);
|
|
93
30
|
if (existing) {
|
|
@@ -98,11 +35,6 @@ export function registerTool(tool: Tool): void {
|
|
|
98
35
|
log.info({ name: tool.name, category: tool.category }, "Tool registered");
|
|
99
36
|
}
|
|
100
37
|
|
|
101
|
-
export function registerLazyTool(descriptor: LazyToolDescriptor): void {
|
|
102
|
-
const lazy = new LazyTool(descriptor);
|
|
103
|
-
registerTool(lazy);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
38
|
export function getTool(name: string): Tool | undefined {
|
|
107
39
|
return tools.get(name);
|
|
108
40
|
}
|
|
@@ -250,27 +182,6 @@ export function unregisterAllMcpTools(): void {
|
|
|
250
182
|
}
|
|
251
183
|
}
|
|
252
184
|
|
|
253
|
-
/**
|
|
254
|
-
* Unregister all tools belonging to a specific MCP server.
|
|
255
|
-
*/
|
|
256
|
-
export function unregisterMcpTools(serverId: string): void {
|
|
257
|
-
for (const [name, tool] of tools) {
|
|
258
|
-
if (tool.origin === "mcp" && tool.ownerMcpServerId === serverId) {
|
|
259
|
-
tools.delete(name);
|
|
260
|
-
log.info({ name, serverId }, "MCP tool unregistered");
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Return the names of all currently registered MCP-origin tools.
|
|
267
|
-
*/
|
|
268
|
-
export function getMcpToolNames(): string[] {
|
|
269
|
-
return Array.from(tools.values())
|
|
270
|
-
.filter((t) => t.origin === "mcp")
|
|
271
|
-
.map((t) => t.name);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
185
|
/**
|
|
275
186
|
* Return tool definitions for all currently registered MCP-origin tools.
|
|
276
187
|
* Used by the session resolver to dynamically pick up MCP tools that
|
|
@@ -312,7 +223,7 @@ export function getAllToolDefinitions(): ToolDefinition[] {
|
|
|
312
223
|
}
|
|
313
224
|
|
|
314
225
|
export async function initializeTools(): Promise<void> {
|
|
315
|
-
const { loadEagerModules, eagerModuleToolNames, explicitTools
|
|
226
|
+
const { loadEagerModules, eagerModuleToolNames, explicitTools } =
|
|
316
227
|
await import("./tool-manifest.js");
|
|
317
228
|
|
|
318
229
|
// Capture tool names already in the registry before any manifest
|
|
@@ -343,24 +254,18 @@ export async function initializeTools(): Promise<void> {
|
|
|
343
254
|
registerUiSurfaceTools();
|
|
344
255
|
registerAppTools();
|
|
345
256
|
|
|
346
|
-
// Lazy tools — defer module loading until first invocation.
|
|
347
|
-
for (const descriptor of lazyTools) {
|
|
348
|
-
registerLazyTool(descriptor);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
257
|
// Snapshot core tools for __resetRegistryForTesting(). We include every
|
|
352
258
|
// non-skill tool that was registered by the manifest, while excluding
|
|
353
259
|
// arbitrary test tools that were registered before init.
|
|
354
260
|
//
|
|
355
261
|
// A pre-existing tool is included only if it is a known manifest tool
|
|
356
|
-
// (declared in eagerModuleToolNames, explicitTools,
|
|
357
|
-
//
|
|
358
|
-
//
|
|
262
|
+
// (declared in eagerModuleToolNames, explicitTools, or hostTools).
|
|
263
|
+
// This handles ESM cache hits where eager-module tools are already in
|
|
264
|
+
// the registry before init ran.
|
|
359
265
|
if (!coreToolsSnapshot) {
|
|
360
266
|
const manifestToolNames = new Set<string>([
|
|
361
267
|
...eagerModuleToolNames,
|
|
362
268
|
...explicitTools.map((t: Tool) => t.name),
|
|
363
|
-
...lazyTools.map((t: LazyToolDescriptor) => t.name),
|
|
364
269
|
...hostTools.map((t: Tool) => t.name),
|
|
365
270
|
...allComputerUseTools.map((t: Tool) => t.name),
|
|
366
271
|
...allUiSurfaceTools.map((t: Tool) => t.name),
|
|
@@ -133,7 +133,11 @@ const initGuard = new PromiseGuard<void>();
|
|
|
133
133
|
* 2. Next to the compiled binary (process.execPath)
|
|
134
134
|
* This matches the pattern used for compiled Bun binary asset resolution.
|
|
135
135
|
*/
|
|
136
|
-
function findWasmPath(
|
|
136
|
+
function findWasmPath(
|
|
137
|
+
pkg: string,
|
|
138
|
+
file: string,
|
|
139
|
+
resolvedPkgDir?: string,
|
|
140
|
+
): string {
|
|
137
141
|
const dir = import.meta.dirname ?? __dirname;
|
|
138
142
|
|
|
139
143
|
// In compiled Bun binaries, import.meta.dirname points into the virtual
|
|
@@ -154,9 +158,16 @@ function findWasmPath(pkg: string, file: string): string {
|
|
|
154
158
|
return execDirPath;
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
// Use
|
|
158
|
-
//
|
|
159
|
-
|
|
161
|
+
// Use a pre-resolved package directory when available (callers pass this so
|
|
162
|
+
// that static-analysis tools like knip can see the literal specifier).
|
|
163
|
+
if (resolvedPkgDir) {
|
|
164
|
+
const resolvedPath = join(resolvedPkgDir, file);
|
|
165
|
+
if (existsSync(resolvedPath)) return resolvedPath;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Fallback: dynamic module resolution. This handles hoisted dependencies
|
|
169
|
+
// (e.g. global bun installs where web-tree-sitter is at the top-level
|
|
170
|
+
// node_modules rather than nested under @vellumai/assistant).
|
|
160
171
|
try {
|
|
161
172
|
const resolved = require.resolve(`${pkg}/package.json`);
|
|
162
173
|
const pkgDir = dirname(resolved);
|
|
@@ -184,13 +195,32 @@ async function ensureParser(): Promise<Parser> {
|
|
|
184
195
|
if (parserInstance) return parserInstance;
|
|
185
196
|
|
|
186
197
|
await initGuard.run(async () => {
|
|
198
|
+
let webTreeSitterDir: string | undefined;
|
|
199
|
+
try {
|
|
200
|
+
webTreeSitterDir = dirname(
|
|
201
|
+
require.resolve("web-tree-sitter/package.json"),
|
|
202
|
+
);
|
|
203
|
+
} catch {
|
|
204
|
+
// Handled by findWasmPath fallbacks
|
|
205
|
+
}
|
|
206
|
+
let treeSitterBashDir: string | undefined;
|
|
207
|
+
try {
|
|
208
|
+
treeSitterBashDir = dirname(
|
|
209
|
+
require.resolve("tree-sitter-bash/package.json"),
|
|
210
|
+
);
|
|
211
|
+
} catch {
|
|
212
|
+
// Handled by findWasmPath fallbacks
|
|
213
|
+
}
|
|
214
|
+
|
|
187
215
|
const treeSitterWasm = findWasmPath(
|
|
188
216
|
"web-tree-sitter",
|
|
189
217
|
"web-tree-sitter.wasm",
|
|
218
|
+
webTreeSitterDir,
|
|
190
219
|
);
|
|
191
220
|
const bashWasmPath = findWasmPath(
|
|
192
221
|
"tree-sitter-bash",
|
|
193
222
|
"tree-sitter-bash.wasm",
|
|
223
|
+
treeSitterBashDir,
|
|
194
224
|
);
|
|
195
225
|
|
|
196
226
|
verifyWasmChecksum(treeSitterWasm, "web-tree-sitter.wasm");
|
|
@@ -15,7 +15,6 @@ import { fileWriteTool } from "./filesystem/write.js";
|
|
|
15
15
|
import { memoryManageTool, memoryRecallTool } from "./memory/register.js";
|
|
16
16
|
import { webFetchTool } from "./network/web-fetch.js";
|
|
17
17
|
import { webSearchTool } from "./network/web-search.js";
|
|
18
|
-
import type { LazyToolDescriptor } from "./registry.js";
|
|
19
18
|
import { skillExecuteTool } from "./skills/execute.js";
|
|
20
19
|
import { skillLoadTool } from "./skills/load.js";
|
|
21
20
|
import { requestSystemPermissionTool } from "./system/request-permission.js";
|
|
@@ -85,12 +84,3 @@ export const explicitTools: Tool[] = [
|
|
|
85
84
|
memoryRecallTool,
|
|
86
85
|
credentialStoreTool,
|
|
87
86
|
];
|
|
88
|
-
|
|
89
|
-
// ── Lazy tool descriptors ───────────────────────────────────────────
|
|
90
|
-
// Tools that defer module loading until first invocation.
|
|
91
|
-
// bash was previously lazy but is now eagerly registered via side-effect
|
|
92
|
-
// imports above, preserving its full definition (including the `reason` field)
|
|
93
|
-
// and fixing bun --compile module-not-found crashes.
|
|
94
|
-
// swarm_delegate has been moved to the orchestration bundled skill.
|
|
95
|
-
|
|
96
|
-
export const lazyTools: LazyToolDescriptor[] = [];
|
package/src/usage/actors.ts
CHANGED
|
@@ -10,15 +10,3 @@ export type UsageActor =
|
|
|
10
10
|
| "suggestion_generator"
|
|
11
11
|
| "computer_use_agent"
|
|
12
12
|
| "memory_embedding";
|
|
13
|
-
|
|
14
|
-
/** All valid actor identifiers (useful for runtime validation). */
|
|
15
|
-
export const USAGE_ACTORS: readonly UsageActor[] = [
|
|
16
|
-
"main_agent",
|
|
17
|
-
"context_compactor",
|
|
18
|
-
"task_classifier",
|
|
19
|
-
"title_generator",
|
|
20
|
-
"ambient_analyzer",
|
|
21
|
-
"suggestion_generator",
|
|
22
|
-
"computer_use_agent",
|
|
23
|
-
"memory_embedding",
|
|
24
|
-
] as const;
|
|
@@ -44,12 +44,3 @@ export function canonicalizeInboundIdentity(
|
|
|
44
44
|
|
|
45
45
|
return trimmed;
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Check whether a channel uses phone-number-based identity.
|
|
50
|
-
* Useful for call sites that need to know whether E.164 normalization
|
|
51
|
-
* applies without re-importing the channel set.
|
|
52
|
-
*/
|
|
53
|
-
export function isPhoneChannel(channel: ChannelId): boolean {
|
|
54
|
-
return PHONE_CHANNELS.has(channel);
|
|
55
|
-
}
|
package/src/util/errors.ts
CHANGED
package/src/util/platform.ts
CHANGED
|
@@ -284,21 +284,38 @@ export function isIOSPairingEnabled(): boolean {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
|
-
* Returns the path
|
|
288
|
-
* This
|
|
289
|
-
*
|
|
287
|
+
* Returns the XDG-compliant path for the platform API token
|
|
288
|
+
* (~/.config/vellum/platform-token). This is the canonical location
|
|
289
|
+
* shared by the CLI and desktop app.
|
|
290
|
+
*/
|
|
291
|
+
function getXdgPlatformTokenPath(): string {
|
|
292
|
+
const configHome =
|
|
293
|
+
process.env.XDG_CONFIG_HOME?.trim() || join(homedir(), ".config");
|
|
294
|
+
return join(configHome, "vellum", "platform-token");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Returns the instance-scoped path to the platform API token file
|
|
299
|
+
* (~/.vellum/platform-token). Used as a fallback for local assistant
|
|
300
|
+
* instances that may have the token written here by the desktop app.
|
|
290
301
|
*/
|
|
291
302
|
export function getPlatformTokenPath(): string {
|
|
292
303
|
return join(getRootDir(), "platform-token");
|
|
293
304
|
}
|
|
294
305
|
|
|
295
306
|
/**
|
|
296
|
-
* Read the platform API token from disk.
|
|
297
|
-
*
|
|
307
|
+
* Read the platform API token from disk. Checks the instance-scoped
|
|
308
|
+
* path first, then falls back to the XDG-compliant shared location.
|
|
309
|
+
* Returns null if neither file exists or can be read.
|
|
298
310
|
*/
|
|
299
311
|
export function readPlatformToken(): string | null {
|
|
300
312
|
try {
|
|
301
313
|
return readFileSync(getPlatformTokenPath(), "utf-8").trim();
|
|
314
|
+
} catch {
|
|
315
|
+
// Instance-scoped token not found; try XDG path
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
return readFileSync(getXdgPlatformTokenPath(), "utf-8").trim();
|
|
302
319
|
} catch {
|
|
303
320
|
return null;
|
|
304
321
|
}
|
|
@@ -321,7 +338,7 @@ export function getHistoryPath(): string {
|
|
|
321
338
|
}
|
|
322
339
|
|
|
323
340
|
export function getHooksDir(): string {
|
|
324
|
-
return
|
|
341
|
+
return join(getRootDir(), "hooks");
|
|
325
342
|
}
|
|
326
343
|
|
|
327
344
|
// --- Workspace path primitives ---
|
|
@@ -369,7 +386,7 @@ export function ensureDataDir(): void {
|
|
|
369
386
|
join(root, "protected"),
|
|
370
387
|
// Workspace dirs
|
|
371
388
|
workspace,
|
|
372
|
-
join(
|
|
389
|
+
join(root, "hooks"),
|
|
373
390
|
join(workspace, "skills"),
|
|
374
391
|
join(workspace, "embedding-models"),
|
|
375
392
|
// Data sub-dirs under workspace
|
package/src/util/pricing.ts
CHANGED
|
@@ -264,41 +264,3 @@ export function resolvePricing(
|
|
|
264
264
|
createDirectUsage(inputTokens, outputTokens),
|
|
265
265
|
);
|
|
266
266
|
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Resolve pricing with optional custom model overrides checked first.
|
|
270
|
-
* Overrides are matched by provider (exact) and modelPattern (prefix match).
|
|
271
|
-
* Falls back to the built-in catalog if no override matches.
|
|
272
|
-
*/
|
|
273
|
-
export function resolvePricingWithOverrides(
|
|
274
|
-
provider: string,
|
|
275
|
-
model: string,
|
|
276
|
-
inputTokens: number,
|
|
277
|
-
outputTokens: number,
|
|
278
|
-
overrides?: ModelPricingOverride[],
|
|
279
|
-
): PricingResult {
|
|
280
|
-
return resolvePricingForUsageWithOverrides(
|
|
281
|
-
provider,
|
|
282
|
-
model,
|
|
283
|
-
createDirectUsage(inputTokens, outputTokens),
|
|
284
|
-
overrides,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Estimate cost in USD for the given token counts, provider, and model.
|
|
290
|
-
* Returns 0 if the provider/model combination is not in the pricing table
|
|
291
|
-
* (e.g. Ollama local models).
|
|
292
|
-
*/
|
|
293
|
-
export function estimateCost(
|
|
294
|
-
inputTokens: number,
|
|
295
|
-
outputTokens: number,
|
|
296
|
-
model: string,
|
|
297
|
-
provider: string,
|
|
298
|
-
): number {
|
|
299
|
-
const result = resolvePricing(provider, model, inputTokens, outputTokens);
|
|
300
|
-
if (result.pricingStatus === "priced" && result.estimatedCostUsd != null) {
|
|
301
|
-
return result.estimatedCostUsd;
|
|
302
|
-
}
|
|
303
|
-
return 0;
|
|
304
|
-
}
|
package/src/watcher/constants.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
/** Default poll interval for watchers (60 seconds). */
|
|
2
2
|
export const DEFAULT_POLL_INTERVAL_MS = 60_000;
|
|
3
|
-
|
|
4
|
-
/** Base delay for exponential backoff on consecutive errors. */
|
|
5
|
-
export const BACKOFF_BASE_MS = 30_000;
|
|
6
|
-
|
|
7
|
-
/** Maximum backoff delay (1 hour). */
|
|
8
|
-
export const MAX_BACKOFF_MS = 60 * 60 * 1000;
|
|
9
|
-
|
|
10
3
|
/** Disable watcher after this many consecutive errors. */
|
|
11
4
|
export const MAX_CONSECUTIVE_ERRORS = 5;
|
|
@@ -402,7 +402,7 @@ function getStateCache(watcherKey: string): Map<string, string> {
|
|
|
402
402
|
* disabled. Prevents unbounded growth of `knownIssueStateIdsByWatcher` in
|
|
403
403
|
* environments that create and delete watchers frequently (watcher churn).
|
|
404
404
|
*/
|
|
405
|
-
|
|
405
|
+
function clearLinearStateCache(watcherKey: string): void {
|
|
406
406
|
knownIssueStateIdsByWatcher.delete(watcherKey);
|
|
407
407
|
lastSeenAssignedIdsByWatcher.delete(watcherKey);
|
|
408
408
|
}
|
|
@@ -147,7 +147,7 @@ export function deleteWorkItem(id: string): void {
|
|
|
147
147
|
|
|
148
148
|
// ── Queue Removal ───────────────────────────────────────────────────
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
interface RemoveWorkItemResult {
|
|
151
151
|
success: boolean;
|
|
152
152
|
title: string;
|
|
153
153
|
message: string;
|
|
@@ -177,7 +177,7 @@ export function removeWorkItemFromQueue(id: string): RemoveWorkItemResult {
|
|
|
177
177
|
|
|
178
178
|
// ── Selectors / Helpers ─────────────────────────────────────────────
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
interface WorkItemSelector {
|
|
181
181
|
workItemId?: string;
|
|
182
182
|
taskId?: string;
|
|
183
183
|
title?: string;
|
|
@@ -356,9 +356,9 @@ export function resolveWorkItem(
|
|
|
356
356
|
|
|
357
357
|
// ── Entity Identification ───────────────────────────────────────────
|
|
358
358
|
|
|
359
|
-
|
|
359
|
+
type EntityType = "task_template" | "work_item" | "unknown";
|
|
360
360
|
|
|
361
|
-
|
|
361
|
+
interface EntityIdentification {
|
|
362
362
|
type: EntityType;
|
|
363
363
|
id: string;
|
|
364
364
|
title?: string;
|
|
@@ -33,7 +33,7 @@ export interface CommitMessageProvider {
|
|
|
33
33
|
/**
|
|
34
34
|
* Build a short summary of what changed from a list of file paths.
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
function buildChangeSummary(files: string[]): string {
|
|
37
37
|
if (files.length === 0) {
|
|
38
38
|
return "workspace changes";
|
|
39
39
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
statSync,
|
|
6
|
+
unlinkSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
3
9
|
import { join } from "node:path";
|
|
4
10
|
import { promisify } from "node:util";
|
|
5
11
|
|
|
@@ -252,6 +258,33 @@ export class WorkspaceGitService {
|
|
|
252
258
|
);
|
|
253
259
|
}
|
|
254
260
|
|
|
261
|
+
/** Age threshold (ms) beyond which an index.lock is considered stale. */
|
|
262
|
+
private static readonly LOCK_STALE_THRESHOLD_MS = 30_000;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Remove `.git/index.lock` only if it is stale — older than
|
|
266
|
+
* {@link LOCK_STALE_THRESHOLD_MS}. A recently-created lock may belong to a
|
|
267
|
+
* concurrent git process outside our mutex (e.g. a user-initiated CLI
|
|
268
|
+
* command), so we leave it alone.
|
|
269
|
+
*/
|
|
270
|
+
private cleanStaleLockFile(): void {
|
|
271
|
+
const lockPath = join(this.workspaceDir, ".git", "index.lock");
|
|
272
|
+
try {
|
|
273
|
+
const stat = statSync(lockPath);
|
|
274
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
275
|
+
if (ageMs < WorkspaceGitService.LOCK_STALE_THRESHOLD_MS) {
|
|
276
|
+
log.debug(
|
|
277
|
+
`index.lock exists but is only ${Math.round(ageMs / 1000)}s old — leaving it`,
|
|
278
|
+
);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
unlinkSync(lockPath);
|
|
282
|
+
log.debug(`Removed stale index.lock (${Math.round(ageMs / 1000)}s old)`);
|
|
283
|
+
} catch {
|
|
284
|
+
// File doesn't exist or can't be stat'd/removed — move on.
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
255
288
|
/**
|
|
256
289
|
* Ensure the git repository is initialized.
|
|
257
290
|
* Idempotent: safe to call multiple times.
|
|
@@ -298,6 +331,11 @@ export class WorkspaceGitService {
|
|
|
298
331
|
|
|
299
332
|
const gitDir = join(this.workspaceDir, ".git");
|
|
300
333
|
|
|
334
|
+
// Clean up stale lock files before any git operations.
|
|
335
|
+
if (existsSync(gitDir)) {
|
|
336
|
+
this.cleanStaleLockFile();
|
|
337
|
+
}
|
|
338
|
+
|
|
301
339
|
if (existsSync(gitDir)) {
|
|
302
340
|
// Validate existing repo is not corrupted before marking as ready.
|
|
303
341
|
// A corrupted .git directory (e.g. missing HEAD) would cause all
|
|
@@ -440,6 +478,8 @@ export class WorkspaceGitService {
|
|
|
440
478
|
await this.ensureInitialized();
|
|
441
479
|
|
|
442
480
|
await this.mutex.withLock(async () => {
|
|
481
|
+
this.cleanStaleLockFile();
|
|
482
|
+
|
|
443
483
|
// Stage all changes
|
|
444
484
|
await this.execGit(["add", "-A"]);
|
|
445
485
|
|
|
@@ -513,6 +553,8 @@ export class WorkspaceGitService {
|
|
|
513
553
|
|
|
514
554
|
try {
|
|
515
555
|
const result = await this.mutex.withLock(async () => {
|
|
556
|
+
this.cleanStaleLockFile();
|
|
557
|
+
|
|
516
558
|
// Re-check breaker under lock: a queued call that started before the
|
|
517
559
|
// breaker opened should not proceed with expensive git work now that
|
|
518
560
|
// the breaker is open.
|
|
@@ -853,6 +895,7 @@ export class WorkspaceGitService {
|
|
|
853
895
|
): Promise<void> {
|
|
854
896
|
await this.ensureInitialized();
|
|
855
897
|
await this.mutex.withLock(async () => {
|
|
898
|
+
this.cleanStaleLockFile();
|
|
856
899
|
await fn((args) => this.execGit(args));
|
|
857
900
|
});
|
|
858
901
|
}
|
|
@@ -141,7 +141,7 @@ export class ProviderCommitMessageGenerator {
|
|
|
141
141
|
// Step 2: Resolve configured provider using fail-open semantics.
|
|
142
142
|
// If nothing is resolvable, differentiate likely missing-key cases from
|
|
143
143
|
// true registry/init failures.
|
|
144
|
-
const resolved = resolveConfiguredProvider();
|
|
144
|
+
const resolved = await resolveConfiguredProvider();
|
|
145
145
|
if (!resolved) {
|
|
146
146
|
const candidates = getProviderCandidates(config);
|
|
147
147
|
const hasAnyKeylessCandidate = candidates.some((name) =>
|