oc-codex-multi-auth 6.0.0

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.
Files changed (248) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/assets/opencode-logo-ornate-dark.svg +18 -0
  4. package/assets/readme-hero.svg +31 -0
  5. package/config/README.md +96 -0
  6. package/config/minimal-opencode.json +13 -0
  7. package/config/opencode-legacy.json +871 -0
  8. package/config/opencode-modern.json +358 -0
  9. package/dist/index.d.ts +45 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +5008 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/accounts/rate-limits.d.ts +22 -0
  14. package/dist/lib/accounts/rate-limits.d.ts.map +1 -0
  15. package/dist/lib/accounts/rate-limits.js +63 -0
  16. package/dist/lib/accounts/rate-limits.js.map +1 -0
  17. package/dist/lib/accounts.d.ts +134 -0
  18. package/dist/lib/accounts.d.ts.map +1 -0
  19. package/dist/lib/accounts.js +795 -0
  20. package/dist/lib/accounts.js.map +1 -0
  21. package/dist/lib/audit.d.ts +45 -0
  22. package/dist/lib/audit.d.ts.map +1 -0
  23. package/dist/lib/audit.js +154 -0
  24. package/dist/lib/audit.js.map +1 -0
  25. package/dist/lib/auth/auth.d.ts +51 -0
  26. package/dist/lib/auth/auth.d.ts.map +1 -0
  27. package/dist/lib/auth/auth.js +191 -0
  28. package/dist/lib/auth/auth.js.map +1 -0
  29. package/dist/lib/auth/browser.d.ts +17 -0
  30. package/dist/lib/auth/browser.d.ts.map +1 -0
  31. package/dist/lib/auth/browser.js +90 -0
  32. package/dist/lib/auth/browser.js.map +1 -0
  33. package/dist/lib/auth/device-code.d.ts +28 -0
  34. package/dist/lib/auth/device-code.d.ts.map +1 -0
  35. package/dist/lib/auth/device-code.js +215 -0
  36. package/dist/lib/auth/device-code.js.map +1 -0
  37. package/dist/lib/auth/login-runner.d.ts +55 -0
  38. package/dist/lib/auth/login-runner.d.ts.map +1 -0
  39. package/dist/lib/auth/login-runner.js +631 -0
  40. package/dist/lib/auth/login-runner.js.map +1 -0
  41. package/dist/lib/auth/server.d.ts +10 -0
  42. package/dist/lib/auth/server.d.ts.map +1 -0
  43. package/dist/lib/auth/server.js +98 -0
  44. package/dist/lib/auth/server.js.map +1 -0
  45. package/dist/lib/auth/token-utils.d.ts +60 -0
  46. package/dist/lib/auth/token-utils.d.ts.map +1 -0
  47. package/dist/lib/auth/token-utils.js +416 -0
  48. package/dist/lib/auth/token-utils.js.map +1 -0
  49. package/dist/lib/auth-rate-limit.d.ts +20 -0
  50. package/dist/lib/auth-rate-limit.d.ts.map +1 -0
  51. package/dist/lib/auth-rate-limit.js +91 -0
  52. package/dist/lib/auth-rate-limit.js.map +1 -0
  53. package/dist/lib/auto-update-checker.d.ts +10 -0
  54. package/dist/lib/auto-update-checker.d.ts.map +1 -0
  55. package/dist/lib/auto-update-checker.js +129 -0
  56. package/dist/lib/auto-update-checker.js.map +1 -0
  57. package/dist/lib/circuit-breaker.d.ts +34 -0
  58. package/dist/lib/circuit-breaker.d.ts.map +1 -0
  59. package/dist/lib/circuit-breaker.js +124 -0
  60. package/dist/lib/circuit-breaker.js.map +1 -0
  61. package/dist/lib/cli.d.ts +46 -0
  62. package/dist/lib/cli.d.ts.map +1 -0
  63. package/dist/lib/cli.js +192 -0
  64. package/dist/lib/cli.js.map +1 -0
  65. package/dist/lib/config.d.ts +42 -0
  66. package/dist/lib/config.d.ts.map +1 -0
  67. package/dist/lib/config.js +314 -0
  68. package/dist/lib/config.js.map +1 -0
  69. package/dist/lib/constants.d.ts +90 -0
  70. package/dist/lib/constants.d.ts.map +1 -0
  71. package/dist/lib/constants.js +90 -0
  72. package/dist/lib/constants.js.map +1 -0
  73. package/dist/lib/context-overflow.d.ts +27 -0
  74. package/dist/lib/context-overflow.d.ts.map +1 -0
  75. package/dist/lib/context-overflow.js +124 -0
  76. package/dist/lib/context-overflow.js.map +1 -0
  77. package/dist/lib/errors.d.ts +113 -0
  78. package/dist/lib/errors.d.ts.map +1 -0
  79. package/dist/lib/errors.js +103 -0
  80. package/dist/lib/errors.js.map +1 -0
  81. package/dist/lib/health.d.ts +33 -0
  82. package/dist/lib/health.d.ts.map +1 -0
  83. package/dist/lib/health.js +70 -0
  84. package/dist/lib/health.js.map +1 -0
  85. package/dist/lib/index.d.ts +20 -0
  86. package/dist/lib/index.d.ts.map +1 -0
  87. package/dist/lib/index.js +20 -0
  88. package/dist/lib/index.js.map +1 -0
  89. package/dist/lib/logger.d.ts +40 -0
  90. package/dist/lib/logger.d.ts.map +1 -0
  91. package/dist/lib/logger.js +337 -0
  92. package/dist/lib/logger.js.map +1 -0
  93. package/dist/lib/oauth-success.html +712 -0
  94. package/dist/lib/parallel-probe.d.ts +28 -0
  95. package/dist/lib/parallel-probe.d.ts.map +1 -0
  96. package/dist/lib/parallel-probe.js +97 -0
  97. package/dist/lib/parallel-probe.js.map +1 -0
  98. package/dist/lib/proactive-refresh.d.ts +67 -0
  99. package/dist/lib/proactive-refresh.d.ts.map +1 -0
  100. package/dist/lib/proactive-refresh.js +161 -0
  101. package/dist/lib/proactive-refresh.js.map +1 -0
  102. package/dist/lib/prompts/codex-opencode-bridge.d.ts +20 -0
  103. package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -0
  104. package/dist/lib/prompts/codex-opencode-bridge.js +126 -0
  105. package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -0
  106. package/dist/lib/prompts/codex.d.ts +41 -0
  107. package/dist/lib/prompts/codex.d.ts.map +1 -0
  108. package/dist/lib/prompts/codex.js +400 -0
  109. package/dist/lib/prompts/codex.js.map +1 -0
  110. package/dist/lib/prompts/opencode-codex.d.ts +25 -0
  111. package/dist/lib/prompts/opencode-codex.d.ts.map +1 -0
  112. package/dist/lib/prompts/opencode-codex.js +223 -0
  113. package/dist/lib/prompts/opencode-codex.js.map +1 -0
  114. package/dist/lib/recovery/constants.d.ts +12 -0
  115. package/dist/lib/recovery/constants.d.ts.map +1 -0
  116. package/dist/lib/recovery/constants.js +25 -0
  117. package/dist/lib/recovery/constants.js.map +1 -0
  118. package/dist/lib/recovery/index.d.ts +12 -0
  119. package/dist/lib/recovery/index.d.ts.map +1 -0
  120. package/dist/lib/recovery/index.js +12 -0
  121. package/dist/lib/recovery/index.js.map +1 -0
  122. package/dist/lib/recovery/storage.d.ts +24 -0
  123. package/dist/lib/recovery/storage.d.ts.map +1 -0
  124. package/dist/lib/recovery/storage.js +362 -0
  125. package/dist/lib/recovery/storage.js.map +1 -0
  126. package/dist/lib/recovery/types.d.ts +116 -0
  127. package/dist/lib/recovery/types.d.ts.map +1 -0
  128. package/dist/lib/recovery/types.js +7 -0
  129. package/dist/lib/recovery/types.js.map +1 -0
  130. package/dist/lib/recovery.d.ts +31 -0
  131. package/dist/lib/recovery.d.ts.map +1 -0
  132. package/dist/lib/recovery.js +313 -0
  133. package/dist/lib/recovery.js.map +1 -0
  134. package/dist/lib/refresh-queue.d.ts +131 -0
  135. package/dist/lib/refresh-queue.d.ts.map +1 -0
  136. package/dist/lib/refresh-queue.js +297 -0
  137. package/dist/lib/refresh-queue.js.map +1 -0
  138. package/dist/lib/request/fetch-helpers.d.ts +142 -0
  139. package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
  140. package/dist/lib/request/fetch-helpers.js +749 -0
  141. package/dist/lib/request/fetch-helpers.js.map +1 -0
  142. package/dist/lib/request/helpers/input-utils.d.ts +7 -0
  143. package/dist/lib/request/helpers/input-utils.d.ts.map +1 -0
  144. package/dist/lib/request/helpers/input-utils.js +214 -0
  145. package/dist/lib/request/helpers/input-utils.js.map +1 -0
  146. package/dist/lib/request/helpers/model-map.d.ts +22 -0
  147. package/dist/lib/request/helpers/model-map.d.ts.map +1 -0
  148. package/dist/lib/request/helpers/model-map.js +183 -0
  149. package/dist/lib/request/helpers/model-map.js.map +1 -0
  150. package/dist/lib/request/helpers/tool-utils.d.ts +29 -0
  151. package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -0
  152. package/dist/lib/request/helpers/tool-utils.js +117 -0
  153. package/dist/lib/request/helpers/tool-utils.js.map +1 -0
  154. package/dist/lib/request/rate-limit-backoff.d.ts +17 -0
  155. package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -0
  156. package/dist/lib/request/rate-limit-backoff.js +83 -0
  157. package/dist/lib/request/rate-limit-backoff.js.map +1 -0
  158. package/dist/lib/request/request-transformer.d.ts +107 -0
  159. package/dist/lib/request/request-transformer.d.ts.map +1 -0
  160. package/dist/lib/request/request-transformer.js +930 -0
  161. package/dist/lib/request/request-transformer.js.map +1 -0
  162. package/dist/lib/request/response-handler.d.ts +23 -0
  163. package/dist/lib/request/response-handler.d.ts.map +1 -0
  164. package/dist/lib/request/response-handler.js +245 -0
  165. package/dist/lib/request/response-handler.js.map +1 -0
  166. package/dist/lib/request/retry-budget.d.ts +19 -0
  167. package/dist/lib/request/retry-budget.d.ts.map +1 -0
  168. package/dist/lib/request/retry-budget.js +99 -0
  169. package/dist/lib/request/retry-budget.js.map +1 -0
  170. package/dist/lib/rotation.d.ts +130 -0
  171. package/dist/lib/rotation.d.ts.map +1 -0
  172. package/dist/lib/rotation.js +301 -0
  173. package/dist/lib/rotation.js.map +1 -0
  174. package/dist/lib/runtime-contracts.d.ts +16 -0
  175. package/dist/lib/runtime-contracts.d.ts.map +1 -0
  176. package/dist/lib/runtime-contracts.js +24 -0
  177. package/dist/lib/runtime-contracts.js.map +1 -0
  178. package/dist/lib/schemas.d.ts +457 -0
  179. package/dist/lib/schemas.d.ts.map +1 -0
  180. package/dist/lib/schemas.js +283 -0
  181. package/dist/lib/schemas.js.map +1 -0
  182. package/dist/lib/shutdown.d.ts +7 -0
  183. package/dist/lib/shutdown.d.ts.map +1 -0
  184. package/dist/lib/shutdown.js +43 -0
  185. package/dist/lib/shutdown.js.map +1 -0
  186. package/dist/lib/storage/migrations.d.ts +65 -0
  187. package/dist/lib/storage/migrations.d.ts.map +1 -0
  188. package/dist/lib/storage/migrations.js +44 -0
  189. package/dist/lib/storage/migrations.js.map +1 -0
  190. package/dist/lib/storage/paths.d.ts +16 -0
  191. package/dist/lib/storage/paths.d.ts.map +1 -0
  192. package/dist/lib/storage/paths.js +92 -0
  193. package/dist/lib/storage/paths.js.map +1 -0
  194. package/dist/lib/storage.d.ts +157 -0
  195. package/dist/lib/storage.d.ts.map +1 -0
  196. package/dist/lib/storage.js +1169 -0
  197. package/dist/lib/storage.js.map +1 -0
  198. package/dist/lib/table-formatter.d.ts +32 -0
  199. package/dist/lib/table-formatter.d.ts.map +1 -0
  200. package/dist/lib/table-formatter.js +44 -0
  201. package/dist/lib/table-formatter.js.map +1 -0
  202. package/dist/lib/types.d.ts +131 -0
  203. package/dist/lib/types.d.ts.map +1 -0
  204. package/dist/lib/types.js +2 -0
  205. package/dist/lib/types.js.map +1 -0
  206. package/dist/lib/ui/ansi.d.ts +22 -0
  207. package/dist/lib/ui/ansi.d.ts.map +1 -0
  208. package/dist/lib/ui/ansi.js +38 -0
  209. package/dist/lib/ui/ansi.js.map +1 -0
  210. package/dist/lib/ui/auth-menu.d.ts +39 -0
  211. package/dist/lib/ui/auth-menu.d.ts.map +1 -0
  212. package/dist/lib/ui/auth-menu.js +186 -0
  213. package/dist/lib/ui/auth-menu.js.map +1 -0
  214. package/dist/lib/ui/beginner.d.ts +62 -0
  215. package/dist/lib/ui/beginner.d.ts.map +1 -0
  216. package/dist/lib/ui/beginner.js +259 -0
  217. package/dist/lib/ui/beginner.js.map +1 -0
  218. package/dist/lib/ui/confirm.d.ts +2 -0
  219. package/dist/lib/ui/confirm.d.ts.map +1 -0
  220. package/dist/lib/ui/confirm.js +21 -0
  221. package/dist/lib/ui/confirm.js.map +1 -0
  222. package/dist/lib/ui/format.d.ts +9 -0
  223. package/dist/lib/ui/format.d.ts.map +1 -0
  224. package/dist/lib/ui/format.js +49 -0
  225. package/dist/lib/ui/format.js.map +1 -0
  226. package/dist/lib/ui/runtime.d.ts +11 -0
  227. package/dist/lib/ui/runtime.d.ts.map +1 -0
  228. package/dist/lib/ui/runtime.js +31 -0
  229. package/dist/lib/ui/runtime.js.map +1 -0
  230. package/dist/lib/ui/select.d.ts +20 -0
  231. package/dist/lib/ui/select.d.ts.map +1 -0
  232. package/dist/lib/ui/select.js +343 -0
  233. package/dist/lib/ui/select.js.map +1 -0
  234. package/dist/lib/ui/theme.d.ts +34 -0
  235. package/dist/lib/ui/theme.d.ts.map +1 -0
  236. package/dist/lib/ui/theme.js +84 -0
  237. package/dist/lib/ui/theme.js.map +1 -0
  238. package/dist/lib/utils.d.ts +29 -0
  239. package/dist/lib/utils.d.ts.map +1 -0
  240. package/dist/lib/utils.js +54 -0
  241. package/dist/lib/utils.js.map +1 -0
  242. package/package.json +113 -0
  243. package/scripts/audit-dev-allowlist.js +114 -0
  244. package/scripts/copy-oauth-success.js +37 -0
  245. package/scripts/install-oc-codex-multi-auth-core.js +381 -0
  246. package/scripts/install-oc-codex-multi-auth.js +15 -0
  247. package/scripts/test-all-models.sh +260 -0
  248. package/scripts/validate-model-map.sh +97 -0
