chat-headers.test.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import { beforeEach, describe, expect, mock, test } from 'bun:test';
  2. import type { PluginInput } from '@opencode-ai/plugin';
  3. import { createInternalAgentTextPart } from '../utils';
  4. import {
  5. __resetInternalMarkerCacheForTesting,
  6. createChatHeadersHook,
  7. } from './chat-headers';
  8. function createMockContext(parts: unknown[] = []) {
  9. return {
  10. client: {
  11. session: {
  12. message: mock(async () => ({
  13. data: {
  14. info: { role: 'user' },
  15. parts,
  16. },
  17. })),
  18. },
  19. },
  20. } as unknown as PluginInput;
  21. }
  22. function createInput(
  23. overrides?: Partial<{
  24. providerID: string;
  25. npm: string;
  26. messageID: string;
  27. }>,
  28. ) {
  29. return {
  30. sessionID: 'session-1',
  31. agent: 'orchestrator',
  32. model: {
  33. id: 'github-copilot/claude',
  34. providerID: overrides?.providerID ?? 'github-copilot',
  35. api: {
  36. id: 'copilot',
  37. url: 'https://example.com',
  38. npm: overrides?.npm ?? '@custom/copilot',
  39. },
  40. name: 'Claude',
  41. capabilities: {
  42. temperature: true,
  43. reasoning: true,
  44. attachment: true,
  45. toolcall: true,
  46. input: {
  47. text: true,
  48. audio: false,
  49. image: false,
  50. video: false,
  51. pdf: false,
  52. },
  53. output: {
  54. text: true,
  55. audio: false,
  56. image: false,
  57. video: false,
  58. pdf: false,
  59. },
  60. },
  61. cost: {
  62. input: 0,
  63. output: 0,
  64. cache: { read: 0, write: 0 },
  65. },
  66. limit: { context: 0, output: 0 },
  67. status: 'active' as const,
  68. options: {},
  69. headers: {},
  70. },
  71. provider: {
  72. id: overrides?.providerID ?? 'github-copilot',
  73. source: 'config' as const,
  74. info: {
  75. id: overrides?.providerID ?? 'github-copilot',
  76. } as never,
  77. options: {},
  78. },
  79. message: {
  80. id: overrides?.messageID ?? 'message-1',
  81. sessionID: 'session-1',
  82. role: 'user' as const,
  83. time: { created: Date.now() },
  84. agent: 'orchestrator',
  85. model: {
  86. providerID: 'github-copilot',
  87. modelID: 'claude',
  88. },
  89. tools: {},
  90. },
  91. };
  92. }
  93. describe('createChatHeadersHook', () => {
  94. beforeEach(() => {
  95. __resetInternalMarkerCacheForTesting();
  96. });
  97. test('sets x-initiator for marked Copilot messages', async () => {
  98. const ctx = createMockContext([
  99. createInternalAgentTextPart('internal notification'),
  100. ]);
  101. const hook = createChatHeadersHook(ctx);
  102. const output = { headers: {} };
  103. await hook['chat.headers'](createInput(), output);
  104. expect(output.headers['x-initiator']).toBe('agent');
  105. });
  106. test('skips non-Copilot providers', async () => {
  107. const ctx = createMockContext([
  108. createInternalAgentTextPart('internal notification'),
  109. ]);
  110. const hook = createChatHeadersHook(ctx);
  111. const output = { headers: {} };
  112. await hook['chat.headers'](
  113. createInput({ providerID: 'anthropic' }),
  114. output,
  115. );
  116. expect(output.headers['x-initiator']).toBeUndefined();
  117. });
  118. test('skips requests handled by @ai-sdk/github-copilot', async () => {
  119. const ctx = createMockContext([
  120. createInternalAgentTextPart('internal notification'),
  121. ]);
  122. const hook = createChatHeadersHook(ctx);
  123. const output = { headers: {} };
  124. await hook['chat.headers'](
  125. createInput({ npm: '@ai-sdk/github-copilot' }),
  126. output,
  127. );
  128. expect(output.headers['x-initiator']).toBeUndefined();
  129. });
  130. test('skips normal user messages', async () => {
  131. const ctx = createMockContext([{ type: 'text', text: 'normal prompt' }]);
  132. const hook = createChatHeadersHook(ctx);
  133. const output = { headers: {} };
  134. await hook['chat.headers'](
  135. createInput({ messageID: 'message-normal' }),
  136. output,
  137. );
  138. expect(output.headers['x-initiator']).toBeUndefined();
  139. });
  140. });