@scotthuang/agent-knock-knock 0.2.16 → 0.2.18
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/CHANGELOG.md +7 -0
- package/dist/src/cli.js +302 -9
- package/dist/src/cli.js.map +1 -1
- package/dist/src/openclaw-plugin.js +19 -6
- package/dist/src/openclaw-plugin.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.18 - 2026-06-30
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Classified Codex remote compact stream disconnects as recoverable executor failures instead of leaving AKK tasks stuck as generic `stalled` conversations.
|
|
8
|
+
- Allowed `AKK recover` to replay saved AKK history and the pending OpenClaw message after a recognized monitor-time executor failure.
|
|
9
|
+
|
|
3
10
|
## 0.2.13 - 2026-06-25
|
|
4
11
|
|
|
5
12
|
### Fixed
|
package/dist/src/cli.js
CHANGED
|
@@ -1589,6 +1589,26 @@ async function runSend(options) {
|
|
|
1589
1589
|
cleanupIdleConversations(storeDirFromOptions(options), options);
|
|
1590
1590
|
const terminalConversation = await resolveTerminalConversationFromOptions(options);
|
|
1591
1591
|
if (terminalConversation) {
|
|
1592
|
+
if (options.background) {
|
|
1593
|
+
const managed = createManagedTerminalConversationFromRawId({
|
|
1594
|
+
options,
|
|
1595
|
+
conversationId: terminalConversation.conversationId,
|
|
1596
|
+
messageBody,
|
|
1597
|
+
terminalControl: terminalConversation.terminalControl
|
|
1598
|
+
});
|
|
1599
|
+
await runTerminalControlSend({
|
|
1600
|
+
options,
|
|
1601
|
+
conversation: managed.conversation,
|
|
1602
|
+
nextConversation: managed.nextConversation,
|
|
1603
|
+
statePath: managed.statePath,
|
|
1604
|
+
logPath: managed.logPath,
|
|
1605
|
+
executor: managed.executor,
|
|
1606
|
+
message: managed.message,
|
|
1607
|
+
terminalControl: terminalConversation.terminalControl,
|
|
1608
|
+
needsNativeTakeoverBootstrap: true
|
|
1609
|
+
});
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1592
1612
|
await runTerminalConversationSend({
|
|
1593
1613
|
options,
|
|
1594
1614
|
conversationId: terminalConversation.conversationId,
|
|
@@ -2083,7 +2103,17 @@ async function runTerminalConversationSend({ options, conversationId, messageBod
|
|
|
2083
2103
|
}
|
|
2084
2104
|
async function runTerminalControlSend({ options, conversation, nextConversation, statePath, logPath, executor, message, terminalControl, needsNativeTakeoverBootstrap }) {
|
|
2085
2105
|
const provider = createTerminalControlProvider(options);
|
|
2086
|
-
|
|
2106
|
+
const terminalPayload = needsNativeTakeoverBootstrap
|
|
2107
|
+
? buildAgentSendPayload({
|
|
2108
|
+
conversation,
|
|
2109
|
+
executor,
|
|
2110
|
+
message,
|
|
2111
|
+
includeNativeTakeoverBootstrap: true,
|
|
2112
|
+
includeForkTakeoverBootstrap: false,
|
|
2113
|
+
forkTakeover: undefined
|
|
2114
|
+
})
|
|
2115
|
+
: String(message.body ?? "");
|
|
2116
|
+
await provider.sendText(terminalControl.target, terminalPayload, {
|
|
2087
2117
|
socketPath: terminalControl.socketPath
|
|
2088
2118
|
});
|
|
2089
2119
|
await provider.sendKeys(terminalControl.target, ["Enter"], {
|
|
@@ -2095,13 +2125,15 @@ async function runTerminalControlSend({ options, conversation, nextConversation,
|
|
|
2095
2125
|
event: "terminal_message_send",
|
|
2096
2126
|
executor,
|
|
2097
2127
|
terminal_control: terminalControl,
|
|
2098
|
-
message: textSummary(message.body)
|
|
2128
|
+
message: textSummary(message.body),
|
|
2129
|
+
payload: textSummary(terminalPayload)
|
|
2099
2130
|
});
|
|
2100
2131
|
runtimeLog("info", "terminal_message_send", {
|
|
2101
2132
|
conversation_id: conversation.conversation_id,
|
|
2102
2133
|
agent: executor.kind,
|
|
2103
2134
|
terminal_target: terminalControl.target,
|
|
2104
|
-
message: textSummary(message.body)
|
|
2135
|
+
message: textSummary(message.body),
|
|
2136
|
+
payload: textSummary(terminalPayload)
|
|
2105
2137
|
});
|
|
2106
2138
|
const deliveredConversation = markTakeoverBootstrapped({
|
|
2107
2139
|
conversation: nextConversation,
|
|
@@ -2128,6 +2160,113 @@ async function runTerminalControlSend({ options, conversation, nextConversation,
|
|
|
2128
2160
|
})
|
|
2129
2161
|
});
|
|
2130
2162
|
}
|
|
2163
|
+
function createManagedTerminalConversationFromRawId({ options, conversationId, messageBody, terminalControl }) {
|
|
2164
|
+
const workspace = terminalControl.currentPath ?? process.cwd();
|
|
2165
|
+
const storeDir = expandHome(options.storeDir ?? options.logDir ?? defaultStoreDir(workspace));
|
|
2166
|
+
cleanupIdleConversations(storeDir, options);
|
|
2167
|
+
const executor = resolveExecutor({
|
|
2168
|
+
kind: "codex",
|
|
2169
|
+
session: conversationId
|
|
2170
|
+
});
|
|
2171
|
+
const now = new Date();
|
|
2172
|
+
const conversation = createConversation({
|
|
2173
|
+
userRequest: String(messageBody),
|
|
2174
|
+
workspace,
|
|
2175
|
+
openclawSession: options.openclawSession ?? "agent:main:main",
|
|
2176
|
+
executorKind: executor.kind,
|
|
2177
|
+
executorSession: executor.session,
|
|
2178
|
+
softLimit: Number(options.softLimit ?? 50),
|
|
2179
|
+
hardLimit: Number(options.hardLimit ?? 100),
|
|
2180
|
+
now
|
|
2181
|
+
});
|
|
2182
|
+
const paths = pathsForConversation(conversation.conversation_id, storeDir);
|
|
2183
|
+
const callbackCommand = options.callbackCommand
|
|
2184
|
+
? expandCallbackCommandTemplate(options.callbackCommand, { statePath: paths.statePath })
|
|
2185
|
+
: buildCallbackCommand({
|
|
2186
|
+
statePath: paths.statePath,
|
|
2187
|
+
gatewayUrl: options.gatewayUrl ?? "ws://127.0.0.1:18789",
|
|
2188
|
+
token: options.token,
|
|
2189
|
+
openclawSession: options.openclawSession ?? "agent:main:main",
|
|
2190
|
+
gatewayMethod: options.gatewayMethod,
|
|
2191
|
+
gatewaySession: options.gatewaySession,
|
|
2192
|
+
openclawBin: options.openclawBin ?? resolveOptionalExecutable("openclaw")
|
|
2193
|
+
});
|
|
2194
|
+
const attachedConversation = withStoragePaths({
|
|
2195
|
+
...conversation,
|
|
2196
|
+
executor,
|
|
2197
|
+
status: "idle",
|
|
2198
|
+
idle_since: now.toISOString(),
|
|
2199
|
+
updated_at: now.toISOString(),
|
|
2200
|
+
callback_command: callbackCommand,
|
|
2201
|
+
gateway_url: options.gatewayUrl ?? "ws://127.0.0.1:18789",
|
|
2202
|
+
gateway_method: options.gatewayMethod,
|
|
2203
|
+
gateway_session: options.gatewaySession ?? options.openclawSession ?? "agent:main:main",
|
|
2204
|
+
openclaw_bin: options.openclawBin ?? resolveOptionalExecutable("openclaw"),
|
|
2205
|
+
executor_all_proxy: proxyForExecutor(executor, options),
|
|
2206
|
+
executor_model: options.model ?? options.codexModel,
|
|
2207
|
+
native_session_takeover: {
|
|
2208
|
+
agent: "codex",
|
|
2209
|
+
native_session_id: conversationId,
|
|
2210
|
+
source_cwd: workspace,
|
|
2211
|
+
source_title: `Terminal-controlled Codex ${terminalControl.target}`,
|
|
2212
|
+
strategy: "terminal_control",
|
|
2213
|
+
attached_at: now.toISOString(),
|
|
2214
|
+
takeover_match_kind: "raw_terminal_send",
|
|
2215
|
+
terminal_control: terminalControl,
|
|
2216
|
+
needs_bootstrap: true
|
|
2217
|
+
}
|
|
2218
|
+
}, paths);
|
|
2219
|
+
saveState(paths.statePath, attachedConversation);
|
|
2220
|
+
appendEvent(paths.logPath, {
|
|
2221
|
+
ts: now.toISOString(),
|
|
2222
|
+
conversation_id: attachedConversation.conversation_id,
|
|
2223
|
+
event: "raw_terminal_session_attached",
|
|
2224
|
+
source_conversation_id: conversationId,
|
|
2225
|
+
agent: "codex",
|
|
2226
|
+
terminal_control: terminalControl,
|
|
2227
|
+
executor
|
|
2228
|
+
});
|
|
2229
|
+
runtimeLog("info", "raw_terminal_session_attached", {
|
|
2230
|
+
conversation_id: attachedConversation.conversation_id,
|
|
2231
|
+
source_conversation_id: conversationId,
|
|
2232
|
+
terminal_target: terminalControl.target,
|
|
2233
|
+
state_path: paths.statePath,
|
|
2234
|
+
event_log_path: paths.logPath
|
|
2235
|
+
});
|
|
2236
|
+
const message = createMessage({
|
|
2237
|
+
conversation: attachedConversation,
|
|
2238
|
+
from: "openclaw",
|
|
2239
|
+
to: executor.actor,
|
|
2240
|
+
type: options.type ?? "task",
|
|
2241
|
+
body: String(messageBody),
|
|
2242
|
+
metadata: {
|
|
2243
|
+
executor_kind: executor.kind,
|
|
2244
|
+
executor_session: executor.session,
|
|
2245
|
+
source_conversation_id: conversationId
|
|
2246
|
+
}
|
|
2247
|
+
});
|
|
2248
|
+
const nextConversation = applyMessageToConversation(attachedConversation, message);
|
|
2249
|
+
saveState(paths.statePath, nextConversation);
|
|
2250
|
+
appendEvent(paths.logPath, messageEvent(message));
|
|
2251
|
+
runtimeLog("info", "message_created", {
|
|
2252
|
+
conversation_id: nextConversation.conversation_id,
|
|
2253
|
+
source_conversation_id: conversationId,
|
|
2254
|
+
agent: executor.kind,
|
|
2255
|
+
executor_session: executor.session,
|
|
2256
|
+
message_type: message.type,
|
|
2257
|
+
state_path: paths.statePath,
|
|
2258
|
+
event_log_path: paths.logPath,
|
|
2259
|
+
message: textSummary(messageBody)
|
|
2260
|
+
});
|
|
2261
|
+
return {
|
|
2262
|
+
conversation: attachedConversation,
|
|
2263
|
+
nextConversation,
|
|
2264
|
+
statePath: paths.statePath,
|
|
2265
|
+
logPath: paths.logPath,
|
|
2266
|
+
executor,
|
|
2267
|
+
message
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2131
2270
|
function runNativeCodexResumeSend({ options, conversation, nextConversation, statePath, logPath, executor, executorEnv, message, payload, nativeTakeover, needsNativeTakeoverBootstrap }) {
|
|
2132
2271
|
const codexPath = resolveExecutable("codex");
|
|
2133
2272
|
const nativeSessionId = String(nativeTakeover["native_session_id"]);
|
|
@@ -2885,7 +3024,8 @@ function runMonitor(options) {
|
|
|
2885
3024
|
return;
|
|
2886
3025
|
}
|
|
2887
3026
|
if (Number.isFinite(pid) && !isProcessAlive(pid)) {
|
|
2888
|
-
const
|
|
3027
|
+
const outputTail = readOutputTail(options.outputPath);
|
|
3028
|
+
const modelSelection = detectModelSelectionError(outputTail);
|
|
2889
3029
|
if (modelSelection) {
|
|
2890
3030
|
const modelSelectionConversation = markConversationNeedsModelSelection({
|
|
2891
3031
|
statePath,
|
|
@@ -2906,6 +3046,30 @@ function runMonitor(options) {
|
|
|
2906
3046
|
});
|
|
2907
3047
|
return;
|
|
2908
3048
|
}
|
|
3049
|
+
const transientFailure = detectTransientExecutorFailure(outputTail);
|
|
3050
|
+
if (transientFailure) {
|
|
3051
|
+
const recoveryResult = markMonitorFailureNeedsRecovery({
|
|
3052
|
+
statePath,
|
|
3053
|
+
logPath,
|
|
3054
|
+
reason: transientFailure.message,
|
|
3055
|
+
detail: {
|
|
3056
|
+
executor_pid: pid,
|
|
3057
|
+
output_path: options.outputPath,
|
|
3058
|
+
transient_failure: transientFailure
|
|
3059
|
+
},
|
|
3060
|
+
outputTail
|
|
3061
|
+
});
|
|
3062
|
+
if (recoveryResult) {
|
|
3063
|
+
printJson({
|
|
3064
|
+
conversation: recoveryResult.conversation,
|
|
3065
|
+
monitored: true,
|
|
3066
|
+
stalled: false,
|
|
3067
|
+
needs_recovery: true,
|
|
3068
|
+
reason: transientFailure.message
|
|
3069
|
+
});
|
|
3070
|
+
return;
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
2909
3073
|
const stalledConversation = markConversationStalled({
|
|
2910
3074
|
statePath,
|
|
2911
3075
|
logPath,
|
|
@@ -3707,6 +3871,110 @@ function markConversationStalled({ statePath, logPath, reason, detail = {} }) {
|
|
|
3707
3871
|
}
|
|
3708
3872
|
return stalledConversation;
|
|
3709
3873
|
}
|
|
3874
|
+
function markMonitorFailureNeedsRecovery({ statePath, logPath, reason, detail = {}, outputTail = "" }) {
|
|
3875
|
+
const releaseLock = acquireFileLock(`${statePath}.lock`);
|
|
3876
|
+
let recoveryResult;
|
|
3877
|
+
let recoveryMessage;
|
|
3878
|
+
try {
|
|
3879
|
+
const conversation = loadState(statePath);
|
|
3880
|
+
if (!isWaitingForAgent(conversation.status)) {
|
|
3881
|
+
runtimeLog("info", "executor_monitor_finished", {
|
|
3882
|
+
conversation_id: conversation.conversation_id,
|
|
3883
|
+
status: conversation.status,
|
|
3884
|
+
reason: "conversation_changed_before_recovery"
|
|
3885
|
+
});
|
|
3886
|
+
return undefined;
|
|
3887
|
+
}
|
|
3888
|
+
const executor = executorForConversation(conversation);
|
|
3889
|
+
const pendingMessage = latestPendingExecutorMessage({
|
|
3890
|
+
events: readExistingEvents(logPath),
|
|
3891
|
+
executor
|
|
3892
|
+
});
|
|
3893
|
+
if (!pendingMessage) {
|
|
3894
|
+
runtimeLog("warn", "monitor_recovery_skipped", {
|
|
3895
|
+
conversation_id: conversation.conversation_id,
|
|
3896
|
+
agent: executor.kind,
|
|
3897
|
+
executor_session: executor.session,
|
|
3898
|
+
reason: "pending_message_missing"
|
|
3899
|
+
});
|
|
3900
|
+
return undefined;
|
|
3901
|
+
}
|
|
3902
|
+
recoveryResult = markConversationNeedsRecovery({
|
|
3903
|
+
conversation,
|
|
3904
|
+
statePath,
|
|
3905
|
+
logPath,
|
|
3906
|
+
executor,
|
|
3907
|
+
message: pendingMessage,
|
|
3908
|
+
failedStage: "executor_monitor",
|
|
3909
|
+
result: {
|
|
3910
|
+
status: 1,
|
|
3911
|
+
stderr: outputTail
|
|
3912
|
+
},
|
|
3913
|
+
reason
|
|
3914
|
+
});
|
|
3915
|
+
const recoveredConversation = recoveryResult.conversation;
|
|
3916
|
+
const shouldNotify = Boolean(recoveredConversation.gateway_method && !recoveredConversation.recovery_notification_sent_at);
|
|
3917
|
+
if (shouldNotify) {
|
|
3918
|
+
recoveryMessage = createMessage({
|
|
3919
|
+
conversation: recoveredConversation,
|
|
3920
|
+
from: executor.actor,
|
|
3921
|
+
to: "openclaw",
|
|
3922
|
+
type: "error",
|
|
3923
|
+
requiresResponse: false,
|
|
3924
|
+
body: [
|
|
3925
|
+
`AKK marked this ${executor.display_name} task as needing recovery: ${reason}.`,
|
|
3926
|
+
"",
|
|
3927
|
+
`Conversation: ${recoveredConversation.conversation_id}`,
|
|
3928
|
+
`Session: ${executor.session}`,
|
|
3929
|
+
"Use `AKK recover` to replay saved AKK history in a new session, or `AKK close` to close it."
|
|
3930
|
+
].join("\n")
|
|
3931
|
+
});
|
|
3932
|
+
const now = new Date().toISOString();
|
|
3933
|
+
recoveryResult.conversation = {
|
|
3934
|
+
...recoveredConversation,
|
|
3935
|
+
recovery_notification_sent_at: now,
|
|
3936
|
+
recovery_notification_message_id: recoveryMessage.id,
|
|
3937
|
+
updated_at: now
|
|
3938
|
+
};
|
|
3939
|
+
saveState(statePath, recoveryResult.conversation);
|
|
3940
|
+
}
|
|
3941
|
+
appendEvent(logPath, {
|
|
3942
|
+
ts: new Date().toISOString(),
|
|
3943
|
+
conversation_id: conversation.conversation_id,
|
|
3944
|
+
event: "monitor_recovery_classified",
|
|
3945
|
+
status: "needs_recovery",
|
|
3946
|
+
reason,
|
|
3947
|
+
...detail
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
finally {
|
|
3951
|
+
releaseLock();
|
|
3952
|
+
}
|
|
3953
|
+
if (recoveryResult?.conversation && recoveryMessage) {
|
|
3954
|
+
deliverMonitorFailureNotification({
|
|
3955
|
+
statePath,
|
|
3956
|
+
logPath,
|
|
3957
|
+
conversation: recoveryResult.conversation,
|
|
3958
|
+
message: recoveryMessage,
|
|
3959
|
+
eventPrefix: "recovery"
|
|
3960
|
+
});
|
|
3961
|
+
}
|
|
3962
|
+
return recoveryResult;
|
|
3963
|
+
}
|
|
3964
|
+
function latestPendingExecutorMessage({ events, executor }) {
|
|
3965
|
+
for (const event of [...events].reverse()) {
|
|
3966
|
+
if (event.event !== "message") {
|
|
3967
|
+
continue;
|
|
3968
|
+
}
|
|
3969
|
+
const message = event.message ?? event;
|
|
3970
|
+
if (message?.from === "openclaw" &&
|
|
3971
|
+
message?.to === executor.actor &&
|
|
3972
|
+
message?.requires_response !== false) {
|
|
3973
|
+
return message;
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
return undefined;
|
|
3977
|
+
}
|
|
3710
3978
|
function markConversationNeedsModelSelection({ statePath, logPath, reason, detail = {} }) {
|
|
3711
3979
|
const releaseLock = acquireFileLock(`${statePath}.lock`);
|
|
3712
3980
|
let modelSelectionConversation;
|
|
@@ -3759,7 +4027,10 @@ function markConversationNeedsModelSelection({ statePath, logPath, reason, detai
|
|
|
3759
4027
|
}
|
|
3760
4028
|
return modelSelectionConversation;
|
|
3761
4029
|
}
|
|
3762
|
-
function
|
|
4030
|
+
function deliverMonitorFailureNotification({ statePath, logPath, conversation, message, eventPrefix }) {
|
|
4031
|
+
deliverStalledNotification({ statePath, logPath, conversation, message, eventPrefix });
|
|
4032
|
+
}
|
|
4033
|
+
function deliverStalledNotification({ statePath, logPath, conversation, message, eventPrefix = "stalled" }) {
|
|
3763
4034
|
if (!conversation.gateway_method) {
|
|
3764
4035
|
return;
|
|
3765
4036
|
}
|
|
@@ -3779,14 +4050,14 @@ function deliverStalledNotification({ statePath, logPath, conversation, message
|
|
|
3779
4050
|
appendEvent(logPath, {
|
|
3780
4051
|
ts: new Date().toISOString(),
|
|
3781
4052
|
conversation_id: conversation.conversation_id,
|
|
3782
|
-
event:
|
|
4053
|
+
event: `${eventPrefix}_gateway_method_delivery`,
|
|
3783
4054
|
method: conversation.gateway_method,
|
|
3784
4055
|
message_id: message.id,
|
|
3785
4056
|
status: delivery.status,
|
|
3786
4057
|
stdout: delivery.stdout,
|
|
3787
4058
|
stderr: delivery.stderr
|
|
3788
4059
|
});
|
|
3789
|
-
runtimeLog("info",
|
|
4060
|
+
runtimeLog("info", `${eventPrefix}_gateway_method_delivery`, {
|
|
3790
4061
|
conversation_id: conversation.conversation_id,
|
|
3791
4062
|
method: conversation.gateway_method,
|
|
3792
4063
|
message_id: message.id,
|
|
@@ -3812,13 +4083,13 @@ function deliverStalledNotification({ statePath, logPath, conversation, message
|
|
|
3812
4083
|
appendEvent(logPath, {
|
|
3813
4084
|
ts: new Date().toISOString(),
|
|
3814
4085
|
conversation_id: conversation.conversation_id,
|
|
3815
|
-
event:
|
|
4086
|
+
event: `${eventPrefix}_chat_send_delivery`,
|
|
3816
4087
|
message_id: message.id,
|
|
3817
4088
|
status: chatSendDelivery.status,
|
|
3818
4089
|
stdout: chatSendDelivery.stdout,
|
|
3819
4090
|
stderr: chatSendDelivery.stderr
|
|
3820
4091
|
});
|
|
3821
|
-
runtimeLog("info",
|
|
4092
|
+
runtimeLog("info", `${eventPrefix}_chat_send_delivery`, {
|
|
3822
4093
|
conversation_id: conversation.conversation_id,
|
|
3823
4094
|
message_id: message.id,
|
|
3824
4095
|
status: chatSendDelivery.status,
|
|
@@ -4143,6 +4414,9 @@ function classifyProcessFailure(result) {
|
|
|
4143
4414
|
if (!combined && status === 0) {
|
|
4144
4415
|
return undefined;
|
|
4145
4416
|
}
|
|
4417
|
+
if (isRemoteCompactStreamDisconnect(combined)) {
|
|
4418
|
+
return "transient_remote_compact_failure";
|
|
4419
|
+
}
|
|
4146
4420
|
if (combined.includes("agent needs reconnect") || combined.includes("internal error")) {
|
|
4147
4421
|
return "agent_reconnect_required";
|
|
4148
4422
|
}
|
|
@@ -4210,6 +4484,25 @@ function detectModelSelectionError(text) {
|
|
|
4210
4484
|
}
|
|
4211
4485
|
return undefined;
|
|
4212
4486
|
}
|
|
4487
|
+
function detectTransientExecutorFailure(text) {
|
|
4488
|
+
const cleaned = cleanProcessText(text);
|
|
4489
|
+
if (!cleaned) {
|
|
4490
|
+
return undefined;
|
|
4491
|
+
}
|
|
4492
|
+
if (isRemoteCompactStreamDisconnect(cleaned.toLowerCase())) {
|
|
4493
|
+
return {
|
|
4494
|
+
kind: "remote_compact_stream_disconnect",
|
|
4495
|
+
message: "Codex remote compact stream disconnected before completion"
|
|
4496
|
+
};
|
|
4497
|
+
}
|
|
4498
|
+
return undefined;
|
|
4499
|
+
}
|
|
4500
|
+
function isRemoteCompactStreamDisconnect(text) {
|
|
4501
|
+
const value = String(text ?? "").toLowerCase();
|
|
4502
|
+
return (value.includes("error running remote compact task") &&
|
|
4503
|
+
value.includes("stream disconnected") &&
|
|
4504
|
+
value.includes("/codex/responses/compact"));
|
|
4505
|
+
}
|
|
4213
4506
|
function runtimeLog(level, event, fields = {}) {
|
|
4214
4507
|
try {
|
|
4215
4508
|
writeRuntimeLog({
|