thinkwork-cli 0.13.1 → 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.
Files changed (262) hide show
  1. package/README.md +3 -14
  2. package/dist/{api-client-4DZ266J5.js → api-client-4P4JZOBX.js} +3 -1
  3. package/dist/bowser-6Z6JFAIR.js +2822 -0
  4. package/dist/{chunk-Y2WHJECR.js → chunk-4OMDWYZQ.js} +5 -44
  5. package/dist/chunk-AD2NIWB5.js +6516 -0
  6. package/dist/chunk-DV3MRBR5.js +43 -0
  7. package/dist/chunk-EI5ZX7CN.js +522 -0
  8. package/dist/chunk-EUJ7X6YD.js +126 -0
  9. package/dist/chunk-GG5WKBOX.js +436 -0
  10. package/dist/chunk-I64EZSU5.js +358 -0
  11. package/dist/chunk-KMIJ5KGT.js +128 -0
  12. package/dist/chunk-MWQ7RO76.js +137 -0
  13. package/dist/chunk-OCPHU34C.js +996 -0
  14. package/dist/chunk-OLUQ4U2Z.js +1728 -0
  15. package/dist/chunk-XYM7PE2A.js +2264 -0
  16. package/dist/chunk-YQCYD6NE.js +3299 -0
  17. package/dist/cli.js +49156 -31257
  18. package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +0 -1
  19. package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +0 -1
  20. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +0 -14
  21. package/dist/dist-es-2OTNTL4N.js +315 -0
  22. package/dist/dist-es-2XVAH4BQ.js +14726 -0
  23. package/dist/dist-es-3DPDUJAH.js +2000 -0
  24. package/dist/dist-es-BNZTBBVC.js +176 -0
  25. package/dist/dist-es-CD2RBDAC.js +88 -0
  26. package/dist/dist-es-I4FW57PS.js +88 -0
  27. package/dist/dist-es-KL7Y5NB2.js +377 -0
  28. package/dist/dist-es-KVFWJNDY.js +318 -0
  29. package/dist/dist-es-PS73DWNL.js +70 -0
  30. package/dist/dist-es-S3NZQFGT.js +170 -0
  31. package/dist/dist-es-SGCA4P32.js +48 -0
  32. package/dist/dist-es-WBM6J4RA.js +70 -0
  33. package/dist/dist-es-ZGVADYIF.js +485 -0
  34. package/dist/dist-es-ZKC2QKD2.js +377 -0
  35. package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +2 -1
  36. package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +1 -0
  37. package/dist/drizzle/0090_brain_schema_extraction.sql +10 -0
  38. package/dist/drizzle/0132_drop_computer_tables.sql +3 -0
  39. package/dist/drizzle/0160_compliance_event_types_plugins.sql +2 -0
  40. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +1 -0
  41. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +1 -0
  42. package/dist/drizzle/0227_analyst_reader_role.sql +17 -20
  43. package/dist/drizzle/0230_analyst_rls.sql +28 -36
  44. package/dist/drizzle/0250_kg_schema_extraction.sql +27 -0
  45. package/dist/drizzle/0251_brain_stragglers.sql +21 -0
  46. package/dist/drizzle/0253_drop_brain_substrate.sql +2 -0
  47. package/dist/drizzle/0257_agents_runtime_allow_harness.sql +24 -0
  48. package/dist/drizzle/0261_native_auth_control_plane.sql +332 -0
  49. package/dist/drizzle/0262_auth_subscription_tickets.sql +59 -0
  50. package/dist/drizzle/0263_agentcore_runtime_identifier.sql +69 -0
  51. package/dist/drizzle/0263_drop_workos_auth_runtime.sql +236 -0
  52. package/dist/drizzle/0264_auth_enrollment_attempt_limits.sql +15 -0
  53. package/dist/drizzle/0265_eval_runs_requester_identity.sql +28 -0
  54. package/dist/drizzle/0265_require_fully_valid_auth_publication.sql +27 -0
  55. package/dist/drizzle/0266_auth_identity_recovery_grants.sql +9 -0
  56. package/dist/drizzle/0266_eval_profiles_runtime_type.sql +12 -0
  57. package/dist/drizzle/0267_ontology_candidate_rejections.sql +53 -0
  58. package/dist/drizzle/0268_identity_crosswalk_routing.sql +182 -0
  59. package/dist/drizzle/0269_ontology_system_map.sql +31 -0
  60. package/dist/drizzle/0270_tool_execution_ledger.sql +115 -0
  61. package/dist/drizzle/0271_ontology_twin_declarations.sql +47 -0
  62. package/dist/drizzle/0272_identity_graph_projection_cursor.sql +23 -0
  63. package/dist/drizzle/0273_twin_materialization_suggestions.sql +29 -0
  64. package/dist/drizzle/0274_tenant_mcp_twin_keys.sql +59 -0
  65. package/dist/drizzle/0275_identity_bulk_rebuild_fence.sql +30 -0
  66. package/dist/drizzle/0276_tool_execution_ledger_allow_cascade_delete.sql +18 -0
  67. package/dist/drizzle/0277_knowledge_base_sources.sql +107 -0
  68. package/dist/drizzle/0278_kb_page_transcription.sql +55 -0
  69. package/dist/drizzle/0279_mcp_servers_off_plugin_provenance.sql +93 -0
  70. package/dist/drizzle/0280_work_item_external_refs_drop_plane_provider.sql +46 -0
  71. package/dist/drizzle/0281_tenant_mcp_twin_keys_expiry_suffix.sql +9 -0
  72. package/dist/drizzle/0282_tenant_mcp_twin_keys_grants.sql +24 -0
  73. package/dist/drizzle/0283_agents_agentcore_runtime_dispatch.sql +13 -0
  74. package/dist/drizzle/0284_user_brain_claims.sql +69 -0
  75. package/dist/drizzle/0285_tenant_mcp_twin_keys_trusted_subsystem.sql +30 -0
  76. package/dist/drizzle/0286_brain_analytics_key.sql +26 -0
  77. package/dist/drizzle/0287_brain_subject.sql +25 -0
  78. package/dist/drizzle/0288_multi_lane_identities.sql +25 -0
  79. package/dist/event-streams-7JNLJEEL.js +894 -0
  80. package/dist/event-streams-KDF5RWR6.js +1515 -0
  81. package/dist/loadSso-ETCXTR4I.js +576 -0
  82. package/dist/loadSso-UBD7KUHE.js +583 -0
  83. package/dist/plugins/README.md +2 -2
  84. package/dist/plugins/catalog/package.json +1 -5
  85. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -4
  86. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
  87. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -94
  88. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -8
  89. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -20
  90. package/dist/plugins/catalog/src/contracts.ts +17 -179
  91. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -24
  92. package/dist/plugins/n8n/test/integrated-app-contract.test.ts +1 -1
  93. package/dist/plugins/twenty/package.json +2 -1
  94. package/dist/plugins/twenty/scripts/LAMBDA_DEPLOY.md +114 -0
  95. package/dist/plugins/twenty/scripts/build-lambda.sh +29 -0
  96. package/dist/plugins/twenty/scripts/configure-product-views.ts +195 -0
  97. package/dist/plugins/twenty/scripts/lambda-handler.ts +49 -0
  98. package/dist/plugins/twenty/scripts/lib/__tests__/load-attachments.test.ts +38 -0
  99. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +145 -2
  100. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +397 -151
  101. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +28 -129
  102. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +10 -0
  103. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +13 -8
  104. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +222 -2
  105. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +29 -0
  106. package/dist/plugins/twenty/scripts/lib/load-records.ts +160 -43
  107. package/dist/plugins/twenty/scripts/lib/mappers.ts +368 -152
  108. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +43 -268
  109. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +57 -0
  110. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +136 -31
  111. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +254 -152
  112. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +44 -1
  113. package/dist/plugins/twenty/scripts/purge-lastmile-import.ts +178 -0
  114. package/dist/plugins/twenty/scripts/restructure-products.ts +312 -0
  115. package/dist/plugins/twenty/scripts/retag-stage-values.ts +362 -0
  116. package/dist/plugins/twenty/test/manifest.test.ts +2 -4
  117. package/dist/scripts/post-deploy.sh +79 -1
  118. package/dist/signin-5QZNRSOO.js +821 -0
  119. package/dist/sso-oidc-XK673P54.js +837 -0
  120. package/dist/sts-OXI2UALF.js +1113 -0
  121. package/dist/sts-SQW4NBK3.js +1117 -0
  122. package/dist/terraform/examples/greenfield/main.tf +391 -176
  123. package/dist/terraform/examples/greenfield/terraform.tfvars.example +26 -8
  124. package/dist/terraform/modules/app/agentcore-admin/main.tf +47 -0
  125. package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +39 -47
  126. package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +13 -66
  127. package/dist/terraform/modules/app/agentcore-identity/main.tf +253 -0
  128. package/dist/terraform/modules/app/agentcore-identity/outputs.tf +51 -0
  129. package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +239 -0
  130. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +22 -0
  131. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_twenty_identity.sh +16 -0
  132. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_workload_identity.sh +19 -0
  133. package/dist/terraform/modules/app/agentcore-identity/scripts/ensure_workload_identity.sh +29 -0
  134. package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +55 -0
  135. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_identity.sh +73 -0
  136. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +45 -0
  137. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +123 -0
  138. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/after.tf +15 -0
  139. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/before.tf +7 -0
  140. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-after.tf +8 -0
  141. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-before.tf +9 -0
  142. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-after.tf +3 -0
  143. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-before.tf +3 -0
  144. package/dist/terraform/modules/app/agentcore-identity/variables.tf +60 -0
  145. package/dist/terraform/modules/app/agentcore-memory/README.md +46 -9
  146. package/dist/terraform/modules/app/agentcore-memory/main.tf +27 -10
  147. package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +156 -24
  148. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  149. package/dist/terraform/modules/app/agentcore-pi/main.tf +42 -52
  150. package/dist/terraform/modules/app/agentcore-pi/outputs.tf +3 -3
  151. package/dist/terraform/modules/app/agentcore-pi/scripts/reconcile_pi_runtime.mjs +175 -0
  152. package/dist/terraform/modules/app/agentcore-pi/variables.tf +35 -28
  153. package/dist/terraform/modules/app/agentcore-platform/main.tf +68 -0
  154. package/dist/terraform/modules/app/agentcore-runtime/main.tf +4 -20
  155. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +127 -21
  156. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +0 -6
  157. package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +5 -0
  158. package/dist/terraform/modules/app/capability-broker/main.tf +2 -2
  159. package/dist/terraform/modules/app/capability-broker/variables.tf +11 -0
  160. package/dist/terraform/modules/app/deployment-control-plane/README.md +264 -0
  161. package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +4 -1
  162. package/dist/terraform/modules/app/deployment-control-plane/main.tf +94 -0
  163. package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +5 -0
  164. package/dist/terraform/modules/app/deployment-control-plane/runner.py +3689 -233
  165. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2579 -140
  166. package/dist/terraform/modules/app/deployment-control-plane/variables.tf +33 -0
  167. package/dist/terraform/modules/app/lambda-api/auth-state.tf +72 -0
  168. package/dist/terraform/modules/app/lambda-api/chat-latency-observability.tf +466 -0
  169. package/dist/terraform/modules/app/lambda-api/dispatch-dlq.tf +55 -0
  170. package/dist/terraform/modules/app/lambda-api/handlers.tf +531 -619
  171. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +350 -241
  172. package/dist/terraform/modules/app/lambda-api/main.tf +75 -13
  173. package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +3 -28
  174. package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +1 -2
  175. package/dist/terraform/modules/app/lambda-api/outputs.tf +17 -10
  176. package/dist/terraform/modules/app/lambda-api/runtime-config.tf +3 -21
  177. package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
  178. package/dist/terraform/modules/app/lambda-api/variables.tf +307 -121
  179. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/README.md +1 -1
  180. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/main.tf +18 -18
  181. package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +11 -10
  182. package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +5 -5
  183. package/dist/terraform/modules/app/static-site/main.tf +1 -1
  184. package/dist/terraform/modules/app/www-dns/main.tf +9 -27
  185. package/dist/terraform/modules/app/www-dns/variables.tf +4 -10
  186. package/dist/terraform/modules/data/aurora-postgres/README.md +34 -0
  187. package/dist/terraform/modules/data/aurora-postgres/alarms.tf +82 -0
  188. package/dist/terraform/modules/data/aurora-postgres/main.tf +27 -57
  189. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +0 -9
  190. package/dist/terraform/modules/data/aurora-postgres/variables.tf +41 -0
  191. package/dist/terraform/modules/data/s3-buckets/main.tf +5 -6
  192. package/dist/terraform/modules/foundation/cognito/main.tf +367 -72
  193. package/dist/terraform/modules/foundation/cognito/outputs.tf +47 -0
  194. package/dist/terraform/modules/foundation/cognito/variables.tf +131 -9
  195. package/dist/terraform/modules/thinkwork/README.md +5 -1
  196. package/dist/terraform/modules/thinkwork/main.tf +459 -234
  197. package/dist/terraform/modules/thinkwork/outputs.tf +44 -62
  198. package/dist/terraform/modules/thinkwork/variables.tf +353 -89
  199. package/dist/terraform/schema.graphql +69 -36
  200. package/dist/workspace-defaults/files/INSTRUCTIONS.md +9 -11
  201. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +38 -35
  202. package/dist/workspace-defaults/files/ROUTER.md +1 -1
  203. package/dist/workspace-defaults/files/TOOLS.md +9 -39
  204. package/dist/workspace-defaults/files/USER.md +1 -0
  205. package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +6 -3
  206. package/dist/workspace-defaults/files/catalog-skills/brain-brief-builder/SKILL.md +165 -0
  207. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/SKILL.md +75 -0
  208. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
  209. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
  210. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/workflow-authoring.md +46 -0
  211. package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +1 -1
  212. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +22 -3
  213. package/package.json +3 -2
  214. package/dist/drizzle/0261_thread_turns_origin_turn_index.sql +0 -11
  215. package/dist/plugins/company-data/README.md +0 -49
  216. package/dist/plugins/company-data/package.json +0 -21
  217. package/dist/plugins/company-data/src/index.ts +0 -30
  218. package/dist/plugins/company-data/src/manifest.ts +0 -26
  219. package/dist/plugins/company-data/test/manifest.test.ts +0 -116
  220. package/dist/plugins/company-data/tsconfig.json +0 -8
  221. package/dist/plugins/company-etl/README.md +0 -46
  222. package/dist/plugins/company-etl/package.json +0 -21
  223. package/dist/plugins/company-etl/src/index.ts +0 -29
  224. package/dist/plugins/company-etl/src/manifest.ts +0 -26
  225. package/dist/plugins/company-etl/test/manifest.test.ts +0 -108
  226. package/dist/plugins/company-etl/tsconfig.json +0 -8
  227. package/dist/plugins/lastmile/README.md +0 -50
  228. package/dist/plugins/lastmile/package.json +0 -23
  229. package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +0 -661
  230. package/dist/plugins/lastmile/src/api/tasks-adapter.ts +0 -572
  231. package/dist/plugins/lastmile/src/discovery.fixture.ts +0 -64
  232. package/dist/plugins/lastmile/src/index.ts +0 -46
  233. package/dist/plugins/lastmile/src/manifest.ts +0 -139
  234. package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +0 -266
  235. package/dist/plugins/lastmile/test/discovery.test.ts +0 -88
  236. package/dist/plugins/lastmile/tsconfig.json +0 -8
  237. package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +0 -409
  238. package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +0 -359
  239. package/dist/plugins/workos-auth/package.json +0 -22
  240. package/dist/plugins/workos-auth/src/index.ts +0 -30
  241. package/dist/plugins/workos-auth/src/manifest.ts +0 -40
  242. package/dist/plugins/workos-auth/src/provider-contract.ts +0 -39
  243. package/dist/plugins/workos-auth/test/manifest.test.ts +0 -94
  244. package/dist/plugins/workos-auth/tsconfig.json +0 -8
  245. package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +0 -61
  246. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +0 -227
  247. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +0 -277
  248. package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +0 -70
  249. package/dist/terraform/modules/app/agentcore-harness/main.tf +0 -101
  250. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -9
  251. package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -25
  252. package/dist/terraform/modules/app/hindsight-memory/README.md +0 -89
  253. package/dist/terraform/modules/app/hindsight-memory/main.tf +0 -411
  254. package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +0 -102
  255. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/outputs.tf +0 -0
  256. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/sync-database.py +0 -0
  257. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/test_sync_database.py +0 -0
  258. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/variables.tf +0 -0
  259. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/README.md +0 -0
  260. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/main.tf +0 -0
  261. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/outputs.tf +0 -0
  262. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/variables.tf +0 -0
