@vellumai/assistant 0.12.3 → 0.12.4-staging.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/Dockerfile +7 -0
- package/README.md +12 -12
- package/docker/mousepad/90-vellum.gschema.override +4 -0
- package/openapi.yaml +9 -3
- package/package.json +1 -1
- package/src/__tests__/approval-routes-http.test.ts +4 -2
- package/src/__tests__/call-routes-http.test.ts +4 -2
- package/src/__tests__/config-loader-backfill.test.ts +1 -0
- package/src/__tests__/conversation-resurface-done.test.ts +289 -0
- package/src/__tests__/default-profile-catalog-fallback.test.ts +1 -0
- package/src/__tests__/gemini-provider.test.ts +31 -0
- package/src/__tests__/inference-profile-validation-jev.test.ts +17 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +43 -0
- package/src/__tests__/plugin-api-model-profiles.test.ts +7 -0
- package/src/__tests__/provider-error-scenarios.test.ts +46 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +45 -15
- package/src/__tests__/retry-fallback-escalation.test.ts +22 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +4 -2
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -2
- package/src/__tests__/steer-on-enqueue-question.test.ts +265 -34
- package/src/__tests__/workspace-migration-159-rename-colliding-jev-profile-name.test.ts +197 -0
- package/src/api/requests/dictation.ts +2 -2
- package/src/calls/__tests__/typesafe-noul.test.ts +66 -0
- package/src/calls/typesafe-noul.ts +8 -2
- package/src/calls/voice-escalation-judge.ts +5 -4
- package/src/cli/commands/channel-verification-sessions.help.ts +12 -2
- package/src/cli/lib/bundled-marketplace.json +4 -4
- package/src/config/__tests__/default-profile-catalog.test.ts +33 -1
- package/src/config/__tests__/jev-managed-profile.test.ts +82 -0
- package/src/config/call-site-defaults.ts +11 -11
- package/src/config/default-profile-catalog.ts +88 -18
- package/src/config/default-profile-names.ts +35 -0
- package/src/config/feature-flag-registry.json +8 -8
- package/src/config/inference-profile-validation.ts +7 -6
- package/src/config/llm-resolver.ts +16 -0
- package/src/config/profile-text-generation.test.ts +9 -0
- package/src/config/schemas/call-site-catalog.ts +1 -1
- package/src/config/schemas/llm.ts +54 -9
- package/src/config/sidebar-done-gate.ts +27 -0
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-turn-finalize.ts +12 -0
- package/src/daemon/dictation-text-processing.ts +4 -4
- package/src/daemon/handlers/__tests__/conversation-history-search.test.ts +61 -0
- package/src/daemon/handlers/config-channels.ts +3 -1
- package/src/daemon/handlers/conversation-history.ts +11 -1
- package/src/daemon/handlers/conversations.ts +118 -10
- package/src/desktop/desktop-browser-endpoint.ts +1 -0
- package/src/ipc/routes/__tests__/conversation-sync-ipc-routes.test.ts +49 -1
- package/src/ipc/routes/conversation-sync-ipc-routes.ts +38 -2
- package/src/permissions/question-prompter.ts +14 -4
- package/src/persistence/conversation-crud.ts +114 -3
- package/src/persistence/conversation-queries.ts +48 -3
- package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +157 -32
- package/src/persistence/embeddings/embedding-backend-shutdown.test.ts +47 -0
- package/src/persistence/embeddings/embedding-backend.ts +23 -41
- package/src/persistence/embeddings/embedding-gemini.test.ts +17 -0
- package/src/persistence/embeddings/embedding-gemini.ts +3 -1
- package/src/persistence/embeddings/embedding-local.ts +72 -17
- package/src/persistence/embeddings/embedding-types.ts +0 -19
- package/src/plugin-api/model-profiles.ts +5 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-skill-card.test.ts +2 -0
- package/src/plugins/defaults/memory/memory-retrospective-skill-card.ts +5 -0
- package/src/providers/gemini/client.ts +17 -1
- package/src/providers/malformed-tool-call.ts +55 -0
- package/src/providers/model-catalog.ts +23 -4
- package/src/providers/openai/__tests__/chat-completions-malformed-tool-call.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +30 -0
- package/src/providers/platform-proxy/constants.ts +5 -0
- package/src/providers/retry.ts +68 -36
- package/src/providers/vellum-model-routing.test.ts +1 -0
- package/src/runtime/__tests__/agent-wake-archived-gate.test.ts +90 -0
- package/src/runtime/__tests__/question-resolution.test.ts +32 -0
- package/src/runtime/agent-wake.ts +9 -2
- package/src/runtime/auth/__tests__/middleware.test.ts +138 -0
- package/src/runtime/auth/middleware.ts +15 -11
- package/src/runtime/middleware/auth.ts +7 -1
- package/src/runtime/pending-interactions.ts +8 -8
- package/src/runtime/question-resolution.ts +54 -8
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +146 -0
- package/src/runtime/routes/__tests__/dictation-processing.test.ts +174 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +74 -1
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +16 -1
- package/src/runtime/routes/conversation-list-routes.ts +16 -14
- package/src/runtime/routes/conversation-query-routes.ts +4 -0
- package/src/runtime/routes/conversation-routes.ts +4 -2
- package/src/runtime/routes/diagnostics-routes.ts +66 -229
- package/src/runtime/routes/global-search-routes.ts +22 -7
- package/src/runtime/routes/llm-call-sites-routes.ts +19 -10
- package/src/runtime/sync/documents-sidecar-publish.test.ts +50 -4
- package/src/runtime/sync/resource-sync-events.ts +11 -0
- package/src/runtime/sync/worker-daemon-notify.test.ts +30 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -0
- package/src/runtime/verification-outbound-actions.ts +2 -2
- package/src/tools/ask-question/ask-question-tool.test.ts +38 -0
- package/src/tools/ask-question/ask-question-tool.ts +12 -1
- package/src/tools/workflows/manage-workflows.ts +4 -2
- package/src/util/worker-pipe.test.ts +100 -0
- package/src/util/worker-pipe.ts +44 -0
- package/src/workspace/migrations/159-rename-colliding-jev-profile-name.ts +344 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { Database } from "bun:sqlite";
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
|
|
14
|
+
import { assertNotLiveDb } from "./assert-not-live-db.js";
|
|
15
|
+
|
|
16
|
+
const migration = WORKSPACE_MIGRATIONS.find(
|
|
17
|
+
(entry) => entry.id === "159-rename-colliding-jev-profile-name",
|
|
18
|
+
)!;
|
|
19
|
+
|
|
20
|
+
let workspaceDir: string;
|
|
21
|
+
|
|
22
|
+
function writeConfig(data: Record<string, unknown>): void {
|
|
23
|
+
writeFileSync(
|
|
24
|
+
join(workspaceDir, "config.json"),
|
|
25
|
+
JSON.stringify(data, null, 2) + "\n",
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readConfig(): Record<string, any> {
|
|
30
|
+
return JSON.parse(readFileSync(join(workspaceDir, "config.json"), "utf-8"));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function seedProfilePinTables(rows: {
|
|
34
|
+
conversations: string[];
|
|
35
|
+
cronJobs: string[];
|
|
36
|
+
}): void {
|
|
37
|
+
mkdirSync(join(workspaceDir, "data", "db"), { recursive: true });
|
|
38
|
+
const db = new Database(join(workspaceDir, "data", "db", "assistant.db"));
|
|
39
|
+
db.run(
|
|
40
|
+
`CREATE TABLE conversations (id TEXT PRIMARY KEY, inference_profile TEXT)`,
|
|
41
|
+
);
|
|
42
|
+
db.run(
|
|
43
|
+
`CREATE TABLE cron_jobs (id TEXT PRIMARY KEY, inference_profile TEXT)`,
|
|
44
|
+
);
|
|
45
|
+
rows.conversations.forEach((profile, index) => {
|
|
46
|
+
db.query(
|
|
47
|
+
`INSERT INTO conversations (id, inference_profile) VALUES (?, ?)`,
|
|
48
|
+
).run(`conv-${index}`, profile);
|
|
49
|
+
});
|
|
50
|
+
rows.cronJobs.forEach((profile, index) => {
|
|
51
|
+
db.query(`INSERT INTO cron_jobs (id, inference_profile) VALUES (?, ?)`).run(
|
|
52
|
+
`job-${index}`,
|
|
53
|
+
profile,
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
db.close();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readPins(table: string): string[] {
|
|
60
|
+
const db = new Database(join(workspaceDir, "data", "db", "assistant.db"));
|
|
61
|
+
try {
|
|
62
|
+
return (
|
|
63
|
+
db
|
|
64
|
+
.query(`SELECT inference_profile AS p FROM ${table} ORDER BY id`)
|
|
65
|
+
.all() as Array<{ p: string }>
|
|
66
|
+
).map((row) => row.p);
|
|
67
|
+
} finally {
|
|
68
|
+
db.close();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
workspaceDir = join(
|
|
74
|
+
tmpdir(),
|
|
75
|
+
`vellum-migration-159-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
76
|
+
);
|
|
77
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
if (existsSync(workspaceDir)) {
|
|
82
|
+
assertNotLiveDb(workspaceDir);
|
|
83
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("159-rename-colliding-jev-profile-name migration", () => {
|
|
88
|
+
test("is registered under its id with checkpoint retry", () => {
|
|
89
|
+
expect(migration).toBeDefined();
|
|
90
|
+
expect(migration.retryFailedCheckpoint).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("renames a colliding user profile and rewrites every reference", async () => {
|
|
94
|
+
writeConfig({
|
|
95
|
+
llm: {
|
|
96
|
+
defaultProvider: { provider: "vellum" },
|
|
97
|
+
profiles: {
|
|
98
|
+
"jev-managed": {
|
|
99
|
+
source: "user",
|
|
100
|
+
provider: "anthropic",
|
|
101
|
+
model: "claude-opus-4-7",
|
|
102
|
+
label: "Mine",
|
|
103
|
+
},
|
|
104
|
+
scratch: { source: "user", fallbackProfile: "jev-managed" },
|
|
105
|
+
armA: { source: "user", speed: "fast" },
|
|
106
|
+
blend: {
|
|
107
|
+
source: "user",
|
|
108
|
+
mix: [
|
|
109
|
+
{ profile: "jev-managed", weight: 1 },
|
|
110
|
+
{ profile: "armA", weight: 1 },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
profileOrder: ["balanced", "jev-managed", "blend"],
|
|
115
|
+
activeProfile: "jev-managed",
|
|
116
|
+
advisorProfile: "jev-managed",
|
|
117
|
+
callSites: { recall: { profile: "jev-managed", maxTokens: 4096 } },
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
seedProfilePinTables({
|
|
121
|
+
conversations: ["jev-managed", "balanced"],
|
|
122
|
+
cronJobs: ["jev-managed"],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await migration.run(workspaceDir);
|
|
126
|
+
|
|
127
|
+
const llm = readConfig().llm;
|
|
128
|
+
expect(llm.profiles["jev-managed"]).toBeUndefined();
|
|
129
|
+
expect(llm.profiles["jev-managed-custom"]).toEqual({
|
|
130
|
+
source: "user",
|
|
131
|
+
provider: "anthropic",
|
|
132
|
+
model: "claude-opus-4-7",
|
|
133
|
+
label: "Mine",
|
|
134
|
+
});
|
|
135
|
+
expect(Object.keys(llm.profiles)).toEqual([
|
|
136
|
+
"jev-managed-custom",
|
|
137
|
+
"scratch",
|
|
138
|
+
"armA",
|
|
139
|
+
"blend",
|
|
140
|
+
]);
|
|
141
|
+
expect(llm.activeProfile).toBe("jev-managed-custom");
|
|
142
|
+
expect(llm.advisorProfile).toBe("jev-managed-custom");
|
|
143
|
+
expect(llm.profileOrder).toEqual([
|
|
144
|
+
"balanced",
|
|
145
|
+
"jev-managed-custom",
|
|
146
|
+
"blend",
|
|
147
|
+
]);
|
|
148
|
+
expect(llm.callSites.recall.profile).toBe("jev-managed-custom");
|
|
149
|
+
expect(llm.profiles.scratch.fallbackProfile).toBe("jev-managed-custom");
|
|
150
|
+
expect(llm.profiles.blend.mix[0].profile).toBe("jev-managed-custom");
|
|
151
|
+
expect(readPins("conversations")).toEqual([
|
|
152
|
+
"jev-managed-custom",
|
|
153
|
+
"balanced",
|
|
154
|
+
]);
|
|
155
|
+
expect(readPins("cron_jobs")).toEqual(["jev-managed-custom"]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("keeps suffixing past taken and reserved names", async () => {
|
|
159
|
+
writeConfig({
|
|
160
|
+
llm: {
|
|
161
|
+
profiles: {
|
|
162
|
+
"jev-managed": {
|
|
163
|
+
source: "user",
|
|
164
|
+
provider: "openai",
|
|
165
|
+
model: "gpt-5.5",
|
|
166
|
+
},
|
|
167
|
+
"jev-managed-custom": { source: "user", provider: "openai" },
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await migration.run(workspaceDir);
|
|
173
|
+
|
|
174
|
+
const keys = Object.keys(readConfig().llm.profiles);
|
|
175
|
+
expect(keys).toEqual(["jev-managed-custom-2", "jev-managed-custom"]);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("leaves a managed stub alone and is idempotent", async () => {
|
|
179
|
+
const original = {
|
|
180
|
+
llm: {
|
|
181
|
+
profiles: {
|
|
182
|
+
"jev-managed": { source: "managed", status: "disabled" },
|
|
183
|
+
balanced: { source: "managed" },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
writeConfig(original);
|
|
188
|
+
|
|
189
|
+
await migration.run(workspaceDir);
|
|
190
|
+
await migration.run(workspaceDir);
|
|
191
|
+
|
|
192
|
+
expect(readConfig()).toEqual(original);
|
|
193
|
+
expect(existsSync(join(workspaceDir, "data", "db", "assistant.db"))).toBe(
|
|
194
|
+
false,
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wire contract for the dictation REST endpoint (`POST /dictation`).
|
|
3
3
|
*
|
|
4
|
-
* The body web/macOS sends
|
|
5
|
-
*
|
|
4
|
+
* The body web/macOS sends to apply explicit dictation replacements, classify
|
|
5
|
+
* voice input, or edit selected text. `context` is its own
|
|
6
6
|
* named schema/type so consumers (web client) can import `DictationContext`
|
|
7
7
|
* directly instead of inferring it from the generated client.
|
|
8
8
|
*
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { afterAll, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { Provider } from "../../providers/types.js";
|
|
4
|
+
|
|
5
|
+
let configured: {
|
|
6
|
+
provider: Provider;
|
|
7
|
+
configuredProviderName: string;
|
|
8
|
+
entryRouted: boolean;
|
|
9
|
+
} | null = null;
|
|
10
|
+
|
|
11
|
+
const actual = await import("../../providers/provider-send-message.js");
|
|
12
|
+
mock.module("../../providers/provider-send-message.js", () => ({
|
|
13
|
+
...actual,
|
|
14
|
+
resolveConfiguredProvider: async () => configured,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const { resolveTypesafeProvider } = await import("../typesafe-noul.js");
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
mock.restore();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function stub(name: string): Provider {
|
|
24
|
+
return {
|
|
25
|
+
name,
|
|
26
|
+
defaultModel: "jev-latest",
|
|
27
|
+
sendMessage: async () => {
|
|
28
|
+
throw new Error("not dispatched in this test");
|
|
29
|
+
},
|
|
30
|
+
} as unknown as Provider;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("resolveTypesafeProvider", () => {
|
|
34
|
+
test("a BYOK TypeSafe profile resolves", async () => {
|
|
35
|
+
configured = {
|
|
36
|
+
provider: stub("typesafe"),
|
|
37
|
+
configuredProviderName: "typesafe",
|
|
38
|
+
entryRouted: false,
|
|
39
|
+
};
|
|
40
|
+
expect((await resolveTypesafeProvider("voiceEscalationJudge"))?.name).toBe(
|
|
41
|
+
"typesafe",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("a managed Jev profile resolves although it is configured as vellum", async () => {
|
|
46
|
+
configured = {
|
|
47
|
+
provider: stub("typesafe"),
|
|
48
|
+
configuredProviderName: "vellum",
|
|
49
|
+
entryRouted: false,
|
|
50
|
+
};
|
|
51
|
+
expect((await resolveTypesafeProvider("voiceEscalationJudge"))?.name).toBe(
|
|
52
|
+
"typesafe",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("any other dispatched provider is unavailable", async () => {
|
|
57
|
+
configured = {
|
|
58
|
+
provider: stub("anthropic"),
|
|
59
|
+
configuredProviderName: "vellum",
|
|
60
|
+
entryRouted: false,
|
|
61
|
+
};
|
|
62
|
+
expect(await resolveTypesafeProvider("voiceEscalationJudge")).toBeNull();
|
|
63
|
+
configured = null;
|
|
64
|
+
expect(await resolveTypesafeProvider("voiceEscalationJudge")).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -28,12 +28,18 @@ export interface TypesafeNoulResult {
|
|
|
28
28
|
latencyMs: number;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* The call site's provider when it resolves to TypeSafe, else null. Judged
|
|
33
|
+
* on the dispatched adapter's name rather than the configured provider: a
|
|
34
|
+
* managed profile is configured as `vellum` and only substitutes the
|
|
35
|
+
* TypeSafe upstream at dispatch, so the configured name alone would report
|
|
36
|
+
* the managed Jev profile as unavailable.
|
|
37
|
+
*/
|
|
32
38
|
export async function resolveTypesafeProvider(
|
|
33
39
|
callSite: LLMCallSite,
|
|
34
40
|
): Promise<Provider | null> {
|
|
35
41
|
const resolved = await resolveConfiguredProvider(callSite);
|
|
36
|
-
return resolved?.
|
|
42
|
+
return resolved?.provider.name === TYPESAFE_PROVIDER_NAME
|
|
37
43
|
? resolved.provider
|
|
38
44
|
: null;
|
|
39
45
|
}
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
* correct reply need something the front door cannot do from here? A
|
|
10
10
|
* confident yes overrules a front-door answer and hands the turn off.
|
|
11
11
|
*
|
|
12
|
-
* The judge
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The judge uses managed Jev by default when Vellum-managed inference is
|
|
13
|
+
* available. An explicit TypeSafe call-site override can use a user-owned
|
|
14
|
+
* connection. Every other outcome (unavailable route, provider error,
|
|
15
|
+
* timeout, unparseable answer) is a "clear" verdict, so the front door's own
|
|
16
|
+
* decision stands exactly as it would without the judge.
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import type { Message, Provider } from "../providers/types.js";
|
|
@@ -44,7 +44,11 @@ Examples:
|
|
|
44
44
|
description:
|
|
45
45
|
"Destination address for outbound verification (handle, phone number, user ID, or email address)",
|
|
46
46
|
},
|
|
47
|
-
{
|
|
47
|
+
{
|
|
48
|
+
flags: "--rebind",
|
|
49
|
+
description:
|
|
50
|
+
"Create a code although the channel already has a guardian (see below)",
|
|
51
|
+
},
|
|
48
52
|
{
|
|
49
53
|
flags: "--conversation-id <conversationId>",
|
|
50
54
|
description: "Conversation ID for inbound challenges",
|
|
@@ -74,7 +78,13 @@ Routes between three creation modes based on the provided options:
|
|
|
74
78
|
2. Outbound: --channel <ch> --destination <dest>
|
|
75
79
|
Sends a verification code to the given destination. Supports telegram
|
|
76
80
|
(handle or chat ID), phone (E.164 number), slack and discord (user ID),
|
|
77
|
-
and email.
|
|
81
|
+
and email.
|
|
82
|
+
|
|
83
|
+
--rebind creates a code although the channel already has a guardian. On
|
|
84
|
+
telegram, slack, discord and email, only the account already bound can redeem
|
|
85
|
+
that code, which is how the same guardian verifies again. A code never moves
|
|
86
|
+
the guardian to a different account there: run "revoke" first, then create
|
|
87
|
+
without --rebind. On phone, an outbound code replaces the bound number.
|
|
78
88
|
|
|
79
89
|
3. Inbound: --channel <ch> (no --destination)
|
|
80
90
|
Generates a challenge secret for the guardian to send back on the
|
|
@@ -176,16 +176,16 @@
|
|
|
176
176
|
"icon": "\ud83d\udcbb"
|
|
177
177
|
},
|
|
178
178
|
{
|
|
179
|
-
"name": "ecommerce-
|
|
179
|
+
"name": "ecommerce-order-supporter",
|
|
180
180
|
"description": "Resolves ecommerce Where-Is-My-Order (WISMO) support inquiries, cross-checks real-time carrier tracking milestones, flags delivery exceptions, and stages customer update drafts without helpdesk seat taxes.",
|
|
181
181
|
"category": "commerce",
|
|
182
182
|
"icon": "📦",
|
|
183
183
|
"license": "MIT",
|
|
184
|
-
"homepage": "https://github.com/ZeebBoyBlue/ecommerce-
|
|
184
|
+
"homepage": "https://github.com/ZeebBoyBlue/ecommerce-order-supporter",
|
|
185
185
|
"source": {
|
|
186
186
|
"source": "github",
|
|
187
|
-
"repo": "ZeebBoyBlue/ecommerce-
|
|
188
|
-
"ref": "
|
|
187
|
+
"repo": "ZeebBoyBlue/ecommerce-order-supporter",
|
|
188
|
+
"ref": "35e2165a14447a897aee750a198c3f166458226b"
|
|
189
189
|
}
|
|
190
190
|
},
|
|
191
191
|
{
|
|
@@ -5,6 +5,7 @@ import { resolveModelIntent } from "../../providers/model-intents.js";
|
|
|
5
5
|
import { CALL_SITE_DEFAULTS } from "../call-site-defaults.js";
|
|
6
6
|
import {
|
|
7
7
|
CODE_DEFAULT_PROFILE_ENTRIES,
|
|
8
|
+
getConversationProfilesForProvider,
|
|
8
9
|
getEffectiveProfile,
|
|
9
10
|
getEffectiveProfiles,
|
|
10
11
|
getEffectiveProfilesForProvider,
|
|
@@ -59,6 +60,37 @@ describe("getEffectiveProfiles", () => {
|
|
|
59
60
|
}
|
|
60
61
|
});
|
|
61
62
|
|
|
63
|
+
test("the managed Jev profile routes jev-latest through TypeSafe and is managed-only", () => {
|
|
64
|
+
const jev = CODE_DEFAULT_PROFILE_ENTRIES["jev-managed"];
|
|
65
|
+
expect(jev.model).toBe("jev-latest");
|
|
66
|
+
expect(jev.provider_connection).toBeUndefined();
|
|
67
|
+
expect(resolveRoutingIdentity(jev.provider, jev.model)).toEqual({
|
|
68
|
+
connectionName: "vellum",
|
|
69
|
+
expectedProvider: "typesafe",
|
|
70
|
+
});
|
|
71
|
+
expect(
|
|
72
|
+
resolveDefaultProfileForProvider(undefined, "jev-managed", null),
|
|
73
|
+
).toBeDefined();
|
|
74
|
+
expect(
|
|
75
|
+
resolveDefaultProfileForProvider(undefined, "jev-managed", {
|
|
76
|
+
provider: "anthropic",
|
|
77
|
+
connectionName: "anthropic-personal",
|
|
78
|
+
}),
|
|
79
|
+
).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("the conversation view drops the managed Jev profile but keeps it selectable for call sites", () => {
|
|
83
|
+
expect(
|
|
84
|
+
Object.keys(getConversationProfilesForProvider(undefined, null)),
|
|
85
|
+
).not.toContain("jev-managed");
|
|
86
|
+
expect(
|
|
87
|
+
Object.keys(getUserSelectableProfilesForProvider(undefined, null)),
|
|
88
|
+
).toContain("jev-managed");
|
|
89
|
+
expect(
|
|
90
|
+
Object.keys(getConversationProfilesForProvider(undefined, null)),
|
|
91
|
+
).toContain("balanced");
|
|
92
|
+
});
|
|
93
|
+
|
|
62
94
|
test("the managed Balanced profile routes GLM 5.3 Flash through Fireworks", () => {
|
|
63
95
|
const balanced = CODE_DEFAULT_PROFILE_ENTRIES.balanced;
|
|
64
96
|
expect(balanced.model).toBe("accounts/fireworks/models/glm-5p3-flash");
|
|
@@ -80,7 +112,7 @@ describe("getEffectiveProfiles", () => {
|
|
|
80
112
|
test("defaults absent from the workspace resolve from the catalog; os-beta stays flag-gated", () => {
|
|
81
113
|
const effective = getEffectiveProfiles(undefined);
|
|
82
114
|
expect(Object.keys(effective).sort()).toEqual(
|
|
83
|
-
[...DEFAULT_PROFILE_KEYS, ...BACKUP_PROFILE_KEYS].sort(),
|
|
115
|
+
[...DEFAULT_PROFILE_KEYS, ...BACKUP_PROFILE_KEYS, "jev-managed"].sort(),
|
|
84
116
|
);
|
|
85
117
|
expect(getEffectiveProfile({}, "balanced")?.model).toBe(
|
|
86
118
|
CODE_DEFAULT_PROFILE_ENTRIES.balanced.model as string,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { LLMSchema } from "../schemas/llm.js";
|
|
4
|
+
|
|
5
|
+
const PIN = { callSites: { voiceEscalationJudge: { profile: "jev-managed" } } };
|
|
6
|
+
|
|
7
|
+
describe("jev-managed as a call-site pin target", () => {
|
|
8
|
+
test("resolves under the managed default provider", () => {
|
|
9
|
+
expect(LLMSchema.safeParse(PIN).success).toBe(true);
|
|
10
|
+
expect(
|
|
11
|
+
LLMSchema.safeParse({
|
|
12
|
+
...PIN,
|
|
13
|
+
defaultProvider: { provider: "vellum" },
|
|
14
|
+
}).success,
|
|
15
|
+
).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("can be pinned only to the verdict-consuming call sites", () => {
|
|
19
|
+
for (const site of [
|
|
20
|
+
"voiceEscalationJudge",
|
|
21
|
+
"voiceContinuationJudge",
|
|
22
|
+
"memoryV3SelectL2",
|
|
23
|
+
]) {
|
|
24
|
+
expect(
|
|
25
|
+
LLMSchema.safeParse({
|
|
26
|
+
callSites: { [site]: { profile: "jev-managed" } },
|
|
27
|
+
}).success,
|
|
28
|
+
).toBe(true);
|
|
29
|
+
}
|
|
30
|
+
const result = LLMSchema.safeParse({
|
|
31
|
+
callSites: { conversationTitle: { profile: "jev-managed" } },
|
|
32
|
+
});
|
|
33
|
+
expect(result.success).toBe(false);
|
|
34
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
35
|
+
"can only be pinned to",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("cannot be a mix constituent", () => {
|
|
40
|
+
const result = LLMSchema.safeParse({
|
|
41
|
+
profiles: {
|
|
42
|
+
blend: {
|
|
43
|
+
mix: [
|
|
44
|
+
{ profile: "jev-managed", weight: 1 },
|
|
45
|
+
{ profile: "balanced", weight: 1 },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
expect(result.success).toBe(false);
|
|
51
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
52
|
+
"cannot be a mix constituent",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("is rejected as the active or advisor profile even on the managed column", () => {
|
|
57
|
+
for (const field of ["activeProfile", "advisorProfile"] as const) {
|
|
58
|
+
const result = LLMSchema.safeParse({ [field]: "jev-managed" });
|
|
59
|
+
expect(result.success).toBe(false);
|
|
60
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
61
|
+
"structured answers rather than chat text",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
expect(LLMSchema.safeParse({ activeProfile: "balanced" }).success).toBe(
|
|
65
|
+
true,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("is rejected under a BYOK default provider, where it has no body", () => {
|
|
70
|
+
const result = LLMSchema.safeParse({
|
|
71
|
+
...PIN,
|
|
72
|
+
defaultProvider: {
|
|
73
|
+
provider: "anthropic",
|
|
74
|
+
connectionName: "anthropic-personal",
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
expect(result.success).toBe(false);
|
|
78
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
79
|
+
"managed Jev profile",
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { JEV_MANAGED_PROFILE_KEY } from "./default-profile-names.js";
|
|
1
2
|
import { type LLMCallSite } from "./schemas/llm.js";
|
|
2
3
|
|
|
3
4
|
type CallSiteDefaultConfig = {
|
|
@@ -123,9 +124,8 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
123
124
|
effort: "low",
|
|
124
125
|
thinking: { enabled: false },
|
|
125
126
|
},
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// answers, so it is priced as a latency call.
|
|
127
|
+
// Voice intent classification and explicit selected-text edits are
|
|
128
|
+
// latency-sensitive: the caller is waiting for insertion or dispatch.
|
|
129
129
|
interactionClassifier: {
|
|
130
130
|
profile: "latency-optimized",
|
|
131
131
|
effort: "low",
|
|
@@ -153,14 +153,14 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
153
153
|
effort: "low",
|
|
154
154
|
thinking: { enabled: false },
|
|
155
155
|
},
|
|
156
|
-
// The
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
//
|
|
163
|
-
// pinned here.
|
|
156
|
+
// The managed-only profile resolves through the Vellum connection. On a
|
|
157
|
+
// BYOK install it has no body, so the judge is unavailable and the front
|
|
158
|
+
// door's own decision stands.
|
|
159
|
+
voiceEscalationJudge: {
|
|
160
|
+
profile: JEV_MANAGED_PROFILE_KEY,
|
|
161
|
+
},
|
|
162
|
+
// The barge-in continuation judge remains off until a TypeSafe profile is
|
|
163
|
+
// pinned here (see live-voice/continuation-judge.ts).
|
|
164
164
|
voiceContinuationJudge: { profile: "cost-optimized" },
|
|
165
165
|
// Names the background continuation a barge-in spawns, from the interrupted
|
|
166
166
|
// transcript. The label is fixed at spawn, so the call runs under a short
|