thinkwork-cli 0.13.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -14
- package/dist/{api-client-JYZXYQJR.js → api-client-4P4JZOBX.js} +1 -1
- package/dist/{chunk-Z2F6RJFR.js → chunk-4OMDWYZQ.js} +4 -5
- package/dist/{chunk-ILZKG5WV.js → chunk-AD2NIWB5.js} +434 -69
- package/dist/chunk-MWQ7RO76.js +137 -0
- package/dist/{chunk-DVPBI5HM.js → chunk-OLUQ4U2Z.js} +26 -26
- package/dist/{chunk-QOERT44A.js → chunk-YQCYD6NE.js} +57 -44
- package/dist/cli.js +4400 -16431
- package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +0 -1
- package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +0 -1
- package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +0 -14
- package/dist/{dist-es-CRV46D5B.js → dist-es-2OTNTL4N.js} +4 -4
- package/dist/{dist-es-ENBUC3JP.js → dist-es-2XVAH4BQ.js} +2 -2
- package/dist/{dist-es-DCY4IC3Y.js → dist-es-3DPDUJAH.js} +2 -2
- package/dist/{dist-es-G7PTG75M.js → dist-es-BNZTBBVC.js} +1 -1
- package/dist/{dist-es-GKDBE7E6.js → dist-es-CD2RBDAC.js} +1 -1
- package/dist/{dist-es-R7FGL2IB.js → dist-es-I4FW57PS.js} +1 -1
- package/dist/{dist-es-USORBXZ5.js → dist-es-KVFWJNDY.js} +4 -4
- package/dist/{dist-es-HY3535OI.js → dist-es-PS73DWNL.js} +2 -2
- package/dist/{dist-es-LDE437AM.js → dist-es-S3NZQFGT.js} +1 -1
- package/dist/{dist-es-G54LBMRN.js → dist-es-SGCA4P32.js} +1 -1
- package/dist/{dist-es-Q57SZXZ7.js → dist-es-WBM6J4RA.js} +2 -2
- package/dist/{dist-es-P26FWFLT.js → dist-es-ZGVADYIF.js} +8 -8
- package/dist/drizzle/0227_analyst_reader_role.sql +17 -26
- package/dist/drizzle/0230_analyst_rls.sql +28 -45
- package/dist/drizzle/0261_native_auth_control_plane.sql +7 -3
- package/dist/drizzle/0270_tool_execution_ledger.sql +115 -0
- package/dist/drizzle/0271_ontology_twin_declarations.sql +47 -0
- package/dist/drizzle/0272_identity_graph_projection_cursor.sql +23 -0
- package/dist/drizzle/0273_twin_materialization_suggestions.sql +29 -0
- package/dist/drizzle/0274_tenant_mcp_twin_keys.sql +59 -0
- package/dist/drizzle/0275_identity_bulk_rebuild_fence.sql +30 -0
- package/dist/drizzle/0276_tool_execution_ledger_allow_cascade_delete.sql +18 -0
- package/dist/drizzle/0277_knowledge_base_sources.sql +107 -0
- package/dist/drizzle/0278_kb_page_transcription.sql +55 -0
- package/dist/drizzle/0279_mcp_servers_off_plugin_provenance.sql +93 -0
- package/dist/drizzle/0280_work_item_external_refs_drop_plane_provider.sql +46 -0
- package/dist/drizzle/0281_tenant_mcp_twin_keys_expiry_suffix.sql +9 -0
- package/dist/drizzle/0282_tenant_mcp_twin_keys_grants.sql +24 -0
- package/dist/drizzle/0283_agents_agentcore_runtime_dispatch.sql +13 -0
- package/dist/drizzle/0284_user_brain_claims.sql +69 -0
- package/dist/drizzle/0285_tenant_mcp_twin_keys_trusted_subsystem.sql +30 -0
- package/dist/drizzle/0286_brain_analytics_key.sql +26 -0
- package/dist/drizzle/0287_brain_subject.sql +25 -0
- package/dist/drizzle/0288_multi_lane_identities.sql +25 -0
- package/dist/{loadSso-DJVB7BXE.js → loadSso-ETCXTR4I.js} +2 -2
- package/dist/{loadSso-JAMNA3WY.js → loadSso-UBD7KUHE.js} +2 -2
- package/dist/plugins/README.md +2 -2
- package/dist/plugins/catalog/package.json +0 -3
- package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -3
- package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
- package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -25
- package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -6
- package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -15
- package/dist/plugins/catalog/src/contracts.ts +15 -0
- package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -18
- package/dist/plugins/n8n/test/integrated-app-contract.test.ts +1 -1
- package/dist/plugins/twenty/package.json +2 -1
- package/dist/plugins/twenty/scripts/LAMBDA_DEPLOY.md +114 -0
- package/dist/plugins/twenty/scripts/build-lambda.sh +29 -0
- package/dist/plugins/twenty/scripts/configure-product-views.ts +195 -0
- package/dist/plugins/twenty/scripts/lambda-handler.ts +49 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/load-attachments.test.ts +38 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +145 -2
- package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +397 -151
- package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +28 -129
- package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +10 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +13 -8
- package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +222 -2
- package/dist/plugins/twenty/scripts/lib/load-attachments.ts +29 -0
- package/dist/plugins/twenty/scripts/lib/load-records.ts +160 -43
- package/dist/plugins/twenty/scripts/lib/mappers.ts +368 -152
- package/dist/plugins/twenty/scripts/lib/members-ensure.ts +43 -268
- package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +57 -0
- package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +136 -31
- package/dist/plugins/twenty/scripts/migrate-lastmile.ts +254 -152
- package/dist/plugins/twenty/scripts/provision-twenty-members.ts +44 -1
- package/dist/plugins/twenty/scripts/purge-lastmile-import.ts +178 -0
- package/dist/plugins/twenty/scripts/restructure-products.ts +312 -0
- package/dist/plugins/twenty/scripts/retag-stage-values.ts +362 -0
- package/dist/plugins/twenty/test/manifest.test.ts +2 -4
- package/dist/scripts/post-deploy.sh +79 -1
- package/dist/{signin-UGDWTGS4.js → signin-5QZNRSOO.js} +2 -2
- package/dist/{sso-oidc-Q4VXANXY.js → sso-oidc-XK673P54.js} +2 -2
- package/dist/{sts-CWLSE3GH.js → sts-OXI2UALF.js} +6 -134
- package/dist/{sts-DNZLNVOV.js → sts-SQW4NBK3.js} +6 -134
- package/dist/terraform/examples/greenfield/main.tf +233 -175
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +12 -8
- package/dist/terraform/modules/app/agentcore-admin/main.tf +47 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +39 -47
- package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +13 -66
- package/dist/terraform/modules/app/agentcore-identity/main.tf +106 -26
- package/dist/terraform/modules/app/agentcore-identity/outputs.tf +6 -6
- package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +216 -99
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +6 -9
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_workload_identity.sh +19 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/ensure_workload_identity.sh +29 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +22 -7
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +17 -46
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +65 -3
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/after.tf +15 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/before.tf +7 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-after.tf +8 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-before.tf +9 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-after.tf +3 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-before.tf +3 -0
- package/dist/terraform/modules/app/agentcore-identity/variables.tf +6 -0
- package/dist/terraform/modules/app/agentcore-memory/README.md +46 -9
- package/dist/terraform/modules/app/agentcore-memory/main.tf +27 -10
- package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +156 -24
- package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
- package/dist/terraform/modules/app/agentcore-pi/main.tf +42 -52
- package/dist/terraform/modules/app/agentcore-pi/outputs.tf +3 -3
- package/dist/terraform/modules/app/agentcore-pi/scripts/reconcile_pi_runtime.mjs +175 -0
- package/dist/terraform/modules/app/agentcore-pi/variables.tf +35 -28
- package/dist/terraform/modules/app/agentcore-platform/main.tf +68 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +4 -20
- package/dist/terraform/modules/app/capability-broker/main.tf +2 -2
- package/dist/terraform/modules/app/capability-broker/variables.tf +11 -0
- package/dist/terraform/modules/app/deployment-control-plane/README.md +264 -0
- package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +4 -1
- package/dist/terraform/modules/app/deployment-control-plane/main.tf +94 -0
- package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +5 -0
- package/dist/terraform/modules/app/deployment-control-plane/runner.py +2641 -124
- package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2034 -154
- package/dist/terraform/modules/app/deployment-control-plane/variables.tf +33 -0
- package/dist/terraform/modules/app/lambda-api/auth-state.tf +72 -0
- package/dist/terraform/modules/app/lambda-api/chat-latency-observability.tf +466 -0
- package/dist/terraform/modules/app/lambda-api/dispatch-dlq.tf +55 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +411 -723
- package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +262 -198
- package/dist/terraform/modules/app/lambda-api/main.tf +51 -13
- package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +4 -181
- package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +3 -28
- package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +1 -2
- package/dist/terraform/modules/app/lambda-api/outputs.tf +17 -45
- package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +219 -126
- package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/README.md +1 -1
- package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/main.tf +18 -18
- package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +11 -10
- package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +5 -5
- package/dist/terraform/modules/app/static-site/main.tf +1 -1
- package/dist/terraform/modules/app/www-dns/main.tf +9 -27
- package/dist/terraform/modules/app/www-dns/variables.tf +4 -10
- package/dist/terraform/modules/data/aurora-postgres/README.md +34 -0
- package/dist/terraform/modules/data/aurora-postgres/alarms.tf +82 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +27 -57
- package/dist/terraform/modules/data/aurora-postgres/outputs.tf +0 -9
- package/dist/terraform/modules/data/aurora-postgres/variables.tf +41 -0
- package/dist/terraform/modules/data/s3-buckets/main.tf +5 -6
- package/dist/terraform/modules/foundation/cognito/main.tf +81 -8
- package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +24 -12
- package/dist/terraform/modules/thinkwork/README.md +5 -1
- package/dist/terraform/modules/thinkwork/main.tf +376 -374
- package/dist/terraform/modules/thinkwork/outputs.tf +12 -158
- package/dist/terraform/modules/thinkwork/variables.tf +246 -89
- package/dist/workspace-defaults/files/INSTRUCTIONS.md +9 -11
- package/dist/workspace-defaults/files/MEMORY_GUIDE.md +38 -35
- package/dist/workspace-defaults/files/ROUTER.md +1 -1
- package/dist/workspace-defaults/files/TOOLS.md +9 -39
- package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +6 -3
- package/dist/workspace-defaults/files/catalog-skills/brain-brief-builder/SKILL.md +165 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/SKILL.md +75 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/workflow-authoring.md +46 -0
- package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +1 -1
- package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +22 -3
- package/package.json +1 -1
- package/dist/drizzle/0261_managed_harness_multiplayer.sql +0 -313
- package/dist/drizzle/0262_parallel_harness_threads.sql +0 -9
- package/dist/drizzle/0264_harness_tool_execution_ledger.sql +0 -113
- package/dist/plugins/company-data/README.md +0 -49
- package/dist/plugins/company-data/package.json +0 -21
- package/dist/plugins/company-data/src/index.ts +0 -30
- package/dist/plugins/company-data/src/manifest.ts +0 -26
- package/dist/plugins/company-data/test/manifest.test.ts +0 -116
- package/dist/plugins/company-data/tsconfig.json +0 -8
- package/dist/plugins/company-etl/README.md +0 -46
- package/dist/plugins/company-etl/package.json +0 -21
- package/dist/plugins/company-etl/src/index.ts +0 -29
- package/dist/plugins/company-etl/src/manifest.ts +0 -26
- package/dist/plugins/company-etl/test/manifest.test.ts +0 -108
- package/dist/plugins/company-etl/tsconfig.json +0 -8
- package/dist/plugins/lastmile/README.md +0 -50
- package/dist/plugins/lastmile/package.json +0 -23
- package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +0 -661
- package/dist/plugins/lastmile/src/api/tasks-adapter.ts +0 -572
- package/dist/plugins/lastmile/src/discovery.fixture.ts +0 -64
- package/dist/plugins/lastmile/src/index.ts +0 -46
- package/dist/plugins/lastmile/src/manifest.ts +0 -139
- package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +0 -266
- package/dist/plugins/lastmile/test/discovery.test.ts +0 -88
- package/dist/plugins/lastmile/tsconfig.json +0 -8
- package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +0 -409
- package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +0 -359
- package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +0 -61
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +0 -227
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +0 -277
- package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +0 -70
- package/dist/terraform/modules/app/agentcore-gateway/main.tf +0 -305
- package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +0 -44
- package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +0 -93
- package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +0 -33
- package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +0 -543
- package/dist/terraform/modules/app/agentcore-gateway/variables.tf +0 -70
- package/dist/terraform/modules/app/agentcore-harness/main.tf +0 -330
- package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -72
- package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +0 -430
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +0 -180
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +0 -193
- package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -96
- package/dist/terraform/modules/app/hindsight-memory/README.md +0 -89
- package/dist/terraform/modules/app/hindsight-memory/main.tf +0 -411
- package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +0 -88
- package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +0 -91
- package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +0 -102
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/outputs.tf +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/sync-database.py +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/test_sync_database.py +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/variables.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/README.md +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/main.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/outputs.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/variables.tf +0 -0
|
@@ -94,3 +94,36 @@ variable "create_secret_placeholders" {
|
|
|
94
94
|
type = bool
|
|
95
95
|
default = true
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
# ── Runner-in-VPC (THINK-1082) ────────────────────────────────────────────────
|
|
99
|
+
# The runner's push_database_schema step opens psql to the Aurora cluster. By
|
|
100
|
+
# default the CodeBuild project has no vpc_config, so that connection rides the
|
|
101
|
+
# cluster's PUBLIC endpoint and depends on a world-open 0.0.0.0/0 → 5432 rule
|
|
102
|
+
# on the DB security group. Customers that lock that rule down (deliberate
|
|
103
|
+
# hardening) break every deploy at the psql step. When runner_in_vpc is on, the
|
|
104
|
+
# build runs inside the VPC's private subnets with its own security group and
|
|
105
|
+
# the DB grants 5432 to that group — no world-open port required.
|
|
106
|
+
|
|
107
|
+
variable "runner_in_vpc" {
|
|
108
|
+
description = "Run the deployment runner CodeBuild inside the VPC (private subnets + runner security group) and grant it 5432 on the database security group. Requires vpc_id, subnet_ids, and db_security_group_id. The private subnets must route egress through a NAT gateway (GitHub, apt/pip, STS/S3/Secrets Manager)."
|
|
109
|
+
type = bool
|
|
110
|
+
default = false
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
variable "vpc_id" {
|
|
114
|
+
description = "VPC that hosts the runner when runner_in_vpc is true."
|
|
115
|
+
type = string
|
|
116
|
+
default = ""
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
variable "subnet_ids" {
|
|
120
|
+
description = "Private subnet IDs (NAT-routed) the runner attaches to when runner_in_vpc is true."
|
|
121
|
+
type = list(string)
|
|
122
|
+
default = []
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
variable "db_security_group_id" {
|
|
126
|
+
description = "Database security group that receives the tcp/5432 ingress from the runner security group when runner_in_vpc is true."
|
|
127
|
+
type = string
|
|
128
|
+
default = ""
|
|
129
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# THINK-643/644 — DynamoDB short-lived state store
|
|
3
|
+
#
|
|
4
|
+
# `auth_subscription_tickets` in Aurora is pure churn: 60-second single-use
|
|
5
|
+
# AppSync tickets, plus the per-principal connect counter that backstops a
|
|
6
|
+
# looping client. One customer stage accumulated 551k rows of it on the primary
|
|
7
|
+
# database. DynamoDB is the right shape — native TTL sweeps expired items with
|
|
8
|
+
# no vacuum pressure, conditional writes give the same single-use guarantee the
|
|
9
|
+
# `status = 'issued'` predicate gave, and the table is reachable from the
|
|
10
|
+
# non-VPC auth Lambdas.
|
|
11
|
+
#
|
|
12
|
+
# THINK-644 widens the same table to webhook idempotency receipts, which have
|
|
13
|
+
# the identical defect in a different table: `webhook_idempotency` has no
|
|
14
|
+
# retention at all — a row survives until its parent webhook is deleted — so a
|
|
15
|
+
# chatty provider grows Aurora forever for records nobody reads after a day.
|
|
16
|
+
#
|
|
17
|
+
# Ship-inert: the table and its IAM grant are count-gated on
|
|
18
|
+
# var.auth_state_store == "dynamo", so a stage that has not flipped the flag
|
|
19
|
+
# plans to ZERO changes from this file.
|
|
20
|
+
################################################################################
|
|
21
|
+
|
|
22
|
+
locals {
|
|
23
|
+
auth_state_enabled = var.auth_state_store == "dynamo"
|
|
24
|
+
auth_state_count = local.auth_state_enabled ? 1 : 0
|
|
25
|
+
auth_state_table_name = "thinkwork-${var.stage}-auth-state"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
resource "aws_dynamodb_table" "auth_state" {
|
|
29
|
+
count = local.auth_state_count
|
|
30
|
+
|
|
31
|
+
name = local.auth_state_table_name
|
|
32
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
33
|
+
hash_key = "pk"
|
|
34
|
+
range_key = "sk"
|
|
35
|
+
|
|
36
|
+
attribute {
|
|
37
|
+
name = "pk"
|
|
38
|
+
type = "S"
|
|
39
|
+
}
|
|
40
|
+
attribute {
|
|
41
|
+
name = "sk"
|
|
42
|
+
type = "S"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Epoch seconds. Tickets set this to their own expiry plus an hour of grace
|
|
46
|
+
# so a replay still finds the consumed tombstone (TTL deletion is best-effort
|
|
47
|
+
# and can lag by up to 48h); rate-limit counters set it to two window widths;
|
|
48
|
+
# webhook idempotency receipts (THINK-644) set it to 7 days, comfortably past
|
|
49
|
+
# any real provider's retry window. Because deletion lags, every reader
|
|
50
|
+
# compares expires_at itself rather than trusting an item's presence.
|
|
51
|
+
ttl {
|
|
52
|
+
attribute_name = "expires_at"
|
|
53
|
+
enabled = true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
server_side_encryption {
|
|
57
|
+
enabled = true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Deliberately OFF. Every item in this table is auth-flow scratch with a
|
|
61
|
+
# lifetime measured in minutes — a point-in-time restore of it would restore
|
|
62
|
+
# nothing anyone can use, and PITR bills continuously against the write churn
|
|
63
|
+
# this table exists to absorb.
|
|
64
|
+
point_in_time_recovery {
|
|
65
|
+
enabled = false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
tags = {
|
|
69
|
+
Name = local.auth_state_table_name
|
|
70
|
+
Purpose = "auth-flow-state"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# THINK-915 — chat turn-latency observability (metrics, dashboard, alarm,
|
|
3
|
+
# saved Logs Insights query).
|
|
4
|
+
#
|
|
5
|
+
# WHY: a 52 s turn shipped with a hidden ~20-24 s AgentCore cold start (the gap
|
|
6
|
+
# between `api.runtime_dispatch.invoke` starting and the container's
|
|
7
|
+
# `runtime.invocation.received`). Nothing alarmed; a human noticed. Every chat
|
|
8
|
+
# turn already emits structured `agentcore_phase` lines, but they were only
|
|
9
|
+
# reachable through ad-hoc Logs Insights runs (scripts/latency-dashboard.sh).
|
|
10
|
+
# This file turns two of those phases into real CloudWatch metrics, puts them on
|
|
11
|
+
# a per-stage dashboard next to the Lambda-native signals, and alarms on the
|
|
12
|
+
# p95 of the whole runtime call.
|
|
13
|
+
#
|
|
14
|
+
# ── Phase lines and where they land ──────────────────────────────────────────
|
|
15
|
+
# api.agentcore.dispatch chat-agent-invoke /aws/lambda/thinkwork-<stage>-api-chat-agent-invoke
|
|
16
|
+
# api.runtime_dispatch.invoke agentcore-runtime-dispatch /aws/lambda/thinkwork-<stage>-api-agentcore-runtime-dispatch
|
|
17
|
+
# api.finalize.process chat-agent-finalize /aws/lambda/thinkwork-<stage>-api-chat-agent-finalize
|
|
18
|
+
# runtime.* Pi container /aws/bedrock-agentcore/runtimes/thinkwork_<stage>_pi-<id>-DEFAULT
|
|
19
|
+
#
|
|
20
|
+
# ── Constraint 1: JSON filter patterns need whole-line JSON ──────────────────
|
|
21
|
+
# CloudWatch metric filters only apply a JSON filter pattern (`{ $.event = … }`)
|
|
22
|
+
# to log events that parse as JSON end to end. The Node Lambda runtime prefixes
|
|
23
|
+
# `console.*` output with `<timestamp>\t<requestId>\tINFO\t…`, which is not
|
|
24
|
+
# valid JSON — so the Lambda-side phase lines were unusable by metric filters
|
|
25
|
+
# even though Logs Insights tolerates the prefix. THINK-915 therefore switched
|
|
26
|
+
# packages/api/src/lib/agentcore-phase-log.ts to write the record straight to
|
|
27
|
+
# `process.stdout` (the Pi container emitter has always done this), which
|
|
28
|
+
# reaches CloudWatch unprefixed. Nothing else about the record changed, so the
|
|
29
|
+
# existing Logs Insights queries keep working.
|
|
30
|
+
#
|
|
31
|
+
# ── Constraint 2: metric-filter dimensions must be JSON references ───────────
|
|
32
|
+
# `metric_transformation.dimensions` values may only be `$.field` references
|
|
33
|
+
# into the matched log event — a literal `Stage = var.stage` (as used by the
|
|
34
|
+
# PutMetricData-sourced Thinkwork/Costs alarms) is rejected. The phase record
|
|
35
|
+
# carries no stage field, so the stage is carried in the namespace instead:
|
|
36
|
+
# `Thinkwork/Chat/<stage>`. Same isolation, no emitter change.
|
|
37
|
+
#
|
|
38
|
+
# ── Constraint 3: implicit log groups ────────────────────────────────────────
|
|
39
|
+
# The API handler log groups are created implicitly by Lambda on first
|
|
40
|
+
# invocation and are NOT Terraform-managed (see memory-alarms.tf: adding metric
|
|
41
|
+
# filters over them broke greenfield applies with ResourceNotFoundException).
|
|
42
|
+
# So the metric filters — and the alarm that depends on one of them — are gated
|
|
43
|
+
# behind `enable_chat_turn_latency_metric_filters`, default OFF. Flip it on for
|
|
44
|
+
# a stage that has already served chat traffic. The dashboard, the Lambda-native
|
|
45
|
+
# alarm, and the saved query have no such dependency and are on by default.
|
|
46
|
+
#
|
|
47
|
+
# ── Constraint 4: the runtime log group name contains a generated id ─────────
|
|
48
|
+
# The AgentCore runtime log group is `/aws/bedrock-agentcore/runtimes/
|
|
49
|
+
# thinkwork_<stage>_pi-<runtimeId>-DEFAULT`, and the runtime is reconciled by
|
|
50
|
+
# scripts/post-deploy.sh (runtime id in SSM), not by Terraform — so no resource
|
|
51
|
+
# here knows the name. It is supplied explicitly via
|
|
52
|
+
# `chat_turn_runtime_log_group_name` (see the revised note in locals below —
|
|
53
|
+
# plan-time prefix discovery broke customer deployment-runner plans). While it
|
|
54
|
+
# is unset the runtime-sourced pieces (AgentLoopMs filter, its dashboard
|
|
55
|
+
# widgets, that leg of the saved query) are simply skipped.
|
|
56
|
+
################################################################################
|
|
57
|
+
|
|
58
|
+
locals {
|
|
59
|
+
chat_latency_enabled = local.deploy_lambda_handlers && var.enable_chat_turn_latency_observability
|
|
60
|
+
|
|
61
|
+
# See constraint 2 — the stage lives in the namespace, not a dimension.
|
|
62
|
+
chat_latency_namespace = "${var.chat_turn_latency_metric_namespace}/${var.stage}"
|
|
63
|
+
|
|
64
|
+
chat_latency_dispatch_fn = local.chat_latency_enabled ? aws_lambda_function.handler["agentcore-runtime-dispatch"].function_name : ""
|
|
65
|
+
chat_latency_invoke_fn = local.chat_latency_enabled ? aws_lambda_function.handler["chat-agent-invoke"].function_name : ""
|
|
66
|
+
chat_latency_finalize_fn = local.chat_latency_enabled ? aws_lambda_function.handler["chat-agent-finalize"].function_name : ""
|
|
67
|
+
chat_latency_graphql_fn = local.chat_latency_enabled ? aws_lambda_function.handler["graphql-http"].function_name : ""
|
|
68
|
+
|
|
69
|
+
# The four Lambdas a chat turn passes through, in turn order.
|
|
70
|
+
chat_latency_functions = local.chat_latency_enabled ? [
|
|
71
|
+
local.chat_latency_graphql_fn,
|
|
72
|
+
local.chat_latency_invoke_fn,
|
|
73
|
+
local.chat_latency_dispatch_fn,
|
|
74
|
+
local.chat_latency_finalize_fn,
|
|
75
|
+
] : []
|
|
76
|
+
|
|
77
|
+
chat_latency_lambda_log_groups = [for fn in local.chat_latency_functions : "/aws/lambda/${fn}"]
|
|
78
|
+
|
|
79
|
+
# Constraint 4, revised after the canary.467/468 mcpherson deploys: the
|
|
80
|
+
# runtime log group comes ONLY from the explicit variable. The prefix
|
|
81
|
+
# discovery data source (aws_cloudwatch_log_groups) defers to apply in the
|
|
82
|
+
# customer deployment-runner flow, and the resulting unknown value first
|
|
83
|
+
# broke a count ("Invalid count argument") and then, once the count was
|
|
84
|
+
# fixed, broke the dashboard-widget conditionals ("Inconsistent conditional
|
|
85
|
+
# result types" — an unknown condition forces both arms of every dependent
|
|
86
|
+
# conditional to unify to one type, which heterogeneous widget objects
|
|
87
|
+
# cannot). A plan-known variable sidesteps the whole class. When it is
|
|
88
|
+
# empty, the runtime-sourced pieces (AgentLoopMs filter, runtime widgets,
|
|
89
|
+
# that leg of the saved query) are skipped; set it per stage once the
|
|
90
|
+
# runtime exists:
|
|
91
|
+
# /aws/bedrock-agentcore/runtimes/thinkwork_<stage>_pi-<runtimeId>-DEFAULT
|
|
92
|
+
chat_latency_runtime_log_group = var.chat_turn_runtime_log_group_name
|
|
93
|
+
|
|
94
|
+
chat_latency_has_runtime_log_group = local.chat_latency_runtime_log_group != ""
|
|
95
|
+
|
|
96
|
+
chat_latency_metric_filters_enabled = local.chat_latency_enabled && var.enable_chat_turn_latency_metric_filters
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
################################################################################
|
|
100
|
+
# Metric filters
|
|
101
|
+
################################################################################
|
|
102
|
+
|
|
103
|
+
# Whole-runtime-call latency: the dispatcher measures from just before
|
|
104
|
+
# InvokeAgentRuntime to the fully drained response, so this number INCLUDES the
|
|
105
|
+
# AgentCore cold start that the 52 s incident hid. Only `completed` lines feed
|
|
106
|
+
# it — a `failed` line's durationMs is time-to-error, not turn latency.
|
|
107
|
+
resource "aws_cloudwatch_log_metric_filter" "turn_runtime_invoke_ms" {
|
|
108
|
+
count = local.chat_latency_metric_filters_enabled ? 1 : 0
|
|
109
|
+
|
|
110
|
+
name = "thinkwork-${var.stage}-turn-runtime-invoke-ms"
|
|
111
|
+
log_group_name = "/aws/lambda/${local.chat_latency_dispatch_fn}"
|
|
112
|
+
pattern = "{ $.event = \"agentcore_phase\" && $.phase = \"api.runtime_dispatch.invoke\" && $.status = \"completed\" && $.durationMs = * }"
|
|
113
|
+
|
|
114
|
+
metric_transformation {
|
|
115
|
+
name = "TurnRuntimeInvokeMs"
|
|
116
|
+
namespace = local.chat_latency_namespace
|
|
117
|
+
value = "$.durationMs"
|
|
118
|
+
unit = "Milliseconds"
|
|
119
|
+
# No default_value: a period with no turns must stay a gap, not a 0 ms turn
|
|
120
|
+
# that would drag the percentiles down and mask a regression.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Model-loop latency from inside the container. Subtracting this from
|
|
125
|
+
# TurnRuntimeInvokeMs is the cold-start / harness-overhead signal (see the
|
|
126
|
+
# dashboard's "runtime overhead" widget).
|
|
127
|
+
#
|
|
128
|
+
# count gates on VARIABLES ONLY — never on the prefix discovery. In the
|
|
129
|
+
# customer deployment-runner flow the aws_cloudwatch_log_groups data source
|
|
130
|
+
# defers to apply, so a count that reads local.chat_latency_has_runtime_log_group
|
|
131
|
+
# fails the whole plan with "Invalid count argument" (canary.467 mcpherson
|
|
132
|
+
# deploy, 2026-08-20). The runtime-side filter therefore requires the explicit
|
|
133
|
+
# chat_turn_runtime_log_group_name override; discovery still feeds the
|
|
134
|
+
# dashboard widgets and saved query, whose bodies tolerate unknown values.
|
|
135
|
+
resource "aws_cloudwatch_log_metric_filter" "agent_loop_ms" {
|
|
136
|
+
count = local.chat_latency_metric_filters_enabled && var.chat_turn_runtime_log_group_name != "" ? 1 : 0
|
|
137
|
+
|
|
138
|
+
name = "thinkwork-${var.stage}-agent-loop-ms"
|
|
139
|
+
log_group_name = var.chat_turn_runtime_log_group_name
|
|
140
|
+
pattern = "{ $.event = \"agentcore_phase\" && $.phase = \"runtime.agent_loop\" && $.status = \"completed\" && $.durationMs = * }"
|
|
141
|
+
|
|
142
|
+
metric_transformation {
|
|
143
|
+
name = "AgentLoopMs"
|
|
144
|
+
namespace = local.chat_latency_namespace
|
|
145
|
+
value = "$.durationMs"
|
|
146
|
+
unit = "Milliseconds"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# THINK-946: pre-dispatch latency — the Lambda async-queue leg between the
|
|
151
|
+
# GraphQL resolver's Event invoke and chat-agent-invoke's first line. Measured
|
|
152
|
+
# live at 4.5-7 s with a 30 s outlier class, and previously invisible: no phase
|
|
153
|
+
# spanned it, so the turn timeline simply began late. `detail` carries the
|
|
154
|
+
# caller-side mutation leg (`queue=Nms;mutation=Nms`) for Logs Insights; the
|
|
155
|
+
# metric is the queue leg alone, which is the part no code change can shorten
|
|
156
|
+
# and which spikes when a spillover invoke misses the single provisioned
|
|
157
|
+
# instance on chat-agent-invoke's `live` alias.
|
|
158
|
+
resource "aws_cloudwatch_log_metric_filter" "pre_dispatch_queue_ms" {
|
|
159
|
+
count = local.chat_latency_metric_filters_enabled ? 1 : 0
|
|
160
|
+
|
|
161
|
+
name = "thinkwork-${var.stage}-pre-dispatch-queue-ms"
|
|
162
|
+
log_group_name = "/aws/lambda/${local.chat_latency_invoke_fn}"
|
|
163
|
+
pattern = "{ $.event = \"agentcore_phase\" && $.phase = \"api.invoke.queue_delay\" && $.status = \"completed\" && $.durationMs = * }"
|
|
164
|
+
|
|
165
|
+
metric_transformation {
|
|
166
|
+
name = "PreDispatchQueueMs"
|
|
167
|
+
namespace = local.chat_latency_namespace
|
|
168
|
+
value = "$.durationMs"
|
|
169
|
+
unit = "Milliseconds"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
################################################################################
|
|
174
|
+
# Alarms
|
|
175
|
+
#
|
|
176
|
+
# alarm_actions default to [] — consistent with memory-alarms.tf and
|
|
177
|
+
# capability-search-alarms.tf. The only SNS topic in this module
|
|
178
|
+
# (aws_sns_topic.cost_alerts) is the cost-alert channel and wiring latency
|
|
179
|
+
# pages into it would misroute them. Pass `chat_turn_latency_alarm_actions`
|
|
180
|
+
# (and `..._ok_actions`) once a stage has a topic worth paging.
|
|
181
|
+
################################################################################
|
|
182
|
+
|
|
183
|
+
resource "aws_cloudwatch_metric_alarm" "chat_turn_runtime_invoke_p95" {
|
|
184
|
+
count = local.chat_latency_metric_filters_enabled ? 1 : 0
|
|
185
|
+
|
|
186
|
+
alarm_name = "thinkwork-${var.stage}-chat-turn-runtime-invoke-p95"
|
|
187
|
+
alarm_description = "p95 of api.runtime_dispatch.invoke (the whole AgentCore runtime call, cold start included) exceeded ${var.chat_turn_p95_alarm_threshold_ms} ms in 2 of the last 3 five-minute periods. This is the regression guard for the 2026-08 incident where a 52 s turn hid a ~20-24 s cold start."
|
|
188
|
+
|
|
189
|
+
namespace = local.chat_latency_namespace
|
|
190
|
+
metric_name = "TurnRuntimeInvokeMs"
|
|
191
|
+
extended_statistic = "p95"
|
|
192
|
+
period = 300
|
|
193
|
+
evaluation_periods = 3
|
|
194
|
+
datapoints_to_alarm = 2
|
|
195
|
+
threshold = var.chat_turn_p95_alarm_threshold_ms
|
|
196
|
+
comparison_operator = "GreaterThanThreshold"
|
|
197
|
+
# Idle stages emit no turns at all; missing data is not a latency problem.
|
|
198
|
+
treat_missing_data = "notBreaching"
|
|
199
|
+
|
|
200
|
+
alarm_actions = var.chat_turn_latency_alarm_actions
|
|
201
|
+
ok_actions = var.chat_turn_latency_alarm_actions
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Log-group-independent companion: the dispatcher's whole job is the runtime
|
|
205
|
+
# call, so its Lambda Duration tracks TurnRuntimeInvokeMs closely. This alarm
|
|
206
|
+
# needs no metric filter, so it protects stages that have not (or cannot yet)
|
|
207
|
+
# turn the filters on.
|
|
208
|
+
resource "aws_cloudwatch_metric_alarm" "chat_dispatch_duration_p95" {
|
|
209
|
+
count = local.chat_latency_enabled ? 1 : 0
|
|
210
|
+
|
|
211
|
+
alarm_name = "thinkwork-${var.stage}-chat-dispatch-duration-p95"
|
|
212
|
+
alarm_description = "p95 agentcore-runtime-dispatch Lambda Duration exceeded ${var.chat_turn_p95_alarm_threshold_ms} ms in 2 of the last 3 five-minute periods. Native-metric stand-in for TurnRuntimeInvokeMs (the dispatcher does nothing but the runtime call), so it fires even where the log metric filters are off."
|
|
213
|
+
|
|
214
|
+
namespace = "AWS/Lambda"
|
|
215
|
+
metric_name = "Duration"
|
|
216
|
+
extended_statistic = "p95"
|
|
217
|
+
period = 300
|
|
218
|
+
evaluation_periods = 3
|
|
219
|
+
datapoints_to_alarm = 2
|
|
220
|
+
threshold = var.chat_turn_p95_alarm_threshold_ms
|
|
221
|
+
comparison_operator = "GreaterThanThreshold"
|
|
222
|
+
treat_missing_data = "notBreaching"
|
|
223
|
+
|
|
224
|
+
alarm_actions = var.chat_turn_latency_alarm_actions
|
|
225
|
+
ok_actions = var.chat_turn_latency_alarm_actions
|
|
226
|
+
|
|
227
|
+
dimensions = {
|
|
228
|
+
FunctionName = local.chat_latency_dispatch_fn
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
################################################################################
|
|
233
|
+
# Dashboard
|
|
234
|
+
################################################################################
|
|
235
|
+
|
|
236
|
+
locals {
|
|
237
|
+
chat_latency_dashboard_region = var.region
|
|
238
|
+
|
|
239
|
+
# No conditional wrapper here: the arms of `enabled ? concat(...) : []`
|
|
240
|
+
# cannot type-unify (a tuple of heterogeneous widget objects vs an empty
|
|
241
|
+
# tuple) and Terraform 1.9 fails the apply with "Inconsistent conditional
|
|
242
|
+
# result types" (canary.469 mcpherson deploy). The only consumer is the
|
|
243
|
+
# dashboard resource, whose count already gates on chat_latency_enabled, so
|
|
244
|
+
# this local is never evaluated when disabled. The runtime-sourced widget
|
|
245
|
+
# uses a for/if filter for the same reason.
|
|
246
|
+
chat_latency_widgets = concat(
|
|
247
|
+
[
|
|
248
|
+
{
|
|
249
|
+
type = "text"
|
|
250
|
+
x = 0
|
|
251
|
+
y = 0
|
|
252
|
+
width = 24
|
|
253
|
+
height = 3
|
|
254
|
+
properties = {
|
|
255
|
+
markdown = join("\n", [
|
|
256
|
+
"## Chat turn latency — ${var.stage}",
|
|
257
|
+
"",
|
|
258
|
+
"Turn path: **graphql-http** (sendMessage) → **chat-agent-invoke** (setup) → **agentcore-runtime-dispatch** → **AgentCore Runtime (Pi)** → **chat-agent-finalize**.",
|
|
259
|
+
"",
|
|
260
|
+
local.chat_latency_metric_filters_enabled ? "Phase metrics come from `agentcore_phase` log metric filters in `${local.chat_latency_namespace}`." : "**Phase metrics are OFF for this stage** (`enable_chat_turn_latency_metric_filters = false`) — the phase widgets stay empty. Lambda-native widgets below still populate.",
|
|
261
|
+
local.chat_latency_has_runtime_log_group ? "Runtime log group: `${local.chat_latency_runtime_log_group}`." : "**AgentCore runtime log group not configured** — set `chat_turn_runtime_log_group_name` to light up `AgentLoopMs` and the runtime-overhead widget.",
|
|
262
|
+
"",
|
|
263
|
+
"Timeline for a single turn: Logs Insights → saved query **thinkwork-${var.stage}-turn-timeline** (paste a `threadTurnId`).",
|
|
264
|
+
])
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
type = "metric"
|
|
269
|
+
x = 0
|
|
270
|
+
y = 3
|
|
271
|
+
width = 12
|
|
272
|
+
height = 6
|
|
273
|
+
properties = {
|
|
274
|
+
title = "Whole runtime call — api.runtime_dispatch.invoke (ms)"
|
|
275
|
+
region = local.chat_latency_dashboard_region
|
|
276
|
+
view = "timeSeries"
|
|
277
|
+
stat = "p50"
|
|
278
|
+
period = 300
|
|
279
|
+
metrics = [
|
|
280
|
+
[local.chat_latency_namespace, "TurnRuntimeInvokeMs", { stat = "p50", label = "p50" }],
|
|
281
|
+
["...", { stat = "p95", label = "p95" }],
|
|
282
|
+
]
|
|
283
|
+
yAxis = { left = { label = "ms", showUnits = false } }
|
|
284
|
+
annotations = {
|
|
285
|
+
horizontal = [{
|
|
286
|
+
label = "p95 alarm threshold"
|
|
287
|
+
value = var.chat_turn_p95_alarm_threshold_ms
|
|
288
|
+
}]
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
type = "metric"
|
|
294
|
+
x = 12
|
|
295
|
+
y = 3
|
|
296
|
+
width = 12
|
|
297
|
+
height = 6
|
|
298
|
+
properties = {
|
|
299
|
+
title = "Model loop — runtime.agent_loop (ms)"
|
|
300
|
+
region = local.chat_latency_dashboard_region
|
|
301
|
+
view = "timeSeries"
|
|
302
|
+
stat = "p50"
|
|
303
|
+
period = 300
|
|
304
|
+
metrics = [
|
|
305
|
+
[local.chat_latency_namespace, "AgentLoopMs", { stat = "p50", label = "p50" }],
|
|
306
|
+
["...", { stat = "p95", label = "p95" }],
|
|
307
|
+
]
|
|
308
|
+
yAxis = { left = { label = "ms", showUnits = false } }
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
type = "metric"
|
|
313
|
+
x = 0
|
|
314
|
+
y = 9
|
|
315
|
+
width = 12
|
|
316
|
+
height = 6
|
|
317
|
+
properties = {
|
|
318
|
+
title = "Runtime overhead p95 (invoke − agent_loop) — cold start lives here"
|
|
319
|
+
region = local.chat_latency_dashboard_region
|
|
320
|
+
view = "timeSeries"
|
|
321
|
+
period = 300
|
|
322
|
+
metrics = [
|
|
323
|
+
[{ expression = "invoke - loop", label = "overhead p95 (ms)", id = "overhead" }],
|
|
324
|
+
[local.chat_latency_namespace, "TurnRuntimeInvokeMs", { stat = "p95", id = "invoke", visible = false }],
|
|
325
|
+
[local.chat_latency_namespace, "AgentLoopMs", { stat = "p95", id = "loop", visible = false }],
|
|
326
|
+
]
|
|
327
|
+
yAxis = { left = { label = "ms", showUnits = false } }
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
type = "metric"
|
|
332
|
+
x = 12
|
|
333
|
+
y = 9
|
|
334
|
+
width = 12
|
|
335
|
+
height = 6
|
|
336
|
+
properties = {
|
|
337
|
+
title = "agentcore-runtime-dispatch Lambda Duration p95 (ms)"
|
|
338
|
+
region = local.chat_latency_dashboard_region
|
|
339
|
+
view = "timeSeries"
|
|
340
|
+
period = 300
|
|
341
|
+
metrics = [
|
|
342
|
+
["AWS/Lambda", "Duration", "FunctionName", local.chat_latency_dispatch_fn, { stat = "p95", label = "p95" }],
|
|
343
|
+
["...", { stat = "Maximum", label = "max" }],
|
|
344
|
+
]
|
|
345
|
+
yAxis = { left = { label = "ms", showUnits = false } }
|
|
346
|
+
annotations = {
|
|
347
|
+
horizontal = [{
|
|
348
|
+
label = "p95 alarm threshold"
|
|
349
|
+
value = var.chat_turn_p95_alarm_threshold_ms
|
|
350
|
+
}]
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
type = "metric"
|
|
356
|
+
x = 0
|
|
357
|
+
y = 15
|
|
358
|
+
width = 12
|
|
359
|
+
height = 6
|
|
360
|
+
properties = {
|
|
361
|
+
title = "Invocations (Sum) — turn path Lambdas"
|
|
362
|
+
region = local.chat_latency_dashboard_region
|
|
363
|
+
view = "timeSeries"
|
|
364
|
+
stat = "Sum"
|
|
365
|
+
period = 300
|
|
366
|
+
metrics = [
|
|
367
|
+
for fn in local.chat_latency_functions :
|
|
368
|
+
["AWS/Lambda", "Invocations", "FunctionName", fn, { label = fn }]
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
type = "metric"
|
|
374
|
+
x = 12
|
|
375
|
+
y = 15
|
|
376
|
+
width = 12
|
|
377
|
+
height = 6
|
|
378
|
+
properties = {
|
|
379
|
+
title = "Errors (Sum) — turn path Lambdas"
|
|
380
|
+
region = local.chat_latency_dashboard_region
|
|
381
|
+
view = "timeSeries"
|
|
382
|
+
stat = "Sum"
|
|
383
|
+
period = 300
|
|
384
|
+
metrics = [
|
|
385
|
+
for fn in local.chat_latency_functions :
|
|
386
|
+
["AWS/Lambda", "Errors", "FunctionName", fn, { label = fn }]
|
|
387
|
+
]
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
type = "metric"
|
|
392
|
+
x = 0
|
|
393
|
+
y = 21
|
|
394
|
+
width = 24
|
|
395
|
+
height = 6
|
|
396
|
+
properties = {
|
|
397
|
+
title = "Pre-dispatch queue — api.invoke.queue_delay (ms)"
|
|
398
|
+
region = local.chat_latency_dashboard_region
|
|
399
|
+
view = "timeSeries"
|
|
400
|
+
period = 300
|
|
401
|
+
metrics = [
|
|
402
|
+
[local.chat_latency_namespace, "PreDispatchQueueMs", { stat = "p50", label = "p50" }],
|
|
403
|
+
["...", { stat = "p95", label = "p95" }],
|
|
404
|
+
["...", { stat = "Maximum", label = "max (outlier class)" }],
|
|
405
|
+
]
|
|
406
|
+
yAxis = { left = { label = "ms", showUnits = false } }
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
],
|
|
410
|
+
[for w in [
|
|
411
|
+
{
|
|
412
|
+
type = "log"
|
|
413
|
+
x = 0
|
|
414
|
+
y = 27
|
|
415
|
+
width = 24
|
|
416
|
+
height = 6
|
|
417
|
+
properties = {
|
|
418
|
+
title = "Slowest turns (last 3h) — runtime call vs model loop"
|
|
419
|
+
region = local.chat_latency_dashboard_region
|
|
420
|
+
view = "table"
|
|
421
|
+
query = <<-EOT
|
|
422
|
+
SOURCE '${local.chat_latency_runtime_log_group}'
|
|
423
|
+
| fields ts, phase, durationMs, sessionId, threadTurnId
|
|
424
|
+
| filter event = "agentcore_phase" and status = "completed" and phase = "runtime.agent_loop"
|
|
425
|
+
| sort durationMs desc
|
|
426
|
+
| limit 20
|
|
427
|
+
EOT
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
] : w if local.chat_latency_has_runtime_log_group],
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
resource "aws_cloudwatch_dashboard" "chat_turn_latency" {
|
|
435
|
+
count = local.chat_latency_enabled ? 1 : 0
|
|
436
|
+
|
|
437
|
+
dashboard_name = "thinkwork-${var.stage}-chat-turn-latency"
|
|
438
|
+
dashboard_body = jsonencode({ widgets = local.chat_latency_widgets })
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
################################################################################
|
|
442
|
+
# Saved Logs Insights query — one turn's timeline across every log group
|
|
443
|
+
#
|
|
444
|
+
# Usage: open the query, replace REPLACE_WITH_THREAD_TURN_ID, run. `sessionId`
|
|
445
|
+
# is matched alongside `threadTurnId` because the container sets sessionId =
|
|
446
|
+
# threadTurnId and some early-turn API lines carry only one of the two.
|
|
447
|
+
################################################################################
|
|
448
|
+
|
|
449
|
+
resource "aws_cloudwatch_query_definition" "turn_timeline" {
|
|
450
|
+
count = local.chat_latency_enabled ? 1 : 0
|
|
451
|
+
|
|
452
|
+
name = "thinkwork-${var.stage}-turn-timeline"
|
|
453
|
+
|
|
454
|
+
log_group_names = local.chat_latency_has_runtime_log_group ? concat(
|
|
455
|
+
local.chat_latency_lambda_log_groups,
|
|
456
|
+
[local.chat_latency_runtime_log_group],
|
|
457
|
+
) : local.chat_latency_lambda_log_groups
|
|
458
|
+
|
|
459
|
+
query_string = <<-EOT
|
|
460
|
+
fields ts, @log, source, phase, status, durationMs, detail, errorType, threadTurnId, sessionId
|
|
461
|
+
| filter event = "agentcore_phase"
|
|
462
|
+
| filter threadTurnId = "REPLACE_WITH_THREAD_TURN_ID" or sessionId = "REPLACE_WITH_THREAD_TURN_ID"
|
|
463
|
+
| sort ts asc
|
|
464
|
+
| limit 500
|
|
465
|
+
EOT
|
|
466
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ---------------------------------------------------------------------------
|
|
2
|
+
# AgentCore runtime dispatch DLQ + redrive (THINK-585 U6, KTD2/R5/R19)
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
# The dispatcher Lambda runs Event-mode with retries=0 (the agent loop is not
|
|
5
|
+
# idempotent). An invoke that dies before the handler can mark its turn
|
|
6
|
+
# failed (crash, OOM, 900 s timeout) lands in this queue via the on-failure
|
|
7
|
+
# destination; the redrive consumer marks the enveloped thread_turn failed
|
|
8
|
+
# idempotently and emits the dispatch_dlq_redrive metric line for the U8
|
|
9
|
+
# dashboard. R19 hardening: SSE-KMS (AWS-managed key), ≤24 h retention.
|
|
10
|
+
|
|
11
|
+
resource "aws_sqs_queue" "agentcore_dispatch_dlq" {
|
|
12
|
+
count = local.deploy_lambda_handlers ? 1 : 0
|
|
13
|
+
|
|
14
|
+
name = "thinkwork-${var.stage}-agentcore-dispatch-dlq"
|
|
15
|
+
message_retention_seconds = 86400 # 24 h (R19)
|
|
16
|
+
# Must be ≥ the redrive consumer's 60 s timeout or CreateEventSourceMapping
|
|
17
|
+
# rejects the mapping; 6x is the AWS-recommended margin.
|
|
18
|
+
visibility_timeout_seconds = 360
|
|
19
|
+
kms_master_key_id = "alias/aws/sqs"
|
|
20
|
+
kms_data_key_reuse_period_seconds = 300
|
|
21
|
+
|
|
22
|
+
tags = {
|
|
23
|
+
Name = "thinkwork-${var.stage}-agentcore-dispatch-dlq"
|
|
24
|
+
Stage = var.stage
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Retries pinned to 0 (KTD2): a retried dispatch would re-burn Bedrock
|
|
29
|
+
# tokens and could overwrite the first attempt's deliverables. Failures go
|
|
30
|
+
# straight to the DLQ for the redrive consumer.
|
|
31
|
+
resource "aws_lambda_function_event_invoke_config" "agentcore_runtime_dispatch" {
|
|
32
|
+
count = local.deploy_lambda_handlers ? 1 : 0
|
|
33
|
+
function_name = aws_lambda_function.handler["agentcore-runtime-dispatch"].function_name
|
|
34
|
+
maximum_retry_attempts = 0
|
|
35
|
+
maximum_event_age_in_seconds = 3600
|
|
36
|
+
|
|
37
|
+
destination_config {
|
|
38
|
+
on_failure {
|
|
39
|
+
destination = aws_sqs_queue.agentcore_dispatch_dlq[0].arn
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
resource "aws_lambda_event_source_mapping" "agentcore_dispatch_dlq_redrive" {
|
|
45
|
+
count = local.deploy_lambda_handlers ? 1 : 0
|
|
46
|
+
|
|
47
|
+
event_source_arn = aws_sqs_queue.agentcore_dispatch_dlq[0].arn
|
|
48
|
+
function_name = aws_lambda_function.handler["agentcore-dispatch-dlq-redrive"].function_name
|
|
49
|
+
batch_size = 10
|
|
50
|
+
# Single consumer is plenty — the queue should be empty in steady state
|
|
51
|
+
# (non-empty is a soak-gate failure per R18).
|
|
52
|
+
scaling_config {
|
|
53
|
+
maximum_concurrency = 2
|
|
54
|
+
}
|
|
55
|
+
}
|