@clawos-dev/clawd 0.2.598 → 0.2.600

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/dist/cli.cjs CHANGED
@@ -46612,6 +46612,7 @@ function applyCommand(state, command, deps) {
46612
46612
  resolvedModel: _model,
46613
46613
  // aiLabel 是旧对话内容生成的标题,/clear 后属于不存在的话题,一并清掉
46614
46614
  aiLabel: _aiLabel,
46615
+ aiLabelGeneratedAt: _titleGeneratedAt,
46615
46616
  ...rest
46616
46617
  } = next.file;
46617
46618
  void _drop;
@@ -46620,6 +46621,7 @@ function applyCommand(state, command, deps) {
46620
46621
  void _branch;
46621
46622
  void _model;
46622
46623
  void _aiLabel;
46624
+ void _titleGeneratedAt;
46623
46625
  const nextFile = {
46624
46626
  ...rest,
46625
46627
  ...command.nextToolSessionId ? { toolSessionId: command.nextToolSessionId } : {},
@@ -47686,7 +47688,7 @@ var SessionManager = class {
47686
47688
  // sessionId → SessionRunner;在 send / ensureSession 时按需创建
47687
47689
  runners = /* @__PURE__ */ new Map();
47688
47690
  // 标题生成 in-flight 防重(进程内);跨重启防重靠 SessionFile.aiLabelGeneratedAt
47689
- titleGenPending = /* @__PURE__ */ new Set();
47691
+ titleGenPending = /* @__PURE__ */ new Map();
47690
47692
  // 当同步命令执行期间,runner 的 emit-frame 临时 push 到这里;
47691
47693
  // null 表示当前是异步路径(stdout 'data' / proc 'exit' 等),走 deps.broadcastFrame
47692
47694
  currentCollector = null;
@@ -48445,6 +48447,10 @@ var SessionManager = class {
48445
48447
  }
48446
48448
  update(args) {
48447
48449
  const existing = this.getFile(args.sessionId);
48450
+ if (args.patch.model !== void 0 || args.patch.label) {
48451
+ this.titleGenPending.get(args.sessionId)?.abort();
48452
+ this.titleGenPending.delete(args.sessionId);
48453
+ }
48448
48454
  const runner = this.runners.get(args.sessionId);
48449
48455
  if (runner) {
48450
48456
  const { value, broadcast } = this.withCollector(() => {
@@ -48454,6 +48460,12 @@ var SessionManager = class {
48454
48460
  return { response: value, broadcast };
48455
48461
  }
48456
48462
  const updated = { ...existing, ...args.patch, updatedAt: nowIso2(this.deps) };
48463
+ if (args.patch.model !== void 0) {
48464
+ delete updated.resolvedModel;
48465
+ delete updated.resolvedEffort;
48466
+ } else if (args.patch.effort !== void 0) {
48467
+ delete updated.resolvedEffort;
48468
+ }
48457
48469
  this.writeOwned(updated);
48458
48470
  return { response: updated, broadcast: [] };
48459
48471
  }
@@ -48517,6 +48529,7 @@ var SessionManager = class {
48517
48529
  delete(args) {
48518
48530
  const existing = this.findOwnedSession(args.sessionId);
48519
48531
  if (!existing) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, args.sessionId);
48532
+ this.titleGenPending.get(args.sessionId)?.abort();
48520
48533
  const scope = this.scopeForFile(existing);
48521
48534
  const runner = this.runners.get(args.sessionId);
48522
48535
  if (runner) {
@@ -48580,7 +48593,7 @@ var SessionManager = class {
48580
48593
  /**
48581
48594
  * 首条 send 触发的智能标题自产(spec:PR #1196 追加拍板 2026-08-17)。
48582
48595
  * 背景见 tools/title-generator.ts 头注释——CC/codex 两条上游标题数据源都等不来
48583
- * 「首轮即有」,daemon 用会话同家族小模型自己生成,与主 turn 并行,几秒后经
48596
+ * 「首轮即有」,daemon 用本会话选定的引擎和模型自己生成,与主 turn 并行,完成后经
48584
48597
  * manager.update 内部通道写 aiLabel(marker patch:不刷 updatedAt、广播 session:info)。
48585
48598
  *
48586
48599
  * 跳过条件:
@@ -48595,15 +48608,20 @@ var SessionManager = class {
48595
48608
  const sessionId = file.sessionId;
48596
48609
  if (file.aiLabelGeneratedAt || file.label || file.larkChatId) return;
48597
48610
  if (this.titleGenPending.has(sessionId)) return;
48598
- this.titleGenPending.add(sessionId);
48599
- void generate(file.tool ?? "claude", text).then((title) => {
48611
+ const controller = new AbortController();
48612
+ this.titleGenPending.set(sessionId, controller);
48613
+ void generate(file, text, controller.signal).then((title) => {
48614
+ if (controller.signal.aborted) return;
48615
+ const current = this.findOwnedSession(sessionId);
48616
+ if (!current || current.label || current.aiLabelGeneratedAt || current.tool !== file.tool || current.model !== file.model) return;
48600
48617
  if (!title) return;
48601
48618
  this.applyResolvedMeta(sessionId, { aiLabel: title });
48602
48619
  this.deps.logger?.info("title-gen ok", { sessionId, tool: file.tool, title });
48603
48620
  }).catch((err) => {
48621
+ if (controller.signal.aborted) return;
48604
48622
  this.deps.logger?.warn("title-gen failed", { sessionId, message: err.message });
48605
48623
  }).finally(() => {
48606
- this.titleGenPending.delete(sessionId);
48624
+ if (this.titleGenPending.get(sessionId) === controller) this.titleGenPending.delete(sessionId);
48607
48625
  });
48608
48626
  }
48609
48627
  /**
@@ -48842,6 +48860,8 @@ var SessionManager = class {
48842
48860
  }
48843
48861
  newSession(args) {
48844
48862
  const existingFile = this.getFile(args.sessionId);
48863
+ this.titleGenPending.get(args.sessionId)?.abort();
48864
+ this.titleGenPending.delete(args.sessionId);
48845
48865
  const nextToolSessionId = this.deps.mode === "tui" && (existingFile.tool ?? "claude") === "claude" ? (0, import_node_crypto3.randomUUID)() : void 0;
48846
48866
  this.clearPendingTurnEnd(args.sessionId);
48847
48867
  const runner = this.runners.get(args.sessionId);
@@ -48862,6 +48882,8 @@ var SessionManager = class {
48862
48882
  contextWindowSize: _win,
48863
48883
  gitBranch: _branch,
48864
48884
  resolvedModel: _model,
48885
+ aiLabel: _aiLabel,
48886
+ aiLabelGeneratedAt: _titleGeneratedAt,
48865
48887
  ...rest
48866
48888
  } = existing;
48867
48889
  void _drop;
@@ -48869,6 +48891,8 @@ var SessionManager = class {
48869
48891
  void _win;
48870
48892
  void _branch;
48871
48893
  void _model;
48894
+ void _aiLabel;
48895
+ void _titleGeneratedAt;
48872
48896
  const updated = {
48873
48897
  ...rest,
48874
48898
  ...nextToolSessionId ? { toolSessionId: nextToolSessionId } : {},
@@ -48941,6 +48965,8 @@ var SessionManager = class {
48941
48965
  return { runningSessions: running };
48942
48966
  }
48943
48967
  stopAll() {
48968
+ for (const controller of this.titleGenPending.values()) controller.abort();
48969
+ this.titleGenPending.clear();
48944
48970
  for (const r2 of this.runners.values()) {
48945
48971
  r2.input({ kind: "command", command: { kind: "stop" } });
48946
48972
  }
@@ -49117,6 +49143,8 @@ var SessionManager = class {
49117
49143
  `sub-session not found: ${scopeKey(args.scope)}/${args.sessionId}`
49118
49144
  );
49119
49145
  }
49146
+ this.titleGenPending.get(args.sessionId)?.abort();
49147
+ this.titleGenPending.delete(args.sessionId);
49120
49148
  const runner = this.ensureRunnerForScope(file, args.scope);
49121
49149
  const nextToolSessionId = this.deps.mode === "tui" && (file.tool ?? "claude") === "claude" ? (0, import_node_crypto3.randomUUID)() : void 0;
49122
49150
  const { broadcast } = this.withCollector(() => {
@@ -64362,6 +64390,7 @@ var CloudGatewayClient = class {
64362
64390
  personaId: personaId2,
64363
64391
  ...opts.toolSessionId ? { engineSessionId: opts.toolSessionId } : {},
64364
64392
  ...opts.instructions ? { instructions: opts.instructions } : {},
64393
+ ...opts.purpose ? { purpose: opts.purpose } : {},
64365
64394
  logger: this.deps.logger
64366
64395
  });
64367
64396
  }
@@ -64428,7 +64457,7 @@ function parseControlLine(line) {
64428
64457
  }
64429
64458
  }
64430
64459
  function openHttpLineStream(opts) {
64431
- const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, engineSessionId, instructions, logger } = opts;
64460
+ const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, engineSessionId, instructions, purpose, logger } = opts;
64432
64461
  const headers = { authorization: `Bearer ${apiKey}`, "content-type": "application/json" };
64433
64462
  const ac = new AbortController();
64434
64463
  const lineCbs = [];
@@ -64496,7 +64525,7 @@ function openHttpLineStream(opts) {
64496
64525
  res = await fetchImpl(gatewayUrl + GATEWAY_ROUTES.session(personaId2), {
64497
64526
  method: "POST",
64498
64527
  headers,
64499
- body: JSON.stringify({ ...engineSessionId ? { engineSessionId } : {}, ...instructions ? { instructions } : {} }),
64528
+ body: JSON.stringify({ ...engineSessionId ? { engineSessionId } : {}, ...instructions ? { instructions } : {}, ...purpose ? { purpose } : {} }),
64500
64529
  signal: ac.signal
64501
64530
  });
64502
64531
  } catch (e) {
@@ -64613,8 +64642,8 @@ init_protocol();
64613
64642
  var import_node_fs38 = __toESM(require("fs"), 1);
64614
64643
  function cloudThreadParams(ctx, paths) {
64615
64644
  const { model: _model, ...base } = threadStartParams(ctx);
64616
- const developerInstructions = [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
64617
- return { ...base, cwd: paths.personaDir, approvalPolicy: "never", sandbox: "danger-full-access", developerInstructions };
64645
+ const developerInstructions = ctx.titleOnly ? "\u53EA\u6839\u636E\u7528\u6237\u6D88\u606F\u8FD4\u56DE\u7B80\u77ED\u6807\u9898\u3002\u4E0D\u8981\u8C03\u7528\u5DE5\u5177\u3001\u8BFB\u53D6\u6587\u4EF6\u3001\u8BBF\u95EE\u7F51\u7EDC\u6216\u4FEE\u6539\u4EFB\u4F55\u5185\u5BB9\u3002" : [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
64646
+ return { ...base, cwd: paths.personaDir, approvalPolicy: "never", sandbox: ctx.titleOnly ? "read-only" : "danger-full-access", developerInstructions };
64618
64647
  }
64619
64648
  function deferredLineStream() {
64620
64649
  let target = null;
@@ -64701,7 +64730,9 @@ function createCloudCodexSession(ctx, sink, deps) {
64701
64730
  // 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
64702
64731
  onTurnStarting: () => channel?.markNextLineTurnStart(),
64703
64732
  // 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
64704
- onTurnCompleted: (outcome, afterFirstByteMs) => void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} }),
64733
+ onTurnCompleted: (outcome, afterFirstByteMs) => {
64734
+ if (!ctx.titleOnly) void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} });
64735
+ },
64705
64736
  now: deps.now
64706
64737
  });
64707
64738
  if (!personaId2) {
@@ -64711,7 +64742,7 @@ function createCloudCodexSession(ctx, sink, deps) {
64711
64742
  });
64712
64743
  return driver.session;
64713
64744
  }
64714
- void deps.client.openSession(personaId2).then(
64745
+ void (ctx.titleOnly ? deps.client.openSession(personaId2, { purpose: "title" }) : deps.client.openSession(personaId2)).then(
64715
64746
  (opened) => {
64716
64747
  channel = opened;
64717
64748
  channel.onControl((c2) => {
@@ -65431,7 +65462,9 @@ function createCloudPiSession(ctx, sink, deps) {
65431
65462
  // 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
65432
65463
  onTurnStarting: () => channel?.markNextLineTurnStart(),
65433
65464
  // 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
65434
- onTurnCompleted: (outcome, afterFirstByteMs) => void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} }),
65465
+ onTurnCompleted: (outcome, afterFirstByteMs) => {
65466
+ if (!ctx.titleOnly) void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} });
65467
+ },
65435
65468
  now: deps.now
65436
65469
  });
65437
65470
  if (!personaId2) {
@@ -65441,7 +65474,7 @@ function createCloudPiSession(ctx, sink, deps) {
65441
65474
  });
65442
65475
  return driver.session;
65443
65476
  }
65444
- void deps.client.openSession(personaId2, { toolSessionId: sessionId, instructions: cloudPiInstructions(ctx) }).then(
65477
+ void deps.client.openSession(personaId2, { toolSessionId: sessionId, instructions: ctx.titleOnly ? "" : cloudPiInstructions(ctx), ...ctx.titleOnly ? { purpose: "title" } : {} }).then(
65445
65478
  (opened) => {
65446
65479
  channel = opened;
65447
65480
  channel.onControl((c2) => {
@@ -66313,10 +66346,6 @@ var import_node_fs45 = __toESM(require("fs"), 1);
66313
66346
  var import_node_os18 = __toESM(require("os"), 1);
66314
66347
  var import_node_path49 = __toESM(require("path"), 1);
66315
66348
  var TITLE_GEN_DIR_PREFIX = "clawd-title-";
66316
- var TITLE_MODEL_BY_TOOL = {
66317
- claude: "haiku",
66318
- codex: "gpt-5.4-mini"
66319
- };
66320
66349
  var TITLE_INPUT_MAX_CHARS = 400;
66321
66350
  var DEFAULT_TITLE_GEN_TIMEOUT_MS = 45e3;
66322
66351
  var outFileSeq = 0;
@@ -66337,8 +66366,8 @@ function sanitizeTitle(raw) {
66337
66366
  if (!stripped) return null;
66338
66367
  return stripped.slice(0, 60);
66339
66368
  }
66340
- function buildClaudeTitleArgs(prompt) {
66341
- return ["-p", "--model", TITLE_MODEL_BY_TOOL.claude, prompt];
66369
+ function buildClaudeTitleArgs(prompt, model) {
66370
+ return ["-p", "--no-session-persistence", "--tools", "", ...model ? ["--model", model] : [], prompt];
66342
66371
  }
66343
66372
  var CODEX_TITLE_SCHEMA = JSON.stringify({
66344
66373
  type: "object",
@@ -66358,7 +66387,7 @@ function parseTitleJson(raw) {
66358
66387
  }
66359
66388
  return sanitizeTitle(text);
66360
66389
  }
66361
- function buildCodexTitleArgs(prompt, outFile, schemaFile) {
66390
+ function buildCodexTitleArgs(prompt, outFile, schemaFile, model) {
66362
66391
  return [
66363
66392
  "exec",
66364
66393
  "--ephemeral",
@@ -66367,8 +66396,7 @@ function buildCodexTitleArgs(prompt, outFile, schemaFile) {
66367
66396
  schemaFile,
66368
66397
  "-s",
66369
66398
  "read-only",
66370
- "-m",
66371
- TITLE_MODEL_BY_TOOL.codex,
66399
+ ...model ? ["-m", model] : [],
66372
66400
  "--color",
66373
66401
  "never",
66374
66402
  "-o",
@@ -66376,10 +66404,35 @@ function buildCodexTitleArgs(prompt, outFile, schemaFile) {
66376
66404
  prompt
66377
66405
  ];
66378
66406
  }
66379
- async function generateSessionTitle(tool, firstMessage, deps = {}) {
66380
- if (tool !== "claude" && !isCodexFamily(tool)) return null;
66407
+ function buildPiTitleArgs(prompt, model) {
66408
+ return [
66409
+ "-p",
66410
+ "--mode",
66411
+ "text",
66412
+ "--no-session",
66413
+ "--no-tools",
66414
+ "--no-skills",
66415
+ "--no-context-files",
66416
+ ...model ? ["--model", model] : [],
66417
+ "--",
66418
+ prompt
66419
+ ];
66420
+ }
66421
+ async function generateSessionTitle(file, firstMessage, deps = {}) {
66422
+ const tool = file.tool ?? "claude";
66423
+ if (tool !== "claude" && tool !== "pi" && tool !== "pi-cloud" && !isCodexFamily(tool)) return null;
66381
66424
  const text = firstMessage.trim();
66382
- if (!text) return null;
66425
+ if (!text || deps.signal?.aborted) return null;
66426
+ if (tool === "codex-cloud" || tool === "pi-cloud") {
66427
+ if (!deps.generateCloud) return null;
66428
+ try {
66429
+ const raw = await deps.generateCloud(file, buildClaudeTitlePrompt(text), deps.signal);
66430
+ return raw && !deps.signal?.aborted ? parseTitleJson(raw) : null;
66431
+ } catch (err) {
66432
+ deps.logger?.warn("title-gen cloud failed", { tool, message: err.message });
66433
+ return null;
66434
+ }
66435
+ }
66383
66436
  let workDir;
66384
66437
  try {
66385
66438
  workDir = import_node_fs45.default.mkdtempSync(import_node_path49.default.join(import_node_os18.default.tmpdir(), TITLE_GEN_DIR_PREFIX));
@@ -66394,7 +66447,7 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
66394
66447
  };
66395
66448
  const outFile = import_node_path49.default.join(workDir, `codex-title-${process.pid}-${++outFileSeq}-${Date.now()}.txt`);
66396
66449
  const schemaFile = import_node_path49.default.join(workDir, "title-schema.json");
66397
- if (isCodexFamily(tool)) {
66450
+ if (tool === "codex") {
66398
66451
  try {
66399
66452
  import_node_fs45.default.writeFileSync(schemaFile, CODEX_TITLE_SCHEMA);
66400
66453
  } catch {
@@ -66402,14 +66455,16 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
66402
66455
  return null;
66403
66456
  }
66404
66457
  }
66405
- const cmd = tool === "claude" ? process.env.CLAUDE_BIN ?? "claude" : process.env.CODEX_BIN ?? "codex";
66406
- const args = tool === "claude" ? buildClaudeTitleArgs(buildClaudeTitlePrompt(text)) : buildCodexTitleArgs(buildTitlePrompt(text), outFile, schemaFile);
66458
+ const cmd = tool === "claude" ? process.env.CLAUDE_BIN ?? "claude" : tool === "pi" ? process.env.PI_BIN ?? "pi" : process.env.CODEX_BIN ?? "codex";
66459
+ const chosenModel = file.resolvedModel || file.model;
66460
+ const model = tool === "codex" ? sanitizeCodexModel(chosenModel) : chosenModel;
66461
+ const args = tool === "claude" ? buildClaudeTitleArgs(buildClaudeTitlePrompt(text), model) : tool === "pi" ? buildPiTitleArgs(buildClaudeTitlePrompt(text), model) : buildCodexTitleArgs(buildTitlePrompt(text), outFile, schemaFile, model);
66407
66462
  const doSpawn = deps.spawnOverride ?? ((c2, a2, o2) => (0, import_node_child_process13.spawn)(c2, a2, { ...o2, stdio: ["ignore", "pipe", "pipe"] }));
66408
66463
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TITLE_GEN_TIMEOUT_MS;
66409
66464
  return await new Promise((resolve9) => {
66410
66465
  let proc;
66411
66466
  try {
66412
- proc = doSpawn(cmd, args, { cwd: workDir });
66467
+ proc = doSpawn(cmd, args, { cwd: model ? workDir : file.cwd });
66413
66468
  } catch (err) {
66414
66469
  deps.logger?.warn("title-gen spawn failed", { tool, message: err.message });
66415
66470
  cleanupWorkDir();
@@ -66421,10 +66476,13 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
66421
66476
  if (settled) return;
66422
66477
  settled = true;
66423
66478
  clearTimeout(timer);
66479
+ deps.signal?.removeEventListener("abort", onAbort);
66424
66480
  if (proc.exitCode === null && proc.signalCode === null) proc.kill("SIGKILL");
66425
66481
  cleanupWorkDir();
66426
66482
  resolve9(title);
66427
66483
  };
66484
+ const onAbort = () => finish(null);
66485
+ deps.signal?.addEventListener("abort", onAbort, { once: true });
66428
66486
  const timer = setTimeout(() => {
66429
66487
  deps.logger?.warn("title-gen timed out", { tool, timeoutMs });
66430
66488
  finish(null);
@@ -66443,7 +66501,7 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
66443
66501
  deps.logger?.warn("title-gen exited non-zero", { tool, code });
66444
66502
  return finish(null);
66445
66503
  }
66446
- if (tool === "claude") return finish(parseTitleJson(stdout));
66504
+ if (tool === "claude" || tool === "pi") return finish(parseTitleJson(stdout));
66447
66505
  let fileText = "";
66448
66506
  try {
66449
66507
  fileText = import_node_fs45.default.readFileSync(outFile, "utf8");
@@ -66455,6 +66513,57 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
66455
66513
  });
66456
66514
  }
66457
66515
 
66516
+ // src/tools/cloud-title.ts
66517
+ function generateCloudTitle(file, prompt, signal, getAdapter2, timeoutMs = 45e3) {
66518
+ if (signal?.aborted) return Promise.resolve(null);
66519
+ return new Promise((resolve9) => {
66520
+ let agent;
66521
+ let settled = false;
66522
+ let finalText = "";
66523
+ let partialText = "";
66524
+ let failed = false;
66525
+ const finish = (raw) => {
66526
+ if (settled) return;
66527
+ settled = true;
66528
+ clearTimeout(timer);
66529
+ signal?.removeEventListener("abort", onAbort);
66530
+ try {
66531
+ void Promise.resolve(agent?.stop("SIGKILL")).catch(() => {
66532
+ });
66533
+ } catch {
66534
+ }
66535
+ resolve9(raw);
66536
+ };
66537
+ const onAbort = () => finish(null);
66538
+ const timer = setTimeout(() => finish(null), timeoutMs);
66539
+ signal?.addEventListener("abort", onAbort, { once: true });
66540
+ try {
66541
+ const adapter = getAdapter2(file.tool);
66542
+ if (!adapter.createSession) return finish(null);
66543
+ agent = adapter.createSession({ cwd: file.cwd, env: {}, titleOnly: true }, {
66544
+ pushEvents(events) {
66545
+ for (const event of events) {
66546
+ if (event.kind === "text") {
66547
+ if (event.partialId) partialText += event.text;
66548
+ else finalText = event.text;
66549
+ } else if (event.kind === "error" || event.kind === "tool_call" || event.kind === "permission_request") {
66550
+ failed = true;
66551
+ finish(null);
66552
+ } else if (event.kind === "turn_end") {
66553
+ finish(failed ? null : finalText || partialText || null);
66554
+ }
66555
+ }
66556
+ },
66557
+ onExit: () => finish(null),
66558
+ onError: () => finish(null)
66559
+ });
66560
+ agent.startTurn(prompt);
66561
+ } catch {
66562
+ finish(null);
66563
+ }
66564
+ });
66565
+ }
66566
+
66458
66567
  // ../node_modules/.pnpm/ws@8.20.0/node_modules/ws/wrapper.mjs
66459
66568
  var import_stream = __toESM(require_stream(), 1);
66460
66569
  var import_extension2 = __toESM(require_extension(), 1);
@@ -72959,7 +73068,7 @@ function computeMethodAccess(args) {
72959
73068
  }
72960
73069
 
72961
73070
  // src/version.ts
72962
- var version = "0.2.598".length > 0 ? "0.2.598" : "dev";
73071
+ var version = "0.2.600".length > 0 ? "0.2.600" : "dev";
72963
73072
 
72964
73073
  // src/cli-probe/probe.ts
72965
73074
  var fs83 = __toESM(require("fs"), 1);
@@ -75912,9 +76021,13 @@ async function startDaemon(config) {
75912
76021
  // per-persona 凭据注入(M1b Step 5):runner 在解释 spawn effect 时按 ctx.personaId 调,
75913
76022
  // 产出拼进 ctx.mcpConfigs。**不在 reducer 里**——渲染要读写 fs,那边是纯函数。
75914
76023
  resolvePersonaMcpConfig,
75915
- // 会话标题自产(首条 send 并行 fire,claude→haiku / codex→gpt-5.4-mini)。
76024
+ // 会话标题自产(首条 send 并行 fire,沿用会话引擎和模型)。
75916
76025
  // 不注入 = 功能关(manager 测试默认关闭防烧真调用),这里是唯一的生产装配点。
75917
- generateTitle: (tool, firstMessage) => generateSessionTitle(tool, firstMessage, { logger }),
76026
+ generateTitle: (file, firstMessage, signal) => generateSessionTitle(file, firstMessage, {
76027
+ logger,
76028
+ signal,
76029
+ generateCloud: (titleFile, prompt, titleSignal) => generateCloudTitle(titleFile, prompt, titleSignal, getAdapter)
76030
+ }),
75918
76031
  broadcastFrame: (frame, target) => {
75919
76032
  if (target === "all") {
75920
76033
  transport?.broadcastAll(frame);