bazilion 0.3.0 → 0.5.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 +21921 -1198
- package/dist/daemon.js.map +1 -1
- package/dist/migrations/0002_profile_groups.sql +0 -1
- package/dist/migrations/0007_mcp_servers.sql +30 -0
- package/dist/migrations/0008_seed_user_md.sql +39 -0
- package/dist/worker.js +436 -92
- package/dist/worker.js.map +1 -1
- package/package.json +11 -10
package/dist/worker.js
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// ../daemon/src/runtime/worker/entry.ts
|
|
4
|
-
import { randomUUID as
|
|
5
|
-
import { join as
|
|
4
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
5
|
+
import { join as join16 } from "path";
|
|
6
6
|
|
|
7
7
|
// ../daemon/src/core/agent/delete.ts
|
|
8
8
|
import { existsSync, rmSync } from "fs";
|
|
9
9
|
|
|
10
|
+
// ../daemon/src/core/agent/resolve.ts
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
|
|
13
|
+
// ../daemon/src/core/profile/identity.ts
|
|
14
|
+
import { readFileSync } from "fs";
|
|
15
|
+
|
|
10
16
|
// ../daemon/src/core/agent/spawn.ts
|
|
11
17
|
import { randomUUID } from "crypto";
|
|
12
|
-
import { mkdirSync as mkdirSync3, writeFileSync as
|
|
13
|
-
import { join as
|
|
18
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
19
|
+
import { join as join6 } from "path";
|
|
14
20
|
|
|
15
21
|
// ../daemon/src/core/profile/load.ts
|
|
16
22
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
17
|
-
import { join } from "path";
|
|
23
|
+
import { join as join2 } from "path";
|
|
18
24
|
|
|
19
|
-
// ../daemon/src/core/profile/
|
|
20
|
-
import { readFileSync } from "fs";
|
|
25
|
+
// ../daemon/src/core/profile/seed.ts
|
|
26
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
27
|
+
import { join as join4 } from "path";
|
|
21
28
|
|
|
22
29
|
// ../daemon/src/core/group/register.ts
|
|
23
30
|
import { existsSync as existsSync3, mkdirSync, statSync, symlinkSync } from "fs";
|
|
@@ -25,56 +32,56 @@ import { resolve } from "path";
|
|
|
25
32
|
|
|
26
33
|
// ../daemon/src/core/profile/create.ts
|
|
27
34
|
import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
28
|
-
import { join as
|
|
35
|
+
import { join as join3 } from "path";
|
|
29
36
|
|
|
30
37
|
// ../daemon/src/core/skills/discover.ts
|
|
31
|
-
import { existsSync as
|
|
32
|
-
import { join as
|
|
38
|
+
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
39
|
+
import { join as join5 } from "path";
|
|
33
40
|
|
|
34
41
|
// ../daemon/src/core/db/client.ts
|
|
35
42
|
import { DatabaseSync } from "node:sqlite";
|
|
36
43
|
|
|
37
44
|
// ../daemon/src/core/db/migrate.ts
|
|
38
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
39
|
-
import { dirname, join as
|
|
45
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
46
|
+
import { dirname, join as join7 } from "path";
|
|
40
47
|
import { fileURLToPath } from "url";
|
|
41
|
-
var migrationsDir =
|
|
48
|
+
var migrationsDir = join7(dirname(fileURLToPath(import.meta.url)), "migrations");
|
|
42
49
|
|
|
43
50
|
// ../daemon/src/core/paths.ts
|
|
44
51
|
import { homedir } from "os";
|
|
45
|
-
import { join as
|
|
52
|
+
import { join as join8 } from "path";
|
|
46
53
|
function resolvePaths(home) {
|
|
47
|
-
const root = home ?? process.env.BAZILION_HOME ??
|
|
54
|
+
const root = home ?? process.env.BAZILION_HOME ?? join8(homedir(), ".bazilion");
|
|
48
55
|
return {
|
|
49
56
|
home: root,
|
|
50
|
-
db:
|
|
51
|
-
authFile:
|
|
52
|
-
profilesDir:
|
|
53
|
-
agentsDir:
|
|
54
|
-
skillsDir:
|
|
55
|
-
groupsDir:
|
|
56
|
-
logsDir:
|
|
57
|
+
db: join8(root, "bazilion.db"),
|
|
58
|
+
authFile: join8(root, "auth.json"),
|
|
59
|
+
profilesDir: join8(root, "profiles"),
|
|
60
|
+
agentsDir: join8(root, "agents"),
|
|
61
|
+
skillsDir: join8(root, "skills"),
|
|
62
|
+
groupsDir: join8(root, "groups"),
|
|
63
|
+
logsDir: join8(root, "logs"),
|
|
57
64
|
profileDir(id) {
|
|
58
|
-
return
|
|
65
|
+
return join8(root, "profiles", id);
|
|
59
66
|
},
|
|
60
67
|
agentDir(id) {
|
|
61
|
-
return
|
|
68
|
+
return join8(root, "agents", id);
|
|
62
69
|
},
|
|
63
70
|
skillDir(name) {
|
|
64
|
-
return
|
|
71
|
+
return join8(root, "skills", name);
|
|
65
72
|
},
|
|
66
73
|
groupDir(slug) {
|
|
67
|
-
return
|
|
74
|
+
return join8(root, "groups", slug);
|
|
68
75
|
}
|
|
69
76
|
};
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
// ../daemon/src/core/profile/delete.ts
|
|
73
|
-
import { existsSync as
|
|
80
|
+
import { existsSync as existsSync6, rmSync as rmSync2 } from "fs";
|
|
74
81
|
|
|
75
82
|
// ../daemon/src/core/profile/update.ts
|
|
76
|
-
import { writeFileSync as
|
|
77
|
-
import { join as
|
|
83
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
84
|
+
import { join as join9 } from "path";
|
|
78
85
|
|
|
79
86
|
// ../daemon/src/core/profile-group/spawn.ts
|
|
80
87
|
import { readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
|
|
@@ -419,6 +426,50 @@ var SERVICES = [
|
|
|
419
426
|
}
|
|
420
427
|
]
|
|
421
428
|
},
|
|
429
|
+
{
|
|
430
|
+
id: "browser",
|
|
431
|
+
displayName: "Browser Automation",
|
|
432
|
+
category: "service",
|
|
433
|
+
group: "Browser",
|
|
434
|
+
hint: "Playwright-driven browser tools (navigate, snapshot, click, screenshot). Run `pnpm exec playwright install chromium` once.",
|
|
435
|
+
fields: [
|
|
436
|
+
{
|
|
437
|
+
envVar: "BROWSER_ENABLED",
|
|
438
|
+
kind: "config",
|
|
439
|
+
label: "Enable browser tools",
|
|
440
|
+
placeholder: "true",
|
|
441
|
+
description: "Expose the browser_* tools to agents (true/false). Default true."
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
envVar: "BROWSER_HEADLESS",
|
|
445
|
+
kind: "config",
|
|
446
|
+
label: "Headless",
|
|
447
|
+
placeholder: "true",
|
|
448
|
+
description: "Run Chromium headless (true/false). Default true."
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
envVar: "BROWSER_ALLOW_PRIVATE_NETWORK",
|
|
452
|
+
kind: "config",
|
|
453
|
+
label: "Allow private network",
|
|
454
|
+
placeholder: "false",
|
|
455
|
+
description: "Permit the browser to reach loopback/private IPs (SSRF guard off). Default false \u2014 only enable for local dev."
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
envVar: "BROWSER_IDLE_MS",
|
|
459
|
+
kind: "config",
|
|
460
|
+
label: "Idle timeout (ms)",
|
|
461
|
+
placeholder: "900000",
|
|
462
|
+
description: "Close an idle browser session after this many ms. Default 900000 (15 min)."
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
envVar: "BROWSER_MAX_SESSIONS",
|
|
466
|
+
kind: "config",
|
|
467
|
+
label: "Max concurrent sessions",
|
|
468
|
+
placeholder: "4",
|
|
469
|
+
description: "Cap on simultaneously-open browser sessions (LRU-evicted). Default 4."
|
|
470
|
+
}
|
|
471
|
+
]
|
|
472
|
+
},
|
|
422
473
|
// --- External integrations (chat bridges, etc) ---
|
|
423
474
|
// Each integration has its own dedicated /config/integrations/* page with
|
|
424
475
|
// workflow-specific UI (preflight health, setup wizard, …). The fields
|
|
@@ -476,42 +527,45 @@ var CONFIG_KEYS = [
|
|
|
476
527
|
];
|
|
477
528
|
var CONFIG_KEY_SET = new Set(CONFIG_KEYS);
|
|
478
529
|
|
|
479
|
-
// ../daemon/src/core/repos/
|
|
530
|
+
// ../daemon/src/core/repos/mcpServers.ts
|
|
480
531
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
481
532
|
|
|
533
|
+
// ../daemon/src/core/repos/messages.ts
|
|
534
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
535
|
+
|
|
482
536
|
// ../daemon/src/core/repos/secrets.ts
|
|
483
537
|
import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from "crypto";
|
|
484
538
|
|
|
485
539
|
// ../daemon/src/core/repos/triggers.ts
|
|
486
|
-
import { randomUUID as
|
|
540
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
487
541
|
|
|
488
542
|
// ../daemon/src/core/repos/webTokens.ts
|
|
489
|
-
import { createHash, randomBytes as randomBytes2, randomUUID as
|
|
543
|
+
import { createHash, randomBytes as randomBytes2, randomUUID as randomUUID5 } from "crypto";
|
|
490
544
|
|
|
491
545
|
// ../daemon/src/core/secrets.ts
|
|
492
|
-
import { existsSync as
|
|
546
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
493
547
|
|
|
494
548
|
// ../daemon/src/core/skills/import.ts
|
|
495
|
-
import { cpSync, existsSync as
|
|
549
|
+
import { cpSync, existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5, statSync as statSync2 } from "fs";
|
|
496
550
|
import { tmpdir } from "os";
|
|
497
|
-
import { basename, join as
|
|
551
|
+
import { basename, join as join10, resolve as resolve2, sep } from "path";
|
|
498
552
|
import AdmZip from "adm-zip";
|
|
499
553
|
|
|
500
554
|
// ../daemon/src/core/skills/parse.ts
|
|
501
|
-
import { readFileSync as
|
|
555
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
502
556
|
import { parse as parseYaml } from "yaml";
|
|
503
557
|
|
|
504
558
|
// ../daemon/src/runtime/memory/qmd.ts
|
|
505
559
|
import {
|
|
506
|
-
existsSync as
|
|
560
|
+
existsSync as existsSync9,
|
|
507
561
|
mkdirSync as mkdirSync4,
|
|
508
562
|
readdirSync as readdirSync5,
|
|
509
|
-
readFileSync as
|
|
563
|
+
readFileSync as readFileSync7,
|
|
510
564
|
rmSync as rmSync6,
|
|
511
565
|
statSync as statSync3,
|
|
512
|
-
writeFileSync as
|
|
566
|
+
writeFileSync as writeFileSync5
|
|
513
567
|
} from "fs";
|
|
514
|
-
import { dirname as dirname2, join as
|
|
568
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
515
569
|
import { createStore, extractSnippet } from "@tobilu/qmd";
|
|
516
570
|
var INDEX_FILENAME = ".qmd-index.sqlite";
|
|
517
571
|
var COLLECTION_NAME = "memory";
|
|
@@ -521,7 +575,7 @@ function getStore(dir) {
|
|
|
521
575
|
let p = storeCache.get(dir);
|
|
522
576
|
if (!p) {
|
|
523
577
|
p = createStore({
|
|
524
|
-
dbPath:
|
|
578
|
+
dbPath: join11(dir, INDEX_FILENAME),
|
|
525
579
|
config: {
|
|
526
580
|
collections: {
|
|
527
581
|
[COLLECTION_NAME]: { path: dir, pattern: PATTERN }
|
|
@@ -536,13 +590,13 @@ function safeKey(root, key) {
|
|
|
536
590
|
if (key.includes("..") || key.startsWith("/") || key.includes("\0")) {
|
|
537
591
|
throw new Error(`unsafe memory key: ${key}`);
|
|
538
592
|
}
|
|
539
|
-
return
|
|
593
|
+
return join11(root, key);
|
|
540
594
|
}
|
|
541
595
|
function walkMd(dir, prefix, out) {
|
|
542
|
-
if (!
|
|
596
|
+
if (!existsSync9(dir)) return;
|
|
543
597
|
for (const e of readdirSync5(dir, { withFileTypes: true })) {
|
|
544
598
|
if (e.name.startsWith(".")) continue;
|
|
545
|
-
const full =
|
|
599
|
+
const full = join11(dir, e.name);
|
|
546
600
|
const key = prefix ? `${prefix}/${e.name}` : e.name;
|
|
547
601
|
if (e.isDirectory()) {
|
|
548
602
|
walkMd(full, key, out);
|
|
@@ -550,7 +604,7 @@ function walkMd(dir, prefix, out) {
|
|
|
550
604
|
const stats = statSync3(full);
|
|
551
605
|
out.push({
|
|
552
606
|
key,
|
|
553
|
-
content:
|
|
607
|
+
content: readFileSync7(full, "utf8"),
|
|
554
608
|
updatedAt: stats.mtimeMs
|
|
555
609
|
});
|
|
556
610
|
}
|
|
@@ -565,20 +619,20 @@ function qmdBackend(root) {
|
|
|
565
619
|
},
|
|
566
620
|
async read(key) {
|
|
567
621
|
const path = safeKey(root, key);
|
|
568
|
-
if (!
|
|
622
|
+
if (!existsSync9(path)) {
|
|
569
623
|
throw new Error(`memory entry not found: ${key}`);
|
|
570
624
|
}
|
|
571
625
|
const stats = statSync3(path);
|
|
572
626
|
return {
|
|
573
627
|
key,
|
|
574
|
-
content:
|
|
628
|
+
content: readFileSync7(path, "utf8"),
|
|
575
629
|
updatedAt: stats.mtimeMs
|
|
576
630
|
};
|
|
577
631
|
},
|
|
578
632
|
async write(key, content) {
|
|
579
633
|
const path = safeKey(root, key);
|
|
580
634
|
mkdirSync4(dirname2(path), { recursive: true });
|
|
581
|
-
|
|
635
|
+
writeFileSync5(path, content);
|
|
582
636
|
const stats = statSync3(path);
|
|
583
637
|
const store = await getStore(root);
|
|
584
638
|
await store.update();
|
|
@@ -598,7 +652,7 @@ function qmdBackend(root) {
|
|
|
598
652
|
let content = r.body ?? "";
|
|
599
653
|
if (!content) {
|
|
600
654
|
try {
|
|
601
|
-
content =
|
|
655
|
+
content = readFileSync7(join11(root, key), "utf8");
|
|
602
656
|
} catch {
|
|
603
657
|
content = "";
|
|
604
658
|
}
|
|
@@ -615,7 +669,7 @@ function qmdBackend(root) {
|
|
|
615
669
|
},
|
|
616
670
|
async remove(key) {
|
|
617
671
|
const path = safeKey(root, key);
|
|
618
|
-
if (
|
|
672
|
+
if (existsSync9(path)) rmSync6(path);
|
|
619
673
|
const store = await getStore(root);
|
|
620
674
|
await store.update();
|
|
621
675
|
}
|
|
@@ -666,7 +720,16 @@ function translatePiEvent(e) {
|
|
|
666
720
|
if (e.isError) {
|
|
667
721
|
return [{ type: "tool_error", id: e.toolCallId, name: e.toolName, error: text }];
|
|
668
722
|
}
|
|
669
|
-
|
|
723
|
+
const images = extractToolResultImages(e.result);
|
|
724
|
+
return [
|
|
725
|
+
{
|
|
726
|
+
type: "tool_result",
|
|
727
|
+
id: e.toolCallId,
|
|
728
|
+
name: e.toolName,
|
|
729
|
+
result: text,
|
|
730
|
+
...images.length > 0 ? { images } : {}
|
|
731
|
+
}
|
|
732
|
+
];
|
|
670
733
|
}
|
|
671
734
|
default:
|
|
672
735
|
return [];
|
|
@@ -697,6 +760,20 @@ function extractToolResultText(result) {
|
|
|
697
760
|
}
|
|
698
761
|
return out;
|
|
699
762
|
}
|
|
763
|
+
function extractToolResultImages(result) {
|
|
764
|
+
const r = result;
|
|
765
|
+
if (!r?.content) return [];
|
|
766
|
+
const out = [];
|
|
767
|
+
for (const block of r.content) {
|
|
768
|
+
if (block.type === "image") {
|
|
769
|
+
const b = block;
|
|
770
|
+
if (typeof b.data === "string" && typeof b.mimeType === "string") {
|
|
771
|
+
out.push({ data: b.data, mimeType: b.mimeType });
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return out;
|
|
776
|
+
}
|
|
700
777
|
function stringifyContent(content) {
|
|
701
778
|
if (typeof content === "string") return content;
|
|
702
779
|
if (!Array.isArray(content)) return "";
|
|
@@ -711,7 +788,13 @@ function piMessagesToProviderView(messages) {
|
|
|
711
788
|
for (const m of messages) {
|
|
712
789
|
switch (m.role) {
|
|
713
790
|
case "user": {
|
|
714
|
-
|
|
791
|
+
const content = m.content;
|
|
792
|
+
const images = extractToolResultImages({ content });
|
|
793
|
+
out.push({
|
|
794
|
+
role: "user",
|
|
795
|
+
content: stringifyContent(content),
|
|
796
|
+
...images.length > 0 ? { images } : {}
|
|
797
|
+
});
|
|
715
798
|
break;
|
|
716
799
|
}
|
|
717
800
|
case "assistant": {
|
|
@@ -725,11 +808,13 @@ function piMessagesToProviderView(messages) {
|
|
|
725
808
|
}
|
|
726
809
|
case "toolResult": {
|
|
727
810
|
const tr = m;
|
|
811
|
+
const images = extractToolResultImages({ content: tr.content });
|
|
728
812
|
out.push({
|
|
729
813
|
role: "tool",
|
|
730
814
|
content: stringifyContent(tr.content),
|
|
731
815
|
toolCallId: tr.toolCallId,
|
|
732
|
-
toolName: tr.toolName
|
|
816
|
+
toolName: tr.toolName,
|
|
817
|
+
...images.length > 0 ? { images } : {}
|
|
733
818
|
});
|
|
734
819
|
break;
|
|
735
820
|
}
|
|
@@ -741,8 +826,8 @@ function piMessagesToProviderView(messages) {
|
|
|
741
826
|
}
|
|
742
827
|
|
|
743
828
|
// ../daemon/src/runtime/pi/session.ts
|
|
744
|
-
import { existsSync as
|
|
745
|
-
import { basename as
|
|
829
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
830
|
+
import { basename as basename3, join as join15 } from "path";
|
|
746
831
|
import {
|
|
747
832
|
AuthStorage,
|
|
748
833
|
createAgentSession,
|
|
@@ -802,8 +887,8 @@ function resolveModel(cfg, modelId) {
|
|
|
802
887
|
import { loginOpenAICodex, refreshOpenAICodexToken } from "@earendil-works/pi-ai/oauth";
|
|
803
888
|
|
|
804
889
|
// ../daemon/src/runtime/session/prompt.ts
|
|
805
|
-
import { existsSync as
|
|
806
|
-
import { join as
|
|
890
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
891
|
+
import { join as join12 } from "path";
|
|
807
892
|
var CONTEXT_FILE_ORDER = [
|
|
808
893
|
"AGENTS.md",
|
|
809
894
|
"SOUL.md",
|
|
@@ -815,9 +900,9 @@ function buildSystemPrompt(agent) {
|
|
|
815
900
|
const parts = [];
|
|
816
901
|
const contextBlocks = [];
|
|
817
902
|
for (const file of CONTEXT_FILE_ORDER) {
|
|
818
|
-
const path =
|
|
819
|
-
if (!
|
|
820
|
-
const content =
|
|
903
|
+
const path = join12(agent.agent.dir, file);
|
|
904
|
+
if (!existsSync10(path)) continue;
|
|
905
|
+
const content = readFileSync8(path, "utf8").trimEnd();
|
|
821
906
|
if (!content) continue;
|
|
822
907
|
contextBlocks.push(`## ${file}
|
|
823
908
|
|
|
@@ -828,9 +913,9 @@ ${content}`);
|
|
|
828
913
|
|
|
829
914
|
${contextBlocks.join("\n\n")}`);
|
|
830
915
|
}
|
|
831
|
-
const bootstrapPath =
|
|
832
|
-
if (
|
|
833
|
-
const bootstrap =
|
|
916
|
+
const bootstrapPath = join12(agent.agent.dir, "BOOTSTRAP.md");
|
|
917
|
+
if (existsSync10(bootstrapPath)) {
|
|
918
|
+
const bootstrap = readFileSync8(bootstrapPath, "utf8").trimEnd();
|
|
834
919
|
if (bootstrap) {
|
|
835
920
|
parts.push(
|
|
836
921
|
[
|
|
@@ -902,8 +987,8 @@ This group's USER.md is empty. As you learn STABLE facts about the human (prefer
|
|
|
902
987
|
import { Type as Type2 } from "typebox";
|
|
903
988
|
|
|
904
989
|
// ../daemon/src/runtime/tools/bootstrap.ts
|
|
905
|
-
import { existsSync as
|
|
906
|
-
import { join as
|
|
990
|
+
import { existsSync as existsSync11, rmSync as rmSync7 } from "fs";
|
|
991
|
+
import { join as join13 } from "path";
|
|
907
992
|
function bootstrapTool(agentDir) {
|
|
908
993
|
return {
|
|
909
994
|
def: {
|
|
@@ -912,8 +997,8 @@ function bootstrapTool(agentDir) {
|
|
|
912
997
|
parameters: { type: "object", properties: {} }
|
|
913
998
|
},
|
|
914
999
|
async invoke() {
|
|
915
|
-
const path =
|
|
916
|
-
if (
|
|
1000
|
+
const path = join13(agentDir, "BOOTSTRAP.md");
|
|
1001
|
+
if (existsSync11(path)) {
|
|
917
1002
|
rmSync7(path);
|
|
918
1003
|
return "BOOTSTRAP.md removed. Bootstrap is complete.";
|
|
919
1004
|
}
|
|
@@ -922,9 +1007,196 @@ function bootstrapTool(agentDir) {
|
|
|
922
1007
|
};
|
|
923
1008
|
}
|
|
924
1009
|
|
|
1010
|
+
// ../daemon/src/runtime/tools/browser.ts
|
|
1011
|
+
var REF_DESC = 'Element ref from the latest browser_snapshot, e.g. "e5".';
|
|
1012
|
+
function browserTools(host, agentId) {
|
|
1013
|
+
const proxy = (name, action, description, properties, required = []) => ({
|
|
1014
|
+
def: {
|
|
1015
|
+
name,
|
|
1016
|
+
description,
|
|
1017
|
+
parameters: { type: "object", properties, required, additionalProperties: false }
|
|
1018
|
+
},
|
|
1019
|
+
invoke: (args) => host.invoke(agentId, action, args)
|
|
1020
|
+
});
|
|
1021
|
+
return [
|
|
1022
|
+
proxy(
|
|
1023
|
+
"browser_navigate",
|
|
1024
|
+
"navigate",
|
|
1025
|
+
"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).",
|
|
1026
|
+
{ url: { type: "string", description: "Absolute URL to navigate to." } },
|
|
1027
|
+
["url"]
|
|
1028
|
+
),
|
|
1029
|
+
proxy(
|
|
1030
|
+
"browser_snapshot",
|
|
1031
|
+
"snapshot",
|
|
1032
|
+
'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.',
|
|
1033
|
+
{}
|
|
1034
|
+
),
|
|
1035
|
+
proxy(
|
|
1036
|
+
"browser_click",
|
|
1037
|
+
"click",
|
|
1038
|
+
"Click an element identified by its snapshot ref. Returns a fresh snapshot.",
|
|
1039
|
+
{ ref: { type: "string", description: REF_DESC } },
|
|
1040
|
+
["ref"]
|
|
1041
|
+
),
|
|
1042
|
+
proxy(
|
|
1043
|
+
"browser_type",
|
|
1044
|
+
"type",
|
|
1045
|
+
"Type text into an input/textarea identified by its ref. Set submit=true to press Enter afterwards. Returns a fresh snapshot.",
|
|
1046
|
+
{
|
|
1047
|
+
ref: { type: "string", description: REF_DESC },
|
|
1048
|
+
text: { type: "string", description: "Text to type into the field." },
|
|
1049
|
+
submit: { type: "boolean", description: "Press Enter after typing." }
|
|
1050
|
+
},
|
|
1051
|
+
["ref", "text"]
|
|
1052
|
+
),
|
|
1053
|
+
proxy(
|
|
1054
|
+
"browser_hover",
|
|
1055
|
+
"hover",
|
|
1056
|
+
"Hover the pointer over an element identified by its ref. Returns a fresh snapshot.",
|
|
1057
|
+
{ ref: { type: "string", description: REF_DESC } },
|
|
1058
|
+
["ref"]
|
|
1059
|
+
),
|
|
1060
|
+
proxy(
|
|
1061
|
+
"browser_select",
|
|
1062
|
+
"select",
|
|
1063
|
+
"Select option(s) in a <select> element identified by its ref.",
|
|
1064
|
+
{
|
|
1065
|
+
ref: { type: "string", description: REF_DESC },
|
|
1066
|
+
values: {
|
|
1067
|
+
type: "array",
|
|
1068
|
+
items: { type: "string" },
|
|
1069
|
+
description: "Option values to select."
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
["ref", "values"]
|
|
1073
|
+
),
|
|
1074
|
+
proxy(
|
|
1075
|
+
"browser_fill_form",
|
|
1076
|
+
"fill_form",
|
|
1077
|
+
"Fill multiple form fields in one call. Each field is { ref, value }.",
|
|
1078
|
+
{
|
|
1079
|
+
fields: {
|
|
1080
|
+
type: "array",
|
|
1081
|
+
description: "Fields to fill.",
|
|
1082
|
+
items: {
|
|
1083
|
+
type: "object",
|
|
1084
|
+
properties: {
|
|
1085
|
+
ref: { type: "string", description: REF_DESC },
|
|
1086
|
+
value: { type: "string" }
|
|
1087
|
+
},
|
|
1088
|
+
required: ["ref", "value"]
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
["fields"]
|
|
1093
|
+
),
|
|
1094
|
+
proxy(
|
|
1095
|
+
"browser_press_key",
|
|
1096
|
+
"press_key",
|
|
1097
|
+
'Press a keyboard key (e.g. "Enter", "Escape", "ArrowDown", "Control+A").',
|
|
1098
|
+
{ key: { type: "string", description: "Key or chord to press." } },
|
|
1099
|
+
["key"]
|
|
1100
|
+
),
|
|
1101
|
+
proxy(
|
|
1102
|
+
"browser_go_back",
|
|
1103
|
+
"go_back",
|
|
1104
|
+
"Navigate back to the previous page in history. Returns a fresh snapshot.",
|
|
1105
|
+
{}
|
|
1106
|
+
),
|
|
1107
|
+
proxy(
|
|
1108
|
+
"browser_tabs",
|
|
1109
|
+
"tabs",
|
|
1110
|
+
'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).',
|
|
1111
|
+
{
|
|
1112
|
+
op: { type: "string", enum: ["list", "new", "select", "close"] },
|
|
1113
|
+
index: { type: "number", description: "Tab index for select/close." },
|
|
1114
|
+
url: { type: "string", description: "URL to open for op=new." }
|
|
1115
|
+
},
|
|
1116
|
+
["op"]
|
|
1117
|
+
),
|
|
1118
|
+
proxy(
|
|
1119
|
+
"browser_take_screenshot",
|
|
1120
|
+
"take_screenshot",
|
|
1121
|
+
"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.",
|
|
1122
|
+
{ full_page: { type: "boolean", description: "Capture the full scrollable page." } }
|
|
1123
|
+
),
|
|
1124
|
+
proxy(
|
|
1125
|
+
"browser_console",
|
|
1126
|
+
"console",
|
|
1127
|
+
"Return recent browser console messages (logs, warnings, errors).",
|
|
1128
|
+
{}
|
|
1129
|
+
),
|
|
1130
|
+
proxy(
|
|
1131
|
+
"browser_network",
|
|
1132
|
+
"network",
|
|
1133
|
+
"Return recent network requests issued by the page (method, status, URL).",
|
|
1134
|
+
{}
|
|
1135
|
+
)
|
|
1136
|
+
];
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// ../daemon/src/runtime/tools/deliver-file.ts
|
|
1140
|
+
import { readFileSync as readFileSync9, statSync as statSync4 } from "fs";
|
|
1141
|
+
import { basename as basename2, extname, isAbsolute, resolve as resolve3 } from "path";
|
|
1142
|
+
var MAX_DELIVER_BYTES = 25 * 1024 * 1024;
|
|
1143
|
+
var MIME = {
|
|
1144
|
+
".pdf": "application/pdf",
|
|
1145
|
+
".txt": "text/plain",
|
|
1146
|
+
".md": "text/markdown",
|
|
1147
|
+
".csv": "text/csv",
|
|
1148
|
+
".json": "application/json",
|
|
1149
|
+
".html": "text/html",
|
|
1150
|
+
".zip": "application/zip",
|
|
1151
|
+
".png": "image/png",
|
|
1152
|
+
".jpg": "image/jpeg",
|
|
1153
|
+
".jpeg": "image/jpeg",
|
|
1154
|
+
".gif": "image/gif",
|
|
1155
|
+
".webp": "image/webp"
|
|
1156
|
+
};
|
|
1157
|
+
function deliverFileTool(cwd, sink) {
|
|
1158
|
+
return {
|
|
1159
|
+
def: {
|
|
1160
|
+
name: "deliver_file",
|
|
1161
|
+
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.",
|
|
1162
|
+
parameters: {
|
|
1163
|
+
type: "object",
|
|
1164
|
+
properties: {
|
|
1165
|
+
path: {
|
|
1166
|
+
type: "string",
|
|
1167
|
+
description: "Path to the file, relative to your workspace or absolute."
|
|
1168
|
+
}
|
|
1169
|
+
},
|
|
1170
|
+
required: ["path"],
|
|
1171
|
+
additionalProperties: false
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
async invoke(args) {
|
|
1175
|
+
const p = String(args.path ?? "");
|
|
1176
|
+
if (!p) throw new Error("deliver_file: path is required");
|
|
1177
|
+
const abs = isAbsolute(p) ? p : resolve3(cwd, p);
|
|
1178
|
+
let size;
|
|
1179
|
+
try {
|
|
1180
|
+
size = statSync4(abs).size;
|
|
1181
|
+
} catch {
|
|
1182
|
+
throw new Error(`deliver_file: no such file: ${p}`);
|
|
1183
|
+
}
|
|
1184
|
+
if (size > MAX_DELIVER_BYTES) {
|
|
1185
|
+
throw new Error(
|
|
1186
|
+
`deliver_file: "${basename2(abs)}" is too large (${(size / 1024 / 1024).toFixed(1)} MB > 25 MB)`
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
const name = basename2(abs);
|
|
1190
|
+
const mimeType = MIME[extname(abs).toLowerCase()] ?? "application/octet-stream";
|
|
1191
|
+
sink({ name, mimeType, data: readFileSync9(abs).toString("base64") });
|
|
1192
|
+
return `Delivered "${name}" (${mimeType}) to the user.`;
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
|
|
925
1197
|
// ../daemon/src/runtime/tools/home.ts
|
|
926
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
927
|
-
import { join as
|
|
1198
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
1199
|
+
import { join as join14 } from "path";
|
|
928
1200
|
var HOME_FILES_READABLE = [
|
|
929
1201
|
"IDENTITY.md",
|
|
930
1202
|
"SOUL.md",
|
|
@@ -961,9 +1233,9 @@ function homeTools(agentDir) {
|
|
|
961
1233
|
`home_read: "file" must be one of ${HOME_FILES_READABLE.join(", ")}; got "${file}"`
|
|
962
1234
|
);
|
|
963
1235
|
}
|
|
964
|
-
const path =
|
|
1236
|
+
const path = join14(agentDir, file);
|
|
965
1237
|
try {
|
|
966
|
-
return
|
|
1238
|
+
return readFileSync10(path, "utf8");
|
|
967
1239
|
} catch (err) {
|
|
968
1240
|
const msg = err instanceof Error ? err.message : String(err);
|
|
969
1241
|
throw new Error(`home_read: could not read ${file}: ${msg}`);
|
|
@@ -991,8 +1263,8 @@ function homeTools(agentDir) {
|
|
|
991
1263
|
);
|
|
992
1264
|
}
|
|
993
1265
|
const content = typeof args.content === "string" ? args.content : "";
|
|
994
|
-
const path =
|
|
995
|
-
|
|
1266
|
+
const path = join14(agentDir, file);
|
|
1267
|
+
writeFileSync6(path, content, "utf8");
|
|
996
1268
|
return `wrote ${file} (${Buffer.byteLength(content, "utf8")} bytes)`;
|
|
997
1269
|
}
|
|
998
1270
|
},
|
|
@@ -1005,9 +1277,9 @@ function homeTools(agentDir) {
|
|
|
1005
1277
|
async invoke() {
|
|
1006
1278
|
const entries = [];
|
|
1007
1279
|
for (const file of HOME_FILES_READABLE) {
|
|
1008
|
-
const path =
|
|
1280
|
+
const path = join14(agentDir, file);
|
|
1009
1281
|
try {
|
|
1010
|
-
const s =
|
|
1282
|
+
const s = statSync5(path);
|
|
1011
1283
|
entries.push(`${file} (${s.size}b)`);
|
|
1012
1284
|
} catch {
|
|
1013
1285
|
}
|
|
@@ -1028,6 +1300,18 @@ function homeTools(agentDir) {
|
|
|
1028
1300
|
];
|
|
1029
1301
|
}
|
|
1030
1302
|
|
|
1303
|
+
// ../daemon/src/runtime/tools/mcp.ts
|
|
1304
|
+
function mcpProxyTools(host, tools) {
|
|
1305
|
+
return tools.map((t) => ({
|
|
1306
|
+
def: {
|
|
1307
|
+
name: t.toolName,
|
|
1308
|
+
description: t.description,
|
|
1309
|
+
parameters: t.inputSchema
|
|
1310
|
+
},
|
|
1311
|
+
invoke: (args) => host.invoke(t.serverId, t.rawName, args)
|
|
1312
|
+
}));
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1031
1315
|
// ../daemon/src/runtime/tools/memory.ts
|
|
1032
1316
|
function memoryTools(memory) {
|
|
1033
1317
|
return [
|
|
@@ -1853,14 +2137,20 @@ function ourToolToPiTool(h) {
|
|
|
1853
2137
|
description: h.def.description,
|
|
1854
2138
|
parameters: Type2.Unsafe(h.def.parameters),
|
|
1855
2139
|
async execute(_toolCallId, params) {
|
|
1856
|
-
const
|
|
2140
|
+
const out = await h.invoke(params);
|
|
1857
2141
|
return {
|
|
1858
|
-
content:
|
|
2142
|
+
content: toPiContent(out),
|
|
1859
2143
|
details: {}
|
|
1860
2144
|
};
|
|
1861
2145
|
}
|
|
1862
2146
|
};
|
|
1863
2147
|
}
|
|
2148
|
+
function toPiContent(out) {
|
|
2149
|
+
if (typeof out === "string") return [{ type: "text", text: out }];
|
|
2150
|
+
return out.map(
|
|
2151
|
+
(p) => p.type === "text" ? { type: "text", text: p.text } : { type: "image", data: p.data, mimeType: p.mimeType }
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
1864
2154
|
function createBazilionCustomTools(opts) {
|
|
1865
2155
|
const handlers = [
|
|
1866
2156
|
...memoryTools(opts.memory),
|
|
@@ -1874,13 +2164,35 @@ function createBazilionCustomTools(opts) {
|
|
|
1874
2164
|
if (opts.userMdHost) {
|
|
1875
2165
|
handlers.push(...userMdTools(opts.userMdHost, opts.agent.group.id));
|
|
1876
2166
|
}
|
|
2167
|
+
if (opts.browserHost) {
|
|
2168
|
+
handlers.push(...browserTools(opts.browserHost, opts.agent.agent.id));
|
|
2169
|
+
}
|
|
2170
|
+
if (opts.mcpHost && opts.mcpTools && opts.mcpTools.length > 0) {
|
|
2171
|
+
handlers.push(...mcpProxyTools(opts.mcpHost, opts.mcpTools));
|
|
2172
|
+
}
|
|
2173
|
+
if (opts.fileSink) {
|
|
2174
|
+
handlers.push(deliverFileTool(opts.agent.group.path, opts.fileSink));
|
|
2175
|
+
}
|
|
1877
2176
|
return handlers.map(ourToolToPiTool);
|
|
1878
2177
|
}
|
|
1879
2178
|
|
|
1880
2179
|
// ../daemon/src/runtime/pi/session.ts
|
|
1881
2180
|
var BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
1882
2181
|
async function createBazilionSession(opts) {
|
|
1883
|
-
const {
|
|
2182
|
+
const {
|
|
2183
|
+
agent,
|
|
2184
|
+
paths,
|
|
2185
|
+
env,
|
|
2186
|
+
memory,
|
|
2187
|
+
enabledProviders,
|
|
2188
|
+
messagingHost,
|
|
2189
|
+
userMdHost,
|
|
2190
|
+
browserHost,
|
|
2191
|
+
mcpHost,
|
|
2192
|
+
mcpTools,
|
|
2193
|
+
fileSink,
|
|
2194
|
+
refreshApiKey
|
|
2195
|
+
} = opts;
|
|
1884
2196
|
const { providerName, modelId } = splitModelString(agent.model);
|
|
1885
2197
|
if (enabledProviders.size > 0 && !enabledProviders.has(providerName)) {
|
|
1886
2198
|
throw new Error(`${providerName} provider is disabled \u2014 enable it on the /config page`);
|
|
@@ -1910,8 +2222,8 @@ async function createBazilionSession(opts) {
|
|
|
1910
2222
|
});
|
|
1911
2223
|
}
|
|
1912
2224
|
const cwd = agent.group.path;
|
|
1913
|
-
if (!
|
|
1914
|
-
const sessionDir =
|
|
2225
|
+
if (!existsSync12(cwd)) mkdirSync5(cwd, { recursive: true });
|
|
2226
|
+
const sessionDir = join15(paths.agentDir(agent.agent.id), "sessions");
|
|
1915
2227
|
mkdirSync5(sessionDir, { recursive: true });
|
|
1916
2228
|
const existing = findMostRecent(sessionDir);
|
|
1917
2229
|
const sessionManager = existing ? SessionManager.open(existing, sessionDir, cwd) : SessionManager.create(cwd, sessionDir);
|
|
@@ -1927,11 +2239,21 @@ async function createBazilionSession(opts) {
|
|
|
1927
2239
|
const bazilionPrompt = buildSystemPrompt(agent);
|
|
1928
2240
|
const resourceLoader = createBazilionResourceLoader(bazilionPrompt);
|
|
1929
2241
|
await resourceLoader.reload();
|
|
1930
|
-
const customTools = createBazilionCustomTools({
|
|
2242
|
+
const customTools = createBazilionCustomTools({
|
|
2243
|
+
agent,
|
|
2244
|
+
memory,
|
|
2245
|
+
messagingHost,
|
|
2246
|
+
userMdHost,
|
|
2247
|
+
browserHost,
|
|
2248
|
+
mcpHost,
|
|
2249
|
+
mcpTools,
|
|
2250
|
+
fileSink,
|
|
2251
|
+
env
|
|
2252
|
+
});
|
|
1931
2253
|
const allowedTools = [...BUILTIN_TOOL_NAMES, ...customTools.map((t) => t.name)];
|
|
1932
2254
|
const { session } = await createAgentSession({
|
|
1933
2255
|
cwd,
|
|
1934
|
-
agentDir:
|
|
2256
|
+
agentDir: join15(paths.home, "pi"),
|
|
1935
2257
|
model,
|
|
1936
2258
|
thinkingLevel: toPiThinkingLevel(agent.reasoningLevel),
|
|
1937
2259
|
tools: allowedTools,
|
|
@@ -2057,13 +2379,13 @@ function createBazilionResourceLoader(appendSystemPrompt) {
|
|
|
2057
2379
|
};
|
|
2058
2380
|
}
|
|
2059
2381
|
function findMostRecent(sessionDir) {
|
|
2060
|
-
if (!
|
|
2382
|
+
if (!existsSync12(sessionDir)) return null;
|
|
2061
2383
|
let newest = null;
|
|
2062
2384
|
for (const entry of readdirSync7(sessionDir)) {
|
|
2063
2385
|
if (!entry.endsWith(".jsonl")) continue;
|
|
2064
|
-
const path =
|
|
2386
|
+
const path = join15(sessionDir, entry);
|
|
2065
2387
|
try {
|
|
2066
|
-
const s =
|
|
2388
|
+
const s = statSync6(path);
|
|
2067
2389
|
if (!newest || s.mtimeMs > newest.mtimeMs) newest = { path, mtimeMs: s.mtimeMs };
|
|
2068
2390
|
} catch {
|
|
2069
2391
|
}
|
|
@@ -2105,10 +2427,10 @@ function createIpcClient() {
|
|
|
2105
2427
|
if (!process.send) {
|
|
2106
2428
|
return Promise.reject(new Error("worker: no IPC channel \u2014 daemon must spawn with stdio:ipc"));
|
|
2107
2429
|
}
|
|
2108
|
-
const id =
|
|
2430
|
+
const id = randomUUID6();
|
|
2109
2431
|
const message = { type: "rpc", id, method, args };
|
|
2110
|
-
return new Promise((
|
|
2111
|
-
pending.set(id, { resolve: (v) =>
|
|
2432
|
+
return new Promise((resolve4, reject) => {
|
|
2433
|
+
pending.set(id, { resolve: (v) => resolve4(v), reject });
|
|
2112
2434
|
process.send?.(message, void 0, void 0, (err) => {
|
|
2113
2435
|
if (err) {
|
|
2114
2436
|
pending.delete(id);
|
|
@@ -2135,6 +2457,16 @@ function createIpcUserMdHost(call) {
|
|
|
2135
2457
|
write: (groupId, content, ifMatch) => call("userMdWrite", { groupId, content, ifMatch })
|
|
2136
2458
|
};
|
|
2137
2459
|
}
|
|
2460
|
+
function createIpcBrowserHost(call) {
|
|
2461
|
+
return {
|
|
2462
|
+
invoke: (agentId, action, args) => call("browserInvoke", { agentId, action, args })
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
function createIpcMcpHost(call) {
|
|
2466
|
+
return {
|
|
2467
|
+
invoke: (serverId, toolName, args) => call("mcpInvoke", { serverId, toolName, args })
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2138
2470
|
function isIpcReply(msg) {
|
|
2139
2471
|
if (!msg || typeof msg !== "object") return false;
|
|
2140
2472
|
const m = msg;
|
|
@@ -2157,13 +2489,15 @@ async function main() {
|
|
|
2157
2489
|
};
|
|
2158
2490
|
process.on("SIGTERM", onSignal);
|
|
2159
2491
|
process.on("SIGINT", onSignal);
|
|
2160
|
-
const { agent, message, enabledProviders, apiKey } = await readInput();
|
|
2492
|
+
const { agent, message, enabledProviders, apiKey, browserEnabled, mcpTools, images } = await readInput();
|
|
2161
2493
|
const paths = resolvePaths();
|
|
2162
|
-
const memory = qmdBackend(
|
|
2494
|
+
const memory = qmdBackend(join16(agent.group.path, "memory"));
|
|
2163
2495
|
await memory.init();
|
|
2164
2496
|
const ipcCall = createIpcClient();
|
|
2165
2497
|
const messagingHost = createIpcMessagingHost(ipcCall);
|
|
2166
2498
|
const userMdHost = createIpcUserMdHost(ipcCall);
|
|
2499
|
+
const browserHost = browserEnabled ? createIpcBrowserHost(ipcCall) : void 0;
|
|
2500
|
+
const mcpHost = mcpTools && mcpTools.length > 0 ? createIpcMcpHost(ipcCall) : void 0;
|
|
2167
2501
|
const { session, dispose } = await createBazilionSession({
|
|
2168
2502
|
agent,
|
|
2169
2503
|
paths,
|
|
@@ -2172,7 +2506,12 @@ async function main() {
|
|
|
2172
2506
|
enabledProviders: new Set(enabledProviders),
|
|
2173
2507
|
messagingHost,
|
|
2174
2508
|
userMdHost,
|
|
2175
|
-
apiKey
|
|
2509
|
+
apiKey,
|
|
2510
|
+
browserHost,
|
|
2511
|
+
mcpHost,
|
|
2512
|
+
mcpTools,
|
|
2513
|
+
// deliver_file emits a `file` event straight onto our stdout frame stream.
|
|
2514
|
+
fileSink: (f) => emit({ kind: "event", event: { type: "file", ...f } })
|
|
2176
2515
|
});
|
|
2177
2516
|
abortSession = () => {
|
|
2178
2517
|
void session.abort();
|
|
@@ -2183,7 +2522,12 @@ async function main() {
|
|
|
2183
2522
|
}
|
|
2184
2523
|
});
|
|
2185
2524
|
try {
|
|
2186
|
-
|
|
2525
|
+
const promptImages = (images ?? []).map((img) => ({
|
|
2526
|
+
type: "image",
|
|
2527
|
+
data: img.data,
|
|
2528
|
+
mimeType: img.mimeType
|
|
2529
|
+
}));
|
|
2530
|
+
await session.prompt(message, promptImages.length > 0 ? { images: promptImages } : void 0);
|
|
2187
2531
|
await session.agent.waitForIdle();
|
|
2188
2532
|
emit({
|
|
2189
2533
|
kind: "done",
|