@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
package/src/index.ts ADDED
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @agent-relay/wrapper
3
+ *
4
+ * CLI agent wrappers for Agent Relay.
5
+ * Phase 2B extraction - utilities and types.
6
+ */
7
+
8
+ // ID generation
9
+ export { IdGenerator, idGen, generateId } from './id-generator.js';
10
+
11
+ // Tmux binary resolution
12
+ export {
13
+ getTmuxPath,
14
+ resolveTmux,
15
+ isTmuxAvailable,
16
+ checkTmuxVersion,
17
+ getBundledTmuxDir,
18
+ getBundledTmuxPath,
19
+ getPlatformIdentifier,
20
+ TmuxNotFoundError,
21
+ type TmuxInfo,
22
+ BUNDLED_TMUX_DIR,
23
+ BUNDLED_TMUX_PATH,
24
+ MIN_TMUX_VERSION,
25
+ } from './tmux-resolver.js';
26
+
27
+ // Output parser
28
+ export {
29
+ OutputParser,
30
+ parseSummaryFromOutput,
31
+ parseSummaryWithDetails,
32
+ parseSessionEndFromOutput,
33
+ parseRelayMetadataFromOutput,
34
+ isPlaceholderTarget,
35
+ type ParsedCommand,
36
+ type ParserOptions,
37
+ type ParsedSummary,
38
+ type ParsedMessageMetadata,
39
+ type MetadataParseResult,
40
+ type SummaryParseResult,
41
+ type SessionEndMarker,
42
+ } from './parser.js';
43
+
44
+ // Shared wrapper utilities and types
45
+ export {
46
+ stripAnsi,
47
+ sleep,
48
+ getDefaultRelayPrefix,
49
+ buildInjectionString,
50
+ injectWithRetry,
51
+ INJECTION_CONSTANTS,
52
+ CLI_QUIRKS,
53
+ type QueuedMessage,
54
+ type InjectionResult,
55
+ type InjectionMetrics,
56
+ type CliType,
57
+ type InjectionCallbacks,
58
+ } from './shared.js';
59
+
60
+ // Auth revocation detection
61
+ export {
62
+ AUTH_REVOCATION_PATTERNS,
63
+ AUTH_FALSE_POSITIVE_PATTERNS,
64
+ PROVIDER_AUTH_PATTERNS,
65
+ detectProviderAuthRevocation,
66
+ type AuthRevocationResult,
67
+ } from './auth-detection.js';
68
+
69
+ // Idle detection
70
+ export {
71
+ UniversalIdleDetector,
72
+ getTmuxPanePid,
73
+ type IdleSignal,
74
+ type IdleResult,
75
+ type IdleDetectorConfig,
76
+ } from './idle-detector.js';
77
+
78
+ // Trajectory integration (PDERO paradigm tracking)
79
+ export {
80
+ TrajectoryIntegration,
81
+ getTrajectoryIntegration,
82
+ detectPhaseFromContent,
83
+ detectToolCalls,
84
+ detectErrors,
85
+ getCompactTrailInstructions,
86
+ getTrailEnvVars,
87
+ getTrailInstructions,
88
+ isTrailAvailable,
89
+ startTrajectory,
90
+ getTrajectoryStatus,
91
+ transitionPhase,
92
+ recordDecision,
93
+ recordEvent,
94
+ recordMessage,
95
+ completeTrajectory,
96
+ abandonTrajectory,
97
+ listTrajectorySteps,
98
+ getTrajectoryHistory,
99
+ type PDEROPhase,
100
+ type StartTrajectoryOptions,
101
+ type CompleteTrajectoryOptions,
102
+ type DecisionOptions,
103
+ type TrajectoryStepData,
104
+ type TrajectoryHistoryEntry,
105
+ type DetectedToolCall,
106
+ type DetectedError,
107
+ } from './trajectory-integration.js';
108
+
109
+ // Wrapper event types
110
+ export {
111
+ type InjectionFailedEvent,
112
+ type SummaryEvent,
113
+ type SessionEndEvent,
114
+ type AuthRevokedEvent,
115
+ } from './wrapper-types.js';
116
+
117
+ // Stuck detection
118
+ export {
119
+ StuckDetector,
120
+ type StuckEvent,
121
+ type StuckReason,
122
+ type StuckDetectorConfig,
123
+ } from './stuck-detector.js';
124
+
125
+ // Prompt composition
126
+ export {
127
+ composeForAgent,
128
+ getAvailableRoles,
129
+ parseRoleFromProfile,
130
+ clearPromptCache,
131
+ type AgentRole,
132
+ type AgentProfile,
133
+ type ComposedPrompt,
134
+ } from './prompt-composer.js';
135
+
136
+ /**
137
+ * Relay client (internal use only)
138
+ *
139
+ * @deprecated **MIGRATION REQUIRED** - Use `@agent-relay/sdk` instead.
140
+ *
141
+ * ```typescript
142
+ * // BEFORE (deprecated)
143
+ * import { RelayClient } from '@agent-relay/wrapper';
144
+ *
145
+ * // AFTER (recommended)
146
+ * import { RelayClient } from '@agent-relay/sdk';
147
+ * ```
148
+ *
149
+ * This export is retained only for internal daemon/wrapper integration.
150
+ * External consumers should migrate to `@agent-relay/sdk`.
151
+ * This export will be removed in a future major version.
152
+ */
153
+ export {
154
+ RelayClient,
155
+ type ClientState,
156
+ type ClientConfig,
157
+ type SyncOptions,
158
+ } from './client.js';
159
+
160
+ // Base wrapper class
161
+ export {
162
+ BaseWrapper,
163
+ type BaseWrapperConfig,
164
+ } from './base-wrapper.js';
165
+
166
+ // RelayPtyOrchestrator (relay-pty Rust binary)
167
+ export {
168
+ RelayPtyOrchestrator,
169
+ type RelayPtyOrchestratorConfig,
170
+ } from './relay-pty-orchestrator.js';
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Parser Regression Tests
3
+ *
4
+ * These tests use real-world CLI output fixtures to ensure the parser
5
+ * correctly handles terminal output from different AI CLIs.
6
+ *
7
+ * Purpose:
8
+ * - Prevent regressions when modifying parser code
9
+ * - Document expected behavior for edge cases
10
+ * - Catch breaking changes from CLI format updates
11
+ *
12
+ * When adding new fixtures:
13
+ * 1. Capture the problematic output from a real session
14
+ * 2. Add to the appropriate fixture file in __fixtures__/
15
+ * 3. Run this test to verify correct parsing
16
+ *
17
+ * When a bug is fixed:
18
+ * 1. Add a fixture that reproduces the bug
19
+ * 2. Verify the test fails before the fix
20
+ * 3. Apply the fix and verify the test passes
21
+ */
22
+
23
+ import { describe, it, expect, beforeEach } from 'vitest';
24
+ import { OutputParser } from './parser.js';
25
+ import {
26
+ claudeOutputFixtures,
27
+ geminiOutputFixtures,
28
+ codexOutputFixtures,
29
+ allFixtures,
30
+ type OutputFixture,
31
+ } from './__fixtures__/index.js';
32
+
33
+ /**
34
+ * Helper to run a fixture through the parser and verify results
35
+ */
36
+ function runFixture(parser: OutputParser, fixture: OutputFixture) {
37
+ const result = parser.parse(fixture.input);
38
+
39
+ // Verify expected commands
40
+ expect(result.commands.length).toBe(
41
+ fixture.expectedCommands.length,
42
+ `Fixture "${fixture.name}": expected ${fixture.expectedCommands.length} commands, got ${result.commands.length}`
43
+ );
44
+
45
+ fixture.expectedCommands.forEach((expected, i) => {
46
+ const actual = result.commands[i];
47
+ expect(actual.to).toBe(expected.to, `Fixture "${fixture.name}" command ${i}: wrong target`);
48
+ expect(actual.body).toBe(expected.body, `Fixture "${fixture.name}" command ${i}: wrong body`);
49
+
50
+ if (expected.kind) {
51
+ expect(actual.kind).toBe(expected.kind, `Fixture "${fixture.name}" command ${i}: wrong kind`);
52
+ }
53
+
54
+ if (expected.thread) {
55
+ expect(actual.thread).toBe(expected.thread, `Fixture "${fixture.name}" command ${i}: wrong thread`);
56
+ }
57
+
58
+ if (expected.project) {
59
+ expect(actual.project).toBe(expected.project, `Fixture "${fixture.name}" command ${i}: wrong project`);
60
+ }
61
+ });
62
+
63
+ // Verify expected output contains
64
+ if (fixture.expectedOutputContains) {
65
+ fixture.expectedOutputContains.forEach((text) => {
66
+ expect(result.output).toContain(
67
+ text,
68
+ `Fixture "${fixture.name}": output should contain "${text}"`
69
+ );
70
+ });
71
+ }
72
+
73
+ // Verify expected output does not contain
74
+ if (fixture.expectedOutputNotContains) {
75
+ fixture.expectedOutputNotContains.forEach((text) => {
76
+ expect(result.output).not.toContain(
77
+ text,
78
+ `Fixture "${fixture.name}": output should NOT contain "${text}"`
79
+ );
80
+ });
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ describe('Parser Regression Tests', () => {
87
+ let parser: OutputParser;
88
+
89
+ beforeEach(() => {
90
+ parser = new OutputParser();
91
+ });
92
+
93
+ describe('Claude CLI outputs', () => {
94
+ claudeOutputFixtures.forEach((fixture) => {
95
+ it(`[${fixture.name}] ${fixture.description}`, () => {
96
+ runFixture(parser, fixture);
97
+ });
98
+ });
99
+ });
100
+
101
+ describe('Gemini CLI outputs', () => {
102
+ geminiOutputFixtures.forEach((fixture) => {
103
+ it(`[${fixture.name}] ${fixture.description}`, () => {
104
+ runFixture(parser, fixture);
105
+ });
106
+ });
107
+ });
108
+
109
+ describe('Codex CLI outputs', () => {
110
+ codexOutputFixtures.forEach((fixture) => {
111
+ it(`[${fixture.name}] ${fixture.description}`, () => {
112
+ runFixture(parser, fixture);
113
+ });
114
+ });
115
+ });
116
+
117
+ describe('Cross-CLI consistency', () => {
118
+ it('handles basic relay command consistently across simulated CLI outputs', () => {
119
+ const basicMessage = '->relay:Lead Hello\n';
120
+
121
+ // Reset parser between tests
122
+ const results = [
123
+ new OutputParser().parse(basicMessage),
124
+ new OutputParser().parse(basicMessage),
125
+ new OutputParser().parse(basicMessage),
126
+ ];
127
+
128
+ results.forEach((result, i) => {
129
+ expect(result.commands.length).toBe(1, `Iteration ${i}: should parse one command`);
130
+ expect(result.commands[0].to).toBe('Lead');
131
+ expect(result.commands[0].body).toBe('Hello');
132
+ });
133
+ });
134
+
135
+ it('maintains parser state correctly across multiple chunks', () => {
136
+ // Simulate streaming output
137
+ const chunks = [
138
+ '->relay:Lead <<<\n',
139
+ 'First line\n',
140
+ 'Second line\n',
141
+ '>>>\n',
142
+ ];
143
+
144
+ let totalCommands = 0;
145
+ for (const chunk of chunks) {
146
+ const result = parser.parse(chunk);
147
+ totalCommands += result.commands.length;
148
+ }
149
+
150
+ expect(totalCommands).toBe(1);
151
+ });
152
+ });
153
+
154
+ describe('Regression scenarios', () => {
155
+ it('handles ANSI codes without breaking command detection', () => {
156
+ // Regression: ANSI codes could interfere with start-of-line detection
157
+ const input = '\x1b[0m\x1b[1m->relay:Lead Message\x1b[0m\n';
158
+ const result = parser.parse(input);
159
+
160
+ expect(result.commands.length).toBe(1);
161
+ expect(result.commands[0].to).toBe('Lead');
162
+ });
163
+
164
+ it('does not strip valid text that looks like orphaned CSI', () => {
165
+ // Regression: [Agent Relay] was being stripped as orphaned CSI
166
+ const input = '[Agent Relay] System message\n->relay:Lead ACK\n';
167
+ const result = parser.parse(input);
168
+
169
+ expect(result.output).toContain('[Agent Relay]');
170
+ expect(result.commands.length).toBe(1);
171
+ });
172
+
173
+ it('handles fence end at end of line correctly', () => {
174
+ // Regression: >>> at end of content line wasn't detected
175
+ const input = '->relay:Lead <<<\nMessage content>>>\n';
176
+ const result = parser.parse(input);
177
+
178
+ expect(result.commands.length).toBe(1);
179
+ expect(result.commands[0].body).toBe('Message content');
180
+ });
181
+
182
+ it('auto-closes fenced block when new relay starts', () => {
183
+ // Regression: incomplete fenced block would discard content
184
+ const input = '->relay:Alice <<<\nImportant\n->relay:Bob Hi\n';
185
+ const result = parser.parse(input);
186
+
187
+ expect(result.commands.length).toBe(2);
188
+ expect(result.commands[0].to).toBe('Alice');
189
+ expect(result.commands[0].body).toBe('Important');
190
+ expect(result.commands[1].to).toBe('Bob');
191
+ });
192
+
193
+ it('filters instructional text in thinking blocks correctly', () => {
194
+ // Regression: relay commands in thinking blocks were being parsed
195
+ // Thinking block content is now stripped from output entirely
196
+ const input = `<thinking>
197
+ ->relay:Test This should be ignored
198
+ </thinking>
199
+ ->relay:Lead Real message
200
+ `;
201
+ const result = parser.parse(input);
202
+
203
+ expect(result.commands.length).toBe(1);
204
+ expect(result.commands[0].to).toBe('Lead');
205
+ // Thinking content is stripped from output - verify it's NOT there
206
+ expect(result.output).not.toContain('->relay:Test');
207
+ });
208
+
209
+ it('handles spawn/release commands as passthrough', () => {
210
+ // Regression: spawn commands were parsed as messages to target "spawn"
211
+ const input = '->relay:spawn Worker claude\n->relay:Lead Done\n';
212
+ const result = parser.parse(input);
213
+
214
+ expect(result.commands.length).toBe(1);
215
+ expect(result.commands[0].to).toBe('Lead');
216
+ expect(result.output).toContain('->relay:spawn');
217
+ });
218
+
219
+ it('handles cursor movement codes gracefully', () => {
220
+ // Regression: cursor codes could break line detection
221
+ const input = '\x1b[1A\x1b[2K->relay:Lead After cursor move\n';
222
+ const result = parser.parse(input);
223
+
224
+ expect(result.commands.length).toBe(1);
225
+ expect(result.commands[0].to).toBe('Lead');
226
+ });
227
+ });
228
+
229
+ describe('Fixture coverage summary', () => {
230
+ it('has comprehensive fixture coverage', () => {
231
+ // This test documents the fixture coverage
232
+ const totalFixtures = allFixtures.length;
233
+ const claudeCount = claudeOutputFixtures.length;
234
+ const geminiCount = geminiOutputFixtures.length;
235
+ const codexCount = codexOutputFixtures.length;
236
+
237
+ console.log(`
238
+ Parser Regression Test Coverage:
239
+ - Total fixtures: ${totalFixtures}
240
+ - Claude fixtures: ${claudeCount}
241
+ - Gemini fixtures: ${geminiCount}
242
+ - Codex fixtures: ${codexCount}
243
+ `);
244
+
245
+ // Ensure we have reasonable coverage
246
+ expect(claudeCount).toBeGreaterThanOrEqual(15, 'Should have at least 15 Claude fixtures');
247
+ expect(geminiCount).toBeGreaterThanOrEqual(5, 'Should have at least 5 Gemini fixtures');
248
+ expect(codexCount).toBeGreaterThanOrEqual(3, 'Should have at least 3 Codex fixtures');
249
+ });
250
+ });
251
+ });