@vellumai/assistant 0.12.4-staging.2 → 0.12.4-staging.4
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/package.json +1 -1
- package/src/__tests__/openai-responses-prompt-cache.test.ts +3 -1
- package/src/__tests__/pricing.test.ts +33 -0
- package/src/__tests__/skill-load-tool.test.ts +42 -11
- package/src/__tests__/skill-tool-factory.test.ts +50 -2
- package/src/__tests__/validate-input.test.ts +73 -0
- package/src/cli/commands/__tests__/plugins.test.ts +29 -1
- package/src/cli/commands/plugins.ts +16 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +29 -0
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +32 -2
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +15 -0
- package/src/cli/lib/__tests__/plugin-catalog-resolve.test.ts +21 -0
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +65 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +43 -0
- package/src/cli/lib/__tests__/plugins-install-offline.test.ts +26 -0
- package/src/cli/lib/inspect-plugin.ts +5 -0
- package/src/cli/lib/plugin-catalog-cache.ts +24 -2
- package/src/cli/lib/plugin-catalog-local.ts +9 -0
- package/src/cli/lib/plugin-catalog-visibility.ts +41 -0
- package/src/cli/lib/plugin-details.ts +10 -7
- package/src/cli/lib/search-plugins.ts +2 -0
- package/src/config/feature-flag-registry.json +9 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +1 -1
- package/src/plugins/defaults/memory/v3/__tests__/render-injection.test.ts +3 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +2 -0
- package/src/providers/anthropic/client.ts +2 -2
- package/src/providers/model-catalog.ts +234 -0
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +8 -0
- package/src/runtime/routes/plugins-routes.ts +5 -2
- package/src/skills/validate-input.ts +75 -11
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/format-input-schema.ts +15 -0
- package/src/tools/skills/load.ts +2 -26
- package/src/tools/skills/skill-tool-factory.ts +2 -1
|
@@ -172,6 +172,17 @@ describe("plugins install by name — disable-platform mode", () => {
|
|
|
172
172
|
expect(message).toContain("not in the bundled marketplace catalog");
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
+
test("does not install a feature-gated bundled integration", async () => {
|
|
176
|
+
process.env.VELLUM_DISABLE_PLATFORM = "true";
|
|
177
|
+
delete process.env.IS_PLATFORM;
|
|
178
|
+
|
|
179
|
+
await runInstall("gamma");
|
|
180
|
+
|
|
181
|
+
expect(installPluginCalls).toHaveLength(0);
|
|
182
|
+
expect(platformInstallCalls).toHaveLength(0);
|
|
183
|
+
expect(process.exitCode).toBe(1);
|
|
184
|
+
});
|
|
185
|
+
|
|
175
186
|
test("uses the platform install endpoint when platform features are enabled", async () => {
|
|
176
187
|
delete process.env.VELLUM_DISABLE_PLATFORM;
|
|
177
188
|
delete process.env.IS_PLATFORM;
|
|
@@ -184,6 +195,21 @@ describe("plugins install by name — disable-platform mode", () => {
|
|
|
184
195
|
expect(process.exitCode).not.toBe(1);
|
|
185
196
|
});
|
|
186
197
|
|
|
198
|
+
test("does not install a name absent from a successful platform catalog", async () => {
|
|
199
|
+
delete process.env.VELLUM_DISABLE_PLATFORM;
|
|
200
|
+
delete process.env.IS_PLATFORM;
|
|
201
|
+
const errSpy = spyOn(console, "error").mockImplementation(() => {});
|
|
202
|
+
|
|
203
|
+
await runInstall("gamma");
|
|
204
|
+
|
|
205
|
+
expect(installPluginCalls).toHaveLength(0);
|
|
206
|
+
expect(platformInstallCalls).toHaveLength(0);
|
|
207
|
+
expect(process.exitCode).toBe(1);
|
|
208
|
+
expect(String(errSpy.mock.calls[0]?.[0])).toContain(
|
|
209
|
+
"not in the marketplace catalog",
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
|
|
187
213
|
test("installs a platform-visible local package without the platform tar endpoint", async () => {
|
|
188
214
|
delete process.env.VELLUM_DISABLE_PLATFORM;
|
|
189
215
|
delete process.env.IS_PLATFORM;
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
arePlatformFeaturesEnabled,
|
|
32
32
|
readBundledLocalPluginCatalog,
|
|
33
33
|
} from "./plugin-catalog-local.js";
|
|
34
|
+
import { isPluginCatalogEntryVisible } from "./plugin-catalog-visibility.js";
|
|
34
35
|
import { DEFAULT_PLUGIN_REF } from "./plugin-constants.js";
|
|
35
36
|
import {
|
|
36
37
|
compareFingerprint,
|
|
@@ -338,6 +339,10 @@ export async function inspectPlugin(
|
|
|
338
339
|
const local = entry ? readLocal(entry, readInstallMeta(entry.target)) : null;
|
|
339
340
|
const surfaces = entry ? detectPluginSurfaces(entry.target) : null;
|
|
340
341
|
|
|
342
|
+
if (!installed && !isPluginCatalogEntryVisible(name)) {
|
|
343
|
+
throw new PluginInspectNotFoundError(name);
|
|
344
|
+
}
|
|
345
|
+
|
|
341
346
|
let remote: PluginRemoteInfo | null = null;
|
|
342
347
|
let remoteError: string | null = null;
|
|
343
348
|
const installedLocalSource = local?.source?.kind === "local";
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
readBundledPluginCatalog,
|
|
15
15
|
} from "./plugin-catalog-local.js";
|
|
16
16
|
import { fetchPluginCatalogFromPlatform } from "./plugin-catalog-platform.js";
|
|
17
|
+
import { filterPluginCatalogByFeatureFlags } from "./plugin-catalog-visibility.js";
|
|
17
18
|
import type { PluginCatalog, SearchPluginsDeps } from "./search-plugins.js";
|
|
18
19
|
|
|
19
20
|
/** How long a fetched catalog is served before a refresh is attempted. */
|
|
@@ -24,6 +25,8 @@ interface CacheEntry {
|
|
|
24
25
|
timestamp: number;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
type PluginCatalogLoadDeps = Pick<SearchPluginsDeps, "fetch">;
|
|
29
|
+
|
|
27
30
|
const cache = new Map<string, CacheEntry>();
|
|
28
31
|
|
|
29
32
|
/** Add bundled local packages without overriding platform-authoritative rows. */
|
|
@@ -50,9 +53,9 @@ export function mergePlatformCatalogWithBundledLocals(
|
|
|
50
53
|
* fetch failure propagates so the caller can surface it (e.g. map a rate-limit
|
|
51
54
|
* to 503) — no stale catalog is ever served.
|
|
52
55
|
*/
|
|
53
|
-
|
|
56
|
+
async function loadPluginCatalog(
|
|
54
57
|
ref: string,
|
|
55
|
-
deps:
|
|
58
|
+
deps: PluginCatalogLoadDeps,
|
|
56
59
|
): Promise<PluginCatalog> {
|
|
57
60
|
if (!arePlatformFeaturesEnabled()) {
|
|
58
61
|
return { ...readBundledPluginCatalog(), ref };
|
|
@@ -69,6 +72,25 @@ export async function getPluginCatalog(
|
|
|
69
72
|
return merged;
|
|
70
73
|
}
|
|
71
74
|
|
|
75
|
+
/** Installable catalog with feature-flag visibility applied. */
|
|
76
|
+
export async function getPluginCatalog(
|
|
77
|
+
ref: string,
|
|
78
|
+
deps: SearchPluginsDeps,
|
|
79
|
+
): Promise<PluginCatalog> {
|
|
80
|
+
return filterPluginCatalogByFeatureFlags(
|
|
81
|
+
await loadPluginCatalog(ref, deps),
|
|
82
|
+
deps.featureFlagEnabled,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Category metadata for plugins already installed, including hidden entries. */
|
|
87
|
+
export async function getPluginCatalogForInstalledMetadata(
|
|
88
|
+
ref: string,
|
|
89
|
+
deps: PluginCatalogLoadDeps,
|
|
90
|
+
): Promise<PluginCatalog> {
|
|
91
|
+
return loadPluginCatalog(ref, deps);
|
|
92
|
+
}
|
|
93
|
+
|
|
72
94
|
/** Invalidate the cache (for testing or forced refresh). */
|
|
73
95
|
export function invalidatePluginCatalogCache(): void {
|
|
74
96
|
cache.clear();
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import bundledManifest from "./bundled-marketplace.json" with { type: "json" };
|
|
16
|
+
import {
|
|
17
|
+
isPluginCatalogEntryVisible,
|
|
18
|
+
type PluginCatalogFeatureFlagResolver,
|
|
19
|
+
} from "./plugin-catalog-visibility.js";
|
|
16
20
|
import {
|
|
17
21
|
type MarketplaceEntry,
|
|
18
22
|
marketplaceManifestSchema,
|
|
@@ -27,6 +31,7 @@ import {
|
|
|
27
31
|
// Re-exported so `local`-tagged CLI commands can gate on platform features
|
|
28
32
|
// without importing `platform/` directly (cli/no-daemon-internals allows lib).
|
|
29
33
|
export { arePlatformFeaturesEnabled } from "../../platform/feature-gate.js";
|
|
34
|
+
export { isPluginCatalogEntryVisible };
|
|
30
35
|
|
|
31
36
|
let memoizedEntries: readonly MarketplaceEntry[] | undefined;
|
|
32
37
|
|
|
@@ -80,6 +85,10 @@ export function readBundledLocalPluginCatalog(): PluginCatalog {
|
|
|
80
85
|
*/
|
|
81
86
|
export function resolveBundledPluginSource(
|
|
82
87
|
name: string,
|
|
88
|
+
isFeatureFlagEnabled?: PluginCatalogFeatureFlagResolver,
|
|
83
89
|
): ResolvedPluginSource | null {
|
|
90
|
+
if (!isPluginCatalogEntryVisible(name, isFeatureFlagEnabled)) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
84
93
|
return resolveMarketplaceSource(name, bundledEntries());
|
|
85
94
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
2
|
+
import type { PluginCatalog } from "./search-plugins.js";
|
|
3
|
+
|
|
4
|
+
export const MCP_CATALOG_QA_INTEGRATIONS_FLAG = "mcp-catalog-qa-integrations";
|
|
5
|
+
|
|
6
|
+
const MCP_CATALOG_QA_INTEGRATION_NAMES = new Set([
|
|
7
|
+
"gamma",
|
|
8
|
+
"guru",
|
|
9
|
+
"intercom",
|
|
10
|
+
"ramp",
|
|
11
|
+
"semrush",
|
|
12
|
+
"typeform",
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
export type PluginCatalogFeatureFlagResolver = (key: string) => boolean;
|
|
16
|
+
|
|
17
|
+
export function isPluginCatalogEntryVisible(
|
|
18
|
+
name: string,
|
|
19
|
+
isFeatureFlagEnabled: PluginCatalogFeatureFlagResolver = isAssistantFeatureFlagEnabled,
|
|
20
|
+
): boolean {
|
|
21
|
+
return (
|
|
22
|
+
!MCP_CATALOG_QA_INTEGRATION_NAMES.has(name) ||
|
|
23
|
+
isFeatureFlagEnabled(MCP_CATALOG_QA_INTEGRATIONS_FLAG)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Hide catalog entries whose OAuth flows are still being validated. Installed
|
|
29
|
+
* plugins are unaffected because this filter only applies to catalog reads.
|
|
30
|
+
*/
|
|
31
|
+
export function filterPluginCatalogByFeatureFlags(
|
|
32
|
+
catalog: PluginCatalog,
|
|
33
|
+
isFeatureFlagEnabled: PluginCatalogFeatureFlagResolver = isAssistantFeatureFlagEnabled,
|
|
34
|
+
): PluginCatalog {
|
|
35
|
+
const matches = catalog.matches.filter((match) =>
|
|
36
|
+
isPluginCatalogEntryVisible(match.name, isFeatureFlagEnabled),
|
|
37
|
+
);
|
|
38
|
+
return matches.length === catalog.matches.length
|
|
39
|
+
? catalog
|
|
40
|
+
: { ...catalog, matches };
|
|
41
|
+
}
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
findCatalogEntry,
|
|
47
47
|
resolveSourceFromMatch,
|
|
48
48
|
} from "./plugin-catalog-resolve.js";
|
|
49
|
+
import { isPluginCatalogEntryVisible } from "./plugin-catalog-visibility.js";
|
|
49
50
|
import { DEFAULT_PLUGIN_REF } from "./plugin-constants.js";
|
|
50
51
|
import { readValidatedPluginIcon } from "./plugin-icon-file.js";
|
|
51
52
|
import { fetchMarketplaceEntries } from "./plugin-marketplace.js";
|
|
@@ -156,8 +157,8 @@ export class PluginDetailsNotFoundError extends Error {
|
|
|
156
157
|
* Resolve the detail view for {@link opts.name}.
|
|
157
158
|
*
|
|
158
159
|
* Throws {@link PluginDetailsNotFoundError} when the name is neither installed
|
|
159
|
-
* locally nor present in the catalog
|
|
160
|
-
*
|
|
160
|
+
* locally nor present in the visibility-gated catalog at either the default
|
|
161
|
+
* or an explicit historical ref. A gated-catalog outage
|
|
161
162
|
* ({@link PluginCatalogUnavailableError}) degrades to the on-disk fields only
|
|
162
163
|
* when a local copy exists; with nothing installed to render it propagates so
|
|
163
164
|
* the caller can map the transient failure to a retryable 503 rather than a
|
|
@@ -400,11 +401,10 @@ async function fetchRawFile(
|
|
|
400
401
|
/**
|
|
401
402
|
* Resolve the external catalog entry claiming {@link name}.
|
|
402
403
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
* history / inspect carve-out.
|
|
404
|
+
* Both default and historical-ref lookups apply catalog visibility first. The
|
|
405
|
+
* default ref then reads the gated catalog used by search and install, while an
|
|
406
|
+
* explicit historical {@link ref} reads the GitHub marketplace manifest at
|
|
407
|
+
* that revision.
|
|
408
408
|
*
|
|
409
409
|
* A gated-catalog outage (fail-hard {@link PluginCatalogUnavailableError})
|
|
410
410
|
* propagates when nothing is installed — there is nothing to render and the
|
|
@@ -420,6 +420,9 @@ async function resolveCatalogEntry(
|
|
|
420
420
|
fetchFn: FetchLike,
|
|
421
421
|
installed: boolean,
|
|
422
422
|
): Promise<PluginSearchMatch | null> {
|
|
423
|
+
if (!isPluginCatalogEntryVisible(name)) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
423
426
|
try {
|
|
424
427
|
const match =
|
|
425
428
|
ref === DEFAULT_PLUGIN_REF
|
|
@@ -19,6 +19,8 @@ import type { MarketplaceEntry } from "./plugin-marketplace.js";
|
|
|
19
19
|
export interface SearchPluginsDeps {
|
|
20
20
|
/** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
|
|
21
21
|
readonly fetch: FetchLike;
|
|
22
|
+
/** Optional feature-flag resolver for deterministic tests. */
|
|
23
|
+
readonly featureFlagEnabled?: (key: string) => boolean;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/** Where a catalog match comes from. */
|
|
@@ -338,6 +338,14 @@
|
|
|
338
338
|
"description": "Show the Add Server action on the MCP settings page. The MCP page itself remains visible; this flag gates only creating new Model Context Protocol server connections.",
|
|
339
339
|
"defaultEnabled": false
|
|
340
340
|
},
|
|
341
|
+
{
|
|
342
|
+
"id": "mcp-catalog-qa-integrations",
|
|
343
|
+
"scope": "assistant",
|
|
344
|
+
"key": "mcp-catalog-qa-integrations",
|
|
345
|
+
"label": "MCP Catalog QA Integrations",
|
|
346
|
+
"description": "Show Gamma, Guru, Intercom, Ramp, Semrush, and Typeform in the installable plugin catalog while their OAuth flows are validated. When off, catalog search, detail, and install-by-name cannot resolve them. Existing installed connections remain available.",
|
|
347
|
+
"defaultEnabled": false
|
|
348
|
+
},
|
|
341
349
|
{
|
|
342
350
|
"id": "os-beta",
|
|
343
351
|
"scope": "assistant",
|
|
@@ -557,7 +565,7 @@
|
|
|
557
565
|
"scope": "both",
|
|
558
566
|
"key": "sidebar-done",
|
|
559
567
|
"label": "Sidebar Done",
|
|
560
|
-
"description": "Turns archiving a chat into marking it done: the sidebar row carries a Done check instead of the overflow menu, an
|
|
568
|
+
"description": "Turns archiving a chat into marking it done: the sidebar row carries a Done check instead of the overflow menu, an All chats page lists the full history, and done chats stay findable in search by default. Off keeps today's Archive wording, the overflow menu, and search that hides archived rows unless the query asks for them.",
|
|
561
569
|
"defaultEnabled": false
|
|
562
570
|
}
|
|
563
571
|
]
|
|
@@ -34,7 +34,7 @@ export const DEFAULT_CARD_CHARS = 500;
|
|
|
34
34
|
* only, so a missing product is not evidence it is unsupported.
|
|
35
35
|
*/
|
|
36
36
|
export const SKILLS_INJECTION_CATALOG_HINT =
|
|
37
|
-
"Injected skills are only ones currently in the workspace. Run `assistant plugins search <name>` and `assistant skills search <name>` before concluding a given integration or skill is unsupported";
|
|
37
|
+
"These skill cards describe availability, not tool instructions. When skill_load is available, use it to load a skill's current instructions and input schemas before calling its tools; follow those schemas over remembered arguments. Injected skills are only ones currently in the workspace. Run `assistant plugins search <name>` and `assistant skills search <name>` before concluding a given integration or skill is unsupported";
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Render the prose-style capability statement embedded into the unified
|
|
@@ -31,6 +31,9 @@ describe("renderInjectionBlockInner", () => {
|
|
|
31
31
|
expect(inner).toContain("assistant plugins search <name>");
|
|
32
32
|
expect(inner).toContain("assistant skills search <name>");
|
|
33
33
|
expect(inner).toContain("currently in the workspace");
|
|
34
|
+
expect(inner).toContain("When skill_load is available, use it");
|
|
35
|
+
expect(inner).toContain("before calling its tools");
|
|
36
|
+
expect(inner).toContain("over remembered arguments");
|
|
34
37
|
expect(inner.indexOf("# Skills")).toBeLessThan(inner.indexOf("# Skill:"));
|
|
35
38
|
expect(inner.startsWith(`${V3_INJECTION_HEADER}\n\n# Skills\n`)).toBe(true);
|
|
36
39
|
});
|
|
@@ -57,6 +57,8 @@ describe("isConnectionCompatibleWithModel", () => {
|
|
|
57
57
|
test("oauth_subscription connection is compatible with a Codex model", () => {
|
|
58
58
|
const conn = { auth: oauthAuth };
|
|
59
59
|
expect(isConnectionCompatibleWithModel(conn, "gpt-6-astra")).toBe(true);
|
|
60
|
+
expect(isConnectionCompatibleWithModel(conn, "gpt-6-sol")).toBe(true);
|
|
61
|
+
expect(isConnectionCompatibleWithModel(conn, "gpt-6-luna")).toBe(true);
|
|
60
62
|
expect(isConnectionCompatibleWithModel(conn, "gpt-5.6-sol")).toBe(true);
|
|
61
63
|
expect(isConnectionCompatibleWithModel(conn, "gpt-5.6-terra")).toBe(true);
|
|
62
64
|
expect(isConnectionCompatibleWithModel(conn, "gpt-5.6-luna")).toBe(true);
|
|
@@ -1004,7 +1004,7 @@ export class AnthropicProvider implements Provider {
|
|
|
1004
1004
|
const isHaiku = effectiveModel.includes("haiku");
|
|
1005
1005
|
// Effort support is per-model: Haiku and Sonnet 4.5 reject the param (see isEffortSupported).
|
|
1006
1006
|
const supportsEffort = isEffortSupported(effectiveModel);
|
|
1007
|
-
// opus-4-7 / opus-4-8 / opus-5 and sonnet-5 reject `temperature`,
|
|
1007
|
+
// opus-4-7 / opus-4-8 / opus-5 / opus-5-5 and sonnet-5 reject `temperature`,
|
|
1008
1008
|
// `top_p`, and `top_k` with a 400 "`temperature`/`top_p` is deprecated
|
|
1009
1009
|
// for this model" — model-wide, not effort-conditional (verified
|
|
1010
1010
|
// 2026-06-23). opus-4-6 / sonnet-4-6 / haiku-4-5 still accept them.
|
|
@@ -1016,7 +1016,7 @@ export class AnthropicProvider implements Provider {
|
|
|
1016
1016
|
// suffix is what matches.
|
|
1017
1017
|
const deprecatesSamplingParams =
|
|
1018
1018
|
/claude-opus-4-[78]\b/.test(effectiveModel) ||
|
|
1019
|
-
/claude-opus-5
|
|
1019
|
+
/claude-opus-5(-5)?\b/.test(effectiveModel) ||
|
|
1020
1020
|
/claude-sonnet-5\b/.test(effectiveModel) ||
|
|
1021
1021
|
/claude-fable-/.test(effectiveModel);
|
|
1022
1022
|
const mergedOutputConfig = {
|
|
@@ -245,6 +245,24 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
245
245
|
cacheReadPer1mTokens: 1,
|
|
246
246
|
},
|
|
247
247
|
},
|
|
248
|
+
{
|
|
249
|
+
id: "claude-opus-5-5",
|
|
250
|
+
displayName: "Claude Opus 5.5",
|
|
251
|
+
contextWindowTokens: 1000000,
|
|
252
|
+
maxOutputTokens: 128000,
|
|
253
|
+
longContextPricingThresholdTokens: 200000,
|
|
254
|
+
supportsThinking: true,
|
|
255
|
+
adaptiveThinkingOnly: true,
|
|
256
|
+
supportsCaching: true,
|
|
257
|
+
supportsVision: true,
|
|
258
|
+
supportsToolUse: true,
|
|
259
|
+
pricing: {
|
|
260
|
+
inputPer1mTokens: 4,
|
|
261
|
+
outputPer1mTokens: 20,
|
|
262
|
+
cacheWritePer1mTokens: 5,
|
|
263
|
+
cacheReadPer1mTokens: 0.2,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
248
266
|
{
|
|
249
267
|
id: "claude-opus-5",
|
|
250
268
|
displayName: "Claude Opus 5",
|
|
@@ -456,6 +474,66 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
456
474
|
],
|
|
457
475
|
},
|
|
458
476
|
},
|
|
477
|
+
{
|
|
478
|
+
id: "gpt-6-sol",
|
|
479
|
+
displayName: "GPT-6 Sol",
|
|
480
|
+
contextWindowTokens: 1050000,
|
|
481
|
+
maxOutputTokens: 128000,
|
|
482
|
+
longContextPricingThresholdTokens:
|
|
483
|
+
OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
484
|
+
supportsThinking: true,
|
|
485
|
+
supportsCaching: true,
|
|
486
|
+
supportsVision: true,
|
|
487
|
+
supportsToolUse: true,
|
|
488
|
+
supportsPromptCacheBreakpoints: true,
|
|
489
|
+
maxEffort: "max",
|
|
490
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
491
|
+
pricing: {
|
|
492
|
+
inputPer1mTokens: 2,
|
|
493
|
+
outputPer1mTokens: 10,
|
|
494
|
+
cacheWritePer1mTokens: 2.5,
|
|
495
|
+
cacheReadPer1mTokens: 0.2,
|
|
496
|
+
tiers: [
|
|
497
|
+
{
|
|
498
|
+
inputTokenThreshold: OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
499
|
+
inputPer1mTokens: 4,
|
|
500
|
+
outputPer1mTokens: 15,
|
|
501
|
+
cacheWritePer1mTokens: 5,
|
|
502
|
+
cacheReadPer1mTokens: 0.4,
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
id: "gpt-6-luna",
|
|
509
|
+
displayName: "GPT-6 Luna",
|
|
510
|
+
contextWindowTokens: 1050000,
|
|
511
|
+
maxOutputTokens: 128000,
|
|
512
|
+
longContextPricingThresholdTokens:
|
|
513
|
+
OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
514
|
+
supportsThinking: true,
|
|
515
|
+
supportsCaching: true,
|
|
516
|
+
supportsVision: true,
|
|
517
|
+
supportsToolUse: true,
|
|
518
|
+
supportsPromptCacheBreakpoints: true,
|
|
519
|
+
maxEffort: "max",
|
|
520
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
521
|
+
pricing: {
|
|
522
|
+
inputPer1mTokens: 0.1,
|
|
523
|
+
outputPer1mTokens: 0.5,
|
|
524
|
+
cacheWritePer1mTokens: 0.125,
|
|
525
|
+
cacheReadPer1mTokens: 0.01,
|
|
526
|
+
tiers: [
|
|
527
|
+
{
|
|
528
|
+
inputTokenThreshold: OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
529
|
+
inputPer1mTokens: 0.2,
|
|
530
|
+
outputPer1mTokens: 0.75,
|
|
531
|
+
cacheWritePer1mTokens: 0.25,
|
|
532
|
+
cacheReadPer1mTokens: 0.02,
|
|
533
|
+
},
|
|
534
|
+
],
|
|
535
|
+
},
|
|
536
|
+
},
|
|
459
537
|
// GPT-5.6 family (Sol / Terra / Luna). cacheRead is the 90% cached-read
|
|
460
538
|
// discount; cacheWrite is the 1.25x-input rate GPT-5.6+ bills for
|
|
461
539
|
// prompt tokens written to the cache (reported as `cache_write_tokens`
|
|
@@ -1209,6 +1287,24 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1209
1287
|
cacheReadPer1mTokens: 1,
|
|
1210
1288
|
},
|
|
1211
1289
|
},
|
|
1290
|
+
{
|
|
1291
|
+
id: "anthropic/claude-opus-5.5",
|
|
1292
|
+
displayName: "Claude Opus 5.5",
|
|
1293
|
+
contextWindowTokens: 1000000,
|
|
1294
|
+
maxOutputTokens: 128000,
|
|
1295
|
+
longContextPricingThresholdTokens: 200000,
|
|
1296
|
+
supportsThinking: true,
|
|
1297
|
+
adaptiveThinkingOnly: true,
|
|
1298
|
+
supportsCaching: true,
|
|
1299
|
+
supportsVision: true,
|
|
1300
|
+
supportsToolUse: true,
|
|
1301
|
+
pricing: {
|
|
1302
|
+
inputPer1mTokens: 4,
|
|
1303
|
+
outputPer1mTokens: 20,
|
|
1304
|
+
cacheWritePer1mTokens: 5,
|
|
1305
|
+
cacheReadPer1mTokens: 0.2,
|
|
1306
|
+
},
|
|
1307
|
+
},
|
|
1212
1308
|
{
|
|
1213
1309
|
id: "anthropic/claude-opus-5",
|
|
1214
1310
|
displayName: "Claude Opus 5",
|
|
@@ -1431,6 +1527,126 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1431
1527
|
],
|
|
1432
1528
|
},
|
|
1433
1529
|
},
|
|
1530
|
+
{
|
|
1531
|
+
id: "openai/gpt-6-sol",
|
|
1532
|
+
displayName: "GPT-6 Sol",
|
|
1533
|
+
contextWindowTokens: 1050000,
|
|
1534
|
+
maxOutputTokens: 128000,
|
|
1535
|
+
longContextPricingThresholdTokens:
|
|
1536
|
+
OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1537
|
+
supportsThinking: true,
|
|
1538
|
+
supportsCaching: true,
|
|
1539
|
+
supportsVision: true,
|
|
1540
|
+
supportsToolUse: true,
|
|
1541
|
+
supportsPromptCacheBreakpoints: true,
|
|
1542
|
+
maxEffort: "max",
|
|
1543
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
1544
|
+
pricing: {
|
|
1545
|
+
inputPer1mTokens: 2,
|
|
1546
|
+
outputPer1mTokens: 10,
|
|
1547
|
+
cacheWritePer1mTokens: 2.5,
|
|
1548
|
+
cacheReadPer1mTokens: 0.2,
|
|
1549
|
+
tiers: [
|
|
1550
|
+
{
|
|
1551
|
+
inputTokenThreshold: OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1552
|
+
inputPer1mTokens: 4,
|
|
1553
|
+
outputPer1mTokens: 15,
|
|
1554
|
+
cacheWritePer1mTokens: 5,
|
|
1555
|
+
cacheReadPer1mTokens: 0.4,
|
|
1556
|
+
},
|
|
1557
|
+
],
|
|
1558
|
+
},
|
|
1559
|
+
},
|
|
1560
|
+
{
|
|
1561
|
+
id: "openai/gpt-6-sol-pro",
|
|
1562
|
+
displayName: "GPT-6 Sol Pro",
|
|
1563
|
+
contextWindowTokens: 1050000,
|
|
1564
|
+
maxOutputTokens: 128000,
|
|
1565
|
+
longContextPricingThresholdTokens:
|
|
1566
|
+
OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1567
|
+
supportsThinking: true,
|
|
1568
|
+
supportsCaching: true,
|
|
1569
|
+
supportsVision: true,
|
|
1570
|
+
supportsToolUse: true,
|
|
1571
|
+
supportsPromptCacheBreakpoints: true,
|
|
1572
|
+
maxEffort: "max",
|
|
1573
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
1574
|
+
pricing: {
|
|
1575
|
+
inputPer1mTokens: 2,
|
|
1576
|
+
outputPer1mTokens: 10,
|
|
1577
|
+
cacheWritePer1mTokens: 2.5,
|
|
1578
|
+
cacheReadPer1mTokens: 0.2,
|
|
1579
|
+
tiers: [
|
|
1580
|
+
{
|
|
1581
|
+
inputTokenThreshold: OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1582
|
+
inputPer1mTokens: 4,
|
|
1583
|
+
outputPer1mTokens: 15,
|
|
1584
|
+
cacheWritePer1mTokens: 5,
|
|
1585
|
+
cacheReadPer1mTokens: 0.4,
|
|
1586
|
+
},
|
|
1587
|
+
],
|
|
1588
|
+
},
|
|
1589
|
+
},
|
|
1590
|
+
{
|
|
1591
|
+
id: "openai/gpt-6-luna",
|
|
1592
|
+
displayName: "GPT-6 Luna",
|
|
1593
|
+
contextWindowTokens: 1050000,
|
|
1594
|
+
maxOutputTokens: 128000,
|
|
1595
|
+
longContextPricingThresholdTokens:
|
|
1596
|
+
OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1597
|
+
supportsThinking: true,
|
|
1598
|
+
supportsCaching: true,
|
|
1599
|
+
supportsVision: true,
|
|
1600
|
+
supportsToolUse: true,
|
|
1601
|
+
supportsPromptCacheBreakpoints: true,
|
|
1602
|
+
maxEffort: "max",
|
|
1603
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
1604
|
+
pricing: {
|
|
1605
|
+
inputPer1mTokens: 0.1,
|
|
1606
|
+
outputPer1mTokens: 0.5,
|
|
1607
|
+
cacheWritePer1mTokens: 0.125,
|
|
1608
|
+
cacheReadPer1mTokens: 0.01,
|
|
1609
|
+
tiers: [
|
|
1610
|
+
{
|
|
1611
|
+
inputTokenThreshold: OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1612
|
+
inputPer1mTokens: 0.2,
|
|
1613
|
+
outputPer1mTokens: 0.75,
|
|
1614
|
+
cacheWritePer1mTokens: 0.25,
|
|
1615
|
+
cacheReadPer1mTokens: 0.02,
|
|
1616
|
+
},
|
|
1617
|
+
],
|
|
1618
|
+
},
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
id: "openai/gpt-6-luna-pro",
|
|
1622
|
+
displayName: "GPT-6 Luna Pro",
|
|
1623
|
+
contextWindowTokens: 1050000,
|
|
1624
|
+
maxOutputTokens: 128000,
|
|
1625
|
+
longContextPricingThresholdTokens:
|
|
1626
|
+
OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1627
|
+
supportsThinking: true,
|
|
1628
|
+
supportsCaching: true,
|
|
1629
|
+
supportsVision: true,
|
|
1630
|
+
supportsToolUse: true,
|
|
1631
|
+
supportsPromptCacheBreakpoints: true,
|
|
1632
|
+
maxEffort: "max",
|
|
1633
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
1634
|
+
pricing: {
|
|
1635
|
+
inputPer1mTokens: 0.1,
|
|
1636
|
+
outputPer1mTokens: 0.5,
|
|
1637
|
+
cacheWritePer1mTokens: 0.125,
|
|
1638
|
+
cacheReadPer1mTokens: 0.01,
|
|
1639
|
+
tiers: [
|
|
1640
|
+
{
|
|
1641
|
+
inputTokenThreshold: OPENAI_LONG_CONTEXT_PRICING_THRESHOLD_TOKENS,
|
|
1642
|
+
inputPer1mTokens: 0.2,
|
|
1643
|
+
outputPer1mTokens: 0.75,
|
|
1644
|
+
cacheWritePer1mTokens: 0.25,
|
|
1645
|
+
cacheReadPer1mTokens: 0.02,
|
|
1646
|
+
},
|
|
1647
|
+
],
|
|
1648
|
+
},
|
|
1649
|
+
},
|
|
1434
1650
|
// GPT-5.6 family (Sol / Terra / Luna). The `*-pro` slugs are the same
|
|
1435
1651
|
// underlying models served with `reasoning.mode: pro` at identical
|
|
1436
1652
|
// rates. cacheWrite is the 1.25x-input rate GPT-5.6+ bills for prompt
|
|
@@ -2142,6 +2358,24 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
2142
2358
|
cacheReadPer1mTokens: 1,
|
|
2143
2359
|
},
|
|
2144
2360
|
},
|
|
2361
|
+
{
|
|
2362
|
+
id: "anthropic/claude-opus-5.5",
|
|
2363
|
+
displayName: "Claude Opus 5.5",
|
|
2364
|
+
contextWindowTokens: 1000000,
|
|
2365
|
+
maxOutputTokens: 128000,
|
|
2366
|
+
longContextPricingThresholdTokens: 200000,
|
|
2367
|
+
supportsThinking: true,
|
|
2368
|
+
adaptiveThinkingOnly: true,
|
|
2369
|
+
supportsCaching: true,
|
|
2370
|
+
supportsVision: true,
|
|
2371
|
+
supportsToolUse: true,
|
|
2372
|
+
pricing: {
|
|
2373
|
+
inputPer1mTokens: 4,
|
|
2374
|
+
outputPer1mTokens: 20,
|
|
2375
|
+
cacheWritePer1mTokens: 5,
|
|
2376
|
+
cacheReadPer1mTokens: 0.2,
|
|
2377
|
+
},
|
|
2378
|
+
},
|
|
2145
2379
|
{
|
|
2146
2380
|
id: "anthropic/claude-opus-5",
|
|
2147
2381
|
displayName: "Claude Opus 5",
|
|
@@ -165,8 +165,14 @@ const getCatalogSpy = mock(
|
|
|
165
165
|
},
|
|
166
166
|
);
|
|
167
167
|
|
|
168
|
+
const getInstalledMetadataCatalogSpy = mock(
|
|
169
|
+
(ref: string, deps: SearchPluginsDeps): Promise<PluginCatalog> =>
|
|
170
|
+
getCatalogSpy(ref, deps),
|
|
171
|
+
);
|
|
172
|
+
|
|
168
173
|
mock.module("../../../cli/lib/plugin-catalog-cache.js", () => ({
|
|
169
174
|
getPluginCatalog: getCatalogSpy,
|
|
175
|
+
getPluginCatalogForInstalledMetadata: getInstalledMetadataCatalogSpy,
|
|
170
176
|
}));
|
|
171
177
|
|
|
172
178
|
// Mock uninstallPlugin. The handler's error mapping is the wiring under
|
|
@@ -471,6 +477,7 @@ beforeEach(() => {
|
|
|
471
477
|
describe("GET /v1/plugins", () => {
|
|
472
478
|
beforeEach(() => {
|
|
473
479
|
getCatalogSpy.mockClear();
|
|
480
|
+
getInstalledMetadataCatalogSpy.mockClear();
|
|
474
481
|
// Default to an empty catalog so every installed plugin's category resolves
|
|
475
482
|
// to `null` (bucketed under "system"). Tests that exercise real categories
|
|
476
483
|
// override this.
|
|
@@ -849,6 +856,7 @@ describe("GET /v1/plugins", () => {
|
|
|
849
856
|
);
|
|
850
857
|
|
|
851
858
|
const map = await loadCategoryMapBounded();
|
|
859
|
+
expect(getInstalledMetadataCatalogSpy).toHaveBeenCalledTimes(1);
|
|
852
860
|
expect(map.get("alpha")).toBe("productivity");
|
|
853
861
|
});
|
|
854
862
|
|
|
@@ -48,7 +48,10 @@ import {
|
|
|
48
48
|
type InstalledPluginInfo,
|
|
49
49
|
listInstalledPlugins,
|
|
50
50
|
} from "../../cli/lib/list-installed-plugins.js";
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
getPluginCatalog,
|
|
53
|
+
getPluginCatalogForInstalledMetadata,
|
|
54
|
+
} from "../../cli/lib/plugin-catalog-cache.js";
|
|
52
55
|
import { resolvePluginSourceFromCatalog } from "../../cli/lib/plugin-catalog-resolve.js";
|
|
53
56
|
import {
|
|
54
57
|
DEFAULT_PIN_HISTORY_LIMIT,
|
|
@@ -969,7 +972,7 @@ export async function loadCategoryMapBounded(
|
|
|
969
972
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
970
973
|
try {
|
|
971
974
|
const catalog = await Promise.race([
|
|
972
|
-
|
|
975
|
+
getPluginCatalogForInstalledMetadata(DEFAULT_PLUGIN_REF, {
|
|
973
976
|
fetch: globalThis.fetch.bind(globalThis),
|
|
974
977
|
}),
|
|
975
978
|
new Promise<null>((resolve) => {
|