@poncho-ai/harness 0.59.16 → 0.59.17

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/harness@0.59.16 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
2
+ > @poncho-ai/harness@0.59.17 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
  CLI tsup v8.5.1
9
9
  CLI Target: es2022
10
10
  ESM Build start
11
- ESM dist/index.js 571.49 KB
12
11
  ESM dist/isolate-F2PPSUL6.js 53.82 KB
13
- ESM ⚡️ Build success in 239ms
12
+ ESM dist/index.js 571.82 KB
13
+ ESM ⚡️ Build success in 264ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 6987ms
16
- DTS dist/index.d.ts 103.12 KB
15
+ DTS ⚡️ Build success in 7586ms
16
+ DTS dist/index.d.ts 104.01 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @poncho-ai/harness
2
2
 
3
+ ## 0.59.17
4
+
5
+ ### Patch Changes
6
+
7
+ - [`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
8
+ and the live viewport.
9
+ - `browser.sessionName` now overrides the browser session id (previously
10
+ always `poncho-${agentId}`). Lets a multi-tenant host isolate sessions
11
+ per user even when every user shares one agent definition.
12
+ - `browser.storagePersistence` lets the host supply its own
13
+ `{ save(json), load() }` for the browser storage state (cookies +
14
+ localStorage). When provided, the harness skips its built-in file-based
15
+ persistence — so state can live in an encrypted/DB-backed per-tenant store.
16
+ - `browser.hostManagedStreaming` makes the harness skip wiring
17
+ `onFrame`/`onStatus` during `run()`, so no `browser:frame` / `browser:status`
18
+ events are emitted into the agent event stream. The host subscribes to the
19
+ `BrowserSession` listeners directly and streams frames out-of-band (and can
20
+ keep the viewport interactive while the agent is idle).
21
+
3
22
  ## 0.59.16
4
23
 
5
24
  ### 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 sessionId = `poncho-${agentId}`;
10655
- const storagePersistence = await this.buildBrowserStoragePersistence(config, sessionId);
10656
- if (storagePersistence) {
10657
- browserCfg.storagePersistence = storagePersistence;
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;
@@ -10982,7 +10985,8 @@ ${this.skillFingerprint}`;
10982
10985
  const browserEventQueue = [];
10983
10986
  const browserCleanups = [];
10984
10987
  const browserSession = this._browserSession;
10985
- if (browserSession) {
10988
+ const hostManagedStreaming = typeof this.loadedConfig?.browser === "object" && this.loadedConfig.browser.hostManagedStreaming === true;
10989
+ if (browserSession && !hostManagedStreaming) {
10986
10990
  browserCleanups.push(
10987
10991
  browserSession.onFrame(conversationId, makeBrowserFrameListener(browserEventQueue)),
10988
10992
  browserSession.onStatus(conversationId, makeBrowserStatusListener(browserEventQueue))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.59.16",
3
+ "version": "0.59.17",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
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
- const sessionId = `poncho-${agentId}`;
2127
-
2128
- const storagePersistence = await this.buildBrowserStoragePersistence(config, sessionId);
2129
- if (storagePersistence) {
2130
- browserCfg.storagePersistence = storagePersistence;
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);
@@ -2577,7 +2584,14 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
2577
2584
  profileDir: string;
2578
2585
  isLaunched: boolean }
2579
2586
  | undefined;
2580
- if (browserSession) {
2587
+ // When the host owns the live viewport (`browser.hostManagedStreaming`),
2588
+ // it subscribes to onFrame/onStatus directly and streams frames out-of-band.
2589
+ // Skip the during-run wiring so frames don't double-emit into the agent
2590
+ // event stream (and don't flood any host-side event buffer).
2591
+ const hostManagedStreaming =
2592
+ typeof this.loadedConfig?.browser === "object" &&
2593
+ this.loadedConfig.browser.hostManagedStreaming === true;
2594
+ if (browserSession && !hostManagedStreaming) {
2581
2595
  browserCleanups.push(
2582
2596
  browserSession.onFrame(conversationId, makeBrowserFrameListener(browserEventQueue)),
2583
2597
  browserSession.onStatus(conversationId, makeBrowserStatusListener(browserEventQueue)),