@f5-sales-demo/xcsh 20.17.0 → 20.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -8
- package/src/cli.ts +1 -0
- package/src/commands/herdr.ts +122 -0
- package/src/extensibility/extensions/bundled/herdr-terminal.ts +135 -0
- package/src/extensibility/extensions/loader.ts +2 -0
- package/src/herdr/client.ts +127 -0
- package/src/herdr/controller.ts +285 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/internal-urls/docs-index.generated.ts +2 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "20.
|
|
4
|
+
"version": "20.18.0",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -61,13 +61,13 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
64
|
-
"@f5-sales-demo/pi-agent-core": "20.
|
|
65
|
-
"@f5-sales-demo/pi-ai": "20.
|
|
66
|
-
"@f5-sales-demo/pi-natives": "20.
|
|
67
|
-
"@f5-sales-demo/pi-resource-management": "20.
|
|
68
|
-
"@f5-sales-demo/pi-tui": "20.
|
|
69
|
-
"@f5-sales-demo/pi-utils": "20.
|
|
70
|
-
"@f5-sales-demo/xcsh-stats": "20.
|
|
64
|
+
"@f5-sales-demo/pi-agent-core": "20.18.0",
|
|
65
|
+
"@f5-sales-demo/pi-ai": "20.18.0",
|
|
66
|
+
"@f5-sales-demo/pi-natives": "20.18.0",
|
|
67
|
+
"@f5-sales-demo/pi-resource-management": "20.18.0",
|
|
68
|
+
"@f5-sales-demo/pi-tui": "20.18.0",
|
|
69
|
+
"@f5-sales-demo/pi-utils": "20.18.0",
|
|
70
|
+
"@f5-sales-demo/xcsh-stats": "20.18.0",
|
|
71
71
|
"@mozilla/readability": "^0.6",
|
|
72
72
|
"@sinclair/typebox": "^0.34",
|
|
73
73
|
"@xterm/headless": "^6.0",
|
package/src/cli.ts
CHANGED
|
@@ -63,6 +63,7 @@ const commands: CommandEntry[] = [
|
|
|
63
63
|
{ name: "chrome", load: () => import("./commands/chrome").then(m => m.default) },
|
|
64
64
|
{ name: "chrome-host", load: () => import("./commands/native-host").then(m => m.default) },
|
|
65
65
|
{ name: "grep", load: () => import("./commands/grep").then(m => m.default) },
|
|
66
|
+
{ name: "herdr", load: () => import("./commands/herdr").then(m => m.default) },
|
|
66
67
|
{ name: "grievances", load: () => import("./commands/grievances").then(m => m.default) },
|
|
67
68
|
{ name: "read", load: () => import("./commands/read").then(m => m.default) },
|
|
68
69
|
{
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { Args, Command, Flags } from "@f5-sales-demo/pi-utils/cli";
|
|
6
|
+
import type { HerdrBindingV1 } from "../herdr/controller";
|
|
7
|
+
|
|
8
|
+
interface CommandResult {
|
|
9
|
+
stdout: string;
|
|
10
|
+
stderr: string;
|
|
11
|
+
exitCode: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function execute(command: string[], inherit = false): Promise<CommandResult> {
|
|
15
|
+
const child = Bun.spawn({
|
|
16
|
+
cmd: command,
|
|
17
|
+
stdin: inherit ? "inherit" : "ignore",
|
|
18
|
+
stdout: inherit ? "inherit" : "pipe",
|
|
19
|
+
stderr: inherit ? "inherit" : "pipe",
|
|
20
|
+
});
|
|
21
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
22
|
+
inherit ? Promise.resolve("") : new Response(child.stdout).text(),
|
|
23
|
+
inherit ? Promise.resolve("") : new Response(child.stderr).text(),
|
|
24
|
+
child.exited,
|
|
25
|
+
]);
|
|
26
|
+
if (exitCode !== 0) throw new Error(stderr.trim() || `${command[0]} exited with ${exitCode}`);
|
|
27
|
+
return { stdout, stderr, exitCode };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function shellQuote(value: string): string {
|
|
31
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildXcshCommand(executable: string, args: string[]): string {
|
|
35
|
+
return [executable, ...args].map(shellQuote).join(" ");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default class Herdr extends Command {
|
|
39
|
+
static description = "Launch xcsh in a conversation-owned Herdr workspace";
|
|
40
|
+
static strict = false;
|
|
41
|
+
static flags = {
|
|
42
|
+
session: Flags.string({ required: true, description: "Herdr named session" }),
|
|
43
|
+
label: Flags.string({ description: "Workspace label" }),
|
|
44
|
+
};
|
|
45
|
+
static args = {
|
|
46
|
+
xcshArgs: Args.string({ required: false, multiple: true, description: "Arguments passed to xcsh after --" }),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
async run(): Promise<void> {
|
|
50
|
+
if (process.env.HERDR_ENV === "1") throw new Error("xcsh herdr must be launched outside an existing Herdr pane");
|
|
51
|
+
const { flags, args } = await this.parse(Herdr);
|
|
52
|
+
const session = flags.session;
|
|
53
|
+
if (!session) throw new Error("--session is required");
|
|
54
|
+
const ownerToken = randomUUID();
|
|
55
|
+
const stateRoot = process.env.XDG_STATE_HOME ?? path.join(os.homedir(), ".local", "state");
|
|
56
|
+
const bindingDir = path.join(stateRoot, "xcsh", "herdr");
|
|
57
|
+
await fs.mkdir(bindingDir, { recursive: true, mode: 0o700 });
|
|
58
|
+
const bindingPath = path.join(bindingDir, `${session}-${ownerToken}.json`);
|
|
59
|
+
const label = flags.label ?? `xcsh-${new Date().toISOString().replace(/[-:.]/g, "").slice(0, 15).toLowerCase()}`;
|
|
60
|
+
const created = await execute([
|
|
61
|
+
"herdr",
|
|
62
|
+
"--session",
|
|
63
|
+
session,
|
|
64
|
+
"workspace",
|
|
65
|
+
"create",
|
|
66
|
+
"--cwd",
|
|
67
|
+
process.cwd(),
|
|
68
|
+
"--label",
|
|
69
|
+
label,
|
|
70
|
+
"--env",
|
|
71
|
+
`XCSH_HERDR_OWNER=${ownerToken}`,
|
|
72
|
+
"--env",
|
|
73
|
+
`XCSH_HERDR_BINDING_PATH=${bindingPath}`,
|
|
74
|
+
"--focus",
|
|
75
|
+
]);
|
|
76
|
+
const response = JSON.parse(created.stdout) as {
|
|
77
|
+
result?: { workspace?: { workspace_id?: string }; root_pane?: { pane_id?: string } };
|
|
78
|
+
};
|
|
79
|
+
const workspaceId = response.result?.workspace?.workspace_id;
|
|
80
|
+
const rootPaneId = response.result?.root_pane?.pane_id;
|
|
81
|
+
if (!workspaceId || !rootPaneId) throw new Error("Herdr returned an invalid workspace creation response");
|
|
82
|
+
const binding: HerdrBindingV1 = {
|
|
83
|
+
version: 1,
|
|
84
|
+
sessionName: session,
|
|
85
|
+
workspaceId,
|
|
86
|
+
rootPaneId,
|
|
87
|
+
ownerToken,
|
|
88
|
+
terminals: [],
|
|
89
|
+
};
|
|
90
|
+
await fs.writeFile(bindingPath, `${JSON.stringify(binding, null, 2)}\n`, { mode: 0o600 });
|
|
91
|
+
await execute([
|
|
92
|
+
"herdr",
|
|
93
|
+
"--session",
|
|
94
|
+
session,
|
|
95
|
+
"workspace",
|
|
96
|
+
"report-metadata",
|
|
97
|
+
workspaceId,
|
|
98
|
+
"--source",
|
|
99
|
+
"xcsh:terminal",
|
|
100
|
+
"--token",
|
|
101
|
+
`xcsh_owner=${ownerToken}`,
|
|
102
|
+
]);
|
|
103
|
+
await execute([
|
|
104
|
+
"herdr",
|
|
105
|
+
"--session",
|
|
106
|
+
session,
|
|
107
|
+
"pane",
|
|
108
|
+
"report-metadata",
|
|
109
|
+
rootPaneId,
|
|
110
|
+
"--source",
|
|
111
|
+
"xcsh:terminal",
|
|
112
|
+
"--token",
|
|
113
|
+
`xcsh_owner=${ownerToken}`,
|
|
114
|
+
]);
|
|
115
|
+
const forwarded = Array.isArray(args.xcshArgs) ? args.xcshArgs : [];
|
|
116
|
+
const xcshCommand = buildXcshCommand(process.env.XCSH_BIN ?? "xcsh", forwarded);
|
|
117
|
+
await execute(["herdr", "--session", session, "pane", "run", rootPaneId, xcshCommand]);
|
|
118
|
+
await execute(["herdr", "session", "attach", session], true);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export { buildXcshCommand, shellQuote };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@f5-sales-demo/xcsh";
|
|
2
|
+
import { HerdrController } from "../../../herdr/controller";
|
|
3
|
+
|
|
4
|
+
type Action = "list" | "create" | "run" | "send" | "read" | "wait" | "status" | "focus" | "close";
|
|
5
|
+
interface TerminalParams {
|
|
6
|
+
action: Action;
|
|
7
|
+
target?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
command?: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
match?: string;
|
|
13
|
+
lines?: number;
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
enter?: boolean;
|
|
16
|
+
force?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createRetryingLazy<T>(factory: () => Promise<T>): () => Promise<T> {
|
|
20
|
+
let pending: Promise<T> | undefined;
|
|
21
|
+
return () => {
|
|
22
|
+
pending ??= factory().catch(error => {
|
|
23
|
+
pending = undefined;
|
|
24
|
+
throw error;
|
|
25
|
+
});
|
|
26
|
+
return pending;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function sessionRef(ctx: ExtensionContext): string | undefined {
|
|
31
|
+
return ctx.sessionManager.getSessionFile?.() ?? ctx.sessionManager.getSessionId?.();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function dispatch(controller: HerdrController, params: TerminalParams): Promise<unknown> {
|
|
35
|
+
switch (params.action) {
|
|
36
|
+
case "list":
|
|
37
|
+
return controller.list();
|
|
38
|
+
case "create":
|
|
39
|
+
return controller.create(params.name ?? "", params.cwd);
|
|
40
|
+
case "run":
|
|
41
|
+
await controller.run(params.target ?? "", params.command ?? "");
|
|
42
|
+
return { ok: true };
|
|
43
|
+
case "send":
|
|
44
|
+
await controller.send(params.target ?? "", params.text ?? "", params.enter);
|
|
45
|
+
return { ok: true };
|
|
46
|
+
case "read":
|
|
47
|
+
return controller.read(params.target ?? "", params.lines);
|
|
48
|
+
case "wait":
|
|
49
|
+
return controller.wait(params.target ?? "", params.match ?? "", params.timeoutMs);
|
|
50
|
+
case "status":
|
|
51
|
+
return controller.status(params.target ?? "");
|
|
52
|
+
case "focus":
|
|
53
|
+
await controller.focus(params.target ?? "");
|
|
54
|
+
return { ok: true };
|
|
55
|
+
case "close":
|
|
56
|
+
await controller.close(params.target ?? "", params.force);
|
|
57
|
+
return { ok: true };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default function herdrTerminal(pi: ExtensionAPI): void {
|
|
62
|
+
const { Type } = pi.typebox;
|
|
63
|
+
const controller = createRetryingLazy(() => HerdrController.connect());
|
|
64
|
+
|
|
65
|
+
pi.registerTool({
|
|
66
|
+
name: "herdr_terminal",
|
|
67
|
+
label: "Herdr Terminal",
|
|
68
|
+
description:
|
|
69
|
+
"Manage support terminals owned by the current xcsh conversation. Actions: list, create, run, send, read, wait, status, focus, close. Focus and forced close must be explicit.",
|
|
70
|
+
parameters: Type.Object({
|
|
71
|
+
action: Type.Union(
|
|
72
|
+
["list", "create", "run", "send", "read", "wait", "status", "focus", "close"].map(value =>
|
|
73
|
+
Type.Literal(value),
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
target: Type.Optional(Type.String({ description: "Terminal name or owned pane ID" })),
|
|
77
|
+
name: Type.Optional(Type.String({ description: "Name for a newly created terminal" })),
|
|
78
|
+
cwd: Type.Optional(Type.String({ description: "Working directory for a newly created terminal" })),
|
|
79
|
+
command: Type.Optional(Type.String({ description: "Command for run" })),
|
|
80
|
+
text: Type.Optional(Type.String({ description: "Text for send" })),
|
|
81
|
+
match: Type.Optional(Type.String({ description: "Literal output awaited by wait" })),
|
|
82
|
+
lines: Type.Optional(Type.Integer({ minimum: 1, maximum: 1000 })),
|
|
83
|
+
timeoutMs: Type.Optional(Type.Integer({ minimum: 0, maximum: 300000 })),
|
|
84
|
+
enter: Type.Optional(Type.Boolean()),
|
|
85
|
+
force: Type.Optional(Type.Boolean({ description: "Required on the second close call for a busy pane" })),
|
|
86
|
+
}),
|
|
87
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
88
|
+
try {
|
|
89
|
+
const active = await controller();
|
|
90
|
+
await active.updateSessionRef(sessionRef(ctx));
|
|
91
|
+
const result = await dispatch(active, params as TerminalParams);
|
|
92
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], details: result };
|
|
93
|
+
} catch (error) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
return { content: [{ type: "text", text: message }], details: { error: message }, isError: true };
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
pi.registerCommand("terminal", {
|
|
101
|
+
description: "Manage Herdr terminals: /terminal <list|create|run|send|read|wait|status|focus|close>",
|
|
102
|
+
handler: async (args, ctx) => {
|
|
103
|
+
const [action = "list", target, ...rest] = args.trim().split(/\s+/).filter(Boolean);
|
|
104
|
+
const params: TerminalParams = { action: action as Action, target };
|
|
105
|
+
if (action === "create") params.name = target;
|
|
106
|
+
if (action === "run") params.command = rest.join(" ");
|
|
107
|
+
if (action === "send") params.text = rest.join(" ");
|
|
108
|
+
if (action === "wait") params.match = rest.join(" ");
|
|
109
|
+
if (action === "read" && rest[0]) params.lines = Number(rest[0]);
|
|
110
|
+
if (action === "close") params.force = rest.includes("--force");
|
|
111
|
+
try {
|
|
112
|
+
const active = await controller();
|
|
113
|
+
await active.updateSessionRef(sessionRef(ctx));
|
|
114
|
+
ctx.ui.notify(JSON.stringify(await dispatch(active, params), null, 2), "info");
|
|
115
|
+
} catch (error) {
|
|
116
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const update = async (_event: unknown, ctx: ExtensionContext): Promise<void> => {
|
|
122
|
+
if (!process.env.HERDR_SOCKET_PATH) return;
|
|
123
|
+
try {
|
|
124
|
+
await (await controller()).updateSessionRef(sessionRef(ctx));
|
|
125
|
+
} catch (error) {
|
|
126
|
+
pi.logger.debug("Herdr terminal binding update failed", {
|
|
127
|
+
error: error instanceof Error ? error.message : String(error),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
pi.on("session_start", update);
|
|
132
|
+
pi.on("session_switch", update);
|
|
133
|
+
pi.on("session_branch", update);
|
|
134
|
+
pi.on("session_tree", update);
|
|
135
|
+
}
|
|
@@ -20,6 +20,7 @@ import { EventBus } from "../../utils/event-bus";
|
|
|
20
20
|
import { getAllPluginExtensionPaths } from "../plugins/loader";
|
|
21
21
|
import { resolvePath } from "../utils";
|
|
22
22
|
import herdrReporter from "./bundled/herdr-reporter";
|
|
23
|
+
import herdrTerminal from "./bundled/herdr-terminal";
|
|
23
24
|
import sandboxGuard from "./bundled/sandbox-guard";
|
|
24
25
|
import type {
|
|
25
26
|
Extension,
|
|
@@ -501,6 +502,7 @@ async function discoverExtensionsInDir(dir: string): Promise<string[]> {
|
|
|
501
502
|
/** Extensions bundled with xcsh and loaded by default (before user extensions). */
|
|
502
503
|
const BUNDLED_EXTENSIONS: ReadonlyArray<{ name: string; factory: ExtensionFactory }> = [
|
|
503
504
|
{ name: "herdr-reporter", factory: herdrReporter },
|
|
505
|
+
{ name: "herdr-terminal", factory: herdrTerminal },
|
|
504
506
|
{ name: "sandbox-guard", factory: sandboxGuard },
|
|
505
507
|
];
|
|
506
508
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { connect } from "node:net";
|
|
3
|
+
|
|
4
|
+
export const HERDR_PROTOCOL_VERSION = 18;
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
6
|
+
const MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
7
|
+
|
|
8
|
+
export class HerdrProtocolError extends Error {
|
|
9
|
+
constructor(
|
|
10
|
+
message: string,
|
|
11
|
+
readonly code = "protocol_error",
|
|
12
|
+
) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "HerdrProtocolError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
19
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Typed, one-request-per-connection client for Herdr's protocol-18 JSONL socket. */
|
|
23
|
+
export class HerdrClient {
|
|
24
|
+
private protocolChecked = false;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
readonly socketPath: string,
|
|
28
|
+
readonly timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
29
|
+
) {
|
|
30
|
+
if (!socketPath) throw new HerdrProtocolError("HERDR_SOCKET_PATH is unavailable", "unavailable");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async ensureProtocol(): Promise<void> {
|
|
34
|
+
if (this.protocolChecked) return;
|
|
35
|
+
const pong = await this.requestRaw<{ type: string; protocol: number; version: string }>("ping", {});
|
|
36
|
+
if (pong.type !== "pong" || pong.protocol !== HERDR_PROTOCOL_VERSION) {
|
|
37
|
+
throw new HerdrProtocolError(
|
|
38
|
+
`Herdr protocol mismatch: expected ${HERDR_PROTOCOL_VERSION}, received ${String(pong.protocol)}`,
|
|
39
|
+
"protocol_mismatch",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
this.protocolChecked = true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async request<T extends Record<string, unknown>>(method: string, params: Record<string, unknown>): Promise<T> {
|
|
46
|
+
await this.ensureProtocol();
|
|
47
|
+
return this.requestRaw<T>(method, params);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private requestRaw<T>(method: string, params: Record<string, unknown>): Promise<T> {
|
|
51
|
+
const id = `xcsh:${randomUUID()}`;
|
|
52
|
+
return new Promise<T>((resolve, reject) => {
|
|
53
|
+
let buffer = "";
|
|
54
|
+
let settled = false;
|
|
55
|
+
const socket = connect({ path: this.socketPath });
|
|
56
|
+
const finish = (error?: unknown, value?: T): void => {
|
|
57
|
+
if (settled) return;
|
|
58
|
+
settled = true;
|
|
59
|
+
socket.destroy();
|
|
60
|
+
if (error !== undefined) reject(error);
|
|
61
|
+
else resolve(value as T);
|
|
62
|
+
};
|
|
63
|
+
socket.setEncoding("utf8");
|
|
64
|
+
socket.setTimeout(this.timeoutMs, () => finish(new HerdrProtocolError("Herdr request timed out", "timeout")));
|
|
65
|
+
socket.once("error", error => finish(new HerdrProtocolError(error.message, "transport_error")));
|
|
66
|
+
socket.once("connect", () => socket.write(`${JSON.stringify({ id, method, params })}\n`));
|
|
67
|
+
socket.on("data", chunk => {
|
|
68
|
+
buffer += chunk;
|
|
69
|
+
if (Buffer.byteLength(buffer) > MAX_RESPONSE_BYTES) {
|
|
70
|
+
finish(new HerdrProtocolError("Herdr response exceeded 4 MiB", "response_too_large"));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (;;) {
|
|
74
|
+
const newline = buffer.indexOf("\n");
|
|
75
|
+
if (newline < 0) break;
|
|
76
|
+
const line = buffer.slice(0, newline);
|
|
77
|
+
buffer = buffer.slice(newline + 1);
|
|
78
|
+
if (!line.trim()) continue;
|
|
79
|
+
let decoded: unknown;
|
|
80
|
+
try {
|
|
81
|
+
decoded = JSON.parse(line);
|
|
82
|
+
} catch {
|
|
83
|
+
finish(new HerdrProtocolError("Herdr returned invalid JSON", "invalid_json"));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!isRecord(decoded) || decoded.id !== id) continue;
|
|
87
|
+
if (isRecord(decoded.error)) {
|
|
88
|
+
finish(
|
|
89
|
+
new HerdrProtocolError(
|
|
90
|
+
typeof decoded.error.message === "string" ? decoded.error.message : "Herdr request failed",
|
|
91
|
+
typeof decoded.error.code === "string" ? decoded.error.code : "remote_error",
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!("result" in decoded) || !isRecord(decoded.result)) {
|
|
97
|
+
finish(new HerdrProtocolError("Herdr response is missing a typed result"));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
finish(undefined, decoded.result as T);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
socket.once("end", () => {
|
|
105
|
+
if (settled) return;
|
|
106
|
+
// Protocol 18 permits the final response to be terminated by EOF. The
|
|
107
|
+
// server normally emits JSONL, but older/current transports may omit the
|
|
108
|
+
// trailing newline when they close a one-shot connection.
|
|
109
|
+
try {
|
|
110
|
+
const decoded = JSON.parse(buffer) as Record<string, unknown>;
|
|
111
|
+
if (decoded.id !== id) throw new Error("response id mismatch");
|
|
112
|
+
if (isRecord(decoded.error)) {
|
|
113
|
+
finish(
|
|
114
|
+
new HerdrProtocolError(
|
|
115
|
+
typeof decoded.error.message === "string" ? decoded.error.message : "Herdr request failed",
|
|
116
|
+
typeof decoded.error.code === "string" ? decoded.error.code : "remote_error",
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
} else if (isRecord(decoded.result)) finish(undefined, decoded.result as T);
|
|
120
|
+
else finish(new HerdrProtocolError("Herdr response is missing a typed result"));
|
|
121
|
+
} catch {
|
|
122
|
+
finish(new HerdrProtocolError("Herdr closed the socket before responding", "eof"));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { HerdrClient, HerdrProtocolError } from "./client";
|
|
5
|
+
|
|
6
|
+
export const HERDR_OWNER_TOKEN = "xcsh_owner";
|
|
7
|
+
const HERDR_SOURCE = "xcsh:terminal";
|
|
8
|
+
|
|
9
|
+
export interface HerdrTerminalRecordV1 {
|
|
10
|
+
name: string;
|
|
11
|
+
tabId: string;
|
|
12
|
+
paneId: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface HerdrBindingV1 {
|
|
17
|
+
version: 1;
|
|
18
|
+
sessionName: string;
|
|
19
|
+
workspaceId: string;
|
|
20
|
+
rootPaneId: string;
|
|
21
|
+
ownerToken: string;
|
|
22
|
+
activeSessionRef?: string;
|
|
23
|
+
terminals: HerdrTerminalRecordV1[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface PaneInfo extends Record<string, unknown> {
|
|
27
|
+
pane_id: string;
|
|
28
|
+
tab_id: string;
|
|
29
|
+
workspace_id: string;
|
|
30
|
+
tokens?: Record<string, string>;
|
|
31
|
+
focused?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface TabCreated extends Record<string, unknown> {
|
|
35
|
+
type: "tab_created";
|
|
36
|
+
tab: { tab_id: string; workspace_id: string; label?: string };
|
|
37
|
+
root_pane: PaneInfo;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function bindingFromEnvironment(): HerdrBindingV1 | undefined {
|
|
41
|
+
const sessionName = process.env.HERDR_SESSION;
|
|
42
|
+
const workspaceId = process.env.HERDR_WORKSPACE_ID;
|
|
43
|
+
const rootPaneId = process.env.HERDR_PANE_ID;
|
|
44
|
+
const ownerToken = process.env.XCSH_HERDR_OWNER;
|
|
45
|
+
if (!sessionName || !workspaceId || !rootPaneId || !ownerToken) return undefined;
|
|
46
|
+
return {
|
|
47
|
+
version: 1,
|
|
48
|
+
sessionName,
|
|
49
|
+
workspaceId,
|
|
50
|
+
rootPaneId,
|
|
51
|
+
ownerToken,
|
|
52
|
+
terminals: [],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class HerdrController {
|
|
57
|
+
private readonly lastInputAt = new Map<string, number>();
|
|
58
|
+
private readonly busyClosePending = new Set<string>();
|
|
59
|
+
private persistQueue: Promise<void> = Promise.resolve();
|
|
60
|
+
private constructor(
|
|
61
|
+
readonly client: HerdrClient,
|
|
62
|
+
readonly bindingPath: string | undefined,
|
|
63
|
+
readonly binding: HerdrBindingV1,
|
|
64
|
+
) {}
|
|
65
|
+
|
|
66
|
+
static async connect(options?: {
|
|
67
|
+
socketPath?: string;
|
|
68
|
+
bindingPath?: string;
|
|
69
|
+
binding?: HerdrBindingV1;
|
|
70
|
+
}): Promise<HerdrController> {
|
|
71
|
+
const socketPath = options?.socketPath ?? process.env.HERDR_SOCKET_PATH;
|
|
72
|
+
if (!socketPath) throw new HerdrProtocolError("terminal management is unavailable outside Herdr", "unavailable");
|
|
73
|
+
const bindingPath = options?.bindingPath ?? process.env.XCSH_HERDR_BINDING_PATH;
|
|
74
|
+
let binding = options?.binding;
|
|
75
|
+
if (!binding && bindingPath) {
|
|
76
|
+
try {
|
|
77
|
+
binding = JSON.parse(await fs.readFile(bindingPath, "utf8")) as HerdrBindingV1;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
binding ??= bindingFromEnvironment();
|
|
83
|
+
if (binding?.version !== 1) throw new HerdrProtocolError("Herdr binding is unavailable", "unavailable");
|
|
84
|
+
const controller = new HerdrController(new HerdrClient(socketPath), bindingPath, binding);
|
|
85
|
+
await controller.claimBinding();
|
|
86
|
+
return controller;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private persist(): Promise<void> {
|
|
90
|
+
if (!this.bindingPath) return Promise.resolve();
|
|
91
|
+
const bindingPath = this.bindingPath;
|
|
92
|
+
const snapshot = `${JSON.stringify(this.binding, null, 2)}\n`;
|
|
93
|
+
const write = async (): Promise<void> => {
|
|
94
|
+
await fs.mkdir(path.dirname(bindingPath), { recursive: true, mode: 0o700 });
|
|
95
|
+
const temporary = `${bindingPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
96
|
+
await fs.writeFile(temporary, snapshot, { mode: 0o600 });
|
|
97
|
+
await fs.rename(temporary, bindingPath);
|
|
98
|
+
};
|
|
99
|
+
const pending = this.persistQueue.then(write, write);
|
|
100
|
+
this.persistQueue = pending.catch(() => {});
|
|
101
|
+
return pending;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private async claimBinding(): Promise<void> {
|
|
105
|
+
await this.client.request("workspace.report_metadata", {
|
|
106
|
+
workspace_id: this.binding.workspaceId,
|
|
107
|
+
source: HERDR_SOURCE,
|
|
108
|
+
tokens: { [HERDR_OWNER_TOKEN]: this.binding.ownerToken },
|
|
109
|
+
seq: Date.now() * 1000,
|
|
110
|
+
});
|
|
111
|
+
await this.client.request("pane.report_metadata", {
|
|
112
|
+
pane_id: this.binding.rootPaneId,
|
|
113
|
+
source: HERDR_SOURCE,
|
|
114
|
+
tokens: { [HERDR_OWNER_TOKEN]: this.binding.ownerToken },
|
|
115
|
+
seq: Date.now() * 1000 + 1,
|
|
116
|
+
});
|
|
117
|
+
await this.persist();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async updateSessionRef(sessionRef: string | undefined): Promise<void> {
|
|
121
|
+
if (!sessionRef || this.binding.activeSessionRef === sessionRef) return;
|
|
122
|
+
this.binding.activeSessionRef = sessionRef;
|
|
123
|
+
await this.client.request("workspace.report_metadata", {
|
|
124
|
+
workspace_id: this.binding.workspaceId,
|
|
125
|
+
source: HERDR_SOURCE,
|
|
126
|
+
tokens: { [HERDR_OWNER_TOKEN]: this.binding.ownerToken, xcsh_session_ref: sessionRef },
|
|
127
|
+
seq: Date.now() * 1000,
|
|
128
|
+
});
|
|
129
|
+
await this.persist();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private async ownedPane(paneId: string): Promise<PaneInfo> {
|
|
133
|
+
const result = await this.client.request<{ type: string; pane: PaneInfo }>("pane.get", { pane_id: paneId });
|
|
134
|
+
const pane = result.pane;
|
|
135
|
+
if (
|
|
136
|
+
!pane ||
|
|
137
|
+
pane.workspace_id !== this.binding.workspaceId ||
|
|
138
|
+
pane.tokens?.[HERDR_OWNER_TOKEN] !== this.binding.ownerToken
|
|
139
|
+
) {
|
|
140
|
+
throw new HerdrProtocolError("terminal is not owned by this xcsh conversation", "not_owned");
|
|
141
|
+
}
|
|
142
|
+
return pane;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async list(): Promise<HerdrTerminalRecordV1[]> {
|
|
146
|
+
const result = await this.client.request<{ type: string; panes: PaneInfo[] }>("pane.list", {
|
|
147
|
+
workspace_id: this.binding.workspaceId,
|
|
148
|
+
});
|
|
149
|
+
const live = new Set(
|
|
150
|
+
(result.panes ?? [])
|
|
151
|
+
.filter(pane => pane.tokens?.[HERDR_OWNER_TOKEN] === this.binding.ownerToken)
|
|
152
|
+
.map(pane => pane.pane_id),
|
|
153
|
+
);
|
|
154
|
+
this.binding.terminals = this.binding.terminals.filter(record => live.has(record.paneId));
|
|
155
|
+
await this.persist();
|
|
156
|
+
return [...this.binding.terminals];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async create(name: string, cwd?: string): Promise<HerdrTerminalRecordV1> {
|
|
160
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,47}$/.test(name)) throw new HerdrProtocolError("invalid terminal name");
|
|
161
|
+
if (this.binding.terminals.some(record => record.name === name))
|
|
162
|
+
throw new HerdrProtocolError("terminal name exists");
|
|
163
|
+
const result = await this.client.request<TabCreated>("tab.create", {
|
|
164
|
+
workspace_id: this.binding.workspaceId,
|
|
165
|
+
label: name,
|
|
166
|
+
cwd: cwd ?? process.cwd(),
|
|
167
|
+
focus: false,
|
|
168
|
+
env: { XCSH_HERDR_OWNER: this.binding.ownerToken },
|
|
169
|
+
});
|
|
170
|
+
if (result.type !== "tab_created" || !result.root_pane?.pane_id)
|
|
171
|
+
throw new HerdrProtocolError("invalid tab.create result");
|
|
172
|
+
await this.client.request("pane.report_metadata", {
|
|
173
|
+
pane_id: result.root_pane.pane_id,
|
|
174
|
+
source: HERDR_SOURCE,
|
|
175
|
+
title: name,
|
|
176
|
+
tokens: { [HERDR_OWNER_TOKEN]: this.binding.ownerToken },
|
|
177
|
+
seq: Date.now() * 1000,
|
|
178
|
+
});
|
|
179
|
+
const record = {
|
|
180
|
+
name,
|
|
181
|
+
tabId: result.tab.tab_id,
|
|
182
|
+
paneId: result.root_pane.pane_id,
|
|
183
|
+
createdAt: new Date().toISOString(),
|
|
184
|
+
};
|
|
185
|
+
this.binding.terminals.push(record);
|
|
186
|
+
await this.persist();
|
|
187
|
+
return record;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private resolve(nameOrId: string): HerdrTerminalRecordV1 {
|
|
191
|
+
const record = this.binding.terminals.find(item => item.name === nameOrId || item.paneId === nameOrId);
|
|
192
|
+
if (!record) throw new HerdrProtocolError("unknown terminal", "not_found");
|
|
193
|
+
return record;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async run(nameOrId: string, command: string): Promise<void> {
|
|
197
|
+
const record = this.resolve(nameOrId);
|
|
198
|
+
await this.ownedPane(record.paneId);
|
|
199
|
+
this.busyClosePending.delete(record.paneId);
|
|
200
|
+
await this.client.request("pane.send_text", { pane_id: record.paneId, text: command });
|
|
201
|
+
await this.client.request("pane.send_keys", { pane_id: record.paneId, keys: ["enter"] });
|
|
202
|
+
this.lastInputAt.set(record.paneId, Date.now());
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async send(nameOrId: string, text: string, enter = false): Promise<void> {
|
|
206
|
+
const record = this.resolve(nameOrId);
|
|
207
|
+
await this.ownedPane(record.paneId);
|
|
208
|
+
this.busyClosePending.delete(record.paneId);
|
|
209
|
+
await this.client.request("pane.send_text", { pane_id: record.paneId, text });
|
|
210
|
+
if (enter) await this.client.request("pane.send_keys", { pane_id: record.paneId, keys: ["enter"] });
|
|
211
|
+
this.lastInputAt.set(record.paneId, Date.now());
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async read(nameOrId: string, lines = 120): Promise<Record<string, unknown>> {
|
|
215
|
+
const record = this.resolve(nameOrId);
|
|
216
|
+
await this.ownedPane(record.paneId);
|
|
217
|
+
return this.client.request("pane.read", {
|
|
218
|
+
pane_id: record.paneId,
|
|
219
|
+
source: "recent_unwrapped",
|
|
220
|
+
format: "text",
|
|
221
|
+
lines: Math.max(1, Math.min(lines, 1000)),
|
|
222
|
+
strip_ansi: true,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async wait(nameOrId: string, match: string, timeoutMs = 30_000): Promise<Record<string, unknown>> {
|
|
227
|
+
const record = this.resolve(nameOrId);
|
|
228
|
+
await this.ownedPane(record.paneId);
|
|
229
|
+
return this.client.request("pane.wait_for_output", {
|
|
230
|
+
pane_id: record.paneId,
|
|
231
|
+
source: "recent_unwrapped",
|
|
232
|
+
match: { type: "substring", value: match },
|
|
233
|
+
timeout_ms: Math.max(0, Math.min(timeoutMs, 300_000)),
|
|
234
|
+
strip_ansi: true,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async status(nameOrId: string): Promise<Record<string, unknown>> {
|
|
239
|
+
const record = this.resolve(nameOrId);
|
|
240
|
+
await this.ownedPane(record.paneId);
|
|
241
|
+
return this.client.request("pane.process_info", { pane_id: record.paneId });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async focus(nameOrId: string): Promise<void> {
|
|
245
|
+
const record = this.resolve(nameOrId);
|
|
246
|
+
await this.ownedPane(record.paneId);
|
|
247
|
+
await this.client.request("pane.focus", { pane_id: record.paneId });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async close(nameOrId: string, force = false): Promise<void> {
|
|
251
|
+
const record = this.resolve(nameOrId);
|
|
252
|
+
const pane = await this.ownedPane(record.paneId);
|
|
253
|
+
if (pane.tab_id !== record.tabId) {
|
|
254
|
+
throw new HerdrProtocolError("terminal tab identity changed", "not_owned");
|
|
255
|
+
}
|
|
256
|
+
const listed = await this.client.request<{ type: string; panes: PaneInfo[] }>("pane.list", {
|
|
257
|
+
workspace_id: this.binding.workspaceId,
|
|
258
|
+
});
|
|
259
|
+
const tabPanes = (listed.panes ?? []).filter(candidate => candidate.tab_id === pane.tab_id);
|
|
260
|
+
if (
|
|
261
|
+
tabPanes.length === 0 ||
|
|
262
|
+
!tabPanes.some(candidate => candidate.pane_id === record.paneId) ||
|
|
263
|
+
tabPanes.some(candidate => candidate.tokens?.[HERDR_OWNER_TOKEN] !== this.binding.ownerToken)
|
|
264
|
+
) {
|
|
265
|
+
throw new HerdrProtocolError("terminal tab contains an unowned pane", "not_owned");
|
|
266
|
+
}
|
|
267
|
+
const status = await this.client.request<{
|
|
268
|
+
type: string;
|
|
269
|
+
process_info?: { shell_pid?: number | null; foreground_processes?: Array<{ pid: number }> } | null;
|
|
270
|
+
}>("pane.process_info", { pane_id: record.paneId });
|
|
271
|
+
const info = status.process_info;
|
|
272
|
+
const recentlySent = Date.now() - (this.lastInputAt.get(record.paneId) ?? 0) < 1_000;
|
|
273
|
+
const busy =
|
|
274
|
+
!info || recentlySent || (info.foreground_processes ?? []).some(process => process.pid !== info.shell_pid);
|
|
275
|
+
if (busy && (!force || !this.busyClosePending.has(record.paneId))) {
|
|
276
|
+
this.busyClosePending.add(record.paneId);
|
|
277
|
+
throw new HerdrProtocolError("terminal is busy; repeat close with force: true", "busy");
|
|
278
|
+
}
|
|
279
|
+
await this.client.request("tab.close", { tab_id: pane.tab_id });
|
|
280
|
+
this.lastInputAt.delete(record.paneId);
|
|
281
|
+
this.busyClosePending.delete(record.paneId);
|
|
282
|
+
this.binding.terminals = this.binding.terminals.filter(item => item.paneId !== record.paneId);
|
|
283
|
+
await this.persist();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "20.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "20.18.0",
|
|
21
|
+
"commit": "bb8af0601559effb96162735f79350a7d6beb9b0",
|
|
22
|
+
"shortCommit": "bb8af06",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v20.
|
|
25
|
-
"commitDate": "2026-08-
|
|
26
|
-
"buildDate": "2026-08-
|
|
24
|
+
"tag": "v20.18.0",
|
|
25
|
+
"commitDate": "2026-08-15T07:52:07Z",
|
|
26
|
+
"buildDate": "2026-08-15T08:18:07.894Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5-sales-demo/xcsh",
|
|
30
30
|
"repoSlug": "f5-sales-demo/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.
|
|
31
|
+
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/bb8af0601559effb96162735f79350a7d6beb9b0",
|
|
32
|
+
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.18.0"
|
|
33
33
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Auto-generated by scripts/generate-docs-index.ts - DO NOT EDIT
|
|
2
2
|
|
|
3
|
-
export const EMBEDDED_DOC_FILENAMES: readonly string[] = ["SYSTEM_PROMPT_GUIDE.md","ar/configuration/blob-artifact-architecture.md","ar/configuration/config-usage.md","ar/configuration/environment-variables.md","ar/configuration/fs-scan-cache-architecture.md","ar/configuration/hooks.md","ar/configuration/porting-from-pi-mono.md","ar/configuration/rpc.md","ar/configuration/sdk.md","ar/configuration/secrets.md","ar/extensions/extension-loading.md","ar/extensions/extensions.md","ar/extensions/gemini-manifest-extensions.md","ar/extensions/marketplace.md","ar/extensions/plugin-manager-installer-plumbing.md","ar/extensions/rulebook-matching-pipeline.md","ar/extensions/skills.md","ar/index.md","ar/mcp/mcp-config.md","ar/mcp/mcp-protocol-transports.md","ar/mcp/mcp-runtime-lifecycle.md","ar/mcp/mcp-server-tool-authoring.md","ar/natives/natives-addon-loader-runtime.md","ar/natives/natives-architecture.md","ar/natives/natives-binding-contract.md","ar/natives/natives-build-release-debugging.md","ar/natives/natives-media-system-utils.md","ar/natives/natives-rust-task-cancellation.md","ar/natives/natives-shell-pty-process.md","ar/natives/natives-text-search-pipeline.md","ar/natives/porting-to-natives.md","ar/providers/models.md","ar/providers/provider-streaming-internals.md","ar/providers/python-repl.md","ar/runtime-tools/bash-tool-runtime.md","ar/runtime-tools/context-command.md","ar/runtime-tools/custom-tools.md","ar/runtime-tools/notebook-tool-runtime.md","ar/runtime-tools/resolve-tool-runtime.md","ar/runtime-tools/slash-command-internals.md","ar/runtime-tools/task-agent-discovery.md","ar/sessions/compaction.md","ar/sessions/handoff-generation-pipeline.md","ar/sessions/memory.md","ar/sessions/non-compaction-retry-policy.md","ar/sessions/session-operations-export-share-fork-resume.md","ar/sessions/session-switching-and-recent-listing.md","ar/sessions/session-tree-plan.md","ar/sessions/session.md","ar/sessions/ttsr-injection-lifecycle.md","ar/tui/theme.md","ar/tui/tree.md","ar/tui/tui-runtime-internals.md","ar/tui/tui.md","de/configuration/blob-artifact-architecture.md","de/configuration/config-usage.md","de/configuration/environment-variables.md","de/configuration/fs-scan-cache-architecture.md","de/configuration/hooks.md","de/configuration/porting-from-pi-mono.md","de/configuration/rpc.md","de/configuration/sdk.md","de/configuration/secrets.md","de/extensions/extension-loading.md","de/extensions/extensions.md","de/extensions/gemini-manifest-extensions.md","de/extensions/marketplace.md","de/extensions/plugin-manager-installer-plumbing.md","de/extensions/rulebook-matching-pipeline.md","de/extensions/skills.md","de/index.md","de/mcp/mcp-config.md","de/mcp/mcp-protocol-transports.md","de/mcp/mcp-runtime-lifecycle.md","de/mcp/mcp-server-tool-authoring.md","de/natives/natives-addon-loader-runtime.md","de/natives/natives-architecture.md","de/natives/natives-binding-contract.md","de/natives/natives-build-release-debugging.md","de/natives/natives-media-system-utils.md","de/natives/natives-rust-task-cancellation.md","de/natives/natives-shell-pty-process.md","de/natives/natives-text-search-pipeline.md","de/natives/porting-to-natives.md","de/providers/models.md","de/providers/provider-streaming-internals.md","de/providers/python-repl.md","de/runtime-tools/bash-tool-runtime.md","de/runtime-tools/context-command.md","de/runtime-tools/custom-tools.md","de/runtime-tools/notebook-tool-runtime.md","de/runtime-tools/resolve-tool-runtime.md","de/runtime-tools/slash-command-internals.md","de/runtime-tools/task-agent-discovery.md","de/sessions/compaction.md","de/sessions/handoff-generation-pipeline.md","de/sessions/memory.md","de/sessions/non-compaction-retry-policy.md","de/sessions/session-operations-export-share-fork-resume.md","de/sessions/session-switching-and-recent-listing.md","de/sessions/session-tree-plan.md","de/sessions/session.md","de/sessions/ttsr-injection-lifecycle.md","de/tui/theme.md","de/tui/tree.md","de/tui/tui-runtime-internals.md","de/tui/tui.md","en/configuration/blob-artifact-architecture.md","en/configuration/config-usage.md","en/configuration/environment-variables.md","en/configuration/fs-scan-cache-architecture.md","en/configuration/hooks.md","en/configuration/porting-from-pi-mono.md","en/configuration/rpc.md","en/configuration/sdk.md","en/configuration/secrets.md","en/container/alpine-deployment.md","en/extensions/extension-loading.md","en/extensions/extensions.md","en/extensions/gemini-manifest-extensions.md","en/extensions/marketplace.md","en/extensions/plugin-manager-installer-plumbing.md","en/extensions/rulebook-matching-pipeline.md","en/extensions/skills.md","en/index.md","en/mcp/mcp-config.md","en/mcp/mcp-protocol-transports.md","en/mcp/mcp-runtime-lifecycle.md","en/mcp/mcp-server-tool-authoring.md","en/natives/natives-addon-loader-runtime.md","en/natives/natives-architecture.md","en/natives/natives-binding-contract.md","en/natives/natives-build-release-debugging.md","en/natives/natives-media-system-utils.md","en/natives/natives-rust-task-cancellation.md","en/natives/natives-shell-pty-process.md","en/natives/natives-text-search-pipeline.md","en/natives/porting-to-natives.md","en/providers/models.md","en/providers/provider-streaming-internals.md","en/providers/python-repl.md","en/runtime-tools/bash-tool-runtime.md","en/runtime-tools/context-command.md","en/runtime-tools/custom-tools.md","en/runtime-tools/notebook-tool-runtime.md","en/runtime-tools/resolve-tool-runtime.md","en/runtime-tools/slash-command-internals.md","en/runtime-tools/task-agent-discovery.md","en/sessions/compaction.md","en/sessions/handoff-generation-pipeline.md","en/sessions/memory.md","en/sessions/non-compaction-retry-policy.md","en/sessions/session-operations-export-share-fork-resume.md","en/sessions/session-switching-and-recent-listing.md","en/sessions/session-tree-plan.md","en/sessions/session.md","en/sessions/ttsr-injection-lifecycle.md","en/tui/theme.md","en/tui/tree.md","en/tui/tui-runtime-internals.md","en/tui/tui.md","es/configuration/blob-artifact-architecture.md","es/configuration/config-usage.md","es/configuration/environment-variables.md","es/configuration/fs-scan-cache-architecture.md","es/configuration/hooks.md","es/configuration/porting-from-pi-mono.md","es/configuration/rpc.md","es/configuration/sdk.md","es/configuration/secrets.md","es/extensions/extension-loading.md","es/extensions/extensions.md","es/extensions/gemini-manifest-extensions.md","es/extensions/marketplace.md","es/extensions/plugin-manager-installer-plumbing.md","es/extensions/rulebook-matching-pipeline.md","es/extensions/skills.md","es/index.md","es/mcp/mcp-config.md","es/mcp/mcp-protocol-transports.md","es/mcp/mcp-runtime-lifecycle.md","es/mcp/mcp-server-tool-authoring.md","es/natives/natives-addon-loader-runtime.md","es/natives/natives-architecture.md","es/natives/natives-binding-contract.md","es/natives/natives-build-release-debugging.md","es/natives/natives-media-system-utils.md","es/natives/natives-rust-task-cancellation.md","es/natives/natives-shell-pty-process.md","es/natives/natives-text-search-pipeline.md","es/natives/porting-to-natives.md","es/providers/models.md","es/providers/provider-streaming-internals.md","es/providers/python-repl.md","es/runtime-tools/bash-tool-runtime.md","es/runtime-tools/context-command.md","es/runtime-tools/custom-tools.md","es/runtime-tools/notebook-tool-runtime.md","es/runtime-tools/resolve-tool-runtime.md","es/runtime-tools/slash-command-internals.md","es/runtime-tools/task-agent-discovery.md","es/sessions/compaction.md","es/sessions/handoff-generation-pipeline.md","es/sessions/memory.md","es/sessions/non-compaction-retry-policy.md","es/sessions/session-operations-export-share-fork-resume.md","es/sessions/session-switching-and-recent-listing.md","es/sessions/session-tree-plan.md","es/sessions/session.md","es/sessions/ttsr-injection-lifecycle.md","es/tui/theme.md","es/tui/tree.md","es/tui/tui-runtime-internals.md","es/tui/tui.md","fr/configuration/blob-artifact-architecture.md","fr/configuration/config-usage.md","fr/configuration/environment-variables.md","fr/configuration/fs-scan-cache-architecture.md","fr/configuration/hooks.md","fr/configuration/porting-from-pi-mono.md","fr/configuration/rpc.md","fr/configuration/sdk.md","fr/configuration/secrets.md","fr/extensions/extension-loading.md","fr/extensions/extensions.md","fr/extensions/gemini-manifest-extensions.md","fr/extensions/marketplace.md","fr/extensions/plugin-manager-installer-plumbing.md","fr/extensions/rulebook-matching-pipeline.md","fr/extensions/skills.md","fr/index.md","fr/mcp/mcp-config.md","fr/mcp/mcp-protocol-transports.md","fr/mcp/mcp-runtime-lifecycle.md","fr/mcp/mcp-server-tool-authoring.md","fr/natives/natives-addon-loader-runtime.md","fr/natives/natives-architecture.md","fr/natives/natives-binding-contract.md","fr/natives/natives-build-release-debugging.md","fr/natives/natives-media-system-utils.md","fr/natives/natives-rust-task-cancellation.md","fr/natives/natives-shell-pty-process.md","fr/natives/natives-text-search-pipeline.md","fr/natives/porting-to-natives.md","fr/providers/models.md","fr/providers/provider-streaming-internals.md","fr/providers/python-repl.md","fr/runtime-tools/bash-tool-runtime.md","fr/runtime-tools/context-command.md","fr/runtime-tools/custom-tools.md","fr/runtime-tools/notebook-tool-runtime.md","fr/runtime-tools/resolve-tool-runtime.md","fr/runtime-tools/slash-command-internals.md","fr/runtime-tools/task-agent-discovery.md","fr/sessions/compaction.md","fr/sessions/handoff-generation-pipeline.md","fr/sessions/memory.md","fr/sessions/non-compaction-retry-policy.md","fr/sessions/session-operations-export-share-fork-resume.md","fr/sessions/session-switching-and-recent-listing.md","fr/sessions/session-tree-plan.md","fr/sessions/session.md","fr/sessions/ttsr-injection-lifecycle.md","fr/tui/theme.md","fr/tui/tree.md","fr/tui/tui-runtime-internals.md","fr/tui/tui.md","hi/configuration/blob-artifact-architecture.md","hi/configuration/config-usage.md","hi/configuration/environment-variables.md","hi/configuration/fs-scan-cache-architecture.md","hi/configuration/hooks.md","hi/configuration/porting-from-pi-mono.md","hi/configuration/rpc.md","hi/configuration/sdk.md","hi/configuration/secrets.md","hi/extensions/extension-loading.md","hi/extensions/extensions.md","hi/extensions/gemini-manifest-extensions.md","hi/extensions/marketplace.md","hi/extensions/plugin-manager-installer-plumbing.md","hi/extensions/rulebook-matching-pipeline.md","hi/extensions/skills.md","hi/index.md","hi/mcp/mcp-config.md","hi/mcp/mcp-protocol-transports.md","hi/mcp/mcp-runtime-lifecycle.md","hi/mcp/mcp-server-tool-authoring.md","hi/natives/natives-addon-loader-runtime.md","hi/natives/natives-architecture.md","hi/natives/natives-binding-contract.md","hi/natives/natives-build-release-debugging.md","hi/natives/natives-media-system-utils.md","hi/natives/natives-rust-task-cancellation.md","hi/natives/natives-shell-pty-process.md","hi/natives/natives-text-search-pipeline.md","hi/natives/porting-to-natives.md","hi/providers/models.md","hi/providers/provider-streaming-internals.md","hi/providers/python-repl.md","hi/runtime-tools/bash-tool-runtime.md","hi/runtime-tools/context-command.md","hi/runtime-tools/custom-tools.md","hi/runtime-tools/notebook-tool-runtime.md","hi/runtime-tools/resolve-tool-runtime.md","hi/runtime-tools/slash-command-internals.md","hi/runtime-tools/task-agent-discovery.md","hi/sessions/compaction.md","hi/sessions/handoff-generation-pipeline.md","hi/sessions/memory.md","hi/sessions/non-compaction-retry-policy.md","hi/sessions/session-operations-export-share-fork-resume.md","hi/sessions/session-switching-and-recent-listing.md","hi/sessions/session-tree-plan.md","hi/sessions/session.md","hi/sessions/ttsr-injection-lifecycle.md","hi/tui/theme.md","hi/tui/tree.md","hi/tui/tui-runtime-internals.md","hi/tui/tui.md","it/configuration/blob-artifact-architecture.md","it/configuration/config-usage.md","it/configuration/environment-variables.md","it/configuration/fs-scan-cache-architecture.md","it/configuration/hooks.md","it/configuration/porting-from-pi-mono.md","it/configuration/rpc.md","it/configuration/sdk.md","it/configuration/secrets.md","it/extensions/extension-loading.md","it/extensions/extensions.md","it/extensions/gemini-manifest-extensions.md","it/extensions/marketplace.md","it/extensions/plugin-manager-installer-plumbing.md","it/extensions/rulebook-matching-pipeline.md","it/extensions/skills.md","it/index.md","it/mcp/mcp-config.md","it/mcp/mcp-protocol-transports.md","it/mcp/mcp-runtime-lifecycle.md","it/mcp/mcp-server-tool-authoring.md","it/natives/natives-addon-loader-runtime.md","it/natives/natives-architecture.md","it/natives/natives-binding-contract.md","it/natives/natives-build-release-debugging.md","it/natives/natives-media-system-utils.md","it/natives/natives-rust-task-cancellation.md","it/natives/natives-shell-pty-process.md","it/natives/natives-text-search-pipeline.md","it/natives/porting-to-natives.md","it/providers/models.md","it/providers/provider-streaming-internals.md","it/providers/python-repl.md","it/runtime-tools/bash-tool-runtime.md","it/runtime-tools/context-command.md","it/runtime-tools/custom-tools.md","it/runtime-tools/notebook-tool-runtime.md","it/runtime-tools/resolve-tool-runtime.md","it/runtime-tools/slash-command-internals.md","it/runtime-tools/task-agent-discovery.md","it/sessions/compaction.md","it/sessions/handoff-generation-pipeline.md","it/sessions/memory.md","it/sessions/non-compaction-retry-policy.md","it/sessions/session-operations-export-share-fork-resume.md","it/sessions/session-switching-and-recent-listing.md","it/sessions/session-tree-plan.md","it/sessions/session.md","it/sessions/ttsr-injection-lifecycle.md","it/tui/theme.md","it/tui/tree.md","it/tui/tui-runtime-internals.md","it/tui/tui.md","ja/configuration/blob-artifact-architecture.md","ja/configuration/config-usage.md","ja/configuration/environment-variables.md","ja/configuration/fs-scan-cache-architecture.md","ja/configuration/hooks.md","ja/configuration/porting-from-pi-mono.md","ja/configuration/rpc.md","ja/configuration/sdk.md","ja/configuration/secrets.md","ja/extensions/extension-loading.md","ja/extensions/extensions.md","ja/extensions/gemini-manifest-extensions.md","ja/extensions/marketplace.md","ja/extensions/plugin-manager-installer-plumbing.md","ja/extensions/rulebook-matching-pipeline.md","ja/extensions/skills.md","ja/index.md","ja/mcp/mcp-config.md","ja/mcp/mcp-protocol-transports.md","ja/mcp/mcp-runtime-lifecycle.md","ja/mcp/mcp-server-tool-authoring.md","ja/natives/natives-addon-loader-runtime.md","ja/natives/natives-architecture.md","ja/natives/natives-binding-contract.md","ja/natives/natives-build-release-debugging.md","ja/natives/natives-media-system-utils.md","ja/natives/natives-rust-task-cancellation.md","ja/natives/natives-shell-pty-process.md","ja/natives/natives-text-search-pipeline.md","ja/natives/porting-to-natives.md","ja/providers/models.md","ja/providers/provider-streaming-internals.md","ja/providers/python-repl.md","ja/runtime-tools/bash-tool-runtime.md","ja/runtime-tools/context-command.md","ja/runtime-tools/custom-tools.md","ja/runtime-tools/notebook-tool-runtime.md","ja/runtime-tools/resolve-tool-runtime.md","ja/runtime-tools/slash-command-internals.md","ja/runtime-tools/task-agent-discovery.md","ja/sessions/compaction.md","ja/sessions/handoff-generation-pipeline.md","ja/sessions/memory.md","ja/sessions/non-compaction-retry-policy.md","ja/sessions/session-operations-export-share-fork-resume.md","ja/sessions/session-switching-and-recent-listing.md","ja/sessions/session-tree-plan.md","ja/sessions/session.md","ja/sessions/ttsr-injection-lifecycle.md","ja/tui/theme.md","ja/tui/tree.md","ja/tui/tui-runtime-internals.md","ja/tui/tui.md","ko/configuration/blob-artifact-architecture.md","ko/configuration/config-usage.md","ko/configuration/environment-variables.md","ko/configuration/fs-scan-cache-architecture.md","ko/configuration/hooks.md","ko/configuration/porting-from-pi-mono.md","ko/configuration/rpc.md","ko/configuration/sdk.md","ko/configuration/secrets.md","ko/extensions/extension-loading.md","ko/extensions/extensions.md","ko/extensions/gemini-manifest-extensions.md","ko/extensions/marketplace.md","ko/extensions/plugin-manager-installer-plumbing.md","ko/extensions/rulebook-matching-pipeline.md","ko/extensions/skills.md","ko/index.md","ko/mcp/mcp-config.md","ko/mcp/mcp-protocol-transports.md","ko/mcp/mcp-runtime-lifecycle.md","ko/mcp/mcp-server-tool-authoring.md","ko/natives/natives-addon-loader-runtime.md","ko/natives/natives-architecture.md","ko/natives/natives-binding-contract.md","ko/natives/natives-build-release-debugging.md","ko/natives/natives-media-system-utils.md","ko/natives/natives-rust-task-cancellation.md","ko/natives/natives-shell-pty-process.md","ko/natives/natives-text-search-pipeline.md","ko/natives/porting-to-natives.md","ko/providers/models.md","ko/providers/provider-streaming-internals.md","ko/providers/python-repl.md","ko/runtime-tools/bash-tool-runtime.md","ko/runtime-tools/context-command.md","ko/runtime-tools/custom-tools.md","ko/runtime-tools/notebook-tool-runtime.md","ko/runtime-tools/resolve-tool-runtime.md","ko/runtime-tools/slash-command-internals.md","ko/runtime-tools/task-agent-discovery.md","ko/sessions/compaction.md","ko/sessions/handoff-generation-pipeline.md","ko/sessions/memory.md","ko/sessions/non-compaction-retry-policy.md","ko/sessions/session-operations-export-share-fork-resume.md","ko/sessions/session-switching-and-recent-listing.md","ko/sessions/session-tree-plan.md","ko/sessions/session.md","ko/sessions/ttsr-injection-lifecycle.md","ko/tui/theme.md","ko/tui/tree.md","ko/tui/tui-runtime-internals.md","ko/tui/tui.md","plans/provider-agnostic-dynamic-model-routing.md","pt-br/configuration/blob-artifact-architecture.md","pt-br/configuration/config-usage.md","pt-br/configuration/environment-variables.md","pt-br/configuration/fs-scan-cache-architecture.md","pt-br/configuration/hooks.md","pt-br/configuration/porting-from-pi-mono.md","pt-br/configuration/rpc.md","pt-br/configuration/sdk.md","pt-br/configuration/secrets.md","pt-br/extensions/extension-loading.md","pt-br/extensions/extensions.md","pt-br/extensions/gemini-manifest-extensions.md","pt-br/extensions/marketplace.md","pt-br/extensions/plugin-manager-installer-plumbing.md","pt-br/extensions/rulebook-matching-pipeline.md","pt-br/extensions/skills.md","pt-br/index.md","pt-br/mcp/mcp-config.md","pt-br/mcp/mcp-protocol-transports.md","pt-br/mcp/mcp-runtime-lifecycle.md","pt-br/mcp/mcp-server-tool-authoring.md","pt-br/natives/natives-addon-loader-runtime.md","pt-br/natives/natives-architecture.md","pt-br/natives/natives-binding-contract.md","pt-br/natives/natives-build-release-debugging.md","pt-br/natives/natives-media-system-utils.md","pt-br/natives/natives-rust-task-cancellation.md","pt-br/natives/natives-shell-pty-process.md","pt-br/natives/natives-text-search-pipeline.md","pt-br/natives/porting-to-natives.md","pt-br/providers/models.md","pt-br/providers/provider-streaming-internals.md","pt-br/providers/python-repl.md","pt-br/runtime-tools/bash-tool-runtime.md","pt-br/runtime-tools/context-command.md","pt-br/runtime-tools/custom-tools.md","pt-br/runtime-tools/notebook-tool-runtime.md","pt-br/runtime-tools/resolve-tool-runtime.md","pt-br/runtime-tools/slash-command-internals.md","pt-br/runtime-tools/task-agent-discovery.md","pt-br/sessions/compaction.md","pt-br/sessions/handoff-generation-pipeline.md","pt-br/sessions/memory.md","pt-br/sessions/non-compaction-retry-policy.md","pt-br/sessions/session-operations-export-share-fork-resume.md","pt-br/sessions/session-switching-and-recent-listing.md","pt-br/sessions/session-tree-plan.md","pt-br/sessions/session.md","pt-br/sessions/ttsr-injection-lifecycle.md","pt-br/tui/theme.md","pt-br/tui/tree.md","pt-br/tui/tui-runtime-internals.md","pt-br/tui/tui.md","th/configuration/blob-artifact-architecture.md","th/configuration/config-usage.md","th/configuration/environment-variables.md","th/configuration/fs-scan-cache-architecture.md","th/configuration/hooks.md","th/configuration/porting-from-pi-mono.md","th/configuration/rpc.md","th/configuration/sdk.md","th/configuration/secrets.md","th/extensions/extension-loading.md","th/extensions/extensions.md","th/extensions/gemini-manifest-extensions.md","th/extensions/marketplace.md","th/extensions/plugin-manager-installer-plumbing.md","th/extensions/rulebook-matching-pipeline.md","th/extensions/skills.md","th/index.md","th/mcp/mcp-config.md","th/mcp/mcp-protocol-transports.md","th/mcp/mcp-runtime-lifecycle.md","th/mcp/mcp-server-tool-authoring.md","th/natives/natives-addon-loader-runtime.md","th/natives/natives-architecture.md","th/natives/natives-binding-contract.md","th/natives/natives-build-release-debugging.md","th/natives/natives-media-system-utils.md","th/natives/natives-rust-task-cancellation.md","th/natives/natives-shell-pty-process.md","th/natives/natives-text-search-pipeline.md","th/natives/porting-to-natives.md","th/providers/models.md","th/providers/provider-streaming-internals.md","th/providers/python-repl.md","th/runtime-tools/bash-tool-runtime.md","th/runtime-tools/context-command.md","th/runtime-tools/custom-tools.md","th/runtime-tools/notebook-tool-runtime.md","th/runtime-tools/resolve-tool-runtime.md","th/runtime-tools/slash-command-internals.md","th/runtime-tools/task-agent-discovery.md","th/sessions/compaction.md","th/sessions/handoff-generation-pipeline.md","th/sessions/memory.md","th/sessions/non-compaction-retry-policy.md","th/sessions/session-operations-export-share-fork-resume.md","th/sessions/session-switching-and-recent-listing.md","th/sessions/session-tree-plan.md","th/sessions/session.md","th/sessions/ttsr-injection-lifecycle.md","th/tui/theme.md","th/tui/tree.md","th/tui/tui-runtime-internals.md","th/tui/tui.md","zh-cn/configuration/blob-artifact-architecture.md","zh-cn/configuration/config-usage.md","zh-cn/configuration/environment-variables.md","zh-cn/configuration/fs-scan-cache-architecture.md","zh-cn/configuration/hooks.md","zh-cn/configuration/porting-from-pi-mono.md","zh-cn/configuration/rpc.md","zh-cn/configuration/sdk.md","zh-cn/configuration/secrets.md","zh-cn/extensions/extension-loading.md","zh-cn/extensions/extensions.md","zh-cn/extensions/gemini-manifest-extensions.md","zh-cn/extensions/marketplace.md","zh-cn/extensions/plugin-manager-installer-plumbing.md","zh-cn/extensions/rulebook-matching-pipeline.md","zh-cn/extensions/skills.md","zh-cn/index.md","zh-cn/mcp/mcp-config.md","zh-cn/mcp/mcp-protocol-transports.md","zh-cn/mcp/mcp-runtime-lifecycle.md","zh-cn/mcp/mcp-server-tool-authoring.md","zh-cn/natives/natives-addon-loader-runtime.md","zh-cn/natives/natives-architecture.md","zh-cn/natives/natives-binding-contract.md","zh-cn/natives/natives-build-release-debugging.md","zh-cn/natives/natives-media-system-utils.md","zh-cn/natives/natives-rust-task-cancellation.md","zh-cn/natives/natives-shell-pty-process.md","zh-cn/natives/natives-text-search-pipeline.md","zh-cn/natives/porting-to-natives.md","zh-cn/providers/models.md","zh-cn/providers/provider-streaming-internals.md","zh-cn/providers/python-repl.md","zh-cn/runtime-tools/bash-tool-runtime.md","zh-cn/runtime-tools/context-command.md","zh-cn/runtime-tools/custom-tools.md","zh-cn/runtime-tools/notebook-tool-runtime.md","zh-cn/runtime-tools/resolve-tool-runtime.md","zh-cn/runtime-tools/slash-command-internals.md","zh-cn/runtime-tools/task-agent-discovery.md","zh-cn/sessions/compaction.md","zh-cn/sessions/handoff-generation-pipeline.md","zh-cn/sessions/memory.md","zh-cn/sessions/non-compaction-retry-policy.md","zh-cn/sessions/session-operations-export-share-fork-resume.md","zh-cn/sessions/session-switching-and-recent-listing.md","zh-cn/sessions/session-tree-plan.md","zh-cn/sessions/session.md","zh-cn/sessions/ttsr-injection-lifecycle.md","zh-cn/tui/theme.md","zh-cn/tui/tree.md","zh-cn/tui/tui-runtime-internals.md","zh-cn/tui/tui.md","zh-tw/configuration/blob-artifact-architecture.md","zh-tw/configuration/config-usage.md","zh-tw/configuration/environment-variables.md","zh-tw/configuration/fs-scan-cache-architecture.md","zh-tw/configuration/hooks.md","zh-tw/configuration/porting-from-pi-mono.md","zh-tw/configuration/rpc.md","zh-tw/configuration/sdk.md","zh-tw/configuration/secrets.md","zh-tw/extensions/extension-loading.md","zh-tw/extensions/extensions.md","zh-tw/extensions/gemini-manifest-extensions.md","zh-tw/extensions/marketplace.md","zh-tw/extensions/plugin-manager-installer-plumbing.md","zh-tw/extensions/rulebook-matching-pipeline.md","zh-tw/extensions/skills.md","zh-tw/index.md","zh-tw/mcp/mcp-config.md","zh-tw/mcp/mcp-protocol-transports.md","zh-tw/mcp/mcp-runtime-lifecycle.md","zh-tw/mcp/mcp-server-tool-authoring.md","zh-tw/natives/natives-addon-loader-runtime.md","zh-tw/natives/natives-architecture.md","zh-tw/natives/natives-binding-contract.md","zh-tw/natives/natives-build-release-debugging.md","zh-tw/natives/natives-media-system-utils.md","zh-tw/natives/natives-rust-task-cancellation.md","zh-tw/natives/natives-shell-pty-process.md","zh-tw/natives/natives-text-search-pipeline.md","zh-tw/natives/porting-to-natives.md","zh-tw/providers/models.md","zh-tw/providers/provider-streaming-internals.md","zh-tw/providers/python-repl.md","zh-tw/runtime-tools/bash-tool-runtime.md","zh-tw/runtime-tools/context-command.md","zh-tw/runtime-tools/custom-tools.md","zh-tw/runtime-tools/notebook-tool-runtime.md","zh-tw/runtime-tools/resolve-tool-runtime.md","zh-tw/runtime-tools/slash-command-internals.md","zh-tw/runtime-tools/task-agent-discovery.md","zh-tw/sessions/compaction.md","zh-tw/sessions/handoff-generation-pipeline.md","zh-tw/sessions/memory.md","zh-tw/sessions/non-compaction-retry-policy.md","zh-tw/sessions/session-operations-export-share-fork-resume.md","zh-tw/sessions/session-switching-and-recent-listing.md","zh-tw/sessions/session-tree-plan.md","zh-tw/sessions/session.md","zh-tw/sessions/ttsr-injection-lifecycle.md","zh-tw/tui/theme.md","zh-tw/tui/tree.md","zh-tw/tui/tui-runtime-internals.md","zh-tw/tui/tui.md"];
|
|
3
|
+
export const EMBEDDED_DOC_FILENAMES: readonly string[] = ["SYSTEM_PROMPT_GUIDE.md","ar/configuration/blob-artifact-architecture.md","ar/configuration/config-usage.md","ar/configuration/environment-variables.md","ar/configuration/fs-scan-cache-architecture.md","ar/configuration/hooks.md","ar/configuration/porting-from-pi-mono.md","ar/configuration/rpc.md","ar/configuration/sdk.md","ar/configuration/secrets.md","ar/extensions/extension-loading.md","ar/extensions/extensions.md","ar/extensions/gemini-manifest-extensions.md","ar/extensions/marketplace.md","ar/extensions/plugin-manager-installer-plumbing.md","ar/extensions/rulebook-matching-pipeline.md","ar/extensions/skills.md","ar/index.md","ar/mcp/mcp-config.md","ar/mcp/mcp-protocol-transports.md","ar/mcp/mcp-runtime-lifecycle.md","ar/mcp/mcp-server-tool-authoring.md","ar/natives/natives-addon-loader-runtime.md","ar/natives/natives-architecture.md","ar/natives/natives-binding-contract.md","ar/natives/natives-build-release-debugging.md","ar/natives/natives-media-system-utils.md","ar/natives/natives-rust-task-cancellation.md","ar/natives/natives-shell-pty-process.md","ar/natives/natives-text-search-pipeline.md","ar/natives/porting-to-natives.md","ar/providers/models.md","ar/providers/provider-streaming-internals.md","ar/providers/python-repl.md","ar/runtime-tools/bash-tool-runtime.md","ar/runtime-tools/context-command.md","ar/runtime-tools/custom-tools.md","ar/runtime-tools/notebook-tool-runtime.md","ar/runtime-tools/resolve-tool-runtime.md","ar/runtime-tools/slash-command-internals.md","ar/runtime-tools/task-agent-discovery.md","ar/sessions/compaction.md","ar/sessions/handoff-generation-pipeline.md","ar/sessions/memory.md","ar/sessions/non-compaction-retry-policy.md","ar/sessions/session-operations-export-share-fork-resume.md","ar/sessions/session-switching-and-recent-listing.md","ar/sessions/session-tree-plan.md","ar/sessions/session.md","ar/sessions/ttsr-injection-lifecycle.md","ar/tui/theme.md","ar/tui/tree.md","ar/tui/tui-runtime-internals.md","ar/tui/tui.md","de/configuration/blob-artifact-architecture.md","de/configuration/config-usage.md","de/configuration/environment-variables.md","de/configuration/fs-scan-cache-architecture.md","de/configuration/hooks.md","de/configuration/porting-from-pi-mono.md","de/configuration/rpc.md","de/configuration/sdk.md","de/configuration/secrets.md","de/extensions/extension-loading.md","de/extensions/extensions.md","de/extensions/gemini-manifest-extensions.md","de/extensions/marketplace.md","de/extensions/plugin-manager-installer-plumbing.md","de/extensions/rulebook-matching-pipeline.md","de/extensions/skills.md","de/index.md","de/mcp/mcp-config.md","de/mcp/mcp-protocol-transports.md","de/mcp/mcp-runtime-lifecycle.md","de/mcp/mcp-server-tool-authoring.md","de/natives/natives-addon-loader-runtime.md","de/natives/natives-architecture.md","de/natives/natives-binding-contract.md","de/natives/natives-build-release-debugging.md","de/natives/natives-media-system-utils.md","de/natives/natives-rust-task-cancellation.md","de/natives/natives-shell-pty-process.md","de/natives/natives-text-search-pipeline.md","de/natives/porting-to-natives.md","de/providers/models.md","de/providers/provider-streaming-internals.md","de/providers/python-repl.md","de/runtime-tools/bash-tool-runtime.md","de/runtime-tools/context-command.md","de/runtime-tools/custom-tools.md","de/runtime-tools/notebook-tool-runtime.md","de/runtime-tools/resolve-tool-runtime.md","de/runtime-tools/slash-command-internals.md","de/runtime-tools/task-agent-discovery.md","de/sessions/compaction.md","de/sessions/handoff-generation-pipeline.md","de/sessions/memory.md","de/sessions/non-compaction-retry-policy.md","de/sessions/session-operations-export-share-fork-resume.md","de/sessions/session-switching-and-recent-listing.md","de/sessions/session-tree-plan.md","de/sessions/session.md","de/sessions/ttsr-injection-lifecycle.md","de/tui/theme.md","de/tui/tree.md","de/tui/tui-runtime-internals.md","de/tui/tui.md","en/configuration/blob-artifact-architecture.md","en/configuration/config-usage.md","en/configuration/environment-variables.md","en/configuration/fs-scan-cache-architecture.md","en/configuration/herdr.md","en/configuration/hooks.md","en/configuration/porting-from-pi-mono.md","en/configuration/rpc.md","en/configuration/sdk.md","en/configuration/secrets.md","en/container/alpine-deployment.md","en/extensions/extension-loading.md","en/extensions/extensions.md","en/extensions/gemini-manifest-extensions.md","en/extensions/marketplace.md","en/extensions/plugin-manager-installer-plumbing.md","en/extensions/rulebook-matching-pipeline.md","en/extensions/skills.md","en/index.md","en/mcp/mcp-config.md","en/mcp/mcp-protocol-transports.md","en/mcp/mcp-runtime-lifecycle.md","en/mcp/mcp-server-tool-authoring.md","en/natives/natives-addon-loader-runtime.md","en/natives/natives-architecture.md","en/natives/natives-binding-contract.md","en/natives/natives-build-release-debugging.md","en/natives/natives-media-system-utils.md","en/natives/natives-rust-task-cancellation.md","en/natives/natives-shell-pty-process.md","en/natives/natives-text-search-pipeline.md","en/natives/porting-to-natives.md","en/providers/models.md","en/providers/provider-streaming-internals.md","en/providers/python-repl.md","en/runtime-tools/bash-tool-runtime.md","en/runtime-tools/context-command.md","en/runtime-tools/custom-tools.md","en/runtime-tools/notebook-tool-runtime.md","en/runtime-tools/resolve-tool-runtime.md","en/runtime-tools/slash-command-internals.md","en/runtime-tools/task-agent-discovery.md","en/sessions/compaction.md","en/sessions/handoff-generation-pipeline.md","en/sessions/memory.md","en/sessions/non-compaction-retry-policy.md","en/sessions/session-operations-export-share-fork-resume.md","en/sessions/session-switching-and-recent-listing.md","en/sessions/session-tree-plan.md","en/sessions/session.md","en/sessions/ttsr-injection-lifecycle.md","en/tui/theme.md","en/tui/tree.md","en/tui/tui-runtime-internals.md","en/tui/tui.md","es/configuration/blob-artifact-architecture.md","es/configuration/config-usage.md","es/configuration/environment-variables.md","es/configuration/fs-scan-cache-architecture.md","es/configuration/hooks.md","es/configuration/porting-from-pi-mono.md","es/configuration/rpc.md","es/configuration/sdk.md","es/configuration/secrets.md","es/extensions/extension-loading.md","es/extensions/extensions.md","es/extensions/gemini-manifest-extensions.md","es/extensions/marketplace.md","es/extensions/plugin-manager-installer-plumbing.md","es/extensions/rulebook-matching-pipeline.md","es/extensions/skills.md","es/index.md","es/mcp/mcp-config.md","es/mcp/mcp-protocol-transports.md","es/mcp/mcp-runtime-lifecycle.md","es/mcp/mcp-server-tool-authoring.md","es/natives/natives-addon-loader-runtime.md","es/natives/natives-architecture.md","es/natives/natives-binding-contract.md","es/natives/natives-build-release-debugging.md","es/natives/natives-media-system-utils.md","es/natives/natives-rust-task-cancellation.md","es/natives/natives-shell-pty-process.md","es/natives/natives-text-search-pipeline.md","es/natives/porting-to-natives.md","es/providers/models.md","es/providers/provider-streaming-internals.md","es/providers/python-repl.md","es/runtime-tools/bash-tool-runtime.md","es/runtime-tools/context-command.md","es/runtime-tools/custom-tools.md","es/runtime-tools/notebook-tool-runtime.md","es/runtime-tools/resolve-tool-runtime.md","es/runtime-tools/slash-command-internals.md","es/runtime-tools/task-agent-discovery.md","es/sessions/compaction.md","es/sessions/handoff-generation-pipeline.md","es/sessions/memory.md","es/sessions/non-compaction-retry-policy.md","es/sessions/session-operations-export-share-fork-resume.md","es/sessions/session-switching-and-recent-listing.md","es/sessions/session-tree-plan.md","es/sessions/session.md","es/sessions/ttsr-injection-lifecycle.md","es/tui/theme.md","es/tui/tree.md","es/tui/tui-runtime-internals.md","es/tui/tui.md","fr/configuration/blob-artifact-architecture.md","fr/configuration/config-usage.md","fr/configuration/environment-variables.md","fr/configuration/fs-scan-cache-architecture.md","fr/configuration/hooks.md","fr/configuration/porting-from-pi-mono.md","fr/configuration/rpc.md","fr/configuration/sdk.md","fr/configuration/secrets.md","fr/extensions/extension-loading.md","fr/extensions/extensions.md","fr/extensions/gemini-manifest-extensions.md","fr/extensions/marketplace.md","fr/extensions/plugin-manager-installer-plumbing.md","fr/extensions/rulebook-matching-pipeline.md","fr/extensions/skills.md","fr/index.md","fr/mcp/mcp-config.md","fr/mcp/mcp-protocol-transports.md","fr/mcp/mcp-runtime-lifecycle.md","fr/mcp/mcp-server-tool-authoring.md","fr/natives/natives-addon-loader-runtime.md","fr/natives/natives-architecture.md","fr/natives/natives-binding-contract.md","fr/natives/natives-build-release-debugging.md","fr/natives/natives-media-system-utils.md","fr/natives/natives-rust-task-cancellation.md","fr/natives/natives-shell-pty-process.md","fr/natives/natives-text-search-pipeline.md","fr/natives/porting-to-natives.md","fr/providers/models.md","fr/providers/provider-streaming-internals.md","fr/providers/python-repl.md","fr/runtime-tools/bash-tool-runtime.md","fr/runtime-tools/context-command.md","fr/runtime-tools/custom-tools.md","fr/runtime-tools/notebook-tool-runtime.md","fr/runtime-tools/resolve-tool-runtime.md","fr/runtime-tools/slash-command-internals.md","fr/runtime-tools/task-agent-discovery.md","fr/sessions/compaction.md","fr/sessions/handoff-generation-pipeline.md","fr/sessions/memory.md","fr/sessions/non-compaction-retry-policy.md","fr/sessions/session-operations-export-share-fork-resume.md","fr/sessions/session-switching-and-recent-listing.md","fr/sessions/session-tree-plan.md","fr/sessions/session.md","fr/sessions/ttsr-injection-lifecycle.md","fr/tui/theme.md","fr/tui/tree.md","fr/tui/tui-runtime-internals.md","fr/tui/tui.md","hi/configuration/blob-artifact-architecture.md","hi/configuration/config-usage.md","hi/configuration/environment-variables.md","hi/configuration/fs-scan-cache-architecture.md","hi/configuration/hooks.md","hi/configuration/porting-from-pi-mono.md","hi/configuration/rpc.md","hi/configuration/sdk.md","hi/configuration/secrets.md","hi/extensions/extension-loading.md","hi/extensions/extensions.md","hi/extensions/gemini-manifest-extensions.md","hi/extensions/marketplace.md","hi/extensions/plugin-manager-installer-plumbing.md","hi/extensions/rulebook-matching-pipeline.md","hi/extensions/skills.md","hi/index.md","hi/mcp/mcp-config.md","hi/mcp/mcp-protocol-transports.md","hi/mcp/mcp-runtime-lifecycle.md","hi/mcp/mcp-server-tool-authoring.md","hi/natives/natives-addon-loader-runtime.md","hi/natives/natives-architecture.md","hi/natives/natives-binding-contract.md","hi/natives/natives-build-release-debugging.md","hi/natives/natives-media-system-utils.md","hi/natives/natives-rust-task-cancellation.md","hi/natives/natives-shell-pty-process.md","hi/natives/natives-text-search-pipeline.md","hi/natives/porting-to-natives.md","hi/providers/models.md","hi/providers/provider-streaming-internals.md","hi/providers/python-repl.md","hi/runtime-tools/bash-tool-runtime.md","hi/runtime-tools/context-command.md","hi/runtime-tools/custom-tools.md","hi/runtime-tools/notebook-tool-runtime.md","hi/runtime-tools/resolve-tool-runtime.md","hi/runtime-tools/slash-command-internals.md","hi/runtime-tools/task-agent-discovery.md","hi/sessions/compaction.md","hi/sessions/handoff-generation-pipeline.md","hi/sessions/memory.md","hi/sessions/non-compaction-retry-policy.md","hi/sessions/session-operations-export-share-fork-resume.md","hi/sessions/session-switching-and-recent-listing.md","hi/sessions/session-tree-plan.md","hi/sessions/session.md","hi/sessions/ttsr-injection-lifecycle.md","hi/tui/theme.md","hi/tui/tree.md","hi/tui/tui-runtime-internals.md","hi/tui/tui.md","it/configuration/blob-artifact-architecture.md","it/configuration/config-usage.md","it/configuration/environment-variables.md","it/configuration/fs-scan-cache-architecture.md","it/configuration/hooks.md","it/configuration/porting-from-pi-mono.md","it/configuration/rpc.md","it/configuration/sdk.md","it/configuration/secrets.md","it/extensions/extension-loading.md","it/extensions/extensions.md","it/extensions/gemini-manifest-extensions.md","it/extensions/marketplace.md","it/extensions/plugin-manager-installer-plumbing.md","it/extensions/rulebook-matching-pipeline.md","it/extensions/skills.md","it/index.md","it/mcp/mcp-config.md","it/mcp/mcp-protocol-transports.md","it/mcp/mcp-runtime-lifecycle.md","it/mcp/mcp-server-tool-authoring.md","it/natives/natives-addon-loader-runtime.md","it/natives/natives-architecture.md","it/natives/natives-binding-contract.md","it/natives/natives-build-release-debugging.md","it/natives/natives-media-system-utils.md","it/natives/natives-rust-task-cancellation.md","it/natives/natives-shell-pty-process.md","it/natives/natives-text-search-pipeline.md","it/natives/porting-to-natives.md","it/providers/models.md","it/providers/provider-streaming-internals.md","it/providers/python-repl.md","it/runtime-tools/bash-tool-runtime.md","it/runtime-tools/context-command.md","it/runtime-tools/custom-tools.md","it/runtime-tools/notebook-tool-runtime.md","it/runtime-tools/resolve-tool-runtime.md","it/runtime-tools/slash-command-internals.md","it/runtime-tools/task-agent-discovery.md","it/sessions/compaction.md","it/sessions/handoff-generation-pipeline.md","it/sessions/memory.md","it/sessions/non-compaction-retry-policy.md","it/sessions/session-operations-export-share-fork-resume.md","it/sessions/session-switching-and-recent-listing.md","it/sessions/session-tree-plan.md","it/sessions/session.md","it/sessions/ttsr-injection-lifecycle.md","it/tui/theme.md","it/tui/tree.md","it/tui/tui-runtime-internals.md","it/tui/tui.md","ja/configuration/blob-artifact-architecture.md","ja/configuration/config-usage.md","ja/configuration/environment-variables.md","ja/configuration/fs-scan-cache-architecture.md","ja/configuration/hooks.md","ja/configuration/porting-from-pi-mono.md","ja/configuration/rpc.md","ja/configuration/sdk.md","ja/configuration/secrets.md","ja/extensions/extension-loading.md","ja/extensions/extensions.md","ja/extensions/gemini-manifest-extensions.md","ja/extensions/marketplace.md","ja/extensions/plugin-manager-installer-plumbing.md","ja/extensions/rulebook-matching-pipeline.md","ja/extensions/skills.md","ja/index.md","ja/mcp/mcp-config.md","ja/mcp/mcp-protocol-transports.md","ja/mcp/mcp-runtime-lifecycle.md","ja/mcp/mcp-server-tool-authoring.md","ja/natives/natives-addon-loader-runtime.md","ja/natives/natives-architecture.md","ja/natives/natives-binding-contract.md","ja/natives/natives-build-release-debugging.md","ja/natives/natives-media-system-utils.md","ja/natives/natives-rust-task-cancellation.md","ja/natives/natives-shell-pty-process.md","ja/natives/natives-text-search-pipeline.md","ja/natives/porting-to-natives.md","ja/providers/models.md","ja/providers/provider-streaming-internals.md","ja/providers/python-repl.md","ja/runtime-tools/bash-tool-runtime.md","ja/runtime-tools/context-command.md","ja/runtime-tools/custom-tools.md","ja/runtime-tools/notebook-tool-runtime.md","ja/runtime-tools/resolve-tool-runtime.md","ja/runtime-tools/slash-command-internals.md","ja/runtime-tools/task-agent-discovery.md","ja/sessions/compaction.md","ja/sessions/handoff-generation-pipeline.md","ja/sessions/memory.md","ja/sessions/non-compaction-retry-policy.md","ja/sessions/session-operations-export-share-fork-resume.md","ja/sessions/session-switching-and-recent-listing.md","ja/sessions/session-tree-plan.md","ja/sessions/session.md","ja/sessions/ttsr-injection-lifecycle.md","ja/tui/theme.md","ja/tui/tree.md","ja/tui/tui-runtime-internals.md","ja/tui/tui.md","ko/configuration/blob-artifact-architecture.md","ko/configuration/config-usage.md","ko/configuration/environment-variables.md","ko/configuration/fs-scan-cache-architecture.md","ko/configuration/hooks.md","ko/configuration/porting-from-pi-mono.md","ko/configuration/rpc.md","ko/configuration/sdk.md","ko/configuration/secrets.md","ko/extensions/extension-loading.md","ko/extensions/extensions.md","ko/extensions/gemini-manifest-extensions.md","ko/extensions/marketplace.md","ko/extensions/plugin-manager-installer-plumbing.md","ko/extensions/rulebook-matching-pipeline.md","ko/extensions/skills.md","ko/index.md","ko/mcp/mcp-config.md","ko/mcp/mcp-protocol-transports.md","ko/mcp/mcp-runtime-lifecycle.md","ko/mcp/mcp-server-tool-authoring.md","ko/natives/natives-addon-loader-runtime.md","ko/natives/natives-architecture.md","ko/natives/natives-binding-contract.md","ko/natives/natives-build-release-debugging.md","ko/natives/natives-media-system-utils.md","ko/natives/natives-rust-task-cancellation.md","ko/natives/natives-shell-pty-process.md","ko/natives/natives-text-search-pipeline.md","ko/natives/porting-to-natives.md","ko/providers/models.md","ko/providers/provider-streaming-internals.md","ko/providers/python-repl.md","ko/runtime-tools/bash-tool-runtime.md","ko/runtime-tools/context-command.md","ko/runtime-tools/custom-tools.md","ko/runtime-tools/notebook-tool-runtime.md","ko/runtime-tools/resolve-tool-runtime.md","ko/runtime-tools/slash-command-internals.md","ko/runtime-tools/task-agent-discovery.md","ko/sessions/compaction.md","ko/sessions/handoff-generation-pipeline.md","ko/sessions/memory.md","ko/sessions/non-compaction-retry-policy.md","ko/sessions/session-operations-export-share-fork-resume.md","ko/sessions/session-switching-and-recent-listing.md","ko/sessions/session-tree-plan.md","ko/sessions/session.md","ko/sessions/ttsr-injection-lifecycle.md","ko/tui/theme.md","ko/tui/tree.md","ko/tui/tui-runtime-internals.md","ko/tui/tui.md","plans/provider-agnostic-dynamic-model-routing.md","pt-br/configuration/blob-artifact-architecture.md","pt-br/configuration/config-usage.md","pt-br/configuration/environment-variables.md","pt-br/configuration/fs-scan-cache-architecture.md","pt-br/configuration/hooks.md","pt-br/configuration/porting-from-pi-mono.md","pt-br/configuration/rpc.md","pt-br/configuration/sdk.md","pt-br/configuration/secrets.md","pt-br/extensions/extension-loading.md","pt-br/extensions/extensions.md","pt-br/extensions/gemini-manifest-extensions.md","pt-br/extensions/marketplace.md","pt-br/extensions/plugin-manager-installer-plumbing.md","pt-br/extensions/rulebook-matching-pipeline.md","pt-br/extensions/skills.md","pt-br/index.md","pt-br/mcp/mcp-config.md","pt-br/mcp/mcp-protocol-transports.md","pt-br/mcp/mcp-runtime-lifecycle.md","pt-br/mcp/mcp-server-tool-authoring.md","pt-br/natives/natives-addon-loader-runtime.md","pt-br/natives/natives-architecture.md","pt-br/natives/natives-binding-contract.md","pt-br/natives/natives-build-release-debugging.md","pt-br/natives/natives-media-system-utils.md","pt-br/natives/natives-rust-task-cancellation.md","pt-br/natives/natives-shell-pty-process.md","pt-br/natives/natives-text-search-pipeline.md","pt-br/natives/porting-to-natives.md","pt-br/providers/models.md","pt-br/providers/provider-streaming-internals.md","pt-br/providers/python-repl.md","pt-br/runtime-tools/bash-tool-runtime.md","pt-br/runtime-tools/context-command.md","pt-br/runtime-tools/custom-tools.md","pt-br/runtime-tools/notebook-tool-runtime.md","pt-br/runtime-tools/resolve-tool-runtime.md","pt-br/runtime-tools/slash-command-internals.md","pt-br/runtime-tools/task-agent-discovery.md","pt-br/sessions/compaction.md","pt-br/sessions/handoff-generation-pipeline.md","pt-br/sessions/memory.md","pt-br/sessions/non-compaction-retry-policy.md","pt-br/sessions/session-operations-export-share-fork-resume.md","pt-br/sessions/session-switching-and-recent-listing.md","pt-br/sessions/session-tree-plan.md","pt-br/sessions/session.md","pt-br/sessions/ttsr-injection-lifecycle.md","pt-br/tui/theme.md","pt-br/tui/tree.md","pt-br/tui/tui-runtime-internals.md","pt-br/tui/tui.md","th/configuration/blob-artifact-architecture.md","th/configuration/config-usage.md","th/configuration/environment-variables.md","th/configuration/fs-scan-cache-architecture.md","th/configuration/hooks.md","th/configuration/porting-from-pi-mono.md","th/configuration/rpc.md","th/configuration/sdk.md","th/configuration/secrets.md","th/extensions/extension-loading.md","th/extensions/extensions.md","th/extensions/gemini-manifest-extensions.md","th/extensions/marketplace.md","th/extensions/plugin-manager-installer-plumbing.md","th/extensions/rulebook-matching-pipeline.md","th/extensions/skills.md","th/index.md","th/mcp/mcp-config.md","th/mcp/mcp-protocol-transports.md","th/mcp/mcp-runtime-lifecycle.md","th/mcp/mcp-server-tool-authoring.md","th/natives/natives-addon-loader-runtime.md","th/natives/natives-architecture.md","th/natives/natives-binding-contract.md","th/natives/natives-build-release-debugging.md","th/natives/natives-media-system-utils.md","th/natives/natives-rust-task-cancellation.md","th/natives/natives-shell-pty-process.md","th/natives/natives-text-search-pipeline.md","th/natives/porting-to-natives.md","th/providers/models.md","th/providers/provider-streaming-internals.md","th/providers/python-repl.md","th/runtime-tools/bash-tool-runtime.md","th/runtime-tools/context-command.md","th/runtime-tools/custom-tools.md","th/runtime-tools/notebook-tool-runtime.md","th/runtime-tools/resolve-tool-runtime.md","th/runtime-tools/slash-command-internals.md","th/runtime-tools/task-agent-discovery.md","th/sessions/compaction.md","th/sessions/handoff-generation-pipeline.md","th/sessions/memory.md","th/sessions/non-compaction-retry-policy.md","th/sessions/session-operations-export-share-fork-resume.md","th/sessions/session-switching-and-recent-listing.md","th/sessions/session-tree-plan.md","th/sessions/session.md","th/sessions/ttsr-injection-lifecycle.md","th/tui/theme.md","th/tui/tree.md","th/tui/tui-runtime-internals.md","th/tui/tui.md","zh-cn/configuration/blob-artifact-architecture.md","zh-cn/configuration/config-usage.md","zh-cn/configuration/environment-variables.md","zh-cn/configuration/fs-scan-cache-architecture.md","zh-cn/configuration/hooks.md","zh-cn/configuration/porting-from-pi-mono.md","zh-cn/configuration/rpc.md","zh-cn/configuration/sdk.md","zh-cn/configuration/secrets.md","zh-cn/extensions/extension-loading.md","zh-cn/extensions/extensions.md","zh-cn/extensions/gemini-manifest-extensions.md","zh-cn/extensions/marketplace.md","zh-cn/extensions/plugin-manager-installer-plumbing.md","zh-cn/extensions/rulebook-matching-pipeline.md","zh-cn/extensions/skills.md","zh-cn/index.md","zh-cn/mcp/mcp-config.md","zh-cn/mcp/mcp-protocol-transports.md","zh-cn/mcp/mcp-runtime-lifecycle.md","zh-cn/mcp/mcp-server-tool-authoring.md","zh-cn/natives/natives-addon-loader-runtime.md","zh-cn/natives/natives-architecture.md","zh-cn/natives/natives-binding-contract.md","zh-cn/natives/natives-build-release-debugging.md","zh-cn/natives/natives-media-system-utils.md","zh-cn/natives/natives-rust-task-cancellation.md","zh-cn/natives/natives-shell-pty-process.md","zh-cn/natives/natives-text-search-pipeline.md","zh-cn/natives/porting-to-natives.md","zh-cn/providers/models.md","zh-cn/providers/provider-streaming-internals.md","zh-cn/providers/python-repl.md","zh-cn/runtime-tools/bash-tool-runtime.md","zh-cn/runtime-tools/context-command.md","zh-cn/runtime-tools/custom-tools.md","zh-cn/runtime-tools/notebook-tool-runtime.md","zh-cn/runtime-tools/resolve-tool-runtime.md","zh-cn/runtime-tools/slash-command-internals.md","zh-cn/runtime-tools/task-agent-discovery.md","zh-cn/sessions/compaction.md","zh-cn/sessions/handoff-generation-pipeline.md","zh-cn/sessions/memory.md","zh-cn/sessions/non-compaction-retry-policy.md","zh-cn/sessions/session-operations-export-share-fork-resume.md","zh-cn/sessions/session-switching-and-recent-listing.md","zh-cn/sessions/session-tree-plan.md","zh-cn/sessions/session.md","zh-cn/sessions/ttsr-injection-lifecycle.md","zh-cn/tui/theme.md","zh-cn/tui/tree.md","zh-cn/tui/tui-runtime-internals.md","zh-cn/tui/tui.md","zh-tw/configuration/blob-artifact-architecture.md","zh-tw/configuration/config-usage.md","zh-tw/configuration/environment-variables.md","zh-tw/configuration/fs-scan-cache-architecture.md","zh-tw/configuration/hooks.md","zh-tw/configuration/porting-from-pi-mono.md","zh-tw/configuration/rpc.md","zh-tw/configuration/sdk.md","zh-tw/configuration/secrets.md","zh-tw/extensions/extension-loading.md","zh-tw/extensions/extensions.md","zh-tw/extensions/gemini-manifest-extensions.md","zh-tw/extensions/marketplace.md","zh-tw/extensions/plugin-manager-installer-plumbing.md","zh-tw/extensions/rulebook-matching-pipeline.md","zh-tw/extensions/skills.md","zh-tw/index.md","zh-tw/mcp/mcp-config.md","zh-tw/mcp/mcp-protocol-transports.md","zh-tw/mcp/mcp-runtime-lifecycle.md","zh-tw/mcp/mcp-server-tool-authoring.md","zh-tw/natives/natives-addon-loader-runtime.md","zh-tw/natives/natives-architecture.md","zh-tw/natives/natives-binding-contract.md","zh-tw/natives/natives-build-release-debugging.md","zh-tw/natives/natives-media-system-utils.md","zh-tw/natives/natives-rust-task-cancellation.md","zh-tw/natives/natives-shell-pty-process.md","zh-tw/natives/natives-text-search-pipeline.md","zh-tw/natives/porting-to-natives.md","zh-tw/providers/models.md","zh-tw/providers/provider-streaming-internals.md","zh-tw/providers/python-repl.md","zh-tw/runtime-tools/bash-tool-runtime.md","zh-tw/runtime-tools/context-command.md","zh-tw/runtime-tools/custom-tools.md","zh-tw/runtime-tools/notebook-tool-runtime.md","zh-tw/runtime-tools/resolve-tool-runtime.md","zh-tw/runtime-tools/slash-command-internals.md","zh-tw/runtime-tools/task-agent-discovery.md","zh-tw/sessions/compaction.md","zh-tw/sessions/handoff-generation-pipeline.md","zh-tw/sessions/memory.md","zh-tw/sessions/non-compaction-retry-policy.md","zh-tw/sessions/session-operations-export-share-fork-resume.md","zh-tw/sessions/session-switching-and-recent-listing.md","zh-tw/sessions/session-tree-plan.md","zh-tw/sessions/session.md","zh-tw/sessions/ttsr-injection-lifecycle.md","zh-tw/tui/theme.md","zh-tw/tui/tree.md","zh-tw/tui/tui-runtime-internals.md","zh-tw/tui/tui.md"];
|
|
4
4
|
|
|
5
5
|
export const EMBEDDED_DOCS: Readonly<Record<string, string>> = {
|
|
6
6
|
"SYSTEM_PROMPT_GUIDE.md": "# Anthropic System Prompting Best Practices Standard\n\nThis document defines the authoritative engineering standard for authoring, refactoring, and maintaining system prompts, agent instructions, and skills across all f5-sales-demo repositories and xcsh AI assistant plugins.\n\nAll system prompts and agent instructions must strictly adhere to this 8-point checklist.\n\n---\n\n## The 8-Point System Prompt Checklist\n\n### 1. XML Tag Hierarchy & Semantic Framing\n- **Standard**: Wrap logical prompt sections in clean, explicit XML tags (`<role>`, `<defensive_scope>`, `<governance>`, `<operational_standards>`, `<execution_protocol>`, `<examples>`, `<structured_reporting>`).\n- **Rationale**: Claude models are optimized to recognize XML tags as deterministic structural boundaries. Using XML tags prevents context confusion, isolates directives, and ensures consistent rule adherence.\n\n### 2. Affirmative Guidance over Negative Prohibitions\n- **Standard**: Frame all operational boundaries, rules, and workflows using positive, action-oriented directives (*what TO do*). Eliminate negative panic keywords (`HALT immediately`, `STOP`, `DON'T`, `NEVER`, `PROHIBITED`).\n- **Rationale**: Negative prohibition language induces cognitive friction and model freezing, causing the assistant to become overly hesitant or refuse valid execution paths. Affirmative guidance provides a clear forward direction.\n\n### 3. Actionable Rationale (\"Why\" Explanations)\n- **Standard**: Pair every guideline, constraint, or workflow step with an explicit explanation of *why* the rule exists and what outcome it guarantees.\n- **Rationale**: Providing rationale equips the model with the underlying engineering intent. This allows Claude to reason safely and adapt flexibly in novel edge cases rather than failing when encountering unexpected inputs.\n\n### 4. Progressive Context Loading & Modular Hierarchy\n- **Standard**: Keep top-level system prompts concise and focused on core persona, scope, and high-level routing. Place granular tool schemas, raw API curl specs, and detailed multi-step SOPs into dynamically loaded skills or specialized subagents.\n- **Rationale**: Progressive context loading prevents prompt bloat, reduces token overhead, avoids recency bias degradation, and maximizes model attention on the immediate task.\n\n### 5. Expert Persona & Professional Confidence\n- **Standard**: Anchor the assistant or agent with an authoritative, expert persona (*\"You are the GitHub Operations Expert agent...\"*) that approaches tasks with professional confidence, precision, and mastery.\n- **Rationale**: An expert persona establishes domain authority, enhances task execution precision, and encourages autonomous problem-solving within safety boundaries.\n\n### 6. Constructive Fallback Paths (Forward Progress)\n- **Standard**: Provide constructive forward-progress actions for handling missing parameters, ambiguous inputs, or transient API errors instead of halting or throwing panic errors.\n- **Rationale**: Forward progress logic guarantees continuous execution, prompting the caller or retrieving missing context proactively.\n\n### 7. Canonical Few-Shot Examples\n- **Standard**: For complex output structures, status reports, or reasoning patterns, provide clean, canonical few-shot examples wrapped in `<examples>` tags, using `<thinking>` blocks when demonstrating multi-step reasoning.\n- **Rationale**: Few-shot examples ground the model's output formatting far more effectively than abstract rules alone.\n\n### 8. Security Guardrails as High-Rigor Engineering Standards\n- **Standard**: Frame security guardrails (credential protection, input sanitization, worktree isolation, commit history integrity) as standard software craftsmanship and high-rigor engineering practices.\n- **Rationale**: Framing safeguards as standard professional practices integrates security seamlessly without triggering timid refusal behavior.\n\n---\n\n## Verification & Audit Standard\n\nPrior to merging any new or updated prompt file (`*.md` agents, system prompts, or skills), verify compliance against the 8-point checklist above.\n",
|
|
@@ -114,6 +114,7 @@ export const EMBEDDED_DOCS: Readonly<Record<string, string>> = {
|
|
|
114
114
|
"en/configuration/config-usage.md": "---\ntitle: Configuration Discovery and Resolution\ndescription: How xcsh discovers, resolves, and layers configuration from project, user, and enterprise roots.\nsidebar:\n order: 1\n label: Configuration\n---\n\n# Configuration Discovery and Resolution\n\nThis document describes how the coding-agent resolves configuration today: which roots are scanned, how precedence works, and how resolved config is consumed by settings, skills, hooks, tools, and extensions.\n\n## Scope\n\nPrimary implementation:\n\n- `src/config.ts`\n- `src/config/settings.ts`\n- `src/config/settings-schema.ts`\n- `src/discovery/builtin.ts`\n- `src/discovery/helpers.ts`\n\nKey integration points:\n\n- `src/capability/index.ts`\n- `src/discovery/index.ts`\n- `src/extensibility/skills.ts`\n- `src/extensibility/hooks/loader.ts`\n- `src/extensibility/custom-tools/loader.ts`\n- `src/extensibility/extensions/loader.ts`\n\n---\n\n## Resolution flow (visual)\n\n```text\n Config roots (ordered)\n┌───────────────────────────────────────┐\n│ 1) ~/.xcsh/agent + <cwd>/.xcsh │\n│ 2) ~/.claude + <cwd>/.claude │\n│ 3) ~/.codex + <cwd>/.codex │\n│ 4) ~/.gemini + <cwd>/.gemini │\n└───────────────────────────────────────┘\n │\n ▼\n config.ts helper resolution\n (getConfigDirs/findConfigFile/findNearest...)\n │\n ▼\n capability providers enumerate items\n (native, claude, codex, gemini, agents, etc.)\n │\n ▼\n priority sort + per-capability dedup\n │\n ▼\n subsystem-specific consumption\n (settings, skills, hooks, tools, extensions)\n```\n\n## 1) Config roots and source order\n\n## Canonical roots\n\n`src/config.ts` defines a fixed source priority list:\n\n1. `.xcsh` (native)\n2. `.claude`\n3. `.codex`\n4. `.gemini`\n\nUser-level bases:\n\n- `~/.xcsh/agent`\n- `~/.claude`\n- `~/.codex`\n- `~/.gemini`\n\nProject-level bases:\n\n- `<cwd>/.xcsh`\n- `<cwd>/.claude`\n- `<cwd>/.codex`\n- `<cwd>/.gemini`\n\n`CONFIG_DIR_NAME` is `.xcsh` (`packages/utils/src/dirs.ts`).\n\n## Important constraint\n\nThe generic helpers in `src/config.ts` do **not** include `.pi` in source discovery order.\n\n---\n\n## 2) Core discovery helpers (`src/config.ts`)\n\n## `getConfigDirs(subpath, options)`\n\nReturns ordered entries:\n\n- User-level entries first (by source priority)\n- Then project-level entries (by same source priority)\n\nOptions:\n\n- `user` (default `true`)\n- `project` (default `true`)\n- `cwd` (default `getProjectDir()`)\n- `existingOnly` (default `false`)\n\nThis API is used for directory-based config lookups (commands, hooks, tools, agents, etc.).\n\n## `findConfigFile(subpath, options)` / `findConfigFileWithMeta(...)`\n\nSearches for the first existing file across ordered bases, returns first match (path-only or path+metadata).\n\n## `findAllNearestProjectConfigDirs(subpath, cwd)`\n\nWalks parent directories upward and returns the **nearest existing directory per source base** (`.xcsh`, `.claude`, `.codex`, `.gemini`), then sorts results by source priority.\n\nUse this when project config should be inherited from ancestor directories (monorepo/nested workspace behavior).\n\n---\n\n## 3) File config wrapper (`ConfigFile<T>` in `src/config.ts`)\n\n`ConfigFile<T>` is the schema-validated loader for single config files.\n\nSupported formats:\n\n- `.yml` / `.yaml`\n- `.json` / `.jsonc`\n\nBehavior:\n\n- Validates parsed data with AJV against a provided TypeBox schema.\n- Caches load result until `invalidate()`.\n- Returns tri-state result via `tryLoad()`:\n - `ok`\n - `not-found`\n - `error` (`ConfigError` with schema/parse context)\n\nLegacy migration still supported:\n\n- If target path is `.yml`/`.yaml`, a sibling `.json` is auto-migrated once (`migrateJsonToYml`).\n\n---\n\n## 4) Settings resolution model (`src/config/settings.ts`)\n\nThe runtime settings model is layered:\n\n1. Global settings: `~/.xcsh/agent/config.yml`\n2. Project settings: discovered via settings capability (`settings.json` from providers)\n3. Runtime overrides: in-memory, non-persistent\n4. Schema defaults: from `SETTINGS_SCHEMA`\n\nEffective read path:\n\n`defaults <- global <- project <- overrides`\n\nWrite behavior:\n\n- `settings.set(...)` writes to the **global** layer (`config.yml`) and queues background save.\n- Project settings are read-only from capability discovery.\n\n## Migration behavior still active\n\nOn startup, if `config.yml` is missing:\n\n1. Migrate from `~/.xcsh/agent/settings.json` (renamed to `.bak` on success)\n2. Merge with legacy DB settings from `agent.db`\n3. Write merged result to `config.yml`\n\nField-level migrations in `#migrateRawSettings`:\n\n- `queueMode` -> `steeringMode`\n- `ask.timeout` milliseconds -> seconds when old value looks like ms (`> 1000`)\n- Legacy flat `theme: \"...\"` -> `theme.dark/theme.light` structure\n\n---\n\n## 5) Capability/discovery integration\n\nMost non-core config loading flows through the capability registry (`src/capability/index.ts` + `src/discovery/index.ts`).\n\n## Provider ordering\n\nProviders are sorted by numeric priority (higher first). Example priorities:\n\n- Native OMP (`builtin.ts`): `100`\n- Claude: `80`\n- Codex / agents / Claude marketplace: `70`\n- Gemini: `60`\n\n```text\nProvider precedence (higher wins)\n\nnative (.xcsh) priority 100\nclaude priority 80\ncodex / agents / ... priority 70\ngemini priority 60\n```\n\n## Dedup semantics\n\nCapabilities define a `key(item)`:\n\n- same key => first item wins (higher-priority/earlier-loaded item)\n- no key (`undefined`) => no dedup, all items retained\n\nRelevant keys:\n\n- skills: `name`\n- tools: `name`\n- hooks: `${type}:${tool}:${name}`\n- extension modules: `name`\n- extensions: `name`\n- settings: no dedup (all items preserved)\n\n---\n\n## 6) Native `.xcsh` provider behavior (`src/discovery/builtin.ts`)\n\nNative provider (`id: native`) reads from:\n\n- project: `<cwd>/.xcsh/...`\n- user: `~/.xcsh/agent/...`\n\n### Directory admission rule\n\n`builtin.ts` only includes a config root if the directory exists **and is non-empty** (`ifNonEmptyDir`).\n\n### Scope-specific loading\n\n- Skills: `skills/*/SKILL.md`\n- Slash commands: `commands/*.md`\n- Rules: `rules/*.{md,mdc}`\n- Prompts: `prompts/*.md`\n- Instructions: `instructions/*.md`\n- Hooks: `hooks/pre/*`, `hooks/post/*`\n- Tools: `tools/*.json|*.md` and `tools/<name>/index.ts`\n- Extension modules: discovered under `extensions/` (+ legacy `settings.json.extensions` string array)\n- Extensions: `extensions/<name>/gemini-extension.json`\n- Settings capability: `settings.json`\n\n### Nearest-project lookup nuance\n\nFor `SYSTEM.md` and `XCSH.md`, native provider uses nearest-ancestor project `.xcsh` directory search (walk-up) but still requires the `.xcsh` dir to be non-empty.\n\n---\n\n## 7) How major subsystems consume config\n\n## Settings subsystem\n\n- `Settings.init()` loads global `config.yml` + discovered project `settings.json` capability items.\n- Only capability items with `level === \"project\"` are merged into project layer.\n\n## Skills subsystem\n\n- `extensibility/skills.ts` loads via `loadCapability(skillCapability.id, { cwd })`.\n- Applies source toggles and filters (`ignoredSkills`, `includeSkills`, custom dirs).\n- Legacy-named toggles still exist (`skills.enablePiUser`, `skills.enablePiProject`) but they gate the native provider (`provider === \"native\"`).\n\n## Hooks subsystem\n\n- `discoverAndLoadHooks()` resolves hook paths from hook capability + explicit configured paths.\n- Then loads modules via Bun import.\n\n## Tools subsystem\n\n- `discoverAndLoadCustomTools()` resolves tool paths from tool capability + plugin tool paths + explicit configured paths.\n- Declarative `.md/.json` tool files are metadata only; executable loading expects code modules.\n\n## Extensions subsystem\n\n- `discoverAndLoadExtensions()` resolves extension modules from extension-module capability plus explicit paths.\n- Current implementation intentionally keeps only capability items with `_source.provider === \"native\"` before loading.\n\n---\n\n## 8) Precedence rules to rely on\n\nUse this mental model:\n\n1. Source directory ordering from `config.ts` determines candidate path order.\n2. Capability provider priority determines cross-provider precedence.\n3. Capability key dedup determines collision behavior (first wins for keyed capabilities).\n4. Subsystem-specific merge logic can further change effective precedence (especially settings).\n\n### Settings-specific caveat\n\nSettings capability items are not deduplicated; `Settings.#loadProjectSettings()` deep-merges project items in returned order. Because merge applies later item values over earlier values, effective override behavior depends on provider emission order, not just capability key semantics.\n\n---\n\n## 9) Legacy/compatibility behaviors still present\n\n- `ConfigFile` JSON -> YAML migration for YAML-targeted files.\n- Settings migration from `settings.json` and `agent.db` to `config.yml`.\n- Settings key migrations (`queueMode`, `ask.timeout`, flat `theme`).\n- Extension manifest compatibility: loader accepts both `package.json.xcsh` and `package.json.pi` manifest sections.\n- Legacy setting names `skills.enablePiUser` / `skills.enablePiProject` are still active gates for native skill source.\n\nIf these compatibility paths are removed in code, update this document immediately; several runtime behaviors still depend on them today.\n",
|
|
115
115
|
"en/configuration/environment-variables.md": "---\ntitle: Environment Variables\ndescription: Runtime environment variable reference for xcsh configuration and behavior control.\nsidebar:\n order: 2\n label: Environment variables\n---\n\n# Environment Variables (Current Runtime Reference)\n\nThis reference is derived from current code paths in:\n\n- `packages/coding-agent/src/**`\n- `packages/ai/src/**` (provider/auth resolution used by coding-agent)\n- `packages/utils/src/**` and `packages/tui/src/**` where those vars directly affect coding-agent runtime\n\nIt documents only active behavior.\n\n## Resolution model and precedence\n\nMost runtime lookups use `$env` from `@f5-sales-demo/pi-utils` (`packages/utils/src/env.ts`).\n\n`$env` loading order:\n\n1. Existing process environment (`Bun.env`)\n2. Project `.env` (`$PWD/.env`) for keys not already set\n3. Home `.env` (`~/.env`) for keys not already set\n\nAdditional rule in `.env` files: `XCSH_*` keys are mirrored to `PI_*` keys during parse.\n\n---\n\n## 1) Model/provider authentication\n\nThese are consumed via `getEnvApiKey()` (`packages/ai/src/stream.ts`) unless noted otherwise.\n\n### Core provider credentials\n\n| Variable | Used for | Required when | Notes / precedence |\n|---------------------------------|---|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|\n| `ANTHROPIC_OAUTH_TOKEN` | Anthropic API auth | Using Anthropic with OAuth token auth | Takes precedence over `ANTHROPIC_API_KEY` for provider auth resolution |\n| `ANTHROPIC_API_KEY` | Anthropic API auth | Using Anthropic without OAuth token | Fallback after `ANTHROPIC_OAUTH_TOKEN` |\n| `ANTHROPIC_FOUNDRY_API_KEY` | Anthropic via Azure Foundry / enterprise gateway | `CLAUDE_CODE_USE_FOUNDRY` enabled | Takes precedence over `ANTHROPIC_OAUTH_TOKEN` and `ANTHROPIC_API_KEY` when Foundry mode is enabled |\n| `OPENAI_API_KEY` | OpenAI auth | Using OpenAI-family providers without explicit apiKey argument | Used by OpenAI Completions/Responses providers |\n| `GEMINI_API_KEY` | Google Gemini auth | Using `google` provider models | Primary key for Gemini provider mapping |\n| `GOOGLE_API_KEY` | Gemini image tool auth fallback | Using `gemini_image` tool without `GEMINI_API_KEY` | Used by coding-agent image tool fallback path |\n| `GROQ_API_KEY` | Groq auth | Using Groq models | |\n| `CEREBRAS_API_KEY` | Cerebras auth | Using Cerebras models | |\n| `TOGETHER_API_KEY` | Together auth | Using `together` provider | |\n| `HUGGINGFACE_HUB_TOKEN` | Hugging Face auth | Using `huggingface` provider | Primary Hugging Face token env var |\n| `HF_TOKEN` | Hugging Face auth | Using `huggingface` provider | Fallback when `HUGGINGFACE_HUB_TOKEN` is unset |\n| `SYNTHETIC_API_KEY` | Synthetic auth | Using Synthetic models | |\n| `NVIDIA_API_KEY` | NVIDIA auth | Using `nvidia` provider | |\n| `NANO_GPT_API_KEY` | NanoGPT auth | Using `nanogpt` provider | |\n| `VENICE_API_KEY` | Venice auth | Using `venice` provider | |\n| `LITELLM_API_KEY` | LiteLLM auth | Using `litellm` provider | OpenAI-compatible LiteLLM proxy key. When set with `LITELLM_BASE_URL`, enables auto-config of `models.yml` |\n| `LM_STUDIO_API_KEY` | LM Studio auth (optional) | Using `lm-studio` provider with authenticated hosts | Local LM Studio usually runs without auth; any non-empty token works when a key is required |\n| `OLLAMA_API_KEY` | Ollama auth (optional) | Using `ollama` provider with authenticated hosts | Local Ollama usually runs without auth; any non-empty token works when a key is required |\n| `LLAMA_CPP_API_KEY` | Ollama auth (optional) | Using `llama-server` with `--api-key` parameter | Local llama.cpp usually runs without auth; any non-empty token works when a key is configured |\n| `XIAOMI_API_KEY` | Xiaomi MiMo auth | Using `xiaomi` provider | |\n| `MOONSHOT_API_KEY` | Moonshot auth | Using `moonshot` provider | |\n| `XAI_API_KEY` | xAI auth | Using xAI models | |\n| `OPENROUTER_API_KEY` | OpenRouter auth | Using OpenRouter models | Also used by image tool when preferred/auto provider is OpenRouter |\n| `MISTRAL_API_KEY` | Mistral auth | Using Mistral models | |\n| `ZAI_API_KEY` | z.ai auth | Using z.ai models | Also used by z.ai web search provider |\n| `MINIMAX_API_KEY` | MiniMax auth | Using `minimax` provider | |\n| `MINIMAX_CODE_API_KEY` | MiniMax Code auth | Using `minimax-code` provider | |\n| `MINIMAX_CODE_CN_API_KEY` | MiniMax Code CN auth | Using `minimax-code-cn` provider | |\n| `OPENCODE_API_KEY` | OpenCode auth | Using OpenCode models | |\n| `QIANFAN_API_KEY` | Qianfan auth | Using `qianfan` provider | |\n| `QWEN_OAUTH_TOKEN` | Qwen Portal auth | Using `qwen-portal` with OAuth token | Takes precedence over `QWEN_PORTAL_API_KEY` |\n| `QWEN_PORTAL_API_KEY` | Qwen Portal auth | Using `qwen-portal` with API key | Fallback after `QWEN_OAUTH_TOKEN` |\n| `ZENMUX_API_KEY` | ZenMux auth | Using `zenmux` provider | Used for ZenMux OpenAI and Anthropic-compatible routes |\n| `VLLM_API_KEY` | vLLM auth/discovery opt-in | Using `vllm` provider (local OpenAI-compatible servers) | Any non-empty value works for no-auth local servers |\n| `CURSOR_ACCESS_TOKEN` | Cursor provider auth | Using Cursor provider | |\n| `AI_GATEWAY_API_KEY` | Vercel AI Gateway auth | Using `vercel-ai-gateway` provider | |\n| `CLOUDFLARE_AI_GATEWAY_API_KEY` | Cloudflare AI Gateway auth | Using `cloudflare-ai-gateway` provider | Base URL must be configured as `https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/anthropic` |\n\n### GitHub/Copilot token chains\n\n| Variable | Used for | Chain |\n|---|---|---|\n| `COPILOT_GITHUB_TOKEN` | GitHub Copilot provider auth | `COPILOT_GITHUB_TOKEN` → `GH_TOKEN` → `GITHUB_TOKEN` |\n| `GH_TOKEN` | Copilot fallback; GitHub API auth in web scraper | In web scraper: `GITHUB_TOKEN` → `GH_TOKEN` |\n| `GITHUB_TOKEN` | Copilot fallback; GitHub API auth in web scraper | In web scraper: checked before `GH_TOKEN` |\n\n---\n\n## 2) Provider-specific runtime configuration\n\n### Anthropic Foundry Gateway (Azure / enterprise proxy)\n\nWhen `CLAUDE_CODE_USE_FOUNDRY` is enabled, Anthropic requests switch to Foundry mode:\n\n- Base URL resolves from `FOUNDRY_BASE_URL` (fallback remains model/default base URL if unset).\n- API key resolution for provider `anthropic` becomes:\n `ANTHROPIC_FOUNDRY_API_KEY` → `ANTHROPIC_OAUTH_TOKEN` → `ANTHROPIC_API_KEY`.\n- `ANTHROPIC_CUSTOM_HEADERS` is parsed as comma/newline-separated `key: value` pairs and merged into request headers.\n- TLS client/server material can be injected from env values:\n `NODE_EXTRA_CA_CERTS`, `CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`.\n Each accepts either:\n - a filesystem path to PEM content, or\n - inline PEM (including escaped `\\n` sequences).\n\n| Variable | Value type | Behavior |\n|---|---|---|\n| `CLAUDE_CODE_USE_FOUNDRY` | Boolean-like string (`1`, `true`, `yes`, `on`) | Enables Foundry mode for Anthropic provider |\n| `FOUNDRY_BASE_URL` | URL string | Anthropic endpoint base URL in Foundry mode |\n| `ANTHROPIC_FOUNDRY_API_KEY` | Token string | Used for `Authorization: Bearer <token>` |\n| `ANTHROPIC_CUSTOM_HEADERS` | Header list string | Extra headers; format `header-a: value, header-b: value` or newline-separated |\n| `NODE_EXTRA_CA_CERTS` | PEM path or inline PEM | Extra CA chain for server certificate validation |\n| `CLAUDE_CODE_CLIENT_CERT` | PEM path or inline PEM | mTLS client certificate |\n| `CLAUDE_CODE_CLIENT_KEY` | PEM path or inline PEM | mTLS client private key (must be paired with cert) |\n\n### Amazon Bedrock\n\n| Variable | Default / behavior |\n|---|---|\n| `AWS_REGION` | Primary region source |\n| `AWS_DEFAULT_REGION` | Fallback if `AWS_REGION` unset |\n| `AWS_PROFILE` | Enables named profile auth path |\n| `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` | Enables IAM key auth path |\n| `AWS_BEARER_TOKEN_BEDROCK` | Enables bearer token auth path |\n| `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` / `AWS_CONTAINER_CREDENTIALS_FULL_URI` | Enables ECS task credential path |\n| `AWS_WEB_IDENTITY_TOKEN_FILE` + `AWS_ROLE_ARN` | Enables web identity auth path |\n| `AWS_BEDROCK_SKIP_AUTH` | If `1`, injects dummy credentials (proxy/non-auth scenarios) |\n| `AWS_BEDROCK_FORCE_HTTP1` | If `1`, forces Node HTTP/1 request handler |\n\nRegion fallback in provider code: `options.region` → `AWS_REGION` → `AWS_DEFAULT_REGION` → `us-east-1`.\n\n### Azure OpenAI Responses\n\n| Variable | Default / behavior |\n|---|---|\n| `AZURE_OPENAI_API_KEY` | Required unless API key passed as option |\n| `AZURE_OPENAI_API_VERSION` | Default `v1` |\n| `AZURE_OPENAI_BASE_URL` | Direct base URL override |\n| `AZURE_OPENAI_RESOURCE_NAME` | Used to construct base URL: `https://<resource>.openai.azure.com/openai/v1` |\n| `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` | Optional mapping string: `modelId=deploymentName,model2=deployment2` |\n\nBase URL resolution: option `azureBaseUrl` → env `AZURE_OPENAI_BASE_URL` → option/env resource name → `model.baseUrl`.\n\n### Google Vertex AI\n\n| Variable | Required? | Notes |\n|---|---|---|\n| `GOOGLE_CLOUD_PROJECT` | Yes (unless passed in options) | Fallback: `GCLOUD_PROJECT` |\n| `GCLOUD_PROJECT` | Fallback | Used as alternate project ID source |\n| `GOOGLE_CLOUD_LOCATION` | Yes (unless passed in options) | No default in provider |\n| `GOOGLE_APPLICATION_CREDENTIALS` | Conditional | If set, file must exist; otherwise ADC fallback path is checked (`~/.config/gcloud/application_default_credentials.json`) |\n\n### Kimi\n\n| Variable | Default / behavior |\n|---|---|\n| `KIMI_CODE_OAUTH_HOST` | Primary OAuth host override |\n| `KIMI_OAUTH_HOST` | Fallback OAuth host override |\n| `KIMI_CODE_BASE_URL` | Overrides Kimi usage endpoint base URL (`usage/kimi.ts`) |\n\nOAuth host chain: `KIMI_CODE_OAUTH_HOST` → `KIMI_OAUTH_HOST` → `https://auth.kimi.com`.\n\n### Antigravity/Gemini image compatibility\n\n| Variable | Default / behavior |\n|---|---|\n| `PI_AI_ANTIGRAVITY_VERSION` | Overrides Antigravity user-agent version tag in Gemini CLI provider |\n\n### OpenAI Codex responses (feature/debug controls)\n\n| Variable | Behavior |\n|---|---|\n| `PI_CODEX_DEBUG` | `1`/`true` enables Codex provider debug logging |\n| `PI_CODEX_WEBSOCKET` | `1`/`true` enables websocket transport preference |\n| `PI_CODEX_WEBSOCKET_V2` | `1`/`true` enables websocket v2 path |\n| `PI_CODEX_WEBSOCKET_IDLE_TIMEOUT_MS` | Positive integer override (default 300000) |\n| `PI_CODEX_WEBSOCKET_RETRY_BUDGET` | Non-negative integer override (default 5) |\n| `PI_CODEX_WEBSOCKET_RETRY_DELAY_MS` | Positive integer base backoff override (default 500) |\n\n### Cursor provider debug\n\n| Variable | Behavior |\n|---|---|\n| `DEBUG_CURSOR` | Enables provider debug logs; `2`/`verbose` for detailed payload snippets |\n| `DEBUG_CURSOR_LOG` | Optional file path for JSONL debug log output |\n\n### Prompt cache compatibility switch\n\n| Variable | Behavior |\n|---|---|\n| `PI_CACHE_RETENTION` | If `long`, enables long retention where supported (`anthropic`, `openai-responses`, Bedrock retention resolution) |\n\n---\n\n## 3) Web search subsystem\n\n### Search provider credentials\n\n| Variable | Used by |\n|---|---|\n| `EXA_API_KEY` | Exa search provider and Exa MCP tools |\n| `BRAVE_API_KEY` | Brave search provider |\n| `PERPLEXITY_API_KEY` | Perplexity search provider API-key mode |\n| `TAVILY_API_KEY` | Tavily search provider |\n| `ZAI_API_KEY` | z.ai search provider (also checks stored OAuth in `agent.db`) |\n| `OPENAI_API_KEY` / Codex OAuth in DB | Codex search provider availability/auth |\n\n### Anthropic web search auth chain\n\n`packages/coding-agent/src/web/search/auth.ts` resolves Anthropic web-search credentials in this order:\n\n1. `ANTHROPIC_SEARCH_API_KEY` (+ optional `ANTHROPIC_SEARCH_BASE_URL`)\n2. `models.json` provider entry with `api: \"anthropic-messages\"`\n3. Anthropic OAuth credentials from `agent.db` (must not expire within 5-minute buffer)\n4. Generic Anthropic env fallback: provider key (`ANTHROPIC_FOUNDRY_API_KEY`/`ANTHROPIC_OAUTH_TOKEN`/`ANTHROPIC_API_KEY`) + optional `ANTHROPIC_BASE_URL` (`FOUNDRY_BASE_URL` when Foundry mode is enabled)\n\nRelated vars:\n\n| Variable | Default / behavior |\n|---|---|\n| `ANTHROPIC_SEARCH_API_KEY` | Highest-priority explicit search key |\n| `ANTHROPIC_SEARCH_BASE_URL` | Defaults to `https://api.anthropic.com` when omitted |\n| `ANTHROPIC_SEARCH_MODEL` | Defaults to `claude-haiku-4-5` |\n| `ANTHROPIC_BASE_URL` | Generic fallback base URL for tier-4 auth path |\n\n### Perplexity OAuth flow behavior flag\n\n| Variable | Behavior |\n|---|---|\n| `PI_AUTH_NO_BORROW` | If set, disables macOS native-app token borrowing path in Perplexity login flow |\n\n---\n\n## 4) Python tooling and kernel runtime\n\n| Variable | Default / behavior |\n|---|---|\n| `PI_PY` | Python tool mode override: `0`/`bash`=`bash-only`, `1`/`py`=`ipy-only`, `mix`/`both`=`both`; invalid values ignored |\n| `PI_PYTHON_SKIP_CHECK` | If `1`, skips Python kernel availability checks/warm checks |\n| `PI_PYTHON_GATEWAY_URL` | If set, uses external kernel gateway instead of local shared gateway |\n| `PI_PYTHON_GATEWAY_TOKEN` | Optional auth token for external gateway (`Authorization: token <value>`) |\n| `PI_PYTHON_IPC_TRACE` | If `1`, enables low-level IPC trace path in kernel module |\n| `VIRTUAL_ENV` | Highest-priority venv path for Python runtime resolution |\n\nExtra conditional behavior:\n\n- If `BUN_ENV=test` or `NODE_ENV=test`, Python availability checks are treated as OK and warming is skipped.\n- Python env filtering denies common API keys and allows safe base vars + `LC_`, `XDG_`, `PI_` prefixes.\n\n---\n\n## 5) Agent/runtime behavior toggles\n\n| Variable | Default / behavior |\n|----------------------------|----------------------------------------------------------------------------------------------|\n| `PI_SMOL_MODEL` | Ephemeral model-role override for `smol` (CLI `--smol` takes precedence) |\n| `PI_SLOW_MODEL` | Ephemeral model-role override for `slow` (CLI `--slow` takes precedence) |\n| `PI_PLAN_MODEL` | Ephemeral model-role override for `plan` (CLI `--plan` takes precedence) |\n| `PI_NO_TITLE` | If set (any non-empty value), disables auto session title generation on first user message |\n| `NULL_PROMPT` | If `true`, system prompt builder returns empty string |\n| `PI_BLOCKED_AGENT` | Blocks a specific subagent type in task tool |\n| `PI_SUBPROCESS_CMD` | Overrides subagent spawn command (`xcsh` / `xcsh.cmd` resolution bypass) |\n| `PI_TASK_MAX_OUTPUT_BYTES` | Max captured output bytes per subagent (default `500000`) |\n| `PI_TASK_MAX_OUTPUT_LINES` | Max captured output lines per subagent (default `5000`) |\n| `PI_TIMING` | If `1`, enables startup/tool timing instrumentation logs |\n| `PI_DEBUG_STARTUP` | Enables startup stage debug prints to stderr in multiple startup paths |\n| `PI_PACKAGE_DIR` | Overrides package asset base dir resolution (docs/examples/changelog path lookup) |\n| `PI_DISABLE_LSPMUX` | If `1`, disables lspmux detection/integration and forces direct LSP server spawning |\n| `LITELLM_BASE_URL` | LiteLLM proxy base URL. When set with `LITELLM_API_KEY`, triggers auto-generation of `models.yml` on first run and self-healing on every startup |\n| `LM_STUDIO_BASE_URL` | Default implicit LM Studio discovery base URL override (`http://127.0.0.1:1234/v1` if unset) |\n| `OLLAMA_BASE_URL` | Default implicit Ollama discovery base URL override (`http://127.0.0.1:11434` if unset) |\n| `LLAMA_CPP_BASE_URL` | Default implicit Llama.cpp discovery base URL override (`http://127.0.0.1:8080` if unset) |\n| `PI_EDIT_VARIANT` | If `hashline`, forces hashline read/grep display mode when edit tool available |\n| `PI_NO_PTY` | If `1`, disables interactive PTY path for bash tool |\n\n`PI_NO_PTY` is also set internally when CLI `--no-pty` is used.\n\n---\n\n## 6) Storage and config root paths\n\nThese are consumed via `@f5-sales-demo/pi-utils/dirs` and affect where coding-agent stores data.\n\n| Variable | Default / behavior |\n|---|---|\n| `PI_CONFIG_DIR` | Config root dirname under home (default `.xcsh`) |\n| `PI_CODING_AGENT_DIR` | Full override for agent directory (default `~/<PI_CONFIG_DIR or .xcsh>/agent`) |\n| `PWD` | Used when matching canonical current working directory in path helpers |\n\n---\n\n## 7) Shell/tool execution environment\n\n(From `packages/utils/src/procmgr.ts` and coding-agent bash tool integration.)\n\n| Variable | Behavior |\n|---|---|\n| `PI_BASH_NO_CI` | Suppresses automatic `CI=true` injection into spawned shell env |\n| `CLAUDE_BASH_NO_CI` | Legacy alias fallback for `PI_BASH_NO_CI` |\n| `PI_BASH_NO_LOGIN` | Intended to disable login shell mode |\n| `CLAUDE_BASH_NO_LOGIN` | Legacy alias fallback for `PI_BASH_NO_LOGIN` |\n| `PI_SHELL_PREFIX` | Optional command prefix wrapper |\n| `CLAUDE_CODE_SHELL_PREFIX` | Legacy alias fallback for `PI_SHELL_PREFIX` |\n| `VISUAL` | Preferred external editor command |\n| `EDITOR` | Fallback external editor command |\n\nCurrent implementation note: `PI_BASH_NO_LOGIN`/`CLAUDE_BASH_NO_LOGIN` are read, but current `getShellArgs()` returns `['-l','-c']` in both branches (effectively no-op today).\n\n---\n\n## 8) UI/theme/session detection (auto-detected env)\n\nThese are read as runtime signals; they are usually set by the terminal/OS rather than manually configured.\n\n| Variable | Used for |\n|---|---|\n| `COLORTERM`, `TERM`, `WT_SESSION` | Color capability detection (theme color mode) |\n| `COLORFGBG` | Terminal background light/dark auto-detection |\n| `TERM_PROGRAM`, `TERM_PROGRAM_VERSION`, `TERMINAL_EMULATOR` | Terminal identity in system prompt/context |\n| `KDE_FULL_SESSION`, `XDG_CURRENT_DESKTOP`, `DESKTOP_SESSION`, `XDG_SESSION_DESKTOP`, `GDMSESSION`, `WINDOWMANAGER` | Desktop/window-manager detection in system prompt/context |\n| `KITTY_WINDOW_ID`, `TMUX_PANE`, `TERM_SESSION_ID`, `WT_SESSION` | Stable per-terminal session breadcrumb IDs |\n| `SHELL`, `ComSpec`, `TERM_PROGRAM`, `TERM` | System info diagnostics |\n| `APPDATA`, `XDG_CONFIG_HOME` | lspmux config path resolution |\n| `HOME` | Path shortening in MCP command UI |\n\n---\n\n## 9) Native loader/debug flags\n\n| Variable | Behavior |\n|---|---|\n| `PI_DEV` | Enables verbose native addon load diagnostics in `packages/natives` |\n\n## 10) TUI runtime flags (shared package, affects coding-agent UX)\n\n| Variable | Behavior |\n|---|---|\n| `PI_NOTIFICATIONS` | `off` / `0` / `false` suppress desktop notifications |\n| `PI_TUI_WRITE_LOG` | If set, logs TUI writes to file |\n| `PI_HARDWARE_CURSOR` | If `1`, enables hardware cursor mode |\n| `PI_CLEAR_ON_SHRINK` | If `1`, clears empty rows when content shrinks |\n| `PI_DEBUG_REDRAW` | If `1`, enables redraw debug logging |\n| `PI_TUI_DEBUG` | If `1`, enables deep TUI debug dump path |\n\n---\n\n## 11) Commit generation controls\n\n| Variable | Behavior |\n|---|---|\n| `PI_COMMIT_TEST_FALLBACK` | If `true` (case-insensitive), force commit fallback generation path |\n| `PI_COMMIT_NO_FALLBACK` | If `true`, disables fallback when agent returns no proposal |\n| `PI_COMMIT_MAP_REDUCE` | If `false`, disables map-reduce commit analysis path |\n| `DEBUG` | If set, commit agent error stack traces are printed |\n\n---\n\n## Security-sensitive variables\n\nTreat these as secrets; do not log or commit them:\n\n- Provider/API keys and OAuth/bearer credentials (all `*_API_KEY`, `*_TOKEN`, OAuth access/refresh tokens)\n- Cloud credentials (`AWS_*`, `GOOGLE_APPLICATION_CREDENTIALS` path may expose service-account material)\n- Search/provider auth vars (`EXA_API_KEY`, `BRAVE_API_KEY`, `PERPLEXITY_API_KEY`, Anthropic search keys)\n- Foundry mTLS material (`CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`, `NODE_EXTRA_CA_CERTS` when it points to private CA bundles)\n\nPython runtime also explicitly strips many common key vars before spawning kernel subprocesses (`packages/coding-agent/src/ipy/runtime.ts`).\n",
|
|
116
116
|
"en/configuration/fs-scan-cache-architecture.md": "---\ntitle: Filesystem Scan Cache Architecture\ndescription: Filesystem scan cache contract for fast file discovery with stale-while-revalidate semantics.\nsidebar:\n order: 8\n label: Filesystem scan cache\n---\n\n# Filesystem Scan Cache Architecture Contract\n\nThis document defines the current contract for the shared filesystem scan cache implemented in Rust (`crates/pi-natives/src/fs_cache.rs`) and consumed by native discovery/search APIs exposed to `packages/coding-agent`.\n\n## What this cache is\n\nThe cache stores full directory-scan entry lists (`GlobMatch[]`) keyed by scan scope and traversal policy, then lets higher-level operations (glob filtering, fuzzy scoring, grep file selection) run against those cached entries.\n\nPrimary goals:\n\n- avoid repeated filesystem walks for repeated discovery/search calls\n- keep consistency across `glob`, `fuzzyFind`, and `grep` when they share the same scan policy\n- allow explicit staleness recovery for empty results and explicit invalidation after file mutations\n\n## Ownership and public surface\n\n- Cache implementation and policy: `crates/pi-natives/src/fs_cache.rs`\n- Native consumers:\n - `crates/pi-natives/src/glob.rs`\n - `crates/pi-natives/src/fd.rs` (`fuzzyFind`)\n - `crates/pi-natives/src/grep.rs`\n- JS binding/export:\n - `packages/natives/src/glob/index.ts` (`invalidateFsScanCache`)\n - `packages/natives/src/glob/types.ts`\n - `packages/natives/src/grep/types.ts`\n- Coding-agent mutation invalidation helpers:\n - `packages/coding-agent/src/tools/fs-cache-invalidation.ts`\n\n## Cache key partitioning (hard contract)\n\nEach entry is keyed by:\n\n- canonicalized `root` directory path\n- `include_hidden` boolean\n- `use_gitignore` boolean\n\nImplications:\n\n- Hidden and non-hidden scans do **not** share entries.\n- Gitignore-respecting and ignore-disabled scans do **not** share entries.\n- Consumers must pass stable semantics for hidden/gitignore behavior; changing either flag creates a different cache partition.\n\n`node_modules` inclusion is **not** in the cache key. The cache stores entries with `node_modules` included; per-consumer filtering is applied after retrieval.\n\n## Scan collection behavior\n\nCache population uses a deterministic walker (`ignore::WalkBuilder`) configured by `include_hidden` and `use_gitignore`:\n\n- `follow_links(false)`\n- sorted by file path\n- `.git` is always skipped\n- `node_modules` is always collected at cache-scan time (and optionally filtered later)\n- entry file type + `mtime` are captured via `symlink_metadata`\n\nSearch roots are resolved by `resolve_search_path`:\n\n- relative paths are resolved against current cwd\n- target must be an existing directory\n- root is canonicalized when possible\n\n## Freshness and eviction policy\n\nGlobal policy (environment-overridable):\n\n- `FS_SCAN_CACHE_TTL_MS` (default `1000`)\n- `FS_SCAN_EMPTY_RECHECK_MS` (default `200`)\n- `FS_SCAN_CACHE_MAX_ENTRIES` (default `16`)\n\nBehavior:\n\n- `get_or_scan(...)`\n - if TTL is `0`: bypass cache entirely, always fresh scan (`cache_age_ms = 0`)\n - on cache hit within TTL: return cached entries + non-zero `cache_age_ms`\n - on expired hit: evict key, rescan, store fresh entry\n- max entry enforcement is oldest-first eviction by `created_at`\n\n## Empty-result fast recheck (separate from normal hits)\n\nNormal cache hit:\n\n- a cache hit inside TTL returns cached entries and does nothing else.\n\nEmpty-result fast recheck:\n\n- this is a **caller-side** policy using `ScanResult.cache_age_ms`\n- if filtered/query result is empty and cached scan age is at least `empty_recheck_ms()`, caller performs one `force_rescan(...)` and retries\n- intended to reduce stale-negative results when files were recently added but cache is still within TTL\n\nCurrent consumers:\n\n- `glob`: rechecks when filtered matches are empty and scan age exceeds threshold\n- `fuzzyFind` (`fd.rs`): rechecks only when query is non-empty and scored matches are empty\n- `grep`: rechecks when selected candidate file list is empty\n\n## Consumer defaults and cache usage\n\nCache is opt-in on all exposed APIs (`cache?: boolean`, default `false`).\n\nCurrent defaults in native APIs:\n\n- `glob`: `hidden=false`, `gitignore=true`, `cache=false`\n- `fuzzyFind`: `hidden=false`, `gitignore=true`, `cache=false`\n- `grep`: `hidden=true`, `cache=false`, and cache scan always uses `use_gitignore=true`\n\nCoding-agent callers today:\n\n- High-volume mention candidate discovery enables cache:\n - `packages/coding-agent/src/utils/file-mentions.ts`\n - profile: `hidden=true`, `gitignore=true`, `includeNodeModules=true`, `cache=true`\n- Tool-level `grep` integration currently disables scan cache (`cache: false`):\n - `packages/coding-agent/src/tools/grep.ts`\n\n## Invalidation contract\n\nNative invalidation entrypoint:\n\n- `invalidateFsScanCache(path?: string)`\n - with `path`: remove cache entries whose root is a prefix of target path\n - without path: clear all scan cache entries\n\nPath handling details:\n\n- relative invalidation paths are resolved against cwd\n- invalidation attempts canonicalization\n- if target does not exist (e.g., delete), fallback canonicalizes parent and reattaches filename when possible\n- this preserves invalidation behavior for create/delete/rename where one side may not exist\n\n## Coding-agent mutation flow responsibilities\n\nCoding-agent code must invalidate after successful filesystem mutations.\n\nCentral helpers:\n\n- `invalidateFsScanAfterWrite(path)`\n- `invalidateFsScanAfterDelete(path)`\n- `invalidateFsScanAfterRename(oldPath, newPath)` (invalidates both sides when paths differ)\n\nCurrent mutation tool callsites:\n\n- `packages/coding-agent/src/tools/write.ts`\n- `packages/coding-agent/src/patch/index.ts` (hashline/patch/replace flows)\n\nRule: if a flow mutates filesystem content or location and bypasses these helpers, cache staleness bugs are expected.\n\n## Adding a new cache consumer safely\n\nWhen introducing cache use in a new scanner/search path:\n\n1. **Use stable scan policy inputs**\n - decide hidden/gitignore semantics first\n - pass them consistently to `get_or_scan`/`force_rescan` so cache partitions are intentional\n\n2. **Treat cache data as pre-filtered only by traversal policy**\n - apply tool-specific filtering (glob patterns, type filters, node_modules rules) after retrieval\n - never assume cached entries already reflect your higher-level filters\n\n3. **Implement empty-result fast recheck only for stale-negative risk**\n - use `scan.cache_age_ms >= empty_recheck_ms()`\n - retry once with `force_rescan(..., store=true, ...)`\n - keep this path separate from normal cache-hit logic\n\n4. **Respect no-cache mode explicitly**\n - when caller disables cache, call `force_rescan(..., store=false, ...)`\n - do not populate shared cache in a no-cache request path\n\n5. **Wire mutation invalidation for any new write path**\n - after successful write/edit/delete/rename, call the coding-agent invalidation helper\n - for rename/move, invalidate both old and new paths\n\n6. **Do not add per-call TTL knobs**\n - current contract is global policy only (env-configured), no per-request TTL override\n\n## Known boundaries\n\n- Cache scope is process-local in-memory (`DashMap`), not persisted across process restarts.\n- Cache stores scan entries, not final tool results.\n- `glob`/`fuzzyFind`/`grep` share scan entries only when key dimensions (`root`, `hidden`, `gitignore`) match.\n- `.git` is always excluded at scan collection time regardless of caller options.\n",
|
|
117
|
+
"en/configuration/herdr.md": "---\ntitle: Herdr terminals\ndescription: Run xcsh conversations with isolated, conversation-owned support terminals.\n---\n\nxcsh can bind one conversation to one Herdr workspace and expose named support terminals as tabs. The recommended full rich-media stack is Ghostty with Kitty graphics, Herdr with `experimental.kitty_graphics=true`, and FFmpeg 6 or newer. Other terminals remain usable and receive static media fallbacks.\n\nLaunch a conversation-owned workspace from outside Herdr:\n\n```bash\nxcsh herdr --session my-organization --label project-task -- --model openai/gpt-5\n```\n\nThe launcher creates the workspace, writes a mode-`0600` `HerdrBindingV1` under the user state directory, starts xcsh in the root pane, and attaches to the named Herdr session. Plain `xcsh` remains fully supported; terminal-management operations report unavailable without both a launcher-issued owner binding and `HERDR_SOCKET_PATH`.\n\nInside the conversation, humans use `/terminal` and the model uses `herdr_terminal`. Both surfaces provide `list`, `create`, `run`, `send`, `read`, `wait`, `status`, `focus`, and `close` actions. Creation is always non-focusing. Focus is an explicit action. A busy terminal rejects the first close and requires a second close with `force: true` (or `/terminal close NAME --force`).\n\nOwnership is enforced by a random `xcsh_owner` metadata token on the workspace and each managed pane. xcsh lists, reads, sends to, focuses, and closes only panes carrying the current binding token. Tabs from other conversations or users are not exposed. Session creation, resume, switching, branching, and tree navigation keep the workspace and tabs while refreshing the stored xcsh session reference.\n\nThe socket client uses Herdr protocol 18 over newline-delimited JSON on `HERDR_SOCKET_PATH`. Each request reconnects independently, validates the `ping` protocol version, caps responses at 4 MiB, and uses bounded timeouts so a Herdr restart cannot wedge the chat process.\n",
|
|
117
118
|
"en/configuration/hooks.md": "---\ntitle: Hooks\ndescription: Hook system for pre/post event automation in the coding agent lifecycle.\nsidebar:\n order: 4\n label: Hooks\n---\n\n# Hooks\n\nThis document describes the **current hook subsystem code** in `src/extensibility/hooks/*`.\n\n## Current status in runtime\n\nThe hook package (`src/extensibility/hooks/`) is still exported and usable as an API surface, but the default CLI runtime now initializes the **extension runner** path. In current startup flow:\n\n- `--hook` is treated as an alias for `--extension` (CLI paths are merged into `additionalExtensionPaths`)\n- tools are wrapped by `ExtensionToolWrapper`, not `HookToolWrapper`\n- context transforms and lifecycle emissions go through `ExtensionRunner`\n\nSo this file documents the hook subsystem implementation itself (types/loader/runner/wrapper), including legacy behavior and constraints.\n\n## Key files\n\n- `src/extensibility/hooks/types.ts` — hook context, event types, and result contracts\n- `src/extensibility/hooks/loader.ts` — module loading and hook discovery bridge\n- `src/extensibility/hooks/runner.ts` — event dispatch, command lookup, error signaling\n- `src/extensibility/hooks/tool-wrapper.ts` — pre/post tool interception wrapper\n- `src/extensibility/hooks/index.ts` — exports/re-exports\n\n## What a hook module is\n\nA hook module must default-export a factory:\n\n```ts\nimport type { HookAPI } from \"@f5-sales-demo/xcsh\";\n\nexport default function hook(pi: HookAPI): void {\n pi.on(\"tool_call\", async (event, ctx) => {\n if (event.toolName === \"bash\" && String(event.input.command ?? \"\").includes(\"rm -rf\")) {\n return { block: true, reason: \"blocked by policy\" };\n }\n });\n}\n```\n\nThe factory can:\n\n- register event handlers with `pi.on(...)`\n- send persistent custom messages with `pi.sendMessage(...)`\n- persist non-LLM state with `pi.appendEntry(...)`\n- register slash commands via `pi.registerCommand(...)`\n- register custom message renderers via `pi.registerMessageRenderer(...)`\n- run shell commands via `pi.exec(...)`\n\n## Discovery and loading\n\n`discoverAndLoadHooks(configuredPaths, cwd)` does:\n\n1. Load discovered hooks from capability registry (`loadCapability(\"hooks\")`)\n2. Append explicitly configured paths (deduped by absolute path)\n3. Call `loadHooks(allPaths, cwd)`\n\n`loadHooks` then imports each path and expects a `default` function.\n\n### Path resolution\n\n`loader.ts` resolves hook paths as:\n\n- absolute path: used as-is\n- `~` path: expanded\n- relative path: resolved against `cwd`\n\n### Important legacy mismatch\n\nDiscovery providers for `hookCapability` still model pre/post shell-style hook files (for example `.claude/hooks/pre/*`, `.xcsh/.../hooks/pre/*`).\n\nThe hook loader here uses dynamic module import and requires a default JS/TS hook factory. If a discovered hook path is not importable as a module, load fails and is reported in `LoadHooksResult.errors`.\n\n## Event surfaces\n\nHook events are strongly typed in `types.ts`.\n\n### Session events\n\n- `session_start`\n- `session_before_switch` → can return `{ cancel?: boolean }`\n- `session_switch`\n- `session_before_branch` → can return `{ cancel?: boolean; skipConversationRestore?: boolean }`\n- `session_branch`\n- `session_before_compact` → can return `{ cancel?: boolean; compaction?: CompactionResult }`\n- `session.compacting` → can return `{ context?: string[]; prompt?: string; preserveData?: Record<string, unknown> }`\n- `session_compact`\n- `session_before_tree` → can return `{ cancel?: boolean; summary?: { summary: string; details?: unknown } }`\n- `session_tree`\n- `session_shutdown`\n\n### Agent/context events\n\n- `context` → can return `{ messages?: Message[] }`\n- `before_agent_start` → can return `{ message?: { customType; content; display; details } }`\n- `agent_start`\n- `agent_end`\n- `turn_start`\n- `turn_end`\n- `auto_compaction_start`\n- `auto_compaction_end`\n- `auto_retry_start`\n- `auto_retry_end`\n- `ttsr_triggered`\n- `todo_reminder`\n\n### Tool events (pre/post model)\n\n- `tool_call` (pre-execution) → can return `{ block?: boolean; reason?: string }`\n- `tool_result` (post-execution) → can return `{ content?; details?; isError? }`\n\nThis is the hook subsystem’s core pre/post interception model.\n\n```text\nHook tool interception flow\n\ntool_call handlers\n │\n ├─ any { block: true }? ── yes ──> throw (tool blocked)\n │\n └─ no\n │\n ▼\n execute underlying tool\n │\n ├─ success ──> tool_result handlers can override { content, details }\n │\n └─ error ──> emit tool_result(isError=true) then rethrow original error\n```\n\n## Execution model and mutation semantics\n\n### 1) Pre-execution: `tool_call`\n\n`HookToolWrapper.execute()` emits `tool_call` before tool execution.\n\n- if any handler returns `{ block: true }`, execution stops\n- if handler throws, wrapper fails closed and blocks execution\n- returned `reason` becomes the thrown error text\n\n### 2) Tool execution\n\nUnderlying tool executes normally if not blocked.\n\n### 3) Post-execution: `tool_result`\n\nAfter success, wrapper emits `tool_result` with:\n\n- `toolName`, `toolCallId`, `input`\n- `content`\n- `details`\n- `isError: false`\n\nIf handler returns overrides:\n\n- `content` can replace result content\n- `details` can replace result details\n\nOn tool failure, wrapper emits `tool_result` with `isError: true` and error text content, then rethrows original error.\n\n### What hooks can mutate\n\n- LLM context for a single call via `context` (`messages` replacement chain)\n- tool output content/details on successful tool calls (`tool_result` path)\n- pre-agent injected message via `before_agent_start`\n- cancellation/custom compaction/tree behavior via `session_before_*` and `session.compacting`\n\n### What hooks cannot mutate in this implementation\n\n- raw tool input parameters in-place (only block/allow on `tool_call`)\n- execution continuation after thrown tool errors (error path rethrows)\n- final success/error status in wrapper behavior (returned `isError` is typed but not applied by `HookToolWrapper`)\n\n## Ordering and conflict behavior\n\n### Discovery-level ordering\n\nCapability providers are priority-sorted (higher first). Dedupe is by capability key, first wins.\n\nFor `hooks`, capability key is `${type}:${tool}:${name}`. Shadowed duplicates from lower-priority providers are marked and excluded from effective discovered list.\n\n### Load order\n\n`discoverAndLoadHooks` builds a flat `allPaths` list, deduped by resolved absolute path, then `loadHooks` iterates in that order.\nFile order within each discovered directory depends on `readdir` output; the hook loader does not perform an additional sort.\n\n### Runtime handler order\n\nInside `HookRunner`, order is deterministic by registration sequence:\n\n1. hooks array order\n2. handler registration order per hook/event\n\nConflict behavior by event type:\n\n- `tool_call`: last returned result wins unless a handler blocks; first block short-circuits\n- `tool_result`: last returned override wins (no short-circuit)\n- `context`: chained; each handler receives prior handler’s message output\n- `before_agent_start`: first returned message is kept; later messages ignored\n- `session_before_*`: latest returned result is tracked; `cancel: true` short-circuits immediately\n- `session.compacting`: latest returned result wins\n\nCommand/renderer conflicts:\n\n- `getCommand(name)` returns first match across hooks (first loaded wins)\n- `getMessageRenderer(customType)` returns first match\n- `getRegisteredCommands()` returns all commands (no dedupe)\n\n## UI interactions (`HookContext.ui`)\n\n`HookUIContext` includes:\n\n- `select`, `confirm`, `input`, `editor`\n- `notify`\n- `setStatus`\n- `custom`\n- `setEditorText`, `getEditorText`\n- `theme` getter\n\n`ctx.hasUI` indicates whether interactive UI is available.\n\nWhen running with no UI, the default no-op context behavior is:\n\n- `select/input/editor` return `undefined`\n- `confirm` returns `false`\n- `notify`, `setStatus`, `setEditorText` are no-ops\n- `getEditorText` returns `\"\"`\n\n### Status line behavior\n\nHook status text set via `ctx.ui.setStatus(key, text)` is:\n\n- stored per key\n- sorted by key name\n- sanitized (`\\r`, `\\n`, `\\t` → spaces; repeated spaces collapsed)\n- joined and width-truncated for display\n\n## Error propagation and fallback\n\n### Load-time\n\n- invalid module or missing default export → captured in `LoadHooksResult.errors`\n- loading continues for other hooks\n\n### Event-time\n\n`HookRunner.emit(...)` catches handler errors for most events and emits `HookError` to listeners (`hookPath`, `event`, `error`), then continues.\n\n`emitToolCall(...)` is stricter: handler errors are not swallowed there; they propagate to caller. In `HookToolWrapper`, this blocks the tool call (fail-safe).\n\n## Realistic API examples\n\n### Block unsafe bash commands\n\n```ts\nimport type { HookAPI } from \"@f5-sales-demo/xcsh\";\n\nexport default function (pi: HookAPI): void {\n pi.on(\"tool_call\", async (event, ctx) => {\n if (event.toolName !== \"bash\") return;\n const cmd = String(event.input.command ?? \"\");\n if (!cmd.includes(\"rm -rf\")) return;\n\n if (!ctx.hasUI) return { block: true, reason: \"rm -rf blocked (no UI)\" };\n const ok = await ctx.ui.confirm(\"Dangerous command\", `Allow: ${cmd}`);\n if (!ok) return { block: true, reason: \"user denied command\" };\n });\n}\n```\n\n### Redact tool output on post-execution\n\n```ts\nimport type { HookAPI } from \"@f5-sales-demo/xcsh\";\n\nexport default function (pi: HookAPI): void {\n pi.on(\"tool_result\", async event => {\n if (event.toolName !== \"read\" || event.isError) return;\n\n const redacted = event.content.map(chunk => {\n if (chunk.type !== \"text\") return chunk;\n return { ...chunk, text: chunk.text.replaceAll(/API_KEY=\\S+/g, \"API_KEY=[REDACTED]\") };\n });\n\n return { content: redacted };\n });\n}\n```\n\n### Modify model context per LLM call\n\n```ts\nimport type { HookAPI } from \"@f5-sales-demo/xcsh\";\n\nexport default function (pi: HookAPI): void {\n pi.on(\"context\", async event => {\n const filtered = event.messages.filter(msg => !(msg.role === \"custom\" && msg.customType === \"debug-only\"));\n return { messages: filtered };\n });\n}\n```\n\n### Register slash command with command-safe context methods\n\n```ts\nimport type { HookAPI } from \"@f5-sales-demo/xcsh\";\n\nexport default function (pi: HookAPI): void {\n pi.registerCommand(\"handoff\", {\n description: \"Create a new session with setup message\",\n handler: async (_args, ctx) => {\n await ctx.waitForIdle();\n await ctx.newSession({\n parentSession: ctx.sessionManager.getSessionFile(),\n setup: async sm => {\n sm.appendMessage({\n role: \"user\",\n content: [{ type: \"text\", text: \"Continue from prior session summary.\" }],\n timestamp: Date.now(),\n });\n },\n });\n },\n });\n}\n```\n\n## Export surface\n\n`src/extensibility/hooks/index.ts` exports:\n\n- loading APIs (`discoverAndLoadHooks`, `loadHooks`)\n- runner and wrapper (`HookRunner`, `HookToolWrapper`)\n- all hook types\n- `execCommand` re-export\n\nAnd package root (`src/index.ts`) re-exports hook **types** as a legacy compatibility surface.\n",
|
|
118
119
|
"en/configuration/porting-from-pi-mono.md": "---\ntitle: \"Porting From pi-mono: A Practical Merge Guide\"\ndescription: Practical guide for migrating code from the pi-mono monorepo into the xcsh codebase.\nsidebar:\n order: 9\n label: Porting from pi-mono\n---\n\n# Porting From pi-mono: A Practical Merge Guide\n\nThis guide is a repeatable checklist for porting changes from pi-mono into this repo.\nUse it for any merge: single file, feature branch, or full release sync.\n\n## Last Sync Point\n\n**Commit:** `b21b42d032919de2f2e6920a76fa9a37c3920c0a`\n**Date:** 2026-03-22\n\nUpdate this section after each sync; do not reuse the previous range.\n\nWhen starting a new sync, generate patches from this commit forward:\n\n```bash\ngit format-patch b21b42d032919de2f2e6920a76fa9a37c3920c0a..HEAD --stdout > changes.patch\n```\n\n## 0) Define the scope\n\n- Identify the upstream reference (commit, tag, or PR).\n- List the packages or folders you plan to touch.\n- Decide which features are in-scope and which are intentionally skipped.\n\n## 1) Bring code over safely\n\n- Prefer a clean, focused diff rather than a wholesale copy.\n- Avoid copying built artifacts or generated files.\n- If upstream added new files, add them explicitly and review contents.\n\n## 2) Match import extension conventions\n\nMost runtime TypeScript sources omit `.js` in internal imports, but some test/bench entrypoints keep `.js` for ESM\nruntime compatibility. Follow the local package’s existing style; do not blanket-strip extensions.\n\n- In `packages/coding-agent` runtime sources, keep internal imports extensionless unless importing non-TS assets.\n- In `packages/tui/test` and `packages/natives/bench`, keep `.js` where surrounding files already use it.\n- Keep real file extensions when required by tooling (e.g., `.json`, `.css`, `.md` text embeds).\n- Example: `import { x } from \"./foo.js\";` → `import { x } from \"./foo\";` (only when the package convention is extensionless).\n\n## 3) Replace import scopes\n\nUpstream uses different package scopes. Replace them consistently.\n\n- Replace old scopes with the local scope used here.\n- Examples (adjust to match the actual packages you are porting):\n - `@mariozechner/pi-coding-agent` → `@f5-sales-demo/xcsh`\n - `@mariozechner/pi-agent-core` → `@f5-sales-demo/pi-agent-core`\n - `@mariozechner/pi-tui` → `@f5-sales-demo/pi-tui`\n - `@mariozechner/pi-ai` → `@f5-sales-demo/pi-ai`\n\n## 4) Use Bun APIs where they improve on Node\n\nWe run on Bun. Replace Node APIs only when Bun provides a better alternative.\n\n**DO replace:**\n\n- Process spawning: `child_process.spawn` → Bun Shell `$` for simple commands, `Bun.spawn`/`Bun.spawnSync` for streaming or long-running work\n- File I/O: `fs.readFileSync` → `Bun.file().text()` / `Bun.write()`\n- HTTP clients: `node-fetch`, `axios` → native `fetch`\n- Crypto hashing: `node:crypto` → Web Crypto or `Bun.hash`\n- SQLite: `better-sqlite3` → `bun:sqlite`\n- Env loading: `dotenv` → Bun loads `.env` automatically\n\n**DO NOT replace (these work fine in Bun):**\n\n- `os.homedir()` — do NOT replace with `Bun.env.HOME`, `Bun.env.HOME`, or literal `\"~\"`\n- `os.tmpdir()` — do NOT replace with `Bun.env.TMPDIR || \"/tmp\"` or hardcoded paths\n- `fs.mkdtempSync()` — do NOT replace with manual path construction\n- `path.join()`, `path.resolve()`, etc. — these are fine\n\n**Import style:** Use the `node:` prefix with namespace imports only (no named imports from `node:fs` or `node:path`).\n\n**Additional Bun conventions:**\n\n- Prefer Bun Shell `$` for short, non-streaming commands; use `Bun.spawn` only when you need streaming I/O or process control.\n- Use `Bun.file()`/`Bun.write()` for files and `node:fs/promises` for directories.\n- Avoid `Bun.file().exists()` checks; use `isEnoent` handling in try/catch.\n- Prefer `Bun.sleep(ms)` over `setTimeout` wrappers.\n\n**Wrong:**\n\n```typescript\n// BROKEN: env vars may be undefined, \"~\" is not expanded\nconst home = Bun.env.HOME || \"~\";\nconst tmp = Bun.env.TMPDIR || \"/tmp\";\n```\n\n**Correct:**\n\n```typescript\nimport * as os from \"node:os\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst configDir = path.join(os.homedir(), \".config\", \"myapp\");\nconst tempDir = fs.mkdtempSync(path.join(os.tmpdir(), \"myapp-\"));\n```\n\n## 5) Prefer Bun embeds (no copying)\n\nDo not copy runtime assets or vendor files at build time.\n\n- If upstream copies assets into a dist folder, replace with Bun-friendly embeds.\n- Prompts are static `.md` files; use Bun text imports (`with { type: \"text\" }`) and Handlebars instead of inline prompt strings.\n- Use `import.meta.dir` + `Bun.file` to load adjacent non-text resources.\n- Keep assets in-repo and let the bundler include them.\n- Eliminate copy scripts unless the user explicitly requests them.\n- If upstream reads a bundled fallback file at runtime, replace filesystem reads with a Bun text embed import.\n - Example (Codex instructions fallback):\n - `const FALLBACK_PROMPT_PATH = join(import.meta.dir, \"codex-instructions.md\");` -> removed\n - `import FALLBACK_INSTRUCTIONS from \"./codex-instructions.md\" with { type: \"text\" };`\n - Use `return FALLBACK_INSTRUCTIONS;` instead of `readFileSync(FALLBACK_PROMPT_PATH, \"utf8\")`\n\n## 6) Port `package.json` carefully\n\nTreat `package.json` as a contract. Merge intentionally.\n\n- Keep existing `name`, `version`, `type`, `exports`, and `bin` unless the port requires changes.\n- Replace npm/node scripts with Bun equivalents (e.g., `bun check`, `bun test`).\n- Ensure dependencies use the correct scope.\n- Do not downgrade dependencies to fix type errors; upgrade instead.\n- Validate workspace package links and `peerDependencies`.\n\n## 7) Align code style and tooling\n\n- Keep existing formatting conventions.\n- Do not introduce `any` unless required.\n- Avoid dynamic imports and inline type imports; use top-level imports only.\n- Never build prompts in code; prompts are static `.md` files rendered with Handlebars.\n- In coding-agent, never use `console.log`/`console.warn`/`console.error`; use `logger` from `@f5-sales-demo/pi-utils`.\n- Use `Promise.withResolvers()` instead of `new Promise((resolve, reject) => ...)`.\n- **No `private`/`protected`/`public` keywords on class fields or methods.** Use ES `#` private fields for encapsulation; leave accessible members bare (no keyword).\n The only exception is constructor parameter properties (`constructor(private readonly x: T)`), where the keyword is required by TypeScript. When porting upstream code that uses `private foo` or `protected bar`, convert to `#foo` (private) or bare `bar` (accessible).\n- Prefer existing helpers and utilities over new ad-hoc code.\n- Preserve Bun-first infrastructure changes already made in this repo:\n - Runtime is Bun (no Node entry points).\n - Package manager is Bun (no npm lockfiles).\n - Heavy Node APIs (`child_process`, `readline`) are replaced with Bun equivalents.\n - Lightweight Node APIs (`os.homedir`, `os.tmpdir`, `fs.mkdtempSync`, `path.*`) are kept.\n - CLI shebangs use `bun` (not `node`, not `tsx`).\n - Packages use source files directly (no TypeScript build step).\n - CI workflows run Bun for install/check/test.\n\n## 8) Remove old compatibility layers\n\nUnless requested, remove upstream compatibility shims.\n\n- Delete old APIs that were replaced.\n- Update all call sites to the new API directly.\n- Do not keep `*_v2` or parallel versions.\n\n## 9) Update docs and references\n\n- Replace pi-mono repo links where appropriate.\n- Update examples to use Bun and correct package scopes.\n- Ensure README instructions still match the current repo behavior.\n\n## 10) Validate the port\n\nRun the standard checks after changes:\n\n- `bun check`\n\nIf the repo already has failing checks unrelated to your changes, call that out.\nTests use Bun's runner (not Vitest), but only run `bun test` when explicitly requested.\n\n## 11) Protect improved features (regression trap list)\n\nIf you already improved behavior locally, treat those as **non‑negotiable**. Before porting, write down\nthe improvements and add explicit checks so they don’t get lost in the merge.\n\n- **Freeze the expected behavior**: add a short “before/after” note for each improvement (inputs, outputs,\n defaults, edge cases). This prevents silent rollback.\n- **Map old → new APIs**: if upstream renamed concepts (hooks → extensions, custom tools → tools, etc.),\n ensure every old entry point still wires through. One missed flag or export equals lost functionality.\n- **Verify exports**: check `package.json` `exports`, public types, and barrel files. Upstream ports often\n forget to re-export local additions.\n- **Cover non‑happy paths**: if you fixed error handling, timeouts, or fallback logic, add a test or at\n least a manual checklist that exercises those paths.\n- **Check defaults and config merge order**: improvements often live in defaults. Confirm new defaults\n didn’t revert (e.g., new config precedence, disabled features, tool lists).\n- **Audit env/shell behavior**: if you fixed execution or sandboxing, verify the new path still uses your\n sanitized env and does not reintroduce alias/function overrides.\n- **Re-run targeted samples**: keep a minimal set of \"known good\" examples and run them after the port\n (CLI flags, extension registration, tool execution).\n\n## 12) Detect and handle reworked code\n\nBefore porting a file, check if upstream significantly refactored it:\n\n```bash\n# Compare the file you're about to port against what you have locally\ngit diff HEAD upstream/main -- path/to/file.ts\n```\n\nIf the diff shows the file was **reworked** (not just patched):\n\n- New abstractions, renamed concepts, merged modules, changed data flow\n\nThen you must **read the new implementation thoroughly** before porting. Blind merging of reworked code loses functionality because:\n\nNote: interactive mode was recently split into controllers/utils/types. When backporting related changes, port updates into the individual files we created and ensure `interactive-mode.ts` wiring stays in sync.\n\n1. **Defaults change silently** - A new variable `defaultFoo = [a, b]` may replace an old `getAllFoo()` that returned `[a, b, c, d, e]`.\n\n2. **API options get dropped** - When systems merge (e.g., `hooks` + `customTools` → `extensions`), old options may not wire through to the new implementation.\n\n3. **Code paths go stale** - A renamed concept (e.g., `hookMessage` → `custom`) needs updates in every switch statement, type guard, and handler—not just the definition.\n\n4. **Context/capabilities shrink** - Old APIs may have exposed `{ logger, typebox, pi }` that new APIs forgot to include.\n\n### Semantic porting process\n\nWhen upstream reworked a module:\n\n1. **Read the old implementation** - Understand what it did, what options it accepted, what it exposed.\n\n2. **Read the new implementation** - Understand the new abstractions and how they map to old behavior.\n\n3. **Verify feature parity** - For each capability in the old code, confirm the new code preserves it or explicitly removes it.\n\n4. **Grep for stragglers** - Search for old names/concepts that may have been missed in switch statements, handlers, UI components.\n\n5. **Test the boundaries** - CLI flags, SDK options, event handlers, default values—these are where regressions hide.\n\n### Quick checks\n\n```bash\n# Find all uses of an old concept that may need updating\nrg \"oldConceptName\" --type ts\n\n# Compare default values between versions\ngit show upstream/main:path/to/file.ts | rg \"default|DEFAULT\"\n\n# Check if all enum/union values have handlers\nrg \"case \\\"\" path/to/file.ts\n```\n\n## 13) Quick audit checklist\n\nUse this as a final pass before you finish:\n\n- [ ] Import extensions follow the local package convention (no blanket `.js` stripping)\n- [ ] No Node-only APIs in new/ported code\n- [ ] All package scopes updated\n- [ ] `package.json` scripts use Bun\n- [ ] Prompts are `.md` text imports (no inline prompt strings)\n- [ ] No `console.*` in coding-agent (use `logger`)\n- [ ] Assets load via Bun embed patterns (no copy scripts)\n- [ ] Tests or checks run (or explicitly noted as blocked)\n- [ ] No functionality regressions (see sections 11-12)\n\n## 14) Commit message format\n\nWhen committing a backport, follow the repo format `<type>(scope): <past-tense description>` and keep the commit\nrange in the title.\n\n```\nfix(coding-agent): backported pi-mono changes (<from>..<to>)\n\npackages/<package>:\n- <type>: <description>\n- <type>: <description> (#<issue> by @<contributor>)\n\npackages/<other-package>:\n- <type>: <description>\n```\n\n**Example:**\n\n```\nfix(coding-agent): backported pi-mono changes (9f3eef65f..52532c7c0)\n\npackages/ai:\n- fix: handle \"sensitive\" stop reason from Anthropic API\n- fix: normalize tool call IDs with special characters for Responses API\n- fix: add overflow detection for Bedrock, MiniMax, Kimi providers\n- fix: 429 status is rate limiting, not context overflow\n\npackages/tui:\n- fix: refactored autocomplete state tracking\n- fix: file autocomplete should not trigger on empty text\n- fix: configurable autocomplete max visible items\n- fix: improved table column width calculation with word-aware wrapping\n\npackages/coding-agent:\n- fix: preserve external config.yml edits on save (#1046 by @nicobailonMD)\n- fix: resolve macOS NFD and curly quote variants in file paths\n```\n\n**Rules:**\n\n- Group changes by package\n- Use conventional commit types (`fix`, `feat`, `refactor`, `perf`, `docs`)\n- Include upstream issue/PR numbers and contributor attribution for external contributions\n- The commit range in the title helps track sync points\n\n## 15) Intentional Divergences\n\nOur fork has architectural decisions that differ from upstream. **Do not port these upstream patterns:**\n\n### UI Architecture\n\n| Upstream | Our Fork | Reason |\n| ------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------- |\n| `FooterDataProvider` class | `StatusLineComponent` | Simpler, integrated status line |\n| `ctx.ui.setHeader()` / `ctx.ui.setFooter()` | Stub in non-TUI modes | Implemented in TUI, no-op elsewhere |\n| `ctx.ui.setEditorComponent()` | Stub in non-TUI modes | Implemented in TUI, no-op elsewhere |\n| `InteractiveModeOptions` options object | Positional constructor args (options type still exported) | Keep constructor signature; update the type when upstream adds fields |\n\n### Component Naming\n\n| Upstream | Our Fork |\n| ---------------------------- | ----------------------- |\n| `extension-input.ts` | `hook-input.ts` |\n| `extension-selector.ts` | `hook-selector.ts` |\n| `ExtensionInputComponent` | `HookInputComponent` |\n| `ExtensionSelectorComponent` | `HookSelectorComponent` |\n\n### API Naming\n\n| Upstream | Our Fork | Notes |\n| ---------------------------------------- | ---------------------------------------- | ----------------------------------------- |\n| `sessionManager.appendSessionInfo(name)` | `sessionManager.setSessionName(name)` | We use `sessionName` throughout |\n| `sessionManager.getSessionName()` | `sessionManager.getSessionName()` | Same (we unified to match upstream's RPC) |\n| `agent.sessionName` / `setSessionName()` | `agent.sessionName` / `setSessionName()` | Same |\n\n### File Consolidation\n\n| Upstream | Our Fork | Reason |\n| -------------------------------------------------- | --------------------------------------- | --------------------------------------- |\n| `clipboard.ts` + `clipboard-image.ts` (tool files) | `@f5-sales-demo/pi-natives` clipboard module | Merged into N-API native implementation |\n\n### Test Framework\n\n| Upstream | Our Fork |\n| ------------------------- | ----------------------------- |\n| `vitest` with `vi.mock()` | `bun:test` with `vi` from bun |\n| `node:test` assertions | `expect()` matchers |\n\n### Tool Architecture\n\n| Upstream | Our Fork | Notes |\n| ----------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------- |\n| `createTool(cwd: string, options?)` | `createTools(session: ToolSession)` via `BUILTIN_TOOLS` registry | Tool factories accept `ToolSession` and can return `null` |\n| Per-tool `*Operations` interfaces | Per-tool interfaces remain (`FindOperations`, `GrepOperations`) | Used for SSH/remote overrides |\n| Node.js `fs/promises` everywhere | `Bun.file()`/`Bun.write()` for files; `node:fs/promises` for dirs | Prefer Bun APIs when they simplify |\n\n### Auth Storage\n\n| Upstream | Our Fork | Notes |\n| ------------------------------- | ------------------------------------------- | -------------------------------------------- |\n| `proper-lockfile` + `auth.json` | `agent.db` (bun:sqlite) | Credentials stored exclusively in `agent.db` |\n| Single credential per provider | Multi-credential with round-robin selection | Session affinity and backoff logic preserved |\n\n### Extensions\n\n| Upstream | Our Fork |\n| ----------------------------- | ------------------------------------------ |\n| `jiti` for TypeScript loading | Native Bun `import()` |\n| `pkg.pi` manifest field | `pkg.xcsh ?? pkg.pi` (prefer our namespace) |\n\n### Skip These Upstream Features\n\nWhen porting, **skip** these files/features entirely:\n\n- `footer-data-provider.ts` — we use StatusLineComponent\n- `clipboard-image.ts` — clipboard is in `@f5-sales-demo/pi-natives` N-API module\n- GitHub workflow files — we have our own CI\n- `models.generated.ts` — auto-generated, regenerate locally (as models.json instead)\n\n### Features We Added (Preserve These)\n\nThese exist in our fork but not upstream. **Never overwrite:**\n\n- `StatusLineComponent` in interactive mode\n- Multi-credential auth with session affinity\n- Capability-based discovery system (`defineCapability`, `registerProvider`, `loadCapability`, `skillCapability`, etc.)\n- MCP/Exa/SSH integrations\n- LSP writethrough for format-on-save\n- Bash interception (`checkBashInterception`)\n- Fuzzy path suggestions in read tool\n",
|
|
119
120
|
"en/configuration/rpc.md": "---\ntitle: RPC Protocol Reference\ndescription: JSON-RPC protocol reference for inter-process communication between xcsh components.\nsidebar:\n order: 5\n label: RPC protocol\n---\n\n# RPC Protocol Reference\n\nRPC mode runs the coding agent as a newline-delimited JSON protocol over stdio.\n\n- **stdin**: commands (`RpcCommand`) and extension UI responses\n- **stdout**: command responses (`RpcResponse`), session/agent events, extension UI requests\n\nPrimary implementation:\n\n- `src/modes/rpc/rpc-mode.ts`\n- `src/modes/rpc/rpc-types.ts`\n- `src/session/agent-session.ts`\n- `packages/agent/src/agent.ts`\n- `packages/agent/src/agent-loop.ts`\n\n## Startup\n\n```bash\nxcsh --mode rpc [regular CLI options]\n```\n\nBehavior notes:\n\n- `@file` CLI arguments are rejected in RPC mode.\n- RPC mode disables automatic session title generation by default to avoid an extra model call.\n- RPC mode resets workflow-altering `todo.*`, `task.*`, and `async.*` settings to their built-in defaults instead of inheriting user overrides.\n- The process reads stdin as JSONL (`readJsonl(Bun.stdin.stream())`).\n- When stdin closes, the process exits with code `0`.\n- Responses/events are written as one JSON object per line.\n\n## Transport and Framing\n\nEach frame is a single JSON object followed by `\\n`.\n\nThere is no envelope beyond the object shape itself.\n\n### Outbound frame categories (stdout)\n\n1. `RpcResponse` (`{ type: \"response\", ... }`)\n2. `AgentSessionEvent` objects (`agent_start`, `message_update`, etc.)\n3. `RpcExtensionUIRequest` (`{ type: \"extension_ui_request\", ... }`)\n4. Extension errors (`{ type: \"extension_error\", extensionPath, event, error }`)\n\n### Inbound frame categories (stdin)\n\n1. `RpcCommand`\n2. `RpcExtensionUIResponse` (`{ type: \"extension_ui_response\", ... }`)\n\n## Request/Response Correlation\n\nAll commands accept optional `id?: string`.\n\n- If provided, normal command responses echo the same `id`.\n- `RpcClient` relies on this for pending-request resolution.\n\nImportant edge behavior from runtime:\n\n- Unknown command responses are emitted with `id: undefined` (even if the request had an `id`).\n- Parse/handler exceptions in the input loop emit `command: \"parse\"` with `id: undefined`.\n- `prompt` and `abort_and_prompt` return immediate success, then may emit a later error response with the **same** id if async prompt scheduling fails.\n\n## Command Schema (canonical)\n\n`RpcCommand` is defined in `src/modes/rpc/rpc-types.ts`:\n\n### Prompting\n\n- `{ id?, type: \"prompt\", message: string, images?: ImageContent[], streamingBehavior?: \"steer\" | \"followUp\" }`\n- `{ id?, type: \"steer\", message: string, images?: ImageContent[] }`\n- `{ id?, type: \"follow_up\", message: string, images?: ImageContent[] }`\n- `{ id?, type: \"abort\" }`\n- `{ id?, type: \"abort_and_prompt\", message: string, images?: ImageContent[] }`\n- `{ id?, type: \"new_session\", parentSession?: string }`\n\n### State\n\n- `{ id?, type: \"get_state\" }`\n- `{ id?, type: \"set_todos\", phases: TodoPhase[] }`\n- `{ id?, type: \"set_host_tools\", tools: RpcHostToolDefinition[] }`\n\n### Model\n\n- `{ id?, type: \"set_model\", provider: string, modelId: string }`\n- `{ id?, type: \"cycle_model\" }`\n- `{ id?, type: \"get_available_models\" }`\n\n### Thinking\n\n- `{ id?, type: \"set_thinking_level\", level: ThinkingLevel }`\n- `{ id?, type: \"cycle_thinking_level\" }`\n\n### Queue modes\n\n- `{ id?, type: \"set_steering_mode\", mode: \"all\" | \"one-at-a-time\" }`\n- `{ id?, type: \"set_follow_up_mode\", mode: \"all\" | \"one-at-a-time\" }`\n- `{ id?, type: \"set_interrupt_mode\", mode: \"immediate\" | \"wait\" }`\n\n### Compaction\n\n- `{ id?, type: \"compact\", customInstructions?: string }`\n- `{ id?, type: \"set_auto_compaction\", enabled: boolean }`\n\n### Retry\n\n- `{ id?, type: \"set_auto_retry\", enabled: boolean }`\n- `{ id?, type: \"abort_retry\" }`\n\n### Bash\n\n- `{ id?, type: \"bash\", command: string }`\n- `{ id?, type: \"abort_bash\" }`\n\n### Session\n\n- `{ id?, type: \"get_session_stats\" }`\n- `{ id?, type: \"export_html\", outputPath?: string }`\n- `{ id?, type: \"switch_session\", sessionPath: string }`\n- `{ id?, type: \"branch\", entryId: string }`\n- `{ id?, type: \"get_branch_messages\" }`\n- `{ id?, type: \"get_last_assistant_text\" }`\n- `{ id?, type: \"set_session_name\", name: string }`\n\n### Messages\n\n- `{ id?, type: \"get_messages\" }`\n\n## Response Schema\n\nAll command results use `RpcResponse`:\n\n- Success: `{ id?, type: \"response\", command: <command>, success: true, data?: ... }`\n- Failure: `{ id?, type: \"response\", command: string, success: false, error: string }`\n\nData payloads are command-specific and defined in `rpc-types.ts`.\n\n### `get_state` payload\n\n```json\n{\n \"model\": { \"provider\": \"...\", \"id\": \"...\" },\n \"thinkingLevel\": \"off|minimal|low|medium|high|xhigh\",\n \"isStreaming\": false,\n \"isCompacting\": false,\n \"steeringMode\": \"all|one-at-a-time\",\n \"followUpMode\": \"all|one-at-a-time\",\n \"interruptMode\": \"immediate|wait\",\n \"sessionFile\": \"...\",\n \"sessionId\": \"...\",\n \"sessionName\": \"...\",\n \"autoCompactionEnabled\": true,\n \"messageCount\": 0,\n \"queuedMessageCount\": 0,\n \"todoPhases\": [\n {\n \"id\": \"phase-1\",\n \"name\": \"Todos\",\n \"tasks\": [\n {\n \"id\": \"task-1\",\n \"content\": \"Map the tool surface\",\n \"status\": \"in_progress\"\n }\n ]\n }\n ]\n}\n```\n\n### `set_todos` payload\n\nReplaces the in-memory todo state for the current session and returns the normalized phase list:\n\n```json\n{\n \"id\": \"req_2\",\n \"type\": \"set_todos\",\n \"phases\": [\n {\n \"id\": \"phase-1\",\n \"name\": \"Evaluation\",\n \"tasks\": [\n {\n \"id\": \"task-1\",\n \"content\": \"Map the read tool surface\",\n \"status\": \"in_progress\"\n },\n {\n \"id\": \"task-2\",\n \"content\": \"Exercise edit operations\",\n \"status\": \"pending\"\n }\n ]\n }\n ]\n}\n```\n\nThis is useful for hosts that want to pre-seed a plan before the first prompt.\n\n### `set_host_tools` payload\n\nReplaces the current set of host-owned tools that the RPC server may call back\ninto over stdio:\n\n```json\n{\n \"id\": \"req_3\",\n \"type\": \"set_host_tools\",\n \"tools\": [\n {\n \"name\": \"echo_host\",\n \"label\": \"Echo Host\",\n \"description\": \"Echo a value from the embedding host\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"message\": { \"type\": \"string\" }\n },\n \"required\": [\"message\"],\n \"additionalProperties\": false\n }\n }\n ]\n}\n```\n\nThe response payload is:\n\n```json\n{\n \"toolNames\": [\"echo_host\"]\n}\n```\n\nThese tools are added to the active session tool registry before the next model\ncall. Re-sending `set_host_tools` replaces the previous host-owned set.\n\n## Event Stream Schema\n\nRPC mode forwards `AgentSessionEvent` objects from `AgentSession.subscribe(...)`.\n\nCommon event types:\n\n- `agent_start`, `agent_end`\n- `turn_start`, `turn_end`\n- `message_start`, `message_update`, `message_end`\n- `tool_execution_start`, `tool_execution_update`, `tool_execution_end`\n- `auto_compaction_start`, `auto_compaction_end`\n- `auto_retry_start`, `auto_retry_end`\n- `ttsr_triggered`\n- `todo_reminder`\n- `todo_auto_clear`\n\nExtension runner errors are emitted separately as:\n\n```json\n{ \"type\": \"extension_error\", \"extensionPath\": \"...\", \"event\": \"...\", \"error\": \"...\" }\n```\n\n`message_update` includes streaming deltas in `assistantMessageEvent` (text/thinking/toolcall deltas).\n\n## Prompt/Queue Concurrency and Ordering\n\nThis is the most important operational behavior.\n\n### Immediate ack vs completion\n\n`prompt` and `abort_and_prompt` are **acknowledged immediately**:\n\n```json\n{ \"id\": \"req_1\", \"type\": \"response\", \"command\": \"prompt\", \"success\": true }\n```\n\nThat means:\n\n- command acceptance != run completion\n- final completion is observed via `agent_end`\n\n### While streaming\n\n`AgentSession.prompt()` requires `streamingBehavior` during active streaming:\n\n- `\"steer\"` => queued steering message (interrupt path)\n- `\"followUp\"` => queued follow-up message (post-turn path)\n\nIf omitted during streaming, prompt fails.\n\n### Queue defaults\n\nFrom the coding-agent settings schema (`packages/coding-agent/src/config/settings-schema.ts`):\n\n- `steeringMode`: `\"one-at-a-time\"`\n- `followUpMode`: `\"one-at-a-time\"`\n- `interruptMode`: `\"wait\"`\n\n### Mode semantics\n\n- `set_steering_mode` / `set_follow_up_mode`\n - `\"one-at-a-time\"`: dequeue one queued message per turn\n - `\"all\"`: dequeue entire queue at once\n- `set_interrupt_mode`\n - `\"immediate\"`: tool execution checks steering between tool calls; pending steering can abort remaining tool calls in the turn\n - `\"wait\"`: defer steering until turn completion\n\n## Extension UI Sub-Protocol\n\nExtensions in RPC mode use request/response UI frames.\n\n### Outbound request\n\n`RpcExtensionUIRequest` (`type: \"extension_ui_request\"`) methods:\n\n- `select`, `confirm`, `input`, `editor`\n- `notify`, `setStatus`, `setWidget`, `setTitle`, `set_editor_text`\n\nRuntime note:\n\n- Automatic session title generation is disabled in RPC mode, and `setTitle` UI\n requests are also suppressed by default because most hosts do not have a\n meaningful terminal-title surface. Set `PI_RPC_EMIT_TITLE=1` to opt back in to\n the UI event only.\n\nExample:\n\n```json\n{ \"type\": \"extension_ui_request\", \"id\": \"123\", \"method\": \"confirm\", \"title\": \"Confirm\", \"message\": \"Continue?\", \"timeout\": 30000 }\n```\n\n### Inbound response\n\n`RpcExtensionUIResponse` (`type: \"extension_ui_response\"`):\n\n- `{ type: \"extension_ui_response\", id: string, value: string }`\n- `{ type: \"extension_ui_response\", id: string, confirmed: boolean }`\n- `{ type: \"extension_ui_response\", id: string, cancelled: true }`\n\nIf a dialog has a timeout, RPC mode resolves to a default value when timeout/abort fires.\n\n## Host Tool Sub-Protocol\n\nRPC hosts can expose custom tools to the agent by sending `set_host_tools`, then\nserving execution requests over the same transport.\n\n### Outbound request\n\nWhen the agent wants the host to execute one of those tools, RPC mode emits:\n\n```json\n{\n \"type\": \"host_tool_call\",\n \"id\": \"host_1\",\n \"toolCallId\": \"toolu_123\",\n \"toolName\": \"echo_host\",\n \"arguments\": { \"message\": \"hello\" }\n}\n```\n\nIf the tool execution is later aborted, RPC mode emits:\n\n```json\n{\n \"type\": \"host_tool_cancel\",\n \"id\": \"host_cancel_1\",\n \"targetId\": \"host_1\"\n}\n```\n\n### Inbound updates and completion\n\nHosts can optionally stream progress:\n\n```json\n{\n \"type\": \"host_tool_update\",\n \"id\": \"host_1\",\n \"partialResult\": {\n \"content\": [{ \"type\": \"text\", \"text\": \"working\" }]\n }\n}\n```\n\nCompletion uses:\n\n```json\n{\n \"type\": \"host_tool_result\",\n \"id\": \"host_1\",\n \"result\": {\n \"content\": [{ \"type\": \"text\", \"text\": \"done\" }]\n }\n}\n```\n\nSet `isError: true` on `host_tool_result` to surface the returned content as a\ntool error.\n\n## Error Model and Recoverability\n\n### Command-level failures\n\nFailures are `success: false` with string `error`.\n\n```json\n{ \"id\": \"req_2\", \"type\": \"response\", \"command\": \"set_model\", \"success\": false, \"error\": \"Model not found: provider/model\" }\n```\n\n### Recoverability expectations\n\n- Most command failures are recoverable; process remains alive.\n- Malformed JSONL / parse-loop exceptions emit a `parse` error response and continue reading subsequent lines.\n- Empty `set_session_name` is rejected (`Session name cannot be empty`).\n- Extension UI responses with unknown `id` are ignored.\n- Process termination conditions are stdin close or explicit extension-triggered shutdown.\n\n## Compact Command Flows\n\n### 1) Prompt and stream\n\nstdin:\n\n```json\n{ \"id\": \"req_1\", \"type\": \"prompt\", \"message\": \"Summarize this repo\" }\n```\n\nstdout sequence (typical):\n\n```json\n{ \"id\": \"req_1\", \"type\": \"response\", \"command\": \"prompt\", \"success\": true }\n{ \"type\": \"agent_start\" }\n{ \"type\": \"message_update\", \"assistantMessageEvent\": { \"type\": \"text_delta\", \"delta\": \"...\" }, \"message\": { \"role\": \"assistant\", \"content\": [] } }\n{ \"type\": \"agent_end\", \"messages\": [] }\n```\n\n### 2) Prompt during streaming with explicit queue policy\n\nstdin:\n\n```json\n{ \"id\": \"req_2\", \"type\": \"prompt\", \"message\": \"Also include risks\", \"streamingBehavior\": \"followUp\" }\n```\n\n### 3) Inspect and tune queue behavior\n\nstdin:\n\n```json\n{ \"id\": \"q1\", \"type\": \"get_state\" }\n{ \"id\": \"q2\", \"type\": \"set_steering_mode\", \"mode\": \"all\" }\n{ \"id\": \"q3\", \"type\": \"set_interrupt_mode\", \"mode\": \"wait\" }\n```\n\n### 4) Extension UI round trip\n\nstdout:\n\n```json\n{ \"type\": \"extension_ui_request\", \"id\": \"ui_7\", \"method\": \"input\", \"title\": \"Branch name\", \"placeholder\": \"feature/...\" }\n```\n\nstdin:\n\n```json\n{ \"type\": \"extension_ui_response\", \"id\": \"ui_7\", \"value\": \"feature/rpc-host\" }\n```\n\n## Notes on `RpcClient` helper\n\n`src/modes/rpc/rpc-client.ts` is a convenience wrapper, not the protocol definition.\n\nCurrent helper characteristics:\n\n- Spawns `bun <cliPath> --mode rpc`\n- Correlates responses by generated `req_<n>` ids\n- Dispatches only recognized `AgentEvent` types to listeners\n- Supports host-owned custom tools via `setCustomTools()` and automatic handling of `host_tool_call` / `host_tool_cancel`\n- Does **not** expose helper methods for every protocol command (for example, `set_interrupt_mode` and `set_session_name` are in protocol types but not wrapped as dedicated methods)\n\nUse raw protocol frames if you need complete surface coverage.\n",
|