feat(coding-agent): expose session metadata to bash tools (#6967)
This commit is contained in:
@@ -694,6 +694,10 @@ export class ExtensionRunner {
|
||||
runner.assertActive();
|
||||
return getModel();
|
||||
},
|
||||
get thinkingLevel() {
|
||||
runner.assertActive();
|
||||
return runner.runtime.getThinkingLevel();
|
||||
},
|
||||
isIdle: () => {
|
||||
runner.assertActive();
|
||||
return runner.isIdleFn();
|
||||
|
||||
@@ -317,6 +317,8 @@ export interface ExtensionContext {
|
||||
modelRegistry: ModelRegistry;
|
||||
/** Current model (may be undefined) */
|
||||
model: Model<any> | undefined;
|
||||
/** Current thinking level, when provided by the session runtime. */
|
||||
thinkingLevel?: ThinkingLevel;
|
||||
/** Whether the agent is idle (not streaming) */
|
||||
isIdle(): boolean;
|
||||
/** Whether project-local trust is active for this context. */
|
||||
|
||||
@@ -133,7 +133,7 @@ Pi documentation (read only when the user asks about pi itself, its SDK, extensi
|
||||
- Additional docs: ${docsPath}
|
||||
- Examples: ${examplesPath} (extensions, custom tools, SDK)
|
||||
- When reading pi docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory
|
||||
- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)
|
||||
- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md), environment variables (docs/environment-variables.md)
|
||||
- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing
|
||||
- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
trackDetachedChildPid,
|
||||
untrackDetachedChildPid,
|
||||
} from "../../utils/shell.ts";
|
||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
||||
import type { ExtensionContext, ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
||||
import { OutputAccumulator } from "./output-accumulator.ts";
|
||||
import { getTextOutput, invalidArgText, str } from "./render-utils.ts";
|
||||
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
||||
@@ -155,8 +155,31 @@ export interface BashSpawnContext {
|
||||
|
||||
export type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;
|
||||
|
||||
function resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {
|
||||
const baseContext: BashSpawnContext = { command, cwd, env: { ...getShellEnv() } };
|
||||
function resolveSpawnContext(
|
||||
command: string,
|
||||
cwd: string,
|
||||
spawnHook: BashSpawnHook | undefined,
|
||||
exposeSessionEnvironment: boolean,
|
||||
ctx: ExtensionContext | undefined,
|
||||
): BashSpawnContext {
|
||||
const env = { ...getShellEnv() };
|
||||
delete env.PI_SESSION_ID;
|
||||
delete env.PI_SESSION_FILE;
|
||||
delete env.PI_PROVIDER;
|
||||
delete env.PI_MODEL;
|
||||
delete env.PI_REASONING_LEVEL;
|
||||
if (exposeSessionEnvironment && ctx) {
|
||||
const model = ctx.model;
|
||||
env.PI_SESSION_ID = ctx.sessionManager.getSessionId();
|
||||
const sessionFile = ctx.sessionManager.getSessionFile();
|
||||
if (sessionFile) env.PI_SESSION_FILE = sessionFile;
|
||||
if (model) {
|
||||
env.PI_PROVIDER = model.provider;
|
||||
env.PI_MODEL = model.id;
|
||||
}
|
||||
if (ctx.thinkingLevel) env.PI_REASONING_LEVEL = ctx.thinkingLevel;
|
||||
}
|
||||
const baseContext: BashSpawnContext = { command, cwd, env };
|
||||
return spawnHook ? spawnHook(baseContext) : baseContext;
|
||||
}
|
||||
|
||||
@@ -167,6 +190,8 @@ export interface BashToolOptions {
|
||||
commandPrefix?: string;
|
||||
/** Optional explicit shell path from settings */
|
||||
shellPath?: string;
|
||||
/** Expose current Pi session metadata as PI_* environment variables. Default: true */
|
||||
exposeSessionEnvironment?: boolean;
|
||||
/** Hook to adjust command, cwd, or env before execution */
|
||||
spawnHook?: BashSpawnHook;
|
||||
}
|
||||
@@ -294,22 +319,26 @@ export function createBashToolDefinition(
|
||||
): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {
|
||||
const ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });
|
||||
const commandPrefix = options?.commandPrefix;
|
||||
const exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;
|
||||
const spawnHook = options?.spawnHook;
|
||||
return {
|
||||
name: "bash",
|
||||
label: "bash",
|
||||
description: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
|
||||
promptSnippet: "Execute bash commands (ls, grep, find, etc.)",
|
||||
promptGuidelines: exposeSessionEnvironment
|
||||
? ["Inspect PI_* environment variables for current model and session details."]
|
||||
: undefined,
|
||||
parameters: bashSchema,
|
||||
async execute(
|
||||
_toolCallId,
|
||||
{ command, timeout }: { command: string; timeout?: number },
|
||||
signal?: AbortSignal,
|
||||
onUpdate?,
|
||||
_ctx?,
|
||||
ctx?,
|
||||
) {
|
||||
const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
|
||||
const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);
|
||||
const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);
|
||||
const output = new OutputAccumulator({ tempFilePrefix: "pi-bash" });
|
||||
let acceptingOutput = true;
|
||||
let updateTimer: NodeJS.Timeout | undefined;
|
||||
@@ -466,5 +495,11 @@ export function createBashToolDefinition(
|
||||
}
|
||||
|
||||
export function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {
|
||||
return wrapToolDefinition(createBashToolDefinition(cwd, options));
|
||||
const definition = createBashToolDefinition(cwd, options);
|
||||
const tool = wrapToolDefinition(definition);
|
||||
Object.assign(tool, {
|
||||
promptSnippet: definition.promptSnippet,
|
||||
promptGuidelines: definition.promptGuidelines,
|
||||
});
|
||||
return tool;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ export function wrapToolDefinition<TDetails = unknown>(
|
||||
parameters: definition.parameters,
|
||||
prepareArguments: definition.prepareArguments,
|
||||
executionMode: definition.executionMode,
|
||||
execute: (toolCallId, params, signal, onUpdate) =>
|
||||
definition.execute(toolCallId, params, signal, onUpdate, ctxFactory?.() as ExtensionContext),
|
||||
execute: (toolCallId, params, signal, onUpdate, ctx?: ExtensionContext) =>
|
||||
definition.execute(toolCallId, params, signal, onUpdate, ctx ?? (ctxFactory?.() as ExtensionContext)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1783,6 +1783,7 @@ export class InteractiveMode {
|
||||
sessionManager: this.sessionManager,
|
||||
modelRegistry: extensionRunner.getModelRegistry(),
|
||||
model: this.session.model,
|
||||
thinkingLevel: this.session.thinkingLevel,
|
||||
isIdle: () => this.session.isIdle,
|
||||
isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
|
||||
signal: this.session.agent.signal,
|
||||
|
||||
Reference in New Issue
Block a user