bazilion 0.3.0 → 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 +308 -129
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +21428 -948
- package/dist/daemon.js.map +1 -1
- package/dist/migrations/0007_mcp_servers.sql +30 -0
- package/dist/worker.js +360 -23
- package/dist/worker.js.map +1 -1
- package/package.json +11 -10
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- MCP (Model Context Protocol) servers.
|
|
2
|
+
--
|
|
3
|
+
-- Each row is one configured server the daemon connects to as an MCP client.
|
|
4
|
+
-- Tools discovered on it (`tools/list`) are namespaced `mcp__<name>__<tool>`
|
|
5
|
+
-- and injected into agent turns. v0.4.0 scopes servers globally — every agent
|
|
6
|
+
-- sees the tools of every enabled server.
|
|
7
|
+
--
|
|
8
|
+
-- Transports:
|
|
9
|
+
-- stdio — local subprocess (`command` + JSON `args`); inherits the daemon's
|
|
10
|
+
-- merged secrets env, so a server needing e.g. GITHUB_TOKEN picks it
|
|
11
|
+
-- up from the normal secrets table.
|
|
12
|
+
-- http — Streamable-HTTP endpoint at `url`.
|
|
13
|
+
-- sse — SSE endpoint at `url`.
|
|
14
|
+
--
|
|
15
|
+
-- Bearer auth for http/sse is NOT stored here: the token lives in the encrypted
|
|
16
|
+
-- `secrets` table under key `MCP_TOKEN_<id>`; `has_auth` records whether one is
|
|
17
|
+
-- set so the UI/API can show it without decrypting.
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS mcp_servers (
|
|
20
|
+
id TEXT PRIMARY KEY,
|
|
21
|
+
name TEXT NOT NULL UNIQUE,
|
|
22
|
+
transport TEXT NOT NULL CHECK (transport IN ('stdio','http','sse')),
|
|
23
|
+
command TEXT,
|
|
24
|
+
args TEXT NOT NULL DEFAULT '[]',
|
|
25
|
+
url TEXT,
|
|
26
|
+
has_auth INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
28
|
+
created_at INTEGER NOT NULL,
|
|
29
|
+
updated_at INTEGER NOT NULL
|
|
30
|
+
);
|
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
|
|
@@ -419,6 +419,50 @@ var SERVICES = [
|
|
|
419
419
|
}
|
|
420
420
|
]
|
|
421
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
|
+
},
|
|
422
466
|
// --- External integrations (chat bridges, etc) ---
|
|
423
467
|
// Each integration has its own dedicated /config/integrations/* page with
|
|
424
468
|
// workflow-specific UI (preflight health, setup wizard, …). The fields
|
|
@@ -476,17 +520,20 @@ var CONFIG_KEYS = [
|
|
|
476
520
|
];
|
|
477
521
|
var CONFIG_KEY_SET = new Set(CONFIG_KEYS);
|
|
478
522
|
|
|
479
|
-
// ../daemon/src/core/repos/
|
|
523
|
+
// ../daemon/src/core/repos/mcpServers.ts
|
|
480
524
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
481
525
|
|
|
526
|
+
// ../daemon/src/core/repos/messages.ts
|
|
527
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
528
|
+
|
|
482
529
|
// ../daemon/src/core/repos/secrets.ts
|
|
483
530
|
import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from "crypto";
|
|
484
531
|
|
|
485
532
|
// ../daemon/src/core/repos/triggers.ts
|
|
486
|
-
import { randomUUID as
|
|
533
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
487
534
|
|
|
488
535
|
// ../daemon/src/core/repos/webTokens.ts
|
|
489
|
-
import { createHash, randomBytes as randomBytes2, randomUUID as
|
|
536
|
+
import { createHash, randomBytes as randomBytes2, randomUUID as randomUUID5 } from "crypto";
|
|
490
537
|
|
|
491
538
|
// ../daemon/src/core/secrets.ts
|
|
492
539
|
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
@@ -666,7 +713,16 @@ function translatePiEvent(e) {
|
|
|
666
713
|
if (e.isError) {
|
|
667
714
|
return [{ type: "tool_error", id: e.toolCallId, name: e.toolName, error: text }];
|
|
668
715
|
}
|
|
669
|
-
|
|
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
|
+
];
|
|
670
726
|
}
|
|
671
727
|
default:
|
|
672
728
|
return [];
|
|
@@ -697,6 +753,20 @@ function extractToolResultText(result) {
|
|
|
697
753
|
}
|
|
698
754
|
return out;
|
|
699
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
|
+
}
|
|
700
770
|
function stringifyContent(content) {
|
|
701
771
|
if (typeof content === "string") return content;
|
|
702
772
|
if (!Array.isArray(content)) return "";
|
|
@@ -711,7 +781,13 @@ function piMessagesToProviderView(messages) {
|
|
|
711
781
|
for (const m of messages) {
|
|
712
782
|
switch (m.role) {
|
|
713
783
|
case "user": {
|
|
714
|
-
|
|
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
|
+
});
|
|
715
791
|
break;
|
|
716
792
|
}
|
|
717
793
|
case "assistant": {
|
|
@@ -725,11 +801,13 @@ function piMessagesToProviderView(messages) {
|
|
|
725
801
|
}
|
|
726
802
|
case "toolResult": {
|
|
727
803
|
const tr = m;
|
|
804
|
+
const images = extractToolResultImages({ content: tr.content });
|
|
728
805
|
out.push({
|
|
729
806
|
role: "tool",
|
|
730
807
|
content: stringifyContent(tr.content),
|
|
731
808
|
toolCallId: tr.toolCallId,
|
|
732
|
-
toolName: tr.toolName
|
|
809
|
+
toolName: tr.toolName,
|
|
810
|
+
...images.length > 0 ? { images } : {}
|
|
733
811
|
});
|
|
734
812
|
break;
|
|
735
813
|
}
|
|
@@ -741,8 +819,8 @@ function piMessagesToProviderView(messages) {
|
|
|
741
819
|
}
|
|
742
820
|
|
|
743
821
|
// ../daemon/src/runtime/pi/session.ts
|
|
744
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync7, statSync as
|
|
745
|
-
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";
|
|
746
824
|
import {
|
|
747
825
|
AuthStorage,
|
|
748
826
|
createAgentSession,
|
|
@@ -922,8 +1000,195 @@ function bootstrapTool(agentDir) {
|
|
|
922
1000
|
};
|
|
923
1001
|
}
|
|
924
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
|
+
|
|
925
1190
|
// ../daemon/src/runtime/tools/home.ts
|
|
926
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
1191
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
927
1192
|
import { join as join12 } from "path";
|
|
928
1193
|
var HOME_FILES_READABLE = [
|
|
929
1194
|
"IDENTITY.md",
|
|
@@ -963,7 +1228,7 @@ function homeTools(agentDir) {
|
|
|
963
1228
|
}
|
|
964
1229
|
const path = join12(agentDir, file);
|
|
965
1230
|
try {
|
|
966
|
-
return
|
|
1231
|
+
return readFileSync9(path, "utf8");
|
|
967
1232
|
} catch (err) {
|
|
968
1233
|
const msg = err instanceof Error ? err.message : String(err);
|
|
969
1234
|
throw new Error(`home_read: could not read ${file}: ${msg}`);
|
|
@@ -1007,7 +1272,7 @@ function homeTools(agentDir) {
|
|
|
1007
1272
|
for (const file of HOME_FILES_READABLE) {
|
|
1008
1273
|
const path = join12(agentDir, file);
|
|
1009
1274
|
try {
|
|
1010
|
-
const s =
|
|
1275
|
+
const s = statSync5(path);
|
|
1011
1276
|
entries.push(`${file} (${s.size}b)`);
|
|
1012
1277
|
} catch {
|
|
1013
1278
|
}
|
|
@@ -1028,6 +1293,18 @@ function homeTools(agentDir) {
|
|
|
1028
1293
|
];
|
|
1029
1294
|
}
|
|
1030
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
|
+
|
|
1031
1308
|
// ../daemon/src/runtime/tools/memory.ts
|
|
1032
1309
|
function memoryTools(memory) {
|
|
1033
1310
|
return [
|
|
@@ -1853,14 +2130,20 @@ function ourToolToPiTool(h) {
|
|
|
1853
2130
|
description: h.def.description,
|
|
1854
2131
|
parameters: Type2.Unsafe(h.def.parameters),
|
|
1855
2132
|
async execute(_toolCallId, params) {
|
|
1856
|
-
const
|
|
2133
|
+
const out = await h.invoke(params);
|
|
1857
2134
|
return {
|
|
1858
|
-
content:
|
|
2135
|
+
content: toPiContent(out),
|
|
1859
2136
|
details: {}
|
|
1860
2137
|
};
|
|
1861
2138
|
}
|
|
1862
2139
|
};
|
|
1863
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
|
+
}
|
|
1864
2147
|
function createBazilionCustomTools(opts) {
|
|
1865
2148
|
const handlers = [
|
|
1866
2149
|
...memoryTools(opts.memory),
|
|
@@ -1874,13 +2157,35 @@ function createBazilionCustomTools(opts) {
|
|
|
1874
2157
|
if (opts.userMdHost) {
|
|
1875
2158
|
handlers.push(...userMdTools(opts.userMdHost, opts.agent.group.id));
|
|
1876
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
|
+
}
|
|
1877
2169
|
return handlers.map(ourToolToPiTool);
|
|
1878
2170
|
}
|
|
1879
2171
|
|
|
1880
2172
|
// ../daemon/src/runtime/pi/session.ts
|
|
1881
2173
|
var BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
1882
2174
|
async function createBazilionSession(opts) {
|
|
1883
|
-
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;
|
|
1884
2189
|
const { providerName, modelId } = splitModelString(agent.model);
|
|
1885
2190
|
if (enabledProviders.size > 0 && !enabledProviders.has(providerName)) {
|
|
1886
2191
|
throw new Error(`${providerName} provider is disabled \u2014 enable it on the /config page`);
|
|
@@ -1927,7 +2232,17 @@ async function createBazilionSession(opts) {
|
|
|
1927
2232
|
const bazilionPrompt = buildSystemPrompt(agent);
|
|
1928
2233
|
const resourceLoader = createBazilionResourceLoader(bazilionPrompt);
|
|
1929
2234
|
await resourceLoader.reload();
|
|
1930
|
-
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
|
+
});
|
|
1931
2246
|
const allowedTools = [...BUILTIN_TOOL_NAMES, ...customTools.map((t) => t.name)];
|
|
1932
2247
|
const { session } = await createAgentSession({
|
|
1933
2248
|
cwd,
|
|
@@ -2063,7 +2378,7 @@ function findMostRecent(sessionDir) {
|
|
|
2063
2378
|
if (!entry.endsWith(".jsonl")) continue;
|
|
2064
2379
|
const path = join13(sessionDir, entry);
|
|
2065
2380
|
try {
|
|
2066
|
-
const s =
|
|
2381
|
+
const s = statSync6(path);
|
|
2067
2382
|
if (!newest || s.mtimeMs > newest.mtimeMs) newest = { path, mtimeMs: s.mtimeMs };
|
|
2068
2383
|
} catch {
|
|
2069
2384
|
}
|
|
@@ -2105,10 +2420,10 @@ function createIpcClient() {
|
|
|
2105
2420
|
if (!process.send) {
|
|
2106
2421
|
return Promise.reject(new Error("worker: no IPC channel \u2014 daemon must spawn with stdio:ipc"));
|
|
2107
2422
|
}
|
|
2108
|
-
const id =
|
|
2423
|
+
const id = randomUUID6();
|
|
2109
2424
|
const message = { type: "rpc", id, method, args };
|
|
2110
|
-
return new Promise((
|
|
2111
|
-
pending.set(id, { resolve: (v) =>
|
|
2425
|
+
return new Promise((resolve4, reject) => {
|
|
2426
|
+
pending.set(id, { resolve: (v) => resolve4(v), reject });
|
|
2112
2427
|
process.send?.(message, void 0, void 0, (err) => {
|
|
2113
2428
|
if (err) {
|
|
2114
2429
|
pending.delete(id);
|
|
@@ -2135,6 +2450,16 @@ function createIpcUserMdHost(call) {
|
|
|
2135
2450
|
write: (groupId, content, ifMatch) => call("userMdWrite", { groupId, content, ifMatch })
|
|
2136
2451
|
};
|
|
2137
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
|
+
}
|
|
2138
2463
|
function isIpcReply(msg) {
|
|
2139
2464
|
if (!msg || typeof msg !== "object") return false;
|
|
2140
2465
|
const m = msg;
|
|
@@ -2157,13 +2482,15 @@ async function main() {
|
|
|
2157
2482
|
};
|
|
2158
2483
|
process.on("SIGTERM", onSignal);
|
|
2159
2484
|
process.on("SIGINT", onSignal);
|
|
2160
|
-
const { agent, message, enabledProviders, apiKey } = await readInput();
|
|
2485
|
+
const { agent, message, enabledProviders, apiKey, browserEnabled, mcpTools, images } = await readInput();
|
|
2161
2486
|
const paths = resolvePaths();
|
|
2162
2487
|
const memory = qmdBackend(join14(agent.group.path, "memory"));
|
|
2163
2488
|
await memory.init();
|
|
2164
2489
|
const ipcCall = createIpcClient();
|
|
2165
2490
|
const messagingHost = createIpcMessagingHost(ipcCall);
|
|
2166
2491
|
const userMdHost = createIpcUserMdHost(ipcCall);
|
|
2492
|
+
const browserHost = browserEnabled ? createIpcBrowserHost(ipcCall) : void 0;
|
|
2493
|
+
const mcpHost = mcpTools && mcpTools.length > 0 ? createIpcMcpHost(ipcCall) : void 0;
|
|
2167
2494
|
const { session, dispose } = await createBazilionSession({
|
|
2168
2495
|
agent,
|
|
2169
2496
|
paths,
|
|
@@ -2172,7 +2499,12 @@ async function main() {
|
|
|
2172
2499
|
enabledProviders: new Set(enabledProviders),
|
|
2173
2500
|
messagingHost,
|
|
2174
2501
|
userMdHost,
|
|
2175
|
-
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 } })
|
|
2176
2508
|
});
|
|
2177
2509
|
abortSession = () => {
|
|
2178
2510
|
void session.abort();
|
|
@@ -2183,7 +2515,12 @@ async function main() {
|
|
|
2183
2515
|
}
|
|
2184
2516
|
});
|
|
2185
2517
|
try {
|
|
2186
|
-
|
|
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);
|
|
2187
2524
|
await session.agent.waitForIdle();
|
|
2188
2525
|
emit({
|
|
2189
2526
|
kind: "done",
|