spine-framework 0.4.3 → 0.4.4
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.
- package/.framework/functions/_shared/agent-runner.ts +1000 -81
- package/.framework/functions/_shared/cil.ts +2 -1
- package/.framework/functions/_shared/db.ts +3 -6
- package/.framework/functions/_shared/kb.ts +61 -7
- package/.framework/functions/_shared/message-sequence.ts +53 -0
- package/.framework/functions/_shared/pipeline-runner.ts +150 -37
- package/.framework/functions/_shared/principal.ts +9 -6
- package/.framework/functions/_shared/trigger-engine.ts +149 -40
- package/.framework/functions/admin-data.ts +9 -3
- package/.framework/functions/ai-agents.ts +93 -27
- package/.framework/functions/pipeline-runner-background.ts +120 -0
- package/.framework/functions/system-cron.ts +10 -1
- package/.framework/functions/trigger-scheduler.ts +65 -5
- package/.framework/migrations/064_episode_leases.sql +196 -0
- package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
- package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
- package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
- package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
- package/.framework/schemas/spine.config.schema.json +62 -0
- package/.framework/src/App.tsx +3 -1
- package/.framework/src/components/admin/AdminSidebar.tsx +4 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
- package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
- package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
- package/.framework/src/components/layout/Layout.tsx +4 -2
- package/.framework/src/components/layout/Sidebar.tsx +4 -2
- package/.framework/src/hooks/useBranding.ts +47 -0
- package/.framework/src/lib/branding.ts +128 -0
- package/.framework/src/pages/NoAppPage.tsx +3 -1
- package/.framework/src/pages/SetupPage.tsx +5 -2
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
- package/.framework/src/pages/auth/LoginPage.tsx +4 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
- package/.framework/templates/project.json +1 -0
- package/.framework/tests/unit/agent-runner.test.ts +17 -3
- package/.framework/tests/unit/episode-lease.test.ts +695 -0
- package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
- package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
- package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
- package/.framework/tests/unit/principal-cache.test.ts +15 -0
- package/.framework/tests/unit/trigger-engine.test.ts +278 -91
- package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
- package/netlify.toml +5 -0
- package/package.json +1 -1
- package/templates/project/package-lock.json +2 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/src/lib/branding.ts"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Instance branding resolved from spine.config.json — app name, logo, favicon, document title"]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Load the optional branding block from /spine.config.json once per session, merge it over Spine defaults, and apply favicon/title to the document"]
|
|
4
|
+
// CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/hooks/useBranding.ts,spine.config.json,.framework/index.html"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding resolved object; applyBrandingToDocument performs DOM mutation" errors="fetch or parse failures fall back to DEFAULT_BRANDING" side_effects="HTTP GET /spine.config.json (single cached request); document.title and link[rel=icon] mutation"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Module-level cache plus single inflight fetch; resolveBranding fills missing keys with defaults; applyBrandingToDocument sets document.title and swaps the favicon link href" notable_decisions="Branding lives in instance-owned spine.config.json so framework upgrades can never overwrite app branding; index.html keeps the Spine default and the runtime swap handles app-branded instances"]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="framework developers"]
|
|
10
|
+
// CIL[DOC-NOTE type="contract" content="branding is a top-level sibling of registration in spine.config.json. All keys optional; absent block yields DEFAULT_BRANDING so bare framework installs stay Spine-branded."]
|
|
11
|
+
// CIL[END stable_id="~/.framework/src/lib/branding.ts:module_header:module_header"]
|
|
12
|
+
|
|
13
|
+
// CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:spinebranding:interface" type="interface" identity="SpineBranding" parent="~/.framework/src/lib/branding.ts"]
|
|
14
|
+
// CIL[DOC-MACRO purpose="Resolved instance branding shape"]
|
|
15
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Type for the branding block after defaults are applied"]
|
|
16
|
+
// CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts"]
|
|
17
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding object" errors="none" side_effects="none"]
|
|
18
|
+
// CIL[DOC-MICRO logic_summary="app_name drives headings and logo alt; title drives document.title and wordmark text; logo_url and favicon_url are public/ asset paths" notable_decisions="title falls back to app_name, then to 'Spine Framework' when no branding block exists"]
|
|
19
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
20
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
21
|
+
export interface SpineBranding {
|
|
22
|
+
app_name: string
|
|
23
|
+
title: string
|
|
24
|
+
logo_url: string
|
|
25
|
+
favicon_url: string
|
|
26
|
+
}
|
|
27
|
+
// CIL[END stable_id="~/.framework/src/lib/branding.ts:spinebranding:interface"]
|
|
28
|
+
|
|
29
|
+
// CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:default_branding:constant" type="constant" identity="DEFAULT_BRANDING" parent="~/.framework/src/lib/branding.ts"]
|
|
30
|
+
// CIL[DOC-MACRO purpose="Spine Framework default branding used when spine.config.json has no branding block"]
|
|
31
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide the fallback brand so the framework is self-branded out of the box"]
|
|
32
|
+
// CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts,.framework/index.html"]
|
|
33
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding constant" errors="none" side_effects="none"]
|
|
34
|
+
// CIL[DOC-MICRO logic_summary="Matches the static values in .framework/index.html (title 'Spine Framework', favicon /spine-logo.svg)" notable_decisions="[]"]
|
|
35
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
36
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
37
|
+
export const DEFAULT_BRANDING: SpineBranding = {
|
|
38
|
+
app_name: 'Spine',
|
|
39
|
+
title: 'Spine Framework',
|
|
40
|
+
logo_url: '/spine-logo.svg',
|
|
41
|
+
favicon_url: '/spine-logo.svg',
|
|
42
|
+
}
|
|
43
|
+
// CIL[END stable_id="~/.framework/src/lib/branding.ts:default_branding:constant"]
|
|
44
|
+
|
|
45
|
+
let cached: SpineBranding | null = null
|
|
46
|
+
let inflight: Promise<SpineBranding> | null = null
|
|
47
|
+
|
|
48
|
+
// CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:resolvebranding:function" type="function" identity="resolveBranding" parent="~/.framework/src/lib/branding.ts"]
|
|
49
|
+
// CIL[DOC-MACRO purpose="Merge a raw branding block over defaults"]
|
|
50
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Produce a complete SpineBranding from a possibly partial branding config value"]
|
|
51
|
+
// CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts"]
|
|
52
|
+
// CIL[DOC-CONTRACT inputs="raw: unknown (config.branding)" outputs="SpineBranding" errors="none" side_effects="none"]
|
|
53
|
+
// CIL[DOC-MICRO logic_summary="Each key falls back independently; title falls back to the resolved app_name so app-branded instances get a sane tab title without duplicating config" notable_decisions="non-string values treated as absent"]
|
|
54
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
55
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
56
|
+
function resolveBranding(raw: unknown): SpineBranding {
|
|
57
|
+
const b = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>
|
|
58
|
+
const appName = typeof b.app_name === 'string' && b.app_name ? b.app_name : DEFAULT_BRANDING.app_name
|
|
59
|
+
const configuredTitle = typeof b.title === 'string' && b.title ? b.title : null
|
|
60
|
+
return {
|
|
61
|
+
app_name: appName,
|
|
62
|
+
title: configuredTitle ?? (b.app_name ? appName : DEFAULT_BRANDING.title),
|
|
63
|
+
logo_url: typeof b.logo_url === 'string' && b.logo_url ? b.logo_url : DEFAULT_BRANDING.logo_url,
|
|
64
|
+
favicon_url:
|
|
65
|
+
typeof b.favicon_url === 'string' && b.favicon_url ? b.favicon_url : DEFAULT_BRANDING.favicon_url,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// CIL[END stable_id="~/.framework/src/lib/branding.ts:resolvebranding:function"]
|
|
69
|
+
|
|
70
|
+
// CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:loadbranding:function" type="function" identity="loadBranding" parent="~/.framework/src/lib/branding.ts"]
|
|
71
|
+
// CIL[DOC-MACRO purpose="Fetch and cache the instance branding from /spine.config.json"]
|
|
72
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Perform a single fetch per session and memoize the resolved branding"]
|
|
73
|
+
// CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts,.framework/src/hooks/useBranding.ts"]
|
|
74
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Promise<SpineBranding>" errors="network/parse failures resolve to DEFAULT_BRANDING" side_effects="one HTTP GET /spine.config.json per session"]
|
|
75
|
+
// CIL[DOC-MICRO logic_summary="Returns cached value if present; otherwise shares one inflight promise across callers; any failure caches defaults so a missing config never retries per component" notable_decisions="[]"]
|
|
76
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
77
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
78
|
+
export function loadBranding(): Promise<SpineBranding> {
|
|
79
|
+
if (cached) return Promise.resolve(cached)
|
|
80
|
+
if (!inflight) {
|
|
81
|
+
inflight = fetch('/spine.config.json')
|
|
82
|
+
.then(res => (res.ok ? res.json() : null))
|
|
83
|
+
.then(config => {
|
|
84
|
+
cached = resolveBranding(config?.branding)
|
|
85
|
+
return cached
|
|
86
|
+
})
|
|
87
|
+
.catch(() => {
|
|
88
|
+
cached = DEFAULT_BRANDING
|
|
89
|
+
return cached
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
return inflight
|
|
93
|
+
}
|
|
94
|
+
// CIL[END stable_id="~/.framework/src/lib/branding.ts:loadbranding:function"]
|
|
95
|
+
|
|
96
|
+
// CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:getcachedbranding:function" type="function" identity="getCachedBranding" parent="~/.framework/src/lib/branding.ts"]
|
|
97
|
+
// CIL[DOC-MACRO purpose="Synchronous read of resolved branding, or defaults before the fetch lands"]
|
|
98
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Give render paths a branding value without awaiting"]
|
|
99
|
+
// CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts"]
|
|
100
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding" errors="none" side_effects="none"]
|
|
101
|
+
// CIL[DOC-MICRO logic_summary="cached ?? DEFAULT_BRANDING" notable_decisions="[]"]
|
|
102
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
103
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
104
|
+
export function getCachedBranding(): SpineBranding {
|
|
105
|
+
return cached ?? DEFAULT_BRANDING
|
|
106
|
+
}
|
|
107
|
+
// CIL[END stable_id="~/.framework/src/lib/branding.ts:getcachedbranding:function"]
|
|
108
|
+
|
|
109
|
+
// CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:applybrandingtodocument:function" type="function" identity="applyBrandingToDocument" parent="~/.framework/src/lib/branding.ts"]
|
|
110
|
+
// CIL[DOC-MACRO purpose="Apply resolved branding to document.title and the favicon link"]
|
|
111
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Swap the framework-shipped favicon/title for instance branding at runtime"]
|
|
112
|
+
// CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts,.framework/index.html"]
|
|
113
|
+
// CIL[DOC-CONTRACT inputs="branding: SpineBranding" outputs="void" errors="none" side_effects="sets document.title; rewrites or creates link[rel~=icon] in document.head"]
|
|
114
|
+
// CIL[DOC-MICRO logic_summary="Idempotent DOM mutation — safe to call on every branding state change from every consumer" notable_decisions="runtime swap keeps .framework/index.html a static framework-owned file that upgrades can replace freely"]
|
|
115
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
116
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
117
|
+
export function applyBrandingToDocument(branding: SpineBranding): void {
|
|
118
|
+
if (branding.title) document.title = branding.title
|
|
119
|
+
if (!branding.favicon_url) return
|
|
120
|
+
let link = document.querySelector<HTMLLinkElement>('link[rel~="icon"]')
|
|
121
|
+
if (!link) {
|
|
122
|
+
link = document.createElement('link')
|
|
123
|
+
link.rel = 'icon'
|
|
124
|
+
document.head.appendChild(link)
|
|
125
|
+
}
|
|
126
|
+
link.href = branding.favicon_url
|
|
127
|
+
}
|
|
128
|
+
// CIL[END stable_id="~/.framework/src/lib/branding.ts:applybrandingtodocument:function"]
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import React from 'react'
|
|
12
12
|
import { useAuth } from '../contexts/AuthContext'
|
|
13
|
+
import { useBranding } from '../hooks/useBranding'
|
|
13
14
|
// CIL[END stable_id="~/.framework/src/pages/NoAppPage.tsx:module_header:module_header"]
|
|
14
15
|
|
|
15
16
|
// CIL[BEGIN stable_id="~/.framework/src/pages/NoAppPage.tsx:noapppage:component" type="component" identity="NoAppPage" parent="~/.framework/src/pages/NoAppPage.tsx"]
|
|
@@ -22,12 +23,13 @@ import { useAuth } from '../contexts/AuthContext'
|
|
|
22
23
|
// CIL[DOC-STABILITY stability="experimental"]
|
|
23
24
|
export function NoAppPage() {
|
|
24
25
|
const { logout } = useAuth()
|
|
26
|
+
const branding = useBranding()
|
|
25
27
|
|
|
26
28
|
return (
|
|
27
29
|
<div className="min-h-screen flex items-center justify-center bg-slate-50 px-4">
|
|
28
30
|
<div className="max-w-md w-full text-center space-y-6">
|
|
29
31
|
<div className="mx-auto flex items-center justify-center">
|
|
30
|
-
<img src=
|
|
32
|
+
<img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
|
|
31
33
|
</div>
|
|
32
34
|
<div className="space-y-2">
|
|
33
35
|
<h1 className="text-2xl font-semibold text-slate-900">No apps yet</h1>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import React from 'react'
|
|
12
12
|
import { Link } from 'react-router-dom'
|
|
13
|
+
import { useBranding } from '../hooks/useBranding'
|
|
13
14
|
// CIL[DOC-VECTOR type="context" content="purpose='Declare module-level imports for the Setup page'; No verified external callers; imports: none"]
|
|
14
15
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
15
16
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
@@ -28,15 +29,17 @@ import { Link } from 'react-router-dom'
|
|
|
28
29
|
// CIL[DOC-STABILITY stable]
|
|
29
30
|
|
|
30
31
|
export function SetupPage() {
|
|
32
|
+
const branding = useBranding()
|
|
33
|
+
|
|
31
34
|
return (
|
|
32
35
|
<div className="min-h-screen flex items-center justify-center bg-slate-50 py-12 px-4 sm:px-6 lg:px-8">
|
|
33
36
|
<div className="max-w-lg w-full space-y-8">
|
|
34
37
|
<div className="text-center">
|
|
35
38
|
<div className="mx-auto flex items-center justify-center">
|
|
36
|
-
<img src=
|
|
39
|
+
<img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
|
|
37
40
|
</div>
|
|
38
41
|
<h2 className="mt-6 text-3xl font-extrabold text-slate-900">
|
|
39
|
-
Welcome to
|
|
42
|
+
Welcome to {branding.app_name}
|
|
40
43
|
</h2>
|
|
41
44
|
<p className="mt-2 text-sm text-slate-600">
|
|
42
45
|
Your project is running but no apps are installed yet.
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
finalizeRegistration,
|
|
18
18
|
readPendingRegistration,
|
|
19
19
|
} from '../../lib/registration'
|
|
20
|
+
import { useBranding } from '../../hooks/useBranding'
|
|
20
21
|
// CIL[DOC-VECTOR type="context" content="purpose='Defines the AuthConfirmPage React component for confirming email signup and password reset tokens.'; No verified external callers; imports: .framework/src/lib/supabase.ts,.framework/src/components/ui/LoadingSpinner.tsx"]
|
|
21
22
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
22
23
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
@@ -59,6 +60,7 @@ export function AuthConfirmPage() {
|
|
|
59
60
|
const [searchParams] = useSearchParams()
|
|
60
61
|
const navigate = useNavigate()
|
|
61
62
|
const { refreshUser } = useAuth()
|
|
63
|
+
const branding = useBranding()
|
|
62
64
|
|
|
63
65
|
const tokenHash = searchParams.get('token_hash')
|
|
64
66
|
const type = searchParams.get('type') as 'signup' | 'recovery' | string | null
|
|
@@ -275,7 +277,7 @@ export function AuthConfirmPage() {
|
|
|
275
277
|
<div className="max-w-md w-full space-y-8">
|
|
276
278
|
<div>
|
|
277
279
|
<div className="mx-auto flex items-center justify-center">
|
|
278
|
-
<img src=
|
|
280
|
+
<img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
|
|
279
281
|
</div>
|
|
280
282
|
<h2 className="mt-6 text-center text-3xl font-extrabold text-slate-900">
|
|
281
283
|
Set a new password
|
|
@@ -13,6 +13,7 @@ import { Link } from 'react-router-dom'
|
|
|
13
13
|
import { supabase } from '../../lib/supabase'
|
|
14
14
|
import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
|
|
15
15
|
import { TurnstileWidget, turnstileEnabled } from '../../components/auth/TurnstileWidget'
|
|
16
|
+
import { useBranding } from '../../hooks/useBranding'
|
|
16
17
|
|
|
17
18
|
// CIL[DOC-VECTOR type="context" content="purpose='Provides imports and entry-point metadata for the ForgotPasswordPage component.'; No verified external callers; imports: .framework/src/lib/supabase.ts,.framework/src/components/ui/LoadingSpinner.tsx"]
|
|
18
19
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
@@ -36,6 +37,7 @@ export function ForgotPasswordPage() {
|
|
|
36
37
|
const [isLoading, setIsLoading] = useState(false)
|
|
37
38
|
const [submitted, setSubmitted] = useState(false)
|
|
38
39
|
const [error, setError] = useState('')
|
|
40
|
+
const branding = useBranding()
|
|
39
41
|
|
|
40
42
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
41
43
|
e.preventDefault()
|
|
@@ -91,7 +93,7 @@ export function ForgotPasswordPage() {
|
|
|
91
93
|
<div className="max-w-md w-full space-y-8">
|
|
92
94
|
<div>
|
|
93
95
|
<div className="mx-auto flex items-center justify-center">
|
|
94
|
-
<img src=
|
|
96
|
+
<img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
|
|
95
97
|
</div>
|
|
96
98
|
<h2 className="mt-6 text-center text-3xl font-extrabold text-slate-900">
|
|
97
99
|
Reset your password
|
|
@@ -13,6 +13,7 @@ import { Link, useNavigate } from 'react-router-dom'
|
|
|
13
13
|
import { useAuth } from '../../contexts/AuthContext'
|
|
14
14
|
import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
|
|
15
15
|
import { TurnstileWidget, turnstileEnabled } from '../../components/auth/TurnstileWidget'
|
|
16
|
+
import { useBranding } from '../../hooks/useBranding'
|
|
16
17
|
|
|
17
18
|
// CIL[DOC-VECTOR type="context" content="purpose='Module header for the LoginPage component: imports, dependencies, and module-level context.'; No verified external callers; imports: .framework/src/contexts/AuthContext.tsx,.framework/src/components/ui/LoadingSpinner.tsx"]
|
|
18
19
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
@@ -37,6 +38,7 @@ export function LoginPage() {
|
|
|
37
38
|
const [isLoading, setIsLoading] = useState(false)
|
|
38
39
|
|
|
39
40
|
const { login } = useAuth()
|
|
41
|
+
const branding = useBranding()
|
|
40
42
|
const navigate = useNavigate()
|
|
41
43
|
|
|
42
44
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
@@ -74,10 +76,10 @@ export function LoginPage() {
|
|
|
74
76
|
<div className="max-w-md w-full space-y-8">
|
|
75
77
|
<div>
|
|
76
78
|
<div className="mx-auto flex items-center justify-center">
|
|
77
|
-
<img src=
|
|
79
|
+
<img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
|
|
78
80
|
</div>
|
|
79
81
|
<h2 className="mt-6 text-center text-3xl font-extrabold text-slate-900">
|
|
80
|
-
Sign in to
|
|
82
|
+
Sign in to {branding.app_name}
|
|
81
83
|
</h2>
|
|
82
84
|
<p className="mt-2 text-center text-sm text-slate-600">
|
|
83
85
|
Or{' '}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
PendingRegistrationIntent,
|
|
24
24
|
} from '../../lib/registration'
|
|
25
25
|
import { TurnstileWidget, turnstileEnabled } from '../../components/auth/TurnstileWidget'
|
|
26
|
+
import { useBranding } from '../../hooks/useBranding'
|
|
26
27
|
// CIL[DOC-NOTE type="change" content="Batch4b: POST invites calls go through apiFetch (authenticated); pending-registration intent helpers imported for confirm-email resume"]
|
|
27
28
|
// CIL[END stable_id="~/.framework/src/pages/auth/RegisterPage.tsx:module_header:module_header"]
|
|
28
29
|
// CIL[BEGIN stable_id="~/.framework/src/pages/auth/RegisterPage.tsx:usetype:type" type="type" identity="UseType" parent="~/.framework/src/pages/auth/RegisterPage.tsx"]
|
|
@@ -270,6 +271,7 @@ export function RegisterPage() {
|
|
|
270
271
|
const [searchParams] = useSearchParams()
|
|
271
272
|
const navigate = useNavigate()
|
|
272
273
|
const { user, refreshUser, logout } = useAuth()
|
|
274
|
+
const branding = useBranding()
|
|
273
275
|
const inviteToken = searchParams.get('t')
|
|
274
276
|
const resumeRequested = searchParams.get('resume') === '1'
|
|
275
277
|
|
|
@@ -652,7 +654,7 @@ export function RegisterPage() {
|
|
|
652
654
|
<div className="max-w-md w-full space-y-8">
|
|
653
655
|
<div>
|
|
654
656
|
<div className="mx-auto flex items-center justify-center">
|
|
655
|
-
<img src=
|
|
657
|
+
<img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
|
|
656
658
|
</div>
|
|
657
659
|
<h2 className="mt-6 text-center text-3xl font-extrabold text-slate-900">
|
|
658
660
|
{isInvite ? 'Complete your registration' : 'Create your account'}
|
|
@@ -1370,7 +1370,7 @@ describe('executeToolAction — cil_chunk_lookup code content search', () => {
|
|
|
1370
1370
|
describe('executeToolAction — escalate_to_human', () => {
|
|
1371
1371
|
beforeEach(() => vi.clearAllMocks())
|
|
1372
1372
|
|
|
1373
|
-
function mockEscalationFlow(opts: { escalationStatus?: string } = {}) {
|
|
1373
|
+
function mockEscalationFlow(opts: { escalationStatus?: string; owner?: string } = {}) {
|
|
1374
1374
|
const updates: { table: string; payload: any }[] = []
|
|
1375
1375
|
vi.mocked(adminDb.from).mockImplementation((table: string) => {
|
|
1376
1376
|
const q: any = {
|
|
@@ -1381,7 +1381,7 @@ describe('executeToolAction — escalate_to_human', () => {
|
|
|
1381
1381
|
update: vi.fn((payload: any) => { updates.push({ table, payload }); return q }),
|
|
1382
1382
|
single: vi.fn(() => {
|
|
1383
1383
|
if (table === 'threads' && q._cols === 'data') {
|
|
1384
|
-
return Promise.resolve({ data: { data: { escalation_status: opts.escalationStatus } }, error: null })
|
|
1384
|
+
return Promise.resolve({ data: { data: { owner: opts.owner, escalation_status: opts.escalationStatus } }, error: null })
|
|
1385
1385
|
}
|
|
1386
1386
|
if (table === 'threads' && q._cols === 'target_type, target_id') {
|
|
1387
1387
|
return Promise.resolve({ data: { target_type: 'items', target_id: 'item-1' }, error: null })
|
|
@@ -1419,7 +1419,7 @@ describe('executeToolAction — escalate_to_human', () => {
|
|
|
1419
1419
|
expect(res).toEqual({ escalated: true, already_escalated: false, reason: 'no relevant docs' })
|
|
1420
1420
|
expect(updates).toContainEqual({
|
|
1421
1421
|
table: 'threads',
|
|
1422
|
-
payload: { data: {
|
|
1422
|
+
payload: { data: { owner: 'human' } }
|
|
1423
1423
|
})
|
|
1424
1424
|
expect(updates).toContainEqual({
|
|
1425
1425
|
table: 'items',
|
|
@@ -1440,6 +1440,20 @@ describe('executeToolAction — escalate_to_human', () => {
|
|
|
1440
1440
|
expect(res.already_escalated).toBe(true)
|
|
1441
1441
|
expect(updates).toHaveLength(0)
|
|
1442
1442
|
})
|
|
1443
|
+
|
|
1444
|
+
it('dedupes when the thread is already human-owned', async () => {
|
|
1445
|
+
const updates = mockEscalationFlow({ owner: 'human' })
|
|
1446
|
+
|
|
1447
|
+
const res = await executeToolAction(
|
|
1448
|
+
{ handler: 'escalate_to_human' },
|
|
1449
|
+
{ reason: 'still stuck' },
|
|
1450
|
+
makeCtx(),
|
|
1451
|
+
{ threadId: THREAD_ID, config: escConfig }
|
|
1452
|
+
)
|
|
1453
|
+
|
|
1454
|
+
expect(res.already_escalated).toBe(true)
|
|
1455
|
+
expect(updates).toHaveLength(0)
|
|
1456
|
+
})
|
|
1443
1457
|
})
|
|
1444
1458
|
// CIL[DOC-VECTOR type="context" content="purpose='Test executeToolAction escalate_to_human handler'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/audit.ts,.framework/functions/_shared/llm.ts,.framework/functions/_shared/outcomes.ts,.framework/functions/admin-data.ts,.framework/functions/integration-outbound.ts,.framework/functions/_shared/agent-runner.ts,.framework/functions/_shared/cil.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
1445
1459
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|