fix: use current selected model
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mikefreno/pygienium",
|
"name": "@mikefreno/pygienium",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"description": "Code hygiene extension for pi — isolated sub-agent checks that scan a target, apply fixes, and emit a findings+changes report.",
|
"description": "Code hygiene extension for pi — isolated sub-agent checks that scan a target, apply fixes, and emit a findings+changes report.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"pi-package",
|
"pi-package",
|
||||||
|
|||||||
@@ -256,9 +256,9 @@ const FILE_RULES = {
|
|||||||
{ from: '\t\tagent.allowedTools ?? ["read", "bash", "grep", "find"];', to: '\t\tagent.allowedTools ?? ["read", "bash", "grep", "glob"];', label: "default tools" },
|
{ from: '\t\tagent.allowedTools ?? ["read", "bash", "grep", "find"];', to: '\t\tagent.allowedTools ?? ["read", "bash", "grep", "glob"];', label: "default tools" },
|
||||||
{
|
{
|
||||||
from:
|
from:
|
||||||
"\tconst { session } = await createAgentSession({\n\t\tcwd: opts.cwd,\n\t\ttools,\n\t\tsessionManager: SessionManager.inMemory(opts.cwd),\n\t\tresourceLoader: loader,\n\t});",
|
"\tconst { session } = await createAgentSession({\n\t\tcwd: opts.cwd,\n\t\t// Pin the sub-agent to the model the user has selected in the invoking\n\t\t// session rather than the settings default. Omitted when the caller\n\t\t// has no live session model (print/RPC modes), which keeps the\n\t\t// settings-default fallback.\n\t\t...(opts.model ? { model: opts.model } : {}),\n\t\ttools,\n\t\tsessionManager: SessionManager.inMemory(opts.cwd),\n\t\tresourceLoader: loader,\n\t});",
|
||||||
to:
|
to:
|
||||||
"\tconst { session } = await createAgentSession({\n\t\tcwd: opts.cwd,\n\t\ttoolNames: tools,\n\t\t// `tools` is an allowlist, not a request list.\n\t\trestrictToolNames: true,\n\t\tsessionManager: SessionManager.inMemory(opts.cwd),\n\t\t// Replace the fully rendered default prompt with the agent body.\n\t\tsystemPrompt: agent.systemPrompt,\n\t\t// Keep the sub-agent isolated: no nested extensions/skills/prompts/etc.\n\t\tdisableExtensionDiscovery: true,\n\t\tskills: [],\n\t\tpromptTemplates: [],\n\t\trules: [],\n\t\tcontextFiles: [],\n\t\tenableMCP: false,\n\t\tenableLsp: false,\n\t\t// Private registry: the host session owns the process-global \"Main\"\n\t\t// identity, so a per-run registry keeps these in-process workers\n\t\t// disjoint from the main agent.\n\t\tagentRegistry: new AgentRegistry(),\n\t});",
|
"\tconst { session } = await createAgentSession({\n\t\tcwd: opts.cwd,\n\t\t// Pin the sub-agent to the model the user has selected in the invoking\n\t\t// session rather than the settings default.\n\t\t...(opts.model ? { model: opts.model } : {}),\n\t\ttoolNames: tools,\n\t\t// `tools` is an allowlist, not a request list.\n\t\trestrictToolNames: true,\n\t\tsessionManager: SessionManager.inMemory(opts.cwd),\n\t\t// Replace the fully rendered default prompt with the agent body.\n\t\tsystemPrompt: agent.systemPrompt,\n\t\t// Keep the sub-agent isolated: no nested extensions/skills/prompts/etc.\n\t\tdisableExtensionDiscovery: true,\n\t\tskills: [],\n\t\tpromptTemplates: [],\n\t\trules: [],\n\t\tcontextFiles: [],\n\t\tenableMCP: false,\n\t\tenableLsp: false,\n\t\t// Private registry: the host session owns the process-global \"Main\"\n\t\t// identity, so a per-run registry keeps these in-process workers\n\t\t// disjoint from the main agent.\n\t\tagentRegistry: new AgentRegistry(),\n\t});",
|
||||||
label: "createAgentSession",
|
label: "createAgentSession",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { dirname, isAbsolute, join } from "node:path";
|
|||||||
import type {
|
import type {
|
||||||
AgentSession,
|
AgentSession,
|
||||||
AgentSessionEvent,
|
AgentSessionEvent,
|
||||||
|
ExtensionCommandContext,
|
||||||
} from "@earendil-works/pi-coding-agent";
|
} from "@earendil-works/pi-coding-agent";
|
||||||
import { loadAgents, extensionRoot, type AgentDef } from "./agents.js";
|
import { loadAgents, extensionRoot, type AgentDef } from "./agents.js";
|
||||||
|
|
||||||
@@ -52,6 +53,11 @@ export interface AgentTaskOptions {
|
|||||||
task: string;
|
task: string;
|
||||||
/** Optional tool allowlist override (else uses the agent's `allowedTools`). */
|
/** Optional tool allowlist override (else uses the agent's `allowedTools`). */
|
||||||
allowedTools?: string[];
|
allowedTools?: string[];
|
||||||
|
/**
|
||||||
|
* The currently selected model from the invoking session. When omitted,
|
||||||
|
* `createAgentSession` falls back to the settings default model.
|
||||||
|
*/
|
||||||
|
model?: ExtensionCommandContext["model"];
|
||||||
/** Optional explicit agent definition (skips `loadAgents`). */
|
/** Optional explicit agent definition (skips `loadAgents`). */
|
||||||
agent?: AgentDef;
|
agent?: AgentDef;
|
||||||
/**
|
/**
|
||||||
@@ -208,6 +214,11 @@ export async function defaultAgentRunner(
|
|||||||
|
|
||||||
const { session } = await createAgentSession({
|
const { session } = await createAgentSession({
|
||||||
cwd: opts.cwd,
|
cwd: opts.cwd,
|
||||||
|
// Pin the sub-agent to the model the user has selected in the invoking
|
||||||
|
// session rather than the settings default. Omitted when the caller
|
||||||
|
// has no live session model (print/RPC modes), which keeps the
|
||||||
|
// settings-default fallback.
|
||||||
|
...(opts.model ? { model: opts.model } : {}),
|
||||||
tools,
|
tools,
|
||||||
sessionManager: SessionManager.inMemory(opts.cwd),
|
sessionManager: SessionManager.inMemory(opts.cwd),
|
||||||
resourceLoader: loader,
|
resourceLoader: loader,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import type { SendChatMessage } from "./phases.js";
|
|||||||
/** Narrow context slice handlers need (a subset of `ExtensionCommandContext`). */
|
/** Narrow context slice handlers need (a subset of `ExtensionCommandContext`). */
|
||||||
export type PygieniumCtx = Pick<
|
export type PygieniumCtx = Pick<
|
||||||
ExtensionCommandContext,
|
ExtensionCommandContext,
|
||||||
"cwd" | "mode" | "hasUI" | "ui"
|
"cwd" | "mode" | "hasUI" | "ui" | "model"
|
||||||
> & {
|
> & {
|
||||||
/** Optional callback to post messages to the chat window. */
|
/** Optional callback to post messages to the chat window. */
|
||||||
sendChatMessage?: SendChatMessage;
|
sendChatMessage?: SendChatMessage;
|
||||||
@@ -176,6 +176,7 @@ export async function handleCheckCommand(
|
|||||||
existingState: existing,
|
existingState: existing,
|
||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
|
model: ctx.model,
|
||||||
sendChatMessage: ctx.sendChatMessage,
|
sendChatMessage: ctx.sendChatMessage,
|
||||||
onAgentEvent: ctx.onAgentEvent,
|
onAgentEvent: ctx.onAgentEvent,
|
||||||
sendPhaseLine: ctx.sendPhaseLine,
|
sendPhaseLine: ctx.sendPhaseLine,
|
||||||
@@ -215,6 +216,7 @@ export async function handleAllCommand(
|
|||||||
only: parsed.only,
|
only: parsed.only,
|
||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
|
model: ctx.model,
|
||||||
sendChatMessage: ctx.sendChatMessage,
|
sendChatMessage: ctx.sendChatMessage,
|
||||||
onAgentEvent: ctx.onAgentEvent,
|
onAgentEvent: ctx.onAgentEvent,
|
||||||
sendPhaseLine: ctx.sendPhaseLine,
|
sendPhaseLine: ctx.sendPhaseLine,
|
||||||
@@ -303,6 +305,7 @@ export async function handleResumeCommand(
|
|||||||
scope: { cwd, target: cwd, fix: entry.fix, rest: [] },
|
scope: { cwd, target: cwd, fix: entry.fix, rest: [] },
|
||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
|
model: ctx.model,
|
||||||
existingState: state,
|
existingState: state,
|
||||||
sendChatMessage: ctx.sendChatMessage,
|
sendChatMessage: ctx.sendChatMessage,
|
||||||
onAgentEvent: ctx.onAgentEvent,
|
onAgentEvent: ctx.onAgentEvent,
|
||||||
|
|||||||
@@ -410,6 +410,7 @@ export default async function pygieniumExtension(
|
|||||||
mode: ctx.mode,
|
mode: ctx.mode,
|
||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
|
model: ctx.model,
|
||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
onAgentEvent,
|
onAgentEvent,
|
||||||
sendPhaseLine,
|
sendPhaseLine,
|
||||||
|
|||||||
@@ -22,10 +22,13 @@
|
|||||||
|
|
||||||
import { mkdir, writeFile } from "node:fs/promises";
|
import { mkdir, writeFile } from "node:fs/promises";
|
||||||
import { dirname, join, resolve } from "node:path";
|
import { dirname, join, resolve } from "node:path";
|
||||||
import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent";
|
import type {
|
||||||
|
AgentSessionEvent,
|
||||||
|
ExtensionCommandContext,
|
||||||
|
ExtensionUIContext,
|
||||||
|
} from "@earendil-works/pi-coding-agent";
|
||||||
import { getAllChecks, type CheckDefinition } from "../checks/registry.js";
|
import { getAllChecks, type CheckDefinition } from "../checks/registry.js";
|
||||||
import { runCheck } from "./check-runner.js";
|
import { runCheck } from "./check-runner.js";
|
||||||
import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
||||||
import { createPhaseStrip, type SendChatMessage } from "../phases.js";
|
import { createPhaseStrip, type SendChatMessage } from "../phases.js";
|
||||||
import { createPipelineFooter, type ItemStatus } from "../footer.js";
|
import { createPipelineFooter, type ItemStatus } from "../footer.js";
|
||||||
import { runRecon } from "../recon.js";
|
import { runRecon } from "../recon.js";
|
||||||
@@ -74,6 +77,12 @@ export interface AllRunOptions {
|
|||||||
ui?: ExtensionUIContext;
|
ui?: ExtensionUIContext;
|
||||||
/** Whether dialog-capable UI is available. */
|
/** Whether dialog-capable UI is available. */
|
||||||
hasUI?: boolean;
|
hasUI?: boolean;
|
||||||
|
/**
|
||||||
|
* The currently selected model from the invoking session; forwarded to
|
||||||
|
* each check's sub-agents so scans run on the model the user picked, not
|
||||||
|
* the settings default.
|
||||||
|
*/
|
||||||
|
model?: ExtensionCommandContext["model"];
|
||||||
/** Optional callback to post completion messages into the chat. */
|
/** Optional callback to post completion messages into the chat. */
|
||||||
sendChatMessage?: SendChatMessage;
|
sendChatMessage?: SendChatMessage;
|
||||||
/** Optional callback forwarding raw sub-agent events to the chat stream. */
|
/** Optional callback forwarding raw sub-agent events to the chat stream. */
|
||||||
@@ -369,6 +378,7 @@ export async function runAllChecks(
|
|||||||
scope: { cwd, target, fix, rest: [] },
|
scope: { cwd, target, fix, rest: [] },
|
||||||
ui: opts.ui,
|
ui: opts.ui,
|
||||||
hasUI,
|
hasUI,
|
||||||
|
model: opts.model,
|
||||||
existingState: state,
|
existingState: state,
|
||||||
sendChatMessage: opts.sendChatMessage,
|
sendChatMessage: opts.sendChatMessage,
|
||||||
onAgentEvent: opts.onAgentEvent,
|
onAgentEvent: opts.onAgentEvent,
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
|
|
||||||
import { rm } from "node:fs/promises";
|
import { rm } from "node:fs/promises";
|
||||||
import { resolve, join } from "node:path";
|
import { resolve, join } from "node:path";
|
||||||
import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent";
|
import type {
|
||||||
|
ExtensionCommandContext,
|
||||||
|
ExtensionUIContext,
|
||||||
|
} from "@earendil-works/pi-coding-agent";
|
||||||
import type { CheckDefinition, CheckScope } from "../checks/registry.js";
|
import type { CheckDefinition, CheckScope } from "../checks/registry.js";
|
||||||
import { runAgentTask } from "../agent-runner.js";
|
import { runAgentTask } from "../agent-runner.js";
|
||||||
import { runRecon } from "../recon.js";
|
import { runRecon } from "../recon.js";
|
||||||
@@ -84,6 +87,12 @@ export interface RunCheckOptions {
|
|||||||
ui?: ExtensionUIContext;
|
ui?: ExtensionUIContext;
|
||||||
/** Whether dialog-capable UI is available. */
|
/** Whether dialog-capable UI is available. */
|
||||||
hasUI?: boolean;
|
hasUI?: boolean;
|
||||||
|
/**
|
||||||
|
* The currently selected model from the invoking session; forwarded to
|
||||||
|
* each sub-agent so scans run on the model the user picked, not the
|
||||||
|
* settings default.
|
||||||
|
*/
|
||||||
|
model?: ExtensionCommandContext["model"];
|
||||||
/** Pre-existing run state to update (for `/pygienium-all` and resume). */
|
/** Pre-existing run state to update (for `/pygienium-all` and resume). */
|
||||||
existingState?: RunState;
|
existingState?: RunState;
|
||||||
/** Optional callback to post completion messages into the chat. */
|
/** Optional callback to post completion messages into the chat. */
|
||||||
@@ -278,6 +287,7 @@ async function runCheckImplInner(
|
|||||||
cwd: scope.target,
|
cwd: scope.target,
|
||||||
agentName: check.agentName,
|
agentName: check.agentName,
|
||||||
task: scanTask,
|
task: scanTask,
|
||||||
|
model: opts.model,
|
||||||
onEvent: forward(PHASE_ANALYSIS),
|
onEvent: forward(PHASE_ANALYSIS),
|
||||||
});
|
});
|
||||||
findings = scanResult.text;
|
findings = scanResult.text;
|
||||||
@@ -310,6 +320,7 @@ async function runCheckImplInner(
|
|||||||
cwd: scope.target,
|
cwd: scope.target,
|
||||||
agentName: check.fixAgentName ?? "fixer",
|
agentName: check.fixAgentName ?? "fixer",
|
||||||
task: fixTask,
|
task: fixTask,
|
||||||
|
model: opts.model,
|
||||||
onEvent: forward(PHASE_FIX),
|
onEvent: forward(PHASE_FIX),
|
||||||
});
|
});
|
||||||
changes = fixResult.text;
|
changes = fixResult.text;
|
||||||
|
|||||||
@@ -110,6 +110,28 @@ describe("check-runner integration", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("forwards the selected model to every sub-agent", async () => {
|
||||||
|
const check = smokeCheck();
|
||||||
|
const selectedModel = {
|
||||||
|
provider: "test-provider",
|
||||||
|
id: "test-model",
|
||||||
|
} as unknown as PygieniumCtx["model"];
|
||||||
|
const seen: unknown[] = [];
|
||||||
|
setAgentRunner(async (opts) => {
|
||||||
|
seen.push(opts.model);
|
||||||
|
return fakeAgentRunner(opts);
|
||||||
|
});
|
||||||
|
await handleCheckCommand(
|
||||||
|
check,
|
||||||
|
"--fix",
|
||||||
|
{ ...stubCtx(cwd), model: selectedModel } as PygieniumCtx,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Analysis + fix phases each dispatch one sub-agent.
|
||||||
|
expect(seen.length).toBe(2);
|
||||||
|
for (const m of seen) expect(m).toBe(selectedModel);
|
||||||
|
});
|
||||||
|
|
||||||
it("persists run-state.json at the expected path", async () => {
|
it("persists run-state.json at the expected path", async () => {
|
||||||
const check = smokeCheck();
|
const check = smokeCheck();
|
||||||
await handleCheckCommand(check, "", stubCtx(cwd));
|
await handleCheckCommand(check, "", stubCtx(cwd));
|
||||||
|
|||||||
Reference in New Issue
Block a user