spine-framework 0.3.114 → 0.3.115

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.
@@ -70,7 +70,7 @@ export interface AgentConfig {
70
70
  * Structured result from a single LLM inference call.
71
71
  *
72
72
  * @outputSpec content: string — agent response text
73
- * @outputSpec confidence: number — 0-1 score; derived from logprobs or 0.85 default
73
+ * @outputSpec confidence: number — 0-1 score; derived from logprobs or 0.70 default
74
74
  * @outputSpec tool_calls: ToolCall[] | undefined — tools the model wants to call
75
75
  * @outputSpec metadata: { model, usage, finish_reason } | undefined
76
76
  */
@@ -372,10 +372,14 @@ async function executeAgentInference(
372
372
 
373
373
  if (!inferenceResult.tool_calls || inferenceResult.tool_calls.length === 0) {
374
374
  // Composite confidence: 60% LLM logprobs + 40% retrieval similarity
375
+ // When logprobs are absent, be conservative: default 0.70 instead of 0.85
375
376
  // If mean retrieval similarity < 0.40, cap composite at 0.60
376
- const llmConf = inferenceResult.confidence ?? 0.85
377
+ // If mean retrieval similarity < 0.25, cap composite at 0.45 (very poor retrieval)
378
+ const llmConf = inferenceResult.confidence ?? 0.70
377
379
  let compositeConf: number
378
- if (meanSimilarity < 0.40 && meanSimilarity > 0) {
380
+ if (meanSimilarity < 0.25 && meanSimilarity > 0) {
381
+ compositeConf = Math.min(llmConf * 0.6 + meanSimilarity * 0.4, 0.45)
382
+ } else if (meanSimilarity < 0.40 && meanSimilarity > 0) {
379
383
  compositeConf = Math.min(llmConf * 0.6 + meanSimilarity * 0.4, 0.60)
380
384
  } else {
381
385
  compositeConf = llmConf * 0.6 + Math.min(meanSimilarity, 1) * 0.4
@@ -799,9 +803,11 @@ async function callInference(
799
803
  const message = result.choices?.[0]?.message
800
804
 
801
805
  // Extract confidence from logprobs if available, otherwise estimate
806
+ // Default is conservative (0.70) when logprobs are absent — retrieval quality
807
+ // should drive the final composite confidence, not an arbitrary high default.
802
808
  const confidence = result.choices?.[0]?.logprobs?.content?.[0]?.logprob
803
809
  ? Math.exp(result.choices[0].logprobs.content[0].logprob)
804
- : 0.85 // Default confidence when not provided
810
+ : 0.70
805
811
 
806
812
  return {
807
813
  content: message?.content || '',
@@ -1195,6 +1201,34 @@ async function handleEscalation(
1195
1201
  })
1196
1202
  .eq('id', threadId)
1197
1203
 
1204
+ // If the thread targets an item (e.g., a support ticket), mark that item as
1205
+ // human-assigned so it shows up in the human queue and stops AI responses.
1206
+ const { data: thread } = await adminDb
1207
+ .from('threads')
1208
+ .select('target_type, target_id')
1209
+ .eq('id', threadId)
1210
+ .single()
1211
+ if (thread?.target_type === 'items' && thread?.target_id) {
1212
+ const { data: item } = await adminDb
1213
+ .from('items')
1214
+ .select('status, data')
1215
+ .eq('id', thread.target_id)
1216
+ .single()
1217
+ if (item && item.status !== 'human_assigned' && item.status !== 'resolved') {
1218
+ await adminDb
1219
+ .from('items')
1220
+ .update({
1221
+ status: 'human_assigned',
1222
+ data: {
1223
+ ...(item.data || {}),
1224
+ aim_escalation_reason: 'low_confidence'
1225
+ },
1226
+ updated_at: new Date().toISOString()
1227
+ })
1228
+ .eq('id', thread.target_id)
1229
+ }
1230
+ }
1231
+
1198
1232
  // Fire trigger event
1199
1233
  await emitAudit(ctx, 'agent.inference.low_confidence', {
1200
1234
  type: 'thread',
@@ -1,3 +1,4 @@
1
+ // ─── CHUNK_START: PERMISSIONS_OVERVIEW ──────────────────────────────────────────────
1
2
  /**
2
3
  * @module permissions
3
4
  * @audience both
@@ -32,12 +33,13 @@
32
33
  * @seeAlso index.ts (stable export surface for custom code)
33
34
  */
34
35
 
36
+ // ─── CHUNK_END: PERMISSIONS_OVERVIEW ────────────────────────────────────────────────
37
+
38
+ // ─── CHUNK_START: PERMISSIONS_TYPES ──────────────────────────────────────────────
35
39
  import { adminDb } from './db'
36
40
  import { Principal } from './principal'
37
41
  import { CoreContext } from './middleware'
38
42
 
39
- // ─── TYPES ────────────────────────────────────────────────────────────────────
40
-
41
43
  /**
42
44
  * Result of a permission resolution for a principal + record + action combination.
43
45
  *
@@ -66,9 +68,9 @@ export interface PermissionResult {
66
68
  }
67
69
 
68
70
  type RequestContext = CoreContext
71
+ // ─── CHUNK_END: PERMISSIONS_TYPES ────────────────────────────────────────────────
69
72
 
70
- // ─── ENGINE CLASS ────────────────────────────────────────────────────────────
71
-
73
+ // ─── CHUNK_START: PERMISSIONS_ENGINE_CLASS ──────────────────────────────────────────────
72
74
  /**
73
75
  * The single permission engine for all authorization in Spine.
74
76
  *
@@ -87,11 +89,12 @@ type RequestContext = CoreContext
87
89
  * @testUnit tests/unit/permissions.test.ts
88
90
  * @testIntegration tests/integration/isolation.test.ts, admin-data-accounts.test.ts
89
91
  */
92
+ // ─── CHUNK_END: PERMISSIONS_ENGINE_CLASS ────────────────────────────────────────────────
93
+
94
+ // ─── CHUNK_START: PERMISSIONS_SURFACE_CLASSIFICATION ──────────────────────────────────────────────
90
95
  class _PermissionEngineInternal {
91
96
  private static instance: _PermissionEngineInternal
92
97
 
93
- // ─── SURFACE CLASSIFICATION ───────────────────────────────────────────────
94
-
95
98
  // Surface classification tables
96
99
  private readonly SECOND_SURFACE_TABLES = new Set([
97
100
  'apps', 'app', 'pipelines', 'triggers', 'ai_agents', 'embeddings',
@@ -189,7 +192,9 @@ class _PermissionEngineInternal {
189
192
  return _PermissionEngineInternal.instance
190
193
  }
191
194
 
192
- // ─── FIRST SURFACE — RUNTIME DATA ──────────────────────────────────────────
195
+ // ─── CHUNK_END: PERMISSIONS_SURFACE_CLASSIFICATION ────────────────────────────────────────────────
196
+
197
+ // ─── CHUNK_START: PERMISSIONS_FIRST_SURFACE_RESOLVE ──────────────────────────────────────────────
193
198
 
194
199
  /**
195
200
  * Resolves record-level and field-level permissions for a human principal
@@ -334,7 +339,9 @@ class _PermissionEngineInternal {
334
339
  }
335
340
  }
336
341
 
337
- // ─── SECOND SURFACE — CONFIG OBJECTS ────────────────────────────────────────
342
+ // ─── CHUNK_END: PERMISSIONS_FIRST_SURFACE_RESOLVE ────────────────────────────────────────────────
343
+
344
+ // ─── CHUNK_START: PERMISSIONS_SECOND_SURFACE_CONFIG_OBJECTS ──────────────────────────────────────────────
338
345
 
339
346
  /**
340
347
  * Checks whether the principal in `ctx` may perform `action` on a second-surface
@@ -465,7 +472,9 @@ class _PermissionEngineInternal {
465
472
  return { valid: false, error: 'Insufficient permissions for this operation' }
466
473
  }
467
474
 
468
- // ─── THIRD SURFACE — SYSTEM METADATA ────────────────────────────────────────
475
+ // ─── CHUNK_END: PERMISSIONS_SECOND_SURFACE_CONFIG_OBJECTS ────────────────────────────────────────────────
476
+
477
+ // ─── CHUNK_START: PERMISSIONS_THIRD_SURFACE_SYSTEM_METADATA ──────────────────────────────────────────────
469
478
 
470
479
  /**
471
480
  * Checks whether the principal may access a third-surface system metadata record
@@ -584,7 +593,9 @@ class _PermissionEngineInternal {
584
593
  return { valid: false, error: 'Insufficient permissions for this operation' }
585
594
  }
586
595
 
587
- // ─── SHARED HELPERS ──────────────────────────────────────────────────────────
596
+ // ─── CHUNK_END: PERMISSIONS_THIRD_SURFACE_SYSTEM_METADATA ────────────────────────────────────────────────
597
+
598
+ // ─── CHUNK_START: PERMISSIONS_SHARED_HELPERS ──────────────────────────────────────────────
588
599
 
589
600
  /**
590
601
  * Returns true if the principal in `ctx` holds the `system_admin` role.
@@ -608,7 +619,9 @@ class _PermissionEngineInternal {
608
619
  return ctx.principal?.roles?.includes('system_admin') || false
609
620
  }
610
621
 
611
- // ─── PUBLIC SURFACE ROUTER METHODS ──────────────────────────────────────────
622
+ // ─── CHUNK_END: PERMISSIONS_SHARED_HELPERS ────────────────────────────────────────────────
623
+
624
+ // ─── CHUNK_START: PERMISSIONS_CAN_ACCESS_RECORD ──────────────────────────────────────────────
612
625
 
613
626
  /**
614
627
  * Checks whether the principal in `ctx` may perform `action` on `record`.
@@ -776,6 +789,9 @@ class _PermissionEngineInternal {
776
789
  * const safe = await sanitizeRecordData(ctx, rawRecord, 'ticket')
777
790
  * ```
778
791
  */
792
+ // ─── CHUNK_END: PERMISSIONS_CAN_ACCESS_RECORD ────────────────────────────────────────────────
793
+
794
+ // ─── CHUNK_START: PERMISSIONS_SANITIZE_RECORD_DATA ──────────────────────────────────────────────
779
795
  async sanitizeRecordData(
780
796
  ctx: RequestContext,
781
797
  record: any,
@@ -803,7 +819,9 @@ class _PermissionEngineInternal {
803
819
  return this.sanitizeFirstSurfaceRecordData(ctx, record, typeSlug || '')
804
820
  }
805
821
  }
822
+ // ─── CHUNK_END: PERMISSIONS_SANITIZE_RECORD_DATA ────────────────────────────────────────────────
806
823
 
824
+ // ─── CHUNK_START: PERMISSIONS_SANITIZE_FIRST_SURFACE ──────────────────────────────────────────────
807
825
  /**
808
826
  * Field-level filter and formatter for first-surface (runtime data) records.
809
827
  *
@@ -924,7 +942,9 @@ class _PermissionEngineInternal {
924
942
 
925
943
  return sanitized
926
944
  }
945
+ // ─── CHUNK_END: PERMISSIONS_SANITIZE_FIRST_SURFACE ────────────────────────────────────────────────
927
946
 
947
+ // ─── CHUNK_START: PERMISSIONS_VALIDATE_UPDATE ──────────────────────────────────────────────
928
948
  /**
929
949
  * Validates that the principal has write permission for every field in `updateData`,
930
950
  * and sanitizes the data using the validation schema before returning it.
@@ -992,7 +1012,9 @@ class _PermissionEngineInternal {
992
1012
  return this.validateFirstSurfaceUpdatePermissions(ctx, updateData, existingRecord, typeSlug || '')
993
1013
  }
994
1014
  }
1015
+ // ─── CHUNK_END: PERMISSIONS_VALIDATE_UPDATE ────────────────────────────────────────────────
995
1016
 
1017
+ // ─── CHUNK_START: PERMISSIONS_VALIDATE_FIRST_SURFACE ──────────────────────────────────────────────
996
1018
  /**
997
1019
  * Field-level write validation and sanitization for first-surface update payloads.
998
1020
  *
@@ -1103,8 +1125,9 @@ class _PermissionEngineInternal {
1103
1125
 
1104
1126
  return { valid: true, sanitizedData }
1105
1127
  }
1128
+ // ─── CHUNK_END: PERMISSIONS_VALIDATE_FIRST_SURFACE ────────────────────────────────────────────────
1106
1129
 
1107
- // ─── UNIFIED PRINCIPAL METHODS ───────────────────────────────────────────────
1130
+ // ─── CHUNK_START: PERMISSIONS_CAN_PRINCIPAL_ACCESS_RECORD ──────────────────────────────────────────────
1108
1131
 
1109
1132
  /**
1110
1133
  * Unified permission check for all principal types (human, machine, cron, trigger).
@@ -1180,7 +1203,9 @@ class _PermissionEngineInternal {
1180
1203
 
1181
1204
  return false
1182
1205
  }
1183
-
1206
+ // ─── CHUNK_END: PERMISSIONS_CAN_PRINCIPAL_ACCESS_RECORD ────────────────────────────────────────────────
1207
+
1208
+ // ─── CHUNK_START: PERMISSIONS_MACHINE_SCOPE ──────────────────────────────────────────────
1184
1209
  /**
1185
1210
  * Evaluates whether a machine principal's scopes permit the requested action.
1186
1211
  *
@@ -1227,7 +1252,9 @@ class _PermissionEngineInternal {
1227
1252
 
1228
1253
  return false
1229
1254
  }
1230
-
1255
+ // ─── CHUNK_END: PERMISSIONS_MACHINE_SCOPE ────────────────────────────────────────────────
1256
+
1257
+ // ─── CHUNK_START: PERMISSIONS_PERMISSION_SUMMARY ──────────────────────────────────────────────
1231
1258
  /**
1232
1259
  * Returns a structured summary of a principal's permission posture for use
1233
1260
  * in audit log entries.
@@ -1274,7 +1301,9 @@ class _PermissionEngineInternal {
1274
1301
  return { type: 'unknown' }
1275
1302
  }
1276
1303
  }
1304
+ // ─── CHUNK_END: PERMISSIONS_PERMISSION_SUMMARY ────────────────────────────────────────────────
1277
1305
 
1306
+ // ─── CHUNK_START: PERMISSIONS_SINGLETON_AND_EXPORTS ──────────────────────────────────────────────
1278
1307
  // ─── SINGLETON EXPORT ────────────────────────────────────────────────────────
1279
1308
 
1280
1309
  /**
@@ -1317,3 +1346,4 @@ export const isSystemAdmin = PermissionEngine.isSystemAdmin.bind(PermissionEngin
1317
1346
  export const canAccessRecord = PermissionEngine.canAccessRecord.bind(PermissionEngine)
1318
1347
  export const sanitizeRecordData = PermissionEngine.sanitizeRecordData.bind(PermissionEngine)
1319
1348
  export const validateUpdatePermissions = PermissionEngine.validateUpdatePermissions.bind(PermissionEngine)
1349
+ // ─── CHUNK_END: PERMISSIONS_SINGLETON_AND_EXPORTS ────────────────────────────────────────────────
@@ -0,0 +1,6 @@
1
+ -- Migration 030: Add missing updated_by column to messages table
2
+ -- The messages table was created without updated_by, causing PATCH requests
3
+ -- via admin-data to fail when the audit stamp tries to set that field.
4
+
5
+ ALTER TABLE public.messages
6
+ ADD COLUMN IF NOT EXISTS updated_by uuid REFERENCES public.people(id) ON DELETE SET NULL;
@@ -0,0 +1,60 @@
1
+ -- Migration 031: CIL vector types — patch cil_chunk type design_schema
2
+ --
3
+ -- The seed file (custom/apps/cortex/seed/types.json) defines cil_chunk with
4
+ -- no vector_types because the generate() function in embeddings.ts reads
5
+ -- vector_types from the type's design_schema at runtime.
6
+ --
7
+ -- This migration patches in the three CIL vector types for cil_chunk so that
8
+ -- calls to generate() produce semantic, structural, and contract vectors.
9
+ --
10
+ -- Also adds vector_types for cil_config (semantic only) and cil_project (semantic).
11
+ --
12
+ -- Safe to re-run: uses jsonb_set which is idempotent on the same value.
13
+ --
14
+ -- NOTE: This only patches rows where the type already exists. The seed must
15
+ -- have been applied (via spine install-app) before this migration runs.
16
+
17
+ -- ── cil_chunk: semantic + structural + contract ───────────────────────────────
18
+ UPDATE public.types
19
+ SET design_schema = jsonb_set(
20
+ design_schema,
21
+ '{vector_types}',
22
+ '["semantic", "structural", "contract"]'::jsonb,
23
+ true -- create if missing
24
+ )
25
+ WHERE kind = 'item'
26
+ AND slug = 'cil_chunk';
27
+
28
+ -- ── cil_project: semantic only (project descriptions) ─────────────────────────
29
+ UPDATE public.types
30
+ SET design_schema = jsonb_set(
31
+ design_schema,
32
+ '{vector_types}',
33
+ '["semantic"]'::jsonb,
34
+ true
35
+ )
36
+ WHERE kind = 'item'
37
+ AND slug = 'cil_project';
38
+
39
+ -- ── cil_config: semantic (config field descriptions) ──────────────────────────
40
+ UPDATE public.types
41
+ SET design_schema = jsonb_set(
42
+ design_schema,
43
+ '{vector_types}',
44
+ '["semantic"]'::jsonb,
45
+ true
46
+ )
47
+ WHERE kind = 'item'
48
+ AND slug = 'cil_config';
49
+
50
+ -- ── embedding_fields for cil_chunk structural vector ─────────────────────────
51
+ -- structural vector uses these fields from item.data for its JSON snapshot
52
+ UPDATE public.types
53
+ SET design_schema = jsonb_set(
54
+ design_schema,
55
+ '{embedding_fields,structural}',
56
+ '["chunk_type","file_path","stability","is_dead_code","boundary_confidence","doc_confidence","review_status"]'::jsonb,
57
+ true
58
+ )
59
+ WHERE kind = 'item'
60
+ AND slug = 'cil_chunk';
@@ -0,0 +1,122 @@
1
+ -- Migration 032: CIL AI Agent + Prompt Config
2
+ --
3
+ -- Seeds the Code Intelligence Layer conversational agent so it can answer
4
+ -- questions about the codebase using cil_chunk embeddings as RAG context.
5
+ --
6
+ -- The agent is scoped to the Cortex app (is_system = false so it can be
7
+ -- overridden per-tenant). A second system-level prompt config with
8
+ -- retrieval_policy.item_types = ["cil_chunk"] ensures the agent only pulls
9
+ -- code chunk embeddings rather than KB articles.
10
+ --
11
+ -- Safe to re-run: uses INSERT ... ON CONFLICT DO NOTHING on (app_id, name)
12
+ -- and ON CONFLICT (slug) DO NOTHING for prompt_configs.
13
+
14
+ -- ── CIL Conversational Agent ─────────────────────────────────────────────────
15
+ INSERT INTO public.ai_agents (
16
+ id,
17
+ app_id,
18
+ name,
19
+ description,
20
+ agent_type,
21
+ system_prompt,
22
+ model_config,
23
+ ownership,
24
+ is_system,
25
+ is_active,
26
+ created_at,
27
+ updated_at
28
+ )
29
+ SELECT
30
+ gen_random_uuid(),
31
+ (SELECT id FROM public.apps WHERE slug = 'cortex'),
32
+ 'CIL Codebase Assistant',
33
+ 'Conversational agent that answers questions about the codebase using the Code Intelligence Layer. Retrieves chunk contracts, call graphs, and documentation via semantic search.',
34
+ 'rag',
35
+ 'You are a Code Intelligence Layer assistant. Your job is to answer questions about this codebase accurately and concisely.
36
+
37
+ You have access to a semantic index of every named chunk in the codebase. Each chunk record contains:
38
+ - chunk_name: the function / class / component name
39
+ - macro: one-sentence purpose statement
40
+ - micro: implementation detail
41
+ - input_spec: parameter names, types, constraints
42
+ - output_spec: return type and shape
43
+ - side_effects: DB writes, API calls, file I/O
44
+ - stability: stable | internal | experimental
45
+ - file_path: source file location
46
+
47
+ When answering:
48
+ 1. Ground your answer in the retrieved chunk contracts — do not invent APIs.
49
+ 2. Always cite the chunk_name and file_path when referencing a specific function.
50
+ 3. If you cannot find a relevant chunk, say so explicitly — do not guess.
51
+ 4. For refactoring questions, always mention callers / blast radius if known.
52
+ 5. Keep answers concise. Use markdown code blocks for code examples.
53
+ 6. If asked "is X safe to change?", check if stability = stable before confirming.
54
+
55
+ Avoid hallucinating parameter names, return types, or side effects that are not in the retrieved contracts.',
56
+ '{"model": "gpt-4o-mini", "temperature": 0.2, "max_tokens": 1500}',
57
+ 'platform',
58
+ false,
59
+ true,
60
+ now(),
61
+ now()
62
+ WHERE NOT EXISTS (
63
+ SELECT 1 FROM public.ai_agents
64
+ WHERE app_id = (SELECT id FROM public.apps WHERE slug = 'cortex')
65
+ AND name = 'CIL Codebase Assistant'
66
+ );
67
+
68
+ -- ── CIL Agent Prompt Config ───────────────────────────────────────────────────
69
+ INSERT INTO public.prompt_configs (
70
+ id,
71
+ app_id,
72
+ slug,
73
+ name,
74
+ context_template,
75
+ model,
76
+ temperature,
77
+ max_tokens,
78
+ is_multi_turn,
79
+ max_history_messages,
80
+ confidence_threshold,
81
+ output_mode,
82
+ knowledge_sources,
83
+ available_tools,
84
+ metadata,
85
+ ownership,
86
+ is_system,
87
+ is_active,
88
+ created_at,
89
+ updated_at
90
+ )
91
+ SELECT
92
+ gen_random_uuid(),
93
+ (SELECT id FROM public.apps WHERE slug = 'cortex'),
94
+ 'cil_codebase_assistant',
95
+ 'CIL Codebase Assistant',
96
+ 'Answer the user''s question about the codebase using the retrieved chunk contracts above.
97
+ If the answer involves a specific function, always include: chunk_name, file_path, and what it does.',
98
+ 'gpt-4o-mini',
99
+ 0.2,
100
+ 1500,
101
+ true,
102
+ 10,
103
+ 0.3,
104
+ 'text',
105
+ '[]'::jsonb,
106
+ '[]'::jsonb,
107
+ jsonb_build_object(
108
+ 'retrieval_policy', jsonb_build_object(
109
+ 'item_types', jsonb_build_array('cil_chunk'),
110
+ 'vector_types', jsonb_build_array('semantic', 'contract'),
111
+ 'max_security_level', 'internal',
112
+ 'require_approved', true
113
+ )
114
+ ),
115
+ 'platform',
116
+ false,
117
+ true,
118
+ now(),
119
+ now()
120
+ WHERE NOT EXISTS (
121
+ SELECT 1 FROM public.prompt_configs WHERE slug = 'cil_codebase_assistant'
122
+ );
@@ -0,0 +1,75 @@
1
+ -- Migration 033: Add missing fields to CIL item type design_schemas
2
+ --
3
+ -- Adds fields that are written by the backend functions but were absent from
4
+ -- the design_schema definitions in types.json:
5
+ --
6
+ -- cil_chunk:
7
+ -- - hash_updated_at written by custom_cil-validate ?action=update_hash
8
+ -- - kb_type written by custom_cil-ingest pass2 (discriminator field)
9
+ --
10
+ -- cil_file:
11
+ -- - line_count written by custom_cil-ingest pass1
12
+ -- - existing_marker_names written by custom_cil-ingest pass1
13
+ -- - existing_marker_count written by custom_cil-ingest pass1
14
+ -- - kb_type discriminator field
15
+ --
16
+ -- Safe to re-run: uses jsonb || operator (merge) so existing schema fields are
17
+ -- preserved. Only adds new keys — does not overwrite existing ones.
18
+
19
+ -- ── cil_chunk — add hash_updated_at + kb_type ───────────────────────────────
20
+ UPDATE public.types
21
+ SET design_schema = design_schema || jsonb_build_object(
22
+ 'fields', (design_schema->'fields') || jsonb_build_object(
23
+ 'hash_updated_at', jsonb_build_object(
24
+ 'label', 'Hash Updated At',
25
+ 'data_type', 'text'
26
+ ),
27
+ 'kb_type', jsonb_build_object(
28
+ 'label', 'KB Type',
29
+ 'data_type', 'text'
30
+ )
31
+ )
32
+ )
33
+ WHERE slug = 'cil_chunk'
34
+ AND kind = 'item'
35
+ AND NOT (design_schema->'fields' ? 'hash_updated_at');
36
+
37
+ -- ── cil_file — add line_count, existing_marker_names, existing_marker_count, kb_type ─
38
+ UPDATE public.types
39
+ SET design_schema = design_schema || jsonb_build_object(
40
+ 'fields', (design_schema->'fields') || jsonb_build_object(
41
+ 'line_count', jsonb_build_object(
42
+ 'label', 'Line Count',
43
+ 'data_type', 'number'
44
+ ),
45
+ 'existing_marker_names', jsonb_build_object(
46
+ 'label', 'Existing Marker Names',
47
+ 'data_type', 'text'
48
+ ),
49
+ 'existing_marker_count', jsonb_build_object(
50
+ 'label', 'Existing Marker Count',
51
+ 'data_type', 'number'
52
+ ),
53
+ 'kb_type', jsonb_build_object(
54
+ 'label', 'KB Type',
55
+ 'data_type', 'text'
56
+ )
57
+ )
58
+ )
59
+ WHERE slug = 'cil_file'
60
+ AND kind = 'item'
61
+ AND NOT (design_schema->'fields' ? 'kb_type');
62
+
63
+ -- ── cil_project — add kb_type ────────────────────────────────────────────────
64
+ UPDATE public.types
65
+ SET design_schema = design_schema || jsonb_build_object(
66
+ 'fields', (design_schema->'fields') || jsonb_build_object(
67
+ 'kb_type', jsonb_build_object(
68
+ 'label', 'KB Type',
69
+ 'data_type', 'text'
70
+ )
71
+ )
72
+ )
73
+ WHERE slug = 'cil_project'
74
+ AND kind = 'item'
75
+ AND NOT (design_schema->'fields' ? 'kb_type');
@@ -0,0 +1,116 @@
1
+ -- Migration 034: CIL Self-Seed — register spine-ia as a CIL project
2
+ --
3
+ -- Seeds the spine-ia repository itself as a `cil_project` item so that
4
+ -- Pass 1–5 of the Code Intelligence Layer pipeline can be run against it
5
+ -- immediately after install, without needing to create the project via the UI.
6
+ --
7
+ -- The repo_path defaults to the standard CascadeProjects location. Operators
8
+ -- running the app on a different machine should update the repo_path via:
9
+ -- PATCH /api/custom_cil-project?action=update&id=<SPINE_IA_PROJECT_ID>
10
+ -- body: { data: { repo_path: "/path/to/your/spine-ia" } }
11
+ --
12
+ -- Safe to re-run: the INSERT is guarded by WHERE NOT EXISTS on the title.
13
+ -- The hard-coded UUID allows other migrations to reference this project.
14
+
15
+ -- Known stable UUID for the spine-ia CIL project (update if your DB uses a different value)
16
+ DO $$
17
+ DECLARE
18
+ v_cil_project_type_id uuid;
19
+ v_spine_account_id uuid;
20
+ v_cortex_app_id uuid;
21
+ v_existing_id uuid;
22
+ BEGIN
23
+ -- Resolve the cil_project type
24
+ SELECT id INTO v_cil_project_type_id
25
+ FROM public.types
26
+ WHERE slug = 'cil_project' AND kind = 'item'
27
+ LIMIT 1;
28
+
29
+ IF v_cil_project_type_id IS NULL THEN
30
+ RAISE NOTICE 'cil_project type not found — skipping CIL self-seed. Run install-app cortex first.';
31
+ RETURN;
32
+ END IF;
33
+
34
+ -- Resolve the system account (spine-system or the first account if not found)
35
+ SELECT id INTO v_spine_account_id
36
+ FROM public.accounts
37
+ WHERE slug = 'spine-system'
38
+ LIMIT 1;
39
+
40
+ IF v_spine_account_id IS NULL THEN
41
+ SELECT id INTO v_spine_account_id FROM public.accounts ORDER BY created_at LIMIT 1;
42
+ END IF;
43
+
44
+ IF v_spine_account_id IS NULL THEN
45
+ RAISE NOTICE 'No account found — skipping CIL self-seed.';
46
+ RETURN;
47
+ END IF;
48
+
49
+ -- Resolve the cortex app
50
+ SELECT id INTO v_cortex_app_id
51
+ FROM public.apps
52
+ WHERE slug = 'cortex'
53
+ LIMIT 1;
54
+
55
+ -- Check if the project already exists
56
+ SELECT id INTO v_existing_id
57
+ FROM public.items
58
+ WHERE title = 'spine-ia'
59
+ AND type_id = v_cil_project_type_id
60
+ AND account_id = v_spine_account_id
61
+ LIMIT 1;
62
+
63
+ IF v_existing_id IS NOT NULL THEN
64
+ RAISE NOTICE 'spine-ia CIL project already seeded (id: %). Skipping.', v_existing_id;
65
+ RETURN;
66
+ END IF;
67
+
68
+ -- Insert the project item
69
+ INSERT INTO public.items (
70
+ id,
71
+ type_id,
72
+ account_id,
73
+ app_id,
74
+ title,
75
+ description,
76
+ status,
77
+ is_active,
78
+ data,
79
+ created_at,
80
+ updated_at
81
+ ) VALUES (
82
+ -- Fixed UUID so other tooling can reference it; change if collision occurs
83
+ '00000001-c11a-4a1e-0000-00000000001a',
84
+ v_cil_project_type_id,
85
+ v_spine_account_id,
86
+ v_cortex_app_id,
87
+ 'spine-ia',
88
+ 'The spine-ia monorepo — Spine framework core + Cortex CIL app',
89
+ 'active',
90
+ true,
91
+ jsonb_build_object(
92
+ 'kb_type', 'cil_project',
93
+ 'project_id', '00000001-c11a-4a1e-0000-00000000001a',
94
+ 'repo_path', '/Users/thistle/CascadeProjects/spine-ia',
95
+ 'language_mix', 'TypeScript, SQL',
96
+ 'description', 'The spine-ia monorepo — Spine framework core + Cortex CIL app',
97
+ 'ingestion_status', 'not_started',
98
+ 'file_count', 0,
99
+ 'chunk_count', 0,
100
+ 'last_ingested_at', NULL,
101
+ 'globs', jsonb_build_array(
102
+ 'custom/**/*.ts', 'custom/**/*.tsx',
103
+ '.framework/functions/**/*.ts',
104
+ '.framework/migrations/**/*.sql'
105
+ ),
106
+ 'exclude_globs', jsonb_build_array(
107
+ 'node_modules/**', '.assembled/**', 'dist/**', 'build/**',
108
+ '**/*.test.ts', '**/*.spec.ts', '**/*.d.ts'
109
+ )
110
+ ),
111
+ now(),
112
+ now()
113
+ );
114
+
115
+ RAISE NOTICE 'spine-ia CIL project seeded successfully (id: 00000001-c11a-4a1e-0000-00000000001a)';
116
+ END $$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.3.114",
3
+ "version": "0.3.115",
4
4
  "description": "Multi-tenant, modular application platform for modern SaaS systems",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,6 +36,11 @@
36
36
  ".framework/migrations/027_embedding_lifecycle.sql",
37
37
  ".framework/migrations/028_fix_message_data_permissions.sql",
38
38
  ".framework/migrations/029_tighten_support_triage_prompt.sql",
39
+ ".framework/migrations/030_messages_updated_by.sql",
40
+ ".framework/migrations/031_cil_vector_types.sql",
41
+ ".framework/migrations/032_cil_agent_config.sql",
42
+ ".framework/migrations/033_cil_chunk_schema_fields.sql",
43
+ ".framework/migrations/034_cil_self_seed.sql",
39
44
  ".framework/public/",
40
45
  ".framework/scripts/",
41
46
  ".framework/seeds/",
@@ -92,7 +97,8 @@
92
97
  "test:core": "vitest run .framework/tests/unit/core-isolation.test.ts",
93
98
  "test:integration": "vitest run .framework/tests/integration",
94
99
  "test:boundary": "bash scripts/boundary-check.sh",
95
- "docs": "typedoc --entryPoints .framework/functions/_shared/index.ts --out docs/generated"
100
+ "docs": "typedoc --entryPoints .framework/functions/_shared/index.ts --out docs/generated",
101
+ "mcp": "tsx custom/apps/cortex/mcp/index.ts"
96
102
  },
97
103
  "dependencies": {
98
104
  "@base-ui/react": "^1.4.1",
@@ -174,6 +180,7 @@
174
180
  "ws": "^8.18.0"
175
181
  },
176
182
  "devDependencies": {
183
+ "@modelcontextprotocol/sdk": "^1.29.0",
177
184
  "@netlify/functions": "^2.8.2",
178
185
  "@supabase/supabase-js": "^2.49.1",
179
186
  "@tailwindcss/typography": "^0.5.19",