thebird 1.2.34 → 1.2.36
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/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/docs/agent-chat.js +60 -8
- package/docs/terminal.js +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [Unreleased - agent-tools]
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- `docs/agent-chat.js`: 3 new tools — `list_files` (IDB snapshot keys), `read_terminal` (xterm buffer snapshot, last N lines), `send_to_terminal` (write to jsh stdin via shellWriter); `read_file` falls back to IDB snapshot when container not ready; `write_file` writes to both container and IDB snapshot; `window.__debug.agent.lastTool` tracks last dispatched tool
|
|
5
|
+
- `docs/terminal.js`: exposes `window.__debug.shellWriter` (jsh stdin writer), `window.__debug.idbSnapshot` (live file map), `window.__debug.idbPersist` (persist snapshot to IndexedDB)
|
|
6
|
+
|
|
1
7
|
## [Unreleased - browser-sdk]
|
|
2
8
|
|
|
3
9
|
### Added
|
package/README.md
CHANGED
|
@@ -182,7 +182,7 @@ Messages follow the Anthropic SDK format. All image block variants are supported
|
|
|
182
182
|
|
|
183
183
|
Live at **[anentrypoint.github.io/thebird](https://anentrypoint.github.io/thebird/)**
|
|
184
184
|
|
|
185
|
-
- **Chat tab** — Agentic chat powered by thebird `streamGemini` running in-browser (bundled in `docs/vendor/thebird-browser.js`).
|
|
185
|
+
- **Chat tab** — Agentic chat powered by thebird `streamGemini` running in-browser (bundled in `docs/vendor/thebird-browser.js`). Tools: `read_file`, `write_file`, `list_files` (IDB-backed, no container required), `run_command` (WebContainer shell), `read_terminal` (xterm buffer snapshot), `send_to_terminal` (jsh stdin). No proxy server required. Gemini API key stored in localStorage.
|
|
186
186
|
- **Terminal tab** — WebContainer (in-browser Node.js) booting thebird's full stack: `npm install`, `node server.js` (Anthropic→Gemini proxy on port 3000), then a `jsh` shell. Run Agent button validates the agent loop from the terminal.
|
|
187
187
|
- **Preview tab** — iframe pointed at the WebContainer's HTTP server, live-updated when the server starts
|
|
188
188
|
|
package/docs/agent-chat.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import { streamGemini } from './vendor/thebird-browser.js';
|
|
2
2
|
|
|
3
|
+
function idbRead(path) {
|
|
4
|
+
const snap = window.__debug.idbSnapshot;
|
|
5
|
+
if (!snap) throw new Error('idb snapshot not ready');
|
|
6
|
+
if (!(path in snap)) throw new Error('not found in snapshot: ' + path);
|
|
7
|
+
return snap[path];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function idbWrite(path, content) {
|
|
11
|
+
const snap = window.__debug.idbSnapshot;
|
|
12
|
+
if (!snap) throw new Error('idb snapshot not ready');
|
|
13
|
+
snap[path] = content;
|
|
14
|
+
window.__debug.idbPersist?.();
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
const TOOLS = {
|
|
4
18
|
read_file: {
|
|
5
19
|
description: 'Read a file from the filesystem',
|
|
6
20
|
parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
7
21
|
execute: async ({ path }) => {
|
|
8
22
|
const c = window.__debug.container;
|
|
9
|
-
if (
|
|
10
|
-
return
|
|
23
|
+
if (c) return await c.fs.readFile(path, 'utf-8');
|
|
24
|
+
return idbRead(path);
|
|
11
25
|
},
|
|
12
26
|
},
|
|
13
27
|
write_file: {
|
|
@@ -15,13 +29,22 @@ const TOOLS = {
|
|
|
15
29
|
parameters: { type: 'object', properties: { path: { type: 'string' }, content: { type: 'string' } }, required: ['path', 'content'] },
|
|
16
30
|
execute: async ({ path, content }) => {
|
|
17
31
|
const c = window.__debug.container;
|
|
18
|
-
if (
|
|
19
|
-
|
|
32
|
+
if (c) await c.fs.writeFile(path, content);
|
|
33
|
+
idbWrite(path, content);
|
|
20
34
|
return 'written: ' + path;
|
|
21
35
|
},
|
|
22
36
|
},
|
|
37
|
+
list_files: {
|
|
38
|
+
description: 'List files available in the filesystem',
|
|
39
|
+
parameters: { type: 'object', properties: { prefix: { type: 'string' } }, required: [] },
|
|
40
|
+
execute: async ({ prefix }) => {
|
|
41
|
+
const snap = window.__debug.idbSnapshot || {};
|
|
42
|
+
const keys = Object.keys(snap).sort();
|
|
43
|
+
return (prefix ? keys.filter(k => k.startsWith(prefix)) : keys).join('\n') || '(empty)';
|
|
44
|
+
},
|
|
45
|
+
},
|
|
23
46
|
run_command: {
|
|
24
|
-
description: 'Run a shell command',
|
|
47
|
+
description: 'Run a shell command in the WebContainer',
|
|
25
48
|
parameters: { type: 'object', properties: { command: { type: 'string' }, cwd: { type: 'string' } }, required: ['command'] },
|
|
26
49
|
execute: async ({ command, cwd }) => {
|
|
27
50
|
const c = window.__debug.container;
|
|
@@ -33,17 +56,46 @@ const TOOLS = {
|
|
|
33
56
|
return out || '(no output)';
|
|
34
57
|
},
|
|
35
58
|
},
|
|
59
|
+
read_terminal: {
|
|
60
|
+
description: 'Read the current terminal display (last N lines)',
|
|
61
|
+
parameters: { type: 'object', properties: { lines: { type: 'number' } }, required: [] },
|
|
62
|
+
execute: async ({ lines = 50 }) => {
|
|
63
|
+
const term = window.__debug.term;
|
|
64
|
+
if (!term) throw new Error('terminal not ready');
|
|
65
|
+
const buf = term.buffer.active;
|
|
66
|
+
const end = buf.length;
|
|
67
|
+
const start = Math.max(0, end - lines);
|
|
68
|
+
const out = [];
|
|
69
|
+
for (let y = start; y < end; y++) {
|
|
70
|
+
const line = buf.getLine(y);
|
|
71
|
+
if (line) out.push(line.translateToString(true));
|
|
72
|
+
}
|
|
73
|
+
return out.join('\n').trimEnd() || '(empty)';
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
send_to_terminal: {
|
|
77
|
+
description: 'Send input to the terminal shell (use \\n for newline/Enter)',
|
|
78
|
+
parameters: { type: 'object', properties: { input: { type: 'string' } }, required: ['input'] },
|
|
79
|
+
execute: async ({ input }) => {
|
|
80
|
+
const w = window.__debug.shellWriter;
|
|
81
|
+
if (!w) throw new Error('shell not ready');
|
|
82
|
+
await w.write(input);
|
|
83
|
+
return 'sent: ' + JSON.stringify(input);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
36
86
|
};
|
|
37
87
|
|
|
38
88
|
export async function agentGenerate(apiKey, model, messages, onChunk, onTool) {
|
|
39
89
|
Object.assign(window.__debug = window.__debug || {}, {
|
|
40
|
-
agent: { model, active: true },
|
|
90
|
+
agent: { model, active: true, lastTool: null },
|
|
41
91
|
});
|
|
42
92
|
try {
|
|
43
93
|
for await (const ev of streamGemini({ model, messages, tools: TOOLS, apiKey, maxOutputTokens: 8192 }).fullStream) {
|
|
44
94
|
if (ev.type === 'text-delta') onChunk(ev.textDelta);
|
|
45
|
-
else if (ev.type === 'tool-call')
|
|
46
|
-
|
|
95
|
+
else if (ev.type === 'tool-call') {
|
|
96
|
+
window.__debug.agent.lastTool = { name: ev.toolName, args: ev.args };
|
|
97
|
+
onTool(ev.toolName, ev.args);
|
|
98
|
+
} else if (ev.type === 'error') throw ev.error;
|
|
47
99
|
}
|
|
48
100
|
} finally {
|
|
49
101
|
window.__debug.agent.active = false;
|
package/docs/terminal.js
CHANGED
|
@@ -114,6 +114,9 @@ async function boot() {
|
|
|
114
114
|
window.__debug.term = term;
|
|
115
115
|
window.__debug.previewUrl = null;
|
|
116
116
|
window.__debug.shell = shell;
|
|
117
|
+
window.__debug.shellWriter = writer;
|
|
118
|
+
window.__debug.idbSnapshot = files;
|
|
119
|
+
window.__debug.idbPersist = () => idbSave(JSON.stringify(window.__debug.idbSnapshot));
|
|
117
120
|
window.__debug.srv = srv;
|
|
118
121
|
window.__debug.validation = null;
|
|
119
122
|
window.__debug.runAgent = async (key, task) => {
|
package/package.json
CHANGED