@clawos-dev/clawd 0.2.10 → 0.2.11
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 +160 -15
- 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 {
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
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:
|
|
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({
|