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
90 changes: 90 additions & 0 deletions apps/server/src/mcp/OrchestratorMcpService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,96 @@ describe("OrchestratorMcpService provider resolution", () => {
);
});

it.effect.each(["already advertised", "newly advertised", "still unavailable"] as const)(
"rechecks a delegated model that is %s",
(availability) =>
Effect.gen(function* () {
const museInstanceId = ProviderInstanceId.make("muse");
const model = "muse-wonder-1.0";
const provider = (advertisedModel: string) =>
providerSnapshot({
instanceId: museInstanceId,
driver: ProviderDriverKind.make("muse"),
model: advertisedModel,
});
const commands: Array<unknown> = [];
const refreshedInstances: Array<ProviderInstanceId> = [];
const task = {
id: taskId,
threadId: parentThreadId,
origin: "app_owned",
childThreadId,
providerInstanceId: museInstanceId,
model,
status: "running",
result: null,
};
const dependencies = Layer.mergeAll(
NodeServices.layer,
NodeCrypto.layer,
Layer.mock(ThreadManagementService.ThreadManagementService)({
hasReceipt: () => Effect.succeed(false),
getThreadRecords: (threadId) =>
Effect.succeed(
threadId === parentThreadId
? parentProjection(commands.length > 0 ? [task] : [])
: childProjection,
),
dispatch: (command) =>
Effect.sync(() => {
commands.push(command);
return {
sequence: 1,
storedEvents: [{ event: { type: "subagent.updated", payload: task } }],
} as never;
}),
}),
ServerSettings.layerTest(),
Layer.mock(ProviderRegistry.ProviderRegistry)({
getProviders: Effect.succeed([
provider(availability === "already advertised" ? model : "muse-spark-1.3"),
]),
refreshInstance: (instanceId) =>
Effect.sync(() => {
refreshedInstances.push(instanceId);
return [provider(availability === "still unavailable" ? "muse-spark-1.3" : model)];
}),
}),
adapterRegistryLayer([museInstanceId]),
Layer.mock(ProjectService.ProjectService)({}),
Layer.mock(SecretRequests.SecretRequests)({}),
Layer.mock(RemoteDelegation.RemoteDelegation)({}),
Layer.mock(ScheduledTaskService.ScheduledTaskService)({}),
);
yield* Effect.gen(function* () {
const service = yield* OrchestratorMcpService.OrchestratorMcpService;
const delegation = service.delegateTask(scope, {
task: "Review the change.",
target: { providerInstanceId: museInstanceId, model },
mode: "async",
clientRequestId: availability,
});
if (availability === "still unavailable") {
const error = yield* delegation.pipe(Effect.flip);
assert.equal(error.code, "model_unavailable");
assert.deepEqual(commands, []);
} else {
const result = yield* delegation;
assert.equal(result.model, model);
assert.equal(commands.length, 1);
assert.deepEqual((commands[0] as { modelSelection: unknown }).modelSelection, {
instanceId: museInstanceId,
model,
});
}
assert.deepEqual(
refreshedInstances,
availability === "already advertised" ? [] : [museInstanceId],
);
}).pipe(Effect.provide(layerOrchestratorService.pipe(Layer.provide(dependencies))));
}),
);

it.effect("rejects delegation to a provider without a registered adapter", () =>
Effect.gen(function* () {
const forkOnlyInstanceId = ProviderInstanceId.make("forkOnly");
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/mcp/OrchestratorMcpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,7 @@ const make = Effect.gen(function* () {
if (instanceId === undefined) return resolved;
return resolved.pipe(
Effect.catchIf(
(error) => error.code === "provider_unavailable",
(error) => error.code === "provider_unavailable" || error.code === "model_unavailable",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry stale models for driver-selected targets

When a supported target specifies driverKind (with or without model) rather than providerInstanceId, the instanceId computed above is undefined, so the early return installs neither this new model_unavailable catch nor the refresh. A stale catalog for the provider selected by resolveTarget therefore still rejects requests such as { driverKind: "muse", model: "muse-wonder-1.0" } without calling refreshInstance, even though this selector combination is accepted by OrchestratorMcpTarget; carry the selected instance into the retry path as well.

Useful? React with 👍 / 👎.

() =>
providerRegistry
.refreshInstance(instanceId)
Expand Down
Loading