thinkwork-cli 0.9.0 → 0.9.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 (57) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +2 -2
  3. package/dist/cli.js +1315 -330
  4. package/dist/terraform/examples/greenfield/main.tf +325 -19
  5. package/dist/terraform/examples/greenfield/terraform.tfvars.example +14 -0
  6. package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +61 -0
  7. package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +54 -0
  8. package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +197 -0
  9. package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +70 -0
  10. package/dist/terraform/modules/app/agentcore-flue/README.md +58 -0
  11. package/dist/terraform/modules/app/agentcore-flue/main.tf +322 -0
  12. package/dist/terraform/modules/app/agentcore-flue/outputs.tf +23 -0
  13. package/dist/terraform/modules/app/agentcore-flue/variables.tf +91 -0
  14. package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +0 -0
  15. package/dist/terraform/modules/app/agentcore-runtime/main.tf +165 -0
  16. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +4 -0
  17. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +5 -0
  18. package/dist/terraform/modules/app/computer-runtime/README.md +15 -0
  19. package/dist/terraform/modules/app/computer-runtime/main.tf +406 -0
  20. package/dist/terraform/modules/app/computer-runtime/outputs.tf +75 -0
  21. package/dist/terraform/modules/app/computer-runtime/variables.tf +66 -0
  22. package/dist/terraform/modules/app/hindsight-memory/main.tf +6 -0
  23. package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +128 -0
  24. package/dist/terraform/modules/app/lambda-api/handlers.tf +1454 -43
  25. package/dist/terraform/modules/app/lambda-api/main.tf +221 -12
  26. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +118 -0
  27. package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +49 -0
  28. package/dist/terraform/modules/app/lambda-api/outputs.tf +38 -0
  29. package/dist/terraform/modules/app/lambda-api/slack-app-secrets.tf +43 -0
  30. package/dist/terraform/modules/app/lambda-api/stripe-secrets.tf +53 -0
  31. package/dist/terraform/modules/app/lambda-api/variables.tf +349 -2
  32. package/dist/terraform/modules/app/lambda-api/workspace-events.tf +125 -0
  33. package/dist/terraform/modules/app/routines-stepfunctions/main.tf +453 -0
  34. package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +66 -0
  35. package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +200 -0
  36. package/dist/terraform/modules/app/static-site/main.tf +146 -5
  37. package/dist/terraform/modules/app/www-dns/main.tf +118 -15
  38. package/dist/terraform/modules/app/www-dns/outputs.tf +10 -0
  39. package/dist/terraform/modules/app/www-dns/variables.tf +42 -0
  40. package/dist/terraform/modules/data/aurora-postgres/main.tf +164 -3
  41. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +34 -0
  42. package/dist/terraform/modules/data/aurora-postgres/variables.tf +16 -0
  43. package/dist/terraform/modules/data/compliance-audit-bucket/README.md +145 -0
  44. package/dist/terraform/modules/data/compliance-audit-bucket/main.tf +573 -0
  45. package/dist/terraform/modules/data/compliance-audit-bucket/outputs.tf +43 -0
  46. package/dist/terraform/modules/data/compliance-audit-bucket/variables.tf +93 -0
  47. package/dist/terraform/modules/data/compliance-exports-bucket/main.tf +269 -0
  48. package/dist/terraform/modules/data/compliance-exports-bucket/outputs.tf +23 -0
  49. package/dist/terraform/modules/data/compliance-exports-bucket/variables.tf +50 -0
  50. package/dist/terraform/modules/data/s3-backups-bucket/main.tf +123 -0
  51. package/dist/terraform/modules/data/s3-buckets/main.tf +13 -0
  52. package/dist/terraform/modules/foundation/cognito/variables.tf +2 -2
  53. package/dist/terraform/modules/thinkwork/main.tf +439 -21
  54. package/dist/terraform/modules/thinkwork/outputs.tf +121 -0
  55. package/dist/terraform/modules/thinkwork/variables.tf +153 -2
  56. package/dist/terraform/schema.graphql +17 -0
  57. package/package.json +15 -14
