@clawos-dev/clawd 0.2.143-beta.300.c2a3792 → 0.2.143-beta.302.5df5cc1

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.
Files changed (29) hide show
  1. package/dist/cli.cjs +1632 -824
  2. package/dist/deploy-kit/.secrets/aliyun.env.example +4 -0
  3. package/dist/deploy-kit/scripts/new-extension.sh +22 -0
  4. package/dist/deploy-kit/scripts/publish.sh +100 -0
  5. package/dist/deploy-kit/scripts/publish.spec.ts +69 -0
  6. package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/scripts/remove-extension.sh +15 -26
  7. package/dist/dispatch/mcp-server.cjs +39756 -0
  8. package/dist/persona-defaults/persona-app-builder/CLAUDE.md +16 -13
  9. package/dist/persona-defaults/persona-app-builder/extension-kit/README.md +24 -18
  10. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/ext.conf +5 -0
  11. package/dist/persona-defaults/persona-bug-fixer/CLAUDE.md +2 -0
  12. package/dist/persona-defaults/persona-dataclaw-builder/CLAUDE.md +48 -2
  13. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/README.md +28 -25
  14. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/ext.conf +5 -0
  15. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/hooks/pre-deploy.sh +25 -0
  16. package/dist/persona-defaults/persona-developer/CLAUDE.md +2 -0
  17. package/package.json +5 -2
  18. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/new-extension.sh +0 -66
  19. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/publish.sh +0 -110
  20. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/contract/bootstrap +0 -22
  21. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/ensure-toolchain.sh +0 -62
  22. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/new-extension.sh +0 -66
  23. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/publish.sh +0 -129
  24. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/remove-extension.sh +0 -70
  25. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/verify.sh +0 -38
  26. /package/dist/{persona-defaults/persona-app-builder → deploy-kit}/.secrets/aliyun.env +0 -0
  27. /package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/contract/bootstrap +0 -0
  28. /package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/scripts/ensure-toolchain.sh +0 -0
  29. /package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/scripts/verify.sh +0 -0
