@clawos-dev/clawd 0.2.165-beta.341.c72ef11 → 0.2.166-beta.342.3eade95
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 +76 -7
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -4710,6 +4710,9 @@ var init_schemas = __esm({
|
|
|
4710
4710
|
// 不受 parent clear / re-spawn 影响);sidebar 据此挂 fork 图标。非 fork session 该字段缺省。
|
|
4711
4711
|
// 旧字段 forkedFromToolSessionId 已弃用,daemon 启动时一次性迁移老数据
|
|
4712
4712
|
forkedFromSessionId: external_exports.string().min(1).optional(),
|
|
4713
|
+
// 快速问答临时会话标记(spec 2026-06-23-fork-quick-ask):true = 关闭抽屉即删、
|
|
4714
|
+
// 不进 sidebar 树、daemon 启动清扫孤儿。普通会话缺省。
|
|
4715
|
+
ephemeral: external_exports.boolean().optional(),
|
|
4713
4716
|
// owner-mode persona session 身份标识;UI 用它在 SessionList 过滤 + jump,
|
|
4714
4717
|
// daemon 用它做 idempotent dedupe + spawn 时派生 ctx.personaMode='owner'
|
|
4715
4718
|
ownerPersonaId: external_exports.string().min(1).optional(),
|
|
@@ -4998,6 +5001,8 @@ var init_schemas = __esm({
|
|
|
4998
5001
|
iconKey: external_exports.string().optional(),
|
|
4999
5002
|
// session:fork 派生 session 时由 UI 传入源 sessionId,daemon 写到 SessionFile.forkedFromSessionId
|
|
5000
5003
|
forkedFromSessionId: external_exports.string().min(1).optional(),
|
|
5004
|
+
// 快速问答:创建时打 ephemeral 标记,daemon 写到 SessionFile.ephemeral
|
|
5005
|
+
ephemeral: external_exports.boolean().optional(),
|
|
5001
5006
|
// owner-mode persona session:传此字段时 daemon 自行派生 cwd 和启动参数,
|
|
5002
5007
|
// 且按 ownerPersonaId 做 idempotent dedupe(每 persona 永远只有一个 owner session)
|
|
5003
5008
|
ownerPersonaId: external_exports.string().min(1).optional()
|
|
@@ -33968,6 +33973,7 @@ var SessionManager = class {
|
|
|
33968
33973
|
forkedFromSessionId: args.forkedFromSessionId,
|
|
33969
33974
|
ownerPersonaId: args.ownerPersonaId,
|
|
33970
33975
|
appBuilderProject: args.appBuilderProject,
|
|
33976
|
+
ephemeral: args.ephemeral,
|
|
33971
33977
|
creatorPrincipalId,
|
|
33972
33978
|
creatorDisplayName,
|
|
33973
33979
|
createdAt: iso,
|
|
@@ -34136,6 +34142,19 @@ var SessionManager = class {
|
|
|
34136
34142
|
this.writeOwned(updated);
|
|
34137
34143
|
return { response: updated, broadcast: [] };
|
|
34138
34144
|
}
|
|
34145
|
+
/**
|
|
34146
|
+
* 启动清扫:删除所有磁盘残留的 ephemeral(快速问答临时)session。
|
|
34147
|
+
* 客户端崩溃 / 没关抽屉会留下永久不可见的孤儿(ephemeral 被 sidebar 过滤,用户自己也删不掉)。
|
|
34148
|
+
* 启动时调用——此刻无客户端连接,纯文件删除,不广播。
|
|
34149
|
+
*/
|
|
34150
|
+
sweepEphemeralOnStartup() {
|
|
34151
|
+
for (const file of this.listAllOwned()) {
|
|
34152
|
+
if (file.ephemeral) {
|
|
34153
|
+
this.deleteOwned(file.sessionId);
|
|
34154
|
+
this.deps.logger?.info?.(`swept ephemeral session ${file.sessionId}`);
|
|
34155
|
+
}
|
|
34156
|
+
}
|
|
34157
|
+
}
|
|
34139
34158
|
delete(args) {
|
|
34140
34159
|
const existing = this.findOwnedSession(args.sessionId);
|
|
34141
34160
|
if (!existing) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, args.sessionId);
|
|
@@ -34791,7 +34810,21 @@ var SessionManager = class {
|
|
|
34791
34810
|
}
|
|
34792
34811
|
}
|
|
34793
34812
|
this.lastObserverEventAt.set(sessionId, (this.deps.now ?? Date.now)());
|
|
34794
|
-
|
|
34813
|
+
let feedEvents = outEvents;
|
|
34814
|
+
if (outEvents.some((e) => e.kind === "turn_end")) {
|
|
34815
|
+
const ev = this.peekTurnEvidence(runner);
|
|
34816
|
+
if (!ev.turnHasContent) {
|
|
34817
|
+
feedEvents = outEvents.filter((e) => e.kind !== "turn_end");
|
|
34818
|
+
this.deps.logger?.info("[TE-PROBE] drop spurious observer turn_end", {
|
|
34819
|
+
sessionId,
|
|
34820
|
+
src: "observer",
|
|
34821
|
+
...ev,
|
|
34822
|
+
batchKinds: outEvents.map((e) => e.kind)
|
|
34823
|
+
});
|
|
34824
|
+
}
|
|
34825
|
+
}
|
|
34826
|
+
if (feedEvents.length === 0) return;
|
|
34827
|
+
runner.feedObserverEvents(feedEvents);
|
|
34795
34828
|
}
|
|
34796
34829
|
// AskUserQuestion 表单回写(plan: clawd-ask-user-question):UI 答完所有 question 后调用。
|
|
34797
34830
|
// - session 不存在 / 无 runner → noop 幂等返回 ok(first-decider-wins)
|
|
@@ -35052,20 +35085,55 @@ var SessionManager = class {
|
|
|
35052
35085
|
runner.input({ kind: "ready-detected" });
|
|
35053
35086
|
}
|
|
35054
35087
|
/**
|
|
35055
|
-
* ClaudeTuiAdapter onTurnIdle callback
|
|
35056
|
-
*
|
|
35057
|
-
*
|
|
35058
|
-
*
|
|
35059
|
-
*
|
|
35060
|
-
*
|
|
35088
|
+
* ClaudeTuiAdapter onTurnIdle callback:屏幕内容静止时**复发**本轮已出现过的权威 turn_end。
|
|
35089
|
+
* 本意:turn_duration 写盘早于尾段正文 → observer 把尾随 text 推进 buffer 盖掉 lastEventKind →
|
|
35090
|
+
* spinner 不熄;屏幕静止时再补一条 turn_end 排到尾随 text 之后。
|
|
35091
|
+
*
|
|
35092
|
+
* Fix A(修 bug1 "UI 还在变却显示结束" / bug2 "发消息后无 spinner"):补偿**只复发不 originate**——
|
|
35093
|
+
* 仅当本轮已出现过 turn_end(turnEndSeenThisTurn,即真有过尾随 text 覆盖场景)才补。turnEndSeenThisTurn
|
|
35094
|
+
* ===false 说明本轮 CC 从未结束过(仍在工作 / 刚发消息没产出 / 漏检弹框等用户),屏幕静止 ≠ turn 结束,
|
|
35095
|
+
* 凭空 inject turn_end 会误灭 spinner——故跳过,让真正的 turn_duration 来时再正常收。
|
|
35096
|
+
* 仅 tui 模式;runner 缺失 noop。turn_end 无 uuid 不参与 dedup。
|
|
35061
35097
|
*/
|
|
35062
35098
|
dispatchTurnIdle(toolSessionId) {
|
|
35063
35099
|
if (this.deps.mode !== "tui") return;
|
|
35064
35100
|
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
35065
35101
|
const runner = sid ? this.runners.get(sid) : void 0;
|
|
35066
35102
|
if (!runner) return;
|
|
35103
|
+
const ev = this.peekTurnEvidence(runner);
|
|
35104
|
+
const willInject = ev.turnEndSeenThisTurn;
|
|
35105
|
+
this.deps.logger?.info("[TE-PROBE] screen-idle compensation", {
|
|
35106
|
+
sessionId: sid,
|
|
35107
|
+
src: "screen-idle",
|
|
35108
|
+
willInject,
|
|
35109
|
+
...ev
|
|
35110
|
+
});
|
|
35111
|
+
if (!willInject) return;
|
|
35067
35112
|
runner.input({ kind: "inject-events", events: [{ kind: "turn_end" }] });
|
|
35068
35113
|
}
|
|
35114
|
+
/**
|
|
35115
|
+
* 读 runner 当前 turn 态快照,供两个 turn_end 注入源(屏幕静止补偿 / observer turn_duration)
|
|
35116
|
+
* 判断误发 + 打 [TE-PROBE] 日志。
|
|
35117
|
+
* turnEndSeenThisTurn:从 buffer 末尾回扫到最近 user_text 期间是否已出现过 turn_end
|
|
35118
|
+
* (已出现=本轮真结束过、合法尾随;未出现=本轮还没结束过)→ Fix A 复发闸。
|
|
35119
|
+
* turnHasContent:末条是否 assistant 产出(非 user_text/turn_end/空)→ Fix B 空 turn 守卫闸。
|
|
35120
|
+
*/
|
|
35121
|
+
peekTurnEvidence(runner) {
|
|
35122
|
+
const st = runner.getState();
|
|
35123
|
+
const buf = st.buffer;
|
|
35124
|
+
const lastEventKindBefore = buf.length > 0 ? buf[buf.length - 1].event.kind : null;
|
|
35125
|
+
let turnEndSeenThisTurn = false;
|
|
35126
|
+
for (let i = buf.length - 1; i >= 0; i--) {
|
|
35127
|
+
const k2 = buf[i].event.kind;
|
|
35128
|
+
if (k2 === "user_text") break;
|
|
35129
|
+
if (k2 === "turn_end") {
|
|
35130
|
+
turnEndSeenThisTurn = true;
|
|
35131
|
+
break;
|
|
35132
|
+
}
|
|
35133
|
+
}
|
|
35134
|
+
const turnHasContent = lastEventKindBefore !== null && lastEventKindBefore !== "user_text" && lastEventKindBefore !== "turn_end";
|
|
35135
|
+
return { turnOpenBefore: st.turnOpen, lastEventKindBefore, turnEndSeenThisTurn, turnHasContent };
|
|
35136
|
+
}
|
|
35069
35137
|
/**
|
|
35070
35138
|
* observer 还需静止多久(ms)才满 idleMs,0 = 已满。observeScreenIdle 复合条件闸:屏幕静止后
|
|
35071
35139
|
* 精确等这段剩余再补 turn_end —— turn_duration 写盘早于尾段正文,observer 把尾随 text poll 落盘
|
|
@@ -47564,6 +47632,7 @@ async function startDaemon(config) {
|
|
|
47564
47632
|
}
|
|
47565
47633
|
}
|
|
47566
47634
|
});
|
|
47635
|
+
manager.sweepEphemeralOnStartup();
|
|
47567
47636
|
await wss.start();
|
|
47568
47637
|
const url = `ws://${config.host}:${config.port}`;
|
|
47569
47638
|
let stateSnapshot = {
|
package/package.json
CHANGED