@cocorograph/hub-agent 0.7.91 → 0.7.93
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 +1 -1
- package/src/codex-stream-bridge.mjs +3 -0
- package/src/main.mjs +22 -6
- package/src/tmux.mjs +6 -2
package/package.json
CHANGED
|
@@ -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")
|
|
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:
|
|
2579
|
+
isFreshUnbound: freshUnbound,
|
|
2566
2580
|
codexThreadId,
|
|
2567
2581
|
sessionCliKind,
|
|
2568
2582
|
})
|
|
@@ -3513,19 +3527,21 @@ async function dispatch(msg, ctx) {
|
|
|
3513
3527
|
// (main.mjs 内 clearChatSignal 呼び出し箇所参照) と対称に、SDK チャットの
|
|
3514
3528
|
// attach 時点でも前セッションのチャット信号を引き継がない。
|
|
3515
3529
|
// - fresh (resume 無し): 新規セッションで session_id はまだ確定しない
|
|
3516
|
-
// (SDK が最初の assistant イベントで通知する)
|
|
3517
|
-
//
|
|
3530
|
+
// (SDK が最初の assistant イベントで通知する)。ただし null を送ると frontend
|
|
3531
|
+
// は「同一会話で一時的に0%になった」と判断して旧値を保持するため、stream_id
|
|
3532
|
+
// 由来の一意な仮IDを先に送る。実 session_id が来た時点で置換される。
|
|
3518
3533
|
// - resume: bound session の jsonl から実 context% を解決し、cli_kind='claude' +
|
|
3519
3534
|
// 実 session_id で identity を確定させて即時 push する (症状③)。
|
|
3520
3535
|
const resolvedCwd = cwdArg || process.env.HOME || process.cwd()
|
|
3521
3536
|
if (resolvedCwd) {
|
|
3522
3537
|
if (!resumeSessionId) {
|
|
3538
|
+
const freshContextSessionId = `fresh:${stream_id}`
|
|
3523
3539
|
clearChatSignal(resolvedCwd, "claude")
|
|
3524
3540
|
recordChatActivity(resolvedCwd, "claude", {
|
|
3525
3541
|
status: "waiting",
|
|
3526
3542
|
contextPct: 0,
|
|
3527
3543
|
inputPending: false,
|
|
3528
|
-
sessionId:
|
|
3544
|
+
sessionId: freshContextSessionId,
|
|
3529
3545
|
})
|
|
3530
3546
|
pushImmediateChatState({
|
|
3531
3547
|
client: ctx.client,
|
|
@@ -3533,7 +3549,7 @@ async function dispatch(msg, ctx) {
|
|
|
3533
3549
|
logger: ctx.logger,
|
|
3534
3550
|
cwd: resolvedCwd,
|
|
3535
3551
|
cliKind: "claude",
|
|
3536
|
-
sessionId:
|
|
3552
|
+
sessionId: freshContextSessionId,
|
|
3537
3553
|
contextPct: 0,
|
|
3538
3554
|
}).catch(() => {})
|
|
3539
3555
|
} else {
|
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
|
-
|
|
692
|
+
freshUnbound
|
|
693
|
+
? 0
|
|
694
|
+
: jsonlInfo?.isReset && typeof jsonlInfo.percent === "number"
|
|
691
695
|
? jsonlInfo.percent
|
|
692
696
|
: typeof fromStatusLine === "number"
|
|
693
697
|
? fromStatusLine
|