@clawos-dev/clawd 0.2.593 → 0.2.595

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
@@ -450,8 +450,8 @@ var init_parseUtil = __esm({
450
450
  init_errors();
451
451
  init_en();
452
452
  makeIssue = (params) => {
453
- const { data, path: path105, errorMaps, issueData } = params;
454
- const fullPath = [...path105, ...issueData.path || []];
453
+ const { data, path: path106, errorMaps, issueData } = params;
454
+ const fullPath = [...path106, ...issueData.path || []];
455
455
  const fullIssue = {
456
456
  ...issueData,
457
457
  path: fullPath
@@ -762,11 +762,11 @@ var init_types = __esm({
762
762
  init_parseUtil();
763
763
  init_util();
764
764
  ParseInputLazyPath = class {
765
- constructor(parent, value, path105, key) {
765
+ constructor(parent, value, path106, key) {
766
766
  this._cachedPath = [];
767
767
  this.parent = parent;
768
768
  this.data = value;
769
- this._path = path105;
769
+ this._path = path106;
770
770
  this._key = key;
771
771
  }
772
772
  get path() {
@@ -8144,8 +8144,8 @@ var init_schemas = __esm({
8144
8144
  tui: external_exports.boolean(),
8145
8145
  observe: external_exports.boolean(),
8146
8146
  fileSharing: external_exports.boolean(),
8147
- // fork:session:fork 走 forkSession(claude jsonl 专用,派生新 jsonl)。codex 无 claude jsonl
8148
- // → fork 不可用。UI 据此 gate assistant 消息上的 fork 按钮(codex 一轮多条消息会刷出一堆按钮且点了报错)。
8147
+ // fork:Claude 派生 transcript JSONL;本机 Codex 按已完成 turn 调原生 thread/fork。
8148
+ // UI 只在有有效 fork 锚点的回复气泡上显示入口。
8149
8149
  fork: external_exports.boolean()
8150
8150
  });
8151
8151
  CapabilitiesResponseSchema = external_exports.object({
@@ -8489,7 +8489,9 @@ var init_schemas = __esm({
8489
8489
  kind: external_exports.literal("turn_end"),
8490
8490
  // 本轮耗时(ms)。SDK=result.duration_ms;TUI=turn_duration.durationMs;缺失时 reducer 按 turnStartedAt 兜底
8491
8491
  durationMs: external_exports.number().int().nonnegative().optional(),
8492
- reason: external_exports.enum(TURN_END_REASONS).optional()
8492
+ reason: external_exports.enum(TURN_END_REASONS).optional(),
8493
+ /** Codex 完成轮次的原生 id;用于在该轮末条回复上显示 Fork / Quick Ask。 */
8494
+ turnId: external_exports.string().min(1).optional()
8493
8495
  }),
8494
8496
  // SDK system 帧 task_started / task_progress / task_notification 的归一化产物:
8495
8497
  // 子 agent 执行过程中实时上报 agentId / lastToolName / stats,UI pending 期就能开抽屉、看进度。
@@ -8707,7 +8709,12 @@ var init_schemas = __esm({
8707
8709
  SessionForkArgs = external_exports.object({
8708
8710
  cwd: external_exports.string().min(1),
8709
8711
  toolSessionId: external_exports.string().min(1),
8710
- messageUuid: external_exports.string().min(1),
8712
+ /** Claude 消息 UUID;Codex 改用 turnId。 */
8713
+ messageUuid: external_exports.string().min(1).optional(),
8714
+ /** Codex 原生已完成 turn ID。 */
8715
+ turnId: external_exports.string().min(1).optional(),
8716
+ /** 源 clawd session ID;Codex 必填,handler 用它校验 thread 归属。 */
8717
+ sourceSessionId: external_exports.string().min(1).optional(),
8711
8718
  /**
8712
8719
  * 派生 jsonl 的落点 cwd(缺省 = 源文件所在目录,保持老行为)。
8713
8720
  *
@@ -8718,7 +8725,7 @@ var init_schemas = __esm({
8718
8725
  * 正确编排:先 sessionCreate 拿到 daemon 定的真实 cwd,再以它作 targetCwd fork。
8719
8726
  */
8720
8727
  targetCwd: external_exports.string().min(1).optional()
8721
- });
8728
+ }).refine((args) => Boolean(args.messageUuid || args.turnId), "messageUuid or turnId required");
8722
8729
  SessionForkResponseSchema = external_exports.object({
8723
8730
  forkedToolSessionId: external_exports.string().min(1),
8724
8731
  forkedFilePath: external_exports.string().min(1)
@@ -9103,8 +9110,11 @@ var init_methods = __esm({
9103
9110
  args: SessionIdArgs
9104
9111
  },
9105
9112
  "session:fork": {
9106
- summary: "\u4ECE\u6307\u5B9A message \u5904 fork \u51FA\u65B0 transcript\uFF08\u622A\u65AD + \u91CD\u5199 sessionId/parentUuid\uFF09\uFF0C\u8FD4\u56DE\u65B0 toolSessionId\uFF1B\u4E0D\u81EA\u52A8\u62C9\u8D77\u8FDB\u7A0B",
9107
- args: SessionForkArgs
9113
+ summary: "\u4ECE Claude \u6D88\u606F\u6216 Codex \u5DF2\u5B8C\u6210 turn \u6D3E\u751F\u65B0\u4F1A\u8BDD\uFF0C\u8FD4\u56DE\u65B0 toolSessionId\uFF1B\u4E0D\u81EA\u52A8\u62C9\u8D77\u4F1A\u8BDD\u8FDB\u7A0B",
9114
+ args: SessionForkArgs,
9115
+ argsConstraints: {
9116
+ anyOf: [{ required: ["messageUuid"] }, { required: ["turnId"] }]
9117
+ }
9108
9118
  },
9109
9119
  "session:new": {
9110
9120
  summary: "\u5728**\u5DF2\u6709**\u4F1A\u8BDD\u4E0A\u6E05\u7A7A\u4E0A\u4E0B\u6587\u5F00\u65B0\u4E00\u8F6E\uFF0C\u76F8\u5F53\u4E8E CC \u7684 /clear\uFF08\u4E0D\u662F\u5EFA\u65B0\u4F1A\u8BDD\u2014\u2014\u5EFA\u65B0\u4F1A\u8BDD\u7528 session:create\uFF09",
@@ -10325,8 +10335,8 @@ var require_req = __commonJS({
10325
10335
  if (req.originalUrl) {
10326
10336
  _req.url = req.originalUrl;
10327
10337
  } else {
10328
- const path105 = req.path;
10329
- _req.url = typeof path105 === "string" ? path105 : req.url ? req.url.path || req.url : void 0;
10338
+ const path106 = req.path;
10339
+ _req.url = typeof path106 === "string" ? path106 : req.url ? req.url.path || req.url : void 0;
10330
10340
  }
10331
10341
  if (req.query) {
10332
10342
  _req.query = req.query;
@@ -10491,14 +10501,14 @@ var require_redact = __commonJS({
10491
10501
  }
10492
10502
  return obj;
10493
10503
  }
10494
- function parsePath(path105) {
10504
+ function parsePath(path106) {
10495
10505
  const parts = [];
10496
10506
  let current = "";
10497
10507
  let inBrackets = false;
10498
10508
  let inQuotes = false;
10499
10509
  let quoteChar = "";
10500
- for (let i2 = 0; i2 < path105.length; i2++) {
10501
- const char = path105[i2];
10510
+ for (let i2 = 0; i2 < path106.length; i2++) {
10511
+ const char = path106[i2];
10502
10512
  if (!inBrackets && char === ".") {
10503
10513
  if (current) {
10504
10514
  parts.push(current);
@@ -10629,10 +10639,10 @@ var require_redact = __commonJS({
10629
10639
  return current;
10630
10640
  }
10631
10641
  function redactPaths(obj, paths, censor, remove = false) {
10632
- for (const path105 of paths) {
10633
- const parts = parsePath(path105);
10642
+ for (const path106 of paths) {
10643
+ const parts = parsePath(path106);
10634
10644
  if (parts.includes("*")) {
10635
- redactWildcardPath(obj, parts, censor, path105, remove);
10645
+ redactWildcardPath(obj, parts, censor, path106, remove);
10636
10646
  } else {
10637
10647
  if (remove) {
10638
10648
  removeKey(obj, parts);
@@ -10717,8 +10727,8 @@ var require_redact = __commonJS({
10717
10727
  }
10718
10728
  } else {
10719
10729
  if (afterWildcard.includes("*")) {
10720
- const wrappedCensor = typeof censor === "function" ? (value, path105) => {
10721
- const fullPath = [...pathArray.slice(0, pathLength), ...path105];
10730
+ const wrappedCensor = typeof censor === "function" ? (value, path106) => {
10731
+ const fullPath = [...pathArray.slice(0, pathLength), ...path106];
10722
10732
  return censor(value, fullPath);
10723
10733
  } : censor;
10724
10734
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -10753,8 +10763,8 @@ var require_redact = __commonJS({
10753
10763
  return null;
10754
10764
  }
10755
10765
  const pathStructure = /* @__PURE__ */ new Map();
10756
- for (const path105 of pathsToClone) {
10757
- const parts = parsePath(path105);
10766
+ for (const path106 of pathsToClone) {
10767
+ const parts = parsePath(path106);
10758
10768
  let current = pathStructure;
10759
10769
  for (let i2 = 0; i2 < parts.length; i2++) {
10760
10770
  const part = parts[i2];
@@ -10806,24 +10816,24 @@ var require_redact = __commonJS({
10806
10816
  }
10807
10817
  return cloneSelectively(obj, pathStructure);
10808
10818
  }
10809
- function validatePath(path105) {
10810
- if (typeof path105 !== "string") {
10819
+ function validatePath(path106) {
10820
+ if (typeof path106 !== "string") {
10811
10821
  throw new Error("Paths must be (non-empty) strings");
10812
10822
  }
10813
- if (path105 === "") {
10823
+ if (path106 === "") {
10814
10824
  throw new Error("Invalid redaction path ()");
10815
10825
  }
10816
- if (path105.includes("..")) {
10817
- throw new Error(`Invalid redaction path (${path105})`);
10826
+ if (path106.includes("..")) {
10827
+ throw new Error(`Invalid redaction path (${path106})`);
10818
10828
  }
10819
- if (path105.includes(",")) {
10820
- throw new Error(`Invalid redaction path (${path105})`);
10829
+ if (path106.includes(",")) {
10830
+ throw new Error(`Invalid redaction path (${path106})`);
10821
10831
  }
10822
10832
  let bracketCount = 0;
10823
10833
  let inQuotes = false;
10824
10834
  let quoteChar = "";
10825
- for (let i2 = 0; i2 < path105.length; i2++) {
10826
- const char = path105[i2];
10835
+ for (let i2 = 0; i2 < path106.length; i2++) {
10836
+ const char = path106[i2];
10827
10837
  if ((char === '"' || char === "'") && bracketCount > 0) {
10828
10838
  if (!inQuotes) {
10829
10839
  inQuotes = true;
@@ -10837,20 +10847,20 @@ var require_redact = __commonJS({
10837
10847
  } else if (char === "]" && !inQuotes) {
10838
10848
  bracketCount--;
10839
10849
  if (bracketCount < 0) {
10840
- throw new Error(`Invalid redaction path (${path105})`);
10850
+ throw new Error(`Invalid redaction path (${path106})`);
10841
10851
  }
10842
10852
  }
10843
10853
  }
10844
10854
  if (bracketCount !== 0) {
10845
- throw new Error(`Invalid redaction path (${path105})`);
10855
+ throw new Error(`Invalid redaction path (${path106})`);
10846
10856
  }
10847
10857
  }
10848
10858
  function validatePaths(paths) {
10849
10859
  if (!Array.isArray(paths)) {
10850
10860
  throw new TypeError("paths must be an array");
10851
10861
  }
10852
- for (const path105 of paths) {
10853
- validatePath(path105);
10862
+ for (const path106 of paths) {
10863
+ validatePath(path106);
10854
10864
  }
10855
10865
  }
10856
10866
  function slowRedact(options = {}) {
@@ -11018,8 +11028,8 @@ var require_redaction = __commonJS({
11018
11028
  if (shape[k3] === null) {
11019
11029
  o2[k3] = (value) => topCensor(value, [k3]);
11020
11030
  } else {
11021
- const wrappedCensor = typeof censor === "function" ? (value, path105) => {
11022
- return censor(value, [k3, ...path105]);
11031
+ const wrappedCensor = typeof censor === "function" ? (value, path106) => {
11032
+ return censor(value, [k3, ...path106]);
11023
11033
  } : censor;
11024
11034
  o2[k3] = Redact({
11025
11035
  paths: shape[k3],
@@ -11240,7 +11250,7 @@ var require_sonic_boom = __commonJS({
11240
11250
  var fs95 = require("fs");
11241
11251
  var EventEmitter2 = require("events");
11242
11252
  var inherits = require("util").inherits;
11243
- var path105 = require("path");
11253
+ var path106 = require("path");
11244
11254
  var sleep2 = require_atomic_sleep();
11245
11255
  var assert = require("assert");
11246
11256
  var BUSY_WRITE_TIMEOUT = 100;
@@ -11294,7 +11304,7 @@ var require_sonic_boom = __commonJS({
11294
11304
  const mode = sonic.mode;
11295
11305
  if (sonic.sync) {
11296
11306
  try {
11297
- if (sonic.mkdir) fs95.mkdirSync(path105.dirname(file), { recursive: true });
11307
+ if (sonic.mkdir) fs95.mkdirSync(path106.dirname(file), { recursive: true });
11298
11308
  const fd = fs95.openSync(file, flags, mode);
11299
11309
  fileOpened(null, fd);
11300
11310
  } catch (err) {
@@ -11302,7 +11312,7 @@ var require_sonic_boom = __commonJS({
11302
11312
  throw err;
11303
11313
  }
11304
11314
  } else if (sonic.mkdir) {
11305
- fs95.mkdir(path105.dirname(file), { recursive: true }, (err) => {
11315
+ fs95.mkdir(path106.dirname(file), { recursive: true }, (err) => {
11306
11316
  if (err) return fileOpened(err);
11307
11317
  fs95.open(file, flags, mode, fileOpened);
11308
11318
  });
@@ -14162,7 +14172,7 @@ var require_multistream = __commonJS({
14162
14172
  var require_pino = __commonJS({
14163
14173
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
14164
14174
  "use strict";
14165
- var os37 = require("os");
14175
+ var os36 = require("os");
14166
14176
  var stdSerializers = require_pino_std_serializers();
14167
14177
  var caller = require_caller();
14168
14178
  var redaction = require_redaction();
@@ -14209,7 +14219,7 @@ var require_pino = __commonJS({
14209
14219
  } = symbols;
14210
14220
  var { epochTime, nullTime } = time;
14211
14221
  var { pid } = process;
14212
- var hostname = os37.hostname();
14222
+ var hostname = os36.hostname();
14213
14223
  var defaultErrorSerializer = stdSerializers.err;
14214
14224
  var defaultOptions2 = {
14215
14225
  level: "info",
@@ -14933,11 +14943,11 @@ var init_lib = __esm({
14933
14943
  }
14934
14944
  }
14935
14945
  },
14936
- addToPath: function addToPath(path105, added, removed, oldPosInc, options) {
14937
- var last = path105.lastComponent;
14946
+ addToPath: function addToPath(path106, added, removed, oldPosInc, options) {
14947
+ var last = path106.lastComponent;
14938
14948
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
14939
14949
  return {
14940
- oldPos: path105.oldPos + oldPosInc,
14950
+ oldPos: path106.oldPos + oldPosInc,
14941
14951
  lastComponent: {
14942
14952
  count: last.count + 1,
14943
14953
  added,
@@ -14947,7 +14957,7 @@ var init_lib = __esm({
14947
14957
  };
14948
14958
  } else {
14949
14959
  return {
14950
- oldPos: path105.oldPos + oldPosInc,
14960
+ oldPos: path106.oldPos + oldPosInc,
14951
14961
  lastComponent: {
14952
14962
  count: 1,
14953
14963
  added,
@@ -15529,10 +15539,10 @@ function attachmentToHistoryMessage(o2, ts) {
15529
15539
  const memories = raw.map((m3) => {
15530
15540
  if (!m3 || typeof m3 !== "object") return null;
15531
15541
  const rec6 = m3;
15532
- const path105 = typeof rec6.path === "string" ? rec6.path : null;
15542
+ const path106 = typeof rec6.path === "string" ? rec6.path : null;
15533
15543
  const content = typeof rec6.content === "string" ? rec6.content : null;
15534
- if (!path105 || content == null) return null;
15535
- const entry = { path: path105, content };
15544
+ if (!path106 || content == null) return null;
15545
+ const entry = { path: path106, content };
15536
15546
  if (typeof rec6.mtimeMs === "number") entry.mtimeMs = rec6.mtimeMs;
15537
15547
  return entry;
15538
15548
  }).filter((m3) => m3 !== null);
@@ -16398,10 +16408,10 @@ function parseAttachment(obj) {
16398
16408
  const memories = raw.map((m3) => {
16399
16409
  if (!m3 || typeof m3 !== "object") return null;
16400
16410
  const rec6 = m3;
16401
- const path105 = typeof rec6.path === "string" ? rec6.path : null;
16411
+ const path106 = typeof rec6.path === "string" ? rec6.path : null;
16402
16412
  const content = typeof rec6.content === "string" ? rec6.content : null;
16403
- if (!path105 || content == null) return null;
16404
- const out = { path: path105, content };
16413
+ if (!path106 || content == null) return null;
16414
+ const out = { path: path106, content };
16405
16415
  if (typeof rec6.mtimeMs === "number") out.mtimeMs = rec6.mtimeMs;
16406
16416
  return out;
16407
16417
  }).filter((m3) => m3 !== null);
@@ -37375,8 +37385,8 @@ var require_utils = __commonJS({
37375
37385
  var result = transform[inputType][outputType](input);
37376
37386
  return result;
37377
37387
  };
37378
- exports2.resolve = function(path105) {
37379
- var parts = path105.split("/");
37388
+ exports2.resolve = function(path106) {
37389
+ var parts = path106.split("/");
37380
37390
  var result = [];
37381
37391
  for (var index = 0; index < parts.length; index++) {
37382
37392
  var part = parts[index];
@@ -43229,18 +43239,18 @@ var require_object = __commonJS({
43229
43239
  var object = new ZipObject(name, zipObjectContent, o2);
43230
43240
  this.files[name] = object;
43231
43241
  };
43232
- var parentFolder = function(path105) {
43233
- if (path105.slice(-1) === "/") {
43234
- path105 = path105.substring(0, path105.length - 1);
43242
+ var parentFolder = function(path106) {
43243
+ if (path106.slice(-1) === "/") {
43244
+ path106 = path106.substring(0, path106.length - 1);
43235
43245
  }
43236
- var lastSlash = path105.lastIndexOf("/");
43237
- return lastSlash > 0 ? path105.substring(0, lastSlash) : "";
43246
+ var lastSlash = path106.lastIndexOf("/");
43247
+ return lastSlash > 0 ? path106.substring(0, lastSlash) : "";
43238
43248
  };
43239
- var forceTrailingSlash = function(path105) {
43240
- if (path105.slice(-1) !== "/") {
43241
- path105 += "/";
43249
+ var forceTrailingSlash = function(path106) {
43250
+ if (path106.slice(-1) !== "/") {
43251
+ path106 += "/";
43242
43252
  }
43243
- return path105;
43253
+ return path106;
43244
43254
  };
43245
43255
  var folderAdd = function(name, createFolders) {
43246
43256
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -44242,7 +44252,7 @@ var require_lib3 = __commonJS({
44242
44252
  // src/run-case/recorder.ts
44243
44253
  function startRunCaseRecorder(opts) {
44244
44254
  const now = opts.now ?? Date.now;
44245
- const dir = import_node_path84.default.dirname(opts.recordPath);
44255
+ const dir = import_node_path85.default.dirname(opts.recordPath);
44246
44256
  let stream = null;
44247
44257
  let closing = false;
44248
44258
  let closedSettled = false;
@@ -44282,12 +44292,12 @@ function startRunCaseRecorder(opts) {
44282
44292
  };
44283
44293
  return { tap, close, closed };
44284
44294
  }
44285
- var import_node_fs66, import_node_path84;
44295
+ var import_node_fs66, import_node_path85;
44286
44296
  var init_recorder = __esm({
44287
44297
  "src/run-case/recorder.ts"() {
44288
44298
  "use strict";
44289
44299
  import_node_fs66 = __toESM(require("fs"), 1);
44290
- import_node_path84 = __toESM(require("path"), 1);
44300
+ import_node_path85 = __toESM(require("path"), 1);
44291
44301
  }
44292
44302
  });
44293
44303
 
@@ -44330,7 +44340,7 @@ var init_wire = __esm({
44330
44340
  // src/run-case/controller.ts
44331
44341
  async function runController(opts) {
44332
44342
  const now = opts.now ?? Date.now;
44333
- const cwd = opts.cwd ?? (0, import_node_fs67.mkdtempSync)(import_node_path85.default.join(import_node_os33.default.tmpdir(), "clawd-runcase-"));
44343
+ const cwd = opts.cwd ?? (0, import_node_fs67.mkdtempSync)(import_node_path86.default.join(import_node_os32.default.tmpdir(), "clawd-runcase-"));
44334
44344
  const ownsCwd = opts.cwd === void 0;
44335
44345
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
44336
44346
  const spawnCtx = { cwd };
@@ -44497,13 +44507,13 @@ async function runController(opts) {
44497
44507
  }
44498
44508
  return exitCode ?? 0;
44499
44509
  }
44500
- var import_node_fs67, import_node_os33, import_node_path85;
44510
+ var import_node_fs67, import_node_os32, import_node_path86;
44501
44511
  var init_controller = __esm({
44502
44512
  "src/run-case/controller.ts"() {
44503
44513
  "use strict";
44504
44514
  import_node_fs67 = require("fs");
44505
- import_node_os33 = __toESM(require("os"), 1);
44506
- import_node_path85 = __toESM(require("path"), 1);
44515
+ import_node_os32 = __toESM(require("os"), 1);
44516
+ import_node_path86 = __toESM(require("path"), 1);
44507
44517
  init_claude();
44508
44518
  init_stdout_splitter();
44509
44519
  init_permission_stdio();
@@ -44775,9 +44785,9 @@ Env (advanced):
44775
44785
  `;
44776
44786
 
44777
44787
  // src/index.ts
44778
- var import_node_path83 = __toESM(require("path"), 1);
44788
+ var import_node_path84 = __toESM(require("path"), 1);
44779
44789
  var import_node_fs65 = __toESM(require("fs"), 1);
44780
- var import_node_os32 = __toESM(require("os"), 1);
44790
+ var import_node_os31 = __toESM(require("os"), 1);
44781
44791
  var import_node_crypto26 = require("crypto");
44782
44792
  var import_node_url6 = require("url");
44783
44793
 
@@ -50064,45 +50074,169 @@ var ProjectsStore = class {
50064
50074
 
50065
50075
  // src/tools/codex-home.ts
50066
50076
  var import_node_os6 = __toESM(require("os"), 1);
50067
- var path14 = __toESM(require("path"), 1);
50068
- var norm = (p3) => path14.resolve(p3);
50077
+ var path15 = __toESM(require("path"), 1);
50078
+
50079
+ // src/tools/transcript-search.ts
50080
+ var import_node_fs10 = __toESM(require("fs"), 1);
50081
+ var import_node_path10 = __toESM(require("path"), 1);
50082
+ var SNIPPET_PAD = 60;
50083
+ var INJECTED_ENVELOPE_RE = /^<(system-reminder|command-name|command-message|command-args|local-command-stdout|local-command-stderr|task-notification|user-prompt-submit-hook|environment_context|user_instructions)\b/;
50084
+ var CODEX_WRAPPER_RE = /<(related-dir|environment_context|user_instructions)>[\s\S]*?<\/\1>/g;
50085
+ function asRecord(v3) {
50086
+ return typeof v3 === "object" && v3 !== null ? v3 : null;
50087
+ }
50088
+ function textOfBlocks(content, types) {
50089
+ if (!Array.isArray(content)) return null;
50090
+ const parts = [];
50091
+ for (const block of content) {
50092
+ const b3 = asRecord(block);
50093
+ if (!b3 || typeof b3.type !== "string" || !types.includes(b3.type)) continue;
50094
+ const text = b3.text;
50095
+ if (typeof text === "string" && text !== "") parts.push(text);
50096
+ }
50097
+ return parts.length > 0 ? parts.join("\n") : null;
50098
+ }
50099
+ function lineToSearchableText(raw) {
50100
+ const line = asRecord(raw);
50101
+ if (!line) return null;
50102
+ if (line.type === "response_item") {
50103
+ const payload = asRecord(line.payload);
50104
+ if (!payload || payload.type !== "message") return null;
50105
+ const role = payload.role;
50106
+ if (role !== "user" && role !== "assistant") return null;
50107
+ const text2 = textOfBlocks(payload.content, ["input_text", "output_text"]);
50108
+ if (text2 === null) return null;
50109
+ if (role !== "user") return text2;
50110
+ const stripped = text2.replace(CODEX_WRAPPER_RE, "").trim();
50111
+ if (stripped === "") return null;
50112
+ return INJECTED_ENVELOPE_RE.test(stripped) ? null : stripped;
50113
+ }
50114
+ if (line.type !== "user" && line.type !== "assistant") return null;
50115
+ const message = asRecord(line.message);
50116
+ if (!message) return null;
50117
+ const content = message.content;
50118
+ const text = typeof content === "string" && content !== "" ? content : textOfBlocks(content, ["text"]);
50119
+ if (text === null) return null;
50120
+ if (line.type === "user" && (line.isMeta === true || line.isSynthetic === true)) return null;
50121
+ if (line.type === "user" && INJECTED_ENVELOPE_RE.test(text.trimStart())) return null;
50122
+ return text;
50123
+ }
50124
+ function escapeRegExp(s2) {
50125
+ return s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50126
+ }
50127
+ function makeSnippet(text, at3, needleLength) {
50128
+ const from = Math.max(0, at3 - SNIPPET_PAD);
50129
+ const to = Math.min(text.length, at3 + needleLength + SNIPPET_PAD);
50130
+ const body = text.slice(from, to).replace(/\s+/g, " ").trim();
50131
+ return `${from > 0 ? "\u2026 " : ""}${body}${to < text.length ? " \u2026" : ""}`;
50132
+ }
50133
+ function searchTranscriptFile(file, query) {
50134
+ let buf;
50135
+ try {
50136
+ buf = import_node_fs10.default.readFileSync(file);
50137
+ } catch {
50138
+ return null;
50139
+ }
50140
+ const haystack = buf.toString("latin1");
50141
+ const needle = Buffer.from(query, "utf8").toString("latin1");
50142
+ const re2 = new RegExp(escapeRegExp(needle), "gi");
50143
+ let m3;
50144
+ while ((m3 = re2.exec(haystack)) !== null) {
50145
+ const lineStart = haystack.lastIndexOf("\n", m3.index) + 1;
50146
+ let lineEnd = haystack.indexOf("\n", m3.index);
50147
+ if (lineEnd < 0) lineEnd = haystack.length;
50148
+ re2.lastIndex = lineEnd + 1;
50149
+ let parsed;
50150
+ try {
50151
+ parsed = JSON.parse(Buffer.from(haystack.slice(lineStart, lineEnd), "latin1").toString("utf8"));
50152
+ } catch {
50153
+ continue;
50154
+ }
50155
+ const text = lineToSearchableText(parsed);
50156
+ if (text === null) continue;
50157
+ const at3 = text.toLowerCase().indexOf(query.toLowerCase());
50158
+ if (at3 < 0) continue;
50159
+ return makeSnippet(text, at3, query.length);
50160
+ }
50161
+ return null;
50162
+ }
50163
+ var ROLLOUT_NAME_RE = /-([0-9a-fA-F-]{36})\.jsonl$/;
50164
+ function createCodexRolloutLocator(roots) {
50165
+ let index = null;
50166
+ const walk2 = (dir, into) => {
50167
+ let entries;
50168
+ try {
50169
+ entries = import_node_fs10.default.readdirSync(dir, { withFileTypes: true });
50170
+ } catch {
50171
+ return;
50172
+ }
50173
+ for (const entry of entries) {
50174
+ const full = import_node_path10.default.join(dir, entry.name);
50175
+ if (entry.isDirectory()) {
50176
+ walk2(full, into);
50177
+ continue;
50178
+ }
50179
+ const matched = ROLLOUT_NAME_RE.exec(entry.name);
50180
+ if (matched) into.set(matched[1].toLowerCase(), full);
50181
+ }
50182
+ };
50183
+ return (toolSessionId) => {
50184
+ if (index === null) {
50185
+ index = /* @__PURE__ */ new Map();
50186
+ for (const root of roots) walk2(root, index);
50187
+ }
50188
+ return index.get(toolSessionId.toLowerCase()) ?? null;
50189
+ };
50190
+ }
50191
+
50192
+ // src/tools/codex-home.ts
50193
+ var norm = (p3) => path15.resolve(p3);
50069
50194
  function resolveCodexHome(input) {
50070
50195
  const cwd = norm(input.cwd);
50071
- const rel = path14.relative(norm(input.personaRoot), cwd);
50072
- const isPersonaRoot = rel !== "" && !rel.startsWith("..") && !path14.isAbsolute(rel) && !rel.includes(path14.sep);
50196
+ const rel = path15.relative(norm(input.personaRoot), cwd);
50197
+ const isPersonaRoot = rel !== "" && !rel.startsWith("..") && !path15.isAbsolute(rel) && !rel.includes(path15.sep);
50073
50198
  if (isPersonaRoot) return input.sharedHome;
50074
50199
  if (input.projectPaths.some((p3) => norm(p3) === cwd)) return input.sharedHome;
50075
50200
  return input.isolatedHome;
50076
50201
  }
50077
- function codexRolloutRoots(dataDir, sharedHome = path14.join(import_node_os6.default.homedir(), ".codex")) {
50078
- const roots = [path14.join(sharedHome, "sessions")];
50079
- if (dataDir) roots.push(path14.join(dataDir, "codex-home", "sessions"));
50202
+ function codexRolloutRoots(dataDir, sharedHome = path15.join(import_node_os6.default.homedir(), ".codex")) {
50203
+ const roots = [path15.join(sharedHome, "sessions")];
50204
+ if (dataDir) roots.push(path15.join(dataDir, "codex-home", "sessions"));
50080
50205
  return roots;
50081
50206
  }
50207
+ function locateCodexThread(roots, threadId) {
50208
+ const file = createCodexRolloutLocator(roots)(threadId);
50209
+ if (!file) return null;
50210
+ const root = roots.find((candidate) => {
50211
+ const relative5 = path15.relative(candidate, file);
50212
+ return relative5 !== "" && !relative5.startsWith("..") && !path15.isAbsolute(relative5);
50213
+ });
50214
+ return root ? { file, home: path15.dirname(root) } : null;
50215
+ }
50082
50216
 
50083
50217
  // src/persona/store.ts
50084
- var fs12 = __toESM(require("fs"), 1);
50085
- var path15 = __toESM(require("path"), 1);
50218
+ var fs13 = __toESM(require("fs"), 1);
50219
+ var path16 = __toESM(require("path"), 1);
50086
50220
  init_protocol();
50087
50221
  var PersonaStore = class {
50088
50222
  constructor(root) {
50089
50223
  this.root = root;
50090
- fs12.mkdirSync(root, { recursive: true });
50224
+ fs13.mkdirSync(root, { recursive: true });
50091
50225
  }
50092
50226
  root;
50093
50227
  personaDir(personaId2) {
50094
- return path15.join(this.root, safeFileName(personaId2));
50228
+ return path16.join(this.root, safeFileName(personaId2));
50095
50229
  }
50096
50230
  metaPath(personaId2) {
50097
- return path15.join(this.personaDir(personaId2), ".clawd", "persona.json");
50231
+ return path16.join(this.personaDir(personaId2), ".clawd", "persona.json");
50098
50232
  }
50099
50233
  claudeMdPath(personaId2) {
50100
- return path15.join(this.personaDir(personaId2), "CLAUDE.md");
50234
+ return path16.join(this.personaDir(personaId2), "CLAUDE.md");
50101
50235
  }
50102
50236
  // codex 原生读 cwd 的 AGENTS.md。人格双写镜像:claude 读 CLAUDE.md、codex 读 AGENTS.md,
50103
50237
  // 两份内容恒一致,persona 切 tool 零迁移。
50104
50238
  agentsMdPath(personaId2) {
50105
- return path15.join(this.personaDir(personaId2), "AGENTS.md");
50239
+ return path16.join(this.personaDir(personaId2), "AGENTS.md");
50106
50240
  }
50107
50241
  /**
50108
50242
  * persona 级 sandbox base 落盘路径 —— 故意放 `.clawd/` 而非 `.claude/`,让 CC 的 project
@@ -50111,7 +50245,7 @@ var PersonaStore = class {
50111
50245
  * spawn 前 per-guest 动态拼到各自 session 目录的那份(base + 强制底座 + 本 guest userWorkDir carve)。
50112
50246
  */
50113
50247
  sandboxSettingsPath(personaId2) {
50114
- return path15.join(this.personaDir(personaId2), ".clawd", "sandbox-settings.json");
50248
+ return path16.join(this.personaDir(personaId2), ".clawd", "sandbox-settings.json");
50115
50249
  }
50116
50250
  /**
50117
50251
  * 人格补充层落盘路径 —— `<personaDir>/.clawd/personality.local.md`。
@@ -50126,11 +50260,11 @@ var PersonaStore = class {
50126
50260
  * (见 local-personality-prompt.ts),通道唯一,不会被当第二份 CLAUDE.md 重复加载。
50127
50261
  */
50128
50262
  personalityLocalPath(personaId2) {
50129
- return path15.join(this.personaDir(personaId2), ".clawd", "personality.local.md");
50263
+ return path16.join(this.personaDir(personaId2), ".clawd", "personality.local.md");
50130
50264
  }
50131
50265
  write(persona, personality) {
50132
50266
  const dir = this.personaDir(persona.personaId);
50133
- fs12.mkdirSync(path15.join(dir, ".clawd"), { recursive: true });
50267
+ fs13.mkdirSync(path16.join(dir, ".clawd"), { recursive: true });
50134
50268
  this.atomicWrite(this.claudeMdPath(persona.personaId), personality);
50135
50269
  this.atomicWrite(this.agentsMdPath(persona.personaId), personality);
50136
50270
  this.writeSandboxSettings(persona.personaId, buildGuestSettingsV1());
@@ -50143,8 +50277,8 @@ var PersonaStore = class {
50143
50277
  /** 读人格补充层正文;文件不存在返回 null(= 该 persona 没有补充,走内置人格)。 */
50144
50278
  readPersonalityLocal(personaId2) {
50145
50279
  const p3 = this.personalityLocalPath(personaId2);
50146
- if (!fs12.existsSync(p3)) return null;
50147
- return fs12.readFileSync(p3, "utf8");
50280
+ if (!fs13.existsSync(p3)) return null;
50281
+ return fs13.readFileSync(p3, "utf8");
50148
50282
  }
50149
50283
  /**
50150
50284
  * 写人格补充层。**纯空白 = 清空**:删文件而不是留个空文件——「有没有补充」的唯一判据
@@ -50153,10 +50287,10 @@ var PersonaStore = class {
50153
50287
  writePersonalityLocal(personaId2, body) {
50154
50288
  const p3 = this.personalityLocalPath(personaId2);
50155
50289
  if (!body.trim()) {
50156
- fs12.rmSync(p3, { force: true });
50290
+ fs13.rmSync(p3, { force: true });
50157
50291
  return;
50158
50292
  }
50159
- fs12.mkdirSync(path15.dirname(p3), { recursive: true });
50293
+ fs13.mkdirSync(path16.dirname(p3), { recursive: true });
50160
50294
  this.atomicWrite(p3, body);
50161
50295
  }
50162
50296
  /**
@@ -50168,9 +50302,9 @@ var PersonaStore = class {
50168
50302
  ensureAgentsMirror(personaId2) {
50169
50303
  const claudeMd = this.claudeMdPath(personaId2);
50170
50304
  const agentsMd = this.agentsMdPath(personaId2);
50171
- if (!fs12.existsSync(claudeMd)) return false;
50172
- if (fs12.existsSync(agentsMd)) return false;
50173
- this.atomicWrite(agentsMd, fs12.readFileSync(claudeMd, "utf8"));
50305
+ if (!fs13.existsSync(claudeMd)) return false;
50306
+ if (fs13.existsSync(agentsMd)) return false;
50307
+ this.atomicWrite(agentsMd, fs13.readFileSync(claudeMd, "utf8"));
50174
50308
  return true;
50175
50309
  }
50176
50310
  /**
@@ -50220,22 +50354,22 @@ var PersonaStore = class {
50220
50354
  };
50221
50355
  }
50222
50356
  codexSandboxSettingsPath(personaId2) {
50223
- return path15.join(this.personaDir(personaId2), ".clawd", "codex-sandbox.json");
50357
+ return path16.join(this.personaDir(personaId2), ".clawd", "codex-sandbox.json");
50224
50358
  }
50225
50359
  /** 读 codex-sandbox.json;不存在/损坏 → null。 */
50226
50360
  readCodexSandboxSettings(personaId2) {
50227
50361
  const p3 = this.codexSandboxSettingsPath(personaId2);
50228
- if (!fs12.existsSync(p3)) return null;
50362
+ if (!fs13.existsSync(p3)) return null;
50229
50363
  try {
50230
- return CodexSandboxSettingsSchema.parse(JSON.parse(fs12.readFileSync(p3, "utf8")));
50364
+ return CodexSandboxSettingsSchema.parse(JSON.parse(fs13.readFileSync(p3, "utf8")));
50231
50365
  } catch {
50232
50366
  return null;
50233
50367
  }
50234
50368
  }
50235
50369
  /** 覆盖写 codex-sandbox.json(seed/migrate 用)。 */
50236
50370
  writeCodexSandboxSettings(personaId2, settings) {
50237
- const dir = path15.join(this.personaDir(personaId2), ".clawd");
50238
- fs12.mkdirSync(dir, { recursive: true });
50371
+ const dir = path16.join(this.personaDir(personaId2), ".clawd");
50372
+ fs13.mkdirSync(dir, { recursive: true });
50239
50373
  this.atomicWrite(this.codexSandboxSettingsPath(personaId2), JSON.stringify(settings, null, 2));
50240
50374
  }
50241
50375
  writeMeta(persona) {
@@ -50243,8 +50377,8 @@ var PersonaStore = class {
50243
50377
  }
50244
50378
  read(personaId2) {
50245
50379
  const p3 = this.metaPath(personaId2);
50246
- if (!fs12.existsSync(p3)) return null;
50247
- const raw = JSON.parse(fs12.readFileSync(p3, "utf8"));
50380
+ if (!fs13.existsSync(p3)) return null;
50381
+ const raw = JSON.parse(fs13.readFileSync(p3, "utf8"));
50248
50382
  if (raw && typeof raw === "object" && "tokenMap" in raw) {
50249
50383
  delete raw.tokenMap;
50250
50384
  this.atomicWrite(p3, JSON.stringify(raw, null, 2));
@@ -50252,13 +50386,13 @@ var PersonaStore = class {
50252
50386
  return PersonaFileSchema.parse(raw);
50253
50387
  }
50254
50388
  has(personaId2) {
50255
- return fs12.existsSync(this.metaPath(personaId2));
50389
+ return fs13.existsSync(this.metaPath(personaId2));
50256
50390
  }
50257
50391
  readPersonality(personaId2) {
50258
50392
  const claudeMd = this.claudeMdPath(personaId2);
50259
- if (fs12.existsSync(claudeMd)) return fs12.readFileSync(claudeMd, "utf8");
50393
+ if (fs13.existsSync(claudeMd)) return fs13.readFileSync(claudeMd, "utf8");
50260
50394
  const agentsMd = this.agentsMdPath(personaId2);
50261
- if (fs12.existsSync(agentsMd)) return fs12.readFileSync(agentsMd, "utf8");
50395
+ if (fs13.existsSync(agentsMd)) return fs13.readFileSync(agentsMd, "utf8");
50262
50396
  return null;
50263
50397
  }
50264
50398
  /**
@@ -50267,23 +50401,23 @@ var PersonaStore = class {
50267
50401
  */
50268
50402
  readSandboxSettings(personaId2) {
50269
50403
  const p3 = this.sandboxSettingsPath(personaId2);
50270
- if (!fs12.existsSync(p3)) return null;
50404
+ if (!fs13.existsSync(p3)) return null;
50271
50405
  try {
50272
- return JSON.parse(fs12.readFileSync(p3, "utf8"));
50406
+ return JSON.parse(fs13.readFileSync(p3, "utf8"));
50273
50407
  } catch {
50274
50408
  return null;
50275
50409
  }
50276
50410
  }
50277
50411
  /** Persona 私有 skills 目录路径:<personaDir>/.claude/skills */
50278
50412
  skillsDir(personaId2) {
50279
- return path15.join(this.personaDir(personaId2), ".claude", "skills");
50413
+ return path16.join(this.personaDir(personaId2), ".claude", "skills");
50280
50414
  }
50281
50415
  /**
50282
50416
  * Claude Code 项目级 settings 路径:`<personaDir>/.claude/settings.json`。
50283
50417
  * 这里只读 `enabledPlugins` 字段,由 owner 通过 CC `/plugin` 之类命令维护,daemon 不写。
50284
50418
  */
50285
50419
  claudeSettingsPath(personaId2) {
50286
- return path15.join(this.personaDir(personaId2), ".claude", "settings.json");
50420
+ return path16.join(this.personaDir(personaId2), ".claude", "settings.json");
50287
50421
  }
50288
50422
  /**
50289
50423
  * 读取 persona 的 `.claude/settings.json` 中 `enabledPlugins` map,把 value === true
@@ -50298,10 +50432,10 @@ var PersonaStore = class {
50298
50432
  */
50299
50433
  readEnabledPlugins(personaId2) {
50300
50434
  const p3 = this.claudeSettingsPath(personaId2);
50301
- if (!fs12.existsSync(p3)) return [];
50435
+ if (!fs13.existsSync(p3)) return [];
50302
50436
  let raw;
50303
50437
  try {
50304
- raw = JSON.parse(fs12.readFileSync(p3, "utf8"));
50438
+ raw = JSON.parse(fs13.readFileSync(p3, "utf8"));
50305
50439
  } catch {
50306
50440
  return [];
50307
50441
  }
@@ -50315,22 +50449,22 @@ var PersonaStore = class {
50315
50449
  return out;
50316
50450
  }
50317
50451
  list() {
50318
- if (!fs12.existsSync(this.root)) return [];
50319
- return fs12.readdirSync(this.root).filter((name) => {
50320
- return fs12.existsSync(path15.join(this.root, name, ".clawd", "persona.json"));
50452
+ if (!fs13.existsSync(this.root)) return [];
50453
+ return fs13.readdirSync(this.root).filter((name) => {
50454
+ return fs13.existsSync(path16.join(this.root, name, ".clawd", "persona.json"));
50321
50455
  });
50322
50456
  }
50323
50457
  remove(personaId2) {
50324
50458
  const dir = this.personaDir(personaId2);
50325
- if (fs12.existsSync(dir)) fs12.rmSync(dir, { recursive: true, force: true });
50459
+ if (fs13.existsSync(dir)) fs13.rmSync(dir, { recursive: true, force: true });
50326
50460
  }
50327
50461
  personaDirPath(personaId2) {
50328
50462
  return this.personaDir(personaId2);
50329
50463
  }
50330
50464
  atomicWrite(file, content) {
50331
50465
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
50332
- fs12.writeFileSync(tmp, content, { mode: 384 });
50333
- fs12.renameSync(tmp, file);
50466
+ fs13.writeFileSync(tmp, content, { mode: 384 });
50467
+ fs13.renameSync(tmp, file);
50334
50468
  }
50335
50469
  };
50336
50470
 
@@ -50384,8 +50518,8 @@ var PersonaRegistry = class {
50384
50518
  };
50385
50519
 
50386
50520
  // src/persona/seed.ts
50387
- var fs13 = __toESM(require("fs"), 1);
50388
- var path16 = __toESM(require("path"), 1);
50521
+ var fs14 = __toESM(require("fs"), 1);
50522
+ var path17 = __toESM(require("path"), 1);
50389
50523
  var import_node_url = require("url");
50390
50524
  init_protocol();
50391
50525
  var import_meta = {};
@@ -50584,24 +50718,24 @@ function bundleSiblingFromArgv(argv1, sibling) {
50584
50718
  if (!argv1) return null;
50585
50719
  let real = argv1;
50586
50720
  try {
50587
- real = fs13.realpathSync(argv1);
50721
+ real = fs14.realpathSync(argv1);
50588
50722
  } catch {
50589
50723
  }
50590
- return path16.resolve(path16.dirname(real), sibling);
50724
+ return path17.resolve(path17.dirname(real), sibling);
50591
50725
  }
50592
50726
  function findDefaultsRoot(logger) {
50593
50727
  const candidates = [];
50594
50728
  try {
50595
- const here = path16.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
50596
- candidates.push(path16.resolve(here, "defaults"));
50597
- candidates.push(path16.resolve(here, "persona-defaults"));
50729
+ const here = path17.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
50730
+ candidates.push(path17.resolve(here, "defaults"));
50731
+ candidates.push(path17.resolve(here, "persona-defaults"));
50598
50732
  } catch {
50599
50733
  }
50600
50734
  const fromArgv = bundleSiblingFromArgv(process.argv[1], "persona-defaults");
50601
50735
  if (fromArgv) candidates.push(fromArgv);
50602
50736
  for (const c2 of candidates) {
50603
50737
  try {
50604
- if (fs13.statSync(c2).isDirectory()) {
50738
+ if (fs14.statSync(c2).isDirectory()) {
50605
50739
  logger?.info("persona.defaults-root.resolved", { root: c2 });
50606
50740
  return c2;
50607
50741
  }
@@ -50618,8 +50752,8 @@ function seedDefaultPersonas(args) {
50618
50752
  args.logger.info("persona.seed.skip", { personaId: entry.personaId, reason: "exists" });
50619
50753
  continue;
50620
50754
  }
50621
- const bundleDir = path16.join(args.defaultsRoot, entry.personaId);
50622
- if (!fs13.existsSync(bundleDir)) {
50755
+ const bundleDir = path17.join(args.defaultsRoot, entry.personaId);
50756
+ if (!fs14.existsSync(bundleDir)) {
50623
50757
  args.logger.warn("persona.seed.skip", {
50624
50758
  personaId: entry.personaId,
50625
50759
  reason: "bundle-missing",
@@ -50627,8 +50761,8 @@ function seedDefaultPersonas(args) {
50627
50761
  });
50628
50762
  continue;
50629
50763
  }
50630
- const claudeMdPath = path16.join(bundleDir, "CLAUDE.md");
50631
- if (!fs13.existsSync(claudeMdPath)) {
50764
+ const claudeMdPath = path17.join(bundleDir, "CLAUDE.md");
50765
+ if (!fs14.existsSync(claudeMdPath)) {
50632
50766
  args.logger.warn("persona.seed.skip", {
50633
50767
  personaId: entry.personaId,
50634
50768
  reason: "no-CLAUDE.md",
@@ -50636,7 +50770,7 @@ function seedDefaultPersonas(args) {
50636
50770
  });
50637
50771
  continue;
50638
50772
  }
50639
- const personality = fs13.readFileSync(claudeMdPath, "utf8");
50773
+ const personality = fs14.readFileSync(claudeMdPath, "utf8");
50640
50774
  const now = Date.now();
50641
50775
  const persona = {
50642
50776
  personaId: entry.personaId,
@@ -50663,51 +50797,51 @@ function seedDefaultPersonas(args) {
50663
50797
  }
50664
50798
  }
50665
50799
  function skipNodeModulesUnder(srcRoot) {
50666
- return (src) => !path16.relative(srcRoot, src).split(path16.sep).includes("node_modules");
50800
+ return (src) => !path17.relative(srcRoot, src).split(path17.sep).includes("node_modules");
50667
50801
  }
50668
50802
  function copyBundleExtras(srcDir, dstDir) {
50669
- for (const entry of fs13.readdirSync(srcDir, { withFileTypes: true })) {
50803
+ for (const entry of fs14.readdirSync(srcDir, { withFileTypes: true })) {
50670
50804
  if (entry.name === "CLAUDE.md" || entry.name === ".clawd") continue;
50671
- const srcPath = path16.join(srcDir, entry.name);
50672
- const dstPath = path16.join(dstDir, entry.name);
50805
+ const srcPath = path17.join(srcDir, entry.name);
50806
+ const dstPath = path17.join(dstDir, entry.name);
50673
50807
  if (entry.isDirectory()) {
50674
- fs13.cpSync(srcPath, dstPath, { recursive: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
50808
+ fs14.cpSync(srcPath, dstPath, { recursive: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
50675
50809
  } else if (entry.isFile()) {
50676
- fs13.copyFileSync(srcPath, dstPath);
50810
+ fs14.copyFileSync(srcPath, dstPath);
50677
50811
  }
50678
50812
  }
50679
50813
  }
50680
50814
  var DAEMON_MANAGED_PATHS = ["extension-kit", "CLAUDE.md", ".mcp.json", ".claude/skills", ".claude/agents", "mcp"];
50681
50815
  function replaceManagedPath(src, dst) {
50682
- if (!fs13.statSync(src).isDirectory()) {
50683
- fs13.mkdirSync(path16.dirname(dst), { recursive: true });
50684
- fs13.rmSync(dst, { recursive: true, force: true });
50685
- fs13.cpSync(src, dst, { dereference: true });
50816
+ if (!fs14.statSync(src).isDirectory()) {
50817
+ fs14.mkdirSync(path17.dirname(dst), { recursive: true });
50818
+ fs14.rmSync(dst, { recursive: true, force: true });
50819
+ fs14.cpSync(src, dst, { dereference: true });
50686
50820
  return;
50687
50821
  }
50688
- fs13.mkdirSync(dst, { recursive: true });
50689
- for (const e of fs13.readdirSync(src, { withFileTypes: true })) {
50822
+ fs14.mkdirSync(dst, { recursive: true });
50823
+ for (const e of fs14.readdirSync(src, { withFileTypes: true })) {
50690
50824
  if (e.name === "node_modules") continue;
50691
- const child = path16.join(dst, e.name);
50692
- fs13.rmSync(child, { recursive: true, force: true });
50693
- fs13.cpSync(path16.join(src, e.name), child, { recursive: true, dereference: true });
50825
+ const child = path17.join(dst, e.name);
50826
+ fs14.rmSync(child, { recursive: true, force: true });
50827
+ fs14.cpSync(path17.join(src, e.name), child, { recursive: true, dereference: true });
50694
50828
  }
50695
50829
  }
50696
50830
  function refreshDaemonManagedDirs(args) {
50697
50831
  const entries = args.entries ?? DEFAULT_PERSONAS;
50698
50832
  for (const entry of entries) {
50699
- const bundleDir = path16.join(args.defaultsRoot, entry.personaId);
50700
- if (!fs13.existsSync(bundleDir)) continue;
50833
+ const bundleDir = path17.join(args.defaultsRoot, entry.personaId);
50834
+ if (!fs14.existsSync(bundleDir)) continue;
50701
50835
  const personaDir = args.store.personaDirPath(entry.personaId);
50702
- if (!fs13.existsSync(personaDir)) continue;
50836
+ if (!fs14.existsSync(personaDir)) continue;
50703
50837
  for (const relPath of DAEMON_MANAGED_PATHS) {
50704
- const srcPath = path16.join(bundleDir, relPath);
50705
- if (!fs13.existsSync(srcPath)) continue;
50706
- const dstPath = path16.join(personaDir, relPath);
50838
+ const srcPath = path17.join(bundleDir, relPath);
50839
+ if (!fs14.existsSync(srcPath)) continue;
50840
+ const dstPath = path17.join(personaDir, relPath);
50707
50841
  try {
50708
50842
  replaceManagedPath(srcPath, dstPath);
50709
50843
  if (relPath === "CLAUDE.md") {
50710
- replaceManagedPath(srcPath, path16.join(personaDir, "AGENTS.md"));
50844
+ replaceManagedPath(srcPath, path17.join(personaDir, "AGENTS.md"));
50711
50845
  }
50712
50846
  args.logger.info("persona.refresh.synced", {
50713
50847
  personaId: entry.personaId,
@@ -50731,12 +50865,12 @@ function migratePersonalityOverlay(args) {
50731
50865
  for (const entry of entries) {
50732
50866
  const personaId2 = entry.personaId;
50733
50867
  try {
50734
- if (fs13.existsSync(args.store.personalityLocalPath(personaId2))) continue;
50735
- const bundleClaudeMd = path16.join(args.defaultsRoot, personaId2, "CLAUDE.md");
50736
- if (!fs13.existsSync(bundleClaudeMd)) continue;
50868
+ if (fs14.existsSync(args.store.personalityLocalPath(personaId2))) continue;
50869
+ const bundleClaudeMd = path17.join(args.defaultsRoot, personaId2, "CLAUDE.md");
50870
+ if (!fs14.existsSync(bundleClaudeMd)) continue;
50737
50871
  const current = args.store.readPersonality(personaId2);
50738
50872
  if (current === null) continue;
50739
- const bundle = fs13.readFileSync(bundleClaudeMd, "utf8");
50873
+ const bundle = fs14.readFileSync(bundleClaudeMd, "utf8");
50740
50874
  if (current === bundle) continue;
50741
50875
  if (!current.startsWith(bundle)) continue;
50742
50876
  const tail2 = current.slice(bundle.length).trim();
@@ -50835,7 +50969,7 @@ function migrateSandboxAllowRead(args) {
50835
50969
  for (const personaId2 of args.store.list()) {
50836
50970
  try {
50837
50971
  const readPath = tildePath(args.store.personaDirPath(personaId2));
50838
- if (!fs13.existsSync(args.store.sandboxSettingsPath(personaId2))) {
50972
+ if (!fs14.existsSync(args.store.sandboxSettingsPath(personaId2))) {
50839
50973
  args.store.writeSandboxSettings(personaId2, buildGuestSettingsV1());
50840
50974
  n2++;
50841
50975
  continue;
@@ -50878,13 +51012,13 @@ var PERSONA_BUILDER_TOMBSTONE = `${PERSONA_BUILDER_TOMBSTONE_MARKER}
50878
51012
  function containsAnyFile(dir) {
50879
51013
  let entries;
50880
51014
  try {
50881
- entries = fs13.readdirSync(dir, { withFileTypes: true });
51015
+ entries = fs14.readdirSync(dir, { withFileTypes: true });
50882
51016
  } catch {
50883
51017
  return false;
50884
51018
  }
50885
51019
  for (const e of entries) {
50886
51020
  if (e.isDirectory()) {
50887
- if (containsAnyFile(path16.join(dir, e.name))) return true;
51021
+ if (containsAnyFile(path17.join(dir, e.name))) return true;
50888
51022
  } else {
50889
51023
  return true;
50890
51024
  }
@@ -50893,9 +51027,9 @@ function containsAnyFile(dir) {
50893
51027
  }
50894
51028
  function retirePersonaBuilder(args) {
50895
51029
  const dir = args.store.personaDirPath(PERSONA_BUILDER_ID);
50896
- if (!fs13.existsSync(dir)) return;
51030
+ if (!fs14.existsSync(dir)) return;
50897
51031
  try {
50898
- const used = containsAnyFile(path16.join(dir, ".clawd", "sessions")) || containsAnyFile(path16.join(dir, ".claude", "skills")) || containsAnyFile(path16.join(dir, "projects"));
51032
+ const used = containsAnyFile(path17.join(dir, ".clawd", "sessions")) || containsAnyFile(path17.join(dir, ".claude", "skills")) || containsAnyFile(path17.join(dir, "projects"));
50899
51033
  if (!used) {
50900
51034
  args.store.remove(PERSONA_BUILDER_ID);
50901
51035
  args.logger.info("persona.retire-builder.removed", { personaId: PERSONA_BUILDER_ID });
@@ -50903,10 +51037,10 @@ function retirePersonaBuilder(args) {
50903
51037
  }
50904
51038
  let wrote = false;
50905
51039
  for (const name of ["CLAUDE.md", "AGENTS.md"]) {
50906
- const p3 = path16.join(dir, name);
50907
- const cur = fs13.existsSync(p3) ? fs13.readFileSync(p3, "utf8") : null;
51040
+ const p3 = path17.join(dir, name);
51041
+ const cur = fs14.existsSync(p3) ? fs14.readFileSync(p3, "utf8") : null;
50908
51042
  if (cur?.startsWith(PERSONA_BUILDER_TOMBSTONE_MARKER)) continue;
50909
- fs13.writeFileSync(p3, PERSONA_BUILDER_TOMBSTONE);
51043
+ fs14.writeFileSync(p3, PERSONA_BUILDER_TOMBSTONE);
50910
51044
  wrote = true;
50911
51045
  }
50912
51046
  if (wrote) args.logger.info("persona.retire-builder.tombstoned", { personaId: PERSONA_BUILDER_ID });
@@ -50919,9 +51053,9 @@ function retirePersonaBuilder(args) {
50919
51053
  }
50920
51054
 
50921
51055
  // src/skills/scanner.ts
50922
- var import_node_fs10 = __toESM(require("fs"), 1);
51056
+ var import_node_fs11 = __toESM(require("fs"), 1);
50923
51057
  var import_node_os7 = __toESM(require("os"), 1);
50924
- var import_node_path10 = __toESM(require("path"), 1);
51058
+ var import_node_path11 = __toESM(require("path"), 1);
50925
51059
 
50926
51060
  // src/skills/frontmatter.ts
50927
51061
  var STRIP_QUOTES = /^["']|["']$/g;
@@ -51030,7 +51164,7 @@ function parseDescription(content) {
51030
51164
  }
51031
51165
  function isDirLikeSync(p3) {
51032
51166
  try {
51033
- return import_node_fs10.default.statSync(p3).isDirectory();
51167
+ return import_node_fs11.default.statSync(p3).isDirectory();
51034
51168
  } catch {
51035
51169
  return false;
51036
51170
  }
@@ -51038,19 +51172,19 @@ function isDirLikeSync(p3) {
51038
51172
  function scanSkillDir(dir, source, seen, out, pluginName) {
51039
51173
  let entries;
51040
51174
  try {
51041
- entries = import_node_fs10.default.readdirSync(dir, { withFileTypes: true });
51175
+ entries = import_node_fs11.default.readdirSync(dir, { withFileTypes: true });
51042
51176
  } catch {
51043
51177
  return;
51044
51178
  }
51045
51179
  for (const ent of entries) {
51046
- const entryPath = import_node_path10.default.join(dir, ent.name);
51180
+ const entryPath = import_node_path11.default.join(dir, ent.name);
51047
51181
  if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
51048
51182
  let content;
51049
51183
  try {
51050
- content = import_node_fs10.default.readFileSync(import_node_path10.default.join(entryPath, "SKILL.md"), "utf8");
51184
+ content = import_node_fs11.default.readFileSync(import_node_path11.default.join(entryPath, "SKILL.md"), "utf8");
51051
51185
  } catch {
51052
51186
  try {
51053
- content = import_node_fs10.default.readFileSync(import_node_path10.default.join(entryPath, "skill.md"), "utf8");
51187
+ content = import_node_fs11.default.readFileSync(import_node_path11.default.join(entryPath, "skill.md"), "utf8");
51054
51188
  } catch {
51055
51189
  continue;
51056
51190
  }
@@ -51074,26 +51208,26 @@ function listSkillsForDir(dir, source) {
51074
51208
  function scanCommandDir(dir, source, seen, out, pluginName) {
51075
51209
  let entries;
51076
51210
  try {
51077
- entries = import_node_fs10.default.readdirSync(dir, { withFileTypes: true });
51211
+ entries = import_node_fs11.default.readdirSync(dir, { withFileTypes: true });
51078
51212
  } catch {
51079
51213
  return;
51080
51214
  }
51081
51215
  for (const ent of entries) {
51082
- const entryPath = import_node_path10.default.join(dir, ent.name);
51216
+ const entryPath = import_node_path11.default.join(dir, ent.name);
51083
51217
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
51084
51218
  const ns = ent.name;
51085
51219
  let subEntries;
51086
51220
  try {
51087
- subEntries = import_node_fs10.default.readdirSync(entryPath, { withFileTypes: true });
51221
+ subEntries = import_node_fs11.default.readdirSync(entryPath, { withFileTypes: true });
51088
51222
  } catch {
51089
51223
  continue;
51090
51224
  }
51091
51225
  for (const se2 of subEntries) {
51092
51226
  if (!se2.name.endsWith(".md")) continue;
51093
- const sePath = import_node_path10.default.join(entryPath, se2.name);
51227
+ const sePath = import_node_path11.default.join(entryPath, se2.name);
51094
51228
  let content;
51095
51229
  try {
51096
- content = import_node_fs10.default.readFileSync(sePath, "utf8");
51230
+ content = import_node_fs11.default.readFileSync(sePath, "utf8");
51097
51231
  } catch {
51098
51232
  continue;
51099
51233
  }
@@ -51110,7 +51244,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
51110
51244
  } else if (ent.name.endsWith(".md")) {
51111
51245
  let content;
51112
51246
  try {
51113
- content = import_node_fs10.default.readFileSync(entryPath, "utf8");
51247
+ content = import_node_fs11.default.readFileSync(entryPath, "utf8");
51114
51248
  } catch {
51115
51249
  continue;
51116
51250
  }
@@ -51126,10 +51260,10 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
51126
51260
  }
51127
51261
  }
51128
51262
  function readInstalledPlugins(home) {
51129
- const file = import_node_path10.default.join(home, ".claude", "plugins", "installed_plugins.json");
51263
+ const file = import_node_path11.default.join(home, ".claude", "plugins", "installed_plugins.json");
51130
51264
  let raw;
51131
51265
  try {
51132
- raw = import_node_fs10.default.readFileSync(file, "utf8");
51266
+ raw = import_node_fs11.default.readFileSync(file, "utf8");
51133
51267
  } catch {
51134
51268
  return [];
51135
51269
  }
@@ -51177,14 +51311,14 @@ var SkillsScanner = class {
51177
51311
  });
51178
51312
  }
51179
51313
  const fsBlock = [];
51180
- scanSkillDir(import_node_path10.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
51181
- scanCommandDir(import_node_path10.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
51182
- scanSkillDir(import_node_path10.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
51183
- scanCommandDir(import_node_path10.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
51314
+ scanSkillDir(import_node_path11.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
51315
+ scanCommandDir(import_node_path11.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
51316
+ scanSkillDir(import_node_path11.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
51317
+ scanCommandDir(import_node_path11.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
51184
51318
  const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
51185
51319
  for (const { name, root } of plugins) {
51186
- scanSkillDir(import_node_path10.default.join(root, "skills"), "plugin", seen, fsBlock, name);
51187
- scanCommandDir(import_node_path10.default.join(root, "commands"), "plugin", seen, fsBlock, name);
51320
+ scanSkillDir(import_node_path11.default.join(root, "skills"), "plugin", seen, fsBlock, name);
51321
+ scanCommandDir(import_node_path11.default.join(root, "commands"), "plugin", seen, fsBlock, name);
51188
51322
  }
51189
51323
  fsBlock.sort((a2, b3) => a2.name < b3.name ? -1 : a2.name > b3.name ? 1 : 0);
51190
51324
  return [...builtinBlock, ...fsBlock];
@@ -51326,12 +51460,12 @@ var PersonaManager = class {
51326
51460
  };
51327
51461
 
51328
51462
  // src/share-md-viewer/load.ts
51329
- var import_node_fs12 = __toESM(require("fs"), 1);
51330
- var import_node_path11 = __toESM(require("path"), 1);
51463
+ var import_node_fs13 = __toESM(require("fs"), 1);
51464
+ var import_node_path12 = __toESM(require("path"), 1);
51331
51465
  var import_node_url2 = require("url");
51332
51466
 
51333
51467
  // src/share-md-viewer/asset-loader.ts
51334
- var import_node_fs11 = __toESM(require("fs"), 1);
51468
+ var import_node_fs12 = __toESM(require("fs"), 1);
51335
51469
  function htmlEscape(s2) {
51336
51470
  return s2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
51337
51471
  }
@@ -51339,12 +51473,12 @@ function createViewerAssetLoader(opts) {
51339
51473
  let viewerTemplate;
51340
51474
  let errorTemplate;
51341
51475
  try {
51342
- viewerTemplate = import_node_fs11.default.readFileSync(opts.viewerHtmlPath, "utf8");
51476
+ viewerTemplate = import_node_fs12.default.readFileSync(opts.viewerHtmlPath, "utf8");
51343
51477
  } catch (err) {
51344
51478
  throw new Error(`viewer asset not found at ${opts.viewerHtmlPath}: ${err.message}`);
51345
51479
  }
51346
51480
  try {
51347
- errorTemplate = import_node_fs11.default.readFileSync(opts.errorHtmlPath, "utf8");
51481
+ errorTemplate = import_node_fs12.default.readFileSync(opts.errorHtmlPath, "utf8");
51348
51482
  } catch (err) {
51349
51483
  throw new Error(`viewer error asset not found at ${opts.errorHtmlPath}: ${err.message}`);
51350
51484
  }
@@ -51363,25 +51497,25 @@ var import_meta2 = {};
51363
51497
  function tryLoadViewerAssets(logger) {
51364
51498
  const candidates = [];
51365
51499
  try {
51366
- const here = import_node_path11.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
51500
+ const here = import_node_path12.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
51367
51501
  candidates.push(here);
51368
- candidates.push(import_node_path11.default.resolve(here, ".."));
51369
- candidates.push(import_node_path11.default.resolve(here, "..", "..", "dist"));
51502
+ candidates.push(import_node_path12.default.resolve(here, ".."));
51503
+ candidates.push(import_node_path12.default.resolve(here, "..", "..", "dist"));
51370
51504
  } catch {
51371
51505
  }
51372
51506
  if (process.argv[1]) {
51373
51507
  let real = process.argv[1];
51374
51508
  try {
51375
- real = import_node_fs12.default.realpathSync(process.argv[1]);
51509
+ real = import_node_fs13.default.realpathSync(process.argv[1]);
51376
51510
  } catch {
51377
51511
  }
51378
- candidates.push(import_node_path11.default.dirname(real));
51512
+ candidates.push(import_node_path12.default.dirname(real));
51379
51513
  }
51380
51514
  for (const root of candidates) {
51381
- const viewerHtmlPath = import_node_path11.default.join(root, "share-md-viewer.html");
51382
- const errorHtmlPath = import_node_path11.default.join(root, "share-md-viewer-error.html");
51515
+ const viewerHtmlPath = import_node_path12.default.join(root, "share-md-viewer.html");
51516
+ const errorHtmlPath = import_node_path12.default.join(root, "share-md-viewer-error.html");
51383
51517
  try {
51384
- if (import_node_fs12.default.statSync(viewerHtmlPath).isFile() && import_node_fs12.default.statSync(errorHtmlPath).isFile()) {
51518
+ if (import_node_fs13.default.statSync(viewerHtmlPath).isFile() && import_node_fs13.default.statSync(errorHtmlPath).isFile()) {
51385
51519
  logger?.info("share-md-viewer.assets-root.resolved", { root });
51386
51520
  return createViewerAssetLoader({ viewerHtmlPath, errorHtmlPath });
51387
51521
  }
@@ -51396,25 +51530,25 @@ function tryLoadViewerAssets(logger) {
51396
51530
  }
51397
51531
 
51398
51532
  // src/dashboard/load.ts
51399
- var import_node_fs13 = __toESM(require("fs"), 1);
51400
- var import_node_path12 = __toESM(require("path"), 1);
51533
+ var import_node_fs14 = __toESM(require("fs"), 1);
51534
+ var import_node_path13 = __toESM(require("path"), 1);
51401
51535
  var import_node_url3 = require("url");
51402
51536
  init_protocol();
51403
51537
  var import_meta3 = {};
51404
51538
  function candidateRoots() {
51405
51539
  const roots = [];
51406
51540
  try {
51407
- const here = import_node_path12.default.dirname((0, import_node_url3.fileURLToPath)(import_meta3.url));
51408
- roots.push(here, import_node_path12.default.resolve(here, ".."), import_node_path12.default.resolve(here, "..", "..", "dist"));
51541
+ const here = import_node_path13.default.dirname((0, import_node_url3.fileURLToPath)(import_meta3.url));
51542
+ roots.push(here, import_node_path13.default.resolve(here, ".."), import_node_path13.default.resolve(here, "..", "..", "dist"));
51409
51543
  } catch {
51410
51544
  }
51411
51545
  if (process.argv[1]) {
51412
51546
  let real = process.argv[1];
51413
51547
  try {
51414
- real = import_node_fs13.default.realpathSync(process.argv[1]);
51548
+ real = import_node_fs14.default.realpathSync(process.argv[1]);
51415
51549
  } catch {
51416
51550
  }
51417
- roots.push(import_node_path12.default.dirname(real));
51551
+ roots.push(import_node_path13.default.dirname(real));
51418
51552
  }
51419
51553
  return roots;
51420
51554
  }
@@ -51422,10 +51556,10 @@ function loadDashboardPages(logger, roots = candidateRoots()) {
51422
51556
  const pages = /* @__PURE__ */ new Map();
51423
51557
  for (const page of DASHBOARD_PAGES) {
51424
51558
  for (const root of roots) {
51425
- const file = import_node_path12.default.join(root, `${page}.html`);
51559
+ const file = import_node_path13.default.join(root, `${page}.html`);
51426
51560
  try {
51427
- if (import_node_fs13.default.statSync(file).isFile()) {
51428
- pages.set(page, import_node_fs13.default.readFileSync(file, "utf8"));
51561
+ if (import_node_fs14.default.statSync(file).isFile()) {
51562
+ pages.set(page, import_node_fs14.default.readFileSync(file, "utf8"));
51429
51563
  logger?.info("dashboard.page-asset.resolved", { page, root });
51430
51564
  break;
51431
51565
  }
@@ -51775,7 +51909,8 @@ function parseCodexRollout(text) {
51775
51909
  kind: "turn-end",
51776
51910
  raw: obj,
51777
51911
  ...ts ? { ts } : {},
51778
- ...durationMs !== void 0 ? { durationMs } : {}
51912
+ ...durationMs !== void 0 ? { durationMs } : {},
51913
+ ...p3.type === "task_complete" && turnId ? { turnId } : {}
51779
51914
  });
51780
51915
  break;
51781
51916
  }
@@ -51843,121 +51978,6 @@ function subagentTerminalResult(started, child) {
51843
51978
  // src/tools/codex-history.ts
51844
51979
  var import_node_fs15 = __toESM(require("fs"), 1);
51845
51980
  var import_node_path14 = __toESM(require("path"), 1);
51846
-
51847
- // src/tools/transcript-search.ts
51848
- var import_node_fs14 = __toESM(require("fs"), 1);
51849
- var import_node_path13 = __toESM(require("path"), 1);
51850
- var SNIPPET_PAD = 60;
51851
- var INJECTED_ENVELOPE_RE = /^<(system-reminder|command-name|command-message|command-args|local-command-stdout|local-command-stderr|task-notification|user-prompt-submit-hook|environment_context|user_instructions)\b/;
51852
- var CODEX_WRAPPER_RE = /<(related-dir|environment_context|user_instructions)>[\s\S]*?<\/\1>/g;
51853
- function asRecord(v3) {
51854
- return typeof v3 === "object" && v3 !== null ? v3 : null;
51855
- }
51856
- function textOfBlocks(content, types) {
51857
- if (!Array.isArray(content)) return null;
51858
- const parts = [];
51859
- for (const block of content) {
51860
- const b3 = asRecord(block);
51861
- if (!b3 || typeof b3.type !== "string" || !types.includes(b3.type)) continue;
51862
- const text = b3.text;
51863
- if (typeof text === "string" && text !== "") parts.push(text);
51864
- }
51865
- return parts.length > 0 ? parts.join("\n") : null;
51866
- }
51867
- function lineToSearchableText(raw) {
51868
- const line = asRecord(raw);
51869
- if (!line) return null;
51870
- if (line.type === "response_item") {
51871
- const payload = asRecord(line.payload);
51872
- if (!payload || payload.type !== "message") return null;
51873
- const role = payload.role;
51874
- if (role !== "user" && role !== "assistant") return null;
51875
- const text2 = textOfBlocks(payload.content, ["input_text", "output_text"]);
51876
- if (text2 === null) return null;
51877
- if (role !== "user") return text2;
51878
- const stripped = text2.replace(CODEX_WRAPPER_RE, "").trim();
51879
- if (stripped === "") return null;
51880
- return INJECTED_ENVELOPE_RE.test(stripped) ? null : stripped;
51881
- }
51882
- if (line.type !== "user" && line.type !== "assistant") return null;
51883
- const message = asRecord(line.message);
51884
- if (!message) return null;
51885
- const content = message.content;
51886
- const text = typeof content === "string" && content !== "" ? content : textOfBlocks(content, ["text"]);
51887
- if (text === null) return null;
51888
- if (line.type === "user" && (line.isMeta === true || line.isSynthetic === true)) return null;
51889
- if (line.type === "user" && INJECTED_ENVELOPE_RE.test(text.trimStart())) return null;
51890
- return text;
51891
- }
51892
- function escapeRegExp(s2) {
51893
- return s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
51894
- }
51895
- function makeSnippet(text, at3, needleLength) {
51896
- const from = Math.max(0, at3 - SNIPPET_PAD);
51897
- const to = Math.min(text.length, at3 + needleLength + SNIPPET_PAD);
51898
- const body = text.slice(from, to).replace(/\s+/g, " ").trim();
51899
- return `${from > 0 ? "\u2026 " : ""}${body}${to < text.length ? " \u2026" : ""}`;
51900
- }
51901
- function searchTranscriptFile(file, query) {
51902
- let buf;
51903
- try {
51904
- buf = import_node_fs14.default.readFileSync(file);
51905
- } catch {
51906
- return null;
51907
- }
51908
- const haystack = buf.toString("latin1");
51909
- const needle = Buffer.from(query, "utf8").toString("latin1");
51910
- const re2 = new RegExp(escapeRegExp(needle), "gi");
51911
- let m3;
51912
- while ((m3 = re2.exec(haystack)) !== null) {
51913
- const lineStart = haystack.lastIndexOf("\n", m3.index) + 1;
51914
- let lineEnd = haystack.indexOf("\n", m3.index);
51915
- if (lineEnd < 0) lineEnd = haystack.length;
51916
- re2.lastIndex = lineEnd + 1;
51917
- let parsed;
51918
- try {
51919
- parsed = JSON.parse(Buffer.from(haystack.slice(lineStart, lineEnd), "latin1").toString("utf8"));
51920
- } catch {
51921
- continue;
51922
- }
51923
- const text = lineToSearchableText(parsed);
51924
- if (text === null) continue;
51925
- const at3 = text.toLowerCase().indexOf(query.toLowerCase());
51926
- if (at3 < 0) continue;
51927
- return makeSnippet(text, at3, query.length);
51928
- }
51929
- return null;
51930
- }
51931
- var ROLLOUT_NAME_RE = /-([0-9a-fA-F-]{36})\.jsonl$/;
51932
- function createCodexRolloutLocator(roots) {
51933
- let index = null;
51934
- const walk2 = (dir, into) => {
51935
- let entries;
51936
- try {
51937
- entries = import_node_fs14.default.readdirSync(dir, { withFileTypes: true });
51938
- } catch {
51939
- return;
51940
- }
51941
- for (const entry of entries) {
51942
- const full = import_node_path13.default.join(dir, entry.name);
51943
- if (entry.isDirectory()) {
51944
- walk2(full, into);
51945
- continue;
51946
- }
51947
- const matched = ROLLOUT_NAME_RE.exec(entry.name);
51948
- if (matched) into.set(matched[1].toLowerCase(), full);
51949
- }
51950
- };
51951
- return (toolSessionId) => {
51952
- if (index === null) {
51953
- index = /* @__PURE__ */ new Map();
51954
- for (const root of roots) walk2(root, index);
51955
- }
51956
- return index.get(toolSessionId.toLowerCase()) ?? null;
51957
- };
51958
- }
51959
-
51960
- // src/tools/codex-history.ts
51961
51981
  var CODEX_LIST_SESSIONS_LIMIT = 100;
51962
51982
  var CODEX_LIST_SESSIONS_SCAN_CAP = 4e3;
51963
51983
  var PEEK_BYTES = 8 * 1024;
@@ -52523,9 +52543,9 @@ var CentralClientError = class extends Error {
52523
52543
  cause;
52524
52544
  status;
52525
52545
  };
52526
- async function centralRequest(opts, path105, init) {
52546
+ async function centralRequest(opts, path106, init) {
52527
52547
  const f2 = opts.fetchImpl ?? globalThis.fetch;
52528
- const url = `${opts.api.replace(/\/+$/, "")}${path105}`;
52548
+ const url = `${opts.api.replace(/\/+$/, "")}${path106}`;
52529
52549
  const ctrl = new AbortController();
52530
52550
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
52531
52551
  let res;
@@ -53234,7 +53254,8 @@ function tokenUsage(usage) {
53234
53254
  }
53235
53255
  function turnCompleted(turn) {
53236
53256
  if (turn?.status === "failed") return [{ kind: "error", message: str3(rec3(turn.error)?.message) ?? "codex turn failed" }, { kind: "turn_end" }];
53237
- return [{ kind: "turn_end" }];
53257
+ const turnId = turn?.status === "completed" ? str3(turn.id) : void 0;
53258
+ return [{ kind: "turn_end", ...turnId ? { turnId } : {} }];
53238
53259
  }
53239
53260
  function rec3(v3) {
53240
53261
  return typeof v3 === "object" && v3 !== null ? v3 : void 0;
@@ -53559,8 +53580,8 @@ function turnStartInput(text, resolveImage) {
53559
53580
  const items = [];
53560
53581
  let leftover = text;
53561
53582
  for (const m3 of text.matchAll(SKILL_RE)) {
53562
- const [marker, name, path105] = m3;
53563
- items.push({ type: "skill", name, path: path105 });
53583
+ const [marker, name, path106] = m3;
53584
+ items.push({ type: "skill", name, path: path106 });
53564
53585
  leftover = leftover.replace(marker, "");
53565
53586
  }
53566
53587
  for (const m3 of text.matchAll(ATTACHMENT_RE2)) {
@@ -54007,7 +54028,7 @@ function createCodexSession(ctx, sink, deps = {}) {
54007
54028
  );
54008
54029
  args.push(...codexAgentsCliArgs(loadCodexAgentsConfig({ cwd: ctx.cwd, logger: deps.logger })));
54009
54030
  args.push(...codexMemoriesCliArgs(ctx));
54010
- const resolvedCodexHome = deps.codexHomeFor?.(ctx.cwd);
54031
+ const resolvedCodexHome = (ctx.toolSessionId ? deps.codexHomeForThread?.(ctx.toolSessionId) : void 0) ?? deps.codexHomeFor?.(ctx.cwd);
54011
54032
  const env = {
54012
54033
  ...process.env,
54013
54034
  ...resolvedCodexHome ? { CODEX_HOME: resolvedCodexHome } : {},
@@ -54294,9 +54315,8 @@ var CODEX_CAPABILITIES_BASE = {
54294
54315
  tool: "codex",
54295
54316
  toolSessionIdLabel: "Codex Thread ID",
54296
54317
  permissionModes: CODEX_APPROVAL_PRESETS,
54297
- // Codex 原生 child thread 已经映射到现有 Agent 卡 + history drawer;其余功能仍不支持
54298
- //(rewind 无 file-snapshot / 无 TUI pty / 无 observe 推送 / 无文件分享 / fork 是 claude jsonl 专用)。
54299
- features: { rewind: false, subagents: true, tui: false, observe: false, fileSharing: false, fork: false },
54318
+ // 本机 Codex 支持原生 child thread、已完成 turn 的 fork;rewind / TUI / observe / 文件分享仍关闭。
54319
+ features: { rewind: false, subagents: true, tui: false, observe: false, fileSharing: false, fork: true },
54300
54320
  configSchema: [
54301
54321
  {
54302
54322
  name: "model",
@@ -54361,6 +54381,7 @@ var CodexAdapter = class {
54361
54381
  fetchImpl;
54362
54382
  /** CODEX_HOME 归属解析(spec 2026-08-18-codex-project-alignment §3.1),由 index.ts 注入 */
54363
54383
  codexHomeFor;
54384
+ codexHomeForThread;
54364
54385
  getSignSecret;
54365
54386
  // capabilities 的进程级 memo。**回落结果同样被缓存**——否则 codex 没装时每次开弹窗都
54366
54387
  // spawn 一次必然失败的子进程。装好 codex / provider 加了模型后不用重启:设置 → 账号「引擎」的
@@ -54374,6 +54395,7 @@ var CodexAdapter = class {
54374
54395
  this.configQueryOverride = opts.configQueryOverride;
54375
54396
  this.fetchImpl = opts.fetchImpl;
54376
54397
  this.codexHomeFor = opts.codexHomeFor;
54398
+ this.codexHomeForThread = opts.codexHomeForThread;
54377
54399
  this.getSignSecret = opts.getSignSecret;
54378
54400
  }
54379
54401
  async probe() {
@@ -54446,6 +54468,7 @@ var CodexAdapter = class {
54446
54468
  return createCodexSession({ ...ctx, model: sanitizeCodexModel(ctx.model) }, sink, {
54447
54469
  logger: this.logger,
54448
54470
  codexHomeFor: this.codexHomeFor,
54471
+ codexHomeForThread: this.codexHomeForThread,
54449
54472
  getSignSecret: this.getSignSecret
54450
54473
  });
54451
54474
  }
@@ -56654,12 +56677,12 @@ var DEFAULT_TIMEOUT_MS2 = 15e3;
56654
56677
  var MAX_BUFFER = 4 * 1024 * 1024;
56655
56678
  function createGhRunner(defaults) {
56656
56679
  const baseTimeoutMs = defaults?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
56657
- const spawn15 = defaults?.execFile ?? import_node_child_process8.execFile;
56680
+ const spawn16 = defaults?.execFile ?? import_node_child_process8.execFile;
56658
56681
  return {
56659
56682
  run(args, opts) {
56660
56683
  const token = defaults?.token?.();
56661
56684
  return new Promise((resolve9, reject) => {
56662
- spawn15(
56685
+ spawn16(
56663
56686
  "gh",
56664
56687
  args,
56665
56688
  {
@@ -60933,10 +60956,10 @@ function createGithubApi(args) {
60933
60956
  const doFetch = args.fetch ?? fetch;
60934
60957
  const seal = args.seal ?? sealWithSodium;
60935
60958
  const timeoutMs = args.timeoutMs ?? TIMEOUT_MS;
60936
- async function request(method, path105, body) {
60959
+ async function request(method, path106, body) {
60937
60960
  let res;
60938
60961
  try {
60939
- res = await doFetch(`${API}${path105}`, {
60962
+ res = await doFetch(`${API}${path106}`, {
60940
60963
  method,
60941
60964
  headers: {
60942
60965
  Authorization: `Bearer ${args.token}`,
@@ -60961,8 +60984,8 @@ function createGithubApi(args) {
60961
60984
  }
60962
60985
  return { status: res.status, json };
60963
60986
  }
60964
- async function must(method, path105, what, body) {
60965
- const r2 = await request(method, path105, body);
60987
+ async function must(method, path106, what, body) {
60988
+ const r2 = await request(method, path106, body);
60966
60989
  if (r2.status < 200 || r2.status >= 300) throw new GithubApiError(r2.status, explain(r2.status, what, r2.json));
60967
60990
  return r2.json;
60968
60991
  }
@@ -61007,9 +61030,9 @@ function createGithubApi(args) {
61007
61030
  return { env: x2.name, ...Number.isFinite(at3) ? { setAt: at3 } : {} };
61008
61031
  });
61009
61032
  },
61010
- async getJson(path105) {
61033
+ async getJson(path106) {
61011
61034
  try {
61012
- const r2 = await request("GET", path105);
61035
+ const r2 = await request("GET", path106);
61013
61036
  return r2.status >= 200 && r2.status < 300 ? r2.json : void 0;
61014
61037
  } catch {
61015
61038
  return void 0;
@@ -62271,21 +62294,6 @@ var import_node_fs35 = __toESM(require("fs"), 1);
62271
62294
  var import_node_os12 = __toESM(require("os"), 1);
62272
62295
  var import_node_path40 = __toESM(require("path"), 1);
62273
62296
  var CHANNELS_FILE_NAME = "subscription-channels.json";
62274
- function providerSection(content) {
62275
- const header = /^\[model_providers\.cliproxyapi\][^\n]*(?:\n|$)/m.exec(content);
62276
- if (!header) return null;
62277
- const body = content.slice(header.index + header[0].length);
62278
- const nextHeader = body.search(/^\[/m);
62279
- return nextHeader < 0 ? body : body.slice(0, nextHeader);
62280
- }
62281
- function readAuthKey2(codexHome) {
62282
- try {
62283
- const key = JSON.parse(import_node_fs35.default.readFileSync(import_node_path40.default.join(codexHome, "auth.json"), "utf8")).OPENAI_API_KEY;
62284
- return typeof key === "string" && key ? key : null;
62285
- } catch {
62286
- return null;
62287
- }
62288
- }
62289
62297
  var SubscriptionChannelStore = class {
62290
62298
  filePath;
62291
62299
  codexHome;
@@ -62412,20 +62420,15 @@ var SubscriptionChannelStore = class {
62412
62420
  if (!import_node_fs35.default.existsSync(configPath)) return null;
62413
62421
  const content = import_node_fs35.default.readFileSync(configPath, "utf8");
62414
62422
  if (!content.includes('model_provider = "cliproxyapi"')) return null;
62415
- const section = providerSection(content);
62416
- if (!section) return null;
62417
- const urlMatch = /^\s*base_url\s*=\s*["']([^"']+)["']/m.exec(section);
62418
- const echoKey = /^\s*auth\s*=\s*\{\s*command\s*=\s*["']echo["']\s*,\s*args\s*=\s*\[\s*["']([^"']+)["']\s*\]/m.exec(section)?.[1];
62419
- const inlineKey = /^\s*experimental_bearer_token\s*=\s*["']([^"']+)["']/m.exec(section)?.[1];
62420
- const envKey = /^\s*env_key\s*=\s*["']([^"']+)["']/m.exec(section)?.[1];
62421
- const key = echoKey ?? inlineKey ?? (envKey ? process.env[envKey] : !/^\s*auth\s*=/m.test(section) ? readAuthKey2(this.codexHome) : null);
62422
- if (urlMatch && key) {
62423
+ const urlMatch = /base_url\s*=\s*["']([^"']+)["']/.exec(content);
62424
+ const keyMatch = /args\s*=\s*\[\s*["']([^"']+)["']\s*\]/.exec(content) || /experimental_bearer_token\s*=\s*["']([^"']+)["']/.exec(content);
62425
+ if (urlMatch && keyMatch) {
62423
62426
  const id = "default-pool";
62424
62427
  const channel = {
62425
62428
  id,
62426
62429
  name: "\u9ED8\u8BA4\u56E2\u961F\u53F7\u6C60",
62427
62430
  baseUrl: normalizeEngineBaseUrl(urlMatch[1]),
62428
- apiKey: key,
62431
+ apiKey: keyMatch[1],
62429
62432
  remark: "\u4ECE\u672C\u673A\u5DF2\u63A5\u5165\u7684\u53F7\u6C60\u914D\u7F6E\u81EA\u52A8\u8FC1\u79FB",
62430
62433
  createdAt: Date.now(),
62431
62434
  updatedAt: Date.now()
@@ -62506,8 +62509,8 @@ function refreshPathFromLoginShell() {
62506
62509
  timeout: 3e3,
62507
62510
  stdio: ["ignore", "pipe", "ignore"]
62508
62511
  });
62509
- const path105 = out.trim();
62510
- if (path105) process.env.PATH = path105;
62512
+ const path106 = out.trim();
62513
+ if (path106) process.env.PATH = path106;
62511
62514
  } catch {
62512
62515
  }
62513
62516
  }
@@ -65456,13 +65459,13 @@ function mapSkillsListResponse(res) {
65456
65459
  const r2 = s2 ?? {};
65457
65460
  const name = str4(r2.name);
65458
65461
  if (!name) continue;
65459
- const path105 = str4(r2.path);
65462
+ const path106 = str4(r2.path);
65460
65463
  const description = str4(r2.description);
65461
65464
  const isPlugin = name.includes(":");
65462
65465
  out.push({
65463
65466
  name,
65464
65467
  source: isPlugin ? "plugin" : "project",
65465
- ...path105 ? { path: path105 } : {},
65468
+ ...path106 ? { path: path106 } : {},
65466
65469
  ...description ? { description } : {},
65467
65470
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
65468
65471
  });
@@ -69996,7 +69999,7 @@ var ServerKeyStore = class {
69996
69999
  init_protocol();
69997
70000
 
69998
70001
  // src/handlers/index.ts
69999
- var import_node_path79 = __toESM(require("path"), 1);
70002
+ var import_node_path80 = __toESM(require("path"), 1);
70000
70003
 
70001
70004
  // src/handlers/session.ts
70002
70005
  init_protocol();
@@ -70069,20 +70072,83 @@ function forkSession(input) {
70069
70072
  return { forkedToolSessionId, forkedFilePath };
70070
70073
  }
70071
70074
 
70072
- // src/session/external-scan.ts
70075
+ // src/tools/codex-fork.ts
70076
+ var import_node_child_process17 = require("child_process");
70073
70077
  var import_node_fs59 = __toESM(require("fs"), 1);
70078
+ var import_node_path65 = __toESM(require("path"), 1);
70079
+ function openCodexAppServer(home, cwd) {
70080
+ const proc = (0, import_node_child_process17.spawn)(process.env.CODEX_BIN ?? "codex", ["app-server", "--listen", "stdio://"], {
70081
+ cwd,
70082
+ env: { ...process.env, CODEX_HOME: home },
70083
+ stdio: ["pipe", "pipe", "pipe"]
70084
+ });
70085
+ proc.on("error", () => proc.stdout?.destroy());
70086
+ const stream = stdioLineStream(proc);
70087
+ return {
70088
+ stream,
70089
+ close: () => {
70090
+ stream.close();
70091
+ if (proc.exitCode === null && proc.signalCode === null) proc.kill("SIGTERM");
70092
+ }
70093
+ };
70094
+ }
70095
+ async function forkCodexThread(input, deps = {}) {
70096
+ const source = locateCodexThread(input.roots, input.sourceThreadId);
70097
+ if (!source) throw new Error(`codex fork: source thread not found: ${input.sourceThreadId}`);
70098
+ const rollout = parseCodexRollout(import_node_fs59.default.readFileSync(source.file, "utf8"));
70099
+ if (rollout.meta?.threadId.toLowerCase() !== input.sourceThreadId.toLowerCase()) {
70100
+ throw new Error(`codex fork: source thread metadata does not match ${input.sourceThreadId}`);
70101
+ }
70102
+ if (!rollout.turns.some((turn) => turn.turnId === input.turnId && turn.status === "completed")) {
70103
+ throw new Error(`codex fork: completed turn not found: ${input.turnId}`);
70104
+ }
70105
+ const connection = (deps.open ?? openCodexAppServer)(source.home, input.targetCwd);
70106
+ const timeoutMs = deps.timeoutMs ?? DEFAULT_CODEX_QUERY_TIMEOUT_MS;
70107
+ let timer;
70108
+ const timeout = new Promise((_3, reject) => {
70109
+ timer = setTimeout(() => reject(new Error(`codex app-server thread/fork timed out after ${timeoutMs}ms`)), timeoutMs);
70110
+ });
70111
+ try {
70112
+ const client = new CodexAppServerClient(connection.stream);
70113
+ return await Promise.race([timeout, (async () => {
70114
+ await client.request("initialize", {
70115
+ clientInfo: { name: "clawd", title: "clawd", version: "0" },
70116
+ capabilities: null
70117
+ });
70118
+ client.notify("initialized", {});
70119
+ const result = await client.request("thread/fork", {
70120
+ threadId: input.sourceThreadId,
70121
+ lastTurnId: input.turnId,
70122
+ cwd: input.targetCwd
70123
+ });
70124
+ const forkedToolSessionId = result?.thread?.id;
70125
+ if (typeof forkedToolSessionId !== "string" || !forkedToolSessionId) {
70126
+ throw new Error("codex fork: app-server returned no thread id");
70127
+ }
70128
+ const forked = locateCodexThread([import_node_path65.default.join(source.home, "sessions")], forkedToolSessionId);
70129
+ if (!forked) throw new Error(`codex fork: rollout not found for forked thread ${forkedToolSessionId}`);
70130
+ return { forkedToolSessionId, forkedFilePath: forked.file };
70131
+ })()]);
70132
+ } finally {
70133
+ if (timer) clearTimeout(timer);
70134
+ connection.close();
70135
+ }
70136
+ }
70137
+
70138
+ // src/session/external-scan.ts
70139
+ var import_node_fs60 = __toESM(require("fs"), 1);
70074
70140
  var import_promises11 = __toESM(require("fs/promises"), 1);
70075
70141
  var import_node_os24 = __toESM(require("os"), 1);
70076
- var import_node_path65 = __toESM(require("path"), 1);
70142
+ var import_node_path66 = __toESM(require("path"), 1);
70077
70143
  var import_node_module = require("module");
70078
- var import_node_child_process17 = require("child_process");
70144
+ var import_node_child_process18 = require("child_process");
70079
70145
  var import_meta7 = {};
70080
70146
  function defaultRoots() {
70081
70147
  const home = import_node_os24.default.homedir();
70082
70148
  return {
70083
- claudeProjectsDir: import_node_path65.default.join(home, ".claude", "projects"),
70084
- codexDir: import_node_path65.default.join(home, ".codex"),
70085
- clawdDir: import_node_path65.default.join(home, ".clawd")
70149
+ claudeProjectsDir: import_node_path66.default.join(home, ".claude", "projects"),
70150
+ codexDir: import_node_path66.default.join(home, ".codex"),
70151
+ clawdDir: import_node_path66.default.join(home, ".clawd")
70086
70152
  };
70087
70153
  }
70088
70154
  function width(s2) {
@@ -70133,7 +70199,7 @@ function userText(msg) {
70133
70199
  }
70134
70200
  function isDir2(p3) {
70135
70201
  try {
70136
- return import_node_fs59.default.statSync(p3).isDirectory();
70202
+ return import_node_fs60.default.statSync(p3).isDirectory();
70137
70203
  } catch {
70138
70204
  return false;
70139
70205
  }
@@ -70142,7 +70208,7 @@ async function scanClaude(roots) {
70142
70208
  const out = [];
70143
70209
  let dirs;
70144
70210
  try {
70145
- dirs = import_node_fs59.default.readdirSync(roots.claudeProjectsDir, { withFileTypes: true });
70211
+ dirs = import_node_fs60.default.readdirSync(roots.claudeProjectsDir, { withFileTypes: true });
70146
70212
  } catch {
70147
70213
  return out;
70148
70214
  }
@@ -70150,16 +70216,16 @@ async function scanClaude(roots) {
70150
70216
  if (!d2.isDirectory()) continue;
70151
70217
  let files;
70152
70218
  try {
70153
- files = import_node_fs59.default.readdirSync(import_node_path65.default.join(roots.claudeProjectsDir, d2.name));
70219
+ files = import_node_fs60.default.readdirSync(import_node_path66.default.join(roots.claudeProjectsDir, d2.name));
70154
70220
  } catch {
70155
70221
  continue;
70156
70222
  }
70157
70223
  for (const name of files) {
70158
70224
  if (!name.endsWith(".jsonl")) continue;
70159
- const file = import_node_path65.default.join(roots.claudeProjectsDir, d2.name, name);
70225
+ const file = import_node_path66.default.join(roots.claudeProjectsDir, d2.name, name);
70160
70226
  let st2;
70161
70227
  try {
70162
- st2 = import_node_fs59.default.statSync(file);
70228
+ st2 = import_node_fs60.default.statSync(file);
70163
70229
  } catch {
70164
70230
  continue;
70165
70231
  }
@@ -70212,7 +70278,7 @@ async function scanClaude(roots) {
70212
70278
  return out;
70213
70279
  }
70214
70280
  async function countCodexTurns(rolloutPath) {
70215
- if (!rolloutPath || !import_node_fs59.default.existsSync(rolloutPath)) return { turns: 0, first: "" };
70281
+ if (!rolloutPath || !import_node_fs60.default.existsSync(rolloutPath)) return { turns: 0, first: "" };
70216
70282
  let turns = 0;
70217
70283
  let first = "";
70218
70284
  for await (const rec6 of jsonLines(rolloutPath)) {
@@ -70243,14 +70309,14 @@ function readCodexThreads(db) {
70243
70309
  } catch {
70244
70310
  }
70245
70311
  try {
70246
- const info = (0, import_node_child_process17.spawnSync)("sqlite3", [db, "PRAGMA table_info(threads);"], { encoding: "utf8" });
70312
+ const info = (0, import_node_child_process18.spawnSync)("sqlite3", [db, "PRAGMA table_info(threads);"], { encoding: "utf8" });
70247
70313
  if (info.status !== 0) return null;
70248
70314
  const have = new Set(
70249
70315
  info.stdout.split("\n").map((l2) => l2.split("|")[1]).filter(Boolean)
70250
70316
  );
70251
70317
  if (!["id", "cwd", "updated_at", "thread_source"].every((c2) => have.has(c2))) return null;
70252
70318
  const sel = COLS.map((c2) => have.has(c2) ? c2 : `NULL AS ${c2}`).join(", ");
70253
- const res = (0, import_node_child_process17.spawnSync)("sqlite3", ["-json", db, `SELECT ${sel} FROM threads;`], {
70319
+ const res = (0, import_node_child_process18.spawnSync)("sqlite3", ["-json", db, `SELECT ${sel} FROM threads;`], {
70254
70320
  encoding: "utf8",
70255
70321
  maxBuffer: 64 * 1024 * 1024
70256
70322
  });
@@ -70286,7 +70352,7 @@ async function codexFromRows(rows) {
70286
70352
  async function scanCodex(roots) {
70287
70353
  let dbs = [];
70288
70354
  try {
70289
- dbs = import_node_fs59.default.readdirSync(roots.codexDir).filter((n2) => /^state_.*\.sqlite$/.test(n2)).sort().map((n2) => import_node_path65.default.join(roots.codexDir, n2));
70355
+ dbs = import_node_fs60.default.readdirSync(roots.codexDir).filter((n2) => /^state_.*\.sqlite$/.test(n2)).sort().map((n2) => import_node_path66.default.join(roots.codexDir, n2));
70290
70356
  } catch {
70291
70357
  }
70292
70358
  if (!dbs.length) return [];
@@ -70295,14 +70361,14 @@ async function scanCodex(roots) {
70295
70361
  }
70296
70362
  async function scanExternalSessions(rootsIn) {
70297
70363
  const roots = { ...defaultRoots(), ...rootsIn };
70298
- const personaRoot = import_node_path65.default.join(roots.clawdDir, "personas");
70364
+ const personaRoot = import_node_path66.default.join(roots.clawdDir, "personas");
70299
70365
  const all = [...await scanClaude(roots), ...await scanCodex(roots)];
70300
70366
  const out = [];
70301
70367
  for (const c2 of all) {
70302
70368
  if (c2.skip) continue;
70303
70369
  if (c2.turns < 1) continue;
70304
70370
  if (!c2.cwd || !isDir2(c2.cwd)) continue;
70305
- if (c2.cwd === roots.clawdDir || c2.cwd === personaRoot || c2.cwd.startsWith(personaRoot + import_node_path65.default.sep) || c2.cwd.startsWith(roots.clawdDir + import_node_path65.default.sep)) {
70371
+ if (c2.cwd === roots.clawdDir || c2.cwd === personaRoot || c2.cwd.startsWith(personaRoot + import_node_path66.default.sep) || c2.cwd.startsWith(roots.clawdDir + import_node_path66.default.sep)) {
70306
70372
  continue;
70307
70373
  }
70308
70374
  out.push({
@@ -70319,13 +70385,13 @@ async function scanExternalSessions(rootsIn) {
70319
70385
  }
70320
70386
 
70321
70387
  // src/session/import-ledger.ts
70322
- var import_node_fs60 = __toESM(require("fs"), 1);
70323
- var import_node_path66 = __toESM(require("path"), 1);
70388
+ var import_node_fs61 = __toESM(require("fs"), 1);
70389
+ var import_node_path67 = __toESM(require("path"), 1);
70324
70390
  var LEDGER_FILENAME = "session-import-ledger.json";
70325
70391
  function readImportLedger(dataDir) {
70326
70392
  try {
70327
70393
  const parsed = JSON.parse(
70328
- import_node_fs60.default.readFileSync(import_node_path66.default.join(dataDir, LEDGER_FILENAME), "utf8")
70394
+ import_node_fs61.default.readFileSync(import_node_path67.default.join(dataDir, LEDGER_FILENAME), "utf8")
70329
70395
  );
70330
70396
  return Array.isArray(parsed) ? parsed : [];
70331
70397
  } catch {
@@ -70333,14 +70399,15 @@ function readImportLedger(dataDir) {
70333
70399
  }
70334
70400
  }
70335
70401
  function appendImportLedger(dataDir, entry) {
70336
- const file = import_node_path66.default.join(dataDir, LEDGER_FILENAME);
70402
+ const file = import_node_path67.default.join(dataDir, LEDGER_FILENAME);
70337
70403
  const entries = readImportLedger(dataDir);
70338
70404
  entries.push(entry);
70339
- import_node_fs60.default.writeFileSync(file, JSON.stringify(entries, null, 2), { encoding: "utf8", mode: 384 });
70405
+ import_node_fs61.default.writeFileSync(file, JSON.stringify(entries, null, 2), { encoding: "utf8", mode: 384 });
70340
70406
  }
70341
70407
 
70342
70408
  // src/handlers/session.ts
70343
70409
  init_protocol();
70410
+ var import_node_path68 = __toESM(require("path"), 1);
70344
70411
  function canReadSearchHit(ctx, file) {
70345
70412
  if (!ctx || ctx.principal.kind === "owner") return true;
70346
70413
  return canAccessPersona(ctx.grants, file.ownerPersonaId, "read") && file.creatorPrincipalId === ctx.principal.id;
@@ -70608,8 +70675,12 @@ ${attribution}` } : args;
70608
70675
  };
70609
70676
  const fork = async (frame, _client, ctx) => {
70610
70677
  const args = SessionForkArgs.parse(frame);
70678
+ const source = args.sourceSessionId ? manager.get({ sessionId: args.sourceSessionId }).response : null;
70679
+ if (source && (source.sessionId !== args.sourceSessionId || source.toolSessionId !== args.toolSessionId)) {
70680
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "fork: source thread does not match source session");
70681
+ }
70611
70682
  if (ctx && ctx.principal.kind === "guest") {
70612
- const src = store.list().find((s2) => s2.toolSessionId === args.toolSessionId);
70683
+ const src = source ?? store.list().find((s2) => s2.toolSessionId === args.toolSessionId);
70613
70684
  const ok = src != null && canAccessPersona(ctx.grants, src.ownerPersonaId, "read") && src.creatorPrincipalId === ctx.principal.id;
70614
70685
  if (!ok) {
70615
70686
  throw new ClawdError(
@@ -70617,8 +70688,33 @@ ${attribution}` } : args;
70617
70688
  `principal guest:${ctx.principal.id} cannot fork tool session ${args.toolSessionId}`
70618
70689
  );
70619
70690
  }
70691
+ if (source?.tool === "codex" && args.targetCwd && import_node_path68.default.resolve(args.targetCwd) !== import_node_path68.default.resolve(source.cwd)) {
70692
+ throw new ClawdError(ERROR_CODES.FORBIDDEN, "fork: guest targetCwd must match source cwd");
70693
+ }
70620
70694
  }
70621
- const result = forkSession(args);
70695
+ if (source && source.tool !== void 0 && source.tool !== "claude" && source.tool !== "codex") {
70696
+ throw new ClawdError(ERROR_CODES.TOOL_NOT_SUPPORTED, `tool ${source.tool} does not support fork`);
70697
+ }
70698
+ if (args.turnId && source?.tool !== "codex") {
70699
+ throw new ClawdError(ERROR_CODES.TOOL_NOT_SUPPORTED, "fork: turnId requires a local Codex source session");
70700
+ }
70701
+ if (source?.tool === "codex" && (!args.turnId || args.messageUuid || !args.targetCwd)) {
70702
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "fork: Codex requires turnId and targetCwd");
70703
+ }
70704
+ if (source?.tool !== "codex" && !args.messageUuid) {
70705
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "fork: Claude requires messageUuid");
70706
+ }
70707
+ const result = source?.tool === "codex" ? await (deps.forkCodexThread ?? forkCodexThread)({
70708
+ roots: codexRolloutRoots(deps.dataDir),
70709
+ sourceThreadId: args.toolSessionId,
70710
+ turnId: args.turnId,
70711
+ targetCwd: args.targetCwd
70712
+ }) : forkSession({
70713
+ cwd: args.cwd,
70714
+ toolSessionId: args.toolSessionId,
70715
+ messageUuid: args.messageUuid,
70716
+ ...args.targetCwd ? { targetCwd: args.targetCwd } : {}
70717
+ });
70622
70718
  deps.logger?.info("session-fork.done", {
70623
70719
  argCwd: args.cwd,
70624
70720
  sourceToolSessionId: args.toolSessionId,
@@ -70869,7 +70965,7 @@ function buildPermissionHandlers(deps) {
70869
70965
  }
70870
70966
 
70871
70967
  // src/handlers/history.ts
70872
- var path81 = __toESM(require("path"), 1);
70968
+ var path83 = __toESM(require("path"), 1);
70873
70969
  init_protocol();
70874
70970
 
70875
70971
  // src/session/recent-dirs.ts
@@ -70887,7 +70983,7 @@ function listRecentDirs(store, limit = 50) {
70887
70983
  }
70888
70984
 
70889
70985
  // src/permission/persona-paths.ts
70890
- var path80 = __toESM(require("path"), 1);
70986
+ var path82 = __toESM(require("path"), 1);
70891
70987
  function getAllowedPersonaIds(grants, action) {
70892
70988
  const ids = /* @__PURE__ */ new Set();
70893
70989
  for (const g3 of grants) {
@@ -70900,42 +70996,42 @@ function getAllowedPersonaIds(grants, action) {
70900
70996
  return ids;
70901
70997
  }
70902
70998
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
70903
- const target = path80.resolve(absPath);
70999
+ const target = path82.resolve(absPath);
70904
71000
  if (userWorkDir) {
70905
- const u2 = path80.resolve(userWorkDir);
70906
- const usep = u2.endsWith(path80.sep) ? "" : path80.sep;
71001
+ const u2 = path82.resolve(userWorkDir);
71002
+ const usep = u2.endsWith(path82.sep) ? "" : path82.sep;
70907
71003
  if (target === u2 || target.startsWith(u2 + usep)) return true;
70908
71004
  }
70909
- const root = path80.resolve(personaRoot);
70910
- const sep4 = root.endsWith(path80.sep) ? "" : path80.sep;
71005
+ const root = path82.resolve(personaRoot);
71006
+ const sep4 = root.endsWith(path82.sep) ? "" : path82.sep;
70911
71007
  if (!target.startsWith(root + sep4)) return false;
70912
- const rel = path80.relative(root, target);
71008
+ const rel = path82.relative(root, target);
70913
71009
  if (!rel || rel.startsWith("..")) return false;
70914
- const personaId2 = rel.split(path80.sep)[0];
71010
+ const personaId2 = rel.split(path82.sep)[0];
70915
71011
  if (!personaId2) return false;
70916
71012
  const allowed = getAllowedPersonaIds(grants, action);
70917
71013
  if (allowed === "*") return true;
70918
71014
  return allowed.has(personaId2);
70919
71015
  }
70920
71016
  function personaIdFromPath(absPath, personaRoot) {
70921
- const root = path80.resolve(personaRoot);
70922
- const target = path80.resolve(absPath);
70923
- const sep4 = root.endsWith(path80.sep) ? "" : path80.sep;
71017
+ const root = path82.resolve(personaRoot);
71018
+ const target = path82.resolve(absPath);
71019
+ const sep4 = root.endsWith(path82.sep) ? "" : path82.sep;
70924
71020
  if (!target.startsWith(root + sep4)) return null;
70925
- const rel = path80.relative(root, target);
71021
+ const rel = path82.relative(root, target);
70926
71022
  if (!rel || rel.startsWith("..")) return null;
70927
- const id = rel.split(path80.sep)[0];
71023
+ const id = rel.split(path82.sep)[0];
70928
71024
  return id || null;
70929
71025
  }
70930
71026
  function isPathWithin(dir, absPath) {
70931
- const d2 = path80.resolve(dir);
70932
- const t2 = path80.resolve(absPath);
70933
- const sep4 = d2.endsWith(path80.sep) ? "" : path80.sep;
71027
+ const d2 = path82.resolve(dir);
71028
+ const t2 = path82.resolve(absPath);
71029
+ const sep4 = d2.endsWith(path82.sep) ? "" : path82.sep;
70934
71030
  return t2 === d2 || t2.startsWith(d2 + sep4);
70935
71031
  }
70936
71032
  function isPathInGuestBoundary(personaRoot, personaId2, userWorkDir, absPath) {
70937
71033
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
70938
- return personaIdFromPath(path80.resolve(absPath), personaRoot) === personaId2;
71034
+ return personaIdFromPath(path82.resolve(absPath), personaRoot) === personaId2;
70939
71035
  }
70940
71036
 
70941
71037
  // src/handlers/history.ts
@@ -70961,6 +71057,7 @@ function slimHistoryMessages(messages) {
70961
71057
  if (m3.isSidechain !== void 0) out.isSidechain = m3.isSidechain;
70962
71058
  if (m3.askQuestionAnswers !== void 0) out.askQuestionAnswers = m3.askQuestionAnswers;
70963
71059
  if (m3.durationMs !== void 0) out.durationMs = m3.durationMs;
71060
+ if (m3.turnId !== void 0) out.turnId = m3.turnId;
70964
71061
  const isErrorPair = m3.toolUseId !== void 0 && errorToolUseIds.has(m3.toolUseId);
70965
71062
  if (m3.kind === "tool_call" && isErrorPair && m3.input !== void 0) out.input = m3.input;
70966
71063
  if (m3.kind === "tool_result" && m3.error !== void 0 && m3.output !== void 0) out.output = m3.output;
@@ -70988,7 +71085,7 @@ function buildHistoryHandlers(deps) {
70988
71085
  if (!pid) return false;
70989
71086
  return isGuestPathAllowed(
70990
71087
  ctx.grants,
70991
- path81.join(personaRoot, pid),
71088
+ path83.join(personaRoot, pid),
70992
71089
  personaRoot,
70993
71090
  "read",
70994
71091
  userWorkDir
@@ -71000,7 +71097,7 @@ function buildHistoryHandlers(deps) {
71000
71097
  };
71001
71098
  const list = async (frame, _client, ctx) => {
71002
71099
  const args = HistoryListArgs.parse(frame);
71003
- assertGuestPath(ctx, path81.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
71100
+ assertGuestPath(ctx, path83.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
71004
71101
  const sessions = await history.listSessions(args);
71005
71102
  return { response: { type: "history:list", sessions } };
71006
71103
  };
@@ -71048,14 +71145,14 @@ function buildHistoryHandlers(deps) {
71048
71145
  };
71049
71146
  const subagents = async (frame, _client, ctx) => {
71050
71147
  const args = HistorySubagentsArgs.parse(frame);
71051
- assertGuestPath(ctx, path81.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
71148
+ assertGuestPath(ctx, path83.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
71052
71149
  assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagents");
71053
71150
  const subs = await historyReaderForToolSession(args.toolSessionId).listSubagents(args);
71054
71151
  return { response: { type: "history:subagents", subagents: subs } };
71055
71152
  };
71056
71153
  const subagentRead = async (frame, _client, ctx) => {
71057
71154
  const args = HistorySubagentReadArgs.parse(frame);
71058
- assertGuestPath(ctx, path81.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
71155
+ assertGuestPath(ctx, path83.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
71059
71156
  assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagent-read");
71060
71157
  const res = await historyReaderForToolSession(args.toolSessionId).readSubagent(args);
71061
71158
  return { response: { type: "history:subagent-read", ...res } };
@@ -71065,7 +71162,7 @@ function buildHistoryHandlers(deps) {
71065
71162
  if (ctx?.principal.kind === "guest" && personaRoot) {
71066
71163
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
71067
71164
  const filtered = dirs.filter(
71068
- (d2) => isGuestPathAllowed(ctx.grants, path81.resolve(d2.cwd), personaRoot, "read", userWorkDir)
71165
+ (d2) => isGuestPathAllowed(ctx.grants, path83.resolve(d2.cwd), personaRoot, "read", userWorkDir)
71069
71166
  );
71070
71167
  return { response: { type: "history:recentDirs", dirs: filtered } };
71071
71168
  }
@@ -71082,17 +71179,17 @@ function buildHistoryHandlers(deps) {
71082
71179
  }
71083
71180
 
71084
71181
  // src/handlers/projects.ts
71085
- var fs78 = __toESM(require("fs"), 1);
71182
+ var fs79 = __toESM(require("fs"), 1);
71086
71183
  var os26 = __toESM(require("os"), 1);
71087
- var path82 = __toESM(require("path"), 1);
71088
- var import_node_child_process18 = require("child_process");
71184
+ var path84 = __toESM(require("path"), 1);
71185
+ var import_node_child_process19 = require("child_process");
71089
71186
  init_protocol();
71090
- var DEFAULT_PROJECTS_ROOT = path82.join(os26.homedir(), "Documents", "ChatGPT");
71187
+ var DEFAULT_PROJECTS_ROOT = path84.join(os26.homedir(), "Documents", "ChatGPT");
71091
71188
  function allocateDir(root, name) {
71092
- let candidate = path82.join(root, name);
71189
+ let candidate = path84.join(root, name);
71093
71190
  let n2 = 2;
71094
- while (fs78.existsSync(candidate)) {
71095
- candidate = path82.join(root, `${name} ${n2}`);
71191
+ while (fs79.existsSync(candidate)) {
71192
+ candidate = path84.join(root, `${name} ${n2}`);
71096
71193
  n2 += 1;
71097
71194
  }
71098
71195
  return candidate;
@@ -71120,11 +71217,11 @@ function buildProjectHandlers(deps) {
71120
71217
  throw new ClawdError(ERROR_CODES.INVALID_CWD, "project:reveal \u4EC5\u652F\u6301 macOS");
71121
71218
  }
71122
71219
  try {
71123
- if (!fs78.statSync(args.path).isDirectory()) throw new Error("not dir");
71220
+ if (!fs79.statSync(args.path).isDirectory()) throw new Error("not dir");
71124
71221
  } catch {
71125
71222
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${args.path}`);
71126
71223
  }
71127
- const r2 = (0, import_node_child_process18.spawnSync)("open", ["-R", args.path], { stdio: "ignore" });
71224
+ const r2 = (0, import_node_child_process19.spawnSync)("open", ["-R", args.path], { stdio: "ignore" });
71128
71225
  if (r2.status !== 0) {
71129
71226
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `open -R \u5931\u8D25: exit ${r2.status}`);
71130
71227
  }
@@ -71136,7 +71233,7 @@ function buildProjectHandlers(deps) {
71136
71233
  let target;
71137
71234
  if (args.path) {
71138
71235
  try {
71139
- if (!fs78.statSync(args.path).isDirectory()) throw new Error("not dir");
71236
+ if (!fs79.statSync(args.path).isDirectory()) throw new Error("not dir");
71140
71237
  } catch {
71141
71238
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${args.path}`);
71142
71239
  }
@@ -71144,15 +71241,15 @@ function buildProjectHandlers(deps) {
71144
71241
  } else {
71145
71242
  const name = args.name;
71146
71243
  try {
71147
- fs78.mkdirSync(root, { recursive: true });
71244
+ fs79.mkdirSync(root, { recursive: true });
71148
71245
  target = allocateDir(root, name);
71149
- fs78.mkdirSync(target);
71246
+ fs79.mkdirSync(target);
71150
71247
  } catch (err) {
71151
71248
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `\u521B\u5EFA\u9879\u76EE\u76EE\u5F55\u5931\u8D25: ${String(err)}`);
71152
71249
  }
71153
- const git2 = (0, import_node_child_process18.spawnSync)("git", ["init"], { cwd: target, stdio: "ignore" });
71250
+ const git2 = (0, import_node_child_process19.spawnSync)("git", ["init"], { cwd: target, stdio: "ignore" });
71154
71251
  if (git2.status !== 0) {
71155
- fs78.rmSync(target, { recursive: true, force: true });
71252
+ fs79.rmSync(target, { recursive: true, force: true });
71156
71253
  throw new ClawdError(
71157
71254
  ERROR_CODES.INVALID_CWD,
71158
71255
  `git init \u5931\u8D25\uFF08${git2.error ? String(git2.error) : `exit ${git2.status}`}\uFF09: ${target}`
@@ -71177,7 +71274,7 @@ function buildProjectHandlers(deps) {
71177
71274
  }
71178
71275
 
71179
71276
  // src/handlers/workspace.ts
71180
- var path83 = __toESM(require("path"), 1);
71277
+ var path85 = __toESM(require("path"), 1);
71181
71278
  init_protocol();
71182
71279
  var os27 = __toESM(require("os"), 1);
71183
71280
  init_protocol();
@@ -71220,22 +71317,22 @@ function buildWorkspaceHandlers(deps) {
71220
71317
  const args = WorkspaceListArgs.parse(frame);
71221
71318
  const isGuest = ctx?.principal.kind === "guest";
71222
71319
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os27.homedir();
71223
- const resolvedCwd = path83.resolve(args.cwd ?? fallbackCwd);
71224
- const target = args.path ? path83.resolve(resolvedCwd, args.path) : resolvedCwd;
71320
+ const resolvedCwd = path85.resolve(args.cwd ?? fallbackCwd);
71321
+ const target = args.path ? path85.resolve(resolvedCwd, args.path) : resolvedCwd;
71225
71322
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
71226
71323
  const res = workspace.list({ ...args, cwd: resolvedCwd });
71227
71324
  return { response: { type: "workspace:list", ...res } };
71228
71325
  };
71229
71326
  const read = async (frame, _client, ctx) => {
71230
71327
  const args = WorkspaceReadArgs.parse(frame);
71231
- const target = path83.isAbsolute(args.path) ? path83.resolve(args.path) : path83.resolve(args.cwd, args.path);
71328
+ const target = path85.isAbsolute(args.path) ? path85.resolve(args.path) : path85.resolve(args.cwd, args.path);
71232
71329
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
71233
71330
  const res = workspace.read(args);
71234
71331
  return { response: { type: "workspace:read", ...res } };
71235
71332
  };
71236
71333
  const skillsList = async (frame, _client, ctx) => {
71237
71334
  const args = SkillsListArgs.parse(frame);
71238
- const cwdAbs = path83.resolve(args.cwd);
71335
+ const cwdAbs = path85.resolve(args.cwd);
71239
71336
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
71240
71337
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
71241
71338
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -71247,7 +71344,7 @@ function buildWorkspaceHandlers(deps) {
71247
71344
  };
71248
71345
  const agentsList = async (frame, _client, ctx) => {
71249
71346
  const args = AgentsListArgs.parse(frame);
71250
- const cwdAbs = path83.resolve(args.cwd);
71347
+ const cwdAbs = path85.resolve(args.cwd);
71251
71348
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
71252
71349
  if (isCodexFamily(args.tool) || isPiFamily(args.tool)) {
71253
71350
  return { response: { type: "agents:list", agents: [] } };
@@ -71269,20 +71366,20 @@ function buildWorkspaceHandlers(deps) {
71269
71366
  }
71270
71367
 
71271
71368
  // src/handlers/git.ts
71272
- var path85 = __toESM(require("path"), 1);
71369
+ var path87 = __toESM(require("path"), 1);
71273
71370
  init_protocol();
71274
71371
  init_protocol();
71275
71372
 
71276
71373
  // src/workspace/git.ts
71277
- var import_node_child_process19 = require("child_process");
71278
- var import_node_fs61 = __toESM(require("fs"), 1);
71279
- var import_node_path67 = __toESM(require("path"), 1);
71374
+ var import_node_child_process20 = require("child_process");
71375
+ var import_node_fs62 = __toESM(require("fs"), 1);
71376
+ var import_node_path69 = __toESM(require("path"), 1);
71280
71377
  var import_node_util2 = require("util");
71281
- var pexec = (0, import_node_util2.promisify)(import_node_child_process19.execFile);
71378
+ var pexec = (0, import_node_util2.promisify)(import_node_child_process20.execFile);
71282
71379
  function normalizePath(p3) {
71283
- const resolved = import_node_path67.default.resolve(p3);
71380
+ const resolved = import_node_path69.default.resolve(p3);
71284
71381
  try {
71285
- return import_node_fs61.default.realpathSync(resolved);
71382
+ return import_node_fs62.default.realpathSync(resolved);
71286
71383
  } catch {
71287
71384
  return resolved;
71288
71385
  }
@@ -71356,7 +71453,7 @@ async function listGitBranches(cwd) {
71356
71453
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
71357
71454
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
71358
71455
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
71359
- if (!isGuestPathAllowed(ctx.grants, path85.resolve(cwd), personaRoot, "read", userWorkDir)) {
71456
+ if (!isGuestPathAllowed(ctx.grants, path87.resolve(cwd), personaRoot, "read", userWorkDir)) {
71360
71457
  throw new ClawdError(
71361
71458
  ERROR_CODES.UNAUTHORIZED,
71362
71459
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -71408,7 +71505,7 @@ function buildCapabilitiesHandlers(deps) {
71408
71505
  init_protocol();
71409
71506
 
71410
71507
  // src/tools/version-probe.ts
71411
- var import_node_child_process20 = require("child_process");
71508
+ var import_node_child_process21 = require("child_process");
71412
71509
  init_claude();
71413
71510
  init_protocol();
71414
71511
  function parseVersionOutput(out) {
@@ -71416,7 +71513,7 @@ function parseVersionOutput(out) {
71416
71513
  }
71417
71514
  function execVersion(bin) {
71418
71515
  return new Promise((resolve9, reject) => {
71419
- (0, import_node_child_process20.execFile)(bin, ["--version"], { timeout: 5e3 }, (err, stdout) => {
71516
+ (0, import_node_child_process21.execFile)(bin, ["--version"], { timeout: 5e3 }, (err, stdout) => {
71420
71517
  if (err) reject(err);
71421
71518
  else resolve9(String(stdout));
71422
71519
  });
@@ -71449,7 +71546,7 @@ async function probeToolVersions(opts = {}) {
71449
71546
  }
71450
71547
 
71451
71548
  // src/tools/pool-connect.ts
71452
- var import_node_child_process21 = require("child_process");
71549
+ var import_node_child_process22 = require("child_process");
71453
71550
  init_protocol();
71454
71551
  var POOL_SETUP_SCRIPT_PATH = "/api/setup.sh";
71455
71552
  var POOL_SCRIPT_FETCH_TIMEOUT_MS = 5e3;
@@ -71475,7 +71572,7 @@ async function fetchScript(deps) {
71475
71572
  }
71476
71573
  function runScript(script, apiKey, deps) {
71477
71574
  return new Promise((resolve9, reject) => {
71478
- const proc = (0, import_node_child_process21.spawn)("bash", ["-s"], {
71575
+ const proc = (0, import_node_child_process22.spawn)("bash", ["-s"], {
71479
71576
  env: { ...deps.env ?? process.env, CPA_KEY: apiKey, CPA_MODE: "api" },
71480
71577
  stdio: ["pipe", "pipe", "pipe"]
71481
71578
  });
@@ -71520,7 +71617,7 @@ function runScript(script, apiKey, deps) {
71520
71617
  }
71521
71618
 
71522
71619
  // src/tools/claude-auth-status.ts
71523
- var import_node_child_process22 = require("child_process");
71620
+ var import_node_child_process23 = require("child_process");
71524
71621
  var str5 = (v3) => typeof v3 === "string" && v3 ? v3 : void 0;
71525
71622
  function mapClaudeAuthStatus(raw) {
71526
71623
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
@@ -71543,7 +71640,7 @@ function mapClaudeAuthStatus(raw) {
71543
71640
  };
71544
71641
  }
71545
71642
  async function queryClaudeAuthStatus(deps = {}) {
71546
- const run = deps.execFileImpl ?? import_node_child_process22.execFile;
71643
+ const run = deps.execFileImpl ?? import_node_child_process23.execFile;
71547
71644
  const env = deps.env ?? process.env;
71548
71645
  const bin = env.CLAUDE_BIN ?? "claude";
71549
71646
  const stdout = await new Promise((resolve9, reject) => {
@@ -71613,12 +71710,12 @@ async function queryCodexAccount(deps = {}) {
71613
71710
  }
71614
71711
 
71615
71712
  // src/tools/pi-account.ts
71616
- var import_node_fs62 = __toESM(require("fs"), 1);
71617
- var import_node_path68 = __toESM(require("path"), 1);
71713
+ var import_node_fs63 = __toESM(require("fs"), 1);
71714
+ var import_node_path70 = __toESM(require("path"), 1);
71618
71715
  function readPiCredentials(agentDir) {
71619
71716
  let raw;
71620
71717
  try {
71621
- raw = JSON.parse(import_node_fs62.default.readFileSync(import_node_path68.default.join(agentDir, "auth.json"), "utf8"));
71718
+ raw = JSON.parse(import_node_fs63.default.readFileSync(import_node_path70.default.join(agentDir, "auth.json"), "utf8"));
71622
71719
  } catch {
71623
71720
  return {};
71624
71721
  }
@@ -71701,15 +71798,10 @@ function buildEngineHandlers(deps, overrides = {}) {
71701
71798
  return { "engine:status": status, "engine:connectPool": connectPool };
71702
71799
  }
71703
71800
 
71704
- // src/handlers/channels.ts
71705
- var import_node_fs64 = __toESM(require("fs"), 1);
71706
- var import_node_os26 = __toESM(require("os"), 1);
71707
- var import_node_path70 = __toESM(require("path"), 1);
71708
-
71709
71801
  // src/tools/subscription-channels-apply.ts
71710
- var import_node_fs63 = __toESM(require("fs"), 1);
71802
+ var import_node_fs64 = __toESM(require("fs"), 1);
71711
71803
  var import_node_os25 = __toESM(require("os"), 1);
71712
- var import_node_path69 = __toESM(require("path"), 1);
71804
+ var import_node_path71 = __toESM(require("path"), 1);
71713
71805
  var TABLE_HEADER = /^\s*\[\[?\s*([A-Za-z0-9_.-]+)\s*\]\]?\s*(?:#.*)?$/;
71714
71806
  var KEY_LINE = /^\s*([A-Za-z0-9_.-]+)\s*=/;
71715
71807
  function stripCodexProvider(text) {
@@ -71737,10 +71829,10 @@ function stripCodexProvider(text) {
71737
71829
  return out.join("\n").trim();
71738
71830
  }
71739
71831
  function applyCodexSubscriptionChannel(opts) {
71740
- const codexHome = opts.codexHome || process.env.CODEX_HOME || import_node_path69.default.join(import_node_os25.default.homedir(), ".codex");
71741
- import_node_fs63.default.mkdirSync(codexHome, { recursive: true });
71742
- const configFile = import_node_path69.default.join(codexHome, "config.toml");
71743
- const original = import_node_fs63.default.existsSync(configFile) ? import_node_fs63.default.readFileSync(configFile, "utf8") : "";
71832
+ const codexHome = opts.codexHome || process.env.CODEX_HOME || import_node_path71.default.join(import_node_os25.default.homedir(), ".codex");
71833
+ import_node_fs64.default.mkdirSync(codexHome, { recursive: true });
71834
+ const configFile = import_node_path71.default.join(codexHome, "config.toml");
71835
+ const original = import_node_fs64.default.existsSync(configFile) ? import_node_fs64.default.readFileSync(configFile, "utf8") : "";
71744
71836
  const rest = stripCodexProvider(original);
71745
71837
  let content;
71746
71838
  if (opts.channel) {
@@ -71764,38 +71856,25 @@ function applyCodexSubscriptionChannel(opts) {
71764
71856
  ].join("\n");
71765
71857
  }
71766
71858
  const tmpFile = `${configFile}.tmp.${Date.now()}`;
71767
- import_node_fs63.default.writeFileSync(tmpFile, content, { mode: 384 });
71768
- import_node_fs63.default.renameSync(tmpFile, configFile);
71859
+ import_node_fs64.default.writeFileSync(tmpFile, content, { mode: 384 });
71860
+ import_node_fs64.default.renameSync(tmpFile, configFile);
71769
71861
  }
71770
71862
  function applyPiSubscriptionChannel(opts) {
71771
- const agentDir = opts.agentDir || process.env.PI_CODING_AGENT_DIR || import_node_path69.default.join(import_node_os25.default.homedir(), ".pi", "agent");
71772
- if (!import_node_fs63.default.existsSync(agentDir)) return false;
71773
- const configFile = import_node_path69.default.join(agentDir, "cliproxyapi.json");
71863
+ const agentDir = opts.agentDir || process.env.PI_CODING_AGENT_DIR || import_node_path71.default.join(import_node_os25.default.homedir(), ".pi", "agent");
71864
+ if (!import_node_fs64.default.existsSync(agentDir)) return false;
71865
+ const configFile = import_node_path71.default.join(agentDir, "cliproxyapi.json");
71774
71866
  if (opts.channel) {
71775
71867
  const data = { baseUrl: opts.channel.baseUrl, apiKey: opts.channel.apiKey };
71776
71868
  const tmp = `${configFile}.tmp.${Date.now()}`;
71777
- import_node_fs63.default.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
71778
- import_node_fs63.default.renameSync(tmp, configFile);
71869
+ import_node_fs64.default.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
71870
+ import_node_fs64.default.renameSync(tmp, configFile);
71779
71871
  return true;
71780
71872
  }
71781
71873
  return false;
71782
71874
  }
71783
71875
 
71784
71876
  // src/handlers/channels.ts
71785
- function snapshotFile(file) {
71786
- const original = import_node_fs64.default.existsSync(file) ? import_node_fs64.default.readFileSync(file) : null;
71787
- return () => {
71788
- if (original === null) {
71789
- import_node_fs64.default.rmSync(file, { force: true });
71790
- } else {
71791
- const tmp = `${file}.tmp.${Date.now()}`;
71792
- import_node_fs64.default.writeFileSync(tmp, original, { mode: 384 });
71793
- import_node_fs64.default.renameSync(tmp, file);
71794
- }
71795
- };
71796
- }
71797
71877
  function buildChannelsHandlers(deps) {
71798
- let activationQueue = Promise.resolve();
71799
71878
  const get = async () => {
71800
71879
  return { response: deps.store.list() };
71801
71880
  };
@@ -71824,37 +71903,13 @@ function buildChannelsHandlers(deps) {
71824
71903
  }
71825
71904
  return { response: { ok: true } };
71826
71905
  };
71827
- const activate = async (frame, context) => {
71828
- const run = activationQueue.then(() => activateChannel(frame, context));
71829
- activationQueue = run.then(() => {
71830
- }, () => {
71831
- });
71832
- return run;
71833
- };
71834
- const activateChannel = async (frame) => {
71906
+ const activate = async (frame) => {
71835
71907
  const args = parseArgs2(SubscriptionChannelActivateArgsSchema, frame, "subscriptionChannel:activate");
71836
- const channel = args.id === null ? null : deps.store.get(args.id);
71837
- if (args.id !== null && !channel) throw new Error(`\u901A\u9053 ${args.id} \u4E0D\u5B58\u5728`);
71838
- const codexHome = deps.codexHome || process.env.CODEX_HOME || import_node_path70.default.join(import_node_os26.default.homedir(), ".codex");
71839
- const agentDir = deps.agentDir || process.env.PI_CODING_AGENT_DIR || import_node_path70.default.join(import_node_os26.default.homedir(), ".pi", "agent");
71840
- const restoreCodex = snapshotFile(import_node_path70.default.join(codexHome, "config.toml"));
71841
- const restorePi = import_node_fs64.default.existsSync(agentDir) ? snapshotFile(import_node_path70.default.join(agentDir, "cliproxyapi.json")) : null;
71842
- let refreshed = false;
71843
- try {
71844
- applyCodexSubscriptionChannel({ codexHome, channel });
71845
- if (channel) await deps.manager.getCapabilities("codex", { refresh: true });
71846
- refreshed = Boolean(channel);
71847
- applyPiSubscriptionChannel({ agentDir, channel });
71848
- deps.store.setActive(args.id);
71849
- } catch (err) {
71850
- restoreCodex();
71851
- restorePi?.();
71852
- if (refreshed) await deps.manager.getCapabilities("codex", { refresh: true }).catch(() => {
71853
- });
71854
- if (channel?.id === "default-pool" && err instanceof Error && /HTTP 401/.test(err.message)) {
71855
- throw new Error("\u9ED8\u8BA4\u53F7\u6C60\u8BA4\u8BC1\u5931\u8D25\uFF08HTTP 401\uFF09\uFF1A\u8BF7\u7F16\u8F91\u300C\u9ED8\u8BA4\u56E2\u961F\u53F7\u6C60\u300D\u5E76\u91CD\u65B0\u586B\u5199\u53F7\u6C60 API Key\uFF0C\u518D\u8BBE\u4E3A\u5F53\u524D\u3002");
71856
- }
71857
- throw err;
71908
+ const channel = deps.store.setActive(args.id);
71909
+ applyCodexSubscriptionChannel({ codexHome: deps.codexHome, channel });
71910
+ applyPiSubscriptionChannel({ agentDir: deps.agentDir, channel });
71911
+ if (channel) {
71912
+ await deps.manager.getCapabilities("codex", { refresh: true });
71858
71913
  }
71859
71914
  return { response: { ok: true } };
71860
71915
  };
@@ -72500,7 +72555,7 @@ function buildDashboardHandlers(deps) {
72500
72555
  }
72501
72556
 
72502
72557
  // src/handlers/meta.ts
72503
- var import_node_os27 = __toESM(require("os"), 1);
72558
+ var import_node_os26 = __toESM(require("os"), 1);
72504
72559
  init_protocol();
72505
72560
 
72506
72561
  // src/handlers/method-grants.ts
@@ -72820,21 +72875,21 @@ function computeMethodAccess(args) {
72820
72875
  }
72821
72876
 
72822
72877
  // src/version.ts
72823
- var version = "0.2.593".length > 0 ? "0.2.593" : "dev";
72878
+ var version = "0.2.595".length > 0 ? "0.2.595" : "dev";
72824
72879
 
72825
72880
  // src/cli-probe/probe.ts
72826
72881
  var fs83 = __toESM(require("fs"), 1);
72827
- var path89 = __toESM(require("path"), 1);
72882
+ var path90 = __toESM(require("path"), 1);
72828
72883
  var PKG_NAME = "@clawos-dev/clawd";
72829
72884
  var BIN_NAME = "clawd";
72830
72885
  var MAX_PARENT_HOPS = 5;
72831
72886
  function probeGlobalCli(pathEnv) {
72832
72887
  if (!pathEnv) return null;
72833
- for (const dir of pathEnv.split(path89.delimiter)) {
72888
+ for (const dir of pathEnv.split(path90.delimiter)) {
72834
72889
  if (!dir) continue;
72835
72890
  let real;
72836
72891
  try {
72837
- real = fs83.realpathSync(path89.join(dir, BIN_NAME));
72892
+ real = fs83.realpathSync(path90.join(dir, BIN_NAME));
72838
72893
  } catch {
72839
72894
  continue;
72840
72895
  }
@@ -72844,15 +72899,15 @@ function probeGlobalCli(pathEnv) {
72844
72899
  return null;
72845
72900
  }
72846
72901
  function readOwnPackageVersion(entryPath) {
72847
- let dir = path89.dirname(entryPath);
72902
+ let dir = path90.dirname(entryPath);
72848
72903
  for (let i2 = 0; i2 < MAX_PARENT_HOPS; i2++) {
72849
72904
  try {
72850
- const raw = fs83.readFileSync(path89.join(dir, "package.json"), "utf8");
72905
+ const raw = fs83.readFileSync(path90.join(dir, "package.json"), "utf8");
72851
72906
  const pkg = JSON.parse(raw);
72852
72907
  if (pkg.name !== PKG_NAME) return null;
72853
72908
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
72854
72909
  } catch {
72855
- const parent = path89.dirname(dir);
72910
+ const parent = path90.dirname(dir);
72856
72911
  if (parent === dir) return null;
72857
72912
  dir = parent;
72858
72913
  }
@@ -72888,7 +72943,7 @@ function buildReadyFrame(deps, client) {
72888
72943
  return {
72889
72944
  version,
72890
72945
  protocolVersion: PROTOCOL_VERSION,
72891
- hostname: import_node_os27.default.hostname(),
72946
+ hostname: import_node_os26.default.hostname(),
72892
72947
  os: process.platform,
72893
72948
  tools,
72894
72949
  runningSessions: info.runningSessions,
@@ -73028,7 +73083,7 @@ function buildPersonaHandlers(deps) {
73028
73083
  }
73029
73084
 
73030
73085
  // src/handlers/attachment.ts
73031
- var import_node_path71 = __toESM(require("path"), 1);
73086
+ var import_node_path72 = __toESM(require("path"), 1);
73032
73087
  init_protocol();
73033
73088
  init_protocol();
73034
73089
  function assertGuestAttachmentPath(ctx, absPath, personaRoot, method, usersRoot) {
@@ -73107,12 +73162,12 @@ function buildAttachmentHandlers(deps) {
73107
73162
  `session ${args.sessionId} scope unresolved`
73108
73163
  );
73109
73164
  }
73110
- const cwdAbs = import_node_path71.default.resolve(sessionFile.cwd);
73111
- const candidateAbs = import_node_path71.default.isAbsolute(args.relPath) ? import_node_path71.default.resolve(args.relPath) : import_node_path71.default.resolve(cwdAbs, args.relPath);
73165
+ const cwdAbs = import_node_path72.default.resolve(sessionFile.cwd);
73166
+ const candidateAbs = import_node_path72.default.isAbsolute(args.relPath) ? import_node_path72.default.resolve(args.relPath) : import_node_path72.default.resolve(cwdAbs, args.relPath);
73112
73167
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
73113
73168
  const entries = deps.groupFileStore.list(scope, args.sessionId);
73114
73169
  const entry = entries.find((e) => {
73115
- const storedAbs = import_node_path71.default.isAbsolute(e.relPath) ? import_node_path71.default.resolve(e.relPath) : import_node_path71.default.resolve(cwdAbs, e.relPath);
73170
+ const storedAbs = import_node_path72.default.isAbsolute(e.relPath) ? import_node_path72.default.resolve(e.relPath) : import_node_path72.default.resolve(cwdAbs, e.relPath);
73116
73171
  return storedAbs === candidateAbs && !e.stale;
73117
73172
  });
73118
73173
  if (!entry) {
@@ -73136,7 +73191,7 @@ function buildAttachmentHandlers(deps) {
73136
73191
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
73137
73192
  const f2 = deps.sessionStore.read(sessionId);
73138
73193
  if (!f2) return;
73139
- assertGuestAttachmentPath(ctx, import_node_path71.default.resolve(f2.cwd), deps.personaRoot, method, deps.usersRoot);
73194
+ assertGuestAttachmentPath(ctx, import_node_path72.default.resolve(f2.cwd), deps.personaRoot, method, deps.usersRoot);
73140
73195
  }
73141
73196
  const groupAdd = async (frame, _client, ctx) => {
73142
73197
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -73151,8 +73206,8 @@ function buildAttachmentHandlers(deps) {
73151
73206
  if (!scope) {
73152
73207
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
73153
73208
  }
73154
- const cwdAbs = import_node_path71.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
73155
- const candidateAbs = import_node_path71.default.isAbsolute(args.relPath) ? import_node_path71.default.resolve(args.relPath) : import_node_path71.default.resolve(cwdAbs, args.relPath);
73209
+ const cwdAbs = import_node_path72.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
73210
+ const candidateAbs = import_node_path72.default.isAbsolute(args.relPath) ? import_node_path72.default.resolve(args.relPath) : import_node_path72.default.resolve(cwdAbs, args.relPath);
73156
73211
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
73157
73212
  const from = args.origin === "agent" ? "agent" : ctx?.principal.kind === "owner" ? "owner" : "agent";
73158
73213
  const size = 0;
@@ -73211,19 +73266,19 @@ function buildAttachmentHandlers(deps) {
73211
73266
 
73212
73267
  // src/handlers/extension.ts
73213
73268
  var import_promises15 = __toESM(require("fs/promises"), 1);
73214
- var import_node_path76 = __toESM(require("path"), 1);
73269
+ var import_node_path77 = __toESM(require("path"), 1);
73215
73270
  init_protocol();
73216
73271
 
73217
73272
  // src/extension/bundle-zip.ts
73218
73273
  var import_promises12 = __toESM(require("fs/promises"), 1);
73219
- var import_node_path72 = __toESM(require("path"), 1);
73274
+ var import_node_path73 = __toESM(require("path"), 1);
73220
73275
  var import_node_crypto22 = __toESM(require("crypto"), 1);
73221
73276
  var import_jszip2 = __toESM(require_lib3(), 1);
73222
73277
  async function bundleExtensionDir(dir) {
73223
73278
  const entries = await listFilesSorted(dir);
73224
73279
  const zip = new import_jszip2.default();
73225
73280
  for (const rel of entries) {
73226
- const abs = import_node_path72.default.join(dir, rel);
73281
+ const abs = import_node_path73.default.join(dir, rel);
73227
73282
  const content = await import_promises12.default.readFile(abs);
73228
73283
  zip.file(rel, content, { date: FIXED_DATE });
73229
73284
  }
@@ -73244,7 +73299,7 @@ async function listFilesSorted(rootDir) {
73244
73299
  return out;
73245
73300
  }
73246
73301
  async function walk(absRoot, relPrefix, out) {
73247
- const dirAbs = import_node_path72.default.join(absRoot, relPrefix);
73302
+ const dirAbs = import_node_path73.default.join(absRoot, relPrefix);
73248
73303
  const entries = await import_promises12.default.readdir(dirAbs, { withFileTypes: true });
73249
73304
  for (const e of entries) {
73250
73305
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -73298,25 +73353,25 @@ function computePublishCheck(args) {
73298
73353
 
73299
73354
  // src/extension/install-flow.ts
73300
73355
  var import_promises13 = __toESM(require("fs/promises"), 1);
73301
- var import_node_path74 = __toESM(require("path"), 1);
73302
- var import_node_os29 = __toESM(require("os"), 1);
73356
+ var import_node_path75 = __toESM(require("path"), 1);
73357
+ var import_node_os28 = __toESM(require("os"), 1);
73303
73358
  var import_node_crypto23 = __toESM(require("crypto"), 1);
73304
73359
  var import_jszip3 = __toESM(require_lib3(), 1);
73305
73360
 
73306
73361
  // src/extension/paths.ts
73307
- var import_node_os28 = __toESM(require("os"), 1);
73308
- var import_node_path73 = __toESM(require("path"), 1);
73362
+ var import_node_os27 = __toESM(require("os"), 1);
73363
+ var import_node_path74 = __toESM(require("path"), 1);
73309
73364
  function clawdHomeRoot(override) {
73310
- return override ?? process.env.CLAWD_HOME ?? import_node_path73.default.join(import_node_os28.default.homedir(), ".clawd");
73365
+ return override ?? process.env.CLAWD_HOME ?? import_node_path74.default.join(import_node_os27.default.homedir(), ".clawd");
73311
73366
  }
73312
73367
  function extensionsRoot(override) {
73313
- return import_node_path73.default.join(clawdHomeRoot(override), "extensions");
73368
+ return import_node_path74.default.join(clawdHomeRoot(override), "extensions");
73314
73369
  }
73315
73370
  function publishedChannelsFile(override) {
73316
- return import_node_path73.default.join(clawdHomeRoot(override), "extensions-published.json");
73371
+ return import_node_path74.default.join(clawdHomeRoot(override), "extensions-published.json");
73317
73372
  }
73318
73373
  function bundleCacheRoot(override) {
73319
- return import_node_path73.default.join(clawdHomeRoot(override), "extension-bundles");
73374
+ return import_node_path74.default.join(clawdHomeRoot(override), "extension-bundles");
73320
73375
  }
73321
73376
 
73322
73377
  // src/extension/install-flow.ts
@@ -73343,7 +73398,7 @@ async function installFromChannel(args, deps) {
73343
73398
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
73344
73399
  }
73345
73400
  for (const name of Object.keys(zip.files)) {
73346
- if (name.includes("..") || name.startsWith("/") || import_node_path74.default.isAbsolute(name)) {
73401
+ if (name.includes("..") || name.startsWith("/") || import_node_path75.default.isAbsolute(name)) {
73347
73402
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
73348
73403
  }
73349
73404
  }
@@ -73375,7 +73430,7 @@ async function installFromChannel(args, deps) {
73375
73430
  );
73376
73431
  }
73377
73432
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
73378
- const destDir = import_node_path74.default.join(deps.extensionsRoot, localExtId);
73433
+ const destDir = import_node_path75.default.join(deps.extensionsRoot, localExtId);
73379
73434
  let destExists = false;
73380
73435
  try {
73381
73436
  await import_promises13.default.access(destDir);
@@ -73389,16 +73444,16 @@ async function installFromChannel(args, deps) {
73389
73444
  );
73390
73445
  }
73391
73446
  const stage = await import_promises13.default.mkdtemp(
73392
- import_node_path74.default.join(import_node_os29.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
73447
+ import_node_path75.default.join(import_node_os28.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
73393
73448
  );
73394
73449
  try {
73395
73450
  for (const [name, entry] of Object.entries(zip.files)) {
73396
- const dest = import_node_path74.default.join(stage, name);
73451
+ const dest = import_node_path75.default.join(stage, name);
73397
73452
  if (entry.dir) {
73398
73453
  await import_promises13.default.mkdir(dest, { recursive: true });
73399
73454
  continue;
73400
73455
  }
73401
- await import_promises13.default.mkdir(import_node_path74.default.dirname(dest), { recursive: true });
73456
+ await import_promises13.default.mkdir(import_node_path75.default.dirname(dest), { recursive: true });
73402
73457
  if (name === "manifest.json") {
73403
73458
  const rewritten = { ...parsed.data, id: localExtId };
73404
73459
  await import_promises13.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -73419,8 +73474,8 @@ async function installFromChannel(args, deps) {
73419
73474
 
73420
73475
  // src/extension/update-flow.ts
73421
73476
  var import_promises14 = __toESM(require("fs/promises"), 1);
73422
- var import_node_path75 = __toESM(require("path"), 1);
73423
- var import_node_os30 = __toESM(require("os"), 1);
73477
+ var import_node_path76 = __toESM(require("path"), 1);
73478
+ var import_node_os29 = __toESM(require("os"), 1);
73424
73479
  var import_node_crypto24 = __toESM(require("crypto"), 1);
73425
73480
  var import_jszip4 = __toESM(require_lib3(), 1);
73426
73481
  var UpdateError = class extends Error {
@@ -73436,11 +73491,11 @@ async function updateFromChannel(args, deps) {
73436
73491
  channelRef.extId,
73437
73492
  channelRef.ownerPrincipalId
73438
73493
  );
73439
- const liveDir = import_node_path75.default.join(deps.extensionsRoot, localExtId);
73494
+ const liveDir = import_node_path76.default.join(deps.extensionsRoot, localExtId);
73440
73495
  const prevDir = `${liveDir}.prev`;
73441
73496
  let existingVersion;
73442
73497
  try {
73443
- const raw = await import_promises14.default.readFile(import_node_path75.default.join(liveDir, "manifest.json"), "utf8");
73498
+ const raw = await import_promises14.default.readFile(import_node_path76.default.join(liveDir, "manifest.json"), "utf8");
73444
73499
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
73445
73500
  if (!parsed2.success) {
73446
73501
  throw new UpdateError(
@@ -73473,7 +73528,7 @@ async function updateFromChannel(args, deps) {
73473
73528
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
73474
73529
  }
73475
73530
  for (const name of Object.keys(zip.files)) {
73476
- if (name.includes("..") || name.startsWith("/") || import_node_path75.default.isAbsolute(name)) {
73531
+ if (name.includes("..") || name.startsWith("/") || import_node_path76.default.isAbsolute(name)) {
73477
73532
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
73478
73533
  }
73479
73534
  }
@@ -73508,16 +73563,16 @@ async function updateFromChannel(args, deps) {
73508
73563
  await import_promises14.default.rm(prevDir, { recursive: true, force: true });
73509
73564
  await import_promises14.default.rename(liveDir, prevDir);
73510
73565
  const stage = await import_promises14.default.mkdtemp(
73511
- import_node_path75.default.join(import_node_os30.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
73566
+ import_node_path76.default.join(import_node_os29.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
73512
73567
  );
73513
73568
  try {
73514
73569
  for (const [name, entry] of Object.entries(zip.files)) {
73515
- const dest = import_node_path75.default.join(stage, name);
73570
+ const dest = import_node_path76.default.join(stage, name);
73516
73571
  if (entry.dir) {
73517
73572
  await import_promises14.default.mkdir(dest, { recursive: true });
73518
73573
  continue;
73519
73574
  }
73520
- await import_promises14.default.mkdir(import_node_path75.default.dirname(dest), { recursive: true });
73575
+ await import_promises14.default.mkdir(import_node_path76.default.dirname(dest), { recursive: true });
73521
73576
  if (name === "manifest.json") {
73522
73577
  const rewritten = { ...parsed.data, id: localExtId };
73523
73578
  await import_promises14.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -73584,7 +73639,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
73584
73639
  );
73585
73640
  }
73586
73641
  }
73587
- const manifestPath = import_node_path76.default.join(root, extId, "manifest.json");
73642
+ const manifestPath = import_node_path77.default.join(root, extId, "manifest.json");
73588
73643
  const manifest = await readManifest(root, extId);
73589
73644
  const next = { ...manifest, version: newVersion };
73590
73645
  const tmp = `${manifestPath}.tmp`;
@@ -73592,7 +73647,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
73592
73647
  await import_promises15.default.rename(tmp, manifestPath);
73593
73648
  }
73594
73649
  async function readManifest(root, extId) {
73595
- const file = import_node_path76.default.join(root, extId, "manifest.json");
73650
+ const file = import_node_path77.default.join(root, extId, "manifest.json");
73596
73651
  let raw;
73597
73652
  try {
73598
73653
  raw = await import_promises15.default.readFile(file, "utf8");
@@ -73676,7 +73731,7 @@ function buildExtensionHandlers(deps) {
73676
73731
  };
73677
73732
  async function buildSnapshotMeta(extId) {
73678
73733
  const manifest = await readManifest(deps.root, extId);
73679
- const { sha256, buffer } = await bundleExtensionDir(import_node_path76.default.join(deps.root, extId));
73734
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path77.default.join(deps.root, extId));
73680
73735
  return { manifest, contentHash: sha256, buffer };
73681
73736
  }
73682
73737
  const publish = async (frame, _client, ctx) => {
@@ -74504,7 +74559,7 @@ function buildVisitorHandlers(deps) {
74504
74559
 
74505
74560
  // src/extension/registry.ts
74506
74561
  var import_promises17 = __toESM(require("fs/promises"), 1);
74507
- var import_node_path77 = __toESM(require("path"), 1);
74562
+ var import_node_path78 = __toESM(require("path"), 1);
74508
74563
  async function loadAll(root) {
74509
74564
  let entries;
74510
74565
  try {
@@ -74517,13 +74572,13 @@ async function loadAll(root) {
74517
74572
  for (const ent of entries) {
74518
74573
  if (!ent.isDirectory()) continue;
74519
74574
  if (ent.name.startsWith(".")) continue;
74520
- records.push(await loadOne(import_node_path77.default.join(root, ent.name), ent.name));
74575
+ records.push(await loadOne(import_node_path78.default.join(root, ent.name), ent.name));
74521
74576
  }
74522
74577
  records.sort((a2, b3) => a2.extId < b3.extId ? -1 : a2.extId > b3.extId ? 1 : 0);
74523
74578
  return records;
74524
74579
  }
74525
74580
  async function loadOne(dir, dirName) {
74526
- const manifestPath = import_node_path77.default.join(dir, "manifest.json");
74581
+ const manifestPath = import_node_path78.default.join(dir, "manifest.json");
74527
74582
  let raw;
74528
74583
  try {
74529
74584
  raw = await import_promises17.default.readFile(manifestPath, "utf8");
@@ -74568,7 +74623,7 @@ async function loadOne(dir, dirName) {
74568
74623
 
74569
74624
  // src/extension/uninstall.ts
74570
74625
  var import_promises18 = __toESM(require("fs/promises"), 1);
74571
- var import_node_path78 = __toESM(require("path"), 1);
74626
+ var import_node_path79 = __toESM(require("path"), 1);
74572
74627
  var UninstallError = class extends Error {
74573
74628
  constructor(code, message) {
74574
74629
  super(message);
@@ -74577,7 +74632,7 @@ var UninstallError = class extends Error {
74577
74632
  code;
74578
74633
  };
74579
74634
  async function uninstall(deps) {
74580
- const dir = import_node_path78.default.join(deps.root, deps.extId);
74635
+ const dir = import_node_path79.default.join(deps.root, deps.extId);
74581
74636
  try {
74582
74637
  await import_promises18.default.access(dir);
74583
74638
  } catch {
@@ -74594,15 +74649,15 @@ var import_node_crypto25 = require("crypto");
74594
74649
  init_protocol();
74595
74650
 
74596
74651
  // src/peer-ops/run-command.ts
74597
- var import_node_child_process23 = require("child_process");
74598
- var import_node_os31 = __toESM(require("os"), 1);
74652
+ var import_node_child_process24 = require("child_process");
74653
+ var import_node_os30 = __toESM(require("os"), 1);
74599
74654
  var DEFAULT_TIMEOUT_MS6 = 3e4;
74600
74655
  var MAX_TIMEOUT_MS = 3e5;
74601
74656
  var MAX_OUTPUT_BYTES = 1048576;
74602
74657
  function runCommand2(command, opts = {}) {
74603
74658
  const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS6, MAX_TIMEOUT_MS);
74604
74659
  return new Promise((resolve9) => {
74605
- const child = (0, import_node_child_process23.spawn)("bash", ["-c", command], { cwd: import_node_os31.default.homedir() });
74660
+ const child = (0, import_node_child_process24.spawn)("bash", ["-c", command], { cwd: import_node_os30.default.homedir() });
74606
74661
  const out = [];
74607
74662
  const err = [];
74608
74663
  let outBytes = 0;
@@ -74795,7 +74850,7 @@ function buildMethodHandlers(deps) {
74795
74850
  broadcast: deps.broadcastToOwners
74796
74851
  }),
74797
74852
  ...buildMasterHandlers({
74798
- authorityTablePath: import_node_path79.default.join(deps.masterDataDir, "master-authority.json")
74853
+ authorityTablePath: import_node_path80.default.join(deps.masterDataDir, "master-authority.json")
74799
74854
  }),
74800
74855
  ...buildTopicHandlers({
74801
74856
  topicStore: deps.topicStore,
@@ -74937,8 +74992,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
74937
74992
  }
74938
74993
 
74939
74994
  // src/extension/runtime.ts
74940
- var import_node_child_process24 = require("child_process");
74941
- var import_node_path80 = __toESM(require("path"), 1);
74995
+ var import_node_child_process25 = require("child_process");
74996
+ var import_node_path81 = __toESM(require("path"), 1);
74942
74997
  var import_promises19 = require("timers/promises");
74943
74998
 
74944
74999
  // src/extension/port-allocator.ts
@@ -75039,13 +75094,13 @@ var Runtime = class {
75039
75094
  /\$CLAWOS_EXT_PORT/g,
75040
75095
  String(port)
75041
75096
  );
75042
- const dir = import_node_path80.default.join(this.root, extId);
75097
+ const dir = import_node_path81.default.join(this.root, extId);
75043
75098
  const env = {
75044
75099
  ...process.env,
75045
75100
  CLAWOS_EXT_PORT: String(port),
75046
75101
  CLAWOS_EXT_ID: extId
75047
75102
  };
75048
- const child = (0, import_node_child_process24.spawn)("sh", ["-c", cmd], {
75103
+ const child = (0, import_node_child_process25.spawn)("sh", ["-c", cmd], {
75049
75104
  cwd: dir,
75050
75105
  env,
75051
75106
  stdio: ["ignore", "pipe", "pipe"],
@@ -75151,7 +75206,7 @@ ${handle.stderrTail}`
75151
75206
 
75152
75207
  // src/extension/published-channels.ts
75153
75208
  var import_promises20 = __toESM(require("fs/promises"), 1);
75154
- var import_node_path81 = __toESM(require("path"), 1);
75209
+ var import_node_path82 = __toESM(require("path"), 1);
75155
75210
  init_zod();
75156
75211
  var PublishedChannelsError = class extends Error {
75157
75212
  constructor(code, message) {
@@ -75250,7 +75305,7 @@ var PublishedChannelStore = class {
75250
75305
  )
75251
75306
  };
75252
75307
  const tmp = `${this.filePath}.tmp`;
75253
- await import_promises20.default.mkdir(import_node_path81.default.dirname(this.filePath), { recursive: true });
75308
+ await import_promises20.default.mkdir(import_node_path82.default.dirname(this.filePath), { recursive: true });
75254
75309
  await import_promises20.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
75255
75310
  await import_promises20.default.rename(tmp, this.filePath);
75256
75311
  }
@@ -75258,7 +75313,7 @@ var PublishedChannelStore = class {
75258
75313
 
75259
75314
  // src/extension/bundle-cache.ts
75260
75315
  var import_promises21 = __toESM(require("fs/promises"), 1);
75261
- var import_node_path82 = __toESM(require("path"), 1);
75316
+ var import_node_path83 = __toESM(require("path"), 1);
75262
75317
  var BundleCache = class {
75263
75318
  constructor(rootDir) {
75264
75319
  this.rootDir = rootDir;
@@ -75267,14 +75322,14 @@ var BundleCache = class {
75267
75322
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
75268
75323
  async write(snapshotHash, buffer) {
75269
75324
  await import_promises21.default.mkdir(this.rootDir, { recursive: true });
75270
- const file = import_node_path82.default.join(this.rootDir, `${snapshotHash}.zip`);
75325
+ const file = import_node_path83.default.join(this.rootDir, `${snapshotHash}.zip`);
75271
75326
  const tmp = `${file}.tmp`;
75272
75327
  await import_promises21.default.writeFile(tmp, buffer, { mode: 384 });
75273
75328
  await import_promises21.default.rename(tmp, file);
75274
75329
  }
75275
75330
  /** Returns the bundle bytes, or null when the file doesn't exist. */
75276
75331
  async read(snapshotHash) {
75277
- const file = import_node_path82.default.join(this.rootDir, `${snapshotHash}.zip`);
75332
+ const file = import_node_path83.default.join(this.rootDir, `${snapshotHash}.zip`);
75278
75333
  try {
75279
75334
  return await import_promises21.default.readFile(file);
75280
75335
  } catch (e) {
@@ -75284,7 +75339,7 @@ var BundleCache = class {
75284
75339
  }
75285
75340
  /** Idempotent — missing file is not an error. */
75286
75341
  async delete(snapshotHash) {
75287
- const file = import_node_path82.default.join(this.rootDir, `${snapshotHash}.zip`);
75342
+ const file = import_node_path83.default.join(this.rootDir, `${snapshotHash}.zip`);
75288
75343
  await import_promises21.default.rm(file, { force: true });
75289
75344
  }
75290
75345
  };
@@ -75305,26 +75360,26 @@ async function startDaemon(config) {
75305
75360
  },
75306
75361
  source: "daemon",
75307
75362
  sampling: logShippingCfg.sampling,
75308
- homeDir: import_node_os32.default.homedir()
75363
+ homeDir: import_node_os31.default.homedir()
75309
75364
  });
75310
- const logDir = import_node_path83.default.join(config.dataDir, "log");
75365
+ const logDir = import_node_path84.default.join(config.dataDir, "log");
75311
75366
  import_node_fs65.default.mkdirSync(logDir, { recursive: true });
75312
75367
  const logger = createLogger({
75313
75368
  level: config.logLevel,
75314
- file: import_node_path83.default.join(logDir, "clawd.log"),
75369
+ file: import_node_path84.default.join(logDir, "clawd.log"),
75315
75370
  logClient
75316
75371
  });
75317
75372
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
75318
75373
  const screenIdleProbeLogger = createFileOnlyLogger({
75319
- file: import_node_path83.default.join(logDir, "screen-idle-probe.log"),
75374
+ file: import_node_path84.default.join(logDir, "screen-idle-probe.log"),
75320
75375
  level: "debug"
75321
75376
  });
75322
75377
  logger.info("screen-idle probe logger enabled", {
75323
- file: import_node_path83.default.join(logDir, "screen-idle-probe.log")
75378
+ file: import_node_path84.default.join(logDir, "screen-idle-probe.log")
75324
75379
  });
75325
75380
  const CAP_TARGETS = [
75326
- import_node_path83.default.join(logDir, "clawd.log"),
75327
- import_node_path83.default.join(logDir, "screen-idle-probe.log")
75381
+ import_node_path84.default.join(logDir, "clawd.log"),
75382
+ import_node_path84.default.join(logDir, "screen-idle-probe.log")
75328
75383
  ];
75329
75384
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
75330
75385
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -75446,9 +75501,9 @@ async function startDaemon(config) {
75446
75501
  const agents = new AgentsScanner();
75447
75502
  const history = new ClaudeHistoryReader();
75448
75503
  let transport = null;
75449
- const personaStore = new PersonaStore(import_node_path83.default.join(config.dataDir, "personas"));
75504
+ const personaStore = new PersonaStore(import_node_path84.default.join(config.dataDir, "personas"));
75450
75505
  const subscriptionChannelStore = new SubscriptionChannelStore({ dataDir: config.dataDir });
75451
- const usersRoot = import_node_path83.default.join(config.dataDir, "users");
75506
+ const usersRoot = import_node_path84.default.join(config.dataDir, "users");
75452
75507
  const defaultsRoot = findDefaultsRoot(logger);
75453
75508
  if (defaultsRoot) {
75454
75509
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -75465,7 +75520,7 @@ async function startDaemon(config) {
75465
75520
  retirePersonaBuilder({ store: personaStore, logger });
75466
75521
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
75467
75522
  const dispatchStore = createDispatchStore({
75468
- filePath: import_node_path83.default.join(config.dataDir, "dispatch.json"),
75523
+ filePath: import_node_path84.default.join(config.dataDir, "dispatch.json"),
75469
75524
  now: () => Date.now(),
75470
75525
  onFlushError: (err) => logger.error("dispatch store flush failed", {
75471
75526
  reason: err instanceof Error ? err.message : String(err)
@@ -75474,7 +75529,7 @@ async function startDaemon(config) {
75474
75529
  await dispatchStore.load();
75475
75530
  dispatchStore.markInterruptedAsFailed((/* @__PURE__ */ new Date()).toISOString());
75476
75531
  const channelStore = createChannelStore({
75477
- filePath: import_node_path83.default.join(config.dataDir, "channels.json"),
75532
+ filePath: import_node_path84.default.join(config.dataDir, "channels.json"),
75478
75533
  now: () => Date.now(),
75479
75534
  genId: () => `ch-${(0, import_node_crypto26.randomUUID)().slice(0, 8)}`,
75480
75535
  onFlushError: (err) => logger.error("channel store flush failed", {
@@ -75483,7 +75538,7 @@ async function startDaemon(config) {
75483
75538
  });
75484
75539
  await channelStore.load();
75485
75540
  const topicStore = createTopicStore({
75486
- filePath: import_node_path83.default.join(config.dataDir, "topics.json"),
75541
+ filePath: import_node_path84.default.join(config.dataDir, "topics.json"),
75487
75542
  now: () => Date.now(),
75488
75543
  genId: () => `tp-${(0, import_node_crypto26.randomUUID)().slice(0, 8)}`,
75489
75544
  onFlushError: (err) => logger.error("topic store flush failed", {
@@ -75510,12 +75565,12 @@ async function startDaemon(config) {
75510
75565
  });
75511
75566
  }
75512
75567
  });
75513
- const here = typeof __dirname === "string" ? __dirname : import_node_path83.default.dirname((0, import_node_url6.fileURLToPath)(import_meta10.url));
75568
+ const here = typeof __dirname === "string" ? __dirname : import_node_path84.default.dirname((0, import_node_url6.fileURLToPath)(import_meta10.url));
75514
75569
  const mcpConfigs = [];
75515
75570
  const secretsBundleRoot = findSecretsBundleRoot(logger);
75516
- const localSecretsDir = import_node_path83.default.join(config.dataDir, LOCAL_SECRETS_DIRNAME);
75571
+ const localSecretsDir = import_node_path84.default.join(config.dataDir, LOCAL_SECRETS_DIRNAME);
75517
75572
  const readSecrets = () => readSecretTable({ bundleRoot: secretsBundleRoot, overrideDir: localSecretsDir, logger });
75518
- const githubTokenFile = import_node_path83.default.join(localSecretsDir, GITHUB_TOKEN_FILE);
75573
+ const githubTokenFile = import_node_path84.default.join(localSecretsDir, GITHUB_TOKEN_FILE);
75519
75574
  const githubIdentity = createGithubIdentity({
75520
75575
  resolve: () => resolveGithubAuth({ exec: runCommand, fileToken: () => readEnvFileVar(githubTokenFile, GITHUB_TOKEN_VAR) }),
75521
75576
  store: (token) => storeGithubAuth({
@@ -75553,22 +75608,22 @@ async function startDaemon(config) {
75553
75608
  void githubIdentity.refresh();
75554
75609
  return renderPersonaMcpSecrets({
75555
75610
  personaId: personaId2,
75556
- personaDir: import_node_path83.default.join(config.dataDir, "personas", personaId2),
75611
+ personaDir: import_node_path84.default.join(config.dataDir, "personas", personaId2),
75557
75612
  secrets,
75558
- outDir: import_node_path83.default.join(config.dataDir, "persona-mcp"),
75613
+ outDir: import_node_path84.default.join(config.dataDir, "persona-mcp"),
75559
75614
  logger
75560
75615
  });
75561
75616
  };
75562
75617
  const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
75563
75618
  const dispatchServerCandidates = [
75564
- import_node_path83.default.join(here, "dispatch", "mcp-server.cjs"),
75619
+ import_node_path84.default.join(here, "dispatch", "mcp-server.cjs"),
75565
75620
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
75566
- import_node_path83.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
75621
+ import_node_path84.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
75567
75622
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
75568
75623
  ];
75569
75624
  const dispatchServerScriptPath = dispatchServerCandidates.find((p3) => import_node_fs65.default.existsSync(p3));
75570
75625
  if (dispatchServerScriptPath) {
75571
- const dispatchLogPath = import_node_path83.default.join(logDir, "dispatch-mcp-server.log");
75626
+ const dispatchLogPath = import_node_path84.default.join(logDir, "dispatch-mcp-server.log");
75572
75627
  const dispatchCfgPath = writeDispatchMcpConfig({
75573
75628
  dataDir: config.dataDir,
75574
75629
  serverScriptPath: dispatchServerScriptPath,
@@ -75588,12 +75643,12 @@ async function startDaemon(config) {
75588
75643
  });
75589
75644
  }
75590
75645
  const ticketServerCandidates = [
75591
- import_node_path83.default.join(here, "ticket", "mcp-server.cjs"),
75592
- import_node_path83.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
75646
+ import_node_path84.default.join(here, "ticket", "mcp-server.cjs"),
75647
+ import_node_path84.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
75593
75648
  ];
75594
75649
  const ticketServerScriptPath = ticketServerCandidates.find((p3) => import_node_fs65.default.existsSync(p3));
75595
75650
  if (ticketServerScriptPath) {
75596
- const ticketLogPath = import_node_path83.default.join(logDir, "ticket-mcp-server.log");
75651
+ const ticketLogPath = import_node_path84.default.join(logDir, "ticket-mcp-server.log");
75597
75652
  const ticketCfgPath = writeTicketMcpConfig({
75598
75653
  dataDir: config.dataDir,
75599
75654
  serverScriptPath: ticketServerScriptPath,
@@ -75615,12 +75670,12 @@ async function startDaemon(config) {
75615
75670
  });
75616
75671
  }
75617
75672
  const shiftServerCandidates = [
75618
- import_node_path83.default.join(here, "shift", "mcp-server.cjs"),
75619
- import_node_path83.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
75673
+ import_node_path84.default.join(here, "shift", "mcp-server.cjs"),
75674
+ import_node_path84.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
75620
75675
  ];
75621
75676
  const shiftServerScriptPath = shiftServerCandidates.find((p3) => import_node_fs65.default.existsSync(p3));
75622
75677
  if (shiftServerScriptPath) {
75623
- const shiftLogPath = import_node_path83.default.join(logDir, "shift-mcp-server.log");
75678
+ const shiftLogPath = import_node_path84.default.join(logDir, "shift-mcp-server.log");
75624
75679
  const shiftCfgPath = await writeShiftMcpConfig({
75625
75680
  dataDir: config.dataDir,
75626
75681
  serverScriptPath: shiftServerScriptPath,
@@ -75641,12 +75696,12 @@ async function startDaemon(config) {
75641
75696
  );
75642
75697
  }
75643
75698
  const inboxServerCandidates = [
75644
- import_node_path83.default.join(here, "inbox", "mcp-server.cjs"),
75645
- import_node_path83.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
75699
+ import_node_path84.default.join(here, "inbox", "mcp-server.cjs"),
75700
+ import_node_path84.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
75646
75701
  ];
75647
75702
  const inboxServerScriptPath = inboxServerCandidates.find((p3) => import_node_fs65.default.existsSync(p3));
75648
75703
  if (inboxServerScriptPath) {
75649
- const inboxLogPath = import_node_path83.default.join(logDir, "inbox-mcp-server.log");
75704
+ const inboxLogPath = import_node_path84.default.join(logDir, "inbox-mcp-server.log");
75650
75705
  const inboxCfgPath = await writeInboxMcpConfig({
75651
75706
  dataDir: config.dataDir,
75652
75707
  serverScriptPath: inboxServerScriptPath,
@@ -75667,8 +75722,8 @@ async function startDaemon(config) {
75667
75722
  );
75668
75723
  }
75669
75724
  const peerOpsServerCandidates = [
75670
- import_node_path83.default.join(here, "peer-ops", "mcp-server.cjs"),
75671
- import_node_path83.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
75725
+ import_node_path84.default.join(here, "peer-ops", "mcp-server.cjs"),
75726
+ import_node_path84.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
75672
75727
  ];
75673
75728
  const peerOpsServerScriptPath = peerOpsServerCandidates.find((p3) => import_node_fs65.default.existsSync(p3));
75674
75729
  if (peerOpsServerScriptPath) {
@@ -75690,12 +75745,12 @@ async function startDaemon(config) {
75690
75745
  );
75691
75746
  }
75692
75747
  const rpcToolServerCandidates = [
75693
- import_node_path83.default.join(here, "rpc-tool", "mcp-server.cjs"),
75694
- import_node_path83.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
75748
+ import_node_path84.default.join(here, "rpc-tool", "mcp-server.cjs"),
75749
+ import_node_path84.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
75695
75750
  ];
75696
75751
  const rpcToolServerScriptPath = rpcToolServerCandidates.find((p3) => import_node_fs65.default.existsSync(p3));
75697
75752
  if (rpcToolServerScriptPath) {
75698
- const rpcToolLogPath = import_node_path83.default.join(logDir, "rpc-tool-mcp-server.log");
75753
+ const rpcToolLogPath = import_node_path84.default.join(logDir, "rpc-tool-mcp-server.log");
75699
75754
  const rpcToolCfgPath = writeRpcToolMcpConfig({
75700
75755
  dataDir: config.dataDir,
75701
75756
  serverScriptPath: rpcToolServerScriptPath,
@@ -75716,7 +75771,7 @@ async function startDaemon(config) {
75716
75771
  );
75717
75772
  }
75718
75773
  const shiftStore = createShiftStore({
75719
- filePath: import_node_path83.default.join(config.dataDir, "shift.json"),
75774
+ filePath: import_node_path84.default.join(config.dataDir, "shift.json"),
75720
75775
  ownerIdProvider: () => ownerPrincipalId,
75721
75776
  now: () => Date.now()
75722
75777
  });
@@ -75738,7 +75793,7 @@ async function startDaemon(config) {
75738
75793
  getAdapter,
75739
75794
  historyReader: history,
75740
75795
  dataDir: config.dataDir,
75741
- personaRoot: import_node_path83.default.join(config.dataDir, "personas"),
75796
+ personaRoot: import_node_path84.default.join(config.dataDir, "personas"),
75742
75797
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
75743
75798
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
75744
75799
  getPersonaDefaults: (personaId2) => personaRegistry.get(personaId2),
@@ -75795,7 +75850,7 @@ async function startDaemon(config) {
75795
75850
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
75796
75851
  attachmentGroup: {
75797
75852
  onFileEdit: (input) => {
75798
- const absPath = import_node_path83.default.isAbsolute(input.relPath) ? input.relPath : import_node_path83.default.join(input.cwd, input.relPath);
75853
+ const absPath = import_node_path84.default.isAbsolute(input.relPath) ? input.relPath : import_node_path84.default.join(input.cwd, input.relPath);
75799
75854
  let size = 0;
75800
75855
  try {
75801
75856
  size = import_node_fs65.default.statSync(absPath).size;
@@ -75839,9 +75894,9 @@ async function startDaemon(config) {
75839
75894
  logger.info("projects.seed-dropped", { dropped: droppedSeed.dropped, backup: droppedSeed.backup });
75840
75895
  }
75841
75896
  const projectsStore = new ProjectsStore({
75842
- file: import_node_path83.default.join(config.dataDir, "projects.json"),
75897
+ file: import_node_path84.default.join(config.dataDir, "projects.json"),
75843
75898
  clawdDir: config.dataDir,
75844
- codexStateFile: import_node_path83.default.join(import_node_os32.default.homedir(), ".codex", ".codex-global-state.json"),
75899
+ codexStateFile: import_node_path84.default.join(import_node_os31.default.homedir(), ".codex", ".codex-global-state.json"),
75845
75900
  logger
75846
75901
  });
75847
75902
  const observer = new SessionObserver({
@@ -75906,12 +75961,12 @@ async function startDaemon(config) {
75906
75961
  logger
75907
75962
  });
75908
75963
  registerAdapter("claude", claudeAdapter);
75909
- const codexIsolatedHome = import_node_path83.default.join(config.dataDir, "codex-home");
75964
+ const codexIsolatedHome = import_node_path84.default.join(config.dataDir, "codex-home");
75910
75965
  try {
75911
75966
  import_node_fs65.default.mkdirSync(codexIsolatedHome, { recursive: true });
75912
75967
  for (const f2 of ["auth.json", "config.toml"]) {
75913
- const link = import_node_path83.default.join(codexIsolatedHome, f2);
75914
- const target = import_node_path83.default.join(import_node_os32.default.homedir(), ".codex", f2);
75968
+ const link = import_node_path84.default.join(codexIsolatedHome, f2);
75969
+ const target = import_node_path84.default.join(import_node_os31.default.homedir(), ".codex", f2);
75915
75970
  if (!import_node_fs65.default.existsSync(link) && import_node_fs65.default.existsSync(target)) import_node_fs65.default.symlinkSync(target, link);
75916
75971
  }
75917
75972
  } catch (err) {
@@ -75923,19 +75978,20 @@ async function startDaemon(config) {
75923
75978
  logger,
75924
75979
  historyReader: new CodexHistoryReader({
75925
75980
  roots: codexRolloutRoots(config.dataDir),
75926
- sharedHome: import_node_path83.default.join(import_node_os32.default.homedir(), ".codex"),
75981
+ sharedHome: import_node_path84.default.join(import_node_os31.default.homedir(), ".codex"),
75927
75982
  logger
75928
75983
  }),
75929
75984
  // projectPaths 每次调用现取 —— 用户新建 project 后立即生效,不用重启 daemon
75930
75985
  codexHomeFor: (cwd) => resolveCodexHome({
75931
75986
  cwd,
75932
- personaRoot: import_node_path83.default.join(config.dataDir, "personas"),
75987
+ personaRoot: import_node_path84.default.join(config.dataDir, "personas"),
75933
75988
  // 必须用 listMerged(Codex registry ∪ 叠加层):只用本地 list() 的话,用户在
75934
75989
  // Codex 里建的 project 不在表里,在它下面开的会话会被误判成隔离 → Codex 侧看不到
75935
75990
  projectPaths: projectsStore.listMerged().map((x2) => x2.path),
75936
- sharedHome: import_node_path83.default.join(import_node_os32.default.homedir(), ".codex"),
75991
+ sharedHome: import_node_path84.default.join(import_node_os31.default.homedir(), ".codex"),
75937
75992
  isolatedHome: codexIsolatedHome
75938
75993
  }),
75994
+ codexHomeForThread: (threadId) => locateCodexThread(codexRolloutRoots(config.dataDir), threadId)?.home,
75939
75995
  // 附件图片验签:与 http-router /files 路由同一把 secret
75940
75996
  getSignSecret: () => authFile.token
75941
75997
  })
@@ -75958,8 +76014,8 @@ async function startDaemon(config) {
75958
76014
  );
75959
76015
  registerAdapter(PI_CLOUD_TOOL_ID, new CloudPiAdapter({ client: cloudGateway, history: cloudHistorySource, logger }));
75960
76016
  const piMcpBridgeCandidates = [
75961
- import_node_path83.default.join(here, "pi-mcp-bridge", "extension.cjs"),
75962
- import_node_path83.default.join(here, "..", "dist", "pi-mcp-bridge", "extension.cjs")
76017
+ import_node_path84.default.join(here, "pi-mcp-bridge", "extension.cjs"),
76018
+ import_node_path84.default.join(here, "..", "dist", "pi-mcp-bridge", "extension.cjs")
75963
76019
  ];
75964
76020
  const piMcpBridgePath = piMcpBridgeCandidates.find((p3) => import_node_fs65.default.existsSync(p3));
75965
76021
  if (piMcpBridgePath) {
@@ -76257,11 +76313,11 @@ async function startDaemon(config) {
76257
76313
  // 'persona/<pid>/owner',default 走 'default'。
76258
76314
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
76259
76315
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
76260
- personaRoot: import_node_path83.default.join(config.dataDir, "personas"),
76316
+ personaRoot: import_node_path84.default.join(config.dataDir, "personas"),
76261
76317
  usersRoot
76262
76318
  },
76263
76319
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
76264
- personaRoot: import_node_path83.default.join(config.dataDir, "personas"),
76320
+ personaRoot: import_node_path84.default.join(config.dataDir, "personas"),
76265
76321
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
76266
76322
  usersRoot,
76267
76323
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -76407,7 +76463,7 @@ async function startDaemon(config) {
76407
76463
  sourceSessionId: args.sourceSessionId
76408
76464
  });
76409
76465
  const sourceFile = manager.findOwnedSession(args.sourceSessionId);
76410
- const sourceJsonlPath = resolveSourceJsonlPath(sourceFile, import_node_os32.default.homedir());
76466
+ const sourceJsonlPath = resolveSourceJsonlPath(sourceFile, import_node_os31.default.homedir());
76411
76467
  logger.info("dispatch.spawnB.source-resolved", {
76412
76468
  dispatchId: args.dispatchId,
76413
76469
  sourceJsonlPath,
@@ -76567,7 +76623,7 @@ async function startDaemon(config) {
76567
76623
  });
76568
76624
  shiftScheduler.start();
76569
76625
  logger.info("shift scheduler started", { shiftCount: shiftStore.list().length });
76570
- const larkBotStore = createLarkBotBindingStore(import_node_path83.default.join(config.dataDir, "personas"));
76626
+ const larkBotStore = createLarkBotBindingStore(import_node_path84.default.join(config.dataDir, "personas"));
76571
76627
  const larkBotCloud = createLarkBotCloudClient({
76572
76628
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
76573
76629
  getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null
@@ -76579,7 +76635,7 @@ async function startDaemon(config) {
76579
76635
  store: larkBotStore,
76580
76636
  fetchResource: (appId, messageId, fileKey, maxBytes, type) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes, type),
76581
76637
  // 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
76582
- mediaRootFor: (chatId) => import_node_path83.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
76638
+ mediaRootFor: (chatId) => import_node_path84.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
76583
76639
  isShuttingDown: () => larkDraining,
76584
76640
  logger: {
76585
76641
  warn: (msg) => logger.warn(msg),
@@ -76879,8 +76935,8 @@ async function startDaemon(config) {
76879
76935
  const lines = [
76880
76936
  `Tunnel: ${r2.url}`,
76881
76937
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
76882
- `Frpc config: ${import_node_path83.default.join(config.dataDir, "frpc.toml")}`,
76883
- `Frpc log: ${import_node_path83.default.join(logDir, "frpc.log")}`
76938
+ `Frpc config: ${import_node_path84.default.join(config.dataDir, "frpc.toml")}`,
76939
+ `Frpc log: ${import_node_path84.default.join(logDir, "frpc.log")}`
76884
76940
  ];
76885
76941
  const width2 = Math.max(...lines.map((l2) => l2.length));
76886
76942
  const bar = "\u2550".repeat(width2 + 4);
@@ -76893,7 +76949,7 @@ ${bar}
76893
76949
 
76894
76950
  `);
76895
76951
  try {
76896
- const connectPath = import_node_path83.default.join(config.dataDir, "connect.txt");
76952
+ const connectPath = import_node_path84.default.join(config.dataDir, "connect.txt");
76897
76953
  import_node_fs65.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
76898
76954
  } catch {
76899
76955
  }
@@ -76992,7 +77048,7 @@ ${bar}
76992
77048
  };
76993
77049
  }
76994
77050
  function migrateDropPersonsDir(dataDir) {
76995
- const dir = import_node_path83.default.join(dataDir, "persons");
77051
+ const dir = import_node_path84.default.join(dataDir, "persons");
76996
77052
  try {
76997
77053
  import_node_fs65.default.rmSync(dir, { recursive: true, force: true });
76998
77054
  } catch {