@clawos-dev/clawd 0.2.559 → 0.2.561

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.
@@ -24750,6 +24750,8 @@ var INNER_TAG_UNESCAPE_RE = new RegExp(`<\\\\(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`
24750
24750
  // ../protocol/src/tool-family.ts
24751
24751
  var CLOUD_TOOL_SUFFIX = "-cloud";
24752
24752
  var CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
24753
+ var PI_TOOL_ID = "pi";
24754
+ var PI_CLOUD_TOOL_ID = `${PI_TOOL_ID}${CLOUD_TOOL_SUFFIX}`;
24753
24755
 
24754
24756
  // src/deploy/deploy-file.ts
24755
24757
  var import_promises = __toESM(require("fs/promises"), 1);
package/dist/cli.cjs CHANGED
@@ -9750,16 +9750,23 @@ var init_dispatch_format = __esm({
9750
9750
  function isCodexFamily(tool) {
9751
9751
  return tool === "codex" || tool === CODEX_CLOUD_TOOL_ID;
9752
9752
  }
9753
+ function isPiFamily(tool) {
9754
+ return tool === PI_TOOL_ID || tool === PI_CLOUD_TOOL_ID;
9755
+ }
9756
+ function isCloudTool(tool) {
9757
+ return typeof tool === "string" && tool.length > CLOUD_TOOL_SUFFIX.length && tool.endsWith(CLOUD_TOOL_SUFFIX);
9758
+ }
9753
9759
  function isRpcAgentTool(tool) {
9754
- return isCodexFamily(tool) || tool === PI_TOOL_ID;
9760
+ return isCodexFamily(tool) || isPiFamily(tool);
9755
9761
  }
9756
- var CLOUD_TOOL_SUFFIX, CODEX_CLOUD_TOOL_ID, PI_TOOL_ID;
9762
+ var CLOUD_TOOL_SUFFIX, CODEX_CLOUD_TOOL_ID, PI_TOOL_ID, PI_CLOUD_TOOL_ID;
9757
9763
  var init_tool_family = __esm({
9758
9764
  "../protocol/src/tool-family.ts"() {
9759
9765
  "use strict";
9760
9766
  CLOUD_TOOL_SUFFIX = "-cloud";
9761
9767
  CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
9762
9768
  PI_TOOL_ID = "pi";
9769
+ PI_CLOUD_TOOL_ID = `${PI_TOOL_ID}${CLOUD_TOOL_SUFFIX}`;
9763
9770
  }
9764
9771
  });
9765
9772
 
@@ -45685,7 +45692,7 @@ function cloneState(s2) {
45685
45692
  }
45686
45693
  var CLOUD_IDLE_KILL_DELAY_MS = 5 * 6e4;
45687
45694
  function idleKillDelayOf(state) {
45688
- if (state.file.tool === CODEX_CLOUD_TOOL_ID) return CLOUD_IDLE_KILL_DELAY_MS;
45695
+ if (isCloudTool(state.file.tool)) return CLOUD_IDLE_KILL_DELAY_MS;
45689
45696
  const ms = state.subSessionMeta?.idleKillDelayMs;
45690
45697
  return ms !== void 0 && ms > 0 ? ms : void 0;
45691
45698
  }
@@ -45861,7 +45868,7 @@ function buildSpawnContext(state, deps) {
45861
45868
  const meta = state.subSessionMeta;
45862
45869
  if (meta?.personaMode) {
45863
45870
  ctx.personaMode = meta.personaMode;
45864
- const workDir = file.tool === CODEX_CLOUD_TOOL_ID ? void 0 : meta.userWorkDir;
45871
+ const workDir = isCloudTool(file.tool) ? void 0 : meta.userWorkDir;
45865
45872
  let promptArgs;
45866
45873
  if (meta.personaMode === "guest") {
45867
45874
  if (!meta.callerDisplayName) {
@@ -46127,7 +46134,7 @@ function pushEventToBufferInPlace(next, event, deps) {
46127
46134
  if (canCloseStdin) {
46128
46135
  next.stdinClosed = true;
46129
46136
  effects.push({ kind: "close-stdin" });
46130
- } else if (isRpcAgentTool(tool) && tool !== CODEX_CLOUD_TOOL_ID) {
46137
+ } else if (isRpcAgentTool(tool) && !isCloudTool(tool)) {
46131
46138
  if (tool === PI_TOOL_ID) next.stdinClosed = true;
46132
46139
  effects.push({ kind: "kill", signal: "SIGTERM" });
46133
46140
  } else {
@@ -51787,7 +51794,11 @@ function codexGuestCliArgs(cs, opts = {}) {
51787
51794
  function codexMcpCliArgs(defs) {
51788
51795
  const out = [];
51789
51796
  for (const d2 of defs) {
51790
- const cfg = "url" in d2 ? { url: d2.url, ...d2.httpHeaders && Object.keys(d2.httpHeaders).length > 0 ? { http_headers: d2.httpHeaders } : {} } : {
51797
+ const cfg = "url" in d2 ? {
51798
+ url: d2.url,
51799
+ ...d2.bearerTokenEnvVar ? { bearer_token_env_var: d2.bearerTokenEnvVar } : {},
51800
+ ...d2.httpHeaders && Object.keys(d2.httpHeaders).length > 0 ? { http_headers: d2.httpHeaders } : {}
51801
+ } : {
51791
51802
  command: d2.command,
51792
51803
  ...d2.args && d2.args.length > 0 ? { args: d2.args } : {},
51793
51804
  ...d2.env && Object.keys(d2.env).length > 0 ? { env: d2.env } : {}
@@ -51983,11 +51994,11 @@ function stringValue2(value) {
51983
51994
  return typeof value === "string" && value.length > 0 ? value : void 0;
51984
51995
  }
51985
51996
 
51986
- // src/tools/codex-mcp-config.ts
51997
+ // src/tools/mcp-server-defs.ts
51987
51998
  var import_node_fs17 = __toESM(require("fs"), 1);
51988
51999
  var import_node_path15 = __toESM(require("path"), 1);
51989
52000
  var HTTP_TYPES = /* @__PURE__ */ new Set(["http", "sse", "streamable_http", "streamable-http"]);
51990
- function loadCodexMcpServerDefs(args) {
52001
+ function loadMcpServerDefs(args) {
51991
52002
  const out = [];
51992
52003
  const seen = /* @__PURE__ */ new Set();
51993
52004
  const sources = [...args.mcpConfigPaths, import_node_path15.default.join(args.cwd, ".mcp.json")];
@@ -51998,7 +52009,7 @@ function loadCodexMcpServerDefs(args) {
51998
52009
  } catch (e) {
51999
52010
  const code = e.code;
52000
52011
  if (code !== "ENOENT") {
52001
- args.logger?.warn("codex mcp config unreadable, skipped", { file, code: code ?? "parse-error" });
52012
+ args.logger?.warn("mcp config unreadable, skipped", { file, code: code ?? "parse-error" });
52002
52013
  }
52003
52014
  continue;
52004
52015
  }
@@ -52009,28 +52020,34 @@ function loadCodexMcpServerDefs(args) {
52009
52020
  const type = typeof def.type === "string" ? def.type.toLowerCase() : typeof def.url === "string" ? "http" : "stdio";
52010
52021
  if (HTTP_TYPES.has(type)) {
52011
52022
  if (typeof def.url !== "string" || !def.url) {
52012
- args.logger?.warn("codex mcp server http without url, skipped", { file, name });
52023
+ args.logger?.warn("mcp server http without url, skipped", { file, name });
52013
52024
  continue;
52014
52025
  }
52015
52026
  if (seen.has(name)) {
52016
- args.logger?.warn("codex mcp server name collision, first-wins", { file, name });
52027
+ args.logger?.warn("mcp server name collision, first-wins", { file, name });
52017
52028
  continue;
52018
52029
  }
52019
52030
  seen.add(name);
52020
52031
  const headers = def.headers && typeof def.headers === "object" ? Object.fromEntries(Object.entries(def.headers).filter((kv) => typeof kv[1] === "string")) : {};
52021
- out.push({ name, url: def.url, ...Object.keys(headers).length > 0 ? { httpHeaders: headers } : {} });
52032
+ const bearerTokenEnvVar = typeof def.bearer_token_env_var === "string" && def.bearer_token_env_var ? def.bearer_token_env_var : void 0;
52033
+ out.push({
52034
+ name,
52035
+ url: def.url,
52036
+ ...Object.keys(headers).length > 0 ? { httpHeaders: headers } : {},
52037
+ ...bearerTokenEnvVar ? { bearerTokenEnvVar } : {}
52038
+ });
52022
52039
  continue;
52023
52040
  }
52024
52041
  if (type !== "stdio") {
52025
- args.logger?.warn("codex mcp server type unsupported, skipped", { file, name, type });
52042
+ args.logger?.warn("mcp server type unsupported, skipped", { file, name, type });
52026
52043
  continue;
52027
52044
  }
52028
52045
  if (typeof def.command !== "string" || !def.command) {
52029
- args.logger?.warn("codex mcp server missing command, skipped", { file, name });
52046
+ args.logger?.warn("mcp server missing command, skipped", { file, name });
52030
52047
  continue;
52031
52048
  }
52032
52049
  if (seen.has(name)) {
52033
- args.logger?.warn("codex mcp server name collision, first-wins", { file, name });
52050
+ args.logger?.warn("mcp server name collision, first-wins", { file, name });
52034
52051
  continue;
52035
52052
  }
52036
52053
  seen.add(name);
@@ -52327,7 +52344,7 @@ function createCodexSession(ctx, sink, deps = {}) {
52327
52344
  );
52328
52345
  args.push(
52329
52346
  ...codexMcpCliArgs(
52330
- loadCodexMcpServerDefs({
52347
+ loadMcpServerDefs({
52331
52348
  mcpConfigPaths: (ctx.mcpConfigs ?? []).map((m3) => m3.path),
52332
52349
  cwd: ctx.cwd,
52333
52350
  baseEnv: clawdEnv,
@@ -63152,21 +63169,43 @@ init_protocol();
63152
63169
 
63153
63170
  // src/tools/pi-rpc-client.ts
63154
63171
  init_stdout_splitter();
63172
+ function piStdioLineStream(proc) {
63173
+ const lineCbs = [];
63174
+ const closeCbs = [];
63175
+ let buf = "";
63176
+ proc.stdout.on("data", (chunk) => {
63177
+ const { newBuf, lines } = splitStdoutChunk(buf, chunk);
63178
+ buf = newBuf;
63179
+ for (const line of lines) for (const cb of lineCbs) cb(line);
63180
+ });
63181
+ proc.stdout.on("close", () => {
63182
+ for (const cb of closeCbs) cb();
63183
+ });
63184
+ return {
63185
+ write: (line) => {
63186
+ proc.stdin?.write(line + "\n");
63187
+ },
63188
+ onLine: (cb) => {
63189
+ lineCbs.push(cb);
63190
+ },
63191
+ onClose: (cb) => {
63192
+ closeCbs.push(cb);
63193
+ },
63194
+ close: () => {
63195
+ proc.stdin?.end();
63196
+ }
63197
+ };
63198
+ }
63155
63199
  var PiRpcClient = class {
63156
- constructor(proc, opts = {}) {
63157
- this.proc = proc;
63200
+ constructor(stream, opts = {}) {
63201
+ this.stream = stream;
63158
63202
  this.opts = opts;
63159
- proc.stdout.on("data", (chunk) => {
63160
- const { newBuf, lines } = splitStdoutChunk(this.buf, chunk);
63161
- this.buf = newBuf;
63162
- for (const line of lines) this.onLine(line);
63163
- });
63164
- proc.stdout.on("close", () => this.handleClose());
63203
+ stream.onLine((line) => this.onLine(line));
63204
+ stream.onClose(() => this.handleClose());
63165
63205
  }
63166
- proc;
63206
+ stream;
63167
63207
  opts;
63168
63208
  nextId = 1;
63169
- buf = "";
63170
63209
  closed = false;
63171
63210
  /** endInput() 之后:写端已关,再写会让 Node 异步 emit ERR_STREAM_WRITE_AFTER_END,直接拒掉 */
63172
63211
  inputClosed = false;
@@ -63185,14 +63224,14 @@ var PiRpcClient = class {
63185
63224
  if (this.closed || this.inputClosed) return;
63186
63225
  this.write(frame);
63187
63226
  }
63188
- /** 关写端 = pi 的优雅退出信号(stdin EOF → 进程退出 code 0) */
63227
+ /** 关写端 = pi 的优雅退出信号(本机 stdin EOF → 进程退出 code 0;云上 = 断开会话通道) */
63189
63228
  endInput() {
63190
63229
  if (this.inputClosed) return;
63191
63230
  this.inputClosed = true;
63192
- this.proc.stdin?.end();
63231
+ this.stream.close();
63193
63232
  }
63194
63233
  write(o2) {
63195
- this.proc.stdin?.write(JSON.stringify(o2) + "\n");
63234
+ this.stream.write(JSON.stringify(o2));
63196
63235
  }
63197
63236
  onLine(line) {
63198
63237
  const t2 = line.replace(/\r$/, "").trim();
@@ -63381,43 +63420,9 @@ function makeImageFetcher(fetchImpl = fetch, opts = {}) {
63381
63420
  };
63382
63421
  }
63383
63422
 
63384
- // src/pi-mcp-bridge/bridge.ts
63385
- var PI_MCP_CONFIGS_ENV = "CLAWD_PI_MCP_CONFIGS";
63386
-
63387
- // src/tools/pi-session.ts
63388
- var PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
63389
- function buildPiArgs(ctx, opts) {
63390
- const args = ["--mode", "rpc", "--session-id", opts.sessionId];
63391
- if (ctx.model) args.push("--model", ctx.model);
63392
- if (ctx.effort && PI_THINKING_LEVELS.includes(ctx.effort)) args.push("--thinking", ctx.effort);
63393
- if (ctx.extraSystemPrompt) args.push("--append-system-prompt", ctx.extraSystemPrompt);
63394
- if (ctx.addDirs && ctx.addDirs.length > 0) args.push("--append-system-prompt", piAddDirsPrompt(ctx.addDirs));
63395
- if (opts.skillsDir) args.push("--skill", opts.skillsDir);
63396
- if (opts.mcpBridgePath) args.push("-e", opts.mcpBridgePath);
63397
- args.push("--exclude-tools", "ask_question");
63398
- return args;
63399
- }
63400
- function piAddDirsPrompt(dirs) {
63401
- return `\u9664\u5F53\u524D\u76EE\u5F55\u5916\uFF0C\u4EE5\u4E0B\u76EE\u5F55\u4E5F\u662F\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u53EF\u8BFB\u53EF\u5199\uFF0C\u9879\u76EE\u6587\u4EF6\u5C31\u5728\u8FD9\u91CC\uFF09\uFF1A
63402
- ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
63403
- }
63404
- function createPiSession(ctx, sink, deps = {}) {
63405
- const cmd = process.env.PI_BIN ?? "pi";
63406
- const sessionId = ctx.toolSessionId ?? (0, import_node_crypto10.randomUUID)();
63407
- const skillsDir = import_node_path39.default.join(ctx.cwd, ".claude", "skills");
63408
- const exists = deps.skillsDirExists ?? ((d2) => import_node_fs35.default.existsSync(d2));
63409
- const args = buildPiArgs(ctx, {
63410
- sessionId,
63411
- skillsDir: exists(skillsDir) ? skillsDir : void 0,
63412
- mcpBridgePath: deps.mcpBridgePath
63413
- });
63414
- const env = {
63415
- ...process.env,
63416
- ...ctx.env,
63417
- ...deps.mcpBridgePath ? { [PI_MCP_CONFIGS_ENV]: JSON.stringify((ctx.mcpConfigs ?? []).map((m3) => m3.path)) } : {}
63418
- };
63419
- const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, ctx.cwd, env) : (0, import_node_child_process10.spawn)(cmd, args, { cwd: ctx.cwd, env, stdio: ["pipe", "pipe", "pipe"] });
63420
- const alive = () => proc.exitCode === null && proc.signalCode === null;
63423
+ // src/tools/pi-session-driver.ts
63424
+ function drivePiSession(opts) {
63425
+ const { sink, stream, sessionId, logger } = opts;
63421
63426
  const st2 = createPiTranslatorState();
63422
63427
  let busy = false;
63423
63428
  let handshakeFailed = false;
@@ -63431,17 +63436,17 @@ function createPiSession(ctx, sink, deps = {}) {
63431
63436
  const handleUiRequest = (ev) => {
63432
63437
  const method = str3(ev.method);
63433
63438
  const id = str3(ev.id);
63434
- deps.logger?.warn("pi extension_ui_request unexpected in clawd session", { method });
63439
+ logger?.warn("pi extension_ui_request unexpected in clawd session", { method });
63435
63440
  if (id && (method === "select" || method === "confirm" || method === "input" || method === "editor")) {
63436
63441
  client.send({ type: "extension_ui_response", id, cancelled: true });
63437
63442
  }
63438
63443
  };
63439
- const client = new PiRpcClient(proc, {
63444
+ const client = new PiRpcClient(stream, {
63440
63445
  onEvent: (ev) => {
63441
63446
  if (ev.type === "agent_start") busy = true;
63442
63447
  if (ev.type === "agent_settled") busy = false;
63443
63448
  if (ev.type === "response") {
63444
- deps.logger?.warn("pi response without pending id", { command: str3(ev.command), error: str3(ev.error) });
63449
+ logger?.warn("pi response without pending id", { command: str3(ev.command), error: str3(ev.error) });
63445
63450
  return;
63446
63451
  }
63447
63452
  if (ev.type === "extension_ui_request") {
@@ -63450,14 +63455,15 @@ function createPiSession(ctx, sink, deps = {}) {
63450
63455
  }
63451
63456
  const events = translatePiEvent(ev, st2);
63452
63457
  if (events.length) sink.pushEvents(events);
63458
+ if (ev.type === "agent_settled") opts.onTurnCompleted?.();
63453
63459
  },
63454
- onClose: () => finish(proc.exitCode)
63460
+ onClose: () => finish(opts.exitCodeOnClose?.() ?? null)
63455
63461
  });
63456
63462
  const ready = client.request({ type: "get_state" }).then((r2) => {
63457
63463
  if (!r2.success) throw new Error(r2.error ?? "get_state failed");
63458
63464
  const data = rec3(r2.data);
63459
63465
  const sid = str3(data?.sessionId) ?? sessionId;
63460
- if (sid !== sessionId) deps.logger?.error("pi sessionId mismatch", { expected: sessionId, actual: sid });
63466
+ if (sid !== sessionId) logger?.error("pi sessionId mismatch", { expected: sessionId, actual: sid });
63461
63467
  const model = rec3(data?.model);
63462
63468
  const patch = {};
63463
63469
  const mid = str3(model?.id);
@@ -63472,24 +63478,14 @@ function createPiSession(ctx, sink, deps = {}) {
63472
63478
  handshakeFailed = true;
63473
63479
  sink.onError(`pi get_state failed: ${e.message}`);
63474
63480
  });
63475
- proc.stderr?.on("data", (c2) => deps.logger?.warn("pi stderr", { line: c2.toString().slice(0, 500) }));
63476
- proc.stdin?.on("error", (err) => deps.logger?.warn("pi stdin error (write dropped)", { message: err.message }));
63477
- proc.on("error", (err) => {
63478
- const message = err.message;
63479
- deps.logger?.error("pi spawn failed", { message });
63480
- const described = describeSpawnError(err, cmd, ctx.cwd);
63481
- sink.onError(described === message ? `pi spawn failed: ${message}` : described);
63482
- });
63483
- proc.on("exit", (code) => finish(code));
63484
- const fetchImage = deps.fetchImage ?? makeImageFetcher();
63485
- return {
63481
+ const session = {
63486
63482
  startTurn(text) {
63487
63483
  void ready.then(async () => {
63488
63484
  if (handshakeFailed) {
63489
63485
  fail("pi session not ready (get_state failed)");
63490
63486
  return;
63491
63487
  }
63492
- const input = await piPromptInput(text, { fetchImage });
63488
+ const input = await piPromptInput(text, { fetchImage: opts.fetchImage });
63493
63489
  const r2 = await client.request({
63494
63490
  type: "prompt",
63495
63491
  ...input,
@@ -63500,24 +63496,86 @@ function createPiSession(ctx, sink, deps = {}) {
63500
63496
  }).catch((e) => fail(`pi prompt failed: ${e.message}`));
63501
63497
  },
63502
63498
  respondPermission(requestId) {
63503
- deps.logger?.warn("pi respondPermission ignored: v1 has no approvals", { requestId });
63499
+ logger?.warn("pi respondPermission ignored: pi has no approvals", { requestId });
63504
63500
  },
63505
63501
  interrupt() {
63506
63502
  st2.aborted = true;
63507
63503
  void client.request({ type: "abort" }).catch(() => {
63508
63504
  });
63509
63505
  },
63510
- async stop(signal) {
63506
+ // 信号由 runner 透传 reducer 的 kill effect;怎么落地由壳定(本机 endInput + 宽限 + kill;云上关流)
63507
+ async stop(signal = "SIGTERM") {
63508
+ await opts.kill(signal);
63509
+ }
63510
+ };
63511
+ return { session, finish, client };
63512
+ }
63513
+
63514
+ // src/pi-mcp-bridge/bridge.ts
63515
+ var PI_MCP_CONFIGS_ENV = "CLAWD_PI_MCP_CONFIGS";
63516
+
63517
+ // src/tools/pi-session.ts
63518
+ var PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
63519
+ function buildPiArgs(ctx, opts) {
63520
+ const args = ["--mode", "rpc", "--session-id", opts.sessionId];
63521
+ if (ctx.model) args.push("--model", ctx.model);
63522
+ if (ctx.effort && PI_THINKING_LEVELS.includes(ctx.effort)) args.push("--thinking", ctx.effort);
63523
+ if (ctx.extraSystemPrompt) args.push("--append-system-prompt", ctx.extraSystemPrompt);
63524
+ if (ctx.addDirs && ctx.addDirs.length > 0) args.push("--append-system-prompt", piAddDirsPrompt(ctx.addDirs));
63525
+ if (opts.skillsDir) args.push("--skill", opts.skillsDir);
63526
+ if (opts.mcpBridgePath) args.push("-e", opts.mcpBridgePath);
63527
+ args.push("--exclude-tools", "ask_question");
63528
+ return args;
63529
+ }
63530
+ function piAddDirsPrompt(dirs) {
63531
+ return `\u9664\u5F53\u524D\u76EE\u5F55\u5916\uFF0C\u4EE5\u4E0B\u76EE\u5F55\u4E5F\u662F\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u53EF\u8BFB\u53EF\u5199\uFF0C\u9879\u76EE\u6587\u4EF6\u5C31\u5728\u8FD9\u91CC\uFF09\uFF1A
63532
+ ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
63533
+ }
63534
+ function createPiSession(ctx, sink, deps = {}) {
63535
+ const cmd = process.env.PI_BIN ?? "pi";
63536
+ const sessionId = ctx.toolSessionId ?? (0, import_node_crypto10.randomUUID)();
63537
+ const skillsDir = import_node_path39.default.join(ctx.cwd, ".claude", "skills");
63538
+ const exists = deps.skillsDirExists ?? ((d2) => import_node_fs35.default.existsSync(d2));
63539
+ const args = buildPiArgs(ctx, {
63540
+ sessionId,
63541
+ skillsDir: exists(skillsDir) ? skillsDir : void 0,
63542
+ mcpBridgePath: deps.mcpBridgePath
63543
+ });
63544
+ const env = {
63545
+ ...process.env,
63546
+ ...ctx.env,
63547
+ ...deps.mcpBridgePath ? { [PI_MCP_CONFIGS_ENV]: JSON.stringify((ctx.mcpConfigs ?? []).map((m3) => m3.path)) } : {}
63548
+ };
63549
+ const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, ctx.cwd, env) : (0, import_node_child_process10.spawn)(cmd, args, { cwd: ctx.cwd, env, stdio: ["pipe", "pipe", "pipe"] });
63550
+ const alive = () => proc.exitCode === null && proc.signalCode === null;
63551
+ const driver = drivePiSession({
63552
+ sink,
63553
+ stream: piStdioLineStream(proc),
63554
+ sessionId,
63555
+ logger: deps.logger,
63556
+ fetchImage: deps.fetchImage ?? makeImageFetcher(),
63557
+ kill: async (signal) => {
63511
63558
  if (!alive()) return;
63512
63559
  if (signal === "SIGKILL") {
63513
63560
  proc.kill("SIGKILL");
63514
63561
  return;
63515
63562
  }
63516
- client.endInput();
63563
+ driver.client.endInput();
63517
63564
  await new Promise((r2) => setTimeout(r2, deps.graceMs ?? 2e3));
63518
- if (alive()) proc.kill(signal ?? "SIGTERM");
63519
- }
63520
- };
63565
+ if (alive()) proc.kill(signal);
63566
+ },
63567
+ exitCodeOnClose: () => proc.exitCode
63568
+ });
63569
+ proc.stderr?.on("data", (c2) => deps.logger?.warn("pi stderr", { line: c2.toString().slice(0, 500) }));
63570
+ proc.stdin?.on("error", (err) => deps.logger?.warn("pi stdin error (write dropped)", { message: err.message }));
63571
+ proc.on("error", (err) => {
63572
+ const message = err.message;
63573
+ deps.logger?.error("pi spawn failed", { message });
63574
+ const described = describeSpawnError(err, cmd, ctx.cwd);
63575
+ sink.onError(described === message ? `pi spawn failed: ${message}` : described);
63576
+ });
63577
+ proc.on("exit", (code) => driver.finish(code));
63578
+ return driver.session;
63521
63579
  }
63522
63580
 
63523
63581
  // src/tools/pi-history.ts
@@ -63807,7 +63865,7 @@ async function queryPiModels(deps = {}) {
63807
63865
  const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args) : (0, import_node_child_process11.spawn)(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
63808
63866
  proc.stdin?.on("error", () => {
63809
63867
  });
63810
- const client = new PiRpcClient(proc);
63868
+ const client = new PiRpcClient(piStdioLineStream(proc));
63811
63869
  const timeoutMs = deps.timeoutMs ?? 15e3;
63812
63870
  let timer;
63813
63871
  let timedOut = false;
@@ -71092,7 +71150,7 @@ function computeMethodAccess(args) {
71092
71150
  }
71093
71151
 
71094
71152
  // src/version.ts
71095
- var version = "0.2.559".length > 0 ? "0.2.559" : "dev";
71153
+ var version = "0.2.561".length > 0 ? "0.2.561" : "dev";
71096
71154
 
71097
71155
  // src/cli-probe/probe.ts
71098
71156
  var fs78 = __toESM(require("fs"), 1);
@@ -74485,7 +74543,7 @@ async function startDaemon(config) {
74485
74543
  skills,
74486
74544
  agents,
74487
74545
  // skills:list 按 tool 路由:codex → app-server skills/list;缺省 claude → SkillsScanner(同步包成 Promise)。
74488
- getSkillsForTool: (tool, cwd) => tool === "codex" ? listCodexSkills(cwd) : tool === CODEX_CLOUD_TOOL_ID ? Promise.resolve([]) : Promise.resolve(skills.list({ cwd })),
74546
+ getSkillsForTool: (tool, cwd) => tool === "codex" ? listCodexSkills(cwd) : isCloudTool(tool) ? Promise.resolve([]) : Promise.resolve(skills.list({ cwd })),
74489
74547
  history,
74490
74548
  // history:read 按 session.tool 路由:codex → adapter.historyReader(CodexHistoryReader);缺省 claude。
74491
74549
  getHistoryReader: (tool) => getAdapter(tool).historyReader ?? history,
@@ -24564,6 +24564,8 @@ var INNER_TAG_UNESCAPE_RE = new RegExp(`<\\\\(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`
24564
24564
  // ../protocol/src/tool-family.ts
24565
24565
  var CLOUD_TOOL_SUFFIX = "-cloud";
24566
24566
  var CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
24567
+ var PI_TOOL_ID = "pi";
24568
+ var PI_CLOUD_TOOL_ID = `${PI_TOOL_ID}${CLOUD_TOOL_SUFFIX}`;
24567
24569
 
24568
24570
  // src/tools/claude-history.ts
24569
24571
  var import_node_crypto = __toESM(require("crypto"), 1);
@@ -24750,6 +24750,8 @@ var INNER_TAG_UNESCAPE_RE = new RegExp(`<\\\\(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`
24750
24750
  // ../protocol/src/tool-family.ts
24751
24751
  var CLOUD_TOOL_SUFFIX = "-cloud";
24752
24752
  var CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
24753
+ var PI_TOOL_ID = "pi";
24754
+ var PI_CLOUD_TOOL_ID = `${PI_TOOL_ID}${CLOUD_TOOL_SUFFIX}`;
24753
24755
 
24754
24756
  // src/deploy/deploy-file.ts
24755
24757
  var import_promises = __toESM(require("fs/promises"), 1);
@@ -24750,6 +24750,8 @@ var INNER_TAG_UNESCAPE_RE = new RegExp(`<\\\\(\\\\*)(/?)(${INNER_TAG_NAMES})\\b`
24750
24750
  // ../protocol/src/tool-family.ts
24751
24751
  var CLOUD_TOOL_SUFFIX = "-cloud";
24752
24752
  var CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
24753
+ var PI_TOOL_ID = "pi";
24754
+ var PI_CLOUD_TOOL_ID = `${PI_TOOL_ID}${CLOUD_TOOL_SUFFIX}`;
24753
24755
 
24754
24756
  // src/deploy/deploy-file.ts
24755
24757
  var import_promises = __toESM(require("fs/promises"), 1);