@clawos-dev/clawd 0.2.191-beta.383.956b6ae → 0.2.192-beta.386.33a5833

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
@@ -733,8 +733,8 @@ var init_parseUtil = __esm({
733
733
  init_errors2();
734
734
  init_en();
735
735
  makeIssue = (params) => {
736
- const { data, path: path67, errorMaps, issueData } = params;
737
- const fullPath = [...path67, ...issueData.path || []];
736
+ const { data, path: path68, errorMaps, issueData } = params;
737
+ const fullPath = [...path68, ...issueData.path || []];
738
738
  const fullIssue = {
739
739
  ...issueData,
740
740
  path: fullPath
@@ -1045,11 +1045,11 @@ var init_types = __esm({
1045
1045
  init_parseUtil();
1046
1046
  init_util();
1047
1047
  ParseInputLazyPath = class {
1048
- constructor(parent, value, path67, key) {
1048
+ constructor(parent, value, path68, key) {
1049
1049
  this._cachedPath = [];
1050
1050
  this.parent = parent;
1051
1051
  this.data = value;
1052
- this._path = path67;
1052
+ this._path = path68;
1053
1053
  this._key = key;
1054
1054
  }
1055
1055
  get path() {
@@ -5996,7 +5996,7 @@ var init_feishu_auth = __esm({
5996
5996
  });
5997
5997
 
5998
5998
  // ../protocol/src/dispatch.ts
5999
- var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema;
5999
+ var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchRunResponseSchema, DispatchCompleteArgsSchema;
6000
6000
  var init_dispatch = __esm({
6001
6001
  "../protocol/src/dispatch.ts"() {
6002
6002
  "use strict";
@@ -6017,10 +6017,21 @@ var init_dispatch = __esm({
6017
6017
  prompt: external_exports.string(),
6018
6018
  // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
6019
6019
  // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
6020
- targetDeviceId: external_exports.string().min(1).optional()
6020
+ targetDeviceId: external_exports.string().min(1).optional(),
6021
+ // 精确寻址已知 B session(复用其上下文):
6022
+ // - 本地(targetDeviceId 缺省):daemon 校验目标为本机 session 且
6023
+ // dispatchedFromSessionId 匹配 A 的 sessionId(防越权)
6024
+ // - 跨设备(targetDeviceId 非空):peer daemon 在其本地按同规则校验
6025
+ // creatorPrincipalId === A.deviceId
6026
+ // - 省略 = 新建 B session
6027
+ targetSessionId: external_exports.string().min(1).optional()
6028
+ });
6029
+ DispatchRunResponseSchema = external_exports.object({
6030
+ type: external_exports.literal("personaDispatch:run:ok"),
6031
+ outcome: DispatchOutcomeSchema,
6032
+ dispatchedSessionId: external_exports.string().min(1).optional()
6021
6033
  });
6022
6034
  DispatchCompleteArgsSchema = external_exports.object({
6023
- dispatchId: external_exports.string().min(1),
6024
6035
  outcome: DispatchOutcomeSchema
6025
6036
  });
6026
6037
  }
@@ -6373,8 +6384,8 @@ var require_req = __commonJS({
6373
6384
  if (req.originalUrl) {
6374
6385
  _req.url = req.originalUrl;
6375
6386
  } else {
6376
- const path67 = req.path;
6377
- _req.url = typeof path67 === "string" ? path67 : req.url ? req.url.path || req.url : void 0;
6387
+ const path68 = req.path;
6388
+ _req.url = typeof path68 === "string" ? path68 : req.url ? req.url.path || req.url : void 0;
6378
6389
  }
6379
6390
  if (req.query) {
6380
6391
  _req.query = req.query;
@@ -6539,14 +6550,14 @@ var require_redact = __commonJS({
6539
6550
  }
6540
6551
  return obj;
6541
6552
  }
6542
- function parsePath(path67) {
6553
+ function parsePath(path68) {
6543
6554
  const parts = [];
6544
6555
  let current = "";
6545
6556
  let inBrackets = false;
6546
6557
  let inQuotes = false;
6547
6558
  let quoteChar = "";
6548
- for (let i = 0; i < path67.length; i++) {
6549
- const char = path67[i];
6559
+ for (let i = 0; i < path68.length; i++) {
6560
+ const char = path68[i];
6550
6561
  if (!inBrackets && char === ".") {
6551
6562
  if (current) {
6552
6563
  parts.push(current);
@@ -6677,10 +6688,10 @@ var require_redact = __commonJS({
6677
6688
  return current;
6678
6689
  }
6679
6690
  function redactPaths(obj, paths, censor, remove = false) {
6680
- for (const path67 of paths) {
6681
- const parts = parsePath(path67);
6691
+ for (const path68 of paths) {
6692
+ const parts = parsePath(path68);
6682
6693
  if (parts.includes("*")) {
6683
- redactWildcardPath(obj, parts, censor, path67, remove);
6694
+ redactWildcardPath(obj, parts, censor, path68, remove);
6684
6695
  } else {
6685
6696
  if (remove) {
6686
6697
  removeKey(obj, parts);
@@ -6765,8 +6776,8 @@ var require_redact = __commonJS({
6765
6776
  }
6766
6777
  } else {
6767
6778
  if (afterWildcard.includes("*")) {
6768
- const wrappedCensor = typeof censor === "function" ? (value, path67) => {
6769
- const fullPath = [...pathArray.slice(0, pathLength), ...path67];
6779
+ const wrappedCensor = typeof censor === "function" ? (value, path68) => {
6780
+ const fullPath = [...pathArray.slice(0, pathLength), ...path68];
6770
6781
  return censor(value, fullPath);
6771
6782
  } : censor;
6772
6783
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6801,8 +6812,8 @@ var require_redact = __commonJS({
6801
6812
  return null;
6802
6813
  }
6803
6814
  const pathStructure = /* @__PURE__ */ new Map();
6804
- for (const path67 of pathsToClone) {
6805
- const parts = parsePath(path67);
6815
+ for (const path68 of pathsToClone) {
6816
+ const parts = parsePath(path68);
6806
6817
  let current = pathStructure;
6807
6818
  for (let i = 0; i < parts.length; i++) {
6808
6819
  const part = parts[i];
@@ -6854,24 +6865,24 @@ var require_redact = __commonJS({
6854
6865
  }
6855
6866
  return cloneSelectively(obj, pathStructure);
6856
6867
  }
6857
- function validatePath(path67) {
6858
- if (typeof path67 !== "string") {
6868
+ function validatePath(path68) {
6869
+ if (typeof path68 !== "string") {
6859
6870
  throw new Error("Paths must be (non-empty) strings");
6860
6871
  }
6861
- if (path67 === "") {
6872
+ if (path68 === "") {
6862
6873
  throw new Error("Invalid redaction path ()");
6863
6874
  }
6864
- if (path67.includes("..")) {
6865
- throw new Error(`Invalid redaction path (${path67})`);
6875
+ if (path68.includes("..")) {
6876
+ throw new Error(`Invalid redaction path (${path68})`);
6866
6877
  }
6867
- if (path67.includes(",")) {
6868
- throw new Error(`Invalid redaction path (${path67})`);
6878
+ if (path68.includes(",")) {
6879
+ throw new Error(`Invalid redaction path (${path68})`);
6869
6880
  }
6870
6881
  let bracketCount = 0;
6871
6882
  let inQuotes = false;
6872
6883
  let quoteChar = "";
6873
- for (let i = 0; i < path67.length; i++) {
6874
- const char = path67[i];
6884
+ for (let i = 0; i < path68.length; i++) {
6885
+ const char = path68[i];
6875
6886
  if ((char === '"' || char === "'") && bracketCount > 0) {
6876
6887
  if (!inQuotes) {
6877
6888
  inQuotes = true;
@@ -6885,20 +6896,20 @@ var require_redact = __commonJS({
6885
6896
  } else if (char === "]" && !inQuotes) {
6886
6897
  bracketCount--;
6887
6898
  if (bracketCount < 0) {
6888
- throw new Error(`Invalid redaction path (${path67})`);
6899
+ throw new Error(`Invalid redaction path (${path68})`);
6889
6900
  }
6890
6901
  }
6891
6902
  }
6892
6903
  if (bracketCount !== 0) {
6893
- throw new Error(`Invalid redaction path (${path67})`);
6904
+ throw new Error(`Invalid redaction path (${path68})`);
6894
6905
  }
6895
6906
  }
6896
6907
  function validatePaths(paths) {
6897
6908
  if (!Array.isArray(paths)) {
6898
6909
  throw new TypeError("paths must be an array");
6899
6910
  }
6900
- for (const path67 of paths) {
6901
- validatePath(path67);
6911
+ for (const path68 of paths) {
6912
+ validatePath(path68);
6902
6913
  }
6903
6914
  }
6904
6915
  function slowRedact(options = {}) {
@@ -7066,8 +7077,8 @@ var require_redaction = __commonJS({
7066
7077
  if (shape[k2] === null) {
7067
7078
  o[k2] = (value) => topCensor(value, [k2]);
7068
7079
  } else {
7069
- const wrappedCensor = typeof censor === "function" ? (value, path67) => {
7070
- return censor(value, [k2, ...path67]);
7080
+ const wrappedCensor = typeof censor === "function" ? (value, path68) => {
7081
+ return censor(value, [k2, ...path68]);
7071
7082
  } : censor;
7072
7083
  o[k2] = Redact({
7073
7084
  paths: shape[k2],
@@ -7288,7 +7299,7 @@ var require_sonic_boom = __commonJS({
7288
7299
  var fs61 = require("fs");
7289
7300
  var EventEmitter3 = require("events");
7290
7301
  var inherits = require("util").inherits;
7291
- var path67 = require("path");
7302
+ var path68 = require("path");
7292
7303
  var sleep2 = require_atomic_sleep();
7293
7304
  var assert = require("assert");
7294
7305
  var BUSY_WRITE_TIMEOUT = 100;
@@ -7342,7 +7353,7 @@ var require_sonic_boom = __commonJS({
7342
7353
  const mode = sonic.mode;
7343
7354
  if (sonic.sync) {
7344
7355
  try {
7345
- if (sonic.mkdir) fs61.mkdirSync(path67.dirname(file), { recursive: true });
7356
+ if (sonic.mkdir) fs61.mkdirSync(path68.dirname(file), { recursive: true });
7346
7357
  const fd = fs61.openSync(file, flags, mode);
7347
7358
  fileOpened(null, fd);
7348
7359
  } catch (err) {
@@ -7350,7 +7361,7 @@ var require_sonic_boom = __commonJS({
7350
7361
  throw err;
7351
7362
  }
7352
7363
  } else if (sonic.mkdir) {
7353
- fs61.mkdir(path67.dirname(file), { recursive: true }, (err) => {
7364
+ fs61.mkdir(path68.dirname(file), { recursive: true }, (err) => {
7354
7365
  if (err) return fileOpened(err);
7355
7366
  fs61.open(file, flags, mode, fileOpened);
7356
7367
  });
@@ -10210,7 +10221,7 @@ var require_multistream = __commonJS({
10210
10221
  var require_pino = __commonJS({
10211
10222
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
10212
10223
  "use strict";
10213
- var os22 = require("os");
10224
+ var os23 = require("os");
10214
10225
  var stdSerializers = require_pino_std_serializers();
10215
10226
  var caller = require_caller();
10216
10227
  var redaction = require_redaction();
@@ -10257,7 +10268,7 @@ var require_pino = __commonJS({
10257
10268
  } = symbols;
10258
10269
  var { epochTime, nullTime } = time;
10259
10270
  var { pid } = process;
10260
- var hostname = os22.hostname();
10271
+ var hostname = os23.hostname();
10261
10272
  var defaultErrorSerializer = stdSerializers.err;
10262
10273
  var defaultOptions = {
10263
10274
  level: "info",
@@ -10981,11 +10992,11 @@ var init_lib = __esm({
10981
10992
  }
10982
10993
  }
10983
10994
  },
10984
- addToPath: function addToPath(path67, added, removed, oldPosInc, options) {
10985
- var last = path67.lastComponent;
10995
+ addToPath: function addToPath(path68, added, removed, oldPosInc, options) {
10996
+ var last = path68.lastComponent;
10986
10997
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10987
10998
  return {
10988
- oldPos: path67.oldPos + oldPosInc,
10999
+ oldPos: path68.oldPos + oldPosInc,
10989
11000
  lastComponent: {
10990
11001
  count: last.count + 1,
10991
11002
  added,
@@ -10995,7 +11006,7 @@ var init_lib = __esm({
10995
11006
  };
10996
11007
  } else {
10997
11008
  return {
10998
- oldPos: path67.oldPos + oldPosInc,
11009
+ oldPos: path68.oldPos + oldPosInc,
10999
11010
  lastComponent: {
11000
11011
  count: 1,
11001
11012
  added,
@@ -11053,7 +11064,7 @@ var init_lib = __esm({
11053
11064
  tokenize: function tokenize(value) {
11054
11065
  return Array.from(value);
11055
11066
  },
11056
- join: function join5(chars) {
11067
+ join: function join3(chars) {
11057
11068
  return chars.join("");
11058
11069
  },
11059
11070
  postProcess: function postProcess(changeObjects) {
@@ -11235,13 +11246,33 @@ var init_tool_result_extra = __esm({
11235
11246
  function cwdToHashDir(cwd) {
11236
11247
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
11237
11248
  }
11249
+ function newestSubagentMtimeMs(projectsRoot, cwd, toolSessionId) {
11250
+ const dir = import_node_path4.default.join(projectsRoot, cwdToHashDir(cwd), toolSessionId, "subagents");
11251
+ let entries;
11252
+ try {
11253
+ entries = import_node_fs4.default.readdirSync(dir, { withFileTypes: true });
11254
+ } catch {
11255
+ return null;
11256
+ }
11257
+ let newest = null;
11258
+ for (const e of entries) {
11259
+ if (!e.isFile()) continue;
11260
+ if (!e.name.startsWith("agent-") || !e.name.endsWith(".jsonl")) continue;
11261
+ try {
11262
+ const m2 = import_node_fs4.default.statSync(import_node_path4.default.join(dir, e.name)).mtimeMs;
11263
+ if (newest === null || m2 > newest) newest = m2;
11264
+ } catch {
11265
+ }
11266
+ }
11267
+ return newest;
11268
+ }
11238
11269
  function hashDirToCwd(hash) {
11239
11270
  const body = hash.startsWith("-") ? hash.slice(1) : hash;
11240
11271
  return "/" + body.replace(/-/g, "/");
11241
11272
  }
11242
11273
  function safeStatMtime(p2) {
11243
11274
  try {
11244
- return import_node_fs13.default.statSync(p2).mtimeMs;
11275
+ return import_node_fs4.default.statSync(p2).mtimeMs;
11245
11276
  } catch {
11246
11277
  return 0;
11247
11278
  }
@@ -11249,7 +11280,7 @@ function safeStatMtime(p2) {
11249
11280
  function readJsonlLines(file) {
11250
11281
  let raw;
11251
11282
  try {
11252
- raw = import_node_fs13.default.readFileSync(file, "utf8");
11283
+ raw = import_node_fs4.default.readFileSync(file, "utf8");
11253
11284
  } catch (err) {
11254
11285
  if (err.code === "ENOENT") return [];
11255
11286
  throw err;
@@ -11441,10 +11472,10 @@ function attachmentToHistoryMessage(o, ts) {
11441
11472
  const memories = raw.map((m2) => {
11442
11473
  if (!m2 || typeof m2 !== "object") return null;
11443
11474
  const rec3 = m2;
11444
- const path67 = typeof rec3.path === "string" ? rec3.path : null;
11475
+ const path68 = typeof rec3.path === "string" ? rec3.path : null;
11445
11476
  const content = typeof rec3.content === "string" ? rec3.content : null;
11446
- if (!path67 || content == null) return null;
11447
- const entry = { path: path67, content };
11477
+ if (!path68 || content == null) return null;
11478
+ const entry = { path: path68, content };
11448
11479
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
11449
11480
  return entry;
11450
11481
  }).filter((m2) => m2 !== null);
@@ -11480,8 +11511,8 @@ function attachmentDeferredToolsText(a) {
11480
11511
  function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
11481
11512
  if (backupFileName === null) return null;
11482
11513
  try {
11483
- return import_node_fs13.default.readFileSync(
11484
- import_node_path12.default.join(fileHistoryRoot, toolSessionId, backupFileName),
11514
+ return import_node_fs4.default.readFileSync(
11515
+ import_node_path4.default.join(fileHistoryRoot, toolSessionId, backupFileName),
11485
11516
  "utf8"
11486
11517
  );
11487
11518
  } catch {
@@ -11490,19 +11521,19 @@ function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
11490
11521
  }
11491
11522
  function readCurrentContent(filePath) {
11492
11523
  try {
11493
- return import_node_fs13.default.readFileSync(filePath, "utf8");
11524
+ return import_node_fs4.default.readFileSync(filePath, "utf8");
11494
11525
  } catch (err) {
11495
11526
  if (err.code === "ENOENT") return null;
11496
11527
  return null;
11497
11528
  }
11498
11529
  }
11499
- var import_node_fs13, import_node_os5, import_node_path12, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
11530
+ var import_node_fs4, import_node_os3, import_node_path4, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
11500
11531
  var init_claude_history = __esm({
11501
11532
  "src/tools/claude-history.ts"() {
11502
11533
  "use strict";
11503
- import_node_fs13 = __toESM(require("fs"), 1);
11504
- import_node_os5 = __toESM(require("os"), 1);
11505
- import_node_path12 = __toESM(require("path"), 1);
11534
+ import_node_fs4 = __toESM(require("fs"), 1);
11535
+ import_node_os3 = __toESM(require("os"), 1);
11536
+ import_node_path4 = __toESM(require("path"), 1);
11506
11537
  init_lib();
11507
11538
  init_tool_result_extra();
11508
11539
  TASK_NOTIFICATION_RE = /<task-notification\b[\s\S]*?<\/task-notification>/i;
@@ -11526,14 +11557,14 @@ var init_claude_history = __esm({
11526
11557
  // 每次 user 提交前 trackEdit 拷一份,作为 rewind 回退目标
11527
11558
  fileHistoryRoot;
11528
11559
  constructor(opts = {}) {
11529
- const base = opts.baseDir ?? import_node_path12.default.join(import_node_os5.default.homedir(), ".claude");
11530
- this.projectsRoot = import_node_path12.default.join(base, "projects");
11531
- this.fileHistoryRoot = import_node_path12.default.join(base, "file-history");
11560
+ const base = opts.baseDir ?? import_node_path4.default.join(import_node_os3.default.homedir(), ".claude");
11561
+ this.projectsRoot = import_node_path4.default.join(base, "projects");
11562
+ this.fileHistoryRoot = import_node_path4.default.join(base, "file-history");
11532
11563
  }
11533
11564
  async listProjects() {
11534
11565
  let entries;
11535
11566
  try {
11536
- entries = import_node_fs13.default.readdirSync(this.projectsRoot, { withFileTypes: true });
11567
+ entries = import_node_fs4.default.readdirSync(this.projectsRoot, { withFileTypes: true });
11537
11568
  } catch (err) {
11538
11569
  if (err.code === "ENOENT") return [];
11539
11570
  throw err;
@@ -11541,9 +11572,9 @@ var init_claude_history = __esm({
11541
11572
  const out = [];
11542
11573
  for (const ent of entries) {
11543
11574
  if (!ent.isDirectory()) continue;
11544
- const dir = import_node_path12.default.join(this.projectsRoot, ent.name);
11545
- const files = import_node_fs13.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11546
- const updatedAtMs = files.reduce((m2, f) => Math.max(m2, safeStatMtime(import_node_path12.default.join(dir, f))), 0);
11575
+ const dir = import_node_path4.default.join(this.projectsRoot, ent.name);
11576
+ const files = import_node_fs4.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11577
+ const updatedAtMs = files.reduce((m2, f) => Math.max(m2, safeStatMtime(import_node_path4.default.join(dir, f))), 0);
11547
11578
  out.push({
11548
11579
  projectPath: hashDirToCwd(ent.name),
11549
11580
  hashDir: ent.name,
@@ -11555,17 +11586,17 @@ var init_claude_history = __esm({
11555
11586
  return out;
11556
11587
  }
11557
11588
  async listSessions(args) {
11558
- const dir = import_node_path12.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
11589
+ const dir = import_node_path4.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
11559
11590
  let files;
11560
11591
  try {
11561
- files = import_node_fs13.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11592
+ files = import_node_fs4.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11562
11593
  } catch (err) {
11563
11594
  if (err.code === "ENOENT") return [];
11564
11595
  throw err;
11565
11596
  }
11566
11597
  const out = [];
11567
11598
  for (const f of files) {
11568
- const full = import_node_path12.default.join(dir, f);
11599
+ const full = import_node_path4.default.join(dir, f);
11569
11600
  const toolSessionId = f.slice(0, -".jsonl".length);
11570
11601
  const lines = readJsonlLines(full);
11571
11602
  let summary = "";
@@ -11620,7 +11651,7 @@ var init_claude_history = __esm({
11620
11651
  return out;
11621
11652
  }
11622
11653
  async read(args) {
11623
- const file = import_node_path12.default.join(
11654
+ const file = import_node_path4.default.join(
11624
11655
  this.projectsRoot,
11625
11656
  cwdToHashDir(args.cwd),
11626
11657
  `${args.toolSessionId}.jsonl`
@@ -11657,7 +11688,7 @@ var init_claude_history = __esm({
11657
11688
  // 独立目录路径:<projectsRoot>/<cwdHash>/<toolSessionId>/subagents/*.jsonl
11658
11689
  // 返回 null 表示目录不存在(调用方回退旧实现);返回空数组表示目录存在但无 jsonl
11659
11690
  listSubagentsFromDirectory(cwd, toolSessionId) {
11660
- const dir = import_node_path12.default.join(
11691
+ const dir = import_node_path4.default.join(
11661
11692
  this.projectsRoot,
11662
11693
  cwdToHashDir(cwd),
11663
11694
  toolSessionId,
@@ -11665,7 +11696,7 @@ var init_claude_history = __esm({
11665
11696
  );
11666
11697
  let entries;
11667
11698
  try {
11668
- entries = import_node_fs13.default.readdirSync(dir, { withFileTypes: true });
11699
+ entries = import_node_fs4.default.readdirSync(dir, { withFileTypes: true });
11669
11700
  } catch (err) {
11670
11701
  if (err.code === "ENOENT") return null;
11671
11702
  return null;
@@ -11675,7 +11706,7 @@ var init_claude_history = __esm({
11675
11706
  if (!e.isFile()) continue;
11676
11707
  if (!e.name.startsWith("agent-") || !e.name.endsWith(".jsonl")) continue;
11677
11708
  const subagentId = e.name.slice("agent-".length, -".jsonl".length);
11678
- const filePath = import_node_path12.default.join(dir, e.name);
11709
+ const filePath = import_node_path4.default.join(dir, e.name);
11679
11710
  const lines = readJsonlLines(filePath);
11680
11711
  let firstText = "";
11681
11712
  let messageCount = 0;
@@ -11692,7 +11723,7 @@ var init_claude_history = __esm({
11692
11723
  return out;
11693
11724
  }
11694
11725
  listSubagentsFromMainJsonl(cwd, toolSessionId) {
11695
- const file = import_node_path12.default.join(
11726
+ const file = import_node_path4.default.join(
11696
11727
  this.projectsRoot,
11697
11728
  cwdToHashDir(cwd),
11698
11729
  `${toolSessionId}.jsonl`
@@ -11727,7 +11758,7 @@ var init_claude_history = __esm({
11727
11758
  }
11728
11759
  // 独立文件路径:agent-<subagentId>.jsonl;文件不存在返回 null 让调用方回退旧实现
11729
11760
  readSubagentFromFile(cwd, toolSessionId, subagentId) {
11730
- const file = import_node_path12.default.join(
11761
+ const file = import_node_path4.default.join(
11731
11762
  this.projectsRoot,
11732
11763
  cwdToHashDir(cwd),
11733
11764
  toolSessionId,
@@ -11736,7 +11767,7 @@ var init_claude_history = __esm({
11736
11767
  );
11737
11768
  let exists = false;
11738
11769
  try {
11739
- exists = import_node_fs13.default.statSync(file).isFile();
11770
+ exists = import_node_fs4.default.statSync(file).isFile();
11740
11771
  } catch {
11741
11772
  return null;
11742
11773
  }
@@ -11755,7 +11786,7 @@ var init_claude_history = __esm({
11755
11786
  * "那一刻每个 tracked 文件对应的 backup 文件名"
11756
11787
  */
11757
11788
  readFileHistorySnapshots(args) {
11758
- const file = import_node_path12.default.join(
11789
+ const file = import_node_path4.default.join(
11759
11790
  this.projectsRoot,
11760
11791
  cwdToHashDir(args.cwd),
11761
11792
  `${args.toolSessionId}.jsonl`
@@ -11800,7 +11831,7 @@ var init_claude_history = __esm({
11800
11831
  for (const [anchorId, target] of snapshots) {
11801
11832
  let hasAny = false;
11802
11833
  for (const [rawPath, backup] of Object.entries(target)) {
11803
- const absPath = import_node_path12.default.isAbsolute(rawPath) ? rawPath : import_node_path12.default.join(args.cwd, rawPath);
11834
+ const absPath = import_node_path4.default.isAbsolute(rawPath) ? rawPath : import_node_path4.default.join(args.cwd, rawPath);
11804
11835
  const backupContent = readBackupContent(
11805
11836
  this.fileHistoryRoot,
11806
11837
  args.toolSessionId,
@@ -11840,7 +11871,7 @@ var init_claude_history = __esm({
11840
11871
  let totalInsertions = 0;
11841
11872
  let totalDeletions = 0;
11842
11873
  for (const [rawPath, backup] of Object.entries(target)) {
11843
- const absPath = import_node_path12.default.isAbsolute(rawPath) ? rawPath : import_node_path12.default.join(args.cwd, rawPath);
11874
+ const absPath = import_node_path4.default.isAbsolute(rawPath) ? rawPath : import_node_path4.default.join(args.cwd, rawPath);
11844
11875
  const backupContent = readBackupContent(
11845
11876
  this.fileHistoryRoot,
11846
11877
  args.toolSessionId,
@@ -11887,7 +11918,7 @@ var init_claude_history = __esm({
11887
11918
  };
11888
11919
  }
11889
11920
  readSubagentFromMainJsonl(cwd, toolSessionId, subagentId) {
11890
- const file = import_node_path12.default.join(
11921
+ const file = import_node_path4.default.join(
11891
11922
  this.projectsRoot,
11892
11923
  cwdToHashDir(cwd),
11893
11924
  `${toolSessionId}.jsonl`
@@ -12256,10 +12287,10 @@ function parseAttachment(obj) {
12256
12287
  const memories = raw.map((m2) => {
12257
12288
  if (!m2 || typeof m2 !== "object") return null;
12258
12289
  const rec3 = m2;
12259
- const path67 = typeof rec3.path === "string" ? rec3.path : null;
12290
+ const path68 = typeof rec3.path === "string" ? rec3.path : null;
12260
12291
  const content = typeof rec3.content === "string" ? rec3.content : null;
12261
- if (!path67 || content == null) return null;
12262
- const out = { path: path67, content };
12292
+ if (!path68 || content == null) return null;
12293
+ const out = { path: path68, content };
12263
12294
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
12264
12295
  return out;
12265
12296
  }).filter((m2) => m2 !== null);
@@ -12391,8 +12422,9 @@ var init_claude = __esm({
12391
12422
  };
12392
12423
  CLAUDE_MODELS = [
12393
12424
  { id: "", label: "Default", description: "CLI default model", contextWindowSize: 2e5, default: true },
12394
- { id: "sonnet", label: "Sonnet", description: "Fast, balanced", contextWindowSize: 2e5 },
12395
12425
  { id: "opus", label: "Opus", description: "Most capable", contextWindowSize: 1e6 },
12426
+ { id: "fable", label: "Fable", contextWindowSize: 2e5 },
12427
+ { id: "sonnet", label: "Sonnet", description: "Fast, balanced", contextWindowSize: 2e5 },
12396
12428
  { id: "haiku", label: "Haiku", description: "Fastest, lightest", contextWindowSize: 2e5 }
12397
12429
  ];
12398
12430
  CLAUDE_PERMISSION_MODES = [
@@ -33223,8 +33255,8 @@ var require_utils = __commonJS({
33223
33255
  var result = transform[inputType][outputType](input);
33224
33256
  return result;
33225
33257
  };
33226
- exports2.resolve = function(path67) {
33227
- var parts = path67.split("/");
33258
+ exports2.resolve = function(path68) {
33259
+ var parts = path68.split("/");
33228
33260
  var result = [];
33229
33261
  for (var index = 0; index < parts.length; index++) {
33230
33262
  var part = parts[index];
@@ -39077,18 +39109,18 @@ var require_object = __commonJS({
39077
39109
  var object = new ZipObject(name, zipObjectContent, o);
39078
39110
  this.files[name] = object;
39079
39111
  };
39080
- var parentFolder = function(path67) {
39081
- if (path67.slice(-1) === "/") {
39082
- path67 = path67.substring(0, path67.length - 1);
39112
+ var parentFolder = function(path68) {
39113
+ if (path68.slice(-1) === "/") {
39114
+ path68 = path68.substring(0, path68.length - 1);
39083
39115
  }
39084
- var lastSlash = path67.lastIndexOf("/");
39085
- return lastSlash > 0 ? path67.substring(0, lastSlash) : "";
39116
+ var lastSlash = path68.lastIndexOf("/");
39117
+ return lastSlash > 0 ? path68.substring(0, lastSlash) : "";
39086
39118
  };
39087
- var forceTrailingSlash = function(path67) {
39088
- if (path67.slice(-1) !== "/") {
39089
- path67 += "/";
39119
+ var forceTrailingSlash = function(path68) {
39120
+ if (path68.slice(-1) !== "/") {
39121
+ path68 += "/";
39090
39122
  }
39091
- return path67;
39123
+ return path68;
39092
39124
  };
39093
39125
  var folderAdd = function(name, createFolders) {
39094
39126
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -40090,7 +40122,7 @@ var require_lib3 = __commonJS({
40090
40122
  // src/run-case/recorder.ts
40091
40123
  function startRunCaseRecorder(opts) {
40092
40124
  const now = opts.now ?? Date.now;
40093
- const dir = import_node_path55.default.dirname(opts.recordPath);
40125
+ const dir = import_node_path56.default.dirname(opts.recordPath);
40094
40126
  let stream = null;
40095
40127
  let closing = false;
40096
40128
  let closedSettled = false;
@@ -40130,12 +40162,12 @@ function startRunCaseRecorder(opts) {
40130
40162
  };
40131
40163
  return { tap, close, closed };
40132
40164
  }
40133
- var import_node_fs43, import_node_path55;
40165
+ var import_node_fs43, import_node_path56;
40134
40166
  var init_recorder = __esm({
40135
40167
  "src/run-case/recorder.ts"() {
40136
40168
  "use strict";
40137
40169
  import_node_fs43 = __toESM(require("fs"), 1);
40138
- import_node_path55 = __toESM(require("path"), 1);
40170
+ import_node_path56 = __toESM(require("path"), 1);
40139
40171
  }
40140
40172
  });
40141
40173
 
@@ -40178,7 +40210,7 @@ var init_wire = __esm({
40178
40210
  // src/run-case/controller.ts
40179
40211
  async function runController(opts) {
40180
40212
  const now = opts.now ?? Date.now;
40181
- const cwd = opts.cwd ?? (0, import_node_fs44.mkdtempSync)(import_node_path56.default.join(import_node_os21.default.tmpdir(), "clawd-runcase-"));
40213
+ const cwd = opts.cwd ?? (0, import_node_fs44.mkdtempSync)(import_node_path57.default.join(import_node_os22.default.tmpdir(), "clawd-runcase-"));
40182
40214
  const ownsCwd = opts.cwd === void 0;
40183
40215
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
40184
40216
  const spawnCtx = { cwd };
@@ -40345,13 +40377,13 @@ async function runController(opts) {
40345
40377
  }
40346
40378
  return exitCode ?? 0;
40347
40379
  }
40348
- var import_node_fs44, import_node_os21, import_node_path56;
40380
+ var import_node_fs44, import_node_os22, import_node_path57;
40349
40381
  var init_controller = __esm({
40350
40382
  "src/run-case/controller.ts"() {
40351
40383
  "use strict";
40352
40384
  import_node_fs44 = require("fs");
40353
- import_node_os21 = __toESM(require("os"), 1);
40354
- import_node_path56 = __toESM(require("path"), 1);
40385
+ import_node_os22 = __toESM(require("os"), 1);
40386
+ import_node_path57 = __toESM(require("path"), 1);
40355
40387
  init_claude();
40356
40388
  init_stdout_splitter();
40357
40389
  init_permission_stdio();
@@ -40613,9 +40645,9 @@ Env (advanced):
40613
40645
  `;
40614
40646
 
40615
40647
  // src/index.ts
40616
- var import_node_path54 = __toESM(require("path"), 1);
40648
+ var import_node_path55 = __toESM(require("path"), 1);
40617
40649
  var import_node_fs42 = __toESM(require("fs"), 1);
40618
- var import_node_os20 = __toESM(require("os"), 1);
40650
+ var import_node_os21 = __toESM(require("os"), 1);
40619
40651
 
40620
40652
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
40621
40653
  var byteToHex = [];
@@ -41232,9 +41264,9 @@ var SessionStoreFactory = class {
41232
41264
  };
41233
41265
 
41234
41266
  // src/session/manager.ts
41235
- var import_node_fs6 = __toESM(require("fs"), 1);
41236
- var import_node_path6 = __toESM(require("path"), 1);
41237
- var import_node_os3 = __toESM(require("os"), 1);
41267
+ var import_node_fs7 = __toESM(require("fs"), 1);
41268
+ var import_node_path8 = __toESM(require("path"), 1);
41269
+ var import_node_os5 = __toESM(require("os"), 1);
41238
41270
  init_protocol();
41239
41271
 
41240
41272
  // src/tools/guest-settings.ts
@@ -41332,6 +41364,10 @@ function escapeAttr(v2) {
41332
41364
  return v2.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
41333
41365
  }
41334
41366
 
41367
+ // src/session/runner.ts
41368
+ var import_node_os4 = __toESM(require("os"), 1);
41369
+ var import_node_path6 = __toESM(require("path"), 1);
41370
+
41335
41371
  // src/session/reducer.ts
41336
41372
  init_runtime();
41337
41373
 
@@ -41418,7 +41454,7 @@ var ATTACHMENT_SHARING_HINT = `## \u628A\u4EA7\u51FA\u6587\u4EF6\u5206\u4EAB\u7E
41418
41454
  // src/dispatch/system-prompt.ts
41419
41455
  var DISPATCH_SYSTEM_PROMPT_HINT = `## \u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08dispatch\uFF09
41420
41456
 
41421
- \u5F53\u7528\u6237\u6D88\u606F\u91CC\u542B \`@persona/<id>\`\uFF08\u5982 \`@persona/persona-bug-fixer\`\uFF09\u65F6\uFF0C**\u4F60\u5FC5\u987B**\u7ACB\u5373\u8C03 MCP tool \`mcp__clawd-dispatch__personaDispatch\` \u59D4\u6D3E\uFF0C\u4E0D\u8981\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF1A
41457
+ \u5F53\u7528\u6237\u6D88\u606F\u91CC\u542B \`@persona/<id>\`\uFF08\u5982 \`@persona/persona-app-builder\`\uFF09\u65F6\uFF0C**\u4F60\u5FC5\u987B**\u7ACB\u5373\u8C03 MCP tool \`mcp__clawd-dispatch__personaDispatch\` \u59D4\u6D3E\uFF0C\u4E0D\u8981\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF1A
41422
41458
 
41423
41459
  - \`targetPersona = <id>\`\uFF08@ token \u91CC\u7684 persona id\uFF09
41424
41460
  - \`prompt = \u53BB\u6389 @persona/<id> token \u7684\u7528\u6237\u539F\u6587\`
@@ -41470,7 +41506,7 @@ function cloneState(s) {
41470
41506
  pendingSend: s.pendingSend.slice()
41471
41507
  };
41472
41508
  }
41473
- var IDLE_KILL_DELAY_MS = 6e4;
41509
+ var IDLE_KILL_DELAY_MS = 6e5;
41474
41510
  function tryFlushPending(state, deps) {
41475
41511
  if (!state.readyForSend || state.pendingSend.length === 0) return [];
41476
41512
  const text = state.pendingSend.shift();
@@ -41527,8 +41563,6 @@ function buildSpawnContext(state, deps) {
41527
41563
  env.CLAWD_SESSION_ID = file.sessionId;
41528
41564
  const daemonUrl = deps.getDaemonUrl?.() ?? null;
41529
41565
  if (daemonUrl) env.CLAWD_DAEMON_URL = daemonUrl;
41530
- const dispatchId = deps.lookupDispatchByBSessionId?.(file.sessionId);
41531
- if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
41532
41566
  const personaId = file.ownerPersonaId;
41533
41567
  if (personaId) env.CLAWD_PERSONA_ID = personaId;
41534
41568
  const dispatchMcpConfigPath2 = deps.getDispatchMcpConfigPath?.() ?? null;
@@ -42141,6 +42175,18 @@ function reduceSession(state, input, deps) {
42141
42175
  if (state.status !== "running-idle") {
42142
42176
  return { state, effects: [] };
42143
42177
  }
42178
+ if (input.subagentActive) {
42179
+ return {
42180
+ state,
42181
+ effects: [
42182
+ {
42183
+ kind: "schedule-idle-kill",
42184
+ sessionId: state.file.sessionId,
42185
+ ms: IDLE_KILL_DELAY_MS
42186
+ }
42187
+ ]
42188
+ };
42189
+ }
42144
42190
  const next = cloneState(state);
42145
42191
  next.status = "stopping";
42146
42192
  return {
@@ -42197,10 +42243,11 @@ function reduceSession(state, input, deps) {
42197
42243
  // src/session/runner.ts
42198
42244
  init_stdout_splitter();
42199
42245
  init_permission_stdio();
42246
+ init_claude_history();
42200
42247
 
42201
42248
  // src/ipc-recorder.ts
42202
- var import_node_fs4 = __toESM(require("fs"), 1);
42203
- var import_node_path4 = __toESM(require("path"), 1);
42249
+ var import_node_fs5 = __toESM(require("fs"), 1);
42250
+ var import_node_path5 = __toESM(require("path"), 1);
42204
42251
  function tsForFilename(ms) {
42205
42252
  return new Date(ms).toISOString().replace(/[:.]/g, "-");
42206
42253
  }
@@ -42211,8 +42258,8 @@ function startRecorder(opts) {
42211
42258
  return null;
42212
42259
  }
42213
42260
  const now = opts.now ?? Date.now;
42214
- const dir = import_node_path4.default.join(opts.dataDir, "ipc-recordings", opts.sessionId);
42215
- const filePath = import_node_path4.default.join(dir, `${tsForFilename(now())}.jsonl`);
42261
+ const dir = import_node_path5.default.join(opts.dataDir, "ipc-recordings", opts.sessionId);
42262
+ const filePath = import_node_path5.default.join(dir, `${tsForFilename(now())}.jsonl`);
42216
42263
  let stream = null;
42217
42264
  let closedResolve;
42218
42265
  const closed = new Promise((resolve6) => {
@@ -42221,8 +42268,8 @@ function startRecorder(opts) {
42221
42268
  let exited = false;
42222
42269
  const ensureStream = () => {
42223
42270
  if (stream) return stream;
42224
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
42225
- stream = import_node_fs4.default.createWriteStream(filePath, { flags: "a" });
42271
+ import_node_fs5.default.mkdirSync(dir, { recursive: true });
42272
+ stream = import_node_fs5.default.createWriteStream(filePath, { flags: "a" });
42226
42273
  stream.on("close", () => closedResolve());
42227
42274
  return stream;
42228
42275
  };
@@ -42267,6 +42314,7 @@ function encodeAllowWithInputControlResponse(requestId, updatedInput) {
42267
42314
  return JSON.stringify(payload) + "\n";
42268
42315
  }
42269
42316
  var DEFAULT_WAIT_STOP_TIMEOUT_MS = 3e3;
42317
+ var SUBAGENT_ACTIVE_WINDOW_MS = 3e5;
42270
42318
  var SessionRunner = class {
42271
42319
  constructor(initial, hooks) {
42272
42320
  this.hooks = hooks;
@@ -42320,14 +42368,14 @@ var SessionRunner = class {
42320
42368
  // 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
42321
42369
  // curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
42322
42370
  getDaemonUrl: this.hooks.getDaemonUrl,
42323
- // Persona dispatch:透传 dispatch.mcp.json 路径闭包 + B→dispatchId 反查闭包。
42324
- // reducer.buildSpawnContext CLAWD_DISPATCH_ID env / 派生 SpawnContext.dispatchMcpConfigPath
42371
+ // Persona dispatch:透传 dispatch.mcp.json 路径闭包,让 cc spawn 加 --mcp-config flag
42372
+ // 使两侧 cc 都能看到 personaDispatch / personaDispatchComplete tool(按 session 身份分工用哪个)。
42373
+ // dispatchId 不注 cc env——complete handler 用 sessionId 反查 in-flight dispatchId 配对。
42325
42374
  getDispatchMcpConfigPath: this.hooks.getDispatchMcpConfigPath,
42326
42375
  getShiftMcpConfigPath: this.hooks.getShiftMcpConfigPath,
42327
42376
  getInboxMcpConfigPath: this.hooks.getInboxMcpConfigPath,
42328
42377
  // Ticket MCP:透传 ticket.mcp.json 路径闭包(reducer 内做 persona-ticket-manager gating)
42329
42378
  getTicketMcpConfigPath: this.hooks.getTicketMcpConfigPath,
42330
- lookupDispatchByBSessionId: this.hooks.lookupDispatchByBSessionId,
42331
42379
  // ReadyGate v2:透传 mode 让 reducer send / ready-detected 分支决定走暂存队列还是直写
42332
42380
  mode: this.hooks.mode,
42333
42381
  // [RG-DBG] 注入 logger 让 reducer rgDbg 走 pino 进 clawd.log;定位完跟 rgDbg 一起删
@@ -42579,7 +42627,7 @@ var SessionRunner = class {
42579
42627
  if (existing) clearTimeout(existing);
42580
42628
  const timer = setTimeout(() => {
42581
42629
  this.idleKillTimers.delete(effect.sessionId);
42582
- this.input({ kind: "idle-kill-fired" });
42630
+ this.input({ kind: "idle-kill-fired", subagentActive: this.isSubagentActive() });
42583
42631
  }, effect.ms);
42584
42632
  timer.unref?.();
42585
42633
  this.idleKillTimers.set(effect.sessionId, timer);
@@ -42595,6 +42643,18 @@ var SessionRunner = class {
42595
42643
  }
42596
42644
  }
42597
42645
  }
42646
+ // idle-kill 到点判定:本 session 的 subagents/ 里是否还有 agent-*.jsonl 在近期写盘。
42647
+ // 新鲜(近 SUBAGENT_ACTIVE_WINDOW_MS 内有写)= 后台 subagent 还在干活 → 不该杀。
42648
+ // 没有 toolSessionId(SDK 模式 CC 内生 id)/ 无目录 / 无 agent 文件 → false(照常回收)。
42649
+ isSubagentActive() {
42650
+ const toolSessionId = this.state.file.toolSessionId;
42651
+ if (!toolSessionId) return false;
42652
+ const projectsRoot = import_node_path6.default.join(this.hooks.home ?? import_node_os4.default.homedir(), ".claude", "projects");
42653
+ const mtime = newestSubagentMtimeMs(projectsRoot, this.state.file.cwd, toolSessionId);
42654
+ if (mtime === null) return false;
42655
+ const now = (this.hooks.now ?? Date.now)();
42656
+ return now - mtime < SUBAGENT_ACTIVE_WINDOW_MS;
42657
+ }
42598
42658
  // 清空所有 idle-kill timer(runner dispose / proc 永久退出时调用)。
42599
42659
  // 不喂 idle-kill-fired —— dispose 路径不再翻 reducer 状态
42600
42660
  clearIdleKillTimers() {
@@ -42677,15 +42737,15 @@ function extractEditPath(input) {
42677
42737
  }
42678
42738
 
42679
42739
  // src/debug/pty-probe.ts
42680
- var import_node_fs5 = __toESM(require("fs"), 1);
42681
- var import_node_path5 = __toESM(require("path"), 1);
42740
+ var import_node_fs6 = __toESM(require("fs"), 1);
42741
+ var import_node_path7 = __toESM(require("path"), 1);
42682
42742
  var PROBE_DIR = "/tmp/clawd-probe";
42683
- var EVENTS_FILE = import_node_path5.default.join(PROBE_DIR, "events.jsonl");
42743
+ var EVENTS_FILE = import_node_path7.default.join(PROBE_DIR, "events.jsonl");
42684
42744
  var inited = false;
42685
42745
  function ensureDir() {
42686
42746
  if (inited) return true;
42687
42747
  try {
42688
- import_node_fs5.default.mkdirSync(PROBE_DIR, { recursive: true });
42748
+ import_node_fs6.default.mkdirSync(PROBE_DIR, { recursive: true });
42689
42749
  inited = true;
42690
42750
  return true;
42691
42751
  } catch {
@@ -42696,15 +42756,15 @@ function probeEvent(event, data = {}) {
42696
42756
  try {
42697
42757
  if (!ensureDir()) return;
42698
42758
  const line = JSON.stringify({ ts: Date.now(), event, ...data }) + "\n";
42699
- import_node_fs5.default.appendFileSync(EVENTS_FILE, line);
42759
+ import_node_fs6.default.appendFileSync(EVENTS_FILE, line);
42700
42760
  } catch {
42701
42761
  }
42702
42762
  }
42703
42763
  function probeDumpReplay(sessionId, payload) {
42704
42764
  try {
42705
42765
  if (!ensureDir()) return "";
42706
- const file = import_node_path5.default.join(PROBE_DIR, `replay-${sessionId}-${Date.now()}.ans`);
42707
- import_node_fs5.default.writeFileSync(file, payload, "utf8");
42766
+ const file = import_node_path7.default.join(PROBE_DIR, `replay-${sessionId}-${Date.now()}.ans`);
42767
+ import_node_fs6.default.writeFileSync(file, payload, "utf8");
42708
42768
  return file;
42709
42769
  } catch {
42710
42770
  return "";
@@ -42799,9 +42859,22 @@ You may read this file with your Read tool to understand context. You do not hav
42799
42859
 
42800
42860
  When done, call the MCP tool \`mcp__clawd-dispatch__personaDispatchComplete\` with your result:
42801
42861
  - Success: { text: "...", filePaths?: ["abs/path", ...] }
42802
- - Failure: { isFailure: true, reason: "..." }
42862
+ - Failure: { isFailure: true, reason: "..." }`;
42863
+ }
42864
+ function buildDispatchContinuationPack(args) {
42865
+ return `[Dispatched from owner \u2014 follow-up query]
42803
42866
 
42804
- dispatchId for this task: ${args.dispatchId}`;
42867
+ Although you reported back on the previous task, the owner has a follow-up question. Continue from where you left off.
42868
+
42869
+ Owner's new message:
42870
+ ${args.prompt}
42871
+
42872
+ Source conversation (jsonl, same path as before, may contain new messages):
42873
+ ${args.sourceJsonlPath}
42874
+
42875
+ When done, call \`mcp__clawd-dispatch__personaDispatchComplete\` again with your result:
42876
+ - Success: { text: "...", filePaths?: ["abs/path", ...] }
42877
+ - Failure: { isFailure: true, reason: "..." }`;
42805
42878
  }
42806
42879
  function derivePersonaSpawnCwd(file, personaRoot) {
42807
42880
  const personaId = file.ownerPersonaId;
@@ -42811,7 +42884,7 @@ function derivePersonaSpawnCwd(file, personaRoot) {
42811
42884
  `derivePersonaSpawnCwd: personaRoot missing for owner session ${file.sessionId} (ownerPersonaId=${personaId})`
42812
42885
  );
42813
42886
  }
42814
- return import_node_path6.default.join(personaRoot, safeFileName(personaId));
42887
+ return import_node_path8.default.join(personaRoot, safeFileName(personaId));
42815
42888
  }
42816
42889
  function makeInitialState(file, subSessionMeta) {
42817
42890
  return {
@@ -42936,10 +43009,10 @@ var SessionManager = class {
42936
43009
  // <dataDir>/sessions/ 列子目录 (排除 'default').
42937
43010
  listPersonaIdsOnDisk() {
42938
43011
  if (!this.deps.dataDir) return [];
42939
- const root = this.deps.storeFactory ? import_node_path6.default.join(this.deps.dataDir, "personas") : import_node_path6.default.join(this.deps.dataDir, "sessions");
43012
+ const root = this.deps.storeFactory ? import_node_path8.default.join(this.deps.dataDir, "personas") : import_node_path8.default.join(this.deps.dataDir, "sessions");
42940
43013
  let entries;
42941
43014
  try {
42942
- entries = import_node_fs6.default.readdirSync(root, { withFileTypes: true });
43015
+ entries = import_node_fs7.default.readdirSync(root, { withFileTypes: true });
42943
43016
  } catch (err) {
42944
43017
  const code = err?.code;
42945
43018
  if (code === "ENOENT") return [];
@@ -42952,7 +43025,7 @@ var SessionManager = class {
42952
43025
  // 只在 storeFactory 注入 (新布局) 下生效, 老布局无 guest 目录.
42953
43026
  listGuestCapIdsForPersona(personaId) {
42954
43027
  if (!this.deps.dataDir || !this.deps.storeFactory) return [];
42955
- const root = import_node_path6.default.join(
43028
+ const root = import_node_path8.default.join(
42956
43029
  this.deps.dataDir,
42957
43030
  "personas",
42958
43031
  personaId,
@@ -42962,7 +43035,7 @@ var SessionManager = class {
42962
43035
  );
42963
43036
  let entries;
42964
43037
  try {
42965
- entries = import_node_fs6.default.readdirSync(root, { withFileTypes: true });
43038
+ entries = import_node_fs7.default.readdirSync(root, { withFileTypes: true });
42966
43039
  } catch (err) {
42967
43040
  const code = err?.code;
42968
43041
  if (code === "ENOENT") return [];
@@ -43081,7 +43154,7 @@ var SessionManager = class {
43081
43154
  callerDisplayName
43082
43155
  );
43083
43156
  if (subSessionMeta?.userWorkDir) {
43084
- import_node_fs6.default.mkdirSync(subSessionMeta.userWorkDir, { recursive: true });
43157
+ import_node_fs7.default.mkdirSync(subSessionMeta.userWorkDir, { recursive: true });
43085
43158
  }
43086
43159
  if (scope.kind === "persona" && scope.mode === "guest") {
43087
43160
  if (!this.deps.personaRoot || !subSessionMeta?.userWorkDir) {
@@ -43092,8 +43165,8 @@ var SessionManager = class {
43092
43165
  const base = this.deps.personaStore?.readSandboxSettings(scope.personaId) ?? null;
43093
43166
  const settings = composeGuestSandbox(base, subSessionMeta.userWorkDir, file.cwd);
43094
43167
  subSessionMeta.extraSettings = JSON.stringify(settings);
43095
- const home = import_node_os3.default.homedir();
43096
- const expand = (p2) => p2 === "~" ? home : p2.startsWith("~/") ? import_node_path6.default.join(home, p2.slice(2)) : p2;
43168
+ const home = import_node_os5.default.homedir();
43169
+ const expand = (p2) => p2 === "~" ? home : p2.startsWith("~/") ? import_node_path8.default.join(home, p2.slice(2)) : p2;
43097
43170
  const codexCfg = this.deps.personaStore?.readCodexSandboxSettings(scope.personaId) ?? null;
43098
43171
  subSessionMeta.codexSandbox = {
43099
43172
  writableRoots: [subSessionMeta.userWorkDir, ...(codexCfg?.writableRoots ?? []).map(expand)],
@@ -43122,13 +43195,13 @@ var SessionManager = class {
43122
43195
  // 单栏 refactor (spec 2026-06-02 §5.1): 透传 daemon HTTP RPC base URL 闭包,
43123
43196
  // reducer 把它注入 cc 子进程 env CLAWD_DAEMON_URL.
43124
43197
  getDaemonUrl: this.deps.getDaemonUrl,
43125
- // Persona dispatch (Task 7): dispatch.mcp.json 路径 + B sessionId → dispatchId 反查
43126
- // 闭包透传给 reducer,让 cc spawn 加 --mcp-config flag + B session CLAWD_DISPATCH_ID env.
43198
+ // Persona dispatch: dispatch.mcp.json 路径闭包透传给 reducer,让 cc spawn
43199
+ // --mcp-config flag dispatch MCP server(两侧 cc 都挂,按 session 身份分工调 tool)。
43200
+ // dispatchId 不再走 cc env——complete handler 用 sessionId 反查 in-flight dispatchId 配对。
43127
43201
  getDispatchMcpConfigPath: this.deps.dispatchMcpConfigPath ? () => this.deps.dispatchMcpConfigPath ?? null : void 0,
43128
43202
  getTicketMcpConfigPath: this.deps.ticketMcpConfigPath ? () => this.deps.ticketMcpConfigPath ?? null : void 0,
43129
43203
  getShiftMcpConfigPath: this.deps.shiftMcpConfigPath ? () => this.deps.shiftMcpConfigPath ?? null : void 0,
43130
43204
  getInboxMcpConfigPath: this.deps.inboxMcpConfigPath ? () => this.deps.inboxMcpConfigPath ?? null : void 0,
43131
- lookupDispatchByBSessionId: this.deps.personaDispatchManager ? (bSid) => this.deps.personaDispatchManager?.lookupDispatchByBSessionId(bSid) : void 0,
43132
43205
  // file-sharing (spec §6 PR 3):闭包 scope + sessionId,runner 只暴露 tool/relPath/cwd
43133
43206
  onFileEdit: attachmentGroup ? (input) => attachmentGroup.onFileEdit({
43134
43207
  scope,
@@ -43246,7 +43319,7 @@ var SessionManager = class {
43246
43319
  throw new ClawdError(ERROR_CODES.INVALID_CWD, "cwd required when ownerPersonaId is absent");
43247
43320
  }
43248
43321
  try {
43249
- const stat = import_node_fs6.default.statSync(cwd);
43322
+ const stat = import_node_fs7.default.statSync(cwd);
43250
43323
  if (!stat.isDirectory()) throw new Error("not dir");
43251
43324
  } catch {
43252
43325
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `cwd not a directory: ${cwd}`);
@@ -43777,7 +43850,7 @@ var SessionManager = class {
43777
43850
  */
43778
43851
  createForScope(args) {
43779
43852
  try {
43780
- const stat = import_node_fs6.default.statSync(args.cwd);
43853
+ const stat = import_node_fs7.default.statSync(args.cwd);
43781
43854
  if (!stat.isDirectory()) throw new Error("not dir");
43782
43855
  } catch {
43783
43856
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `cwd not a directory: ${args.cwd}`);
@@ -43968,10 +44041,11 @@ var SessionManager = class {
43968
44041
  * Persona dispatch: 启动 B session 并投递任务包当第一条 user message。
43969
44042
  *
43970
44043
  * - 复用 persona-owner 创建路径(cwd=personaDir、scope=persona-owner、不沙箱)
43971
- * - SessionFile 上写 dispatchedFromSessionId 关联回 A(UI 折叠用;mirror 一侧 strip
43972
- * - 跟 PersonaDispatchManager 双向登记 dispatchId ↔ B sessionId,让后续 reducer
43973
- * buildSpawnContext 能反查到 dispatchId CLAWD_DISPATCH_ID env
43974
- * - inject-owner-text 任务包;包尾教 B personaDispatchComplete tool 回传
44044
+ * - SessionFile 上写 dispatchedFromSessionId 关联回 A(UI 折叠用;mirror 一侧 strip
44045
+ * targetSessionId 复用路径也按此字段校验寻址来源)
44046
+ * - PersonaDispatchManager 登记 dispatchId B sessionId(complete handler 用
44047
+ * findInflightDispatchByBSessionId 反查回 dispatchId 配对回 A waiter)
44048
+ * - 投任务包;包尾教 B 用 personaDispatchComplete tool 回传
43975
44049
  *
43976
44050
  * cwd 派生与 SessionManager.create 的 owner persona 路径一致(derivePersonaSpawnCwd)。
43977
44051
  */
@@ -43993,7 +44067,7 @@ var SessionManager = class {
43993
44067
  personaId: args.targetPersona,
43994
44068
  mode: "owner"
43995
44069
  };
43996
- const cwd = import_node_path6.default.join(this.deps.personaRoot, safeFileName(args.targetPersona));
44070
+ const cwd = import_node_path8.default.join(this.deps.personaRoot, safeFileName(args.targetPersona));
43997
44071
  const now = (/* @__PURE__ */ new Date()).toISOString();
43998
44072
  const file = {
43999
44073
  sessionId,
@@ -44026,8 +44100,7 @@ var SessionManager = class {
44026
44100
  });
44027
44101
  const taskPack = buildDispatchTaskPack({
44028
44102
  prompt: args.prompt,
44029
- sourceJsonlPath: args.sourceJsonlPath,
44030
- dispatchId: args.dispatchId
44103
+ sourceJsonlPath: args.sourceJsonlPath
44031
44104
  });
44032
44105
  runner.input({ kind: "command", command: { kind: "send", text: taskPack } });
44033
44106
  this.deps.logger?.info("dispatch.createDispatchedSession.task-pack-sent", {
@@ -44036,6 +44109,59 @@ var SessionManager = class {
44036
44109
  });
44037
44110
  return { sessionId };
44038
44111
  }
44112
+ /**
44113
+ * targetSessionId 精确寻址已知 B session 复用其上下文。投 continuation pack 给 runner。
44114
+ *
44115
+ * 关键差异 vs createDispatchedSession:
44116
+ * - 不 newSessionId() / 不写 SessionFile —— B session 复用(走 findOwnedSession)
44117
+ * - 权限校验:本地 dispatch → SessionFile.dispatchedFromSessionId === A 的 sessionId;
44118
+ * 跨设备 B 角色 → SessionFile.creatorPrincipalId === A 的 deviceId
44119
+ * - 跟 PersonaDispatchManager 重新登记(新 dispatchId 复用同 bSessionId)
44120
+ * - ensureRunnerForScope: cc 活复用;cc 死时首次 send 触发 spawn,jsonl 已存在自动 --resume
44121
+ *
44122
+ * @param callerIdentity 权限校验依据:
44123
+ * - kind='local': A 的 sessionId(本机 dispatch),校验 dispatchedFromSessionId 匹配
44124
+ * - kind='guest': A 的 deviceId(跨设备转发进来的 A),校验 creatorPrincipalId 匹配
44125
+ */
44126
+ resumeDispatchedSession(args) {
44127
+ if (!this.deps.personaDispatchManager) {
44128
+ throw new Error("resumeDispatchedSession: personaDispatchManager missing in ManagerDeps");
44129
+ }
44130
+ const file = this.findOwnedSession(args.bSessionId);
44131
+ if (!file) {
44132
+ throw new Error(`resumeDispatchedSession: B session ${args.bSessionId} not found`);
44133
+ }
44134
+ if (args.callerIdentity.kind === "local") {
44135
+ if (file.dispatchedFromSessionId !== args.callerIdentity.sourceSessionId) {
44136
+ throw new Error(
44137
+ `resumeDispatchedSession: session ${args.bSessionId} not dispatched by caller (dispatchedFromSessionId mismatch)`
44138
+ );
44139
+ }
44140
+ } else {
44141
+ if (file.creatorPrincipalId !== args.callerIdentity.sourcePrincipalId) {
44142
+ throw new Error(
44143
+ `resumeDispatchedSession: session ${args.bSessionId} not dispatched by caller device (creatorPrincipalId mismatch)`
44144
+ );
44145
+ }
44146
+ }
44147
+ const scope = this.scopeForFile(file);
44148
+ this.deps.personaDispatchManager.registerBSession(args.dispatchId, args.bSessionId);
44149
+ this.deps.logger?.info("dispatch.resumeDispatchedSession.registered", {
44150
+ dispatchId: args.dispatchId,
44151
+ bSessionId: args.bSessionId
44152
+ });
44153
+ const runner = this.ensureRunnerForScope(file, scope);
44154
+ const pack = buildDispatchContinuationPack({
44155
+ prompt: args.prompt,
44156
+ sourceJsonlPath: args.sourceJsonlPath
44157
+ });
44158
+ runner.input({ kind: "command", command: { kind: "send", text: pack } });
44159
+ this.deps.logger?.info("dispatch.resumeDispatchedSession.continuation-sent", {
44160
+ dispatchId: args.dispatchId,
44161
+ bSessionId: args.bSessionId
44162
+ });
44163
+ return { sessionId: args.bSessionId };
44164
+ }
44039
44165
  /**
44040
44166
  * shift v1 (spec 2026-06-24-clawd-shift):到点 fire 时由 ShiftScheduler 调,
44041
44167
  * 起一个新 cc session 跑 prompt(fire-and-forget,不绑 dispatchId / 不挂 caller)。
@@ -44058,7 +44184,7 @@ var SessionManager = class {
44058
44184
  personaId: args.targetPersona,
44059
44185
  mode: "owner"
44060
44186
  };
44061
- const cwd = import_node_path6.default.join(this.deps.personaRoot, safeFileName(args.targetPersona));
44187
+ const cwd = import_node_path8.default.join(this.deps.personaRoot, safeFileName(args.targetPersona));
44062
44188
  const now = (/* @__PURE__ */ new Date()).toISOString();
44063
44189
  const file = {
44064
44190
  sessionId,
@@ -44540,28 +44666,28 @@ var SessionManager = class {
44540
44666
  };
44541
44667
 
44542
44668
  // src/persona/store.ts
44543
- var fs7 = __toESM(require("fs"), 1);
44544
- var path9 = __toESM(require("path"), 1);
44669
+ var fs8 = __toESM(require("fs"), 1);
44670
+ var path11 = __toESM(require("path"), 1);
44545
44671
  init_protocol();
44546
44672
  var PersonaStore = class {
44547
44673
  constructor(root) {
44548
44674
  this.root = root;
44549
- fs7.mkdirSync(root, { recursive: true });
44675
+ fs8.mkdirSync(root, { recursive: true });
44550
44676
  }
44551
44677
  root;
44552
44678
  personaDir(personaId) {
44553
- return path9.join(this.root, safeFileName(personaId));
44679
+ return path11.join(this.root, safeFileName(personaId));
44554
44680
  }
44555
44681
  metaPath(personaId) {
44556
- return path9.join(this.personaDir(personaId), ".clawd", "persona.json");
44682
+ return path11.join(this.personaDir(personaId), ".clawd", "persona.json");
44557
44683
  }
44558
44684
  claudeMdPath(personaId) {
44559
- return path9.join(this.personaDir(personaId), "CLAUDE.md");
44685
+ return path11.join(this.personaDir(personaId), "CLAUDE.md");
44560
44686
  }
44561
44687
  // codex 原生读 cwd 的 AGENTS.md。人格双写镜像:claude 读 CLAUDE.md、codex 读 AGENTS.md,
44562
44688
  // 两份内容恒一致,persona 切 tool 零迁移。
44563
44689
  agentsMdPath(personaId) {
44564
- return path9.join(this.personaDir(personaId), "AGENTS.md");
44690
+ return path11.join(this.personaDir(personaId), "AGENTS.md");
44565
44691
  }
44566
44692
  /**
44567
44693
  * persona 级 sandbox base 落盘路径 —— 故意放 `.clawd/` 而非 `.claude/`,让 CC 的 project
@@ -44570,11 +44696,11 @@ var PersonaStore = class {
44570
44696
  * spawn 前 per-guest 动态拼到各自 session 目录的那份(base + 强制底座 + 本 guest userWorkDir carve)。
44571
44697
  */
44572
44698
  sandboxSettingsPath(personaId) {
44573
- return path9.join(this.personaDir(personaId), ".clawd", "sandbox-settings.json");
44699
+ return path11.join(this.personaDir(personaId), ".clawd", "sandbox-settings.json");
44574
44700
  }
44575
44701
  write(persona, personality) {
44576
44702
  const dir = this.personaDir(persona.personaId);
44577
- fs7.mkdirSync(path9.join(dir, ".clawd"), { recursive: true });
44703
+ fs8.mkdirSync(path11.join(dir, ".clawd"), { recursive: true });
44578
44704
  this.atomicWrite(this.claudeMdPath(persona.personaId), personality);
44579
44705
  this.atomicWrite(this.agentsMdPath(persona.personaId), personality);
44580
44706
  this.writeSandboxSettings(persona.personaId, buildGuestSettingsV1());
@@ -44593,9 +44719,9 @@ var PersonaStore = class {
44593
44719
  ensureAgentsMirror(personaId) {
44594
44720
  const claudeMd = this.claudeMdPath(personaId);
44595
44721
  const agentsMd = this.agentsMdPath(personaId);
44596
- if (!fs7.existsSync(claudeMd)) return false;
44597
- if (fs7.existsSync(agentsMd)) return false;
44598
- this.atomicWrite(agentsMd, fs7.readFileSync(claudeMd, "utf8"));
44722
+ if (!fs8.existsSync(claudeMd)) return false;
44723
+ if (fs8.existsSync(agentsMd)) return false;
44724
+ this.atomicWrite(agentsMd, fs8.readFileSync(claudeMd, "utf8"));
44599
44725
  return true;
44600
44726
  }
44601
44727
  /**
@@ -44619,22 +44745,22 @@ var PersonaStore = class {
44619
44745
  return { ...s, permissions: { ...s.permissions ?? {}, deny: [...prev, rule] } };
44620
44746
  }
44621
44747
  codexSandboxSettingsPath(personaId) {
44622
- return path9.join(this.personaDir(personaId), ".clawd", "codex-sandbox.json");
44748
+ return path11.join(this.personaDir(personaId), ".clawd", "codex-sandbox.json");
44623
44749
  }
44624
44750
  /** 读 codex-sandbox.json;不存在/损坏 → null。 */
44625
44751
  readCodexSandboxSettings(personaId) {
44626
44752
  const p2 = this.codexSandboxSettingsPath(personaId);
44627
- if (!fs7.existsSync(p2)) return null;
44753
+ if (!fs8.existsSync(p2)) return null;
44628
44754
  try {
44629
- return CodexSandboxSettingsSchema.parse(JSON.parse(fs7.readFileSync(p2, "utf8")));
44755
+ return CodexSandboxSettingsSchema.parse(JSON.parse(fs8.readFileSync(p2, "utf8")));
44630
44756
  } catch {
44631
44757
  return null;
44632
44758
  }
44633
44759
  }
44634
44760
  /** 覆盖写 codex-sandbox.json(seed/migrate 用)。 */
44635
44761
  writeCodexSandboxSettings(personaId, settings) {
44636
- const dir = path9.join(this.personaDir(personaId), ".clawd");
44637
- fs7.mkdirSync(dir, { recursive: true });
44762
+ const dir = path11.join(this.personaDir(personaId), ".clawd");
44763
+ fs8.mkdirSync(dir, { recursive: true });
44638
44764
  this.atomicWrite(this.codexSandboxSettingsPath(personaId), JSON.stringify(settings, null, 2));
44639
44765
  }
44640
44766
  writeMeta(persona) {
@@ -44642,8 +44768,8 @@ var PersonaStore = class {
44642
44768
  }
44643
44769
  read(personaId) {
44644
44770
  const p2 = this.metaPath(personaId);
44645
- if (!fs7.existsSync(p2)) return null;
44646
- const raw = JSON.parse(fs7.readFileSync(p2, "utf8"));
44771
+ if (!fs8.existsSync(p2)) return null;
44772
+ const raw = JSON.parse(fs8.readFileSync(p2, "utf8"));
44647
44773
  if (raw && typeof raw === "object" && "tokenMap" in raw) {
44648
44774
  delete raw.tokenMap;
44649
44775
  this.atomicWrite(p2, JSON.stringify(raw, null, 2));
@@ -44651,13 +44777,13 @@ var PersonaStore = class {
44651
44777
  return PersonaFileSchema.parse(raw);
44652
44778
  }
44653
44779
  has(personaId) {
44654
- return fs7.existsSync(this.metaPath(personaId));
44780
+ return fs8.existsSync(this.metaPath(personaId));
44655
44781
  }
44656
44782
  readPersonality(personaId) {
44657
44783
  const claudeMd = this.claudeMdPath(personaId);
44658
- if (fs7.existsSync(claudeMd)) return fs7.readFileSync(claudeMd, "utf8");
44784
+ if (fs8.existsSync(claudeMd)) return fs8.readFileSync(claudeMd, "utf8");
44659
44785
  const agentsMd = this.agentsMdPath(personaId);
44660
- if (fs7.existsSync(agentsMd)) return fs7.readFileSync(agentsMd, "utf8");
44786
+ if (fs8.existsSync(agentsMd)) return fs8.readFileSync(agentsMd, "utf8");
44661
44787
  return null;
44662
44788
  }
44663
44789
  /**
@@ -44666,23 +44792,23 @@ var PersonaStore = class {
44666
44792
  */
44667
44793
  readSandboxSettings(personaId) {
44668
44794
  const p2 = this.sandboxSettingsPath(personaId);
44669
- if (!fs7.existsSync(p2)) return null;
44795
+ if (!fs8.existsSync(p2)) return null;
44670
44796
  try {
44671
- return JSON.parse(fs7.readFileSync(p2, "utf8"));
44797
+ return JSON.parse(fs8.readFileSync(p2, "utf8"));
44672
44798
  } catch {
44673
44799
  return null;
44674
44800
  }
44675
44801
  }
44676
44802
  /** Persona 私有 skills 目录路径:<personaDir>/.claude/skills */
44677
44803
  skillsDir(personaId) {
44678
- return path9.join(this.personaDir(personaId), ".claude", "skills");
44804
+ return path11.join(this.personaDir(personaId), ".claude", "skills");
44679
44805
  }
44680
44806
  /**
44681
44807
  * Claude Code 项目级 settings 路径:`<personaDir>/.claude/settings.json`。
44682
44808
  * 这里只读 `enabledPlugins` 字段,由 owner 通过 CC `/plugin` 之类命令维护,daemon 不写。
44683
44809
  */
44684
44810
  claudeSettingsPath(personaId) {
44685
- return path9.join(this.personaDir(personaId), ".claude", "settings.json");
44811
+ return path11.join(this.personaDir(personaId), ".claude", "settings.json");
44686
44812
  }
44687
44813
  /**
44688
44814
  * 读取 persona 的 `.claude/settings.json` 中 `enabledPlugins` map,把 value === true
@@ -44697,10 +44823,10 @@ var PersonaStore = class {
44697
44823
  */
44698
44824
  readEnabledPlugins(personaId) {
44699
44825
  const p2 = this.claudeSettingsPath(personaId);
44700
- if (!fs7.existsSync(p2)) return [];
44826
+ if (!fs8.existsSync(p2)) return [];
44701
44827
  let raw;
44702
44828
  try {
44703
- raw = JSON.parse(fs7.readFileSync(p2, "utf8"));
44829
+ raw = JSON.parse(fs8.readFileSync(p2, "utf8"));
44704
44830
  } catch {
44705
44831
  return [];
44706
44832
  }
@@ -44714,22 +44840,22 @@ var PersonaStore = class {
44714
44840
  return out;
44715
44841
  }
44716
44842
  list() {
44717
- if (!fs7.existsSync(this.root)) return [];
44718
- return fs7.readdirSync(this.root).filter((name) => {
44719
- return fs7.existsSync(path9.join(this.root, name, ".clawd", "persona.json"));
44843
+ if (!fs8.existsSync(this.root)) return [];
44844
+ return fs8.readdirSync(this.root).filter((name) => {
44845
+ return fs8.existsSync(path11.join(this.root, name, ".clawd", "persona.json"));
44720
44846
  });
44721
44847
  }
44722
44848
  remove(personaId) {
44723
44849
  const dir = this.personaDir(personaId);
44724
- if (fs7.existsSync(dir)) fs7.rmSync(dir, { recursive: true, force: true });
44850
+ if (fs8.existsSync(dir)) fs8.rmSync(dir, { recursive: true, force: true });
44725
44851
  }
44726
44852
  personaDirPath(personaId) {
44727
44853
  return this.personaDir(personaId);
44728
44854
  }
44729
44855
  atomicWrite(file, content) {
44730
44856
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
44731
- fs7.writeFileSync(tmp, content, { mode: 384 });
44732
- fs7.renameSync(tmp, file);
44857
+ fs8.writeFileSync(tmp, content, { mode: 384 });
44858
+ fs8.renameSync(tmp, file);
44733
44859
  }
44734
44860
  };
44735
44861
 
@@ -44775,9 +44901,9 @@ var PersonaRegistry = class {
44775
44901
  var import_node_crypto3 = __toESM(require("crypto"), 1);
44776
44902
 
44777
44903
  // src/skills/scanner.ts
44778
- var import_node_fs7 = __toESM(require("fs"), 1);
44779
- var import_node_os4 = __toESM(require("os"), 1);
44780
- var import_node_path7 = __toESM(require("path"), 1);
44904
+ var import_node_fs8 = __toESM(require("fs"), 1);
44905
+ var import_node_os6 = __toESM(require("os"), 1);
44906
+ var import_node_path9 = __toESM(require("path"), 1);
44781
44907
 
44782
44908
  // src/skills/frontmatter.ts
44783
44909
  var STRIP_QUOTES = /^["']|["']$/g;
@@ -44886,7 +45012,7 @@ function parseDescription(content) {
44886
45012
  }
44887
45013
  function isDirLikeSync(p2) {
44888
45014
  try {
44889
- return import_node_fs7.default.statSync(p2).isDirectory();
45015
+ return import_node_fs8.default.statSync(p2).isDirectory();
44890
45016
  } catch {
44891
45017
  return false;
44892
45018
  }
@@ -44894,19 +45020,19 @@ function isDirLikeSync(p2) {
44894
45020
  function scanSkillDir(dir, source, seen, out, pluginName) {
44895
45021
  let entries;
44896
45022
  try {
44897
- entries = import_node_fs7.default.readdirSync(dir, { withFileTypes: true });
45023
+ entries = import_node_fs8.default.readdirSync(dir, { withFileTypes: true });
44898
45024
  } catch {
44899
45025
  return;
44900
45026
  }
44901
45027
  for (const ent of entries) {
44902
- const entryPath = import_node_path7.default.join(dir, ent.name);
45028
+ const entryPath = import_node_path9.default.join(dir, ent.name);
44903
45029
  if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
44904
45030
  let content;
44905
45031
  try {
44906
- content = import_node_fs7.default.readFileSync(import_node_path7.default.join(entryPath, "SKILL.md"), "utf8");
45032
+ content = import_node_fs8.default.readFileSync(import_node_path9.default.join(entryPath, "SKILL.md"), "utf8");
44907
45033
  } catch {
44908
45034
  try {
44909
- content = import_node_fs7.default.readFileSync(import_node_path7.default.join(entryPath, "skill.md"), "utf8");
45035
+ content = import_node_fs8.default.readFileSync(import_node_path9.default.join(entryPath, "skill.md"), "utf8");
44910
45036
  } catch {
44911
45037
  continue;
44912
45038
  }
@@ -44930,26 +45056,26 @@ function listSkillsForDir(dir, source) {
44930
45056
  function scanCommandDir(dir, source, seen, out, pluginName) {
44931
45057
  let entries;
44932
45058
  try {
44933
- entries = import_node_fs7.default.readdirSync(dir, { withFileTypes: true });
45059
+ entries = import_node_fs8.default.readdirSync(dir, { withFileTypes: true });
44934
45060
  } catch {
44935
45061
  return;
44936
45062
  }
44937
45063
  for (const ent of entries) {
44938
- const entryPath = import_node_path7.default.join(dir, ent.name);
45064
+ const entryPath = import_node_path9.default.join(dir, ent.name);
44939
45065
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
44940
45066
  const ns = ent.name;
44941
45067
  let subEntries;
44942
45068
  try {
44943
- subEntries = import_node_fs7.default.readdirSync(entryPath, { withFileTypes: true });
45069
+ subEntries = import_node_fs8.default.readdirSync(entryPath, { withFileTypes: true });
44944
45070
  } catch {
44945
45071
  continue;
44946
45072
  }
44947
45073
  for (const se of subEntries) {
44948
45074
  if (!se.name.endsWith(".md")) continue;
44949
- const sePath = import_node_path7.default.join(entryPath, se.name);
45075
+ const sePath = import_node_path9.default.join(entryPath, se.name);
44950
45076
  let content;
44951
45077
  try {
44952
- content = import_node_fs7.default.readFileSync(sePath, "utf8");
45078
+ content = import_node_fs8.default.readFileSync(sePath, "utf8");
44953
45079
  } catch {
44954
45080
  continue;
44955
45081
  }
@@ -44966,7 +45092,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
44966
45092
  } else if (ent.name.endsWith(".md")) {
44967
45093
  let content;
44968
45094
  try {
44969
- content = import_node_fs7.default.readFileSync(entryPath, "utf8");
45095
+ content = import_node_fs8.default.readFileSync(entryPath, "utf8");
44970
45096
  } catch {
44971
45097
  continue;
44972
45098
  }
@@ -44982,10 +45108,10 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
44982
45108
  }
44983
45109
  }
44984
45110
  function readInstalledPlugins(home) {
44985
- const file = import_node_path7.default.join(home, ".claude", "plugins", "installed_plugins.json");
45111
+ const file = import_node_path9.default.join(home, ".claude", "plugins", "installed_plugins.json");
44986
45112
  let raw;
44987
45113
  try {
44988
- raw = import_node_fs7.default.readFileSync(file, "utf8");
45114
+ raw = import_node_fs8.default.readFileSync(file, "utf8");
44989
45115
  } catch {
44990
45116
  return [];
44991
45117
  }
@@ -45010,7 +45136,7 @@ var SkillsScanner = class {
45010
45136
  home;
45011
45137
  extraPluginRoots;
45012
45138
  constructor(opts = {}) {
45013
- this.home = opts.home ?? import_node_os4.default.homedir();
45139
+ this.home = opts.home ?? import_node_os6.default.homedir();
45014
45140
  this.extraPluginRoots = opts.extraPluginRoots ?? [];
45015
45141
  }
45016
45142
  /**
@@ -45033,14 +45159,14 @@ var SkillsScanner = class {
45033
45159
  });
45034
45160
  }
45035
45161
  const fsBlock = [];
45036
- scanSkillDir(import_node_path7.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
45037
- scanCommandDir(import_node_path7.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
45038
- scanSkillDir(import_node_path7.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
45039
- scanCommandDir(import_node_path7.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
45162
+ scanSkillDir(import_node_path9.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
45163
+ scanCommandDir(import_node_path9.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
45164
+ scanSkillDir(import_node_path9.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
45165
+ scanCommandDir(import_node_path9.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
45040
45166
  const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
45041
45167
  for (const { name, root } of plugins) {
45042
- scanSkillDir(import_node_path7.default.join(root, "skills"), "plugin", seen, fsBlock, name);
45043
- scanCommandDir(import_node_path7.default.join(root, "commands"), "plugin", seen, fsBlock, name);
45168
+ scanSkillDir(import_node_path9.default.join(root, "skills"), "plugin", seen, fsBlock, name);
45169
+ scanCommandDir(import_node_path9.default.join(root, "commands"), "plugin", seen, fsBlock, name);
45044
45170
  }
45045
45171
  fsBlock.sort((a, b2) => a.name < b2.name ? -1 : a.name > b2.name ? 1 : 0);
45046
45172
  return [...builtinBlock, ...fsBlock];
@@ -45146,8 +45272,8 @@ var PersonaManager = class {
45146
45272
  };
45147
45273
 
45148
45274
  // src/persona/seed.ts
45149
- var fs9 = __toESM(require("fs"), 1);
45150
- var path11 = __toESM(require("path"), 1);
45275
+ var fs10 = __toESM(require("fs"), 1);
45276
+ var path13 = __toESM(require("path"), 1);
45151
45277
  var import_node_url = require("url");
45152
45278
  var import_meta = {};
45153
45279
  var DEFAULT_BYPASS_PROFILE = {
@@ -45178,16 +45304,6 @@ var DEFAULT_PERSONAS = [
45178
45304
  public: false,
45179
45305
  sandboxProfile: DEFAULT_BYPASS_PROFILE
45180
45306
  },
45181
- {
45182
- // 工单管理员:纯操作员,按指令调 clawd-ticket MCP 增删改查 ticket。MCP 由 daemon 全局
45183
- // 注入(index.ts ticket-mcp wiring),persona 端不需要单独 .mcp.json。
45184
- personaId: "persona-ticket-manager",
45185
- label: "\u5DE5\u5355\u7BA1\u7406\u5458",
45186
- model: "opus",
45187
- iconKey: "assist",
45188
- public: false,
45189
- sandboxProfile: DEFAULT_BYPASS_PROFILE
45190
- },
45191
45307
  {
45192
45308
  personaId: "persona-feishu-assistant",
45193
45309
  label: "\u98DE\u4E66\u52A9\u7406",
@@ -45281,14 +45397,6 @@ var DEFAULT_PERSONAS = [
45281
45397
  public: false,
45282
45398
  sandboxProfile: DEFAULT_BYPASS_PROFILE
45283
45399
  },
45284
- {
45285
- personaId: "persona-bug-fixer",
45286
- label: "Bug \u4FEE\u590D\u5E08",
45287
- model: "opus",
45288
- iconKey: "debug",
45289
- public: false,
45290
- sandboxProfile: DEFAULT_BYPASS_PROFILE
45291
- },
45292
45400
  {
45293
45401
  // HTML PPT 制作师:把想法/文字/旧 PPT 转成单文件 HTML 演示稿
45294
45402
  // bundle 含 frontend-slides skill(MIT,Zara Zhang),daemon ship 进 .claude/skills/
@@ -45335,24 +45443,24 @@ function bundleSiblingFromArgv(argv1, sibling) {
45335
45443
  if (!argv1) return null;
45336
45444
  let real = argv1;
45337
45445
  try {
45338
- real = fs9.realpathSync(argv1);
45446
+ real = fs10.realpathSync(argv1);
45339
45447
  } catch {
45340
45448
  }
45341
- return path11.resolve(path11.dirname(real), sibling);
45449
+ return path13.resolve(path13.dirname(real), sibling);
45342
45450
  }
45343
45451
  function findDefaultsRoot(logger) {
45344
45452
  const candidates = [];
45345
45453
  try {
45346
- const here = path11.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
45347
- candidates.push(path11.resolve(here, "defaults"));
45348
- candidates.push(path11.resolve(here, "persona-defaults"));
45454
+ const here = path13.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
45455
+ candidates.push(path13.resolve(here, "defaults"));
45456
+ candidates.push(path13.resolve(here, "persona-defaults"));
45349
45457
  } catch {
45350
45458
  }
45351
45459
  const fromArgv = bundleSiblingFromArgv(process.argv[1], "persona-defaults");
45352
45460
  if (fromArgv) candidates.push(fromArgv);
45353
45461
  for (const c of candidates) {
45354
45462
  try {
45355
- if (fs9.statSync(c).isDirectory()) {
45463
+ if (fs10.statSync(c).isDirectory()) {
45356
45464
  logger?.info("persona.defaults-root.resolved", { root: c });
45357
45465
  return c;
45358
45466
  }
@@ -45369,8 +45477,8 @@ function seedDefaultPersonas(args) {
45369
45477
  args.logger.info("persona.seed.skip", { personaId: entry.personaId, reason: "exists" });
45370
45478
  continue;
45371
45479
  }
45372
- const bundleDir = path11.join(args.defaultsRoot, entry.personaId);
45373
- if (!fs9.existsSync(bundleDir)) {
45480
+ const bundleDir = path13.join(args.defaultsRoot, entry.personaId);
45481
+ if (!fs10.existsSync(bundleDir)) {
45374
45482
  args.logger.warn("persona.seed.skip", {
45375
45483
  personaId: entry.personaId,
45376
45484
  reason: "bundle-missing",
@@ -45378,8 +45486,8 @@ function seedDefaultPersonas(args) {
45378
45486
  });
45379
45487
  continue;
45380
45488
  }
45381
- const claudeMdPath = path11.join(bundleDir, "CLAUDE.md");
45382
- if (!fs9.existsSync(claudeMdPath)) {
45489
+ const claudeMdPath = path13.join(bundleDir, "CLAUDE.md");
45490
+ if (!fs10.existsSync(claudeMdPath)) {
45383
45491
  args.logger.warn("persona.seed.skip", {
45384
45492
  personaId: entry.personaId,
45385
45493
  reason: "no-CLAUDE.md",
@@ -45387,7 +45495,7 @@ function seedDefaultPersonas(args) {
45387
45495
  });
45388
45496
  continue;
45389
45497
  }
45390
- const personality = fs9.readFileSync(claudeMdPath, "utf8");
45498
+ const personality = fs10.readFileSync(claudeMdPath, "utf8");
45391
45499
  const now = Date.now();
45392
45500
  const persona = {
45393
45501
  personaId: entry.personaId,
@@ -45413,17 +45521,17 @@ function seedDefaultPersonas(args) {
45413
45521
  }
45414
45522
  }
45415
45523
  function skipNodeModulesUnder(srcRoot) {
45416
- return (src) => !path11.relative(srcRoot, src).split(path11.sep).includes("node_modules");
45524
+ return (src) => !path13.relative(srcRoot, src).split(path13.sep).includes("node_modules");
45417
45525
  }
45418
45526
  function copyBundleExtras(srcDir, dstDir) {
45419
- for (const entry of fs9.readdirSync(srcDir, { withFileTypes: true })) {
45527
+ for (const entry of fs10.readdirSync(srcDir, { withFileTypes: true })) {
45420
45528
  if (entry.name === "CLAUDE.md" || entry.name === ".clawd") continue;
45421
- const srcPath = path11.join(srcDir, entry.name);
45422
- const dstPath = path11.join(dstDir, entry.name);
45529
+ const srcPath = path13.join(srcDir, entry.name);
45530
+ const dstPath = path13.join(dstDir, entry.name);
45423
45531
  if (entry.isDirectory()) {
45424
- fs9.cpSync(srcPath, dstPath, { recursive: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
45532
+ fs10.cpSync(srcPath, dstPath, { recursive: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
45425
45533
  } else if (entry.isFile()) {
45426
- fs9.copyFileSync(srcPath, dstPath);
45534
+ fs10.copyFileSync(srcPath, dstPath);
45427
45535
  }
45428
45536
  }
45429
45537
  }
@@ -45431,16 +45539,16 @@ var DAEMON_MANAGED_PATHS = ["extension-kit", "CLAUDE.md", ".mcp.json"];
45431
45539
  function refreshDaemonManagedDirs(args) {
45432
45540
  const entries = args.entries ?? DEFAULT_PERSONAS;
45433
45541
  for (const entry of entries) {
45434
- const bundleDir = path11.join(args.defaultsRoot, entry.personaId);
45435
- if (!fs9.existsSync(bundleDir)) continue;
45542
+ const bundleDir = path13.join(args.defaultsRoot, entry.personaId);
45543
+ if (!fs10.existsSync(bundleDir)) continue;
45436
45544
  const personaDir = args.store.personaDirPath(entry.personaId);
45437
- if (!fs9.existsSync(personaDir)) continue;
45545
+ if (!fs10.existsSync(personaDir)) continue;
45438
45546
  for (const relPath of DAEMON_MANAGED_PATHS) {
45439
- const srcPath = path11.join(bundleDir, relPath);
45440
- if (!fs9.existsSync(srcPath)) continue;
45441
- const dstPath = path11.join(personaDir, relPath);
45547
+ const srcPath = path13.join(bundleDir, relPath);
45548
+ if (!fs10.existsSync(srcPath)) continue;
45549
+ const dstPath = path13.join(personaDir, relPath);
45442
45550
  try {
45443
- fs9.cpSync(srcPath, dstPath, { recursive: true, force: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
45551
+ fs10.cpSync(srcPath, dstPath, { recursive: true, force: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
45444
45552
  args.logger.info("persona.refresh.synced", {
45445
45553
  personaId: entry.personaId,
45446
45554
  path: relPath
@@ -45500,15 +45608,15 @@ function migrateCodexSandbox(args) {
45500
45608
  function findDeployKitRoot(logger) {
45501
45609
  const candidates = [];
45502
45610
  try {
45503
- const here = path11.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
45504
- candidates.push(path11.resolve(here, "..", "deploy-kit"));
45611
+ const here = path13.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
45612
+ candidates.push(path13.resolve(here, "..", "deploy-kit"));
45505
45613
  } catch {
45506
45614
  }
45507
45615
  const fromArgv = bundleSiblingFromArgv(process.argv[1], "deploy-kit");
45508
45616
  if (fromArgv) candidates.push(fromArgv);
45509
45617
  for (const c of candidates) {
45510
45618
  try {
45511
- if (fs9.statSync(c).isDirectory()) {
45619
+ if (fs10.statSync(c).isDirectory()) {
45512
45620
  logger?.info("persona.deploy-kit-root.resolved", { root: c });
45513
45621
  return c;
45514
45622
  }
@@ -45519,8 +45627,8 @@ function findDeployKitRoot(logger) {
45519
45627
  return null;
45520
45628
  }
45521
45629
  function seedDeployKit(args) {
45522
- const dst = path11.join(args.dataDir, "deploy-kit");
45523
- fs9.cpSync(args.deployKitBundleRoot, dst, {
45630
+ const dst = path13.join(args.dataDir, "deploy-kit");
45631
+ fs10.cpSync(args.deployKitBundleRoot, dst, {
45524
45632
  recursive: true,
45525
45633
  dereference: true,
45526
45634
  force: false,
@@ -45529,35 +45637,35 @@ function seedDeployKit(args) {
45529
45637
  args.logger.info("deploy-kit.seed.done", { dst });
45530
45638
  }
45531
45639
  function refreshDeployKit(args) {
45532
- const dst = path11.join(args.dataDir, "deploy-kit");
45533
- if (!fs9.existsSync(dst)) {
45640
+ const dst = path13.join(args.dataDir, "deploy-kit");
45641
+ if (!fs10.existsSync(dst)) {
45534
45642
  seedDeployKit(args);
45535
45643
  return;
45536
45644
  }
45537
45645
  for (const sub of ["scripts", "contract"]) {
45538
- const s = path11.join(args.deployKitBundleRoot, sub);
45539
- if (fs9.existsSync(s)) {
45540
- fs9.cpSync(s, path11.join(dst, sub), { recursive: true, force: true, dereference: true });
45646
+ const s = path13.join(args.deployKitBundleRoot, sub);
45647
+ if (fs10.existsSync(s)) {
45648
+ fs10.cpSync(s, path13.join(dst, sub), { recursive: true, force: true, dereference: true });
45541
45649
  }
45542
45650
  }
45543
- const secretsSrc = path11.join(args.deployKitBundleRoot, ".secrets");
45544
- if (fs9.existsSync(secretsSrc)) {
45545
- fs9.mkdirSync(path11.join(dst, ".secrets"), { recursive: true });
45546
- for (const f of fs9.readdirSync(secretsSrc)) {
45651
+ const secretsSrc = path13.join(args.deployKitBundleRoot, ".secrets");
45652
+ if (fs10.existsSync(secretsSrc)) {
45653
+ fs10.mkdirSync(path13.join(dst, ".secrets"), { recursive: true });
45654
+ for (const f of fs10.readdirSync(secretsSrc)) {
45547
45655
  if (!f.endsWith(".example")) continue;
45548
- fs9.copyFileSync(path11.join(secretsSrc, f), path11.join(dst, ".secrets", f));
45656
+ fs10.copyFileSync(path13.join(secretsSrc, f), path13.join(dst, ".secrets", f));
45549
45657
  }
45550
45658
  }
45551
45659
  args.logger.info("deploy-kit.refresh.done", { dst });
45552
45660
  }
45553
45661
 
45554
45662
  // src/share-md-viewer/load.ts
45555
- var import_node_fs9 = __toESM(require("fs"), 1);
45556
- var import_node_path8 = __toESM(require("path"), 1);
45663
+ var import_node_fs10 = __toESM(require("fs"), 1);
45664
+ var import_node_path10 = __toESM(require("path"), 1);
45557
45665
  var import_node_url2 = require("url");
45558
45666
 
45559
45667
  // src/share-md-viewer/asset-loader.ts
45560
- var import_node_fs8 = __toESM(require("fs"), 1);
45668
+ var import_node_fs9 = __toESM(require("fs"), 1);
45561
45669
  function htmlEscape(s) {
45562
45670
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
45563
45671
  }
@@ -45565,12 +45673,12 @@ function createViewerAssetLoader(opts) {
45565
45673
  let viewerTemplate;
45566
45674
  let errorTemplate;
45567
45675
  try {
45568
- viewerTemplate = import_node_fs8.default.readFileSync(opts.viewerHtmlPath, "utf8");
45676
+ viewerTemplate = import_node_fs9.default.readFileSync(opts.viewerHtmlPath, "utf8");
45569
45677
  } catch (err) {
45570
45678
  throw new Error(`viewer asset not found at ${opts.viewerHtmlPath}: ${err.message}`);
45571
45679
  }
45572
45680
  try {
45573
- errorTemplate = import_node_fs8.default.readFileSync(opts.errorHtmlPath, "utf8");
45681
+ errorTemplate = import_node_fs9.default.readFileSync(opts.errorHtmlPath, "utf8");
45574
45682
  } catch (err) {
45575
45683
  throw new Error(`viewer error asset not found at ${opts.errorHtmlPath}: ${err.message}`);
45576
45684
  }
@@ -45589,25 +45697,25 @@ var import_meta2 = {};
45589
45697
  function tryLoadViewerAssets(logger) {
45590
45698
  const candidates = [];
45591
45699
  try {
45592
- const here = import_node_path8.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
45700
+ const here = import_node_path10.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
45593
45701
  candidates.push(here);
45594
- candidates.push(import_node_path8.default.resolve(here, ".."));
45595
- candidates.push(import_node_path8.default.resolve(here, "..", "..", "dist"));
45702
+ candidates.push(import_node_path10.default.resolve(here, ".."));
45703
+ candidates.push(import_node_path10.default.resolve(here, "..", "..", "dist"));
45596
45704
  } catch {
45597
45705
  }
45598
45706
  if (process.argv[1]) {
45599
45707
  let real = process.argv[1];
45600
45708
  try {
45601
- real = import_node_fs9.default.realpathSync(process.argv[1]);
45709
+ real = import_node_fs10.default.realpathSync(process.argv[1]);
45602
45710
  } catch {
45603
45711
  }
45604
- candidates.push(import_node_path8.default.dirname(real));
45712
+ candidates.push(import_node_path10.default.dirname(real));
45605
45713
  }
45606
45714
  for (const root of candidates) {
45607
- const viewerHtmlPath = import_node_path8.default.join(root, "share-md-viewer.html");
45608
- const errorHtmlPath = import_node_path8.default.join(root, "share-md-viewer-error.html");
45715
+ const viewerHtmlPath = import_node_path10.default.join(root, "share-md-viewer.html");
45716
+ const errorHtmlPath = import_node_path10.default.join(root, "share-md-viewer-error.html");
45609
45717
  try {
45610
- if (import_node_fs9.default.statSync(viewerHtmlPath).isFile() && import_node_fs9.default.statSync(errorHtmlPath).isFile()) {
45718
+ if (import_node_fs10.default.statSync(viewerHtmlPath).isFile() && import_node_fs10.default.statSync(errorHtmlPath).isFile()) {
45611
45719
  logger?.info("share-md-viewer.assets-root.resolved", { root });
45612
45720
  return createViewerAssetLoader({ viewerHtmlPath, errorHtmlPath });
45613
45721
  }
@@ -45622,30 +45730,30 @@ function tryLoadViewerAssets(logger) {
45622
45730
  }
45623
45731
 
45624
45732
  // src/share-ui/load.ts
45625
- var import_node_fs11 = __toESM(require("fs"), 1);
45626
- var import_node_path10 = __toESM(require("path"), 1);
45733
+ var import_node_fs12 = __toESM(require("fs"), 1);
45734
+ var import_node_path12 = __toESM(require("path"), 1);
45627
45735
  var import_node_url3 = require("url");
45628
45736
 
45629
45737
  // src/share-ui/asset-loader.ts
45630
- var import_node_fs10 = __toESM(require("fs"), 1);
45631
- var import_node_path9 = __toESM(require("path"), 1);
45738
+ var import_node_fs11 = __toESM(require("fs"), 1);
45739
+ var import_node_path11 = __toESM(require("path"), 1);
45632
45740
  function createShareUiAssetLoader(opts) {
45633
45741
  let indexHtml;
45634
45742
  try {
45635
- indexHtml = import_node_fs10.default.readFileSync(opts.indexHtmlPath, "utf8");
45743
+ indexHtml = import_node_fs11.default.readFileSync(opts.indexHtmlPath, "utf8");
45636
45744
  } catch (err) {
45637
45745
  throw new Error(`share-ui index.html not found at ${opts.indexHtmlPath}: ${err.message}`);
45638
45746
  }
45639
- const assetsDir = import_node_path9.default.resolve(opts.assetsDir);
45747
+ const assetsDir = import_node_path11.default.resolve(opts.assetsDir);
45640
45748
  return {
45641
45749
  renderIndexHtml() {
45642
45750
  return indexHtml;
45643
45751
  },
45644
45752
  resolveAssetPath(rel) {
45645
- const resolved = import_node_path9.default.resolve(assetsDir, rel);
45646
- if (resolved !== assetsDir && !resolved.startsWith(assetsDir + import_node_path9.default.sep)) return null;
45753
+ const resolved = import_node_path11.default.resolve(assetsDir, rel);
45754
+ if (resolved !== assetsDir && !resolved.startsWith(assetsDir + import_node_path11.default.sep)) return null;
45647
45755
  try {
45648
- if (import_node_fs10.default.statSync(resolved).isFile()) return resolved;
45756
+ if (import_node_fs11.default.statSync(resolved).isFile()) return resolved;
45649
45757
  } catch {
45650
45758
  }
45651
45759
  return null;
@@ -45659,28 +45767,28 @@ function bundleDirFromArgv(argv1) {
45659
45767
  if (!argv1) return null;
45660
45768
  let real = argv1;
45661
45769
  try {
45662
- real = import_node_fs11.default.realpathSync(argv1);
45770
+ real = import_node_fs12.default.realpathSync(argv1);
45663
45771
  } catch {
45664
45772
  }
45665
- return import_node_path10.default.dirname(real);
45773
+ return import_node_path12.default.dirname(real);
45666
45774
  }
45667
45775
  function tryLoadShareUi(logger) {
45668
45776
  const candidates = [];
45669
45777
  try {
45670
- const here = import_node_path10.default.dirname((0, import_node_url3.fileURLToPath)(import_meta3.url));
45778
+ const here = import_node_path12.default.dirname((0, import_node_url3.fileURLToPath)(import_meta3.url));
45671
45779
  candidates.push(here);
45672
- candidates.push(import_node_path10.default.resolve(here, ".."));
45673
- candidates.push(import_node_path10.default.resolve(here, "..", "..", "dist"));
45780
+ candidates.push(import_node_path12.default.resolve(here, ".."));
45781
+ candidates.push(import_node_path12.default.resolve(here, "..", "..", "dist"));
45674
45782
  } catch {
45675
45783
  }
45676
45784
  const argvDir = bundleDirFromArgv(process.argv[1]);
45677
45785
  if (argvDir) candidates.push(argvDir);
45678
45786
  for (const root of candidates) {
45679
- const shareUiDir = import_node_path10.default.join(root, "share-ui");
45680
- const indexHtmlPath = import_node_path10.default.join(shareUiDir, "guest.html");
45681
- const assetsDir = import_node_path10.default.join(shareUiDir, "assets");
45787
+ const shareUiDir = import_node_path12.default.join(root, "share-ui");
45788
+ const indexHtmlPath = import_node_path12.default.join(shareUiDir, "guest.html");
45789
+ const assetsDir = import_node_path12.default.join(shareUiDir, "assets");
45682
45790
  try {
45683
- if (import_node_fs11.default.statSync(indexHtmlPath).isFile() && import_node_fs11.default.statSync(assetsDir).isDirectory()) {
45791
+ if (import_node_fs12.default.statSync(indexHtmlPath).isFile() && import_node_fs12.default.statSync(assetsDir).isDirectory()) {
45684
45792
  logger?.info("share-ui.assets-root.resolved", { root: shareUiDir });
45685
45793
  return createShareUiAssetLoader({ indexHtmlPath, assetsDir });
45686
45794
  }
@@ -45762,20 +45870,20 @@ function buildVisitorLogin(deps) {
45762
45870
  }
45763
45871
 
45764
45872
  // src/visitor/visitor-store.ts
45765
- var import_node_fs12 = __toESM(require("fs"), 1);
45766
- var import_node_path11 = __toESM(require("path"), 1);
45873
+ var import_node_fs13 = __toESM(require("fs"), 1);
45874
+ var import_node_path13 = __toESM(require("path"), 1);
45767
45875
  function createVisitorStore(opts) {
45768
- const file = import_node_path11.default.join(opts.dir, "visitors.json");
45876
+ const file = import_node_path13.default.join(opts.dir, "visitors.json");
45769
45877
  const read = () => {
45770
45878
  try {
45771
- return JSON.parse(import_node_fs12.default.readFileSync(file, "utf8"));
45879
+ return JSON.parse(import_node_fs13.default.readFileSync(file, "utf8"));
45772
45880
  } catch {
45773
45881
  return [];
45774
45882
  }
45775
45883
  };
45776
45884
  const write = (rows) => {
45777
- import_node_fs12.default.mkdirSync(opts.dir, { recursive: true });
45778
- import_node_fs12.default.writeFileSync(file, JSON.stringify(rows, null, 2));
45885
+ import_node_fs13.default.mkdirSync(opts.dir, { recursive: true });
45886
+ import_node_fs13.default.writeFileSync(file, JSON.stringify(rows, null, 2));
45779
45887
  };
45780
45888
  return {
45781
45889
  upsert(r) {
@@ -46057,8 +46165,8 @@ function turnStartInput(text) {
46057
46165
  const items = [];
46058
46166
  let leftover = text;
46059
46167
  for (const m2 of text.matchAll(SKILL_RE)) {
46060
- const [marker, name, path67] = m2;
46061
- items.push({ type: "skill", name, path: path67 });
46168
+ const [marker, name, path68] = m2;
46169
+ items.push({ type: "skill", name, path: path68 });
46062
46170
  leftover = leftover.replace(marker, "");
46063
46171
  }
46064
46172
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -46291,8 +46399,8 @@ var CodexAdapter = class {
46291
46399
 
46292
46400
  // src/tools/claude-tui.ts
46293
46401
  var import_node_fs16 = __toESM(require("fs"), 1);
46294
- var import_node_os6 = __toESM(require("os"), 1);
46295
- var import_node_path13 = __toESM(require("path"), 1);
46402
+ var import_node_os7 = __toESM(require("os"), 1);
46403
+ var import_node_path14 = __toESM(require("path"), 1);
46296
46404
  var import_headless = __toESM(require_xterm_headless(), 1);
46297
46405
 
46298
46406
  // ../node_modules/.pnpm/@xterm+addon-serialize@0.14.0/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs
@@ -47428,8 +47536,8 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
47428
47536
  }
47429
47537
  function jsonlExistsForCtx(ctx) {
47430
47538
  if (!ctx.toolSessionId) return false;
47431
- const home = import_node_os6.default.homedir();
47432
- const file = import_node_path13.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
47539
+ const home = import_node_os7.default.homedir();
47540
+ const file = import_node_path14.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
47433
47541
  try {
47434
47542
  return import_node_fs16.default.statSync(file).isFile();
47435
47543
  } catch {
@@ -47460,6 +47568,7 @@ var PersonaDispatchManager = class {
47460
47568
  dispatchId,
47461
47569
  sourceSessionId: args.sourceSessionId,
47462
47570
  targetPersona: args.targetPersona,
47571
+ bSessionId: args.bSessionId,
47463
47572
  waiters: [],
47464
47573
  outcome: null
47465
47574
  });
@@ -47490,10 +47599,22 @@ var PersonaDispatchManager = class {
47490
47599
  if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
47491
47600
  state.bSessionId = bSessionId;
47492
47601
  }
47493
- /** reducer buildSpawnContext 调,按 B 的 sessionId 反查 dispatchId(用于注 CLAWD_DISPATCH_ID env) */
47494
- lookupDispatchByBSessionId(bSessionId) {
47602
+ /**
47603
+ * 按 B sessionId 找当前 in-flight(未 resolved)的 dispatchId。complete handler 用这个把
47604
+ * B 的 outcome 配对回 A 的 waiter —— dispatchId 不再走 cc env,全靠
47605
+ * (bSessionId → in-flight dispatchId) 反查。
47606
+ *
47607
+ * 同一 bSessionId 不会同时有多个 in-flight:A 拿不到 outcome 不会发起新 dispatch;
47608
+ * 老板规则下 A 一次只能挂一个 waiter。
47609
+ *
47610
+ * 历史 entry(已 resolved)暂不清 —— 单 daemon lifetime 累积可接受;未来用量大了加
47611
+ * TTL GC(complete 后 N min 删 entry)。
47612
+ */
47613
+ findInflightDispatchByBSessionId(bSessionId) {
47495
47614
  for (const state of this.map.values()) {
47496
- if (state.bSessionId === bSessionId) return state.dispatchId;
47615
+ if (state.bSessionId === bSessionId && state.outcome === null) {
47616
+ return state.dispatchId;
47617
+ }
47497
47618
  }
47498
47619
  return void 0;
47499
47620
  }
@@ -47501,9 +47622,9 @@ var PersonaDispatchManager = class {
47501
47622
 
47502
47623
  // src/dispatch/mcp-config.ts
47503
47624
  var import_node_fs17 = __toESM(require("fs"), 1);
47504
- var import_node_path14 = __toESM(require("path"), 1);
47625
+ var import_node_path15 = __toESM(require("path"), 1);
47505
47626
  function dispatchMcpConfigPath(dataDir) {
47506
- return import_node_path14.default.join(dataDir, "dispatch.mcp.json");
47627
+ return import_node_path15.default.join(dataDir, "dispatch.mcp.json");
47507
47628
  }
47508
47629
  function writeDispatchMcpConfig(args) {
47509
47630
  const cfgPath = dispatchMcpConfigPath(args.dataDir);
@@ -47525,9 +47646,9 @@ function writeDispatchMcpConfig(args) {
47525
47646
 
47526
47647
  // src/ticket/mcp-config.ts
47527
47648
  var import_node_fs18 = __toESM(require("fs"), 1);
47528
- var import_node_path15 = __toESM(require("path"), 1);
47649
+ var import_node_path16 = __toESM(require("path"), 1);
47529
47650
  function ticketMcpConfigPath(dataDir) {
47530
- return import_node_path15.default.join(dataDir, "ticket.mcp.json");
47651
+ return import_node_path16.default.join(dataDir, "ticket.mcp.json");
47531
47652
  }
47532
47653
  function writeTicketMcpConfig(args) {
47533
47654
  const cfgPath = ticketMcpConfigPath(args.dataDir);
@@ -47555,9 +47676,9 @@ function writeTicketMcpConfig(args) {
47555
47676
 
47556
47677
  // src/shift/mcp-config.ts
47557
47678
  var import_node_fs19 = __toESM(require("fs"), 1);
47558
- var import_node_path16 = __toESM(require("path"), 1);
47679
+ var import_node_path17 = __toESM(require("path"), 1);
47559
47680
  function shiftMcpConfigPath(dataDir) {
47560
- return import_node_path16.default.join(dataDir, "shift.mcp.json");
47681
+ return import_node_path17.default.join(dataDir, "shift.mcp.json");
47561
47682
  }
47562
47683
  async function writeShiftMcpConfig(args) {
47563
47684
  const cfgPath = shiftMcpConfigPath(args.dataDir);
@@ -47579,9 +47700,9 @@ async function writeShiftMcpConfig(args) {
47579
47700
 
47580
47701
  // src/inbox/mcp-config.ts
47581
47702
  var import_node_fs20 = __toESM(require("fs"), 1);
47582
- var import_node_path17 = __toESM(require("path"), 1);
47703
+ var import_node_path18 = __toESM(require("path"), 1);
47583
47704
  function inboxMcpConfigPath(dataDir) {
47584
- return import_node_path17.default.join(dataDir, "inbox.mcp.json");
47705
+ return import_node_path18.default.join(dataDir, "inbox.mcp.json");
47585
47706
  }
47586
47707
  async function writeInboxMcpConfig(args) {
47587
47708
  const cfgPath = inboxMcpConfigPath(args.dataDir);
@@ -47603,7 +47724,7 @@ async function writeInboxMcpConfig(args) {
47603
47724
 
47604
47725
  // src/shift/store.ts
47605
47726
  var import_promises = __toESM(require("fs/promises"), 1);
47606
- var import_node_path18 = __toESM(require("path"), 1);
47727
+ var import_node_path19 = __toESM(require("path"), 1);
47607
47728
  var import_node_crypto5 = require("crypto");
47608
47729
 
47609
47730
  // src/shift/constants.ts
@@ -47672,7 +47793,7 @@ function createShiftStore(deps) {
47672
47793
  flushTimer = null;
47673
47794
  }
47674
47795
  const content = { version: 1, shifts };
47675
- await import_promises.default.mkdir(import_node_path18.default.dirname(deps.filePath), { recursive: true });
47796
+ await import_promises.default.mkdir(import_node_path19.default.dirname(deps.filePath), { recursive: true });
47676
47797
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
47677
47798
  await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
47678
47799
  await import_promises.default.rename(tmp, deps.filePath);
@@ -48092,6 +48213,28 @@ function canAccessPersona(grants, personaId, action) {
48092
48213
  }
48093
48214
 
48094
48215
  // src/handlers/persona-dispatch.ts
48216
+ function assertLocalReuseAllowed(bFile, sourceSessionId) {
48217
+ if (bFile.dispatchedFromSessionId !== sourceSessionId) {
48218
+ throw new ClawdError(
48219
+ ERROR_CODES.UNAUTHORIZED,
48220
+ `targetSessionId not dispatched by caller (dispatchedFromSessionId mismatch)`
48221
+ );
48222
+ }
48223
+ }
48224
+ function assertGuestReuseAllowed(bFile, sourcePrincipalId, targetPersona) {
48225
+ if (bFile.ownerPersonaId !== targetPersona) {
48226
+ throw new ClawdError(
48227
+ ERROR_CODES.UNAUTHORIZED,
48228
+ `targetSessionId does not belong to targetPersona`
48229
+ );
48230
+ }
48231
+ if (bFile.creatorPrincipalId !== sourcePrincipalId) {
48232
+ throw new ClawdError(
48233
+ ERROR_CODES.UNAUTHORIZED,
48234
+ `targetSessionId not dispatched by caller device (creatorPrincipalId mismatch)`
48235
+ );
48236
+ }
48237
+ }
48095
48238
  function buildPersonaDispatchHandlers(deps) {
48096
48239
  const { personaDispatchManager: mgr, spawnB, logger } = deps;
48097
48240
  const run = async (frame, _client, ctx) => {
@@ -48110,15 +48253,21 @@ function buildPersonaDispatchHandlers(deps) {
48110
48253
  }
48111
48254
  logger?.info("dispatch.run.forward", {
48112
48255
  targetDeviceId: args.targetDeviceId,
48113
- targetPersona: args.targetPersona
48256
+ targetPersona: args.targetPersona,
48257
+ hasTargetSessionId: Boolean(args.targetSessionId)
48114
48258
  });
48115
- const outcome2 = await deps.forwardToPeer({
48259
+ const { outcome: outcome2, dispatchedSessionId } = await deps.forwardToPeer({
48116
48260
  targetDeviceId: args.targetDeviceId,
48117
48261
  targetPersona: args.targetPersona,
48118
- prompt: args.prompt
48262
+ prompt: args.prompt,
48263
+ targetSessionId: args.targetSessionId
48119
48264
  });
48120
48265
  return {
48121
- response: { type: "personaDispatch:run:ok", outcome: outcome2 }
48266
+ response: {
48267
+ type: "personaDispatch:run:ok",
48268
+ outcome: outcome2,
48269
+ ...dispatchedSessionId ? { dispatchedSessionId } : {}
48270
+ }
48122
48271
  };
48123
48272
  }
48124
48273
  if (ctx?.principal.kind === "guest") {
@@ -48129,34 +48278,65 @@ function buildPersonaDispatchHandlers(deps) {
48129
48278
  `persona not dispatchable: ${args.targetPersona}`
48130
48279
  );
48131
48280
  }
48281
+ const guestSourceId = ctx.principal.id;
48282
+ let bSessionId2;
48283
+ let route2 = "new";
48284
+ if (args.targetSessionId) {
48285
+ if (!deps.findOwnedSession) {
48286
+ throw new Error("targetSessionId reuse not wired (findOwnedSession missing)");
48287
+ }
48288
+ const bFile = deps.findOwnedSession(args.targetSessionId);
48289
+ if (!bFile) {
48290
+ throw new ClawdError(
48291
+ ERROR_CODES.SESSION_NOT_FOUND,
48292
+ `targetSessionId not found: ${args.targetSessionId}`
48293
+ );
48294
+ }
48295
+ assertGuestReuseAllowed(bFile, guestSourceId, args.targetPersona);
48296
+ route2 = "resume";
48297
+ bSessionId2 = args.targetSessionId;
48298
+ }
48132
48299
  const { dispatchId: dispatchId2 } = mgr.start({
48133
- sourceSessionId: ctx.principal.id,
48134
- targetPersona: args.targetPersona
48300
+ sourceSessionId: guestSourceId,
48301
+ targetPersona: args.targetPersona,
48302
+ bSessionId: bSessionId2
48135
48303
  });
48136
48304
  logger?.info("dispatch.run.received.guest", {
48137
48305
  dispatchId: dispatchId2,
48138
- sourcePrincipal: ctx.principal.id,
48306
+ sourcePrincipal: guestSourceId,
48139
48307
  targetPersona: args.targetPersona,
48140
- promptLen: args.prompt.length
48308
+ promptLen: args.prompt.length,
48309
+ route: route2
48141
48310
  });
48142
- void spawnB({
48143
- dispatchId: dispatchId2,
48144
- sourceSessionId: ctx.principal.id,
48145
- targetPersona: args.targetPersona,
48146
- prompt: args.prompt,
48147
- guestPrincipalId: ctx.principal.id,
48148
- guestDisplayName: ctx.principal.displayName
48149
- }).then(() => logger?.info("dispatch.spawnB.ok", { dispatchId: dispatchId2 })).catch((err) => {
48311
+ let resolvedBSessionId2 = bSessionId2;
48312
+ try {
48313
+ const { bSessionId: minted } = await spawnB({
48314
+ dispatchId: dispatchId2,
48315
+ sourceSessionId: guestSourceId,
48316
+ targetPersona: args.targetPersona,
48317
+ prompt: args.prompt,
48318
+ route: route2,
48319
+ bSessionId: bSessionId2,
48320
+ guestPrincipalId: guestSourceId,
48321
+ guestDisplayName: ctx.principal.displayName
48322
+ });
48323
+ resolvedBSessionId2 = minted;
48324
+ logger?.info("dispatch.spawnB.ok", { dispatchId: dispatchId2, route: route2 });
48325
+ } catch (err) {
48150
48326
  const reason = err instanceof Error ? err.message : String(err);
48151
- logger?.warn("dispatch.spawnB.failed", { dispatchId: dispatchId2, reason });
48327
+ logger?.warn("dispatch.spawnB.failed", { dispatchId: dispatchId2, route: route2, reason });
48152
48328
  mgr.complete(dispatchId2, {
48153
48329
  kind: "failure",
48154
48330
  reason: `failed to spawn B: ${reason}`
48155
48331
  });
48156
- });
48332
+ }
48157
48333
  const outcome2 = await mgr.wait(dispatchId2);
48158
48334
  return {
48159
- response: { type: "personaDispatch:run:ok", outcome: outcome2 }
48335
+ response: {
48336
+ type: "personaDispatch:run:ok",
48337
+ outcome: outcome2,
48338
+ ...resolvedBSessionId2 ? { dispatchedSessionId: resolvedBSessionId2 } : {}
48339
+ }
48160
48340
  };
48161
48341
  }
48162
48342
  if (!sourceSessionId) {
@@ -48164,31 +48344,61 @@ function buildPersonaDispatchHandlers(deps) {
48164
48344
  "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
48165
48345
  );
48166
48346
  }
48347
+ let bSessionId;
48348
+ let route = "new";
48349
+ if (args.targetSessionId) {
48350
+ if (!deps.findOwnedSession) {
48351
+ throw new Error("targetSessionId reuse not wired (findOwnedSession missing)");
48352
+ }
48353
+ const bFile = deps.findOwnedSession(args.targetSessionId);
48354
+ if (!bFile) {
48355
+ throw new ClawdError(
48356
+ ERROR_CODES.SESSION_NOT_FOUND,
48357
+ `targetSessionId not found: ${args.targetSessionId}`
48358
+ );
48359
+ }
48360
+ if (bFile.ownerPersonaId !== args.targetPersona) {
48361
+ throw new ClawdError(
48362
+ ERROR_CODES.UNAUTHORIZED,
48363
+ `targetSessionId does not belong to targetPersona`
48364
+ );
48365
+ }
48366
+ assertLocalReuseAllowed(bFile, sourceSessionId);
48367
+ route = "resume";
48368
+ bSessionId = args.targetSessionId;
48369
+ }
48167
48370
  const { dispatchId } = mgr.start({
48168
48371
  sourceSessionId,
48169
- targetPersona: args.targetPersona
48372
+ targetPersona: args.targetPersona,
48373
+ bSessionId
48170
48374
  });
48171
48375
  logger?.info("dispatch.run.received", {
48172
48376
  dispatchId,
48173
48377
  sourceSessionId,
48174
48378
  targetPersona: args.targetPersona,
48175
- promptLen: args.prompt.length
48379
+ promptLen: args.prompt.length,
48380
+ route
48176
48381
  });
48177
- void spawnB({
48178
- dispatchId,
48179
- sourceSessionId,
48180
- targetPersona: args.targetPersona,
48181
- prompt: args.prompt
48182
- }).then(() => {
48183
- logger?.info("dispatch.spawnB.ok", { dispatchId });
48184
- }).catch((err) => {
48382
+ let resolvedBSessionId = bSessionId;
48383
+ try {
48384
+ const { bSessionId: minted } = await spawnB({
48385
+ dispatchId,
48386
+ sourceSessionId,
48387
+ targetPersona: args.targetPersona,
48388
+ prompt: args.prompt,
48389
+ route,
48390
+ bSessionId
48391
+ });
48392
+ resolvedBSessionId = minted;
48393
+ logger?.info("dispatch.spawnB.ok", { dispatchId, route });
48394
+ } catch (err) {
48185
48395
  const reason = err instanceof Error ? err.message : String(err);
48186
- logger?.warn("dispatch.spawnB.failed", { dispatchId, reason });
48396
+ logger?.warn("dispatch.spawnB.failed", { dispatchId, route, reason });
48187
48397
  mgr.complete(dispatchId, {
48188
48398
  kind: "failure",
48189
48399
  reason: `failed to spawn B: ${reason}`
48190
48400
  });
48191
- });
48401
+ }
48192
48402
  logger?.info("dispatch.run.waiting", { dispatchId });
48193
48403
  const outcome = await mgr.wait(dispatchId);
48194
48404
  logger?.info("dispatch.run.resolved", {
@@ -48196,17 +48406,34 @@ function buildPersonaDispatchHandlers(deps) {
48196
48406
  outcomeKind: outcome.kind
48197
48407
  });
48198
48408
  return {
48199
- response: { type: "personaDispatch:run:ok", outcome }
48409
+ response: {
48410
+ type: "personaDispatch:run:ok",
48411
+ outcome,
48412
+ ...resolvedBSessionId ? { dispatchedSessionId: resolvedBSessionId } : {}
48413
+ }
48200
48414
  };
48201
48415
  };
48202
48416
  const complete = async (frame) => {
48203
48417
  const { type: _t, requestId: _r, ...rest } = frame;
48418
+ const sessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
48204
48419
  const args = DispatchCompleteArgsSchema.parse(rest);
48420
+ if (!sessionId) {
48421
+ throw new Error(
48422
+ "personaDispatch:complete requires sessionId (caller must pass x-clawd-session-id header)"
48423
+ );
48424
+ }
48425
+ const dispatchId = mgr.findInflightDispatchByBSessionId(sessionId);
48426
+ if (!dispatchId) {
48427
+ throw new Error(
48428
+ `no in-flight dispatch for session ${sessionId} (already completed or B was never dispatched)`
48429
+ );
48430
+ }
48205
48431
  logger?.info("dispatch.complete.received", {
48206
- dispatchId: args.dispatchId,
48432
+ dispatchId,
48433
+ sessionId,
48207
48434
  outcomeKind: args.outcome.kind
48208
48435
  });
48209
- mgr.complete(args.dispatchId, args.outcome);
48436
+ mgr.complete(dispatchId, args.outcome);
48210
48437
  return {
48211
48438
  response: { type: "personaDispatch:complete:ok" }
48212
48439
  };
@@ -48236,25 +48463,37 @@ async function forwardDispatchToPeer(args) {
48236
48463
  authorization: `Bearer ${args.contact.connectToken}`
48237
48464
  },
48238
48465
  // 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
48239
- body: JSON.stringify({ targetPersona: args.targetPersona, prompt: args.prompt })
48466
+ // targetSessionId 透传:peer 那台机会按 creatorPrincipalId === A.deviceId 校验。
48467
+ body: JSON.stringify({
48468
+ targetPersona: args.targetPersona,
48469
+ prompt: args.prompt,
48470
+ ...args.targetSessionId ? { targetSessionId: args.targetSessionId } : {}
48471
+ })
48240
48472
  });
48241
48473
  } catch (err) {
48242
48474
  const msg = err instanceof Error ? err.message : String(err);
48243
- return { kind: "failure", reason: `forward to peer failed: ${msg}` };
48475
+ return { outcome: { kind: "failure", reason: `forward to peer failed: ${msg}` } };
48244
48476
  }
48245
48477
  let json;
48246
48478
  try {
48247
48479
  json = await res.json();
48248
48480
  } catch {
48249
48481
  return {
48250
- kind: "failure",
48251
- reason: `peer returned non-JSON response (HTTP ${res.status})`
48482
+ outcome: {
48483
+ kind: "failure",
48484
+ reason: `peer returned non-JSON response (HTTP ${res.status})`
48485
+ }
48252
48486
  };
48253
48487
  }
48254
48488
  if (json.ok === false) {
48255
- return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
48489
+ return {
48490
+ outcome: { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` }
48491
+ };
48256
48492
  }
48257
- return json.result.outcome;
48493
+ return {
48494
+ outcome: json.result.outcome,
48495
+ dispatchedSessionId: json.result.dispatchedSessionId
48496
+ };
48258
48497
  }
48259
48498
  async function forwardInboxPostToPeer(args) {
48260
48499
  const f = args.fetchImpl ?? fetch;
@@ -48476,13 +48715,13 @@ function mapSkillsListResponse(res) {
48476
48715
  const r = s ?? {};
48477
48716
  const name = str3(r.name);
48478
48717
  if (!name) continue;
48479
- const path67 = str3(r.path);
48718
+ const path68 = str3(r.path);
48480
48719
  const description = str3(r.description);
48481
48720
  const isPlugin = name.includes(":");
48482
48721
  out.push({
48483
48722
  name,
48484
48723
  source: isPlugin ? "plugin" : "project",
48485
- ...path67 ? { path: path67 } : {},
48724
+ ...path68 ? { path: path68 } : {},
48486
48725
  ...description ? { description } : {},
48487
48726
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
48488
48727
  });
@@ -48522,15 +48761,15 @@ async function listCodexSkills(cwd, deps = {}) {
48522
48761
 
48523
48762
  // src/workspace/browser.ts
48524
48763
  var import_node_fs21 = __toESM(require("fs"), 1);
48525
- var import_node_os7 = __toESM(require("os"), 1);
48526
- var import_node_path19 = __toESM(require("path"), 1);
48764
+ var import_node_os8 = __toESM(require("os"), 1);
48765
+ var import_node_path20 = __toESM(require("path"), 1);
48527
48766
  init_protocol();
48528
48767
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
48529
48768
  function resolveInsideCwd(cwd, subpath) {
48530
- const absCwd = import_node_path19.default.resolve(cwd);
48531
- const joined = import_node_path19.default.resolve(absCwd, subpath ?? ".");
48532
- const rel = import_node_path19.default.relative(absCwd, joined);
48533
- if (rel.startsWith("..") || import_node_path19.default.isAbsolute(rel)) {
48769
+ const absCwd = import_node_path20.default.resolve(cwd);
48770
+ const joined = import_node_path20.default.resolve(absCwd, subpath ?? ".");
48771
+ const rel = import_node_path20.default.relative(absCwd, joined);
48772
+ if (rel.startsWith("..") || import_node_path20.default.isAbsolute(rel)) {
48534
48773
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
48535
48774
  }
48536
48775
  return joined;
@@ -48548,7 +48787,7 @@ function ensureCwd(cwd) {
48548
48787
  }
48549
48788
  var WorkspaceBrowser = class {
48550
48789
  list(args) {
48551
- const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os7.default.homedir();
48790
+ const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
48552
48791
  ensureCwd(cwd);
48553
48792
  const full = resolveInsideCwd(cwd, args.path);
48554
48793
  const dirents = import_node_fs21.default.readdirSync(full, { withFileTypes: true });
@@ -48561,7 +48800,7 @@ var WorkspaceBrowser = class {
48561
48800
  mtime: ""
48562
48801
  };
48563
48802
  try {
48564
- const st = import_node_fs21.default.statSync(import_node_path19.default.join(full, d.name));
48803
+ const st = import_node_fs21.default.statSync(import_node_path20.default.join(full, d.name));
48565
48804
  entry.mtime = new Date(st.mtimeMs).toISOString();
48566
48805
  if (d.isFile()) entry.size = st.size;
48567
48806
  } catch {
@@ -48607,8 +48846,8 @@ var WorkspaceBrowser = class {
48607
48846
 
48608
48847
  // src/skills/agents-scanner.ts
48609
48848
  var import_node_fs22 = __toESM(require("fs"), 1);
48610
- var import_node_os8 = __toESM(require("os"), 1);
48611
- var import_node_path20 = __toESM(require("path"), 1);
48849
+ var import_node_os9 = __toESM(require("os"), 1);
48850
+ var import_node_path21 = __toESM(require("path"), 1);
48612
48851
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
48613
48852
  function isDirLikeSync2(p2) {
48614
48853
  try {
@@ -48646,10 +48885,10 @@ function scanAgentsDir(dir, source, seen, out) {
48646
48885
  }
48647
48886
  for (const ent of entries) {
48648
48887
  if (!ent.name.endsWith(".md")) continue;
48649
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path20.default.join(dir, ent.name)))) {
48888
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path21.default.join(dir, ent.name)))) {
48650
48889
  continue;
48651
48890
  }
48652
- const filePath = import_node_path20.default.join(dir, ent.name);
48891
+ const filePath = import_node_path21.default.join(dir, ent.name);
48653
48892
  const baseName = ent.name.replace(/\.md$/, "");
48654
48893
  if (seen.has(baseName)) continue;
48655
48894
  seen.add(baseName);
@@ -48672,7 +48911,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
48672
48911
  return;
48673
48912
  }
48674
48913
  for (const ent of entries) {
48675
- const childPath = import_node_path20.default.join(dir, ent.name);
48914
+ const childPath = import_node_path21.default.join(dir, ent.name);
48676
48915
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
48677
48916
  walk2(childPath, [...namespaces, ent.name]);
48678
48917
  continue;
@@ -48697,9 +48936,9 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
48697
48936
  walk2(root, []);
48698
48937
  }
48699
48938
  function readInstalledPlugins2(home) {
48700
- const pluginsDir = import_node_path20.default.join(home, ".claude", "plugins");
48701
- const v2 = import_node_path20.default.join(pluginsDir, "installed_plugins_v2.json");
48702
- const v1 = import_node_path20.default.join(pluginsDir, "installed_plugins.json");
48939
+ const pluginsDir = import_node_path21.default.join(home, ".claude", "plugins");
48940
+ const v2 = import_node_path21.default.join(pluginsDir, "installed_plugins_v2.json");
48941
+ const v1 = import_node_path21.default.join(pluginsDir, "installed_plugins.json");
48703
48942
  let raw = null;
48704
48943
  for (const candidate of [v2, v1]) {
48705
48944
  try {
@@ -48728,19 +48967,19 @@ function readInstalledPlugins2(home) {
48728
48967
  return out;
48729
48968
  }
48730
48969
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
48731
- let cur = import_node_path20.default.resolve(startCwd);
48732
- const fsRoot = import_node_path20.default.parse(cur).root;
48970
+ let cur = import_node_path21.default.resolve(startCwd);
48971
+ const fsRoot = import_node_path21.default.parse(cur).root;
48733
48972
  while (true) {
48734
- scanAgentsDir(import_node_path20.default.join(cur, ".claude", "agents"), "project", seen, out);
48973
+ scanAgentsDir(import_node_path21.default.join(cur, ".claude", "agents"), "project", seen, out);
48735
48974
  let hasGit = false;
48736
48975
  try {
48737
- hasGit = import_node_fs22.default.existsSync(import_node_path20.default.join(cur, ".git"));
48976
+ hasGit = import_node_fs22.default.existsSync(import_node_path21.default.join(cur, ".git"));
48738
48977
  } catch {
48739
48978
  }
48740
48979
  if (hasGit) return;
48741
48980
  if (cur === home) return;
48742
48981
  if (cur === fsRoot) return;
48743
- const parent = import_node_path20.default.dirname(cur);
48982
+ const parent = import_node_path21.default.dirname(cur);
48744
48983
  if (parent === cur) return;
48745
48984
  cur = parent;
48746
48985
  }
@@ -48750,7 +48989,7 @@ var AgentsScanner = class {
48750
48989
  extraPluginRoots;
48751
48990
  policyDir;
48752
48991
  constructor(opts = {}) {
48753
- this.home = opts.home ?? process.env.CLAUDE_CONFIG_DIR ?? import_node_os8.default.homedir();
48992
+ this.home = opts.home ?? process.env.CLAUDE_CONFIG_DIR ?? import_node_os9.default.homedir();
48754
48993
  this.extraPluginRoots = opts.extraPluginRoots ?? [];
48755
48994
  if (opts.policyDir !== void 0) {
48756
48995
  this.policyDir = opts.policyDir;
@@ -48775,7 +49014,7 @@ var AgentsScanner = class {
48775
49014
  }
48776
49015
  const fsBlock = [];
48777
49016
  scanAgentsDir(
48778
- import_node_path20.default.join(this.home, ".claude", "agents"),
49017
+ import_node_path21.default.join(this.home, ".claude", "agents"),
48779
49018
  "global",
48780
49019
  seen,
48781
49020
  fsBlock
@@ -48789,7 +49028,7 @@ var AgentsScanner = class {
48789
49028
  ...this.extraPluginRoots
48790
49029
  ];
48791
49030
  for (const { name, root } of plugins) {
48792
- const agentsRoot = import_node_path20.default.join(root, "agents");
49031
+ const agentsRoot = import_node_path21.default.join(root, "agents");
48793
49032
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
48794
49033
  }
48795
49034
  return [...builtinBlock, ...fsBlock];
@@ -48798,27 +49037,27 @@ var AgentsScanner = class {
48798
49037
 
48799
49038
  // src/observer/session-observer.ts
48800
49039
  var import_node_fs24 = __toESM(require("fs"), 1);
48801
- var import_node_os10 = __toESM(require("os"), 1);
48802
- var import_node_path22 = __toESM(require("path"), 1);
49040
+ var import_node_os11 = __toESM(require("os"), 1);
49041
+ var import_node_path23 = __toESM(require("path"), 1);
48803
49042
  init_claude_history();
48804
49043
 
48805
49044
  // src/observer/subagent-meta-observer.ts
48806
49045
  var import_node_fs23 = __toESM(require("fs"), 1);
48807
- var import_node_os9 = __toESM(require("os"), 1);
48808
- var import_node_path21 = __toESM(require("path"), 1);
49046
+ var import_node_os10 = __toESM(require("os"), 1);
49047
+ var import_node_path22 = __toESM(require("path"), 1);
48809
49048
  init_claude_history();
48810
49049
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
48811
49050
  var SubagentMetaObserver = class {
48812
49051
  constructor(opts) {
48813
49052
  this.opts = opts;
48814
- this.home = opts.home ?? import_node_os9.default.homedir();
49053
+ this.home = opts.home ?? import_node_os10.default.homedir();
48815
49054
  }
48816
49055
  opts;
48817
49056
  home;
48818
49057
  watches = /* @__PURE__ */ new Map();
48819
49058
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
48820
49059
  resolveSubagentDir(cwd, toolSessionId) {
48821
- return import_node_path21.default.join(
49060
+ return import_node_path22.default.join(
48822
49061
  this.home,
48823
49062
  ".claude",
48824
49063
  "projects",
@@ -48874,7 +49113,7 @@ var SubagentMetaObserver = class {
48874
49113
  if (!m2) return;
48875
49114
  const agentId = m2[1];
48876
49115
  if (w2.emitted.has(agentId)) return;
48877
- const file = import_node_path21.default.join(w2.dirPath, name);
49116
+ const file = import_node_path22.default.join(w2.dirPath, name);
48878
49117
  let raw;
48879
49118
  try {
48880
49119
  raw = import_node_fs23.default.readFileSync(file, "utf8");
@@ -48922,7 +49161,7 @@ var SubagentMetaObserver = class {
48922
49161
  var SessionObserver = class {
48923
49162
  constructor(opts) {
48924
49163
  this.opts = opts;
48925
- this.home = opts.home ?? import_node_os10.default.homedir();
49164
+ this.home = opts.home ?? import_node_os11.default.homedir();
48926
49165
  this.metaObserver = opts.enableSubagentMetaObserver ? new SubagentMetaObserver({ home: this.home, onEvent: opts.onEvent }) : null;
48927
49166
  }
48928
49167
  opts;
@@ -48934,7 +49173,7 @@ var SessionObserver = class {
48934
49173
  metaObserver;
48935
49174
  resolveJsonlPath(cwd, toolSessionId, override) {
48936
49175
  if (override) return override;
48937
- return import_node_path22.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
49176
+ return import_node_path23.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
48938
49177
  }
48939
49178
  start(args) {
48940
49179
  this.stop(args.sessionId);
@@ -48955,10 +49194,10 @@ var SessionObserver = class {
48955
49194
  prevIsRejectSentinel: false
48956
49195
  };
48957
49196
  try {
48958
- import_node_fs24.default.mkdirSync(import_node_path22.default.dirname(filePath), { recursive: true });
49197
+ import_node_fs24.default.mkdirSync(import_node_path23.default.dirname(filePath), { recursive: true });
48959
49198
  } catch {
48960
49199
  }
48961
- w2.watcher = import_node_fs24.default.watch(import_node_path22.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
49200
+ w2.watcher = import_node_fs24.default.watch(import_node_path23.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
48962
49201
  if (!changedName || !filePath.endsWith(changedName)) return;
48963
49202
  this.poll(w2);
48964
49203
  });
@@ -49840,7 +50079,7 @@ async function authenticate(token, deps) {
49840
50079
 
49841
50080
  // src/permission/capability-store.ts
49842
50081
  var fs28 = __toESM(require("fs"), 1);
49843
- var path27 = __toESM(require("path"), 1);
50082
+ var path28 = __toESM(require("path"), 1);
49844
50083
  var CAPABILITIES_FILE_NAME = "capabilities.json";
49845
50084
  var FILE_VERSION = 1;
49846
50085
  var CapabilityStore = class {
@@ -49870,7 +50109,7 @@ var CapabilityStore = class {
49870
50109
  this.flush();
49871
50110
  }
49872
50111
  filePath() {
49873
- return path27.join(this.dataDir, CAPABILITIES_FILE_NAME);
50112
+ return path28.join(this.dataDir, CAPABILITIES_FILE_NAME);
49874
50113
  }
49875
50114
  readFromDisk() {
49876
50115
  const file = this.filePath();
@@ -50017,7 +50256,7 @@ function cleanupGuestSessionsForCapability(cap, factory) {
50017
50256
 
50018
50257
  // src/inbox/inbox-store.ts
50019
50258
  var fs30 = __toESM(require("fs"), 1);
50020
- var path28 = __toESM(require("path"), 1);
50259
+ var path29 = __toESM(require("path"), 1);
50021
50260
  var INBOX_SUBDIR = "inbox";
50022
50261
  var InboxStore = class {
50023
50262
  constructor(dataDir) {
@@ -50115,10 +50354,10 @@ var InboxStore = class {
50115
50354
  }
50116
50355
  }
50117
50356
  dirPath() {
50118
- return path28.join(this.dataDir, INBOX_SUBDIR);
50357
+ return path29.join(this.dataDir, INBOX_SUBDIR);
50119
50358
  }
50120
50359
  filePath(peerDeviceId) {
50121
- return path28.join(this.dirPath(), `${peerDeviceId}.jsonl`);
50360
+ return path29.join(this.dirPath(), `${peerDeviceId}.jsonl`);
50122
50361
  }
50123
50362
  };
50124
50363
  function parseAllLines(raw) {
@@ -50207,7 +50446,7 @@ var InboxManager = class {
50207
50446
 
50208
50447
  // src/state/contact-store.ts
50209
50448
  var fs31 = __toESM(require("fs"), 1);
50210
- var path29 = __toESM(require("path"), 1);
50449
+ var path30 = __toESM(require("path"), 1);
50211
50450
  var FILE_NAME = "contacts.json";
50212
50451
  var ContactStore = class {
50213
50452
  constructor(dataDir) {
@@ -50217,7 +50456,7 @@ var ContactStore = class {
50217
50456
  contacts = /* @__PURE__ */ new Map();
50218
50457
  load() {
50219
50458
  this.contacts.clear();
50220
- const file = path29.join(this.dataDir, FILE_NAME);
50459
+ const file = path30.join(this.dataDir, FILE_NAME);
50221
50460
  let raw;
50222
50461
  try {
50223
50462
  raw = fs31.readFileSync(file, "utf8");
@@ -50263,7 +50502,7 @@ var ContactStore = class {
50263
50502
  return existed;
50264
50503
  }
50265
50504
  flush() {
50266
- const file = path29.join(this.dataDir, FILE_NAME);
50505
+ const file = path30.join(this.dataDir, FILE_NAME);
50267
50506
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
50268
50507
  const content = JSON.stringify(
50269
50508
  { contacts: Array.from(this.contacts.values()) },
@@ -50427,52 +50666,52 @@ async function autoReverseContact(args) {
50427
50666
 
50428
50667
  // src/migrations/2026-05-20-flatten-sessions.ts
50429
50668
  var fs32 = __toESM(require("fs"), 1);
50430
- var path30 = __toESM(require("path"), 1);
50669
+ var path31 = __toESM(require("path"), 1);
50431
50670
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
50432
50671
  function migrateFlattenSessions(opts) {
50433
50672
  const dataDir = opts.dataDir;
50434
50673
  const now = opts.now ?? Date.now;
50435
- const sessionsDir = path30.join(dataDir, "sessions");
50436
- const flagPath = path30.join(sessionsDir, MIGRATION_FLAG_NAME);
50674
+ const sessionsDir = path31.join(dataDir, "sessions");
50675
+ const flagPath = path31.join(sessionsDir, MIGRATION_FLAG_NAME);
50437
50676
  if (existsSync3(flagPath)) {
50438
50677
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
50439
50678
  }
50440
50679
  let movedBare = 0;
50441
50680
  let movedVmOwner = 0;
50442
50681
  let archivedListener = 0;
50443
- const defaultDir = path30.join(sessionsDir, "default");
50682
+ const defaultDir = path31.join(sessionsDir, "default");
50444
50683
  if (existsSync3(defaultDir)) {
50445
50684
  for (const entry of readdirSafe(defaultDir)) {
50446
50685
  if (!entry.endsWith(".json")) continue;
50447
- const src = path30.join(defaultDir, entry);
50448
- const dst = path30.join(sessionsDir, entry);
50686
+ const src = path31.join(defaultDir, entry);
50687
+ const dst = path31.join(sessionsDir, entry);
50449
50688
  fs32.renameSync(src, dst);
50450
50689
  movedBare += 1;
50451
50690
  }
50452
50691
  rmdirIfEmpty(defaultDir);
50453
50692
  }
50454
50693
  for (const pid of readdirSafe(sessionsDir)) {
50455
- const personaDir = path30.join(sessionsDir, pid);
50694
+ const personaDir = path31.join(sessionsDir, pid);
50456
50695
  if (!isDir(personaDir)) continue;
50457
50696
  if (pid === "default") continue;
50458
- const ownerSrc = path30.join(personaDir, "owner");
50697
+ const ownerSrc = path31.join(personaDir, "owner");
50459
50698
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
50460
- const ownerDst = path30.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
50699
+ const ownerDst = path31.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
50461
50700
  fs32.mkdirSync(ownerDst, { recursive: true });
50462
50701
  for (const file of readdirSafe(ownerSrc)) {
50463
50702
  if (!file.endsWith(".json")) continue;
50464
- fs32.renameSync(path30.join(ownerSrc, file), path30.join(ownerDst, file));
50703
+ fs32.renameSync(path31.join(ownerSrc, file), path31.join(ownerDst, file));
50465
50704
  movedVmOwner += 1;
50466
50705
  }
50467
50706
  rmdirIfEmpty(ownerSrc);
50468
50707
  }
50469
- const listenerSrc = path30.join(personaDir, "listener");
50708
+ const listenerSrc = path31.join(personaDir, "listener");
50470
50709
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
50471
- const archiveDst = path30.join(dataDir, ".legacy", `listener-${pid}`);
50710
+ const archiveDst = path31.join(dataDir, ".legacy", `listener-${pid}`);
50472
50711
  fs32.mkdirSync(archiveDst, { recursive: true });
50473
50712
  for (const file of readdirSafe(listenerSrc)) {
50474
50713
  if (!file.endsWith(".json")) continue;
50475
- fs32.renameSync(path30.join(listenerSrc, file), path30.join(archiveDst, file));
50714
+ fs32.renameSync(path31.join(listenerSrc, file), path31.join(archiveDst, file));
50476
50715
  archivedListener += 1;
50477
50716
  }
50478
50717
  rmdirIfEmpty(listenerSrc);
@@ -50520,10 +50759,10 @@ function rmdirIfEmpty(p2) {
50520
50759
 
50521
50760
  // src/transport/http-router.ts
50522
50761
  var import_node_fs26 = __toESM(require("fs"), 1);
50523
- var import_node_path26 = __toESM(require("path"), 1);
50762
+ var import_node_path27 = __toESM(require("path"), 1);
50524
50763
 
50525
50764
  // src/attachment/mime.ts
50526
- var import_node_path23 = __toESM(require("path"), 1);
50765
+ var import_node_path24 = __toESM(require("path"), 1);
50527
50766
  var TEXT_PLAIN = "text/plain; charset=utf-8";
50528
50767
  var EXT_TO_NATIVE_MIME = {
50529
50768
  // 图片
@@ -50630,7 +50869,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
50630
50869
  ".mk"
50631
50870
  ]);
50632
50871
  function lookupMime(filePathOrName) {
50633
- const ext = import_node_path23.default.extname(filePathOrName).toLowerCase();
50872
+ const ext = import_node_path24.default.extname(filePathOrName).toLowerCase();
50634
50873
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
50635
50874
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
50636
50875
  return "application/octet-stream";
@@ -50700,7 +50939,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
50700
50939
 
50701
50940
  // src/attachment/upload.ts
50702
50941
  var import_node_fs25 = __toESM(require("fs"), 1);
50703
- var import_node_path24 = __toESM(require("path"), 1);
50942
+ var import_node_path25 = __toESM(require("path"), 1);
50704
50943
  var import_node_crypto7 = __toESM(require("crypto"), 1);
50705
50944
  var import_promises2 = require("stream/promises");
50706
50945
  var UploadError = class extends Error {
@@ -50712,14 +50951,14 @@ var UploadError = class extends Error {
50712
50951
  code;
50713
50952
  };
50714
50953
  function assertValidFileName(fileName) {
50715
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path24.default.basename(fileName)) {
50954
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path25.default.basename(fileName)) {
50716
50955
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
50717
50956
  }
50718
50957
  }
50719
50958
  var HASH_PREFIX_LEN = 16;
50720
50959
  async function writeUploadedAttachment(args) {
50721
50960
  assertValidFileName(args.fileName);
50722
- const attachmentsRoot = import_node_path24.default.join(args.sessionDir, ".attachments");
50961
+ const attachmentsRoot = import_node_path25.default.join(args.sessionDir, ".attachments");
50723
50962
  try {
50724
50963
  import_node_fs25.default.mkdirSync(attachmentsRoot, { recursive: true });
50725
50964
  } catch (err) {
@@ -50727,7 +50966,7 @@ async function writeUploadedAttachment(args) {
50727
50966
  }
50728
50967
  const hasher = import_node_crypto7.default.createHash("sha256");
50729
50968
  let actualSize = 0;
50730
- const tmpPath = import_node_path24.default.join(
50969
+ const tmpPath = import_node_path25.default.join(
50731
50970
  attachmentsRoot,
50732
50971
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto7.default.randomBytes(4).toString("hex")}`
50733
50972
  );
@@ -50762,7 +51001,7 @@ async function writeUploadedAttachment(args) {
50762
51001
  );
50763
51002
  }
50764
51003
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
50765
- const hashDir = import_node_path24.default.join(attachmentsRoot, attachmentId);
51004
+ const hashDir = import_node_path25.default.join(attachmentsRoot, attachmentId);
50766
51005
  let finalFileName;
50767
51006
  let hashDirExists = false;
50768
51007
  try {
@@ -50780,7 +51019,7 @@ async function writeUploadedAttachment(args) {
50780
51019
  try {
50781
51020
  import_node_fs25.default.mkdirSync(hashDir, { recursive: true });
50782
51021
  finalFileName = args.fileName;
50783
- import_node_fs25.default.renameSync(tmpPath, import_node_path24.default.join(hashDir, finalFileName));
51022
+ import_node_fs25.default.renameSync(tmpPath, import_node_path25.default.join(hashDir, finalFileName));
50784
51023
  } catch (err) {
50785
51024
  try {
50786
51025
  import_node_fs25.default.unlinkSync(tmpPath);
@@ -50789,8 +51028,8 @@ async function writeUploadedAttachment(args) {
50789
51028
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
50790
51029
  }
50791
51030
  }
50792
- const absPath = import_node_path24.default.join(hashDir, finalFileName);
50793
- const relPath = import_node_path24.default.relative(args.sessionDir, absPath);
51031
+ const absPath = import_node_path25.default.join(hashDir, finalFileName);
51032
+ const relPath = import_node_path25.default.relative(args.sessionDir, absPath);
50794
51033
  args.groupFileStore.upsert(args.scope, args.sessionId, {
50795
51034
  relPath: absPath,
50796
51035
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -50804,8 +51043,8 @@ async function writeUploadedAttachment(args) {
50804
51043
 
50805
51044
  // src/extension/import.ts
50806
51045
  var import_promises3 = __toESM(require("fs/promises"), 1);
50807
- var import_node_path25 = __toESM(require("path"), 1);
50808
- var import_node_os11 = __toESM(require("os"), 1);
51046
+ var import_node_path26 = __toESM(require("path"), 1);
51047
+ var import_node_os12 = __toESM(require("os"), 1);
50809
51048
  var import_jszip = __toESM(require_lib3(), 1);
50810
51049
  var ImportError = class extends Error {
50811
51050
  constructor(code, message) {
@@ -50822,7 +51061,7 @@ async function importZip(buf, root) {
50822
51061
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
50823
51062
  }
50824
51063
  for (const name of Object.keys(zip.files)) {
50825
- if (name.includes("..") || name.startsWith("/") || import_node_path25.default.isAbsolute(name)) {
51064
+ if (name.includes("..") || name.startsWith("/") || import_node_path26.default.isAbsolute(name)) {
50826
51065
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
50827
51066
  }
50828
51067
  }
@@ -50855,7 +51094,7 @@ async function importZip(buf, root) {
50855
51094
  );
50856
51095
  }
50857
51096
  }
50858
- const destDir = import_node_path25.default.join(root, manifest.id);
51097
+ const destDir = import_node_path26.default.join(root, manifest.id);
50859
51098
  let destExists = false;
50860
51099
  try {
50861
51100
  await import_promises3.default.access(destDir);
@@ -50865,15 +51104,15 @@ async function importZip(buf, root) {
50865
51104
  if (destExists) {
50866
51105
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
50867
51106
  }
50868
- const stage = await import_promises3.default.mkdtemp(import_node_path25.default.join(import_node_os11.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
51107
+ const stage = await import_promises3.default.mkdtemp(import_node_path26.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
50869
51108
  try {
50870
51109
  for (const [name, entry] of Object.entries(zip.files)) {
50871
- const dest = import_node_path25.default.join(stage, name);
51110
+ const dest = import_node_path26.default.join(stage, name);
50872
51111
  if (entry.dir) {
50873
51112
  await import_promises3.default.mkdir(dest, { recursive: true });
50874
51113
  continue;
50875
51114
  }
50876
- await import_promises3.default.mkdir(import_node_path25.default.dirname(dest), { recursive: true });
51115
+ await import_promises3.default.mkdir(import_node_path26.default.dirname(dest), { recursive: true });
50877
51116
  const content = await entry.async("nodebuffer");
50878
51117
  await import_promises3.default.writeFile(dest, content);
50879
51118
  }
@@ -50904,7 +51143,7 @@ var SHARE_UI_ASSET_MIME = {
50904
51143
  ".wasm": "application/wasm"
50905
51144
  };
50906
51145
  function shareUiAssetMime(filePath) {
50907
- const ext = import_node_path26.default.extname(filePath).toLowerCase();
51146
+ const ext = import_node_path27.default.extname(filePath).toLowerCase();
50908
51147
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
50909
51148
  }
50910
51149
  var DISPATCH_HEARTBEAT_MS = 25e3;
@@ -50991,7 +51230,7 @@ function isValidUploadFileName(fileName) {
50991
51230
  if (fileName === "." || fileName === "..") return false;
50992
51231
  if (fileName.startsWith(".")) return false;
50993
51232
  if (fileName.includes("/") || fileName.includes("\\")) return false;
50994
- return fileName === import_node_path26.default.basename(fileName);
51233
+ return fileName === import_node_path27.default.basename(fileName);
50995
51234
  }
50996
51235
  function createHttpRouter(deps) {
50997
51236
  return async (req, res) => {
@@ -51287,7 +51526,7 @@ function createHttpRouter(deps) {
51287
51526
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
51288
51527
  return true;
51289
51528
  }
51290
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path26.default.basename(r.absPath)));
51529
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path27.default.basename(r.absPath)));
51291
51530
  return true;
51292
51531
  }
51293
51532
  const ctx = deps.authResolver.resolveFromHeader(
@@ -51408,7 +51647,7 @@ function createHttpRouter(deps) {
51408
51647
  return true;
51409
51648
  }
51410
51649
  let absPath;
51411
- if (import_node_path26.default.isAbsolute(pathParam)) {
51650
+ if (import_node_path27.default.isAbsolute(pathParam)) {
51412
51651
  absPath = pathParam;
51413
51652
  } else if (deps.sessionStore) {
51414
51653
  const file = deps.sessionStore.read(sid);
@@ -51416,7 +51655,7 @@ function createHttpRouter(deps) {
51416
51655
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
51417
51656
  return true;
51418
51657
  }
51419
- absPath = import_node_path26.default.join(file.cwd, pathParam);
51658
+ absPath = import_node_path27.default.join(file.cwd, pathParam);
51420
51659
  } else {
51421
51660
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
51422
51661
  return true;
@@ -51518,7 +51757,7 @@ function streamFile(res, absPath, logger) {
51518
51757
  return;
51519
51758
  }
51520
51759
  const mime = lookupMime(absPath);
51521
- const basename = import_node_path26.default.basename(absPath);
51760
+ const basename = import_node_path27.default.basename(absPath);
51522
51761
  res.writeHead(200, {
51523
51762
  "Content-Type": mime,
51524
51763
  "Content-Length": String(stat.size),
@@ -51536,7 +51775,7 @@ function streamFile(res, absPath, logger) {
51536
51775
 
51537
51776
  // src/attachment/gc.ts
51538
51777
  var import_node_fs27 = __toESM(require("fs"), 1);
51539
- var import_node_path27 = __toESM(require("path"), 1);
51778
+ var import_node_path28 = __toESM(require("path"), 1);
51540
51779
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
51541
51780
  function runAttachmentGc(args) {
51542
51781
  const now = (args.now ?? Date.now)();
@@ -51545,17 +51784,17 @@ function runAttachmentGc(args) {
51545
51784
  for (const { scope, sessionId } of args.sessionScopes) {
51546
51785
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
51547
51786
  if (entry.stale) continue;
51548
- if (import_node_path27.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
51787
+ if (import_node_path28.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
51549
51788
  }
51550
51789
  }
51551
51790
  for (const { scope, sessionId } of args.sessionScopes) {
51552
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path27.default.join(
51791
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path28.default.join(
51553
51792
  args.dataDir,
51554
51793
  "sessions",
51555
51794
  ...scopeSubPath(scope).map(safeFileName),
51556
51795
  safeFileName(sessionId)
51557
51796
  );
51558
- const attRoot = import_node_path27.default.join(sessionDir, ".attachments");
51797
+ const attRoot = import_node_path28.default.join(sessionDir, ".attachments");
51559
51798
  let hashDirs;
51560
51799
  try {
51561
51800
  hashDirs = import_node_fs27.default.readdirSync(attRoot);
@@ -51565,7 +51804,7 @@ function runAttachmentGc(args) {
51565
51804
  continue;
51566
51805
  }
51567
51806
  for (const hashDir of hashDirs) {
51568
- const hashDirAbs = import_node_path27.default.join(attRoot, hashDir);
51807
+ const hashDirAbs = import_node_path28.default.join(attRoot, hashDir);
51569
51808
  let files;
51570
51809
  try {
51571
51810
  files = import_node_fs27.default.readdirSync(hashDirAbs);
@@ -51573,7 +51812,7 @@ function runAttachmentGc(args) {
51573
51812
  continue;
51574
51813
  }
51575
51814
  for (const name of files) {
51576
- const file = import_node_path27.default.join(hashDirAbs, name);
51815
+ const file = import_node_path28.default.join(hashDirAbs, name);
51577
51816
  let stat;
51578
51817
  try {
51579
51818
  stat = import_node_fs27.default.statSync(file);
@@ -51604,7 +51843,7 @@ function runAttachmentGc(args) {
51604
51843
 
51605
51844
  // src/attachment/group.ts
51606
51845
  var import_node_fs28 = __toESM(require("fs"), 1);
51607
- var import_node_path28 = __toESM(require("path"), 1);
51846
+ var import_node_path29 = __toESM(require("path"), 1);
51608
51847
  var import_node_crypto8 = __toESM(require("crypto"), 1);
51609
51848
  init_protocol();
51610
51849
  var GroupFileStore = class {
@@ -51616,11 +51855,11 @@ var GroupFileStore = class {
51616
51855
  this.logger = opts.logger;
51617
51856
  }
51618
51857
  rootForScope(scope) {
51619
- return import_node_path28.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
51858
+ return import_node_path29.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
51620
51859
  }
51621
51860
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
51622
51861
  filePath(scope, sessionId) {
51623
- return import_node_path28.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
51862
+ return import_node_path29.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
51624
51863
  }
51625
51864
  cacheKey(scope, sessionId) {
51626
51865
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -51655,7 +51894,7 @@ var GroupFileStore = class {
51655
51894
  }
51656
51895
  writeFile(scope, sessionId, entries) {
51657
51896
  const file = this.filePath(scope, sessionId);
51658
- import_node_fs28.default.mkdirSync(import_node_path28.default.dirname(file), { recursive: true });
51897
+ import_node_fs28.default.mkdirSync(import_node_path29.default.dirname(file), { recursive: true });
51659
51898
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
51660
51899
  import_node_fs28.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
51661
51900
  import_node_fs28.default.renameSync(tmp, file);
@@ -51745,9 +51984,9 @@ var GroupFileStore = class {
51745
51984
 
51746
51985
  // src/discovery/state-file.ts
51747
51986
  var import_node_fs29 = __toESM(require("fs"), 1);
51748
- var import_node_path29 = __toESM(require("path"), 1);
51987
+ var import_node_path30 = __toESM(require("path"), 1);
51749
51988
  function defaultStateFilePath(dataDir) {
51750
- return import_node_path29.default.join(dataDir, "state.json");
51989
+ return import_node_path30.default.join(dataDir, "state.json");
51751
51990
  }
51752
51991
  function isPidAlive(pid) {
51753
51992
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -51783,7 +52022,7 @@ var StateFileManager = class {
51783
52022
  return { status: "stale", existing };
51784
52023
  }
51785
52024
  write(state) {
51786
- import_node_fs29.default.mkdirSync(import_node_path29.default.dirname(this.file), { recursive: true });
52025
+ import_node_fs29.default.mkdirSync(import_node_path30.default.dirname(this.file), { recursive: true });
51787
52026
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
51788
52027
  import_node_fs29.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
51789
52028
  import_node_fs29.default.renameSync(tmp, this.file);
@@ -51812,13 +52051,13 @@ function readDaemonSourceFromEnv(env = process.env) {
51812
52051
 
51813
52052
  // src/tunnel/tunnel-manager.ts
51814
52053
  var import_node_fs33 = __toESM(require("fs"), 1);
51815
- var import_node_path33 = __toESM(require("path"), 1);
52054
+ var import_node_path34 = __toESM(require("path"), 1);
51816
52055
  var import_node_crypto9 = __toESM(require("crypto"), 1);
51817
52056
  var import_node_child_process9 = require("child_process");
51818
52057
 
51819
52058
  // src/tunnel/tunnel-store.ts
51820
52059
  var import_node_fs30 = __toESM(require("fs"), 1);
51821
- var import_node_path30 = __toESM(require("path"), 1);
52060
+ var import_node_path31 = __toESM(require("path"), 1);
51822
52061
  var TunnelStore = class {
51823
52062
  constructor(filePath) {
51824
52063
  this.filePath = filePath;
@@ -51837,7 +52076,7 @@ var TunnelStore = class {
51837
52076
  }
51838
52077
  }
51839
52078
  async set(v2) {
51840
- const dir = import_node_path30.default.dirname(this.filePath);
52079
+ const dir = import_node_path31.default.dirname(this.filePath);
51841
52080
  await import_node_fs30.default.promises.mkdir(dir, { recursive: true });
51842
52081
  const data = JSON.stringify(v2, null, 2);
51843
52082
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
@@ -51948,8 +52187,8 @@ function escape(v2) {
51948
52187
 
51949
52188
  // src/tunnel/frpc-binary.ts
51950
52189
  var import_node_fs31 = __toESM(require("fs"), 1);
51951
- var import_node_os12 = __toESM(require("os"), 1);
51952
- var import_node_path31 = __toESM(require("path"), 1);
52190
+ var import_node_os13 = __toESM(require("os"), 1);
52191
+ var import_node_path32 = __toESM(require("path"), 1);
51953
52192
  var import_node_child_process7 = require("child_process");
51954
52193
  var import_node_stream3 = require("stream");
51955
52194
  var import_promises4 = require("stream/promises");
@@ -51988,13 +52227,13 @@ async function ensureFrpcBinary(opts) {
51988
52227
  }
51989
52228
  const version2 = opts.version ?? FRPC_VERSION;
51990
52229
  const platform = opts.platform ?? detectPlatform();
51991
- const binDir = import_node_path31.default.join(opts.dataDir, "bin");
52230
+ const binDir = import_node_path32.default.join(opts.dataDir, "bin");
51992
52231
  import_node_fs31.default.mkdirSync(binDir, { recursive: true });
51993
52232
  cleanupStaleArtifacts(binDir);
51994
- const stableBin = import_node_path31.default.join(binDir, "frpc");
52233
+ const stableBin = import_node_path32.default.join(binDir, "frpc");
51995
52234
  if (import_node_fs31.default.existsSync(stableBin)) return stableBin;
51996
52235
  const partialBin = `${stableBin}.partial`;
51997
- const tarballPath = import_node_path31.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
52236
+ const tarballPath = import_node_path32.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
51998
52237
  try {
51999
52238
  const url = frpcDownloadUrl(version2, platform);
52000
52239
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -52020,7 +52259,7 @@ function cleanupStaleArtifacts(binDir) {
52020
52259
  }
52021
52260
  for (const name of entries) {
52022
52261
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
52023
- const full = import_node_path31.default.join(binDir, name);
52262
+ const full = import_node_path32.default.join(binDir, name);
52024
52263
  try {
52025
52264
  import_node_fs31.default.rmSync(full, { recursive: true, force: true });
52026
52265
  } catch {
@@ -52046,7 +52285,7 @@ async function downloadToFile(url, dest, fetchImpl) {
52046
52285
  await (0, import_promises4.pipeline)(nodeStream, out);
52047
52286
  }
52048
52287
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
52049
- const work = import_node_path31.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
52288
+ const work = import_node_path32.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
52050
52289
  import_node_fs31.default.mkdirSync(work, { recursive: true });
52051
52290
  try {
52052
52291
  await new Promise((resolve6, reject) => {
@@ -52055,7 +52294,7 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
52055
52294
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
52056
52295
  });
52057
52296
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
52058
- const src = import_node_path31.default.join(work, dirName, "frpc");
52297
+ const src = import_node_path32.default.join(work, dirName, "frpc");
52059
52298
  if (!import_node_fs31.default.existsSync(src)) {
52060
52299
  throw new Error(`frpc not found inside tarball at ${src}`);
52061
52300
  }
@@ -52067,10 +52306,10 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
52067
52306
 
52068
52307
  // src/tunnel/frpc-process.ts
52069
52308
  var import_node_fs32 = __toESM(require("fs"), 1);
52070
- var import_node_path32 = __toESM(require("path"), 1);
52309
+ var import_node_path33 = __toESM(require("path"), 1);
52071
52310
  var import_node_child_process8 = require("child_process");
52072
52311
  function frpcPidFilePath(dataDir) {
52073
- return import_node_path32.default.join(dataDir, "frpc.pid");
52312
+ return import_node_path33.default.join(dataDir, "frpc.pid");
52074
52313
  }
52075
52314
  function writeFrpcPid(dataDir, pid) {
52076
52315
  try {
@@ -52112,7 +52351,7 @@ function defaultSleep(ms) {
52112
52351
  }
52113
52352
  async function killStaleFrpc(deps) {
52114
52353
  const pidFile = frpcPidFilePath(deps.dataDir);
52115
- const tomlPath = import_node_path32.default.join(deps.dataDir, "frpc.toml");
52354
+ const tomlPath = import_node_path33.default.join(deps.dataDir, "frpc.toml");
52116
52355
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
52117
52356
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
52118
52357
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -52184,7 +52423,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
52184
52423
  var TunnelManager = class {
52185
52424
  constructor(deps) {
52186
52425
  this.deps = deps;
52187
- this.store = deps.store ?? new TunnelStore(import_node_path33.default.join(deps.dataDir, "tunnel.json"));
52426
+ this.store = deps.store ?? new TunnelStore(import_node_path34.default.join(deps.dataDir, "tunnel.json"));
52188
52427
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
52189
52428
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
52190
52429
  }
@@ -52311,7 +52550,7 @@ var TunnelManager = class {
52311
52550
  dataDir: this.deps.dataDir,
52312
52551
  override: this.deps.frpcBinaryOverride ?? void 0
52313
52552
  });
52314
- const tomlPath = import_node_path33.default.join(this.deps.dataDir, "frpc.toml");
52553
+ const tomlPath = import_node_path34.default.join(this.deps.dataDir, "frpc.toml");
52315
52554
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto9.default.randomBytes(3).toString("hex")}`;
52316
52555
  const toml = buildFrpcToml({
52317
52556
  serverAddr: t.frpsHost,
@@ -52326,7 +52565,7 @@ var TunnelManager = class {
52326
52565
  const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
52327
52566
  stdio: ["ignore", "pipe", "pipe"]
52328
52567
  });
52329
- const logFilePath = import_node_path33.default.join(this.deps.dataDir, "frpc.log");
52568
+ const logFilePath = import_node_path34.default.join(this.deps.dataDir, "frpc.log");
52330
52569
  const logStream = import_node_fs33.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
52331
52570
  logStream.on("error", () => {
52332
52571
  });
@@ -52409,16 +52648,16 @@ async function waitForFrpcReady(proc, timeoutMs) {
52409
52648
  }
52410
52649
 
52411
52650
  // src/tunnel/device-key.ts
52412
- var import_node_os13 = __toESM(require("os"), 1);
52413
- var import_node_path34 = __toESM(require("path"), 1);
52651
+ var import_node_os14 = __toESM(require("os"), 1);
52652
+ var import_node_path35 = __toESM(require("path"), 1);
52414
52653
  var import_node_crypto10 = __toESM(require("crypto"), 1);
52415
52654
  var DERIVE_SALT = "clawd-tunnel-device-v1";
52416
52655
  function deriveStableDeviceKey(opts = {}) {
52417
- const hostname = opts.hostname ?? import_node_os13.default.hostname();
52418
- const uid = opts.uid ?? (typeof import_node_os13.default.userInfo === "function" ? import_node_os13.default.userInfo().uid : 0);
52419
- const home = opts.home ?? import_node_os13.default.homedir();
52420
- const defaultDataDir = import_node_path34.default.resolve(import_node_path34.default.join(home, ".clawd"));
52421
- const normalizedDataDir = opts.dataDir ? import_node_path34.default.resolve(opts.dataDir) : null;
52656
+ const hostname = opts.hostname ?? import_node_os14.default.hostname();
52657
+ const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
52658
+ const home = opts.home ?? import_node_os14.default.homedir();
52659
+ const defaultDataDir = import_node_path35.default.resolve(import_node_path35.default.join(home, ".clawd"));
52660
+ const normalizedDataDir = opts.dataDir ? import_node_path35.default.resolve(opts.dataDir) : null;
52422
52661
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
52423
52662
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
52424
52663
  return import_node_crypto10.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
@@ -52426,11 +52665,11 @@ function deriveStableDeviceKey(opts = {}) {
52426
52665
 
52427
52666
  // src/auth-store.ts
52428
52667
  var import_node_fs34 = __toESM(require("fs"), 1);
52429
- var import_node_path35 = __toESM(require("path"), 1);
52668
+ var import_node_path36 = __toESM(require("path"), 1);
52430
52669
  var import_node_crypto11 = __toESM(require("crypto"), 1);
52431
52670
  var AUTH_FILE_NAME = "auth.json";
52432
52671
  function authFilePath(dataDir) {
52433
- return import_node_path35.default.join(dataDir, AUTH_FILE_NAME);
52672
+ return import_node_path36.default.join(dataDir, AUTH_FILE_NAME);
52434
52673
  }
52435
52674
  function loadOrCreateAuthFile(opts) {
52436
52675
  const file = authFilePath(opts.dataDir);
@@ -52486,7 +52725,7 @@ function readAuthFile(file) {
52486
52725
  }
52487
52726
  }
52488
52727
  function writeAuthFile(file, content) {
52489
- import_node_fs34.default.mkdirSync(import_node_path35.default.dirname(file), { recursive: true });
52728
+ import_node_fs34.default.mkdirSync(import_node_path36.default.dirname(file), { recursive: true });
52490
52729
  import_node_fs34.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
52491
52730
  try {
52492
52731
  import_node_fs34.default.chmodSync(file, 384);
@@ -52496,12 +52735,12 @@ function writeAuthFile(file, content) {
52496
52735
 
52497
52736
  // src/owner-profile.ts
52498
52737
  var import_node_fs35 = __toESM(require("fs"), 1);
52499
- var import_node_os14 = __toESM(require("os"), 1);
52500
- var import_node_path36 = __toESM(require("path"), 1);
52738
+ var import_node_os15 = __toESM(require("os"), 1);
52739
+ var import_node_path37 = __toESM(require("path"), 1);
52501
52740
  var PROFILE_FILENAME = "profile.json";
52502
52741
  function loadOwnerDisplayName(dataDir) {
52503
- const fallback = import_node_os14.default.userInfo().username;
52504
- const profilePath = import_node_path36.default.join(dataDir, PROFILE_FILENAME);
52742
+ const fallback = import_node_os15.default.userInfo().username;
52743
+ const profilePath = import_node_path37.default.join(dataDir, PROFILE_FILENAME);
52505
52744
  let raw;
52506
52745
  try {
52507
52746
  raw = import_node_fs35.default.readFileSync(profilePath, "utf8");
@@ -52528,12 +52767,12 @@ function loadOwnerDisplayName(dataDir) {
52528
52767
 
52529
52768
  // src/feishu-auth/owner-identity-store.ts
52530
52769
  var import_node_fs36 = __toESM(require("fs"), 1);
52531
- var import_node_path37 = __toESM(require("path"), 1);
52770
+ var import_node_path38 = __toESM(require("path"), 1);
52532
52771
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
52533
52772
  var OwnerIdentityStore = class {
52534
52773
  file;
52535
52774
  constructor(dataDir) {
52536
- this.file = import_node_path37.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
52775
+ this.file = import_node_path38.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
52537
52776
  }
52538
52777
  read() {
52539
52778
  let raw;
@@ -52566,7 +52805,7 @@ var OwnerIdentityStore = class {
52566
52805
  };
52567
52806
  }
52568
52807
  write(record) {
52569
- import_node_fs36.default.mkdirSync(import_node_path37.default.dirname(this.file), { recursive: true });
52808
+ import_node_fs36.default.mkdirSync(import_node_path38.default.dirname(this.file), { recursive: true });
52570
52809
  import_node_fs36.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
52571
52810
  try {
52572
52811
  import_node_fs36.default.chmodSync(this.file, 384);
@@ -52696,9 +52935,9 @@ var CentralClientError = class extends Error {
52696
52935
  code;
52697
52936
  cause;
52698
52937
  };
52699
- async function centralRequest(opts, path67, init) {
52938
+ async function centralRequest(opts, path68, init) {
52700
52939
  const f = opts.fetchImpl ?? globalThis.fetch;
52701
- const url = `${opts.api.replace(/\/+$/, "")}${path67}`;
52940
+ const url = `${opts.api.replace(/\/+$/, "")}${path68}`;
52702
52941
  const ctrl = new AbortController();
52703
52942
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
52704
52943
  let res;
@@ -52841,7 +53080,7 @@ function verifyConnectToken(args) {
52841
53080
 
52842
53081
  // src/feishu-auth/server-key.ts
52843
53082
  var fs46 = __toESM(require("fs"), 1);
52844
- var path46 = __toESM(require("path"), 1);
53083
+ var path47 = __toESM(require("path"), 1);
52845
53084
  var FILE_NAME2 = "server-signing-key.json";
52846
53085
  var ServerKeyStore = class {
52847
53086
  constructor(dataDir) {
@@ -52849,7 +53088,7 @@ var ServerKeyStore = class {
52849
53088
  }
52850
53089
  dataDir;
52851
53090
  filePath() {
52852
- return path46.join(this.dataDir, FILE_NAME2);
53091
+ return path47.join(this.dataDir, FILE_NAME2);
52853
53092
  }
52854
53093
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
52855
53094
  read() {
@@ -52888,8 +53127,8 @@ init_protocol();
52888
53127
 
52889
53128
  // src/session/fork.ts
52890
53129
  var import_node_fs37 = __toESM(require("fs"), 1);
52891
- var import_node_os15 = __toESM(require("os"), 1);
52892
- var import_node_path38 = __toESM(require("path"), 1);
53130
+ var import_node_os16 = __toESM(require("os"), 1);
53131
+ var import_node_path39 = __toESM(require("path"), 1);
52893
53132
  init_claude_history();
52894
53133
  function readJsonlEntries(file) {
52895
53134
  const raw = import_node_fs37.default.readFileSync(file, "utf8");
@@ -52905,9 +53144,9 @@ function readJsonlEntries(file) {
52905
53144
  return out;
52906
53145
  }
52907
53146
  function forkSession(input) {
52908
- const baseDir = input.baseDir ?? import_node_path38.default.join(import_node_os15.default.homedir(), ".claude");
52909
- const projectDir = import_node_path38.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
52910
- const sourceFile = import_node_path38.default.join(projectDir, `${input.toolSessionId}.jsonl`);
53147
+ const baseDir = input.baseDir ?? import_node_path39.default.join(import_node_os16.default.homedir(), ".claude");
53148
+ const projectDir = import_node_path39.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
53149
+ const sourceFile = import_node_path39.default.join(projectDir, `${input.toolSessionId}.jsonl`);
52911
53150
  if (!import_node_fs37.default.existsSync(sourceFile)) {
52912
53151
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
52913
53152
  }
@@ -52938,7 +53177,7 @@ function forkSession(input) {
52938
53177
  }
52939
53178
  forkedLines.push(JSON.stringify(forked));
52940
53179
  }
52941
- const forkedFilePath = import_node_path38.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
53180
+ const forkedFilePath = import_node_path39.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
52942
53181
  import_node_fs37.default.mkdirSync(projectDir, { recursive: true });
52943
53182
  import_node_fs37.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
52944
53183
  return { forkedToolSessionId, forkedFilePath };
@@ -53292,7 +53531,7 @@ function buildPermissionHandlers(deps) {
53292
53531
  }
53293
53532
 
53294
53533
  // src/handlers/history.ts
53295
- var path49 = __toESM(require("path"), 1);
53534
+ var path50 = __toESM(require("path"), 1);
53296
53535
  init_protocol();
53297
53536
 
53298
53537
  // src/session/recent-dirs.ts
@@ -53310,7 +53549,7 @@ function listRecentDirs(store, limit = 50) {
53310
53549
  }
53311
53550
 
53312
53551
  // src/permission/persona-paths.ts
53313
- var path48 = __toESM(require("path"), 1);
53552
+ var path49 = __toESM(require("path"), 1);
53314
53553
  function getAllowedPersonaIds(grants, action) {
53315
53554
  const ids = /* @__PURE__ */ new Set();
53316
53555
  for (const g2 of grants) {
@@ -53323,42 +53562,42 @@ function getAllowedPersonaIds(grants, action) {
53323
53562
  return ids;
53324
53563
  }
53325
53564
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
53326
- const target = path48.resolve(absPath);
53565
+ const target = path49.resolve(absPath);
53327
53566
  if (userWorkDir) {
53328
- const u = path48.resolve(userWorkDir);
53329
- const usep = u.endsWith(path48.sep) ? "" : path48.sep;
53567
+ const u = path49.resolve(userWorkDir);
53568
+ const usep = u.endsWith(path49.sep) ? "" : path49.sep;
53330
53569
  if (target === u || target.startsWith(u + usep)) return true;
53331
53570
  }
53332
- const root = path48.resolve(personaRoot);
53333
- const sep3 = root.endsWith(path48.sep) ? "" : path48.sep;
53571
+ const root = path49.resolve(personaRoot);
53572
+ const sep3 = root.endsWith(path49.sep) ? "" : path49.sep;
53334
53573
  if (!target.startsWith(root + sep3)) return false;
53335
- const rel = path48.relative(root, target);
53574
+ const rel = path49.relative(root, target);
53336
53575
  if (!rel || rel.startsWith("..")) return false;
53337
- const personaId = rel.split(path48.sep)[0];
53576
+ const personaId = rel.split(path49.sep)[0];
53338
53577
  if (!personaId) return false;
53339
53578
  const allowed = getAllowedPersonaIds(grants, action);
53340
53579
  if (allowed === "*") return true;
53341
53580
  return allowed.has(personaId);
53342
53581
  }
53343
53582
  function personaIdFromPath(absPath, personaRoot) {
53344
- const root = path48.resolve(personaRoot);
53345
- const target = path48.resolve(absPath);
53346
- const sep3 = root.endsWith(path48.sep) ? "" : path48.sep;
53583
+ const root = path49.resolve(personaRoot);
53584
+ const target = path49.resolve(absPath);
53585
+ const sep3 = root.endsWith(path49.sep) ? "" : path49.sep;
53347
53586
  if (!target.startsWith(root + sep3)) return null;
53348
- const rel = path48.relative(root, target);
53587
+ const rel = path49.relative(root, target);
53349
53588
  if (!rel || rel.startsWith("..")) return null;
53350
- const id = rel.split(path48.sep)[0];
53589
+ const id = rel.split(path49.sep)[0];
53351
53590
  return id || null;
53352
53591
  }
53353
53592
  function isPathWithin(dir, absPath) {
53354
- const d = path48.resolve(dir);
53355
- const t = path48.resolve(absPath);
53356
- const sep3 = d.endsWith(path48.sep) ? "" : path48.sep;
53593
+ const d = path49.resolve(dir);
53594
+ const t = path49.resolve(absPath);
53595
+ const sep3 = d.endsWith(path49.sep) ? "" : path49.sep;
53357
53596
  return t === d || t.startsWith(d + sep3);
53358
53597
  }
53359
53598
  function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
53360
53599
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
53361
- return personaIdFromPath(path48.resolve(absPath), personaRoot) === personaId;
53600
+ return personaIdFromPath(path49.resolve(absPath), personaRoot) === personaId;
53362
53601
  }
53363
53602
 
53364
53603
  // src/handlers/history.ts
@@ -53384,7 +53623,7 @@ function buildHistoryHandlers(deps) {
53384
53623
  if (!pid) return false;
53385
53624
  return isGuestPathAllowed(
53386
53625
  ctx.grants,
53387
- path49.join(personaRoot, pid),
53626
+ path50.join(personaRoot, pid),
53388
53627
  personaRoot,
53389
53628
  "read",
53390
53629
  userWorkDir
@@ -53396,7 +53635,7 @@ function buildHistoryHandlers(deps) {
53396
53635
  };
53397
53636
  const list = async (frame, _client, ctx) => {
53398
53637
  const args = HistoryListArgs.parse(frame);
53399
- assertGuestPath(ctx, path49.resolve(args.projectPath), personaRoot, "history:list");
53638
+ assertGuestPath(ctx, path50.resolve(args.projectPath), personaRoot, "history:list");
53400
53639
  const sessions = await history.listSessions(args);
53401
53640
  return { response: { type: "history:list", sessions } };
53402
53641
  };
@@ -53428,13 +53667,13 @@ function buildHistoryHandlers(deps) {
53428
53667
  };
53429
53668
  const subagents = async (frame, _client, ctx) => {
53430
53669
  const args = HistorySubagentsArgs.parse(frame);
53431
- assertGuestPath(ctx, path49.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
53670
+ assertGuestPath(ctx, path50.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
53432
53671
  const subs = await history.listSubagents(args);
53433
53672
  return { response: { type: "history:subagents", subagents: subs } };
53434
53673
  };
53435
53674
  const subagentRead = async (frame, _client, ctx) => {
53436
53675
  const args = HistorySubagentReadArgs.parse(frame);
53437
- assertGuestPath(ctx, path49.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
53676
+ assertGuestPath(ctx, path50.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
53438
53677
  const res = await history.readSubagent(args);
53439
53678
  return { response: { type: "history:subagent-read", ...res } };
53440
53679
  };
@@ -53443,7 +53682,7 @@ function buildHistoryHandlers(deps) {
53443
53682
  if (ctx?.principal.kind === "guest" && personaRoot) {
53444
53683
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
53445
53684
  const filtered = dirs.filter(
53446
- (d) => isGuestPathAllowed(ctx.grants, path49.resolve(d.cwd), personaRoot, "read", userWorkDir)
53685
+ (d) => isGuestPathAllowed(ctx.grants, path50.resolve(d.cwd), personaRoot, "read", userWorkDir)
53447
53686
  );
53448
53687
  return { response: { type: "history:recentDirs", dirs: filtered } };
53449
53688
  }
@@ -53460,8 +53699,8 @@ function buildHistoryHandlers(deps) {
53460
53699
  }
53461
53700
 
53462
53701
  // src/handlers/workspace.ts
53463
- var path50 = __toESM(require("path"), 1);
53464
- var os15 = __toESM(require("os"), 1);
53702
+ var path51 = __toESM(require("path"), 1);
53703
+ var os16 = __toESM(require("os"), 1);
53465
53704
  init_protocol();
53466
53705
  init_protocol();
53467
53706
  function buildEnabledPluginNames(personaManager, personaId) {
@@ -53501,23 +53740,23 @@ function buildWorkspaceHandlers(deps) {
53501
53740
  const list = async (frame, _client, ctx) => {
53502
53741
  const args = WorkspaceListArgs.parse(frame);
53503
53742
  const isGuest = ctx?.principal.kind === "guest";
53504
- const fallbackCwd = isGuest && personaRoot ? personaRoot : os15.homedir();
53505
- const resolvedCwd = path50.resolve(args.cwd ?? fallbackCwd);
53506
- const target = args.path ? path50.resolve(resolvedCwd, args.path) : resolvedCwd;
53743
+ const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
53744
+ const resolvedCwd = path51.resolve(args.cwd ?? fallbackCwd);
53745
+ const target = args.path ? path51.resolve(resolvedCwd, args.path) : resolvedCwd;
53507
53746
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
53508
53747
  const res = workspace.list({ ...args, cwd: resolvedCwd });
53509
53748
  return { response: { type: "workspace:list", ...res } };
53510
53749
  };
53511
53750
  const read = async (frame, _client, ctx) => {
53512
53751
  const args = WorkspaceReadArgs.parse(frame);
53513
- const target = path50.isAbsolute(args.path) ? path50.resolve(args.path) : path50.resolve(args.cwd, args.path);
53752
+ const target = path51.isAbsolute(args.path) ? path51.resolve(args.path) : path51.resolve(args.cwd, args.path);
53514
53753
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
53515
53754
  const res = workspace.read(args);
53516
53755
  return { response: { type: "workspace:read", ...res } };
53517
53756
  };
53518
53757
  const skillsList = async (frame, _client, ctx) => {
53519
53758
  const args = SkillsListArgs.parse(frame);
53520
- const cwdAbs = path50.resolve(args.cwd);
53759
+ const cwdAbs = path51.resolve(args.cwd);
53521
53760
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
53522
53761
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
53523
53762
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -53529,7 +53768,7 @@ function buildWorkspaceHandlers(deps) {
53529
53768
  };
53530
53769
  const agentsList = async (frame, _client, ctx) => {
53531
53770
  const args = AgentsListArgs.parse(frame);
53532
- const cwdAbs = path50.resolve(args.cwd);
53771
+ const cwdAbs = path51.resolve(args.cwd);
53533
53772
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
53534
53773
  if (args.tool === "codex") {
53535
53774
  return { response: { type: "agents:list", agents: [] } };
@@ -53551,18 +53790,18 @@ function buildWorkspaceHandlers(deps) {
53551
53790
  }
53552
53791
 
53553
53792
  // src/handlers/git.ts
53554
- var path52 = __toESM(require("path"), 1);
53793
+ var path53 = __toESM(require("path"), 1);
53555
53794
  init_protocol();
53556
53795
  init_protocol();
53557
53796
 
53558
53797
  // src/workspace/git.ts
53559
53798
  var import_node_child_process10 = require("child_process");
53560
53799
  var import_node_fs38 = __toESM(require("fs"), 1);
53561
- var import_node_path39 = __toESM(require("path"), 1);
53800
+ var import_node_path40 = __toESM(require("path"), 1);
53562
53801
  var import_node_util = require("util");
53563
53802
  var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
53564
53803
  function normalizePath(p2) {
53565
- const resolved = import_node_path39.default.resolve(p2);
53804
+ const resolved = import_node_path40.default.resolve(p2);
53566
53805
  try {
53567
53806
  return import_node_fs38.default.realpathSync(resolved);
53568
53807
  } catch {
@@ -53638,7 +53877,7 @@ async function listGitBranches(cwd) {
53638
53877
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
53639
53878
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
53640
53879
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
53641
- if (!isGuestPathAllowed(ctx.grants, path52.resolve(cwd), personaRoot, "read", userWorkDir)) {
53880
+ if (!isGuestPathAllowed(ctx.grants, path53.resolve(cwd), personaRoot, "read", userWorkDir)) {
53642
53881
  throw new ClawdError(
53643
53882
  ERROR_CODES.UNAUTHORIZED,
53644
53883
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -54124,7 +54363,7 @@ function buildDeviceHandlers(deps) {
54124
54363
  }
54125
54364
 
54126
54365
  // src/handlers/meta.ts
54127
- var import_node_os16 = __toESM(require("os"), 1);
54366
+ var import_node_os17 = __toESM(require("os"), 1);
54128
54367
  init_protocol();
54129
54368
 
54130
54369
  // src/version.ts
@@ -54156,7 +54395,7 @@ function buildReadyFrame(deps, client) {
54156
54395
  return {
54157
54396
  version,
54158
54397
  protocolVersion: PROTOCOL_VERSION,
54159
- hostname: import_node_os16.default.hostname(),
54398
+ hostname: import_node_os17.default.hostname(),
54160
54399
  os: process.platform,
54161
54400
  tools,
54162
54401
  runningSessions: info.runningSessions,
@@ -54253,7 +54492,7 @@ function buildPersonaHandlers(deps) {
54253
54492
  }
54254
54493
 
54255
54494
  // src/handlers/attachment.ts
54256
- var import_node_path40 = __toESM(require("path"), 1);
54495
+ var import_node_path41 = __toESM(require("path"), 1);
54257
54496
  init_protocol();
54258
54497
  init_protocol();
54259
54498
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -54333,12 +54572,12 @@ function buildAttachmentHandlers(deps) {
54333
54572
  `session ${args.sessionId} scope unresolved`
54334
54573
  );
54335
54574
  }
54336
- const cwdAbs = import_node_path40.default.resolve(sessionFile.cwd);
54337
- const candidateAbs = import_node_path40.default.isAbsolute(args.relPath) ? import_node_path40.default.resolve(args.relPath) : import_node_path40.default.resolve(cwdAbs, args.relPath);
54575
+ const cwdAbs = import_node_path41.default.resolve(sessionFile.cwd);
54576
+ const candidateAbs = import_node_path41.default.isAbsolute(args.relPath) ? import_node_path41.default.resolve(args.relPath) : import_node_path41.default.resolve(cwdAbs, args.relPath);
54338
54577
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
54339
54578
  const entries = deps.groupFileStore.list(scope, args.sessionId);
54340
54579
  const entry = entries.find((e) => {
54341
- const storedAbs = import_node_path40.default.isAbsolute(e.relPath) ? import_node_path40.default.resolve(e.relPath) : import_node_path40.default.resolve(cwdAbs, e.relPath);
54580
+ const storedAbs = import_node_path41.default.isAbsolute(e.relPath) ? import_node_path41.default.resolve(e.relPath) : import_node_path41.default.resolve(cwdAbs, e.relPath);
54342
54581
  return storedAbs === candidateAbs && !e.stale;
54343
54582
  });
54344
54583
  if (!entry) {
@@ -54363,7 +54602,7 @@ function buildAttachmentHandlers(deps) {
54363
54602
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
54364
54603
  const f = deps.sessionStore.read(sessionId);
54365
54604
  if (!f) return;
54366
- assertGuestAttachmentPath(ctx, import_node_path40.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
54605
+ assertGuestAttachmentPath(ctx, import_node_path41.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
54367
54606
  }
54368
54607
  const groupAdd = async (frame, _client, ctx) => {
54369
54608
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -54378,8 +54617,8 @@ function buildAttachmentHandlers(deps) {
54378
54617
  if (!scope) {
54379
54618
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
54380
54619
  }
54381
- const cwdAbs = import_node_path40.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
54382
- const candidateAbs = import_node_path40.default.isAbsolute(args.relPath) ? import_node_path40.default.resolve(args.relPath) : import_node_path40.default.resolve(cwdAbs, args.relPath);
54620
+ const cwdAbs = import_node_path41.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
54621
+ const candidateAbs = import_node_path41.default.isAbsolute(args.relPath) ? import_node_path41.default.resolve(args.relPath) : import_node_path41.default.resolve(cwdAbs, args.relPath);
54383
54622
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
54384
54623
  const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
54385
54624
  const size = 0;
@@ -54438,19 +54677,19 @@ function buildAttachmentHandlers(deps) {
54438
54677
 
54439
54678
  // src/handlers/extension.ts
54440
54679
  var import_promises8 = __toESM(require("fs/promises"), 1);
54441
- var import_node_path45 = __toESM(require("path"), 1);
54680
+ var import_node_path46 = __toESM(require("path"), 1);
54442
54681
  init_protocol();
54443
54682
 
54444
54683
  // src/extension/bundle-zip.ts
54445
54684
  var import_promises5 = __toESM(require("fs/promises"), 1);
54446
- var import_node_path41 = __toESM(require("path"), 1);
54685
+ var import_node_path42 = __toESM(require("path"), 1);
54447
54686
  var import_node_crypto13 = __toESM(require("crypto"), 1);
54448
54687
  var import_jszip2 = __toESM(require_lib3(), 1);
54449
54688
  async function bundleExtensionDir(dir) {
54450
54689
  const entries = await listFilesSorted(dir);
54451
54690
  const zip = new import_jszip2.default();
54452
54691
  for (const rel of entries) {
54453
- const abs = import_node_path41.default.join(dir, rel);
54692
+ const abs = import_node_path42.default.join(dir, rel);
54454
54693
  const content = await import_promises5.default.readFile(abs);
54455
54694
  zip.file(rel, content, { date: FIXED_DATE });
54456
54695
  }
@@ -54471,7 +54710,7 @@ async function listFilesSorted(rootDir) {
54471
54710
  return out;
54472
54711
  }
54473
54712
  async function walk(absRoot, relPrefix, out) {
54474
- const dirAbs = import_node_path41.default.join(absRoot, relPrefix);
54713
+ const dirAbs = import_node_path42.default.join(absRoot, relPrefix);
54475
54714
  const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
54476
54715
  for (const e of entries) {
54477
54716
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -54525,25 +54764,25 @@ function computePublishCheck(args) {
54525
54764
 
54526
54765
  // src/extension/install-flow.ts
54527
54766
  var import_promises6 = __toESM(require("fs/promises"), 1);
54528
- var import_node_path43 = __toESM(require("path"), 1);
54529
- var import_node_os18 = __toESM(require("os"), 1);
54767
+ var import_node_path44 = __toESM(require("path"), 1);
54768
+ var import_node_os19 = __toESM(require("os"), 1);
54530
54769
  var import_node_crypto14 = __toESM(require("crypto"), 1);
54531
54770
  var import_jszip3 = __toESM(require_lib3(), 1);
54532
54771
 
54533
54772
  // src/extension/paths.ts
54534
- var import_node_os17 = __toESM(require("os"), 1);
54535
- var import_node_path42 = __toESM(require("path"), 1);
54773
+ var import_node_os18 = __toESM(require("os"), 1);
54774
+ var import_node_path43 = __toESM(require("path"), 1);
54536
54775
  function clawdHomeRoot(override) {
54537
- return override ?? process.env.CLAWD_HOME ?? import_node_path42.default.join(import_node_os17.default.homedir(), ".clawd");
54776
+ return override ?? process.env.CLAWD_HOME ?? import_node_path43.default.join(import_node_os18.default.homedir(), ".clawd");
54538
54777
  }
54539
54778
  function extensionsRoot(override) {
54540
- return import_node_path42.default.join(clawdHomeRoot(override), "extensions");
54779
+ return import_node_path43.default.join(clawdHomeRoot(override), "extensions");
54541
54780
  }
54542
54781
  function publishedChannelsFile(override) {
54543
- return import_node_path42.default.join(clawdHomeRoot(override), "extensions-published.json");
54782
+ return import_node_path43.default.join(clawdHomeRoot(override), "extensions-published.json");
54544
54783
  }
54545
54784
  function bundleCacheRoot(override) {
54546
- return import_node_path42.default.join(clawdHomeRoot(override), "extension-bundles");
54785
+ return import_node_path43.default.join(clawdHomeRoot(override), "extension-bundles");
54547
54786
  }
54548
54787
 
54549
54788
  // src/extension/install-flow.ts
@@ -54570,7 +54809,7 @@ async function installFromChannel(args, deps) {
54570
54809
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
54571
54810
  }
54572
54811
  for (const name of Object.keys(zip.files)) {
54573
- if (name.includes("..") || name.startsWith("/") || import_node_path43.default.isAbsolute(name)) {
54812
+ if (name.includes("..") || name.startsWith("/") || import_node_path44.default.isAbsolute(name)) {
54574
54813
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
54575
54814
  }
54576
54815
  }
@@ -54602,7 +54841,7 @@ async function installFromChannel(args, deps) {
54602
54841
  );
54603
54842
  }
54604
54843
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
54605
- const destDir = import_node_path43.default.join(deps.extensionsRoot, localExtId);
54844
+ const destDir = import_node_path44.default.join(deps.extensionsRoot, localExtId);
54606
54845
  let destExists = false;
54607
54846
  try {
54608
54847
  await import_promises6.default.access(destDir);
@@ -54616,16 +54855,16 @@ async function installFromChannel(args, deps) {
54616
54855
  );
54617
54856
  }
54618
54857
  const stage = await import_promises6.default.mkdtemp(
54619
- import_node_path43.default.join(import_node_os18.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
54858
+ import_node_path44.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
54620
54859
  );
54621
54860
  try {
54622
54861
  for (const [name, entry] of Object.entries(zip.files)) {
54623
- const dest = import_node_path43.default.join(stage, name);
54862
+ const dest = import_node_path44.default.join(stage, name);
54624
54863
  if (entry.dir) {
54625
54864
  await import_promises6.default.mkdir(dest, { recursive: true });
54626
54865
  continue;
54627
54866
  }
54628
- await import_promises6.default.mkdir(import_node_path43.default.dirname(dest), { recursive: true });
54867
+ await import_promises6.default.mkdir(import_node_path44.default.dirname(dest), { recursive: true });
54629
54868
  if (name === "manifest.json") {
54630
54869
  const rewritten = { ...parsed.data, id: localExtId };
54631
54870
  await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -54646,8 +54885,8 @@ async function installFromChannel(args, deps) {
54646
54885
 
54647
54886
  // src/extension/update-flow.ts
54648
54887
  var import_promises7 = __toESM(require("fs/promises"), 1);
54649
- var import_node_path44 = __toESM(require("path"), 1);
54650
- var import_node_os19 = __toESM(require("os"), 1);
54888
+ var import_node_path45 = __toESM(require("path"), 1);
54889
+ var import_node_os20 = __toESM(require("os"), 1);
54651
54890
  var import_node_crypto15 = __toESM(require("crypto"), 1);
54652
54891
  var import_jszip4 = __toESM(require_lib3(), 1);
54653
54892
  var UpdateError = class extends Error {
@@ -54663,11 +54902,11 @@ async function updateFromChannel(args, deps) {
54663
54902
  channelRef.extId,
54664
54903
  channelRef.ownerPrincipalId
54665
54904
  );
54666
- const liveDir = import_node_path44.default.join(deps.extensionsRoot, localExtId);
54905
+ const liveDir = import_node_path45.default.join(deps.extensionsRoot, localExtId);
54667
54906
  const prevDir = `${liveDir}.prev`;
54668
54907
  let existingVersion;
54669
54908
  try {
54670
- const raw = await import_promises7.default.readFile(import_node_path44.default.join(liveDir, "manifest.json"), "utf8");
54909
+ const raw = await import_promises7.default.readFile(import_node_path45.default.join(liveDir, "manifest.json"), "utf8");
54671
54910
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
54672
54911
  if (!parsed2.success) {
54673
54912
  throw new UpdateError(
@@ -54700,7 +54939,7 @@ async function updateFromChannel(args, deps) {
54700
54939
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
54701
54940
  }
54702
54941
  for (const name of Object.keys(zip.files)) {
54703
- if (name.includes("..") || name.startsWith("/") || import_node_path44.default.isAbsolute(name)) {
54942
+ if (name.includes("..") || name.startsWith("/") || import_node_path45.default.isAbsolute(name)) {
54704
54943
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
54705
54944
  }
54706
54945
  }
@@ -54735,16 +54974,16 @@ async function updateFromChannel(args, deps) {
54735
54974
  await import_promises7.default.rm(prevDir, { recursive: true, force: true });
54736
54975
  await import_promises7.default.rename(liveDir, prevDir);
54737
54976
  const stage = await import_promises7.default.mkdtemp(
54738
- import_node_path44.default.join(import_node_os19.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
54977
+ import_node_path45.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
54739
54978
  );
54740
54979
  try {
54741
54980
  for (const [name, entry] of Object.entries(zip.files)) {
54742
- const dest = import_node_path44.default.join(stage, name);
54981
+ const dest = import_node_path45.default.join(stage, name);
54743
54982
  if (entry.dir) {
54744
54983
  await import_promises7.default.mkdir(dest, { recursive: true });
54745
54984
  continue;
54746
54985
  }
54747
- await import_promises7.default.mkdir(import_node_path44.default.dirname(dest), { recursive: true });
54986
+ await import_promises7.default.mkdir(import_node_path45.default.dirname(dest), { recursive: true });
54748
54987
  if (name === "manifest.json") {
54749
54988
  const rewritten = { ...parsed.data, id: localExtId };
54750
54989
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -54837,7 +55076,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
54837
55076
  );
54838
55077
  }
54839
55078
  }
54840
- const manifestPath = import_node_path45.default.join(root, extId, "manifest.json");
55079
+ const manifestPath = import_node_path46.default.join(root, extId, "manifest.json");
54841
55080
  const manifest = await readManifest(root, extId);
54842
55081
  const next = { ...manifest, version: newVersion };
54843
55082
  const tmp = `${manifestPath}.tmp`;
@@ -54845,7 +55084,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
54845
55084
  await import_promises8.default.rename(tmp, manifestPath);
54846
55085
  }
54847
55086
  async function readManifest(root, extId) {
54848
- const file = import_node_path45.default.join(root, extId, "manifest.json");
55087
+ const file = import_node_path46.default.join(root, extId, "manifest.json");
54849
55088
  let raw;
54850
55089
  try {
54851
55090
  raw = await import_promises8.default.readFile(file, "utf8");
@@ -54936,7 +55175,7 @@ function buildExtensionHandlers(deps) {
54936
55175
  };
54937
55176
  async function buildSnapshotMeta(extId) {
54938
55177
  const manifest = await readManifest(deps.root, extId);
54939
- const { sha256, buffer } = await bundleExtensionDir(import_node_path45.default.join(deps.root, extId));
55178
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path46.default.join(deps.root, extId));
54940
55179
  return { manifest, contentHash: sha256, buffer };
54941
55180
  }
54942
55181
  const publish = async (frame, _client, ctx) => {
@@ -55119,7 +55358,7 @@ function buildExtensionHandlers(deps) {
55119
55358
  // src/app-builder/project-store.ts
55120
55359
  var import_node_fs39 = require("fs");
55121
55360
  var import_node_child_process11 = require("child_process");
55122
- var import_node_path46 = require("path");
55361
+ var import_node_path47 = require("path");
55123
55362
  init_protocol();
55124
55363
  var PROJECTS_DIR = "projects";
55125
55364
  var META_FILE = ".clawd-project.json";
@@ -55133,14 +55372,14 @@ var ProjectStore = class {
55133
55372
  root;
55134
55373
  /** projects/<name>/.clawd-project.json 路径 */
55135
55374
  metaPath(name) {
55136
- return (0, import_node_path46.join)(this.projectsRoot(), name, META_FILE);
55375
+ return (0, import_node_path47.join)(this.projectsRoot(), name, META_FILE);
55137
55376
  }
55138
55377
  /** projects/<name>/ 目录路径(cwd 用) */
55139
55378
  projectDir(name) {
55140
- return (0, import_node_path46.join)(this.projectsRoot(), name);
55379
+ return (0, import_node_path47.join)(this.projectsRoot(), name);
55141
55380
  }
55142
55381
  projectsRoot() {
55143
- return (0, import_node_path46.join)(this.root, PROJECTS_DIR);
55382
+ return (0, import_node_path47.join)(this.root, PROJECTS_DIR);
55144
55383
  }
55145
55384
  async list() {
55146
55385
  let entries;
@@ -55443,7 +55682,7 @@ var PublishJobRegistry = class {
55443
55682
  // src/app-builder/publish-job-runner.ts
55444
55683
  var import_node_child_process13 = require("child_process");
55445
55684
  var import_node_fs40 = require("fs");
55446
- var import_node_path47 = require("path");
55685
+ var import_node_path48 = require("path");
55447
55686
 
55448
55687
  // src/app-builder/publish-stage-parser.ts
55449
55688
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -55475,7 +55714,7 @@ async function startPublishJob(deps, args) {
55475
55714
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
55476
55715
  }
55477
55716
  const projDir = projectDir(args.name);
55478
- const logPath = (0, import_node_path47.join)(projDir, ".publish.log");
55717
+ const logPath = (0, import_node_path48.join)(projDir, ".publish.log");
55479
55718
  let logStream = null;
55480
55719
  try {
55481
55720
  logStream = (0, import_node_fs40.createWriteStream)(logPath, { flags: "w" });
@@ -55735,7 +55974,7 @@ async function recoverInterruptedJobs(deps) {
55735
55974
 
55736
55975
  // src/handlers/app-builder.ts
55737
55976
  init_protocol();
55738
- var import_node_path48 = require("path");
55977
+ var import_node_path49 = require("path");
55739
55978
  var import_node_fs41 = require("fs");
55740
55979
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
55741
55980
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
@@ -55893,8 +56132,8 @@ function buildAppBuilderHandlers(deps) {
55893
56132
  const project = await userStore.create(f.name, reservedPorts);
55894
56133
  try {
55895
56134
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
55896
- const templateSrcDir = (0, import_node_path48.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
55897
- const scaffoldScript = (0, import_node_path48.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
56135
+ const templateSrcDir = (0, import_node_path49.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
56136
+ const scaffoldScript = (0, import_node_path49.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
55898
56137
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
55899
56138
  deps.logger?.info("app-builder.scaffold.done", {
55900
56139
  name: project.name,
@@ -56115,7 +56354,7 @@ function buildAppBuilderHandlers(deps) {
56115
56354
  await userStore.clearPublishJob(args.name);
56116
56355
  }
56117
56356
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
56118
- const scriptPath = (0, import_node_path48.join)(deps.deployKitRoot, "scripts", "publish.sh");
56357
+ const scriptPath = (0, import_node_path49.join)(deps.deployKitRoot, "scripts", "publish.sh");
56119
56358
  deps.logger?.info("app-builder.publish.start", {
56120
56359
  name: args.name,
56121
56360
  sessionId: boundSession.sessionId,
@@ -56284,7 +56523,7 @@ function buildVisitorHandlers(deps) {
56284
56523
 
56285
56524
  // src/extension/registry.ts
56286
56525
  var import_promises9 = __toESM(require("fs/promises"), 1);
56287
- var import_node_path49 = __toESM(require("path"), 1);
56526
+ var import_node_path50 = __toESM(require("path"), 1);
56288
56527
  async function loadAll(root) {
56289
56528
  let entries;
56290
56529
  try {
@@ -56297,13 +56536,13 @@ async function loadAll(root) {
56297
56536
  for (const ent of entries) {
56298
56537
  if (!ent.isDirectory()) continue;
56299
56538
  if (ent.name.startsWith(".")) continue;
56300
- records.push(await loadOne(import_node_path49.default.join(root, ent.name), ent.name));
56539
+ records.push(await loadOne(import_node_path50.default.join(root, ent.name), ent.name));
56301
56540
  }
56302
56541
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
56303
56542
  return records;
56304
56543
  }
56305
56544
  async function loadOne(dir, dirName) {
56306
- const manifestPath = import_node_path49.default.join(dir, "manifest.json");
56545
+ const manifestPath = import_node_path50.default.join(dir, "manifest.json");
56307
56546
  let raw;
56308
56547
  try {
56309
56548
  raw = await import_promises9.default.readFile(manifestPath, "utf8");
@@ -56348,7 +56587,7 @@ async function loadOne(dir, dirName) {
56348
56587
 
56349
56588
  // src/extension/uninstall.ts
56350
56589
  var import_promises10 = __toESM(require("fs/promises"), 1);
56351
- var import_node_path50 = __toESM(require("path"), 1);
56590
+ var import_node_path51 = __toESM(require("path"), 1);
56352
56591
  var UninstallError = class extends Error {
56353
56592
  constructor(code, message) {
56354
56593
  super(message);
@@ -56357,7 +56596,7 @@ var UninstallError = class extends Error {
56357
56596
  code;
56358
56597
  };
56359
56598
  async function uninstall(deps) {
56360
- const dir = import_node_path50.default.join(deps.root, deps.extId);
56599
+ const dir = import_node_path51.default.join(deps.root, deps.extId);
56361
56600
  try {
56362
56601
  await import_promises10.default.access(dir);
56363
56602
  } catch {
@@ -56926,7 +57165,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
56926
57165
 
56927
57166
  // src/extension/runtime.ts
56928
57167
  var import_node_child_process15 = require("child_process");
56929
- var import_node_path51 = __toESM(require("path"), 1);
57168
+ var import_node_path52 = __toESM(require("path"), 1);
56930
57169
  var import_promises11 = require("timers/promises");
56931
57170
 
56932
57171
  // src/extension/port-allocator.ts
@@ -57027,7 +57266,7 @@ var Runtime = class {
57027
57266
  /\$CLAWOS_EXT_PORT/g,
57028
57267
  String(port)
57029
57268
  );
57030
- const dir = import_node_path51.default.join(this.root, extId);
57269
+ const dir = import_node_path52.default.join(this.root, extId);
57031
57270
  const env = {
57032
57271
  ...process.env,
57033
57272
  CLAWOS_EXT_PORT: String(port),
@@ -57139,7 +57378,7 @@ ${handle.stderrTail}`
57139
57378
 
57140
57379
  // src/extension/published-channels.ts
57141
57380
  var import_promises12 = __toESM(require("fs/promises"), 1);
57142
- var import_node_path52 = __toESM(require("path"), 1);
57381
+ var import_node_path53 = __toESM(require("path"), 1);
57143
57382
  init_zod();
57144
57383
  var PublishedChannelsError = class extends Error {
57145
57384
  constructor(code, message) {
@@ -57238,7 +57477,7 @@ var PublishedChannelStore = class {
57238
57477
  )
57239
57478
  };
57240
57479
  const tmp = `${this.filePath}.tmp`;
57241
- await import_promises12.default.mkdir(import_node_path52.default.dirname(this.filePath), { recursive: true });
57480
+ await import_promises12.default.mkdir(import_node_path53.default.dirname(this.filePath), { recursive: true });
57242
57481
  await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
57243
57482
  await import_promises12.default.rename(tmp, this.filePath);
57244
57483
  }
@@ -57246,7 +57485,7 @@ var PublishedChannelStore = class {
57246
57485
 
57247
57486
  // src/extension/bundle-cache.ts
57248
57487
  var import_promises13 = __toESM(require("fs/promises"), 1);
57249
- var import_node_path53 = __toESM(require("path"), 1);
57488
+ var import_node_path54 = __toESM(require("path"), 1);
57250
57489
  var BundleCache = class {
57251
57490
  constructor(rootDir) {
57252
57491
  this.rootDir = rootDir;
@@ -57255,14 +57494,14 @@ var BundleCache = class {
57255
57494
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
57256
57495
  async write(snapshotHash, buffer) {
57257
57496
  await import_promises13.default.mkdir(this.rootDir, { recursive: true });
57258
- const file = import_node_path53.default.join(this.rootDir, `${snapshotHash}.zip`);
57497
+ const file = import_node_path54.default.join(this.rootDir, `${snapshotHash}.zip`);
57259
57498
  const tmp = `${file}.tmp`;
57260
57499
  await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
57261
57500
  await import_promises13.default.rename(tmp, file);
57262
57501
  }
57263
57502
  /** Returns the bundle bytes, or null when the file doesn't exist. */
57264
57503
  async read(snapshotHash) {
57265
- const file = import_node_path53.default.join(this.rootDir, `${snapshotHash}.zip`);
57504
+ const file = import_node_path54.default.join(this.rootDir, `${snapshotHash}.zip`);
57266
57505
  try {
57267
57506
  return await import_promises13.default.readFile(file);
57268
57507
  } catch (e) {
@@ -57272,7 +57511,7 @@ var BundleCache = class {
57272
57511
  }
57273
57512
  /** Idempotent — missing file is not an error. */
57274
57513
  async delete(snapshotHash) {
57275
- const file = import_node_path53.default.join(this.rootDir, `${snapshotHash}.zip`);
57514
+ const file = import_node_path54.default.join(this.rootDir, `${snapshotHash}.zip`);
57276
57515
  await import_promises13.default.rm(file, { force: true });
57277
57516
  }
57278
57517
  };
@@ -57293,11 +57532,11 @@ async function startDaemon(config) {
57293
57532
  },
57294
57533
  source: "daemon",
57295
57534
  sampling: logShippingCfg.sampling,
57296
- homeDir: import_node_os20.default.homedir()
57535
+ homeDir: import_node_os21.default.homedir()
57297
57536
  });
57298
57537
  const logger = createLogger({
57299
57538
  level: config.logLevel,
57300
- file: import_node_path54.default.join(config.dataDir, "clawd.log"),
57539
+ file: import_node_path55.default.join(config.dataDir, "clawd.log"),
57301
57540
  logClient
57302
57541
  });
57303
57542
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
@@ -57437,8 +57676,8 @@ async function startDaemon(config) {
57437
57676
  const agents = new AgentsScanner();
57438
57677
  const history = new ClaudeHistoryReader();
57439
57678
  let transport = null;
57440
- const personaStore = new PersonaStore(import_node_path54.default.join(config.dataDir, "personas"));
57441
- const usersRoot = import_node_path54.default.join(config.dataDir, "users");
57679
+ const personaStore = new PersonaStore(import_node_path55.default.join(config.dataDir, "personas"));
57680
+ const usersRoot = import_node_path55.default.join(config.dataDir, "users");
57442
57681
  const defaultsRoot = findDefaultsRoot(logger);
57443
57682
  if (defaultsRoot) {
57444
57683
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -57458,17 +57697,17 @@ async function startDaemon(config) {
57458
57697
  migrateCodexSandbox({ store: personaStore, logger });
57459
57698
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
57460
57699
  const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
57461
- const here = typeof __dirname === "string" ? __dirname : import_node_path54.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
57700
+ const here = typeof __dirname === "string" ? __dirname : import_node_path55.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
57462
57701
  const dispatchServerCandidates = [
57463
- import_node_path54.default.join(here, "dispatch", "mcp-server.cjs"),
57702
+ import_node_path55.default.join(here, "dispatch", "mcp-server.cjs"),
57464
57703
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
57465
- import_node_path54.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
57704
+ import_node_path55.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
57466
57705
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
57467
57706
  ];
57468
57707
  const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
57469
57708
  let dispatchMcpConfigPath2;
57470
57709
  if (dispatchServerScriptPath) {
57471
- const dispatchLogPath = import_node_path54.default.join(config.dataDir, "dispatch-mcp-server.log");
57710
+ const dispatchLogPath = import_node_path55.default.join(config.dataDir, "dispatch-mcp-server.log");
57472
57711
  dispatchMcpConfigPath2 = writeDispatchMcpConfig({
57473
57712
  dataDir: config.dataDir,
57474
57713
  serverScriptPath: dispatchServerScriptPath,
@@ -57485,15 +57724,15 @@ async function startDaemon(config) {
57485
57724
  });
57486
57725
  }
57487
57726
  const ticketServerCandidates = [
57488
- import_node_path54.default.join(here, "ticket", "mcp-server.cjs"),
57489
- import_node_path54.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
57727
+ import_node_path55.default.join(here, "ticket", "mcp-server.cjs"),
57728
+ import_node_path55.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
57490
57729
  ];
57491
57730
  const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
57492
57731
  const ticketOwnerUnionId = feishuIdentity?.identity.unionId ?? "";
57493
57732
  const ticketOwnerName = feishuIdentity?.identity.displayName ?? "";
57494
57733
  let ticketMcpConfigPath2;
57495
57734
  if (ticketServerScriptPath && ticketOwnerUnionId) {
57496
- const ticketLogPath = import_node_path54.default.join(config.dataDir, "ticket-mcp-server.log");
57735
+ const ticketLogPath = import_node_path55.default.join(config.dataDir, "ticket-mcp-server.log");
57497
57736
  ticketMcpConfigPath2 = writeTicketMcpConfig({
57498
57737
  dataDir: config.dataDir,
57499
57738
  serverScriptPath: ticketServerScriptPath,
@@ -57514,13 +57753,13 @@ async function startDaemon(config) {
57514
57753
  });
57515
57754
  }
57516
57755
  const shiftServerCandidates = [
57517
- import_node_path54.default.join(here, "shift", "mcp-server.cjs"),
57518
- import_node_path54.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
57756
+ import_node_path55.default.join(here, "shift", "mcp-server.cjs"),
57757
+ import_node_path55.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
57519
57758
  ];
57520
57759
  const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
57521
57760
  let shiftMcpConfigPath2;
57522
57761
  if (shiftServerScriptPath) {
57523
- const shiftLogPath = import_node_path54.default.join(config.dataDir, "shift-mcp-server.log");
57762
+ const shiftLogPath = import_node_path55.default.join(config.dataDir, "shift-mcp-server.log");
57524
57763
  shiftMcpConfigPath2 = await writeShiftMcpConfig({
57525
57764
  dataDir: config.dataDir,
57526
57765
  serverScriptPath: shiftServerScriptPath,
@@ -57538,13 +57777,13 @@ async function startDaemon(config) {
57538
57777
  );
57539
57778
  }
57540
57779
  const inboxServerCandidates = [
57541
- import_node_path54.default.join(here, "inbox", "mcp-server.cjs"),
57542
- import_node_path54.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
57780
+ import_node_path55.default.join(here, "inbox", "mcp-server.cjs"),
57781
+ import_node_path55.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
57543
57782
  ];
57544
57783
  const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs42.default.existsSync(p2));
57545
57784
  let inboxMcpConfigPath2;
57546
57785
  if (inboxServerScriptPath) {
57547
- const inboxLogPath = import_node_path54.default.join(config.dataDir, "inbox-mcp-server.log");
57786
+ const inboxLogPath = import_node_path55.default.join(config.dataDir, "inbox-mcp-server.log");
57548
57787
  inboxMcpConfigPath2 = await writeInboxMcpConfig({
57549
57788
  dataDir: config.dataDir,
57550
57789
  serverScriptPath: inboxServerScriptPath,
@@ -57562,7 +57801,7 @@ async function startDaemon(config) {
57562
57801
  );
57563
57802
  }
57564
57803
  const shiftStore = createShiftStore({
57565
- filePath: import_node_path54.default.join(config.dataDir, "shift.json"),
57804
+ filePath: import_node_path55.default.join(config.dataDir, "shift.json"),
57566
57805
  ownerIdProvider: () => ownerPrincipalId,
57567
57806
  now: () => Date.now()
57568
57807
  });
@@ -57580,7 +57819,7 @@ async function startDaemon(config) {
57580
57819
  getAdapter,
57581
57820
  historyReader: history,
57582
57821
  dataDir: config.dataDir,
57583
- personaRoot: import_node_path54.default.join(config.dataDir, "personas"),
57822
+ personaRoot: import_node_path55.default.join(config.dataDir, "personas"),
57584
57823
  usersRoot,
57585
57824
  personaStore,
57586
57825
  ownerDisplayName,
@@ -57592,9 +57831,9 @@ async function startDaemon(config) {
57592
57831
  // 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
57593
57832
  // 的 isLoopback 兜底已确保安全。
57594
57833
  getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
57595
- // Persona dispatch (Task 8): manager 通过这两个 deps 跟 PersonaDispatchManager 协作。
57596
- // - personaDispatchManager: manager.createDispatchedSession 用它 registerBSession;
57597
- // reducer 通过闭包反查 dispatchId CLAWD_DISPATCH_ID env
57834
+ // Persona dispatch: manager 通过这两个 deps 跟 PersonaDispatchManager 协作。
57835
+ // - personaDispatchManager: createDispatchedSession / resumeDispatchedSession 用它
57836
+ // registerBSession;complete handler findInflightDispatchByBSessionId 反查回 dispatchId
57598
57837
  // - dispatchMcpConfigPath: reducer 透传到 SpawnContext,cc spawn 加 --mcp-config flag。
57599
57838
  personaDispatchManager,
57600
57839
  dispatchMcpConfigPath: dispatchMcpConfigPath2,
@@ -57623,7 +57862,7 @@ async function startDaemon(config) {
57623
57862
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
57624
57863
  attachmentGroup: {
57625
57864
  onFileEdit: (input) => {
57626
- const absPath = import_node_path54.default.isAbsolute(input.relPath) ? input.relPath : import_node_path54.default.join(input.cwd, input.relPath);
57865
+ const absPath = import_node_path55.default.isAbsolute(input.relPath) ? input.relPath : import_node_path55.default.join(input.cwd, input.relPath);
57627
57866
  let size = 0;
57628
57867
  try {
57629
57868
  size = import_node_fs42.default.statSync(absPath).size;
@@ -57824,11 +58063,11 @@ async function startDaemon(config) {
57824
58063
  // 'persona/<pid>/owner',default 走 'default'。
57825
58064
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
57826
58065
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
57827
- personaRoot: import_node_path54.default.join(config.dataDir, "personas"),
58066
+ personaRoot: import_node_path55.default.join(config.dataDir, "personas"),
57828
58067
  usersRoot
57829
58068
  },
57830
58069
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
57831
- personaRoot: import_node_path54.default.join(config.dataDir, "personas"),
58070
+ personaRoot: import_node_path55.default.join(config.dataDir, "personas"),
57832
58071
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
57833
58072
  usersRoot,
57834
58073
  // capability:list / delete handler 依赖
@@ -57937,11 +58176,11 @@ async function startDaemon(config) {
57937
58176
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
57938
58177
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
57939
58178
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
57940
- appBuilderPersonaRoot: import_node_path54.default.join(config.dataDir, "personas", "persona-app-builder"),
58179
+ appBuilderPersonaRoot: import_node_path55.default.join(config.dataDir, "personas", "persona-app-builder"),
57941
58180
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
57942
- deployKitRoot: import_node_path54.default.join(config.dataDir, "deploy-kit"),
58181
+ deployKitRoot: import_node_path55.default.join(config.dataDir, "deploy-kit"),
57943
58182
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
57944
- resolvePersonaRoot: (personaId) => import_node_path54.default.join(config.dataDir, "personas", personaId),
58183
+ resolvePersonaRoot: (personaId) => import_node_path55.default.join(config.dataDir, "personas", personaId),
57945
58184
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
57946
58185
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
57947
58186
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -57976,7 +58215,8 @@ async function startDaemon(config) {
57976
58215
  logger.info("dispatch.spawnB.start", {
57977
58216
  dispatchId: args.dispatchId,
57978
58217
  targetPersona: args.targetPersona,
57979
- sourceSessionId: args.sourceSessionId
58218
+ sourceSessionId: args.sourceSessionId,
58219
+ route: args.route
57980
58220
  });
57981
58221
  const sourceFile = manager.findOwnedSession(args.sourceSessionId);
57982
58222
  if (!sourceFile && !args.guestPrincipalId) {
@@ -57984,8 +58224,8 @@ async function startDaemon(config) {
57984
58224
  }
57985
58225
  let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
57986
58226
  if (sourceFile && sourceFile.toolSessionId) {
57987
- sourceJsonlPath = import_node_path54.default.join(
57988
- import_node_os20.default.homedir(),
58227
+ sourceJsonlPath = import_node_path55.default.join(
58228
+ import_node_os21.default.homedir(),
57989
58229
  ".claude",
57990
58230
  "projects",
57991
58231
  cwdToHashDir(sourceFile.cwd),
@@ -57995,9 +58235,23 @@ async function startDaemon(config) {
57995
58235
  logger.info("dispatch.spawnB.source-resolved", {
57996
58236
  dispatchId: args.dispatchId,
57997
58237
  sourceJsonlPath,
57998
- hasToolSessionId: Boolean(sourceFile?.toolSessionId)
58238
+ hasToolSessionId: Boolean(sourceFile?.toolSessionId),
58239
+ route: args.route
57999
58240
  });
58000
- manager.createDispatchedSession({
58241
+ if (args.route === "resume") {
58242
+ if (!args.bSessionId) {
58243
+ throw new Error("resume route requires bSessionId");
58244
+ }
58245
+ const { sessionId: sessionId2 } = manager.resumeDispatchedSession({
58246
+ dispatchId: args.dispatchId,
58247
+ bSessionId: args.bSessionId,
58248
+ prompt: args.prompt,
58249
+ sourceJsonlPath,
58250
+ callerIdentity: args.guestPrincipalId ? { kind: "guest", sourcePrincipalId: args.guestPrincipalId } : { kind: "local", sourceSessionId: args.sourceSessionId }
58251
+ });
58252
+ return { bSessionId: sessionId2 };
58253
+ }
58254
+ const { sessionId } = manager.createDispatchedSession({
58001
58255
  dispatchId: args.dispatchId,
58002
58256
  sourceSessionId: args.sourceSessionId,
58003
58257
  targetPersona: args.targetPersona,
@@ -58007,24 +58261,30 @@ async function startDaemon(config) {
58007
58261
  guestPrincipalId: args.guestPrincipalId,
58008
58262
  guestDisplayName: args.guestDisplayName
58009
58263
  });
58264
+ return { bSessionId: sessionId };
58010
58265
  },
58011
58266
  // A 角色:从 ContactStore 取 peer 可达 URL + connect token,转发到对端 daemon /rpc。
58012
- forwardToPeer: async ({ targetDeviceId, targetPersona, prompt }) => {
58267
+ forwardToPeer: async ({ targetDeviceId, targetPersona, prompt, targetSessionId }) => {
58013
58268
  const contact = contactStore.get(targetDeviceId);
58014
58269
  if (!contact || !contact.remoteUrl || !contact.connectToken) {
58015
58270
  return {
58016
- kind: "failure",
58017
- reason: `unknown or unreachable contact: ${targetDeviceId}`
58271
+ outcome: {
58272
+ kind: "failure",
58273
+ reason: `unknown or unreachable contact: ${targetDeviceId}`
58274
+ }
58018
58275
  };
58019
58276
  }
58020
58277
  return forwardDispatchToPeer({
58021
58278
  contact: { remoteUrl: contact.remoteUrl, connectToken: contact.connectToken },
58022
58279
  targetPersona,
58023
- prompt
58280
+ prompt,
58281
+ targetSessionId
58024
58282
  });
58025
58283
  },
58026
58284
  // B 角色:判断 targetPersona 是否 public(跨设备授权边界,private 拒)。
58027
- getPersonaPublic: (personaId) => personaRegistry.get(personaId)?.public ?? false
58285
+ getPersonaPublic: (personaId) => personaRegistry.get(personaId)?.public ?? false,
58286
+ // targetSessionId 复用路径的权限校验数据源:读 B session 的 SessionFile。
58287
+ findOwnedSession: (sessionId) => manager.findOwnedSession(sessionId)
58028
58288
  });
58029
58289
  handlers = { ...handlers, ...dispatchHandlers };
58030
58290
  const shiftHandlers = buildShiftInternalHandlers({
@@ -58284,8 +58544,8 @@ async function startDaemon(config) {
58284
58544
  const lines = [
58285
58545
  `Tunnel: ${r.url}`,
58286
58546
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
58287
- `Frpc config: ${import_node_path54.default.join(config.dataDir, "frpc.toml")}`,
58288
- `Frpc log: ${import_node_path54.default.join(config.dataDir, "frpc.log")}`
58547
+ `Frpc config: ${import_node_path55.default.join(config.dataDir, "frpc.toml")}`,
58548
+ `Frpc log: ${import_node_path55.default.join(config.dataDir, "frpc.log")}`
58289
58549
  ];
58290
58550
  const width = Math.max(...lines.map((l) => l.length));
58291
58551
  const bar = "\u2550".repeat(width + 4);
@@ -58298,7 +58558,7 @@ ${bar}
58298
58558
 
58299
58559
  `);
58300
58560
  try {
58301
- const connectPath = import_node_path54.default.join(config.dataDir, "connect.txt");
58561
+ const connectPath = import_node_path55.default.join(config.dataDir, "connect.txt");
58302
58562
  import_node_fs42.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
58303
58563
  } catch {
58304
58564
  }
@@ -58371,7 +58631,7 @@ ${bar}
58371
58631
  };
58372
58632
  }
58373
58633
  function migrateDropPersonsDir(dataDir) {
58374
- const dir = import_node_path54.default.join(dataDir, "persons");
58634
+ const dir = import_node_path55.default.join(dataDir, "persons");
58375
58635
  try {
58376
58636
  import_node_fs42.default.rmSync(dir, { recursive: true, force: true });
58377
58637
  } catch {