thinkwork-cli 0.12.17 → 0.13.1

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 (74) hide show
  1. package/dist/{api-client-JUBCQQDF.js → api-client-4DZ266J5.js} +1 -1
  2. package/dist/{chunk-STY44FQO.js → chunk-Y2WHJECR.js} +44 -12
  3. package/dist/cli.js +36715 -12965
  4. package/dist/drizzle/0227_analyst_reader_role.sql +100 -38
  5. package/dist/drizzle/0230_analyst_rls.sql +183 -66
  6. package/dist/drizzle/0232_memory_source_thin_ledger.sql +176 -0
  7. package/dist/drizzle/0232_message_source_event_id.sql +9 -0
  8. package/dist/drizzle/0233_msteams_install_and_links.sql +151 -0
  9. package/dist/drizzle/0233_workflow_memory_stage_tokens.sql +20 -0
  10. package/dist/drizzle/0234_memory_claim_lifecycle.sql +160 -0
  11. package/dist/drizzle/0235_workflow_token_execution_claim.sql +34 -0
  12. package/dist/drizzle/0236_memory_retraction_fencing.sql +50 -0
  13. package/dist/drizzle/0237_memory_claims_active_value_uidx.sql +172 -0
  14. package/dist/drizzle/0238_memory_erase_generation_fence.sql +97 -0
  15. package/dist/drizzle/0239_canonical_entity_identity.sql +257 -0
  16. package/dist/drizzle/0240_memory_personal_scope.sql +45 -0
  17. package/dist/drizzle/0241_knowledge_base_document_manifest.sql +67 -0
  18. package/dist/drizzle/0242_search_queries.sql +28 -0
  19. package/dist/drizzle/0243_section_sources_thread_refs.sql +12 -0
  20. package/dist/drizzle/0244_system_automations_and_memory_stage_overrides.sql +45 -0
  21. package/dist/drizzle/0245_wiki_page_render.sql +21 -0
  22. package/dist/drizzle/0246_automation_workflow_ownership.sql +30 -0
  23. package/dist/drizzle/0247_capability_runtime.sql +402 -0
  24. package/dist/drizzle/0248_capability_private_interpreter.sql +13 -0
  25. package/dist/drizzle/0249_capability_headless_execution_evidence.sql +40 -0
  26. package/dist/drizzle/0250_kg_schema_extraction.sql +549 -0
  27. package/dist/drizzle/0251_agents_json_render_ui.sql +25 -0
  28. package/dist/drizzle/0251_brain_stragglers.sql +242 -0
  29. package/dist/drizzle/0252_drop_kg_and_straggler_compat_views.sql +100 -0
  30. package/dist/drizzle/0253_drop_brain_substrate.sql +91 -0
  31. package/dist/drizzle/0254_capability_autonomous_admission.sql +29 -0
  32. package/dist/drizzle/0255_capability_autonomous_approval_mode.sql +16 -0
  33. package/dist/drizzle/0256_agent_profile_manifest_authority.sql +18 -0
  34. package/dist/drizzle/0257_capability_approvals.sql +53 -0
  35. package/dist/drizzle/0258_pending_tool_approvals.sql +84 -0
  36. package/dist/drizzle/0259_tenant_capability_registry_trust.sql +19 -0
  37. package/dist/drizzle/0260_scheduled_jobs_run_as_user.sql +20 -0
  38. package/dist/drizzle/0261_thread_turns_origin_turn_index.sql +11 -0
  39. package/dist/terraform/examples/greenfield/main.tf +40 -0
  40. package/dist/terraform/modules/app/agentcore-admin/main.tf +341 -0
  41. package/dist/terraform/modules/app/agentcore-admin/outputs.tf +20 -0
  42. package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +61 -0
  43. package/dist/terraform/modules/app/agentcore-harness/main.tf +101 -0
  44. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +9 -0
  45. package/dist/terraform/modules/app/agentcore-harness/variables.tf +25 -0
  46. package/dist/terraform/modules/app/agentcore-pi/main.tf +7 -0
  47. package/dist/terraform/modules/app/capability-broker/main.tf +393 -0
  48. package/dist/terraform/modules/app/capability-broker/outputs.tf +54 -0
  49. package/dist/terraform/modules/app/capability-broker/variables.tf +98 -0
  50. package/dist/terraform/modules/app/deployment-control-plane/runner.py +46 -5
  51. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +94 -0
  52. package/dist/terraform/modules/app/lambda-api/capability-search-alarms.tf +60 -0
  53. package/dist/terraform/modules/app/lambda-api/handlers.tf +361 -16
  54. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +131 -5
  55. package/dist/terraform/modules/app/lambda-api/main.tf +11 -0
  56. package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +92 -0
  57. package/dist/terraform/modules/app/lambda-api/msteams-app-secrets.tf +46 -0
  58. package/dist/terraform/modules/app/lambda-api/variables.tf +92 -0
  59. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/alarms.tf +47 -0
  60. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/main.tf +43 -2
  61. package/dist/terraform/modules/foundation/cognito/main.tf +32 -8
  62. package/dist/terraform/modules/foundation/cognito/variables.tf +12 -0
  63. package/dist/terraform/modules/foundation/vpc/outputs.tf +5 -0
  64. package/dist/terraform/modules/thinkwork/main.tf +152 -5
  65. package/dist/terraform/modules/thinkwork/outputs.tf +55 -0
  66. package/dist/terraform/modules/thinkwork/variables.tf +42 -0
  67. package/dist/workspace-defaults/files/TOOLS.md +39 -0
  68. package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +18 -0
  69. package/dist/workspace-defaults/files/agents/coding/INSTRUCTIONS.md +11 -0
  70. package/dist/workspace-defaults/files/agents/research/INSTRUCTIONS.md +12 -0
  71. package/dist/workspace-defaults/files/agents/reviewer/INSTRUCTIONS.md +15 -0
  72. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +6 -2
  73. package/package.json +4 -2
  74. /package/dist/workspace-defaults/files/{AGENTS.md → INSTRUCTIONS.md} +0 -0
