@vellumai/assistant 0.12.5 → 0.12.6-dev.202609292116.41f7caa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/ARCHITECTURE.md +26 -3
- package/docs/automatic-done.md +32 -0
- package/docs/desktop-browser-cli.md +3 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/environments/src/shell.test.ts +13 -1
- package/node_modules/@vellumai/environments/src/shell.ts +22 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/status-contracts.test.ts +39 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +25 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/openapi.yaml +270 -10
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +76 -0
- package/src/__tests__/anthropic-provider.test.ts +2 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +7 -0
- package/src/__tests__/config-get-vision-flag.test.ts +1 -1
- package/src/__tests__/config-loader-validation-reset-notice.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +160 -154
- package/src/__tests__/conversation-evictor.test.ts +54 -0
- package/src/__tests__/conversation-pairing.test.ts +112 -0
- package/src/__tests__/document-append-idempotency.test.ts +13 -0
- package/src/__tests__/document-create-dedupe.test.ts +13 -0
- package/src/__tests__/document-find-replace.test.ts +13 -0
- package/src/__tests__/document-revision-history.test.ts +390 -0
- package/src/__tests__/document-revision.test.ts +425 -0
- package/src/__tests__/document-tool-security.test.ts +13 -0
- package/src/__tests__/document-update-default-surface.test.ts +13 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +270 -0
- package/src/__tests__/gateway-flag-listener.test.ts +59 -6
- package/src/__tests__/headless-browser-navigate.test.ts +20 -30
- package/src/__tests__/host-cu-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +14 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -2
- package/src/__tests__/identity-routes.test.ts +4 -3
- package/src/__tests__/list-messages-client-message-id.test.ts +43 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -4
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/notification-platform-adapter.test.ts +83 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
- package/src/__tests__/openai-provider.test.ts +38 -11
- package/src/__tests__/pricing.test.ts +11 -0
- package/src/__tests__/public-ingress-urls.test.ts +26 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +47 -0
- package/src/__tests__/skill-execute-input.test.ts +11 -0
- package/src/__tests__/tool-approval-handler.test.ts +4 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +26 -0
- package/src/__tests__/usage-attribution.test.ts +31 -0
- package/src/__tests__/workspace-migration-133-collapse-provider-connections.test.ts +3 -3
- package/src/__tests__/workspace-migration-160-repair-retired-fireworks-model-ids.test.ts +262 -0
- package/src/__tests__/workspace-migration-161-repair-retired-fireworks-glm-5p2-model-id.test.ts +233 -0
- package/src/acp/agent-process.ts +6 -1
- package/src/acp/client-handler.ts +6 -1
- package/src/agent/loop-unparseable-tool-retry.test.ts +333 -0
- package/src/agent/loop.ts +161 -17
- package/src/api/events/document-editor-show.ts +5 -0
- package/src/api/events/document-editor-update.ts +10 -0
- package/src/api/events/tool-result.ts +1 -0
- package/src/calls/__tests__/call-controller.test.ts +273 -1
- package/src/calls/__tests__/media-stream-server-integration.test.ts +40 -0
- package/src/calls/__tests__/media-stream-stt-session.test.ts +33 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +54 -0
- package/src/calls/call-constants.ts +10 -0
- package/src/calls/call-controller.ts +359 -25
- package/src/calls/channel-admission-reader.ts +15 -9
- package/src/calls/media-stream-server.ts +18 -0
- package/src/calls/media-stream-stt-session.ts +30 -9
- package/src/calls/twilio-provider.ts +14 -31
- package/src/calls/voice-session-bridge.ts +18 -1
- package/src/cli/commands/browser.ts +1 -2
- package/src/cli/lib/__tests__/plugin-artifact.test.ts +23 -0
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +2 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +16 -0
- package/src/cli/lib/bundled-marketplace.json +54 -2
- package/src/cli/lib/inspect-plugin.ts +15 -15
- package/src/cli/lib/list-installed-plugins.ts +29 -28
- package/src/cli/lib/plugin-artifact.ts +74 -63
- package/src/cli/lib/plugin-catalog-visibility.ts +1 -0
- package/src/cli/lib/plugin-details.ts +27 -42
- package/src/cli/lib/plugin-marketplace.ts +6 -8
- package/src/cli/lib/plugin-surfaces.ts +8 -7
- package/src/cli.ts +1 -3
- package/src/config/__tests__/chat-settings.test.ts +121 -0
- package/src/config/assistant-feature-flags.ts +28 -23
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +11 -11
- package/src/config/chat-settings.ts +100 -0
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/feature-flag-cache.ts +7 -25
- package/src/config/feature-flag-registry.json +9 -9
- package/src/config/loader.ts +5 -1
- package/src/config/profile-text-generation.ts +25 -21
- package/src/config/schemas/conversations.ts +14 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/notifications.ts +8 -1
- package/src/contacts/gateway-channel-read.ts +1 -1
- package/src/conversations/__tests__/auto-archive-activity.test.ts +136 -0
- package/src/conversations/__tests__/auto-archive.test.ts +304 -0
- package/src/conversations/auto-archive-activity.ts +61 -0
- package/src/conversations/auto-archive.ts +201 -0
- package/src/daemon/computer-use-observation.test.ts +81 -0
- package/src/daemon/computer-use-observation.ts +23 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -2
- package/src/daemon/conversation-agent-loop.ts +14 -6
- package/src/daemon/conversation-evictor.ts +50 -16
- package/src/daemon/conversation-messaging.ts +4 -0
- package/src/daemon/conversation-process.ts +15 -0
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/doordash-steps.ts +1 -3
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +43 -1
- package/src/daemon/handlers/__tests__/config-channels.test.ts +1 -1
- package/src/daemon/handlers/config-a2a.ts +75 -2
- package/src/daemon/handlers/shared.ts +13 -2
- package/src/daemon/host-cu-proxy.ts +34 -3
- package/src/daemon/lifecycle.ts +15 -2
- package/src/daemon/process-message.ts +5 -0
- package/src/daemon/shutdown-handlers.ts +3 -0
- package/src/daemon/turn-finalization.ts +4 -0
- package/src/desktop/desktop-browser-endpoint.ts +11 -2
- package/src/desktop/desktop-chrome-policy.test.ts +1 -0
- package/src/desktop/desktop-chrome-policy.ts +7 -1
- package/src/desktop/desktop-computer-use.test.ts +14 -4
- package/src/desktop/desktop-session-manager.ts +5 -3
- package/src/documents/document-revisions-store.ts +153 -0
- package/src/documents/document-store.ts +471 -133
- package/src/followups/followup-store.ts +0 -4
- package/src/i18n/__tests__/i18n.test.ts +25 -0
- package/src/i18n/assistant-text.ts +18 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/messages.ts +11 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +25 -2
- package/src/inbound/oauth-callback-url.ts +30 -1
- package/src/inbound/public-ingress-urls.ts +12 -2
- package/src/ipc/gateway-client.test.ts +40 -0
- package/src/ipc/gateway-client.ts +12 -29
- package/src/ipc/gateway-flag-listener.ts +7 -10
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +71 -0
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +83 -0
- package/src/live-voice/__tests__/protocol.test.ts +77 -0
- package/src/live-voice/__tests__/share-targets.test.ts +163 -0
- package/src/live-voice/live-voice-session.ts +23 -6
- package/src/live-voice/protocol.ts +22 -0
- package/src/live-voice/share-targets.ts +225 -0
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +144 -3
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -0
- package/src/mcp/client.ts +1 -19
- package/src/mcp/mcp-oauth-provider.ts +66 -49
- package/src/media/openrouter-image-service.ts +39 -22
- package/src/monitoring/__tests__/oom-inheritance-sweep.test.ts +86 -0
- package/src/monitoring/__tests__/oom-kill-reporter.test.ts +374 -0
- package/src/monitoring/control.ts +2 -1
- package/src/monitoring/oom-inheritance-sweep.ts +96 -0
- package/src/monitoring/oom-kill-reporter.ts +469 -0
- package/src/monitoring/resource-sampler.ts +30 -0
- package/src/notifications/README.md +53 -4
- package/src/notifications/__tests__/assistant-initiated-thread.test.ts +254 -0
- package/src/notifications/__tests__/broadcaster.test.ts +558 -6
- package/src/notifications/__tests__/completion-policy.test.ts +37 -0
- package/src/notifications/__tests__/from-me-alert-integration.test.ts +192 -0
- package/src/notifications/adapters/platform.ts +14 -0
- package/src/notifications/assistant-initiated-thread.ts +101 -0
- package/src/notifications/broadcaster.ts +94 -16
- package/src/notifications/chat-reply-policy.ts +11 -0
- package/src/notifications/completion-policy.ts +12 -2
- package/src/notifications/conversation-pairing.ts +15 -70
- package/src/notifications/emit-signal.ts +34 -3
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connect-orchestrator.ts +1 -2
- package/src/persistence/__tests__/conversation-auto-archive.test.ts +330 -0
- package/src/persistence/conversation-auto-archive.ts +170 -0
- package/src/persistence/conversation-crud.ts +2 -1
- package/src/persistence/conversation-queries.ts +15 -8
- package/src/persistence/embeddings/embed.test.ts +96 -0
- package/src/persistence/embeddings/embed.ts +20 -2
- package/src/persistence/embeddings/embedding-gemini.test.ts +39 -3
- package/src/persistence/embeddings/embedding-gemini.ts +39 -1
- package/src/persistence/embeddings/embedding-local.ts +6 -0
- package/src/persistence/embeddings/qdrant-manager.ts +5 -0
- package/src/persistence/job-utils.ts +10 -0
- package/src/persistence/migrations/384-add-document-revision.test.ts +83 -0
- package/src/persistence/migrations/384-add-document-revision.ts +21 -0
- package/src/persistence/migrations/385-create-document-revisions.test.ts +67 -0
- package/src/persistence/migrations/385-create-document-revisions.ts +28 -0
- package/src/persistence/migrations/386-conversations-last-reopened-at.ts +10 -0
- package/src/persistence/migrations/__tests__/386-conversations-last-reopened-at.test.ts +32 -0
- package/src/persistence/schema/conversations.ts +1 -0
- package/src/persistence/schema/documents.ts +20 -0
- package/src/persistence/steps.ts +6 -0
- package/src/platform/managed-speech.ts +42 -46
- package/src/plugin-api/vision-support.test.ts +21 -21
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +11 -6
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +1 -1
- package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
- package/src/providers/__tests__/unparseable-tool-args.test.ts +25 -0
- package/src/providers/anthropic/client.ts +1 -1
- package/src/providers/fireworks/client.test.ts +168 -0
- package/src/providers/fireworks/client.ts +24 -0
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/ionet/client.test.ts +143 -0
- package/src/providers/ionet/client.ts +37 -0
- package/src/providers/model-catalog.ts +141 -84
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +33 -2
- package/src/providers/retry.ts +56 -4
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +10 -2
- package/src/providers/unparseable-tool-args.ts +29 -5
- package/src/providers/usage-tracking.ts +5 -3
- package/src/runtime/assistant-event-hub.ts +23 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +0 -7
- package/src/runtime/auth/__tests__/route-policy.test.ts +1 -1
- package/src/runtime/channel-reply-delivery.ts +2 -0
- package/src/runtime/channel-retry-sweep.ts +1 -2
- package/src/runtime/http-types.ts +3 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +35 -10
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +48 -0
- package/src/runtime/migrations/vbundle-builder.ts +19 -3
- package/src/runtime/migrations/vbundle-streaming-importer.ts +14 -19
- package/src/runtime/migrations/vbundle-streaming-validator.ts +14 -7
- package/src/runtime/migrations/vbundle-validator.ts +54 -1
- package/src/runtime/pending-interactions.ts +20 -2
- package/src/runtime/routes/__tests__/contact-routes.test.ts +5 -3
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +36 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +375 -2
- package/src/runtime/routes/__tests__/inference-send-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +12 -0
- package/src/runtime/routes/contact-routes.ts +2 -2
- package/src/runtime/routes/conversation-list-routes.ts +10 -2
- package/src/runtime/routes/conversation-query-routes.ts +48 -10
- package/src/runtime/routes/conversation-routes.ts +7 -0
- package/src/runtime/routes/documents-routes.ts +185 -47
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -0
- package/src/runtime/routes/host-bash-routes.ts +1 -0
- package/src/runtime/routes/host-browser-routes.ts +15 -0
- package/src/runtime/routes/host-cu-routes.ts +4 -1
- package/src/runtime/routes/host-file-routes.ts +1 -0
- package/src/runtime/routes/host-proxy-result-binding.test.ts +83 -0
- package/src/runtime/routes/host-proxy-result-binding.ts +25 -0
- package/src/runtime/routes/host-transfer-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/integrations/a2a.ts +4 -1
- package/src/runtime/routes/migration-routes.ts +3 -1
- package/src/runtime/routes/notification-routes.ts +1 -2
- package/src/runtime/routes/ui-snapshot-routes.test.ts +1 -0
- package/src/runtime/routes/user-route-dispatcher.ts +5 -1
- package/src/runtime/verification-templates.ts +1 -2
- package/src/sequence/analytics.ts +0 -4
- package/src/sequence/engine.ts +1 -11
- package/src/sequence/guardrails.ts +1 -6
- package/src/sequence/importer.ts +0 -8
- package/src/sequence/store.ts +0 -10
- package/src/signals/user-message.ts +0 -6
- package/src/skills/catalog-install.ts +52 -23
- package/src/tools/browser/browser-execution.ts +20 -23
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +1 -3
- package/src/tools/document/document-tool.ts +13 -11
- package/src/tools/executor.ts +1 -2
- package/src/tools/host-terminal/host-shell.ts +4 -2
- package/src/tools/network/__tests__/web-search.test.ts +105 -0
- package/src/tools/network/web-fetch.ts +2 -4
- package/src/tools/network/web-search.ts +239 -12
- package/src/tools/shared/filesystem/format-diff.ts +1 -2
- package/src/tools/shared/shell-output.test.ts +42 -0
- package/src/tools/shared/shell-output.ts +27 -4
- package/src/tools/skills/execute.ts +10 -7
- package/src/tools/terminal/shell.ts +3 -3
- package/src/tools/tool-approval-handler.ts +9 -1
- package/src/tools/ui-surface/visual-validation.ts +1 -2
- package/src/usage/attribution.ts +18 -1
- package/src/util/__tests__/oom-priority.test.ts +55 -0
- package/src/util/__tests__/worker-memory.test.ts +16 -0
- package/src/util/debounce.ts +0 -4
- package/src/util/oom-priority.ts +79 -0
- package/src/util/platform.ts +0 -2
- package/src/util/plugin-manifest.ts +5 -7
- package/src/util/pricing.ts +25 -0
- package/src/util/worker-memory.ts +9 -3
- package/src/util/worker-process.ts +8 -0
- package/src/workspace/byok-default-profile-ensure.ts +11 -0
- package/src/workspace/hatched-date.ts +11 -4
- package/src/workspace/migrations/160-repair-retired-fireworks-model-ids.ts +209 -0
- package/src/workspace/migrations/161-repair-retired-fireworks-glm-5p2-model-id.ts +197 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateCreateDocumentRevisions } from "./385-create-document-revisions.js";
|
|
8
|
+
|
|
9
|
+
function createTestDb() {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
sqlite.exec(/*sql*/ `
|
|
12
|
+
PRAGMA foreign_keys = ON;
|
|
13
|
+
CREATE TABLE documents (
|
|
14
|
+
surface_id TEXT PRIMARY KEY,
|
|
15
|
+
title TEXT NOT NULL,
|
|
16
|
+
content TEXT NOT NULL
|
|
17
|
+
);
|
|
18
|
+
INSERT INTO documents (surface_id, title, content) VALUES ('doc-1', 'Doc', 'body');
|
|
19
|
+
`);
|
|
20
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function insertSnapshot(sqlite: Database, revision: number): void {
|
|
24
|
+
sqlite
|
|
25
|
+
.query(
|
|
26
|
+
/*sql*/ `INSERT INTO document_revisions (surface_id, revision, title, content, author, created_at)
|
|
27
|
+
VALUES ('doc-1', ?, 'Doc', 'body', 'user', 1000)`,
|
|
28
|
+
)
|
|
29
|
+
.run(revision);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("migration 385: document_revisions", () => {
|
|
33
|
+
test("creates the table keyed by (surface_id, revision)", () => {
|
|
34
|
+
const { sqlite, db } = createTestDb();
|
|
35
|
+
|
|
36
|
+
migrateCreateDocumentRevisions(db);
|
|
37
|
+
|
|
38
|
+
insertSnapshot(sqlite, 1);
|
|
39
|
+
expect(() => insertSnapshot(sqlite, 1)).toThrow(/UNIQUE/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("cascades from documents", () => {
|
|
43
|
+
const { sqlite, db } = createTestDb();
|
|
44
|
+
migrateCreateDocumentRevisions(db);
|
|
45
|
+
insertSnapshot(sqlite, 1);
|
|
46
|
+
|
|
47
|
+
sqlite.run("DELETE FROM documents WHERE surface_id = 'doc-1'");
|
|
48
|
+
|
|
49
|
+
const row = sqlite
|
|
50
|
+
.query("SELECT COUNT(*) AS n FROM document_revisions")
|
|
51
|
+
.get() as { n: number };
|
|
52
|
+
expect(row.n).toBe(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("is idempotent", () => {
|
|
56
|
+
const { sqlite, db } = createTestDb();
|
|
57
|
+
migrateCreateDocumentRevisions(db);
|
|
58
|
+
insertSnapshot(sqlite, 1);
|
|
59
|
+
|
|
60
|
+
expect(() => migrateCreateDocumentRevisions(db)).not.toThrow();
|
|
61
|
+
|
|
62
|
+
const row = sqlite
|
|
63
|
+
.query("SELECT COUNT(*) AS n FROM document_revisions")
|
|
64
|
+
.get() as { n: number };
|
|
65
|
+
expect(row.n).toBe(1);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create `document_revisions`: snapshots of a document's title and body as
|
|
6
|
+
* they stood at a given revision, taken just before a write replaces them.
|
|
7
|
+
*
|
|
8
|
+
* `(surface_id, revision)` is the primary key, so a state is stored at most
|
|
9
|
+
* once. The FK cascades from `documents`, so deleting a document drops its
|
|
10
|
+
* history.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent: `IF NOT EXISTS` makes a re-run a no-op.
|
|
13
|
+
*/
|
|
14
|
+
export function migrateCreateDocumentRevisions(database: DrizzleDb): void {
|
|
15
|
+
getSqliteFrom(database).exec(/*sql*/ `
|
|
16
|
+
CREATE TABLE IF NOT EXISTS document_revisions (
|
|
17
|
+
surface_id TEXT NOT NULL,
|
|
18
|
+
revision INTEGER NOT NULL,
|
|
19
|
+
title TEXT NOT NULL,
|
|
20
|
+
content TEXT NOT NULL,
|
|
21
|
+
word_count INTEGER NOT NULL DEFAULT 0,
|
|
22
|
+
author TEXT NOT NULL,
|
|
23
|
+
created_at INTEGER NOT NULL,
|
|
24
|
+
PRIMARY KEY (surface_id, revision),
|
|
25
|
+
FOREIGN KEY (surface_id) REFERENCES documents(surface_id) ON DELETE CASCADE
|
|
26
|
+
)
|
|
27
|
+
`);
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
import { tableHasColumn } from "./schema-introspection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateConversationsLastReopenedAt(database: DrizzleDb): void {
|
|
5
|
+
if (!tableHasColumn(database, "conversations", "last_reopened_at")) {
|
|
6
|
+
getSqliteFrom(database).exec(
|
|
7
|
+
"ALTER TABLE conversations ADD COLUMN last_reopened_at INTEGER",
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../../schema/index.js";
|
|
7
|
+
import { migrateConversationsLastReopenedAt } from "../386-conversations-last-reopened-at.js";
|
|
8
|
+
|
|
9
|
+
test("adds a nullable reopen marker without changing existing chats and is idempotent", () => {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
try {
|
|
12
|
+
sqlite.exec(
|
|
13
|
+
"CREATE TABLE conversations (id TEXT PRIMARY KEY, archived_at INTEGER)",
|
|
14
|
+
);
|
|
15
|
+
sqlite.exec("INSERT INTO conversations VALUES ('conv-123', 123)");
|
|
16
|
+
const db = drizzle(sqlite, { schema });
|
|
17
|
+
migrateConversationsLastReopenedAt(db);
|
|
18
|
+
migrateConversationsLastReopenedAt(db);
|
|
19
|
+
expect(sqlite.query("SELECT * FROM conversations").get()).toEqual({
|
|
20
|
+
id: "conv-123",
|
|
21
|
+
archived_at: 123,
|
|
22
|
+
last_reopened_at: null,
|
|
23
|
+
});
|
|
24
|
+
sqlite.exec("UPDATE conversations SET last_reopened_at = 456");
|
|
25
|
+
migrateConversationsLastReopenedAt(db);
|
|
26
|
+
expect(
|
|
27
|
+
sqlite.query("SELECT last_reopened_at FROM conversations").get(),
|
|
28
|
+
).toEqual({ last_reopened_at: 456 });
|
|
29
|
+
} finally {
|
|
30
|
+
sqlite.close();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -83,6 +83,7 @@ export const conversations = sqliteTable(
|
|
|
83
83
|
scheduleJobId: text("schedule_job_id"),
|
|
84
84
|
lastMessageAt: integer("last_message_at"),
|
|
85
85
|
archivedAt: integer("archived_at"),
|
|
86
|
+
lastReopenedAt: integer("last_reopened_at"),
|
|
86
87
|
/**
|
|
87
88
|
* Epoch-ms timestamp set when a background/scheduled conversation is
|
|
88
89
|
* explicitly promoted into the sidebar's Recents grouping. NULL (the
|
|
@@ -22,6 +22,8 @@ export const documents = sqliteTable(
|
|
|
22
22
|
wordCount: integer("word_count").notNull().default(0),
|
|
23
23
|
createdAt: integer("created_at").notNull(),
|
|
24
24
|
updatedAt: integer("updated_at").notNull(),
|
|
25
|
+
// Bumped by one on every write; conditional saves compare against it.
|
|
26
|
+
revision: integer("revision").notNull().default(0),
|
|
25
27
|
// Unused by the daemon: no code reads or writes the column, and some rows
|
|
26
28
|
// carry a non-NULL path. It stays in the model because migration 360 puts
|
|
27
29
|
// it in the physical table and the model must keep describing that table.
|
|
@@ -78,3 +80,21 @@ export const documentComments = sqliteTable(
|
|
|
78
80
|
index("idx_document_comments_parent").on(table.parentCommentId),
|
|
79
81
|
],
|
|
80
82
|
);
|
|
83
|
+
|
|
84
|
+
// Snapshots of a document as it stood at `revision`, taken just before a write
|
|
85
|
+
// replaced that state. `author` is who made the replacing write.
|
|
86
|
+
export const documentRevisions = sqliteTable(
|
|
87
|
+
"document_revisions",
|
|
88
|
+
{
|
|
89
|
+
surfaceId: text("surface_id")
|
|
90
|
+
.notNull()
|
|
91
|
+
.references(() => documents.surfaceId, { onDelete: "cascade" }),
|
|
92
|
+
revision: integer("revision").notNull(),
|
|
93
|
+
title: text("title").notNull(),
|
|
94
|
+
content: text("content").notNull(),
|
|
95
|
+
wordCount: integer("word_count").notNull().default(0),
|
|
96
|
+
author: text("author").notNull(),
|
|
97
|
+
createdAt: integer("created_at").notNull(),
|
|
98
|
+
},
|
|
99
|
+
(table) => [primaryKey({ columns: [table.surfaceId, table.revision] })],
|
|
100
|
+
);
|
package/src/persistence/steps.ts
CHANGED
|
@@ -494,6 +494,9 @@ import {
|
|
|
494
494
|
} from "./migrations/381-create-conversation-mode-sessions.js";
|
|
495
495
|
import { migrateCreateClientConnectionEvents } from "./migrations/382-create-client-connection-events.js";
|
|
496
496
|
import { migrateNormalizeOpencodeHostConnections } from "./migrations/383-normalize-opencode-host-connections.js";
|
|
497
|
+
import { migrateAddDocumentRevision } from "./migrations/384-add-document-revision.js";
|
|
498
|
+
import { migrateCreateDocumentRevisions } from "./migrations/385-create-document-revisions.js";
|
|
499
|
+
import { migrateConversationsLastReopenedAt } from "./migrations/386-conversations-last-reopened-at.js";
|
|
497
500
|
import type { MigrationStep } from "./migrations/run-migrations.js";
|
|
498
501
|
|
|
499
502
|
export const migrationSteps: MigrationStep[] = [
|
|
@@ -1661,4 +1664,7 @@ export const migrationSteps: MigrationStep[] = [
|
|
|
1661
1664
|
"migrateProviderConnectionBaseUrlAndModels",
|
|
1662
1665
|
],
|
|
1663
1666
|
},
|
|
1667
|
+
migrateAddDocumentRevision,
|
|
1668
|
+
migrateCreateDocumentRevisions,
|
|
1669
|
+
migrateConversationsLastReopenedAt,
|
|
1664
1670
|
];
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* imports — the vellum STT/TTS providers build on top of it.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
|
|
20
22
|
import { VellumPlatformClient } from "./client.js";
|
|
21
23
|
|
|
22
24
|
export type ManagedSpeechResult<T> =
|
|
@@ -43,6 +45,11 @@ export interface ManagedSpeechTranscription {
|
|
|
43
45
|
durationSeconds: number;
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
const ManagedSpeechTranscriptionSchema = z.object({
|
|
49
|
+
text: z.string(),
|
|
50
|
+
durationSeconds: z.number(),
|
|
51
|
+
});
|
|
52
|
+
|
|
46
53
|
export interface ManagedSpeechSynthesis {
|
|
47
54
|
audio: Buffer;
|
|
48
55
|
contentType: string;
|
|
@@ -125,13 +132,10 @@ export async function managedSpeechTranscribe(input: {
|
|
|
125
132
|
return await platformError(response, "transcription");
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
typeof (body as { text?: unknown }).text !== "string" ||
|
|
133
|
-
typeof (body as { durationSeconds?: unknown }).durationSeconds !== "number"
|
|
134
|
-
) {
|
|
135
|
+
const parsed = ManagedSpeechTranscriptionSchema.safeParse(
|
|
136
|
+
await response.json().catch(() => null),
|
|
137
|
+
);
|
|
138
|
+
if (!parsed.success) {
|
|
135
139
|
return {
|
|
136
140
|
ok: false,
|
|
137
141
|
kind: "platform-error",
|
|
@@ -139,10 +143,12 @@ export async function managedSpeechTranscribe(input: {
|
|
|
139
143
|
message: "Managed speech transcription returned a malformed response.",
|
|
140
144
|
};
|
|
141
145
|
}
|
|
142
|
-
const parsed = body as { text: string; durationSeconds: number };
|
|
143
146
|
return {
|
|
144
147
|
ok: true,
|
|
145
|
-
value: {
|
|
148
|
+
value: {
|
|
149
|
+
text: parsed.data.text,
|
|
150
|
+
durationSeconds: parsed.data.durationSeconds,
|
|
151
|
+
},
|
|
146
152
|
};
|
|
147
153
|
}
|
|
148
154
|
|
|
@@ -211,25 +217,21 @@ export interface ManagedSpeechVoiceCatalog {
|
|
|
211
217
|
defaultModel: string | null;
|
|
212
218
|
}
|
|
213
219
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
sampleUrl: voice.sampleUrl,
|
|
230
|
-
source: voice.source,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
220
|
+
const ManagedSpeechVoiceSchema = z.object({
|
|
221
|
+
model: z.string(),
|
|
222
|
+
label: z.string(),
|
|
223
|
+
description: z.string(),
|
|
224
|
+
sampleUrl: z.string(),
|
|
225
|
+
source: z.string(),
|
|
226
|
+
});
|
|
227
|
+
const ManagedSpeechVoiceCatalogSchema = z.object({
|
|
228
|
+
voices: z.array(ManagedSpeechVoiceSchema.nullable().catch(null)),
|
|
229
|
+
defaultModel: z.string().nullable().optional().catch(null),
|
|
230
|
+
});
|
|
231
|
+
const ManagedSpeechErrorSchema = z.object({
|
|
232
|
+
code: z.string().optional().catch(undefined),
|
|
233
|
+
detail: z.string().optional().catch(undefined),
|
|
234
|
+
});
|
|
233
235
|
|
|
234
236
|
export async function managedSpeechVoices(input?: {
|
|
235
237
|
signal?: AbortSignal;
|
|
@@ -249,12 +251,10 @@ export async function managedSpeechVoices(input?: {
|
|
|
249
251
|
return await platformError(response, "voice listing");
|
|
250
252
|
}
|
|
251
253
|
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
: null;
|
|
257
|
-
if (!voices) {
|
|
254
|
+
const parsed = ManagedSpeechVoiceCatalogSchema.safeParse(
|
|
255
|
+
await response.json().catch(() => null),
|
|
256
|
+
);
|
|
257
|
+
if (!parsed.success) {
|
|
258
258
|
return {
|
|
259
259
|
ok: false,
|
|
260
260
|
kind: "platform-error",
|
|
@@ -265,9 +265,10 @@ export async function managedSpeechVoices(input?: {
|
|
|
265
265
|
return {
|
|
266
266
|
ok: true,
|
|
267
267
|
value: {
|
|
268
|
-
voices
|
|
269
|
-
|
|
270
|
-
|
|
268
|
+
voices: parsed.data.voices.filter(
|
|
269
|
+
(voice): voice is ManagedSpeechVoice => voice !== null,
|
|
270
|
+
),
|
|
271
|
+
defaultModel: parsed.data.defaultModel ?? null,
|
|
271
272
|
},
|
|
272
273
|
};
|
|
273
274
|
}
|
|
@@ -279,15 +280,10 @@ async function platformError(
|
|
|
279
280
|
let code: string | undefined;
|
|
280
281
|
let detail: string | undefined;
|
|
281
282
|
try {
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (typeof body?.code === "string") {
|
|
287
|
-
code = body.code;
|
|
288
|
-
}
|
|
289
|
-
if (typeof body?.detail === "string") {
|
|
290
|
-
detail = body.detail;
|
|
283
|
+
const parsed = ManagedSpeechErrorSchema.safeParse(await response.json());
|
|
284
|
+
if (parsed.success) {
|
|
285
|
+
code = parsed.data.code;
|
|
286
|
+
detail = parsed.data.detail;
|
|
291
287
|
}
|
|
292
288
|
} catch {
|
|
293
289
|
// Non-JSON error body; status alone will have to do.
|
|
@@ -91,7 +91,7 @@ describe("doesSupportVision", () => {
|
|
|
91
91
|
setMockConfig({
|
|
92
92
|
"text-profile": {
|
|
93
93
|
provider: "fireworks",
|
|
94
|
-
model: "accounts/fireworks/models/glm-
|
|
94
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
95
95
|
},
|
|
96
96
|
});
|
|
97
97
|
expect(doesSupportVision(profile("text-profile"))).toBe(false);
|
|
@@ -162,7 +162,7 @@ describe("doesSupportVision", () => {
|
|
|
162
162
|
managed: { provider: "vellum", model: "claude-fable-5" },
|
|
163
163
|
"managed-text": {
|
|
164
164
|
provider: "vellum",
|
|
165
|
-
model: "accounts/fireworks/models/
|
|
165
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
166
166
|
},
|
|
167
167
|
});
|
|
168
168
|
expect(doesSupportVision(profile("managed"))).toBe(true);
|
|
@@ -186,7 +186,7 @@ describe("doesSupportVision", () => {
|
|
|
186
186
|
},
|
|
187
187
|
"text-arm": {
|
|
188
188
|
provider: "fireworks",
|
|
189
|
-
model: "accounts/fireworks/models/glm-
|
|
189
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
190
190
|
},
|
|
191
191
|
"vision-arm": { provider: "anthropic", model: "claude-opus-4-6" },
|
|
192
192
|
});
|
|
@@ -203,7 +203,7 @@ describe("doesSupportVision", () => {
|
|
|
203
203
|
},
|
|
204
204
|
"text-arm": {
|
|
205
205
|
provider: "fireworks",
|
|
206
|
-
model: "accounts/fireworks/models/glm-
|
|
206
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
207
207
|
},
|
|
208
208
|
"gateway-arm": {
|
|
209
209
|
provider: "openai-compatible",
|
|
@@ -224,11 +224,11 @@ describe("doesSupportVision", () => {
|
|
|
224
224
|
},
|
|
225
225
|
"text-arm-1": {
|
|
226
226
|
provider: "fireworks",
|
|
227
|
-
model: "accounts/fireworks/models/glm-
|
|
227
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
228
228
|
},
|
|
229
229
|
"text-arm-2": {
|
|
230
230
|
provider: "fireworks",
|
|
231
|
-
model: "accounts/fireworks/models/glm-
|
|
231
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
232
232
|
},
|
|
233
233
|
});
|
|
234
234
|
expect(doesSupportVision(profile("mix-profile"))).toBe(false);
|
|
@@ -237,19 +237,19 @@ describe("doesSupportVision", () => {
|
|
|
237
237
|
|
|
238
238
|
describe("doesSupportVision with a BYO default provider", () => {
|
|
239
239
|
test("judges a default profile against the default provider's column model", () => {
|
|
240
|
-
// Managed/vellum column: cost-optimized → deepseek-
|
|
241
|
-
// supportsVision:
|
|
242
|
-
//
|
|
240
|
+
// Managed/vellum column: cost-optimized → deepseek-v4p1-flash
|
|
241
|
+
// (supportsVision: true). OpenRouter column: cost-optimized →
|
|
242
|
+
// deepseek/deepseek-v4-flash (text-only, supportsVision: false).
|
|
243
243
|
// The judged model must be the one the BYO install actually runs.
|
|
244
|
-
setMockConfig({}, { provider: "
|
|
245
|
-
expect(doesSupportVision(profile("cost-optimized"))).toBe(
|
|
244
|
+
setMockConfig({}, { provider: "openrouter" });
|
|
245
|
+
expect(doesSupportVision(profile("cost-optimized"))).toBe(false);
|
|
246
246
|
});
|
|
247
247
|
|
|
248
248
|
test("without a default provider, a default profile judges the managed column", () => {
|
|
249
249
|
// Null-reduction: no defaultProvider resolves cost-optimized through the
|
|
250
|
-
// vellum column (deepseek-
|
|
250
|
+
// vellum column (deepseek-v4p1-flash, vision-capable).
|
|
251
251
|
setMockConfig({});
|
|
252
|
-
expect(doesSupportVision(profile("cost-optimized"))).toBe(
|
|
252
|
+
expect(doesSupportVision(profile("cost-optimized"))).toBe(true);
|
|
253
253
|
});
|
|
254
254
|
|
|
255
255
|
test("mix arms naming a default profile resolve through the same column", () => {
|
|
@@ -263,14 +263,14 @@ describe("doesSupportVision with a BYO default provider", () => {
|
|
|
263
263
|
},
|
|
264
264
|
"text-arm": {
|
|
265
265
|
provider: "fireworks",
|
|
266
|
-
model: "accounts/fireworks/models/glm-
|
|
266
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
267
267
|
},
|
|
268
268
|
},
|
|
269
|
-
{ provider: "
|
|
269
|
+
{ provider: "openrouter" },
|
|
270
270
|
);
|
|
271
|
-
// The "cost-optimized" arm resolves to the
|
|
272
|
-
//
|
|
273
|
-
expect(doesSupportVision(profile("mix-profile"))).toBe(
|
|
271
|
+
// The "cost-optimized" arm resolves to the openrouter column's
|
|
272
|
+
// text-only model, so no arm of the mix can route to vision.
|
|
273
|
+
expect(doesSupportVision(profile("mix-profile"))).toBe(false);
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
test("a user-owned profile is unaffected by the default provider", () => {
|
|
@@ -278,7 +278,7 @@ describe("doesSupportVision with a BYO default provider", () => {
|
|
|
278
278
|
{
|
|
279
279
|
"custom-text": {
|
|
280
280
|
provider: "fireworks",
|
|
281
|
-
model: "accounts/fireworks/models/glm-
|
|
281
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
282
282
|
},
|
|
283
283
|
},
|
|
284
284
|
{ provider: "anthropic" },
|
|
@@ -369,7 +369,7 @@ describe("doesSupportVision with a bare string", () => {
|
|
|
369
369
|
});
|
|
370
370
|
|
|
371
371
|
test("returns false for a known text-only model id", () => {
|
|
372
|
-
expect(doesSupportVision("accounts/fireworks/models/glm-
|
|
372
|
+
expect(doesSupportVision("accounts/fireworks/models/glm-5p3")).toBe(false);
|
|
373
373
|
});
|
|
374
374
|
|
|
375
375
|
test("falls back to resolving the string as a profile key", () => {
|
|
@@ -403,7 +403,7 @@ describe("doesSupportVision with a resolved target", () => {
|
|
|
403
403
|
expect(
|
|
404
404
|
doesSupportVision({
|
|
405
405
|
provider: "fireworks",
|
|
406
|
-
model: "accounts/fireworks/models/glm-
|
|
406
|
+
model: "accounts/fireworks/models/glm-5p3",
|
|
407
407
|
inputModalities: { image: { enabled: true, supported: true } },
|
|
408
408
|
}),
|
|
409
409
|
).toBe(true);
|
|
@@ -203,12 +203,17 @@ const postModelCall: HookFunction<PostModelCallContext> = async (ctx) => {
|
|
|
203
203
|
!priorAssistantHadVisibleText;
|
|
204
204
|
|
|
205
205
|
if (isEmptyTurnAfterTools) {
|
|
206
|
-
// Only the user-facing reply
|
|
207
|
-
// and compaction calls have no
|
|
208
|
-
// post-model-call contract requires self-gating on call site to avoid
|
|
209
|
-
// re-querying them.
|
|
210
|
-
//
|
|
211
|
-
|
|
206
|
+
// Only the user-facing reply and subagent runs get the re-query nudge.
|
|
207
|
+
// Background and compaction calls have no consumer awaiting a summary, and
|
|
208
|
+
// the post-model-call contract requires self-gating on call site to avoid
|
|
209
|
+
// re-querying them. Subagents DO need the nudge: without it, an empty
|
|
210
|
+
// model response after a tool result makes the agent loop exit via
|
|
211
|
+
// "no_tool_calls" and the runner reports the subagent as `completed` with
|
|
212
|
+
// empty output — a silent failure the parent can't distinguish from a
|
|
213
|
+
// real result (see vellum-ai/vellum-assistant#43327). The refusal-rewrite
|
|
214
|
+
// above is a user-facing terminal fallback, not a re-query, so it stays
|
|
215
|
+
// ungated.
|
|
216
|
+
if (ctx.callSite !== "mainAgent" && ctx.callSite !== "subagentSpawn") {
|
|
212
217
|
return;
|
|
213
218
|
}
|
|
214
219
|
|
|
@@ -374,7 +374,7 @@ describe("routing identities", () => {
|
|
|
374
374
|
expectedProvider: "anthropic",
|
|
375
375
|
});
|
|
376
376
|
expect(
|
|
377
|
-
resolveRoutingIdentity("vellum", "accounts/fireworks/models/glm-
|
|
377
|
+
resolveRoutingIdentity("vellum", "accounts/fireworks/models/glm-5p3"),
|
|
378
378
|
).toEqual({ connectionName: "vellum", expectedProvider: "fireworks" });
|
|
379
379
|
expect(resolveRoutingIdentity("vellum", "qwen/qwen3-8b")).toEqual({
|
|
380
380
|
connectionName: "vellum",
|
|
@@ -112,4 +112,12 @@ describe("search-provider-catalog", () => {
|
|
|
112
112
|
expect(exa?.supportsApiBase).toBeUndefined();
|
|
113
113
|
expect(exa?.envVar).toBe("EXA_API_KEY");
|
|
114
114
|
});
|
|
115
|
+
|
|
116
|
+
test("Serply is a keyed BYOK provider without a custom API base", () => {
|
|
117
|
+
const serply = getSearchProvider("serply");
|
|
118
|
+
expect(serply?.kind).toBe("byok");
|
|
119
|
+
expect(serply?.keyless).toBeUndefined();
|
|
120
|
+
expect(serply?.supportsApiBase).toBeUndefined();
|
|
121
|
+
expect(serply?.envVar).toBe("SERPLY_API_KEY");
|
|
122
|
+
});
|
|
115
123
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
boundUnparseableToolArgs,
|
|
5
|
+
boundUnparseableToolArgsRaw,
|
|
4
6
|
isUnparseableToolArgs,
|
|
5
7
|
unparseableToolArgsMessage,
|
|
6
8
|
wrapUnparseableToolArgs,
|
|
@@ -31,6 +33,29 @@ describe("unparseable tool args marker", () => {
|
|
|
31
33
|
expect(isUnparseableToolArgs({})).toBe(false);
|
|
32
34
|
});
|
|
33
35
|
|
|
36
|
+
test("bounds long raw args while preserving the marker shape", () => {
|
|
37
|
+
const raw = "a".repeat(1_000);
|
|
38
|
+
const bounded = boundUnparseableToolArgs(wrapUnparseableToolArgs(raw));
|
|
39
|
+
|
|
40
|
+
expect(isUnparseableToolArgs(bounded)).toBe(true);
|
|
41
|
+
expect(bounded).toEqual({ _raw: "a".repeat(200) + "…" });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("bounds raw diagnostic copies with the same preview contract", () => {
|
|
45
|
+
expect(boundUnparseableToolArgsRaw("a".repeat(1_000))).toBe(
|
|
46
|
+
"a".repeat(200) + "…",
|
|
47
|
+
);
|
|
48
|
+
expect(boundUnparseableToolArgsRaw("short")).toBe("short");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("leaves short markers and ordinary input unchanged", () => {
|
|
52
|
+
const marker = wrapUnparseableToolArgs("short");
|
|
53
|
+
const ordinary = { command: "ls" };
|
|
54
|
+
|
|
55
|
+
expect(boundUnparseableToolArgs(marker)).toBe(marker);
|
|
56
|
+
expect(boundUnparseableToolArgs(ordinary)).toBe(ordinary);
|
|
57
|
+
});
|
|
58
|
+
|
|
34
59
|
test("message includes tool name, raw preview, and retry instruction", () => {
|
|
35
60
|
const msg = unparseableToolArgsMessage("ui_show", '{"surface_type": ');
|
|
36
61
|
expect(msg).toContain('"ui_show"');
|
|
@@ -1008,7 +1008,7 @@ export class AnthropicProvider implements Provider {
|
|
|
1008
1008
|
const isHaiku = effectiveModel.includes("haiku");
|
|
1009
1009
|
// Effort support is per-model: Haiku and Sonnet 4.5 reject the param (see isEffortSupported).
|
|
1010
1010
|
const supportsEffort = isEffortSupported(effectiveModel);
|
|
1011
|
-
// opus-4-7 / opus-4-8 / opus-5 / opus-5-5 and sonnet-5 reject `temperature`,
|
|
1011
|
+
// opus-4-7 / opus-4-8 / opus-5 / opus-5-5 and sonnet-5 / sonnet-5-5 reject `temperature`,
|
|
1012
1012
|
// `top_p`, and `top_k` with a 400 "`temperature`/`top_p` is deprecated
|
|
1013
1013
|
// for this model" — model-wide, not effort-conditional (verified
|
|
1014
1014
|
// 2026-06-23). opus-4-6 / sonnet-4-6 / haiku-4-5 still accept them.
|