Tell replies their envs and call queued work runs

Chat replies now list the envs that reach them (keys and descriptions,
never values), so a bot no longer asks for keys that are set or "checks"
them with show_secret or printenv. request_secret refuses a key that's
already set unless the user asked to replace it, and search_computers
says envs aren't loaded in its shell.

start_task and delegate_task results say "Run #N" and that list_tasks
doesn't show it, since bots sent users to the to-do list for queue jobs.
start_task refuses a second open run in the session on the same
computer unless it's separate work, and says how many runs are ahead on
that computer.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-27 19:33:45 +01:00
co-authored by Claude Opus 5.5
parent f39c824c34
commit 8890d80e6e
13 changed files with 247 additions and 32 deletions
+29
View File
@@ -0,0 +1,29 @@
import { describe, expect, test } from "bun:test";
import composeEnvsNote from "./compose-envs-note";
describe("composeEnvsNote", () => {
test("lists the computer's envs and the tools-only ones apart", () => {
const note = composeEnvsNote({
on_computer: [{ key: "DB_PASSWORD", description: "Production database" }],
tools_only: [{ key: "API_TOKEN", description: "" }],
has_computer: true,
});
expect(note).toContain("- DB_PASSWORD: Production database");
expect(note).toContain("- API_TOKEN");
expect(note).not.toContain("- API_TOKEN:");
expect(note.indexOf("DB_PASSWORD")).toBeLessThan(note.indexOf("Only in tools"));
expect(note).toContain("Don't ask the user for them");
});
test("never mentions values", () => {
const note = composeEnvsNote({ on_computer: [{ key: "K", description: "d" }], tools_only: [], has_computer: true });
expect(note).not.toContain("Only in tools");
expect(note).toContain("you never see values");
});
test("says so when nothing is set", () => {
expect(composeEnvsNote({ on_computer: [], tools_only: [], has_computer: false })).toContain("No envs are set");
});
});
+43
View File
@@ -0,0 +1,43 @@
type Env = { key: string; description: string };
type Params = {
/**
* Set on the bot's current computer for its tasks (`grabReachableEnvs`).
*/
on_computer: Env[];
/**
* Only filled into tools' templates.
*/
tools_only: Env[];
/**
* Whether the bot has a computer, so tasks can use the computer's envs.
*/
has_computer: boolean;
};
/**
* Added to a chat reply so the bot knows which envs it already has (keys
* and descriptions, never values). Without it a reply can't tell, asks the
* user for keys that are set (`request_secret`), or "checks" them with
* `show_secret` or a shell where they aren't loaded.
*/
export default function composeEnvsNote({ on_computer, tools_only, has_computer }: Params) {
const list = (envs: Env[]) =>
envs.map((env) => `- ${env.key}${env.description ? `: ${env.description}` : ""}`).join("\n");
if (!on_computer.length && !tools_only.length) {
return `# Your envs\n\nNo envs are set for you here. If you need a secret, ask for it with request_secret.`;
}
return [
`# Your envs\n\nThese are already set for you here (keys and descriptions; you never see values). Don't ask the user for them, and don't call show_secret or run printenv to check them: this list is the check.`,
on_computer.length
? `On your computer, as $KEY in a task's shell and code (start_task; search_computers doesn't load them):\n${list(on_computer)}`
: "",
tools_only.length
? `${has_computer ? "Only in tools" : "In tools"}, as {{env.KEY}}${has_computer ? " (not set on computers)" : ""}:\n${list(tools_only)}`
: "",
]
.filter(Boolean)
.join("\n\n");
}
+14
View File
@@ -35,6 +35,8 @@ import openBotKnowledgebases from "@/src/harness/tools/knowledgebases/open-bot-k
import grabKnowledgebaseContext from "@/src/harness/tools/knowledgebases/grab-knowledgebase-context";
import botMessagesToHarness from "./bot-messages-to-harness";
import composeComputersNote from "./compose-computers-note";
import composeEnvsNote from "./compose-envs-note";
import grabReachableEnvs from "@/src/functions/backend/db/envs/grab-reachable-envs";
import grabSessionCarryOver from "./grab-session-carry-over";
import continueInNewSessionTool from "@/src/harness/tools/bot/continue-in-new-session-tool";
import searchPastSessionsTool from "@/src/harness/tools/bot/sessions/search-past-sessions-tool";
@@ -145,6 +147,17 @@ export default async function runBotReply({
const prompt = await grabBotPrompt({ bot_id: bot.id, user_id, workspace_id });
const computers = await grabBotComputers({ bot_id: bot.id, workspace_id });
/**
* The envs the reply has (keys only), so it doesn't ask for or test
* ones that are set.
*/
const envs = await grabReachableEnvs({
bot_id: bot.id!,
user_id,
workspace_id,
computer_id: computers[0]?.id || null,
});
/**
* A summary that can't be written now (no model free) is left out
* rather than holding up the reply.
@@ -312,6 +325,7 @@ export default async function runBotReply({
TaskReminderNote,
ActionCheckNote,
SecretsNote,
composeEnvsNote({ ...envs, has_computer: computers.length > 0 }),
GraphicsNote,
workspace_id ? WorkspaceLearningNote : "",
composeComputersNote({