@parall/daemon 1.49.0 → 1.50.1

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.
Files changed (38) hide show
  1. package/bundle/manifest.json +11 -11
  2. package/bundle/parall-browser-pod.js +318 -104
  3. package/bundle/parall-claude-agent.js +124 -29
  4. package/bundle/parall-codex-agent.js +124 -29
  5. package/bundle/parall-daemon.js +368 -184
  6. package/dist/browser-pod.d.ts.map +1 -1
  7. package/dist/browser-pod.js +9 -14
  8. package/dist/browser-profile-viewer-control.d.ts +15 -0
  9. package/dist/browser-profile-viewer-control.d.ts.map +1 -0
  10. package/dist/browser-profile-viewer-control.js +27 -0
  11. package/dist/clip-runtime/bb-viewer-command-client.d.ts +19 -0
  12. package/dist/clip-runtime/bb-viewer-command-client.d.ts.map +1 -0
  13. package/dist/clip-runtime/bb-viewer-command-client.js +89 -0
  14. package/dist/clip-runtime/browser-profile-manager.d.ts +3 -1
  15. package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
  16. package/dist/clip-runtime/browser-profile-manager.js +2 -2
  17. package/dist/clip-runtime/browser-viewer-streamer.d.ts +7 -7
  18. package/dist/clip-runtime/browser-viewer-streamer.d.ts.map +1 -1
  19. package/dist/clip-runtime/browser-viewer-streamer.js +39 -47
  20. package/dist/clip-runtime/clip-provider.d.ts +3 -2
  21. package/dist/clip-runtime/clip-provider.d.ts.map +1 -1
  22. package/dist/clip-runtime/clip-provider.js +13 -6
  23. package/dist/clip-runtime/hosted-viewer-command-queue.d.ts +43 -0
  24. package/dist/clip-runtime/hosted-viewer-command-queue.d.ts.map +1 -0
  25. package/dist/clip-runtime/hosted-viewer-command-queue.js +72 -0
  26. package/dist/clip-runtime/index.d.ts +1 -0
  27. package/dist/clip-runtime/index.d.ts.map +1 -1
  28. package/dist/clip-runtime/index.js +1 -0
  29. package/dist/clip-runtime/viewer-command-deadline.d.ts +14 -0
  30. package/dist/clip-runtime/viewer-command-deadline.d.ts.map +1 -0
  31. package/dist/clip-runtime/viewer-command-deadline.js +29 -0
  32. package/dist/clip-runtime/viewer-command-error.d.ts +18 -0
  33. package/dist/clip-runtime/viewer-command-error.d.ts.map +1 -0
  34. package/dist/clip-runtime/viewer-command-error.js +23 -0
  35. package/dist/supervisor.d.ts +0 -8
  36. package/dist/supervisor.d.ts.map +1 -1
  37. package/dist/supervisor.js +3 -32
  38. package/package.json +6 -6
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.49.0",
3
- "built_at": "2026-07-26T04:12:18.813Z",
2
+ "version": "1.50.1",
3
+ "built_at": "2026-07-28T15:25:57.441Z",
4
4
  "min_node_version": "20.0.0",
5
5
  "files": {
6
6
  "bb-browser-daemon.js": {
@@ -16,29 +16,29 @@
16
16
  "size": 18
17
17
  },
18
18
  "parall-browser-pod.js": {
19
- "sha256": "20a0ee67c5be9cd2352ed8e5a503fa60b8fe86d7dba04ecbe2345397c6e7e00f",
20
- "size": 2899080
19
+ "sha256": "f20c74453ddfbef10528afc1d9b6c4de48fd4a780ddb715431d61185c3454b20",
20
+ "size": 2908552
21
21
  },
22
22
  "parall-channel-exec.js": {
23
23
  "sha256": "7c2a6e12483a45e9eb4a42cd7d4cdd3c4fa21dd09ac1347d9a3e177b4ddee930",
24
24
  "size": 7220
25
25
  },
26
26
  "parall-claude-agent.js": {
27
- "sha256": "19203c62234b641e691de41af3444d52876beaabc3fa6abeb7907fd5276da711",
28
- "size": 2732891
27
+ "sha256": "38048b9d69561a7d92ddc97b6322ae215e2e59dbcbbb6353bfa371632683a17c",
28
+ "size": 2737754
29
29
  },
30
30
  "parall-codex-agent.js": {
31
- "sha256": "8a80870d3f213ae3d36b5c46b4e4d4e6eb1d91855d5a2ce1c72be43e9cc960ba",
32
- "size": 2774631
31
+ "sha256": "c65c89982b58f6e1aea155991d364f1325eef166fe0fb92ab989ea477dfe3dd3",
32
+ "size": 2779494
33
33
  },
34
34
  "parall-daemon.js": {
35
- "sha256": "ef3377051a8310fb760d2ce7826a6654d83225f093a8cdade696ad6216540b3c",
36
- "size": 2961111
35
+ "sha256": "fd0bf688bcf0df4227c7eef61907276437b2f40961e4ebba28c12d2dda34de73",
36
+ "size": 2969136
37
37
  },
38
38
  "parall-openclaw-agent.js": {
39
39
  "sha256": "856a1c3a9ae0ffef8efe2481fa1171dbeb1d67cccb88616aa9bf16da96a69355",
40
40
  "size": 9923
41
41
  }
42
42
  },
