@clawos-dev/clawd 0.2.162-beta.338.ff6197c → 0.2.163-beta.339.74a8a46

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 +36 -4
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -4933,7 +4933,8 @@ var init_schemas = __esm({
4933
4933
  // ① SDK 模式 stream-json `result` 帧(claude.ts)—— stdout 最后一帧,可靠
4934
4934
  // ② TUI 模式 observer 读 JSONL `system/turn_duration` 行(claude-tui.ts parseLine)
4935
4935
  // ⚠️ CC 写此行早于该轮尾段正文落盘,可能被尾随 text 覆盖 lastEventKind(③ 存在的原因)
4936
- // ③ TUI 模式屏幕内容静止(observeScreenIdle manager.dispatchTurnIdle inject)—— 兜 ② 的覆盖
4936
+ // ③ TUI 模式屏幕静止 AND observer 静止(observeScreenIdle + manager.observerIdleWaitMs 复合闸
4937
+ // dispatchTurnIdle inject)—— 兜 ② 的覆盖;等尾随 text 全部 poll 落盘后补,排在 buffer 末条
4937
4938
  external_exports.object({
4938
4939
  ...ParsedEventBase,
4939
4940
  kind: external_exports.literal("turn_end")
@@ -33535,6 +33536,9 @@ var SessionManager = class {
33535
33536
  // 由 observer 监听 jsonl user 行后调 recordRealUserUuid 建立映射;rewind 系列 RPC 在
33536
33537
  // 入参 / 出参做转译,保证 UI 看到的 uuid 始终是 events 流里的 synth uuid
33537
33538
  realUuidBySynth = /* @__PURE__ */ new Map();
33539
+ // observeScreenIdle 复合条件闸用:sessionId → observer 上次喂出业务事件的时刻(deps.now())。
33540
+ // observerIdleWaitMs 据此判 observer 是否也静止 ≥ idleMs(屏幕静止 AND observer 静止才补 turn_end)。
33541
+ lastObserverEventAt = /* @__PURE__ */ new Map();
33538
33542
  // SessionStore 按 scope 派生(root = <dataDir>/sessions/<scopeSubPath>/)。
33539
33543
  // default scope 直接复用 deps.store;persona scope(owner / listener)第一次访问时按需创建并缓存。
33540
33544
  // 取代旧的 storesByAgent —— agentId 概念由 SessionScope 取代,路径即身份,
@@ -34126,6 +34130,7 @@ var SessionManager = class {
34126
34130
  this.runners.delete(args.sessionId);
34127
34131
  this.realUuidBySynth.delete(args.sessionId);
34128
34132
  this.lastUiSizeBySessionId.delete(args.sessionId);
34133
+ this.lastObserverEventAt.delete(args.sessionId);
34129
34134
  return { response: { sessionId: args.sessionId }, broadcast };
34130
34135
  }
34131
34136
  this.deleteOwned(args.sessionId);
@@ -34604,6 +34609,7 @@ var SessionManager = class {
34604
34609
  this.runners.delete(args.sessionId);
34605
34610
  this.realUuidBySynth.delete(args.sessionId);
34606
34611
  this.lastUiSizeBySessionId.delete(args.sessionId);
34612
+ this.lastObserverEventAt.delete(args.sessionId);
34607
34613
  return { response: { sessionId: args.sessionId }, broadcast };
34608
34614
  }
34609
34615
  this.storeFor(args.scope).delete(args.sessionId);
@@ -34755,6 +34761,7 @@ var SessionManager = class {
34755
34761
  return;
34756
34762
  }
34757
34763
  }
34764
+ this.lastObserverEventAt.set(sessionId, (this.deps.now ?? Date.now)());
34758
34765
  runner.feedObserverEvents(outEvents);
34759
34766
  }
34760
34767
  // AskUserQuestion 表单回写(plan: clawd-ask-user-question):UI 答完所有 question 后调用。
@@ -35030,6 +35037,20 @@ var SessionManager = class {
35030
35037
  if (!runner) return;
35031
35038
  runner.input({ kind: "inject-events", events: [{ kind: "turn_end" }] });
35032
35039
  }
35040
+ /**
35041
+ * observer 还需静止多久(ms)才满 idleMs,0 = 已满。observeScreenIdle 复合条件闸:屏幕静止后
35042
+ * 精确等这段剩余再补 turn_end —— turn_duration 写盘早于尾段正文,observer 把尾随 text poll 落盘
35043
+ * 期间屏幕可能已静止,仅看屏幕会早 fire(补的 turn_end 盖不到尾随 text 之后)。
35044
+ * 找不到 runner / 从无事件 → 0(不阻塞 fire)。idleMs 由装配处传 SCREEN_IDLE_MS。
35045
+ */
35046
+ observerIdleWaitMs(toolSessionId, idleMs) {
35047
+ const sid = this.sessionIdByToolSid(toolSessionId);
35048
+ if (!sid) return 0;
35049
+ const last = this.lastObserverEventAt.get(sid);
35050
+ if (last === void 0) return 0;
35051
+ const elapsed = (this.deps.now ?? Date.now)() - last;
35052
+ return Math.max(0, idleMs - elapsed);
35053
+ }
35033
35054
  /** toolSessionId → sessionId 反查(遍历 runners);session 数典型 < 10,O(n) 可接受 */
35034
35055
  sessionIdByToolSid(toolSessionId) {
35035
35056
  for (const [sid, runner] of this.runners) {
@@ -37396,6 +37417,7 @@ function observeReady(surface, opts) {
37396
37417
  }
37397
37418
  function observeScreenIdle(surface, opts) {
37398
37419
  const visibleRows = opts.visibleRows ?? 44;
37420
+ const REWAIT_MIN_MS = 50;
37399
37421
  let lastSnap = null;
37400
37422
  let armed = false;
37401
37423
  let timer = null;
@@ -37414,6 +37436,11 @@ function observeScreenIdle(surface, opts) {
37414
37436
  timer = setTimeout(fire, opts.idleMs);
37415
37437
  return;
37416
37438
  }
37439
+ const obsWait = opts.getObserverWaitMs?.() ?? 0;
37440
+ if (obsWait > 0) {
37441
+ timer = setTimeout(fire, Math.max(obsWait, REWAIT_MIN_MS));
37442
+ return;
37443
+ }
37417
37444
  if (armed) return;
37418
37445
  armed = true;
37419
37446
  opts.onIdle();
@@ -37436,7 +37463,7 @@ function observeScreenIdle(surface, opts) {
37436
37463
  };
37437
37464
  }
37438
37465
  var BYPASS_SETTLE_MS = 300;
37439
- var SCREEN_IDLE_MS = 1200;
37466
+ var SCREEN_IDLE_MS = 5e3;
37440
37467
  function createBootGate(pty, logger) {
37441
37468
  const stripAnsi = (s) => s.replace(/\x1b\[[0-9;?]*C/g, " ").replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\x1b[NOPM=>78]/g, "").replace(/[\x00-\x1f\x7f]/g, " ");
37442
37469
  const popups = {
@@ -37575,7 +37602,10 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
37575
37602
  this.tuiLogger?.debug("screen-idle \u2192 turn_end", { toolSessionId: ctx.toolSessionId });
37576
37603
  this.tuiOpts.onTurnIdle(ctx.toolSessionId);
37577
37604
  },
37578
- getPopupVisible: () => popupObserver.visibleKind !== null
37605
+ getPopupVisible: () => popupObserver.visibleKind !== null,
37606
+ // observer 还需静止多久才满 SCREEN_IDLE_MS(复合条件 AND):屏幕静止后精确等这段剩余再补
37607
+ // turn_end,确保它排在尾段 text + turn_duration 全部 poll 落盘之后 = buffer 末条。
37608
+ getObserverWaitMs: () => ctx.toolSessionId ? this.tuiOpts.getObserverWaitMs?.(ctx.toolSessionId, SCREEN_IDLE_MS) ?? 0 : 0
37579
37609
  });
37580
37610
  if (ctx.toolSessionId && this.tuiOpts.onSurfaceRegister) {
37581
37611
  this.tuiOpts.onSurfaceRegister(ctx.toolSessionId, surface);
@@ -46896,7 +46926,9 @@ async function startDaemon(config) {
46896
46926
  // ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
46897
46927
  onReady: (tsid) => manager.dispatchReadyDetected(tsid),
46898
46928
  // 屏幕静止 → 补权威 turn_end(修 turn_duration 尾随 text 覆盖 lastEventKind)
46899
- onTurnIdle: (tsid) => manager.dispatchTurnIdle(tsid)
46929
+ onTurnIdle: (tsid) => manager.dispatchTurnIdle(tsid),
46930
+ // 复合条件闸:observer 还需静止多久才满 idleMs(屏幕静止后精确等这段剩余再补 turn_end)
46931
+ getObserverWaitMs: (tsid, idleMs) => manager.observerIdleWaitMs(tsid, idleMs)
46900
46932
  }) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
46901
46933
  registerAdapter("claude", claudeAdapter);
46902
46934
  registerAdapter("codex", new CodexAdapter({ logger, historyReader: new CodexHistoryReader() }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.162-beta.338.ff6197c",
3
+ "version": "0.2.163-beta.339.74a8a46",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",