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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
// ───
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|