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
@@ -62,6 +62,15 @@
62
62
  -- creates-policy: public.agent_operation_leases.analyst_tenant_isolation
63
63
  -- creates-policy: public.eval_results.analyst_tenant_isolation
64
64
  -- creates-policy: public.plugin_components.analyst_tenant_isolation
65
+ --
66
+ -- THINK-84 U6: the msteams tables landed after this file was first applied,
67
+ -- so their policies are declared explicitly — the drift gate must force a
68
+ -- re-apply on databases that ran 0230 before the msteams tables existed.
69
+ -- (0227's grants for the same tables are not marker-checkable; re-apply
70
+ -- 0227 alongside 0233 and this file — see the 0233 header.)
71
+ -- creates-policy: public.msteams_tenant_installs.analyst_tenant_isolation
72
+ -- creates-policy: public.msteams_user_links.analyst_tenant_isolation
73
+ -- creates-policy: public.msteams_threads.analyst_tenant_isolation
65
74
 
66
75
  \set ON_ERROR_STOP on
67
76
 
@@ -335,32 +344,59 @@ BEGIN
335
344
  ELSE
336
345
  missing := missing || 'billing_export_line_items'::text;
337
346
  END IF;
338
- IF to_regclass('public.brain_dream_actions') IS NOT NULL THEN
339
- ALTER TABLE public.brain_dream_actions ENABLE ROW LEVEL SECURITY;
340
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.brain_dream_actions;
341
- CREATE POLICY analyst_tenant_isolation ON public.brain_dream_actions
347
+ IF to_regclass('public.budget_policies') IS NOT NULL THEN
348
+ ALTER TABLE public.budget_policies ENABLE ROW LEVEL SECURITY;
349
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.budget_policies;
350
+ CREATE POLICY analyst_tenant_isolation ON public.budget_policies
342
351
  FOR SELECT TO analyst_reader
343
- USING (EXISTS (SELECT 1 FROM public.brain_dream_runs p WHERE p.id = public.brain_dream_actions.run_id AND p.tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid));
352
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
344
353
  ELSE
345
- missing := missing || 'brain_dream_actions'::text;
354
+ missing := missing || 'budget_policies'::text;
346
355
  END IF;
347
- IF to_regclass('public.brain_dream_runs') IS NOT NULL THEN
348
- ALTER TABLE public.brain_dream_runs ENABLE ROW LEVEL SECURITY;
349
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.brain_dream_runs;
350
- CREATE POLICY analyst_tenant_isolation ON public.brain_dream_runs
356
+ IF to_regclass('public.capability_broker_calls') IS NOT NULL THEN
357
+ ALTER TABLE public.capability_broker_calls ENABLE ROW LEVEL SECURITY;
358
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.capability_broker_calls;
359
+ CREATE POLICY analyst_tenant_isolation ON public.capability_broker_calls
351
360
  FOR SELECT TO analyst_reader
352
361
  USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
353
362
  ELSE
354
- missing := missing || 'brain_dream_runs'::text;
363
+ missing := missing || 'capability_broker_calls'::text;
355
364
  END IF;
356
- IF to_regclass('public.budget_policies') IS NOT NULL THEN
357
- ALTER TABLE public.budget_policies ENABLE ROW LEVEL SECURITY;
358
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.budget_policies;
359
- CREATE POLICY analyst_tenant_isolation ON public.budget_policies
365
+ IF to_regclass('public.capability_broker_sessions') IS NOT NULL THEN
366
+ ALTER TABLE public.capability_broker_sessions ENABLE ROW LEVEL SECURITY;
367
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.capability_broker_sessions;
368
+ CREATE POLICY analyst_tenant_isolation ON public.capability_broker_sessions
360
369
  FOR SELECT TO analyst_reader
361
370
  USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
362
371
  ELSE
363
- missing := missing || 'budget_policies'::text;
372
+ missing := missing || 'capability_broker_sessions'::text;
373
+ END IF;
374
+ IF to_regclass('public.capability_connection_proposals') IS NOT NULL THEN
375
+ ALTER TABLE public.capability_connection_proposals ENABLE ROW LEVEL SECURITY;
376
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.capability_connection_proposals;
377
+ CREATE POLICY analyst_tenant_isolation ON public.capability_connection_proposals
378
+ FOR SELECT TO analyst_reader
379
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
380
+ ELSE
381
+ missing := missing || 'capability_connection_proposals'::text;
382
+ END IF;
383
+ IF to_regclass('public.capability_definitions') IS NOT NULL THEN
384
+ ALTER TABLE public.capability_definitions ENABLE ROW LEVEL SECURITY;
385
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.capability_definitions;
386
+ CREATE POLICY analyst_tenant_isolation ON public.capability_definitions
387
+ FOR SELECT TO analyst_reader
388
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
389
+ ELSE
390
+ missing := missing || 'capability_definitions'::text;
391
+ END IF;
392
+ IF to_regclass('public.capability_routine_proposals') IS NOT NULL THEN
393
+ ALTER TABLE public.capability_routine_proposals ENABLE ROW LEVEL SECURITY;
394
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.capability_routine_proposals;
395
+ CREATE POLICY analyst_tenant_isolation ON public.capability_routine_proposals
396
+ FOR SELECT TO analyst_reader
397
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
398
+ ELSE
399
+ missing := missing || 'capability_routine_proposals'::text;
364
400
  END IF;
