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
@@ -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)
@@ -21,12 +21,22 @@ terraform {
21
21
  ################################################################################
22
22
 
23
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
24
31
  workload_identity_name = "thinkwork-${var.stage}-multiplayer-proof"
25
32
  credential_provider_name = "thinkwork-${var.stage}-proof-oauth"
26
33
  twenty_credential_provider_name = "thinkwork-${var.stage}-twenty-crm"
27
- oauth_issuer = trimsuffix(var.oauth_issuer, "/")
28
- oauth_return_url = "${local.oauth_issuer}/complete"
29
- allowed_oauth_return_urls = distinct(concat([local.oauth_return_url], var.user_federation_return_urls))
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))
30
40
  configuration_hash = nonsensitive(sha256(jsonencode({
31
41
  issuer = local.oauth_issuer
32
42
  client_id = var.oauth_client_id
@@ -39,6 +49,9 @@ locals {
39
49
  twenty_oauth_issuer = var.twenty_oauth_issuer
40
50
  twenty_oauth_resource = var.twenty_oauth_resource
41
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")
42
55
  })))
43
56
  }
44
57
 
@@ -46,7 +59,7 @@ locals {
46
59
  # value never enters Terraform state. AgentCore Identity reads the JSON key
47
60
  # directly and remains the sole custodian of each user's downstream grant.
48
61
  resource "aws_secretsmanager_secret" "twenty_oauth_client" {
49
- count = var.enabled ? 1 : 0
62
+ count = var.twenty_enabled ? 1 : 0
50
63
 
51
64
  name = "thinkwork/${var.stage}/agentcore-identity/twenty-crm-oauth-client"
52
65
  description = "Confidential Twenty OAuth client used only by AgentCore Identity Token Vault"
@@ -60,7 +73,7 @@ resource "aws_secretsmanager_secret" "twenty_oauth_client" {
60
73
  }
61
74
 
62
75
  resource "aws_secretsmanager_secret_policy" "twenty_oauth_client" {
63
- count = var.enabled ? 1 : 0
76
+ count = var.twenty_enabled ? 1 : 0
64
77
  secret_arn = aws_secretsmanager_secret.twenty_oauth_client[0].arn
65
78
  policy = jsonencode({
66
79
  Version = "2012-10-17"
@@ -111,12 +124,82 @@ resource "terraform_data" "identity_lifecycle" {
111
124
  }
112
125
  }
113
126
 
114
- # Additive user-federation lifecycle. It may update the workload's callback
115
- # allowlist in place, but it never owns or deletes the workload/Gateway provider.
116
- # This keeps the proven exact-user admission path live while Twenty is added,
117
- # rotated, or removed independently.
118
- resource "terraform_data" "twenty_identity_lifecycle" {
119
- count = var.enabled ? 1 : 0
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
120
203
 
121
204
  input = {
122
205
  region = var.region
@@ -138,17 +221,10 @@ resource "terraform_data" "twenty_identity_lifecycle" {
138
221
  }
139
222
  }
140
223
 
141
- provisioner "local-exec" {
142
- when = destroy
143
- command = "bash ${path.module}/scripts/delete_twenty_identity.sh"
144
- environment = {
145
- AWS_REGION = self.input.region
146
- TWENTY_CREDENTIAL_PROVIDER_NAME = self.input.twenty_credential_provider_name
147
- }
148
- }
149
-
150
224
  depends_on = [
151
225
  terraform_data.identity_lifecycle,
226
+ terraform_data.workload_identity_ensure,
227
+ terraform_data.twenty_identity_cleanup_owner,
152
228
  aws_secretsmanager_secret_policy.twenty_oauth_client,
153
229
  ]
154
230
  }
@@ -157,17 +233,21 @@ resource "terraform_data" "twenty_identity_lifecycle" {
157
233
  # Gateway IAM can therefore scope vault and secret access exactly rather than
158
234
  # granting a token-vault or Secrets Manager wildcard.
159
235
  data "external" "identity_state" {
160
- count = var.enabled ? 1 : 0
236
+ count = local.identity_enabled ? 1 : 0
161
237
  depends_on = [
162
238
  terraform_data.identity_lifecycle,
163
- terraform_data.twenty_identity_lifecycle,
239
+ terraform_data.workload_identity_ensure,
240
+ terraform_data.twenty_identity_reconciliation,
164
241
  ]
165
242
  program = ["bash", "${path.module}/scripts/read_identity.sh"]
166
243
 
167
244
  query = {
168
- region = var.region
169
- workload_identity_name = local.workload_identity_name
170
- credential_provider_name = local.credential_provider_name
171
- twenty_credential_provider_name = local.twenty_credential_provider_name
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 : ""
172
252
  }
173
253
  }
@@ -1,6 +1,6 @@
1
1
  output "workload_identity_name" {
2
2
  description = "Name used for GetWorkloadAccessTokenForJWT. Empty when disabled."
3
- value = var.enabled ? local.workload_identity_name : ""
3
+ value = local.identity_enabled ? local.workload_identity_name : ""
4
4
  }
5
5
 
6
6
  output "credential_provider_name" {
@@ -15,28 +15,28 @@ output "oauth_return_url" {
15
15
 
16
16
  output "twenty_credential_provider_name" {
17
17
  description = "AgentCore Identity provider name for per-user Twenty grants."
18
- value = var.enabled ? local.twenty_credential_provider_name : ""
18
+ value = var.twenty_enabled ? local.twenty_credential_provider_name : ""
19
19
  }
20
20
 
21
21
  output "twenty_credential_provider_arn" {
22
22
  description = "AgentCore Identity provider ARN for per-user Twenty grants."
23
- value = var.enabled ? data.external.identity_state[0].result.twenty_credential_provider_arn : ""
23
+ value = var.twenty_enabled ? data.external.identity_state[0].result.twenty_credential_provider_arn : ""
24
24
  }
25
25
 
26
26
  output "twenty_oauth_callback_url" {
27
27
  description = "Service-generated callback URL registered with Twenty OAuth."
28
- value = var.enabled ? data.external.identity_state[0].result.twenty_oauth_callback_url : ""
28
+ value = var.twenty_enabled ? data.external.identity_state[0].result.twenty_oauth_callback_url : ""
29
29
  }
30
30
 
31
31
  output "twenty_client_secret_arn" {
32
32
  description = "External Secrets Manager client record consumed by AgentCore Identity."
33
- value = var.enabled ? aws_secretsmanager_secret.twenty_oauth_client[0].arn : ""
33
+ value = var.twenty_enabled ? aws_secretsmanager_secret.twenty_oauth_client[0].arn : ""
34
34
  sensitive = true
35
35
  }
36
36
 
37
37
  output "workload_identity_arn" {
38
38
  description = "Service-generated workload identity ARN. Empty when disabled."
39
- value = var.enabled ? data.external.identity_state[0].result.workload_identity_arn : ""
39
+ value = local.identity_enabled ? data.external.identity_state[0].result.workload_identity_arn : ""
40
40
  }
41
41
 
42
42
  output "credential_provider_arn" {