thinkwork-cli 0.12.12 → 0.12.16

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 (475) hide show
  1. package/README.md +42 -38
  2. package/dist/{api-client-ASJWIG3Z.js → api-client-JUBCQQDF.js} +1 -1
  3. package/dist/{chunk-JEHV35EU.js → chunk-STY44FQO.js} +353 -30
  4. package/dist/cli.js +23823 -10151
  5. package/dist/commands/enterprise/templates/deploy-repo/.github/workflows/deploy.yml +17 -6
  6. package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +81 -9
  7. package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +183 -21
  8. package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +1 -2
  9. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +286 -0
  10. package/dist/drizzle/0000_petite_marvel_apes.sql +1188 -0
  11. package/dist/drizzle/0001_milky_surge.sql +103 -0
  12. package/dist/drizzle/0002_aberrant_mattie_franklin.sql +41 -0
  13. package/dist/drizzle/0003_exotic_ogun.sql +3 -0
  14. package/dist/drizzle/0004_glamorous_magneto.sql +2 -0
  15. package/dist/drizzle/0005_ancient_nocturne.sql +2 -0
  16. package/dist/drizzle/0006_new_colonel_america.sql +2 -0
  17. package/dist/drizzle/0007_unique_external_task_id.sql +31 -0
  18. package/dist/drizzle/0008_external_task_id_column.sql +43 -0
  19. package/dist/drizzle/0009_evaluations.sql +64 -0
  20. package/dist/drizzle/0010_eval_agent_template.sql +2 -0
  21. package/dist/drizzle/0011_eval_run_template.sql +2 -0
  22. package/dist/drizzle/0012_eval_seed_unique.sql +16 -0
  23. package/dist/drizzle/0013_motionless_white_queen.sql +140 -0
  24. package/dist/drizzle/0014_wiki_aggregation.sql +11 -0
  25. package/dist/drizzle/0015_pg_trgm_alias_title_indexes.sql +9 -0
  26. package/dist/drizzle/0016_wiki_schema_drops.sql +24 -0
  27. package/dist/drizzle/0017_wiki_places.sql +46 -0
  28. package/dist/drizzle/0018_agent_workspace_overlay.sql +26 -0
  29. package/dist/drizzle/0018_cool_firebird.sql +24 -0
  30. package/dist/drizzle/0018_skill_runs.sql +84 -0
  31. package/dist/drizzle/0019_condemned_dragon_man.sql +95 -0
  32. package/dist/drizzle/0019_webhook_skill_runs.sql +58 -0
  33. package/dist/drizzle/0020_crazy_the_anarchist.sql +9 -0
  34. package/dist/drizzle/0020_mutation_idempotency.sql +64 -0
  35. package/dist/drizzle/0021_crm_work_links.sql +76 -0
  36. package/dist/drizzle/0021_skill_runs_completion_hmac.sql +33 -0
  37. package/dist/drizzle/0022_add_billing_tables.sql +70 -0
  38. package/dist/drizzle/0022_seed_thinkwork_admin_permissions.sql +160 -0
  39. package/dist/drizzle/0022_stripe_billing_indexes.sql +48 -0
  40. package/dist/drizzle/0023_tenants_deactivation.sql +27 -0
  41. package/dist/drizzle/0024_tenant_mcp_admin_keys.sql +63 -0
  42. package/dist/drizzle/0025_v1_agent_architecture.sql +182 -0
  43. package/dist/drizzle/0026_thread_turns_add_kind.sql +28 -0
  44. package/dist/drizzle/0027_capability_catalog_and_manifests.sql +219 -0
  45. package/dist/drizzle/0028_aws_s3_extension.sql +26 -0
  46. package/dist/drizzle/0029_collapse_execution_types.sql +42 -0
  47. package/dist/drizzle/0030_retire_composition_primitives.sql +48 -0
  48. package/dist/drizzle/0031_thread_cleanup_drops.sql +131 -0
  49. package/dist/drizzle/0032_add_browser_automation_template_config.sql +24 -0
  50. package/dist/drizzle/0033_agent_operation_leases.sql +32 -0
  51. package/dist/drizzle/0034_agent_workspace_events.sql +116 -0
  52. package/dist/drizzle/0035_workspace_review_decisions.sql +19 -0
  53. package/dist/drizzle/0036_user_scoped_memory_wiki.sql +152 -0
  54. package/dist/drizzle/0037_user_profile_operating_model.sql +34 -0
  55. package/dist/drizzle/0042_agent_runtime_selector.sql +41 -0
  56. package/dist/drizzle/0043_wiki_search_tsv_tokenization.sql +48 -0
  57. package/dist/drizzle/0044_add_web_search_template_config.sql +7 -0
  58. package/dist/drizzle/0045_add_send_email_template_config.sql +7 -0
  59. package/dist/drizzle/0046_tenant_mcp_context_tools.sql +26 -0
  60. package/dist/drizzle/0047_add_context_engine_template_config.sql +7 -0
  61. package/dist/drizzle/0048_tenant_context_provider_settings.sql +22 -0
  62. package/dist/drizzle/0050_brain_v0_entity_subtype.sql +29 -0
  63. package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +170 -0
  64. package/dist/drizzle/0052_brain_v0_external_refs.sql +41 -0
  65. package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +48 -0
  66. package/dist/drizzle/0054_wiki_compile_jobs_input.sql +28 -0
  67. package/dist/drizzle/0055_routines_stepfunctions_substrate.sql +202 -0
  68. package/dist/drizzle/0056_routine_step_events_dedup_idx.sql +25 -0
  69. package/dist/drizzle/0057_archive_legacy_python_routines.sql +36 -0
  70. package/dist/drizzle/0058_routines_visibility_and_owning_agent.sql +61 -0
  71. package/dist/drizzle/0061_routine_execution_asl_version_id.sql +29 -0
  72. package/dist/drizzle/0062_migrate_pi_to_flue.sql +110 -0
  73. package/dist/drizzle/0063_threads_session_data.sql +23 -0
  74. package/dist/drizzle/0064_tenant_credentials.sql +65 -0
  75. package/dist/drizzle/0067_thinkwork_computers_phase_one.sql +174 -0
  76. package/dist/drizzle/0068_drop_system_workflows_and_activation.sql +115 -0
  77. package/dist/drizzle/0069_compliance_schema.sql +364 -0
  78. package/dist/drizzle/0070_compliance_aurora_roles.sql +171 -0
  79. package/dist/drizzle/0072_threads_computer_ownership.sql +24 -0
  80. package/dist/drizzle/0073_compliance_tenant_anchor_state.sql +105 -0
  81. package/dist/drizzle/0074_compliance_event_hash_index.sql +49 -0
  82. package/dist/drizzle/0075_scheduled_jobs_computer_ownership.sql +27 -0
  83. package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +69 -0
  84. package/dist/drizzle/0077_computers_primary_agent_id.sql +38 -0
  85. package/dist/drizzle/0078_tenant_customize_catalog.sql +46 -0
  86. package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +53 -0
  87. package/dist/drizzle/0081_routines_catalog_slug.sql +42 -0
  88. package/dist/drizzle/0082_messages_parts_jsonb.sql +52 -0
  89. package/dist/drizzle/0083_computer_runbooks.sql +103 -0
  90. package/dist/drizzle/0084_artifacts_favorited_at.sql +32 -0
  91. package/dist/drizzle/0085_seed_thinkwork_computer_default_template.sql +46 -0
  92. package/dist/drizzle/0086_update_default_template_models.sql +19 -0
  93. package/dist/drizzle/0087_retire_oss_connectors.sql +77 -0
  94. package/dist/drizzle/0088_compliance_event_types_finance_pilot.sql +37 -0
  95. package/dist/drizzle/0089_remove_maniflow_eval_seeds.sql +81 -0
  96. package/dist/drizzle/0089_wiki_schema_extraction.sql +315 -0
  97. package/dist/drizzle/0090_brain_schema_extraction.sql +274 -0
  98. package/dist/drizzle/0091_drop_wiki_brain_compat_views.sql +118 -0
  99. package/dist/drizzle/0092_finish_oss_connector_retirement.sql +97 -0
  100. package/dist/drizzle/0093_eval_runs_scheduled_job_id.sql +23 -0
  101. package/dist/drizzle/0094_slack_workspace_app.sql +140 -0
  102. package/dist/drizzle/0095_eval_runs_computer_id.sql +22 -0
  103. package/dist/drizzle/0096_true_redteam_eval_seed_cleanup.sql +67 -0
  104. package/dist/drizzle/0097_shared_computers.sql +191 -0
  105. package/dist/drizzle/0098_business_ontology.sql +587 -0
  106. package/dist/drizzle/0099_thread_idle_learning.sql +145 -0
  107. package/dist/drizzle/0100_ontology_gated_brain_dynamic_subtypes.sql +7 -0
  108. package/dist/drizzle/0101_ontology_gated_unresolved_mentions_dynamic_subtypes.sql +7 -0
  109. package/dist/drizzle/0102_wiki_brain_owner_repair.sql +87 -0
  110. package/dist/drizzle/0103_brain_section_source_trigger_repair.sql +75 -0
  111. package/dist/drizzle/0104_expand_seed_ontology_for_user_memory.sql +166 -0
  112. package/dist/drizzle/0105_spaces_domain.sql +349 -0
  113. package/dist/drizzle/0106_expand_seed_ontology_for_triples.sql +173 -0
  114. package/dist/drizzle/0106_space_threads_participants.sql +197 -0
  115. package/dist/drizzle/0107_linked_task_mirror.sql +245 -0
  116. package/dist/drizzle/0108_message_mentions.sql +95 -0
  117. package/dist/drizzle/0109_space_owned_threads.sql +217 -0
  118. package/dist/drizzle/0110_thread_participant_read_state.sql +21 -0
  119. package/dist/drizzle/0111_agents_tenant_name_unique.sql +38 -0
  120. package/dist/drizzle/0112_recast_spaces_as_contextual_workrooms.sql +138 -0
  121. package/dist/drizzle/0113_agents_own_runtime_fields.sql +48 -0
  122. package/dist/drizzle/0114_migrate_templates_to_agents_and_spaces.sql +361 -0
  123. package/dist/drizzle/0115_remove_computer_eval_template_dependencies.sql +69 -0
  124. package/dist/drizzle/0116_backfill_thread_participants_access.sql +126 -0
  125. package/dist/drizzle/0117_space_access_mode.sql +40 -0
  126. package/dist/drizzle/0118_normalize_tenant_member_principal_type.sql +41 -0
  127. package/dist/drizzle/0119_space_knowledge_bases.sql +126 -0
  128. package/dist/drizzle/0120_space_automations.sql +58 -0
  129. package/dist/drizzle/0121_drop_computer_runbooks.sql +26 -0
  130. package/dist/drizzle/0122_space_email_triggers.sql +24 -0
  131. package/dist/drizzle/0123_single_platform_agent_and_overrides.sql +58 -0
  132. package/dist/drizzle/0124_thread_turns_finalized_at.sql +17 -0
  133. package/dist/drizzle/0125_drop_space_agent_assignments.sql +42 -0
  134. package/dist/drizzle/0126_migrate_flue_to_pi.sql +42 -0
  135. package/dist/drizzle/0127_runtime_type_observability.sql +28 -0
  136. package/dist/drizzle/0128_drop_eval_test_cases_agent_id.sql +37 -0
  137. package/dist/drizzle/0129_drop_code_factory_tables.sql +43 -0
  138. package/dist/drizzle/0129_eval_results_system_prompt.sql +30 -0
  139. package/dist/drizzle/0130_drop_teams_and_team_id_columns.sql +54 -0
  140. package/dist/drizzle/0131_drop_skill_catalog_and_tenant_skills.sql +33 -0
  141. package/dist/drizzle/0132_drop_computer_tables.sql +31 -0
  142. package/dist/drizzle/0133_drop_seed_view_tombstones.sql +79 -0
  143. package/dist/drizzle/0133_thread_turns_system_prompt.sql +22 -0
  144. package/dist/drizzle/0134_drop_dead_agent_runtime_tables.sql +53 -0
  145. package/dist/drizzle/0135_native_checklist_linked_tasks.sql +48 -0
  146. package/dist/drizzle/0136_goal_ledger.sql +55 -0
  147. package/dist/drizzle/0136_space_email_trigger_status.sql +66 -0
  148. package/dist/drizzle/0137_thread_participant_pins.sql +25 -0
  149. package/dist/drizzle/0138_users_cognito_sub.sql +30 -0
  150. package/dist/drizzle/0139_mobile_turn_client_id.sql +23 -0
  151. package/dist/drizzle/0140_workspace_folder_names.sql +36 -0
  152. package/dist/drizzle/0141_eval_runs_desktop_pi_provenance.sql +31 -0
  153. package/dist/drizzle/0142_pi_only_agent_runtime.sql +61 -0
  154. package/dist/drizzle/0143_add_web_extract_builtin_config.sql +28 -0
  155. package/dist/drizzle/0144_skill_catalog_index.sql +40 -0
  156. package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +512 -0
  157. package/dist/drizzle/0146_knowledge_graph_source_scope.sql +413 -0
  158. package/dist/drizzle/0147_knowledge_graph_scope_trigger_qualification.sql +211 -0
  159. package/dist/drizzle/0148_user_cost_attribution.sql +171 -0
  160. package/dist/drizzle/0149_managed_mcp_servers.sql +64 -0
  161. package/dist/drizzle/0149_user_model_approvals.sql +89 -0
  162. package/dist/drizzle/0150_bootstrap_first_admin_claim.sql +11 -0
  163. package/dist/drizzle/0151_managed_deployments.sql +91 -0
  164. package/dist/drizzle/0152_agent_profiles.sql +81 -0
  165. package/dist/drizzle/0153_message_mentions_agent_profile.sql +9 -0
  166. package/dist/drizzle/0154_customer_deployment_sessions.sql +61 -0
  167. package/dist/drizzle/0155_tenant_model_catalog.sql +152 -0
  168. package/dist/drizzle/0156_bootstrap_credential_leases.sql +52 -0
  169. package/dist/drizzle/0157_knowledge_graph_observations_source.sql +79 -0
  170. package/dist/drizzle/0158_pending_user_questions.sql +80 -0
  171. package/dist/drizzle/0158_wiki_tenant_scope.sql +38 -0
  172. package/dist/drizzle/0159_agent_profiles_space_local.sql +49 -0
  173. package/dist/drizzle/0159_application_plugins.sql +193 -0
  174. package/dist/drizzle/0159_eval_scoring_version.sql +46 -0
  175. package/dist/drizzle/0160_compliance_event_types_plugins.sql +37 -0
  176. package/dist/drizzle/0160_eval_datasets.sql +75 -0
  177. package/dist/drizzle/0160_eval_results_thread_turn.sql +35 -0
  178. package/dist/drizzle/0161_eval_run_pinning.sql +54 -0
  179. package/dist/drizzle/0161_tenant_mcp_servers_plugin_source.sql +62 -0
  180. package/dist/drizzle/0162_eval_result_override.sql +49 -0
  181. package/dist/drizzle/0163_eval_replay_tool_allowlist.sql +50 -0
  182. package/dist/drizzle/0164_eval_replay_tool_override_mode.sql +52 -0
  183. package/dist/drizzle/0165_plugin_premium_entitlements.sql +153 -0
  184. package/dist/drizzle/0166_company_brain_substrate_contract.sql +386 -0
  185. package/dist/drizzle/0167_company_brain_artifact_manifest_runtime.sql +85 -0
  186. package/dist/drizzle/0168_eval_skill_gate.sql +44 -0
  187. package/dist/drizzle/0169_release_update_jobs.sql +67 -0
  188. package/dist/drizzle/0170_email_channel_plugin.sql +291 -0
  189. package/dist/drizzle/0171_linked_task_external_providers.sql +30 -0
  190. package/dist/drizzle/0172_email_channel_sendgrid_provider.sql +8 -0
  191. package/dist/drizzle/0173_auth_provider_resources.sql +83 -0
  192. package/dist/drizzle/0174_workos_auth_bridges.sql +37 -0
  193. package/dist/drizzle/0175_workos_auth_sessions.sql +38 -0
  194. package/dist/drizzle/0176_n8n_agent_step_runs.sql +107 -0
  195. package/dist/drizzle/0177_workflow_control_plane.sql +281 -0
  196. package/dist/drizzle/0178_workflow_backfill_existing_routines.sql +525 -0
  197. package/dist/drizzle/0179_tenant_goal_budget_settings.sql +21 -0
  198. package/dist/drizzle/0180_skill_drafts.sql +89 -0
  199. package/dist/drizzle/0181_skill_draft_publish_status.sql +21 -0
  200. package/dist/drizzle/0182_agent_loops.sql +260 -0
  201. package/dist/drizzle/0183_okf_artifact_manifests.sql +52 -0
  202. package/dist/drizzle/0184_skill_catalog_trust_cache.sql +35 -0
  203. package/dist/drizzle/0185_agent_loop_space.sql +16 -0
  204. package/dist/drizzle/0186_tenant_mcp_runtime_metadata.sql +10 -0
  205. package/dist/drizzle/0187_native_work_items.sql +220 -0
  206. package/dist/drizzle/0188_company_etl_plugin_rename.sql +99 -0
  207. package/dist/drizzle/0189_trace_cost_substrate.sql +247 -0
  208. package/dist/drizzle/0190_billing_export_reconciliation.sql +101 -0
  209. package/dist/drizzle/0191_open_engine_work_item_queue.sql +66 -0
  210. package/dist/drizzle/0192_work_item_labels.sql +44 -0
  211. package/dist/drizzle/0193_work_item_documents.sql +31 -0
  212. package/dist/drizzle/0194_memory_retain_attempts.sql +103 -0
  213. package/dist/drizzle/0194_work_item_comments.sql +35 -0
  214. package/dist/drizzle/0195_plugin_app_overlays.sql +45 -0
  215. package/dist/drizzle/0196_pi_extensions.sql +118 -0
  216. package/dist/drizzle/0197_eval_profiles.sql +150 -0
  217. package/dist/drizzle/0197_rcm_context_columns.sql +28 -0
  218. package/dist/drizzle/0198_eval_runs_unstable.sql +27 -0
  219. package/dist/drizzle/0199_eval_runs_cost_partial.sql +27 -0
  220. package/dist/drizzle/meta/_journal.json +160 -0
  221. package/dist/plugins/README.md +162 -0
  222. package/dist/plugins/catalog/package.json +40 -0
  223. package/dist/plugins/catalog/scripts/build-catalog.ts +120 -0
  224. package/dist/plugins/catalog/scripts/generate-plugin-registry.ts +144 -0
  225. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +86 -0
  226. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +308 -0
  227. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +861 -0
  228. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +124 -0
  229. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +120 -0
  230. package/dist/plugins/catalog/src/catalog.ts +434 -0
  231. package/dist/plugins/catalog/src/contracts.ts +1498 -0
  232. package/dist/plugins/catalog/src/index.ts +4 -0
  233. package/dist/plugins/catalog/src/plugin-package.ts +171 -0
  234. package/dist/plugins/catalog/src/registry/generated-first-party.ts +76 -0
  235. package/dist/plugins/catalog/src/registry/index.ts +1 -0
  236. package/dist/plugins/catalog/tsconfig.json +8 -0
  237. package/dist/plugins/company-brain/README.md +37 -0
  238. package/dist/plugins/company-brain/package.json +33 -0
  239. package/dist/plugins/company-brain/runtime/cognee/Dockerfile +11 -0
  240. package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +458 -0
  241. package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +539 -0
  242. package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +312 -0
  243. package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +449 -0
  244. package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +655 -0
  245. package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +474 -0
  246. package/dist/plugins/company-brain/src/api/cognee-client.ts +896 -0
  247. package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +49 -0
  248. package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +84 -0
  249. package/dist/plugins/company-brain/src/api/context-engine-provider.ts +888 -0
  250. package/dist/plugins/company-brain/src/api/migration.ts +665 -0
  251. package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +396 -0
  252. package/dist/plugins/company-brain/src/index.ts +62 -0
  253. package/dist/plugins/company-brain/src/manifest.ts +112 -0
  254. package/dist/plugins/company-brain/terraform/cognee/README.md +263 -0
  255. package/dist/plugins/company-brain/terraform/cognee/main.tf +766 -0
  256. package/dist/plugins/company-brain/terraform/cognee/outputs.tf +144 -0
  257. package/dist/plugins/company-brain/terraform/cognee/variables.tf +402 -0
  258. package/dist/plugins/company-brain/test/api/cognee-client.test.ts +562 -0
  259. package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +74 -0
  260. package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +447 -0
  261. package/dist/plugins/company-brain/test/api/migration.test.ts +465 -0
  262. package/dist/plugins/company-brain/test/manifest.test.ts +95 -0
  263. package/dist/plugins/company-brain/tsconfig.json +8 -0
  264. package/dist/plugins/company-data/README.md +49 -0
  265. package/dist/plugins/company-data/package.json +21 -0
  266. package/dist/plugins/company-data/src/index.ts +30 -0
  267. package/dist/plugins/company-data/src/manifest.ts +26 -0
  268. package/dist/plugins/company-data/test/manifest.test.ts +116 -0
  269. package/dist/plugins/company-data/tsconfig.json +8 -0
  270. package/dist/plugins/company-etl/README.md +46 -0
  271. package/dist/plugins/company-etl/package.json +21 -0
  272. package/dist/plugins/company-etl/src/index.ts +29 -0
  273. package/dist/plugins/company-etl/src/manifest.ts +26 -0
  274. package/dist/plugins/company-etl/test/manifest.test.ts +108 -0
  275. package/dist/plugins/company-etl/tsconfig.json +8 -0
  276. package/dist/plugins/email-channel/README.md +41 -0
  277. package/dist/plugins/email-channel/package.json +22 -0
  278. package/dist/plugins/email-channel/src/index.ts +36 -0
  279. package/dist/plugins/email-channel/src/manifest.ts +44 -0
  280. package/dist/plugins/email-channel/src/provider-contract.ts +36 -0
  281. package/dist/plugins/email-channel/test/manifest.test.ts +119 -0
  282. package/dist/plugins/email-channel/tsconfig.json +8 -0
  283. package/dist/plugins/lastmile/README.md +50 -0
  284. package/dist/plugins/lastmile/package.json +23 -0
  285. package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +661 -0
  286. package/dist/plugins/lastmile/src/api/tasks-adapter.ts +572 -0
  287. package/dist/plugins/lastmile/src/discovery.fixture.ts +64 -0
  288. package/dist/plugins/lastmile/src/index.ts +46 -0
  289. package/dist/plugins/lastmile/src/manifest.ts +139 -0
  290. package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +266 -0
  291. package/dist/plugins/lastmile/test/discovery.test.ts +88 -0
  292. package/dist/plugins/lastmile/tsconfig.json +8 -0
  293. package/dist/plugins/n8n/README.md +201 -0
  294. package/dist/plugins/n8n/n8n-app/README.md +33 -0
  295. package/dist/plugins/n8n/n8n-app/package.json +23 -0
  296. package/dist/plugins/n8n/n8n-app/src/application-config.ts +45 -0
  297. package/dist/plugins/n8n/n8n-app/src/front-components/thinkwork-workflows.front-component.tsx +970 -0
  298. package/dist/plugins/n8n/n8n-app/src/lib/n8n-app-data.ts +190 -0
  299. package/dist/plugins/n8n/n8n-app/src/vite-env.d.ts +7 -0
  300. package/dist/plugins/n8n/n8n-app/tsconfig.json +15 -0
  301. package/dist/plugins/n8n/package.json +35 -0
  302. package/dist/plugins/n8n/runtime/Dockerfile +18 -0
  303. package/dist/plugins/n8n/runtime/n8n-task-runners.json +14 -0
  304. package/dist/plugins/n8n/runtime/n8n-task-runners.json.template +14 -0
  305. package/dist/plugins/n8n/runtime/package-lock.json +13 -0
  306. package/dist/plugins/n8n/runtime/package.json +7 -0
  307. package/dist/plugins/n8n/scripts/sync-thinkwork-app.mjs +268 -0
  308. package/dist/plugins/n8n/smoke/README.md +95 -0
  309. package/dist/plugins/n8n/smoke/n8n-agent-step-bridge-smoke.mjs +866 -0
  310. package/dist/plugins/n8n/smoke/n8n-integrated-app-smoke.mjs +262 -0
  311. package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +409 -0
  312. package/dist/plugins/n8n/smoke/n8n-mcp-smoke.mjs +624 -0
  313. package/dist/plugins/n8n/src/deployment/image-build.ts +172 -0
  314. package/dist/plugins/n8n/src/deployment/managed-app.ts +429 -0
  315. package/dist/plugins/n8n/src/index.ts +79 -0
  316. package/dist/plugins/n8n/src/manifest.ts +403 -0
  317. package/dist/plugins/n8n/src/package-config.ts +186 -0
  318. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/SKILL.md +76 -0
  319. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
  320. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
  321. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/workflow-authoring.md +45 -0
  322. package/dist/plugins/n8n/terraform/n8n/README.md +113 -0
  323. package/dist/plugins/n8n/terraform/n8n/main.tf +918 -0
  324. package/dist/plugins/n8n/terraform/n8n/outputs.tf +124 -0
  325. package/dist/plugins/n8n/terraform/n8n/scripts/sync-database.py +250 -0
  326. package/dist/plugins/n8n/terraform/n8n/scripts/test_sync_database.py +55 -0
  327. package/dist/plugins/n8n/terraform/n8n/variables.tf +354 -0
  328. package/dist/plugins/n8n/test/image-build.test.ts +118 -0
  329. package/dist/plugins/n8n/test/integrated-app-contract.test.ts +353 -0
  330. package/dist/plugins/n8n/test/manifest.test.ts +278 -0
  331. package/dist/plugins/n8n/test/package-config.test.ts +57 -0
  332. package/dist/plugins/n8n/tsconfig.json +8 -0
  333. package/dist/plugins/sendgrid/README.md +30 -0
  334. package/dist/plugins/sendgrid/package.json +22 -0
  335. package/dist/plugins/sendgrid/src/index.ts +36 -0
  336. package/dist/plugins/sendgrid/src/manifest.ts +37 -0
  337. package/dist/plugins/sendgrid/src/provider-contract.ts +14 -0
  338. package/dist/plugins/sendgrid/test/manifest.test.ts +80 -0
  339. package/dist/plugins/sendgrid/tsconfig.json +8 -0
  340. package/dist/plugins/twenty/README.md +78 -0
  341. package/dist/plugins/twenty/package.json +27 -0
  342. package/dist/plugins/twenty/scripts/__tests__/sync-thinkwork-app.test.mjs +67 -0
  343. package/dist/plugins/twenty/scripts/sync-thinkwork-app.mjs +185 -0
  344. package/dist/plugins/twenty/scripts/wire-thinkwork-workflow.mjs +658 -0
  345. package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +359 -0
  346. package/dist/plugins/twenty/smoke/twenty-mcp-oauth-smoke.mjs +574 -0
  347. package/dist/plugins/twenty/src/api/cutover.ts +209 -0
  348. package/dist/plugins/twenty/src/deployment/managed-app.ts +253 -0
  349. package/dist/plugins/twenty/src/index.ts +55 -0
  350. package/dist/plugins/twenty/src/manifest.ts +259 -0
  351. package/dist/plugins/twenty/terraform/twenty/README.md +97 -0
  352. package/dist/plugins/twenty/terraform/twenty/main.tf +829 -0
  353. package/dist/plugins/twenty/terraform/twenty/outputs.tf +79 -0
  354. package/dist/plugins/twenty/terraform/twenty/variables.tf +268 -0
  355. package/dist/plugins/twenty/test/api/cutover.test.ts +183 -0
  356. package/dist/plugins/twenty/test/manifest.test.ts +463 -0
  357. package/dist/plugins/twenty/tsconfig.json +9 -0
  358. package/dist/plugins/twenty/twenty-app/.nvmrc +1 -0
  359. package/dist/plugins/twenty/twenty-app/README.md +148 -0
  360. package/dist/plugins/twenty/twenty-app/package.json +30 -0
  361. package/dist/plugins/twenty/twenty-app/src/application-config.ts +44 -0
  362. package/dist/plugins/twenty/twenty-app/src/constants/universal-identifiers.ts +18 -0
  363. package/dist/plugins/twenty/twenty-app/src/default-role.ts +24 -0
  364. package/dist/plugins/twenty/twenty-app/src/front-components/thinkwork-settings.front-component.tsx +299 -0
  365. package/dist/plugins/twenty/twenty-app/src/logic-functions/thinkwork-webhook.logic-function.ts +354 -0
  366. package/dist/plugins/twenty/twenty-app/tsconfig.json +14 -0
  367. package/dist/plugins/twenty/twenty-app/yarn.lock +4144 -0
  368. package/dist/plugins/workos-auth/package.json +22 -0
  369. package/dist/plugins/workos-auth/src/index.ts +30 -0
  370. package/dist/plugins/workos-auth/src/manifest.ts +40 -0
  371. package/dist/plugins/workos-auth/src/provider-contract.ts +39 -0
  372. package/dist/plugins/workos-auth/test/manifest.test.ts +94 -0
  373. package/dist/plugins/workos-auth/tsconfig.json +8 -0
  374. package/dist/scripts/bootstrap-workspace.sh +162 -0
  375. package/dist/scripts/post-deploy.sh +290 -0
  376. package/dist/terraform/examples/greenfield/main.tf +903 -75
  377. package/dist/terraform/examples/greenfield/terraform.tfvars.example +67 -8
  378. package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +2 -2
  379. package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +8 -9
  380. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +227 -0
  381. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +277 -0
  382. package/dist/terraform/modules/app/agentcore-memory/README.md +6 -6
  383. package/dist/terraform/modules/app/agentcore-memory/main.tf +1 -1
  384. package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +40 -9
  385. package/dist/terraform/modules/app/agentcore-pi/README.md +61 -0
  386. package/dist/terraform/modules/app/agentcore-pi/main.tf +466 -0
  387. package/dist/terraform/modules/app/agentcore-pi/outputs.tf +38 -0
  388. package/dist/terraform/modules/app/agentcore-pi/variables.tf +145 -0
  389. package/dist/terraform/modules/app/agentcore-platform/main.tf +83 -0
  390. package/dist/terraform/modules/app/agentcore-runtime/main.tf +9 -11
  391. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +5 -1
  392. package/dist/terraform/modules/app/customer-domain/main.tf +483 -0
  393. package/dist/terraform/modules/app/deployment-control-plane/README.md +47 -0
  394. package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +24 -0
  395. package/dist/terraform/modules/app/deployment-control-plane/main.tf +614 -0
  396. package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +54 -0
  397. package/dist/terraform/modules/app/deployment-control-plane/runner.py +4720 -0
  398. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +3026 -0
  399. package/dist/terraform/modules/app/deployment-control-plane/variables.tf +96 -0
  400. package/dist/terraform/modules/app/hindsight-memory/README.md +25 -23
  401. package/dist/terraform/modules/app/hindsight-memory/main.tf +26 -2
  402. package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +17 -50
  403. package/dist/terraform/modules/app/lambda-api/handlers.tf +1290 -679
  404. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +933 -0
  405. package/dist/terraform/modules/app/lambda-api/main.tf +14 -527
  406. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +3 -19
  407. package/dist/terraform/modules/app/lambda-api/outputs.tf +50 -6
  408. package/dist/terraform/modules/app/lambda-api/runtime-config.tf +163 -0
  409. package/dist/terraform/modules/app/lambda-api/slack-app-secrets.tf +3 -1
  410. package/dist/terraform/modules/app/lambda-api/stripe-secrets.tf +3 -1
  411. package/dist/terraform/modules/app/lambda-api/variables.tf +240 -53
  412. package/dist/terraform/modules/app/lambda-api/workspace-events.tf +2 -25
  413. package/dist/terraform/modules/app/ses-email/main.tf +143 -30
  414. package/dist/terraform/modules/app/static-site/main.tf +4 -3
  415. package/dist/terraform/modules/app/www-dns/main.tf +45 -5
  416. package/dist/terraform/modules/app/www-dns/outputs.tf +10 -0
  417. package/dist/terraform/modules/app/www-dns/variables.tf +24 -0
  418. package/dist/terraform/modules/data/aurora-postgres/main.tf +15 -1
  419. package/dist/terraform/modules/data/compliance-exports-bucket/main.tf +1 -1
  420. package/dist/terraform/modules/data/compliance-exports-bucket/variables.tf +6 -0
  421. package/dist/terraform/modules/foundation/cognito/main.tf +167 -11
  422. package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
  423. package/dist/terraform/modules/foundation/cognito/variables.tf +110 -0
  424. package/dist/terraform/modules/foundation/vpc/main.tf +45 -3
  425. package/dist/terraform/modules/foundation/vpc/outputs.tf +20 -0
  426. package/dist/terraform/modules/foundation/vpc/variables.tf +18 -0
  427. package/dist/terraform/modules/thinkwork/README.md +71 -0
  428. package/dist/terraform/modules/thinkwork/main.tf +1325 -174
  429. package/dist/terraform/modules/thinkwork/outputs.tf +457 -24
  430. package/dist/terraform/modules/thinkwork/variables.tf +1169 -93
  431. package/dist/terraform/schema.graphql +82 -9
  432. package/dist/workspace-defaults/files/AGENTS.md +344 -0
  433. package/dist/workspace-defaults/files/CONTEXT.md +8 -0
  434. package/dist/workspace-defaults/files/GUARDRAILS.md +51 -0
  435. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +65 -0
  436. package/dist/workspace-defaults/files/ROUTER.md +24 -0
  437. package/dist/workspace-defaults/files/SPACE.md +62 -0
  438. package/dist/workspace-defaults/files/TOOLS.md +65 -0
  439. package/dist/workspace-defaults/files/USER.md +40 -0
  440. package/dist/workspace-defaults/files/mcp.json +3 -0
  441. package/dist/workspace-defaults/files/memory/contacts.md +13 -0
  442. package/dist/workspace-defaults/files/memory/lessons.md +13 -0
  443. package/dist/workspace-defaults/files/memory/preferences.md +12 -0
  444. package/dist/workspace-defaults/files/skills/.gitkeep +1 -0
  445. package/dist/workspace-defaults/files/skills/artifact-builder/SKILL.md +79 -0
  446. package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +166 -0
  447. package/dist/workspace-defaults/files/skills/automation-loop-designer/SKILL.md +120 -0
  448. package/dist/workspace-defaults/files/skills/skill-creator/LICENSE.txt +202 -0
  449. package/dist/workspace-defaults/files/skills/skill-creator/SKILL.md +485 -0
  450. package/dist/workspace-defaults/files/skills/skill-creator/UPSTREAM.json +124 -0
  451. package/dist/workspace-defaults/files/skills/skill-creator/agents/analyzer.md +274 -0
  452. package/dist/workspace-defaults/files/skills/skill-creator/agents/comparator.md +202 -0
  453. package/dist/workspace-defaults/files/skills/skill-creator/agents/grader.md +223 -0
  454. package/dist/workspace-defaults/files/skills/skill-creator/assets/eval_review.html +146 -0
  455. package/dist/workspace-defaults/files/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  456. package/dist/workspace-defaults/files/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  457. package/dist/workspace-defaults/files/skills/skill-creator/references/schemas.md +430 -0
  458. package/dist/workspace-defaults/files/skills/skill-creator/scripts/__init__.py +0 -0
  459. package/dist/workspace-defaults/files/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  460. package/dist/workspace-defaults/files/skills/skill-creator/scripts/generate_report.py +326 -0
  461. package/dist/workspace-defaults/files/skills/skill-creator/scripts/improve_description.py +247 -0
  462. package/dist/workspace-defaults/files/skills/skill-creator/scripts/package_skill.py +136 -0
  463. package/dist/workspace-defaults/files/skills/skill-creator/scripts/quick_validate.py +103 -0
  464. package/dist/workspace-defaults/files/skills/skill-creator/scripts/run_eval.py +310 -0
  465. package/dist/workspace-defaults/files/skills/skill-creator/scripts/run_loop.py +328 -0
  466. package/dist/workspace-defaults/files/skills/skill-creator/scripts/utils.py +47 -0
  467. package/package.json +2 -1
  468. package/dist/terraform/modules/app/agentcore-flue/README.md +0 -58
  469. package/dist/terraform/modules/app/agentcore-flue/main.tf +0 -323
  470. package/dist/terraform/modules/app/agentcore-flue/outputs.tf +0 -23
  471. package/dist/terraform/modules/app/agentcore-flue/variables.tf +0 -97
  472. package/dist/terraform/modules/app/computer-runtime/README.md +0 -15
  473. package/dist/terraform/modules/app/computer-runtime/main.tf +0 -406
  474. package/dist/terraform/modules/app/computer-runtime/outputs.tf +0 -75
  475. package/dist/terraform/modules/app/computer-runtime/variables.tf +0 -66
