mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
151 lines
5.2 KiB
TypeScript
151 lines
5.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { resolveAgentInvocationConfig, resolveJoinMode } from "../src/invocation-config.js";
|
|
import type { AgentConfig } from "../src/types.js";
|
|
|
|
function makeConfig(overrides: Partial<AgentConfig> = {}): AgentConfig {
|
|
return {
|
|
name: "Explore",
|
|
description: "Explore",
|
|
builtinToolNames: ["read"],
|
|
extensions: false,
|
|
skills: false,
|
|
systemPrompt: "Test agent",
|
|
promptMode: "replace",
|
|
inheritContext: false,
|
|
runInBackground: false,
|
|
isolated: false,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("resolveAgentInvocationConfig", () => {
|
|
it("prefers agent config over tool-call params for locked fields", () => {
|
|
const resolved = resolveAgentInvocationConfig(
|
|
makeConfig({
|
|
model: "provider/config-model",
|
|
thinking: "high",
|
|
maxTurns: 42,
|
|
inheritContext: false,
|
|
runInBackground: false,
|
|
isolated: false,
|
|
isolation: "worktree",
|
|
}),
|
|
{
|
|
model: "provider/param-model",
|
|
thinking: "minimal",
|
|
max_turns: 1,
|
|
inherit_context: true,
|
|
run_in_background: true,
|
|
isolated: true,
|
|
isolation: "worktree",
|
|
},
|
|
);
|
|
|
|
expect(resolved.modelInput).toBe("provider/config-model");
|
|
expect(resolved.modelFromParams).toBe(false);
|
|
expect(resolved.thinking).toBe("high");
|
|
expect(resolved.maxTurns).toBe(42);
|
|
expect(resolved.inheritContext).toBe(false);
|
|
expect(resolved.runInBackground).toBe(false);
|
|
expect(resolved.isolated).toBe(false);
|
|
expect(resolved.isolation).toBe("worktree");
|
|
});
|
|
|
|
it("uses tool-call params when no agent config is available", () => {
|
|
const resolved = resolveAgentInvocationConfig(undefined, {
|
|
model: "provider/param-model",
|
|
thinking: "minimal",
|
|
max_turns: 3,
|
|
inherit_context: true,
|
|
run_in_background: true,
|
|
isolated: true,
|
|
isolation: "worktree",
|
|
});
|
|
|
|
expect(resolved.modelInput).toBe("provider/param-model");
|
|
expect(resolved.modelFromParams).toBe(true);
|
|
expect(resolved.thinking).toBe("minimal");
|
|
expect(resolved.maxTurns).toBe(3);
|
|
expect(resolved.inheritContext).toBe(true);
|
|
expect(resolved.runInBackground).toBe(true);
|
|
expect(resolved.isolated).toBe(true);
|
|
expect(resolved.isolation).toBe("worktree");
|
|
});
|
|
|
|
it("lets parent fill in booleans when config leaves them undefined", () => {
|
|
const resolved = resolveAgentInvocationConfig(
|
|
makeConfig({
|
|
inheritContext: undefined,
|
|
runInBackground: undefined,
|
|
isolated: undefined,
|
|
}),
|
|
{
|
|
inherit_context: true,
|
|
run_in_background: true,
|
|
isolated: true,
|
|
},
|
|
);
|
|
|
|
expect(resolved.inheritContext).toBe(true);
|
|
expect(resolved.runInBackground).toBe(true);
|
|
expect(resolved.isolated).toBe(true);
|
|
});
|
|
|
|
it("defaults booleans to false when neither config nor params set them", () => {
|
|
const resolved = resolveAgentInvocationConfig(
|
|
makeConfig({
|
|
inheritContext: undefined,
|
|
runInBackground: undefined,
|
|
isolated: undefined,
|
|
}),
|
|
{},
|
|
);
|
|
|
|
expect(resolved.inheritContext).toBe(false);
|
|
expect(resolved.runInBackground).toBe(false);
|
|
expect(resolved.isolated).toBe(false);
|
|
});
|
|
|
|
// "off" exists so a model that cannot bring itself to omit an optional field
|
|
// has a legal way to say no (#231). It is an input spelling only — the
|
|
// resolver collapses it to undefined so no consumer downstream grows a branch.
|
|
it('collapses a param isolation of "off" to undefined', () => {
|
|
const resolved = resolveAgentInvocationConfig(makeConfig({ isolation: undefined }), { isolation: "off" });
|
|
expect(resolved.isolation).toBeUndefined();
|
|
});
|
|
|
|
// Agent config outranks tool-call params, so "off" in frontmatter is the only
|
|
// way to veto a caller's worktree — before #231 no value could do this.
|
|
it('lets a config isolation of "off" veto a param "worktree"', () => {
|
|
const resolved = resolveAgentInvocationConfig(makeConfig({ isolation: "off" }), { isolation: "worktree" });
|
|
expect(resolved.isolation).toBeUndefined();
|
|
});
|
|
|
|
it('still honours a param "worktree" when the config leaves isolation unset', () => {
|
|
const resolved = resolveAgentInvocationConfig(makeConfig({ isolation: undefined }), { isolation: "worktree" });
|
|
expect(resolved.isolation).toBe("worktree");
|
|
});
|
|
|
|
it("drops worktree isolation when the project disallows it", () => {
|
|
const resolved = resolveAgentInvocationConfig(makeConfig({ isolation: "worktree" }), { isolation: "worktree" }, { worktreeAllowed: false });
|
|
expect(resolved.isolation).toBeUndefined();
|
|
});
|
|
|
|
it("keeps worktree isolation when the project allows it", () => {
|
|
const resolved = resolveAgentInvocationConfig(makeConfig({ isolation: "worktree" }), {}, { worktreeAllowed: true });
|
|
expect(resolved.isolation).toBe("worktree");
|
|
});
|
|
});
|
|
|
|
describe("resolveJoinMode", () => {
|
|
it("returns the global default for background agents", () => {
|
|
expect(resolveJoinMode("smart", true)).toBe("smart");
|
|
expect(resolveJoinMode("async", true)).toBe("async");
|
|
});
|
|
|
|
it("ignores join mode for foreground agents", () => {
|
|
expect(resolveJoinMode("smart", false)).toBeUndefined();
|
|
expect(resolveJoinMode("group", false)).toBeUndefined();
|
|
});
|
|
});
|