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
5 changes: 4 additions & 1 deletion src/lib/agent/runner/harness/pi/orchestrator-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
applyComplete,
applyEnqueue,
applyReadHandoffs,
ENQUEUE_MODEL_DESCRIPTION,
HANDOFF_FIELDS,
REMARK_ASK,
type EnqueueArgs,
Expand Down Expand Up @@ -86,7 +87,9 @@ export function createPiOrchestratorTools(
description: 'Task ids that must be done before this task runs.',
}),
),
model: Type.Optional(Type.String()),
model: Type.Optional(
Type.String({ description: ENQUEUE_MODEL_DESCRIPTION }),
),
reason: Type.String({
description: 'One line on why this task is needed.',
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { z } from 'zod';
import { analytics } from '@utils/analytics';
import { QueueStore } from '@lib/agent/runner/sequence/orchestrator/queue';

Expand All @@ -11,9 +12,15 @@ import {
applyComplete,
applyEnqueue,
applyReadHandoffs,
buildOrchestratorTools,
checkEnqueueGuards,
ENQUEUE_MODEL_DESCRIPTION,
type OrchestratorToolsContext,
} from '@lib/agent/runner/sequence/orchestrator/queue-tools';
import {
isValidModel,
VALID_MODELS,
} from '@lib/agent/runner/switchboard/models';

function tmpDir(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), 'queue-tools-test-'));
Expand Down Expand Up @@ -222,3 +229,35 @@ describe('apply functions', () => {
expect(handoffs[0].did).toBe('installed');
});
});

/**
* The `invalid-model` guard rejects any model outside the allow-list, so an
* agent that cannot see the list has to trip the guard to learn it. The
* description is the only place it can read the list before it picks.
*/
describe('enqueue_task model description', () => {
it('lists exactly the models the guard accepts', () => {
const listed = ENQUEUE_MODEL_DESCRIPTION.split('one of: ')[1]
.replace(/\.$/, '')
.split(', ');
expect(listed.every(isValidModel)).toBe(true);
expect(listed.sort()).toEqual([...VALID_MODELS].sort());
});

it('is carried by the model field of the MCP schema', () => {
const dir = tmpDir();
try {
const schemas: Record<string, z.ZodTypeAny>[] = [];
buildOrchestratorTools(
(_name, _description, schema) => {
schemas.push(schema);
return null;
},
{ store: new QueueStore(dir, 'run-1'), validTypes: VALID },
);
expect(schemas[0].model.description).toBe(ENQUEUE_MODEL_DESCRIPTION);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
});
16 changes: 15 additions & 1 deletion src/lib/agent/runner/sequence/orchestrator/queue-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ import {
type TaskHandoff,
} from './queue';

/**
* The `enqueue_task` `model` description, shared by both harnesses' schemas.
*
* The field was declared as a bare optional string while
* {@link checkEnqueueGuards} rejects anything outside {@link VALID_MODELS}, so
* the allow-list existed only in the rejection message — an agent had to guess
* a gateway model id, trip the `invalid-model` guard, and spend a turn reading
* the list back before it could enqueue. Naming the list where the agent picks
* the value costs nothing and makes the guess unnecessary.
*/
export const ENQUEUE_MODEL_DESCRIPTION = `Optional model override for this task. Omit it to use the task's default, which is almost always right. If you do set it, it must be one of: ${[
...VALID_MODELS,
].join(', ')}.`;

/** The per-task remark ask, shared by both harnesses' complete_task schemas. */
export const REMARK_ASK =
'What information or guidance would have been useful to have in the integration prompt or documentation for this task — specifically anything that would have prevented tool failures, erroneous edits, or other wasted turns.';
Expand Down Expand Up @@ -432,7 +446,7 @@ export function buildOrchestratorTools(
.array(z.string())
.optional()
.describe('Task ids that must be done before this task runs.'),
model: z.string().optional(),
model: z.string().optional().describe(ENQUEUE_MODEL_DESCRIPTION),
reason: z.string().describe('One line on why this task is needed.'),
},
((args: EnqueueArgs) => {
Expand Down
Loading