@vellumai/assistant 0.5.6 → 0.5.7
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 +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +1 -1
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +0 -114
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +8 -14
- package/src/config/feature-flag-registry.json +48 -8
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +227 -51
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +12 -0
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/jobs-store.ts +30 -5
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +27 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.ts +24 -6
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +3 -50
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
import { isAllowDecision } from "../permissions/types.js";
|
|
24
24
|
import type { Message, ToolDefinition } from "../providers/types.js";
|
|
25
25
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
26
|
-
import { getEffectiveMode } from "../runtime/conversation-approval-overrides.js";
|
|
27
26
|
import { coreAppProxyTools } from "../tools/apps/definitions.js";
|
|
28
27
|
import { registerConversationSender } from "../tools/browser/browser-screencast.js";
|
|
29
28
|
import type { ToolExecutor } from "../tools/executor.js";
|
|
@@ -234,110 +233,6 @@ export function createToolExecutor(
|
|
|
234
233
|
params.allowedDomains,
|
|
235
234
|
);
|
|
236
235
|
},
|
|
237
|
-
requestConfirmation: async (req) => {
|
|
238
|
-
// Check trust store before prompting
|
|
239
|
-
const existingRule = findHighestPriorityRule(
|
|
240
|
-
"cc:" + req.toolName,
|
|
241
|
-
[req.toolName, `cc:${req.toolName}`, "cc:*"],
|
|
242
|
-
ctx.workingDir,
|
|
243
|
-
);
|
|
244
|
-
if (existingRule && existingRule.decision !== "ask") {
|
|
245
|
-
return {
|
|
246
|
-
decision:
|
|
247
|
-
existingRule.decision === "allow"
|
|
248
|
-
? ("allow" as const)
|
|
249
|
-
: ("deny" as const),
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
// Auto-approve sub-tool confirmations when a temporary approval
|
|
253
|
-
// override is active for this conversation (guardian only).
|
|
254
|
-
const guardianTrust = ctx.trustContext?.trustClass ?? "unknown";
|
|
255
|
-
if (
|
|
256
|
-
guardianTrust === "guardian" &&
|
|
257
|
-
getEffectiveMode(ctx.conversationId) !== undefined
|
|
258
|
-
) {
|
|
259
|
-
return { decision: "allow" as const };
|
|
260
|
-
}
|
|
261
|
-
const allowlistOptions = [
|
|
262
|
-
{
|
|
263
|
-
label: `cc:${req.toolName}`,
|
|
264
|
-
description: `Claude Code ${req.toolName}`,
|
|
265
|
-
pattern: `cc:${req.toolName}`,
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
label: "cc:*",
|
|
269
|
-
description: "All Claude Code sub-tools",
|
|
270
|
-
pattern: "cc:*",
|
|
271
|
-
},
|
|
272
|
-
];
|
|
273
|
-
const scopeOptions = generateScopeOptions(ctx.workingDir);
|
|
274
|
-
const response = await prompter.prompt(
|
|
275
|
-
`cc:${req.toolName}`,
|
|
276
|
-
req.input,
|
|
277
|
-
req.riskLevel,
|
|
278
|
-
allowlistOptions,
|
|
279
|
-
scopeOptions,
|
|
280
|
-
undefined,
|
|
281
|
-
undefined,
|
|
282
|
-
ctx.conversationId,
|
|
283
|
-
req.executionTarget,
|
|
284
|
-
undefined,
|
|
285
|
-
undefined,
|
|
286
|
-
undefined,
|
|
287
|
-
toolUseId,
|
|
288
|
-
);
|
|
289
|
-
if (
|
|
290
|
-
(response.decision === "always_allow" ||
|
|
291
|
-
response.decision === "always_allow_high_risk") &&
|
|
292
|
-
response.selectedPattern &&
|
|
293
|
-
response.selectedScope
|
|
294
|
-
) {
|
|
295
|
-
log.info(
|
|
296
|
-
{
|
|
297
|
-
toolName: "cc:" + req.toolName,
|
|
298
|
-
pattern: response.selectedPattern,
|
|
299
|
-
scope: response.selectedScope,
|
|
300
|
-
highRisk: response.decision === "always_allow_high_risk",
|
|
301
|
-
},
|
|
302
|
-
"Persisting always-allow trust rule",
|
|
303
|
-
);
|
|
304
|
-
addRule(
|
|
305
|
-
"cc:" + req.toolName,
|
|
306
|
-
response.selectedPattern,
|
|
307
|
-
response.selectedScope,
|
|
308
|
-
"allow",
|
|
309
|
-
100,
|
|
310
|
-
response.decision === "always_allow_high_risk"
|
|
311
|
-
? { allowHighRisk: true }
|
|
312
|
-
: undefined,
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
if (
|
|
316
|
-
response.decision === "always_deny" &&
|
|
317
|
-
response.selectedPattern &&
|
|
318
|
-
response.selectedScope
|
|
319
|
-
) {
|
|
320
|
-
log.info(
|
|
321
|
-
{
|
|
322
|
-
toolName: "cc:" + req.toolName,
|
|
323
|
-
pattern: response.selectedPattern,
|
|
324
|
-
scope: response.selectedScope,
|
|
325
|
-
},
|
|
326
|
-
"Persisting always-deny trust rule",
|
|
327
|
-
);
|
|
328
|
-
addRule(
|
|
329
|
-
"cc:" + req.toolName,
|
|
330
|
-
response.selectedPattern,
|
|
331
|
-
response.selectedScope,
|
|
332
|
-
"deny",
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
return {
|
|
336
|
-
decision: isAllowDecision(response.decision)
|
|
337
|
-
? ("allow" as const)
|
|
338
|
-
: ("deny" as const),
|
|
339
|
-
};
|
|
340
|
-
},
|
|
341
236
|
};
|
|
342
237
|
|
|
343
238
|
// Intercept skill_execute: extract the real tool name and input, then
|
|
@@ -42,6 +42,7 @@ import { PermissionPrompter } from "../permissions/prompter.js";
|
|
|
42
42
|
import { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
43
43
|
import { patternMatchesCandidate } from "../permissions/trust-store.js";
|
|
44
44
|
import type { UserDecision } from "../permissions/types.js";
|
|
45
|
+
import { resolvePersonaContext } from "../prompts/persona-resolver.js";
|
|
45
46
|
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
46
47
|
import type { Message } from "../providers/types.js";
|
|
47
48
|
import type { Provider } from "../providers/types.js";
|
|
@@ -353,10 +354,18 @@ export class Conversation {
|
|
|
353
354
|
const resolveSystemPromptCallback = (
|
|
354
355
|
_history: import("../providers/types.js").Message[],
|
|
355
356
|
): ResolvedSystemPrompt => {
|
|
357
|
+
const persona = resolvePersonaContext(
|
|
358
|
+
this.trustContext,
|
|
359
|
+
this.channelCapabilities,
|
|
360
|
+
);
|
|
356
361
|
const resolved = {
|
|
357
362
|
systemPrompt: hasSystemPromptOverride
|
|
358
363
|
? systemPrompt
|
|
359
|
-
: buildSystemPrompt({
|
|
364
|
+
: buildSystemPrompt({
|
|
365
|
+
hasNoClient: this.hasNoClient,
|
|
366
|
+
userPersona: persona.userPersona,
|
|
367
|
+
channelPersona: persona.channelPersona,
|
|
368
|
+
}),
|
|
360
369
|
maxTokens: configuredMaxTokens,
|
|
361
370
|
};
|
|
362
371
|
return resolved;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business logic for Vercel API token management.
|
|
3
|
+
*
|
|
4
|
+
* The Vercel token is stored in the secure credential vault under
|
|
5
|
+
* `credential/vercel/api_token`. These functions provide get/set/delete
|
|
6
|
+
* operations that mirror the pattern used by Telegram config handlers.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
10
|
+
import {
|
|
11
|
+
deleteSecureKeyAsync,
|
|
12
|
+
getSecureKeyAsync,
|
|
13
|
+
setSecureKeyAsync,
|
|
14
|
+
} from "../../security/secure-keys.js";
|
|
15
|
+
import {
|
|
16
|
+
deleteCredentialMetadata,
|
|
17
|
+
upsertCredentialMetadata,
|
|
18
|
+
} from "../../tools/credentials/metadata-store.js";
|
|
19
|
+
import { getLogger } from "../../util/logger.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("config-vercel");
|
|
22
|
+
|
|
23
|
+
// -- Transport-agnostic result type --
|
|
24
|
+
|
|
25
|
+
export interface VercelConfigResult {
|
|
26
|
+
hasToken: boolean;
|
|
27
|
+
success: boolean;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check whether a Vercel API token is stored in the credential vault.
|
|
33
|
+
*/
|
|
34
|
+
export async function getVercelConfig(): Promise<VercelConfigResult> {
|
|
35
|
+
const token = await getSecureKeyAsync(credentialKey("vercel", "api_token"));
|
|
36
|
+
return { hasToken: !!token, success: true };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Store a Vercel API token in the credential vault.
|
|
41
|
+
*/
|
|
42
|
+
export async function setVercelConfig(
|
|
43
|
+
apiToken?: string,
|
|
44
|
+
): Promise<VercelConfigResult> {
|
|
45
|
+
if (!apiToken) {
|
|
46
|
+
return { hasToken: false, success: false, error: "apiToken is required" };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const stored = await setSecureKeyAsync(
|
|
50
|
+
credentialKey("vercel", "api_token"),
|
|
51
|
+
apiToken,
|
|
52
|
+
);
|
|
53
|
+
if (!stored) {
|
|
54
|
+
return {
|
|
55
|
+
hasToken: false,
|
|
56
|
+
success: false,
|
|
57
|
+
error: "Failed to store Vercel API token in secure storage",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
upsertCredentialMetadata("vercel", "api_token", {
|
|
62
|
+
allowedTools: ["deploy", "publish_page"],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
log.info("Vercel API token stored successfully");
|
|
66
|
+
return { hasToken: true, success: true };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Delete the Vercel API token from the credential vault.
|
|
71
|
+
*/
|
|
72
|
+
export async function deleteVercelConfig(): Promise<VercelConfigResult> {
|
|
73
|
+
const result = await deleteSecureKeyAsync(
|
|
74
|
+
credentialKey("vercel", "api_token"),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (result === "error") {
|
|
78
|
+
const stillPresent = !!(await getSecureKeyAsync(
|
|
79
|
+
credentialKey("vercel", "api_token"),
|
|
80
|
+
));
|
|
81
|
+
return {
|
|
82
|
+
hasToken: stillPresent,
|
|
83
|
+
success: false,
|
|
84
|
+
error: "Failed to delete Vercel API token from secure storage",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
deleteCredentialMetadata("vercel", "api_token");
|
|
89
|
+
|
|
90
|
+
log.info("Vercel API token deleted");
|
|
91
|
+
return { hasToken: false, success: true };
|
|
92
|
+
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
getConfiguredProvider,
|
|
25
25
|
userMessage,
|
|
26
26
|
} from "../../providers/provider-send-message.js";
|
|
27
|
+
import { isTextMimeType as isTextMime } from "../../runtime/routes/workspace-utils.js";
|
|
27
28
|
import {
|
|
28
29
|
clawhubCheckUpdates,
|
|
29
30
|
clawhubInspect,
|
|
@@ -49,20 +50,6 @@ import {
|
|
|
49
50
|
|
|
50
51
|
// ─── MIME detection helpers ───────────────────────────────────────────────────
|
|
51
52
|
|
|
52
|
-
const TEXT_MIME_PREFIXES = [
|
|
53
|
-
"text/",
|
|
54
|
-
"application/json",
|
|
55
|
-
"application/javascript",
|
|
56
|
-
"application/typescript",
|
|
57
|
-
"application/xml",
|
|
58
|
-
"application/yaml",
|
|
59
|
-
"application/x-yaml",
|
|
60
|
-
"application/toml",
|
|
61
|
-
"application/x-sh",
|
|
62
|
-
];
|
|
63
|
-
function isTextMime(mimeType: string): boolean {
|
|
64
|
-
return TEXT_MIME_PREFIXES.some((prefix) => mimeType.startsWith(prefix));
|
|
65
|
-
}
|
|
66
53
|
const MAX_INLINE_SIZE = 2 * 1024 * 1024; // 2 MB
|
|
67
54
|
|
|
68
55
|
// ─── Shared context for standalone functions ─────────────────────────────────
|
|
@@ -380,7 +367,7 @@ function readDirRecursive(dir: string, rootDir: string): SkillFileEntry[] {
|
|
|
380
367
|
try {
|
|
381
368
|
const stat = statSync(fullPath);
|
|
382
369
|
const mimeType = Bun.file(fullPath).type;
|
|
383
|
-
const isText = isTextMime(mimeType);
|
|
370
|
+
const isText = isTextMime(mimeType, dirent.name);
|
|
384
371
|
let content: string | null = null;
|
|
385
372
|
if (isText && stat.size <= MAX_INLINE_SIZE) {
|
|
386
373
|
content = readFileSync(fullPath, "utf-8");
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import {
|
|
3
|
+
appendFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
lstatSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
readlinkSync,
|
|
9
|
+
symlinkSync,
|
|
10
|
+
unlinkSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
|
|
15
|
+
import { getLogger } from "../util/logger.js";
|
|
16
|
+
|
|
17
|
+
const log = getLogger("install-symlink");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the path to the `vellum-assistant` binary that should be symlinked
|
|
21
|
+
* as `assistant`.
|
|
22
|
+
*
|
|
23
|
+
* - **Bundled (desktop app):** The daemon runs from a compiled binary inside
|
|
24
|
+
* `Vellum.app/Contents/MacOS/`. `process.execPath` is the daemon binary
|
|
25
|
+
* itself — the `vellum-assistant` CLI binary lives alongside it in the same
|
|
26
|
+
* directory.
|
|
27
|
+
*
|
|
28
|
+
* - **Dev (bun):** `process.execPath` is the bun runtime. We resolve the
|
|
29
|
+
* assistant entrypoint relative to this file's location in the source tree
|
|
30
|
+
* and return a wrapper script path that would need bun to run — so we skip
|
|
31
|
+
* symlinking in dev mode since developers manage their own PATH.
|
|
32
|
+
*/
|
|
33
|
+
function resolveAssistantBinary(): string | null {
|
|
34
|
+
const execPath = process.execPath;
|
|
35
|
+
// Detect the bundled desktop app case by checking for the app bundle path.
|
|
36
|
+
const isBundled = execPath.includes("/Contents/MacOS/");
|
|
37
|
+
|
|
38
|
+
if (isBundled) {
|
|
39
|
+
// The assistant CLI binary is a sibling of the daemon binary in
|
|
40
|
+
// Contents/MacOS/.
|
|
41
|
+
const macosDir = dirname(execPath);
|
|
42
|
+
const assistantBinary = join(macosDir, "vellum-assistant");
|
|
43
|
+
if (existsSync(assistantBinary)) {
|
|
44
|
+
return assistantBinary;
|
|
45
|
+
}
|
|
46
|
+
log.warn(
|
|
47
|
+
{ expected: assistantBinary },
|
|
48
|
+
"Bundled vellum-assistant binary not found alongside daemon",
|
|
49
|
+
);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Dev mode: resolve the assistant entrypoint from the source tree.
|
|
54
|
+
// The daemon entry is at assistant/src/daemon/main.ts and the assistant
|
|
55
|
+
// CLI entry is at assistant/src/index.ts.
|
|
56
|
+
const assistantEntry = join(dirname(__filename), "..", "index.ts");
|
|
57
|
+
if (existsSync(assistantEntry)) {
|
|
58
|
+
return assistantEntry;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Attempts to place a symlink at `symlinkPath` pointing to `target`.
|
|
65
|
+
* Returns true if the symlink was created or already points to the target.
|
|
66
|
+
*/
|
|
67
|
+
function trySymlink(target: string, symlinkPath: string): boolean {
|
|
68
|
+
try {
|
|
69
|
+
try {
|
|
70
|
+
const stats = lstatSync(symlinkPath);
|
|
71
|
+
if (!stats.isSymbolicLink()) {
|
|
72
|
+
// Real file — don't overwrite (could be a developer's local install)
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const dest = readlinkSync(symlinkPath);
|
|
76
|
+
if (dest === target) return true;
|
|
77
|
+
// Stale or dangling symlink — remove before creating new one
|
|
78
|
+
unlinkSync(symlinkPath);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
if ((e as NodeJS.ErrnoException)?.code !== "ENOENT") return false;
|
|
81
|
+
// Path doesn't exist — proceed to create symlink
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const dir = dirname(symlinkPath);
|
|
85
|
+
if (!existsSync(dir)) {
|
|
86
|
+
mkdirSync(dir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
symlinkSync(target, symlinkPath);
|
|
89
|
+
return true;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Ensures ~/.local/bin is present in the user's shell profile so that
|
|
97
|
+
* symlinks placed there are on PATH in new terminal sessions.
|
|
98
|
+
*/
|
|
99
|
+
function ensureLocalBinInShellProfile(localBinDir: string): void {
|
|
100
|
+
const shell = process.env.SHELL ?? "";
|
|
101
|
+
const home = homedir();
|
|
102
|
+
const profilePath = shell.endsWith("/zsh")
|
|
103
|
+
? join(home, ".zshrc")
|
|
104
|
+
: shell.endsWith("/bash")
|
|
105
|
+
? join(home, ".bash_profile")
|
|
106
|
+
: null;
|
|
107
|
+
if (!profilePath) return;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const contents = existsSync(profilePath)
|
|
111
|
+
? readFileSync(profilePath, "utf-8")
|
|
112
|
+
: "";
|
|
113
|
+
if (contents.includes(localBinDir)) return;
|
|
114
|
+
const line = `\nexport PATH="${localBinDir}:$PATH"\n`;
|
|
115
|
+
appendFileSync(profilePath, line);
|
|
116
|
+
log.info(
|
|
117
|
+
{ profilePath, localBinDir },
|
|
118
|
+
"Added ~/.local/bin to shell profile",
|
|
119
|
+
);
|
|
120
|
+
} catch {
|
|
121
|
+
// Not critical — user can add it manually
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Checks whether `assistant` already resolves on PATH to something other than
|
|
127
|
+
* our candidate symlink locations. If so, we skip symlinking to avoid
|
|
128
|
+
* overwriting a developer's local build.
|
|
129
|
+
*/
|
|
130
|
+
function commandResolvesElsewhere(
|
|
131
|
+
commandName: string,
|
|
132
|
+
candidatePaths: Set<string>,
|
|
133
|
+
): boolean {
|
|
134
|
+
try {
|
|
135
|
+
const resolved = execFileSync("/usr/bin/which", [commandName], {
|
|
136
|
+
encoding: "utf-8",
|
|
137
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
138
|
+
}).trim();
|
|
139
|
+
return resolved !== "" && !candidatePaths.has(resolved);
|
|
140
|
+
} catch {
|
|
141
|
+
// `which` exited non-zero — command not found, safe to proceed
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Idempotent: installs (or verifies) a symlink for the `assistant` command.
|
|
148
|
+
*
|
|
149
|
+
* Called on every daemon startup. Handles two runtime modes:
|
|
150
|
+
* - **Bundled binary** (desktop app): symlinks to the compiled
|
|
151
|
+
* `vellum-assistant` binary in the app bundle.
|
|
152
|
+
* - **Bun dev mode**: symlinks to a small wrapper script that invokes
|
|
153
|
+
* `bun run` on the assistant entrypoint.
|
|
154
|
+
*
|
|
155
|
+
* Tries `/usr/local/bin/assistant` first, then falls back to
|
|
156
|
+
* `~/.local/bin/assistant` (and patches the shell profile if needed).
|
|
157
|
+
*
|
|
158
|
+
* Skipped when VELLUM_DEV=1 (developers manage their own PATH).
|
|
159
|
+
*/
|
|
160
|
+
export function installAssistantSymlink(): void {
|
|
161
|
+
if (process.env.VELLUM_DEV === "1") return;
|
|
162
|
+
|
|
163
|
+
const target = resolveAssistantBinary();
|
|
164
|
+
if (!target) return;
|
|
165
|
+
|
|
166
|
+
const localBinDir = join(homedir(), ".local", "bin");
|
|
167
|
+
const candidateDirs = ["/usr/local/bin", localBinDir];
|
|
168
|
+
const candidatePaths = new Set(
|
|
169
|
+
candidateDirs.map((dir) => `${dir}/assistant`),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (commandResolvesElsewhere("assistant", candidatePaths)) {
|
|
173
|
+
log.info(
|
|
174
|
+
"`assistant` already resolves to a non-managed path — skipping symlink",
|
|
175
|
+
);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const dir of candidateDirs) {
|
|
180
|
+
const symlinkPath = join(dir, "assistant");
|
|
181
|
+
if (trySymlink(target, symlinkPath)) {
|
|
182
|
+
log.info({ symlinkPath, target }, "Installed assistant symlink");
|
|
183
|
+
if (dir === localBinDir) {
|
|
184
|
+
ensureLocalBinInShellProfile(localBinDir);
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
log.info(
|
|
189
|
+
{ symlinkPath },
|
|
190
|
+
"Could not install assistant symlink at candidate — trying next",
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
log.warn("Could not install assistant symlink in any candidate directory");
|
|
195
|
+
}
|