365
401
  IF to_regclass('public.connections') IS NOT NULL THEN
366
402
  ALTER TABLE public.connections ENABLE ROW LEVEL SECURITY;
@@ -632,6 +668,15 @@ BEGIN
632
668
  ELSE
633
669
  missing := missing || 'guardrails'::text;
634
670
  END IF;
671
+ IF to_regclass('public.harness_tool_execution_events') IS NOT NULL THEN
672
+ ALTER TABLE public.harness_tool_execution_events ENABLE ROW LEVEL SECURITY;
673
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.harness_tool_execution_events;
674
+ CREATE POLICY analyst_tenant_isolation ON public.harness_tool_execution_events
675
+ FOR SELECT TO analyst_reader
676
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
677
+ ELSE
678
+ missing := missing || 'harness_tool_execution_events'::text;
679
+ END IF;
635
680
  IF to_regclass('public.inbox_item_comments') IS NOT NULL THEN
636
681
  ALTER TABLE public.inbox_item_comments ENABLE ROW LEVEL SECURITY;
637
682
  DROP POLICY IF EXISTS analyst_tenant_isolation ON public.inbox_item_comments;
@@ -659,6 +704,15 @@ BEGIN
659
704
  ELSE
660
705
  missing := missing || 'inbox_items'::text;
661
706
  END IF;
707
+ IF to_regclass('public.knowledge_base_documents') IS NOT NULL THEN
708
+ ALTER TABLE public.knowledge_base_documents ENABLE ROW LEVEL SECURITY;
709
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.knowledge_base_documents;
710
+ CREATE POLICY analyst_tenant_isolation ON public.knowledge_base_documents
711
+ FOR SELECT TO analyst_reader
712
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
713
+ ELSE
714
+ missing := missing || 'knowledge_base_documents'::text;
715
+ END IF;
662
716
  IF to_regclass('public.knowledge_bases') IS NOT NULL THEN
663
717
  ALTER TABLE public.knowledge_bases ENABLE ROW LEVEL SECURITY;
664
718
  DROP POLICY IF EXISTS analyst_tenant_isolation ON public.knowledge_bases;
@@ -668,51 +722,6 @@ BEGIN
668
722
  ELSE
669
723
  missing := missing || 'knowledge_bases'::text;
670
724
  END IF;
671
- IF to_regclass('public.knowledge_graph_entities') IS NOT NULL THEN
672
- ALTER TABLE public.knowledge_graph_entities ENABLE ROW LEVEL SECURITY;
673
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.knowledge_graph_entities;
674
- CREATE POLICY analyst_tenant_isolation ON public.knowledge_graph_entities
675
- FOR SELECT TO analyst_reader
676
- USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
677
- ELSE
678
- missing := missing || 'knowledge_graph_entities'::text;
679
- END IF;
680
- IF to_regclass('public.knowledge_graph_evidence') IS NOT NULL THEN
681
- ALTER TABLE public.knowledge_graph_evidence ENABLE ROW LEVEL SECURITY;
682
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.knowledge_graph_evidence;
683
- CREATE POLICY analyst_tenant_isolation ON public.knowledge_graph_evidence
684
- FOR SELECT TO analyst_reader
685
- USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
686
- ELSE
687
- missing := missing || 'knowledge_graph_evidence'::text;
688
- END IF;
689
- IF to_regclass('public.knowledge_graph_ingest_runs') IS NOT NULL THEN
690
- ALTER TABLE public.knowledge_graph_ingest_runs ENABLE ROW LEVEL SECURITY;
691
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.knowledge_graph_ingest_runs;
692
- CREATE POLICY analyst_tenant_isolation ON public.knowledge_graph_ingest_runs
693
- FOR SELECT TO analyst_reader
694
- USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
695
- ELSE
696
- missing := missing || 'knowledge_graph_ingest_runs'::text;
697
- END IF;
698
- IF to_regclass('public.knowledge_graph_observation_cursors') IS NOT NULL THEN
699
- ALTER TABLE public.knowledge_graph_observation_cursors ENABLE ROW LEVEL SECURITY;
700
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.knowledge_graph_observation_cursors;
701
- CREATE POLICY analyst_tenant_isolation ON public.knowledge_graph_observation_cursors
702
- FOR SELECT TO analyst_reader
703
- USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
704
- ELSE
705
- missing := missing || 'knowledge_graph_observation_cursors'::text;
706
- END IF;
707
- IF to_regclass('public.knowledge_graph_relationships') IS NOT NULL THEN
708
- ALTER TABLE public.knowledge_graph_relationships ENABLE ROW LEVEL SECURITY;
709
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.knowledge_graph_relationships;
710
- CREATE POLICY analyst_tenant_isolation ON public.knowledge_graph_relationships
711
- FOR SELECT TO analyst_reader
712
- USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
713
- ELSE
714
- missing := missing || 'knowledge_graph_relationships'::text;
715
- END IF;
716
725
  IF to_regclass('public.linked_task_events') IS NOT NULL THEN
