@clawos-dev/clawd 0.2.71-beta.123.6a5ed6f → 0.2.71-beta.125.4951782

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 (2) hide show
  1. package/dist/cli.cjs +585 -134
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -121,6 +121,11 @@ var init_methods = __esm({
121
121
  "capability:issue",
122
122
  "capability:list",
123
123
  "capability:revoke",
124
+ // ---- inbox:* (capability platform Phase 3 跨用户通知) ----
125
+ // owner 接 guest 的 cross-principal 消息事件. 当前 Phase 3 仅 owner 调 (admin-only),
126
+ // Phase 4 DM 真接通后 guest 也能调 inbox:postMessage 等 (本批次不加 postMessage).
127
+ "inbox:list",
128
+ "inbox:markRead",
124
129
  "info",
125
130
  "ping"
126
131
  ];
@@ -612,8 +617,8 @@ var init_parseUtil = __esm({
612
617
  init_errors2();
613
618
  init_en();
614
619
  makeIssue = (params) => {
615
- const { data, path: path32, errorMaps, issueData } = params;
616
- const fullPath = [...path32, ...issueData.path || []];
620
+ const { data, path: path36, errorMaps, issueData } = params;
621
+ const fullPath = [...path36, ...issueData.path || []];
617
622
  const fullIssue = {
618
623
  ...issueData,
619
624
  path: fullPath
@@ -924,11 +929,11 @@ var init_types = __esm({
924
929
  init_parseUtil();
925
930
  init_util();
926
931
  ParseInputLazyPath = class {
927
- constructor(parent, value, path32, key) {
932
+ constructor(parent, value, path36, key) {
928
933
  this._cachedPath = [];
929
934
  this.parent = parent;
930
935
  this.data = value;
931
- this._path = path32;
936
+ this._path = path36;
932
937
  this._key = key;
933
938
  }
934
939
  get path() {
@@ -5112,6 +5117,40 @@ var init_capability = __esm({
5112
5117
  }
5113
5118
  });
5114
5119
 
5120
+ // ../protocol/src/inbox.ts
5121
+ var INBOX_EVENT_KIND_VALUES, InboxEventKindSchema, INBOX_PREVIEW_MAX_LENGTH, InboxEventSchema, InboxListArgsSchema, InboxMarkReadArgsSchema;
5122
+ var init_inbox = __esm({
5123
+ "../protocol/src/inbox.ts"() {
5124
+ "use strict";
5125
+ init_zod();
5126
+ init_principal();
5127
+ init_capability();
5128
+ INBOX_EVENT_KIND_VALUES = ["persona-mention", "direct-message"];
5129
+ InboxEventKindSchema = external_exports.enum(INBOX_EVENT_KIND_VALUES);
5130
+ INBOX_PREVIEW_MAX_LENGTH = 140;
5131
+ InboxEventSchema = external_exports.object({
5132
+ id: external_exports.string().min(1),
5133
+ kind: InboxEventKindSchema,
5134
+ fromPrincipal: PrincipalSchema,
5135
+ toPrincipal: PrincipalSchema,
5136
+ // persona-mention 才有, direct-message 不带
5137
+ resource: ResourceSchema.optional(),
5138
+ preview: external_exports.string().max(INBOX_PREVIEW_MAX_LENGTH),
5139
+ createdAt: external_exports.number().int().nonnegative(),
5140
+ readAt: external_exports.number().int().positive().optional(),
5141
+ // direct-message 才有, sha256 hex 64; 派生规则: sha256(min(A,B) + '/' + max(A,B))
5142
+ threadId: external_exports.string().regex(/^[a-f0-9]{64}$/).optional()
5143
+ }).strict();
5144
+ InboxListArgsSchema = external_exports.object({
5145
+ // false / 缺省 = 只返回未读; true = 含已读 (历史回溯用)
5146
+ includeRead: external_exports.boolean().optional()
5147
+ }).strict();
5148
+ InboxMarkReadArgsSchema = external_exports.object({
5149
+ eventId: external_exports.string().min(1)
5150
+ }).strict();
5151
+ }
5152
+ });
5153
+
5115
5154
  // ../protocol/src/runtime.ts
5116
5155
  var init_runtime = __esm({
5117
5156
  "../protocol/src/runtime.ts"() {
@@ -5126,6 +5165,7 @@ var init_runtime = __esm({
5126
5165
  init_attachment_schemas();
5127
5166
  init_principal();
5128
5167
  init_capability();
5168
+ init_inbox();
5129
5169
  }
5130
5170
  });
5131
5171
 
@@ -5400,8 +5440,8 @@ var require_req = __commonJS({
5400
5440
  if (req.originalUrl) {
5401
5441
  _req.url = req.originalUrl;
5402
5442
  } else {
5403
- const path32 = req.path;
5404
- _req.url = typeof path32 === "string" ? path32 : req.url ? req.url.path || req.url : void 0;
5443
+ const path36 = req.path;
5444
+ _req.url = typeof path36 === "string" ? path36 : req.url ? req.url.path || req.url : void 0;
5405
5445
  }
5406
5446
  if (req.query) {
5407
5447
  _req.query = req.query;
@@ -5566,14 +5606,14 @@ var require_redact = __commonJS({
5566
5606
  }
5567
5607
  return obj;
5568
5608
  }
5569
- function parsePath(path32) {
5609
+ function parsePath(path36) {
5570
5610
  const parts = [];
5571
5611
  let current = "";
5572
5612
  let inBrackets = false;
5573
5613
  let inQuotes = false;
5574
5614
  let quoteChar = "";
5575
- for (let i = 0; i < path32.length; i++) {
5576
- const char = path32[i];
5615
+ for (let i = 0; i < path36.length; i++) {
5616
+ const char = path36[i];
5577
5617
  if (!inBrackets && char === ".") {
5578
5618
  if (current) {
5579
5619
  parts.push(current);
@@ -5704,10 +5744,10 @@ var require_redact = __commonJS({
5704
5744
  return current;
5705
5745
  }
5706
5746
  function redactPaths(obj, paths, censor, remove = false) {
5707
- for (const path32 of paths) {
5708
- const parts = parsePath(path32);
5747
+ for (const path36 of paths) {
5748
+ const parts = parsePath(path36);
5709
5749
  if (parts.includes("*")) {
5710
- redactWildcardPath(obj, parts, censor, path32, remove);
5750
+ redactWildcardPath(obj, parts, censor, path36, remove);
5711
5751
  } else {
5712
5752
  if (remove) {
5713
5753
  removeKey(obj, parts);
@@ -5792,8 +5832,8 @@ var require_redact = __commonJS({
5792
5832
  }
5793
5833
  } else {
5794
5834
  if (afterWildcard.includes("*")) {
5795
- const wrappedCensor = typeof censor === "function" ? (value, path32) => {
5796
- const fullPath = [...pathArray.slice(0, pathLength), ...path32];
5835
+ const wrappedCensor = typeof censor === "function" ? (value, path36) => {
5836
+ const fullPath = [...pathArray.slice(0, pathLength), ...path36];
5797
5837
  return censor(value, fullPath);
5798
5838
  } : censor;
5799
5839
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -5828,8 +5868,8 @@ var require_redact = __commonJS({
5828
5868
  return null;
5829
5869
  }
5830
5870
  const pathStructure = /* @__PURE__ */ new Map();
5831
- for (const path32 of pathsToClone) {
5832
- const parts = parsePath(path32);
5871
+ for (const path36 of pathsToClone) {
5872
+ const parts = parsePath(path36);
5833
5873
  let current = pathStructure;
5834
5874
  for (let i = 0; i < parts.length; i++) {
5835
5875
  const part = parts[i];
@@ -5881,24 +5921,24 @@ var require_redact = __commonJS({
5881
5921
  }
5882
5922
  return cloneSelectively(obj, pathStructure);
5883
5923
  }
5884
- function validatePath(path32) {
5885
- if (typeof path32 !== "string") {
5924
+ function validatePath(path36) {
5925
+ if (typeof path36 !== "string") {
5886
5926
  throw new Error("Paths must be (non-empty) strings");
5887
5927
  }
5888
- if (path32 === "") {
5928
+ if (path36 === "") {
5889
5929
  throw new Error("Invalid redaction path ()");
5890
5930
  }
5891
- if (path32.includes("..")) {
5892
- throw new Error(`Invalid redaction path (${path32})`);
5931
+ if (path36.includes("..")) {
5932
+ throw new Error(`Invalid redaction path (${path36})`);
5893
5933
  }
5894
- if (path32.includes(",")) {
5895
- throw new Error(`Invalid redaction path (${path32})`);
5934
+ if (path36.includes(",")) {
5935
+ throw new Error(`Invalid redaction path (${path36})`);
5896
5936
  }
5897
5937
  let bracketCount = 0;
5898
5938
  let inQuotes = false;
5899
5939
  let quoteChar = "";
5900
- for (let i = 0; i < path32.length; i++) {
5901
- const char = path32[i];
5940
+ for (let i = 0; i < path36.length; i++) {
5941
+ const char = path36[i];
5902
5942
  if ((char === '"' || char === "'") && bracketCount > 0) {
5903
5943
  if (!inQuotes) {
5904
5944
  inQuotes = true;
@@ -5912,20 +5952,20 @@ var require_redact = __commonJS({
5912
5952
  } else if (char === "]" && !inQuotes) {
5913
5953
  bracketCount--;
5914
5954
  if (bracketCount < 0) {
5915
- throw new Error(`Invalid redaction path (${path32})`);
5955
+ throw new Error(`Invalid redaction path (${path36})`);
5916
5956
  }
5917
5957
  }
5918
5958
  }
5919
5959
  if (bracketCount !== 0) {
5920
- throw new Error(`Invalid redaction path (${path32})`);
5960
+ throw new Error(`Invalid redaction path (${path36})`);
5921
5961
  }
5922
5962
  }
5923
5963
  function validatePaths(paths) {
5924
5964
  if (!Array.isArray(paths)) {
5925
5965
  throw new TypeError("paths must be an array");
5926
5966
  }
5927
- for (const path32 of paths) {
5928
- validatePath(path32);
5967
+ for (const path36 of paths) {
5968
+ validatePath(path36);
5929
5969
  }
5930
5970
  }
5931
5971
  function slowRedact(options = {}) {
@@ -6093,8 +6133,8 @@ var require_redaction = __commonJS({
6093
6133
  if (shape[k2] === null) {
6094
6134
  o[k2] = (value) => topCensor(value, [k2]);
6095
6135
  } else {
6096
- const wrappedCensor = typeof censor === "function" ? (value, path32) => {
6097
- return censor(value, [k2, ...path32]);
6136
+ const wrappedCensor = typeof censor === "function" ? (value, path36) => {
6137
+ return censor(value, [k2, ...path36]);
6098
6138
  } : censor;
6099
6139
  o[k2] = Redact({
6100
6140
  paths: shape[k2],
@@ -6312,10 +6352,10 @@ var require_atomic_sleep = __commonJS({
6312
6352
  var require_sonic_boom = __commonJS({
6313
6353
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
6314
6354
  "use strict";
6315
- var fs29 = require("fs");
6355
+ var fs32 = require("fs");
6316
6356
  var EventEmitter2 = require("events");
6317
6357
  var inherits = require("util").inherits;
6318
- var path32 = require("path");
6358
+ var path36 = require("path");
6319
6359
  var sleep = require_atomic_sleep();
6320
6360
  var assert = require("assert");
6321
6361
  var BUSY_WRITE_TIMEOUT = 100;
@@ -6369,20 +6409,20 @@ var require_sonic_boom = __commonJS({
6369
6409
  const mode = sonic.mode;
6370
6410
  if (sonic.sync) {
6371
6411
  try {
6372
- if (sonic.mkdir) fs29.mkdirSync(path32.dirname(file), { recursive: true });
6373
- const fd = fs29.openSync(file, flags, mode);
6412
+ if (sonic.mkdir) fs32.mkdirSync(path36.dirname(file), { recursive: true });
6413
+ const fd = fs32.openSync(file, flags, mode);
6374
6414
  fileOpened(null, fd);
6375
6415
  } catch (err) {
6376
6416
  fileOpened(err);
6377
6417
  throw err;
6378
6418
  }
6379
6419
  } else if (sonic.mkdir) {
6380
- fs29.mkdir(path32.dirname(file), { recursive: true }, (err) => {
6420
+ fs32.mkdir(path36.dirname(file), { recursive: true }, (err) => {
6381
6421
  if (err) return fileOpened(err);
6382
- fs29.open(file, flags, mode, fileOpened);
6422
+ fs32.open(file, flags, mode, fileOpened);
6383
6423
  });
6384
6424
  } else {
6385
- fs29.open(file, flags, mode, fileOpened);
6425
+ fs32.open(file, flags, mode, fileOpened);
6386
6426
  }
6387
6427
  }
6388
6428
  function SonicBoom(opts) {
@@ -6423,8 +6463,8 @@ var require_sonic_boom = __commonJS({
6423
6463
  this.flush = flushBuffer;
6424
6464
  this.flushSync = flushBufferSync;
6425
6465
  this._actualWrite = actualWriteBuffer;
6426
- fsWriteSync = () => fs29.writeSync(this.fd, this._writingBuf);
6427
- fsWrite = () => fs29.write(this.fd, this._writingBuf, this.release);
6466
+ fsWriteSync = () => fs32.writeSync(this.fd, this._writingBuf);
6467
+ fsWrite = () => fs32.write(this.fd, this._writingBuf, this.release);
6428
6468
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
6429
6469
  this._writingBuf = "";
6430
6470
  this.write = write;
@@ -6433,15 +6473,15 @@ var require_sonic_boom = __commonJS({
6433
6473
  this._actualWrite = actualWrite;
6434
6474
  fsWriteSync = () => {
6435
6475
  if (Buffer.isBuffer(this._writingBuf)) {
6436
- return fs29.writeSync(this.fd, this._writingBuf);
6476
+ return fs32.writeSync(this.fd, this._writingBuf);
6437
6477
  }
6438
- return fs29.writeSync(this.fd, this._writingBuf, "utf8");
6478
+ return fs32.writeSync(this.fd, this._writingBuf, "utf8");
6439
6479
  };
6440
6480
  fsWrite = () => {
6441
6481
  if (Buffer.isBuffer(this._writingBuf)) {
6442
- return fs29.write(this.fd, this._writingBuf, this.release);
6482
+ return fs32.write(this.fd, this._writingBuf, this.release);
6443
6483
  }
6444
- return fs29.write(this.fd, this._writingBuf, "utf8", this.release);
6484
+ return fs32.write(this.fd, this._writingBuf, "utf8", this.release);
6445
6485
  };
6446
6486
  } else {
6447
6487
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -6498,7 +6538,7 @@ var require_sonic_boom = __commonJS({
6498
6538
  }
6499
6539
  }
6500
6540
  if (this._fsync) {
6501
- fs29.fsyncSync(this.fd);
6541
+ fs32.fsyncSync(this.fd);
6502
6542
  }
6503
6543
  const len = this._len;
6504
6544
  if (this._reopening) {
@@ -6612,7 +6652,7 @@ var require_sonic_boom = __commonJS({
6612
6652
  const onDrain = () => {
6613
6653
  if (!this._fsync) {
6614
6654
  try {
6615
- fs29.fsync(this.fd, (err) => {
6655
+ fs32.fsync(this.fd, (err) => {
6616
6656
  this._flushPending = false;
6617
6657
  cb(err);
6618
6658
  });
@@ -6714,7 +6754,7 @@ var require_sonic_boom = __commonJS({
6714
6754
  const fd = this.fd;
6715
6755
  this.once("ready", () => {
6716
6756
  if (fd !== this.fd) {
6717
- fs29.close(fd, (err) => {
6757
+ fs32.close(fd, (err) => {
6718
6758
  if (err) {
6719
6759
  return this.emit("error", err);
6720
6760
  }
@@ -6763,7 +6803,7 @@ var require_sonic_boom = __commonJS({
6763
6803
  buf = this._bufs[0];
6764
6804
  }
6765
6805
  try {
6766
- const n = Buffer.isBuffer(buf) ? fs29.writeSync(this.fd, buf) : fs29.writeSync(this.fd, buf, "utf8");
6806
+ const n = Buffer.isBuffer(buf) ? fs32.writeSync(this.fd, buf) : fs32.writeSync(this.fd, buf, "utf8");
6767
6807
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
6768
6808
  buf = releasedBufObj.writingBuf;
6769
6809
  this._len = releasedBufObj.len;
@@ -6779,7 +6819,7 @@ var require_sonic_boom = __commonJS({
6779
6819
  }
6780
6820
  }
6781
6821
  try {
6782
- fs29.fsyncSync(this.fd);
6822
+ fs32.fsyncSync(this.fd);
6783
6823
  } catch {
6784
6824
  }
6785
6825
  }
@@ -6800,7 +6840,7 @@ var require_sonic_boom = __commonJS({
6800
6840
  buf = mergeBuf(this._bufs[0], this._lens[0]);
6801
6841
  }
6802
6842
  try {
6803
- const n = fs29.writeSync(this.fd, buf);
6843
+ const n = fs32.writeSync(this.fd, buf);
6804
6844
  buf = buf.subarray(n);
6805
6845
  this._len = Math.max(this._len - n, 0);
6806
6846
  if (buf.length <= 0) {
@@ -6828,13 +6868,13 @@ var require_sonic_boom = __commonJS({
6828
6868
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
6829
6869
  if (this.sync) {
6830
6870
  try {
6831
- const written = Buffer.isBuffer(this._writingBuf) ? fs29.writeSync(this.fd, this._writingBuf) : fs29.writeSync(this.fd, this._writingBuf, "utf8");
6871
+ const written = Buffer.isBuffer(this._writingBuf) ? fs32.writeSync(this.fd, this._writingBuf) : fs32.writeSync(this.fd, this._writingBuf, "utf8");
6832
6872
  release(null, written);
6833
6873
  } catch (err) {
6834
6874
  release(err);
6835
6875
  }
6836
6876
  } else {
6837
- fs29.write(this.fd, this._writingBuf, release);
6877
+ fs32.write(this.fd, this._writingBuf, release);
6838
6878
  }
6839
6879
  }
6840
6880
  function actualWriteBuffer() {
@@ -6843,7 +6883,7 @@ var require_sonic_boom = __commonJS({
6843
6883
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
6844
6884
  if (this.sync) {
6845
6885
  try {
6846
- const written = fs29.writeSync(this.fd, this._writingBuf);
6886
+ const written = fs32.writeSync(this.fd, this._writingBuf);
6847
6887
  release(null, written);
6848
6888
  } catch (err) {
6849
6889
  release(err);
@@ -6852,7 +6892,7 @@ var require_sonic_boom = __commonJS({
6852
6892
  if (kCopyBuffer) {
6853
6893
  this._writingBuf = Buffer.from(this._writingBuf);
6854
6894
  }
6855
- fs29.write(this.fd, this._writingBuf, release);
6895
+ fs32.write(this.fd, this._writingBuf, release);
6856
6896
  }
6857
6897
  }
6858
6898
  function actualClose(sonic) {
@@ -6868,12 +6908,12 @@ var require_sonic_boom = __commonJS({
6868
6908
  sonic._lens = [];
6869
6909
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
6870
6910
  try {
6871
- fs29.fsync(sonic.fd, closeWrapped);
6911
+ fs32.fsync(sonic.fd, closeWrapped);
6872
6912
  } catch {
6873
6913
  }
6874
6914
  function closeWrapped() {
6875
6915
  if (sonic.fd !== 1 && sonic.fd !== 2) {
6876
- fs29.close(sonic.fd, done);
6916
+ fs32.close(sonic.fd, done);
6877
6917
  } else {
6878
6918
  done();
6879
6919
  }
@@ -7130,7 +7170,7 @@ var require_thread_stream = __commonJS({
7130
7170
  var { version: version2 } = require_package();
7131
7171
  var { EventEmitter: EventEmitter2 } = require("events");
7132
7172
  var { Worker } = require("worker_threads");
7133
- var { join: join5 } = require("path");
7173
+ var { join: join9 } = require("path");
7134
7174
  var { pathToFileURL } = require("url");
7135
7175
  var { wait } = require_wait();
7136
7176
  var {
@@ -7166,7 +7206,7 @@ var require_thread_stream = __commonJS({
7166
7206
  function createWorker(stream, opts) {
7167
7207
  const { filename, workerData } = opts;
7168
7208
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
7169
- const toExecute = bundlerOverrides["thread-stream-worker"] || join5(__dirname, "lib", "worker.js");
7209
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join9(__dirname, "lib", "worker.js");
7170
7210
  const worker = new Worker(toExecute, {
7171
7211
  ...opts.workerOpts,
7172
7212
  trackUnmanagedFds: false,
@@ -7552,7 +7592,7 @@ var require_transport = __commonJS({
7552
7592
  "use strict";
7553
7593
  var { createRequire } = require("module");
7554
7594
  var getCallers = require_caller();
7555
- var { join: join5, isAbsolute, sep } = require("path");
7595
+ var { join: join9, isAbsolute, sep: sep2 } = require("path");
7556
7596
  var sleep = require_atomic_sleep();
7557
7597
  var onExit = require_on_exit_leak_free();
7558
7598
  var ThreadStream = require_thread_stream();
@@ -7615,7 +7655,7 @@ var require_transport = __commonJS({
7615
7655
  throw new Error("only one of target or targets can be specified");
7616
7656
  }
7617
7657
  if (targets) {
7618
- target = bundlerOverrides["pino-worker"] || join5(__dirname, "worker.js");
7658
+ target = bundlerOverrides["pino-worker"] || join9(__dirname, "worker.js");
7619
7659
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
7620
7660
  return {
7621
7661
  ...dest,
@@ -7633,7 +7673,7 @@ var require_transport = __commonJS({
7633
7673
  });
7634
7674
  });
7635
7675
  } else if (pipeline2) {
7636
- target = bundlerOverrides["pino-worker"] || join5(__dirname, "worker.js");
7676
+ target = bundlerOverrides["pino-worker"] || join9(__dirname, "worker.js");
7637
7677
  options.pipelines = [pipeline2.map((dest) => {
7638
7678
  return {
7639
7679
  ...dest,
@@ -7655,12 +7695,12 @@ var require_transport = __commonJS({
7655
7695
  return origin;
7656
7696
  }
7657
7697
  if (origin === "pino/file") {
7658
- return join5(__dirname, "..", "file.js");
7698
+ return join9(__dirname, "..", "file.js");
7659
7699
  }
7660
7700
  let fixTarget2;
7661
7701
  for (const filePath of callers) {
7662
7702
  try {
7663
- const context = filePath === "node:repl" ? process.cwd() + sep : filePath;
7703
+ const context = filePath === "node:repl" ? process.cwd() + sep2 : filePath;
7664
7704
  fixTarget2 = createRequire(context).resolve(origin);
7665
7705
  break;
7666
7706
  } catch (err) {
@@ -8645,7 +8685,7 @@ var require_safe_stable_stringify = __commonJS({
8645
8685
  return circularValue;
8646
8686
  }
8647
8687
  let res = "";
8648
- let join5 = ",";
8688
+ let join9 = ",";
8649
8689
  const originalIndentation = indentation;
8650
8690
  if (Array.isArray(value)) {
8651
8691
  if (value.length === 0) {
@@ -8659,7 +8699,7 @@ var require_safe_stable_stringify = __commonJS({
8659
8699
  indentation += spacer;
8660
8700
  res += `
8661
8701
  ${indentation}`;
8662
- join5 = `,
8702
+ join9 = `,
8663
8703
  ${indentation}`;
8664
8704
  }
8665
8705
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -8667,13 +8707,13 @@ ${indentation}`;
8667
8707
  for (; i < maximumValuesToStringify - 1; i++) {
8668
8708
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
8669
8709
  res += tmp2 !== void 0 ? tmp2 : "null";
8670
- res += join5;
8710
+ res += join9;
8671
8711
  }
8672
8712
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
8673
8713
  res += tmp !== void 0 ? tmp : "null";
8674
8714
  if (value.length - 1 > maximumBreadth) {
8675
8715
  const removedKeys = value.length - maximumBreadth - 1;
8676
- res += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
8716
+ res += `${join9}"... ${getItemCount(removedKeys)} not stringified"`;
8677
8717
  }
8678
8718
  if (spacer !== "") {
8679
8719
  res += `
@@ -8694,7 +8734,7 @@ ${originalIndentation}`;
8694
8734
  let separator = "";
8695
8735
  if (spacer !== "") {
8696
8736
  indentation += spacer;
8697
- join5 = `,
8737
+ join9 = `,
8698
8738
  ${indentation}`;
8699
8739
  whitespace = " ";
8700
8740
  }
@@ -8708,13 +8748,13 @@ ${indentation}`;
8708
8748
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
8709
8749
  if (tmp !== void 0) {
8710
8750
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
8711
- separator = join5;
8751
+ separator = join9;
8712
8752
  }
8713
8753
  }
8714
8754
  if (keyLength > maximumBreadth) {
8715
8755
  const removedKeys = keyLength - maximumBreadth;
8716
8756
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
8717
- separator = join5;
8757
+ separator = join9;
8718
8758
  }
8719
8759
  if (spacer !== "" && separator.length > 1) {
8720
8760
  res = `
@@ -8755,7 +8795,7 @@ ${originalIndentation}`;
8755
8795
  }
8756
8796
  const originalIndentation = indentation;
8757
8797
  let res = "";
8758
- let join5 = ",";
8798
+ let join9 = ",";
8759
8799
  if (Array.isArray(value)) {
8760
8800
  if (value.length === 0) {
8761
8801
  return "[]";
@@ -8768,7 +8808,7 @@ ${originalIndentation}`;
8768
8808
  indentation += spacer;
8769
8809
  res += `
8770
8810
  ${indentation}`;
8771
- join5 = `,
8811
+ join9 = `,
8772
8812
  ${indentation}`;
8773
8813
  }
8774
8814
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -8776,13 +8816,13 @@ ${indentation}`;
8776
8816
  for (; i < maximumValuesToStringify - 1; i++) {
8777
8817
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
8778
8818
  res += tmp2 !== void 0 ? tmp2 : "null";
8779
- res += join5;
8819
+ res += join9;
8780
8820
  }
8781
8821
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
8782
8822
  res += tmp !== void 0 ? tmp : "null";
8783
8823
  if (value.length - 1 > maximumBreadth) {
8784
8824
  const removedKeys = value.length - maximumBreadth - 1;
8785
- res += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
8825
+ res += `${join9}"... ${getItemCount(removedKeys)} not stringified"`;
8786
8826
  }
8787
8827
  if (spacer !== "") {
8788
8828
  res += `
@@ -8795,7 +8835,7 @@ ${originalIndentation}`;
8795
8835
  let whitespace = "";
8796
8836
  if (spacer !== "") {
8797
8837
  indentation += spacer;
8798
- join5 = `,
8838
+ join9 = `,
8799
8839
  ${indentation}`;
8800
8840
  whitespace = " ";
8801
8841
  }
@@ -8804,7 +8844,7 @@ ${indentation}`;
8804
8844
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
8805
8845
  if (tmp !== void 0) {
8806
8846
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
8807
- separator = join5;
8847
+ separator = join9;
8808
8848
  }
8809
8849
  }
8810
8850
  if (spacer !== "" && separator.length > 1) {
@@ -8862,20 +8902,20 @@ ${originalIndentation}`;
8862
8902
  indentation += spacer;
8863
8903
  let res2 = `
8864
8904
  ${indentation}`;
8865
- const join6 = `,
8905
+ const join10 = `,
8866
8906
  ${indentation}`;
8867
8907
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
8868
8908
  let i = 0;
8869
8909
  for (; i < maximumValuesToStringify - 1; i++) {
8870
8910
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
8871
8911
  res2 += tmp2 !== void 0 ? tmp2 : "null";
8872
- res2 += join6;
8912
+ res2 += join10;
8873
8913
  }
8874
8914
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
8875
8915
  res2 += tmp !== void 0 ? tmp : "null";
8876
8916
  if (value.length - 1 > maximumBreadth) {
8877
8917
  const removedKeys = value.length - maximumBreadth - 1;
8878
- res2 += `${join6}"... ${getItemCount(removedKeys)} not stringified"`;
8918
+ res2 += `${join10}"... ${getItemCount(removedKeys)} not stringified"`;
8879
8919
  }
8880
8920
  res2 += `
8881
8921
  ${originalIndentation}`;
@@ -8891,16 +8931,16 @@ ${originalIndentation}`;
8891
8931
  return '"[Object]"';
8892
8932
  }
8893
8933
  indentation += spacer;
8894
- const join5 = `,
8934
+ const join9 = `,
8895
8935
  ${indentation}`;
8896
8936
  let res = "";
8897
8937
  let separator = "";
8898
8938
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
8899
8939
  if (isTypedArrayWithEntries(value)) {
8900
- res += stringifyTypedArray(value, join5, maximumBreadth);
8940
+ res += stringifyTypedArray(value, join9, maximumBreadth);
8901
8941
  keys = keys.slice(value.length);
8902
8942
  maximumPropertiesToStringify -= value.length;
8903
- separator = join5;
8943
+ separator = join9;
8904
8944
  }
8905
8945
  if (deterministic) {
8906
8946
  keys = sort(keys, comparator);
@@ -8911,13 +8951,13 @@ ${indentation}`;
8911
8951
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
8912
8952
  if (tmp !== void 0) {
8913
8953
  res += `${separator}${strEscape(key2)}: ${tmp}`;
8914
- separator = join5;
8954
+ separator = join9;
8915
8955
  }
8916
8956
  }
8917
8957
  if (keyLength > maximumBreadth) {
8918
8958
  const removedKeys = keyLength - maximumBreadth;
8919
8959
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
8920
- separator = join5;
8960
+ separator = join9;
8921
8961
  }
8922
8962
  if (separator !== "") {
8923
8963
  res = `
@@ -10008,11 +10048,11 @@ var init_lib = __esm({
10008
10048
  }
10009
10049
  }
10010
10050
  },
10011
- addToPath: function addToPath(path32, added, removed, oldPosInc, options) {
10012
- var last = path32.lastComponent;
10051
+ addToPath: function addToPath(path36, added, removed, oldPosInc, options) {
10052
+ var last = path36.lastComponent;
10013
10053
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10014
10054
  return {
10015
- oldPos: path32.oldPos + oldPosInc,
10055
+ oldPos: path36.oldPos + oldPosInc,
10016
10056
  lastComponent: {
10017
10057
  count: last.count + 1,
10018
10058
  added,
@@ -10022,7 +10062,7 @@ var init_lib = __esm({
10022
10062
  };
10023
10063
  } else {
10024
10064
  return {
10025
- oldPos: path32.oldPos + oldPosInc,
10065
+ oldPos: path36.oldPos + oldPosInc,
10026
10066
  lastComponent: {
10027
10067
  count: 1,
10028
10068
  added,
@@ -10080,7 +10120,7 @@ var init_lib = __esm({
10080
10120
  tokenize: function tokenize(value) {
10081
10121
  return Array.from(value);
10082
10122
  },
10083
- join: function join3(chars) {
10123
+ join: function join4(chars) {
10084
10124
  return chars.join("");
10085
10125
  },
10086
10126
  postProcess: function postProcess(changeObjects) {
@@ -10453,10 +10493,10 @@ function attachmentToHistoryMessage(o, ts) {
10453
10493
  const memories = raw.map((m2) => {
10454
10494
  if (!m2 || typeof m2 !== "object") return null;
10455
10495
  const rec = m2;
10456
- const path32 = typeof rec.path === "string" ? rec.path : null;
10496
+ const path36 = typeof rec.path === "string" ? rec.path : null;
10457
10497
  const content = typeof rec.content === "string" ? rec.content : null;
10458
- if (!path32 || content == null) return null;
10459
- const entry = { path: path32, content };
10498
+ if (!path36 || content == null) return null;
10499
+ const entry = { path: path36, content };
10460
10500
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
10461
10501
  return entry;
10462
10502
  }).filter((m2) => m2 !== null);
@@ -10916,6 +10956,27 @@ var init_claude_history = __esm({
10916
10956
  }
10917
10957
  });
10918
10958
 
10959
+ // src/tools/sandbox.ts
10960
+ function shouldSandbox(cwd, personaRoot) {
10961
+ if (!personaRoot) return false;
10962
+ const sep2 = personaRoot.endsWith(path12.sep) ? "" : path12.sep;
10963
+ return cwd.startsWith(personaRoot + sep2) && cwd !== personaRoot;
10964
+ }
10965
+ function inferSandboxSettingsPath(cwd, personaRoot) {
10966
+ if (!shouldSandbox(cwd, personaRoot)) return null;
10967
+ const rel = path12.relative(personaRoot, cwd);
10968
+ const personaId = rel.split(path12.sep)[0];
10969
+ if (!personaId) return null;
10970
+ return path12.join(personaRoot, personaId, ".clawd", "sandbox-settings.json");
10971
+ }
10972
+ var path12;
10973
+ var init_sandbox = __esm({
10974
+ "src/tools/sandbox.ts"() {
10975
+ "use strict";
10976
+ path12 = __toESM(require("path"), 1);
10977
+ }
10978
+ });
10979
+
10919
10980
  // src/tools/claude.ts
10920
10981
  function macOSDesktopCandidates(home) {
10921
10982
  return [
@@ -10980,7 +11041,8 @@ function buildSpawnArgs(ctx) {
10980
11041
  throw new Error(`unexpected personaMode: ${String(_exhaustive)}`);
10981
11042
  }
10982
11043
  }
10983
- if (ctx.extraSettings) args.push("--settings", ctx.extraSettings);
11044
+ const sandboxSettings = ctx.extraSettings ?? inferSandboxSettingsPath(ctx.cwd, ctx.personaRoot);
11045
+ if (sandboxSettings) args.push("--settings", sandboxSettings);
10984
11046
  if (ctx.extraSystemPrompt) args.push("--append-system-prompt", ctx.extraSystemPrompt);
10985
11047
  if (ctx.effort) args.push("--effort", ctx.effort);
10986
11048
  if (ctx.toolSessionId) args.push("--resume", ctx.toolSessionId);
@@ -11260,10 +11322,10 @@ function parseAttachment(obj) {
11260
11322
  const memories = raw.map((m2) => {
11261
11323
  if (!m2 || typeof m2 !== "object") return null;
11262
11324
  const rec = m2;
11263
- const path32 = typeof rec.path === "string" ? rec.path : null;
11325
+ const path36 = typeof rec.path === "string" ? rec.path : null;
11264
11326
  const content = typeof rec.content === "string" ? rec.content : null;
11265
- if (!path32 || content == null) return null;
11266
- const out = { path: path32, content };
11327
+ if (!path36 || content == null) return null;
11328
+ const out = { path: path36, content };
11267
11329
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11268
11330
  return out;
11269
11331
  }).filter((m2) => m2 !== null);
@@ -11379,6 +11441,7 @@ var init_claude = __esm({
11379
11441
  init_protocol();
11380
11442
  init_claude_history();
11381
11443
  init_tool_result_extra();
11444
+ init_sandbox();
11382
11445
  ATTACHMENT_SILENT_SUBTYPES2 = /* @__PURE__ */ new Set([
11383
11446
  "hook_additional_context",
11384
11447
  "hook_success",
@@ -18761,7 +18824,7 @@ var require_websocket = __commonJS({
18761
18824
  var http2 = require("http");
18762
18825
  var net = require("net");
18763
18826
  var tls = require("tls");
18764
- var { randomBytes: randomBytes2, createHash: createHash3 } = require("crypto");
18827
+ var { randomBytes: randomBytes3, createHash: createHash3 } = require("crypto");
18765
18828
  var { Duplex, Readable: Readable3 } = require("stream");
18766
18829
  var { URL: URL2 } = require("url");
18767
18830
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -19291,7 +19354,7 @@ var require_websocket = __commonJS({
19291
19354
  }
19292
19355
  }
19293
19356
  const defaultPort = isSecure ? 443 : 80;
19294
- const key = randomBytes2(16).toString("base64");
19357
+ const key = randomBytes3(16).toString("base64");
19295
19358
  const request = isSecure ? https.request : http2.request;
19296
19359
  const protocolSet = /* @__PURE__ */ new Set();
19297
19360
  let perMessageDeflate;
@@ -20708,6 +20771,9 @@ function createLogger(opts = {}) {
20708
20771
  return wrap(base);
20709
20772
  }
20710
20773
 
20774
+ // src/session/store-factory.ts
20775
+ var path5 = __toESM(require("path"), 1);
20776
+
20711
20777
  // src/session/store.ts
20712
20778
  var import_node_fs3 = __toESM(require("fs"), 1);
20713
20779
  var import_node_path4 = __toESM(require("path"), 1);
@@ -20745,12 +20811,16 @@ function safeFileName(sessionId) {
20745
20811
  var SessionStore = class {
20746
20812
  root;
20747
20813
  constructor(opts) {
20748
- const scope = opts.scope ?? { kind: "default" };
20749
- this.root = import_node_path4.default.join(
20750
- opts.dataDir,
20751
- "sessions",
20752
- ...scopeSubPath(scope).map(safeFileName)
20753
- );
20814
+ if ("root" in opts) {
20815
+ this.root = opts.root;
20816
+ } else {
20817
+ const scope = opts.scope ?? { kind: "default" };
20818
+ this.root = import_node_path4.default.join(
20819
+ opts.dataDir,
20820
+ "sessions",
20821
+ ...scopeSubPath(scope).map(safeFileName)
20822
+ );
20823
+ }
20754
20824
  }
20755
20825
  filePath(sessionId) {
20756
20826
  return import_node_path4.default.join(this.root, `${safeFileName(sessionId)}.json`);
@@ -20815,6 +20885,66 @@ var SessionStore = class {
20815
20885
  }
20816
20886
  };
20817
20887
 
20888
+ // src/session/store-factory.ts
20889
+ var SessionStoreFactory = class {
20890
+ dataDir;
20891
+ bareStore = null;
20892
+ vmOwnerStores = /* @__PURE__ */ new Map();
20893
+ // vmGuest 索引 key = `${pid}::${capId}`
20894
+ vmGuestStores = /* @__PURE__ */ new Map();
20895
+ constructor(opts) {
20896
+ this.dataDir = opts.dataDir;
20897
+ }
20898
+ // ---- root path 派生(暴露给 migration / revoke cascade 等外部消费方) ----
20899
+ bareRoot() {
20900
+ return path5.join(this.dataDir, "sessions");
20901
+ }
20902
+ vmOwnerRoot(personaId) {
20903
+ return path5.join(
20904
+ this.dataDir,
20905
+ "personas",
20906
+ safeFileName(personaId),
20907
+ ".clawd",
20908
+ "sessions",
20909
+ "owner"
20910
+ );
20911
+ }
20912
+ vmGuestRoot(personaId, capabilityId) {
20913
+ return path5.join(
20914
+ this.dataDir,
20915
+ "personas",
20916
+ safeFileName(personaId),
20917
+ ".clawd",
20918
+ "sessions",
20919
+ "guests",
20920
+ safeFileName(capabilityId)
20921
+ );
20922
+ }
20923
+ // ---- SessionStore 工厂(缓存) ----
20924
+ forBare() {
20925
+ if (!this.bareStore) {
20926
+ this.bareStore = new SessionStore({ root: this.bareRoot() });
20927
+ }
20928
+ return this.bareStore;
20929
+ }
20930
+ forVmOwner(personaId) {
20931
+ const key = personaId;
20932
+ const cached = this.vmOwnerStores.get(key);
20933
+ if (cached) return cached;
20934
+ const st = new SessionStore({ root: this.vmOwnerRoot(personaId) });
20935
+ this.vmOwnerStores.set(key, st);
20936
+ return st;
20937
+ }
20938
+ forVmGuest(personaId, capabilityId) {
20939
+ const key = `${personaId}::${capabilityId}`;
20940
+ const cached = this.vmGuestStores.get(key);
20941
+ if (cached) return cached;
20942
+ const st = new SessionStore({ root: this.vmGuestRoot(personaId, capabilityId) });
20943
+ this.vmGuestStores.set(key, st);
20944
+ return st;
20945
+ }
20946
+ };
20947
+
20818
20948
  // src/session/manager.ts
20819
20949
  var import_node_fs5 = __toESM(require("fs"), 1);
20820
20950
  var import_node_path6 = __toESM(require("path"), 1);
@@ -20974,7 +21104,10 @@ function buildSpawnContext(state, deps) {
20974
21104
  toolSessionId: file.toolSessionId,
20975
21105
  model: file.model,
20976
21106
  permissionMode: file.permissionMode,
20977
- effort: file.effort
21107
+ effort: file.effort,
21108
+ // Phase 2 capability platform (plan §3): personaRoot 透传给 buildSpawnArgs;
21109
+ // 内部 shouldSandbox(cwd, personaRoot) 决定是否注入 --settings sandbox-settings.
21110
+ personaRoot: deps.personaRoot
20978
21111
  };
20979
21112
  const meta = state.subSessionMeta;
20980
21113
  if (meta?.personaMode) {
@@ -21701,7 +21834,8 @@ var SessionRunner = class {
21701
21834
  now: this.hooks.now ?? Date.now,
21702
21835
  resolveContextWindow: this.hooks.resolveContextWindow,
21703
21836
  genUuid: this.hooks.genUuid ?? v4_default,
21704
- ownerDisplayName: this.hooks.ownerDisplayName
21837
+ ownerDisplayName: this.hooks.ownerDisplayName,
21838
+ personaRoot: this.hooks.personaRoot
21705
21839
  };
21706
21840
  const { state, effects } = reduceSession(this.state, inputMsg, deps);
21707
21841
  this.state = state;
@@ -22125,9 +22259,20 @@ var SessionManager = class {
22125
22259
  attachObserver(observer) {
22126
22260
  this.attachedObserver = observer;
22127
22261
  }
22128
- // 按 scope 拿对应的 SessionStore。default scope 复用 deps.store(保证与
22129
- // bootstrap 注入的 store 一致);persona scope 第一次访问时按需创建并缓存。
22262
+ // 按 scope 拿对应的 SessionStore.
22263
+ // Phase 2 (capability platform plan §1) 路由切到 SessionStoreFactory:
22264
+ // default → factory.forBare() <dataDir>/sessions/
22265
+ // persona/<pid>/owner → factory.forVmOwner(pid) <dataDir>/personas/<pid>/.clawd/sessions/owner/
22266
+ // persona/<pid>/listener → throw (listener 角色 #698 已下线, schema 字段保留但运行时不该出现)
22267
+ // 缺省 (无 factory 注入): 回退 dataDir+scope 老路径 (向后兼容旧 spec).
22130
22268
  storeFor(scope) {
22269
+ if (this.deps.storeFactory) {
22270
+ if (scope.kind === "default") return this.deps.storeFactory.forBare();
22271
+ if (scope.mode === "owner") return this.deps.storeFactory.forVmOwner(scope.personaId);
22272
+ throw new Error(
22273
+ `SessionManager: listener scope is deprecated (#698); use forVmGuest in Phase 3+ instead`
22274
+ );
22275
+ }
22131
22276
  if (scope.kind === "default") return this.deps.store;
22132
22277
  const key = scopeKey(scope);
22133
22278
  const cached = this.storesByScope.get(key);
@@ -22155,11 +22300,13 @@ var SessionManager = class {
22155
22300
  scopeForFile(file) {
22156
22301
  return file.ownerPersonaId ? { kind: "persona", personaId: file.ownerPersonaId, mode: "owner" } : { kind: "default" };
22157
22302
  }
22158
- // <dataDir>/sessions/ 列出所有 persona 命名空间(不含 'default')。
22159
- // 用于 findOwnedSession / listAllOwned scope 查询。
22303
+ // 扫所有 persona 命名空间. 用于 findOwnedSession / listAllOwned scope 查询.
22304
+ // Phase 2 capability platform: 新布局下 persona 资产在 <dataDir>/personas/<pid>/,
22305
+ // 直接 readdir 这个目录拿所有 personaId. 老布局 (无 storeFactory) fallback 扫
22306
+ // <dataDir>/sessions/ 列子目录 (排除 'default').
22160
22307
  listPersonaIdsOnDisk() {
22161
22308
  if (!this.deps.dataDir) return [];
22162
- const root = import_node_path6.default.join(this.deps.dataDir, "sessions");
22309
+ const root = this.deps.storeFactory ? import_node_path6.default.join(this.deps.dataDir, "personas") : import_node_path6.default.join(this.deps.dataDir, "sessions");
22163
22310
  let entries;
22164
22311
  try {
22165
22312
  entries = import_node_fs5.default.readdirSync(root, { withFileTypes: true });
@@ -22234,6 +22381,9 @@ var SessionManager = class {
22234
22381
  dataDir: this.deps.dataDir,
22235
22382
  personaStore: this.deps.personaStore,
22236
22383
  ownerDisplayName: this.deps.ownerDisplayName,
22384
+ // Phase 2 capability platform (plan §3): 透传 personaRoot, buildSpawnArgs 据
22385
+ // cwd 是否在 personaRoot 下自动决定是否注入 --settings sandbox-settings.json.
22386
+ personaRoot: this.deps.personaRoot,
22237
22387
  // file-sharing (spec §6 PR 3):闭包 scope + sessionId,runner 只暴露 tool/relPath/cwd
22238
22388
  onFileEdit: attachmentGroup ? (input) => attachmentGroup.onFileEdit({
22239
22389
  scope,
@@ -23246,7 +23396,7 @@ var SessionManager = class {
23246
23396
 
23247
23397
  // src/persona/store.ts
23248
23398
  var fs6 = __toESM(require("fs"), 1);
23249
- var path7 = __toESM(require("path"), 1);
23399
+ var path8 = __toESM(require("path"), 1);
23250
23400
  init_protocol();
23251
23401
  var DEFAULT_SETTINGS = {
23252
23402
  permissions: {
@@ -23275,13 +23425,13 @@ var PersonaStore = class {
23275
23425
  }
23276
23426
  root;
23277
23427
  personaDir(personaId) {
23278
- return path7.join(this.root, safeFileName(personaId));
23428
+ return path8.join(this.root, safeFileName(personaId));
23279
23429
  }
23280
23430
  metaPath(personaId) {
23281
- return path7.join(this.personaDir(personaId), ".clawd", "persona.json");
23431
+ return path8.join(this.personaDir(personaId), ".clawd", "persona.json");
23282
23432
  }
23283
23433
  claudeMdPath(personaId) {
23284
- return path7.join(this.personaDir(personaId), "CLAUDE.md");
23434
+ return path8.join(this.personaDir(personaId), "CLAUDE.md");
23285
23435
  }
23286
23436
  /**
23287
23437
  * Sandbox settings 落盘路径 —— 故意放在 `.clawd/` 而不是 `.claude/`,让 CC 的
@@ -23291,11 +23441,11 @@ var PersonaStore = class {
23291
23441
  * 加载 persona 人格,只有 listener 多一层 OS sandbox。
23292
23442
  */
23293
23443
  sandboxSettingsPath(personaId) {
23294
- return path7.join(this.personaDir(personaId), ".clawd", "sandbox-settings.json");
23444
+ return path8.join(this.personaDir(personaId), ".clawd", "sandbox-settings.json");
23295
23445
  }
23296
23446
  write(persona, personality) {
23297
23447
  const dir = this.personaDir(persona.personaId);
23298
- fs6.mkdirSync(path7.join(dir, ".clawd"), { recursive: true });
23448
+ fs6.mkdirSync(path8.join(dir, ".clawd"), { recursive: true });
23299
23449
  this.atomicWrite(this.claudeMdPath(persona.personaId), personality);
23300
23450
  this.atomicWrite(
23301
23451
  this.sandboxSettingsPath(persona.personaId),
@@ -23338,12 +23488,12 @@ var PersonaStore = class {
23338
23488
  }
23339
23489
  /** Persona 私有 skills 目录路径:<personaDir>/.claude/skills */
23340
23490
  skillsDir(personaId) {
23341
- return path7.join(this.personaDir(personaId), ".claude", "skills");
23491
+ return path8.join(this.personaDir(personaId), ".claude", "skills");
23342
23492
  }
23343
23493
  list() {
23344
23494
  if (!fs6.existsSync(this.root)) return [];
23345
23495
  return fs6.readdirSync(this.root).filter((name) => {
23346
- return fs6.existsSync(path7.join(this.root, name, ".clawd", "persona.json"));
23496
+ return fs6.existsSync(path8.join(this.root, name, ".clawd", "persona.json"));
23347
23497
  });
23348
23498
  }
23349
23499
  remove(personaId) {
@@ -23742,7 +23892,17 @@ var PersonaManager = class {
23742
23892
  }
23743
23893
  /**
23744
23894
  * 删除 persona。
23745
- * PersonaStore.remove(personaId) 已级联删除整个 <personaRoot>/<personaId>/ 目录。
23895
+ * PersonaStore.remove(personaId) 已级联删除整个 <personaRoot>/<personaId>/ 目录,
23896
+ * Phase 2 capability platform 新布局下含 .clawd/sessions/owner/ +
23897
+ * .clawd/sessions/guests/<capId>/, rmSync recursive force 一锅清.
23898
+ *
23899
+ * ⚠️ TODO (Phase 3+ design gap, reviewer P1 flagged):
23900
+ * ~/.clawd/capabilities.json 里若有 cap.grants 含本 personaId, grant 会变成
23901
+ * 悬空引用 (guest 仍能持 token 接入, 但调依赖该 persona 的 RPC 会 fail).
23902
+ * 选项: (A) 这里扫所有 cap 过滤掉该 personaId 的 grant; (B) revoke 所有 grant
23903
+ * 含该 personaId 的 cap. 当前因 cleanupGuestSessionsForCapability 用 rmSync
23904
+ * force=true 吞 ENOENT 不会 crash, 但 UI CapabilityManagerDrawer 会展示
23905
+ * "无效的 persona" grant. Phase 3 加 cross-reference 矩阵后正式实现.
23746
23906
  */
23747
23907
  delete(personaId) {
23748
23908
  this.deps.store.remove(personaId);
@@ -23807,7 +23967,7 @@ var PersonaManager = class {
23807
23967
 
23808
23968
  // src/persona/seed.ts
23809
23969
  var fs8 = __toESM(require("fs"), 1);
23810
- var path9 = __toESM(require("path"), 1);
23970
+ var path10 = __toESM(require("path"), 1);
23811
23971
  var import_node_url = require("url");
23812
23972
  var import_meta = {};
23813
23973
  var DEFAULT_PERSONAS = [
@@ -23843,14 +24003,14 @@ var DEFAULT_PERSONAS = [
23843
24003
  function findDefaultsRoot() {
23844
24004
  const candidates = [];
23845
24005
  try {
23846
- const here = path9.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
23847
- candidates.push(path9.resolve(here, "defaults"));
23848
- candidates.push(path9.resolve(here, "persona-defaults"));
24006
+ const here = path10.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
24007
+ candidates.push(path10.resolve(here, "defaults"));
24008
+ candidates.push(path10.resolve(here, "persona-defaults"));
23849
24009
  } catch {
23850
24010
  }
23851
24011
  if (process.argv[1]) {
23852
- const argvDir = path9.dirname(process.argv[1]);
23853
- candidates.push(path9.resolve(argvDir, "persona-defaults"));
24012
+ const argvDir = path10.dirname(process.argv[1]);
24013
+ candidates.push(path10.resolve(argvDir, "persona-defaults"));
23854
24014
  }
23855
24015
  for (const c of candidates) {
23856
24016
  try {
@@ -23867,7 +24027,7 @@ function seedDefaultPersonas(args) {
23867
24027
  args.logger.info("persona.seed.skip", { personaId: entry.personaId, reason: "exists" });
23868
24028
  continue;
23869
24029
  }
23870
- const bundleDir = path9.join(args.defaultsRoot, entry.personaId);
24030
+ const bundleDir = path10.join(args.defaultsRoot, entry.personaId);
23871
24031
  if (!fs8.existsSync(bundleDir)) {
23872
24032
  args.logger.warn("persona.seed.skip", {
23873
24033
  personaId: entry.personaId,
@@ -23876,7 +24036,7 @@ function seedDefaultPersonas(args) {
23876
24036
  });
23877
24037
  continue;
23878
24038
  }
23879
- const claudeMdPath = path9.join(bundleDir, "CLAUDE.md");
24039
+ const claudeMdPath = path10.join(bundleDir, "CLAUDE.md");
23880
24040
  if (!fs8.existsSync(claudeMdPath)) {
23881
24041
  args.logger.warn("persona.seed.skip", {
23882
24042
  personaId: entry.personaId,
@@ -23905,8 +24065,8 @@ function seedDefaultPersonas(args) {
23905
24065
  function copyBundleExtras(srcDir, dstDir) {
23906
24066
  for (const entry of fs8.readdirSync(srcDir, { withFileTypes: true })) {
23907
24067
  if (entry.name === "CLAUDE.md" || entry.name === ".clawd") continue;
23908
- const srcPath = path9.join(srcDir, entry.name);
23909
- const dstPath = path9.join(dstDir, entry.name);
24068
+ const srcPath = path10.join(srcDir, entry.name);
24069
+ const dstPath = path10.join(dstDir, entry.name);
23910
24070
  if (entry.isDirectory()) {
23911
24071
  fs8.cpSync(srcPath, dstPath, { recursive: true, dereference: true });
23912
24072
  } else if (entry.isFile()) {
@@ -25792,7 +25952,7 @@ function authenticate(token, deps) {
25792
25952
 
25793
25953
  // src/permission/capability-store.ts
25794
25954
  var fs15 = __toESM(require("fs"), 1);
25795
- var path16 = __toESM(require("path"), 1);
25955
+ var path18 = __toESM(require("path"), 1);
25796
25956
  var CAPABILITIES_FILE_NAME = "capabilities.json";
25797
25957
  var FILE_VERSION = 1;
25798
25958
  var CapabilityStore = class {
@@ -25822,7 +25982,7 @@ var CapabilityStore = class {
25822
25982
  this.flush();
25823
25983
  }
25824
25984
  filePath() {
25825
- return path16.join(this.dataDir, CAPABILITIES_FILE_NAME);
25985
+ return path18.join(this.dataDir, CAPABILITIES_FILE_NAME);
25826
25986
  }
25827
25987
  readFromDisk() {
25828
25988
  const file = this.filePath();
@@ -25976,6 +26136,243 @@ function sha256Hex2(s) {
25976
26136
  return crypto5.createHash("sha256").update(s).digest("hex");
25977
26137
  }
25978
26138
 
26139
+ // src/permission/cleanup.ts
26140
+ var fs16 = __toESM(require("fs"), 1);
26141
+ function cleanupGuestSessionsForCapability(cap, factory) {
26142
+ const removed = [];
26143
+ for (const g2 of cap.grants) {
26144
+ if (g2.resource.type !== "persona") continue;
26145
+ const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
26146
+ try {
26147
+ fs16.rmSync(dir, { recursive: true, force: true });
26148
+ removed.push(dir);
26149
+ } catch {
26150
+ }
26151
+ }
26152
+ return { removed };
26153
+ }
26154
+
26155
+ // src/inbox/inbox-store.ts
26156
+ var fs17 = __toESM(require("fs"), 1);
26157
+ var path19 = __toESM(require("path"), 1);
26158
+ var INBOX_FILE_NAME = "inbox.jsonl";
26159
+ var InboxStore = class {
26160
+ constructor(dataDir) {
26161
+ this.dataDir = dataDir;
26162
+ fs17.mkdirSync(dataDir, { recursive: true });
26163
+ }
26164
+ dataDir;
26165
+ list() {
26166
+ const file = this.filePath();
26167
+ let raw;
26168
+ try {
26169
+ raw = fs17.readFileSync(file, "utf8");
26170
+ } catch (err) {
26171
+ if (err?.code === "ENOENT") return [];
26172
+ return [];
26173
+ }
26174
+ return parseAllLines(raw);
26175
+ }
26176
+ append(ev) {
26177
+ const file = this.filePath();
26178
+ const line = JSON.stringify(ev) + "\n";
26179
+ fs17.appendFileSync(file, line, { mode: 384 });
26180
+ try {
26181
+ fs17.chmodSync(file, 384);
26182
+ } catch {
26183
+ }
26184
+ }
26185
+ /**
26186
+ * 标记某条 event 为已读. 已有 readAt 时不覆盖 (idempotent: 第二次调用维持第一次时间).
26187
+ * 未知 id 静默 no-op (不抛, 不写文件).
26188
+ */
26189
+ markRead(id, at) {
26190
+ const events = this.list();
26191
+ let changed = false;
26192
+ const next = events.map((e) => {
26193
+ if (e.id !== id) return e;
26194
+ if (e.readAt !== void 0) return e;
26195
+ changed = true;
26196
+ return { ...e, readAt: at };
26197
+ });
26198
+ if (!changed) return;
26199
+ this.rewrite(next);
26200
+ }
26201
+ rewrite(events) {
26202
+ const file = this.filePath();
26203
+ const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
26204
+ const content = events.map((e) => JSON.stringify(e)).join("\n") + (events.length > 0 ? "\n" : "");
26205
+ fs17.writeFileSync(tmp, content, { mode: 384 });
26206
+ fs17.renameSync(tmp, file);
26207
+ try {
26208
+ fs17.chmodSync(file, 384);
26209
+ } catch {
26210
+ }
26211
+ }
26212
+ filePath() {
26213
+ return path19.join(this.dataDir, INBOX_FILE_NAME);
26214
+ }
26215
+ };
26216
+ function parseAllLines(raw) {
26217
+ const out = [];
26218
+ for (const line of raw.split("\n")) {
26219
+ const trimmed = line.trim();
26220
+ if (!trimmed) continue;
26221
+ let parsed;
26222
+ try {
26223
+ parsed = JSON.parse(trimmed);
26224
+ } catch {
26225
+ continue;
26226
+ }
26227
+ const r = InboxEventSchema.safeParse(parsed);
26228
+ if (r.success) out.push(r.data);
26229
+ }
26230
+ return out;
26231
+ }
26232
+
26233
+ // src/inbox/inbox-manager.ts
26234
+ var crypto6 = __toESM(require("crypto"), 1);
26235
+ var InboxManager = class {
26236
+ constructor(store, broadcast, opts = {}) {
26237
+ this.store = store;
26238
+ this.broadcast = broadcast;
26239
+ this.now = opts.now ?? Date.now;
26240
+ this.genId = opts.genId ?? defaultGenId;
26241
+ }
26242
+ store;
26243
+ broadcast;
26244
+ now;
26245
+ genId;
26246
+ /**
26247
+ * persona VM 内某 sender 给 CC 发消息 → 跨 principal 时记 inbox event.
26248
+ * 返回 null = owner 自己 (no-op); 否则返回写入的 event (含派生 id / createdAt).
26249
+ */
26250
+ recordPersonaMention(args) {
26251
+ if (args.sender.kind === "owner") return null;
26252
+ const resource = { type: "persona", id: args.personaId };
26253
+ const ev = {
26254
+ id: this.genId(),
26255
+ kind: "persona-mention",
26256
+ fromPrincipal: args.sender,
26257
+ toPrincipal: OWNER_PRINCIPAL,
26258
+ resource,
26259
+ preview: truncatePreview(args.preview),
26260
+ createdAt: this.now()
26261
+ };
26262
+ this.store.append(ev);
26263
+ this.broadcast({ type: "inbox:event", event: ev });
26264
+ return ev;
26265
+ }
26266
+ list(opts = {}) {
26267
+ const all = this.store.list();
26268
+ if (opts.includeRead) return all;
26269
+ return all.filter((e) => e.readAt === void 0);
26270
+ }
26271
+ markRead(eventId) {
26272
+ this.store.markRead(eventId, this.now());
26273
+ }
26274
+ };
26275
+ function truncatePreview(s) {
26276
+ if (s.length <= INBOX_PREVIEW_MAX_LENGTH) return s;
26277
+ return s.slice(0, INBOX_PREVIEW_MAX_LENGTH);
26278
+ }
26279
+ function defaultGenId() {
26280
+ return "inb_" + crypto6.randomBytes(6).toString("base64url");
26281
+ }
26282
+
26283
+ // src/migrations/2026-05-20-flatten-sessions.ts
26284
+ var fs18 = __toESM(require("fs"), 1);
26285
+ var path20 = __toESM(require("path"), 1);
26286
+ var MIGRATION_FLAG_NAME = ".migration.v1.done";
26287
+ function migrateFlattenSessions(opts) {
26288
+ const dataDir = opts.dataDir;
26289
+ const now = opts.now ?? Date.now;
26290
+ const sessionsDir = path20.join(dataDir, "sessions");
26291
+ const flagPath = path20.join(sessionsDir, MIGRATION_FLAG_NAME);
26292
+ if (existsSync3(flagPath)) {
26293
+ return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
26294
+ }
26295
+ let movedBare = 0;
26296
+ let movedVmOwner = 0;
26297
+ let archivedListener = 0;
26298
+ const defaultDir = path20.join(sessionsDir, "default");
26299
+ if (existsSync3(defaultDir)) {
26300
+ for (const entry of readdirSafe(defaultDir)) {
26301
+ if (!entry.endsWith(".json")) continue;
26302
+ const src = path20.join(defaultDir, entry);
26303
+ const dst = path20.join(sessionsDir, entry);
26304
+ fs18.renameSync(src, dst);
26305
+ movedBare += 1;
26306
+ }
26307
+ rmdirIfEmpty(defaultDir);
26308
+ }
26309
+ for (const pid of readdirSafe(sessionsDir)) {
26310
+ const personaDir = path20.join(sessionsDir, pid);
26311
+ if (!isDir(personaDir)) continue;
26312
+ if (pid === "default") continue;
26313
+ const ownerSrc = path20.join(personaDir, "owner");
26314
+ if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
26315
+ const ownerDst = path20.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
26316
+ fs18.mkdirSync(ownerDst, { recursive: true });
26317
+ for (const file of readdirSafe(ownerSrc)) {
26318
+ if (!file.endsWith(".json")) continue;
26319
+ fs18.renameSync(path20.join(ownerSrc, file), path20.join(ownerDst, file));
26320
+ movedVmOwner += 1;
26321
+ }
26322
+ rmdirIfEmpty(ownerSrc);
26323
+ }
26324
+ const listenerSrc = path20.join(personaDir, "listener");
26325
+ if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
26326
+ const archiveDst = path20.join(dataDir, ".legacy", `listener-${pid}`);
26327
+ fs18.mkdirSync(archiveDst, { recursive: true });
26328
+ for (const file of readdirSafe(listenerSrc)) {
26329
+ if (!file.endsWith(".json")) continue;
26330
+ fs18.renameSync(path20.join(listenerSrc, file), path20.join(archiveDst, file));
26331
+ archivedListener += 1;
26332
+ }
26333
+ rmdirIfEmpty(listenerSrc);
26334
+ }
26335
+ rmdirIfEmpty(personaDir);
26336
+ }
26337
+ fs18.mkdirSync(sessionsDir, { recursive: true });
26338
+ fs18.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
26339
+ return {
26340
+ skipped: false,
26341
+ flagWritten: true,
26342
+ movedBare,
26343
+ movedVmOwner,
26344
+ archivedListener
26345
+ };
26346
+ }
26347
+ function existsSync3(p2) {
26348
+ try {
26349
+ fs18.statSync(p2);
26350
+ return true;
26351
+ } catch {
26352
+ return false;
26353
+ }
26354
+ }
26355
+ function isDir(p2) {
26356
+ try {
26357
+ return fs18.statSync(p2).isDirectory();
26358
+ } catch {
26359
+ return false;
26360
+ }
26361
+ }
26362
+ function readdirSafe(p2) {
26363
+ try {
26364
+ return fs18.readdirSync(p2);
26365
+ } catch {
26366
+ return [];
26367
+ }
26368
+ }
26369
+ function rmdirIfEmpty(p2) {
26370
+ try {
26371
+ fs18.rmdirSync(p2);
26372
+ } catch {
26373
+ }
26374
+ }
26375
+
25979
26376
  // src/transport/http-router.ts
25980
26377
  var import_node_fs14 = __toESM(require("fs"), 1);
25981
26378
  var import_node_path16 = __toESM(require("path"), 1);
@@ -27822,6 +28219,31 @@ function buildCapabilityHandlers(deps) {
27822
28219
  };
27823
28220
  }
27824
28221
 
28222
+ // src/handlers/inbox.ts
28223
+ function buildInboxHandlers(deps) {
28224
+ const { manager } = deps;
28225
+ const list = async (frame) => {
28226
+ const { type: _t, requestId: _r, ...rest } = frame;
28227
+ const args = InboxListArgsSchema.parse(rest);
28228
+ const events = manager.list({ includeRead: args.includeRead ?? false });
28229
+ return {
28230
+ response: { type: "inbox:list", events }
28231
+ };
28232
+ };
28233
+ const markRead = async (frame) => {
28234
+ const { type: _t, requestId: _r, ...rest } = frame;
28235
+ const args = InboxMarkReadArgsSchema.parse(rest);
28236
+ manager.markRead(args.eventId);
28237
+ return {
28238
+ response: { type: "inbox:markRead:ok", eventId: args.eventId }
28239
+ };
28240
+ };
28241
+ return {
28242
+ "inbox:list": list,
28243
+ "inbox:markRead": markRead
28244
+ };
28245
+ }
28246
+
27825
28247
  // src/handlers/meta.ts
27826
28248
  var import_node_os13 = __toESM(require("os"), 1);
27827
28249
  init_protocol();
@@ -28074,6 +28496,7 @@ function buildMethodHandlers(deps) {
28074
28496
  personaRegistry: deps.personaRegistry
28075
28497
  }),
28076
28498
  ...buildCapabilityHandlers({ manager: deps.capabilityManager }),
28499
+ ...buildInboxHandlers({ manager: deps.inboxManager }),
28077
28500
  ...deps.attachment ? buildAttachmentHandlers(deps.attachment) : {}
28078
28501
  };
28079
28502
  }
@@ -28092,6 +28515,9 @@ var METHOD_GRANT_MAP = {
28092
28515
  "capability:issue": ADMIN_ANY,
28093
28516
  "capability:list": ADMIN_ANY,
28094
28517
  "capability:revoke": ADMIN_ANY,
28518
+ // ---- inbox 跨用户通知 (Phase 3 admin-only, owner 调; Phase 4 加 postMessage) ----
28519
+ "inbox:list": ADMIN_ANY,
28520
+ "inbox:markRead": ADMIN_ANY,
28095
28521
  // ---- 业务方法:Phase 1 全 admin-only(owner 自动通过;guest 无法调用) ----
28096
28522
  "session:create": ADMIN_ANY,
28097
28523
  "session:list": ADMIN_ANY,
@@ -28213,8 +28639,19 @@ async function startDaemon(config) {
28213
28639
  revokedAt: cap.revokedAt
28214
28640
  });
28215
28641
  wsServer?.closeConnectionsByCapability(cap.id);
28642
+ const cleanup = cleanupGuestSessionsForCapability(cap, sessionStoreFactory);
28643
+ if (cleanup.removed.length > 0) {
28644
+ logger.info("capability revoke cascade: guest sessions removed", {
28645
+ capabilityId: cap.id,
28646
+ removedDirs: cleanup.removed
28647
+ });
28648
+ }
28216
28649
  }
28217
28650
  });
28651
+ const inboxStore = new InboxStore(config.dataDir);
28652
+ const inboxManager = new InboxManager(inboxStore, (frame) => {
28653
+ wsServer?.broadcastToOwners(frame);
28654
+ });
28218
28655
  let wsServer = null;
28219
28656
  const authGate = authMode === "first-message" ? new AuthGate({
28220
28657
  shouldEnforce: buildShouldEnforce({ tunnel: config.tunnel }),
@@ -28231,7 +28668,16 @@ async function startDaemon(config) {
28231
28668
  sendOk: (h, payload) => wsServer?.sendToClient(h.id, payload)
28232
28669
  }) : null;
28233
28670
  resetRegistry();
28234
- const store = new SessionStore({ dataDir: config.dataDir });
28671
+ const migrateResult = migrateFlattenSessions({ dataDir: config.dataDir });
28672
+ if (!migrateResult.skipped && (migrateResult.movedBare || migrateResult.movedVmOwner || migrateResult.archivedListener)) {
28673
+ logger.info("sessions migration applied", {
28674
+ movedBare: migrateResult.movedBare,
28675
+ movedVmOwner: migrateResult.movedVmOwner,
28676
+ archivedListener: migrateResult.archivedListener
28677
+ });
28678
+ }
28679
+ const sessionStoreFactory = new SessionStoreFactory({ dataDir: config.dataDir });
28680
+ const store = sessionStoreFactory.forBare();
28235
28681
  const workspace = new WorkspaceBrowser();
28236
28682
  const skills = new SkillsScanner();
28237
28683
  const agents = new AgentsScanner();
@@ -28248,6 +28694,9 @@ async function startDaemon(config) {
28248
28694
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
28249
28695
  const manager = new SessionManager({
28250
28696
  store,
28697
+ // Phase 2 (capability platform plan §1): factory 注入后 manager.storeFor 走
28698
+ // 新布局派生 (sessions/* + personas/<pid>/.clawd/sessions/owner/*)
28699
+ storeFactory: sessionStoreFactory,
28251
28700
  logger,
28252
28701
  getAdapter,
28253
28702
  historyReader: history,
@@ -28392,7 +28841,9 @@ async function startDaemon(config) {
28392
28841
  }
28393
28842
  },
28394
28843
  // Task 1.9: capability:issue/list/revoke handler 依赖
28395
- capabilityManager
28844
+ capabilityManager,
28845
+ // Phase 3 Task 3.4: inbox:list/markRead handler 依赖
28846
+ inboxManager
28396
28847
  });
28397
28848
  const authResolver = new AuthContextResolver({
28398
28849
  ownerToken: resolvedAuthToken,