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.
Files changed (231) hide show
  1. package/README.md +3 -14
  2. package/dist/{api-client-JYZXYQJR.js → api-client-4P4JZOBX.js} +1 -1
  3. package/dist/{chunk-Z2F6RJFR.js → chunk-4OMDWYZQ.js} +4 -5
  4. package/dist/{chunk-ILZKG5WV.js → chunk-AD2NIWB5.js} +434 -69
  5. package/dist/chunk-MWQ7RO76.js +137 -0
  6. package/dist/{chunk-DVPBI5HM.js → chunk-OLUQ4U2Z.js} +26 -26
  7. package/dist/{chunk-QOERT44A.js → chunk-YQCYD6NE.js} +57 -44
  8. package/dist/cli.js +4400 -16431
  9. package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +0 -1
  10. package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +0 -1
  11. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +0 -14
  12. package/dist/{dist-es-CRV46D5B.js → dist-es-2OTNTL4N.js} +4 -4
  13. package/dist/{dist-es-ENBUC3JP.js → dist-es-2XVAH4BQ.js} +2 -2
  14. package/dist/{dist-es-DCY4IC3Y.js → dist-es-3DPDUJAH.js} +2 -2
  15. package/dist/{dist-es-G7PTG75M.js → dist-es-BNZTBBVC.js} +1 -1
  16. package/dist/{dist-es-GKDBE7E6.js → dist-es-CD2RBDAC.js} +1 -1
  17. package/dist/{dist-es-R7FGL2IB.js → dist-es-I4FW57PS.js} +1 -1
  18. package/dist/{dist-es-USORBXZ5.js → dist-es-KVFWJNDY.js} +4 -4
  19. package/dist/{dist-es-HY3535OI.js → dist-es-PS73DWNL.js} +2 -2
  20. package/dist/{dist-es-LDE437AM.js → dist-es-S3NZQFGT.js} +1 -1
  21. package/dist/{dist-es-G54LBMRN.js → dist-es-SGCA4P32.js} +1 -1
  22. package/dist/{dist-es-Q57SZXZ7.js → dist-es-WBM6J4RA.js} +2 -2
  23. package/dist/{dist-es-P26FWFLT.js → dist-es-ZGVADYIF.js} +8 -8
  24. package/dist/drizzle/0227_analyst_reader_role.sql +17 -26
  25. package/dist/drizzle/0230_analyst_rls.sql +28 -45
  26. package/dist/drizzle/0261_native_auth_control_plane.sql +7 -3
  27. package/dist/drizzle/0270_tool_execution_ledger.sql +115 -0
  28. package/dist/drizzle/0271_ontology_twin_declarations.sql +47 -0
  29. package/dist/drizzle/0272_identity_graph_projection_cursor.sql +23 -0
  30. package/dist/drizzle/0273_twin_materialization_suggestions.sql +29 -0
  31. package/dist/drizzle/0274_tenant_mcp_twin_keys.sql +59 -0
  32. package/dist/drizzle/0275_identity_bulk_rebuild_fence.sql +30 -0
  33. package/dist/drizzle/0276_tool_execution_ledger_allow_cascade_delete.sql +18 -0
  34. package/dist/drizzle/0277_knowledge_base_sources.sql +107 -0
  35. package/dist/drizzle/0278_kb_page_transcription.sql +55 -0
  36. package/dist/drizzle/0279_mcp_servers_off_plugin_provenance.sql +93 -0
  37. package/dist/drizzle/0280_work_item_external_refs_drop_plane_provider.sql +46 -0
  38. package/dist/drizzle/0281_tenant_mcp_twin_keys_expiry_suffix.sql +9 -0
  39. package/dist/drizzle/0282_tenant_mcp_twin_keys_grants.sql +24 -0
  40. package/dist/drizzle/0283_agents_agentcore_runtime_dispatch.sql +13 -0
  41. package/dist/drizzle/0284_user_brain_claims.sql +69 -0
  42. package/dist/drizzle/0285_tenant_mcp_twin_keys_trusted_subsystem.sql +30 -0
  43. package/dist/drizzle/0286_brain_analytics_key.sql +26 -0
  44. package/dist/drizzle/0287_brain_subject.sql +25 -0
  45. package/dist/drizzle/0288_multi_lane_identities.sql +25 -0
  46. package/dist/{loadSso-DJVB7BXE.js → loadSso-ETCXTR4I.js} +2 -2
  47. package/dist/{loadSso-JAMNA3WY.js → loadSso-UBD7KUHE.js} +2 -2
  48. package/dist/plugins/README.md +2 -2
  49. package/dist/plugins/catalog/package.json +0 -3
  50. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -3
  51. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
  52. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -25
  53. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -6
  54. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -15
  55. package/dist/plugins/catalog/src/contracts.ts +15 -0
  56. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -18
  57. package/dist/plugins/n8n/test/integrated-app-contract.test.ts +1 -1
  58. package/dist/plugins/twenty/package.json +2 -1
  59. package/dist/plugins/twenty/scripts/LAMBDA_DEPLOY.md +114 -0
  60. package/dist/plugins/twenty/scripts/build-lambda.sh +29 -0
  61. package/dist/plugins/twenty/scripts/configure-product-views.ts +195 -0
  62. package/dist/plugins/twenty/scripts/lambda-handler.ts +49 -0
  63. package/dist/plugins/twenty/scripts/lib/__tests__/load-attachments.test.ts +38 -0
  64. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +145 -2
  65. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +397 -151
  66. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +28 -129
  67. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +10 -0
  68. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +13 -8
  69. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +222 -2
  70. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +29 -0
  71. package/dist/plugins/twenty/scripts/lib/load-records.ts +160 -43
  72. package/dist/plugins/twenty/scripts/lib/mappers.ts +368 -152
  73. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +43 -268
  74. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +57 -0
  75. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +136 -31
  76. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +254 -152
  77. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +44 -1
  78. package/dist/plugins/twenty/scripts/purge-lastmile-import.ts +178 -0
  79. package/dist/plugins/twenty/scripts/restructure-products.ts +312 -0
  80. package/dist/plugins/twenty/scripts/retag-stage-values.ts +362 -0
  81. package/dist/plugins/twenty/test/manifest.test.ts +2 -4
  82. package/dist/scripts/post-deploy.sh +79 -1
  83. package/dist/{signin-UGDWTGS4.js → signin-5QZNRSOO.js} +2 -2
  84. package/dist/{sso-oidc-Q4VXANXY.js → sso-oidc-XK673P54.js} +2 -2
  85. package/dist/{sts-CWLSE3GH.js → sts-OXI2UALF.js} +6 -134
  86. package/dist/{sts-DNZLNVOV.js → sts-SQW4NBK3.js} +6 -134
  87. package/dist/terraform/examples/greenfield/main.tf +233 -175
  88. package/dist/terraform/examples/greenfield/terraform.tfvars.example +12 -8
  89. package/dist/terraform/modules/app/agentcore-admin/main.tf +47 -0
  90. package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +39 -47
  91. package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +13 -66
  92. package/dist/terraform/modules/app/agentcore-identity/main.tf +106 -26
  93. package/dist/terraform/modules/app/agentcore-identity/outputs.tf +6 -6
  94. package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +216 -99
  95. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +6 -9
  96. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_workload_identity.sh +19 -0
  97. package/dist/terraform/modules/app/agentcore-identity/scripts/ensure_workload_identity.sh +29 -0
  98. package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +22 -7
  99. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +17 -46
  100. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +65 -3
  101. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/after.tf +15 -0
  102. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/before.tf +7 -0
  103. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-after.tf +8 -0
  104. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-before.tf +9 -0
  105. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-after.tf +3 -0
  106. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-before.tf +3 -0
  107. package/dist/terraform/modules/app/agentcore-identity/variables.tf +6 -0
  108. package/dist/terraform/modules/app/agentcore-memory/README.md +46 -9
  109. package/dist/terraform/modules/app/agentcore-memory/main.tf +27 -10
  110. package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +156 -24
  111. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  112. package/dist/terraform/modules/app/agentcore-pi/main.tf +42 -52
  113. package/dist/terraform/modules/app/agentcore-pi/outputs.tf +3 -3
  114. package/dist/terraform/modules/app/agentcore-pi/scripts/reconcile_pi_runtime.mjs +175 -0
  115. package/dist/terraform/modules/app/agentcore-pi/variables.tf +35 -28
  116. package/dist/terraform/modules/app/agentcore-platform/main.tf +68 -0
  117. package/dist/terraform/modules/app/agentcore-runtime/main.tf +4 -20
  118. package/dist/terraform/modules/app/capability-broker/main.tf +2 -2
  119. package/dist/terraform/modules/app/capability-broker/variables.tf +11 -0
  120. package/dist/terraform/modules/app/deployment-control-plane/README.md +264 -0
  121. package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +4 -1
  122. package/dist/terraform/modules/app/deployment-control-plane/main.tf +94 -0
  123. package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +5 -0
  124. package/dist/terraform/modules/app/deployment-control-plane/runner.py +2641 -124
  125. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2034 -154
  126. package/dist/terraform/modules/app/deployment-control-plane/variables.tf +33 -0
  127. package/dist/terraform/modules/app/lambda-api/auth-state.tf +72 -0
  128. package/dist/terraform/modules/app/lambda-api/chat-latency-observability.tf +466 -0
  129. package/dist/terraform/modules/app/lambda-api/dispatch-dlq.tf +55 -0
  130. package/dist/terraform/modules/app/lambda-api/handlers.tf +411 -723
  131. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +262 -198
  132. package/dist/terraform/modules/app/lambda-api/main.tf +51 -13
  133. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +4 -181
  134. package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +3 -28
  135. package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +1 -2
  136. package/dist/terraform/modules/app/lambda-api/outputs.tf +17 -45
  137. package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
  138. package/dist/terraform/modules/app/lambda-api/variables.tf +219 -126
  139. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/README.md +1 -1
  140. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/main.tf +18 -18
  141. package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +11 -10
  142. package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +5 -5
  143. package/dist/terraform/modules/app/static-site/main.tf +1 -1
  144. package/dist/terraform/modules/app/www-dns/main.tf +9 -27
  145. package/dist/terraform/modules/app/www-dns/variables.tf +4 -10
  146. package/dist/terraform/modules/data/aurora-postgres/README.md +34 -0
  147. package/dist/terraform/modules/data/aurora-postgres/alarms.tf +82 -0
  148. package/dist/terraform/modules/data/aurora-postgres/main.tf +27 -57
  149. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +0 -9
  150. package/dist/terraform/modules/data/aurora-postgres/variables.tf +41 -0
  151. package/dist/terraform/modules/data/s3-buckets/main.tf +5 -6
  152. package/dist/terraform/modules/foundation/cognito/main.tf +81 -8
  153. package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
  154. package/dist/terraform/modules/foundation/cognito/variables.tf +24 -12
  155. package/dist/terraform/modules/thinkwork/README.md +5 -1
  156. package/dist/terraform/modules/thinkwork/main.tf +376 -374
  157. package/dist/terraform/modules/thinkwork/outputs.tf +12 -158
  158. package/dist/terraform/modules/thinkwork/variables.tf +246 -89
  159. package/dist/workspace-defaults/files/INSTRUCTIONS.md +9 -11
  160. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +38 -35
  161. package/dist/workspace-defaults/files/ROUTER.md +1 -1
  162. package/dist/workspace-defaults/files/TOOLS.md +9 -39
  163. package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +6 -3
  164. package/dist/workspace-defaults/files/catalog-skills/brain-brief-builder/SKILL.md +165 -0
  165. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/SKILL.md +75 -0
  166. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
  167. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
  168. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/workflow-authoring.md +46 -0
  169. package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +1 -1
  170. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +22 -3
  171. package/package.json +1 -1
  172. package/dist/drizzle/0261_managed_harness_multiplayer.sql +0 -313
  173. package/dist/drizzle/0262_parallel_harness_threads.sql +0 -9
  174. package/dist/drizzle/0264_harness_tool_execution_ledger.sql +0 -113
  175. package/dist/plugins/company-data/README.md +0 -49
  176. package/dist/plugins/company-data/package.json +0 -21
  177. package/dist/plugins/company-data/src/index.ts +0 -30
  178. package/dist/plugins/company-data/src/manifest.ts +0 -26
  179. package/dist/plugins/company-data/test/manifest.test.ts +0 -116
  180. package/dist/plugins/company-data/tsconfig.json +0 -8
  181. package/dist/plugins/company-etl/README.md +0 -46
  182. package/dist/plugins/company-etl/package.json +0 -21
  183. package/dist/plugins/company-etl/src/index.ts +0 -29
  184. package/dist/plugins/company-etl/src/manifest.ts +0 -26
  185. package/dist/plugins/company-etl/test/manifest.test.ts +0 -108
  186. package/dist/plugins/company-etl/tsconfig.json +0 -8
  187. package/dist/plugins/lastmile/README.md +0 -50
  188. package/dist/plugins/lastmile/package.json +0 -23
  189. package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +0 -661
  190. package/dist/plugins/lastmile/src/api/tasks-adapter.ts +0 -572
  191. package/dist/plugins/lastmile/src/discovery.fixture.ts +0 -64
  192. package/dist/plugins/lastmile/src/index.ts +0 -46
  193. package/dist/plugins/lastmile/src/manifest.ts +0 -139
  194. package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +0 -266
  195. package/dist/plugins/lastmile/test/discovery.test.ts +0 -88
  196. package/dist/plugins/lastmile/tsconfig.json +0 -8
  197. package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +0 -409
  198. package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +0 -359
  199. package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +0 -61
  200. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +0 -227
  201. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +0 -277
  202. package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +0 -70
  203. package/dist/terraform/modules/app/agentcore-gateway/main.tf +0 -305
  204. package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +0 -44
  205. package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +0 -93
  206. package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +0 -33
  207. package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +0 -543
  208. package/dist/terraform/modules/app/agentcore-gateway/variables.tf +0 -70
  209. package/dist/terraform/modules/app/agentcore-harness/main.tf +0 -330
  210. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -72
  211. package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +0 -5
  212. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +0 -430
  213. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +0 -180
  214. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +0 -193
  215. package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +0 -5
  216. package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +0 -5
  217. package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +0 -5
  218. package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -96
  219. package/dist/terraform/modules/app/hindsight-memory/README.md +0 -89
  220. package/dist/terraform/modules/app/hindsight-memory/main.tf +0 -411
  221. package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +0 -88
  222. package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +0 -91
  223. package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +0 -102
  224. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/outputs.tf +0 -0
  225. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/sync-database.py +0 -0
  226. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/test_sync_database.py +0 -0
  227. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/variables.tf +0 -0
  228. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/README.md +0 -0
  229. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/main.tf +0 -0
  230. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/outputs.tf +0 -0
  231. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/variables.tf +0 -0
