Architecture
ThinkWork has two useful views of the system:
- a conceptual model for reasoning about how work flows through the product
- an infrastructure model for understanding what gets deployed in AWS
The conceptual model is:
- Threads are the record of work
- Memory surfaces useful context into a turn, including document retrieval and long-term memory
- Agents are the execution layer that decide and act
- Connectors connect ThinkWork to outside systems, including MCP-based tool connectors
Those concepts map onto a three-tier AWS deployment. Every resource lives in your AWS account. There is no shared infrastructure, no callbacks to external control planes, and no telemetry sent outside your account.
That matters because ThinkWork is not just trying to run agents. It is trying to give customers an open, customer-owned harness for AI work.
System model
Section titled “System model”Before getting into the deployment tiers, it helps to frame the runtime in product terms:
External systems and users ↓Threads ↓Memory ↓Agents ↓Connectors and responses back outThreads
Section titled “Threads”Threads are the durable record of work. User chats, connector events, emails, and automations all become threads with history, status, metadata, and auditability.
See Threads.
Memory
Section titled “Memory”Memory is the context layer. It determines what gets surfaced into the current turn beyond the latest message, and over time it should be portable through a ThinkWork-owned contract rather than defined by any one backend.
In the current open source app, that mainly means:
- thread history selected for the context window
- document retrieval through Bedrock Knowledge Bases
- long-term memory recall through AWS AgentCore LongTerm memory by default, or Hindsight when configured
- context assembly before model invocation
The default long-term memory setup includes semantic, summarization, user-preference, and episodic strategies.
See Memory.
Agents
Section titled “Agents”Agents are the execution layer. They receive a thread plus assembled context, decide what to do, call tools, and produce a response.
In managed mode, this execution happens in AgentCore, but the surrounding harness remains ThinkWork’s. That distinction is important: managed does not mean vendor-hosted.
See Agents.
Connectors
Section titled “Connectors”Connectors are the integration boundary. Some connectors bring inbound events into threads, and some expose external tools for agents to call.
This includes:
- channel and event connectors such as Slack, GitHub, and Google Workspace
- tool connectors, including MCP Tools
See Connectors.
Three-tier deployment model
Section titled “Three-tier deployment model”┌─────────────────────────────────────────────────────────────┐│ App Tier ││ AppSync · API Gateway · AgentCore Lambda · Crons · SES ││ CloudFront · Connector Lambdas · Step Functions │├─────────────────────────────────────────────────────────────┤│ Data Tier ││ Aurora Postgres (pgvector) · S3 (skills, KB, logs) ││ Bedrock KB · Secrets Manager │├─────────────────────────────────────────────────────────────┤│ Foundation Tier ││ VPC · Subnets · Cognito · KMS · Route53 · ACM · SES Setup │└─────────────────────────────────────────────────────────────┘Foundation tier
Section titled “Foundation tier”The foundation tier provides identity, networking, and encryption. It changes rarely and is the most stable part of the deployment.
| Resource | Purpose |
|---|---|
| VPC + subnets | Isolated network with public and private subnets across 2 AZs |
| NAT Gateway | Outbound internet access for private subnet resources |
| Cognito User Pool | User authentication and JWT issuance |
| Cognito Identity Pool | Maps Cognito users to IAM roles for direct AWS resource access |
| KMS keys | Encryption at rest for app data, audit logs, and credential vault |
| Route53 records | DNS for admin app, API, and email |
| ACM certificates | TLS for CloudFront and API Gateway custom domains |
| SES domain identity | Verified sending domain for outbound email |
Data tier
Section titled “Data tier”The data tier holds all persistent state. It depends on the foundation tier for network access and KMS encryption.
| Resource | Purpose |
|---|---|
| Aurora Postgres | Primary data store: agents, threads, messages, automations, connectors, users. Also hosts the pgvector index used by Bedrock Knowledge Bases — no separate vector DB |
| S3 — skill catalog | skills/catalog/*.md — skill packs loaded at invoke time |
| S3 — knowledge docs | Source documents for Bedrock Knowledge Bases |
| S3 — audit logs | Append-only log of every agent invoke (NDJSON, partitioned by date) |
| S3 — assets | Admin and end-user app static files |
| Bedrock Knowledge Base | Vector-indexed document store for inline RAG, backed by Aurora pgvector |
| Secrets Manager | DB credentials, OAuth client secrets |
App tier
Section titled “App tier”The app tier is where computation happens. It depends on both lower tiers.
| Resource | Purpose |
|---|---|
| AppSync GraphQL API | Real-time subscriptions (WebSocket), used for streaming responses |
| API Gateway v2 | HTTP queries and mutations, connector webhook ingress |
| AgentCore Lambda | Container-based agent runtime (Python/Strands + Bedrock) |
| Connector Lambdas | One per connector (Slack, GitHub, Google) — handles inbound events |
| Step Functions | Automation runner, routine executor |
| EventBridge | Triggers for scheduled automations |
| Bedrock AgentCore Memory | Always on — automatic per-turn retention into four strategies (semantic, preferences, summaries, episodes) |
| ECS Fargate (optional) | Hindsight memory add-on (if enable_hindsight = true) |
| CloudFront | CDN for admin app, end-user app static files |
| SES (sending) | Outbound email from agent responses |
Data flow: agent invoke
Section titled “Data flow: agent invoke”A full round trip from user message to agent response:
1. User sends message └─ POST /graphql (API Gateway) └─ JWT validated by Cognito authorizer └─ createMessage resolver → Aurora (writes message record) └─ Triggers AgentCore Lambda invocation (async via SQS)
2. AgentCore Lambda receives event └─ Reads thread history from Aurora └─ Downloads assigned skill packs from S3 └─ Queries Bedrock Knowledge Base (if assigned) → retrieves relevant chunks └─ Agent tools read long-term memories from AgentCore Memory (always on) via the `recall()` tool, and optionally from Hindsight (ECS) when `enable_hindsight = true` └─ After the turn completes, the container auto-emits a CreateEvent into AgentCore Memory so background strategies extract facts for future recall └─ Builds context: system prompt + selected history + retrieved knowledge + recalled memory + tool config
3. Bedrock inference └─ Strands sends context + message to Bedrock (Claude) └─ Model may request tool calls └─ Tools execute (SQL, S3, HTTP, skill-defined functions) └─ Tool results injected, model generates final response
4. Response delivery └─ AgentCore writes response message to Aurora └─ Publishes AppSync mutation → NewMessageEvent subscription └─ Stream chunks published in real time via AppSync └─ Thread status updated in Aurora
5. Client receives response └─ AppSync WebSocket delivers StreamChunkEvent chunks └─ Final NewMessageEvent marks completionData flow: connector inbound
Section titled “Data flow: connector inbound”External service (Slack, GitHub, etc.) → POST /connectors/<id>/webhook (API Gateway) → Connector Lambda └─ Validates signature └─ Writes thread record to Aurora (channel=SLACK, metadata={...}) └─ Invokes AgentCore (same path as user message above) → Agent response → Outbound connector or tool call posts reply back to Slack/GitHub/etc.Data flow: MCP tool connector
Section titled “Data flow: MCP tool connector”AgentCore receives thread + assembled context → Resolves enabled tool connectors from template/agent config → Connects to MCP server over HTTP streaming or SSE → Discovers available tools for this invocation → Model calls MCP tool when needed → Tool result returns into the same turn → Final response written back to the threadData flow: automation
Section titled “Data flow: automation”EventBridge scheduled rule (cron) → Step Functions state machine starts → Creates AUTO- thread in Aurora → Invokes AgentCore with configured prompt → (same agent loop as above) → Step Functions records execution result → Thread marked closed (or failed)Where state lives
Section titled “Where state lives”This split is useful to keep in mind:
- Threads preserve the canonical record of work in Aurora
- Memory combines persisted sources like documents and memories with retrieval-time assembly
- Agents are mostly stateless between invocations aside from their configuration
- Connectors store credentials and integration configuration, but the resulting work still lands in threads
| What | Where | Backup |
|---|---|---|
| Agents, threads, messages | Aurora Postgres | Automated daily snapshots (7-day retention) |
| User accounts | Cognito User Pool | Cognito-managed, multi-AZ |
| Skill packs | S3 (skill catalog bucket) | S3 versioning enabled |
| Knowledge documents | S3 (knowledge bucket) | S3 versioning enabled |
| Audit logs | S3 (audit log bucket) | S3 versioning + lifecycle to Glacier after 90d |
| Memories (managed) | Aurora Postgres | Same as above |
| Memories (Hindsight) | Aurora Postgres + ECS in-flight processing | Same as above |
| OAuth tokens / API keys | SSM Parameter Store (SecureString, KMS) | SSM-managed |
| Terraform state | S3 (tfstate bucket) + DynamoDB (lock table) | S3 versioning enabled |
| Vector index | Aurora Postgres (pgvector) | Same as Aurora above |
Multi-tenancy
Section titled “Multi-tenancy”ThinkWork is multi-tenant within a single deployment. Every Aurora table has a tenant_id column and all queries are tenant-scoped. The Cognito identity pool maps users to tenants at login time.
Row-level security (Postgres RLS) enforces tenant isolation at the database level — even if application code has a bug, a query cannot return data from another tenant.
-- RLS policy (applied automatically by ThinkWork migrations)CREATE POLICY tenant_isolation ON threads USING (tenant_id = current_setting('app.current_tenant_id')::uuid);Security model
Section titled “Security model”| Boundary | Enforcement |
|---|---|
| External → API | Cognito JWT (API Gateway authorizer) |
| API → Aurora | IAM auth + VPC security groups |
| AgentCore → Bedrock | IAM role (least privilege) |
| AgentCore → S3 | IAM role (read skills, read KB docs, write audit logs) |
| AgentCore → SSM | IAM role (read credentials for assigned connectors) |
| Tenant isolation | Postgres RLS on all tables |
| Secrets at rest | KMS encryption (dedicated key per secret category) |
| Audit trail | Append-only S3 bucket (no delete permissions on Lambda role) |
AgentCore container
Section titled “AgentCore container”AgentCore is deployed as a Lambda container image stored in ECR. The image is built from the ThinkWork base image and includes:
- Python 3.12
- Strands agent framework
- Boto3 (Bedrock, S3, SSM, Secrets Manager clients)
- httpx (for skill HTTP tools)
- psycopg3 (Aurora connection)
- The ThinkWork runtime library (tool registration, memory read/write, context assembly)
The container is 512MB compressed. Lambda allocates 3GB memory (configurable), which maps to approximately 2 vCPUs. Cold start time is 3–5 seconds for the first invocation after a period of inactivity; warm invocations start in under 100ms.