Refactor runtime around isolated bot instances

This commit is contained in:
zenord
2026-08-16 23:45:09 +08:00
parent 914579a94c
commit f9fb4ef776
51 changed files with 2355 additions and 1459 deletions
+12 -11
View File
@@ -1,8 +1,9 @@
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
import type { AcpConfig } from "../config.js";
import type { ResolvedRole } from "../roles/role-registry.js";
import type { ResolvedBot } from "../roles/role-registry.js";
import { AcpClient } from "./client.js";
import type { AcpBackendSpec } from "./types.js";
export class AcpSessionRestoreError extends Error {}
export class AcpWorker {
private child?: ChildProcessWithoutNullStreams;
@@ -16,16 +17,15 @@ export class AcpWorker {
inFlight = false;
constructor(
readonly backend: AcpBackendSpec,
readonly role: ResolvedRole,
readonly bot: ResolvedBot,
private readonly config: AcpConfig,
private readonly onCrash: (worker: AcpWorker, error: Error) => void
) {}
async start(nativeSessionId?: string): Promise<string> {
this.child = spawn(this.backend.command, this.backend.args, {
cwd: this.role.workspace,
env: { ...process.env, ...this.backend.env },
this.child = spawn(this.bot.agent.command, this.bot.agent.args, {
cwd: this.bot.workspace,
env: { ...process.env, ...this.bot.agent.env },
shell: false,
stdio: ["pipe", "pipe", "pipe"]
});
@@ -35,18 +35,19 @@ export class AcpWorker {
this.exited = true;
if (!this.stopping) this.crashed(new Error(`ACP worker exited (code=${code ?? "null"}, signal=${signal ?? "null"})`));
});
this.client = new AcpClient(this.child, { initializeTimeoutMs: this.config.initializeTimeoutMs, policy: this.role.policy });
this.client = new AcpClient(this.child, { initializeTimeoutMs: this.config.initializeTimeoutMs, policy: this.bot.permissions });
try {
await this.client.initialize();
if (nativeSessionId) {
await this.client.resumeSession(nativeSessionId, this.role.workspace);
await this.client.resumeSession(nativeSessionId, this.bot.workspace);
this.nativeSessionId = nativeSessionId;
} else {
this.nativeSessionId = await this.client.newSession(this.role.workspace);
this.nativeSessionId = await this.client.newSession(this.bot.workspace);
}
return this.nativeSessionId;
} catch (error) {
await this.terminate();
if (nativeSessionId) throw new AcpSessionRestoreError(`Cannot restore ACP session '${nativeSessionId}': ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
@@ -109,7 +110,7 @@ export class AcpWorker {
if (!remaining) return;
const text = chunk.subarray(0, remaining).toString("utf8").trimEnd();
this.stderrBytes += Buffer.byteLength(text);
if (text) console.error(`[acp:${this.backend.id}] ${text}`);
if (text) console.error(`[acp:${this.bot.agent.id}] ${text}`);
}
private crashed(error: Error): void {