@clawos-dev/clawd 0.2.47 → 0.2.48-beta.74.b742e0f

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 = [
@@ -17943,14 +17985,14 @@ var DEFAULT_PERSONAS = [
17943
17985
  function findDefaultsRoot() {
17944
17986
  const candidates = [];
17945
17987
  try {
17946
- const here = path7.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
17947
- candidates.push(path7.resolve(here, "defaults"));
17948
- candidates.push(path7.resolve(here, "persona-defaults"));
17988
+ const here = path8.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
17989
+ candidates.push(path8.resolve(here, "defaults"));
17990
+ candidates.push(path8.resolve(here, "persona-defaults"));
17949
17991
  } catch {
17950
17992
  }
17951
17993
  if (process.argv[1]) {
17952
- const argvDir = path7.dirname(process.argv[1]);
17953
- candidates.push(path7.resolve(argvDir, "persona-defaults"));
17994
+ const argvDir = path8.dirname(process.argv[1]);
17995
+ candidates.push(path8.resolve(argvDir, "persona-defaults"));
17954
17996
  }
17955
17997
  for (const c of candidates) {
17956
17998
  try {
@@ -17967,7 +18009,7 @@ function seedDefaultPersonas(args) {
17967
18009
  args.logger.info("persona.seed.skip", { personaId: entry.personaId, reason: "exists" });
17968
18010
  continue;
17969
18011
  }
17970
- const bundleDir = path7.join(args.defaultsRoot, entry.personaId);
18012
+ const bundleDir = path8.join(args.defaultsRoot, entry.personaId);
17971
18013
  if (!fs7.existsSync(bundleDir)) {
17972
18014
  args.logger.warn("persona.seed.skip", {
17973
18015
  personaId: entry.personaId,
@@ -17976,7 +18018,7 @@ function seedDefaultPersonas(args) {
17976
18018
  });
17977
18019
  continue;
17978
18020
  }
17979
- const claudeMdPath = path7.join(bundleDir, "CLAUDE.md");
18021
+ const claudeMdPath = path8.join(bundleDir, "CLAUDE.md");
17980
18022
  if (!fs7.existsSync(claudeMdPath)) {
17981
18023
  args.logger.warn("persona.seed.skip", {
17982
18024
  personaId: entry.personaId,
@@ -18005,8 +18047,8 @@ function seedDefaultPersonas(args) {
18005
18047
  function copyBundleExtras(srcDir, dstDir) {
18006
18048
  for (const entry of fs7.readdirSync(srcDir, { withFileTypes: true })) {
18007
18049
  if (entry.name === "CLAUDE.md" || entry.name === ".clawd") continue;
18008
- const srcPath = path7.join(srcDir, entry.name);
18009
- const dstPath = path7.join(dstDir, entry.name);
18050
+ const srcPath = path8.join(srcDir, entry.name);
18051
+ const dstPath = path8.join(dstDir, entry.name);
18010
18052
  if (entry.isDirectory()) {
18011
18053
  fs7.cpSync(srcPath, dstPath, { recursive: true, dereference: true });
18012
18054
  } else if (entry.isFile()) {
@@ -18022,14 +18064,14 @@ init_claude_history();
18022
18064
  // src/workspace/browser.ts
18023
18065
  var import_node_fs8 = __toESM(require("fs"), 1);
18024
18066
  var import_node_os4 = __toESM(require("os"), 1);
18025
- var import_node_path8 = __toESM(require("path"), 1);
18067
+ var import_node_path9 = __toESM(require("path"), 1);
18026
18068
  init_protocol();
18027
18069
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
18028
18070
  function resolveInsideCwd(cwd, subpath) {
18029
- const absCwd = import_node_path8.default.resolve(cwd);
18030
- const joined = import_node_path8.default.resolve(absCwd, subpath ?? ".");
18031
- const rel = import_node_path8.default.relative(absCwd, joined);
18032
- if (rel.startsWith("..") || import_node_path8.default.isAbsolute(rel)) {
18071
+ const absCwd = import_node_path9.default.resolve(cwd);
18072
+ const joined = import_node_path9.default.resolve(absCwd, subpath ?? ".");
18073
+ const rel = import_node_path9.default.relative(absCwd, joined);
18074
+ if (rel.startsWith("..") || import_node_path9.default.isAbsolute(rel)) {
18033
18075
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
18034
18076
  }
18035
18077
  return joined;
@@ -18060,7 +18102,7 @@ var WorkspaceBrowser = class {
18060
18102
  mtime: ""
18061
18103
  };
18062
18104
  try {
18063
- const st = import_node_fs8.default.statSync(import_node_path8.default.join(full, d.name));
18105
+ const st = import_node_fs8.default.statSync(import_node_path9.default.join(full, d.name));
18064
18106
  entry.mtime = new Date(st.mtimeMs).toISOString();
18065
18107
  if (d.isFile()) entry.size = st.size;
18066
18108
  } catch {
@@ -18107,7 +18149,7 @@ var WorkspaceBrowser = class {
18107
18149
  // src/skills/scanner.ts
18108
18150
  var import_node_fs9 = __toESM(require("fs"), 1);
18109
18151
  var import_node_os5 = __toESM(require("os"), 1);
18110
- var import_node_path9 = __toESM(require("path"), 1);
18152
+ var import_node_path10 = __toESM(require("path"), 1);
18111
18153
 
18112
18154
  // src/skills/frontmatter.ts
18113
18155
  var STRIP_QUOTES = /^["']|["']$/g;
@@ -18229,14 +18271,14 @@ function scanSkillDir(dir, source, seen, out, pluginName) {
18229
18271
  return;
18230
18272
  }
18231
18273
  for (const ent of entries) {
18232
- const entryPath = import_node_path9.default.join(dir, ent.name);
18274
+ const entryPath = import_node_path10.default.join(dir, ent.name);
18233
18275
  if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
18234
18276
  let content;
18235
18277
  try {
18236
- content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "SKILL.md"), "utf8");
18278
+ content = import_node_fs9.default.readFileSync(import_node_path10.default.join(entryPath, "SKILL.md"), "utf8");
18237
18279
  } catch {
18238
18280
  try {
18239
- content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "skill.md"), "utf8");
18281
+ content = import_node_fs9.default.readFileSync(import_node_path10.default.join(entryPath, "skill.md"), "utf8");
18240
18282
  } catch {
18241
18283
  continue;
18242
18284
  }
@@ -18259,7 +18301,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
18259
18301
  return;
18260
18302
  }
18261
18303
  for (const ent of entries) {
18262
- const entryPath = import_node_path9.default.join(dir, ent.name);
18304
+ const entryPath = import_node_path10.default.join(dir, ent.name);
18263
18305
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
18264
18306
  const ns = ent.name;
18265
18307
  let subEntries;
@@ -18270,7 +18312,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
18270
18312
  }
18271
18313
  for (const se of subEntries) {
18272
18314
  if (!se.name.endsWith(".md")) continue;
18273
- const sePath = import_node_path9.default.join(entryPath, se.name);
18315
+ const sePath = import_node_path10.default.join(entryPath, se.name);
18274
18316
  let content;
18275
18317
  try {
18276
18318
  content = import_node_fs9.default.readFileSync(sePath, "utf8");
@@ -18306,7 +18348,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
18306
18348
  }
18307
18349
  }
18308
18350
  function readInstalledPlugins(home) {
18309
- const file = import_node_path9.default.join(home, ".claude", "plugins", "installed_plugins.json");
18351
+ const file = import_node_path10.default.join(home, ".claude", "plugins", "installed_plugins.json");
18310
18352
  let raw;
18311
18353
  try {
18312
18354
  raw = import_node_fs9.default.readFileSync(file, "utf8");
@@ -18357,14 +18399,14 @@ var SkillsScanner = class {
18357
18399
  });
18358
18400
  }
18359
18401
  const fsBlock = [];
18360
- scanSkillDir(import_node_path9.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
18361
- scanCommandDir(import_node_path9.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
18362
- scanSkillDir(import_node_path9.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
18363
- scanCommandDir(import_node_path9.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
18402
+ scanSkillDir(import_node_path10.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
18403
+ scanCommandDir(import_node_path10.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
18404
+ scanSkillDir(import_node_path10.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
18405
+ scanCommandDir(import_node_path10.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
18364
18406
  const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
18365
18407
  for (const { name, root } of plugins) {
18366
- scanSkillDir(import_node_path9.default.join(root, "skills"), "plugin", seen, fsBlock, name);
18367
- scanCommandDir(import_node_path9.default.join(root, "commands"), "plugin", seen, fsBlock, name);
18408
+ scanSkillDir(import_node_path10.default.join(root, "skills"), "plugin", seen, fsBlock, name);
18409
+ scanCommandDir(import_node_path10.default.join(root, "commands"), "plugin", seen, fsBlock, name);
18368
18410
  }
18369
18411
  fsBlock.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
18370
18412
  return [...builtinBlock, ...fsBlock];
@@ -18374,7 +18416,7 @@ var SkillsScanner = class {
18374
18416
  // src/skills/agents-scanner.ts
18375
18417
  var import_node_fs10 = __toESM(require("fs"), 1);
18376
18418
  var import_node_os6 = __toESM(require("os"), 1);
18377
- var import_node_path10 = __toESM(require("path"), 1);
18419
+ var import_node_path11 = __toESM(require("path"), 1);
18378
18420
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
18379
18421
  function isDirLikeSync2(p) {
18380
18422
  try {
@@ -18412,10 +18454,10 @@ function scanAgentsDir(dir, source, seen, out) {
18412
18454
  }
18413
18455
  for (const ent of entries) {
18414
18456
  if (!ent.name.endsWith(".md")) continue;
18415
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path10.default.join(dir, ent.name)))) {
18457
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path11.default.join(dir, ent.name)))) {
18416
18458
  continue;
18417
18459
  }
18418
- const filePath = import_node_path10.default.join(dir, ent.name);
18460
+ const filePath = import_node_path11.default.join(dir, ent.name);
18419
18461
  const baseName = ent.name.replace(/\.md$/, "");
18420
18462
  if (seen.has(baseName)) continue;
18421
18463
  seen.add(baseName);
@@ -18438,7 +18480,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
18438
18480
  return;
18439
18481
  }
18440
18482
  for (const ent of entries) {
18441
- const childPath = import_node_path10.default.join(dir, ent.name);
18483
+ const childPath = import_node_path11.default.join(dir, ent.name);
18442
18484
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
18443
18485
  walk(childPath, [...namespaces, ent.name]);
18444
18486
  continue;
@@ -18463,9 +18505,9 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
18463
18505
  walk(root, []);
18464
18506
  }
18465
18507
  function readInstalledPlugins2(home) {
18466
- const pluginsDir = import_node_path10.default.join(home, ".claude", "plugins");
18467
- const v2 = import_node_path10.default.join(pluginsDir, "installed_plugins_v2.json");
18468
- const v1 = import_node_path10.default.join(pluginsDir, "installed_plugins.json");
18508
+ const pluginsDir = import_node_path11.default.join(home, ".claude", "plugins");
18509
+ const v2 = import_node_path11.default.join(pluginsDir, "installed_plugins_v2.json");
18510
+ const v1 = import_node_path11.default.join(pluginsDir, "installed_plugins.json");
18469
18511
  let raw = null;
18470
18512
  for (const candidate of [v2, v1]) {
18471
18513
  try {
@@ -18494,19 +18536,19 @@ function readInstalledPlugins2(home) {
18494
18536
  return out;
18495
18537
  }
18496
18538
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
18497
- let cur = import_node_path10.default.resolve(startCwd);
18498
- const fsRoot = import_node_path10.default.parse(cur).root;
18539
+ let cur = import_node_path11.default.resolve(startCwd);
18540
+ const fsRoot = import_node_path11.default.parse(cur).root;
18499
18541
  while (true) {
18500
- scanAgentsDir(import_node_path10.default.join(cur, ".claude", "agents"), "project", seen, out);
18542
+ scanAgentsDir(import_node_path11.default.join(cur, ".claude", "agents"), "project", seen, out);
18501
18543
  let hasGit = false;
18502
18544
  try {
18503
- hasGit = import_node_fs10.default.existsSync(import_node_path10.default.join(cur, ".git"));
18545
+ hasGit = import_node_fs10.default.existsSync(import_node_path11.default.join(cur, ".git"));
18504
18546
  } catch {
18505
18547
  }
18506
18548
  if (hasGit) return;
18507
18549
  if (cur === home) return;
18508
18550
  if (cur === fsRoot) return;
18509
- const parent = import_node_path10.default.dirname(cur);
18551
+ const parent = import_node_path11.default.dirname(cur);
18510
18552
  if (parent === cur) return;
18511
18553
  cur = parent;
18512
18554
  }
@@ -18541,7 +18583,7 @@ var AgentsScanner = class {
18541
18583
  }
18542
18584
  const fsBlock = [];
18543
18585
  scanAgentsDir(
18544
- import_node_path10.default.join(this.home, ".claude", "agents"),
18586
+ import_node_path11.default.join(this.home, ".claude", "agents"),
18545
18587
  "global",
18546
18588
  seen,
18547
18589
  fsBlock
@@ -18555,7 +18597,7 @@ var AgentsScanner = class {
18555
18597
  ...this.extraPluginRoots
18556
18598
  ];
18557
18599
  for (const { name, root } of plugins) {
18558
- const agentsRoot = import_node_path10.default.join(root, "agents");
18600
+ const agentsRoot = import_node_path11.default.join(root, "agents");
18559
18601
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
18560
18602
  }
18561
18603
  return [...builtinBlock, ...fsBlock];
@@ -18565,7 +18607,7 @@ var AgentsScanner = class {
18565
18607
  // src/observer/session-observer.ts
18566
18608
  var import_node_fs11 = __toESM(require("fs"), 1);
18567
18609
  var import_node_os7 = __toESM(require("os"), 1);
18568
- var import_node_path11 = __toESM(require("path"), 1);
18610
+ var import_node_path12 = __toESM(require("path"), 1);
18569
18611
  init_claude_history();
18570
18612
  var SessionObserver = class {
18571
18613
  constructor(opts) {
@@ -18577,7 +18619,7 @@ var SessionObserver = class {
18577
18619
  watches = /* @__PURE__ */ new Map();
18578
18620
  resolveJsonlPath(cwd, toolSessionId, override) {
18579
18621
  if (override) return override;
18580
- return import_node_path11.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
18622
+ return import_node_path12.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
18581
18623
  }
18582
18624
  start(args) {
18583
18625
  this.stop(args.sessionId);
@@ -18597,10 +18639,10 @@ var SessionObserver = class {
18597
18639
  adapter: args.adapter
18598
18640
  };
18599
18641
  try {
18600
- import_node_fs11.default.mkdirSync(import_node_path11.default.dirname(filePath), { recursive: true });
18642
+ import_node_fs11.default.mkdirSync(import_node_path12.default.dirname(filePath), { recursive: true });
18601
18643
  } catch {
18602
18644
  }
18603
- w.watcher = import_node_fs11.default.watch(import_node_path11.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
18645
+ w.watcher = import_node_fs11.default.watch(import_node_path12.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
18604
18646
  if (!changedName || !filePath.endsWith(changedName)) return;
18605
18647
  this.poll(w);
18606
18648
  });
@@ -19116,13 +19158,18 @@ var PersonaBoundHandler = class {
19116
19158
  }
19117
19159
  return true;
19118
19160
  };
19161
+ const listenerScope = () => ({
19162
+ kind: "persona",
19163
+ personaId: scope.personaId,
19164
+ mode: "listener"
19165
+ });
19119
19166
  switch (type) {
19120
19167
  case "session:subscribe": {
19121
19168
  if (!requireScopedSession()) return;
19122
19169
  if (unsubscribe) unsubscribe();
19123
19170
  unsubscribe = this.deps.sessionManager.subscribe(
19124
19171
  scope.subSessionId,
19125
- scope.personaId,
19172
+ listenerScope(),
19126
19173
  (eventFrame) => send(eventFrame)
19127
19174
  );
19128
19175
  if (requestId)
@@ -19152,9 +19199,9 @@ var PersonaBoundHandler = class {
19152
19199
  return;
19153
19200
  }
19154
19201
  try {
19155
- this.deps.sessionManager.sendForAgent({
19202
+ this.deps.sessionManager.sendForScope({
19156
19203
  sessionId: scope.subSessionId,
19157
- agentId: scope.personaId,
19204
+ scope: listenerScope(),
19158
19205
  text
19159
19206
  });
19160
19207
  if (requestId) send({ type: "session:send", ok: true, requestId });
@@ -19167,9 +19214,9 @@ var PersonaBoundHandler = class {
19167
19214
  case "session:new": {
19168
19215
  if (!requireScopedSession()) return;
19169
19216
  try {
19170
- this.deps.sessionManager.resetForAgent({
19217
+ this.deps.sessionManager.resetForScope({
19171
19218
  sessionId: scope.subSessionId,
19172
- agentId: scope.personaId
19219
+ scope: listenerScope()
19173
19220
  });
19174
19221
  if (requestId) send({ type: "session:info", sessionId: scope.subSessionId, requestId });
19175
19222
  } catch (err) {
@@ -19183,9 +19230,9 @@ var PersonaBoundHandler = class {
19183
19230
  const limit = typeof frame.limit === "number" && frame.limit > 0 ? frame.limit : 50;
19184
19231
  const offset = typeof frame.offset === "number" && frame.offset >= 0 ? frame.offset : 0;
19185
19232
  try {
19186
- const page = this.deps.sessionManager.readHistoryPage(
19233
+ const page = this.deps.sessionManager.readHistoryPageForScope(
19187
19234
  scope.subSessionId,
19188
- scope.personaId,
19235
+ listenerScope(),
19189
19236
  limit,
19190
19237
  offset
19191
19238
  );
@@ -19316,9 +19363,9 @@ function isLocalhost(addr) {
19316
19363
 
19317
19364
  // src/discovery/state-file.ts
19318
19365
  var import_node_fs12 = __toESM(require("fs"), 1);
19319
- var import_node_path12 = __toESM(require("path"), 1);
19366
+ var import_node_path13 = __toESM(require("path"), 1);
19320
19367
  function defaultStateFilePath(dataDir) {
19321
- return import_node_path12.default.join(dataDir, "state.json");
19368
+ return import_node_path13.default.join(dataDir, "state.json");
19322
19369
  }
19323
19370
  function isPidAlive(pid) {
19324
19371
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -19354,7 +19401,7 @@ var StateFileManager = class {
19354
19401
  return { status: "stale", existing };
19355
19402
  }
19356
19403
  write(state) {
19357
- import_node_fs12.default.mkdirSync(import_node_path12.default.dirname(this.file), { recursive: true });
19404
+ import_node_fs12.default.mkdirSync(import_node_path13.default.dirname(this.file), { recursive: true });
19358
19405
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
19359
19406
  import_node_fs12.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
19360
19407
  import_node_fs12.default.renameSync(tmp, this.file);
@@ -19375,13 +19422,13 @@ var StateFileManager = class {
19375
19422
 
19376
19423
  // src/tunnel/tunnel-manager.ts
19377
19424
  var import_node_fs15 = __toESM(require("fs"), 1);
19378
- var import_node_path15 = __toESM(require("path"), 1);
19425
+ var import_node_path16 = __toESM(require("path"), 1);
19379
19426
  var import_node_crypto4 = __toESM(require("crypto"), 1);
19380
19427
  var import_node_child_process4 = require("child_process");
19381
19428
 
19382
19429
  // src/tunnel/tunnel-store.ts
19383
19430
  var import_node_fs13 = __toESM(require("fs"), 1);
19384
- var import_node_path13 = __toESM(require("path"), 1);
19431
+ var import_node_path14 = __toESM(require("path"), 1);
19385
19432
  var TunnelStore = class {
19386
19433
  constructor(filePath) {
19387
19434
  this.filePath = filePath;
@@ -19400,7 +19447,7 @@ var TunnelStore = class {
19400
19447
  }
19401
19448
  }
19402
19449
  async set(v) {
19403
- const dir = import_node_path13.default.dirname(this.filePath);
19450
+ const dir = import_node_path14.default.dirname(this.filePath);
19404
19451
  await import_node_fs13.default.promises.mkdir(dir, { recursive: true });
19405
19452
  const data = JSON.stringify(v, null, 2);
19406
19453
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
@@ -19512,7 +19559,7 @@ function escape(v) {
19512
19559
  // src/tunnel/frpc-binary.ts
19513
19560
  var import_node_fs14 = __toESM(require("fs"), 1);
19514
19561
  var import_node_os8 = __toESM(require("os"), 1);
19515
- var import_node_path14 = __toESM(require("path"), 1);
19562
+ var import_node_path15 = __toESM(require("path"), 1);
19516
19563
  var import_node_child_process3 = require("child_process");
19517
19564
  var import_node_stream = require("stream");
19518
19565
  var import_promises = require("stream/promises");
@@ -19551,13 +19598,13 @@ async function ensureFrpcBinary(opts) {
19551
19598
  }
19552
19599
  const version2 = opts.version ?? FRPC_VERSION;
19553
19600
  const platform = opts.platform ?? detectPlatform();
19554
- const binDir = import_node_path14.default.join(opts.dataDir, "bin");
19601
+ const binDir = import_node_path15.default.join(opts.dataDir, "bin");
19555
19602
  import_node_fs14.default.mkdirSync(binDir, { recursive: true });
19556
19603
  cleanupStaleArtifacts(binDir);
19557
- const stableBin = import_node_path14.default.join(binDir, "frpc");
19604
+ const stableBin = import_node_path15.default.join(binDir, "frpc");
19558
19605
  if (import_node_fs14.default.existsSync(stableBin)) return stableBin;
19559
19606
  const partialBin = `${stableBin}.partial`;
19560
- const tarballPath = import_node_path14.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
19607
+ const tarballPath = import_node_path15.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
19561
19608
  try {
19562
19609
  const url = frpcDownloadUrl(version2, platform);
19563
19610
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -19583,7 +19630,7 @@ function cleanupStaleArtifacts(binDir) {
19583
19630
  }
19584
19631
  for (const name of entries) {
19585
19632
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
19586
- const full = import_node_path14.default.join(binDir, name);
19633
+ const full = import_node_path15.default.join(binDir, name);
19587
19634
  try {
19588
19635
  import_node_fs14.default.rmSync(full, { recursive: true, force: true });
19589
19636
  } catch {
@@ -19609,7 +19656,7 @@ async function downloadToFile(url, dest, fetchImpl) {
19609
19656
  await (0, import_promises.pipeline)(nodeStream, out);
19610
19657
  }
19611
19658
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
19612
- const work = import_node_path14.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
19659
+ const work = import_node_path15.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
19613
19660
  import_node_fs14.default.mkdirSync(work, { recursive: true });
19614
19661
  try {
19615
19662
  await new Promise((resolve2, reject) => {
@@ -19618,7 +19665,7 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
19618
19665
  proc.on("exit", (code) => code === 0 ? resolve2() : reject(new Error(`tar exited ${code}`)));
19619
19666
  });
19620
19667
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
19621
- const src = import_node_path14.default.join(work, dirName, "frpc");
19668
+ const src = import_node_path15.default.join(work, dirName, "frpc");
19622
19669
  if (!import_node_fs14.default.existsSync(src)) {
19623
19670
  throw new Error(`frpc not found inside tarball at ${src}`);
19624
19671
  }
@@ -19633,7 +19680,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
19633
19680
  var TunnelManager = class {
19634
19681
  constructor(deps) {
19635
19682
  this.deps = deps;
19636
- this.store = deps.store ?? new TunnelStore(import_node_path15.default.join(deps.dataDir, "tunnel.json"));
19683
+ this.store = deps.store ?? new TunnelStore(import_node_path16.default.join(deps.dataDir, "tunnel.json"));
19637
19684
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
19638
19685
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
19639
19686
  }
@@ -19750,7 +19797,7 @@ var TunnelManager = class {
19750
19797
  dataDir: this.deps.dataDir,
19751
19798
  override: this.deps.frpcBinaryOverride ?? void 0
19752
19799
  });
19753
- const tomlPath = import_node_path15.default.join(this.deps.dataDir, "frpc.toml");
19800
+ const tomlPath = import_node_path16.default.join(this.deps.dataDir, "frpc.toml");
19754
19801
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto4.default.randomBytes(3).toString("hex")}`;
19755
19802
  const toml = buildFrpcToml({
19756
19803
  serverAddr: t.frpsHost,
@@ -19765,7 +19812,7 @@ var TunnelManager = class {
19765
19812
  const proc = (this.deps.spawnImpl ?? import_node_child_process4.spawn)(frpcBin, ["-c", tomlPath], {
19766
19813
  stdio: ["ignore", "pipe", "pipe"]
19767
19814
  });
19768
- const logFilePath = import_node_path15.default.join(this.deps.dataDir, "frpc.log");
19815
+ const logFilePath = import_node_path16.default.join(this.deps.dataDir, "frpc.log");
19769
19816
  const logStream = import_node_fs15.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
19770
19817
  logStream.on("error", () => {
19771
19818
  });
@@ -19859,11 +19906,11 @@ function deriveStableDeviceKey(opts = {}) {
19859
19906
 
19860
19907
  // src/auth-store.ts
19861
19908
  var import_node_fs16 = __toESM(require("fs"), 1);
19862
- var import_node_path16 = __toESM(require("path"), 1);
19909
+ var import_node_path17 = __toESM(require("path"), 1);
19863
19910
  var import_node_crypto6 = __toESM(require("crypto"), 1);
19864
19911
  var AUTH_FILE_NAME = "auth.json";
19865
19912
  function authFilePath(dataDir) {
19866
- return import_node_path16.default.join(dataDir, AUTH_FILE_NAME);
19913
+ return import_node_path17.default.join(dataDir, AUTH_FILE_NAME);
19867
19914
  }
19868
19915
  function loadOrCreateAuthToken(opts) {
19869
19916
  const file = authFilePath(opts.dataDir);
@@ -19895,7 +19942,7 @@ function readAuthFile(file) {
19895
19942
  }
19896
19943
  }
19897
19944
  function writeAuthFile(file, content) {
19898
- import_node_fs16.default.mkdirSync(import_node_path16.default.dirname(file), { recursive: true });
19945
+ import_node_fs16.default.mkdirSync(import_node_path17.default.dirname(file), { recursive: true });
19899
19946
  import_node_fs16.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
19900
19947
  try {
19901
19948
  import_node_fs16.default.chmodSync(file, 384);
@@ -19906,11 +19953,11 @@ function writeAuthFile(file, content) {
19906
19953
  // src/owner-profile.ts
19907
19954
  var import_node_fs17 = __toESM(require("fs"), 1);
19908
19955
  var import_node_os10 = __toESM(require("os"), 1);
19909
- var import_node_path17 = __toESM(require("path"), 1);
19956
+ var import_node_path18 = __toESM(require("path"), 1);
19910
19957
  var PROFILE_FILENAME = "profile.json";
19911
19958
  function loadOwnerDisplayName(dataDir) {
19912
19959
  const fallback = import_node_os10.default.userInfo().username;
19913
- const profilePath = import_node_path17.default.join(dataDir, PROFILE_FILENAME);
19960
+ const profilePath = import_node_path18.default.join(dataDir, PROFILE_FILENAME);
19914
19961
  let raw;
19915
19962
  try {
19916
19963
  raw = import_node_fs17.default.readFileSync(profilePath, "utf8");
@@ -19944,7 +19991,7 @@ init_protocol();
19944
19991
  // src/session/fork.ts
19945
19992
  var import_node_fs18 = __toESM(require("fs"), 1);
19946
19993
  var import_node_os11 = __toESM(require("os"), 1);
19947
- var import_node_path18 = __toESM(require("path"), 1);
19994
+ var import_node_path19 = __toESM(require("path"), 1);
19948
19995
  init_claude_history();
19949
19996
  function readJsonlEntries(file) {
19950
19997
  const raw = import_node_fs18.default.readFileSync(file, "utf8");
@@ -19960,9 +20007,9 @@ function readJsonlEntries(file) {
19960
20007
  return out;
19961
20008
  }
19962
20009
  function forkSession(input) {
19963
- const baseDir = input.baseDir ?? import_node_path18.default.join(import_node_os11.default.homedir(), ".claude");
19964
- const projectDir = import_node_path18.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
19965
- const sourceFile = import_node_path18.default.join(projectDir, `${input.toolSessionId}.jsonl`);
20010
+ const baseDir = input.baseDir ?? import_node_path19.default.join(import_node_os11.default.homedir(), ".claude");
20011
+ const projectDir = import_node_path19.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
20012
+ const sourceFile = import_node_path19.default.join(projectDir, `${input.toolSessionId}.jsonl`);
19966
20013
  if (!import_node_fs18.default.existsSync(sourceFile)) {
19967
20014
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
19968
20015
  }
@@ -19993,7 +20040,7 @@ function forkSession(input) {
19993
20040
  }
19994
20041
  forkedLines.push(JSON.stringify(forked));
19995
20042
  }
19996
- const forkedFilePath = import_node_path18.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
20043
+ const forkedFilePath = import_node_path19.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
19997
20044
  import_node_fs18.default.mkdirSync(projectDir, { recursive: true });
19998
20045
  import_node_fs18.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
19999
20046
  return { forkedToolSessionId, forkedFilePath };
@@ -20285,7 +20332,7 @@ init_protocol();
20285
20332
  var import_node_child_process5 = require("child_process");
20286
20333
  var import_node_fs19 = __toESM(require("fs"), 1);
20287
20334
  var import_node_os12 = __toESM(require("os"), 1);
20288
- var import_node_path19 = __toESM(require("path"), 1);
20335
+ var import_node_path20 = __toESM(require("path"), 1);
20289
20336
  var import_node_util = require("util");
20290
20337
  var pexec = (0, import_node_util.promisify)(import_node_child_process5.execFile);
20291
20338
  function formatChildProcessError(err) {
@@ -20300,7 +20347,7 @@ function formatChildProcessError(err) {
20300
20347
  return e.message ?? "unknown error";
20301
20348
  }
20302
20349
  function normalizePath(p) {
20303
- const resolved = import_node_path19.default.resolve(p);
20350
+ const resolved = import_node_path20.default.resolve(p);
20304
20351
  try {
20305
20352
  return import_node_fs19.default.realpathSync(resolved);
20306
20353
  } catch {
@@ -20403,13 +20450,13 @@ function flattenToDirName(branch) {
20403
20450
  }
20404
20451
  function encodeClaudeProjectDir(absPath) {
20405
20452
  if (!absPath || typeof absPath !== "string") return "";
20406
- let canonical = import_node_path19.default.resolve(absPath);
20453
+ let canonical = import_node_path20.default.resolve(absPath);
20407
20454
  try {
20408
20455
  canonical = import_node_fs19.default.realpathSync(canonical);
20409
20456
  } catch {
20410
20457
  try {
20411
- const parent = import_node_fs19.default.realpathSync(import_node_path19.default.dirname(canonical));
20412
- canonical = import_node_path19.default.join(parent, import_node_path19.default.basename(canonical));
20458
+ const parent = import_node_fs19.default.realpathSync(import_node_path20.default.dirname(canonical));
20459
+ canonical = import_node_path20.default.join(parent, import_node_path20.default.basename(canonical));
20413
20460
  } catch {
20414
20461
  }
20415
20462
  }
@@ -20433,11 +20480,11 @@ async function createWorktree(input) {
20433
20480
  if (!isGitRoot) {
20434
20481
  throw new Error(`\u76EE\u5F55 ${cwd} \u4E0D\u662F git repo \u6839`);
20435
20482
  }
20436
- const parent = import_node_path19.default.dirname(import_node_path19.default.resolve(cwd));
20437
- if (parent === "/" || parent === import_node_path19.default.resolve(cwd)) {
20483
+ const parent = import_node_path20.default.dirname(import_node_path20.default.resolve(cwd));
20484
+ if (parent === "/" || parent === import_node_path20.default.resolve(cwd)) {
20438
20485
  throw new Error("repo \u5728\u78C1\u76D8\u6839\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5728\u540C\u7EA7\u521B\u5EFA worktree");
20439
20486
  }
20440
- const worktreeRoot = import_node_path19.default.join(parent, dirName);
20487
+ const worktreeRoot = import_node_path20.default.join(parent, dirName);
20441
20488
  try {
20442
20489
  await pexec("git", ["-C", cwd, "fetch", "origin", baseBranch, "--no-tags"], {
20443
20490
  timeout: 3e4
@@ -20484,8 +20531,8 @@ async function removeWorktree(input) {
20484
20531
  );
20485
20532
  const gitCommonDir = stdout.trim();
20486
20533
  if (!gitCommonDir) throw new Error("empty git-common-dir");
20487
- const absGitCommon = import_node_path19.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path19.default.resolve(worktreeRoot, gitCommonDir);
20488
- repoRoot = import_node_path19.default.dirname(absGitCommon);
20534
+ const absGitCommon = import_node_path20.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path20.default.resolve(worktreeRoot, gitCommonDir);
20535
+ repoRoot = import_node_path20.default.dirname(absGitCommon);
20489
20536
  } catch {
20490
20537
  repoRoot = null;
20491
20538
  }
@@ -20516,9 +20563,9 @@ async function removeWorktree(input) {
20516
20563
  try {
20517
20564
  const encoded = encodeClaudeProjectDir(worktreeRoot);
20518
20565
  if (encoded) {
20519
- const projectsRoot = import_node_path19.default.join(import_node_os12.default.homedir(), ".claude", "projects");
20520
- const target = import_node_path19.default.resolve(projectsRoot, encoded);
20521
- if (target.startsWith(projectsRoot + import_node_path19.default.sep) && target !== projectsRoot) {
20566
+ const projectsRoot = import_node_path20.default.join(import_node_os12.default.homedir(), ".claude", "projects");
20567
+ const target = import_node_path20.default.resolve(projectsRoot, encoded);
20568
+ if (target.startsWith(projectsRoot + import_node_path20.default.sep) && target !== projectsRoot) {
20522
20569
  import_node_fs19.default.rmSync(target, { recursive: true, force: true });
20523
20570
  }
20524
20571
  }
@@ -20687,7 +20734,7 @@ function buildPersonaHandlers(deps) {
20687
20734
  };
20688
20735
  const listSubSessions = async (frame) => {
20689
20736
  const args = PersonaIdArgsSchema.parse(frame);
20690
- const sessions = sessionManager.listForAgent(args.personaId);
20737
+ const sessions = sessionManager.listPersonaSessions(args.personaId, "listener");
20691
20738
  return {
20692
20739
  response: { type: "persona:subSessions", sessions }
20693
20740
  };
@@ -20734,7 +20781,7 @@ function buildMethodHandlers(deps) {
20734
20781
  async function startDaemon(config) {
20735
20782
  const logger = createLogger({
20736
20783
  level: config.logLevel,
20737
- file: import_node_path20.default.join(config.dataDir, "clawd.log")
20784
+ file: import_node_path21.default.join(config.dataDir, "clawd.log")
20738
20785
  });
20739
20786
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
20740
20787
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -20770,7 +20817,7 @@ async function startDaemon(config) {
20770
20817
  const agents = new AgentsScanner();
20771
20818
  const history = new ClaudeHistoryReader();
20772
20819
  let transport = null;
20773
- const personaStore = new PersonaStore(import_node_path20.default.join(config.dataDir, "personas"));
20820
+ const personaStore = new PersonaStore(import_node_path21.default.join(config.dataDir, "personas"));
20774
20821
  const defaultsRoot = findDefaultsRoot();
20775
20822
  if (defaultsRoot) {
20776
20823
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -20784,7 +20831,7 @@ async function startDaemon(config) {
20784
20831
  getAdapter,
20785
20832
  historyReader: history,
20786
20833
  dataDir: config.dataDir,
20787
- personaRoot: import_node_path20.default.join(config.dataDir, "personas"),
20834
+ personaRoot: import_node_path21.default.join(config.dataDir, "personas"),
20788
20835
  personaStore,
20789
20836
  ownerDisplayName,
20790
20837
  broadcastFrame: (frame, target) => {
@@ -20958,8 +21005,8 @@ async function startDaemon(config) {
20958
21005
  const lines = [
20959
21006
  `Tunnel: ${r.url}`,
20960
21007
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
20961
- `Frpc config: ${import_node_path20.default.join(config.dataDir, "frpc.toml")}`,
20962
- `Frpc log: ${import_node_path20.default.join(config.dataDir, "frpc.log")}`
21008
+ `Frpc config: ${import_node_path21.default.join(config.dataDir, "frpc.toml")}`,
21009
+ `Frpc log: ${import_node_path21.default.join(config.dataDir, "frpc.log")}`
20963
21010
  ];
20964
21011
  const width = Math.max(...lines.map((l) => l.length));
20965
21012
  const bar = "\u2550".repeat(width + 4);
@@ -20972,7 +21019,7 @@ ${bar}
20972
21019
 
20973
21020
  `);
20974
21021
  try {
20975
- const connectPath = import_node_path20.default.join(config.dataDir, "connect.txt");
21022
+ const connectPath = import_node_path21.default.join(config.dataDir, "connect.txt");
20976
21023
  import_node_fs20.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
20977
21024
  } catch {
20978
21025
  }