@cocorograph/hub-agent 0.8.40 → 0.8.41
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 +47 -6
- package/src/main.mjs +28 -3
package/package.json
CHANGED
|
@@ -56,6 +56,11 @@ import { readPositiveIntEnv, sanitizePositiveInt } from "./positive-int-env.mjs"
|
|
|
56
56
|
import { nextSessionSeq } from "./session-seq.mjs"
|
|
57
57
|
import { codexRolloutPresence, listCodexThreadsFromDisk } from "./usage.mjs"
|
|
58
58
|
|
|
59
|
+
function parseTurnMismatch(reason) {
|
|
60
|
+
const mismatch = /^expected active turn id (\S+) but found (\S+)$/.exec(reason)
|
|
61
|
+
return mismatch ? { expected: mismatch[1], found: mismatch[2] } : null
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
/**
|
|
60
65
|
* sibling-inject (2026-07-04) 用: spawn する codex app-server プロセス自身の
|
|
61
66
|
* 環境変数に、この cwd の capability トークンを乗せる。ディスクには書かず、
|
|
@@ -2463,15 +2468,51 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
2463
2468
|
if (!session.dead) this._emitQueueState(session)
|
|
2464
2469
|
}
|
|
2465
2470
|
|
|
2466
|
-
/**
|
|
2471
|
+
/**
|
|
2472
|
+
* @returns {Promise<
|
|
2473
|
+
* | { outcome: "interrupted", turnId: string, corrected: boolean }
|
|
2474
|
+
* | { outcome: "no_active_turn" }
|
|
2475
|
+
* | { outcome: "failed", reason: string }
|
|
2476
|
+
* >}
|
|
2477
|
+
*/
|
|
2467
2478
|
async interrupt({ stream_id }) {
|
|
2468
2479
|
const session = this._require(stream_id)
|
|
2469
2480
|
this._touch(session, stream_id)
|
|
2470
|
-
if (!session.activeTurnId) return
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2481
|
+
if (!session.activeTurnId) return { outcome: "no_active_turn" }
|
|
2482
|
+
|
|
2483
|
+
const turnId = session.activeTurnId
|
|
2484
|
+
try {
|
|
2485
|
+
await session.client.request("turn/interrupt", {
|
|
2486
|
+
threadId: session.threadId,
|
|
2487
|
+
turnId,
|
|
2488
|
+
})
|
|
2489
|
+
return { outcome: "interrupted", turnId, corrected: false }
|
|
2490
|
+
} catch (err) {
|
|
2491
|
+
const reason = err?.message || String(err)
|
|
2492
|
+
const mismatch = parseTurnMismatch(reason)
|
|
2493
|
+
if (!mismatch) return { outcome: "failed", reason }
|
|
2494
|
+
|
|
2495
|
+
const correctedTurnId = mismatch.found
|
|
2496
|
+
// 是正は再送前に行い、再送中の corrected turn の完了通知を正しく照合できるようにする。
|
|
2497
|
+
// 初回リクエスト中に状態が進んでいた場合は、その新しい状態を上書きしない。
|
|
2498
|
+
if (session.activeTurnId === turnId) {
|
|
2499
|
+
session.activeTurnId = correctedTurnId
|
|
2500
|
+
}
|
|
2501
|
+
try {
|
|
2502
|
+
await session.client.request("turn/interrupt", {
|
|
2503
|
+
threadId: session.threadId,
|
|
2504
|
+
turnId: correctedTurnId,
|
|
2505
|
+
})
|
|
2506
|
+
return { outcome: "interrupted", turnId: correctedTurnId, corrected: true }
|
|
2507
|
+
} catch (retryErr) {
|
|
2508
|
+
const retryReason = retryErr?.message || String(retryErr)
|
|
2509
|
+
const retryMismatch = parseTurnMismatch(retryReason)
|
|
2510
|
+
if (retryMismatch && session.activeTurnId === correctedTurnId) {
|
|
2511
|
+
session.activeTurnId = retryMismatch.found
|
|
2512
|
+
}
|
|
2513
|
+
return { outcome: "failed", reason: retryReason }
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2475
2516
|
}
|
|
2476
2517
|
|
|
2477
2518
|
/** サーバー発の承認/入力要求への応答。result の shape は method ごとに呼び出し側が組み立てる。 */
|
package/src/main.mjs
CHANGED
|
@@ -3194,6 +3194,30 @@ export function handleCodexChatInput(msg, ctx) {
|
|
|
3194
3194
|
return p
|
|
3195
3195
|
}
|
|
3196
3196
|
|
|
3197
|
+
export async function handleCodexInterrupt(msg, ctx) {
|
|
3198
|
+
let result
|
|
3199
|
+
if (!ctx.codexBridge) {
|
|
3200
|
+
result = { outcome: "failed", reason: "bridge_unavailable" }
|
|
3201
|
+
} else {
|
|
3202
|
+
try {
|
|
3203
|
+
result = await ctx.codexBridge.interrupt({ stream_id: msg.stream_id })
|
|
3204
|
+
if (result.outcome === "failed") {
|
|
3205
|
+
ctx.logger?.warn?.({ err: result.reason }, "codex.interrupt failed")
|
|
3206
|
+
}
|
|
3207
|
+
} catch (err) {
|
|
3208
|
+
const reason = err?.message || String(err)
|
|
3209
|
+
ctx.logger?.warn?.({ err: reason }, "codex.interrupt failed")
|
|
3210
|
+
result = { outcome: "failed", reason }
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
ctx.client.send({
|
|
3214
|
+
type: "codex.interrupt.response",
|
|
3215
|
+
stream_id: msg.stream_id,
|
|
3216
|
+
request_id: msg.request_id ?? null,
|
|
3217
|
+
...result,
|
|
3218
|
+
})
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3197
3221
|
/**
|
|
3198
3222
|
* tmux.kill_session の処理 (症状3 根治)。要求された session を kill し、要求元へ
|
|
3199
3223
|
* tmux.kill_session.result を返したうえで、kill できた各 session について
|
|
@@ -4333,9 +4357,10 @@ async function dispatch(msg, ctx) {
|
|
|
4333
4357
|
})
|
|
4334
4358
|
return
|
|
4335
4359
|
case "codex.interrupt":
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4360
|
+
// fire-and-forget だが async なので、送信失敗を未処理 rejection にしない
|
|
4361
|
+
// (handleBundleSyncRequest と同じ作法)。停止経路が例外で無言死しないための保険。
|
|
4362
|
+
handleCodexInterrupt(msg, ctx).catch((err) => {
|
|
4363
|
+
ctx.logger?.warn?.({ err: err?.message || String(err) }, "codex.interrupt handler failed")
|
|
4339
4364
|
})
|
|
4340
4365
|
return
|
|
4341
4366
|
case "codex.detach":
|