@@ -0,0 +1,549 @@
1
+ -- 0250_kg_schema_extraction.sql
2
+ --
3
+ -- Phase A of the knowledge-graph schema extraction (PR 1 of the THINK-290
4
+ -- arc). Moves the five public.knowledge_graph_* tables into the new `kg.*`
5
+ -- Postgres schema, drops the redundant `knowledge_graph_` prefix from table
6
+ -- and CHECK-constraint names, re-points the scope-guard trigger functions at
7
+ -- the new schema-qualified names, and creates compat views in public.* so
8
+ -- old bundled Lambda code keeps reading during the deploy bridge window.
9
+ --
10
+ -- Tables moved:
11
+ -- public.knowledge_graph_ingest_runs → kg.ingest_runs
12
+ -- public.knowledge_graph_entities → kg.entities
13
+ -- public.knowledge_graph_relationships → kg.relationships
14
+ -- public.knowledge_graph_evidence → kg.evidence
15
+ -- public.knowledge_graph_observation_cursors → kg.observation_cursors
16
+ --
17
+ -- Index names already use the `idx_kg_*` / `uq_kg_*` shorthand and are kept
18
+ -- verbatim; they move with their tables via SET SCHEMA. FK constraint names
19
+ -- and the `knowledge_graph_*_scope_guard` trigger names keep their original
20
+ -- prefixes — cosmetic cleanup deferred, same as the 0089/0090 precedent.
21
+ -- CHECK constraint names embed the table name in Drizzle's naming, so they
22
+ -- are renamed to the new stems to keep the Drizzle source and DB aligned.
23
+ --
24
+ -- The scope-guard trigger functions (0146/0147) query
25
+ -- public.knowledge_graph_* schema-qualified inside their plpgsql bodies.
26
+ -- Left alone they would resolve to the compat views (works during the
27
+ -- bridge) and break when the views drop, so this migration CREATE OR
28
+ -- REPLACEs the three functions that reference the moved tables with kg.*
29
+ -- qualified queries. enforce_knowledge_graph_ingest_run_scope references
30
+ -- only public.threads and is untouched.
31
+ --
32
+ -- Analyst surface: the semantic model deliberately includes public-schema
33
+ -- tables only (THINK-283), so the moved tables leave the analyst grants/RLS
34
+ -- surface, matching the wiki/ontology/brain extractions. Stale table-level
35
+ -- grants and analyst_tenant_isolation policies ride along harmlessly (the
36
+ -- app role owns the tables and bypasses RLS; analyst_reader has no USAGE on
37
+ -- kg).
38
+ --
39
+ -- Plan reference: docs/plans/2026-07-14-001-refactor-kg-schema-extraction-and-brain-cleanup-plan.md
40
+ -- Pattern doc: docs/solutions/database-issues/feature-schema-extraction-pattern.md
41
+ --
42
+ -- Apply manually (pause the observations-ingest schedule first — see the
43
+ -- writer runbook in the PR body):
44
+ -- psql "$DATABASE_URL" -f packages/database-pg/drizzle/0250_kg_schema_extraction.sql
45
+ -- Then verify:
46
+ -- bash scripts/db-migrate-manual.sh packages/database-pg/drizzle/0250_kg_schema_extraction.sql
47
+ -- psql -c "\dt kg.*" -- 5 tables expected
48
+ -- psql -c "\dv public.knowledge_graph_*" -- 5 compat views expected
49
+ -- psql -c "\dt public.knowledge_graph_*" -- 0 tables expected
50
+ --
51
+ -- Inverse runbook (rollback): drop the views, SET SCHEMA back, RENAME back,
52
+ -- restore the CHECK constraint names, and re-run the 0146/0147 function
53
+ -- bodies to restore the public.* qualification:
54
+ -- DROP VIEW IF EXISTS public.knowledge_graph_ingest_runs, ...; -- × 5
55
+ -- ALTER TABLE kg.evidence SET SCHEMA public;
56
+ -- ALTER TABLE public.evidence RENAME TO knowledge_graph_evidence;
57
+ -- ALTER TABLE public.knowledge_graph_evidence RENAME CONSTRAINT evidence_source_kind_allowed TO knowledge_graph_evidence_source_kind_allowed;
58
+ -- ... (× 5 tables, × 14 constraints, leaf-first)
59
+ -- -- re-apply 0146 + 0147 function definitions
60
+ -- DROP SCHEMA kg; -- last step; fails if any objects remain
61
+ --
62
+ -- Markers (consumed by scripts/db-migrate-manual.sh):
63
+ --
64
+ -- creates: kg.ingest_runs
65
+ -- creates: kg.entities
66
+ -- creates: kg.relationships
67
+ -- creates: kg.evidence
68
+ -- creates: kg.observation_cursors
69
+ -- creates-constraint: kg.ingest_runs.ingest_runs_status_allowed
70
+ -- creates-constraint: kg.ingest_runs.ingest_runs_trigger_allowed
71
+ -- creates-constraint: kg.ingest_runs.ingest_runs_source_kind_allowed
72
+ -- creates-constraint: kg.ingest_runs.ingest_runs_thread_scope_required
73
+ -- creates-constraint: kg.entities.entities_grounding_allowed
74
+ -- creates-constraint: kg.entities.entities_provenance_allowed
75
+ -- creates-constraint: kg.entities.entities_source_kind_allowed
76
+ -- creates-constraint: kg.entities.entities_resolution_state_allowed
77
+ -- creates-constraint: kg.relationships.relationships_grounding_allowed
78
+ -- creates-constraint: kg.relationships.relationships_provenance_allowed
79
+ -- creates-constraint: kg.relationships.relationships_source_kind_allowed
80
+ -- creates-constraint: kg.evidence.evidence_source_kind_allowed
81
+ -- creates-constraint: kg.evidence.evidence_evidence_source_kind_allowed
82
+ -- creates-constraint: kg.evidence.evidence_subject_required
83
+ -- creates: public.knowledge_graph_ingest_runs
84
+ -- creates: public.knowledge_graph_entities
85
+ -- creates: public.knowledge_graph_relationships
86
+ -- creates: public.knowledge_graph_evidence
87
+ -- creates: public.knowledge_graph_observation_cursors
88
+
89
+ \set ON_ERROR_STOP on
90
+
91
+ BEGIN;
92
+
93
+ -- Set timeouts BEFORE acquiring the advisory lock so the lock acquisition
94
+ -- itself is bounded.
95
+ SET LOCAL lock_timeout = '30s';
96
+ SET LOCAL statement_timeout = '300s';
97
+
98
+ -- Serialize concurrent application attempts.
99
+ SELECT pg_advisory_xact_lock(hashtext('kg_schema_extraction'));
100
+
101
+ -- Refuse to apply against an unexpected DB.
102
+ DO $$
103
+ BEGIN
104
+ IF current_database() != 'thinkwork' THEN
105
+ RAISE EXCEPTION 'wrong database: %, expected thinkwork', current_database();
106
+ END IF;
107
+ END $$;
108
+
109
+ -- Pre-flight invariants: for each table, assert old name exists AND new
110
+ -- name does not. Symmetric checks convert any partial-state scenario into a
111
+ -- clear pre-flight error. If the file was already applied (all five moved),
112
+ -- the customer runner's unattended re-sweep must be a no-op, so short-circuit
113
+ -- cleanly in that case.
114
+ DO $$
115
+ BEGIN
116
+ IF to_regclass('kg.ingest_runs') IS NOT NULL
117
+ AND to_regclass('kg.entities') IS NOT NULL
118
+ AND to_regclass('kg.relationships') IS NOT NULL
119
+ AND to_regclass('kg.evidence') IS NOT NULL
120
+ AND to_regclass('kg.observation_cursors') IS NOT NULL
121
+ AND to_regclass('public.knowledge_graph_ingest_runs') IS NOT NULL THEN
122
+ -- Already applied (views present at the old names). Nothing to do.
123
+ RETURN;
124
+ END IF;
125
+
126
+ IF to_regclass('public.knowledge_graph_ingest_runs') IS NULL THEN
127
+ RAISE EXCEPTION 'pre-flight: public.knowledge_graph_ingest_runs does not exist';
128
+ END IF;
129
+ IF to_regclass('kg.ingest_runs') IS NOT NULL THEN
130
+ RAISE EXCEPTION 'pre-flight: kg.ingest_runs already exists — refusing to re-apply';
131
+ END IF;
132
+ IF to_regclass('public.knowledge_graph_entities') IS NULL THEN
133
+ RAISE EXCEPTION 'pre-flight: public.knowledge_graph_entities does not exist';
134
+ END IF;
135
+ IF to_regclass('kg.entities') IS NOT NULL THEN
136
+ RAISE EXCEPTION 'pre-flight: kg.entities already exists — refusing to re-apply';
137
+ END IF;
138
+ IF to_regclass('public.knowledge_graph_relationships') IS NULL THEN
139
+ RAISE EXCEPTION 'pre-flight: public.knowledge_graph_relationships does not exist';
140
+ END IF;
141
+ IF to_regclass('kg.relationships') IS NOT NULL THEN
142
+ RAISE EXCEPTION 'pre-flight: kg.relationships already exists — refusing to re-apply';
143
+ END IF;
144
+ IF to_regclass('public.knowledge_graph_evidence') IS NULL THEN
145
+ RAISE EXCEPTION 'pre-flight: public.knowledge_graph_evidence does not exist';
146
+ END IF;
147
+ IF to_regclass('kg.evidence') IS NOT NULL THEN
148
+ RAISE EXCEPTION 'pre-flight: kg.evidence already exists — refusing to re-apply';
149
+ END IF;
150
+ IF to_regclass('public.knowledge_graph_observation_cursors') IS NULL THEN
151
+ RAISE EXCEPTION 'pre-flight: public.knowledge_graph_observation_cursors does not exist';
152
+ END IF;
153
+ IF to_regclass('kg.observation_cursors') IS NOT NULL THEN
154
+ RAISE EXCEPTION 'pre-flight: kg.observation_cursors already exists — refusing to re-apply';
155
+ END IF;
156
+ END $$;
157
+
158
+ -- Everything below is guarded so the whole file is idempotent for the
159
+ -- customer runner's unattended sweep: once applied, the pre-flight above
160
+ -- short-circuits nothing (DO blocks can't skip top-level DDL), so each DDL
161
+ -- statement carries its own existence guard.
162
+
163
+ -- Schema creation.
164
+ CREATE SCHEMA IF NOT EXISTS kg;
165
+ COMMENT ON SCHEMA kg IS 'Knowledge-graph mirror tables. Tenant-scoped entity/relationship/evidence snapshots and the ingest-run ledger. Extracted from public.knowledge_graph_* on 2026-07-14 (THINK-290).';
166
+
167
+ -- ── Move and rename tables ─────────────────────────────────────────────
168
+ -- FK-leaf-first ordering (evidence → relationships → entities →
169
+ -- observation_cursors → ingest_runs). Postgres preserves FKs across
170
+ -- SET SCHEMA automatically; ordering matters only for operator mental model.
171
+
172
+ DO $$
173
+ BEGIN
174
+ IF to_regclass('public.knowledge_graph_evidence') IS NOT NULL
175
+ AND to_regclass('kg.evidence') IS NULL THEN
176
+ ALTER TABLE public.knowledge_graph_evidence SET SCHEMA kg;
177
+ ALTER TABLE kg.knowledge_graph_evidence RENAME TO evidence;
178
+ END IF;
179
+
180
+ IF to_regclass('public.knowledge_graph_relationships') IS NOT NULL
181
+ AND to_regclass('kg.relationships') IS NULL THEN
182
+ ALTER TABLE public.knowledge_graph_relationships SET SCHEMA kg;
183
+ ALTER TABLE kg.knowledge_graph_relationships RENAME TO relationships;
184
+ END IF;
185
+
186
+ IF to_regclass('public.knowledge_graph_entities') IS NOT NULL
187
+ AND to_regclass('kg.entities') IS NULL THEN
188
+ ALTER TABLE public.knowledge_graph_entities SET SCHEMA kg;
189
+ ALTER TABLE kg.knowledge_graph_entities RENAME TO entities;
190
+ END IF;
191
+
192
+ IF to_regclass('public.knowledge_graph_observation_cursors') IS NOT NULL
193
+ AND to_regclass('kg.observation_cursors') IS NULL THEN
194
+ ALTER TABLE public.knowledge_graph_observation_cursors SET SCHEMA kg;
195
+ ALTER TABLE kg.knowledge_graph_observation_cursors RENAME TO observation_cursors;
196
+ END IF;
197
+
198
+ IF to_regclass('public.knowledge_graph_ingest_runs') IS NOT NULL
199
+ AND to_regclass('kg.ingest_runs') IS NULL THEN
200
+ ALTER TABLE public.knowledge_graph_ingest_runs SET SCHEMA kg;
201
+ ALTER TABLE kg.knowledge_graph_ingest_runs RENAME TO ingest_runs;
202
+ END IF;
203
+ END $$;
204
+
205
+ -- ── Rename CHECK constraints to drop the knowledge_graph_ prefix ────────
206
+ -- Drizzle's source names these after the (new) table stems; renaming keeps
207
+ -- the DB aligned with src/schema/knowledge-graph.ts.
208
+
209
+ DO $$
210
+ DECLARE
211
+ pair record;
212
+ BEGIN
213
+ FOR pair IN
214
+ SELECT * FROM (VALUES
215
+ ('kg.ingest_runs', 'knowledge_graph_ingest_runs_status_allowed', 'ingest_runs_status_allowed'),
216
+ ('kg.ingest_runs', 'knowledge_graph_ingest_runs_trigger_allowed', 'ingest_runs_trigger_allowed'),
217
+ ('kg.ingest_runs', 'knowledge_graph_ingest_runs_source_kind_allowed', 'ingest_runs_source_kind_allowed'),
218
+ ('kg.ingest_runs', 'knowledge_graph_ingest_runs_thread_scope_required', 'ingest_runs_thread_scope_required'),
219
+ ('kg.entities', 'knowledge_graph_entities_grounding_allowed', 'entities_grounding_allowed'),
220
+ ('kg.entities', 'knowledge_graph_entities_provenance_allowed', 'entities_provenance_allowed'),
221
+ ('kg.entities', 'knowledge_graph_entities_source_kind_allowed', 'entities_source_kind_allowed'),
222
+ ('kg.entities', 'knowledge_graph_entities_resolution_state_allowed', 'entities_resolution_state_allowed'),
223
+ ('kg.relationships', 'knowledge_graph_relationships_grounding_allowed', 'relationships_grounding_allowed'),
224
+ ('kg.relationships', 'knowledge_graph_relationships_provenance_allowed', 'relationships_provenance_allowed'),
225
+ ('kg.relationships', 'knowledge_graph_relationships_source_kind_allowed', 'relationships_source_kind_allowed'),
226
+ ('kg.evidence', 'knowledge_graph_evidence_source_kind_allowed', 'evidence_source_kind_allowed'),
227
+ ('kg.evidence', 'knowledge_graph_evidence_evidence_source_kind_allowed', 'evidence_evidence_source_kind_allowed'),
228
+ ('kg.evidence', 'knowledge_graph_evidence_subject_required', 'evidence_subject_required')
229
+ ) AS v(tbl, old_name, new_name)
230
+ LOOP
231
+ IF EXISTS (
232
+ SELECT 1 FROM pg_constraint
233
+ WHERE conrelid = to_regclass(pair.tbl) AND conname = pair.old_name
234
+ ) THEN
235
+ EXECUTE format('ALTER TABLE %s RENAME CONSTRAINT %I TO %I',
236
+ pair.tbl, pair.old_name, pair.new_name);
237
+ END IF;
238
+ END LOOP;
239
+ END $$;
240
+
241
+ -- ── Re-point scope-guard trigger functions at kg.* ─────────────────────
242
+ -- Bodies below are the 0146/0147 definitions with public.knowledge_graph_*
243
+ -- replaced by kg.* (and nothing else changed). CREATE OR REPLACE is
244
+ -- idempotent. Function and trigger names keep their original prefixes.
245
+
246
+ CREATE OR REPLACE FUNCTION public.enforce_knowledge_graph_entity_scope()
247
+ RETURNS trigger
248
+ LANGUAGE plpgsql
249
+ AS $$
250
+ DECLARE
251
+ run_tenant uuid;
252
+ run_thread uuid;
253
+ run_source_kind text;
254
+ run_source_ref text;
255
+ ontology_tenant uuid;
256
+ BEGIN
257
+ SELECT tenant_id, thread_id, source_kind, source_ref
258
+ INTO run_tenant, run_thread, run_source_kind, run_source_ref
259
+ FROM kg.ingest_runs
260
+ WHERE id = NEW.ingest_run_id;
261
+
262
+ IF run_tenant IS NOT NULL
263
+ AND (
264
+ run_tenant != NEW.tenant_id
265
+ OR run_thread IS DISTINCT FROM NEW.thread_id
266
+ OR run_source_kind != NEW.source_kind
267
+ OR run_source_ref != NEW.source_ref
268
+ ) THEN
269
+ RAISE EXCEPTION 'knowledge graph entity run scope mismatch for run %', NEW.ingest_run_id;
270
+ END IF;
271
+
272
+ IF NEW.ontology_entity_type_id IS NOT NULL THEN
273
+ SELECT tenant_id INTO ontology_tenant
274
+ FROM ontology.entity_types
275
+ WHERE id = NEW.ontology_entity_type_id;
276
+
277
+ IF ontology_tenant IS NOT NULL AND ontology_tenant != NEW.tenant_id THEN
278
+ RAISE EXCEPTION 'knowledge graph entity ontology tenant mismatch for entity type %', NEW.ontology_entity_type_id;
279
+ END IF;
280
+ END IF;
281
+
282
+ RETURN NEW;
283
+ END;
284
+ $$;
285
+
286
+ CREATE OR REPLACE FUNCTION public.enforce_knowledge_graph_relationship_scope()
287
+ RETURNS trigger
288
+ LANGUAGE plpgsql
289
+ AS $$
290
+ DECLARE
291
+ run_tenant uuid;
292
+ run_thread uuid;
293
+ run_source_kind text;
294
+ run_source_ref text;
295
+ source_tenant uuid;
296
+ source_thread uuid;
297
+ source_run uuid;
298
+ source_entity_kind text;
299
+ source_entity_ref text;
300
+ target_tenant uuid;
301
+ target_thread uuid;
302
+ target_run uuid;
303
+ target_kind text;
304
+ target_ref text;
305
+ ontology_tenant uuid;
306
+ BEGIN
307
+ SELECT
308
+ r.tenant_id,
309
+ r.thread_id,
310
+ r.source_kind,
311
+ r.source_ref
312
+ INTO run_tenant, run_thread, run_source_kind, run_source_ref
313
+ FROM kg.ingest_runs AS r
314
+ WHERE r.id = NEW.ingest_run_id;
315
+
316
+ IF run_tenant IS NOT NULL
317
+ AND (
318
+ run_tenant != NEW.tenant_id
319
+ OR run_thread IS DISTINCT FROM NEW.thread_id
320
+ OR run_source_kind != NEW.source_kind
321
+ OR run_source_ref != NEW.source_ref
322
+ ) THEN
323
+ RAISE EXCEPTION 'knowledge graph relationship run scope mismatch for run %', NEW.ingest_run_id;
324
+ END IF;
325
+
326
+ SELECT
327
+ e.tenant_id,
328
+ e.thread_id,
329
+ e.ingest_run_id,
330
+ e.source_kind,
331
+ e.source_ref
332
+ INTO source_tenant, source_thread, source_run, source_entity_kind, source_entity_ref
333
+ FROM kg.entities AS e
334
+ WHERE e.id = NEW.source_entity_id;
335
+
336
+ SELECT
337
+ e.tenant_id,
338
+ e.thread_id,
339
+ e.ingest_run_id,
340
+ e.source_kind,
341
+ e.source_ref
342
+ INTO target_tenant, target_thread, target_run, target_kind, target_ref
343
+ FROM kg.entities AS e
344
+ WHERE e.id = NEW.target_entity_id;
345
+
346
+ IF source_tenant IS NOT NULL
347
+ AND (
348
+ source_tenant != NEW.tenant_id
349
+ OR source_thread IS DISTINCT FROM NEW.thread_id
350
+ OR source_run != NEW.ingest_run_id
351
+ OR source_entity_kind != NEW.source_kind
352
+ OR source_entity_ref != NEW.source_ref
353
+ ) THEN
354
+ RAISE EXCEPTION 'knowledge graph relationship source scope mismatch for entity %', NEW.source_entity_id;
355
+ END IF;
356
+
357
+ IF target_tenant IS NOT NULL
358
+ AND (
359
+ target_tenant != NEW.tenant_id
360
+ OR target_thread IS DISTINCT FROM NEW.thread_id
361
+ OR target_run != NEW.ingest_run_id
362
+ OR target_kind != NEW.source_kind
363
+ OR target_ref != NEW.source_ref
364
+ ) THEN
365
+ RAISE EXCEPTION 'knowledge graph relationship target scope mismatch for entity %', NEW.target_entity_id;
366
+ END IF;
367
+
368
+ IF NEW.ontology_relationship_type_id IS NOT NULL THEN
369
+ SELECT rt.tenant_id INTO ontology_tenant
370
+ FROM ontology.relationship_types AS rt
371
+ WHERE rt.id = NEW.ontology_relationship_type_id;
372
+
373
+ IF ontology_tenant IS NOT NULL AND ontology_tenant != NEW.tenant_id THEN
374
+ RAISE EXCEPTION 'knowledge graph relationship ontology tenant mismatch for relationship type %', NEW.ontology_relationship_type_id;
375
+ END IF;
376
+ END IF;
377
+
378
+ RETURN NEW;
379
+ END;
380
+ $$;
381
+
382
+ CREATE OR REPLACE FUNCTION public.enforce_knowledge_graph_evidence_scope()
383
+ RETURNS trigger
384
+ LANGUAGE plpgsql
385
+ AS $$
386
+ DECLARE
387
+ run_tenant uuid;
388
+ run_thread uuid;
389
+ run_source_kind text;
390
+ run_source_ref text;
391
+ entity_tenant uuid;
392
+ entity_thread uuid;
393
+ entity_run uuid;
394
+ entity_kind text;
395
+ entity_ref text;
396
+ relationship_tenant uuid;
397
+ relationship_thread uuid;
398
+ relationship_run uuid;
399
+ relationship_kind text;
400
+ relationship_ref text;
401
+ message_tenant uuid;
402
+ message_thread uuid;
403
+ BEGIN
404
+ SELECT
405
+ r.tenant_id,
406
+ r.thread_id,
407
+ r.source_kind,
408
+ r.source_ref
409
+ INTO run_tenant, run_thread, run_source_kind, run_source_ref
410
+ FROM kg.ingest_runs AS r
411
+ WHERE r.id = NEW.ingest_run_id;
412
+
413
+ IF run_tenant IS NOT NULL
414
+ AND (
415
+ run_tenant != NEW.tenant_id
416
+ OR run_thread IS DISTINCT FROM NEW.thread_id
417
+ OR run_source_kind != NEW.source_kind
418
+ OR run_source_ref != NEW.source_ref
419
+ ) THEN
420
+ RAISE EXCEPTION 'knowledge graph evidence run scope mismatch for run %', NEW.ingest_run_id;
421
+ END IF;
422
+
423
+ IF NEW.entity_id IS NOT NULL THEN
424
+ SELECT
425
+ e.tenant_id,
426
+ e.thread_id,
427
+ e.ingest_run_id,
428
+ e.source_kind,
429
+ e.source_ref
430
+ INTO entity_tenant, entity_thread, entity_run, entity_kind, entity_ref
431
+ FROM kg.entities AS e
432
+ WHERE e.id = NEW.entity_id;
433
+
434
+ IF entity_tenant IS NOT NULL
435
+ AND (
436
+ entity_tenant != NEW.tenant_id
437
+ OR entity_thread IS DISTINCT FROM NEW.thread_id
438
+ OR entity_run != NEW.ingest_run_id
439
+ OR entity_kind != NEW.source_kind
440
+ OR entity_ref != NEW.source_ref
441
+ ) THEN
442
+ RAISE EXCEPTION 'knowledge graph evidence entity scope mismatch for entity %', NEW.entity_id;
443
+ END IF;
444
+ END IF;
445
+
446
+ IF NEW.relationship_id IS NOT NULL THEN
447
+ SELECT
448
+ r.tenant_id,
449
+ r.thread_id,
450
+ r.ingest_run_id,
451
+ r.source_kind,
452
+ r.source_ref
453
+ INTO relationship_tenant, relationship_thread, relationship_run, relationship_kind, relationship_ref
454
+ FROM kg.relationships AS r
455
+ WHERE r.id = NEW.relationship_id;
456
+
457
+ IF relationship_tenant IS NOT NULL
458
+ AND (
459
+ relationship_tenant != NEW.tenant_id
460
+ OR relationship_thread IS DISTINCT FROM NEW.thread_id
461
+ OR relationship_run != NEW.ingest_run_id
462
+ OR relationship_kind != NEW.source_kind
463
+ OR relationship_ref != NEW.source_ref
464
+ ) THEN
465
+ RAISE EXCEPTION 'knowledge graph evidence relationship scope mismatch for relationship %', NEW.relationship_id;
466
+ END IF;
467
+ END IF;
468
+
469
+ IF NEW.message_id IS NOT NULL THEN
470
+ SELECT m.tenant_id, m.thread_id INTO message_tenant, message_thread
471
+ FROM public.messages AS m
472
+ WHERE m.id = NEW.message_id;
473
+
474
+ IF message_tenant IS NOT NULL
475
+ AND (
476
+ message_tenant != NEW.tenant_id
477
+ OR message_thread IS DISTINCT FROM NEW.thread_id
478
+ ) THEN
479
+ RAISE EXCEPTION 'knowledge graph evidence message scope mismatch for message %', NEW.message_id;
480
+ END IF;
481
+ END IF;
482
+
483
+ RETURN NEW;
484
+ END;
485
+ $$;
486
+
487
+ -- ── Compat views in public.* ────────────────────────────────────────────
488
+ -- Old bundled Lambda code references public.knowledge_graph_*. Views are
489
+ -- column-enumerated per the plan's R6 (none of the five tables has a
490
+ -- generated column, so the enumeration is a stability guard, not a
491
+ -- workaround). Simple single-table views are auto-updatable, but the
492
+ -- ON CONFLICT writers are paused during the window regardless — views
493
+ -- reject ON CONFLICT at parse time. The U4 PR drops these.
494
+
495
+ DO $$
496
+ BEGIN
497
+ IF to_regclass('public.knowledge_graph_ingest_runs') IS NULL THEN
498
+ CREATE VIEW public.knowledge_graph_ingest_runs AS
499
+ SELECT id, tenant_id, thread_id, source_kind, source_ref, source_label,
500
+ requested_by_user_id, status, trigger, source_dataset_name,
501
+ source_dataset_id, started_at, finished_at, duration_ms, error,
502
+ entity_count, relationship_count, evidence_count,
503
+ diagnostic_count, message_count, input, metrics, metadata,
504
+ created_at, updated_at
505
+ FROM kg.ingest_runs;
506
+ END IF;
507
+
508
+ IF to_regclass('public.knowledge_graph_entities') IS NULL THEN
509
+ CREATE VIEW public.knowledge_graph_entities AS
510
+ SELECT id, tenant_id, thread_id, source_kind, source_ref,
511
+ ingest_run_id, graph_node_id, label, normalized_label,
512
+ type_label, ontology_entity_type_id, ontology_type_slug,
513
+ canonical_entity_id, resolution_state, grounding_status,
514
+ provenance_status, summary, aliases, properties, diagnostics,
515
+ relationship_count, evidence_count, last_seen_at, created_at,
516
+ updated_at
517
+ FROM kg.entities;
518
+ END IF;
519
+
520
+ IF to_regclass('public.knowledge_graph_relationships') IS NULL THEN
521
+ CREATE VIEW public.knowledge_graph_relationships AS
522
+ SELECT id, tenant_id, thread_id, source_kind, source_ref,
523
+ ingest_run_id, graph_edge_id, source_entity_id,
524
+ target_entity_id, label, ontology_relationship_type_id,
525
+ ontology_type_slug, grounding_status, provenance_status,
526
+ confidence, properties, diagnostics, evidence_count,
527
+ last_seen_at, created_at, updated_at
528
+ FROM kg.relationships;
529
+ END IF;
530
+
531
+ IF to_regclass('public.knowledge_graph_evidence') IS NULL THEN
532
+ CREATE VIEW public.knowledge_graph_evidence AS
533
+ SELECT id, tenant_id, thread_id, source_kind, source_ref,
534
+ ingest_run_id, entity_id, relationship_id, message_id,
535
+ message_role, message_created_at, speaker_label, snippet,
536
+ char_start, char_end, evidence_source_kind,
537
+ evidence_source_ref, metadata, observed_at, created_at
538
+ FROM kg.evidence;
539
+ END IF;
540
+
541
+ IF to_regclass('public.knowledge_graph_observation_cursors') IS NULL THEN
542
+ CREATE VIEW public.knowledge_graph_observation_cursors AS
543
+ SELECT tenant_id, bank_id, last_record_updated_at, last_record_id,
544
+ updated_at
545
+ FROM kg.observation_cursors;
546
+ END IF;
547
+ END $$;
548
+
549
+ COMMIT;
@@ -0,0 +1,25 @@
1
+ -- THINK-291: promote json-render-ui (emit_json_render_ui generated UI) from a
2
+ -- hidden agent_capabilities row to a standard platform-tool opt-in column on
3
+ -- agents, matching sandbox/browser/web_search/web_extract/send_email/
4
+ -- context_engine. Default-on so charts and rich result chunks work out of the
5
+ -- box on greenfield stages (the legacy row was unseeded and invisible).
6
+ --
7
+ -- The runtime cutover in the same PR reads this column and retires the
8
+ -- agent_capabilities 'thread-json-render-ui' read; existing agents inherit
9
+ -- enabled-on via the UPDATE below unless a legacy row explicitly disabled the
10
+ -- capability.
11
+ --
12
+ -- creates-column: public.agents.json_render_ui
13
+
14
+ ALTER TABLE public.agents
15
+ ADD COLUMN IF NOT EXISTS json_render_ui jsonb DEFAULT '{"enabled": true}'::jsonb;
16
+
17
+ -- Legacy carry-over: an agent whose capability row was explicitly disabled
18
+ -- stays disabled (ADD COLUMN already filled every existing row with the
19
+ -- enabled default).
20
+ UPDATE public.agents a
21
+ SET json_render_ui = '{"enabled": false}'::jsonb
22
+ FROM public.agent_capabilities c
23
+ WHERE c.agent_id = a.id
24
+ AND c.capability = 'thread-json-render-ui'
25
+ AND c.enabled = false;