refactor(coding-agent): replace AgentSessionRuntimeHost with closure-based AgentSessionRuntime

- Replace AgentSessionRuntimeHost and bootstrap abstractions with AgentSessionRuntime
- Runtime creation is now closure-based via CreateAgentSessionRuntimeFactory
- Factory closes over process-global fixed inputs, recreates cwd-bound services per effective cwd
- Session config (model, thinking, tools, scoped models) re-resolved per target cwd
- CLI resource paths resolved once at startup as absolute paths
- Swap lifecycle: teardown old, create next, apply next (hard fail on creation error)
- Unified diagnostics model (info/warning/error) for args, services, session resolution, resources
- No logging or process exits inside creation/parsing logic
- Removed session_directory support
- Removed session_switch and session_fork extension events (use session_start with reason)
- Moved package/config CLI to package-manager-cli.ts
- Fixed theme init for --resume session picker
- Fixed flaky reftable footer test (content-based polling)
- Fixed silent drop of unknown single-dash CLI flags
- Added error diagnostics for missing explicit CLI resource paths
- Updated SDK docs, examples, plans, exports, tests, changelog

fixes #2753
This commit is contained in:
Mario Zechner
2026-04-03 20:14:12 +02:00
parent 042066b982
commit 9f9277ccdd
38 changed files with 2180 additions and 1366 deletions

View File

@@ -12,7 +12,7 @@
*/
import * as crypto from "node:crypto";
import type { AgentSessionRuntimeHost } from "../../core/agent-session-runtime.js";
import type { AgentSessionRuntime } from "../../core/agent-session-runtime.js";
import type {
ExtensionUIContext,
ExtensionUIDialogOptions,
@@ -43,7 +43,7 @@ export type {
* Run in RPC mode.
* Listens for JSON commands on stdin, outputs events and responses on stdout.
*/
export async function runRpcMode(runtimeHost: AgentSessionRuntimeHost): Promise<never> {
export async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<never> {
takeOverStdout();
let session = runtimeHost.session;
let unsubscribe: (() => void) | undefined;
@@ -628,29 +628,49 @@ export async function runRpcMode(runtimeHost: AgentSessionRuntimeHost): Promise<
}
const handleInputLine = async (line: string) => {
let parsed: unknown;
try {
const parsed = JSON.parse(line);
parsed = JSON.parse(line);
} catch (parseError: unknown) {
output(
error(
undefined,
"parse",
`Failed to parse command: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
),
);
return;
}
// Handle extension UI responses
if (parsed.type === "extension_ui_response") {
const response = parsed as RpcExtensionUIResponse;
const pending = pendingExtensionRequests.get(response.id);
if (pending) {
pendingExtensionRequests.delete(response.id);
pending.resolve(response);
}
return;
// Handle extension UI responses
if (
typeof parsed === "object" &&
parsed !== null &&
"type" in parsed &&
parsed.type === "extension_ui_response"
) {
const response = parsed as RpcExtensionUIResponse;
const pending = pendingExtensionRequests.get(response.id);
if (pending) {
pendingExtensionRequests.delete(response.id);
pending.resolve(response);
}
return;
}
// Handle regular commands
const command = parsed as RpcCommand;
const command = parsed as RpcCommand;
try {
const response = await handleCommand(command);
output(response);
// Check for deferred shutdown request (idle between commands)
await checkShutdownRequested();
} catch (e: any) {
output(error(undefined, "parse", `Failed to parse command: ${e.message}`));
} catch (commandError: unknown) {
output(
error(
command.id,
command.type,
commandError instanceof Error ? commandError.message : String(commandError),
),
);
}
};