bazilion 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.
- package/README.md +1 -1
- package/dist/cli.js +12 -12
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +648 -76
- package/dist/daemon.js.map +1 -1
- package/dist/worker.js +409 -63
- package/dist/worker.js.map +1 -1
- package/package.json +13 -13
package/dist/worker.js
CHANGED
|
@@ -79,6 +79,25 @@ import { join as join7 } from "path";
|
|
|
79
79
|
// ../daemon/src/core/services.ts
|
|
80
80
|
var SERVICES = [
|
|
81
81
|
// --- LLM providers (configured via API keys / URLs) ---
|
|
82
|
+
// Top 3: openai-codex (ChatGPT OAuth), openai (API key), anthropic.
|
|
83
|
+
// Everything else in rough popularity order; locals last.
|
|
84
|
+
{
|
|
85
|
+
id: "openai-codex",
|
|
86
|
+
displayName: "OpenAI ChatGPT (OAuth)",
|
|
87
|
+
category: "provider",
|
|
88
|
+
hint: "Use your ChatGPT Plus/Pro/Team account (same login as Codex CLI)",
|
|
89
|
+
// No form fields — credentials come from an OAuth flow. The /config page
|
|
90
|
+
// renders a Connect/Disconnect card using /api/auth/openai instead of the
|
|
91
|
+
// standard field inputs.
|
|
92
|
+
fields: []
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "openai",
|
|
96
|
+
displayName: "OpenAI",
|
|
97
|
+
category: "provider",
|
|
98
|
+
hint: "GPT models \xB7 platform.openai.com",
|
|
99
|
+
fields: [{ envVar: "OPENAI_API_KEY", kind: "secret", label: "API key", placeholder: "sk-..." }]
|
|
100
|
+
},
|
|
82
101
|
{
|
|
83
102
|
id: "anthropic",
|
|
84
103
|
displayName: "Anthropic",
|
|
@@ -94,23 +113,6 @@ var SERVICES = [
|
|
|
94
113
|
}
|
|
95
114
|
]
|
|
96
115
|
},
|
|
97
|
-
{
|
|
98
|
-
id: "openai",
|
|
99
|
-
displayName: "OpenAI",
|
|
100
|
-
category: "provider",
|
|
101
|
-
hint: "GPT models \xB7 platform.openai.com",
|
|
102
|
-
fields: [{ envVar: "OPENAI_API_KEY", kind: "secret", label: "API key", placeholder: "sk-..." }]
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
id: "openai-codex",
|
|
106
|
-
displayName: "OpenAI ChatGPT (OAuth)",
|
|
107
|
-
category: "provider",
|
|
108
|
-
hint: "Use your ChatGPT Plus/Pro/Team account (same login as Codex CLI)",
|
|
109
|
-
// No form fields — credentials come from an OAuth flow. The /config page
|
|
110
|
-
// renders a Connect/Disconnect card using /api/auth/openai instead of the
|
|
111
|
-
// standard field inputs.
|
|
112
|
-
fields: []
|
|
113
|
-
},
|
|
114
116
|
{
|
|
115
117
|
id: "google",
|
|
116
118
|
displayName: "Google (Gemini)",
|
|
@@ -153,6 +155,27 @@ var SERVICES = [
|
|
|
153
155
|
hint: "Authenticates via AWS SDK env (AWS_PROFILE or AWS_ACCESS_KEY_ID/SECRET)",
|
|
154
156
|
fields: []
|
|
155
157
|
},
|
|
158
|
+
{
|
|
159
|
+
id: "github-copilot",
|
|
160
|
+
displayName: "GitHub Copilot",
|
|
161
|
+
category: "provider",
|
|
162
|
+
hint: "Use a GitHub Copilot subscription to call Claude/GPT/Gemini via Copilot",
|
|
163
|
+
fields: [
|
|
164
|
+
{
|
|
165
|
+
envVar: "COPILOT_GITHUB_TOKEN",
|
|
166
|
+
kind: "secret",
|
|
167
|
+
label: "GitHub token",
|
|
168
|
+
description: "Generic GH_TOKEN/GITHUB_TOKEN are ignored \u2014 set this scoped variable explicitly (or run `bazilion auth copilot login` once available)."
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "deepseek",
|
|
174
|
+
displayName: "DeepSeek",
|
|
175
|
+
category: "provider",
|
|
176
|
+
hint: "DeepSeek V4 Flash / Pro \xB7 platform.deepseek.com",
|
|
177
|
+
fields: [{ envVar: "DEEPSEEK_API_KEY", kind: "secret", label: "API key" }]
|
|
178
|
+
},
|
|
156
179
|
{
|
|
157
180
|
id: "mistral",
|
|
158
181
|
displayName: "Mistral",
|
|
@@ -160,6 +183,13 @@ var SERVICES = [
|
|
|
160
183
|
hint: "mistral.ai",
|
|
161
184
|
fields: [{ envVar: "MISTRAL_API_KEY", kind: "secret", label: "API key" }]
|
|
162
185
|
},
|
|
186
|
+
{
|
|
187
|
+
id: "xai",
|
|
188
|
+
displayName: "xAI",
|
|
189
|
+
category: "provider",
|
|
190
|
+
hint: "Grok \xB7 x.ai",
|
|
191
|
+
fields: [{ envVar: "XAI_API_KEY", kind: "secret", label: "API key" }]
|
|
192
|
+
},
|
|
163
193
|
{
|
|
164
194
|
id: "groq",
|
|
165
195
|
displayName: "Groq",
|
|
@@ -174,11 +204,46 @@ var SERVICES = [
|
|
|
174
204
|
fields: [{ envVar: "CEREBRAS_API_KEY", kind: "secret", label: "API key" }]
|
|
175
205
|
},
|
|
176
206
|
{
|
|
177
|
-
id: "
|
|
178
|
-
displayName: "
|
|
207
|
+
id: "fireworks",
|
|
208
|
+
displayName: "Fireworks AI",
|
|
179
209
|
category: "provider",
|
|
180
|
-
hint: "
|
|
181
|
-
fields: [{ envVar: "
|
|
210
|
+
hint: "DeepSeek/GLM/Kimi via fireworks.ai",
|
|
211
|
+
fields: [{ envVar: "FIREWORKS_API_KEY", kind: "secret", label: "API key" }]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "together",
|
|
215
|
+
displayName: "Together AI",
|
|
216
|
+
category: "provider",
|
|
217
|
+
hint: "Open-weight models \xB7 together.ai",
|
|
218
|
+
fields: [{ envVar: "TOGETHER_API_KEY", kind: "secret", label: "API key" }]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: "moonshotai",
|
|
222
|
+
displayName: "Moonshot AI",
|
|
223
|
+
category: "provider",
|
|
224
|
+
hint: "Kimi K2/K2.5/K2.6 \xB7 platform.moonshot.ai",
|
|
225
|
+
fields: [{ envVar: "MOONSHOT_API_KEY", kind: "secret", label: "API key" }]
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: "kimi-coding",
|
|
229
|
+
displayName: "Kimi Coding",
|
|
230
|
+
category: "provider",
|
|
231
|
+
hint: "Coding-tuned Kimi endpoint \xB7 platform.moonshot.cn",
|
|
232
|
+
fields: [{ envVar: "KIMI_API_KEY", kind: "secret", label: "API key" }]
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: "minimax",
|
|
236
|
+
displayName: "MiniMax",
|
|
237
|
+
category: "provider",
|
|
238
|
+
hint: "MiniMax M2 family \xB7 platform.minimaxi.com",
|
|
239
|
+
fields: [{ envVar: "MINIMAX_API_KEY", kind: "secret", label: "API key" }]
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: "xiaomi",
|
|
243
|
+
displayName: "Xiaomi MiMo",
|
|
244
|
+
category: "provider",
|
|
245
|
+
hint: "API billing endpoint \xB7 platform.xiaomimimo.com",
|
|
246
|
+
fields: [{ envVar: "XIAOMI_API_KEY", kind: "secret", label: "API key" }]
|
|
182
247
|
},
|
|
183
248
|
{
|
|
184
249
|
id: "zai",
|
|
@@ -193,6 +258,27 @@ var SERVICES = [
|
|
|
193
258
|
hint: "Inference endpoints \xB7 huggingface.co",
|
|
194
259
|
fields: [{ envVar: "HF_TOKEN", kind: "secret", label: "Access token", placeholder: "hf_..." }]
|
|
195
260
|
},
|
|
261
|
+
{
|
|
262
|
+
id: "cloudflare-ai-gateway",
|
|
263
|
+
displayName: "Cloudflare AI Gateway",
|
|
264
|
+
category: "provider",
|
|
265
|
+
hint: "Per-gateway routing to OpenAI/Anthropic/Workers AI",
|
|
266
|
+
fields: [
|
|
267
|
+
{ envVar: "CLOUDFLARE_API_KEY", kind: "secret", label: "API key" },
|
|
268
|
+
{ envVar: "CLOUDFLARE_ACCOUNT_ID", kind: "config", label: "Account ID" },
|
|
269
|
+
{ envVar: "CLOUDFLARE_GATEWAY_ID", kind: "config", label: "Gateway ID" }
|
|
270
|
+
]
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
id: "cloudflare-workers-ai",
|
|
274
|
+
displayName: "Cloudflare Workers AI",
|
|
275
|
+
category: "provider",
|
|
276
|
+
hint: "Inference on Cloudflare Workers \xB7 ai.cloudflare.com",
|
|
277
|
+
fields: [
|
|
278
|
+
{ envVar: "CLOUDFLARE_API_KEY", kind: "secret", label: "API key" },
|
|
279
|
+
{ envVar: "CLOUDFLARE_ACCOUNT_ID", kind: "config", label: "Account ID" }
|
|
280
|
+
]
|
|
281
|
+
},
|
|
196
282
|
{
|
|
197
283
|
id: "openrouter",
|
|
198
284
|
displayName: "OpenRouter",
|
|
@@ -216,6 +302,13 @@ var SERVICES = [
|
|
|
216
302
|
}
|
|
217
303
|
]
|
|
218
304
|
},
|
|
305
|
+
{
|
|
306
|
+
id: "opencode",
|
|
307
|
+
displayName: "OpenCode",
|
|
308
|
+
category: "provider",
|
|
309
|
+
hint: "OpenAI-compatible proxy from the OpenCode CLI",
|
|
310
|
+
fields: [{ envVar: "OPENCODE_API_KEY", kind: "secret", label: "API key" }]
|
|
311
|
+
},
|
|
219
312
|
{
|
|
220
313
|
id: "lmstudio",
|
|
221
314
|
displayName: "LM Studio",
|
|
@@ -254,11 +347,54 @@ var SERVICES = [
|
|
|
254
347
|
}
|
|
255
348
|
]
|
|
256
349
|
},
|
|
350
|
+
{
|
|
351
|
+
id: "llamacpp",
|
|
352
|
+
displayName: "llama.cpp",
|
|
353
|
+
category: "provider",
|
|
354
|
+
hint: "Local inference \xB7 llama.cpp llama-server (OpenAI-compat /v1 endpoint)",
|
|
355
|
+
fields: [
|
|
356
|
+
{
|
|
357
|
+
envVar: "LLAMACPP_URL",
|
|
358
|
+
kind: "config",
|
|
359
|
+
label: "Endpoint URL",
|
|
360
|
+
placeholder: "http://127.0.0.1:8080/v1"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
envVar: "LLAMACPP_API_KEY",
|
|
364
|
+
kind: "secret",
|
|
365
|
+
label: "API key (only if started with --api-key)",
|
|
366
|
+
description: "llama-server runs without auth by default. Set this only if you launched the server with the `--api-key KEY` flag."
|
|
367
|
+
}
|
|
368
|
+
]
|
|
369
|
+
},
|
|
257
370
|
// --- Ancillary services (web search, etc) ---
|
|
371
|
+
{
|
|
372
|
+
id: "firecrawl",
|
|
373
|
+
displayName: "Firecrawl",
|
|
374
|
+
category: "service",
|
|
375
|
+
group: "Web Search",
|
|
376
|
+
hint: "web_fetch fallback for JS-heavy/blocked pages \xB7 firecrawl.dev (free tier available)",
|
|
377
|
+
fields: [
|
|
378
|
+
{
|
|
379
|
+
envVar: "FIRECRAWL_API_KEY",
|
|
380
|
+
kind: "secret",
|
|
381
|
+
label: "API key",
|
|
382
|
+
placeholder: "fc-...",
|
|
383
|
+
description: "When set, web_fetch automatically falls back to Firecrawl if the primary Readability extraction yields too little content."
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
envVar: "FIRECRAWL_URL",
|
|
387
|
+
kind: "config",
|
|
388
|
+
label: "Base URL (optional, for self-hosted)",
|
|
389
|
+
placeholder: "https://api.firecrawl.dev"
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
},
|
|
258
393
|
{
|
|
259
394
|
id: "brave-search",
|
|
260
395
|
displayName: "Brave Search",
|
|
261
396
|
category: "service",
|
|
397
|
+
group: "Web Search",
|
|
262
398
|
hint: "Web search tool \xB7 free tier at brave.com/search/api/",
|
|
263
399
|
fields: [{ envVar: "BRAVE_API_KEY", kind: "secret", label: "API key", placeholder: "BSA..." }]
|
|
264
400
|
},
|
|
@@ -266,6 +402,7 @@ var SERVICES = [
|
|
|
266
402
|
id: "searxng",
|
|
267
403
|
displayName: "SearXNG",
|
|
268
404
|
category: "service",
|
|
405
|
+
group: "Web Search",
|
|
269
406
|
hint: "Self-hosted meta-search engine \xB7 searxng.org",
|
|
270
407
|
fields: [
|
|
271
408
|
{
|
|
@@ -567,14 +704,14 @@ import {
|
|
|
567
704
|
ModelRegistry,
|
|
568
705
|
SessionManager,
|
|
569
706
|
SettingsManager
|
|
570
|
-
} from "@
|
|
707
|
+
} from "@earendil-works/pi-coding-agent";
|
|
571
708
|
|
|
572
709
|
// ../daemon/src/runtime/providers/pi-adapter.ts
|
|
573
710
|
import {
|
|
574
711
|
getModel,
|
|
575
712
|
streamSimple,
|
|
576
713
|
Type
|
|
577
|
-
} from "@
|
|
714
|
+
} from "@earendil-works/pi-ai";
|
|
578
715
|
function defaultBaseUrlFor(providerName) {
|
|
579
716
|
switch (providerName) {
|
|
580
717
|
case "lmstudio":
|
|
@@ -616,7 +753,7 @@ function resolveModel(cfg, modelId) {
|
|
|
616
753
|
}
|
|
617
754
|
|
|
618
755
|
// ../daemon/src/runtime/auth/openai-codex.ts
|
|
619
|
-
import { loginOpenAICodex, refreshOpenAICodexToken } from "@
|
|
756
|
+
import { loginOpenAICodex, refreshOpenAICodexToken } from "@earendil-works/pi-ai/oauth";
|
|
620
757
|
|
|
621
758
|
// ../daemon/src/runtime/session/prompt.ts
|
|
622
759
|
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
@@ -626,13 +763,11 @@ var CONTEXT_FILE_ORDER = [
|
|
|
626
763
|
"SOUL.md",
|
|
627
764
|
"TOOLS.md",
|
|
628
765
|
"IDENTITY.md",
|
|
629
|
-
"HEARTBEAT.md"
|
|
630
|
-
"BOOTSTRAP.md"
|
|
766
|
+
"HEARTBEAT.md"
|
|
631
767
|
];
|
|
632
768
|
function buildSystemPrompt(agent) {
|
|
633
769
|
const parts = [];
|
|
634
770
|
const contextBlocks = [];
|
|
635
|
-
let bootstrapPresent = false;
|
|
636
771
|
for (const file of CONTEXT_FILE_ORDER) {
|
|
637
772
|
const path = join10(agent.agent.dir, file);
|
|
638
773
|
if (!existsSync9(path)) continue;
|
|
@@ -641,23 +776,39 @@ function buildSystemPrompt(agent) {
|
|
|
641
776
|
contextBlocks.push(`## ${file}
|
|
642
777
|
|
|
643
778
|
${content}`);
|
|
644
|
-
if (file === "BOOTSTRAP.md") bootstrapPresent = true;
|
|
645
779
|
}
|
|
646
780
|
if (contextBlocks.length > 0) {
|
|
647
781
|
parts.push(`# Project Context
|
|
648
782
|
|
|
649
783
|
${contextBlocks.join("\n\n")}`);
|
|
650
784
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
)
|
|
785
|
+
const bootstrapPath = join10(agent.agent.dir, "BOOTSTRAP.md");
|
|
786
|
+
if (existsSync9(bootstrapPath)) {
|
|
787
|
+
const bootstrap = readFileSync7(bootstrapPath, "utf8").trimEnd();
|
|
788
|
+
if (bootstrap) {
|
|
789
|
+
parts.push(
|
|
790
|
+
[
|
|
791
|
+
"# First-Run Ritual",
|
|
792
|
+
"",
|
|
793
|
+
"This is your first session. The document below is **conversational guidance**, not a checklist to execute in one shot. It describes a multi-turn Q&A you should have with the human, one question per turn.",
|
|
794
|
+
"",
|
|
795
|
+
"## Hard rules",
|
|
796
|
+
"- Your first reply is ONLY a greeting + ONE question. No tool calls. Wait for the human to answer.",
|
|
797
|
+
"- Each subsequent turn: at most one new question. Wait between turns.",
|
|
798
|
+
"- Only after the ritual is complete (you have enough to write IDENTITY.md): call `home_write` once, then `bootstrap_done`.",
|
|
799
|
+
"",
|
|
800
|
+
"## BOOTSTRAP.md",
|
|
801
|
+
"",
|
|
802
|
+
bootstrap
|
|
803
|
+
].join("\n")
|
|
804
|
+
);
|
|
805
|
+
}
|
|
655
806
|
}
|
|
656
807
|
parts.push(
|
|
657
808
|
[
|
|
658
809
|
"# Agent Home",
|
|
659
810
|
"",
|
|
660
|
-
"Your private home holds who you are \u2014 identity, soul, behaviour rules, wake-up routine. It is not shared with other agents and cannot be overwritten by them. The files above (IDENTITY.md, SOUL.md, AGENTS.md, TOOLS.md, HEARTBEAT.md, BOOTSTRAP.md
|
|
811
|
+
"Your private home holds who you are \u2014 identity, soul, behaviour rules, wake-up routine. It is not shared with other agents and cannot be overwritten by them. The files above (IDENTITY.md, SOUL.md, AGENTS.md, TOOLS.md, HEARTBEAT.md) live in this home, plus BOOTSTRAP.md when you are still in your first-run ritual.",
|
|
661
812
|
"",
|
|
662
813
|
"- To change who you are (name, vibe, personality, how you behave): use `home_write`.",
|
|
663
814
|
"- To inspect exact wording of your own files: use `home_read` or `home_list`.",
|
|
@@ -684,13 +835,19 @@ You have access to the following skills: ${agent.skills.join(", ")}.`
|
|
|
684
835
|
parts.push(
|
|
685
836
|
`# About the User
|
|
686
837
|
|
|
687
|
-
|
|
838
|
+
Shared context about the human you're working with in this group. Both you and the human curate it. To update: call \`user_md_get\` (returns current content + an etag), merge your change into the full text, then call \`user_md_write\` with the merged content and the etag. Use this for STABLE user-specific facts (preferences, role, working hours, how they like to be addressed) \u2014 and to CORRECT stale entries when the human tells you something different from what's recorded. For project knowledge use \`memory_write\` instead; for personal notes about yourself use \`home_write\` on IDENTITY.md. **Do NOT send peer messages announcing USER.md changes \u2014 every agent in the group sees the new content in their system prompt on their next turn automatically.**
|
|
688
839
|
|
|
689
840
|
${agent.group.userMd.trim()}`
|
|
690
841
|
);
|
|
842
|
+
} else {
|
|
843
|
+
parts.push(
|
|
844
|
+
`# About the User
|
|
845
|
+
|
|
846
|
+
This group's USER.md is empty. As you learn STABLE facts about the human (preferences, role, working hours, how they like to be addressed), populate it via \`user_md_get\` then \`user_md_write\` (always get first \u2014 you need the etag). Reserve this for things you're confident are durable \u2014 project knowledge belongs in \`memory_write\`, personal notes about yourself in \`home_write\` on IDENTITY.md. **Do NOT send peer messages announcing USER.md changes \u2014 every agent in the group sees the new content in their system prompt on their next turn automatically.**`
|
|
847
|
+
);
|
|
691
848
|
}
|
|
692
849
|
parts.push(
|
|
693
|
-
"# Memory\n\nYou share a persistent memory backend with every other agent in this group. Use `memory_write` to remember things across sessions, and `memory_search` / `memory_read` / `memory_list` to recall them. This memory is for project knowledge \u2014 codebase notes, decisions, things the user told you about the work. For personal notes about yourself (preferences, persona quirks), use `home_write` on IDENTITY.md instead. Always check memory at the start of a session: another agent in the group may have already learned something useful."
|
|
850
|
+
"# Memory\n\nYou share a persistent memory backend with every other agent in this group. Use `memory_write` to remember things across sessions, and `memory_search` / `memory_read` / `memory_list` to recall them. This memory is for project knowledge \u2014 codebase notes, decisions, things the user told you about the work. For personal notes about yourself (preferences, persona quirks), use `home_write` on IDENTITY.md instead. Always check memory at the start of a session: another agent in the group may have already learned something useful. **Do NOT send peer messages announcing memory writes \u2014 every agent has access to the same store via `memory_search` and will find your note when they need it.**"
|
|
694
851
|
);
|
|
695
852
|
return parts.join("\n\n---\n\n");
|
|
696
853
|
}
|
|
@@ -831,7 +988,7 @@ function memoryTools(memory) {
|
|
|
831
988
|
{
|
|
832
989
|
def: {
|
|
833
990
|
name: "memory_write",
|
|
834
|
-
description: 'Write or update a memory note in the GROUP-SHARED memory. All agents in this group can read what you write. Use it for project knowledge, codebase notes, decisions, and findings \u2014 anything other agents in the group should benefit from. For personal notes about yourself (preferences, persona)
|
|
991
|
+
description: 'Write or update a memory note in the GROUP-SHARED memory. All agents in this group can read what you write. Use it for project knowledge, codebase notes, decisions, and findings \u2014 anything other agents in the group should benefit from. For personal notes about yourself (preferences, persona) use `home_write` on IDENTITY.md. For STABLE facts about the human you\'re working with (their preferences, role, working hours, how they like to be addressed) use `user_md_get` then `user_md_write` \u2014 those land in every agent\'s system prompt directly. Key is a path-like string with a markdown extension, e.g. "auth-flow.md" or "decisions/2026-05-migration.md".',
|
|
835
992
|
parameters: {
|
|
836
993
|
type: "object",
|
|
837
994
|
properties: {
|
|
@@ -917,7 +1074,7 @@ function messagingTools(host, fromAgentId) {
|
|
|
917
1074
|
{
|
|
918
1075
|
def: {
|
|
919
1076
|
name: "send_message",
|
|
920
|
-
description:
|
|
1077
|
+
description: `Send a message to another agent. Use the recipient's agent id (UUID). Use this ONLY for things the recipient needs to ACT on: delegating a task, asking a peer for information you cannot get yourself, escalating a decision. Do NOT use it for status updates or to announce changes to group-shared resources \u2014 USER.md and the group memory backend both propagate to every agent in the group automatically on their next turn, so messages like "I updated USER.md" or "I wrote a new memory note" are pure noise and will trigger an inbox-wake loop on the recipient.`,
|
|
921
1078
|
parameters: {
|
|
922
1079
|
type: "object",
|
|
923
1080
|
properties: {
|
|
@@ -1021,6 +1178,64 @@ function messagingTools(host, fromAgentId) {
|
|
|
1021
1178
|
];
|
|
1022
1179
|
}
|
|
1023
1180
|
|
|
1181
|
+
// ../daemon/src/runtime/tools/user-md.ts
|
|
1182
|
+
function userMdTools(host, groupId) {
|
|
1183
|
+
return [
|
|
1184
|
+
{
|
|
1185
|
+
def: {
|
|
1186
|
+
name: "user_md_get",
|
|
1187
|
+
description: "Read the group-shared USER.md (facts every agent in the group knows about the human). Returns the current content followed by an `etag:` line \u2014 you MUST pass that etag back as `if_match` on the next `user_md_write` so the daemon can detect concurrent edits by other agents in the group. Always call this immediately before any `user_md_write`.",
|
|
1188
|
+
parameters: {
|
|
1189
|
+
type: "object",
|
|
1190
|
+
properties: {}
|
|
1191
|
+
}
|
|
1192
|
+
},
|
|
1193
|
+
async invoke() {
|
|
1194
|
+
const { content, etag } = await host.get(groupId);
|
|
1195
|
+
const body = content.length > 0 ? content : "(USER.md is empty)";
|
|
1196
|
+
return `${body}
|
|
1197
|
+
|
|
1198
|
+
---
|
|
1199
|
+
etag: ${etag}`;
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
def: {
|
|
1204
|
+
name: "user_md_write",
|
|
1205
|
+
description: "Replace the group-shared USER.md with new content. Use this for STABLE user-specific facts (preferences, role, working hours, how the human likes to be addressed). MANDATORY workflow: (1) call `user_md_get` first, (2) integrate your change into the full content preserving everything unrelated, (3) call `user_md_write` with the merged content and the etag you got from `user_md_get`. If another agent in the group wrote to USER.md between your get and write, this returns an etag-mismatch error \u2014 just call `user_md_get` again and retry the merge. The full result must fit under 12 KB. For project knowledge use `memory_write`; for notes about yourself use `home_write` on IDENTITY.md.",
|
|
1206
|
+
parameters: {
|
|
1207
|
+
type: "object",
|
|
1208
|
+
properties: {
|
|
1209
|
+
content: {
|
|
1210
|
+
type: "string",
|
|
1211
|
+
description: "Full new contents of USER.md (this is a complete replacement, NOT an append). Include everything you want to keep."
|
|
1212
|
+
},
|
|
1213
|
+
if_match: {
|
|
1214
|
+
type: "string",
|
|
1215
|
+
description: "The etag returned by your most recent `user_md_get`. The write fails if USER.md changed in the meantime."
|
|
1216
|
+
}
|
|
1217
|
+
},
|
|
1218
|
+
required: ["content", "if_match"]
|
|
1219
|
+
}
|
|
1220
|
+
},
|
|
1221
|
+
async invoke(args) {
|
|
1222
|
+
const content = String(args.content ?? "");
|
|
1223
|
+
const ifMatch = String(args.if_match ?? "");
|
|
1224
|
+
if (!ifMatch) {
|
|
1225
|
+
throw new Error(
|
|
1226
|
+
'user_md_write: "if_match" is required \u2014 call user_md_get first to obtain the current etag.'
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
const { etag, totalBytes } = await host.write(groupId, content, ifMatch);
|
|
1230
|
+
return `wrote USER.md (${totalBytes} bytes, new etag: ${etag})`;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
];
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// ../daemon/src/runtime/tools/web.ts
|
|
1237
|
+
import { fetch as undiciFetch2 } from "undici";
|
|
1238
|
+
|
|
1024
1239
|
// ../daemon/src/runtime/tools/web-extract.ts
|
|
1025
1240
|
import { Readability } from "@mozilla/readability";
|
|
1026
1241
|
import { parseHTML } from "linkedom";
|
|
@@ -1101,7 +1316,7 @@ function extractReadable(html, url, mode) {
|
|
|
1101
1316
|
// ../daemon/src/runtime/tools/web-ssrf.ts
|
|
1102
1317
|
import { lookup as dnsLookupCb } from "dns";
|
|
1103
1318
|
import { lookup as dnsLookup } from "dns/promises";
|
|
1104
|
-
import { Agent } from "undici";
|
|
1319
|
+
import { Agent, fetch as undiciFetch } from "undici";
|
|
1105
1320
|
var SsrFBlockedError = class extends Error {
|
|
1106
1321
|
constructor(message) {
|
|
1107
1322
|
super(message);
|
|
@@ -1210,7 +1425,7 @@ async function closeDispatcher(d) {
|
|
|
1210
1425
|
}
|
|
1211
1426
|
}
|
|
1212
1427
|
async function guardedFetch(opts) {
|
|
1213
|
-
const fetcher = opts.fetchImpl ??
|
|
1428
|
+
const fetcher = opts.fetchImpl ?? undiciFetch;
|
|
1214
1429
|
const maxRedirects = opts.maxRedirects ?? 3;
|
|
1215
1430
|
const abortController = new AbortController();
|
|
1216
1431
|
const timeoutId = opts.timeoutMs ? setTimeout(() => abortController.abort(new Error("timeout")), opts.timeoutMs) : null;
|
|
@@ -1291,7 +1506,10 @@ var DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_2) AppleWe
|
|
|
1291
1506
|
var DEFAULT_CACHE_TTL_MS = 15 * 6e4;
|
|
1292
1507
|
var DEFAULT_CACHE_MAX = 100;
|
|
1293
1508
|
var DEFAULT_MAX_LENGTH = 2e4;
|
|
1294
|
-
var DEFAULT_TIMEOUT_MS =
|
|
1509
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
1510
|
+
var DEFAULT_MAX_BODY_BYTES = 3 * 1024 * 1024;
|
|
1511
|
+
var FIRECRAWL_FALLBACK_THRESHOLD = 200;
|
|
1512
|
+
var FIRECRAWL_DEFAULT_URL = "https://api.firecrawl.dev";
|
|
1295
1513
|
function stripHtml(s) {
|
|
1296
1514
|
return s.replace(/<[^>]*>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").trim();
|
|
1297
1515
|
}
|
|
@@ -1325,6 +1543,92 @@ async function searxngSearch(query, limit, baseURL, fetchFn) {
|
|
|
1325
1543
|
snippet: r.content ?? ""
|
|
1326
1544
|
}));
|
|
1327
1545
|
}
|
|
1546
|
+
function describeError(err) {
|
|
1547
|
+
if (!(err instanceof Error)) return String(err);
|
|
1548
|
+
const parts = [];
|
|
1549
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1550
|
+
let cur = err;
|
|
1551
|
+
while (cur instanceof Error && !seen.has(cur)) {
|
|
1552
|
+
seen.add(cur);
|
|
1553
|
+
const code = cur.code;
|
|
1554
|
+
parts.push(code ? `[${code}] ${cur.message}` : cur.message);
|
|
1555
|
+
cur = cur.cause;
|
|
1556
|
+
}
|
|
1557
|
+
return parts.join(" \u2014 cause: ");
|
|
1558
|
+
}
|
|
1559
|
+
async function readBodyCapped(res, maxBytes) {
|
|
1560
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
1561
|
+
const charset = /charset=([^;]+)/i.exec(ct)?.[1]?.trim().toLowerCase() || "utf-8";
|
|
1562
|
+
const decoder = new TextDecoder(charset, { fatal: false });
|
|
1563
|
+
const reader = res.body?.getReader();
|
|
1564
|
+
if (!reader) {
|
|
1565
|
+
const text = await res.text();
|
|
1566
|
+
if (text.length > maxBytes) return { text: text.slice(0, maxBytes), truncated: true };
|
|
1567
|
+
return { text, truncated: false };
|
|
1568
|
+
}
|
|
1569
|
+
const chunks = [];
|
|
1570
|
+
let total = 0;
|
|
1571
|
+
let truncated = false;
|
|
1572
|
+
while (true) {
|
|
1573
|
+
const { value, done } = await reader.read();
|
|
1574
|
+
if (done) break;
|
|
1575
|
+
if (!value) continue;
|
|
1576
|
+
if (total + value.byteLength > maxBytes) {
|
|
1577
|
+
const keep = maxBytes - total;
|
|
1578
|
+
if (keep > 0) chunks.push(value.subarray(0, keep));
|
|
1579
|
+
truncated = true;
|
|
1580
|
+
try {
|
|
1581
|
+
await reader.cancel();
|
|
1582
|
+
} catch {
|
|
1583
|
+
}
|
|
1584
|
+
break;
|
|
1585
|
+
}
|
|
1586
|
+
chunks.push(value);
|
|
1587
|
+
total += value.byteLength;
|
|
1588
|
+
}
|
|
1589
|
+
const sum = chunks.reduce((s, c) => s + c.byteLength, 0);
|
|
1590
|
+
const merged = new Uint8Array(sum);
|
|
1591
|
+
let off = 0;
|
|
1592
|
+
for (const c of chunks) {
|
|
1593
|
+
merged.set(c, off);
|
|
1594
|
+
off += c.byteLength;
|
|
1595
|
+
}
|
|
1596
|
+
return { text: decoder.decode(merged), truncated };
|
|
1597
|
+
}
|
|
1598
|
+
async function firecrawlScrape(url, mode, env, fetchFn, timeoutMs) {
|
|
1599
|
+
const apiKey = env.FIRECRAWL_API_KEY;
|
|
1600
|
+
if (!apiKey) return null;
|
|
1601
|
+
const base = (env.FIRECRAWL_URL ?? FIRECRAWL_DEFAULT_URL).replace(/\/$/, "");
|
|
1602
|
+
const ac = new AbortController();
|
|
1603
|
+
const t = setTimeout(() => ac.abort(new Error("firecrawl timeout")), timeoutMs);
|
|
1604
|
+
try {
|
|
1605
|
+
const res = await fetchFn(`${base}/v1/scrape`, {
|
|
1606
|
+
method: "POST",
|
|
1607
|
+
headers: {
|
|
1608
|
+
"content-type": "application/json",
|
|
1609
|
+
authorization: `Bearer ${apiKey}`,
|
|
1610
|
+
accept: "application/json"
|
|
1611
|
+
},
|
|
1612
|
+
body: JSON.stringify({
|
|
1613
|
+
url,
|
|
1614
|
+
formats: ["markdown"],
|
|
1615
|
+
onlyMainContent: true
|
|
1616
|
+
}),
|
|
1617
|
+
signal: ac.signal
|
|
1618
|
+
});
|
|
1619
|
+
if (!res.ok) return null;
|
|
1620
|
+
const body = await res.json();
|
|
1621
|
+
if (!body.success || !body.data?.markdown) return null;
|
|
1622
|
+
const md = body.data.markdown;
|
|
1623
|
+
const title = body.data.metadata?.title;
|
|
1624
|
+
const text = mode === "text" ? markdownToPlain(md) : md;
|
|
1625
|
+
return title ? { text, title } : { text };
|
|
1626
|
+
} catch {
|
|
1627
|
+
return null;
|
|
1628
|
+
} finally {
|
|
1629
|
+
clearTimeout(t);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1328
1632
|
function cacheGet(cache, key) {
|
|
1329
1633
|
const entry = cache.get(key);
|
|
1330
1634
|
if (!entry) return null;
|
|
@@ -1345,12 +1649,14 @@ function cacheSet(cache, key, value, ttlMs, maxEntries) {
|
|
|
1345
1649
|
}
|
|
1346
1650
|
}
|
|
1347
1651
|
function webTools(opts) {
|
|
1348
|
-
const fetchFn = opts?.fetchImpl ??
|
|
1652
|
+
const fetchFn = opts?.fetchImpl ?? undiciFetch2;
|
|
1349
1653
|
const env = opts?.env ?? process.env;
|
|
1350
1654
|
const allowPrivate = opts?.allowPrivate ?? false;
|
|
1351
1655
|
const cacheTtlMs = opts?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
1352
1656
|
const cacheMax = opts?.cacheMax ?? DEFAULT_CACHE_MAX;
|
|
1353
1657
|
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1658
|
+
const maxBodyBytes = opts?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
1659
|
+
const firecrawlDisabled = opts?.firecrawlDisabled ?? false;
|
|
1354
1660
|
const cache = /* @__PURE__ */ new Map();
|
|
1355
1661
|
return [
|
|
1356
1662
|
{
|
|
@@ -1388,7 +1694,7 @@ function webTools(opts) {
|
|
|
1388
1694
|
{
|
|
1389
1695
|
def: {
|
|
1390
1696
|
name: "web_fetch",
|
|
1391
|
-
description: "Fetch a URL and return its readable content. HTML is extracted via Readability and converted to markdown. Results are cached for 15 minutes.",
|
|
1697
|
+
description: "Fetch a URL and return its readable content. HTML is extracted via Readability and converted to markdown. When the primary extraction returns near-empty content from a 2xx HTML page (typical of JS-only shells), the tool automatically retries via Firecrawl if FIRECRAWL_API_KEY is configured. Results are cached for 15 minutes.",
|
|
1392
1698
|
parameters: {
|
|
1393
1699
|
type: "object",
|
|
1394
1700
|
properties: {
|
|
@@ -1414,7 +1720,7 @@ function webTools(opts) {
|
|
|
1414
1720
|
const cacheKey = `${mode}|${url}`;
|
|
1415
1721
|
const cached = cacheGet(cache, cacheKey);
|
|
1416
1722
|
if (cached) return formatOutput(cached, maxLen);
|
|
1417
|
-
let result;
|
|
1723
|
+
let result = null;
|
|
1418
1724
|
try {
|
|
1419
1725
|
result = await guardedFetch({
|
|
1420
1726
|
url,
|
|
@@ -1429,18 +1735,14 @@ function webTools(opts) {
|
|
|
1429
1735
|
}
|
|
1430
1736
|
}
|
|
1431
1737
|
});
|
|
1432
|
-
} catch (err) {
|
|
1433
|
-
if (err instanceof SsrFBlockedError) throw new Error(`web_fetch: ${err.message}`);
|
|
1434
|
-
throw err;
|
|
1435
|
-
}
|
|
1436
|
-
try {
|
|
1437
1738
|
if (!result.response.ok) {
|
|
1438
|
-
throw new Error(
|
|
1739
|
+
throw new Error(`${result.response.status} ${result.response.statusText}`);
|
|
1439
1740
|
}
|
|
1440
1741
|
const ct = result.response.headers.get("content-type") ?? "";
|
|
1441
|
-
const
|
|
1742
|
+
const isHtml = ct.includes("text/html") || ct.includes("xhtml");
|
|
1743
|
+
const { text: body, truncated } = await readBodyCapped(result.response, maxBodyBytes);
|
|
1442
1744
|
let extracted;
|
|
1443
|
-
if (
|
|
1745
|
+
if (isHtml) {
|
|
1444
1746
|
extracted = extractReadable(body, result.finalUrl, mode);
|
|
1445
1747
|
} else if (ct.includes("application/json")) {
|
|
1446
1748
|
try {
|
|
@@ -1451,10 +1753,38 @@ function webTools(opts) {
|
|
|
1451
1753
|
} else {
|
|
1452
1754
|
extracted = { text: body };
|
|
1453
1755
|
}
|
|
1756
|
+
if (isHtml && !firecrawlDisabled && extracted.text.length < FIRECRAWL_FALLBACK_THRESHOLD) {
|
|
1757
|
+
const rescued = await firecrawlScrape(
|
|
1758
|
+
result.finalUrl,
|
|
1759
|
+
mode,
|
|
1760
|
+
env,
|
|
1761
|
+
fetchFn,
|
|
1762
|
+
timeoutMs
|
|
1763
|
+
);
|
|
1764
|
+
if (rescued) {
|
|
1765
|
+
extracted = {
|
|
1766
|
+
...rescued,
|
|
1767
|
+
text: `${rescued.text}
|
|
1768
|
+
|
|
1769
|
+
[content rendered via Firecrawl fallback \u2014 primary extraction returned ${extracted.text.length} chars]`
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
if (truncated) {
|
|
1774
|
+
extracted = {
|
|
1775
|
+
...extracted,
|
|
1776
|
+
text: `${extracted.text}
|
|
1777
|
+
|
|
1778
|
+
[raw body truncated at ${maxBodyBytes} bytes before extraction \u2014 page exceeded the size cap]`
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1454
1781
|
cacheSet(cache, cacheKey, extracted, cacheTtlMs, cacheMax);
|
|
1455
1782
|
return formatOutput(extracted, maxLen);
|
|
1783
|
+
} catch (err) {
|
|
1784
|
+
if (err instanceof SsrFBlockedError) throw new Error(`web_fetch: ${err.message}`);
|
|
1785
|
+
throw new Error(`web_fetch: ${describeError(err)}`);
|
|
1456
1786
|
} finally {
|
|
1457
|
-
await result.release();
|
|
1787
|
+
if (result) await result.release();
|
|
1458
1788
|
}
|
|
1459
1789
|
}
|
|
1460
1790
|
}
|
|
@@ -1501,13 +1831,16 @@ function createBazilionCustomTools(opts) {
|
|
|
1501
1831
|
if (opts.messagingHost) {
|
|
1502
1832
|
handlers.push(...messagingTools(opts.messagingHost, opts.agent.agent.id));
|
|
1503
1833
|
}
|
|
1834
|
+
if (opts.userMdHost) {
|
|
1835
|
+
handlers.push(...userMdTools(opts.userMdHost, opts.agent.group.id));
|
|
1836
|
+
}
|
|
1504
1837
|
return handlers.map(ourToolToPiTool);
|
|
1505
1838
|
}
|
|
1506
1839
|
|
|
1507
1840
|
// ../daemon/src/runtime/pi/session.ts
|
|
1508
1841
|
var BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
1509
1842
|
async function createBazilionSession(opts) {
|
|
1510
|
-
const { agent, paths, env, memory, enabledProviders, messagingHost, refreshApiKey } = opts;
|
|
1843
|
+
const { agent, paths, env, memory, enabledProviders, messagingHost, userMdHost, refreshApiKey } = opts;
|
|
1511
1844
|
const { providerName, modelId } = splitModelString(agent.model);
|
|
1512
1845
|
if (enabledProviders.size > 0 && !enabledProviders.has(providerName)) {
|
|
1513
1846
|
throw new Error(`${providerName} provider is disabled \u2014 enable it on the /config page`);
|
|
@@ -1544,12 +1877,17 @@ async function createBazilionSession(opts) {
|
|
|
1544
1877
|
const sessionManager = existing ? SessionManager.open(existing, sessionDir, cwd) : SessionManager.create(cwd, sessionDir);
|
|
1545
1878
|
const settingsManager = SettingsManager.inMemory({
|
|
1546
1879
|
compaction: { enabled: false },
|
|
1547
|
-
retry: {
|
|
1880
|
+
retry: {
|
|
1881
|
+
enabled: true,
|
|
1882
|
+
maxRetries: 2,
|
|
1883
|
+
baseDelayMs: 500,
|
|
1884
|
+
provider: { maxRetryDelayMs: 8e3 }
|
|
1885
|
+
}
|
|
1548
1886
|
});
|
|
1549
1887
|
const bazilionPrompt = buildSystemPrompt(agent);
|
|
1550
1888
|
const resourceLoader = createBazilionResourceLoader(bazilionPrompt);
|
|
1551
1889
|
await resourceLoader.reload();
|
|
1552
|
-
const customTools = createBazilionCustomTools({ agent, memory, messagingHost, env });
|
|
1890
|
+
const customTools = createBazilionCustomTools({ agent, memory, messagingHost, userMdHost, env });
|
|
1553
1891
|
const allowedTools = [...BUILTIN_TOOL_NAMES, ...customTools.map((t) => t.name)];
|
|
1554
1892
|
const { session } = await createAgentSession({
|
|
1555
1893
|
cwd,
|
|
@@ -1709,7 +2047,7 @@ async function readInput() {
|
|
|
1709
2047
|
}
|
|
1710
2048
|
return parsed;
|
|
1711
2049
|
}
|
|
1712
|
-
function
|
|
2050
|
+
function createIpcClient() {
|
|
1713
2051
|
const pending = /* @__PURE__ */ new Map();
|
|
1714
2052
|
process.on("message", (msg) => {
|
|
1715
2053
|
if (!isIpcReply(msg)) return;
|
|
@@ -1723,17 +2061,14 @@ function createIpcMessagingHost() {
|
|
|
1723
2061
|
for (const [, entry] of pending) entry.reject(new Error("worker IPC channel disconnected"));
|
|
1724
2062
|
pending.clear();
|
|
1725
2063
|
});
|
|
1726
|
-
|
|
2064
|
+
return (method, args) => {
|
|
1727
2065
|
if (!process.send) {
|
|
1728
2066
|
return Promise.reject(new Error("worker: no IPC channel \u2014 daemon must spawn with stdio:ipc"));
|
|
1729
2067
|
}
|
|
1730
2068
|
const id = randomUUID5();
|
|
1731
2069
|
const message = { type: "rpc", id, method, args };
|
|
1732
2070
|
return new Promise((resolve3, reject) => {
|
|
1733
|
-
pending.set(id, {
|
|
1734
|
-
resolve: (v) => resolve3(v),
|
|
1735
|
-
reject
|
|
1736
|
-
});
|
|
2071
|
+
pending.set(id, { resolve: (v) => resolve3(v), reject });
|
|
1737
2072
|
process.send?.(message, void 0, void 0, (err) => {
|
|
1738
2073
|
if (err) {
|
|
1739
2074
|
pending.delete(id);
|
|
@@ -1741,7 +2076,9 @@ function createIpcMessagingHost() {
|
|
|
1741
2076
|
}
|
|
1742
2077
|
});
|
|
1743
2078
|
});
|
|
1744
|
-
}
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2081
|
+
function createIpcMessagingHost(call) {
|
|
1745
2082
|
return {
|
|
1746
2083
|
agentExists: (agentId) => call("agentExists", { agentId }),
|
|
1747
2084
|
sendMessage: (input) => call("sendMessage", input),
|
|
@@ -1752,6 +2089,12 @@ function createIpcMessagingHost() {
|
|
|
1752
2089
|
findReplies: (agentId, replyTo) => call("findReplies", { agentId, replyTo })
|
|
1753
2090
|
};
|
|
1754
2091
|
}
|
|
2092
|
+
function createIpcUserMdHost(call) {
|
|
2093
|
+
return {
|
|
2094
|
+
get: (groupId) => call("userMdGet", { groupId }),
|
|
2095
|
+
write: (groupId, content, ifMatch) => call("userMdWrite", { groupId, content, ifMatch })
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
1755
2098
|
function isIpcReply(msg) {
|
|
1756
2099
|
if (!msg || typeof msg !== "object") return false;
|
|
1757
2100
|
const m = msg;
|
|
@@ -1778,7 +2121,9 @@ async function main() {
|
|
|
1778
2121
|
const paths = resolvePaths();
|
|
1779
2122
|
const memory = qmdBackend(join14(agent.group.path, "memory"));
|
|
1780
2123
|
await memory.init();
|
|
1781
|
-
const
|
|
2124
|
+
const ipcCall = createIpcClient();
|
|
2125
|
+
const messagingHost = createIpcMessagingHost(ipcCall);
|
|
2126
|
+
const userMdHost = createIpcUserMdHost(ipcCall);
|
|
1782
2127
|
const { session, dispose } = await createBazilionSession({
|
|
1783
2128
|
agent,
|
|
1784
2129
|
paths,
|
|
@@ -1786,6 +2131,7 @@ async function main() {
|
|
|
1786
2131
|
memory,
|
|
1787
2132
|
enabledProviders: new Set(enabledProviders),
|
|
1788
2133
|
messagingHost,
|
|
2134
|
+
userMdHost,
|
|
1789
2135
|
apiKey
|
|
1790
2136
|
});
|
|
1791
2137
|
abortSession = () => {
|