@fenixforce/edition-voices 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/dist/index.js +400 -22
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1,54 +1,432 @@
1
- import{createKernel as oe,bootKernel as le,KERNEL_VERSION as ke,EDITION_VOICES as ce}from"@fenixforce/kernel";var _={name:"fenix-voices",channels:["telegram","http-api","websocket","voice-telnyx","cli"],tools:["memory","web","files","schedule","curriculum","exercises","assessment","content-pipeline"],storage:"postgres",extensions:["default/*","voices/*"],features:{sandbox:!0,subagents:!0,teamMemory:!0,observability:!0,skillMarketplace:!1,teacherLayer:!0,teachingLoop:!0,contentPipeline:!0}};var j=`You are an autonomous language teacher powered by the Fenix Voices platform.
1
+ var In=Object.defineProperty;var ae=(t,e)=>()=>(t&&(e=t(t=0)),e);var Pn=(t,e)=>{for(var r in e)In(t,r,{get:e[r],enumerable:!0})};import*as Hr from"pg";function it(t){return{id:t.id,conversationId:t.conversation_id,userId:t.user_id,role:t.role,content:t.content,channel:t.channel,tokenCount:t.token_count??null,metadata:t.metadata??{},createdAt:new Date(t.created_at)}}function qt(t){return{id:t.id,userId:t.user_id,title:t.title??null,channel:t.channel,metadata:t.metadata??{},createdAt:new Date(t.created_at),updatedAt:new Date(t.updated_at)}}function Ht(t){return{id:t.id,userId:t.user_id,content:t.content,category:t.category,embedding:t.embedding?Vt(t.embedding):null,heat:t.heat,accessCount:t.access_count,lastAccessedAt:new Date(t.last_accessed_at),createdAt:new Date(t.created_at),updatedAt:new Date(t.updated_at)}}function Wt(t){return{userId:t.user_id,summary:t.summary,updatedAt:new Date(t.updated_at)}}function Jt(t){return{id:t.id,userId:t.user_id,conversationId:t.conversation_id??null,content:t.content,category:t.category,reviewed:t.reviewed,createdAt:new Date(t.created_at),reviewedAt:t.reviewed_at?new Date(t.reviewed_at):null}}function Kt(t){return{id:t.id,conversationId:t.conversation_id,artifactType:t.artifact_type,title:t.title,content:t.content,metadata:t.metadata??{},createdAt:new Date(t.created_at),updatedAt:new Date(t.updated_at)}}function Gt(t){return{id:t.id,userId:t.user_id,conversationId:t.conversation_id??null,provider:t.provider,model:t.model,inputTokens:t.input_tokens,outputTokens:t.output_tokens,costUsd:t.cost_usd,createdAt:new Date(t.created_at)}}function re(t){return{id:t.id,type:t.type,status:t.status,priority:t.priority,payload:t.payload??{},result:t.result??null,error:t.error??null,attempts:t.attempts,maxAttempts:t.max_attempts,claimedAt:t.claimed_at?new Date(t.claimed_at):null,completedAt:t.completed_at?new Date(t.completed_at):null,createdAt:new Date(t.created_at),updatedAt:new Date(t.updated_at)}}function fe(t){return{id:t.id,userId:t.user_id,sourceType:t.source_type,uri:t.uri,status:t.status,metadata:t.metadata??{},createdAt:new Date(t.created_at),updatedAt:new Date(t.updated_at)}}function at(t){return{id:t.id,sourceId:t.source_id,content:t.content,embedding:t.embedding?Vt(t.embedding):null,chunkIndex:t.chunk_index,metadata:t.metadata??{},createdAt:new Date(t.created_at)}}function ct(t){return{id:t.id,userId:t.user_id,provider:t.provider,encryptedData:t.encrypted_data,iv:t.iv,authTag:t.auth_tag,createdAt:new Date(t.created_at),updatedAt:new Date(t.updated_at)}}function lt(t){return{id:t.id,workspaceId:t.workspace_id,fileName:t.file_name,content:t.content,metadata:t.metadata??{},createdAt:new Date(t.created_at),updatedAt:new Date(t.updated_at)}}function Vt(t){return Array.isArray(t)?t:typeof t=="string"?t.replace(/^\[|\]$/g,"").split(",").map(Number):[]}function he(t){return`[${t.join(",")}]`}function oe(t){let e=new Wr({connectionString:t.connectionString,max:t.max??20});return{async initialize(){let n=await e.connect();try{await n.query(Jr),console.log("[kernel/storage] PostgreSQL migration complete")}finally{n.release()}},async close(){await e.end()},async saveMessage(n){let{rows:o}=await e.query(`INSERT INTO messages (conversation_id, user_id, role, content, channel, token_count, metadata)
2
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
3
+ RETURNING *`,[n.conversationId,n.userId,n.role,n.content,n.channel,n.tokenCount,JSON.stringify(n.metadata)]);return it(o[0])},async getMessagesByConversation(n,o=50){let{rows:s}=await e.query("SELECT * FROM messages WHERE conversation_id = $1 ORDER BY created_at ASC LIMIT $2",[n,o]);return s.map(it)},async getMessageById(n){let{rows:o}=await e.query("SELECT * FROM messages WHERE id = $1",[n]);return o[0]?it(o[0]):null},async createConversation(n){let{rows:o}=await e.query(`INSERT INTO conversations (user_id, title, channel, metadata)
4
+ VALUES ($1, $2, $3, $4)
5
+ RETURNING *`,[n.userId,n.title,n.channel,JSON.stringify(n.metadata)]);return qt(o[0])},async listConversations(n,o){let s=o?.limit??50,i=o?.offset??0,{rows:a}=await e.query("SELECT * FROM conversations WHERE user_id = $1 ORDER BY updated_at DESC LIMIT $2 OFFSET $3",[n,s,i]);return a.map(qt)},async deleteConversation(n){await e.query("DELETE FROM conversations WHERE id = $1",[n])},async saveMemorySegment(n){let o=n.embedding?he(n.embedding):null,{rows:s}=await e.query(`INSERT INTO memory_segments (user_id, content, category, embedding, heat)
6
+ VALUES ($1, $2, $3, $4::vector, $5)
7
+ RETURNING *`,[n.userId,n.content,n.category,o,n.heat]);return Ht(s[0])},async searchMemoryByEmbedding(n,o,s=10){let i=he(o),{rows:a}=await e.query(`SELECT *, 1 - (embedding <=> $2::vector) AS score
8
+ FROM memory_segments
9
+ WHERE user_id = $1 AND embedding IS NOT NULL
10
+ ORDER BY embedding <=> $2::vector
11
+ LIMIT $3`,[n,i,s]);return a.map(c=>({segment:Ht(c),score:c.score}))},async getProfile(n){let{rows:o}=await e.query("SELECT * FROM user_profiles WHERE user_id = $1",[n]);return o[0]?Wt(o[0]):null},async upsertProfile(n,o){let{rows:s}=await e.query(`INSERT INTO user_profiles (user_id, summary, updated_at)
12
+ VALUES ($1, $2, now())
13
+ ON CONFLICT (user_id) DO UPDATE SET summary = $2, updated_at = now()
14
+ RETURNING *`,[n,o]);return Wt(s[0])},async updateHeatScores(n,o){n.length!==0&&await e.query(`UPDATE memory_segments
15
+ SET heat = heat + $2, access_count = access_count + 1, last_accessed_at = now()
16
+ WHERE id = ANY($1)`,[n,o])},async saveObservation(n){let{rows:o}=await e.query(`INSERT INTO observations (user_id, conversation_id, content, category)
17
+ VALUES ($1, $2, $3, $4)
18
+ RETURNING *`,[n.userId,n.conversationId,n.content,n.category]);return Jt(o[0])},async listPendingObservations(n,o){let s=o?.limit??50,i=o?.offset??0,{rows:a}=await e.query(`SELECT * FROM observations
19
+ WHERE user_id = $1 AND NOT reviewed
20
+ ORDER BY created_at DESC
21
+ LIMIT $2 OFFSET $3`,[n,s,i]);return a.map(Jt)},async markObservationReviewed(n){await e.query("UPDATE observations SET reviewed = TRUE, reviewed_at = now() WHERE id = $1",[n])},async saveBrainArtifact(n){let{rows:o}=await e.query(`INSERT INTO brain_artifacts (conversation_id, artifact_type, title, content, metadata)
22
+ VALUES ($1, $2, $3, $4, $5)
23
+ RETURNING *`,[n.conversationId,n.artifactType,n.title,n.content,JSON.stringify(n.metadata)]);return Kt(o[0])},async loadBrainArtifacts(n){let{rows:o}=await e.query("SELECT * FROM brain_artifacts WHERE conversation_id = $1 ORDER BY created_at ASC",[n]);return o.map(Kt)},async deleteBrainArtifact(n){await e.query("DELETE FROM brain_artifacts WHERE id = $1",[n])},async insertCostLog(n){let{rows:o}=await e.query(`INSERT INTO cost_logs (user_id, conversation_id, provider, model, input_tokens, output_tokens, cost_usd)
24
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
25
+ RETURNING *`,[n.userId,n.conversationId,n.provider,n.model,n.inputTokens,n.outputTokens,n.costUsd]);return Gt(o[0])},async queryCostLogs(n){let{rows:o}=await e.query(`SELECT * FROM cost_logs
26
+ WHERE user_id = $1 AND created_at >= $2 AND created_at <= $3
27
+ ORDER BY created_at DESC`,[n.userId,n.startDate,n.endDate]);return o.map(Gt)},async createJob(n){let{rows:o}=await e.query(`INSERT INTO jobs (type, priority, payload, max_attempts)
28
+ VALUES ($1, $2, $3, $4)
29
+ RETURNING *`,[n.type,n.priority,JSON.stringify(n.payload),n.maxAttempts]);return re(o[0])},async claimNextJob(n){let o=n&&n.length>0?"AND type = ANY($1)":"",s=n&&n.length>0?[n]:[],{rows:i}=await e.query(`UPDATE jobs
30
+ SET status = 'running', claimed_at = now(), attempts = attempts + 1, updated_at = now()
31
+ WHERE id = (
32
+ SELECT id FROM jobs
33
+ WHERE status = 'pending' ${o}
34
+ ORDER BY priority DESC, created_at ASC
35
+ LIMIT 1
36
+ FOR UPDATE SKIP LOCKED
37
+ )
38
+ RETURNING *`,s);return i[0]?re(i[0]):null},async updateJobStatus(n,o,s,i){let a=o==="completed"||o==="failed"||o==="dead"?"now()":"NULL",{rows:c}=await e.query(`UPDATE jobs
39
+ SET status = $2,
40
+ result = $3,
41
+ error = $4,
42
+ completed_at = ${a==="now()"?"now()":"completed_at"},
43
+ updated_at = now()
44
+ WHERE id = $1
45
+ RETURNING *`,[n,o,s?JSON.stringify(s):null,i??null]);return re(c[0])},async getJobById(n){let{rows:o}=await e.query("SELECT * FROM jobs WHERE id = $1",[n]);return o[0]?re(o[0]):null},async listJobs(n,o){let s=[],i=[],a=1;n.status&&(s.push(`status = $${a++}`),i.push(n.status)),n.type&&(s.push(`type = $${a++}`),i.push(n.type));let c=s.length>0?`WHERE ${s.join(" AND ")}`:"",l=o?.limit??50,u=o?.offset??0;i.push(l,u);let{rows:p}=await e.query(`SELECT * FROM jobs ${c} ORDER BY created_at DESC LIMIT $${a++} OFFSET $${a}`,i);return p.map(re)},async markDeadJobs(n){let{rowCount:o}=await e.query(`UPDATE jobs
46
+ SET status = 'dead', updated_at = now(), completed_at = now()
47
+ WHERE status = 'running'
48
+ AND claimed_at < now() - ($1 || ' seconds')::interval`,[n]);return o??0},async createContentSource(n){let{rows:o}=await e.query(`INSERT INTO content_sources (user_id, source_type, uri, status, metadata)
49
+ VALUES ($1, $2, $3, $4, $5)
50
+ RETURNING *`,[n.userId,n.sourceType,n.uri,n.status,JSON.stringify(n.metadata)]);return fe(o[0])},async updateContentSourceStatus(n,o){let{rows:s}=await e.query("UPDATE content_sources SET status = $2, updated_at = now() WHERE id = $1 RETURNING *",[n,o]);return fe(s[0])},async getContentSourceById(n){let{rows:o}=await e.query("SELECT * FROM content_sources WHERE id = $1",[n]);return o[0]?fe(o[0]):null},async listContentSources(n,o){let s=o?.limit??50,i=o?.offset??0,{rows:a}=await e.query("SELECT * FROM content_sources WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3",[n,s,i]);return a.map(fe)},async bulkInsertContentChunks(n){if(n.length===0)return[];let o=[],s=[],i=1;for(let c of n){let l=c.embedding?he(c.embedding):null;o.push(`($${i++}, $${i++}, $${i++}::vector, $${i++}, $${i++})`),s.push(c.sourceId,c.content,l,c.chunkIndex,JSON.stringify(c.metadata))}let{rows:a}=await e.query(`INSERT INTO content_chunks (source_id, content, embedding, chunk_index, metadata)
51
+ VALUES ${o.join(", ")}
52
+ RETURNING *`,s);return a.map(at)},async searchContentChunksByEmbedding(n,o,s=10){if(n.length===0)return[];let i=he(o),{rows:a}=await e.query(`SELECT *, 1 - (embedding <=> $2::vector) AS score
53
+ FROM content_chunks
54
+ WHERE source_id = ANY($1) AND embedding IS NOT NULL
55
+ ORDER BY embedding <=> $2::vector
56
+ LIMIT $3`,[n,i,s]);return a.map(c=>({chunk:at(c),score:c.score}))},async searchContentChunksByText(n,o,s=10){if(n.length===0)return[];let{rows:i}=await e.query(`SELECT *, ts_rank(tsv, plainto_tsquery('english', $2)) AS score
57
+ FROM content_chunks
58
+ WHERE source_id = ANY($1) AND tsv @@ plainto_tsquery('english', $2)
59
+ ORDER BY score DESC
60
+ LIMIT $3`,[n,o,s]);return i.map(a=>({chunk:at(a),score:a.score}))},async deleteContentChunksBySource(n){let{rowCount:o}=await e.query("DELETE FROM content_chunks WHERE source_id = $1",[n]);return o??0},async saveCredential(n){let{rows:o}=await e.query(`INSERT INTO encrypted_credentials (user_id, provider, encrypted_data, iv, auth_tag)
61
+ VALUES ($1, $2, $3, $4, $5)
62
+ ON CONFLICT (user_id, provider) DO UPDATE
63
+ SET encrypted_data = $3, iv = $4, auth_tag = $5, updated_at = now()
64
+ RETURNING *`,[n.userId,n.provider,n.encryptedData,n.iv,n.authTag]);return ct(o[0])},async getCredential(n,o){let{rows:s}=await e.query("SELECT * FROM encrypted_credentials WHERE user_id = $1 AND provider = $2",[n,o]);return s[0]?ct(s[0]):null},async deleteCredential(n){await e.query("DELETE FROM encrypted_credentials WHERE id = $1",[n])},async listCredentials(n){let{rows:o}=await e.query("SELECT * FROM encrypted_credentials WHERE user_id = $1 ORDER BY provider",[n]);return o.map(ct)},async saveIdentityFile(n){let{rows:o}=await e.query(`INSERT INTO identity_files (workspace_id, file_name, content, metadata)
65
+ VALUES ($1, $2, $3, $4)
66
+ ON CONFLICT (workspace_id, file_name) DO UPDATE
67
+ SET content = $3, metadata = $4, updated_at = now()
68
+ RETURNING *`,[n.workspaceId,n.fileName,n.content,JSON.stringify(n.metadata)]);return lt(o[0])},async getIdentityFile(n){let{rows:o}=await e.query("SELECT * FROM identity_files WHERE workspace_id = $1 ORDER BY created_at DESC LIMIT 1",[n]);return o[0]?lt(o[0]):null},async listIdentityFiles(){let{rows:n}=await e.query("SELECT * FROM identity_files ORDER BY created_at DESC");return n.map(lt)},async query(n,o){let{rows:s}=await e.query(n,o);return s}}}var Wr,Jr,ye=ae(()=>{"use strict";({Pool:Wr}=Hr),Jr=`
69
+ -- \u2550\u2550\u2550 Extensions \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
70
+ CREATE EXTENSION IF NOT EXISTS vector;
71
+ CREATE EXTENSION IF NOT EXISTS pgcrypto;
72
+
73
+ -- \u2550\u2550\u2550 v1.2 \u2014 Core tables \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
74
+
75
+ CREATE TABLE IF NOT EXISTS users (
76
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
77
+ external_id TEXT NOT NULL,
78
+ platform TEXT NOT NULL,
79
+ display_name TEXT,
80
+ email TEXT,
81
+ metadata JSONB NOT NULL DEFAULT '{}',
82
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
83
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
84
+ UNIQUE (external_id, platform)
85
+ );
86
+
87
+ CREATE TABLE IF NOT EXISTS conversations (
88
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
89
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
90
+ title TEXT,
91
+ channel TEXT NOT NULL DEFAULT 'web',
92
+ metadata JSONB NOT NULL DEFAULT '{}',
93
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
94
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
95
+ );
96
+ CREATE INDEX IF NOT EXISTS idx_conversations_user ON conversations(user_id, updated_at DESC);
97
+
98
+ CREATE TABLE IF NOT EXISTS messages (
99
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
100
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
101
+ user_id TEXT NOT NULL,
102
+ role TEXT NOT NULL,
103
+ content TEXT NOT NULL,
104
+ channel TEXT NOT NULL DEFAULT 'web',
105
+ token_count INTEGER,
106
+ metadata JSONB NOT NULL DEFAULT '{}',
107
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
108
+ );
109
+ CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id, created_at);
110
+ CREATE INDEX IF NOT EXISTS idx_messages_user ON messages(user_id, created_at DESC);
111
+
112
+ CREATE TABLE IF NOT EXISTS memory_segments (
113
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
114
+ user_id TEXT NOT NULL,
115
+ content TEXT NOT NULL,
116
+ category TEXT NOT NULL DEFAULT 'fact',
117
+ embedding vector(1536),
118
+ heat REAL NOT NULL DEFAULT 1.0,
119
+ access_count INTEGER NOT NULL DEFAULT 0,
120
+ last_accessed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
121
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
122
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
123
+ );
124
+ CREATE INDEX IF NOT EXISTS idx_memory_user ON memory_segments(user_id);
125
+ CREATE INDEX IF NOT EXISTS idx_memory_updated ON memory_segments(updated_at DESC);
126
+
127
+ -- ivfflat index for vector search (created only if enough rows exist or deferred)
128
+ -- We use a DO block so it doesn't fail on first run with zero rows.
129
+ DO $$
130
+ BEGIN
131
+ IF NOT EXISTS (
132
+ SELECT 1 FROM pg_indexes WHERE indexname = 'idx_memory_embedding_ivfflat'
133
+ ) THEN
134
+ BEGIN
135
+ CREATE INDEX idx_memory_embedding_ivfflat
136
+ ON memory_segments USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
137
+ EXCEPTION WHEN others THEN
138
+ -- ivfflat requires rows to train; will be created later
139
+ RAISE NOTICE 'Skipping ivfflat index (likely not enough rows): %', SQLERRM;
140
+ END;
141
+ END IF;
142
+ END $$;
143
+
144
+ CREATE TABLE IF NOT EXISTS user_profiles (
145
+ user_id TEXT PRIMARY KEY,
146
+ summary TEXT NOT NULL,
147
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
148
+ );
149
+
150
+ CREATE TABLE IF NOT EXISTS observations (
151
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
152
+ user_id TEXT NOT NULL,
153
+ conversation_id TEXT,
154
+ content TEXT NOT NULL,
155
+ category TEXT NOT NULL DEFAULT 'general',
156
+ reviewed BOOLEAN NOT NULL DEFAULT FALSE,
157
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
158
+ reviewed_at TIMESTAMPTZ
159
+ );
160
+ CREATE INDEX IF NOT EXISTS idx_observations_pending ON observations(user_id) WHERE NOT reviewed;
161
+
162
+ -- \u2550\u2550\u2550 v1.4 \u2014 Knowledge graph + cost + artifacts \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
163
+
164
+ CREATE TABLE IF NOT EXISTS entities (
165
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
166
+ user_id TEXT NOT NULL,
167
+ name TEXT NOT NULL,
168
+ entity_type TEXT NOT NULL,
169
+ metadata JSONB NOT NULL DEFAULT '{}',
170
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
171
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
172
+ );
173
+ CREATE INDEX IF NOT EXISTS idx_entities_user ON entities(user_id);
174
+
175
+ CREATE TABLE IF NOT EXISTS relations (
176
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
177
+ user_id TEXT NOT NULL,
178
+ source_entity_id TEXT NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
179
+ target_entity_id TEXT NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
180
+ relation_type TEXT NOT NULL,
181
+ strength REAL NOT NULL DEFAULT 1.0,
182
+ metadata JSONB NOT NULL DEFAULT '{}',
183
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
184
+ );
185
+ CREATE INDEX IF NOT EXISTS idx_relations_source ON relations(source_entity_id);
186
+ CREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_entity_id);
187
+
188
+ CREATE TABLE IF NOT EXISTS checkpoints (
189
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
190
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
191
+ summary TEXT NOT NULL,
192
+ tokens_saved INTEGER NOT NULL DEFAULT 0,
193
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
194
+ );
195
+ CREATE INDEX IF NOT EXISTS idx_checkpoints_conv ON checkpoints(conversation_id, created_at DESC);
196
+
197
+ CREATE TABLE IF NOT EXISTS cost_logs (
198
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
199
+ user_id TEXT NOT NULL,
200
+ conversation_id TEXT,
201
+ provider TEXT NOT NULL,
202
+ model TEXT NOT NULL,
203
+ input_tokens INTEGER NOT NULL DEFAULT 0,
204
+ output_tokens INTEGER NOT NULL DEFAULT 0,
205
+ cost_usd REAL NOT NULL DEFAULT 0,
206
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
207
+ );
208
+ CREATE INDEX IF NOT EXISTS idx_cost_logs_user_date ON cost_logs(user_id, created_at);
209
+
210
+ CREATE TABLE IF NOT EXISTS brain_artifacts (
211
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
212
+ conversation_id TEXT NOT NULL,
213
+ artifact_type TEXT NOT NULL,
214
+ title TEXT NOT NULL,
215
+ content TEXT NOT NULL,
216
+ metadata JSONB NOT NULL DEFAULT '{}',
217
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
218
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
219
+ );
220
+ CREATE INDEX IF NOT EXISTS idx_brain_artifacts_conv ON brain_artifacts(conversation_id);
221
+
222
+ CREATE TABLE IF NOT EXISTS approval_records (
223
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
224
+ user_id TEXT NOT NULL,
225
+ conversation_id TEXT NOT NULL,
226
+ action TEXT NOT NULL,
227
+ status TEXT NOT NULL DEFAULT 'pending',
228
+ payload JSONB NOT NULL DEFAULT '{}',
229
+ decided_at TIMESTAMPTZ,
230
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
231
+ );
232
+ CREATE INDEX IF NOT EXISTS idx_approvals_user ON approval_records(user_id, created_at DESC);
233
+
234
+ -- \u2550\u2550\u2550 v1.5 \u2014 Jobs, content pipeline, credentials \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
235
+
236
+ CREATE TABLE IF NOT EXISTS jobs (
237
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
238
+ type TEXT NOT NULL,
239
+ status TEXT NOT NULL DEFAULT 'pending',
240
+ priority INTEGER NOT NULL DEFAULT 0,
241
+ payload JSONB NOT NULL DEFAULT '{}',
242
+ result JSONB,
243
+ error TEXT,
244
+ attempts INTEGER NOT NULL DEFAULT 0,
245
+ max_attempts INTEGER NOT NULL DEFAULT 3,
246
+ claimed_at TIMESTAMPTZ,
247
+ completed_at TIMESTAMPTZ,
248
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
249
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
250
+ );
251
+ -- Priority-ordered index for SKIP LOCKED claiming
252
+ CREATE INDEX IF NOT EXISTS idx_jobs_claimable
253
+ ON jobs(priority DESC, created_at ASC)
254
+ WHERE status = 'pending';
255
+ CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
256
+ CREATE INDEX IF NOT EXISTS idx_jobs_type ON jobs(type);
257
+
258
+ CREATE TABLE IF NOT EXISTS content_sources (
259
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
260
+ user_id TEXT NOT NULL,
261
+ source_type TEXT NOT NULL,
262
+ uri TEXT NOT NULL,
263
+ status TEXT NOT NULL DEFAULT 'pending',
264
+ metadata JSONB NOT NULL DEFAULT '{}',
265
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
266
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
267
+ );
268
+ CREATE INDEX IF NOT EXISTS idx_content_sources_user ON content_sources(user_id);
269
+
270
+ CREATE TABLE IF NOT EXISTS content_chunks (
271
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
272
+ source_id TEXT NOT NULL REFERENCES content_sources(id) ON DELETE CASCADE,
273
+ content TEXT NOT NULL,
274
+ embedding vector(1536),
275
+ chunk_index INTEGER NOT NULL DEFAULT 0,
276
+ metadata JSONB NOT NULL DEFAULT '{}',
277
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
278
+ );
279
+ CREATE INDEX IF NOT EXISTS idx_content_chunks_source ON content_chunks(source_id);
280
+
281
+ -- Full-text GIN index on content_chunks
282
+ DO $$
283
+ BEGIN
284
+ IF NOT EXISTS (
285
+ SELECT 1 FROM information_schema.columns
286
+ WHERE table_name = 'content_chunks' AND column_name = 'tsv'
287
+ ) THEN
288
+ ALTER TABLE content_chunks ADD COLUMN tsv tsvector
289
+ GENERATED ALWAYS AS (to_tsvector('english', content)) STORED;
290
+ CREATE INDEX idx_content_chunks_tsv ON content_chunks USING GIN(tsv);
291
+ END IF;
292
+ END $$;
293
+
294
+ -- pgvector index on content_chunks
295
+ DO $$
296
+ BEGIN
297
+ IF NOT EXISTS (
298
+ SELECT 1 FROM pg_indexes WHERE indexname = 'idx_content_chunks_embedding'
299
+ ) THEN
300
+ BEGIN
301
+ CREATE INDEX idx_content_chunks_embedding
302
+ ON content_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
303
+ EXCEPTION WHEN others THEN
304
+ RAISE NOTICE 'Skipping content_chunks ivfflat index: %', SQLERRM;
305
+ END;
306
+ END IF;
307
+ END $$;
308
+
309
+ -- AES-256-GCM encrypted credentials
310
+ CREATE TABLE IF NOT EXISTS encrypted_credentials (
311
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
312
+ user_id TEXT NOT NULL,
313
+ provider TEXT NOT NULL,
314
+ encrypted_data BYTEA NOT NULL,
315
+ iv BYTEA NOT NULL,
316
+ auth_tag BYTEA NOT NULL,
317
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
318
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
319
+ UNIQUE (user_id, provider)
320
+ );
321
+
322
+ -- \u2550\u2550\u2550 v1.6 \u2014 Identity files, cost events \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
323
+
324
+ CREATE TABLE IF NOT EXISTS identity_files (
325
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
326
+ workspace_id TEXT NOT NULL,
327
+ file_name TEXT NOT NULL,
328
+ content TEXT NOT NULL,
329
+ metadata JSONB NOT NULL DEFAULT '{}',
330
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
331
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
332
+ UNIQUE (workspace_id, file_name)
333
+ );
334
+
335
+ CREATE TABLE IF NOT EXISTS cost_events (
336
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
337
+ user_id TEXT NOT NULL,
338
+ event_type TEXT NOT NULL,
339
+ amount REAL NOT NULL DEFAULT 0,
340
+ currency TEXT NOT NULL DEFAULT 'USD',
341
+ metadata JSONB NOT NULL DEFAULT '{}',
342
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
343
+ );
344
+ CREATE INDEX IF NOT EXISTS idx_cost_events_user ON cost_events(user_id, created_at);
345
+ `});function d(){throw new Error(Kr)}function se(){return{initialize:d,close:d,saveMessage:d,getMessagesByConversation:d,getMessageById:d,createConversation:d,listConversations:d,deleteConversation:d,saveMemorySegment:d,searchMemoryByEmbedding:d,getProfile:d,upsertProfile:d,updateHeatScores:d,saveObservation:d,listPendingObservations:d,markObservationReviewed:d,saveBrainArtifact:d,loadBrainArtifacts:d,deleteBrainArtifact:d,insertCostLog:d,queryCostLogs:d,createJob:d,claimNextJob:d,updateJobStatus:d,getJobById:d,listJobs:d,markDeadJobs:d,createContentSource:d,updateContentSourceStatus:d,getContentSourceById:d,listContentSources:d,bulkInsertContentChunks:d,searchContentChunksByEmbedding:d,searchContentChunksByText:d,deleteContentChunksBySource:d,saveCredential:d,getCredential:d,deleteCredential:d,listCredentials:d,saveIdentityFile:d,getIdentityFile:d,listIdentityFiles:d,query:d}}var Kr,ve=ae(()=>{"use strict";Kr="SQLite not implemented \u2014 use PostgreSQL. Mobile edition will use SQLite + zvec."});var Xt=ae(()=>{"use strict"});var Yt={};Pn(Yt,{createPostgresStorage:()=>oe,createSqliteStorage:()=>se,createStorage:()=>zt});function zt(t){switch(t.backend){case"postgres":{if(!t.connectionString)throw new Error("connectionString is required for the postgres backend");return oe({connectionString:t.connectionString,max:t.maxConnections})}case"sqlite":return se();default:throw new Error(`Unknown storage backend: ${t.backend}`)}}var ut=ae(()=>{"use strict";ye();ve();Xt();ye();ve()});var ke={name:"voices",label:"Fenix Voices",tools:["*"],maxWorkers:8};var Vn={failureThreshold:5,resetTimeoutMs:6e4,maxResetTimeoutMs:36e5,billingResetTimeoutMs:18e6,billingMaxResetTimeoutMs:864e5};function Xn(t){if(typeof t=="object"&&t!==null){let e=t.statusCode??t.status;if(e===402||e===429)return!0;let r=t.message??"";return/quota|billing|rate.limit|insufficient.funds|payment.required/i.test(r)}return!1}var K=class t{state="closed";failureCount=0;consecutiveResets=0;lastFailureTime=0;isBillingFailure=!1;config;keyBreakers=null;constructor(e){this.config={...Vn,...e}}getState(){return this.state==="open"&&Date.now()-this.lastFailureTime>=this.currentTimeout()&&(this.state="half-open"),this.state}canAttempt(){let e=this.getState();return e==="closed"||e==="half-open"}recordSuccess(){this.failureCount=0,this.consecutiveResets=0,this.isBillingFailure=!1,this.state="closed"}recordFailure(e){this.failureCount++,this.lastFailureTime=Date.now(),this.isBillingFailure=Xn(e),(this.state==="half-open"||this.failureCount>=this.config.failureThreshold)&&(this.state==="half-open"&&this.consecutiveResets++,this.state="open")}getKeyBreaker(e){this.keyBreakers||(this.keyBreakers=new Map);let r=this.keyBreakers.get(e);return r||(r=new t(this.config),this.keyBreakers.set(e,r)),r}getHealthyKey(e){if(!this.keyBreakers)return e[0]??null;for(let r of e){let n=this.keyBreakers.get(r);if(!n||n.canAttempt())return r}return null}currentTimeout(){return this.isBillingFailure?Math.min(this.config.billingResetTimeoutMs*2**this.consecutiveResets,this.config.billingMaxResetTimeoutMs):Math.min(this.config.resetTimeoutMs*2**this.consecutiveResets,this.config.maxResetTimeoutMs)}};function xt(t){return t.priority??5}function zn(t){return t.urgency??"immediate"}function wt(t,e){return e.priority-t.priority}var j=class{handlers=new Map;allHandlers=new Set;errorHandler;soonQueue=[];soonScheduled=!1;laterQueue=[];subscribe(e,r){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(r),()=>{this.handlers.get(e)?.delete(r)}}subscribeAll(e){return this.allHandlers.add(e),()=>{this.allHandlers.delete(e)}}onError(e){this.errorHandler=e}async emit(e){let r=zn(e);if(r==="soon"){this.soonQueue.push({event:e,priority:xt(e)}),this.scheduleSoonDrain();return}if(r==="later"){this.laterQueue.push({event:e,priority:xt(e)});return}await this.deliver(e)}async drainDeferred(){if(this.laterQueue.length===0)return 0;let e=this.laterQueue.splice(0);e.sort(wt);for(let{event:r}of e)await this.deliver(r);return e.length}get pendingCount(){return this.soonQueue.length+this.laterQueue.length}listenerCount(e){if(e)return(this.handlers.get(e)?.size??0)+this.allHandlers.size;let r=this.allHandlers.size;for(let n of this.handlers.values())r+=n.size;return r}clear(){this.handlers.clear(),this.allHandlers.clear(),this.soonQueue.length=0,this.laterQueue.length=0}async deliver(e){let r=[],n=this.handlers.get(e.type);if(n&&r.push(...n),r.push(...this.allHandlers),r.length===0)return;let o=await Promise.allSettled(r.map(async s=>s(e)));for(let s of o)s.status==="rejected"&&this.errorHandler&&this.errorHandler(s.reason,e)}scheduleSoonDrain(){this.soonScheduled||(this.soonScheduled=!0,queueMicrotask(async()=>{this.soonScheduled=!1;let e=this.soonQueue.splice(0);e.sort(wt);for(let{event:r}of e)await this.deliver(r)}))}};var q=class{sessions=new Map;timers=new Map;bufferTtlMs;maxBufferSize;constructor(e){this.bufferTtlMs=e?.bufferTtlMs??1800*1e3,this.maxBufferSize=e?.maxBufferSize??1e4}create(e){let r=e??`sess-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,n={id:r,createdAt:Date.now(),events:[],listeners:new Set};return this.sessions.set(r,n),this.resetTtl(r),n}get(e){return this.sessions.get(e)}subscribe(e,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session not found: ${e}`);return n.listeners.add(r),()=>{n.listeners.delete(r)}}async bufferEvent(e,r){let n=this.sessions.get(e);if(n){n.events.length>=this.maxBufferSize&&n.events.shift(),n.events.push(r),this.resetTtl(e);for(let o of n.listeners)o(r)}}replay(e,r){let n=this.sessions.get(e);if(!n)return 0;for(let o of n.events)r(o);return n.events.length}connectBus(e,r){return e.subscribeAll(n=>{"sessionId"in n&&n.sessionId===r&&this.bufferEvent(r,n)})}delete(e){return this.clearTtl(e),this.sessions.delete(e)}get size(){return this.sessions.size}destroy(){for(let[e]of this.sessions)this.clearTtl(e);this.sessions.clear()}resetTtl(e){this.clearTtl(e),this.timers.set(e,setTimeout(()=>{this.sessions.delete(e),this.timers.delete(e)},this.bufferTtlMs))}clearTtl(e){let r=this.timers.get(e);r&&(clearTimeout(r),this.timers.delete(e))}};var ce=class{constructor(e,r=3,n=15){this.inner=e;this.inputCostPerM=r;this.outputCostPerM=n;this.id=e.id,e.stream&&(this.stream=o=>{this.model=o.model??this.model;let s=e.stream(o);return this.wrapStream(s)}),e.generateObject&&(this.generateObject=async o=>{this.model=o.model??this.model;let s=await e.generateObject(o),i=o.messages.reduce((c,l)=>c+(typeof l.content=="string"?l.content.length:0),0),a=JSON.stringify(s).length;return this.record(Math.ceil(i/4),Math.ceil(a/4)),s}),e.streamObject&&(this.streamObject=o=>{this.model=o.model??this.model;let s=e.streamObject(o);return this.wrapObjectStream(s)})}id;records=[];model="unknown";stream;generateObject;streamObject;capabilities(){return this.inner.capabilities()}async complete(e){this.model=e.model??this.model;let r=await this.inner.complete(e);return r.usage&&this.record(r.usage.inputTokens,r.usage.outputTokens),r}async embed(e){if(!this.inner.embed)throw new Error(`Provider ${this.id} does not support embeddings`);let r=await this.inner.embed(e);return r.usage&&this.record(r.usage.totalTokens,0),r}getRecords(){return this.records}getSummary(){let e=0,r=0,n=0;for(let o of this.records)e+=o.inputTokens,r+=o.outputTokens,n+=o.estimatedCost;return{totalInputTokens:e,totalOutputTokens:r,totalEstimatedCost:n,recordCount:this.records.length}}record(e,r){let n=e/1e6*this.inputCostPerM+r/1e6*this.outputCostPerM;this.records.push({provider:this.id,model:this.model,inputTokens:e,outputTokens:r,estimatedCost:n,timestamp:Date.now()})}wrapStream(e){let r=this;return{[Symbol.asyncIterator](){let n=e[Symbol.asyncIterator]();return{async next(){let o=await n.next();return!o.done&&o.value.usage&&r.record(o.value.usage.prompt_tokens??0,o.value.usage.completion_tokens??0),o}}}}}wrapObjectStream(e){let r=this;return{[Symbol.asyncIterator](){let n=e[Symbol.asyncIterator]();return{async next(){let o=await n.next();return!o.done&&o.value.usage&&r.record(o.value.usage.inputTokens,o.value.usage.outputTokens),o}}}}}};var St=[{id:"anthropic",envVars:["ANTHROPIC_API_KEY"]},{id:"openai",envVars:["OPENAI_API_KEY"]},{id:"google",envVars:["GOOGLE_API_KEY"]},{id:"openrouter",envVars:["OPENROUTER_API_KEY"]},{id:"groq",envVars:["GROQ_API_KEY"]},{id:"bedrock",envVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY"]},{id:"nvidia",envVars:["NVIDIA_API_KEY"]},{id:"cloudflare",envVars:["CLOUDFLARE_API_TOKEN"]},{id:"github",envVars:["GITHUB_TOKEN"]}],Ct="http://localhost:11434",Yn=[{id:"sidecar-qwen3",type:"gpu-model",modelPath:"Qwen3-TTS"},{id:"google-tts",type:"env",envVars:["GOOGLE_API_KEY"]},{id:"sidecar-moss",type:"gpu-model",modelPath:"MOSS-TTS"},{id:"speechmatics-tts",type:"env",envVars:["SPEECHMATICS_API_KEY"]},{id:"elevenlabs",type:"env",envVars:["ELEVENLABS_API_KEY"]},{id:"deepgram-tts",type:"env",envVars:["DEEPGRAM_API_KEY"]},{id:"openai-tts",type:"env",envVars:["OPENAI_API_KEY"]},{id:"groq",type:"env",envVars:["GROQ_API_KEY"]},{id:"nvidia",type:"env",envVars:["NVIDIA_API_KEY"]},{id:"cloudflare",type:"env",envVars:["CLOUDFLARE_API_TOKEN"]}],Qn=[{id:"sidecar-parakeet",type:"gpu-model",modelPath:"Parakeet"},{id:"groq",type:"env",envVars:["GROQ_API_KEY"]},{id:"speechmatics",type:"env",envVars:["SPEECHMATICS_API_KEY"]},{id:"deepgram",type:"env",envVars:["DEEPGRAM_API_KEY"]},{id:"google-stt",type:"env",envVars:["GOOGLE_API_KEY"]},{id:"sidecar-whisper",type:"gpu-model",modelPath:"Whisper"},{id:"nvidia",type:"env",envVars:["NVIDIA_API_KEY"]}];function xe(t){return t.every(e=>!!process.env[e])}function Zn(){return!!(process.env.CUDA_VISIBLE_DEVICES||process.env.NVIDIA_VISIBLE_DEVICES)}async function er(t){try{let e=new AbortController,r=setTimeout(()=>e.abort(),2e3),n=await fetch(`${t}/api/tags`,{signal:e.signal});return clearTimeout(r),n.ok}catch{return!1}}async function G(){let t=[],e=null;for(let s of St)if(xe(s.envVars)){e={id:s.id,slot:"chat",apiKeyEnv:s.envVars[0]};break}if(!e&&await er(Ct)&&(e={id:"ollama",slot:"chat",baseUrl:Ct}),!e){let s=St.map(i=>i.envVars.join(" + ")).join(", ");throw new Error(`No LLM provider detected. Set one of: ${s}, or run Ollama on localhost:11434`)}let r=null,n=Zn();for(let s of Yn){if(s.type==="gpu-model"&&n){r={id:s.id,slot:"tts"};break}if(s.type==="env"&&s.envVars&&xe(s.envVars)){r={id:s.id,slot:"tts",apiKeyEnv:s.envVars[0]},e.apiKeyEnv&&e.apiKeyEnv===s.envVars[0]&&t.push(e.apiKeyEnv);break}}let o=null;for(let s of Qn){if(s.type==="gpu-model"&&n){o={id:s.id,slot:"stt"};break}if(s.type==="env"&&s.envVars&&xe(s.envVars)){o={id:s.id,slot:"stt",apiKeyEnv:s.envVars[0]},e.apiKeyEnv&&e.apiKeyEnv===s.envVars[0]&&!t.includes(e.apiKeyEnv)&&t.push(e.apiKeyEnv);break}}return{chat:e,tts:r,stt:o,reusedKeys:t}}var V=class{slots=new Map;enableCostTracking;constructor(e={}){this.enableCostTracking=e.enableCostTracking??!1}register(e,r){let n=this.enableCostTracking?new ce(r):r,o=new K;this.slots.set(e,{provider:n,breaker:o,slot:e})}getHealthy(e){let r=this.slots.get(e);return!r||!r.breaker.canAttempt()?null:r.provider}getBreaker(e){return this.slots.get(e)?.breaker??null}getByCapability(e,r="chat"){let n=[r];e==="vision"?(r!=="vision"&&n.unshift("vision"),n.includes("chat")||n.push("chat")):(n.includes("chat")||n.push("chat"),n.includes("utility")||n.push("utility"));for(let o of n){let s=this.slots.get(o);if(!s||!s.breaker.canAttempt())continue;if(s.provider.capabilities()[e])return s.provider}return null}listSlots(){return Array.from(this.slots.keys())}get(e){return this.slots.get(e)}hasSlots(){return this.slots.size>0}static async autoDetect(){return G()}};function le(t){try{return JSON.parse(t)}catch{}let e=t.trim();e.startsWith("```json")?e=e.slice(7):e.startsWith("```")&&(e=e.slice(3)),e.endsWith("```")&&(e=e.slice(0,-3)),e=e.trim();try{return JSON.parse(e)}catch{}e=e.replace(/,\s*([\]}])/g,"$1");try{return JSON.parse(e)}catch{}e=At(e);try{return JSON.parse(e)}catch{let r=tr(e);if(r!==e)try{return JSON.parse(r)}catch{}}throw new Error(`Unable to repair JSON: ${t.slice(0,100)}...`)}function H(t){if(t.trim())try{return le(t)}catch{return}}function At(t){let e=!1,r=!1,n=[];for(let o=0;o<t.length;o++){let s=t[o];if(r){r=!1;continue}if(s==="\\"){r=!0;continue}if(s==='"'){e=!e;continue}e||(s==="{"?n.push("}"):s==="["?n.push("]"):(s==="}"||s==="]")&&n.length>0&&n[n.length-1]===s&&n.pop())}for(e&&(t+='"'),t=t.replace(/,\s*$/,"");n.length>0;)t+=n.pop();return t}function tr(t){let e=t.lastIndexOf(",");if(e>0){let r=t.slice(0,e);return At(r)}return t}var I=class{id;baseUrl;apiKey;model;caps;constructor(e){this.id=e.id??`openai-compat:${e.model}`,this.baseUrl=e.baseUrl.replace(/\/+$/,""),this.apiKey=e.apiKey,this.model=e.model,this.caps={nativeToolCalling:!0,vision:!1,streaming:!0,structuredOutput:!0,maxContextTokens:128e3,...e.capabilities}}capabilities(){return{...this.caps}}async complete(e){let r=this.buildRequestBody(e,!1),n=await fetch(`${this.baseUrl}/v1/chat/completions`,{method:"POST",headers:this.headers(),body:JSON.stringify(r)});if(!n.ok){let a=await n.text().catch(()=>"");throw Object.assign(new Error(`OpenAI API error ${n.status}: ${a}`),{statusCode:n.status})}let o=await n.json(),s=o.choices[0],i=s?.message?.tool_calls?.map(a=>({id:a.id,type:"function",function:{name:a.function.name,arguments:a.function.arguments}}));return{content:s?.message?.content??"",toolCalls:i?.length?i:void 0,finishReason:this.mapFinishReason(s?.finish_reason),usage:o.usage?{inputTokens:o.usage.prompt_tokens,outputTokens:o.usage.completion_tokens}:void 0}}stream(e){let r=this,n=this.buildRequestBody(e,!0);return{[Symbol.asyncIterator](){let o=null,s="",i=!1;return{async next(){if(i)return{done:!0,value:void 0};if(!o){let c=await fetch(`${r.baseUrl}/v1/chat/completions`,{method:"POST",headers:r.headers(),body:JSON.stringify(n)});if(!c.ok){let l=await c.text().catch(()=>"");throw Object.assign(new Error(`OpenAI API error ${c.status}: ${l}`),{statusCode:c.status})}o=c.body.getReader()}let a=new TextDecoder;for(;;){let c=s.indexOf(`
346
+ `);if(c!==-1){let p=s.slice(0,c).trim();if(s=s.slice(c+1),p==="data: [DONE]")return i=!0,{done:!0,value:void 0};if(p.startsWith("data: "))try{return{done:!1,value:JSON.parse(p.slice(6))}}catch{continue}continue}let{value:l,done:u}=await o.read();if(u)return i=!0,{done:!0,value:void 0};s+=a.decode(l,{stream:!0})}}}}}}async embed(e){let r=Array.isArray(e)?e:[e],n=await fetch(`${this.baseUrl}/v1/embeddings`,{method:"POST",headers:this.headers(),body:JSON.stringify({model:this.model,input:r})});if(!n.ok){let s=await n.text().catch(()=>"");throw Object.assign(new Error(`OpenAI Embeddings API error ${n.status}: ${s}`),{statusCode:n.status})}let o=await n.json();return{embeddings:o.data.map(s=>s.embedding),usage:o.usage?{totalTokens:o.usage.total_tokens}:void 0}}async generateObject(e){let r={model:e.model??this.model,messages:e.messages,response_format:{type:"json_schema",json_schema:{name:e.schemaName??"response",description:e.schemaDescription,schema:e.schema,strict:!0}}};e.temperature!==void 0&&(r.temperature=e.temperature),e.maxTokens!==void 0&&(r.max_tokens=e.maxTokens);let n=await fetch(`${this.baseUrl}/v1/chat/completions`,{method:"POST",headers:this.headers(),body:JSON.stringify(r)});if(!n.ok){let i=await n.text().catch(()=>"");throw Object.assign(new Error(`OpenAI API error ${n.status}: ${i}`),{statusCode:n.status})}let s=(await n.json()).choices[0]?.message?.content??"";return le(s)}streamObject(e){let r=this,n={model:e.model??this.model,messages:e.messages,stream:!0,stream_options:{include_usage:!0},response_format:{type:"json_schema",json_schema:{name:e.schemaName??"response",description:e.schemaDescription,schema:e.schema,strict:!0}}};return e.temperature!==void 0&&(n.temperature=e.temperature),e.maxTokens!==void 0&&(n.max_tokens=e.maxTokens),{[Symbol.asyncIterator](){let o=null,s="",i="",a=!1;return{async next(){if(a)return{done:!0,value:void 0};if(!o){let l=await fetch(`${r.baseUrl}/v1/chat/completions`,{method:"POST",headers:r.headers(),body:JSON.stringify(n)});if(!l.ok){let u=await l.text().catch(()=>"");throw Object.assign(new Error(`OpenAI API error ${l.status}: ${u}`),{statusCode:l.status})}o=l.body.getReader()}let c=new TextDecoder;for(;;){let l=s.indexOf(`
347
+ `);if(l!==-1){let m=s.slice(0,l).trim();if(s=s.slice(l+1),m==="data: [DONE]")return a=!0,{done:!1,value:{partial:H(i)??{},done:!0}};if(m.startsWith("data: "))try{let b=JSON.parse(m.slice(6)),f=b.choices?.[0]?.delta?.content;if(f){i+=f;let P=H(i);if(P!==void 0){let w=b.usage?{inputTokens:b.usage.prompt_tokens??0,outputTokens:b.usage.completion_tokens??0}:void 0;return{done:!1,value:{partial:P,done:!1,usage:w}}}}if(b.usage&&!f)return{done:!1,value:{partial:H(i)??{},done:!1,usage:{inputTokens:b.usage.prompt_tokens??0,outputTokens:b.usage.completion_tokens??0}}}}catch{continue}continue}let{value:u,done:p}=await o.read();if(p)return a=!0,{done:!1,value:{partial:H(i)??{},done:!0}};s+=c.decode(u,{stream:!0})}}}}}}headers(){return{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}}buildRequestBody(e,r){let n={model:e.model??this.model,messages:e.messages,stream:r};return e.temperature!==void 0&&(n.temperature=e.temperature),e.maxTokens!==void 0&&(n.max_tokens=e.maxTokens),e.tools?.length&&(n.tools=e.tools),e.stop!==void 0&&(n.stop=e.stop),r&&(n.stream_options={include_usage:!0}),n}mapFinishReason(e){switch(e){case"stop":return"stop";case"tool_calls":return"tool_calls";case"length":return"length";case"content_filter":return"content_filter";default:return null}}};var nr="2023-06-01",X=class{id;apiKey;model;baseUrl;maxTokens;constructor(e){this.id=e.id??`anthropic:${e.model}`,this.apiKey=e.apiKey,this.model=e.model,this.baseUrl=(e.baseUrl??"https://api.anthropic.com").replace(/\/+$/,""),this.maxTokens=e.maxTokens??4096}capabilities(){return{nativeToolCalling:!0,vision:!0,streaming:!0,structuredOutput:!1,maxContextTokens:2e5}}async complete(e){let r=this.buildRequestBody(e),n=await fetch(`${this.baseUrl}/v1/messages`,{method:"POST",headers:this.headers(),body:JSON.stringify(r)});if(!n.ok){let a=await n.text().catch(()=>"");throw Object.assign(new Error(`Anthropic API error ${n.status}: ${a}`),{statusCode:n.status})}let o=await n.json(),s="",i=[];for(let a of o.content)a.type==="text"&&a.text?s+=a.text:a.type==="tool_use"&&a.id&&a.name&&i.push({id:a.id,type:"function",function:{name:a.name,arguments:JSON.stringify(a.input??{})}});return{content:s,toolCalls:i.length>0?i:void 0,finishReason:this.mapStopReason(o.stop_reason),usage:{inputTokens:o.usage.input_tokens,outputTokens:o.usage.output_tokens}}}stream(e){let r=this,n={...this.buildRequestBody(e),stream:!0};return{[Symbol.asyncIterator](){let o=null,s="",i=!1,a=0;return{async next(){if(i)return{done:!0,value:void 0};if(!o){let l=await fetch(`${r.baseUrl}/v1/messages`,{method:"POST",headers:r.headers(),body:JSON.stringify(n)});if(!l.ok){let u=await l.text().catch(()=>"");throw Object.assign(new Error(`Anthropic API error ${l.status}: ${u}`),{statusCode:l.status})}o=l.body.getReader()}let c=new TextDecoder;for(;;){let l=s.indexOf(`
348
+ `);if(l!==-1){let m=s.slice(0,l).trim();if(s=s.slice(l+1),!m.startsWith("data: "))continue;let b=m.slice(6),f;try{f=JSON.parse(b)}catch{continue}if(f.type==="message_start"&&f.message?.usage&&(a=f.message.usage.input_tokens),f.type==="content_block_delta"&&f.delta?.text)return{done:!1,value:{choices:[{delta:{content:f.delta.text},finish_reason:null,index:0}]}};if(f.type==="message_delta"){let P=f.usage?.output_tokens??0;return{done:!1,value:{choices:[{delta:{},finish_reason:f.delta?.stop_reason==="end_turn"?"stop":f.delta?.stop_reason??null,index:0}],usage:{prompt_tokens:a,completion_tokens:P}}}}if(f.type==="message_stop")return i=!0,{done:!0,value:void 0};continue}let{value:u,done:p}=await o.read();if(p)return i=!0,{done:!0,value:void 0};s+=c.decode(u,{stream:!0})}}}}}}headers(){return{"Content-Type":"application/json","x-api-key":this.apiKey,"anthropic-version":nr}}buildRequestBody(e){let r,n=[];for(let s of e.messages)s.role==="system"?r=typeof s.content=="string"?s.content:"":n.push({role:s.role==="tool"?"user":s.role,content:this.convertContent(s)});let o={model:e.model??this.model,messages:n,max_tokens:e.maxTokens??this.maxTokens};return r&&(o.system=r),e.temperature!==void 0&&(o.temperature=e.temperature),e.stop!==void 0&&(o.stop_sequences=Array.isArray(e.stop)?e.stop:[e.stop]),e.tools?.length&&(o.tools=e.tools.map(s=>({name:s.function.name,description:s.function.description,input_schema:s.function.parameters??{type:"object",properties:{}}}))),o}convertContent(e){return typeof e.content=="string"?e.content:e.content===null?"":e.content.map(r=>r.type==="text"?{type:"text",text:r.text}:r.type==="image_url"&&r.image_url?{type:"image",source:{type:"url",url:r.image_url.url}}:{type:"text",text:""})}mapStopReason(e){switch(e){case"end_turn":case"stop_sequence":return"stop";case"tool_use":return"tool_calls";case"max_tokens":return"length";default:return null}}};async function we(t,e){let r=[...t].sort((o,s)=>o.priority-s.priority),n={...e.data};for(let o of r){if(!o.enabled||o.point==="event"&&e.event&&o.eventTypes?.length&&!o.eventTypes.includes(e.event.type))continue;let s={...e,data:n},i=await o.handler(s);if(i.data&&(n={...n,...i.data}),i.action==="skip"||i.action==="halt")return{action:i.action,data:n}}return{action:"continue",data:n}}var Rt=0;function _t(t,e,r,n){return Rt++,{id:n?.id??`hook-${Rt}`,extensionId:t,point:e,priority:n?.priority??50,eventTypes:n?.eventTypes,handler:r,enabled:n?.enabled??!0}}var z=class{extensions=new Map;hooks=new Map;allHooks=new Map;async register(e){this.extensions.has(e.id)&&await this.unregister(e.id),this.extensions.set(e.id,e);let r={extensionId:e.id,registerHook:(n,o,s)=>{let i=_t(e.id,n,o,s);this.allHooks.has(i.id)&&this.removeHook(i.id),this.allHooks.set(i.id,i);let a=this.hooks.get(n)??[];return a.push(i),this.hooks.set(n,a),i.id}};await e.setup(r)}async registerAll(e){for(let r of e)await this.register(r)}async unregister(e){let r=this.extensions.get(e);if(r){r.teardown&&await r.teardown();for(let[n,o]of this.hooks)this.hooks.set(n,o.filter(s=>s.extensionId!==e));for(let[n,o]of this.allHooks)o.extensionId===e&&this.allHooks.delete(n);this.extensions.delete(e)}}has(e){return this.extensions.has(e)}listExtensions(){return Array.from(this.extensions.keys())}getHooks(e){return[...this.hooks.get(e)??[]].sort((n,o)=>n.priority-o.priority)}getHook(e){return this.allHooks.get(e)}setHookEnabled(e,r){let n=this.allHooks.get(e);return n?(n.enabled=r,!0):!1}get hookCount(){return this.allHooks.size}async dispatch(e,r){let n=this.hooks.get(e)??[];return n.length===0?{action:"continue",data:r.data}:we(n,{...r,hookPoint:e})}async dispatchEvent(e,r,n){let o=this.hooks.get("event")??[];o.length!==0&&await we(o,{sessionId:r,hookPoint:"event",event:e,data:n??{}})}removeHook(e){let r=this.allHooks.get(e);if(!r)return;let n=this.hooks.get(r.point);n&&this.hooks.set(r.point,n.filter(o=>o.id!==e)),this.allHooks.delete(e)}};var Y=class{tools=new Map;register(e){if(this.tools.has(e.name))throw new Error(`Tool "${e.name}" is already registered`);this.tools.set(e.name,e)}registerAll(e){for(let r of e)this.register(r)}get(e){return this.tools.get(e)}has(e){return this.tools.has(e)}list(e){let r=[];for(let n of this.tools.values())(!e||!n.modes?.length||n.modes.includes(e))&&r.push(n);return r}get size(){return this.tools.size}};var ue=class{workspaceConfig={};editionConfig=null;canAccess(e,r){return e.requiredPermissions?.length?e.requiredPermissions.every(n=>r.permissions.includes(n)):!0}check(e,r){if(!e.requiredPermissions?.length)return null;let n=e.requiredPermissions.filter(o=>!r.permissions.includes(o));return n.length===0?null:{tool:e.name,reason:`Missing permissions: ${n.join(", ")}`,missing:n}}setWorkspaceConfig(e){this.workspaceConfig={...e}}getWorkspaceConfig(){return{...this.workspaceConfig}}setEditionConfig(e){this.editionConfig={...e}}getEditionConfig(){return this.editionConfig?{...this.editionConfig}:null}checkACL(e,r){let n=e.name;if(this.editionConfig&&!this.isToolInEdition(n))return{allowed:!1,tool:n,reason:`Tool "${n}" not available in active edition`};if(this.workspaceConfig.whitelist?.length&&!this.matchesPattern(n,this.workspaceConfig.whitelist))return{allowed:!1,tool:n,reason:`Tool "${n}" not in workspace whitelist`};if(this.workspaceConfig.blacklist?.length&&this.matchesPattern(n,this.workspaceConfig.blacklist))return{allowed:!1,tool:n,reason:`Tool "${n}" blocked by workspace blacklist`};let o=this.check(e,r);return o?{allowed:!1,tool:n,reason:o.reason}:{allowed:!0,tool:n,reason:"Access granted"}}filterVisible(e,r){return e.filter(n=>this.checkACL(n,r).allowed)}isToolInEdition(e){if(!this.editionConfig||this.editionConfig.tools.includes("*")||this.editionConfig.tools.includes(e))return!0;if(this.editionConfig.categories){for(let r of this.editionConfig.categories)if(e.startsWith(`${r}.`)||e===r)return!0}return!1}matchesPattern(e,r){for(let n of r)if(n==="*"||n===e||n.endsWith(".*")&&e.startsWith(`${n.slice(0,-2)}.`))return!0;return!1}};var Q=class{constructor(e,r){this.registry=e;this.acl=r??new ue}acl;async route(e,r){let n=this.registry.get(e.name);if(!n)return{name:e.name,error:`Unknown tool: "${e.name}"`};if(n.modes?.length&&!n.modes.includes(r.mode))return{name:e.name,error:`Tool "${e.name}" is not available in "${r.mode}" mode`};let o=this.acl.check(n,r);if(o)return{name:e.name,error:o.reason};try{let s=await n.handler(e.arguments,r);return{name:e.name,result:s}}catch(s){let i=s instanceof Error?s.message:String(s);return{name:e.name,error:i}}}listAvailable(e){return this.registry.list(e.mode).filter(r=>this.acl.canAccess(r,e))}};function rr(t){return t.role!=="assistant"||!t.tool_calls?[]:t.tool_calls.map(e=>e.id)}function Z(t){let e=or(t);return sr(t,e),ar(t),cr(t,e),lr(t,e),ur(t),dr(t),t}function or(t){let e=new Set;for(let r of t)for(let n of rr(r))e.add(n);return e}function sr(t,e){for(let r=t.length-1;r>=0;r--)t[r].role==="tool"&&t[r].tool_call_id&&!e.has(t[r].tool_call_id)&&t.splice(r,1)}function ir(t){return t.content===null?!t.tool_calls?.length:typeof t.content=="string"||Array.isArray(t.content)?t.content.length===0:!0}function ar(t){for(let e=t.length-1;e>=0;e--)ir(t[e])&&t.splice(e,1)}function cr(t,e){let r=[];for(let n=t.length-1;n>=0;n--)t[n].role==="tool"&&t[n].tool_call_id&&(r.push(t[n]),t.splice(n,1));r.reverse();for(let n of r){let o=n.tool_call_id,s=t.length;for(let i=0;i<t.length;i++){let a=t[i];if(a.role==="assistant"&&a.tool_calls&&a.tool_calls.some(c=>c.id===o)){for(s=i+1;s<t.length&&t[s].role==="tool";)s++;break}}t.splice(s,0,n)}}function lr(t,e){let r=new Set;for(let n of t)n.role==="tool"&&n.tool_call_id&&r.add(n.tool_call_id);for(let n of e)if(!r.has(n))for(let o=0;o<t.length;o++){let s=t[o];if(s.role==="assistant"&&s.tool_calls&&s.tool_calls.some(i=>i.id===n)){let i=o+1;for(;i<t.length&&t[i].role==="tool";)i++;t.splice(i,0,{role:"tool",content:"[session-repair] Tool result missing \u2014 session was interrupted.",tool_call_id:n});break}}}function ur(t){let e=new Set;for(let r=t.length-1;r>=0;r--)t[r].role==="tool"&&t[r].tool_call_id&&(e.has(t[r].tool_call_id)?t.splice(r,1):e.add(t[r].tool_call_id))}function dr(t){for(let e=t.length-1;e>0;e--){let r=t[e],n=t[e-1];r.role===n.role&&(r.role==="user"||r.role==="system")&&typeof r.content=="string"&&typeof n.content=="string"&&!r.tool_call_id&&!n.tool_call_id&&(n.content=n.content+`
349
+ `+r.content,t.splice(e,1))}}function A(t){let e=0;for(let r of t)e+=pr(r);return Math.ceil(e/4)}function pr(t){if(t.content===null)return 0;if(typeof t.content=="string")return t.content.length;let e=0;for(let r of t.content)e+=r.text?.length??0,r.image_url&&(e+=200);return e}function mr(t){return t.map(e=>e.role!=="tool"||typeof e.content!="string"||e.content.length<=512?e:{...e,content:e.content.slice(0,512)+`
350
+ ...[truncated]`})}function It(t,e){let r=t.filter(s=>s.role==="system"),o=t.filter(s=>s.role!=="system").slice(-e);return[...r,...o]}var gr="Summarize the following conversation concisely. Preserve key facts, decisions, tool results, and user preferences. Output only the summary.";async function fr(t,e,r){let n=t.filter(l=>l.role==="system"),o=t.filter(l=>l.role!=="system");if(o.length<=4)return t;let s=o.slice(0,-4),i=o.slice(-4),a=s.map(l=>{let u=l.content===null?"":typeof l.content=="string"?l.content:l.content.map(p=>p.text??"").join("");return`[${l.role}]: ${u}`}).join(`
351
+ `),c={model:r,messages:[{role:"system",content:gr},{role:"user",content:a}],maxTokens:1024,temperature:0};try{let l=await e.complete(c),u={role:"system",content:`[Compacted ${s.length} messages]
352
+ ${l.content}`};return[...n,u,...i]}catch{return t}}async function*Se(t,e){let r=e.contextWindowTokens??2e5,n=e.sessionId,o=Date.now(),s=t,i=A(s)/r;if(i<.7)return{messages:s,stage:0,strategy:"none"};let a=A(s);if(s=mr(s),i=A(s)/r,yield{type:"context:overflow",timestamp:o,sessionId:n,tokensUsed:a,tokenBudget:r},yield{type:"context:truncating",timestamp:o,sessionId:n,beforeTokens:a,afterTokens:A(s),strategy:"trim_tool_results"},i<.7)return{messages:s,stage:1,strategy:"trim_tool_results"};let c=A(s);if(s=It(s,10),i=A(s)/r,yield{type:"context:truncating",timestamp:o,sessionId:n,beforeTokens:c,afterTokens:A(s),strategy:"remove_oldest"},i<.7)return{messages:s,stage:2,strategy:"remove_oldest"};if(e.utilityProvider){let u=A(s);if(s=await fr(s,e.utilityProvider,e.utilityModel??"claude-haiku-4-5-20251001"),i=A(s)/r,yield{type:"context:truncating",timestamp:o,sessionId:n,beforeTokens:u,afterTokens:A(s),strategy:"llm_compaction"},i<.7)return{messages:s,stage:3,strategy:"llm_compaction"}}let l=A(s);return s=It(s,2),yield{type:"context:truncating",timestamp:o,sessionId:n,beforeTokens:l,afterTokens:A(s),strategy:"emergency_truncation"},{messages:s,stage:4,strategy:"emergency_truncation"}}function Ce(t,e){let r=e??2e5,n=A(t);return{ratio:n/r,overLimit:n/r>=.7}}var Pt={blocked:!1,warning:!1};function Ae(t){let e=t.function.name+":"+t.function.arguments;return Bun.hash(e).toString(16)}function hr(t){return Bun.hash(t).toString(16)}var U=class{maxTotal;history=[];callCounts=new Map;pairCounts=new Map;constructor(e=25){this.maxTotal=e}check(e){for(let n of e){let o=Ae(n);this.history.push(o),this.callCounts.set(o,(this.callCounts.get(o)??0)+1)}if(this.history.length>=this.maxTotal)return{blocked:!0,warning:!1,reason:`Max total calls reached (${this.maxTotal})`};for(let n of e){let o=Ae(n),s=this.callCounts.get(o);if(s>=5)return{blocked:!0,warning:!1,reason:`Identical call repeated ${s} times`};if(s>=3){let i=this.detectPingPong();return i||{blocked:!1,warning:!0,reason:`Identical call repeated ${s} times`}}}let r=this.detectPingPong();return r||Pt}recordResult(e,r){let n=Ae(e)+":"+hr(r),o=(this.pairCounts.get(n)??0)+1;return this.pairCounts.set(n,o),o>=3?{blocked:!0,warning:!1,reason:`Same call+result pair repeated ${o} times`}:o>=2?{blocked:!1,warning:!0,reason:`Same call+result pair repeated ${o} times`}:Pt}reset(){this.history=[],this.callCounts.clear(),this.pairCounts.clear()}totalCalls(){return this.history.length}detectPingPong(){let e=this.history;if(e.length<6)return null;let r=e.length,n=e[r-1],o=e[r-2];if(n===o)return null;let s=1;for(let i=r-3;i>=0&&!(i-1<0);i-=2)if(e[i]===n&&e[i-1]===o)s++;else break;return s>=3?{blocked:!0,warning:!1,reason:`Ping-pong pattern detected (${s} alternations)`}:null}};var yr=0;function vr(){return`task-${Date.now()}-${++yr}`}var ee=class{_tasks=[];get tasks(){return this._tasks}add(e){let r={id:vr(),title:e,status:"pending"};return this._tasks.push(r),r}update(e,r){let n=this._tasks.find(o=>o.id===e);n&&(n.status=r)}current(){let e=this._tasks.find(r=>r.status==="in_progress");return e||this._tasks.find(r=>r.status==="pending")}plan(e){let r=[];for(let n of e)r.push(this.add(n));return r}complete(e){this.update(e,"done")}block(e){this.update(e,"blocked")}summary(){let e=0,r=0,n=0,o=0;for(let s of this._tasks)switch(s.status){case"pending":e++;break;case"in_progress":r++;break;case"done":n++;break;case"blocked":o++;break}return{total:this._tasks.length,pending:e,inProgress:r,done:n,blocked:o}}};async function*Re(t){let{sessionId:e,tracker:r,toolLog:n,brainArtifacts:o,conversationId:s,userId:i}=t;for(let m of r.tasks)m.status==="in_progress"&&r.update(m.id,"blocked");let a=0,c=0,l=0;for(let m of r.tasks)switch(m.status){case"done":a++;break;case"pending":c++;break;case"blocked":l++;break}let u=r.tasks.length,p=n.map(m=>`${m.tool}:${m.ok?"ok":"fail"}`).join(", ");if(o&&s&&i){let m=r.tasks.filter(b=>b.status==="pending"||b.status==="blocked");if(m.length>0){let b=["# Open Tasks (from wrapup)","",...m.map(f=>`- [${f.status}] ${f.title}`),"",`Session: ${e}`,`Timestamp: ${new Date().toISOString()}`].join(`
353
+ `);try{await o.save(s,i,"open-tasks.md",b)}catch{}}}yield{type:"wrapup:complete",timestamp:Date.now(),sessionId:e,summary:`Completed ${a}/${u} tasks (${c} pending, ${l} blocked). Tools: ${p||"none"}`}}var de=class{blocks=new Map;nextId=1;create(e,r){let n=`blk-${this.nextId++}`;return this.blocks.set(n,{type:e,data:r}),n}append(e,r){let n=this.blocks.get(e);return n?(typeof n.data=="string"&&(n.data+=r),{op:"append",path:"data",value:r}):null}get(e){return this.blocks.get(e)}get size(){return this.blocks.size}};async function*_e(t,e){let{providers:r,toolRouter:n,jobQueue:o,extensions:s,sessionId:i,maxTurns:a=20,tokenBudget:c=128e3,contextWindowTokens:l=2e5,initialMode:u="conversation",systemPrompt:p,permissions:m=[],classifyQuery:b,emitBlocks:f=!1,readOnlyTools:P=new Set}=t,w=u,C=0,M=0,T=[],F=new de,y=new U(a),N=new ee,k=[],R={sessionId:i,mode:w,turnNumber:0,tokenBudget:c,tokensUsed:0,activeTools:[]};if(yield{type:"agent:start",timestamp:Date.now(),sessionId:i,mode:w,input:e},p&&T.push({role:"system",content:p}),e.text&&T.push({role:"user",content:e.text}),o)try{let S=await o.getJobsByStatus("queued");S.length>0&&T.push({role:"system",content:`[System] ${S.length} pending job(s) in queue: ${S.map(O=>`${O.type}(${O.id})`).join(", ")}`})}catch{}let be=br(T);if(M+=be,yield{type:"context:built",timestamp:Date.now(),sessionId:i,tokenCount:be,messageCount:T.length},b&&e.text)try{let S=await b(e.text,R);if(yield{type:"query:classified",timestamp:Date.now(),sessionId:i,queryId:`q-${i}-${C}`,classification:S.classification,standaloneQuery:S.standaloneQuery,skipAgent:S.skipAgent},S.skipAgent&&S.directResponse){yield*Mt(S.directResponse,F,f,i,0),yield{type:"response:final",timestamp:Date.now(),sessionId:i,content:S.directResponse,usage:{inputTokens:be,outputTokens:Tr(S.directResponse)},finishReason:"stop"},yield{type:"agent:end",timestamp:Date.now(),sessionId:i,reason:"complete",totalTurns:0,totalTokens:M};return}}catch{}for(;C<a;){if(C++,R.turnNumber=C,R.mode=w,R.tokensUsed=M,Z(T),Ce(T,l).overLimit){let v=Se(T,{contextWindowTokens:l,utilityProvider:r.getHealthy("utility")??void 0,sessionId:i}),$;for(;;){let W=await v.next();if(W.done){$=W.value;break}yield W.value}T.length=0,T.push(...$.messages)}if(M>=c){yield{type:"context:overflow",timestamp:Date.now(),sessionId:i,tokensUsed:M,tokenBudget:c};break}let O=r.getHealthy("chat");if(!O){yield{type:"error",timestamp:Date.now(),sessionId:i,error:`No healthy provider available for mode "${w}"`,recoverable:!1};break}let Te={mode:w,permissions:m},yt=n.listAvailable(Te);R.activeTools=yt.map(v=>v.name);let vt=yt.map(v=>({type:"function",function:{name:v.name,description:v.description,parameters:v.parameters?kr(v.parameters):void 0}}));s&&await s.dispatch("prompt:build",{sessionId:i,data:{messages:T,mode:w,turn:C},messages:T.map(v=>({role:v.role,content:typeof v.content=="string"?v.content:""}))});let bt={messages:[...T],tools:vt.length>0?vt:void 0,temperature:w==="code"?.2:.7},h;try{h=await O.complete(bt)}catch(v){let $=v instanceof Error?v.message:String(v);yield{type:"error",timestamp:Date.now(),sessionId:i,error:`LLM call failed: ${$}`,code:"LLM_ERROR",recoverable:!0};break}if(h.usage&&(M+=h.usage.inputTokens+h.usage.outputTokens,yield{type:"cost:tracked",timestamp:Date.now(),sessionId:i,provider:O.id,model:bt.model??"default",inputTokens:h.usage.inputTokens,outputTokens:h.usage.outputTokens,estimatedCost:Er(h.usage.inputTokens,h.usage.outputTokens)}),h.content&&(yield*Mt(h.content,F,f,i,C-1),T.push({role:"assistant",content:h.content})),h.toolCalls&&h.toolCalls.length>0){T.push({role:"assistant",content:null,tool_calls:h.toolCalls});let v=y.check(h.toolCalls);if(v.blocked){yield{type:"loop:blocked",timestamp:Date.now(),sessionId:i,turnCount:C,reason:v.reason??"Loop guard blocked"};break}v.warning&&(yield{type:"loop:warning",timestamp:Date.now(),sessionId:i,turnCount:C,pattern:v.reason??"repetition_detected"});let $=h.toolCalls.filter(E=>P.has(E.function.name)),W=h.toolCalls.filter(E=>!P.has(E.function.name));if($.length>0){let E=$.map(async L=>{let g=Lt(L.function.arguments);if(s&&(await s.dispatch("tool:before",{sessionId:i,data:{toolName:L.function.name,arguments:g}})).action==="halt")return{tc:L,toolResult:{name:L.function.name,error:"Blocked by extension"},args:g,durationMs:0};let x=Date.now(),B=await n.route({name:L.function.name,arguments:g},Te);return{tc:L,toolResult:B,args:g,durationMs:Date.now()-x}}),J=await Promise.allSettled(E);for(let L of J){if(L.status!=="fulfilled")continue;let{tc:g,toolResult:x,args:B,durationMs:_}=L.value;if(k.push({tool:g.function.name,ok:!x.error}),yield{type:"tool:start",timestamp:Date.now(),sessionId:i,toolName:g.function.name,arguments:B,callId:g.id},yield{type:"tool:result",timestamp:Date.now(),sessionId:i,toolName:g.function.name,callId:g.id,result:x.result,error:x.error,durationMs:_},s&&await s.dispatch("tool:after",{sessionId:i,data:{toolName:g.function.name,result:x,durationMs:_}}),f){let _n=F.create(x.error?"error":"tool_result",x.error??x.result);yield{type:"block:emitted",timestamp:Date.now(),sessionId:i,blockId:_n,blockType:x.error?"error":"tool_result",data:x.error??x.result}}let Tt=JSON.stringify(x.error??x.result);y.recordResult(g,Tt),T.push({role:"tool",content:Tt,tool_call_id:g.id})}}for(let E of W){let J=Lt(E.function.arguments);if(s&&(await s.dispatch("tool:before",{sessionId:i,data:{toolName:E.function.name,arguments:J}})).action==="halt"){k.push({tool:E.function.name,ok:!1}),T.push({role:"tool",content:'{"error":"Blocked by extension"}',tool_call_id:E.id});continue}yield{type:"tool:start",timestamp:Date.now(),sessionId:i,toolName:E.function.name,arguments:J,callId:E.id};let L=Date.now(),g=await n.route({name:E.function.name,arguments:J},Te),x=Date.now()-L;if(k.push({tool:E.function.name,ok:!g.error}),yield{type:"tool:result",timestamp:Date.now(),sessionId:i,toolName:E.function.name,callId:E.id,result:g.result,error:g.error,durationMs:x},s&&await s.dispatch("tool:after",{sessionId:i,data:{toolName:E.function.name,result:g,durationMs:x}}),f){let _=F.create(g.error?"error":"tool_result",g.error??g.result);yield{type:"block:emitted",timestamp:Date.now(),sessionId:i,blockId:_,blockType:g.error?"error":"tool_result",data:g.error??g.result}}let B=JSON.stringify(g.error??g.result);if(y.recordResult(E,B),g.result&&typeof g.result=="object"){let _=g.result;_.id&&_.status==="queued"&&_.type&&(yield{type:"job:enqueued",timestamp:Date.now(),sessionId:i,jobId:_.id,jobType:_.type,jobPriority:_.priority??"normal"})}T.push({role:"tool",content:B,tool_call_id:E.id})}continue}if(h.finishReason==="stop"||h.finishReason===null){if(w==="task"&&h.content?.includes("[PLAN]")){let v=w;w="code",yield{type:"mode:changed",timestamp:Date.now(),sessionId:i,from:v,to:w,reason:"Planning phase complete, moving to execution"};continue}yield{type:"response:final",timestamp:Date.now(),sessionId:i,content:h.content??"",usage:h.usage??{inputTokens:0,outputTokens:0},finishReason:h.finishReason};break}if(h.finishReason==="length"){yield{type:"context:overflow",timestamp:Date.now(),sessionId:i,tokensUsed:M,tokenBudget:c};break}}if(C>=a){yield{type:"loop:warning",timestamp:Date.now(),sessionId:i,turnCount:C,pattern:"max_turns_reached"};let S=Re({sessionId:i,tracker:N,toolLog:k});for await(let O of S)yield O}yield{type:"agent:end",timestamp:Date.now(),sessionId:i,reason:C>=a?"timeout":"complete",totalTurns:C,totalTokens:M}}function Lt(t){try{return JSON.parse(t)}catch{return{}}}function*Mt(t,e,r,n,o){if(r){let s=e.create("text",t);yield{type:"block:emitted",timestamp:Date.now(),sessionId:n,blockId:s,blockType:"text",data:t}}else yield{type:"token",timestamp:Date.now(),sessionId:n,content:t,index:o}}function br(t){let e=0;for(let r of t)if(typeof r.content=="string")e+=r.content.length;else if(Array.isArray(r.content))for(let n of r.content)n.text&&(e+=n.text.length);return Math.ceil(e/4)}function Tr(t){return Math.ceil(t.length/4)}function Er(t,e){return t/1e6*3+e/1e6*15}function kr(t){let e={},r=[];for(let[n,o]of Object.entries(t))e[n]={type:o.type,description:o.description},o.required&&r.push(n);return{type:"object",properties:e,required:r}}var xr={type:"object",properties:{intent:{type:"string",enum:["factual","creative","analytical","conversational","code","search"],description:"Primary intent of the query"},confidence:{type:"number",description:"Confidence score 0-1"},standaloneQuery:{type:"string",description:"Rewritten standalone version of the query (for follow-ups)"},skipAgent:{type:"boolean",description:"True if the query can be answered directly without tools"},directResponse:{type:"string",description:"Direct response if skipAgent is true, omit otherwise"}},required:["intent","confidence","standaloneQuery","skipAgent"],additionalProperties:!1},Nt=`You are a query classifier. Analyze the user's query and determine:
354
+ 1. The primary intent (factual, creative, analytical, conversational, code, search)
355
+ 2. Whether it can be answered directly without tools (simple greetings, factual questions you know)
356
+ 3. A standalone rewrite if the query references prior context
357
+
358
+ For trivial queries like "hello", "thanks", "what is 2+2", set skipAgent=true and provide a directResponse.
359
+ For anything requiring tools, code execution, file access, or research, set skipAgent=false.`;function Ie(t){return async(e,r)=>{let n=Cr(e);if(n)return n;if(t.generateObject){let s=await t.generateObject({messages:[{role:"system",content:Nt},{role:"user",content:e}],schema:xr,schemaName:"query_classification",temperature:0,maxTokens:256});return Ot(s,e)}let o=await t.complete({messages:[{role:"system",content:Nt+`
360
+
361
+ Respond with JSON only.`},{role:"user",content:e}],temperature:0,maxTokens:256});try{let s=JSON.parse(o.content);return Ot(s,e)}catch{return{classification:{intent:"conversational",confidence:.5},standaloneQuery:e,skipAgent:!1}}}}var wr=/^(hi|hello|hey|howdy|greetings|yo|sup)\b[!?.]*$/i,Sr=/^(thanks?|thank\s*you|thx|ty)\b[!?.]*$/i;function Cr(t){let e=t.trim();return wr.test(e)?{classification:{intent:"conversational",confidence:1},standaloneQuery:e,skipAgent:!0,directResponse:"Hello! How can I help you today?"}:Sr.test(e)?{classification:{intent:"conversational",confidence:1},standaloneQuery:e,skipAgent:!0,directResponse:"You're welcome! Let me know if you need anything else."}:null}function Ot(t,e){return{classification:{intent:t.intent,confidence:Math.max(0,Math.min(1,t.confidence))},standaloneQuery:t.standaloneQuery||e,skipAgent:t.skipAgent,directResponse:t.skipAgent?t.directResponse:void 0}}function Pe(t){t.register({id:"fenix.session-repair",name:"Session Repair",setup(e){e.registerHook("prompt:build",async r=>{let n=r.data.messages;return n&&Z(n),{action:"continue",data:r.data}},{id:"session:repair",priority:5})}})}function Le(t){t.register({id:"fenix.logging",name:"Event Logging",setup(e){e.registerHook("event",async r=>(r.event&&console.log(`[event] ${r.event.type}`,r.event.sessionId),{action:"continue",data:r.data}),{id:"event:log",priority:10})}})}function Me(t,e){let r=new U(e);t.register({id:"fenix.loop-guard",name:"Loop Guard",setup(n){n.registerHook("tool:before",async o=>{let s=o.data.toolCalls;if(s&&s.length>0){let i=r.check(s);if(i.blocked)return{action:"halt",data:{reason:i.reason}}}return{action:"continue",data:o.data}},{id:"loop:guard",priority:15})}})}function Dt(t){let e=t.split(".");if(e.length!==4)return null;let r=0;for(let n of e){let o=n.startsWith("0")&&n.length>1?parseInt(n,8):parseInt(n,10);if(Number.isNaN(o)||o<0||o>255)return null;r=r<<8|o}return r>>>0}var Ar=[{base:0,mask:4278190080,label:"Blocked IP range"},{base:167772160,mask:4278190080,label:"private (10.0.0.0/8)"},{base:2886729728,mask:4293918720,label:"private (172.16.0.0/12)"},{base:3232235520,mask:4294901760,label:"private (192.168.0.0/16)"},{base:2130706432,mask:4278190080,label:"loopback (127.0.0.0/8)"},{base:2851995648,mask:4294901760,label:"linkLocal (169.254.0.0/16)"}];function Ne(t){for(let e of Ar)if((t&e.mask)>>>0===e.base)return e.label;return null}function Rr(t){let e=t.replace(/^\[|]$/g,"").toLowerCase(),r=e.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);if(r){let o=Dt(r[1]);if(o!==null){let s=Ne(o);if(s)return`IPv6-mapped ${s}`}}let n=e.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);if(n){let o=parseInt(n[1],16),s=parseInt(n[2],16),i=(o<<16|s)>>>0,a=Ne(i);if(a)return`IPv6-mapped ${a}`}return e==="::1"?"IPv6 loopback":/^fc/.test(e)||/^fd/.test(e)?"fc00::/7 unique local":/^fe[89ab]/.test(e)?"fe80::/10 link-local":null}function _r(t){let e=Dt(t);if(e!==null){let r=Ne(e);return r?{safe:!1,reason:`IP in ${r}`}:{safe:!0}}if(t.startsWith("[")||t.includes(":")){let r=Rr(t);return r?{safe:!1,reason:r}:{safe:!0}}return{safe:!0}}var Ir=new Set(["file:","data:","javascript:","ftp:","gopher:"]),Pr=new Set(["http:","https:"]);function $t(t){if(t.includes("\0"))return{safe:!1,reason:"URL contains null bytes"};if(/\.\.[/\\]/.test(t))return{safe:!1,reason:"URL contains path traversal"};let e;try{e=new URL(t)}catch{return{safe:!1,reason:"Invalid URL"}}return Ir.has(e.protocol)?{safe:!1,reason:`Blocked protocol: ${e.protocol}`}:Pr.has(e.protocol)?_r(e.hostname):{safe:!1,reason:`Unsupported protocol: ${e.protocol}`}}function Oe(t){t.register({id:"fenix.ssrf-protection",name:"SSRF Protection",setup(e){e.registerHook("tool:before",async r=>{let n=r.data.url;if(n){let o=$t(n);if(!o.safe)return{action:"halt",data:{reason:o.reason}}}return{action:"continue",data:r.data}},{id:"security:ssrf",priority:25})}})}function De(t,e=6e4,r=60){let n=new Map;t.register({id:"fenix.rate-limiting",name:"Rate Limiting",setup(o){o.registerHook("tool:before",async s=>{let i=s.data.userId,a=s.data.action;if(!i)return{action:"continue",data:s.data};let c=`${i}:${a??"default"}`,l=Date.now(),u=n.get(c)?.filter(p=>p>l-e)??[];return u.push(l),n.set(c,u),u.length>r?{action:"halt",data:{reason:`Rate limit exceeded: ${r}/${e}ms`}}:{action:"continue",data:s.data}},{id:"security:rate-limit",priority:30})}})}var Lr=new Set(["PATH","HOME","TERM","USER","SHELL","PWD","LANG","NODE_ENV","BUN_ENV"]);function Mr(t){return Lr.has(t)||t.startsWith("LC_")}var Nr=[{re:/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g,label:"${VAR}",group:1},{re:/(?<![\\$\{])\$([A-Za-z_][A-Za-z0-9_]*)/g,label:"$VAR",group:1},{re:/process\.env\.([A-Za-z_][A-Za-z0-9_]*)/g,label:"process.env.VAR",group:1},{re:/process\.env\[['"]([A-Za-z_][A-Za-z0-9_]*)['"]\]/g,label:"process.env['VAR']",group:1},{re:/os\.environ\[['"]([A-Za-z_][A-Za-z0-9_]*)['"]\]/g,label:"os.environ['VAR']",group:1},{re:/os\.environ\.get\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,label:"os.environ.get('VAR')",group:1},{re:/ENV\[['"]([A-Za-z_][A-Za-z0-9_]*)['"]\]/g,label:"ENV['VAR']",group:1},{re:/ENV\.fetch\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,label:"ENV.fetch('VAR')",group:1}];function Ut(t,e="warn"){let r=t.split(`
362
+ `),n=[],o=new Set;for(let i=0;i<r.length;i++){let a=r[i];for(let c of Nr){c.re.lastIndex=0;let l;for(;(l=c.re.exec(a))!==null;){let u=l[c.group],p=`${i}:${u}:${c.label}`;Mr(u)||o.has(p)||(o.add(p),n.push({variable:u,line:i+1,pattern:c.label}))}}}let s=n.length===0;return{safe:s,action:e,leaks:n,blocked:!s&&e==="block"}}function $e(t){t.register({id:"fenix.shell-bleed",name:"Shell Bleed Detection",setup(e){e.registerHook("tool:before",async r=>{let n=r.data.script;if(n){let o=Ut(n);if(!o.safe)return{action:"halt",data:{reason:`Shell bleed: ${o.leaks.map(s=>s.variable).join(", ")}`}}}return{action:"continue",data:r.data}},{id:"security:shell-bleed",priority:35})}})}var Or="Summarize the following conversation concisely. Preserve key facts, decisions, tool results, and user preferences. Output only the summary, no preamble.";function Ue(t){let e=0;for(let r of t)if(r.content!==null)if(typeof r.content=="string")e+=r.content.length;else for(let n of r.content)e+=n.text?.length??0;return Math.ceil(e/4)}function Fe(t,e){return t.length>30?!0:Ue(t)/e.contextWindowTokens>.7}function Dr(t,e){return e.utilityProvider?t.length>=50?"hybrid":"summarize-all":"summarize-oldest"}function $r(t){return t.map(e=>{let r=e.content===null?"":typeof e.content=="string"?e.content:e.content.map(n=>n.text??"").join("");return`[${e.role}]: ${r}`}).join(`
363
+ `)}async function Ft(t,e,r){return(await e.complete({model:r,messages:[{role:"system",content:Or},{role:"user",content:$r(t)}],maxTokens:1024,temperature:0})).content}async function Ur(t,e,r){return Ft(t,e,r)}async function Fr(t){let e=t[0],r=t[t.length-1],n=e?.content===null?"":typeof e?.content=="string"?e.content:"[complex content]",o=r?.content===null?"":typeof r?.content=="string"?r.content:"[complex content]";return`[Compacted: ${t.length} messages removed]
364
+ First: ${n.slice(0,200)}
365
+ Last: ${o.slice(0,200)}`}async function Br(t,e,r){let n=[];for(let s=0;s<t.length;s+=10)n.push(t.slice(s,s+10));return(await Promise.all(n.map(s=>Ft(s,e,r)))).join(`
366
+
367
+ `)}async function Be(t,e){let r=Dr(t,e),n=e.utilityModel??"claude-haiku-4-5-20251001",o=Ue(t);if(t.length<=6)return{messages:t,removed:0,strategy:r,beforeTokens:o,afterTokens:o};let s=t.slice(0,-6),i=t.slice(-6),a;switch(r){case"summarize-all":a=await Ur(s,e.utilityProvider,n);break;case"hybrid":a=await Br(s,e.utilityProvider,n);break;case"summarize-oldest":a=await Fr(s);break}let l=[{role:"system",content:`[Session Summary]
368
+ ${a}`},...i],u=Ue(l);return{messages:l,removed:s.length,strategy:r,beforeTokens:o,afterTokens:u}}function je(t){t.register({id:"fenix.context-survival",name:"Context Survival",setup(e){e.registerHook("prompt:build",async r=>{let n=r.data.messages,o=r.data.contextWindowTokens;if(!n)return{action:"continue",data:r.data};let s={contextWindowTokens:o??2e5};if(!Fe(n,s))return{action:"continue",data:{...r.data,compacted:!1}};let i=await Be(n,s);return{action:"continue",data:{...r.data,messages:i.messages,compacted:!0}}},{id:"context:survival",priority:45})}})}var qe={steeringRules:.2,memories:.4,orientContext:.2,brainArtifacts:.2};function pe(t){return Math.ceil(t.length/4)}function He(t,e){let r=Math.max(0,t-200),o=Object.keys(qe).filter(a=>e[a]>0);if(o.length===0)return{identity:200,steeringRules:0,memories:0,orientContext:0,brainArtifacts:0,total:200};let s=o.reduce((a,c)=>a+qe[c],0),i={identity:200,steeringRules:0,memories:0,orientContext:0,brainArtifacts:0,total:0};for(let a of o)i[a]=Math.floor(r*(qe[a]/s));return i.total=i.identity+i.steeringRules+i.memories+i.orientContext+i.brainArtifacts,i}function me(t,e){if(e<=0)return"";if(pe(t)<=e)return t;let n=e*4,o=t.slice(0,n),s=o.lastIndexOf(`
369
+ `);return(s>0?o.slice(0,s):o)+`
370
+ [...truncated]`}function We(t){t.register({id:"fenix.context-budget",name:"Context Budget",setup(e){e.registerHook("prompt:build",async r=>{let n=r.data.sections,o=r.data.totalBudget;if(!n||!o)return{action:"continue",data:r.data};let s=Object.fromEntries(Object.entries(n).map(([c,l])=>[c,pe(l)])),i=He(o,s),a=Object.fromEntries(Object.entries(n).map(([c,l])=>{let u=i[c]??o;return[c,me(l,u)]}));return{action:"continue",data:{...r.data,sections:a,allocation:i}}},{id:"context:budget",priority:50})}})}function Je(t={}){let e=[];return{id:"fenix.job-dispatch",name:"Job Dispatch Tracker",version:"1.0.0",setup(r){r.registerHook("event",async n=>{let o=n.event;if(!o||o.type!=="tool:result")return{action:"continue"};let s=o.result;if(s&&typeof s=="object"){let i=s;if(i.id&&i.status==="queued"&&i.type){let a={id:i.id,type:i.type,toolName:o.toolName,timestamp:o.timestamp};return e.push(a),t.onJobDispatched&&t.onJobDispatched({id:a.id,type:a.type,toolName:a.toolName}),{action:"continue",data:{lastDispatchedJob:a,dispatchedJobCount:e.length}}}}return{action:"continue"}},{id:"fenix.job-dispatch.tracker",priority:65,eventTypes:["tool:result"]})},teardown(){e.length=0}}}function Ke(t){let e=[`# Skill: ${t.name}`,t.description,"",t.body];return t.files.length>0&&e.push("","Files:",...t.files.map(r=>`- ${r}`)),e.join(`
371
+ `)}function Ge(t,e,r=3){let n=e.toLowerCase().split(/\s+/).filter(Boolean);return n.length===0?[]:t.map(s=>{let i=`${s.name} ${s.description}`.toLowerCase(),a=n.reduce((c,l)=>c+(i.includes(l)?1:0),0);return{skill:s,score:a}}).filter(s=>s.score>0).sort((s,i)=>i.score-s.score).slice(0,r).map(s=>Ke(s.skill))}function Ve(t){let e=t.maxSkills??2;return{id:"fenix.skill-loader",name:"Skill Loader",version:"1.0.0",setup(r){r.registerHook("prompt:build",async n=>{let o=n.data.query;if(!o||t.skills.length===0)return{action:"continue"};let s=Ge(t.skills,o,e);if(s.length===0)return{action:"continue"};if(n.messages)for(let i of s)n.messages.push({role:"system",content:`[Skill Context]
372
+ ${i}`});return t.onSkillsLoaded&&t.onSkillsLoaded(s),{action:"continue",data:{loadedSkills:s.length,skillNames:s.map(i=>i.match(/^# Skill: (.+)$/m)?.[1]??"unknown")}}},{id:"fenix.skill-loader.prompt-build",priority:70})}}}function Xe(t){return[{name:"memory.search",description:"Search user memory for relevant facts, preferences, and knowledge by semantic similarity",parameters:{userId:{type:"string",required:!0,description:"User ID to search memories for"},query:{type:"string",required:!0,description:"Natural language search query"},limit:{type:"number",description:"Max results to return (default: 5)"}},handler:async e=>(await t.search(e.userId,e.query,e.limit??5)).map(n=>({id:n.segment.id,content:n.segment.content,category:n.segment.category,score:n.score,heat:n.segment.heat}))},{name:"memory.expand",description:"Expand a memory segment with related context and connections",parameters:{userId:{type:"string",required:!0,description:"User ID"},query:{type:"string",required:!0,description:"Topic to expand on"},limit:{type:"number",description:"Max related segments (default: 10)"}},handler:async e=>{let r=await t.search(e.userId,e.query,e.limit??10);return{topic:e.query,segments:r.map(n=>({content:n.segment.content,category:n.segment.category,score:n.score})),count:r.length}}},{name:"memory.context",description:"Get the user's profile summary and recent memory context",parameters:{userId:{type:"string",required:!0,description:"User ID"}},handler:async e=>{let r=await t.getProfile(e.userId);return{userId:e.userId,profile:r??"No profile available"}}},{name:"memory.save",description:"Save a new memory segment (fact, preference, experience, or knowledge)",parameters:{userId:{type:"string",required:!0,description:"User ID"},content:{type:"string",required:!0,description:"Memory content to save"},category:{type:"string",required:!0,description:"fact | preference | experience | knowledge"}},requiredPermissions:["memory:write"],handler:async e=>{let r=await t.save(e.userId,e.content,e.category);return{id:r.id,saved:!0,category:r.category}}},{name:"memory.forget",description:"Remove a specific memory segment by ID",parameters:{segmentId:{type:"string",required:!0,description:"Memory segment ID to remove"}},requiredPermissions:["memory:write"],handler:async e=>({deleted:await t.forget(e.segmentId),segmentId:e.segmentId})},{name:"memory.list",description:"List memory segments for a user, optionally filtered by category",parameters:{userId:{type:"string",required:!0,description:"User ID"},category:{type:"string",description:"Filter by: fact | preference | experience | knowledge"},limit:{type:"number",description:"Max results (default: 20)"}},handler:async e=>(await t.list(e.userId,e.category,e.limit??20)).map(n=>({id:n.id,content:n.content,category:n.category,heat:n.heat,createdAt:n.createdAt}))},{name:"memory.graph",description:"Query the knowledge graph for entity relationships",parameters:{userId:{type:"string",required:!0,description:"User ID"},entity:{type:"string",required:!0,description:"Entity name to query"}},handler:async e=>t.graphQuery?t.graphQuery(e.userId,e.entity):{error:"Knowledge graph not available"}}]}function ze(t){return[{name:"web.search",description:"Search the web for information. Uses primary search provider with fallback on failure",parameters:{query:{type:"string",required:!0,description:"Search query"},limit:{type:"number",description:"Max results (default: 5)"}},handler:async e=>{let r=e.query,n=e.limit??5;try{return await t.primary.search(r,{limit:n})}catch(o){if(!t.fallback)throw o;try{return await t.fallback.search(r,{limit:n})}catch{throw o}}}},{name:"web.fetch",description:"Fetch and extract content from a URL. Returns cleaned page text",parameters:{url:{type:"string",required:!0,description:"URL to fetch content from"}},handler:async e=>t.fetcher.fetch(e.url)}]}function Ye(t){return[{name:"code.execute",description:"Execute code in a sandboxed environment. Supports JavaScript, Python, and shell",parameters:{code:{type:"string",required:!0,description:"Code to execute"},language:{type:"string",required:!0,description:"Language: javascript | python | shell"},timeout:{type:"number",description:"Timeout in ms (default: 30000)"}},requiredPermissions:["code:execute"],handler:async e=>t.execute(e.code,e.language,e.timeout??3e4)},{name:"code.analyze",description:"Analyze code for complexity, patterns, and potential issues",parameters:{code:{type:"string",required:!0,description:"Code to analyze"},language:{type:"string",required:!0,description:"Programming language"}},handler:async e=>t.analyze(e.code,e.language)},{name:"code.explain",description:"Generate a human-readable explanation of code",parameters:{code:{type:"string",required:!0,description:"Code to explain"},language:{type:"string",required:!0,description:"Programming language"}},handler:async e=>({explanation:await t.explain(e.code,e.language),language:e.language})}]}function Qe(t){return[{name:"files.read",description:"Read the contents of a file",parameters:{path:{type:"string",required:!0,description:"File path to read"}},handler:async e=>{let r=await t.read(e.path);return{path:e.path,content:r}}},{name:"files.write",description:"Write content to a file (creates or overwrites)",parameters:{path:{type:"string",required:!0,description:"File path to write to"},content:{type:"string",required:!0,description:"Content to write"}},requiredPermissions:["files:write"],handler:async e=>t.write(e.path,e.content)},{name:"files.list",description:"List files and directories at a given path",parameters:{directory:{type:"string",required:!0,description:"Directory path to list"},recursive:{type:"boolean",description:"Include subdirectories recursively (default: false)"}},handler:async e=>(await t.list(e.directory,e.recursive)).map(n=>({path:n.path,name:n.name,size:n.size,isDirectory:n.isDirectory,modifiedAt:n.modifiedAt}))},{name:"files.search",description:"Search for text patterns across files in a directory",parameters:{directory:{type:"string",required:!0,description:"Directory to search in"},pattern:{type:"string",required:!0,description:"Search pattern (regex supported)"},limit:{type:"number",description:"Max results (default: 20)"}},handler:async e=>t.search(e.directory,e.pattern,{limit:e.limit??20})}]}function Ze(t){return[{name:"schedule.create",description:"Create a scheduled task. Supports one-time (runAt) or recurring (cron) schedules",parameters:{name:{type:"string",required:!0,description:"Schedule name/description"},cron:{type:"string",description:"Cron expression for recurring (e.g. '0 9 * * *')"},runAt:{type:"string",description:"ISO 8601 datetime for one-time execution"},payload:{type:"object",required:!0,description:"Data to pass to the scheduled task"}},requiredPermissions:["schedule:write"],handler:async e=>{let r=await t.create({name:e.name,cron:e.cron,runAt:e.runAt,payload:e.payload??{}});return{id:r.id,name:r.name,status:r.status,cron:r.cron,runAt:r.runAt}}},{name:"schedule.list",description:"List scheduled tasks, optionally filtered by status",parameters:{status:{type:"string",description:"Filter by: active | paused | cancelled | completed"}},handler:async e=>(await t.list(e.status)).map(n=>({id:n.id,name:n.name,status:n.status,cron:n.cron,runAt:n.runAt,createdAt:n.createdAt}))},{name:"schedule.cancel",description:"Cancel a scheduled task by ID",parameters:{id:{type:"string",required:!0,description:"Schedule ID to cancel"}},requiredPermissions:["schedule:write"],handler:async e=>({cancelled:await t.cancel(e.id),id:e.id})}]}function et(t){return[{name:"tasks.plan",description:"Create a task plan by decomposing a goal into actionable steps",parameters:{goal:{type:"string",required:!0,description:"High-level goal to accomplish"},context:{type:"string",description:"Additional context or constraints"}},handler:async e=>t.plan(e.goal,e.context)},{name:"tasks.update",description:"Update the status of a step within a task plan",parameters:{planId:{type:"string",required:!0,description:"Task plan ID"},stepId:{type:"string",required:!0,description:"Step ID within the plan"},status:{type:"string",required:!0,description:"pending | in_progress | done | skipped"},result:{type:"string",description:"Result or notes for this step"}},handler:async e=>t.update(e.planId,e.stepId,e.status,e.result)},{name:"tasks.list",description:"List task plans, optionally filtered by status",parameters:{status:{type:"string",description:"Filter by: planning | active | completed | cancelled"}},handler:async e=>t.list(e.status)},{name:"tasks.complete",description:"Mark a task plan as completed with a summary",parameters:{planId:{type:"string",required:!0,description:"Task plan ID to complete"},summary:{type:"string",required:!0,description:"Completion summary"}},handler:async e=>t.complete(e.planId,e.summary)}]}function tt(t){return[{name:"browser.navigate",description:"Navigate to a URL and return the page title and content summary",parameters:{url:{type:"string",required:!0,description:"The URL to navigate to"}},handler:async e=>t.navigate(e.url)},{name:"browser.click",description:"Click an element on the current page by CSS selector",parameters:{selector:{type:"string",required:!0,description:"CSS selector of element to click"}},handler:async e=>t.click(e.selector)},{name:"browser.type",description:"Fill an input field on the current page",parameters:{selector:{type:"string",required:!0,description:"CSS selector of the input field"},text:{type:"string",required:!0,description:"Text to type into the field"}},handler:async e=>t.type(e.selector,e.text)},{name:"browser.extract",description:"Extract cleaned page content (strips nav, ads, scripts). Optionally scope to a CSS selector",parameters:{selector:{type:"string",description:"Optional CSS selector to scope extraction"}},handler:async e=>t.extract(e.selector)},{name:"browser.screenshot",description:"Capture a screenshot of the current viewport as base64 PNG for vision analysis",handler:async()=>t.screenshot()}]}var D={maxRestartAttempts:3,baseCooldownMs:5e3,backoffMultiplier:3,maxCooldownMs:3e5,healthCheckIntervalMs:3e4},te=class{sidecars=new Map;startFn;healthCheckFn;pendingEvents=[];constructor(e,r){this.startFn=e,this.healthCheckFn=r}add(e){this.sidecars.set(e.id,{config:e,state:"stopped",restartAttempts:0,cooldownCycle:0,cooldownUntilMs:0,cooldownTimer:null,healthTimer:null})}async*start(e,r){let n=this.sidecars.get(e);return n?(n.state="starting",await this.startFn(n.config.command)?(n.state="running",n.restartAttempts=0,n.cooldownCycle=0,n.cooldownUntilMs=0,yield{type:"voice:sidecar:started",timestamp:Date.now(),sessionId:r,provider:n.config.id,endpoint:n.config.healthEndpoint},this.startHealthLoop(n,r),!0):(yield*this.handleFailure(n,r),n.state==="running")):!1}inFailureCooldown(e){let r=this.sidecars.get(e);return r?r.state==="cooldown"&&Date.now()<r.cooldownUntilMs:!1}cooldownUntil(e){return this.sidecars.get(e)?.cooldownUntilMs??0}async*markFailure(e,r){let n=this.sidecars.get(e);n&&(yield*this.handleFailure(n,r))}async*clearFailure(e,r){let n=this.sidecars.get(e);return n?(n.cooldownTimer&&(clearTimeout(n.cooldownTimer),n.cooldownTimer=null),n.restartAttempts=0,n.cooldownCycle=0,n.cooldownUntilMs=0,yield*this.start(e,r)):!1}activeFallback(e){let r=this.sidecars.get(e);return!r||r.state!=="cooldown"?null:r.config.fallbackProvider??null}getState(e){return this.sidecars.get(e)?.state??null}stop(e){let r=this.sidecars.get(e);r&&(r.cooldownTimer&&(clearTimeout(r.cooldownTimer),r.cooldownTimer=null),r.healthTimer&&(clearTimeout(r.healthTimer),r.healthTimer=null),r.state="stopped")}stopAll(){for(let e of this.sidecars.keys())this.stop(e)}drainEvents(){let e=[...this.pendingEvents];return this.pendingEvents=[],e}async*handleFailure(e,r){let n=e.config.maxRestartAttempts??D.maxRestartAttempts;for(;e.restartAttempts<n;)if(e.restartAttempts++,e.state="restarting",yield{type:"voice:sidecar:crashed",timestamp:Date.now(),sessionId:r,provider:e.config.id,error:`Restart attempt ${e.restartAttempts}/${n}`,restartAttempt:e.restartAttempts},await this.startFn(e.config.command)){e.state="running",e.restartAttempts=0,e.cooldownCycle=0,e.cooldownUntilMs=0,yield{type:"voice:sidecar:started",timestamp:Date.now(),sessionId:r,provider:e.config.id,endpoint:e.config.healthEndpoint},this.startHealthLoop(e,r);return}yield*this.enterCooldown(e,r)}async*enterCooldown(e,r){let n=e.config.baseCooldownMs??D.baseCooldownMs,o=e.config.backoffMultiplier??D.backoffMultiplier,s=e.config.maxCooldownMs??D.maxCooldownMs,i=Math.min(n*Math.pow(o,e.cooldownCycle),s);e.state="cooldown",e.cooldownUntilMs=Date.now()+i,yield{type:"sidecar:cooldown",timestamp:Date.now(),sessionId:r,provider:e.config.id,cooldownUntil:e.cooldownUntilMs,attempts:e.restartAttempts},e.cooldownTimer=setTimeout(()=>{e.cooldownTimer=null,this.probeAfterCooldown(e,r)},i)}async probeAfterCooldown(e,r){if(!await this.healthCheckFn(e.config.healthEndpoint)&&!await this.startFn(e.config.command)){e.cooldownCycle++,e.restartAttempts=0;let s=e.config.baseCooldownMs??D.baseCooldownMs,i=e.config.backoffMultiplier??D.backoffMultiplier,a=e.config.maxCooldownMs??D.maxCooldownMs,c=Math.min(s*Math.pow(i,e.cooldownCycle),a);e.cooldownUntilMs=Date.now()+c,this.pendingEvents.push({type:"sidecar:cooldown",timestamp:Date.now(),sessionId:r,provider:e.config.id,cooldownUntil:e.cooldownUntilMs,attempts:e.restartAttempts}),e.cooldownTimer=setTimeout(()=>{e.cooldownTimer=null,this.probeAfterCooldown(e,r)},c);return}e.state="running",e.restartAttempts=0,e.cooldownCycle=0,e.cooldownUntilMs=0,this.pendingEvents.push({type:"voice:sidecar:started",timestamp:Date.now(),sessionId:r,provider:e.config.id,endpoint:e.config.healthEndpoint}),this.startHealthLoop(e,r)}startHealthLoop(e,r){e.healthTimer&&clearTimeout(e.healthTimer);let n=e.config.healthCheckIntervalMs??D.healthCheckIntervalMs,o=async()=>{if(e.state!=="running")return;if(!await this.healthCheckFn(e.config.healthEndpoint)&&e.state==="running"){e.healthTimer=null,this.pendingEvents.push({type:"voice:sidecar:crashed",timestamp:Date.now(),sessionId:r,provider:e.config.id,error:"Health check failed",restartAttempt:0}),this.probeAfterCooldown(e,r);return}e.state==="running"&&(e.healthTimer=setTimeout(o,n))};e.healthTimer=setTimeout(o,n)}};var ne=class{queue=[];playing=null;listener;onEvent(e){this.listener=e}enqueue(e){if(e.interruptMode==="force"){this.forceInterrupt(e);return}if(e.interruptMode==="soft"&&this.playing&&e.priority>this.playing.priority){this.queue.unshift(e),this.emit("speech:queued",e);return}let r=this.queue.findIndex(n=>n.priority<e.priority);r===-1?this.queue.push(e):this.queue.splice(r,0,e),this.emit("speech:queued",e)}forceInterrupt(e){this.playing&&this.emit("speech:interrupted",this.playing),this.playing=e,this.emit("speech:started",e)}interrupt(e){if(this.playing?.id===e)return this.emit("speech:interrupted",this.playing),this.playing=null,this.advance(),!0;let r=this.queue.findIndex(n=>n.id===e);return r>=0?(this.queue.splice(r,1),!0):!1}completeCurrent(){this.playing&&(this.emit("speech:completed",this.playing),this.playing=null),this.advance()}current(){return this.playing}pending(){return[...this.queue]}get size(){return(this.playing?1:0)+this.queue.length}flush(){this.queue.length=0}advance(){if(this.playing)return;let e=this.queue.shift();e&&(this.playing=e,this.emit("speech:started",e))}emit(e,r){this.listener?.(e,r)}};var Bt={happy:{intensity:.7,speed:1.05},sad:{intensity:.6,speed:.9},excited:{intensity:.9,speed:1.15},angry:{intensity:.8,speed:1.1},thoughtful:{intensity:.4,speed:.85},calm:{intensity:.3,speed:.95},nervous:{intensity:.6,speed:1.1},whisper:{intensity:.2,speed:.8}},jt=/\[([a-zA-Z][\w-]*)\]([\s\S]*?)\[\/\1\]/g,jr=/\[([a-zA-Z][\w-]*)\]/g,qr=/\[\/([a-zA-Z][\w-]*)\]/g;function nt(t,e=Bt){let r=[],n=0;for(let o of t.matchAll(jt)){let s=o.index;if(s>n){let l=t.slice(n,s).trim();l&&r.push({text:l})}let i=o[1],a=o[2].trim(),c=e[i];a&&r.push({text:a,emotion:i,intensity:c?.intensity,speed:c?.speed}),n=s+o[0].length}if(n<t.length){let o=t.slice(n).trim();o&&r.push({text:o})}return r.length===0&&t.trim()&&r.push({text:t.trim()}),r}function rt(t){return t.replace(jt,"$2").replace(jr,"").replace(qr,"").replace(/\s{2,}/g," ").trim()}function ot(t,e,r=""){let n=[];if(t==null)return n.push(`${r||"root"}: expected ${e.type}, got ${t}`),n;if(e.type==="object"&&typeof t=="object"&&!Array.isArray(t)){let o=t;if(e.required)for(let s of e.required)s in o||n.push(`${r?r+".":""}${s}: required field missing`);if(e.properties)for(let[s,i]of Object.entries(e.properties))s in o&&n.push(...ot(o[s],i,`${r?r+".":""}${s}`))}else if(e.type==="array"&&Array.isArray(t)){if(e.items)for(let o=0;o<t.length;o++)n.push(...ot(t[o],e.items,`${r}[${o}]`))}else e.type==="string"&&typeof t!="string"?n.push(`${r||"root"}: expected string, got ${typeof t}`):e.type==="number"&&typeof t!="number"?n.push(`${r||"root"}: expected number, got ${typeof t}`):e.type==="boolean"&&typeof t!="boolean"&&n.push(`${r||"root"}: expected boolean, got ${typeof t}`);return e.enum&&!e.enum.includes(t)&&n.push(`${r||"root"}: value ${JSON.stringify(t)} not in enum [${e.enum.join(", ")}]`),n}async function st(t,e,r={}){if(!t.generateObject)throw new Error(`Provider "${t.id}" does not support generateObject`);let n=r.maxRetries??2,o=[],s=0;for(let i=0;i<=n;i++){s++;let a=await t.generateObject(e),c=ot(a,e.schema),l=r.validate?.(a);if(l&&c.push(l),c.length===0)return{data:a,attempts:s,violations:o};o.push(...c.map(u=>`[attempt ${s}] ${u}`)),i<n&&(e={...e,messages:[...e.messages,{role:"assistant",content:JSON.stringify(a)},{role:"user",content:`The previous response had validation errors:
373
+ ${c.join(`
374
+ `)}
375
+
376
+ Please fix and return a valid response.`}]})}throw new ge(`Structured output validation failed after ${s} attempts`,o)}var ge=class extends Error{constructor(r,n){super(r);this.violations=n;this.name="StructuredOutputError"}};var Qt="0.1.0";async function dt(t,e={}){let r=new j,n=new q,o=e.storage??null;if(!o&&(t.databaseUrl||t.storageBackend)){let{createStorage:y}=await Promise.resolve().then(()=>(ut(),Yt));o=y({backend:t.storageBackend??"postgres",connectionString:t.databaseUrl})}let s=new V({enableCostTracking:!0});if(t.providers&&Object.keys(t.providers).length>0)for(let[y,N]of Object.entries(t.providers)){let k=Zt(N);k&&s.register(y,k)}else{let y=await G(),N=[y.chat,y.tts,y.stt].filter(k=>k!==null);for(let k of N){let R=Zt({provider:k.id,model:k.id,apiKey:k.apiKeyEnv?process.env[k.apiKeyEnv]:void 0,baseUrl:k.baseUrl});R&&s.register(k.slot,R)}}let i=new z,a=[Pe,Le,Oe,$e,je,We];for(let y of a)try{y(i)}catch{}try{Me(i)}catch{}try{De(i)}catch{}try{await i.register(Je())}catch{}try{await i.register(Ve({skills:[]}))}catch{}let c=new Y,l=new Q(c),u=t.tools??["*"],p=u.includes("*");e.memoryBackend&&(p||u.includes("memory"))&&c.registerAll(Xe(e.memoryBackend)),e.webBackend&&(p||u.includes("web"))&&c.registerAll(ze(e.webBackend)),e.codeBackend&&(p||u.includes("code"))&&c.registerAll(Ye(e.codeBackend)),e.fileBackend&&(p||u.includes("files"))&&c.registerAll(Qe(e.fileBackend)),e.scheduleBackend&&(p||u.includes("schedule"))&&c.registerAll(Ze(e.scheduleBackend)),e.taskBackend&&(p||u.includes("tasks"))&&c.registerAll(et(e.taskBackend)),e.browserEngine&&(p||u.includes("browser"))&&c.registerAll(tt(e.browserEngine));let m=new te(async y=>!1,async y=>!1),b=new ne,f=null,P;if(t.enablePreClassification){let y=s.getHealthy("utility")??s.getHealthy("chat");y&&(P=Ie(y))}let w=t.systemPrompt??`You are Fenix, an AI agent (${t.edition} edition). You can use tools to help the user.`,C=new Set(t.readOnlyTools??["memory.search","memory.list","memory.context","web.search","web.fetch","files.read","files.list","files.search","tasks.list"]);async function*M(y,N){yield*_e({providers:s,toolRouter:l,extensions:i,sessionId:y,maxTurns:t.maxTurns??20,tokenBudget:t.tokenBudget??128e3,contextWindowTokens:t.contextWindowTokens??2e5,systemPrompt:w,emitBlocks:t.emitBlocks??!1,readOnlyTools:C,classifyQuery:P},N)}function T(){let N=["chat","utility","embedding","vision","stt","tts"].map(R=>({slot:R,healthy:s.getHealthy(R)!==null}));return{status:N.some(R=>R.healthy)?"ok":"degraded",edition:t.edition,version:Qt,providers:N,storage:o!==null,extensions:i.listExtensions().length,tools:c.list().length}}async function F(){m.stopAll();for(let y of i.listExtensions())try{await i.unregister(y)}catch{}}return{edition:t.edition,version:Qt,config:t,bus:r,sessions:n,providers:s,extensions:i,toolRegistry:c,toolRouter:l,storage:o,voice:{sidecarManager:m,speechQueue:b,parseMarkers:nt,stripMarkers:rt},memory:{progressiveSearch:f},verifiedGenerateObject:st,sendMessage:M,health:T,stop:F}}function Zt(t){switch(t.provider){case"anthropic":return new X({apiKey:t.apiKey??process.env.ANTHROPIC_API_KEY??"",model:t.model});case"openrouter":return new I({apiKey:t.apiKey??process.env.OPENROUTER_API_KEY??"",baseUrl:t.baseUrl??"https://openrouter.ai/api/v1",model:t.model,id:`openrouter:${t.model}`});case"ollama":return new I({apiKey:"",baseUrl:t.baseUrl??"http://localhost:11434/v1",model:t.model,id:`ollama:${t.model}`});case"groq":return new I({apiKey:t.apiKey??process.env.GROQ_API_KEY??"",baseUrl:t.baseUrl??"https://api.groq.com/openai/v1",model:t.model,id:`groq:${t.model}`});case"openai":return new I({apiKey:t.apiKey??process.env.OPENAI_API_KEY??"",baseUrl:t.baseUrl??"https://api.openai.com/v1",model:t.model,id:`openai:${t.model}`});case"nvidia":return new I({apiKey:t.apiKey??process.env.NVIDIA_API_KEY??"",baseUrl:t.baseUrl??"https://integrate.api.nvidia.com/v1",model:t.model,id:`nvidia:${t.model}`});case"cloudflare":return new I({apiKey:t.apiKey??process.env.CLOUDFLARE_API_TOKEN??"",baseUrl:t.baseUrl??"https://api.cloudflare.com/client/v4/accounts",model:t.model,id:`cloudflare:${t.model}`});case"github":return new I({apiKey:t.apiKey??process.env.GITHUB_TOKEN??"",baseUrl:t.baseUrl??"https://models.inference.ai.azure.com",model:t.model,id:`github:${t.model}`});default:return t.baseUrl?new I({apiKey:t.apiKey??"",baseUrl:t.baseUrl,model:t.model,id:`${t.provider}:${t.model}`}):null}}var Ia=new TextEncoder;ut();ye();ve();var no={plan:{filename:"plan.md",template:["# Task Plan","","## Objective","<!-- Describe the current goal -->","","## Checklist","- [ ] Step 1","","## Notes",""].join(`
377
+ `)},observations:{filename:"observations.md",template:["# Observations","","Key observations accumulated this session:","","- ",""].join(`
378
+ `)},context:{filename:"context.md",template:["# Critical Context","","Context that must survive truncation:","","## User Preferences","","## Active Constraints","","## Key Decisions",""].join(`
379
+ `)}};var sn="0.1.0";function an(t){let e=new j,r=new q;return{edition:t.edition,version:sn,config:t,bus:e,sessions:r}}var cn={name:"fenix-voices",channels:["telegram","http-api","websocket","voice-telnyx","cli"],tools:["memory","web","files","schedule","curriculum","exercises","assessment","content-pipeline"],storage:"postgres",extensions:["default/*","voices/*"],features:{sandbox:!0,subagents:!0,teamMemory:!0,observability:!0,skillMarketplace:!1,teacherLayer:!0,teachingLoop:!0,contentPipeline:!0}};var io=`You are an autonomous language teacher powered by the Fenix Voices platform.
2
380
  Your goal is to help learners achieve fluency through structured, adaptive lessons.
3
- Be patient, encouraging, and culturally sensitive.`,H=`## Available Teacher Tools
381
+ Be patient, encouraging, and culturally sensitive.`,ao=`## Available Teacher Tools
4
382
  - **plan_lesson** \u2014 Create a lesson plan mixing new curriculum material with spaced-repetition reviews.
5
383
  - **placement_test** \u2014 Run an adaptive placement test to determine a learner's CEFR level.
6
384
  - **curriculum** \u2014 Browse and manage curriculum templates and units.
7
385
  - **exercises** \u2014 Generate exercises (vocabulary drill, fill-in-blank, translation, listening, pronunciation, dialogue roleplay, etc.).
8
386
  - **assessment** \u2014 Evaluate exercise responses, generate feedback, and produce session summaries.
9
- - **content-pipeline** \u2014 Import, tag, and manage corpus content (CSV, JSON, or web scraping via Scrapthing MCP).`,W=`## Learner Interaction Guidelines
387
+ - **content-pipeline** \u2014 Import, tag, and manage corpus content (CSV, JSON, or web scraping via Scrapthing MCP).`,co=`## Learner Interaction Guidelines
10
388
  - Use the learner's target language progressively: start with simple phrases and increase as they advance.
11
389
  - Match your language complexity to the learner's CEFR level (pre-A1 through C2).
12
390
  - For levels pre-A1 to A2: use the bridge/native language for explanations, target language for practice.
13
391
  - For levels B1+: conduct most interaction in the target language.
14
392
  - Celebrate milestones and streaks to keep motivation high.
15
- - When the learner asks a general question, respond conversationally \u2014 only call plan_lesson when starting a structured practice session.`,G=`## When to Use Tools
393
+ - When the learner asks a general question, respond conversationally \u2014 only call plan_lesson when starting a structured practice session.`,lo=`## When to Use Tools
16
394
  - **Starting a session**: Call plan_lesson to generate a structured lesson.
17
395
  - **First interaction with a new learner**: Call placement_test to assess their level.
18
396
  - **Learner submits an answer**: Use assessment to evaluate and provide feedback.
19
397
  - **Casual conversation**: Respond naturally without tool calls \u2014 use the target language when appropriate.
20
- - **Learner asks about their progress**: Retrieve stats from teacher memory.`;function B(){return[j,H,W,G].join(`
398
+ - **Learner asks about their progress**: Retrieve stats from teacher memory.`;function uo(){return[io,ao,co,lo].join(`
21
399
 
22
- `)}function L(e,t){let r=B();return t==="standalone"||!e?r:`${e}
400
+ `)}function ln(t,e){let r=uo();return e==="standalone"||!t?r:`${t}
23
401
 
24
402
  ---
25
403
 
26
- ${r}`}import{sql as m}from"kysely";async function d(e,t){let r=await m`
27
- SELECT * FROM learner_profiles WHERE id = ${t}
28
- `.execute(e);return r.rows[0]?w(r.rows[0]):null}async function v(e,t,r){let a=[],n=[];if(r.currentLevel!==void 0&&(a.push("current_level = $"+(n.length+1)),n.push(r.currentLevel)),r.bridgeLanguage!==void 0&&(a.push("bridge_language = $"+(n.length+1)),n.push(r.bridgeLanguage)),r.goals!==void 0&&(a.push("goals = $"+(n.length+1)+"::jsonb"),n.push(JSON.stringify(r.goals))),r.preferences!==void 0&&(a.push("preferences = $"+(n.length+1)+"::jsonb"),n.push(JSON.stringify(r.preferences))),r.stats!==void 0&&(a.push("stats = $"+(n.length+1)+"::jsonb"),n.push(JSON.stringify(r.stats))),a.length===0)return d(e,t);let s=await m`
404
+ ${r}`}import{sql as ie}from"kysely";async function pt(t,e){let r=await ie`
405
+ SELECT * FROM learner_profiles WHERE id = ${e}
406
+ `.execute(t);return r.rows[0]?pn(r.rows[0]):null}async function un(t,e,r){let n=[],o=[];if(r.currentLevel!==void 0&&(n.push("current_level = $"+(o.length+1)),o.push(r.currentLevel)),r.bridgeLanguage!==void 0&&(n.push("bridge_language = $"+(o.length+1)),o.push(r.bridgeLanguage)),r.goals!==void 0&&(n.push("goals = $"+(o.length+1)+"::jsonb"),o.push(JSON.stringify(r.goals))),r.preferences!==void 0&&(n.push("preferences = $"+(o.length+1)+"::jsonb"),o.push(JSON.stringify(r.preferences))),r.stats!==void 0&&(n.push("stats = $"+(o.length+1)+"::jsonb"),o.push(JSON.stringify(r.stats))),n.length===0)return pt(t,e);let s=await ie`
29
407
  UPDATE learner_profiles
30
- SET ${m.raw(a.join(", "))}, updated_at = now()
31
- WHERE id = ${t}
408
+ SET ${ie.raw(n.join(", "))}, updated_at = now()
409
+ WHERE id = ${e}
32
410
  RETURNING *
33
- `.execute(e);return s.rows[0]?w(s.rows[0]):null}async function S(e,t,r=20){let a=await m`
411
+ `.execute(t);return s.rows[0]?pn(s.rows[0]):null}async function dn(t,e,r=20){let n=await ie`
34
412
  SELECT * FROM vocab_entries
35
- WHERE learner_id = ${t} AND next_review_at <= now()
413
+ WHERE learner_id = ${e} AND next_review_at <= now()
36
414
  ORDER BY next_review_at ASC
37
415
  LIMIT ${r}
38
- `.execute(e),n=await m`
416
+ `.execute(t),o=await ie`
39
417
  SELECT * FROM grammar_entries
40
- WHERE learner_id = ${t} AND next_review_at <= now()
418
+ WHERE learner_id = ${e} AND next_review_at <= now()
41
419
  ORDER BY next_review_at ASC
42
420
  LIMIT ${r}
43
- `.execute(e);return{vocab:a.rows.map(k),grammar:n.rows.map(J)}}function w(e){return{id:e.id,userId:e.user_id,targetLanguage:e.target_language,nativeLanguage:e.native_language,bridgeLanguage:e.bridge_language??void 0,currentLevel:e.current_level,goals:e.goals??[],preferences:e.preferences??{},stats:e.stats??{},createdAt:new Date(e.created_at).toISOString(),updatedAt:new Date(e.updated_at).toISOString()}}function k(e){return{id:e.id,learnerId:e.learner_id,word:e.word,targetLanguage:e.target_language,translation:e.translation,mastery:e.mastery,nextReviewAt:new Date(e.next_review_at).toISOString(),reviewCount:e.review_count,lastReviewedAt:e.last_reviewed_at?new Date(e.last_reviewed_at).toISOString():void 0,context:e.context??void 0}}function J(e){return{id:e.id,learnerId:e.learner_id,concept:e.concept,targetLanguage:e.target_language,mastery:e.mastery,nextReviewAt:new Date(e.next_review_at).toISOString(),reviewCount:e.review_count,lastReviewedAt:e.last_reviewed_at?new Date(e.last_reviewed_at).toISOString():void 0,notes:e.notes??void 0}}import{sql as Z}from"kysely";import{readFileSync as N,readdirSync as Ne,existsSync as R}from"fs";import{join as E}from"path";var X=new Set(["pre-A1","A1","A2","B1","B2","C1","C2"]),Y=/^[a-z]{2,3}$/;function x(e){let t=[];if(!e||typeof e!="object")return t.push({path:"",message:"template must be an object"}),t;let r=e;if((!r.language||typeof r.language!="string"||!Y.test(r.language))&&t.push({path:"language",message:"must be a valid ISO 639 code"}),(!r.version||typeof r.version!="string")&&t.push({path:"version",message:"must be a string"}),!Array.isArray(r.units))return t.push({path:"units",message:"must be an array"}),t;let a=new Set;for(let n=0;n<r.units.length;n++){let s=r.units[n],i=`units[${n}]`;if(!s||typeof s!="object"){t.push({path:i,message:"must be an object"});continue}!s.id||typeof s.id!="string"?t.push({path:`${i}.id`,message:"must be a non-empty string"}):(a.has(s.id)&&t.push({path:`${i}.id`,message:`duplicate unit id: ${s.id}`}),a.add(s.id)),(!s.title||typeof s.title!="string")&&t.push({path:`${i}.title`,message:"must be a non-empty string"}),(!s.level||!X.has(s.level))&&t.push({path:`${i}.level`,message:"must be a valid CEFR level"}),Array.isArray(s.objectives)||t.push({path:`${i}.objectives`,message:"must be an array"}),Array.isArray(s.vocabularyTargets)||t.push({path:`${i}.vocabularyTargets`,message:"must be an array"}),Array.isArray(s.grammarTargets)||t.push({path:`${i}.grammarTargets`,message:"must be an array"}),Array.isArray(s.prerequisiteUnits)||t.push({path:`${i}.prerequisiteUnits`,message:"must be an array"}),(typeof s.estimatedSessions!="number"||s.estimatedSessions<1)&&t.push({path:`${i}.estimatedSessions`,message:"must be a positive number"}),(!s.exerciseTypeWeights||typeof s.exerciseTypeWeights!="object")&&t.push({path:`${i}.exerciseTypeWeights`,message:"must be an object"})}for(let n=0;n<r.units.length;n++){let s=r.units[n];if(Array.isArray(s.prerequisiteUnits))for(let i of s.prerequisiteUnits)a.has(i)||t.push({path:`units[${n}].prerequisiteUnits`,message:`references unknown unit: ${i}`})}return t}var A=E(process.cwd(),"data","curricula");function q(e,t=A){let r=E(t,`${e}.json`);if(!R(r)){let a=E(t,"sample.json");if(R(a))try{let n=N(a,"utf-8"),s=JSON.parse(n);if(s.language===e)return s}catch{}return null}try{let a=N(r,"utf-8"),n=JSON.parse(a);return x(n).length>0?null:n}catch{return null}}async function h(e,t,r=A){let a=await Z`
44
- SELECT template FROM curriculum_templates WHERE language = ${t}
45
- `.execute(e);return a.rows[0]?a.rows[0].template:q(t,r)}import{sql as O}from"kysely";async function T(e,t,r){let n=(await O`
421
+ `.execute(t);return{vocab:n.rows.map(po),grammar:o.rows.map(mo)}}function pn(t){return{id:t.id,userId:t.user_id,targetLanguage:t.target_language,nativeLanguage:t.native_language,bridgeLanguage:t.bridge_language??void 0,currentLevel:t.current_level,goals:t.goals??[],preferences:t.preferences??{},stats:t.stats??{},createdAt:new Date(t.created_at).toISOString(),updatedAt:new Date(t.updated_at).toISOString()}}function po(t){return{id:t.id,learnerId:t.learner_id,word:t.word,targetLanguage:t.target_language,translation:t.translation,mastery:t.mastery,nextReviewAt:new Date(t.next_review_at).toISOString(),reviewCount:t.review_count,lastReviewedAt:t.last_reviewed_at?new Date(t.last_reviewed_at).toISOString():void 0,context:t.context??void 0}}function mo(t){return{id:t.id,learnerId:t.learner_id,concept:t.concept,targetLanguage:t.target_language,mastery:t.mastery,nextReviewAt:new Date(t.next_review_at).toISOString(),reviewCount:t.review_count,lastReviewedAt:t.last_reviewed_at?new Date(t.last_reviewed_at).toISOString():void 0,notes:t.notes??void 0}}import{sql as ho}from"kysely";import{readFileSync as gn,readdirSync as Gm,existsSync as fn}from"fs";import{join as mt}from"path";var go=new Set(["pre-A1","A1","A2","B1","B2","C1","C2"]),fo=/^[a-z]{2,3}$/;function mn(t){let e=[];if(!t||typeof t!="object")return e.push({path:"",message:"template must be an object"}),e;let r=t;if((!r.language||typeof r.language!="string"||!fo.test(r.language))&&e.push({path:"language",message:"must be a valid ISO 639 code"}),(!r.version||typeof r.version!="string")&&e.push({path:"version",message:"must be a string"}),!Array.isArray(r.units))return e.push({path:"units",message:"must be an array"}),e;let n=new Set;for(let o=0;o<r.units.length;o++){let s=r.units[o],i=`units[${o}]`;if(!s||typeof s!="object"){e.push({path:i,message:"must be an object"});continue}!s.id||typeof s.id!="string"?e.push({path:`${i}.id`,message:"must be a non-empty string"}):(n.has(s.id)&&e.push({path:`${i}.id`,message:`duplicate unit id: ${s.id}`}),n.add(s.id)),(!s.title||typeof s.title!="string")&&e.push({path:`${i}.title`,message:"must be a non-empty string"}),(!s.level||!go.has(s.level))&&e.push({path:`${i}.level`,message:"must be a valid CEFR level"}),Array.isArray(s.objectives)||e.push({path:`${i}.objectives`,message:"must be an array"}),Array.isArray(s.vocabularyTargets)||e.push({path:`${i}.vocabularyTargets`,message:"must be an array"}),Array.isArray(s.grammarTargets)||e.push({path:`${i}.grammarTargets`,message:"must be an array"}),Array.isArray(s.prerequisiteUnits)||e.push({path:`${i}.prerequisiteUnits`,message:"must be an array"}),(typeof s.estimatedSessions!="number"||s.estimatedSessions<1)&&e.push({path:`${i}.estimatedSessions`,message:"must be a positive number"}),(!s.exerciseTypeWeights||typeof s.exerciseTypeWeights!="object")&&e.push({path:`${i}.exerciseTypeWeights`,message:"must be an object"})}for(let o=0;o<r.units.length;o++){let s=r.units[o];if(Array.isArray(s.prerequisiteUnits))for(let i of s.prerequisiteUnits)n.has(i)||e.push({path:`units[${o}].prerequisiteUnits`,message:`references unknown unit: ${i}`})}return e}var hn=mt(process.cwd(),"data","curricula");function yo(t,e=hn){let r=mt(e,`${t}.json`);if(!fn(r)){let n=mt(e,"sample.json");if(fn(n))try{let o=gn(n,"utf-8"),s=JSON.parse(o);if(s.language===t)return s}catch{}return null}try{let n=gn(r,"utf-8"),o=JSON.parse(n);return mn(o).length>0?null:o}catch{return null}}async function yn(t,e,r=hn){let n=await ho`
422
+ SELECT template FROM curriculum_templates WHERE language = ${e}
423
+ `.execute(t);return n.rows[0]?n.rows[0].template:yo(e,r)}import{sql as vn}from"kysely";async function gt(t,e,r){let o=(await vn`
46
424
  SELECT * FROM learner_progress
47
- WHERE learner_id = ${t} AND template_language = ${r}
48
- `.execute(e)).rows[0];return n?z(n):null}async function U(e,t,r,a){return await O`
425
+ WHERE learner_id = ${e} AND template_language = ${r}
426
+ `.execute(t)).rows[0];return o?vo(o):null}async function bn(t,e,r,n){return await vn`
49
427
  INSERT INTO learner_progress
50
428
  (learner_id, template_language, current_unit_id, current_session, completed_units, unit_scores)
51
429
  VALUES
52
- (${t}, ${r}, ${a}, 1, '[]'::jsonb, '{}'::jsonb)
430
+ (${e}, ${r}, ${n}, 1, '[]'::jsonb, '{}'::jsonb)
53
431
  ON CONFLICT (learner_id, template_language) DO NOTHING
54
- `.execute(e),await T(e,t,r)}function z(e){return{learnerId:e.learner_id,templateLanguage:e.template_language,completedUnits:e.completed_units??[],currentUnitId:e.current_unit_id,currentSession:e.current_session,unitScores:e.unit_scores??{}}}var Q=10,ee=30;function te(e){return{entryId:e.id,entryType:"vocab",word:e.word,lastScore:e.mastery}}function re(e){return{entryId:e.id,entryType:"grammar",concept:e.concept,lastScore:e.mastery}}function ne(e){return{"pre-A1":.1,A1:.2,A2:.35,B1:.5,B2:.65,C1:.8,C2:.95}[e]??.3}function I(e,t,r=6){let a=[],n=ne(e.level),s=e.exerciseTypeWeights,i=Object.values(s).reduce((o,l)=>o+l,0)||1;for(let[o,l]of Object.entries(s)){let g=Math.max(1,Math.round(l/i*r));for(let c=0;c<g&&a.length<r;c++)a.push({type:o,objective:e.objectives[a.length%e.objectives.length]??e.title,targetVocabulary:e.vocabularyTargets.length>0?e.vocabularyTargets.slice(0,3):void 0,targetGrammar:e.grammarTargets.length>0?e.grammarTargets.slice(0,2):void 0,contentQuery:`${e.title} ${e.level}`,difficulty:n})}return a}function b(e,t=8){return e.slice(0,t).map(r=>({type:r.entryType==="vocab"?"vocabulary_drill":"grammar_exercise",objective:`Review: ${r.word??r.concept??"item"}`,targetVocabulary:r.word?[r.word]:void 0,targetGrammar:r.concept?[r.concept]:void 0,difficulty:Math.max(.1,1-r.lastScore)}))}async function P(e,t){let{db:r}=e,a=await d(r,t);if(!a)throw new Error(`Learner not found: ${t}`);let n=await h(r,a.targetLanguage);if(!n&&e.generateTemplate&&(n=await e.generateTemplate(a.targetLanguage)),!n)throw new Error(`No curriculum template for language: ${a.targetLanguage}`);let s=await T(r,t,a.targetLanguage);if(!s){let u=n.units[0];if(!u)throw new Error("Curriculum has no units");s=await U(r,t,a.targetLanguage,u.id)}let i=n.units.find(u=>u.id===s.currentUnitId);if(!i)throw new Error(`Unit not found: ${s.currentUnitId}`);let o=await S(r,t,20),l=[...o.vocab.map(te),...o.grammar.map(re)],g=a.preferences?.sessionDuration??ee,c;if(l.length>=Q)c=b(l);else{let u=b(l,3),p=I(i,s.currentSession,6-u.length);c=[...u,...p]}return{learnerId:t,unitId:i.id,sessionNumber:s.currentSession,objectives:i.objectives,exercises:c,estimatedDuration:g,reviewItems:l}}var f={name:"plan_lesson",description:"Plan a language lesson for the current learner",handler:async(e,t)=>P(t.deps,e.learnerId)};var se=["pre-A1","A1","A2","B1","B2"],ae=.5,C=2;function $(e){let t="pre-A1";for(let r of e)r.passed&&(t=r.level);return t}function D(e){return e.length<C?!0:!e.slice(-C).every(r=>!r.passed)}async function F(e,t,r){let{db:a,generateExercises:n,scoreExercises:s,getResponses:i}=e,o=[];for(let g of se){if(!D(o))break;let c=await n(g,r);if(c.length===0)continue;let u=await i(c),p=await s(c,u),K=p>=ae;o.push({level:g,score:p,passed:K})}let l=$(o);return await v(a,t,{currentLevel:l}),{learnerId:t,testedLevels:o,assignedLevel:l}}var y={name:"placement_test",description:"Assess learner's current level in target language",handler:async(e,t)=>F(t.deps,e.learnerId,e.language)};var ie=[f,y];function M(e,t="teacher_skills"){for(let r of ie)e.tool(t,r.name,r)}var V="voices",Ze={...ce},ue={edition:V,channels:["telegram","web","voice"],tools:["*"],layers:["memory","knowledge-graph","teacher"]};function qe(e){return oe({...ue,...e})}async function ze(e){return le({edition:V,tools:["*"],maxTurns:20,...e})}var Qe={ttsProvider:"fenix-tts",ttsdProvider:"fenix-ttsd",sttProvider:"groq-whisper",sttProviderClassroom:"speechmatics",turnDetectionPreset:"smart_turn"};async function et(e,t){await t.runMigrations(e.databaseUrl);let r=t.createToolRegistry();M(r);let a=e.port??3333,n=await t.mountApi(a);t.registerMemoryCategories(["teacher:profile","teacher:vocab","teacher:grammar","teacher:session"]);let s=await t.getActiveLearnerIds(),i=await t.startScheduler(s),o=L(void 0,"standalone");return{manifest:_,systemPrompt:o,shutdown:async()=>{i.stop(),n.stop()}}}export{V as EDITION,ke as KERNEL_VERSION,ue as VOICES_KERNEL_CONFIG,Ze as VOICES_MANIFEST,Qe as VOICE_DEFAULTS,le as bootKernel,ze as bootVoices,oe as createKernel,et as initVoicesStandalone,qe as startVoices};
432
+ `.execute(t),await gt(t,e,r)}function vo(t){return{learnerId:t.learner_id,templateLanguage:t.template_language,completedUnits:t.completed_units??[],currentUnitId:t.current_unit_id,currentSession:t.current_session,unitScores:t.unit_scores??{}}}var bo=10,To=30;function Eo(t){return{entryId:t.id,entryType:"vocab",word:t.word,lastScore:t.mastery}}function ko(t){return{entryId:t.id,entryType:"grammar",concept:t.concept,lastScore:t.mastery}}function xo(t){return{"pre-A1":.1,A1:.2,A2:.35,B1:.5,B2:.65,C1:.8,C2:.95}[t]??.3}function En(t,e,r=6){let n=[],o=xo(t.level),s=t.exerciseTypeWeights,i=Object.values(s).reduce((a,c)=>a+c,0)||1;for(let[a,c]of Object.entries(s)){let l=Math.max(1,Math.round(c/i*r));for(let u=0;u<l&&n.length<r;u++)n.push({type:a,objective:t.objectives[n.length%t.objectives.length]??t.title,targetVocabulary:t.vocabularyTargets.length>0?t.vocabularyTargets.slice(0,3):void 0,targetGrammar:t.grammarTargets.length>0?t.grammarTargets.slice(0,2):void 0,contentQuery:`${t.title} ${t.level}`,difficulty:o})}return n}function Tn(t,e=8){return t.slice(0,e).map(r=>({type:r.entryType==="vocab"?"vocabulary_drill":"grammar_exercise",objective:`Review: ${r.word??r.concept??"item"}`,targetVocabulary:r.word?[r.word]:void 0,targetGrammar:r.concept?[r.concept]:void 0,difficulty:Math.max(.1,1-r.lastScore)}))}async function kn(t,e){let{db:r}=t,n=await pt(r,e);if(!n)throw new Error(`Learner not found: ${e}`);let o=await yn(r,n.targetLanguage);if(!o&&t.generateTemplate&&(o=await t.generateTemplate(n.targetLanguage)),!o)throw new Error(`No curriculum template for language: ${n.targetLanguage}`);let s=await gt(r,e,n.targetLanguage);if(!s){let p=o.units[0];if(!p)throw new Error("Curriculum has no units");s=await bn(r,e,n.targetLanguage,p.id)}let i=o.units.find(p=>p.id===s.currentUnitId);if(!i)throw new Error(`Unit not found: ${s.currentUnitId}`);let a=await dn(r,e,20),c=[...a.vocab.map(Eo),...a.grammar.map(ko)],l=n.preferences?.sessionDuration??To,u;if(c.length>=bo)u=Tn(c);else{let p=Tn(c,3),m=En(i,s.currentSession,6-p.length);u=[...p,...m]}return{learnerId:e,unitId:i.id,sessionNumber:s.currentSession,objectives:i.objectives,exercises:u,estimatedDuration:l,reviewItems:c}}var ft={name:"plan_lesson",description:"Plan a language lesson for the current learner",handler:async(t,e)=>kn(e.deps,t.learnerId)};var wo=["pre-A1","A1","A2","B1","B2"],So=.5,xn=2;function wn(t){let e="pre-A1";for(let r of t)r.passed&&(e=r.level);return e}function Sn(t){return t.length<xn?!0:!t.slice(-xn).every(r=>!r.passed)}async function Cn(t,e,r){let{db:n,generateExercises:o,scoreExercises:s,getResponses:i}=t,a=[];for(let l of wo){if(!Sn(a))break;let u=await o(l,r);if(u.length===0)continue;let p=await i(u),m=await s(u,p),b=m>=So;a.push({level:l,score:m,passed:b})}let c=wn(a);return await un(n,e,{currentLevel:c}),{learnerId:e,testedLevels:a,assignedLevel:c}}var ht={name:"placement_test",description:"Assess learner's current level in target language",handler:async(t,e)=>Cn(e.deps,t.learnerId,t.language)};var Co=[ft,ht];function An(t,e="teacher_skills"){for(let r of Co)t.tool(e,r.name,r)}var Rn="voices",yg={...ke},Ao={edition:Rn,channels:["telegram","web","voice"],tools:["*"],layers:["memory","knowledge-graph","teacher"]};function vg(t){return an({...Ao,...t})}async function bg(t){return dt({edition:Rn,tools:["*"],maxTurns:20,...t})}var Tg={ttsProvider:"fenix-tts",ttsdProvider:"fenix-ttsd",sttProvider:"groq-whisper",sttProviderClassroom:"speechmatics",turnDetectionPreset:"smart_turn"};async function Eg(t,e){await e.runMigrations(t.databaseUrl);let r=e.createToolRegistry();An(r);let n=t.port??3333,o=await e.mountApi(n);e.registerMemoryCategories(["teacher:profile","teacher:vocab","teacher:grammar","teacher:session"]);let s=await e.getActiveLearnerIds(),i=await e.startScheduler(s),a=ln(void 0,"standalone");return{manifest:cn,systemPrompt:a,shutdown:async()=>{i.stop(),o.stop()}}}export{Rn as EDITION,sn as KERNEL_VERSION,Ao as VOICES_KERNEL_CONFIG,yg as VOICES_MANIFEST,Tg as VOICE_DEFAULTS,dt as bootKernel,bg as bootVoices,an as createKernel,Eg as initVoicesStandalone,vg as startVoices};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenixforce/edition-voices",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,12 +23,12 @@
23
23
  "dist"
24
24
  ],
25
25
  "scripts": {
26
- "build": "esbuild src/index.ts --bundle --format=esm --platform=node --target=es2022 --minify --outfile=dist/index.js --external:@fenixforce/kernel --external:kysely --external:nanoid && tsc --emitDeclarationOnly --project tsconfig.build.json",
26
+ "build": "esbuild src/index.ts --bundle --format=esm --platform=node --target=es2022 --minify --outfile=dist/index.js --external:pg --external:kysely --external:nanoid && tsc --emitDeclarationOnly --project tsconfig.build.json",
27
27
  "test": "bun test",
28
28
  "typecheck": "tsc --noEmit"
29
29
  },
30
30
  "dependencies": {
31
- "@fenixforce/kernel": "workspace:*",
31
+ "pg": "^8.13.0",
32
32
  "kysely": "^0.27.0",
33
33
  "nanoid": "^5.1.6"
34
34
  },