@@ -9,6 +9,31 @@
9
9
  # source = "thinkwork-ai/thinkwork/aws//modules/foundation/vpc"
10
10
  ################################################################################
11
11
 
12
+ terraform {
13
+ required_providers {
14
+ aws = {
15
+ source = "hashicorp/aws"
16
+ version = "~> 5.0"
17
+
18
+ # The customer-domain ACM certificate must live in us-east-1
19
+ # (CloudFront requirement) regardless of the deployment region, so the
20
+ # module needs an explicitly aliased us-east-1 provider. Root modules
21
+ # must declare it and pass it through:
22
+ #
23
+ # provider "aws" {
24
+ # alias = "us_east_1"
25
+ # region = "us-east-1"
26
+ # }
27
+ #
28
+ # module "thinkwork" {
29
+ # providers = { aws.us_east_1 = aws.us_east_1 }
30
+ # ...
31
+ # }
32
+ configuration_aliases = [aws.us_east_1]
33
+ }
34
+ }
35
+ }
36
+
12
37
  locals {
13
38
  bucket_name = var.bucket_name != "" ? var.bucket_name : "thinkwork-${var.stage}-storage"
14
39
  backups_bucket_name = "thinkwork-${var.stage}-backups"
@@ -20,30 +45,139 @@ locals {
20
45
  : module.vpc.private_subnet_ids
21
46
  )
22
47
 
23
- # Hindsight is an optional add-on. Preferred toggle: var.enable_hindsight.
24
- # For one release we also honor the legacy var.memory_engine == "hindsight"
25
- # so existing tfvars keep working.
26
- hindsight_enabled = var.enable_hindsight || var.memory_engine == "hindsight"
48
+ # Hindsight is the canonical user/Space memory provider for full ThinkWork
49
+ # installs. AgentCore managed memory remains available as an explicit
50
+ # low-cost/development opt-out; Cognee stays optional Brain infrastructure.
51
+ # We still honor var.memory_engine == "hindsight" so existing tfvars keep
52
+ # working even when enable_hindsight was not set.
53
+ hindsight_enabled = var.enable_hindsight || var.memory_engine == "hindsight"
54
+ cognee_enabled = var.enable_cognee
55
+ twenty_provisioned = var.twenty_provisioned
56
+ twenty_runtime_enabled = var.twenty_provisioned && var.twenty_runtime_enabled
57
+ twenty_domain = var.twenty_domain != "" ? var.twenty_domain : (var.www_domain != "" ? "crm.${var.www_domain}" : "")
58
+ twenty_public_url = var.twenty_public_url != "" ? var.twenty_public_url : (local.twenty_domain != "" ? "https://${local.twenty_domain}" : "")
59
+ twenty_certificate_arn = var.twenty_certificate_arn != "" ? var.twenty_certificate_arn : var.www_certificate_arn
60
+ twenty_email_domain = var.twenty_email_domain != "" ? var.twenty_email_domain : var.ses_inbound_domain
61
+ twenty_email_from_address = (
62
+ var.twenty_email_from_address != ""
63
+ ? var.twenty_email_from_address
64
+ : local.twenty_email_domain != "" ? "noreply@${local.twenty_email_domain}" : ""
65
+ )
66
+ n8n_provisioned = var.n8n_provisioned
67
+ n8n_runtime_enabled = var.n8n_provisioned && var.n8n_runtime_enabled
68
+ n8n_domain = var.n8n_domain != "" ? var.n8n_domain : (var.www_domain != "" ? "n8n.${var.www_domain}" : "")
69
+ n8n_public_url = var.n8n_public_url != "" ? var.n8n_public_url : (local.n8n_domain != "" ? "https://${local.n8n_domain}" : "")
70
+ n8n_certificate_arn = var.n8n_certificate_arn != "" ? var.n8n_certificate_arn : var.www_certificate_arn
71
+ cognee_worker_subnet_ids = (
72
+ length(module.vpc.private_subnet_ids) > 0
73
+ ? module.vpc.private_subnet_ids
74
+ : module.vpc.public_subnet_ids
75
+ )
76
+ okf_wiki_subnet_ids = (
77
+ length(module.vpc.private_subnet_ids) > 0
78
+ ? module.vpc.private_subnet_ids
79
+ : module.vpc.public_subnet_ids
80
+ )
81
+ okf_wiki_route_table_ids = concat(
82
+ module.vpc.private_route_table_ids,
83
+ module.vpc.public_route_table_ids,
84
+ )
85
+ okf_wiki_private_endpoints_enabled = var.okf_wiki_efs_enabled && var.okf_wiki_create_vpc_endpoints
86
+ okf_wiki_interface_endpoint_services = toset(concat(
87
+ [
88
+ "bedrock",
89
+ "bedrock-agentcore",
90
+ "bedrock-agentcore-control",
91
+ "bedrock-agentcore.gateway",
92
+ "execute-api",
93
+ "lambda",
94
+ "logs",
95
+ "rds-data",
96
+ "secretsmanager",
97
+ "ssm",
98
+ "sts",
99
+ "xray",
100
+ ],
101
+ local.cognee_enabled ? [] : ["bedrock-runtime"],
102
+ ))
103
+ brain_private_interface_endpoint_services = toset([
104
+ "bedrock",
105
+ "bedrock-agentcore",
106
+ "bedrock-agentcore-control",
107
+ "bedrock-agentcore.gateway",
108
+ "execute-api",
109
+ "lambda",
110
+ "logs",
111
+ "rds-data",
112
+ "secretsmanager",
113
+ "ssm",
114
+ "sts",
115
+ "xray",
116
+ ])
27
117
 
28
118
  # Canonical long-term memory engine for this deployment. Exactly one engine
29
- # is active per deployment for recall/inspect/export. Auto-selects from
30
- # enable_hindsight when var.memory_engine is left empty so existing deploys
31
- # keep working without config changes. Legacy value "managed" maps to
32
- # "agentcore".
119
+ # is active per deployment for recall/inspect/export. Empty selects
120
+ # Hindsight for full installs; callers can explicitly choose AgentCore for
121
+ # low-cost/development deployments. Legacy value "managed" maps to
122
+ # "agentcore"; "cognee" remains a legacy diagnostic compatibility value, not
123
+ # the supported user/Space memory path.
33
124
  resolved_memory_engine = (
34
- var.memory_engine == "hindsight" || var.memory_engine == "agentcore"
125
+ var.memory_engine == "hindsight" || var.memory_engine == "agentcore" || var.memory_engine == "cognee"
35
126
  ? var.memory_engine
36
127
  : var.memory_engine == "managed"
37
128
  ? "agentcore"
38
129
  : local.hindsight_enabled ? "hindsight" : "agentcore"
39
130
  )
40
131
 
132
+ # Customer-domain namespace (<name>.thinkwork.ai). The zone is created as
133
+ # soon as customer_domain is set; the cert/aliases/callback entries wait
134
+ # behind the delegation gate. Once delegated, the customer domain becomes
135
+ # the canonical end-user app domain (CloudFront takes exactly one ACM
136
+ # cert, so the customer cert replaces the legacy app cert on the
137
+ # distribution; legacy web access during the dual window is via the
138
+ # CloudFront default domain, which always serves). The retirement gate
139
+ # only ever takes effect on a delegated customer domain — the guardrail
140
+ # below rejects any other combination at plan time.
141
+ customer_domain_web_enabled = var.customer_domain != "" && var.customer_domain_delegated
142
+ customer_domain_legacy_retired = (
143
+ local.customer_domain_web_enabled && var.customer_domain_legacy_retired
144
+ )
145
+
146
+ # KTD6 — SES allows ONE active receipt rule set per account/region, and the
147
+ # owner differs by account kind. Whenever the ses-email module is enabled
148
+ # (mirrors its own `enabled` local: legacy inbound domain OR tenant
149
+ # subdomains configured), it owns the active set and the customer-domain
150
+ # module must not activate its own. In customer accounts the ses-email
151
+ # module is never enabled, so the customer-domain module owns activation —
152
+ # still subject to ses_manage_active_rule_set for stages sharing an account.
153
+ ses_email_module_enabled = (
154
+ var.ses_inbound_domain != "" ||
155
+ (var.ses_parent_domain != "" && length(var.ses_tenant_slugs) > 0)
156
+ )
157
+ customer_domain_manage_active_rule_set = (
158
+ var.ses_manage_active_rule_set && !local.ses_email_module_enabled
159
+ )
160
+
41
161
  # The end-user app is now canonically hosted at app.<domain>. Keep
42
162
  # computer_domain as a compatibility fallback so older module callers and
43
- # stages can upgrade without a flag-day.
44
- end_user_app_domain = var.app_domain != "" ? var.app_domain : var.computer_domain
45
- end_user_app_certificate_arn = var.app_certificate_arn != "" ? var.app_certificate_arn : var.computer_certificate_arn
163
+ # stages can upgrade without a flag-day. A delegated customer domain takes
164
+ # precedence over both.
165
+ legacy_end_user_app_domain = var.app_domain != "" ? var.app_domain : var.computer_domain
166
+ legacy_end_user_app_certificate_arn = var.app_certificate_arn != "" ? var.app_certificate_arn : var.computer_certificate_arn
167
+ end_user_app_domain = (
168
+ local.customer_domain_web_enabled ? var.customer_domain : local.legacy_end_user_app_domain
169
+ )
170
+ end_user_app_certificate_arn = (
171
+ local.customer_domain_web_enabled
172
+ ? module.customer_domain.certificate_arn
173
+ : local.legacy_end_user_app_certificate_arn
174
+ )
175
+ # The compat redirect rides on the same distribution/cert as the app
176
+ # domain. Once the customer-domain cert takes over, computer_domain is no
177
+ # longer covered by the distribution's certificate, so the compat alias
178
+ # must drop off (CloudFront rejects aliases the cert doesn't cover).
46
179
  computer_compat_redirect_enabled = (
180
+ !local.customer_domain_web_enabled &&
47
181
  var.app_domain != "" &&
48
182
  var.computer_domain != "" &&
49
183
  var.computer_domain != local.end_user_app_domain
@@ -84,6 +218,13 @@ locals {
84
218
  ) : ""
85
219
  }
86
220
 
221
+ check "memory_engine_requires_enabled_backend" {
222
+ assert {
223
+ condition = var.memory_engine != "cognee" || var.enable_cognee
224
+ error_message = "memory_engine = 'cognee' requires enable_cognee = true."
225
+ }
226
+ }
227
+
87
228
  ################################################################################
88
229
  # Workspace Guard
89
230
  ################################################################################
@@ -93,6 +234,727 @@ module "workspace_guard" {
93
234
  stage = var.stage
94
235
  }
95
236
 
237
+ resource "terraform_data" "customer_domain_configuration_guardrails" {
238
+ count = (
239
+ var.customer_domain != "" ||
240
+ var.customer_domain_delegated ||
241
+ var.customer_domain_legacy_retired
242
+ ) ? 1 : 0
243
+
244
+ input = {
245
+ customer_domain = var.customer_domain
246
+ customer_domain_delegated = var.customer_domain_delegated
247
+ customer_domain_legacy_retired = var.customer_domain_legacy_retired
248
+ }
249
+
250
+ lifecycle {
251
+ precondition {
252
+ condition = !var.customer_domain_delegated || var.customer_domain != ""
253
+ error_message = "customer_domain_delegated requires customer_domain to be set."
254
+ }
255
+
256
+ precondition {
257
+ condition = !var.customer_domain_legacy_retired || (var.customer_domain != "" && var.customer_domain_delegated)
258
+ error_message = "customer_domain_legacy_retired requires customer_domain and customer_domain_delegated — the legacy app URLs can only be retired after the customer domain serves the app."
259
+ }
260
+ }
261
+ }
262
+
263
+ resource "terraform_data" "cognee_configuration_guardrails" {
264
+ count = var.enable_cognee ? 1 : 0
265
+
266
+ input = {
267
+ cognee_backend_mode = var.cognee_backend_mode
268
+ cognee_brain_storage_tier = var.cognee_brain_storage_tier
269
+ cognee_desired_count = var.cognee_desired_count
270
+ cognee_image_uri = var.cognee_image_uri
271
+ cognee_db_name = var.cognee_db_name
272
+ cognee_db_password_secret_arn = var.cognee_db_password_secret_arn
273
+ cognee_llm_provider = var.cognee_llm_provider
274
+ cognee_embedding_provider = var.cognee_embedding_provider
275
+ cognee_bedrock_model_resources = var.cognee_bedrock_model_resource_arns
276
+ public_subnet_count = length(module.vpc.public_subnet_ids)
277
+ }
278
+
279
+ lifecycle {
280
+ precondition {
281
+ condition = var.cognee_image_uri != ""
282
+ error_message = "enable_cognee requires cognee_image_uri pinned to an immutable digest."
283
+ }
284
+
285
+ precondition {
286
+ condition = var.cognee_db_password_secret_arn != ""
287
+ error_message = "enable_cognee requires cognee_db_password_secret_arn for a dedicated Cognee database user."
288
+ }
289
+
290
+ precondition {
291
+ condition = var.cognee_db_password_secret_arn != module.database.graphql_db_secret_arn
292
+ error_message = "enable_cognee requires a dedicated Cognee database secret, not the shared Thinkwork admin database secret."
293
+ }
294
+
295
+ precondition {
296
+ condition = var.cognee_db_name != var.database_name
297
+ error_message = "cognee_db_name must be distinct from the shared Thinkwork database name."
298
+ }
299
+
300
+ precondition {
301
+ condition = length(module.vpc.public_subnet_ids) > 0
302
+ error_message = "enable_cognee requires at least one public subnet for the phase-1 public-subnet task egress pattern."
303
+ }
304
+
305
+ precondition {
306
+ condition = var.cognee_backend_mode != "dogfood" || var.cognee_desired_count == 1
307
+ error_message = "cognee_backend_mode = dogfood requires cognee_desired_count = 1."
308
+ }
309
+
310
+ precondition {
311
+ condition = (
312
+ var.cognee_brain_storage_tier != "default" ||
313
+ (
314
+ var.cognee_backend_mode == "dogfood" &&
315
+ var.cognee_vector_db_provider == "lancedb" &&
316
+ var.cognee_graph_database_provider == "kuzu" &&
317
+ var.cognee_desired_count == 1
318
+ )
319
+ )
320
+ error_message = "cognee_brain_storage_tier = default requires dogfood backend mode, lancedb vectors, kuzu graph storage, and desired_count = 1."
321
+ }
322
+
323
+ precondition {
324
+ condition = (
325
+ var.cognee_brain_storage_tier != "production" ||
326
+ (
327
+ var.cognee_backend_mode == "remote" &&
328
+ var.cognee_vector_db_provider == "neptune_analytics" &&
329
+ var.cognee_graph_database_provider == "neptune_analytics" &&
330
+ var.cognee_neptune_graph_id != "" &&
331
+ var.cognee_neptune_endpoint != ""
332
+ )
333
+ )
334
+ error_message = "cognee_brain_storage_tier = production requires remote mode with Neptune Analytics graph/vector providers, cognee_neptune_graph_id, and cognee_neptune_endpoint."
335
+ }
336
+
337
+ precondition {
338
+ condition = !contains(["opensearch", "opensearch_serverless", "aoss"], lower(var.cognee_vector_db_provider))
339
+ error_message = "Company Brain production must not use direct OpenSearch vector storage; use Neptune Analytics for production graph/vector."
340
+ }
341
+
342
+ precondition {
343
+ condition = (
344
+ var.cognee_backend_mode != "remote" ||
345
+ (
346
+ (
347
+ var.cognee_vector_db_url != "" ||
348
+ (var.cognee_vector_db_provider == "neptune_analytics" && var.cognee_neptune_endpoint != "")
349
+ ) &&
350
+ (
351
+ var.cognee_graph_database_url != "" ||
352
+ (var.cognee_graph_database_provider == "neptune_analytics" && var.cognee_neptune_endpoint != "")
353
+ )
354
+ )
355
+ )
356
+ error_message = "cognee_backend_mode = remote requires vector/graph URLs, or Neptune Analytics providers with cognee_neptune_endpoint."
357
+ }
358
+
359
+ precondition {
360
+ condition = (
361
+ (var.cognee_llm_provider == "bedrock" || var.cognee_llm_api_key_secret_arn != "") &&
362
+ (var.cognee_embedding_provider == "bedrock" || var.cognee_embedding_api_key_secret_arn != "")
363
+ )
364
+ error_message = "Non-Bedrock Cognee LLM or embedding providers require matching secret ARN inputs."
365
+ }
366
+
367
+ precondition {
368
+ condition = (
369
+ (var.cognee_llm_provider != "bedrock" && var.cognee_embedding_provider != "bedrock") ||
370
+ length(var.cognee_bedrock_model_resource_arns) > 0
371
+ )
372
+ error_message = "Bedrock Cognee providers require explicit cognee_bedrock_model_resource_arns."
373
+ }
374
+ }
375
+ }
376
+
377
+ resource "terraform_data" "okf_wiki_efs_guardrails" {
378
+ count = var.okf_wiki_efs_enabled ? 1 : 0
379
+
380
+ input = {
381
+ subnet_count = length(local.okf_wiki_subnet_ids)
382
+ route_table_count = length(local.okf_wiki_route_table_ids)
383
+ create_endpoints = var.okf_wiki_create_vpc_endpoints
384
+ }
385
+
386
+ lifecycle {
387
+ precondition {
388
+ condition = length(local.okf_wiki_subnet_ids) > 0
389
+ error_message = "okf_wiki_efs_enabled requires at least one VPC subnet."
390
+ }
391
+
392
+ precondition {
393
+ condition = !var.okf_wiki_create_vpc_endpoints || length(local.okf_wiki_route_table_ids) > 0
394
+ error_message = "okf_wiki_create_vpc_endpoints requires route table IDs so the S3 gateway endpoint can be attached. For BYO VPC, set existing_public_route_table_ids and/or existing_private_route_table_ids."
395
+ }
396
+ }
397
+ }
398
+
399
+ resource "terraform_data" "twenty_configuration_guardrails" {
400
+ count = local.twenty_provisioned ? 1 : 0
401
+
402
+ input = {
403
+ twenty_runtime_enabled = local.twenty_runtime_enabled
404
+ twenty_image_uri = var.twenty_image_uri
405
+ twenty_db_name = var.twenty_db_name
406
+ twenty_db_username = var.twenty_db_username
407
+ twenty_db_url_secret_arn = var.twenty_db_url_secret_arn
408
+ twenty_encryption_key_secret_arn = var.twenty_encryption_key_secret_arn
409
+ twenty_public_url = local.twenty_public_url
410
+ twenty_certificate_arn = local.twenty_certificate_arn
411
+ public_subnet_count = length(module.vpc.public_subnet_ids)
412
+ cache_subnet_count = length(module.vpc.private_subnet_ids)
413
+ }
414
+
415
+ lifecycle {
416
+ precondition {
417
+ condition = var.twenty_image_uri != ""
418
+ error_message = "twenty_provisioned requires twenty_image_uri pinned to an immutable digest."
419
+ }
420
+
421
+ precondition {
422
+ condition = var.twenty_db_url_secret_arn != "" || var.deployment_control_plane_create_secret_placeholders
423
+ error_message = "twenty_provisioned requires twenty_db_url_secret_arn or deployment_control_plane_create_secret_placeholders = true."
424
+ }
425
+
426
+ precondition {
427
+ condition = var.twenty_encryption_key_secret_arn != "" || var.deployment_control_plane_create_secret_placeholders
428
+ error_message = "twenty_provisioned requires twenty_encryption_key_secret_arn or deployment_control_plane_create_secret_placeholders = true."
429
+ }
430
+
431
+ precondition {
432
+ condition = var.twenty_db_url_secret_arn != module.database.graphql_db_secret_arn
433
+ error_message = "twenty_provisioned requires a dedicated Twenty database URL secret, not the shared Thinkwork admin database secret."
434
+ }
435
+
436
+ precondition {
437
+ condition = var.twenty_db_name != var.database_name
438
+ error_message = "twenty_db_name must be distinct from the shared Thinkwork database name."
439
+ }
440
+
441
+ precondition {
442
+ condition = local.twenty_public_url != ""
443
+ error_message = "twenty_provisioned requires twenty_public_url or a www_domain-derived crm.<domain> URL."
444
+ }
445
+
446
+ precondition {
447
+ condition = local.twenty_certificate_arn != ""
448
+ error_message = "twenty_provisioned requires twenty_certificate_arn or www_certificate_arn."
449
+ }
450
+
451
+ precondition {
452
+ condition = length(module.vpc.public_subnet_ids) > 0
453
+ error_message = "twenty_provisioned requires at least one public subnet for the public ALB and phase-1 task egress pattern."
454
+ }
455
+
456
+ precondition {
457
+ condition = length(module.vpc.private_subnet_ids) > 0
458
+ error_message = "twenty_provisioned requires at least one private subnet for ElastiCache."
459
+ }
460
+
461
+ precondition {
462
+ condition = !var.twenty_runtime_enabled || var.twenty_provisioned
463
+ error_message = "twenty_runtime_enabled requires twenty_provisioned = true."
464
+ }
465
+ }
466
+ }
467
+
468
+ resource "terraform_data" "twenty_runtime_state_guardrails" {
469
+ count = var.twenty_runtime_enabled && !var.twenty_provisioned ? 1 : 0
470
+
471
+ input = {
472
+ twenty_provisioned = var.twenty_provisioned
473
+ twenty_runtime_enabled = var.twenty_runtime_enabled
474
+ }
475
+
476
+ lifecycle {
477
+ precondition {
478
+ condition = !var.twenty_runtime_enabled || var.twenty_provisioned
479
+ error_message = "twenty_runtime_enabled requires twenty_provisioned = true."
480
+ }
481
+ }
482
+ }
483
+
484
+ resource "terraform_data" "n8n_configuration_guardrails" {
485
+ count = local.n8n_provisioned ? 1 : 0
486
+
487
+ input = {
488
+ n8n_runtime_enabled = local.n8n_runtime_enabled
489
+ n8n_image_uri = var.n8n_image_uri
490
+ n8n_database_name = var.n8n_database_name
491
+ n8n_database_username = var.n8n_database_username
492
+ n8n_database_admin_secret_arn = var.n8n_database_admin_secret_arn
493
+ n8n_database_url_secret_arn = var.n8n_database_url_secret_arn
494
+ n8n_encryption_key_secret_arn = var.n8n_encryption_key_secret_arn
495
+ n8n_operator_secret_arn = var.n8n_operator_secret_arn
496
+ n8n_service_credential_secret_arn = var.n8n_service_credential_secret_arn
497
+ n8n_agent_step_bridge_credential_secret_arn = var.n8n_agent_step_bridge_credential_secret_arn
498
+ n8n_storage_bucket_name = var.n8n_storage_bucket_name
499
+ n8n_create_storage_bucket = var.n8n_create_storage_bucket
500
+ n8n_storage_prefix = var.n8n_storage_prefix
501
+ n8n_public_url = local.n8n_public_url
502
+ n8n_certificate_arn = local.n8n_certificate_arn
503
+ n8n_main_desired_count = var.n8n_main_desired_count
504
+ n8n_worker_desired_count = var.n8n_worker_desired_count
505
+ n8n_worker_concurrency = var.n8n_worker_concurrency
506
+ n8n_container_port = var.n8n_container_port
507
+ n8n_queue_mode = var.n8n_queue_mode
508
+ n8n_task_runners_enabled = var.n8n_task_runners_enabled
509
+ n8n_package_config_digest = var.n8n_package_config_digest
510
+ n8n_custom_package_specs = var.n8n_custom_package_specs
511
+ n8n_execution_data_storage_mode = var.n8n_execution_data_storage_mode
512
+ n8n_binary_data_mode = var.n8n_binary_data_mode
513
+ n8n_cache_engine = var.n8n_cache_engine
514
+ n8n_cache_engine_version = var.n8n_cache_engine_version
515
+ n8n_cache_parameter_group_family = var.n8n_cache_parameter_group_family
516
+ n8n_cache_node_type = var.n8n_cache_node_type
517
+ n8n_cache_num_cache_clusters = var.n8n_cache_num_cache_clusters
518
+ n8n_allowed_public_cidr_blocks = var.n8n_allowed_public_cidr_blocks
519
+ n8n_kms_key_arns = var.n8n_kms_key_arns
520
+ public_subnet_count = length(module.vpc.public_subnet_ids)
521
+ private_subnet_count = length(module.vpc.private_subnet_ids)
522
+ }
523
+
524
+ lifecycle {
525
+ precondition {
526
+ condition = var.n8n_image_uri != ""
527
+ error_message = "n8n_provisioned requires n8n_image_uri pinned to an immutable digest."
528
+ }
529
+
530
+ precondition {
531
+ condition = var.n8n_queue_mode
532
+ error_message = "n8n_provisioned requires n8n_queue_mode = true."
533
+ }
534
+
535
+ precondition {
536
+ condition = var.n8n_database_admin_secret_arn != ""
537
+ error_message = "n8n_provisioned requires n8n_database_admin_secret_arn for dedicated database lifecycle setup."
538
+ }
539
+
540
+ precondition {
541
+ condition = var.n8n_database_url_secret_arn != "" || var.deployment_control_plane_create_secret_placeholders
542
+ error_message = "n8n_provisioned requires n8n_database_url_secret_arn or deployment_control_plane_create_secret_placeholders = true."
543
+ }
544
+
545
+ precondition {
546
+ condition = var.n8n_encryption_key_secret_arn != "" || var.deployment_control_plane_create_secret_placeholders
547
+ error_message = "n8n_provisioned requires n8n_encryption_key_secret_arn or deployment_control_plane_create_secret_placeholders = true."
548
+ }
549
+
550
+ precondition {
551
+ condition = var.n8n_operator_secret_arn != "" || var.deployment_control_plane_create_secret_placeholders
552
+ error_message = "n8n_provisioned requires n8n_operator_secret_arn or deployment_control_plane_create_secret_placeholders = true."
553
+ }
554
+
555
+ precondition {
556
+ condition = var.n8n_service_credential_secret_arn != "" || var.deployment_control_plane_create_secret_placeholders
557
+ error_message = "n8n_provisioned requires n8n_service_credential_secret_arn or deployment_control_plane_create_secret_placeholders = true."
558
+ }
559
+
560
+ precondition {
561
+ condition = var.n8n_agent_step_bridge_credential_secret_arn != "" || var.deployment_control_plane_create_secret_placeholders
562
+ error_message = "n8n_provisioned requires n8n_agent_step_bridge_credential_secret_arn or deployment_control_plane_create_secret_placeholders = true."
563
+ }
564
+
565
+ precondition {
566
+ condition = var.n8n_database_url_secret_arn == "" || var.n8n_database_url_secret_arn != module.database.graphql_db_secret_arn
567
+ error_message = "n8n_provisioned requires a dedicated n8n database URL secret, not the shared Thinkwork admin database secret."
568
+ }
569
+
570
+ precondition {
571
+ condition = var.n8n_database_name != var.database_name
572
+ error_message = "n8n_database_name must be distinct from the shared Thinkwork database name."
573
+ }
574
+
575
+ precondition {
576
+ condition = var.n8n_storage_bucket_name != ""
577
+ error_message = "n8n_provisioned requires n8n_storage_bucket_name."
578
+ }
579
+
580
+ precondition {
581
+ condition = trim(var.n8n_storage_prefix, "/") != ""
582
+ error_message = "n8n_provisioned requires a non-empty n8n_storage_prefix."
583
+ }
584
+
585
+ precondition {
586
+ condition = local.n8n_public_url != ""
587
+ error_message = "n8n_provisioned requires n8n_public_url or a www_domain-derived n8n.<domain> URL."
588
+ }
589
+
590
+ precondition {
591
+ condition = local.n8n_certificate_arn != ""
592
+ error_message = "n8n_provisioned requires n8n_certificate_arn or www_certificate_arn."
593
+ }
594
+
595
+ precondition {
596
+ condition = length(module.vpc.public_subnet_ids) > 0
597
+ error_message = "n8n_provisioned requires at least one public subnet for the public ALB and phase-1 task egress pattern."
598
+ }
599
+
600
+ precondition {
601
+ condition = length(module.vpc.private_subnet_ids) > 0
602
+ error_message = "n8n_provisioned requires at least one private subnet for managed Valkey/Redis."
603
+ }
604
+
605
+ precondition {
606
+ condition = !var.n8n_runtime_enabled || var.n8n_provisioned
607
+ error_message = "n8n_runtime_enabled requires n8n_provisioned = true."
608
+ }
609
+ }
610
+ }
611
+
612
+ resource "terraform_data" "n8n_runtime_state_guardrails" {
613
+ count = var.n8n_runtime_enabled && !var.n8n_provisioned ? 1 : 0
614
+
615
+ input = {
616
+ n8n_provisioned = var.n8n_provisioned
617
+ n8n_runtime_enabled = var.n8n_runtime_enabled
618
+ }
619
+
620
+ lifecycle {
621
+ precondition {
622
+ condition = !var.n8n_runtime_enabled || var.n8n_provisioned
623
+ error_message = "n8n_runtime_enabled requires n8n_provisioned = true."
624
+ }
625
+ }
626
+ }
627
+
628
+ resource "aws_security_group" "cognee_worker" {
629
+ count = local.cognee_enabled ? 1 : 0
630
+
631
+ name_prefix = "thinkwork-${var.stage}-cognee-worker-"
632
+ description = "Knowledge Graph ingest Lambda access to Cognee and Aurora"
633
+ vpc_id = module.vpc.vpc_id
634
+
635
+ egress {
636
+ from_port = 0
637
+ to_port = 0
638
+ protocol = "-1"
639
+ cidr_blocks = ["0.0.0.0/0"]
640
+ }
641
+
642
+ tags = { Name = "thinkwork-${var.stage}-cognee-worker-sg" }
643
+ lifecycle { create_before_destroy = true }
644
+ }
645
+
646
+ resource "aws_security_group_rule" "aurora_from_cognee_worker" {
647
+ count = local.cognee_enabled ? 1 : 0
648
+
649
+ type = "ingress"
650
+ from_port = 5432
651
+ to_port = 5432
652
+ protocol = "tcp"
653
+ source_security_group_id = aws_security_group.cognee_worker[0].id
654
+ security_group_id = module.database.db_security_group_id
655
+ }
656
+
657
+ # VPC-attached Knowledge Graph workers (attached for Cognee's internal ALB)
658
+ # have no public egress — the VPC has no NAT — so the observation promotion
659
+ # classifier's direct Bedrock calls need an interface endpoint inside the VPC.
660
+ # Private DNS keeps the SDK's default bedrock-runtime hostname resolving to
661
+ # the endpoint ENIs — for EVERY resource in the VPC, so the ingress must
662
+ # admit the whole VPC CIDR: the Cognee ECS task (its own SG) and any future
663
+ # in-VPC Bedrock caller resolve to this endpoint the moment it exists.
664
+ data "aws_vpc" "bedrock_endpoint_scope" {
665
+ count = local.cognee_enabled ? 1 : 0
666
+ id = module.vpc.vpc_id
667
+ }
668
+
669
+ resource "aws_security_group" "bedrock_runtime_endpoint" {
670
+ count = local.cognee_enabled ? 1 : 0
671
+
672
+ name_prefix = "thinkwork-${var.stage}-bedrock-vpce-"
673
+ description = "HTTPS to the Bedrock runtime interface endpoint"
674
+ vpc_id = module.vpc.vpc_id
675
+
676
+ ingress {
677
+ from_port = 443
678
+ to_port = 443
679
+ protocol = "tcp"
680
+ cidr_blocks = [data.aws_vpc.bedrock_endpoint_scope[0].cidr_block]
681
+ }
682
+
683
+ tags = { Name = "thinkwork-${var.stage}-bedrock-vpce-sg" }
684
+ lifecycle { create_before_destroy = true }
685
+ }
686
+
687
+ resource "aws_vpc_endpoint" "bedrock_runtime" {
688
+ count = local.cognee_enabled ? 1 : 0
689
+
690
+ vpc_id = module.vpc.vpc_id
691
+ service_name = "com.amazonaws.${var.region}.bedrock-runtime"
692
+ vpc_endpoint_type = "Interface"
693
+ subnet_ids = local.cognee_worker_subnet_ids
694
+ security_group_ids = [aws_security_group.bedrock_runtime_endpoint[0].id]
695
+ private_dns_enabled = true
696
+
697
+ tags = { Name = "thinkwork-${var.stage}-bedrock-runtime-vpce" }
698
+ }
699
+
700
+ resource "aws_security_group" "okf_wiki_lambda" {
701
+ count = var.okf_wiki_efs_enabled ? 1 : 0
702
+
703
+ name_prefix = "thinkwork-${var.stage}-okf-wiki-lambda-"
704
+ description = "OKF wiki EFS clients for hydrator and Pi"
705
+ vpc_id = module.vpc.vpc_id
706
+
707
+ egress {
708
+ from_port = 0
709
+ to_port = 0
710
+ protocol = "-1"
711
+ cidr_blocks = ["0.0.0.0/0"]
712
+ }
713
+
714
+ tags = { Name = "thinkwork-${var.stage}-okf-wiki-lambda-sg" }
715
+ lifecycle { create_before_destroy = true }
716
+ }
717
+
718
+ resource "aws_security_group" "okf_wiki_efs" {
719
+ count = var.okf_wiki_efs_enabled ? 1 : 0
720
+
721
+ name_prefix = "thinkwork-${var.stage}-okf-wiki-efs-"
722
+ description = "NFS ingress for OKF wiki current-view EFS"
723
+ vpc_id = module.vpc.vpc_id
724
+
725
+ tags = { Name = "thinkwork-${var.stage}-okf-wiki-efs-sg" }
726
+ lifecycle { create_before_destroy = true }
727
+ }
728
+
729
+ resource "aws_security_group" "okf_wiki_vpc_endpoint" {
730
+ count = var.okf_wiki_efs_enabled && var.okf_wiki_create_vpc_endpoints ? 1 : 0
731
+
732
+ name_prefix = "thinkwork-${var.stage}-okf-wiki-vpce-"
733
+ description = "HTTPS endpoints for OKF wiki VPC-attached Lambdas"
734
+ vpc_id = module.vpc.vpc_id
735
+
736
+ tags = { Name = "thinkwork-${var.stage}-okf-wiki-vpce-sg" }
737
+ lifecycle { create_before_destroy = true }
738
+ }
739
+
740
+ resource "aws_security_group_rule" "okf_wiki_vpce_from_lambda" {
741
+ count = local.okf_wiki_private_endpoints_enabled ? 1 : 0
742
+
743
+ type = "ingress"
744
+ from_port = 443
745
+ to_port = 443
746
+ protocol = "tcp"
747
+ source_security_group_id = aws_security_group.okf_wiki_lambda[0].id
748
+ security_group_id = aws_security_group.okf_wiki_vpc_endpoint[0].id
749
+ }
750
+
751
+ resource "aws_security_group_rule" "okf_wiki_vpce_from_cognee_worker" {
752
+ count = local.okf_wiki_private_endpoints_enabled && local.cognee_enabled ? 1 : 0
753
+
754
+ type = "ingress"
755
+ from_port = 443
756
+ to_port = 443
757
+ protocol = "tcp"
758
+ source_security_group_id = aws_security_group.cognee_worker[0].id
759
+ security_group_id = aws_security_group.okf_wiki_vpc_endpoint[0].id
760
+ }
761
+
762
+ # Interface endpoints with private DNS capture the WHOLE VPC's traffic to the
763
+ # covered AWS APIs (logs, ssm, sts, bedrock, ...): every workload in the VPC
764
+ # resolves those hostnames to the endpoint's private IP, so per-source-SG
765
+ # rules silently black-hole anything not enumerated — harness cycle-7's
766
+ # Hindsight tasks died on "cannot find the CloudWatch log group ... connection
767
+ # issue" because only the Lambda SG was admitted. Allow the VPC CIDR.
768
+ resource "aws_security_group_rule" "okf_wiki_vpce_from_vpc" {
769
+ count = local.okf_wiki_private_endpoints_enabled ? 1 : 0
770
+
771
+ type = "ingress"
772
+ from_port = 443
773
+ to_port = 443
774
+ protocol = "tcp"
775
+ cidr_blocks = [module.vpc.vpc_cidr_block]
776
+ security_group_id = aws_security_group.okf_wiki_vpc_endpoint[0].id
777
+ description = "All VPC workloads (private-DNS endpoints capture the whole VPC)"
778
+ }
779
+
780
+ resource "aws_security_group_rule" "okf_wiki_vpce_from_twenty" {
781
+ count = local.okf_wiki_private_endpoints_enabled && local.twenty_provisioned ? 1 : 0
782
+
783
+ type = "ingress"
784
+ from_port = 443
785
+ to_port = 443
786
+ protocol = "tcp"
787
+ source_security_group_id = module.twenty[0].twenty_security_group_id
788
+ security_group_id = aws_security_group.okf_wiki_vpc_endpoint[0].id
789
+ }
790
+
791
+ resource "aws_vpc_endpoint" "okf_wiki_interface" {
792
+ for_each = local.okf_wiki_private_endpoints_enabled ? local.okf_wiki_interface_endpoint_services : toset([])
793
+
794
+ vpc_id = module.vpc.vpc_id
795
+ service_name = "com.amazonaws.${var.region}.${each.key}"
796
+ vpc_endpoint_type = "Interface"
797
+ subnet_ids = local.okf_wiki_subnet_ids
798
+ security_group_ids = [aws_security_group.okf_wiki_vpc_endpoint[0].id]
799
+ private_dns_enabled = true
800
+
801
+ tags = {
802
+ Name = "thinkwork-${var.stage}-okf-wiki-${each.key}-vpce"
803
+ Purpose = "okf-wiki-private-egress"
804
+ }
805
+ }
806
+
807
+ resource "aws_vpc_endpoint" "okf_wiki_s3" {
808
+ count = local.okf_wiki_private_endpoints_enabled && length(local.okf_wiki_route_table_ids) > 0 ? 1 : 0
809
+
810
+ vpc_id = module.vpc.vpc_id
811
+ service_name = "com.amazonaws.${var.region}.s3"
812
+ vpc_endpoint_type = "Gateway"
813
+ route_table_ids = local.okf_wiki_route_table_ids
814
+
815
+ tags = {
816
+ Name = "thinkwork-${var.stage}-okf-wiki-s3-vpce"
817
+ Purpose = "okf-wiki-private-egress"
818
+ }
819
+ }
820
+
821
+ resource "aws_security_group" "brain_private_vpc_endpoint" {
822
+ count = local.cognee_enabled && !local.okf_wiki_private_endpoints_enabled ? 1 : 0
823
+
824
+ name_prefix = "thinkwork-${var.stage}-brain-vpce-"
825
+ description = "HTTPS endpoints for private Company Brain/Pi runtime egress"
826
+ vpc_id = module.vpc.vpc_id
827
+
828
+ tags = { Name = "thinkwork-${var.stage}-brain-vpce-sg" }
829
+ lifecycle { create_before_destroy = true }
830
+ }
831
+
832
+ resource "aws_security_group_rule" "brain_vpce_from_cognee_worker" {
833
+ count = local.cognee_enabled && !local.okf_wiki_private_endpoints_enabled ? 1 : 0
834
+
835
+ type = "ingress"
836
+ from_port = 443
837
+ to_port = 443
838
+ protocol = "tcp"
839
+ source_security_group_id = aws_security_group.cognee_worker[0].id
840
+ security_group_id = aws_security_group.brain_private_vpc_endpoint[0].id
841
+ }
842
+
843
+ resource "aws_vpc_endpoint" "brain_private_interface" {
844
+ for_each = local.cognee_enabled && !local.okf_wiki_private_endpoints_enabled ? local.brain_private_interface_endpoint_services : toset([])
845
+
846
+ vpc_id = module.vpc.vpc_id
847
+ service_name = "com.amazonaws.${var.region}.${each.key}"
848
+ vpc_endpoint_type = "Interface"
849
+ subnet_ids = local.cognee_worker_subnet_ids
850
+ security_group_ids = [aws_security_group.brain_private_vpc_endpoint[0].id]
851
+ private_dns_enabled = true
852
+
853
+ tags = {
854
+ Name = "thinkwork-${var.stage}-brain-${each.key}-vpce"
855
+ Purpose = "company-brain-private-runtime-egress"
856
+ }
857
+ }
858
+
859
+ resource "aws_vpc_endpoint" "brain_private_s3" {
860
+ count = local.cognee_enabled && !local.okf_wiki_private_endpoints_enabled && length(local.okf_wiki_route_table_ids) > 0 ? 1 : 0
861
+
862
+ vpc_id = module.vpc.vpc_id
863
+ service_name = "com.amazonaws.${var.region}.s3"
864
+ vpc_endpoint_type = "Gateway"
865
+ route_table_ids = local.okf_wiki_route_table_ids
866
+
867
+ tags = {
868
+ Name = "thinkwork-${var.stage}-brain-s3-vpce"
869
+ Purpose = "company-brain-private-runtime-egress"
870
+ }
871
+ }
872
+
873
+ resource "aws_security_group_rule" "okf_wiki_efs_from_lambda" {
874
+ count = var.okf_wiki_efs_enabled ? 1 : 0
875
+
876
+ type = "ingress"
877
+ from_port = 2049
878
+ to_port = 2049
879
+ protocol = "tcp"
880
+ source_security_group_id = aws_security_group.okf_wiki_lambda[0].id
881
+ security_group_id = aws_security_group.okf_wiki_efs[0].id
882
+ }
883
+
884
+ resource "aws_efs_file_system" "okf_wiki" {
885
+ count = var.okf_wiki_efs_enabled ? 1 : 0
886
+
887
+ creation_token = "thinkwork-${var.stage}-okf-wiki"
888
+ encrypted = true
889
+
890
+ lifecycle_policy {
891
+ transition_to_ia = "AFTER_30_DAYS"
892
+ }
893
+
894
+ tags = {
895
+ Name = "thinkwork-${var.stage}-okf-wiki"
896
+ Purpose = "okf-wiki-current-view"
897
+ }
898
+ }
899
+
900
+ resource "aws_efs_mount_target" "okf_wiki" {
901
+ count = var.okf_wiki_efs_enabled ? length(local.okf_wiki_subnet_ids) : 0
902
+
903
+ file_system_id = aws_efs_file_system.okf_wiki[0].id
904
+ subnet_id = local.okf_wiki_subnet_ids[count.index]
905
+ security_groups = [aws_security_group.okf_wiki_efs[0].id]
906
+ }
907
+
908
+ resource "aws_efs_access_point" "okf_wiki_refresh" {
909
+ count = var.okf_wiki_efs_enabled ? 1 : 0
910
+
911
+ file_system_id = aws_efs_file_system.okf_wiki[0].id
912
+
913
+ posix_user {
914
+ uid = 1000
915
+ gid = 1000
916
+ }
917
+
918
+ root_directory {
919
+ path = "/okf"
920
+ creation_info {
921
+ owner_uid = 1000
922
+ owner_gid = 1000
923
+ permissions = "0755"
924
+ }
925
+ }
926
+
927
+ tags = {
928
+ Name = "thinkwork-${var.stage}-okf-wiki-refresh-ap"
929
+ Purpose = "okf-wiki-hydrator-write"
930
+ }
931
+ }
932
+
933
+ resource "aws_efs_access_point" "okf_wiki_pi_read" {
934
+ count = var.okf_wiki_efs_enabled ? 1 : 0
935
+
936
+ file_system_id = aws_efs_file_system.okf_wiki[0].id
937
+
938
+ posix_user {
939
+ uid = 2000
940
+ gid = 2000
941
+ }
942
+
943
+ root_directory {
944
+ path = "/okf"
945
+ creation_info {
946
+ owner_uid = 1000
947
+ owner_gid = 1000
948
+ permissions = "0755"
949
+ }
950
+ }
951
+
952
+ tags = {
953
+ Name = "thinkwork-${var.stage}-okf-wiki-pi-read-ap"
954
+ Purpose = "okf-wiki-pi-read"
955
+ }
956
+ }
957
+
96
958
  ################################################################################
97
959
  # Foundation Tier
98
960
  ################################################################################
@@ -100,11 +962,14 @@ module "workspace_guard" {
100
962
  module "vpc" {
101
963
  source = "../foundation/vpc"
102
964
 
103
- stage = var.stage
104
- create_vpc = var.create_vpc
105
- existing_vpc_id = var.existing_vpc_id
106
- existing_public_subnet_ids = var.existing_public_subnet_ids
107
- existing_private_subnet_ids = var.existing_private_subnet_ids
965
+ stage = var.stage
966
+ create_vpc = var.create_vpc
967
+ existing_vpc_id = var.existing_vpc_id
968
+ existing_public_subnet_ids = var.existing_public_subnet_ids
969
+ existing_private_subnet_ids = var.existing_private_subnet_ids
970
+ existing_public_route_table_ids = var.existing_public_route_table_ids
971
+ existing_private_route_table_ids = var.existing_private_route_table_ids
972
+ enable_nat_gateway = var.okf_wiki_efs_enabled && var.okf_wiki_create_nat_gateway
108
973
  }
109
974
 
110
975
  module "kms" {
@@ -127,35 +992,60 @@ module "cognito" {
127
992
 
128
993
  google_oauth_client_id = var.google_oauth_client_id
129
994
  google_oauth_client_secret = var.google_oauth_client_secret
995
+ oidc_identity_providers = var.oidc_identity_providers
996
+ saml_identity_providers = var.saml_identity_providers
130
997
  pre_signup_lambda_zip = var.pre_signup_lambda_zip
131
-
132
- # Single ThinkworkAdmin Cognito client serves both admin and end-user SPAs.
133
- # apps/computer reuses this client by design — same humans, same tenant
134
- # invitations, single sign-in across both surfaces. Each origin (admin
135
- # distribution, admin custom domain, app distribution, app custom domain,
136
- # compatibility computer domain, plus localhost dev for both) needs both
137
- # bare and /auth/callback entries because the OAuth flow lands on
138
- # /auth/callback and the SPA's post-OAuth redirect lands on the bare origin.
998
+ custom_auth_lambda_zip = var.cognito_custom_auth_lambda_zip
999
+ api_auth_secret = var.api_auth_secret
1000
+ email_source_arn = var.cognito_email_source_arn
1001
+ from_email_address = var.cognito_from_email_address
1002
+ reply_to_email_address = var.cognito_reply_to_email_address
1003
+ invite_email_subject = var.cognito_invite_email_subject
1004
+ invite_email_message = (
1005
+ var.cognito_invite_email_message != ""
1006
+ ? var.cognito_invite_email_message
1007
+ : format(
1008
+ "<p>You have been invited to ThinkWork.</p><p>Sign in: <a href=\"%s/sign-in\">%s/sign-in</a></p><p>Username: <strong>{username}</strong></p><p>Temporary password: <strong>{####}</strong></p>",
1009
+ local.end_user_app_domain != "" ? "https://${local.end_user_app_domain}" : "https://${module.computer_site.distribution_domain}",
1010
+ local.end_user_app_domain != "" ? "https://${local.end_user_app_domain}" : "https://${module.computer_site.distribution_domain}",
1011
+ )
1012
+ )
1013
+ invite_sms_message = var.cognito_invite_sms_message
1014
+
1015
+ # Single ThinkworkAdmin Cognito client serves the unified web app. The
1016
+ # historical client name stays for compatibility; the standalone admin
1017
+ # SPA/static site is retired. Each origin needs both bare and /auth/callback
1018
+ # entries because OAuth lands on /auth/callback and the SPA's post-OAuth
1019
+ # redirect lands on the bare origin.
1020
+ #
1021
+ # Customer-domain cutover (dual window): the customer-domain entries are
1022
+ # ADDED once customer_domain_delegated is true, alongside every legacy
1023
+ # entry — login works on both domains. Flipping
1024
+ # customer_domain_legacy_retired REMOVES the legacy (non-customer-domain)
1025
+ # app entries — the CloudFront default domain, the legacy app/computer
1026
+ # domains — as a reviewable Terraform change. Explicit caller-supplied,
1027
+ # localhost dev, and desktop entries are never retired here.
139
1028
  admin_callback_urls = distinct(concat(
140
1029
  var.admin_callback_urls,
141
- ["https://${module.admin_site.distribution_domain}", "https://${module.admin_site.distribution_domain}/auth/callback"],
142
- var.admin_domain != "" ? ["https://${var.admin_domain}", "https://${var.admin_domain}/auth/callback"] : [],
143
- ["https://${module.computer_site.distribution_domain}", "https://${module.computer_site.distribution_domain}/auth/callback"],
144
- local.end_user_app_domain != "" ? ["https://${local.end_user_app_domain}", "https://${local.end_user_app_domain}/auth/callback"] : [],
145
- var.computer_domain != "" ? ["https://${var.computer_domain}", "https://${var.computer_domain}/auth/callback"] : [],
146
- ["http://localhost:5180", "http://localhost:5180/auth/callback"]
1030
+ local.customer_domain_legacy_retired ? [] : ["https://${module.computer_site.distribution_domain}", "https://${module.computer_site.distribution_domain}/auth/callback"],
1031
+ local.customer_domain_legacy_retired ? [] : (local.legacy_end_user_app_domain != "" ? ["https://${local.legacy_end_user_app_domain}", "https://${local.legacy_end_user_app_domain}/auth/callback"] : []),
1032
+ local.customer_domain_web_enabled ? ["https://${var.customer_domain}", "https://${var.customer_domain}/auth/callback"] : [],
1033
+ local.customer_domain_legacy_retired ? [] : (var.computer_domain != "" ? ["https://${var.computer_domain}", "https://${var.computer_domain}/auth/callback"] : []),
1034
+ ["http://localhost:5180", "http://localhost:5180/auth/callback"],
1035
+ var.desktop_callback_urls
147
1036
  ))
148
1037
  admin_logout_urls = distinct(concat(
149
1038
  var.admin_logout_urls,
150
- ["https://${module.admin_site.distribution_domain}"],
151
- var.admin_domain != "" ? ["https://${var.admin_domain}"] : [],
152
- ["https://${module.computer_site.distribution_domain}"],
153
- local.end_user_app_domain != "" ? ["https://${local.end_user_app_domain}"] : [],
154
- var.computer_domain != "" ? ["https://${var.computer_domain}"] : [],
155
- ["http://localhost:5180"]
1039
+ local.customer_domain_legacy_retired ? [] : ["https://${module.computer_site.distribution_domain}"],
1040
+ local.customer_domain_legacy_retired ? [] : (local.legacy_end_user_app_domain != "" ? ["https://${local.legacy_end_user_app_domain}"] : []),
1041
+ local.customer_domain_web_enabled ? ["https://${var.customer_domain}"] : [],
1042
+ local.customer_domain_legacy_retired ? [] : (var.computer_domain != "" ? ["https://${var.computer_domain}"] : []),
1043
+ ["http://localhost:5180"],
1044
+ var.desktop_callback_urls
156
1045
  ))
157
- mobile_callback_urls = var.mobile_callback_urls
158
- mobile_logout_urls = var.mobile_logout_urls
1046
+ desktop_callback_urls = var.desktop_callback_urls
1047
+ mobile_callback_urls = var.mobile_callback_urls
1048
+ mobile_logout_urls = var.mobile_logout_urls
159
1049
  }
160
1050
 
161
1051
  module "dns" {
@@ -226,7 +1116,8 @@ module "compliance_exports" {
226
1116
  # compliance.export_jobs and SELECT on compliance.audit_events.
227
1117
  # Without this grant the runner throws AccessDenied at first SQS
228
1118
  # invocation (deploy run 25561658625 failed the smoke gate this way).
229
- database_secret_arn = module.database.graphql_db_secret_arn
1119
+ database_secret_arn = module.database.graphql_db_secret_arn
1120
+ enable_runner_database_secret_access = true
230
1121
  }
231
1122
 
232
1123
  module "database" {
@@ -284,17 +1175,36 @@ module "appsync" {
284
1175
  subscription_schema = local.subscription_schema
285
1176
  }
286
1177
 
287
- module "computer_runtime" {
288
- source = "../app/computer-runtime"
1178
+ locals {
1179
+ deployment_terraform_state_bucket = var.deployment_terraform_state_bucket != "" ? var.deployment_terraform_state_bucket : "thinkwork-terraform-state"
1180
+ deployment_terraform_lock_table = var.deployment_terraform_lock_table != "" ? var.deployment_terraform_lock_table : "thinkwork-terraform-locks"
1181
+ deployment_release_artifact_bucket = var.deployment_release_artifact_bucket != "" ? var.deployment_release_artifact_bucket : "thinkwork-release-artifacts"
1182
+ }
1183
+
1184
+ module "deployment_control_plane" {
1185
+ count = var.enable_deployment_control_plane ? 1 : 0
1186
+ source = "../app/deployment-control-plane"
1187
+
1188
+ stage = var.stage
1189
+ account_id = var.account_id
1190
+ region = var.region
1191
+
1192
+ release_version = var.deployment_release_version
1193
+ release_manifest_url = var.deployment_release_manifest_url
1194
+ release_manifest_sha256 = var.deployment_release_manifest_sha256
1195
+ release_manifest_signature_url = var.deployment_release_manifest_signature_url
1196
+ release_manifest_trust_policy = var.deployment_release_manifest_trust_policy
1197
+ release_manifest_trusted_keys_json = var.deployment_release_manifest_trusted_keys_json
289
1198
 
290
- stage = var.stage
291
- account_id = var.account_id
292
- region = var.region
293
- vpc_id = module.vpc.vpc_id
294
- subnet_ids = module.vpc.private_subnet_ids
295
- task_subnet_ids = local.computer_task_subnet_ids
296
- assign_public_ip = length(module.vpc.public_subnet_ids) > 0
297
- appsync_api_arn = module.appsync.graphql_api_arn
1199
+ terraform_state_bucket = local.deployment_terraform_state_bucket
1200
+ terraform_lock_table = local.deployment_terraform_lock_table
1201
+ release_artifact_bucket = local.deployment_release_artifact_bucket
1202
+
1203
+ terraform_module_source = var.deployment_terraform_module_source
1204
+ terraform_module_version = var.deployment_terraform_module_version
1205
+
1206
+ log_retention_days = var.deployment_control_plane_log_retention_days
1207
+ create_secret_placeholders = var.deployment_control_plane_create_secret_placeholders
298
1208
  }
299
1209
 
300
1210
  module "api" {
@@ -313,6 +1223,10 @@ module "api" {
313
1223
  graphql_db_secret_arn = module.database.graphql_db_secret_arn
314
1224
  database_name = var.database_name
315
1225
 
1226
+ billing_export_bucket_name = var.billing_export_bucket_name
1227
+ billing_export_manifest_key = var.billing_export_manifest_key
1228
+ billing_reconciliation_tolerance_usd = var.billing_reconciliation_tolerance_usd
1229
+
316
1230
  # Phase 3 U4 — compliance-outbox-drainer connects as `compliance_drainer`
317
1231
  # via this dedicated secret (provisioned in U2 / PR #887, populated by
318
1232
  # the compliance-bootstrap CI step in deploy.yml).
@@ -358,6 +1272,10 @@ module "api" {
358
1272
  bucket_name = module.s3.bucket_name
359
1273
  bucket_arn = module.s3.bucket_arn
360
1274
 
1275
+ plugin_catalog_github_token_secret_arn = var.plugin_catalog_github_token_secret_arn
1276
+
1277
+ brain_artifacts_kms_key_arn = module.kms.key_arn
1278
+
361
1279
  user_pool_id = module.cognito.user_pool_id
362
1280
  user_pool_arn = module.cognito.user_pool_arn
363
1281
  admin_client_id = module.cognito.admin_client_id
@@ -372,25 +1290,64 @@ module "api" {
372
1290
  lambda_zips_dir = var.lambda_zips_dir
373
1291
  api_auth_secret = var.api_auth_secret
374
1292
  db_password = var.db_password
375
- agentcore_function_name = module.agentcore.agentcore_function_name
376
- agentcore_flue_function_name = module.agentcore_flue.agentcore_flue_function_name
377
- agentcore_function_arn = module.agentcore.agentcore_function_arn
378
- agentcore_flue_function_arn = module.agentcore_flue.agentcore_flue_function_arn
1293
+ bootstrap_credential_lease_kms_key_id = var.bootstrap_credential_lease_kms_key_id
1294
+ agentcore_pi_function_name = module.agentcore_pi.agentcore_pi_function_name
1295
+ agentcore_pi_function_arn = module.agentcore_pi.agentcore_pi_function_arn
1296
+ enable_agentcore_pi_invoke_policy = true
379
1297
  hindsight_endpoint = local.hindsight_enabled ? module.hindsight[0].hindsight_endpoint : ""
380
1298
  agentcore_memory_id = module.agentcore_memory.memory_id
381
1299
  memory_engine = local.resolved_memory_engine
382
- admin_url = var.admin_domain != "" ? "https://${var.admin_domain}" : "https://${module.admin_site.distribution_domain}"
1300
+ cognee_enabled = local.cognee_enabled
1301
+ cognee_endpoint = local.cognee_enabled ? module.cognee[0].cognee_endpoint : ""
1302
+ cognee_log_group_name = local.cognee_enabled ? module.cognee[0].cognee_log_group_name : ""
1303
+ cognee_backend_mode = local.cognee_enabled ? module.cognee[0].cognee_backend_mode : ""
1304
+ cognee_cluster_arn = local.cognee_enabled ? module.cognee[0].cognee_cluster_arn : ""
1305
+ cognee_service_name = local.cognee_enabled ? module.cognee[0].cognee_service_name : ""
1306
+ cognee_worker_subnet_ids = local.cognee_enabled ? local.cognee_worker_subnet_ids : []
1307
+ cognee_worker_security_group_ids = local.cognee_enabled ? [aws_security_group.cognee_worker[0].id] : []
1308
+ okf_efs_subnet_ids = var.okf_wiki_efs_enabled ? local.okf_wiki_subnet_ids : []
1309
+ okf_efs_security_group_ids = var.okf_wiki_efs_enabled ? [aws_security_group.okf_wiki_lambda[0].id] : []
1310
+ okf_efs_mount_target_ids = var.okf_wiki_efs_enabled ? aws_efs_mount_target.okf_wiki[*].id : []
1311
+ okf_efs_file_system_arn = var.okf_wiki_efs_enabled ? aws_efs_file_system.okf_wiki[0].arn : ""
1312
+ okf_efs_refresh_access_point_arn = var.okf_wiki_efs_enabled ? aws_efs_access_point.okf_wiki_refresh[0].arn : ""
1313
+ twenty_provisioned = local.twenty_provisioned
1314
+ twenty_runtime_enabled = local.twenty_runtime_enabled
1315
+ twenty_url = local.twenty_provisioned ? module.twenty[0].twenty_url : ""
1316
+ twenty_alb_arn = local.twenty_provisioned ? module.twenty[0].twenty_alb_arn : ""
1317
+ twenty_target_group_arn = local.twenty_provisioned ? module.twenty[0].twenty_target_group_arn : ""
1318
+ twenty_cluster_arn = local.twenty_provisioned ? module.twenty[0].twenty_cluster_arn : ""
1319
+ twenty_server_service_name = local.twenty_provisioned ? module.twenty[0].twenty_server_service_name : ""
1320
+ twenty_worker_service_name = local.twenty_provisioned ? module.twenty[0].twenty_worker_service_name : ""
1321
+ twenty_server_log_group_name = local.twenty_provisioned ? module.twenty[0].twenty_server_log_group_name : ""
1322
+ twenty_worker_log_group_name = local.twenty_provisioned ? module.twenty[0].twenty_worker_log_group_name : ""
1323
+ admin_url = local.end_user_app_url
383
1324
  docs_url = "https://${module.docs_site.distribution_domain}"
384
1325
  www_url = var.www_domain != "" ? "https://${var.www_domain}" : "https://${module.www_site.distribution_domain}"
385
1326
  stripe_price_ids_json = var.stripe_price_ids_json
386
1327
  appsync_realtime_url = module.appsync.graphql_realtime_url
387
- ecr_repository_url = module.agentcore.ecr_repository_url
1328
+ ecr_repository_url = module.agentcore_platform.ecr_repository_url
1329
+ # Static truth for count gating: agentcore_platform's ECR repo is
1330
+ # unconditional, but its URL attribute is unknown until apply. The runner
1331
+ # additionally needs its image tag seeded (CI does this for repo-managed
1332
+ # stages) — scaffolded installs disable it via skill_trust_runner_enabled.
1333
+ ecr_repository_provisioned = var.skill_trust_runner_enabled
1334
+ manage_bedrock_invocation_logging = var.manage_bedrock_invocation_logging
388
1335
  job_scheduler_role_arn = module.job_triggers.job_scheduler_role_arn
389
1336
  routines_execution_role_arn = module.routines_stepfunctions.execution_role_arn
390
1337
  routines_log_group_arn = module.routines_stepfunctions.log_group_arn
1338
+ deployment_state_machine_arn = var.enable_deployment_control_plane ? module.deployment_control_plane[0].state_machine_arn : var.deployment_state_machine_arn
1339
+ deployment_control_plane_enabled = var.enable_deployment_control_plane || trimspace(var.deployment_state_machine_arn) != ""
1340
+ deployment_evidence_bucket = var.enable_deployment_control_plane ? module.deployment_control_plane[0].evidence_bucket_name : var.deployment_evidence_bucket
1341
+ deployment_release_version = var.deployment_release_version
1342
+ deployment_release_manifest_url = var.deployment_release_manifest_url
1343
+ deployment_release_manifest_sha256 = var.deployment_release_manifest_sha256
1344
+ enable_stripe_billing = var.enable_stripe_billing
1345
+ enable_slack_workspace_app = var.enable_slack_workspace_app
391
1346
  agentcore_code_interpreter_id = var.agentcore_code_interpreter_id
392
1347
  wiki_compile_model_id = var.wiki_compile_model_id
393
1348
  company_brain_source_agent_model_id = var.company_brain_source_agent_model_id
1349
+ company_brain_backdoor_install_key_secret_arn = var.company_brain_backdoor_install_key_secret_arn
1350
+ company_brain_backdoor_install_key_stages = var.company_brain_backdoor_install_key_stages
394
1351
  wiki_aggregation_pass_enabled = var.wiki_aggregation_pass_enabled
395
1352
  wiki_deterministic_linking_enabled = var.wiki_deterministic_linking_enabled
396
1353
  google_places_api_key = var.google_places_api_key
@@ -399,27 +1356,6 @@ module "api" {
399
1356
  requester_memory_dreaming_enabled = var.requester_memory_dreaming_enabled
400
1357
  requester_memory_dreaming_schedule_expression = var.requester_memory_dreaming_schedule_expression
401
1358
  requester_memory_dreaming_model_id = var.requester_memory_dreaming_model_id
402
- computer_runtime_cluster_name = module.computer_runtime.cluster_name
403
- computer_runtime_cluster_arn = module.computer_runtime.cluster_arn
404
- computer_runtime_efs_file_system_id = module.computer_runtime.efs_file_system_id
405
- computer_runtime_subnet_ids = module.computer_runtime.task_subnet_ids
406
- computer_runtime_assign_public_ip = module.computer_runtime.assign_public_ip
407
- computer_runtime_task_sg_id = module.computer_runtime.task_security_group_id
408
- computer_runtime_execution_role_arn = module.computer_runtime.execution_role_arn
409
- computer_runtime_task_role_arn = module.computer_runtime.task_role_arn
410
- computer_runtime_log_group_name = module.computer_runtime.log_group_name
411
- computer_runtime_repository_url = module.computer_runtime.repository_url
412
- computer_runtime_default_cpu = module.computer_runtime.default_cpu
413
- computer_runtime_default_memory = module.computer_runtime.default_memory
414
- computer_runtime_manager_policy_arn = module.computer_runtime.manager_policy_arn
415
-
416
- # workspace-files-efs sidecar: VPC-attached Lambda that reads any Computer's
417
- # workspace files directly off the shared EFS (bypasses the
418
- # computer_tasks queue for list/get). See plan
419
- # docs/plans/2026-05-13-XXX-feat-admin-computer-efs-listing-plan.md.
420
- workspace_admin_efs_access_point_arn = module.computer_runtime.workspace_admin_access_point_arn
421
- workspace_admin_lambda_sg_id = module.computer_runtime.workspace_admin_lambda_sg_id
422
-
423
1359
  # Per-user OAuth client credentials — fed to Secrets Manager in
424
1360
  # app/lambda-api/oauth-secrets.tf. Reuses the same google_oauth_client_*
425
1361
  # tfvars that already flow to the Cognito federated-signin module.
@@ -450,47 +1386,28 @@ module "agentcore_memory" {
450
1386
  existing_memory_id = var.agentcore_memory_id
451
1387
  }
452
1388
 
453
- module "agentcore" {
454
- source = "../app/agentcore-runtime"
455
-
456
- stage = var.stage
457
- account_id = var.account_id
458
- region = var.region
459
- bucket_name = module.s3.bucket_name
460
-
461
- hindsight_endpoint = local.hindsight_enabled ? module.hindsight[0].hindsight_endpoint : ""
462
- agentcore_memory_id = module.agentcore_memory.memory_id
463
- memory_engine = local.resolved_memory_engine
464
- requester_idle_memory_learning_enabled = var.requester_idle_memory_learning_enabled
1389
+ module "agentcore_platform" {
1390
+ source = "../app/agentcore-platform"
465
1391
 
466
- # Threaded through so the container's run_skill_dispatch can POST
467
- # terminal state back to /api/skills/complete. The lambda-api module
468
- # is declared above at line 156 as `module "api"`, so the reference
469
- # is `module.api` — not `module.lambda_api` (which doesn't exist and
470
- # broke terraform apply on every merge since #389).
471
- api_endpoint = module.api.api_endpoint
472
- api_auth_secret = var.api_auth_secret
473
- nova_act_api_key = var.nova_act_api_key
1392
+ stage = var.stage
474
1393
  }
475
1394
 
476
1395
  ################################################################################
477
- # AgentCore FluePlan §005 U2 splits the Flue Lambda + log group + IAM role
478
- # + event-invoke config out of the Strands `agentcore-runtime` module into a
479
- # dedicated module so Flue can carry its own permissions surface independently.
480
- # The shared ECR repo and async DLQ stay with `module.agentcore` and are
481
- # injected here.
1396
+ # AgentCore Pidedicated Lambda + log group + IAM role + event-invoke config.
1397
+ # Shared AgentCore substrate lives in `module.agentcore_platform`.
482
1398
  ################################################################################
483
1399
 
484
- module "agentcore_flue" {
485
- source = "../app/agentcore-flue"
1400
+ module "agentcore_pi" {
1401
+ source = "../app/agentcore-pi"
486
1402
 
487
1403
  stage = var.stage
488
1404
  account_id = var.account_id
489
1405
  region = var.region
490
1406
  bucket_name = module.s3.bucket_name
491
1407
 
492
- ecr_repository_url = module.agentcore.ecr_repository_url
493
- async_dlq_arn = module.agentcore.agentcore_async_dlq_arn
1408
+ ecr_repository_url = module.agentcore_platform.ecr_repository_url
1409
+ source_image_uri = var.agentcore_pi_source_image_uri
1410
+ async_dlq_arn = module.agentcore_platform.agentcore_async_dlq_arn
494
1411
 
495
1412
  hindsight_endpoint = local.hindsight_enabled ? module.hindsight[0].hindsight_endpoint : ""
496
1413
  agentcore_memory_id = module.agentcore_memory.memory_id
@@ -501,61 +1418,85 @@ module "agentcore_flue" {
501
1418
  api_auth_secret = var.api_auth_secret
502
1419
 
503
1420
  # Plan §005 U4 — AuroraSessionStore uses the RDS Data API. Cluster ARN
504
- # + secret come from the existing aurora-postgres module so Flue and
1421
+ # + secret come from the existing aurora-postgres module so Pi and
505
1422
  # graphql-http hit the same cluster + same credential rotation surface.
506
1423
  db_cluster_arn = module.database.db_cluster_arn
507
1424
  db_secret_arn = module.database.graphql_db_secret_arn
1425
+
1426
+ cognee_subnet_ids = local.cognee_enabled ? local.cognee_worker_subnet_ids : []
1427
+ cognee_security_group_ids = local.cognee_enabled ? [aws_security_group.cognee_worker[0].id] : []
1428
+
1429
+ okf_efs_enabled = var.okf_wiki_efs_enabled
1430
+ okf_efs_subnet_ids = var.okf_wiki_efs_enabled ? local.okf_wiki_subnet_ids : []
1431
+ okf_efs_security_group_ids = var.okf_wiki_efs_enabled ? [aws_security_group.okf_wiki_lambda[0].id] : []
1432
+ okf_efs_file_system_arn = var.okf_wiki_efs_enabled ? aws_efs_file_system.okf_wiki[0].arn : ""
1433
+ okf_efs_read_access_point_arn = var.okf_wiki_efs_enabled ? aws_efs_access_point.okf_wiki_pi_read[0].arn : ""
1434
+ }
1435
+
1436
+ # Runtime identity rename: the former dedicated Flue module is now the Pi
1437
+ # module. Move existing state to the renamed module/resource addresses so the
1438
+ # deployed Lambda, role, log group, and invoke config are updated in place.
1439
+ moved {
1440
+ from = module.agentcore_flue.aws_cloudwatch_log_group.agentcore_flue
1441
+ to = module.agentcore_pi.aws_cloudwatch_log_group.agentcore_pi
1442
+ }
1443
+
1444
+ moved {
1445
+ from = module.agentcore_flue.aws_lambda_function.agentcore_flue
1446
+ to = module.agentcore_pi.aws_lambda_function.agentcore_pi
508
1447
  }
509
1448
 
510
- # Plan §005 U2 — cross-module state migration. The Flue resources moved from
511
- # `module.agentcore` to `module.agentcore_flue`; the underlying AWS resource
512
- # attributes (function_name, log group name, ARN) are unchanged from U1, so
513
- # this is pure state-address realignment without destroy+create.
514
- #
515
- # Two `moved {}` blocks per resource form a CHAIN that covers both possible
516
- # starting states:
517
- # * Stages that never applied U1 (operator-managed greenfield, or any
518
- # stage that skipped the U1 deploy) have state at
519
- # `module.agentcore.aws_*.agentcore_pi` — the first block migrates that
520
- # to `module.agentcore.aws_*.agentcore_flue` (U1's destination), then
521
- # the second block migrates THAT to the new module.
522
- # * Stages that applied U1 (e.g. dev) have state at
523
- # `module.agentcore.aws_*.agentcore_flue` — only the second block
524
- # fires.
525
- #
526
- # Terraform follows the chain transitively. The earlier shape (both
527
- # blocks pointing directly at `module.agentcore_flue.…`) was rejected
528
- # with "Ambiguous move statements" because each destination can only have
529
- # one source — chaining through the intermediate disambiguates while still
530
- # covering both starting states.
531
1449
  moved {
532
- from = module.agentcore.aws_cloudwatch_log_group.agentcore_pi
533
- to = module.agentcore.aws_cloudwatch_log_group.agentcore_flue
1450
+ from = module.agentcore_flue.aws_lambda_function_event_invoke_config.agentcore_flue
1451
+ to = module.agentcore_pi.aws_lambda_function_event_invoke_config.agentcore_pi
534
1452
  }
535
1453
 
536
1454
  moved {
537
- from = module.agentcore.aws_cloudwatch_log_group.agentcore_flue
538
- to = module.agentcore_flue.aws_cloudwatch_log_group.agentcore_flue
1455
+ from = module.agentcore_flue.aws_iam_role.agentcore_flue
1456
+ to = module.agentcore_pi.aws_iam_role.agentcore_pi
539
1457
  }
540
1458
 
541
1459
  moved {
542
- from = module.agentcore.aws_lambda_function.agentcore_pi
543
- to = module.agentcore.aws_lambda_function.agentcore_flue
1460
+ from = module.agentcore_flue.aws_iam_role_policy.agentcore_flue
1461
+ to = module.agentcore_pi.aws_iam_role_policy.agentcore_pi
544
1462
  }
545
1463
 
546
1464
  moved {
547
- from = module.agentcore.aws_lambda_function.agentcore_flue
548
- to = module.agentcore_flue.aws_lambda_function.agentcore_flue
1465
+ from = module.agentcore_flue.aws_iam_role_policy.agentcore_flue_dlq_send
1466
+ to = module.agentcore_pi.aws_iam_role_policy.agentcore_pi_dlq_send
549
1467
  }
550
1468
 
1469
+ # Shared AgentCore platform resources moved out of the retired legacy runtime
1470
+ # module so deleting that runtime does not destroy Pi's ECR repository or async
1471
+ # invoke DLQ.
551
1472
  moved {
552
- from = module.agentcore.aws_lambda_function_event_invoke_config.agentcore_pi
553
- to = module.agentcore.aws_lambda_function_event_invoke_config.agentcore_flue
1473
+ from = module.agentcore.aws_ecr_repository.agentcore
1474
+ to = module.agentcore_platform.aws_ecr_repository.agentcore
554
1475
  }
555
1476
 
556
1477
  moved {
557
- from = module.agentcore.aws_lambda_function_event_invoke_config.agentcore_flue
558
- to = module.agentcore_flue.aws_lambda_function_event_invoke_config.agentcore_flue
1478
+ from = module.agentcore.aws_ecr_lifecycle_policy.agentcore
1479
+ to = module.agentcore_platform.aws_ecr_lifecycle_policy.agentcore
1480
+ }
1481
+
1482
+ moved {
1483
+ from = module.agentcore.aws_sqs_queue.agentcore_async_dlq
1484
+ to = module.agentcore_platform.aws_sqs_queue.agentcore_async_dlq
1485
+ }
1486
+
1487
+ moved {
1488
+ from = module.routines_stepfunctions.aws_cloudwatch_event_rule.sfn_state_change[0]
1489
+ to = module.api.aws_cloudwatch_event_rule.routine_sfn_state_change[0]
1490
+ }
1491
+
1492
+ moved {
1493
+ from = module.routines_stepfunctions.aws_cloudwatch_event_target.sfn_state_change[0]
1494
+ to = module.api.aws_cloudwatch_event_target.routine_sfn_state_change[0]
1495
+ }
1496
+
1497
+ moved {
1498
+ from = module.routines_stepfunctions.aws_lambda_permission.sfn_state_change[0]
1499
+ to = module.api.aws_lambda_permission.routine_sfn_state_change[0]
559
1500
  }
560
1501
 
561
1502
  module "crons" {
@@ -581,13 +1522,12 @@ module "routines_stepfunctions" {
581
1522
  account_id = var.account_id
582
1523
  region = var.region
583
1524
 
584
- # Phase B U9: EventBridge → routine-execution-callback. Constructed
585
- # from the lambda-api naming convention rather than referencing the
586
- # module output directly to avoid a cycle (lambda-api consumes
587
- # routines_execution_role_arn from this module). The function exists
588
- # for_each-iterated under aws_lambda_function.handler[*] in lambda-api;
589
- # the ARN follows the deterministic naming pattern.
590
- execution_callback_lambda_arn = "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-routine-execution-callback"
1525
+ # The Lambda API module owns EventBridge → routine-execution-callback
1526
+ # wiring because it owns the callback function resource. Keeping the
1527
+ # callback disabled here avoids a cycle: lambda-api needs the routines
1528
+ # execution role/log group outputs, while the callback permission needs
1529
+ # the lambda-api function to exist.
1530
+ execution_callback_lambda_arn = ""
591
1531
  }
592
1532
 
593
1533
  module "hindsight" {
@@ -600,39 +1540,189 @@ module "hindsight" {
600
1540
  db_security_group_id = module.database.db_security_group_id
601
1541
  database_url = module.database.database_url
602
1542
  image_tag = var.hindsight_image_tag
1543
+
1544
+ enable_auto_consolidation = var.hindsight_enable_auto_consolidation
1545
+ consolidation_dedup_threshold = var.hindsight_consolidation_dedup_threshold
1546
+ observations_mission = var.hindsight_observations_mission
603
1547
  }
604
1548
 
605
- module "ses" {
606
- source = "../app/ses-email"
1549
+ module "cognee" {
1550
+ count = local.cognee_enabled ? 1 : 0
1551
+ source = "../../../plugins/company-brain/terraform/cognee"
1552
+
1553
+ stage = var.stage
1554
+ vpc_id = module.vpc.vpc_id
1555
+ subnet_ids = module.vpc.public_subnet_ids
1556
+ db_security_group_id = module.database.db_security_group_id
1557
+ db_host = module.database.cluster_endpoint
1558
+ db_name = var.cognee_db_name
1559
+ db_username = var.cognee_db_username
1560
+ db_password_secret_arn = var.cognee_db_password_secret_arn
1561
+
1562
+ allowed_internal_cidr_blocks = var.cognee_allowed_internal_cidr_blocks
1563
+ allowed_internal_security_group_ids = concat(
1564
+ var.cognee_allowed_internal_security_group_ids,
1565
+ [aws_security_group.cognee_worker[0].id],
1566
+ )
1567
+ image_uri = var.cognee_image_uri
1568
+ desired_count = var.cognee_desired_count
1569
+ backend_mode = var.cognee_backend_mode
1570
+
1571
+ brain_tenant_id = var.cognee_brain_tenant_id
1572
+ brain_instance_key = var.cognee_brain_instance_key
1573
+ brain_storage_tier = var.cognee_brain_storage_tier
1574
+ brain_s3_artifact_root = var.cognee_brain_s3_artifact_root
1575
+ brain_s3_manifest_root = var.cognee_brain_s3_manifest_root
1576
+ brain_s3_vault_projection_root = var.cognee_brain_s3_vault_projection_root
1577
+ brain_artifacts_bucket_arn = var.cognee_brain_artifacts_bucket_arn
1578
+ brain_artifacts_prefixes = var.cognee_brain_artifacts_prefixes
1579
+ private_substrate_mode = var.cognee_private_substrate_mode
1580
+ require_authentication = var.cognee_require_authentication
1581
+ enable_backend_access_control = var.cognee_enable_backend_access_control
1582
+ cors_allowed_origins = var.cognee_cors_allowed_origins
1583
+
1584
+ llm_provider = var.cognee_llm_provider
1585
+ llm_model = var.cognee_llm_model
1586
+ llm_api_key_secret_arn = var.cognee_llm_api_key_secret_arn
1587
+
1588
+ embedding_provider = var.cognee_embedding_provider
1589
+ embedding_model = var.cognee_embedding_model
1590
+ embedding_dimensions = var.cognee_embedding_dimensions
1591
+ embedding_api_key_secret_arn = var.cognee_embedding_api_key_secret_arn
1592
+
1593
+ vector_db_provider = var.cognee_vector_db_provider
1594
+ vector_db_url = var.cognee_vector_db_url
1595
+ vector_db_key_secret_arn = var.cognee_vector_db_key_secret_arn
1596
+
1597
+ graph_database_provider = var.cognee_graph_database_provider
1598
+ graph_database_url = var.cognee_graph_database_url
1599
+ graph_database_username = var.cognee_graph_database_username
1600
+ graph_database_password_secret_arn = var.cognee_graph_database_password_secret_arn
1601
+
1602
+ neptune_graph_id = var.cognee_neptune_graph_id
1603
+ neptune_graph_arn = var.cognee_neptune_graph_arn
1604
+ neptune_endpoint = var.cognee_neptune_endpoint
1605
+ production_posture = var.cognee_production_posture
1606
+
1607
+ bedrock_model_resource_arns = var.cognee_bedrock_model_resource_arns
1608
+ kms_key_arns = var.cognee_kms_key_arns
1609
+
1610
+ depends_on = [terraform_data.cognee_configuration_guardrails]
1611
+ }
607
1612
 
608
- stage = var.stage
609
- account_id = var.account_id
610
- region = var.region
611
- email_domain = var.ses_inbound_domain
1613
+ module "twenty" {
1614
+ count = local.twenty_provisioned ? 1 : 0
1615
+ source = "../../../plugins/twenty/terraform/twenty"
612
1616
 
613
- inbound_bucket_name = module.s3.bucket_name
614
- email_inbound_fn_arn = module.api.email_inbound_fn_arn
615
- email_inbound_fn_name = module.api.email_inbound_fn_name
1617
+ stage = var.stage
1618
+ vpc_id = module.vpc.vpc_id
1619
+ subnet_ids = module.vpc.public_subnet_ids
1620
+ cache_subnet_ids = module.vpc.private_subnet_ids
1621
+ storage_subnet_ids = module.vpc.private_subnet_ids
1622
+ db_security_group_id = module.database.db_security_group_id
1623
+ public_url = local.twenty_public_url
1624
+ certificate_arn = local.twenty_certificate_arn
1625
+ image_uri = var.twenty_image_uri
1626
+
1627
+ runtime_enabled = local.twenty_runtime_enabled
1628
+ server_desired_count = var.twenty_server_desired_count
1629
+ worker_desired_count = var.twenty_worker_desired_count
1630
+
1631
+ db_url_secret_arn = var.twenty_db_url_secret_arn
1632
+ encryption_key_secret_arn = var.twenty_encryption_key_secret_arn
1633
+ fallback_encryption_key_secret_arn = var.twenty_fallback_encryption_key_secret_arn
1634
+ app_secret_arn = var.twenty_app_secret_arn
1635
+
1636
+ email_from_address = local.twenty_email_from_address
1637
+ email_from_name = var.twenty_email_from_name
1638
+ email_smtp_host = var.twenty_email_smtp_host
1639
+
1640
+ cache_engine = var.twenty_cache_engine
1641
+ cache_engine_version = var.twenty_cache_engine_version
1642
+ cache_parameter_group_family = var.twenty_cache_parameter_group_family
1643
+ cache_node_type = var.twenty_cache_node_type
1644
+ cache_num_cache_clusters = var.twenty_cache_num_cache_clusters
1645
+ allowed_public_cidr_blocks = var.twenty_allowed_public_cidr_blocks
1646
+ kms_key_arns = var.twenty_kms_key_arns
1647
+
1648
+ depends_on = [terraform_data.twenty_configuration_guardrails]
1649
+ }
616
1650
 
617
- manage_active_rule_set = var.ses_manage_active_rule_set
1651
+ module "n8n" {
1652
+ count = local.n8n_provisioned ? 1 : 0
1653
+ source = "../../../plugins/n8n/terraform/n8n"
1654
+
1655
+ stage = var.stage
1656
+ vpc_id = module.vpc.vpc_id
1657
+ subnet_ids = module.vpc.public_subnet_ids
1658
+ cache_subnet_ids = module.vpc.private_subnet_ids
1659
+ db_security_group_id = module.database.db_security_group_id
1660
+ database_host = module.database.cluster_endpoint
1661
+ public_url = local.n8n_public_url
1662
+ certificate_arn = local.n8n_certificate_arn
1663
+ image_uri = var.n8n_image_uri
1664
+
1665
+ runtime_enabled = local.n8n_runtime_enabled
1666
+ main_desired_count = var.n8n_main_desired_count
1667
+ worker_desired_count = var.n8n_worker_desired_count
1668
+ worker_concurrency = var.n8n_worker_concurrency
1669
+ container_port = var.n8n_container_port
1670
+
1671
+ database_admin_secret_arn = var.n8n_database_admin_secret_arn
1672
+ database_url_secret_arn = var.n8n_database_url_secret_arn
1673
+ database_name = var.n8n_database_name
1674
+ database_username = var.n8n_database_username
1675
+ encryption_key_secret_arn = var.n8n_encryption_key_secret_arn
1676
+ operator_secret_arn = var.n8n_operator_secret_arn
1677
+ service_credential_secret_arn = var.n8n_service_credential_secret_arn
1678
+ agent_step_bridge_credential_secret_arn = var.n8n_agent_step_bridge_credential_secret_arn
1679
+ create_secret_placeholders = var.deployment_control_plane_create_secret_placeholders
1680
+
1681
+ storage_bucket_name = var.n8n_storage_bucket_name
1682
+ create_storage_bucket = (
1683
+ var.n8n_storage_bucket_name != "" ? var.n8n_create_storage_bucket : false
1684
+ )
1685
+ storage_prefix = var.n8n_storage_prefix
1686
+ execution_data_storage_mode = var.n8n_execution_data_storage_mode
1687
+ binary_data_mode = var.n8n_binary_data_mode
1688
+
1689
+ queue_mode = var.n8n_queue_mode
1690
+ task_runners_enabled = var.n8n_task_runners_enabled
1691
+ package_config_digest = var.n8n_package_config_digest
1692
+ custom_package_specs = var.n8n_custom_package_specs
1693
+
1694
+ cache_engine = var.n8n_cache_engine
1695
+ cache_engine_version = var.n8n_cache_engine_version
1696
+ cache_parameter_group_family = var.n8n_cache_parameter_group_family
1697
+ cache_node_type = var.n8n_cache_node_type
1698
+ cache_num_cache_clusters = var.n8n_cache_num_cache_clusters
1699
+
1700
+ allowed_public_cidr_blocks = var.n8n_allowed_public_cidr_blocks
1701
+ kms_key_arns = var.n8n_kms_key_arns
1702
+
1703
+ depends_on = [terraform_data.n8n_configuration_guardrails]
618
1704
  }
619
1705
 
620
- ################################################################################
621
- # Admin Static Site
622
- ################################################################################
1706
+ module "ses" {
1707
+ source = "../app/ses-email"
623
1708
 
624
- module "admin_site" {
625
- source = "../app/static-site"
1709
+ stage = var.stage
1710
+ account_id = var.account_id
1711
+ region = var.region
1712
+ email_domain = var.ses_inbound_domain
1713
+ parent_domain = var.ses_parent_domain
1714
+ tenant_slugs = var.ses_tenant_slugs
626
1715
 
627
- stage = var.stage
628
- site_name = "admin"
629
- is_spa = true
630
- custom_domain = var.admin_domain
631
- certificate_arn = var.admin_certificate_arn
1716
+ inbound_bucket_name = module.s3.bucket_name
1717
+ email_inbound_fn_arn = module.api.email_inbound_fn_arn
1718
+ email_inbound_fn_name = module.api.email_inbound_fn_name
1719
+ enable_email_inbound_lambda_action = true
1720
+
1721
+ manage_active_rule_set = var.ses_manage_active_rule_set
632
1722
  }
633
1723
 
634
1724
  ################################################################################
635
- # End-User App Static Site (apps/computer — canonical surface at app.thinkwork.ai)
1725
+ # End-User App Static Site (apps/web — canonical surface at app.thinkwork.ai)
636
1726
  ################################################################################
637
1727
 
638
1728
  locals {
@@ -644,11 +1734,9 @@ locals {
644
1734
  computer_host_frame_src = local.computer_sandbox_enabled ? "https://${var.computer_sandbox_domain}" : "'none'"
645
1735
  computer_host_frame_ancestors = join(" ", compact([
646
1736
  "'self'",
647
- var.admin_domain != "" ? "https://${var.admin_domain}" : "",
648
- "https://${module.admin_site.distribution_domain}",
649
1737
  ]))
650
1738
 
651
- 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; 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};"
1739
+ 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};"
652
1740
  }
653
1741
 
654
1742
  module "computer_site" {
@@ -656,6 +1744,7 @@ module "computer_site" {
656
1744
 
657
1745
  stage = var.stage
658
1746
  site_name = "computer"
1747
+ bucket_name = "thinkwork-${var.stage}-${var.account_id}-computer"
659
1748
  is_spa = true
660
1749
  custom_domain = local.end_user_app_domain
661
1750
  custom_domain_aliases = local.computer_compat_redirect_enabled ? [
@@ -680,6 +1769,52 @@ module "computer_site" {
680
1769
  }
681
1770
  }
682
1771
 
1772
+ ################################################################################
1773
+ # Customer Domain (<name>.thinkwork.ai — optional)
1774
+ #
1775
+ # Route53 zone + CAA as soon as customer_domain is set; ACM cert (us-east-1),
1776
+ # validation, and A/AAAA aliases onto the app distribution once
1777
+ # customer_domain_delegated flips. The module is always instantiated — every
1778
+ # resource inside is count-gated on the plain bool/string vars, so stacks
1779
+ # without a customer domain plan zero resources here. The bidirectional
1780
+ # module references (cert → distribution, distribution domain → alias
1781
+ # records) are acyclic at the resource level.
1782
+ ################################################################################
1783
+
1784
+ module "customer_domain" {
1785
+ source = "../app/customer-domain"
1786
+
1787
+ providers = {
1788
+ aws.us_east_1 = aws.us_east_1
1789
+ }
1790
+
1791
+ # No depends_on on the guardrails: a module-level depends_on coarsens the
1792
+ # module's output dependencies, which would turn the intentional
1793
+ # cert → distribution → alias-records chain into a module-level cycle.
1794
+ # The guardrail preconditions fail the plan on their own.
1795
+ stage = var.stage
1796
+ region = var.region
1797
+ account_id = var.account_id
1798
+ customer_domain = var.customer_domain
1799
+ customer_domain_delegated = var.customer_domain_delegated
1800
+ app_distribution_domain_name = module.computer_site.distribution_domain
1801
+
1802
+ # SES send + receive for the customer domain. Created with the zone —
1803
+ # intentionally pre-delegation — see the module header for the ~72h
1804
+ # verification-expiry note and re-verify procedure.
1805
+ inbound_bucket_name = module.s3.bucket_name
1806
+ email_inbound_fn_arn = module.api.email_inbound_fn_arn
1807
+ email_inbound_fn_name = module.api.email_inbound_fn_name
1808
+ enable_email_inbound_lambda_action = true
1809
+
1810
+ # KTD6 — one active receipt rule set per account/region. The customer-domain
1811
+ # module only activates its rule set when the ses-email module is disabled
1812
+ # in this account (customer deployments: always disabled — the controller
1813
+ # threads zero ses_* vars). ses_manage_active_rule_set still applies so a
1814
+ # secondary stage sharing an account never fights over activation.
1815
+ manage_active_rule_set = local.customer_domain_manage_active_rule_set
1816
+ }
1817
+
683
1818
  ################################################################################
684
1819
  # Computer Sandbox Static Site (sandbox.thinkwork.ai — LLM-fragment iframe host)
685
1820
  #
@@ -693,7 +1828,7 @@ module "computer_site" {
693
1828
  # channelId nonce + no-secrets-in-payload (see contract v1).
694
1829
  #
695
1830
  # Bucket is empty in this PR. U9 populates it with the iframe-shell bundle
696
- # via scripts/build-computer.sh.
1831
+ # via scripts/build-web.sh.
697
1832
  #
698
1833
  # Iframe CSP profile (per contract v1 §CSP profile):
699
1834
  # default-src 'none'; script-src 'self' blob:; worker-src 'self' blob:;
@@ -701,7 +1836,7 @@ module "computer_site" {
701
1836
  # https://*.tile.openstreetmap.org https://api.mapbox.com;
702
1837
  # font-src 'self' data:; connect-src 'none';
703
1838
  # frame-src https://www.openstreetmap.org; object-src 'none';
704
- # base-uri 'self'; frame-ancestors <var.computer_sandbox_allowed_parent_origins>;
1839
+ # base-uri 'self'; frame-ancestors <web parents + desktop custom protocols>;
705
1840
  #
706
1841
  # Provisioning is gated on var.computer_sandbox_domain — leave empty in
707
1842
  # stages that haven't allocated the subdomain yet.
@@ -710,7 +1845,20 @@ module "computer_site" {
710
1845
  locals {
711
1846
  computer_sandbox_enabled = var.computer_sandbox_domain != ""
712
1847
 
713
- computer_sandbox_frame_ancestors = local.computer_sandbox_enabled && var.computer_sandbox_allowed_parent_origins != "" ? join(" ", split(",", replace(var.computer_sandbox_allowed_parent_origins, " ", ""))) : "'none'"
1848
+ computer_sandbox_desktop_parent_origins = [
1849
+ "thinkwork://app",
1850
+ "thinkwork-dev://app",
1851
+ "thinkwork-canary://app",
1852
+ ]
1853
+ computer_sandbox_allowed_parent_origin_list = distinct(concat(
1854
+ [
1855
+ for origin in split(",", replace(var.computer_sandbox_allowed_parent_origins, " ", "")) :
1856
+ origin if origin != ""
1857
+ ],
1858
+ local.computer_sandbox_desktop_parent_origins
1859
+ ))
1860
+ computer_sandbox_allowed_parent_origins_effective = join(",", local.computer_sandbox_allowed_parent_origin_list)
1861
+ 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'"
714
1862
 
715
1863
  computer_sandbox_map_img_src = "https://*.tile.openstreetmap.org https://api.mapbox.com"
716
1864
  computer_sandbox_map_frame_src = "https://www.openstreetmap.org"
@@ -724,6 +1872,7 @@ module "computer_sandbox_site" {
724
1872
 
725
1873
  stage = var.stage
726
1874
  site_name = "computer-sandbox"
1875
+ bucket_name = "thinkwork-${var.stage}-${var.account_id}-computer-sandbox"
727
1876
  is_spa = false
728
1877
  custom_domain = var.computer_sandbox_domain
729
1878
  certificate_arn = var.computer_sandbox_certificate_arn
@@ -768,6 +1917,7 @@ module "docs_site" {
768
1917
 
769
1918
  stage = var.stage
770
1919
  site_name = "docs"
1920
+ bucket_name = "thinkwork-${var.stage}-${var.account_id}-docs"
771
1921
  custom_domain = var.docs_domain
772
1922
  certificate_arn = var.docs_certificate_arn
773
1923
  }
@@ -781,6 +1931,7 @@ module "www_site" {
781
1931
 
782
1932
  stage = var.stage
783
1933
  site_name = "www"
1934
+ bucket_name = "thinkwork-${var.stage}-${var.account_id}-www"
784
1935
  custom_domain = var.www_domain
785
1936
  certificate_arn = var.www_certificate_arn
786
1937
  # is_spa defaults to false — SSG output, directory URIs get rewritten to index.html