Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ or in free mode.
_Avoid_: room, channel, group chat

**Color**:
One harness, model and effort used together, such as Claude Opus 5.5 at high
effort. A Drafter runs on one Color.
One harness, model, effort and advertised model options used together, such as
Codex at high effort with Fast speed. Different options make distinct Colors.
A Drafter runs on one Color.
_Avoid_: model config, route

**White**:
Expand Down
42 changes: 1 addition & 41 deletions apps/server/src/mcp/OrchestratorMcpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { callerLinkOrigin, assertSameLink } from "./linkOrigin.ts";
import { SpectrumMcpInterrupt } from "../spectrum/mcpInterrupt.ts";
import { SpectrumMcpSend } from "../spectrum/mcpSend.ts";
import * as Prism from "../prism/PrismService.ts";
import { invalidOptionSelections } from "../prism/modelOptions.ts";
import {
CommandId,
type RunId,
Expand Down Expand Up @@ -76,8 +77,6 @@ import {
type OrchestratorMcpThreadWaitInput,
type OrchestratorMcpThreadWaitResult,
type ProviderInteractionMode,
type ProviderOptionDescriptor,
type ProviderOptionSelection,
type RuntimeMode,
type ScheduledTask,
type ScheduledTaskError,
Expand Down Expand Up @@ -377,45 +376,6 @@ function providerConstraints(
return constraints;
}

/**
* Models without descriptors skip the descriptor checks (mirroring how model slugs are only
* validated when the provider advertises models), but duplicate ids always fail: downstream
* consumers disagree on whether the first or last value of a duplicated id wins.
*/
function invalidOptionSelections(
selections: ReadonlyArray<ProviderOptionSelection>,
descriptors: ReadonlyArray<ProviderOptionDescriptor> | undefined,
): ReadonlyArray<string> {
const problems: Array<string> = [];
const seen = new Set<string>();
for (const selection of selections) {
if (seen.has(selection.id)) {
problems.push(`Option ${selection.id} was specified more than once.`);
continue;
}
seen.add(selection.id);
if (descriptors === undefined) continue;
const descriptor = descriptors.find((candidate) => candidate.id === selection.id);
if (descriptor === undefined) {
const known = descriptors.map((candidate) => candidate.id).join(", ");
problems.push(`Unknown option ${selection.id}; supported options: ${known || "none"}.`);
continue;
}
if (descriptor.type === "boolean" && typeof selection.value !== "boolean") {
problems.push(`Option ${selection.id} expects a boolean value.`);
continue;
}
if (
descriptor.type === "select" &&
!descriptor.options.some((choice) => choice.id === selection.value)
) {
const choices = descriptor.options.map((choice) => choice.id).join(", ");
problems.push(`Option ${selection.id} must be one of: ${choices}.`);
}
}
return problems;
}

function taskStatusForRun(
run: Pick<OrchestrationV2Run, "status" | "restartCancelledBackgroundWork"> | undefined,
): OrchestratorMcpDelegateTaskResult["status"] {
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/peer/PeerAuthority.workflow.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2182,6 +2182,7 @@ export const makeNode = (
};
});
const restartSchedulerTransport = restartSchedulerTransportWith(attachmentRequests);
const launchValidation = yield* layerLaunchCatalog.pipe(Layer.build);
return {
...peer,
pendingRequests: Context.get(pendingRequests, PendingRequests.PendingRequests),
Expand Down Expand Up @@ -2209,7 +2210,7 @@ export const makeNode = (
databasePath: Context.get(databaseConfig, Config.ServerConfig).dbPath,
lifecycleRecovery,
signals,
runtime,
runtime: Context.merge(runtime, launchValidation),
requestContext,
secretOwners,
secretRequestClock,
Expand Down
176 changes: 176 additions & 0 deletions apps/server/src/prism/PrismService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ProviderDriverKind,
ProviderInstanceId,
type ServerProvider,
type ProviderOptionSelection,
} from "@t3tools/contracts";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
Expand All @@ -13,6 +14,7 @@ import * as ProviderRegistry from "../provider/ProviderRegistry.ts";
import { buildUnavailableProviderSnapshot } from "../provider/unavailableProviderSnapshot.ts";
import * as Settings from "../serverSettings.ts";
import * as Prism from "./PrismService.ts";
import * as Adapters from "../orchestration-v2/ProviderAdapterRegistry.ts";

const instanceId = ProviderInstanceId.make("codex");
const projectId = ProjectId.make("project:prism");
Expand Down Expand Up @@ -542,3 +544,177 @@ describe("PrismService", () => {
assert.strictEqual(Prism.delegatedPrismRole(), "worker");
});
});

