@shipfox/api-integration-github 12.5.0 → 12.6.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 (39) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +8 -0
  3. package/dist/api/client.d.ts.map +1 -1
  4. package/dist/api/client.js +8 -5
  5. package/dist/api/client.js.map +1 -1
  6. package/dist/api/github-octokit.d.ts +7 -0
  7. package/dist/api/github-octokit.d.ts.map +1 -0
  8. package/dist/api/github-octokit.js +32 -0
  9. package/dist/api/github-octokit.js.map +1 -0
  10. package/dist/api/installation-token-provider.d.ts.map +1 -1
  11. package/dist/api/installation-token-provider.js +4 -2
  12. package/dist/api/installation-token-provider.js.map +1 -1
  13. package/dist/config.d.ts +1 -0
  14. package/dist/config.d.ts.map +1 -1
  15. package/dist/config.js +8 -0
  16. package/dist/config.js.map +1 -1
  17. package/dist/core/agent-tools.d.ts.map +1 -1
  18. package/dist/core/agent-tools.js +124 -78
  19. package/dist/core/agent-tools.js.map +1 -1
  20. package/dist/metrics/instance.d.ts +1 -0
  21. package/dist/metrics/instance.d.ts.map +1 -1
  22. package/dist/metrics/instance.js +19 -0
  23. package/dist/metrics/instance.js.map +1 -1
  24. package/dist/tsconfig.test.tsbuildinfo +1 -1
  25. package/package.json +1 -1
  26. package/src/api/client.test.ts +25 -5
  27. package/src/api/client.ts +22 -5
  28. package/src/api/github-octokit.test.ts +115 -0
  29. package/src/api/github-octokit.ts +49 -0
  30. package/src/api/installation-token-provider.test.ts +44 -5
  31. package/src/api/installation-token-provider.ts +5 -2
  32. package/src/config.ts +5 -0
  33. package/src/core/agent-tools.test.ts +261 -74
  34. package/src/core/agent-tools.ts +191 -80
  35. package/src/metrics/instance.ts +25 -0
  36. package/test/env.ts +1 -0
  37. package/test/fixtures/github-installation-token.ts +8 -0
  38. package/test/index.ts +4 -0
  39. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,7 +1,7 @@
1
1
  import { Octokit } from 'octokit';
2
2
  import { mapGithubError } from '#api/client.js';
3
3
  import { createGithubInstallationTokenProvider } from '#api/installation-token-provider.js';
4
- import { normalizedGithubApiBaseUrl } from '#config.js';
4
+ import { config, normalizedGithubApiBaseUrl } from '#config.js';
5
5
  import { GithubIntegrationProviderError } from './errors.js';
6
6
  import { githubAgentToolCatalog, githubAgentToolSelectionCatalog } from './github-agent-tool-catalog.js';
7
7
  export { buildGithubAgentToolSelectionCatalog, DEFAULT_JOB_LOG_TAIL_LINES, githubAgentToolCatalog, githubAgentToolSelectionCatalog } from './github-agent-tool-catalog.js';
@@ -9,27 +9,13 @@ const GITHUB_GRAPHQL_ROUTE = 'POST /graphql';
9
9
  const GITHUB_ARTIFACT_ARCHIVE_FORMAT = 'zip';
10
10
  const GITHUB_ARTIFACT_DOWNLOAD_ROUTE = `GET /repos/{owner}/{repo}/actions/artifacts/{resource_id}/${GITHUB_ARTIFACT_ARCHIVE_FORMAT}`;
11
11
  const GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS = 30_000;
12
+ const GITHUB_APP_BOT_SUFFIX = '[bot]';
13
+ const PENDING_REVIEW_PAGE_SIZE = 100;
14
+ const PENDING_REVIEW_MAX_PAGE_REQUESTS = 5;
15
+ const PENDING_REVIEW_LOOKUP_TIMEOUT_MS = 15_000;
16
+ const PENDING_REVIEW_PAGE_TIMEOUT_MS = 5_000;
17
+ const PENDING_REVIEW_PAGE_PATTERN = /[?&]page=(\d+)/u;
12
18
  const NO_PENDING_REVIEW_MESSAGE = 'No pending pull request review found for the authenticated GitHub user.';
