diff --git a/agentclaw/app/agent_claw_main/main.py b/agentclaw/app/agent_claw_main/main.py index 9003dae..04ab812 100644 --- a/agentclaw/app/agent_claw_main/main.py +++ b/agentclaw/app/agent_claw_main/main.py @@ -140,8 +140,16 @@ def main(payload: dict, context) -> dict: region_name='us-east-1', ) - # Build system prompt (cached across warm invocations) - system_prompt = build_system_prompt() + # Build system prompt — base cached, user context injected per-invocation + user_profile = payload.get('user_profile', {}) + user_context = '' + if user_profile: + name = user_profile.get('display_name', '') + username = user_profile.get('telegram_username', '') + user_context = f'Name: {name}' + if username: + user_context += f'\nTelegram username: @{username}' + system_prompt = build_system_prompt(user_context=user_context) # Model: claude-sonnet-4-6 via cross-region inference model = BedrockModel( diff --git a/agentclaw/app/agent_claw_main/prompt_builder.py b/agentclaw/app/agent_claw_main/prompt_builder.py index 7d48673..6cbfab4 100644 --- a/agentclaw/app/agent_claw_main/prompt_builder.py +++ b/agentclaw/app/agent_claw_main/prompt_builder.py @@ -1,28 +1,35 @@ import os import boto3 -# Cache: built once per warm session -_system_prompt: str | None = None +# Cache: built once per warm session (shared base only) +_base_prompt: str | None = None -def build_system_prompt() -> str: - """Build system prompt from S3 workspace files (cached for warm session).""" - global _system_prompt - if _system_prompt is not None: - return _system_prompt +def build_system_prompt(user_context: str = '') -> str: + """Build system prompt from S3 workspace files + optional per-user context.""" + base = _get_base_prompt() + if user_context: + return base + f'\n\n---\n\n## User\n{user_context}' + return base + + +def _get_base_prompt() -> str: + global _base_prompt + if _base_prompt is not None: + return _base_prompt bucket = os.environ.get('WORKSPACE_BUCKET_NAME', '') or 'agent-claw-workspace-495395224548' print(f'[prompt_builder] Loading from bucket: {bucket!r}') if not bucket: print('[prompt_builder] WARNING: WORKSPACE_BUCKET_NAME not set!') - _system_prompt = 'You are a helpful personal assistant.' - return _system_prompt + _base_prompt = 'You are a helpful personal assistant.' + return _base_prompt s3 = boto3.client('s3') parts = [] - for fname in ['SOUL.md', 'AGENTS.md', 'IDENTITY.md', 'USER.md', 'MEMORY.md', 'TOOLS.md']: + for fname in ['SOUL.md', 'AGENTS.md', 'IDENTITY.md', 'TOOLS.md']: try: obj = s3.get_object(Bucket=bucket, Key=fname) content = obj['Body'].read().decode('utf-8') @@ -33,12 +40,12 @@ def build_system_prompt() -> str: parts.append('## Runtime\nRuntime: agent-claw | host=AgentCore | model=bedrock-claude-sonnet | channel=telegram\nCurrent date/time is provided by the system. Timezone: America/Chicago.') - _system_prompt = '\n\n---\n\n'.join(parts) - print(f'[prompt_builder] System prompt built: {len(_system_prompt)} chars') - return _system_prompt + _base_prompt = '\n\n---\n\n'.join(parts) + print(f'[prompt_builder] Base prompt built: {len(_base_prompt)} chars') + return _base_prompt def invalidate_prompt() -> None: """Force rebuild of system prompt on next invocation (call after workspace write).""" - global _system_prompt - _system_prompt = None + global _base_prompt + _base_prompt = None diff --git a/cdk/cdk.json b/cdk/cdk.json index 653a2fb..89598be 100644 --- a/cdk/cdk.json +++ b/cdk/cdk.json @@ -1,7 +1,5 @@ { - "app": { - "outdir": "cdk.out" - }, + "app": "npx ts-node --prefer-ts-exts bin/agent-claw.ts", "context": { "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, "@aws-cdk/core:stackRelativeExports": true diff --git a/cdk/cdk.out/.cache/7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387.zip b/cdk/cdk.out/.cache/7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387.zip new file mode 100644 index 0000000..df6512a Binary files /dev/null and b/cdk/cdk.out/.cache/7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387.zip differ diff --git a/cdk/cdk.out/.cache/8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875.zip b/cdk/cdk.out/.cache/8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875.zip new file mode 100644 index 0000000..7d4cec9 Binary files /dev/null and b/cdk/cdk.out/.cache/8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875.zip differ diff --git a/cdk/cdk.out/AgentClawStack.assets.json b/cdk/cdk.out/AgentClawStack.assets.json index cf75c69..361ae9b 100644 --- a/cdk/cdk.out/AgentClawStack.assets.json +++ b/cdk/cdk.out/AgentClawStack.assets.json @@ -1,91 +1,46 @@ { "version": "53.0.0", "files": { - "e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d": { - "displayName": "WorkspaceFiles/AwsCliLayer/Code", - "source": { - "path": "asset.e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip", - "packaging": "file" - }, - "destinations": { - "495395224548-us-east-1-b19c5879": { - "bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1", - "objectKey": "e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1" - } - } - }, - "3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9": { - "displayName": "Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code", - "source": { - "path": "asset.3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9", - "packaging": "zip" - }, - "destinations": { - "495395224548-us-east-1-12f29a1a": { - "bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1", - "objectKey": "3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9.zip", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1" - } - } - }, - "d5a4044422f3c0ab39b0d5bfa4e4ea2b1212f0d420a58b542fbc88917d7a676a": { - "displayName": "WorkspaceFiles/Asset1", - "source": { - "path": "asset.d5a4044422f3c0ab39b0d5bfa4e4ea2b1212f0d420a58b542fbc88917d7a676a", - "packaging": "zip" - }, - "destinations": { - "495395224548-us-east-1-2f513a77": { - "bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1", - "objectKey": "d5a4044422f3c0ab39b0d5bfa4e4ea2b1212f0d420a58b542fbc88917d7a676a.zip", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1" - } - } - }, - "9d7af346bbad17b4c228d09e33a602eedc03747fe1cec1c7c9b7c8723ce74e5d": { + "8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875": { "displayName": "TgIngest/Code", "source": { - "path": "asset.9d7af346bbad17b4c228d09e33a602eedc03747fe1cec1c7c9b7c8723ce74e5d", + "path": "asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875", "packaging": "zip" }, "destinations": { - "495395224548-us-east-1-e75a9fd4": { + "495395224548-us-east-1-0e5cdb5b": { "bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1", - "objectKey": "9d7af346bbad17b4c228d09e33a602eedc03747fe1cec1c7c9b7c8723ce74e5d.zip", + "objectKey": "8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1" } } }, - "eeef9ac2146cd644e1727e77104b58bed992e19379d5070de3a05714ff2dba48": { + "7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387": { "displayName": "AgentRunner/Code", "source": { - "path": "asset.eeef9ac2146cd644e1727e77104b58bed992e19379d5070de3a05714ff2dba48", + "path": "asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387", "packaging": "zip" }, "destinations": { - "495395224548-us-east-1-4a4b19df": { + "495395224548-us-east-1-63ace858": { "bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1", - "objectKey": "eeef9ac2146cd644e1727e77104b58bed992e19379d5070de3a05714ff2dba48.zip", + "objectKey": "7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1" } } }, - "d7e0fade0cb46eefc22ea1239ac2735f5c6d3cf3829571a1c221c37e986ed966": { + "2765094d543818b111d837ea62bad41260a47615c5b99bc608a58e99f24d5b85": { "displayName": "AgentClawStack Template", "source": { "path": "AgentClawStack.template.json", "packaging": "file" }, "destinations": { - "495395224548-us-east-1-2306706a": { + "495395224548-us-east-1-b10aaf8d": { "bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1", - "objectKey": "d7e0fade0cb46eefc22ea1239ac2735f5c6d3cf3829571a1c221c37e986ed966.json", + "objectKey": "2765094d543818b111d837ea62bad41260a47615c5b99bc608a58e99f24d5b85.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1" } diff --git a/cdk/cdk.out/AgentClawStack.metadata.json b/cdk/cdk.out/AgentClawStack.metadata.json index 2e49fe7..ebe94b5 100644 --- a/cdk/cdk.out/AgentClawStack.metadata.json +++ b/cdk/cdk.out/AgentClawStack.metadata.json @@ -3,21 +3,11 @@ { "type": "aws:cdk:creationStack", "data": [ - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], - "/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C": [ - { - "type": "aws:cdk:is-custom-resource-handler-singleton", - "data": true - }, - { - "type": "aws:cdk:is-custom-resource-handler-runtime-family", - "data": 2 - } - ], "/AgentClawStack/SessionStore": [ { "type": "aws:cdk:hasPhysicalName", @@ -26,6 +16,42 @@ } } ], + "/AgentClawStack/UsersTable": [ + { + "type": "aws:cdk:hasPhysicalName", + "data": { + "Ref": "UsersTable9725E9C8" + } + } + ], + "/AgentClawStack/WorkspaceMcpFunctionUrl": [ + { + "type": "aws:cdk:logicalId", + "data": "WorkspaceMcpFunctionUrl" + }, + { + "type": "aws:cdk:creationStack", + "data": [ + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:234:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." + ] + } + ], + "/AgentClawStack/GoogleCredentialsSecretArn": [ + { + "type": "aws:cdk:logicalId", + "data": "GoogleCredentialsSecretArn" + }, + { + "type": "aws:cdk:creationStack", + "data": [ + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:238:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." + ] + } + ], "/AgentClawStack/WebhookUrl": [ { "type": "aws:cdk:logicalId", @@ -34,9 +60,9 @@ { "type": "aws:cdk:creationStack", "data": [ - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:188:9)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:243:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -48,9 +74,9 @@ { "type": "aws:cdk:creationStack", "data": [ - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:192:9)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:248:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -62,9 +88,23 @@ { "type": "aws:cdk:creationStack", "data": [ - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:196:9)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:253:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." + ] + } + ], + "/AgentClawStack/UsersTableName": [ + { + "type": "aws:cdk:logicalId", + "data": "UsersTableName" + }, + { + "type": "aws:cdk:creationStack", + "data": [ + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:258:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -76,9 +116,9 @@ { "type": "aws:cdk:creationStack", "data": [ - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:200:9)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:263:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -90,9 +130,9 @@ { "type": "aws:cdk:creationStack", "data": [ - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:204:9)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:268:5)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -104,7 +144,7 @@ { "type": "aws:cdk:creationStack", "data": [ - "...aws-cdk-lib, node internals, source-map-support...", + "...aws-cdk-lib, node internals, @cspotcode/source-map-support...", "(no user code in 9007199254740991 frames, use --stack-trace-limit to capture more)" ] } @@ -117,41 +157,11 @@ { "type": "aws:cdk:creationStack", "data": [ - "...aws-cdk-lib, node internals, source-map-support...", + "...aws-cdk-lib, node internals, @cspotcode/source-map-support...", "(no user code in 9007199254740991 frames, use --stack-trace-limit to capture more)" ] } ], - "/AgentClawStack/WorkspaceBucket/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "WorkspaceBucket53E30B92" - }, - { - "type": "aws:cdk:creationStack", - "data": [ - "...new Bucket2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:69:15)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." - ] - } - ], - "/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536" - }, - { - "type": "aws:cdk:creationStack", - "data": [ - "...new BucketDeployment2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:77:13)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." - ] - } - ], "/AgentClawStack/SessionStore/Resource": [ { "type": "aws:cdk:logicalId", @@ -161,9 +171,24 @@ "type": "aws:cdk:creationStack", "data": [ "...new Table2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:83:30)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:60:26)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." + ] + } + ], + "/AgentClawStack/UsersTable/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "UsersTable9725E9C8" + }, + { + "type": "aws:cdk:creationStack", + "data": [ + "...new Table2 in aws-cdk-lib...", + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:69:24)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -176,9 +201,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new Queue2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:91:30)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:77:26)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -191,9 +216,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new Function2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:99:28)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:86:24)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -206,9 +231,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new Function2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:115:31)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:103:27)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -221,9 +246,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new HttpApi2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:147:25)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:141:21)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -236,9 +261,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new Role2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:160:30)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:159:26)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -250,56 +275,11 @@ { "type": "aws:cdk:creationStack", "data": [ - "...aws-cdk-lib, node internals, source-map-support...", + "...aws-cdk-lib, node internals, @cspotcode/source-map-support...", "(no user code in 9007199254740991 frames, use --stack-trace-limit to capture more)" ] } ], - "/AgentClawStack/WorkspaceFiles/AwsCliLayer/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "WorkspaceFilesAwsCliLayer50B6E9D8" - }, - { - "type": "aws:cdk:creationStack", - "data": [ - "...new BucketDeployment2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:77:13)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." - ] - } - ], - "/AgentClawStack/WorkspaceFiles/CustomResource/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "WorkspaceFilesCustomResourceA7FC771F" - }, - { - "type": "aws:cdk:creationStack", - "data": [ - "...new BucketDeployment2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:77:13)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." - ] - } - ], - "/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" - }, - { - "type": "aws:cdk:creationStack", - "data": [ - "...new BucketDeployment2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:77:13)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." - ] - } - ], "/AgentClawStack/TgIngest/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", @@ -309,9 +289,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new Function2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:99:28)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:86:24)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -324,9 +304,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new Function2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:115:31)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:103:27)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -339,9 +319,9 @@ "type": "aws:cdk:creationStack", "data": [ "...WrappedClass.addEventSource in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:142:23)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:135:19)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -354,9 +334,9 @@ "type": "aws:cdk:creationStack", "data": [ "...new HttpApi2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:147:25)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:141:21)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -371,9 +351,9 @@ ".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...", "Array.map (:)", "...WrappedClass. in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:150:17)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:145:13)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -388,9 +368,9 @@ ".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...", "Array.map (:)", "...WrappedClass. in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:150:17)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:145:13)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -403,26 +383,24 @@ "type": "aws:cdk:creationStack", "data": [ "...WrappedClass. in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:164:22)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:163:18)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], - "/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource": [ + "/AgentClawStack/WorkspaceMcpRole/Policy/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF" + "data": "WorkspaceMcpRolePolicy5B8B0072" }, { "type": "aws:cdk:creationStack", "data": [ - ".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.js:1:71 in aws-cdk-lib...", - "Array.map (:)", - "...new BucketDeployment2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:77:13)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "...SecretBase.grantRead in aws-cdk-lib...", + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:207:29)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -435,9 +413,9 @@ "type": "aws:cdk:creationStack", "data": [ "...WrappedClass.grantSendMessages in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:112:22)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:99:18)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -450,9 +428,9 @@ "type": "aws:cdk:creationStack", "data": [ "...WrappedClass.grantReadWriteData in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:131:22)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:120:18)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ], @@ -467,9 +445,9 @@ ".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...", "Array.map (:)", "...WrappedClass. in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:150:17)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)", - "...node internals..." + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:145:13)", + " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", + "...node internals, ts-node, ts-node, ts-node..." ] } ] diff --git a/cdk/cdk.out/AgentClawStack.template.json b/cdk/cdk.out/AgentClawStack.template.json index 05ccd2c..091ecfe 100644 --- a/cdk/cdk.out/AgentClawStack.template.json +++ b/cdk/cdk.out/AgentClawStack.template.json @@ -1,238 +1,6 @@ { "Description": "agent-claw: serverless personal assistant on AgentCore", "Resources": { - "WorkspaceBucket53E30B92": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "ServerSideEncryptionByDefault": { - "SSEAlgorithm": "AES256" - } - } - ] - }, - "BucketName": "agent-claw-workspace-495395224548", - "Tags": [ - { - "Key": "aws-cdk:cr-owned:254e75d0", - "Value": "true" - } - ] - }, - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain", - "Metadata": { - "aws:cdk:path": "AgentClawStack/WorkspaceBucket/Resource" - } - }, - "WorkspaceFilesAwsCliLayer50B6E9D8": { - "Type": "AWS::Lambda::LayerVersion", - "Properties": { - "Content": { - "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip" - }, - "Description": "/opt/awscli/aws" - }, - "Metadata": { - "aws:cdk:path": "AgentClawStack/WorkspaceFiles/AwsCliLayer/Resource", - "aws:asset:path": "asset.e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip", - "aws:asset:is-bundled": false, - "aws:asset:property": "Content" - } - }, - "WorkspaceFilesCustomResourceA7FC771F": { - "Type": "Custom::CDKBucketDeployment", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536", - "Arn" - ] - }, - "SourceBucketNames": [ - "cdk-hnb659fds-assets-495395224548-us-east-1" - ], - "SourceObjectKeys": [ - "d5a4044422f3c0ab39b0d5bfa4e4ea2b1212f0d420a58b542fbc88917d7a676a.zip" - ], - "DestinationBucketName": { - "Ref": "WorkspaceBucket53E30B92" - }, - "WaitForDistributionInvalidation": true, - "Prune": true, - "OutputObjectKeys": true - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete", - "Metadata": { - "aws:cdk:path": "AgentClawStack/WorkspaceFiles/CustomResource/Default" - } - }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - }, - "Metadata": { - "aws:cdk:path": "AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource" - } - }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":s3:::cdk-hnb659fds-assets-495395224548-us-east-1" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":s3:::cdk-hnb659fds-assets-495395224548-us-east-1/*" - ] - ] - } - ] - }, - { - "Action": [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - "s3:DeleteObject*", - "s3:PutObject", - "s3:PutObjectLegalHold", - "s3:PutObjectRetention", - "s3:PutObjectTagging", - "s3:PutObjectVersionTagging", - "s3:Abort*" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", - "Roles": [ - { - "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" - } - ] - }, - "Metadata": { - "aws:cdk:path": "AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource" - } - }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9.zip" - }, - "Environment": { - "Variables": { - "AWS_CA_BUNDLE": "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" - } - }, - "Handler": "index.handler", - "Layers": [ - { - "Ref": "WorkspaceFilesAwsCliLayer50B6E9D8" - } - ], - "Role": { - "Fn::GetAtt": [ - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265", - "Arn" - ] - }, - "Runtime": "python3.13", - "Timeout": 900 - }, - "DependsOn": [ - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" - ], - "Metadata": { - "aws:cdk:path": "AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource", - "aws:asset:path": "asset.3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9", - "aws:asset:is-bundled": false, - "aws:asset:property": "Code" - } - }, "SessionStore8C86EEFE": { "Type": "AWS::DynamoDB::Table", "Properties": { @@ -261,6 +29,30 @@ "aws:cdk:path": "AgentClawStack/SessionStore/Resource" } }, + "UsersTable9725E9C8": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "actor_id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST", + "KeySchema": [ + { + "AttributeName": "actor_id", + "KeyType": "HASH" + } + ], + "TableName": "agent-claw-users" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "aws:cdk:path": "AgentClawStack/UsersTable/Resource" + } + }, "MessageQueue7A3BF959": { "Type": "AWS::SQS::Queue", "Properties": { @@ -356,7 +148,7 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "9d7af346bbad17b4c228d09e33a602eedc03747fe1cec1c7c9b7c8723ce74e5d.zip" + "S3Key": "8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875.zip" }, "Environment": { "Variables": { @@ -385,7 +177,7 @@ ], "Metadata": { "aws:cdk:path": "AgentClawStack/TgIngest/Resource", - "aws:asset:path": "asset.9d7af346bbad17b4c228d09e33a602eedc03747fe1cec1c7c9b7c8723ce74e5d", + "aws:asset:path": "asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875", "aws:asset:is-bundled": false, "aws:asset:property": "Code" } @@ -467,6 +259,44 @@ } ] }, + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:Query", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:ConditionCheckItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "UsersTable9725E9C8", + "Arn" + ] + } + ] + }, + { + "Action": [ + "dynamodb:GetRecords", + "dynamodb:GetShardIterator" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "UsersTable9725E9C8", + "Arn" + ] + } + ] + }, { "Action": [ "s3:GetObject*", @@ -476,22 +306,26 @@ "Effect": "Allow", "Resource": [ { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::agent-claw-workspace-495395224548" + ] ] }, { "Fn::Join": [ "", [ + "arn:", { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" - ] + "Ref": "AWS::Partition" }, - "/*" + ":s3:::agent-claw-workspace-495395224548/*" ] ] } @@ -553,20 +387,22 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "eeef9ac2146cd644e1727e77104b58bed992e19379d5070de3a05714ff2dba48.zip" + "S3Key": "7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387.zip" }, "Environment": { "Variables": { "SESSION_TABLE_NAME": { "Ref": "SessionStore8C86EEFE" }, - "WORKSPACE_BUCKET_NAME": { - "Ref": "WorkspaceBucket53E30B92" - }, + "WORKSPACE_BUCKET_NAME": "agent-claw-workspace-495395224548", "TELEGRAM_BOT_TOKEN_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3", "BRAVE_API_KEY_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi", - "RUNTIME_1_ARN": "PLACEHOLDER_SET_AFTER_RUNTIME_DEPLOY", - "AWS_REGION_NAME": "us-east-1" + "RUNTIME_1_ARN": "arn:aws:bedrock-agentcore:us-east-1:495395224548:runtime/agentclaw_agent_claw_main-vTRGIEG6ON", + "AWS_REGION_NAME": "us-east-1", + "USERS_TABLE_NAME": { + "Ref": "UsersTable9725E9C8" + }, + "WORKSPACE_MCP_URL": "https://25hugrzw4uwtueeg77jsmft6lq0wunmd.lambda-url.us-east-1.on.aws/mcp" } }, "FunctionName": "agent-claw-agent-runner", @@ -587,7 +423,7 @@ ], "Metadata": { "aws:cdk:path": "AgentClawStack/AgentRunner/Resource", - "aws:asset:path": "asset.eeef9ac2146cd644e1727e77104b58bed992e19379d5070de3a05714ff2dba48", + "aws:asset:path": "asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387", "aws:asset:is-bundled": false, "aws:asset:property": "Code" } @@ -752,22 +588,26 @@ "Effect": "Allow", "Resource": [ { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::agent-claw-workspace-495395224548" + ] ] }, { "Fn::Join": [ "", [ + "arn:", { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" - ] + "Ref": "AWS::Partition" }, - "/*" + ":s3:::agent-claw-workspace-495395224548/*" ] ] } @@ -797,6 +637,66 @@ ], "Effect": "Allow", "Resource": "*" + }, + { + "Action": "lambda:InvokeFunctionUrl", + "Condition": { + "StringEquals": { + "lambda:FunctionUrlAuthType": "AWS_IAM" + } + }, + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp" + ] + ] + }, + "Sid": "WorkspaceMcpInvoke" + }, + { + "Action": [ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials-??????" + ] + ] + } + }, + { + "Action": [ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-??????" + ] + ] + } } ], "Version": "2012-10-17" @@ -812,10 +712,65 @@ "aws:cdk:path": "AgentClawStack/Runtime1Role/DefaultPolicy/Resource" } }, + "WorkspaceMcpRolePolicy5B8B0072": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials-??????" + ] + ] + } + }, + { + "Action": [ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-??????" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "WorkspaceMcpRolePolicy5B8B0072", + "Roles": [ + "agent-claw-workspace-mcp-role" + ] + }, + "Metadata": { + "aws:cdk:path": "AgentClawStack/WorkspaceMcpRole/Policy/Resource" + } + }, "CDKMetadata": { "Type": "AWS::CDK::Metadata", "Properties": { - "Analytics": "v2:deflate64:H4sIAAAAAAAA/22R0U7DMAxFv4X3LIxufMBWQCCBGC3itXJbr8qWJqV2VlVR/x0lZQMhnu7JvY4VO4lMbhO5vIKBFlV9XGhVSp8zVEeRIVnXVyhgoMLTSvqtq47IIt2bb5plC4SToFXha+y0HVs0LOfo7mIIIEImuQkyCQ1tWYP06d48w4j9B/akrBG5Mo1GtubBmYqDc4F0/2Pen9BwHp/3Al2nTBPi/90d9q2i0H0SClrpM6sxBFF3VqtqjHWRJlGPBlpbl9K/QzlXRpgEfZL0bw5dNCNMAjrVAOMA4ymR/pG523Qq5EHCMWdo4oUZgpVZxzM9Gcamh/OAf46xbjpvq9BhUwUMVGklNwOlWsXlibjU0D6O7Ihte/m90OYXvzruHE/C2Brlga5PyVrerOXy6kBKLXpnWLUos1m/AKsec0UeAgAA" + "Analytics": "v2:deflate64:H4sIAAAAAAAA/22PwU7DMAyGn2X31IxuPMCGQHBAjI775KZela1NSu1sqqK8O0rKOCBO/+ff+WO7hPKhhOUCr1zo5lx0poawF9RnVRE7P2pSeOVD4BVsvT6TbJFJNZPF3jU1hE+sO1KPR5shKv5iCB+efDYzRNVhXzcI4dlbLcbZ1PrlpwtZ2edRbzgMxrap/b+7o7E3zCl2y6d9ojLYQ6jcvErWneuMnnIoU1S8OiAzCcMmicLBtCh0xelSQngRGTaDSYEkqdwLtvnDGZJVOS8zvVqhdsTbOX/K/C5GlSel+M8B714GL1FZ1xCc+O5SruF+DcvFiY0pRm/F9ATVrN8RDS1cnQEAAA==" }, "Metadata": { "aws:cdk:path": "AgentClawStack/CDKMetadata/Default" @@ -823,6 +778,25 @@ } }, "Outputs": { + "WorkspaceMcpFunctionUrl": { + "Description": "workspace-mcp Lambda Function URL (MCP endpoint for Gmail/Calendar)", + "Value": "https://25hugrzw4uwtueeg77jsmft6lq0wunmd.lambda-url.us-east-1.on.aws" + }, + "GoogleCredentialsSecretArn": { + "Description": "Google OAuth user credentials secret ARN", + "Value": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials" + ] + ] + } + }, "WebhookUrl": { "Description": "Register this URL with Telegram BotFather as webhook endpoint", "Value": { @@ -844,9 +818,7 @@ }, "WorkspaceBucketName": { "Description": "S3 bucket containing agent workspace files", - "Value": { - "Ref": "WorkspaceBucket53E30B92" - } + "Value": "agent-claw-workspace-495395224548" }, "SessionTableName": { "Description": "DynamoDB table for session mapping", @@ -854,6 +826,12 @@ "Ref": "SessionStore8C86EEFE" } }, + "UsersTableName": { + "Description": "DynamoDB user registry table", + "Value": { + "Ref": "UsersTable9725E9C8" + } + }, "MessageQueueUrl": { "Description": "SQS FIFO queue for incoming messages", "Value": { diff --git a/cdk/cdk.out/asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387/handler.py b/cdk/cdk.out/asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387/handler.py new file mode 100644 index 0000000..56680d5 --- /dev/null +++ b/cdk/cdk.out/asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387/handler.py @@ -0,0 +1,151 @@ +import json +import os +import time +import uuid +import boto3 +from typing import Any + +# AWS clients +_ddb = None +_agentcore = None + + +def get_ddb(): + global _ddb + if _ddb is None: + _ddb = boto3.resource('dynamodb') + return _ddb + + +def get_agentcore(): + global _agentcore + if _agentcore is None: + _agentcore = boto3.client('bedrock-agentcore', region_name='us-east-1') + return _agentcore + + +def get_or_create_user(actor_id: str, from_info: dict) -> dict: + """Look up user in registry, auto-registering on first contact.""" + table_name = os.environ.get('USERS_TABLE_NAME', '') + if not table_name: + return {'actor_id': actor_id, 'display_name': from_info.get('from_name', actor_id)} + table = get_ddb().Table(table_name) + response = table.get_item(Key={'actor_id': actor_id}) + item = response.get('Item') + if item: + return item + now = int(time.time()) + item = { + 'actor_id': actor_id, + 'display_name': from_info.get('from_name') or actor_id, + 'telegram_username': from_info.get('from_username', ''), + 'created_at': str(now), + 'allowed': True, + } + table.put_item(Item=item) + print(f'[agent-runner] Registered new user: {actor_id}') + return item + + +def get_or_create_session(actor_id: str) -> str: + """Look up active session for actor, or create a new one.""" + table = get_ddb().Table(os.environ['SESSION_TABLE_NAME']) + + response = table.get_item(Key={'actor_id': actor_id}) + item = response.get('Item') + + now = int(time.time()) + ttl_8hr = now + (8 * 3600) + + if item and item.get('ttl', 0) > now: + # Active session exists — extend TTL + table.update_item( + Key={'actor_id': actor_id}, + UpdateExpression='SET #ttl = :ttl', + ExpressionAttributeNames={'#ttl': 'ttl'}, + ExpressionAttributeValues={':ttl': ttl_8hr}, + ) + return item['session_id'] + + # Create new session + session_id = str(uuid.uuid4()) + table.put_item(Item={ + 'actor_id': actor_id, + 'session_id': session_id, + 'created_at': str(now), + 'ttl': ttl_8hr, + }) + return session_id + + +def handler(event, context): + # ── Parse SQS records (FIFO — all from same actor) ─────────────────── + records = [] + for record in event.get('Records', []): + try: + records.append(json.loads(record['body'])) + except (json.JSONDecodeError, KeyError): + continue + + if not records: + return + + first = records[0] + channel = first.get('channel', 'telegram') + chat_id = first.get('chat_id', '') + actor_id = f"{channel}:{chat_id}" + + # ── User registry ───────────────────────────────────────────────────── + from_info = first.get('messages', [{}])[0] + user_profile = get_or_create_user(actor_id, from_info) + + # ── Get or create AgentCore session ────────────────────────────────── + session_id = get_or_create_session(actor_id) + print(f"[agent-runner] actor={actor_id} session={session_id} user={user_profile.get('display_name', '')}") + + # ── Bundle messages ─────────────────────────────────────────────────── + if len(records) == 1: + prompt = records[0]['messages'][0]['text'] + else: + lines = [ + f"[{i+1}] {r['messages'][0]['text']}" + for i, r in enumerate(records) + ] + prompt = f"You have {len(records)} queued messages:\n" + "\n".join(lines) + + # ── Build payload for AgentCore Runtime 1 ──────────────────────────── + payload: dict[str, Any] = { + 'prompt': prompt, + 'actor_id': actor_id, + 'session_id': session_id, + 'user_profile': { + 'display_name': user_profile.get('display_name', actor_id), + 'telegram_username': user_profile.get('telegram_username', ''), + 'allowed': user_profile.get('allowed', True), + }, + 'channel_adapter': { + 'type': channel, + 'target_id': str(chat_id), + 'bot_token_secret_arn': os.environ.get('TELEGRAM_BOT_TOKEN_SECRET_ARN', ''), + }, + } + + # ── Invoke AgentCore Runtime 1 ──────────────────────────────────────── + runtime_arn = os.environ.get('RUNTIME_1_ARN', '') + if not runtime_arn or runtime_arn == 'PLACEHOLDER_SET_AFTER_RUNTIME_DEPLOY': + print(f"[agent-runner] RUNTIME_1_ARN not set — skipping AgentCore invoke") + print(f"[agent-runner] Would have sent: {json.dumps(payload)[:200]}") + return + + client = get_agentcore() + response = client.invoke_agent_runtime( + agentRuntimeArn=runtime_arn, + runtimeSessionId=session_id, + payload=json.dumps(payload).encode(), + ) + + # Consume streaming response (agent delivers to Telegram via send_message tool) + for chunk in response.get('response', []): + pass # intentional no-op — agent handles delivery internally + + print(f"[agent-runner] Completed session={session_id} actor={actor_id}") diff --git a/cdk/cdk.out/asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387/requirements.txt b/cdk/cdk.out/asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387/requirements.txt new file mode 100644 index 0000000..011ba23 --- /dev/null +++ b/cdk/cdk.out/asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387/requirements.txt @@ -0,0 +1 @@ +boto3>=1.34.0 diff --git a/cdk/cdk.out/asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875/handler.py b/cdk/cdk.out/asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875/handler.py new file mode 100644 index 0000000..58ae946 --- /dev/null +++ b/cdk/cdk.out/asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875/handler.py @@ -0,0 +1,101 @@ +import json +import os +import threading +import urllib.request +import urllib.parse +import boto3 + +# Cache bot token (fetched once at Lambda init) +_bot_token: str | None = None +_token_lock = threading.Lock() + + +def get_bot_token() -> str: + global _bot_token + if _bot_token is None: + with _token_lock: + if _bot_token is None: + sm = boto3.client('secretsmanager') + _bot_token = sm.get_secret_value( + SecretId=os.environ['TELEGRAM_BOT_TOKEN_SECRET_ARN'] + )['SecretString'] + return _bot_token + + +def send_typing(chat_id: str) -> None: + """Fire-and-forget typing action (does not raise on failure).""" + try: + token = get_bot_token() + data = json.dumps({'chat_id': chat_id, 'action': 'typing'}).encode() + req = urllib.request.Request( + f'https://api.telegram.org/bot{token}/sendChatAction', + data=data, + headers={'Content-Type': 'application/json'}, + ) + urllib.request.urlopen(req, timeout=3) + except Exception: + pass # typing is best-effort + + +def handler(event, context): + # ── Validate Telegram webhook secret ────────────────────────────────── + expected_secret = os.environ.get('TELEGRAM_WEBHOOK_SECRET', '') + if expected_secret: + headers = event.get('headers') or {} + received = headers.get('x-telegram-bot-api-secret-token', '') + if received != expected_secret: + return {'statusCode': 403, 'body': 'Forbidden'} + + # ── Parse Telegram Update ───────────────────────────────────────────── + try: + body = json.loads(event.get('body', '{}')) + except json.JSONDecodeError: + print(f'[tg-ingest] Bad JSON body') + return {'statusCode': 400, 'body': 'Bad Request'} + + print(f'[tg-ingest] Update keys: {list(body.keys())}') + update_id = body.get('update_id') + + # Support regular messages and edited messages + message = body.get('message') or body.get('edited_message') + if not message: + print(f'[tg-ingest] No message field, update_type={list(body.keys())}') + return {'statusCode': 200, 'body': 'ok'} + + chat_id = str(message.get('chat', {}).get('id', '')) + text = message.get('text', '') + from_user = message.get('from', {}) + timestamp = message.get('date', 0) + + print(f'[tg-ingest] chat_id={chat_id} text_len={len(text)} update_id={update_id}') + + if not chat_id or not text: + print(f'[tg-ingest] Dropping: chat_id={chat_id!r} text={text!r}') + return {'statusCode': 200, 'body': 'ok'} + + # ── Send typing action (non-blocking, background thread) ────────────── + t = threading.Thread(target=send_typing, args=(chat_id,)) + t.daemon = True + t.start() + + # ── Enqueue to SQS FIFO ─────────────────────────────────────────────── + sqs = boto3.client('sqs') + sqs.send_message( + QueueUrl=os.environ['MESSAGE_QUEUE_URL'], + MessageGroupId=chat_id, + MessageDeduplicationId=str(update_id), + MessageBody=json.dumps({ + 'channel': 'telegram', + 'chat_id': chat_id, + 'messages': [{ + 'text': text, + 'from_id': str(from_user.get('id', '')), + 'from_username': from_user.get('username', ''), + 'from_name': f"{from_user.get('first_name', '')} {from_user.get('last_name', '')}".strip(), + }], + 'update_id': update_id, + 'timestamp': timestamp, + }), + ) + + return {'statusCode': 200, 'body': 'ok'} diff --git a/cdk/cdk.out/asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875/requirements.txt b/cdk/cdk.out/asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875/requirements.txt new file mode 100644 index 0000000..011ba23 --- /dev/null +++ b/cdk/cdk.out/asset.8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875/requirements.txt @@ -0,0 +1 @@ +boto3>=1.34.0 diff --git a/cdk/cdk.out/manifest.json b/cdk/cdk.out/manifest.json index 2aed6b9..3a205be 100644 --- a/cdk/cdk.out/manifest.json +++ b/cdk/cdk.out/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-deploy-role-495395224548-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-cfn-exec-role-495395224548-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-495395224548-us-east-1/d7e0fade0cb46eefc22ea1239ac2735f5c6d3cf3829571a1c221c37e986ed966.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-495395224548-us-east-1/2765094d543818b111d837ea62bad41260a47615c5b99bc608a58e99f24d5b85.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/cdk/cdk.out/tree.json b/cdk/cdk.out/tree.json index e3b0e9f..9d429ab 100644 --- a/cdk/cdk.out/tree.json +++ b/cdk/cdk.out/tree.json @@ -1 +1 @@ -{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"2.252.0"},"children":{"AgentClawStack":{"id":"AgentClawStack","path":"AgentClawStack","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"2.252.0"},"children":{"TelegramBotToken":{"id":"TelegramBotToken","path":"AgentClawStack/TelegramBotToken","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"BraveApiKey":{"id":"BraveApiKey","path":"AgentClawStack/BraveApiKey","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"WorkspaceBucket":{"id":"WorkspaceBucket","path":"AgentClawStack/WorkspaceBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.Bucket","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WorkspaceBucket/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.CfnBucket","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::S3::Bucket","aws:cdk:cloudformation:logicalId":"WorkspaceBucket53E30B92","aws:cdk:cloudformation:props":{"bucketEncryption":{"serverSideEncryptionConfiguration":[{"serverSideEncryptionByDefault":{"sseAlgorithm":"AES256"}}]},"bucketName":"agent-claw-workspace-495395224548","tags":[{"key":"aws-cdk:cr-owned:254e75d0","value":"true"}]}}}}},"WorkspaceFiles":{"id":"WorkspaceFiles","path":"AgentClawStack/WorkspaceFiles","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_deployment.BucketDeployment","version":"2.252.0"},"children":{"AwsCliLayer":{"id":"AwsCliLayer","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer","constructInfo":{"fqn":"aws-cdk-lib.lambda_layer_awscli.AwsCliLayer","version":"2.252.0"},"children":{"Code":{"id":"Code","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnLayerVersion","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::LayerVersion","aws:cdk:cloudformation:logicalId":"WorkspaceFilesAwsCliLayer50B6E9D8","aws:cdk:cloudformation:props":{"content":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip"},"description":"/opt/awscli/aws"}}}}},"CustomResourceHandler":{"id":"CustomResourceHandler","path":"AgentClawStack/WorkspaceFiles/CustomResourceHandler","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.SingletonFunction","version":"2.252.0"}},"Asset1":{"id":"Asset1","path":"AgentClawStack/WorkspaceFiles/Asset1","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/WorkspaceFiles/Asset1/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/WorkspaceFiles/Asset1/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"CustomResource":{"id":"CustomResource","path":"AgentClawStack/WorkspaceFiles/CustomResource","constructInfo":{"fqn":"aws-cdk-lib.CustomResource","version":"2.252.0"},"children":{"Default":{"id":"Default","path":"AgentClawStack/WorkspaceFiles/CustomResource/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"2.252.0"}}}}}},"Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C":{"id":"Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"}}],"Version":"2012-10-17"},"managedPolicyArns":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::cdk-hnb659fds-assets-495395224548-us-east-1"]]},{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::cdk-hnb659fds-assets-495395224548-us-east-1/*"]]}]},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*","s3:DeleteObject*","s3:PutObject","s3:PutObjectLegalHold","s3:PutObjectRetention","s3:PutObjectTagging","s3:PutObjectVersionTagging","s3:Abort*"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},{"Fn::Join":["",[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},"/*"]]}]}],"Version":"2012-10-17"},"policyName":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF","roles":[{"Ref":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9.zip"},"environment":{"variables":{"AWS_CA_BUNDLE":"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}},"handler":"index.handler","layers":[{"Ref":"WorkspaceFilesAwsCliLayer50B6E9D8"}],"role":{"Fn::GetAtt":["CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265","Arn"]},"runtime":"python3.13","timeout":900}}}}},"SessionStore":{"id":"SessionStore","path":"AgentClawStack/SessionStore","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.Table","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/SessionStore/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.CfnTable","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::DynamoDB::Table","aws:cdk:cloudformation:logicalId":"SessionStore8C86EEFE","aws:cdk:cloudformation:props":{"attributeDefinitions":[{"attributeName":"actor_id","attributeType":"S"}],"billingMode":"PAY_PER_REQUEST","keySchema":[{"attributeName":"actor_id","keyType":"HASH"}],"tableName":"agent-claw-sessions","timeToLiveSpecification":{"attributeName":"ttl","enabled":true}}}},"ScalingRole":{"id":"ScalingRole","path":"AgentClawStack/SessionStore/ScalingRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}}}},"MessageQueue":{"id":"MessageQueue","path":"AgentClawStack/MessageQueue","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.Queue","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/MessageQueue/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.CfnQueue","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SQS::Queue","aws:cdk:cloudformation:logicalId":"MessageQueue7A3BF959","aws:cdk:cloudformation:props":{"contentBasedDeduplication":false,"fifoQueue":true,"queueName":"agent-claw-messages.fifo","receiveMessageWaitTimeSeconds":20,"visibilityTimeout":900}}}}},"TgIngest":{"id":"TgIngest","path":"AgentClawStack/TgIngest","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/TgIngest/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleB96980B6","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"}}],"Version":"2012-10-17"},"managedPolicyArns":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleDefaultPolicyCC51E135","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["sqs:SendMessage","sqs:GetQueueAttributes","sqs:GetQueueUrl"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"}],"Version":"2012-10-17"},"policyName":"TgIngestServiceRoleDefaultPolicyCC51E135","roles":[{"Ref":"TgIngestServiceRoleB96980B6"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/TgIngest/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/TgIngest/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/TgIngest/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"TgIngest4CB35C2F","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"9d7af346bbad17b4c228d09e33a602eedc03747fe1cec1c7c9b7c8723ce74e5d.zip"},"environment":{"variables":{"MESSAGE_QUEUE_URL":{"Ref":"MessageQueue7A3BF959"},"TELEGRAM_BOT_TOKEN_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3","TELEGRAM_WEBHOOK_SECRET":""}},"functionName":"agent-claw-tg-ingest","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["TgIngestServiceRoleB96980B6","Arn"]},"runtime":"python3.12","timeout":10}}}}},"AgentRunner":{"id":"AgentRunner","path":"AgentClawStack/AgentRunner","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/AgentRunner/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRole40CA0A00","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"}}],"Version":"2012-10-17"},"managedPolicyArns":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},{"Fn::Join":["",[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},"/*"]]}]},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"},{"Action":["sqs:ReceiveMessage","sqs:ChangeMessageVisibility","sqs:GetQueueUrl","sqs:DeleteMessage","sqs:GetQueueAttributes"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":"bedrock-agentcore:InvokeAgentRuntime","Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"},"policyName":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","roles":[{"Ref":"AgentRunnerServiceRole40CA0A00"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/AgentRunner/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/AgentRunner/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/AgentRunner/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"AgentRunnerBDE3FA56","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"eeef9ac2146cd644e1727e77104b58bed992e19379d5070de3a05714ff2dba48.zip"},"environment":{"variables":{"SESSION_TABLE_NAME":{"Ref":"SessionStore8C86EEFE"},"WORKSPACE_BUCKET_NAME":{"Ref":"WorkspaceBucket53E30B92"},"TELEGRAM_BOT_TOKEN_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3","BRAVE_API_KEY_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi","RUNTIME_1_ARN":"PLACEHOLDER_SET_AFTER_RUNTIME_DEPLOY","AWS_REGION_NAME":"us-east-1"}},"functionName":"agent-claw-agent-runner","handler":"handler.handler","memorySize":256,"role":{"Fn::GetAtt":["AgentRunnerServiceRole40CA0A00","Arn"]},"runtime":"python3.12","timeout":900}}},"SqsEventSource:AgentClawStackMessageQueue9AF4DF23":{"id":"SqsEventSource:AgentClawStackMessageQueue9AF4DF23","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.EventSourceMapping","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnEventSourceMapping","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::EventSourceMapping","aws:cdk:cloudformation:logicalId":"AgentRunnerSqsEventSourceAgentClawStackMessageQueue9AF4DF234671B32B","aws:cdk:cloudformation:props":{"batchSize":10,"enabled":true,"eventSourceArn":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]},"functionName":{"Ref":"AgentRunnerBDE3FA56"}}}}}}}},"WebhookApi":{"id":"WebhookApi","path":"AgentClawStack/WebhookApi","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpApi","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnApi","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Api","aws:cdk:cloudformation:logicalId":"WebhookApi28122C53","aws:cdk:cloudformation:props":{"name":"agent-claw-webhook","protocolType":"HTTP"}}},"DefaultStage":{"id":"DefaultStage","path":"AgentClawStack/WebhookApi/DefaultStage","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpStage","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/DefaultStage/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnStage","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Stage","aws:cdk:cloudformation:logicalId":"WebhookApiDefaultStageC0BC9CA5","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"autoDeploy":true,"stageName":"$default"}}}}},"POST--telegram":{"id":"POST--telegram","path":"AgentClawStack/WebhookApi/POST--telegram","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"TgIngestIntegration":{"id":"TgIngestIntegration","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"payloadFormatVersion":"2.0"}}}}},"TgIngestIntegration-Permission":{"id":"TgIngestIntegration-Permission","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegrationPermissionFEBC2E3B","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/telegram"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramF7127CFF","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"POST /telegram","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85"}]]}}}}}}}},"Runtime1Role":{"id":"Runtime1Role","path":"AgentClawStack/Runtime1Role","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"Runtime1RoleA7A82078","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"bedrock-agentcore.amazonaws.com"}}],"Version":"2012-10-17"},"description":"Execution role for agent-claw Runtime 1 (main assistant)"}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Runtime1Role/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"Runtime1RoleDefaultPolicy1A3D5ACF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["bedrock:InvokeModel","bedrock:InvokeModelWithResponseStream"],"Effect":"Allow","Resource":"*"},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},{"Fn::Join":["",[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},"/*"]]}]},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"},{"Action":["bedrock-agentcore:CreateEvent","bedrock-agentcore:ListEvents","bedrock-agentcore:RetrieveMemoryRecords"],"Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"},"policyName":"Runtime1RoleDefaultPolicy1A3D5ACF","roles":[{"Ref":"Runtime1RoleA7A82078"}]}}}}}}},"WebhookUrl":{"id":"WebhookUrl","path":"AgentClawStack/WebhookUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"WorkspaceBucketName":{"id":"WorkspaceBucketName","path":"AgentClawStack/WorkspaceBucketName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"SessionTableName":{"id":"SessionTableName","path":"AgentClawStack/SessionTableName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"MessageQueueUrl":{"id":"MessageQueueUrl","path":"AgentClawStack/MessageQueueUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"Runtime1RoleArn":{"id":"Runtime1RoleArn","path":"AgentClawStack/Runtime1RoleArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"CDKMetadata":{"id":"CDKMetadata","path":"AgentClawStack/CDKMetadata","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"},"children":{"Default":{"id":"Default","path":"AgentClawStack/CDKMetadata/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"2.252.0"}}}},"BootstrapVersion":{"id":"BootstrapVersion","path":"AgentClawStack/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"2.252.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"AgentClawStack/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"2.252.0"}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"}}}}} \ No newline at end of file +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"2.252.0"},"children":{"AgentClawStack":{"id":"AgentClawStack","path":"AgentClawStack","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"2.252.0"},"children":{"TelegramBotToken":{"id":"TelegramBotToken","path":"AgentClawStack/TelegramBotToken","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"BraveApiKey":{"id":"BraveApiKey","path":"AgentClawStack/BraveApiKey","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"WorkspaceBucket":{"id":"WorkspaceBucket","path":"AgentClawStack/WorkspaceBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}},"SessionStore":{"id":"SessionStore","path":"AgentClawStack/SessionStore","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.Table","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/SessionStore/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.CfnTable","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::DynamoDB::Table","aws:cdk:cloudformation:logicalId":"SessionStore8C86EEFE","aws:cdk:cloudformation:props":{"attributeDefinitions":[{"attributeName":"actor_id","attributeType":"S"}],"billingMode":"PAY_PER_REQUEST","keySchema":[{"attributeName":"actor_id","keyType":"HASH"}],"tableName":"agent-claw-sessions","timeToLiveSpecification":{"attributeName":"ttl","enabled":true}}}},"ScalingRole":{"id":"ScalingRole","path":"AgentClawStack/SessionStore/ScalingRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}}}},"UsersTable":{"id":"UsersTable","path":"AgentClawStack/UsersTable","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.Table","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/UsersTable/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.CfnTable","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::DynamoDB::Table","aws:cdk:cloudformation:logicalId":"UsersTable9725E9C8","aws:cdk:cloudformation:props":{"attributeDefinitions":[{"attributeName":"actor_id","attributeType":"S"}],"billingMode":"PAY_PER_REQUEST","keySchema":[{"attributeName":"actor_id","keyType":"HASH"}],"tableName":"agent-claw-users"}}},"ScalingRole":{"id":"ScalingRole","path":"AgentClawStack/UsersTable/ScalingRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}}}},"MessageQueue":{"id":"MessageQueue","path":"AgentClawStack/MessageQueue","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.Queue","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/MessageQueue/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.CfnQueue","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SQS::Queue","aws:cdk:cloudformation:logicalId":"MessageQueue7A3BF959","aws:cdk:cloudformation:props":{"contentBasedDeduplication":false,"fifoQueue":true,"queueName":"agent-claw-messages.fifo","receiveMessageWaitTimeSeconds":20,"visibilityTimeout":900}}}}},"TgIngest":{"id":"TgIngest","path":"AgentClawStack/TgIngest","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/TgIngest/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleB96980B6","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"}}],"Version":"2012-10-17"},"managedPolicyArns":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleDefaultPolicyCC51E135","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["sqs:SendMessage","sqs:GetQueueAttributes","sqs:GetQueueUrl"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"}],"Version":"2012-10-17"},"policyName":"TgIngestServiceRoleDefaultPolicyCC51E135","roles":[{"Ref":"TgIngestServiceRoleB96980B6"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/TgIngest/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/TgIngest/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/TgIngest/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"TgIngest4CB35C2F","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"8da48fd743d1e2cb70d8d1935cee795b6f8cf02609db05e2b8f28449be9ef875.zip"},"environment":{"variables":{"MESSAGE_QUEUE_URL":{"Ref":"MessageQueue7A3BF959"},"TELEGRAM_BOT_TOKEN_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3","TELEGRAM_WEBHOOK_SECRET":""}},"functionName":"agent-claw-tg-ingest","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["TgIngestServiceRoleB96980B6","Arn"]},"runtime":"python3.12","timeout":10}}}}},"AgentRunner":{"id":"AgentRunner","path":"AgentClawStack/AgentRunner","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/AgentRunner/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRole40CA0A00","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"}}],"Version":"2012-10-17"},"managedPolicyArns":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548"]]},{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548/*"]]}]},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"},{"Action":["sqs:ReceiveMessage","sqs:ChangeMessageVisibility","sqs:GetQueueUrl","sqs:DeleteMessage","sqs:GetQueueAttributes"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":"bedrock-agentcore:InvokeAgentRuntime","Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"},"policyName":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","roles":[{"Ref":"AgentRunnerServiceRole40CA0A00"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/AgentRunner/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/AgentRunner/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/AgentRunner/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"AgentRunnerBDE3FA56","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387.zip"},"environment":{"variables":{"SESSION_TABLE_NAME":{"Ref":"SessionStore8C86EEFE"},"WORKSPACE_BUCKET_NAME":"agent-claw-workspace-495395224548","TELEGRAM_BOT_TOKEN_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3","BRAVE_API_KEY_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi","RUNTIME_1_ARN":"arn:aws:bedrock-agentcore:us-east-1:495395224548:runtime/agentclaw_agent_claw_main-vTRGIEG6ON","AWS_REGION_NAME":"us-east-1","USERS_TABLE_NAME":{"Ref":"UsersTable9725E9C8"},"WORKSPACE_MCP_URL":"https://25hugrzw4uwtueeg77jsmft6lq0wunmd.lambda-url.us-east-1.on.aws/mcp"}},"functionName":"agent-claw-agent-runner","handler":"handler.handler","memorySize":256,"role":{"Fn::GetAtt":["AgentRunnerServiceRole40CA0A00","Arn"]},"runtime":"python3.12","timeout":900}}},"SqsEventSource:AgentClawStackMessageQueue9AF4DF23":{"id":"SqsEventSource:AgentClawStackMessageQueue9AF4DF23","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.EventSourceMapping","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnEventSourceMapping","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::EventSourceMapping","aws:cdk:cloudformation:logicalId":"AgentRunnerSqsEventSourceAgentClawStackMessageQueue9AF4DF234671B32B","aws:cdk:cloudformation:props":{"batchSize":10,"enabled":true,"eventSourceArn":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]},"functionName":{"Ref":"AgentRunnerBDE3FA56"}}}}}}}},"WebhookApi":{"id":"WebhookApi","path":"AgentClawStack/WebhookApi","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpApi","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnApi","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Api","aws:cdk:cloudformation:logicalId":"WebhookApi28122C53","aws:cdk:cloudformation:props":{"name":"agent-claw-webhook","protocolType":"HTTP"}}},"DefaultStage":{"id":"DefaultStage","path":"AgentClawStack/WebhookApi/DefaultStage","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpStage","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/DefaultStage/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnStage","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Stage","aws:cdk:cloudformation:logicalId":"WebhookApiDefaultStageC0BC9CA5","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"autoDeploy":true,"stageName":"$default"}}}}},"POST--telegram":{"id":"POST--telegram","path":"AgentClawStack/WebhookApi/POST--telegram","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"TgIngestIntegration":{"id":"TgIngestIntegration","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"payloadFormatVersion":"2.0"}}}}},"TgIngestIntegration-Permission":{"id":"TgIngestIntegration-Permission","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegrationPermissionFEBC2E3B","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/telegram"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramF7127CFF","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"POST /telegram","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85"}]]}}}}}}}},"Runtime1Role":{"id":"Runtime1Role","path":"AgentClawStack/Runtime1Role","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"Runtime1RoleA7A82078","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"bedrock-agentcore.amazonaws.com"}}],"Version":"2012-10-17"},"description":"Execution role for agent-claw Runtime 1 (main assistant)"}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Runtime1Role/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"Runtime1RoleDefaultPolicy1A3D5ACF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["bedrock:InvokeModel","bedrock:InvokeModelWithResponseStream"],"Effect":"Allow","Resource":"*"},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548"]]},{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548/*"]]}]},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"},{"Action":["bedrock-agentcore:CreateEvent","bedrock-agentcore:ListEvents","bedrock-agentcore:RetrieveMemoryRecords"],"Effect":"Allow","Resource":"*"},{"Action":"lambda:InvokeFunctionUrl","Condition":{"StringEquals":{"lambda:FunctionUrlAuthType":"AWS_IAM"}},"Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp"]]},"Sid":"WorkspaceMcpInvoke"},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials-??????"]]}},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-??????"]]}}],"Version":"2012-10-17"},"policyName":"Runtime1RoleDefaultPolicy1A3D5ACF","roles":[{"Ref":"Runtime1RoleA7A82078"}]}}}}}}},"GoogleWorkspaceCredentials":{"id":"GoogleWorkspaceCredentials","path":"AgentClawStack/GoogleWorkspaceCredentials","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"GoogleOAuthClient":{"id":"GoogleOAuthClient","path":"AgentClawStack/GoogleOAuthClient","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"WorkspaceMcpRole":{"id":"WorkspaceMcpRole","path":"AgentClawStack/WorkspaceMcpRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"},"children":{"Policy":{"id":"Policy","path":"AgentClawStack/WorkspaceMcpRole/Policy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WorkspaceMcpRole/Policy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"WorkspaceMcpRolePolicy5B8B0072","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials-??????"]]}},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-??????"]]}}],"Version":"2012-10-17"},"policyName":"WorkspaceMcpRolePolicy5B8B0072","roles":["agent-claw-workspace-mcp-role"]}}}}}}},"WorkspaceMcp":{"id":"WorkspaceMcp","path":"AgentClawStack/WorkspaceMcp","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.FunctionBase","version":"2.252.0"}},"WorkspaceMcpFunctionUrl":{"id":"WorkspaceMcpFunctionUrl","path":"AgentClawStack/WorkspaceMcpFunctionUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"GoogleCredentialsSecretArn":{"id":"GoogleCredentialsSecretArn","path":"AgentClawStack/GoogleCredentialsSecretArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"WebhookUrl":{"id":"WebhookUrl","path":"AgentClawStack/WebhookUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"WorkspaceBucketName":{"id":"WorkspaceBucketName","path":"AgentClawStack/WorkspaceBucketName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"SessionTableName":{"id":"SessionTableName","path":"AgentClawStack/SessionTableName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"UsersTableName":{"id":"UsersTableName","path":"AgentClawStack/UsersTableName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"MessageQueueUrl":{"id":"MessageQueueUrl","path":"AgentClawStack/MessageQueueUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"Runtime1RoleArn":{"id":"Runtime1RoleArn","path":"AgentClawStack/Runtime1RoleArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"CDKMetadata":{"id":"CDKMetadata","path":"AgentClawStack/CDKMetadata","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"},"children":{"Default":{"id":"Default","path":"AgentClawStack/CDKMetadata/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"2.252.0"}}}},"BootstrapVersion":{"id":"BootstrapVersion","path":"AgentClawStack/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"2.252.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"AgentClawStack/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"2.252.0"}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"}}}}} \ No newline at end of file diff --git a/cdk/lib/agent-claw-stack.ts b/cdk/lib/agent-claw-stack.ts index 16319e5..d7ddb36 100644 --- a/cdk/lib/agent-claw-stack.ts +++ b/cdk/lib/agent-claw-stack.ts @@ -65,6 +65,14 @@ export class AgentClawStack extends cdk.Stack { removalPolicy: cdk.RemovalPolicy.RETAIN, }); + // ── DynamoDB user registry ───────────────────────────────────────────── + const usersTable = new dynamodb.Table(this, 'UsersTable', { + tableName: 'agent-claw-users', + partitionKey: { name: 'actor_id', type: dynamodb.AttributeType.STRING }, + billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, + removalPolicy: cdk.RemovalPolicy.RETAIN, + }); + // ── SQS FIFO message queue ───────────────────────────────────────────── const messageQueue = new sqs.Queue(this, 'MessageQueue', { queueName: 'agent-claw-messages.fifo', @@ -110,6 +118,8 @@ export class AgentClawStack extends cdk.Stack { }); sessionTable.grantReadWriteData(agentRunnerFn); + usersTable.grantReadWriteData(agentRunnerFn); + agentRunnerFn.addEnvironment('USERS_TABLE_NAME', usersTable.tableName); workspaceBucket.grantRead(agentRunnerFn); botTokenSecret.grantRead(agentRunnerFn); braveApiKeySecret.grantRead(agentRunnerFn); @@ -245,6 +255,11 @@ export class AgentClawStack extends cdk.Stack { description: 'DynamoDB table for session mapping', }); + new cdk.CfnOutput(this, 'UsersTableName', { + value: usersTable.tableName, + description: 'DynamoDB user registry table', + }); + new cdk.CfnOutput(this, 'MessageQueueUrl', { value: messageQueue.queueUrl, description: 'SQS FIFO queue for incoming messages', diff --git a/src/lambdas/agent-runner/handler.py b/src/lambdas/agent-runner/handler.py index efe0fc9..56680d5 100644 --- a/src/lambdas/agent-runner/handler.py +++ b/src/lambdas/agent-runner/handler.py @@ -24,6 +24,29 @@ def get_agentcore(): return _agentcore +def get_or_create_user(actor_id: str, from_info: dict) -> dict: + """Look up user in registry, auto-registering on first contact.""" + table_name = os.environ.get('USERS_TABLE_NAME', '') + if not table_name: + return {'actor_id': actor_id, 'display_name': from_info.get('from_name', actor_id)} + table = get_ddb().Table(table_name) + response = table.get_item(Key={'actor_id': actor_id}) + item = response.get('Item') + if item: + return item + now = int(time.time()) + item = { + 'actor_id': actor_id, + 'display_name': from_info.get('from_name') or actor_id, + 'telegram_username': from_info.get('from_username', ''), + 'created_at': str(now), + 'allowed': True, + } + table.put_item(Item=item) + print(f'[agent-runner] Registered new user: {actor_id}') + return item + + def get_or_create_session(actor_id: str) -> str: """Look up active session for actor, or create a new one.""" table = get_ddb().Table(os.environ['SESSION_TABLE_NAME']) @@ -72,9 +95,13 @@ def handler(event, context): chat_id = first.get('chat_id', '') actor_id = f"{channel}:{chat_id}" + # ── User registry ───────────────────────────────────────────────────── + from_info = first.get('messages', [{}])[0] + user_profile = get_or_create_user(actor_id, from_info) + # ── Get or create AgentCore session ────────────────────────────────── session_id = get_or_create_session(actor_id) - print(f"[agent-runner] actor={actor_id} session={session_id}") + print(f"[agent-runner] actor={actor_id} session={session_id} user={user_profile.get('display_name', '')}") # ── Bundle messages ─────────────────────────────────────────────────── if len(records) == 1: @@ -91,6 +118,11 @@ def handler(event, context): 'prompt': prompt, 'actor_id': actor_id, 'session_id': session_id, + 'user_profile': { + 'display_name': user_profile.get('display_name', actor_id), + 'telegram_username': user_profile.get('telegram_username', ''), + 'allowed': user_profile.get('allowed', True), + }, 'channel_adapter': { 'type': channel, 'target_id': str(chat_id),