package/dist/cli.cjs CHANGED
@@ -720,8 +720,8 @@ var init_parseUtil = __esm({
720
720
  init_errors2();
721
721
  init_en();
722
722
  makeIssue = (params) => {
723
- const { data, path: path58, errorMaps, issueData } = params;
724
- const fullPath = [...path58, ...issueData.path || []];
723
+ const { data, path: path59, errorMaps, issueData } = params;
724
+ const fullPath = [...path59, ...issueData.path || []];
725
725
  const fullIssue = {
726
726
  ...issueData,
727
727
  path: fullPath
@@ -1032,11 +1032,11 @@ var init_types = __esm({
1032
1032
  init_parseUtil();
1033
1033
  init_util();
1034
1034
  ParseInputLazyPath = class {
1035
- constructor(parent, value, path58, key) {
1035
+ constructor(parent, value, path59, key) {
1036
1036
  this._cachedPath = [];
1037
1037
  this.parent = parent;
1038
1038
  this.data = value;
1039
- this._path = path58;
1039
+ this._path = path59;
1040
1040
  this._key = key;
1041
1041
  }
1042
1042
  get path() {
@@ -4752,6 +4752,13 @@ var init_schemas = __esm({
4752
4752
  * - native `session:*` handler 完全不感知 mirror(UI 操作 mirror 不走 native 路径)
4753
4753
  */
4754
4754
  originOwnerPrincipalId: external_exports.string().min(1).optional(),
4755
+ /**
4756
+ * Dispatch 派生的 session 在 create 时记录源 session 的 sessionId(稳定 key);
4757
+ * sidebar 据此把 dispatch 出来的 sub-session 折叠在源 session 下。非 dispatch session 缺省。
4758
+ * mirror SessionFile 上必须 strip(朋友 daemon 上的 sessionId 在本机无意义,会让本机
4759
+ * 反查路径走错;按 #750 一族防线处理,跟 ownerPersonaId 同地点 strip)。
4760
+ */
4761
+ dispatchedFromSessionId: external_exports.string().min(1).optional(),
4755
4762
  createdAt: external_exports.string().min(1),
4756
4763
  updatedAt: external_exports.string().min(1)
4757
4764
  });
@@ -5928,6 +5935,60 @@ var init_feishu_auth = __esm({
5928
5935
  }
5929
5936
  });
5930
5937
 
5938
+ // ../protocol/src/dispatch.ts
5939
+ var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema;
5940
+ var init_dispatch = __esm({
5941
+ "../protocol/src/dispatch.ts"() {
5942
+ "use strict";
5943
+ init_zod();
5944
+ DispatchOutcomeSchema = external_exports.discriminatedUnion("kind", [
5945
+ external_exports.object({
5946
+ kind: external_exports.literal("success"),
5947
+ text: external_exports.string(),
5948
+ filePaths: external_exports.array(external_exports.string()).optional()
5949
+ }),
5950
+ external_exports.object({
5951
+ kind: external_exports.literal("failure"),
5952
+ reason: external_exports.string()
5953
+ })
5954
+ ]);
5955
+ DispatchRunArgsSchema = external_exports.object({
5956
+ targetPersona: external_exports.string().min(1),
5957
+ prompt: external_exports.string()
5958
+ });
5959
+ DispatchCompleteArgsSchema = external_exports.object({
5960
+ dispatchId: external_exports.string().min(1),
5961
+ outcome: DispatchOutcomeSchema
5962
+ });
5963
+ }
5964
+ });
5965
+
5966
+ // ../protocol/src/log.ts
5967
+ var LOG_SOURCES, LogSourceSchema, LOG_LEVELS, LogLevelSchema, LogRecordSchema;
5968
+ var init_log = __esm({
5969
+ "../protocol/src/log.ts"() {
5970
+ "use strict";
5971
+ init_zod();
5972
+ LOG_SOURCES = ["daemon", "main", "renderer"];
5973
+ LogSourceSchema = external_exports.enum(LOG_SOURCES);
5974
+ LOG_LEVELS = ["debug", "info", "warn", "error"];
5975
+ LogLevelSchema = external_exports.enum(LOG_LEVELS);
5976
+ LogRecordSchema = external_exports.object({
5977
+ ts: external_exports.number().int(),
5978
+ source: LogSourceSchema,
5979
+ level: LogLevelSchema,
5980
+ msg: external_exports.string(),
5981
+ ownerPrincipalId: external_exports.string(),
5982
+ deviceId: external_exports.string(),
5983
+ appVersion: external_exports.string(),
5984
+ daemonVersion: external_exports.string().optional(),
5985
+ os: external_exports.string(),
5986
+ meta_json: external_exports.string().optional(),
5987
+ sessionId: external_exports.string().optional()
5988
+ });
5989
+ }
5990
+ });
5991
+
5931
5992
  // ../protocol/src/runtime.ts
5932
5993
  var init_runtime = __esm({
5933
5994
  "../protocol/src/runtime.ts"() {
@@ -5946,6 +6007,8 @@ var init_runtime = __esm({
5946
6007
  init_contact();
5947
6008
  init_extension();
5948
6009
  init_feishu_auth();
6010
+ init_dispatch();
6011
+ init_log();
5949
6012
  }
5950
6013
  });
5951
6014
 
@@ -6220,8 +6283,8 @@ var require_req = __commonJS({
6220
6283
  if (req.originalUrl) {
6221
6284
  _req.url = req.originalUrl;
6222
6285
  } else {
6223
- const path58 = req.path;
6224
- _req.url = typeof path58 === "string" ? path58 : req.url ? req.url.path || req.url : void 0;
6286
+ const path59 = req.path;
6287
+ _req.url = typeof path59 === "string" ? path59 : req.url ? req.url.path || req.url : void 0;
6225
6288
  }
6226
6289
  if (req.query) {
6227
6290
  _req.query = req.query;
@@ -6386,14 +6449,14 @@ var require_redact = __commonJS({
6386
6449
  }
6387
6450
  return obj;
6388
6451
  }
6389
- function parsePath(path58) {
6452
+ function parsePath(path59) {
6390
6453
  const parts = [];
6391
6454
  let current = "";
6392
6455
  let inBrackets = false;
6393
6456
  let inQuotes = false;
6394
6457
  let quoteChar = "";
6395
- for (let i = 0; i < path58.length; i++) {
6396
- const char = path58[i];
6458
+ for (let i = 0; i < path59.length; i++) {
6459
+ const char = path59[i];
6397
6460
  if (!inBrackets && char === ".") {
6398
6461
  if (current) {
6399
6462
  parts.push(current);
@@ -6524,10 +6587,10 @@ var require_redact = __commonJS({
6524
6587
  return current;
6525
6588
  }
6526
6589
  function redactPaths(obj, paths, censor, remove = false) {
6527
- for (const path58 of paths) {
6528
- const parts = parsePath(path58);
6590
+ for (const path59 of paths) {
6591
+ const parts = parsePath(path59);
6529
6592
  if (parts.includes("*")) {
6530
- redactWildcardPath(obj, parts, censor, path58, remove);
6593
+ redactWildcardPath(obj, parts, censor, path59, remove);
6531
6594
  } else {
6532
6595
  if (remove) {
6533
6596
  removeKey(obj, parts);
@@ -6612,8 +6675,8 @@ var require_redact = __commonJS({
6612
6675
  }
6613
6676
  } else {
6614
6677
  if (afterWildcard.includes("*")) {
6615
- const wrappedCensor = typeof censor === "function" ? (value, path58) => {
6616
- const fullPath = [...pathArray.slice(0, pathLength), ...path58];
6678
+ const wrappedCensor = typeof censor === "function" ? (value, path59) => {
6679
+ const fullPath = [...pathArray.slice(0, pathLength), ...path59];
6617
6680
  return censor(value, fullPath);
6618
6681
  } : censor;
6619
6682
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6648,8 +6711,8 @@ var require_redact = __commonJS({
6648
6711
  return null;
6649
6712
  }
6650
6713
  const pathStructure = /* @__PURE__ */ new Map();
6651
- for (const path58 of pathsToClone) {
6652
- const parts = parsePath(path58);
6714
+ for (const path59 of pathsToClone) {
6715
+ const parts = parsePath(path59);
6653
6716
  let current = pathStructure;
6654
6717
  for (let i = 0; i < parts.length; i++) {
6655
6718
  const part = parts[i];
@@ -6701,24 +6764,24 @@ var require_redact = __commonJS({
6701
6764
  }
6702
6765
  return cloneSelectively(obj, pathStructure);
6703
6766
  }
6704
- function validatePath(path58) {
6705
- if (typeof path58 !== "string") {
6767
+ function validatePath(path59) {
6768
+ if (typeof path59 !== "string") {
6706
6769
  throw new Error("Paths must be (non-empty) strings");
6707
6770
  }
6708
- if (path58 === "") {
6771
+ if (path59 === "") {
6709
6772
  throw new Error("Invalid redaction path ()");
6710
6773
  }
6711
- if (path58.includes("..")) {
6712
- throw new Error(`Invalid redaction path (${path58})`);
6774
+ if (path59.includes("..")) {
6775
+ throw new Error(`Invalid redaction path (${path59})`);
6713
6776
  }
6714
- if (path58.includes(",")) {
6715
- throw new Error(`Invalid redaction path (${path58})`);
6777
+ if (path59.includes(",")) {
6778
+ throw new Error(`Invalid redaction path (${path59})`);
6716
6779
  }
6717
6780
  let bracketCount = 0;
6718
6781
  let inQuotes = false;
6719
6782
  let quoteChar = "";
6720
- for (let i = 0; i < path58.length; i++) {
6721
- const char = path58[i];
6783
+ for (let i = 0; i < path59.length; i++) {
6784
+ const char = path59[i];
6722
6785
  if ((char === '"' || char === "'") && bracketCount > 0) {
6723
6786
  if (!inQuotes) {
6724
6787
  inQuotes = true;
@@ -6732,20 +6795,20 @@ var require_redact = __commonJS({
6732
6795
  } else if (char === "]" && !inQuotes) {
6733
6796
  bracketCount--;
6734
6797
  if (bracketCount < 0) {
6735
- throw new Error(`Invalid redaction path (${path58})`);
6798
+ throw new Error(`Invalid redaction path (${path59})`);
6736
6799
  }
6737
6800
  }
6738
6801
  }
6739
6802
  if (bracketCount !== 0) {
6740
- throw new Error(`Invalid redaction path (${path58})`);
6803
+ throw new Error(`Invalid redaction path (${path59})`);
6741
6804
  }
6742
6805
  }
6743
6806
  function validatePaths(paths) {
6744
6807
  if (!Array.isArray(paths)) {
6745
6808
  throw new TypeError("paths must be an array");
6746
6809
  }
6747
- for (const path58 of paths) {
6748
- validatePath(path58);
6810
+ for (const path59 of paths) {
6811
+ validatePath(path59);
6749
6812
  }
6750
6813
  }
6751
6814
  function slowRedact(options = {}) {
@@ -6913,8 +6976,8 @@ var require_redaction = __commonJS({
6913
6976
  if (shape[k2] === null) {
6914
6977
  o[k2] = (value) => topCensor(value, [k2]);
6915
6978
  } else {
6916
- const wrappedCensor = typeof censor === "function" ? (value, path58) => {
6917
- return censor(value, [k2, ...path58]);
6979
+ const wrappedCensor = typeof censor === "function" ? (value, path59) => {
6980
+ return censor(value, [k2, ...path59]);
6918
6981
  } : censor;
6919
6982
  o[k2] = Redact({
6920
6983
  paths: shape[k2],
@@ -7098,7 +7161,7 @@ var require_atomic_sleep = __commonJS({
7098
7161
  "../node_modules/.pnpm/atomic-sleep@1.0.0/node_modules/atomic-sleep/index.js"(exports2, module2) {
7099
7162
  "use strict";
7100
7163
  if (typeof SharedArrayBuffer !== "undefined" && typeof Atomics !== "undefined") {
7101
- let sleep = function(ms) {
7164
+ let sleep2 = function(ms) {
7102
7165
  const valid = ms > 0 && ms < Infinity;
7103
7166
  if (valid === false) {
7104
7167
  if (typeof ms !== "number" && typeof ms !== "bigint") {
@@ -7109,9 +7172,9 @@ var require_atomic_sleep = __commonJS({
7109
7172
  Atomics.wait(nil, 0, 0, Number(ms));
7110
7173
  };
7111
7174
  const nil = new Int32Array(new SharedArrayBuffer(4));
7112
- module2.exports = sleep;
7175
+ module2.exports = sleep2;
7113
7176
  } else {
7114
- let sleep = function(ms) {
7177
+ let sleep2 = function(ms) {
7115
7178
  const valid = ms > 0 && ms < Infinity;
7116
7179
  if (valid === false) {
7117
7180
  if (typeof ms !== "number" && typeof ms !== "bigint") {
@@ -7123,7 +7186,7 @@ var require_atomic_sleep = __commonJS({
7123
7186
  while (target > Date.now()) {
7124
7187
  }
7125
7188
  };
7126
- module2.exports = sleep;
7189
+ module2.exports = sleep2;
7127
7190
  }
7128
7191
  }
7129
7192
  });
@@ -7132,11 +7195,11 @@ var require_atomic_sleep = __commonJS({
7132
7195
  var require_sonic_boom = __commonJS({
7133
7196
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
7134
7197
  "use strict";
7135
- var fs51 = require("fs");
7198
+ var fs52 = require("fs");
7136
7199
  var EventEmitter3 = require("events");
7137
7200
  var inherits = require("util").inherits;
7138
- var path58 = require("path");
7139
- var sleep = require_atomic_sleep();
7201
+ var path59 = require("path");
7202
+ var sleep2 = require_atomic_sleep();
7140
7203
  var assert = require("assert");
7141
7204
  var BUSY_WRITE_TIMEOUT = 100;
7142
7205
  var kEmptyBuffer = Buffer.allocUnsafe(0);
@@ -7189,20 +7252,20 @@ var require_sonic_boom = __commonJS({
7189
7252
  const mode = sonic.mode;
7190
7253
  if (sonic.sync) {
7191
7254
  try {
7192
- if (sonic.mkdir) fs51.mkdirSync(path58.dirname(file), { recursive: true });
7193
- const fd = fs51.openSync(file, flags, mode);
7255
+ if (sonic.mkdir) fs52.mkdirSync(path59.dirname(file), { recursive: true });
7256
+ const fd = fs52.openSync(file, flags, mode);
7194
7257
  fileOpened(null, fd);
7195
7258
  } catch (err) {
7196
7259
  fileOpened(err);
7197
7260
  throw err;
7198
7261
  }
7199
7262
  } else if (sonic.mkdir) {
7200
- fs51.mkdir(path58.dirname(file), { recursive: true }, (err) => {
7263
+ fs52.mkdir(path59.dirname(file), { recursive: true }, (err) => {
7201
7264
  if (err) return fileOpened(err);
7202
- fs51.open(file, flags, mode, fileOpened);
7265
+ fs52.open(file, flags, mode, fileOpened);
7203
7266
  });
7204
7267
  } else {
7205
- fs51.open(file, flags, mode, fileOpened);
7268
+ fs52.open(file, flags, mode, fileOpened);
7206
7269
  }
7207
7270
  }
7208
7271
  function SonicBoom(opts) {
@@ -7243,8 +7306,8 @@ var require_sonic_boom = __commonJS({
7243
7306
  this.flush = flushBuffer;
7244
7307
  this.flushSync = flushBufferSync;
7245
7308
  this._actualWrite = actualWriteBuffer;
7246
- fsWriteSync = () => fs51.writeSync(this.fd, this._writingBuf);
7247
- fsWrite = () => fs51.write(this.fd, this._writingBuf, this.release);
7309
+ fsWriteSync = () => fs52.writeSync(this.fd, this._writingBuf);
7310
+ fsWrite = () => fs52.write(this.fd, this._writingBuf, this.release);
7248
7311
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
7249
7312
  this._writingBuf = "";
7250
7313
  this.write = write;
@@ -7253,15 +7316,15 @@ var require_sonic_boom = __commonJS({
7253
7316
  this._actualWrite = actualWrite;
7254
7317
  fsWriteSync = () => {
7255
7318
  if (Buffer.isBuffer(this._writingBuf)) {
7256
- return fs51.writeSync(this.fd, this._writingBuf);
7319
+ return fs52.writeSync(this.fd, this._writingBuf);
7257
7320
  }
7258
- return fs51.writeSync(this.fd, this._writingBuf, "utf8");
7321
+ return fs52.writeSync(this.fd, this._writingBuf, "utf8");
7259
7322
  };
7260
7323
  fsWrite = () => {
7261
7324
  if (Buffer.isBuffer(this._writingBuf)) {
7262
- return fs51.write(this.fd, this._writingBuf, this.release);
7325
+ return fs52.write(this.fd, this._writingBuf, this.release);
7263
7326
  }
7264
- return fs51.write(this.fd, this._writingBuf, "utf8", this.release);
7327
+ return fs52.write(this.fd, this._writingBuf, "utf8", this.release);
7265
7328
  };
7266
7329
  } else {
7267
7330
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -7282,7 +7345,7 @@ var require_sonic_boom = __commonJS({
7282
7345
  if ((err.code === "EAGAIN" || err.code === "EBUSY") && this.retryEAGAIN(err, this._writingBuf.length, this._len - this._writingBuf.length)) {
7283
7346
  if (this.sync) {
7284
7347
  try {
7285
- sleep(BUSY_WRITE_TIMEOUT);
7348
+ sleep2(BUSY_WRITE_TIMEOUT);
7286
7349
  this.release(void 0, 0);
7287
7350
  } catch (err2) {
7288
7351
  this.release(err2);
@@ -7318,7 +7381,7 @@ var require_sonic_boom = __commonJS({
7318
7381
  }
7319
7382
  }
7320
7383
  if (this._fsync) {
7321
- fs51.fsyncSync(this.fd);
7384
+ fs52.fsyncSync(this.fd);
7322
7385
  }
7323
7386
  const len = this._len;
7324
7387
  if (this._reopening) {
@@ -7432,7 +7495,7 @@ var require_sonic_boom = __commonJS({
7432
7495
  const onDrain = () => {
7433
7496
  if (!this._fsync) {
7434
7497
  try {
7435
- fs51.fsync(this.fd, (err) => {
7498
+ fs52.fsync(this.fd, (err) => {
7436
7499
  this._flushPending = false;
7437
7500
  cb(err);
7438
7501
  });
@@ -7534,7 +7597,7 @@ var require_sonic_boom = __commonJS({
7534
7597
  const fd = this.fd;
7535
7598
  this.once("ready", () => {
7536
7599
  if (fd !== this.fd) {
7537
- fs51.close(fd, (err) => {
7600
+ fs52.close(fd, (err) => {
7538
7601
  if (err) {
7539
7602
  return this.emit("error", err);
7540
7603
  }
@@ -7583,7 +7646,7 @@ var require_sonic_boom = __commonJS({
7583
7646
  buf = this._bufs[0];
7584
7647
  }
7585
7648
  try {
7586
- const n = Buffer.isBuffer(buf) ? fs51.writeSync(this.fd, buf) : fs51.writeSync(this.fd, buf, "utf8");
7649
+ const n = Buffer.isBuffer(buf) ? fs52.writeSync(this.fd, buf) : fs52.writeSync(this.fd, buf, "utf8");
7587
7650
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
7588
7651
  buf = releasedBufObj.writingBuf;
7589
7652
  this._len = releasedBufObj.len;
@@ -7595,11 +7658,11 @@ var require_sonic_boom = __commonJS({
7595
7658
  if (shouldRetry && !this.retryEAGAIN(err, buf.length, this._len - buf.length)) {
7596
7659
  throw err;
7597
7660
  }
7598
- sleep(BUSY_WRITE_TIMEOUT);
7661
+ sleep2(BUSY_WRITE_TIMEOUT);
7599
7662
  }
7600
7663
  }
7601
7664
  try {
7602
- fs51.fsyncSync(this.fd);
7665
+ fs52.fsyncSync(this.fd);
7603
7666
  } catch {
7604
7667
  }
7605
7668
  }
@@ -7620,7 +7683,7 @@ var require_sonic_boom = __commonJS({
7620
7683
  buf = mergeBuf(this._bufs[0], this._lens[0]);
7621
7684
  }
7622
7685
  try {
7623
- const n = fs51.writeSync(this.fd, buf);
7686
+ const n = fs52.writeSync(this.fd, buf);
7624
7687
  buf = buf.subarray(n);
7625
7688
  this._len = Math.max(this._len - n, 0);
7626
7689
  if (buf.length <= 0) {
@@ -7632,7 +7695,7 @@ var require_sonic_boom = __commonJS({
7632
7695
  if (shouldRetry && !this.retryEAGAIN(err, buf.length, this._len - buf.length)) {
7633
7696
  throw err;
7634
7697
  }
7635
- sleep(BUSY_WRITE_TIMEOUT);
7698
+ sleep2(BUSY_WRITE_TIMEOUT);
7636
7699
  }
7637
7700
  }
7638
7701
  }
@@ -7648,13 +7711,13 @@ var require_sonic_boom = __commonJS({
7648
7711
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
7649
7712
  if (this.sync) {
7650
7713
  try {
7651
- const written = Buffer.isBuffer(this._writingBuf) ? fs51.writeSync(this.fd, this._writingBuf) : fs51.writeSync(this.fd, this._writingBuf, "utf8");
7714
+ const written = Buffer.isBuffer(this._writingBuf) ? fs52.writeSync(this.fd, this._writingBuf) : fs52.writeSync(this.fd, this._writingBuf, "utf8");
7652
7715
  release(null, written);
7653
7716
  } catch (err) {
7654
7717
  release(err);
7655
7718
  }
7656
7719
  } else {
7657
- fs51.write(this.fd, this._writingBuf, release);
7720
+ fs52.write(this.fd, this._writingBuf, release);
7658
7721
  }
7659
7722
  }
7660
7723
  function actualWriteBuffer() {
@@ -7663,7 +7726,7 @@ var require_sonic_boom = __commonJS({
7663
7726
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
7664
7727
  if (this.sync) {
7665
7728
  try {
7666
- const written = fs51.writeSync(this.fd, this._writingBuf);
7729
+ const written = fs52.writeSync(this.fd, this._writingBuf);
7667
7730
  release(null, written);
7668
7731
  } catch (err) {
7669
7732
  release(err);
@@ -7672,7 +7735,7 @@ var require_sonic_boom = __commonJS({
7672
7735
  if (kCopyBuffer) {
7673
7736
  this._writingBuf = Buffer.from(this._writingBuf);
7674
7737
  }
7675
- fs51.write(this.fd, this._writingBuf, release);
7738
+ fs52.write(this.fd, this._writingBuf, release);
7676
7739
  }
7677
7740
  }
7678
7741
  function actualClose(sonic) {
@@ -7688,12 +7751,12 @@ var require_sonic_boom = __commonJS({
7688
7751
  sonic._lens = [];
7689
7752
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
7690
7753
  try {
7691
- fs51.fsync(sonic.fd, closeWrapped);
7754
+ fs52.fsync(sonic.fd, closeWrapped);
7692
7755
  } catch {
7693
7756
  }
7694
7757
  function closeWrapped() {
7695
7758
  if (sonic.fd !== 1 && sonic.fd !== 2) {
7696
- fs51.close(sonic.fd, done);
7759
+ fs52.close(sonic.fd, done);
7697
7760
  } else {
7698
7761
  done();
7699
7762
  }
@@ -8373,7 +8436,7 @@ var require_transport = __commonJS({
8373
8436
  var { createRequire } = require("module");
8374
8437
  var getCallers = require_caller();
8375
8438
  var { join: join15, isAbsolute: isAbsolute2, sep: sep2 } = require("path");
8376
- var sleep = require_atomic_sleep();
8439
+ var sleep2 = require_atomic_sleep();
8377
8440
  var onExit = require_on_exit_leak_free();
8378
8441
  var ThreadStream = require_thread_stream();
8379
8442
  function setupOnExit(stream) {
@@ -8407,7 +8470,7 @@ var require_transport = __commonJS({
8407
8470
  return;
8408
8471
  }
8409
8472
  stream.flushSync();
8410
- sleep(100);
8473
+ sleep2(100);
8411
8474
  stream.end();
8412
8475
  }
8413
8476
  return stream;
@@ -10057,7 +10120,7 @@ var require_multistream = __commonJS({
10057
10120
  var require_pino = __commonJS({
10058
10121
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
10059
10122
  "use strict";
10060
- var os21 = require("os");
10123
+ var os22 = require("os");
10061
10124
  var stdSerializers = require_pino_std_serializers();
10062
10125
  var caller = require_caller();
10063
10126
  var redaction = require_redaction();
@@ -10104,7 +10167,7 @@ var require_pino = __commonJS({
10104
10167
  } = symbols;
10105
10168
  var { epochTime, nullTime } = time;
10106
10169
  var { pid } = process;
10107
- var hostname = os21.hostname();
10170
+ var hostname = os22.hostname();
10108
10171
  var defaultErrorSerializer = stdSerializers.err;
10109
10172
  var defaultOptions = {
10110
10173
  level: "info",
@@ -10828,11 +10891,11 @@ var init_lib = __esm({
10828
10891
  }
10829
10892
  }
10830
10893
  },
10831
- addToPath: function addToPath(path58, added, removed, oldPosInc, options) {
10832
- var last = path58.lastComponent;
10894
+ addToPath: function addToPath(path59, added, removed, oldPosInc, options) {
10895
+ var last = path59.lastComponent;
10833
10896
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10834
10897
  return {
10835
- oldPos: path58.oldPos + oldPosInc,
10898
+ oldPos: path59.oldPos + oldPosInc,
10836
10899
  lastComponent: {
10837
10900
  count: last.count + 1,
10838
10901
  added,
@@ -10842,7 +10905,7 @@ var init_lib = __esm({
10842
10905
  };
10843
10906
  } else {
10844
10907
  return {
10845
- oldPos: path58.oldPos + oldPosInc,
10908
+ oldPos: path59.oldPos + oldPosInc,
10846
10909
  lastComponent: {
10847
10910
  count: 1,
10848
10911
  added,
@@ -11288,10 +11351,10 @@ function attachmentToHistoryMessage(o, ts) {
11288
11351
  const memories = raw.map((m2) => {
11289
11352
  if (!m2 || typeof m2 !== "object") return null;
11290
11353
  const rec3 = m2;
11291
- const path58 = typeof rec3.path === "string" ? rec3.path : null;
11354
+ const path59 = typeof rec3.path === "string" ? rec3.path : null;
11292
11355
  const content = typeof rec3.content === "string" ? rec3.content : null;
11293
- if (!path58 || content == null) return null;
11294
- const entry = { path: path58, content };
11356
+ if (!path59 || content == null) return null;
11357
+ const entry = { path: path59, content };
11295
11358
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
11296
11359
  return entry;
11297
11360
  }).filter((m2) => m2 !== null);
@@ -11811,6 +11874,9 @@ function buildSpawnArgs(ctx) {
11811
11874
  if (ctx.addDirs) {
11812
11875
  for (const dir of ctx.addDirs) args.push("--add-dir", dir);
11813
11876
  }
11877
+ if (ctx.dispatchMcpConfigPath) {
11878
+ args.push("--mcp-config", ctx.dispatchMcpConfigPath);
11879
+ }
11814
11880
  if (ctx.effort) args.push("--effort", ctx.effort);
11815
11881
  if (ctx.toolSessionId) args.push("--resume", ctx.toolSessionId);
11816
11882
  return args;
@@ -12090,10 +12156,10 @@ function parseAttachment(obj) {
12090
12156
  const memories = raw.map((m2) => {
12091
12157
  if (!m2 || typeof m2 !== "object") return null;
12092
12158
  const rec3 = m2;
12093
- const path58 = typeof rec3.path === "string" ? rec3.path : null;
12159
+ const path59 = typeof rec3.path === "string" ? rec3.path : null;
12094
12160
  const content = typeof rec3.content === "string" ? rec3.content : null;
12095
- if (!path58 || content == null) return null;
12096
- const out = { path: path58, content };
12161
+ if (!path59 || content == null) return null;
12162
+ const out = { path: path59, content };
12097
12163
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
12098
12164
  return out;
12099
12165
  }).filter((m2) => m2 !== null);
@@ -18123,7 +18189,7 @@ var require_validation = __commonJS({
18123
18189
  var require_receiver = __commonJS({
18124
18190
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/receiver.js"(exports2, module2) {
18125
18191
  "use strict";
18126
- var { Writable: Writable2 } = require("stream");
18192
+ var { Writable: Writable3 } = require("stream");
18127
18193
  var PerMessageDeflate2 = require_permessage_deflate();
18128
18194
  var {
18129
18195
  BINARY_TYPES,
@@ -18141,7 +18207,7 @@ var require_receiver = __commonJS({
18141
18207
  var GET_DATA = 4;
18142
18208
  var INFLATING = 5;
18143
18209
  var DEFER_EVENT = 6;
18144
- var Receiver2 = class extends Writable2 {
18210
+ var Receiver2 = class extends Writable3 {
18145
18211
  /**
18146
18212
  * Creates a Receiver instance.
18147
18213
  *
@@ -21404,7 +21470,7 @@ var require_stream_writable = __commonJS({
21404
21470
  "../node_modules/.pnpm/readable-stream@2.3.8/node_modules/readable-stream/lib/_stream_writable.js"(exports2, module2) {
21405
21471
  "use strict";
21406
21472
  var pna = require_process_nextick_args();
21407
- module2.exports = Writable2;
21473
+ module2.exports = Writable3;
21408
21474
  function CorkedRequest(state) {
21409
21475
  var _this = this;
21410
21476
  this.next = null;
@@ -21415,7 +21481,7 @@ var require_stream_writable = __commonJS({
21415
21481
  }
21416
21482
  var asyncWrite = !process.browser && ["v0.10", "v0.9."].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick;
21417
21483
  var Duplex;
21418
- Writable2.WritableState = WritableState;
21484
+ Writable3.WritableState = WritableState;
21419
21485
  var util2 = Object.create(require_util());
21420
21486
  util2.inherits = require_inherits();
21421
21487
  var internalUtil = {
@@ -21432,7 +21498,7 @@ var require_stream_writable = __commonJS({
21432
21498
  return Buffer2.isBuffer(obj) || obj instanceof OurUint8Array;
21433
21499
  }
21434
21500
  var destroyImpl = require_destroy();
21435
- util2.inherits(Writable2, Stream);
21501
+ util2.inherits(Writable3, Stream);
21436
21502
  function nop() {
21437
21503
  }
21438
21504
  function WritableState(options, stream) {
@@ -21497,10 +21563,10 @@ var require_stream_writable = __commonJS({
21497
21563
  var realHasInstance;
21498
21564
  if (typeof Symbol === "function" && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === "function") {
21499
21565
  realHasInstance = Function.prototype[Symbol.hasInstance];
21500
- Object.defineProperty(Writable2, Symbol.hasInstance, {
21566
+ Object.defineProperty(Writable3, Symbol.hasInstance, {
21501
21567
  value: function(object) {
21502
21568
  if (realHasInstance.call(this, object)) return true;
21503
- if (this !== Writable2) return false;
21569
+ if (this !== Writable3) return false;
21504
21570
  return object && object._writableState instanceof WritableState;
21505
21571
  }
21506
21572
  });
@@ -21509,10 +21575,10 @@ var require_stream_writable = __commonJS({
21509
21575
  return object instanceof this;
21510
21576
  };
21511
21577
  }
21512
- function Writable2(options) {
21578
+ function Writable3(options) {
21513
21579
  Duplex = Duplex || require_stream_duplex();
21514
- if (!realHasInstance.call(Writable2, this) && !(this instanceof Duplex)) {
21515
- return new Writable2(options);
21580
+ if (!realHasInstance.call(Writable3, this) && !(this instanceof Duplex)) {
21581
+ return new Writable3(options);
21516
21582
  }
21517
21583
  this._writableState = new WritableState(options, this);
21518
21584
  this.writable = true;
@@ -21524,7 +21590,7 @@ var require_stream_writable = __commonJS({
21524
21590
  }
21525
21591
  Stream.call(this);
21526
21592
  }
21527
- Writable2.prototype.pipe = function() {
21593
+ Writable3.prototype.pipe = function() {
21528
21594
  this.emit("error", new Error("Cannot pipe, not readable"));
21529
21595
  };
21530
21596
  function writeAfterEnd(stream, cb) {
@@ -21547,7 +21613,7 @@ var require_stream_writable = __commonJS({
21547
21613
  }
21548
21614
  return valid;
21549
21615
  }
21550
- Writable2.prototype.write = function(chunk, encoding, cb) {
21616
+ Writable3.prototype.write = function(chunk, encoding, cb) {
21551
21617
  var state = this._writableState;
21552
21618
  var ret = false;
21553
21619
  var isBuf = !state.objectMode && _isUint8Array(chunk);
@@ -21568,18 +21634,18 @@ var require_stream_writable = __commonJS({
21568
21634
  }
21569
21635
  return ret;
21570
21636
  };
21571
- Writable2.prototype.cork = function() {
21637
+ Writable3.prototype.cork = function() {
21572
21638
  var state = this._writableState;
21573
21639
  state.corked++;
21574
21640
  };
21575
- Writable2.prototype.uncork = function() {
21641
+ Writable3.prototype.uncork = function() {
21576
21642
  var state = this._writableState;
21577
21643
  if (state.corked) {
21578
21644
  state.corked--;
21579
21645
  if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
21580
21646
  }
21581
21647
  };
21582
- Writable2.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
21648
+ Writable3.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
21583
21649
  if (typeof encoding === "string") encoding = encoding.toLowerCase();
21584
21650
  if (!(["hex", "utf8", "utf-8", "ascii", "binary", "base64", "ucs2", "ucs-2", "utf16le", "utf-16le", "raw"].indexOf((encoding + "").toLowerCase()) > -1)) throw new TypeError("Unknown encoding: " + encoding);
21585
21651
  this._writableState.defaultEncoding = encoding;
@@ -21591,7 +21657,7 @@ var require_stream_writable = __commonJS({
21591
21657
  }
21592
21658
  return chunk;
21593
21659
  }
21594
- Object.defineProperty(Writable2.prototype, "writableHighWaterMark", {
21660
+ Object.defineProperty(Writable3.prototype, "writableHighWaterMark", {
21595
21661
  // making it explicit this property is not enumerable
21596
21662
  // because otherwise some prototype manipulation in
21597
21663
  // userland will fail
@@ -21737,11 +21803,11 @@ var require_stream_writable = __commonJS({
21737
21803
  state.bufferedRequest = entry;
21738
21804
  state.bufferProcessing = false;
21739
21805
  }
21740
- Writable2.prototype._write = function(chunk, encoding, cb) {
21806
+ Writable3.prototype._write = function(chunk, encoding, cb) {
21741
21807
  cb(new Error("_write() is not implemented"));
21742
21808
  };
21743
- Writable2.prototype._writev = null;
21744
- Writable2.prototype.end = function(chunk, encoding, cb) {
21809
+ Writable3.prototype._writev = null;
21810
+ Writable3.prototype.end = function(chunk, encoding, cb) {
21745
21811
  var state = this._writableState;
21746
21812
  if (typeof chunk === "function") {
21747
21813
  cb = chunk;
@@ -21816,7 +21882,7 @@ var require_stream_writable = __commonJS({
21816
21882
  }
21817
21883
  state.corkedRequestsFree.next = corkReq;
21818
21884
  }
21819
- Object.defineProperty(Writable2.prototype, "destroyed", {
21885
+ Object.defineProperty(Writable3.prototype, "destroyed", {
21820
21886
  get: function() {
21821
21887
  if (this._writableState === void 0) {
21822
21888
  return false;
@@ -21830,9 +21896,9 @@ var require_stream_writable = __commonJS({
21830
21896
  this._writableState.destroyed = value;
21831
21897
  }
21832
21898
  });
21833
- Writable2.prototype.destroy = destroyImpl.destroy;
21834
- Writable2.prototype._undestroy = destroyImpl.undestroy;
21835
- Writable2.prototype._destroy = function(err, cb) {
21899
+ Writable3.prototype.destroy = destroyImpl.destroy;
21900
+ Writable3.prototype._undestroy = destroyImpl.undestroy;
21901
+ Writable3.prototype._destroy = function(err, cb) {
21836
21902
  this.end();
21837
21903
  cb(err);
21838
21904
  };
@@ -21855,13 +21921,13 @@ var require_stream_duplex = __commonJS({
21855
21921
  var util2 = Object.create(require_util());
21856
21922
  util2.inherits = require_inherits();
21857
21923
  var Readable3 = require_stream_readable();
21858
- var Writable2 = require_stream_writable();
21924
+ var Writable3 = require_stream_writable();
21859
21925
  util2.inherits(Duplex, Readable3);
21860
21926
  {
21861
- keys = objectKeys(Writable2.prototype);
21927
+ keys = objectKeys(Writable3.prototype);
21862
21928
  for (v2 = 0; v2 < keys.length; v2++) {
21863
21929
  method = keys[v2];
21864
- if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable2.prototype[method];
21930
+ if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable3.prototype[method];
21865
21931
  }
21866
21932
  }
21867
21933
  var keys;
@@ -21870,7 +21936,7 @@ var require_stream_duplex = __commonJS({
21870
21936
  function Duplex(options) {
21871
21937
  if (!(this instanceof Duplex)) return new Duplex(options);
21872
21938
  Readable3.call(this, options);
21873
- Writable2.call(this, options);
21939
+ Writable3.call(this, options);
21874
21940
  if (options && options.readable === false) this.readable = false;
21875
21941
  if (options && options.writable === false) this.writable = false;
21876
21942
  this.allowHalfOpen = true;
@@ -23880,8 +23946,8 @@ var require_utils = __commonJS({
23880
23946
  var result = transform[inputType][outputType](input);
23881
23947
  return result;
23882
23948
  };
23883
- exports2.resolve = function(path58) {
23884
- var parts = path58.split("/");
23949
+ exports2.resolve = function(path59) {
23950
+ var parts = path59.split("/");
23885
23951
  var result = [];
23886
23952
  for (var index = 0; index < parts.length; index++) {
23887
23953
  var part = parts[index];
@@ -29734,18 +29800,18 @@ var require_object = __commonJS({
29734
29800
  var object = new ZipObject(name, zipObjectContent, o);
29735
29801
  this.files[name] = object;
29736
29802
  };
29737
- var parentFolder = function(path58) {
29738
- if (path58.slice(-1) === "/") {
29739
- path58 = path58.substring(0, path58.length - 1);
29803
+ var parentFolder = function(path59) {
29804
+ if (path59.slice(-1) === "/") {
29805
+ path59 = path59.substring(0, path59.length - 1);
29740
29806
  }
29741
- var lastSlash = path58.lastIndexOf("/");
29742
- return lastSlash > 0 ? path58.substring(0, lastSlash) : "";
29807
+ var lastSlash = path59.lastIndexOf("/");
29808
+ return lastSlash > 0 ? path59.substring(0, lastSlash) : "";
29743
29809
  };
29744
- var forceTrailingSlash = function(path58) {
29745
- if (path58.slice(-1) !== "/") {
29746
- path58 += "/";
29810
+ var forceTrailingSlash = function(path59) {
29811
+ if (path59.slice(-1) !== "/") {
29812
+ path59 += "/";
29747
29813
  }
29748
- return path58;
29814
+ return path59;
29749
29815
  };
29750
29816
  var folderAdd = function(name, createFolders) {
29751
29817
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -30747,7 +30813,7 @@ var require_lib3 = __commonJS({
30747
30813
  // src/run-case/recorder.ts
30748
30814
  function startRunCaseRecorder(opts) {
30749
30815
  const now = opts.now ?? Date.now;
30750
- const dir = import_node_path46.default.dirname(opts.recordPath);
30816
+ const dir = import_node_path47.default.dirname(opts.recordPath);
30751
30817
  let stream = null;
30752
30818
  let closing = false;
30753
30819
  let closedSettled = false;
@@ -30761,8 +30827,8 @@ function startRunCaseRecorder(opts) {
30761
30827
  });
30762
30828
  const ensureStream = () => {
30763
30829
  if (stream) return stream;
30764
- import_node_fs34.default.mkdirSync(dir, { recursive: true });
30765
- stream = import_node_fs34.default.createWriteStream(opts.recordPath, { flags: "a" });
30830
+ import_node_fs35.default.mkdirSync(dir, { recursive: true });
30831
+ stream = import_node_fs35.default.createWriteStream(opts.recordPath, { flags: "a" });
30766
30832
  stream.on("close", () => closedResolve());
30767
30833
  return stream;
30768
30834
  };
@@ -30787,12 +30853,12 @@ function startRunCaseRecorder(opts) {
30787
30853
  };
30788
30854
  return { tap, close, closed };
30789
30855
  }
30790
- var import_node_fs34, import_node_path46;
30856
+ var import_node_fs35, import_node_path47;
30791
30857
  var init_recorder = __esm({
30792
30858
  "src/run-case/recorder.ts"() {
30793
30859
  "use strict";
30794
- import_node_fs34 = __toESM(require("fs"), 1);
30795
- import_node_path46 = __toESM(require("path"), 1);
30860
+ import_node_fs35 = __toESM(require("fs"), 1);
30861
+ import_node_path47 = __toESM(require("path"), 1);
30796
30862
  }
30797
30863
  });
30798
30864
 
@@ -30835,7 +30901,7 @@ var init_wire = __esm({
30835
30901
  // src/run-case/controller.ts
30836
30902
  async function runController(opts) {
30837
30903
  const now = opts.now ?? Date.now;
30838
- const cwd = opts.cwd ?? (0, import_node_fs35.mkdtempSync)(import_node_path47.default.join(import_node_os19.default.tmpdir(), "clawd-runcase-"));
30904
+ const cwd = opts.cwd ?? (0, import_node_fs36.mkdtempSync)(import_node_path48.default.join(import_node_os20.default.tmpdir(), "clawd-runcase-"));
30839
30905
  const ownsCwd = opts.cwd === void 0;
30840
30906
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
30841
30907
  const spawnCtx = { cwd };
@@ -30996,19 +31062,19 @@ async function runController(opts) {
30996
31062
  if (sigintHandler) process.off("SIGINT", sigintHandler);
30997
31063
  if (ownsCwd) {
30998
31064
  try {
30999
- (0, import_node_fs35.rmSync)(cwd, { recursive: true, force: true });
31065
+ (0, import_node_fs36.rmSync)(cwd, { recursive: true, force: true });
31000
31066
  } catch {
31001
31067
  }
31002
31068
  }
31003
31069
  return exitCode ?? 0;
31004
31070
  }
31005
- var import_node_fs35, import_node_os19, import_node_path47;
31071
+ var import_node_fs36, import_node_os20, import_node_path48;
31006
31072
  var init_controller = __esm({
31007
31073
  "src/run-case/controller.ts"() {
31008
31074
  "use strict";
31009
- import_node_fs35 = require("fs");
31010
- import_node_os19 = __toESM(require("os"), 1);
31011
- import_node_path47 = __toESM(require("path"), 1);
31075
+ import_node_fs36 = require("fs");
31076
+ import_node_os20 = __toESM(require("os"), 1);
31077
+ import_node_path48 = __toESM(require("path"), 1);
31012
31078
  init_claude();
31013
31079
  init_stdout_splitter();
31014
31080
  init_permission_stdio();
@@ -31112,6 +31178,24 @@ init_protocol();
31112
31178
  var DEFAULT_PORT = 18790;
31113
31179
  var DEFAULT_HOST = "127.0.0.1";
31114
31180
  var DEFAULT_CLAWOS_API = "https://api.clawos.chat";
31181
+ var DEFAULT_LOG_ENDPOINT = "https://clawd-prod.cn-hangzhou.log.aliyuncs.com/logstores/app-logs/track";
31182
+ function resolveLogShipping(raw, cliNoShipping) {
31183
+ if (cliNoShipping) {
31184
+ return { mode: "off", endpoint: null, sampling: {} };
31185
+ }
31186
+ const rawMode = raw?.mode;
31187
+ const mode = rawMode === "off" || rawMode === "errors-only" ? rawMode : "on";
31188
+ if (mode === "off") {
31189
+ return { mode, endpoint: null, sampling: {} };
31190
+ }
31191
+ const endpoint = typeof raw?.endpoint === "string" && raw.endpoint ? raw.endpoint : raw?.endpoint === null ? null : DEFAULT_LOG_ENDPOINT;
31192
+ const sampling = mode === "errors-only" ? { debug: 0, info: 0, warn: 1, error: 1 } : isPlainSamplingMap(raw?.sampling) ? raw.sampling : {};
31193
+ return { mode, endpoint, sampling };
31194
+ }
31195
+ function isPlainSamplingMap(v2) {
31196
+ if (!v2 || typeof v2 !== "object") return false;
31197
+ return Object.values(v2).every((n) => typeof n === "number");
31198
+ }
31115
31199
  function parseArgs(argv) {
31116
31200
  const out = {};
31117
31201
  for (let i = 0; i < argv.length; i++) {
@@ -31157,6 +31241,9 @@ function parseArgs(argv) {
31157
31241
  case "-v":
31158
31242
  out.version = true;
31159
31243
  break;
31244
+ case "--no-log-shipping":
31245
+ out.noLogShipping = true;
31246
+ break;
31160
31247
  default:
31161
31248
  if (a.startsWith("--")) throw new Error(`unknown flag: ${a}`);
31162
31249
  break;
@@ -31202,6 +31289,10 @@ function resolveConfig(opts) {
31202
31289
  const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
31203
31290
  const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
31204
31291
  const previewPorts = env.CLAWD_PREVIEW_PORTS ? env.CLAWD_PREVIEW_PORTS.split(",").map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0) : filePreviewPorts && filePreviewPorts.length > 0 ? filePreviewPorts : null;
31292
+ const logShipping = resolveLogShipping(
31293
+ fileCfg.logShipping,
31294
+ args.noLogShipping ?? false
31295
+ );
31205
31296
  return {
31206
31297
  port,
31207
31298
  host,
@@ -31214,7 +31305,8 @@ function resolveConfig(opts) {
31214
31305
  noTunnelPersist,
31215
31306
  frpcBinary,
31216
31307
  mode,
31217
- previewPorts
31308
+ previewPorts,
31309
+ logShipping
31218
31310
  };
31219
31311
  }
31220
31312
  var HELP_TEXT = `clawd [options]
@@ -31225,6 +31317,7 @@ var HELP_TEXT = `clawd [options]
31225
31317
  --tunnel \u542F\u7528 frp tunnel \u628A\u672C\u5730\u7AEF\u53E3\u66B4\u9732\u5230\u516C\u7F51
31226
31318
  --clawos-api <url> tunnel register \u63A5\u53E3\u7684 base url\uFF08\u9ED8\u8BA4 https://api.clawos.chat\uFF09
31227
31319
  --auth-token <s> \u6307\u5B9A daemon auth token\uFF1B\u7F3A\u7701\u65F6 tunnel \u6A21\u5F0F\u4ECE ~/.clawd/auth.json \u590D\u7528\uFF0C\u6CA1\u6709\u5C31\u751F\u6210
31320
+ --no-log-shipping \u7981\u7528 SLS \u65E5\u5FD7\u4E0A\u884C\uFF08\u8986\u76D6 config.json logShipping.mode=off\uFF09
31228
31321
  --help / -h \u663E\u793A\u5E2E\u52A9
31229
31322
  --version / -v \u663E\u793A\u7248\u672C
31230
31323
 
@@ -31243,12 +31336,64 @@ Env (advanced):
31243
31336
  `;
31244
31337
 
31245
31338
  // src/index.ts
31246
- var import_node_path45 = __toESM(require("path"), 1);
31247
- var import_node_fs33 = __toESM(require("fs"), 1);
31339
+ var import_node_path46 = __toESM(require("path"), 1);
31340
+ var import_node_fs34 = __toESM(require("fs"), 1);
31341
+ var import_node_os19 = __toESM(require("os"), 1);
31342
+
31343
+ // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
31344
+ var byteToHex = [];
31345
+ for (let i = 0; i < 256; ++i) {
31346
+ byteToHex.push((i + 256).toString(16).slice(1));
31347
+ }
31348
+ function unsafeStringify(arr, offset = 0) {
31349
+ return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
31350
+ }
31351
+
31352
+ // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/rng.js
31353
+ var import_node_crypto = __toESM(require("crypto"));
31354
+ var rnds8Pool = new Uint8Array(256);
31355
+ var poolPtr = rnds8Pool.length;
31356
+ function rng() {
31357
+ if (poolPtr > rnds8Pool.length - 16) {
31358
+ import_node_crypto.default.randomFillSync(rnds8Pool);
31359
+ poolPtr = 0;
31360
+ }
31361
+ return rnds8Pool.slice(poolPtr, poolPtr += 16);
31362
+ }
31363
+
31364
+ // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/native.js
31365
+ var import_node_crypto2 = __toESM(require("crypto"));
31366
+ var native_default = {
31367
+ randomUUID: import_node_crypto2.default.randomUUID
31368
+ };
31369
+
31370
+ // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/v4.js
31371
+ function v4(options, buf, offset) {
31372
+ if (native_default.randomUUID && !buf && !options) {
31373
+ return native_default.randomUUID();
31374
+ }
31375
+ options = options || {};
31376
+ const rnds = options.random || (options.rng || rng)();
31377
+ rnds[6] = rnds[6] & 15 | 64;
31378
+ rnds[8] = rnds[8] & 63 | 128;
31379
+ if (buf) {
31380
+ offset = offset || 0;
31381
+ for (let i = 0; i < 16; ++i) {
31382
+ buf[offset + i] = rnds[i];
31383
+ }
31384
+ return buf;
31385
+ }
31386
+ return unsafeStringify(rnds);
31387
+ }
31388
+ var v4_default = v4;
31389
+
31390
+ // src/index.ts
31391
+ var import_node_url2 = require("url");
31248
31392
 
31249
31393
  // src/logger.ts
31250
31394
  var import_node_fs2 = __toESM(require("fs"), 1);
31251
31395
  var import_node_path2 = __toESM(require("path"), 1);
31396
+ var import_node_stream = require("stream");
31252
31397
  var import_pino = __toESM(require_pino(), 1);
31253
31398
  function wrap(inner) {
31254
31399
  return {
@@ -31274,12 +31419,328 @@ function createLogger(opts = {}) {
31274
31419
  } catch {
31275
31420
  }
31276
31421
  }
31422
+ if (opts.logClient) {
31423
+ const client = opts.logClient;
31424
+ const sink = new import_node_stream.Writable({
31425
+ write(chunk, _enc, cb) {
31426
+ try {
31427
+ const line = String(chunk).trim();
31428
+ if (line) {
31429
+ const obj = JSON.parse(line);
31430
+ const lvl = pinoLevelToString(obj.level);
31431
+ if (lvl) {
31432
+ const meta = {};
31433
+ for (const [k2, v2] of Object.entries(obj)) {
31434
+ if (k2 === "level" || k2 === "msg" || k2 === "time" || k2 === "pid" || k2 === "hostname") continue;
31435
+ if (k2 === "sessionId") continue;
31436
+ meta[k2] = v2;
31437
+ }
31438
+ client.push({
31439
+ ts: typeof obj.time === "number" ? obj.time : void 0,
31440
+ level: lvl,
31441
+ msg: typeof obj.msg === "string" ? obj.msg : "",
31442
+ meta: Object.keys(meta).length > 0 ? meta : void 0,
31443
+ sessionId: typeof obj.sessionId === "string" ? obj.sessionId : void 0
31444
+ });
31445
+ }
31446
+ }
31447
+ } catch {
31448
+ }
31449
+ cb();
31450
+ }
31451
+ });
31452
+ streams.push({ stream: sink, level });
31453
+ }
31277
31454
  const base = (0, import_pino.default)(
31278
31455
  { level, base: { pid: process.pid } },
31279
31456
  import_pino.default.multistream(streams)
31280
31457
  );
31281
31458
  return wrap(base);
31282
31459
  }
31460
+ function pinoLevelToString(n) {
31461
+ if (typeof n !== "number") return null;
31462
+ if (n >= 50) return "error";
31463
+ if (n >= 40) return "warn";
31464
+ if (n >= 30) return "info";
31465
+ if (n >= 20) return "debug";
31466
+ return null;
31467
+ }
31468
+
31469
+ // ../log-client/dist/index.js
31470
+ var DEFAULT_BATCH = {
31471
+ maxRecords: 100,
31472
+ maxBytes: 512 * 1024,
31473
+ maxWaitMs: 5e3
31474
+ };
31475
+ var DEFAULT_BUFFER_CAP = 5e3;
31476
+ var DEFAULT_SAMPLING = {
31477
+ debug: 0,
31478
+ info: 1,
31479
+ warn: 1,
31480
+ error: 1
31481
+ };
31482
+ var MAX_MSG_BYTES = 1024;
31483
+ var MAX_META_JSON_BYTES = 2048;
31484
+ var REDACT_KEY_REGEX = /token|secret|password|auth(?!or)|apikey|signsecret/i;
31485
+ var REDACTED_PLACEHOLDER = "[REDACTED]";
31486
+ var TRUNCATE_SUFFIX = "\u2026[truncated]";
31487
+ function redactMeta(meta, homeDir) {
31488
+ const cleaned = cleanValue(meta, homeDir, 0);
31489
+ const json = safeStringify(cleaned);
31490
+ if (json.length <= MAX_META_JSON_BYTES) return cleaned;
31491
+ const truncated = json.slice(0, MAX_META_JSON_BYTES) + TRUNCATE_SUFFIX;
31492
+ return { __truncated__: truncated };
31493
+ }
31494
+ function cleanValue(v2, homeDir, depth) {
31495
+ if (depth > 10) return "[depth-limit]";
31496
+ if (v2 == null) return v2;
31497
+ if (typeof v2 === "string") {
31498
+ return homeDir ? v2.split(homeDir).join("~") : v2;
31499
+ }
31500
+ if (typeof v2 !== "object") return v2;
31501
+ if (Array.isArray(v2)) return v2.map((x) => cleanValue(x, homeDir, depth + 1));
31502
+ const out = {};
31503
+ for (const [k2, val] of Object.entries(v2)) {
31504
+ if (REDACT_KEY_REGEX.test(k2)) {
31505
+ out[k2] = REDACTED_PLACEHOLDER;
31506
+ } else {
31507
+ out[k2] = cleanValue(val, homeDir, depth + 1);
31508
+ }
31509
+ }
31510
+ return out;
31511
+ }
31512
+ function safeStringify(v2) {
31513
+ try {
31514
+ return JSON.stringify(v2) ?? "";
31515
+ } catch {
31516
+ return '"[serialize-failed]"';
31517
+ }
31518
+ }
31519
+ function truncateMsg(msg) {
31520
+ if (msg.length <= MAX_MSG_BYTES) return msg;
31521
+ return msg.slice(0, MAX_MSG_BYTES) + TRUNCATE_SUFFIX;
31522
+ }
31523
+ function shouldSample(level, config, rng2) {
31524
+ const rate = config[level] ?? DEFAULT_SAMPLING[level];
31525
+ if (rate <= 0) return false;
31526
+ if (rate >= 1) return true;
31527
+ return rng2() < rate;
31528
+ }
31529
+ var Batcher = class {
31530
+ constructor(deps) {
31531
+ this.deps = deps;
31532
+ }
31533
+ deps;
31534
+ queue = [];
31535
+ bytes = 0;
31536
+ droppedSinceFlush = 0;
31537
+ identityReady = false;
31538
+ timer = null;
31539
+ disposed = false;
31540
+ pendingSends = [];
31541
+ enqueue(record) {
31542
+ if (this.disposed) return;
31543
+ const sz = approxSize(record);
31544
+ while (this.queue.length >= this.deps.bufferCap) {
31545
+ this.queue.shift();
31546
+ this.droppedSinceFlush++;
31547
+ }
31548
+ this.queue.push(record);
31549
+ this.bytes += sz;
31550
+ if (!this.identityReady) return;
31551
+ this.maybeTrigger();
31552
+ }
31553
+ setIdentityReady(ready) {
31554
+ this.identityReady = ready;
31555
+ if (ready && this.queue.length > 0) this.maybeTrigger();
31556
+ }
31557
+ async flush() {
31558
+ if (this.queue.length > 0) {
31559
+ this.fire("flush");
31560
+ }
31561
+ await Promise.allSettled(this.pendingSends);
31562
+ }
31563
+ stats() {
31564
+ return { queued: this.queue.length, dropped: this.droppedSinceFlush };
31565
+ }
31566
+ async dispose() {
31567
+ if (this.disposed) return;
31568
+ this.disposed = true;
31569
+ if (this.timer) {
31570
+ clearTimeout(this.timer);
31571
+ this.timer = null;
31572
+ }
31573
+ await this.flush();
31574
+ }
31575
+ maybeTrigger() {
31576
+ const { maxRecords, maxBytes, maxWaitMs } = this.deps.batch;
31577
+ if (this.queue.length >= maxRecords) {
31578
+ this.fire("records");
31579
+ return;
31580
+ }
31581
+ if (this.bytes >= maxBytes) {
31582
+ this.fire("bytes");
31583
+ return;
31584
+ }
31585
+ if (this.timer == null) {
31586
+ this.timer = setTimeout(() => {
31587
+ this.timer = null;
31588
+ if (this.queue.length > 0 && this.identityReady) this.fire("time");
31589
+ }, maxWaitMs);
31590
+ }
31591
+ }
31592
+ fire(reason) {
31593
+ if (this.timer) {
31594
+ clearTimeout(this.timer);
31595
+ this.timer = null;
31596
+ }
31597
+ const records = this.queue;
31598
+ const droppedCarryover = this.droppedSinceFlush;
31599
+ this.queue = [];
31600
+ this.bytes = 0;
31601
+ this.droppedSinceFlush = 0;
31602
+ const p2 = this.deps.onBatch({ reason, records, droppedCarryover }).catch(() => {
31603
+ });
31604
+ this.pendingSends.push(p2);
31605
+ p2.finally(() => {
31606
+ this.pendingSends = this.pendingSends.filter((x) => x !== p2);
31607
+ });
31608
+ }
31609
+ };
31610
+ function approxSize(record) {
31611
+ return (record.msg?.length ?? 0) + (record.meta_json?.length ?? 0) + 200;
31612
+ }
31613
+ var BACKOFF_MS = [1e3, 2e3, 4e3];
31614
+ async function sendBatch(deps, batch) {
31615
+ const logsForSls = batch.records.map(stringifyRecord);
31616
+ const body = JSON.stringify({
31617
+ __topic__: deps.topic ?? "clawd",
31618
+ __source__: deps.source,
31619
+ __logs__: logsForSls,
31620
+ ...batch.droppedCarryover > 0 ? { __tags__: { dropped: String(batch.droppedCarryover) } } : {}
31621
+ });
31622
+ for (let attempt = 0; attempt < BACKOFF_MS.length; attempt++) {
31623
+ if (attempt > 0) await sleep(BACKOFF_MS[attempt - 1]);
31624
+ try {
31625
+ const res = await deps.fetchImpl(deps.endpoint, {
31626
+ method: "POST",
31627
+ headers: { "Content-Type": "application/json" },
31628
+ body
31629
+ });
31630
+ if (res.status >= 200 && res.status < 300) {
31631
+ return { sent: batch.records.length, failed: 0 };
31632
+ }
31633
+ if (res.status >= 400 && res.status < 500) {
31634
+ return { sent: 0, failed: batch.records.length };
31635
+ }
31636
+ } catch {
31637
+ }
31638
+ }
31639
+ return { sent: 0, failed: batch.records.length };
31640
+ }
31641
+ function stringifyRecord(rec3) {
31642
+ const out = {};
31643
+ for (const [k2, v2] of Object.entries(rec3)) {
31644
+ if (v2 == null) continue;
31645
+ out[k2] = typeof v2 === "string" ? v2 : String(v2);
31646
+ }
31647
+ return out;
31648
+ }
31649
+ function sleep(ms) {
31650
+ return new Promise((r) => setTimeout(r, ms));
31651
+ }
31652
+ function createLogClient(opts) {
31653
+ const stats = { sent: 0, dropped: 0, queued: 0, failed: 0 };
31654
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
31655
+ const now = opts.now ?? Date.now;
31656
+ const sampling = { ...DEFAULT_SAMPLING, ...opts.sampling ?? {} };
31657
+ const homeDir = opts.homeDir === void 0 ? null : opts.homeDir;
31658
+ const batchConfig = { ...DEFAULT_BATCH, ...opts.batch ?? {} };
31659
+ const bufferCap = opts.bufferCap ?? DEFAULT_BUFFER_CAP;
31660
+ let identity = opts.identity;
31661
+ let disposed = false;
31662
+ if (opts.endpoint == null) {
31663
+ return {
31664
+ push: () => {
31665
+ if (disposed) return;
31666
+ stats.dropped++;
31667
+ },
31668
+ flush: async () => {
31669
+ },
31670
+ setIdentity: (next) => {
31671
+ identity = next;
31672
+ },
31673
+ stats: () => ({ ...stats }),
31674
+ dispose: async () => {
31675
+ disposed = true;
31676
+ }
31677
+ };
31678
+ }
31679
+ const endpoint = opts.endpoint;
31680
+ const batcher = new Batcher({
31681
+ batch: batchConfig,
31682
+ bufferCap,
31683
+ now,
31684
+ onBatch: async (b2) => {
31685
+ const res = await sendBatch({ endpoint, source: opts.source, fetchImpl }, b2);
31686
+ stats.sent += res.sent;
31687
+ stats.failed += res.failed;
31688
+ }
31689
+ });
31690
+ batcher.setIdentityReady(identity != null);
31691
+ return {
31692
+ push(input) {
31693
+ if (disposed) return;
31694
+ try {
31695
+ if (!shouldSample(input.level, sampling, Math.random)) {
31696
+ stats.dropped++;
31697
+ return;
31698
+ }
31699
+ const record = buildRecord(input, identity, opts.source, homeDir, now);
31700
+ batcher.enqueue(record);
31701
+ } catch {
31702
+ }
31703
+ },
31704
+ async flush() {
31705
+ try {
31706
+ await batcher.flush();
31707
+ } catch {
31708
+ }
31709
+ },
31710
+ setIdentity(next) {
31711
+ identity = next;
31712
+ batcher.setIdentityReady(next != null);
31713
+ },
31714
+ stats() {
31715
+ const bs = batcher.stats();
31716
+ return { ...stats, queued: bs.queued, dropped: stats.dropped + bs.dropped };
31717
+ },
31718
+ async dispose() {
31719
+ disposed = true;
31720
+ try {
31721
+ await batcher.dispose();
31722
+ } catch {
31723
+ }
31724
+ }
31725
+ };
31726
+ }
31727
+ function buildRecord(input, identity, source, homeDir, now) {
31728
+ const cleanedMeta = input.meta ? redactMeta(input.meta, homeDir) : void 0;
31729
+ const meta_json = cleanedMeta ? JSON.stringify(cleanedMeta) : void 0;
31730
+ return {
31731
+ ts: input.ts ?? now(),
31732
+ source,
31733
+ level: input.level,
31734
+ msg: truncateMsg(input.msg),
31735
+ ownerPrincipalId: identity?.ownerPrincipalId ?? "__pending__",
31736
+ deviceId: identity?.deviceId ?? "__pending__",
31737
+ appVersion: identity?.appVersion ?? "__pending__",
31738
+ daemonVersion: identity?.daemonVersion,
31739
+ os: identity?.os ?? "unknown",
31740
+ meta_json,
31741
+ sessionId: input.sessionId
31742
+ };
31743
+ }
31283
31744
 
31284
31745
  // src/session/store-factory.ts
31285
31746
  var path5 = __toESM(require("path"), 1);
@@ -31486,55 +31947,6 @@ var SessionStoreFactory = class {
31486
31947
  var import_node_fs6 = __toESM(require("fs"), 1);
31487
31948
  var import_node_path6 = __toESM(require("path"), 1);
31488
31949
  var import_node_os2 = __toESM(require("os"), 1);
31489
-
31490
- // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
31491
- var byteToHex = [];
31492
- for (let i = 0; i < 256; ++i) {
31493
- byteToHex.push((i + 256).toString(16).slice(1));
31494
- }
31495
- function unsafeStringify(arr, offset = 0) {
31496
- return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
31497
- }
31498
-
31499
- // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/rng.js
31500
- var import_node_crypto = __toESM(require("crypto"));
31501
- var rnds8Pool = new Uint8Array(256);
31502
- var poolPtr = rnds8Pool.length;
31503
- function rng() {
31504
- if (poolPtr > rnds8Pool.length - 16) {
31505
- import_node_crypto.default.randomFillSync(rnds8Pool);
31506
- poolPtr = 0;
31507
- }
31508
- return rnds8Pool.slice(poolPtr, poolPtr += 16);
31509
- }
31510
-
31511
- // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/native.js
31512
- var import_node_crypto2 = __toESM(require("crypto"));
31513
- var native_default = {
31514
- randomUUID: import_node_crypto2.default.randomUUID
31515
- };
31516
-
31517
- // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/v4.js
31518
- function v4(options, buf, offset) {
31519
- if (native_default.randomUUID && !buf && !options) {
31520
- return native_default.randomUUID();
31521
- }
31522
- options = options || {};
31523
- const rnds = options.random || (options.rng || rng)();
31524
- rnds[6] = rnds[6] & 15 | 64;
31525
- rnds[8] = rnds[8] & 63 | 128;
31526
- if (buf) {
31527
- offset = offset || 0;
31528
- for (let i = 0; i < 16; ++i) {
31529
- buf[offset + i] = rnds[i];
31530
- }
31531
- return buf;
31532
- }
31533
- return unsafeStringify(rnds);
31534
- }
31535
- var v4_default = v4;
31536
-
31537
- // src/session/manager.ts
31538
31950
  init_protocol();
31539
31951
 
31540
31952
  // src/tools/guest-settings.ts
@@ -31659,6 +32071,32 @@ function buildConnectionPrompt(args) {
31659
32071
  return result;
31660
32072
  }
31661
32073
 
32074
+ // src/dispatch/system-prompt.ts
32075
+ var DISPATCH_SYSTEM_PROMPT_HINT = `## \u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08dispatch\uFF09
32076
+
32077
+ \u5F53\u7528\u6237\u6D88\u606F\u91CC\u542B \`@persona/<id>\`\uFF08\u5982 \`@persona/persona-bug-fixer\`\uFF09\u65F6\uFF0C**\u4F60\u5FC5\u987B**\u7ACB\u5373\u8C03 MCP tool \`mcp__clawd-dispatch__personaDispatch\` \u59D4\u6D3E\uFF0C\u4E0D\u8981\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF1A
32078
+
32079
+ - \`targetPersona = <id>\`\uFF08@ token \u91CC\u7684 persona id\uFF09
32080
+ - \`prompt = \u53BB\u6389 @persona/<id> token \u7684\u7528\u6237\u539F\u6587\`
32081
+
32082
+ 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
32083
+
32084
+ - **\u4E0D\u8981**\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF08\u4F60\u4E0D\u662F\u90A3\u4E2A persona\uFF09
32085
+ - **\u4E0D\u8981** in-place \u6A21\u4EFF\u5BF9\u65B9\u4EBA\u683C \u2014\u2014 dispatch \u662F\u771F\u5207\u5230\u53E6\u4E00\u4E2A\u72EC\u7ACB session
32086
+ - \u4E00\u53E5\u8BDD\u91CC\u591A\u4E2A \`@persona/<id>\` \u65F6\u9009\u7B2C\u4E00\u4E2A\uFF08tool \u4E00\u6B21\u53EA\u5904\u7406\u4E00\u4E2A target\uFF09
32087
+ - \u59D4\u6D3E\u5931\u8D25\uFF08tool_result.is_error=true\uFF09\u65F6\u5982\u5B9E\u544A\u8BC9\u7528\u6237\u539F\u56E0\uFF0C**\u4E0D\u8981\u91CD\u8BD5**
32088
+
32089
+ ## \u88AB dispatch \u63A5\u5230\u4EFB\u52A1
32090
+
32091
+ \u5982\u679C\u4F60\u7684 session \u7B2C\u4E00\u6761 user message \u662F\u4EE5 \`[Dispatched from owner \u2014 another persona session\u59D4\u6D3E\u7ED9\u4F60\u6267\u884C\u4EFB\u52A1]\` \u8D77\u5934\u7684 plain text \u4EFB\u52A1\u5305\uFF0C\u4F60\u662F\u88AB\u53E6\u4E00\u4E2A persona \u59D4\u6D3E\u6765\u5E72\u6D3B\u7684\uFF08\u4EFB\u52A1\u5305\u4E0D\u7528 \`<system-reminder>\` \u5305\u662F\u4E3A\u4E86 reducer dedup \u5DE5\u4F5C\u6B63\u5E38\uFF0C\u89C1 manager.ts buildDispatchTaskPack \u6CE8\u91CA\uFF09\uFF1A
32092
+
32093
+ - \u4EFB\u52A1\u5728 \`Task:\` \u6BB5
32094
+ - \u539F\u4F1A\u8BDD\u7684\u5BF9\u8BDD\u5386\u53F2\u5728 \`Source conversation\` \u6BB5\u7ED9\u7684 jsonl \u8DEF\u5F84\u4E0B\uFF1B\u6309\u9700 Read \u5B83\u4E86\u89E3\u4E0A\u4E0B\u6587\uFF08\u4E0D\u5FC5\u8BFB\u5B8C\uFF09
32095
+ - \u5E72\u5B8C\u540E**\u5FC5\u987B**\u8C03 \`mcp__clawd-dispatch__personaDispatchComplete\` \u62A5\u544A\u7ED3\u679C\uFF1A
32096
+ - \u6210\u529F\uFF1A\`{ text: "\u603B\u7ED3/\u7ED3\u8BBA", filePaths?: ["\u4F60\u4EA7\u51FA/\u6539\u7684\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84"] }\`
32097
+ - \u5931\u8D25\uFF1A\`{ isFailure: true, reason: "..." }\`
32098
+ - \u62A5\u544A\u5B8C\u540E\u4F60\u7684 session \u5C31\u7ED3\u675F\u4E86\uFF0C\u4E0D\u8981\u7EE7\u7EED\u8F93\u51FA`;
32099
+
31662
32100
  // src/session/reducer.ts
31663
32101
  function cloneState(s) {
31664
32102
  return {
@@ -31737,6 +32175,9 @@ function buildSpawnContext(state, deps) {
31737
32175
  env.CLAWD_SESSION_ID = file.sessionId;
31738
32176
  const daemonUrl = deps.getDaemonUrl?.() ?? null;
31739
32177
  if (daemonUrl) env.CLAWD_DAEMON_URL = daemonUrl;
32178
+ const dispatchId = deps.lookupDispatchByBSessionId?.(file.sessionId);
32179
+ if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
32180
+ const dispatchMcpConfigPath2 = deps.getDispatchMcpConfigPath?.() ?? null;
31740
32181
  const ctx = {
31741
32182
  cwd: file.cwd,
31742
32183
  toolSessionId: file.toolSessionId,
@@ -31746,7 +32187,10 @@ function buildSpawnContext(state, deps) {
31746
32187
  env,
31747
32188
  // Phase 2 capability platform (plan §3): personaRoot 透传给 buildSpawnArgs;
31748
32189
  // 内部 shouldSandbox(cwd, personaRoot) 决定是否注入 --settings sandbox-settings.
31749
- personaRoot: deps.personaRoot
32190
+ personaRoot: deps.personaRoot,
32191
+ // Persona dispatch:所有 session(A 和 B)都挂同一份 dispatch.mcp.json,让两边的 cc
32192
+ // 都能看到 personaDispatch / personaDispatchComplete tool;按 env 区分实际行为。
32193
+ dispatchMcpConfigPath: dispatchMcpConfigPath2 ?? void 0
31750
32194
  };
31751
32195
  const meta = state.subSessionMeta;
31752
32196
  if (meta?.personaMode) {
@@ -31757,6 +32201,9 @@ function buildSpawnContext(state, deps) {
31757
32201
  workDir: meta.userWorkDir
31758
32202
  });
31759
32203
  }
32204
+ if (dispatchMcpConfigPath2) {
32205
+ ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + DISPATCH_SYSTEM_PROMPT_HINT;
32206
+ }
31760
32207
  if (meta?.extraSettings) {
31761
32208
  ctx.extraSettings = meta.extraSettings;
31762
32209
  }
@@ -32422,35 +32869,6 @@ function startRecorder(opts) {
32422
32869
  };
32423
32870
  }
32424
32871
 
32425
- // src/persona/mention-injection.ts
32426
- var MENTION_RE = /@persona\/([A-Za-z0-9_-]+)(?=\s|$|[^A-Za-z0-9_-])/g;
32427
- function injectPersonaMentions(rawText, store) {
32428
- const resolved = [];
32429
- const body = rawText.replace(MENTION_RE, (match, id) => {
32430
- const meta = store.read(id);
32431
- const personality = store.readPersonality(id);
32432
- if (meta && personality !== null) {
32433
- resolved.push({ label: meta.label, personality });
32434
- return "";
32435
- }
32436
- return match;
32437
- });
32438
- if (resolved.length === 0) {
32439
- return { systemReminder: null, userText: rawText };
32440
- }
32441
- const blocks = resolved.map((r) => {
32442
- const safe = r.personality.replace(/<\/system-reminder>/gi, "</ system-reminder>");
32443
- return `[Acting as persona "${r.label}" for this message]
32444
- ${safe}`;
32445
- });
32446
- return {
32447
- systemReminder: `<system-reminder>
32448
- ${blocks.join("\n\n")}
32449
- </system-reminder>`,
32450
- userText: body.trim()
32451
- };
32452
- }
32453
-
32454
32872
  // src/session/runner.ts
32455
32873
  var DEFAULT_CONTROL_REQUEST_TIMEOUT_MS = 1e4;
32456
32874
  function encodeAllowWithInputControlResponse(requestId, updatedInput) {
@@ -32499,7 +32917,6 @@ var SessionRunner = class {
32499
32917
  }
32500
32918
  // 外部推送 input 到 reducer,然后执行产出的 effects
32501
32919
  input(inputMsg) {
32502
- const personaStore = this.hooks.personaStore;
32503
32920
  const adapter = this.hooks.adapter;
32504
32921
  const deps = {
32505
32922
  // file-sharing (spec §6 PR 3):在 stdout-line 解析时同步观察 file-edit 工具事件,
@@ -32509,20 +32926,10 @@ var SessionRunner = class {
32509
32926
  if (this.hooks.onFileEdit) this.observeForFileEdit(events);
32510
32927
  return events;
32511
32928
  },
32512
- // persona mention injection deps 装配处包一层,对 reducer 完全透明。
32513
- // 命中 mention 时拆成两条 stdin 帧(先 system-reminder 块、后 user 原文):
32514
- // - CC stream-json 按行处理,落盘是两条独立 user message;
32515
- // - 第一条文本起头 `<system-reminder>` classifyUserMessage 命中 UI 折叠;
32516
- // - 第二条正常用户气泡,刷新页面不再暴露 personality 文本。
32517
- encodeStdin: (t, ctx) => {
32518
- if (!personaStore) return adapter.encodeStdin(t, ctx) ?? "";
32519
- const { systemReminder, userText } = injectPersonaMentions(t, personaStore);
32520
- if (systemReminder === null) return adapter.encodeStdin(userText, ctx) ?? "";
32521
- const metaFrame = adapter.encodeStdin(systemReminder, ctx) ?? "";
32522
- if (userText === "") return metaFrame;
32523
- const userFrame = adapter.encodeStdin(userText, ctx) ?? "";
32524
- return metaFrame + userFrame;
32525
- },
32929
+ // Persona dispatch (Task 9):原 in-place `@persona/<id>` 注入路径已删,cc 自己看到字面
32930
+ // token 会调 personaDispatch MCP tool 委派(dispatch.mcp.json 通过 cc --mcp-config 加载,
32931
+ // CLAUDE.md cc 触发条件)。encodeStdin 现在是 adapter 的简单 passthrough。
32932
+ encodeStdin: (t, ctx) => adapter.encodeStdin(t, ctx) ?? "",
32526
32933
  bufferCap: this.hooks.bufferCap ?? 500,
32527
32934
  now: this.hooks.now ?? Date.now,
32528
32935
  resolveContextWindow: this.hooks.resolveContextWindow,
@@ -32532,6 +32939,10 @@ var SessionRunner = class {
32532
32939
  // 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
32533
32940
  // curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
32534
32941
  getDaemonUrl: this.hooks.getDaemonUrl,
32942
+ // Persona dispatch:透传 dispatch.mcp.json 路径闭包 + B→dispatchId 反查闭包。
32943
+ // 见 reducer.buildSpawnContext 注 CLAWD_DISPATCH_ID env / 派生 SpawnContext.dispatchMcpConfigPath
32944
+ getDispatchMcpConfigPath: this.hooks.getDispatchMcpConfigPath,
32945
+ lookupDispatchByBSessionId: this.hooks.lookupDispatchByBSessionId,
32535
32946
  // ReadyGate v2:透传 mode 让 reducer send / ready-detected 分支决定走暂存队列还是直写
32536
32947
  mode: this.hooks.mode,
32537
32948
  // [RG-DBG] 注入 logger 让 reducer rgDbg 走 pino 进 clawd.log;定位完跟 rgDbg 一起删
@@ -32991,6 +33402,22 @@ function nowIso2(deps) {
32991
33402
  function newSessionId() {
32992
33403
  return v4_default().replace(/-/g, "").slice(0, 16);
32993
33404
  }
33405
+ function buildDispatchTaskPack(args) {
33406
+ return `[Dispatched from owner \u2014 another persona session\u59D4\u6D3E\u7ED9\u4F60\u6267\u884C\u4EFB\u52A1]
33407
+
33408
+ Task:
33409
+ ${args.prompt}
33410
+
33411
+ Source conversation (jsonl, CC stream-json format, one message per line):
33412
+ ${args.sourceJsonlPath}
33413
+ You may read this file with your Read tool to understand context. You do not have to read it all.
33414
+
33415
+ When done, call the MCP tool \`mcp__clawd-dispatch__personaDispatchComplete\` with your result:
33416
+ - Success: { text: "...", filePaths?: ["abs/path", ...] }
33417
+ - Failure: { isFailure: true, reason: "..." }
33418
+
33419
+ dispatchId for this task: ${args.dispatchId}`;
33420
+ }
32994
33421
  function derivePersonaSpawnCwd(file, personaRoot) {
32995
33422
  const personaId = file.ownerPersonaId;
32996
33423
  if (!personaId) return file.cwd;
@@ -33288,7 +33715,8 @@ var SessionManager = class {
33288
33715
  // adapter 自己持有模型表 + 兜底逻辑(contains / opus-1M / [1m] 等),manager 不再 cache 转发
33289
33716
  resolveContextWindow: (tool, modelId) => this.deps.getAdapter(tool).resolveContextWindow(modelId),
33290
33717
  dataDir: this.deps.dataDir,
33291
- personaStore: this.deps.personaStore,
33718
+ // personaStore 不再透给 runner hooks(in-place mention 注入路径已删,runner 不用);
33719
+ // manager 自身的 sandbox 派生仍直接读 this.deps.personaStore。
33292
33720
  ownerDisplayName: this.deps.ownerDisplayName,
33293
33721
  // Phase 2 capability platform (plan §3): 透传 personaRoot, buildSpawnArgs 据
33294
33722
  // cwd 是否在 personaRoot 下自动决定是否注入 --settings sandbox-settings.json.
@@ -33296,6 +33724,10 @@ var SessionManager = class {
33296
33724
  // 单栏 refactor (spec 2026-06-02 §5.1): 透传 daemon HTTP RPC base URL 闭包,
33297
33725
  // reducer 把它注入 cc 子进程 env CLAWD_DAEMON_URL.
33298
33726
  getDaemonUrl: this.deps.getDaemonUrl,
33727
+ // Persona dispatch (Task 7): dispatch.mcp.json 路径 + B sessionId → dispatchId 反查
33728
+ // 闭包透传给 reducer,让 cc spawn 加 --mcp-config flag + B session 注 CLAWD_DISPATCH_ID env.
33729
+ getDispatchMcpConfigPath: this.deps.dispatchMcpConfigPath ? () => this.deps.dispatchMcpConfigPath ?? null : void 0,
33730
+ lookupDispatchByBSessionId: this.deps.personaDispatchManager ? (bSid) => this.deps.personaDispatchManager?.lookupDispatchByBSessionId(bSid) : void 0,
33299
33731
  // file-sharing (spec §6 PR 3):闭包 scope + sessionId,runner 只暴露 tool/relPath/cwd
33300
33732
  onFileEdit: attachmentGroup ? (input) => attachmentGroup.onFileEdit({
33301
33733
  scope,
@@ -33511,15 +33943,20 @@ var SessionManager = class {
33511
33943
  // 强制走 deps.store(即 default scope),不走 scopeForFile:mirror 物理落 default 即可。
33512
33944
  // 远端 creator / origin 字段原值保留(不被本机 ctx 派生覆写)。
33513
33945
  //
33514
- // strip 远端 personaId:mirror SessionFile 上的 ownerPersonaId 是朋友 daemon 的 persona
33515
- // 目录 id,本机不持有对应 persona。留着会让按 ownerPersonaId 反查的本机消费者
33516
- // MyPersonasSection 等)把 mirror 误聚到同 id persona 下。写入前清掉。
33946
+ // strip 远端 personaId / dispatchedFromSessionId:mirror SessionFile 上这两个字段都指向
33947
+ // 朋友 daemon 上的本地 id,本机不持有对应 persona / 源 session。留着会让按这些字段反查的
33948
+ // 本机消费者(MyPersonasSection ownerPersonaId 反查 persona、sidebar
33949
+ // dispatchedFromSessionId 折叠 sub-session)走错分支。写入前清掉。#750 一族防线。
33517
33950
  //
33518
33951
  // 与 manager.create / manager.update 区别:
33519
33952
  // - 不走 newRunner / attachObserver / cwd 校验(mirror 是纯 metadata 层)
33520
33953
  // - 不发 session:info 帧(manager.create / .update 通过 reducer/wireCollector 发 push)
33521
33954
  mirrorUpsert(file) {
33522
- const { ownerPersonaId: _stripped, ...rest } = file;
33955
+ const {
33956
+ ownerPersonaId: _strippedPid,
33957
+ dispatchedFromSessionId: _strippedDsp,
33958
+ ...rest
33959
+ } = file;
33523
33960
  return this.deps.store.write(rest);
33524
33961
  }
33525
33962
  // mirror peer sessions:删本机 mirror SessionFile。mirror 一律落 default(mirrorUpsert
@@ -34104,6 +34541,66 @@ var SessionManager = class {
34104
34541
  this.runners.set(file.sessionId, runner);
34105
34542
  return runner;
34106
34543
  }
34544
+ /**
34545
+ * Persona dispatch: 启动 B session 并投递任务包当第一条 user message。
34546
+ *
34547
+ * - 复用 persona-owner 创建路径(cwd=personaDir、scope=persona-owner、不沙箱)
34548
+ * - SessionFile 上写 dispatchedFromSessionId 关联回 A(UI 折叠用;mirror 一侧 strip)
34549
+ * - 跟 PersonaDispatchManager 双向登记 dispatchId ↔ B sessionId,让后续 reducer
34550
+ * buildSpawnContext 能反查到 dispatchId 注 CLAWD_DISPATCH_ID env
34551
+ * - 投 inject-owner-text 任务包;包尾教 B 用 personaDispatchComplete tool 回传
34552
+ *
34553
+ * cwd 派生与 SessionManager.create 的 owner persona 路径一致(derivePersonaSpawnCwd)。
34554
+ */
34555
+ createDispatchedSession(args) {
34556
+ if (!this.deps.personaRoot) {
34557
+ throw new Error("createDispatchedSession: personaRoot missing in ManagerDeps");
34558
+ }
34559
+ if (!this.deps.personaDispatchManager) {
34560
+ throw new Error("createDispatchedSession: personaDispatchManager missing in ManagerDeps");
34561
+ }
34562
+ const sessionId = newSessionId();
34563
+ const scope = {
34564
+ kind: "persona",
34565
+ personaId: args.targetPersona,
34566
+ mode: "owner"
34567
+ };
34568
+ const cwd = import_node_path6.default.join(this.deps.personaRoot, safeFileName(args.targetPersona));
34569
+ const now = (/* @__PURE__ */ new Date()).toISOString();
34570
+ const file = {
34571
+ sessionId,
34572
+ cwd,
34573
+ tool: "claude",
34574
+ ownerPersonaId: args.targetPersona,
34575
+ dispatchedFromSessionId: args.sourceSessionId,
34576
+ createdAt: now,
34577
+ updatedAt: now
34578
+ };
34579
+ this.storeFor(scope).write(file);
34580
+ this.deps.personaDispatchManager.registerBSession(args.dispatchId, sessionId);
34581
+ this.deps.logger?.info("dispatch.createDispatchedSession.file-written", {
34582
+ dispatchId: args.dispatchId,
34583
+ bSessionId: sessionId,
34584
+ targetPersona: args.targetPersona,
34585
+ cwd
34586
+ });
34587
+ const runner = this.ensureRunnerForScope(file, scope);
34588
+ this.deps.logger?.info("dispatch.createDispatchedSession.runner-ready", {
34589
+ dispatchId: args.dispatchId,
34590
+ bSessionId: sessionId
34591
+ });
34592
+ const taskPack = buildDispatchTaskPack({
34593
+ prompt: args.prompt,
34594
+ sourceJsonlPath: args.sourceJsonlPath,
34595
+ dispatchId: args.dispatchId
34596
+ });
34597
+ runner.input({ kind: "command", command: { kind: "send", text: taskPack } });
34598
+ this.deps.logger?.info("dispatch.createDispatchedSession.task-pack-sent", {
34599
+ dispatchId: args.dispatchId,
34600
+ bSessionId: sessionId
34601
+ });
34602
+ return { sessionId };
34603
+ }
34107
34604
  /**
34108
34605
  * 老板插话:把 'inject-owner-text' input 喂给对应 runner,
34109
34606
  * runner 不存在时按 scope 懒创建。
@@ -35316,6 +35813,56 @@ function migrateCodexSandbox(args) {
35316
35813
  }
35317
35814
  if (n > 0) args.logger.info("persona.codex-sandbox.done", { migrated: n });
35318
35815
  }
35816
+ function findDeployKitRoot() {
35817
+ const candidates = [];
35818
+ try {
35819
+ const here = path11.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
35820
+ candidates.push(path11.resolve(here, "..", "deploy-kit"));
35821
+ } catch {
35822
+ }
35823
+ if (process.argv[1]) {
35824
+ candidates.push(path11.resolve(path11.dirname(process.argv[1]), "deploy-kit"));
35825
+ }
35826
+ for (const c of candidates) {
35827
+ try {
35828
+ if (fs9.statSync(c).isDirectory()) return c;
35829
+ } catch {
35830
+ }
35831
+ }
35832
+ return null;
35833
+ }
35834
+ function seedDeployKit(args) {
35835
+ const dst = path11.join(args.dataDir, "deploy-kit");
35836
+ fs9.cpSync(args.deployKitBundleRoot, dst, {
35837
+ recursive: true,
35838
+ dereference: true,
35839
+ force: false,
35840
+ errorOnExist: false
35841
+ });
35842
+ args.logger.info("deploy-kit.seed.done", { dst });
35843
+ }
35844
+ function refreshDeployKit(args) {
35845
+ const dst = path11.join(args.dataDir, "deploy-kit");
35846
+ if (!fs9.existsSync(dst)) {
35847
+ seedDeployKit(args);
35848
+ return;
35849
+ }
35850
+ for (const sub of ["scripts", "contract"]) {
35851
+ const s = path11.join(args.deployKitBundleRoot, sub);
35852
+ if (fs9.existsSync(s)) {
35853
+ fs9.cpSync(s, path11.join(dst, sub), { recursive: true, force: true, dereference: true });
35854
+ }
35855
+ }
35856
+ const secretsSrc = path11.join(args.deployKitBundleRoot, ".secrets");
35857
+ if (fs9.existsSync(secretsSrc)) {
35858
+ fs9.mkdirSync(path11.join(dst, ".secrets"), { recursive: true });
35859
+ for (const f of fs9.readdirSync(secretsSrc)) {
35860
+ if (!f.endsWith(".example")) continue;
35861
+ fs9.copyFileSync(path11.join(secretsSrc, f), path11.join(dst, ".secrets", f));
35862
+ }
35863
+ }
35864
+ args.logger.info("deploy-kit.refresh.done", { dst });
35865
+ }
35319
35866
 
35320
35867
  // src/index.ts
35321
35868
  init_claude();
@@ -35570,8 +36117,8 @@ function turnStartInput(text) {
35570
36117
  const items = [];
35571
36118
  let leftover = text;
35572
36119
  for (const m2 of text.matchAll(SKILL_RE)) {
35573
- const [marker, name, path58] = m2;
35574
- items.push({ type: "skill", name, path: path58 });
36120
+ const [marker, name, path59] = m2;
36121
+ items.push({ type: "skill", name, path: path59 });
35575
36122
  leftover = leftover.replace(marker, "");
35576
36123
  }
35577
36124
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -36222,7 +36769,7 @@ init_claude_history();
36222
36769
 
36223
36770
  // src/pty/pty-child-process.ts
36224
36771
  var import_node_events = require("events");
36225
- var import_node_stream = require("stream");
36772
+ var import_node_stream2 = require("stream");
36226
36773
  var PtyChildProcess = class extends import_node_events.EventEmitter {
36227
36774
  constructor(pty, opts = {}) {
36228
36775
  super();
@@ -36251,7 +36798,7 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
36251
36798
  pty.write(data);
36252
36799
  }
36253
36800
  };
36254
- this.stdin = new import_node_stream.Writable({
36801
+ this.stdin = new import_node_stream2.Writable({
36255
36802
  decodeStrings: false,
36256
36803
  write: (chunk, _enc, cb) => {
36257
36804
  const s = typeof chunk === "string" ? chunk : chunk.toString("utf8");
@@ -36270,7 +36817,7 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
36270
36817
  }
36271
36818
  }
36272
36819
  });
36273
- this.stdout = new import_node_stream.Readable({ read() {
36820
+ this.stdout = new import_node_stream2.Readable({ read() {
36274
36821
  } });
36275
36822
  pty.onData((data) => {
36276
36823
  if (!this.firstDataLogged) {
@@ -36283,7 +36830,7 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
36283
36830
  }
36284
36831
  this.stdout.push(Buffer.from(data, "utf8"));
36285
36832
  });
36286
- this.stderr = new import_node_stream.Readable({ read() {
36833
+ this.stderr = new import_node_stream2.Readable({ read() {
36287
36834
  } });
36288
36835
  pty.onExit(({ exitCode, signal }) => {
36289
36836
  this.exitCode = exitCode;
@@ -36798,6 +37345,9 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
36798
37345
  if (ctx.addDirs) {
36799
37346
  for (const dir of ctx.addDirs) args.push("--add-dir", dir);
36800
37347
  }
37348
+ if (ctx.dispatchMcpConfigPath) {
37349
+ args.push("--mcp-config", ctx.dispatchMcpConfigPath);
37350
+ }
36801
37351
  if (ctx.effort) args.push("--effort", ctx.effort);
36802
37352
  return args;
36803
37353
  }
@@ -36815,6 +37365,155 @@ function jsonlExistsForCtx(ctx) {
36815
37365
  // src/index.ts
36816
37366
  init_claude_history();
36817
37367
 
37368
+ // src/dispatch/manager.ts
37369
+ function defer() {
37370
+ let resolve6;
37371
+ const promise = new Promise((r) => {
37372
+ resolve6 = r;
37373
+ });
37374
+ return { promise, resolve: resolve6 };
37375
+ }
37376
+ var PersonaDispatchManager = class {
37377
+ constructor(deps) {
37378
+ this.deps = deps;
37379
+ }
37380
+ deps;
37381
+ map = /* @__PURE__ */ new Map();
37382
+ start(args) {
37383
+ const dispatchId = this.deps.genId();
37384
+ this.map.set(dispatchId, {
37385
+ dispatchId,
37386
+ sourceSessionId: args.sourceSessionId,
37387
+ targetPersona: args.targetPersona,
37388
+ waiters: [],
37389
+ outcome: null
37390
+ });
37391
+ return { dispatchId };
37392
+ }
37393
+ get(dispatchId) {
37394
+ return this.map.get(dispatchId);
37395
+ }
37396
+ async wait(dispatchId) {
37397
+ const state = this.map.get(dispatchId);
37398
+ if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
37399
+ if (state.outcome) return state.outcome;
37400
+ const d = defer();
37401
+ state.waiters.push(d);
37402
+ return d.promise;
37403
+ }
37404
+ complete(dispatchId, outcome) {
37405
+ const state = this.map.get(dispatchId);
37406
+ if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
37407
+ state.outcome = outcome;
37408
+ const waiters = state.waiters;
37409
+ state.waiters = [];
37410
+ for (const w2 of waiters) w2.resolve(outcome);
37411
+ }
37412
+ /** SessionManager 在 createDispatchedSession 时调,建立 dispatchId → B sessionId 映射 */
37413
+ registerBSession(dispatchId, bSessionId) {
37414
+ const state = this.map.get(dispatchId);
37415
+ if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
37416
+ state.bSessionId = bSessionId;
37417
+ }
37418
+ /** reducer buildSpawnContext 调,按 B 的 sessionId 反查 dispatchId(用于注 CLAWD_DISPATCH_ID env) */
37419
+ lookupDispatchByBSessionId(bSessionId) {
37420
+ for (const state of this.map.values()) {
37421
+ if (state.bSessionId === bSessionId) return state.dispatchId;
37422
+ }
37423
+ return void 0;
37424
+ }
37425
+ };
37426
+
37427
+ // src/dispatch/mcp-config.ts
37428
+ var import_node_fs12 = __toESM(require("fs"), 1);
37429
+ var import_node_path10 = __toESM(require("path"), 1);
37430
+ function dispatchMcpConfigPath(dataDir) {
37431
+ return import_node_path10.default.join(dataDir, "dispatch.mcp.json");
37432
+ }
37433
+ function writeDispatchMcpConfig(args) {
37434
+ const cfgPath = dispatchMcpConfigPath(args.dataDir);
37435
+ const content = {
37436
+ _comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002dispatch-mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\u3002env.CLAWD_DISPATCH_LOG \u6307 server \u5199\u65E5\u5FD7\u7684 append \u6587\u4EF6\u8DEF\u5F84\uFF08dispatch \u6D41\u7A0B\u8282\u70B9 + fetch \u9519\u8BEF\u7EC6\u8282\uFF09\u3002",
37437
+ mcpServers: {
37438
+ "clawd-dispatch": {
37439
+ type: "stdio",
37440
+ command: "node",
37441
+ args: [args.serverScriptPath],
37442
+ ...args.logPath ? { env: { CLAWD_DISPATCH_LOG: args.logPath } } : {}
37443
+ }
37444
+ }
37445
+ };
37446
+ import_node_fs12.default.mkdirSync(args.dataDir, { recursive: true });
37447
+ import_node_fs12.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
37448
+ return cfgPath;
37449
+ }
37450
+
37451
+ // src/handlers/persona-dispatch.ts
37452
+ init_protocol();
37453
+ function buildPersonaDispatchHandlers(deps) {
37454
+ const { personaDispatchManager: mgr, spawnB, logger } = deps;
37455
+ const run = async (frame) => {
37456
+ const { type: _t, requestId: _r, ...rest } = frame;
37457
+ const sourceSessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
37458
+ const args = DispatchRunArgsSchema.parse(rest);
37459
+ if (!sourceSessionId) {
37460
+ throw new Error(
37461
+ "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
37462
+ );
37463
+ }
37464
+ const { dispatchId } = mgr.start({
37465
+ sourceSessionId,
37466
+ targetPersona: args.targetPersona
37467
+ });
37468
+ logger?.info("dispatch.run.received", {
37469
+ dispatchId,
37470
+ sourceSessionId,
37471
+ targetPersona: args.targetPersona,
37472
+ promptLen: args.prompt.length
37473
+ });
37474
+ void spawnB({
37475
+ dispatchId,
37476
+ sourceSessionId,
37477
+ targetPersona: args.targetPersona,
37478
+ prompt: args.prompt
37479
+ }).then(() => {
37480
+ logger?.info("dispatch.spawnB.ok", { dispatchId });
37481
+ }).catch((err) => {
37482
+ const reason = err instanceof Error ? err.message : String(err);
37483
+ logger?.warn("dispatch.spawnB.failed", { dispatchId, reason });
37484
+ mgr.complete(dispatchId, {
37485
+ kind: "failure",
37486
+ reason: `failed to spawn B: ${reason}`
37487
+ });
37488
+ });
37489
+ logger?.info("dispatch.run.waiting", { dispatchId });
37490
+ const outcome = await mgr.wait(dispatchId);
37491
+ logger?.info("dispatch.run.resolved", {
37492
+ dispatchId,
37493
+ outcomeKind: outcome.kind
37494
+ });
37495
+ return {
37496
+ response: { type: "personaDispatch:run:ok", outcome }
37497
+ };
37498
+ };
37499
+ const complete = async (frame) => {
37500
+ const { type: _t, requestId: _r, ...rest } = frame;
37501
+ const args = DispatchCompleteArgsSchema.parse(rest);
37502
+ logger?.info("dispatch.complete.received", {
37503
+ dispatchId: args.dispatchId,
37504
+ outcomeKind: args.outcome.kind
37505
+ });
37506
+ mgr.complete(args.dispatchId, args.outcome);
37507
+ return {
37508
+ response: { type: "personaDispatch:complete:ok" }
37509
+ };
37510
+ };
37511
+ return {
37512
+ "personaDispatch:run": run,
37513
+ "personaDispatch:complete": complete
37514
+ };
37515
+ }
37516
+
36818
37517
  // src/tools/codex-history.ts
36819
37518
  var import_node_child_process5 = require("child_process");
36820
37519
  var DEFAULT_TIMEOUT_MS = 8e3;
@@ -37001,13 +37700,13 @@ function mapSkillsListResponse(res) {
37001
37700
  const r = s ?? {};
37002
37701
  const name = str3(r.name);
37003
37702
  if (!name) continue;
37004
- const path58 = str3(r.path);
37703
+ const path59 = str3(r.path);
37005
37704
  const description = str3(r.description);
37006
37705
  const isPlugin = name.includes(":");
37007
37706
  out.push({
37008
37707
  name,
37009
37708
  source: isPlugin ? "plugin" : "project",
37010
- ...path58 ? { path: path58 } : {},
37709
+ ...path59 ? { path: path59 } : {},
37011
37710
  ...description ? { description } : {},
37012
37711
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
37013
37712
  });
@@ -37046,23 +37745,23 @@ async function listCodexSkills(cwd, deps = {}) {
37046
37745
  }
37047
37746
 
37048
37747
  // src/workspace/browser.ts
37049
- var import_node_fs12 = __toESM(require("fs"), 1);
37748
+ var import_node_fs13 = __toESM(require("fs"), 1);
37050
37749
  var import_node_os6 = __toESM(require("os"), 1);
37051
- var import_node_path10 = __toESM(require("path"), 1);
37750
+ var import_node_path11 = __toESM(require("path"), 1);
37052
37751
  init_protocol();
37053
37752
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
37054
37753
  function resolveInsideCwd(cwd, subpath) {
37055
- const absCwd = import_node_path10.default.resolve(cwd);
37056
- const joined = import_node_path10.default.resolve(absCwd, subpath ?? ".");
37057
- const rel = import_node_path10.default.relative(absCwd, joined);
37058
- if (rel.startsWith("..") || import_node_path10.default.isAbsolute(rel)) {
37754
+ const absCwd = import_node_path11.default.resolve(cwd);
37755
+ const joined = import_node_path11.default.resolve(absCwd, subpath ?? ".");
37756
+ const rel = import_node_path11.default.relative(absCwd, joined);
37757
+ if (rel.startsWith("..") || import_node_path11.default.isAbsolute(rel)) {
37059
37758
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
37060
37759
  }
37061
37760
  return joined;
37062
37761
  }
37063
37762
  function ensureCwd(cwd) {
37064
37763
  try {
37065
- const stat = import_node_fs12.default.statSync(cwd);
37764
+ const stat = import_node_fs13.default.statSync(cwd);
37066
37765
  if (!stat.isDirectory()) {
37067
37766
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
37068
37767
  }
@@ -37076,7 +37775,7 @@ var WorkspaceBrowser = class {
37076
37775
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os6.default.homedir();
37077
37776
  ensureCwd(cwd);
37078
37777
  const full = resolveInsideCwd(cwd, args.path);
37079
- const dirents = import_node_fs12.default.readdirSync(full, { withFileTypes: true });
37778
+ const dirents = import_node_fs13.default.readdirSync(full, { withFileTypes: true });
37080
37779
  const entries = [];
37081
37780
  for (const d of dirents) {
37082
37781
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -37086,7 +37785,7 @@ var WorkspaceBrowser = class {
37086
37785
  mtime: ""
37087
37786
  };
37088
37787
  try {
37089
- const st = import_node_fs12.default.statSync(import_node_path10.default.join(full, d.name));
37788
+ const st = import_node_fs13.default.statSync(import_node_path11.default.join(full, d.name));
37090
37789
  entry.mtime = new Date(st.mtimeMs).toISOString();
37091
37790
  if (d.isFile()) entry.size = st.size;
37092
37791
  } catch {
@@ -37102,14 +37801,14 @@ var WorkspaceBrowser = class {
37102
37801
  read(args) {
37103
37802
  ensureCwd(args.cwd);
37104
37803
  const full = resolveInsideCwd(args.cwd, args.path);
37105
- const st = import_node_fs12.default.statSync(full);
37804
+ const st = import_node_fs13.default.statSync(full);
37106
37805
  if (!st.isFile()) {
37107
37806
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
37108
37807
  }
37109
37808
  if (st.size > MAX_FILE_BYTES) {
37110
37809
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
37111
37810
  }
37112
- const buf = import_node_fs12.default.readFileSync(full);
37811
+ const buf = import_node_fs13.default.readFileSync(full);
37113
37812
  const isBinary = buf.includes(0);
37114
37813
  if (isBinary) {
37115
37814
  return {
@@ -37131,20 +37830,20 @@ var WorkspaceBrowser = class {
37131
37830
  };
37132
37831
 
37133
37832
  // src/skills/agents-scanner.ts
37134
- var import_node_fs13 = __toESM(require("fs"), 1);
37833
+ var import_node_fs14 = __toESM(require("fs"), 1);
37135
37834
  var import_node_os7 = __toESM(require("os"), 1);
37136
- var import_node_path11 = __toESM(require("path"), 1);
37835
+ var import_node_path12 = __toESM(require("path"), 1);
37137
37836
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
37138
37837
  function isDirLikeSync2(p2) {
37139
37838
  try {
37140
- return import_node_fs13.default.statSync(p2).isDirectory();
37839
+ return import_node_fs14.default.statSync(p2).isDirectory();
37141
37840
  } catch {
37142
37841
  return false;
37143
37842
  }
37144
37843
  }
37145
37844
  function fileExistsSync(p2) {
37146
37845
  try {
37147
- return import_node_fs13.default.statSync(p2).isFile();
37846
+ return import_node_fs14.default.statSync(p2).isFile();
37148
37847
  } catch {
37149
37848
  return false;
37150
37849
  }
@@ -37152,7 +37851,7 @@ function fileExistsSync(p2) {
37152
37851
  function parseAgentFile(filePath) {
37153
37852
  let content;
37154
37853
  try {
37155
- content = import_node_fs13.default.readFileSync(filePath, "utf8");
37854
+ content = import_node_fs14.default.readFileSync(filePath, "utf8");
37156
37855
  } catch {
37157
37856
  return {};
37158
37857
  }
@@ -37165,16 +37864,16 @@ function parseAgentFile(filePath) {
37165
37864
  function scanAgentsDir(dir, source, seen, out) {
37166
37865
  let entries;
37167
37866
  try {
37168
- entries = import_node_fs13.default.readdirSync(dir, { withFileTypes: true });
37867
+ entries = import_node_fs14.default.readdirSync(dir, { withFileTypes: true });
37169
37868
  } catch {
37170
37869
  return;
37171
37870
  }
37172
37871
  for (const ent of entries) {
37173
37872
  if (!ent.name.endsWith(".md")) continue;
37174
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path11.default.join(dir, ent.name)))) {
37873
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path12.default.join(dir, ent.name)))) {
37175
37874
  continue;
37176
37875
  }
37177
- const filePath = import_node_path11.default.join(dir, ent.name);
37876
+ const filePath = import_node_path12.default.join(dir, ent.name);
37178
37877
  const baseName = ent.name.replace(/\.md$/, "");
37179
37878
  if (seen.has(baseName)) continue;
37180
37879
  seen.add(baseName);
@@ -37192,12 +37891,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
37192
37891
  function walk2(dir, namespaces) {
37193
37892
  let entries;
37194
37893
  try {
37195
- entries = import_node_fs13.default.readdirSync(dir, { withFileTypes: true });
37894
+ entries = import_node_fs14.default.readdirSync(dir, { withFileTypes: true });
37196
37895
  } catch {
37197
37896
  return;
37198
37897
  }
37199
37898
  for (const ent of entries) {
37200
- const childPath = import_node_path11.default.join(dir, ent.name);
37899
+ const childPath = import_node_path12.default.join(dir, ent.name);
37201
37900
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
37202
37901
  walk2(childPath, [...namespaces, ent.name]);
37203
37902
  continue;
@@ -37222,13 +37921,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
37222
37921
  walk2(root, []);
37223
37922
  }
37224
37923
  function readInstalledPlugins2(home) {
37225
- const pluginsDir = import_node_path11.default.join(home, ".claude", "plugins");
37226
- const v2 = import_node_path11.default.join(pluginsDir, "installed_plugins_v2.json");
37227
- const v1 = import_node_path11.default.join(pluginsDir, "installed_plugins.json");
37924
+ const pluginsDir = import_node_path12.default.join(home, ".claude", "plugins");
37925
+ const v2 = import_node_path12.default.join(pluginsDir, "installed_plugins_v2.json");
37926
+ const v1 = import_node_path12.default.join(pluginsDir, "installed_plugins.json");
37228
37927
  let raw = null;
37229
37928
  for (const candidate of [v2, v1]) {
37230
37929
  try {
37231
- raw = import_node_fs13.default.readFileSync(candidate, "utf8");
37930
+ raw = import_node_fs14.default.readFileSync(candidate, "utf8");
37232
37931
  break;
37233
37932
  } catch {
37234
37933
  }
@@ -37253,19 +37952,19 @@ function readInstalledPlugins2(home) {
37253
37952
  return out;
37254
37953
  }
37255
37954
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
37256
- let cur = import_node_path11.default.resolve(startCwd);
37257
- const fsRoot = import_node_path11.default.parse(cur).root;
37955
+ let cur = import_node_path12.default.resolve(startCwd);
37956
+ const fsRoot = import_node_path12.default.parse(cur).root;
37258
37957
  while (true) {
37259
- scanAgentsDir(import_node_path11.default.join(cur, ".claude", "agents"), "project", seen, out);
37958
+ scanAgentsDir(import_node_path12.default.join(cur, ".claude", "agents"), "project", seen, out);
37260
37959
  let hasGit = false;
37261
37960
  try {
37262
- hasGit = import_node_fs13.default.existsSync(import_node_path11.default.join(cur, ".git"));
37961
+ hasGit = import_node_fs14.default.existsSync(import_node_path12.default.join(cur, ".git"));
37263
37962
  } catch {
37264
37963
  }
37265
37964
  if (hasGit) return;
37266
37965
  if (cur === home) return;
37267
37966
  if (cur === fsRoot) return;
37268
- const parent = import_node_path11.default.dirname(cur);
37967
+ const parent = import_node_path12.default.dirname(cur);
37269
37968
  if (parent === cur) return;
37270
37969
  cur = parent;
37271
37970
  }
@@ -37300,7 +37999,7 @@ var AgentsScanner = class {
37300
37999
  }
37301
38000
  const fsBlock = [];
37302
38001
  scanAgentsDir(
37303
- import_node_path11.default.join(this.home, ".claude", "agents"),
38002
+ import_node_path12.default.join(this.home, ".claude", "agents"),
37304
38003
  "global",
37305
38004
  seen,
37306
38005
  fsBlock
@@ -37314,7 +38013,7 @@ var AgentsScanner = class {
37314
38013
  ...this.extraPluginRoots
37315
38014
  ];
37316
38015
  for (const { name, root } of plugins) {
37317
- const agentsRoot = import_node_path11.default.join(root, "agents");
38016
+ const agentsRoot = import_node_path12.default.join(root, "agents");
37318
38017
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
37319
38018
  }
37320
38019
  return [...builtinBlock, ...fsBlock];
@@ -37322,15 +38021,15 @@ var AgentsScanner = class {
37322
38021
  };
37323
38022
 
37324
38023
  // src/observer/session-observer.ts
37325
- var import_node_fs15 = __toESM(require("fs"), 1);
38024
+ var import_node_fs16 = __toESM(require("fs"), 1);
37326
38025
  var import_node_os9 = __toESM(require("os"), 1);
37327
- var import_node_path13 = __toESM(require("path"), 1);
38026
+ var import_node_path14 = __toESM(require("path"), 1);
37328
38027
  init_claude_history();
37329
38028
 
37330
38029
  // src/observer/subagent-meta-observer.ts
37331
- var import_node_fs14 = __toESM(require("fs"), 1);
38030
+ var import_node_fs15 = __toESM(require("fs"), 1);
37332
38031
  var import_node_os8 = __toESM(require("os"), 1);
37333
- var import_node_path12 = __toESM(require("path"), 1);
38032
+ var import_node_path13 = __toESM(require("path"), 1);
37334
38033
  init_claude_history();
37335
38034
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
37336
38035
  var SubagentMetaObserver = class {
@@ -37343,7 +38042,7 @@ var SubagentMetaObserver = class {
37343
38042
  watches = /* @__PURE__ */ new Map();
37344
38043
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
37345
38044
  resolveSubagentDir(cwd, toolSessionId) {
37346
- return import_node_path12.default.join(
38045
+ return import_node_path13.default.join(
37347
38046
  this.home,
37348
38047
  ".claude",
37349
38048
  "projects",
@@ -37373,7 +38072,7 @@ var SubagentMetaObserver = class {
37373
38072
  attachWatcher(w2) {
37374
38073
  if (w2.watcher) return;
37375
38074
  try {
37376
- w2.watcher = import_node_fs14.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
38075
+ w2.watcher = import_node_fs15.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
37377
38076
  if (!name) return;
37378
38077
  const m2 = META_RE.exec(String(name));
37379
38078
  if (!m2) return;
@@ -37385,7 +38084,7 @@ var SubagentMetaObserver = class {
37385
38084
  scan(w2) {
37386
38085
  let entries;
37387
38086
  try {
37388
- entries = import_node_fs14.default.readdirSync(w2.dirPath);
38087
+ entries = import_node_fs15.default.readdirSync(w2.dirPath);
37389
38088
  } catch {
37390
38089
  return;
37391
38090
  }
@@ -37399,10 +38098,10 @@ var SubagentMetaObserver = class {
37399
38098
  if (!m2) return;
37400
38099
  const agentId = m2[1];
37401
38100
  if (w2.emitted.has(agentId)) return;
37402
- const file = import_node_path12.default.join(w2.dirPath, name);
38101
+ const file = import_node_path13.default.join(w2.dirPath, name);
37403
38102
  let raw;
37404
38103
  try {
37405
- raw = import_node_fs14.default.readFileSync(file, "utf8");
38104
+ raw = import_node_fs15.default.readFileSync(file, "utf8");
37406
38105
  } catch {
37407
38106
  return;
37408
38107
  }
@@ -37459,14 +38158,14 @@ var SessionObserver = class {
37459
38158
  metaObserver;
37460
38159
  resolveJsonlPath(cwd, toolSessionId, override) {
37461
38160
  if (override) return override;
37462
- return import_node_path13.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
38161
+ return import_node_path14.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
37463
38162
  }
37464
38163
  start(args) {
37465
38164
  this.stop(args.sessionId);
37466
38165
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
37467
38166
  let size = 0;
37468
38167
  try {
37469
- size = import_node_fs15.default.statSync(filePath).size;
38168
+ size = import_node_fs16.default.statSync(filePath).size;
37470
38169
  } catch {
37471
38170
  }
37472
38171
  const w2 = {
@@ -37480,10 +38179,10 @@ var SessionObserver = class {
37480
38179
  prevIsRejectSentinel: false
37481
38180
  };
37482
38181
  try {
37483
- import_node_fs15.default.mkdirSync(import_node_path13.default.dirname(filePath), { recursive: true });
38182
+ import_node_fs16.default.mkdirSync(import_node_path14.default.dirname(filePath), { recursive: true });
37484
38183
  } catch {
37485
38184
  }
37486
- w2.watcher = import_node_fs15.default.watch(import_node_path13.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
38185
+ w2.watcher = import_node_fs16.default.watch(import_node_path14.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
37487
38186
  if (!changedName || !filePath.endsWith(changedName)) return;
37488
38187
  this.poll(w2);
37489
38188
  });
@@ -37506,7 +38205,7 @@ var SessionObserver = class {
37506
38205
  // 异常静默吞,不阻塞 watcher 启动
37507
38206
  hydrateMetaTail(w2, maxLines = 200) {
37508
38207
  try {
37509
- const raw = import_node_fs15.default.readFileSync(w2.filePath, "utf8");
38208
+ const raw = import_node_fs16.default.readFileSync(w2.filePath, "utf8");
37510
38209
  if (!raw) return;
37511
38210
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
37512
38211
  if (allLines.length === 0) return;
@@ -37530,7 +38229,7 @@ var SessionObserver = class {
37530
38229
  poll(w2) {
37531
38230
  let size = 0;
37532
38231
  try {
37533
- size = import_node_fs15.default.statSync(w2.filePath).size;
38232
+ size = import_node_fs16.default.statSync(w2.filePath).size;
37534
38233
  } catch {
37535
38234
  return;
37536
38235
  }
@@ -37539,11 +38238,11 @@ var SessionObserver = class {
37539
38238
  w2.buf = "";
37540
38239
  }
37541
38240
  if (size === w2.lastSize) return;
37542
- const fd = import_node_fs15.default.openSync(w2.filePath, "r");
38241
+ const fd = import_node_fs16.default.openSync(w2.filePath, "r");
37543
38242
  try {
37544
38243
  const len = size - w2.lastSize;
37545
38244
  const buf = Buffer.alloc(len);
37546
- import_node_fs15.default.readSync(fd, buf, 0, len, w2.lastSize);
38245
+ import_node_fs16.default.readSync(fd, buf, 0, len, w2.lastSize);
37547
38246
  w2.lastSize = size;
37548
38247
  w2.buf += buf.toString("utf8");
37549
38248
  let newlineIndex;
@@ -37566,7 +38265,7 @@ var SessionObserver = class {
37566
38265
  }
37567
38266
  }
37568
38267
  } finally {
37569
- import_node_fs15.default.closeSync(fd);
38268
+ import_node_fs16.default.closeSync(fd);
37570
38269
  }
37571
38270
  }
37572
38271
  // 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
@@ -38289,14 +38988,14 @@ async function authenticate(token, deps) {
38289
38988
  }
38290
38989
 
38291
38990
  // src/permission/capability-store.ts
38292
- var fs18 = __toESM(require("fs"), 1);
38293
- var path18 = __toESM(require("path"), 1);
38991
+ var fs19 = __toESM(require("fs"), 1);
38992
+ var path19 = __toESM(require("path"), 1);
38294
38993
  var CAPABILITIES_FILE_NAME = "capabilities.json";
38295
38994
  var FILE_VERSION = 1;
38296
38995
  var CapabilityStore = class {
38297
38996
  constructor(dataDir) {
38298
38997
  this.dataDir = dataDir;
38299
- fs18.mkdirSync(dataDir, { recursive: true });
38998
+ fs19.mkdirSync(dataDir, { recursive: true });
38300
38999
  this.cache = this.readFromDisk();
38301
39000
  }
38302
39001
  dataDir;
@@ -38320,13 +39019,13 @@ var CapabilityStore = class {
38320
39019
  this.flush();
38321
39020
  }
38322
39021
  filePath() {
38323
- return path18.join(this.dataDir, CAPABILITIES_FILE_NAME);
39022
+ return path19.join(this.dataDir, CAPABILITIES_FILE_NAME);
38324
39023
  }
38325
39024
  readFromDisk() {
38326
39025
  const file = this.filePath();
38327
39026
  let raw;
38328
39027
  try {
38329
- raw = fs18.readFileSync(file, "utf8");
39028
+ raw = fs19.readFileSync(file, "utf8");
38330
39029
  } catch (err) {
38331
39030
  if (err?.code === "ENOENT") return [];
38332
39031
  return [];
@@ -38354,10 +39053,10 @@ var CapabilityStore = class {
38354
39053
  }
38355
39054
  atomicWrite(file, content) {
38356
39055
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
38357
- fs18.writeFileSync(tmp, content, { mode: 384 });
38358
- fs18.renameSync(tmp, file);
39056
+ fs19.writeFileSync(tmp, content, { mode: 384 });
39057
+ fs19.renameSync(tmp, file);
38359
39058
  try {
38360
- fs18.chmodSync(file, 384);
39059
+ fs19.chmodSync(file, 384);
38361
39060
  } catch {
38362
39061
  }
38363
39062
  }
@@ -38450,14 +39149,14 @@ var CapabilityManager = class {
38450
39149
  };
38451
39150
 
38452
39151
  // src/permission/cleanup.ts
38453
- var fs19 = __toESM(require("fs"), 1);
39152
+ var fs20 = __toESM(require("fs"), 1);
38454
39153
  function cleanupGuestSessionsForCapability(cap, factory) {
38455
39154
  const removed = [];
38456
39155
  for (const g2 of cap.grants) {
38457
39156
  if (g2.resource.type !== "persona") continue;
38458
39157
  const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
38459
39158
  try {
38460
- fs19.rmSync(dir, { recursive: true, force: true });
39159
+ fs20.rmSync(dir, { recursive: true, force: true });
38461
39160
  removed.push(dir);
38462
39161
  } catch {
38463
39162
  }
@@ -38466,13 +39165,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
38466
39165
  }
38467
39166
 
38468
39167
  // src/inbox/inbox-store.ts
38469
- var fs20 = __toESM(require("fs"), 1);
38470
- var path19 = __toESM(require("path"), 1);
39168
+ var fs21 = __toESM(require("fs"), 1);
39169
+ var path20 = __toESM(require("path"), 1);
38471
39170
  var INBOX_SUBDIR = "inbox";
38472
39171
  var InboxStore = class {
38473
39172
  constructor(dataDir) {
38474
39173
  this.dataDir = dataDir;
38475
- fs20.mkdirSync(this.dirPath(), { recursive: true });
39174
+ fs21.mkdirSync(this.dirPath(), { recursive: true });
38476
39175
  }
38477
39176
  dataDir;
38478
39177
  /**
@@ -38484,7 +39183,7 @@ var InboxStore = class {
38484
39183
  const file = this.filePath(peerDeviceId);
38485
39184
  let raw;
38486
39185
  try {
38487
- raw = fs20.readFileSync(file, "utf8");
39186
+ raw = fs21.readFileSync(file, "utf8");
38488
39187
  } catch (err) {
38489
39188
  if (err?.code === "ENOENT") return [];
38490
39189
  return [];
@@ -38500,7 +39199,7 @@ var InboxStore = class {
38500
39199
  const dir = this.dirPath();
38501
39200
  let entries;
38502
39201
  try {
38503
- entries = fs20.readdirSync(dir);
39202
+ entries = fs21.readdirSync(dir);
38504
39203
  } catch (err) {
38505
39204
  if (err?.code === "ENOENT") return [];
38506
39205
  return [];
@@ -38516,9 +39215,9 @@ var InboxStore = class {
38516
39215
  if (existing.some((m2) => m2.id === message.id)) return;
38517
39216
  const file = this.filePath(message.peerDeviceId);
38518
39217
  const line = JSON.stringify(message) + "\n";
38519
- fs20.appendFileSync(file, line, { mode: 384 });
39218
+ fs21.appendFileSync(file, line, { mode: 384 });
38520
39219
  try {
38521
- fs20.chmodSync(file, 384);
39220
+ fs21.chmodSync(file, 384);
38522
39221
  } catch {
38523
39222
  }
38524
39223
  }
@@ -38548,7 +39247,7 @@ var InboxStore = class {
38548
39247
  removeByPeerDeviceId(peerDeviceId) {
38549
39248
  const file = this.filePath(peerDeviceId);
38550
39249
  try {
38551
- fs20.unlinkSync(file);
39250
+ fs21.unlinkSync(file);
38552
39251
  } catch (err) {
38553
39252
  if (err?.code === "ENOENT") return;
38554
39253
  }
@@ -38557,18 +39256,18 @@ var InboxStore = class {
38557
39256
  const file = this.filePath(peerDeviceId);
38558
39257
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
38559
39258
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
38560
- fs20.writeFileSync(tmp, content, { mode: 384 });
38561
- fs20.renameSync(tmp, file);
39259
+ fs21.writeFileSync(tmp, content, { mode: 384 });
39260
+ fs21.renameSync(tmp, file);
38562
39261
  try {
38563
- fs20.chmodSync(file, 384);
39262
+ fs21.chmodSync(file, 384);
38564
39263
  } catch {
38565
39264
  }
38566
39265
  }
38567
39266
  dirPath() {
38568
- return path19.join(this.dataDir, INBOX_SUBDIR);
39267
+ return path20.join(this.dataDir, INBOX_SUBDIR);
38569
39268
  }
38570
39269
  filePath(peerDeviceId) {
38571
- return path19.join(this.dirPath(), `${peerDeviceId}.jsonl`);
39270
+ return path20.join(this.dirPath(), `${peerDeviceId}.jsonl`);
38572
39271
  }
38573
39272
  };
38574
39273
  function parseAllLines(raw) {
@@ -38655,8 +39354,8 @@ var InboxManager = class {
38655
39354
  };
38656
39355
 
38657
39356
  // src/state/contact-store.ts
38658
- var fs21 = __toESM(require("fs"), 1);
38659
- var path20 = __toESM(require("path"), 1);
39357
+ var fs22 = __toESM(require("fs"), 1);
39358
+ var path21 = __toESM(require("path"), 1);
38660
39359
  var FILE_NAME = "contacts.json";
38661
39360
  var ContactStore = class {
38662
39361
  constructor(dataDir) {
@@ -38666,10 +39365,10 @@ var ContactStore = class {
38666
39365
  contacts = /* @__PURE__ */ new Map();
38667
39366
  load() {
38668
39367
  this.contacts.clear();
38669
- const file = path20.join(this.dataDir, FILE_NAME);
39368
+ const file = path21.join(this.dataDir, FILE_NAME);
38670
39369
  let raw;
38671
39370
  try {
38672
- raw = fs21.readFileSync(file, "utf8");
39371
+ raw = fs22.readFileSync(file, "utf8");
38673
39372
  } catch (err) {
38674
39373
  if (err?.code !== "ENOENT") this.renameBak(file);
38675
39374
  return;
@@ -38712,20 +39411,20 @@ var ContactStore = class {
38712
39411
  return existed;
38713
39412
  }
38714
39413
  flush() {
38715
- const file = path20.join(this.dataDir, FILE_NAME);
39414
+ const file = path21.join(this.dataDir, FILE_NAME);
38716
39415
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
38717
39416
  const content = JSON.stringify(
38718
39417
  { contacts: Array.from(this.contacts.values()) },
38719
39418
  null,
38720
39419
  2
38721
39420
  );
38722
- fs21.mkdirSync(this.dataDir, { recursive: true });
38723
- fs21.writeFileSync(tmp, content, { mode: 384 });
38724
- fs21.renameSync(tmp, file);
39421
+ fs22.mkdirSync(this.dataDir, { recursive: true });
39422
+ fs22.writeFileSync(tmp, content, { mode: 384 });
39423
+ fs22.renameSync(tmp, file);
38725
39424
  }
38726
39425
  renameBak(file) {
38727
39426
  try {
38728
- fs21.renameSync(file, `${file}.bak`);
39427
+ fs22.renameSync(file, `${file}.bak`);
38729
39428
  } catch {
38730
39429
  }
38731
39430
  }
@@ -38875,61 +39574,61 @@ async function autoReverseContact(args) {
38875
39574
  }
38876
39575
 
38877
39576
  // src/migrations/2026-05-20-flatten-sessions.ts
38878
- var fs22 = __toESM(require("fs"), 1);
38879
- var path21 = __toESM(require("path"), 1);
39577
+ var fs23 = __toESM(require("fs"), 1);
39578
+ var path22 = __toESM(require("path"), 1);
38880
39579
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
38881
39580
  function migrateFlattenSessions(opts) {
38882
39581
  const dataDir = opts.dataDir;
38883
39582
  const now = opts.now ?? Date.now;
38884
- const sessionsDir = path21.join(dataDir, "sessions");
38885
- const flagPath = path21.join(sessionsDir, MIGRATION_FLAG_NAME);
39583
+ const sessionsDir = path22.join(dataDir, "sessions");
39584
+ const flagPath = path22.join(sessionsDir, MIGRATION_FLAG_NAME);
38886
39585
  if (existsSync3(flagPath)) {
38887
39586
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
38888
39587
  }
38889
39588
  let movedBare = 0;
38890
39589
  let movedVmOwner = 0;
38891
39590
  let archivedListener = 0;
38892
- const defaultDir = path21.join(sessionsDir, "default");
39591
+ const defaultDir = path22.join(sessionsDir, "default");
38893
39592
  if (existsSync3(defaultDir)) {
38894
39593
  for (const entry of readdirSafe(defaultDir)) {
38895
39594
  if (!entry.endsWith(".json")) continue;
38896
- const src = path21.join(defaultDir, entry);
38897
- const dst = path21.join(sessionsDir, entry);
38898
- fs22.renameSync(src, dst);
39595
+ const src = path22.join(defaultDir, entry);
39596
+ const dst = path22.join(sessionsDir, entry);
39597
+ fs23.renameSync(src, dst);
38899
39598
  movedBare += 1;
38900
39599
  }
38901
39600
  rmdirIfEmpty(defaultDir);
38902
39601
  }
38903
39602
  for (const pid of readdirSafe(sessionsDir)) {
38904
- const personaDir = path21.join(sessionsDir, pid);
39603
+ const personaDir = path22.join(sessionsDir, pid);
38905
39604
  if (!isDir(personaDir)) continue;
38906
39605
  if (pid === "default") continue;
38907
- const ownerSrc = path21.join(personaDir, "owner");
39606
+ const ownerSrc = path22.join(personaDir, "owner");
38908
39607
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
38909
- const ownerDst = path21.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
38910
- fs22.mkdirSync(ownerDst, { recursive: true });
39608
+ const ownerDst = path22.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
39609
+ fs23.mkdirSync(ownerDst, { recursive: true });
38911
39610
  for (const file of readdirSafe(ownerSrc)) {
38912
39611
  if (!file.endsWith(".json")) continue;
38913
- fs22.renameSync(path21.join(ownerSrc, file), path21.join(ownerDst, file));
39612
+ fs23.renameSync(path22.join(ownerSrc, file), path22.join(ownerDst, file));
38914
39613
  movedVmOwner += 1;
38915
39614
  }
38916
39615
  rmdirIfEmpty(ownerSrc);
38917
39616
  }
38918
- const listenerSrc = path21.join(personaDir, "listener");
39617
+ const listenerSrc = path22.join(personaDir, "listener");
38919
39618
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
38920
- const archiveDst = path21.join(dataDir, ".legacy", `listener-${pid}`);
38921
- fs22.mkdirSync(archiveDst, { recursive: true });
39619
+ const archiveDst = path22.join(dataDir, ".legacy", `listener-${pid}`);
39620
+ fs23.mkdirSync(archiveDst, { recursive: true });
38922
39621
  for (const file of readdirSafe(listenerSrc)) {
38923
39622
  if (!file.endsWith(".json")) continue;
38924
- fs22.renameSync(path21.join(listenerSrc, file), path21.join(archiveDst, file));
39623
+ fs23.renameSync(path22.join(listenerSrc, file), path22.join(archiveDst, file));
38925
39624
  archivedListener += 1;
38926
39625
  }
38927
39626
  rmdirIfEmpty(listenerSrc);
38928
39627
  }
38929
39628
  rmdirIfEmpty(personaDir);
38930
39629
  }
38931
- fs22.mkdirSync(sessionsDir, { recursive: true });
38932
- fs22.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
39630
+ fs23.mkdirSync(sessionsDir, { recursive: true });
39631
+ fs23.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
38933
39632
  return {
38934
39633
  skipped: false,
38935
39634
  flagWritten: true,
@@ -38940,7 +39639,7 @@ function migrateFlattenSessions(opts) {
38940
39639
  }
38941
39640
  function existsSync3(p2) {
38942
39641
  try {
38943
- fs22.statSync(p2);
39642
+ fs23.statSync(p2);
38944
39643
  return true;
38945
39644
  } catch {
38946
39645
  return false;
@@ -38948,31 +39647,31 @@ function existsSync3(p2) {
38948
39647
  }
38949
39648
  function isDir(p2) {
38950
39649
  try {
38951
- return fs22.statSync(p2).isDirectory();
39650
+ return fs23.statSync(p2).isDirectory();
38952
39651
  } catch {
38953
39652
  return false;
38954
39653
  }
38955
39654
  }
38956
39655
  function readdirSafe(p2) {
38957
39656
  try {
38958
- return fs22.readdirSync(p2);
39657
+ return fs23.readdirSync(p2);
38959
39658
  } catch {
38960
39659
  return [];
38961
39660
  }
38962
39661
  }
38963
39662
  function rmdirIfEmpty(p2) {
38964
39663
  try {
38965
- fs22.rmdirSync(p2);
39664
+ fs23.rmdirSync(p2);
38966
39665
  } catch {
38967
39666
  }
38968
39667
  }
38969
39668
 
38970
39669
  // src/transport/http-router.ts
38971
- var import_node_fs17 = __toESM(require("fs"), 1);
38972
- var import_node_path17 = __toESM(require("path"), 1);
39670
+ var import_node_fs18 = __toESM(require("fs"), 1);
39671
+ var import_node_path18 = __toESM(require("path"), 1);
38973
39672
 
38974
39673
  // src/attachment/mime.ts
38975
- var import_node_path14 = __toESM(require("path"), 1);
39674
+ var import_node_path15 = __toESM(require("path"), 1);
38976
39675
  var TEXT_PLAIN = "text/plain; charset=utf-8";
38977
39676
  var EXT_TO_NATIVE_MIME = {
38978
39677
  // 图片
@@ -39079,7 +39778,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
39079
39778
  ".mk"
39080
39779
  ]);
39081
39780
  function lookupMime(filePathOrName) {
39082
- const ext = import_node_path14.default.extname(filePathOrName).toLowerCase();
39781
+ const ext = import_node_path15.default.extname(filePathOrName).toLowerCase();
39083
39782
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
39084
39783
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
39085
39784
  return "application/octet-stream";
@@ -39148,8 +39847,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
39148
39847
  }
39149
39848
 
39150
39849
  // src/attachment/upload.ts
39151
- var import_node_fs16 = __toESM(require("fs"), 1);
39152
- var import_node_path15 = __toESM(require("path"), 1);
39850
+ var import_node_fs17 = __toESM(require("fs"), 1);
39851
+ var import_node_path16 = __toESM(require("path"), 1);
39153
39852
  var import_node_crypto5 = __toESM(require("crypto"), 1);
39154
39853
  var import_promises = require("stream/promises");
39155
39854
  var UploadError = class extends Error {
@@ -39161,22 +39860,22 @@ var UploadError = class extends Error {
39161
39860
  code;
39162
39861
  };
39163
39862
  function assertValidFileName(fileName) {
39164
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path15.default.basename(fileName)) {
39863
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path16.default.basename(fileName)) {
39165
39864
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
39166
39865
  }
39167
39866
  }
39168
39867
  var HASH_PREFIX_LEN = 16;
39169
39868
  async function writeUploadedAttachment(args) {
39170
39869
  assertValidFileName(args.fileName);
39171
- const attachmentsRoot = import_node_path15.default.join(args.sessionDir, ".attachments");
39870
+ const attachmentsRoot = import_node_path16.default.join(args.sessionDir, ".attachments");
39172
39871
  try {
39173
- import_node_fs16.default.mkdirSync(attachmentsRoot, { recursive: true });
39872
+ import_node_fs17.default.mkdirSync(attachmentsRoot, { recursive: true });
39174
39873
  } catch (err) {
39175
39874
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
39176
39875
  }
39177
39876
  const hasher = import_node_crypto5.default.createHash("sha256");
39178
39877
  let actualSize = 0;
39179
- const tmpPath = import_node_path15.default.join(
39878
+ const tmpPath = import_node_path16.default.join(
39180
39879
  attachmentsRoot,
39181
39880
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto5.default.randomBytes(4).toString("hex")}`
39182
39881
  );
@@ -39191,18 +39890,18 @@ async function writeUploadedAttachment(args) {
39191
39890
  yield buf;
39192
39891
  }
39193
39892
  },
39194
- import_node_fs16.default.createWriteStream(tmpPath, { mode: 384 })
39893
+ import_node_fs17.default.createWriteStream(tmpPath, { mode: 384 })
39195
39894
  );
39196
39895
  } catch (err) {
39197
39896
  try {
39198
- import_node_fs16.default.unlinkSync(tmpPath);
39897
+ import_node_fs17.default.unlinkSync(tmpPath);
39199
39898
  } catch {
39200
39899
  }
39201
39900
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
39202
39901
  }
39203
39902
  if (actualSize !== args.contentLength) {
39204
39903
  try {
39205
- import_node_fs16.default.unlinkSync(tmpPath);
39904
+ import_node_fs17.default.unlinkSync(tmpPath);
39206
39905
  } catch {
39207
39906
  }
39208
39907
  throw new UploadError(
@@ -39211,35 +39910,35 @@ async function writeUploadedAttachment(args) {
39211
39910
  );
39212
39911
  }
39213
39912
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
39214
- const hashDir = import_node_path15.default.join(attachmentsRoot, attachmentId);
39913
+ const hashDir = import_node_path16.default.join(attachmentsRoot, attachmentId);
39215
39914
  let finalFileName;
39216
39915
  let hashDirExists = false;
39217
39916
  try {
39218
- hashDirExists = import_node_fs16.default.statSync(hashDir).isDirectory();
39917
+ hashDirExists = import_node_fs17.default.statSync(hashDir).isDirectory();
39219
39918
  } catch {
39220
39919
  }
39221
39920
  if (hashDirExists) {
39222
- const existing = import_node_fs16.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
39921
+ const existing = import_node_fs17.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
39223
39922
  finalFileName = existing[0] ?? args.fileName;
39224
39923
  try {
39225
- import_node_fs16.default.unlinkSync(tmpPath);
39924
+ import_node_fs17.default.unlinkSync(tmpPath);
39226
39925
  } catch {
39227
39926
  }
39228
39927
  } else {
39229
39928
  try {
39230
- import_node_fs16.default.mkdirSync(hashDir, { recursive: true });
39929
+ import_node_fs17.default.mkdirSync(hashDir, { recursive: true });
39231
39930
  finalFileName = args.fileName;
39232
- import_node_fs16.default.renameSync(tmpPath, import_node_path15.default.join(hashDir, finalFileName));
39931
+ import_node_fs17.default.renameSync(tmpPath, import_node_path16.default.join(hashDir, finalFileName));
39233
39932
  } catch (err) {
39234
39933
  try {
39235
- import_node_fs16.default.unlinkSync(tmpPath);
39934
+ import_node_fs17.default.unlinkSync(tmpPath);
39236
39935
  } catch {
39237
39936
  }
39238
39937
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
39239
39938
  }
39240
39939
  }
39241
- const absPath = import_node_path15.default.join(hashDir, finalFileName);
39242
- const relPath = import_node_path15.default.relative(args.sessionDir, absPath);
39940
+ const absPath = import_node_path16.default.join(hashDir, finalFileName);
39941
+ const relPath = import_node_path16.default.relative(args.sessionDir, absPath);
39243
39942
  args.groupFileStore.upsert(args.scope, args.sessionId, {
39244
39943
  relPath: absPath,
39245
39944
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -39253,7 +39952,7 @@ async function writeUploadedAttachment(args) {
39253
39952
 
39254
39953
  // src/extension/import.ts
39255
39954
  var import_promises2 = __toESM(require("fs/promises"), 1);
39256
- var import_node_path16 = __toESM(require("path"), 1);
39955
+ var import_node_path17 = __toESM(require("path"), 1);
39257
39956
  var import_node_os10 = __toESM(require("os"), 1);
39258
39957
  var import_jszip = __toESM(require_lib3(), 1);
39259
39958
  var ImportError = class extends Error {
@@ -39271,7 +39970,7 @@ async function importZip(buf, root) {
39271
39970
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
39272
39971
  }
39273
39972
  for (const name of Object.keys(zip.files)) {
39274
- if (name.includes("..") || name.startsWith("/") || import_node_path16.default.isAbsolute(name)) {
39973
+ if (name.includes("..") || name.startsWith("/") || import_node_path17.default.isAbsolute(name)) {
39275
39974
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
39276
39975
  }
39277
39976
  }
@@ -39304,7 +40003,7 @@ async function importZip(buf, root) {
39304
40003
  );
39305
40004
  }
39306
40005
  }
39307
- const destDir = import_node_path16.default.join(root, manifest.id);
40006
+ const destDir = import_node_path17.default.join(root, manifest.id);
39308
40007
  let destExists = false;
39309
40008
  try {
39310
40009
  await import_promises2.default.access(destDir);
@@ -39314,15 +40013,15 @@ async function importZip(buf, root) {
39314
40013
  if (destExists) {
39315
40014
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
39316
40015
  }
39317
- const stage = await import_promises2.default.mkdtemp(import_node_path16.default.join(import_node_os10.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
40016
+ const stage = await import_promises2.default.mkdtemp(import_node_path17.default.join(import_node_os10.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
39318
40017
  try {
39319
40018
  for (const [name, entry] of Object.entries(zip.files)) {
39320
- const dest = import_node_path16.default.join(stage, name);
40019
+ const dest = import_node_path17.default.join(stage, name);
39321
40020
  if (entry.dir) {
39322
40021
  await import_promises2.default.mkdir(dest, { recursive: true });
39323
40022
  continue;
39324
40023
  }
39325
- await import_promises2.default.mkdir(import_node_path16.default.dirname(dest), { recursive: true });
40024
+ await import_promises2.default.mkdir(import_node_path17.default.dirname(dest), { recursive: true });
39326
40025
  const content = await entry.async("nodebuffer");
39327
40026
  await import_promises2.default.writeFile(dest, content);
39328
40027
  }
@@ -39350,7 +40049,12 @@ var EXPOSED_RPC_METHODS = [
39350
40049
  "appBuilder.setActiveSession",
39351
40050
  // dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
39352
40051
  // 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
39353
- "auth.getIdentity"
40052
+ "auth.getIdentity",
40053
+ // Persona dispatch (Task 4): A 端 dispatch MCP server 调 run(hold-and-wait 一次);
40054
+ // B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
40055
+ // protocol/src/dispatch.ts。
40056
+ "personaDispatch.run",
40057
+ "personaDispatch.complete"
39354
40058
  ];
39355
40059
  async function handleRpcRequest(input) {
39356
40060
  const { method, body, sessionId, dispatcher } = input;
@@ -39439,7 +40143,7 @@ function isValidUploadFileName(fileName) {
39439
40143
  if (fileName === "." || fileName === "..") return false;
39440
40144
  if (fileName.startsWith(".")) return false;
39441
40145
  if (fileName.includes("/") || fileName.includes("\\")) return false;
39442
- return fileName === import_node_path17.default.basename(fileName);
40146
+ return fileName === import_node_path18.default.basename(fileName);
39443
40147
  }
39444
40148
  function createHttpRouter(deps) {
39445
40149
  return async (req, res) => {
@@ -39486,6 +40190,8 @@ function createHttpRouter(deps) {
39486
40190
  sendJson(res, 403, { code: "NOT_LOOPBACK", message: "RPC adapter only accepts loopback connections" });
39487
40191
  return true;
39488
40192
  }
40193
+ req.socket.setTimeout(0);
40194
+ res.socket?.setTimeout(0);
39489
40195
  const method = url.pathname.slice(RPC_ROUTE_PREFIX.length).split("?")[0] ?? "";
39490
40196
  const sessionId = req.headers["x-clawd-session-id"] ?? void 0;
39491
40197
  let body;
@@ -39668,7 +40374,7 @@ function createHttpRouter(deps) {
39668
40374
  return true;
39669
40375
  }
39670
40376
  let absPath;
39671
- if (import_node_path17.default.isAbsolute(pathParam)) {
40377
+ if (import_node_path18.default.isAbsolute(pathParam)) {
39672
40378
  absPath = pathParam;
39673
40379
  } else if (deps.sessionStore) {
39674
40380
  const file = deps.sessionStore.read(sid);
@@ -39676,7 +40382,7 @@ function createHttpRouter(deps) {
39676
40382
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
39677
40383
  return true;
39678
40384
  }
39679
- absPath = import_node_path17.default.join(file.cwd, pathParam);
40385
+ absPath = import_node_path18.default.join(file.cwd, pathParam);
39680
40386
  } else {
39681
40387
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
39682
40388
  return true;
@@ -39759,7 +40465,7 @@ function withCtx(ctx, body) {
39759
40465
  function streamFile(res, absPath, logger) {
39760
40466
  let stat;
39761
40467
  try {
39762
- stat = import_node_fs17.default.statSync(absPath);
40468
+ stat = import_node_fs18.default.statSync(absPath);
39763
40469
  } catch (err) {
39764
40470
  const code = err?.code;
39765
40471
  if (code === "ENOENT") {
@@ -39774,7 +40480,7 @@ function streamFile(res, absPath, logger) {
39774
40480
  return;
39775
40481
  }
39776
40482
  const mime = lookupMime(absPath);
39777
- const basename = import_node_path17.default.basename(absPath);
40483
+ const basename = import_node_path18.default.basename(absPath);
39778
40484
  res.writeHead(200, {
39779
40485
  "Content-Type": mime,
39780
40486
  "Content-Length": String(stat.size),
@@ -39782,7 +40488,7 @@ function streamFile(res, absPath, logger) {
39782
40488
  // 防止浏览器把任意 mime 当 html 渲染
39783
40489
  "X-Content-Type-Options": "nosniff"
39784
40490
  });
39785
- const stream = import_node_fs17.default.createReadStream(absPath);
40491
+ const stream = import_node_fs18.default.createReadStream(absPath);
39786
40492
  stream.on("error", (err) => {
39787
40493
  logger?.warn("streamFile read error", { absPath, err: err.message });
39788
40494
  res.destroy();
@@ -39791,8 +40497,8 @@ function streamFile(res, absPath, logger) {
39791
40497
  }
39792
40498
 
39793
40499
  // src/attachment/gc.ts
39794
- var import_node_fs18 = __toESM(require("fs"), 1);
39795
- var import_node_path18 = __toESM(require("path"), 1);
40500
+ var import_node_fs19 = __toESM(require("fs"), 1);
40501
+ var import_node_path19 = __toESM(require("path"), 1);
39796
40502
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
39797
40503
  function runAttachmentGc(args) {
39798
40504
  const now = (args.now ?? Date.now)();
@@ -39801,38 +40507,38 @@ function runAttachmentGc(args) {
39801
40507
  for (const { scope, sessionId } of args.sessionScopes) {
39802
40508
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
39803
40509
  if (entry.stale) continue;
39804
- if (import_node_path18.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
40510
+ if (import_node_path19.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
39805
40511
  }
39806
40512
  }
39807
40513
  for (const { scope, sessionId } of args.sessionScopes) {
39808
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path18.default.join(
40514
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path19.default.join(
39809
40515
  args.dataDir,
39810
40516
  "sessions",
39811
40517
  ...scopeSubPath(scope).map(safeFileName),
39812
40518
  safeFileName(sessionId)
39813
40519
  );
39814
- const attRoot = import_node_path18.default.join(sessionDir, ".attachments");
40520
+ const attRoot = import_node_path19.default.join(sessionDir, ".attachments");
39815
40521
  let hashDirs;
39816
40522
  try {
39817
- hashDirs = import_node_fs18.default.readdirSync(attRoot);
40523
+ hashDirs = import_node_fs19.default.readdirSync(attRoot);
39818
40524
  } catch (err) {
39819
40525
  if (err.code === "ENOENT") continue;
39820
40526
  args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
39821
40527
  continue;
39822
40528
  }
39823
40529
  for (const hashDir of hashDirs) {
39824
- const hashDirAbs = import_node_path18.default.join(attRoot, hashDir);
40530
+ const hashDirAbs = import_node_path19.default.join(attRoot, hashDir);
39825
40531
  let files;
39826
40532
  try {
39827
- files = import_node_fs18.default.readdirSync(hashDirAbs);
40533
+ files = import_node_fs19.default.readdirSync(hashDirAbs);
39828
40534
  } catch {
39829
40535
  continue;
39830
40536
  }
39831
40537
  for (const name of files) {
39832
- const file = import_node_path18.default.join(hashDirAbs, name);
40538
+ const file = import_node_path19.default.join(hashDirAbs, name);
39833
40539
  let stat;
39834
40540
  try {
39835
- stat = import_node_fs18.default.statSync(file);
40541
+ stat = import_node_fs19.default.statSync(file);
39836
40542
  } catch {
39837
40543
  continue;
39838
40544
  }
@@ -39841,26 +40547,26 @@ function runAttachmentGc(args) {
39841
40547
  if (age < ttlMs) continue;
39842
40548
  if (liveAbs.has(file)) continue;
39843
40549
  try {
39844
- import_node_fs18.default.unlinkSync(file);
40550
+ import_node_fs19.default.unlinkSync(file);
39845
40551
  } catch (err) {
39846
40552
  args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
39847
40553
  }
39848
40554
  }
39849
40555
  try {
39850
- if (import_node_fs18.default.readdirSync(hashDirAbs).length === 0) import_node_fs18.default.rmdirSync(hashDirAbs);
40556
+ if (import_node_fs19.default.readdirSync(hashDirAbs).length === 0) import_node_fs19.default.rmdirSync(hashDirAbs);
39851
40557
  } catch {
39852
40558
  }
39853
40559
  }
39854
40560
  try {
39855
- if (import_node_fs18.default.readdirSync(attRoot).length === 0) import_node_fs18.default.rmdirSync(attRoot);
40561
+ if (import_node_fs19.default.readdirSync(attRoot).length === 0) import_node_fs19.default.rmdirSync(attRoot);
39856
40562
  } catch {
39857
40563
  }
39858
40564
  }
39859
40565
  }
39860
40566
 
39861
40567
  // src/attachment/group.ts
39862
- var import_node_fs19 = __toESM(require("fs"), 1);
39863
- var import_node_path19 = __toESM(require("path"), 1);
40568
+ var import_node_fs20 = __toESM(require("fs"), 1);
40569
+ var import_node_path20 = __toESM(require("path"), 1);
39864
40570
  var import_node_crypto6 = __toESM(require("crypto"), 1);
39865
40571
  init_protocol();
39866
40572
  var GroupFileStore = class {
@@ -39872,11 +40578,11 @@ var GroupFileStore = class {
39872
40578
  this.logger = opts.logger;
39873
40579
  }
39874
40580
  rootForScope(scope) {
39875
- return import_node_path19.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
40581
+ return import_node_path20.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
39876
40582
  }
39877
40583
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
39878
40584
  filePath(scope, sessionId) {
39879
- return import_node_path19.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
40585
+ return import_node_path20.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
39880
40586
  }
39881
40587
  cacheKey(scope, sessionId) {
39882
40588
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -39885,7 +40591,7 @@ var GroupFileStore = class {
39885
40591
  readFile(scope, sessionId) {
39886
40592
  const file = this.filePath(scope, sessionId);
39887
40593
  try {
39888
- const raw = import_node_fs19.default.readFileSync(file, "utf8");
40594
+ const raw = import_node_fs20.default.readFileSync(file, "utf8");
39889
40595
  const parsed = JSON.parse(raw);
39890
40596
  if (!Array.isArray(parsed)) {
39891
40597
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -39911,10 +40617,10 @@ var GroupFileStore = class {
39911
40617
  }
39912
40618
  writeFile(scope, sessionId, entries) {
39913
40619
  const file = this.filePath(scope, sessionId);
39914
- import_node_fs19.default.mkdirSync(import_node_path19.default.dirname(file), { recursive: true });
40620
+ import_node_fs20.default.mkdirSync(import_node_path20.default.dirname(file), { recursive: true });
39915
40621
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
39916
- import_node_fs19.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
39917
- import_node_fs19.default.renameSync(tmp, file);
40622
+ import_node_fs20.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
40623
+ import_node_fs20.default.renameSync(tmp, file);
39918
40624
  }
39919
40625
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
39920
40626
  list(scope, sessionId) {
@@ -40000,10 +40706,10 @@ var GroupFileStore = class {
40000
40706
  };
40001
40707
 
40002
40708
  // src/discovery/state-file.ts
40003
- var import_node_fs20 = __toESM(require("fs"), 1);
40004
- var import_node_path20 = __toESM(require("path"), 1);
40709
+ var import_node_fs21 = __toESM(require("fs"), 1);
40710
+ var import_node_path21 = __toESM(require("path"), 1);
40005
40711
  function defaultStateFilePath(dataDir) {
40006
- return import_node_path20.default.join(dataDir, "state.json");
40712
+ return import_node_path21.default.join(dataDir, "state.json");
40007
40713
  }
40008
40714
  function isPidAlive(pid) {
40009
40715
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -40025,7 +40731,7 @@ var StateFileManager = class {
40025
40731
  }
40026
40732
  read() {
40027
40733
  try {
40028
- const raw = import_node_fs20.default.readFileSync(this.file, "utf8");
40734
+ const raw = import_node_fs21.default.readFileSync(this.file, "utf8");
40029
40735
  const parsed = JSON.parse(raw);
40030
40736
  return parsed;
40031
40737
  } catch {
@@ -40039,20 +40745,20 @@ var StateFileManager = class {
40039
40745
  return { status: "stale", existing };
40040
40746
  }
40041
40747
  write(state) {
40042
- import_node_fs20.default.mkdirSync(import_node_path20.default.dirname(this.file), { recursive: true });
40748
+ import_node_fs21.default.mkdirSync(import_node_path21.default.dirname(this.file), { recursive: true });
40043
40749
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
40044
- import_node_fs20.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
40045
- import_node_fs20.default.renameSync(tmp, this.file);
40750
+ import_node_fs21.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
40751
+ import_node_fs21.default.renameSync(tmp, this.file);
40046
40752
  if (process.platform !== "win32") {
40047
40753
  try {
40048
- import_node_fs20.default.chmodSync(this.file, 384);
40754
+ import_node_fs21.default.chmodSync(this.file, 384);
40049
40755
  } catch {
40050
40756
  }
40051
40757
  }
40052
40758
  }
40053
40759
  delete() {
40054
40760
  try {
40055
- import_node_fs20.default.unlinkSync(this.file);
40761
+ import_node_fs21.default.unlinkSync(this.file);
40056
40762
  } catch {
40057
40763
  }
40058
40764
  }
@@ -40067,14 +40773,14 @@ function readDaemonSourceFromEnv(env = process.env) {
40067
40773
  }
40068
40774
 
40069
40775
  // src/tunnel/tunnel-manager.ts
40070
- var import_node_fs24 = __toESM(require("fs"), 1);
40071
- var import_node_path24 = __toESM(require("path"), 1);
40776
+ var import_node_fs25 = __toESM(require("fs"), 1);
40777
+ var import_node_path25 = __toESM(require("path"), 1);
40072
40778
  var import_node_crypto7 = __toESM(require("crypto"), 1);
40073
40779
  var import_node_child_process9 = require("child_process");
40074
40780
 
40075
40781
  // src/tunnel/tunnel-store.ts
40076
- var import_node_fs21 = __toESM(require("fs"), 1);
40077
- var import_node_path21 = __toESM(require("path"), 1);
40782
+ var import_node_fs22 = __toESM(require("fs"), 1);
40783
+ var import_node_path22 = __toESM(require("path"), 1);
40078
40784
  var TunnelStore = class {
40079
40785
  constructor(filePath) {
40080
40786
  this.filePath = filePath;
@@ -40082,7 +40788,7 @@ var TunnelStore = class {
40082
40788
  filePath;
40083
40789
  async get() {
40084
40790
  try {
40085
- const raw = await import_node_fs21.default.promises.readFile(this.filePath, "utf8");
40791
+ const raw = await import_node_fs22.default.promises.readFile(this.filePath, "utf8");
40086
40792
  const obj = JSON.parse(raw);
40087
40793
  if (!isPersistedTunnel(obj)) return null;
40088
40794
  return obj;
@@ -40093,22 +40799,22 @@ var TunnelStore = class {
40093
40799
  }
40094
40800
  }
40095
40801
  async set(v2) {
40096
- const dir = import_node_path21.default.dirname(this.filePath);
40097
- await import_node_fs21.default.promises.mkdir(dir, { recursive: true });
40802
+ const dir = import_node_path22.default.dirname(this.filePath);
40803
+ await import_node_fs22.default.promises.mkdir(dir, { recursive: true });
40098
40804
  const data = JSON.stringify(v2, null, 2);
40099
40805
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
40100
- await import_node_fs21.default.promises.writeFile(tmp, data, { mode: 384 });
40806
+ await import_node_fs22.default.promises.writeFile(tmp, data, { mode: 384 });
40101
40807
  if (process.platform !== "win32") {
40102
40808
  try {
40103
- await import_node_fs21.default.promises.chmod(tmp, 384);
40809
+ await import_node_fs22.default.promises.chmod(tmp, 384);
40104
40810
  } catch {
40105
40811
  }
40106
40812
  }
40107
- await import_node_fs21.default.promises.rename(tmp, this.filePath);
40813
+ await import_node_fs22.default.promises.rename(tmp, this.filePath);
40108
40814
  }
40109
40815
  async clear() {
40110
40816
  try {
40111
- await import_node_fs21.default.promises.unlink(this.filePath);
40817
+ await import_node_fs22.default.promises.unlink(this.filePath);
40112
40818
  } catch (err) {
40113
40819
  const code = err?.code;
40114
40820
  if (code !== "ENOENT") throw err;
@@ -40203,11 +40909,11 @@ function escape(v2) {
40203
40909
  }
40204
40910
 
40205
40911
  // src/tunnel/frpc-binary.ts
40206
- var import_node_fs22 = __toESM(require("fs"), 1);
40912
+ var import_node_fs23 = __toESM(require("fs"), 1);
40207
40913
  var import_node_os11 = __toESM(require("os"), 1);
40208
- var import_node_path22 = __toESM(require("path"), 1);
40914
+ var import_node_path23 = __toESM(require("path"), 1);
40209
40915
  var import_node_child_process7 = require("child_process");
40210
- var import_node_stream2 = require("stream");
40916
+ var import_node_stream3 = require("stream");
40211
40917
  var import_promises3 = require("stream/promises");
40212
40918
  var FRPC_VERSION = "0.68.0";
40213
40919
  var UnsupportedPlatformError = class extends Error {
@@ -40237,20 +40943,20 @@ function frpcDownloadUrl(version2, p2) {
40237
40943
  }
40238
40944
  async function ensureFrpcBinary(opts) {
40239
40945
  if (opts.override) {
40240
- if (!import_node_fs22.default.existsSync(opts.override)) {
40946
+ if (!import_node_fs23.default.existsSync(opts.override)) {
40241
40947
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
40242
40948
  }
40243
40949
  return opts.override;
40244
40950
  }
40245
40951
  const version2 = opts.version ?? FRPC_VERSION;
40246
40952
  const platform = opts.platform ?? detectPlatform();
40247
- const binDir = import_node_path22.default.join(opts.dataDir, "bin");
40248
- import_node_fs22.default.mkdirSync(binDir, { recursive: true });
40953
+ const binDir = import_node_path23.default.join(opts.dataDir, "bin");
40954
+ import_node_fs23.default.mkdirSync(binDir, { recursive: true });
40249
40955
  cleanupStaleArtifacts(binDir);
40250
- const stableBin = import_node_path22.default.join(binDir, "frpc");
40251
- if (import_node_fs22.default.existsSync(stableBin)) return stableBin;
40956
+ const stableBin = import_node_path23.default.join(binDir, "frpc");
40957
+ if (import_node_fs23.default.existsSync(stableBin)) return stableBin;
40252
40958
  const partialBin = `${stableBin}.partial`;
40253
- const tarballPath = import_node_path22.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
40959
+ const tarballPath = import_node_path23.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
40254
40960
  try {
40255
40961
  const url = frpcDownloadUrl(version2, platform);
40256
40962
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -40259,8 +40965,8 @@ async function ensureFrpcBinary(opts) {
40259
40965
  } else {
40260
40966
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
40261
40967
  }
40262
- import_node_fs22.default.chmodSync(partialBin, 493);
40263
- import_node_fs22.default.renameSync(partialBin, stableBin);
40968
+ import_node_fs23.default.chmodSync(partialBin, 493);
40969
+ import_node_fs23.default.renameSync(partialBin, stableBin);
40264
40970
  } finally {
40265
40971
  safeUnlink(tarballPath);
40266
40972
  safeUnlink(partialBin);
@@ -40270,15 +40976,15 @@ async function ensureFrpcBinary(opts) {
40270
40976
  function cleanupStaleArtifacts(binDir) {
40271
40977
  let entries;
40272
40978
  try {
40273
- entries = import_node_fs22.default.readdirSync(binDir);
40979
+ entries = import_node_fs23.default.readdirSync(binDir);
40274
40980
  } catch {
40275
40981
  return;
40276
40982
  }
40277
40983
  for (const name of entries) {
40278
40984
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
40279
- const full = import_node_path22.default.join(binDir, name);
40985
+ const full = import_node_path23.default.join(binDir, name);
40280
40986
  try {
40281
- import_node_fs22.default.rmSync(full, { recursive: true, force: true });
40987
+ import_node_fs23.default.rmSync(full, { recursive: true, force: true });
40282
40988
  } catch {
40283
40989
  }
40284
40990
  }
@@ -40286,7 +40992,7 @@ function cleanupStaleArtifacts(binDir) {
40286
40992
  }
40287
40993
  function safeUnlink(p2) {
40288
40994
  try {
40289
- import_node_fs22.default.unlinkSync(p2);
40995
+ import_node_fs23.default.unlinkSync(p2);
40290
40996
  } catch {
40291
40997
  }
40292
40998
  }
@@ -40297,13 +41003,13 @@ async function downloadToFile(url, dest, fetchImpl) {
40297
41003
  if (!res.ok || !res.body) {
40298
41004
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
40299
41005
  }
40300
- const out = import_node_fs22.default.createWriteStream(dest);
40301
- const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
41006
+ const out = import_node_fs23.default.createWriteStream(dest);
41007
+ const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
40302
41008
  await (0, import_promises3.pipeline)(nodeStream, out);
40303
41009
  }
40304
41010
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
40305
- const work = import_node_path22.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
40306
- import_node_fs22.default.mkdirSync(work, { recursive: true });
41011
+ const work = import_node_path23.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
41012
+ import_node_fs23.default.mkdirSync(work, { recursive: true });
40307
41013
  try {
40308
41014
  await new Promise((resolve6, reject) => {
40309
41015
  const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -40311,32 +41017,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
40311
41017
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
40312
41018
  });
40313
41019
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
40314
- const src = import_node_path22.default.join(work, dirName, "frpc");
40315
- if (!import_node_fs22.default.existsSync(src)) {
41020
+ const src = import_node_path23.default.join(work, dirName, "frpc");
41021
+ if (!import_node_fs23.default.existsSync(src)) {
40316
41022
  throw new Error(`frpc not found inside tarball at ${src}`);
40317
41023
  }
40318
- import_node_fs22.default.copyFileSync(src, destBin);
41024
+ import_node_fs23.default.copyFileSync(src, destBin);
40319
41025
  } finally {
40320
- import_node_fs22.default.rmSync(work, { recursive: true, force: true });
41026
+ import_node_fs23.default.rmSync(work, { recursive: true, force: true });
40321
41027
  }
40322
41028
  }
40323
41029
 
40324
41030
  // src/tunnel/frpc-process.ts
40325
- var import_node_fs23 = __toESM(require("fs"), 1);
40326
- var import_node_path23 = __toESM(require("path"), 1);
41031
+ var import_node_fs24 = __toESM(require("fs"), 1);
41032
+ var import_node_path24 = __toESM(require("path"), 1);
40327
41033
  var import_node_child_process8 = require("child_process");
40328
41034
  function frpcPidFilePath(dataDir) {
40329
- return import_node_path23.default.join(dataDir, "frpc.pid");
41035
+ return import_node_path24.default.join(dataDir, "frpc.pid");
40330
41036
  }
40331
41037
  function writeFrpcPid(dataDir, pid) {
40332
41038
  try {
40333
- import_node_fs23.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
41039
+ import_node_fs24.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
40334
41040
  } catch {
40335
41041
  }
40336
41042
  }
40337
41043
  function clearFrpcPid(dataDir) {
40338
41044
  try {
40339
- import_node_fs23.default.unlinkSync(frpcPidFilePath(dataDir));
41045
+ import_node_fs24.default.unlinkSync(frpcPidFilePath(dataDir));
40340
41046
  } catch {
40341
41047
  }
40342
41048
  }
@@ -40352,7 +41058,7 @@ function defaultIsPidAlive(pid) {
40352
41058
  }
40353
41059
  function defaultReadPidFile(file) {
40354
41060
  try {
40355
- return import_node_fs23.default.readFileSync(file, "utf8");
41061
+ return import_node_fs24.default.readFileSync(file, "utf8");
40356
41062
  } catch {
40357
41063
  return null;
40358
41064
  }
@@ -40368,12 +41074,12 @@ function defaultSleep(ms) {
40368
41074
  }
40369
41075
  async function killStaleFrpc(deps) {
40370
41076
  const pidFile = frpcPidFilePath(deps.dataDir);
40371
- const tomlPath = import_node_path23.default.join(deps.dataDir, "frpc.toml");
41077
+ const tomlPath = import_node_path24.default.join(deps.dataDir, "frpc.toml");
40372
41078
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
40373
41079
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
40374
41080
  const killPid = deps.killPidImpl ?? defaultKillPid;
40375
41081
  const scanPids = deps.scanFrpcPidsImpl ?? ((tp) => defaultScanFrpcPidsByCmdline(tp, deps.logger));
40376
- const sleep = deps.sleepImpl ?? defaultSleep;
41082
+ const sleep2 = deps.sleepImpl ?? defaultSleep;
40377
41083
  const victims = /* @__PURE__ */ new Set();
40378
41084
  const raw = readPidFile(pidFile);
40379
41085
  if (raw) {
@@ -40392,7 +41098,7 @@ async function killStaleFrpc(deps) {
40392
41098
  }
40393
41099
  if (victims.size === 0) {
40394
41100
  try {
40395
- import_node_fs23.default.unlinkSync(pidFile);
41101
+ import_node_fs24.default.unlinkSync(pidFile);
40396
41102
  } catch {
40397
41103
  }
40398
41104
  return;
@@ -40401,9 +41107,9 @@ async function killStaleFrpc(deps) {
40401
41107
  deps.logger?.warn("tunnel: killing stale frpc before respawn", { pid });
40402
41108
  killPid(pid, "SIGKILL");
40403
41109
  }
40404
- await sleep(deps.reapWaitMs ?? 300);
41110
+ await sleep2(deps.reapWaitMs ?? 300);
40405
41111
  try {
40406
- import_node_fs23.default.unlinkSync(pidFile);
41112
+ import_node_fs24.default.unlinkSync(pidFile);
40407
41113
  } catch {
40408
41114
  }
40409
41115
  }
@@ -40440,7 +41146,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
40440
41146
  var TunnelManager = class {
40441
41147
  constructor(deps) {
40442
41148
  this.deps = deps;
40443
- this.store = deps.store ?? new TunnelStore(import_node_path24.default.join(deps.dataDir, "tunnel.json"));
41149
+ this.store = deps.store ?? new TunnelStore(import_node_path25.default.join(deps.dataDir, "tunnel.json"));
40444
41150
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
40445
41151
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
40446
41152
  }
@@ -40567,7 +41273,7 @@ var TunnelManager = class {
40567
41273
  dataDir: this.deps.dataDir,
40568
41274
  override: this.deps.frpcBinaryOverride ?? void 0
40569
41275
  });
40570
- const tomlPath = import_node_path24.default.join(this.deps.dataDir, "frpc.toml");
41276
+ const tomlPath = import_node_path25.default.join(this.deps.dataDir, "frpc.toml");
40571
41277
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto7.default.randomBytes(3).toString("hex")}`;
40572
41278
  const toml = buildFrpcToml({
40573
41279
  serverAddr: t.frpsHost,
@@ -40578,12 +41284,12 @@ var TunnelManager = class {
40578
41284
  localPort,
40579
41285
  logLevel: "info"
40580
41286
  });
40581
- await import_node_fs24.default.promises.writeFile(tomlPath, toml, { mode: 384 });
41287
+ await import_node_fs25.default.promises.writeFile(tomlPath, toml, { mode: 384 });
40582
41288
  const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
40583
41289
  stdio: ["ignore", "pipe", "pipe"]
40584
41290
  });
40585
- const logFilePath = import_node_path24.default.join(this.deps.dataDir, "frpc.log");
40586
- const logStream = import_node_fs24.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
41291
+ const logFilePath = import_node_path25.default.join(this.deps.dataDir, "frpc.log");
41292
+ const logStream = import_node_fs25.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
40587
41293
  logStream.on("error", () => {
40588
41294
  });
40589
41295
  const tee = (chunk) => {
@@ -40666,27 +41372,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
40666
41372
 
40667
41373
  // src/tunnel/device-key.ts
40668
41374
  var import_node_os12 = __toESM(require("os"), 1);
40669
- var import_node_path25 = __toESM(require("path"), 1);
41375
+ var import_node_path26 = __toESM(require("path"), 1);
40670
41376
  var import_node_crypto8 = __toESM(require("crypto"), 1);
40671
41377
  var DERIVE_SALT = "clawd-tunnel-device-v1";
40672
41378
  function deriveStableDeviceKey(opts = {}) {
40673
41379
  const hostname = opts.hostname ?? import_node_os12.default.hostname();
40674
41380
  const uid = opts.uid ?? (typeof import_node_os12.default.userInfo === "function" ? import_node_os12.default.userInfo().uid : 0);
40675
41381
  const home = opts.home ?? import_node_os12.default.homedir();
40676
- const defaultDataDir = import_node_path25.default.resolve(import_node_path25.default.join(home, ".clawd"));
40677
- const normalizedDataDir = opts.dataDir ? import_node_path25.default.resolve(opts.dataDir) : null;
41382
+ const defaultDataDir = import_node_path26.default.resolve(import_node_path26.default.join(home, ".clawd"));
41383
+ const normalizedDataDir = opts.dataDir ? import_node_path26.default.resolve(opts.dataDir) : null;
40678
41384
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
40679
41385
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
40680
41386
  return import_node_crypto8.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
40681
41387
  }
40682
41388
 
40683
41389
  // src/auth-store.ts
40684
- var import_node_fs25 = __toESM(require("fs"), 1);
40685
- var import_node_path26 = __toESM(require("path"), 1);
41390
+ var import_node_fs26 = __toESM(require("fs"), 1);
41391
+ var import_node_path27 = __toESM(require("path"), 1);
40686
41392
  var import_node_crypto9 = __toESM(require("crypto"), 1);
40687
41393
  var AUTH_FILE_NAME = "auth.json";
40688
41394
  function authFilePath(dataDir) {
40689
- return import_node_path26.default.join(dataDir, AUTH_FILE_NAME);
41395
+ return import_node_path27.default.join(dataDir, AUTH_FILE_NAME);
40690
41396
  }
40691
41397
  function loadOrCreateAuthFile(opts) {
40692
41398
  const file = authFilePath(opts.dataDir);
@@ -40720,7 +41426,7 @@ function defaultGenerateOwnerPrincipalId() {
40720
41426
  }
40721
41427
  function readAuthFile(file) {
40722
41428
  try {
40723
- const raw = import_node_fs25.default.readFileSync(file, "utf8");
41429
+ const raw = import_node_fs26.default.readFileSync(file, "utf8");
40724
41430
  const parsed = JSON.parse(raw);
40725
41431
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
40726
41432
  return null;
@@ -40739,25 +41445,25 @@ function readAuthFile(file) {
40739
41445
  }
40740
41446
  }
40741
41447
  function writeAuthFile(file, content) {
40742
- import_node_fs25.default.mkdirSync(import_node_path26.default.dirname(file), { recursive: true });
40743
- import_node_fs25.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
41448
+ import_node_fs26.default.mkdirSync(import_node_path27.default.dirname(file), { recursive: true });
41449
+ import_node_fs26.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
40744
41450
  try {
40745
- import_node_fs25.default.chmodSync(file, 384);
41451
+ import_node_fs26.default.chmodSync(file, 384);
40746
41452
  } catch {
40747
41453
  }
40748
41454
  }
40749
41455
 
40750
41456
  // src/owner-profile.ts
40751
- var import_node_fs26 = __toESM(require("fs"), 1);
41457
+ var import_node_fs27 = __toESM(require("fs"), 1);
40752
41458
  var import_node_os13 = __toESM(require("os"), 1);
40753
- var import_node_path27 = __toESM(require("path"), 1);
41459
+ var import_node_path28 = __toESM(require("path"), 1);
40754
41460
  var PROFILE_FILENAME = "profile.json";
40755
41461
  function loadOwnerDisplayName(dataDir) {
40756
41462
  const fallback = import_node_os13.default.userInfo().username;
40757
- const profilePath = import_node_path27.default.join(dataDir, PROFILE_FILENAME);
41463
+ const profilePath = import_node_path28.default.join(dataDir, PROFILE_FILENAME);
40758
41464
  let raw;
40759
41465
  try {
40760
- raw = import_node_fs26.default.readFileSync(profilePath, "utf8");
41466
+ raw = import_node_fs27.default.readFileSync(profilePath, "utf8");
40761
41467
  } catch {
40762
41468
  return fallback;
40763
41469
  }
@@ -40780,18 +41486,18 @@ function loadOwnerDisplayName(dataDir) {
40780
41486
  }
40781
41487
 
40782
41488
  // src/feishu-auth/owner-identity-store.ts
40783
- var import_node_fs27 = __toESM(require("fs"), 1);
40784
- var import_node_path28 = __toESM(require("path"), 1);
41489
+ var import_node_fs28 = __toESM(require("fs"), 1);
41490
+ var import_node_path29 = __toESM(require("path"), 1);
40785
41491
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
40786
41492
  var OwnerIdentityStore = class {
40787
41493
  file;
40788
41494
  constructor(dataDir) {
40789
- this.file = import_node_path28.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
41495
+ this.file = import_node_path29.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
40790
41496
  }
40791
41497
  read() {
40792
41498
  let raw;
40793
41499
  try {
40794
- raw = import_node_fs27.default.readFileSync(this.file, "utf8");
41500
+ raw = import_node_fs28.default.readFileSync(this.file, "utf8");
40795
41501
  } catch {
40796
41502
  return null;
40797
41503
  }
@@ -40819,16 +41525,16 @@ var OwnerIdentityStore = class {
40819
41525
  };
40820
41526
  }
40821
41527
  write(record) {
40822
- import_node_fs27.default.mkdirSync(import_node_path28.default.dirname(this.file), { recursive: true });
40823
- import_node_fs27.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
41528
+ import_node_fs28.default.mkdirSync(import_node_path29.default.dirname(this.file), { recursive: true });
41529
+ import_node_fs28.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
40824
41530
  try {
40825
- import_node_fs27.default.chmodSync(this.file, 384);
41531
+ import_node_fs28.default.chmodSync(this.file, 384);
40826
41532
  } catch {
40827
41533
  }
40828
41534
  }
40829
41535
  clear() {
40830
41536
  try {
40831
- import_node_fs27.default.unlinkSync(this.file);
41537
+ import_node_fs28.default.unlinkSync(this.file);
40832
41538
  } catch (err) {
40833
41539
  const code = err?.code;
40834
41540
  if (code !== "ENOENT") throw err;
@@ -40949,9 +41655,9 @@ var CentralClientError = class extends Error {
40949
41655
  code;
40950
41656
  cause;
40951
41657
  };
40952
- async function centralRequest(opts, path58, init) {
41658
+ async function centralRequest(opts, path59, init) {
40953
41659
  const f = opts.fetchImpl ?? globalThis.fetch;
40954
- const url = `${opts.api.replace(/\/+$/, "")}${path58}`;
41660
+ const url = `${opts.api.replace(/\/+$/, "")}${path59}`;
40955
41661
  const ctrl = new AbortController();
40956
41662
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
40957
41663
  let res;
@@ -41093,8 +41799,8 @@ function verifyConnectToken(args) {
41093
41799
  }
41094
41800
 
41095
41801
  // src/feishu-auth/server-key.ts
41096
- var fs36 = __toESM(require("fs"), 1);
41097
- var path37 = __toESM(require("path"), 1);
41802
+ var fs37 = __toESM(require("fs"), 1);
41803
+ var path38 = __toESM(require("path"), 1);
41098
41804
  var FILE_NAME2 = "server-signing-key.json";
41099
41805
  var ServerKeyStore = class {
41100
41806
  constructor(dataDir) {
@@ -41102,12 +41808,12 @@ var ServerKeyStore = class {
41102
41808
  }
41103
41809
  dataDir;
41104
41810
  filePath() {
41105
- return path37.join(this.dataDir, FILE_NAME2);
41811
+ return path38.join(this.dataDir, FILE_NAME2);
41106
41812
  }
41107
41813
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
41108
41814
  read() {
41109
41815
  try {
41110
- const raw = fs36.readFileSync(this.filePath(), "utf8");
41816
+ const raw = fs37.readFileSync(this.filePath(), "utf8");
41111
41817
  const parsed = JSON.parse(raw);
41112
41818
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
41113
41819
  return parsed.publicKeyPem;
@@ -41122,12 +41828,12 @@ var ServerKeyStore = class {
41122
41828
  publicKeyPem,
41123
41829
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
41124
41830
  };
41125
- fs36.mkdirSync(this.dataDir, { recursive: true });
41126
- fs36.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
41831
+ fs37.mkdirSync(this.dataDir, { recursive: true });
41832
+ fs37.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
41127
41833
  }
41128
41834
  clear() {
41129
41835
  try {
41130
- fs36.unlinkSync(this.filePath());
41836
+ fs37.unlinkSync(this.filePath());
41131
41837
  } catch {
41132
41838
  }
41133
41839
  }
@@ -41140,12 +41846,12 @@ init_protocol();
41140
41846
  init_protocol();
41141
41847
 
41142
41848
  // src/session/fork.ts
41143
- var import_node_fs28 = __toESM(require("fs"), 1);
41849
+ var import_node_fs29 = __toESM(require("fs"), 1);
41144
41850
  var import_node_os14 = __toESM(require("os"), 1);
41145
- var import_node_path29 = __toESM(require("path"), 1);
41851
+ var import_node_path30 = __toESM(require("path"), 1);
41146
41852
  init_claude_history();
41147
41853
  function readJsonlEntries(file) {
41148
- const raw = import_node_fs28.default.readFileSync(file, "utf8");
41854
+ const raw = import_node_fs29.default.readFileSync(file, "utf8");
41149
41855
  const out = [];
41150
41856
  for (const line of raw.split("\n")) {
41151
41857
  const t = line.trim();
@@ -41158,10 +41864,10 @@ function readJsonlEntries(file) {
41158
41864
  return out;
41159
41865
  }
41160
41866
  function forkSession(input) {
41161
- const baseDir = input.baseDir ?? import_node_path29.default.join(import_node_os14.default.homedir(), ".claude");
41162
- const projectDir = import_node_path29.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
41163
- const sourceFile = import_node_path29.default.join(projectDir, `${input.toolSessionId}.jsonl`);
41164
- if (!import_node_fs28.default.existsSync(sourceFile)) {
41867
+ const baseDir = input.baseDir ?? import_node_path30.default.join(import_node_os14.default.homedir(), ".claude");
41868
+ const projectDir = import_node_path30.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
41869
+ const sourceFile = import_node_path30.default.join(projectDir, `${input.toolSessionId}.jsonl`);
41870
+ if (!import_node_fs29.default.existsSync(sourceFile)) {
41165
41871
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
41166
41872
  }
41167
41873
  const entries = readJsonlEntries(sourceFile);
@@ -41191,9 +41897,9 @@ function forkSession(input) {
41191
41897
  }
41192
41898
  forkedLines.push(JSON.stringify(forked));
41193
41899
  }
41194
- const forkedFilePath = import_node_path29.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
41195
- import_node_fs28.default.mkdirSync(projectDir, { recursive: true });
41196
- import_node_fs28.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
41900
+ const forkedFilePath = import_node_path30.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
41901
+ import_node_fs29.default.mkdirSync(projectDir, { recursive: true });
41902
+ import_node_fs29.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
41197
41903
  return { forkedToolSessionId, forkedFilePath };
41198
41904
  }
41199
41905
 
@@ -41569,7 +42275,7 @@ function buildPermissionHandlers(deps) {
41569
42275
  }
41570
42276
 
41571
42277
  // src/handlers/history.ts
41572
- var path40 = __toESM(require("path"), 1);
42278
+ var path41 = __toESM(require("path"), 1);
41573
42279
  init_protocol();
41574
42280
 
41575
42281
  // src/session/recent-dirs.ts
@@ -41587,7 +42293,7 @@ function listRecentDirs(store, limit = 50) {
41587
42293
  }
41588
42294
 
41589
42295
  // src/permission/persona-paths.ts
41590
- var path39 = __toESM(require("path"), 1);
42296
+ var path40 = __toESM(require("path"), 1);
41591
42297
  function getAllowedPersonaIds(grants, action) {
41592
42298
  const ids = /* @__PURE__ */ new Set();
41593
42299
  for (const g2 of grants) {
@@ -41600,31 +42306,31 @@ function getAllowedPersonaIds(grants, action) {
41600
42306
  return ids;
41601
42307
  }
41602
42308
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
41603
- const target = path39.resolve(absPath);
42309
+ const target = path40.resolve(absPath);
41604
42310
  if (userWorkDir) {
41605
- const u = path39.resolve(userWorkDir);
41606
- const usep = u.endsWith(path39.sep) ? "" : path39.sep;
42311
+ const u = path40.resolve(userWorkDir);
42312
+ const usep = u.endsWith(path40.sep) ? "" : path40.sep;
41607
42313
  if (target === u || target.startsWith(u + usep)) return true;
41608
42314
  }
41609
- const root = path39.resolve(personaRoot);
41610
- const sep2 = root.endsWith(path39.sep) ? "" : path39.sep;
42315
+ const root = path40.resolve(personaRoot);
42316
+ const sep2 = root.endsWith(path40.sep) ? "" : path40.sep;
41611
42317
  if (!target.startsWith(root + sep2)) return false;
41612
- const rel = path39.relative(root, target);
42318
+ const rel = path40.relative(root, target);
41613
42319
  if (!rel || rel.startsWith("..")) return false;
41614
- const personaId = rel.split(path39.sep)[0];
42320
+ const personaId = rel.split(path40.sep)[0];
41615
42321
  if (!personaId) return false;
41616
42322
  const allowed = getAllowedPersonaIds(grants, action);
41617
42323
  if (allowed === "*") return true;
41618
42324
  return allowed.has(personaId);
41619
42325
  }
41620
42326
  function personaIdFromPath(absPath, personaRoot) {
41621
- const root = path39.resolve(personaRoot);
41622
- const target = path39.resolve(absPath);
41623
- const sep2 = root.endsWith(path39.sep) ? "" : path39.sep;
42327
+ const root = path40.resolve(personaRoot);
42328
+ const target = path40.resolve(absPath);
42329
+ const sep2 = root.endsWith(path40.sep) ? "" : path40.sep;
41624
42330
  if (!target.startsWith(root + sep2)) return null;
41625
- const rel = path39.relative(root, target);
42331
+ const rel = path40.relative(root, target);
41626
42332
  if (!rel || rel.startsWith("..")) return null;
41627
- const id = rel.split(path39.sep)[0];
42333
+ const id = rel.split(path40.sep)[0];
41628
42334
  return id || null;
41629
42335
  }
41630
42336
 
@@ -41651,7 +42357,7 @@ function buildHistoryHandlers(deps) {
41651
42357
  if (!pid) return false;
41652
42358
  return isGuestPathAllowed(
41653
42359
  ctx.grants,
41654
- path40.join(personaRoot, pid),
42360
+ path41.join(personaRoot, pid),
41655
42361
  personaRoot,
41656
42362
  "read",
41657
42363
  userWorkDir
@@ -41663,7 +42369,7 @@ function buildHistoryHandlers(deps) {
41663
42369
  };
41664
42370
  const list = async (frame, _client, ctx) => {
41665
42371
  const args = HistoryListArgs.parse(frame);
41666
- assertGuestPath(ctx, path40.resolve(args.projectPath), personaRoot, "history:list");
42372
+ assertGuestPath(ctx, path41.resolve(args.projectPath), personaRoot, "history:list");
41667
42373
  const sessions = await history.listSessions(args);
41668
42374
  return { response: { type: "history:list", sessions } };
41669
42375
  };
@@ -41695,13 +42401,13 @@ function buildHistoryHandlers(deps) {
41695
42401
  };
41696
42402
  const subagents = async (frame, _client, ctx) => {
41697
42403
  const args = HistorySubagentsArgs.parse(frame);
41698
- assertGuestPath(ctx, path40.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
42404
+ assertGuestPath(ctx, path41.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
41699
42405
  const subs = await history.listSubagents(args);
41700
42406
  return { response: { type: "history:subagents", subagents: subs } };
41701
42407
  };
41702
42408
  const subagentRead = async (frame, _client, ctx) => {
41703
42409
  const args = HistorySubagentReadArgs.parse(frame);
41704
- assertGuestPath(ctx, path40.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
42410
+ assertGuestPath(ctx, path41.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
41705
42411
  const res = await history.readSubagent(args);
41706
42412
  return { response: { type: "history:subagent-read", ...res } };
41707
42413
  };
@@ -41710,7 +42416,7 @@ function buildHistoryHandlers(deps) {
41710
42416
  if (ctx?.principal.kind === "guest" && personaRoot) {
41711
42417
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
41712
42418
  const filtered = dirs.filter(
41713
- (d) => isGuestPathAllowed(ctx.grants, path40.resolve(d.cwd), personaRoot, "read", userWorkDir)
42419
+ (d) => isGuestPathAllowed(ctx.grants, path41.resolve(d.cwd), personaRoot, "read", userWorkDir)
41714
42420
  );
41715
42421
  return { response: { type: "history:recentDirs", dirs: filtered } };
41716
42422
  }
@@ -41727,7 +42433,7 @@ function buildHistoryHandlers(deps) {
41727
42433
  }
41728
42434
 
41729
42435
  // src/handlers/workspace.ts
41730
- var path41 = __toESM(require("path"), 1);
42436
+ var path42 = __toESM(require("path"), 1);
41731
42437
  var os15 = __toESM(require("os"), 1);
41732
42438
  init_protocol();
41733
42439
  init_protocol();
@@ -41769,22 +42475,22 @@ function buildWorkspaceHandlers(deps) {
41769
42475
  const args = WorkspaceListArgs.parse(frame);
41770
42476
  const isGuest = ctx?.principal.kind === "guest";
41771
42477
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os15.homedir();
41772
- const resolvedCwd = path41.resolve(args.cwd ?? fallbackCwd);
41773
- const target = args.path ? path41.resolve(resolvedCwd, args.path) : resolvedCwd;
42478
+ const resolvedCwd = path42.resolve(args.cwd ?? fallbackCwd);
42479
+ const target = args.path ? path42.resolve(resolvedCwd, args.path) : resolvedCwd;
41774
42480
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
41775
42481
  const res = workspace.list({ ...args, cwd: resolvedCwd });
41776
42482
  return { response: { type: "workspace:list", ...res } };
41777
42483
  };
41778
42484
  const read = async (frame, _client, ctx) => {
41779
42485
  const args = WorkspaceReadArgs.parse(frame);
41780
- const target = path41.isAbsolute(args.path) ? path41.resolve(args.path) : path41.resolve(args.cwd, args.path);
42486
+ const target = path42.isAbsolute(args.path) ? path42.resolve(args.path) : path42.resolve(args.cwd, args.path);
41781
42487
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
41782
42488
  const res = workspace.read(args);
41783
42489
  return { response: { type: "workspace:read", ...res } };
41784
42490
  };
41785
42491
  const skillsList = async (frame, _client, ctx) => {
41786
42492
  const args = SkillsListArgs.parse(frame);
41787
- const cwdAbs = path41.resolve(args.cwd);
42493
+ const cwdAbs = path42.resolve(args.cwd);
41788
42494
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
41789
42495
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
41790
42496
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -41796,7 +42502,7 @@ function buildWorkspaceHandlers(deps) {
41796
42502
  };
41797
42503
  const agentsList = async (frame, _client, ctx) => {
41798
42504
  const args = AgentsListArgs.parse(frame);
41799
- const cwdAbs = path41.resolve(args.cwd);
42505
+ const cwdAbs = path42.resolve(args.cwd);
41800
42506
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
41801
42507
  if (args.tool === "codex") {
41802
42508
  return { response: { type: "agents:list", agents: [] } };
@@ -41818,20 +42524,20 @@ function buildWorkspaceHandlers(deps) {
41818
42524
  }
41819
42525
 
41820
42526
  // src/handlers/git.ts
41821
- var path43 = __toESM(require("path"), 1);
42527
+ var path44 = __toESM(require("path"), 1);
41822
42528
  init_protocol();
41823
42529
  init_protocol();
41824
42530
 
41825
42531
  // src/workspace/git.ts
41826
42532
  var import_node_child_process10 = require("child_process");
41827
- var import_node_fs29 = __toESM(require("fs"), 1);
41828
- var import_node_path30 = __toESM(require("path"), 1);
42533
+ var import_node_fs30 = __toESM(require("fs"), 1);
42534
+ var import_node_path31 = __toESM(require("path"), 1);
41829
42535
  var import_node_util = require("util");
41830
42536
  var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
41831
42537
  function normalizePath(p2) {
41832
- const resolved = import_node_path30.default.resolve(p2);
42538
+ const resolved = import_node_path31.default.resolve(p2);
41833
42539
  try {
41834
- return import_node_fs29.default.realpathSync(resolved);
42540
+ return import_node_fs30.default.realpathSync(resolved);
41835
42541
  } catch {
41836
42542
  return resolved;
41837
42543
  }
@@ -41905,7 +42611,7 @@ async function listGitBranches(cwd) {
41905
42611
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
41906
42612
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
41907
42613
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
41908
- if (!isGuestPathAllowed(ctx.grants, path43.resolve(cwd), personaRoot, "read", userWorkDir)) {
42614
+ if (!isGuestPathAllowed(ctx.grants, path44.resolve(cwd), personaRoot, "read", userWorkDir)) {
41909
42615
  throw new ClawdError(
41910
42616
  ERROR_CODES.UNAUTHORIZED,
41911
42617
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -42321,6 +43027,7 @@ function buildReadyFrame(deps, client) {
42321
43027
  if (deps.httpToken) fileSharing.httpToken = deps.httpToken;
42322
43028
  }
42323
43029
  const daemonSource = process.env.CLAWD_DAEMON_SOURCE === "ota" ? "ota" : "packaged";
43030
+ const logShipping = deps.getLogShipping ? deps.getLogShipping() : null;
42324
43031
  return {
42325
43032
  version,
42326
43033
  protocolVersion: PROTOCOL_VERSION,
@@ -42331,7 +43038,8 @@ function buildReadyFrame(deps, client) {
42331
43038
  tunnelUrl,
42332
43039
  mode: deps.mode,
42333
43040
  daemonSource,
42334
- ...fileSharing
43041
+ ...fileSharing,
43042
+ ...logShipping ? { logShipping } : {}
42335
43043
  };
42336
43044
  }
42337
43045
  function buildMetaHandlers(deps) {
@@ -42419,7 +43127,7 @@ function buildPersonaHandlers(deps) {
42419
43127
  }
42420
43128
 
42421
43129
  // src/handlers/attachment.ts
42422
- var import_node_path31 = __toESM(require("path"), 1);
43130
+ var import_node_path32 = __toESM(require("path"), 1);
42423
43131
  init_protocol();
42424
43132
  init_protocol();
42425
43133
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -42474,12 +43182,12 @@ function buildAttachmentHandlers(deps) {
42474
43182
  `session ${args.sessionId} scope unresolved`
42475
43183
  );
42476
43184
  }
42477
- const cwdAbs = import_node_path31.default.resolve(sessionFile.cwd);
42478
- const candidateAbs = import_node_path31.default.isAbsolute(args.relPath) ? import_node_path31.default.resolve(args.relPath) : import_node_path31.default.resolve(cwdAbs, args.relPath);
43185
+ const cwdAbs = import_node_path32.default.resolve(sessionFile.cwd);
43186
+ const candidateAbs = import_node_path32.default.isAbsolute(args.relPath) ? import_node_path32.default.resolve(args.relPath) : import_node_path32.default.resolve(cwdAbs, args.relPath);
42479
43187
  assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl", deps.usersRoot);
42480
43188
  const entries = deps.groupFileStore.list(scope, args.sessionId);
42481
43189
  const entry = entries.find((e) => {
42482
- const storedAbs = import_node_path31.default.isAbsolute(e.relPath) ? import_node_path31.default.resolve(e.relPath) : import_node_path31.default.resolve(cwdAbs, e.relPath);
43190
+ const storedAbs = import_node_path32.default.isAbsolute(e.relPath) ? import_node_path32.default.resolve(e.relPath) : import_node_path32.default.resolve(cwdAbs, e.relPath);
42483
43191
  return storedAbs === candidateAbs && !e.stale;
42484
43192
  });
42485
43193
  if (!entry) {
@@ -42503,7 +43211,7 @@ function buildAttachmentHandlers(deps) {
42503
43211
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
42504
43212
  const f = deps.sessionStore.read(sessionId);
42505
43213
  if (!f) return;
42506
- assertGuestAttachmentPath(ctx, import_node_path31.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
43214
+ assertGuestAttachmentPath(ctx, import_node_path32.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
42507
43215
  }
42508
43216
  const groupAdd = async (frame, _client, ctx) => {
42509
43217
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -42575,19 +43283,19 @@ function buildAttachmentHandlers(deps) {
42575
43283
 
42576
43284
  // src/handlers/extension.ts
42577
43285
  var import_promises7 = __toESM(require("fs/promises"), 1);
42578
- var import_node_path36 = __toESM(require("path"), 1);
43286
+ var import_node_path37 = __toESM(require("path"), 1);
42579
43287
  init_protocol();
42580
43288
 
42581
43289
  // src/extension/bundle-zip.ts
42582
43290
  var import_promises4 = __toESM(require("fs/promises"), 1);
42583
- var import_node_path32 = __toESM(require("path"), 1);
43291
+ var import_node_path33 = __toESM(require("path"), 1);
42584
43292
  var import_node_crypto11 = __toESM(require("crypto"), 1);
42585
43293
  var import_jszip2 = __toESM(require_lib3(), 1);
42586
43294
  async function bundleExtensionDir(dir) {
42587
43295
  const entries = await listFilesSorted(dir);
42588
43296
  const zip = new import_jszip2.default();
42589
43297
  for (const rel of entries) {
42590
- const abs = import_node_path32.default.join(dir, rel);
43298
+ const abs = import_node_path33.default.join(dir, rel);
42591
43299
  const content = await import_promises4.default.readFile(abs);
42592
43300
  zip.file(rel, content, { date: FIXED_DATE });
42593
43301
  }
@@ -42608,7 +43316,7 @@ async function listFilesSorted(rootDir) {
42608
43316
  return out;
42609
43317
  }
42610
43318
  async function walk(absRoot, relPrefix, out) {
42611
- const dirAbs = import_node_path32.default.join(absRoot, relPrefix);
43319
+ const dirAbs = import_node_path33.default.join(absRoot, relPrefix);
42612
43320
  const entries = await import_promises4.default.readdir(dirAbs, { withFileTypes: true });
42613
43321
  for (const e of entries) {
42614
43322
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -42662,25 +43370,25 @@ function computePublishCheck(args) {
42662
43370
 
42663
43371
  // src/extension/install-flow.ts
42664
43372
  var import_promises5 = __toESM(require("fs/promises"), 1);
42665
- var import_node_path34 = __toESM(require("path"), 1);
43373
+ var import_node_path35 = __toESM(require("path"), 1);
42666
43374
  var import_node_os17 = __toESM(require("os"), 1);
42667
43375
  var import_node_crypto12 = __toESM(require("crypto"), 1);
42668
43376
  var import_jszip3 = __toESM(require_lib3(), 1);
42669
43377
 
42670
43378
  // src/extension/paths.ts
42671
43379
  var import_node_os16 = __toESM(require("os"), 1);
42672
- var import_node_path33 = __toESM(require("path"), 1);
43380
+ var import_node_path34 = __toESM(require("path"), 1);
42673
43381
  function clawdHomeRoot(override) {
42674
- return override ?? process.env.CLAWD_HOME ?? import_node_path33.default.join(import_node_os16.default.homedir(), ".clawd");
43382
+ return override ?? process.env.CLAWD_HOME ?? import_node_path34.default.join(import_node_os16.default.homedir(), ".clawd");
42675
43383
  }
42676
43384
  function extensionsRoot(override) {
42677
- return import_node_path33.default.join(clawdHomeRoot(override), "extensions");
43385
+ return import_node_path34.default.join(clawdHomeRoot(override), "extensions");
42678
43386
  }
42679
43387
  function publishedChannelsFile(override) {
42680
- return import_node_path33.default.join(clawdHomeRoot(override), "extensions-published.json");
43388
+ return import_node_path34.default.join(clawdHomeRoot(override), "extensions-published.json");
42681
43389
  }
42682
43390
  function bundleCacheRoot(override) {
42683
- return import_node_path33.default.join(clawdHomeRoot(override), "extension-bundles");
43391
+ return import_node_path34.default.join(clawdHomeRoot(override), "extension-bundles");
42684
43392
  }
42685
43393
 
42686
43394
  // src/extension/install-flow.ts
@@ -42707,7 +43415,7 @@ async function installFromChannel(args, deps) {
42707
43415
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
42708
43416
  }
42709
43417
  for (const name of Object.keys(zip.files)) {
42710
- if (name.includes("..") || name.startsWith("/") || import_node_path34.default.isAbsolute(name)) {
43418
+ if (name.includes("..") || name.startsWith("/") || import_node_path35.default.isAbsolute(name)) {
42711
43419
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
42712
43420
  }
42713
43421
  }
@@ -42739,7 +43447,7 @@ async function installFromChannel(args, deps) {
42739
43447
  );
42740
43448
  }
42741
43449
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
42742
- const destDir = import_node_path34.default.join(deps.extensionsRoot, localExtId);
43450
+ const destDir = import_node_path35.default.join(deps.extensionsRoot, localExtId);
42743
43451
  let destExists = false;
42744
43452
  try {
42745
43453
  await import_promises5.default.access(destDir);
@@ -42753,16 +43461,16 @@ async function installFromChannel(args, deps) {
42753
43461
  );
42754
43462
  }
42755
43463
  const stage = await import_promises5.default.mkdtemp(
42756
- import_node_path34.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
43464
+ import_node_path35.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
42757
43465
  );
42758
43466
  try {
42759
43467
  for (const [name, entry] of Object.entries(zip.files)) {
42760
- const dest = import_node_path34.default.join(stage, name);
43468
+ const dest = import_node_path35.default.join(stage, name);
42761
43469
  if (entry.dir) {
42762
43470
  await import_promises5.default.mkdir(dest, { recursive: true });
42763
43471
  continue;
42764
43472
  }
42765
- await import_promises5.default.mkdir(import_node_path34.default.dirname(dest), { recursive: true });
43473
+ await import_promises5.default.mkdir(import_node_path35.default.dirname(dest), { recursive: true });
42766
43474
  if (name === "manifest.json") {
42767
43475
  const rewritten = { ...parsed.data, id: localExtId };
42768
43476
  await import_promises5.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -42783,7 +43491,7 @@ async function installFromChannel(args, deps) {
42783
43491
 
42784
43492
  // src/extension/update-flow.ts
42785
43493
  var import_promises6 = __toESM(require("fs/promises"), 1);
42786
- var import_node_path35 = __toESM(require("path"), 1);
43494
+ var import_node_path36 = __toESM(require("path"), 1);
42787
43495
  var import_node_os18 = __toESM(require("os"), 1);
42788
43496
  var import_node_crypto13 = __toESM(require("crypto"), 1);
42789
43497
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -42800,11 +43508,11 @@ async function updateFromChannel(args, deps) {
42800
43508
  channelRef.extId,
42801
43509
  channelRef.ownerPrincipalId
42802
43510
  );
42803
- const liveDir = import_node_path35.default.join(deps.extensionsRoot, localExtId);
43511
+ const liveDir = import_node_path36.default.join(deps.extensionsRoot, localExtId);
42804
43512
  const prevDir = `${liveDir}.prev`;
42805
43513
  let existingVersion;
42806
43514
  try {
42807
- const raw = await import_promises6.default.readFile(import_node_path35.default.join(liveDir, "manifest.json"), "utf8");
43515
+ const raw = await import_promises6.default.readFile(import_node_path36.default.join(liveDir, "manifest.json"), "utf8");
42808
43516
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
42809
43517
  if (!parsed2.success) {
42810
43518
  throw new UpdateError(
@@ -42837,7 +43545,7 @@ async function updateFromChannel(args, deps) {
42837
43545
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
42838
43546
  }
42839
43547
  for (const name of Object.keys(zip.files)) {
42840
- if (name.includes("..") || name.startsWith("/") || import_node_path35.default.isAbsolute(name)) {
43548
+ if (name.includes("..") || name.startsWith("/") || import_node_path36.default.isAbsolute(name)) {
42841
43549
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
42842
43550
  }
42843
43551
  }
@@ -42872,16 +43580,16 @@ async function updateFromChannel(args, deps) {
42872
43580
  await import_promises6.default.rm(prevDir, { recursive: true, force: true });
42873
43581
  await import_promises6.default.rename(liveDir, prevDir);
42874
43582
  const stage = await import_promises6.default.mkdtemp(
42875
- import_node_path35.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
43583
+ import_node_path36.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
42876
43584
  );
42877
43585
  try {
42878
43586
  for (const [name, entry] of Object.entries(zip.files)) {
42879
- const dest = import_node_path35.default.join(stage, name);
43587
+ const dest = import_node_path36.default.join(stage, name);
42880
43588
  if (entry.dir) {
42881
43589
  await import_promises6.default.mkdir(dest, { recursive: true });
42882
43590
  continue;
42883
43591
  }
42884
- await import_promises6.default.mkdir(import_node_path35.default.dirname(dest), { recursive: true });
43592
+ await import_promises6.default.mkdir(import_node_path36.default.dirname(dest), { recursive: true });
42885
43593
  if (name === "manifest.json") {
42886
43594
  const rewritten = { ...parsed.data, id: localExtId };
42887
43595
  await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -42974,7 +43682,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
42974
43682
  );
42975
43683
  }
42976
43684
  }
42977
- const manifestPath = import_node_path36.default.join(root, extId, "manifest.json");
43685
+ const manifestPath = import_node_path37.default.join(root, extId, "manifest.json");
42978
43686
  const manifest = await readManifest(root, extId);
42979
43687
  const next = { ...manifest, version: newVersion };
42980
43688
  const tmp = `${manifestPath}.tmp`;
@@ -42982,7 +43690,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
42982
43690
  await import_promises7.default.rename(tmp, manifestPath);
42983
43691
  }
42984
43692
  async function readManifest(root, extId) {
42985
- const file = import_node_path36.default.join(root, extId, "manifest.json");
43693
+ const file = import_node_path37.default.join(root, extId, "manifest.json");
42986
43694
  let raw;
42987
43695
  try {
42988
43696
  raw = await import_promises7.default.readFile(file, "utf8");
@@ -43073,7 +43781,7 @@ function buildExtensionHandlers(deps) {
43073
43781
  };
43074
43782
  async function buildSnapshotMeta(extId) {
43075
43783
  const manifest = await readManifest(deps.root, extId);
43076
- const { sha256, buffer } = await bundleExtensionDir(import_node_path36.default.join(deps.root, extId));
43784
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path37.default.join(deps.root, extId));
43077
43785
  return { manifest, contentHash: sha256, buffer };
43078
43786
  }
43079
43787
  const publish = async (frame, _client, ctx) => {
@@ -43253,8 +43961,218 @@ function buildExtensionHandlers(deps) {
43253
43961
  };
43254
43962
  }
43255
43963
 
43256
- // src/app-builder/kill-port.ts
43964
+ // src/app-builder/project-store.ts
43965
+ var import_node_fs31 = require("fs");
43257
43966
  var import_node_child_process11 = require("child_process");
43967
+ var import_node_path38 = require("path");
43968
+ init_protocol();
43969
+ var PROJECTS_DIR = "projects";
43970
+ var META_FILE = ".clawd-project.json";
43971
+ var DEFAULT_TEMPLATE = "nestjs-react";
43972
+ var SCAFFOLD_TIMEOUT_MS = 3e4;
43973
+ var ProjectStore = class {
43974
+ /** @param root projects 父目录,例如 `~/.clawd/users/<userId>/`(v2 per-user) */
43975
+ constructor(root) {
43976
+ this.root = root;
43977
+ }
43978
+ root;
43979
+ /** projects/<name>/.clawd-project.json 路径 */
43980
+ metaPath(name) {
43981
+ return (0, import_node_path38.join)(this.projectsRoot(), name, META_FILE);
43982
+ }
43983
+ /** projects/<name>/ 目录路径(cwd 用) */
43984
+ projectDir(name) {
43985
+ return (0, import_node_path38.join)(this.projectsRoot(), name);
43986
+ }
43987
+ projectsRoot() {
43988
+ return (0, import_node_path38.join)(this.root, PROJECTS_DIR);
43989
+ }
43990
+ async list() {
43991
+ let entries;
43992
+ try {
43993
+ entries = await import_node_fs31.promises.readdir(this.projectsRoot());
43994
+ } catch (err) {
43995
+ if (err.code === "ENOENT") return [];
43996
+ throw err;
43997
+ }
43998
+ const out = [];
43999
+ for (const name of entries) {
44000
+ try {
44001
+ const raw = await import_node_fs31.promises.readFile(this.metaPath(name), "utf8");
44002
+ const json = JSON.parse(raw);
44003
+ let migrated = false;
44004
+ if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
44005
+ json.devCommand = DEFAULT_DEV_COMMAND;
44006
+ migrated = true;
44007
+ }
44008
+ const parsed = ProjectMetadataSchema.safeParse(json);
44009
+ if (parsed.success) {
44010
+ out.push(parsed.data);
44011
+ if (migrated) {
44012
+ void import_node_fs31.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
44013
+ });
44014
+ }
44015
+ }
44016
+ } catch {
44017
+ }
44018
+ }
44019
+ return out.sort((a, b2) => a.name.localeCompare(b2.name));
44020
+ }
44021
+ /**
44022
+ * @param reservedPorts 其它 user 已占端口(v2 per-user:端口必须全局唯一,所有 user 的
44023
+ * dev server 同机跑;per-user store 各自只 list 自己,靠 handler 收集全局端口传进来,
44024
+ * 否则不同 user 的首个项目都从 PROJECT_PORT_MIN 起 → 全撞 6173)。
44025
+ */
44026
+ async create(name, reservedPorts) {
44027
+ const existing = await this.list();
44028
+ if (existing.some((p2) => p2.name === name)) {
44029
+ throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
44030
+ }
44031
+ const usedPorts = new Set(existing.map((p2) => p2.port));
44032
+ if (reservedPorts) for (const p2 of reservedPorts) usedPorts.add(p2);
44033
+ let port = -1;
44034
+ for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
44035
+ if (!usedPorts.has(p2)) {
44036
+ port = p2;
44037
+ break;
44038
+ }
44039
+ }
44040
+ if (port < 0) {
44041
+ throw new Error(
44042
+ `port pool exhausted / \u7AEF\u53E3\u6C60\u5DF2\u6EE1\uFF08${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}\uFF09\uFF0C\u5148\u5220\u4E00\u4E2A project \u91CA\u653E\u7AEF\u53E3`
44043
+ );
44044
+ }
44045
+ const meta = {
44046
+ name,
44047
+ port,
44048
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
44049
+ devCommand: DEFAULT_DEV_COMMAND
44050
+ };
44051
+ const validated = ProjectMetadataSchema.safeParse(meta);
44052
+ if (!validated.success) {
44053
+ throw new Error(`invalid name "${name}": ${validated.error.message}`);
44054
+ }
44055
+ const dir = this.projectDir(name);
44056
+ await import_node_fs31.promises.mkdir(dir, { recursive: true });
44057
+ await import_node_fs31.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
44058
+ return meta;
44059
+ }
44060
+ /**
44061
+ * 同步内联 scaffold:spawn `extension-kit/scripts/new-extension.sh <name>` 等 exit。
44062
+ * 必须在 `create()` 之后调(脚本期望 projects/<name>/.clawd-project.json 已就位 = picker 模式)。
44063
+ * 失败抛错;上层(handler)应当 catch 后回滚(删 dir + 不 mutate session)。
44064
+ *
44065
+ * spec 2026-06-02 §5.6.3:scaffold 对 assistant 不可见,避免 assistant 被诱导去"读 example
44066
+ * 代码再参考"。assistant 进 project 时目录已是完整模板,直接 pnpm install 即可。
44067
+ */
44068
+ async scaffold(name, templateSrcDir, scaffoldScriptPath) {
44069
+ const destDir = this.projectDir(name);
44070
+ return await new Promise((resolve6, reject) => {
44071
+ const child = (0, import_node_child_process11.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
44072
+ env: { ...process.env, PATH: process.env.PATH ?? "" },
44073
+ stdio: ["ignore", "pipe", "pipe"]
44074
+ });
44075
+ let stdout = "";
44076
+ let stderr = "";
44077
+ child.stdout.on("data", (d) => {
44078
+ stdout += d.toString("utf8");
44079
+ });
44080
+ child.stderr.on("data", (d) => {
44081
+ stderr += d.toString("utf8");
44082
+ });
44083
+ const timer = setTimeout(() => {
44084
+ child.kill("SIGKILL");
44085
+ reject(new Error(`scaffold timeout (>${SCAFFOLD_TIMEOUT_MS}ms): ${scaffoldScriptPath} ${name}`));
44086
+ }, SCAFFOLD_TIMEOUT_MS);
44087
+ child.on("error", (err) => {
44088
+ clearTimeout(timer);
44089
+ reject(new Error(`scaffold spawn failed: ${err.message}`));
44090
+ });
44091
+ child.on("exit", (code, signal) => {
44092
+ clearTimeout(timer);
44093
+ if (code === 0) resolve6({ stdout, stderr });
44094
+ else reject(new Error(`scaffold exit ${code ?? signal}: ${stderr.slice(0, 500)}`));
44095
+ });
44096
+ });
44097
+ }
44098
+ async delete(name) {
44099
+ const dir = this.projectDir(name);
44100
+ await import_node_fs31.promises.rm(dir, { recursive: true, force: true });
44101
+ }
44102
+ async updatePort(name, newPort) {
44103
+ if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
44104
+ throw new Error(
44105
+ `port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
44106
+ );
44107
+ }
44108
+ const list = await this.list();
44109
+ const target = list.find((p2) => p2.name === name);
44110
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44111
+ const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
44112
+ if (conflict) {
44113
+ throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
44114
+ }
44115
+ const updated = { ...target, port: newPort };
44116
+ await import_node_fs31.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
44117
+ return updated;
44118
+ }
44119
+ /**
44120
+ * 单栏 refactor (spec 2026-06-02 §5.6.1):发布上线后写公网 URL 到 .clawd-project.json.prodUrl。
44121
+ * UI 通过 listProjects 读到 prodUrl 后切预览栏到线上 URL,取代旧 prod marker 机制。
44122
+ * 重复调用幂等(覆盖旧 URL)。url 由 ProjectMetadataSchema 校验 url() 格式。
44123
+ */
44124
+ async setProdUrl(name, url) {
44125
+ const list = await this.list();
44126
+ const target = list.find((p2) => p2.name === name);
44127
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44128
+ const updated = { ...target, prodUrl: url };
44129
+ const validated = ProjectMetadataSchema.safeParse(updated);
44130
+ if (!validated.success) {
44131
+ throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
44132
+ }
44133
+ await import_node_fs31.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
44134
+ return validated.data;
44135
+ }
44136
+ /**
44137
+ * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
44138
+ * 写 publishJob 字段到 .clawd-project.json。仅承载 in-flight 状态 ——
44139
+ * - daemon 收到第一个 ::stage::build marker 时写入
44140
+ * - 每次 stage 切换更新 stage 字段(jobId / startedAt 不变)
44141
+ * - 成功 / 失败 / dismiss 时调 clearPublishJob 清掉
44142
+ * - 只有 daemon 崩才会留残骸
44143
+ * 写入时保留所有其他字段(prodUrl 等不受影响)。
44144
+ */
44145
+ async setPublishJob(name, job) {
44146
+ const list = await this.list();
44147
+ const target = list.find((p2) => p2.name === name);
44148
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44149
+ const updated = { ...target, publishJob: job };
44150
+ const validated = ProjectMetadataSchema.safeParse(updated);
44151
+ if (!validated.success) {
44152
+ throw new Error(`invalid publishJob: ${validated.error.message}`);
44153
+ }
44154
+ await import_node_fs31.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
44155
+ return validated.data;
44156
+ }
44157
+ /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
44158
+ async clearPublishJob(name) {
44159
+ const list = await this.list();
44160
+ const target = list.find((p2) => p2.name === name);
44161
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44162
+ if (!target.publishJob) return target;
44163
+ const { publishJob: _drop, ...rest } = target;
44164
+ void _drop;
44165
+ const validated = ProjectMetadataSchema.safeParse(rest);
44166
+ if (!validated.success) {
44167
+ throw new Error(`failed to clear publishJob: ${validated.error.message}`);
44168
+ }
44169
+ await import_node_fs31.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
44170
+ return validated.data;
44171
+ }
44172
+ };
44173
+
44174
+ // src/app-builder/kill-port.ts
44175
+ var import_node_child_process12 = require("child_process");
43258
44176
  async function killPortOccupants(port, ownedPids, logger) {
43259
44177
  let pids;
43260
44178
  try {
@@ -43296,7 +44214,7 @@ async function killPortOccupants(port, ownedPids, logger) {
43296
44214
  }
43297
44215
  function listPidsOnPort(port) {
43298
44216
  return new Promise((resolve6, reject) => {
43299
- (0, import_node_child_process11.execFile)(
44217
+ (0, import_node_child_process12.execFile)(
43300
44218
  "lsof",
43301
44219
  ["-ti", `:${port}`],
43302
44220
  { timeout: 3e3 },
@@ -43368,9 +44286,9 @@ var PublishJobRegistry = class {
43368
44286
  };
43369
44287
 
43370
44288
  // src/app-builder/publish-job-runner.ts
43371
- var import_node_child_process12 = require("child_process");
43372
- var import_node_fs30 = require("fs");
43373
- var import_node_path37 = require("path");
44289
+ var import_node_child_process13 = require("child_process");
44290
+ var import_node_fs32 = require("fs");
44291
+ var import_node_path39 = require("path");
43374
44292
 
43375
44293
  // src/app-builder/publish-stage-parser.ts
43376
44294
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -43397,19 +44315,19 @@ function tailStderrLines(buf, n) {
43397
44315
  // src/app-builder/publish-job-runner.ts
43398
44316
  async function startPublishJob(deps, args) {
43399
44317
  const { registry: registry2, projectDir } = deps;
43400
- const spawn12 = deps.spawnImpl ?? import_node_child_process12.spawn;
44318
+ const spawn12 = deps.spawnImpl ?? import_node_child_process13.spawn;
43401
44319
  if (registry2.has(args.name)) {
43402
44320
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
43403
44321
  }
43404
44322
  const projDir = projectDir(args.name);
43405
- const logPath = (0, import_node_path37.join)(projDir, ".publish.log");
44323
+ const logPath = (0, import_node_path39.join)(projDir, ".publish.log");
43406
44324
  let logStream = null;
43407
44325
  try {
43408
- logStream = (0, import_node_fs30.createWriteStream)(logPath, { flags: "w" });
44326
+ logStream = (0, import_node_fs32.createWriteStream)(logPath, { flags: "w" });
43409
44327
  } catch {
43410
44328
  logStream = null;
43411
44329
  }
43412
- const child = spawn12("bash", [args.scriptPath, projDir], {
44330
+ const child = spawn12("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
43413
44331
  cwd: projDir,
43414
44332
  env: process.env,
43415
44333
  stdio: ["ignore", "pipe", "pipe"]
@@ -43662,10 +44580,9 @@ async function recoverInterruptedJobs(deps) {
43662
44580
 
43663
44581
  // src/handlers/app-builder.ts
43664
44582
  init_protocol();
43665
- var import_node_path38 = require("path");
43666
- var import_node_fs31 = require("fs");
44583
+ var import_node_path40 = require("path");
44584
+ var import_node_fs33 = require("fs");
43667
44585
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
43668
- var PUBLISH_SCRIPT_REL = "extension-kit/scripts/publish.sh";
43669
44586
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
43670
44587
  async function recoverInterruptedPublishJobs(store, logger) {
43671
44588
  await recoverInterruptedJobs({
@@ -43745,7 +44662,7 @@ function buildAppBuilderHandlers(deps) {
43745
44662
  async function listAllUsersProjects() {
43746
44663
  if (!deps.usersRoot || !deps.getStore) return [];
43747
44664
  const getStore = deps.getStore;
43748
- const userIds = await import_node_fs31.promises.readdir(deps.usersRoot).catch(() => []);
44665
+ const userIds = await import_node_fs33.promises.readdir(deps.usersRoot).catch(() => []);
43749
44666
  const perUser = await Promise.all(
43750
44667
  userIds.map((uid) => getStore(uid).list().catch(() => []))
43751
44668
  );
@@ -43821,7 +44738,9 @@ function buildAppBuilderHandlers(deps) {
43821
44738
  const project = await userStore.create(f.name, reservedPorts);
43822
44739
  try {
43823
44740
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
43824
- const scaffoldResult = await userStore.scaffold(project.name, void 0, personaRoot);
44741
+ const templateSrcDir = (0, import_node_path40.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
44742
+ const scaffoldScript = (0, import_node_path40.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
44743
+ const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
43825
44744
  deps.logger?.info("app-builder.scaffold.done", {
43826
44745
  name: project.name,
43827
44746
  stdout: scaffoldResult.stdout.slice(0, 200)
@@ -44040,11 +44959,13 @@ function buildAppBuilderHandlers(deps) {
44040
44959
  });
44041
44960
  await userStore.clearPublishJob(args.name);
44042
44961
  }
44043
- const scriptPath = (0, import_node_path38.join)(deps.personaRoot, PUBLISH_SCRIPT_REL);
44962
+ const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
44963
+ const scriptPath = (0, import_node_path40.join)(deps.deployKitRoot, "scripts", "publish.sh");
44044
44964
  deps.logger?.info("app-builder.publish.start", {
44045
44965
  name: args.name,
44046
44966
  sessionId: boundSession.sessionId,
44047
- scriptPath
44967
+ scriptPath,
44968
+ personaRoot
44048
44969
  });
44049
44970
  const result = await startPublishJob(
44050
44971
  {
@@ -44079,7 +45000,8 @@ function buildAppBuilderHandlers(deps) {
44079
45000
  {
44080
45001
  name: args.name,
44081
45002
  sessionId: boundSession.sessionId,
44082
- scriptPath
45003
+ scriptPath,
45004
+ personaRoot
44083
45005
  }
44084
45006
  );
44085
45007
  return { response: { ...result } };
@@ -44134,7 +45056,7 @@ function buildAppBuilderHandlers(deps) {
44134
45056
 
44135
45057
  // src/extension/registry.ts
44136
45058
  var import_promises8 = __toESM(require("fs/promises"), 1);
44137
- var import_node_path39 = __toESM(require("path"), 1);
45059
+ var import_node_path41 = __toESM(require("path"), 1);
44138
45060
  async function loadAll(root) {
44139
45061
  let entries;
44140
45062
  try {
@@ -44147,13 +45069,13 @@ async function loadAll(root) {
44147
45069
  for (const ent of entries) {
44148
45070
  if (!ent.isDirectory()) continue;
44149
45071
  if (ent.name.startsWith(".")) continue;
44150
- records.push(await loadOne(import_node_path39.default.join(root, ent.name), ent.name));
45072
+ records.push(await loadOne(import_node_path41.default.join(root, ent.name), ent.name));
44151
45073
  }
44152
45074
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
44153
45075
  return records;
44154
45076
  }
44155
45077
  async function loadOne(dir, dirName) {
44156
- const manifestPath = import_node_path39.default.join(dir, "manifest.json");
45078
+ const manifestPath = import_node_path41.default.join(dir, "manifest.json");
44157
45079
  let raw;
44158
45080
  try {
44159
45081
  raw = await import_promises8.default.readFile(manifestPath, "utf8");
@@ -44198,7 +45120,7 @@ async function loadOne(dir, dirName) {
44198
45120
 
44199
45121
  // src/extension/uninstall.ts
44200
45122
  var import_promises9 = __toESM(require("fs/promises"), 1);
44201
- var import_node_path40 = __toESM(require("path"), 1);
45123
+ var import_node_path42 = __toESM(require("path"), 1);
44202
45124
  var UninstallError = class extends Error {
44203
45125
  constructor(code, message) {
44204
45126
  super(message);
@@ -44207,7 +45129,7 @@ var UninstallError = class extends Error {
44207
45129
  code;
44208
45130
  };
44209
45131
  async function uninstall(deps) {
44210
- const dir = import_node_path40.default.join(deps.root, deps.extId);
45132
+ const dir = import_node_path42.default.join(deps.root, deps.extId);
44211
45133
  try {
44212
45134
  await import_promises9.default.access(dir);
44213
45135
  } catch {
@@ -44277,228 +45199,16 @@ function buildMethodHandlers(deps) {
44277
45199
  broadcastProjectUpdated: deps.broadcastProjectUpdated,
44278
45200
  logger: deps.logger,
44279
45201
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
44280
- // personaRoot 用于拼 publish.sh 绝对路径;broadcastSessionFrame 用于失败回灌路径
44281
- // runner 调 manager.send 后逐帧 dispatch 到 transport)。
45202
+ // personaRoot 仅作 resolvePersonaRoot 未注入时的兜底;deployKitRoot 为共享脚本根。
45203
+ // broadcastSessionFrame 用于失败回灌路径(runner 调 manager.send 后逐帧 dispatch 到 transport)。
44282
45204
  personaRoot: deps.appBuilderPersonaRoot,
45205
+ deployKitRoot: deps.deployKitRoot,
44283
45206
  resolvePersonaRoot: deps.resolvePersonaRoot,
44284
45207
  broadcastSessionFrame: deps.broadcastSessionFrame
44285
45208
  })
44286
45209
  };
44287
45210
  }
44288
45211
 
44289
- // src/app-builder/project-store.ts
44290
- var import_node_fs32 = require("fs");
44291
- var import_node_child_process13 = require("child_process");
44292
- var import_node_path41 = require("path");
44293
- init_protocol();
44294
- var PROJECTS_DIR = "projects";
44295
- var META_FILE = ".clawd-project.json";
44296
- var SCAFFOLD_SCRIPT_REL = "extension-kit/scripts/new-extension.sh";
44297
- var DEFAULT_TEMPLATE = "nestjs-react";
44298
- var SCAFFOLD_TIMEOUT_MS = 3e4;
44299
- var ProjectStore = class {
44300
- /** @param root projects 父目录,例如 `~/.clawd/users/<userId>/`(v2 per-user) */
44301
- constructor(root) {
44302
- this.root = root;
44303
- }
44304
- root;
44305
- /** projects/<name>/.clawd-project.json 路径 */
44306
- metaPath(name) {
44307
- return (0, import_node_path41.join)(this.projectsRoot(), name, META_FILE);
44308
- }
44309
- /** projects/<name>/ 目录路径(cwd 用) */
44310
- projectDir(name) {
44311
- return (0, import_node_path41.join)(this.projectsRoot(), name);
44312
- }
44313
- projectsRoot() {
44314
- return (0, import_node_path41.join)(this.root, PROJECTS_DIR);
44315
- }
44316
- async list() {
44317
- let entries;
44318
- try {
44319
- entries = await import_node_fs32.promises.readdir(this.projectsRoot());
44320
- } catch (err) {
44321
- if (err.code === "ENOENT") return [];
44322
- throw err;
44323
- }
44324
- const out = [];
44325
- for (const name of entries) {
44326
- try {
44327
- const raw = await import_node_fs32.promises.readFile(this.metaPath(name), "utf8");
44328
- const json = JSON.parse(raw);
44329
- let migrated = false;
44330
- if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
44331
- json.devCommand = DEFAULT_DEV_COMMAND;
44332
- migrated = true;
44333
- }
44334
- const parsed = ProjectMetadataSchema.safeParse(json);
44335
- if (parsed.success) {
44336
- out.push(parsed.data);
44337
- if (migrated) {
44338
- void import_node_fs32.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
44339
- });
44340
- }
44341
- }
44342
- } catch {
44343
- }
44344
- }
44345
- return out.sort((a, b2) => a.name.localeCompare(b2.name));
44346
- }
44347
- /**
44348
- * @param reservedPorts 其它 user 已占端口(v2 per-user:端口必须全局唯一,所有 user 的
44349
- * dev server 同机跑;per-user store 各自只 list 自己,靠 handler 收集全局端口传进来,
44350
- * 否则不同 user 的首个项目都从 PROJECT_PORT_MIN 起 → 全撞 6173)。
44351
- */
44352
- async create(name, reservedPorts) {
44353
- const existing = await this.list();
44354
- if (existing.some((p2) => p2.name === name)) {
44355
- throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
44356
- }
44357
- const usedPorts = new Set(existing.map((p2) => p2.port));
44358
- if (reservedPorts) for (const p2 of reservedPorts) usedPorts.add(p2);
44359
- let port = -1;
44360
- for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
44361
- if (!usedPorts.has(p2)) {
44362
- port = p2;
44363
- break;
44364
- }
44365
- }
44366
- if (port < 0) {
44367
- throw new Error(
44368
- `port pool exhausted / \u7AEF\u53E3\u6C60\u5DF2\u6EE1\uFF08${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}\uFF09\uFF0C\u5148\u5220\u4E00\u4E2A project \u91CA\u653E\u7AEF\u53E3`
44369
- );
44370
- }
44371
- const meta = {
44372
- name,
44373
- port,
44374
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
44375
- devCommand: DEFAULT_DEV_COMMAND
44376
- };
44377
- const validated = ProjectMetadataSchema.safeParse(meta);
44378
- if (!validated.success) {
44379
- throw new Error(`invalid name "${name}": ${validated.error.message}`);
44380
- }
44381
- const dir = this.projectDir(name);
44382
- await import_node_fs32.promises.mkdir(dir, { recursive: true });
44383
- await import_node_fs32.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
44384
- return meta;
44385
- }
44386
- /**
44387
- * 同步内联 scaffold:spawn `extension-kit/scripts/new-extension.sh <name>` 等 exit。
44388
- * 必须在 `create()` 之后调(脚本期望 projects/<name>/.clawd-project.json 已就位 = picker 模式)。
44389
- * 失败抛错;上层(handler)应当 catch 后回滚(删 dir + 不 mutate session)。
44390
- *
44391
- * spec 2026-06-02 §5.6.3:scaffold 对 assistant 不可见,避免 assistant 被诱导去"读 example
44392
- * 代码再参考"。assistant 进 project 时目录已是完整模板,直接 pnpm install 即可。
44393
- */
44394
- async scaffold(name, template = DEFAULT_TEMPLATE, personaRoot = this.root) {
44395
- const scriptPath = (0, import_node_path41.join)(personaRoot, SCAFFOLD_SCRIPT_REL);
44396
- const destDir = this.projectDir(name);
44397
- return await new Promise((resolve6, reject) => {
44398
- const child = (0, import_node_child_process13.spawn)("bash", [scriptPath, name, template, destDir], {
44399
- cwd: personaRoot,
44400
- env: { ...process.env, PATH: process.env.PATH ?? "" },
44401
- stdio: ["ignore", "pipe", "pipe"]
44402
- });
44403
- let stdout = "";
44404
- let stderr = "";
44405
- child.stdout.on("data", (d) => {
44406
- stdout += d.toString("utf8");
44407
- });
44408
- child.stderr.on("data", (d) => {
44409
- stderr += d.toString("utf8");
44410
- });
44411
- const timer = setTimeout(() => {
44412
- child.kill("SIGKILL");
44413
- reject(new Error(`scaffold timeout (>${SCAFFOLD_TIMEOUT_MS}ms): ${scriptPath} ${name} ${template}`));
44414
- }, SCAFFOLD_TIMEOUT_MS);
44415
- child.on("error", (err) => {
44416
- clearTimeout(timer);
44417
- reject(new Error(`scaffold spawn failed: ${err.message}`));
44418
- });
44419
- child.on("exit", (code, signal) => {
44420
- clearTimeout(timer);
44421
- if (code === 0) resolve6({ stdout, stderr });
44422
- else reject(new Error(`scaffold exit ${code ?? signal}: ${stderr.slice(0, 500)}`));
44423
- });
44424
- });
44425
- }
44426
- async delete(name) {
44427
- const dir = this.projectDir(name);
44428
- await import_node_fs32.promises.rm(dir, { recursive: true, force: true });
44429
- }
44430
- async updatePort(name, newPort) {
44431
- if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
44432
- throw new Error(
44433
- `port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
44434
- );
44435
- }
44436
- const list = await this.list();
44437
- const target = list.find((p2) => p2.name === name);
44438
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44439
- const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
44440
- if (conflict) {
44441
- throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
44442
- }
44443
- const updated = { ...target, port: newPort };
44444
- await import_node_fs32.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
44445
- return updated;
44446
- }
44447
- /**
44448
- * 单栏 refactor (spec 2026-06-02 §5.6.1):发布上线后写公网 URL 到 .clawd-project.json.prodUrl。
44449
- * UI 通过 listProjects 读到 prodUrl 后切预览栏到线上 URL,取代旧 prod marker 机制。
44450
- * 重复调用幂等(覆盖旧 URL)。url 由 ProjectMetadataSchema 校验 url() 格式。
44451
- */
44452
- async setProdUrl(name, url) {
44453
- const list = await this.list();
44454
- const target = list.find((p2) => p2.name === name);
44455
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44456
- const updated = { ...target, prodUrl: url };
44457
- const validated = ProjectMetadataSchema.safeParse(updated);
44458
- if (!validated.success) {
44459
- throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
44460
- }
44461
- await import_node_fs32.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
44462
- return validated.data;
44463
- }
44464
- /**
44465
- * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
44466
- * 写 publishJob 字段到 .clawd-project.json。仅承载 in-flight 状态 ——
44467
- * - daemon 收到第一个 ::stage::build marker 时写入
44468
- * - 每次 stage 切换更新 stage 字段(jobId / startedAt 不变)
44469
- * - 成功 / 失败 / dismiss 时调 clearPublishJob 清掉
44470
- * - 只有 daemon 崩才会留残骸
44471
- * 写入时保留所有其他字段(prodUrl 等不受影响)。
44472
- */
44473
- async setPublishJob(name, job) {
44474
- const list = await this.list();
44475
- const target = list.find((p2) => p2.name === name);
44476
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44477
- const updated = { ...target, publishJob: job };
44478
- const validated = ProjectMetadataSchema.safeParse(updated);
44479
- if (!validated.success) {
44480
- throw new Error(`invalid publishJob: ${validated.error.message}`);
44481
- }
44482
- await import_node_fs32.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
44483
- return validated.data;
44484
- }
44485
- /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
44486
- async clearPublishJob(name) {
44487
- const list = await this.list();
44488
- const target = list.find((p2) => p2.name === name);
44489
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
44490
- if (!target.publishJob) return target;
44491
- const { publishJob: _drop, ...rest } = target;
44492
- void _drop;
44493
- const validated = ProjectMetadataSchema.safeParse(rest);
44494
- if (!validated.success) {
44495
- throw new Error(`failed to clear publishJob: ${validated.error.message}`);
44496
- }
44497
- await import_node_fs32.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
44498
- return validated.data;
44499
- }
44500
- };
44501
-
44502
45212
  // src/app-builder/dev-server-supervisor.ts
44503
45213
  var import_node_child_process14 = require("child_process");
44504
45214
  var import_node_events2 = require("events");
@@ -44930,7 +45640,7 @@ function computeGrantForFrame(method, frame) {
44930
45640
 
44931
45641
  // src/extension/runtime.ts
44932
45642
  var import_node_child_process15 = require("child_process");
44933
- var import_node_path42 = __toESM(require("path"), 1);
45643
+ var import_node_path43 = __toESM(require("path"), 1);
44934
45644
  var import_promises10 = require("timers/promises");
44935
45645
 
44936
45646
  // src/extension/port-allocator.ts
@@ -45031,7 +45741,7 @@ var Runtime = class {
45031
45741
  /\$CLAWOS_EXT_PORT/g,
45032
45742
  String(port)
45033
45743
  );
45034
- const dir = import_node_path42.default.join(this.root, extId);
45744
+ const dir = import_node_path43.default.join(this.root, extId);
45035
45745
  const env = {
45036
45746
  ...process.env,
45037
45747
  CLAWOS_EXT_PORT: String(port),
@@ -45143,7 +45853,7 @@ ${handle.stderrTail}`
45143
45853
 
45144
45854
  // src/extension/published-channels.ts
45145
45855
  var import_promises11 = __toESM(require("fs/promises"), 1);
45146
- var import_node_path43 = __toESM(require("path"), 1);
45856
+ var import_node_path44 = __toESM(require("path"), 1);
45147
45857
  init_zod();
45148
45858
  var PublishedChannelsError = class extends Error {
45149
45859
  constructor(code, message) {
@@ -45242,7 +45952,7 @@ var PublishedChannelStore = class {
45242
45952
  )
45243
45953
  };
45244
45954
  const tmp = `${this.filePath}.tmp`;
45245
- await import_promises11.default.mkdir(import_node_path43.default.dirname(this.filePath), { recursive: true });
45955
+ await import_promises11.default.mkdir(import_node_path44.default.dirname(this.filePath), { recursive: true });
45246
45956
  await import_promises11.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
45247
45957
  await import_promises11.default.rename(tmp, this.filePath);
45248
45958
  }
@@ -45250,7 +45960,7 @@ var PublishedChannelStore = class {
45250
45960
 
45251
45961
  // src/extension/bundle-cache.ts
45252
45962
  var import_promises12 = __toESM(require("fs/promises"), 1);
45253
- var import_node_path44 = __toESM(require("path"), 1);
45963
+ var import_node_path45 = __toESM(require("path"), 1);
45254
45964
  var BundleCache = class {
45255
45965
  constructor(rootDir) {
45256
45966
  this.rootDir = rootDir;
@@ -45259,14 +45969,14 @@ var BundleCache = class {
45259
45969
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
45260
45970
  async write(snapshotHash, buffer) {
45261
45971
  await import_promises12.default.mkdir(this.rootDir, { recursive: true });
45262
- const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
45972
+ const file = import_node_path45.default.join(this.rootDir, `${snapshotHash}.zip`);
45263
45973
  const tmp = `${file}.tmp`;
45264
45974
  await import_promises12.default.writeFile(tmp, buffer, { mode: 384 });
45265
45975
  await import_promises12.default.rename(tmp, file);
45266
45976
  }
45267
45977
  /** Returns the bundle bytes, or null when the file doesn't exist. */
45268
45978
  async read(snapshotHash) {
45269
- const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
45979
+ const file = import_node_path45.default.join(this.rootDir, `${snapshotHash}.zip`);
45270
45980
  try {
45271
45981
  return await import_promises12.default.readFile(file);
45272
45982
  } catch (e) {
@@ -45276,16 +45986,33 @@ var BundleCache = class {
45276
45986
  }
45277
45987
  /** Idempotent — missing file is not an error. */
45278
45988
  async delete(snapshotHash) {
45279
- const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
45989
+ const file = import_node_path45.default.join(this.rootDir, `${snapshotHash}.zip`);
45280
45990
  await import_promises12.default.rm(file, { force: true });
45281
45991
  }
45282
45992
  };
45283
45993
 
45284
45994
  // src/index.ts
45995
+ var import_meta4 = {};
45285
45996
  async function startDaemon(config) {
45997
+ const authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
45998
+ const logShippingCfg = config.logShipping ?? { endpoint: null, sampling: {}, mode: "off" };
45999
+ const logClient = createLogClient({
46000
+ endpoint: logShippingCfg.endpoint,
46001
+ identity: {
46002
+ ownerPrincipalId: authFile.ownerPrincipalId,
46003
+ deviceId: authFile.deviceId,
46004
+ appVersion: version,
46005
+ daemonVersion: version,
46006
+ os: `${process.platform}-${process.arch}`
46007
+ },
46008
+ source: "daemon",
46009
+ sampling: logShippingCfg.sampling,
46010
+ homeDir: import_node_os19.default.homedir()
46011
+ });
45286
46012
  const logger = createLogger({
45287
46013
  level: config.logLevel,
45288
- file: import_node_path45.default.join(config.dataDir, "clawd.log")
46014
+ file: import_node_path46.default.join(config.dataDir, "clawd.log"),
46015
+ logClient
45289
46016
  });
45290
46017
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
45291
46018
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -45296,7 +46023,6 @@ async function startDaemon(config) {
45296
46023
  if (pre.status === "stale") {
45297
46024
  logger.warn("stale state file detected, overwriting", { pid: pre.existing.pid });
45298
46025
  }
45299
- const authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
45300
46026
  let resolvedAuthToken = null;
45301
46027
  if (config.authToken && config.authToken.trim()) {
45302
46028
  resolvedAuthToken = config.authToken.trim();
@@ -45421,8 +46147,8 @@ async function startDaemon(config) {
45421
46147
  const agents = new AgentsScanner();
45422
46148
  const history = new ClaudeHistoryReader();
45423
46149
  let transport = null;
45424
- const personaStore = new PersonaStore(import_node_path45.default.join(config.dataDir, "personas"));
45425
- const usersRoot = import_node_path45.default.join(config.dataDir, "users");
46150
+ const personaStore = new PersonaStore(import_node_path46.default.join(config.dataDir, "personas"));
46151
+ const usersRoot = import_node_path46.default.join(config.dataDir, "users");
45426
46152
  const defaultsRoot = findDefaultsRoot();
45427
46153
  if (defaultsRoot) {
45428
46154
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -45430,9 +46156,43 @@ async function startDaemon(config) {
45430
46156
  } else {
45431
46157
  logger.warn("persona.seed.skip", { reason: "defaults-root-not-found" });
45432
46158
  }
46159
+ const deployKitBundleRoot = findDeployKitRoot();
46160
+ if (deployKitBundleRoot) {
46161
+ seedDeployKit({ deployKitBundleRoot, dataDir: config.dataDir, logger });
46162
+ refreshDeployKit({ deployKitBundleRoot, dataDir: config.dataDir, logger });
46163
+ } else {
46164
+ logger.warn("deploy-kit.bundle-missing", {});
46165
+ }
45433
46166
  migrateAgentsMirror({ store: personaStore, logger });
45434
46167
  migrateCodexSandbox({ store: personaStore, logger });
45435
46168
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
46169
+ const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
46170
+ const here = typeof __dirname === "string" ? __dirname : import_node_path46.default.dirname((0, import_node_url2.fileURLToPath)(import_meta4.url));
46171
+ const dispatchServerCandidates = [
46172
+ import_node_path46.default.join(here, "dispatch", "mcp-server.cjs"),
46173
+ // 生产 dist/index → dist/dispatch/mcp-server.cjs
46174
+ import_node_path46.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
46175
+ // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
46176
+ ];
46177
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs34.default.existsSync(p2));
46178
+ let dispatchMcpConfigPath2;
46179
+ if (dispatchServerScriptPath) {
46180
+ const dispatchLogPath = import_node_path46.default.join(config.dataDir, "dispatch-mcp-server.log");
46181
+ dispatchMcpConfigPath2 = writeDispatchMcpConfig({
46182
+ dataDir: config.dataDir,
46183
+ serverScriptPath: dispatchServerScriptPath,
46184
+ logPath: dispatchLogPath
46185
+ });
46186
+ logger.info("dispatch.mcp.json written", {
46187
+ path: dispatchMcpConfigPath2,
46188
+ server: dispatchServerScriptPath,
46189
+ serverLog: dispatchLogPath
46190
+ });
46191
+ } else {
46192
+ logger.warn("dispatch-mcp-server.cjs not found; persona dispatch disabled (dev tsx \u6A21\u5F0F\u4E0B\u9700\u5148\u8DD1\u8FC7 build)", {
46193
+ tried: dispatchServerCandidates
46194
+ });
46195
+ }
45436
46196
  const manager = new SessionManager({
45437
46197
  store,
45438
46198
  // Phase 2 (capability platform plan §1): factory 注入后 manager.storeFor 走
@@ -45442,7 +46202,7 @@ async function startDaemon(config) {
45442
46202
  getAdapter,
45443
46203
  historyReader: history,
45444
46204
  dataDir: config.dataDir,
45445
- personaRoot: import_node_path45.default.join(config.dataDir, "personas"),
46205
+ personaRoot: import_node_path46.default.join(config.dataDir, "personas"),
45446
46206
  usersRoot,
45447
46207
  personaStore,
45448
46208
  ownerDisplayName,
@@ -45453,6 +46213,12 @@ async function startDaemon(config) {
45453
46213
  // 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
45454
46214
  // 的 isLoopback 兜底已确保安全。
45455
46215
  getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
46216
+ // Persona dispatch (Task 8): manager 通过这两个 deps 跟 PersonaDispatchManager 协作。
46217
+ // - personaDispatchManager: manager.createDispatchedSession 用它 registerBSession;
46218
+ // reducer 通过闭包反查 dispatchId 注 CLAWD_DISPATCH_ID env。
46219
+ // - dispatchMcpConfigPath: reducer 透传到 SpawnContext,cc spawn 加 --mcp-config flag。
46220
+ personaDispatchManager,
46221
+ dispatchMcpConfigPath: dispatchMcpConfigPath2,
45456
46222
  broadcastFrame: (frame, target) => {
45457
46223
  if (target === "all") {
45458
46224
  transport?.broadcastAll(frame);
@@ -45472,10 +46238,10 @@ async function startDaemon(config) {
45472
46238
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
45473
46239
  attachmentGroup: {
45474
46240
  onFileEdit: (input) => {
45475
- const absPath = import_node_path45.default.isAbsolute(input.relPath) ? input.relPath : import_node_path45.default.join(input.cwd, input.relPath);
46241
+ const absPath = import_node_path46.default.isAbsolute(input.relPath) ? input.relPath : import_node_path46.default.join(input.cwd, input.relPath);
45476
46242
  let size = 0;
45477
46243
  try {
45478
- size = import_node_fs33.default.statSync(absPath).size;
46244
+ size = import_node_fs34.default.statSync(absPath).size;
45479
46245
  } catch (err) {
45480
46246
  logger.warn("attachment.onFileEdit stat failed", {
45481
46247
  sessionId: input.sessionId,
@@ -45661,11 +46427,11 @@ async function startDaemon(config) {
45661
46427
  // 'persona/<pid>/owner',default 走 'default'。
45662
46428
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
45663
46429
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
45664
- personaRoot: import_node_path45.default.join(config.dataDir, "personas"),
46430
+ personaRoot: import_node_path46.default.join(config.dataDir, "personas"),
45665
46431
  usersRoot
45666
46432
  },
45667
46433
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
45668
- personaRoot: import_node_path45.default.join(config.dataDir, "personas"),
46434
+ personaRoot: import_node_path46.default.join(config.dataDir, "personas"),
45669
46435
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
45670
46436
  usersRoot,
45671
46437
  // capability:list / delete handler 依赖
@@ -45754,9 +46520,11 @@ async function startDaemon(config) {
45754
46520
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
45755
46521
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
45756
46522
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
45757
- appBuilderPersonaRoot: import_node_path45.default.join(config.dataDir, "personas", "persona-app-builder"),
45758
- // scaffold 按当前 session persona 解析其安装根,让新 persona 复用流水线用自己的模板。
45759
- resolvePersonaRoot: (personaId) => import_node_path45.default.join(config.dataDir, "personas", personaId),
46523
+ appBuilderPersonaRoot: import_node_path46.default.join(config.dataDir, "personas", "persona-app-builder"),
46524
+ // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
46525
+ deployKitRoot: import_node_path46.default.join(config.dataDir, "deploy-kit"),
46526
+ // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
46527
+ resolvePersonaRoot: (personaId) => import_node_path46.default.join(config.dataDir, "personas", personaId),
45760
46528
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
45761
46529
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
45762
46530
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -45777,6 +46545,41 @@ async function startDaemon(config) {
45777
46545
  logger
45778
46546
  });
45779
46547
  let handlers = makeHandlers();
46548
+ const dispatchHandlers = buildPersonaDispatchHandlers({
46549
+ personaDispatchManager,
46550
+ logger,
46551
+ spawnB: async (args) => {
46552
+ logger.info("dispatch.spawnB.start", {
46553
+ dispatchId: args.dispatchId,
46554
+ targetPersona: args.targetPersona,
46555
+ sourceSessionId: args.sourceSessionId
46556
+ });
46557
+ const sourceFile = manager.findOwnedSession(args.sourceSessionId);
46558
+ if (!sourceFile) {
46559
+ throw new Error(`personaDispatch:run source session not found: ${args.sourceSessionId}`);
46560
+ }
46561
+ const sourceJsonlPath = sourceFile.toolSessionId ? import_node_path46.default.join(
46562
+ import_node_os19.default.homedir(),
46563
+ ".claude",
46564
+ "projects",
46565
+ cwdToHashDir(sourceFile.cwd),
46566
+ `${sourceFile.toolSessionId}.jsonl`
46567
+ ) : "(no transcript yet \u2014 operate from the task description alone)";
46568
+ logger.info("dispatch.spawnB.source-resolved", {
46569
+ dispatchId: args.dispatchId,
46570
+ sourceJsonlPath,
46571
+ hasToolSessionId: Boolean(sourceFile.toolSessionId)
46572
+ });
46573
+ manager.createDispatchedSession({
46574
+ dispatchId: args.dispatchId,
46575
+ sourceSessionId: args.sourceSessionId,
46576
+ targetPersona: args.targetPersona,
46577
+ prompt: args.prompt,
46578
+ sourceJsonlPath
46579
+ });
46580
+ }
46581
+ });
46582
+ handlers = { ...handlers, ...dispatchHandlers };
45780
46583
  const authResolver = new AuthContextResolver({
45781
46584
  ownerToken: resolvedAuthToken
45782
46585
  });
@@ -45851,6 +46654,10 @@ async function startDaemon(config) {
45851
46654
  manager,
45852
46655
  getAdapter,
45853
46656
  getTunnelUrl: () => currentTunnelUrl,
46657
+ getLogShipping: () => config.logShipping ? {
46658
+ endpoint: config.logShipping.endpoint,
46659
+ sampling: config.logShipping.sampling
46660
+ } : null,
45854
46661
  // ready 帧 mode = daemon CC spawn 模式('sdk' | 'tui');UI 用它挂 XtermPanel
45855
46662
  mode: config.mode,
45856
46663
  // file-sharing 字段:httpBaseUrl 跟 tunnel 状态走;httpToken 复用 owner WS token
@@ -46005,8 +46812,8 @@ async function startDaemon(config) {
46005
46812
  const lines = [
46006
46813
  `Tunnel: ${r.url}`,
46007
46814
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
46008
- `Frpc config: ${import_node_path45.default.join(config.dataDir, "frpc.toml")}`,
46009
- `Frpc log: ${import_node_path45.default.join(config.dataDir, "frpc.log")}`
46815
+ `Frpc config: ${import_node_path46.default.join(config.dataDir, "frpc.toml")}`,
46816
+ `Frpc log: ${import_node_path46.default.join(config.dataDir, "frpc.log")}`
46010
46817
  ];
46011
46818
  const width = Math.max(...lines.map((l) => l.length));
46012
46819
  const bar = "\u2550".repeat(width + 4);
@@ -46019,8 +46826,8 @@ ${bar}
46019
46826
 
46020
46827
  `);
46021
46828
  try {
46022
- const connectPath = import_node_path45.default.join(config.dataDir, "connect.txt");
46023
- import_node_fs33.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
46829
+ const connectPath = import_node_path46.default.join(config.dataDir, "connect.txt");
46830
+ import_node_fs34.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
46024
46831
  } catch {
46025
46832
  }
46026
46833
  } catch (err) {
@@ -46081,6 +46888,7 @@ ${bar}
46081
46888
  }
46082
46889
  await wss.stop();
46083
46890
  stateMgr.delete();
46891
+ if (logClient) await logClient.dispose();
46084
46892
  };
46085
46893
  return {
46086
46894
  stop: shutdown,
@@ -46091,9 +46899,9 @@ ${bar}
46091
46899
  };
46092
46900
  }
46093
46901
  function migrateDropPersonsDir(dataDir) {
46094
- const dir = import_node_path45.default.join(dataDir, "persons");
46902
+ const dir = import_node_path46.default.join(dataDir, "persons");
46095
46903
  try {
46096
- import_node_fs33.default.rmSync(dir, { recursive: true, force: true });
46904
+ import_node_fs34.default.rmSync(dir, { recursive: true, force: true });
46097
46905
  } catch {
46098
46906
  }
46099
46907
  }