@inetafrica/open-claudia 3.5.26 → 3.5.28
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 +7 -0
- package/core/browser-control.js +55 -1
- package/package.json +1 -1
- package/test-provider-language.js +5 -1
- package/web.js +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.5.28
|
|
4
|
+
|
|
5
|
+
The Browser app is always on: opening it while the bot is not browsing opens the bot's browser, instead of a dead end (Sumeet, 10 Oct: "half the time i have to ask you switch on the browser"; 11 Oct 07:43 EAT network-mon's Browser showed only "isn't open right now"). A canary build for the Browser canary bot, on 3.5.26; it does not carry 3.5.27 (calls, Sumeet's own bot).
|
|
6
|
+
|
|
7
|
+
- **`POST /api/browser/start`** (Agent Space's control token only, like every Browser app route). With no browser open, the bot's own default session opens on a start page (`https://www.google.com/`), with the bot's own agent-browser environment and socket folder (`AGENT_BROWSER_SOCKET_DIR`, else `~/.agent-browser`), exactly as its own `agent-browser open` would, so the bot carries on in that tab. A browser already open is answered as it is (the newest session), and two people opening the app at once start one browser. `GET /api/browser/control` says `start: true`; Agent Space's front door (223b978) asks only bots that say so.
|
|
8
|
+
- **Tests:** `test-browser-control.js`, `test-web-browser-app.js`.
|
|
9
|
+
|
|
3
10
|
## 3.5.26
|
|
4
11
|
|
|
5
12
|
The Browser app's phone and web view stays sharp after a page load. A canary build for the Browser canary bot first; the fleet waits for Sumeet's OK.
|
package/core/browser-control.js
CHANGED
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
// makes the bot's tab take its size, phone layout and touch, and then the driver's input goes
|
|
18
18
|
// straight to Chrome through that view (typed text too: input_text). Only with Chrome in its
|
|
19
19
|
// own container; elsewhere the browser keeps its size and input goes through agent-browser.
|
|
20
|
+
// - always on: a person who opens the Browser app while the bot's browser is not open gets it
|
|
21
|
+
// opened on a start page, in the bot's own default session (the one its `agent-browser open`
|
|
22
|
+
// uses), with the bot's own agent-browser environment, so the bot carries on in that tab.
|
|
20
23
|
// - hand back (release, a closed panel, or a lock found expired by the 30 s sweeper): the lock
|
|
21
24
|
// goes, the view and the socket close, any recording stops, and the bot is woken in the
|
|
22
25
|
// person's own chat (C6), but only when it waits for them: it sent them an Open Browser card,
|
|
@@ -26,6 +29,8 @@
|
|
|
26
29
|
// Every route answers { status, body }; web.js writes it. Agent Space reaches these routes with
|
|
27
30
|
// the control token only.
|
|
28
31
|
|
|
32
|
+
const os = require("os");
|
|
33
|
+
const path = require("path");
|
|
29
34
|
const WebSocket = require("ws");
|
|
30
35
|
const lockFile = require("./browser-lock");
|
|
31
36
|
const view = require("./browser-view");
|
|
@@ -48,6 +53,10 @@ const ID_RE = /^[A-Za-z0-9._:-]{1,100}$/;
|
|
|
48
53
|
// An address-bar command still running after this is killed, and the person told it failed.
|
|
49
54
|
const NAVIGATE_TIMEOUT_MS = 20 * 1000;
|
|
50
55
|
const NAVIGATIONS = new Set(["open", "back", "forward", "reload"]);
|
|
56
|
+
// Always on: the page a browser opened for a person starts on, and the session it opens (the one
|
|
57
|
+
// the bot's own `agent-browser` commands use when they name none).
|
|
58
|
+
const START_PAGE = "https://www.google.com/";
|
|
59
|
+
const START_SESSION = "default";
|
|
51
60
|
|
|
52
61
|
// An Open Browser card counts as a request for a person this long.
|
|
53
62
|
const REQUEST_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
@@ -521,7 +530,8 @@ async function control(body = {}) {
|
|
|
521
530
|
}
|
|
522
531
|
|
|
523
532
|
// navigate: true tells the front door this bot has the address bar (POST /api/browser/navigate);
|
|
524
|
-
// fit: true that a take can bring the driver's view (core/browser-drive.js) and input_text works
|
|
533
|
+
// fit: true that a take can bring the driver's view (core/browser-drive.js) and input_text works;
|
|
534
|
+
// start: true that it opens its browser on a start page when there is none (POST /api/browser/start).
|
|
525
535
|
function status() {
|
|
526
536
|
const lock = lockFile.liveLock(deps.now());
|
|
527
537
|
return reply(200, {
|
|
@@ -530,9 +540,49 @@ function status() {
|
|
|
530
540
|
recording: teach.activeSummary(),
|
|
531
541
|
navigate: true,
|
|
532
542
|
fit: drive.supported(),
|
|
543
|
+
start: true,
|
|
533
544
|
});
|
|
534
545
|
}
|
|
535
546
|
|
|
547
|
+
// ---------------------------------------------------------------------------
|
|
548
|
+
// Always on: POST /api/browser/start
|
|
549
|
+
|
|
550
|
+
// The socket folder the bot's own agent-browser commands use (its agent runs without
|
|
551
|
+
// XDG_RUNTIME_DIR): AGENT_BROWSER_SOCKET_DIR, else ~/.agent-browser.
|
|
552
|
+
function agentSocketDir(env = process.env, home = os.homedir()) {
|
|
553
|
+
if (env.AGENT_BROWSER_SOCKET_DIR) return env.AGENT_BROWSER_SOCKET_DIR;
|
|
554
|
+
return home ? path.join(home, ".agent-browser") : null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
let starting = null;
|
|
558
|
+
|
|
559
|
+
// { who: { userId } }. A browser already open is answered as it is (the newest session); otherwise
|
|
560
|
+
// the bot's default session opens on the start page, as the bot's own `agent-browser open` would,
|
|
561
|
+
// and two people opening the app at once start one browser. { session, started }.
|
|
562
|
+
async function start({ who } = {}) {
|
|
563
|
+
if (!validWho(who)) return reply(400, { error: "bad-request" });
|
|
564
|
+
const open = deps.listSessions();
|
|
565
|
+
if (open.length) return reply(200, { session: open[0].name, started: false });
|
|
566
|
+
if (!starting) {
|
|
567
|
+
starting = (async () => {
|
|
568
|
+
const dir = agentSocketDir();
|
|
569
|
+
const run = await runAgentBrowser({ name: START_SESSION, dir }, ["open", START_PAGE], {
|
|
570
|
+
timeoutMs: deps.navigateTimeoutMs,
|
|
571
|
+
execFile: deps.execFile || undefined,
|
|
572
|
+
});
|
|
573
|
+
const listed = run.ok ? deps.listSessions().find((s) => s.name === START_SESSION) : null;
|
|
574
|
+
if (!listed) {
|
|
575
|
+
const why = run.timedOut ? `agent-browser took longer than ${Math.ceil(deps.navigateTimeoutMs / 1000)} s and was stopped` : run.ok ? "the browser did not come up" : "agent-browser failed";
|
|
576
|
+
console.error(`[browser] could not open the browser for ${who.userId}: ${why}`);
|
|
577
|
+
return reply(503, { error: "browser-unavailable" });
|
|
578
|
+
}
|
|
579
|
+
console.log(`[browser] opened the browser on the start page for ${who.userId}`);
|
|
580
|
+
return reply(200, { session: listed.name, started: true });
|
|
581
|
+
})().finally(() => { starting = null; });
|
|
582
|
+
}
|
|
583
|
+
return starting;
|
|
584
|
+
}
|
|
585
|
+
|
|
536
586
|
// ---------------------------------------------------------------------------
|
|
537
587
|
// C3: input
|
|
538
588
|
|
|
@@ -738,7 +788,10 @@ module.exports = {
|
|
|
738
788
|
LOCK_TTL_MS,
|
|
739
789
|
MAX_EVENTS,
|
|
740
790
|
NAVIGATE_TIMEOUT_MS,
|
|
791
|
+
START_PAGE,
|
|
792
|
+
START_SESSION,
|
|
741
793
|
SWEEP_INTERVAL_MS,
|
|
794
|
+
agentSocketDir,
|
|
742
795
|
checkEvent,
|
|
743
796
|
chatOrigin,
|
|
744
797
|
control,
|
|
@@ -748,6 +801,7 @@ module.exports = {
|
|
|
748
801
|
navigate,
|
|
749
802
|
release,
|
|
750
803
|
renew,
|
|
804
|
+
start,
|
|
751
805
|
startSweeper,
|
|
752
806
|
status,
|
|
753
807
|
sweepLock,
|
package/package.json
CHANGED
|
@@ -617,7 +617,11 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
617
617
|
// only a waiting bot. A canary build for the Browser canary bot; the fleet waits for his OK.
|
|
618
618
|
// 3.5.26: the same go-ahead; 3.5.25's pictures fell back to 1x after a page load on the
|
|
619
619
|
// canary's Chrome 150, now nudged back to the view's density. Canary first, fleet on his OK.
|
|
620
|
-
|
|
620
|
+
// 3.5.28 approved under Sumeet's 2026-10-11 08:21 EAT go-ahead ("Yeah, that's fine") to the
|
|
621
|
+
// resize fix and Chrome-style bar first, then the always-on browser with one restart of the
|
|
622
|
+
// Browser canary bot: the Browser app opens the bot's browser when none is open. A canary build
|
|
623
|
+
// for the Browser canary bot on 3.5.26; it does not carry 3.5.27 (calls, his own bot only).
|
|
624
|
+
assert.strictEqual(pkg.version, "3.5.28", "release version must remain unchanged without explicit approval");
|
|
621
625
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
622
626
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
623
627
|
}
|
package/web.js
CHANGED
|
@@ -382,9 +382,11 @@ const USER_ACCESS_ROUTES = new Set(["/api/auth/approve", "/api/auth/deny", "/api
|
|
|
382
382
|
// POST /api/browser/navigate the driver's address bar: open <url>
|
|
383
383
|
// (public addresses only), back,
|
|
384
384
|
// forward, reload
|
|
385
|
+
// POST /api/browser/start always on: open the bot's browser on
|
|
386
|
+
// a start page when none is open
|
|
385
387
|
// POST /api/browser/teach Teach me: start / stop / cancel (C4)
|
|
386
388
|
// GET /api/browser/teach[/<id>[/pictures/<n>]]?userId= that person's recordings only
|
|
387
|
-
const BROWSER_APP_ROUTE = /^\/api\/(?:apps\/check|browser\/(?:control|input|navigate|teach))(?:[/?]|$)/;
|
|
389
|
+
const BROWSER_APP_ROUTE = /^\/api\/(?:apps\/check|browser\/(?:control|input|navigate|start|teach))(?:[/?]|$)/;
|
|
388
390
|
const BROWSER_APP_MAX_BODY = 64 * 1024;
|
|
389
391
|
const BROWSER_APP_USER_ID = /^[A-Za-z0-9._:-]{1,100}$/;
|
|
390
392
|
|
|
@@ -408,6 +410,7 @@ async function handleBrowserApp(req, res, body) {
|
|
|
408
410
|
if (route === "/api/browser/control" && req.method === "GET") return answer(control.status());
|
|
409
411
|
if (route === "/api/browser/input" && req.method === "POST") return answer(await control.input(parsed));
|
|
410
412
|
if (route === "/api/browser/navigate" && req.method === "POST") return answer(await control.navigate(parsed));
|
|
413
|
+
if (route === "/api/browser/start" && req.method === "POST") return answer(await control.start(parsed));
|
|
411
414
|
if (route === "/api/browser/teach" && req.method === "POST") return answer(await control.teachAction(parsed));
|
|
412
415
|
if (req.method === "GET" && (route === "/api/browser/teach" || route.startsWith("/api/browser/teach/"))) {
|
|
413
416
|
const teach = require("./core/browser-teach");
|