thinkwork-cli 0.13.0 → 0.13.2

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 (178) hide show
  1. package/dist/{api-client-JUBCQQDF.js → api-client-JYZXYQJR.js} +3 -1
  2. package/dist/bowser-6Z6JFAIR.js +2822 -0
  3. package/dist/chunk-DV3MRBR5.js +43 -0
  4. package/dist/chunk-DVPBI5HM.js +1728 -0
  5. package/dist/chunk-EI5ZX7CN.js +522 -0
  6. package/dist/chunk-EUJ7X6YD.js +126 -0
  7. package/dist/chunk-GG5WKBOX.js +436 -0
  8. package/dist/chunk-I64EZSU5.js +358 -0
  9. package/dist/chunk-ILZKG5WV.js +6151 -0
  10. package/dist/chunk-KMIJ5KGT.js +128 -0
  11. package/dist/chunk-OCPHU34C.js +996 -0
  12. package/dist/chunk-QOERT44A.js +3286 -0
  13. package/dist/chunk-XYM7PE2A.js +2264 -0
  14. package/dist/{chunk-STY44FQO.js → chunk-Z2F6RJFR.js} +12 -18
  15. package/dist/cli.js +72564 -18884
  16. package/dist/dist-es-CRV46D5B.js +315 -0
  17. package/dist/dist-es-DCY4IC3Y.js +2000 -0
  18. package/dist/dist-es-ENBUC3JP.js +14726 -0
  19. package/dist/dist-es-G54LBMRN.js +48 -0
  20. package/dist/dist-es-G7PTG75M.js +176 -0
  21. package/dist/dist-es-GKDBE7E6.js +88 -0
  22. package/dist/dist-es-HY3535OI.js +70 -0
  23. package/dist/dist-es-KL7Y5NB2.js +377 -0
  24. package/dist/dist-es-LDE437AM.js +170 -0
  25. package/dist/dist-es-P26FWFLT.js +485 -0
  26. package/dist/dist-es-Q57SZXZ7.js +70 -0
  27. package/dist/dist-es-R7FGL2IB.js +88 -0
  28. package/dist/dist-es-USORBXZ5.js +318 -0
  29. package/dist/dist-es-ZKC2QKD2.js +377 -0
  30. package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +2 -1
  31. package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +1 -0
  32. package/dist/drizzle/0090_brain_schema_extraction.sql +10 -0
  33. package/dist/drizzle/0132_drop_computer_tables.sql +3 -0
  34. package/dist/drizzle/0160_compliance_event_types_plugins.sql +2 -0
  35. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +1 -0
  36. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +1 -0
  37. package/dist/drizzle/0227_analyst_reader_role.sql +106 -38
  38. package/dist/drizzle/0230_analyst_rls.sql +192 -66
  39. package/dist/drizzle/0232_memory_source_thin_ledger.sql +176 -0
  40. package/dist/drizzle/0232_message_source_event_id.sql +9 -0
  41. package/dist/drizzle/0233_msteams_install_and_links.sql +151 -0
  42. package/dist/drizzle/0233_workflow_memory_stage_tokens.sql +20 -0
  43. package/dist/drizzle/0234_memory_claim_lifecycle.sql +160 -0
  44. package/dist/drizzle/0235_workflow_token_execution_claim.sql +34 -0
  45. package/dist/drizzle/0236_memory_retraction_fencing.sql +50 -0
  46. package/dist/drizzle/0237_memory_claims_active_value_uidx.sql +172 -0
  47. package/dist/drizzle/0238_memory_erase_generation_fence.sql +97 -0
  48. package/dist/drizzle/0239_canonical_entity_identity.sql +257 -0
  49. package/dist/drizzle/0240_memory_personal_scope.sql +45 -0
  50. package/dist/drizzle/0241_knowledge_base_document_manifest.sql +67 -0
  51. package/dist/drizzle/0242_search_queries.sql +28 -0
  52. package/dist/drizzle/0243_section_sources_thread_refs.sql +12 -0
  53. package/dist/drizzle/0244_system_automations_and_memory_stage_overrides.sql +45 -0
  54. package/dist/drizzle/0245_wiki_page_render.sql +21 -0
  55. package/dist/drizzle/0246_automation_workflow_ownership.sql +30 -0
  56. package/dist/drizzle/0247_capability_runtime.sql +402 -0
  57. package/dist/drizzle/0248_capability_private_interpreter.sql +13 -0
  58. package/dist/drizzle/0249_capability_headless_execution_evidence.sql +40 -0
  59. package/dist/drizzle/0250_kg_schema_extraction.sql +576 -0
  60. package/dist/drizzle/0251_agents_json_render_ui.sql +25 -0
  61. package/dist/drizzle/0251_brain_stragglers.sql +263 -0
  62. package/dist/drizzle/0252_drop_kg_and_straggler_compat_views.sql +100 -0
  63. package/dist/drizzle/0253_drop_brain_substrate.sql +93 -0
  64. package/dist/drizzle/0254_capability_autonomous_admission.sql +29 -0
  65. package/dist/drizzle/0255_capability_autonomous_approval_mode.sql +16 -0
  66. package/dist/drizzle/0256_agent_profile_manifest_authority.sql +18 -0
  67. package/dist/drizzle/0257_agents_runtime_allow_harness.sql +24 -0
  68. package/dist/drizzle/0257_capability_approvals.sql +53 -0
  69. package/dist/drizzle/0258_pending_tool_approvals.sql +84 -0
  70. package/dist/drizzle/0259_tenant_capability_registry_trust.sql +19 -0
  71. package/dist/drizzle/0260_scheduled_jobs_run_as_user.sql +20 -0
  72. package/dist/drizzle/0261_managed_harness_multiplayer.sql +313 -0
  73. package/dist/drizzle/0261_native_auth_control_plane.sql +328 -0
  74. package/dist/drizzle/0262_auth_subscription_tickets.sql +59 -0
  75. package/dist/drizzle/0262_parallel_harness_threads.sql +9 -0
  76. package/dist/drizzle/0263_agentcore_runtime_identifier.sql +69 -0
  77. package/dist/drizzle/0263_drop_workos_auth_runtime.sql +236 -0
  78. package/dist/drizzle/0264_auth_enrollment_attempt_limits.sql +15 -0
  79. package/dist/drizzle/0264_harness_tool_execution_ledger.sql +113 -0
  80. package/dist/drizzle/0265_eval_runs_requester_identity.sql +28 -0
  81. package/dist/drizzle/0265_require_fully_valid_auth_publication.sql +27 -0
  82. package/dist/drizzle/0266_auth_identity_recovery_grants.sql +9 -0
  83. package/dist/drizzle/0266_eval_profiles_runtime_type.sql +12 -0
  84. package/dist/drizzle/0267_ontology_candidate_rejections.sql +53 -0
  85. package/dist/drizzle/0268_identity_crosswalk_routing.sql +182 -0
  86. package/dist/drizzle/0269_ontology_system_map.sql +31 -0
  87. package/dist/event-streams-7JNLJEEL.js +894 -0
  88. package/dist/event-streams-KDF5RWR6.js +1515 -0
  89. package/dist/loadSso-DJVB7BXE.js +576 -0
  90. package/dist/loadSso-JAMNA3WY.js +583 -0
  91. package/dist/plugins/catalog/package.json +1 -2
  92. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
  93. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +0 -69
  94. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
  95. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
  96. package/dist/plugins/catalog/src/contracts.ts +2 -179
  97. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
  98. package/dist/signin-UGDWTGS4.js +821 -0
  99. package/dist/sso-oidc-Q4VXANXY.js +837 -0
  100. package/dist/sts-CWLSE3GH.js +1241 -0
  101. package/dist/sts-DNZLNVOV.js +1245 -0
  102. package/dist/terraform/examples/greenfield/main.tf +247 -50
  103. package/dist/terraform/examples/greenfield/terraform.tfvars.example +15 -1
  104. package/dist/terraform/modules/app/agentcore-admin/main.tf +341 -0
  105. package/dist/terraform/modules/app/agentcore-admin/outputs.tf +20 -0
  106. package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +61 -0
  107. package/dist/terraform/modules/app/agentcore-gateway/main.tf +305 -0
  108. package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +44 -0
  109. package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +93 -0
  110. package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +33 -0
  111. package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +543 -0
  112. package/dist/terraform/modules/app/agentcore-gateway/variables.tf +70 -0
  113. package/dist/terraform/modules/app/agentcore-harness/main.tf +330 -0
  114. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +72 -0
  115. package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +5 -0
  116. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +430 -0
  117. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +180 -0
  118. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +193 -0
  119. package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +5 -0
  120. package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +5 -0
  121. package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +5 -0
  122. package/dist/terraform/modules/app/agentcore-harness/variables.tf +96 -0
  123. package/dist/terraform/modules/app/agentcore-identity/main.tf +173 -0
  124. package/dist/terraform/modules/app/agentcore-identity/outputs.tf +51 -0
  125. package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +122 -0
  126. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +25 -0
  127. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_twenty_identity.sh +16 -0
  128. package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +40 -0
  129. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_identity.sh +73 -0
  130. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +74 -0
  131. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +61 -0
  132. package/dist/terraform/modules/app/agentcore-identity/variables.tf +54 -0
  133. package/dist/terraform/modules/app/agentcore-pi/main.tf +7 -0
  134. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +127 -21
  135. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +0 -6
  136. package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +5 -0
  137. package/dist/terraform/modules/app/capability-broker/main.tf +393 -0
  138. package/dist/terraform/modules/app/capability-broker/outputs.tf +54 -0
  139. package/dist/terraform/modules/app/capability-broker/variables.tf +98 -0
  140. package/dist/terraform/modules/app/deployment-control-plane/runner.py +1011 -31
  141. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +655 -2
  142. package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +88 -0
  143. package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +91 -0
  144. package/dist/terraform/modules/app/lambda-api/capability-search-alarms.tf +60 -0
  145. package/dist/terraform/modules/app/lambda-api/handlers.tf +606 -37
  146. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +192 -21
  147. package/dist/terraform/modules/app/lambda-api/main.tf +35 -0
  148. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +181 -4
  149. package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +92 -0
  150. package/dist/terraform/modules/app/lambda-api/msteams-app-secrets.tf +46 -0
  151. package/dist/terraform/modules/app/lambda-api/outputs.tf +35 -0
  152. package/dist/terraform/modules/app/lambda-api/runtime-config.tf +3 -21
  153. package/dist/terraform/modules/app/lambda-api/variables.tf +188 -3
  154. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/alarms.tf +47 -0
  155. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/main.tf +43 -2
  156. package/dist/terraform/modules/foundation/cognito/main.tf +319 -73
  157. package/dist/terraform/modules/foundation/cognito/outputs.tf +42 -0
  158. package/dist/terraform/modules/foundation/cognito/variables.tf +124 -2
  159. package/dist/terraform/modules/foundation/vpc/outputs.tf +5 -0
  160. package/dist/terraform/modules/thinkwork/main.tf +396 -26
  161. package/dist/terraform/modules/thinkwork/outputs.tf +188 -5
  162. package/dist/terraform/modules/thinkwork/variables.tf +154 -5
  163. package/dist/terraform/schema.graphql +69 -36
  164. package/dist/workspace-defaults/files/TOOLS.md +39 -0
  165. package/dist/workspace-defaults/files/USER.md +1 -0
  166. package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +18 -0
  167. package/dist/workspace-defaults/files/agents/coding/INSTRUCTIONS.md +11 -0
  168. package/dist/workspace-defaults/files/agents/research/INSTRUCTIONS.md +12 -0
  169. package/dist/workspace-defaults/files/agents/reviewer/INSTRUCTIONS.md +15 -0
  170. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +6 -2
  171. package/package.json +6 -3
  172. package/dist/plugins/workos-auth/package.json +0 -22
  173. package/dist/plugins/workos-auth/src/index.ts +0 -30
  174. package/dist/plugins/workos-auth/src/manifest.ts +0 -40
  175. package/dist/plugins/workos-auth/src/provider-contract.ts +0 -39
  176. package/dist/plugins/workos-auth/test/manifest.test.ts +0 -94
  177. package/dist/plugins/workos-auth/tsconfig.json +0 -8
  178. /package/dist/workspace-defaults/files/{AGENTS.md → INSTRUCTIONS.md} +0 -0
