thinkwork-cli 0.12.11 → 0.12.15
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.
- package/README.md +42 -38
- package/dist/{api-client-ASJWIG3Z.js → api-client-4VT3FWOH.js} +1 -1
- package/dist/{chunk-JEHV35EU.js → chunk-H7AB42ES.js} +340 -30
- package/dist/cli.js +22157 -9229
- package/dist/commands/enterprise/templates/deploy-repo/.github/workflows/deploy.yml +17 -6
- package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +81 -9
- package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +183 -21
- package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +1 -2
- package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +286 -0
- package/dist/drizzle/0000_petite_marvel_apes.sql +1188 -0
- package/dist/drizzle/0001_milky_surge.sql +103 -0
- package/dist/drizzle/0002_aberrant_mattie_franklin.sql +41 -0
- package/dist/drizzle/0003_exotic_ogun.sql +3 -0
- package/dist/drizzle/0004_glamorous_magneto.sql +2 -0
- package/dist/drizzle/0005_ancient_nocturne.sql +2 -0
- package/dist/drizzle/0006_new_colonel_america.sql +2 -0
- package/dist/drizzle/0007_unique_external_task_id.sql +31 -0
- package/dist/drizzle/0008_external_task_id_column.sql +43 -0
- package/dist/drizzle/0009_evaluations.sql +64 -0
- package/dist/drizzle/0010_eval_agent_template.sql +2 -0
- package/dist/drizzle/0011_eval_run_template.sql +2 -0
- package/dist/drizzle/0012_eval_seed_unique.sql +16 -0
- package/dist/drizzle/0013_motionless_white_queen.sql +140 -0
- package/dist/drizzle/0014_wiki_aggregation.sql +11 -0
- package/dist/drizzle/0015_pg_trgm_alias_title_indexes.sql +9 -0
- package/dist/drizzle/0016_wiki_schema_drops.sql +24 -0
- package/dist/drizzle/0017_wiki_places.sql +46 -0
- package/dist/drizzle/0018_agent_workspace_overlay.sql +26 -0
- package/dist/drizzle/0018_cool_firebird.sql +24 -0
- package/dist/drizzle/0018_skill_runs.sql +84 -0
- package/dist/drizzle/0019_condemned_dragon_man.sql +95 -0
- package/dist/drizzle/0019_webhook_skill_runs.sql +58 -0
- package/dist/drizzle/0020_crazy_the_anarchist.sql +9 -0
- package/dist/drizzle/0020_mutation_idempotency.sql +64 -0
- package/dist/drizzle/0021_crm_work_links.sql +76 -0
- package/dist/drizzle/0021_skill_runs_completion_hmac.sql +33 -0
- package/dist/drizzle/0022_add_billing_tables.sql +70 -0
- package/dist/drizzle/0022_seed_thinkwork_admin_permissions.sql +160 -0
- package/dist/drizzle/0022_stripe_billing_indexes.sql +48 -0
- package/dist/drizzle/0023_tenants_deactivation.sql +27 -0
- package/dist/drizzle/0024_tenant_mcp_admin_keys.sql +63 -0
- package/dist/drizzle/0025_v1_agent_architecture.sql +182 -0
- package/dist/drizzle/0026_thread_turns_add_kind.sql +28 -0
- package/dist/drizzle/0027_capability_catalog_and_manifests.sql +219 -0
- package/dist/drizzle/0028_aws_s3_extension.sql +26 -0
- package/dist/drizzle/0029_collapse_execution_types.sql +42 -0
- package/dist/drizzle/0030_retire_composition_primitives.sql +48 -0
- package/dist/drizzle/0031_thread_cleanup_drops.sql +131 -0
- package/dist/drizzle/0032_add_browser_automation_template_config.sql +24 -0
- package/dist/drizzle/0033_agent_operation_leases.sql +32 -0
- package/dist/drizzle/0034_agent_workspace_events.sql +116 -0
- package/dist/drizzle/0035_workspace_review_decisions.sql +19 -0
- package/dist/drizzle/0036_user_scoped_memory_wiki.sql +152 -0
- package/dist/drizzle/0037_user_profile_operating_model.sql +34 -0
- package/dist/drizzle/0042_agent_runtime_selector.sql +41 -0
- package/dist/drizzle/0043_wiki_search_tsv_tokenization.sql +48 -0
- package/dist/drizzle/0044_add_web_search_template_config.sql +7 -0
- package/dist/drizzle/0045_add_send_email_template_config.sql +7 -0
- package/dist/drizzle/0046_tenant_mcp_context_tools.sql +26 -0
- package/dist/drizzle/0047_add_context_engine_template_config.sql +7 -0
- package/dist/drizzle/0048_tenant_context_provider_settings.sql +22 -0
- package/dist/drizzle/0050_brain_v0_entity_subtype.sql +29 -0
- package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +170 -0
- package/dist/drizzle/0052_brain_v0_external_refs.sql +41 -0
- package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +48 -0
- package/dist/drizzle/0054_wiki_compile_jobs_input.sql +28 -0
- package/dist/drizzle/0055_routines_stepfunctions_substrate.sql +202 -0
- package/dist/drizzle/0056_routine_step_events_dedup_idx.sql +25 -0
- package/dist/drizzle/0057_archive_legacy_python_routines.sql +36 -0
- package/dist/drizzle/0058_routines_visibility_and_owning_agent.sql +61 -0
- package/dist/drizzle/0061_routine_execution_asl_version_id.sql +29 -0
- package/dist/drizzle/0062_migrate_pi_to_flue.sql +110 -0
- package/dist/drizzle/0063_threads_session_data.sql +23 -0
- package/dist/drizzle/0064_tenant_credentials.sql +65 -0
- package/dist/drizzle/0067_thinkwork_computers_phase_one.sql +174 -0
- package/dist/drizzle/0068_drop_system_workflows_and_activation.sql +115 -0
- package/dist/drizzle/0069_compliance_schema.sql +364 -0
- package/dist/drizzle/0070_compliance_aurora_roles.sql +171 -0
- package/dist/drizzle/0072_threads_computer_ownership.sql +24 -0
- package/dist/drizzle/0073_compliance_tenant_anchor_state.sql +105 -0
- package/dist/drizzle/0074_compliance_event_hash_index.sql +49 -0
- package/dist/drizzle/0075_scheduled_jobs_computer_ownership.sql +27 -0
- package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +69 -0
- package/dist/drizzle/0077_computers_primary_agent_id.sql +38 -0
- package/dist/drizzle/0078_tenant_customize_catalog.sql +46 -0
- package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +53 -0
- package/dist/drizzle/0081_routines_catalog_slug.sql +42 -0
- package/dist/drizzle/0082_messages_parts_jsonb.sql +52 -0
- package/dist/drizzle/0083_computer_runbooks.sql +103 -0
- package/dist/drizzle/0084_artifacts_favorited_at.sql +32 -0
- package/dist/drizzle/0085_seed_thinkwork_computer_default_template.sql +46 -0
- package/dist/drizzle/0086_update_default_template_models.sql +19 -0
- package/dist/drizzle/0087_retire_oss_connectors.sql +77 -0
- package/dist/drizzle/0088_compliance_event_types_finance_pilot.sql +37 -0
- package/dist/drizzle/0089_remove_maniflow_eval_seeds.sql +81 -0
- package/dist/drizzle/0089_wiki_schema_extraction.sql +315 -0
- package/dist/drizzle/0090_brain_schema_extraction.sql +274 -0
- package/dist/drizzle/0091_drop_wiki_brain_compat_views.sql +118 -0
- package/dist/drizzle/0092_finish_oss_connector_retirement.sql +97 -0
- package/dist/drizzle/0093_eval_runs_scheduled_job_id.sql +23 -0
- package/dist/drizzle/0094_slack_workspace_app.sql +140 -0
- package/dist/drizzle/0095_eval_runs_computer_id.sql +22 -0
- package/dist/drizzle/0096_true_redteam_eval_seed_cleanup.sql +67 -0
- package/dist/drizzle/0097_shared_computers.sql +191 -0
- package/dist/drizzle/0098_business_ontology.sql +587 -0
- package/dist/drizzle/0099_thread_idle_learning.sql +145 -0
- package/dist/drizzle/0100_ontology_gated_brain_dynamic_subtypes.sql +7 -0
- package/dist/drizzle/0101_ontology_gated_unresolved_mentions_dynamic_subtypes.sql +7 -0
- package/dist/drizzle/0102_wiki_brain_owner_repair.sql +87 -0
- package/dist/drizzle/0103_brain_section_source_trigger_repair.sql +75 -0
- package/dist/drizzle/0104_expand_seed_ontology_for_user_memory.sql +166 -0
- package/dist/drizzle/0105_spaces_domain.sql +349 -0
- package/dist/drizzle/0106_expand_seed_ontology_for_triples.sql +173 -0
- package/dist/drizzle/0106_space_threads_participants.sql +197 -0
- package/dist/drizzle/0107_linked_task_mirror.sql +245 -0
- package/dist/drizzle/0108_message_mentions.sql +95 -0
- package/dist/drizzle/0109_space_owned_threads.sql +217 -0
- package/dist/drizzle/0110_thread_participant_read_state.sql +21 -0
- package/dist/drizzle/0111_agents_tenant_name_unique.sql +38 -0
- package/dist/drizzle/0112_recast_spaces_as_contextual_workrooms.sql +138 -0
- package/dist/drizzle/0113_agents_own_runtime_fields.sql +48 -0
- package/dist/drizzle/0114_migrate_templates_to_agents_and_spaces.sql +361 -0
- package/dist/drizzle/0115_remove_computer_eval_template_dependencies.sql +69 -0
- package/dist/drizzle/0116_backfill_thread_participants_access.sql +126 -0
- package/dist/drizzle/0117_space_access_mode.sql +40 -0
- package/dist/drizzle/0118_normalize_tenant_member_principal_type.sql +41 -0
- package/dist/drizzle/0119_space_knowledge_bases.sql +126 -0
- package/dist/drizzle/0120_space_automations.sql +58 -0
- package/dist/drizzle/0121_drop_computer_runbooks.sql +26 -0
- package/dist/drizzle/0122_space_email_triggers.sql +24 -0
- package/dist/drizzle/0123_single_platform_agent_and_overrides.sql +58 -0
- package/dist/drizzle/0124_thread_turns_finalized_at.sql +17 -0
- package/dist/drizzle/0125_drop_space_agent_assignments.sql +42 -0
- package/dist/drizzle/0126_migrate_flue_to_pi.sql +42 -0
- package/dist/drizzle/0127_runtime_type_observability.sql +28 -0
- package/dist/drizzle/0128_drop_eval_test_cases_agent_id.sql +37 -0
- package/dist/drizzle/0129_drop_code_factory_tables.sql +43 -0
- package/dist/drizzle/0129_eval_results_system_prompt.sql +30 -0
- package/dist/drizzle/0130_drop_teams_and_team_id_columns.sql +54 -0
- package/dist/drizzle/0131_drop_skill_catalog_and_tenant_skills.sql +33 -0
- package/dist/drizzle/0132_drop_computer_tables.sql +31 -0
- package/dist/drizzle/0133_drop_seed_view_tombstones.sql +79 -0
- package/dist/drizzle/0133_thread_turns_system_prompt.sql +22 -0
- package/dist/drizzle/0134_drop_dead_agent_runtime_tables.sql +53 -0
- package/dist/drizzle/0135_native_checklist_linked_tasks.sql +48 -0
- package/dist/drizzle/0136_goal_ledger.sql +55 -0
- package/dist/drizzle/0136_space_email_trigger_status.sql +66 -0
- package/dist/drizzle/0137_thread_participant_pins.sql +25 -0
- package/dist/drizzle/0138_users_cognito_sub.sql +30 -0
- package/dist/drizzle/0139_mobile_turn_client_id.sql +23 -0
- package/dist/drizzle/0140_workspace_folder_names.sql +36 -0
- package/dist/drizzle/0141_eval_runs_desktop_pi_provenance.sql +31 -0
- package/dist/drizzle/0142_pi_only_agent_runtime.sql +61 -0
- package/dist/drizzle/0143_add_web_extract_builtin_config.sql +28 -0
- package/dist/drizzle/0144_skill_catalog_index.sql +40 -0
- package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +512 -0
- package/dist/drizzle/0146_knowledge_graph_source_scope.sql +413 -0
- package/dist/drizzle/0147_knowledge_graph_scope_trigger_qualification.sql +211 -0
- package/dist/drizzle/0148_user_cost_attribution.sql +171 -0
- package/dist/drizzle/0149_managed_mcp_servers.sql +64 -0
- package/dist/drizzle/0149_user_model_approvals.sql +89 -0
- package/dist/drizzle/0150_bootstrap_first_admin_claim.sql +11 -0
- package/dist/drizzle/0151_managed_deployments.sql +91 -0
- package/dist/drizzle/0152_agent_profiles.sql +81 -0
- package/dist/drizzle/0153_message_mentions_agent_profile.sql +9 -0
- package/dist/drizzle/0154_customer_deployment_sessions.sql +61 -0
- package/dist/drizzle/0155_tenant_model_catalog.sql +152 -0
- package/dist/drizzle/0156_bootstrap_credential_leases.sql +52 -0
- package/dist/drizzle/0157_knowledge_graph_observations_source.sql +79 -0
- package/dist/drizzle/0158_pending_user_questions.sql +80 -0
- package/dist/drizzle/0158_wiki_tenant_scope.sql +38 -0
- package/dist/drizzle/0159_agent_profiles_space_local.sql +49 -0
- package/dist/drizzle/0159_application_plugins.sql +193 -0
- package/dist/drizzle/0159_eval_scoring_version.sql +46 -0
- package/dist/drizzle/0160_compliance_event_types_plugins.sql +37 -0
- package/dist/drizzle/0160_eval_datasets.sql +75 -0
- package/dist/drizzle/0160_eval_results_thread_turn.sql +35 -0
- package/dist/drizzle/0161_eval_run_pinning.sql +54 -0
- package/dist/drizzle/0161_tenant_mcp_servers_plugin_source.sql +62 -0
- package/dist/drizzle/0162_eval_result_override.sql +49 -0
- package/dist/drizzle/0163_eval_replay_tool_allowlist.sql +50 -0
- package/dist/drizzle/0164_eval_replay_tool_override_mode.sql +52 -0
- package/dist/drizzle/0165_plugin_premium_entitlements.sql +153 -0
- package/dist/drizzle/0166_company_brain_substrate_contract.sql +386 -0
- package/dist/drizzle/0167_company_brain_artifact_manifest_runtime.sql +85 -0
- package/dist/drizzle/0168_eval_skill_gate.sql +44 -0
- package/dist/drizzle/0169_release_update_jobs.sql +67 -0
- package/dist/drizzle/0170_email_channel_plugin.sql +291 -0
- package/dist/drizzle/0171_linked_task_external_providers.sql +30 -0
- package/dist/drizzle/0172_email_channel_sendgrid_provider.sql +8 -0
- package/dist/drizzle/0173_auth_provider_resources.sql +83 -0
- package/dist/drizzle/0174_workos_auth_bridges.sql +37 -0
- package/dist/drizzle/0175_workos_auth_sessions.sql +38 -0
- package/dist/drizzle/0176_n8n_agent_step_runs.sql +107 -0
- package/dist/drizzle/0177_workflow_control_plane.sql +281 -0
- package/dist/drizzle/0178_workflow_backfill_existing_routines.sql +525 -0
- package/dist/drizzle/0179_tenant_goal_budget_settings.sql +21 -0
- package/dist/drizzle/0180_skill_drafts.sql +89 -0
- package/dist/drizzle/0181_skill_draft_publish_status.sql +21 -0
- package/dist/drizzle/0182_agent_loops.sql +260 -0
- package/dist/drizzle/0183_okf_artifact_manifests.sql +52 -0
- package/dist/drizzle/0184_skill_catalog_trust_cache.sql +35 -0
- package/dist/drizzle/0185_agent_loop_space.sql +16 -0
- package/dist/drizzle/0186_tenant_mcp_runtime_metadata.sql +10 -0
- package/dist/drizzle/0187_native_work_items.sql +220 -0
- package/dist/drizzle/0188_company_etl_plugin_rename.sql +99 -0
- package/dist/drizzle/0189_trace_cost_substrate.sql +247 -0
- package/dist/drizzle/0190_billing_export_reconciliation.sql +101 -0
- package/dist/drizzle/0191_open_engine_work_item_queue.sql +66 -0
- package/dist/drizzle/0192_work_item_labels.sql +44 -0
- package/dist/drizzle/0193_work_item_documents.sql +31 -0
- package/dist/drizzle/0194_memory_retain_attempts.sql +103 -0
- package/dist/drizzle/0194_work_item_comments.sql +35 -0
- package/dist/drizzle/0195_plugin_app_overlays.sql +45 -0
- package/dist/drizzle/0196_pi_extensions.sql +118 -0
- package/dist/drizzle/0197_eval_profiles.sql +150 -0
- package/dist/drizzle/0198_eval_runs_unstable.sql +27 -0
- package/dist/drizzle/0199_eval_runs_cost_partial.sql +27 -0
- package/dist/drizzle/meta/_journal.json +160 -0
- package/dist/plugins/README.md +162 -0
- package/dist/plugins/catalog/package.json +40 -0
- package/dist/plugins/catalog/scripts/build-catalog.ts +120 -0
- package/dist/plugins/catalog/scripts/generate-plugin-registry.ts +144 -0
- package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +86 -0
- package/dist/plugins/catalog/src/__tests__/catalog.test.ts +308 -0
- package/dist/plugins/catalog/src/__tests__/contracts.test.ts +861 -0
- package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +124 -0
- package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +120 -0
- package/dist/plugins/catalog/src/catalog.ts +434 -0
- package/dist/plugins/catalog/src/contracts.ts +1498 -0
- package/dist/plugins/catalog/src/index.ts +4 -0
- package/dist/plugins/catalog/src/plugin-package.ts +171 -0
- package/dist/plugins/catalog/src/registry/generated-first-party.ts +76 -0
- package/dist/plugins/catalog/src/registry/index.ts +1 -0
- package/dist/plugins/catalog/tsconfig.json +8 -0
- package/dist/plugins/company-brain/README.md +37 -0
- package/dist/plugins/company-brain/package.json +33 -0
- package/dist/plugins/company-brain/runtime/cognee/Dockerfile +11 -0
- package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +458 -0
- package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +539 -0
- package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +312 -0
- package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +449 -0
- package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +655 -0
- package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +474 -0
- package/dist/plugins/company-brain/src/api/cognee-client.ts +896 -0
- package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +49 -0
- package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +84 -0
- package/dist/plugins/company-brain/src/api/context-engine-provider.ts +888 -0
- package/dist/plugins/company-brain/src/api/migration.ts +665 -0
- package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +396 -0
- package/dist/plugins/company-brain/src/index.ts +62 -0
- package/dist/plugins/company-brain/src/manifest.ts +112 -0
- package/dist/plugins/company-brain/terraform/cognee/README.md +263 -0
- package/dist/plugins/company-brain/terraform/cognee/main.tf +766 -0
- package/dist/plugins/company-brain/terraform/cognee/outputs.tf +144 -0
- package/dist/plugins/company-brain/terraform/cognee/variables.tf +402 -0
- package/dist/plugins/company-brain/test/api/cognee-client.test.ts +562 -0
- package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +74 -0
- package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +447 -0
- package/dist/plugins/company-brain/test/api/migration.test.ts +465 -0
- package/dist/plugins/company-brain/test/manifest.test.ts +95 -0
- package/dist/plugins/company-brain/tsconfig.json +8 -0
- package/dist/plugins/company-data/README.md +49 -0
- package/dist/plugins/company-data/package.json +21 -0
- package/dist/plugins/company-data/src/index.ts +30 -0
- package/dist/plugins/company-data/src/manifest.ts +26 -0
- package/dist/plugins/company-data/test/manifest.test.ts +116 -0
- package/dist/plugins/company-data/tsconfig.json +8 -0
- package/dist/plugins/company-etl/README.md +46 -0
- package/dist/plugins/company-etl/package.json +21 -0
- package/dist/plugins/company-etl/src/index.ts +29 -0
- package/dist/plugins/company-etl/src/manifest.ts +26 -0
- package/dist/plugins/company-etl/test/manifest.test.ts +108 -0
- package/dist/plugins/company-etl/tsconfig.json +8 -0
- package/dist/plugins/email-channel/README.md +41 -0
- package/dist/plugins/email-channel/package.json +22 -0
- package/dist/plugins/email-channel/src/index.ts +36 -0
- package/dist/plugins/email-channel/src/manifest.ts +44 -0
- package/dist/plugins/email-channel/src/provider-contract.ts +36 -0
- package/dist/plugins/email-channel/test/manifest.test.ts +119 -0
- package/dist/plugins/email-channel/tsconfig.json +8 -0
- package/dist/plugins/lastmile/README.md +50 -0
- package/dist/plugins/lastmile/package.json +23 -0
- package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +661 -0
- package/dist/plugins/lastmile/src/api/tasks-adapter.ts +572 -0
- package/dist/plugins/lastmile/src/discovery.fixture.ts +64 -0
- package/dist/plugins/lastmile/src/index.ts +46 -0
- package/dist/plugins/lastmile/src/manifest.ts +139 -0
- package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +266 -0
- package/dist/plugins/lastmile/test/discovery.test.ts +88 -0
- package/dist/plugins/lastmile/tsconfig.json +8 -0
- package/dist/plugins/n8n/README.md +201 -0
- package/dist/plugins/n8n/n8n-app/README.md +33 -0
- package/dist/plugins/n8n/n8n-app/package.json +23 -0
- package/dist/plugins/n8n/n8n-app/src/application-config.ts +45 -0
- package/dist/plugins/n8n/n8n-app/src/front-components/thinkwork-workflows.front-component.tsx +970 -0
- package/dist/plugins/n8n/n8n-app/src/lib/n8n-app-data.ts +190 -0
- package/dist/plugins/n8n/n8n-app/src/vite-env.d.ts +7 -0
- package/dist/plugins/n8n/n8n-app/tsconfig.json +15 -0
- package/dist/plugins/n8n/package.json +35 -0
- package/dist/plugins/n8n/runtime/Dockerfile +18 -0
- package/dist/plugins/n8n/runtime/n8n-task-runners.json +14 -0
- package/dist/plugins/n8n/runtime/n8n-task-runners.json.template +14 -0
- package/dist/plugins/n8n/runtime/package-lock.json +13 -0
- package/dist/plugins/n8n/runtime/package.json +7 -0
- package/dist/plugins/n8n/scripts/sync-thinkwork-app.mjs +268 -0
- package/dist/plugins/n8n/smoke/README.md +95 -0
- package/dist/plugins/n8n/smoke/n8n-agent-step-bridge-smoke.mjs +866 -0
- package/dist/plugins/n8n/smoke/n8n-integrated-app-smoke.mjs +262 -0
- package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +409 -0
- package/dist/plugins/n8n/smoke/n8n-mcp-smoke.mjs +624 -0
- package/dist/plugins/n8n/src/deployment/image-build.ts +172 -0
- package/dist/plugins/n8n/src/deployment/managed-app.ts +429 -0
- package/dist/plugins/n8n/src/index.ts +79 -0
- package/dist/plugins/n8n/src/manifest.ts +403 -0
- package/dist/plugins/n8n/src/package-config.ts +186 -0
- package/dist/plugins/n8n/src/skills/n8n-workflow-operator/SKILL.md +76 -0
- package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
- package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
- package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/workflow-authoring.md +45 -0
- package/dist/plugins/n8n/terraform/n8n/README.md +113 -0
- package/dist/plugins/n8n/terraform/n8n/main.tf +918 -0
- package/dist/plugins/n8n/terraform/n8n/outputs.tf +124 -0
- package/dist/plugins/n8n/terraform/n8n/scripts/sync-database.py +250 -0
- package/dist/plugins/n8n/terraform/n8n/scripts/test_sync_database.py +55 -0
- package/dist/plugins/n8n/terraform/n8n/variables.tf +354 -0
- package/dist/plugins/n8n/test/image-build.test.ts +118 -0
- package/dist/plugins/n8n/test/integrated-app-contract.test.ts +353 -0
- package/dist/plugins/n8n/test/manifest.test.ts +278 -0
- package/dist/plugins/n8n/test/package-config.test.ts +57 -0
- package/dist/plugins/n8n/tsconfig.json +8 -0
- package/dist/plugins/sendgrid/README.md +30 -0
- package/dist/plugins/sendgrid/package.json +22 -0
- package/dist/plugins/sendgrid/src/index.ts +36 -0
- package/dist/plugins/sendgrid/src/manifest.ts +37 -0
- package/dist/plugins/sendgrid/src/provider-contract.ts +14 -0
- package/dist/plugins/sendgrid/test/manifest.test.ts +80 -0
- package/dist/plugins/sendgrid/tsconfig.json +8 -0
- package/dist/plugins/twenty/README.md +78 -0
- package/dist/plugins/twenty/package.json +27 -0
- package/dist/plugins/twenty/scripts/__tests__/sync-thinkwork-app.test.mjs +67 -0
- package/dist/plugins/twenty/scripts/sync-thinkwork-app.mjs +185 -0
- package/dist/plugins/twenty/scripts/wire-thinkwork-workflow.mjs +658 -0
- package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +359 -0
- package/dist/plugins/twenty/smoke/twenty-mcp-oauth-smoke.mjs +574 -0
- package/dist/plugins/twenty/src/api/cutover.ts +209 -0
- package/dist/plugins/twenty/src/deployment/managed-app.ts +253 -0
- package/dist/plugins/twenty/src/index.ts +55 -0
- package/dist/plugins/twenty/src/manifest.ts +259 -0
- package/dist/plugins/twenty/terraform/twenty/README.md +97 -0
- package/dist/plugins/twenty/terraform/twenty/main.tf +829 -0
- package/dist/plugins/twenty/terraform/twenty/outputs.tf +79 -0
- package/dist/plugins/twenty/terraform/twenty/variables.tf +268 -0
- package/dist/plugins/twenty/test/api/cutover.test.ts +183 -0
- package/dist/plugins/twenty/test/manifest.test.ts +463 -0
- package/dist/plugins/twenty/tsconfig.json +9 -0
- package/dist/plugins/twenty/twenty-app/.nvmrc +1 -0
- package/dist/plugins/twenty/twenty-app/README.md +148 -0
- package/dist/plugins/twenty/twenty-app/package.json +30 -0
- package/dist/plugins/twenty/twenty-app/src/application-config.ts +44 -0
- package/dist/plugins/twenty/twenty-app/src/constants/universal-identifiers.ts +18 -0
- package/dist/plugins/twenty/twenty-app/src/default-role.ts +24 -0
- package/dist/plugins/twenty/twenty-app/src/front-components/thinkwork-settings.front-component.tsx +299 -0
- package/dist/plugins/twenty/twenty-app/src/logic-functions/thinkwork-webhook.logic-function.ts +354 -0
- package/dist/plugins/twenty/twenty-app/tsconfig.json +14 -0
- package/dist/plugins/twenty/twenty-app/yarn.lock +4144 -0
- package/dist/plugins/workos-auth/package.json +22 -0
- package/dist/plugins/workos-auth/src/index.ts +30 -0
- package/dist/plugins/workos-auth/src/manifest.ts +40 -0
- package/dist/plugins/workos-auth/src/provider-contract.ts +39 -0
- package/dist/plugins/workos-auth/test/manifest.test.ts +94 -0
- package/dist/plugins/workos-auth/tsconfig.json +8 -0
- package/dist/scripts/bootstrap-workspace.sh +162 -0
- package/dist/scripts/post-deploy.sh +290 -0
- package/dist/terraform/examples/greenfield/main.tf +903 -75
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +67 -8
- package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +2 -2
- package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +8 -9
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +227 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +277 -0
- package/dist/terraform/modules/app/agentcore-memory/README.md +6 -6
- package/dist/terraform/modules/app/agentcore-memory/main.tf +1 -1
- package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +40 -9
- package/dist/terraform/modules/app/agentcore-pi/README.md +61 -0
- package/dist/terraform/modules/app/agentcore-pi/main.tf +453 -0
- package/dist/terraform/modules/app/agentcore-pi/outputs.tf +38 -0
- package/dist/terraform/modules/app/agentcore-pi/variables.tf +145 -0
- package/dist/terraform/modules/app/agentcore-platform/main.tf +83 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +9 -11
- package/dist/terraform/modules/app/appsync-subscriptions/main.tf +5 -1
- package/dist/terraform/modules/app/customer-domain/main.tf +483 -0
- package/dist/terraform/modules/app/deployment-control-plane/README.md +47 -0
- package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +24 -0
- package/dist/terraform/modules/app/deployment-control-plane/main.tf +614 -0
- package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +54 -0
- package/dist/terraform/modules/app/deployment-control-plane/runner.py +4720 -0
- package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +3026 -0
- package/dist/terraform/modules/app/deployment-control-plane/variables.tf +96 -0
- package/dist/terraform/modules/app/hindsight-memory/README.md +25 -23
- package/dist/terraform/modules/app/hindsight-memory/main.tf +26 -2
- package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +17 -50
- package/dist/terraform/modules/app/lambda-api/handlers.tf +1290 -679
- package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +933 -0
- package/dist/terraform/modules/app/lambda-api/main.tf +14 -527
- package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +3 -19
- package/dist/terraform/modules/app/lambda-api/outputs.tf +50 -6
- package/dist/terraform/modules/app/lambda-api/runtime-config.tf +163 -0
- package/dist/terraform/modules/app/lambda-api/slack-app-secrets.tf +3 -1
- package/dist/terraform/modules/app/lambda-api/stripe-secrets.tf +3 -1
- package/dist/terraform/modules/app/lambda-api/variables.tf +240 -53
- package/dist/terraform/modules/app/lambda-api/workspace-events.tf +2 -25
- package/dist/terraform/modules/app/ses-email/main.tf +143 -30
- package/dist/terraform/modules/app/static-site/main.tf +4 -3
- package/dist/terraform/modules/app/www-dns/main.tf +45 -5
- package/dist/terraform/modules/app/www-dns/outputs.tf +10 -0
- package/dist/terraform/modules/app/www-dns/variables.tf +24 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +15 -1
- package/dist/terraform/modules/data/compliance-exports-bucket/main.tf +1 -1
- package/dist/terraform/modules/data/compliance-exports-bucket/variables.tf +6 -0
- package/dist/terraform/modules/foundation/cognito/main.tf +167 -11
- package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +110 -0
- package/dist/terraform/modules/foundation/vpc/main.tf +45 -3
- package/dist/terraform/modules/foundation/vpc/outputs.tf +20 -0
- package/dist/terraform/modules/foundation/vpc/variables.tf +18 -0
- package/dist/terraform/modules/thinkwork/README.md +71 -0
- package/dist/terraform/modules/thinkwork/main.tf +1325 -174
- package/dist/terraform/modules/thinkwork/outputs.tf +457 -24
- package/dist/terraform/modules/thinkwork/variables.tf +1169 -93
- package/dist/terraform/schema.graphql +82 -9
- package/dist/workspace-defaults/files/AGENTS.md +344 -0
- package/dist/workspace-defaults/files/CONTEXT.md +8 -0
- package/dist/workspace-defaults/files/GUARDRAILS.md +51 -0
- package/dist/workspace-defaults/files/MEMORY_GUIDE.md +65 -0
- package/dist/workspace-defaults/files/ROUTER.md +24 -0
- package/dist/workspace-defaults/files/SPACE.md +62 -0
- package/dist/workspace-defaults/files/TOOLS.md +65 -0
- package/dist/workspace-defaults/files/USER.md +40 -0
- package/dist/workspace-defaults/files/mcp.json +3 -0
- package/dist/workspace-defaults/files/memory/contacts.md +13 -0
- package/dist/workspace-defaults/files/memory/lessons.md +13 -0
- package/dist/workspace-defaults/files/memory/preferences.md +12 -0
- package/dist/workspace-defaults/files/skills/.gitkeep +1 -0
- package/dist/workspace-defaults/files/skills/artifact-builder/SKILL.md +79 -0
- package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +166 -0
- package/dist/workspace-defaults/files/skills/automation-loop-designer/SKILL.md +120 -0
- package/dist/workspace-defaults/files/skills/skill-creator/LICENSE.txt +202 -0
- package/dist/workspace-defaults/files/skills/skill-creator/SKILL.md +485 -0
- package/dist/workspace-defaults/files/skills/skill-creator/UPSTREAM.json +124 -0
- package/dist/workspace-defaults/files/skills/skill-creator/agents/analyzer.md +274 -0
- package/dist/workspace-defaults/files/skills/skill-creator/agents/comparator.md +202 -0
- package/dist/workspace-defaults/files/skills/skill-creator/agents/grader.md +223 -0
- package/dist/workspace-defaults/files/skills/skill-creator/assets/eval_review.html +146 -0
- package/dist/workspace-defaults/files/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/dist/workspace-defaults/files/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/dist/workspace-defaults/files/skills/skill-creator/references/schemas.md +430 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/__init__.py +0 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/generate_report.py +326 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/improve_description.py +247 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/package_skill.py +136 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/run_eval.py +310 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/run_loop.py +328 -0
- package/dist/workspace-defaults/files/skills/skill-creator/scripts/utils.py +47 -0
- package/package.json +1 -1
- package/dist/terraform/modules/app/agentcore-flue/README.md +0 -58
- package/dist/terraform/modules/app/agentcore-flue/main.tf +0 -323
- package/dist/terraform/modules/app/agentcore-flue/outputs.tf +0 -23
- package/dist/terraform/modules/app/agentcore-flue/variables.tf +0 -97
- package/dist/terraform/modules/app/computer-runtime/README.md +0 -15
- package/dist/terraform/modules/app/computer-runtime/main.tf +0 -406
- package/dist/terraform/modules/app/computer-runtime/outputs.tf +0 -75
- package/dist/terraform/modules/app/computer-runtime/variables.tf +0 -66
|
@@ -13,12 +13,59 @@ account_id = "123456789012" # your AWS account ID
|
|
|
13
13
|
database_engine = "rds-postgres"
|
|
14
14
|
|
|
15
15
|
# Memory:
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
16
|
+
# Hindsight is the canonical user and Space memory provider for full
|
|
17
|
+
# ThinkWork installs and is enabled by default. Set enable_hindsight = false
|
|
18
|
+
# and memory_engine = "agentcore" only for explicit low-cost/development
|
|
19
|
+
# AgentCore-only deployments.
|
|
20
|
+
# enable_hindsight = false
|
|
21
|
+
# memory_engine = "agentcore"
|
|
22
|
+
|
|
23
|
+
# Managed applications:
|
|
24
|
+
# Twenty CRM is optional. First enable should set both booleans true after
|
|
25
|
+
# the deployment workflow has created the dedicated database URL secret and
|
|
26
|
+
# encryption-key secret. Later disable should leave twenty_provisioned=true
|
|
27
|
+
# and set twenty_runtime_enabled=false so CRM data/storage/cache are retained.
|
|
28
|
+
# Full cleanup should set both booleans false through the Managed
|
|
29
|
+
# Applications Destroy action so the deploy workflow can also drop the
|
|
30
|
+
# dedicated database and delete Twenty secrets after Terraform teardown.
|
|
31
|
+
#
|
|
32
|
+
# twenty_provisioned = false
|
|
33
|
+
# twenty_runtime_enabled = false
|
|
34
|
+
# twenty_image_uri = "twentycrm/twenty@sha256:..."
|
|
35
|
+
# twenty_db_username = "thinkwork_twenty"
|
|
36
|
+
# twenty_db_name = "thinkwork_twenty"
|
|
37
|
+
# twenty_db_url_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/twenty/db-url-..."
|
|
38
|
+
# twenty_encryption_key_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/twenty/encryption-key-..."
|
|
39
|
+
# twenty_public_url = "" # empty derives https://crm.<www_domain>
|
|
40
|
+
# twenty_certificate_arn = "" # empty creates a dedicated crm.<www_domain> certificate
|
|
41
|
+
# twenty_email_from_address = "" # empty derives noreply@ses_inbound_domain for SES SMTP
|
|
42
|
+
# twenty_email_from_name = "ThinkWork CRM"
|
|
43
|
+
#
|
|
44
|
+
# n8n is optional. First enable should set both booleans true after the
|
|
45
|
+
# managed-app workflow has prepared the dedicated thinkwork_n8n database URL
|
|
46
|
+
# secret, N8N_ENCRYPTION_KEY secret, shared operator credential, tenant
|
|
47
|
+
# service credential, wrapper image, and retained artifact bucket. Later
|
|
48
|
+
# disable should leave n8n_provisioned=true and set
|
|
49
|
+
# n8n_runtime_enabled=false so workflow data, credentials, cache, and
|
|
50
|
+
# artifacts are retained.
|
|
51
|
+
#
|
|
52
|
+
# n8n_provisioned = false
|
|
53
|
+
# n8n_runtime_enabled = false
|
|
54
|
+
# n8n_image_uri = "123456789012.dkr.ecr.us-east-1.amazonaws.com/thinkwork-n8n@sha256:..."
|
|
55
|
+
# n8n_database_admin_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/database-admin-..."
|
|
56
|
+
# n8n_database_url_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/n8n/database-url-..."
|
|
57
|
+
# n8n_database_username = "thinkwork_n8n"
|
|
58
|
+
# n8n_database_name = "thinkwork_n8n"
|
|
59
|
+
# n8n_encryption_key_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/n8n/encryption-key-..."
|
|
60
|
+
# n8n_operator_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/n8n/operator-..."
|
|
61
|
+
# n8n_service_credential_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/n8n/service-credential-..."
|
|
62
|
+
# n8n_agent_step_bridge_credential_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:thinkwork/dev/n8n/agent-step-bridge-credential-..."
|
|
63
|
+
# n8n_storage_bucket_name = "thinkwork-dev-n8n"
|
|
64
|
+
# n8n_container_port = 5678
|
|
65
|
+
# n8n_cache_engine = "valkey"
|
|
66
|
+
# n8n_domain = "" # empty derives n8n.<www_domain>
|
|
67
|
+
# n8n_public_url = "" # empty derives https://n8n.<www_domain>
|
|
68
|
+
# n8n_certificate_arn = "" # empty creates a dedicated n8n.<www_domain> certificate
|
|
22
69
|
|
|
23
70
|
# Database master password — use a strong password
|
|
24
71
|
db_password = "CHANGE_ME_strong_password_here"
|
|
@@ -27,8 +74,9 @@ db_password = "CHANGE_ME_strong_password_here"
|
|
|
27
74
|
# google_oauth_client_id = ""
|
|
28
75
|
# google_oauth_client_secret = ""
|
|
29
76
|
|
|
30
|
-
#
|
|
31
|
-
# pre_signup_lambda_zip
|
|
77
|
+
# Cognito trigger Lambdas (optional — leave empty if not using custom auth logic)
|
|
78
|
+
# pre_signup_lambda_zip = "./lambdas/cognito-pre-signup.zip"
|
|
79
|
+
# cognito_custom_auth_lambda_zip = "./lambdas/cognito-custom-auth.zip"
|
|
32
80
|
|
|
33
81
|
# Lambda artifacts:
|
|
34
82
|
# - Source checkout deploys can set lambda_zips_dir after `pnpm build:lambdas`.
|
|
@@ -65,3 +113,14 @@ db_password = "CHANGE_ME_strong_password_here"
|
|
|
65
113
|
# variable. NEVER put the token in this file.
|
|
66
114
|
# www_domain = "thinkwork.ai"
|
|
67
115
|
# cloudflare_zone_id = "your-cloudflare-zone-id"
|
|
116
|
+
|
|
117
|
+
# Tenant-scoped Space email addresses (optional).
|
|
118
|
+
# Keep the legacy agents.thinkwork.ai domain configured until retirement
|
|
119
|
+
# notices are no longer needed, then add tenant slugs before announcing
|
|
120
|
+
# <space>@<tenant>.thinkwork.ai addresses. Terraform creates one SES receiving
|
|
121
|
+
# identity and delegated Route53 subzone per tenant slug, then publishes NS
|
|
122
|
+
# delegation records in the Cloudflare parent zone above.
|
|
123
|
+
#
|
|
124
|
+
# ses_inbound_domain = "agents.thinkwork.ai"
|
|
125
|
+
# ses_parent_domain = "thinkwork.ai"
|
|
126
|
+
# tenant_slugs = ["dev"]
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
# Tags are immutable; a bump ships as a reviewable PR.
|
|
10
10
|
#
|
|
11
11
|
# Build context is the repository root so the COPY line can reach the
|
|
12
|
-
# Python source
|
|
12
|
+
# module-owned Python source below.
|
|
13
13
|
#
|
|
14
14
|
# docker build -f terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base .
|
|
15
15
|
|
|
@@ -47,7 +47,7 @@ RUN pip install --no-cache-dir \
|
|
|
47
47
|
# (and before the framework traceback on cold start). See the module
|
|
48
48
|
# docstring for the honestly-scoped invariant and named residual gaps.
|
|
49
49
|
# ---------------------------------------------------------------------------
|
|
50
|
-
COPY
|
|
50
|
+
COPY terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py \
|
|
51
51
|
/usr/local/lib/python3.12/site-packages/sitecustomize.py
|
|
52
52
|
|
|
53
53
|
# ---------------------------------------------------------------------------
|
|
@@ -19,16 +19,15 @@ stops at the substrate everything else depends on.
|
|
|
19
19
|
|
|
20
20
|
## Files
|
|
21
21
|
|
|
22
|
-
| File
|
|
23
|
-
|
|
24
|
-
| `main.tf`
|
|
25
|
-
| `Dockerfile.sandbox-base`
|
|
26
|
-
| `scripts/build_and_push_sandbox_base.sh` | CI builds + pushes. Not called from Terraform — image lifecycle is a reviewable-PR concern.
|
|
22
|
+
| File | Purpose |
|
|
23
|
+
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| `main.tf` | ECR + outputs. No per-tenant resources. |
|
|
25
|
+
| `Dockerfile.sandbox-base` | Python 3.12 + pinned libs + `sitecustomize.py`. Build context is the **repo root** so the COPY can reach the module-owned sandbox helper. |
|
|
26
|
+
| `scripts/build_and_push_sandbox_base.sh` | CI builds + pushes. Not called from Terraform — image lifecycle is a reviewable-PR concern. |
|
|
27
27
|
|
|
28
28
|
The Python source `sitecustomize.py` and its pytest suite `test_sitecustomize.py`
|
|
29
|
-
live under `
|
|
30
|
-
the
|
|
31
|
-
where `uv run pytest` finds them without extra config.
|
|
29
|
+
live under `terraform/modules/app/agentcore-code-interpreter/sandbox/`, next to
|
|
30
|
+
the sandbox base image they protect.
|
|
32
31
|
|
|
33
32
|
## What this module does **not** do
|
|
34
33
|
|
|
@@ -39,7 +38,7 @@ where `uv run pytest` finds them without extra config.
|
|
|
39
38
|
## Bumping the image
|
|
40
39
|
|
|
41
40
|
1. Edit `Dockerfile.sandbox-base` (pin a new pandas / add a lib / etc.).
|
|
42
|
-
2. Edit `
|
|
41
|
+
2. Edit `terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py` if the R13 invariant or its coverage gaps shift.
|
|
43
42
|
3. PR. CI runs `pytest` on the scrubber plus a build-then-startup-assertion smoke test.
|
|
44
43
|
4. After merge, `build_and_push_sandbox_base.sh` tags with the merge-commit SHA.
|
|
45
44
|
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""sitecustomize — primary R13 token redactor for the AgentCore sandbox base image.
|
|
2
|
+
|
|
3
|
+
Python auto-imports any module named ``sitecustomize`` during interpreter
|
|
4
|
+
startup (before user code, before the preamble, before framework tracebacks on
|
|
5
|
+
cold start). This file installs a stdio write-wrapper keyed on a session-scoped
|
|
6
|
+
set of token values so no token leaks to stdout / stderr via Python-stdio
|
|
7
|
+
writes.
|
|
8
|
+
|
|
9
|
+
The invariant is honestly scoped per the brainstorm's R13 (as aligned in
|
|
10
|
+
docs/brainstorms/2026-04-22-agentcore-code-sandbox-requirements.md):
|
|
11
|
+
|
|
12
|
+
"no token value reaches a persisted log ... via Python-stdio-mediated writes"
|
|
13
|
+
|
|
14
|
+
Bytes that bypass Python stdio (``os.write(fd, ...)``, subprocesses inheriting
|
|
15
|
+
fds, C extensions, multiprocessing children) are the named residual class
|
|
16
|
+
covered by the Unit 12 CloudWatch subscription-filter backstop for known
|
|
17
|
+
OAuth prefixes, not by this module.
|
|
18
|
+
|
|
19
|
+
Public API, callable from the sandbox preamble:
|
|
20
|
+
register_token(value: str) -> None
|
|
21
|
+
register_tokens(values: Iterable[str]) -> None
|
|
22
|
+
clear_tokens() -> None
|
|
23
|
+
installed() -> bool
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import base64
|
|
29
|
+
import binascii
|
|
30
|
+
import os
|
|
31
|
+
import sys
|
|
32
|
+
import urllib.parse
|
|
33
|
+
from collections.abc import Iterable
|
|
34
|
+
from typing import Any
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# Module state
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
# Every unique byte-sequence we redact. Populated by ``register_token`` — each
|
|
41
|
+
# registered token expands into its cleartext, standard base64, URL-safe
|
|
42
|
+
# base64, URL-encoded, and hex forms (only the forms whose length is at least
|
|
43
|
+
# MIN_TOKEN_LEN, to avoid redacting short accidental matches like "AQ").
|
|
44
|
+
_redactions: set[bytes] = set()
|
|
45
|
+
|
|
46
|
+
# Redaction placeholder. Length unimportant — stream layout survives because
|
|
47
|
+
# writes stay synchronous.
|
|
48
|
+
_REDACTION = b"<redacted>"
|
|
49
|
+
|
|
50
|
+
# Forms shorter than this are rejected — redacting 3-character strings is the
|
|
51
|
+
# shape of a wildcard bug, not a security control.
|
|
52
|
+
MIN_TOKEN_LEN = 12
|
|
53
|
+
|
|
54
|
+
# Rolling-suffix buffer size. Set at ``register_token`` time to
|
|
55
|
+
# max(existing, 2 * len(new_token_form)). The wrapper keeps this many bytes
|
|
56
|
+
# from the previous write and prepends them to the current write before
|
|
57
|
+
# searching, so a token split across two ``write()`` calls is caught as long
|
|
58
|
+
# as neither split half exceeds the buffer. Adversarial fragmentation at
|
|
59
|
+
# arbitrary positions is a named residual (see R13).
|
|
60
|
+
_rolling_buffer_size: int = 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _expand(value: str) -> list[bytes]:
|
|
64
|
+
"""Return the encoded forms of a token value we treat as equivalent.
|
|
65
|
+
|
|
66
|
+
Each form is added to the redaction set so ``print(base64.b64encode(token))``
|
|
67
|
+
and the cleartext ``print(token)`` are both scrubbed.
|
|
68
|
+
"""
|
|
69
|
+
if not value:
|
|
70
|
+
return []
|
|
71
|
+
raw = value.encode("utf-8")
|
|
72
|
+
forms: list[bytes] = []
|
|
73
|
+
|
|
74
|
+
def maybe_add(candidate: bytes) -> None:
|
|
75
|
+
if len(candidate) >= MIN_TOKEN_LEN:
|
|
76
|
+
forms.append(candidate)
|
|
77
|
+
|
|
78
|
+
maybe_add(raw)
|
|
79
|
+
maybe_add(base64.b64encode(raw).rstrip(b"="))
|
|
80
|
+
maybe_add(base64.b64encode(raw))
|
|
81
|
+
maybe_add(base64.urlsafe_b64encode(raw).rstrip(b"="))
|
|
82
|
+
maybe_add(base64.urlsafe_b64encode(raw))
|
|
83
|
+
maybe_add(urllib.parse.quote(value, safe="").encode("ascii"))
|
|
84
|
+
maybe_add(binascii.hexlify(raw))
|
|
85
|
+
return forms
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def register_token(value: str) -> None:
|
|
89
|
+
"""Add a token (and its derived encodings) to the redaction set.
|
|
90
|
+
|
|
91
|
+
Safe to call multiple times with the same value. No-op for empty values.
|
|
92
|
+
"""
|
|
93
|
+
global _rolling_buffer_size
|
|
94
|
+
for form in _expand(value):
|
|
95
|
+
_redactions.add(form)
|
|
96
|
+
required = 2 * len(form)
|
|
97
|
+
if required > _rolling_buffer_size:
|
|
98
|
+
_rolling_buffer_size = required
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def register_tokens(values: Iterable[str]) -> None:
|
|
102
|
+
for v in values:
|
|
103
|
+
register_token(v)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def clear_tokens() -> None:
|
|
107
|
+
"""Drop every registered token. Called at session teardown by the container."""
|
|
108
|
+
global _rolling_buffer_size
|
|
109
|
+
_redactions.clear()
|
|
110
|
+
_rolling_buffer_size = 0
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
# Stdio wrapper
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _redact(chunk: bytes, tail_buffer: bytearray) -> tuple[bytes, bytearray]:
|
|
119
|
+
"""Return (scrubbed_output_chunk, new_tail_buffer).
|
|
120
|
+
|
|
121
|
+
The tail buffer holds the trailing N bytes of the previous write so we can
|
|
122
|
+
catch tokens split across write boundaries. The output we emit is the part
|
|
123
|
+
of ``tail_buffer + chunk`` that is safely past the trailing window; the
|
|
124
|
+
suffix stays in the new tail for the next call.
|
|
125
|
+
"""
|
|
126
|
+
if not _redactions:
|
|
127
|
+
return chunk, tail_buffer
|
|
128
|
+
|
|
129
|
+
combined = bytes(tail_buffer) + chunk
|
|
130
|
+
for form in _redactions:
|
|
131
|
+
combined = combined.replace(form, _REDACTION)
|
|
132
|
+
|
|
133
|
+
if _rolling_buffer_size and len(combined) > _rolling_buffer_size:
|
|
134
|
+
emit = combined[:-_rolling_buffer_size]
|
|
135
|
+
new_tail = bytearray(combined[-_rolling_buffer_size:])
|
|
136
|
+
else:
|
|
137
|
+
emit = b""
|
|
138
|
+
new_tail = bytearray(combined)
|
|
139
|
+
|
|
140
|
+
return emit, new_tail
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class _RedactingStream:
|
|
144
|
+
"""Wraps sys.stdout / sys.stderr's underlying buffer with a redactor.
|
|
145
|
+
|
|
146
|
+
Intercepts both ``write(str)`` (TextIOBase) and ``.buffer.write(bytes)``
|
|
147
|
+
paths so ``print(...)``, ``sys.stdout.write(...)``, and
|
|
148
|
+
``sys.stdout.buffer.write(...)`` all flow through the scrubber.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def __init__(self, wrapped: Any, name: str) -> None:
|
|
152
|
+
self._wrapped = wrapped
|
|
153
|
+
self._name = name
|
|
154
|
+
self._tail = bytearray()
|
|
155
|
+
# AgentCore writes to stdout/stderr line-buffered; we flush our tail
|
|
156
|
+
# on every newline-terminated write so output appears promptly.
|
|
157
|
+
|
|
158
|
+
def write(self, data: Any) -> int:
|
|
159
|
+
if isinstance(data, str):
|
|
160
|
+
payload = data.encode("utf-8", errors="replace")
|
|
161
|
+
wrote_str = True
|
|
162
|
+
elif isinstance(data, (bytes, bytearray, memoryview)):
|
|
163
|
+
payload = bytes(data)
|
|
164
|
+
wrote_str = False
|
|
165
|
+
else:
|
|
166
|
+
# Defer unknown types to the wrapped object — let it raise.
|
|
167
|
+
return self._wrapped.write(data)
|
|
168
|
+
|
|
169
|
+
emit, self._tail = _redact(payload, self._tail)
|
|
170
|
+
# A newline forces a tail flush so output doesn't lag arbitrarily when
|
|
171
|
+
# no new writes arrive. This is a UX concession, not a security one —
|
|
172
|
+
# redaction is complete whether we flush or not.
|
|
173
|
+
if b"\n" in payload:
|
|
174
|
+
emit = emit + bytes(self._tail)
|
|
175
|
+
self._tail = bytearray()
|
|
176
|
+
|
|
177
|
+
if not emit:
|
|
178
|
+
return len(data) if wrote_str else len(payload)
|
|
179
|
+
|
|
180
|
+
if wrote_str:
|
|
181
|
+
self._wrapped.write(emit.decode("utf-8", errors="replace"))
|
|
182
|
+
else:
|
|
183
|
+
# TextIOWrapper.write raises on bytes; prefer the underlying buffer.
|
|
184
|
+
buf = getattr(self._wrapped, "buffer", None)
|
|
185
|
+
if buf is not None:
|
|
186
|
+
buf.write(emit)
|
|
187
|
+
else:
|
|
188
|
+
self._wrapped.write(emit)
|
|
189
|
+
return len(data) if wrote_str else len(payload)
|
|
190
|
+
|
|
191
|
+
def flush(self) -> None:
|
|
192
|
+
if self._tail:
|
|
193
|
+
data = bytes(self._tail)
|
|
194
|
+
self._tail = bytearray()
|
|
195
|
+
buf = getattr(self._wrapped, "buffer", None)
|
|
196
|
+
if buf is not None:
|
|
197
|
+
buf.write(data)
|
|
198
|
+
else:
|
|
199
|
+
self._wrapped.write(data.decode("utf-8", errors="replace"))
|
|
200
|
+
self._wrapped.flush()
|
|
201
|
+
|
|
202
|
+
# Forward any other attribute access (fileno, isatty, encoding, etc.) so
|
|
203
|
+
# libraries that introspect the stream still see what they expect.
|
|
204
|
+
def __getattr__(self, item: str) -> Any:
|
|
205
|
+
return getattr(self._wrapped, item)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
_installed = False
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def installed() -> bool:
|
|
212
|
+
return _installed
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _install() -> None:
|
|
216
|
+
global _installed
|
|
217
|
+
if _installed:
|
|
218
|
+
return
|
|
219
|
+
sys.stdout = _RedactingStream(sys.stdout, "stdout")
|
|
220
|
+
sys.stderr = _RedactingStream(sys.stderr, "stderr")
|
|
221
|
+
_installed = True
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# Install unconditionally on import. Disable only for a pytest diagnostic
|
|
225
|
+
# marker the test suite can opt out of (set THINKWORK_SANDBOX_SCRUBBER=off).
|
|
226
|
+
if os.environ.get("THINKWORK_SANDBOX_SCRUBBER", "on") != "off":
|
|
227
|
+
_install()
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Unit tests for the AgentCore sandbox base-image stdio token redactor.
|
|
2
|
+
|
|
3
|
+
Tests hit ``_RedactingStream`` directly with a test-owned stream instead of
|
|
4
|
+
mutating ``sys.stdout`` — that side-steps pytest's capture machinery, which
|
|
5
|
+
owns sys.stdout at test time and would otherwise see writes before the
|
|
6
|
+
wrapper does. Correctness of the sys.stdout replacement path is proven by
|
|
7
|
+
a separate end-to-end test that runs the module as a subprocess with its
|
|
8
|
+
auto-install path active.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import base64
|
|
14
|
+
import binascii
|
|
15
|
+
import importlib.util
|
|
16
|
+
import io
|
|
17
|
+
import os
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
import urllib.parse
|
|
21
|
+
|
|
22
|
+
import pytest
|
|
23
|
+
|
|
24
|
+
os.environ["THINKWORK_SANDBOX_SCRUBBER"] = "off"
|
|
25
|
+
|
|
26
|
+
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
27
|
+
_SPEC = importlib.util.spec_from_file_location(
|
|
28
|
+
"sandbox_base_sitecustomize",
|
|
29
|
+
os.path.join(_HERE, "sitecustomize.py"),
|
|
30
|
+
)
|
|
31
|
+
assert _SPEC is not None and _SPEC.loader is not None
|
|
32
|
+
sc = importlib.util.module_from_spec(_SPEC)
|
|
33
|
+
_SPEC.loader.exec_module(sc)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class _StringSink:
|
|
37
|
+
"""Minimal sys.stdout-shaped target: the wrapper writes strings into us,
|
|
38
|
+
we collect them as UTF-8 bytes. Also exposes ``.buffer`` so bytes-path
|
|
39
|
+
writes have a path through the wrapper."""
|
|
40
|
+
|
|
41
|
+
def __init__(self) -> None:
|
|
42
|
+
self.str_bytes = bytearray()
|
|
43
|
+
self.buffer = io.BytesIO()
|
|
44
|
+
self.name = "test-sink"
|
|
45
|
+
self.encoding = "utf-8"
|
|
46
|
+
self.errors = "strict"
|
|
47
|
+
|
|
48
|
+
def write(self, s: str) -> int:
|
|
49
|
+
self.str_bytes.extend(s.encode("utf-8", errors="replace"))
|
|
50
|
+
return len(s)
|
|
51
|
+
|
|
52
|
+
def flush(self) -> None:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
def captured(self) -> str:
|
|
56
|
+
combined = bytes(self.str_bytes) + self.buffer.getvalue()
|
|
57
|
+
return combined.decode("utf-8", errors="replace")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.fixture
|
|
61
|
+
def wrapper() -> tuple:
|
|
62
|
+
"""A fresh redactor wrapping a new sink, with an empty token set."""
|
|
63
|
+
sc.clear_tokens()
|
|
64
|
+
sink = _StringSink()
|
|
65
|
+
stream = sc._RedactingStream(sink, "test-stdout") # type: ignore[attr-defined]
|
|
66
|
+
yield stream, sink
|
|
67
|
+
stream.flush()
|
|
68
|
+
sc.clear_tokens()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_cleartext_is_redacted(wrapper):
|
|
72
|
+
stream, sink = wrapper
|
|
73
|
+
token = "ghp_abc123abc123abc123"
|
|
74
|
+
sc.register_token(token)
|
|
75
|
+
stream.write(f"the token was {token} and not noticeable\n")
|
|
76
|
+
stream.flush()
|
|
77
|
+
text = sink.captured()
|
|
78
|
+
assert token not in text
|
|
79
|
+
assert "<redacted>" in text
|
|
80
|
+
assert "the token was" in text
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_base64_form_is_redacted(wrapper):
|
|
84
|
+
stream, sink = wrapper
|
|
85
|
+
token = "ghp_abc123abc123abc123"
|
|
86
|
+
sc.register_token(token)
|
|
87
|
+
encoded = base64.b64encode(token.encode()).decode()
|
|
88
|
+
stream.write(f"encoded={encoded}\n")
|
|
89
|
+
stream.flush()
|
|
90
|
+
text = sink.captured()
|
|
91
|
+
assert encoded not in text
|
|
92
|
+
assert "<redacted>" in text
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_urlsafe_base64_form_is_redacted(wrapper):
|
|
96
|
+
stream, sink = wrapper
|
|
97
|
+
token = "some_opaque_secret_for_urlsafe"
|
|
98
|
+
sc.register_token(token)
|
|
99
|
+
encoded = base64.urlsafe_b64encode(token.encode()).decode()
|
|
100
|
+
stream.write(f"encoded={encoded}\n")
|
|
101
|
+
stream.flush()
|
|
102
|
+
text = sink.captured()
|
|
103
|
+
assert encoded not in text
|
|
104
|
+
assert "<redacted>" in text
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_url_encoded_form_is_redacted(wrapper):
|
|
108
|
+
stream, sink = wrapper
|
|
109
|
+
token = "token with spaces & special=chars"
|
|
110
|
+
sc.register_token(token)
|
|
111
|
+
encoded = urllib.parse.quote(token, safe="")
|
|
112
|
+
stream.write(f"in_url={encoded}\n")
|
|
113
|
+
stream.flush()
|
|
114
|
+
text = sink.captured()
|
|
115
|
+
assert encoded not in text
|
|
116
|
+
assert "<redacted>" in text
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_hex_form_is_redacted(wrapper):
|
|
120
|
+
stream, sink = wrapper
|
|
121
|
+
token = "0123456789abcdef01234567"
|
|
122
|
+
sc.register_token(token)
|
|
123
|
+
encoded = binascii.hexlify(token.encode()).decode()
|
|
124
|
+
stream.write(f"hex={encoded}\n")
|
|
125
|
+
stream.flush()
|
|
126
|
+
text = sink.captured()
|
|
127
|
+
assert encoded not in text
|
|
128
|
+
assert "<redacted>" in text
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_register_after_first_write_still_redacts_subsequent(wrapper):
|
|
132
|
+
stream, sink = wrapper
|
|
133
|
+
stream.write("before registration\n")
|
|
134
|
+
token = "late_registered_token_value"
|
|
135
|
+
sc.register_token(token)
|
|
136
|
+
stream.write(f"after {token} registration\n")
|
|
137
|
+
stream.flush()
|
|
138
|
+
text = sink.captured()
|
|
139
|
+
assert "before registration" in text
|
|
140
|
+
assert token not in text
|
|
141
|
+
assert "<redacted>" in text
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_split_write_across_boundary_is_caught(wrapper):
|
|
145
|
+
stream, sink = wrapper
|
|
146
|
+
token = "ghp_splitfragmenttokenvalue"
|
|
147
|
+
sc.register_token(token)
|
|
148
|
+
stream.write(token[:10])
|
|
149
|
+
stream.write(token[10:])
|
|
150
|
+
stream.write("\n")
|
|
151
|
+
stream.flush()
|
|
152
|
+
text = sink.captured()
|
|
153
|
+
assert token not in text
|
|
154
|
+
assert "<redacted>" in text
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_short_token_below_min_len_is_ignored(wrapper):
|
|
158
|
+
"""Redacting 3-char strings would produce wildcard false-positives."""
|
|
159
|
+
stream, sink = wrapper
|
|
160
|
+
sc.register_token("AQ")
|
|
161
|
+
stream.write("AQ should appear unchanged because AQ is too short to redact\n")
|
|
162
|
+
stream.flush()
|
|
163
|
+
text = sink.captured()
|
|
164
|
+
assert "AQ" in text
|
|
165
|
+
assert "<redacted>" not in text
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_bytes_path_through_buffer(wrapper):
|
|
169
|
+
stream, sink = wrapper
|
|
170
|
+
token = "some_token_value_we_registered"
|
|
171
|
+
sc.register_token(token)
|
|
172
|
+
# Write bytes through the wrapper — it routes into sink.buffer.
|
|
173
|
+
stream.write(b"\x00\x01\x02binary with " + token.encode() + b" embedded\n")
|
|
174
|
+
stream.flush()
|
|
175
|
+
text = sink.captured()
|
|
176
|
+
assert token not in text
|
|
177
|
+
assert "<redacted>" in text
|
|
178
|
+
assert "binary with" in text
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_clear_tokens_disables_redaction_for_later_writes(wrapper):
|
|
182
|
+
stream, sink = wrapper
|
|
183
|
+
token = "a_token_that_will_be_cleared_soon"
|
|
184
|
+
sc.register_token(token)
|
|
185
|
+
stream.write(f"before clear: {token}\n")
|
|
186
|
+
sc.clear_tokens()
|
|
187
|
+
stream.write(f"after clear: {token}\n")
|
|
188
|
+
stream.flush()
|
|
189
|
+
text = sink.captured()
|
|
190
|
+
assert text.count("<redacted>") == 1
|
|
191
|
+
assert text.count(token) == 1
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_register_tokens_plural_is_equivalent(wrapper):
|
|
195
|
+
stream, sink = wrapper
|
|
196
|
+
sc.register_tokens(["first_registered_token_abc", "second_registered_token_def"])
|
|
197
|
+
stream.write("first_registered_token_abc / second_registered_token_def\n")
|
|
198
|
+
stream.flush()
|
|
199
|
+
text = sink.captured()
|
|
200
|
+
assert "first_registered_token_abc" not in text
|
|
201
|
+
assert "second_registered_token_def" not in text
|
|
202
|
+
assert text.count("<redacted>") >= 2
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_empty_token_is_ignored(wrapper):
|
|
206
|
+
stream, sink = wrapper
|
|
207
|
+
sc.register_token("")
|
|
208
|
+
stream.write("nothing to redact here\n")
|
|
209
|
+
stream.flush()
|
|
210
|
+
text = sink.captured()
|
|
211
|
+
assert "nothing to redact here" in text
|
|
212
|
+
assert "<redacted>" not in text
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_no_crash_on_raw_bytes_without_matching_token(wrapper):
|
|
216
|
+
stream, sink = wrapper
|
|
217
|
+
sc.register_token("something_registered_but_not_present")
|
|
218
|
+
stream.write(b"\x00\x01\x02binary bytes\n")
|
|
219
|
+
stream.flush()
|
|
220
|
+
text = sink.captured()
|
|
221
|
+
assert "binary bytes" in text
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# --- End-to-end: the auto-install path ---------------------------------------
|
|
225
|
+
#
|
|
226
|
+
# Tests above exercise _RedactingStream directly, which is fine for the
|
|
227
|
+
# redaction logic itself but bypasses the sitecustomize auto-install step
|
|
228
|
+
# that replaces sys.stdout on interpreter startup. This subprocess test
|
|
229
|
+
# proves that step works — a fresh Python process, with PYTHONPATH pointed
|
|
230
|
+
# at the module directory and THINKWORK_SANDBOX_SCRUBBER unset, installs
|
|
231
|
+
# the wrapper on its own and redacts a registered token before stdout is
|
|
232
|
+
# flushed.
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def test_auto_install_redacts_on_fresh_interpreter(tmp_path):
|
|
236
|
+
script = tmp_path / "run.py"
|
|
237
|
+
script.write_text(
|
|
238
|
+
"""
|
|
239
|
+
import sys, sitecustomize
|
|
240
|
+
sitecustomize.register_token("ghp_autoInstallValueTokenX")
|
|
241
|
+
print("auto-install redacts ghp_autoInstallValueTokenX mid-line here")
|
|
242
|
+
""".strip()
|
|
243
|
+
)
|
|
244
|
+
env = os.environ.copy()
|
|
245
|
+
# Strip our opt-out so sitecustomize installs on import.
|
|
246
|
+
env.pop("THINKWORK_SANDBOX_SCRUBBER", None)
|
|
247
|
+
# Prepend the module dir so Python's startup hook finds our
|
|
248
|
+
# sitecustomize.py before any installed one.
|
|
249
|
+
env["PYTHONPATH"] = _HERE + os.pathsep + env.get("PYTHONPATH", "")
|
|
250
|
+
out = subprocess.run(
|
|
251
|
+
[sys.executable, str(script)],
|
|
252
|
+
env=env,
|
|
253
|
+
check=True,
|
|
254
|
+
capture_output=True,
|
|
255
|
+
text=True,
|
|
256
|
+
)
|
|
257
|
+
assert "ghp_autoInstallValueTokenX" not in out.stdout
|
|
258
|
+
assert "<redacted>" in out.stdout
|
|
259
|
+
assert "auto-install redacts" in out.stdout
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_installed_reports_true_after_auto_install(tmp_path):
|
|
263
|
+
"""Probe ``installed()`` from a fresh interpreter — confirms the startup
|
|
264
|
+
hook ran (and set the flag)."""
|
|
265
|
+
script = tmp_path / "run.py"
|
|
266
|
+
script.write_text("import sitecustomize; print(sitecustomize.installed())")
|
|
267
|
+
env = os.environ.copy()
|
|
268
|
+
env.pop("THINKWORK_SANDBOX_SCRUBBER", None)
|
|
269
|
+
env["PYTHONPATH"] = _HERE + os.pathsep + env.get("PYTHONPATH", "")
|
|
270
|
+
out = subprocess.run(
|
|
271
|
+
[sys.executable, str(script)],
|
|
272
|
+
env=env,
|
|
273
|
+
check=True,
|
|
274
|
+
capture_output=True,
|
|
275
|
+
text=True,
|
|
276
|
+
)
|
|
277
|
+
assert out.stdout.strip() == "True"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# AgentCore Memory — App Module
|
|
2
2
|
|
|
3
3
|
Provisions an AWS Bedrock AgentCore Memory resource with the four strategies
|
|
4
|
-
the
|
|
4
|
+
the ThinkWork AgentCore runtime uses for automatic retention:
|
|
5
5
|
|
|
6
|
-
| Strategy | Namespace template
|
|
7
|
-
|
|
8
|
-
| semantic | `assistant_{actorId}`
|
|
9
|
-
| preferences | `preferences_{actorId}`
|
|
10
|
-
| summaries | `session_{sessionId}`
|
|
6
|
+
| Strategy | Namespace template | Purpose |
|
|
7
|
+
| ----------- | -------------------------------- | ------------------------------------ |
|
|
8
|
+
| semantic | `assistant_{actorId}` | Cross-thread facts about the user |
|
|
9
|
+
| preferences | `preferences_{actorId}` | User-stated preferences |
|
|
10
|
+
| summaries | `session_{sessionId}` | Per-thread rolling summaries |
|
|
11
11
|
| episodes | `episodes_{actorId}/{sessionId}` | Episodic memory of past interactions |
|
|
12
12
|
|
|
13
13
|
Automatic retention is wired in the agent container: every turn emits a
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# AgentCore Memory — App Module
|
|
3
3
|
#
|
|
4
4
|
# Provisions an AWS Bedrock AgentCore Memory resource with the four strategies
|
|
5
|
-
# the
|
|
5
|
+
# the ThinkWork AgentCore runtime expects (semantic, preferences, summaries,
|
|
6
6
|
# episodes). The resource is always created — AgentCore managed memory is
|
|
7
7
|
# on by default so every agent gets automatic per-turn retention into
|
|
8
8
|
# semantic / preference / summary / episode strategies without any tool-
|