@clawos-dev/clawd 0.2.48-beta.76.f411c83 → 0.2.49

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 +389 -342
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -296,8 +296,8 @@ var require_req = __commonJS({
296
296
  if (req.originalUrl) {
297
297
  _req.url = req.originalUrl;
298
298
  } else {
299
- const path25 = req.path;
300
- _req.url = typeof path25 === "string" ? path25 : req.url ? req.url.path || req.url : void 0;
299
+ const path26 = req.path;
300
+ _req.url = typeof path26 === "string" ? path26 : req.url ? req.url.path || req.url : void 0;
301
301
  }
302
302
  if (req.query) {
303
303
  _req.query = req.query;
@@ -462,14 +462,14 @@ var require_redact = __commonJS({
462
462
  }
463
463
  return obj;
464
464
  }
465
- function parsePath(path25) {
465
+ function parsePath(path26) {
466
466
  const parts = [];
467
467
  let current = "";
468
468
  let inBrackets = false;
469
469
  let inQuotes = false;
470
470
  let quoteChar = "";
471
- for (let i = 0; i < path25.length; i++) {
472
- const char = path25[i];
471
+ for (let i = 0; i < path26.length; i++) {
472
+ const char = path26[i];
473
473
  if (!inBrackets && char === ".") {
474
474
  if (current) {
475
475
  parts.push(current);
@@ -600,10 +600,10 @@ var require_redact = __commonJS({
600
600
  return current;
601
601
  }
602
602
  function redactPaths(obj, paths, censor, remove = false) {
603
- for (const path25 of paths) {
604
- const parts = parsePath(path25);
603
+ for (const path26 of paths) {
604
+ const parts = parsePath(path26);
605
605
  if (parts.includes("*")) {
606
- redactWildcardPath(obj, parts, censor, path25, remove);
606
+ redactWildcardPath(obj, parts, censor, path26, remove);
607
607
  } else {
608
608
  if (remove) {
609
609
  removeKey(obj, parts);
@@ -688,8 +688,8 @@ var require_redact = __commonJS({
688
688
  }
689
689
  } else {
690
690
  if (afterWildcard.includes("*")) {
691
- const wrappedCensor = typeof censor === "function" ? (value, path25) => {
692
- const fullPath = [...pathArray.slice(0, pathLength), ...path25];
691
+ const wrappedCensor = typeof censor === "function" ? (value, path26) => {
692
+ const fullPath = [...pathArray.slice(0, pathLength), ...path26];
693
693
  return censor(value, fullPath);
694
694
  } : censor;
695
695
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -724,8 +724,8 @@ var require_redact = __commonJS({
724
724
  return null;
725
725
  }
726
726
  const pathStructure = /* @__PURE__ */ new Map();
727
- for (const path25 of pathsToClone) {
728
- const parts = parsePath(path25);
727
+ for (const path26 of pathsToClone) {
728
+ const parts = parsePath(path26);
729
729
  let current = pathStructure;
730
730
  for (let i = 0; i < parts.length; i++) {
731
731
  const part = parts[i];
@@ -777,24 +777,24 @@ var require_redact = __commonJS({
777
777
  }
778
778
  return cloneSelectively(obj, pathStructure);
779
779
  }
780
- function validatePath(path25) {
781
- if (typeof path25 !== "string") {
780
+ function validatePath(path26) {
781
+ if (typeof path26 !== "string") {
782
782
  throw new Error("Paths must be (non-empty) strings");
783
783
  }
784
- if (path25 === "") {
784
+ if (path26 === "") {
785
785
  throw new Error("Invalid redaction path ()");
786
786
  }
787
- if (path25.includes("..")) {
788
- throw new Error(`Invalid redaction path (${path25})`);
787
+ if (path26.includes("..")) {
788
+ throw new Error(`Invalid redaction path (${path26})`);
789
789
  }
790
- if (path25.includes(",")) {
791
- throw new Error(`Invalid redaction path (${path25})`);
790
+ if (path26.includes(",")) {
791
+ throw new Error(`Invalid redaction path (${path26})`);
792
792
  }
793
793
  let bracketCount = 0;
794
794
  let inQuotes = false;
795
795
  let quoteChar = "";
796
- for (let i = 0; i < path25.length; i++) {
797
- const char = path25[i];
796
+ for (let i = 0; i < path26.length; i++) {
797
+ const char = path26[i];
798
798
  if ((char === '"' || char === "'") && bracketCount > 0) {
799
799
  if (!inQuotes) {
800
800
  inQuotes = true;
@@ -808,20 +808,20 @@ var require_redact = __commonJS({
808
808
  } else if (char === "]" && !inQuotes) {
809
809
  bracketCount--;
810
810
  if (bracketCount < 0) {
811
- throw new Error(`Invalid redaction path (${path25})`);
811
+ throw new Error(`Invalid redaction path (${path26})`);
812
812
  }
813
813
  }
814
814
  }
815
815
  if (bracketCount !== 0) {
816
- throw new Error(`Invalid redaction path (${path25})`);
816
+ throw new Error(`Invalid redaction path (${path26})`);
817
817
  }
818
818
  }
819
819
  function validatePaths(paths) {
820
820
  if (!Array.isArray(paths)) {
821
821
  throw new TypeError("paths must be an array");
822
822
  }
823
- for (const path25 of paths) {
824
- validatePath(path25);
823
+ for (const path26 of paths) {
824
+ validatePath(path26);
825
825
  }
826
826
  }
827
827
  function slowRedact(options = {}) {
@@ -989,8 +989,8 @@ var require_redaction = __commonJS({
989
989
  if (shape[k] === null) {
990
990
  o[k] = (value) => topCensor(value, [k]);
991
991
  } else {
992
- const wrappedCensor = typeof censor === "function" ? (value, path25) => {
993
- return censor(value, [k, ...path25]);
992
+ const wrappedCensor = typeof censor === "function" ? (value, path26) => {
993
+ return censor(value, [k, ...path26]);
994
994
  } : censor;
995
995
  o[k] = Redact({
996
996
  paths: shape[k],
@@ -1211,7 +1211,7 @@ var require_sonic_boom = __commonJS({
1211
1211
  var fs24 = require("fs");
1212
1212
  var EventEmitter = require("events");
1213
1213
  var inherits = require("util").inherits;
1214
- var path25 = require("path");
1214
+ var path26 = require("path");
1215
1215
  var sleep = require_atomic_sleep();
1216
1216
  var assert = require("assert");
1217
1217
  var BUSY_WRITE_TIMEOUT = 100;
@@ -1265,7 +1265,7 @@ var require_sonic_boom = __commonJS({
1265
1265
  const mode = sonic.mode;
1266
1266
  if (sonic.sync) {
1267
1267
  try {
1268
- if (sonic.mkdir) fs24.mkdirSync(path25.dirname(file), { recursive: true });
1268
+ if (sonic.mkdir) fs24.mkdirSync(path26.dirname(file), { recursive: true });
1269
1269
  const fd = fs24.openSync(file, flags, mode);
1270
1270
  fileOpened(null, fd);
1271
1271
  } catch (err) {
@@ -1273,7 +1273,7 @@ var require_sonic_boom = __commonJS({
1273
1273
  throw err;
1274
1274
  }
1275
1275
  } else if (sonic.mkdir) {
1276
- fs24.mkdir(path25.dirname(file), { recursive: true }, (err) => {
1276
+ fs24.mkdir(path26.dirname(file), { recursive: true }, (err) => {
1277
1277
  if (err) return fileOpened(err);
1278
1278
  fs24.open(file, flags, mode, fileOpened);
1279
1279
  });
@@ -4895,8 +4895,8 @@ var init_parseUtil = __esm({
4895
4895
  init_errors2();
4896
4896
  init_en();
4897
4897
  makeIssue = (params) => {
4898
- const { data, path: path25, errorMaps, issueData } = params;
4899
- const fullPath = [...path25, ...issueData.path || []];
4898
+ const { data, path: path26, errorMaps, issueData } = params;
4899
+ const fullPath = [...path26, ...issueData.path || []];
4900
4900
  const fullIssue = {
4901
4901
  ...issueData,
4902
4902
  path: fullPath
@@ -5207,11 +5207,11 @@ var init_types = __esm({
5207
5207
  init_parseUtil();
5208
5208
  init_util();
5209
5209
  ParseInputLazyPath = class {
5210
- constructor(parent, value, path25, key) {
5210
+ constructor(parent, value, path26, key) {
5211
5211
  this._cachedPath = [];
5212
5212
  this.parent = parent;
5213
5213
  this.data = value;
5214
- this._path = path25;
5214
+ this._path = path26;
5215
5215
  this._key = key;
5216
5216
  }
5217
5217
  get path() {
@@ -9823,11 +9823,11 @@ var init_lib = __esm({
9823
9823
  }
9824
9824
  }
9825
9825
  },
9826
- addToPath: function addToPath(path25, added, removed, oldPosInc, options) {
9827
- var last = path25.lastComponent;
9826
+ addToPath: function addToPath(path26, added, removed, oldPosInc, options) {
9827
+ var last = path26.lastComponent;
9828
9828
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
9829
9829
  return {
9830
- oldPos: path25.oldPos + oldPosInc,
9830
+ oldPos: path26.oldPos + oldPosInc,
9831
9831
  lastComponent: {
9832
9832
  count: last.count + 1,
9833
9833
  added,
@@ -9837,7 +9837,7 @@ var init_lib = __esm({
9837
9837
  };
9838
9838
  } else {
9839
9839
  return {
9840
- oldPos: path25.oldPos + oldPosInc,
9840
+ oldPos: path26.oldPos + oldPosInc,
9841
9841
  lastComponent: {
9842
9842
  count: 1,
9843
9843
  added,
@@ -10268,10 +10268,10 @@ function attachmentToHistoryMessage(o, ts) {
10268
10268
  const memories = raw.map((m) => {
10269
10269
  if (!m || typeof m !== "object") return null;
10270
10270
  const rec = m;
10271
- const path25 = typeof rec.path === "string" ? rec.path : null;
10271
+ const path26 = typeof rec.path === "string" ? rec.path : null;
10272
10272
  const content = typeof rec.content === "string" ? rec.content : null;
10273
- if (!path25 || content == null) return null;
10274
- const entry = { path: path25, content };
10273
+ if (!path26 || content == null) return null;
10274
+ const entry = { path: path26, content };
10275
10275
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
10276
10276
  return entry;
10277
10277
  }).filter((m) => m !== null);
@@ -10308,7 +10308,7 @@ function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
10308
10308
  if (backupFileName === null) return null;
10309
10309
  try {
10310
10310
  return import_node_fs6.default.readFileSync(
10311
- import_node_path6.default.join(fileHistoryRoot, toolSessionId, backupFileName),
10311
+ import_node_path7.default.join(fileHistoryRoot, toolSessionId, backupFileName),
10312
10312
  "utf8"
10313
10313
  );
10314
10314
  } catch {
@@ -10323,13 +10323,13 @@ function readCurrentContent(filePath) {
10323
10323
  return null;
10324
10324
  }
10325
10325
  }
10326
- var import_node_fs6, import_node_os2, import_node_path6, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
10326
+ var import_node_fs6, import_node_os2, import_node_path7, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
10327
10327
  var init_claude_history = __esm({
10328
10328
  "src/tools/claude-history.ts"() {
10329
10329
  "use strict";
10330
10330
  import_node_fs6 = __toESM(require("fs"), 1);
10331
10331
  import_node_os2 = __toESM(require("os"), 1);
10332
- import_node_path6 = __toESM(require("path"), 1);
10332
+ import_node_path7 = __toESM(require("path"), 1);
10333
10333
  init_lib();
10334
10334
  init_tool_result_extra();
10335
10335
  TASK_NOTIFICATION_RE = /<task-notification\b[\s\S]*?<\/task-notification>/i;
@@ -10353,9 +10353,9 @@ var init_claude_history = __esm({
10353
10353
  // 每次 user 提交前 trackEdit 拷一份,作为 rewind 回退目标
10354
10354
  fileHistoryRoot;
10355
10355
  constructor(opts = {}) {
10356
- const base = opts.baseDir ?? import_node_path6.default.join(import_node_os2.default.homedir(), ".claude");
10357
- this.projectsRoot = import_node_path6.default.join(base, "projects");
10358
- this.fileHistoryRoot = import_node_path6.default.join(base, "file-history");
10356
+ const base = opts.baseDir ?? import_node_path7.default.join(import_node_os2.default.homedir(), ".claude");
10357
+ this.projectsRoot = import_node_path7.default.join(base, "projects");
10358
+ this.fileHistoryRoot = import_node_path7.default.join(base, "file-history");
10359
10359
  }
10360
10360
  async listProjects() {
10361
10361
  let entries;
@@ -10368,9 +10368,9 @@ var init_claude_history = __esm({
10368
10368
  const out = [];
10369
10369
  for (const ent of entries) {
10370
10370
  if (!ent.isDirectory()) continue;
10371
- const dir = import_node_path6.default.join(this.projectsRoot, ent.name);
10371
+ const dir = import_node_path7.default.join(this.projectsRoot, ent.name);
10372
10372
  const files = import_node_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
10373
- const updatedAtMs = files.reduce((m, f) => Math.max(m, safeStatMtime(import_node_path6.default.join(dir, f))), 0);
10373
+ const updatedAtMs = files.reduce((m, f) => Math.max(m, safeStatMtime(import_node_path7.default.join(dir, f))), 0);
10374
10374
  out.push({
10375
10375
  projectPath: hashDirToCwd(ent.name),
10376
10376
  hashDir: ent.name,
@@ -10382,7 +10382,7 @@ var init_claude_history = __esm({
10382
10382
  return out;
10383
10383
  }
10384
10384
  async listSessions(args) {
10385
- const dir = import_node_path6.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
10385
+ const dir = import_node_path7.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
10386
10386
  let files;
10387
10387
  try {
10388
10388
  files = import_node_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
@@ -10392,7 +10392,7 @@ var init_claude_history = __esm({
10392
10392
  }
10393
10393
  const out = [];
10394
10394
  for (const f of files) {
10395
- const full = import_node_path6.default.join(dir, f);
10395
+ const full = import_node_path7.default.join(dir, f);
10396
10396
  const toolSessionId = f.slice(0, -".jsonl".length);
10397
10397
  const lines = readJsonlLines(full);
10398
10398
  let summary = "";
@@ -10447,7 +10447,7 @@ var init_claude_history = __esm({
10447
10447
  return out;
10448
10448
  }
10449
10449
  async read(args) {
10450
- const file = import_node_path6.default.join(
10450
+ const file = import_node_path7.default.join(
10451
10451
  this.projectsRoot,
10452
10452
  cwdToHashDir(args.cwd),
10453
10453
  `${args.toolSessionId}.jsonl`
@@ -10480,7 +10480,7 @@ var init_claude_history = __esm({
10480
10480
  // 独立目录路径:<projectsRoot>/<cwdHash>/<toolSessionId>/subagents/*.jsonl
10481
10481
  // 返回 null 表示目录不存在(调用方回退旧实现);返回空数组表示目录存在但无 jsonl
10482
10482
  listSubagentsFromDirectory(cwd, toolSessionId) {
10483
- const dir = import_node_path6.default.join(
10483
+ const dir = import_node_path7.default.join(
10484
10484
  this.projectsRoot,
10485
10485
  cwdToHashDir(cwd),
10486
10486
  toolSessionId,
@@ -10498,7 +10498,7 @@ var init_claude_history = __esm({
10498
10498
  if (!e.isFile()) continue;
10499
10499
  if (!e.name.startsWith("agent-") || !e.name.endsWith(".jsonl")) continue;
10500
10500
  const subagentId = e.name.slice("agent-".length, -".jsonl".length);
10501
- const filePath = import_node_path6.default.join(dir, e.name);
10501
+ const filePath = import_node_path7.default.join(dir, e.name);
10502
10502
  const lines = readJsonlLines(filePath);
10503
10503
  let firstText = "";
10504
10504
  let messageCount = 0;
@@ -10515,7 +10515,7 @@ var init_claude_history = __esm({
10515
10515
  return out;
10516
10516
  }
10517
10517
  listSubagentsFromMainJsonl(cwd, toolSessionId) {
10518
- const file = import_node_path6.default.join(
10518
+ const file = import_node_path7.default.join(
10519
10519
  this.projectsRoot,
10520
10520
  cwdToHashDir(cwd),
10521
10521
  `${toolSessionId}.jsonl`
@@ -10550,7 +10550,7 @@ var init_claude_history = __esm({
10550
10550
  }
10551
10551
  // 独立文件路径:agent-<subagentId>.jsonl;文件不存在返回 null 让调用方回退旧实现
10552
10552
  readSubagentFromFile(cwd, toolSessionId, subagentId) {
10553
- const file = import_node_path6.default.join(
10553
+ const file = import_node_path7.default.join(
10554
10554
  this.projectsRoot,
10555
10555
  cwdToHashDir(cwd),
10556
10556
  toolSessionId,
@@ -10578,7 +10578,7 @@ var init_claude_history = __esm({
10578
10578
  * "那一刻每个 tracked 文件对应的 backup 文件名"
10579
10579
  */
10580
10580
  readFileHistorySnapshots(args) {
10581
- const file = import_node_path6.default.join(
10581
+ const file = import_node_path7.default.join(
10582
10582
  this.projectsRoot,
10583
10583
  cwdToHashDir(args.cwd),
10584
10584
  `${args.toolSessionId}.jsonl`
@@ -10623,7 +10623,7 @@ var init_claude_history = __esm({
10623
10623
  for (const [anchorId, target] of snapshots) {
10624
10624
  let hasAny = false;
10625
10625
  for (const [rawPath, backup] of Object.entries(target)) {
10626
- const absPath = import_node_path6.default.isAbsolute(rawPath) ? rawPath : import_node_path6.default.join(args.cwd, rawPath);
10626
+ const absPath = import_node_path7.default.isAbsolute(rawPath) ? rawPath : import_node_path7.default.join(args.cwd, rawPath);
10627
10627
  const backupContent = readBackupContent(
10628
10628
  this.fileHistoryRoot,
10629
10629
  args.toolSessionId,
@@ -10663,7 +10663,7 @@ var init_claude_history = __esm({
10663
10663
  let totalInsertions = 0;
10664
10664
  let totalDeletions = 0;
10665
10665
  for (const [rawPath, backup] of Object.entries(target)) {
10666
- const absPath = import_node_path6.default.isAbsolute(rawPath) ? rawPath : import_node_path6.default.join(args.cwd, rawPath);
10666
+ const absPath = import_node_path7.default.isAbsolute(rawPath) ? rawPath : import_node_path7.default.join(args.cwd, rawPath);
10667
10667
  const backupContent = readBackupContent(
10668
10668
  this.fileHistoryRoot,
10669
10669
  args.toolSessionId,
@@ -10710,7 +10710,7 @@ var init_claude_history = __esm({
10710
10710
  };
10711
10711
  }
10712
10712
  readSubagentFromMainJsonl(cwd, toolSessionId, subagentId) {
10713
- const file = import_node_path6.default.join(
10713
+ const file = import_node_path7.default.join(
10714
10714
  this.projectsRoot,
10715
10715
  cwdToHashDir(cwd),
10716
10716
  `${toolSessionId}.jsonl`
@@ -10734,7 +10734,7 @@ var init_claude_history = __esm({
10734
10734
  // src/tools/claude.ts
10735
10735
  function macOSDesktopCandidates(home) {
10736
10736
  return [
10737
- import_node_path7.default.join(home, "Applications", "Claude.app", "Contents", "Resources", "app.asar.unpacked", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
10737
+ import_node_path8.default.join(home, "Applications", "Claude.app", "Contents", "Resources", "app.asar.unpacked", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
10738
10738
  "/Applications/Claude.app/Contents/Resources/app.asar.unpacked/node_modules/@anthropic-ai/claude-code/cli.js"
10739
10739
  ];
10740
10740
  }
@@ -11075,10 +11075,10 @@ function parseAttachment(obj) {
11075
11075
  const memories = raw.map((m) => {
11076
11076
  if (!m || typeof m !== "object") return null;
11077
11077
  const rec = m;
11078
- const path25 = typeof rec.path === "string" ? rec.path : null;
11078
+ const path26 = typeof rec.path === "string" ? rec.path : null;
11079
11079
  const content = typeof rec.content === "string" ? rec.content : null;
11080
- if (!path25 || content == null) return null;
11081
- const out = { path: path25, content };
11080
+ if (!path26 || content == null) return null;
11081
+ const out = { path: path26, content };
11082
11082
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11083
11083
  return out;
11084
11084
  }).filter((m) => m !== null);
@@ -11182,7 +11182,7 @@ function encodeClaudeStdin(text) {
11182
11182
  };
11183
11183
  return JSON.stringify(frame) + "\n";
11184
11184
  }
11185
- var import_node_child_process, import_node_child_process2, import_node_fs7, import_node_os3, import_node_path7, ATTACHMENT_SILENT_SUBTYPES2, unknownTypeHandler, ATTACHMENT_RE, IMAGE_EXT_MIME, CLAUDE_MODELS, CLAUDE_PERMISSION_MODES, CLAUDE_CAPABILITIES, ClaudeAdapter;
11185
+ var import_node_child_process, import_node_child_process2, import_node_fs7, import_node_os3, import_node_path8, ATTACHMENT_SILENT_SUBTYPES2, unknownTypeHandler, ATTACHMENT_RE, IMAGE_EXT_MIME, CLAUDE_MODELS, CLAUDE_PERMISSION_MODES, CLAUDE_CAPABILITIES, ClaudeAdapter;
11186
11186
  var init_claude = __esm({
11187
11187
  "src/tools/claude.ts"() {
11188
11188
  "use strict";
@@ -11190,7 +11190,7 @@ var init_claude = __esm({
11190
11190
  import_node_child_process2 = require("child_process");
11191
11191
  import_node_fs7 = __toESM(require("fs"), 1);
11192
11192
  import_node_os3 = __toESM(require("os"), 1);
11193
- import_node_path7 = __toESM(require("path"), 1);
11193
+ import_node_path8 = __toESM(require("path"), 1);
11194
11194
  init_protocol();
11195
11195
  init_claude_history();
11196
11196
  init_tool_result_extra();
@@ -14945,7 +14945,7 @@ var require_websocket_server = __commonJS({
14945
14945
  // src/run-case/recorder.ts
14946
14946
  function startRunCaseRecorder(opts) {
14947
14947
  const now = opts.now ?? Date.now;
14948
- const dir = import_node_path21.default.dirname(opts.recordPath);
14948
+ const dir = import_node_path22.default.dirname(opts.recordPath);
14949
14949
  let stream = null;
14950
14950
  let closing = false;
14951
14951
  let closedSettled = false;
@@ -14985,12 +14985,12 @@ function startRunCaseRecorder(opts) {
14985
14985
  };
14986
14986
  return { tap, close, closed };
14987
14987
  }
14988
- var import_node_fs21, import_node_path21;
14988
+ var import_node_fs21, import_node_path22;
14989
14989
  var init_recorder = __esm({
14990
14990
  "src/run-case/recorder.ts"() {
14991
14991
  "use strict";
14992
14992
  import_node_fs21 = __toESM(require("fs"), 1);
14993
- import_node_path21 = __toESM(require("path"), 1);
14993
+ import_node_path22 = __toESM(require("path"), 1);
14994
14994
  }
14995
14995
  });
14996
14996
 
@@ -15033,7 +15033,7 @@ var init_wire = __esm({
15033
15033
  // src/run-case/controller.ts
15034
15034
  async function runController(opts) {
15035
15035
  const now = opts.now ?? Date.now;
15036
- const cwd = opts.cwd ?? (0, import_node_fs22.mkdtempSync)(import_node_path22.default.join(import_node_os14.default.tmpdir(), "clawd-runcase-"));
15036
+ const cwd = opts.cwd ?? (0, import_node_fs22.mkdtempSync)(import_node_path23.default.join(import_node_os14.default.tmpdir(), "clawd-runcase-"));
15037
15037
  const ownsCwd = opts.cwd === void 0;
15038
15038
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
15039
15039
  const spawnCtx = { cwd };
@@ -15200,13 +15200,13 @@ async function runController(opts) {
15200
15200
  }
15201
15201
  return exitCode ?? 0;
15202
15202
  }
15203
- var import_node_fs22, import_node_os14, import_node_path22;
15203
+ var import_node_fs22, import_node_os14, import_node_path23;
15204
15204
  var init_controller = __esm({
15205
15205
  "src/run-case/controller.ts"() {
15206
15206
  "use strict";
15207
15207
  import_node_fs22 = require("fs");
15208
15208
  import_node_os14 = __toESM(require("os"), 1);
15209
- import_node_path22 = __toESM(require("path"), 1);
15209
+ import_node_path23 = __toESM(require("path"), 1);
15210
15210
  init_claude();
15211
15211
  init_stdout_splitter();
15212
15212
  init_permission_stdio();
@@ -15431,7 +15431,7 @@ Env (advanced):
15431
15431
  `;
15432
15432
 
15433
15433
  // src/index.ts
15434
- var import_node_path20 = __toESM(require("path"), 1);
15434
+ var import_node_path21 = __toESM(require("path"), 1);
15435
15435
  var import_node_fs20 = __toESM(require("fs"), 1);
15436
15436
 
15437
15437
  // src/logger.ts
@@ -15471,9 +15471,30 @@ function createLogger(opts = {}) {
15471
15471
 
15472
15472
  // src/session/store.ts
15473
15473
  var import_node_fs3 = __toESM(require("fs"), 1);
15474
- var import_node_path3 = __toESM(require("path"), 1);
15474
+ var import_node_path4 = __toESM(require("path"), 1);
15475
15475
  init_protocol();
15476
- var DEFAULT_AGENT_ID = "default";
15476
+
15477
+ // src/session/scope.ts
15478
+ var import_node_path3 = __toESM(require("path"), 1);
15479
+ function scopeKey(scope) {
15480
+ return scope.kind === "default" ? "default" : `persona:${scope.personaId}:${scope.mode}`;
15481
+ }
15482
+ function scopeSubPath(scope) {
15483
+ return scope.kind === "default" ? ["default"] : [scope.personaId, scope.mode];
15484
+ }
15485
+ function metaFromScope(scope, personaRoot) {
15486
+ if (scope.kind === "default") return void 0;
15487
+ if (scope.mode === "owner") {
15488
+ return { idleKillEnabled: false, personaMode: "owner" };
15489
+ }
15490
+ return {
15491
+ idleKillEnabled: true,
15492
+ personaMode: "listener",
15493
+ extraSettings: import_node_path3.default.join(personaRoot, scope.personaId, ".clawd", "sandbox-settings.json")
15494
+ };
15495
+ }
15496
+
15497
+ // src/session/store.ts
15477
15498
  function safeFileName(sessionId) {
15478
15499
  const base = sessionId.replace(/[^a-zA-Z0-9_\-.]/g, "_");
15479
15500
  const cleaned = base.replace(/^\.+/, (dots) => "_".repeat(dots.length));
@@ -15485,20 +15506,15 @@ function safeFileName(sessionId) {
15485
15506
  var SessionStore = class {
15486
15507
  root;
15487
15508
  constructor(opts) {
15488
- const agentId = opts.agentId ?? DEFAULT_AGENT_ID;
15489
- if (agentId !== DEFAULT_AGENT_ID) {
15490
- if (!opts.personaRoot) {
15491
- throw new Error(
15492
- `SessionStore: personaRoot is required for non-default agentId='${agentId}'. Bootstrap must wire personaRoot when constructing SessionStore for persona agents.`
15493
- );
15494
- }
15495
- this.root = import_node_path3.default.join(opts.personaRoot, safeFileName(agentId), ".clawd", "sub-sessions");
15496
- } else {
15497
- this.root = import_node_path3.default.join(opts.dataDir, "sessions", agentId);
15498
- }
15509
+ const scope = opts.scope ?? { kind: "default" };
15510
+ this.root = import_node_path4.default.join(
15511
+ opts.dataDir,
15512
+ "sessions",
15513
+ ...scopeSubPath(scope).map(safeFileName)
15514
+ );
15499
15515
  }
15500
15516
  filePath(sessionId) {
15501
- return import_node_path3.default.join(this.root, `${safeFileName(sessionId)}.json`);
15517
+ return import_node_path4.default.join(this.root, `${safeFileName(sessionId)}.json`);
15502
15518
  }
15503
15519
  ensureDir() {
15504
15520
  import_node_fs3.default.mkdirSync(this.root, { recursive: true });
@@ -15546,7 +15562,7 @@ var SessionStore = class {
15546
15562
  for (const name of entries) {
15547
15563
  if (!name.endsWith(".json")) continue;
15548
15564
  if (name.includes(".tmp-")) continue;
15549
- const full = import_node_path3.default.join(this.root, name);
15565
+ const full = import_node_path4.default.join(this.root, name);
15550
15566
  try {
15551
15567
  const raw = import_node_fs3.default.readFileSync(full, "utf8");
15552
15568
  const parsed = JSON.parse(raw);
@@ -15562,7 +15578,7 @@ var SessionStore = class {
15562
15578
 
15563
15579
  // src/session/manager.ts
15564
15580
  var import_node_fs5 = __toESM(require("fs"), 1);
15565
- var import_node_path5 = __toESM(require("path"), 1);
15581
+ var import_node_path6 = __toESM(require("path"), 1);
15566
15582
 
15567
15583
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
15568
15584
  var byteToHex = [];
@@ -16290,7 +16306,7 @@ init_stdout_splitter();
16290
16306
 
16291
16307
  // src/ipc-recorder.ts
16292
16308
  var import_node_fs4 = __toESM(require("fs"), 1);
16293
- var import_node_path4 = __toESM(require("path"), 1);
16309
+ var import_node_path5 = __toESM(require("path"), 1);
16294
16310
  function tsForFilename(ms) {
16295
16311
  return new Date(ms).toISOString().replace(/[:.]/g, "-");
16296
16312
  }
@@ -16301,8 +16317,8 @@ function startRecorder(opts) {
16301
16317
  return null;
16302
16318
  }
16303
16319
  const now = opts.now ?? Date.now;
16304
- const dir = import_node_path4.default.join(opts.dataDir, "ipc-recordings", opts.sessionId);
16305
- const filePath = import_node_path4.default.join(dir, `${tsForFilename(now())}.jsonl`);
16320
+ const dir = import_node_path5.default.join(opts.dataDir, "ipc-recordings", opts.sessionId);
16321
+ const filePath = import_node_path5.default.join(dir, `${tsForFilename(now())}.jsonl`);
16306
16322
  let stream = null;
16307
16323
  let closedResolve;
16308
16324
  const closed = new Promise((resolve2) => {
@@ -16744,7 +16760,6 @@ function makeInitialState(file, subSessionMeta) {
16744
16760
  var SessionManager = class {
16745
16761
  constructor(deps) {
16746
16762
  this.deps = deps;
16747
- this.storesByAgent.set(DEFAULT_AGENT_ID, deps.store);
16748
16763
  }
16749
16764
  deps;
16750
16765
  // sessionId → SessionRunner;在 send / ensureSession 时按需创建
@@ -16760,33 +16775,28 @@ var SessionManager = class {
16760
16775
  // 由 observer 监听 jsonl user 行后调 recordRealUserUuid 建立映射;rewind 系列 RPC 在
16761
16776
  // 入参 / 出参做转译,保证 UI 看到的 uuid 始终是 events 流里的 synth uuid
16762
16777
  realUuidBySynth = /* @__PURE__ */ new Map();
16763
- // persona sub-session 路径:按 agentId 派生 SessionStore(root = dataDir/sessions/<agentId>/)。
16764
- // 'default' 直接复用 deps.store;其它 agentId 第一次访问时按需创建并缓存
16765
- storesByAgent = /* @__PURE__ */ new Map();
16766
- // sub-session 创建时记录的 subSessionMeta;ensureSession / runner 创建时塞入 reducer state。
16767
- // 不进 SessionFile schema(避免破坏现有 zod parse),仅运行时缓存
16768
- subSessionMetaBySid = /* @__PURE__ */ new Map();
16778
+ // SessionStore scope 派生(root = <dataDir>/sessions/<scopeSubPath>/)。
16779
+ // default scope 直接复用 deps.store;persona scope(owner / listener)第一次访问时按需创建并缓存。
16780
+ // 取代旧的 storesByAgent —— agentId 概念由 SessionScope 取代,路径即身份,
16781
+ // SubSessionMeta scope 派生(metaFromScope),不再需要运行时 cache ownerPersonaId 兜底。
16782
+ storesByScope = /* @__PURE__ */ new Map();
16769
16783
  // persona-bound transport 订阅器:sessionId → Set<listener>。
16770
16784
  // 透传 owner 路径白名单 EventFrame(routeFromRunner 决定哪些 type 进入),listener 端按
16771
16785
  // `frame.type` narrow 出 'session:event'(ParsedEvent)/ 'session:status'(procAlive 同步)等。
16772
16786
  // 这样 owner / listener 共用同一组事件字面量,新增跨端信号时只需在 fan-out 白名单里加 type。
16773
16787
  eventSubscribers = /* @__PURE__ */ new Map();
16774
- // 按 agentId 拿对应的 SessionStore;persona-* agentId 使用 personaRoot 路径。
16775
- // 'default' 直接复用 deps.store;其它 agentId 第一次访问时按需创建并缓存。
16776
- // personaRoot 必须与 PersonaStore 同源,两者共享相同的 persona 目录层级
16777
- storeFor(agentId) {
16778
- const cached = this.storesByAgent.get(agentId);
16788
+ // 按 scope 拿对应的 SessionStore。default scope 复用 deps.store(保证与
16789
+ // bootstrap 注入的 store 一致);persona scope 第一次访问时按需创建并缓存。
16790
+ storeFor(scope) {
16791
+ if (scope.kind === "default") return this.deps.store;
16792
+ const key = scopeKey(scope);
16793
+ const cached = this.storesByScope.get(key);
16779
16794
  if (cached) return cached;
16780
16795
  if (!this.deps.dataDir) {
16781
- throw new Error(`SessionManager: dataDir required to route agentId='${agentId}'`);
16782
- }
16783
- if (!this.deps.personaRoot) {
16784
- throw new Error(
16785
- `SessionManager: personaRoot is required to route agentId='${agentId}'. Bootstrap must wire personaRoot when constructing SessionManager for persona sub-sessions.`
16786
- );
16796
+ throw new Error(`SessionManager: dataDir required to construct SessionStore for scope=${key}`);
16787
16797
  }
16788
- const st = new SessionStore({ dataDir: this.deps.dataDir, agentId, personaRoot: this.deps.personaRoot });
16789
- this.storesByAgent.set(agentId, st);
16798
+ const st = new SessionStore({ dataDir: this.deps.dataDir, scope });
16799
+ this.storesByScope.set(key, st);
16790
16800
  return st;
16791
16801
  }
16792
16802
  async getCapabilities(tool) {
@@ -16797,26 +16807,80 @@ var SessionManager = class {
16797
16807
  this.capabilitiesCache.set(tool, caps);
16798
16808
  return caps;
16799
16809
  }
16800
- // 按优先级解析 SubSessionMeta:subSessionMetaBySid 缓存(内存,首次 create/registerSubSession 写入)
16801
- // SessionFile.ownerPersonaId 派生(持久化兜底,daemon 重启后 subSessionMetaBySid 丢失时生效)
16802
- // undefined(普通 session)
16803
- resolveSubSessionMeta(file) {
16804
- const cached = this.subSessionMetaBySid.get(file.sessionId);
16805
- if (cached) return cached;
16806
- if (file.ownerPersonaId) {
16807
- return { idleKillEnabled: false, personaMode: "owner" };
16810
+ // SessionFile.ownerPersonaId schema 字段推回写入 scope。
16811
+ // ownerPersonaId 由 create({ownerPersonaId}) schema 落盘时记入,永远只反映
16812
+ // session 出身(不可变);此函数只用于 SessionStore 的写入路由,不参与运行时行为
16813
+ // 决策(personaMode='owner' 等由路径 → scope → metaFromScope 派生)。
16814
+ // 仅覆盖 owner + default 两类——listener sub-session 不会从 default RPC 路径进来。
16815
+ scopeForFile(file) {
16816
+ return file.ownerPersonaId ? { kind: "persona", personaId: file.ownerPersonaId, mode: "owner" } : { kind: "default" };
16817
+ }
16818
+ // 扫 <dataDir>/sessions/ 列出所有 persona 命名空间(不含 'default')。
16819
+ // 用于 findOwnedSession / listAllOwned 跨 scope 查询。
16820
+ listPersonaIdsOnDisk() {
16821
+ if (!this.deps.dataDir) return [];
16822
+ const root = import_node_path6.default.join(this.deps.dataDir, "sessions");
16823
+ let entries;
16824
+ try {
16825
+ entries = import_node_fs5.default.readdirSync(root, { withFileTypes: true });
16826
+ } catch (err) {
16827
+ const code = err?.code;
16828
+ if (code === "ENOENT") return [];
16829
+ throw err;
16830
+ }
16831
+ return entries.filter((e) => e.isDirectory() && e.name !== "default").map((e) => e.name);
16832
+ }
16833
+ // owner / default 两个分类下按 sessionId 找文件——前端只传 sessionId 不带 scope,
16834
+ // SessionManager 在这里扫盘定位(default 直接试,未命中再轮询所有 persona owner 目录)。
16835
+ // listener sub-session 不走这条——transport 始终明确传 (personaId, sessionId)。
16836
+ findOwnedSession(sessionId) {
16837
+ const dflt = this.deps.store.read(sessionId);
16838
+ if (dflt) return dflt;
16839
+ for (const personaId of this.listPersonaIdsOnDisk()) {
16840
+ const ownerStore = this.storeFor({ kind: "persona", personaId, mode: "owner" });
16841
+ const file = ownerStore.read(sessionId);
16842
+ if (file) return file;
16808
16843
  }
16809
- return void 0;
16844
+ return null;
16845
+ }
16846
+ // 合并 default + 所有 persona owner 的 SessionFile —— 桌面 App 主 session 列表入口。
16847
+ // 同样不含 listener sub-session(listener 走 persona:listSubSessions RPC 单独入口)。
16848
+ listAllOwned() {
16849
+ const out = [];
16850
+ out.push(...this.deps.store.list());
16851
+ for (const personaId of this.listPersonaIdsOnDisk()) {
16852
+ const ownerStore = this.storeFor({ kind: "persona", personaId, mode: "owner" });
16853
+ out.push(...ownerStore.list());
16854
+ }
16855
+ return out.sort(
16856
+ (a, b) => a.updatedAt > b.updatedAt ? -1 : a.updatedAt < b.updatedAt ? 1 : 0
16857
+ );
16858
+ }
16859
+ // 写回 SessionFile,自动根据 ownerPersonaId 派生写入 scope。
16860
+ // 调用方原先都是 `deps.store.write(updated)`,全部走这里收口路由。
16861
+ writeOwned(file) {
16862
+ return this.storeFor(this.scopeForFile(file)).write(file);
16863
+ }
16864
+ // 删除 owned SessionFile(default 或 persona/owner),与 findOwnedSession 对称扫盘。
16865
+ deleteOwned(sessionId) {
16866
+ if (this.deps.store.delete(sessionId)) return true;
16867
+ for (const personaId of this.listPersonaIdsOnDisk()) {
16868
+ const ownerStore = this.storeFor({ kind: "persona", personaId, mode: "owner" });
16869
+ if (ownerStore.delete(sessionId)) return true;
16870
+ }
16871
+ return false;
16810
16872
  }
16811
16873
  // 创建 runner 时包一层 broadcast hook:所有外出 frame 统一走 routeFromRunner,
16812
- // 经过 compressFrameForWire 后决定是 push collector 还是走 deps.broadcastFrame
16813
- // store:默认 deps.store;persona sub-session 路径下传该 personaId 对应的 SessionStore
16814
- // subSessionMeta:listener { idleKillEnabled: true, personaMode: 'listener' };
16815
- // owner { idleKillEnabled: false, personaMode: 'owner' };普通 session 不传
16816
- newRunner(file, opts = {}) {
16874
+ // 经过 compressFrameForWire 后决定是 push collector 还是走 deps.broadcastFrame
16875
+ // scope 决定两件事:SessionStore 路由 + SubSessionMeta 派生(metaFromScope)。
16876
+ // - default 复用 deps.store,无 subSessionMeta(普通 session)
16877
+ // - persona/owner <dataDir>/sessions/<personaId>/owner/,meta={idleKillEnabled:false, personaMode:'owner'}
16878
+ // - persona/listener → <dataDir>/sessions/<personaId>/listener/,meta={idleKillEnabled:true, personaMode:'listener', extraSettings:<sandbox>}
16879
+ newRunner(file, scope) {
16817
16880
  const adapter = this.deps.getAdapter(file.tool ?? "claude");
16818
- const store = opts.store ?? this.deps.store;
16819
- const runner = new SessionRunner(makeInitialState(file, opts.subSessionMeta), {
16881
+ const store = this.storeFor(scope);
16882
+ const subSessionMeta = metaFromScope(scope, this.deps.personaRoot ?? "");
16883
+ const runner = new SessionRunner(makeInitialState(file, subSessionMeta), {
16820
16884
  broadcastFrame: (frame, target) => this.routeFromRunner(frame, target),
16821
16885
  store,
16822
16886
  adapter,
@@ -16877,7 +16941,7 @@ var SessionManager = class {
16877
16941
  if (!this.deps.personaRoot) {
16878
16942
  throw new Error("personaRoot required to derive cwd from ownerPersonaId");
16879
16943
  }
16880
- cwd = import_node_path5.default.join(this.deps.personaRoot, safeFileName(args.ownerPersonaId));
16944
+ cwd = import_node_path6.default.join(this.deps.personaRoot, safeFileName(args.ownerPersonaId));
16881
16945
  }
16882
16946
  if (!cwd) {
16883
16947
  throw new ClawdError(ERROR_CODES.INVALID_CWD, "cwd required when ownerPersonaId is absent");
@@ -16907,13 +16971,8 @@ var SessionManager = class {
16907
16971
  createdAt: iso,
16908
16972
  updatedAt: iso
16909
16973
  };
16910
- const written = this.deps.store.write(file);
16911
- if (args.ownerPersonaId) {
16912
- this.subSessionMetaBySid.set(written.sessionId, {
16913
- idleKillEnabled: false,
16914
- personaMode: "owner"
16915
- });
16916
- }
16974
+ const scope = args.ownerPersonaId ? { kind: "persona", personaId: args.ownerPersonaId, mode: "owner" } : { kind: "default" };
16975
+ const written = this.storeFor(scope).write(file);
16917
16976
  return { response: written, broadcast: [] };
16918
16977
  }
16919
16978
  pin(args) {
@@ -16928,13 +16987,13 @@ var SessionManager = class {
16928
16987
  return { response: value, broadcast };
16929
16988
  }
16930
16989
  const updated = { ...existing, pinnedAt };
16931
- this.deps.store.write(updated);
16990
+ this.writeOwned(updated);
16932
16991
  return { response: updated, broadcast: [] };
16933
16992
  }
16934
16993
  // sidebar 拖拽完成 → 整体重写 pinned root 的 pinSortOrder(按 orderedIds 下标 0,1,2...)
16935
16994
  // orderedIds 必须正好覆盖所有当前 pinned root(既不能漏,也不能含 unpinned id)
16936
16995
  reorderPins(args) {
16937
- const all = this.deps.store.list();
16996
+ const all = this.listAllOwned();
16938
16997
  const currentPinnedIds = new Set(
16939
16998
  all.filter((f) => typeof f.pinnedAt === "number").map((f) => f.sessionId)
16940
16999
  );
@@ -16964,14 +17023,14 @@ var SessionManager = class {
16964
17023
  } else {
16965
17024
  const existing = this.getFile(sessionId);
16966
17025
  const updated = { ...existing, pinSortOrder: index };
16967
- this.deps.store.write(updated);
17026
+ this.writeOwned(updated);
16968
17027
  updatedFiles.push(updated);
16969
17028
  }
16970
17029
  });
16971
17030
  return { response: { sessions: updatedFiles }, broadcast };
16972
17031
  }
16973
17032
  list() {
16974
- return { response: { sessions: this.deps.store.list() }, broadcast: [] };
17033
+ return { response: { sessions: this.listAllOwned() }, broadcast: [] };
16975
17034
  }
16976
17035
  get(args) {
16977
17036
  const file = this.getFile(args.sessionId);
@@ -17024,10 +17083,11 @@ var SessionManager = class {
17024
17083
  for (const [synth, real] of map) reverse.set(real, synth);
17025
17084
  return realUuids.map((r) => reverse.get(r) ?? r);
17026
17085
  }
17027
- // 内部帮手:不走 ManagerResult 的 get,直接拿 SessionFile
17086
+ // 内部帮手:不走 ManagerResult 的 get,直接拿 SessionFile
17087
+ // 优先 runner cache(hot path);冷态走 findOwnedSession 跨 default + 所有 persona owner 扫盘。
17028
17088
  getFile(sessionId) {
17029
17089
  const runner = this.runners.get(sessionId);
17030
- const existing = runner?.getState().file ?? this.deps.store.read(sessionId);
17090
+ const existing = runner?.getState().file ?? this.findOwnedSession(sessionId);
17031
17091
  if (!existing) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, sessionId);
17032
17092
  return existing;
17033
17093
  }
@@ -17042,11 +17102,11 @@ var SessionManager = class {
17042
17102
  return { response: value, broadcast };
17043
17103
  }
17044
17104
  const updated = { ...existing, ...args.patch, updatedAt: nowIso2(this.deps) };
17045
- this.deps.store.write(updated);
17105
+ this.writeOwned(updated);
17046
17106
  return { response: updated, broadcast: [] };
17047
17107
  }
17048
17108
  delete(args) {
17049
- const existing = this.deps.store.read(args.sessionId);
17109
+ const existing = this.findOwnedSession(args.sessionId);
17050
17110
  if (!existing) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, args.sessionId);
17051
17111
  const runner = this.runners.get(args.sessionId);
17052
17112
  if (runner) {
@@ -17057,7 +17117,7 @@ var SessionManager = class {
17057
17117
  this.realUuidBySynth.delete(args.sessionId);
17058
17118
  return { response: { sessionId: args.sessionId }, broadcast };
17059
17119
  }
17060
- this.deps.store.delete(args.sessionId);
17120
+ this.deleteOwned(args.sessionId);
17061
17121
  return {
17062
17122
  response: { sessionId: args.sessionId },
17063
17123
  broadcast: [
@@ -17069,8 +17129,7 @@ var SessionManager = class {
17069
17129
  const existing = this.getFile(args.sessionId);
17070
17130
  let runner = this.runners.get(args.sessionId);
17071
17131
  if (!runner) {
17072
- const subSessionMeta = this.resolveSubSessionMeta(existing);
17073
- runner = this.newRunner(existing, { subSessionMeta });
17132
+ runner = this.newRunner(existing, this.scopeForFile(existing));
17074
17133
  this.runners.set(args.sessionId, runner);
17075
17134
  }
17076
17135
  const { broadcast } = this.withCollector(() => {
@@ -17164,8 +17223,7 @@ var SessionManager = class {
17164
17223
  const file = this.getFile(args.sessionId);
17165
17224
  let runner = this.runners.get(args.sessionId);
17166
17225
  if (!runner) {
17167
- const subSessionMeta = this.resolveSubSessionMeta(file);
17168
- runner = this.newRunner(file, { subSessionMeta });
17226
+ runner = this.newRunner(file, this.scopeForFile(file));
17169
17227
  this.runners.set(args.sessionId, runner);
17170
17228
  }
17171
17229
  if (!runner.getState().procAlive) {
@@ -17216,7 +17274,7 @@ var SessionManager = class {
17216
17274
  });
17217
17275
  return { response: value, broadcast };
17218
17276
  }
17219
- const existing = this.deps.store.read(args.sessionId);
17277
+ const existing = this.findOwnedSession(args.sessionId);
17220
17278
  const {
17221
17279
  toolSessionId: _drop,
17222
17280
  contextUsage: _ctx,
@@ -17231,7 +17289,7 @@ var SessionManager = class {
17231
17289
  void _branch;
17232
17290
  void _model;
17233
17291
  const updated = { ...rest, updatedAt: nowIso2(this.deps) };
17234
- return { response: this.deps.store.write(updated), broadcast: [] };
17292
+ return { response: this.writeOwned(updated), broadcast: [] };
17235
17293
  }
17236
17294
  resume(args) {
17237
17295
  this.getFile(args.sessionId);
@@ -17246,13 +17304,13 @@ var SessionManager = class {
17246
17304
  });
17247
17305
  return { response: value, broadcast };
17248
17306
  }
17249
- const existing = this.deps.store.read(args.sessionId);
17307
+ const existing = this.findOwnedSession(args.sessionId);
17250
17308
  const updated = {
17251
17309
  ...existing,
17252
17310
  toolSessionId: args.toolSessionId,
17253
17311
  updatedAt: nowIso2(this.deps)
17254
17312
  };
17255
- return { response: this.deps.store.write(updated), broadcast: [] };
17313
+ return { response: this.writeOwned(updated), broadcast: [] };
17256
17314
  }
17257
17315
  getEvents(args) {
17258
17316
  const runner = this.runners.get(args.sessionId);
@@ -17320,39 +17378,44 @@ var SessionManager = class {
17320
17378
  if (!runner) return "idle";
17321
17379
  return compressStatus(runner.getState().status);
17322
17380
  }
17323
- // 给 observer 用:保证有 runner 但不 spawn(仅创建 state 容器)
17381
+ // 给 observer 用:保证有 runner 但不 spawn(仅创建 state 容器)。
17382
+ // observer 喂事件路径只走 default + owner(普通 + owner persona session)
17383
+ // ——listener sub-session 的 observer 路径走 ensureRunnerForScope。
17324
17384
  ensureSession(file) {
17325
17385
  let r = this.runners.get(file.sessionId);
17326
17386
  if (r) return r;
17327
- const subSessionMeta = this.resolveSubSessionMeta(file);
17328
- r = this.newRunner(file, { subSessionMeta });
17387
+ r = this.newRunner(file, this.scopeForFile(file));
17329
17388
  this.runners.set(file.sessionId, r);
17330
17389
  return r;
17331
17390
  }
17332
- // ---------------- persona / sub-session 专用 API ----------------
17391
+ // ---------------- persona / listener sub-session 专用 API ----------------
17333
17392
  //
17334
- // PersonaManager 是 SessionManager 之上的薄编排层,sub-session 的持久化(SessionFile)
17335
- // 仍由 SessionManager 持有。区别于普通 session:
17336
- // - SessionFile 落到 <personaRoot>/<personaId>/.clawd/sub-sessions/ 而非 sessions/default/
17393
+ // PersonaManager 是 SessionManager 之上的薄编排层,listener sub-session 的持久化
17394
+ // (SessionFile)仍由 SessionManager 持有。区别于普通 / owner session:
17395
+ // - SessionFile 落到 <dataDir>/sessions/<personaId>/listener/(拓扑分类)
17337
17396
  // - reducer state.subSessionMeta.idleKillEnabled = true(turn_end 自动 idle-kill)
17338
17397
  // - sessionId 由 PersonaManager 派生(personaId-<tokenHash>),不走自动 newSessionId
17339
- /** 按 agentId 读取 SessionFile;不存在返回 null */
17340
- readForAgent(sessionId, agentId) {
17341
- return this.storeFor(agentId).read(sessionId);
17398
+ //
17399
+ // 这些 API 接 SessionScope 参数(不再是单参 agentId)—— transport 始终明确传
17400
+ // {kind:'persona', personaId, mode:'listener'},与 owner / default 分类隔离。
17401
+ /** 按 scope 读取 SessionFile;不存在返回 null */
17402
+ readForScope(sessionId, scope) {
17403
+ return this.storeFor(scope).read(sessionId);
17342
17404
  }
17343
17405
  /**
17344
- * agentId 列出该命名空间下所有 SessionFile(persona:listSubSessions 入口)。
17345
- * agentId 还未访问过 → storeFor 第一次创建对应 SessionStore(root 不存在则 list 返回 [])
17406
+ * 列出指定 persona 在指定 mode 下的所有 SessionFile
17407
+ * persona:listSubSessions RPC 入口(mode='listener');
17408
+ * 路径下还未写过任何 session 时 list() 返回空数组(SessionStore.list 内部 ENOENT 处理)
17346
17409
  */
17347
- listForAgent(agentId) {
17348
- return this.storeFor(agentId).list();
17410
+ listPersonaSessions(personaId, mode) {
17411
+ return this.storeFor({ kind: "persona", personaId, mode }).list();
17349
17412
  }
17350
17413
  /**
17351
17414
  * 创建 sub-session 的 SessionFile + 准备 runner(不 spawn)。
17352
- * subSessionMeta 不进 SessionFile schema,仅缓存进 runner state。
17353
- * 同一 sessionId 重复调用:抛错(PersonaManager 应先调 readForAgent 命中复用)
17415
+ * SubSessionMeta 不进 SessionFile schema,运行时由 scope → metaFromScope 派生。
17416
+ * 同一 sessionId 重复调用:抛错(PersonaManager 应先调 readForScope 命中复用)
17354
17417
  */
17355
- createForAgent(args) {
17418
+ createForScope(args) {
17356
17419
  try {
17357
17420
  const stat = import_node_fs5.default.statSync(args.cwd);
17358
17421
  if (!stat.isDirectory()) throw new Error("not dir");
@@ -17361,9 +17424,11 @@ var SessionManager = class {
17361
17424
  }
17362
17425
  const tool = args.tool ?? "claude";
17363
17426
  this.deps.getAdapter(tool);
17364
- const store = this.storeFor(args.agentId);
17427
+ const store = this.storeFor(args.scope);
17365
17428
  if (store.read(args.sessionId)) {
17366
- throw new Error(`session already exists for agent ${args.agentId}: ${args.sessionId}`);
17429
+ throw new Error(
17430
+ `session already exists for scope ${scopeKey(args.scope)}: ${args.sessionId}`
17431
+ );
17367
17432
  }
17368
17433
  const iso = nowIso2(this.deps);
17369
17434
  const file = {
@@ -17376,11 +17441,7 @@ var SessionManager = class {
17376
17441
  createdAt: iso,
17377
17442
  updatedAt: iso
17378
17443
  };
17379
- const written = store.write(file);
17380
- if (args.subSessionMeta) {
17381
- this.subSessionMetaBySid.set(args.sessionId, args.subSessionMeta);
17382
- }
17383
- return written;
17444
+ return store.write(file);
17384
17445
  }
17385
17446
  /**
17386
17447
  * persona-bound transport 用:读取 sub-session 的历史 ParsedEvent[]。
@@ -17391,22 +17452,21 @@ var SessionManager = class {
17391
17452
  * 不读 jsonl:
17392
17453
  * 1. observer 路径在 spawn 后会自动接管 jsonl 回灌,进 reducer buffer。
17393
17454
  * 2. 第一次握手时 sub-session 还没 spawn → toolSessionId 为空 → jsonl 不存在。
17394
- * sessionFile 不存在抛 SESSION_NOT_FOUND;上层应先调 createForAgent
17455
+ * sessionFile 不存在抛 SESSION_NOT_FOUND;上层应先调 createForScope
17395
17456
  */
17396
- readHistoryEvents(sessionId, agentId) {
17397
- const store = this.storeFor(agentId);
17398
- const file = store.read(sessionId);
17457
+ readHistoryEventsForScope(sessionId, scope) {
17458
+ const file = this.storeFor(scope).read(sessionId);
17399
17459
  if (!file) {
17400
17460
  throw new ClawdError(
17401
17461
  ERROR_CODES.SESSION_NOT_FOUND,
17402
- `sub-session not found: ${agentId}/${sessionId}`
17462
+ `sub-session not found: ${scopeKey(scope)}/${sessionId}`
17403
17463
  );
17404
17464
  }
17405
- const runner = this.ensureRunnerFor(file, agentId);
17465
+ const runner = this.ensureRunnerForScope(file, scope);
17406
17466
  return runner.getState().buffer.map((e) => e.event);
17407
17467
  }
17408
17468
  /**
17409
- * persona-bound transport 用:基于 readHistoryEvents 的 buffer 切片视图,支持分页。
17469
+ * persona-bound transport 用:基于 readHistoryEventsForScope 的 buffer 切片视图,支持分页。
17410
17470
  *
17411
17471
  * Why a new method instead of reusing `history:read` RPC handler:
17412
17472
  * - `history:read` 走 HandlerDeps.history.read(),读 CC 写盘的 jsonl 文件
@@ -17414,18 +17474,15 @@ var SessionManager = class {
17414
17474
  * 两条路径数据形态、时序、字段不同,不能共享 helper。persona-bound 客户端必须
17415
17475
  * 走 buffer 路径,才能保证首屏回放和实时帧无 gap、无重复。
17416
17476
  *
17417
- * Why not 改 readHistoryEvents 加 limit/offset:现有 readHistoryEvents 的语义是
17418
- * "返回 buffer 全部",被其它路径(rewind uuid 转译等)依赖;保留它不动,新加分页方法。
17419
- *
17420
17477
  * 切片语义:clip 安全边界,response.offset 保留请求值便于客户端识别越界请求。
17421
17478
  * - offset >= total → events: [], offset: 请求值, total: 当前 buffer 长度
17422
17479
  * - offset + limit > total → 返回 tail [offset, total),长度 = total - offset
17423
17480
  * - 正常 → 返回 [offset, offset + limit)
17424
17481
  *
17425
- * SESSION_NOT_FOUND 行为继承 readHistoryEvents(store.read 返回 null 即抛)。
17482
+ * SESSION_NOT_FOUND 行为继承 readHistoryEventsForScope(store.read 返回 null 即抛)。
17426
17483
  */
17427
- readHistoryPage(sessionId, agentId, limit, offset) {
17428
- const all = this.readHistoryEvents(sessionId, agentId);
17484
+ readHistoryPageForScope(sessionId, scope, limit, offset) {
17485
+ const all = this.readHistoryEventsForScope(sessionId, scope);
17429
17486
  const total = all.length;
17430
17487
  const start = Math.min(offset, total);
17431
17488
  const end = Math.min(start + limit, total);
@@ -17438,36 +17495,34 @@ var SessionManager = class {
17438
17495
  * 返回 unsubscribe;不破坏现有 wire 广播路径——routeFromRunner 同时 fan-out 到
17439
17496
  * eventSubscribers 和 deps.broadcastFrame
17440
17497
  */
17441
- subscribe(_sessionId, _agentId, listener) {
17442
- const sid = _sessionId;
17443
- let subs = this.eventSubscribers.get(sid);
17498
+ subscribe(sessionId, _scope, listener) {
17499
+ let subs = this.eventSubscribers.get(sessionId);
17444
17500
  if (!subs) {
17445
17501
  subs = /* @__PURE__ */ new Set();
17446
- this.eventSubscribers.set(sid, subs);
17502
+ this.eventSubscribers.set(sessionId, subs);
17447
17503
  }
17448
17504
  subs.add(listener);
17449
17505
  return () => {
17450
- const cur = this.eventSubscribers.get(sid);
17506
+ const cur = this.eventSubscribers.get(sessionId);
17451
17507
  if (!cur) return;
17452
17508
  cur.delete(listener);
17453
- if (cur.size === 0) this.eventSubscribers.delete(sid);
17509
+ if (cur.size === 0) this.eventSubscribers.delete(sessionId);
17454
17510
  };
17455
17511
  }
17456
17512
  /**
17457
- * persona-bound transport 用:sub-session 路径的 send(按 agentId 路由 SessionStore)。
17458
- * 现状 send(args.sessionId, args.text) 默认 'default' agent;这里按 agentId 拿对应 store
17459
- * + ensureRunnerFor 保证 runner 用同一个 store 写盘
17513
+ * persona-bound transport 用:sub-session 路径的 send(按 scope 路由 SessionStore)。
17514
+ * 区别于 default scope 走 SessionManager.send:transport hello 已知 personaId mode,
17515
+ * 显式传 scope 避免跨命名空间 sessionId 撞车。
17460
17516
  */
17461
- sendForAgent(args) {
17462
- const store = this.storeFor(args.agentId);
17463
- const file = store.read(args.sessionId);
17517
+ sendForScope(args) {
17518
+ const file = this.storeFor(args.scope).read(args.sessionId);
17464
17519
  if (!file) {
17465
17520
  throw new ClawdError(
17466
17521
  ERROR_CODES.SESSION_NOT_FOUND,
17467
- `sub-session not found: ${args.agentId}/${args.sessionId}`
17522
+ `sub-session not found: ${scopeKey(args.scope)}/${args.sessionId}`
17468
17523
  );
17469
17524
  }
17470
- const runner = this.ensureRunnerFor(file, args.agentId);
17525
+ const runner = this.ensureRunnerForScope(file, args.scope);
17471
17526
  const { broadcast } = this.withCollector(() => {
17472
17527
  runner.input({ kind: "command", command: { kind: "send", text: args.text } });
17473
17528
  });
@@ -17477,53 +17532,47 @@ var SessionManager = class {
17477
17532
  * persona-bound transport 用:sub-session reset。
17478
17533
  * 复用 reducer 'new' 命令:清 toolSessionId / buffer / nextSeq / pending* + kill proc + emit
17479
17534
  * session:cleared。语义上等价 alice 端"清空当前会话上下文"。
17480
- * 归档已写盘的 jsonl 不在本路径处理(CC 后续 spawn 会写新的 toolSessionId.jsonl,旧的留盘);
17481
- * 物理归档可在后续单独 task 加(spec § 6 待 plan 决定项)。
17535
+ * 归档已写盘的 jsonl 不在本路径处理(CC 后续 spawn 会写新的 toolSessionId.jsonl,旧的留盘)。
17482
17536
  */
17483
- resetForAgent(args) {
17484
- const store = this.storeFor(args.agentId);
17485
- const file = store.read(args.sessionId);
17537
+ resetForScope(args) {
17538
+ const file = this.storeFor(args.scope).read(args.sessionId);
17486
17539
  if (!file) {
17487
17540
  throw new ClawdError(
17488
17541
  ERROR_CODES.SESSION_NOT_FOUND,
17489
- `sub-session not found: ${args.agentId}/${args.sessionId}`
17542
+ `sub-session not found: ${scopeKey(args.scope)}/${args.sessionId}`
17490
17543
  );
17491
17544
  }
17492
- const runner = this.ensureRunnerFor(file, args.agentId);
17545
+ const runner = this.ensureRunnerForScope(file, args.scope);
17493
17546
  const { broadcast } = this.withCollector(() => {
17494
17547
  runner.input({ kind: "command", command: { kind: "new" } });
17495
17548
  });
17496
17549
  return { response: { ok: true }, broadcast };
17497
17550
  }
17498
- /** ensureSession 的 agentId-aware 版本:复用现有 runner 或按 agentId 派生 store 创建 */
17499
- ensureRunnerFor(file, agentId) {
17551
+ /** ensureSession 的 scope-aware 版本:复用现有 runner 或按 scope 创建(含 metaFromScope 派生 meta) */
17552
+ ensureRunnerForScope(file, scope) {
17500
17553
  const existing = this.runners.get(file.sessionId);
17501
17554
  if (existing) return existing;
17502
- const store = this.storeFor(agentId);
17503
- const subSessionMeta = this.resolveSubSessionMeta(file);
17504
- const runner = this.newRunner(file, { store, subSessionMeta });
17555
+ const runner = this.newRunner(file, scope);
17505
17556
  this.runners.set(file.sessionId, runner);
17506
17557
  return runner;
17507
17558
  }
17508
17559
  /**
17509
17560
  * 老板插话:把 'inject-owner-text' input 喂给对应 runner,
17510
- * runner 不存在时按 ensureSession 路径懒创建(以 subSessionMeta 缓存为准)。
17561
+ * runner 不存在时按 scope 懒创建。
17511
17562
  * frames 走异步路径(broadcastFrame):调用方一般在 PersonaManager.appendOwnerMessage
17512
17563
  * 内部走 RPC 处理流,没有同步 collector 上下文
17513
17564
  */
17514
17565
  injectOwnerMessage(args) {
17515
- const store = this.storeFor(args.agentId);
17516
- const file = store.read(args.sessionId);
17566
+ const file = this.storeFor(args.scope).read(args.sessionId);
17517
17567
  if (!file) {
17518
17568
  throw new ClawdError(
17519
17569
  ERROR_CODES.SESSION_NOT_FOUND,
17520
- `sub-session not found: ${args.agentId}/${args.sessionId}`
17570
+ `sub-session not found: ${scopeKey(args.scope)}/${args.sessionId}`
17521
17571
  );
17522
17572
  }
17523
17573
  let runner = this.runners.get(args.sessionId);
17524
17574
  if (!runner) {
17525
- const subSessionMeta = this.resolveSubSessionMeta(file);
17526
- runner = this.newRunner(file, { store, subSessionMeta });
17575
+ runner = this.newRunner(file, args.scope);
17527
17576
  this.runners.set(args.sessionId, runner);
17528
17577
  }
17529
17578
  runner.input({ kind: "inject-owner-text", text: args.text });
@@ -17619,7 +17668,7 @@ var SessionManager = class {
17619
17668
 
17620
17669
  // src/persona/store.ts
17621
17670
  var fs6 = __toESM(require("fs"), 1);
17622
- var path6 = __toESM(require("path"), 1);
17671
+ var path7 = __toESM(require("path"), 1);
17623
17672
  init_protocol();
17624
17673
  var DEFAULT_SETTINGS = {
17625
17674
  permissions: {
@@ -17648,13 +17697,13 @@ var PersonaStore = class {
17648
17697
  }
17649
17698
  root;
17650
17699
  personaDir(personaId) {
17651
- return path6.join(this.root, safeFileName(personaId));
17700
+ return path7.join(this.root, safeFileName(personaId));
17652
17701
  }
17653
17702
  metaPath(personaId) {
17654
- return path6.join(this.personaDir(personaId), ".clawd", "persona.json");
17703
+ return path7.join(this.personaDir(personaId), ".clawd", "persona.json");
17655
17704
  }
17656
17705
  claudeMdPath(personaId) {
17657
- return path6.join(this.personaDir(personaId), "CLAUDE.md");
17706
+ return path7.join(this.personaDir(personaId), "CLAUDE.md");
17658
17707
  }
17659
17708
  /**
17660
17709
  * Sandbox settings 落盘路径 —— 故意放在 `.clawd/` 而不是 `.claude/`,让 CC 的
@@ -17664,11 +17713,11 @@ var PersonaStore = class {
17664
17713
  * 加载 persona 人格,只有 listener 多一层 OS sandbox。
17665
17714
  */
17666
17715
  sandboxSettingsPath(personaId) {
17667
- return path6.join(this.personaDir(personaId), ".clawd", "sandbox-settings.json");
17716
+ return path7.join(this.personaDir(personaId), ".clawd", "sandbox-settings.json");
17668
17717
  }
17669
17718
  write(persona, personality) {
17670
17719
  const dir = this.personaDir(persona.personaId);
17671
- fs6.mkdirSync(path6.join(dir, ".clawd"), { recursive: true });
17720
+ fs6.mkdirSync(path7.join(dir, ".clawd"), { recursive: true });
17672
17721
  this.atomicWrite(this.claudeMdPath(persona.personaId), personality);
17673
17722
  this.atomicWrite(
17674
17723
  this.sandboxSettingsPath(persona.personaId),
@@ -17699,7 +17748,7 @@ var PersonaStore = class {
17699
17748
  list() {
17700
17749
  if (!fs6.existsSync(this.root)) return [];
17701
17750
  return fs6.readdirSync(this.root).filter((name) => {
17702
- return fs6.existsSync(path6.join(this.root, name, ".clawd", "persona.json"));
17751
+ return fs6.existsSync(path7.join(this.root, name, ".clawd", "persona.json"));
17703
17752
  });
17704
17753
  }
17705
17754
  remove(personaId) {
@@ -17861,27 +17910,20 @@ var PersonaManager = class {
17861
17910
  const persona = this.deps.registry.get(personaId);
17862
17911
  if (!persona) throw new Error(`persona not found: ${personaId}`);
17863
17912
  const subSessionId = this.deriveSubSessionId(personaId, token);
17864
- const existing = this.deps.sessionManager.readForAgent(subSessionId, personaId);
17913
+ const scope = { kind: "persona", personaId, mode: "listener" };
17914
+ const existing = this.deps.sessionManager.readForScope(subSessionId, scope);
17865
17915
  if (existing) {
17866
17916
  return { sessionFile: existing, isNew: false };
17867
17917
  }
17868
17918
  const tokenEntry = persona.tokenMap[token];
17869
17919
  const subLabel = tokenEntry?.label ?? "unknown";
17870
- const sessionFile = this.deps.sessionManager.createForAgent({
17920
+ const sessionFile = this.deps.sessionManager.createForScope({
17871
17921
  sessionId: subSessionId,
17872
- agentId: personaId,
17922
+ scope,
17873
17923
  cwd: this.deps.store.personaDirPath(personaId),
17874
17924
  tool: "claude",
17875
17925
  label: subLabel,
17876
- model: persona.model,
17877
- // listener 走 OS sandbox:sandbox JSON 不在 project 自动发现路径上(PersonaStore 把它
17878
- // 落在 .clawd/sandbox-settings.json),靠 extraSettings 透传到 spawn 的 `--settings <file>`
17879
- // 显式拉回。owner 路径不传 extraSettings,天然无 sandbox。
17880
- subSessionMeta: {
17881
- idleKillEnabled: true,
17882
- personaMode: "listener",
17883
- extraSettings: this.deps.store.sandboxSettingsPath(personaId)
17884
- }
17926
+ model: persona.model
17885
17927
  });
17886
17928
  return { sessionFile, isNew: true };
17887
17929
  }
@@ -17892,7 +17934,7 @@ var PersonaManager = class {
17892
17934
  appendOwnerMessage(personaId, subSessionId, text) {
17893
17935
  this.deps.sessionManager.injectOwnerMessage({
17894
17936
  sessionId: subSessionId,
17895
- agentId: personaId,
17937
+ scope: { kind: "persona", personaId, mode: "listener" },
17896
17938
  text
17897
17939
  });
17898
17940
  }
@@ -17921,7 +17963,7 @@ var PersonaManager = class {
17921
17963
 
17922
17964
  // src/persona/seed.ts
17923
17965
  var fs7 = __toESM(require("fs"), 1);
17924
- var path7 = __toESM(require("path"), 1);
17966
+ var path8 = __toESM(require("path"), 1);
17925
17967
  var import_node_url = require("url");
17926
17968
  var import_meta = {};
17927
17969
  var DEFAULT_PERSONAS = [
@@ -17950,14 +17992,14 @@ var DEFAULT_PERSONAS = [
17950
17992
  function findDefaultsRoot() {
17951
17993
  const candidates = [];
17952
17994
  try {
17953
- const here = path7.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
17954
- candidates.push(path7.resolve(here, "defaults"));
17955
- candidates.push(path7.resolve(here, "persona-defaults"));
17995
+ const here = path8.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
17996
+ candidates.push(path8.resolve(here, "defaults"));
17997
+ candidates.push(path8.resolve(here, "persona-defaults"));
17956
17998
  } catch {
17957
17999
  }
17958
18000
  if (process.argv[1]) {
17959
- const argvDir = path7.dirname(process.argv[1]);
17960
- candidates.push(path7.resolve(argvDir, "persona-defaults"));
18001
+ const argvDir = path8.dirname(process.argv[1]);
18002
+ candidates.push(path8.resolve(argvDir, "persona-defaults"));
17961
18003
  }
17962
18004
  for (const c of candidates) {
17963
18005
  try {
@@ -17974,7 +18016,7 @@ function seedDefaultPersonas(args) {
17974
18016
  args.logger.info("persona.seed.skip", { personaId: entry.personaId, reason: "exists" });
17975
18017
  continue;
17976
18018
  }
17977
- const bundleDir = path7.join(args.defaultsRoot, entry.personaId);
18019
+ const bundleDir = path8.join(args.defaultsRoot, entry.personaId);
17978
18020
  if (!fs7.existsSync(bundleDir)) {
17979
18021
  args.logger.warn("persona.seed.skip", {
17980
18022
  personaId: entry.personaId,
@@ -17983,7 +18025,7 @@ function seedDefaultPersonas(args) {
17983
18025
  });
17984
18026
  continue;
17985
18027
  }
17986
- const claudeMdPath = path7.join(bundleDir, "CLAUDE.md");
18028
+ const claudeMdPath = path8.join(bundleDir, "CLAUDE.md");
17987
18029
  if (!fs7.existsSync(claudeMdPath)) {
17988
18030
  args.logger.warn("persona.seed.skip", {
17989
18031
  personaId: entry.personaId,
@@ -18012,8 +18054,8 @@ function seedDefaultPersonas(args) {
18012
18054
  function copyBundleExtras(srcDir, dstDir) {
18013
18055
  for (const entry of fs7.readdirSync(srcDir, { withFileTypes: true })) {
18014
18056
  if (entry.name === "CLAUDE.md" || entry.name === ".clawd") continue;
18015
- const srcPath = path7.join(srcDir, entry.name);
18016
- const dstPath = path7.join(dstDir, entry.name);
18057
+ const srcPath = path8.join(srcDir, entry.name);
18058
+ const dstPath = path8.join(dstDir, entry.name);
18017
18059
  if (entry.isDirectory()) {
18018
18060
  fs7.cpSync(srcPath, dstPath, { recursive: true, dereference: true });
18019
18061
  } else if (entry.isFile()) {
@@ -18029,14 +18071,14 @@ init_claude_history();
18029
18071
  // src/workspace/browser.ts
18030
18072
  var import_node_fs8 = __toESM(require("fs"), 1);
18031
18073
  var import_node_os4 = __toESM(require("os"), 1);
18032
- var import_node_path8 = __toESM(require("path"), 1);
18074
+ var import_node_path9 = __toESM(require("path"), 1);
18033
18075
  init_protocol();
18034
18076
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
18035
18077
  function resolveInsideCwd(cwd, subpath) {
18036
- const absCwd = import_node_path8.default.resolve(cwd);
18037
- const joined = import_node_path8.default.resolve(absCwd, subpath ?? ".");
18038
- const rel = import_node_path8.default.relative(absCwd, joined);
18039
- if (rel.startsWith("..") || import_node_path8.default.isAbsolute(rel)) {
18078
+ const absCwd = import_node_path9.default.resolve(cwd);
18079
+ const joined = import_node_path9.default.resolve(absCwd, subpath ?? ".");
18080
+ const rel = import_node_path9.default.relative(absCwd, joined);
18081
+ if (rel.startsWith("..") || import_node_path9.default.isAbsolute(rel)) {
18040
18082
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
18041
18083
  }
18042
18084
  return joined;
@@ -18067,7 +18109,7 @@ var WorkspaceBrowser = class {
18067
18109
  mtime: ""
18068
18110
  };
18069
18111
  try {
18070
- const st = import_node_fs8.default.statSync(import_node_path8.default.join(full, d.name));
18112
+ const st = import_node_fs8.default.statSync(import_node_path9.default.join(full, d.name));
18071
18113
  entry.mtime = new Date(st.mtimeMs).toISOString();
18072
18114
  if (d.isFile()) entry.size = st.size;
18073
18115
  } catch {
@@ -18114,7 +18156,7 @@ var WorkspaceBrowser = class {
18114
18156
  // src/skills/scanner.ts
18115
18157
  var import_node_fs9 = __toESM(require("fs"), 1);
18116
18158
  var import_node_os5 = __toESM(require("os"), 1);
18117
- var import_node_path9 = __toESM(require("path"), 1);
18159
+ var import_node_path10 = __toESM(require("path"), 1);
18118
18160
 
18119
18161
  // src/skills/frontmatter.ts
18120
18162
  var STRIP_QUOTES = /^["']|["']$/g;
@@ -18236,14 +18278,14 @@ function scanSkillDir(dir, source, seen, out, pluginName) {
18236
18278
  return;
18237
18279
  }
18238
18280
  for (const ent of entries) {
18239
- const entryPath = import_node_path9.default.join(dir, ent.name);
18281
+ const entryPath = import_node_path10.default.join(dir, ent.name);
18240
18282
  if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
18241
18283
  let content;
18242
18284
  try {
18243
- content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "SKILL.md"), "utf8");
18285
+ content = import_node_fs9.default.readFileSync(import_node_path10.default.join(entryPath, "SKILL.md"), "utf8");
18244
18286
  } catch {
18245
18287
  try {
18246
- content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "skill.md"), "utf8");
18288
+ content = import_node_fs9.default.readFileSync(import_node_path10.default.join(entryPath, "skill.md"), "utf8");
18247
18289
  } catch {
18248
18290
  continue;
18249
18291
  }
@@ -18266,7 +18308,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
18266
18308
  return;
18267
18309
  }
18268
18310
  for (const ent of entries) {
18269
- const entryPath = import_node_path9.default.join(dir, ent.name);
18311
+ const entryPath = import_node_path10.default.join(dir, ent.name);
18270
18312
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
18271
18313
  const ns = ent.name;
18272
18314
  let subEntries;
@@ -18277,7 +18319,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
18277
18319
  }
18278
18320
  for (const se of subEntries) {
18279
18321
  if (!se.name.endsWith(".md")) continue;
18280
- const sePath = import_node_path9.default.join(entryPath, se.name);
18322
+ const sePath = import_node_path10.default.join(entryPath, se.name);
18281
18323
  let content;
18282
18324
  try {
18283
18325
  content = import_node_fs9.default.readFileSync(sePath, "utf8");
@@ -18313,7 +18355,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
18313
18355
  }
18314
18356
  }
18315
18357
  function readInstalledPlugins(home) {
18316
- const file = import_node_path9.default.join(home, ".claude", "plugins", "installed_plugins.json");
18358
+ const file = import_node_path10.default.join(home, ".claude", "plugins", "installed_plugins.json");
18317
18359
  let raw;
18318
18360
  try {
18319
18361
  raw = import_node_fs9.default.readFileSync(file, "utf8");
@@ -18364,14 +18406,14 @@ var SkillsScanner = class {
18364
18406
  });
18365
18407
  }
18366
18408
  const fsBlock = [];
18367
- scanSkillDir(import_node_path9.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
18368
- scanCommandDir(import_node_path9.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
18369
- scanSkillDir(import_node_path9.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
18370
- scanCommandDir(import_node_path9.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
18409
+ scanSkillDir(import_node_path10.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
18410
+ scanCommandDir(import_node_path10.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
18411
+ scanSkillDir(import_node_path10.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
18412
+ scanCommandDir(import_node_path10.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
18371
18413
  const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
18372
18414
  for (const { name, root } of plugins) {
18373
- scanSkillDir(import_node_path9.default.join(root, "skills"), "plugin", seen, fsBlock, name);
18374
- scanCommandDir(import_node_path9.default.join(root, "commands"), "plugin", seen, fsBlock, name);
18415
+ scanSkillDir(import_node_path10.default.join(root, "skills"), "plugin", seen, fsBlock, name);
18416
+ scanCommandDir(import_node_path10.default.join(root, "commands"), "plugin", seen, fsBlock, name);
18375
18417
  }
18376
18418
  fsBlock.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
18377
18419
  return [...builtinBlock, ...fsBlock];
@@ -18381,7 +18423,7 @@ var SkillsScanner = class {
18381
18423
  // src/skills/agents-scanner.ts
18382
18424
  var import_node_fs10 = __toESM(require("fs"), 1);
18383
18425
  var import_node_os6 = __toESM(require("os"), 1);
18384
- var import_node_path10 = __toESM(require("path"), 1);
18426
+ var import_node_path11 = __toESM(require("path"), 1);
18385
18427
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
18386
18428
  function isDirLikeSync2(p) {
18387
18429
  try {
@@ -18419,10 +18461,10 @@ function scanAgentsDir(dir, source, seen, out) {
18419
18461
  }
18420
18462
  for (const ent of entries) {
18421
18463
  if (!ent.name.endsWith(".md")) continue;
18422
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path10.default.join(dir, ent.name)))) {
18464
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path11.default.join(dir, ent.name)))) {
18423
18465
  continue;
18424
18466
  }
18425
- const filePath = import_node_path10.default.join(dir, ent.name);
18467
+ const filePath = import_node_path11.default.join(dir, ent.name);
18426
18468
  const baseName = ent.name.replace(/\.md$/, "");
18427
18469
  if (seen.has(baseName)) continue;
18428
18470
  seen.add(baseName);
@@ -18445,7 +18487,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
18445
18487
  return;
18446
18488
  }
18447
18489
  for (const ent of entries) {
18448
- const childPath = import_node_path10.default.join(dir, ent.name);
18490
+ const childPath = import_node_path11.default.join(dir, ent.name);
18449
18491
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
18450
18492
  walk(childPath, [...namespaces, ent.name]);
18451
18493
  continue;
@@ -18470,9 +18512,9 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
18470
18512
  walk(root, []);
18471
18513
  }
18472
18514
  function readInstalledPlugins2(home) {
18473
- const pluginsDir = import_node_path10.default.join(home, ".claude", "plugins");
18474
- const v2 = import_node_path10.default.join(pluginsDir, "installed_plugins_v2.json");
18475
- const v1 = import_node_path10.default.join(pluginsDir, "installed_plugins.json");
18515
+ const pluginsDir = import_node_path11.default.join(home, ".claude", "plugins");
18516
+ const v2 = import_node_path11.default.join(pluginsDir, "installed_plugins_v2.json");
18517
+ const v1 = import_node_path11.default.join(pluginsDir, "installed_plugins.json");
18476
18518
  let raw = null;
18477
18519
  for (const candidate of [v2, v1]) {
18478
18520
  try {
@@ -18501,19 +18543,19 @@ function readInstalledPlugins2(home) {
18501
18543
  return out;
18502
18544
  }
18503
18545
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
18504
- let cur = import_node_path10.default.resolve(startCwd);
18505
- const fsRoot = import_node_path10.default.parse(cur).root;
18546
+ let cur = import_node_path11.default.resolve(startCwd);
18547
+ const fsRoot = import_node_path11.default.parse(cur).root;
18506
18548
  while (true) {
18507
- scanAgentsDir(import_node_path10.default.join(cur, ".claude", "agents"), "project", seen, out);
18549
+ scanAgentsDir(import_node_path11.default.join(cur, ".claude", "agents"), "project", seen, out);
18508
18550
  let hasGit = false;
18509
18551
  try {
18510
- hasGit = import_node_fs10.default.existsSync(import_node_path10.default.join(cur, ".git"));
18552
+ hasGit = import_node_fs10.default.existsSync(import_node_path11.default.join(cur, ".git"));
18511
18553
  } catch {
18512
18554
  }
18513
18555
  if (hasGit) return;
18514
18556
  if (cur === home) return;
18515
18557
  if (cur === fsRoot) return;
18516
- const parent = import_node_path10.default.dirname(cur);
18558
+ const parent = import_node_path11.default.dirname(cur);
18517
18559
  if (parent === cur) return;
18518
18560
  cur = parent;
18519
18561
  }
@@ -18548,7 +18590,7 @@ var AgentsScanner = class {
18548
18590
  }
18549
18591
  const fsBlock = [];
18550
18592
  scanAgentsDir(
18551
- import_node_path10.default.join(this.home, ".claude", "agents"),
18593
+ import_node_path11.default.join(this.home, ".claude", "agents"),
18552
18594
  "global",
18553
18595
  seen,
18554
18596
  fsBlock
@@ -18562,7 +18604,7 @@ var AgentsScanner = class {
18562
18604
  ...this.extraPluginRoots
18563
18605
  ];
18564
18606
  for (const { name, root } of plugins) {
18565
- const agentsRoot = import_node_path10.default.join(root, "agents");
18607
+ const agentsRoot = import_node_path11.default.join(root, "agents");
18566
18608
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
18567
18609
  }
18568
18610
  return [...builtinBlock, ...fsBlock];
@@ -18572,7 +18614,7 @@ var AgentsScanner = class {
18572
18614
  // src/observer/session-observer.ts
18573
18615
  var import_node_fs11 = __toESM(require("fs"), 1);
18574
18616
  var import_node_os7 = __toESM(require("os"), 1);
18575
- var import_node_path11 = __toESM(require("path"), 1);
18617
+ var import_node_path12 = __toESM(require("path"), 1);
18576
18618
  init_claude_history();
18577
18619
  var SessionObserver = class {
18578
18620
  constructor(opts) {
@@ -18584,7 +18626,7 @@ var SessionObserver = class {
18584
18626
  watches = /* @__PURE__ */ new Map();
18585
18627
  resolveJsonlPath(cwd, toolSessionId, override) {
18586
18628
  if (override) return override;
18587
- return import_node_path11.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
18629
+ return import_node_path12.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
18588
18630
  }
18589
18631
  start(args) {
18590
18632
  this.stop(args.sessionId);
@@ -18604,10 +18646,10 @@ var SessionObserver = class {
18604
18646
  adapter: args.adapter
18605
18647
  };
18606
18648
  try {
18607
- import_node_fs11.default.mkdirSync(import_node_path11.default.dirname(filePath), { recursive: true });
18649
+ import_node_fs11.default.mkdirSync(import_node_path12.default.dirname(filePath), { recursive: true });
18608
18650
  } catch {
18609
18651
  }
18610
- w.watcher = import_node_fs11.default.watch(import_node_path11.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
18652
+ w.watcher = import_node_fs11.default.watch(import_node_path12.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
18611
18653
  if (!changedName || !filePath.endsWith(changedName)) return;
18612
18654
  this.poll(w);
18613
18655
  });
@@ -19123,13 +19165,18 @@ var PersonaBoundHandler = class {
19123
19165
  }
19124
19166
  return true;
19125
19167
  };
19168
+ const listenerScope = () => ({
19169
+ kind: "persona",
19170
+ personaId: scope.personaId,
19171
+ mode: "listener"
19172
+ });
19126
19173
  switch (type) {
19127
19174
  case "session:subscribe": {
19128
19175
  if (!requireScopedSession()) return;
19129
19176
  if (unsubscribe) unsubscribe();
19130
19177
  unsubscribe = this.deps.sessionManager.subscribe(
19131
19178
  scope.subSessionId,
19132
- scope.personaId,
19179
+ listenerScope(),
19133
19180
  (eventFrame) => send(eventFrame)
19134
19181
  );
19135
19182
  if (requestId)
@@ -19159,9 +19206,9 @@ var PersonaBoundHandler = class {
19159
19206
  return;
19160
19207
  }
19161
19208
  try {
19162
- this.deps.sessionManager.sendForAgent({
19209
+ this.deps.sessionManager.sendForScope({
19163
19210
  sessionId: scope.subSessionId,
19164
- agentId: scope.personaId,
19211
+ scope: listenerScope(),
19165
19212
  text
19166
19213
  });
19167
19214
  if (requestId) send({ type: "session:send", ok: true, requestId });
@@ -19174,9 +19221,9 @@ var PersonaBoundHandler = class {
19174
19221
  case "session:new": {
19175
19222
  if (!requireScopedSession()) return;
19176
19223
  try {
19177
- this.deps.sessionManager.resetForAgent({
19224
+ this.deps.sessionManager.resetForScope({
19178
19225
  sessionId: scope.subSessionId,
19179
- agentId: scope.personaId
19226
+ scope: listenerScope()
19180
19227
  });
19181
19228
  if (requestId) send({ type: "session:info", sessionId: scope.subSessionId, requestId });
19182
19229
  } catch (err) {
@@ -19190,9 +19237,9 @@ var PersonaBoundHandler = class {
19190
19237
  const limit = typeof frame.limit === "number" && frame.limit > 0 ? frame.limit : 50;
19191
19238
  const offset = typeof frame.offset === "number" && frame.offset >= 0 ? frame.offset : 0;
19192
19239
  try {
19193
- const page = this.deps.sessionManager.readHistoryPage(
19240
+ const page = this.deps.sessionManager.readHistoryPageForScope(
19194
19241
  scope.subSessionId,
19195
- scope.personaId,
19242
+ listenerScope(),
19196
19243
  limit,
19197
19244
  offset
19198
19245
  );
@@ -19323,9 +19370,9 @@ function isLocalhost(addr) {
19323
19370
 
19324
19371
  // src/discovery/state-file.ts
19325
19372
  var import_node_fs12 = __toESM(require("fs"), 1);
19326
- var import_node_path12 = __toESM(require("path"), 1);
19373
+ var import_node_path13 = __toESM(require("path"), 1);
19327
19374
  function defaultStateFilePath(dataDir) {
19328
- return import_node_path12.default.join(dataDir, "state.json");
19375
+ return import_node_path13.default.join(dataDir, "state.json");
19329
19376
  }
19330
19377
  function isPidAlive(pid) {
19331
19378
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -19361,7 +19408,7 @@ var StateFileManager = class {
19361
19408
  return { status: "stale", existing };
19362
19409
  }
19363
19410
  write(state) {
19364
- import_node_fs12.default.mkdirSync(import_node_path12.default.dirname(this.file), { recursive: true });
19411
+ import_node_fs12.default.mkdirSync(import_node_path13.default.dirname(this.file), { recursive: true });
19365
19412
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
19366
19413
  import_node_fs12.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
19367
19414
  import_node_fs12.default.renameSync(tmp, this.file);
@@ -19382,13 +19429,13 @@ var StateFileManager = class {
19382
19429
 
19383
19430
  // src/tunnel/tunnel-manager.ts
19384
19431
  var import_node_fs15 = __toESM(require("fs"), 1);
19385
- var import_node_path15 = __toESM(require("path"), 1);
19432
+ var import_node_path16 = __toESM(require("path"), 1);
19386
19433
  var import_node_crypto4 = __toESM(require("crypto"), 1);
19387
19434
  var import_node_child_process4 = require("child_process");
19388
19435
 
19389
19436
  // src/tunnel/tunnel-store.ts
19390
19437
  var import_node_fs13 = __toESM(require("fs"), 1);
19391
- var import_node_path13 = __toESM(require("path"), 1);
19438
+ var import_node_path14 = __toESM(require("path"), 1);
19392
19439
  var TunnelStore = class {
19393
19440
  constructor(filePath) {
19394
19441
  this.filePath = filePath;
@@ -19407,7 +19454,7 @@ var TunnelStore = class {
19407
19454
  }
19408
19455
  }
19409
19456
  async set(v) {
19410
- const dir = import_node_path13.default.dirname(this.filePath);
19457
+ const dir = import_node_path14.default.dirname(this.filePath);
19411
19458
  await import_node_fs13.default.promises.mkdir(dir, { recursive: true });
19412
19459
  const data = JSON.stringify(v, null, 2);
19413
19460
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
@@ -19519,7 +19566,7 @@ function escape(v) {
19519
19566
  // src/tunnel/frpc-binary.ts
19520
19567
  var import_node_fs14 = __toESM(require("fs"), 1);
19521
19568
  var import_node_os8 = __toESM(require("os"), 1);
19522
- var import_node_path14 = __toESM(require("path"), 1);
19569
+ var import_node_path15 = __toESM(require("path"), 1);
19523
19570
  var import_node_child_process3 = require("child_process");
19524
19571
  var import_node_stream = require("stream");
19525
19572
  var import_promises = require("stream/promises");
@@ -19558,13 +19605,13 @@ async function ensureFrpcBinary(opts) {
19558
19605
  }
19559
19606
  const version2 = opts.version ?? FRPC_VERSION;
19560
19607
  const platform = opts.platform ?? detectPlatform();
19561
- const binDir = import_node_path14.default.join(opts.dataDir, "bin");
19608
+ const binDir = import_node_path15.default.join(opts.dataDir, "bin");
19562
19609
  import_node_fs14.default.mkdirSync(binDir, { recursive: true });
19563
19610
  cleanupStaleArtifacts(binDir);
19564
- const stableBin = import_node_path14.default.join(binDir, "frpc");
19611
+ const stableBin = import_node_path15.default.join(binDir, "frpc");
19565
19612
  if (import_node_fs14.default.existsSync(stableBin)) return stableBin;
19566
19613
  const partialBin = `${stableBin}.partial`;
19567
- const tarballPath = import_node_path14.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
19614
+ const tarballPath = import_node_path15.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
19568
19615
  try {
19569
19616
  const url = frpcDownloadUrl(version2, platform);
19570
19617
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -19590,7 +19637,7 @@ function cleanupStaleArtifacts(binDir) {
19590
19637
  }
19591
19638
  for (const name of entries) {
19592
19639
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
19593
- const full = import_node_path14.default.join(binDir, name);
19640
+ const full = import_node_path15.default.join(binDir, name);
19594
19641
  try {
19595
19642
  import_node_fs14.default.rmSync(full, { recursive: true, force: true });
19596
19643
  } catch {
@@ -19616,7 +19663,7 @@ async function downloadToFile(url, dest, fetchImpl) {
19616
19663
  await (0, import_promises.pipeline)(nodeStream, out);
19617
19664
  }
19618
19665
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
19619
- const work = import_node_path14.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
19666
+ const work = import_node_path15.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
19620
19667
  import_node_fs14.default.mkdirSync(work, { recursive: true });
19621
19668
  try {
19622
19669
  await new Promise((resolve2, reject) => {
@@ -19625,7 +19672,7 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
19625
19672
  proc.on("exit", (code) => code === 0 ? resolve2() : reject(new Error(`tar exited ${code}`)));
19626
19673
  });
19627
19674
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
19628
- const src = import_node_path14.default.join(work, dirName, "frpc");
19675
+ const src = import_node_path15.default.join(work, dirName, "frpc");
19629
19676
  if (!import_node_fs14.default.existsSync(src)) {
19630
19677
  throw new Error(`frpc not found inside tarball at ${src}`);
19631
19678
  }
@@ -19640,7 +19687,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
19640
19687
  var TunnelManager = class {
19641
19688
  constructor(deps) {
19642
19689
  this.deps = deps;
19643
- this.store = deps.store ?? new TunnelStore(import_node_path15.default.join(deps.dataDir, "tunnel.json"));
19690
+ this.store = deps.store ?? new TunnelStore(import_node_path16.default.join(deps.dataDir, "tunnel.json"));
19644
19691
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
19645
19692
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
19646
19693
  }
@@ -19757,7 +19804,7 @@ var TunnelManager = class {
19757
19804
  dataDir: this.deps.dataDir,
19758
19805
  override: this.deps.frpcBinaryOverride ?? void 0
19759
19806
  });
19760
- const tomlPath = import_node_path15.default.join(this.deps.dataDir, "frpc.toml");
19807
+ const tomlPath = import_node_path16.default.join(this.deps.dataDir, "frpc.toml");
19761
19808
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto4.default.randomBytes(3).toString("hex")}`;
19762
19809
  const toml = buildFrpcToml({
19763
19810
  serverAddr: t.frpsHost,
@@ -19772,7 +19819,7 @@ var TunnelManager = class {
19772
19819
  const proc = (this.deps.spawnImpl ?? import_node_child_process4.spawn)(frpcBin, ["-c", tomlPath], {
19773
19820
  stdio: ["ignore", "pipe", "pipe"]
19774
19821
  });
19775
- const logFilePath = import_node_path15.default.join(this.deps.dataDir, "frpc.log");
19822
+ const logFilePath = import_node_path16.default.join(this.deps.dataDir, "frpc.log");
19776
19823
  const logStream = import_node_fs15.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
19777
19824
  logStream.on("error", () => {
19778
19825
  });
@@ -19866,11 +19913,11 @@ function deriveStableDeviceKey(opts = {}) {
19866
19913
 
19867
19914
  // src/auth-store.ts
19868
19915
  var import_node_fs16 = __toESM(require("fs"), 1);
19869
- var import_node_path16 = __toESM(require("path"), 1);
19916
+ var import_node_path17 = __toESM(require("path"), 1);
19870
19917
  var import_node_crypto6 = __toESM(require("crypto"), 1);
19871
19918
  var AUTH_FILE_NAME = "auth.json";
19872
19919
  function authFilePath(dataDir) {
19873
- return import_node_path16.default.join(dataDir, AUTH_FILE_NAME);
19920
+ return import_node_path17.default.join(dataDir, AUTH_FILE_NAME);
19874
19921
  }
19875
19922
  function loadOrCreateAuthToken(opts) {
19876
19923
  const file = authFilePath(opts.dataDir);
@@ -19902,7 +19949,7 @@ function readAuthFile(file) {
19902
19949
  }
19903
19950
  }
19904
19951
  function writeAuthFile(file, content) {
19905
- import_node_fs16.default.mkdirSync(import_node_path16.default.dirname(file), { recursive: true });
19952
+ import_node_fs16.default.mkdirSync(import_node_path17.default.dirname(file), { recursive: true });
19906
19953
  import_node_fs16.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
19907
19954
  try {
19908
19955
  import_node_fs16.default.chmodSync(file, 384);
@@ -19913,11 +19960,11 @@ function writeAuthFile(file, content) {
19913
19960
  // src/owner-profile.ts
19914
19961
  var import_node_fs17 = __toESM(require("fs"), 1);
19915
19962
  var import_node_os10 = __toESM(require("os"), 1);
19916
- var import_node_path17 = __toESM(require("path"), 1);
19963
+ var import_node_path18 = __toESM(require("path"), 1);
19917
19964
  var PROFILE_FILENAME = "profile.json";
19918
19965
  function loadOwnerDisplayName(dataDir) {
19919
19966
  const fallback = import_node_os10.default.userInfo().username;
19920
- const profilePath = import_node_path17.default.join(dataDir, PROFILE_FILENAME);
19967
+ const profilePath = import_node_path18.default.join(dataDir, PROFILE_FILENAME);
19921
19968
  let raw;
19922
19969
  try {
19923
19970
  raw = import_node_fs17.default.readFileSync(profilePath, "utf8");
@@ -19951,7 +19998,7 @@ init_protocol();
19951
19998
  // src/session/fork.ts
19952
19999
  var import_node_fs18 = __toESM(require("fs"), 1);
19953
20000
  var import_node_os11 = __toESM(require("os"), 1);
19954
- var import_node_path18 = __toESM(require("path"), 1);
20001
+ var import_node_path19 = __toESM(require("path"), 1);
19955
20002
  init_claude_history();
19956
20003
  function readJsonlEntries(file) {
19957
20004
  const raw = import_node_fs18.default.readFileSync(file, "utf8");
@@ -19967,9 +20014,9 @@ function readJsonlEntries(file) {
19967
20014
  return out;
19968
20015
  }
19969
20016
  function forkSession(input) {
19970
- const baseDir = input.baseDir ?? import_node_path18.default.join(import_node_os11.default.homedir(), ".claude");
19971
- const projectDir = import_node_path18.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
19972
- const sourceFile = import_node_path18.default.join(projectDir, `${input.toolSessionId}.jsonl`);
20017
+ const baseDir = input.baseDir ?? import_node_path19.default.join(import_node_os11.default.homedir(), ".claude");
20018
+ const projectDir = import_node_path19.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
20019
+ const sourceFile = import_node_path19.default.join(projectDir, `${input.toolSessionId}.jsonl`);
19973
20020
  if (!import_node_fs18.default.existsSync(sourceFile)) {
19974
20021
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
19975
20022
  }
@@ -20000,7 +20047,7 @@ function forkSession(input) {
20000
20047
  }
20001
20048
  forkedLines.push(JSON.stringify(forked));
20002
20049
  }
20003
- const forkedFilePath = import_node_path18.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
20050
+ const forkedFilePath = import_node_path19.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
20004
20051
  import_node_fs18.default.mkdirSync(projectDir, { recursive: true });
20005
20052
  import_node_fs18.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
20006
20053
  return { forkedToolSessionId, forkedFilePath };
@@ -20292,7 +20339,7 @@ init_protocol();
20292
20339
  var import_node_child_process5 = require("child_process");
20293
20340
  var import_node_fs19 = __toESM(require("fs"), 1);
20294
20341
  var import_node_os12 = __toESM(require("os"), 1);
20295
- var import_node_path19 = __toESM(require("path"), 1);
20342
+ var import_node_path20 = __toESM(require("path"), 1);
20296
20343
  var import_node_util = require("util");
20297
20344
  var pexec = (0, import_node_util.promisify)(import_node_child_process5.execFile);
20298
20345
  function formatChildProcessError(err) {
@@ -20307,7 +20354,7 @@ function formatChildProcessError(err) {
20307
20354
  return e.message ?? "unknown error";
20308
20355
  }
20309
20356
  function normalizePath(p) {
20310
- const resolved = import_node_path19.default.resolve(p);
20357
+ const resolved = import_node_path20.default.resolve(p);
20311
20358
  try {
20312
20359
  return import_node_fs19.default.realpathSync(resolved);
20313
20360
  } catch {
@@ -20410,13 +20457,13 @@ function flattenToDirName(branch) {
20410
20457
  }
20411
20458
  function encodeClaudeProjectDir(absPath) {
20412
20459
  if (!absPath || typeof absPath !== "string") return "";
20413
- let canonical = import_node_path19.default.resolve(absPath);
20460
+ let canonical = import_node_path20.default.resolve(absPath);
20414
20461
  try {
20415
20462
  canonical = import_node_fs19.default.realpathSync(canonical);
20416
20463
  } catch {
20417
20464
  try {
20418
- const parent = import_node_fs19.default.realpathSync(import_node_path19.default.dirname(canonical));
20419
- canonical = import_node_path19.default.join(parent, import_node_path19.default.basename(canonical));
20465
+ const parent = import_node_fs19.default.realpathSync(import_node_path20.default.dirname(canonical));
20466
+ canonical = import_node_path20.default.join(parent, import_node_path20.default.basename(canonical));
20420
20467
  } catch {
20421
20468
  }
20422
20469
  }
@@ -20440,11 +20487,11 @@ async function createWorktree(input) {
20440
20487
  if (!isGitRoot) {
20441
20488
  throw new Error(`\u76EE\u5F55 ${cwd} \u4E0D\u662F git repo \u6839`);
20442
20489
  }
20443
- const parent = import_node_path19.default.dirname(import_node_path19.default.resolve(cwd));
20444
- if (parent === "/" || parent === import_node_path19.default.resolve(cwd)) {
20490
+ const parent = import_node_path20.default.dirname(import_node_path20.default.resolve(cwd));
20491
+ if (parent === "/" || parent === import_node_path20.default.resolve(cwd)) {
20445
20492
  throw new Error("repo \u5728\u78C1\u76D8\u6839\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5728\u540C\u7EA7\u521B\u5EFA worktree");
20446
20493
  }
20447
- const worktreeRoot = import_node_path19.default.join(parent, dirName);
20494
+ const worktreeRoot = import_node_path20.default.join(parent, dirName);
20448
20495
  try {
20449
20496
  await pexec("git", ["-C", cwd, "fetch", "origin", baseBranch, "--no-tags"], {
20450
20497
  timeout: 3e4
@@ -20491,8 +20538,8 @@ async function removeWorktree(input) {
20491
20538
  );
20492
20539
  const gitCommonDir = stdout.trim();
20493
20540
  if (!gitCommonDir) throw new Error("empty git-common-dir");
20494
- const absGitCommon = import_node_path19.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path19.default.resolve(worktreeRoot, gitCommonDir);
20495
- repoRoot = import_node_path19.default.dirname(absGitCommon);
20541
+ const absGitCommon = import_node_path20.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path20.default.resolve(worktreeRoot, gitCommonDir);
20542
+ repoRoot = import_node_path20.default.dirname(absGitCommon);
20496
20543
  } catch {
20497
20544
  repoRoot = null;
20498
20545
  }
@@ -20523,9 +20570,9 @@ async function removeWorktree(input) {
20523
20570
  try {
20524
20571
  const encoded = encodeClaudeProjectDir(worktreeRoot);
20525
20572
  if (encoded) {
20526
- const projectsRoot = import_node_path19.default.join(import_node_os12.default.homedir(), ".claude", "projects");
20527
- const target = import_node_path19.default.resolve(projectsRoot, encoded);
20528
- if (target.startsWith(projectsRoot + import_node_path19.default.sep) && target !== projectsRoot) {
20573
+ const projectsRoot = import_node_path20.default.join(import_node_os12.default.homedir(), ".claude", "projects");
20574
+ const target = import_node_path20.default.resolve(projectsRoot, encoded);
20575
+ if (target.startsWith(projectsRoot + import_node_path20.default.sep) && target !== projectsRoot) {
20529
20576
  import_node_fs19.default.rmSync(target, { recursive: true, force: true });
20530
20577
  }
20531
20578
  }
@@ -20694,7 +20741,7 @@ function buildPersonaHandlers(deps) {
20694
20741
  };
20695
20742
  const listSubSessions = async (frame) => {
20696
20743
  const args = PersonaIdArgsSchema.parse(frame);
20697
- const sessions = sessionManager.listForAgent(args.personaId);
20744
+ const sessions = sessionManager.listPersonaSessions(args.personaId, "listener");
20698
20745
  return {
20699
20746
  response: { type: "persona:subSessions", sessions }
20700
20747
  };
@@ -20741,7 +20788,7 @@ function buildMethodHandlers(deps) {
20741
20788
  async function startDaemon(config) {
20742
20789
  const logger = createLogger({
20743
20790
  level: config.logLevel,
20744
- file: import_node_path20.default.join(config.dataDir, "clawd.log")
20791
+ file: import_node_path21.default.join(config.dataDir, "clawd.log")
20745
20792
  });
20746
20793
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
20747
20794
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -20777,7 +20824,7 @@ async function startDaemon(config) {
20777
20824
  const agents = new AgentsScanner();
20778
20825
  const history = new ClaudeHistoryReader();
20779
20826
  let transport = null;
20780
- const personaStore = new PersonaStore(import_node_path20.default.join(config.dataDir, "personas"));
20827
+ const personaStore = new PersonaStore(import_node_path21.default.join(config.dataDir, "personas"));
20781
20828
  const defaultsRoot = findDefaultsRoot();
20782
20829
  if (defaultsRoot) {
20783
20830
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -20791,7 +20838,7 @@ async function startDaemon(config) {
20791
20838
  getAdapter,
20792
20839
  historyReader: history,
20793
20840
  dataDir: config.dataDir,
20794
- personaRoot: import_node_path20.default.join(config.dataDir, "personas"),
20841
+ personaRoot: import_node_path21.default.join(config.dataDir, "personas"),
20795
20842
  personaStore,
20796
20843
  ownerDisplayName,
20797
20844
  broadcastFrame: (frame, target) => {
@@ -20965,8 +21012,8 @@ async function startDaemon(config) {
20965
21012
  const lines = [
20966
21013
  `Tunnel: ${r.url}`,
20967
21014
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
20968
- `Frpc config: ${import_node_path20.default.join(config.dataDir, "frpc.toml")}`,
20969
- `Frpc log: ${import_node_path20.default.join(config.dataDir, "frpc.log")}`
21015
+ `Frpc config: ${import_node_path21.default.join(config.dataDir, "frpc.toml")}`,
21016
+ `Frpc log: ${import_node_path21.default.join(config.dataDir, "frpc.log")}`
20970
21017
  ];
20971
21018
  const width = Math.max(...lines.map((l) => l.length));
20972
21019
  const bar = "\u2550".repeat(width + 4);
@@ -20979,7 +21026,7 @@ ${bar}
20979
21026
 
20980
21027
  `);
20981
21028
  try {
20982
- const connectPath = import_node_path20.default.join(config.dataDir, "connect.txt");
21029
+ const connectPath = import_node_path21.default.join(config.dataDir, "connect.txt");
20983
21030
  import_node_fs20.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
20984
21031
  } catch {
20985
21032
  }