@@ -0,0 +1,91 @@
1
+ ################################################################################
2
+ # AgentCore Flue — App Module (variables)
3
+ #
4
+ # Plan §005 U2 — provisions the Flue agent runtime as a Lambda+LWA function
5
+ # (the same shape as the Strands runtime in `../agentcore-runtime`, NOT the
6
+ # Bedrock AgentCore Runtime ECR-substrate pattern in `../agentcore-code-
7
+ # interpreter`).
8
+ #
9
+ # ECR repo + async DLQ are shared with the Strands runtime — they're injected
10
+ # from `module.agentcore` outputs at the parent composition layer rather than
11
+ # being created here. This avoids a duplicate ECR repository and a parallel DLQ
12
+ # while still letting Flue carry its own IAM role, log group, Lambda function,
13
+ # and event-invoke config.
14
+ ################################################################################
15
+
16
+ variable "stage" {
17
+ description = "Deployment stage"
18
+ type = string
19
+ }
20
+
21
+ variable "account_id" {
22
+ description = "AWS account ID"
23
+ type = string
24
+ }
25
+
26
+ variable "region" {
27
+ description = "AWS region"
28
+ type = string
29
+ }
30
+
31
+ variable "bucket_name" {
32
+ description = "Primary S3 bucket for skills and workspace files."
33
+ type = string
34
+ }
35
+
36
+ variable "ecr_repository_url" {
37
+ description = "ECR repository URL for the AgentCore container image. Shared with the Strands runtime (thinkwork-<stage>-agentcore); the Flue runtime pulls the flue-latest / <sha>-flue image tags from this repo."
38
+ type = string
39
+ }
40
+
41
+ variable "async_dlq_arn" {
42
+ description = "SQS DLQ ARN for failed `kind=run_skill` async invokes. Shared with the Strands runtime so operator inspection has a single queue to watch."
43
+ type = string
44
+ }
45
+
46
+ variable "hindsight_endpoint" {
47
+ description = "Hindsight API endpoint. Empty string (default) disables Hindsight tools in the container; set to an endpoint URL to enable Hindsight as an add-on alongside the always-on managed memory."
48
+ type = string
49
+ default = ""
50
+ }
51
+
52
+ variable "agentcore_memory_id" {
53
+ description = "AgentCore Memory resource ID. Populated automatically by the agentcore-memory module; injected into the container as AGENTCORE_MEMORY_ID for auto-retention."
54
+ type = string
55
+ default = ""
56
+ }
57
+
58
+ variable "api_endpoint" {
59
+ description = "Deployed API Gateway base URL. Injected as THINKWORK_API_URL so the composition runner (run_skill dispatch) can POST terminal state back to /api/skills/complete."
60
+ type = string
61
+ default = ""
62
+ }
63
+
64
+ variable "api_auth_secret" {
65
+ description = "Service-auth bearer shared secret. Injected as API_AUTH_SECRET so the composition runner can authenticate to /api/skills/complete. Matches the lambda-api module's value."
66
+ type = string
67
+ default = ""
68
+ sensitive = true
69
+ }
70
+
71
+ variable "memory_engine" {
72
+ description = "Active long-term memory engine ('hindsight' or 'agentcore'). Surfaced to the runtime as MEMORY_ENGINE for telemetry/debugging only; engine selection itself happens in the API's normalized memory layer when memory-retain is invoked."
73
+ type = string
74
+ default = "hindsight"
75
+ validation {
76
+ condition = contains(["hindsight", "agentcore"], var.memory_engine)
77
+ error_message = "memory_engine must be 'hindsight' or 'agentcore'."
78
+ }
79
+ }
80
+
81
+ variable "db_cluster_arn" {
82
+ description = "Aurora DB cluster ARN. Injected as DB_CLUSTER_ARN so AuroraSessionStore (plan §005 U4) can target the cluster via the RDS Data API. The cluster's IAM resource scope (thinkwork-<stage>-db-* in agentcore-flue's role policy) covers any cluster-id suffix."
83
+ type = string
84
+ default = ""
85
+ }
86
+
87
+ variable "db_secret_arn" {
88
+ description = "Secrets Manager ARN for the Aurora cluster credentials. Injected as DB_SECRET_ARN so AuroraSessionStore can authenticate against the cluster via the RDS Data API. Matches the secret graphql-http already consumes — single source of truth."
89
+ type = string
90
+ default = ""
91
+ }
@@ -38,6 +38,26 @@ variable "agentcore_memory_id" {
38
38
  default = ""
39
39
  }
40
40
 