@@ -13,12 +13,9 @@ account_id = "123456789012" # your AWS account ID
13
13
  database_engine = "rds-postgres"
14
14
 
15
15
  # Memory:
16
- # Hindsight is the canonical user and Space memory provider for full
17
- # ThinkWork installs and is enabled by default. Set enable_hindsight = false
18
- # and memory_engine = "agentcore" only for explicit low-cost/development
19
- # AgentCore-only deployments.
20
- # enable_hindsight = false
21
- # memory_engine = "agentcore"
16
+ # Bedrock AgentCore managed memory is the only engine. The legacy
17
+ # `enable_hindsight` / `memory_engine` inputs are deprecated no-ops that are
18
+ # still accepted so old tfvars keep applying.
22
19
 
23
20
  # Managed applications:
24
21
  # Twenty CRM is optional. First enable should set both booleans true after
@@ -72,10 +69,24 @@ db_password = "CHANGE_ME_strong_password_here"
72
69
  # Google OAuth (optional — leave empty to skip Google social login)
73
70
  # google_oauth_client_id = ""
74
71
  # google_oauth_client_secret = ""
72
+ # microsoft_oauth_client_id = ""
73
+ # microsoft_oauth_client_secret = "" # Supply securely; never commit a real value.
74
+ # microsoft_oauth_tenant = "" # Entra directory GUID, or common/organizations/consumers (Cognito-supported aliases).
75
+
76
+ # Tenant-specific Entra IdPs are reconciled by `thinkwork enterprise
77
+ # identity-provider`; Terraform receives safe provider metadata only.
78
+ # tenant_entra_connections = [{
79
+ # connection_key = "microsoft:tenant:00000000-0000-4000-8000-000000000000"
80
+ # tenant_id = "00000000-0000-4000-8000-000000000000"
81
+ # provider_name = "Entra_0000000000004000_ab12cd34"
82
+ # display_name = "Example organization"
83
+ # }]
75
84
 
