@clawos-dev/clawd 0.2.246 → 0.2.248

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
@@ -67,6 +67,8 @@ var init_methods = __esm({
67
67
  "session:subscribe",
68
68
  "session:unsubscribe",
69
69
  "session:pin",
70
+ // 清共享未读标记(SessionFile.unreadAt → null)+ 广播 session:info;幂等
71
+ "session:markRead",
70
72
  "session:reorderPins",
71
73
  // mirror peer sessions (2026-05-26): owner-only RPC,写/删本机 mirror SessionFile —
72
74
  // mirror 是「**我**在朋友 daemon 上自己创建的 session」的本机持久化副本(不是朋友的会话),
@@ -749,8 +751,8 @@ var init_parseUtil = __esm({
749
751
  init_errors2();
750
752
  init_en();
751
753
  makeIssue = (params) => {
752
- const { data, path: path70, errorMaps, issueData } = params;
753
- const fullPath = [...path70, ...issueData.path || []];
754
+ const { data, path: path71, errorMaps, issueData } = params;
755
+ const fullPath = [...path71, ...issueData.path || []];
754
756
  const fullIssue = {
755
757
  ...issueData,
756
758
  path: fullPath
@@ -1061,11 +1063,11 @@ var init_types = __esm({
1061
1063
  init_parseUtil();
1062
1064
  init_util();
1063
1065
  ParseInputLazyPath = class {
1064
- constructor(parent, value, path70, key) {
1066
+ constructor(parent, value, path71, key) {
1065
1067
  this._cachedPath = [];
1066
1068
  this.parent = parent;
1067
1069
  this.data = value;
1068
- this._path = path70;
1070
+ this._path = path71;
1069
1071
  this._key = key;
1070
1072
  }
1071
1073
  get path() {
@@ -4781,6 +4783,11 @@ var init_schemas = __esm({
4781
4783
  // pinned 行手动拖拽顺序;越小越靠前。session:reorderPins 整体重写所有 pinned 的此字段。
4782
4784
  // 缺失时 view-state 排序回退到 pinnedAt desc(旧数据兼容)
4783
4785
  pinSortOrder: external_exports.number().int().nullable().optional(),
4786
+ // daemon 权威的共享未读标记(spec 2026-07-20-shared-session-unread):
4787
+ // session wire status 从 running 跃迁为非 running 时 daemon 置为当时 ISO 时间戳;
4788
+ // session:markRead RPC 或新 turn 开始(→ running)时清为 null。
4789
+ // 所有客户端(desktop / mobile)共享同一份,任一端标读全端清。
4790
+ unreadAt: external_exports.string().min(1).nullable().optional(),
4784
4791
  // 用户在 NewSessionDialog 或 Edit modal 选择的 icon 标识;UI 端做 enum 兜底
4785
4792
  iconKey: external_exports.string().optional(),
4786
4793
  // 由 session:fork 派生的 session 在 create 时记录源 session 的 sessionId(稳定 key,
@@ -6180,7 +6187,7 @@ var init_feishu_auth = __esm({
6180
6187
  });
6181
6188
 
6182
6189
  // ../protocol/src/dispatch.ts
6183
- var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema;
6190
+ var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema, DISPATCH_STATUSES, DispatchStatusSchema, DispatchRecordSchema, DispatchListArgsSchema, DispatchListResultSchema;
6184
6191
  var init_dispatch = __esm({
6185
6192
  "../protocol/src/dispatch.ts"() {
6186
6193
  "use strict";
@@ -6212,6 +6219,32 @@ var init_dispatch = __esm({
6212
6219
  dispatchId: external_exports.string().min(1),
6213
6220
  outcome: DispatchOutcomeSchema
6214
6221
  });
6222
+ DISPATCH_STATUSES = ["running", "completed", "failed"];
6223
+ DispatchStatusSchema = external_exports.enum(DISPATCH_STATUSES);
6224
+ DispatchRecordSchema = external_exports.object({
6225
+ dispatchId: external_exports.string().min(1),
6226
+ sourceSessionId: external_exports.string().min(1),
6227
+ sourcePersonaId: external_exports.string().min(1).optional(),
6228
+ targetPersonaId: external_exports.string().min(1),
6229
+ /** B 的 sessionId;registerBSession 后回填 */
6230
+ workerSessionId: external_exports.string().min(1).optional(),
6231
+ /** 任务摘要;截断(≤500 字符)在 daemon store.add 处保证 */
6232
+ taskText: external_exports.string(),
6233
+ status: DispatchStatusSchema,
6234
+ outcome: DispatchOutcomeSchema.optional(),
6235
+ /** 上层消费者语义(如司礼监的口谕/验收状态),daemon 不解释 */
6236
+ meta: external_exports.record(external_exports.unknown()).optional(),
6237
+ createdAt: external_exports.string().min(1),
6238
+ completedAt: external_exports.string().min(1).optional(),
6239
+ /** 结果注入 source session 成功时戳,兼当投递 dedup 标记 */
6240
+ deliveredAt: external_exports.string().min(1).optional()
6241
+ });
6242
+ DispatchListArgsSchema = external_exports.object({
6243
+ sourceSessionId: external_exports.string().min(1).optional()
6244
+ });
6245
+ DispatchListResultSchema = external_exports.object({
6246
+ records: external_exports.array(DispatchRecordSchema)
6247
+ });
6215
6248
  }
6216
6249
  });
6217
6250
 
@@ -6564,8 +6597,8 @@ var require_req = __commonJS({
6564
6597
  if (req.originalUrl) {
6565
6598
  _req.url = req.originalUrl;
6566
6599
  } else {
6567
- const path70 = req.path;
6568
- _req.url = typeof path70 === "string" ? path70 : req.url ? req.url.path || req.url : void 0;
6600
+ const path71 = req.path;
6601
+ _req.url = typeof path71 === "string" ? path71 : req.url ? req.url.path || req.url : void 0;
6569
6602
  }
6570
6603
  if (req.query) {
6571
6604
  _req.query = req.query;
@@ -6730,14 +6763,14 @@ var require_redact = __commonJS({
6730
6763
  }
6731
6764
  return obj;
6732
6765
  }
6733
- function parsePath(path70) {
6766
+ function parsePath(path71) {
6734
6767
  const parts = [];
6735
6768
  let current = "";
6736
6769
  let inBrackets = false;
6737
6770
  let inQuotes = false;
6738
6771
  let quoteChar = "";
6739
- for (let i = 0; i < path70.length; i++) {
6740
- const char = path70[i];
6772
+ for (let i = 0; i < path71.length; i++) {
6773
+ const char = path71[i];
6741
6774
  if (!inBrackets && char === ".") {
6742
6775
  if (current) {
6743
6776
  parts.push(current);
@@ -6868,10 +6901,10 @@ var require_redact = __commonJS({
6868
6901
  return current;
6869
6902
  }
6870
6903
  function redactPaths(obj, paths, censor, remove = false) {
6871
- for (const path70 of paths) {
6872
- const parts = parsePath(path70);
6904
+ for (const path71 of paths) {
6905
+ const parts = parsePath(path71);
6873
6906
  if (parts.includes("*")) {
6874
- redactWildcardPath(obj, parts, censor, path70, remove);
6907
+ redactWildcardPath(obj, parts, censor, path71, remove);
6875
6908
  } else {
6876
6909
  if (remove) {
6877
6910
  removeKey(obj, parts);
@@ -6956,8 +6989,8 @@ var require_redact = __commonJS({
6956
6989
  }
6957
6990
  } else {
6958
6991
  if (afterWildcard.includes("*")) {
6959
- const wrappedCensor = typeof censor === "function" ? (value, path70) => {
6960
- const fullPath = [...pathArray.slice(0, pathLength), ...path70];
6992
+ const wrappedCensor = typeof censor === "function" ? (value, path71) => {
6993
+ const fullPath = [...pathArray.slice(0, pathLength), ...path71];
6961
6994
  return censor(value, fullPath);
6962
6995
  } : censor;
6963
6996
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6992,8 +7025,8 @@ var require_redact = __commonJS({
6992
7025
  return null;
6993
7026
  }
6994
7027
  const pathStructure = /* @__PURE__ */ new Map();
6995
- for (const path70 of pathsToClone) {
6996
- const parts = parsePath(path70);
7028
+ for (const path71 of pathsToClone) {
7029
+ const parts = parsePath(path71);
6997
7030
  let current = pathStructure;
6998
7031
  for (let i = 0; i < parts.length; i++) {
6999
7032
  const part = parts[i];
@@ -7045,24 +7078,24 @@ var require_redact = __commonJS({
7045
7078
  }
7046
7079
  return cloneSelectively(obj, pathStructure);
7047
7080
  }
7048
- function validatePath(path70) {
7049
- if (typeof path70 !== "string") {
7081
+ function validatePath(path71) {
7082
+ if (typeof path71 !== "string") {
7050
7083
  throw new Error("Paths must be (non-empty) strings");
7051
7084
  }
7052
- if (path70 === "") {
7085
+ if (path71 === "") {
7053
7086
  throw new Error("Invalid redaction path ()");
7054
7087
  }
7055
- if (path70.includes("..")) {
7056
- throw new Error(`Invalid redaction path (${path70})`);
7088
+ if (path71.includes("..")) {
7089
+ throw new Error(`Invalid redaction path (${path71})`);
7057
7090
  }
7058
- if (path70.includes(",")) {
7059
- throw new Error(`Invalid redaction path (${path70})`);
7091
+ if (path71.includes(",")) {
7092
+ throw new Error(`Invalid redaction path (${path71})`);
7060
7093
  }
7061
7094
  let bracketCount = 0;
7062
7095
  let inQuotes = false;
7063
7096
  let quoteChar = "";
7064
- for (let i = 0; i < path70.length; i++) {
7065
- const char = path70[i];
7097
+ for (let i = 0; i < path71.length; i++) {
7098
+ const char = path71[i];
7066
7099
  if ((char === '"' || char === "'") && bracketCount > 0) {
7067
7100
  if (!inQuotes) {
7068
7101
  inQuotes = true;
@@ -7076,20 +7109,20 @@ var require_redact = __commonJS({
7076
7109
  } else if (char === "]" && !inQuotes) {
7077
7110
  bracketCount--;
7078
7111
  if (bracketCount < 0) {
7079
- throw new Error(`Invalid redaction path (${path70})`);
7112
+ throw new Error(`Invalid redaction path (${path71})`);
7080
7113
  }
7081
7114
  }
7082
7115
  }
7083
7116
  if (bracketCount !== 0) {
7084
- throw new Error(`Invalid redaction path (${path70})`);
7117
+ throw new Error(`Invalid redaction path (${path71})`);
7085
7118
  }
7086
7119
  }
7087
7120
  function validatePaths(paths) {
7088
7121
  if (!Array.isArray(paths)) {
7089
7122
  throw new TypeError("paths must be an array");
7090
7123
  }
7091
- for (const path70 of paths) {
7092
- validatePath(path70);
7124
+ for (const path71 of paths) {
7125
+ validatePath(path71);
7093
7126
  }
7094
7127
  }
7095
7128
  function slowRedact(options = {}) {
@@ -7257,8 +7290,8 @@ var require_redaction = __commonJS({
7257
7290
  if (shape[k2] === null) {
7258
7291
  o[k2] = (value) => topCensor(value, [k2]);
7259
7292
  } else {
7260
- const wrappedCensor = typeof censor === "function" ? (value, path70) => {
7261
- return censor(value, [k2, ...path70]);
7293
+ const wrappedCensor = typeof censor === "function" ? (value, path71) => {
7294
+ return censor(value, [k2, ...path71]);
7262
7295
  } : censor;
7263
7296
  o[k2] = Redact({
7264
7297
  paths: shape[k2],
@@ -7476,10 +7509,10 @@ var require_atomic_sleep = __commonJS({
7476
7509
  var require_sonic_boom = __commonJS({
7477
7510
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
7478
7511
  "use strict";
7479
- var fs64 = require("fs");
7512
+ var fs65 = require("fs");
7480
7513
  var EventEmitter3 = require("events");
7481
7514
  var inherits = require("util").inherits;
7482
- var path70 = require("path");
7515
+ var path71 = require("path");
7483
7516
  var sleep2 = require_atomic_sleep();
7484
7517
  var assert = require("assert");
7485
7518
  var BUSY_WRITE_TIMEOUT = 100;
@@ -7533,20 +7566,20 @@ var require_sonic_boom = __commonJS({
7533
7566
  const mode = sonic.mode;
7534
7567
  if (sonic.sync) {
7535
7568
  try {
7536
- if (sonic.mkdir) fs64.mkdirSync(path70.dirname(file), { recursive: true });
7537
- const fd = fs64.openSync(file, flags, mode);
7569
+ if (sonic.mkdir) fs65.mkdirSync(path71.dirname(file), { recursive: true });
7570
+ const fd = fs65.openSync(file, flags, mode);
7538
7571
  fileOpened(null, fd);
7539
7572
  } catch (err) {
7540
7573
  fileOpened(err);
7541
7574
  throw err;
7542
7575
  }
7543
7576
  } else if (sonic.mkdir) {
7544
- fs64.mkdir(path70.dirname(file), { recursive: true }, (err) => {
7577
+ fs65.mkdir(path71.dirname(file), { recursive: true }, (err) => {
7545
7578
  if (err) return fileOpened(err);
7546
- fs64.open(file, flags, mode, fileOpened);
7579
+ fs65.open(file, flags, mode, fileOpened);
7547
7580
  });
7548
7581
  } else {
7549
- fs64.open(file, flags, mode, fileOpened);
7582
+ fs65.open(file, flags, mode, fileOpened);
7550
7583
  }
7551
7584
  }
7552
7585
  function SonicBoom(opts) {
@@ -7587,8 +7620,8 @@ var require_sonic_boom = __commonJS({
7587
7620
  this.flush = flushBuffer;
7588
7621
  this.flushSync = flushBufferSync;
7589
7622
  this._actualWrite = actualWriteBuffer;
7590
- fsWriteSync = () => fs64.writeSync(this.fd, this._writingBuf);
7591
- fsWrite = () => fs64.write(this.fd, this._writingBuf, this.release);
7623
+ fsWriteSync = () => fs65.writeSync(this.fd, this._writingBuf);
7624
+ fsWrite = () => fs65.write(this.fd, this._writingBuf, this.release);
7592
7625
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
7593
7626
  this._writingBuf = "";
7594
7627
  this.write = write;
@@ -7597,15 +7630,15 @@ var require_sonic_boom = __commonJS({
7597
7630
  this._actualWrite = actualWrite;
7598
7631
  fsWriteSync = () => {
7599
7632
  if (Buffer.isBuffer(this._writingBuf)) {
7600
- return fs64.writeSync(this.fd, this._writingBuf);
7633
+ return fs65.writeSync(this.fd, this._writingBuf);
7601
7634
  }
7602
- return fs64.writeSync(this.fd, this._writingBuf, "utf8");
7635
+ return fs65.writeSync(this.fd, this._writingBuf, "utf8");
7603
7636
  };
7604
7637
  fsWrite = () => {
7605
7638
  if (Buffer.isBuffer(this._writingBuf)) {
7606
- return fs64.write(this.fd, this._writingBuf, this.release);
7639
+ return fs65.write(this.fd, this._writingBuf, this.release);
7607
7640
  }
7608
- return fs64.write(this.fd, this._writingBuf, "utf8", this.release);
7641
+ return fs65.write(this.fd, this._writingBuf, "utf8", this.release);
7609
7642
  };
7610
7643
  } else {
7611
7644
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -7662,7 +7695,7 @@ var require_sonic_boom = __commonJS({
7662
7695
  }
7663
7696
  }
7664
7697
  if (this._fsync) {
7665
- fs64.fsyncSync(this.fd);
7698
+ fs65.fsyncSync(this.fd);
7666
7699
  }
7667
7700
  const len = this._len;
7668
7701
  if (this._reopening) {
@@ -7776,7 +7809,7 @@ var require_sonic_boom = __commonJS({
7776
7809
  const onDrain = () => {
7777
7810
  if (!this._fsync) {
7778
7811
  try {
7779
- fs64.fsync(this.fd, (err) => {
7812
+ fs65.fsync(this.fd, (err) => {
7780
7813
  this._flushPending = false;
7781
7814
  cb(err);
7782
7815
  });
@@ -7878,7 +7911,7 @@ var require_sonic_boom = __commonJS({
7878
7911
  const fd = this.fd;
7879
7912
  this.once("ready", () => {
7880
7913
  if (fd !== this.fd) {
7881
- fs64.close(fd, (err) => {
7914
+ fs65.close(fd, (err) => {
7882
7915
  if (err) {
7883
7916
  return this.emit("error", err);
7884
7917
  }
@@ -7927,7 +7960,7 @@ var require_sonic_boom = __commonJS({
7927
7960
  buf = this._bufs[0];
7928
7961
  }
7929
7962
  try {
7930
- const n = Buffer.isBuffer(buf) ? fs64.writeSync(this.fd, buf) : fs64.writeSync(this.fd, buf, "utf8");
7963
+ const n = Buffer.isBuffer(buf) ? fs65.writeSync(this.fd, buf) : fs65.writeSync(this.fd, buf, "utf8");
7931
7964
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
7932
7965
  buf = releasedBufObj.writingBuf;
7933
7966
  this._len = releasedBufObj.len;
@@ -7943,7 +7976,7 @@ var require_sonic_boom = __commonJS({
7943
7976
  }
7944
7977
  }
7945
7978
  try {
7946
- fs64.fsyncSync(this.fd);
7979
+ fs65.fsyncSync(this.fd);
7947
7980
  } catch {
7948
7981
  }
7949
7982
  }
@@ -7964,7 +7997,7 @@ var require_sonic_boom = __commonJS({
7964
7997
  buf = mergeBuf(this._bufs[0], this._lens[0]);
7965
7998
  }
7966
7999
  try {
7967
- const n = fs64.writeSync(this.fd, buf);
8000
+ const n = fs65.writeSync(this.fd, buf);
7968
8001
  buf = buf.subarray(n);
7969
8002
  this._len = Math.max(this._len - n, 0);
7970
8003
  if (buf.length <= 0) {
@@ -7992,13 +8025,13 @@ var require_sonic_boom = __commonJS({
7992
8025
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
7993
8026
  if (this.sync) {
7994
8027
  try {
7995
- const written = Buffer.isBuffer(this._writingBuf) ? fs64.writeSync(this.fd, this._writingBuf) : fs64.writeSync(this.fd, this._writingBuf, "utf8");
8028
+ const written = Buffer.isBuffer(this._writingBuf) ? fs65.writeSync(this.fd, this._writingBuf) : fs65.writeSync(this.fd, this._writingBuf, "utf8");
7996
8029
  release(null, written);
7997
8030
  } catch (err) {
7998
8031
  release(err);
7999
8032
  }
8000
8033
  } else {
8001
- fs64.write(this.fd, this._writingBuf, release);
8034
+ fs65.write(this.fd, this._writingBuf, release);
8002
8035
  }
8003
8036
  }
8004
8037
  function actualWriteBuffer() {
@@ -8007,7 +8040,7 @@ var require_sonic_boom = __commonJS({
8007
8040
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
8008
8041
  if (this.sync) {
8009
8042
  try {
8010
- const written = fs64.writeSync(this.fd, this._writingBuf);
8043
+ const written = fs65.writeSync(this.fd, this._writingBuf);
8011
8044
  release(null, written);
8012
8045
  } catch (err) {
8013
8046
  release(err);
@@ -8016,7 +8049,7 @@ var require_sonic_boom = __commonJS({
8016
8049
  if (kCopyBuffer) {
8017
8050
  this._writingBuf = Buffer.from(this._writingBuf);
8018
8051
  }
8019
- fs64.write(this.fd, this._writingBuf, release);
8052
+ fs65.write(this.fd, this._writingBuf, release);
8020
8053
  }
8021
8054
  }
8022
8055
  function actualClose(sonic) {
@@ -8032,12 +8065,12 @@ var require_sonic_boom = __commonJS({
8032
8065
  sonic._lens = [];
8033
8066
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
8034
8067
  try {
8035
- fs64.fsync(sonic.fd, closeWrapped);
8068
+ fs65.fsync(sonic.fd, closeWrapped);
8036
8069
  } catch {
8037
8070
  }
8038
8071
  function closeWrapped() {
8039
8072
  if (sonic.fd !== 1 && sonic.fd !== 2) {
8040
- fs64.close(sonic.fd, done);
8073
+ fs65.close(sonic.fd, done);
8041
8074
  } else {
8042
8075
  done();
8043
8076
  }
@@ -11172,11 +11205,11 @@ var init_lib = __esm({
11172
11205
  }
11173
11206
  }
11174
11207
  },
11175
- addToPath: function addToPath(path70, added, removed, oldPosInc, options) {
11176
- var last = path70.lastComponent;
11208
+ addToPath: function addToPath(path71, added, removed, oldPosInc, options) {
11209
+ var last = path71.lastComponent;
11177
11210
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
11178
11211
  return {
11179
- oldPos: path70.oldPos + oldPosInc,
11212
+ oldPos: path71.oldPos + oldPosInc,
11180
11213
  lastComponent: {
11181
11214
  count: last.count + 1,
11182
11215
  added,
@@ -11186,7 +11219,7 @@ var init_lib = __esm({
11186
11219
  };
11187
11220
  } else {
11188
11221
  return {
11189
- oldPos: path70.oldPos + oldPosInc,
11222
+ oldPos: path71.oldPos + oldPosInc,
11190
11223
  lastComponent: {
11191
11224
  count: 1,
11192
11225
  added,
@@ -11695,10 +11728,10 @@ function attachmentToHistoryMessage(o, ts) {
11695
11728
  const memories = raw.map((m2) => {
11696
11729
  if (!m2 || typeof m2 !== "object") return null;
11697
11730
  const rec3 = m2;
11698
- const path70 = typeof rec3.path === "string" ? rec3.path : null;
11731
+ const path71 = typeof rec3.path === "string" ? rec3.path : null;
11699
11732
  const content = typeof rec3.content === "string" ? rec3.content : null;
11700
- if (!path70 || content == null) return null;
11701
- const entry = { path: path70, content };
11733
+ if (!path71 || content == null) return null;
11734
+ const entry = { path: path71, content };
11702
11735
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
11703
11736
  return entry;
11704
11737
  }).filter((m2) => m2 !== null);
@@ -12501,10 +12534,10 @@ function parseAttachment(obj) {
12501
12534
  const memories = raw.map((m2) => {
12502
12535
  if (!m2 || typeof m2 !== "object") return null;
12503
12536
  const rec3 = m2;
12504
- const path70 = typeof rec3.path === "string" ? rec3.path : null;
12537
+ const path71 = typeof rec3.path === "string" ? rec3.path : null;
12505
12538
  const content = typeof rec3.content === "string" ? rec3.content : null;
12506
- if (!path70 || content == null) return null;
12507
- const out = { path: path70, content };
12539
+ if (!path71 || content == null) return null;
12540
+ const out = { path: path71, content };
12508
12541
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
12509
12542
  return out;
12510
12543
  }).filter((m2) => m2 !== null);
@@ -33473,8 +33506,8 @@ var require_utils = __commonJS({
33473
33506
  var result = transform[inputType][outputType](input);
33474
33507
  return result;
33475
33508
  };
33476
- exports2.resolve = function(path70) {
33477
- var parts = path70.split("/");
33509
+ exports2.resolve = function(path71) {
33510
+ var parts = path71.split("/");
33478
33511
  var result = [];
33479
33512
  for (var index = 0; index < parts.length; index++) {
33480
33513
  var part = parts[index];
@@ -39327,18 +39360,18 @@ var require_object = __commonJS({
39327
39360
  var object = new ZipObject(name, zipObjectContent, o);
39328
39361
  this.files[name] = object;
39329
39362
  };
39330
- var parentFolder = function(path70) {
39331
- if (path70.slice(-1) === "/") {
39332
- path70 = path70.substring(0, path70.length - 1);
39363
+ var parentFolder = function(path71) {
39364
+ if (path71.slice(-1) === "/") {
39365
+ path71 = path71.substring(0, path71.length - 1);
39333
39366
  }
39334
- var lastSlash = path70.lastIndexOf("/");
39335
- return lastSlash > 0 ? path70.substring(0, lastSlash) : "";
39367
+ var lastSlash = path71.lastIndexOf("/");
39368
+ return lastSlash > 0 ? path71.substring(0, lastSlash) : "";
39336
39369
  };
39337
- var forceTrailingSlash = function(path70) {
39338
- if (path70.slice(-1) !== "/") {
39339
- path70 += "/";
39370
+ var forceTrailingSlash = function(path71) {
39371
+ if (path71.slice(-1) !== "/") {
39372
+ path71 += "/";
39340
39373
  }
39341
- return path70;
39374
+ return path71;
39342
39375
  };
39343
39376
  var folderAdd = function(name, createFolders) {
39344
39377
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -40340,7 +40373,7 @@ var require_lib3 = __commonJS({
40340
40373
  // src/run-case/recorder.ts
40341
40374
  function startRunCaseRecorder(opts) {
40342
40375
  const now = opts.now ?? Date.now;
40343
- const dir = import_node_path58.default.dirname(opts.recordPath);
40376
+ const dir = import_node_path59.default.dirname(opts.recordPath);
40344
40377
  let stream = null;
40345
40378
  let closing = false;
40346
40379
  let closedSettled = false;
@@ -40380,12 +40413,12 @@ function startRunCaseRecorder(opts) {
40380
40413
  };
40381
40414
  return { tap, close, closed };
40382
40415
  }
40383
- var import_node_fs46, import_node_path58;
40416
+ var import_node_fs46, import_node_path59;
40384
40417
  var init_recorder = __esm({
40385
40418
  "src/run-case/recorder.ts"() {
40386
40419
  "use strict";
40387
40420
  import_node_fs46 = __toESM(require("fs"), 1);
40388
- import_node_path58 = __toESM(require("path"), 1);
40421
+ import_node_path59 = __toESM(require("path"), 1);
40389
40422
  }
40390
40423
  });
40391
40424
 
@@ -40428,7 +40461,7 @@ var init_wire = __esm({
40428
40461
  // src/run-case/controller.ts
40429
40462
  async function runController(opts) {
40430
40463
  const now = opts.now ?? Date.now;
40431
- const cwd = opts.cwd ?? (0, import_node_fs47.mkdtempSync)(import_node_path59.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
40464
+ const cwd = opts.cwd ?? (0, import_node_fs47.mkdtempSync)(import_node_path60.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
40432
40465
  const ownsCwd = opts.cwd === void 0;
40433
40466
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
40434
40467
  const spawnCtx = { cwd };
@@ -40595,13 +40628,13 @@ async function runController(opts) {
40595
40628
  }
40596
40629
  return exitCode ?? 0;
40597
40630
  }
40598
- var import_node_fs47, import_node_os23, import_node_path59;
40631
+ var import_node_fs47, import_node_os23, import_node_path60;
40599
40632
  var init_controller = __esm({
40600
40633
  "src/run-case/controller.ts"() {
40601
40634
  "use strict";
40602
40635
  import_node_fs47 = require("fs");
40603
40636
  import_node_os23 = __toESM(require("os"), 1);
40604
- import_node_path59 = __toESM(require("path"), 1);
40637
+ import_node_path60 = __toESM(require("path"), 1);
40605
40638
  init_claude();
40606
40639
  init_stdout_splitter();
40607
40640
  init_permission_stdio();
@@ -40863,7 +40896,7 @@ Env (advanced):
40863
40896
  `;
40864
40897
 
40865
40898
  // src/index.ts
40866
- var import_node_path57 = __toESM(require("path"), 1);
40899
+ var import_node_path58 = __toESM(require("path"), 1);
40867
40900
  var import_node_fs45 = __toESM(require("fs"), 1);
40868
40901
  var import_node_os22 = __toESM(require("os"), 1);
40869
40902
 
@@ -41728,6 +41761,28 @@ function dispatchShiftEnd(info, settle, killImpl) {
41728
41761
  throw new Error(`dispatchShiftEnd: unhandled kind ${JSON.stringify(_exhaustive)}`);
41729
41762
  }
41730
41763
 
41764
+ // src/dispatch/delivery.ts
41765
+ function selectPendingForDelivery(records, sourceSessionId) {
41766
+ return records.filter(
41767
+ (r) => r.sourceSessionId === sourceSessionId && (r.status === "completed" || r.status === "failed") && !r.deliveredAt
41768
+ ).slice().sort((a, b2) => a.createdAt < b2.createdAt ? -1 : a.createdAt > b2.createdAt ? 1 : 0);
41769
+ }
41770
+ function formatDeliveryText(records) {
41771
+ const blocks = records.map((r) => {
41772
+ const head = `[dispatch ${r.dispatchId} \u2192 ${r.targetPersonaId}]`;
41773
+ if (r.outcome?.kind === "success") {
41774
+ const files = r.outcome.filePaths && r.outcome.filePaths.length > 0 ? `
41775
+ Related files:
41776
+ ${r.outcome.filePaths.map((p2) => `- ${p2}`).join("\n")}` : "";
41777
+ return `${head} \u5B8C\u6210\uFF1A
41778
+ ${r.outcome.text}${files}`;
41779
+ }
41780
+ const reason = r.outcome?.kind === "failure" ? r.outcome.reason : "unknown failure";
41781
+ return `${head} \u5931\u8D25\uFF1A${reason}`;
41782
+ });
41783
+ return ["\u4F60\u4E4B\u524D\u59D4\u6D3E\u51FA\u53BB\u7684\u4EFB\u52A1\u6709\u4E86\u7ED3\u679C\uFF1A", "", blocks.join("\n\n")].join("\n");
41784
+ }
41785
+
41731
41786
  // src/session/runner.ts
41732
41787
  var import_node_os4 = __toESM(require("os"), 1);
41733
41788
  var import_node_path7 = __toESM(require("path"), 1);
@@ -41855,7 +41910,7 @@ var DISPATCH_SYSTEM_PROMPT_HINT = `## \u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A perso
41855
41910
  - \`targetPersona = <id>\`\uFF08@ token \u91CC\u7684 persona id\uFF09
41856
41911
  - \`prompt = \u53BB\u6389 @persona/<id> token \u7684\u7528\u6237\u539F\u6587\`
41857
41912
 
41858
- tool \u4F1A\u963B\u585E\u6302\u8D77\u7B49\u5BF9\u65B9\u4EBA\u683C\u5E72\u5B8C\uFF0C\u628A outcome\uFF08\u542B text + \u53EF\u9009 filePaths\uFF09\u4F5C\u4E3A tool_result \u56DE\u7ED9\u4F60\uFF0C\u4F60\u518D\u7EFC\u5408\u56DE\u5E94\u7528\u6237\u3002
41913
+ tool \u4F1A**\u7ACB\u5373\u8FD4\u56DE** dispatchId\uFF08\u5F02\u6B65\u59D4\u6D3E\uFF09\uFF0C\u5BF9\u65B9\u4EBA\u683C\u5728\u540E\u53F0\u5E72\u6D3B\u3002\u4F60\u544A\u8BC9\u7528\u6237"\u4EFB\u52A1\u5DF2\u4EA4\u529E"\u5373\u53EF\u7ED3\u675F\u672C\u8F6E\u2014\u2014**\u4E0D\u8981\u7B49\u5F85\u3001\u4E0D\u8981\u8F6E\u8BE2**\u3002\u5BF9\u65B9\u5E72\u5B8C\u540E\uFF0C\u7ED3\u679C\u4F1A\u4F5C\u4E3A\u4E00\u6761\u65B0\u6D88\u606F\u81EA\u52A8\u6CE8\u5165\u56DE\u4F60\u7684 session\uFF0C\u4F60\u5C4A\u65F6\u518D\u7EFC\u5408\u8F6C\u8FBE\u7ED9\u7528\u6237\u3002\u60F3\u67E5\u8FDB\u5C55\u65F6\uFF08\u7528\u6237\u95EE\u8D77\uFF09\u8C03 \`mcp__clawd-dispatch__personaDispatchList\`\u3002
41859
41914
 
41860
41915
  - **\u4E0D\u8981**\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF08\u4F60\u4E0D\u662F\u90A3\u4E2A persona\uFF09
41861
41916
  - **\u4E0D\u8981** in-place \u6A21\u4EFF\u5BF9\u65B9\u4EBA\u683C \u2014\u2014 dispatch \u662F\u771F\u5207\u5230\u53E6\u4E00\u4E2A\u72EC\u7ACB session
@@ -41868,7 +41923,7 @@ tool \u4F1A\u963B\u585E\u6302\u8D77\u7B49\u5BF9\u65B9\u4EBA\u683C\u5E72\u5B8C\uF
41868
41923
 
41869
41924
  - \`targetDeviceId = <token \u7B2C\u4E8C\u6BB5\u7684 deviceId\uFF0C\u539F\u6837\u586B>\`
41870
41925
 
41871
- deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731C\u6D4B\u3002\u5BF9\u65B9\u4E0D\u53EF\u8FBE / \u76EE\u6807 persona \u672A\u516C\u5F00\u4F1A\u8FD4\u56DE\u660E\u786E\u9519\u8BEF\uFF0C\u7167\u5E38\u628A\u9519\u8BEF\u8F6C\u8FF0\u7ED9\u7528\u6237\u5373\u53EF\u3002\u5176\u4F59\u884C\u4E3A\uFF08\u963B\u585E\u7B49\u7ED3\u679C\u3001\u5931\u8D25\u4E0D\u91CD\u8BD5\uFF09\u4E0E\u672C\u5730\u59D4\u6D3E\u4E00\u81F4\u3002
41926
+ deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731C\u6D4B\u3002\u5BF9\u65B9\u4E0D\u53EF\u8FBE / \u76EE\u6807 persona \u672A\u516C\u5F00\u4F1A\u5728\u7ED3\u679C\u56DE\u6D41\u65F6\u62A5\u5931\u8D25\uFF0C\u7167\u5E38\u8F6C\u8FF0\u7ED9\u7528\u6237\u5373\u53EF\u3002\u5176\u4F59\u884C\u4E3A\uFF08\u79D2\u56DE dispatchId\u3001\u7ED3\u679C\u81EA\u52A8\u6CE8\u5165\u3001\u5931\u8D25\u4E0D\u91CD\u8BD5\uFF09\u4E0E\u672C\u5730\u59D4\u6D3E\u4E00\u81F4\u3002
41872
41927
 
41873
41928
  ## \u88AB dispatch \u63A5\u5230\u4EFB\u52A1
41874
41929
 
@@ -41881,6 +41936,25 @@ deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731
41881
41936
  - \u5931\u8D25\uFF1A\`{ isFailure: true, reason: "..." }\`
41882
41937
  - \u62A5\u544A\u5B8C\u540E\u4F60\u7684 session \u5C31\u7ED3\u675F\u4E86\uFF0C\u4E0D\u8981\u7EE7\u7EED\u8F93\u51FA`;
41883
41938
 
41939
+ // src/session/reducer-types.ts
41940
+ function compressStatus(status) {
41941
+ switch (status) {
41942
+ case "spawning":
41943
+ case "running":
41944
+ return "running";
41945
+ case "running-idle":
41946
+ return "running-idle";
41947
+ case "stopping":
41948
+ case "stopped":
41949
+ return "stopped";
41950
+ case "error":
41951
+ return "error";
41952
+ case "idle":
41953
+ default:
41954
+ return "idle";
41955
+ }
41956
+ }
41957
+
41884
41958
  // src/session/reducer.ts
41885
41959
  function cloneState(s) {
41886
41960
  return {
@@ -42190,7 +42264,7 @@ var RUNTIME_PATCH_KEYS = [
42190
42264
  "effort",
42191
42265
  "cwd"
42192
42266
  ];
42193
- var MARKER_PATCH_KEYS = ["pinnedAt", "pinSortOrder"];
42267
+ var MARKER_PATCH_KEYS = ["pinnedAt", "pinSortOrder", "unreadAt"];
42194
42268
  function isMarkerOnlyPatch(patch) {
42195
42269
  const keys = Object.keys(patch).filter(
42196
42270
  (k2) => patch[k2] !== void 0
@@ -42435,6 +42509,24 @@ function assertNever(x) {
42435
42509
  throw new Error(`unexpected command: ${JSON.stringify(x)}`);
42436
42510
  }
42437
42511
  function reduceSession(state, input, deps) {
42512
+ const result = reduceSessionInner(state, input, deps);
42513
+ const prevWire = compressStatus(state.status);
42514
+ const nextWire = compressStatus(result.state.status);
42515
+ if (prevWire === nextWire) return result;
42516
+ let file;
42517
+ if (prevWire === "running") {
42518
+ file = { ...result.state.file, unreadAt: nowIso(deps) };
42519
+ } else if (nextWire === "running" && result.state.file.unreadAt != null) {
42520
+ file = { ...result.state.file, unreadAt: null };
42521
+ } else {
42522
+ return result;
42523
+ }
42524
+ return {
42525
+ state: { ...result.state, file },
42526
+ effects: [...result.effects, { kind: "persist-file", file }, sessionInfoFrame(file)]
42527
+ };
42528
+ }
42529
+ function reduceSessionInner(state, input, deps) {
42438
42530
  switch (input.kind) {
42439
42531
  case "stdout-line": {
42440
42532
  const events = deps.parseLine(input.line);
@@ -43216,53 +43308,13 @@ function probePtyDataThrottled(tsid, len, tail) {
43216
43308
  function compressFrameForWire(frame) {
43217
43309
  if (frame.type !== "session:status") return frame;
43218
43310
  const raw = frame;
43219
- const status = raw.status;
43220
- let compressed;
43221
- switch (status) {
43222
- case "spawning":
43223
- case "running":
43224
- compressed = "running";
43225
- break;
43226
- case "running-idle":
43227
- compressed = "running-idle";
43228
- break;
43229
- case "stopping":
43230
- case "stopped":
43231
- compressed = "stopped";
43232
- break;
43233
- case "error":
43234
- compressed = "error";
43235
- break;
43236
- case "idle":
43237
- compressed = "idle";
43238
- break;
43239
- default:
43240
- compressed = "idle";
43241
- break;
43242
- }
43311
+ const compressed = compressStatus(raw.status);
43243
43312
  return {
43244
43313
  type: "session:status",
43245
43314
  sessionId: raw.sessionId,
43246
43315
  status: compressed
43247
43316
  };
43248
43317
  }
43249
- function compressStatus(status) {
43250
- switch (status) {
43251
- case "spawning":
43252
- case "running":
43253
- return "running";
43254
- case "running-idle":
43255
- return "running-idle";
43256
- case "stopping":
43257
- case "stopped":
43258
- return "stopped";
43259
- case "error":
43260
- return "error";
43261
- case "idle":
43262
- default:
43263
- return "idle";
43264
- }
43265
- }
43266
43318
  function nowIso2(deps) {
43267
43319
  return new Date((deps.now ?? Date.now)()).toISOString();
43268
43320
  }
@@ -43721,6 +43773,13 @@ var SessionManager = class {
43721
43773
  }
43722
43774
  }
43723
43775
  }
43776
+ if (compressed.type === "session:status" && this.deps.dispatchStore) {
43777
+ const s = compressed;
43778
+ if (s.sessionId && s.status !== "running") {
43779
+ const sid = s.sessionId;
43780
+ setTimeout(() => this.deliverPending(sid), 0);
43781
+ }
43782
+ }
43724
43783
  if (this.currentCollector) {
43725
43784
  this.currentCollector.push({ frame: compressed, target });
43726
43785
  return;
@@ -43808,6 +43867,29 @@ var SessionManager = class {
43808
43867
  this.writeOwned(updated);
43809
43868
  return { response: updated, broadcast: [] };
43810
43869
  }
43870
+ // 清共享未读(spec 2026-07-20-shared-session-unread)。幂等:已读直接 no-op 不广播。
43871
+ // 与 pin 的差异:no-runner 路径也要广播 session:info——跨端实时清红点是本 RPC 的核心
43872
+ // 语义,不能等下次 session:list。
43873
+ markRead(args) {
43874
+ const existing = this.getFile(args.sessionId);
43875
+ if (existing.unreadAt == null) return { response: existing, broadcast: [] };
43876
+ const runner = this.runners.get(args.sessionId);
43877
+ if (runner) {
43878
+ const { value, broadcast } = this.withCollector(() => {
43879
+ runner.input({ kind: "command", command: { kind: "update", patch: { unreadAt: null } } });
43880
+ return runner.getState().file;
43881
+ });
43882
+ return { response: value, broadcast };
43883
+ }
43884
+ const updated = { ...existing, unreadAt: null };
43885
+ this.writeOwned(updated);
43886
+ return {
43887
+ response: updated,
43888
+ broadcast: [
43889
+ { frame: { type: "session:info", ...updated }, target: "all" }
43890
+ ]
43891
+ };
43892
+ }
43811
43893
  // sidebar 拖拽完成 → 整体重写 pinned root 的 pinSortOrder(按 orderedIds 下标 0,1,2...)
43812
43894
  // orderedIds 必须正好覆盖所有当前 pinned root(既不能漏,也不能含 unpinned id)
43813
43895
  reorderPins(args) {
@@ -44024,6 +44106,67 @@ var SessionManager = class {
44024
44106
  });
44025
44107
  return { response: { ok: true }, broadcast };
44026
44108
  }
44109
+ /**
44110
+ * Dispatch 结果投递(spec 2026-07-20 §5):查台账里该 source session 名下已完成
44111
+ * 且未投递的行,合并成一段 plain text 结果包注入回 source session。
44112
+ *
44113
+ * 触发点两个:complete 钩子(PersonaDispatchManager.onCompleted)+ source turn-end
44114
+ * 兜底 sweep(routeFromRunner 的 session:status 钩子)。幂等:投递成功盖 deliveredAt,
44115
+ * 再次调用查无未投递行直接返回。
44116
+ *
44117
+ * 判定口径:source 当前 running(含 reducer spawning,压缩后同 running)→ 不打扰,
44118
+ * 结果躺台账等下次 sweep。send 抛错(session 已删等)→ 记 log 不盖 deliveredAt,
44119
+ * 下次 sweep 自然重试。注入直接走 runner.input(无 collector),frames 经
44120
+ * routeFromRunner 正常广播上 wire。
44121
+ */
44122
+ deliverPending(sourceSessionId) {
44123
+ const store = this.deps.dispatchStore;
44124
+ if (!store) return;
44125
+ const pending = selectPendingForDelivery(store.list({ sourceSessionId }), sourceSessionId);
44126
+ if (pending.length === 0) return;
44127
+ const state = this.tryGetState(sourceSessionId);
44128
+ if (state && compressStatus(state.status) === "running") return;
44129
+ if (!this.deps.ownerPrincipalId) {
44130
+ this.deps.logger?.warn("dispatch.deliver.no-owner-principal", { sourceSessionId });
44131
+ return;
44132
+ }
44133
+ const sender = principalToSender(
44134
+ makeOwnerPrincipal(
44135
+ this.deps.ownerPrincipalId,
44136
+ this.deps.ownerDisplayName ?? "Owner",
44137
+ this.deps.ownerFeishuUnionId
44138
+ )
44139
+ );
44140
+ try {
44141
+ const file = this.findOwnedSession(sourceSessionId);
44142
+ if (!file) {
44143
+ this.deps.logger?.info("dispatch.deliver.source-gone", { sourceSessionId });
44144
+ return;
44145
+ }
44146
+ const runner = this.ensureRunnerForScope(file, this.scopeForFile(file));
44147
+ runner.input({
44148
+ kind: "command",
44149
+ command: {
44150
+ kind: "send",
44151
+ text: formatDeliveryText(pending),
44152
+ senderPrincipal: sender
44153
+ }
44154
+ });
44155
+ } catch (err) {
44156
+ this.deps.logger?.warn("dispatch.deliver.send-failed", {
44157
+ sourceSessionId,
44158
+ reason: err instanceof Error ? err.message : String(err)
44159
+ });
44160
+ return;
44161
+ }
44162
+ const nowIso3 = new Date(this.deps.now?.() ?? Date.now()).toISOString();
44163
+ for (const r of pending) store.update(r.dispatchId, { deliveredAt: nowIso3 });
44164
+ this.deps.logger?.info("dispatch.deliver.ok", {
44165
+ sourceSessionId,
44166
+ count: pending.length,
44167
+ dispatchIds: pending.map((r) => r.dispatchId)
44168
+ });
44169
+ }
44027
44170
  /**
44028
44171
  * 只读拿 SessionState;session 不存在返 null(不 throw)。
44029
44172
  * handlers/session.ts:currentTurnSender 用(loopback HTTP RPC 绝对不能因不存在的 sid 崩)。
@@ -46630,8 +46773,8 @@ function turnStartInput(text) {
46630
46773
  const items = [];
46631
46774
  let leftover = text;
46632
46775
  for (const m2 of text.matchAll(SKILL_RE)) {
46633
- const [marker, name, path70] = m2;
46634
- items.push({ type: "skill", name, path: path70 });
46776
+ const [marker, name, path71] = m2;
46777
+ items.push({ type: "skill", name, path: path71 });
46635
46778
  leftover = leftover.replace(marker, "");
46636
46779
  }
46637
46780
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -48130,35 +48273,60 @@ var PersonaDispatchManager = class {
48130
48273
  dispatchId,
48131
48274
  sourceSessionId: args.sourceSessionId,
48132
48275
  targetPersona: args.targetPersona,
48133
- waiters: [],
48134
- outcome: null
48276
+ waiters: []
48277
+ });
48278
+ this.deps.store.add({
48279
+ dispatchId,
48280
+ sourceSessionId: args.sourceSessionId,
48281
+ ...args.sourcePersonaId ? { sourcePersonaId: args.sourcePersonaId } : {},
48282
+ targetPersonaId: args.targetPersona,
48283
+ taskText: args.taskText
48135
48284
  });
48136
48285
  return { dispatchId };
48137
48286
  }
48138
48287
  get(dispatchId) {
48139
48288
  return this.map.get(dispatchId);
48140
48289
  }
48141
- async wait(dispatchId) {
48290
+ complete(dispatchId, outcome) {
48142
48291
  const state = this.map.get(dispatchId);
48143
- if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
48144
- if (state.outcome) return state.outcome;
48292
+ const status = outcome.kind === "success" ? "completed" : "failed";
48293
+ const updated = this.deps.store.update(dispatchId, {
48294
+ status,
48295
+ outcome,
48296
+ completedAt: new Date(this.deps.now()).toISOString()
48297
+ });
48298
+ if (state) {
48299
+ const waiters = state.waiters;
48300
+ state.waiters = [];
48301
+ for (const w2 of waiters) w2.resolve(outcome);
48302
+ this.map.delete(dispatchId);
48303
+ }
48304
+ const sourceSessionId = updated?.sourceSessionId ?? state?.sourceSessionId;
48305
+ if (sourceSessionId) this.deps.onCompleted?.(sourceSessionId);
48306
+ }
48307
+ /**
48308
+ * mesh B 角色 hold 专用(跨设备 dispatch 的 peer 侧):guest B-role handler 用它
48309
+ * 挂住 /rpc HTTP 响应直到 complete,把 outcome 回传给 A 侧 daemon 的 forwardToPeer
48310
+ * (A 侧后台 await,A 的 tool call 不受影响照样秒回)。
48311
+ * 本地 dispatch 链路**不用**这个——本地 outcome 只走台账 + deliverPending。
48312
+ */
48313
+ async waitForOutcome(dispatchId) {
48314
+ const state = this.map.get(dispatchId);
48315
+ if (!state) {
48316
+ const rec3 = this.deps.store.get(dispatchId);
48317
+ if (rec3?.outcome) return rec3.outcome;
48318
+ throw new Error(`unknown dispatchId: ${dispatchId}`);
48319
+ }
48145
48320
  const d = defer();
48146
48321
  state.waiters.push(d);
48147
48322
  return d.promise;
48148
48323
  }
48149
- complete(dispatchId, outcome) {
48150
- const state = this.map.get(dispatchId);
48151
- if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
48152
- state.outcome = outcome;
48153
- const waiters = state.waiters;
48154
- state.waiters = [];
48155
- for (const w2 of waiters) w2.resolve(outcome);
48156
- }
48157
48324
  /** SessionManager 在 createDispatchedSession 时调,建立 dispatchId → B sessionId 映射 */
48158
48325
  registerBSession(dispatchId, bSessionId) {
48159
48326
  const state = this.map.get(dispatchId);
48160
48327
  if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
48161
48328
  state.bSessionId = bSessionId;
48329
+ this.deps.store.update(dispatchId, { workerSessionId: bSessionId });
48162
48330
  }
48163
48331
  /** reducer buildSpawnContext 调,按 B 的 sessionId 反查 dispatchId(用于注 CLAWD_DISPATCH_ID env) */
48164
48332
  lookupDispatchByBSessionId(bSessionId) {
@@ -48169,11 +48337,112 @@ var PersonaDispatchManager = class {
48169
48337
  }
48170
48338
  };
48171
48339
 
48340
+ // src/dispatch/store.ts
48341
+ var import_promises = __toESM(require("fs/promises"), 1);
48342
+ var import_node_path16 = __toESM(require("path"), 1);
48343
+
48344
+ // src/shift/constants.ts
48345
+ var MAX_RUNS_PER_SHIFT = 30;
48346
+ var DEFAULT_TIMEOUT_MS = 30 * 6e4;
48347
+ var CONSECUTIVE_ERROR_THRESHOLD = 5;
48348
+ var TICK_INTERVAL_MS = 1e4;
48349
+ var STORE_FLUSH_DEBOUNCE_MS = 500;
48350
+ var CLOCK_JUMP_THRESHOLD_MS = 5 * 6e4;
48351
+
48352
+ // src/dispatch/store.ts
48353
+ var TASK_TEXT_MAX = 500;
48354
+ function createDispatchStore(deps) {
48355
+ let records = [];
48356
+ let flushTimer = null;
48357
+ async function load() {
48358
+ let raw;
48359
+ try {
48360
+ raw = await import_promises.default.readFile(deps.filePath, "utf8");
48361
+ } catch (e) {
48362
+ if (e.code === "ENOENT") {
48363
+ records = [];
48364
+ return;
48365
+ }
48366
+ throw e;
48367
+ }
48368
+ const parsed = JSON.parse(raw);
48369
+ if (parsed.version !== 1) {
48370
+ throw new Error(`dispatch.json: unsupported version ${parsed.version}`);
48371
+ }
48372
+ records = parsed.records ?? [];
48373
+ }
48374
+ async function flushNow() {
48375
+ if (flushTimer) {
48376
+ clearTimeout(flushTimer);
48377
+ flushTimer = null;
48378
+ }
48379
+ const content = { version: 1, records };
48380
+ await import_promises.default.mkdir(import_node_path16.default.dirname(deps.filePath), { recursive: true });
48381
+ const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
48382
+ await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48383
+ await import_promises.default.rename(tmp, deps.filePath);
48384
+ }
48385
+ function scheduleFlush() {
48386
+ if (flushTimer) return;
48387
+ flushTimer = setTimeout(() => {
48388
+ flushTimer = null;
48389
+ flushNow().catch((err) => {
48390
+ deps.onFlushError?.(err);
48391
+ });
48392
+ }, STORE_FLUSH_DEBOUNCE_MS);
48393
+ }
48394
+ function get(dispatchId) {
48395
+ return records.find((r) => r.dispatchId === dispatchId);
48396
+ }
48397
+ function add(input) {
48398
+ const rec3 = {
48399
+ dispatchId: input.dispatchId,
48400
+ sourceSessionId: input.sourceSessionId,
48401
+ ...input.sourcePersonaId ? { sourcePersonaId: input.sourcePersonaId } : {},
48402
+ targetPersonaId: input.targetPersonaId,
48403
+ taskText: input.taskText.slice(0, TASK_TEXT_MAX),
48404
+ status: "running",
48405
+ ...input.meta ? { meta: input.meta } : {},
48406
+ createdAt: new Date(deps.now()).toISOString()
48407
+ };
48408
+ records.push(rec3);
48409
+ scheduleFlush();
48410
+ return rec3;
48411
+ }
48412
+ function update(dispatchId, patch) {
48413
+ const idx = records.findIndex((r) => r.dispatchId === dispatchId);
48414
+ if (idx < 0) return void 0;
48415
+ const merged = { ...records[idx], ...patch };
48416
+ records[idx] = merged;
48417
+ scheduleFlush();
48418
+ return merged;
48419
+ }
48420
+ function list(filter) {
48421
+ return records.filter((r) => !filter?.sourceSessionId || r.sourceSessionId === filter.sourceSessionId).slice().sort((a, b2) => a.createdAt < b2.createdAt ? 1 : a.createdAt > b2.createdAt ? -1 : 0);
48422
+ }
48423
+ function markInterruptedAsFailed(nowIso3) {
48424
+ let touched = false;
48425
+ for (const r of records) {
48426
+ if (r.status === "running") {
48427
+ r.status = "failed";
48428
+ r.outcome = {
48429
+ kind: "failure",
48430
+ reason: "daemon restarted while dispatch in flight"
48431
+ };
48432
+ r.completedAt = nowIso3;
48433
+ touched = true;
48434
+ }
48435
+ }
48436
+ if (touched) scheduleFlush();
48437
+ }
48438
+ return { load, flushNow, add, update, list, get, markInterruptedAsFailed };
48439
+ }
48440
+
48172
48441
  // src/dispatch/mcp-config.ts
48173
48442
  var import_node_fs19 = __toESM(require("fs"), 1);
48174
- var import_node_path16 = __toESM(require("path"), 1);
48443
+ var import_node_path17 = __toESM(require("path"), 1);
48175
48444
  function dispatchMcpConfigPath(dataDir) {
48176
- return import_node_path16.default.join(dataDir, "dispatch.mcp.json");
48445
+ return import_node_path17.default.join(dataDir, "dispatch.mcp.json");
48177
48446
  }
48178
48447
  function writeDispatchMcpConfig(args) {
48179
48448
  const cfgPath = dispatchMcpConfigPath(args.dataDir);
@@ -48195,9 +48464,9 @@ function writeDispatchMcpConfig(args) {
48195
48464
 
48196
48465
  // src/ticket/mcp-config.ts
48197
48466
  var import_node_fs20 = __toESM(require("fs"), 1);
48198
- var import_node_path17 = __toESM(require("path"), 1);
48467
+ var import_node_path18 = __toESM(require("path"), 1);
48199
48468
  function ticketMcpConfigPath(dataDir) {
48200
- return import_node_path17.default.join(dataDir, "ticket.mcp.json");
48469
+ return import_node_path18.default.join(dataDir, "ticket.mcp.json");
48201
48470
  }
48202
48471
  function writeTicketMcpConfig(args) {
48203
48472
  const cfgPath = ticketMcpConfigPath(args.dataDir);
@@ -48224,9 +48493,9 @@ function writeTicketMcpConfig(args) {
48224
48493
 
48225
48494
  // src/shift/mcp-config.ts
48226
48495
  var import_node_fs21 = __toESM(require("fs"), 1);
48227
- var import_node_path18 = __toESM(require("path"), 1);
48496
+ var import_node_path19 = __toESM(require("path"), 1);
48228
48497
  function shiftMcpConfigPath(dataDir) {
48229
- return import_node_path18.default.join(dataDir, "shift.mcp.json");
48498
+ return import_node_path19.default.join(dataDir, "shift.mcp.json");
48230
48499
  }
48231
48500
  async function writeShiftMcpConfig(args) {
48232
48501
  const cfgPath = shiftMcpConfigPath(args.dataDir);
@@ -48248,9 +48517,9 @@ async function writeShiftMcpConfig(args) {
48248
48517
 
48249
48518
  // src/inbox/mcp-config.ts
48250
48519
  var import_node_fs22 = __toESM(require("fs"), 1);
48251
- var import_node_path19 = __toESM(require("path"), 1);
48520
+ var import_node_path20 = __toESM(require("path"), 1);
48252
48521
  function inboxMcpConfigPath(dataDir) {
48253
- return import_node_path19.default.join(dataDir, "inbox.mcp.json");
48522
+ return import_node_path20.default.join(dataDir, "inbox.mcp.json");
48254
48523
  }
48255
48524
  async function writeInboxMcpConfig(args) {
48256
48525
  const cfgPath = inboxMcpConfigPath(args.dataDir);
@@ -48272,9 +48541,9 @@ async function writeInboxMcpConfig(args) {
48272
48541
 
48273
48542
  // src/peer-ops/mcp-config.ts
48274
48543
  var import_node_fs23 = __toESM(require("fs"), 1);
48275
- var import_node_path20 = __toESM(require("path"), 1);
48544
+ var import_node_path21 = __toESM(require("path"), 1);
48276
48545
  function peerOpsMcpConfigPath(dataDir) {
48277
- return import_node_path20.default.join(dataDir, "peer-ops.mcp.json");
48546
+ return import_node_path21.default.join(dataDir, "peer-ops.mcp.json");
48278
48547
  }
48279
48548
  function writePeerOpsMcpConfig(args) {
48280
48549
  const cfgPath = peerOpsMcpConfigPath(args.dataDir);
@@ -48293,18 +48562,10 @@ function writePeerOpsMcpConfig(args) {
48293
48562
  }
48294
48563
 
48295
48564
  // src/shift/store.ts
48296
- var import_promises = __toESM(require("fs/promises"), 1);
48297
- var import_node_path21 = __toESM(require("path"), 1);
48565
+ var import_promises2 = __toESM(require("fs/promises"), 1);
48566
+ var import_node_path22 = __toESM(require("path"), 1);
48298
48567
  var import_node_crypto6 = require("crypto");
48299
48568
 
48300
- // src/shift/constants.ts
48301
- var MAX_RUNS_PER_SHIFT = 30;
48302
- var DEFAULT_TIMEOUT_MS = 30 * 6e4;
48303
- var CONSECUTIVE_ERROR_THRESHOLD = 5;
48304
- var TICK_INTERVAL_MS = 1e4;
48305
- var STORE_FLUSH_DEBOUNCE_MS = 500;
48306
- var CLOCK_JUMP_THRESHOLD_MS = 5 * 6e4;
48307
-
48308
48569
  // src/shift/schedule.ts
48309
48570
  var import_cron_parser = __toESM(require_dist(), 1);
48310
48571
  function computeNextRunAtMs(schedule, fromMs) {
@@ -48349,7 +48610,7 @@ function createShiftStore(deps) {
48349
48610
  async function load() {
48350
48611
  let raw;
48351
48612
  try {
48352
- raw = await import_promises.default.readFile(deps.filePath, "utf8");
48613
+ raw = await import_promises2.default.readFile(deps.filePath, "utf8");
48353
48614
  } catch (e) {
48354
48615
  if (e.code === "ENOENT") {
48355
48616
  shifts = [];
@@ -48377,10 +48638,10 @@ function createShiftStore(deps) {
48377
48638
  flushTimer = null;
48378
48639
  }
48379
48640
  const content = { version: 1, shifts };
48380
- await import_promises.default.mkdir(import_node_path21.default.dirname(deps.filePath), { recursive: true });
48641
+ await import_promises2.default.mkdir(import_node_path22.default.dirname(deps.filePath), { recursive: true });
48381
48642
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
48382
- await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48383
- await import_promises.default.rename(tmp, deps.filePath);
48643
+ await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48644
+ await import_promises2.default.rename(tmp, deps.filePath);
48384
48645
  }
48385
48646
  function scheduleFlush() {
48386
48647
  if (flushTimer) return;
@@ -48815,18 +49076,33 @@ function buildPersonaDispatchHandlers(deps) {
48815
49076
  if (!deps.forwardToPeer) {
48816
49077
  throw new Error("cross-device dispatch not wired (forwardToPeer missing)");
48817
49078
  }
49079
+ if (!sourceSessionId) {
49080
+ throw new Error(
49081
+ "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49082
+ );
49083
+ }
49084
+ const { dispatchId: dispatchId2 } = mgr.start({
49085
+ sourceSessionId,
49086
+ targetPersona: args.targetPersona,
49087
+ taskText: args.prompt
49088
+ });
48818
49089
  logger?.info("dispatch.run.forward", {
49090
+ dispatchId: dispatchId2,
48819
49091
  targetDeviceId: args.targetDeviceId,
48820
49092
  targetPersona: args.targetPersona
48821
49093
  });
48822
- const outcome2 = await deps.forwardToPeer({
49094
+ void deps.forwardToPeer({
48823
49095
  targetDeviceId: args.targetDeviceId,
48824
49096
  targetPersona: args.targetPersona,
48825
49097
  prompt: args.prompt,
48826
49098
  ...args.model ? { model: args.model } : {}
49099
+ }).then((outcome) => mgr.complete(dispatchId2, outcome)).catch((err) => {
49100
+ const reason = err instanceof Error ? err.message : String(err);
49101
+ logger?.warn("dispatch.forward.failed", { dispatchId: dispatchId2, reason });
49102
+ mgr.complete(dispatchId2, { kind: "failure", reason });
48827
49103
  });
48828
49104
  return {
48829
- response: { type: "personaDispatch:run:ok", outcome: outcome2 }
49105
+ response: { type: "personaDispatch:run:ok", dispatchId: dispatchId2 }
48830
49106
  };
48831
49107
  }
48832
49108
  if (ctx?.principal.kind === "guest") {
@@ -48839,7 +49115,8 @@ function buildPersonaDispatchHandlers(deps) {
48839
49115
  }
48840
49116
  const { dispatchId: dispatchId2 } = mgr.start({
48841
49117
  sourceSessionId: ctx.principal.id,
48842
- targetPersona: args.targetPersona
49118
+ targetPersona: args.targetPersona,
49119
+ taskText: args.prompt
48843
49120
  });
48844
49121
  logger?.info("dispatch.run.received.guest", {
48845
49122
  dispatchId: dispatchId2,
@@ -48865,9 +49142,9 @@ function buildPersonaDispatchHandlers(deps) {
48865
49142
  reason: `failed to spawn B: ${reason}`
48866
49143
  });
48867
49144
  });
48868
- const outcome2 = await mgr.wait(dispatchId2);
49145
+ const outcome = await mgr.waitForOutcome(dispatchId2);
48869
49146
  return {
48870
- response: { type: "personaDispatch:run:ok", outcome: outcome2 }
49147
+ response: { type: "personaDispatch:run:ok", outcome }
48871
49148
  };
48872
49149
  }
48873
49150
  if (!sourceSessionId) {
@@ -48877,7 +49154,8 @@ function buildPersonaDispatchHandlers(deps) {
48877
49154
  }
48878
49155
  const { dispatchId } = mgr.start({
48879
49156
  sourceSessionId,
48880
- targetPersona: args.targetPersona
49157
+ targetPersona: args.targetPersona,
49158
+ taskText: args.prompt
48881
49159
  });
48882
49160
  logger?.info("dispatch.run.received", {
48883
49161
  dispatchId,
@@ -48901,14 +49179,8 @@ function buildPersonaDispatchHandlers(deps) {
48901
49179
  reason: `failed to spawn B: ${reason}`
48902
49180
  });
48903
49181
  });
48904
- logger?.info("dispatch.run.waiting", { dispatchId });
48905
- const outcome = await mgr.wait(dispatchId);
48906
- logger?.info("dispatch.run.resolved", {
48907
- dispatchId,
48908
- outcomeKind: outcome.kind
48909
- });
48910
49182
  return {
48911
- response: { type: "personaDispatch:run:ok", outcome }
49183
+ response: { type: "personaDispatch:run:ok", dispatchId }
48912
49184
  };
48913
49185
  };
48914
49186
  const complete = async (frame) => {
@@ -48923,9 +49195,20 @@ function buildPersonaDispatchHandlers(deps) {
48923
49195
  response: { type: "personaDispatch:complete:ok" }
48924
49196
  };
48925
49197
  };
49198
+ const list = async (frame) => {
49199
+ const { type: _t, requestId: _r, ...rest } = frame;
49200
+ const args = DispatchListArgsSchema.parse(rest);
49201
+ const records = deps.store.list(
49202
+ args.sourceSessionId ? { sourceSessionId: args.sourceSessionId } : void 0
49203
+ );
49204
+ return {
49205
+ response: { type: "personaDispatch:list:ok", records }
49206
+ };
49207
+ };
48926
49208
  return {
48927
49209
  "personaDispatch:run": run,
48928
- "personaDispatch:complete": complete
49210
+ "personaDispatch:complete": complete,
49211
+ "personaDispatch:list": list
48929
49212
  };
48930
49213
  }
48931
49214
 
@@ -49231,13 +49514,13 @@ function mapSkillsListResponse(res) {
49231
49514
  const r = s ?? {};
49232
49515
  const name = str3(r.name);
49233
49516
  if (!name) continue;
49234
- const path70 = str3(r.path);
49517
+ const path71 = str3(r.path);
49235
49518
  const description = str3(r.description);
49236
49519
  const isPlugin = name.includes(":");
49237
49520
  out.push({
49238
49521
  name,
49239
49522
  source: isPlugin ? "plugin" : "project",
49240
- ...path70 ? { path: path70 } : {},
49523
+ ...path71 ? { path: path71 } : {},
49241
49524
  ...description ? { description } : {},
49242
49525
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
49243
49526
  });
@@ -49278,14 +49561,14 @@ async function listCodexSkills(cwd, deps = {}) {
49278
49561
  // src/workspace/browser.ts
49279
49562
  var import_node_fs24 = __toESM(require("fs"), 1);
49280
49563
  var import_node_os8 = __toESM(require("os"), 1);
49281
- var import_node_path22 = __toESM(require("path"), 1);
49564
+ var import_node_path23 = __toESM(require("path"), 1);
49282
49565
  init_protocol();
49283
49566
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
49284
49567
  function resolveInsideCwd(cwd, subpath) {
49285
- const absCwd = import_node_path22.default.resolve(cwd);
49286
- const joined = import_node_path22.default.resolve(absCwd, subpath ?? ".");
49287
- const rel = import_node_path22.default.relative(absCwd, joined);
49288
- if (rel.startsWith("..") || import_node_path22.default.isAbsolute(rel)) {
49568
+ const absCwd = import_node_path23.default.resolve(cwd);
49569
+ const joined = import_node_path23.default.resolve(absCwd, subpath ?? ".");
49570
+ const rel = import_node_path23.default.relative(absCwd, joined);
49571
+ if (rel.startsWith("..") || import_node_path23.default.isAbsolute(rel)) {
49289
49572
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
49290
49573
  }
49291
49574
  return joined;
@@ -49316,7 +49599,7 @@ var WorkspaceBrowser = class {
49316
49599
  mtime: ""
49317
49600
  };
49318
49601
  try {
49319
- const st = import_node_fs24.default.statSync(import_node_path22.default.join(full, d.name));
49602
+ const st = import_node_fs24.default.statSync(import_node_path23.default.join(full, d.name));
49320
49603
  entry.mtime = new Date(st.mtimeMs).toISOString();
49321
49604
  if (d.isFile()) entry.size = st.size;
49322
49605
  } catch {
@@ -49363,7 +49646,7 @@ var WorkspaceBrowser = class {
49363
49646
  // src/skills/agents-scanner.ts
49364
49647
  var import_node_fs25 = __toESM(require("fs"), 1);
49365
49648
  var import_node_os9 = __toESM(require("os"), 1);
49366
- var import_node_path23 = __toESM(require("path"), 1);
49649
+ var import_node_path24 = __toESM(require("path"), 1);
49367
49650
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
49368
49651
  function isDirLikeSync2(p2) {
49369
49652
  try {
@@ -49401,10 +49684,10 @@ function scanAgentsDir(dir, source, seen, out) {
49401
49684
  }
49402
49685
  for (const ent of entries) {
49403
49686
  if (!ent.name.endsWith(".md")) continue;
49404
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path23.default.join(dir, ent.name)))) {
49687
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path24.default.join(dir, ent.name)))) {
49405
49688
  continue;
49406
49689
  }
49407
- const filePath = import_node_path23.default.join(dir, ent.name);
49690
+ const filePath = import_node_path24.default.join(dir, ent.name);
49408
49691
  const baseName = ent.name.replace(/\.md$/, "");
49409
49692
  if (seen.has(baseName)) continue;
49410
49693
  seen.add(baseName);
@@ -49427,7 +49710,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
49427
49710
  return;
49428
49711
  }
49429
49712
  for (const ent of entries) {
49430
- const childPath = import_node_path23.default.join(dir, ent.name);
49713
+ const childPath = import_node_path24.default.join(dir, ent.name);
49431
49714
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
49432
49715
  walk2(childPath, [...namespaces, ent.name]);
49433
49716
  continue;
@@ -49452,9 +49735,9 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
49452
49735
  walk2(root, []);
49453
49736
  }
49454
49737
  function readInstalledPlugins2(home) {
49455
- const pluginsDir = import_node_path23.default.join(home, ".claude", "plugins");
49456
- const v2 = import_node_path23.default.join(pluginsDir, "installed_plugins_v2.json");
49457
- const v1 = import_node_path23.default.join(pluginsDir, "installed_plugins.json");
49738
+ const pluginsDir = import_node_path24.default.join(home, ".claude", "plugins");
49739
+ const v2 = import_node_path24.default.join(pluginsDir, "installed_plugins_v2.json");
49740
+ const v1 = import_node_path24.default.join(pluginsDir, "installed_plugins.json");
49458
49741
  let raw = null;
49459
49742
  for (const candidate of [v2, v1]) {
49460
49743
  try {
@@ -49483,19 +49766,19 @@ function readInstalledPlugins2(home) {
49483
49766
  return out;
49484
49767
  }
49485
49768
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
49486
- let cur = import_node_path23.default.resolve(startCwd);
49487
- const fsRoot = import_node_path23.default.parse(cur).root;
49769
+ let cur = import_node_path24.default.resolve(startCwd);
49770
+ const fsRoot = import_node_path24.default.parse(cur).root;
49488
49771
  while (true) {
49489
- scanAgentsDir(import_node_path23.default.join(cur, ".claude", "agents"), "project", seen, out);
49772
+ scanAgentsDir(import_node_path24.default.join(cur, ".claude", "agents"), "project", seen, out);
49490
49773
  let hasGit = false;
49491
49774
  try {
49492
- hasGit = import_node_fs25.default.existsSync(import_node_path23.default.join(cur, ".git"));
49775
+ hasGit = import_node_fs25.default.existsSync(import_node_path24.default.join(cur, ".git"));
49493
49776
  } catch {
49494
49777
  }
49495
49778
  if (hasGit) return;
49496
49779
  if (cur === home) return;
49497
49780
  if (cur === fsRoot) return;
49498
- const parent = import_node_path23.default.dirname(cur);
49781
+ const parent = import_node_path24.default.dirname(cur);
49499
49782
  if (parent === cur) return;
49500
49783
  cur = parent;
49501
49784
  }
@@ -49530,7 +49813,7 @@ var AgentsScanner = class {
49530
49813
  }
49531
49814
  const fsBlock = [];
49532
49815
  scanAgentsDir(
49533
- import_node_path23.default.join(this.home, ".claude", "agents"),
49816
+ import_node_path24.default.join(this.home, ".claude", "agents"),
49534
49817
  "global",
49535
49818
  seen,
49536
49819
  fsBlock
@@ -49544,7 +49827,7 @@ var AgentsScanner = class {
49544
49827
  ...this.extraPluginRoots
49545
49828
  ];
49546
49829
  for (const { name, root } of plugins) {
49547
- const agentsRoot = import_node_path23.default.join(root, "agents");
49830
+ const agentsRoot = import_node_path24.default.join(root, "agents");
49548
49831
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
49549
49832
  }
49550
49833
  return [...builtinBlock, ...fsBlock];
@@ -49554,13 +49837,13 @@ var AgentsScanner = class {
49554
49837
  // src/observer/session-observer.ts
49555
49838
  var import_node_fs27 = __toESM(require("fs"), 1);
49556
49839
  var import_node_os11 = __toESM(require("os"), 1);
49557
- var import_node_path25 = __toESM(require("path"), 1);
49840
+ var import_node_path26 = __toESM(require("path"), 1);
49558
49841
  init_claude_history();
49559
49842
 
49560
49843
  // src/observer/subagent-meta-observer.ts
49561
49844
  var import_node_fs26 = __toESM(require("fs"), 1);
49562
49845
  var import_node_os10 = __toESM(require("os"), 1);
49563
- var import_node_path24 = __toESM(require("path"), 1);
49846
+ var import_node_path25 = __toESM(require("path"), 1);
49564
49847
  init_claude_history();
49565
49848
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
49566
49849
  var SubagentMetaObserver = class {
@@ -49573,7 +49856,7 @@ var SubagentMetaObserver = class {
49573
49856
  watches = /* @__PURE__ */ new Map();
49574
49857
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
49575
49858
  resolveSubagentDir(cwd, toolSessionId) {
49576
- return import_node_path24.default.join(
49859
+ return import_node_path25.default.join(
49577
49860
  this.home,
49578
49861
  ".claude",
49579
49862
  "projects",
@@ -49629,7 +49912,7 @@ var SubagentMetaObserver = class {
49629
49912
  if (!m2) return;
49630
49913
  const agentId = m2[1];
49631
49914
  if (w2.emitted.has(agentId)) return;
49632
- const file = import_node_path24.default.join(w2.dirPath, name);
49915
+ const file = import_node_path25.default.join(w2.dirPath, name);
49633
49916
  let raw;
49634
49917
  try {
49635
49918
  raw = import_node_fs26.default.readFileSync(file, "utf8");
@@ -49689,7 +49972,7 @@ var SessionObserver = class {
49689
49972
  metaObserver;
49690
49973
  resolveJsonlPath(cwd, toolSessionId, override) {
49691
49974
  if (override) return override;
49692
- return import_node_path25.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
49975
+ return import_node_path26.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
49693
49976
  }
49694
49977
  start(args) {
49695
49978
  this.stop(args.sessionId);
@@ -49715,10 +49998,10 @@ var SessionObserver = class {
49715
49998
  lastDeriveMs: 0
49716
49999
  };
49717
50000
  try {
49718
- import_node_fs27.default.mkdirSync(import_node_path25.default.dirname(filePath), { recursive: true });
50001
+ import_node_fs27.default.mkdirSync(import_node_path26.default.dirname(filePath), { recursive: true });
49719
50002
  } catch {
49720
50003
  }
49721
- w2.watcher = import_node_fs27.default.watch(import_node_path25.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
50004
+ w2.watcher = import_node_fs27.default.watch(import_node_path26.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
49722
50005
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
49723
50006
  this.poll(w2);
49724
50007
  });
@@ -49815,7 +50098,7 @@ var SessionObserver = class {
49815
50098
  const now = Date.now();
49816
50099
  if (now - w2.lastRelocateScanMs < 1e3) return;
49817
50100
  w2.lastRelocateScanMs = now;
49818
- const projectsRoot = import_node_path25.default.join(this.home, ".claude", "projects");
50101
+ const projectsRoot = import_node_path26.default.join(this.home, ".claude", "projects");
49819
50102
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
49820
50103
  if (!found || found === w2.filePath) return;
49821
50104
  try {
@@ -49824,7 +50107,7 @@ var SessionObserver = class {
49824
50107
  }
49825
50108
  w2.filePath = found;
49826
50109
  try {
49827
- w2.watcher = import_node_fs27.default.watch(import_node_path25.default.dirname(found), { persistent: false }, (_event, changedName) => {
50110
+ w2.watcher = import_node_fs27.default.watch(import_node_path26.default.dirname(found), { persistent: false }, (_event, changedName) => {
49828
50111
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
49829
50112
  this.poll(w2);
49830
50113
  });
@@ -50408,13 +50691,13 @@ var EXPOSED_RPC_METHODS = [
50408
50691
  // dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
50409
50692
  // 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
50410
50693
  "auth.getIdentity",
50411
- // Persona dispatch (Task 4): A 端 dispatch MCP server 调 run(hold-and-wait 一次);
50412
- // B dispatch MCP server complete(带 outcome 触发 run 端 resolve)。详见
50413
- // protocol/src/dispatch.ts。
50694
+ // Persona dispatch(异步,spec 2026-07-20):A 端 dispatch MCP server 调 run(秒回 dispatchId);
50695
+ // B 端调 complete(带 outcome 台账 + 投递);发起方调 list 查台账。详见 protocol/src/dispatch.ts。
50414
50696
  "personaDispatch.run",
50415
50697
  // 跨设备 exec(spec 2026-07-16):A 侧本地 cc 经 /api/rpc 调,带 peerDeviceId → daemon forward。
50416
50698
  "peerExec.run",
50417
50699
  "personaDispatch.complete",
50700
+ "personaDispatch.list",
50418
50701
  // clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
50419
50702
  // 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
50420
50703
  // (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
@@ -50679,14 +50962,14 @@ async function authenticate(token, deps) {
50679
50962
  }
50680
50963
 
50681
50964
  // src/permission/capability-store.ts
50682
- var fs31 = __toESM(require("fs"), 1);
50683
- var path30 = __toESM(require("path"), 1);
50965
+ var fs32 = __toESM(require("fs"), 1);
50966
+ var path31 = __toESM(require("path"), 1);
50684
50967
  var CAPABILITIES_FILE_NAME = "capabilities.json";
50685
50968
  var FILE_VERSION = 1;
50686
50969
  var CapabilityStore = class {
50687
50970
  constructor(dataDir) {
50688
50971
  this.dataDir = dataDir;
50689
- fs31.mkdirSync(dataDir, { recursive: true });
50972
+ fs32.mkdirSync(dataDir, { recursive: true });
50690
50973
  this.cache = this.readFromDisk();
50691
50974
  }
50692
50975
  dataDir;
@@ -50710,13 +50993,13 @@ var CapabilityStore = class {
50710
50993
  this.flush();
50711
50994
  }
50712
50995
  filePath() {
50713
- return path30.join(this.dataDir, CAPABILITIES_FILE_NAME);
50996
+ return path31.join(this.dataDir, CAPABILITIES_FILE_NAME);
50714
50997
  }
50715
50998
  readFromDisk() {
50716
50999
  const file = this.filePath();
50717
51000
  let raw;
50718
51001
  try {
50719
- raw = fs31.readFileSync(file, "utf8");
51002
+ raw = fs32.readFileSync(file, "utf8");
50720
51003
  } catch (err) {
50721
51004
  if (err?.code === "ENOENT") return [];
50722
51005
  return [];
@@ -50744,10 +51027,10 @@ var CapabilityStore = class {
50744
51027
  }
50745
51028
  atomicWrite(file, content) {
50746
51029
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
50747
- fs31.writeFileSync(tmp, content, { mode: 384 });
50748
- fs31.renameSync(tmp, file);
51030
+ fs32.writeFileSync(tmp, content, { mode: 384 });
51031
+ fs32.renameSync(tmp, file);
50749
51032
  try {
50750
- fs31.chmodSync(file, 384);
51033
+ fs32.chmodSync(file, 384);
50751
51034
  } catch {
50752
51035
  }
50753
51036
  }
@@ -50840,14 +51123,14 @@ var CapabilityManager = class {
50840
51123
  };
50841
51124
 
50842
51125
  // src/permission/cleanup.ts
50843
- var fs32 = __toESM(require("fs"), 1);
51126
+ var fs33 = __toESM(require("fs"), 1);
50844
51127
  function cleanupGuestSessionsForCapability(cap, factory) {
50845
51128
  const removed = [];
50846
51129
  for (const g2 of cap.grants) {
50847
51130
  if (g2.resource.type !== "persona") continue;
50848
51131
  const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
50849
51132
  try {
50850
- fs32.rmSync(dir, { recursive: true, force: true });
51133
+ fs33.rmSync(dir, { recursive: true, force: true });
50851
51134
  removed.push(dir);
50852
51135
  } catch {
50853
51136
  }
@@ -50856,13 +51139,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
50856
51139
  }
50857
51140
 
50858
51141
  // src/inbox/inbox-store.ts
50859
- var fs33 = __toESM(require("fs"), 1);
50860
- var path31 = __toESM(require("path"), 1);
51142
+ var fs34 = __toESM(require("fs"), 1);
51143
+ var path32 = __toESM(require("path"), 1);
50861
51144
  var INBOX_SUBDIR = "inbox";
50862
51145
  var InboxStore = class {
50863
51146
  constructor(dataDir) {
50864
51147
  this.dataDir = dataDir;
50865
- fs33.mkdirSync(this.dirPath(), { recursive: true });
51148
+ fs34.mkdirSync(this.dirPath(), { recursive: true });
50866
51149
  }
50867
51150
  dataDir;
50868
51151
  /**
@@ -50874,7 +51157,7 @@ var InboxStore = class {
50874
51157
  const file = this.filePath(peerDeviceId);
50875
51158
  let raw;
50876
51159
  try {
50877
- raw = fs33.readFileSync(file, "utf8");
51160
+ raw = fs34.readFileSync(file, "utf8");
50878
51161
  } catch (err) {
50879
51162
  if (err?.code === "ENOENT") return [];
50880
51163
  return [];
@@ -50890,7 +51173,7 @@ var InboxStore = class {
50890
51173
  const dir = this.dirPath();
50891
51174
  let entries;
50892
51175
  try {
50893
- entries = fs33.readdirSync(dir);
51176
+ entries = fs34.readdirSync(dir);
50894
51177
  } catch (err) {
50895
51178
  if (err?.code === "ENOENT") return [];
50896
51179
  return [];
@@ -50906,9 +51189,9 @@ var InboxStore = class {
50906
51189
  if (existing.some((m2) => m2.id === message.id)) return;
50907
51190
  const file = this.filePath(message.peerDeviceId);
50908
51191
  const line = JSON.stringify(message) + "\n";
50909
- fs33.appendFileSync(file, line, { mode: 384 });
51192
+ fs34.appendFileSync(file, line, { mode: 384 });
50910
51193
  try {
50911
- fs33.chmodSync(file, 384);
51194
+ fs34.chmodSync(file, 384);
50912
51195
  } catch {
50913
51196
  }
50914
51197
  }
@@ -50938,7 +51221,7 @@ var InboxStore = class {
50938
51221
  removeByPeerDeviceId(peerDeviceId) {
50939
51222
  const file = this.filePath(peerDeviceId);
50940
51223
  try {
50941
- fs33.unlinkSync(file);
51224
+ fs34.unlinkSync(file);
50942
51225
  } catch (err) {
50943
51226
  if (err?.code === "ENOENT") return;
50944
51227
  }
@@ -50947,18 +51230,18 @@ var InboxStore = class {
50947
51230
  const file = this.filePath(peerDeviceId);
50948
51231
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
50949
51232
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
50950
- fs33.writeFileSync(tmp, content, { mode: 384 });
50951
- fs33.renameSync(tmp, file);
51233
+ fs34.writeFileSync(tmp, content, { mode: 384 });
51234
+ fs34.renameSync(tmp, file);
50952
51235
  try {
50953
- fs33.chmodSync(file, 384);
51236
+ fs34.chmodSync(file, 384);
50954
51237
  } catch {
50955
51238
  }
50956
51239
  }
50957
51240
  dirPath() {
50958
- return path31.join(this.dataDir, INBOX_SUBDIR);
51241
+ return path32.join(this.dataDir, INBOX_SUBDIR);
50959
51242
  }
50960
51243
  filePath(peerDeviceId) {
50961
- return path31.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51244
+ return path32.join(this.dirPath(), `${peerDeviceId}.jsonl`);
50962
51245
  }
50963
51246
  };
50964
51247
  function parseAllLines(raw) {
@@ -51046,8 +51329,8 @@ var InboxManager = class {
51046
51329
  };
51047
51330
 
51048
51331
  // src/state/contact-store.ts
51049
- var fs34 = __toESM(require("fs"), 1);
51050
- var path32 = __toESM(require("path"), 1);
51332
+ var fs35 = __toESM(require("fs"), 1);
51333
+ var path33 = __toESM(require("path"), 1);
51051
51334
  var FILE_NAME = "contacts.json";
51052
51335
  var ContactStore = class {
51053
51336
  constructor(dataDir) {
@@ -51057,10 +51340,10 @@ var ContactStore = class {
51057
51340
  contacts = /* @__PURE__ */ new Map();
51058
51341
  load() {
51059
51342
  this.contacts.clear();
51060
- const file = path32.join(this.dataDir, FILE_NAME);
51343
+ const file = path33.join(this.dataDir, FILE_NAME);
51061
51344
  let raw;
51062
51345
  try {
51063
- raw = fs34.readFileSync(file, "utf8");
51346
+ raw = fs35.readFileSync(file, "utf8");
51064
51347
  } catch (err) {
51065
51348
  if (err?.code !== "ENOENT") this.renameBak(file);
51066
51349
  return;
@@ -51129,20 +51412,20 @@ var ContactStore = class {
51129
51412
  return true;
51130
51413
  }
51131
51414
  flush() {
51132
- const file = path32.join(this.dataDir, FILE_NAME);
51415
+ const file = path33.join(this.dataDir, FILE_NAME);
51133
51416
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
51134
51417
  const content = JSON.stringify(
51135
51418
  { contacts: Array.from(this.contacts.values()) },
51136
51419
  null,
51137
51420
  2
51138
51421
  );
51139
- fs34.mkdirSync(this.dataDir, { recursive: true });
51140
- fs34.writeFileSync(tmp, content, { mode: 384 });
51141
- fs34.renameSync(tmp, file);
51422
+ fs35.mkdirSync(this.dataDir, { recursive: true });
51423
+ fs35.writeFileSync(tmp, content, { mode: 384 });
51424
+ fs35.renameSync(tmp, file);
51142
51425
  }
51143
51426
  renameBak(file) {
51144
51427
  try {
51145
- fs34.renameSync(file, `${file}.bak`);
51428
+ fs35.renameSync(file, `${file}.bak`);
51146
51429
  } catch {
51147
51430
  }
51148
51431
  }
@@ -51295,61 +51578,61 @@ async function autoReverseContact(args) {
51295
51578
  }
51296
51579
 
51297
51580
  // src/migrations/2026-05-20-flatten-sessions.ts
51298
- var fs35 = __toESM(require("fs"), 1);
51299
- var path33 = __toESM(require("path"), 1);
51581
+ var fs36 = __toESM(require("fs"), 1);
51582
+ var path34 = __toESM(require("path"), 1);
51300
51583
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
51301
51584
  function migrateFlattenSessions(opts) {
51302
51585
  const dataDir = opts.dataDir;
51303
51586
  const now = opts.now ?? Date.now;
51304
- const sessionsDir = path33.join(dataDir, "sessions");
51305
- const flagPath = path33.join(sessionsDir, MIGRATION_FLAG_NAME);
51587
+ const sessionsDir = path34.join(dataDir, "sessions");
51588
+ const flagPath = path34.join(sessionsDir, MIGRATION_FLAG_NAME);
51306
51589
  if (existsSync3(flagPath)) {
51307
51590
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
51308
51591
  }
51309
51592
  let movedBare = 0;
51310
51593
  let movedVmOwner = 0;
51311
51594
  let archivedListener = 0;
51312
- const defaultDir = path33.join(sessionsDir, "default");
51595
+ const defaultDir = path34.join(sessionsDir, "default");
51313
51596
  if (existsSync3(defaultDir)) {
51314
51597
  for (const entry of readdirSafe(defaultDir)) {
51315
51598
  if (!entry.endsWith(".json")) continue;
51316
- const src = path33.join(defaultDir, entry);
51317
- const dst = path33.join(sessionsDir, entry);
51318
- fs35.renameSync(src, dst);
51599
+ const src = path34.join(defaultDir, entry);
51600
+ const dst = path34.join(sessionsDir, entry);
51601
+ fs36.renameSync(src, dst);
51319
51602
  movedBare += 1;
51320
51603
  }
51321
51604
  rmdirIfEmpty(defaultDir);
51322
51605
  }
51323
51606
  for (const pid of readdirSafe(sessionsDir)) {
51324
- const personaDir = path33.join(sessionsDir, pid);
51607
+ const personaDir = path34.join(sessionsDir, pid);
51325
51608
  if (!isDir(personaDir)) continue;
51326
51609
  if (pid === "default") continue;
51327
- const ownerSrc = path33.join(personaDir, "owner");
51610
+ const ownerSrc = path34.join(personaDir, "owner");
51328
51611
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
51329
- const ownerDst = path33.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51330
- fs35.mkdirSync(ownerDst, { recursive: true });
51612
+ const ownerDst = path34.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51613
+ fs36.mkdirSync(ownerDst, { recursive: true });
51331
51614
  for (const file of readdirSafe(ownerSrc)) {
51332
51615
  if (!file.endsWith(".json")) continue;
51333
- fs35.renameSync(path33.join(ownerSrc, file), path33.join(ownerDst, file));
51616
+ fs36.renameSync(path34.join(ownerSrc, file), path34.join(ownerDst, file));
51334
51617
  movedVmOwner += 1;
51335
51618
  }
51336
51619
  rmdirIfEmpty(ownerSrc);
51337
51620
  }
51338
- const listenerSrc = path33.join(personaDir, "listener");
51621
+ const listenerSrc = path34.join(personaDir, "listener");
51339
51622
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
51340
- const archiveDst = path33.join(dataDir, ".legacy", `listener-${pid}`);
51341
- fs35.mkdirSync(archiveDst, { recursive: true });
51623
+ const archiveDst = path34.join(dataDir, ".legacy", `listener-${pid}`);
51624
+ fs36.mkdirSync(archiveDst, { recursive: true });
51342
51625
  for (const file of readdirSafe(listenerSrc)) {
51343
51626
  if (!file.endsWith(".json")) continue;
51344
- fs35.renameSync(path33.join(listenerSrc, file), path33.join(archiveDst, file));
51627
+ fs36.renameSync(path34.join(listenerSrc, file), path34.join(archiveDst, file));
51345
51628
  archivedListener += 1;
51346
51629
  }
51347
51630
  rmdirIfEmpty(listenerSrc);
51348
51631
  }
51349
51632
  rmdirIfEmpty(personaDir);
51350
51633
  }
51351
- fs35.mkdirSync(sessionsDir, { recursive: true });
51352
- fs35.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
51634
+ fs36.mkdirSync(sessionsDir, { recursive: true });
51635
+ fs36.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
51353
51636
  return {
51354
51637
  skipped: false,
51355
51638
  flagWritten: true,
@@ -51360,7 +51643,7 @@ function migrateFlattenSessions(opts) {
51360
51643
  }
51361
51644
  function existsSync3(p2) {
51362
51645
  try {
51363
- fs35.statSync(p2);
51646
+ fs36.statSync(p2);
51364
51647
  return true;
51365
51648
  } catch {
51366
51649
  return false;
@@ -51368,31 +51651,31 @@ function existsSync3(p2) {
51368
51651
  }
51369
51652
  function isDir(p2) {
51370
51653
  try {
51371
- return fs35.statSync(p2).isDirectory();
51654
+ return fs36.statSync(p2).isDirectory();
51372
51655
  } catch {
51373
51656
  return false;
51374
51657
  }
51375
51658
  }
51376
51659
  function readdirSafe(p2) {
51377
51660
  try {
51378
- return fs35.readdirSync(p2);
51661
+ return fs36.readdirSync(p2);
51379
51662
  } catch {
51380
51663
  return [];
51381
51664
  }
51382
51665
  }
51383
51666
  function rmdirIfEmpty(p2) {
51384
51667
  try {
51385
- fs35.rmdirSync(p2);
51668
+ fs36.rmdirSync(p2);
51386
51669
  } catch {
51387
51670
  }
51388
51671
  }
51389
51672
 
51390
51673
  // src/transport/http-router.ts
51391
51674
  var import_node_fs29 = __toESM(require("fs"), 1);
51392
- var import_node_path29 = __toESM(require("path"), 1);
51675
+ var import_node_path30 = __toESM(require("path"), 1);
51393
51676
 
51394
51677
  // src/attachment/mime.ts
51395
- var import_node_path26 = __toESM(require("path"), 1);
51678
+ var import_node_path27 = __toESM(require("path"), 1);
51396
51679
  var TEXT_PLAIN = "text/plain; charset=utf-8";
51397
51680
  var EXT_TO_NATIVE_MIME = {
51398
51681
  // 图片
@@ -51499,7 +51782,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
51499
51782
  ".mk"
51500
51783
  ]);
51501
51784
  function lookupMime(filePathOrName) {
51502
- const ext = import_node_path26.default.extname(filePathOrName).toLowerCase();
51785
+ const ext = import_node_path27.default.extname(filePathOrName).toLowerCase();
51503
51786
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
51504
51787
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
51505
51788
  return "application/octet-stream";
@@ -51569,9 +51852,9 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
51569
51852
 
51570
51853
  // src/attachment/upload.ts
51571
51854
  var import_node_fs28 = __toESM(require("fs"), 1);
51572
- var import_node_path27 = __toESM(require("path"), 1);
51855
+ var import_node_path28 = __toESM(require("path"), 1);
51573
51856
  var import_node_crypto8 = __toESM(require("crypto"), 1);
51574
- var import_promises2 = require("stream/promises");
51857
+ var import_promises3 = require("stream/promises");
51575
51858
  var UploadError = class extends Error {
51576
51859
  constructor(code, message) {
51577
51860
  super(message);
@@ -51581,14 +51864,14 @@ var UploadError = class extends Error {
51581
51864
  code;
51582
51865
  };
51583
51866
  function assertValidFileName(fileName) {
51584
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path27.default.basename(fileName)) {
51867
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path28.default.basename(fileName)) {
51585
51868
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
51586
51869
  }
51587
51870
  }
51588
51871
  var HASH_PREFIX_LEN = 16;
51589
51872
  async function writeUploadedAttachment(args) {
51590
51873
  assertValidFileName(args.fileName);
51591
- const attachmentsRoot = import_node_path27.default.join(args.sessionDir, ".attachments");
51874
+ const attachmentsRoot = import_node_path28.default.join(args.sessionDir, ".attachments");
51592
51875
  try {
51593
51876
  import_node_fs28.default.mkdirSync(attachmentsRoot, { recursive: true });
51594
51877
  } catch (err) {
@@ -51596,12 +51879,12 @@ async function writeUploadedAttachment(args) {
51596
51879
  }
51597
51880
  const hasher = import_node_crypto8.default.createHash("sha256");
51598
51881
  let actualSize = 0;
51599
- const tmpPath = import_node_path27.default.join(
51882
+ const tmpPath = import_node_path28.default.join(
51600
51883
  attachmentsRoot,
51601
51884
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
51602
51885
  );
51603
51886
  try {
51604
- await (0, import_promises2.pipeline)(
51887
+ await (0, import_promises3.pipeline)(
51605
51888
  args.body,
51606
51889
  async function* (source) {
51607
51890
  for await (const chunk of source) {
@@ -51631,7 +51914,7 @@ async function writeUploadedAttachment(args) {
51631
51914
  );
51632
51915
  }
51633
51916
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
51634
- const hashDir = import_node_path27.default.join(attachmentsRoot, attachmentId);
51917
+ const hashDir = import_node_path28.default.join(attachmentsRoot, attachmentId);
51635
51918
  let finalFileName;
51636
51919
  let hashDirExists = false;
51637
51920
  try {
@@ -51649,7 +51932,7 @@ async function writeUploadedAttachment(args) {
51649
51932
  try {
51650
51933
  import_node_fs28.default.mkdirSync(hashDir, { recursive: true });
51651
51934
  finalFileName = args.fileName;
51652
- import_node_fs28.default.renameSync(tmpPath, import_node_path27.default.join(hashDir, finalFileName));
51935
+ import_node_fs28.default.renameSync(tmpPath, import_node_path28.default.join(hashDir, finalFileName));
51653
51936
  } catch (err) {
51654
51937
  try {
51655
51938
  import_node_fs28.default.unlinkSync(tmpPath);
@@ -51658,8 +51941,8 @@ async function writeUploadedAttachment(args) {
51658
51941
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
51659
51942
  }
51660
51943
  }
51661
- const absPath = import_node_path27.default.join(hashDir, finalFileName);
51662
- const relPath = import_node_path27.default.relative(args.sessionDir, absPath);
51944
+ const absPath = import_node_path28.default.join(hashDir, finalFileName);
51945
+ const relPath = import_node_path28.default.relative(args.sessionDir, absPath);
51663
51946
  args.groupFileStore.upsert(args.scope, args.sessionId, {
51664
51947
  relPath: absPath,
51665
51948
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -51672,8 +51955,8 @@ async function writeUploadedAttachment(args) {
51672
51955
  }
51673
51956
 
51674
51957
  // src/extension/import.ts
51675
- var import_promises3 = __toESM(require("fs/promises"), 1);
51676
- var import_node_path28 = __toESM(require("path"), 1);
51958
+ var import_promises4 = __toESM(require("fs/promises"), 1);
51959
+ var import_node_path29 = __toESM(require("path"), 1);
51677
51960
  var import_node_os12 = __toESM(require("os"), 1);
51678
51961
  var import_jszip = __toESM(require_lib3(), 1);
51679
51962
  var ImportError = class extends Error {
@@ -51691,7 +51974,7 @@ async function importZip(buf, root) {
51691
51974
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
51692
51975
  }
51693
51976
  for (const name of Object.keys(zip.files)) {
51694
- if (name.includes("..") || name.startsWith("/") || import_node_path28.default.isAbsolute(name)) {
51977
+ if (name.includes("..") || name.startsWith("/") || import_node_path29.default.isAbsolute(name)) {
51695
51978
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
51696
51979
  }
51697
51980
  }
@@ -51724,33 +52007,33 @@ async function importZip(buf, root) {
51724
52007
  );
51725
52008
  }
51726
52009
  }
51727
- const destDir = import_node_path28.default.join(root, manifest.id);
52010
+ const destDir = import_node_path29.default.join(root, manifest.id);
51728
52011
  let destExists = false;
51729
52012
  try {
51730
- await import_promises3.default.access(destDir);
52013
+ await import_promises4.default.access(destDir);
51731
52014
  destExists = true;
51732
52015
  } catch {
51733
52016
  }
51734
52017
  if (destExists) {
51735
52018
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
51736
52019
  }
51737
- const stage = await import_promises3.default.mkdtemp(import_node_path28.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
52020
+ const stage = await import_promises4.default.mkdtemp(import_node_path29.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
51738
52021
  try {
51739
52022
  for (const [name, entry] of Object.entries(zip.files)) {
51740
- const dest = import_node_path28.default.join(stage, name);
52023
+ const dest = import_node_path29.default.join(stage, name);
51741
52024
  if (entry.dir) {
51742
- await import_promises3.default.mkdir(dest, { recursive: true });
52025
+ await import_promises4.default.mkdir(dest, { recursive: true });
51743
52026
  continue;
51744
52027
  }
51745
- await import_promises3.default.mkdir(import_node_path28.default.dirname(dest), { recursive: true });
52028
+ await import_promises4.default.mkdir(import_node_path29.default.dirname(dest), { recursive: true });
51746
52029
  const content = await entry.async("nodebuffer");
51747
- await import_promises3.default.writeFile(dest, content);
52030
+ await import_promises4.default.writeFile(dest, content);
51748
52031
  }
51749
- await import_promises3.default.mkdir(root, { recursive: true });
51750
- await import_promises3.default.rename(stage, destDir);
52032
+ await import_promises4.default.mkdir(root, { recursive: true });
52033
+ await import_promises4.default.rename(stage, destDir);
51751
52034
  return { extId: manifest.id };
51752
52035
  } catch (e) {
51753
- await import_promises3.default.rm(stage, { recursive: true, force: true }).catch(() => {
52036
+ await import_promises4.default.rm(stage, { recursive: true, force: true }).catch(() => {
51754
52037
  });
51755
52038
  throw e;
51756
52039
  }
@@ -51773,7 +52056,7 @@ var SHARE_UI_ASSET_MIME = {
51773
52056
  ".wasm": "application/wasm"
51774
52057
  };
51775
52058
  function shareUiAssetMime(filePath) {
51776
- const ext = import_node_path29.default.extname(filePath).toLowerCase();
52059
+ const ext = import_node_path30.default.extname(filePath).toLowerCase();
51777
52060
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
51778
52061
  }
51779
52062
  var DISPATCH_HEARTBEAT_MS = 25e3;
@@ -51860,7 +52143,7 @@ function isValidUploadFileName(fileName) {
51860
52143
  if (fileName === "." || fileName === "..") return false;
51861
52144
  if (fileName.startsWith(".")) return false;
51862
52145
  if (fileName.includes("/") || fileName.includes("\\")) return false;
51863
- return fileName === import_node_path29.default.basename(fileName);
52146
+ return fileName === import_node_path30.default.basename(fileName);
51864
52147
  }
51865
52148
  function createHttpRouter(deps) {
51866
52149
  return async (req, res) => {
@@ -52156,7 +52439,7 @@ function createHttpRouter(deps) {
52156
52439
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
52157
52440
  return true;
52158
52441
  }
52159
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path29.default.basename(r.absPath)));
52442
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path30.default.basename(r.absPath)));
52160
52443
  return true;
52161
52444
  }
52162
52445
  const ctx = deps.authResolver.resolveFromHeader(
@@ -52277,7 +52560,7 @@ function createHttpRouter(deps) {
52277
52560
  return true;
52278
52561
  }
52279
52562
  let absPath;
52280
- if (import_node_path29.default.isAbsolute(pathParam)) {
52563
+ if (import_node_path30.default.isAbsolute(pathParam)) {
52281
52564
  absPath = pathParam;
52282
52565
  } else if (deps.sessionStore) {
52283
52566
  const file = deps.sessionStore.read(sid);
@@ -52285,7 +52568,7 @@ function createHttpRouter(deps) {
52285
52568
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
52286
52569
  return true;
52287
52570
  }
52288
- absPath = import_node_path29.default.join(file.cwd, pathParam);
52571
+ absPath = import_node_path30.default.join(file.cwd, pathParam);
52289
52572
  } else {
52290
52573
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
52291
52574
  return true;
@@ -52387,7 +52670,7 @@ function streamFile(res, absPath, logger) {
52387
52670
  return;
52388
52671
  }
52389
52672
  const mime = lookupMime(absPath);
52390
- const basename = import_node_path29.default.basename(absPath);
52673
+ const basename = import_node_path30.default.basename(absPath);
52391
52674
  res.writeHead(200, {
52392
52675
  "Content-Type": mime,
52393
52676
  "Content-Length": String(stat.size),
@@ -52405,7 +52688,7 @@ function streamFile(res, absPath, logger) {
52405
52688
 
52406
52689
  // src/attachment/gc.ts
52407
52690
  var import_node_fs30 = __toESM(require("fs"), 1);
52408
- var import_node_path30 = __toESM(require("path"), 1);
52691
+ var import_node_path31 = __toESM(require("path"), 1);
52409
52692
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
52410
52693
  function runAttachmentGc(args) {
52411
52694
  const now = (args.now ?? Date.now)();
@@ -52414,17 +52697,17 @@ function runAttachmentGc(args) {
52414
52697
  for (const { scope, sessionId } of args.sessionScopes) {
52415
52698
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
52416
52699
  if (entry.stale) continue;
52417
- if (import_node_path30.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52700
+ if (import_node_path31.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52418
52701
  }
52419
52702
  }
52420
52703
  for (const { scope, sessionId } of args.sessionScopes) {
52421
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path30.default.join(
52704
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path31.default.join(
52422
52705
  args.dataDir,
52423
52706
  "sessions",
52424
52707
  ...scopeSubPath(scope).map(safeFileName),
52425
52708
  safeFileName(sessionId)
52426
52709
  );
52427
- const attRoot = import_node_path30.default.join(sessionDir, ".attachments");
52710
+ const attRoot = import_node_path31.default.join(sessionDir, ".attachments");
52428
52711
  let hashDirs;
52429
52712
  try {
52430
52713
  hashDirs = import_node_fs30.default.readdirSync(attRoot);
@@ -52434,7 +52717,7 @@ function runAttachmentGc(args) {
52434
52717
  continue;
52435
52718
  }
52436
52719
  for (const hashDir of hashDirs) {
52437
- const hashDirAbs = import_node_path30.default.join(attRoot, hashDir);
52720
+ const hashDirAbs = import_node_path31.default.join(attRoot, hashDir);
52438
52721
  let files;
52439
52722
  try {
52440
52723
  files = import_node_fs30.default.readdirSync(hashDirAbs);
@@ -52442,7 +52725,7 @@ function runAttachmentGc(args) {
52442
52725
  continue;
52443
52726
  }
52444
52727
  for (const name of files) {
52445
- const file = import_node_path30.default.join(hashDirAbs, name);
52728
+ const file = import_node_path31.default.join(hashDirAbs, name);
52446
52729
  let stat;
52447
52730
  try {
52448
52731
  stat = import_node_fs30.default.statSync(file);
@@ -52473,7 +52756,7 @@ function runAttachmentGc(args) {
52473
52756
 
52474
52757
  // src/attachment/group.ts
52475
52758
  var import_node_fs31 = __toESM(require("fs"), 1);
52476
- var import_node_path31 = __toESM(require("path"), 1);
52759
+ var import_node_path32 = __toESM(require("path"), 1);
52477
52760
  var import_node_crypto9 = __toESM(require("crypto"), 1);
52478
52761
  init_protocol();
52479
52762
  var GroupFileStore = class {
@@ -52485,11 +52768,11 @@ var GroupFileStore = class {
52485
52768
  this.logger = opts.logger;
52486
52769
  }
52487
52770
  rootForScope(scope) {
52488
- return import_node_path31.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52771
+ return import_node_path32.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52489
52772
  }
52490
52773
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
52491
52774
  filePath(scope, sessionId) {
52492
- return import_node_path31.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52775
+ return import_node_path32.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52493
52776
  }
52494
52777
  cacheKey(scope, sessionId) {
52495
52778
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -52524,7 +52807,7 @@ var GroupFileStore = class {
52524
52807
  }
52525
52808
  writeFile(scope, sessionId, entries) {
52526
52809
  const file = this.filePath(scope, sessionId);
52527
- import_node_fs31.default.mkdirSync(import_node_path31.default.dirname(file), { recursive: true });
52810
+ import_node_fs31.default.mkdirSync(import_node_path32.default.dirname(file), { recursive: true });
52528
52811
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
52529
52812
  import_node_fs31.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52530
52813
  import_node_fs31.default.renameSync(tmp, file);
@@ -52614,9 +52897,9 @@ var GroupFileStore = class {
52614
52897
 
52615
52898
  // src/discovery/state-file.ts
52616
52899
  var import_node_fs32 = __toESM(require("fs"), 1);
52617
- var import_node_path32 = __toESM(require("path"), 1);
52900
+ var import_node_path33 = __toESM(require("path"), 1);
52618
52901
  function defaultStateFilePath(dataDir) {
52619
- return import_node_path32.default.join(dataDir, "state.json");
52902
+ return import_node_path33.default.join(dataDir, "state.json");
52620
52903
  }
52621
52904
  function isPidAlive(pid) {
52622
52905
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -52652,7 +52935,7 @@ var StateFileManager = class {
52652
52935
  return { status: "stale", existing };
52653
52936
  }
52654
52937
  write(state) {
52655
- import_node_fs32.default.mkdirSync(import_node_path32.default.dirname(this.file), { recursive: true });
52938
+ import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(this.file), { recursive: true });
52656
52939
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
52657
52940
  import_node_fs32.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
52658
52941
  import_node_fs32.default.renameSync(tmp, this.file);
@@ -52680,13 +52963,13 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
52680
52963
 
52681
52964
  // src/tunnel/tunnel-manager.ts
52682
52965
  var import_node_fs36 = __toESM(require("fs"), 1);
52683
- var import_node_path36 = __toESM(require("path"), 1);
52966
+ var import_node_path37 = __toESM(require("path"), 1);
52684
52967
  var import_node_crypto10 = __toESM(require("crypto"), 1);
52685
52968
  var import_node_child_process9 = require("child_process");
52686
52969
 
52687
52970
  // src/tunnel/tunnel-store.ts
52688
52971
  var import_node_fs33 = __toESM(require("fs"), 1);
52689
- var import_node_path33 = __toESM(require("path"), 1);
52972
+ var import_node_path34 = __toESM(require("path"), 1);
52690
52973
  var TunnelStore = class {
52691
52974
  constructor(filePath) {
52692
52975
  this.filePath = filePath;
@@ -52705,7 +52988,7 @@ var TunnelStore = class {
52705
52988
  }
52706
52989
  }
52707
52990
  async set(v2) {
52708
- const dir = import_node_path33.default.dirname(this.filePath);
52991
+ const dir = import_node_path34.default.dirname(this.filePath);
52709
52992
  await import_node_fs33.default.promises.mkdir(dir, { recursive: true });
52710
52993
  const data = JSON.stringify(v2, null, 2);
52711
52994
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
@@ -52817,10 +53100,10 @@ function escape(v2) {
52817
53100
  // src/tunnel/frpc-binary.ts
52818
53101
  var import_node_fs34 = __toESM(require("fs"), 1);
52819
53102
  var import_node_os13 = __toESM(require("os"), 1);
52820
- var import_node_path34 = __toESM(require("path"), 1);
53103
+ var import_node_path35 = __toESM(require("path"), 1);
52821
53104
  var import_node_child_process7 = require("child_process");
52822
53105
  var import_node_stream3 = require("stream");
52823
- var import_promises4 = require("stream/promises");
53106
+ var import_promises5 = require("stream/promises");
52824
53107
  var FRPC_VERSION = "0.68.0";
52825
53108
  var UnsupportedPlatformError = class extends Error {
52826
53109
  constructor(platform, arch) {
@@ -52856,13 +53139,13 @@ async function ensureFrpcBinary(opts) {
52856
53139
  }
52857
53140
  const version2 = opts.version ?? FRPC_VERSION;
52858
53141
  const platform = opts.platform ?? detectPlatform();
52859
- const binDir = import_node_path34.default.join(opts.dataDir, "bin");
53142
+ const binDir = import_node_path35.default.join(opts.dataDir, "bin");
52860
53143
  import_node_fs34.default.mkdirSync(binDir, { recursive: true });
52861
53144
  cleanupStaleArtifacts(binDir);
52862
- const stableBin = import_node_path34.default.join(binDir, "frpc");
53145
+ const stableBin = import_node_path35.default.join(binDir, "frpc");
52863
53146
  if (import_node_fs34.default.existsSync(stableBin)) return stableBin;
52864
53147
  const partialBin = `${stableBin}.partial`;
52865
- const tarballPath = import_node_path34.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
53148
+ const tarballPath = import_node_path35.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
52866
53149
  try {
52867
53150
  const url = frpcDownloadUrl(version2, platform);
52868
53151
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -52888,7 +53171,7 @@ function cleanupStaleArtifacts(binDir) {
52888
53171
  }
52889
53172
  for (const name of entries) {
52890
53173
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
52891
- const full = import_node_path34.default.join(binDir, name);
53174
+ const full = import_node_path35.default.join(binDir, name);
52892
53175
  try {
52893
53176
  import_node_fs34.default.rmSync(full, { recursive: true, force: true });
52894
53177
  } catch {
@@ -52911,10 +53194,10 @@ async function downloadToFile(url, dest, fetchImpl) {
52911
53194
  }
52912
53195
  const out = import_node_fs34.default.createWriteStream(dest);
52913
53196
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
52914
- await (0, import_promises4.pipeline)(nodeStream, out);
53197
+ await (0, import_promises5.pipeline)(nodeStream, out);
52915
53198
  }
52916
53199
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
52917
- const work = import_node_path34.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53200
+ const work = import_node_path35.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
52918
53201
  import_node_fs34.default.mkdirSync(work, { recursive: true });
52919
53202
  try {
52920
53203
  await new Promise((resolve6, reject) => {
@@ -52923,7 +53206,7 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
52923
53206
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
52924
53207
  });
52925
53208
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
52926
- const src = import_node_path34.default.join(work, dirName, "frpc");
53209
+ const src = import_node_path35.default.join(work, dirName, "frpc");
52927
53210
  if (!import_node_fs34.default.existsSync(src)) {
52928
53211
  throw new Error(`frpc not found inside tarball at ${src}`);
52929
53212
  }
@@ -52935,10 +53218,10 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
52935
53218
 
52936
53219
  // src/tunnel/frpc-process.ts
52937
53220
  var import_node_fs35 = __toESM(require("fs"), 1);
52938
- var import_node_path35 = __toESM(require("path"), 1);
53221
+ var import_node_path36 = __toESM(require("path"), 1);
52939
53222
  var import_node_child_process8 = require("child_process");
52940
53223
  function frpcPidFilePath(dataDir) {
52941
- return import_node_path35.default.join(dataDir, "frpc.pid");
53224
+ return import_node_path36.default.join(dataDir, "frpc.pid");
52942
53225
  }
52943
53226
  function writeFrpcPid(dataDir, pid) {
52944
53227
  try {
@@ -52980,7 +53263,7 @@ function defaultSleep(ms) {
52980
53263
  }
52981
53264
  async function killStaleFrpc(deps) {
52982
53265
  const pidFile = frpcPidFilePath(deps.dataDir);
52983
- const tomlPath = import_node_path35.default.join(deps.dataDir, "frpc.toml");
53266
+ const tomlPath = import_node_path36.default.join(deps.dataDir, "frpc.toml");
52984
53267
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
52985
53268
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
52986
53269
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -53052,7 +53335,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
53052
53335
  var TunnelManager = class {
53053
53336
  constructor(deps) {
53054
53337
  this.deps = deps;
53055
- this.store = deps.store ?? new TunnelStore(import_node_path36.default.join(deps.dataDir, "tunnel.json"));
53338
+ this.store = deps.store ?? new TunnelStore(import_node_path37.default.join(deps.dataDir, "tunnel.json"));
53056
53339
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
53057
53340
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
53058
53341
  }
@@ -53179,7 +53462,7 @@ var TunnelManager = class {
53179
53462
  dataDir: this.deps.dataDir,
53180
53463
  override: this.deps.frpcBinaryOverride ?? void 0
53181
53464
  });
53182
- const tomlPath = import_node_path36.default.join(this.deps.dataDir, "frpc.toml");
53465
+ const tomlPath = import_node_path37.default.join(this.deps.dataDir, "frpc.toml");
53183
53466
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
53184
53467
  const toml = buildFrpcToml({
53185
53468
  serverAddr: t.frpsHost,
@@ -53194,9 +53477,9 @@ var TunnelManager = class {
53194
53477
  const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
53195
53478
  stdio: ["ignore", "pipe", "pipe"]
53196
53479
  });
53197
- const logDir = import_node_path36.default.join(this.deps.dataDir, "log");
53480
+ const logDir = import_node_path37.default.join(this.deps.dataDir, "log");
53198
53481
  import_node_fs36.default.mkdirSync(logDir, { recursive: true });
53199
- const logFilePath = import_node_path36.default.join(logDir, "frpc.log");
53482
+ const logFilePath = import_node_path37.default.join(logDir, "frpc.log");
53200
53483
  const logStream = import_node_fs36.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
53201
53484
  logStream.on("error", () => {
53202
53485
  });
@@ -53280,15 +53563,15 @@ async function waitForFrpcReady(proc, timeoutMs) {
53280
53563
 
53281
53564
  // src/tunnel/device-key.ts
53282
53565
  var import_node_os14 = __toESM(require("os"), 1);
53283
- var import_node_path37 = __toESM(require("path"), 1);
53566
+ var import_node_path38 = __toESM(require("path"), 1);
53284
53567
  var import_node_crypto11 = __toESM(require("crypto"), 1);
53285
53568
  var DERIVE_SALT = "clawd-tunnel-device-v1";
53286
53569
  function deriveStableDeviceKey(opts = {}) {
53287
53570
  const hostname = opts.hostname ?? import_node_os14.default.hostname();
53288
53571
  const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
53289
53572
  const home = opts.home ?? import_node_os14.default.homedir();
53290
- const defaultDataDir = import_node_path37.default.resolve(import_node_path37.default.join(home, ".clawd"));
53291
- const normalizedDataDir = opts.dataDir ? import_node_path37.default.resolve(opts.dataDir) : null;
53573
+ const defaultDataDir = import_node_path38.default.resolve(import_node_path38.default.join(home, ".clawd"));
53574
+ const normalizedDataDir = opts.dataDir ? import_node_path38.default.resolve(opts.dataDir) : null;
53292
53575
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
53293
53576
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
53294
53577
  return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
@@ -53296,11 +53579,11 @@ function deriveStableDeviceKey(opts = {}) {
53296
53579
 
53297
53580
  // src/auth-store.ts
53298
53581
  var import_node_fs37 = __toESM(require("fs"), 1);
53299
- var import_node_path38 = __toESM(require("path"), 1);
53582
+ var import_node_path39 = __toESM(require("path"), 1);
53300
53583
  var import_node_crypto12 = __toESM(require("crypto"), 1);
53301
53584
  var AUTH_FILE_NAME = "auth.json";
53302
53585
  function authFilePath(dataDir) {
53303
- return import_node_path38.default.join(dataDir, AUTH_FILE_NAME);
53586
+ return import_node_path39.default.join(dataDir, AUTH_FILE_NAME);
53304
53587
  }
53305
53588
  function loadOrCreateAuthFile(opts) {
53306
53589
  const file = authFilePath(opts.dataDir);
@@ -53356,7 +53639,7 @@ function readAuthFile(file) {
53356
53639
  }
53357
53640
  }
53358
53641
  function writeAuthFile(file, content) {
53359
- import_node_fs37.default.mkdirSync(import_node_path38.default.dirname(file), { recursive: true });
53642
+ import_node_fs37.default.mkdirSync(import_node_path39.default.dirname(file), { recursive: true });
53360
53643
  import_node_fs37.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
53361
53644
  try {
53362
53645
  import_node_fs37.default.chmodSync(file, 384);
@@ -53367,11 +53650,11 @@ function writeAuthFile(file, content) {
53367
53650
  // src/owner-profile.ts
53368
53651
  var import_node_fs38 = __toESM(require("fs"), 1);
53369
53652
  var import_node_os15 = __toESM(require("os"), 1);
53370
- var import_node_path39 = __toESM(require("path"), 1);
53653
+ var import_node_path40 = __toESM(require("path"), 1);
53371
53654
  var PROFILE_FILENAME = "profile.json";
53372
53655
  function loadOwnerDisplayName(dataDir) {
53373
53656
  const fallback = import_node_os15.default.userInfo().username;
53374
- const profilePath = import_node_path39.default.join(dataDir, PROFILE_FILENAME);
53657
+ const profilePath = import_node_path40.default.join(dataDir, PROFILE_FILENAME);
53375
53658
  let raw;
53376
53659
  try {
53377
53660
  raw = import_node_fs38.default.readFileSync(profilePath, "utf8");
@@ -53398,12 +53681,12 @@ function loadOwnerDisplayName(dataDir) {
53398
53681
 
53399
53682
  // src/feishu-auth/owner-identity-store.ts
53400
53683
  var import_node_fs39 = __toESM(require("fs"), 1);
53401
- var import_node_path40 = __toESM(require("path"), 1);
53684
+ var import_node_path41 = __toESM(require("path"), 1);
53402
53685
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
53403
53686
  var OwnerIdentityStore = class {
53404
53687
  file;
53405
53688
  constructor(dataDir) {
53406
- this.file = import_node_path40.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
53689
+ this.file = import_node_path41.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
53407
53690
  }
53408
53691
  read() {
53409
53692
  let raw;
@@ -53436,7 +53719,7 @@ var OwnerIdentityStore = class {
53436
53719
  };
53437
53720
  }
53438
53721
  write(record) {
53439
- import_node_fs39.default.mkdirSync(import_node_path40.default.dirname(this.file), { recursive: true });
53722
+ import_node_fs39.default.mkdirSync(import_node_path41.default.dirname(this.file), { recursive: true });
53440
53723
  import_node_fs39.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
53441
53724
  try {
53442
53725
  import_node_fs39.default.chmodSync(this.file, 384);
@@ -53582,9 +53865,9 @@ var CentralClientError = class extends Error {
53582
53865
  code;
53583
53866
  cause;
53584
53867
  };
53585
- async function centralRequest(opts, path70, init) {
53868
+ async function centralRequest(opts, path71, init) {
53586
53869
  const f = opts.fetchImpl ?? globalThis.fetch;
53587
- const url = `${opts.api.replace(/\/+$/, "")}${path70}`;
53870
+ const url = `${opts.api.replace(/\/+$/, "")}${path71}`;
53588
53871
  const ctrl = new AbortController();
53589
53872
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
53590
53873
  let res;
@@ -53778,8 +54061,8 @@ function verifyConnectToken(args) {
53778
54061
  }
53779
54062
 
53780
54063
  // src/feishu-auth/server-key.ts
53781
- var fs49 = __toESM(require("fs"), 1);
53782
- var path49 = __toESM(require("path"), 1);
54064
+ var fs50 = __toESM(require("fs"), 1);
54065
+ var path50 = __toESM(require("path"), 1);
53783
54066
  var FILE_NAME2 = "server-signing-key.json";
53784
54067
  var ServerKeyStore = class {
53785
54068
  constructor(dataDir) {
@@ -53787,12 +54070,12 @@ var ServerKeyStore = class {
53787
54070
  }
53788
54071
  dataDir;
53789
54072
  filePath() {
53790
- return path49.join(this.dataDir, FILE_NAME2);
54073
+ return path50.join(this.dataDir, FILE_NAME2);
53791
54074
  }
53792
54075
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
53793
54076
  read() {
53794
54077
  try {
53795
- const raw = fs49.readFileSync(this.filePath(), "utf8");
54078
+ const raw = fs50.readFileSync(this.filePath(), "utf8");
53796
54079
  const parsed = JSON.parse(raw);
53797
54080
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
53798
54081
  return parsed.publicKeyPem;
@@ -53807,12 +54090,12 @@ var ServerKeyStore = class {
53807
54090
  publicKeyPem,
53808
54091
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
53809
54092
  };
53810
- fs49.mkdirSync(this.dataDir, { recursive: true });
53811
- fs49.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
54093
+ fs50.mkdirSync(this.dataDir, { recursive: true });
54094
+ fs50.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
53812
54095
  }
53813
54096
  clear() {
53814
54097
  try {
53815
- fs49.unlinkSync(this.filePath());
54098
+ fs50.unlinkSync(this.filePath());
53816
54099
  } catch {
53817
54100
  }
53818
54101
  }
@@ -53827,7 +54110,7 @@ init_protocol();
53827
54110
  // src/session/fork.ts
53828
54111
  var import_node_fs40 = __toESM(require("fs"), 1);
53829
54112
  var import_node_os16 = __toESM(require("os"), 1);
53830
- var import_node_path41 = __toESM(require("path"), 1);
54113
+ var import_node_path42 = __toESM(require("path"), 1);
53831
54114
  init_claude_history();
53832
54115
  function readJsonlEntries(file) {
53833
54116
  const raw = import_node_fs40.default.readFileSync(file, "utf8");
@@ -53843,19 +54126,19 @@ function readJsonlEntries(file) {
53843
54126
  return out;
53844
54127
  }
53845
54128
  function forkSession(input) {
53846
- const baseDir = input.baseDir ?? import_node_path41.default.join(import_node_os16.default.homedir(), ".claude");
53847
- let projectDir = import_node_path41.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
53848
- let sourceFile = import_node_path41.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54129
+ const baseDir = input.baseDir ?? import_node_path42.default.join(import_node_os16.default.homedir(), ".claude");
54130
+ let projectDir = import_node_path42.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54131
+ let sourceFile = import_node_path42.default.join(projectDir, `${input.toolSessionId}.jsonl`);
53849
54132
  if (!import_node_fs40.default.existsSync(sourceFile)) {
53850
54133
  const found = findTranscriptByToolSessionId(
53851
- import_node_path41.default.join(baseDir, "projects"),
54134
+ import_node_path42.default.join(baseDir, "projects"),
53852
54135
  input.toolSessionId
53853
54136
  );
53854
54137
  if (!found) {
53855
54138
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
53856
54139
  }
53857
54140
  sourceFile = found;
53858
- projectDir = import_node_path41.default.dirname(found);
54141
+ projectDir = import_node_path42.default.dirname(found);
53859
54142
  }
53860
54143
  const entries = readJsonlEntries(sourceFile);
53861
54144
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -53884,7 +54167,7 @@ function forkSession(input) {
53884
54167
  }
53885
54168
  forkedLines.push(JSON.stringify(forked));
53886
54169
  }
53887
- const forkedFilePath = import_node_path41.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54170
+ const forkedFilePath = import_node_path42.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
53888
54171
  import_node_fs40.default.mkdirSync(projectDir, { recursive: true });
53889
54172
  import_node_fs40.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
53890
54173
  return { forkedToolSessionId, forkedFilePath };
@@ -54136,6 +54419,12 @@ function buildSessionHandlers(deps) {
54136
54419
  const { response, broadcast } = manager.pin(args);
54137
54420
  return { response: { type: "session:info", ...response }, broadcast };
54138
54421
  };
54422
+ const markRead = async (frame, _client, ctx) => {
54423
+ const args = SessionIdArgs.parse(frame);
54424
+ ensureSessionAccess(ctx, args.sessionId, "send");
54425
+ const { response, broadcast } = manager.markRead(args);
54426
+ return { response: { type: "session:info", ...response }, broadcast };
54427
+ };
54139
54428
  const reorderPins = async (frame) => {
54140
54429
  const args = SessionReorderPinsArgs.parse(frame);
54141
54430
  const { response, broadcast } = manager.reorderPins(args);
@@ -54251,6 +54540,7 @@ function buildSessionHandlers(deps) {
54251
54540
  "session:subscribe": subscribe,
54252
54541
  "session:unsubscribe": unsubscribe,
54253
54542
  "session:pin": pin,
54543
+ "session:markRead": markRead,
54254
54544
  "session:reorderPins": reorderPins,
54255
54545
  "session:answerQuestion": answerQuestion,
54256
54546
  "session:cancelQuestion": cancelQuestion,
@@ -54277,7 +54567,7 @@ function buildPermissionHandlers(deps) {
54277
54567
  }
54278
54568
 
54279
54569
  // src/handlers/history.ts
54280
- var path52 = __toESM(require("path"), 1);
54570
+ var path53 = __toESM(require("path"), 1);
54281
54571
  init_protocol();
54282
54572
 
54283
54573
  // src/session/recent-dirs.ts
@@ -54295,7 +54585,7 @@ function listRecentDirs(store, limit = 50) {
54295
54585
  }
54296
54586
 
54297
54587
  // src/permission/persona-paths.ts
54298
- var path51 = __toESM(require("path"), 1);
54588
+ var path52 = __toESM(require("path"), 1);
54299
54589
  function getAllowedPersonaIds(grants, action) {
54300
54590
  const ids = /* @__PURE__ */ new Set();
54301
54591
  for (const g2 of grants) {
@@ -54308,42 +54598,42 @@ function getAllowedPersonaIds(grants, action) {
54308
54598
  return ids;
54309
54599
  }
54310
54600
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
54311
- const target = path51.resolve(absPath);
54601
+ const target = path52.resolve(absPath);
54312
54602
  if (userWorkDir) {
54313
- const u = path51.resolve(userWorkDir);
54314
- const usep = u.endsWith(path51.sep) ? "" : path51.sep;
54603
+ const u = path52.resolve(userWorkDir);
54604
+ const usep = u.endsWith(path52.sep) ? "" : path52.sep;
54315
54605
  if (target === u || target.startsWith(u + usep)) return true;
54316
54606
  }
54317
- const root = path51.resolve(personaRoot);
54318
- const sep3 = root.endsWith(path51.sep) ? "" : path51.sep;
54607
+ const root = path52.resolve(personaRoot);
54608
+ const sep3 = root.endsWith(path52.sep) ? "" : path52.sep;
54319
54609
  if (!target.startsWith(root + sep3)) return false;
54320
- const rel = path51.relative(root, target);
54610
+ const rel = path52.relative(root, target);
54321
54611
  if (!rel || rel.startsWith("..")) return false;
54322
- const personaId = rel.split(path51.sep)[0];
54612
+ const personaId = rel.split(path52.sep)[0];
54323
54613
  if (!personaId) return false;
54324
54614
  const allowed = getAllowedPersonaIds(grants, action);
54325
54615
  if (allowed === "*") return true;
54326
54616
  return allowed.has(personaId);
54327
54617
  }
54328
54618
  function personaIdFromPath(absPath, personaRoot) {
54329
- const root = path51.resolve(personaRoot);
54330
- const target = path51.resolve(absPath);
54331
- const sep3 = root.endsWith(path51.sep) ? "" : path51.sep;
54619
+ const root = path52.resolve(personaRoot);
54620
+ const target = path52.resolve(absPath);
54621
+ const sep3 = root.endsWith(path52.sep) ? "" : path52.sep;
54332
54622
  if (!target.startsWith(root + sep3)) return null;
54333
- const rel = path51.relative(root, target);
54623
+ const rel = path52.relative(root, target);
54334
54624
  if (!rel || rel.startsWith("..")) return null;
54335
- const id = rel.split(path51.sep)[0];
54625
+ const id = rel.split(path52.sep)[0];
54336
54626
  return id || null;
54337
54627
  }
54338
54628
  function isPathWithin(dir, absPath) {
54339
- const d = path51.resolve(dir);
54340
- const t = path51.resolve(absPath);
54341
- const sep3 = d.endsWith(path51.sep) ? "" : path51.sep;
54629
+ const d = path52.resolve(dir);
54630
+ const t = path52.resolve(absPath);
54631
+ const sep3 = d.endsWith(path52.sep) ? "" : path52.sep;
54342
54632
  return t === d || t.startsWith(d + sep3);
54343
54633
  }
54344
54634
  function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
54345
54635
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
54346
- return personaIdFromPath(path51.resolve(absPath), personaRoot) === personaId;
54636
+ return personaIdFromPath(path52.resolve(absPath), personaRoot) === personaId;
54347
54637
  }
54348
54638
 
54349
54639
  // src/handlers/history.ts
@@ -54395,7 +54685,7 @@ function buildHistoryHandlers(deps) {
54395
54685
  if (!pid) return false;
54396
54686
  return isGuestPathAllowed(
54397
54687
  ctx.grants,
54398
- path52.join(personaRoot, pid),
54688
+ path53.join(personaRoot, pid),
54399
54689
  personaRoot,
54400
54690
  "read",
54401
54691
  userWorkDir
@@ -54407,7 +54697,7 @@ function buildHistoryHandlers(deps) {
54407
54697
  };
54408
54698
  const list = async (frame, _client, ctx) => {
54409
54699
  const args = HistoryListArgs.parse(frame);
54410
- assertGuestPath(ctx, path52.resolve(args.projectPath), personaRoot, "history:list");
54700
+ assertGuestPath(ctx, path53.resolve(args.projectPath), personaRoot, "history:list");
54411
54701
  const sessions = await history.listSessions(args);
54412
54702
  return { response: { type: "history:list", sessions } };
54413
54703
  };
@@ -54440,13 +54730,13 @@ function buildHistoryHandlers(deps) {
54440
54730
  };
54441
54731
  const subagents = async (frame, _client, ctx) => {
54442
54732
  const args = HistorySubagentsArgs.parse(frame);
54443
- assertGuestPath(ctx, path52.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54733
+ assertGuestPath(ctx, path53.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54444
54734
  const subs = await history.listSubagents(args);
54445
54735
  return { response: { type: "history:subagents", subagents: subs } };
54446
54736
  };
54447
54737
  const subagentRead = async (frame, _client, ctx) => {
54448
54738
  const args = HistorySubagentReadArgs.parse(frame);
54449
- assertGuestPath(ctx, path52.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54739
+ assertGuestPath(ctx, path53.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54450
54740
  const res = await history.readSubagent(args);
54451
54741
  return { response: { type: "history:subagent-read", ...res } };
54452
54742
  };
@@ -54455,7 +54745,7 @@ function buildHistoryHandlers(deps) {
54455
54745
  if (ctx?.principal.kind === "guest" && personaRoot) {
54456
54746
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
54457
54747
  const filtered = dirs.filter(
54458
- (d) => isGuestPathAllowed(ctx.grants, path52.resolve(d.cwd), personaRoot, "read", userWorkDir)
54748
+ (d) => isGuestPathAllowed(ctx.grants, path53.resolve(d.cwd), personaRoot, "read", userWorkDir)
54459
54749
  );
54460
54750
  return { response: { type: "history:recentDirs", dirs: filtered } };
54461
54751
  }
@@ -54472,7 +54762,7 @@ function buildHistoryHandlers(deps) {
54472
54762
  }
54473
54763
 
54474
54764
  // src/handlers/workspace.ts
54475
- var path53 = __toESM(require("path"), 1);
54765
+ var path54 = __toESM(require("path"), 1);
54476
54766
  var os16 = __toESM(require("os"), 1);
54477
54767
  init_protocol();
54478
54768
  init_protocol();
@@ -54514,22 +54804,22 @@ function buildWorkspaceHandlers(deps) {
54514
54804
  const args = WorkspaceListArgs.parse(frame);
54515
54805
  const isGuest = ctx?.principal.kind === "guest";
54516
54806
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
54517
- const resolvedCwd = path53.resolve(args.cwd ?? fallbackCwd);
54518
- const target = args.path ? path53.resolve(resolvedCwd, args.path) : resolvedCwd;
54807
+ const resolvedCwd = path54.resolve(args.cwd ?? fallbackCwd);
54808
+ const target = args.path ? path54.resolve(resolvedCwd, args.path) : resolvedCwd;
54519
54809
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
54520
54810
  const res = workspace.list({ ...args, cwd: resolvedCwd });
54521
54811
  return { response: { type: "workspace:list", ...res } };
54522
54812
  };
54523
54813
  const read = async (frame, _client, ctx) => {
54524
54814
  const args = WorkspaceReadArgs.parse(frame);
54525
- const target = path53.isAbsolute(args.path) ? path53.resolve(args.path) : path53.resolve(args.cwd, args.path);
54815
+ const target = path54.isAbsolute(args.path) ? path54.resolve(args.path) : path54.resolve(args.cwd, args.path);
54526
54816
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
54527
54817
  const res = workspace.read(args);
54528
54818
  return { response: { type: "workspace:read", ...res } };
54529
54819
  };
54530
54820
  const skillsList = async (frame, _client, ctx) => {
54531
54821
  const args = SkillsListArgs.parse(frame);
54532
- const cwdAbs = path53.resolve(args.cwd);
54822
+ const cwdAbs = path54.resolve(args.cwd);
54533
54823
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
54534
54824
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
54535
54825
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -54541,7 +54831,7 @@ function buildWorkspaceHandlers(deps) {
54541
54831
  };
54542
54832
  const agentsList = async (frame, _client, ctx) => {
54543
54833
  const args = AgentsListArgs.parse(frame);
54544
- const cwdAbs = path53.resolve(args.cwd);
54834
+ const cwdAbs = path54.resolve(args.cwd);
54545
54835
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
54546
54836
  if (args.tool === "codex") {
54547
54837
  return { response: { type: "agents:list", agents: [] } };
@@ -54563,18 +54853,18 @@ function buildWorkspaceHandlers(deps) {
54563
54853
  }
54564
54854
 
54565
54855
  // src/handlers/git.ts
54566
- var path55 = __toESM(require("path"), 1);
54856
+ var path56 = __toESM(require("path"), 1);
54567
54857
  init_protocol();
54568
54858
  init_protocol();
54569
54859
 
54570
54860
  // src/workspace/git.ts
54571
54861
  var import_node_child_process10 = require("child_process");
54572
54862
  var import_node_fs41 = __toESM(require("fs"), 1);
54573
- var import_node_path42 = __toESM(require("path"), 1);
54863
+ var import_node_path43 = __toESM(require("path"), 1);
54574
54864
  var import_node_util = require("util");
54575
54865
  var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
54576
54866
  function normalizePath(p2) {
54577
- const resolved = import_node_path42.default.resolve(p2);
54867
+ const resolved = import_node_path43.default.resolve(p2);
54578
54868
  try {
54579
54869
  return import_node_fs41.default.realpathSync(resolved);
54580
54870
  } catch {
@@ -54650,7 +54940,7 @@ async function listGitBranches(cwd) {
54650
54940
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
54651
54941
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
54652
54942
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
54653
- if (!isGuestPathAllowed(ctx.grants, path55.resolve(cwd), personaRoot, "read", userWorkDir)) {
54943
+ if (!isGuestPathAllowed(ctx.grants, path56.resolve(cwd), personaRoot, "read", userWorkDir)) {
54654
54944
  throw new ClawdError(
54655
54945
  ERROR_CODES.UNAUTHORIZED,
54656
54946
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -55336,7 +55626,7 @@ function buildPersonaHandlers(deps) {
55336
55626
  }
55337
55627
 
55338
55628
  // src/handlers/attachment.ts
55339
- var import_node_path43 = __toESM(require("path"), 1);
55629
+ var import_node_path44 = __toESM(require("path"), 1);
55340
55630
  init_protocol();
55341
55631
  init_protocol();
55342
55632
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -55416,12 +55706,12 @@ function buildAttachmentHandlers(deps) {
55416
55706
  `session ${args.sessionId} scope unresolved`
55417
55707
  );
55418
55708
  }
55419
- const cwdAbs = import_node_path43.default.resolve(sessionFile.cwd);
55420
- const candidateAbs = import_node_path43.default.isAbsolute(args.relPath) ? import_node_path43.default.resolve(args.relPath) : import_node_path43.default.resolve(cwdAbs, args.relPath);
55709
+ const cwdAbs = import_node_path44.default.resolve(sessionFile.cwd);
55710
+ const candidateAbs = import_node_path44.default.isAbsolute(args.relPath) ? import_node_path44.default.resolve(args.relPath) : import_node_path44.default.resolve(cwdAbs, args.relPath);
55421
55711
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
55422
55712
  const entries = deps.groupFileStore.list(scope, args.sessionId);
55423
55713
  const entry = entries.find((e) => {
55424
- const storedAbs = import_node_path43.default.isAbsolute(e.relPath) ? import_node_path43.default.resolve(e.relPath) : import_node_path43.default.resolve(cwdAbs, e.relPath);
55714
+ const storedAbs = import_node_path44.default.isAbsolute(e.relPath) ? import_node_path44.default.resolve(e.relPath) : import_node_path44.default.resolve(cwdAbs, e.relPath);
55425
55715
  return storedAbs === candidateAbs && !e.stale;
55426
55716
  });
55427
55717
  if (!entry) {
@@ -55446,7 +55736,7 @@ function buildAttachmentHandlers(deps) {
55446
55736
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
55447
55737
  const f = deps.sessionStore.read(sessionId);
55448
55738
  if (!f) return;
55449
- assertGuestAttachmentPath(ctx, import_node_path43.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
55739
+ assertGuestAttachmentPath(ctx, import_node_path44.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
55450
55740
  }
55451
55741
  const groupAdd = async (frame, _client, ctx) => {
55452
55742
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -55461,8 +55751,8 @@ function buildAttachmentHandlers(deps) {
55461
55751
  if (!scope) {
55462
55752
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
55463
55753
  }
55464
- const cwdAbs = import_node_path43.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
55465
- const candidateAbs = import_node_path43.default.isAbsolute(args.relPath) ? import_node_path43.default.resolve(args.relPath) : import_node_path43.default.resolve(cwdAbs, args.relPath);
55754
+ const cwdAbs = import_node_path44.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
55755
+ const candidateAbs = import_node_path44.default.isAbsolute(args.relPath) ? import_node_path44.default.resolve(args.relPath) : import_node_path44.default.resolve(cwdAbs, args.relPath);
55466
55756
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
55467
55757
  const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
55468
55758
  const size = 0;
@@ -55520,21 +55810,21 @@ function buildAttachmentHandlers(deps) {
55520
55810
  }
55521
55811
 
55522
55812
  // src/handlers/extension.ts
55523
- var import_promises8 = __toESM(require("fs/promises"), 1);
55524
- var import_node_path48 = __toESM(require("path"), 1);
55813
+ var import_promises9 = __toESM(require("fs/promises"), 1);
55814
+ var import_node_path49 = __toESM(require("path"), 1);
55525
55815
  init_protocol();
55526
55816
 
55527
55817
  // src/extension/bundle-zip.ts
55528
- var import_promises5 = __toESM(require("fs/promises"), 1);
55529
- var import_node_path44 = __toESM(require("path"), 1);
55818
+ var import_promises6 = __toESM(require("fs/promises"), 1);
55819
+ var import_node_path45 = __toESM(require("path"), 1);
55530
55820
  var import_node_crypto14 = __toESM(require("crypto"), 1);
55531
55821
  var import_jszip2 = __toESM(require_lib3(), 1);
55532
55822
  async function bundleExtensionDir(dir) {
55533
55823
  const entries = await listFilesSorted(dir);
55534
55824
  const zip = new import_jszip2.default();
55535
55825
  for (const rel of entries) {
55536
- const abs = import_node_path44.default.join(dir, rel);
55537
- const content = await import_promises5.default.readFile(abs);
55826
+ const abs = import_node_path45.default.join(dir, rel);
55827
+ const content = await import_promises6.default.readFile(abs);
55538
55828
  zip.file(rel, content, { date: FIXED_DATE });
55539
55829
  }
55540
55830
  const buffer = await zip.generateAsync({
@@ -55554,8 +55844,8 @@ async function listFilesSorted(rootDir) {
55554
55844
  return out;
55555
55845
  }
55556
55846
  async function walk(absRoot, relPrefix, out) {
55557
- const dirAbs = import_node_path44.default.join(absRoot, relPrefix);
55558
- const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
55847
+ const dirAbs = import_node_path45.default.join(absRoot, relPrefix);
55848
+ const entries = await import_promises6.default.readdir(dirAbs, { withFileTypes: true });
55559
55849
  for (const e of entries) {
55560
55850
  if (IGNORE_BASENAMES.has(e.name)) continue;
55561
55851
  const rel = relPrefix ? `${relPrefix}/${e.name}` : e.name;
@@ -55607,26 +55897,26 @@ function computePublishCheck(args) {
55607
55897
  }
55608
55898
 
55609
55899
  // src/extension/install-flow.ts
55610
- var import_promises6 = __toESM(require("fs/promises"), 1);
55611
- var import_node_path46 = __toESM(require("path"), 1);
55900
+ var import_promises7 = __toESM(require("fs/promises"), 1);
55901
+ var import_node_path47 = __toESM(require("path"), 1);
55612
55902
  var import_node_os19 = __toESM(require("os"), 1);
55613
55903
  var import_node_crypto15 = __toESM(require("crypto"), 1);
55614
55904
  var import_jszip3 = __toESM(require_lib3(), 1);
55615
55905
 
55616
55906
  // src/extension/paths.ts
55617
55907
  var import_node_os18 = __toESM(require("os"), 1);
55618
- var import_node_path45 = __toESM(require("path"), 1);
55908
+ var import_node_path46 = __toESM(require("path"), 1);
55619
55909
  function clawdHomeRoot(override) {
55620
- return override ?? process.env.CLAWD_HOME ?? import_node_path45.default.join(import_node_os18.default.homedir(), ".clawd");
55910
+ return override ?? process.env.CLAWD_HOME ?? import_node_path46.default.join(import_node_os18.default.homedir(), ".clawd");
55621
55911
  }
55622
55912
  function extensionsRoot(override) {
55623
- return import_node_path45.default.join(clawdHomeRoot(override), "extensions");
55913
+ return import_node_path46.default.join(clawdHomeRoot(override), "extensions");
55624
55914
  }
55625
55915
  function publishedChannelsFile(override) {
55626
- return import_node_path45.default.join(clawdHomeRoot(override), "extensions-published.json");
55916
+ return import_node_path46.default.join(clawdHomeRoot(override), "extensions-published.json");
55627
55917
  }
55628
55918
  function bundleCacheRoot(override) {
55629
- return import_node_path45.default.join(clawdHomeRoot(override), "extension-bundles");
55919
+ return import_node_path46.default.join(clawdHomeRoot(override), "extension-bundles");
55630
55920
  }
55631
55921
 
55632
55922
  // src/extension/install-flow.ts
@@ -55653,7 +55943,7 @@ async function installFromChannel(args, deps) {
55653
55943
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
55654
55944
  }
55655
55945
  for (const name of Object.keys(zip.files)) {
55656
- if (name.includes("..") || name.startsWith("/") || import_node_path46.default.isAbsolute(name)) {
55946
+ if (name.includes("..") || name.startsWith("/") || import_node_path47.default.isAbsolute(name)) {
55657
55947
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
55658
55948
  }
55659
55949
  }
@@ -55685,10 +55975,10 @@ async function installFromChannel(args, deps) {
55685
55975
  );
55686
55976
  }
55687
55977
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
55688
- const destDir = import_node_path46.default.join(deps.extensionsRoot, localExtId);
55978
+ const destDir = import_node_path47.default.join(deps.extensionsRoot, localExtId);
55689
55979
  let destExists = false;
55690
55980
  try {
55691
- await import_promises6.default.access(destDir);
55981
+ await import_promises7.default.access(destDir);
55692
55982
  destExists = true;
55693
55983
  } catch {
55694
55984
  }
@@ -55698,28 +55988,28 @@ async function installFromChannel(args, deps) {
55698
55988
  `extension ${localExtId} already installed locally (from owner ${channelRef.ownerPrincipalId})`
55699
55989
  );
55700
55990
  }
55701
- const stage = await import_promises6.default.mkdtemp(
55702
- import_node_path46.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
55991
+ const stage = await import_promises7.default.mkdtemp(
55992
+ import_node_path47.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
55703
55993
  );
55704
55994
  try {
55705
55995
  for (const [name, entry] of Object.entries(zip.files)) {
55706
- const dest = import_node_path46.default.join(stage, name);
55996
+ const dest = import_node_path47.default.join(stage, name);
55707
55997
  if (entry.dir) {
55708
- await import_promises6.default.mkdir(dest, { recursive: true });
55998
+ await import_promises7.default.mkdir(dest, { recursive: true });
55709
55999
  continue;
55710
56000
  }
55711
- await import_promises6.default.mkdir(import_node_path46.default.dirname(dest), { recursive: true });
56001
+ await import_promises7.default.mkdir(import_node_path47.default.dirname(dest), { recursive: true });
55712
56002
  if (name === "manifest.json") {
55713
56003
  const rewritten = { ...parsed.data, id: localExtId };
55714
- await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
56004
+ await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
55715
56005
  } else {
55716
- await import_promises6.default.writeFile(dest, await entry.async("nodebuffer"));
56006
+ await import_promises7.default.writeFile(dest, await entry.async("nodebuffer"));
55717
56007
  }
55718
56008
  }
55719
- await import_promises6.default.mkdir(deps.extensionsRoot, { recursive: true });
55720
- await import_promises6.default.rename(stage, destDir);
56009
+ await import_promises7.default.mkdir(deps.extensionsRoot, { recursive: true });
56010
+ await import_promises7.default.rename(stage, destDir);
55721
56011
  } catch (e) {
55722
- await import_promises6.default.rm(stage, { recursive: true, force: true }).catch(() => {
56012
+ await import_promises7.default.rm(stage, { recursive: true, force: true }).catch(() => {
55723
56013
  });
55724
56014
  throw e;
55725
56015
  }
@@ -55728,8 +56018,8 @@ async function installFromChannel(args, deps) {
55728
56018
  }
55729
56019
 
55730
56020
  // src/extension/update-flow.ts
55731
- var import_promises7 = __toESM(require("fs/promises"), 1);
55732
- var import_node_path47 = __toESM(require("path"), 1);
56021
+ var import_promises8 = __toESM(require("fs/promises"), 1);
56022
+ var import_node_path48 = __toESM(require("path"), 1);
55733
56023
  var import_node_os20 = __toESM(require("os"), 1);
55734
56024
  var import_node_crypto16 = __toESM(require("crypto"), 1);
55735
56025
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -55746,11 +56036,11 @@ async function updateFromChannel(args, deps) {
55746
56036
  channelRef.extId,
55747
56037
  channelRef.ownerPrincipalId
55748
56038
  );
55749
- const liveDir = import_node_path47.default.join(deps.extensionsRoot, localExtId);
56039
+ const liveDir = import_node_path48.default.join(deps.extensionsRoot, localExtId);
55750
56040
  const prevDir = `${liveDir}.prev`;
55751
56041
  let existingVersion;
55752
56042
  try {
55753
- const raw = await import_promises7.default.readFile(import_node_path47.default.join(liveDir, "manifest.json"), "utf8");
56043
+ const raw = await import_promises8.default.readFile(import_node_path48.default.join(liveDir, "manifest.json"), "utf8");
55754
56044
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
55755
56045
  if (!parsed2.success) {
55756
56046
  throw new UpdateError(
@@ -55783,7 +56073,7 @@ async function updateFromChannel(args, deps) {
55783
56073
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
55784
56074
  }
55785
56075
  for (const name of Object.keys(zip.files)) {
55786
- if (name.includes("..") || name.startsWith("/") || import_node_path47.default.isAbsolute(name)) {
56076
+ if (name.includes("..") || name.startsWith("/") || import_node_path48.default.isAbsolute(name)) {
55787
56077
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
55788
56078
  }
55789
56079
  }
@@ -55815,31 +56105,31 @@ async function updateFromChannel(args, deps) {
55815
56105
  );
55816
56106
  }
55817
56107
  await deps.runtime.close(localExtId);
55818
- await import_promises7.default.rm(prevDir, { recursive: true, force: true });
55819
- await import_promises7.default.rename(liveDir, prevDir);
55820
- const stage = await import_promises7.default.mkdtemp(
55821
- import_node_path47.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
56108
+ await import_promises8.default.rm(prevDir, { recursive: true, force: true });
56109
+ await import_promises8.default.rename(liveDir, prevDir);
56110
+ const stage = await import_promises8.default.mkdtemp(
56111
+ import_node_path48.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
55822
56112
  );
55823
56113
  try {
55824
56114
  for (const [name, entry] of Object.entries(zip.files)) {
55825
- const dest = import_node_path47.default.join(stage, name);
56115
+ const dest = import_node_path48.default.join(stage, name);
55826
56116
  if (entry.dir) {
55827
- await import_promises7.default.mkdir(dest, { recursive: true });
56117
+ await import_promises8.default.mkdir(dest, { recursive: true });
55828
56118
  continue;
55829
56119
  }
55830
- await import_promises7.default.mkdir(import_node_path47.default.dirname(dest), { recursive: true });
56120
+ await import_promises8.default.mkdir(import_node_path48.default.dirname(dest), { recursive: true });
55831
56121
  if (name === "manifest.json") {
55832
56122
  const rewritten = { ...parsed.data, id: localExtId };
55833
- await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
56123
+ await import_promises8.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
55834
56124
  } else {
55835
- await import_promises7.default.writeFile(dest, await entry.async("nodebuffer"));
56125
+ await import_promises8.default.writeFile(dest, await entry.async("nodebuffer"));
55836
56126
  }
55837
56127
  }
55838
- await import_promises7.default.rename(stage, liveDir);
56128
+ await import_promises8.default.rename(stage, liveDir);
55839
56129
  } catch (e) {
55840
- await import_promises7.default.rm(stage, { recursive: true, force: true }).catch(() => {
56130
+ await import_promises8.default.rm(stage, { recursive: true, force: true }).catch(() => {
55841
56131
  });
55842
- await import_promises7.default.rename(prevDir, liveDir).catch(() => {
56132
+ await import_promises8.default.rename(prevDir, liveDir).catch(() => {
55843
56133
  });
55844
56134
  await deps.runtime.open(localExtId).catch(() => {
55845
56135
  });
@@ -55861,7 +56151,7 @@ async function updateFromChannel(args, deps) {
55861
56151
  reason: e.message
55862
56152
  };
55863
56153
  }
55864
- await import_promises7.default.rm(prevDir, { recursive: true, force: true });
56154
+ await import_promises8.default.rm(prevDir, { recursive: true, force: true });
55865
56155
  return {
55866
56156
  kind: "updated",
55867
56157
  extId: localExtId,
@@ -55869,9 +56159,9 @@ async function updateFromChannel(args, deps) {
55869
56159
  };
55870
56160
  }
55871
56161
  async function rollback(deps, localExtId, liveDir, prevDir) {
55872
- await import_promises7.default.rm(liveDir, { recursive: true, force: true }).catch(() => {
56162
+ await import_promises8.default.rm(liveDir, { recursive: true, force: true }).catch(() => {
55873
56163
  });
55874
- await import_promises7.default.rename(prevDir, liveDir).catch(() => {
56164
+ await import_promises8.default.rename(prevDir, liveDir).catch(() => {
55875
56165
  });
55876
56166
  await deps.runtime.open(localExtId).catch(() => {
55877
56167
  });
@@ -55920,18 +56210,18 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
55920
56210
  );
55921
56211
  }
55922
56212
  }
55923
- const manifestPath = import_node_path48.default.join(root, extId, "manifest.json");
56213
+ const manifestPath = import_node_path49.default.join(root, extId, "manifest.json");
55924
56214
  const manifest = await readManifest(root, extId);
55925
56215
  const next = { ...manifest, version: newVersion };
55926
56216
  const tmp = `${manifestPath}.tmp`;
55927
- await import_promises8.default.writeFile(tmp, JSON.stringify(next, null, 2));
55928
- await import_promises8.default.rename(tmp, manifestPath);
56217
+ await import_promises9.default.writeFile(tmp, JSON.stringify(next, null, 2));
56218
+ await import_promises9.default.rename(tmp, manifestPath);
55929
56219
  }
55930
56220
  async function readManifest(root, extId) {
55931
- const file = import_node_path48.default.join(root, extId, "manifest.json");
56221
+ const file = import_node_path49.default.join(root, extId, "manifest.json");
55932
56222
  let raw;
55933
56223
  try {
55934
- raw = await import_promises8.default.readFile(file, "utf8");
56224
+ raw = await import_promises9.default.readFile(file, "utf8");
55935
56225
  } catch (e) {
55936
56226
  if (e.code === "ENOENT") {
55937
56227
  throw new ClawdError(ERROR_CODES.INVALID_PARAM, `extension ${extId} not installed`);
@@ -56019,7 +56309,7 @@ function buildExtensionHandlers(deps) {
56019
56309
  };
56020
56310
  async function buildSnapshotMeta(extId) {
56021
56311
  const manifest = await readManifest(deps.root, extId);
56022
- const { sha256, buffer } = await bundleExtensionDir(import_node_path48.default.join(deps.root, extId));
56312
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path49.default.join(deps.root, extId));
56023
56313
  return { manifest, contentHash: sha256, buffer };
56024
56314
  }
56025
56315
  const publish = async (frame, _client, ctx) => {
@@ -56202,7 +56492,7 @@ function buildExtensionHandlers(deps) {
56202
56492
  // src/app-builder/project-store.ts
56203
56493
  var import_node_fs42 = require("fs");
56204
56494
  var import_node_child_process11 = require("child_process");
56205
- var import_node_path49 = require("path");
56495
+ var import_node_path50 = require("path");
56206
56496
  init_protocol();
56207
56497
  var PROJECTS_DIR = "projects";
56208
56498
  var META_FILE = ".clawd-project.json";
@@ -56216,14 +56506,14 @@ var ProjectStore = class {
56216
56506
  root;
56217
56507
  /** projects/<name>/.clawd-project.json 路径 */
56218
56508
  metaPath(name) {
56219
- return (0, import_node_path49.join)(this.projectsRoot(), name, META_FILE);
56509
+ return (0, import_node_path50.join)(this.projectsRoot(), name, META_FILE);
56220
56510
  }
56221
56511
  /** projects/<name>/ 目录路径(cwd 用) */
56222
56512
  projectDir(name) {
56223
- return (0, import_node_path49.join)(this.projectsRoot(), name);
56513
+ return (0, import_node_path50.join)(this.projectsRoot(), name);
56224
56514
  }
56225
56515
  projectsRoot() {
56226
- return (0, import_node_path49.join)(this.root, PROJECTS_DIR);
56516
+ return (0, import_node_path50.join)(this.root, PROJECTS_DIR);
56227
56517
  }
56228
56518
  async list() {
56229
56519
  let entries;
@@ -56526,7 +56816,7 @@ var PublishJobRegistry = class {
56526
56816
  // src/app-builder/publish-job-runner.ts
56527
56817
  var import_node_child_process13 = require("child_process");
56528
56818
  var import_node_fs43 = require("fs");
56529
- var import_node_path50 = require("path");
56819
+ var import_node_path51 = require("path");
56530
56820
 
56531
56821
  // src/app-builder/publish-stage-parser.ts
56532
56822
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -56558,7 +56848,7 @@ async function startPublishJob(deps, args) {
56558
56848
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
56559
56849
  }
56560
56850
  const projDir = projectDir(args.name);
56561
- const logPath = (0, import_node_path50.join)(projDir, ".publish.log");
56851
+ const logPath = (0, import_node_path51.join)(projDir, ".publish.log");
56562
56852
  let logStream = null;
56563
56853
  try {
56564
56854
  logStream = (0, import_node_fs43.createWriteStream)(logPath, { flags: "w" });
@@ -56818,7 +57108,7 @@ async function recoverInterruptedJobs(deps) {
56818
57108
 
56819
57109
  // src/handlers/app-builder.ts
56820
57110
  init_protocol();
56821
- var import_node_path51 = require("path");
57111
+ var import_node_path52 = require("path");
56822
57112
  var import_node_fs44 = require("fs");
56823
57113
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
56824
57114
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
@@ -56976,8 +57266,8 @@ function buildAppBuilderHandlers(deps) {
56976
57266
  const project = await userStore.create(f.name, reservedPorts);
56977
57267
  try {
56978
57268
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
56979
- const templateSrcDir = (0, import_node_path51.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
56980
- const scaffoldScript = (0, import_node_path51.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
57269
+ const templateSrcDir = (0, import_node_path52.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
57270
+ const scaffoldScript = (0, import_node_path52.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
56981
57271
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
56982
57272
  deps.logger?.info("app-builder.scaffold.done", {
56983
57273
  name: project.name,
@@ -57198,7 +57488,7 @@ function buildAppBuilderHandlers(deps) {
57198
57488
  await userStore.clearPublishJob(args.name);
57199
57489
  }
57200
57490
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
57201
- const scriptPath = (0, import_node_path51.join)(deps.deployKitRoot, "scripts", "publish.sh");
57491
+ const scriptPath = (0, import_node_path52.join)(deps.deployKitRoot, "scripts", "publish.sh");
57202
57492
  deps.logger?.info("app-builder.publish.start", {
57203
57493
  name: args.name,
57204
57494
  sessionId: boundSession.sessionId,
@@ -57373,12 +57663,12 @@ function buildVisitorHandlers(deps) {
57373
57663
  }
57374
57664
 
57375
57665
  // src/extension/registry.ts
57376
- var import_promises9 = __toESM(require("fs/promises"), 1);
57377
- var import_node_path52 = __toESM(require("path"), 1);
57666
+ var import_promises10 = __toESM(require("fs/promises"), 1);
57667
+ var import_node_path53 = __toESM(require("path"), 1);
57378
57668
  async function loadAll(root) {
57379
57669
  let entries;
57380
57670
  try {
57381
- entries = await import_promises9.default.readdir(root, { withFileTypes: true });
57671
+ entries = await import_promises10.default.readdir(root, { withFileTypes: true });
57382
57672
  } catch (e) {
57383
57673
  if (e.code === "ENOENT") return [];
57384
57674
  throw e;
@@ -57387,16 +57677,16 @@ async function loadAll(root) {
57387
57677
  for (const ent of entries) {
57388
57678
  if (!ent.isDirectory()) continue;
57389
57679
  if (ent.name.startsWith(".")) continue;
57390
- records.push(await loadOne(import_node_path52.default.join(root, ent.name), ent.name));
57680
+ records.push(await loadOne(import_node_path53.default.join(root, ent.name), ent.name));
57391
57681
  }
57392
57682
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
57393
57683
  return records;
57394
57684
  }
57395
57685
  async function loadOne(dir, dirName) {
57396
- const manifestPath = import_node_path52.default.join(dir, "manifest.json");
57686
+ const manifestPath = import_node_path53.default.join(dir, "manifest.json");
57397
57687
  let raw;
57398
57688
  try {
57399
- raw = await import_promises9.default.readFile(manifestPath, "utf8");
57689
+ raw = await import_promises10.default.readFile(manifestPath, "utf8");
57400
57690
  } catch {
57401
57691
  return {
57402
57692
  extId: dirName,
@@ -57437,8 +57727,8 @@ async function loadOne(dir, dirName) {
57437
57727
  }
57438
57728
 
57439
57729
  // src/extension/uninstall.ts
57440
- var import_promises10 = __toESM(require("fs/promises"), 1);
57441
- var import_node_path53 = __toESM(require("path"), 1);
57730
+ var import_promises11 = __toESM(require("fs/promises"), 1);
57731
+ var import_node_path54 = __toESM(require("path"), 1);
57442
57732
  var UninstallError = class extends Error {
57443
57733
  constructor(code, message) {
57444
57734
  super(message);
@@ -57447,14 +57737,14 @@ var UninstallError = class extends Error {
57447
57737
  code;
57448
57738
  };
57449
57739
  async function uninstall(deps) {
57450
- const dir = import_node_path53.default.join(deps.root, deps.extId);
57740
+ const dir = import_node_path54.default.join(deps.root, deps.extId);
57451
57741
  try {
57452
- await import_promises10.default.access(dir);
57742
+ await import_promises11.default.access(dir);
57453
57743
  } catch {
57454
57744
  throw new UninstallError("NOT_FOUND", `extension ${deps.extId} not installed`);
57455
57745
  }
57456
57746
  await deps.runtime.close(deps.extId);
57457
- await import_promises10.default.rm(dir, { recursive: true, force: true });
57747
+ await import_promises11.default.rm(dir, { recursive: true, force: true });
57458
57748
  }
57459
57749
 
57460
57750
  // src/handlers/index.ts
@@ -58007,6 +58297,7 @@ var METHOD_GRANT_MAP = {
58007
58297
  "session:subscribe": CAPABILITY_SCOPED,
58008
58298
  "session:unsubscribe": CAPABILITY_SCOPED,
58009
58299
  "session:pin": CAPABILITY_SCOPED,
58300
+ "session:markRead": CAPABILITY_SCOPED,
58010
58301
  "session:reorderPins": ADMIN_ANY,
58011
58302
  // owner 全局操作,无 personaId 维度
58012
58303
  // mirror peer sessions (2026-05-26):owner-only — guest 不应能写/删本机 mirror SessionFile
@@ -58166,8 +58457,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
58166
58457
 
58167
58458
  // src/extension/runtime.ts
58168
58459
  var import_node_child_process16 = require("child_process");
58169
- var import_node_path54 = __toESM(require("path"), 1);
58170
- var import_promises11 = require("timers/promises");
58460
+ var import_node_path55 = __toESM(require("path"), 1);
58461
+ var import_promises12 = require("timers/promises");
58171
58462
 
58172
58463
  // src/extension/port-allocator.ts
58173
58464
  var import_node_net2 = __toESM(require("net"), 1);
@@ -58267,7 +58558,7 @@ var Runtime = class {
58267
58558
  /\$CLAWOS_EXT_PORT/g,
58268
58559
  String(port)
58269
58560
  );
58270
- const dir = import_node_path54.default.join(this.root, extId);
58561
+ const dir = import_node_path55.default.join(this.root, extId);
58271
58562
  const env = {
58272
58563
  ...process.env,
58273
58564
  CLAWOS_EXT_PORT: String(port),
@@ -58343,7 +58634,7 @@ ${handle.stderrTail}`
58343
58634
  if (res.ok) return;
58344
58635
  } catch {
58345
58636
  }
58346
- await (0, import_promises11.setTimeout)(this.healthzIntervalMs);
58637
+ await (0, import_promises12.setTimeout)(this.healthzIntervalMs);
58347
58638
  }
58348
58639
  throw new RuntimeError(
58349
58640
  "HEALTHZ_FAILED",
@@ -58368,7 +58659,7 @@ ${handle.stderrTail}`
58368
58659
  };
58369
58660
  killGroup("SIGTERM");
58370
58661
  const exited = new Promise((resolve6) => child.once("exit", () => resolve6()));
58371
- const timed = (0, import_promises11.setTimeout)(5e3).then(() => "timeout");
58662
+ const timed = (0, import_promises12.setTimeout)(5e3).then(() => "timeout");
58372
58663
  const winner = await Promise.race([exited.then(() => "exited"), timed]);
58373
58664
  if (winner === "timeout" && child.exitCode === null) {
58374
58665
  killGroup("SIGKILL");
@@ -58378,8 +58669,8 @@ ${handle.stderrTail}`
58378
58669
  };
58379
58670
 
58380
58671
  // src/extension/published-channels.ts
58381
- var import_promises12 = __toESM(require("fs/promises"), 1);
58382
- var import_node_path55 = __toESM(require("path"), 1);
58672
+ var import_promises13 = __toESM(require("fs/promises"), 1);
58673
+ var import_node_path56 = __toESM(require("path"), 1);
58383
58674
  init_zod();
58384
58675
  var PublishedChannelsError = class extends Error {
58385
58676
  constructor(code, message) {
@@ -58409,7 +58700,7 @@ var PublishedChannelStore = class {
58409
58700
  this.channels.clear();
58410
58701
  let raw;
58411
58702
  try {
58412
- raw = await import_promises12.default.readFile(this.filePath, "utf8");
58703
+ raw = await import_promises13.default.readFile(this.filePath, "utf8");
58413
58704
  } catch (e) {
58414
58705
  if (e.code === "ENOENT") {
58415
58706
  this.loaded = true;
@@ -58478,15 +58769,15 @@ var PublishedChannelStore = class {
58478
58769
  )
58479
58770
  };
58480
58771
  const tmp = `${this.filePath}.tmp`;
58481
- await import_promises12.default.mkdir(import_node_path55.default.dirname(this.filePath), { recursive: true });
58482
- await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
58483
- await import_promises12.default.rename(tmp, this.filePath);
58772
+ await import_promises13.default.mkdir(import_node_path56.default.dirname(this.filePath), { recursive: true });
58773
+ await import_promises13.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
58774
+ await import_promises13.default.rename(tmp, this.filePath);
58484
58775
  }
58485
58776
  };
58486
58777
 
58487
58778
  // src/extension/bundle-cache.ts
58488
- var import_promises13 = __toESM(require("fs/promises"), 1);
58489
- var import_node_path56 = __toESM(require("path"), 1);
58779
+ var import_promises14 = __toESM(require("fs/promises"), 1);
58780
+ var import_node_path57 = __toESM(require("path"), 1);
58490
58781
  var BundleCache = class {
58491
58782
  constructor(rootDir) {
58492
58783
  this.rootDir = rootDir;
@@ -58494,17 +58785,17 @@ var BundleCache = class {
58494
58785
  rootDir;
58495
58786
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
58496
58787
  async write(snapshotHash, buffer) {
58497
- await import_promises13.default.mkdir(this.rootDir, { recursive: true });
58498
- const file = import_node_path56.default.join(this.rootDir, `${snapshotHash}.zip`);
58788
+ await import_promises14.default.mkdir(this.rootDir, { recursive: true });
58789
+ const file = import_node_path57.default.join(this.rootDir, `${snapshotHash}.zip`);
58499
58790
  const tmp = `${file}.tmp`;
58500
- await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
58501
- await import_promises13.default.rename(tmp, file);
58791
+ await import_promises14.default.writeFile(tmp, buffer, { mode: 384 });
58792
+ await import_promises14.default.rename(tmp, file);
58502
58793
  }
58503
58794
  /** Returns the bundle bytes, or null when the file doesn't exist. */
58504
58795
  async read(snapshotHash) {
58505
- const file = import_node_path56.default.join(this.rootDir, `${snapshotHash}.zip`);
58796
+ const file = import_node_path57.default.join(this.rootDir, `${snapshotHash}.zip`);
58506
58797
  try {
58507
- return await import_promises13.default.readFile(file);
58798
+ return await import_promises14.default.readFile(file);
58508
58799
  } catch (e) {
58509
58800
  if (e.code === "ENOENT") return null;
58510
58801
  throw e;
@@ -58512,8 +58803,8 @@ var BundleCache = class {
58512
58803
  }
58513
58804
  /** Idempotent — missing file is not an error. */
58514
58805
  async delete(snapshotHash) {
58515
- const file = import_node_path56.default.join(this.rootDir, `${snapshotHash}.zip`);
58516
- await import_promises13.default.rm(file, { force: true });
58806
+ const file = import_node_path57.default.join(this.rootDir, `${snapshotHash}.zip`);
58807
+ await import_promises14.default.rm(file, { force: true });
58517
58808
  }
58518
58809
  };
58519
58810
 
@@ -58535,24 +58826,24 @@ async function startDaemon(config) {
58535
58826
  sampling: logShippingCfg.sampling,
58536
58827
  homeDir: import_node_os22.default.homedir()
58537
58828
  });
58538
- const logDir = import_node_path57.default.join(config.dataDir, "log");
58829
+ const logDir = import_node_path58.default.join(config.dataDir, "log");
58539
58830
  import_node_fs45.default.mkdirSync(logDir, { recursive: true });
58540
58831
  const logger = createLogger({
58541
58832
  level: config.logLevel,
58542
- file: import_node_path57.default.join(logDir, "clawd.log"),
58833
+ file: import_node_path58.default.join(logDir, "clawd.log"),
58543
58834
  logClient
58544
58835
  });
58545
58836
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
58546
58837
  const screenIdleProbeLogger = createFileOnlyLogger({
58547
- file: import_node_path57.default.join(logDir, "screen-idle-probe.log"),
58838
+ file: import_node_path58.default.join(logDir, "screen-idle-probe.log"),
58548
58839
  level: "debug"
58549
58840
  });
58550
58841
  logger.info("screen-idle probe logger enabled", {
58551
- file: import_node_path57.default.join(logDir, "screen-idle-probe.log")
58842
+ file: import_node_path58.default.join(logDir, "screen-idle-probe.log")
58552
58843
  });
58553
58844
  const CAP_TARGETS = [
58554
- import_node_path57.default.join(logDir, "clawd.log"),
58555
- import_node_path57.default.join(logDir, "screen-idle-probe.log")
58845
+ import_node_path58.default.join(logDir, "clawd.log"),
58846
+ import_node_path58.default.join(logDir, "screen-idle-probe.log")
58556
58847
  ];
58557
58848
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
58558
58849
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -58706,8 +58997,8 @@ async function startDaemon(config) {
58706
58997
  const agents = new AgentsScanner();
58707
58998
  const history = new ClaudeHistoryReader();
58708
58999
  let transport = null;
58709
- const personaStore = new PersonaStore(import_node_path57.default.join(config.dataDir, "personas"));
58710
- const usersRoot = import_node_path57.default.join(config.dataDir, "users");
59000
+ const personaStore = new PersonaStore(import_node_path58.default.join(config.dataDir, "personas"));
59001
+ const usersRoot = import_node_path58.default.join(config.dataDir, "users");
58711
59002
  const defaultsRoot = findDefaultsRoot(logger);
58712
59003
  if (defaultsRoot) {
58713
59004
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -58726,18 +59017,33 @@ async function startDaemon(config) {
58726
59017
  migrateAgentsMirror({ store: personaStore, logger });
58727
59018
  migrateCodexSandbox({ store: personaStore, logger });
58728
59019
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
58729
- const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
58730
- const here = typeof __dirname === "string" ? __dirname : import_node_path57.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
59020
+ const dispatchStore = createDispatchStore({
59021
+ filePath: import_node_path58.default.join(config.dataDir, "dispatch.json"),
59022
+ now: () => Date.now(),
59023
+ onFlushError: (err) => logger.error("dispatch store flush failed", {
59024
+ reason: err instanceof Error ? err.message : String(err)
59025
+ })
59026
+ });
59027
+ await dispatchStore.load();
59028
+ dispatchStore.markInterruptedAsFailed((/* @__PURE__ */ new Date()).toISOString());
59029
+ let deliverPendingRef;
59030
+ const personaDispatchManager = new PersonaDispatchManager({
59031
+ genId: () => v4_default(),
59032
+ store: dispatchStore,
59033
+ now: () => Date.now(),
59034
+ onCompleted: (sid) => deliverPendingRef?.(sid)
59035
+ });
59036
+ const here = typeof __dirname === "string" ? __dirname : import_node_path58.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
58731
59037
  const mcpConfigs = [];
58732
59038
  const dispatchServerCandidates = [
58733
- import_node_path57.default.join(here, "dispatch", "mcp-server.cjs"),
59039
+ import_node_path58.default.join(here, "dispatch", "mcp-server.cjs"),
58734
59040
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
58735
- import_node_path57.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59041
+ import_node_path58.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
58736
59042
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
58737
59043
  ];
58738
59044
  const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
58739
59045
  if (dispatchServerScriptPath) {
58740
- const dispatchLogPath = import_node_path57.default.join(logDir, "dispatch-mcp-server.log");
59046
+ const dispatchLogPath = import_node_path58.default.join(logDir, "dispatch-mcp-server.log");
58741
59047
  const dispatchCfgPath = writeDispatchMcpConfig({
58742
59048
  dataDir: config.dataDir,
58743
59049
  serverScriptPath: dispatchServerScriptPath,
@@ -58755,12 +59061,12 @@ async function startDaemon(config) {
58755
59061
  });
58756
59062
  }
58757
59063
  const ticketServerCandidates = [
58758
- import_node_path57.default.join(here, "ticket", "mcp-server.cjs"),
58759
- import_node_path57.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
59064
+ import_node_path58.default.join(here, "ticket", "mcp-server.cjs"),
59065
+ import_node_path58.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
58760
59066
  ];
58761
59067
  const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
58762
59068
  if (ticketServerScriptPath) {
58763
- const ticketLogPath = import_node_path57.default.join(logDir, "ticket-mcp-server.log");
59069
+ const ticketLogPath = import_node_path58.default.join(logDir, "ticket-mcp-server.log");
58764
59070
  const ticketCfgPath = writeTicketMcpConfig({
58765
59071
  dataDir: config.dataDir,
58766
59072
  serverScriptPath: ticketServerScriptPath,
@@ -58780,12 +59086,12 @@ async function startDaemon(config) {
58780
59086
  });
58781
59087
  }
58782
59088
  const shiftServerCandidates = [
58783
- import_node_path57.default.join(here, "shift", "mcp-server.cjs"),
58784
- import_node_path57.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
59089
+ import_node_path58.default.join(here, "shift", "mcp-server.cjs"),
59090
+ import_node_path58.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
58785
59091
  ];
58786
59092
  const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
58787
59093
  if (shiftServerScriptPath) {
58788
- const shiftLogPath = import_node_path57.default.join(logDir, "shift-mcp-server.log");
59094
+ const shiftLogPath = import_node_path58.default.join(logDir, "shift-mcp-server.log");
58789
59095
  const shiftCfgPath = await writeShiftMcpConfig({
58790
59096
  dataDir: config.dataDir,
58791
59097
  serverScriptPath: shiftServerScriptPath,
@@ -58804,12 +59110,12 @@ async function startDaemon(config) {
58804
59110
  );
58805
59111
  }
58806
59112
  const inboxServerCandidates = [
58807
- import_node_path57.default.join(here, "inbox", "mcp-server.cjs"),
58808
- import_node_path57.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
59113
+ import_node_path58.default.join(here, "inbox", "mcp-server.cjs"),
59114
+ import_node_path58.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
58809
59115
  ];
58810
59116
  const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
58811
59117
  if (inboxServerScriptPath) {
58812
- const inboxLogPath = import_node_path57.default.join(logDir, "inbox-mcp-server.log");
59118
+ const inboxLogPath = import_node_path58.default.join(logDir, "inbox-mcp-server.log");
58813
59119
  const inboxCfgPath = await writeInboxMcpConfig({
58814
59120
  dataDir: config.dataDir,
58815
59121
  serverScriptPath: inboxServerScriptPath,
@@ -58828,8 +59134,8 @@ async function startDaemon(config) {
58828
59134
  );
58829
59135
  }
58830
59136
  const peerOpsServerCandidates = [
58831
- import_node_path57.default.join(here, "peer-ops", "mcp-server.cjs"),
58832
- import_node_path57.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
59137
+ import_node_path58.default.join(here, "peer-ops", "mcp-server.cjs"),
59138
+ import_node_path58.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
58833
59139
  ];
58834
59140
  const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
58835
59141
  if (peerOpsServerScriptPath) {
@@ -58849,7 +59155,7 @@ async function startDaemon(config) {
58849
59155
  );
58850
59156
  }
58851
59157
  const shiftStore = createShiftStore({
58852
- filePath: import_node_path57.default.join(config.dataDir, "shift.json"),
59158
+ filePath: import_node_path58.default.join(config.dataDir, "shift.json"),
58853
59159
  ownerIdProvider: () => ownerPrincipalId,
58854
59160
  now: () => Date.now()
58855
59161
  });
@@ -58871,7 +59177,7 @@ async function startDaemon(config) {
58871
59177
  getAdapter,
58872
59178
  historyReader: history,
58873
59179
  dataDir: config.dataDir,
58874
- personaRoot: import_node_path57.default.join(config.dataDir, "personas"),
59180
+ personaRoot: import_node_path58.default.join(config.dataDir, "personas"),
58875
59181
  usersRoot,
58876
59182
  personaStore,
58877
59183
  ownerDisplayName,
@@ -58887,6 +59193,8 @@ async function startDaemon(config) {
58887
59193
  // Persona dispatch (Task 8): manager.createDispatchedSession 用 personaDispatchManager
58888
59194
  // registerBSession;reducer 通过闭包反查 dispatchId 注 CLAWD_DISPATCH_ID env。
58889
59195
  personaDispatchManager,
59196
+ // Dispatch 结果投递:deliverPending 查台账 + turn-end sweep(spec 2026-07-20 §5)。
59197
+ dispatchStore,
58890
59198
  // daemon 级 MCP server 登记表(dispatch / ticket / shift / inbox / ...,上方各 writer
58891
59199
  // 块写盘成功即登记);reducer 透传到 SpawnContext.mcpConfigs,cc spawn 逐项 --mcp-config。
58892
59200
  mcpConfigs,
@@ -58909,7 +59217,7 @@ async function startDaemon(config) {
58909
59217
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
58910
59218
  attachmentGroup: {
58911
59219
  onFileEdit: (input) => {
58912
- const absPath = import_node_path57.default.isAbsolute(input.relPath) ? input.relPath : import_node_path57.default.join(input.cwd, input.relPath);
59220
+ const absPath = import_node_path58.default.isAbsolute(input.relPath) ? input.relPath : import_node_path58.default.join(input.cwd, input.relPath);
58913
59221
  let size = 0;
58914
59222
  try {
58915
59223
  size = import_node_fs45.default.statSync(absPath).size;
@@ -58975,6 +59283,7 @@ async function startDaemon(config) {
58975
59283
  }
58976
59284
  });
58977
59285
  manager.attachObserver(observer);
59286
+ deliverPendingRef = (sid) => manager.deliverPending(sid);
58978
59287
  const claudeAdapter = config.mode === "tui" ? new ClaudeTuiAdapter({
58979
59288
  logger,
58980
59289
  historyReader: new ClaudeHistoryReader(),
@@ -59141,11 +59450,11 @@ async function startDaemon(config) {
59141
59450
  // 'persona/<pid>/owner',default 走 'default'。
59142
59451
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59143
59452
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
59144
- personaRoot: import_node_path57.default.join(config.dataDir, "personas"),
59453
+ personaRoot: import_node_path58.default.join(config.dataDir, "personas"),
59145
59454
  usersRoot
59146
59455
  },
59147
59456
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
59148
- personaRoot: import_node_path57.default.join(config.dataDir, "personas"),
59457
+ personaRoot: import_node_path58.default.join(config.dataDir, "personas"),
59149
59458
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
59150
59459
  usersRoot,
59151
59460
  // capability:list / delete handler 依赖
@@ -59256,11 +59565,11 @@ async function startDaemon(config) {
59256
59565
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
59257
59566
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
59258
59567
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
59259
- appBuilderPersonaRoot: import_node_path57.default.join(config.dataDir, "personas", "persona-app-builder"),
59568
+ appBuilderPersonaRoot: import_node_path58.default.join(config.dataDir, "personas", "persona-app-builder"),
59260
59569
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
59261
- deployKitRoot: import_node_path57.default.join(config.dataDir, "deploy-kit"),
59570
+ deployKitRoot: import_node_path58.default.join(config.dataDir, "deploy-kit"),
59262
59571
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
59263
- resolvePersonaRoot: (personaId) => import_node_path57.default.join(config.dataDir, "personas", personaId),
59572
+ resolvePersonaRoot: (personaId) => import_node_path58.default.join(config.dataDir, "personas", personaId),
59264
59573
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
59265
59574
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
59266
59575
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -59290,6 +59599,7 @@ async function startDaemon(config) {
59290
59599
  let handlers = makeHandlers();
59291
59600
  const dispatchHandlers = buildPersonaDispatchHandlers({
59292
59601
  personaDispatchManager,
59602
+ store: dispatchStore,
59293
59603
  logger,
59294
59604
  spawnB: async (args) => {
59295
59605
  logger.info("dispatch.spawnB.start", {
@@ -59303,7 +59613,7 @@ async function startDaemon(config) {
59303
59613
  }
59304
59614
  let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
59305
59615
  if (sourceFile && sourceFile.toolSessionId) {
59306
- sourceJsonlPath = import_node_path57.default.join(
59616
+ sourceJsonlPath = import_node_path58.default.join(
59307
59617
  import_node_os22.default.homedir(),
59308
59618
  ".claude",
59309
59619
  "projects",
@@ -59621,8 +59931,8 @@ async function startDaemon(config) {
59621
59931
  const lines = [
59622
59932
  `Tunnel: ${r.url}`,
59623
59933
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
59624
- `Frpc config: ${import_node_path57.default.join(config.dataDir, "frpc.toml")}`,
59625
- `Frpc log: ${import_node_path57.default.join(logDir, "frpc.log")}`
59934
+ `Frpc config: ${import_node_path58.default.join(config.dataDir, "frpc.toml")}`,
59935
+ `Frpc log: ${import_node_path58.default.join(logDir, "frpc.log")}`
59626
59936
  ];
59627
59937
  const width = Math.max(...lines.map((l) => l.length));
59628
59938
  const bar = "\u2550".repeat(width + 4);
@@ -59635,7 +59945,7 @@ ${bar}
59635
59945
 
59636
59946
  `);
59637
59947
  try {
59638
- const connectPath = import_node_path57.default.join(config.dataDir, "connect.txt");
59948
+ const connectPath = import_node_path58.default.join(config.dataDir, "connect.txt");
59639
59949
  import_node_fs45.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
59640
59950
  } catch {
59641
59951
  }
@@ -59708,7 +60018,7 @@ ${bar}
59708
60018
  };
59709
60019
  }
59710
60020
  function migrateDropPersonsDir(dataDir) {
59711
- const dir = import_node_path57.default.join(dataDir, "persons");
60021
+ const dir = import_node_path58.default.join(dataDir, "persons");
59712
60022
  try {
59713
60023
  import_node_fs45.default.rmSync(dir, { recursive: true, force: true });
59714
60024
  } catch {