@@ -146,6 +146,31 @@ locals {
146
146
  Action = ["s3:ListBucket"]
147
147
  Resource = aws_s3_bucket.brain_artifacts.arn
148
148
  },
149
+ # THINK-193 U2 (Codex S2 + round-6 P1): bulk/prefix destruction of
150
+ # evidence snapshots lives EXCLUSIVELY on the memory-retraction-drainer's
151
+ # dedicated role (handlers.tf). The SHARED role gets only the narrow
152
+ # erase-fence COMPENSATION capability: a stage worker that loses the
153
+ # fence mid-PutObject must delete the EXACT object version it just
154
+ # wrote (VersionId from its own PutObject response) and verify the key
155
+ # is gone. Without ListBucketVersions-driven enumeration + bulk delete
156
+ # this cannot destroy anything the caller did not itself write —
157
+ # version ids are unguessable. ListBucketVersions is granted read-only
158
+ # for the verification step, prefix-conditioned.
159
+ {
160
+ Effect = "Allow"
161
+ Action = ["s3:DeleteObjectVersion"]
162
+ Resource = "${aws_s3_bucket.brain_artifacts.arn}/evidence-snapshots/*"
163
+ },
164
+ {
165
+ Effect = "Allow"
166
+ Action = ["s3:ListBucketVersions"]
167
+ Resource = aws_s3_bucket.brain_artifacts.arn
168
+ Condition = {
169
+ StringLike = {
170
+ "s3:prefix" = "evidence-snapshots/*"
171
+ }
172
+ }
173
+ },
149
174
  # (was inline policy "cognito-access")
