@clawos-dev/clawd 0.2.287 → 0.2.288

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -7376,7 +7376,17 @@ var init_schemas = __esm({
7376
7376
  SessionForkArgs = external_exports.object({
7377
7377
  cwd: external_exports.string().min(1),
7378
7378
  toolSessionId: external_exports.string().min(1),
7379
- messageUuid: external_exports.string().min(1)
7379
+ messageUuid: external_exports.string().min(1),
7380
+ /**
7381
+ * 派生 jsonl 的落点 cwd(缺省 = 源文件所在目录,保持老行为)。
7382
+ *
7383
+ * 必须传的场景:调用方随后要用这条 transcript 建 session,而那个 session 的 cwd 与
7384
+ * 源 session 不同——最典型是 persona session(daemon 按 ownerPersonaId 把 cwd 强制
7385
+ * 派生成 persona 根)。CC `--resume` 只在**当前 cwd 的** projects 目录里找 session,
7386
+ * 落点与目标 cwd 分家 = 历史读不到 + resume 接不上(bug: quick-ask-blank-after-worktree)。
7387
+ * 正确编排:先 sessionCreate 拿到 daemon 定的真实 cwd,再以它作 targetCwd fork。
7388
+ */
7389
+ targetCwd: external_exports.string().min(1).optional()
7380
7390
  });
7381
7391
  SessionForkResponseSchema = external_exports.object({
7382
7392
  forkedToolSessionId: external_exports.string().min(1),
@@ -13968,6 +13978,26 @@ var init_claude_history = __esm({
13968
13978
  this.projectsRoot = import_node_path5.default.join(base, "projects");
13969
13979
  this.fileHistoryRoot = import_node_path5.default.join(base, "file-history");
13970
13980
  }
13981
+ /**
13982
+ * [T-17] 定位一条 session 的 transcript 所在 project 目录,带 relocation 兜底。
13983
+ *
13984
+ * 常规路径:`<projectsRoot>/cwdToHashDir(cwd)`。但 CC 中途改 cwd(典型:会话里
13985
+ * EnterWorktree)会把 `<tsid>.jsonl` 挪到新 hash 目录,而调用方手里的 cwd 可能仍是老值——
13986
+ * persona session 尤其顽固:fork 出的新 session 因带 ownerPersonaId 被 manager 强制
13987
+ * 改写 cwd 回 persona 根(session/manager.ts derivePersonaSpawnCwd),与 jsonl 实际
13988
+ * 落点(源文件所在目录,见 session/fork.ts)永久分家。
13989
+ * 拼不出路径时按 tsid 全局兜底(与 fork.ts / observer 同款),命中则用它所在目录。
13990
+ *
13991
+ * 兜底失败仍返回按 cwd 算的目录:下游 readJsonlLines / readdir 都容忍不存在,
13992
+ * 保持"查无此 session 返回空"的既有语义,不抛。
13993
+ * bug: quick-ask-blank-after-worktree(fork 出的快速问答面板整片空白)
13994
+ */
13995
+ resolveSessionDir(cwd, toolSessionId) {
13996
+ const byCwd = import_node_path5.default.join(this.projectsRoot, cwdToHashDir(cwd));
13997
+ if (import_node_fs6.default.existsSync(import_node_path5.default.join(byCwd, `${toolSessionId}.jsonl`))) return byCwd;
13998
+ const found = findTranscriptByToolSessionId(this.projectsRoot, toolSessionId);
13999
+ return found ? import_node_path5.default.dirname(found) : byCwd;
14000
+ }
13971
14001
  async listProjects() {
13972
14002
  let entries;
13973
14003
  try {
@@ -14059,8 +14089,7 @@ var init_claude_history = __esm({
14059
14089
  }
14060
14090
  async read(args) {
14061
14091
  const file = import_node_path5.default.join(
14062
- this.projectsRoot,
14063
- cwdToHashDir(args.cwd),
14092
+ this.resolveSessionDir(args.cwd, args.toolSessionId),
14064
14093
  `${args.toolSessionId}.jsonl`
14065
14094
  );
14066
14095
  const lines = readJsonlLines(file);
@@ -14096,8 +14125,7 @@ var init_claude_history = __esm({
14096
14125
  // 返回 null 表示目录不存在(调用方回退旧实现);返回空数组表示目录存在但无 jsonl
14097
14126
  listSubagentsFromDirectory(cwd, toolSessionId) {
14098
14127
  const dir = import_node_path5.default.join(
14099
- this.projectsRoot,
14100
- cwdToHashDir(cwd),
14128
+ this.resolveSessionDir(cwd, toolSessionId),
14101
14129
  toolSessionId,
14102
14130
  "subagents"
14103
14131
  );
@@ -14131,8 +14159,7 @@ var init_claude_history = __esm({
14131
14159
  }
14132
14160
  listSubagentsFromMainJsonl(cwd, toolSessionId) {
14133
14161
  const file = import_node_path5.default.join(
14134
- this.projectsRoot,
14135
- cwdToHashDir(cwd),
14162
+ this.resolveSessionDir(cwd, toolSessionId),
14136
14163
  `${toolSessionId}.jsonl`
14137
14164
  );
14138
14165
  const lines = readJsonlLines(file);
@@ -14166,8 +14193,7 @@ var init_claude_history = __esm({
14166
14193
  // 独立文件路径:agent-<subagentId>.jsonl;文件不存在返回 null 让调用方回退旧实现
14167
14194
  readSubagentFromFile(cwd, toolSessionId, subagentId) {
14168
14195
  const file = import_node_path5.default.join(
14169
- this.projectsRoot,
14170
- cwdToHashDir(cwd),
14196
+ this.resolveSessionDir(cwd, toolSessionId),
14171
14197
  toolSessionId,
14172
14198
  "subagents",
14173
14199
  `agent-${subagentId}.jsonl`
@@ -14194,8 +14220,7 @@ var init_claude_history = __esm({
14194
14220
  */
14195
14221
  readFileHistorySnapshots(args) {
14196
14222
  const file = import_node_path5.default.join(
14197
- this.projectsRoot,
14198
- cwdToHashDir(args.cwd),
14223
+ this.resolveSessionDir(args.cwd, args.toolSessionId),
14199
14224
  `${args.toolSessionId}.jsonl`
14200
14225
  );
14201
14226
  const lines = readJsonlLines(file);
@@ -14326,8 +14351,7 @@ var init_claude_history = __esm({
14326
14351
  }
14327
14352
  readSubagentFromMainJsonl(cwd, toolSessionId, subagentId) {
14328
14353
  const file = import_node_path5.default.join(
14329
- this.projectsRoot,
14330
- cwdToHashDir(cwd),
14354
+ this.resolveSessionDir(cwd, toolSessionId),
14331
14355
  `${toolSessionId}.jsonl`
14332
14356
  );
14333
14357
  const lines = readJsonlLines(file);
@@ -46020,6 +46044,14 @@ var SessionManager = class {
46020
46044
  },
46021
46045
  this.deps.personaRoot
46022
46046
  );
46047
+ if (args.forkedFromSessionId && args.cwd && args.cwd !== cwd) {
46048
+ this.deps.logger?.info("session-create.fork-cwd-rewritten", {
46049
+ argCwd: args.cwd,
46050
+ derivedCwd: cwd,
46051
+ ownerPersonaId: args.ownerPersonaId,
46052
+ forkedFromSessionId: args.forkedFromSessionId
46053
+ });
46054
+ }
46023
46055
  } else if (args.cwd) {
46024
46056
  cwd = args.cwd;
46025
46057
  } else {
@@ -57029,8 +57061,9 @@ function forkSession(input) {
57029
57061
  }
57030
57062
  forkedLines.push(JSON.stringify(forked));
57031
57063
  }
57032
- const forkedFilePath = import_node_path48.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
57033
- import_node_fs43.default.mkdirSync(projectDir, { recursive: true });
57064
+ const outDir = input.targetCwd ? import_node_path48.default.join(baseDir, "projects", cwdToHashDir(input.targetCwd)) : projectDir;
57065
+ const forkedFilePath = import_node_path48.default.join(outDir, `${forkedToolSessionId}.jsonl`);
57066
+ import_node_fs43.default.mkdirSync(outDir, { recursive: true });
57034
57067
  import_node_fs43.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
57035
57068
  return { forkedToolSessionId, forkedFilePath };
57036
57069
  }
@@ -57255,9 +57288,25 @@ function buildSessionHandlers(deps) {
57255
57288
  response: { type: "session:rewindable-message-ids", ...response }
57256
57289
  };
57257
57290
  };
57258
- const fork = async (frame) => {
57291
+ const fork = async (frame, _client, ctx) => {
57259
57292
  const args = SessionForkArgs.parse(frame);
57293
+ if (ctx && ctx.principal.kind === "guest") {
57294
+ const src = store.list().find((s) => s.toolSessionId === args.toolSessionId);
57295
+ const ok = src != null && canAccessPersona(ctx.grants, src.ownerPersonaId, "read") && src.creatorPrincipalId === ctx.principal.id;
57296
+ if (!ok) {
57297
+ throw new ClawdError(
57298
+ ERROR_CODES.UNAUTHORIZED,
57299
+ `principal guest:${ctx.principal.id} cannot fork tool session ${args.toolSessionId}`
57300
+ );
57301
+ }
57302
+ }
57260
57303
  const result = forkSession(args);
57304
+ deps.logger?.info("session-fork.done", {
57305
+ argCwd: args.cwd,
57306
+ sourceToolSessionId: args.toolSessionId,
57307
+ forkedToolSessionId: result.forkedToolSessionId,
57308
+ forkedFilePath: result.forkedFilePath
57309
+ });
57261
57310
  return { response: { type: "session:fork", ...result } };
57262
57311
  };
57263
57312
  const newSession = async (frame, _client, ctx) => {
@@ -57635,15 +57684,28 @@ function buildHistoryHandlers(deps) {
57635
57684
  const messages = args.slim ? slimHistoryMessages(res.messages) : res.messages;
57636
57685
  return { response: { type: "history:read", ...res, messages } };
57637
57686
  };
57687
+ const assertGuestOwnsToolSession = (ctx, toolSessionId, method) => {
57688
+ if (!ctx || ctx.principal.kind !== "guest") return;
57689
+ const f = store.list().find((s) => s.toolSessionId === toolSessionId);
57690
+ const ok = f != null && canAccessPersona(ctx.grants, f.ownerPersonaId, "read") && f.creatorPrincipalId === ctx.principal.id;
57691
+ if (!ok) {
57692
+ throw new ClawdError(
57693
+ ERROR_CODES.UNAUTHORIZED,
57694
+ `principal guest:${ctx.principal.id} cannot ${method} of tool session ${toolSessionId}`
57695
+ );
57696
+ }
57697
+ };
57638
57698
  const subagents = async (frame, _client, ctx) => {
57639
57699
  const args = HistorySubagentsArgs.parse(frame);
57640
57700
  assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
57701
+ assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagents");
57641
57702
  const subs = await history.listSubagents(args);
57642
57703
  return { response: { type: "history:subagents", subagents: subs } };
57643
57704
  };
57644
57705
  const subagentRead = async (frame, _client, ctx) => {
57645
57706
  const args = HistorySubagentReadArgs.parse(frame);
57646
57707
  assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
57708
+ assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagent-read");
57647
57709
  const res = await history.readSubagent(args);
57648
57710
  return { response: { type: "history:subagent-read", ...res } };
57649
57711
  };
@@ -58712,7 +58774,7 @@ function computeMethodAccess(args) {
58712
58774
  }
58713
58775
 
58714
58776
  // src/version.ts
58715
- var version = "0.2.287".length > 0 ? "0.2.287" : "dev";
58777
+ var version = "0.2.288".length > 0 ? "0.2.288" : "dev";
58716
58778
 
58717
58779
  // src/cli-probe/probe.ts
58718
58780
  var fs55 = __toESM(require("fs"), 1);
@@ -22520,7 +22520,17 @@ var SessionResumeArgs = external_exports.object({
22520
22520
  var SessionForkArgs = external_exports.object({
22521
22521
  cwd: external_exports.string().min(1),
22522
22522
  toolSessionId: external_exports.string().min(1),
22523
- messageUuid: external_exports.string().min(1)
22523
+ messageUuid: external_exports.string().min(1),
22524
+ /**
22525
+ * 派生 jsonl 的落点 cwd(缺省 = 源文件所在目录,保持老行为)。
22526
+ *
22527
+ * 必须传的场景:调用方随后要用这条 transcript 建 session,而那个 session 的 cwd 与
22528
+ * 源 session 不同——最典型是 persona session(daemon 按 ownerPersonaId 把 cwd 强制
22529
+ * 派生成 persona 根)。CC `--resume` 只在**当前 cwd 的** projects 目录里找 session,
22530
+ * 落点与目标 cwd 分家 = 历史读不到 + resume 接不上(bug: quick-ask-blank-after-worktree)。
22531
+ * 正确编排:先 sessionCreate 拿到 daemon 定的真实 cwd,再以它作 targetCwd fork。
22532
+ */
22533
+ targetCwd: external_exports.string().min(1).optional()
22524
22534
  });
22525
22535
  var SessionForkResponseSchema = external_exports.object({
22526
22536
  forkedToolSessionId: external_exports.string().min(1),