spine-framework 0.3.114 → 0.3.116

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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.3.114",
3
+ "version": "0.3.116",
4
4
  "description": "Multi-tenant, modular application platform for modern SaaS systems",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,6 +36,7 @@
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",
39
40
  ".framework/public/",
40
41
  ".framework/scripts/",
41
42
  ".framework/seeds/",
@@ -92,7 +93,8 @@
92
93
  "test:core": "vitest run .framework/tests/unit/core-isolation.test.ts",
93
94
  "test:integration": "vitest run .framework/tests/integration",
94
95
  "test:boundary": "bash scripts/boundary-check.sh",
95
- "docs": "typedoc --entryPoints .framework/functions/_shared/index.ts --out docs/generated"
96
+ "docs": "typedoc --entryPoints .framework/functions/_shared/index.ts --out docs/generated",
97
+ "mcp": "tsx custom/apps/cortex/mcp/index.ts"
96
98
  },
97
99
  "dependencies": {
98
100
  "@base-ui/react": "^1.4.1",
@@ -174,6 +176,7 @@
174
176
  "ws": "^8.18.0"
175
177
  },
176
178
  "devDependencies": {
179
+ "@modelcontextprotocol/sdk": "^1.29.0",
177
180
  "@netlify/functions": "^2.8.2",
178
181
  "@supabase/supabase-js": "^2.49.1",
179
182
  "@tailwindcss/typography": "^0.5.19",