mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(codex): deliver task-suggestion tools to Codex-harness runs with calibrated guidance (#121568)
- forward taskSuggestionDeliveryMode through the Codex app-server dynamic tool build (same sibling-harness omission class previously fixed for clientCaps); spawn_task/dismiss_task silently never existed for GPT-backed sessions - regression test asserting the forward, mirroring the existing clientCaps case - spawn_task/dismiss_task descriptions rewritten with usage calibration: when to flag, when not to, scope-vs-flagging, prose-is-lost, operator-phrasing bridge, prompt self-containment, git-checkout cwd, registry ephemerality; param descriptions state where each field renders Release-note context: GPT-backed sessions can now create suggested-task cards (the tools were silently absent on the Codex harness), and the tools carry much clearer model-facing guidance.
This commit is contained in:
committed by
GitHub
parent
572fab0b5b
commit
c3f4d5ee60
@@ -442,6 +442,25 @@ describe("Codex app-server dynamic tool build", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards the task-suggestion delivery mode", async () => {
|
||||
// Regression: spawn_task/dismiss_task silently never existed on the Codex
|
||||
// app-server path because this harness dropped params.taskSuggestionDeliveryMode.
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const params = createParams(path.join(tempDir, "session.jsonl"), workspaceDir);
|
||||
params.disableTools = false;
|
||||
params.runtimePlan = createCodexRuntimePlanFixture();
|
||||
params.taskSuggestionDeliveryMode = "gateway";
|
||||
let receivedOptions: unknown;
|
||||
setOpenClawCodingToolsFactoryForTests((options) => {
|
||||
receivedOptions = options;
|
||||
return [createRuntimeDynamicTool("message")];
|
||||
});
|
||||
|
||||
await buildDynamicToolsForTest(params, workspaceDir);
|
||||
|
||||
expect(receivedOptions).toMatchObject({ taskSuggestionDeliveryMode: "gateway" });
|
||||
});
|
||||
|
||||
it("preserves the host-provided OpenClaw tool through the Codex allowlist", async () => {
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const params = createParams(path.join(tempDir, "session.jsonl"), workspaceDir);
|
||||
|
||||
@@ -328,6 +328,9 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
|
||||
requireExplicitMessageTarget:
|
||||
params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
|
||||
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
||||
// Same sibling-harness rule as clientCaps above: without this forward,
|
||||
// spawn_task/dismiss_task silently never exist for Codex-harness runs.
|
||||
taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
|
||||
disableMessageTool: input.ignoreDisableMessageTool ? false : params.disableMessageTool,
|
||||
forceMessageTool: shouldForceMessageTool(messagePolicyParams),
|
||||
enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
|
||||
|
||||
@@ -17,17 +17,20 @@ const SpawnTaskToolSchema = Type.Object(
|
||||
title: Type.String({
|
||||
minLength: 1,
|
||||
maxLength: 60,
|
||||
description: "Imperative task title under 60 characters.",
|
||||
description:
|
||||
"Imperative task title under 60 characters (start with a verb); shown as the card title and the started session's name.",
|
||||
}),
|
||||
prompt: Type.String({
|
||||
minLength: 1,
|
||||
maxLength: 32_768,
|
||||
description: "Self-contained task prompt with relevant file paths and context.",
|
||||
description:
|
||||
"Self-contained task prompt with file paths and enough context to act without this conversation.",
|
||||
}),
|
||||
tldr: Type.String({
|
||||
minLength: 1,
|
||||
maxLength: 1_024,
|
||||
description: "One or two plain-language sentences explaining the value; no code or paths.",
|
||||
description:
|
||||
"One or two plain-language sentences shown on the card explaining the value; no code or paths.",
|
||||
}),
|
||||
cwd: Type.Optional(
|
||||
Type.String({
|
||||
@@ -74,8 +77,14 @@ export function createTaskSuggestionTools(params: {
|
||||
name: "spawn_task",
|
||||
displaySummary: SPAWN_TASK_TOOL_DISPLAY_SUMMARY,
|
||||
description: [
|
||||
"Suggest confirmed valuable out-of-scope follow-up: dead code, stale docs, missing coverage, verified TODO, security issue.",
|
||||
"Operator suggestion only; does not start work. cwd must be an absolute path inside a git checkout.",
|
||||
"Flag an out-of-scope issue as a separate follow-up task instead of ignoring it, fixing it inline, or only mentioning it in your reply — a follow-up described in prose is lost; recording it here is what surfaces it to the operator.",
|
||||
"This is the tool behind requests like 'flag it as a follow-up', 'note that for later', or 'make a task for that'; whenever you would write 'Follow-up:' in a reply, call this instead.",
|
||||
"Use this whenever work you were not asked to do surfaces along the way: dead code, stale docs, missing coverage, a confirmed TODO, or a security issue spotted in passing.",
|
||||
"Requests to stay scoped or skip cleanup apply to doing the work, not to flagging it: this only records a suggestion card in the operator's UI; nothing runs unless they accept it, and your current turn continues uninterrupted.",
|
||||
"Do not flag vague code-smell observations or low-confidence hunches.",
|
||||
"The prompt must stand alone: the started task sees only that text, never this conversation.",
|
||||
"cwd must be an absolute path inside a git checkout.",
|
||||
"Suggestions are ephemeral; ids do not survive a gateway restart.",
|
||||
].join(" "),
|
||||
parameters: SpawnTaskToolSchema,
|
||||
outputSchema: SpawnTaskOutputSchema,
|
||||
@@ -110,8 +119,11 @@ export function createTaskSuggestionTools(params: {
|
||||
label: "Dismiss Task",
|
||||
name: "dismiss_task",
|
||||
displaySummary: DISMISS_TASK_TOOL_DISPLAY_SUMMARY,
|
||||
description:
|
||||
"Withdraw stale/irrelevant pending spawn_task. Accepted suggestion cannot withdraw.",
|
||||
description: [
|
||||
"Withdraw a pending suggestion card you created with spawn_task when it is now stale, superseded, or already handled in this session.",
|
||||
"To replace a card, call spawn_task with the better suggestion first, then dismiss the old task_id.",
|
||||
"Only cards the operator has not acted on can be withdrawn; accepted ones cannot.",
|
||||
].join(" "),
|
||||
parameters: DismissTaskToolSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
const input = args as Record<string, unknown>;
|
||||
|
||||
Reference in New Issue
Block a user