fix(coding-agent): pair checkpoint/rewind for restricted sessions too
The !restrictToolNames guard on the pairing blocks was wrong: a restricted session with tools:[checkpoint] passes isToolAllowed (requestedTools is defined) but the pairing is skipped, stranding the agent without rewind. Remove the guard — this is a safety pairing, not a convenience widening. Added restricted-session tests in both createTools and SDK active-set paths.
This commit is contained in:
@@ -2796,8 +2796,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
// Checkpoint and rewind are a pair: `createTools` auto-includes the sister
|
||||
// tool in the registry, but an explicit `toolNames` list would otherwise
|
||||
// drop it from the ACTIVE set — leaving the agent able to checkpoint but
|
||||
// unable to rewind (or vice versa). Mirror the pairing here.
|
||||
if (!restrictToolNames && explicitlyRequestedToolNames) {
|
||||
// unable to rewind (or vice versa). Mirror the pairing here. Unlike the
|
||||
// manage_skill/learn mirror above, this is a safety pairing — it applies
|
||||
// to restricted sessions too.
|
||||
if (explicitlyRequestedToolNames) {
|
||||
if (builtInToolNames.includes("checkpoint") && !explicitlyRequestedToolNames.includes("rewind")) {
|
||||
explicitlyRequestedToolNames.push("rewind");
|
||||
} else if (builtInToolNames.includes("rewind") && !explicitlyRequestedToolNames.includes("checkpoint")) {
|
||||
|
||||
@@ -505,8 +505,9 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
||||
// Checkpoint and rewind are a pair: listing one without the other strands
|
||||
// the agent (it can checkpoint but not rewind, or vice versa). Auto-include
|
||||
// the sister tool so a one-sided frontmatter `tools:` entry still works.
|
||||
// Like the AST/auto-learn siblings below, restricted callers own the list.
|
||||
if (requestedTools && !restrictToolNames && session.settings.get("checkpoint.enabled")) {
|
||||
// Unlike the AST/auto-learn convenience auto-includes below, this is a
|
||||
// safety pairing — it applies to restricted sessions too.
|
||||
if (requestedTools && session.settings.get("checkpoint.enabled")) {
|
||||
if (requestedTools.includes("checkpoint") && !requestedTools.includes("rewind")) {
|
||||
requestedTools.push("rewind");
|
||||
} else if (requestedTools.includes("rewind") && !requestedTools.includes("checkpoint")) {
|
||||
|
||||
@@ -122,4 +122,23 @@ describe("createAgentSession auto-learn tool activation", () => {
|
||||
expect(names).toContain("checkpoint");
|
||||
expect(names).toContain("rewind");
|
||||
});
|
||||
|
||||
it("activates checkpoint and rewind in a restricted session with one-sided toolNames", async () => {
|
||||
const { session } = await createAgentSession({
|
||||
cwd: registryDir,
|
||||
agentDir: registryDir,
|
||||
modelRegistry,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
settings: Settings.isolated({ "checkpoint.enabled": true }),
|
||||
model: getBundledModel("openai", "gpt-4o-mini"),
|
||||
disableExtensionDiscovery: true,
|
||||
toolNames: ["checkpoint"],
|
||||
requireYieldTool: true,
|
||||
restrictToolNames: true,
|
||||
});
|
||||
sessions.push(session);
|
||||
const names = session.getActiveToolNames();
|
||||
expect(names).toContain("checkpoint");
|
||||
expect(names).toContain("rewind");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -397,6 +397,21 @@ describe("createTools", () => {
|
||||
expect(names).not.toContain("rewind");
|
||||
});
|
||||
|
||||
it("auto-pairs checkpoint/rewind in a restricted subagent with one-sided list", async () => {
|
||||
const names = (
|
||||
await createTools(
|
||||
createTestSession({
|
||||
taskDepth: 1,
|
||||
restrictToolNames: true,
|
||||
settings: createSettingsWithOverrides({ "checkpoint.enabled": true }),
|
||||
}),
|
||||
["checkpoint"],
|
||||
)
|
||||
).map(t => t.name);
|
||||
expect(names).toContain("checkpoint");
|
||||
expect(names).toContain("rewind");
|
||||
});
|
||||
|
||||
it("HIDDEN_TOOLS contains yield and goal", () => {
|
||||
expect(Object.keys(HIDDEN_TOOLS).sort()).toEqual(["goal", "yield"]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user