thinkwork-cli 0.13.1 → 0.13.2

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.
Files changed (130) hide show
  1. package/dist/{api-client-4DZ266J5.js → api-client-JYZXYQJR.js} +3 -1
  2. package/dist/bowser-6Z6JFAIR.js +2822 -0
  3. package/dist/chunk-DV3MRBR5.js +43 -0
  4. package/dist/chunk-DVPBI5HM.js +1728 -0
  5. package/dist/chunk-EI5ZX7CN.js +522 -0
  6. package/dist/chunk-EUJ7X6YD.js +126 -0
  7. package/dist/chunk-GG5WKBOX.js +436 -0
  8. package/dist/chunk-I64EZSU5.js +358 -0
  9. package/dist/chunk-ILZKG5WV.js +6151 -0
  10. package/dist/chunk-KMIJ5KGT.js +128 -0
  11. package/dist/chunk-OCPHU34C.js +996 -0
  12. package/dist/chunk-QOERT44A.js +3286 -0
  13. package/dist/chunk-XYM7PE2A.js +2264 -0
  14. package/dist/{chunk-Y2WHJECR.js → chunk-Z2F6RJFR.js} +1 -39
  15. package/dist/cli.js +43080 -13150
  16. package/dist/dist-es-CRV46D5B.js +315 -0
  17. package/dist/dist-es-DCY4IC3Y.js +2000 -0
  18. package/dist/dist-es-ENBUC3JP.js +14726 -0
  19. package/dist/dist-es-G54LBMRN.js +48 -0
  20. package/dist/dist-es-G7PTG75M.js +176 -0
  21. package/dist/dist-es-GKDBE7E6.js +88 -0
  22. package/dist/dist-es-HY3535OI.js +70 -0
  23. package/dist/dist-es-KL7Y5NB2.js +377 -0
  24. package/dist/dist-es-LDE437AM.js +170 -0
  25. package/dist/dist-es-P26FWFLT.js +485 -0
  26. package/dist/dist-es-Q57SZXZ7.js +70 -0
  27. package/dist/dist-es-R7FGL2IB.js +88 -0
  28. package/dist/dist-es-USORBXZ5.js +318 -0
  29. package/dist/dist-es-ZKC2QKD2.js +377 -0
  30. package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +2 -1
  31. package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +1 -0
  32. package/dist/drizzle/0090_brain_schema_extraction.sql +10 -0
  33. package/dist/drizzle/0132_drop_computer_tables.sql +3 -0
  34. package/dist/drizzle/0160_compliance_event_types_plugins.sql +2 -0
  35. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +1 -0
  36. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +1 -0
  37. package/dist/drizzle/0227_analyst_reader_role.sql +6 -0
  38. package/dist/drizzle/0230_analyst_rls.sql +9 -0
  39. package/dist/drizzle/0250_kg_schema_extraction.sql +27 -0
  40. package/dist/drizzle/0251_brain_stragglers.sql +21 -0
  41. package/dist/drizzle/0253_drop_brain_substrate.sql +2 -0
  42. package/dist/drizzle/0257_agents_runtime_allow_harness.sql +24 -0
  43. package/dist/drizzle/0261_managed_harness_multiplayer.sql +313 -0
  44. package/dist/drizzle/0261_native_auth_control_plane.sql +328 -0
  45. package/dist/drizzle/0262_auth_subscription_tickets.sql +59 -0
  46. package/dist/drizzle/0262_parallel_harness_threads.sql +9 -0
  47. package/dist/drizzle/0263_agentcore_runtime_identifier.sql +69 -0
  48. package/dist/drizzle/0263_drop_workos_auth_runtime.sql +236 -0
  49. package/dist/drizzle/0264_auth_enrollment_attempt_limits.sql +15 -0
  50. package/dist/drizzle/0264_harness_tool_execution_ledger.sql +113 -0
  51. package/dist/drizzle/0265_eval_runs_requester_identity.sql +28 -0
  52. package/dist/drizzle/0265_require_fully_valid_auth_publication.sql +27 -0
  53. package/dist/drizzle/0266_auth_identity_recovery_grants.sql +9 -0
  54. package/dist/drizzle/0266_eval_profiles_runtime_type.sql +12 -0
  55. package/dist/drizzle/0267_ontology_candidate_rejections.sql +53 -0
  56. package/dist/drizzle/0268_identity_crosswalk_routing.sql +182 -0
  57. package/dist/drizzle/0269_ontology_system_map.sql +31 -0
  58. package/dist/event-streams-7JNLJEEL.js +894 -0
  59. package/dist/event-streams-KDF5RWR6.js +1515 -0
  60. package/dist/loadSso-DJVB7BXE.js +576 -0
  61. package/dist/loadSso-JAMNA3WY.js +583 -0
  62. package/dist/plugins/catalog/package.json +1 -2
  63. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
  64. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +0 -69
  65. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
  66. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
  67. package/dist/plugins/catalog/src/contracts.ts +2 -179
  68. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
  69. package/dist/signin-UGDWTGS4.js +821 -0
  70. package/dist/sso-oidc-Q4VXANXY.js +837 -0
  71. package/dist/sts-CWLSE3GH.js +1241 -0
  72. package/dist/sts-DNZLNVOV.js +1245 -0
  73. package/dist/terraform/examples/greenfield/main.tf +223 -66
  74. package/dist/terraform/examples/greenfield/terraform.tfvars.example +15 -1
  75. package/dist/terraform/modules/app/agentcore-gateway/main.tf +305 -0
  76. package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +44 -0
  77. package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +93 -0
  78. package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +33 -0
  79. package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +543 -0
  80. package/dist/terraform/modules/app/agentcore-gateway/variables.tf +70 -0
  81. package/dist/terraform/modules/app/agentcore-harness/main.tf +237 -8
  82. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +63 -0
  83. package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +5 -0
  84. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +430 -0
  85. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +180 -0
  86. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +193 -0
  87. package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +5 -0
  88. package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +5 -0
  89. package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +5 -0
  90. package/dist/terraform/modules/app/agentcore-harness/variables.tf +72 -1
  91. package/dist/terraform/modules/app/agentcore-identity/main.tf +173 -0
  92. package/dist/terraform/modules/app/agentcore-identity/outputs.tf +51 -0
  93. package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +122 -0
  94. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +25 -0
  95. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_twenty_identity.sh +16 -0
  96. package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +40 -0
  97. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_identity.sh +73 -0
  98. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +74 -0
  99. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +61 -0
  100. package/dist/terraform/modules/app/agentcore-identity/variables.tf +54 -0
  101. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +127 -21
  102. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +0 -6
  103. package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +5 -0
  104. package/dist/terraform/modules/app/deployment-control-plane/runner.py +966 -27
  105. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +581 -22
  106. package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +88 -0
  107. package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +91 -0
  108. package/dist/terraform/modules/app/lambda-api/handlers.tf +298 -74
  109. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +99 -54
  110. package/dist/terraform/modules/app/lambda-api/main.tf +24 -0
  111. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +181 -4
  112. package/dist/terraform/modules/app/lambda-api/outputs.tf +35 -0
  113. package/dist/terraform/modules/app/lambda-api/runtime-config.tf +3 -21
  114. package/dist/terraform/modules/app/lambda-api/variables.tf +109 -16
  115. package/dist/terraform/modules/foundation/cognito/main.tf +294 -72
  116. package/dist/terraform/modules/foundation/cognito/outputs.tf +42 -0
  117. package/dist/terraform/modules/foundation/cognito/variables.tf +114 -4
  118. package/dist/terraform/modules/thinkwork/main.tf +269 -46
  119. package/dist/terraform/modules/thinkwork/outputs.tf +136 -8
  120. package/dist/terraform/modules/thinkwork/variables.tf +124 -17
  121. package/dist/terraform/schema.graphql +69 -36
  122. package/dist/workspace-defaults/files/USER.md +1 -0
  123. package/package.json +3 -2
  124. package/dist/drizzle/0261_thread_turns_origin_turn_index.sql +0 -11
  125. package/dist/plugins/workos-auth/package.json +0 -22
  126. package/dist/plugins/workos-auth/src/index.ts +0 -30
  127. package/dist/plugins/workos-auth/src/manifest.ts +0 -40
  128. package/dist/plugins/workos-auth/src/provider-contract.ts +0 -39
  129. package/dist/plugins/workos-auth/test/manifest.test.ts +0 -94
  130. package/dist/plugins/workos-auth/tsconfig.json +0 -8
