@poncho-ai/harness 0.59.16 → 0.59.18
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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +32 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +12 -5
- package/package.json +1 -1
- package/src/config.ts +15 -0
- package/src/harness.ts +23 -6
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/harness@0.59.
|
|
2
|
+
> @poncho-ai/harness@0.59.18 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
|
|
3
3
|
> node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[embed-docs] Generated poncho-docs.ts with 4 topics
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
[34mCLI[39m tsup v8.5.1
|
|
9
9
|
[34mCLI[39m Target: es2022
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
11
|
+
[32mESM[39m [1mdist/index.js [22m[32m572.34 KB[39m
|
|
12
12
|
[32mESM[39m [1mdist/isolate-F2PPSUL6.js [22m[32m53.82 KB[39m
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 214ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 7534ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m104.01 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @poncho-ai/harness
|
|
2
2
|
|
|
3
|
+
## 0.59.18
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#184](https://github.com/cesr/poncho-ai/pull/184) [`12ce2be`](https://github.com/cesr/poncho-ai/commit/12ce2be01c9d98b1d9aa634d4d8051c4c0094a44) Thanks [@cesr](https://github.com/cesr)! - Add `browser_download` so the agent can save files from the browser into the
|
|
8
|
+
VFS. The tool fetches a file using the page's logged-in session (so it works
|
|
9
|
+
for files behind a login) and writes the bytes straight to the tenant's VFS via
|
|
10
|
+
`ToolContext.vfs` — never through the model. `url` defaults to the current page,
|
|
11
|
+
or pass a same-origin link's href. The fetch runs inside the page (`evaluate`),
|
|
12
|
+
so it works identically for local and remote/cloud browsers (bytes return over
|
|
13
|
+
CDP). Capped at 25 MB. The harness browser system prompt now documents it under
|
|
14
|
+
a "Saving files" section.
|
|
15
|
+
|
|
16
|
+
## 0.59.17
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [`963a0e3`](https://github.com/cesr/poncho-ai/commit/963a0e34b9f362ab21b7b8d94835f37713d4e5da) Thanks [@cesr](https://github.com/cesr)! - Browser config: support embedding apps that own per-tenant browser sessions
|
|
21
|
+
and the live viewport.
|
|
22
|
+
- `browser.sessionName` now overrides the browser session id (previously
|
|
23
|
+
always `poncho-${agentId}`). Lets a multi-tenant host isolate sessions
|
|
24
|
+
per user even when every user shares one agent definition.
|
|
25
|
+
- `browser.storagePersistence` lets the host supply its own
|
|
26
|
+
`{ save(json), load() }` for the browser storage state (cookies +
|
|
27
|
+
localStorage). When provided, the harness skips its built-in file-based
|
|
28
|
+
persistence — so state can live in an encrypted/DB-backed per-tenant store.
|
|
29
|
+
- `browser.hostManagedStreaming` makes the harness skip wiring
|
|
30
|
+
`onFrame`/`onStatus` during `run()`, so no `browser:frame` / `browser:status`
|
|
31
|
+
events are emitted into the agent event stream. The host subscribes to the
|
|
32
|
+
`BrowserSession` listeners directly and streams frames out-of-band (and can
|
|
33
|
+
keep the viewport interactive while the agent is idle).
|
|
34
|
+
|
|
3
35
|
## 0.59.16
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -820,6 +820,21 @@ interface PonchoConfig extends McpConfig {
|
|
|
820
820
|
/** Connect to an existing browser via CDP URL or port.
|
|
821
821
|
* Mutually exclusive with `provider`. */
|
|
822
822
|
cdpUrl?: string;
|
|
823
|
+
/** Host-supplied persistence for the browser's storage state
|
|
824
|
+
* (cookies + localStorage as a Playwright storageState JSON). When
|
|
825
|
+
* provided, the harness uses this instead of its built-in file-based
|
|
826
|
+
* persistence — letting an embedding app store session state in its
|
|
827
|
+
* own (e.g. encrypted, per-tenant) backend. */
|
|
828
|
+
storagePersistence?: {
|
|
829
|
+
save(json: string): Promise<void>;
|
|
830
|
+
load(): Promise<string | undefined>;
|
|
831
|
+
};
|
|
832
|
+
/** When true, the host owns the live viewport: the harness will NOT
|
|
833
|
+
* wire frame/status listeners during run() (so no `browser:frame` /
|
|
834
|
+
* `browser:status` events are emitted into the agent event stream).
|
|
835
|
+
* The host is expected to subscribe to the BrowserSession's
|
|
836
|
+
* `onFrame`/`onStatus` directly and stream frames out-of-band. */
|
|
837
|
+
hostManagedStreaming?: boolean;
|
|
823
838
|
};
|
|
824
839
|
}
|
|
825
840
|
declare const resolveStateConfig: (config: PonchoConfig | undefined) => StateConfig | undefined;
|
package/dist/index.js
CHANGED
|
@@ -10651,10 +10651,13 @@ var AgentHarness = class _AgentHarness {
|
|
|
10651
10651
|
this._browserMod = browserMod;
|
|
10652
10652
|
const browserCfg = typeof config.browser === "object" ? { ...config.browser } : {};
|
|
10653
10653
|
const agentId = this.parsedAgent?.frontmatter.id ?? this.parsedAgent?.frontmatter.name ?? "default";
|
|
10654
|
-
const
|
|
10655
|
-
const
|
|
10656
|
-
if (storagePersistence) {
|
|
10657
|
-
|
|
10654
|
+
const sessionName = typeof config.browser === "object" ? config.browser.sessionName : void 0;
|
|
10655
|
+
const sessionId = sessionName ?? `poncho-${agentId}`;
|
|
10656
|
+
if (!browserCfg.storagePersistence) {
|
|
10657
|
+
const storagePersistence = await this.buildBrowserStoragePersistence(config, sessionId);
|
|
10658
|
+
if (storagePersistence) {
|
|
10659
|
+
browserCfg.storagePersistence = storagePersistence;
|
|
10660
|
+
}
|
|
10658
10661
|
}
|
|
10659
10662
|
const session = new browserMod.BrowserSession(sessionId, browserCfg);
|
|
10660
10663
|
this._browserSession = session;
|
|
@@ -10852,6 +10855,9 @@ Browser sessions (cookies, localStorage, login state) are automatically saved an
|
|
|
10852
10855
|
- Use \`browser_screenshot\` only when you need to see visual layout or images. Screenshots consume significantly more tokens.
|
|
10853
10856
|
- The accessibility tree may be sparse on some pages. If \`browser_snapshot\` returns little or no content, fall back to \`browser_content\` or \`browser_screenshot\`.
|
|
10854
10857
|
|
|
10858
|
+
### Saving files
|
|
10859
|
+
To keep a file the page offers (a PDF, CSV, image, export, etc.), use \`browser_download\` to save it into the user's filesystem. It fetches with the browser's logged-in session, so it works for files behind a login. For a download link, get its href from a snapshot and pass it as \`url\`; for a file that opens in the browser, navigate to it and call \`browser_download\` with no \`url\`. The bytes go straight to the filesystem (not through the chat), so prefer this over screenshotting or copy-pasting file contents.
|
|
10860
|
+
|
|
10855
10861
|
### Tabs and resources
|
|
10856
10862
|
Each conversation gets its own browser tab sharing a single browser instance. Call \`browser_close\` when done to free the tab. If you don't close it, the tab stays open and the user can continue interacting with it.` : "";
|
|
10857
10863
|
const mainMemory = await memoryPromise;
|
|
@@ -10982,7 +10988,8 @@ ${this.skillFingerprint}`;
|
|
|
10982
10988
|
const browserEventQueue = [];
|
|
10983
10989
|
const browserCleanups = [];
|
|
10984
10990
|
const browserSession = this._browserSession;
|
|
10985
|
-
|
|
10991
|
+
const hostManagedStreaming = typeof this.loadedConfig?.browser === "object" && this.loadedConfig.browser.hostManagedStreaming === true;
|
|
10992
|
+
if (browserSession && !hostManagedStreaming) {
|
|
10986
10993
|
browserCleanups.push(
|
|
10987
10994
|
browserSession.onFrame(conversationId, makeBrowserFrameListener(browserEventQueue)),
|
|
10988
10995
|
browserSession.onStatus(conversationId, makeBrowserStatusListener(browserEventQueue))
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -294,6 +294,21 @@ export interface PonchoConfig extends McpConfig {
|
|
|
294
294
|
/** Connect to an existing browser via CDP URL or port.
|
|
295
295
|
* Mutually exclusive with `provider`. */
|
|
296
296
|
cdpUrl?: string;
|
|
297
|
+
/** Host-supplied persistence for the browser's storage state
|
|
298
|
+
* (cookies + localStorage as a Playwright storageState JSON). When
|
|
299
|
+
* provided, the harness uses this instead of its built-in file-based
|
|
300
|
+
* persistence — letting an embedding app store session state in its
|
|
301
|
+
* own (e.g. encrypted, per-tenant) backend. */
|
|
302
|
+
storagePersistence?: {
|
|
303
|
+
save(json: string): Promise<void>;
|
|
304
|
+
load(): Promise<string | undefined>;
|
|
305
|
+
};
|
|
306
|
+
/** When true, the host owns the live viewport: the harness will NOT
|
|
307
|
+
* wire frame/status listeners during run() (so no `browser:frame` /
|
|
308
|
+
* `browser:status` events are emitted into the agent event stream).
|
|
309
|
+
* The host is expected to subscribe to the BrowserSession's
|
|
310
|
+
* `onFrame`/`onStatus` directly and stream frames out-of-band. */
|
|
311
|
+
hostManagedStreaming?: boolean;
|
|
297
312
|
};
|
|
298
313
|
}
|
|
299
314
|
|
package/src/harness.ts
CHANGED
|
@@ -2123,11 +2123,18 @@ export class AgentHarness {
|
|
|
2123
2123
|
this._browserMod = browserMod;
|
|
2124
2124
|
const browserCfg: Record<string, unknown> = typeof config.browser === "object" ? { ...config.browser } : {};
|
|
2125
2125
|
const agentId = this.parsedAgent?.frontmatter.id ?? this.parsedAgent?.frontmatter.name ?? "default";
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
const
|
|
2129
|
-
|
|
2130
|
-
|
|
2126
|
+
// Let an embedding app override the session id (e.g. to make it per-tenant
|
|
2127
|
+
// when many users share one agent definition). Falls back to the agent id.
|
|
2128
|
+
const sessionName = typeof config.browser === "object" ? config.browser.sessionName : undefined;
|
|
2129
|
+
const sessionId = sessionName ?? `poncho-${agentId}`;
|
|
2130
|
+
|
|
2131
|
+
// Only build the built-in file persistence when the host didn't supply its
|
|
2132
|
+
// own. A host-provided `storagePersistence` (e.g. encrypted/DB-backed) wins.
|
|
2133
|
+
if (!browserCfg.storagePersistence) {
|
|
2134
|
+
const storagePersistence = await this.buildBrowserStoragePersistence(config, sessionId);
|
|
2135
|
+
if (storagePersistence) {
|
|
2136
|
+
browserCfg.storagePersistence = storagePersistence;
|
|
2137
|
+
}
|
|
2131
2138
|
}
|
|
2132
2139
|
|
|
2133
2140
|
const session = new browserMod.BrowserSession(sessionId, browserCfg);
|
|
@@ -2369,6 +2376,9 @@ Browser sessions (cookies, localStorage, login state) are automatically saved an
|
|
|
2369
2376
|
- Use \`browser_screenshot\` only when you need to see visual layout or images. Screenshots consume significantly more tokens.
|
|
2370
2377
|
- The accessibility tree may be sparse on some pages. If \`browser_snapshot\` returns little or no content, fall back to \`browser_content\` or \`browser_screenshot\`.
|
|
2371
2378
|
|
|
2379
|
+
### Saving files
|
|
2380
|
+
To keep a file the page offers (a PDF, CSV, image, export, etc.), use \`browser_download\` to save it into the user's filesystem. It fetches with the browser's logged-in session, so it works for files behind a login. For a download link, get its href from a snapshot and pass it as \`url\`; for a file that opens in the browser, navigate to it and call \`browser_download\` with no \`url\`. The bytes go straight to the filesystem (not through the chat), so prefer this over screenshotting or copy-pasting file contents.
|
|
2381
|
+
|
|
2372
2382
|
### Tabs and resources
|
|
2373
2383
|
Each conversation gets its own browser tab sharing a single browser instance. Call \`browser_close\` when done to free the tab. If you don't close it, the tab stays open and the user can continue interacting with it.`
|
|
2374
2384
|
: "";
|
|
@@ -2577,7 +2587,14 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2577
2587
|
profileDir: string;
|
|
2578
2588
|
isLaunched: boolean }
|
|
2579
2589
|
| undefined;
|
|
2580
|
-
|
|
2590
|
+
// When the host owns the live viewport (`browser.hostManagedStreaming`),
|
|
2591
|
+
// it subscribes to onFrame/onStatus directly and streams frames out-of-band.
|
|
2592
|
+
// Skip the during-run wiring so frames don't double-emit into the agent
|
|
2593
|
+
// event stream (and don't flood any host-side event buffer).
|
|
2594
|
+
const hostManagedStreaming =
|
|
2595
|
+
typeof this.loadedConfig?.browser === "object" &&
|
|
2596
|
+
this.loadedConfig.browser.hostManagedStreaming === true;
|
|
2597
|
+
if (browserSession && !hostManagedStreaming) {
|
|
2581
2598
|
browserCleanups.push(
|
|
2582
2599
|
browserSession.onFrame(conversationId, makeBrowserFrameListener(browserEventQueue)),
|
|
2583
2600
|
browserSession.onStatus(conversationId, makeBrowserStatusListener(browserEventQueue)),
|