@vellumai/assistant 0.5.1 → 0.5.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/ARCHITECTURE.md +54 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +149 -5
- package/src/__tests__/conversation-agent-loop.test.ts +290 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +32 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-recall-quality.test.ts +5 -5
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +8 -0
- package/src/config/loader.ts +64 -0
- package/src/config/raw-config-utils.ts +30 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/services.ts +8 -6
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +48 -7
- package/src/daemon/conversation-agent-loop.ts +56 -19
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +39 -15
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +21 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +143 -20
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +51 -29
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +148 -0
- package/src/daemon/handlers/config-model.ts +71 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +106 -64
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +19 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +442 -3
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +28 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +9 -7
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +1 -4
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +6 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/oauth.ts +6 -0
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +97 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +71 -8
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +68 -0
- package/src/runtime/routes/conversation-query-routes.ts +180 -10
- package/src/runtime/routes/conversation-routes.ts +222 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1199 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +4 -0
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +28 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +9 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +2 -7
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +6 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
|
@@ -4,18 +4,14 @@ import { join } from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import QRCode from "qrcode";
|
|
6
6
|
|
|
7
|
+
import type { InterfaceId } from "../channels/types.js";
|
|
7
8
|
import { getGatewayPort, getIngressPublicBaseUrl } from "../config/env.js";
|
|
8
|
-
import { getConfig
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
getConfiguredProviders,
|
|
12
|
-
isProviderAvailable,
|
|
13
|
-
} from "../providers/provider-availability.js";
|
|
14
|
-
import { initializeProviders } from "../providers/registry.js";
|
|
9
|
+
import { getConfig } from "../config/loader.js";
|
|
10
|
+
import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
|
|
11
|
+
import { getConfiguredProviders } from "../providers/provider-availability.js";
|
|
15
12
|
import { getLocalIPv4 } from "../util/network-info.js";
|
|
16
13
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
17
14
|
import { silentlyWithLog } from "../util/silently.js";
|
|
18
|
-
import { getAssistantName } from "./identity-helpers.js";
|
|
19
15
|
import type { PairingStore } from "./pairing-store.js";
|
|
20
16
|
|
|
21
17
|
export type SlashResolution =
|
|
@@ -45,160 +41,51 @@ export interface SlashContext {
|
|
|
45
41
|
model: string;
|
|
46
42
|
provider: string;
|
|
47
43
|
estimatedCost: number;
|
|
44
|
+
userMessageInterface?: InterfaceId;
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
// ──
|
|
51
|
-
|
|
52
|
-
const AVAILABLE_MODELS = [
|
|
53
|
-
"claude-opus-4-6",
|
|
54
|
-
"claude-sonnet-4-6",
|
|
55
|
-
"claude-haiku-4-5-20251001",
|
|
56
|
-
] as const;
|
|
57
|
-
|
|
58
|
-
const MODEL_DISPLAY_NAMES: Record<string, string> = {
|
|
59
|
-
"claude-opus-4-6": "Claude Opus 4.6",
|
|
60
|
-
"claude-sonnet-4-6": "Claude Sonnet 4.6",
|
|
61
|
-
"claude-haiku-4-5-20251001": "Claude Haiku 4.5",
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const PROVIDER_MODEL_SHORTCUTS: Record<
|
|
65
|
-
string,
|
|
66
|
-
{ provider: string; model: string; displayName: string }
|
|
67
|
-
> = {
|
|
68
|
-
// Anthropic
|
|
69
|
-
opus: {
|
|
70
|
-
provider: "anthropic",
|
|
71
|
-
model: "claude-opus-4-6",
|
|
72
|
-
displayName: "Claude Opus 4.6",
|
|
73
|
-
},
|
|
74
|
-
sonnet: {
|
|
75
|
-
provider: "anthropic",
|
|
76
|
-
model: "claude-sonnet-4-6",
|
|
77
|
-
displayName: "Claude Sonnet 4.6",
|
|
78
|
-
},
|
|
79
|
-
haiku: {
|
|
80
|
-
provider: "anthropic",
|
|
81
|
-
model: "claude-haiku-4-5-20251001",
|
|
82
|
-
displayName: "Claude Haiku 4.5",
|
|
83
|
-
},
|
|
84
|
-
"grok-beta": {
|
|
85
|
-
provider: "openrouter",
|
|
86
|
-
model: "x-ai/grok-4.20-beta",
|
|
87
|
-
displayName: "Grok 4.20 Beta (OpenRouter)",
|
|
88
|
-
},
|
|
89
|
-
"grok-multi": {
|
|
90
|
-
provider: "openrouter",
|
|
91
|
-
model: "x-ai/grok-4.20-beta",
|
|
92
|
-
displayName: "Grok 4.20 Beta (OpenRouter)",
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/** True when the trimmed content matches a provider shortcut like /opus, /gpt4, etc. */
|
|
97
|
-
export function isProviderShortcut(content: string): boolean {
|
|
98
|
-
const match = content.trim().match(/^\/([a-z0-9-]+)(\s|$)/i);
|
|
99
|
-
if (!match) return false;
|
|
100
|
-
return match[1].toLowerCase() in PROVIDER_MODEL_SHORTCUTS;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Reverse lookup: model ID → provider, derived from PROVIDER_MODEL_SHORTCUTS. */
|
|
104
|
-
export const MODEL_TO_PROVIDER: Record<string, string> = Object.fromEntries(
|
|
105
|
-
Object.values(PROVIDER_MODEL_SHORTCUTS).map(({ model, provider }) => [
|
|
106
|
-
model,
|
|
107
|
-
provider,
|
|
108
|
-
]),
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
/** Partial-match a user input like "opus", "sonnet", "haiku" to a full model ID. */
|
|
112
|
-
function matchModel(input: string): string | undefined {
|
|
113
|
-
const lower = input.toLowerCase().trim();
|
|
114
|
-
// Exact match first
|
|
115
|
-
const exact = AVAILABLE_MODELS.find((m) => m === lower);
|
|
116
|
-
if (exact) return exact;
|
|
117
|
-
// Partial match (e.g. "opus" → "claude-opus-4-6")
|
|
118
|
-
return AVAILABLE_MODELS.find((m) => m.includes(lower));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function resolveProviderModelCommand(
|
|
122
|
-
content: string,
|
|
123
|
-
): Promise<SlashResolution | null> {
|
|
124
|
-
const trimmed = content.trim();
|
|
125
|
-
if (!trimmed.startsWith("/")) return null;
|
|
47
|
+
// ── Deprecated model-switching shortcuts ─────────────────────────────
|
|
126
48
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Check if provider is available (secure key, env var, managed proxy, or no key needed)
|
|
140
|
-
if (!(await isProviderAvailable(provider))) {
|
|
141
|
-
return {
|
|
142
|
-
kind: "unknown",
|
|
143
|
-
message: `Cannot switch to ${displayName}. No API key configured for ${provider}.\n\nSet it with: \`keys set ${provider} <your-key>\``,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Check if already using this provider+model
|
|
148
|
-
if (
|
|
149
|
-
config.services.inference.provider === provider &&
|
|
150
|
-
config.services.inference.model === model
|
|
151
|
-
) {
|
|
152
|
-
const alreadyMsg = name
|
|
153
|
-
? `${name} is already running on **${displayName}**.`
|
|
154
|
-
: `Already using **${displayName}**.`;
|
|
155
|
-
return {
|
|
156
|
-
kind: "unknown",
|
|
157
|
-
message: alreadyMsg,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Update config with both provider and model
|
|
162
|
-
const raw = loadRawConfig();
|
|
163
|
-
setServiceField(raw, "inference", "provider", provider);
|
|
164
|
-
setServiceField(raw, "inference", "model", model);
|
|
165
|
-
saveRawConfig(raw);
|
|
166
|
-
|
|
167
|
-
// Re-initialize providers with new config
|
|
168
|
-
const newConfig = getConfig();
|
|
169
|
-
await initializeProviders(newConfig);
|
|
49
|
+
/**
|
|
50
|
+
* Former provider shortcut commands that switched models. These are now
|
|
51
|
+
* removed — model switching lives in Settings. We reject them explicitly
|
|
52
|
+
* so they don't fall through to the LLM as passthrough text.
|
|
53
|
+
*/
|
|
54
|
+
const DEPRECATED_MODEL_SHORTCUTS = new Set([
|
|
55
|
+
"opus",
|
|
56
|
+
"sonnet",
|
|
57
|
+
"haiku",
|
|
58
|
+
"grok-beta",
|
|
59
|
+
"grok-multi",
|
|
60
|
+
]);
|
|
170
61
|
|
|
171
|
-
|
|
172
|
-
? `Switched ${name} to **${displayName}**. New conversations will use this model.`
|
|
173
|
-
: `Switched to **${displayName}**. New conversations will use this model.`;
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
kind: "unknown",
|
|
177
|
-
message: switchedMsg,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
62
|
+
// ── /models command ──────────────────────────────────────────────────
|
|
180
63
|
|
|
181
64
|
async function resolveModelList(): Promise<SlashResolution> {
|
|
182
65
|
const config = getConfig();
|
|
183
|
-
|
|
184
|
-
// Build a set of providers that are usable (secure key, env var, or managed proxy).
|
|
185
66
|
const configuredProviders = new Set<string>(await getConfiguredProviders());
|
|
186
67
|
|
|
187
68
|
const lines = ["Available models:\n"];
|
|
188
69
|
|
|
189
|
-
for (const
|
|
190
|
-
|
|
191
|
-
|
|
70
|
+
for (const {
|
|
71
|
+
id: provider,
|
|
72
|
+
displayName: providerName,
|
|
73
|
+
models,
|
|
74
|
+
} of PROVIDER_CATALOG) {
|
|
192
75
|
const hasKey = configuredProviders.has(provider);
|
|
193
|
-
const isCurrent =
|
|
194
|
-
config.services.inference.provider === provider &&
|
|
195
|
-
config.services.inference.model === model;
|
|
196
76
|
const status = hasKey ? "✓" : "✗";
|
|
197
|
-
|
|
198
|
-
|
|
77
|
+
lines.push(`**${providerName}** ${status}`);
|
|
78
|
+
for (const { id, displayName } of models) {
|
|
79
|
+
const isCurrent =
|
|
80
|
+
config.services.inference.provider === provider &&
|
|
81
|
+
config.services.inference.model === id;
|
|
82
|
+
const current = isCurrent ? " **[current]**" : "";
|
|
83
|
+
lines.push(` - ${displayName} (\`${id}\`)${current}`);
|
|
84
|
+
}
|
|
85
|
+
lines.push("");
|
|
199
86
|
}
|
|
200
87
|
|
|
201
|
-
lines.push("
|
|
88
|
+
lines.push("✓ = API key configured, ✗ = not configured");
|
|
202
89
|
lines.push("\nTip: Configure a provider with `keys set <provider> <key>`");
|
|
203
90
|
|
|
204
91
|
return {
|
|
@@ -207,92 +94,6 @@ async function resolveModelList(): Promise<SlashResolution> {
|
|
|
207
94
|
};
|
|
208
95
|
}
|
|
209
96
|
|
|
210
|
-
async function resolveModelCommand(
|
|
211
|
-
content: string,
|
|
212
|
-
): Promise<SlashResolution | null> {
|
|
213
|
-
const trimmed = content.trim();
|
|
214
|
-
// Match /models → route to list
|
|
215
|
-
if (trimmed === "/models") {
|
|
216
|
-
return await resolveModelList();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (!trimmed.startsWith("/model")) return null;
|
|
220
|
-
// Ensure it's exactly "/model" or "/model " (not "/modelsomething")
|
|
221
|
-
if (trimmed.length > 6 && trimmed[6] !== " ") return null;
|
|
222
|
-
|
|
223
|
-
const args = trimmed.slice(6).trim();
|
|
224
|
-
const name = getAssistantName();
|
|
225
|
-
|
|
226
|
-
if (!args) {
|
|
227
|
-
// Show current model
|
|
228
|
-
const config = getConfig();
|
|
229
|
-
const displayName =
|
|
230
|
-
MODEL_DISPLAY_NAMES[config.services.inference.model] ??
|
|
231
|
-
config.services.inference.model;
|
|
232
|
-
const prefix = name ? `${name} is running on` : `Currently using`;
|
|
233
|
-
return {
|
|
234
|
-
kind: "unknown",
|
|
235
|
-
message: `${prefix} **${displayName}** (\`${config.services.inference.model}\`).`,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Handle /model list
|
|
240
|
-
if (args === "list") {
|
|
241
|
-
return await resolveModelList();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Try to match the model name
|
|
245
|
-
const matched = matchModel(args);
|
|
246
|
-
if (!matched) {
|
|
247
|
-
const available = AVAILABLE_MODELS.map(
|
|
248
|
-
(m) => `- **${MODEL_DISPLAY_NAMES[m]}** (\`${m}\`)`,
|
|
249
|
-
).join("\n");
|
|
250
|
-
return {
|
|
251
|
-
kind: "unknown",
|
|
252
|
-
message: `Hmm, "${args}" doesn't match any available model. Here's what you can pick from:\n${available}`,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Check if already using this model
|
|
257
|
-
const currentConfig = getConfig();
|
|
258
|
-
if (currentConfig.services.inference.model === matched) {
|
|
259
|
-
const displayName = MODEL_DISPLAY_NAMES[matched] ?? matched;
|
|
260
|
-
const alreadyMsg = name
|
|
261
|
-
? `${name} is already running on **${displayName}**.`
|
|
262
|
-
: `Already on **${displayName}**.`;
|
|
263
|
-
return {
|
|
264
|
-
kind: "unknown",
|
|
265
|
-
message: alreadyMsg,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Validate that Anthropic provider is available (secure key, env var, or managed proxy)
|
|
270
|
-
if (!(await isProviderAvailable("anthropic"))) {
|
|
271
|
-
const displayName = MODEL_DISPLAY_NAMES[matched] ?? matched;
|
|
272
|
-
return {
|
|
273
|
-
kind: "unknown",
|
|
274
|
-
message: `Cannot switch to ${displayName}. No API key configured for Anthropic.\n\nSet it with: \`keys set anthropic <your-key>\``,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Change model: save config and re-initialize providers
|
|
279
|
-
const raw = loadRawConfig();
|
|
280
|
-
setServiceField(raw, "inference", "provider", "anthropic"); // Ensure provider is set for Anthropic models
|
|
281
|
-
setServiceField(raw, "inference", "model", matched);
|
|
282
|
-
saveRawConfig(raw);
|
|
283
|
-
const config = getConfig();
|
|
284
|
-
await initializeProviders(config);
|
|
285
|
-
|
|
286
|
-
const displayName = MODEL_DISPLAY_NAMES[matched] ?? matched;
|
|
287
|
-
const switchedMsg = name
|
|
288
|
-
? `Switched ${name} to **${displayName}**. New conversations will use this model.`
|
|
289
|
-
: `Switched to **${displayName}**. New conversations will use this model.`;
|
|
290
|
-
return {
|
|
291
|
-
kind: "unknown",
|
|
292
|
-
message: switchedMsg,
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
|
|
296
97
|
function resolveStatusCommand(context: SlashContext): SlashResolution {
|
|
297
98
|
const {
|
|
298
99
|
inputTokens,
|
|
@@ -310,14 +111,13 @@ function resolveStatusCommand(context: SlashContext): SlashResolution {
|
|
|
310
111
|
const filled = Math.round(pct / 5);
|
|
311
112
|
const bar = "█".repeat(filled) + "░".repeat(20 - filled);
|
|
312
113
|
const fmt = (n: number) => n.toLocaleString("en-US");
|
|
313
|
-
const displayName = MODEL_DISPLAY_NAMES[model] ?? model;
|
|
314
114
|
|
|
315
115
|
const lines = [
|
|
316
116
|
"Conversation Status\n",
|
|
317
117
|
`Context: ${bar} ${pct}% (${fmt(inputTokens)} / ${fmt(
|
|
318
118
|
maxInputTokens,
|
|
319
119
|
)} tokens)`,
|
|
320
|
-
`Model: ${
|
|
120
|
+
`Model: ${model} (${provider})`,
|
|
321
121
|
`Messages: ${fmt(messageCount)}`,
|
|
322
122
|
`Tokens: ${fmt(inputTokens)} in / ${fmt(outputTokens)} out`,
|
|
323
123
|
`Cost: $${estimatedCost.toFixed(2)} (estimated)`,
|
|
@@ -326,28 +126,91 @@ function resolveStatusCommand(context: SlashContext): SlashResolution {
|
|
|
326
126
|
return { kind: "unknown", message: lines.join("\n") };
|
|
327
127
|
}
|
|
328
128
|
|
|
129
|
+
function resolveCommandsList(context?: SlashContext): string[] {
|
|
130
|
+
const fallbackLines = [
|
|
131
|
+
"/commands — List all available commands",
|
|
132
|
+
"/models — List all available models",
|
|
133
|
+
"/pair — Generate pairing info for connecting a mobile device",
|
|
134
|
+
];
|
|
135
|
+
if (context) {
|
|
136
|
+
fallbackLines.push("/status — Show conversation status and context usage");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!context?.userMessageInterface) return fallbackLines;
|
|
140
|
+
|
|
141
|
+
if (context.userMessageInterface === "macos") {
|
|
142
|
+
return [
|
|
143
|
+
"/commands — List all available commands",
|
|
144
|
+
"/models — List all available models",
|
|
145
|
+
"/status — Show conversation status and context usage",
|
|
146
|
+
"/btw — Ask a side question while the assistant is working",
|
|
147
|
+
"/fork — Fork the current conversation into a new branch",
|
|
148
|
+
"/pair — Generate pairing info for connecting a mobile device",
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (context.userMessageInterface === "ios") {
|
|
153
|
+
return [
|
|
154
|
+
"/commands — List all available commands",
|
|
155
|
+
"/models — List all available models",
|
|
156
|
+
"/status — Show conversation status and context usage",
|
|
157
|
+
"/btw — Ask a side question while the assistant is working",
|
|
158
|
+
"/fork — Fork the current conversation into a new branch",
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return [
|
|
163
|
+
"/commands — List all available commands",
|
|
164
|
+
"/models — List all available models",
|
|
165
|
+
"/status — Show conversation status and context usage",
|
|
166
|
+
"/btw — Ask a side question while the assistant is working",
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
|
|
329
170
|
/**
|
|
330
|
-
* Resolve built-in slash commands (/
|
|
171
|
+
* Resolve built-in slash commands (/models, /status, /commands, /pair).
|
|
331
172
|
* Returns `unknown` with a deterministic message, or the (possibly rewritten) content.
|
|
332
173
|
*/
|
|
333
174
|
export async function resolveSlash(
|
|
334
175
|
content: string,
|
|
335
176
|
context?: SlashContext,
|
|
336
177
|
): Promise<SlashResolution> {
|
|
337
|
-
//
|
|
338
|
-
const
|
|
339
|
-
if (
|
|
178
|
+
// Handle deprecated model-switching commands — direct users to Settings
|
|
179
|
+
const trimmed = content.trim();
|
|
180
|
+
if (
|
|
181
|
+
trimmed === "/model" ||
|
|
182
|
+
(trimmed.startsWith("/model ") && trimmed !== "/models")
|
|
183
|
+
) {
|
|
184
|
+
return {
|
|
185
|
+
kind: "unknown",
|
|
186
|
+
message:
|
|
187
|
+
"The `/model` command has been removed. Use **Settings → Models & Services** to change your model and provider.",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
340
190
|
|
|
341
|
-
//
|
|
342
|
-
const
|
|
343
|
-
if (
|
|
191
|
+
// Reject deprecated provider shortcut commands (/opus, /sonnet, /haiku, etc.)
|
|
192
|
+
const shortcutMatch = trimmed.match(/^\/([a-z0-9-]+)(\s|$)/i);
|
|
193
|
+
if (
|
|
194
|
+
shortcutMatch &&
|
|
195
|
+
DEPRECATED_MODEL_SHORTCUTS.has(shortcutMatch[1].toLowerCase())
|
|
196
|
+
) {
|
|
197
|
+
return {
|
|
198
|
+
kind: "unknown",
|
|
199
|
+
message: `The \`/${shortcutMatch[1]}\` shortcut has been removed. Use **Settings → Models & Services** to change your model and provider.`,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Handle /models command (read-only listing)
|
|
204
|
+
if (trimmed === "/models") {
|
|
205
|
+
return await resolveModelList();
|
|
206
|
+
}
|
|
344
207
|
|
|
345
208
|
// Handle /pair command
|
|
346
|
-
const pairResult = resolvePairCommand(content);
|
|
209
|
+
const pairResult = resolvePairCommand(content, context);
|
|
347
210
|
if (pairResult) return pairResult;
|
|
348
211
|
|
|
349
212
|
// Handle /status command
|
|
350
|
-
if (
|
|
213
|
+
if (trimmed === "/status") {
|
|
351
214
|
if (!context) {
|
|
352
215
|
return {
|
|
353
216
|
kind: "unknown",
|
|
@@ -358,19 +221,10 @@ export async function resolveSlash(
|
|
|
358
221
|
}
|
|
359
222
|
|
|
360
223
|
// Handle /commands command
|
|
361
|
-
if (
|
|
362
|
-
const lines = [
|
|
363
|
-
"/commands — List all available commands",
|
|
364
|
-
"/model — Show or switch the current model",
|
|
365
|
-
"/models — List all available models",
|
|
366
|
-
"/pair — Generate pairing info for connecting a mobile device",
|
|
367
|
-
];
|
|
368
|
-
if (context) {
|
|
369
|
-
lines.push("/status — Show conversation status and context usage");
|
|
370
|
-
}
|
|
224
|
+
if (trimmed === "/commands") {
|
|
371
225
|
return {
|
|
372
226
|
kind: "unknown",
|
|
373
|
-
message:
|
|
227
|
+
message: resolveCommandsList(context).join("\n"),
|
|
374
228
|
};
|
|
375
229
|
}
|
|
376
230
|
|
|
@@ -406,9 +260,20 @@ async function savePairingQRCodePng(
|
|
|
406
260
|
writeFileSync(qrPngPath, pngBuffer);
|
|
407
261
|
}
|
|
408
262
|
|
|
409
|
-
function resolvePairCommand(
|
|
263
|
+
function resolvePairCommand(
|
|
264
|
+
content: string,
|
|
265
|
+
context?: SlashContext,
|
|
266
|
+
): SlashResolution | null {
|
|
410
267
|
if (content.trim() !== "/pair") return null;
|
|
411
268
|
|
|
269
|
+
if (context?.userMessageInterface && context.userMessageInterface !== "macos") {
|
|
270
|
+
return {
|
|
271
|
+
kind: "unknown",
|
|
272
|
+
message:
|
|
273
|
+
"The `/pair` command is only available in the macOS desktop app.",
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
412
277
|
if (!pairingStoreRef) {
|
|
413
278
|
return {
|
|
414
279
|
kind: "unknown",
|