@cocorograph/hub-agent 0.8.71 → 0.8.72
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 +32 -6
- package/src/main.mjs +16 -2
package/package.json
CHANGED
|
@@ -980,6 +980,8 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
980
980
|
/** 取消成功 id は accepted(二重実行ガード)から外さず、browser が「accepted +キュー不在」
|
|
981
981
|
* のバブル昇格から除外するための別軸として保持する。 */
|
|
982
982
|
cancelledInputIds: [],
|
|
983
|
+
/** 取消が input より先着した場合の guard。後着 input で消費する。 */
|
|
984
|
+
canceledInputIds: new Set(),
|
|
983
985
|
/** @type {{resolve: Function, timer: any} | null} round3 major#2: turn/start 後に turn/started
|
|
984
986
|
* (activeTurnId 確定) を待つ barrier の待機者。timeout 付きでハングを防ぐ。 */
|
|
985
987
|
_turnActiveWaiter: null,
|
|
@@ -2166,16 +2168,34 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
2166
2168
|
/** 次ターン FIFO に残っている browser 入力を id 指定で取り消す。
|
|
2167
2169
|
* dispatchingInput は既に turn/start RPC 中なので対象外(実行開始済みとして扱う)。 */
|
|
2168
2170
|
cancelQueued({ stream_id, id }) {
|
|
2169
|
-
const session = this.
|
|
2170
|
-
if (!
|
|
2171
|
+
const session = this.sessions.get(stream_id)
|
|
2172
|
+
if (!session || session.dead) {
|
|
2173
|
+
return { outcome: "stream_missing", flush: () => {} }
|
|
2174
|
+
}
|
|
2175
|
+
if (typeof id !== "string" || !id) throw new Error("invalid_input_id")
|
|
2171
2176
|
const index = session.inputQueue.findIndex(
|
|
2172
2177
|
(item) =>
|
|
2173
2178
|
(item?.inputId === id || item?.deliveryId === id) &&
|
|
2174
2179
|
item !== session.dispatchingInput,
|
|
2175
2180
|
)
|
|
2176
2181
|
if (index < 0) {
|
|
2177
|
-
|
|
2178
|
-
|
|
2182
|
+
// accepted は二重実行防止の記録。取消済みなら再要求でも実行開始済みに変えない。
|
|
2183
|
+
if (session.cancelledInputIds?.includes(id)) {
|
|
2184
|
+
return { outcome: "cancelled", flush: () => this._emitQueueState(session) }
|
|
2185
|
+
}
|
|
2186
|
+
const dispatching = session.dispatchingInput
|
|
2187
|
+
const alreadyStarted =
|
|
2188
|
+
dispatching?.inputId === id ||
|
|
2189
|
+
dispatching?.deliveryId === id ||
|
|
2190
|
+
session.acceptedInputIds?.includes(id)
|
|
2191
|
+
if (!alreadyStarted) {
|
|
2192
|
+
if (session.canceledInputIds.size >= 1024) session.canceledInputIds.clear()
|
|
2193
|
+
session.canceledInputIds.add(id)
|
|
2194
|
+
}
|
|
2195
|
+
return {
|
|
2196
|
+
outcome: alreadyStarted ? "already_started" : "cancelled",
|
|
2197
|
+
flush: () => this._emitQueueState(session),
|
|
2198
|
+
}
|
|
2179
2199
|
}
|
|
2180
2200
|
const [item] = session.inputQueue.splice(index, 1)
|
|
2181
2201
|
this._recordCancelledInput(session, item.inputId || item.deliveryId)
|
|
@@ -2185,8 +2205,8 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
2185
2205
|
})
|
|
2186
2206
|
item.reject(err)
|
|
2187
2207
|
}
|
|
2188
|
-
|
|
2189
|
-
return
|
|
2208
|
+
// main が outcome を送った後で snapshot を送る。
|
|
2209
|
+
return { outcome: "cancelled", flush: () => this._emitQueueState(session) }
|
|
2190
2210
|
}
|
|
2191
2211
|
|
|
2192
2212
|
_emitQueueState(session, startedItem = null) {
|
|
@@ -3573,6 +3593,12 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
3573
3593
|
this._touch(session, stream_id)
|
|
3574
3594
|
const input = userInputFromMessage(message)
|
|
3575
3595
|
const text = input.map((part) => part?.text || "").join("\n")
|
|
3596
|
+
if (inputId && session.canceledInputIds.delete(inputId)) {
|
|
3597
|
+
this._recordAcceptedInput(session, inputId)
|
|
3598
|
+
this._recordCancelledInput(session, inputId)
|
|
3599
|
+
this._emitQueueState(session)
|
|
3600
|
+
return { method: "queued", deliveryId }
|
|
3601
|
+
}
|
|
3576
3602
|
if (inputId && session.acceptedInputIds?.includes(inputId)) {
|
|
3577
3603
|
return { method: session.activeTurnId ? "queued" : "turn/start", deliveryId }
|
|
3578
3604
|
}
|
package/src/main.mjs
CHANGED
|
@@ -2331,6 +2331,8 @@ export function buildCodexReadyPayload(streamId, info, queueState) {
|
|
|
2331
2331
|
// Browser は旧 hub-agent との混在期間、この明示 capability が true のときだけ
|
|
2332
2332
|
// 子 thread の個別停止 UI を有効化する。省略は未対応を意味する。
|
|
2333
2333
|
supports_subagent_interrupt: true,
|
|
2334
|
+
// 旧 agent との混在時、取消結果を待てることを明示する。
|
|
2335
|
+
supports_queue_cancel_response: true,
|
|
2334
2336
|
}
|
|
2335
2337
|
if (Number.isInteger(info?.historyStart)) payload.history_start = info.historyStart
|
|
2336
2338
|
if (Number.isInteger(info?.historyTotal)) payload.history_total = info.historyTotal
|
|
@@ -4797,9 +4799,16 @@ export async function dispatch(msg, ctx) {
|
|
|
4797
4799
|
// turn/steer / turn/interrupt は明示操作の別経路として扱う。
|
|
4798
4800
|
handleCodexChatInput(msg, ctx)
|
|
4799
4801
|
return
|
|
4800
|
-
case "codex.queue.cancel":
|
|
4802
|
+
case "codex.queue.cancel": {
|
|
4803
|
+
const cancelRes = {
|
|
4804
|
+
type: "codex.queue.response",
|
|
4805
|
+
stream_id: msg.stream_id,
|
|
4806
|
+
request_id: msg.request_id || null,
|
|
4807
|
+
input_id: msg.id || null,
|
|
4808
|
+
}
|
|
4809
|
+
let canceled
|
|
4801
4810
|
try {
|
|
4802
|
-
ctx.codexBridge?.cancelQueued({
|
|
4811
|
+
canceled = ctx.codexBridge?.cancelQueued({
|
|
4803
4812
|
stream_id: msg.stream_id,
|
|
4804
4813
|
id: msg.id,
|
|
4805
4814
|
})
|
|
@@ -4808,8 +4817,13 @@ export async function dispatch(msg, ctx) {
|
|
|
4808
4817
|
{ err: err?.message, stream_id: msg.stream_id, input_id: msg.id },
|
|
4809
4818
|
"codex queued input cancel failed",
|
|
4810
4819
|
)
|
|
4820
|
+
ctx.client.send({ ...cancelRes, error: err?.message || "cancel_failed" })
|
|
4821
|
+
return
|
|
4811
4822
|
}
|
|
4823
|
+
ctx.client.send({ ...cancelRes, outcome: canceled?.outcome || "stream_missing" })
|
|
4824
|
+
canceled?.flush?.()
|
|
4812
4825
|
return
|
|
4826
|
+
}
|
|
4813
4827
|
case "codex.permission.reply":
|
|
4814
4828
|
if (!ctx.codexBridge) return
|
|
4815
4829
|
ctx.codexBridge.permissionReply({
|