43
- "signature": "sDr+si9wQsnB/LezX3xlTWHu2bHV5DzO5KWEtQz/5Qj2XOWfpKqvb0JjjVFGOxCG07mIGSfj9U5KpeT3GMgbDg=="
43
+ "signature": "VOZtJbTWb3YvTHoc3hGgfBw3bnL4JM9jdTXmPW6rdc3YpZXCunf//CcfviSu0Acn/p6vnHSEizSX00RAJbrCAw=="
44
44
  }
@@ -63495,6 +63495,17 @@ function clearAllProviderCreds(env) {
63495
63495
  delete env.PRLL_CLAUDE_ALLOW_API_KEY;
63496
63496
  }
63497
63497
 
63498
+ // ts/sdk/dist/browser-viewer.js
63499
+ var BROWSER_VIEWER_READINESS_TIMEOUT_MS = 6e4;
63500
+ function browserViewerRequestOptions(command, opts) {
63501
+ if (command !== "stream.answer" && command !== "stream.switch")
63502
+ return opts;
63503
+ return {
63504
+ ...opts,
63505
+ timeoutMs: Math.max(opts?.timeoutMs ?? 0, BROWSER_VIEWER_READINESS_TIMEOUT_MS)
63506
+ };
63507
+ }
63508
+
63498
63509
  // ts/sdk/dist/constants.js
63499
63510
  var API_BASE = "/api/v1";
63500
63511
  var WIKI_BASE = "/wiki/v1";
@@ -63529,6 +63540,12 @@ var ENDPOINTS = {
63529
63540
  ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
63530
63541
  ORG_MEMBER_CHATS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/chats`,
63531
63542
  ORG_MEMBER_TASKS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/tasks`,
63543
+ // Org-scoped public profile (title / description). GET readable by every
63544
+ // active member; PATCH is CAS-guarded (expected_version).
63545
+ ORG_MEMBER_PROFILE: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}/profile`,
63546
+ // Private agent Instructions — visibility gated server-side
63547
+ // (agent self + Human org admin/owner). Never cached.
63548
+ AGENT_INSTRUCTIONS: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/instructions`,
63532
63549
  REF_SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/refs/search`,
63533
63550
  // Direct messages (org-scoped, atomic find-or-create + send)
63534
63551
  DM: (orgId) => `${API_BASE}/orgs/${orgId}/dm`,
@@ -63812,6 +63829,11 @@ var ENDPOINTS = {
63812
63829
  SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/search`,
63813
63830
  // Feature flags (org-scoped, server-evaluated)
63814
63831
  FEATURE_FLAGS: (orgId) => `${API_BASE}/orgs/${orgId}/feature-flags`,
63832
+ // Team templates (org-scoped, owner/admin only)
63833
+ TEMPLATES: (orgId) => `${API_BASE}/orgs/${orgId}/templates`,
63834
+ TEMPLATE: (orgId, templateId) => `${API_BASE}/orgs/${orgId}/templates/${templateId}`,
63835
+ TEMPLATE_DEPLOYMENTS: (orgId) => `${API_BASE}/orgs/${orgId}/template-deployments`,
63836
+ TEMPLATE_DEPLOYMENT: (orgId, deploymentId) => `${API_BASE}/orgs/${orgId}/template-deployments/${deploymentId}`,
63815
63837
  // Billing & Credits (org-scoped)
63816
63838
  BILLING: (orgId) => `${API_BASE}/orgs/${orgId}/billing`,
63817
63839
  BILLING_TRANSACTIONS: (orgId) => `${API_BASE}/orgs/${orgId}/billing/transactions`,
