opencodekit 0.18.24 → 0.18.25

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/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
21
  //#endregion
22
22
  //#region package.json
23
- var version = "0.18.24";
23
+ var version = "0.18.25";
24
24
 
25
25
  //#endregion
26
26
  //#region src/utils/license.ts
Binary file
@@ -921,6 +921,7 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
921
921
  let fallbacksUsed = 0;
922
922
  let attempt = 0;
923
923
  let recoveryCyclesUsed = 0;
924
+ let attempted400Recovery = false;
924
925
 
925
926
  while (attempt <= RATE_LIMIT_CONFIG.maxRetries) {
926
927
  try {
@@ -1067,6 +1068,116 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
1067
1068
  );
1068
1069
  }
1069
1070
 
1071
+ // Handle 400 Bad Request with auto-recovery
1072
+ if (response.status === 400 && !attempted400Recovery) {
1073
+ let errorDetail = "Bad Request";
1074
+ try {
1075
+ const clonedResponse = response.clone();
1076
+ const errorBody = await clonedResponse.json();
1077
+ errorDetail =
1078
+ errorBody?.error?.message ||
1079
+ errorBody?.message ||
1080
+ "Bad Request";
1081
+ } catch {}
1082
+
1083
+ log(
1084
+ "warn",
1085
+ `[400-RECOVERY] Bad Request from Copilot API`,
1086
+ {
1087
+ model: currentModel,
1088
+ error_detail: errorDetail,
1089
+ attempt,
1090
+ },
1091
+ );
1092
+
1093
+ // Check for recoverable 400 causes
1094
+ const isThinkingBlockError =
1095
+ /thinking.?block|invalid.*signature|reasoning.*invalid/i.test(
1096
+ errorDetail,
1097
+ );
1098
+ const isIdError =
1099
+ /invalid.*\bid\b|item.*\bid\b|unknown.*\bid\b|malformed.*\bid\b/i.test(
1100
+ errorDetail,
1101
+ );
1102
+
1103
+ if (isThinkingBlockError || isIdError) {
1104
+ let bodyObj: any;
1105
+ try {
1106
+ bodyObj =
1107
+ typeof activeFinalInit.body === "string"
1108
+ ? JSON.parse(activeFinalInit.body)
1109
+ : activeFinalInit.body;
1110
+ } catch {
1111
+ // Can't parse body — not recoverable
1112
+ log(
1113
+ "warn",
1114
+ `[400-RECOVERY] Cannot parse request body, giving up`,
1115
+ );
1116
+ return response;
1117
+ }
1118
+
1119
+ // Cancel original response body only after confirming we can recover
1120
+ try {
1121
+ await response.body?.cancel();
1122
+ } catch {}
1123
+
1124
+ if (isThinkingBlockError && bodyObj?.messages) {
1125
+ // Strip ALL thinking/reasoning content aggressively
1126
+ bodyObj.messages = bodyObj.messages.map(
1127
+ (msg: any) => {
1128
+ if (msg.role !== "assistant") return msg;
1129
+ const {
1130
+ reasoning_text: _rt,
1131
+ reasoning_opaque: _ro,
1132
+ ...cleaned
1133
+ } = msg;
1134
+ if (Array.isArray(cleaned.content)) {
1135
+ cleaned.content = cleaned.content.filter(
1136
+ (part: any) => part.type !== "thinking",
1137
+ );
1138
+ if (cleaned.content.length === 0)
1139
+ cleaned.content = null;
1140
+ }
1141
+ return cleaned;
1142
+ },
1143
+ );
1144
+ delete bodyObj.thinking_budget;
1145
+ recovered = true;
1146
+ log(
1147
+ "info",
1148
+ `[400-RECOVERY] Stripped all thinking/reasoning content for retry`,
1149
+ );
1150
+ }
1151
+
1152
+ if (isIdError && bodyObj?.input) {
1153
+ bodyObj.input = sanitizeResponseInputIds(
1154
+ bodyObj.input,
1155
+ );
1156
+ recovered = true;
1157
+ log(
1158
+ "info",
1159
+ `[400-RECOVERY] Re-sanitized Responses API IDs for retry`,
1160
+ );
1161
+ }
1162
+
1163
+ if (recovered) {
1164
+ attempted400Recovery = true;
1165
+ activeFinalInit = {
1166
+ ...activeFinalInit,
1167
+ body: JSON.stringify(bodyObj),
1168
+ };
1169
+ attempt++;
1170
+ continue;
1171
+ }
1172
+ }
1173
+
1174
+ // Not recoverable — log detail and return original response
1175
+ log(
1176
+ "warn",
1177
+ `[400-RECOVERY] Non-recoverable 400: ${errorDetail}`,
1178
+ );
1179
+ }
1180
+
1070
1181
  // Response transformation is handled by the custom SDK at
1071
1182
  // .opencode/plugin/sdk/copilot/
1072
1183
  return response;
@@ -179,8 +179,25 @@ export function createHooks(deps: HookDeps) {
179
179
  statusCode === 400 ||
180
180
  /bad request|invalid.*request/i.test(errorMsg)
181
181
  ) {
182
- guidance =
183
- "Bad request — try starting a new session or using /compact";
182
+ // Sub-classify 400 errors for more specific guidance
183
+ if (
184
+ /thinking.?block|invalid.*signature|reasoning/i.test(errorMsg)
185
+ ) {
186
+ guidance =
187
+ "Thinking block error — start a new session to reset";
188
+ } else if (
189
+ /context.*(too|exceed|length|large|limit)|too.?long|max.?length|content.?length/i.test(errorMsg)
190
+ ) {
191
+ guidance =
192
+ "Request too large — use /compact to reduce context";
193
+ } else if (
194
+ /invalid.*\bid\b|item.*\bid\b|unknown.*\bid\b|malformed.*\bid\b/i.test(errorMsg)
195
+ ) {
196
+ guidance = "API format error — start a new session";
197
+ } else {
198
+ guidance =
199
+ "Bad request — try starting a new session or using /compact";
200
+ }
184
201
  } else if (
185
202
  /timeout|ETIMEDOUT|ECONNRESET|network|fetch failed/i.test(errorMsg)
186
203
  ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencodekit",
3
- "version": "0.18.24",
3
+ "version": "0.18.25",
4
4
  "description": "CLI tool for bootstrapping and managing OpenCodeKit projects",
5
5
  "keywords": [
6
6
  "agents",