multi-tenant Phase 1: user registry + per-user memory
- CDK: add agent-claw-users DynamoDB table (actor_id PK, RETAIN policy) - CDK: grant agent-runner read/write on users table; add USERS_TABLE_NAME env - CDK: fix cdk.json app field (was object, must be command string) - CDK: add UsersTableName output - agent-runner: get_or_create_user() auto-registers users on first contact (stores display_name, telegram_username, created_at, allowed) - agent-runner: pass user_profile in AgentCore payload - prompt_builder: split base prompt (cached) from per-user context (injected per-call) removes USER.md/MEMORY.md from shared load; user name/username injected dynamically - main.py: extract user_profile from payload, build user_context string for prompt
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||
}
|
||||
|
||||
@@ -3,21 +3,11 @@
|
||||
{
|
||||
"type": "aws:cdk:creationStack",
|
||||
"data": [
|
||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.js:41:1)",
|
||||
"...node internals..."
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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.<anonymous> in aws-cdk-lib...",
|
||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:150:17)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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.<anonymous> in aws-cdk-lib...",
|
||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:150:17)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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.<anonymous> in aws-cdk-lib...",
|
||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:164:22)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/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.<anonymous> in aws-cdk-lib...",
|
||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.js:150:17)",
|
||||
"<anonymous> (/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)",
|
||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||
"...node internals, ts-node, ts-node, ts-node..."
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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}")
|
||||
@@ -0,0 +1 @@
|
||||
boto3>=1.34.0
|
||||
@@ -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'}
|
||||
@@ -0,0 +1 @@
|
||||
boto3>=1.34.0
|
||||
@@ -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": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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',
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user