feat(core): align shell tool environment with agent conventions (#51975)

This commit is contained in:
Aiden Cline
2026-09-28 19:53:53 -05:00
committed by GitHub
parent 35bd8ac442
commit 3740ec311b
2 changed files with 57 additions and 0 deletions
+4
View File
@@ -207,6 +207,10 @@ export const Plugin = {
},
(invocation) =>
Effect.gen(function* () {
invocation.env.AGENT = "1"
invocation.env.OPENCODE = "1"
invocation.env.AI_AGENT ||= "opencode"
invocation.env.OPENCODE_SESSION_ID = context.sessionID
finalTimeout = yield* prepare(invocation, context)
}),
)
+53
View File
@@ -956,6 +956,59 @@ describe("ShellTool", () => {
{ timeout: 15_000 },
)
productionIt.live(
"identifies agent shell commands without inheriting a stale session ID",
() =>
Effect.acquireUseRelease(
Effect.promise(() => tmpdir()),
(tmp) => {
reset()
return withSession(tmp.path, (registry) =>
Effect.gen(function* () {
const sessions = yield* Session.Service
yield* sessions.environment({
sessionID,
variables: { AGENT: "0", OPENCODE: "0", AI_AGENT: "", OPENCODE_SESSION_ID: "stale" },
})
const command = isWindows
? '[Console]::Out.Write("$env:AGENT|$env:OPENCODE|$env:AI_AGENT|$env:OPENCODE_SESSION_ID")'
: 'printf %s "$AGENT|$OPENCODE|$AI_AGENT|$OPENCODE_SESSION_ID"'
const settled = yield* executeTool(registry, call({ command }))
expect(settled.status).toBe("completed")
expect(settled.content?.[0]).toEqual({ type: "text", text: `1|1|opencode|${sessionID}` })
}),
)
},
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
),
{ timeout: 15_000 },
)
productionIt.live(
"preserves an outer AI_AGENT marker in agent shell commands",
() =>
Effect.acquireUseRelease(
Effect.promise(() => tmpdir()),
(tmp) => {
reset()
return withSession(tmp.path, (registry) =>
Effect.gen(function* () {
const sessions = yield* Session.Service
yield* sessions.environment({ sessionID, variables: { AI_AGENT: "outer-agent" } })
const command = isWindows ? '[Console]::Out.Write($env:AI_AGENT)' : 'printf %s "$AI_AGENT"'
const settled = yield* executeTool(registry, call({ command }))
expect(settled.status).toBe("completed")
expect(settled.content?.[0]).toEqual({ type: "text", text: "outer-agent" })
}),
)
},
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
),
{ timeout: 15_000 },
)
it.live("resolves a relative workdir from the active Location", () =>
Effect.acquireUseRelease(
Effect.promise(() => tmpdir()),