const optionDescriptors = [
{
id: "reasoningEffort",
label: "Reasoning",
type: "select" as const,
options: [{ id: "high", label: "High" }],
},
{
id: "serviceTier",
label: "Speed",
type: "select" as const,
options: [
{ id: "priority", label: "Fast" },
{ id: "default", label: "Standard" },
],
},
{ id: "enabled", label: "Enabled", type: "boolean" as const },
];
const advertised = Effect.map(provider, (p) => ({
...p,
installed: true,
status: "ready" as const,
models: p.models.map((m) => ({ ...m, aliases: ["alias"], capabilities: { optionDescriptors } })),
}));
const fastOptions = [{ id: "serviceTier", value: "priority" }];

it.effect(
"preserves tier-only distinct Colors, effort and inherited options without a validation callback",
() =>
Effect.gen(function* () {
const p = yield* advertised;
yield* Effect.gen(function* () {
const service = yield* Prism.PrismService;
const result = yield* service.resolve({ projectId, role: "reviewer", count: 2 });
assert.deepStrictEqual(result.modelSelections, [
{
...selection,
options: [
{ id: "serviceTier", value: "default" },
{ id: "reasoningEffort", value: "high" },
],
},
{ ...selection, options: [...fastOptions, { id: "reasoningEffort", value: "high" }] },
]);
const inherited = {
...selection,
options: [...fastOptions, { id: "enabled", value: false }],
};
assert.deepStrictEqual(
(yield* service.resolve({ projectId, role: "planner", inherited })).modelSelection,
inherited,
);
assert.deepStrictEqual(
(yield* service.resolve({ projectId, role: "promachos" })).modelSelection.options,
[...fastOptions, { id: "reasoningEffort", value: "high" }],
);
}).pipe(
Effect.provide(
Prism.layer.pipe(
Layer.provide(
Layer.mergeAll(
registry([p]),
Settings.layerTest({
prismRoles: {
reviewer: {
models: [
{
...selection,
effort: "high",
options: [{ id: "serviceTier", value: "default" }],
},
{ ...selection, model: "alias", effort: "high", options: fastOptions },
],
},
promachos: { models: [{ ...selection, effort: "high", options: fastOptions }] },
},
}),
),
),
),
),
);
}),
);

it.effect(
"rejects unknown ids, values, types and duplicate Color options without falling back or needing a callback",
() =>
Effect.gen(function* () {
const p = yield* advertised;
const cases: readonly (readonly ProviderOptionSelection[])[] = [
[{ id: "unknown", value: "priority" }],
[{ id: "serviceTier", value: "turbo" }],
[{ id: "serviceTier", value: true }],
[{ id: "enabled", value: "true" }],
[{ id: "serviceTier", value: "default" }, ...fastOptions],
[{ id: "reasoningEffort", value: "invalid" }],
];
for (const options of cases) {
const error = yield* Effect.gen(function* () {
const service = yield* Prism.PrismService;
return yield* service.resolve({ projectId, role: "reviewer" });
}).pipe(
Effect.flip,
Effect.provide(
Prism.layer.pipe(
Layer.provide(
Layer.mergeAll(
registry([p]),
Settings.layerTest({
prismRoles: {
reviewer: { models: [{ ...selection, effort: "high", options }, selection] },
},
}),
),
),
),
),
);
assert.strictEqual(error.code, "invalid_request");
}
const error = yield* Effect.gen(function* () {
return yield* (yield* Prism.PrismService).resolve({ projectId, role: "reviewer" });
}).pipe(
Effect.flip,
Effect.provide(
Prism.layer.pipe(
Layer.provide(
Layer.mergeAll(
registry([yield* provider]),
Settings.layerTest({
prismRoles: { reviewer: { models: [{ ...selection, options: fastOptions }] } },
}),
),
),
),
),
);
assert.strictEqual(error.code, "invalid_request");
}),
);

