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
@@ -0,0 +1,393 @@
1
+ # Capability Broker (THINK-280 U3) — action-time policy, replay-safe PoP
2
+ # sessions, and evidence for the governed capability runtime.
3
+ #
4
+ # SHIP-INERT: gated by var.enabled (default false). Nothing here is created
5
+ # until U4 flips it on after the live negative-egress proof. The Lambda code
6
+ # itself also fails closed — its default authorization loader denies every
7
+ # call — so even an accidental enable dispatches no provider operation.
8
+ #
9
+ # THINK-144: this module owns a DEDICATED execute-api interface VPC endpoint
10
+ # with private DNS DISABLED. It is never added to the app VPC's shared
11
+ # okf_wiki_interface_endpoint_services list — that list's private DNS would
12
+ # capture every *.execute-api call in the VPC and 403 the stack's public HTTP
13
+ # APIs. The session bootstrap targets this endpoint's own DNS name instead.
14
+
15
+ locals {
16
+ enabled = var.enabled
17
+ count_enabled = var.enabled ? 1 : 0
18
+
19
+ name_prefix = "thinkwork-${var.stage}-capability-broker"
20
+ session_table_name = "thinkwork-${var.stage}-capability-broker-sessions"
21
+ broker_audience = "thinkwork-${var.stage}-capability-broker"
22
+ use_local_zips = var.lambda_zips_dir != ""
23
+ use_remote_artifacts = trimspace(var.lambda_artifact_bucket) != ""
24
+ broker_zip_local = "${var.lambda_zips_dir}/capability-broker.zip"
25
+ broker_artifact_s3key = "${var.lambda_artifact_prefix}/capability-broker.zip"
26
+ }
27
+
28
+ # --- Replay-safe session state (DynamoDB, conditional writes + TTL) ----------
29
+
30
+ resource "aws_dynamodb_table" "sessions" {
31
+ count = local.count_enabled
32
+
33
+ name = local.session_table_name
34
+ billing_mode = "PAY_PER_REQUEST"
35
+ hash_key = "pk"
36
+ range_key = "sk"
37
+
38
+ attribute {
39
+ name = "pk"
40
+ type = "S"
41
+ }
42
+ attribute {
43
+ name = "sk"
44
+ type = "S"
45
+ }
46
+
47
+ # Session + nonce records self-expire at session TTL (≤15 min). Epoch seconds.
48
+ ttl {
49
+ attribute_name = "ttl"
50
+ enabled = true
51
+ }
52
+
53
+ point_in_time_recovery {
54
+ enabled = true
55
+ }
56
+
57
+ tags = {
58
+ Name = local.session_table_name
59
+ Purpose = "capability-broker-sessions"
60
+ }
61
+ }
62
+
63
+ # --- Broker Lambda execution role -------------------------------------------
64
+
65
+ data "aws_iam_policy_document" "broker_assume" {
66
+ statement {
67
+ actions = ["sts:AssumeRole"]
68
+ principals {
69
+ type = "Service"
70
+ identifiers = ["lambda.amazonaws.com"]
71
+ }
72
+ }
73
+ }
74
+
75
+ resource "aws_iam_role" "broker" {
76
+ count = local.count_enabled
77
+ name = "${local.name_prefix}-role"
78
+ assume_role_policy = data.aws_iam_policy_document.broker_assume.json
79
+ }
80
+
81
+ data "aws_iam_policy_document" "broker_policy" {
82
+ count = local.count_enabled
83
+
84
+ # Session state: conditional reads/writes only, scoped to the one table.
85
+ statement {
86
+ sid = "SessionState"
87
+ actions = [
88
+ "dynamodb:GetItem",
89
+ "dynamodb:PutItem",
90
+ "dynamodb:UpdateItem",
91
+ "dynamodb:DeleteItem",
92
+ "dynamodb:Query",
93
+ ]
94
+ resources = [aws_dynamodb_table.sessions[0].arn]
95
+ }
96
+
97
+ # Evidence: append capability_broker_calls rows via the RDS Data API on the
98
+ # existing Aurora cluster (same credential-rotation surface as graphql-http).
99
+ dynamic "statement" {
100
+ for_each = var.db_cluster_arn != "" ? [1] : []
101
+ content {
102
+ sid = "EvidenceDataApi"
103
+ actions = ["rds-data:ExecuteStatement", "rds-data:BatchExecuteStatement"]
104
+ resources = [var.db_cluster_arn]
105
+ }
106
+ }
107
+
108
+ dynamic "statement" {
109
+ for_each = var.db_secret_arn != "" ? [1] : []
110
+ content {
111
+ sid = "EvidenceDbSecret"
112
+ actions = ["secretsmanager:GetSecretValue"]
113
+ resources = [var.db_secret_arn]
114
+ }
115
+ }
116
+
117
+ # VPC ENI management for the in-VPC Lambda.
118
+ statement {
119
+ sid = "VpcEni"
120
+ actions = [
121
+ "ec2:CreateNetworkInterface",
122
+ "ec2:DescribeNetworkInterfaces",
123
+ "ec2:DeleteNetworkInterface",
124
+ "ec2:AssignPrivateIpAddresses",
125
+ "ec2:UnassignPrivateIpAddresses",
126
+ ]
127
+ resources = ["*"]
128
+ }
129
+
130
+ statement {
131
+ sid = "Logs"
132
+ actions = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
133
+ resources = ["arn:aws:logs:${var.region}:${var.account_id}:log-group:/aws/lambda/${local.name_prefix}*"]
134
+ }
135
+
136
+ statement {
137
+ sid = "Xray"
138
+ actions = ["xray:PutTraceSegments", "xray:PutTelemetryRecords"]
139
+ resources = ["*"]
140
+ }
141
+ }
142
+
143
+ resource "aws_iam_role_policy" "broker" {
144
+ count = local.count_enabled
145
+ name = "${local.name_prefix}-policy"
146
+ role = aws_iam_role.broker[0].id
147
+ policy = data.aws_iam_policy_document.broker_policy[0].json
148
+ }
149
+
150
+ # --- Broker Lambda security group (egress only) ------------------------------
151
+
152
+ resource "aws_security_group" "broker_lambda" {
153
+ count = local.count_enabled
154
+ name = "${local.name_prefix}-lambda"
155
+ description = "Capability broker Lambda egress"
156
+ vpc_id = var.vpc_id
157
+
158
+ egress {
159
+ from_port = 0
160
+ to_port = 0
161
+ protocol = "-1"
162
+ cidr_blocks = ["0.0.0.0/0"]
163
+ description = "Egress to Aurora Data API, DynamoDB, Secrets Manager, provider endpoints"
164
+ }
165
+
166
+ tags = { Name = "${local.name_prefix}-lambda" }
167
+ }
168
+
169
+ resource "aws_cloudwatch_log_group" "broker" {
170
+ count = local.count_enabled
171
+ name = "/aws/lambda/${local.name_prefix}"
172
+ retention_in_days = var.log_retention_days
173
+ }
174
+
175
+ resource "aws_lambda_function" "broker" {
176
+ count = local.count_enabled
177
+ function_name = local.name_prefix
178
+ role = aws_iam_role.broker[0].arn
179
+ runtime = var.lambda_runtime
180
+ handler = "index.handler"
181
+ timeout = 30
182
+ memory_size = 512
183
+
184
+ filename = local.use_local_zips ? local.broker_zip_local : null
185
+ source_code_hash = local.use_local_zips ? filebase64sha256(local.broker_zip_local) : null
186
+ s3_bucket = local.use_remote_artifacts ? var.lambda_artifact_bucket : null
187
+ s3_key = local.use_remote_artifacts ? local.broker_artifact_s3key : null
188
+
189
+ vpc_config {
190
+ subnet_ids = var.subnet_ids
191
+ security_group_ids = [aws_security_group.broker_lambda[0].id]
192
+ }
193
+
194
+ environment {
195
+ variables = {
196
+ CAPABILITY_BROKER_SESSION_TABLE = aws_dynamodb_table.sessions[0].name
197
+ CAPABILITY_BROKER_AUDIENCE = local.broker_audience
198
+ # getDb() (@thinkwork/database-pg) MUST get DATABASE_URL, not just
199
+ # DATABASE_SECRET_ARN: the secret-only path returns an async Proxy whose
200
+ # `db.select()` resolves to a Promise, which breaks Drizzle's fluent
201
+ # `.select().from().where()` chaining (`.from is not a function`) — the
202
+ # cause of the loader's `authorization reload failed`. DATABASE_URL takes
203
+ # the synchronous pool path, matching every api handler's common_env.
204
+ DATABASE_URL = "postgresql://${var.db_username}:${urlencode(var.db_password)}@${var.db_cluster_endpoint}:5432/${var.database_name}?sslmode=no-verify"
205
+ DATABASE_SECRET_ARN = var.db_secret_arn
206
+ DATABASE_HOST = var.db_cluster_endpoint
207
+ }
208
+ }
209
+
210
+ tracing_config {
211
+ mode = "Active"
212
+ }
213
+
214
+ depends_on = [aws_cloudwatch_log_group.broker]
215
+ }
216
+
217
+ # --- Private REST API Gateway fronting the broker ----------------------------
218
+ #
219
+ # PRIVATE endpoint: reachable ONLY through the dedicated execute-api VPCE below.
220
+ # The resource policy denies anything not arriving via that endpoint.
221
+
222
+ resource "aws_api_gateway_rest_api" "broker" {
223
+ count = local.count_enabled
224
+ name = local.name_prefix
225
+
226
+ endpoint_configuration {
227
+ types = ["PRIVATE"]
228
+ vpc_endpoint_ids = [aws_vpc_endpoint.broker_execute_api[0].id]
229
+ }
230
+
231
+ # Only traffic arriving through our own VPCE is allowed; everything else 403s.
232
+ policy = jsonencode({
233
+ Version = "2012-10-17"
234
+ Statement = [
235
+ {
236
+ Effect = "Deny"
237
+ Principal = "*"
238
+ Action = "execute-api:Invoke"
239
+ Resource = "arn:aws:execute-api:${var.region}:${var.account_id}:*/*/*/*"
240
+ Condition = {
241
+ StringNotEquals = { "aws:SourceVpce" = aws_vpc_endpoint.broker_execute_api[0].id }
242
+ }
243
+ },
244
+ {
245
+ Effect = "Allow"
246
+ Principal = "*"
247
+ Action = "execute-api:Invoke"
248
+ Resource = "arn:aws:execute-api:${var.region}:${var.account_id}:*/*/*/*"
249
+ },
250
+ ]
251
+ })
252
+ }
253
+
254
+ resource "aws_api_gateway_resource" "broker_proxy" {
255
+ count = local.count_enabled
256
+ rest_api_id = aws_api_gateway_rest_api.broker[0].id
257
+ parent_id = aws_api_gateway_rest_api.broker[0].root_resource_id
258
+ path_part = "{proxy+}"
259
+ }
260
+
261
+ resource "aws_api_gateway_method" "broker_any" {
262
+ count = local.count_enabled
263
+ rest_api_id = aws_api_gateway_rest_api.broker[0].id
264
+ resource_id = aws_api_gateway_resource.broker_proxy[0].id
265
+ http_method = "ANY"
266
+ authorization = "NONE"
267
+ }
268
+
269
+ resource "aws_api_gateway_integration" "broker" {
270
+ count = local.count_enabled
271
+ rest_api_id = aws_api_gateway_rest_api.broker[0].id
272
+ resource_id = aws_api_gateway_resource.broker_proxy[0].id
273
+ http_method = aws_api_gateway_method.broker_any[0].http_method
274
+ integration_http_method = "POST"
275
+ type = "AWS_PROXY"
276
+ uri = aws_lambda_function.broker[0].invoke_arn
277
+ }
278
+
279
+ resource "aws_lambda_permission" "broker_apigw" {
280
+ count = local.count_enabled
281
+ statement_id = "AllowBrokerPrivateApiInvoke"
282
+ action = "lambda:InvokeFunction"
283
+ function_name = aws_lambda_function.broker[0].function_name
284
+ principal = "apigateway.amazonaws.com"
285
+ source_arn = "${aws_api_gateway_rest_api.broker[0].execution_arn}/*/*/*"
286
+ }
287
+
288
+ resource "aws_api_gateway_deployment" "broker" {
289
+ count = local.count_enabled
290
+ rest_api_id = aws_api_gateway_rest_api.broker[0].id
291
+
292
+ triggers = {
293
+ redeploy = sha1(jsonencode([
294
+ aws_api_gateway_resource.broker_proxy[0].id,
295
+ aws_api_gateway_method.broker_any[0].id,
296
+ aws_api_gateway_integration.broker[0].id,
297
+ aws_api_gateway_rest_api.broker[0].policy,
298
+ ]))
299
+ }
300
+
301
+ lifecycle {
302
+ create_before_destroy = true
303
+ }
304
+ depends_on = [aws_api_gateway_integration.broker]
305
+ }
306
+
307
+ resource "aws_api_gateway_stage" "broker" {
308
+ count = local.count_enabled
309
+ rest_api_id = aws_api_gateway_rest_api.broker[0].id
310
+ deployment_id = aws_api_gateway_deployment.broker[0].id
311
+ stage_name = var.stage
312
+ }
313
+
314
+ # --- Dedicated execute-api VPC endpoint (PRIVATE DNS DISABLED — THINK-144) ----
315
+
316
+ resource "aws_security_group" "broker_vpce" {
317
+ count = local.count_enabled
318
+ name = "${local.name_prefix}-vpce"
319
+ description = "Capability broker execute-api VPCE (broker Lambda + capability-private interpreter subnet only)"
320
+ vpc_id = var.vpc_id
321
+
322
+ ingress {
323
+ from_port = 443
324
+ to_port = 443
325
+ protocol = "tcp"
326
+ cidr_blocks = [var.vpc_cidr_block]
327
+ description = "HTTPS from in-VPC PoP callers (broker Lambda self-calls + U4 capability-private interpreter)"
328
+ }
329
+
330
+ tags = { Name = "${local.name_prefix}-vpce" }
331
+ }
332
+
333
+ resource "aws_vpc_endpoint" "broker_execute_api" {
334
+ count = local.count_enabled
335
+
336
+ vpc_id = var.vpc_id
337
+ service_name = "com.amazonaws.${var.region}.execute-api"
338
+ vpc_endpoint_type = "Interface"
339
+ subnet_ids = var.subnet_ids
340
+ security_group_ids = [aws_security_group.broker_vpce[0].id]
341
+
342
+ # CRITICAL (THINK-144): private DNS OFF. Callers target the endpoint-specific
343
+ # DNS name (surfaced as an output), never the regional execute-api domain, so
344
+ # this endpoint cannot capture the stack's public HTTP API traffic.
345
+ private_dns_enabled = false
346
+
347
+ tags = {
348
+ Name = "${local.name_prefix}-execute-api-vpce"
349
+ Purpose = "capability-broker-private-ingress"
350
+ }
351
+ }
352
+
353
+ # --- Capability-private interpreter SG (no NAT; broker VPCE only) — U4 --------
354
+ #
355
+ # The U4 capability-private VPC-mode Code Interpreter attaches to var.subnet_ids
356
+ # (the no-NAT private egress subnets) under THIS security group. Its ONLY egress
357
+ # is 443 to the broker execute-api VPCE SG plus DNS to the VPC resolver — there
358
+ # is no NAT and no 0.0.0.0/0 route, so direct GitHub / Secrets Manager / internet
359
+ # access does not exist (AE5 negative-egress boundary). The broker_vpce ingress
360
+ # above already admits 443 from the VPC CIDR, which covers these subnets.
361
+ resource "aws_security_group" "capability_private_interpreter" {
362
+ count = local.count_enabled
363
+ name = "${local.name_prefix}-cappriv-interpreter"
364
+ description = "Capability-private Code Interpreter egress: broker execute-api VPCE + DNS only"
365
+ vpc_id = var.vpc_id
366
+
367
+ # execute-api VPCE only (reach the private broker API).
368
+ egress {
369
+ from_port = 443
370
+ to_port = 443
371
+ protocol = "tcp"
372
+ security_groups = [aws_security_group.broker_vpce[0].id]
373
+ description = "HTTPS to the broker execute-api VPCE"
374
+ }
375
+
376
+ # DNS to the in-VPC resolver so the endpoint-specific VPCE hostname resolves.
377
+ egress {
378
+ from_port = 53
379
+ to_port = 53
380
+ protocol = "udp"
381
+ cidr_blocks = [var.vpc_cidr_block]
382
+ description = "DNS (UDP) to the VPC resolver"
383
+ }
384
+ egress {
385
+ from_port = 53
386
+ to_port = 53
387
+ protocol = "tcp"
388
+ cidr_blocks = [var.vpc_cidr_block]
389
+ description = "DNS (TCP) to the VPC resolver"
390
+ }
391
+
392
+ tags = { Name = "${local.name_prefix}-cappriv-interpreter" }
393
+ }
@@ -0,0 +1,54 @@
1
+ # Consumed by the API-side broker-session opener to build the sandbox
2
+ # SessionBootstrap. All null when the module is disabled (ship-inert).
3
+
4
+ output "enabled" {
5
+ value = local.enabled
6
+ }
7
+
8
+ output "session_table_name" {
9
+ description = "DynamoDB session table (CAPABILITY_BROKER_SESSION_TABLE)."
10
+ value = local.enabled ? aws_dynamodb_table.sessions[0].name : ""
11
+ }
12
+
13
+ output "session_table_arn" {
14
+ description = "ARN of the broker session table — granted to the lambda-api role so routine-exec-git can mint/advance PoP sessions. Empty when disabled."
15
+ value = local.enabled ? aws_dynamodb_table.sessions[0].arn : ""
16
+ }
17
+
18
+ output "broker_audience" {
19
+ description = "Audience the PoP session signatures are bound to (CAPABILITY_BROKER_AUDIENCE)."
20
+ value = local.enabled ? local.broker_audience : ""
21
+ }
22
+
23
+ output "private_api_id" {
24
+ description = "Private REST API id the sandbox targets through the VPCE (CAPABILITY_BROKER_API_ID)."
25
+ value = local.enabled ? aws_api_gateway_rest_api.broker[0].id : ""
26
+ }
27
+
28
+ output "execute_api_vpce_id" {
29
+ value = local.enabled ? aws_vpc_endpoint.broker_execute_api[0].id : ""
30
+ }
31
+
32
+ output "execute_api_vpce_dns_name" {
33
+ description = "Endpoint-specific execute-api DNS name (private DNS is OFF). The session bootstrap uses this exact host — CAPABILITY_BROKER_VPCE_DNS."
34
+ value = local.enabled ? tolist(aws_vpc_endpoint.broker_execute_api[0].dns_entry)[0].dns_name : ""
35
+ }
36
+
37
+ output "broker_function_name" {
38
+ value = local.enabled ? aws_lambda_function.broker[0].function_name : ""
39
+ }
40
+
41
+ # --- U4: capability-private interpreter VPC placement ------------------------
42
+ # Consumed by the agentcore-admin Lambda (CAPABILITY_PRIVATE_SUBNET_IDS /
43
+ # CAPABILITY_PRIVATE_SECURITY_GROUP_IDS). Empty lists when disabled so the
44
+ # Lambda skips capability-private provisioning entirely.
45
+
46
+ output "capability_private_interpreter_subnet_ids" {
47
+ description = "No-NAT subnets the capability-private interpreter attaches to (same egress subnets as the broker)."
48
+ value = local.enabled ? var.subnet_ids : []
49
+ }
50
+
51
+ output "capability_private_interpreter_security_group_id" {
52
+ description = "Egress-only SG (broker execute-api VPCE + DNS only) for the capability-private interpreter. Empty when disabled."
53
+ value = local.enabled ? aws_security_group.capability_private_interpreter[0].id : ""
54
+ }
@@ -0,0 +1,98 @@
1
+ variable "enabled" {
2
+ description = "Master switch. When false the module creates nothing (ship-inert: U3 lands the broker code + this module gated off; U4 flips it on after the negative-egress proof)."
3
+ type = bool
4
+ default = false
5
+ }
6
+
7
+ variable "stage" {
8
+ type = string
9
+ }
10
+
11
+ variable "account_id" {
12
+ type = string
13
+ }
14
+
15
+ variable "region" {
16
+ type = string
17
+ }
18
+
19
+ # --- VPC placement (broker Lambda egress subnets + the dedicated VPCE) -------
20
+
21
+ variable "vpc_id" {
22
+ type = string
23
+ }
24
+
25
+ variable "vpc_cidr_block" {
26
+ type = string
27
+ }
28
+
29
+ variable "subnet_ids" {
30
+ description = "Private egress subnets the broker Lambda and its dedicated execute-api VPC endpoint attach to."
31
+ type = list(string)
32
+ default = []
33
+ }
34
+
35
+ # --- Evidence datastore (Aurora, RDS Data API — same cluster as graphql-http)-
36
+
37
+ variable "db_cluster_arn" {
38
+ type = string
39
+ default = ""
40
+ }
41
+
42
+ variable "db_secret_arn" {
43
+ type = string
44
+ default = ""
45
+ }
46
+
47
+ variable "db_cluster_endpoint" {
48
+ description = "Aurora writer endpoint hostname — DATABASE_HOST for getDb() (evidence store + authorization loader)."
49
+ type = string
50
+ default = ""
51
+ }
52
+
53
+ variable "db_username" {
54
+ description = "Aurora master username for the broker's DATABASE_URL."
55
+ type = string
56
+ default = "thinkwork_admin"
57
+ }
58
+
59
+ variable "db_password" {
60
+ description = "Aurora master password for the broker's DATABASE_URL (synchronous getDb() path)."
61
+ type = string
62
+ default = ""
63
+ sensitive = true
64
+ }
65
+
66
+ variable "database_name" {
67
+ description = "Aurora database name for the broker's DATABASE_URL."
68
+ type = string
69
+ default = "thinkwork"
70
+ }
71
+
72
+ # --- Lambda artifact source (mirrors lambda-api's dual local/remote pattern) -
73
+
74
+ variable "lambda_zips_dir" {
75
+ description = "Local path holding dist/lambdas/*.zip for local deploys. Empty when using remote S3 artifacts."
76
+ type = string
77
+ default = ""
78
+ }
79
+
80
+ variable "lambda_artifact_bucket" {
81
+ type = string
82
+ default = ""
83
+ }
84
+
85
+ variable "lambda_artifact_prefix" {
86
+ type = string
87
+ default = ""
88
+ }
89
+
90
+ variable "lambda_runtime" {
91
+ type = string
92
+ default = "nodejs22.x"
93
+ }
94
+
95
+ variable "log_retention_days" {
96
+ type = number
97
+ default = 30
98
+ }