@agent-relay/wrapper 0.1.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 (115) hide show
  1. package/dist/__fixtures__/claude-outputs.d.ts +49 -0
  2. package/dist/__fixtures__/claude-outputs.d.ts.map +1 -0
  3. package/dist/__fixtures__/claude-outputs.js +443 -0
  4. package/dist/__fixtures__/claude-outputs.js.map +1 -0
  5. package/dist/__fixtures__/codex-outputs.d.ts +9 -0
  6. package/dist/__fixtures__/codex-outputs.d.ts.map +1 -0
  7. package/dist/__fixtures__/codex-outputs.js +94 -0
  8. package/dist/__fixtures__/codex-outputs.js.map +1 -0
  9. package/dist/__fixtures__/gemini-outputs.d.ts +19 -0
  10. package/dist/__fixtures__/gemini-outputs.d.ts.map +1 -0
  11. package/dist/__fixtures__/gemini-outputs.js +144 -0
  12. package/dist/__fixtures__/gemini-outputs.js.map +1 -0
  13. package/dist/__fixtures__/index.d.ts +68 -0
  14. package/dist/__fixtures__/index.d.ts.map +1 -0
  15. package/dist/__fixtures__/index.js +44 -0
  16. package/dist/__fixtures__/index.js.map +1 -0
  17. package/dist/auth-detection.d.ts +49 -0
  18. package/dist/auth-detection.d.ts.map +1 -0
  19. package/dist/auth-detection.js +199 -0
  20. package/dist/auth-detection.js.map +1 -0
  21. package/dist/base-wrapper.d.ts +225 -0
  22. package/dist/base-wrapper.d.ts.map +1 -0
  23. package/dist/base-wrapper.js +572 -0
  24. package/dist/base-wrapper.js.map +1 -0
  25. package/dist/client.d.ts +254 -0
  26. package/dist/client.d.ts.map +1 -0
  27. package/dist/client.js +801 -0
  28. package/dist/client.js.map +1 -0
  29. package/dist/id-generator.d.ts +35 -0
  30. package/dist/id-generator.d.ts.map +1 -0
  31. package/dist/id-generator.js +60 -0
  32. package/dist/id-generator.js.map +1 -0
  33. package/dist/idle-detector.d.ts +110 -0
  34. package/dist/idle-detector.d.ts.map +1 -0
  35. package/dist/idle-detector.js +304 -0
  36. package/dist/idle-detector.js.map +1 -0
  37. package/dist/inbox.d.ts +37 -0
  38. package/dist/inbox.d.ts.map +1 -0
  39. package/dist/inbox.js +73 -0
  40. package/dist/inbox.js.map +1 -0
  41. package/dist/index.d.ts +37 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +47 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/parser.d.ts +236 -0
  46. package/dist/parser.d.ts.map +1 -0
  47. package/dist/parser.js +1238 -0
  48. package/dist/parser.js.map +1 -0
  49. package/dist/prompt-composer.d.ts +67 -0
  50. package/dist/prompt-composer.d.ts.map +1 -0
  51. package/dist/prompt-composer.js +168 -0
  52. package/dist/prompt-composer.js.map +1 -0
  53. package/dist/relay-pty-orchestrator.d.ts +407 -0
  54. package/dist/relay-pty-orchestrator.d.ts.map +1 -0
  55. package/dist/relay-pty-orchestrator.js +1885 -0
  56. package/dist/relay-pty-orchestrator.js.map +1 -0
  57. package/dist/shared.d.ts +201 -0
  58. package/dist/shared.d.ts.map +1 -0
  59. package/dist/shared.js +341 -0
  60. package/dist/shared.js.map +1 -0
  61. package/dist/stuck-detector.d.ts +161 -0
  62. package/dist/stuck-detector.d.ts.map +1 -0
  63. package/dist/stuck-detector.js +402 -0
  64. package/dist/stuck-detector.js.map +1 -0
  65. package/dist/tmux-resolver.d.ts +55 -0
  66. package/dist/tmux-resolver.d.ts.map +1 -0
  67. package/dist/tmux-resolver.js +175 -0
  68. package/dist/tmux-resolver.js.map +1 -0
  69. package/dist/tmux-wrapper.d.ts +345 -0
  70. package/dist/tmux-wrapper.d.ts.map +1 -0
  71. package/dist/tmux-wrapper.js +1747 -0
  72. package/dist/tmux-wrapper.js.map +1 -0
  73. package/dist/trajectory-integration.d.ts +292 -0
  74. package/dist/trajectory-integration.d.ts.map +1 -0
  75. package/dist/trajectory-integration.js +979 -0
  76. package/dist/trajectory-integration.js.map +1 -0
  77. package/dist/wrapper-types.d.ts +41 -0
  78. package/dist/wrapper-types.d.ts.map +1 -0
  79. package/dist/wrapper-types.js +7 -0
  80. package/dist/wrapper-types.js.map +1 -0
  81. package/package.json +63 -0
  82. package/src/__fixtures__/claude-outputs.ts +471 -0
  83. package/src/__fixtures__/codex-outputs.ts +99 -0
  84. package/src/__fixtures__/gemini-outputs.ts +151 -0
  85. package/src/__fixtures__/index.ts +47 -0
  86. package/src/auth-detection.ts +244 -0
  87. package/src/base-wrapper.test.ts +540 -0
  88. package/src/base-wrapper.ts +741 -0
  89. package/src/client.test.ts +262 -0
  90. package/src/client.ts +984 -0
  91. package/src/id-generator.test.ts +71 -0
  92. package/src/id-generator.ts +69 -0
  93. package/src/idle-detector.test.ts +390 -0
  94. package/src/idle-detector.ts +370 -0
  95. package/src/inbox.test.ts +233 -0
  96. package/src/inbox.ts +89 -0
  97. package/src/index.ts +170 -0
  98. package/src/parser.regression.test.ts +251 -0
  99. package/src/parser.test.ts +1359 -0
  100. package/src/parser.ts +1477 -0
  101. package/src/prompt-composer.test.ts +219 -0
  102. package/src/prompt-composer.ts +231 -0
  103. package/src/relay-pty-orchestrator.test.ts +1027 -0
  104. package/src/relay-pty-orchestrator.ts +2270 -0
  105. package/src/shared.test.ts +221 -0
  106. package/src/shared.ts +454 -0
  107. package/src/stuck-detector.test.ts +303 -0
  108. package/src/stuck-detector.ts +511 -0
  109. package/src/tmux-resolver.test.ts +104 -0
  110. package/src/tmux-resolver.ts +207 -0
  111. package/src/tmux-wrapper.test.ts +316 -0
  112. package/src/tmux-wrapper.ts +2010 -0
  113. package/src/trajectory-detection.test.ts +151 -0
  114. package/src/trajectory-integration.ts +1261 -0
  115. package/src/wrapper-types.ts +45 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Codex CLI output fixtures for parser regression testing.