it.effect(
"native launch validation canonicalizes the model and applies explicit-target option validation",
() =>
Effect.gen(function* () {
const p = yield* advertised;
yield* Effect.gen(function* () {
const valid = yield* Prism.validateLaunchSelection({
...selection,
model: "alias",
options: fastOptions,
});
assert.deepStrictEqual(valid, { ...selection, options: fastOptions });
const error = yield* Effect.flip(
Prism.validateLaunchSelection({
...selection,
options: [{ id: "serviceTier", value: "turbo" }],
}),
);
assert.strictEqual(error.code, "invalid_request");
}).pipe(
Effect.provide(
Layer.mergeAll(
registry([p]),
Layer.mock(Adapters.ProviderAdapterRegistryV2)({
list: () => Effect.succeed([instanceId]),
}),
),
),
);
}),
);
62 changes: 38 additions & 24 deletions apps/server/src/prism/PrismService.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { colorModelSelection, invalidOptionSelections } from "./modelOptions.ts";
import { unsupportedBare } from "@t3tools/provider-core/server/bareLaunch";
import {
DEFAULT_PRISM_LANE,
Expand Down Expand Up @@ -87,6 +88,15 @@ export const validateLaunchSelection = Effect.fn("Prism.validateLaunchSelection"
code: "model_unavailable",
message: `Model ${selection.model} is not advertised by provider ${selection.instanceId}.`,
});
const invalid = invalidOptionSelections(
selection.options ?? [],
model.capabilities?.optionDescriptors,
);
if (invalid.length > 0)
return yield* new OrchestratorMcpFailure({
code: "invalid_request",
message: `Model ${model.slug} on provider ${selection.instanceId} rejected options: ${invalid.join(" ")}`,
});
return { ...selection, model: model.slug };
});

Expand All @@ -111,19 +121,6 @@ function blocked(provider: ServerProvider, now: number) {
);
}

function effortOption(driver: string) {
switch (driver) {
case "codex":
case "grok":
case "muse":
return "reasoningEffort";
case "opencode":
return "variant";
default:
return "effort";
}
}

export class PrismService extends Context.Service<
PrismService,
{
Expand Down Expand Up @@ -280,16 +277,10 @@ const make = Effect.gen(function* () {
);
if (!model && !rechecking && (preferences.length > 0 || provider.models.length > 0))
continue;
const effort = "effort" in candidate ? candidate.effort : undefined;
const selection: ModelSelection = {
instanceId: candidate.instanceId,
model: model?.slug ?? candidate.model,
...(effort === undefined
? "options" in candidate
? { options: candidate.options }
: {}
: { options: [{ id: effortOption(provider.driver), value: effort }] }),
};
const selection = colorModelSelection(
{ ...candidate, model: model?.slug ?? candidate.model },
provider.driver,
);
const validated = yield* (
input.validate ? input.validate(selection) : Effect.succeed(selection)
).pipe(
Expand All @@ -299,7 +290,30 @@ const make = Effect.gen(function* () {
: Effect.fail(error),
),
);
if (validated !== null) started.push(validated);
if (validated === null) continue;
const advertisedModel = rechecking
? (yield* registry.getProviders)
.find((p) => p.instanceId === validated.instanceId)
?.models.find(
(m) => m.slug === validated.model || m.aliases?.includes(validated.model),
)
: model;
// New Color options must be advertised; legacy effort still works with older catalogs.
const descriptors = advertisedModel?.capabilities?.optionDescriptors;
const invalidColorOptions = invalidOptionSelections(
candidate.options ?? [],
preferences.length > 0 ? (descriptors ?? []) : descriptors,
);
const invalid =
invalidColorOptions.length > 0
? invalidColorOptions
: invalidOptionSelections(validated.options ?? [], descriptors);
if (invalid.length > 0)
return yield* new OrchestratorMcpFailure({
code: "invalid_request",
message: `Model ${validated.model} on provider ${validated.instanceId} rejected options: ${invalid.join(" ")}`,
});
started.push(validated);
}
const [first, ...rest] = started;
if (first !== undefined)
Expand Down
Loading
Loading