thinkwork-cli 0.12.15 → 0.12.17

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 (152) hide show
  1. package/dist/{api-client-4VT3FWOH.js → api-client-JUBCQQDF.js} +1 -1
  2. package/dist/{chunk-H7AB42ES.js → chunk-STY44FQO.js} +13 -0
  3. package/dist/cli.js +1107 -10225
  4. package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +3 -8
  5. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +1 -184
  6. package/dist/drizzle/0050_brain_v0_entity_subtype.sql +2 -2
  7. package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +6 -6
  8. package/dist/drizzle/0052_brain_v0_external_refs.sql +2 -2
  9. package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +2 -2
  10. package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +14 -14
  11. package/dist/drizzle/0146_knowledge_graph_source_scope.sql +1 -1
  12. package/dist/drizzle/0157_knowledge_graph_observations_source.sql +2 -2
  13. package/dist/drizzle/0158_wiki_tenant_scope.sql +1 -1
  14. package/dist/drizzle/0165_plugin_premium_entitlements.sql +2 -2
  15. package/dist/drizzle/{0166_company_brain_substrate_contract.sql → 0166_brain_substrate_contract.sql} +9 -9
  16. package/dist/drizzle/{0167_company_brain_artifact_manifest_runtime.sql → 0167_brain_artifact_manifest_runtime.sql} +5 -5
  17. package/dist/drizzle/0168_eval_skill_gate.sql +1 -1
  18. package/dist/drizzle/0183_okf_artifact_manifests.sql +1 -1
  19. package/dist/drizzle/0197_rcm_context_columns.sql +28 -0
  20. package/dist/drizzle/0200_eval_results_execution_tier.sql +40 -0
  21. package/dist/drizzle/0201_brain_dream_runs.sql +72 -0
  22. package/dist/drizzle/0202_backfill_thread_participants_from_senders.sql +110 -0
  23. package/dist/drizzle/0203_threads_mode_override.sql +61 -0
  24. package/dist/drizzle/0204_thread_turns_triggering_message_id.sql +48 -0
  25. package/dist/drizzle/0205_routines_git_python.sql +185 -0
  26. package/dist/drizzle/0206_agent_loop_versions_routine_actions.sql +40 -0
  27. package/dist/drizzle/0207_tenant_credentials_github_repo_kind.sql +52 -0
  28. package/dist/drizzle/0208_knowledge_graph_neutral_identifiers.sql +253 -0
  29. package/dist/drizzle/0209_living_artifacts_schema.sql +109 -0
  30. package/dist/drizzle/0210_think137_automations_additive_schema.sql +113 -0
  31. package/dist/drizzle/0211_backfill_agent_loop_versions_target_spec.sql +120 -0
  32. package/dist/drizzle/0212_fold_webhooks_into_automations.sql +312 -0
  33. package/dist/drizzle/0213_agent_loop_versions_judge_evidence_nullable.sql +63 -0
  34. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +99 -0
  35. package/dist/drizzle/0215_agent_loop_versions_goal_worker_policy_nullable.sql +70 -0
  36. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +81 -0
  37. package/dist/drizzle/0217_agents_capability_folder_dispatch.sql +12 -0
  38. package/dist/drizzle/0218_document_plates.sql +30 -0
  39. package/dist/drizzle/0219_document_section_waivers.sql +29 -0
  40. package/dist/drizzle/0220_artifacts_created_by_user.sql +21 -0
  41. package/dist/drizzle/0221_artifact_shares.sql +29 -0
  42. package/dist/drizzle/0221_workflow_interpreter_thin_slice.sql +71 -0
  43. package/dist/drizzle/0222_compliance_drainer_select_audit_events.sql +21 -0
  44. package/dist/drizzle/0223_document_conformance_reports.sql +38 -0
  45. package/dist/drizzle/0224_artifact_refresh_state.sql +11 -0
  46. package/dist/drizzle/0225_think216_agent_loop_convergence.sql +21 -0
  47. package/dist/drizzle/0226_drop_n8n_agent_step_runs.sql +6 -0
  48. package/dist/drizzle/0227_analyst_reader_role.sql +1055 -0
  49. package/dist/drizzle/0228_tenant_credentials_rds_iam_kind.sql +62 -0
  50. package/dist/drizzle/0229_analyst_reader_rds_iam_grant.sql +79 -0
  51. package/dist/drizzle/0230_analyst_rls.sql +1613 -0
  52. package/dist/drizzle/0231_cost_events_cache_write_enforcement_exempt.sql +11 -0
  53. package/dist/plugins/catalog/package.json +0 -1
  54. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
  55. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +16 -8
  56. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +56 -0
  57. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
  58. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
  59. package/dist/plugins/catalog/src/contracts.ts +112 -0
  60. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
  61. package/dist/plugins/n8n/README.md +4 -36
  62. package/dist/plugins/n8n/src/deployment/managed-app.ts +0 -17
  63. package/dist/plugins/n8n/src/manifest.ts +0 -5
  64. package/dist/plugins/n8n/terraform/n8n/main.tf +0 -26
  65. package/dist/plugins/n8n/terraform/n8n/outputs.tf +0 -5
  66. package/dist/plugins/n8n/terraform/n8n/variables.tf +0 -6
  67. package/dist/plugins/n8n/test/manifest.test.ts +0 -1
  68. package/dist/plugins/twenty/package.json +5 -0
  69. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +232 -0
  70. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +489 -0
  71. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +199 -0
  72. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +43 -0
  73. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +230 -0
  74. package/dist/plugins/twenty/scripts/lib/__tests__/twenty-client.test.ts +207 -0
  75. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +282 -0
  76. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +220 -0
  77. package/dist/plugins/twenty/scripts/lib/load-records.ts +680 -0
  78. package/dist/plugins/twenty/scripts/lib/mappers.ts +559 -0
  79. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +381 -0
  80. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +191 -0
  81. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +425 -0
  82. package/dist/plugins/twenty/scripts/lib/twenty-client.ts +246 -0
  83. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +610 -0
  84. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +214 -0
  85. package/dist/plugins/twenty/src/manifest.ts +99 -0
  86. package/dist/plugins/twenty/terraform/twenty/README.md +1 -1
  87. package/dist/plugins/twenty/test/manifest.test.ts +12 -2
  88. package/dist/terraform/examples/greenfield/main.tf +90 -380
  89. package/dist/terraform/examples/greenfield/terraform.tfvars.example +0 -1
  90. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  91. package/dist/terraform/modules/app/agentcore-pi/main.tf +36 -32
  92. package/dist/terraform/modules/app/agentcore-pi/variables.tf +23 -15
  93. package/dist/terraform/modules/app/agentcore-runtime/main.tf +3 -3
  94. package/dist/terraform/modules/app/customer-domain/main.tf +16 -2
  95. package/dist/terraform/modules/app/deployment-control-plane/main.tf +5 -0
  96. package/dist/terraform/modules/app/deployment-control-plane/runner.py +85 -207
  97. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +56 -2
  98. package/dist/terraform/modules/app/hindsight-memory/main.tf +52 -4
  99. package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +7 -0
  100. package/dist/terraform/modules/app/lambda-api/handlers.tf +493 -89
  101. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +215 -96
  102. package/dist/terraform/modules/app/lambda-api/main.tf +24 -5
  103. package/dist/terraform/modules/app/lambda-api/outputs.tf +19 -11
  104. package/dist/terraform/modules/app/lambda-api/variables.tf +94 -67
  105. package/dist/terraform/modules/app/ses-email/main.tf +14 -2
  106. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/main.tf +493 -0
  107. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/outputs.tf +34 -0
  108. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/variables.tf +32 -0
  109. package/dist/terraform/modules/data/aurora-postgres/main.tf +55 -0
  110. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +15 -0
  111. package/dist/terraform/modules/data/s3-buckets/main.tf +26 -0
  112. package/dist/terraform/modules/thinkwork/README.md +0 -1
  113. package/dist/terraform/modules/thinkwork/main.tf +167 -446
  114. package/dist/terraform/modules/thinkwork/outputs.tf +2 -142
  115. package/dist/terraform/modules/thinkwork/variables.tf +55 -309
  116. package/dist/terraform/schema.graphql +4 -0
  117. package/dist/workspace-defaults/files/AGENTS.md +45 -31
  118. package/dist/workspace-defaults/files/CONTEXT.md +48 -4
  119. package/dist/workspace-defaults/files/GUARDRAILS.md +18 -0
  120. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +32 -4
  121. package/dist/workspace-defaults/files/SPACE.md +34 -19
  122. package/dist/workspace-defaults/files/TOOLS.md +30 -0
  123. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +154 -0
  124. package/dist/workspace-defaults/files/skills/document-composer/references/authoring-rules.md +101 -0
  125. package/package.json +2 -1
  126. package/dist/plugins/company-brain/README.md +0 -37
  127. package/dist/plugins/company-brain/package.json +0 -33
  128. package/dist/plugins/company-brain/runtime/cognee/Dockerfile +0 -11
  129. package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +0 -458
  130. package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +0 -539
  131. package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +0 -312
  132. package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +0 -449
  133. package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +0 -655
  134. package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +0 -474
  135. package/dist/plugins/company-brain/src/api/cognee-client.ts +0 -896
  136. package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +0 -49
  137. package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +0 -84
  138. package/dist/plugins/company-brain/src/api/context-engine-provider.ts +0 -888
  139. package/dist/plugins/company-brain/src/api/migration.ts +0 -665
  140. package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +0 -396
  141. package/dist/plugins/company-brain/src/index.ts +0 -62
  142. package/dist/plugins/company-brain/src/manifest.ts +0 -112
  143. package/dist/plugins/company-brain/terraform/cognee/README.md +0 -263
  144. package/dist/plugins/company-brain/terraform/cognee/main.tf +0 -766
  145. package/dist/plugins/company-brain/terraform/cognee/outputs.tf +0 -144
  146. package/dist/plugins/company-brain/terraform/cognee/variables.tf +0 -402
  147. package/dist/plugins/company-brain/test/api/cognee-client.test.ts +0 -562
  148. package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +0 -74
  149. package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +0 -447
  150. package/dist/plugins/company-brain/test/api/migration.test.ts +0 -465
  151. package/dist/plugins/company-brain/test/manifest.test.ts +0 -95
  152. package/dist/plugins/company-brain/tsconfig.json +0 -8