13
- const LATEST_PENDING_REVIEW_QUERY = `
14
- query LatestPendingPullRequestReview($owner: String!, $repo: String!, $pullNumber: Int!) {
15
- viewer {
16
- login
17
- }
18
- repository(owner: $owner, name: $repo) {
19
- pullRequest(number: $pullNumber) {
20
- reviews(last: 100, states: [PENDING]) {
21
- nodes {
22
- id
23
- author {
24
- login
25
- }
26
- createdAt
27
- }
28
- }
29
- }
30
- }
31
- }
32
- `;
33
19
  const ADD_PENDING_REVIEW_COMMENT_MUTATION = `
34
20
  mutation AddCommentToPendingReview($input: AddPullRequestReviewThreadInput!) {
35
21
  addPullRequestReviewThread(input: $input) {
@@ -63,25 +49,25 @@ export class GithubAgentToolsProvider {
63
49
  return {
64
50
  call: async (call)=>{
65
51
  const tool = input.tools.find((candidate)=>candidate.id === call.toolId);
66
- if (!tool) return githubToolError(`Unknown GitHub tool: ${call.toolId}`);
52
+ if (!tool) return githubToolError(`Unknown GitHub tool: ${call.toolId}`, 'invalid-request');
67
53
  const operation = resolveGithubOperation(tool, call);
68
- if (operation === undefined) return githubToolError('Unknown GitHub tool operation');
54
+ if (operation === undefined) return githubToolError('Unknown GitHub tool operation', 'invalid-request');
69
55
  const validationError = validateGithubToolArguments(tool, call.arguments);
70
- if (validationError) return githubToolError(validationError);
56
+ if (validationError) return githubToolError(validationError, 'invalid-request');
71
57
  tokenPromise ??= this.tokenProvider.getInstallationAccessToken(installationId);
72
58
  const token = await tokenPromise;
73
59
  if (!hasGrantedPermissions(token.permissions ?? {}, tool, call)) {
74
- return githubToolError('GitHub installation token is missing permission for this operation');
60
+ return githubToolError('GitHub installation token is missing permission for this operation', 'access-denied');
75
61
  }
76
62
  const client = (this.options.createClient ?? createOctokitClient)(token.token);
77
63
  const method = typeof call.arguments.method === 'string' ? call.arguments.method : undefined;
78
64
  if (operation.kind === 'graphql') {
79
65
  const data = await mapGithubError(()=>addCommentToPendingReview(client, operation.parameters));
80
- return data === undefined ? githubToolError(NO_PENDING_REVIEW_MESSAGE) : githubToolResult(tool.id, data);
66
+ return data === undefined ? githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected') : githubToolResult(tool.id, data);
81
67
  }
82
68
  const operationParameters = await mapGithubError(()=>resolvePendingReviewParameters(client, operation.parameters, tool.id, method));
83
69
  if (operationParameters === undefined) {
84
- return githubToolError(NO_PENDING_REVIEW_MESSAGE);
70
+ return githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected');
85
71
  }
86
72
  const response = await mapGithubError(()=>client.request(operation.route, operationParameters));
87
73
  return githubToolResult(tool.id, response.data, response, operationParameters, operation.route);
@@ -254,15 +240,13 @@ async function addCommentToPendingReview(client, args) {
254
240
  if (client.graphql === undefined) {
255
241
  throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub client does not support GraphQL operations');
256
242
  }
257
- const reviewLookup = await client.graphql(LATEST_PENDING_REVIEW_QUERY, {
258
- owner: args.owner,
259
- repo: args.repo,
260
- pullNumber: args.pull_number
261
- });
262
- const reviewId = latestPendingReviewNodeId(reviewLookup);
263
- if (reviewId === undefined) return undefined;
243
+ const review = await latestPendingReview(client, args, 'nodeId');
244
+ if (review === undefined) return undefined;
245
+ if (review.nodeId === undefined) {
246
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub pending pull request review did not include a node ID');
247
+ }
264
248
  const input = {
265
- pullRequestReviewId: reviewId,
249
+ pullRequestReviewId: review.nodeId,
266
250
  path: args.path,
267
251
  body: args.body,
268
252
  subjectType: args.subject_type
@@ -275,28 +259,6 @@ async function addCommentToPendingReview(client, args) {
275
259
  input
276
260
  });
277
261
  }
278
- function latestPendingReviewNodeId(data) {
279
- if (!isRecord(data)) return undefined;
280
- const viewer = isRecord(data.viewer) ? data.viewer.login : undefined;
281
- if (typeof viewer !== 'string') return undefined;
282
- const repository = isRecord(data.repository) ? data.repository : undefined;
283
- const pullRequest = repository && isRecord(repository.pullRequest) ? repository.pullRequest : undefined;
284
- const reviews = pullRequest && isRecord(pullRequest.reviews) ? pullRequest.reviews : undefined;
285
- const nodes = reviews && Array.isArray(reviews.nodes) ? reviews.nodes : [];
286
- let latest;
287
- for (const node of nodes){
288
- if (!isRecord(node)) continue;
289
- const author = isRecord(node.author) ? node.author.login : undefined;
290
- const id = node.id;
291
- const createdAt = node.createdAt;
292
- if (author !== viewer || typeof id !== 'string' || typeof createdAt !== 'string') continue;
293
- if (latest === undefined || createdAt > latest.createdAt) latest = {
294
- createdAt,
295
- id
296
- };
297
- }
298
- return latest?.id;
299
- }
300
262
  export function projectGithubOperationParameters(toolId, method, args) {
301
263
  const parameters = {
302
264
  ...args
@@ -315,45 +277,126 @@ export function projectGithubOperationParameters(toolId, method, args) {
315
277
  }
316
278
  async function resolvePendingReviewParameters(client, parameters, toolId, method) {
317
279
  if (!isPendingReviewOperation(toolId, method)) return parameters;
318
- const reviewId = await latestPendingReviewId(client, parameters);
319
- return reviewId === undefined ? undefined : {
280
+ const review = await latestPendingReview(client, parameters, 'id');
281
+ if (review === undefined) return undefined;
282
+ if (review.id === undefined) {
283
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub pending pull request review did not include a numeric ID');
284
+ }
285
+ return {
320
286
  ...parameters,
321
- review_id: reviewId
287
+ review_id: review.id
322
288
  };
323
289
  }
324
290
  function isPendingReviewOperation(toolId, method) {
325
291
  return toolId === 'pull_request_review_write' && (method === 'submit_pending' || method === 'delete_pending');
326
292
  }
327
- async function latestPendingReviewId(client, parameters) {
328
- const perPage = 100;
329
- let page = 1;
330
- let reviewId;
331
- while(true){
332
- const response = await client.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews', {
293
+ async function latestPendingReview(client, parameters, requiredIdentifier) {
294
+ const lookupController = new AbortController();
295
+ const lookupTimeout = setTimeout(()=>lookupController.abort(), PENDING_REVIEW_LOOKUP_TIMEOUT_MS);
296
+ try {
297
+ return await latestPendingReviewBeforeDeadline(client, parameters, requiredIdentifier, lookupController.signal);
298
+ } finally{
299
+ clearTimeout(lookupTimeout);
300
+ }
301
+ }
302
+ async function latestPendingReviewBeforeDeadline(client, parameters, requiredIdentifier, lookupSignal) {
303
+ const firstPage = await requestPendingReviewPage(client, parameters, 1, lookupSignal);
304
+ const lastPage = pendingReviewLastPage(firstPage.headers);
305
+ let requests = 1;
306
+ let malformed = false;
307
+ for(let page = lastPage; page >= 1; page -= 1){
308
+ let response = firstPage;
309
+ if (page !== 1) {
310
+ if (requests >= PENDING_REVIEW_MAX_PAGE_REQUESTS) {
311
+ throw new GithubIntegrationProviderError('content-too-large', 'GitHub pull request review history exceeded the pending review lookup limit');
312
+ }
313
+ response = await requestPendingReviewPage(client, parameters, page, lookupSignal);
314
+ requests += 1;
315
+ }
316
+ if (!Array.isArray(response.data)) {
317
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub pull request review list response was malformed');
318
+ }
319
+ const result = latestPendingReviewOnPage(response.data, requiredIdentifier);
320
+ if (result.review !== undefined) return result.review;
321
+ malformed ||= result.malformed;
322
+ }
323
+ if (malformed) {
324
+ throw new GithubIntegrationProviderError('malformed-provider-response', requiredIdentifier === 'nodeId' ? 'GitHub pending pull request review did not include a node ID' : 'GitHub pending pull request review did not include a numeric ID');
325
+ }
326
+ return undefined;
327
+ }
328
+ async function requestPendingReviewPage(client, parameters, page, lookupSignal) {
329
+ const pageController = new AbortController();
330
+ const abortPage = ()=>pageController.abort();
331
+ if (lookupSignal.aborted) abortPage();
332
+ else lookupSignal.addEventListener('abort', abortPage, {
333
+ once: true
334
+ });
335
+ const pageTimeout = setTimeout(abortPage, PENDING_REVIEW_PAGE_TIMEOUT_MS);
336
+ try {
337
+ return await client.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews', {
333
338
  owner: parameters.owner,
334
339
  repo: parameters.repo,
335
340
  pull_number: parameters.pull_number,
336
- per_page: perPage,
337
- page
341
+ per_page: PENDING_REVIEW_PAGE_SIZE,
342
+ page,
343
+ request: {
344
+ signal: pageController.signal
345
+ }
338
346
  });
339
- if (!Array.isArray(response.data)) return undefined;
340
- reviewId = latestPendingReviewIdOnPage(response.data) ?? reviewId;
341
- if (response.data.length < perPage) return reviewId;
342
- page += 1;
347
+ } finally{
348
+ clearTimeout(pageTimeout);
349
+ lookupSignal.removeEventListener('abort', abortPage);
350
+ }
351
+ }
352
+ function pendingReviewLastPage(headers) {
353
+ const link = headers?.link;
354
+ if (typeof link !== 'string') return 1;
355
+ const lastLink = link.split(',').find((part)=>part.includes('rel="last"'));
356
+ if (lastLink === undefined) return 1;
357
+ const match = PENDING_REVIEW_PAGE_PATTERN.exec(lastLink);
358
+ const page = match?.[1] === undefined ? Number.NaN : Number.parseInt(match[1], 10);
359
+ if (!Number.isSafeInteger(page) || page < 1) {
360
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub pull request review pagination response was malformed');
343
361
  }
362
+ return page;
344
363
  }
345
- function latestPendingReviewIdOnPage(data) {
364
+ function latestPendingReviewOnPage(data, requiredIdentifier) {
365
+ let malformed = false;
366
+ const appLogin = githubAppBotLogin().toLowerCase();
346
367
  for(let index = data.length - 1; index >= 0; index -= 1){
347
368
  const review = data[index];
348
369
  if (!isRecord(review) || review.state !== 'PENDING') continue;
349
- if (typeof review.id === 'number' && Number.isSafeInteger(review.id)) return review.id;
370
+ const userLogin = isRecord(review.user) ? review.user.login : undefined;
371
+ if (typeof userLogin !== 'string' || userLogin.trim().length === 0) {
372
+ malformed = true;
373
+ continue;
374
+ }
375
+ if (userLogin.trim().toLowerCase() !== appLogin) continue;
376
+ const id = typeof review.id === 'number' && Number.isSafeInteger(review.id) && review.id > 0 ? review.id : undefined;
377
+ const nodeId = typeof review.node_id === 'string' && review.node_id.trim().length > 0 ? review.node_id.trim() : undefined;
378
+ const reference = {
379
+ id,
380
+ nodeId
381
+ };
382
+ if (reference[requiredIdentifier] !== undefined) return {
383
+ malformed,
384
+ review: reference
385
+ };
386
+ malformed = true;
350
387
  }
351
- return undefined;
388
+ return {
389
+ malformed
390
+ };
391
+ }
392
+ function githubAppBotLogin() {
393
+ const configuredUsername = config.GITHUB_APP_USERNAME?.trim() || config.GITHUB_APP_SLUG.trim();
394
+ return configuredUsername.toLowerCase().endsWith(GITHUB_APP_BOT_SUFFIX) ? configuredUsername : `${configuredUsername}${GITHUB_APP_BOT_SUFFIX}`;
352
395
  }
353
396
  function githubToolResult(toolId, data, response, parameters, route) {
354
397
  const structuredContent = projectGithubToolOutput(toolId, data, response, parameters, route);
355
398
  if (structuredContent === undefined) {
356
- return githubToolError('GitHub artifact download did not return a download URL');
399
+ return githubToolError('GitHub artifact download did not return a download URL', 'malformed-provider-response');
357
400
  }
358
401
  return {
359
402
  content: [
@@ -424,7 +467,7 @@ function projectGithubArtifactDownloadOutput(response, parameters) {
424
467
  function githubSearchItems(data) {
425
468
  return isRecord(data) ? data.items : data;
426
469
  }
427
- function githubToolError(message) {
470
+ function githubToolError(message, code) {
428
471
  return {
429
472
  isError: true,
430
473
  content: [
@@ -432,7 +475,10 @@ function githubToolError(message) {
432
475
  type: 'text',
433
476
  text: message
434
477
  }
435
- ]
478
+ ],
479
+ structuredContent: {
480
+ code
481
+ }
436
482
  };
437
483
  }
438
484
  function isRecord(value) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/agent-tools.ts"],"sourcesContent":["import type {\n AgentToolCallInput,\n AgentToolCatalogEntry,\n AgentToolSelectionCatalog,\n AgentToolSession,\n AgentToolsProvider,\n IntegrationConnection,\n OpenAgentToolsSessionInput,\n} from '@shipfox/api-integration-spi';\nimport {Octokit} from 'octokit';\nimport {mapGithubError} from '#api/client.js';\nimport {\n createGithubInstallationTokenProvider,\n type GithubInstallationTokenProvider,\n} from '#api/installation-token-provider.js';\nimport {normalizedGithubApiBaseUrl} from '#config.js';\nimport type {GithubInstallation} from '#db/installations.js';\nimport {GithubIntegrationProviderError} from './errors.js';\nimport {\n type GithubAgentToolCatalogEntry,\n type GithubAgentToolId,\n type GithubAgentToolRequiredScope,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\nexport type {\n GithubAgentToolCatalogEntry,\n GithubAgentToolCategory,\n GithubAgentToolId,\n GithubAgentToolPermission,\n GithubAgentToolPermissionAccess,\n GithubAgentToolRequiredPermission,\n GithubAgentToolRequiredScope,\n GithubAgentToolSensitivity,\n} from './github-agent-tool-catalog.js';\nexport {\n buildGithubAgentToolSelectionCatalog,\n DEFAULT_JOB_LOG_TAIL_LINES,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\ntype GithubToolCallResult = {\n isError?: boolean | undefined;\n content: readonly {type: 'text'; text: string}[];\n structuredContent?: Record<string, unknown> | undefined;\n};\n\nconst GITHUB_GRAPHQL_ROUTE = 'POST /graphql';\nconst GITHUB_ARTIFACT_ARCHIVE_FORMAT = 'zip';\nconst GITHUB_ARTIFACT_DOWNLOAD_ROUTE = `GET /repos/{owner}/{repo}/actions/artifacts/{resource_id}/${GITHUB_ARTIFACT_ARCHIVE_FORMAT}`;\nconst GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS = 30_000;\nconst NO_PENDING_REVIEW_MESSAGE =\n 'No pending pull request review found for the authenticated GitHub user.';\n\nconst LATEST_PENDING_REVIEW_QUERY = `\n query LatestPendingPullRequestReview($owner: String!, $repo: String!, $pullNumber: Int!) {\n viewer {\n login\n }\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $pullNumber) {\n reviews(last: 100, states: [PENDING]) {\n nodes {\n id\n author {\n login\n }\n createdAt\n }\n }\n }\n }\n }\n`;\n\nconst ADD_PENDING_REVIEW_COMMENT_MUTATION = `\n mutation AddCommentToPendingReview($input: AddPullRequestReviewThreadInput!) {\n addPullRequestReviewThread(input: $input) {\n thread {\n id\n }\n }\n }\n`;\n\nexport class GithubAgentToolsProvider\n implements\n AgentToolsProvider<\n GithubIntegrationConnection,\n GithubAgentToolRequiredScope,\n unknown,\n GithubToolCallResult\n >\n{\n private readonly tokenProvider: GithubInstallationTokenProvider;\n\n constructor(private readonly options: GithubAgentToolsProviderOptions = {}) {\n this.tokenProvider = options.tokenProvider ?? createGithubInstallationTokenProvider();\n }\n\n catalog(): readonly GithubAgentToolCatalogEntry[] {\n return githubAgentToolCatalog;\n }\n\n selectionCatalog(): AgentToolSelectionCatalog {\n return githubAgentToolSelectionCatalog;\n }\n\n async openSession(\n input: OpenAgentToolsSessionInput<GithubIntegrationConnection, GithubAgentToolRequiredScope>,\n ): Promise<AgentToolSession<GithubToolCallResult>> {\n const installation = await this.options.getInstallationByConnectionId?.(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n 'GitHub installation is not connected to this integration',\n );\n }\n const installationId = Number(installation.installationId);\n if (!Number.isSafeInteger(installationId) || installationId < 1) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation has an invalid installation ID',\n );\n }\n let tokenPromise:\n | ReturnType<GithubInstallationTokenProvider['getInstallationAccessToken']>\n | undefined;\n\n return {\n call: async (call) => {\n const tool = input.tools.find((candidate) => candidate.id === call.toolId);\n if (!tool) return githubToolError(`Unknown GitHub tool: ${call.toolId}`);\n const operation = resolveGithubOperation(tool, call);\n if (operation === undefined) return githubToolError('Unknown GitHub tool operation');\n const validationError = validateGithubToolArguments(tool, call.arguments);\n if (validationError) return githubToolError(validationError);\n tokenPromise ??= this.tokenProvider.getInstallationAccessToken(installationId);\n const token = await tokenPromise;\n if (!hasGrantedPermissions(token.permissions ?? {}, tool, call)) {\n return githubToolError(\n 'GitHub installation token is missing permission for this operation',\n );\n }\n const client = (this.options.createClient ?? createOctokitClient)(token.token);\n const method =\n typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n\n if (operation.kind === 'graphql') {\n const data = await mapGithubError(() =>\n addCommentToPendingReview(client, operation.parameters),\n );\n return data === undefined\n ? githubToolError(NO_PENDING_REVIEW_MESSAGE)\n : githubToolResult(tool.id as GithubAgentToolId, data);\n }\n\n const operationParameters = await mapGithubError(() =>\n resolvePendingReviewParameters(\n client,\n operation.parameters,\n tool.id as GithubAgentToolId,\n method,\n ),\n );\n if (operationParameters === undefined) {\n return githubToolError(NO_PENDING_REVIEW_MESSAGE);\n }\n const response = await mapGithubError(() =>\n client.request(operation.route, operationParameters),\n );\n return githubToolResult(\n tool.id as GithubAgentToolId,\n response.data,\n response,\n operationParameters,\n operation.route,\n );\n },\n };\n }\n}\n\nexport interface GithubAgentToolsProviderOptions {\n getInstallationByConnectionId?:\n | ((connectionId: string) => Promise<GithubInstallation | undefined>)\n | undefined;\n tokenProvider?: GithubInstallationTokenProvider | undefined;\n createClient?: GithubToolClientFactory | undefined;\n}\n\nexport interface GithubToolResponse {\n data: unknown;\n headers?: Record<string, string | number | undefined> | undefined;\n status?: number | undefined;\n url?: string | undefined;\n}\n\nexport interface GithubToolClient {\n request(route: string, parameters: Record<string, unknown>): Promise<GithubToolResponse>;\n graphql?: ((query: string, variables: Record<string, unknown>) => Promise<unknown>) | undefined;\n}\n\nexport type GithubToolClientFactory = (token: string) => GithubToolClient;\n\ninterface GithubToolOperation {\n route: string;\n parameters: Record<string, unknown>;\n kind: 'rest' | 'graphql';\n}\n\nfunction createOctokitClient(token: string): GithubToolClient {\n const octokit = new Octokit({\n auth: token,\n baseUrl: normalizedGithubApiBaseUrl(),\n retry: {enabled: false},\n });\n return {\n request: async (route, parameters) => {\n if (route !== GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return await octokit.request(route, parameters);\n }\n\n const abortController = new AbortController();\n const timeout = setTimeout(\n () => abortController.abort(),\n GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS,\n );\n try {\n return await octokit.request(route, {\n ...parameters,\n request: {\n redirect: 'manual',\n parseSuccessResponseBody: false,\n signal: abortController.signal,\n },\n });\n } finally {\n clearTimeout(timeout);\n }\n },\n graphql: async (query, variables) => await octokit.graphql(query, variables),\n };\n}\n\nfunction resolveGithubOperation(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): GithubToolOperation | undefined {\n const args = call.arguments;\n const method = typeof args.method === 'string' ? args.method : undefined;\n const params = {...args};\n delete params.method;\n\n if (tool.methods && !tool.methods.some((candidate) => candidate.id === method)) return undefined;\n\n const toolId = tool.id as GithubAgentToolId;\n const route = githubOperationRoute(toolId, method, params);\n return route === undefined\n ? undefined\n : {\n route,\n parameters: projectGithubOperationParameters(toolId, method, params),\n kind: route === GITHUB_GRAPHQL_ROUTE ? 'graphql' : 'rest',\n };\n}\n\nexport function githubOperationRoute(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): string | undefined {\n const owner = '{owner}';\n const repo = '{repo}';\n const issue = '{issue_number}';\n const pull = '{pull_number}';\n const run = '{run_id}';\n const resource = '{resource_id}';\n const repoPath = `/repos/${owner}/${repo}`;\n\n switch (`${toolId}.${method ?? ''}`) {\n case 'issue_read.get':\n return `GET ${repoPath}/issues/${issue}`;\n case 'issue_read.get_comments':\n return `GET ${repoPath}/issues/${issue}/comments`;\n case 'issue_read.get_sub_issues':\n return `GET ${repoPath}/issues/${issue}/sub_issues`;\n case 'issue_read.get_parent':\n return `GET ${repoPath}/issues/${issue}/parent`;\n case 'issue_read.get_labels':\n return `GET ${repoPath}/issues/${issue}/labels`;\n case 'list_issue_types.':\n return args.repo === undefined\n ? 'GET /orgs/{owner}/issue-types'\n : `GET ${repoPath}/issue-types`;\n case 'list_issues.':\n return `GET ${repoPath}/issues`;\n case 'search_issues.':\n return 'GET /search/issues';\n case 'add_issue_comment.':\n if (args.comment_id !== undefined)\n return `POST ${repoPath}/issues/comments/{comment_id}/reactions`;\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/issues/${issue}/reactions`\n : `POST ${repoPath}/issues/${issue}/comments`;\n case 'issue_write.create':\n return `POST ${repoPath}/issues`;\n case 'issue_write.update':\n return `PATCH ${repoPath}/issues/${issue}`;\n case 'sub_issue_write.add':\n return `POST ${repoPath}/issues/${issue}/sub_issues`;\n case 'sub_issue_write.remove':\n return `DELETE ${repoPath}/issues/${issue}/sub_issues/{sub_issue_id}`;\n case 'sub_issue_write.reprioritize':\n return `PATCH ${repoPath}/issues/${issue}/sub_issues/priority`;\n case 'pull_request_read.get':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_diff':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_status':\n return `GET ${repoPath}/commits/{ref}/status`;\n case 'pull_request_read.get_files':\n return `GET ${repoPath}/pulls/${pull}/files`;\n case 'pull_request_read.get_commits':\n return `GET ${repoPath}/pulls/${pull}/commits`;\n case 'pull_request_read.get_review_comments':\n return `GET ${repoPath}/pulls/${pull}/comments`;\n case 'pull_request_read.get_reviews':\n return `GET ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_read.get_comments':\n return `GET ${repoPath}/issues/${pull}/comments`;\n case 'pull_request_read.get_check_runs':\n return `GET ${repoPath}/commits/{ref}/check-runs`;\n case 'list_pull_requests.':\n return `GET ${repoPath}/pulls`;\n case 'search_pull_requests.':\n return 'GET /search/issues';\n case 'create_pull_request.':\n return `POST ${repoPath}/pulls`;\n case 'update_pull_request.':\n return `PATCH ${repoPath}/pulls/${pull}`;\n case 'add_reply_to_pull_request_comment.':\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/pulls/comments/{comment_id}/reactions`\n : `POST ${repoPath}/pulls/${pull}/comments/{comment_id}/replies`;\n case 'merge_pull_request.':\n return `PUT ${repoPath}/pulls/${pull}/merge`;\n case 'update_pull_request_branch.':\n return `PUT ${repoPath}/pulls/${pull}/update-branch`;\n case 'pull_request_review_write.create':\n return `POST ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_review_write.submit_pending':\n return `POST ${repoPath}/pulls/${pull}/reviews/{review_id}/events`;\n case 'pull_request_review_write.delete_pending':\n return `DELETE ${repoPath}/pulls/${pull}/reviews/{review_id}`;\n case 'add_comment_to_pending_review.':\n return GITHUB_GRAPHQL_ROUTE;\n case 'actions_list.list_workflows':\n return `GET ${repoPath}/actions/workflows`;\n case 'actions_list.list_workflow_runs':\n return `GET ${repoPath}/actions/workflows/${resource}/runs`;\n case 'actions_list.list_workflow_jobs':\n return `GET ${repoPath}/actions/runs/${resource}/jobs`;\n case 'actions_list.list_workflow_run_artifacts':\n return `GET ${repoPath}/actions/runs/${resource}/artifacts`;\n case 'actions_get.get_workflow':\n return `GET ${repoPath}/actions/workflows/${resource}`;\n case 'actions_get.get_workflow_run':\n return `GET ${repoPath}/actions/runs/${resource}`;\n case 'actions_get.get_workflow_job':\n return `GET ${repoPath}/actions/jobs/${resource}`;\n case 'actions_get.download_workflow_run_artifact':\n return GITHUB_ARTIFACT_DOWNLOAD_ROUTE;\n case 'actions_get.get_workflow_run_usage':\n return `GET ${repoPath}/actions/runs/${resource}/timing`;\n case 'actions_get.get_workflow_run_logs_url':\n return `GET ${repoPath}/actions/runs/${resource}/logs`;\n case 'actions_run_trigger.run_workflow':\n return `POST ${repoPath}/actions/workflows/{workflow_id}/dispatches`;\n case 'actions_run_trigger.rerun_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/rerun`;\n case 'actions_run_trigger.rerun_failed_jobs':\n return `POST ${repoPath}/actions/runs/${run}/rerun-failed-jobs`;\n case 'actions_run_trigger.cancel_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/cancel`;\n case 'actions_run_trigger.delete_workflow_run_logs':\n return `DELETE ${repoPath}/actions/runs/${run}/logs`;\n case 'get_job_logs.':\n return `GET ${repoPath}/actions/jobs/{job_id}/logs`;\n default:\n return undefined;\n }\n}\n\nasync function addCommentToPendingReview(\n client: GithubToolClient,\n args: Record<string, unknown>,\n): Promise<unknown | undefined> {\n if (client.graphql === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub client does not support GraphQL operations',\n );\n }\n\n const reviewLookup = await client.graphql(LATEST_PENDING_REVIEW_QUERY, {\n owner: args.owner,\n repo: args.repo,\n pullNumber: args.pull_number,\n });\n const reviewId = latestPendingReviewNodeId(reviewLookup);\n if (reviewId === undefined) return undefined;\n\n const input: Record<string, unknown> = {\n pullRequestReviewId: reviewId,\n path: args.path,\n body: args.body,\n subjectType: args.subject_type,\n };\n if (args.line !== undefined) input.line = args.line;\n if (args.side !== undefined) input.side = args.side;\n if (args.start_line !== undefined) input.startLine = args.start_line;\n if (args.start_side !== undefined) input.startSide = args.start_side;\n\n return await client.graphql(ADD_PENDING_REVIEW_COMMENT_MUTATION, {input});\n}\n\nfunction latestPendingReviewNodeId(data: unknown): string | undefined {\n if (!isRecord(data)) return undefined;\n\n const viewer = isRecord(data.viewer) ? data.viewer.login : undefined;\n if (typeof viewer !== 'string') return undefined;\n\n const repository = isRecord(data.repository) ? data.repository : undefined;\n const pullRequest =\n repository && isRecord(repository.pullRequest) ? repository.pullRequest : undefined;\n const reviews = pullRequest && isRecord(pullRequest.reviews) ? pullRequest.reviews : undefined;\n const nodes = reviews && Array.isArray(reviews.nodes) ? reviews.nodes : [];\n\n let latest: {createdAt: string; id: string} | undefined;\n for (const node of nodes) {\n if (!isRecord(node)) continue;\n const author = isRecord(node.author) ? node.author.login : undefined;\n const id = node.id;\n const createdAt = node.createdAt;\n if (author !== viewer || typeof id !== 'string' || typeof createdAt !== 'string') continue;\n if (latest === undefined || createdAt > latest.createdAt) latest = {createdAt, id};\n }\n\n return latest?.id;\n}\n\nexport function projectGithubOperationParameters(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): Record<string, unknown> {\n const parameters = {...args};\n if (toolId === 'add_issue_comment' && parameters.reaction !== undefined) {\n parameters.content = parameters.reaction;\n delete parameters.reaction;\n if (parameters.body === undefined) delete parameters.body;\n }\n if (toolId === 'pull_request_read' && method === 'get_diff') {\n parameters.headers = {accept: 'application/vnd.github.diff'};\n }\n return parameters;\n}\n\nasync function resolvePendingReviewParameters(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n toolId: GithubAgentToolId,\n method: string | undefined,\n): Promise<Record<string, unknown> | undefined> {\n if (!isPendingReviewOperation(toolId, method)) return parameters;\n\n const reviewId = await latestPendingReviewId(client, parameters);\n return reviewId === undefined ? undefined : {...parameters, review_id: reviewId};\n}\n\nfunction isPendingReviewOperation(toolId: GithubAgentToolId, method: string | undefined): boolean {\n return (\n toolId === 'pull_request_review_write' &&\n (method === 'submit_pending' || method === 'delete_pending')\n );\n}\n\nasync function latestPendingReviewId(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n): Promise<number | undefined> {\n const perPage = 100;\n let page = 1;\n let reviewId: number | undefined;\n\n while (true) {\n const response = await client.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews', {\n owner: parameters.owner,\n repo: parameters.repo,\n pull_number: parameters.pull_number,\n per_page: perPage,\n page,\n });\n if (!Array.isArray(response.data)) return undefined;\n\n reviewId = latestPendingReviewIdOnPage(response.data) ?? reviewId;\n if (response.data.length < perPage) return reviewId;\n page += 1;\n }\n}\n\nfunction latestPendingReviewIdOnPage(data: readonly unknown[]): number | undefined {\n for (let index = data.length - 1; index >= 0; index -= 1) {\n const review = data[index];\n if (!isRecord(review) || review.state !== 'PENDING') continue;\n if (typeof review.id === 'number' && Number.isSafeInteger(review.id)) return review.id;\n }\n\n return undefined;\n}\n\nfunction githubToolResult(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): GithubToolCallResult {\n const structuredContent = projectGithubToolOutput(toolId, data, response, parameters, route);\n if (structuredContent === undefined) {\n return githubToolError('GitHub artifact download did not return a download URL');\n }\n return {\n content: [{type: 'text', text: JSON.stringify(structuredContent)}],\n structuredContent,\n };\n}\n\nfunction projectGithubToolOutput(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): Record<string, unknown> | undefined {\n if (route === GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return projectGithubArtifactDownloadOutput(response, parameters);\n }\n\n switch (toolId) {\n case 'list_issue_types':\n return {issue_types: data};\n case 'list_issues':\n return {issues: data};\n case 'search_issues':\n return {issues: githubSearchItems(data)};\n case 'list_pull_requests':\n return {pull_requests: data};\n case 'search_pull_requests':\n return {pull_requests: githubSearchItems(data)};\n case 'create_pull_request':\n case 'update_pull_request':\n return {pull_request: data};\n case 'merge_pull_request':\n return {merge: data};\n default:\n return isRecord(data) ? data : {result: data};\n }\n}\n\nfunction projectGithubArtifactDownloadOutput(\n response: GithubToolResponse | undefined,\n parameters: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (response === undefined) return undefined;\n const downloadUrl = response.headers?.location;\n if (typeof downloadUrl !== 'string' || downloadUrl.length === 0) return undefined;\n\n const output: Record<string, unknown> = {\n archive_format: GITHUB_ARTIFACT_ARCHIVE_FORMAT,\n download_url: downloadUrl,\n };\n if (typeof parameters?.resource_id === 'string') output.artifact_id = parameters.resource_id;\n\n const contentType = response.headers?.['content-type'];\n if (typeof contentType === 'string') output.content_type = contentType;\n\n const contentLength = response.headers?.['content-length'];\n const sizeBytes = typeof contentLength === 'number' ? contentLength : Number(contentLength);\n if (Number.isSafeInteger(sizeBytes) && sizeBytes >= 0) output.size_bytes = sizeBytes;\n\n return output;\n}\n\nfunction githubSearchItems(data: unknown): unknown {\n return isRecord(data) ? data.items : data;\n}\n\nfunction githubToolError(message: string): GithubToolCallResult {\n return {isError: true, content: [{type: 'text', text: message}]};\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validateGithubToolArguments(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n arguments_: Record<string, unknown>,\n): string | undefined {\n const required = Array.isArray(tool.inputSchema.required) ? tool.inputSchema.required : [];\n for (const name of required) {\n if (typeof name === 'string' && arguments_[name] === undefined) {\n return `Missing required parameter: ${name}`;\n }\n }\n\n const methodRequired = methodRequiredParameters(tool.inputSchema, arguments_);\n for (const name of methodRequired) {\n if (arguments_[name] === undefined) return `Missing required parameter: ${name}`;\n }\n\n const properties = tool.inputSchema.properties;\n if (typeof properties !== 'object' || properties === null || Array.isArray(properties)) {\n return undefined;\n }\n const propertySchemas = properties as Record<string, unknown>;\n for (const [name, value] of Object.entries(arguments_)) {\n const schema = propertySchemas[name];\n if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) continue;\n const type = (schema as {type?: unknown}).type;\n if (type === 'integer' && (!Number.isInteger(value) || typeof value !== 'number')) {\n return `Parameter ${name} must be an integer`;\n }\n if (type === 'array' && !Array.isArray(value)) return `Parameter ${name} must be an array`;\n }\n return undefined;\n}\n\nfunction methodRequiredParameters(\n inputSchema: AgentToolCatalogEntry<GithubAgentToolRequiredScope>['inputSchema'],\n arguments_: Record<string, unknown>,\n): string[] {\n const method = arguments_.method;\n if (typeof method !== 'string' || !Array.isArray(inputSchema.oneOf)) return [];\n\n for (const candidate of inputSchema.oneOf) {\n if (!isRecord(candidate) || !isRecord(candidate.properties)) continue;\n const methodSchema = candidate.properties.method;\n if (!isRecord(methodSchema) || methodSchema.const !== method) continue;\n return Array.isArray(candidate.required)\n ? candidate.required.filter((name): name is string => typeof name === 'string')\n : [];\n }\n\n return [];\n}\n\nfunction hasGrantedPermissions(\n granted: Record<string, 'read' | 'write' | 'admin'>,\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): boolean {\n const method = typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n const required =\n tool.methods?.find((candidate) => candidate.id === method)?.requiredScope ?? tool.requiredScope;\n return required.every(({permission, access}) => {\n const actual = granted[permission];\n return actual === 'write' || actual === 'admin' || actual === access;\n });\n}\n"],"names":["Octokit","mapGithubError","createGithubInstallationTokenProvider","normalizedGithubApiBaseUrl","GithubIntegrationProviderError","githubAgentToolCatalog","githubAgentToolSelectionCatalog","buildGithubAgentToolSelectionCatalog","DEFAULT_JOB_LOG_TAIL_LINES","GITHUB_GRAPHQL_ROUTE","GITHUB_ARTIFACT_ARCHIVE_FORMAT","GITHUB_ARTIFACT_DOWNLOAD_ROUTE","GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS","NO_PENDING_REVIEW_MESSAGE","LATEST_PENDING_REVIEW_QUERY","ADD_PENDING_REVIEW_COMMENT_MUTATION","GithubAgentToolsProvider","options","tokenProvider","catalog","selectionCatalog","openSession","input","installation","getInstallationByConnectionId","connection","id","installationId","Number","isSafeInteger","tokenPromise","call","tool","tools","find","candidate","toolId","githubToolError","operation","resolveGithubOperation","undefined","validationError","validateGithubToolArguments","arguments","getInstallationAccessToken","token","hasGrantedPermissions","permissions","client","createClient","createOctokitClient","method","kind","data","addCommentToPendingReview","parameters","githubToolResult","operationParameters","resolvePendingReviewParameters","response","request","route","octokit","auth","baseUrl","retry","enabled","abortController","AbortController","timeout","setTimeout","abort","redirect","parseSuccessResponseBody","signal","clearTimeout","graphql","query","variables","args","params","methods","some","githubOperationRoute","projectGithubOperationParameters","owner","repo","issue","pull","run","resource","repoPath","comment_id","reaction","body","reviewLookup","pullNumber","pull_number","reviewId","latestPendingReviewNodeId","pullRequestReviewId","path","subjectType","subject_type","line","side","start_line","startLine","start_side","startSide","isRecord","viewer","login","repository","pullRequest","reviews","nodes","Array","isArray","latest","node","author","createdAt","content","headers","accept","isPendingReviewOperation","latestPendingReviewId","review_id","perPage","page","per_page","latestPendingReviewIdOnPage","length","index","review","state","structuredContent","projectGithubToolOutput","type","text","JSON","stringify","projectGithubArtifactDownloadOutput","issue_types","issues","githubSearchItems","pull_requests","pull_request","merge","result","downloadUrl","location","output","archive_format","download_url","resource_id","artifact_id","contentType","content_type","contentLength","sizeBytes","size_bytes","items","message","isError","value","arguments_","required","inputSchema","name","methodRequired","methodRequiredParameters","properties","propertySchemas","Object","entries","schema","isInteger","oneOf","methodSchema","const","filter","granted","requiredScope","every","permission","access","actual"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/agent-tools.ts"],"sourcesContent":["import type {\n AgentToolCallInput,\n AgentToolCatalogEntry,\n AgentToolSelectionCatalog,\n AgentToolSession,\n AgentToolsProvider,\n IntegrationConnection,\n OpenAgentToolsSessionInput,\n} from '@shipfox/api-integration-spi';\nimport {Octokit} from 'octokit';\nimport {mapGithubError} from '#api/client.js';\nimport {\n createGithubInstallationTokenProvider,\n type GithubInstallationTokenProvider,\n} from '#api/installation-token-provider.js';\nimport {config, normalizedGithubApiBaseUrl} from '#config.js';\nimport type {GithubInstallation} from '#db/installations.js';\nimport {GithubIntegrationProviderError} from './errors.js';\nimport {\n type GithubAgentToolCatalogEntry,\n type GithubAgentToolId,\n type GithubAgentToolRequiredScope,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\nexport type {\n GithubAgentToolCatalogEntry,\n GithubAgentToolCategory,\n GithubAgentToolId,\n GithubAgentToolPermission,\n GithubAgentToolPermissionAccess,\n GithubAgentToolRequiredPermission,\n GithubAgentToolRequiredScope,\n GithubAgentToolSensitivity,\n} from './github-agent-tool-catalog.js';\nexport {\n buildGithubAgentToolSelectionCatalog,\n DEFAULT_JOB_LOG_TAIL_LINES,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\ntype GithubToolCallResult = {\n isError?: boolean | undefined;\n content: readonly {type: 'text'; text: string}[];\n structuredContent?: Record<string, unknown> | undefined;\n};\n\ntype GithubToolErrorCode =\n | 'invalid-request'\n | 'access-denied'\n | 'provider-rejected'\n | 'malformed-provider-response';\n\nconst GITHUB_GRAPHQL_ROUTE = 'POST /graphql';\nconst GITHUB_ARTIFACT_ARCHIVE_FORMAT = 'zip';\nconst GITHUB_ARTIFACT_DOWNLOAD_ROUTE = `GET /repos/{owner}/{repo}/actions/artifacts/{resource_id}/${GITHUB_ARTIFACT_ARCHIVE_FORMAT}`;\nconst GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS = 30_000;\nconst GITHUB_APP_BOT_SUFFIX = '[bot]';\nconst PENDING_REVIEW_PAGE_SIZE = 100;\nconst PENDING_REVIEW_MAX_PAGE_REQUESTS = 5;\nconst PENDING_REVIEW_LOOKUP_TIMEOUT_MS = 15_000;\nconst PENDING_REVIEW_PAGE_TIMEOUT_MS = 5_000;\nconst PENDING_REVIEW_PAGE_PATTERN = /[?&]page=(\\d+)/u;\nconst NO_PENDING_REVIEW_MESSAGE =\n 'No pending pull request review found for the authenticated GitHub user.';\n\nconst ADD_PENDING_REVIEW_COMMENT_MUTATION = `\n mutation AddCommentToPendingReview($input: AddPullRequestReviewThreadInput!) {\n addPullRequestReviewThread(input: $input) {\n thread {\n id\n }\n }\n }\n`;\n\nexport class GithubAgentToolsProvider\n implements\n AgentToolsProvider<\n GithubIntegrationConnection,\n GithubAgentToolRequiredScope,\n unknown,\n GithubToolCallResult\n >\n{\n private readonly tokenProvider: GithubInstallationTokenProvider;\n\n constructor(private readonly options: GithubAgentToolsProviderOptions = {}) {\n this.tokenProvider = options.tokenProvider ?? createGithubInstallationTokenProvider();\n }\n\n catalog(): readonly GithubAgentToolCatalogEntry[] {\n return githubAgentToolCatalog;\n }\n\n selectionCatalog(): AgentToolSelectionCatalog {\n return githubAgentToolSelectionCatalog;\n }\n\n async openSession(\n input: OpenAgentToolsSessionInput<GithubIntegrationConnection, GithubAgentToolRequiredScope>,\n ): Promise<AgentToolSession<GithubToolCallResult>> {\n const installation = await this.options.getInstallationByConnectionId?.(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n 'GitHub installation is not connected to this integration',\n );\n }\n const installationId = Number(installation.installationId);\n if (!Number.isSafeInteger(installationId) || installationId < 1) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation has an invalid installation ID',\n );\n }\n let tokenPromise:\n | ReturnType<GithubInstallationTokenProvider['getInstallationAccessToken']>\n | undefined;\n\n return {\n call: async (call) => {\n const tool = input.tools.find((candidate) => candidate.id === call.toolId);\n if (!tool) return githubToolError(`Unknown GitHub tool: ${call.toolId}`, 'invalid-request');\n const operation = resolveGithubOperation(tool, call);\n if (operation === undefined)\n return githubToolError('Unknown GitHub tool operation', 'invalid-request');\n const validationError = validateGithubToolArguments(tool, call.arguments);\n if (validationError) return githubToolError(validationError, 'invalid-request');\n tokenPromise ??= this.tokenProvider.getInstallationAccessToken(installationId);\n const token = await tokenPromise;\n if (!hasGrantedPermissions(token.permissions ?? {}, tool, call)) {\n return githubToolError(\n 'GitHub installation token is missing permission for this operation',\n 'access-denied',\n );\n }\n const client = (this.options.createClient ?? createOctokitClient)(token.token);\n const method =\n typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n\n if (operation.kind === 'graphql') {\n const data = await mapGithubError(() =>\n addCommentToPendingReview(client, operation.parameters),\n );\n return data === undefined\n ? githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected')\n : githubToolResult(tool.id as GithubAgentToolId, data);\n }\n\n const operationParameters = await mapGithubError(() =>\n resolvePendingReviewParameters(\n client,\n operation.parameters,\n tool.id as GithubAgentToolId,\n method,\n ),\n );\n if (operationParameters === undefined) {\n return githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected');\n }\n const response = await mapGithubError(() =>\n client.request(operation.route, operationParameters),\n );\n return githubToolResult(\n tool.id as GithubAgentToolId,\n response.data,\n response,\n operationParameters,\n operation.route,\n );\n },\n };\n }\n}\n\nexport interface GithubAgentToolsProviderOptions {\n getInstallationByConnectionId?:\n | ((connectionId: string) => Promise<GithubInstallation | undefined>)\n | undefined;\n tokenProvider?: GithubInstallationTokenProvider | undefined;\n createClient?: GithubToolClientFactory | undefined;\n}\n\nexport interface GithubToolResponse {\n data: unknown;\n headers?: Record<string, string | number | undefined> | undefined;\n status?: number | undefined;\n url?: string | undefined;\n}\n\nexport interface GithubToolClient {\n request(route: string, parameters: Record<string, unknown>): Promise<GithubToolResponse>;\n graphql?: ((query: string, variables: Record<string, unknown>) => Promise<unknown>) | undefined;\n}\n\nexport type GithubToolClientFactory = (token: string) => GithubToolClient;\n\ninterface GithubToolOperation {\n route: string;\n parameters: Record<string, unknown>;\n kind: 'rest' | 'graphql';\n}\n\nfunction createOctokitClient(token: string): GithubToolClient {\n const octokit = new Octokit({\n auth: token,\n baseUrl: normalizedGithubApiBaseUrl(),\n retry: {enabled: false},\n });\n return {\n request: async (route, parameters) => {\n if (route !== GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return await octokit.request(route, parameters);\n }\n\n const abortController = new AbortController();\n const timeout = setTimeout(\n () => abortController.abort(),\n GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS,\n );\n try {\n return await octokit.request(route, {\n ...parameters,\n request: {\n redirect: 'manual',\n parseSuccessResponseBody: false,\n signal: abortController.signal,\n },\n });\n } finally {\n clearTimeout(timeout);\n }\n },\n graphql: async (query, variables) => await octokit.graphql(query, variables),\n };\n}\n\nfunction resolveGithubOperation(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): GithubToolOperation | undefined {\n const args = call.arguments;\n const method = typeof args.method === 'string' ? args.method : undefined;\n const params = {...args};\n delete params.method;\n\n if (tool.methods && !tool.methods.some((candidate) => candidate.id === method)) return undefined;\n\n const toolId = tool.id as GithubAgentToolId;\n const route = githubOperationRoute(toolId, method, params);\n return route === undefined\n ? undefined\n : {\n route,\n parameters: projectGithubOperationParameters(toolId, method, params),\n kind: route === GITHUB_GRAPHQL_ROUTE ? 'graphql' : 'rest',\n };\n}\n\nexport function githubOperationRoute(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): string | undefined {\n const owner = '{owner}';\n const repo = '{repo}';\n const issue = '{issue_number}';\n const pull = '{pull_number}';\n const run = '{run_id}';\n const resource = '{resource_id}';\n const repoPath = `/repos/${owner}/${repo}`;\n\n switch (`${toolId}.${method ?? ''}`) {\n case 'issue_read.get':\n return `GET ${repoPath}/issues/${issue}`;\n case 'issue_read.get_comments':\n return `GET ${repoPath}/issues/${issue}/comments`;\n case 'issue_read.get_sub_issues':\n return `GET ${repoPath}/issues/${issue}/sub_issues`;\n case 'issue_read.get_parent':\n return `GET ${repoPath}/issues/${issue}/parent`;\n case 'issue_read.get_labels':\n return `GET ${repoPath}/issues/${issue}/labels`;\n case 'list_issue_types.':\n return args.repo === undefined\n ? 'GET /orgs/{owner}/issue-types'\n : `GET ${repoPath}/issue-types`;\n case 'list_issues.':\n return `GET ${repoPath}/issues`;\n case 'search_issues.':\n return 'GET /search/issues';\n case 'add_issue_comment.':\n if (args.comment_id !== undefined)\n return `POST ${repoPath}/issues/comments/{comment_id}/reactions`;\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/issues/${issue}/reactions`\n : `POST ${repoPath}/issues/${issue}/comments`;\n case 'issue_write.create':\n return `POST ${repoPath}/issues`;\n case 'issue_write.update':\n return `PATCH ${repoPath}/issues/${issue}`;\n case 'sub_issue_write.add':\n return `POST ${repoPath}/issues/${issue}/sub_issues`;\n case 'sub_issue_write.remove':\n return `DELETE ${repoPath}/issues/${issue}/sub_issues/{sub_issue_id}`;\n case 'sub_issue_write.reprioritize':\n return `PATCH ${repoPath}/issues/${issue}/sub_issues/priority`;\n case 'pull_request_read.get':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_diff':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_status':\n return `GET ${repoPath}/commits/{ref}/status`;\n case 'pull_request_read.get_files':\n return `GET ${repoPath}/pulls/${pull}/files`;\n case 'pull_request_read.get_commits':\n return `GET ${repoPath}/pulls/${pull}/commits`;\n case 'pull_request_read.get_review_comments':\n return `GET ${repoPath}/pulls/${pull}/comments`;\n case 'pull_request_read.get_reviews':\n return `GET ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_read.get_comments':\n return `GET ${repoPath}/issues/${pull}/comments`;\n case 'pull_request_read.get_check_runs':\n return `GET ${repoPath}/commits/{ref}/check-runs`;\n case 'list_pull_requests.':\n return `GET ${repoPath}/pulls`;\n case 'search_pull_requests.':\n return 'GET /search/issues';\n case 'create_pull_request.':\n return `POST ${repoPath}/pulls`;\n case 'update_pull_request.':\n return `PATCH ${repoPath}/pulls/${pull}`;\n case 'add_reply_to_pull_request_comment.':\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/pulls/comments/{comment_id}/reactions`\n : `POST ${repoPath}/pulls/${pull}/comments/{comment_id}/replies`;\n case 'merge_pull_request.':\n return `PUT ${repoPath}/pulls/${pull}/merge`;\n case 'update_pull_request_branch.':\n return `PUT ${repoPath}/pulls/${pull}/update-branch`;\n case 'pull_request_review_write.create':\n return `POST ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_review_write.submit_pending':\n return `POST ${repoPath}/pulls/${pull}/reviews/{review_id}/events`;\n case 'pull_request_review_write.delete_pending':\n return `DELETE ${repoPath}/pulls/${pull}/reviews/{review_id}`;\n case 'add_comment_to_pending_review.':\n return GITHUB_GRAPHQL_ROUTE;\n case 'actions_list.list_workflows':\n return `GET ${repoPath}/actions/workflows`;\n case 'actions_list.list_workflow_runs':\n return `GET ${repoPath}/actions/workflows/${resource}/runs`;\n case 'actions_list.list_workflow_jobs':\n return `GET ${repoPath}/actions/runs/${resource}/jobs`;\n case 'actions_list.list_workflow_run_artifacts':\n return `GET ${repoPath}/actions/runs/${resource}/artifacts`;\n case 'actions_get.get_workflow':\n return `GET ${repoPath}/actions/workflows/${resource}`;\n case 'actions_get.get_workflow_run':\n return `GET ${repoPath}/actions/runs/${resource}`;\n case 'actions_get.get_workflow_job':\n return `GET ${repoPath}/actions/jobs/${resource}`;\n case 'actions_get.download_workflow_run_artifact':\n return GITHUB_ARTIFACT_DOWNLOAD_ROUTE;\n case 'actions_get.get_workflow_run_usage':\n return `GET ${repoPath}/actions/runs/${resource}/timing`;\n case 'actions_get.get_workflow_run_logs_url':\n return `GET ${repoPath}/actions/runs/${resource}/logs`;\n case 'actions_run_trigger.run_workflow':\n return `POST ${repoPath}/actions/workflows/{workflow_id}/dispatches`;\n case 'actions_run_trigger.rerun_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/rerun`;\n case 'actions_run_trigger.rerun_failed_jobs':\n return `POST ${repoPath}/actions/runs/${run}/rerun-failed-jobs`;\n case 'actions_run_trigger.cancel_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/cancel`;\n case 'actions_run_trigger.delete_workflow_run_logs':\n return `DELETE ${repoPath}/actions/runs/${run}/logs`;\n case 'get_job_logs.':\n return `GET ${repoPath}/actions/jobs/{job_id}/logs`;\n default:\n return undefined;\n }\n}\n\nasync function addCommentToPendingReview(\n client: GithubToolClient,\n args: Record<string, unknown>,\n): Promise<unknown | undefined> {\n if (client.graphql === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub client does not support GraphQL operations',\n );\n }\n\n const review = await latestPendingReview(client, args, 'nodeId');\n if (review === undefined) return undefined;\n if (review.nodeId === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pending pull request review did not include a node ID',\n );\n }\n\n const input: Record<string, unknown> = {\n pullRequestReviewId: review.nodeId,\n path: args.path,\n body: args.body,\n subjectType: args.subject_type,\n };\n if (args.line !== undefined) input.line = args.line;\n if (args.side !== undefined) input.side = args.side;\n if (args.start_line !== undefined) input.startLine = args.start_line;\n if (args.start_side !== undefined) input.startSide = args.start_side;\n\n return await client.graphql(ADD_PENDING_REVIEW_COMMENT_MUTATION, {input});\n}\n\nexport function projectGithubOperationParameters(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): Record<string, unknown> {\n const parameters = {...args};\n if (toolId === 'add_issue_comment' && parameters.reaction !== undefined) {\n parameters.content = parameters.reaction;\n delete parameters.reaction;\n if (parameters.body === undefined) delete parameters.body;\n }\n if (toolId === 'pull_request_read' && method === 'get_diff') {\n parameters.headers = {accept: 'application/vnd.github.diff'};\n }\n return parameters;\n}\n\nasync function resolvePendingReviewParameters(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n toolId: GithubAgentToolId,\n method: string | undefined,\n): Promise<Record<string, unknown> | undefined> {\n if (!isPendingReviewOperation(toolId, method)) return parameters;\n\n const review = await latestPendingReview(client, parameters, 'id');\n if (review === undefined) return undefined;\n if (review.id === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pending pull request review did not include a numeric ID',\n );\n }\n return {...parameters, review_id: review.id};\n}\n\nfunction isPendingReviewOperation(toolId: GithubAgentToolId, method: string | undefined): boolean {\n return (\n toolId === 'pull_request_review_write' &&\n (method === 'submit_pending' || method === 'delete_pending')\n );\n}\n\ninterface PendingReviewReference {\n id?: number | undefined;\n nodeId?: string | undefined;\n}\n\ntype PendingReviewIdentifier = keyof PendingReviewReference;\n\ninterface PendingReviewPageResult {\n malformed: boolean;\n review?: PendingReviewReference | undefined;\n}\n\nasync function latestPendingReview(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n requiredIdentifier: PendingReviewIdentifier,\n): Promise<PendingReviewReference | undefined> {\n const lookupController = new AbortController();\n const lookupTimeout = setTimeout(\n () => lookupController.abort(),\n PENDING_REVIEW_LOOKUP_TIMEOUT_MS,\n );\n\n try {\n return await latestPendingReviewBeforeDeadline(\n client,\n parameters,\n requiredIdentifier,\n lookupController.signal,\n );\n } finally {\n clearTimeout(lookupTimeout);\n }\n}\n\nasync function latestPendingReviewBeforeDeadline(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n requiredIdentifier: PendingReviewIdentifier,\n lookupSignal: AbortSignal,\n): Promise<PendingReviewReference | undefined> {\n const firstPage = await requestPendingReviewPage(client, parameters, 1, lookupSignal);\n const lastPage = pendingReviewLastPage(firstPage.headers);\n let requests = 1;\n let malformed = false;\n\n for (let page = lastPage; page >= 1; page -= 1) {\n let response = firstPage;\n if (page !== 1) {\n if (requests >= PENDING_REVIEW_MAX_PAGE_REQUESTS) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub pull request review history exceeded the pending review lookup limit',\n );\n }\n response = await requestPendingReviewPage(client, parameters, page, lookupSignal);\n requests += 1;\n }\n if (!Array.isArray(response.data)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pull request review list response was malformed',\n );\n }\n\n const result = latestPendingReviewOnPage(response.data, requiredIdentifier);\n if (result.review !== undefined) return result.review;\n malformed ||= result.malformed;\n }\n\n if (malformed) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n requiredIdentifier === 'nodeId'\n ? 'GitHub pending pull request review did not include a node ID'\n : 'GitHub pending pull request review did not include a numeric ID',\n );\n }\n return undefined;\n}\n\nasync function requestPendingReviewPage(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n page: number,\n lookupSignal: AbortSignal,\n): Promise<GithubToolResponse> {\n const pageController = new AbortController();\n const abortPage = () => pageController.abort();\n if (lookupSignal.aborted) abortPage();\n else lookupSignal.addEventListener('abort', abortPage, {once: true});\n const pageTimeout = setTimeout(abortPage, PENDING_REVIEW_PAGE_TIMEOUT_MS);\n\n try {\n return await client.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews', {\n owner: parameters.owner,\n repo: parameters.repo,\n pull_number: parameters.pull_number,\n per_page: PENDING_REVIEW_PAGE_SIZE,\n page,\n request: {signal: pageController.signal},\n });\n } finally {\n clearTimeout(pageTimeout);\n lookupSignal.removeEventListener('abort', abortPage);\n }\n}\n\nfunction pendingReviewLastPage(headers: GithubToolResponse['headers']): number {\n const link = headers?.link;\n if (typeof link !== 'string') return 1;\n const lastLink = link.split(',').find((part) => part.includes('rel=\"last\"'));\n if (lastLink === undefined) return 1;\n const match = PENDING_REVIEW_PAGE_PATTERN.exec(lastLink);\n const page = match?.[1] === undefined ? Number.NaN : Number.parseInt(match[1], 10);\n if (!Number.isSafeInteger(page) || page < 1) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pull request review pagination response was malformed',\n );\n }\n return page;\n}\n\nfunction latestPendingReviewOnPage(\n data: readonly unknown[],\n requiredIdentifier: PendingReviewIdentifier,\n): PendingReviewPageResult {\n let malformed = false;\n const appLogin = githubAppBotLogin().toLowerCase();\n for (let index = data.length - 1; index >= 0; index -= 1) {\n const review = data[index];\n if (!isRecord(review) || review.state !== 'PENDING') continue;\n const userLogin = isRecord(review.user) ? review.user.login : undefined;\n if (typeof userLogin !== 'string' || userLogin.trim().length === 0) {\n malformed = true;\n continue;\n }\n if (userLogin.trim().toLowerCase() !== appLogin) continue;\n const id =\n typeof review.id === 'number' && Number.isSafeInteger(review.id) && review.id > 0\n ? review.id\n : undefined;\n const nodeId =\n typeof review.node_id === 'string' && review.node_id.trim().length > 0\n ? review.node_id.trim()\n : undefined;\n const reference = {id, nodeId};\n if (reference[requiredIdentifier] !== undefined) return {malformed, review: reference};\n malformed = true;\n }\n\n return {malformed};\n}\n\nfunction githubAppBotLogin(): string {\n const configuredUsername = config.GITHUB_APP_USERNAME?.trim() || config.GITHUB_APP_SLUG.trim();\n return configuredUsername.toLowerCase().endsWith(GITHUB_APP_BOT_SUFFIX)\n ? configuredUsername\n : `${configuredUsername}${GITHUB_APP_BOT_SUFFIX}`;\n}\n\nfunction githubToolResult(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): GithubToolCallResult {\n const structuredContent = projectGithubToolOutput(toolId, data, response, parameters, route);\n if (structuredContent === undefined) {\n return githubToolError(\n 'GitHub artifact download did not return a download URL',\n 'malformed-provider-response',\n );\n }\n return {\n content: [{type: 'text', text: JSON.stringify(structuredContent)}],\n structuredContent,\n };\n}\n\nfunction projectGithubToolOutput(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): Record<string, unknown> | undefined {\n if (route === GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return projectGithubArtifactDownloadOutput(response, parameters);\n }\n\n switch (toolId) {\n case 'list_issue_types':\n return {issue_types: data};\n case 'list_issues':\n return {issues: data};\n case 'search_issues':\n return {issues: githubSearchItems(data)};\n case 'list_pull_requests':\n return {pull_requests: data};\n case 'search_pull_requests':\n return {pull_requests: githubSearchItems(data)};\n case 'create_pull_request':\n case 'update_pull_request':\n return {pull_request: data};\n case 'merge_pull_request':\n return {merge: data};\n default:\n return isRecord(data) ? data : {result: data};\n }\n}\n\nfunction projectGithubArtifactDownloadOutput(\n response: GithubToolResponse | undefined,\n parameters: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (response === undefined) return undefined;\n const downloadUrl = response.headers?.location;\n if (typeof downloadUrl !== 'string' || downloadUrl.length === 0) return undefined;\n\n const output: Record<string, unknown> = {\n archive_format: GITHUB_ARTIFACT_ARCHIVE_FORMAT,\n download_url: downloadUrl,\n };\n if (typeof parameters?.resource_id === 'string') output.artifact_id = parameters.resource_id;\n\n const contentType = response.headers?.['content-type'];\n if (typeof contentType === 'string') output.content_type = contentType;\n\n const contentLength = response.headers?.['content-length'];\n const sizeBytes = typeof contentLength === 'number' ? contentLength : Number(contentLength);\n if (Number.isSafeInteger(sizeBytes) && sizeBytes >= 0) output.size_bytes = sizeBytes;\n\n return output;\n}\n\nfunction githubSearchItems(data: unknown): unknown {\n return isRecord(data) ? data.items : data;\n}\n\nfunction githubToolError(message: string, code: GithubToolErrorCode): GithubToolCallResult {\n return {\n isError: true,\n content: [{type: 'text', text: message}],\n structuredContent: {code},\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validateGithubToolArguments(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n arguments_: Record<string, unknown>,\n): string | undefined {\n const required = Array.isArray(tool.inputSchema.required) ? tool.inputSchema.required : [];\n for (const name of required) {\n if (typeof name === 'string' && arguments_[name] === undefined) {\n return `Missing required parameter: ${name}`;\n }\n }\n\n const methodRequired = methodRequiredParameters(tool.inputSchema, arguments_);\n for (const name of methodRequired) {\n if (arguments_[name] === undefined) return `Missing required parameter: ${name}`;\n }\n\n const properties = tool.inputSchema.properties;\n if (typeof properties !== 'object' || properties === null || Array.isArray(properties)) {\n return undefined;\n }\n const propertySchemas = properties as Record<string, unknown>;\n for (const [name, value] of Object.entries(arguments_)) {\n const schema = propertySchemas[name];\n if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) continue;\n const type = (schema as {type?: unknown}).type;\n if (type === 'integer' && (!Number.isInteger(value) || typeof value !== 'number')) {\n return `Parameter ${name} must be an integer`;\n }\n if (type === 'array' && !Array.isArray(value)) return `Parameter ${name} must be an array`;\n }\n return undefined;\n}\n\nfunction methodRequiredParameters(\n inputSchema: AgentToolCatalogEntry<GithubAgentToolRequiredScope>['inputSchema'],\n arguments_: Record<string, unknown>,\n): string[] {\n const method = arguments_.method;\n if (typeof method !== 'string' || !Array.isArray(inputSchema.oneOf)) return [];\n\n for (const candidate of inputSchema.oneOf) {\n if (!isRecord(candidate) || !isRecord(candidate.properties)) continue;\n const methodSchema = candidate.properties.method;\n if (!isRecord(methodSchema) || methodSchema.const !== method) continue;\n return Array.isArray(candidate.required)\n ? candidate.required.filter((name): name is string => typeof name === 'string')\n : [];\n }\n\n return [];\n}\n\nfunction hasGrantedPermissions(\n granted: Record<string, 'read' | 'write' | 'admin'>,\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): boolean {\n const method = typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n const required =\n tool.methods?.find((candidate) => candidate.id === method)?.requiredScope ?? tool.requiredScope;\n return required.every(({permission, access}) => {\n const actual = granted[permission];\n return actual === 'write' || actual === 'admin' || actual === access;\n });\n}\n"],"names":["Octokit","mapGithubError","createGithubInstallationTokenProvider","config","normalizedGithubApiBaseUrl","GithubIntegrationProviderError","githubAgentToolCatalog","githubAgentToolSelectionCatalog","buildGithubAgentToolSelectionCatalog","DEFAULT_JOB_LOG_TAIL_LINES","GITHUB_GRAPHQL_ROUTE","GITHUB_ARTIFACT_ARCHIVE_FORMAT","GITHUB_ARTIFACT_DOWNLOAD_ROUTE","GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS","GITHUB_APP_BOT_SUFFIX","PENDING_REVIEW_PAGE_SIZE","PENDING_REVIEW_MAX_PAGE_REQUESTS","PENDING_REVIEW_LOOKUP_TIMEOUT_MS","PENDING_REVIEW_PAGE_TIMEOUT_MS","PENDING_REVIEW_PAGE_PATTERN","NO_PENDING_REVIEW_MESSAGE","ADD_PENDING_REVIEW_COMMENT_MUTATION","GithubAgentToolsProvider","options","tokenProvider","catalog","selectionCatalog","openSession","input","installation","getInstallationByConnectionId","connection","id","installationId","Number","isSafeInteger","tokenPromise","call","tool","tools","find","candidate","toolId","githubToolError","operation","resolveGithubOperation","undefined","validationError","validateGithubToolArguments","arguments","getInstallationAccessToken","token","hasGrantedPermissions","permissions","client","createClient","createOctokitClient","method","kind","data","addCommentToPendingReview","parameters","githubToolResult","operationParameters","resolvePendingReviewParameters","response","request","route","octokit","auth","baseUrl","retry","enabled","abortController","AbortController","timeout","setTimeout","abort","redirect","parseSuccessResponseBody","signal","clearTimeout","graphql","query","variables","args","params","methods","some","githubOperationRoute","projectGithubOperationParameters","owner","repo","issue","pull","run","resource","repoPath","comment_id","reaction","body","review","latestPendingReview","nodeId","pullRequestReviewId","path","subjectType","subject_type","line","side","start_line","startLine","start_side","startSide","content","headers","accept","isPendingReviewOperation","review_id","requiredIdentifier","lookupController","lookupTimeout","latestPendingReviewBeforeDeadline","lookupSignal","firstPage","requestPendingReviewPage","lastPage","pendingReviewLastPage","requests","malformed","page","Array","isArray","result","latestPendingReviewOnPage","pageController","abortPage","aborted","addEventListener","once","pageTimeout","pull_number","per_page","removeEventListener","link","lastLink","split","part","includes","match","exec","NaN","parseInt","appLogin","githubAppBotLogin","toLowerCase","index","length","isRecord","state","userLogin","user","login","trim","node_id","reference","configuredUsername","GITHUB_APP_USERNAME","GITHUB_APP_SLUG","endsWith","structuredContent","projectGithubToolOutput","type","text","JSON","stringify","projectGithubArtifactDownloadOutput","issue_types","issues","githubSearchItems","pull_requests","pull_request","merge","downloadUrl","location","output","archive_format","download_url","resource_id","artifact_id","contentType","content_type","contentLength","sizeBytes","size_bytes","items","message","code","isError","value","arguments_","required","inputSchema","name","methodRequired","methodRequiredParameters","properties","propertySchemas","Object","entries","schema","isInteger","oneOf","methodSchema","const","filter","granted","requiredScope","every","permission","access","actual"],"mappings":"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"}
@@ -6,6 +6,7 @@ export declare function recordInstallationTokenMint(params: {
6
6
  outcome: 'success' | 'failure';
7
7
  durationMs: number;
8
8
  }): void;
9
+ export declare function recordInstallationTokenFormat(token: string): void;
9
10
  export declare function recordInstallationTokenLockWait(durationMs: number): void;
10
11
  export declare function recordInstallationTokenBackoff(params: {
11
12
  reason: IntegrationProviderErrorReason;
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/metrics/instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,8BAA8B,EAAC,MAAM,8BAA8B,CAAC;AAEjF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qCAAqC,CAAC;AAIxE,MAAM,MAAM,oCAAoC,GAC5C,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,SAAS,GACT,gBAAgB,CAAC;AA8CrB,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,oCAAoC,GAAG,IAAI,CAEjG;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CAKP;AAED,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAExE;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,MAAM,EAAE,8BAA8B,CAAC;IACvC,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,IAAI,CAEP"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/metrics/instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,8BAA8B,EAAC,MAAM,8BAA8B,CAAC;AAEjF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qCAAqC,CAAC;AAKxE,MAAM,MAAM,oCAAoC,GAC5C,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,SAAS,GACT,gBAAgB,CAAC;AAqDrB,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,oCAAoC,GAAG,IAAI,CAEjG;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CAKP;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAOjE;AAED,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAExE;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,MAAM,EAAE,8BAA8B,CAAC;IACvC,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,IAAI,CAEP"}
@@ -1,4 +1,5 @@
1
1
  import { instanceMetrics } from '@shipfox/node-opentelemetry';
2
+ import { config } from '#config.js';
2
3
  const meter = instanceMetrics.getMeter('github');
3
4
  const installationTokenLookupCount = meter.createCounter('github_installation_token_lookup', {
4
5
  description: 'GitHub installation token cache lookups by serving outcome'
@@ -23,6 +24,9 @@ const installationTokenMintDuration = meter.createHistogram('github_installation
23
24
  ]
24
25
  }
25
26
  });
27
+ const installationTokenFormatCount = meter.createCounter('github_installation_token_format', {
28
+ description: 'GitHub installation tokens observed by format and requested override'
29
+ });
26
30
  const installationTokenLockWaitDuration = meter.createHistogram('github_installation_token_lock_wait_duration', {
27
31
  description: 'GitHub installation token advisory lock acquire and hold duration',
28
32
  unit: 'ms',
@@ -65,11 +69,26 @@ export function recordInstallationTokenMint(params) {
65
69
  installationTokenMintDuration.record(params.durationMs);
66
70
  });
67
71
  }
72
+ export function recordInstallationTokenFormat(token) {
73
+ recordMetric(()=>{
74
+ installationTokenFormatCount.add(1, {
75
+ format: installationTokenFormat(token),
76
+ override: config.GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE ?? 'absent'
77
+ });
78
+ });
79
+ }
68
80
  export function recordInstallationTokenLockWait(durationMs) {
69
81
  recordMetric(()=>installationTokenLockWaitDuration.record(durationMs));
70
82
  }
71
83
  export function recordInstallationTokenBackoff(params) {
72
84
  recordMetric(()=>installationTokenBackoffCount.add(1, params));
73
85
  }
86
+ function installationTokenFormat(token) {
87
+ if (!token.startsWith('ghs_')) return 'unknown';
88
+ const dotCount = token.slice(4).split('.').length - 1;
89
+ if (dotCount === 2) return 'stateless';
90
+ if (dotCount === 0) return 'stateful';
91
+ return 'unknown';
92
+ }
74
93
 
75
94
  //# sourceMappingURL=instance.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/metrics/instance.ts"],"sourcesContent":["import type {IntegrationProviderErrorReason} from '@shipfox/api-integration-spi';\nimport {instanceMetrics} from '@shipfox/node-opentelemetry';\nimport type {MintErrorClass} from '#api/installation-token-envelope.js';\n\nconst meter = instanceMetrics.getMeter('github');\n\nexport type GithubInstallationTokenLookupOutcome =\n | 'ram-hit'\n | 'db-hit'\n | 'minted'\n | 'served-stale'\n | 'backoff'\n | 'contended-poll';\n\nconst installationTokenLookupCount = meter.createCounter<{\n outcome: GithubInstallationTokenLookupOutcome;\n}>('github_installation_token_lookup', {\n description: 'GitHub installation token cache lookups by serving outcome',\n});\n\nconst installationTokenMintCount = meter.createCounter<{outcome: 'success' | 'failure'}>(\n 'github_installation_token_mint',\n {description: 'GitHub installation token mint attempts by outcome'},\n);\n\nconst installationTokenMintDuration = meter.createHistogram<Record<string, never>>(\n 'github_installation_token_mint_duration',\n {\n description: 'GitHub installation token mint duration',\n unit: 'ms',\n advice: {explicitBucketBoundaries: [10, 50, 100, 250, 500, 1000, 2500, 5000, 10000]},\n },\n);\n\nconst installationTokenLockWaitDuration = meter.createHistogram<Record<string, never>>(\n 'github_installation_token_lock_wait_duration',\n {\n description: 'GitHub installation token advisory lock acquire and hold duration',\n unit: 'ms',\n advice: {explicitBucketBoundaries: [0, 1, 5, 10, 25, 50, 100, 250, 500, 1000, 5000]},\n },\n);\n\nconst installationTokenBackoffCount = meter.createCounter<{\n reason: IntegrationProviderErrorReason;\n class: MintErrorClass;\n}>('github_installation_token_backoff', {\n description: 'GitHub installation token mint backoff activations by reason and class',\n});\n\nfunction recordMetric(record: () => void): void {\n try {\n record();\n } catch {\n // Metrics must not affect GitHub provider outcomes.\n }\n}\n\nexport function recordInstallationTokenLookup(outcome: GithubInstallationTokenLookupOutcome): void {\n recordMetric(() => installationTokenLookupCount.add(1, {outcome}));\n}\n\nexport function recordInstallationTokenMint(params: {\n outcome: 'success' | 'failure';\n durationMs: number;\n}): void {\n recordMetric(() => {\n installationTokenMintCount.add(1, {outcome: params.outcome});\n installationTokenMintDuration.record(params.durationMs);\n });\n}\n\nexport function recordInstallationTokenLockWait(durationMs: number): void {\n recordMetric(() => installationTokenLockWaitDuration.record(durationMs));\n}\n\nexport function recordInstallationTokenBackoff(params: {\n reason: IntegrationProviderErrorReason;\n class: MintErrorClass;\n}): void {\n recordMetric(() => installationTokenBackoffCount.add(1, params));\n}\n"],"names":["instanceMetrics","meter","getMeter","installationTokenLookupCount","createCounter","description","installationTokenMintCount","installationTokenMintDuration","createHistogram","unit","advice","explicitBucketBoundaries","installationTokenLockWaitDuration","installationTokenBackoffCount","recordMetric","record","recordInstallationTokenLookup","outcome","add","recordInstallationTokenMint","params","durationMs","recordInstallationTokenLockWait","recordInstallationTokenBackoff"],"mappings":"AACA,SAAQA,eAAe,QAAO,8BAA8B;AAG5D,MAAMC,QAAQD,gBAAgBE,QAAQ,CAAC;AAUvC,MAAMC,+BAA+BF,MAAMG,aAAa,CAErD,oCAAoC;IACrCC,aAAa;AACf;AAEA,MAAMC,6BAA6BL,MAAMG,aAAa,CACpD,kCACA;IAACC,aAAa;AAAoD;AAGpE,MAAME,gCAAgCN,MAAMO,eAAe,CACzD,2CACA;IACEH,aAAa;IACbI,MAAM;IACNC,QAAQ;QAACC,0BAA0B;YAAC;YAAI;YAAI;YAAK;YAAK;YAAK;YAAM;YAAM;YAAM;SAAM;IAAA;AACrF;AAGF,MAAMC,oCAAoCX,MAAMO,eAAe,CAC7D,gDACA;IACEH,aAAa;IACbI,MAAM;IACNC,QAAQ;QAACC,0BAA0B;YAAC;YAAG;YAAG;YAAG;YAAI;YAAI;YAAI;YAAK;YAAK;YAAK;YAAM;SAAK;IAAA;AACrF;AAGF,MAAME,gCAAgCZ,MAAMG,aAAa,CAGtD,qCAAqC;IACtCC,aAAa;AACf;AAEA,SAASS,aAAaC,MAAkB;IACtC,IAAI;QACFA;IACF,EAAE,OAAM;IACN,oDAAoD;IACtD;AACF;AAEA,OAAO,SAASC,8BAA8BC,OAA6C;IACzFH,aAAa,IAAMX,6BAA6Be,GAAG,CAAC,GAAG;YAACD;QAAO;AACjE;AAEA,OAAO,SAASE,4BAA4BC,MAG3C;IACCN,aAAa;QACXR,2BAA2BY,GAAG,CAAC,GAAG;YAACD,SAASG,OAAOH,OAAO;QAAA;QAC1DV,8BAA8BQ,MAAM,CAACK,OAAOC,UAAU;IACxD;AACF;AAEA,OAAO,SAASC,gCAAgCD,UAAkB;IAChEP,aAAa,IAAMF,kCAAkCG,MAAM,CAACM;AAC9D;AAEA,OAAO,SAASE,+BAA+BH,MAG9C;IACCN,aAAa,IAAMD,8BAA8BK,GAAG,CAAC,GAAGE;AAC1D"}
1
+ {"version":3,"sources":["../../src/metrics/instance.ts"],"sourcesContent":["import type {IntegrationProviderErrorReason} from '@shipfox/api-integration-spi';\nimport {instanceMetrics} from '@shipfox/node-opentelemetry';\nimport type {MintErrorClass} from '#api/installation-token-envelope.js';\nimport {config} from '#config.js';\n\nconst meter = instanceMetrics.getMeter('github');\n\nexport type GithubInstallationTokenLookupOutcome =\n | 'ram-hit'\n | 'db-hit'\n | 'minted'\n | 'served-stale'\n | 'backoff'\n | 'contended-poll';\n\nconst installationTokenLookupCount = meter.createCounter<{\n outcome: GithubInstallationTokenLookupOutcome;\n}>('github_installation_token_lookup', {\n description: 'GitHub installation token cache lookups by serving outcome',\n});\n\nconst installationTokenMintCount = meter.createCounter<{outcome: 'success' | 'failure'}>(\n 'github_installation_token_mint',\n {description: 'GitHub installation token mint attempts by outcome'},\n);\n\nconst installationTokenMintDuration = meter.createHistogram<Record<string, never>>(\n 'github_installation_token_mint_duration',\n {\n description: 'GitHub installation token mint duration',\n unit: 'ms',\n advice: {explicitBucketBoundaries: [10, 50, 100, 250, 500, 1000, 2500, 5000, 10000]},\n },\n);\n\nconst installationTokenFormatCount = meter.createCounter<{\n format: 'stateless' | 'stateful' | 'unknown';\n override: 'enabled' | 'disabled' | 'absent';\n}>('github_installation_token_format', {\n description: 'GitHub installation tokens observed by format and requested override',\n});\n\nconst installationTokenLockWaitDuration = meter.createHistogram<Record<string, never>>(\n 'github_installation_token_lock_wait_duration',\n {\n description: 'GitHub installation token advisory lock acquire and hold duration',\n unit: 'ms',\n advice: {explicitBucketBoundaries: [0, 1, 5, 10, 25, 50, 100, 250, 500, 1000, 5000]},\n },\n);\n\nconst installationTokenBackoffCount = meter.createCounter<{\n reason: IntegrationProviderErrorReason;\n class: MintErrorClass;\n}>('github_installation_token_backoff', {\n description: 'GitHub installation token mint backoff activations by reason and class',\n});\n\nfunction recordMetric(record: () => void): void {\n try {\n record();\n } catch {\n // Metrics must not affect GitHub provider outcomes.\n }\n}\n\nexport function recordInstallationTokenLookup(outcome: GithubInstallationTokenLookupOutcome): void {\n recordMetric(() => installationTokenLookupCount.add(1, {outcome}));\n}\n\nexport function recordInstallationTokenMint(params: {\n outcome: 'success' | 'failure';\n durationMs: number;\n}): void {\n recordMetric(() => {\n installationTokenMintCount.add(1, {outcome: params.outcome});\n installationTokenMintDuration.record(params.durationMs);\n });\n}\n\nexport function recordInstallationTokenFormat(token: string): void {\n recordMetric(() => {\n installationTokenFormatCount.add(1, {\n format: installationTokenFormat(token),\n override: config.GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE ?? 'absent',\n });\n });\n}\n\nexport function recordInstallationTokenLockWait(durationMs: number): void {\n recordMetric(() => installationTokenLockWaitDuration.record(durationMs));\n}\n\nexport function recordInstallationTokenBackoff(params: {\n reason: IntegrationProviderErrorReason;\n class: MintErrorClass;\n}): void {\n recordMetric(() => installationTokenBackoffCount.add(1, params));\n}\n\nfunction installationTokenFormat(token: string): 'stateless' | 'stateful' | 'unknown' {\n if (!token.startsWith('ghs_')) return 'unknown';\n const dotCount = token.slice(4).split('.').length - 1;\n if (dotCount === 2) return 'stateless';\n if (dotCount === 0) return 'stateful';\n return 'unknown';\n}\n"],"names":["instanceMetrics","config","meter","getMeter","installationTokenLookupCount","createCounter","description","installationTokenMintCount","installationTokenMintDuration","createHistogram","unit","advice","explicitBucketBoundaries","installationTokenFormatCount","installationTokenLockWaitDuration","installationTokenBackoffCount","recordMetric","record","recordInstallationTokenLookup","outcome","add","recordInstallationTokenMint","params","durationMs","recordInstallationTokenFormat","token","format","installationTokenFormat","override","GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE","recordInstallationTokenLockWait","recordInstallationTokenBackoff","startsWith","dotCount","slice","split","length"],"mappings":"AACA,SAAQA,eAAe,QAAO,8BAA8B;AAE5D,SAAQC,MAAM,QAAO,aAAa;AAElC,MAAMC,QAAQF,gBAAgBG,QAAQ,CAAC;AAUvC,MAAMC,+BAA+BF,MAAMG,aAAa,CAErD,oCAAoC;IACrCC,aAAa;AACf;AAEA,MAAMC,6BAA6BL,MAAMG,aAAa,CACpD,kCACA;IAACC,aAAa;AAAoD;AAGpE,MAAME,gCAAgCN,MAAMO,eAAe,CACzD,2CACA;IACEH,aAAa;IACbI,MAAM;IACNC,QAAQ;QAACC,0BAA0B;YAAC;YAAI;YAAI;YAAK;YAAK;YAAK;YAAM;YAAM;YAAM;SAAM;IAAA;AACrF;AAGF,MAAMC,+BAA+BX,MAAMG,aAAa,CAGrD,oCAAoC;IACrCC,aAAa;AACf;AAEA,MAAMQ,oCAAoCZ,MAAMO,eAAe,CAC7D,gDACA;IACEH,aAAa;IACbI,MAAM;IACNC,QAAQ;QAACC,0BAA0B;YAAC;YAAG;YAAG;YAAG;YAAI;YAAI;YAAI;YAAK;YAAK;YAAK;YAAM;SAAK;IAAA;AACrF;AAGF,MAAMG,gCAAgCb,MAAMG,aAAa,CAGtD,qCAAqC;IACtCC,aAAa;AACf;AAEA,SAASU,aAAaC,MAAkB;IACtC,IAAI;QACFA;IACF,EAAE,OAAM;IACN,oDAAoD;IACtD;AACF;AAEA,OAAO,SAASC,8BAA8BC,OAA6C;IACzFH,aAAa,IAAMZ,6BAA6BgB,GAAG,CAAC,GAAG;YAACD;QAAO;AACjE;AAEA,OAAO,SAASE,4BAA4BC,MAG3C;IACCN,aAAa;QACXT,2BAA2Ba,GAAG,CAAC,GAAG;YAACD,SAASG,OAAOH,OAAO;QAAA;QAC1DX,8BAA8BS,MAAM,CAACK,OAAOC,UAAU;IACxD;AACF;AAEA,OAAO,SAASC,8BAA8BC,KAAa;IACzDT,aAAa;QACXH,6BAA6BO,GAAG,CAAC,GAAG;YAClCM,QAAQC,wBAAwBF;YAChCG,UAAU3B,OAAO4B,yCAAyC,IAAI;QAChE;IACF;AACF;AAEA,OAAO,SAASC,gCAAgCP,UAAkB;IAChEP,aAAa,IAAMF,kCAAkCG,MAAM,CAACM;AAC9D;AAEA,OAAO,SAASQ,+BAA+BT,MAG9C;IACCN,aAAa,IAAMD,8BAA8BK,GAAG,CAAC,GAAGE;AAC1D;AAEA,SAASK,wBAAwBF,KAAa;IAC5C,IAAI,CAACA,MAAMO,UAAU,CAAC,SAAS,OAAO;IACtC,MAAMC,WAAWR,MAAMS,KAAK,CAAC,GAAGC,KAAK,CAAC,KAAKC,MAAM,GAAG;IACpD,IAAIH,aAAa,GAAG,OAAO;IAC3B,IAAIA,aAAa,GAAG,OAAO;IAC3B,OAAO;AACT"}