mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 16:34:36 +00:00
fix(plugins): composers by reference follow the focused agent and reach retained composers
- `{ focused: true }` is the agent Command Center hands to agent items, published by its
registration through plugins/focused-agent.ts and empty outside a workspace. The composer source
reads that module instead of the remembered workspace selection, the navigation store and the
layout store, which also removes the require cycles those imports added.
- A composer the app retains in a workspace the user left has no live input. `insertText` and
`setPrompt` now store the text with `replaceDraftText`, which asks the mounted input to show it;
before, the text reached the store but the retained input never displayed it.
- `model` and `thinking` read exactly what the picker resolves before the catalog loads (no
models), then re-resolve when it lands, instead of reading null.
- The daemon validates a thinking option against the model the agent runs: the runtime model when
the provider lists it, else the configured one.
- The composer sources subscribe to query-cache updates only. A query another component creates
during its render is announced as `added`, and a subscriber that updates then made React warn
about updating a component while rendering a different one. A catalog read no longer starts its
load synchronously either.
This commit is contained in:
@@ -5436,6 +5436,81 @@ test("a running agent rejects a model or thinking option its provider does not l
|
||||
expect(manager.getAgent(snapshot.id)?.config.model).toBeUndefined();
|
||||
});
|
||||
|
||||
test("a thinking option is checked against the model the agent runs, as the picker chooses it", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-thinking-runtime-model-"));
|
||||
const provider: ProviderSnapshotEntry = {
|
||||
provider: "codex",
|
||||
status: "ready",
|
||||
enabled: true,
|
||||
models: [
|
||||
{
|
||||
provider: "codex",
|
||||
id: "configured",
|
||||
label: "Configured",
|
||||
isDefault: true,
|
||||
thinkingOptions: [{ id: "configured-only", label: "Configured only" }],
|
||||
},
|
||||
{
|
||||
provider: "codex",
|
||||
id: "runtime",
|
||||
label: "Runtime",
|
||||
thinkingOptions: [{ id: "runtime-only", label: "Runtime only" }],
|
||||
},
|
||||
],
|
||||
modes: [],
|
||||
};
|
||||
class RuntimeModelSession extends TestAgentSession {
|
||||
constructor(
|
||||
config: AgentSessionConfig,
|
||||
private readonly runtimeModel: string,
|
||||
) {
|
||||
super(config);
|
||||
}
|
||||
override async getRuntimeInfo() {
|
||||
return { ...(await super.getRuntimeInfo()), model: this.runtimeModel };
|
||||
}
|
||||
}
|
||||
class RuntimeModelClient extends TestAgentClient {
|
||||
constructor(private readonly runtimeModel: string) {
|
||||
super();
|
||||
}
|
||||
override async createSession(config: AgentSessionConfig): Promise<AgentSession> {
|
||||
return new RuntimeModelSession(config, this.runtimeModel);
|
||||
}
|
||||
}
|
||||
async function agentRunning(runtimeModel: string) {
|
||||
const manager = new AgentManager({
|
||||
clients: { codex: new RuntimeModelClient(runtimeModel) },
|
||||
registry: new AgentStorage(join(workdir, `agents-${runtimeModel}`), logger),
|
||||
validateAgentSelection: async (input) =>
|
||||
validateAgentConfigurationAgainstProvider({ input, provider }),
|
||||
logger,
|
||||
idFactory: () =>
|
||||
`00000000-0000-4000-8000-${runtimeModel === "runtime" ? "000000000137" : "000000000138"}`,
|
||||
});
|
||||
const snapshot = await manager.createAgent(
|
||||
{ provider: "codex", cwd: workdir, model: "configured" },
|
||||
undefined,
|
||||
{ workspaceId: undefined },
|
||||
);
|
||||
return { manager, id: snapshot.id };
|
||||
}
|
||||
|
||||
// The session runs a listed model other than the configured one: its options are the ones shown.
|
||||
const listed = await agentRunning("runtime");
|
||||
await expect(listed.manager.setAgentThinkingOption(listed.id, "configured-only")).rejects.toThrow(
|
||||
"Thinking option 'configured-only' is not available",
|
||||
);
|
||||
await listed.manager.setAgentThinkingOption(listed.id, "runtime-only");
|
||||
|
||||
// The session reports a model the catalog does not list: the picker falls back to the configured one.
|
||||
const unlisted = await agentRunning("unlisted");
|
||||
await unlisted.manager.setAgentThinkingOption(unlisted.id, "configured-only");
|
||||
await expect(
|
||||
unlisted.manager.setAgentThinkingOption(unlisted.id, "runtime-only"),
|
||||
).rejects.toThrow("Thinking option 'runtime-only' is not available");
|
||||
});
|
||||
|
||||
test("setAgentThinkingOption adopts the session's effective clamped level", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-thinking-clamp-"));
|
||||
class ClampingSession extends TestAgentSession {
|
||||
|
||||
@@ -2302,6 +2302,35 @@ export class AgentManager {
|
||||
if (issues.length > 0) throw new Error(issues.map((issue) => issue.message).join("; "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the option against the model the agent runs, chosen as the model picker chooses it: the
|
||||
* model the session reports when the provider lists it, otherwise the configured model, otherwise
|
||||
* the provider's default.
|
||||
*/
|
||||
private async assertThinkingOptionListed(
|
||||
agent: ActiveManagedAgent,
|
||||
thinkingOptionId: string,
|
||||
): Promise<void> {
|
||||
const reported = agent.runtimeInfo?.model;
|
||||
const candidates = [reported, agent.config.model].filter(
|
||||
(model): model is string => typeof model === "string" && model.length > 0,
|
||||
);
|
||||
for (const model of candidates) {
|
||||
const issues =
|
||||
(await this.validateAgentSelection?.({
|
||||
provider: agent.provider,
|
||||
model,
|
||||
thinkingOptionId,
|
||||
})) ?? [];
|
||||
const modelListed = !issues.some((issue) => issue.path[0] === "model");
|
||||
if (modelListed) {
|
||||
if (issues.length > 0) throw new Error(issues.map((issue) => issue.message).join("; "));
|
||||
return;
|
||||
}
|
||||
}
|
||||
await this.assertSelectionListed({ provider: agent.provider, thinkingOptionId });
|
||||
}
|
||||
|
||||
async setAgentModel(agentId: string, modelId: string | null): Promise<void> {
|
||||
const agent = await this.ensureInteractiveSession(agentId);
|
||||
const normalizedModelId =
|
||||
@@ -2337,11 +2366,7 @@ export class AgentManager {
|
||||
? thinkingOptionId
|
||||
: null;
|
||||
if (normalizedThinkingOptionId) {
|
||||
await this.assertSelectionListed({
|
||||
provider: agent.provider,
|
||||
model: agent.config.model,
|
||||
thinkingOptionId: normalizedThinkingOptionId,
|
||||
});
|
||||
await this.assertThinkingOptionListed(agent, normalizedThinkingOptionId);
|
||||
}
|
||||
|
||||
let notice: AgentProviderNotice | null = null;
|
||||
|
||||
Reference in New Issue
Block a user