bazilion 0.2.1 → 0.4.0
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/dist/cli.js +644 -115
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +41788 -4649
- package/dist/daemon.js.map +1 -1
- package/dist/migrations/0003_agent_telegram.sql +40 -0
- package/dist/migrations/0004_agent_mirror_mode.sql +17 -0
- package/dist/migrations/0005_group_topic_name_format.sql +15 -0
- package/dist/migrations/0006_telegram_acl.sql +18 -0
- package/dist/migrations/0007_mcp_servers.sql +30 -0
- package/dist/worker.js +403 -26
- package/dist/worker.js.map +1 -1
- package/package.json +11 -10
package/dist/worker.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// ../daemon/src/runtime/worker/entry.ts
|
|
4
|
-
import { randomUUID as
|
|
4
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
5
5
|
import { join as join14 } from "path";
|
|
6
6
|
|
|
7
7
|
// ../daemon/src/core/agent/delete.ts
|
|
@@ -418,6 +418,80 @@ var SERVICES = [
|
|
|
418
418
|
placeholder: "https://searxng.example.com"
|
|
419
419
|
}
|
|
420
420
|
]
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
id: "browser",
|
|
424
|
+
displayName: "Browser Automation",
|
|
425
|
+
category: "service",
|
|
426
|
+
group: "Browser",
|
|
427
|
+
hint: "Playwright-driven browser tools (navigate, snapshot, click, screenshot). Run `pnpm exec playwright install chromium` once.",
|
|
428
|
+
fields: [
|
|
429
|
+
{
|
|
430
|
+
envVar: "BROWSER_ENABLED",
|
|
431
|
+
kind: "config",
|
|
432
|
+
label: "Enable browser tools",
|
|
433
|
+
placeholder: "true",
|
|
434
|
+
description: "Expose the browser_* tools to agents (true/false). Default true."
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
envVar: "BROWSER_HEADLESS",
|
|
438
|
+
kind: "config",
|
|
439
|
+
label: "Headless",
|
|
440
|
+
placeholder: "true",
|
|
441
|
+
description: "Run Chromium headless (true/false). Default true."
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
envVar: "BROWSER_ALLOW_PRIVATE_NETWORK",
|
|
445
|
+
kind: "config",
|
|
446
|
+
label: "Allow private network",
|
|
447
|
+
placeholder: "false",
|
|
448
|
+
description: "Permit the browser to reach loopback/private IPs (SSRF guard off). Default false \u2014 only enable for local dev."
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
envVar: "BROWSER_IDLE_MS",
|
|
452
|
+
kind: "config",
|
|
453
|
+
label: "Idle timeout (ms)",
|
|
454
|
+
placeholder: "900000",
|
|
455
|
+
description: "Close an idle browser session after this many ms. Default 900000 (15 min)."
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
envVar: "BROWSER_MAX_SESSIONS",
|
|
459
|
+
kind: "config",
|
|
460
|
+
label: "Max concurrent sessions",
|
|
461
|
+
placeholder: "4",
|
|
462
|
+
description: "Cap on simultaneously-open browser sessions (LRU-evicted). Default 4."
|
|
463
|
+
}
|
|
464
|
+
]
|
|
465
|
+
},
|
|
466
|
+
// --- External integrations (chat bridges, etc) ---
|
|
467
|
+
// Each integration has its own dedicated /config/integrations/* page with
|
|
468
|
+
// workflow-specific UI (preflight health, setup wizard, …). The fields
|
|
469
|
+
// here exist so the keys are allowlisted in the config/secrets stores and
|
|
470
|
+
// surfaced through the generic `PUT /api/config/fields/:envVar` endpoint.
|
|
471
|
+
// Daemon-managed internal state keys (watermarks, derived topic ids) live
|
|
472
|
+
// in repos/config.ts:INTERNAL_CONFIG_KEYS instead, since the user never
|
|
473
|
+
// edits them.
|
|
474
|
+
{
|
|
475
|
+
id: "telegram",
|
|
476
|
+
displayName: "Telegram",
|
|
477
|
+
category: "integration",
|
|
478
|
+
hint: "Forum-supergroup bot for talking to your agents from a phone",
|
|
479
|
+
fields: [
|
|
480
|
+
{
|
|
481
|
+
envVar: "TELEGRAM_BOT_TOKEN",
|
|
482
|
+
kind: "secret",
|
|
483
|
+
label: "Bot token",
|
|
484
|
+
placeholder: "1234567890:ABC...",
|
|
485
|
+
description: "Get one from @BotFather \u2192 /newbot. Disable Privacy Mode in Bot Settings."
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
envVar: "TELEGRAM_CHAT_ID",
|
|
489
|
+
kind: "config",
|
|
490
|
+
label: "Supergroup chat ID",
|
|
491
|
+
placeholder: "-1001234567890",
|
|
492
|
+
description: "Numeric id of the forum-enabled supergroup the bot is admin in."
|
|
493
|
+
}
|
|
494
|
+
]
|
|
421
495
|
}
|
|
422
496
|
];
|
|
423
497
|
var FIELD_INDEX = (() => {
|
|
@@ -431,22 +505,35 @@ var FIELD_INDEX = (() => {
|
|
|
431
505
|
})();
|
|
432
506
|
|
|
433
507
|
// ../daemon/src/core/repos/config.ts
|
|
434
|
-
var
|
|
435
|
-
|
|
436
|
-
|
|
508
|
+
var INTERNAL_CONFIG_KEYS = [
|
|
509
|
+
// Telegram (step 2+): polling watermark + derived topic/message ids.
|
|
510
|
+
"TELEGRAM_LAST_UPDATE_ID",
|
|
511
|
+
"TELEGRAM_SERVICE_TOPIC_ID",
|
|
512
|
+
"TELEGRAM_DIRECTORY_MESSAGE_ID",
|
|
513
|
+
// Phase 5: proposed new chat id after a migrate_to_chat_id event, pending
|
|
514
|
+
// operator confirmation via POST /api/config/telegram/reconnect.
|
|
515
|
+
"TELEGRAM_MIGRATED_CHAT_ID"
|
|
516
|
+
];
|
|
517
|
+
var CONFIG_KEYS = [
|
|
518
|
+
...SERVICES.flatMap((s) => s.fields.filter((f) => f.kind === "config").map((f) => f.envVar)),
|
|
519
|
+
...INTERNAL_CONFIG_KEYS
|
|
520
|
+
];
|
|
437
521
|
var CONFIG_KEY_SET = new Set(CONFIG_KEYS);
|
|
438
522
|
|
|
439
|
-
// ../daemon/src/core/repos/
|
|
523
|
+
// ../daemon/src/core/repos/mcpServers.ts
|
|
440
524
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
441
525
|
|
|
526
|
+
// ../daemon/src/core/repos/messages.ts
|
|
527
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
528
|
+
|
|
442
529
|
// ../daemon/src/core/repos/secrets.ts
|
|
443
530
|
import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from "crypto";
|
|
444
531
|
|
|
445
532
|
// ../daemon/src/core/repos/triggers.ts
|
|
446
|
-
import { randomUUID as
|
|
533
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
447
534
|
|
|
448
535
|
// ../daemon/src/core/repos/webTokens.ts
|
|
449
|
-
import { createHash, randomBytes as randomBytes2, randomUUID as
|
|
536
|
+
import { createHash, randomBytes as randomBytes2, randomUUID as randomUUID5 } from "crypto";
|
|
450
537
|
|
|
451
538
|
// ../daemon/src/core/secrets.ts
|
|
452
539
|
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
@@ -626,7 +713,16 @@ function translatePiEvent(e) {
|
|
|
626
713
|
if (e.isError) {
|
|
627
714
|
return [{ type: "tool_error", id: e.toolCallId, name: e.toolName, error: text }];
|
|
628
715
|
}
|
|
629
|
-
|
|
716
|
+
const images = extractToolResultImages(e.result);
|
|
717
|
+
return [
|
|
718
|
+
{
|
|
719
|
+
type: "tool_result",
|
|
720
|
+
id: e.toolCallId,
|
|
721
|
+
name: e.toolName,
|
|
722
|
+
result: text,
|
|
723
|
+
...images.length > 0 ? { images } : {}
|
|
724
|
+
}
|
|
725
|
+
];
|
|
630
726
|
}
|
|
631
727
|
default:
|
|
632
728
|
return [];
|
|
@@ -657,6 +753,20 @@ function extractToolResultText(result) {
|
|
|
657
753
|
}
|
|
658
754
|
return out;
|
|
659
755
|
}
|
|
756
|
+
function extractToolResultImages(result) {
|
|
757
|
+
const r = result;
|
|
758
|
+
if (!r?.content) return [];
|
|
759
|
+
const out = [];
|
|
760
|
+
for (const block of r.content) {
|
|
761
|
+
if (block.type === "image") {
|
|
762
|
+
const b = block;
|
|
763
|
+
if (typeof b.data === "string" && typeof b.mimeType === "string") {
|
|
764
|
+
out.push({ data: b.data, mimeType: b.mimeType });
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return out;
|
|
769
|
+
}
|
|
660
770
|
function stringifyContent(content) {
|
|
661
771
|
if (typeof content === "string") return content;
|
|
662
772
|
if (!Array.isArray(content)) return "";
|
|
@@ -671,7 +781,13 @@ function piMessagesToProviderView(messages) {
|
|
|
671
781
|
for (const m of messages) {
|
|
672
782
|
switch (m.role) {
|
|
673
783
|
case "user": {
|
|
674
|
-
|
|
784
|
+
const content = m.content;
|
|
785
|
+
const images = extractToolResultImages({ content });
|
|
786
|
+
out.push({
|
|
787
|
+
role: "user",
|
|
788
|
+
content: stringifyContent(content),
|
|
789
|
+
...images.length > 0 ? { images } : {}
|
|
790
|
+
});
|
|
675
791
|
break;
|
|
676
792
|
}
|
|
677
793
|
case "assistant": {
|
|
@@ -685,11 +801,13 @@ function piMessagesToProviderView(messages) {
|
|
|
685
801
|
}
|
|
686
802
|
case "toolResult": {
|
|
687
803
|
const tr = m;
|
|
804
|
+
const images = extractToolResultImages({ content: tr.content });
|
|
688
805
|
out.push({
|
|
689
806
|
role: "tool",
|
|
690
807
|
content: stringifyContent(tr.content),
|
|
691
808
|
toolCallId: tr.toolCallId,
|
|
692
|
-
toolName: tr.toolName
|
|
809
|
+
toolName: tr.toolName,
|
|
810
|
+
...images.length > 0 ? { images } : {}
|
|
693
811
|
});
|
|
694
812
|
break;
|
|
695
813
|
}
|
|
@@ -701,8 +819,8 @@ function piMessagesToProviderView(messages) {
|
|
|
701
819
|
}
|
|
702
820
|
|
|
703
821
|
// ../daemon/src/runtime/pi/session.ts
|
|
704
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync7, statSync as
|
|
705
|
-
import { basename as
|
|
822
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
823
|
+
import { basename as basename3, join as join13 } from "path";
|
|
706
824
|
import {
|
|
707
825
|
AuthStorage,
|
|
708
826
|
createAgentSession,
|
|
@@ -882,8 +1000,195 @@ function bootstrapTool(agentDir) {
|
|
|
882
1000
|
};
|
|
883
1001
|
}
|
|
884
1002
|
|
|
1003
|
+
// ../daemon/src/runtime/tools/browser.ts
|
|
1004
|
+
var REF_DESC = 'Element ref from the latest browser_snapshot, e.g. "e5".';
|
|
1005
|
+
function browserTools(host, agentId) {
|
|
1006
|
+
const proxy = (name, action, description, properties, required = []) => ({
|
|
1007
|
+
def: {
|
|
1008
|
+
name,
|
|
1009
|
+
description,
|
|
1010
|
+
parameters: { type: "object", properties, required, additionalProperties: false }
|
|
1011
|
+
},
|
|
1012
|
+
invoke: (args) => host.invoke(agentId, action, args)
|
|
1013
|
+
});
|
|
1014
|
+
return [
|
|
1015
|
+
proxy(
|
|
1016
|
+
"browser_navigate",
|
|
1017
|
+
"navigate",
|
|
1018
|
+
"Open a URL in the browser and return an accessibility snapshot of the loaded page. Starts a persistent browser session that survives across turns (cookies/logins carry over).",
|
|
1019
|
+
{ url: { type: "string", description: "Absolute URL to navigate to." } },
|
|
1020
|
+
["url"]
|
|
1021
|
+
),
|
|
1022
|
+
proxy(
|
|
1023
|
+
"browser_snapshot",
|
|
1024
|
+
"snapshot",
|
|
1025
|
+
'Capture the current page as an accessibility tree (YAML) with [ref=eN] element references. This is the primary way to "see" the page \u2014 prefer it over screenshots. Use the refs with browser_click/type/etc.',
|
|
1026
|
+
{}
|
|
1027
|
+
),
|
|
1028
|
+
proxy(
|
|
1029
|
+
"browser_click",
|
|
1030
|
+
"click",
|
|
1031
|
+
"Click an element identified by its snapshot ref. Returns a fresh snapshot.",
|
|
1032
|
+
{ ref: { type: "string", description: REF_DESC } },
|
|
1033
|
+
["ref"]
|
|
1034
|
+
),
|
|
1035
|
+
proxy(
|
|
1036
|
+
"browser_type",
|
|
1037
|
+
"type",
|
|
1038
|
+
"Type text into an input/textarea identified by its ref. Set submit=true to press Enter afterwards. Returns a fresh snapshot.",
|
|
1039
|
+
{
|
|
1040
|
+
ref: { type: "string", description: REF_DESC },
|
|
1041
|
+
text: { type: "string", description: "Text to type into the field." },
|
|
1042
|
+
submit: { type: "boolean", description: "Press Enter after typing." }
|
|
1043
|
+
},
|
|
1044
|
+
["ref", "text"]
|
|
1045
|
+
),
|
|
1046
|
+
proxy(
|
|
1047
|
+
"browser_hover",
|
|
1048
|
+
"hover",
|
|
1049
|
+
"Hover the pointer over an element identified by its ref. Returns a fresh snapshot.",
|
|
1050
|
+
{ ref: { type: "string", description: REF_DESC } },
|
|
1051
|
+
["ref"]
|
|
1052
|
+
),
|
|
1053
|
+
proxy(
|
|
1054
|
+
"browser_select",
|
|
1055
|
+
"select",
|
|
1056
|
+
"Select option(s) in a <select> element identified by its ref.",
|
|
1057
|
+
{
|
|
1058
|
+
ref: { type: "string", description: REF_DESC },
|
|
1059
|
+
values: {
|
|
1060
|
+
type: "array",
|
|
1061
|
+
items: { type: "string" },
|
|
1062
|
+
description: "Option values to select."
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
["ref", "values"]
|
|
1066
|
+
),
|
|
1067
|
+
proxy(
|
|
1068
|
+
"browser_fill_form",
|
|
1069
|
+
"fill_form",
|
|
1070
|
+
"Fill multiple form fields in one call. Each field is { ref, value }.",
|
|
1071
|
+
{
|
|
1072
|
+
fields: {
|
|
1073
|
+
type: "array",
|
|
1074
|
+
description: "Fields to fill.",
|
|
1075
|
+
items: {
|
|
1076
|
+
type: "object",
|
|
1077
|
+
properties: {
|
|
1078
|
+
ref: { type: "string", description: REF_DESC },
|
|
1079
|
+
value: { type: "string" }
|
|
1080
|
+
},
|
|
1081
|
+
required: ["ref", "value"]
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
["fields"]
|
|
1086
|
+
),
|
|
1087
|
+
proxy(
|
|
1088
|
+
"browser_press_key",
|
|
1089
|
+
"press_key",
|
|
1090
|
+
'Press a keyboard key (e.g. "Enter", "Escape", "ArrowDown", "Control+A").',
|
|
1091
|
+
{ key: { type: "string", description: "Key or chord to press." } },
|
|
1092
|
+
["key"]
|
|
1093
|
+
),
|
|
1094
|
+
proxy(
|
|
1095
|
+
"browser_go_back",
|
|
1096
|
+
"go_back",
|
|
1097
|
+
"Navigate back to the previous page in history. Returns a fresh snapshot.",
|
|
1098
|
+
{}
|
|
1099
|
+
),
|
|
1100
|
+
proxy(
|
|
1101
|
+
"browser_tabs",
|
|
1102
|
+
"tabs",
|
|
1103
|
+
'Manage tabs. op="list" lists open tabs; "new" opens a tab (optional url); "select" focuses tab at index; "close" closes tab at index (defaults to active).',
|
|
1104
|
+
{
|
|
1105
|
+
op: { type: "string", enum: ["list", "new", "select", "close"] },
|
|
1106
|
+
index: { type: "number", description: "Tab index for select/close." },
|
|
1107
|
+
url: { type: "string", description: "URL to open for op=new." }
|
|
1108
|
+
},
|
|
1109
|
+
["op"]
|
|
1110
|
+
),
|
|
1111
|
+
proxy(
|
|
1112
|
+
"browser_take_screenshot",
|
|
1113
|
+
"take_screenshot",
|
|
1114
|
+
"Take a PNG screenshot of the current page (returned as an image). Use only for visual verification or canvas/pixel content the accessibility snapshot cannot represent \u2014 prefer browser_snapshot for interaction.",
|
|
1115
|
+
{ full_page: { type: "boolean", description: "Capture the full scrollable page." } }
|
|
1116
|
+
),
|
|
1117
|
+
proxy(
|
|
1118
|
+
"browser_console",
|
|
1119
|
+
"console",
|
|
1120
|
+
"Return recent browser console messages (logs, warnings, errors).",
|
|
1121
|
+
{}
|
|
1122
|
+
),
|
|
1123
|
+
proxy(
|
|
1124
|
+
"browser_network",
|
|
1125
|
+
"network",
|
|
1126
|
+
"Return recent network requests issued by the page (method, status, URL).",
|
|
1127
|
+
{}
|
|
1128
|
+
)
|
|
1129
|
+
];
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// ../daemon/src/runtime/tools/deliver-file.ts
|
|
1133
|
+
import { readFileSync as readFileSync8, statSync as statSync4 } from "fs";
|
|
1134
|
+
import { basename as basename2, extname, isAbsolute, resolve as resolve3 } from "path";
|
|
1135
|
+
var MAX_DELIVER_BYTES = 25 * 1024 * 1024;
|
|
1136
|
+
var MIME = {
|
|
1137
|
+
".pdf": "application/pdf",
|
|
1138
|
+
".txt": "text/plain",
|
|
1139
|
+
".md": "text/markdown",
|
|
1140
|
+
".csv": "text/csv",
|
|
1141
|
+
".json": "application/json",
|
|
1142
|
+
".html": "text/html",
|
|
1143
|
+
".zip": "application/zip",
|
|
1144
|
+
".png": "image/png",
|
|
1145
|
+
".jpg": "image/jpeg",
|
|
1146
|
+
".jpeg": "image/jpeg",
|
|
1147
|
+
".gif": "image/gif",
|
|
1148
|
+
".webp": "image/webp"
|
|
1149
|
+
};
|
|
1150
|
+
function deliverFileTool(cwd, sink) {
|
|
1151
|
+
return {
|
|
1152
|
+
def: {
|
|
1153
|
+
name: "deliver_file",
|
|
1154
|
+
description: "Send a file from your workspace to the user so they can download it (web), receive it as a document (Telegram), or save it (CLI). Use this to deliver reports, exports, or any artifact you produced. Max 25 MB.",
|
|
1155
|
+
parameters: {
|
|
1156
|
+
type: "object",
|
|
1157
|
+
properties: {
|
|
1158
|
+
path: {
|
|
1159
|
+
type: "string",
|
|
1160
|
+
description: "Path to the file, relative to your workspace or absolute."
|
|
1161
|
+
}
|
|
1162
|
+
},
|
|
1163
|
+
required: ["path"],
|
|
1164
|
+
additionalProperties: false
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
async invoke(args) {
|
|
1168
|
+
const p = String(args.path ?? "");
|
|
1169
|
+
if (!p) throw new Error("deliver_file: path is required");
|
|
1170
|
+
const abs = isAbsolute(p) ? p : resolve3(cwd, p);
|
|
1171
|
+
let size;
|
|
1172
|
+
try {
|
|
1173
|
+
size = statSync4(abs).size;
|
|
1174
|
+
} catch {
|
|
1175
|
+
throw new Error(`deliver_file: no such file: ${p}`);
|
|
1176
|
+
}
|
|
1177
|
+
if (size > MAX_DELIVER_BYTES) {
|
|
1178
|
+
throw new Error(
|
|
1179
|
+
`deliver_file: "${basename2(abs)}" is too large (${(size / 1024 / 1024).toFixed(1)} MB > 25 MB)`
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
const name = basename2(abs);
|
|
1183
|
+
const mimeType = MIME[extname(abs).toLowerCase()] ?? "application/octet-stream";
|
|
1184
|
+
sink({ name, mimeType, data: readFileSync8(abs).toString("base64") });
|
|
1185
|
+
return `Delivered "${name}" (${mimeType}) to the user.`;
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
|
|
885
1190
|
// ../daemon/src/runtime/tools/home.ts
|
|
886
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
1191
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
887
1192
|
import { join as join12 } from "path";
|
|
888
1193
|
var HOME_FILES_READABLE = [
|
|
889
1194
|
"IDENTITY.md",
|
|
@@ -923,7 +1228,7 @@ function homeTools(agentDir) {
|
|
|
923
1228
|
}
|
|
924
1229
|
const path = join12(agentDir, file);
|
|
925
1230
|
try {
|
|
926
|
-
return
|
|
1231
|
+
return readFileSync9(path, "utf8");
|
|
927
1232
|
} catch (err) {
|
|
928
1233
|
const msg = err instanceof Error ? err.message : String(err);
|
|
929
1234
|
throw new Error(`home_read: could not read ${file}: ${msg}`);
|
|
@@ -967,7 +1272,7 @@ function homeTools(agentDir) {
|
|
|
967
1272
|
for (const file of HOME_FILES_READABLE) {
|
|
968
1273
|
const path = join12(agentDir, file);
|
|
969
1274
|
try {
|
|
970
|
-
const s =
|
|
1275
|
+
const s = statSync5(path);
|
|
971
1276
|
entries.push(`${file} (${s.size}b)`);
|
|
972
1277
|
} catch {
|
|
973
1278
|
}
|
|
@@ -988,6 +1293,18 @@ function homeTools(agentDir) {
|
|
|
988
1293
|
];
|
|
989
1294
|
}
|
|
990
1295
|
|
|
1296
|
+
// ../daemon/src/runtime/tools/mcp.ts
|
|
1297
|
+
function mcpProxyTools(host, tools) {
|
|
1298
|
+
return tools.map((t) => ({
|
|
1299
|
+
def: {
|
|
1300
|
+
name: t.toolName,
|
|
1301
|
+
description: t.description,
|
|
1302
|
+
parameters: t.inputSchema
|
|
1303
|
+
},
|
|
1304
|
+
invoke: (args) => host.invoke(t.serverId, t.rawName, args)
|
|
1305
|
+
}));
|
|
1306
|
+
}
|
|
1307
|
+
|
|
991
1308
|
// ../daemon/src/runtime/tools/memory.ts
|
|
992
1309
|
function memoryTools(memory) {
|
|
993
1310
|
return [
|
|
@@ -1813,14 +2130,20 @@ function ourToolToPiTool(h) {
|
|
|
1813
2130
|
description: h.def.description,
|
|
1814
2131
|
parameters: Type2.Unsafe(h.def.parameters),
|
|
1815
2132
|
async execute(_toolCallId, params) {
|
|
1816
|
-
const
|
|
2133
|
+
const out = await h.invoke(params);
|
|
1817
2134
|
return {
|
|
1818
|
-
content:
|
|
2135
|
+
content: toPiContent(out),
|
|
1819
2136
|
details: {}
|
|
1820
2137
|
};
|
|
1821
2138
|
}
|
|
1822
2139
|
};
|
|
1823
2140
|
}
|
|
2141
|
+
function toPiContent(out) {
|
|
2142
|
+
if (typeof out === "string") return [{ type: "text", text: out }];
|
|
2143
|
+
return out.map(
|
|
2144
|
+
(p) => p.type === "text" ? { type: "text", text: p.text } : { type: "image", data: p.data, mimeType: p.mimeType }
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
1824
2147
|
function createBazilionCustomTools(opts) {
|
|
1825
2148
|
const handlers = [
|
|
1826
2149
|
...memoryTools(opts.memory),
|
|
@@ -1834,13 +2157,35 @@ function createBazilionCustomTools(opts) {
|
|
|
1834
2157
|
if (opts.userMdHost) {
|
|
1835
2158
|
handlers.push(...userMdTools(opts.userMdHost, opts.agent.group.id));
|
|
1836
2159
|
}
|
|
2160
|
+
if (opts.browserHost) {
|
|
2161
|
+
handlers.push(...browserTools(opts.browserHost, opts.agent.agent.id));
|
|
2162
|
+
}
|
|
2163
|
+
if (opts.mcpHost && opts.mcpTools && opts.mcpTools.length > 0) {
|
|
2164
|
+
handlers.push(...mcpProxyTools(opts.mcpHost, opts.mcpTools));
|
|
2165
|
+
}
|
|
2166
|
+
if (opts.fileSink) {
|
|
2167
|
+
handlers.push(deliverFileTool(opts.agent.group.path, opts.fileSink));
|
|
2168
|
+
}
|
|
1837
2169
|
return handlers.map(ourToolToPiTool);
|
|
1838
2170
|
}
|
|
1839
2171
|
|
|
1840
2172
|
// ../daemon/src/runtime/pi/session.ts
|
|
1841
2173
|
var BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
1842
2174
|
async function createBazilionSession(opts) {
|
|
1843
|
-
const {
|
|
2175
|
+
const {
|
|
2176
|
+
agent,
|
|
2177
|
+
paths,
|
|
2178
|
+
env,
|
|
2179
|
+
memory,
|
|
2180
|
+
enabledProviders,
|
|
2181
|
+
messagingHost,
|
|
2182
|
+
userMdHost,
|
|
2183
|
+
browserHost,
|
|
2184
|
+
mcpHost,
|
|
2185
|
+
mcpTools,
|
|
2186
|
+
fileSink,
|
|
2187
|
+
refreshApiKey
|
|
2188
|
+
} = opts;
|
|
1844
2189
|
const { providerName, modelId } = splitModelString(agent.model);
|
|
1845
2190
|
if (enabledProviders.size > 0 && !enabledProviders.has(providerName)) {
|
|
1846
2191
|
throw new Error(`${providerName} provider is disabled \u2014 enable it on the /config page`);
|
|
@@ -1887,7 +2232,17 @@ async function createBazilionSession(opts) {
|
|
|
1887
2232
|
const bazilionPrompt = buildSystemPrompt(agent);
|
|
1888
2233
|
const resourceLoader = createBazilionResourceLoader(bazilionPrompt);
|
|
1889
2234
|
await resourceLoader.reload();
|
|
1890
|
-
const customTools = createBazilionCustomTools({
|
|
2235
|
+
const customTools = createBazilionCustomTools({
|
|
2236
|
+
agent,
|
|
2237
|
+
memory,
|
|
2238
|
+
messagingHost,
|
|
2239
|
+
userMdHost,
|
|
2240
|
+
browserHost,
|
|
2241
|
+
mcpHost,
|
|
2242
|
+
mcpTools,
|
|
2243
|
+
fileSink,
|
|
2244
|
+
env
|
|
2245
|
+
});
|
|
1891
2246
|
const allowedTools = [...BUILTIN_TOOL_NAMES, ...customTools.map((t) => t.name)];
|
|
1892
2247
|
const { session } = await createAgentSession({
|
|
1893
2248
|
cwd,
|
|
@@ -2023,7 +2378,7 @@ function findMostRecent(sessionDir) {
|
|
|
2023
2378
|
if (!entry.endsWith(".jsonl")) continue;
|
|
2024
2379
|
const path = join13(sessionDir, entry);
|
|
2025
2380
|
try {
|
|
2026
|
-
const s =
|
|
2381
|
+
const s = statSync6(path);
|
|
2027
2382
|
if (!newest || s.mtimeMs > newest.mtimeMs) newest = { path, mtimeMs: s.mtimeMs };
|
|
2028
2383
|
} catch {
|
|
2029
2384
|
}
|
|
@@ -2065,10 +2420,10 @@ function createIpcClient() {
|
|
|
2065
2420
|
if (!process.send) {
|
|
2066
2421
|
return Promise.reject(new Error("worker: no IPC channel \u2014 daemon must spawn with stdio:ipc"));
|
|
2067
2422
|
}
|
|
2068
|
-
const id =
|
|
2423
|
+
const id = randomUUID6();
|
|
2069
2424
|
const message = { type: "rpc", id, method, args };
|
|
2070
|
-
return new Promise((
|
|
2071
|
-
pending.set(id, { resolve: (v) =>
|
|
2425
|
+
return new Promise((resolve4, reject) => {
|
|
2426
|
+
pending.set(id, { resolve: (v) => resolve4(v), reject });
|
|
2072
2427
|
process.send?.(message, void 0, void 0, (err) => {
|
|
2073
2428
|
if (err) {
|
|
2074
2429
|
pending.delete(id);
|
|
@@ -2095,6 +2450,16 @@ function createIpcUserMdHost(call) {
|
|
|
2095
2450
|
write: (groupId, content, ifMatch) => call("userMdWrite", { groupId, content, ifMatch })
|
|
2096
2451
|
};
|
|
2097
2452
|
}
|
|
2453
|
+
function createIpcBrowserHost(call) {
|
|
2454
|
+
return {
|
|
2455
|
+
invoke: (agentId, action, args) => call("browserInvoke", { agentId, action, args })
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
function createIpcMcpHost(call) {
|
|
2459
|
+
return {
|
|
2460
|
+
invoke: (serverId, toolName, args) => call("mcpInvoke", { serverId, toolName, args })
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2098
2463
|
function isIpcReply(msg) {
|
|
2099
2464
|
if (!msg || typeof msg !== "object") return false;
|
|
2100
2465
|
const m = msg;
|
|
@@ -2117,13 +2482,15 @@ async function main() {
|
|
|
2117
2482
|
};
|
|
2118
2483
|
process.on("SIGTERM", onSignal);
|
|
2119
2484
|
process.on("SIGINT", onSignal);
|
|
2120
|
-
const { agent, message, enabledProviders, apiKey } = await readInput();
|
|
2485
|
+
const { agent, message, enabledProviders, apiKey, browserEnabled, mcpTools, images } = await readInput();
|
|
2121
2486
|
const paths = resolvePaths();
|
|
2122
2487
|
const memory = qmdBackend(join14(agent.group.path, "memory"));
|
|
2123
2488
|
await memory.init();
|
|
2124
2489
|
const ipcCall = createIpcClient();
|
|
2125
2490
|
const messagingHost = createIpcMessagingHost(ipcCall);
|
|
2126
2491
|
const userMdHost = createIpcUserMdHost(ipcCall);
|
|
2492
|
+
const browserHost = browserEnabled ? createIpcBrowserHost(ipcCall) : void 0;
|
|
2493
|
+
const mcpHost = mcpTools && mcpTools.length > 0 ? createIpcMcpHost(ipcCall) : void 0;
|
|
2127
2494
|
const { session, dispose } = await createBazilionSession({
|
|
2128
2495
|
agent,
|
|
2129
2496
|
paths,
|
|
@@ -2132,7 +2499,12 @@ async function main() {
|
|
|
2132
2499
|
enabledProviders: new Set(enabledProviders),
|
|
2133
2500
|
messagingHost,
|
|
2134
2501
|
userMdHost,
|
|
2135
|
-
apiKey
|
|
2502
|
+
apiKey,
|
|
2503
|
+
browserHost,
|
|
2504
|
+
mcpHost,
|
|
2505
|
+
mcpTools,
|
|
2506
|
+
// deliver_file emits a `file` event straight onto our stdout frame stream.
|
|
2507
|
+
fileSink: (f) => emit({ kind: "event", event: { type: "file", ...f } })
|
|
2136
2508
|
});
|
|
2137
2509
|
abortSession = () => {
|
|
2138
2510
|
void session.abort();
|
|
@@ -2143,7 +2515,12 @@ async function main() {
|
|
|
2143
2515
|
}
|
|
2144
2516
|
});
|
|
2145
2517
|
try {
|
|
2146
|
-
|
|
2518
|
+
const promptImages = (images ?? []).map((img) => ({
|
|
2519
|
+
type: "image",
|
|
2520
|
+
data: img.data,
|
|
2521
|
+
mimeType: img.mimeType
|
|
2522
|
+
}));
|
|
2523
|
+
await session.prompt(message, promptImages.length > 0 ? { images: promptImages } : void 0);
|
|
2147
2524
|
await session.agent.waitForIdle();
|
|
2148
2525
|
emit({
|
|
2149
2526
|
kind: "done",
|