diff --git a/server/src/services/native-runtime/runner-bridge-tools.test.ts b/server/src/services/native-runtime/runner-bridge-tools.test.ts index d06839777ac..11151ffe319 100644 --- a/server/src/services/native-runtime/runner-bridge-tools.test.ts +++ b/server/src/services/native-runtime/runner-bridge-tools.test.ts @@ -86,6 +86,16 @@ describe("Runner workspace bridge", () => { expect(await readFile(join(directory, "result.txt"), "utf8")).toBe("hello"); }); + it("tells agents that a comment on a running task waits for the assignee's next turn", () => { + // A chat lead routing a requirement change to busy work otherwise reads "without interrupting" + // as "never delivered" and reaches for the assignee's workspace or a duplicate task instead. + const comment = runnerBridgeDefinitions({ workspace: false, skills: false, api: true, mode: "standard" }).find(tool => tool.name === "comment_on_task"); + expect(comment?.description).toContain("existing task's assignee a new or changed requirement"); + expect(comment?.description).toContain("rather than creating a duplicate task or editing that task's workspace files"); + expect(comment?.description).toContain("delivers it at the start of the assignee's next turn on that task"); + expect(comment?.description).toContain("not as already applied"); + }); + it.skipIf(process.platform === "linux")("does not expose or execute commands without descendant containment", async () => { const directory = await root(); expect(workspaceCommandSandboxAvailable()).toBe(false); diff --git a/server/src/services/native-runtime/runner-bridge-tools.ts b/server/src/services/native-runtime/runner-bridge-tools.ts index fab6e460af7..20a079fcf8e 100644 --- a/server/src/services/native-runtime/runner-bridge-tools.ts +++ b/server/src/services/native-runtime/runner-bridge-tools.ts @@ -14,7 +14,7 @@ const specs = [ ["get_identity", "Read your agent identity, responsible person, permissions and currently available tools.", z.object({}).strict(), "read"], ["list_people", "List active company people and their IDs for human assignment; roles identify the owner.", z.object({ after: z.string().optional(), limit: z.number().int().min(1).max(100).default(50) }).strict(), "read"], ["get_task", "Read another task through normal task permissions.", z.object({ taskId: z.uuid() }).strict(), "read"], - ["comment_on_task", "Post an attributed comment on an authorized task, without reopening or interrupting it.", z.object({ taskId: z.uuid(), body: z.string().trim().min(1).max(20000) }).strict(), "mutation"], + ["comment_on_task", "Post an attributed comment on an authorized task, without reopening or interrupting it. Use it to give an existing task's assignee a new or changed requirement, rather than creating a duplicate task or editing that task's workspace files. If the assignee is running on that task now, the comment does not reach the run in progress: Paperclip saves it in the task's queue and delivers it at the start of the assignee's next turn on that task. Report it as saved for that next turn, not as already applied.", z.object({ taskId: z.uuid(), body: z.string().trim().min(1).max(20000) }).strict(), "mutation"], ["list_task_documents", "List documents on an authorized task.", z.object({ taskId: z.uuid() }).strict(), "read"], ["read_task_document", "Read a document on an authorized task.", z.object({ taskId: z.uuid(), key: documentKey }).strict(), "read"], ["write_task_document", "Write a document on an authorized task using its current revision. Permissions and document locks apply.", z.object({ taskId: z.uuid(), key: documentKey, title: z.string().min(1).max(200), body: z.string().max(20000), baseRevisionId: z.uuid().nullable() }).strict(), "mutation"],