diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index f4793d1a7987..fc4f7e86578c 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1484,6 +1484,8 @@ NOTE: At any point in time through this workflow you should feel free to ask the env: { ...process.env, TERM: "dumb", + OPENCODE_SESSION_ID: input.sessionID, + OPENCODE_SESSION_TITLE: session.title, }, }) diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index f3a1b04d4310..a7204ae52a30 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -16,6 +16,7 @@ import { Shell } from "@/shell/shell" import { BashArity } from "@/permission/arity" import { Truncate } from "./truncation" +import { Session } from "@/session" const MAX_METADATA_LENGTH = 30_000 const DEFAULT_TIMEOUT = Flag.OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS || 2 * 60 * 1000 @@ -154,11 +155,20 @@ export const BashTool = Tool.define("bash", async () => { }) } + const session = await (async () => { + try { + return await Session.get(ctx.sessionID) + } catch { + return undefined + } + })() const proc = spawn(params.command, { shell, cwd, env: { ...process.env, + OPENCODE_SESSION_ID: ctx.sessionID, + ...(session?.title && { OPENCODE_SESSION_TITLE: session.title }), }, stdio: ["ignore", "pipe", "pipe"], detached: process.platform !== "win32", diff --git a/packages/opencode/test/tool/bash.test.ts b/packages/opencode/test/tool/bash.test.ts index 750ff8193e9d..a8a5a4624b6d 100644 --- a/packages/opencode/test/tool/bash.test.ts +++ b/packages/opencode/test/tool/bash.test.ts @@ -5,6 +5,7 @@ import { Instance } from "../../src/project/instance" import { tmpdir } from "../fixture/fixture" import type { PermissionNext } from "../../src/permission/next" import { Truncate } from "../../src/tool/truncation" +import { Session } from "../../src/session" const ctx = { sessionID: "test", @@ -36,6 +37,54 @@ describe("tool.bash", () => { }, }) }) + + test("exposes OPENCODE_SESSION_ID environment variable", async () => { + await Instance.provide({ + directory: projectRoot, + fn: async () => { + const bash = await BashTool.init() + const sessionID = "session_abc123" + const testCtx = { + ...ctx, + sessionID, + } + const result = await bash.execute( + { + command: process.platform === "win32" ? "echo %OPENCODE_SESSION_ID%" : "echo $OPENCODE_SESSION_ID", + description: "Print session ID env var", + }, + testCtx, + ) + expect(result.metadata.exit).toBe(0) + expect(result.metadata.output.trim()).toBe(sessionID) + }, + }) + }) + + test("exposes OPENCODE_SESSION_TITLE environment variable", async () => { + await Instance.provide({ + directory: projectRoot, + fn: async () => { + const title = "My Custom Session Title" + const session = await Session.create({ title }) + const bash = await BashTool.init() + const testCtx = { + ...ctx, + sessionID: session.id, + } + const result = await bash.execute( + { + command: process.platform === "win32" ? "echo %OPENCODE_SESSION_TITLE%" : "echo $OPENCODE_SESSION_TITLE", + description: "Print session title env var", + }, + testCtx, + ) + expect(result.metadata.exit).toBe(0) + expect(result.metadata.output.trim()).toBe(title) + await Session.remove(session.id) + }, + }) + }) }) describe("tool.bash permissions", () => {