717
726
  ALTER TABLE public.linked_task_events ENABLE ROW LEVEL SECURITY;
718
727
  DROP POLICY IF EXISTS analyst_tenant_isolation ON public.linked_task_events;
@@ -758,14 +767,95 @@ BEGIN
758
767
  ELSE
759
768
  missing := missing || 'managed_applications'::text;
760
769
  END IF;
761
- IF to_regclass('public.memory_retain_attempts') IS NOT NULL THEN
762
- ALTER TABLE public.memory_retain_attempts ENABLE ROW LEVEL SECURITY;
763
- DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_retain_attempts;
764
- CREATE POLICY analyst_tenant_isolation ON public.memory_retain_attempts
770
+ IF to_regclass('public.memory_claim_evidence') IS NOT NULL THEN
771
+ ALTER TABLE public.memory_claim_evidence ENABLE ROW LEVEL SECURITY;
772
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_claim_evidence;
773
+ CREATE POLICY analyst_tenant_isolation ON public.memory_claim_evidence
765
774
  FOR SELECT TO analyst_reader
766
775
  USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
767
776
  ELSE
768
- missing := missing || 'memory_retain_attempts'::text;
777
+ missing := missing || 'memory_claim_evidence'::text;
778
+ END IF;
779
+ IF to_regclass('public.memory_claims') IS NOT NULL THEN
780
+ ALTER TABLE public.memory_claims ENABLE ROW LEVEL SECURITY;
781
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_claims;
782
+ CREATE POLICY analyst_tenant_isolation ON public.memory_claims
783
+ FOR SELECT TO analyst_reader
784
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
785
+ ELSE
786
+ missing := missing || 'memory_claims'::text;
787
+ END IF;
788
+ IF to_regclass('public.memory_derivations') IS NOT NULL THEN
789
+ ALTER TABLE public.memory_derivations ENABLE ROW LEVEL SECURITY;
790
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_derivations;
791
+ CREATE POLICY analyst_tenant_isolation ON public.memory_derivations
792
+ FOR SELECT TO analyst_reader
793
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
794
+ ELSE
795
+ missing := missing || 'memory_derivations'::text;
796
+ END IF;
797
+ IF to_regclass('public.memory_evidence_items') IS NOT NULL THEN
798
+ ALTER TABLE public.memory_evidence_items ENABLE ROW LEVEL SECURITY;
799
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_evidence_items;
800
+ CREATE POLICY analyst_tenant_isolation ON public.memory_evidence_items
801
+ FOR SELECT TO analyst_reader
802
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
803
+ ELSE
804
+ missing := missing || 'memory_evidence_items'::text;
805
+ END IF;
806
+ IF to_regclass('public.memory_processor_configs') IS NOT NULL THEN
807
+ ALTER TABLE public.memory_processor_configs ENABLE ROW LEVEL SECURITY;
808
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_processor_configs;
809
+ CREATE POLICY analyst_tenant_isolation ON public.memory_processor_configs
810
+ FOR SELECT TO analyst_reader
811
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
812
+ ELSE
813
+ missing := missing || 'memory_processor_configs'::text;
814
+ END IF;
815
+ IF to_regclass('public.memory_retraction_attempts') IS NOT NULL THEN
816
+ ALTER TABLE public.memory_retraction_attempts ENABLE ROW LEVEL SECURITY;
817
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_retraction_attempts;
818
+ CREATE POLICY analyst_tenant_isolation ON public.memory_retraction_attempts
819
+ FOR SELECT TO analyst_reader
820
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
821
+ ELSE
822
+ missing := missing || 'memory_retraction_attempts'::text;
823
+ END IF;
824
+ IF to_regclass('public.memory_run_items') IS NOT NULL THEN
825
+ ALTER TABLE public.memory_run_items ENABLE ROW LEVEL SECURITY;
826
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_run_items;
827
+ CREATE POLICY analyst_tenant_isolation ON public.memory_run_items
828
+ FOR SELECT TO analyst_reader
829
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
830
+ ELSE
831
+ missing := missing || 'memory_run_items'::text;
832
+ END IF;
833
+ IF to_regclass('public.memory_source_authorizations') IS NOT NULL THEN
834
+ ALTER TABLE public.memory_source_authorizations ENABLE ROW LEVEL SECURITY;
835
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_source_authorizations;
836
+ CREATE POLICY analyst_tenant_isolation ON public.memory_source_authorizations
837
+ FOR SELECT TO analyst_reader
838
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
839
+ ELSE
840
+ missing := missing || 'memory_source_authorizations'::text;
841
+ END IF;
842
+ IF to_regclass('public.memory_source_checkpoints') IS NOT NULL THEN
843
+ ALTER TABLE public.memory_source_checkpoints ENABLE ROW LEVEL SECURITY;
844
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_source_checkpoints;
845
+ CREATE POLICY analyst_tenant_isolation ON public.memory_source_checkpoints
846
+ FOR SELECT TO analyst_reader
847
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
848
+ ELSE
849
+ missing := missing || 'memory_source_checkpoints'::text;
850
+ END IF;
851
+ IF to_regclass('public.memory_source_configs') IS NOT NULL THEN
852
+ ALTER TABLE public.memory_source_configs ENABLE ROW LEVEL SECURITY;
853
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.memory_source_configs;
854
+ CREATE POLICY analyst_tenant_isolation ON public.memory_source_configs
855
+ FOR SELECT TO analyst_reader
856
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
857
+ ELSE
858
+ missing := missing || 'memory_source_configs'::text;
769
859
  END IF;
