@clawos-dev/clawd 0.2.19 → 0.2.20-beta.28.51ba742
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 +169 -4
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -8145,6 +8145,9 @@ var METHOD_NAMES = [
|
|
|
8145
8145
|
"session:unsubscribe",
|
|
8146
8146
|
"session:pin",
|
|
8147
8147
|
"permission:respond",
|
|
8148
|
+
// AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
|
|
8149
|
+
// 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
|
|
8150
|
+
"session:answerQuestion",
|
|
8148
8151
|
"history:projects",
|
|
8149
8152
|
"history:list",
|
|
8150
8153
|
"history:read",
|
|
@@ -8176,6 +8179,7 @@ var HISTORY_USER_META_VALUES = [
|
|
|
8176
8179
|
"attachment-skills",
|
|
8177
8180
|
"attachment-deferred-tools"
|
|
8178
8181
|
];
|
|
8182
|
+
var ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
|
|
8179
8183
|
|
|
8180
8184
|
// ../protocol/src/errors.ts
|
|
8181
8185
|
var ERROR_CODES = {
|
|
@@ -12378,6 +12382,16 @@ var MemoryEntrySchema = external_exports.object({
|
|
|
12378
12382
|
content: external_exports.string(),
|
|
12379
12383
|
mtimeMs: external_exports.number().optional()
|
|
12380
12384
|
});
|
|
12385
|
+
var AskQuestionOptionSchema = external_exports.object({
|
|
12386
|
+
label: external_exports.string().min(1),
|
|
12387
|
+
description: external_exports.string().optional()
|
|
12388
|
+
});
|
|
12389
|
+
var AskQuestionItemSchema = external_exports.object({
|
|
12390
|
+
question: external_exports.string().min(1),
|
|
12391
|
+
// multiSelect 必填(boolean 不给默认值):daemon emit 时必须显式传,避免 UI 端漂移
|
|
12392
|
+
multiSelect: external_exports.boolean(),
|
|
12393
|
+
options: external_exports.array(AskQuestionOptionSchema).min(1)
|
|
12394
|
+
});
|
|
12381
12395
|
var ParsedEventSchema = external_exports.discriminatedUnion("kind", [
|
|
12382
12396
|
// session_init 仅携带 session 标识;resolvedModel 等元信息走 meta_update
|
|
12383
12397
|
external_exports.object({
|
|
@@ -12430,7 +12444,13 @@ var ParsedEventSchema = external_exports.discriminatedUnion("kind", [
|
|
|
12430
12444
|
// 老 CC 走 <task-notification> user 消息携带统计,新 CC 直接挂在这里——兼容期两者可能并存
|
|
12431
12445
|
toolResultExtra: ToolResultExtraSchema.optional(),
|
|
12432
12446
|
// 精确指向发起 tool 的 assistant 消息顶层 uuid;toolUseId 已够 UI 聚合,这个仅作调试辅助
|
|
12433
|
-
sourceToolAssistantUUID: external_exports.string().optional()
|
|
12447
|
+
sourceToolAssistantUUID: external_exports.string().optional(),
|
|
12448
|
+
// AskUserQuestion 实时回放(plan: clawd-ask-user-question 任务 B 修复):
|
|
12449
|
+
// CC 用户答完后把 answers 写在 user 帧顶层 toolUseResult.answers,daemon parser
|
|
12450
|
+
// 抽到这里。UI eventsToLines 用 toolUseId 配对到 question-card line,把卡片转为
|
|
12451
|
+
// 只读态显示用户的答案。仅 AskUserQuestion 配对的 tool_result 行携带;普通工具不带。
|
|
12452
|
+
// 等价于 HistoryMessage.askQuestionAnswers(历史回放路径),两端字段名对齐。
|
|
12453
|
+
askQuestionAnswers: external_exports.record(external_exports.string(), external_exports.string()).optional()
|
|
12434
12454
|
}),
|
|
12435
12455
|
// CC 命中项目记忆(`type:"attachment"`,`attachment.type:"memories"`)的独立事件;
|
|
12436
12456
|
// 其它 attachment 子类型(skills / hook_context / deferred_tools / hook_success / unknown)
|
|
@@ -12451,6 +12471,20 @@ var ParsedEventSchema = external_exports.discriminatedUnion("kind", [
|
|
|
12451
12471
|
// 选项 ii)。老 daemon / legacy CC 不发该字段时保持向后兼容(optional)。
|
|
12452
12472
|
toolUseId: external_exports.string().optional()
|
|
12453
12473
|
}),
|
|
12474
|
+
// AskUserQuestion 工具调用的内联锚点(plan: clawd-ask-user-question 问题 1 修复):
|
|
12475
|
+
// daemon parser 把 assistant tool_use AskUserQuestion 翻译成本 kind(不是 permission_request、
|
|
12476
|
+
// 也不是 tool_call),让 UI 在事件流自然位置渲染问答卡(紧跟 assistant 消息 / 在后续消息之前),
|
|
12477
|
+
// 而不是被强行挂在末尾。permission_request 帧仍正常下发驱动 pendingQuestions 状态机,
|
|
12478
|
+
// 但只走 wire 帧不入 ring buffer——状态由 daemon pendingQuestions 持有,
|
|
12479
|
+
// 渲染锚点由本 kind 的 ParsedEvent 提供。
|
|
12480
|
+
// 历史回放:history-to-lines.ts 在 jsonl tool_use AskUserQuestion 处插入对应 'question-card' line,
|
|
12481
|
+
// 配对的 tool_result 携带 toolUseResult.answers,已答完的展示只读态。
|
|
12482
|
+
external_exports.object({
|
|
12483
|
+
...ParsedEventBase,
|
|
12484
|
+
kind: external_exports.literal("ask_user_question"),
|
|
12485
|
+
toolUseId: external_exports.string(),
|
|
12486
|
+
questions: external_exports.array(AskQuestionItemSchema)
|
|
12487
|
+
}),
|
|
12454
12488
|
external_exports.object({
|
|
12455
12489
|
...ParsedEventBase,
|
|
12456
12490
|
kind: external_exports.literal("turn_end")
|
|
@@ -12658,6 +12692,20 @@ var RecentDirEntrySchema = external_exports.object({
|
|
|
12658
12692
|
var HistoryRecentDirsResponseSchema = external_exports.object({
|
|
12659
12693
|
dirs: external_exports.array(RecentDirEntrySchema)
|
|
12660
12694
|
});
|
|
12695
|
+
var SessionQuestionFrameSchema = external_exports.object({
|
|
12696
|
+
type: external_exports.literal("session:question"),
|
|
12697
|
+
sessionId: external_exports.string().min(1),
|
|
12698
|
+
toolUseId: external_exports.string().min(1),
|
|
12699
|
+
requestId: external_exports.string().min(1),
|
|
12700
|
+
// UI 单卡承载多 question;空数组语义无效,schema 必拒
|
|
12701
|
+
questions: external_exports.array(AskQuestionItemSchema).min(1)
|
|
12702
|
+
});
|
|
12703
|
+
var AnswerQuestionArgs = external_exports.object({
|
|
12704
|
+
sessionId: external_exports.string().min(1),
|
|
12705
|
+
toolUseId: external_exports.string().min(1),
|
|
12706
|
+
answers: external_exports.record(external_exports.string(), external_exports.string())
|
|
12707
|
+
});
|
|
12708
|
+
var AnswerQuestionResponseSchema = external_exports.object({ ok: external_exports.literal(true) });
|
|
12661
12709
|
var AuthRequestFrameSchema = external_exports.object({
|
|
12662
12710
|
type: external_exports.literal("auth"),
|
|
12663
12711
|
token: external_exports.string().min(1),
|
|
@@ -12847,6 +12895,7 @@ function cloneState(s) {
|
|
|
12847
12895
|
bufferStartSeq: s.bufferStartSeq,
|
|
12848
12896
|
turnOpen: s.turnOpen,
|
|
12849
12897
|
pendingPermissions: s.pendingPermissions,
|
|
12898
|
+
pendingQuestions: s.pendingQuestions,
|
|
12850
12899
|
freshSpawn: s.freshSpawn,
|
|
12851
12900
|
procAlive: s.procAlive,
|
|
12852
12901
|
seenUuids: s.seenUuids
|
|
@@ -12947,10 +12996,29 @@ function applyParsedEvent(state, event, deps) {
|
|
|
12947
12996
|
const tool = event.tool;
|
|
12948
12997
|
const input = event.input;
|
|
12949
12998
|
const requestId = event.requestId;
|
|
12950
|
-
const
|
|
12999
|
+
const toolUseId = event.toolUseId;
|
|
13000
|
+
if (tool === ASK_USER_QUESTION_TOOL_NAME) {
|
|
13001
|
+
const questions = input && typeof input === "object" && "questions" in input ? input.questions : void 0;
|
|
13002
|
+
const frame = {
|
|
13003
|
+
type: "session:question",
|
|
13004
|
+
sessionId,
|
|
13005
|
+
toolUseId: toolUseId ?? "",
|
|
13006
|
+
requestId,
|
|
13007
|
+
questions: Array.isArray(questions) ? questions : []
|
|
13008
|
+
};
|
|
13009
|
+
effects.push({ kind: "emit-frame", frame });
|
|
13010
|
+
if (toolUseId) {
|
|
13011
|
+
next.pendingQuestions = {
|
|
13012
|
+
...next.pendingQuestions,
|
|
13013
|
+
[toolUseId]: { requestId, input }
|
|
13014
|
+
};
|
|
13015
|
+
}
|
|
13016
|
+
return { state: next, effects };
|
|
13017
|
+
}
|
|
12951
13018
|
const pushed2 = pushEventToBuffer(next, event, deps);
|
|
12952
13019
|
next = pushed2.state;
|
|
12953
13020
|
effects.push(...pushed2.effects);
|
|
13021
|
+
const hit = matchesAnyRule(next.file.permissionRules, tool, input);
|
|
12954
13022
|
if (hit) {
|
|
12955
13023
|
effects.push({
|
|
12956
13024
|
kind: "write-stdin",
|
|
@@ -13071,6 +13139,7 @@ function applyCommand(state, command, deps) {
|
|
|
13071
13139
|
}
|
|
13072
13140
|
case "stop": {
|
|
13073
13141
|
next.pendingPermissions = {};
|
|
13142
|
+
next.pendingQuestions = {};
|
|
13074
13143
|
if (next.procAlive) {
|
|
13075
13144
|
next.status = "stopping";
|
|
13076
13145
|
effects.push({ kind: "kill", signal: "SIGKILL" });
|
|
@@ -13097,6 +13166,7 @@ function applyCommand(state, command, deps) {
|
|
|
13097
13166
|
}
|
|
13098
13167
|
case "delete": {
|
|
13099
13168
|
next.pendingPermissions = {};
|
|
13169
|
+
next.pendingQuestions = {};
|
|
13100
13170
|
if (next.procAlive) {
|
|
13101
13171
|
effects.push({ kind: "kill", signal: "SIGKILL" });
|
|
13102
13172
|
}
|
|
@@ -13206,6 +13276,7 @@ function reduceSession(state, input, deps) {
|
|
|
13206
13276
|
const next = cloneState(state);
|
|
13207
13277
|
next.procAlive = false;
|
|
13208
13278
|
next.status = "stopped";
|
|
13279
|
+
next.pendingQuestions = {};
|
|
13209
13280
|
return {
|
|
13210
13281
|
state: next,
|
|
13211
13282
|
effects: [
|
|
@@ -13277,6 +13348,31 @@ function reduceSession(state, input, deps) {
|
|
|
13277
13348
|
}
|
|
13278
13349
|
return { state: next, effects };
|
|
13279
13350
|
}
|
|
13351
|
+
case "answer-question": {
|
|
13352
|
+
const pending = state.pendingQuestions?.[input.toolUseId];
|
|
13353
|
+
if (!pending) {
|
|
13354
|
+
return { state, effects: [] };
|
|
13355
|
+
}
|
|
13356
|
+
const next = cloneState(state);
|
|
13357
|
+
const nextPending = { ...next.pendingQuestions };
|
|
13358
|
+
delete nextPending[input.toolUseId];
|
|
13359
|
+
next.pendingQuestions = nextPending;
|
|
13360
|
+
const baseInput = pending.input && typeof pending.input === "object" && !Array.isArray(pending.input) ? pending.input : {};
|
|
13361
|
+
const updatedInput = {
|
|
13362
|
+
...baseInput,
|
|
13363
|
+
answers: input.answers
|
|
13364
|
+
};
|
|
13365
|
+
return {
|
|
13366
|
+
state: next,
|
|
13367
|
+
effects: [
|
|
13368
|
+
{
|
|
13369
|
+
kind: "send-control-response-allow-with-input",
|
|
13370
|
+
requestId: pending.requestId,
|
|
13371
|
+
updatedInput
|
|
13372
|
+
}
|
|
13373
|
+
]
|
|
13374
|
+
};
|
|
13375
|
+
}
|
|
13280
13376
|
case "tick": {
|
|
13281
13377
|
const staleMs = 10 * 60 * 1e3;
|
|
13282
13378
|
const now = input.nowMs;
|
|
@@ -13301,6 +13397,20 @@ function reduceSession(state, input, deps) {
|
|
|
13301
13397
|
|
|
13302
13398
|
// src/session/runner.ts
|
|
13303
13399
|
var DEFAULT_CONTROL_REQUEST_TIMEOUT_MS = 1e4;
|
|
13400
|
+
function encodeAllowWithInputControlResponse(requestId, updatedInput) {
|
|
13401
|
+
const payload = {
|
|
13402
|
+
type: "control_response",
|
|
13403
|
+
response: {
|
|
13404
|
+
subtype: "success",
|
|
13405
|
+
request_id: requestId,
|
|
13406
|
+
response: {
|
|
13407
|
+
behavior: "allow",
|
|
13408
|
+
updatedInput
|
|
13409
|
+
}
|
|
13410
|
+
}
|
|
13411
|
+
};
|
|
13412
|
+
return JSON.stringify(payload) + "\n";
|
|
13413
|
+
}
|
|
13304
13414
|
var DEFAULT_WAIT_STOP_TIMEOUT_MS = 3e3;
|
|
13305
13415
|
var SessionRunner = class {
|
|
13306
13416
|
constructor(initial, hooks) {
|
|
@@ -13460,6 +13570,9 @@ var SessionRunner = class {
|
|
|
13460
13570
|
case "write-stdin":
|
|
13461
13571
|
this.proc?.stdin?.write(effect.payload);
|
|
13462
13572
|
break;
|
|
13573
|
+
case "send-control-response-allow-with-input":
|
|
13574
|
+
this.proc?.stdin?.write(encodeAllowWithInputControlResponse(effect.requestId, effect.updatedInput));
|
|
13575
|
+
break;
|
|
13463
13576
|
case "persist-file":
|
|
13464
13577
|
try {
|
|
13465
13578
|
this.hooks.store.write(effect.file);
|
|
@@ -13586,6 +13699,7 @@ function makeInitialState(file) {
|
|
|
13586
13699
|
bufferStartSeq: 0,
|
|
13587
13700
|
turnOpen: false,
|
|
13588
13701
|
pendingPermissions: {},
|
|
13702
|
+
pendingQuestions: {},
|
|
13589
13703
|
freshSpawn: false,
|
|
13590
13704
|
procAlive: false,
|
|
13591
13705
|
seenUuids: []
|
|
@@ -14058,6 +14172,23 @@ var SessionManager = class {
|
|
|
14058
14172
|
if (runner.getState().procAlive) return;
|
|
14059
14173
|
runner.feedObserverEvents(events);
|
|
14060
14174
|
}
|
|
14175
|
+
// AskUserQuestion 表单回写(plan: clawd-ask-user-question):UI 答完所有 question 后调用。
|
|
14176
|
+
// - session 不存在 / 无 runner → noop 幂等返回 ok(first-decider-wins)
|
|
14177
|
+
// - reducer noop(toolUseId 不存在或已答过)也保持幂等返回,handler 不抛
|
|
14178
|
+
answerQuestion(args) {
|
|
14179
|
+
const runner = this.runners.get(args.sessionId);
|
|
14180
|
+
if (!runner) {
|
|
14181
|
+
return { response: { ok: true }, broadcast: [] };
|
|
14182
|
+
}
|
|
14183
|
+
const { broadcast } = this.withCollector(() => {
|
|
14184
|
+
runner.input({
|
|
14185
|
+
kind: "answer-question",
|
|
14186
|
+
toolUseId: args.toolUseId,
|
|
14187
|
+
answers: args.answers
|
|
14188
|
+
});
|
|
14189
|
+
});
|
|
14190
|
+
return { response: { ok: true }, broadcast };
|
|
14191
|
+
}
|
|
14061
14192
|
// 权限决定:dispatcher 收到 permission:respond 时调用
|
|
14062
14193
|
// 先查 state.pendingPermissions:不存在 → 抛 PERMISSION_REQUEST_STALE
|
|
14063
14194
|
respondPermission(args) {
|
|
@@ -14850,6 +14981,19 @@ function splitLines(text) {
|
|
|
14850
14981
|
return result;
|
|
14851
14982
|
}
|
|
14852
14983
|
|
|
14984
|
+
// src/tools/tool-result-extra.ts
|
|
14985
|
+
function extractAskQuestionAnswers(raw) {
|
|
14986
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
14987
|
+
const r = raw;
|
|
14988
|
+
const answers = r.answers;
|
|
14989
|
+
if (!answers || typeof answers !== "object" || Array.isArray(answers)) return void 0;
|
|
14990
|
+
const out = {};
|
|
14991
|
+
for (const [k, v] of Object.entries(answers)) {
|
|
14992
|
+
if (typeof v === "string") out[k] = v;
|
|
14993
|
+
}
|
|
14994
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
14995
|
+
}
|
|
14996
|
+
|
|
14853
14997
|
// src/tools/claude-history.ts
|
|
14854
14998
|
function cwdToHashDir(cwd) {
|
|
14855
14999
|
return "-" + cwd.replace(/^\//, "").replace(/\//g, "-");
|
|
@@ -14974,6 +15118,8 @@ function messageFromJsonl(obj) {
|
|
|
14974
15118
|
};
|
|
14975
15119
|
if (toolResultExtra) msg.toolResultExtra = toolResultExtra;
|
|
14976
15120
|
if (sourceToolAssistantUUID) msg.sourceToolAssistantUUID = sourceToolAssistantUUID;
|
|
15121
|
+
const answers = extractAskQuestionAnswers(o.toolUseResult);
|
|
15122
|
+
if (answers) msg.askQuestionAnswers = answers;
|
|
14977
15123
|
return withCommonFields(msg, o);
|
|
14978
15124
|
}
|
|
14979
15125
|
if (texts.length > 0) {
|
|
@@ -15625,10 +15771,21 @@ function parseClaudeObjectInner(obj) {
|
|
|
15625
15771
|
} else if (partType === "thinking" && typeof part.thinking === "string") {
|
|
15626
15772
|
events.push({ kind: "thinking", text: part.thinking });
|
|
15627
15773
|
} else if (partType === "tool_use") {
|
|
15774
|
+
const toolName = String(part.name ?? "");
|
|
15775
|
+
if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
|
|
15776
|
+
const qInput = part.input ?? {};
|
|
15777
|
+
const rawQs = Array.isArray(qInput.questions) ? qInput.questions : [];
|
|
15778
|
+
events.push({
|
|
15779
|
+
kind: "ask_user_question",
|
|
15780
|
+
toolUseId: String(part.id ?? ""),
|
|
15781
|
+
questions: rawQs
|
|
15782
|
+
});
|
|
15783
|
+
continue;
|
|
15784
|
+
}
|
|
15628
15785
|
events.push({
|
|
15629
15786
|
kind: "tool_call",
|
|
15630
15787
|
toolUseId: String(part.id ?? ""),
|
|
15631
|
-
tool:
|
|
15788
|
+
tool: toolName,
|
|
15632
15789
|
input: part.input
|
|
15633
15790
|
});
|
|
15634
15791
|
}
|
|
@@ -15641,6 +15798,7 @@ function parseClaudeObjectInner(obj) {
|
|
|
15641
15798
|
if (!Array.isArray(content)) return events;
|
|
15642
15799
|
const toolResultExtra = extractToolResultExtra(obj.toolUseResult ?? obj.tool_use_result);
|
|
15643
15800
|
const sourceToolAssistantUUID = typeof obj.sourceToolAssistantUUID === "string" ? obj.sourceToolAssistantUUID : void 0;
|
|
15801
|
+
const askQuestionAnswers = extractAskQuestionAnswers(obj.toolUseResult ?? obj.tool_use_result);
|
|
15644
15802
|
for (const part of content) {
|
|
15645
15803
|
const partType = part.type;
|
|
15646
15804
|
if (partType === "text" && typeof part.text === "string") {
|
|
@@ -15664,6 +15822,7 @@ function parseClaudeObjectInner(obj) {
|
|
|
15664
15822
|
}
|
|
15665
15823
|
if (toolResultExtra) ev.toolResultExtra = toolResultExtra;
|
|
15666
15824
|
if (sourceToolAssistantUUID) ev.sourceToolAssistantUUID = sourceToolAssistantUUID;
|
|
15825
|
+
if (askQuestionAnswers) ev.askQuestionAnswers = askQuestionAnswers;
|
|
15667
15826
|
events.push(ev);
|
|
15668
15827
|
}
|
|
15669
15828
|
}
|
|
@@ -17585,6 +17744,11 @@ function buildSessionHandlers(deps) {
|
|
|
17585
17744
|
const { response, broadcast } = manager.pin(args);
|
|
17586
17745
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
17587
17746
|
};
|
|
17747
|
+
const answerQuestion = async (frame) => {
|
|
17748
|
+
const args = AnswerQuestionArgs.parse(frame);
|
|
17749
|
+
const { response, broadcast } = manager.answerQuestion(args);
|
|
17750
|
+
return { response: { type: "session:answerQuestion", ...response }, broadcast };
|
|
17751
|
+
};
|
|
17588
17752
|
return {
|
|
17589
17753
|
"session:create": create,
|
|
17590
17754
|
"session:list": list,
|
|
@@ -17604,7 +17768,8 @@ function buildSessionHandlers(deps) {
|
|
|
17604
17768
|
"session:events": events,
|
|
17605
17769
|
"session:subscribe": subscribe,
|
|
17606
17770
|
"session:unsubscribe": unsubscribe,
|
|
17607
|
-
"session:pin": pin
|
|
17771
|
+
"session:pin": pin,
|
|
17772
|
+
"session:answerQuestion": answerQuestion
|
|
17608
17773
|
};
|
|
17609
17774
|
}
|
|
17610
17775
|
|
package/package.json
CHANGED