Core declaration blocks
5.1 @plugins
| @plugins {
crm = company/salesforce@1.0 {
instance = "production"
api_version = "v58"
}
ocr = scailabs/document-ocr@2.0
scaidrive = scailabs/scaidrive@1.0
erp = company/exact-online@3.0
}
|
5.2 @llm / @models — AI Model Declarations
@models is the preferred form; @llm is accepted for backward compatibility.
Both are identical in behavior. Each entry declares a named model role with
provider, model identifier, and modality configuration.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 | @models {
// Text completion / reasoning models
primary = {
provider = "scaigrid" // "scaigrid" | "openai"
model = "scailabs/poolnoodle-omni"
temperature = 0.5
role = "general"
modalities = [:text, :structured_output, :vision]
fallback = [
{ provider = "scaigrid", model = "scailabs/poolnoodle-turbo" }
]
}
fast = {
provider = "scaigrid"
model = "scailabs/poolnoodle-mini"
temperature = 0.3
role = "utility"
modalities = [:text, :structured_output]
use_when = "classification, simple extraction, tagging"
}
creative = {
provider = "scaigrid"
model = "scailabs/poolnoodle-omni"
temperature = 0.9
role = "creator"
modalities = [:text]
}
critic = {
provider = "scaigrid"
model = "scailabs/poolnoodle-omni"
temperature = 0.2
role = "reviewer"
modalities = [:text, :structured_output]
}
// Non-text modalities
voice = {
provider = "scaigrid"
model = "scai-tts-1"
role = "voice"
modalities = [:tts]
}
transcriber = {
provider = "openai"
model = "whisper-1"
role = "transcription"
modalities = [:stt]
}
embedder = {
provider = "scaigrid"
model = "scai-embed-1"
role = "embedding"
modalities = [:embedding]
}
}
|
When provider is omitted, defaults to "scaigrid". When modalities is
omitted, defaults to [:text, :structured_output]. See Model Provider spec
for the full provider interface and supported modalities.
5.3 @memory
Persistent state owned exclusively by the instance. For :entity Cores,
each entity key gets its own partition. For :stateless Cores, memory
is per-Core (one partition, atomic ops). For :singleton, one partition.
No instance can access another instance's memory.
| @memory {
vendor_aliases: map[string, string]
categorization_history: array[CategorizationRecord]
interaction_count: int
}
|
Access via memory.* (see §9 Memory API).
5.4 @config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | @config {
@param api_endpoint: string {
description = "CRM API endpoint"
required = true
}
@param max_retries: int = 3 @hot_reload {
description = "Maximum retry attempts"
validation = value >= 0 && value <= 10
}
@param urgency_threshold: float = 0.7 @runtime_configurable {
description = "Threshold for urgent escalation"
validation = value >= 0.0 && value <= 1.0
}
@param api_key: string @secret {
description = "CRM API key"
required = true
}
@param keywords: array[string] = ["urgent", "legal"] @hot_reload @runtime_configurable {
description = "Keywords that trigger escalation"
}
}
|
5.5 @constraints
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | @constraints {
never = [
"promise refund > €100 without approval",
"share customer data with other customers",
"admit legal liability"
]
always = [
"log all interactions to CRM",
"identify customer before discussing account details",
"respect GDPR data access/deletion requests"
]
prefer = [
"resolve without escalation when possible",
"de-escalation over defensiveness"
]
}
|
5.6 @conversation_policy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | @conversation_policy {
routing = {
match_strategy = "flexible"
match_on = [sender, session_id, topic_similarity]
similarity_threshold = 0.8
no_match = "new_conversation"
}
context = {
always = [customer.profile, customer.tier]
if_relevant = [customer.billing_status, customer.recent_tickets(30d)]
on_demand = [customer.full_history]
max_history_turns = 50
}
timeout = {
idle = 30m
absolute = 4h
idle_warning = 25m
idle_message = "Are you still there?"
}
channels = [chat, email, api, internal]
}
|
5.7 @reference — Read-Only Reference Data
Data injected at deployment time. Available to all instances of a Core.
Immutable at runtime — no instance can write to it. Replaces the need
for shared memory in most cases.
| @reference {
faq: map[string, FAQEntry] // loaded from faq.json
product_catalog: array[Product] // loaded from catalog.json
escalation_rules: array[EscalationRule] // loaded from rules.json
compliance_templates: map[string, Template] // loaded from templates/
}
|
Access in flows via reference.*:
| @flow handle_question(customer_id: string, question: string): Response {
@rigid {
// Read-only — same for all instances, fast local reads
relevant_faq = reference.faq.search(question)
rules = reference.escalation_rules
}
}
|
Key properties:
- Loaded at Core deployment or activation time
- Source: files in deployment bundle or external URIs
- Refresh: redeployment or host-level refresh signal
- Can be cached in-memory (immutable → no invalidation needed)
5.8 @triggers — Trigger Declarations
Declares how external events map to flows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | @triggers {
@webhook invoice_uploaded {
flow = process_invoice
method = "POST"
path = "/webhooks/invoice"
auth = "hmac"
}
@schedule daily_reconcile {
flow = reconcile_batch
cron = "0 2 * * *"
timezone = "Europe/Amsterdam"
}
@api process {
flow = process_invoice
// Exposed as: POST /cores/{core_id}/flows/process
}
}
|
Trigger dispatch is a Host responsibility — the runtime declares
triggers, the Host implements the transport (HTTP, cron, etc.).
5.9 @on — Event Subscriptions
Subscribe to events emitted by other Cores:
| @on invoice_processed from core://invoice-processor {
flow = record_transaction
}
@on payment_received from core://payment-gateway {
flow = reconcile_payment
}
|
Event types can be declared in @core_interface for compile-time
type safety. Events are delivered at-least-once with per-source ordering.
5.10 @internal — Internal Flow Marker
Marks a flow as not reachable from triggers. Internal flows don't need
the entity key parameter (they're already running within an instance context):
1
2
3
4
5
6
7
8
9
10
11
12 | @internal
@flow build_context(): ConversationContext {
// Called only via @call from other flows within this Core
// Does not need customer_id parameter even though
// this Core is :entity(key = "customer_id")
@rigid {
return {
history: memory.interaction_history.last(10),
preferences: memory.preferences
}
}
}
|