@clawos-dev/clawd 0.2.414 → 0.2.415
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 +82 -20
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -45085,6 +45085,9 @@ function cloneState(s) {
|
|
|
45085
45085
|
procAlive: s.procAlive,
|
|
45086
45086
|
seenUuids: s.seenUuids,
|
|
45087
45087
|
subSessionMeta: s.subSessionMeta,
|
|
45088
|
+
// 浅拷贝:下方 trackBackgroundTask 在 next 上原地增删,不能共享调用方数组(纯函数契约)
|
|
45089
|
+
pendingBgTasks: s.pendingBgTasks ? s.pendingBgTasks.slice() : void 0,
|
|
45090
|
+
stdinClosed: s.stdinClosed,
|
|
45088
45091
|
// ReadyGate v2:readyForSend 是基础类型直接复制;pendingSend 必须浅拷贝防止
|
|
45089
45092
|
// reduce 内 push/shift 在调用方原 state 上副作用(reducer 纯函数契约)
|
|
45090
45093
|
readyForSend: s.readyForSend,
|
|
@@ -45372,6 +45375,16 @@ function pushEventToBufferInPlace(next, event, deps) {
|
|
|
45372
45375
|
const isTurnEnd = withSeq.kind === "turn_end";
|
|
45373
45376
|
const isUserText = withSeq.kind === "user_text";
|
|
45374
45377
|
const isBackgroundEvent = withSeq.kind === "subagent_progress";
|
|
45378
|
+
if (isBackgroundEvent) {
|
|
45379
|
+
const bg = withSeq;
|
|
45380
|
+
const done = bg.status === "completed" || bg.status === "failed";
|
|
45381
|
+
const pending = next.pendingBgTasks ?? [];
|
|
45382
|
+
if (done) {
|
|
45383
|
+
next.pendingBgTasks = pending.filter((id) => id !== bg.agentId);
|
|
45384
|
+
} else if (bg.agentId && !pending.includes(bg.agentId)) {
|
|
45385
|
+
next.pendingBgTasks = [...pending, bg.agentId];
|
|
45386
|
+
}
|
|
45387
|
+
}
|
|
45375
45388
|
if (isTurnEnd && next.turnOpen && isLateTurnEnd(withSeq, next.turnStartedAt, deps.now())) {
|
|
45376
45389
|
return [emitSessionEvent(next.file.sessionId, withSeq)];
|
|
45377
45390
|
}
|
|
@@ -45427,13 +45440,22 @@ function pushEventToBufferInPlace(next, event, deps) {
|
|
|
45427
45440
|
status: "running-idle"
|
|
45428
45441
|
}
|
|
45429
45442
|
});
|
|
45430
|
-
const
|
|
45431
|
-
|
|
45432
|
-
|
|
45433
|
-
|
|
45434
|
-
|
|
45435
|
-
|
|
45436
|
-
});
|
|
45443
|
+
const tool = next.file.tool ?? "claude";
|
|
45444
|
+
const canCloseStdin = tool === "claude" && deps.mode !== "tui" && (next.pendingBgTasks?.length ?? 0) === 0;
|
|
45445
|
+
if (canCloseStdin) {
|
|
45446
|
+
next.stdinClosed = true;
|
|
45447
|
+
effects.push({ kind: "close-stdin" });
|
|
45448
|
+
} else if (tool === "codex") {
|
|
45449
|
+
effects.push({ kind: "kill", signal: "SIGTERM" });
|
|
45450
|
+
} else {
|
|
45451
|
+
const idleKillMs = idleKillDelayOf(next);
|
|
45452
|
+
if (idleKillMs !== void 0) {
|
|
45453
|
+
effects.push({
|
|
45454
|
+
kind: "schedule-idle-kill",
|
|
45455
|
+
sessionId: next.file.sessionId,
|
|
45456
|
+
ms: idleKillMs
|
|
45457
|
+
});
|
|
45458
|
+
}
|
|
45437
45459
|
}
|
|
45438
45460
|
}
|
|
45439
45461
|
return effects;
|
|
@@ -45468,12 +45490,16 @@ function applyCommand(state, command, deps) {
|
|
|
45468
45490
|
});
|
|
45469
45491
|
}
|
|
45470
45492
|
}
|
|
45493
|
+
if (next.procAlive && next.stdinClosed) {
|
|
45494
|
+
effects.push({ kind: "kill", signal: "SIGKILL" });
|
|
45495
|
+
next.procAlive = false;
|
|
45496
|
+
next.stdinClosed = false;
|
|
45497
|
+
}
|
|
45471
45498
|
if (!next.procAlive) {
|
|
45472
45499
|
next.procAlive = true;
|
|
45473
45500
|
next.freshSpawn = true;
|
|
45501
|
+
next.stdinClosed = false;
|
|
45474
45502
|
next.buffer = [];
|
|
45475
|
-
next.bufferStartSeq = 0;
|
|
45476
|
-
next.nextSeq = 0;
|
|
45477
45503
|
next.turnOpen = false;
|
|
45478
45504
|
next.turnStartedAt = null;
|
|
45479
45505
|
next.status = "running";
|
|
@@ -45559,8 +45585,6 @@ function applyCommand(state, command, deps) {
|
|
|
45559
45585
|
next.procAlive = true;
|
|
45560
45586
|
next.freshSpawn = true;
|
|
45561
45587
|
next.buffer = [];
|
|
45562
|
-
next.bufferStartSeq = 0;
|
|
45563
|
-
next.nextSeq = 0;
|
|
45564
45588
|
next.turnOpen = false;
|
|
45565
45589
|
next.turnStartedAt = null;
|
|
45566
45590
|
next.status = "running";
|
|
@@ -45741,6 +45765,8 @@ function reduceSessionInner(state, input, deps) {
|
|
|
45741
45765
|
const next = cloneState(state);
|
|
45742
45766
|
next.procAlive = false;
|
|
45743
45767
|
next.status = "stopped";
|
|
45768
|
+
next.stdinClosed = false;
|
|
45769
|
+
next.pendingBgTasks = [];
|
|
45744
45770
|
const interruptEffects = next.turnOpen ? pushEventToBufferInPlace(next, { kind: "turn_end", reason: "interrupted" }, deps) : [];
|
|
45745
45771
|
resetReadyGate(next);
|
|
45746
45772
|
const sessionId = next.file.sessionId;
|
|
@@ -46234,8 +46260,7 @@ var SessionRunner = class {
|
|
|
46234
46260
|
timer.unref?.();
|
|
46235
46261
|
this.pendingControlRequests.set(requestId, { resolve: resolve9, reject, timer });
|
|
46236
46262
|
try {
|
|
46237
|
-
|
|
46238
|
-
this.recorder?.tapStdinWrite(payload);
|
|
46263
|
+
if (!this.writeStdin(payload)) throw new Error(`stdin closed: ${subtype}`);
|
|
46239
46264
|
} catch (err) {
|
|
46240
46265
|
clearTimeout(timer);
|
|
46241
46266
|
this.pendingControlRequests.delete(requestId);
|
|
@@ -46293,6 +46318,9 @@ var SessionRunner = class {
|
|
|
46293
46318
|
}
|
|
46294
46319
|
this.doKill(effect.signal);
|
|
46295
46320
|
break;
|
|
46321
|
+
case "close-stdin":
|
|
46322
|
+
this.proc?.stdin?.end();
|
|
46323
|
+
break;
|
|
46296
46324
|
case "write-stdin":
|
|
46297
46325
|
if (this.session) {
|
|
46298
46326
|
this.session.startTurn(effect.payload);
|
|
@@ -46304,8 +46332,7 @@ var SessionRunner = class {
|
|
|
46304
46332
|
len: effect.payload.length,
|
|
46305
46333
|
preview: effect.payload.replace(/\x1b\[20[01]~/g, "\xB7paste\xB7").slice(0, 60)
|
|
46306
46334
|
});
|
|
46307
|
-
this.
|
|
46308
|
-
this.recorder?.tapStdinWrite(effect.payload);
|
|
46335
|
+
this.writeStdin(effect.payload);
|
|
46309
46336
|
break;
|
|
46310
46337
|
case "respond-permission": {
|
|
46311
46338
|
if (this.session) {
|
|
@@ -46313,14 +46340,12 @@ var SessionRunner = class {
|
|
|
46313
46340
|
break;
|
|
46314
46341
|
}
|
|
46315
46342
|
const payload = encodePermissionResponse(effect.requestId, effect.allow, effect.input, effect.message);
|
|
46316
|
-
this.
|
|
46317
|
-
this.recorder?.tapStdinWrite(payload);
|
|
46343
|
+
this.writeStdin(payload);
|
|
46318
46344
|
break;
|
|
46319
46345
|
}
|
|
46320
46346
|
case "send-control-response-allow-with-input": {
|
|
46321
46347
|
const payload = encodeAllowWithInputControlResponse(effect.requestId, effect.updatedInput);
|
|
46322
|
-
this.
|
|
46323
|
-
this.recorder?.tapStdinWrite(payload);
|
|
46348
|
+
this.writeStdin(payload);
|
|
46324
46349
|
break;
|
|
46325
46350
|
}
|
|
46326
46351
|
case "persist-file":
|
|
@@ -46427,6 +46452,7 @@ var SessionRunner = class {
|
|
|
46427
46452
|
now: this.hooks.now
|
|
46428
46453
|
});
|
|
46429
46454
|
proc.stdout?.on("data", (chunk) => {
|
|
46455
|
+
if (this.proc !== proc) return;
|
|
46430
46456
|
const { newBuf, lines } = splitStdoutChunk(this.stdoutBuf, chunk);
|
|
46431
46457
|
this.stdoutBuf = newBuf;
|
|
46432
46458
|
for (const line of lines) {
|
|
@@ -46435,10 +46461,21 @@ var SessionRunner = class {
|
|
|
46435
46461
|
}
|
|
46436
46462
|
});
|
|
46437
46463
|
proc.stderr?.on("data", (chunk) => {
|
|
46464
|
+
if (this.proc !== proc) return;
|
|
46438
46465
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
46439
46466
|
this.hooks.logger?.warn("tool stderr", { line: text.slice(0, 500) });
|
|
46440
46467
|
});
|
|
46468
|
+
const stdin = proc.stdin;
|
|
46469
|
+
if (stdin && typeof stdin.on === "function") {
|
|
46470
|
+
stdin.on("error", (err) => {
|
|
46471
|
+
this.hooks.logger?.warn("stdin error (write dropped)", {
|
|
46472
|
+
sessionId: this.state.file.sessionId,
|
|
46473
|
+
message: err.message
|
|
46474
|
+
});
|
|
46475
|
+
});
|
|
46476
|
+
}
|
|
46441
46477
|
proc.on("exit", (code) => {
|
|
46478
|
+
if (this.proc !== proc) return;
|
|
46442
46479
|
this.proc = null;
|
|
46443
46480
|
this.recorder = null;
|
|
46444
46481
|
this.rejectAllPending(new Error("session gone"));
|
|
@@ -46446,11 +46483,33 @@ var SessionRunner = class {
|
|
|
46446
46483
|
this.input({ kind: "proc-exit", code });
|
|
46447
46484
|
});
|
|
46448
46485
|
proc.on("error", (err) => {
|
|
46486
|
+
if (this.proc !== proc) return;
|
|
46449
46487
|
const message = err.message;
|
|
46450
46488
|
this.hooks.logger?.error("claude spawn failed", { message });
|
|
46451
46489
|
this.input({ kind: "proc-error", message });
|
|
46452
46490
|
});
|
|
46453
46491
|
}
|
|
46492
|
+
/**
|
|
46493
|
+
* stdin 唯一写入口。stdin 已 EOF(close-stdin 收进程收尾中)时丢弃并告警——写进已关闭的
|
|
46494
|
+
* 管道会抛 EPIPE 把 daemon 打崩。reducer 侧保证正常路径不会撞上(send 遇 stdinClosed 先
|
|
46495
|
+
* kill 再 spawn),这道闸兜的是 permission / 问答回写等异步路径:它们的 effect 可能在
|
|
46496
|
+
* turn_end 关 stdin 之后才被执行。
|
|
46497
|
+
*
|
|
46498
|
+
* recorder tap 收在这里,保证"写成功才录",录制流与真实字节流不分叉。
|
|
46499
|
+
*/
|
|
46500
|
+
writeStdin(payload) {
|
|
46501
|
+
const stdin = this.proc?.stdin;
|
|
46502
|
+
if (!stdin || stdin.writableEnded) {
|
|
46503
|
+
this.hooks.logger?.warn("stdin write dropped: pipe already closed", {
|
|
46504
|
+
sessionId: this.state.file.sessionId,
|
|
46505
|
+
len: payload.length
|
|
46506
|
+
});
|
|
46507
|
+
return false;
|
|
46508
|
+
}
|
|
46509
|
+
stdin.write(payload);
|
|
46510
|
+
this.recorder?.tapStdinWrite(payload);
|
|
46511
|
+
return true;
|
|
46512
|
+
}
|
|
46454
46513
|
// kill 当前 proc:单发 signal,没有"先 TERM 后 KILL"的兜底。
|
|
46455
46514
|
// reducer 推下来的 signal 是 SIGKILL(见 reducer.ts 各 kill 推送处)——选择 SIGKILL
|
|
46456
46515
|
// 是因为 CC 在 streaming hang / 等 control_response / API 半连接 等场景下不响应 SIGTERM,
|
|
@@ -46550,6 +46609,9 @@ function makeInitialState(file, subSessionMeta) {
|
|
|
46550
46609
|
procAlive: false,
|
|
46551
46610
|
seenUuids: [],
|
|
46552
46611
|
subSessionMeta,
|
|
46612
|
+
// 后台任务台账(close-stdin 判据);由 subagent_progress 事件流维护,spawn 时天然为空
|
|
46613
|
+
pendingBgTasks: [],
|
|
46614
|
+
stdinClosed: false,
|
|
46553
46615
|
// ReadyGate v2:默认值兼容 SDK happy path(直写 stdin,不走队列)。
|
|
46554
46616
|
// TUI 模式由 reducer send 分支按 deps.mode='tui' 主动翻 readyForSend=false
|
|
46555
46617
|
readyForSend: true,
|
|
@@ -62216,7 +62278,7 @@ function computeMethodAccess(args) {
|
|
|
62216
62278
|
}
|
|
62217
62279
|
|
|
62218
62280
|
// src/version.ts
|
|
62219
|
-
var version = "0.2.
|
|
62281
|
+
var version = "0.2.415".length > 0 ? "0.2.415" : "dev";
|
|
62220
62282
|
|
|
62221
62283
|
// src/cli-probe/probe.ts
|
|
62222
62284
|
var fs65 = __toESM(require("fs"), 1);
|