@@ -64246,6 +64268,30 @@ var ParallClient = class _ParallClient {
64246
64268
  async updateOrgMember(orgId, userId, role) {
64247
64269
  return this.request("PATCH", ENDPOINTS.ORG_MEMBER(orgId, userId), { role });
64248
64270
  }
64271
+ /** Org-scoped public profile (title / description) of any active member. */
64272
+ async getMemberProfile(orgId, userId) {
64273
+ return this.request("GET", ENDPOINTS.ORG_MEMBER_PROFILE(orgId, userId));
64274
+ }
64275
+ /**
64276
+ * CAS full-replacement of a member's public profile. Humans edit their
64277
+ * own; Human org admins/owners edit agents'. A stale expected_version
64278
+ * gets 409 PROFILE_VERSION_CONFLICT with error.details.current_version.
64279
+ */
64280
+ async updateMemberProfile(orgId, userId, req) {
64281
+ return this.request("PATCH", ENDPOINTS.ORG_MEMBER_PROFILE(orgId, userId), req);
64282
+ }
64283
+ /** Private agent Instructions — agent self or Human org admin/owner only. */
64284
+ async getAgentInstructions(orgId, agentId) {
64285
+ return this.request("GET", ENDPOINTS.AGENT_INSTRUCTIONS(orgId, agentId));
64286
+ }
64287
+ /**
64288
+ * CAS update of the private Instructions (Human org admin/owner only).
64289
+ * A stale expected_version gets 409 INSTRUCTIONS_VERSION_CONFLICT with
64290
+ * error.details.current_version.
64291
+ */
64292
+ async updateAgentInstructions(orgId, agentId, req) {
64293
+ return this.request("PATCH", ENDPOINTS.AGENT_INSTRUCTIONS(orgId, agentId), req);
64294
+ }
64249
64295
  // Member activity surfaces — chats the member participates in within this org,
64250
64296
  // ordered by most recent activity. Powers the member profile Activity tab.
64251
64297
  async getMemberChats(orgId, memberId, params) {
@@ -64803,11 +64849,7 @@ var ParallClient = class _ParallClient {
64803
64849
  async postBrowseResponse(requestId, response) {
64804
64850
  return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
64805
64851
  }
64806
- /**
64807
- * `POST /machines/me/browser-profiles/viewer-response/{requestId}` — daemon
64808
- * reply to a `machine.browser_profile.viewer` control command. Wakes the
64809
- * api-server request/reply bridge (mirrors {@link postBrowseResponse}).
64810
- */
64852
+ /** Reply to `machine.browser_profile.viewer` and wake its request/reply bridge. */
64811
64853
  async postBrowserProfileViewerResponse(requestId, response) {
64812
64854
  return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_VIEWER_RESPONSE(requestId), response);
64813
64855
  }
@@ -64817,7 +64859,7 @@ var ParallClient = class _ParallClient {
64817
64859
  * owner or org admin. api-server brokers the command to the host daemon.
64818
64860
  */
64819
64861
  async browserViewerCommand(orgId, profileId, req, opts) {
64820
- return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, opts);
64862
+ return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, browserViewerRequestOptions(req.command, opts));
64821
64863
  }
64822
64864
  /**
64823
64865
  * Drive the Cloud Edge live viewer (V1b, design §6): WebRTC signaling + input +
@@ -65589,6 +65631,22 @@ var ParallClient = class _ParallClient {
65589
65631
  async getFeatureFlags(orgId) {
65590
65632
  return this.request("GET", ENDPOINTS.FEATURE_FLAGS(orgId));
65591
65633
  }
65634
+ // ---- Official Template catalog (org-contextual owner/admin reads + Hire) ----
65635
+ async listOfficialTemplates(orgId) {
65636
+ const response = await this.request("GET", ENDPOINTS.TEMPLATES(orgId));
65637
+ return response.templates ?? [];
65638
+ }
65639
+ async getOfficialTemplate(orgId, templateId) {
65640
+ return this.request("GET", ENDPOINTS.TEMPLATE(orgId, templateId));
65641
+ }
65642
+ async deployTemplate(orgId, request) {
65643
+ return this.request("POST", ENDPOINTS.TEMPLATE_DEPLOYMENTS(orgId), request, void 0, false, {
65644
+ timeoutMs: 185e3
65645
+ });
65646
+ }
65647
+ async getTemplateDeployment(orgId, deploymentId) {
65648
+ return this.request("GET", ENDPOINTS.TEMPLATE_DEPLOYMENT(orgId, deploymentId));
65649
+ }
65592
65650
  // ---- Billing & Credits (org-scoped) ----
65593
65651
  async getBilling(orgId) {
65594
65652
  return this.request("GET", ENDPOINTS.BILLING(orgId));
@@ -66829,8 +66887,113 @@ function comparableUrl(url) {
66829
66887
 
66830
66888
  // ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
66831
66889
  import { spawn } from "node:child_process";
66832
- var BB_VIEWER_HEALTH_TIMEOUT_MS = 1e4;
66890
+
66891
+ // ts/daemon/dist/clip-runtime/viewer-command-error.js
66892
+ var ViewerCommandError = class extends Error {
66893
+ code;
66894
+ constructor(message, code) {
66895
+ super(message);
66896
+ this.code = code;
66897
+ this.name = "ViewerCommandError";
66898
+ }
66899
+ };
66900
+ function viewerCommandFailure(error2) {
66901
+ const message = error2 instanceof Error ? error2.message : String(error2);
66902
+ if (error2 instanceof ViewerCommandError && error2.code) {
66903
+ return { code: error2.code, message };
66904
+ }
66905
+ return { message };
66906
+ }
66907
+
66908
+ // ts/daemon/dist/clip-runtime/viewer-command-deadline.js
66909
+ var HOSTED_VIEWER_READINESS_POD_BUDGET_MS = 3e4;
66910
+ var HOSTED_VIEWER_REPLY_RESERVE_MS = 5e3;
66911
+ function isViewerReadinessCommand(command) {
66912
+ return command === "stream.answer" || command === "stream.switch";
66913
+ }
66914
+ function parseViewerDispatchDeadlineUnixMs(value) {
66915
+ let parsed;
66916
+ if (typeof value === "number") {
66917
+ parsed = value;
66918
+ } else if (typeof value === "string" && /^[1-9]\d*$/.test(value)) {
66919
+ parsed = Number(value);
66920
+ } else {
66921
+ return void 0;
66922
+ }
66923
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
66924
+ }
66925
+ function viewerReadinessDeadlineError(phase) {
66926
+ return new ViewerCommandError(`viewer readiness deadline elapsed ${phase}`, "VIEWER_STREAM_CANCELLED");
66927
+ }
66928
+
66929
+ // ts/daemon/dist/clip-runtime/bb-viewer-command-client.js
66833
66930
  var BB_VIEWER_COMMAND_TIMEOUT_MS = 15e3;
66931
+ var BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS = 3e4;
66932
+ function bbViewerCommandTimeoutMs(method) {
66933
+ return method === "answer" || method === "switch" ? BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS : BB_VIEWER_COMMAND_TIMEOUT_MS;
66934
+ }
66935
+ function bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs, now = Date.now()) {
66936
+ const configured = bbViewerCommandTimeoutMs(method);
66937
+ if (deadlineUnixMs === void 0)
66938
+ return configured;
66939
+ const remaining = deadlineUnixMs - now;
66940
+ if (remaining <= 0) {
66941
+ throw viewerReadinessDeadlineError(`before bb-viewer ${method}`);
66942
+ }
66943
+ return Math.max(1, Math.min(configured, remaining));
66944
+ }
66945
+ async function sendBBViewerCommand(port, method, params, deadlineUnixMs) {
66946
+ const controller = new AbortController();
66947
+ const timeoutMs = bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs);
66948
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
66949
+ try {
66950
+ const resp = await fetch(`http://127.0.0.1:${port}/command`, {
66951
+ method: "POST",
66952
+ headers: { "Content-Type": "application/json" },
66953
+ body: JSON.stringify({ method, params }),
66954
+ signal: controller.signal
66955
+ });
66956
+ const text = await resp.text();
66957
+ return parseBBViewerCommandResponse(resp.status, text, method);
66958
+ } catch (err) {
66959
+ if (controller.signal.aborted && (method === "answer" || method === "switch")) {
66960
+ throw viewerReadinessDeadlineError(`while waiting for bb-viewer ${method}`);
66961
+ }
66962
+ throw err;
66963
+ } finally {
66964
+ clearTimeout(timer);
66965
+ }
66966
+ }
66967
+ function parseBBViewerCommandResponse(status, text, method) {
66968
+ let parsed;
66969
+ try {
66970
+ parsed = text ? JSON.parse(text) : {};
66971
+ } catch {
66972
+ throw new ViewerCommandError(`bb-viewer ${method} returned invalid JSON`);
66973
+ }
66974
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
66975
+ throw new ViewerCommandError(`bb-viewer ${method} returned an invalid response`);
66976
+ }
66977
+ const envelope = parsed;
66978
+ if (envelope.error) {
66979
+ const code = typeof envelope.error.code === "string" && envelope.error.code.trim() ? envelope.error.code.trim() : void 0;
66980
+ const message = typeof envelope.error.message === "string" && envelope.error.message.trim() ? envelope.error.message : `bb-viewer ${method} failed`;
66981
+ throw new ViewerCommandError(message, code);
66982
+ }
66983
+ if (status < 200 || status >= 300) {
66984
+ throw new ViewerCommandError(`bb-viewer ${method} returned ${status}`);
66985
+ }
66986
+ if (envelope.result === void 0 || envelope.result === null) {
66987
+ return {};
66988
+ }
66989
+ if (typeof envelope.result !== "object" || Array.isArray(envelope.result)) {
66990
+ throw new ViewerCommandError(`bb-viewer ${method} returned an invalid result`);
66991
+ }
66992
+ return envelope.result;
66993
+ }
66994
+
66995
+ // ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
66996
+ var BB_VIEWER_HEALTH_TIMEOUT_MS = 1e4;
66834
66997
  var BB_VIEWER_UNANSWERED_REAP_MS = 9e4;
66835
66998
  var BB_VIEWER_TERM_GRACE_MS = 5e3;
66836
66999
  function isLoopbackBindHost(host) {
@@ -66867,7 +67030,7 @@ var BrowserViewerStreamer = class {
66867
67030
  * partial result. `data.input` / `data.turn` / `data.session_id` come from
66868
67031
  * MachineBrowserProfileViewerData.
66869
67032
  */
66870
- async handleViewerCommand(profileId, sessionId, command, input, turn) {
67033
+ async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
66871
67034
  if (!profileId)
66872
67035
  throw new Error("browser profile id is required");
66873
67036
  if (command !== "stream.start" && sessionId && this.kickedSessions.get(profileId) === sessionId) {
@@ -66877,11 +67040,11 @@ var BrowserViewerStreamer = class {
66877
67040
  case "stream.start":
66878
67041
  return this.viewerStreamStart(profileId, sessionId, turn);
66879
67042
  case "stream.answer":
66880
- return this.viewerStreamAnswer(profileId, sessionId, input);
67043
+ return this.viewerStreamAnswer(profileId, sessionId, input, execution?.deadlineUnixMs);
66881
67044
  case "stream.close":
66882
67045
  return this.viewerStreamClose(profileId, sessionId, input);
66883
67046
  case "stream.switch":
66884
- return this.viewerStreamSwitch(profileId, sessionId, input);
67047
+ return this.viewerStreamSwitch(profileId, sessionId, input, execution?.deadlineUnixMs);
66885
67048
  case "kick":
66886
67049
  return this.viewerKick(profileId, sessionId, input);
66887
67050
  case "close":
@@ -66933,7 +67096,7 @@ var BrowserViewerStreamer = class {
66933
67096
  await this.bringTargetToFront(cdpUrl);
66934
67097
  const streamer = await this.spawnStreamer(profileId, sessionId, turn);
66935
67098
  try {
66936
- const result = await this.streamerCommand(streamer, "connect", { cdpUrl });
67099
+ const result = await sendBBViewerCommand(streamer.port, "connect", { cdpUrl });
66937
67100
  streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
66938
67101
  const reapTimer = setTimeout(() => {
66939
67102
  const current = this.streamers.get(profileId);
@@ -66954,14 +67117,17 @@ var BrowserViewerStreamer = class {
66954
67117
  * Rejects if the streamer's sessionId no longer matches (a stale viewer
66955
67118
  * session from a superseded stream.start).
66956
67119
  */
66957
- async viewerStreamAnswer(profileId, sessionId, input) {
67120
+ async viewerStreamAnswer(profileId, sessionId, input, deadlineUnixMs) {
66958
67121
  const streamer = this.streamers.get(profileId);
66959
67122
  if (!streamer)
66960
67123
  throw new Error("no active viewer streamer for profile");
66961
67124
  if (streamer.sessionId !== sessionId)
66962
67125
  throw new Error("stale viewer session");
67126
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer answer");
66963
67127
  streamer.answered = true;
66964
- return this.streamerCommand(streamer, "answer", input ?? {});
67128
+ const result = await sendBBViewerCommand(streamer.port, "answer", input ?? {}, deadlineUnixMs);
67129
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer answer");
67130
+ return result;
66965
67131
  }
66966
67132
  /**
66967
67133
  * stream.close — best-effort stop the streamer, then kill + remove it. A
@@ -66982,7 +67148,7 @@ var BrowserViewerStreamer = class {
66982
67148
  return { ok: true, stale: true };
66983
67149
  }
66984
67150
  try {
66985
- await this.streamerCommand(streamer, "stop", {});
67151
+ await sendBBViewerCommand(streamer.port, "stop", {});
66986
67152
  } catch (err) {
66987
67153
  this.host.log.warn(`[bb-viewer] stop command failed for ${profileId} (killing anyway): ${formatErrorForLog(err)}`);
66988
67154
  }
@@ -66999,7 +67165,7 @@ var BrowserViewerStreamer = class {
66999
67165
  * the stream.start response), so a session-less switch is by definition not
67000
67166
  * the current viewer.
67001
67167
  */
67002
- async viewerStreamSwitch(profileId, sessionId, input) {
67168
+ async viewerStreamSwitch(profileId, sessionId, input, deadlineUnixMs) {
67003
67169
  const streamer = this.streamers.get(profileId);
67004
67170
  if (!streamer)
67005
67171
  throw new Error("no active viewer streamer for profile");
@@ -67009,9 +67175,13 @@ var BrowserViewerStreamer = class {
67009
67175
  if (tab === void 0 || tab === null || tab === "") {
67010
67176
  throw new Error("stream.switch requires input.tab");
67011
67177
  }
67178
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before resolving the switch target");
67012
67179
  const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId, tab), `viewer stream.switch ${profileId}`);
67013
- await this.bringTargetToFront(cdpUrl);
67014
- const result = await this.streamerCommand(streamer, "switch", { cdpUrl });
67180
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "after resolving the switch target");
67181
+ await this.bringTargetToFront(cdpUrl, deadlineUnixMs);
67182
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer switch");
67183
+ const result = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl }, deadlineUnixMs);
67184
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer switch");
67015
67185
  streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
67016
67186
  return { ...result, streamed_tab_id: streamer.streamedTabId };
67017
67187
  }
@@ -67035,7 +67205,7 @@ var BrowserViewerStreamer = class {
67035
67205
  }
67036
67206
  const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId), `viewer close re-switch ${profileId}`);
67037
67207
  await this.bringTargetToFront(cdpUrl);
67038
- const sw = await this.streamerCommand(streamer, "switch", { cdpUrl });
67208
+ const sw = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl });
67039
67209
  streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
67040
67210
  return { ...result, ...sw, streamed_tab_id: streamer.streamedTabId };
67041
67211
  }
@@ -67047,15 +67217,17 @@ var BrowserViewerStreamer = class {
67047
67217
  * must not block the stream. host:port + targetId are parsed from the cdpUrl
67048
67218
  * (`ws://host:port/devtools/page/<targetId>`) to avoid an extra /status call.
67049
67219
  */
67050
- async bringTargetToFront(cdpUrl) {
67220
+ async bringTargetToFront(cdpUrl, deadlineUnixMs) {
67051
67221
  const m3 = /^ws:\/\/([^/]+)\/devtools\/page\/(.+)$/.exec(cdpUrl);
67052
67222
  if (!m3)
67053
67223
  return;
67054
67224
  const [, hostPort, targetId] = m3;
67225
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before foregrounding the switch target");
67226
+ const timeoutMs = deadlineUnixMs === void 0 ? 5e3 : Math.max(1, Math.min(5e3, deadlineUnixMs - Date.now()));
67055
67227
  try {
67056
67228
  const resp = await fetch(`http://${hostPort}/json/activate/${targetId}`, {
67057
67229
  method: "GET",
67058
- signal: AbortSignal.timeout(5e3)
67230
+ signal: AbortSignal.timeout(timeoutMs)
67059
67231
  });
67060
67232
  if (!resp.ok) {
67061
67233
  this.host.log.warn(`[bb-viewer] activate target ${targetId} returned ${resp.status} (continuing)`);
@@ -67064,6 +67236,11 @@ var BrowserViewerStreamer = class {
67064
67236
  this.host.log.warn(`[bb-viewer] activate target ${targetId} failed (continuing): ${formatErrorForLog(err)}`);
67065
67237
  }
67066
67238
  }
67239
+ assertViewerExecutionDeadline(deadlineUnixMs, phase) {
67240
+ if (deadlineUnixMs !== void 0 && Date.now() >= deadlineUnixMs) {
67241
+ throw viewerReadinessDeadlineError(phase);
67242
+ }
67243
+ }
67067
67244
  /**
67068
67245
  * Forward a navigation command (tab_list/tab_new/open/reload/back/forward/
67069
67246
  * close) to bb-browser scoped to the profile's account. This lets the human
@@ -67264,34 +67441,6 @@ var BrowserViewerStreamer = class {
67264
67441
  killTimer.unref?.();
67265
67442
  child.once("exit", () => clearTimeout(killTimer));
67266
67443
  }
67267
- /**
67268
- * POST a command to the profile's bb-viewer `/command` endpoint. Parses the
67269
- * `{result}` / `{error:{message}}` envelope (bb-viewer api.go) and returns
67270
- * the inner result, throwing the error message on failure.
67271
- */
67272
- async streamerCommand(streamer, method, params) {
67273
- const controller = new AbortController();
67274
- const timer = setTimeout(() => controller.abort(), BB_VIEWER_COMMAND_TIMEOUT_MS);
67275
- try {
67276
- const resp = await fetch(`http://127.0.0.1:${streamer.port}/command`, {
67277
- method: "POST",
67278
- headers: { "Content-Type": "application/json" },
67279
- body: JSON.stringify({ method, params }),
67280
- signal: controller.signal
67281
- });
67282
- const text = await resp.text();
67283
- const parsed = text ? JSON.parse(text) : {};
67284
- if (parsed.error) {
67285
- throw new Error(parsed.error.message || `bb-viewer ${method} failed`);
67286
- }
67287
- if (!resp.ok) {
67288
- throw new Error(`bb-viewer ${method} returned ${resp.status}: ${text}`);
67289
- }
67290
- return parsed.result ?? {};
67291
- } finally {
67292
- clearTimeout(timer);
67293
- }
67294
- }
67295
67444
  /**
67296
67445
  * Kill + remove the profile's bb-viewer streamer, if any. Idempotent. Also
67297
67446
  * clears any kick marker for the profile: a (re)kill establishes a clean
@@ -67551,8 +67700,8 @@ var BrowserProfileManager = class {
67551
67700
  * (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
67552
67701
  * subprocesses and viewer tab navigation.
67553
67702
  */
67554
- async handleViewerCommand(profileId, sessionId, command, input, turn) {
67555
- return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn);
67703
+ async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
67704
+ return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn, execution);
67556
67705
  }
67557
67706
  /** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
67558
67707
  async cdpEndpoint() {
@@ -68402,6 +68551,46 @@ function errMsg(err) {
68402
68551
  // ts/daemon/dist/clip-runtime/clip-provider.js
68403
68552
  import * as http2 from "node:http2";
68404
68553
 
68554
+ // ts/daemon/dist/clip-runtime/browser-dependency.js
68555
+ var BROWSER_CAPABILITY_NAME = "browser";
68556
+ var BROWSER_CAPABILITY_PACKAGE = "@pinixai/bb-browser-pro";
68557
+ var BROWSER_DEPENDENCY_NAMES = /* @__PURE__ */ new Set([
68558
+ BROWSER_CAPABILITY_NAME,
68559
+ "@pinix/browser",
68560
+ "@pinixai/bb-browser-pro",
68561
+ "bb-browser"
68562
+ ]);
68563
+ function isBrowserDependencyName(name) {
68564
+ return !!name && BROWSER_DEPENDENCY_NAMES.has(name);
68565
+ }
68566
+ function browserCapabilityCommands() {
68567
+ return [
68568
+ { name: "eval", description: "Run JavaScript in a page context" },
68569
+ { name: "open", description: "Open a URL in a new tab" },
68570
+ { name: "snapshot", description: "Capture the accessibility/DOM snapshot of a page" },
68571
+ { name: "click", description: "Click an element" },
68572
+ { name: "type", description: "Type text into an element" }
68573
+ ];
68574
+ }
68575
+ function manifestDeclaresBrowserDependency(manifest) {
68576
+ return dependencyMaps(manifest).some((deps) => Object.entries(deps).some(([name, dep]) => isBrowserDependencyName(name) || isBrowserDependencyName(dep?.package)));
68577
+ }
68578
+ function manifestMapsDependencyToBrowser(manifest, dependencyName) {
68579
+ const maps = dependencyMaps(manifest);
68580
+ if (maps.length === 0)
68581
+ return false;
68582
+ for (const deps of maps) {
68583
+ const declared = deps[dependencyName];
68584
+ if (declared) {
68585
+ return isBrowserDependencyName(dependencyName) || isBrowserDependencyName(declared.package);
68586
+ }
68587
+ }
68588
+ return isBrowserDependencyName(dependencyName) && manifestDeclaresBrowserDependency(manifest);
68589
+ }
68590
+ function dependencyMaps(manifest) {
68591
+ return [manifest?.dependencies, manifest?.dependencySlots].filter((deps) => !!deps);
68592
+ }
68593
+
68405
68594
  // ts/daemon/dist/clip-runtime/process.js
68406
68595
  import { spawn as spawn4 } from "node:child_process";
68407
68596
  import { createInterface as createInterface2 } from "node:readline";
@@ -68658,46 +68847,6 @@ function finalizeManifest(manifest) {
68658
68847
  return m3;
68659
68848
  }
68660
68849
 
68661
- // ts/daemon/dist/clip-runtime/browser-dependency.js
68662
- var BROWSER_CAPABILITY_NAME = "browser";
68663
- var BROWSER_CAPABILITY_PACKAGE = "@pinixai/bb-browser-pro";
68664
- var BROWSER_DEPENDENCY_NAMES = /* @__PURE__ */ new Set([
68665
- BROWSER_CAPABILITY_NAME,
68666
- "@pinix/browser",
68667
- "@pinixai/bb-browser-pro",
68668
- "bb-browser"
68669
- ]);
68670
- function isBrowserDependencyName(name) {
68671
- return !!name && BROWSER_DEPENDENCY_NAMES.has(name);
68672
- }
68673
- function browserCapabilityCommands() {
68674
- return [
68675
- { name: "eval", description: "Run JavaScript in a page context" },
68676
- { name: "open", description: "Open a URL in a new tab" },
68677
- { name: "snapshot", description: "Capture the accessibility/DOM snapshot of a page" },
68678
- { name: "click", description: "Click an element" },
68679
- { name: "type", description: "Type text into an element" }
68680
- ];
68681
- }
68682
- function manifestDeclaresBrowserDependency(manifest) {
68683
- return dependencyMaps(manifest).some((deps) => Object.entries(deps).some(([name, dep]) => isBrowserDependencyName(name) || isBrowserDependencyName(dep?.package)));
68684
- }
68685
- function manifestMapsDependencyToBrowser(manifest, dependencyName) {
68686
- const maps = dependencyMaps(manifest);
68687
- if (maps.length === 0)
68688
- return false;
68689
- for (const deps of maps) {
68690
- const declared = deps[dependencyName];
68691
- if (declared) {
68692
- return isBrowserDependencyName(dependencyName) || isBrowserDependencyName(declared.package);
68693
- }
68694
- }
68695
- return isBrowserDependencyName(dependencyName) && manifestDeclaresBrowserDependency(manifest);
68696
- }
68697
- function dependencyMaps(manifest) {
68698
- return [manifest?.dependencies, manifest?.dependencySlots].filter((deps) => !!deps);
68699
- }
68700
-
68701
68850
  // ts/daemon/dist/clip-runtime/process.js
68702
68851
  var CLIP_REGISTER_TIMEOUT_MS = 1e4;
68703
68852
  var CLIP_STOP_TIMEOUT_MS = 5e3;
@@ -69452,8 +69601,8 @@ var ClipProvider = class _ClipProvider {
69452
69601
  /**
69453
69602
  * Handle a hosted-browser live-viewer command relayed by clip-service over the
69454
69603
  * stream and reply on the same stream (design §3.4). Always answers the
69455
- * request/reply bridge exactly once — `{result}` on success, `{error:{message}}`
69456
- * on failure — and never throws into the message loop, mirroring the BYOC
69604
+ * request/reply bridge exactly once — `{result}` on success,
69605
+ * `{error:{code?,message}}` on failure — and never throws into the message loop, mirroring the BYOC
69457
69606
  * daemon's handleBrowserProfileViewer (which replies over HTTP instead).
69458
69607
  */
69459
69608
  async handleViewerCommand(cmd) {
@@ -69466,6 +69615,10 @@ var ClipProvider = class _ClipProvider {
69466
69615
  return;
69467
69616
  }
69468
69617
  try {
69618
+ const dispatchDeadlineUnixMs = parseViewerDispatchDeadlineUnixMs(cmd.dispatchDeadlineUnixMs);
69619
+ if (isViewerReadinessCommand(cmd.command) && dispatchDeadlineUnixMs === void 0) {
69620
+ throw viewerReadinessDeadlineError("because the ProviderStream command has no authenticated deadline");
69621
+ }
69469
69622
  let input;
69470
69623
  if (cmd.input) {
69471
69624
  const decoded = Buffer.from(cmd.input, "base64").toString("utf-8");
@@ -69480,13 +69633,14 @@ var ClipProvider = class _ClipProvider {
69480
69633
  sessionId: cmd.sessionId,
69481
69634
  command: cmd.command,
69482
69635
  input,
69483
- turn: cmd.turn
69636
+ turn: cmd.turn,
69637
+ dispatchDeadlineUnixMs
69484
69638
  });
69485
69639
  await this.sendViewerReply(requestId, { result });
69486
69640
  } catch (err) {
69487
- const message = err instanceof Error ? err.message : String(err);
69488
- this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${message}`);
69489
- await this.sendViewerReply(requestId, { error: { message } }).catch(() => {
69641
+ const failure = viewerCommandFailure(err);
69642
+ this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${failure.message}`);
69643
+ await this.sendViewerReply(requestId, { error: failure }).catch(() => {
69490
69644
  });
69491
69645
  }
69492
69646
  }
@@ -69572,6 +69726,70 @@ var ClipProvider = class _ClipProvider {
69572
69726
  }
69573
69727
  };
69574
69728
 
69729
+ // ts/daemon/dist/clip-runtime/hosted-viewer-command-queue.js
69730
+ var HostedViewerCommandQueue = class {
69731
+ run;
69732
+ tail = Promise.resolve();
69733
+ now;
69734
+ readinessBudgetMs;
69735
+ replyReserveMs;
69736
+ schedule;
69737
+ constructor(run, opts = {}) {
69738
+ this.run = run;
69739
+ this.now = opts.now ?? Date.now;
69740
+ this.readinessBudgetMs = opts.readinessBudgetMs ?? HOSTED_VIEWER_READINESS_POD_BUDGET_MS;
69741
+ this.replyReserveMs = opts.replyReserveMs ?? HOSTED_VIEWER_REPLY_RESERVE_MS;
69742
+ this.schedule = opts.schedule ?? ((callback, delayMs) => {
69743
+ const handle = setTimeout(callback, delayMs);
69744
+ return {
69745
+ cancel: () => clearTimeout(handle),
69746
+ unref: () => handle.unref?.()
69747
+ };
69748
+ });
69749
+ }
69750
+ enqueue(command) {
69751
+ const receivedAt = this.now();
69752
+ const executionDeadline = this.executionDeadline(command, receivedAt);
69753
+ const run = this.tail.catch(() => {
69754
+ }).then(() => {
69755
+ if (executionDeadline !== void 0 && this.now() >= executionDeadline) {
69756
+ throw viewerReadinessDeadlineError("before hosted pod execution");
69757
+ }
69758
+ return this.run(command, executionDeadline);
69759
+ });
69760
+ this.tail = run.catch(() => {
69761
+ });
69762
+ if (executionDeadline === void 0) {
69763
+ return run;
69764
+ }
69765
+ let timer;
69766
+ const timeout = new Promise((_, reject) => {
69767
+ const delay = Math.max(0, executionDeadline - this.now());
69768
+ timer = this.schedule(() => {
69769
+ reject(viewerReadinessDeadlineError("inside the hosted pod queue"));
69770
+ }, delay);
69771
+ timer.unref?.();
69772
+ });
69773
+ return Promise.race([run, timeout]).finally(() => {
69774
+ timer?.cancel();
69775
+ });
69776
+ }
69777
+ executionDeadline(command, receivedAt) {
69778
+ if (!isViewerReadinessCommand(command.command)) {
69779
+ return void 0;
69780
+ }
69781
+ const dispatchDeadline = command.dispatchDeadlineUnixMs;
69782
+ if (!Number.isSafeInteger(dispatchDeadline) || dispatchDeadline === void 0) {
69783
+ throw viewerReadinessDeadlineError("because the authenticated deadline is missing");
69784
+ }
69785
+ const deadline = Math.min(receivedAt + this.readinessBudgetMs, dispatchDeadline - this.replyReserveMs);
69786
+ if (deadline <= receivedAt) {
69787
+ throw viewerReadinessDeadlineError("before hosted pod receipt");
69788
+ }
69789
+ return deadline;
69790
+ }
69791
+ };
69792
+
69575
69793
  // ts/daemon/dist/clip-runtime/process-manager.js
69576
69794
  import * as fs3 from "node:fs";
69577
69795
  import * as path5 from "node:path";
@@ -70548,14 +70766,10 @@ async function preparePodRuntime(config, env, log) {
70548
70766
  dataDir: path6.join(config.stateDir, "clip-data"),
70549
70767
  browserProfileManager: browser
70550
70768
  });
70551
- let viewerChain = Promise.resolve();
70552
- const onViewerCommand = (cmd) => {
70553
- const run = viewerChain.catch(() => {
70554
- }).then(() => browser.handleViewerCommand(cmd.profileId, cmd.sessionId, cmd.command, cmd.input, cmd.turn));
70555
- viewerChain = run.catch(() => {
70556
- });
70557
- return run;
70558
- };
70769
+ const viewerCommands = new HostedViewerCommandQueue((cmd, executionDeadlineUnixMs) => browser.handleViewerCommand(cmd.profileId, cmd.sessionId, cmd.command, cmd.input, cmd.turn, {
70770
+ deadlineUnixMs: executionDeadlineUnixMs
70771
+ }));
70772
+ const onViewerCommand = viewerCommands.enqueue.bind(viewerCommands);
70559
70773
  const provider = new ClipProvider({
70560
70774
  serviceUrl: config.clipRpcUrl,
70561
70775
  authKey: config.assignmentToken,