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.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/assets/opencode-logo-ornate-dark.svg +18 -0
- package/assets/readme-hero.svg +31 -0
- package/config/README.md +96 -0
- package/config/minimal-opencode.json +13 -0
- package/config/opencode-legacy.json +871 -0
- package/config/opencode-modern.json +358 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5008 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/accounts/rate-limits.d.ts +22 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -0
- package/dist/lib/accounts/rate-limits.js +63 -0
- package/dist/lib/accounts/rate-limits.js.map +1 -0
- package/dist/lib/accounts.d.ts +134 -0
- package/dist/lib/accounts.d.ts.map +1 -0
- package/dist/lib/accounts.js +795 -0
- package/dist/lib/accounts.js.map +1 -0
- package/dist/lib/audit.d.ts +45 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +154 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/auth/auth.d.ts +51 -0
- package/dist/lib/auth/auth.d.ts.map +1 -0
- package/dist/lib/auth/auth.js +191 -0
- package/dist/lib/auth/auth.js.map +1 -0
- package/dist/lib/auth/browser.d.ts +17 -0
- package/dist/lib/auth/browser.d.ts.map +1 -0
- package/dist/lib/auth/browser.js +90 -0
- package/dist/lib/auth/browser.js.map +1 -0
- package/dist/lib/auth/device-code.d.ts +28 -0
- package/dist/lib/auth/device-code.d.ts.map +1 -0
- package/dist/lib/auth/device-code.js +215 -0
- package/dist/lib/auth/device-code.js.map +1 -0
- package/dist/lib/auth/login-runner.d.ts +55 -0
- package/dist/lib/auth/login-runner.d.ts.map +1 -0
- package/dist/lib/auth/login-runner.js +631 -0
- package/dist/lib/auth/login-runner.js.map +1 -0
- package/dist/lib/auth/server.d.ts +10 -0
- package/dist/lib/auth/server.d.ts.map +1 -0
- package/dist/lib/auth/server.js +98 -0
- package/dist/lib/auth/server.js.map +1 -0
- package/dist/lib/auth/token-utils.d.ts +60 -0
- package/dist/lib/auth/token-utils.d.ts.map +1 -0
- package/dist/lib/auth/token-utils.js +416 -0
- package/dist/lib/auth/token-utils.js.map +1 -0
- package/dist/lib/auth-rate-limit.d.ts +20 -0
- package/dist/lib/auth-rate-limit.d.ts.map +1 -0
- package/dist/lib/auth-rate-limit.js +91 -0
- package/dist/lib/auth-rate-limit.js.map +1 -0
- package/dist/lib/auto-update-checker.d.ts +10 -0
- package/dist/lib/auto-update-checker.d.ts.map +1 -0
- package/dist/lib/auto-update-checker.js +129 -0
- package/dist/lib/auto-update-checker.js.map +1 -0
- package/dist/lib/circuit-breaker.d.ts +34 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.js +124 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/cli.d.ts +46 -0
- package/dist/lib/cli.d.ts.map +1 -0
- package/dist/lib/cli.js +192 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/config.d.ts +42 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +314 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +90 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +90 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/context-overflow.d.ts +27 -0
- package/dist/lib/context-overflow.d.ts.map +1 -0
- package/dist/lib/context-overflow.js +124 -0
- package/dist/lib/context-overflow.js.map +1 -0
- package/dist/lib/errors.d.ts +113 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +103 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/health.d.ts +33 -0
- package/dist/lib/health.d.ts.map +1 -0
- package/dist/lib/health.js +70 -0
- package/dist/lib/health.js.map +1 -0
- package/dist/lib/index.d.ts +20 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +20 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logger.d.ts +40 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +337 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/oauth-success.html +712 -0
- package/dist/lib/parallel-probe.d.ts +28 -0
- package/dist/lib/parallel-probe.d.ts.map +1 -0
- package/dist/lib/parallel-probe.js +97 -0
- package/dist/lib/parallel-probe.js.map +1 -0
- package/dist/lib/proactive-refresh.d.ts +67 -0
- package/dist/lib/proactive-refresh.d.ts.map +1 -0
- package/dist/lib/proactive-refresh.js +161 -0
- package/dist/lib/proactive-refresh.js.map +1 -0
- package/dist/lib/prompts/codex-opencode-bridge.d.ts +20 -0
- package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -0
- package/dist/lib/prompts/codex-opencode-bridge.js +126 -0
- package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -0
- package/dist/lib/prompts/codex.d.ts +41 -0
- package/dist/lib/prompts/codex.d.ts.map +1 -0
- package/dist/lib/prompts/codex.js +400 -0
- package/dist/lib/prompts/codex.js.map +1 -0
- package/dist/lib/prompts/opencode-codex.d.ts +25 -0
- package/dist/lib/prompts/opencode-codex.d.ts.map +1 -0
- package/dist/lib/prompts/opencode-codex.js +223 -0
- package/dist/lib/prompts/opencode-codex.js.map +1 -0
- package/dist/lib/recovery/constants.d.ts +12 -0
- package/dist/lib/recovery/constants.d.ts.map +1 -0
- package/dist/lib/recovery/constants.js +25 -0
- package/dist/lib/recovery/constants.js.map +1 -0
- package/dist/lib/recovery/index.d.ts +12 -0
- package/dist/lib/recovery/index.d.ts.map +1 -0
- package/dist/lib/recovery/index.js +12 -0
- package/dist/lib/recovery/index.js.map +1 -0
- package/dist/lib/recovery/storage.d.ts +24 -0
- package/dist/lib/recovery/storage.d.ts.map +1 -0
- package/dist/lib/recovery/storage.js +362 -0
- package/dist/lib/recovery/storage.js.map +1 -0
- package/dist/lib/recovery/types.d.ts +116 -0
- package/dist/lib/recovery/types.d.ts.map +1 -0
- package/dist/lib/recovery/types.js +7 -0
- package/dist/lib/recovery/types.js.map +1 -0
- package/dist/lib/recovery.d.ts +31 -0
- package/dist/lib/recovery.d.ts.map +1 -0
- package/dist/lib/recovery.js +313 -0
- package/dist/lib/recovery.js.map +1 -0
- package/dist/lib/refresh-queue.d.ts +131 -0
- package/dist/lib/refresh-queue.d.ts.map +1 -0
- package/dist/lib/refresh-queue.js +297 -0
- package/dist/lib/refresh-queue.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts +142 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
- package/dist/lib/request/fetch-helpers.js +749 -0
- package/dist/lib/request/fetch-helpers.js.map +1 -0
- package/dist/lib/request/helpers/input-utils.d.ts +7 -0
- package/dist/lib/request/helpers/input-utils.d.ts.map +1 -0
- package/dist/lib/request/helpers/input-utils.js +214 -0
- package/dist/lib/request/helpers/input-utils.js.map +1 -0
- package/dist/lib/request/helpers/model-map.d.ts +22 -0
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -0
- package/dist/lib/request/helpers/model-map.js +183 -0
- package/dist/lib/request/helpers/model-map.js.map +1 -0
- package/dist/lib/request/helpers/tool-utils.d.ts +29 -0
- package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -0
- package/dist/lib/request/helpers/tool-utils.js +117 -0
- package/dist/lib/request/helpers/tool-utils.js.map +1 -0
- package/dist/lib/request/rate-limit-backoff.d.ts +17 -0
- package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -0
- package/dist/lib/request/rate-limit-backoff.js +83 -0
- package/dist/lib/request/rate-limit-backoff.js.map +1 -0
- package/dist/lib/request/request-transformer.d.ts +107 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -0
- package/dist/lib/request/request-transformer.js +930 -0
- package/dist/lib/request/request-transformer.js.map +1 -0
- package/dist/lib/request/response-handler.d.ts +23 -0
- package/dist/lib/request/response-handler.d.ts.map +1 -0
- package/dist/lib/request/response-handler.js +245 -0
- package/dist/lib/request/response-handler.js.map +1 -0
- package/dist/lib/request/retry-budget.d.ts +19 -0
- package/dist/lib/request/retry-budget.d.ts.map +1 -0
- package/dist/lib/request/retry-budget.js +99 -0
- package/dist/lib/request/retry-budget.js.map +1 -0
- package/dist/lib/rotation.d.ts +130 -0
- package/dist/lib/rotation.d.ts.map +1 -0
- package/dist/lib/rotation.js +301 -0
- package/dist/lib/rotation.js.map +1 -0
- package/dist/lib/runtime-contracts.d.ts +16 -0
- package/dist/lib/runtime-contracts.d.ts.map +1 -0
- package/dist/lib/runtime-contracts.js +24 -0
- package/dist/lib/runtime-contracts.js.map +1 -0
- package/dist/lib/schemas.d.ts +457 -0
- package/dist/lib/schemas.d.ts.map +1 -0
- package/dist/lib/schemas.js +283 -0
- package/dist/lib/schemas.js.map +1 -0
- package/dist/lib/shutdown.d.ts +7 -0
- package/dist/lib/shutdown.d.ts.map +1 -0
- package/dist/lib/shutdown.js +43 -0
- package/dist/lib/shutdown.js.map +1 -0
- package/dist/lib/storage/migrations.d.ts +65 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -0
- package/dist/lib/storage/migrations.js +44 -0
- package/dist/lib/storage/migrations.js.map +1 -0
- package/dist/lib/storage/paths.d.ts +16 -0
- package/dist/lib/storage/paths.d.ts.map +1 -0
- package/dist/lib/storage/paths.js +92 -0
- package/dist/lib/storage/paths.js.map +1 -0
- package/dist/lib/storage.d.ts +157 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/storage.js +1169 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/table-formatter.d.ts +32 -0
- package/dist/lib/table-formatter.d.ts.map +1 -0
- package/dist/lib/table-formatter.js +44 -0
- package/dist/lib/table-formatter.js.map +1 -0
- package/dist/lib/types.d.ts +131 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/ui/ansi.d.ts +22 -0
- package/dist/lib/ui/ansi.d.ts.map +1 -0
- package/dist/lib/ui/ansi.js +38 -0
- package/dist/lib/ui/ansi.js.map +1 -0
- package/dist/lib/ui/auth-menu.d.ts +39 -0
- package/dist/lib/ui/auth-menu.d.ts.map +1 -0
- package/dist/lib/ui/auth-menu.js +186 -0
- package/dist/lib/ui/auth-menu.js.map +1 -0
- package/dist/lib/ui/beginner.d.ts +62 -0
- package/dist/lib/ui/beginner.d.ts.map +1 -0
- package/dist/lib/ui/beginner.js +259 -0
- package/dist/lib/ui/beginner.js.map +1 -0
- package/dist/lib/ui/confirm.d.ts +2 -0
- package/dist/lib/ui/confirm.d.ts.map +1 -0
- package/dist/lib/ui/confirm.js +21 -0
- package/dist/lib/ui/confirm.js.map +1 -0
- package/dist/lib/ui/format.d.ts +9 -0
- package/dist/lib/ui/format.d.ts.map +1 -0
- package/dist/lib/ui/format.js +49 -0
- package/dist/lib/ui/format.js.map +1 -0
- package/dist/lib/ui/runtime.d.ts +11 -0
- package/dist/lib/ui/runtime.d.ts.map +1 -0
- package/dist/lib/ui/runtime.js +31 -0
- package/dist/lib/ui/runtime.js.map +1 -0
- package/dist/lib/ui/select.d.ts +20 -0
- package/dist/lib/ui/select.d.ts.map +1 -0
- package/dist/lib/ui/select.js +343 -0
- package/dist/lib/ui/select.js.map +1 -0
- package/dist/lib/ui/theme.d.ts +34 -0
- package/dist/lib/ui/theme.d.ts.map +1 -0
- package/dist/lib/ui/theme.js +84 -0
- package/dist/lib/ui/theme.js.map +1 -0
- package/dist/lib/utils.d.ts +29 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +54 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +113 -0
- package/scripts/audit-dev-allowlist.js +114 -0
- package/scripts/copy-oauth-success.js +37 -0
- package/scripts/install-oc-codex-multi-auth-core.js +381 -0
- package/scripts/install-oc-codex-multi-auth.js +15 -0
- package/scripts/test-all-models.sh +260 -0
- 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"}
|