@@ -0,0 +1,31 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { PluginConfig } from "./types.js";
3
+ import type { MessageInfo, MessageData, RecoveryErrorType, ResumeConfig } from "./recovery/types.js";
4
+ export type { RecoveryErrorType, MessageInfo, MessageData, ResumeConfig };
5
+ type PluginClient = PluginInput["client"];
6
+ export declare function detectErrorType(error: unknown): RecoveryErrorType;
7
+ export declare function isRecoverableError(error: unknown): boolean;
8
+ export declare function getRecoveryToastContent(errorType: RecoveryErrorType): {
9
+ title: string;
10
+ message: string;
11
+ };
12
+ export declare function getRecoverySuccessToast(): {
13
+ title: string;
14
+ message: string;
15
+ };
16
+ export declare function getRecoveryFailureToast(): {
17
+ title: string;
18
+ message: string;
19
+ };
20
+ export interface SessionRecoveryHook {
21
+ handleSessionRecovery: (info: MessageInfo) => Promise<boolean>;
22
+ isRecoverableError: (error: unknown) => boolean;
23
+ setOnAbortCallback: (callback: (sessionID: string) => void) => void;
24
+ setOnRecoveryCompleteCallback: (callback: (sessionID: string) => void) => void;
25
+ }
26
+ export interface SessionRecoveryContext {
27
+ client: PluginClient;
28
+ directory: string;
29
+ }
30
+ export declare function createSessionRecoveryHook(ctx: SessionRecoveryContext, config: PluginConfig): SessionRecoveryHook | null;
31
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../lib/recovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS/C,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAEX,iBAAiB,EACjB,YAAY,EAEb,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AAE1E,KAAK,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAwC1C,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAsBjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE1D;AAsKD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,iBAAiB,GAAG;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAWA;AAED,wBAAgB,uBAAuB,IAAI;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAKA;AAED,wBAAgB,uBAAuB,IAAI;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAKA;AAED,MAAM,WAAW,mBAAmB;IAClC,qBAAqB,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,kBAAkB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAChD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;IACpE,6BAA6B,EAAE,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;CAChF;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,sBAAsB,EAC3B,MAAM,EAAE,YAAY,GACnB,mBAAmB,GAAG,IAAI,CA6H5B"}
@@ -0,0 +1,313 @@
1
+ import { createLogger } from "./logger.js";
2
+ import { readParts, findMessagesWithThinkingBlocks, findMessagesWithOrphanThinking, findMessageByIndexNeedingThinking, prependThinkingPart, stripThinkingParts, } from "./recovery/storage.js";
3
+ const RECOVERY_RESUME_TEXT = "[session recovered - continuing previous task]";
4
+ function getErrorMessage(error) {
5
+ if (!error)
6
+ return "";
7
+ if (typeof error === "string")
8
+ return error.toLowerCase();
9
+ const errorObj = error;
10
+ const paths = [
11
+ errorObj.data,
12
+ errorObj.error,
13
+ errorObj,
14
+ errorObj.data?.error,
15
+ ];
16
+ for (const obj of paths) {
17
+ if (obj && typeof obj === "object") {
18
+ const msg = obj.message;
19
+ if (typeof msg === "string" && msg.length > 0) {
20
+ return msg.toLowerCase();
21
+ }
22
+ }
23
+ }
24
+ try {
25
+ return JSON.stringify(error).toLowerCase();
26
+ }
27
+ catch {
28
+ return "";
29
+ }
30
+ }
31
+ function extractMessageIndex(error) {
32
+ const message = getErrorMessage(error);
33
+ const match = message.match(/messages\.(\d+)/);
34
+ if (!match || !match[1])
35
+ return null;
36
+ return parseInt(match[1], 10);
37
+ }
38
+ export function detectErrorType(error) {
39
+ const message = getErrorMessage(error);
40
+ if (message.includes("tool_use") && message.includes("tool_result")) {
41
+ return "tool_result_missing";
42
+ }
43
+ if (message.includes("thinking") &&
44
+ (message.includes("first block") ||
45
+ message.includes("must start with") ||
46
+ message.includes("preceeding") ||
47
+ (message.includes("expected") && message.includes("found")))) {
48
+ return "thinking_block_order";
49
+ }
50
+ if (message.includes("thinking is disabled") && message.includes("cannot contain")) {
51
+ return "thinking_disabled_violation";
52
+ }
53
+ return null;
54
+ }
55
+ export function isRecoverableError(error) {
56
+ return detectErrorType(error) !== null;
57
+ }
58
+ function extractToolUseIds(parts) {
59
+ return parts
60
+ .filter((p) => p.type === "tool_use" && !!p.id)
61
+ .map((p) => p.id);
62
+ }
63
+ async function sendToolResultsForRecovery(client, sessionID, toolResultParts) {
64
+ const bodyWithParts = {
65
+ parts: toolResultParts,
66
+ };
67
+ await client.session.prompt({
68
+ path: { id: sessionID },
69
+ body: bodyWithParts,
70
+ });
71
+ }
72
+ async function recoverToolResultMissing(client, sessionID, failedMsg) {
73
+ let parts = failedMsg.parts || [];
74
+ if (parts.length === 0 && failedMsg.info?.id) {
75
+ const storedParts = readParts(failedMsg.info.id);
76
+ parts = storedParts.map((p) => ({
77
+ type: p.type === "tool" ? "tool_use" : p.type,
78
+ id: "callID" in p ? p.callID : p.id,
79
+ name: "tool" in p ? p.tool : undefined,
80
+ input: "state" in p ? p.state?.input : undefined,
81
+ }));
82
+ }
83
+ const toolUseIds = extractToolUseIds(parts);
84
+ if (toolUseIds.length === 0) {
85
+ return false;
86
+ }
87
+ const toolResultParts = toolUseIds.map((id) => ({
88
+ type: "tool_result",
89
+ tool_use_id: id,
90
+ content: "Operation cancelled by user (ESC pressed)",
91
+ }));
92
+ try {
93
+ await sendToolResultsForRecovery(client, sessionID, toolResultParts);
94
+ return true;
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
100
+ function recoverThinkingBlockOrder(sessionID, _failedMsg, error) {
101
+ const targetIndex = extractMessageIndex(error);
102
+ if (targetIndex !== null) {
103
+ const targetMessageID = findMessageByIndexNeedingThinking(sessionID, targetIndex);
104
+ if (targetMessageID) {
105
+ return prependThinkingPart(sessionID, targetMessageID);
106
+ }
107
+ }
108
+ const orphanMessages = findMessagesWithOrphanThinking(sessionID);
109
+ if (orphanMessages.length === 0) {
110
+ return false;
111
+ }
112
+ let anySuccess = false;
113
+ for (const messageID of orphanMessages) {
114
+ if (prependThinkingPart(sessionID, messageID)) {
115
+ anySuccess = true;
116
+ }
117
+ }
118
+ return anySuccess;
119
+ }
120
+ function recoverThinkingDisabledViolation(sessionID, _failedMsg) {
121
+ const messagesWithThinking = findMessagesWithThinkingBlocks(sessionID);
122
+ if (messagesWithThinking.length === 0) {
123
+ return false;
124
+ }
125
+ let anySuccess = false;
126
+ for (const messageID of messagesWithThinking) {
127
+ if (stripThinkingParts(messageID)) {
128
+ anySuccess = true;
129
+ }
130
+ }
131
+ return anySuccess;
132
+ }
133
+ function findLastUserMessage(messages) {
134
+ for (let i = messages.length - 1; i >= 0; i--) {
135
+ if (messages[i]?.info?.role === "user") {
136
+ return messages[i];
137
+ }
138
+ }
139
+ return undefined;
140
+ }
141
+ function extractResumeConfig(userMessage, sessionID) {
142
+ return {
143
+ sessionID,
144
+ agent: userMessage?.info?.agent,
145
+ model: userMessage?.info?.model,
146
+ };
147
+ }
148
+ async function resumeSession(client, config, directory) {
149
+ try {
150
+ await client.session.prompt({
151
+ path: { id: config.sessionID },
152
+ body: {
153
+ parts: [{ type: "text", text: RECOVERY_RESUME_TEXT }],
154
+ agent: config.agent,
155
+ model: config.model,
156
+ },
157
+ query: { directory },
158
+ });
159
+ return true;
160
+ }
161
+ catch {
162
+ return false;
163
+ }
164
+ }
165
+ const TOAST_TITLES = {
166
+ tool_result_missing: "Tool Crash Recovery",
167
+ thinking_block_order: "Thinking Block Recovery",
168
+ thinking_disabled_violation: "Thinking Strip Recovery",
169
+ };
170
+ const TOAST_MESSAGES = {
171
+ tool_result_missing: "Injecting cancelled tool results...",
172
+ thinking_block_order: "Fixing message structure...",
173
+ thinking_disabled_violation: "Stripping thinking blocks...",
174
+ };
175
+ export function getRecoveryToastContent(errorType) {
176
+ if (!errorType) {
177
+ return {
178
+ title: "Session Recovery",
179
+ message: "Attempting to recover session...",
180
+ };
181
+ }
182
+ return {
183
+ title: TOAST_TITLES[errorType] || "Session Recovery",
184
+ message: TOAST_MESSAGES[errorType] || "Attempting to recover session...",
185
+ };
186
+ }
187
+ export function getRecoverySuccessToast() {
188
+ return {
189
+ title: "Session Recovered",
190
+ message: "Continuing where you left off...",
191
+ };
192
+ }
193
+ export function getRecoveryFailureToast() {
194
+ return {
195
+ title: "Recovery Failed",
196
+ message: "Please retry or start a new session.",
197
+ };
198
+ }
199
+ export function createSessionRecoveryHook(ctx, config) {
200
+ if (!config.sessionRecovery) {
201
+ return null;
202
+ }
203
+ const { client, directory } = ctx;
204
+ const processingErrors = new Set();
205
+ let onAbortCallback = null;
206
+ let onRecoveryCompleteCallback = null;
207
+ const setOnAbortCallback = (callback) => {
208
+ onAbortCallback = callback;
209
+ };
210
+ const setOnRecoveryCompleteCallback = (callback) => {
211
+ onRecoveryCompleteCallback = callback;
212
+ };
213
+ const handleSessionRecovery = async (info) => {
214
+ if (!info || info.role !== "assistant" || !info.error)
215
+ return false;
216
+ const errorType = detectErrorType(info.error);
217
+ if (!errorType)
218
+ return false;
219
+ const sessionID = info.sessionID;
220
+ if (!sessionID)
221
+ return false;
222
+ let assistantMsgID = info.id;
223
+ const log = createLogger("session-recovery");
224
+ log.debug("Recovery attempt started", {
225
+ errorType,
226
+ sessionID,
227
+ providedMsgID: assistantMsgID ?? "none",
228
+ });
229
+ if (onAbortCallback) {
230
+ onAbortCallback(sessionID);
231
+ }
232
+ await client.session.abort({ path: { id: sessionID } }).catch(() => { });
233
+ const messagesResp = await client.session.messages({
234
+ path: { id: sessionID },
235
+ query: { directory },
236
+ });
237
+ const msgs = messagesResp.data;
238
+ if (!assistantMsgID && msgs && msgs.length > 0) {
239
+ for (let i = msgs.length - 1; i >= 0; i--) {
240
+ const m = msgs[i];
241
+ if (m && m.info?.role === "assistant" && m.info?.id) {
242
+ assistantMsgID = m.info.id;
243
+ log.debug("Found assistant message ID from session messages", {
244
+ msgID: assistantMsgID,
245
+ msgIndex: i,
246
+ });
247
+ break;
248
+ }
249
+ }
250
+ }
251
+ if (!assistantMsgID) {
252
+ log.debug("No assistant message ID found, cannot recover");
253
+ return false;
254
+ }
255
+ if (processingErrors.has(assistantMsgID))
256
+ return false;
257
+ processingErrors.add(assistantMsgID);
258
+ try {
259
+ const failedMsg = msgs?.find((m) => m.info?.id === assistantMsgID);
260
+ if (!failedMsg) {
261
+ return false;
262
+ }
263
+ const toastContent = getRecoveryToastContent(errorType);
264
+ await client.tui
265
+ .showToast({
266
+ body: {
267
+ title: toastContent.title,
268
+ message: toastContent.message,
269
+ variant: "warning",
270
+ },
271
+ })
272
+ .catch(() => { });
273
+ let success = false;
274
+ if (errorType === "tool_result_missing") {
275
+ success = await recoverToolResultMissing(client, sessionID, failedMsg);
276
+ }
277
+ else if (errorType === "thinking_block_order") {
278
+ success = recoverThinkingBlockOrder(sessionID, failedMsg, info.error);
279
+ if (success && config.autoResume) {
280
+ const lastUser = findLastUserMessage(msgs ?? []);
281
+ const resumeConfig = extractResumeConfig(lastUser, sessionID);
282
+ await resumeSession(client, resumeConfig, directory);
283
+ }
284
+ }
285
+ else if (errorType === "thinking_disabled_violation") {
286
+ success = recoverThinkingDisabledViolation(sessionID, failedMsg);
287
+ if (success && config.autoResume) {
288
+ const lastUser = findLastUserMessage(msgs ?? []);
289
+ const resumeConfig = extractResumeConfig(lastUser, sessionID);
290
+ await resumeSession(client, resumeConfig, directory);
291
+ }
292
+ }
293
+ return success;
294
+ }
295
+ catch (err) {
296
+ log.error("Recovery failed", { error: String(err) });
297
+ return false;
298
+ }
299
+ finally {
300
+ processingErrors.delete(assistantMsgID);
301
+ if (sessionID && onRecoveryCompleteCallback) {
302
+ onRecoveryCompleteCallback(sessionID);
303
+ }
304
+ }
305
+ };
306
+ return {
307
+ handleSessionRecovery,
308
+ isRecoverableError,
309
+ setOnAbortCallback,
310
+ setOnRecoveryCompleteCallback,
311
+ };
312
+ }
313
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../lib/recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Refresh Queue Module
3
+ *
4
+ * Prevents race conditions when multiple concurrent requests try to refresh
5
+ * the same account's token simultaneously. Instead of firing parallel refresh
6
+ * requests, subsequent callers await the existing in-flight refresh.
7
+ *
8
+ * Ported from antigravity-auth refresh-queue.ts pattern.
9
+ */
10
+ import type { TokenResult } from "./types.js";
11
+ export interface RefreshQueueMetrics {
12
+ started: number;
13
+ deduplicated: number;
14
+ rotationReused: number;
15
+ succeeded: number;
16
+ failed: number;
17
+ exceptions: number;
18
+ rotated: number;
19
+ staleEvictions: number;
20
+ lastDurationMs: number;
21
+ lastFailureReason: string | null;
22
+ pending: number;
23
+ }
24
+ /**
25
+ * Manages queued token refresh operations to prevent race conditions.
26
+ *
27
+ * When multiple concurrent requests need to refresh the same account's token,
28
+ * only the first request triggers the actual refresh. Subsequent requests
29
+ * await the same promise, ensuring:
30
+ * - No duplicate refresh API calls for the same refresh token
31
+ * - Consistent token state across all waiting callers
32
+ * - Reduced load on OpenAI's token endpoint
33
+ *
34
+ * Token Rotation Handling:
35
+ * When OpenAI rotates the refresh token during a refresh operation, we maintain
36
+ * a mapping from old token → new token. This ensures that requests arriving with
37
+ * either the old or new token will find the in-flight refresh and not trigger
38
+ * duplicate refreshes.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const queue = new RefreshQueue();
43
+ *
44
+ * // These three concurrent calls will only trigger ONE actual refresh
45
+ * const [result1, result2, result3] = await Promise.all([
46
+ * queue.refresh(refreshToken),
47
+ * queue.refresh(refreshToken),
48
+ * queue.refresh(refreshToken),
49
+ * ]);
50
+ *
51
+ * // All three get the same result
52
+ * console.log(result1 === result2); // true (same object reference)
53
+ * ```
54
+ */
55
+ export declare class RefreshQueue {
56
+ private pending;
57
+ private metrics;
58
+ /**
59
+ * Maps old refresh tokens to new tokens after rotation.
60
+ * This allows lookups with either old or new token to find the same entry.
61
+ * Format: oldToken → newToken
62
+ */
63
+ private tokenRotationMap;
64
+ /**
65
+ * Maximum time to keep a refresh entry in the queue (prevents memory leaks
66
+ * from stuck requests). After this timeout, the entry is removed and new
67
+ * callers will trigger a fresh refresh.
68
+ */
69
+ private readonly maxEntryAgeMs;
70
+ /**
71
+ * Create a new RefreshQueue instance.
72
+ * @param maxEntryAgeMs - Maximum age for pending entries before cleanup (default: 30s)
73
+ */
74
+ constructor(maxEntryAgeMs?: number);
75
+ /**
76
+ * Refresh a token, deduplicating concurrent requests for the same refresh token.
77
+ *
78
+ * If a refresh is already in-flight for this token, returns the existing promise.
79
+ * Otherwise, initiates a new refresh and caches the promise for other callers.
80
+ *
81
+ * @param refreshToken - The refresh token to use
82
+ * @returns Token result (success with new tokens, or failure)
83
+ */
84
+ refresh(refreshToken: string): Promise<TokenResult>;
85
+ private findOriginalToken;
86
+ private cleanupRotationMapping;
87
+ private executeRefreshWithRotationTracking;
88
+ /**
89
+ * Execute the actual refresh and log results.
90
+ */
91
+ private executeRefresh;
92
+ /**
93
+ * Remove stale entries that have been pending too long.
94
+ * This prevents memory leaks from stuck or abandoned refresh operations.
95
+ */
96
+ private cleanup;
97
+ /**
98
+ * Check if there's an in-flight refresh for a given token.
99
+ * @param refreshToken - The refresh token to check
100
+ * @returns True if refresh is in progress
101
+ */
102
+ isRefreshing(refreshToken: string): boolean;
103
+ /**
104
+ * Get the number of pending refresh operations.
105
+ * Useful for debugging and monitoring.
106
+ */
107
+ get pendingCount(): number;
108
+ /**
109
+ * Clear all pending entries (primarily for testing).
110
+ */
111
+ clear(): void;
112
+ getMetricsSnapshot(): RefreshQueueMetrics;
113
+ }
114
+ /**
115
+ * Get the singleton RefreshQueue instance.
116
+ * @param maxEntryAgeMs - Maximum age for pending entries (only used on first call)
117
+ * @returns The global RefreshQueue instance
118
+ */
119
+ export declare function getRefreshQueue(maxEntryAgeMs?: number): RefreshQueue;
120
+ /**
121
+ * Reset the singleton instance (primarily for testing).
122
+ */
123
+ export declare function resetRefreshQueue(): void;
124
+ /**
125
+ * Convenience function to refresh a token using the singleton queue.
126
+ * @param refreshToken - The refresh token to use
127
+ * @returns Token result
128
+ */
129
+ export declare function queuedRefresh(refreshToken: string): Promise<TokenResult>;
130
+ export declare function getRefreshQueueMetrics(): RefreshQueueMetrics;
131
+ //# sourceMappingURL=refresh-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-queue.d.ts","sourceRoot":"","sources":["../../lib/refresh-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAa9C,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CAChB;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,OAAO,CAA+C;IAE9D;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAkC;IAE1D;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;;OAGG;gBACS,aAAa,GAAE,MAAe;IAI1C;;;;;;;;OAQG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAiDzD,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,sBAAsB;YAShB,kCAAkC;IAehD;;OAEG;YACW,cAAc;IAgD5B;;;OAGG;IACH,OAAO,CAAC,OAAO;IAuBf;;;;OAIG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb,kBAAkB,IAAI,mBAAmB;CAM1C;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,CAKpE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE9E;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,CAE5D"}