fix(agent): keep background sandbox tasks running
Auto-approve sandbox Shell only for host-bound task execution, stage native results within their owning session, and preserve active lease state on response stream loss. The repeated failures came from interactive approval in unattended runs and transport cleanup invalidating execution state; focused regressions cover both boundaries.
This commit is contained in:
@@ -8,6 +8,8 @@ import { isTrustedInternalBridgeRequest } from "../product/internal-bridge-auth.
|
||||
import { getRuntimeSessionContext } from "../runtime/sessionContext.js";
|
||||
import { executeSandboxCommand } from "../sandbox/landlockSandbox.js";
|
||||
import { setSandboxOwnership } from "../runtime/conversationWorkspace.js";
|
||||
import type { OpencodeRuntimeAdapter } from "../runtime/opencode.js";
|
||||
import { nativeToolOutputDirectory, stageSessionToolOutputs } from "../sandbox/tool-output.js";
|
||||
|
||||
export const sandboxInput = z.object({
|
||||
session_id: z.string().min(1).max(128),
|
||||
@@ -37,6 +39,7 @@ export function isShellCommandDenied(command: string): boolean {
|
||||
export function registerSandboxToolBridge(app: Express, options: {
|
||||
internalToken: string;
|
||||
runtime: {
|
||||
messages: OpencodeRuntimeAdapter["messages"];
|
||||
ensureSessionWorkspace(sessionId: string): Promise<string>;
|
||||
approveSandboxShell(input: { sessionId: string; command: string; description?: string; callId: string; messageId: string }, signal: AbortSignal): Promise<void>;
|
||||
};
|
||||
@@ -61,6 +64,15 @@ export function registerSandboxToolBridge(app: Express, options: {
|
||||
sessionId: context.sessionId, command: parsed.data.command, description: parsed.data.description,
|
||||
callId: parsed.data.call_id, messageId: parsed.data.message_id,
|
||||
}, controller.signal);
|
||||
if (parsed.data.command.includes(`${nativeToolOutputDirectory()}/`)) {
|
||||
const files = await stageSessionToolOutputs(parsed.data.command, workspace, context.sessionId,
|
||||
await options.runtime.messages(context.sessionId, 100));
|
||||
if (files.length) {
|
||||
res.json({ outcome: "requires_session_path", files,
|
||||
detail: "命令尚未执行。当前会话的完整工具结果已复制到会话目录。请用返回的 shell_path 重新调用 Shell;relative_path 相对于会话 workspace,shell_path 相对于 Shell 的 files/ 工作目录。" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
const result = await executeSandboxCommand(workspace, parsed.data.command, parsed.data.timeout, controller.signal);
|
||||
const files: Partial<Record<"stdout_file" | "stderr_file", string>> = {};
|
||||
for (const stream of ["stdout", "stderr"] as const) {
|
||||
|
||||
Reference in New Issue
Block a user