76
85
  # Cognito trigger Lambdas (optional — leave empty if not using custom auth logic)
77
86
  # pre_signup_lambda_zip = "./lambdas/cognito-pre-signup.zip"
78
- # cognito_custom_auth_lambda_zip = "./lambdas/cognito-custom-auth.zip"
87
+ # auth_retirement_phase = "retired" # greenfield default; existing upgrades explicitly use coexistence -> cutover -> retired
88
+ # auth_migration_recovery_deadline = "2026-08-01T00:00:00Z" # required when auth_retirement_phase = "coexistence"
89
+ # cognito_custom_auth_lambda_zip = "./lambdas/cognito-custom-auth.zip" # rollback only
79
90
 
80
91
  # Lambda artifacts:
81
92
  # - Source checkout deploys can set lambda_zips_dir after `pnpm build:lambdas`.
@@ -101,7 +112,6 @@ db_password = "CHANGE_ME_strong_password_here"
101
112
  # --name /thinkwork/<stage>/google-places/api-key --value <KEY>
102
113
  # (terraform's lifecycle.ignore_changes on the value keeps your manual
103
114
  # value from being overwritten on future applies.)
104
- # google_places_api_key = ""
105
115
 
106
116
  # Public website (apps/www) custom domain.
107
117
  # Leave www_domain empty to skip the custom domain and serve on the raw