@@ -181,6 +181,8 @@ locals {
181
181
  "cognito-idp:AdminRespondToAuthChallenge",
182
182
  "cognito-idp:AdminSetUserPassword",
183
183
  "cognito-idp:AdminUpdateUserAttributes",
184
+ "cognito-idp:DescribeIdentityProvider",
185
+ "cognito-idp:DescribeUserPoolClient",
184
186
  "cognito-idp:ListUsers",
185
187
  ]
186
188
  Resource = var.user_pool_arn
@@ -346,6 +348,7 @@ locals {
346
348
  # refresh path cannot replace the old manual AWS CLI step.
347
349
  var.deployment_evidence_bucket != "" ? [
348
350
  {
351
+ Sid = "DeploymentEvidenceBucket"
349
352
  Effect = "Allow"
350
353
  Action = [
351
354
  "s3:GetObject",
@@ -357,6 +360,12 @@ locals {
357
360
  "arn:aws:s3:::${var.deployment_evidence_bucket}/*",
358
361
  ]
359
362
  },
363
+ {
364
+ Sid = "AgentCoreHarnessGatewayOauthSecret"
365
+ Effect = "Allow"
366
+ Action = ["secretsmanager:GetSecretValue"]
367
+ Resource = ["arn:aws:secretsmanager:${var.region}:${var.account_id}:secret:bedrock-agentcore-identity!default/oauth2/thinkwork-${var.stage}-proof-oauth-*"]
368
+ },
360
369
  ] : [],
361
370
  var.billing_export_bucket_name != "" ? [
362
371
  {
@@ -524,6 +533,17 @@ locals {
524
533
  # wiki-bootstrap-import: bootstrapJournalImport admin mutation
525
534
  # Event-invokes this for the long-running ingest path.
526
535
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-wiki-bootstrap-import",
536
+ # harness-runner: chat-agent-invoke Event-invokes this for chat
537
+ # turns routed to the AWS AgentCore runtime (THINK-311 trial).
538
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-harness-runner",
539
+ # harness-runner queues the same canonical post-turn memory pipeline
540
+ # as Pi after a completed AgentCore turn. The retain ledger makes the
541
+ # Event invoke idempotent on the thread-turn source key.
542
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-memory-retain",
543
+ # harness-runner RequestResponse-invokes this dedicated signer. The
544
+ # signer re-derives the actor/tenant/thread tuple from the running
545
+ # turn and alone holds KMS Sign; the shared runner role never does.
546
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-turn-assertion-mint",
527
547
  # ontology-scan: startOntologySuggestionScan Event-invokes this
528
548
  # after inserting a durable scan job row.
529
549
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-ontology-scan",
@@ -887,14 +907,14 @@ locals {
887
907
  Resource = "*"
888
908
  },
889
909
  ],
890
- # THINK-311 U4AgentCore Harness trial invoker grants, present only when
891
- # the agentcore-harness module is enabled (empty role ARN = module off).
892
- # Ship-inert: no handler calls Harness until U5's harness-runner lands.
910
+ # THINK-316 U2request path receives data-plane invocation only.
911
+ # Provisioning/version/endpoint/PassRole rights belong to Terraform's
912
+ # dedicated Harness lifecycle and never reach a chat Lambda.
893
913
  #
894
914
  # CRITICAL: never grant `bedrock-agentcore:InvokeAgentRuntimeCommand`
895
915
  # anywhere — it bypasses Harness allowedTools enforcement. Invocation goes
896
916
  # through InvokeHarness / InvokeAgentRuntime only.
897
- var.agentcore_harness_execution_role_arn != "" ? [
917
+ var.enable_agentcore_harness_invocation ? [
898
918
  {
899
919
  # Data plane — invoke a Harness (and its runtime endpoints) directly.
900
920
  Sid = "AgentCoreHarnessInvoke"
@@ -903,45 +923,27 @@ locals {
903
923
  "bedrock-agentcore:InvokeHarness",
904
924
  "bedrock-agentcore:InvokeAgentRuntime",
905
925
  ]
906
- Resource = "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:harness/*"
926
+ Resource = ["arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:harness/*"]
907
927
  },
908
928
  {
909
- # Control plane CreateHarness has no resource ARN at create time
910
- # (same non-scopable shape as bedrock:CreateKnowledgeBase above), so
911
- # this statement is "*"; PassRole below stays scoped to the single
912
- # Harness execution role.
913
- Sid = "AgentCoreHarnessControl"
929
+ # The deterministic report adapter uses the same AgentCore Identity
930
+ # chain as Harness Gateway outbound auth: turn JWT -> workload token
931
+ # -> OBO Gateway token. No provider client secret enters Lambda.
932
+ Sid = "AgentCoreHarnessGatewayIdentity"
914
933
  Effect = "Allow"
915
934
  Action = [
916
- "bedrock-agentcore:CreateHarness",
917
- "bedrock-agentcore:GetHarness",
918
- "bedrock-agentcore:UpdateHarness",
919
- "bedrock-agentcore:DeleteHarness",
920
- "bedrock-agentcore:CreateHarnessEndpoint",
921
- "bedrock-agentcore:GetHarnessEndpoint",
922
- "bedrock-agentcore:ListHarnessEndpoints",
923
- "bedrock-agentcore:ListHarnesses",
924
- "bedrock-agentcore:ListHarnessVersions",
935
+ "bedrock-agentcore:GetWorkloadAccessTokenForJWT",
936
+ "bedrock-agentcore:GetWorkloadAccessTokenForUserId",
937
+ "bedrock-agentcore:GetResourceOauth2Token",
938
+ "bedrock-agentcore:CompleteResourceTokenAuth",
939
+ ]
940
+ Resource = [
941
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default",
942
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default/workload-identity/thinkwork-${var.stage}-multiplayer-proof",
943
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:token-vault/default",
944
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:token-vault/default/oauth2credentialprovider/thinkwork-${var.stage}-proof-oauth",
945
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:token-vault/default/oauth2credentialprovider/thinkwork-${var.stage}-twenty-crm",
925
946
  ]
926
- Resource = "*"
927
- },
928
- ] : [],
929
- # (Separate conditional from the two statements above only because the
930
- # Condition attribute gives this object a different Terraform type —
931
- # same gate, same feature.)
932
- var.agentcore_harness_execution_role_arn != "" ? [
933
- {
934
- # CreateHarness takes executionRoleArn; the caller must be able to
935
- # pass exactly that role, and only to the bedrock-agentcore service.
936
- Sid = "AgentCoreHarnessPassExecutionRole"
937
- Effect = "Allow"
938
- Action = ["iam:PassRole"]
939
- Resource = var.agentcore_harness_execution_role_arn
940
- Condition = {
941
- StringEquals = {
942
- "iam:PassedToService" = "bedrock-agentcore.amazonaws.com"
943
- }
944
- }
945
947
  },
946
948
  ] : [],
947
949
  )
@@ -1040,6 +1042,33 @@ locals {
1040
1042
  )
1041
1043
 
1042
1044
  api_observability_statements = concat(local.api_observability_sqs_statements, [
1045
+ # Backend notification publishing is IAM-only. Scope the shared API
1046
+ # execution role to the NONE-resolver mutation fields; end-user identity-
1047
+ # pool credentials have no AppSync grant. This statement lives in the
1048
+ # observability envelope for size balance: adding subscription invalidation
1049
+ # pushed the data-plane policy over IAM's 6,144-character hard cap.
1050
+ {
1051
+ Sid = "PublishAppSyncNotifications"
1052
+ Effect = "Allow"
1053
+ Action = ["appsync:GraphQL"]
1054
+ Resource = [
1055
+ for field in [
1056
+ "notifyAgentStatus",
1057
+ "notifyNewMessage",
1058
+ "notifyHeartbeatActivity",
1059
+ "notifyThreadActivity",
1060
+ "notifyThreadUpdate",
1061
+ "notifyInboxItemUpdate",
1062
+ "notifyThreadTurnUpdate",
1063
+ "notifyThreadTurnStep",
1064
+ "notifyOrgUpdate",
1065
+ "notifyCostRecorded",
1066
+ "notifyEvalRunUpdate",
1067
+ "notifyWorkspaceAccessRevoked",
1068
+ "invalidateSubscription",
1069
+ ] : "arn:aws:appsync:${var.region}:${var.account_id}:apis/${var.appsync_api_id}/types/Mutation/fields/${field}"
1070
+ ]
1071
+ },
1043
1072
  # (was inline policy "cloudwatch-logs-read")
1044
1073
  {
1045
1074
  Effect = "Allow"
@@ -1083,16 +1112,41 @@ locals {
1083
1112
  Resource = "*"
1084
1113
  },
1085
1114
  ])
1115
+
1116
+ api_grouped_policy_documents = {
1117
+ data_plane = jsonencode({
1118
+ Version = "2012-10-17"
1119
+ Statement = local.api_data_plane_statements
1120
+ })
1121
+ orchestration = jsonencode({
1122
+ Version = "2012-10-17"
1123
+ Statement = local.api_orchestration_statements
1124
+ })
1125
+ ai = jsonencode({
1126
+ Version = "2012-10-17"
1127
+ Statement = local.api_ai_statements
1128
+ })
1129
+ observability = jsonencode({
1130
+ Version = "2012-10-17"
1131
+ Statement = local.api_observability_statements
1132
+ })
1133
+ }
1134
+ }
1135
+
1136
+ check "api_grouped_managed_policy_sizes" {
1137
+ assert {
1138
+ condition = alltrue([
1139
+ for document in values(local.api_grouped_policy_documents) : length(document) <= 6144
1140
+ ])
1141
+ error_message = "Each grouped API managed-policy document must remain within AWS IAM's 6,144-character limit."
1142
+ }
1086
1143
  }
1087
1144
 
1088
1145
  resource "aws_iam_policy" "api_data_plane" {
1089
1146
  name = "thinkwork-${var.stage}-api-data-plane"
1090
1147
  description = "Grouped data-plane grants (RDS Data API, Secrets Manager, S3, DynamoDB, Cognito, SSM, KMS-via-SSM, SQS) for the shared api Lambda role"
1091
1148
 
1092
- policy = jsonencode({
1093
- Version = "2012-10-17"
1094
- Statement = local.api_data_plane_statements
1095
- })
1149
+ policy = local.api_grouped_policy_documents.data_plane
1096
1150
  }
1097
1151
 
1098
1152
  resource "aws_iam_role_policy_attachment" "api_data_plane" {
@@ -1104,10 +1158,7 @@ resource "aws_iam_policy" "api_orchestration" {
1104
1158
  name = "thinkwork-${var.stage}-api-orchestration"
1105
1159
  description = "Grouped orchestration grants (Lambda invoke, EventBridge Scheduler, Step Functions, SES) for the shared api Lambda role"
1106
1160
 
1107
- policy = jsonencode({
1108
- Version = "2012-10-17"
1109
- Statement = local.api_orchestration_statements
1110
- })
1161
+ policy = local.api_grouped_policy_documents.orchestration
1111
1162
  }
1112
1163
 
1113
1164
  resource "aws_iam_role_policy_attachment" "api_orchestration" {
@@ -1119,10 +1170,7 @@ resource "aws_iam_policy" "api_ai" {
1119
1170
  name = "thinkwork-${var.stage}-api-ai"
1120
1171
  description = "Grouped AI grants (Bedrock invoke, Knowledge Bases, AgentCore memory/eval/code-interpreter) for the shared api Lambda role"
1121
1172
 
1122
- policy = jsonencode({
1123
- Version = "2012-10-17"
1124
- Statement = local.api_ai_statements
1125
- })
1173
+ policy = local.api_grouped_policy_documents.ai
1126
1174
  }
1127
1175
 
1128
1176
  resource "aws_iam_role_policy_attachment" "api_ai" {
@@ -1134,10 +1182,7 @@ resource "aws_iam_policy" "api_observability" {
1134
1182
  name = "thinkwork-${var.stage}-api-observability"
1135
1183
  description = "Grouped observability grants (CloudWatch Logs reads, ECS/ALB health reads) for the shared api Lambda role"
1136
1184
 
1137
- policy = jsonencode({
1138
- Version = "2012-10-17"
1139
- Statement = local.api_observability_statements
1140
- })
1185
+ policy = local.api_grouped_policy_documents.observability
1141
1186
  }
1142
1187
 
1143
1188
  resource "aws_iam_role_policy_attachment" "api_observability" {
@@ -73,6 +73,14 @@ resource "aws_apigatewayv2_stage" "default" {
73
73
  throttling_burst_limit = 20
74
74
  }
75
75
 
76
+ # Session revocation is authenticated and independently rate-limited in the
77
+ # handler per principal. This edge throttle also bounds aggregate abuse.
78
+ route_settings {
79
+ route_key = "POST /api/auth/revoke"
80
+ throttling_rate_limit = 5
81
+ throttling_burst_limit = 10
82
+ }
83
+
76
84
  # UpdateStage 404s ("Unable to find Route by key … within the provided
77
85
  # RouteSettings") if the stage update races the route creation — Terraform
78
86
  # sees no reference edge between route_settings.route_key and the route
@@ -256,3 +264,19 @@ resource "aws_lambda_permission" "placeholder_apigw" {
256
264
  principal = "apigateway.amazonaws.com"
257
265
  source_arn = "${aws_apigatewayv2_api.main.execution_arn}/*/*"
258
266
  }
267
+ resource "terraform_data" "auth_migration_recovery_deadline_guard" {
268
+ input = {
269
+ phase = var.auth_retirement_phase
270
+ deadline = var.auth_migration_recovery_deadline
271
+ }
272
+
273
+ lifecycle {
274
+ precondition {
275
+ condition = var.auth_retirement_phase != "coexistence" || (
276
+ trimspace(var.auth_migration_recovery_deadline) != "" &&
277
+ can(timecmp(var.auth_migration_recovery_deadline, var.auth_migration_recovery_deadline))
278
+ )
279
+ error_message = "auth_migration_recovery_deadline must be an RFC3339 timestamp when auth_retirement_phase is coexistence."
280
+ }
281
+ }
282
+ }
@@ -1,8 +1,185 @@
1
1
  locals {
2
- mcp_oauth_api_base_url = "https://${aws_apigatewayv2_api.main.id}.execute-api.${var.region}.amazonaws.com"
3
- mcp_oauth_cognito_base_url = var.cognito_auth_domain != "" ? "https://${var.cognito_auth_domain}.auth.${var.region}.amazoncognito.com" : ""
4
- mcp_oauth_identity_providers = var.google_oauth_client_id != "" ? ["Google", "COGNITO"] : ["COGNITO"]
5
- mcp_oauth_logo_path = "${path.module}/../../../../apps/web/public/logo.png"
2
+ mcp_oauth_api_base_url = "https://${aws_apigatewayv2_api.main.id}.execute-api.${var.region}.amazonaws.com"
3
+ mcp_oauth_cognito_base_url = var.cognito_auth_domain != "" ? "https://${var.cognito_auth_domain}.auth.${var.region}.amazoncognito.com" : ""
4
+ mcp_oauth_identity_providers = var.google_oauth_client_id != "" ? ["Google", "COGNITO"] : ["COGNITO"]
5
+ mcp_oauth_logo_path = "${path.module}/../../../../apps/web/public/logo.png"
6
+ agentcore_turn_assertion_issuer = "${local.mcp_oauth_api_base_url}/agentcore"
7
+ agentcore_harness_audience = "urn:thinkwork:${var.stage}:agentcore:harness"
8
+ agentcore_gateway_audience = "urn:thinkwork:${var.stage}:agentcore:gateway"
9
+ agentcore_turn_assertion_keys = {
10
+ for version, key in aws_kms_key.agentcore_turn_assertion : version => {
11
+ key_id = key.arn
12
+ # A version label alone is not globally unique: destroying and
13
+ # reprovisioning v1 creates different RSA material while downstream JWT
14
+ # validators may still cache the old public key under the same kid.
15
+ # Bind the kid to the immutable KMS key id so a reprovision is observed
16
+ # as a real key rotation instead of an invalid-signature outage.
17
+ kid = "thinkwork-${var.stage}-agentcore-turn-${version}-${substr(key.key_id, 0, 8)}"
18
+ }
19
+ }
20
+ agentcore_turn_assertion_active_key = try(
21
+ local.agentcore_turn_assertion_keys[var.agentcore_turn_assertion_active_key_version],
22
+ { key_id = "", kid = "" },
23
+ )
24
+ }
25
+
26
+ # THINK-316 U1: short-lived CUSTOM_JWT assertions are signed by KMS so private
27
+ # key material never enters Lambda configuration or process memory. The key
28
+ # policy is the capability boundary: the dedicated mint role can Sign, while
29
+ # the shared mcp-oauth role can only fetch the public key for JWKS publication.
30
+ resource "aws_iam_role" "turn_assertion_mint" {
31
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
32
+ name = "thinkwork-${var.stage}-turn-assertion-mint-role"
33
+
34
+ assume_role_policy = jsonencode({
35
+ Version = "2012-10-17"
36
+ Statement = [{
37
+ Effect = "Allow"
38
+ Principal = { Service = "lambda.amazonaws.com" }
39
+ Action = "sts:AssumeRole"
40
+ }]
41
+ })
42
+ }
43
+
44
+ resource "aws_iam_role_policy_attachment" "turn_assertion_mint_basic" {
45
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
46
+ role = aws_iam_role.turn_assertion_mint[0].name
47
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
48
+ }
49
+
50
+ # The synthetic OAuth provider and boundary target are public proof surfaces
51
+ # but need no AWS data-plane permissions. A sibling logs-only role prevents a
52
+ # parser defect from inheriting the shared API role or its platform secrets.
53
+ resource "aws_iam_role" "agentcore_proof_boundary" {
54
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
55
+ name = "thinkwork-${var.stage}-agentcore-proof-boundary-role"
56
+
57
+ assume_role_policy = jsonencode({
58
+ Version = "2012-10-17"
59
+ Statement = [{
60
+ Effect = "Allow"
61
+ Principal = { Service = "lambda.amazonaws.com" }
62
+ Action = "sts:AssumeRole"
63
+ }]
64
+ })
65
+ }
66
+
67
+ resource "aws_iam_role_policy_attachment" "agentcore_proof_boundary_basic" {
68
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
69
+ role = aws_iam_role.agentcore_proof_boundary[0].name
70
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
71
+ }
72
+
73
+ # The token-exchange provider is the only proof boundary allowed to mint the
74
+ # derived Gateway assertion. Keep that capability off the public target role.
75
+ resource "aws_iam_role" "agentcore_proof_provider" {
76
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
77
+ name = "thinkwork-${var.stage}-agentcore-proof-provider-role"
78
+
79
+ assume_role_policy = jsonencode({
80
+ Version = "2012-10-17"
81
+ Statement = [{
82
+ Effect = "Allow"
83
+ Principal = { Service = "lambda.amazonaws.com" }
84
+ Action = "sts:AssumeRole"
85
+ }]
86
+ })
87
+ }
88
+
89
+ resource "aws_iam_role_policy_attachment" "agentcore_proof_provider_basic" {
90
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
91
+ role = aws_iam_role.agentcore_proof_provider[0].name
92
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
93
+ }
94
+
95
+ resource "aws_iam_role_policy" "agentcore_proof_provider_kms" {
96
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
97
+ name = "thinkwork-${var.stage}-agentcore-proof-provider-key"
98
+ role = aws_iam_role.agentcore_proof_provider[0].id
99
+
100
+ policy = jsonencode({
101
+ Version = "2012-10-17"
102
+ Statement = [{
103
+ Effect = "Allow"
104
+ Action = ["kms:Sign"]
105
+ Resource = [local.agentcore_turn_assertion_active_key.key_id]
106
+ Condition = {
107
+ StringEquals = {
108
+ "kms:SigningAlgorithm" = "RSASSA_PKCS1_V1_5_SHA_256"
109
+ }
110
+ }
111
+ }]
112
+ })
113
+ }
114
+
115
+ resource "aws_kms_key" "agentcore_turn_assertion" {
116
+ for_each = var.enable_agentcore_multiplayer_proof ? toset(var.agentcore_turn_assertion_key_versions) : toset([])
117
+
118
+ description = "ThinkWork ${var.stage} AgentCore per-turn assertion signing ${each.key}"
119
+ key_usage = "SIGN_VERIFY"
120
+ customer_master_key_spec = "RSA_2048"
121
+ deletion_window_in_days = 7
122
+
123
+ policy = jsonencode({
124
+ Version = "2012-10-17"
125
+ Statement = [
126
+ {
127
+ Sid = "AccountAdministration"
128
+ Effect = "Allow"
129
+ Principal = { AWS = "arn:aws:iam::${var.account_id}:root" }
130
+ Action = "kms:*"
131
+ Resource = "*"
132
+ },
133
+ ]
134
+ })
135
+
136
+ tags = {
137
+ Name = "thinkwork-${var.stage}-agentcore-turn-assertion-${each.key}"
138
+ KeyVersion = each.key
139
+ }
140
+ }
141
+
142
+ resource "aws_kms_alias" "agentcore_turn_assertion" {
143
+ for_each = aws_kms_key.agentcore_turn_assertion
144
+ name = "alias/thinkwork-${var.stage}-agentcore-turn-assertion-${each.key}"
145
+ target_key_id = each.value.key_id
146
+ }
147
+
148
+ # The mint role can sign with exactly the active key. The public discovery
149
+ # handler can read all currently published public keys, but can never sign.
150
+ resource "aws_iam_role_policy" "turn_assertion_mint_kms" {
151
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
152
+ name = "thinkwork-${var.stage}-turn-assertion-active-key"
153
+ role = aws_iam_role.turn_assertion_mint[0].id
154
+
155
+ policy = jsonencode({
156
+ Version = "2012-10-17"
157
+ Statement = [{
158
+ Effect = "Allow"
159
+ Action = ["kms:Sign"]
160
+ Resource = [local.agentcore_turn_assertion_active_key.key_id]
161
+ Condition = {
162
+ StringEquals = {
163
+ "kms:SigningAlgorithm" = "RSASSA_PKCS1_V1_5_SHA_256"
164
+ }
165
+ }
166
+ }]
167
+ })
168
+ }
169
+
170
+ resource "aws_iam_role_policy" "turn_assertion_jwks_kms" {
171
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
172
+ name = "thinkwork-${var.stage}-turn-assertion-published-keys"
173
+ role = aws_iam_role.lambda.id
174
+
175
+ policy = jsonencode({
176
+ Version = "2012-10-17"
177
+ Statement = [{
178
+ Effect = "Allow"
179
+ Action = ["kms:GetPublicKey"]
180
+ Resource = [for key in values(local.agentcore_turn_assertion_keys) : key.key_id]
181
+ }]
182
+ })
6
183
  }
7
184
 
8
185
  resource "aws_dynamodb_table" "mcp_oauth_revocations" {
@@ -8,6 +8,41 @@ output "api_endpoint" {
8
8
  value = local.api_base_url
9
9
  }
10
10
 
11
+ output "agentcore_turn_assertion_issuer" {
12
+ description = "Proof-only CUSTOM_JWT issuer on the regional HTTP API."
13
+ value = var.enable_agentcore_multiplayer_proof ? local.agentcore_turn_assertion_issuer : ""
14
+ }
15
+
16
+ output "agentcore_harness_audience" {
17
+ description = "Purpose-bound audience for proof Harness invocations."
18
+ value = var.enable_agentcore_multiplayer_proof ? local.agentcore_harness_audience : ""
19
+ }
20
+
21
+ output "agentcore_gateway_audience" {
22
+ description = "Purpose-bound audience for proof Gateway operations."
23
+ value = var.enable_agentcore_multiplayer_proof ? local.agentcore_gateway_audience : ""
24
+ }
25
+
26
+ output "agentcore_turn_assertion_active_key_arn" {
27
+ description = "Operator-only active proof signing key ARN used by deployed verification probes."
28
+ value = var.enable_agentcore_multiplayer_proof ? local.agentcore_turn_assertion_active_key.key_id : ""
29
+ }
30
+
31
+ output "agentcore_turn_assertion_active_kid" {
32
+ description = "Public JWKS key identifier for the active proof signing key."
33
+ value = var.enable_agentcore_multiplayer_proof ? local.agentcore_turn_assertion_active_key.kid : ""
34
+ }
35
+
36
+ output "agentcore_proof_oauth_issuer" {
37
+ description = "Synthetic proof OAuth issuer on the regional HTTP API."
38
+ value = var.enable_agentcore_multiplayer_proof ? "${local.mcp_oauth_api_base_url}/agentcore-proof/oauth" : ""
39
+ }
40
+
41
+ output "agentcore_proof_target_base_url" {
42
+ description = "Regional HTTP API base URL hosting the controlled identity target."
43
+ value = var.enable_agentcore_multiplayer_proof ? local.mcp_oauth_api_base_url : ""
44
+ }
45
+
11
46
  output "api_execution_arn" {
12
47
  description = "API Gateway V2 execution ARN (for Lambda permissions)"
13
48
  value = aws_apigatewayv2_api.main.execution_arn
@@ -51,9 +51,9 @@ locals {
51
51
  # ----------------------------------------------------------------------------
52
52
  # Platform secrets (plan 2026-06-11-006 U5/R4)
53
53
  #
54
- # API_AUTH_SECRET and APPSYNC_API_KEY leave plaintext Lambda env: the
55
- # runtime-config loader prefetches these at cold start (getApiAuthSecret /
56
- # getAppsyncApiKey accessors, env-wins during the transition window). Names
54
+ # API_AUTH_SECRET leaves plaintext Lambda env: the runtime-config loader
55
+ # prefetches it at cold start (getApiAuthSecret, env-wins during the transition
56
+ # window). Names
57
57
  # use the `thinkwork/${stage}/...` prefix so the shared role's existing
58
58
  # `secretsmanager:GetSecretValue` grant on `thinkwork/*` covers them.
59
59
  # The env copies in common_env are dropped one release AFTER this ships
@@ -83,24 +83,6 @@ resource "aws_secretsmanager_secret_version" "api_auth" {
83
83
  # console.
84
84
  }
85
85
 
86
- resource "aws_secretsmanager_secret" "appsync_api_key" {
87
- name = "thinkwork/${var.stage}/appsync-api-key"
88
- description = "AppSync API key for subscription notify fan-out. Prefetched at Lambda cold start by @thinkwork/runtime-config."
89
- tags = {
90
- Name = "thinkwork-${var.stage}-appsync-api-key"
91
- Stage = var.stage
92
- }
93
- }
94
-
95
- resource "aws_secretsmanager_secret_version" "appsync_api_key" {
96
- secret_id = aws_secretsmanager_secret.appsync_api_key.id
97
- secret_string = var.appsync_api_key
98
-
99
- # AppSync keys are terraform-rotated (the appsync module recreates them),
100
- # so track the var here — unlike api_auth there is no operator rotation
101
- # path outside terraform.
102
- }
103
-
104
86
  # The standalone compliance Lambdas (anchor, export-runner) bundle the
105
87
  # runtime-config loader but run under dedicated roles that lack the shared
106
88
  # role's ssm grant. Without these, every cold start auto-prime fails