3
+ *
4
+ * Codex (OpenAI) has its own output formatting patterns.
5
+ */
6
+
7
+ import type { OutputFixture } from './claude-outputs.js';
8
+
9
+ export const codexOutputFixtures: OutputFixture[] = [
10
+ // =====================================================================
11
+ // Basic Codex output
12
+ // =====================================================================
13
+ {
14
+ name: 'codex-basic-relay',
15
+ description: 'Basic relay command from Codex',
16
+ input: `I'll help you with that task.
17
+
18
+ ->relay:Lead Task analysis complete. Ready for review.
19
+
20
+ Let me know if you need anything else.
21
+ `,
22
+ expectedCommands: [
23
+ { to: 'Lead', body: 'Task analysis complete. Ready for review.' },
24
+ ],
25
+ expectedOutputContains: ['help you with that task', 'Let me know'],
26
+ },
27
+ {
28
+ name: 'codex-with-code-output',
29
+ description: 'Codex output with code blocks',
30
+ input: `Here's the fix:
31
+
32
+ \`\`\`python
33
+ def authenticate(user, password):
34
+ # ->relay:Agent This is in code, ignore
35
+ return check_credentials(user, password)
36
+ \`\`\`
37
+
38
+ ->relay:Lead Code fix implemented. Please review.
39
+ `,
40
+ expectedCommands: [
41
+ { to: 'Lead', body: 'Code fix implemented. Please review.' },
42
+ ],
43
+ expectedOutputContains: ['->relay:Agent This is in code'],
44
+ },
45
+
46
+ // =====================================================================
47
+ // Codex multi-line messages
48
+ // =====================================================================
49
+ {
50
+ name: 'codex-fenced-message',
51
+ description: 'Codex sending fenced multi-line message',
52
+ input: `->relay:Lead <<<
53
+ Code review complete. Issues found:
54
+
55
+ 1. Missing error handling in auth.py:45
56
+ 2. SQL injection risk in query.py:120
57
+ 3. Unused import in utils.py:3
58
+
59
+ Priority: Fix items 1 and 2 immediately.
60
+ >>>
61
+
62
+ I've documented all issues above.
63
+ `,
64
+ expectedCommands: [
65
+ {
66
+ to: 'Lead',
67
+ body: `Code review complete. Issues found:
68
+
69
+ 1. Missing error handling in auth.py:45
70
+ 2. SQL injection risk in query.py:120
71
+ 3. Unused import in utils.py:3
72
+
73
+ Priority: Fix items 1 and 2 immediately.`,
74
+ },
75
+ ],
76
+ },
77
+
78
+ // =====================================================================
79
+ // Codex with tool use
80
+ // =====================================================================
81
+ {
82
+ name: 'codex-after-tool-use',
83
+ description: 'Relay command after Codex tool execution',
84
+ input: `Running: git status
85
+
86
+ On branch main
87
+ Changes not staged for commit:
88
+ modified: src/auth.ts
89
+
90
+ ->relay:Lead Git status shows 1 modified file: src/auth.ts
91
+ `,
92
+ expectedCommands: [
93
+ { to: 'Lead', body: 'Git status shows 1 modified file: src/auth.ts' },
94
+ ],
95
+ expectedOutputContains: ['Running: git status', 'On branch main'],
96
+ },
97
+ ];
98
+
99
+ export default codexOutputFixtures;
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Gemini CLI output fixtures for parser regression testing.
3
+ *
4
+ * Gemini has unique characteristics:
5
+ * - Uses sparkle character (✦) as output prefix
6
+ * - Can drop into shell mode ($ prompt)
7
+ * - Has specific keyword interpretation issues
8
+ */
9
+
10
+ import type { OutputFixture } from './claude-outputs.js';
11
+
12
+ /**
13
+ * Gemini-specific characters and patterns
14
+ */
15
+ export const GEMINI = {
16
+ SPARKLE: '✦',
17
+ SHELL_PROMPT: '$',
18
+ };
19
+
20
+ export const geminiOutputFixtures: OutputFixture[] = [
21
+ // =====================================================================
22
+ // Sparkle prefix handling
23
+ // =====================================================================
24
+ {
25
+ name: 'gemini-sparkle-prefix',
26
+ description: 'Relay command with Gemini sparkle prefix',
27
+ input: `${GEMINI.SPARKLE} ->relay:Lead STATUS: Ready for task
28
+ `,
29
+ expectedCommands: [
30
+ { to: 'Lead', body: 'STATUS: Ready for task' },
31
+ ],
32
+ },
33
+ {
34
+ name: 'gemini-sparkle-with-space',
35
+ description: 'Sparkle with extra spacing',
36
+ input: `${GEMINI.SPARKLE} ->relay:Lead Message with extra space
37
+ `,
38
+ expectedCommands: [
39
+ { to: 'Lead', body: 'Message with extra space' },
40
+ ],
41
+ },
42
+ {
43
+ name: 'gemini-multiple-sparkle-lines',
44
+ description: 'Multiple lines with sparkle prefix',
45
+ input: `${GEMINI.SPARKLE} Processing your request...
46
+ ${GEMINI.SPARKLE} ->relay:Lead Task complete
47
+ ${GEMINI.SPARKLE} Ready for next task.
48
+ `,
49
+ expectedCommands: [
50
+ { to: 'Lead', body: 'Task complete' },
51
+ ],
52
+ expectedOutputContains: ['Processing your request', 'Ready for next task'],
53
+ },
54
+
55
+ // =====================================================================
56
+ // Shell mode detection
57
+ // =====================================================================
58
+ {
59
+ name: 'gemini-shell-mode-output',
60
+ description: 'Output that includes shell prompt',
61
+ input: `${GEMINI.SPARKLE} Let me run that command for you.
62
+ $ ls -la
63
+ total 48
64
+ drwxr-xr-x 5 user user 4096 Jan 23 10:00 .
65
+ ${GEMINI.SPARKLE} ->relay:Lead Command executed successfully
66
+ `,
67
+ expectedCommands: [
68
+ { to: 'Lead', body: 'Command executed successfully' },
69
+ ],
70
+ expectedOutputContains: ['$ ls -la', 'total 48'],
71
+ },
72
+
73
+ // =====================================================================
74
+ // Gemini-specific edge cases
75
+ // =====================================================================
76
+ {
77
+ name: 'gemini-fenced-with-sparkle',
78
+ description: 'Fenced message with sparkle prefix',
79
+ input: `${GEMINI.SPARKLE} ->relay:Lead <<<
80
+ Here's my detailed analysis:
81
+
82
+ The issue is in the authentication flow.
83
+ Consider these changes:
84
+ 1. Update token validation
85
+ 2. Add refresh logic
86
+ >>>
87
+ `,
88
+ expectedCommands: [
89
+ {
90
+ to: 'Lead',
91
+ body: `Here's my detailed analysis:
92
+
93
+ The issue is in the authentication flow.
94
+ Consider these changes:
95
+ 1. Update token validation
96
+ 2. Add refresh logic`,
97
+ },
98
+ ],
99
+ },
100
+ {
101
+ name: 'gemini-mixed-output',
102
+ description: 'Mix of sparkle and non-sparkle output',
103
+ input: `Processing...
104
+ ${GEMINI.SPARKLE} Analyzing the codebase
105
+ ${GEMINI.SPARKLE} Found 3 issues
106
+ ->relay:Lead Analysis complete with 3 issues found
107
+ ${GEMINI.SPARKLE} Done!
108
+ `,
109
+ expectedCommands: [
110
+ { to: 'Lead', body: 'Analysis complete with 3 issues found' },
111
+ ],
112
+ },
113
+
114
+ // =====================================================================
115
+ // Complex Gemini scenarios
116
+ // =====================================================================
117
+ {
118
+ name: 'gemini-code-execution-output',
119
+ description: 'Gemini executing code with relay after',
120
+ input: `${GEMINI.SPARKLE} I'll run the tests for you.
121
+
122
+ $ npm test
123
+
124
+ > project@1.0.0 test
125
+ > vitest run
126
+
127
+ ✓ src/auth.test.ts (5 tests)
128
+ ✓ src/api.test.ts (12 tests)
129
+
130
+ Test Files 2 passed
131
+ Tests 17 passed
132
+
133
+ ${GEMINI.SPARKLE} ->relay:Lead Tests passed: 17/17. Build is green.
134
+ `,
135
+ expectedCommands: [
136
+ { to: 'Lead', body: 'Tests passed: 17/17. Build is green.' },
137
+ ],
138
+ expectedOutputContains: ['npm test', '17 passed'],
139
+ },
140
+ {
141
+ name: 'gemini-broadcast',
142
+ description: 'Gemini sending broadcast message',
143
+ input: `${GEMINI.SPARKLE} ->relay:* All agents: deployment starting in 5 minutes
144
+ `,
145
+ expectedCommands: [
146
+ { to: '*', body: 'All agents: deployment starting in 5 minutes' },
147
+ ],
148
+ },
149
+ ];
150
+
151
+ export default geminiOutputFixtures;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Parser regression test fixtures
3
+ *
4
+ * This module exports CLI output fixtures for testing the parser
5
+ * against real-world terminal output patterns.
6
+ *
7
+ * To add new fixtures:
8
+ * 1. Capture the problematic output from a real CLI session
9
+ * 2. Add it to the appropriate CLI-specific fixture file
10
+ * 3. Run tests to ensure parser handles it correctly
11
+ *
12
+ * When a parser bug is fixed, add a regression test fixture
13
+ * to prevent the bug from recurring.
14
+ */
15
+
16
+ export { claudeOutputFixtures, type OutputFixture, ANSI } from './claude-outputs.js';
17
+ export { geminiOutputFixtures, GEMINI } from './gemini-outputs.js';
18
+ export { codexOutputFixtures } from './codex-outputs.js';
19
+
20
+ import { claudeOutputFixtures } from './claude-outputs.js';
21
+ import { geminiOutputFixtures } from './gemini-outputs.js';
22
+ import { codexOutputFixtures } from './codex-outputs.js';
23
+
24
+ /**
25
+ * All fixtures combined for comprehensive testing
26
+ */
27
+ export const allFixtures = [
28
+ ...claudeOutputFixtures.map(f => ({ ...f, cli: 'claude' as const })),
29
+ ...geminiOutputFixtures.map(f => ({ ...f, cli: 'gemini' as const })),
30
+ ...codexOutputFixtures.map(f => ({ ...f, cli: 'codex' as const })),
31
+ ];
32
+
33
+ /**
34
+ * Get fixtures by CLI type
35
+ */
36
+ export function getFixturesByCli(cli: 'claude' | 'gemini' | 'codex') {
37
+ switch (cli) {
38
+ case 'claude':
39
+ return claudeOutputFixtures;
40
+ case 'gemini':
41
+ return geminiOutputFixtures;
42
+ case 'codex':
43
+ return codexOutputFixtures;
44
+ default:
45
+ return [];
46
+ }
47
+ }
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Auth Revocation Detection
3
+ *
4
+ * Detects when an AI CLI's authentication has been revoked.
5
+ * This can happen when:
6
+ * 1. User authenticates the same provider elsewhere (limited sessions)
7
+ * 2. Token expires or is invalidated
8
+ * 3. OAuth refresh fails
9
+ */
10
+
11
+ /**
12
+ * Patterns that indicate authentication has been revoked or expired.
13
+ * These are typically output by Claude CLI, Codex, etc. when auth fails.
14
+ */
15
+ export const AUTH_REVOCATION_PATTERNS: RegExp[] = [
16
+ // Session/token expiration
17
+ /session\s+(has\s+)?expired/i,
18
+ /token\s+(has\s+)?expired/i,
19
+ /credentials?\s+(have\s+)?expired/i,
20
+
21
+ // Login required
22
+ /please\s+log\s*in\s+again/i,
23
+ /login\s+required/i,
24
+ /authentication\s+required/i,
25
+ /must\s+(be\s+)?log(ged)?\s*in/i,
26
+ /you\s+need\s+to\s+log\s*in/i,
27
+
28
+ // Unauthorized
29
+ /\bunauthorized\b/i,
30
+ /not\s+authorized/i,
31
+ /access\s+denied/i,
32
+
33
+ // Invalid credentials
34
+ /invalid\s+credentials?/i,
35
+ /invalid\s+token/i,
36
+ /invalid\s+session/i,
37
+ /not\s+authenticated/i,
38
+
39
+ // OAuth specific
40
+ /oauth\s+error.*401/i,
41
+ /oauth\s+error.*403/i,
42
+ /oauth\s+token\s+(has\s+)?expired/i,
43
+ /refresh\s+token\s+(is\s+)?invalid/i,
44
+ /failed\s+to\s+refresh/i,
45
+ /please\s+obtain\s+a\s+new\s+token/i,
46
+
47
+ // API errors that indicate auth issues
48
+ /api\s+error.*401/i,
49
+ /api\s+error.*403/i,
50
+ /http\s+401/i,
51
+ /http\s+403/i,
52
+
53
+ // Claude-specific patterns
54
+ /your\s+api\s+key\s+is\s+invalid/i,
55
+ /api\s+key\s+not\s+found/i,
56
+ /signed\s+out/i,
57
+ /session\s+revoked/i,
58
+ ];
59
+
60
+ /**
61
+ * Patterns that should NOT trigger auth revocation detection.
62
+ * These are false positives that might match auth patterns but aren't actual auth errors.
63
+ */
64
+ export const AUTH_FALSE_POSITIVE_PATTERNS: RegExp[] = [
65
+ // Documentation or help text
66
+ /how\s+to\s+log\s*in/i,
67
+ /login\s+instructions/i,
68
+ /authentication\s+guide/i,
69
+
70
+ // Code comments or strings
71
+ /\/\/.*unauthorized/i,
72
+ /\/\*.*unauthorized.*\*\//i,
73
+ /".*unauthorized.*"/i,
74
+ /'.*unauthorized.*'/i,
75
+
76
+ // Error handling code
77
+ /catch.*unauthorized/i,
78
+ /handle.*auth.*error/i,
79
+ /if.*session.*expired/i,
80
+
81
+ // Instructional content
82
+ /you\s+should\s+log\s*in/i,
83
+ /make\s+sure\s+you('re)?\s+logged\s*in/i,
84
+ ];
85
+
86
+ export interface AuthRevocationResult {
87
+ detected: boolean;
88
+ pattern?: string;
89
+ confidence: 'high' | 'medium' | 'low';
90
+ message?: string;
91
+ }
92
+
93
+ /**
94
+ * Detect if output indicates authentication has been revoked.
95
+ *
96
+ * @param output - The CLI output to analyze
97
+ * @param recentOutputOnly - If true, only check the last ~500 chars (for real-time detection)
98
+ * @returns Detection result with confidence level
99
+ */
100
+ export function detectAuthRevocation(
101
+ output: string,
102
+ recentOutputOnly = false
103
+ ): AuthRevocationResult {
104
+ // If checking recent output only, truncate to last 500 chars
105
+ const textToCheck = recentOutputOnly ? output.slice(-500) : output;
106
+
107
+ // First check for false positives
108
+ for (const falsePositive of AUTH_FALSE_POSITIVE_PATTERNS) {
109
+ if (falsePositive.test(textToCheck)) {
110
+ return { detected: false, confidence: 'low' };
111
+ }
112
+ }
113
+
114
+ // Check each auth revocation pattern
115
+ for (const pattern of AUTH_REVOCATION_PATTERNS) {
116
+ const match = textToCheck.match(pattern);
117
+ if (match) {
118
+ // Determine confidence based on pattern specificity
119
+ const confidence = getConfidenceLevel(pattern, match[0]);
120
+
121
+ return {
122
+ detected: true,
123
+ pattern: pattern.source,
124
+ confidence,
125
+ message: match[0],
126
+ };
127
+ }
128
+ }
129
+
130
+ return { detected: false, confidence: 'low' };
131
+ }
132
+
133
+ /**
134
+ * Determine confidence level based on the matched pattern.
135
+ */
136
+ function getConfidenceLevel(
137
+ pattern: RegExp,
138
+ _matchedText: string
139
+ ): 'high' | 'medium' | 'low' {
140
+ const patternStr = pattern.source.toLowerCase();
141
+
142
+ // High confidence: Explicit auth failure messages
143
+ if (
144
+ patternStr.includes('session') && patternStr.includes('expired') ||
145
+ patternStr.includes('please') && patternStr.includes('log') ||
146
+ patternStr.includes('authentication required') ||
147
+ patternStr.includes('token') && patternStr.includes('expired') ||
148
+ patternStr.includes('oauth') && patternStr.includes('expired') ||
149
+ patternStr.includes('authentication_error') ||
150
+ patternStr.includes('signed out') ||
151
+ patternStr.includes('session revoked') ||
152
+ patternStr.includes('obtain') && patternStr.includes('token')
153
+ ) {
154
+ return 'high';
155
+ }
156
+
157
+ // Medium confidence: General auth errors
158
+ if (
159
+ patternStr.includes('unauthorized') ||
160
+ patternStr.includes('401') ||
161
+ patternStr.includes('403') ||
162
+ patternStr.includes('invalid') && patternStr.includes('credentials')
163
+ ) {
164
+ return 'medium';
165
+ }
166
+
167
+ // Low confidence: Could be related to other errors
168
+ return 'low';
169
+ }
170
+
171
+ /**
172
+ * Check if the given text looks like an auth-related CLI prompt
173
+ * that's waiting for user action (not an error, but a request to auth).
174
+ */
175
+ export function isAuthPrompt(text: string): boolean {
176
+ const authPromptPatterns = [
177
+ /open\s+this\s+url/i,
178
+ /visit\s+.*to\s+authorize/i,
179
+ /enter\s+your\s+api\s+key/i,
180
+ /paste\s+your\s+token/i,
181
+ /waiting\s+for\s+authorization/i,
182
+ /complete\s+login\s+in\s+browser/i,
183
+ ];
184
+
185
+ return authPromptPatterns.some(pattern => pattern.test(text));
186
+ }
187
+
188
+ /**
189
+ * Provider-specific auth detection configuration.
190
+ * Different AI CLIs may have different error messages.
191
+ */
192
+ export const PROVIDER_AUTH_PATTERNS: Record<string, RegExp[]> = {
193
+ claude: [
194
+ /claude.*session.*expired/i,
195
+ /anthropic.*unauthorized/i,
196
+ /claude.*not\s+authenticated/i,
197
+ /please\s+run\s+claude\s+login/i,
198
+ /please\s+run\s+\/login/i,
199
+ /authentication_error/i,
200
+ ],
201
+ codex: [
202
+ /codex.*session.*expired/i,
203
+ /openai.*unauthorized/i,
204
+ /codex.*not\s+authenticated/i,
205
+ ],
206
+ gemini: [
207
+ /gemini.*session.*expired/i,
208
+ /google.*unauthorized/i,
209
+ /gemini.*not\s+authenticated/i,
210
+ ],
211
+ };
212
+
213
+ /**
214
+ * Detect auth revocation for a specific provider.
215
+ * Uses provider-specific patterns in addition to general patterns.
216
+ */
217
+ export function detectProviderAuthRevocation(
218
+ output: string,
219
+ provider: string
220
+ ): AuthRevocationResult {
221
+ // First check general patterns
222
+ const generalResult = detectAuthRevocation(output, true);
223
+ if (generalResult.detected && generalResult.confidence === 'high') {
224
+ return generalResult;
225
+ }
226
+
227
+ // Check provider-specific patterns
228
+ const providerPatterns = PROVIDER_AUTH_PATTERNS[provider.toLowerCase()];
229
+ if (providerPatterns) {
230
+ for (const pattern of providerPatterns) {
231
+ const match = output.match(pattern);
232
+ if (match) {
233
+ return {
234
+ detected: true,
235
+ pattern: pattern.source,
236
+ confidence: 'high',
237
+ message: match[0],
238
+ };
239
+ }
240
+ }
241
+ }
242
+
243
+ return generalResult;
244
+ }