150
175
  {
151
176
  Effect = "Allow"
@@ -156,6 +181,8 @@ locals {
156
181
  "cognito-idp:AdminRespondToAuthChallenge",
157
182
  "cognito-idp:AdminSetUserPassword",
158
183
  "cognito-idp:AdminUpdateUserAttributes",
184
+ "cognito-idp:DescribeIdentityProvider",
185
+ "cognito-idp:DescribeUserPoolClient",
159
186
  "cognito-idp:ListUsers",
160
187
  ]
161
188
  Resource = var.user_pool_arn
@@ -321,6 +348,7 @@ locals {
321
348
  # refresh path cannot replace the old manual AWS CLI step.
322
349
  var.deployment_evidence_bucket != "" ? [
323
350
  {
351
+ Sid = "DeploymentEvidenceBucket"
324
352
  Effect = "Allow"
325
353
  Action = [
326
354
  "s3:GetObject",
@@ -332,6 +360,12 @@ locals {
332
360
  "arn:aws:s3:::${var.deployment_evidence_bucket}/*",
333
361
  ]
334
362
  },
363
+ {
364
+ Sid = "AgentCoreHarnessGatewayOauthSecret"
365
+ Effect = "Allow"
366
+ Action = ["secretsmanager:GetSecretValue"]
367
+ Resource = ["arn:aws:secretsmanager:${var.region}:${var.account_id}:secret:bedrock-agentcore-identity!default/oauth2/thinkwork-${var.stage}-proof-oauth-*"]
368
+ },
335
369
  ] : [],
336
370
  var.billing_export_bucket_name != "" ? [
337
371
  {
@@ -394,6 +428,22 @@ locals {
394
428
  }
395
429
  },
396
430
  ] : [],
431
+ # THINK-280 — the headless capability executor (routine-exec-git) mints and
432
+ # advances the broker PoP session by conditional reads/writes on the broker's
433
+ # DynamoDB session table. Empty (→ no grant) when the broker is disabled.
434
+ var.capability_broker_session_table_arn != "" ? [
435
+ {
436
+ Effect = "Allow"
437
+ Action = [
438
+ "dynamodb:GetItem",
439
+ "dynamodb:PutItem",
440
+ "dynamodb:UpdateItem",
441
+ "dynamodb:DeleteItem",
442
+ "dynamodb:Query",
443
+ ]
444
+ Resource = var.capability_broker_session_table_arn
445
+ },
446
+ ] : [],
397
447
  )
398
448
 
399
449
  # ---------------------------------------------------------------------------
@@ -472,13 +522,28 @@ locals {
472
522
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-wiki-compile",
473
523
  # knowledge-graph-observations-ingest: graphql-http's
474
524
  # startKnowledgeGraphObservationsIngest mutation invokes this with
475
- # RequestResponse. (The worker's self-invoke drain is retired —
476
- # AWS recursive-loop detection terminated those chains; backlog
477
- # drains in-process now.)
525
+ # RequestResponse, and memory-stage-worker's graph stage (THINK-193
526
+ # U4 stitch) RequestResponse-invokes it with a targeted bankIds
527
+ # payload the Bedrock classifier/extraction model config lives on
528
+ # the ingest Lambda's own env, so the worker never runs the ingest
529
+ # in-process. (The worker's self-invoke drain is retired — AWS
530
+ # recursive-loop detection terminated those chains; backlog drains
531
+ # in-process now.)
478
532
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-knowledge-graph-observations-ingest",
479
533
  # wiki-bootstrap-import: bootstrapJournalImport admin mutation
480
534
  # Event-invokes this for the long-running ingest path.
481
535
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-wiki-bootstrap-import",
536
+ # harness-runner: chat-agent-invoke Event-invokes this for chat
537
+ # turns routed to the AWS AgentCore runtime (THINK-311 trial).
538
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-harness-runner",
539
+ # harness-runner queues the same canonical post-turn memory pipeline
540
+ # as Pi after a completed AgentCore turn. The retain ledger makes the
541
+ # Event invoke idempotent on the thread-turn source key.
542
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-memory-retain",
543
+ # harness-runner RequestResponse-invokes this dedicated signer. The
544
+ # signer re-derives the actor/tenant/thread tuple from the running
545
+ # turn and alone holds KMS Sign; the shared runner role never does.
546
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-turn-assertion-mint",
482
547
  # ontology-scan: startOntologySuggestionScan Event-invokes this
483
548
  # after inserting a durable scan job row.
484
549
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-ontology-scan",
@@ -518,6 +583,11 @@ locals {
518
583
  # step RequestResponse-invokes its workflow-delivery mode to email
519
584
  # the maintained report to the operator-configured recipient list.
520
585
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-artifact-deliver",
586
+ # memory-stage-worker (external-memory-compounding U1):
587
+ # workflow-step-dispatch's memory_stage step Event-invokes it after
588
+ # parking on the task token; the worker resumes the token via
589
+ # SendTaskSuccess when the pipeline stage ends.
590
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-memory-stage-worker",
521
591
  # job-trigger self-target (plan 2026-07-03-004 U5, KTD-3): the
522
592
  # manual GraphQL trigger Event-invokes job-trigger with the
523
593
  # agent_loop_continue_dispatch event so routine actions never run
@@ -528,6 +598,12 @@ locals {
528
598
  # triggers), and job-trigger's canvas_refresh branch RequestResponse-
529
599
  # invokes it (scheduled trigger). Both run on this shared role.
530
600
  "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-canvas-refresh",
601
+ # agentcore-admin (THINK-280): graphql-http's createTenant
602
+ # RequestResponse-invokes it to provision per-tenant sandbox
603
+ # resources; routine-exec-git (same shared role) reaches it for the
604
+ # capability-private provisioning path. Deployed by the dedicated
605
+ # agentcore-admin module with its own least-privilege role.
606
+ "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-agentcore-admin",
531
607
  ]
532
608
  },
533
609
  # (was standalone managed policy "workspace_renderer_invoke")
@@ -709,7 +785,7 @@ locals {
709
785
  # Group 3: AI — Bedrock model invocation, Knowledge Bases, AgentCore
710
786
  # memory / evaluations / code interpreter.
711
787
  # ---------------------------------------------------------------------------
712
- api_ai_statements = [
788
+ api_ai_statements = concat([
713
789
  # (was inline policy "bedrock-invoke")
714
790
  # Cross-region inference profiles (us.anthropic.claude-*) require
715
791
  # `bedrock:InvokeModel` on the *inference-profile* ARN AND on the
@@ -750,6 +826,18 @@ locals {
750
826
  "bedrock:GetIngestionJob",
751
827
  "bedrock:ListIngestionJobs",
752
828
  "bedrock:Retrieve",
829
+ # THINK-193 U7: knowledge-base-manager reconciles the per-document
830
+ # manifest after successful syncs (List/Get) and gates deletion
831
+ # settlement on Get + the scoped Retrieve above. Ingest/Delete cover
832
+ # the explicit direct-delete path for S3 RETAIN'd documents — the
833
+ # manager never races them with StartIngestionJob. The workspace-
834
+ # bucket S3 read the memory-stage-worker needs for document text is
835
+ # already granted by the shared role's workspace "s3-access"
836
+ # statement (data-plane group).
837
+ "bedrock:ListKnowledgeBaseDocuments",
838
+ "bedrock:GetKnowledgeBaseDocuments",
839
+ "bedrock:IngestKnowledgeBaseDocuments",
840
+ "bedrock:DeleteKnowledgeBaseDocuments",
753
841
  ]
754
842
  # CreateKnowledgeBase/CreateDataSource have no resource ARN at create
755
843
  # time and don't support resource-level scoping, so a knowledge-base/*
@@ -818,7 +906,47 @@ locals {
818
906
  ]
819
907
  Resource = "*"
820
908
  },
821
- ]
909
+ ],
910
+ # THINK-316 U2 — request path receives data-plane invocation only.
911
+ # Provisioning/version/endpoint/PassRole rights belong to Terraform's
912
+ # dedicated Harness lifecycle and never reach a chat Lambda.
913
+ #
914
+ # CRITICAL: never grant `bedrock-agentcore:InvokeAgentRuntimeCommand`
915
+ # anywhere — it bypasses Harness allowedTools enforcement. Invocation goes
916
+ # through InvokeHarness / InvokeAgentRuntime only.
917
+ var.enable_agentcore_harness_invocation ? [
918
+ {
919
+ # Data plane — invoke a Harness (and its runtime endpoints) directly.
920
+ Sid = "AgentCoreHarnessInvoke"
921
+ Effect = "Allow"
922
+ Action = [
923
+ "bedrock-agentcore:InvokeHarness",
924
+ "bedrock-agentcore:InvokeAgentRuntime",
925
+ ]
926
+ Resource = ["arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:harness/*"]
927
+ },
928
+ {
929
+ # The deterministic report adapter uses the same AgentCore Identity
930
+ # chain as Harness Gateway outbound auth: turn JWT -> workload token
931
+ # -> OBO Gateway token. No provider client secret enters Lambda.
932
+ Sid = "AgentCoreHarnessGatewayIdentity"
933
+ Effect = "Allow"
934
+ Action = [
935
+ "bedrock-agentcore:GetWorkloadAccessTokenForJWT",
936
+ "bedrock-agentcore:GetWorkloadAccessTokenForUserId",
937
+ "bedrock-agentcore:GetResourceOauth2Token",
938
+ "bedrock-agentcore:CompleteResourceTokenAuth",
939
+ ]
940
+ Resource = [
941
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default",
942
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default/workload-identity/thinkwork-${var.stage}-multiplayer-proof",
943
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:token-vault/default",
944
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:token-vault/default/oauth2credentialprovider/thinkwork-${var.stage}-proof-oauth",
945
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:token-vault/default/oauth2credentialprovider/thinkwork-${var.stage}-twenty-crm",
946
+ ]
947
+ },
948
+ ] : [],
949
+ )
822
950
 
823
951
  # ---------------------------------------------------------------------------
824
952
  # Group 4: observability — CloudWatch Logs reads, ECS/ALB health reads.
@@ -914,6 +1042,33 @@ locals {
914
1042
  )
915
1043
 
916
1044
  api_observability_statements = concat(local.api_observability_sqs_statements, [
1045
+ # Backend notification publishing is IAM-only. Scope the shared API
1046
+ # execution role to the NONE-resolver mutation fields; end-user identity-
1047
+ # pool credentials have no AppSync grant. This statement lives in the
1048
+ # observability envelope for size balance: adding subscription invalidation
1049
+ # pushed the data-plane policy over IAM's 6,144-character hard cap.
1050
+ {
1051
+ Sid = "PublishAppSyncNotifications"
1052
+ Effect = "Allow"
1053
+ Action = ["appsync:GraphQL"]
1054
+ Resource = [
1055
+ for field in [
1056
+ "notifyAgentStatus",
1057
+ "notifyNewMessage",
1058
+ "notifyHeartbeatActivity",
1059
+ "notifyThreadActivity",
1060
+ "notifyThreadUpdate",
1061
+ "notifyInboxItemUpdate",
1062
+ "notifyThreadTurnUpdate",
1063
+ "notifyThreadTurnStep",
1064
+ "notifyOrgUpdate",
1065
+ "notifyCostRecorded",
1066
+ "notifyEvalRunUpdate",
1067
+ "notifyWorkspaceAccessRevoked",
1068
+ "invalidateSubscription",
1069
+ ] : "arn:aws:appsync:${var.region}:${var.account_id}:apis/${var.appsync_api_id}/types/Mutation/fields/${field}"
1070
+ ]
1071
+ },
917
1072
  # (was inline policy "cloudwatch-logs-read")
918
1073
  {
919
1074
  Effect = "Allow"
@@ -957,16 +1112,41 @@ locals {
957
1112
  Resource = "*"
958
1113
  },
959
1114
  ])
1115
+
1116
+ api_grouped_policy_documents = {
1117
+ data_plane = jsonencode({
1118
+ Version = "2012-10-17"
1119
+ Statement = local.api_data_plane_statements
1120
+ })
1121
+ orchestration = jsonencode({
1122
+ Version = "2012-10-17"
1123
+ Statement = local.api_orchestration_statements
1124
+ })
1125
+ ai = jsonencode({
1126
+ Version = "2012-10-17"
1127
+ Statement = local.api_ai_statements
1128
+ })
1129
+ observability = jsonencode({
1130
+ Version = "2012-10-17"
1131
+ Statement = local.api_observability_statements
1132
+ })
1133
+ }
1134
+ }
1135
+
1136
+ check "api_grouped_managed_policy_sizes" {
1137
+ assert {
1138
+ condition = alltrue([
1139
+ for document in values(local.api_grouped_policy_documents) : length(document) <= 6144
1140
+ ])
1141
+ error_message = "Each grouped API managed-policy document must remain within AWS IAM's 6,144-character limit."
1142
+ }
960
1143
  }
961
1144
 
962
1145
  resource "aws_iam_policy" "api_data_plane" {
963
1146
  name = "thinkwork-${var.stage}-api-data-plane"
964
1147
  description = "Grouped data-plane grants (RDS Data API, Secrets Manager, S3, DynamoDB, Cognito, SSM, KMS-via-SSM, SQS) for the shared api Lambda role"
965
1148
 
966
- policy = jsonencode({
967
- Version = "2012-10-17"
968
- Statement = local.api_data_plane_statements
969
- })
1149
+ policy = local.api_grouped_policy_documents.data_plane
970
1150
  }
971
1151
 
972
1152
  resource "aws_iam_role_policy_attachment" "api_data_plane" {
@@ -978,10 +1158,7 @@ resource "aws_iam_policy" "api_orchestration" {
978
1158
  name = "thinkwork-${var.stage}-api-orchestration"
979
1159
  description = "Grouped orchestration grants (Lambda invoke, EventBridge Scheduler, Step Functions, SES) for the shared api Lambda role"
980
1160
 
981
- policy = jsonencode({
982
- Version = "2012-10-17"
983
- Statement = local.api_orchestration_statements
984
- })
1161
+ policy = local.api_grouped_policy_documents.orchestration
985
1162
  }
986
1163
 
987
1164
  resource "aws_iam_role_policy_attachment" "api_orchestration" {
@@ -993,10 +1170,7 @@ resource "aws_iam_policy" "api_ai" {
993
1170
  name = "thinkwork-${var.stage}-api-ai"
994
1171
  description = "Grouped AI grants (Bedrock invoke, Knowledge Bases, AgentCore memory/eval/code-interpreter) for the shared api Lambda role"
995
1172
 
996
- policy = jsonencode({
997
- Version = "2012-10-17"
998
- Statement = local.api_ai_statements
999
- })
1173
+ policy = local.api_grouped_policy_documents.ai
1000
1174
  }
1001
1175
 
1002
1176
  resource "aws_iam_role_policy_attachment" "api_ai" {
@@ -1008,10 +1182,7 @@ resource "aws_iam_policy" "api_observability" {
1008
1182
  name = "thinkwork-${var.stage}-api-observability"
1009
1183
  description = "Grouped observability grants (CloudWatch Logs reads, ECS/ALB health reads) for the shared api Lambda role"
1010
1184
 
1011
- policy = jsonencode({
1012
- Version = "2012-10-17"
1013
- Statement = local.api_observability_statements
1014
- })
1185
+ policy = local.api_grouped_policy_documents.observability
1015
1186
  }
1016
1187
 
1017
1188
  resource "aws_iam_role_policy_attachment" "api_observability" {
@@ -20,6 +20,17 @@ data "aws_caller_identity" "current" {}
20
20
 
21
21
  locals {
22
22
  okf_efs_vpc_enabled = length(var.okf_efs_subnet_ids) > 0 && length(var.okf_efs_security_group_ids) > 0
23
+ # Handlers that open direct Postgres connections to registered analyst data
24
+ # sources. VPC-attaching them (analyst_egress_subnet_ids) gives their egress
25
+ # the NAT gateway's stable EIP, which external databases behind an IP
26
+ # allowlist can admit — a non-VPC Lambda egresses from the shared AWS pool
27
+ # and can never satisfy such an allowlist.
28
+ analyst_vpc_enabled = length(var.analyst_egress_subnet_ids) > 0 && length(var.analyst_egress_security_group_ids) > 0
29
+ analyst_vpc_handlers = toset([
30
+ "graphql-http",
31
+ "analyst-query-broker",
32
+ "analyst-connection-reconciler",
33
+ ])
23
34
  # Constructed rather than read from aws_apigatewayv2_stage.default.invoke_url
24
35
  # (the two are identical for an HTTP API's $default stage). Reading it off
25
36
  # the stage made every Lambda's env depend on the stage, which forbids the
@@ -62,6 +73,14 @@ resource "aws_apigatewayv2_stage" "default" {
62
73
  throttling_burst_limit = 20
63
74
  }
64
75
 
76
+ # Session revocation is authenticated and independently rate-limited in the
77
+ # handler per principal. This edge throttle also bounds aggregate abuse.
78
+ route_settings {
79
+ route_key = "POST /api/auth/revoke"
80
+ throttling_rate_limit = 5
81
+ throttling_burst_limit = 10
82
+ }
83
+
65
84
  # UpdateStage 404s ("Unable to find Route by key … within the provided
66
85
  # RouteSettings") if the stage update races the route creation — Terraform
67
86
  # sees no reference edge between route_settings.route_key and the route
@@ -245,3 +264,19 @@ resource "aws_lambda_permission" "placeholder_apigw" {
245
264
  principal = "apigateway.amazonaws.com"
246
265
  source_arn = "${aws_apigatewayv2_api.main.execution_arn}/*/*"
247
266
  }
267
+ resource "terraform_data" "auth_migration_recovery_deadline_guard" {
268
+ input = {
269
+ phase = var.auth_retirement_phase
270
+ deadline = var.auth_migration_recovery_deadline
271
+ }
272
+
273
+ lifecycle {
274
+ precondition {
275
+ condition = var.auth_retirement_phase != "coexistence" || (
276
+ trimspace(var.auth_migration_recovery_deadline) != "" &&
277
+ can(timecmp(var.auth_migration_recovery_deadline, var.auth_migration_recovery_deadline))
278
+ )
279
+ error_message = "auth_migration_recovery_deadline must be an RFC3339 timestamp when auth_retirement_phase is coexistence."
280
+ }
281
+ }
282
+ }
@@ -1,8 +1,185 @@
1
1
  locals {
2
- mcp_oauth_api_base_url = "https://${aws_apigatewayv2_api.main.id}.execute-api.${var.region}.amazonaws.com"
3
- mcp_oauth_cognito_base_url = var.cognito_auth_domain != "" ? "https://${var.cognito_auth_domain}.auth.${var.region}.amazoncognito.com" : ""
4
- mcp_oauth_identity_providers = var.google_oauth_client_id != "" ? ["Google", "COGNITO"] : ["COGNITO"]
5
- mcp_oauth_logo_path = "${path.module}/../../../../apps/web/public/logo.png"
2
+ mcp_oauth_api_base_url = "https://${aws_apigatewayv2_api.main.id}.execute-api.${var.region}.amazonaws.com"
3
+ mcp_oauth_cognito_base_url = var.cognito_auth_domain != "" ? "https://${var.cognito_auth_domain}.auth.${var.region}.amazoncognito.com" : ""
4
+ mcp_oauth_identity_providers = var.google_oauth_client_id != "" ? ["Google", "COGNITO"] : ["COGNITO"]
5
+ mcp_oauth_logo_path = "${path.module}/../../../../apps/web/public/logo.png"
6
+ agentcore_turn_assertion_issuer = "${local.mcp_oauth_api_base_url}/agentcore"
7
+ agentcore_harness_audience = "urn:thinkwork:${var.stage}:agentcore:harness"
8
+ agentcore_gateway_audience = "urn:thinkwork:${var.stage}:agentcore:gateway"
9
+ agentcore_turn_assertion_keys = {
10
+ for version, key in aws_kms_key.agentcore_turn_assertion : version => {
11
+ key_id = key.arn
12
+ # A version label alone is not globally unique: destroying and
13
+ # reprovisioning v1 creates different RSA material while downstream JWT
14
+ # validators may still cache the old public key under the same kid.
15
+ # Bind the kid to the immutable KMS key id so a reprovision is observed
16
+ # as a real key rotation instead of an invalid-signature outage.
17
+ kid = "thinkwork-${var.stage}-agentcore-turn-${version}-${substr(key.key_id, 0, 8)}"
18
+ }
19
+ }
20
+ agentcore_turn_assertion_active_key = try(
21
+ local.agentcore_turn_assertion_keys[var.agentcore_turn_assertion_active_key_version],
22
+ { key_id = "", kid = "" },
23
+ )
24
+ }
25
+
26
+ # THINK-316 U1: short-lived CUSTOM_JWT assertions are signed by KMS so private
27
+ # key material never enters Lambda configuration or process memory. The key
28
+ # policy is the capability boundary: the dedicated mint role can Sign, while
29
+ # the shared mcp-oauth role can only fetch the public key for JWKS publication.
30
+ resource "aws_iam_role" "turn_assertion_mint" {
31
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
32
+ name = "thinkwork-${var.stage}-turn-assertion-mint-role"
33
+
34
+ assume_role_policy = jsonencode({
35
+ Version = "2012-10-17"
36
+ Statement = [{
37
+ Effect = "Allow"
38
+ Principal = { Service = "lambda.amazonaws.com" }
39
+ Action = "sts:AssumeRole"
40
+ }]
41
+ })
42
+ }
43
+
44
+ resource "aws_iam_role_policy_attachment" "turn_assertion_mint_basic" {
45
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
46
+ role = aws_iam_role.turn_assertion_mint[0].name
47
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
48
+ }
49
+
50
+ # The synthetic OAuth provider and boundary target are public proof surfaces
51
+ # but need no AWS data-plane permissions. A sibling logs-only role prevents a
52
+ # parser defect from inheriting the shared API role or its platform secrets.
53
+ resource "aws_iam_role" "agentcore_proof_boundary" {
54
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
55
+ name = "thinkwork-${var.stage}-agentcore-proof-boundary-role"
56
+
57
+ assume_role_policy = jsonencode({
58
+ Version = "2012-10-17"
59
+ Statement = [{
60
+ Effect = "Allow"
61
+ Principal = { Service = "lambda.amazonaws.com" }
62
+ Action = "sts:AssumeRole"
63
+ }]
64
+ })
65
+ }
66
+
67
+ resource "aws_iam_role_policy_attachment" "agentcore_proof_boundary_basic" {
68
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
69
+ role = aws_iam_role.agentcore_proof_boundary[0].name
70
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
71
+ }
72
+
73
+ # The token-exchange provider is the only proof boundary allowed to mint the
74
+ # derived Gateway assertion. Keep that capability off the public target role.
75
+ resource "aws_iam_role" "agentcore_proof_provider" {
76
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
77
+ name = "thinkwork-${var.stage}-agentcore-proof-provider-role"
78
+
79
+ assume_role_policy = jsonencode({
80
+ Version = "2012-10-17"
81
+ Statement = [{
82
+ Effect = "Allow"
83
+ Principal = { Service = "lambda.amazonaws.com" }
84
+ Action = "sts:AssumeRole"
85
+ }]
86
+ })
87
+ }
88
+
89
+ resource "aws_iam_role_policy_attachment" "agentcore_proof_provider_basic" {
90
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
91
+ role = aws_iam_role.agentcore_proof_provider[0].name
92
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
93
+ }
94
+
95
+ resource "aws_iam_role_policy" "agentcore_proof_provider_kms" {
96
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
97
+ name = "thinkwork-${var.stage}-agentcore-proof-provider-key"
98
+ role = aws_iam_role.agentcore_proof_provider[0].id
99
+
100
+ policy = jsonencode({
101
+ Version = "2012-10-17"
102
+ Statement = [{
103
+ Effect = "Allow"
104
+ Action = ["kms:Sign"]
105
+ Resource = [local.agentcore_turn_assertion_active_key.key_id]
106
+ Condition = {
107
+ StringEquals = {
108
+ "kms:SigningAlgorithm" = "RSASSA_PKCS1_V1_5_SHA_256"
109
+ }
110
+ }
111
+ }]
112
+ })
113
+ }
114
+
115
+ resource "aws_kms_key" "agentcore_turn_assertion" {
116
+ for_each = var.enable_agentcore_multiplayer_proof ? toset(var.agentcore_turn_assertion_key_versions) : toset([])
117
+
118
+ description = "ThinkWork ${var.stage} AgentCore per-turn assertion signing ${each.key}"
119
+ key_usage = "SIGN_VERIFY"
120
+ customer_master_key_spec = "RSA_2048"
121
+ deletion_window_in_days = 7
122
+
123
+ policy = jsonencode({
124
+ Version = "2012-10-17"
125
+ Statement = [
126
+ {
127
+ Sid = "AccountAdministration"
128
+ Effect = "Allow"
129
+ Principal = { AWS = "arn:aws:iam::${var.account_id}:root" }
130
+ Action = "kms:*"
131
+ Resource = "*"
132
+ },
133
+ ]
134
+ })
135
+
136
+ tags = {
137
+ Name = "thinkwork-${var.stage}-agentcore-turn-assertion-${each.key}"
138
+ KeyVersion = each.key
139
+ }
140
+ }
141
+
142
+ resource "aws_kms_alias" "agentcore_turn_assertion" {
143
+ for_each = aws_kms_key.agentcore_turn_assertion
144
+ name = "alias/thinkwork-${var.stage}-agentcore-turn-assertion-${each.key}"
145
+ target_key_id = each.value.key_id
146
+ }
147
+
148
+ # The mint role can sign with exactly the active key. The public discovery
149
+ # handler can read all currently published public keys, but can never sign.
150
+ resource "aws_iam_role_policy" "turn_assertion_mint_kms" {
151
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
152
+ name = "thinkwork-${var.stage}-turn-assertion-active-key"
153
+ role = aws_iam_role.turn_assertion_mint[0].id
154
+
155
+ policy = jsonencode({
156
+ Version = "2012-10-17"
157
+ Statement = [{
158
+ Effect = "Allow"
159
+ Action = ["kms:Sign"]
160
+ Resource = [local.agentcore_turn_assertion_active_key.key_id]
161
+ Condition = {
162
+ StringEquals = {
163
+ "kms:SigningAlgorithm" = "RSASSA_PKCS1_V1_5_SHA_256"
164
+ }
165
+ }
166
+ }]
167
+ })
168
+ }
169
+
170
+ resource "aws_iam_role_policy" "turn_assertion_jwks_kms" {
171
+ count = var.enable_agentcore_multiplayer_proof ? 1 : 0
172
+ name = "thinkwork-${var.stage}-turn-assertion-published-keys"
173
+ role = aws_iam_role.lambda.id
174
+
175
+ policy = jsonencode({
176
+ Version = "2012-10-17"
177
+ Statement = [{
178
+ Effect = "Allow"
179
+ Action = ["kms:GetPublicKey"]
180
+ Resource = [for key in values(local.agentcore_turn_assertion_keys) : key.key_id]
181
+ }]
182
+ })
6
183
  }
7
184
 
8
185
  resource "aws_dynamodb_table" "mcp_oauth_revocations" {
@@ -0,0 +1,92 @@
1
+ # THINK-193 U8 — external-memory compounding observability.
2
+ #
3
+ # Alarms here fire from metrics that EXIST without any log-group management:
4
+ # - AWS/Lambda Errors for the memory-stage worker and the retraction drainer
5
+ # (handler crashes, including ones that never reach a log line);
6
+ # - the wiki-compile DLQ depth (the only memory-path Lambda with an
7
+ # on_failure destination).
8
+ #
9
+ # DELIBERATELY NOT HERE — log-metric filters over the drainer's structured
10
+ # summary line and the worker's "status=failed" line. They require the
11
+ # /aws/lambda/<function> log groups, which only exist after a function's first
12
+ # invocation: PutMetricFilter failed with ResourceNotFoundException on customer
13
+ # stages taking a fresh release (dev passed only because its groups already
14
+ # existed). Managing the groups in Terraform instead needs a coordinated state
15
+ # import on every stage that already has the implicit group, so it is a
16
+ # follow-up. Until then, drainer errors / dead-letters and stage failures are
17
+ # observable through the structured logs and
18
+ # packages/api/scripts/external-memory-readiness.ts (which reports due,
19
+ # failed, and dead-lettered attempts directly from the ledger).
20
+ #
21
+ # Also runbook-only (no metric exists): source checkpoint age, graph-to-wiki
22
+ # lag, evidence lag, deferred identity count/age.
23
+ #
24
+ # alarm_actions are empty: the only SNS topic in this module
25
+ # (aws_sns_topic.cost_alerts) is the cost-alert channel — wiring memory alarms
26
+ # into it would misroute pages. Operators subscribe a topic when the rollout
27
+ # needs paging.
28
+
29
+
30
+ resource "aws_cloudwatch_metric_alarm" "memory_stage_worker_lambda_errors" {
31
+ count = local.deploy_lambda_handlers ? 1 : 0
32
+
33
+ alarm_name = "thinkwork-${var.stage}-memory-stage-worker-lambda-errors"
34
+ alarm_description = "memory-stage-worker Lambda crashed (unhandled error, retries pinned to 0, no failure destination) — the parked workflow run will need a redrive."
35
+ namespace = "AWS/Lambda"
36
+ metric_name = "Errors"
37
+ statistic = "Sum"
38
+ period = 300
39
+ evaluation_periods = 1
40
+ threshold = 1
41
+ comparison_operator = "GreaterThanOrEqualToThreshold"
42
+ treat_missing_data = "notBreaching"
43
+ alarm_actions = []
44
+
45
+ dimensions = {
46
+ FunctionName = aws_lambda_function.handler["memory-stage-worker"].function_name
47
+ }
48
+ }
49
+
50
+ resource "aws_cloudwatch_metric_alarm" "memory_retraction_drainer_lambda_errors" {
51
+ count = local.deploy_lambda_handlers ? 1 : 0
52
+
53
+ alarm_name = "thinkwork-${var.stage}-memory-retraction-drainer-lambda-errors"
54
+ alarm_description = "memory-retraction-drainer Lambda crashed before emitting its summary line — due retraction work is stalled until the next scheduled tick succeeds."
55
+ namespace = "AWS/Lambda"
56
+ metric_name = "Errors"
57
+ statistic = "Sum"
58
+ period = 300
59
+ evaluation_periods = 1
60
+ threshold = 1
61
+ comparison_operator = "GreaterThanOrEqualToThreshold"
62
+ treat_missing_data = "notBreaching"
63
+ alarm_actions = []
64
+
65
+ dimensions = {
66
+ FunctionName = aws_lambda_function.handler["memory-retraction-drainer"].function_name
67
+ }
68
+ }
69
+
70
+ # ---------------------------------------------------------------------------
71
+ # wiki-compile DLQ depth (graph → wiki handoff failures)
72
+ # ---------------------------------------------------------------------------
73
+
74
+ resource "aws_cloudwatch_metric_alarm" "wiki_compile_dlq_depth" {
75
+ count = local.deploy_lambda_handlers ? 1 : 0
76
+
77
+ alarm_name = "thinkwork-${var.stage}-wiki-compile-dlq-depth"
78
+ alarm_description = "wiki-compile DLQ has messages — a compile invocation crashed before the job ledger recorded an outcome; canonical pages are stale until replayed."
79
+ namespace = "AWS/SQS"
80
+ metric_name = "ApproximateNumberOfMessagesVisible"
81
+ statistic = "Maximum"
82
+ period = 60
83
+ evaluation_periods = 1
84
+ threshold = 1
85
+ comparison_operator = "GreaterThanOrEqualToThreshold"
86
+ treat_missing_data = "notBreaching"
87
+ alarm_actions = []
88
+
89
+ dimensions = {
90
+ QueueName = aws_sqs_queue.wiki_compile_dlq[0].name
91
+ }
92
+ }