1import { assert, it } from "@effect/vitest";
2import { classifyCodexManagedError } from "./CodexManagedErrors.ts";
3it("maps streamed failures to safe actionable messages and reconnect decisions", () => {
4 assert.deepEqual(
5 classifyCodexManagedError({
6 error: {
7 code: "subscription_sharing_v2_invalid_user",
8 message: "opaque token dummy-sensitive",
9 },
10 }),
11 {
12 message:
13 "ChatGPT could not validate this connection. Check the selected account and sharing permissions.",
14 code: "subscription_sharing_v2_invalid_user",
15 usageLimit: false,
16 },
17 );
18 assert.include(
19 classifyCodexManagedError("subscription_sharing_usage_limit_exceeded")!.message,
20 "Usage settings",
21 );
22 assert.equal(
23 classifyCodexManagedError("subscription_sharing_usage_limit_exceeded")!.code,
24 "subscription_sharing_usage_limit_exceeded",
25 );
26 assert.include(
27 classifyCodexManagedError("subscription_sharing_unsupported_capability")!.message,
28 "feature",
29 );
30 assert.isUndefined(classifyCodexManagedError(undefined));
31 assert.isUndefined(classifyCodexManagedError({ error: "unknown" }));
32});
33
34it("distinguishes unsupported tools from input items without exposing the raw response", () => {
35 const code = "subscription_sharing_unsupported_capability";
36 for (const [detail, expected] of [
37 ["tool 'namespace' is not supported", "tool namespace"],
38 ["input item 'additional_tools' is not supported", "input item"],
39 ]) {
40 const response = { error: { code, message: `${detail}; dummy-sensitive` } };
41 for (const value of [response, JSON.stringify(response)]) {
42 const failure = classifyCodexManagedError(value);
43 assert.isDefined(failure);
44 assert.include(failure!.message, expected!);
45 assert.notInclude(failure!.message, "dummy-sensitive");
46 }
47 }
48});
49
50it("preserves credentials for the current subscriber and permission error codes", () => {
51 for (const code of [
52 "subscription_sharing_invalid_user",
53 "subscription_sharing_user_not_eligible",
54 "subscription_sharing_route_not_supported",
55 "subscription_sharing_user_unavailable",
56 "chatpass_v2_scope_not_authorized",
57 "chatpass_v2_invalid_authorization_context",
58 ]) {
59 assert.strictEqual(classifyCodexManagedError({ error: { code } })?.code, code);
60 }
61});
62
63it("treats only ChatGPT sharing allowance limits as usage limits", () => {
64 assert.deepInclude(
65 classifyCodexManagedError(
66 "stream disconnected before completion: The ChatGPT user has reached their Subscription Sharing usage limit. Ask the user to try again after their usage limit resets or use an API key instead.",
67 ),
68 { code: "subscription_sharing_usage_limit_exceeded", usageLimit: true },
69 );
70 assert.isTrue(
71 classifyCodexManagedError("subscription_sharing_workplace_individual_usage_limit_exceeded")!
72 .usageLimit,
73 );
74 assert.isFalse(classifyCodexManagedError("subscription_sharing_usage_unavailable")!.usageLimit);
75 assert.isUndefined(
76 classifyCodexManagedError("stream disconnected before completion: error decoding response"),
77 );
78 assert.isUndefined(classifyCodexManagedError("You've hit your usage limit."));
79});
80
81it("does not infer sharing codes from incidental mentions in turn errors", () => {
82 const mention = "Transport unavailable; this is not subscription_sharing_usage_limit_exceeded.";
83 for (const value of [
84 mention,
85 { message: mention },
86 { message: "Transport unavailable", additionalDetails: mention },
87 { code: "other", message: mention },
88 ]) {
89 assert.isUndefined(classifyCodexManagedError(value));
90 }
91 assert.deepInclude(
92 classifyCodexManagedError({
93 error: { code: "subscription_sharing_usage_unavailable", message: mention },
94 }),
95 { code: "subscription_sharing_usage_unavailable", usageLimit: false },
96 );
97});
98
99it("reads sharing codes from structured errors and exact turn details", () => {
100 const code = "subscription_sharing_usage_limit_exceeded";
101 const response = { error: { code } };
102 for (const value of [
103 response,
104 JSON.stringify(response),
105 { message: "Provider stopped", additionalDetails: code },
106 { message: "Provider stopped", additionalDetails: JSON.stringify(response) },
107 ]) {
108 assert.deepInclude(classifyCodexManagedError(value), { code, usageLimit: true });
109 }
110});