From 1279dfc20cf9a4dc2b8eaf0b8b96000ea873edd3 Mon Sep 17 00:00:00 2001 From: Umberto Sgueglia Date: Fri, 7 Aug 2026 17:05:23 +0200 Subject: [PATCH] feat: centralize anthropic usage Signed-off-by: Umberto Sgueglia --- pnpm-lock.yaml | 42 ++++--- services/apps/packages_worker/package.json | 2 +- .../__tests__/reachabilityStage.test.ts | 14 +-- .../blast-radius/stages/cargo/intelCargo.ts | 4 +- .../src/blast-radius/stages/go/intelGo.ts | 4 +- .../blast-radius/stages/maven/intelMaven.ts | 4 +- .../src/blast-radius/stages/npm/intelNpm.ts | 4 +- .../blast-radius/stages/nuget/intelNuGet.ts | 4 +- .../blast-radius/stages/reachabilityStage.ts | 4 +- .../stages/rubygems/intelRubyGems.ts | 4 +- .../src/blast-radius/workflows.ts | 2 +- services/libs/anthropic-aws/package.json | 20 ++++ .../anthropic-aws/src/agent.ts} | 109 ++++++++++-------- .../libs/anthropic-aws/src/credentials.ts | 34 ++++++ services/libs/anthropic-aws/src/index.ts | 2 + services/libs/anthropic-aws/tsconfig.json | 4 + 16 files changed, 174 insertions(+), 83 deletions(-) create mode 100644 services/libs/anthropic-aws/package.json rename services/{apps/packages_worker/src/blast-radius/agent/runner.ts => libs/anthropic-aws/src/agent.ts} (50%) create mode 100644 services/libs/anthropic-aws/src/credentials.ts create mode 100644 services/libs/anthropic-aws/src/index.ts create mode 100644 services/libs/anthropic-aws/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 913979af01..44e00af382 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1297,12 +1297,12 @@ importers: services/apps/packages_worker: dependencies: - '@anthropic-ai/claude-agent-sdk': - specifier: ^0.3.216 - version: 0.3.217(@anthropic-ai/sdk@0.112.5(zod@4.3.6))(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(zod@4.3.6) '@aws-sdk/client-bedrock-runtime': specifier: ^3.572.0 version: 3.572.0 + '@crowd/anthropic-aws': + specifier: workspace:* + version: link:../../libs/anthropic-aws '@crowd/archetype-standard': specifier: workspace:* version: link:../../archetypes/standard @@ -2061,6 +2061,22 @@ importers: specifier: ^5.6.3 version: 5.6.3 + services/libs/anthropic-aws: + dependencies: + '@anthropic-ai/claude-agent-sdk': + specifier: ^0.3.216 + version: 0.3.217(@anthropic-ai/sdk@0.112.5(zod@4.3.6))(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(zod@4.3.6) + '@crowd/logging': + specifier: workspace:* + version: link:../logging + devDependencies: + '@types/node': + specifier: ^20.8.2 + version: 20.12.7 + typescript: + specifier: ^5.6.3 + version: 5.6.3 + services/libs/audit-logs: dependencies: '@crowd/data-access-layer': @@ -10953,8 +10969,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.572.0 - '@aws-sdk/client-sts': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0) + '@aws-sdk/client-sso-oidc': 3.572.0(@aws-sdk/client-sts@3.572.0) + '@aws-sdk/client-sts': 3.572.0 '@aws-sdk/core': 3.572.0 '@aws-sdk/credential-provider-node': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0)(@aws-sdk/client-sts@3.572.0) '@aws-sdk/middleware-host-header': 3.567.0 @@ -11148,11 +11164,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.572.0': + '@aws-sdk/client-sso-oidc@3.572.0(@aws-sdk/client-sts@3.572.0)': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0) + '@aws-sdk/client-sts': 3.572.0 '@aws-sdk/core': 3.572.0 '@aws-sdk/credential-provider-node': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0)(@aws-sdk/client-sts@3.572.0) '@aws-sdk/middleware-host-header': 3.567.0 @@ -11191,6 +11207,7 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.6.2 transitivePeerDependencies: + - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso@3.556.0': @@ -11366,11 +11383,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.572.0(@aws-sdk/client-sso-oidc@3.572.0)': + '@aws-sdk/client-sts@3.572.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.572.0 + '@aws-sdk/client-sso-oidc': 3.572.0(@aws-sdk/client-sts@3.572.0) '@aws-sdk/core': 3.572.0 '@aws-sdk/credential-provider-node': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0)(@aws-sdk/client-sts@3.572.0) '@aws-sdk/middleware-host-header': 3.567.0 @@ -11409,7 +11426,6 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.6.2 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - aws-crt '@aws-sdk/client-sts@3.985.0': @@ -11575,7 +11591,7 @@ snapshots: '@aws-sdk/credential-provider-ini@3.572.0(@aws-sdk/client-sso-oidc@3.572.0)(@aws-sdk/client-sts@3.572.0)': dependencies: - '@aws-sdk/client-sts': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0) + '@aws-sdk/client-sts': 3.572.0 '@aws-sdk/credential-provider-env': 3.568.0 '@aws-sdk/credential-provider-process': 3.572.0 '@aws-sdk/credential-provider-sso': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0) @@ -11752,7 +11768,7 @@ snapshots: '@aws-sdk/credential-provider-web-identity@3.568.0(@aws-sdk/client-sts@3.572.0)': dependencies: - '@aws-sdk/client-sts': 3.572.0(@aws-sdk/client-sso-oidc@3.572.0) + '@aws-sdk/client-sts': 3.572.0 '@aws-sdk/types': 3.567.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 @@ -12064,7 +12080,7 @@ snapshots: '@aws-sdk/token-providers@3.572.0(@aws-sdk/client-sso-oidc@3.572.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.572.0 + '@aws-sdk/client-sso-oidc': 3.572.0(@aws-sdk/client-sts@3.572.0) '@aws-sdk/types': 3.567.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 diff --git a/services/apps/packages_worker/package.json b/services/apps/packages_worker/package.json index abad411c0a..73fa9912f0 100644 --- a/services/apps/packages_worker/package.json +++ b/services/apps/packages_worker/package.json @@ -84,13 +84,13 @@ "@crowd/archetype-standard": "workspace:*", "@crowd/archetype-worker": "workspace:*", "@crowd/temporal": "workspace:*", + "@crowd/anthropic-aws": "workspace:*", "@crowd/common": "workspace:*", "@crowd/data-access-layer": "workspace:*", "@crowd/database": "workspace:*", "@crowd/logging": "workspace:*", "@crowd/slack": "workspace:*", "@crowd/types": "workspace:*", - "@anthropic-ai/claude-agent-sdk": "^0.3.216", "@aws-sdk/client-bedrock-runtime": "^3.572.0", "@dsnp/parquetjs": "^1.7.0", "@google-cloud/bigquery": "^8.3.1", diff --git a/services/apps/packages_worker/src/blast-radius/stages/__tests__/reachabilityStage.test.ts b/services/apps/packages_worker/src/blast-radius/stages/__tests__/reachabilityStage.test.ts index ea92507c30..63900eee3a 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/__tests__/reachabilityStage.test.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/__tests__/reachabilityStage.test.ts @@ -1,8 +1,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' -import { runAnalysisAgent } from '../../agent/runner' import { ReachabilitySourceConfig, runReachabilityStage } from '../reachabilityStage' vi.mock('@crowd/data-access-layer/src/packages/blastRadius', () => ({ @@ -15,7 +15,7 @@ vi.mock('@crowd/data-access-layer/src/packages/blastRadius', () => ({ completeStageRun: vi.fn(), failStageRun: vi.fn(), })) -vi.mock('../../agent/runner', () => ({ runAnalysisAgent: vi.fn() })) +vi.mock('@crowd/anthropic-aws', () => ({ runClaudeAgentQuery: vi.fn() })) const qx = {} as never const SYMBOL_SPEC_ROW = { @@ -71,7 +71,7 @@ describe('runReachabilityStage', () => { qx, expect.objectContaining({ dependentId: 'dep-1', reasoning: 'no source', model: null }), ) - expect(runAnalysisAgent).not.toHaveBeenCalled() + expect(runClaudeAgentQuery).not.toHaveBeenCalled() }) it('persists an error verdict with downloadErrorPrefix when download throws', async () => { @@ -89,11 +89,11 @@ describe('runReachabilityStage', () => { reasoning: 'download failed: boom', }), ) - expect(runAnalysisAgent).not.toHaveBeenCalled() + expect(runClaudeAgentQuery).not.toHaveBeenCalled() }) it('persists the mapped verdict on a successful agent run', async () => { - vi.mocked(runAnalysisAgent).mockResolvedValue({ + vi.mocked(runClaudeAgentQuery).mockResolvedValue({ structuredOutput: { uses_package: true, imports_vulnerable_symbol: true, @@ -127,7 +127,7 @@ describe('runReachabilityStage', () => { }) it('retries on agent error and succeeds on a later attempt', async () => { - vi.mocked(runAnalysisAgent) + vi.mocked(runClaudeAgentQuery) .mockResolvedValueOnce({ structuredOutput: null, isError: true, @@ -153,7 +153,7 @@ describe('runReachabilityStage', () => { await runReachabilityStage(qx, 'analysis-1', makeConfig()) - expect(runAnalysisAgent).toHaveBeenCalledTimes(2) + expect(runClaudeAgentQuery).toHaveBeenCalledTimes(2) expect(blastRadiusDal.upsertVerdict).toHaveBeenCalledWith( qx, expect.objectContaining({ dependentId: 'dep-1', reachableVerdict: 'not_affected' }), diff --git a/services/apps/packages_worker/src/blast-radius/stages/cargo/intelCargo.ts b/services/apps/packages_worker/src/blast-radius/stages/cargo/intelCargo.ts index d4b338589f..acbb8325af 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/cargo/intelCargo.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/cargo/intelCargo.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as os from 'os' import * as path from 'path' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' import { getVersionNumbers } from '@crowd/data-access-layer/src/packages/blastRadiusDependents' import { findPackageId } from '@crowd/data-access-layer/src/packages/osv' @@ -12,7 +13,6 @@ import { CARGO_INTEL_SYSTEM_PROMPT, buildCargoIntelPrompt, } from '../../agent/cargoPrompts' -import { runAnalysisAgent } from '../../agent/runner' import { fetchPatch } from '../../clients/githubPatch' import { downloadAndExtractTarball } from '../../clients/npmTarball' import { @@ -135,7 +135,7 @@ export async function runIntelStageCargo( patches, ) - const agentResult = await runAnalysisAgent({ + const agentResult = await runClaudeAgentQuery({ prompt: agentPrompt, systemPrompt: CARGO_INTEL_SYSTEM_PROMPT, cwd: pkgsrcDir, diff --git a/services/apps/packages_worker/src/blast-radius/stages/go/intelGo.ts b/services/apps/packages_worker/src/blast-radius/stages/go/intelGo.ts index c5de0c9e5e..d89b6ae017 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/go/intelGo.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/go/intelGo.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as os from 'os' import * as path from 'path' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' import { getVersionNumbers } from '@crowd/data-access-layer/src/packages/blastRadiusDependents' import { findPackageId } from '@crowd/data-access-layer/src/packages/osv' @@ -9,7 +10,6 @@ import { QueryExecutor } from '@crowd/data-access-layer/src/queryExecutor' import { fetchVersionList } from '../../../go/proxyClient' import { GO_INTEL_SCHEMA, GO_INTEL_SYSTEM_PROMPT, buildGoIntelPrompt } from '../../agent/goPrompts' -import { runAnalysisAgent } from '../../agent/runner' import { fetchPatch } from '../../clients/githubPatch' import { downloadAndExtractGoModule } from '../../clients/goModuleZip' import { @@ -125,7 +125,7 @@ export async function runIntelStageGo( patches, ) - const agentResult = await runAnalysisAgent({ + const agentResult = await runClaudeAgentQuery({ prompt: agentPrompt, systemPrompt: GO_INTEL_SYSTEM_PROMPT, cwd: pkgsrcDir, diff --git a/services/apps/packages_worker/src/blast-radius/stages/maven/intelMaven.ts b/services/apps/packages_worker/src/blast-radius/stages/maven/intelMaven.ts index 60a3450324..2a8fe1456c 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/maven/intelMaven.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/maven/intelMaven.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as os from 'os' import * as path from 'path' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' import { getVersionNumbers } from '@crowd/data-access-layer/src/packages/blastRadiusDependents' import { findPackageId } from '@crowd/data-access-layer/src/packages/osv' @@ -14,7 +15,6 @@ import { MAVEN_INTEL_SYSTEM_PROMPT, buildMavenIntelPrompt, } from '../../agent/mavenPrompts' -import { runAnalysisAgent } from '../../agent/runner' import { fetchPatch } from '../../clients/githubPatch' import { downloadAndExtractMavenSources } from '../../clients/mavenSourcesJar' import { @@ -141,7 +141,7 @@ export async function runIntelStageMaven( patches, ) - const agentResult = await runAnalysisAgent({ + const agentResult = await runClaudeAgentQuery({ prompt: agentPrompt, systemPrompt: MAVEN_INTEL_SYSTEM_PROMPT, cwd: pkgsrcDir, diff --git a/services/apps/packages_worker/src/blast-radius/stages/npm/intelNpm.ts b/services/apps/packages_worker/src/blast-radius/stages/npm/intelNpm.ts index 53b3e3f054..efd88b1ad8 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/npm/intelNpm.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/npm/intelNpm.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as os from 'os' import * as path from 'path' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' import { findPackageId } from '@crowd/data-access-layer/src/packages/osv' import { QueryExecutor } from '@crowd/data-access-layer/src/queryExecutor' @@ -10,7 +11,6 @@ import { fetchPackument } from '../../../npm/fetchPackument' import { parseNpmName } from '../../../npm/normalize' import { isFetchError } from '../../../npm/types' import { INTEL_SCHEMA, INTEL_SYSTEM_PROMPT, buildIntelPrompt } from '../../agent/prompts' -import { runAnalysisAgent } from '../../agent/runner' import { fetchPatch } from '../../clients/githubPatch' import { downloadAndExtractTarball } from '../../clients/npmTarball' import { @@ -124,7 +124,7 @@ export async function runIntelStageNpm( patches, ) - const agentResult = await runAnalysisAgent({ + const agentResult = await runClaudeAgentQuery({ prompt: agentPrompt, systemPrompt: INTEL_SYSTEM_PROMPT, cwd: pkgsrcDir, diff --git a/services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts b/services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts index 33b2be5ba4..056dd9f1e6 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as os from 'os' import * as path from 'path' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' import { getVersionNumbers } from '@crowd/data-access-layer/src/packages/blastRadiusDependents' import { findPackageId } from '@crowd/data-access-layer/src/packages/osv' @@ -14,7 +15,6 @@ import { NUGET_INTEL_SYSTEM_PROMPT, buildNuGetIntelPrompt, } from '../../agent/nugetPrompts' -import { runAnalysisAgent } from '../../agent/runner' import { fetchPatch } from '../../clients/githubPatch' import { downloadAndExtractNuGetSource } from '../../clients/nugetSource' import { @@ -135,7 +135,7 @@ export async function runIntelStageNuGet( patches, ) - const agentResult = await runAnalysisAgent({ + const agentResult = await runClaudeAgentQuery({ prompt: agentPrompt, systemPrompt: NUGET_INTEL_SYSTEM_PROMPT, cwd: pkgsrcDir, diff --git a/services/apps/packages_worker/src/blast-radius/stages/reachabilityStage.ts b/services/apps/packages_worker/src/blast-radius/stages/reachabilityStage.ts index 6c50b98c07..f35fe07d77 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/reachabilityStage.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/reachabilityStage.ts @@ -3,12 +3,12 @@ import * as os from 'os' import * as path from 'path' import { promisify } from 'util' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' import { QueryExecutor } from '@crowd/data-access-layer/src/queryExecutor' import { toPromptSymbolSpec } from '../agent/promptKit' import { SymbolSpec } from '../agent/prompts' -import { runAnalysisAgent } from '../agent/runner' import { forEachWithConcurrency } from '../dependentsScan' const mkdtemp = promisify(fs.mkdtemp) @@ -139,7 +139,7 @@ export async function runReachabilityStage( try { const systemPrompt = cfg.buildSystemPrompt(spec) - const agentResult = await runAnalysisAgent({ + const agentResult = await runClaudeAgentQuery({ prompt: cfg.prompt, systemPrompt, cwd: depDir, diff --git a/services/apps/packages_worker/src/blast-radius/stages/rubygems/intelRubyGems.ts b/services/apps/packages_worker/src/blast-radius/stages/rubygems/intelRubyGems.ts index 919d7298df..73d4ac8b43 100644 --- a/services/apps/packages_worker/src/blast-radius/stages/rubygems/intelRubyGems.ts +++ b/services/apps/packages_worker/src/blast-radius/stages/rubygems/intelRubyGems.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as os from 'os' import * as path from 'path' +import { runClaudeAgentQuery } from '@crowd/anthropic-aws' import * as blastRadiusDal from '@crowd/data-access-layer/src/packages/blastRadius' import { getVersionNumbers } from '@crowd/data-access-layer/src/packages/blastRadiusDependents' import { findPackageId } from '@crowd/data-access-layer/src/packages/osv' @@ -14,7 +15,6 @@ import { RUBYGEMS_INTEL_SYSTEM_PROMPT, buildRubyGemsIntelPrompt, } from '../../agent/rubygemsPrompts' -import { runAnalysisAgent } from '../../agent/runner' import { fetchPatch } from '../../clients/githubPatch' import { affectedEntriesForEcosystem, @@ -136,7 +136,7 @@ export async function runIntelStageRubyGems( patches, ) - const agentResult = await runAnalysisAgent({ + const agentResult = await runClaudeAgentQuery({ prompt: agentPrompt, systemPrompt: RUBYGEMS_INTEL_SYSTEM_PROMPT, cwd: pkgsrcDir, diff --git a/services/apps/packages_worker/src/blast-radius/workflows.ts b/services/apps/packages_worker/src/blast-radius/workflows.ts index 1748310c4f..9543050f7b 100644 --- a/services/apps/packages_worker/src/blast-radius/workflows.ts +++ b/services/apps/packages_worker/src/blast-radius/workflows.ts @@ -11,7 +11,7 @@ const { blastRadiusStart, blastRadiusFail } = proxyActivities }) // Intel runs an Opus agent over the downloaded package source (up to 15 turns, -// 10-minute agent timeout in runAnalysisAgent) — give it headroom past that. +// 10-minute agent timeout in runClaudeAgentQuery) — give it headroom past that. const { blastRadiusIntel } = proxyActivities({ startToCloseTimeout: '20 minutes', heartbeatTimeout: '5 minutes', diff --git a/services/libs/anthropic-aws/package.json b/services/libs/anthropic-aws/package.json new file mode 100644 index 0000000000..711909d2dd --- /dev/null +++ b/services/libs/anthropic-aws/package.json @@ -0,0 +1,20 @@ +{ + "name": "@crowd/anthropic-aws", + "private": true, + "main": "src/index.ts", + "type": "commonjs", + "scripts": { + "lint": "npx eslint --ext .ts src --max-warnings=0", + "format": "npx prettier --write \"src/**/*.ts\"", + "format-check": "npx prettier --check .", + "tsc-check": "tsc --noEmit" + }, + "devDependencies": { + "@types/node": "^20.8.2", + "typescript": "^5.6.3" + }, + "dependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.3.216", + "@crowd/logging": "workspace:*" + } +} diff --git a/services/apps/packages_worker/src/blast-radius/agent/runner.ts b/services/libs/anthropic-aws/src/agent.ts similarity index 50% rename from services/apps/packages_worker/src/blast-radius/agent/runner.ts rename to services/libs/anthropic-aws/src/agent.ts index 9aad7470ae..a9d845c79b 100644 --- a/services/apps/packages_worker/src/blast-radius/agent/runner.ts +++ b/services/libs/anthropic-aws/src/agent.ts @@ -1,10 +1,8 @@ -// @anthropic-ai/claude-agent-sdk ships ESM-only; packages_worker compiles to -// CommonJS, so it must be loaded via dynamic import rather than a static one. -// Agent runner wrapping Claude Agent SDK with read-only tool restrictions, -// API key fallback, structured output, and timeout support. import { getServiceChildLogger } from '@crowd/logging' -const log = getServiceChildLogger('blast-radius-agent-runner') +import { getAnthropicAwsAgentSdkEnv } from './credentials' + +const log = getServiceChildLogger('anthropic-aws-agent') export interface AgentRunResult { structuredOutput: Record | null @@ -14,7 +12,7 @@ export interface AgentRunResult { costUsd: number } -export interface RunAnalysisAgentInput { +export interface RunClaudeAgentQueryInput { prompt: string systemPrompt: string cwd: string @@ -22,13 +20,43 @@ export interface RunAnalysisAgentInput { schema: Record maxTurns?: number timeoutMs?: number - // Called on every streamed message (i.e. at least once per agent turn), so a - // caller can heartbeat a Temporal activity during a single up-to-timeoutMs - // agent call rather than only once the whole call returns. + // Read-only tools auto-allowed instead of bypassing permissions entirely. + // CLI refuses --dangerously-skip-permissions as root (container standard). + allowedTools?: string[] + disallowedTools?: string[] + // Called on every message to allow Temporal heartbeat without waiting for run completion. + // Enables activity to signal liveness during up-to-timeoutMs agent call. onProgress?: () => void } -export async function runAnalysisAgent(input: RunAnalysisAgentInput): Promise { +const DEFAULT_ALLOWED_TOOLS = ['Read', 'Grep', 'Glob'] +const DEFAULT_DISALLOWED_TOOLS = [ + 'Bash', + 'Write', + 'Edit', + 'NotebookEdit', + 'WebFetch', + 'WebSearch', + 'Task', +] + +function getErrorMessage( + isError: boolean, + result: string | undefined, + structuredOutput: Record | null, +): string { + if (isError) return result || 'Unknown error' + if (!structuredOutput) + return `Agent completed without structured output: ${result || 'no result text'}` + return '' +} + +// @anthropic-ai/claude-agent-sdk ships ESM-only; callers may compile to +// CommonJS, so dynamic import is required. Wraps SDK with read-only tool restrictions, +// AWS auth fallback, structured output, and timeout support. +export async function runClaudeAgentQuery( + input: RunClaudeAgentQueryInput, +): Promise { const { prompt, systemPrompt, @@ -37,34 +65,25 @@ export async function runAnalysisAgent(input: RunAnalysisAgentInput): Promise controller.abort(), timeoutMs) @@ -77,13 +96,9 @@ export async function runAnalysisAgent(input: RunAnalysisAgentInput): Promise { + return { + CLAUDE_CODE_USE_ANTHROPIC_AWS: '1', + ANTHROPIC_AWS_WORKSPACE_ID: credentials.workspaceId, + AWS_REGION: credentials.region, + ANTHROPIC_AWS_API_KEY: credentials.apiKey, + } +} diff --git a/services/libs/anthropic-aws/src/index.ts b/services/libs/anthropic-aws/src/index.ts new file mode 100644 index 0000000000..0f70577f51 --- /dev/null +++ b/services/libs/anthropic-aws/src/index.ts @@ -0,0 +1,2 @@ +export * from './credentials' +export * from './agent' diff --git a/services/libs/anthropic-aws/tsconfig.json b/services/libs/anthropic-aws/tsconfig.json new file mode 100644 index 0000000000..bf7f183850 --- /dev/null +++ b/services/libs/anthropic-aws/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../base.tsconfig.json", + "include": ["src/**/*"] +}