770
860
  IF to_regclass('public.message_artifacts') IS NOT NULL THEN
771
861
  ALTER TABLE public.message_artifacts ENABLE ROW LEVEL SECURITY;
@@ -794,6 +884,33 @@ BEGIN
794
884
  ELSE
795
885
  missing := missing || 'messages'::text;
796
886
  END IF;
887
+ IF to_regclass('public.msteams_tenant_installs') IS NOT NULL THEN
888
+ ALTER TABLE public.msteams_tenant_installs ENABLE ROW LEVEL SECURITY;
889
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.msteams_tenant_installs;
890
+ CREATE POLICY analyst_tenant_isolation ON public.msteams_tenant_installs
891
+ FOR SELECT TO analyst_reader
892
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
893
+ ELSE
894
+ missing := missing || 'msteams_tenant_installs'::text;
895
+ END IF;
896
+ IF to_regclass('public.msteams_threads') IS NOT NULL THEN
897
+ ALTER TABLE public.msteams_threads ENABLE ROW LEVEL SECURITY;
898
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.msteams_threads;
899
+ CREATE POLICY analyst_tenant_isolation ON public.msteams_threads
900
+ FOR SELECT TO analyst_reader
901
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
902
+ ELSE
903
+ missing := missing || 'msteams_threads'::text;
904
+ END IF;
905
+ IF to_regclass('public.msteams_user_links') IS NOT NULL THEN
906
+ ALTER TABLE public.msteams_user_links ENABLE ROW LEVEL SECURITY;
907
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.msteams_user_links;
908
+ CREATE POLICY analyst_tenant_isolation ON public.msteams_user_links
909
+ FOR SELECT TO analyst_reader
910
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
911
+ ELSE
912
+ missing := missing || 'msteams_user_links'::text;
913
+ END IF;
797
914
  IF to_regclass('public.mutation_idempotency') IS NOT NULL THEN
798
915
  ALTER TABLE public.mutation_idempotency ENABLE ROW LEVEL SECURITY;
799
916
  DROP POLICY IF EXISTS analyst_tenant_isolation ON public.mutation_idempotency;
@@ -1226,6 +1343,15 @@ BEGIN
1226
1343
  ELSE
1227
1344
  missing := missing || 'tenant_policy_events'::text;
1228
1345
  END IF;
1346
+ IF to_regclass('public.tenant_service_principals') IS NOT NULL THEN
1347
+ ALTER TABLE public.tenant_service_principals ENABLE ROW LEVEL SECURITY;
1348
+ DROP POLICY IF EXISTS analyst_tenant_isolation ON public.tenant_service_principals;
1349
+ CREATE POLICY analyst_tenant_isolation ON public.tenant_service_principals
1350
+ FOR SELECT TO analyst_reader
1351
+ USING (tenant_id = current_setting('thinkwork.analyst_tenant', true)::uuid);
1352
+ ELSE
1353
+ missing := missing || 'tenant_service_principals'::text;
1354
+ END IF;
1229
1355
  IF to_regclass('public.tenant_settings') IS NOT NULL THEN
1230
1356
  ALTER TABLE public.tenant_settings ENABLE ROW LEVEL SECURITY;
1231
1357
  DROP POLICY IF EXISTS analyst_tenant_isolation ON public.tenant_settings;
