diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index b353d4f70..1b7ff2852 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -115,6 +115,7 @@ ### Fixed +- Clarified async task and hub guidance: inspecting a settled job consumes its automatic delivery, job IDs expire from process memory after roughly five minutes, and completion does not verify claimed artifacts ([#5869](https://github.com/can1357/oh-my-pi/issues/5869)). - Fixed loading issues for linked legacy extensions importing `DefaultPackageManager` or `linkedom`. - Fixed the advisor retrying terminal, non-retriable provider failures (e.g., blocked prompts), ensuring they fail immediately while transient failures still retry. - Fixed an issue where reassigning the `plan` role model mid-planning did not take effect until the next plan-mode entry; it now applies at the next turn boundary. diff --git a/packages/coding-agent/src/prompts/tools/hub.md b/packages/coding-agent/src/prompts/tools/hub.md index 73be57b32..0d48b70e9 100644 --- a/packages/coding-agent/src/prompts/tools/hub.md +++ b/packages/coding-agent/src/prompts/tools/hub.md @@ -3,7 +3,7 @@ Use `op: "list"` to discover peers. Address peers by exact roster ID — NEVER i # Messaging & Jobs -Background jobs deliver their results automatically the moment they finish. You NEVER need to poll for output — intervene only to block, kill, or inspect. +Background jobs auto-deliver when they finish. You NEVER need to poll; if `jobs`/`wait` observes a settled job first, that snapshot is the delivery and suppresses duplicate `async-result`. - **`send`** (with `to`): fire-and-forget, NEVER blocks. Delivery receipts (`delivered`/`failed`) immediate; `failed` → peer gone, don't retry. Sending wakes `idle`/`parked` peers. Answering: lead with answer, NEVER quote, set `replyTo`. @@ -12,7 +12,9 @@ Background jobs deliver their results automatically the moment they finish. You - Bare `wait` watches every running job AND incoming messages. NEVER pass an array of every running ID; `ids` narrows to specific jobs, `from` to one peer (or use `await: true` on send). - **`inbox`**: drain queued messages without blocking. - **`cancel`**: kill background jobs by `ids` when they have hung, stalled, or are no longer needed. Returns immediately. -- **`jobs`**: status snapshot of every job without waiting. Also names running subagents with no job entry — coordinate with those via `send`. +- **`jobs`**: status snapshot of every job without waiting. A settled row consumes auto-delivery. Also names running subagents with no job entry — coordinate with those via `send`. +- Job rows are process-local and expire roughly five minutes after settlement. Afterward, use the agent ID with `send`, `agent://`, or `history://`. +- `completed` means successful yield/job exit, not artifact acceptance. Verify claimed changes. - NEVER use shell tools, grep, or read other sessions' files to figure out what a peer is doing. Message them directly. - NEVER use hub messaging for something a tool can answer (e.g., grepping codebase, running a build). diff --git a/packages/coding-agent/src/prompts/tools/task-async-contract.md b/packages/coding-agent/src/prompts/tools/task-async-contract.md new file mode 100644 index 000000000..95d6efeae --- /dev/null +++ b/packages/coding-agent/src/prompts/tools/task-async-contract.md @@ -0,0 +1 @@ +No polling is needed. Inspecting a settled job with `hub jobs` or `hub wait` makes that snapshot its delivery, so no duplicate `async-result` follows. Job IDs live in process memory for roughly five minutes after settlement; afterward, use the agent ID with `hub send`, `agent://`, or `history://`. `completed` means the subagent yielded successfully, not that claimed artifacts were verified. diff --git a/packages/coding-agent/src/prompts/tools/task.md b/packages/coding-agent/src/prompts/tools/task.md index 08fed3a8e..f6adb3bab 100644 --- a/packages/coding-agent/src/prompts/tools/task.md +++ b/packages/coding-agent/src/prompts/tools/task.md @@ -1,7 +1,14 @@ {{#if asyncEnabled}}{{#if batchEnabled}}Delegate work to background subagents by passing multiple items in a single `tasks[]` batch. -Execution does not block — you receive IDs immediately; results deliver when subagents finish.{{else}}Delegate work to ONE background subagent per call. -Execution does not block — you receive an ID immediately; the result delivers when the subagent finishes.{{/if}}{{#if hasBlockingAgents}} +Execution does not block — you receive IDs immediately.{{else}}Delegate work to ONE background subagent per call. +Execution does not block — you receive an ID immediately.{{/if}}{{#if hasBlockingAgents}} Agents marked BLOCKING run inline — results return in this call; non-blocking items in the same batch still spawn as background jobs.{{/if}}{{else}}{{#if batchEnabled}}Run subagents synchronously by passing items in a `tasks[]` batch. Execution blocks until all work finishes.{{else}}Run ONE subagent synchronously. Execution blocks until work finishes.{{/if}}{{/if}} +{{#if asyncEnabled}} + +# Async Job Contract +- Results auto-deliver. A settled `hub jobs`/`hub wait` snapshot is the delivery; no duplicate `async-result` follows. +- Job IDs are process-local and expire roughly five minutes after settlement. Afterward, use the agent ID with `hub send`, `agent://`, or `history://`. +- `completed` means successful yield/job exit, not artifact acceptance. Verify claimed changes. +{{/if}} # Task Design - **Agent typing:** Pick each item's `agent` type. Read-only research MUST use `agent: "scout"` (faster model). Use default worker only when no specialist fits. diff --git a/packages/coding-agent/src/task/index.ts b/packages/coding-agent/src/task/index.ts index 89880c747..0e28ad345 100644 --- a/packages/coding-agent/src/task/index.ts +++ b/packages/coding-agent/src/task/index.ts @@ -21,6 +21,7 @@ import type { ToolSession } from ".."; import type { Theme } from "../modes/theme/theme"; import subagentUserPromptTemplate from "../prompts/system/subagent-user-prompt.md" with { type: "text" }; import taskDescriptionTemplate from "../prompts/tools/task.md" with { type: "text" }; +import taskAsyncContractTemplate from "../prompts/tools/task-async-contract.md" with { type: "text" }; import taskSummaryTemplate from "../prompts/tools/task-summary.md" with { type: "text" }; import { truncateForPrompt } from "../tools/approval"; import { isIrcEnabled } from "../tools/hub"; @@ -894,14 +895,16 @@ export class TaskTool implements AgentTool 0 ? ` Failed to schedule ${failedSchedules.length} spawn${failedSchedules.length === 1 ? "" : "s"}: ${failedSchedules.join("; ")}.` : ""; - const coordinationHint = + const coordinationHint = [ started.length === 1 ? ircEnabled ? `DM \`${started[0].agentId}\` via \`hub\` send to coordinate while it runs; use \`hub\` only to inspect (\`jobs\`), wait, or cancel a stuck task.` : `Use \`hub\` to inspect (\`jobs\`), wait, or cancel a stuck task.` : ircEnabled ? `DM these ids via \`hub\` send to coordinate while they run; use \`hub\` only to inspect (\`jobs\`), wait, or cancel a stuck task.` - : `Use \`hub\` to inspect (\`jobs\`), wait, or cancel a stuck task by id.`; + : `Use \`hub\` to inspect (\`jobs\`), wait, or cancel a stuck task by id.`, + taskAsyncContractTemplate.trim(), + ].join("\n"); if (syncSpawns.length === 0) { if (spawns.length === 1) {