@cocorograph/hub-agent 0.7.91 → 0.7.92

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.7.91",
3
+ "version": "0.7.92",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -438,6 +438,9 @@ export class CodexStreamBridge extends EventEmitter {
438
438
  attachId: attachId || null,
439
439
  client,
440
440
  threadId: null,
441
+ // 新規 thread/start は、最初の usage.response を受け取るまで context 使用量が 0%。
442
+ // state loop が同じ cwd の古い TUI 値を拾って上書きしないための境界マーカー。
443
+ isFresh: !resumeThreadId,
441
444
  // T06541 major#3: cwd reattach の in-flight 引き当てキー。thread/resume 完了まで threadId は
442
445
  // null なので、並行 cold attach が「同一 (cwd, resumeThreadId)」を検出できるよう resume 対象の
443
446
  // thread id を **client 生成前 (同期)** に持たせる。これで後続 attach は _findLiveSessionByCwd で
package/src/main.mjs CHANGED
@@ -2557,12 +2557,26 @@ export function startStateLoop({ client, plugins, logger, intervalMs, claudeBrid
2557
2557
  !paneIsProcessing
2558
2558
  ) {
2559
2559
  const liveCtx = codexLiveContextForProducer(codexAuthSession.seq)
2560
- if (typeof liveCtx === "number") contextPct = liveCtx
2560
+ if (typeof liveCtx === "number") {
2561
+ contextPct = liveCtx
2562
+ } else if (codexAuthSession.isFresh === true) {
2563
+ // 新規 Codex thread は thread/start の時点で ID だけ確定し、最初の
2564
+ // usage.response までは使用量がない。この間に tmux の旧 pane 値を
2565
+ // 同じ新 thread ID で送ると、即時 push した 0% を打ち消してしまう。
2566
+ contextPct = 0
2567
+ }
2561
2568
  }
2569
+ // fresh bind は実 session_id がまだ確定していない。tmux.list の statusLine / cwd 最新
2570
+ // JSONL は直前会話の値を返し得るため、この間は必ず 0% を正本にする。これをしないと
2571
+ // bind の即時 0% snapshot の直後に state loop が旧値を同じ fresh:<id> で再送して
2572
+ // ドーナツを復活させる。
2573
+ const freshUnbound =
2574
+ sessionCliKind === "claude" && isFreshUnboundBind(boundSessions?.get(s.session_name))
2575
+ if (freshUnbound) contextPct = 0
2562
2576
  const turnActiveSource = decideTurnActiveSource({
2563
2577
  chatStatus: chat?.status,
2564
2578
  cwd: s.cwd,
2565
- isFreshUnbound: isFreshUnboundBind(boundSessions?.get(s.session_name)),
2579
+ isFreshUnbound: freshUnbound,
2566
2580
  codexThreadId,
2567
2581
  sessionCliKind,
2568
2582
  })
package/src/tmux.mjs CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  getSessionCwd,
30
30
  } from "./state.mjs"
31
31
  import { baseSessionName, sessionNameFor } from "./session-names.mjs"
32
- import { boundSessionId, getSessionUsages, jsonlContextForCwd } from "./usage.mjs"
32
+ import { boundSessionId, getSessionUsages, isFreshUnboundBind, jsonlContextForCwd } from "./usage.mjs"
33
33
 
34
34
  const execFileP = promisify(execFile)
35
35
 
@@ -681,13 +681,17 @@ export async function listSessions(opts = {}) {
681
681
  // tmux 上の bind 台帳が無いため、これが無いと cold-start (session.state snapshot 未取得)
682
682
  // の poll 応答で cwd の別アクティビティ jsonl を「最新」と取り違える cross-activity 汚染が
683
683
  // 起き得る (2026-07 発覚。TUI 版は本行の boundSessions 解決で既に根治済みだった)。
684
+ const boundValue = opts.boundSessions?.get(s.name)
685
+ const freshUnbound = isFreshUnboundBind(boundValue)
684
686
  const boundId =
685
687
  boundSessionId(opts.boundSessions, s.name) ||
686
688
  (cwd && opts.claudeBridge?.liveSessionIdForCwd?.(cwd)) ||
687
689
  null
688
690
  const jsonlInfo = cwd ? await jsonlContextForCwd(cwd, boundId) : null
689
691
  const context_pct =
690
- jsonlInfo?.isReset && typeof jsonlInfo.percent === "number"
692
+ freshUnbound
693
+ ? 0
694
+ : jsonlInfo?.isReset && typeof jsonlInfo.percent === "number"
691
695
  ? jsonlInfo.percent
692
696
  : typeof fromStatusLine === "number"
693
697
  ? fromStatusLine