@@ -0,0 +1,176 @@
1
+ -- THINK-193 U1: external-memory-compounding thin ledger.
2
+ -- Contents:
3
+ -- * memory_processor_configs — one processor per shared scope (partial
4
+ -- unique on active target).
5
+ -- * memory_source_configs — source bindings under a processor.
6
+ -- * memory_source_checkpoints — CAS cursor per source partition.
7
+ -- * memory_evidence_items — evidence ledger with lifecycle + lineage inputs.
8
+ -- * memory_run_items — per-run stage idempotency ledger.
9
+ -- * memory_derivations — evidence -> Hindsight projection lineage.
10
+ --
11
+ -- NOT done here (deliberately): no claims tables, no identity tables —
12
+ -- thin U1 set only.
13
+ --
14
+ -- Hand-rolled (drizzle meta journal is not in use for this change);
15
+ -- apply with: psql "$DATABASE_URL" -f drizzle/0232_memory_source_thin_ledger.sql
16
+ -- creates: public.memory_processor_configs
17
+ -- creates: public.memory_source_configs
18
+ -- creates: public.memory_source_checkpoints
19
+ -- creates: public.memory_evidence_items
20
+ -- creates: public.memory_run_items
21
+ -- creates: public.memory_derivations
22
+
23
+ CREATE TABLE IF NOT EXISTS public.memory_processor_configs (
24
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
25
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
26
+ mode text NOT NULL,
27
+ target_scope text NOT NULL,
28
+ target_id uuid NOT NULL,
29
+ workflow_id uuid REFERENCES public.workflows(id) ON DELETE SET NULL,
30
+ enabled boolean NOT NULL DEFAULT true,
31
+ status text NOT NULL DEFAULT 'active',
32
+ budget jsonb NOT NULL DEFAULT '{}'::jsonb,
33
+ config_version integer NOT NULL DEFAULT 1,
34
+ created_by_user_id uuid REFERENCES public.users(id) ON DELETE SET NULL,
35
+ created_at timestamptz NOT NULL DEFAULT now(),
36
+ updated_at timestamptz NOT NULL DEFAULT now(),
37
+ CONSTRAINT memory_processor_configs_mode_check
38
+ CHECK (mode IN ('personal', 'shared')),
39
+ CONSTRAINT memory_processor_configs_target_scope_check
40
+ CHECK (target_scope IN ('user', 'space', 'tenant')),
41
+ CONSTRAINT memory_processor_configs_status_check
42
+ CHECK (status IN ('active', 'disabled'))
43
+ );
44
+
45
+ CREATE UNIQUE INDEX IF NOT EXISTS memory_processor_configs_active_target_uidx
46
+ ON public.memory_processor_configs (tenant_id, mode, target_scope, target_id)
47
+ WHERE status = 'active';
48
+
49
+ CREATE INDEX IF NOT EXISTS memory_processor_configs_tenant_idx
50
+ ON public.memory_processor_configs (tenant_id);
51
+
52
+ CREATE TABLE IF NOT EXISTS public.memory_source_configs (
53
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
54
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
55
+ processor_config_id uuid NOT NULL
56
+ REFERENCES public.memory_processor_configs(id) ON DELETE CASCADE,
57
+ source_family text NOT NULL,
58
+ source_binding_key text NOT NULL,
59
+ enabled boolean NOT NULL DEFAULT true,
60
+ boundary jsonb NOT NULL DEFAULT '{}'::jsonb,
61
+ policy_version integer NOT NULL DEFAULT 1,
62
+ created_at timestamptz NOT NULL DEFAULT now(),
63
+ updated_at timestamptz NOT NULL DEFAULT now(),
64
+ CONSTRAINT memory_source_configs_source_family_check
65
+ CHECK (source_family IN ('twenty', 'firecrawl', 'email', 'bedrock_kb'))
66
+ );
67
+
68
+ CREATE UNIQUE INDEX IF NOT EXISTS memory_source_configs_binding_uidx
69
+ ON public.memory_source_configs (processor_config_id, source_family, source_binding_key);
70
+
71
+ CREATE INDEX IF NOT EXISTS memory_source_configs_tenant_idx
72
+ ON public.memory_source_configs (tenant_id);
73
+
74
+ CREATE TABLE IF NOT EXISTS public.memory_source_checkpoints (
75
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
76
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
77
+ source_config_id uuid NOT NULL
78
+ REFERENCES public.memory_source_configs(id) ON DELETE CASCADE,
79
+ partition_key text NOT NULL,
80
+ "cursor" jsonb NOT NULL DEFAULT '{}'::jsonb,
81
+ version integer NOT NULL DEFAULT 0,
82
+ last_advanced_at timestamptz,
83
+ created_at timestamptz NOT NULL DEFAULT now(),
84
+ updated_at timestamptz NOT NULL DEFAULT now()
85
+ );
86
+
87
+ CREATE UNIQUE INDEX IF NOT EXISTS memory_source_checkpoints_partition_uidx
88
+ ON public.memory_source_checkpoints (source_config_id, partition_key);
89
+
90
+ CREATE TABLE IF NOT EXISTS public.memory_evidence_items (
91
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
92
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
93
+ source_config_id uuid NOT NULL
94
+ REFERENCES public.memory_source_configs(id) ON DELETE CASCADE,
95
+ source_item_id text NOT NULL,
96
+ source_version text NOT NULL,
97
+ source_timestamp timestamptz,
98
+ content_hash text NOT NULL,
99
+ acquisition_run_id uuid REFERENCES public.workflow_runs(id) ON DELETE SET NULL,
100
+ target_scope text NOT NULL,
101
+ target_id uuid NOT NULL,
102
+ lifecycle text NOT NULL DEFAULT 'active',
103
+ sensitivity text,
104
+ snapshot_ref text,
105
+ normalized_snapshot jsonb,
106
+ extraction_recipe jsonb NOT NULL DEFAULT '{}'::jsonb,
107
+ last_error text,
108
+ created_at timestamptz NOT NULL DEFAULT now(),
109
+ updated_at timestamptz NOT NULL DEFAULT now(),
110
+ CONSTRAINT memory_evidence_items_target_scope_check
111
+ CHECK (target_scope IN ('space', 'tenant')),
112
+ CONSTRAINT memory_evidence_items_lifecycle_check
113
+ CHECK (lifecycle IN ('active', 'superseded', 'deleted', 'deferred', 'failed'))
114
+ );
115
+
116
+ CREATE UNIQUE INDEX IF NOT EXISTS memory_evidence_items_source_version_uidx
117
+ ON public.memory_evidence_items (source_config_id, source_item_id, source_version);
118
+
119
+ CREATE INDEX IF NOT EXISTS memory_evidence_items_tenant_idx
120
+ ON public.memory_evidence_items (tenant_id);
121
+
122
+ CREATE INDEX IF NOT EXISTS memory_evidence_items_source_lifecycle_idx
123
+ ON public.memory_evidence_items (source_config_id, lifecycle);
124
+
125
+ CREATE TABLE IF NOT EXISTS public.memory_run_items (
126
+ id bigserial PRIMARY KEY,
127
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
128
+ workflow_run_id uuid NOT NULL
129
+ REFERENCES public.workflow_runs(id) ON DELETE CASCADE,
130
+ source_config_id uuid NOT NULL
131
+ REFERENCES public.memory_source_configs(id) ON DELETE CASCADE,
132
+ source_item_id text NOT NULL,
133
+ stage text NOT NULL,
134
+ result text NOT NULL,
135
+ detail jsonb NOT NULL DEFAULT '{}'::jsonb,
136
+ created_at timestamptz NOT NULL DEFAULT now(),
137
+ CONSTRAINT memory_run_items_stage_check
138
+ CHECK (stage IN ('acquire', 'extract', 'project', 'resolve', 'retain', 'compound', 'graph', 'wiki', 'preflight')),
139
+ CONSTRAINT memory_run_items_result_check
140
+ CHECK (result IN ('seen', 'changed', 'retracted', 'deferred', 'failed', 'noop'))
141
+ );
142
+
143
+ CREATE UNIQUE INDEX IF NOT EXISTS memory_run_items_stage_uidx
144
+ ON public.memory_run_items (workflow_run_id, source_config_id, source_item_id, stage);
145
+
146
+ CREATE INDEX IF NOT EXISTS memory_run_items_run_idx
147
+ ON public.memory_run_items (workflow_run_id);
148
+
149
+ CREATE TABLE IF NOT EXISTS public.memory_derivations (
150
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
151
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
152
+ source_config_id uuid NOT NULL
153
+ REFERENCES public.memory_source_configs(id) ON DELETE CASCADE,
154
+ evidence_item_id uuid NOT NULL
155
+ REFERENCES public.memory_evidence_items(id) ON DELETE CASCADE,
156
+ projection_key text NOT NULL,
157
+ target_bank_id text NOT NULL,
158
+ hindsight_document_id text NOT NULL,
159
+ current_version text NOT NULL,
160
+ lifecycle text NOT NULL DEFAULT 'active',
161
+ retracted_at timestamptz,
162
+ created_at timestamptz NOT NULL DEFAULT now(),
163
+ updated_at timestamptz NOT NULL DEFAULT now(),
164
+ CONSTRAINT memory_derivations_lifecycle_check
165
+ CHECK (lifecycle IN ('active', 'superseded', 'retracted'))
166
+ );
167
+
168
+ CREATE UNIQUE INDEX IF NOT EXISTS memory_derivations_active_projection_uidx
169
+ ON public.memory_derivations (source_config_id, projection_key)
170
+ WHERE lifecycle = 'active';
171
+
172
+ CREATE INDEX IF NOT EXISTS memory_derivations_tenant_idx
173
+ ON public.memory_derivations (tenant_id);
174
+
175
+ CREATE INDEX IF NOT EXISTS memory_derivations_evidence_idx
176
+ ON public.memory_derivations (evidence_item_id);
@@ -0,0 +1,9 @@
1
+ -- creates-column: public.messages.source_event_id
2
+ -- creates: public.uq_messages_tenant_source_event_id
3
+
4
+ ALTER TABLE public.messages
5
+ ADD COLUMN IF NOT EXISTS source_event_id text;
6
+
7
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_messages_tenant_source_event_id
8
+ ON public.messages (tenant_id, source_event_id)
9
+ WHERE source_event_id IS NOT NULL;
@@ -0,0 +1,151 @@
1
+ -- Purpose: add Microsoft Teams application persistence for tenant installs, user links, and Teams conversation mapping.
2
+ -- Plan: THINK-84 (U6 install path; msteams_threads is created now so one migration covers the domain for U7)
3
+ -- Apply manually: psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f packages/database-pg/drizzle/0233_msteams_install_and_links.sql
4
+ -- After applying, RE-APPLY 0227_analyst_reader_role.sql and 0230_analyst_rls.sql:
5
+ -- their regenerated grant/RLS sections cover the msteams tables, and databases
6
+ -- that ran them before these tables existed have no analyst grants/policies
7
+ -- for them (0230's msteams policy markers gate this in the drift reporter;
8
+ -- 0227's grants are not marker-checkable).
9
+ -- creates: public.msteams_tenant_installs
10
+ -- creates: public.msteams_user_links
11
+ -- creates: public.msteams_threads
12
+ -- creates: public.uq_msteams_tenant_installs_entra_tenant
13
+ -- creates: public.uq_msteams_tenant_installs_tenant_entra_tenant
14
+ -- creates: public.uq_msteams_tenant_installs_tenant_active
15
+ -- creates: public.idx_msteams_tenant_installs_tenant_status
16
+ -- creates: public.uq_msteams_user_links_entra_tenant_aad_object
17
+ -- creates: public.idx_msteams_user_links_tenant_user
18
+ -- creates: public.idx_msteams_user_links_user
19
+ -- creates: public.uq_msteams_threads_entra_tenant_conversation
20
+ -- creates: public.idx_msteams_threads_thread
21
+ -- creates: public.idx_msteams_threads_tenant_entra_tenant
22
+ -- creates-constraint: public.msteams_tenant_installs.msteams_tenant_installs_tenant_id_tenants_id_fk
23
+ -- creates-constraint: public.msteams_tenant_installs.msteams_tenant_installs_installed_by_user_id_users_id_fk
24
+ -- creates-constraint: public.msteams_tenant_installs.msteams_tenant_installs_status_allowed
25
+ -- creates-constraint: public.msteams_tenant_installs.msteams_tenant_installs_consent_status_allowed
26
+ -- creates-constraint: public.msteams_user_links.msteams_user_links_tenant_id_tenants_id_fk
27
+ -- creates-constraint: public.msteams_user_links.msteams_user_links_entra_tenant_id_installs_entra_tenant_id_fk
28
+ -- creates-constraint: public.msteams_user_links.msteams_user_links_user_id_users_id_fk
29
+ -- creates-constraint: public.msteams_user_links.msteams_user_links_status_allowed
30
+ -- creates-constraint: public.msteams_threads.msteams_threads_tenant_id_tenants_id_fk
31
+ -- creates-constraint: public.msteams_threads.msteams_threads_entra_tenant_id_installs_entra_tenant_id_fk
32
+ -- creates-constraint: public.msteams_threads.msteams_threads_thread_id_threads_id_fk
33
+
34
+ \set ON_ERROR_STOP on
35
+
36
+ BEGIN;
37
+
38
+ SET LOCAL lock_timeout = '5s';
39
+ SET LOCAL statement_timeout = '120s';
40
+
41
+ CREATE TABLE IF NOT EXISTS public.msteams_tenant_installs (
42
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
43
+ tenant_id uuid NOT NULL,
44
+ entra_tenant_id text NOT NULL,
45
+ bot_app_id text NOT NULL,
46
+ status text NOT NULL DEFAULT 'pending',
47
+ consent_status text NOT NULL DEFAULT 'pending',
48
+ installed_by_user_id uuid,
49
+ installed_at timestamptz,
50
+ uninstalled_at timestamptz,
51
+ created_at timestamptz NOT NULL DEFAULT now(),
52
+ updated_at timestamptz NOT NULL DEFAULT now(),
53
+ CONSTRAINT msteams_tenant_installs_tenant_id_tenants_id_fk
54
+ FOREIGN KEY (tenant_id)
55
+ REFERENCES public.tenants(id)
56
+ ON DELETE CASCADE,
57
+ CONSTRAINT msteams_tenant_installs_installed_by_user_id_users_id_fk
58
+ FOREIGN KEY (installed_by_user_id)
59
+ REFERENCES public.users(id)
60
+ ON DELETE SET NULL,
61
+ CONSTRAINT msteams_tenant_installs_status_allowed
62
+ CHECK (status IN ('pending','active','uninstalled','revoked')),
63
+ CONSTRAINT msteams_tenant_installs_consent_status_allowed
64
+ CHECK (consent_status IN ('pending','granted','admin_required','revoked'))
65
+ );
66
+
67
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_msteams_tenant_installs_entra_tenant
68
+ ON public.msteams_tenant_installs (entra_tenant_id);
69
+
70
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_msteams_tenant_installs_tenant_entra_tenant
71
+ ON public.msteams_tenant_installs (tenant_id, entra_tenant_id);
72
+
73
+ -- A ThinkWork tenant has at most one ACTIVE Entra binding; concurrent
74
+ -- consent callbacks cannot race two bindings live.
75
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_msteams_tenant_installs_tenant_active
76
+ ON public.msteams_tenant_installs (tenant_id)
77
+ WHERE status = 'active';
78
+
79
+ CREATE INDEX IF NOT EXISTS idx_msteams_tenant_installs_tenant_status
80
+ ON public.msteams_tenant_installs (tenant_id, status);
81
+
82
+ CREATE TABLE IF NOT EXISTS public.msteams_user_links (
83
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
84
+ tenant_id uuid NOT NULL,
85
+ entra_tenant_id text NOT NULL,
86
+ aad_object_id text NOT NULL,
87
+ user_id uuid NOT NULL,
88
+ display_name text,
89
+ status text NOT NULL DEFAULT 'active',
90
+ linked_at timestamptz NOT NULL DEFAULT now(),
91
+ unlinked_at timestamptz,
92
+ created_at timestamptz NOT NULL DEFAULT now(),
93
+ updated_at timestamptz NOT NULL DEFAULT now(),
94
+ CONSTRAINT msteams_user_links_tenant_id_tenants_id_fk
95
+ FOREIGN KEY (tenant_id)
96
+ REFERENCES public.tenants(id)
97
+ ON DELETE CASCADE,
98
+ CONSTRAINT msteams_user_links_entra_tenant_id_installs_entra_tenant_id_fk
99
+ FOREIGN KEY (entra_tenant_id)
100
+ REFERENCES public.msteams_tenant_installs(entra_tenant_id)
101
+ ON DELETE RESTRICT,
102
+ CONSTRAINT msteams_user_links_user_id_users_id_fk
103
+ FOREIGN KEY (user_id)
104
+ REFERENCES public.users(id)
105
+ ON DELETE RESTRICT,
106
+ CONSTRAINT msteams_user_links_status_allowed
107
+ CHECK (status IN ('active','unlinked','orphaned','suspended'))
108
+ );
109
+
110
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_msteams_user_links_entra_tenant_aad_object
111
+ ON public.msteams_user_links (entra_tenant_id, aad_object_id);
112
+
113
+ CREATE INDEX IF NOT EXISTS idx_msteams_user_links_tenant_user
114
+ ON public.msteams_user_links (tenant_id, user_id);
115
+
116
+ CREATE INDEX IF NOT EXISTS idx_msteams_user_links_user
117
+ ON public.msteams_user_links (user_id);
118
+
119
+ CREATE TABLE IF NOT EXISTS public.msteams_threads (
120
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
121
+ tenant_id uuid NOT NULL,
122
+ entra_tenant_id text NOT NULL,
123
+ conversation_id text NOT NULL,
124
+ service_url text NOT NULL,
125
+ thread_id uuid NOT NULL,
126
+ created_at timestamptz NOT NULL DEFAULT now(),
127
+ updated_at timestamptz NOT NULL DEFAULT now(),
128
+ CONSTRAINT msteams_threads_tenant_id_tenants_id_fk
129
+ FOREIGN KEY (tenant_id)
130
+ REFERENCES public.tenants(id)
131
+ ON DELETE CASCADE,
132
+ CONSTRAINT msteams_threads_entra_tenant_id_installs_entra_tenant_id_fk
133
+ FOREIGN KEY (entra_tenant_id)
134
+ REFERENCES public.msteams_tenant_installs(entra_tenant_id)
135
+ ON DELETE RESTRICT,
136
+ CONSTRAINT msteams_threads_thread_id_threads_id_fk
137
+ FOREIGN KEY (thread_id)
138
+ REFERENCES public.threads(id)
139
+ ON DELETE CASCADE
140
+ );
141
+
142
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_msteams_threads_entra_tenant_conversation
143
+ ON public.msteams_threads (entra_tenant_id, conversation_id);
144
+
145
+ CREATE INDEX IF NOT EXISTS idx_msteams_threads_thread
146
+ ON public.msteams_threads (thread_id);
147
+
148
+ CREATE INDEX IF NOT EXISTS idx_msteams_threads_tenant_entra_tenant
149
+ ON public.msteams_threads (tenant_id, entra_tenant_id);
150
+
151
+ COMMIT;