41
+ variable "api_endpoint" {
42
+ description = "Deployed API Gateway base URL. Injected as THINKWORK_API_URL so the composition runner (run_skill dispatch) can POST terminal state back to /api/skills/complete."
43
+ type = string
44
+ default = ""
45
+ }
46
+
47
+ variable "api_auth_secret" {
48
+ description = "Service-auth bearer shared secret. Injected as API_AUTH_SECRET so the composition runner can authenticate to /api/skills/complete. Matches the lambda-api module's value."
49
+ type = string
50
+ default = ""
51
+ sensitive = true
52
+ }
53
+
54
+ variable "nova_act_api_key" {
55
+ description = "Nova Act API key used by the Strands Browser Automation tool. Stored as SSM SecureString at /thinkwork/<stage>/agentcore/nova-act-api-key. Empty string creates a placeholder; rotate the real value with aws ssm put-parameter --overwrite."
56
+ type = string
57
+ default = ""
58
+ sensitive = true
59
+ }
60
+
41
61
  variable "memory_engine" {
42
62
  description = "Active long-term memory engine ('hindsight' or 'agentcore'). Surfaced to the runtime as MEMORY_ENGINE for telemetry/debugging only; engine selection itself happens in the API's normalized memory layer when memory-retain is invoked."
43
63
  type = string
@@ -58,6 +78,19 @@ locals {
58
78
  memory_retain_fn_arn = "arn:aws:lambda:${var.region}:${var.account_id}:function:${local.memory_retain_fn_name}"
59
79
  }
60
80
 
81
+ resource "aws_ssm_parameter" "nova_act_api_key" {
82
+ name = "/thinkwork/${var.stage}/agentcore/nova-act-api-key"
83
+ type = "SecureString"
84
+ value = var.nova_act_api_key != "" ? var.nova_act_api_key : "PLACEHOLDER_SET_VIA_CLI"
85
+ description = "Nova Act API key consumed by the Strands Browser Automation tool."
86
+
87
+ lifecycle {
88
+ # Allow operator rotation to stick across applies. The runtime treats
89
+ # PLACEHOLDER_SET_VIA_CLI as unconfigured until the real key is populated.
90
+ ignore_changes = [value]
91
+ }
92
+ }
93
+
61
94
  ################################################################################
62
95
  # ECR Repository
63
96
  ################################################################################
@@ -113,6 +146,11 @@ resource "aws_iam_role" "agentcore" {
113
146
  }
114
147
 
115
148
  resource "aws_iam_role_policy" "agentcore" {
149
+ # Sibling policy: ../agentcore-flue/main.tf `aws_iam_role_policy.agentcore_flue`.
150
+ # The two policies share ~83% of statements (S3, Bedrock, AgentCore Memory,
151
+ # Code Interpreter, Logs, X-Ray, ECR, SSM, MemoryRetain). Flue adds Aurora
152
+ # Data API + Secrets Manager for U4 SessionStore. Keep both surfaces in
153
+ # sync for shared statements.
116
154
  name = "agentcore-permissions"
117
155
  role = aws_iam_role.agentcore.id
118
156
 
@@ -153,6 +191,50 @@ resource "aws_iam_role_policy" "agentcore" {
153
191
  ]
154
192
  Resource = "*"
155
193
  },
194
+ {
195
+ # Code Sandbox (execute_code tool). The runtime starts a
196
+ # Code Interpreter session at the top of every sandbox-
197
+ # registered turn and executes the preamble + user code
198
+ # against it. Without this, the runtime role can register
199
+ # the tool but every invocation fails with
200
+ # AccessDeniedException on StartCodeInterpreterSession.
201
+ # Resource wildcards under code-interpreter-custom/* so any
202
+ # tenant's interpreter (provisioned under this account) is
203
+ # reachable by the Strands runtime.
204
+ Sid = "AgentCoreCodeInterpreter"
205
+ Effect = "Allow"
206
+ Action = [
207
+ "bedrock-agentcore:StartCodeInterpreterSession",
208
+ "bedrock-agentcore:StopCodeInterpreterSession",
209
+ "bedrock-agentcore:InvokeCodeInterpreter",
210
+ "bedrock-agentcore:GetCodeInterpreterSession",
211
+ "bedrock-agentcore:ListCodeInterpreterSessions",
212
+ "bedrock-agentcore:GetCodeInterpreter",
213
+ ]
214
+ Resource = "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:code-interpreter-custom/*"
215
+ },
216
+ {
217
+ # Browser Automation (browser_automation tool). The Strands runtime
218
+ # starts a managed AgentCore Browser session, then connects to the
219
+ # automation stream over CDP. The AWS-managed browser resource uses the
220
+ # literal account segment `aws`, while AWS docs show account-scoped
221
+ # browser ARNs for the stream actions; grant both shapes.
222
+ Sid = "AgentCoreBrowser"
223
+ Effect = "Allow"
224
+ Action = [
225
+ "bedrock-agentcore:StartBrowserSession",
226
+ "bedrock-agentcore:StopBrowserSession",
227
+ "bedrock-agentcore:GetBrowserSession",
228
+ "bedrock-agentcore:ListBrowserSessions",
229
+ "bedrock-agentcore:UpdateBrowserStream",
230
+ "bedrock-agentcore:ConnectBrowserAutomationStream",
231
+ "bedrock-agentcore:ConnectBrowserLiveViewStream",
232
+ ]
233
+ Resource = [
234
+ "arn:aws:bedrock-agentcore:${var.region}:aws:browser/aws.browser.v1",
235
+ "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:browser/*",
236
+ ]
237
+ },
156
238
  {
157
239
  Sid = "CloudWatchLogs"
158
240
  Effect = "Allow"
@@ -260,6 +342,11 @@ resource "aws_lambda_function" "agentcore" {
260
342
  AGENTCORE_FILES_BUCKET = var.bucket_name
261
343
  MEMORY_ENGINE = var.memory_engine
262
344
  MEMORY_RETAIN_FN_NAME = local.memory_retain_fn_name
345
+ # Needed by run_skill_dispatch.py to POST terminal state back to
346
+ # /api/skills/complete after a composition run finishes.
347
+ THINKWORK_API_URL = var.api_endpoint
348
+ API_AUTH_SECRET = var.api_auth_secret
349
+ NOVA_ACT_SSM_PARAM_NAME = aws_ssm_parameter.nova_act_api_key.name
263
350
  }
264
351
  }
265
352
 
@@ -277,6 +364,67 @@ resource "aws_lambda_function" "agentcore" {
277
364
  # chat-agent-invoke — no Function URL is needed, and exposing one would be
278
365
  # a public attack surface for prompt injection.
279
366
 
367
+ ################################################################################
368
+ # Async-invoke hardening — plan §U4 (InvocationType=Event)
369
+ #
370
+ # Post §U4 the kind=run_skill dispatch flipped to InvocationType=Event so the
371
+ # agent loop has the full 900s Lambda budget. AWS Lambda async-invoke defaults
372
+ # to MaximumRetryAttempts=2, which on this path means a single transient
373
+ # failure (5xx on the /api/skills/complete callback, container OOM, etc.)
374
+ # causes the whole agent turn to run again on a fresh invocation. The agent
375
+ # turn is NOT idempotent — Bedrock tokens get re-burned and (in pathological
376
+ # cases) a second partial deliverable could overwrite the first before
377
+ # /api/skills/complete's atomic CAS rejects it with 409.
378
+ #
379
+ # MaximumRetryAttempts=0 makes the dispatch one-shot. The two durable
380
+ # backstops remain:
381
+ # 1. /api/skills/complete's atomic compare-and-swap on status='running' —
382
+ # idempotent writeback even if a retry did somehow fire.
383
+ # 2. skill-runs-reconciler cron — flips rows stuck in `running` past the
384
+ # stale cutoff (default 15 min) to `failed` so the dedup slot frees.
385
+ #
386
+ # Failed invokes (IAM, image-pull, crash-before-callback) land in the
387
+ # `async_dlq` SQS queue for operator visibility. 14-day retention matches
388
+ # the reconciler's backstop window with margin.
389
+ ################################################################################
390
+
391
+ resource "aws_sqs_queue" "agentcore_async_dlq" {
392
+ name = "thinkwork-${var.stage}-agentcore-async-dlq"
393
+ message_retention_seconds = 1209600 # 14 days
394
+ visibility_timeout_seconds = 900 # matches Lambda timeout
395
+ sqs_managed_sse_enabled = true
396
+
397
+ tags = {
398
+ Name = "thinkwork-${var.stage}-agentcore-async-dlq"
399
+ }
400
+ }
401
+
402
+ resource "aws_iam_role_policy" "agentcore_dlq_send" {
403
+ name = "agentcore-dlq-send"
404
+ role = aws_iam_role.agentcore.id
405
+
406
+ policy = jsonencode({
407
+ Version = "2012-10-17"
408
+ Statement = [{
409
+ Effect = "Allow"
410
+ Action = ["sqs:SendMessage"]
411
+ Resource = aws_sqs_queue.agentcore_async_dlq.arn
412
+ }]
413
+ })
414
+ }
415
+
416
+ resource "aws_lambda_function_event_invoke_config" "agentcore" {
417
+ function_name = aws_lambda_function.agentcore.function_name
418
+ maximum_retry_attempts = 0
419
+ maximum_event_age_in_seconds = 3600
420
+
421
+ destination_config {
422
+ on_failure {
423
+ destination = aws_sqs_queue.agentcore_async_dlq.arn
424
+ }
425
+ }
426
+ }
427
+
280
428
  ################################################################################
281
429
  # Outputs
282
430
  ################################################################################
@@ -300,3 +448,20 @@ output "agentcore_function_arn" {
300
448
  description = "AgentCore Lambda function ARN (for IAM policy on callers)"
301
449
  value = aws_lambda_function.agentcore.arn
302
450
  }
451
+
452
+ output "agentcore_async_dlq_arn" {
453
+ description = "SQS queue ARN that catches failed kind=run_skill async invokes"
454
+ value = aws_sqs_queue.agentcore_async_dlq.arn
455
+ }
456
+
457
+ output "agentcore_async_dlq_url" {
458
+ description = "SQS queue URL for operator inspection of failed async invokes"
459
+ value = aws_sqs_queue.agentcore_async_dlq.url
460
+ }
461
+
462
+ # Plan §005 U1 → U2 — the `agentcore_flue` resources that previously lived here
463
+ # (renamed from `agentcore_pi` in U1) moved out to `../agentcore-flue/main.tf` in
464
+ # U2. Cross-module state migration is declared in `terraform/modules/thinkwork/main.tf`
465
+ # via `moved {}` blocks at the parent composition layer. The U1 in-module `moved`
466
+ # blocks (`agentcore_pi` → `agentcore_flue`) were applied on the U1 deploy and
467
+ # are no longer needed here — the resources have left the module entirely.
@@ -77,6 +77,7 @@ locals {
77
77
  "notifyThreadUpdate",
78
78
  "notifyInboxItemUpdate",
79
79
  "notifyThreadTurnUpdate",
80
+ "publishComputerThreadChunk",
80
81
  "notifyOrgUpdate",
81
82
  ]
82
83
  }
@@ -101,6 +102,9 @@ resource "aws_appsync_resolver" "notifications" {
101
102
  #if(!$result.createdAt)
102
103
  #set($result.createdAt = $util.time.nowISO8601())
103
104
  #end
105
+ #if(!$result.publishedAt)
106
+ #set($result.publishedAt = $util.time.nowISO8601())
107
+ #end
104
108
  $util.toJson($result)
105
109
  EOF
106
110
  }
@@ -3,6 +3,11 @@ output "graphql_api_id" {
3
3
  value = aws_appsync_graphql_api.subscriptions.id
4
4
  }
5
5
 
6
+ output "graphql_api_arn" {
7
+ description = "AppSync GraphQL API ARN"
8
+ value = aws_appsync_graphql_api.subscriptions.arn
9
+ }
10
+
6
11
  output "graphql_api_url" {
7
12
  description = "AppSync GraphQL endpoint URL (used by backend to push notifications)"
8
13
  value = aws_appsync_graphql_api.subscriptions.uris["GRAPHQL"]
@@ -0,0 +1,15 @@
1
+ # Computer Runtime
2
+
3
+ Shared ECS/EFS substrate for ThinkWork Computers.
4
+
5
+ This module creates shared infrastructure only:
6
+
7
+ - ECR repository for the Computer runtime image.
8
+ - ECS/Fargate cluster.
9
+ - Encrypted EFS filesystem and mount targets.
10
+ - Task and EFS security groups, with NFS scoped to runtime tasks.
11
+ - Optional public-IP task networking for low-cost outbound API/CLI access without a NAT gateway.
12
+ - Execution/task roles and CloudWatch log group.
13
+ - Manager IAM policy for per-Computer access points, task definitions, and services.
14
+
15
+ Per-Computer EFS access points, task-definition revisions, and ECS services are created by the Computer manager Lambda from database rows. Terraform should not create one resource set per user.