@@ -10,14 +10,9 @@ locals {
10
10
  use_local_zips = var.lambda_zips_dir != ""
11
11
  eval_fanout_queue_url = local.deploy_lambda_handlers ? aws_sqs_queue.eval_fanout[0].url : ""
12
12
  runtime = "nodejs20.x"
13
- cognee_env = var.cognee_enabled ? {
14
- # graphql-http is close to Lambda's 4 KB environment ceiling. Keep Cognee
15
- # status in one compact value; stable names are derived in the resolver.
16
- COGNEE = "${var.cognee_backend_mode}|${var.cognee_endpoint}"
17
- } : {}
18
13
  # Twenty managed-app status is DB-served (managed_applications +
19
14
  # deployment jobs — plan 2026-06-12-001 U10); the TWENTY config key is
20
- # retired. Cognee's env-var status projection above is unchanged.
15
+ # retired.
21
16
  optional_integration_handler_names = concat(
22
17
  var.deployment_control_plane_enabled ? [] : [
23
18
  # Host-only onboarding/deployment API. Customer foundations disable the
@@ -53,9 +48,32 @@ locals {
53
48
  # env forever, secrets live in Secrets Manager (R4), never in the String
54
49
  # document.
55
50
  config_env = merge({
56
- DATABASE_SECRET_ARN = var.graphql_db_secret_arn
57
- DATABASE_HOST = var.db_cluster_endpoint
58
- DATABASE_NAME = var.database_name
51
+ # THINK-173: PUBLIC verification key (not a secret) + the NAME of the
52
+ # private-key secret (resolved via runtime-config's secret loader —
53
+ # the PEM itself never enters the env or the String document).
54
+ CAPABILITY_SIGNING_PUBLIC_KEY = var.capability_signing_public_key
55
+ CAPABILITY_SIGNING_PRIVATE_KEY_SECRET = var.capability_signing_private_key_secret
56
+ # THINK-229 U3/KTD5 — the analyst policy-source enforcement flip.
57
+ # "row" (default) keeps sidecar policy shadow-only; "sidecar" makes the
58
+ # signed sidecar block authoritative (budgets/policyClaims flow). Flip
59
+ # ONLY after clean shadow parity on live traffic.
60
+ ANALYST_POLICY_SOURCE = var.analyst_policy_source
61
+ # THINK-230 — the operator-facing provisionAnalystConnector mutation runs
62
+ # the analyst connector provisioning ceremony inside graphql-http, so the
63
+ # shared api handlers read the same broker-secret ARN + rds_iam connect
64
+ # config the analyst-query-broker handler carries per-handler. Read via
65
+ # getConfig() (env-wins), never process.env (runtime-config fixture gate).
66
+ # ANALYST_DB_CLUSTER_ENDPOINT gates on the resource id so a stage without
67
+ # the IAM grant leaves rds_iam provisioning off (resolveAnalystRdsIamConfig
68
+ # returns null) instead of half-seeding a credential row.
69
+ ANALYST_BROKER_SECRET_ARN = var.analyst_broker_secret_arn
70
+ ANALYST_DB_CLUSTER_ENDPOINT = var.analyst_db_cluster_resource_id != "" ? var.db_cluster_endpoint : ""
71
+ ANALYST_DB_CLUSTER_RESOURCE_ID = var.analyst_db_cluster_resource_id
72
+ ANALYST_DB_NAME = var.database_name
73
+ ANALYST_DB_USER = "analyst_reader"
74
+ DATABASE_SECRET_ARN = var.graphql_db_secret_arn
75
+ DATABASE_HOST = var.db_cluster_endpoint
76
+ DATABASE_NAME = var.database_name
59
77
  # BUCKET_NAME and USER_POOL_ID were duplicate aliases of WORKSPACE_BUCKET
60
78
  # and COGNITO_USER_POOL_ID; GRAPHQL_API_KEY duplicated APPSYNC_API_KEY;
61
79
  # THINKWORK_API_SECRET and EMAIL_HMAC_SECRET duplicated API_AUTH_SECRET
@@ -72,6 +90,27 @@ locals {
72
90
  COGNITO_APP_CLIENT_IDS = "${var.admin_client_id},${var.mobile_client_id}"
73
91
  APPSYNC_ENDPOINT = var.appsync_api_url
74
92
  THINKWORK_API_URL = local.api_base_url
93
+ # Deterministic routines v1 (plan 2026-07-03-004 U6): activates the
94
+ # git-backed routine lifecycle tool suite on the admin-ops MCP server
95
+ # (routine_repo_list/read/commit, routine_run_fixtures, routine_runs).
96
+ # Read via getConfig() in admin-ops-mcp.ts; without this the tools are
97
+ # listed but every call returns not_yet_enabled.
98
+ ROUTINES_AGENT_TOOLS_ENABLED = "true"
99
+ # THINK-137 Automations U9 (R15): activates the read-only Automation
100
+ # agent tools on the admin-ops MCP server (automations_list,
101
+ # automation_get). Read via getConfig() in admin-ops-mcp.ts; without
102
+ # this the tools are listed but every call returns not_yet_enabled.
103
+ AUTOMATIONS_AGENT_TOOLS_ENABLED = "true"
104
+ # THINK-227 U10 (KTD11): DEDICATED inert gate for the automation WRITE
105
+ # tools (automation_save, automation_delete). Deliberately "false" at
106
+ # merge — the read flag above has been live stage-wide since THINK-137,
107
+ # so reusing it would pre-activate writes on every already-assigned
108
+ # tenant. Flipped per-stage as the U12 rollout step (dev dogfood first,
109
+ # then TEI) after the pre-deploy assignment enumeration. Flipped for dev
110
+ # 2026-07-09: enumeration found admin-ops assigned to two internal
111
+ # tenants only (sleek-squirrel-230, academic-bobcat-897); AE1-AE4 smokes
112
+ # green on the deployed U1-U11 stack.
113
+ AUTOMATIONS_AGENT_WRITE_TOOLS_ENABLED = "true"
75
114
  # Comma-separated allowlist of caller emails permitted to invoke
76
115
  # operator-gated mutations (updateTenantPolicy, sandbox fixture
77
116
  # setup, etc.). Resolved against ctx.auth.email, which is pulled
@@ -84,10 +123,13 @@ locals {
84
123
  # WORKSPACE_RENDERER_FUNCTION_NAME is derived from the per-stage naming
85
124
  # convention by deriveFunctionName("workspace-renderer") — stored
86
125
  # nowhere (R7).
87
- WORKSPACE_BUCKET = var.bucket_name
88
- HINDSIGHT_ENDPOINT = var.hindsight_endpoint
89
- AGENTCORE_MEMORY_ID = var.agentcore_memory_id
90
- MEMORY_ENGINE = var.memory_engine
126
+ WORKSPACE_BUCKET = var.bucket_name
127
+ HINDSIGHT_ENDPOINT = var.hindsight_endpoint
128
+ # THINK-220 cutover flag: empty = hindsight schema on the primary DB;
129
+ # set = that database's public schema via the database-pg seam.
130
+ HINDSIGHT_DATABASE_NAME = var.hindsight_database_name
131
+ AGENTCORE_MEMORY_ID = var.agentcore_memory_id
132
+ MEMORY_ENGINE = var.memory_engine
91
133
  # CHAT_AGENT_INVOKE_FN_ARN (~112 serialized bytes) was dropped for the
92
134
  # 4KB env ceiling (#2375): getChatAgentInvokeFnArn and managed-dispatch
93
135
  # now derive the ARN from the deterministic naming pattern
@@ -113,13 +155,11 @@ locals {
113
155
  # OAUTH_CALLBACK_URL is the URL registered with Google/Azure OAuth apps.
114
156
  # REDIRECT_SUCCESS_URL is the fallback post-OAuth redirect when the
115
157
  # caller doesn't pass a per-request returnUrl (mobile passes thinkwork://).
116
- GOOGLE_PRODUCTIVITY_OAUTH_SECRET_ARN = aws_secretsmanager_secret.oauth_google_productivity.arn
117
- OAUTH_CALLBACK_URL = "https://${aws_apigatewayv2_api.main.id}.execute-api.${var.region}.amazonaws.com/api/oauth/callback"
118
- REDIRECT_SUCCESS_URL = var.redirect_success_url
119
- COMPANY_BRAIN_SOURCE_AGENT_MODEL_ID = var.company_brain_source_agent_model_id
120
- COMPANY_BRAIN_BACKDOOR_INSTALL_KEY_SECRET_ARN = var.company_brain_backdoor_install_key_secret_arn
121
- COMPANY_BRAIN_BACKDOOR_INSTALL_KEY_STAGES = var.company_brain_backdoor_install_key_stages
122
- WWW_URL = var.www_url
158
+ GOOGLE_PRODUCTIVITY_OAUTH_SECRET_ARN = aws_secretsmanager_secret.oauth_google_productivity.arn
159
+ OAUTH_CALLBACK_URL = "https://${aws_apigatewayv2_api.main.id}.execute-api.${var.region}.amazonaws.com/api/oauth/callback"
160
+ REDIRECT_SUCCESS_URL = var.redirect_success_url
161
+ BRAIN_SOURCE_AGENT_MODEL_ID = var.brain_source_agent_model_id
162
+ WWW_URL = var.www_url
123
163
  },
124
164
  # Stripe billing — see stripe-secrets.tf. The ARN is the indirection;
125
165
  # the actual keys live in Secrets Manager and are fetched by
@@ -139,7 +179,6 @@ locals {
139
179
  # set to hello@thinkwork.ai once the bare-apex identity is verified in SES.
140
180
  STRIPE_WELCOME_FROM_EMAIL = var.stripe_welcome_from_email
141
181
  } : {},
142
- local.cognee_env,
143
182
  )
144
183
 
145
184
  # graphql-http-only config that also belongs in the runtime-config
@@ -152,13 +191,6 @@ locals {
152
191
  # Settings > General starts release updates from the GraphQL API.
153
192
  DEPLOYMENT_STATE_MACHINE_ARN = var.deployment_state_machine_arn
154
193
  DEPLOYMENT_EVIDENCE_BUCKET = var.deployment_evidence_bucket
155
- # Cognee user + Space memory captures use explicit add+cognify so accepted
156
- # documents enter the scoped graph. GraphQL has a 30s Lambda ceiling, so
157
- # indexing wait is intentionally short/best-effort; callers poll search for
158
- # eventual retrieval instead of pinning the capture request.
159
- COGNEE_INGEST_MODE = "add_cognify"
160
- COGNEE_INDEX_TIMEOUT_MS = "8000"
161
- COGNEE_INDEX_POLL_MS = "2000"
162
194
  # THNK-37 — the GraphQL API is the runtime trust boundary for the
163
195
  # GitHub-hosted signed plugin catalog. Browsers keep reading through
164
196
  # GraphQL; API verifies the release asset with the trusted public key
@@ -206,6 +238,46 @@ locals {
206
238
  }
207
239
 
208
240
  handler_extra_env = {
241
+ # Analyst query broker (THINK-228 U3). Reader role + caller credential
242
+ # secrets, and the workspace bucket's analyst-staging/ prefix for
243
+ # large-result CSVs (lifecycle TTL lives on the bucket module). The
244
+ # shared writer DATABASE_SECRET_ARN also lands in this handler's env
245
+ # via common_env, but the broker code never uses it — SQL runs only as
246
+ # analyst_reader.
247
+ "analyst-query-broker" = {
248
+ ANALYST_READER_SECRET_ARN = var.analyst_reader_secret_arn
249
+ ANALYST_BROKER_SECRET_ARN = var.analyst_broker_secret_arn
250
+ ANALYST_STAGING_BUCKET = var.bucket_name
251
+ ANALYST_STAGING_PREFIX = "analyst-staging"
252
+ # THINK-229 U1: RDS IAM connect config. Endpoint presence switches
253
+ # analyst-reader-db.ts to the IAM-token path (password secret above
254
+ # is the pre-GRANT-rds_iam fallback, retired once IAM is proven).
255
+ # Gated on the resource ID so a stage without the IAM grant keeps
256
+ # the password path instead of failing into the fallback every cold
257
+ # start.
258
+ ANALYST_DB_CLUSTER_ENDPOINT = var.analyst_db_cluster_resource_id != "" ? var.db_cluster_endpoint : ""
259
+ ANALYST_DB_CLUSTER_RESOURCE_ID = var.analyst_db_cluster_resource_id
260
+ ANALYST_DB_NAME = var.database_name
261
+ ANALYST_DB_USER = "analyst_reader"
262
+ }
263
+ # THINK-229 U5 — the connection reconciler probes the analyst_reader
264
+ # connection EXACTLY as the broker does (getAnalystReaderClient), so it
265
+ # needs the same IAM-connect config + the password-fallback secret. The
266
+ # shared lambda execution role already holds rds-db:connect on the
267
+ # analyst_reader dbuser (iam-grouped.tf, granted in U1), so no extra IAM.
268
+ "analyst-connection-reconciler" = {
269
+ ANALYST_READER_SECRET_ARN = var.analyst_reader_secret_arn
270
+ ANALYST_DB_CLUSTER_ENDPOINT = var.analyst_db_cluster_resource_id != "" ? var.db_cluster_endpoint : ""
271
+ ANALYST_DB_CLUSTER_RESOURCE_ID = var.analyst_db_cluster_resource_id
272
+ ANALYST_DB_NAME = var.database_name
273
+ ANALYST_DB_USER = "analyst_reader"
274
+ }
275
+ # THINK-246: customer stages cannot send as the dev fallback domain
276
+ # (noreply@agents.thinkwork.ai is only verified in the dev account) —
277
+ # observed live on TEI as ses_send_failed on the deliver step.
278
+ "artifact-deliver" = {
279
+ ARTIFACT_DELIVERY_FROM_EMAIL = var.artifact_delivery_from_email
280
+ }
209
281
  "extension-proxy" = {
210
282
  EXTENSION_PROXY_BACKENDS_JSON = var.extension_proxy_backends_json
211
283
  EXTENSION_PROXY_SIGNING_SECRET = var.extension_proxy_signing_secret
@@ -250,6 +322,10 @@ locals {
250
322
  # The parameter may contain a placeholder value at apply time — the
251
323
  # Lambda logs and degrades gracefully if decryption returns empty.
252
324
  GOOGLE_PLACES_SSM_PARAM_NAME = "/thinkwork/${var.stage}/google-places/api-key"
325
+ # THINK-200 chain: a successful compile Event-invokes okf-materialize
326
+ # so the OKF projection (and, chained, the Pi navigator's EFS view)
327
+ # stays current without manual invocation.
328
+ OKF_MATERIALIZE_FN_NAME = "thinkwork-${var.stage}-api-okf-materialize"
253
329
  }
254
330
  "ontology-scan" = {
255
331
  BEDROCK_MODEL_ID = var.wiki_compile_model_id
@@ -260,6 +336,8 @@ locals {
260
336
  }
261
337
  "okf-materialize" = {
262
338
  BRAIN_ARTIFACTS_BUCKET = aws_s3_bucket.brain_artifacts.bucket
339
+ # THINK-200 chain: fresh bundles fan out to the EFS current view.
340
+ OKF_EFS_REFRESH_FN_NAME = "thinkwork-${var.stage}-api-okf-efs-refresh"
263
341
  }
264
342
  "okf-efs-refresh" = {
265
343
  BRAIN_ARTIFACTS_BUCKET = aws_s3_bucket.brain_artifacts.bucket
@@ -287,8 +365,15 @@ locals {
287
365
  "chat-agent-invoke" = {
288
366
  KNOWLEDGE_GRAPH_TOOL_ENABLED = tostring(var.knowledge_graph_tool_enabled)
289
367
  }
368
+ # 240s: sync Hindsight retain (LLM extraction + auto-consolidation) can
369
+ # exceed 60s; the client timeout must stay below the Lambda timeout (300s)
370
+ # and below the Hindsight ALB idle_timeout (300s) so failures classify as
371
+ # client timeouts, never ALB 504s.
290
372
  "memory-retain" = {
291
- HINDSIGHT_TIMEOUT_MS = "75000"
373
+ HINDSIGHT_TIMEOUT_MS = "240000"
374
+ }
375
+ "brain-dream-state" = {
376
+ BRAIN_DREAM_STATE_ENABLED = tostring(var.brain_dream_state_enabled)
292
377
  }
293
378
  # Bedrock KB provisioning. Per-handler (not common_env) so these don't bloat
294
379
  # the already-near-4KB graphql-http env. Bedrock's RDS-backed KB needs the
@@ -297,32 +382,19 @@ locals {
297
382
  KB_SERVICE_ROLE_ARN = var.kb_service_role_arn
298
383
  DATABASE_CLUSTER_ARN = var.db_cluster_arn
299
384
  }
300
- "knowledge-graph-thread-ingest" = {
301
- COGNEE_ENDPOINT = var.cognee_endpoint
302
- COGNEE_BACKEND_MODE = var.cognee_backend_mode
303
- COGNEE_INGEST_MODE = "remember"
304
- BRAIN_ARTIFACTS_BUCKET = aws_s3_bucket.brain_artifacts.bucket
305
- }
306
- # Observations → Knowledge Graph worker (plan 2026-06-09-004 U5).
307
- # add_cognify pins the incremental ingest path into the stable
308
- # per-tenant dataset; the promotion-gate classifier reads
309
- # OBSERVATION_CLASSIFIER_MODEL_ID (Bedrock IAM via the shared
310
- # lambda_bedrock invoke policy).
385
+ # Observations → Knowledge Graph worker. Extraction is now a Bedrock
386
+ # structured-output call inside this Lambda. KG_EXTRACTION_MODEL_ID pins
387
+ # the gpt-oss extraction model (Bedrock IAM via the shared lambda_bedrock
388
+ # invoke policy, same as the promotion-gate classifier).
311
389
  "knowledge-graph-observations-ingest" = {
312
- COGNEE_ENDPOINT = var.cognee_endpoint
313
- COGNEE_BACKEND_MODE = var.cognee_backend_mode
314
- COGNEE_INGEST_MODE = "add_cognify"
315
390
  BRAIN_ARTIFACTS_BUCKET = aws_s3_bucket.brain_artifacts.bucket
316
391
  OBSERVATION_CLASSIFIER_MODEL_ID = var.observation_classifier_model_id
317
- # Per-run candidate cap: bounds classifier cost AND keeps each Cognee
318
- # cognify small enough to index within budget on the single dogfood
319
- # task; truncated runs self-invoke (Event) to drain the backlog across
320
- # successive runs. COGNEE_INDEX_TIMEOUT_MS raised well above the default
321
- # 240s (dogfood indexing of a fresh dataset is slow) but under the 900s
322
- # Lambda ceiling, leaving room for graph fetch + normalize + snapshot.
392
+ KG_EXTRACTION_MODEL_ID = var.kg_extraction_model_id
393
+ # Per-run candidate cap bounds classifier + extraction cost; a
394
+ # truncated backlog drains via an in-process loop inside one
395
+ # invocation (never Lambda self-invoke AWS recursive-loop
396
+ # detection terminates worker-to-self Event chains).
323
397
  KG_OBS_MAX_CANDIDATES_PER_RUN = var.kg_obs_max_candidates_per_run
324
- COGNEE_INDEX_TIMEOUT_MS = "700000"
325
- COGNEE_INDEX_POLL_MS = "7000"
326
398
  }
327
399
  # routine-task-python (Phase B U6) needs the AgentCore code-interpreter
328
400
  # id + the per-stage S3 routine-output bucket. The interpreter id is
@@ -335,6 +407,14 @@ locals {
335
407
  ROUTINE_OUTPUT_BUCKET = "thinkwork-${var.stage}-routine-output"
336
408
  ROUTINE_PYTHON_ENV_ALLOWLIST = "TENANT_ID,ROUTINE_ID,EXECUTION_ID"
337
409
  }
410
+ # routine-exec-git (plan 2026-07-03-004 U3, KTD-10): configuration on
411
+ # the executor's own env, never new graphql-http env vars (4KB ceiling).
412
+ # The S3 SHA code cache rides the existing routine-output bucket under
413
+ # the routine-code-cache/ prefix.
414
+ "routine-exec-git" = {
415
+ SANDBOX_INTERPRETER_ID = var.agentcore_code_interpreter_id
416
+ ROUTINE_OUTPUT_BUCKET = "thinkwork-${var.stage}-routine-output"
417
+ }
338
418
  # graphql-http hosts the createRoutine / publishRoutineVersion / etc.
339
419
  # resolvers (Phase B U7) AND the routine-approval-bridge (Phase B
340
420
  # U8) which invokes routine-resume via the AWS SDK.
@@ -349,8 +429,15 @@ locals {
349
429
  # queue until U3; eval-worker is a throwing inert stub that redrives
350
430
  # accidental traffic to the DLQ.
351
431
  "eval-runner" = {
352
- EVAL_FANOUT_QUEUE_URL = local.eval_fanout_queue_url
353
- EVAL_DIRECT_AGENTCORE_MESSAGE_SHARDS = "20"
432
+ EVAL_FANOUT_QUEUE_URL = local.eval_fanout_queue_url
433
+ # 40 lanes to match the fan-out event source mapping's
434
+ # maximum_concurrency (eval-fanout.tf) — FIFO delivers at most one
435
+ # in-flight message per group, so lanes < concurrency wastes workers.
436
+ EVAL_DIRECT_AGENTCORE_MESSAGE_SHARDS = "40"
437
+ # Anthropic models run under a 10 RPM Bedrock quota in this account
438
+ # (quota increase PENDING) — pace their runs onto few lanes so they
439
+ # finish cleanly instead of throttling out. See evalLaneCountForModel.
440
+ EVAL_ANTHROPIC_MESSAGE_SHARDS = "2"
354
441
  # SSM parameter name for the Pi Bedrock AgentCore Runtime ID. deploy.yml's
355
442
  # runtime update job writes this in `update-agentcore-runtime-image.sh`;
356
443
  # eval-runner reads it via `loadRuntimeId(runtimeType)`.
@@ -393,6 +480,14 @@ locals {
393
480
  "compliance-outbox-drainer" = {
394
481
  COMPLIANCE_DRAINER_SECRET_ARN = var.compliance_drainer_secret_arn
395
482
  }
483
+ # THINK-189 U5: the conformance judge model pin lives on the sweeper
484
+ # ONLY (KTD7 — graphql-http's 4KB env ceiling is a known deploy
485
+ # blocker; the deterministic layer needs no configuration). The shared
486
+ # api_ai_statements grant already authorizes Converse with this
487
+ # inference-profile ID.
488
+ "document-conformance-judge" = {
489
+ CONFORMANCE_JUDGE_MODEL_ID = "us.anthropic.claude-haiku-4-5-20251001-v1:0"
490
+ }
396
491
  }
397
492
  }
398
493
 
@@ -466,6 +561,17 @@ resource "aws_lambda_function" "handler" {
466
561
  # Bearer API_AUTH_SECRET. Idempotent on thread_turns.finalized_at
467
562
  # (migration 0123). Plan: 2026-05-22-006.
468
563
  "chat-agent-finalize",
564
+ # canvas-refresh — headless Living Artifacts data-refresh (THINK-145 U6).
565
+ # Invoked RequestResponse by the refreshCanvasData mutation (graphql-http)
566
+ # and by job-trigger's canvas_refresh branch (U7). Re-runs the saved
567
+ # tenant-scoped MCP tool call behind each bound widget and writes the fresh
568
+ # payload into the canvas head. Runs on the SHARED lambda role, which already
569
+ # grants secretsmanager:GetSecretValue on thinkwork/* (all tenants) and
570
+ # lambda:InvokeFunction — there is no per-Lambda role in this pool. Per-
571
+ # invocation TENANT scoping is enforced IN CODE: the artifact, its bindings,
572
+ # and the tenant_mcp_servers row are all filtered by the event's tenantId,
573
+ # and the only secret read is that server row's own auth_config.secretRef.
574
+ "canvas-refresh",
469
575
  # chat-agent-activity — POST /api/threads/{threadId}/activity. The Pi
470
576
  # runtime POSTs here mid-turn so live agent activity (tool/skill/phase
471
577
  # steps, coalesced text deltas) streams to the Spaces thread via
@@ -490,6 +596,9 @@ resource "aws_lambda_function" "handler" {
490
596
  "auth-me",
491
597
  "public-auth-options",
492
598
  "workos-auth",
599
+ # Public artifact share links (THINK-208): GET /share/{token}, token
600
+ # verified in handler code (no gateway auth, uniform 404 on any miss).
601
+ "artifact-share",
493
602
  "extension-proxy",
494
603
  "tenants",
495
604
  "users",
@@ -510,11 +619,14 @@ resource "aws_lambda_function" "handler" {
510
619
  "job-trigger",
511
620
  "routine-task-weather-email",
512
621
  "webhooks",
513
- "n8n-agent-step-bridge",
514
- "n8n-agent-step-expirer",
515
622
  "webhooks-admin",
516
623
  "webhook-deliveries-cleanup",
517
624
  "skill-runs-reconciler",
625
+ # THINK-229 U5 — probes the analyst_reader connection (reachability, IAM
626
+ # auth, SELECT-grant introspection, schema drift; all read-only) every 30
627
+ # min and stamps the verdict onto runtime_metadata.analyst_probe so
628
+ # dispatch can withhold a failing/stale connection loudly.
629
+ "analyst-connection-reconciler",
518
630
  "cron-stall-monitor",
519
631
  "webhook-crm-opportunity",
520
632
  "webhook-task-event",
@@ -536,8 +648,8 @@ resource "aws_lambda_function" "handler" {
536
648
  "github-app",
537
649
  "memory",
538
650
  "memory-retain",
651
+ "brain-dream-state",
539
652
  "wiki-compile",
540
- "knowledge-graph-thread-ingest",
541
653
  "knowledge-graph-observations-ingest",
542
654
  "ontology-scan",
543
655
  "ontology-reprocess",
@@ -559,6 +671,8 @@ resource "aws_lambda_function" "handler" {
559
671
  # THNK-74 U3 — reconciles runtime-reported model usage against Bedrock
560
672
  # model invocation logs and appends invocation-scope trace ledger facts.
561
673
  "trace-invocation-reconciler",
674
+ # THINK-245 U10 — daily per-model drift check against Cost Explorer.
675
+ "cost-drift-check",
562
676
  # AgentCore Code Sandbox narrow REST endpoints (plan Unit 10 + Unit 11).
563
677
  # Both are service-endpoint shape: the runtime POSTs with
564
678
  # Bearer API_AUTH_SECRET. No GraphQL resolver involvement, no extra IAM.
@@ -578,6 +692,13 @@ resource "aws_lambda_function" "handler" {
578
692
  # (Start/Invoke/Stop CodeInterpreterSession) + S3 PutObject IAM —
579
693
  # see main.tf.
580
694
  "routine-task-python",
695
+ # routine-exec-git: deterministic git-backed routine executor (plan
696
+ # 2026-07-03-004 U3). SDK-invoked (job-trigger / dispatcher / manual
697
+ # async job). Pulls the tenant routine repo at branch HEAD,
698
+ # fixture-gates new SHAs, executes run(input) in the AgentCore code
699
+ # interpreter, and writes routine_executions rows directly. Shares the
700
+ # role-wide bedrock-agentcore + S3 + Secrets Manager grants.
701
+ "routine-exec-git",
581
702
  # routine-resume: SDK-invoked by routine-approval-bridge (Phase B
582
703
  # U8) after a HITL decision. Calls SendTaskSuccess/SendTaskFailure;
583
704
  # idempotent on already-consumed tokens. Needs states:SendTaskSuccess
@@ -598,6 +719,22 @@ resource "aws_lambda_function" "handler" {
598
719
  # for steps + on the conditional UPDATE for executions.
599
720
  "routine-step-callback",
600
721
  "routine-execution-callback",
722
+ # Workflow Interpreter Lambdas (THINK-219). One shared static state
723
+ # machine per stage (workflow-interpreter-stepfunctions module) drives
724
+ # these. workflow-step-dispatch serves EVERY interpreter ASL phase
725
+ # (load_next / dispatch_agent / await_approval / record_approval /
726
+ # record_advance) — the SFN execution role's lambda:InvokeFunction grant
727
+ # is scoped to this one function. workflow-execution-callback is the
728
+ # EventBridge SFN-state-change target that projects terminal status onto
729
+ # workflow_runs. workflow-resume is SDK-invoked by resolveWorkflowApproval
730
+ # (SendTaskSuccess/SendTaskFailure) — SendTask* is granted stage-wide in
731
+ # the RoutineTaskTokens statement (iam-grouped.tf), states:StartExecution
732
+ # on the interpreter machine is granted in api_orchestration. All three
733
+ # ship inert-throwing until U5/U6 wire them (plan KTD10 — a silent no-op
734
+ # would strand runs in queued).
735
+ "workflow-step-dispatch",
736
+ "workflow-execution-callback",
737
+ "workflow-resume",
601
738
  # Skill-run dispatcher runtime-config fetch (plan
602
739
  # docs/plans/2026-04-24-008-feat-skill-run-dispatcher-plan.md §U1). The
603
740
  # runtime skill dispatch calls this with Bearer API_AUTH_SECRET to pull
@@ -607,6 +744,12 @@ resource "aws_lambda_function" "handler" {
607
744
  # Admin-Ops MCP — JSON-RPC endpoint at POST /mcp/admin, exposes the
608
745
  # @thinkwork/admin-ops package as MCP tools for managed agents.
609
746
  "admin-ops-mcp",
747
+ # Analyst query broker — first-party MCP server at POST /mcp/analyst
748
+ # (THINK-228 U3). One tool, query: EXPLAIN-gated, extended-protocol
749
+ # single-statement SQL as the analyst_reader role; large results stage
750
+ # to S3 under analyst-staging/; every query emits a data.query_executed
751
+ # compliance audit event via POST /api/compliance/events.
752
+ "analyst-query-broker",
610
753
  # MCP admin key management — per-tenant Bearer tokens for admin-ops.
611
754
  # Admin-ops-mcp authenticates incoming tokens by sha256-hash lookup
612
755
  # against tenant_mcp_admin_keys, populated by this handler's routes.
@@ -665,6 +808,11 @@ resource "aws_lambda_function" "handler" {
665
808
  # U2). EventBridge rate(1 minute) schedule + DLQ + MaxRetryAttempts=0
666
809
  # (defined in dedicated resources below).
667
810
  "compliance-outbox-drainer",
811
+ # THINK-189 U5: conformance judge sweeper. Scheduled (rate 2 min) +
812
+ # reserved_concurrent_executions=1 (single-writer makes direct
813
+ # process-and-complete safe) + MaxRetryAttempts=0 — the sweeper's own
814
+ # next tick is the retry (dedicated resources below).
815
+ "document-conformance-judge",
668
816
  # Phase 3 U6 of the Compliance audit-event log: runtime REST emit path.
669
817
  # Cross-runtime emit endpoint POST /api/compliance/events — Bearer
670
818
  # API_AUTH_SECRET, runtime clients post here with a
@@ -711,8 +859,8 @@ resource "aws_lambda_function" "handler" {
711
859
  # validates the agent, builds the AgentCore invoke payload, dispatches
712
860
  # Event-mode, and returns. Setup is ~5s in practice; 60s gives 12×
713
861
  # headroom for transient slowness.
714
- timeout = each.key == "wakeup-processor" ? 300 : each.key == "chat-agent-invoke" ? 60 : each.key == "chat-agent-finalize" ? 60 : each.key == "workspace-event-dispatcher" ? 60 : each.key == "eval-runner" ? 900 : each.key == "eval-worker" ? 240 : each.key == "wiki-compile" ? 480 : each.key == "knowledge-graph-thread-ingest" ? 300 : each.key == "knowledge-graph-observations-ingest" ? 480 : each.key == "requester-memory-dreaming" ? 300 : each.key == "ontology-scan" ? 300 : each.key == "ontology-reprocess" ? 300 : each.key == "wiki-lint" ? 300 : each.key == "wiki-export" ? 600 : each.key == "okf-materialize" ? 600 : each.key == "okf-efs-refresh" ? 600 : each.key == "wiki-bootstrap-import" ? 900 : each.key == "folder-bundle-import" ? 300 : each.key == "routine-task-python" ? 360 : each.key == "model-converse" ? 60 : each.key == "memory-retain" ? 90 : 30
715
- memory_size = each.key == "graphql-http" ? 512 : each.key == "wakeup-processor" ? 512 : each.key == "workspace-event-dispatcher" ? 512 : each.key == "eval-runner" ? 512 : each.key == "eval-worker" ? 512 : each.key == "wiki-compile" ? 1024 : each.key == "knowledge-graph-thread-ingest" ? 1024 : each.key == "knowledge-graph-observations-ingest" ? 1024 : each.key == "requester-memory-dreaming" ? 512 : each.key == "ontology-scan" ? 512 : each.key == "wiki-export" ? 1024 : each.key == "okf-materialize" ? 1024 : each.key == "okf-efs-refresh" ? 1024 : each.key == "wiki-bootstrap-import" ? 1024 : each.key == "folder-bundle-import" ? 1024 : 256
862
+ timeout = each.key == "wakeup-processor" ? 300 : each.key == "chat-agent-invoke" ? 60 : each.key == "chat-agent-finalize" ? 60 : each.key == "workspace-event-dispatcher" ? 60 : each.key == "eval-runner" ? 900 : each.key == "eval-worker" ? 240 : each.key == "wiki-compile" ? 480 : each.key == "knowledge-graph-observations-ingest" ? 480 : each.key == "requester-memory-dreaming" ? 300 : each.key == "ontology-scan" ? 300 : each.key == "ontology-reprocess" ? 300 : each.key == "wiki-lint" ? 300 : each.key == "wiki-export" ? 600 : each.key == "okf-materialize" ? 600 : each.key == "okf-efs-refresh" ? 600 : each.key == "wiki-bootstrap-import" ? 900 : each.key == "folder-bundle-import" ? 300 : each.key == "routine-task-python" ? 360 : each.key == "routine-exec-git" ? 360 : each.key == "job-trigger" ? 600 : each.key == "model-converse" ? 60 : each.key == "memory-retain" ? 300 : each.key == "brain-dream-state" ? 900 : each.key == "canvas-refresh" ? 120 : each.key == "document-conformance-judge" ? 300 : each.key == "workflow-step-dispatch" ? 600 : each.key == "workflow-execution-callback" ? 60 : each.key == "workflow-resume" ? 60 : 30
863
+ memory_size = each.key == "graphql-http" ? 512 : each.key == "wakeup-processor" ? 512 : each.key == "workspace-event-dispatcher" ? 512 : each.key == "eval-runner" ? 512 : each.key == "eval-worker" ? 512 : each.key == "wiki-compile" ? 1024 : each.key == "knowledge-graph-observations-ingest" ? 1024 : each.key == "requester-memory-dreaming" ? 512 : each.key == "ontology-scan" ? 512 : each.key == "wiki-export" ? 1024 : each.key == "okf-materialize" ? 1024 : each.key == "okf-efs-refresh" ? 1024 : each.key == "wiki-bootstrap-import" ? 1024 : each.key == "folder-bundle-import" ? 1024 : 256
716
864
 
717
865
  filename = local.use_local_zips ? "${var.lambda_zips_dir}/${each.key}.zip" : null
718
866
  source_code_hash = local.use_local_zips ? filebase64sha256("${var.lambda_zips_dir}/${each.key}.zip") : null
@@ -724,7 +872,20 @@ resource "aws_lambda_function" "handler" {
724
872
  # concurrent drainers would race the chain head SELECT and produce
725
873
  # orphan prev_hash links). All other handlers run with the default
726
874
  # account-level concurrency pool.
727
- reserved_concurrent_executions = each.key == "compliance-outbox-drainer" ? 1 : each.key == "eval-worker" ? 20 : -1
875
+ # eval-worker's cap must be >= the fan-out event source mapping's
876
+ # maximum_concurrency (eval-fanout.tf) or UpdateEventSourceMapping
877
+ # rejects the apply.
878
+ # document-conformance-judge is also a single-writer: direct
879
+ # process-and-complete with no in-flight claim status depends on never
880
+ # having two sweepers race the same pending rows (THINK-189 KTD4).
881
+ # analyst-query-broker is capped low: each concurrent execution holds a
882
+ # dedicated analyst_reader Postgres connection and runs model-authored
883
+ # SQL — the cap bounds both connection pressure and the blast radius of
884
+ # a runaway delegation (THINK-228 U3).
885
+ # analyst-connection-reconciler is capped at 1: the probe holds one
886
+ # analyst_reader connection and overlapping probes are pointless (the
887
+ # cluster-global reader has one grant surface / one live schema).
888
+ reserved_concurrent_executions = each.key == "compliance-outbox-drainer" ? 1 : each.key == "document-conformance-judge" ? 1 : each.key == "eval-worker" ? 40 : each.key == "analyst-query-broker" ? 4 : each.key == "analyst-connection-reconciler" ? 1 : -1
728
889
 
729
890
  environment {
730
891
  variables = merge(
@@ -735,16 +896,11 @@ resource "aws_lambda_function" "handler" {
735
896
  }
736
897
 
737
898
  dynamic "vpc_config" {
738
- for_each = (
739
- (
740
- contains(["knowledge-graph-thread-ingest", "knowledge-graph-observations-ingest"], each.key) ||
741
- (each.key == "graphql-http" && var.memory_engine == "cognee")
742
- ) && local.cognee_worker_vpc_enabled
743
- ) ? [1] : each.key == "okf-efs-refresh" && local.okf_efs_vpc_enabled ? [1] : []
899
+ for_each = each.key == "okf-efs-refresh" && local.okf_efs_vpc_enabled ? [1] : []
744
900
 
745
901
  content {
746
- subnet_ids = each.key == "okf-efs-refresh" ? var.okf_efs_subnet_ids : var.cognee_worker_subnet_ids
747
- security_group_ids = each.key == "okf-efs-refresh" ? var.okf_efs_security_group_ids : var.cognee_worker_security_group_ids
902
+ subnet_ids = var.okf_efs_subnet_ids
903
+ security_group_ids = var.okf_efs_security_group_ids
748
904
  }
749
905
  }
750
906
 
@@ -993,6 +1149,41 @@ resource "aws_scheduler_schedule" "compliance_outbox_drainer" {
993
1149
  }
994
1150
  }
995
1151
 
1152
+ # ---------------------------------------------------------------------------
1153
+ # THINK-189 U5: document-conformance-judge schedule + retry config
1154
+ #
1155
+ # The sweeper claims judge_status='pending' conformance reports and scores
1156
+ # each with one Bedrock Converse call. MaxRetryAttempts=0: rows stay
1157
+ # pending on a crash and the next tick re-claims them — Lambda-level async
1158
+ # retries would just race the schedule. Reserved concurrency 1 (set above)
1159
+ # is the single-writer guarantee for direct process-and-complete.
1160
+ # ---------------------------------------------------------------------------
1161
+
1162
+ resource "aws_lambda_function_event_invoke_config" "document_conformance_judge" {
1163
+ count = local.deploy_lambda_handlers ? 1 : 0
1164
+ function_name = aws_lambda_function.handler["document-conformance-judge"].function_name
1165
+ maximum_retry_attempts = 0
1166
+ maximum_event_age_in_seconds = 3600
1167
+ }
1168
+
1169
+ resource "aws_scheduler_schedule" "document_conformance_judge" {
1170
+ count = local.deploy_lambda_handlers ? 1 : 0
1171
+
1172
+ name = "thinkwork-${var.stage}-document-conformance-judge"
1173
+ group_name = "default"
1174
+ schedule_expression = "rate(2 minutes)"
1175
+ state = "ENABLED"
1176
+
1177
+ flexible_time_window {
1178
+ mode = "OFF"
1179
+ }
1180
+
1181
+ target {
1182
+ arn = aws_lambda_function.handler["document-conformance-judge"].arn
1183
+ role_arn = aws_iam_role.scheduler.arn
1184
+ }
1185
+ }
1186
+
996
1187
  # ---------------------------------------------------------------------------
997
1188
  # API Gateway routes → Lambda integrations
998
1189
  # ---------------------------------------------------------------------------
@@ -1197,10 +1388,11 @@ locals {
1197
1388
  # Webhooks (public trigger) — legacy PRD-19 tokenized webhooks.
1198
1389
  "POST /webhooks/{proxy+}" = "webhooks"
1199
1390
 
1200
- # n8n agent-step bridge public tenant-scoped credential endpoint for
1201
- # stock n8n HTTP Request nodes.
1202
- "POST /api/integrations/n8n/agent-steps" = "n8n-agent-step-bridge"
1203
- "OPTIONS /api/integrations/n8n/agent-steps" = "n8n-agent-step-bridge"
1391
+ # Public artifact share links (THINK-208). Unauthenticated by design:
1392
+ # the HMAC-signed token is the access grant, verified in handler code;
1393
+ # every miss returns a uniform 404. GET-only — the page is a top-level
1394
+ # navigation, so no OPTIONS/preflight fires (KTD-9).
1395
+ "GET /share/{token}" = "artifact-share"
1204
1396
 
1205
1397
  # Webhooks admin
1206
1398
  "ANY /api/webhooks/{proxy+}" = "webhooks-admin"
@@ -1299,6 +1491,20 @@ locals {
1299
1491
  # retained as a break-glass superuser path for bootstrap/debug.
1300
1492
  "POST /mcp/admin" = "admin-ops-mcp"
1301
1493
 
1494
+ # Analyst query broker — first-party MCP server exposing query
1495
+ # (THINK-228 U3). Callers present the tenant-wide broker service
1496
+ # credential as Bearer; SQL executes as the hardened analyst_reader
1497
+ # role. The seeded postgres-dev connector row points at this route.
1498
+ "POST /mcp/analyst" = "analyst-query-broker"
1499
+
1500
+ # Sourced analyst broker route (THINK-239) — the same handler serves a
1501
+ # registered EXTERNAL Postgres source at /mcp/analyst/<slug>. The broker
1502
+ # requires a signed caller context whose sourceClaims.slug matches the
1503
+ # path (the legacy bearer is never accepted here) and connects using the
1504
+ # per-source reader credential (thinkwork/<stage>/analyst/*, covered by
1505
+ # the shared secretsmanager:GetSecretValue on thinkwork/*).
1506
+ "POST /mcp/analyst/{sourceSlug}" = "analyst-query-broker"
1507
+
1302
1508
  # MCP admin key management — per-tenant Bearer token CRUD. Tokens
1303
1509
  # are shown ONCE at creation (POST returns raw value); server stores
1304
1510
  # sha256 hash only. These specific routes take precedence over the
@@ -1448,15 +1654,15 @@ resource "aws_scheduler_schedule" "wakeup_processor" {
1448
1654
  }
1449
1655
 
1450
1656
  # ---------------------------------------------------------------------------
1451
- # n8n agent-step expirerresumes waiting n8n executions on timeout/retry
1657
+ # webhook_deliveries retention crondaily delete of rows older than 90 days
1452
1658
  # ---------------------------------------------------------------------------
1453
1659
 
1454
- resource "aws_scheduler_schedule" "n8n_agent_step_expirer" {
1660
+ resource "aws_scheduler_schedule" "webhook_deliveries_cleanup" {
1455
1661
  count = local.deploy_lambda_handlers ? 1 : 0
1456
1662
 
1457
- name = "thinkwork-${var.stage}-n8n-agent-step-expirer"
1663
+ name = "thinkwork-${var.stage}-webhook-deliveries-cleanup"
1458
1664
  group_name = "default"
1459
- schedule_expression = "rate(1 minutes)"
1665
+ schedule_expression = "cron(0 4 * * ? *)" # daily at 04:00 UTC
1460
1666
  state = "ENABLED"
1461
1667
 
1462
1668
  flexible_time_window {
@@ -1464,37 +1670,51 @@ resource "aws_scheduler_schedule" "n8n_agent_step_expirer" {
1464
1670
  }
1465
1671
 
1466
1672
  target {
1467
- arn = aws_lambda_function.handler["n8n-agent-step-expirer"].arn
1673
+ arn = aws_lambda_function.handler["webhook-deliveries-cleanup"].arn
1468
1674
  role_arn = aws_iam_role.scheduler.arn
1469
1675
  }
1470
1676
  }
1471
1677
 
1472
1678
  # ---------------------------------------------------------------------------
1473
- # webhook_deliveries retention crondaily delete of rows older than 90 days
1679
+ # Requester memory dreamingbroad per-user memory compaction/reflection sweep
1474
1680
  # ---------------------------------------------------------------------------
1475
1681
 
1476
- resource "aws_scheduler_schedule" "webhook_deliveries_cleanup" {
1682
+ # ---------------------------------------------------------------------------
1683
+ # Brain dream state — per-bank Hindsight consolidation with audit ledger
1684
+ # (THINK-133 U4). Retries are the ledger's job (staged plan -> atomic apply
1685
+ # -> applied markers; unfinished runs resume on the next tick), so Lambda
1686
+ # async retries stay at 0, mirroring memory-retain.
1687
+ # ---------------------------------------------------------------------------
1688
+
1689
+ resource "aws_lambda_function_event_invoke_config" "brain_dream_state" {
1690
+ count = local.deploy_lambda_handlers ? 1 : 0
1691
+ function_name = aws_lambda_function.handler["brain-dream-state"].function_name
1692
+ maximum_retry_attempts = 0
1693
+ maximum_event_age_in_seconds = 3600
1694
+ }
1695
+
1696
+ resource "aws_scheduler_schedule" "brain_dream_state" {
1477
1697
  count = local.deploy_lambda_handlers ? 1 : 0
1478
1698
 
1479
- name = "thinkwork-${var.stage}-webhook-deliveries-cleanup"
1699
+ name = "thinkwork-${var.stage}-brain-dream-state"
1480
1700
  group_name = "default"
1481
- schedule_expression = "cron(0 4 * * ? *)" # daily at 04:00 UTC
1482
- state = "ENABLED"
1701
+ schedule_expression = var.brain_dream_state_schedule_expression
1702
+ state = var.brain_dream_state_enabled ? "ENABLED" : "DISABLED"
1483
1703
 
1484
1704
  flexible_time_window {
1485
1705
  mode = "OFF"
1486
1706
  }
1487
1707
 
1488
1708
  target {
1489
- arn = aws_lambda_function.handler["webhook-deliveries-cleanup"].arn
1709
+ arn = aws_lambda_function.handler["brain-dream-state"].arn
1490
1710
  role_arn = aws_iam_role.scheduler.arn
1711
+
1712
+ retry_policy {
1713
+ maximum_retry_attempts = 0
1714
+ }
1491
1715
  }
1492
1716
  }
1493
1717
 
1494
- # ---------------------------------------------------------------------------
1495
- # Requester memory dreaming — broad per-user memory compaction/reflection sweep
1496
- # ---------------------------------------------------------------------------
1497
-
1498
1718
  resource "aws_scheduler_schedule" "requester_memory_dreaming" {
1499
1719
  count = local.deploy_lambda_handlers ? 1 : 0
1500
1720
 
@@ -1564,6 +1784,64 @@ resource "aws_scheduler_schedule" "skill_runs_reconciler" {
1564
1784
  }
1565
1785
  }
1566
1786
 
1787
+ # ---------------------------------------------------------------------------
1788
+ # analyst_connection_reconciler — probes the analyst_reader connection every
1789
+ # 30 min (reachability, IAM auth, SELECT-grant introspection, zero-write
1790
+ # assertion, schema-drift hash; all read-only) and stamps the verdict onto
1791
+ # tenant_mcp_servers.runtime_metadata.analyst_probe. Dispatch withholds a
1792
+ # failing/stale connection loudly (THINK-229 U5, R7/R8, KTD8).
1793
+ #
1794
+ # retry-0 + DLQ (project_async_retry_idempotency_lessons): the probe is
1795
+ # cheap and idempotent — the next 30-minute tick IS the retry, so stacking
1796
+ # scheduler retries only adds redundant connections. A failure lands in the
1797
+ # DLQ for operator visibility instead.
1798
+ # ---------------------------------------------------------------------------
1799
+
1800
+ resource "aws_sqs_queue" "analyst_connection_reconciler_dlq" {
1801
+ count = local.deploy_lambda_handlers ? 1 : 0
1802
+ name = "thinkwork-${var.stage}-analyst-connection-reconciler-dlq"
1803
+ message_retention_seconds = 1209600 # 14 days
1804
+
1805
+ tags = {
1806
+ Name = "thinkwork-${var.stage}-analyst-connection-reconciler-dlq"
1807
+ }
1808
+ }
1809
+
1810
+ resource "aws_lambda_function_event_invoke_config" "analyst_connection_reconciler" {
1811
+ count = local.deploy_lambda_handlers ? 1 : 0
1812
+ function_name = aws_lambda_function.handler["analyst-connection-reconciler"].function_name
1813
+ maximum_retry_attempts = 0
1814
+ maximum_event_age_in_seconds = 3600
1815
+
1816
+ destination_config {
1817
+ on_failure {
1818
+ destination = aws_sqs_queue.analyst_connection_reconciler_dlq[0].arn
1819
+ }
1820
+ }
1821
+ }
1822
+
1823
+ resource "aws_scheduler_schedule" "analyst_connection_reconciler" {
1824
+ count = local.deploy_lambda_handlers ? 1 : 0
1825
+
1826
+ name = "thinkwork-${var.stage}-analyst-connection-reconciler"
1827
+ group_name = "default"
1828
+ schedule_expression = "rate(30 minutes)"
1829
+ state = "ENABLED"
1830
+
1831
+ flexible_time_window {
1832
+ mode = "OFF"
1833
+ }
1834
+
1835
+ target {
1836
+ arn = aws_lambda_function.handler["analyst-connection-reconciler"].arn
1837
+ role_arn = aws_iam_role.scheduler.arn
1838
+
1839
+ retry_policy {
1840
+ maximum_retry_attempts = 0
1841
+ }
1842
+ }
1843
+ }
1844
+
1567
1845
  # ---------------------------------------------------------------------------
1568
1846
  # eval_runs reconciler — finalizes stuck-running eval runs every 5 min.
1569
1847
  # Guards against worker crashes/timeouts that occur before a per-case result
@@ -1782,6 +2060,121 @@ resource "aws_scheduler_schedule" "cost_bill_reconciler" {
1782
2060
  }
1783
2061
  }
1784
2062
 
2063
+ # ---------------------------------------------------------------------------
2064
+ # cost_drift_check — THINK-245 U10/R9. Daily comparison of recorded per-model
2065
+ # LLM spend against Cost Explorer (day D-2; CE lags ~24h and refreshes by
2066
+ # early UTC afternoon). Emits CostDriftPercent / CostDriftCheckFailed EMF
2067
+ # metrics; the alarms below page the cost-alerts SNS topic. Drift discovered
2068
+ # by a customer instead of an alarm is the incident this exists to prevent.
2069
+ # ---------------------------------------------------------------------------
2070
+
2071
+ resource "aws_scheduler_schedule" "cost_drift_check" {
2072
+ count = local.deploy_lambda_handlers ? 1 : 0
2073
+
2074
+ name = "thinkwork-${var.stage}-cost-drift-check"
2075
+ group_name = "default"
2076
+ schedule_expression = "cron(0 14 * * ? *)"
2077
+ state = "ENABLED"
2078
+
2079
+ flexible_time_window {
2080
+ mode = "OFF"
2081
+ }
2082
+
2083
+ target {
2084
+ arn = aws_lambda_function.handler["cost-drift-check"].arn
2085
+ role_arn = aws_iam_role.scheduler.arn
2086
+ }
2087
+ }
2088
+
2089
+ # THINK-245 U10 — cost alerting channel. First notification-wired alarms in
2090
+ # the stack; endpoints come from var.cost_alert_emails (sensitive).
2091
+ resource "aws_sns_topic" "cost_alerts" {
2092
+ count = local.deploy_lambda_handlers ? 1 : 0
2093
+ name = "thinkwork-${var.stage}-cost-alerts"
2094
+ }
2095
+
2096
+ resource "aws_sns_topic_subscription" "cost_alert_emails" {
2097
+ for_each = local.deploy_lambda_handlers ? toset(nonsensitive(var.cost_alert_emails)) : toset([])
2098
+
2099
+ topic_arn = aws_sns_topic.cost_alerts[0].arn
2100
+ protocol = "email"
2101
+ endpoint = each.value
2102
+ }
2103
+
2104
+ # Drift > 1% for the daily datapoint, or the drift check stopped emitting
2105
+ # entirely (treat_missing_data = breaching — a silent checker is itself the
2106
+ # failure mode; see the Jun 25–Jul 9 reconciler incident).
2107
+ resource "aws_cloudwatch_metric_alarm" "cost_drift" {
2108
+ count = local.deploy_lambda_handlers ? 1 : 0
2109
+
2110
+ alarm_name = "thinkwork-${var.stage}-cost-drift"
2111
+ alarm_description = "Recorded per-model LLM spend diverges >1% from Cost Explorer (THINK-245 R9/AE5), or the drift check stopped emitting."
2112
+ namespace = "Thinkwork/Costs"
2113
+ metric_name = "CostDriftPercent"
2114
+ statistic = "Maximum"
2115
+ period = 86400
2116
+ evaluation_periods = 1
2117
+ threshold = 1
2118
+ comparison_operator = "GreaterThanThreshold"
2119
+ treat_missing_data = "breaching"
2120
+ alarm_actions = [aws_sns_topic.cost_alerts[0].arn]
2121
+ ok_actions = [aws_sns_topic.cost_alerts[0].arn]
2122
+
2123
+ dimensions = {
2124
+ Stage = var.stage
2125
+ }
2126
+ }
2127
+
2128
+ # Reconciler health (THINK-245 R8/AE4): sustained matched==0 while
2129
+ # unreconciled work exists, or the reconciler stopped emitting metrics at
2130
+ # all. Metric math because the condition spans two metrics.
2131
+ resource "aws_cloudwatch_metric_alarm" "cost_reconciler_stalled" {
2132
+ count = local.deploy_lambda_handlers ? 1 : 0
2133
+
2134
+ alarm_name = "thinkwork-${var.stage}-cost-reconciler-stalled"
2135
+ alarm_description = "trace-invocation-reconciler matched 0 turns across an hour while unreconciled turns exist, or stopped emitting (THINK-245 R8/AE4 — this exact condition ran silently Jun 25–Jul 9)."
2136
+ evaluation_periods = 12
2137
+ datapoints_to_alarm = 12
2138
+ threshold = 1
2139
+ comparison_operator = "GreaterThanOrEqualToThreshold"
2140
+ treat_missing_data = "breaching"
2141
+ alarm_actions = [aws_sns_topic.cost_alerts[0].arn]
2142
+ ok_actions = [aws_sns_topic.cost_alerts[0].arn]
2143
+
2144
+ metric_query {
2145
+ id = "stalled"
2146
+ expression = "IF(matched == 0 AND unreconciled > 0, 1, 0)"
2147
+ label = "reconciler stalled"
2148
+ return_data = true
2149
+ }
2150
+
2151
+ metric_query {
2152
+ id = "matched"
2153
+ metric {
2154
+ namespace = "Thinkwork/Costs"
2155
+ metric_name = "ReconcilerMatched"
2156
+ period = 300
2157
+ stat = "Sum"
2158
+ dimensions = {
2159
+ Stage = var.stage
2160
+ }
2161
+ }
2162
+ }
2163
+
2164
+ metric_query {
2165
+ id = "unreconciled"
2166
+ metric {
2167
+ namespace = "Thinkwork/Costs"
2168
+ metric_name = "ReconcilerUnreconciled"
2169
+ period = 300
2170
+ stat = "Sum"
2171
+ dimensions = {
2172
+ Stage = var.stage
2173
+ }
2174
+ }
2175
+ }
2176
+ }
2177
+
1785
2178
  # ---------------------------------------------------------------------------
1786
2179
  # Stall monitor — marks stalled thread turns and runbook steps failed every
1787
2180
  # minute. This is the global backstop for agent/runtime crashes; the Computer
@@ -1839,7 +2232,10 @@ resource "aws_scheduler_schedule" "knowledge_graph_observations_ingest" {
1839
2232
  name = "thinkwork-${var.stage}-knowledge-graph-observations-ingest"
1840
2233
  group_name = "default"
1841
2234
  schedule_expression = "rate(30 minutes)"
1842
- state = "ENABLED"
2235
+ # Ships DISABLED (plan 2026-07-03-005 U4/KTD-6): the schedule enables on dev
2236
+ # only after a manual golden-set-validated run. Driven by the GHA
2237
+ # WIKI_KG_INGEST_ENABLED var.
2238
+ state = var.knowledge_graph_observations_ingest_enabled ? "ENABLED" : "DISABLED"
1843
2239
 
1844
2240
  flexible_time_window {
1845
2241
  mode = "OFF"
@@ -1849,6 +2245,14 @@ resource "aws_scheduler_schedule" "knowledge_graph_observations_ingest" {
1849
2245
  arn = aws_lambda_function.handler["knowledge-graph-observations-ingest"].arn
1850
2246
  role_arn = aws_iam_role.scheduler.arn
1851
2247
  input = jsonencode({ sweep = true, trigger = "scheduled" })
2248
+
2249
+ # Periodic idempotent worker: the next 30-minute tick IS the retry.
2250
+ # Scheduler-level retries stack extra invocations onto a cadence that
2251
+ # already self-corrects (cursor-guarded), compounding the invocation
2252
+ # volume that helped trip Lambda recursive-loop detection.
2253
+ retry_policy {
2254
+ maximum_retry_attempts = 0
2255
+ }
1852
2256
  }
1853
2257
  }
1854
2258
 
@@ -1922,7 +2326,7 @@ resource "aws_s3_bucket_lifecycle_configuration" "wiki_exports" {
1922
2326
  }
1923
2327
  }
1924
2328
 
1925
- # Canonical Company Brain artifact store. Unlike wiki_exports, this bucket
2329
+ # Canonical ThinkWork Brain artifact store. Unlike wiki_exports, this bucket
1926
2330
  # is the durable replay/projection substrate for Brain source artifacts,
1927
2331
  # ingestion manifests, migration snapshots, vault projections, and exports.
1928
2332
  resource "aws_s3_bucket" "brain_artifacts" {
@@ -1931,7 +2335,7 @@ resource "aws_s3_bucket" "brain_artifacts" {
1931
2335
 
1932
2336
  tags = {
1933
2337
  Name = "thinkwork-${var.stage}-brain-artifacts"
1934
- Purpose = "company-brain-artifacts"
2338
+ Purpose = "brain-artifacts"
1935
2339
  }
1936
2340
  }
1937
2341