@@ -53,11 +53,6 @@ locals {
53
53
  : module.vpc.private_subnet_ids
54
54
  )
55
55
 
56
- # Hindsight is the canonical user/Space memory provider for full ThinkWork
57
- # installs. AgentCore managed memory remains available as an explicit
58
- # low-cost/development opt-out. We still honor var.memory_engine == "hindsight"
59
- # so existing tfvars keep working even when enable_hindsight was not set.
60
- hindsight_enabled = var.enable_hindsight || var.memory_engine == "hindsight"
61
56
  twenty_provisioned = var.twenty_provisioned
62
57
  twenty_runtime_enabled = var.twenty_provisioned && var.twenty_runtime_enabled
63
58
  twenty_domain = var.twenty_domain != "" ? var.twenty_domain : (var.www_domain != "" ? "crm.${var.www_domain}" : "")
@@ -83,8 +78,28 @@ locals {
83
78
  module.vpc.private_route_table_ids,
84
79
  module.vpc.public_route_table_ids,
85
80
  )
86
- okf_wiki_private_endpoints_enabled = var.okf_wiki_efs_enabled && var.okf_wiki_create_vpc_endpoints
87
- okf_wiki_interface_endpoint_services = toset(concat(
81
+ # THINK-589 PR 1 (deprecate-to-no-op): the OKF wiki EFS feature is dead.
82
+ # Every EFS consumer below (client/EFS security groups, NFS rule, mount
83
+ # targets, access points, Pi/hydrator wiring) is hard-conditioned off via
84
+ # this local regardless of the deprecated okf_wiki_* inputs. The EFS
85
+ # filesystem itself and the NAT gateway are intentionally NOT conditioned
86
+ # off: destroying the filesystem is PR 2's gated delete, and the NAT is
87
+ # shared VPC egress that live non-OKF workloads ride.
88
+ #
89
+ # The VPC interface endpoints were re-homed out of the OKF lane on
90
+ # 2026-08-28 (the vpc_interface_* resources below — they were only ever
91
+ # "okf-wiki" by accident of being created by the first VPC-attached
92
+ # workload). They still ride the okf_wiki_* enable gates for stage
93
+ # compatibility; the SERVICE SET and ENI count are now per-stage inputs.
94
+ okf_wiki_efs_consumers_active = false
95
+ vpc_interface_endpoints_enabled = var.okf_wiki_efs_enabled && var.okf_wiki_create_vpc_endpoints
96
+ # Legacy full set — what every stage had before the cost review. An
97
+ # interface endpoint with private DNS does not grant reachability (the
98
+ # private subnets are NAT-routed and the Lambda SGs allow 443 out); it only
99
+ # keeps the call on AWS's network, at ~$7.30/mo per ENI regardless of
100
+ # traffic. Stages that care about posture keep the data-touching ones
101
+ # (secretsmanager, rds-data, bedrock-runtime) and let the rest ride the NAT.
102
+ vpc_interface_endpoint_services_legacy = toset(concat(
88
103
  [
89
104
  "bedrock",
90
105
  "bedrock-agentcore",
@@ -106,18 +121,17 @@ locals {
106
121
  "bedrock-runtime",
107
122
  ],
108
123
  ))
109
-
110
- # Canonical long-term memory engine for this deployment. Exactly one engine
111
- # is active per deployment for recall/inspect/export. Empty selects
112
- # Hindsight for full installs; callers can explicitly choose AgentCore for
113
- # low-cost/development deployments. Legacy value "managed" maps to
114
- # "agentcore".
115
- resolved_memory_engine = (
116
- var.memory_engine == "hindsight" || var.memory_engine == "agentcore"
117
- ? var.memory_engine
118
- : var.memory_engine == "managed"
119
- ? "agentcore"
120
- : local.hindsight_enabled ? "hindsight" : "agentcore"
124
+ vpc_interface_endpoint_services = (
125
+ var.vpc_interface_endpoint_services != null
126
+ ? toset(var.vpc_interface_endpoint_services)
127
+ : local.vpc_interface_endpoint_services_legacy
128
+ )
129
+ # Single-AZ: one ENI in the lowest sorted subnet. Callers in the other AZ
130
+ # cross AZs at $0.01/GB — nothing at these volumes next to a second ENI.
131
+ vpc_interface_endpoint_subnet_ids = (
132
+ var.vpc_interface_endpoint_single_az
133
+ ? slice(sort(local.okf_wiki_subnet_ids), 0, min(1, length(local.okf_wiki_subnet_ids)))
134
+ : local.okf_wiki_subnet_ids
121
135
  )
122
136
 
123
137
  # Customer-domain namespace (<name>.thinkwork.ai). The zone is created as
@@ -487,14 +501,16 @@ resource "terraform_data" "n8n_runtime_state_guardrails" {
487
501
  }
488
502
  }
489
503
  }
490
- # Egress-only SG for the VPC-attached analyst data-path Lambdas
491
- # (analyst_lambda_vpc_egress). Their whole point is a stable NAT egress IP for
492
- # external database allowlists; they accept no inbound traffic.
493
- resource "aws_security_group" "analyst_egress_lambda" {
494
- count = var.analyst_lambda_vpc_egress ? 1 : 0
495
-
496
- name_prefix = "thinkwork-${var.stage}-analyst-egress-"
497
- description = "Egress for VPC-attached analyst data-path Lambdas"
504
+ # Company Brain U5: general egress for the VPC-attached identity-graph-
505
+ # projector (twin-query retired to the platform service THINK-339 U15).
506
+ # Pairs with the etl neptune stack's
507
+ # client SG (8182-only) — this one covers Aurora (5432), Secrets/S3/Lambda
508
+ # APIs (443) through the NAT-routed private subnets.
509
+ resource "aws_security_group" "twin_lambda_egress" {
510
+ #checkov:skip=CKV2_AWS_5: attached to the twin Lambdas via lambda-api's neptune vpc_config
511
+ count = var.neptune_client_security_group_id != "" ? 1 : 0
512
+ name = "thinkwork-${var.stage}-twin-lambda-egress"
513
+ description = "Twin Lambdas - general egress (Aurora, AWS APIs)"
498
514
  vpc_id = module.vpc.vpc_id
499
515
 
500
516
  egress {
@@ -503,20 +519,10 @@ resource "aws_security_group" "analyst_egress_lambda" {
503
519
  protocol = "-1"
504
520
  cidr_blocks = ["0.0.0.0/0"]
505
521
  }
506
-
507
- tags = { Name = "thinkwork-${var.stage}-analyst-egress-lambda-sg" }
508
-
509
- lifecycle {
510
- create_before_destroy = true
511
- precondition {
512
- condition = length(module.vpc.private_subnet_ids) > 0
513
- error_message = "analyst_lambda_vpc_egress requires private (NAT-routed) subnets — a Lambda in a public subnet has no internet egress."
514
- }
515
- }
516
522
  }
517
523
 
518
524
  resource "aws_security_group" "okf_wiki_lambda" {
519
- count = var.okf_wiki_efs_enabled ? 1 : 0
525
+ count = local.okf_wiki_efs_consumers_active ? 1 : 0 # THINK-589: always off
520
526
 
521
527
  name_prefix = "thinkwork-${var.stage}-okf-wiki-lambda-"
522
528
  description = "OKF wiki EFS clients for hydrator and Pi"
@@ -534,7 +540,7 @@ resource "aws_security_group" "okf_wiki_lambda" {
534
540
  }
535
541
 
536
542
  resource "aws_security_group" "okf_wiki_efs" {
537
- count = var.okf_wiki_efs_enabled ? 1 : 0
543
+ count = local.okf_wiki_efs_consumers_active ? 1 : 0 # THINK-589: always off
538
544
 
539
545
  name_prefix = "thinkwork-${var.stage}-okf-wiki-efs-"
540
546
  description = "NFS ingress for OKF wiki current-view EFS"
@@ -544,74 +550,83 @@ resource "aws_security_group" "okf_wiki_efs" {
544
550
  lifecycle { create_before_destroy = true }
545
551
  }
546
552
 
547
- resource "aws_security_group" "okf_wiki_vpc_endpoint" {
548
- count = var.okf_wiki_efs_enabled && var.okf_wiki_create_vpc_endpoints ? 1 : 0
553
+ # Re-homed from okf_wiki_vpc_endpoint (2026-08-28). name_prefix AND
554
+ # description keep their historical values on purpose: either change replaces
555
+ # the SG, which every live endpoint references — the Name tag carries the new
556
+ # identity instead.
557
+ resource "aws_security_group" "vpc_interface_endpoint" {
558
+ count = local.vpc_interface_endpoints_enabled ? 1 : 0
549
559
 
550
560
  name_prefix = "thinkwork-${var.stage}-okf-wiki-vpce-"
551
561
  description = "HTTPS endpoints for OKF wiki VPC-attached Lambdas"
552
562
  vpc_id = module.vpc.vpc_id
553
563
 
554
- tags = { Name = "thinkwork-${var.stage}-okf-wiki-vpce-sg" }
564
+ tags = { Name = "thinkwork-${var.stage}-vpce-sg" }
555
565
  lifecycle { create_before_destroy = true }
556
566
  }
557
567
 
558
568
  resource "aws_security_group_rule" "okf_wiki_vpce_from_lambda" {
559
- count = local.okf_wiki_private_endpoints_enabled ? 1 : 0
569
+ # THINK-589: always off the OKF client SG no longer exists; VPC workloads
570
+ # reach the endpoints via vpc_interface_endpoint_from_vpc (VPC CIDR).
571
+ count = local.okf_wiki_efs_consumers_active ? 1 : 0
560
572
 
561
573
  type = "ingress"
562
574
  from_port = 443
563
575
  to_port = 443
564
576
  protocol = "tcp"
565
577
  source_security_group_id = aws_security_group.okf_wiki_lambda[0].id
566
- security_group_id = aws_security_group.okf_wiki_vpc_endpoint[0].id
578
+ security_group_id = aws_security_group.vpc_interface_endpoint[0].id
567
579
  }
568
580
  # Interface endpoints with private DNS capture the WHOLE VPC's traffic to the
569
581
  # covered AWS APIs (logs, ssm, sts, bedrock, ...): every workload in the VPC
570
582
  # resolves those hostnames to the endpoint's private IP, so per-source-SG
571
- # rules silently black-hole anything not enumerated — harness cycle-7's
572
- # Hindsight tasks died on "cannot find the CloudWatch log group ... connection
573
- # issue" because only the Lambda SG was admitted. Allow the VPC CIDR.
574
- resource "aws_security_group_rule" "okf_wiki_vpce_from_vpc" {
575
- count = local.okf_wiki_private_endpoints_enabled ? 1 : 0
583
+ # rules silently black-hole anything not enumerated — harness cycle-7's ECS
584
+ # tasks died on "cannot find the CloudWatch log group ... connection issue"
585
+ # because only the Lambda SG was admitted. Allow the VPC CIDR.
586
+ resource "aws_security_group_rule" "vpc_interface_endpoint_from_vpc" {
587
+ count = local.vpc_interface_endpoints_enabled ? 1 : 0
576
588
 
577
589
  type = "ingress"
578
590
  from_port = 443
579
591
  to_port = 443
580
592
  protocol = "tcp"
581
593
  cidr_blocks = [module.vpc.vpc_cidr_block]
582
- security_group_id = aws_security_group.okf_wiki_vpc_endpoint[0].id
594
+ security_group_id = aws_security_group.vpc_interface_endpoint[0].id
583
595
  description = "All VPC workloads (private-DNS endpoints capture the whole VPC)"
584
596
  }
585
597
 
586
- resource "aws_security_group_rule" "okf_wiki_vpce_from_twenty" {
587
- count = local.okf_wiki_private_endpoints_enabled && local.twenty_provisioned ? 1 : 0
598
+ resource "aws_security_group_rule" "vpc_interface_endpoint_from_twenty" {
599
+ count = local.vpc_interface_endpoints_enabled && local.twenty_provisioned ? 1 : 0
588
600
 
589
601
  type = "ingress"
590
602
  from_port = 443
591
603
  to_port = 443
592
604
  protocol = "tcp"
593
605
  source_security_group_id = module.twenty[0].twenty_security_group_id
594
- security_group_id = aws_security_group.okf_wiki_vpc_endpoint[0].id
606
+ security_group_id = aws_security_group.vpc_interface_endpoint[0].id
595
607
  }
596
608
 
597
- resource "aws_vpc_endpoint" "okf_wiki_interface" {
598
- for_each = local.okf_wiki_private_endpoints_enabled ? local.okf_wiki_interface_endpoint_services : toset([])
609
+ resource "aws_vpc_endpoint" "vpc_interface" {
610
+ for_each = local.vpc_interface_endpoints_enabled ? local.vpc_interface_endpoint_services : toset([])
599
611
 
600
612
  vpc_id = module.vpc.vpc_id
601
613
  service_name = "com.amazonaws.${var.region}.${each.key}"
602
614
  vpc_endpoint_type = "Interface"
603
- subnet_ids = local.okf_wiki_subnet_ids
604
- security_group_ids = [aws_security_group.okf_wiki_vpc_endpoint[0].id]
615
+ subnet_ids = local.vpc_interface_endpoint_subnet_ids
616
+ security_group_ids = [aws_security_group.vpc_interface_endpoint[0].id]
605
617
  private_dns_enabled = true
606
618
 
607
619
  tags = {
608
- Name = "thinkwork-${var.stage}-okf-wiki-${each.key}-vpce"
609
- Purpose = "okf-wiki-private-egress"
620
+ Name = "thinkwork-${var.stage}-vpce-${each.key}"
621
+ Purpose = "vpc-private-egress"
610
622
  }
611
623
  }
612
624
 
613
- resource "aws_vpc_endpoint" "okf_wiki_s3" {
614
- count = local.okf_wiki_private_endpoints_enabled && length(local.okf_wiki_route_table_ids) > 0 ? 1 : 0
625
+ # Gateway endpoint: free, no ENIs — never part of the diet. The Brain stack
626
+ # mirrors these tags on the shared mcpherson endpoint (its accounts tfvars);
627
+ # change them in lockstep.
628
+ resource "aws_vpc_endpoint" "vpc_s3" {
629
+ count = local.vpc_interface_endpoints_enabled && length(local.okf_wiki_route_table_ids) > 0 ? 1 : 0
615
630
 
616
631
  vpc_id = module.vpc.vpc_id
617
632
  service_name = "com.amazonaws.${var.region}.s3"
@@ -619,12 +634,37 @@ resource "aws_vpc_endpoint" "okf_wiki_s3" {
619
634
  route_table_ids = local.okf_wiki_route_table_ids
620
635
 
621
636
  tags = {
622
- Name = "thinkwork-${var.stage}-okf-wiki-s3-vpce"
623
- Purpose = "okf-wiki-private-egress"
637
+ Name = "thinkwork-${var.stage}-vpce-s3"
638
+ Purpose = "vpc-private-egress"
624
639
  }
625
640
  }
641
+
642
+ # 2026-08-28 re-home: the endpoint group was created by (and named after)
643
+ # the dead OKF wiki lane. Same AWS resources, new addresses — a VPC endpoint
644
+ # Terraform destroys and recreates is a live outage for everything routing
645
+ # through it.
646
+ moved {
647
+ from = aws_security_group.okf_wiki_vpc_endpoint
648
+ to = aws_security_group.vpc_interface_endpoint
649
+ }
650
+ moved {
651
+ from = aws_security_group_rule.okf_wiki_vpce_from_vpc
652
+ to = aws_security_group_rule.vpc_interface_endpoint_from_vpc
653
+ }
654
+ moved {
655
+ from = aws_security_group_rule.okf_wiki_vpce_from_twenty
656
+ to = aws_security_group_rule.vpc_interface_endpoint_from_twenty
657
+ }
658
+ moved {
659
+ from = aws_vpc_endpoint.okf_wiki_interface
660
+ to = aws_vpc_endpoint.vpc_interface
661
+ }
662
+ moved {
663
+ from = aws_vpc_endpoint.okf_wiki_s3
664
+ to = aws_vpc_endpoint.vpc_s3
665
+ }
626
666
  resource "aws_security_group_rule" "okf_wiki_efs_from_lambda" {
627
- count = var.okf_wiki_efs_enabled ? 1 : 0
667
+ count = local.okf_wiki_efs_consumers_active ? 1 : 0 # THINK-589: always off
628
668
 
629
669
  type = "ingress"
630
670
  from_port = 2049
@@ -634,6 +674,10 @@ resource "aws_security_group_rule" "okf_wiki_efs_from_lambda" {
634
674
  security_group_id = aws_security_group.okf_wiki_efs[0].id
635
675
  }
636
676
 
677
+ # THINK-589 PR 1: deliberately NOT conditioned off — flipping this count to 0
678
+ # would DESTROY the filesystem (and its data) on the next apply. All consumers
679
+ # (mount targets, access points, Lambda mounts, IAM) are detached below; the
680
+ # filesystem delete happens in PR 2 behind the destructive-step gates.
637
681
  resource "aws_efs_file_system" "okf_wiki" {
638
682
  count = var.okf_wiki_efs_enabled ? 1 : 0
639
683
 
@@ -651,7 +695,7 @@ resource "aws_efs_file_system" "okf_wiki" {
651
695
  }
652
696
 
653
697
  resource "aws_efs_mount_target" "okf_wiki" {
654
- count = var.okf_wiki_efs_enabled ? length(local.okf_wiki_subnet_ids) : 0
698
+ count = local.okf_wiki_efs_consumers_active ? length(local.okf_wiki_subnet_ids) : 0 # THINK-589: always off
655
699
 
656
700
  file_system_id = aws_efs_file_system.okf_wiki[0].id
657
701
  subnet_id = local.okf_wiki_subnet_ids[count.index]
@@ -659,7 +703,7 @@ resource "aws_efs_mount_target" "okf_wiki" {
659
703
  }
660
704
 
661
705
  resource "aws_efs_access_point" "okf_wiki_refresh" {
662
- count = var.okf_wiki_efs_enabled ? 1 : 0
706
+ count = local.okf_wiki_efs_consumers_active ? 1 : 0 # THINK-589: always off
663
707
 
664
708
  file_system_id = aws_efs_file_system.okf_wiki[0].id
665
709
 
@@ -684,7 +728,7 @@ resource "aws_efs_access_point" "okf_wiki_refresh" {
684
728
  }
685
729
 
686
730
  resource "aws_efs_access_point" "okf_wiki_pi_read" {
687
- count = var.okf_wiki_efs_enabled ? 1 : 0
731
+ count = local.okf_wiki_efs_consumers_active ? 1 : 0 # THINK-589: always off
688
732
 
689
733
  file_system_id = aws_efs_file_system.okf_wiki[0].id
690
734
 
@@ -722,7 +766,16 @@ module "vpc" {
722
766
  existing_private_subnet_ids = var.existing_private_subnet_ids
723
767
  existing_public_route_table_ids = var.existing_public_route_table_ids
724
768
  existing_private_route_table_ids = var.existing_private_route_table_ids
725
- enable_nat_gateway = var.okf_wiki_efs_enabled && var.okf_wiki_create_nat_gateway
769
+ # THINK-589 PR 1: NAT egress historically rode the OKF EFS flags but is
770
+ # shared VPC plumbing — Neptune twin Lambdas (and any other VPC-attached
771
+ # workload) depend on the NAT-routed private subnets. Left gated as before
772
+ # so PR 1 changes no networking; PR 2 re-homes this onto the surviving VPC
773
+ # consumers (the lambda_vpc_access precedent in lambda-api/main.tf).
774
+ # THINK-1082: a VPC-attached deployment runner has no public IP and needs
775
+ # NAT egress (GitHub release manifest, apt/pip, STS/S3/Secrets Manager).
776
+ # THINK-1082: VPC-attached api Lambdas / Pi have no public IP either (AWS
777
+ # APIs, Bedrock, Slack, GitHub, model providers).
778
+ enable_nat_gateway = (var.okf_wiki_efs_enabled && var.okf_wiki_create_nat_gateway) || var.deployment_runner_in_vpc || var.api_lambdas_in_vpc
726
779
  }
727
780
 
728
781
  module "kms" {
@@ -787,8 +840,8 @@ module "cognito" {
787
840
  # entry — login works on both domains. Flipping
788
841
  # customer_domain_legacy_retired REMOVES the legacy (non-customer-domain)
789
842
  # app entries — the CloudFront default domain, the legacy app/computer
790
- # domains — as a reviewable Terraform change. Explicit caller-supplied,
791
- # localhost dev, and desktop entries are never retired here.
843
+ # domains — as a reviewable Terraform change. Explicit caller-supplied and
844
+ # localhost dev entries are never retired here.
792
845
  admin_callback_urls = distinct(concat(
793
846
  var.admin_callback_urls,
794
847
  local.customer_domain_legacy_retired ? [] : ["https://${module.computer_site.distribution_domain}", "https://${module.computer_site.distribution_domain}/auth/callback"],
@@ -800,7 +853,25 @@ module "cognito" {
800
853
  "http://localhost:5180/auth/callback",
801
854
  "http://127.0.0.1:5180",
802
855
  "http://127.0.0.1:5180/auth/callback",
803
- ]
856
+ ],
857
+ # Hosted Company Brain console origins (dev only): the console's CloudFront
858
+ # distribution and the future custom domain must be able to complete OAuth
859
+ # against the dev web-family clients. Dev-scoped so customer stacks don't
860
+ # carry dev console origins in their pools.
861
+ var.stage == "dev" ? [
862
+ "https://d3q9a98mhsakjf.cloudfront.net",
863
+ "https://d3q9a98mhsakjf.cloudfront.net/auth/callback",
864
+ "https://brain.thinkwork.ai",
865
+ "https://brain.thinkwork.ai/auth/callback",
866
+ ] : [],
867
+ # Operator-supplied web-family callback origins. Customer stacks host the
868
+ # Company Brain console in their OWN account (separate repo/CloudFront), so
869
+ # the product stack can't derive those URLs — they arrive here from the
870
+ # deployment runner (runner-secrets `adminCallbackUrls`). Empty by default;
871
+ # both bare and /auth/callback entries should be supplied by the caller.
872
+ # This is what keeps the console origins from being stripped on every
873
+ # controller apply (dev has the hardcoded block above; customers use this).
874
+ var.additional_admin_callback_urls,
804
875
  ))
805
876
  admin_logout_urls = distinct(concat(
806
877
  var.admin_logout_urls,
@@ -808,13 +879,28 @@ module "cognito" {
808
879
  local.customer_domain_legacy_retired ? [] : (local.legacy_end_user_app_domain != "" ? ["https://${local.legacy_end_user_app_domain}"] : []),
809
880
  local.customer_domain_web_enabled ? ["https://${var.customer_domain}"] : [],
810
881
  local.customer_domain_legacy_retired ? [] : (var.computer_domain != "" ? ["https://${var.computer_domain}"] : []),
811
- ["http://localhost:5180", "http://127.0.0.1:5180"]
882
+ ["http://localhost:5180", "http://127.0.0.1:5180"],
883
+ # Hosted Company Brain console origins (dev only) — see admin_callback_urls.
884
+ var.stage == "dev" ? [
885
+ "https://d3q9a98mhsakjf.cloudfront.net",
886
+ "https://d3q9a98mhsakjf.cloudfront.net/auth/callback",
887
+ "https://brain.thinkwork.ai",
888
+ "https://brain.thinkwork.ai/auth/callback",
889
+ ] : [],
890
+ # Operator-supplied web-family logout origins — see admin_callback_urls.
891
+ var.additional_admin_logout_urls,
812
892
  ))
813
- desktop_callback_urls = var.desktop_callback_urls
814
- cli_callback_urls = var.cli_callback_urls
815
- cli_logout_urls = var.cli_logout_urls
816
- mobile_callback_urls = var.mobile_callback_urls
817
- mobile_logout_urls = var.mobile_logout_urls
893
+ cli_callback_urls = var.cli_callback_urls
894
+ cli_logout_urls = var.cli_logout_urls
895
+ mobile_callback_urls = var.mobile_callback_urls
896
+ mobile_logout_urls = var.mobile_logout_urls
897
+
898
+ # Company Brain console client (U13 — ship-inert, default off). The console's
899
+ # CloudFront URL must be in console_callback_urls BEFORE first login
900
+ # (redirect_mismatch otherwise).
901
+ console_client_enabled = var.console_client_enabled
902
+ console_callback_urls = var.console_callback_urls
903
+ console_logout_urls = var.console_logout_urls
818
904
  }
819
905
 
820
906
  module "dns" {
@@ -904,6 +990,12 @@ module "database" {
904
990
  subnet_ids = module.vpc.public_subnet_ids
905
991
  db_password = var.db_password
906
992
 
993
+ # THINK-1082: network exposure. Defaults = today's public endpoint + world-
994
+ # open 5432. Closed per stage only after the Lambdas and the runner are in
995
+ # the VPC (api_lambdas_in_vpc / deployment_runner_in_vpc).
996
+ publicly_accessible = var.db_publicly_accessible
997
+ public_ingress_cidrs = var.db_public_ingress_cidrs
998
+
907
999
  database_name = var.database_name
908
1000
  database_engine = var.database_engine
909
1001
 
@@ -917,13 +1009,54 @@ module "database" {
917
1009
  enable_aws_s3 = var.database_engine == "aurora-serverless"
918
1010
  }
919
1011
 
920
- module "bedrock_kb" {
921
- source = "../data/bedrock-knowledge-base"
1012
+ ################################################################################
1013
+ # API Lambdas inside the VPC (THINK-1082)
1014
+ #
1015
+ # Off by default (ship-inert). When on, every database-connected Lambda of the
1016
+ # agent (lambda-api handlers, agentcore-admin, agentcore-pi, the capability
1017
+ # broker) attaches to the private subnets with this security group, and the
1018
+ # database security group grants it tcp/5432 — the same standalone-rule
1019
+ # pattern the ECS modules and the deployment runner use, so the grant survives
1020
+ # the aurora-postgres module's `ignore_changes = [ingress]`. With this live the
1021
+ # world-open 0.0.0.0/0 -> 5432 rule can be revoked without taking the app down.
1022
+ ################################################################################
1023
+
1024
+ resource "aws_security_group" "api_lambda" {
1025
+ count = var.api_lambdas_in_vpc ? 1 : 0
1026
+
1027
+ name = "thinkwork-${var.stage}-api-lambda-sg"
1028
+ description = "ThinkWork API Lambdas (VPC) for ${var.stage}."
1029
+ vpc_id = module.vpc.vpc_id
1030
+
1031
+ egress {
1032
+ from_port = 0
1033
+ to_port = 0
1034
+ protocol = "-1"
1035
+ cidr_blocks = ["0.0.0.0/0"]
1036
+ }
1037
+
1038
+ tags = {
1039
+ Name = "thinkwork-${var.stage}-api-lambda-sg"
1040
+ Stage = var.stage
1041
+ Purpose = "api-lambda"
1042
+ }
1043
+ }
1044
+
1045
+ resource "aws_security_group_rule" "db_from_api_lambda" {
1046
+ count = var.api_lambdas_in_vpc ? 1 : 0
922
1047
 
923
- stage = var.stage
924
- account_id = var.account_id
925
- region = var.region
926
- bucket_name = module.s3.bucket_name
1048
+ type = "ingress"
1049
+ description = "ThinkWork API Lambdas (VPC)"
1050
+ from_port = 5432
1051
+ to_port = 5432
1052
+ protocol = "tcp"
1053
+ security_group_id = module.database.db_security_group_id
1054
+ source_security_group_id = aws_security_group.api_lambda[0].id
1055
+ }
1056
+
1057
+ locals {
1058
+ api_lambda_vpc_subnet_ids = var.api_lambdas_in_vpc ? module.vpc.private_subnet_ids : []
1059
+ api_lambda_vpc_security_group_ids = var.api_lambdas_in_vpc ? [aws_security_group.api_lambda[0].id] : []
927
1060
  }
928
1061
 
929
1062
  ################################################################################
@@ -975,6 +1108,13 @@ module "deployment_control_plane" {
975
1108
 
976
1109
  log_retention_days = var.deployment_control_plane_log_retention_days
977
1110
  create_secret_placeholders = var.deployment_control_plane_create_secret_placeholders
1111
+
1112
+ # THINK-1082: runner inside the VPC — DB access by security group, not a
1113
+ # world-open port. The NAT this needs is switched on module.vpc above.
1114
+ runner_in_vpc = var.deployment_runner_in_vpc
1115
+ vpc_id = module.vpc.vpc_id
1116
+ subnet_ids = module.vpc.private_subnet_ids
1117
+ db_security_group_id = module.database.db_security_group_id
978
1118
  }
979
1119
 
980
1120
  # ── Capability signing keypair (THINK-173 KTD-3) ──────────────────────────
@@ -989,23 +1129,6 @@ resource "tls_private_key" "capability_signing" {
989
1129
  algorithm = "ED25519"
990
1130
  }
991
1131
 
992
- resource "random_password" "agentcore_proof_oauth_client_secret" {
993
- count = var.enable_agentcore_multiplayer_proof ? 1 : 0
994
- length = 48
995
- special = false
996
- }
997
-
998
- check "agentcore_multiplayer_proof_requires_harness" {
999
- assert {
1000
- condition = !var.enable_agentcore_multiplayer_proof || (
1001
- var.enable_agentcore_harness &&
1002
- can(regex("^[a-z0-9][a-z0-9-]{0,47}$", var.agentcore_multiplayer_proof_tenant_slug)) &&
1003
- length(distinct(compact([for owner in split(",", var.agentcore_multiplayer_proof_owner_allowlist) : trimspace(owner)]))) >= 2
1004
- )
1005
- error_message = "enable_agentcore_multiplayer_proof requires enable_agentcore_harness, an explicit lowercase tenant slug, and at least two explicit distinct proof owner subjects."
1006
- }
1007
- }
1008
-
1009
1132
  resource "aws_secretsmanager_secret" "capability_signing_key" {
1010
1133
  name = "thinkwork/${var.stage}/capability-signing-key"
1011
1134
  description = "Ed25519 private key (PKCS8 PEM) signing capability sidecars and manifests (THINK-173)."
@@ -1091,37 +1214,20 @@ module "api" {
1091
1214
  compliance_reader_secret_arn = module.database.compliance_reader_secret_arn
1092
1215
  compliance_anchor_object_lock_retention_days = var.compliance_anchor_retention_days
1093
1216
 
1094
- # THINK-228 — analyst query broker: hardened reader role credentials +
1095
- # the broker caller credential the seeded connector row references.
1096
1217
  # THINK-246 — artifact email delivery sender: customer stages send from
1097
1218
  # their own verified domain (noreply@<customer_domain>); dev keeps the
1098
1219
  # code fallback via the empty default.
1099
1220
  artifact_delivery_from_email = var.customer_domain != "" ? "noreply@${var.customer_domain}" : ""
1100
1221
 
1101
- analyst_reader_secret_arn = module.database.analyst_reader_secret_arn
1102
- analyst_broker_secret_arn = module.database.analyst_broker_secret_arn
1103
- # THINK-229 U1 — keys the broker's rds-db:connect grant and switches the
1104
- # reader connection to per-connect RDS IAM auth tokens.
1105
- analyst_db_cluster_resource_id = module.database.cluster_resource_id
1106
- analyst_policy_source = var.analyst_policy_source
1107
-
1108
1222
  # Governed autonomy — per-tenant self-extension opt-in allowlist (default off).
1109
- capability_self_extension_tenants = var.capability_self_extension_tenants
1110
1223
 
1111
1224
  # THINK-316 U2 — request path receives only Harness invocation. Terraform
1112
1225
  # owns provisioning/version/endpoint control, avoiding a module cycle and
1113
1226
  # keeping PassRole/create/update/list out of chat Lambdas.
1114
- enable_agentcore_harness_invocation = var.enable_agentcore_multiplayer_proof
1115
-
1116
- # THINK-316 U1 proof-only assertion/provider handlers remain absent unless
1117
- # explicitly enabled. The generated secret stays in Terraform sensitive
1118
- # state and is passed only to the two narrow boundary Lambdas and Identity.
1119
- enable_agentcore_multiplayer_proof = var.enable_agentcore_multiplayer_proof
1120
- agentcore_turn_assertion_key_versions = var.agentcore_turn_assertion_key_versions
1121
- agentcore_turn_assertion_active_key_version = var.agentcore_turn_assertion_active_key_version
1122
- agentcore_proof_oauth_client_id = "thinkwork-${var.stage}-agentcore-proof"
1123
- agentcore_proof_oauth_client_secret = var.enable_agentcore_multiplayer_proof ? random_password.agentcore_proof_oauth_client_secret[0].result : ""
1124
- agentcore_proof_owner_allowlist = var.agentcore_multiplayer_proof_owner_allowlist
1227
+ # THINK-324: the managed-harness/proof plane is retired; its handlers,
1228
+ # KMS plane, and Gateway are gone. The proof flag survives only as a
1229
+ # declared variable so existing -var passthroughs stay valid.
1230
+ enable_agentcore_multiplayer_proof = false
1125
1231
 
1126
1232
  # Phase 3 U8b — KMS key + Object Lock mode forwarded as
1127
1233
  # COMPLIANCE_ANCHOR_KMS_KEY_ARN and COMPLIANCE_ANCHOR_OBJECT_LOCK_MODE
@@ -1143,20 +1249,24 @@ module "api" {
1143
1249
  bucket_arn = module.s3.bucket_arn
1144
1250
 
1145
1251
  plugin_catalog_github_token_secret_arn = var.plugin_catalog_github_token_secret_arn
1252
+ brain_m2m_platform_agent_secret_arn = var.brain_m2m_platform_agent_secret_arn
1253
+ brain_ops_api_url = var.brain_ops_api_url
1254
+ brain_ops_m2m_secret_arn = var.brain_ops_m2m_secret_arn
1146
1255
 
1147
1256
  brain_artifacts_kms_key_arn = module.kms.key_arn
1148
1257
 
1149
- user_pool_id = module.cognito.user_pool_id
1150
- user_pool_arn = module.cognito.user_pool_arn
1151
- admin_client_id = module.cognito.admin_client_id
1152
- mobile_client_id = module.cognito.mobile_client_id
1258
+ user_pool_id = module.cognito.user_pool_id
1259
+ user_pool_arn = module.cognito.user_pool_arn
1260
+ admin_client_id = module.cognito.admin_client_id
1261
+ mobile_client_id = module.cognito.mobile_client_id
1262
+ # R16: console client id joins the COGNITO_APP_CLIENT_IDS allowlist when
1263
+ # enabled ("" otherwise — the lambda-api module compacts it away).
1264
+ console_client_id = var.console_client_enabled ? module.cognito.console_client_id : ""
1153
1265
  cognito_auth_domain = module.cognito.auth_domain
1154
1266
 
1155
1267
  appsync_api_id = module.appsync.graphql_api_id
1156
1268
  appsync_api_url = module.appsync.graphql_api_url
1157
1269
 
1158
- kb_service_role_arn = module.bedrock_kb.kb_service_role_arn
1159
-
1160
1270
  lambda_zips_dir = var.lambda_zips_dir
1161
1271
  api_auth_secret = var.api_auth_secret
1162
1272
  db_password = var.db_password
@@ -1164,33 +1274,51 @@ module "api" {
1164
1274
  agentcore_pi_function_name = module.agentcore_pi.agentcore_pi_function_name
1165
1275
  agentcore_pi_function_arn = module.agentcore_pi.agentcore_pi_function_arn
1166
1276
  enable_agentcore_pi_invoke_policy = true
1167
- hindsight_endpoint = local.hindsight_enabled ? module.hindsight[0].hindsight_endpoint : ""
1168
- hindsight_database_name = var.hindsight_database_name
1169
1277
  agentcore_memory_id = module.agentcore_memory.memory_id
1170
- memory_engine = local.resolved_memory_engine
1171
- okf_efs_subnet_ids = var.okf_wiki_efs_enabled ? local.okf_wiki_subnet_ids : []
1172
- okf_efs_security_group_ids = var.okf_wiki_efs_enabled ? [aws_security_group.okf_wiki_lambda[0].id] : []
1173
- analyst_egress_subnet_ids = var.analyst_lambda_vpc_egress ? module.vpc.private_subnet_ids : []
1174
- analyst_egress_security_group_ids = var.analyst_lambda_vpc_egress ? [aws_security_group.analyst_egress_lambda[0].id] : []
1175
- okf_efs_mount_target_ids = var.okf_wiki_efs_enabled ? aws_efs_mount_target.okf_wiki[*].id : []
1176
- okf_efs_file_system_arn = var.okf_wiki_efs_enabled ? aws_efs_file_system.okf_wiki[0].arn : ""
1177
- okf_efs_refresh_access_point_arn = var.okf_wiki_efs_enabled ? aws_efs_access_point.okf_wiki_refresh[0].arn : ""
1178
- twenty_provisioned = local.twenty_provisioned
1179
- twenty_runtime_enabled = local.twenty_runtime_enabled
1180
- twenty_url = local.twenty_provisioned ? module.twenty[0].twenty_url : ""
1181
- twenty_alb_arn = local.twenty_provisioned ? module.twenty[0].twenty_alb_arn : ""
1182
- twenty_target_group_arn = local.twenty_provisioned ? module.twenty[0].twenty_target_group_arn : ""
1183
- twenty_cluster_arn = local.twenty_provisioned ? module.twenty[0].twenty_cluster_arn : ""
1184
- twenty_server_service_name = local.twenty_provisioned ? module.twenty[0].twenty_server_service_name : ""
1185
- twenty_worker_service_name = local.twenty_provisioned ? module.twenty[0].twenty_worker_service_name : ""
1186
- twenty_server_log_group_name = local.twenty_provisioned ? module.twenty[0].twenty_server_log_group_name : ""
1187
- twenty_worker_log_group_name = local.twenty_provisioned ? module.twenty[0].twenty_worker_log_group_name : ""
1188
- admin_url = local.end_user_app_url
1189
- docs_url = "https://${module.docs_site.distribution_domain}"
1190
- www_url = var.www_domain != "" ? "https://${var.www_domain}" : "https://${module.www_site.distribution_domain}"
1191
- stripe_price_ids_json = var.stripe_price_ids_json
1192
- appsync_realtime_url = module.appsync.graphql_realtime_url
1193
- ecr_repository_url = module.agentcore_platform.ecr_repository_url
1278
+ # THINK-589: OKF EFS inputs are deprecated no-ops — always pass empty.
1279
+ okf_efs_subnet_ids = []
1280
+ okf_efs_security_group_ids = []
1281
+ # Company Brain U5: projector VPC attach only when the neptune-client SG
1282
+ # is configured (the twin's Neptune cluster lives in this same VPC). The
1283
+ # client SG only opens 8182→Neptune, so twin Lambdas ALSO attach the
1284
+ # egress SG below without it a VPC-attached Lambda cannot reach
1285
+ # Aurora/Secrets/S3 and every DB connection times out (seen live on the
1286
+ # first dev rebuild invoke).
1287
+ lambda_max_memory_mb = var.lambda_max_memory_mb
1288
+ neptune_endpoint = var.neptune_endpoint
1289
+ brain_mcp_url = var.brain_mcp_url
1290
+ neptune_cluster_resource_id = var.neptune_cluster_resource_id
1291
+ neptune_subnet_ids = var.neptune_client_security_group_id != "" ? module.vpc.private_subnet_ids : []
1292
+ neptune_security_group_ids = var.neptune_client_security_group_id != "" ? [var.neptune_client_security_group_id, aws_security_group.twin_lambda_egress[0].id] : []
1293
+ # THINK-1082: every handler inside the VPC behind the api-lambda SG (merged
1294
+ # with the Neptune SGs on the projector). NAT is switched on module.vpc.
1295
+ lambdas_in_vpc = var.api_lambdas_in_vpc
1296
+ vpc_subnet_ids = local.api_lambda_vpc_subnet_ids
1297
+ vpc_security_group_ids = local.api_lambda_vpc_security_group_ids
1298
+ # Bulk-rebuild lane (THINK-331): loader staging bucket + cluster loader
1299
+ # role, both default-empty (ship inert, KTD-9).
1300
+ neptune_load_bucket = var.neptune_load_bucket
1301
+ neptune_loader_role_arn = var.neptune_loader_role_arn
1302
+ # Company Brain U7: twin tool seam flag (KTD-5 rollout posture).
1303
+ # THINK-589: OKF EFS inputs are deprecated no-ops — always pass empty.
1304
+ okf_efs_mount_target_ids = []
1305
+ okf_efs_file_system_arn = ""
1306
+ okf_efs_refresh_access_point_arn = ""
1307
+ twenty_provisioned = local.twenty_provisioned
1308
+ twenty_runtime_enabled = local.twenty_runtime_enabled
1309
+ twenty_url = local.twenty_provisioned ? module.twenty[0].twenty_url : ""
1310
+ twenty_alb_arn = local.twenty_provisioned ? module.twenty[0].twenty_alb_arn : ""
1311
+ twenty_target_group_arn = local.twenty_provisioned ? module.twenty[0].twenty_target_group_arn : ""
1312
+ twenty_cluster_arn = local.twenty_provisioned ? module.twenty[0].twenty_cluster_arn : ""
1313
+ twenty_server_service_name = local.twenty_provisioned ? module.twenty[0].twenty_server_service_name : ""
1314
+ twenty_worker_service_name = local.twenty_provisioned ? module.twenty[0].twenty_worker_service_name : ""
1315
+ twenty_server_log_group_name = local.twenty_provisioned ? module.twenty[0].twenty_server_log_group_name : ""
1316
+ twenty_worker_log_group_name = local.twenty_provisioned ? module.twenty[0].twenty_worker_log_group_name : ""
1317
+ admin_url = local.end_user_app_url
1318
+ www_url = var.www_domain != "" ? "https://${var.www_domain}" : "https://${module.www_site.distribution_domain}"
1319
+ stripe_price_ids_json = var.stripe_price_ids_json
1320
+ appsync_realtime_url = module.appsync.graphql_realtime_url
1321
+ ecr_repository_url = module.agentcore_platform.ecr_repository_url
1194
1322
  # Static truth for count gating: agentcore_platform's ECR repo is
1195
1323
  # unconditional, but its URL attribute is unknown until apply. The runner
1196
1324
  # additionally needs its image tag seeded (CI does this for repo-managed
@@ -1210,15 +1338,13 @@ module "api" {
1210
1338
  enable_slack_workspace_app = var.enable_slack_workspace_app
1211
1339
  enable_msteams_app = var.enable_msteams_app
1212
1340
  agentcore_code_interpreter_id = var.agentcore_code_interpreter_id
1213
- wiki_compile_model_id = var.wiki_compile_model_id
1214
1341
  brain_source_agent_model_id = var.brain_source_agent_model_id
1215
1342
  wiki_aggregation_pass_enabled = var.wiki_aggregation_pass_enabled
1216
1343
  wiki_source = var.wiki_source
1217
- knowledge_graph_observations_ingest_enabled = var.knowledge_graph_observations_ingest_enabled
1218
- ontology_scan_sweep_enabled = var.ontology_scan_sweep_enabled
1344
+ identity_drift_match_enabled = var.identity_drift_match_enabled
1219
1345
  wiki_deterministic_linking_enabled = var.wiki_deterministic_linking_enabled
1220
- google_places_api_key = var.google_places_api_key
1221
1346
  enable_workspace_orchestration = var.enable_workspace_orchestration
1347
+ agentcore_session_scope = var.agentcore_session_scope
1222
1348
  requester_idle_memory_learning_enabled = var.requester_idle_memory_learning_enabled
1223
1349
  requester_memory_dreaming_enabled = var.requester_memory_dreaming_enabled
1224
1350
  requester_memory_dreaming_schedule_expression = var.requester_memory_dreaming_schedule_expression
@@ -1233,6 +1359,19 @@ module "api" {
1233
1359
  redirect_success_url = var.redirect_success_url
1234
1360
  platform_operator_emails = var.platform_operator_emails
1235
1361
 
1362
+ # THINK-583 U3 — warm chat path (KTD5: these two handlers only).
1363
+ chat_agent_invoke_provisioned_concurrency = var.chat_agent_invoke_provisioned_concurrency
1364
+ workspace_renderer_provisioned_concurrency = var.workspace_renderer_provisioned_concurrency
1365
+ agentcore_runtime_dispatch_enabled = var.agentcore_runtime_dispatch_enabled
1366
+
1367
+ # THINK-915 — chat turn-latency observability.
1368
+ enable_chat_turn_latency_observability = var.enable_chat_turn_latency_observability
1369
+ enable_chat_turn_latency_metric_filters = var.enable_chat_turn_latency_metric_filters
1370
+ chat_turn_latency_metric_namespace = var.chat_turn_latency_metric_namespace
1371
+ chat_turn_runtime_log_group_name = var.chat_turn_runtime_log_group_name
1372
+ chat_turn_p95_alarm_threshold_ms = var.chat_turn_p95_alarm_threshold_ms
1373
+ chat_turn_latency_alarm_actions = var.chat_turn_latency_alarm_actions
1374
+
1236
1375
  mcp_custom_domain = var.mcp_custom_domain
1237
1376
  mcp_custom_domain_ready = var.mcp_custom_domain_ready
1238
1377
 
@@ -1247,6 +1386,8 @@ module "api" {
1247
1386
  capability_broker_audience = module.capability_broker.broker_audience
1248
1387
  capability_broker_api_id = module.capability_broker.private_api_id
1249
1388
  capability_broker_vpce_dns = module.capability_broker.execute_api_vpce_dns_name
1389
+ # THINK-643 — "postgres" (default) leaves the auth-state table unprovisioned.
1390
+ auth_state_store = var.auth_state_store
1250
1391
 
1251
1392
  depends_on = [module.cognito]
1252
1393
  }
@@ -1270,7 +1411,8 @@ module "agentcore_memory" {
1270
1411
  module "agentcore_platform" {
1271
1412
  source = "../app/agentcore-platform"
1272
1413
 
1273
- stage = var.stage
1414
+ stage = var.stage
1415
+ release_mirror_principal_arns = var.release_image_mirror_principal_arns
1274
1416
  }
1275
1417
 
1276
1418
  ################################################################################
@@ -1292,10 +1434,7 @@ module "agentcore_pi" {
1292
1434
  source_image_uri = var.agentcore_pi_source_image_uri
1293
1435
  async_dlq_arn = module.agentcore_platform.agentcore_async_dlq_arn
1294
1436
 
1295
- hindsight_endpoint = local.hindsight_enabled ? module.hindsight[0].hindsight_endpoint : ""
1296
- hindsight_database_name = var.hindsight_database_name
1297
1437
  agentcore_memory_id = module.agentcore_memory.memory_id
1298
- memory_engine = local.resolved_memory_engine
1299
1438
  requester_idle_memory_learning_enabled = var.requester_idle_memory_learning_enabled
1300
1439
 
1301
1440
  api_endpoint = module.api.api_endpoint
@@ -1307,151 +1446,17 @@ module "agentcore_pi" {
1307
1446
  db_cluster_arn = module.database.db_cluster_arn
1308
1447
  db_secret_arn = module.database.graphql_db_secret_arn
1309
1448
 
1310
- okf_efs_enabled = var.okf_wiki_efs_enabled
1311
- okf_efs_subnet_ids = var.okf_wiki_efs_enabled ? local.okf_wiki_subnet_ids : []
1312
- okf_efs_security_group_ids = var.okf_wiki_efs_enabled ? [aws_security_group.okf_wiki_lambda[0].id] : []
1313
- okf_efs_file_system_arn = var.okf_wiki_efs_enabled ? aws_efs_file_system.okf_wiki[0].arn : ""
1314
- okf_efs_read_access_point_arn = var.okf_wiki_efs_enabled ? aws_efs_access_point.okf_wiki_pi_read[0].arn : ""
1315
- }
1316
-
1317
- # AgentCore Harness execution role and managed tenant/profile runtime. The
1318
- # public root inputs retain their rollout-era names until the example-stack
1319
- # compatibility window closes; this module boundary uses managed semantics.
1320
- module "agentcore_harness" {
1321
- source = "../app/agentcore-harness"
1449
+ # THINK-589: OKF EFS inputs are deprecated no-ops — always pass disabled.
1450
+ okf_efs_enabled = false
1451
+ okf_efs_subnet_ids = []
1452
+ okf_efs_security_group_ids = []
1453
+ okf_efs_file_system_arn = ""
1454
+ okf_efs_read_access_point_arn = ""
1322
1455
 
1323
- enabled = var.enable_agentcore_harness
1324
- managed_runtime_enabled = var.enable_agentcore_multiplayer_proof
1325
- stage = var.stage
1326
- region = var.region
1327
- account_id = var.account_id
1328
- bucket_name = module.s3.bucket_name
1329
- tenant_slug = var.agentcore_multiplayer_proof_tenant_slug
1330
- discovery_url = "${module.api.agentcore_turn_assertion_issuer}/.well-known/openid-configuration"
1331
- harness_audience = module.api.agentcore_harness_audience
1332
- gateway_arn = module.agentcore_proof_gateway.gateway_arn
1333
- oauth_credential_provider_arn = module.agentcore_proof_identity.credential_provider_arn
1334
- oauth_credential_secret_arn = module.agentcore_proof_identity.credential_secret_arn
1335
- oauth_return_url = module.agentcore_proof_identity.oauth_return_url
1336
- }
1337
-
1338
- # The Harness depends on the public issuer/API, so feeding its generated ARN
1339
- # back into the api module would create a Terraform cycle. Publish the
1340
- # non-secret, attested readiness contract under deterministic SSM keys; the
1341
- # runner and DeploymentStatus resolver read it server-side. No browser token,
1342
- # participant id, credential, prompt, or private result is stored here. Keep
1343
- # the original address during the migration so deployed runners never lose
1344
- # their profile between releases.
1345
- locals {
1346
- agentcore_harness_profile_value = jsonencode({
1347
- tenantSlug = var.agentcore_multiplayer_proof_tenant_slug
1348
- trustProfile = "default"
1349
- harnessArn = module.agentcore_harness.managed_harness_arn
1350
- endpointArn = module.agentcore_harness.managed_endpoint_arn
1351
- endpointName = module.agentcore_harness.managed_endpoint_name
1352
- expectedVersion = module.agentcore_harness.managed_target_version
1353
- liveVersion = module.agentcore_harness.managed_live_version
1354
- modelId = module.agentcore_harness.managed_model_id
1355
- status = module.agentcore_harness.managed_status
1356
- configurationFingerprint = module.agentcore_harness.managed_configuration_fingerprint
1357
- authorizerAudience = module.api.agentcore_harness_audience
1358
- sessionStrategy = "fresh"
1359
- gatewayArn = module.agentcore_proof_gateway.gateway_arn
1360
- gatewayUrl = module.agentcore_proof_gateway.gateway_url
1361
- gatewayTargetName = module.agentcore_proof_gateway.target_name
1362
- identityWorkloadName = module.agentcore_proof_identity.workload_identity_name
1363
- identityCredentialProviderName = module.agentcore_proof_identity.credential_provider_name
1364
- identityCredentialProviderArn = module.agentcore_proof_identity.credential_provider_arn
1365
- invocationToolsContract = "control-plane-attested-full-override-v1"
1366
- invocationToolsFingerprint = module.agentcore_harness.managed_invocation_tools_fingerprint
1367
- invocationTools = jsondecode(module.agentcore_harness.managed_invocation_tools_json)
1368
- strategyDecisionRule = "reuse_only_if_correct_and_20_percent_benefit"
1369
- capacityEnvelope = {
1370
- activeSessions = 100
1371
- newSessionsPerSecond = 10
1372
- minimumHeadroom = 0.50
1373
- }
1374
- })
1375
- }
1376
-
1377
- resource "aws_ssm_parameter" "agentcore_harness_proof_profile" {
1378
- count = var.enable_agentcore_multiplayer_proof ? 1 : 0
1379
-
1380
- name = "/thinkwork/${var.stage}/agentcore-harness-proof-profile"
1381
- type = "String"
1382
- # The exact immutable Harness tool schemas sit close to Standard's 4 KiB
1383
- # ceiling. Intelligent-Tiering stays Standard while possible and safely
1384
- # promotes only when a legitimate governed schema requires more room.
1385
- tier = "Intelligent-Tiering"
1386
- value = local.agentcore_harness_profile_value
1387
-
1388
- lifecycle {
1389
- precondition {
1390
- # Base64 expands every three UTF-8 bytes to four ASCII characters. A
1391
- # conservative 10,920-character ceiling admits at most 8,190 bytes and
1392
- # cannot cross SSM's 8 KiB Advanced parameter limit.
1393
- condition = length(base64encode(local.agentcore_harness_profile_value)) <= 10920
1394
- error_message = "The AgentCore Harness managed profile exceeds SSM's 8 KiB parameter limit."
1395
- }
1396
- }
1397
-
1398
- tags = {
1399
- Name = "thinkwork-${var.stage}-agentcore-harness-proof-profile"
1400
- "thinkwork:proof" = "THINK-316"
1401
- "thinkwork:tenant" = var.agentcore_multiplayer_proof_tenant_slug
1402
- "thinkwork:profile" = "default"
1403
- "thinkwork:visibility" = "server-only-nonsecret"
1404
- }
1405
- }
1406
-
1407
- # Managed per-tenant profile address. Runtime lookup reads this first and only
1408
- # falls back to the original single-tenant key during the migration window.
1409
- # Adding the scoped key is non-destructive and lets later tenants receive their
1410
- # own attested profile without changing Lambda configuration or hot-path
1411
- # control-plane resources.
1412
- resource "aws_ssm_parameter" "agentcore_harness_profile" {
1413
- count = var.enable_agentcore_multiplayer_proof ? 1 : 0
1414
-
1415
- name = "/thinkwork/${var.stage}/agentcore-harness-profiles/${var.agentcore_multiplayer_proof_tenant_slug}"
1416
- type = aws_ssm_parameter.agentcore_harness_proof_profile[0].type
1417
- tier = "Intelligent-Tiering"
1418
- value = aws_ssm_parameter.agentcore_harness_proof_profile[0].value
1419
-
1420
- tags = {
1421
- Name = "thinkwork-${var.stage}-agentcore-harness-profile-${var.agentcore_multiplayer_proof_tenant_slug}"
1422
- "thinkwork:managed-by" = "terraform"
1423
- "thinkwork:tenant" = var.agentcore_multiplayer_proof_tenant_slug
1424
- "thinkwork:profile" = "default"
1425
- "thinkwork:visibility" = "server-only-nonsecret"
1426
- }
1427
- }
1428
-
1429
- # Publish the new immutable endpoint/profile first, then reclaim older
1430
- # endpoint versions. Retain the active endpoint plus exactly one prior endpoint
1431
- # for guarded rollback; the dedicated Harness owns no unrelated endpoints.
1432
- resource "terraform_data" "agentcore_harness_endpoint_retention" {
1433
- count = var.enable_agentcore_multiplayer_proof ? 1 : 0
1434
-
1435
- triggers_replace = {
1436
- active_endpoint = module.agentcore_harness.managed_endpoint_name
1437
- profile_digest = module.agentcore_harness.managed_configuration_fingerprint
1438
- }
1439
-
1440
- provisioner "local-exec" {
1441
- command = "bash ${path.module}/../app/agentcore-harness/scripts/prune_harness.sh"
1442
- environment = {
1443
- AWS_REGION = var.region
1444
- HARNESS_ID = module.agentcore_harness.managed_harness_id
1445
- ACTIVE_ENDPOINT_NAME = module.agentcore_harness.managed_endpoint_name
1446
- ENDPOINT_PREFIX = "ThinkworkProofV"
1447
- LEGACY_ENDPOINT_NAME = "ThinkworkProof"
1448
- }
1449
- }
1450
-
1451
- depends_on = [
1452
- aws_ssm_parameter.agentcore_harness_proof_profile,
1453
- aws_ssm_parameter.agentcore_harness_profile,
1454
- ]
1456
+ # THINK-1082: network-only VPC attachment alongside the api handlers.
1457
+ in_vpc = var.api_lambdas_in_vpc
1458
+ vpc_subnet_ids = local.api_lambda_vpc_subnet_ids
1459
+ vpc_security_group_ids = local.api_lambda_vpc_security_group_ids
1455
1460
  }
1456
1461
 
1457
1462
  # THINK-316 U1 proof-only Identity and Gateway plane. These resources use the
@@ -1460,39 +1465,41 @@ resource "terraform_data" "agentcore_harness_endpoint_retention" {
1460
1465
  module "agentcore_proof_identity" {
1461
1466
  source = "../app/agentcore-identity"
1462
1467
 
1463
- enabled = var.enable_agentcore_multiplayer_proof
1468
+ # THINK-324: the proof plane is retired — permanently off. The module's
1469
+ # proof half (workload-identity lifecycle + proof credential provider) no
1470
+ # longer reconciles; delete_identity.sh retains the shared workload
1471
+ # identity for the Twenty half below.
1472
+ enabled = false
1473
+ # THINK-324 — the Twenty user-federation identity (shared workload
1474
+ # identity + twenty-crm credential provider + DCR client secret) survives
1475
+ # the proof plane's retirement: it stays enabled wherever a Twenty
1476
+ # instance is provisioned, and — while the proof flag is still on — on
1477
+ # proof stages too, so flipping the proof flag off later destroys only
1478
+ # proof resources. After the harness retirement removes the proof flag,
1479
+ # this reduces to twenty_provisioned alone.
1480
+ twenty_enabled = local.twenty_provisioned || var.enable_agentcore_multiplayer_proof
1464
1481
  stage = var.stage
1465
1482
  region = var.region
1466
1483
  account_id = var.account_id
1467
- oauth_issuer = module.api.agentcore_proof_oauth_issuer
1484
+ oauth_issuer = ""
1468
1485
  oauth_client_id = "thinkwork-${var.stage}-agentcore-proof"
1469
- oauth_client_secret = var.enable_agentcore_multiplayer_proof ? random_password.agentcore_proof_oauth_client_secret[0].result : ""
1486
+ oauth_client_secret = ""
1470
1487
  user_federation_return_urls = [
1471
1488
  "${module.api.api_endpoint}/api/skills/mcp-oauth/agentcore/complete",
1472
1489
  ]
1473
- }
1474
-
1475
- module "agentcore_proof_gateway" {
1476
- source = "../app/agentcore-gateway"
1477
-
1478
- enabled = var.enable_agentcore_multiplayer_proof
1479
- stage = var.stage
1480
- region = var.region
1481
- account_id = var.account_id
1482
- discovery_url = "${module.api.agentcore_turn_assertion_issuer}/.well-known/openid-configuration"
1483
- gateway_audience = module.api.agentcore_gateway_audience
1484
- target_base_url = module.api.agentcore_proof_target_base_url
1485
- oauth_credential_provider_arn = module.agentcore_proof_identity.credential_provider_arn
1486
- oauth_credential_secret_arn = module.agentcore_proof_identity.credential_secret_arn
1487
- oauth_return_url = module.agentcore_proof_identity.oauth_return_url
1488
- proof_owner_allowlist = var.agentcore_multiplayer_proof_owner_allowlist
1490
+ # The identity module's issuer/resource defaults are dev's CRM domain;
1491
+ # every stage must point at its own Twenty instance or the credential
1492
+ # provider is reconciled with a foreign issuer (observed live on tei-e2e:
1493
+ # Connect-AgentCore 404'd against crm.thinkwork.ai).
1494
+ twenty_oauth_issuer = local.twenty_public_url != "" ? trimsuffix(local.twenty_public_url, "/") : "https://crm.thinkwork.ai"
1495
+ twenty_oauth_resource = local.twenty_public_url != "" ? "${trimsuffix(local.twenty_public_url, "/")}/mcp" : "https://crm.thinkwork.ai/mcp"
1489
1496
  }
1490
1497
 
1491
1498
  # Capability Broker (THINK-280 U3) — ship-inert, gated by
1492
1499
  # var.enable_capability_broker (default false). When off, this module creates
1493
1500
  # nothing; the broker Lambda code also fails closed. Owns a DEDICATED
1494
1501
  # execute-api VPCE with private DNS disabled — never folded into
1495
- # okf_wiki_interface_endpoint_services (THINK-144).
1502
+ # vpc_interface_endpoint_services (THINK-144).
1496
1503
  module "capability_broker" {
1497
1504
  source = "../app/capability-broker"
1498
1505
 
@@ -1511,6 +1518,10 @@ module "capability_broker" {
1511
1518
  db_password = var.db_password
1512
1519
  database_name = var.database_name
1513
1520
 
1521
+ # THINK-1082: the broker is already VPC-attached; give it the api-lambda SG
1522
+ # so it keeps its DB path once the public 5432 rule closes.
1523
+ additional_security_group_ids = local.api_lambda_vpc_security_group_ids
1524
+
1514
1525
  lambda_zips_dir = var.lambda_zips_dir
1515
1526
  lambda_artifact_bucket = var.lambda_artifact_bucket
1516
1527
  lambda_artifact_prefix = var.lambda_artifact_prefix
@@ -1557,6 +1568,11 @@ module "agentcore_admin" {
1557
1568
 
1558
1569
  capability_private_subnet_ids = module.agentcore_code_interpreter.capability_private_subnet_ids
1559
1570
  capability_private_security_group_ids = module.agentcore_code_interpreter.capability_private_security_group_ids
1571
+
1572
+ # THINK-1082: the admin handler opens DATABASE_URL — same VPC lane.
1573
+ in_vpc = var.api_lambdas_in_vpc
1574
+ vpc_subnet_ids = local.api_lambda_vpc_subnet_ids
1575
+ vpc_security_group_ids = local.api_lambda_vpc_security_group_ids
1560
1576
  }
1561
1577
 
1562
1578
  # Runtime identity rename: the former dedicated Flue module is now the Pi
@@ -1682,25 +1698,9 @@ module "workflow_interpreter_stepfunctions" {
1682
1698
  execution_callback_lambda_arn = module.api.workflow_execution_callback_fn_arn
1683
1699
  }
1684
1700
 
1685
- module "hindsight" {
1686
- count = local.hindsight_enabled ? 1 : 0
1687
- source = "../app/hindsight-memory"
1688
-
1689
- stage = var.stage
1690
- vpc_id = module.vpc.vpc_id
1691
- subnet_ids = module.vpc.public_subnet_ids
1692
- db_security_group_id = module.database.db_security_group_id
1693
- database_url = module.database.database_url
1694
- database_name = var.hindsight_database_name
1695
- image_tag = var.hindsight_image_tag
1696
-
1697
- enable_auto_consolidation = var.hindsight_enable_auto_consolidation
1698
- consolidation_dedup_threshold = var.hindsight_consolidation_dedup_threshold
1699
- observations_mission = var.hindsight_observations_mission
1700
- }
1701
1701
  module "twenty" {
1702
1702
  count = local.twenty_provisioned ? 1 : 0
1703
- source = "../../../plugins/twenty/terraform/twenty"
1703
+ source = "../app/twenty"
1704
1704
 
1705
1705
  stage = var.stage
1706
1706
  vpc_id = module.vpc.vpc_id
@@ -1738,7 +1738,7 @@ module "twenty" {
1738
1738
 
1739
1739
  module "n8n" {
1740
1740
  count = local.n8n_provisioned ? 1 : 0
1741
- source = "../../../plugins/n8n/terraform/n8n"
1741
+ source = "../app/n8n"
1742
1742
 
1743
1743
  stage = var.stage
1744
1744
  vpc_id = module.vpc.vpc_id
@@ -1818,14 +1818,38 @@ locals {
1818
1818
  # Host CSP for the Computer SPA (plan-012 U10 / contract v1 §CSP profile).
1819
1819
  # Generated apps always execute in the sandbox iframe shell, so the parent
1820
1820
  # origin never needs blob: script/worker execution for transformed modules.
1821
- computer_host_script_src = "'self'"
1822
- computer_host_worker_src = "'self'"
1823
- computer_host_frame_src = local.computer_sandbox_enabled ? "https://${var.computer_sandbox_domain}" : "'none'"
1821
+ #
1822
+ # 'wasm-unsafe-eval' permits WebAssembly compilation only (not JS eval) —
1823
+ # the in-app knowledge document viewer (Zrimo) renders Office files with
1824
+ # client-side WASM.
1825
+ computer_host_script_src = "'self' 'wasm-unsafe-eval'"
1826
+ # blob: workers are how Zrimo (knowledge document viewer) spawns its
1827
+ # render workers. With script-src carrying no blob:/eval, only
1828
+ # first-party page code can mint a blob worker, and blob workers
1829
+ # inherit this document's CSP — the U10 blob-execution escape hatch
1830
+ # (generated-app script on the host origin) stays closed.
1831
+ computer_host_worker_src = "'self' blob:"
1832
+ # frame-src carries the sandbox shell plus the cited-knowledge-document
1833
+ # frames: browser-native formats (pdf/txt/md) render in an iframe on the
1834
+ # signed /kb/doc link — a *.thinkwork.ai brain host that 302s to the
1835
+ # customer's evidence bucket presign, and CSP re-checks every redirect
1836
+ # hop, so both origins must be allowed (mirroring what connect-src
1837
+ # already trusts for the Zrimo fetch path).
1838
+ computer_host_frame_src = join(" ", compact([
1839
+ local.computer_sandbox_enabled ? "https://${var.computer_sandbox_domain}" : "",
1840
+ "https://*.thinkwork.ai",
1841
+ "https://*.s3.${var.region}.amazonaws.com",
1842
+ "https://s3.${var.region}.amazonaws.com",
1843
+ ]))
1824
1844
  computer_host_frame_ancestors = join(" ", compact([
1825
1845
  "'self'",
1826
1846
  ]))
1827
1847
 
1828
- computer_host_csp = "default-src 'self'; script-src ${local.computer_host_script_src}; style-src 'self' 'unsafe-inline'; worker-src ${local.computer_host_worker_src}; frame-src ${local.computer_host_frame_src}; connect-src 'self' https://*.execute-api.${var.region}.amazonaws.com https://*.appsync-api.${var.region}.amazonaws.com wss://*.appsync-realtime-api.${var.region}.amazonaws.com https://cognito-idp.${var.region}.amazonaws.com https://*.auth.${var.region}.amazoncognito.com https://*.s3.${var.region}.amazonaws.com https://s3.${var.region}.amazonaws.com; img-src 'self' data: blob: ${local.computer_sandbox_map_img_src}; font-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors ${local.computer_host_frame_ancestors};"
1848
+ # connect-src includes https://*.thinkwork.ai for the knowledge document
1849
+ # viewer: signed /kb/doc citation links live on brain MCP hosts
1850
+ # (mcp.brain.<customer>.thinkwork.ai), and a tenant may register another
1851
+ # stage's brain, so the host cannot be pinned per-stage.
1852
+ computer_host_csp = "default-src 'self'; script-src ${local.computer_host_script_src}; style-src 'self' 'unsafe-inline'; worker-src ${local.computer_host_worker_src}; frame-src ${local.computer_host_frame_src}; connect-src 'self' https://*.thinkwork.ai https://*.execute-api.${var.region}.amazonaws.com https://*.appsync-api.${var.region}.amazonaws.com wss://*.appsync-realtime-api.${var.region}.amazonaws.com https://cognito-idp.${var.region}.amazonaws.com https://*.auth.${var.region}.amazoncognito.com https://*.s3.${var.region}.amazonaws.com https://s3.${var.region}.amazonaws.com; img-src 'self' data: blob: ${local.computer_sandbox_map_img_src}; font-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors ${local.computer_host_frame_ancestors};"
1829
1853
  }
1830
1854
 
1831
1855
  module "computer_site" {
@@ -1927,7 +1951,7 @@ module "customer_domain" {
1927
1951
  # https://*.tile.openstreetmap.org https://api.mapbox.com;
1928
1952
  # font-src 'self' data:; connect-src 'none';
1929
1953
  # frame-src https://www.openstreetmap.org; object-src 'none';
1930
- # base-uri 'self'; frame-ancestors <web parents + desktop custom protocols>;
1954
+ # base-uri 'self'; frame-ancestors <web parents>;
1931
1955
  #
1932
1956
  # Provisioning is gated on var.computer_sandbox_domain — leave empty in
1933
1957
  # stages that haven't allocated the subdomain yet.
@@ -1936,18 +1960,10 @@ module "customer_domain" {
1936
1960
  locals {
1937
1961
  computer_sandbox_enabled = var.computer_sandbox_domain != ""
1938
1962
 
1939
- computer_sandbox_desktop_parent_origins = [
1940
- "thinkwork://app",
1941
- "thinkwork-dev://app",
1942
- "thinkwork-canary://app",
1943
- ]
1944
- computer_sandbox_allowed_parent_origin_list = distinct(concat(
1945
- [
1946
- for origin in split(",", replace(var.computer_sandbox_allowed_parent_origins, " ", "")) :
1947
- origin if origin != ""
1948
- ],
1949
- local.computer_sandbox_desktop_parent_origins
1950
- ))
1963
+ computer_sandbox_allowed_parent_origin_list = distinct([
1964
+ for origin in split(",", replace(var.computer_sandbox_allowed_parent_origins, " ", "")) :
1965
+ origin if origin != ""
1966
+ ])
1951
1967
  computer_sandbox_allowed_parent_origins_effective = join(",", local.computer_sandbox_allowed_parent_origin_list)
1952
1968
  computer_sandbox_frame_ancestors = local.computer_sandbox_enabled && length(local.computer_sandbox_allowed_parent_origin_list) > 0 ? join(" ", local.computer_sandbox_allowed_parent_origin_list) : "'none'"
1953
1969
 
@@ -1999,20 +2015,6 @@ module "computer_sandbox_site" {
1999
2015
  }
2000
2016
  }
2001
2017
 
2002
- ################################################################################
2003
- # Docs Static Site
2004
- ################################################################################
2005
-
2006
- module "docs_site" {
2007
- source = "../app/static-site"
2008
-
2009
- stage = var.stage
2010
- site_name = "docs"
2011
- bucket_name = "thinkwork-${var.stage}-${var.account_id}-docs"
2012
- custom_domain = var.docs_domain
2013
- certificate_arn = var.docs_certificate_arn
2014
- }
2015
-
2016
2018
  ################################################################################
2017
2019
  # Public Website (www)
2018
2020
  ################################################################################