@@ -123,3 +133,11 @@ db_password = "CHANGE_ME_strong_password_here"
123
133
  # ses_inbound_domain = "agents.thinkwork.ai"
124
134
  # ses_parent_domain = "thinkwork.ai"
125
135
  # tenant_slugs = ["dev"]
136
+
137
+ # Company Brain platform-agent machine lane (optional, THINK-628/THINK-710).
138
+ # Secrets Manager ARN of etl-platform/brain-mcp/m2m/platform-agent in this
139
+ # account. When set, the shared API Lambda role gets read on it so MCP config
140
+ # build can mint short-lived Cognito bearers for the Brain connector instead
141
+ # of using a stored tkt_ key. Leave empty until the lane's manifest entry is
142
+ # published (see the Brain repo's brain-mcp-m2m-migration runbook).
143
+ # brain_m2m_platform_agent_secret_arn = "arn:aws:secretsmanager:us-east-1:<account-id>:secret:etl-platform/brain-mcp/m2m/platform-agent-XXXXXX"
@@ -100,6 +100,28 @@ variable "graphql_db_secret_arn" {
100
100
  type = string
101
101
  }
102
102
 
103
+ # ---- VPC placement (THINK-1082) ------------------------------------------------
104
+ # The handler opens DATABASE_URL against Aurora's public endpoint today. When
105
+ # in_vpc is on it attaches to the private subnets behind the api-lambda
106
+ # security group (which the database SG admits) so the public rule can close.
107
+ variable "in_vpc" {
108
+ description = "Attach the admin Lambda to the VPC (vpc_subnet_ids + vpc_security_group_ids); NAT-routed subnets required. Default off = no change."
109
+ type = bool
110
+ default = false
111
+ }
112
+
113
+ variable "vpc_subnet_ids" {
114
+ description = "Private (NAT-routed) subnet IDs for the admin Lambda when in_vpc is true."
115
+ type = list(string)
116
+ default = []
117
+ }
118
+
119
+ variable "vpc_security_group_ids" {
120
+ description = "Security groups for the admin Lambda when in_vpc is true."
121
+ type = list(string)
122
+ default = []
123
+ }
124
+
103
125
  # ---- Capability-private interpreter VPC placement (THINK-280 U4) ---------------
104
126
  # Empty when the capability broker is disabled → handler skips capability-private
105
127
  # provisioning. Sourced from the broker outputs via the code-interpreter module.
