Skip to content

Commit 8773744

Browse files
feat(handlers): carry ABCA solution UA on every SDK client; trace per invocation
All ~60 client instantiations now pass abcaUserAgent() and are wrapped withAbcaTrace() (document clients instrument the inner DynamoDBClient — shared middleware stack). Handler entry points set the trace to their freshly minted ulid() request id; orchestrate-task uses the task id. Stream consumers (fanout, approval-metrics) carry no trace by design. No behavior change beyond the UA headers; all 2031 existing tests pass unmodified (withAbcaTrace no-ops on bare-object constructor mocks). Task 4 of PR #338 plan. Part of #319 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 74c45a2 commit 8773744

46 files changed

Lines changed: 131 additions & 66 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cdk/src/handlers/approve-task.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ import { logger } from './shared/logger';
2727
import { formatMinuteBucket } from './shared/rate-limit';
2828
import { ErrorCode, errorResponse, successResponse } from './shared/response';
2929
import type { ApprovalRequest, ApprovalResponse, ApprovalScope } from './shared/types';
30+
import { abcaUserAgent, setAbcaTrace, withAbcaTrace } from './shared/ua';
3031

31-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
32+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
3233
const TASK_TABLE_NAME = process.env.TASK_TABLE_NAME;
3334
const TASK_APPROVALS_TABLE_NAME = process.env.TASK_APPROVALS_TABLE_NAME;
3435
const EVENTS_TABLE_NAME = process.env.TASK_EVENTS_TABLE_NAME;
@@ -65,6 +66,7 @@ const AUDIT_EVENT_RETENTION_DAYS = Number(process.env.TASK_RETENTION_DAYS ?? '90
6566
*/
6667
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
6768
const requestId = ulid();
69+
setAbcaTrace(requestId);
6870

6971
try {
7072
// 1. Auth

cdk/src/handlers/cancel-task.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ import { extractUserId } from './shared/gateway';
2828
import { logger } from './shared/logger';
2929
import { ErrorCode, errorResponse, successResponse } from './shared/response';
3030
import type { TaskRecord } from './shared/types';
31+
import { abcaUserAgent, setAbcaTrace, withAbcaTrace } from './shared/ua';
3132
import { computeTtlEpoch } from './shared/validation';
3233

33-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
34-
const agentCoreClient = new BedrockAgentCoreClient({});
35-
const ecsClient = new ECSClient({});
34+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
35+
const agentCoreClient = withAbcaTrace(new BedrockAgentCoreClient(abcaUserAgent()));
36+
const ecsClient = withAbcaTrace(new ECSClient(abcaUserAgent()));
3637
const TABLE_NAME = process.env.TASK_TABLE_NAME!;
3738
const EVENTS_TABLE_NAME = process.env.TASK_EVENTS_TABLE_NAME!;
3839
const TASK_RETENTION_DAYS = Number(process.env.TASK_RETENTION_DAYS ?? '90');
@@ -44,6 +45,7 @@ const ECS_CLUSTER_ARN = process.env.ECS_CLUSTER_ARN;
4445
*/
4546
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
4647
const requestId = ulid();
48+
setAbcaTrace(requestId);
4749

4850
try {
4951
// 1. Extract authenticated user

cdk/src/handlers/cleanup-pending-uploads.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ import { DeleteObjectsCommand, ListObjectVersionsCommand, S3Client } from '@aws-
4444
import { ulid } from 'ulid';
4545
import { ATTACHMENT_OBJECT_KEY_PREFIX } from '../constructs/attachments-bucket';
4646
import { logger } from './shared/logger';
47+
import { abcaUserAgent, withAbcaTrace } from './shared/ua';
4748

48-
const ddb = new DynamoDBClient({});
49-
const s3 = new S3Client({});
49+
const ddb = withAbcaTrace(new DynamoDBClient(abcaUserAgent()));
50+
const s3 = withAbcaTrace(new S3Client(abcaUserAgent()));
5051

5152
const TASK_TABLE = process.env.TASK_TABLE_NAME!;
5253
const EVENTS_TABLE = process.env.TASK_EVENTS_TABLE_NAME!;

cdk/src/handlers/confirm-uploads.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ import { estimateImageTokensFromBuffer } from './shared/image-tokens';
3535
import { logger } from './shared/logger';
3636
import { ErrorCode, errorResponse, successResponse } from './shared/response';
3737
import { type AttachmentRecord, createAttachmentRecord, type TaskRecord, toTaskDetail } from './shared/types';
38+
import { abcaUserAgent, setAbcaTrace, withAbcaTrace } from './shared/ua';
3839
import { computeTtlEpoch } from './shared/validation';
3940

40-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
41-
const s3Client = new S3Client({});
42-
const lambdaClient = process.env.ORCHESTRATOR_FUNCTION_ARN ? new LambdaClient({}) : undefined;
41+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
42+
const s3Client = withAbcaTrace(new S3Client(abcaUserAgent()));
43+
const lambdaClient = process.env.ORCHESTRATOR_FUNCTION_ARN ? withAbcaTrace(new LambdaClient(abcaUserAgent())) : undefined;
4344

4445
const TABLE_NAME = process.env.TASK_TABLE_NAME!;
4546
const EVENTS_TABLE_NAME = process.env.TASK_EVENTS_TABLE_NAME!;
@@ -80,6 +81,7 @@ interface S3ObjectMeta {
8081
*/
8182
export async function handler(event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> {
8283
const requestId = ulid();
84+
setAbcaTrace(requestId);
8385

8486
try {
8587
// 1. Auth
@@ -697,7 +699,7 @@ async function buildScreeningConfig(): Promise<ScreeningConfig | undefined> {
697699
if (!process.env.GUARDRAIL_ID || !process.env.GUARDRAIL_VERSION) return undefined;
698700
if (!_bedrockClient) {
699701
const { BedrockRuntimeClient } = await import('@aws-sdk/client-bedrock-runtime');
700-
_bedrockClient = new BedrockRuntimeClient({});
702+
_bedrockClient = withAbcaTrace(new BedrockRuntimeClient(abcaUserAgent()));
701703
}
702704
return {
703705
guardrailId: process.env.GUARDRAIL_ID,

cdk/src/handlers/create-task.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ import { buildChannelMetadata, extractUserId } from './shared/gateway';
2424
import { logger } from './shared/logger';
2525
import { ErrorCode, errorResponse } from './shared/response';
2626
import type { CreateTaskRequest } from './shared/types';
27+
import { setAbcaTrace } from './shared/ua';
2728
import { parseBody } from './shared/validation';
2829

2930
/**
3031
* POST /v1/tasks — Create a new task (Cognito-authenticated).
3132
*/
3233
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
3334
const requestId = ulid();
35+
setAbcaTrace(requestId);
3436

3537
try {
3638
// 1. Extract authenticated user

cdk/src/handlers/create-webhook.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ import { extractUserId } from './shared/gateway';
2727
import { logger } from './shared/logger';
2828
import { ErrorCode, errorResponse, successResponse } from './shared/response';
2929
import type { CreateWebhookRequest, CreateWebhookResponse, WebhookRecord } from './shared/types';
30+
import { abcaUserAgent, setAbcaTrace, withAbcaTrace } from './shared/ua';
3031
import { isValidWebhookName, parseBody } from './shared/validation';
3132

32-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
33-
const sm = new SecretsManagerClient({});
33+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
34+
const sm = withAbcaTrace(new SecretsManagerClient(abcaUserAgent()));
3435
const TABLE_NAME = process.env.WEBHOOK_TABLE_NAME!;
3536
const SECRET_PREFIX = 'bgagent/webhook/';
3637

@@ -39,6 +40,7 @@ const SECRET_PREFIX = 'bgagent/webhook/';
3940
*/
4041
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
4142
const requestId = ulid();
43+
setAbcaTrace(requestId);
4244

4345
try {
4446
const userId = extractUserId(event);

cdk/src/handlers/delete-webhook.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import { extractUserId } from './shared/gateway';
2626
import { logger } from './shared/logger';
2727
import { ErrorCode, errorResponse, successResponse } from './shared/response';
2828
import { type WebhookRecord, toWebhookDetail } from './shared/types';
29+
import { abcaUserAgent, setAbcaTrace, withAbcaTrace } from './shared/ua';
2930
import { computeTtlEpoch } from './shared/validation';
3031

31-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
32-
const sm = new SecretsManagerClient({});
32+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
33+
const sm = withAbcaTrace(new SecretsManagerClient(abcaUserAgent()));
3334
const TABLE_NAME = process.env.WEBHOOK_TABLE_NAME!;
3435
const SECRET_PREFIX = 'bgagent/webhook/';
3536
const WEBHOOK_RETENTION_DAYS = Number(process.env.WEBHOOK_RETENTION_DAYS ?? '30');
@@ -39,6 +40,7 @@ const WEBHOOK_RETENTION_DAYS = Number(process.env.WEBHOOK_RETENTION_DAYS ?? '30'
3940
*/
4041
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
4142
const requestId = ulid();
43+
setAbcaTrace(requestId);
4244

4345
try {
4446
const userId = extractUserId(event);

cdk/src/handlers/deny-task.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ import { logger } from './shared/logger';
2727
import { formatMinuteBucket } from './shared/rate-limit';
2828
import { ErrorCode, errorResponse, successResponse } from './shared/response';
2929
import { DENY_REASON_MAX_LENGTH, type DenyRequest, type DenyResponse } from './shared/types';
30+
import { abcaUserAgent, setAbcaTrace, withAbcaTrace } from './shared/ua';
3031

31-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
32+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
3233
const TASK_TABLE_NAME = process.env.TASK_TABLE_NAME;
3334
const TASK_APPROVALS_TABLE_NAME = process.env.TASK_APPROVALS_TABLE_NAME;
3435
const EVENTS_TABLE_NAME = process.env.TASK_EVENTS_TABLE_NAME;
@@ -63,6 +64,7 @@ const AUDIT_EVENT_RETENTION_DAYS = Number(process.env.TASK_RETENTION_DAYS ?? '90
6364
*/
6465
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
6566
const requestId = ulid();
67+
setAbcaTrace(requestId);
6668

6769
try {
6870
// 1. Auth

cdk/src/handlers/fanout-task-events.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { logger } from './shared/logger';
5353
import { coerceNumericOrNull } from './shared/numeric';
5454
import { loadRepoConfig } from './shared/repo-config';
5555
import type { ChannelConfig, TaskNotificationsConfig, TaskRecord } from './shared/types';
56+
import { abcaUserAgent, withAbcaTrace } from './shared/ua';
5657
import { dispatchSlackEvent, SlackApiError } from './slack-notify';
5758

5859
// Re-export the shared types so existing test imports (and any future
@@ -362,7 +363,7 @@ export function shouldFanOut(event: FanOutEvent, overrides?: TaskNotificationsCo
362363
* internally (the Slack API rejecting a message — e.g.
363364
* ``channel_not_found`` — is not recoverable by a Lambda retry).
364365
*/
365-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
366+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
366367

367368
/**
368369
* Slack dispatcher — hands the event to the in-module

cdk/src/handlers/get-pending.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ import { logger } from './shared/logger';
2626
import { formatMinuteBucket } from './shared/rate-limit';
2727
import { ErrorCode, errorResponse, successResponse } from './shared/response';
2828
import type { GetPendingResponse, PendingApprovalSummary, Severity } from './shared/types';
29+
import { abcaUserAgent, setAbcaTrace, withAbcaTrace } from './shared/ua';
2930

30-
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
31+
const ddb = DynamoDBDocumentClient.from(withAbcaTrace(new DynamoDBClient(abcaUserAgent())));
3132
const TASK_APPROVALS_TABLE_NAME = process.env.TASK_APPROVALS_TABLE_NAME;
3233
if (!TASK_APPROVALS_TABLE_NAME) {
3334
throw new Error('get-pending handler requires TASK_APPROVALS_TABLE_NAME env var');
@@ -54,6 +55,7 @@ const PENDING_LIST_LIMIT = 100;
5455
*/
5556
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
5657
const requestId = ulid();
58+
setAbcaTrace(requestId);
5759

5860
try {
5961
const userId = extractUserId(event);

0 commit comments

Comments
 (0)