





















@@ -0,0 +1,97 @@
1+import { embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
2+import type { CodexAppServerClient } from "./client.js";
3+import { retireSharedCodexAppServerClientIfCurrent } from "./shared-client.js";
4+5+export const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5_000;
6+export const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5_000;
7+8+export function interruptCodexTurnBestEffort(
9+client: CodexAppServerClient,
10+params: {
11+threadId: string;
12+turnId: string;
13+timeoutMs?: number;
14+},
15+): void {
16+const requestOptions =
17+params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0
18+ ? { timeoutMs: params.timeoutMs }
19+ : undefined;
20+const requestParams = { threadId: params.threadId, turnId: params.turnId };
21+try {
22+const interrupt = requestOptions
23+ ? client.request("turn/interrupt", requestParams, requestOptions)
24+ : client.request("turn/interrupt", requestParams);
25+void Promise.resolve(interrupt).catch((error: unknown) => {
26+embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
27+});
28+} catch (error) {
29+embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
30+}
31+}
32+33+export async function unsubscribeCodexThreadBestEffort(
34+client: CodexAppServerClient,
35+params: {
36+threadId: string;
37+timeoutMs: number;
38+},
39+): Promise<void> {
40+try {
41+await client.request(
42+"thread/unsubscribe",
43+{ threadId: params.threadId },
44+{ timeoutMs: params.timeoutMs },
45+);
46+} catch (error) {
47+embeddedAgentLog.debug("codex app-server thread unsubscribe cleanup failed", {
48+threadId: params.threadId,
49+ error,
50+});
51+}
52+}
53+54+export async function retireCodexAppServerClientAfterTimedOutTurn(
55+client: CodexAppServerClient,
56+params: {
57+threadId: string;
58+turnId: string;
59+reason: string;
60+},
61+): Promise<void> {
62+const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
63+const detachedSharedClient = Boolean(retiredSharedClient);
64+interruptCodexTurnBestEffort(client, {
65+threadId: params.threadId,
66+turnId: params.turnId,
67+timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS,
68+});
69+await unsubscribeCodexThreadBestEffort(client, {
70+threadId: params.threadId,
71+timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS,
72+});
73+let closedClient = retiredSharedClient?.closed ?? false;
74+if (!detachedSharedClient) {
75+const close = (client as { close?: () => void }).close;
76+if (typeof close === "function") {
77+try {
78+close.call(client);
79+closedClient = true;
80+} catch (error) {
81+embeddedAgentLog.debug("codex app-server client close failed during timeout cleanup", {
82+threadId: params.threadId,
83+turnId: params.turnId,
84+ error,
85+});
86+}
87+}
88+}
89+embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
90+threadId: params.threadId,
91+turnId: params.turnId,
92+reason: params.reason,
93+ detachedSharedClient,
94+ closedClient,
95+activeSharedClientLeases: retiredSharedClient?.activeLeases ?? 0,
96+});
97+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。