thebird 1.2.33 → 1.2.35

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 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/CLAUDE.md CHANGED
@@ -130,6 +130,9 @@ Run examples against real Gemini API to validate message translation.
130
130
  - Some models have different tool naming conventions — check provider docs
131
131
  - Streaming response parsing varies by provider — see lib/providers/ for details
132
132
  - OAuth tokens expire — gembird uses browser session instead of capturing tokens
133
+ - Cannot bundle index.js directly for browser — it imports Node-only modules (oauth.js, config.js, cloud-generate.js) at top level. Create a separate browser entry that imports only lib/client.js, lib/errors.js, lib/convert.js. Use ESM wrapper (not CJS module.exports) to preserve named exports in bundle.
134
+ - Tool parameter types must be lowercase for Gemini API — `object`, `string`, `number` not `OBJECT`, `STRING`, `NUMBER`. Uppercase types fail schema validation.
135
+ - agentGenerate passes raw Anthropic-format messages to streamGemini internally, which calls convertMessages. Do NOT pre-convert in app.js — double-conversion breaks tool schemas.
133
136
 
134
137
  ## Files
135
138
 
@@ -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 (!c) throw new Error('container not ready');
10
- return await c.fs.readFile(path, 'utf-8');
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 (!c) throw new Error('container not ready');
19
- await c.fs.writeFile(path, content);
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') onTool(ev.toolName, ev.args);
46
- else if (ev.type === 'error') throw ev.error;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thebird",
3
- "version": "1.2.33",
3
+ "version": "1.2.35",
4
4
  "description": "Anthropic SDK to Gemini streaming bridge — drop-in proxy that translates Anthropic message format and tool calls to Google Gemini",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",