@@ -174,6 +196,14 @@ resource "aws_secretsmanager_secret_version" "admin_token" {
174
196
  # AWS SDK calls (see packages/lambda/agentcore-admin.ts).
175
197
  ################################################################################
176
198
 
199
+ # THINK-1082: ENI grant for the in_vpc lane; never created when off.
200
+ resource "aws_iam_role_policy_attachment" "admin_vpc_access" {
201
+ count = local.deploy && var.in_vpc ? 1 : 0
202
+
203
+ role = aws_iam_role.admin[0].name
204
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
205
+ }
206
+
177
207
  resource "aws_iam_role" "admin" {
178
208
  count = local.deploy ? 1 : 0
179
209
  name = "thinkwork-${var.stage}-agentcore-admin"
@@ -333,6 +363,23 @@ resource "aws_lambda_function" "admin" {
333
363
  }
334
364
  }
335
365
 
366
+ # THINK-1082: network-only VPC attachment; absent when in_vpc is off.
367
+ dynamic "vpc_config" {
368
+ for_each = var.in_vpc ? [1] : []
369
+
370
+ content {
371
+ subnet_ids = var.vpc_subnet_ids
372
+ security_group_ids = var.vpc_security_group_ids
373
+ }
374
+ }
375
+
376
+ lifecycle {
377
+ precondition {
378
+ condition = !var.in_vpc || (length(var.vpc_subnet_ids) > 0 && length(var.vpc_security_group_ids) > 0)
379
+ error_message = "in_vpc requires non-empty vpc_subnet_ids and vpc_security_group_ids."
380
+ }
381
+ }
382
+
336
383
  tags = {
337
384
  Name = local.function_name
338
385
  Stage = var.stage
@@ -1,53 +1,45 @@
1
- # `agentcore-code-interpreter` — stage-level base image substrate
1
+ # `agentcore-code-interpreter` — stage-level sandbox substrate
2
2
 
3
- Stage-scoped artifacts for the AgentCore Code Interpreter sandbox. **Per-tenant
3
+ Stage-scoped substrate for the AgentCore Code Interpreter sandbox. **Per-tenant
4
4
  Code Interpreter instances are created at runtime** by the `agentcore-admin`
5
5
  Lambda (see `docs/adrs/per-tenant-aws-resource-fanout.md`) — this module
6
- stops at the substrate everything else depends on.
7
-
8
- ## What it creates
9
-
10
- - **ECR repository** `thinkwork-{stage}-sandbox-base` immutable tags, last 10
11
- kept, scan-on-push.
12
- - **Outputs** consumed by downstream modules + the provisioning Lambda:
13
- - `ecr_repository_url` — where the blessed image lives.
14
- - `environment_ids` + `environments` — the v1 environment catalog (`default-public`,
15
- `internal-only`) with network modes.
16
- - `tenant_role_trust_policy_template` / `tenant_role_inline_policy_template`
17
- JSON templates the provisioning Lambda substitutes `{tenant_id}` into at
18
- `CreateRole` time (plan Unit 5).
19
-
20
- ## Files
21
-
22
- | File | Purpose |
23
- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
24
- | `main.tf` | ECR + outputs. No per-tenant resources. |
25
- | `Dockerfile.sandbox-base` | Python 3.12 + pinned libs + `sitecustomize.py`. Build context is the **repo root** so the COPY can reach the module-owned sandbox helper. |
26
- | `scripts/build_and_push_sandbox_base.sh` | CI builds + pushes. Not called from Terraform — image lifecycle is a reviewable-PR concern. |
27
-
28
- The Python source `sitecustomize.py` and its pytest suite `test_sitecustomize.py`
29
- live under `terraform/modules/app/agentcore-code-interpreter/sandbox/`, next to
30
- the sandbox base image they protect.
6
+ stops at the stage-level pieces everything else depends on.
7
+
8
+ ## What it provides
9
+
10
+ The module creates no AWS resources today; it is a typed catalog + policy-template
11
+ surface consumed by the provisioning Lambda and by downstream modules:
12
+
13
+ - `environment_ids` + `environments` — the environment catalog (`default-public`,
14
+ `internal-only`, `capability-private`) with network modes.
15
+ - `tenant_role_trust_policy_template` / `tenant_role_inline_policy_template`
16
+ JSON templates the provisioning Lambda substitutes `{tenant_id}` into at
17
+ `CreateRole` time (plan Unit 5).
18
+ - `capability_private_role_inline_policy_template` logs-only policy for the
19
+ THINK-280 U4 capability-private interpreter role.
20
+ - `capability_private_subnet_ids` / `capability_private_security_group_ids` —
21
+ pass-through of the broker's no-NAT VPC placement (empty when the broker is
22
+ disabled); wired into the `agentcore-admin` module.
23
+
24
+ ## No custom sandbox image (THINK-617)
25
+
26
+ This module used to own an ECR repo (`thinkwork-{stage}-sandbox-base`), a
27
+ `Dockerfile.sandbox-base` (Python 3.12 + pinned libs + a `sitecustomize.py`
28
+ stdio scrubber) and a CI build script. **AgentCore Code Interpreter cannot use
29
+ a custom image** — `CreateCodeInterpreterRequest` has no image/container
30
+ parameter (verified against `@aws-sdk/client-bedrock-agentcore-control`
31
+ 3.1103.0), and `agentcore-admin` has only ever created interpreters on the
32
+ AWS-managed image.
33
+
34
+ The practical need (openpyxl et al. in the sandbox) is covered by the runtime's
35
+ on-demand install preamble — `ON_DEMAND_LIBRARIES` in
36
+ `packages/agentcore-pi/agent-container/src/runtime/tools/execute-code.ts`.
37
+
38
+ Consequence for log scrubbing: the value-based in-image redactor never ran in
39
+ production. The `sandbox-log-scrubber` CloudWatch subscription filter
40
+ (pattern-based, known OAuth token shapes) is the only scrubbing layer.
31
41
 
32
42
  ## What this module does **not** do
33
43
 
34
- - Create per-tenant Code Interpreter instances — that's Unit 5 (agentcore-admin Lambda).
35
- - Build or push the Docker image — that's CI, via the shell script above.
36
- - Grant the provisioning Lambda IAM permissions on the new resources — that's added in Unit 5 when the Lambda resource lands.
37
-
38
- ## Bumping the image
39
-
40
- 1. Edit `Dockerfile.sandbox-base` (pin a new pandas / add a lib / etc.).
41
- 2. Edit `terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py` if the R13 invariant or its coverage gaps shift.
42
- 3. PR. CI runs `pytest` on the scrubber plus a build-then-startup-assertion smoke test.
43
- 4. After merge, `build_and_push_sandbox_base.sh` tags with the merge-commit SHA.
44
-
45
- ## Named residual
46
-
47
- The R13 invariant this module's scrubber enforces is **honestly scoped** to
48
- Python-stdio-mediated writes + known-shape CloudWatch patterns. See the
49
- brainstorm and the `sitecustomize.py` module docstring for the full list of
50
- stdio-bypass classes (os.write, subprocess env dumps, C-extension direct
51
- writes, multiprocessing workers, adversarial split-writes) that the Unit 12
52
- pattern backstop catches partially and the v2 in-process credential proxy
53
- addresses structurally.
44
+ - Create per-tenant Code Interpreter instances — that's the `agentcore-admin` Lambda.
45
+ - Build or push any container image.
@@ -1,23 +1,25 @@
1
1
  ################################################################################
2
2
  # AgentCore Code Interpreter — App Module (stage-level)
3
3
  #
4
- # Stage-scoped artifacts for the AgentCore Code Interpreter sandbox:
5
- # * ECR repository for the blessed sandbox base image (Python 3.12 +
6
- # pinned libs + sitecustomize.py R13 scrubber baked in).
7
- # * Lifecycle policy to trim old images.
8
- # * Outputs the per-tenant provisioning Lambda (Unit 5) consumes to
9
- # CreateCodeInterpreter for each tenant on demand.
4
+ # Stage-scoped substrate for the AgentCore Code Interpreter sandbox:
5
+ # * The environment catalog (network modes) the runtime selects from.
6
+ # * IAM policy templates + capability-private VPC placement the per-tenant
7
+ # provisioning Lambda (Unit 5) consumes to CreateCodeInterpreter for each
8
+ # tenant on demand.
10
9
  #
11
10
  # **Per-tenant resources live elsewhere.** AgentCore Code Interpreter
12
11
  # instances are created per-tenant by the ``agentcore-admin`` Lambda at
13
12
  # tenant-create time — see docs/adrs/per-tenant-aws-resource-fanout.md.
14
13
  # This module stops at the stage-level substrate.
15
14
  #
16
- # **Image build.** The Dockerfile.sandbox-base next to this file is built
17
- # and pushed by a CI job (scripts/build_and_push_sandbox_base.sh) not by
18
- # Terraform. Terraform owns the ECR repo and IAM plumbing; the image
19
- # lifecycle is intentionally owned by CI so a repository bump is a
20
- # reviewable PR rather than a ``terraform apply`` side-effect.
15
+ # **No custom image (THINK-617).** This module used to own an ECR repo for a
16
+ # "blessed" sandbox base image (Python 3.12 + pinned libs + a sitecustomize.py
17
+ # stdio scrubber). AgentCore Code Interpreter has no way to attach one:
18
+ # CreateCodeInterpreterRequest carries no image/container parameter. Sandboxes
19
+ # always run the AWS-managed image; libraries that image may lack are installed
20
+ # on demand by the runtime's execute_code preamble (ON_DEMAND_LIBRARIES in
21
+ # packages/agentcore-pi/.../tools/execute-code.ts). The image substrate was
22
+ # retired — git history and THINK-617 preserve it.
21
23
  ################################################################################
22
24
 
23
25
  terraform {
@@ -44,12 +46,6 @@ variable "account_id" {
44
46
  type = string
45
47
  }
46
48
 
47
- variable "image_retention_count" {
48
- description = "Keep the last N image tags in ECR; older ones are lifecycle-expired."
49
- type = number
50
- default = 10
51
- }
52
-
53
49
  # THINK-280 U4 — VPC placement for the capability-private interpreter. Sourced
54
50
  # (when the capability broker is enabled) from the broker module's no-NAT
55
51
  # interpreter subnets + dedicated egress-only SG. Empty by default: the
@@ -91,45 +87,6 @@ locals {
91
87
  }
92
88
  }
93
89
 
94
- ################################################################################
95
- # ECR repository — stage-level base image
96
- ################################################################################
97
-
98
- resource "aws_ecr_repository" "sandbox_base" {
99
- name = "thinkwork-${var.stage}-sandbox-base"
100
- image_tag_mutability = "IMMUTABLE"
101
- force_delete = true
102
-
103
- image_scanning_configuration {
104
- scan_on_push = true
105
- }
106
-
107
- tags = {
108
- Name = "thinkwork-${var.stage}-sandbox-base"
109
- Stage = var.stage
110
- Purpose = "agentcore-code-interpreter-base-image"
111
- }
112
- }
113
-
114
- resource "aws_ecr_lifecycle_policy" "sandbox_base" {
115
- repository = aws_ecr_repository.sandbox_base.name
116
-
117
- policy = jsonencode({
118
- rules = [{
119
- rulePriority = 1
120
- description = "Keep last ${var.image_retention_count} images"
121
- selection = {
122
- tagStatus = "any"
123
- countType = "imageCountMoreThan"
124
- countNumber = var.image_retention_count
125
- }
126
- action = {
127
- type = "expire"
128
- }
129
- }]
130
- })
131
- }
132
-
133
90
  ################################################################################
134
91
  # IAM policy document — per-tenant trust template
135
92
  #
@@ -207,16 +164,6 @@ locals {
207
164
  # Outputs
208
165
  ################################################################################
209
166
 
210
- output "ecr_repository_name" {
211
- description = "Name of the sandbox base image ECR repo."
212
- value = aws_ecr_repository.sandbox_base.name
213
- }
214
-
215
- output "ecr_repository_url" {
216
- description = "Full URL of the sandbox base image ECR repo."
217
- value = aws_ecr_repository.sandbox_base.repository_url
218
- }
219
-
220
167
  output "environment_ids" {
221
168
  description = "Enum of valid sandbox environment identifiers."
222
169
  value = keys(local.environments)
@@ -0,0 +1,253 @@
1
+ ################################################################################
2
+
3
+ terraform {
4
+ required_providers {
5
+ aws = {
6
+ source = "hashicorp/aws"
7
+ version = ">= 5.0"
8
+ }
9
+ external = {
10
+ source = "hashicorp/external"
11
+ version = ">= 2.3.0"
12
+ }
13
+ }
14
+ }
15
+ # THINK-316 U1 — AgentCore Identity proof substrate
16
+ #
17
+ # The AWS provider does not yet model Workload Identity or OAuth2 credential
18
+ # providers. A terraform_data lifecycle wrapper reconciles them with the
19
+ # current AWS CLI and owns destroy cleanup. The client secret is passed only in
20
+ # the local-exec environment and never printed by the scripts.
21
+ ################################################################################
22
+
23
+ locals {
24
+ # THINK-324: the workload identity is SHARED — live Twenty 3LO
25
+ # (lib/harness/agentcore-user-oauth.ts via AGENTCORE_IDENTITY_WORKLOAD_NAME)
26
+ # rides the same workload identity the proof plane created. It must exist
27
+ # whenever either half is enabled, and its deletion is owned by the Twenty
28
+ # cleanup owner (not the proof lifecycle) so retiring the proof plane never
29
+ # invalidates per-user Twenty grants.
30
+ identity_enabled = var.enabled || var.twenty_enabled
31
+ workload_identity_name = "thinkwork-${var.stage}-multiplayer-proof"
32
+ credential_provider_name = "thinkwork-${var.stage}-proof-oauth"
33
+ twenty_credential_provider_name = "thinkwork-${var.stage}-twenty-crm"
34
+ oauth_issuer = trimsuffix(var.oauth_issuer, "/")
35
+ # THINK-324: with the proof plane retired the issuer is empty; a bare
36
+ # "/complete" fails UpdateWorkloadIdentity's URL-pattern validation, so the
37
+ # proof return URL only joins the allowlist when an issuer is configured.
38
+ oauth_return_url = local.oauth_issuer != "" ? "${local.oauth_issuer}/complete" : ""
39
+ allowed_oauth_return_urls = distinct(concat(compact([local.oauth_return_url]), var.user_federation_return_urls))
40
+ configuration_hash = nonsensitive(sha256(jsonencode({
41
+ issuer = local.oauth_issuer
42
+ client_id = var.oauth_client_id
43
+ secret_digest = sha256(var.oauth_client_secret)
44
+ return_url = local.oauth_return_url
45
+ })))
46
+ twenty_configuration_hash = nonsensitive(sha256(jsonencode({
47
+ allowed_oauth_return_urls = local.allowed_oauth_return_urls
48
+ twenty_credential_provider_name = local.twenty_credential_provider_name
49
+ twenty_oauth_issuer = var.twenty_oauth_issuer
50
+ twenty_oauth_resource = var.twenty_oauth_resource
51
+ twenty_client_secret_arn = try(aws_secretsmanager_secret.twenty_oauth_client[0].arn, "")
52
+ bootstrap_script_sha256 = filesha256("${path.module}/scripts/bootstrap_twenty_oauth_client.sh")
53
+ provider_reconciler_sha256 = filesha256("${path.module}/scripts/reconcile_twenty_provider.mjs")
54
+ identity_reconciler_sha256 = filesha256("${path.module}/scripts/reconcile_twenty_identity.sh")
55
+ })))
56
+ }
57
+
58
+ # The DCR client secret is written by the reconciler, not Terraform, so its
59
+ # value never enters Terraform state. AgentCore Identity reads the JSON key
60
+ # directly and remains the sole custodian of each user's downstream grant.
61
+ resource "aws_secretsmanager_secret" "twenty_oauth_client" {
62
+ count = var.twenty_enabled ? 1 : 0
63
+
64
+ name = "thinkwork/${var.stage}/agentcore-identity/twenty-crm-oauth-client"
65
+ description = "Confidential Twenty OAuth client used only by AgentCore Identity Token Vault"
66
+ recovery_window_in_days = 7
67
+
68
+ tags = {
69
+ "managed-by" = "terraform"
70
+ "thinkwork:stage" = var.stage
71
+ "thinkwork:purpose" = "agentcore-identity-user-federation"
72
+ }
73
+ }
74
+
75
+ resource "aws_secretsmanager_secret_policy" "twenty_oauth_client" {
76
+ count = var.twenty_enabled ? 1 : 0
77
+ secret_arn = aws_secretsmanager_secret.twenty_oauth_client[0].arn
78
+ policy = jsonencode({
79
+ Version = "2012-10-17"
80
+ Statement = [{
81
+ Sid = "AgentCoreIdentityRead"
82
+ Effect = "Allow"
83
+ Principal = { Service = "bedrock-agentcore.amazonaws.com" }
84
+ Action = "secretsmanager:GetSecretValue"
85
+ Resource = aws_secretsmanager_secret.twenty_oauth_client[0].arn
86
+ Condition = {
87
+ StringEquals = { "aws:SourceAccount" = var.account_id }
88
+ }
89
+ }]
90
+ })
91
+ }
92
+
93
+ resource "terraform_data" "identity_lifecycle" {
94
+ count = var.enabled ? 1 : 0
95
+
96
+ input = {
97
+ region = var.region
98
+ workload_identity_name = local.workload_identity_name
99
+ credential_provider_name = local.credential_provider_name
100
+ }
101
+ triggers_replace = [local.configuration_hash]
102
+
103
+ provisioner "local-exec" {
104
+ command = "bash ${path.module}/scripts/reconcile_identity.sh"
105
+ environment = {
106
+ AWS_REGION = var.region
107
+ WORKLOAD_IDENTITY_NAME = local.workload_identity_name
108
+ CREDENTIAL_PROVIDER_NAME = local.credential_provider_name
109
+ OAUTH_ISSUER = local.oauth_issuer
110
+ OAUTH_CLIENT_ID = var.oauth_client_id
111
+ OAUTH_CLIENT_SECRET = var.oauth_client_secret
112
+ OAUTH_RETURN_URL = local.oauth_return_url
113
+ }
114
+ }
115
+
116
+ provisioner "local-exec" {
117
+ when = destroy
118
+ command = "bash ${path.module}/scripts/delete_identity.sh"
119
+ environment = {
120
+ AWS_REGION = self.input.region
121
+ WORKLOAD_IDENTITY_NAME = self.input.workload_identity_name
122
+ CREDENTIAL_PROVIDER_NAME = self.input.credential_provider_name
123
+ }
124
+ }
125
+ }
126
+
127
+ # THINK-324 — Twenty-side workload-identity guarantee. When the proof plane
128
+ # is retired (enabled = false) the identity_lifecycle resource above no longer
129
+ # runs, but live Twenty 3LO still needs the shared workload identity. This
130
+ # ensure step is create-if-missing only (it never rewrites the return-url
131
+ # allowlist — twenty_identity_reconciliation owns that) and has NO destroy
132
+ # action; deletion is owned by workload_identity_cleanup_owner below.
133
+ resource "terraform_data" "workload_identity_ensure" {
134
+ count = var.twenty_enabled ? 1 : 0
135
+
136
+ input = {
137
+ region = var.region
138
+ workload_identity_name = local.workload_identity_name
139
+ }
140
+
141
+ provisioner "local-exec" {
142
+ command = "bash ${path.module}/scripts/ensure_workload_identity.sh"
143
+ environment = {
144
+ AWS_REGION = var.region
145
+ WORKLOAD_IDENTITY_NAME = local.workload_identity_name
146
+ OAUTH_RETURN_URLS_JSON = jsonencode(local.allowed_oauth_return_urls)
147
+ }
148
+ }
149
+ }
150
+
151
+ # THINK-324 — the shared workload identity's deletion moved here from the
152
+ # proof lifecycle (delete_identity.sh no longer touches it): it is deleted
153
+ # only when the Twenty identity half itself is removed, never when the proof
154
+ # plane is retired. Note: on a hypothetical proof-only stage (enabled = true,
155
+ # twenty_enabled = false) the workload identity is leaked on teardown rather
156
+ # than risking a destructive replace of identity_lifecycle here; no such
157
+ # stage exists (dev, the only proof stage, runs Twenty).
158
+ resource "terraform_data" "workload_identity_cleanup_owner" {
159
+ count = var.twenty_enabled ? 1 : 0
160
+
161
+ input = {
162
+ region = var.region
163
+ workload_identity_name = local.workload_identity_name
164
+ }
165
+
166
+ provisioner "local-exec" {
167
+ when = destroy
168
+ command = "bash ${path.module}/scripts/delete_workload_identity.sh"
169
+ environment = {
170
+ AWS_REGION = self.input.region
171
+ WORKLOAD_IDENTITY_NAME = self.input.workload_identity_name
172
+ }
173
+ }
174
+ }
175
+
176
+ # Stable cleanup ownership is deliberately separate from reconciliation.
177
+ # Script-only changes must never replace a resource whose destroy provisioner
178
+ # deletes the external provider and invalidates its service-issued callback.
179
+ resource "terraform_data" "twenty_identity_cleanup_owner" {
180
+ count = var.twenty_enabled ? 1 : 0
181
+
182
+ input = {
183
+ region = var.region
184
+ twenty_credential_provider_name = local.twenty_credential_provider_name
185
+ }
186
+
187
+ provisioner "local-exec" {
188
+ when = destroy
189
+ command = "bash ${path.module}/scripts/delete_twenty_identity.sh"
190
+ environment = {
191
+ AWS_REGION = self.input.region
192
+ TWENTY_CREDENTIAL_PROVIDER_NAME = self.input.twenty_credential_provider_name
193
+ }
194
+ }
195
+ }
196
+
197
+ # Additive user-federation reconciliation. Code/configuration revisions replace
198
+ # only this terraform_data record; it has no destroy action and always updates
199
+ # the provider in place. The stable owner above deletes only when the module is
200
+ # truly disabled or removed.
201
+ resource "terraform_data" "twenty_identity_reconciliation" {
202
+ count = var.twenty_enabled ? 1 : 0
203
+
204
+ input = {
205
+ region = var.region
206
+ workload_identity_name = local.workload_identity_name
207
+ twenty_credential_provider_name = local.twenty_credential_provider_name
208
+ }
209
+ triggers_replace = [local.twenty_configuration_hash]
210
+
211
+ provisioner "local-exec" {
212
+ command = "bash ${path.module}/scripts/reconcile_twenty_identity.sh"
213
+ environment = {
214
+ AWS_REGION = var.region
215
+ WORKLOAD_IDENTITY_NAME = local.workload_identity_name
216
+ OAUTH_RETURN_URLS_JSON = jsonencode(local.allowed_oauth_return_urls)
217
+ TWENTY_CREDENTIAL_PROVIDER_NAME = local.twenty_credential_provider_name
218
+ TWENTY_CLIENT_SECRET_ARN = aws_secretsmanager_secret.twenty_oauth_client[0].arn
219
+ TWENTY_OAUTH_ISSUER = trimsuffix(var.twenty_oauth_issuer, "/")
220
+ TWENTY_OAUTH_RESOURCE = var.twenty_oauth_resource
221
+ }
222
+ }
223
+
224
+ depends_on = [
225
+ terraform_data.identity_lifecycle,
226
+ terraform_data.workload_identity_ensure,
227
+ terraform_data.twenty_identity_cleanup_owner,
228
+ aws_secretsmanager_secret_policy.twenty_oauth_client,
229
+ ]
230
+ }
231
+
232
+ # Read back the service-generated ARNs only after apply-time reconciliation.
233
+ # Gateway IAM can therefore scope vault and secret access exactly rather than
234
+ # granting a token-vault or Secrets Manager wildcard.
235
+ data "external" "identity_state" {
236
+ count = local.identity_enabled ? 1 : 0
237
+ depends_on = [
238
+ terraform_data.identity_lifecycle,
239
+ terraform_data.workload_identity_ensure,
240
+ terraform_data.twenty_identity_reconciliation,
241
+ ]
242
+ program = ["bash", "${path.module}/scripts/read_identity.sh"]
243
+
244
+ query = {
245
+ region = var.region
246
+ workload_identity_name = local.workload_identity_name
247
+ # Empty names tell the reader to skip that half (THINK-324: the proof
248
+ # provider is absent once the proof plane is retired; the Twenty provider
249
+ # is absent on proof-only stages).
250
+ credential_provider_name = var.enabled ? local.credential_provider_name : ""
251
+ twenty_credential_provider_name = var.twenty_enabled ? local.twenty_credential_provider_name : ""
252
+ }
253
+ }
@@ -0,0 +1,51 @@
1
+ output "workload_identity_name" {
2
+ description = "Name used for GetWorkloadAccessTokenForJWT. Empty when disabled."
3
+ value = local.identity_enabled ? local.workload_identity_name : ""
4
+ }
5
+
6
+ output "credential_provider_name" {
7
+ description = "AgentCore Identity OAuth2 credential-provider name. Empty when disabled."
8
+ value = var.enabled ? local.credential_provider_name : ""
9
+ }
10
+
11
+ output "oauth_return_url" {
12
+ description = "Allowed user-federation return URL. Empty when disabled."
13
+ value = var.enabled ? local.oauth_return_url : ""
14
+ }
15
+
16
+ output "twenty_credential_provider_name" {
17
+ description = "AgentCore Identity provider name for per-user Twenty grants."
18
+ value = var.twenty_enabled ? local.twenty_credential_provider_name : ""
19
+ }
20
+
21
+ output "twenty_credential_provider_arn" {
22
+ description = "AgentCore Identity provider ARN for per-user Twenty grants."
23
+ value = var.twenty_enabled ? data.external.identity_state[0].result.twenty_credential_provider_arn : ""
24
+ }
25
+
26
+ output "twenty_oauth_callback_url" {
27
+ description = "Service-generated callback URL registered with Twenty OAuth."
28
+ value = var.twenty_enabled ? data.external.identity_state[0].result.twenty_oauth_callback_url : ""
29
+ }
30
+
31
+ output "twenty_client_secret_arn" {
32
+ description = "External Secrets Manager client record consumed by AgentCore Identity."
33
+ value = var.twenty_enabled ? aws_secretsmanager_secret.twenty_oauth_client[0].arn : ""
34
+ sensitive = true
35
+ }
36
+
37
+ output "workload_identity_arn" {
38
+ description = "Service-generated workload identity ARN. Empty when disabled."
39
+ value = local.identity_enabled ? data.external.identity_state[0].result.workload_identity_arn : ""
40
+ }
41
+
42
+ output "credential_provider_arn" {
43
+ description = "Service-generated OAuth2 credential-provider ARN. Empty when disabled."
44
+ value = var.enabled ? data.external.identity_state[0].result.credential_provider_arn : ""
45
+ }
46
+
47
+ output "credential_secret_arn" {
48
+ description = "AgentCore-managed Secrets Manager ARN backing the provider. Empty when disabled."
49
+ value = var.enabled ? data.external.identity_state[0].result.credential_secret_arn : ""
50
+ sensitive = true
51
+ }