@clawos-dev/clawd 0.2.10-beta.7.d4b8b64 → 0.2.11-beta.8.3707c2b

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 +160 -15
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -12448,6 +12448,24 @@ var ParsedEventSchema = external_exports.discriminatedUnion("kind", [
12448
12448
  ...ParsedEventBase,
12449
12449
  kind: external_exports.literal("turn_end")
12450
12450
  }),
12451
+ // SDK system 帧 task_started / task_progress / task_notification 的归一化产物:
12452
+ // 子 agent 执行过程中实时上报 agentId / lastToolName / stats,UI pending 期就能开抽屉、看进度。
12453
+ // toolUseId 对齐父 Agent 工具的 tool_use_id;status 由 SDK subtype 派生。
12454
+ external_exports.object({
12455
+ ...ParsedEventBase,
12456
+ kind: external_exports.literal("subagent_progress"),
12457
+ toolUseId: external_exports.string(),
12458
+ agentId: external_exports.string(),
12459
+ status: external_exports.enum(["started", "running", "completed", "failed"]),
12460
+ description: external_exports.string().optional(),
12461
+ lastToolName: external_exports.string().optional(),
12462
+ prompt: external_exports.string().optional(),
12463
+ stats: external_exports.object({
12464
+ durationMs: external_exports.number().int().nonnegative().optional(),
12465
+ tokens: external_exports.number().int().nonnegative().optional(),
12466
+ toolUseCount: external_exports.number().int().nonnegative().optional()
12467
+ }).optional()
12468
+ }),
12451
12469
  external_exports.object({
12452
12470
  ...ParsedEventBase,
12453
12471
  kind: external_exports.literal("error"),
@@ -13559,6 +13577,12 @@ var SessionManager = class {
13559
13577
  currentCollector = null;
13560
13578
  // tool id → capabilities 快照(仅 capabilities:get RPC 缓存用;contextWindowSize 走 adapter.resolveContextWindow,无需缓存)
13561
13579
  capabilitiesCache = /* @__PURE__ */ new Map();
13580
+ // sessionId → (synth uuid → real uuid):
13581
+ // synth uuid = daemon 在 reducer 'send' 命令里生成、写进 buffer 里 user_text 事件的 uuid
13582
+ // real uuid = CC 写 jsonl 时给 user 行分配的 uuid(也是 file-history-snapshot.messageId 的来源)
13583
+ // 由 observer 监听 jsonl user 行后调 recordRealUserUuid 建立映射;rewind 系列 RPC 在
13584
+ // 入参 / 出参做转译,保证 UI 看到的 uuid 始终是 events 流里的 synth uuid
13585
+ realUuidBySynth = /* @__PURE__ */ new Map();
13562
13586
  async getCapabilities(tool) {
13563
13587
  const cached = this.capabilitiesCache.get(tool);
13564
13588
  if (cached) return cached;
@@ -13658,6 +13682,53 @@ var SessionManager = class {
13658
13682
  const file = this.getFile(args.sessionId);
13659
13683
  return { response: file, broadcast: [] };
13660
13684
  }
13685
+ // observer 上报"jsonl 写入了一条 user 行"时调用:在 buffer 里找到与之对应的、
13686
+ // 还没建立映射的最早一条 synth user_text,记下 synth → real 映射。
13687
+ // 找不到匹配的 synth(runner 不存在 / buffer 已被 /clear 清空 / 真 uuid 来自纯 history
13688
+ // 重启后的 session)→ 静默跳过;rewindable 集合稍后在 RPC 出口走 fallback 路径
13689
+ recordRealUserUuid(args) {
13690
+ const runner = this.runners.get(args.sessionId);
13691
+ if (!runner) return;
13692
+ let map = this.realUuidBySynth.get(args.sessionId);
13693
+ if (map && map.has(args.realUuid)) return;
13694
+ if (map) {
13695
+ for (const v of map.values()) if (v === args.realUuid) return;
13696
+ }
13697
+ const buffer = runner.getState().buffer;
13698
+ let matchedSynth = null;
13699
+ for (const seqd of buffer) {
13700
+ const ev = seqd.event;
13701
+ if (ev.kind !== "user_text") continue;
13702
+ if (ev.text !== args.text) continue;
13703
+ if (typeof ev.uuid !== "string") continue;
13704
+ if (map && map.has(ev.uuid)) continue;
13705
+ matchedSynth = ev.uuid;
13706
+ break;
13707
+ }
13708
+ if (!matchedSynth) return;
13709
+ if (!map) {
13710
+ map = /* @__PURE__ */ new Map();
13711
+ this.realUuidBySynth.set(args.sessionId, map);
13712
+ }
13713
+ map.set(matchedSynth, args.realUuid);
13714
+ }
13715
+ // 把 UI 传来的 uuid(events 流里的 synth uuid)转成 jsonl / file-history 用的 real uuid。
13716
+ // 没有映射时返回原值——history-only session(buffer 为空)的 uuid 本来就来自 jsonl
13717
+ // 真 uuid,UI 端 line.uuid 也是真 uuid,不需要转译
13718
+ toRealUuid(sessionId, uuid) {
13719
+ const map = this.realUuidBySynth.get(sessionId);
13720
+ if (!map) return uuid;
13721
+ return map.get(uuid) ?? uuid;
13722
+ }
13723
+ // file-history reader 拿到的是 real uuid 集合,反查映射输出给 UI 的 synth uuid。
13724
+ // 没建立映射的 real uuid 原样输出(同上:history-only session 的 line.uuid 是 real uuid)
13725
+ toUiUuids(sessionId, realUuids) {
13726
+ const map = this.realUuidBySynth.get(sessionId);
13727
+ if (!map) return realUuids;
13728
+ const reverse = /* @__PURE__ */ new Map();
13729
+ for (const [synth, real] of map) reverse.set(real, synth);
13730
+ return realUuids.map((r) => reverse.get(r) ?? r);
13731
+ }
13661
13732
  // 内部帮手:不走 ManagerResult 的 get,直接拿 SessionFile
13662
13733
  getFile(sessionId) {
13663
13734
  const runner = this.runners.get(sessionId);
@@ -13688,6 +13759,7 @@ var SessionManager = class {
13688
13759
  runner.input({ kind: "command", command: { kind: "delete" } });
13689
13760
  });
13690
13761
  this.runners.delete(args.sessionId);
13762
+ this.realUuidBySynth.delete(args.sessionId);
13691
13763
  return { response: { sessionId: args.sessionId }, broadcast };
13692
13764
  }
13693
13765
  this.deps.store.delete(args.sessionId);
@@ -13753,7 +13825,10 @@ var SessionManager = class {
13753
13825
  cwd: file.cwd,
13754
13826
  toolSessionId
13755
13827
  });
13756
- return { response: { userMessageIds: ids }, broadcast: [] };
13828
+ return {
13829
+ response: { userMessageIds: this.toUiUuids(args.sessionId, ids) },
13830
+ broadcast: []
13831
+ };
13757
13832
  }
13758
13833
  // 预览 rewind 的真实 diff:对齐 CC `fileHistoryGetDiffStats`。daemon 读
13759
13834
  // ~/.claude/file-history/<toolSessionId>/<backupFileName> vs 磁盘当前内容算 hunks,
@@ -13776,7 +13851,8 @@ var SessionManager = class {
13776
13851
  const r = this.deps.historyReader.computeRewindDiff({
13777
13852
  cwd: file.cwd,
13778
13853
  toolSessionId,
13779
- userMessageId: args.userMessageId
13854
+ // UI 传 synth uuid,转译成 jsonl 真 uuid 给 historyReader
13855
+ userMessageId: this.toRealUuid(args.sessionId, args.userMessageId)
13780
13856
  });
13781
13857
  return { response: { ...r }, broadcast: [] };
13782
13858
  }
@@ -13804,7 +13880,8 @@ var SessionManager = class {
13804
13880
  let raw;
13805
13881
  try {
13806
13882
  raw = await runner.sendControlRequest("rewind_files", {
13807
- user_message_id: args.userMessageId,
13883
+ // UI 传 synth uuid,CC IPC 要的是 jsonl 真 uuid(CC 进程内 FileHistoryState 用 real uuid 锚点)
13884
+ user_message_id: this.toRealUuid(args.sessionId, args.userMessageId),
13808
13885
  dry_run: dryRun
13809
13886
  });
13810
13887
  } catch (err) {
@@ -15470,6 +15547,7 @@ function normalizeUsage(raw) {
15470
15547
  }
15471
15548
  function parseClaudeObject(obj) {
15472
15549
  if (obj.isSidechain === true) return [];
15550
+ if (typeof obj.parent_tool_use_id === "string" && obj.parent_tool_use_id.length > 0) return [];
15473
15551
  const events = parseClaudeObjectInner(obj);
15474
15552
  const uuid = obj.uuid;
15475
15553
  if (typeof uuid === "string" && uuid.length > 0) {
@@ -15496,6 +15574,11 @@ function parseClaudeObjectInner(obj) {
15496
15574
  }
15497
15575
  return events;
15498
15576
  }
15577
+ if (type === "system" && (subtype === "task_started" || subtype === "task_progress" || subtype === "task_notification")) {
15578
+ const ev = parseTaskSystemEvent(subtype, obj);
15579
+ if (ev) events.push(ev);
15580
+ return events;
15581
+ }
15499
15582
  if (type === "assistant") {
15500
15583
  const message = obj.message;
15501
15584
  if (message) {
@@ -15532,7 +15615,7 @@ function parseClaudeObjectInner(obj) {
15532
15615
  const message = obj.message;
15533
15616
  const content = message?.content ?? [];
15534
15617
  if (!Array.isArray(content)) return events;
15535
- const toolResultExtra = extractToolResultExtra(obj.toolUseResult);
15618
+ const toolResultExtra = extractToolResultExtra(obj.toolUseResult ?? obj.tool_use_result);
15536
15619
  const sourceToolAssistantUUID = typeof obj.sourceToolAssistantUUID === "string" ? obj.sourceToolAssistantUUID : void 0;
15537
15620
  for (const part of content) {
15538
15621
  const partType = part.type;
@@ -15607,16 +15690,8 @@ function parseClaudeObjectInner(obj) {
15607
15690
  });
15608
15691
  return events;
15609
15692
  }
15610
- if (type === "usage" && obj.usage && typeof obj.usage === "object") {
15611
- const usage = normalizeUsage(obj.usage);
15612
- if (usage) events.push({ kind: "meta_update", patch: { contextUsage: usage } });
15613
- return events;
15614
- }
15693
+ if (type === "usage") return events;
15615
15694
  if (type === "result") {
15616
- if (obj.usage && typeof obj.usage === "object") {
15617
- const usage = normalizeUsage(obj.usage);
15618
- if (usage) events.push({ kind: "meta_update", patch: { contextUsage: usage } });
15619
- }
15620
15695
  events.push({ kind: "turn_end" });
15621
15696
  return events;
15622
15697
  }
@@ -15649,6 +15724,25 @@ function extractStructuredPatch(raw) {
15649
15724
  }
15650
15725
  return hunks.length > 0 ? hunks : void 0;
15651
15726
  }
15727
+ function parseTaskSystemEvent(subtype, obj) {
15728
+ const toolUseId = typeof obj.tool_use_id === "string" ? obj.tool_use_id : "";
15729
+ const agentId = typeof obj.task_id === "string" ? obj.task_id : "";
15730
+ if (!toolUseId || !agentId) return null;
15731
+ const status = subtype === "task_started" ? "started" : subtype === "task_progress" ? "running" : typeof obj.status === "string" && obj.status === "failed" ? "failed" : "completed";
15732
+ const ev = { kind: "subagent_progress", toolUseId, agentId, status };
15733
+ if (typeof obj.description === "string" && obj.description.length > 0) ev.description = obj.description;
15734
+ if (typeof obj.last_tool_name === "string" && obj.last_tool_name.length > 0) ev.lastToolName = obj.last_tool_name;
15735
+ if (typeof obj.prompt === "string" && obj.prompt.length > 0) ev.prompt = obj.prompt;
15736
+ const usage = obj.usage;
15737
+ if (usage && typeof usage === "object") {
15738
+ const stats = {};
15739
+ if (typeof usage.duration_ms === "number") stats.durationMs = usage.duration_ms;
15740
+ if (typeof usage.total_tokens === "number") stats.tokens = usage.total_tokens;
15741
+ if (typeof usage.tool_uses === "number") stats.toolUseCount = usage.tool_uses;
15742
+ if (Object.keys(stats).length > 0) ev.stats = stats;
15743
+ }
15744
+ return ev;
15745
+ }
15652
15746
  function extractToolResultExtra(raw) {
15653
15747
  if (!raw || typeof raw !== "object") return void 0;
15654
15748
  const r = raw;
@@ -15740,6 +15834,13 @@ function setUnknownTypeHandler(handler) {
15740
15834
  function notifyUnknownType(type, subtype, sessionId) {
15741
15835
  if (unknownTypeHandler) unknownTypeHandler(type, subtype, sessionId);
15742
15836
  }
15837
+ function buildSpawnEnv(ctxEnv, base = process.env) {
15838
+ return {
15839
+ CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING: "1",
15840
+ ...base,
15841
+ ...ctxEnv ?? {}
15842
+ };
15843
+ }
15743
15844
  function encodeClaudeStdin(text) {
15744
15845
  const frame = {
15745
15846
  type: "user",
@@ -15846,7 +15947,7 @@ var ClaudeAdapter = class {
15846
15947
  const cmd = process.env.CLAUDE_BIN ?? "claude";
15847
15948
  const child = (0, import_node_child_process.spawn)(cmd, args, {
15848
15949
  cwd: ctx.cwd,
15849
- env: { ...process.env, ...ctx.env ?? {} },
15950
+ env: buildSpawnEnv(ctx.env),
15850
15951
  stdio: ["pipe", "pipe", "pipe"]
15851
15952
  });
15852
15953
  this.logger?.info("claude spawn", { cmd, args, cwd: ctx.cwd });
@@ -16229,11 +16330,50 @@ var SessionObserver = class {
16229
16330
  if (events.length > 0) {
16230
16331
  this.opts.onEvent({ sessionId: w.sessionId, events });
16231
16332
  }
16333
+ this.maybeReportUserMessage(w.sessionId, line);
16232
16334
  }
16233
16335
  } finally {
16234
16336
  import_node_fs9.default.closeSync(fd);
16235
16337
  }
16236
16338
  }
16339
+ // 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
16340
+ // 且 content 含 text part)时,抽出 real uuid + 第一段 text + timestamp 上报。
16341
+ // 不依赖 adapter——这是 CC JSONL 格式的兜底识别,与 observer 整体的 CC 耦合一致
16342
+ maybeReportUserMessage(sessionId, line) {
16343
+ const cb = this.opts.onUserMessageObserved;
16344
+ if (!cb) return;
16345
+ const trimmed = line.trim();
16346
+ if (!trimmed) return;
16347
+ let obj;
16348
+ try {
16349
+ obj = JSON.parse(trimmed);
16350
+ } catch {
16351
+ return;
16352
+ }
16353
+ if (obj.type !== "user") return;
16354
+ if (obj.isSidechain === true) return;
16355
+ if (typeof obj.parent_tool_use_id === "string" && obj.parent_tool_use_id.length > 0) return;
16356
+ if (typeof obj.parentToolUseId === "string" && obj.parentToolUseId.length > 0) return;
16357
+ const uuid = obj.uuid;
16358
+ if (typeof uuid !== "string" || uuid.length === 0) return;
16359
+ const message = obj.message;
16360
+ if (!message || message.role !== "user") return;
16361
+ const content = message.content;
16362
+ let text = null;
16363
+ if (Array.isArray(content)) {
16364
+ for (const part of content) {
16365
+ if (part && typeof part === "object" && part.type === "text" && typeof part.text === "string") {
16366
+ text = part.text;
16367
+ break;
16368
+ }
16369
+ }
16370
+ } else if (typeof content === "string") {
16371
+ text = content;
16372
+ }
16373
+ if (text === null) return;
16374
+ const ts = typeof obj.timestamp === "string" ? obj.timestamp : void 0;
16375
+ cb(sessionId, uuid, text, ts);
16376
+ }
16237
16377
  isObserving(sessionId) {
16238
16378
  return this.watches.has(sessionId);
16239
16379
  }
@@ -17907,7 +18047,12 @@ async function startDaemon(config) {
17907
18047
  onEvent: ({ sessionId, events }) => {
17908
18048
  manager.feedObserverEvents(sessionId, events);
17909
18049
  },
17910
- onStatus: (sessionId, status) => transport?.broadcastToSession(sessionId, { type: "session:status", sessionId, status })
18050
+ onStatus: (sessionId, status) => transport?.broadcastToSession(sessionId, { type: "session:status", sessionId, status }),
18051
+ // jsonl user 行的 real uuid 映射到 buffer 里 synth user_text,rewind 系列 RPC 在
18052
+ // daemon 内部转译,UI 看到的始终是 events 流里的 synth uuid(详见 manager 注释)
18053
+ onUserMessageObserved: (sessionId, realUuid, text) => {
18054
+ manager.recordRealUserUuid({ sessionId, realUuid, text });
18055
+ }
17911
18056
  });
17912
18057
  const handlers = buildMethodHandlers({ manager, workspace, skills, history, observer, getAdapter, store });
17913
18058
  wsServer = new LocalWsServer({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.10-beta.7.d4b8b64",
3
+ "version": "0.2.11-beta.8.3707c2b",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",