@parall/daemon 1.36.1 → 1.38.0

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 (56) hide show
  1. package/bundle/manifest.json +11 -11
  2. package/bundle/parall-browser-pod.js +352 -27
  3. package/bundle/parall-claude-agent.js +1219 -269
  4. package/bundle/parall-codex-agent.js +1225 -276
  5. package/bundle/parall-daemon.js +33552 -31712
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +2 -2
  8. package/dist/clip-runtime/browser-daemon-env.d.ts +29 -0
  9. package/dist/clip-runtime/browser-daemon-env.d.ts.map +1 -0
  10. package/dist/clip-runtime/browser-daemon-env.js +70 -0
  11. package/dist/clip-runtime/browser-profile-manager.d.ts +18 -5
  12. package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
  13. package/dist/clip-runtime/browser-profile-manager.js +149 -24
  14. package/dist/clip-runtime/browser-profile-pool.d.ts +250 -0
  15. package/dist/clip-runtime/browser-profile-pool.d.ts.map +1 -0
  16. package/dist/clip-runtime/browser-profile-pool.js +581 -0
  17. package/dist/clip-runtime/index.d.ts +2 -2
  18. package/dist/clip-runtime/index.d.ts.map +1 -1
  19. package/dist/clip-runtime/index.js +2 -1
  20. package/dist/clip-runtime/process-manager.d.ts +5 -3
  21. package/dist/clip-runtime/process-manager.d.ts.map +1 -1
  22. package/dist/clip-runtime/subprocess.d.ts +14 -0
  23. package/dist/clip-runtime/subprocess.d.ts.map +1 -1
  24. package/dist/clip-runtime/subprocess.js +49 -0
  25. package/dist/config.d.ts +14 -15
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +8 -26
  28. package/dist/daemon-main.d.ts +8 -0
  29. package/dist/daemon-main.d.ts.map +1 -0
  30. package/dist/daemon-main.js +165 -0
  31. package/dist/daemon-paths.d.ts +14 -0
  32. package/dist/daemon-paths.d.ts.map +1 -0
  33. package/dist/daemon-paths.js +26 -0
  34. package/dist/daemon-update-mode.d.ts +8 -0
  35. package/dist/daemon-update-mode.d.ts.map +1 -0
  36. package/dist/daemon-update-mode.js +18 -0
  37. package/dist/index.js +41 -167
  38. package/dist/runtime-bin-resolver.d.ts +4 -0
  39. package/dist/runtime-bin-resolver.d.ts.map +1 -1
  40. package/dist/runtime-bin-resolver.js +40 -7
  41. package/dist/runtime-detector.d.ts +30 -0
  42. package/dist/runtime-detector.d.ts.map +1 -0
  43. package/dist/runtime-detector.js +100 -0
  44. package/dist/supervisor.d.ts +53 -2
  45. package/dist/supervisor.d.ts.map +1 -1
  46. package/dist/supervisor.js +604 -67
  47. package/dist/update-health-gate.d.ts +66 -0
  48. package/dist/update-health-gate.d.ts.map +1 -0
  49. package/dist/update-health-gate.js +93 -0
  50. package/dist/updater-manifest.d.ts +2 -1
  51. package/dist/updater-manifest.d.ts.map +1 -1
  52. package/dist/updater-manifest.js +38 -7
  53. package/dist/updater.d.ts +13 -2
  54. package/dist/updater.d.ts.map +1 -1
  55. package/dist/updater.js +126 -17
  56. package/package.json +6 -6
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.36.1",
3
- "built_at": "2026-06-26T10:09:22.541Z",
2
+ "version": "1.38.0",
3
+ "built_at": "2026-07-06T10:59:40.316Z",
4
4
  "min_node_version": "20.0.0",
5
5
  "files": {
6
6
  "bb-browser-daemon.js": {
@@ -16,25 +16,25 @@
16
16
  "size": 18
17
17
  },
18
18
  "parall-browser-pod.js": {
19
- "sha256": "a9f602f001380c7fdfd2db7e7109343f07267a1acd6c020f5802a949769cd9fb",
20
- "size": 1682059
19
+ "sha256": "9426af841c2011c341e28e03a0176da146e6c30da88878cc2e70f3211512e7e8",
20
+ "size": 1697014
21
21
  },
22
22
  "parall-claude-agent.js": {
23
- "sha256": "f38cb2274682403d735e9ea3772d56fcfeee9636a1866eb599e41b4a23075ea6",
24
- "size": 1574204
23
+ "sha256": "11436cabd17a19f2af8eb433a506efc6342efb76fbe25c8c882aea13220b5350",
24
+ "size": 1616948
25
25
  },
26
26
  "parall-codex-agent.js": {
27
- "sha256": "ceee4b4852cb9cc058dbb70022c8fcf81fd802d1bff709d063386b95a9d501e4",
28
- "size": 1605214
27
+ "sha256": "5d28aecbd029c64cd1402f748e12ae6bdf502ab38a21b95bb546407469e4d826",
28
+ "size": 1647758
29
29
  },
30
30
  "parall-daemon.js": {
31
- "sha256": "a7cae49ec5efb258f60e35f9a03b71a9edbd947886b0b037c761aaa36bf9af79",
32
- "size": 1612971
31
+ "sha256": "0470fa07cce7477c70c075eb939e9e14b6dab40cfa4a6f609974c68f28537923",
32
+ "size": 1717086
33
33
  },
34
34
  "parall-openclaw-agent.js": {
35
35
  "sha256": "505433b4bf3a4b8dbb7b96978d8693d6c48079f4513282ff1ee59a6d13b3eddf",
36
36
  "size": 9801
37
37
  }
38
38
  },
39
- "signature": "P/Lgycpu5bMlOGiMzEto4lrKQuoz4c17B0IJ0xmyP6n6DBpuUIEEGJZKs9lQtkz1kVhEgQ4dN9IipxLIZ6hhCg=="
39
+ "signature": "bfGTE2slDSlantJRoa8j9CEENod1u89vVzhn0aFI29c+V9Lj2IuXdage/Mu3vfr+kDUYCisH9DPqBD6o7X4kDA=="
40
40
  }
@@ -8055,8 +8055,8 @@ var init_requestBuilder = __esm({
8055
8055
  async build() {
8056
8056
  const { hostname, protocol = "https", port, path: basePath } = await this.context.endpoint();
8057
8057
  this.path = basePath;
8058
- for (const resolvePath of this.resolvePathStack) {
8059
- resolvePath(this.path);
8058
+ for (const resolvePath2 of this.resolvePathStack) {
8059
+ resolvePath2(this.path);
8060
8060
  }
8061
8061
  return new import_protocol_http5.HttpRequest({
8062
8062
  protocol,
@@ -35113,6 +35113,7 @@ var ENDPOINTS = {
35113
35113
  // Org-scoped
35114
35114
  ORG: (orgId) => `${API_BASE}/orgs/${orgId}`,
35115
35115
  ORG_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/members`,
35116
+ ORG_MEMBERS_FORMER: (orgId) => `${API_BASE}/orgs/${orgId}/members/former`,
35116
35117
  TEAMS: (orgId) => `${API_BASE}/orgs/${orgId}/teams`,
35117
35118
  ORG_MEMBERS_ONLINE: (orgId) => `${API_BASE}/orgs/${orgId}/members/online`,
35118
35119
  ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
@@ -35262,6 +35263,19 @@ var ENDPOINTS = {
35262
35263
  EXTERNAL_TRIGGER: (orgId, triggerId) => `${API_BASE}/orgs/${orgId}/external-triggers/${triggerId}`,
35263
35264
  EXTERNAL_TRIGGER_RUNS: (orgId) => `${API_BASE}/orgs/${orgId}/external-trigger-runs`,
35264
35265
  EXTERNAL_TRIGGER_RUN: (orgId, runId) => `${API_BASE}/orgs/${orgId}/external-trigger-runs/${runId}`,
35266
+ // External IM channel (org-scoped, platform-mediated Feishu/Slack)
35267
+ CHANNEL_CONNECTIONS: (orgId) => `${API_BASE}/orgs/${orgId}/channel-connections`,
35268
+ CHANNEL_CONNECTION: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}`,
35269
+ CHANNEL_CONNECTION_CREDENTIALS: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/credentials`,
35270
+ CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/ingress-token/regenerate`,
35271
+ CHANNEL_CONNECTION_CONVERSATIONS: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/conversations`,
35272
+ CHANNEL_CONVERSATION: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}`,
35273
+ CHANNEL_CONVERSATION_MESSAGES: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/messages`,
35274
+ CHANNEL_CONVERSATION_SESSION: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/session`,
35275
+ CHANNEL_MESSAGE: (orgId, messageId) => `${API_BASE}/orgs/${orgId}/channel-messages/${messageId}`,
35276
+ CHANNEL_PROVISIONING: (orgId) => `${API_BASE}/orgs/${orgId}/channel-provisioning`,
35277
+ CHANNEL_PROVISIONING_SESSION: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}`,
35278
+ CHANNEL_PROVISIONING_CANCEL: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}/cancel`,
35265
35279
  // Invitations (org-scoped, admin)
35266
35280
  ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
35267
35281
  ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
@@ -35279,9 +35293,16 @@ var ENDPOINTS = {
35279
35293
  INVITE_LINK_JOIN: `${API_BASE}/invite-link/join`,
35280
35294
  // Wikis (org-scoped, served by wiki-service)
35281
35295
  WIKIS: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis`,
35296
+ // Recycle bin — soft-deleted wikis (owner only). Must precede WIKI in the
35297
+ // backend router so `deleted` isn't captured as a {wikiId}; the SDK builder
35298
+ // is just a string.
35299
+ WIKIS_DELETED: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis/deleted`,
35300
+ // Detail URL — also reused for DELETE (soft-delete) and `${WIKI}/restore`.
35282
35301
  WIKI: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}`,
35302
+ WIKI_RESTORE: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/restore`,
35283
35303
  WIKI_TREE: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/tree`,
35284
35304
  WIKI_BLOB: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/blob`,
35305
+ WIKI_COPY: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/copy`,
35285
35306
  WIKI_NODE_SECTIONS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/node-sections`,
35286
35307
  WIKI_SEARCH: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/search`,
35287
35308
  WIKI_PAGE_INDEX: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/page-index`,
@@ -35335,6 +35356,11 @@ var ENDPOINTS = {
35335
35356
  DISPATCH_ACK_BY_ID: (orgId, id) => `${API_BASE}/orgs/${orgId}/dispatch/${id}/ack`,
35336
35357
  DISPATCH_EXPIRE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/expire`,
35337
35358
  DISPATCH_BY_MESSAGES: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/by-messages`,
35359
+ DISPATCH_CLAIM: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/claim`,
35360
+ DISPATCH_STEER: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/steer`,
35361
+ DISPATCH_COMPLETE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete`,
35362
+ DISPATCH_RELEASE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/release`,
35363
+ DISPATCH_HEARTBEAT: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/heartbeat`,
35338
35364
  // Unread
35339
35365
  UNREAD: `${API_BASE}/me/unread`,
35340
35366
  ORG_UNREAD: (orgId) => `${API_BASE}/orgs/${orgId}/unread`,
@@ -35364,6 +35390,14 @@ var ENDPOINTS = {
35364
35390
  BILLING_AUTO_RELOAD: (orgId) => `${API_BASE}/orgs/${orgId}/billing/auto-reload`,
35365
35391
  BILLING_SETUP_INTENT: (orgId) => `${API_BASE}/orgs/${orgId}/billing/setup-intent`,
35366
35392
  COMPUTE_PRICING: () => `${API_BASE}/billing/compute-pricing`,
35393
+ // Runtime capability table (public, no auth) — SSOT for the create/settings
35394
+ // interlock: per-runtime compute modes, native model family, cross-family
35395
+ // availability, and recommended model.
35396
+ RUNTIMES: () => `${API_BASE}/runtimes`,
35397
+ // Model catalog (public, no auth) — served from the server's DB-backed
35398
+ // snapshot. Optional include_model keeps an agent's pinned hidden legacy
35399
+ // model representable in settings pickers.
35400
+ MODELS: (includeModel) => includeModel ? `${API_BASE}/models?include_model=${encodeURIComponent(includeModel)}` : `${API_BASE}/models`,
35367
35401
  // ADMIN_GRANTS intentionally NOT exported here — it sits under the
35368
35402
  // unauthenticated `/internal/admin/*` surface and must not bleed into the
35369
35403
  // public SDK. Admin dashboard hits the URL directly from its own client.
@@ -35376,6 +35410,7 @@ var ENDPOINTS = {
35376
35410
  AGENT_CLIP: (orgId, agentId, clipId) => `${CLIP_BASE}/orgs/${orgId}/agents/${agentId}/clips/${clipId}`,
35377
35411
  CLIP_INVOKE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/invoke`,
35378
35412
  CLIP_ONLINE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/online`,
35413
+ BROWSER_RUNTIME_STATUS: (orgId) => `${CLIP_BASE}/orgs/${orgId}/browser-runtime/status`,
35379
35414
  BROWSER_PROFILES: (orgId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles`,
35380
35415
  BROWSER_PROFILE: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}`,
35381
35416
  BROWSER_PROFILE_OPEN: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/open`,
@@ -35571,6 +35606,7 @@ var ParallClient = class _ParallClient {
35571
35606
  const rawErrorBody = await res.json().catch(() => ({}));
35572
35607
  throw buildApiError(res, rawErrorBody);
35573
35608
  }
35609
+ opts?.onStatus?.(res.status);
35574
35610
  if (res.status === 204)
35575
35611
  return void 0;
35576
35612
  if (res.status === 202) {
@@ -35720,6 +35756,13 @@ var ParallClient = class _ParallClient {
35720
35756
  const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS(orgId));
35721
35757
  return res.data;
35722
35758
  }
35759
+ /** User IDs of soft-removed (former) org members — for marking their avatar /
35760
+ * name as "left" where dormant relations still surface them (DMs, task
35761
+ * assignees, message history). */
35762
+ async getFormerMemberIds(orgId) {
35763
+ const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS_FORMER(orgId));
35764
+ return res.user_ids ?? [];
35765
+ }
35723
35766
  async getTeams(orgId) {
35724
35767
  const res = await this.request("GET", ENDPOINTS.TEAMS(orgId));
35725
35768
  return res.data;
@@ -35897,6 +35940,21 @@ var ParallClient = class _ParallClient {
35897
35940
  async sendMessage(orgId, chatId, req) {
35898
35941
  return this.request("POST", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req);
35899
35942
  }
35943
+ /**
35944
+ * sendMessage variant that also reports whether the server answered with an
35945
+ * idempotent replay (HTTP 200 — the message already existed for this
35946
+ * idempotency/effect key) instead of a fresh create (201). Used by the CLI
35947
+ * to surface "already sent by a previous run (deduplicated)".
35948
+ */
35949
+ async sendMessageDetailed(orgId, chatId, req) {
35950
+ let status = 0;
35951
+ const message = await this.request("POST", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req, void 0, false, {
35952
+ onStatus: (s) => {
35953
+ status = s;
35954
+ }
35955
+ });
35956
+ return { message, deduplicated: status === 200 };
35957
+ }
35900
35958
  async getMessages(orgId, chatId, params) {
35901
35959
  return this.request("GET", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), void 0, params);
35902
35960
  }
@@ -36208,20 +36266,25 @@ var ParallClient = class _ParallClient {
36208
36266
  return res.data;
36209
36267
  }
36210
36268
  /** `POST /machines/me/browser-profiles/{profileId}/status` — report runtime status. */
36211
- async reportBrowserProfileStatus(profileId, status, errorMsg) {
36269
+ async reportBrowserProfileStatus(profileId, status, errorMsg, generation) {
36212
36270
  await this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_STATUS(profileId), {
36213
36271
  status,
36214
- ...errorMsg ? { error_msg: errorMsg } : {}
36272
+ ...errorMsg ? { error_msg: errorMsg } : {},
36273
+ ...generation !== void 0 ? { generation } : {}
36215
36274
  });
36216
36275
  }
36217
36276
  /**
36218
36277
  * `POST /machines/me/health` — bump the Machine's `updated_at` to now and
36219
- * report daemon state. The daemon should call this on a fixed cadence (e.g.
36220
- * every 30s) so an external observer can detect a wedged supervisor. Both
36278
+ * report daemon state. The daemon calls this on startup and again whenever
36279
+ * periodic runtime re-detection produces a CHANGED result (there is no
36280
+ * fixed-cadence keepalive loop today — steady state posts nothing). All
36221
36281
  * fields are optional and only persisted when changed:
36222
36282
  * - `daemonVersion` — the running bundle/launcher version.
36223
36283
  * - `selfUpdateCapable` — whether the daemon can act on a machine.update
36224
36284
  * signal (true under a service manager, false for bare `npx` foreground).
36285
+ * - `detectedRuntimes` — runtime CLIs found on the host. Omitted = no
36286
+ * report this beat (server keeps the stored value); [] = detection ran
36287
+ * and found nothing (server clears to empty).
36225
36288
  */
36226
36289
  async postMachineHeartbeat(opts) {
36227
36290
  const body = {};
@@ -36229,6 +36292,8 @@ var ParallClient = class _ParallClient {
36229
36292
  body.daemon_version = opts.daemonVersion;
36230
36293
  if (opts?.selfUpdateCapable !== void 0)
36231
36294
  body.self_update_capable = opts.selfUpdateCapable;
36295
+ if (opts?.detectedRuntimes !== void 0)
36296
+ body.detected_runtimes = opts.detectedRuntimes;
36232
36297
  return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH, Object.keys(body).length > 0 ? body : void 0);
36233
36298
  }
36234
36299
  async reportAgentWorkspaceState(agentId, state2) {
@@ -36351,6 +36416,30 @@ var ParallClient = class _ParallClient {
36351
36416
  const params = maxAgeHours !== void 0 ? { max_age_hours: String(maxAgeHours) } : void 0;
36352
36417
  return this.request("POST", ENDPOINTS.DISPATCH_EXPIRE(orgId), void 0, params);
36353
36418
  }
36419
+ /**
36420
+ * Claim a dispatch lane (explicit consume endpoint). Occupies the
36421
+ * (agent, target, thread) lane and folds claimable WorkItems into it;
36422
+ * `claimed: false` means a healthy incumbent holds the lane.
36423
+ */
36424
+ async claimDispatch(orgId, req) {
36425
+ return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
36426
+ }
36427
+ /** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
36428
+ async steerDispatch(orgId, req) {
36429
+ return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
36430
+ }
36431
+ /** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
36432
+ async completeDispatch(orgId, req) {
36433
+ return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
36434
+ }
36435
+ /** Release a lane on graceful shutdown — members return to pending immediately. */
36436
+ async releaseDispatchLane(orgId, lane) {
36437
+ return this.request("POST", ENDPOINTS.DISPATCH_RELEASE(orgId), { lane });
36438
+ }
36439
+ /** Renew a live lane's lease (long-turn keepalive). 409 STALE_LANE when dethroned. */
36440
+ async heartbeatDispatchLane(orgId, req) {
36441
+ return this.request("POST", ENDPOINTS.DISPATCH_HEARTBEAT(orgId), req);
36442
+ }
36354
36443
  async getDispatchByMessages(orgId, chatId, messageIds) {
36355
36444
  const params = { chat_id: chatId, message_ids: messageIds.join(",") };
36356
36445
  return this.request("GET", ENDPOINTS.DISPATCH_BY_MESSAGES(orgId), void 0, params);
@@ -36547,6 +36636,59 @@ var ParallClient = class _ParallClient {
36547
36636
  async deleteExternalConnection(orgId, connectionId) {
36548
36637
  return this.request("DELETE", ENDPOINTS.EXTERNAL_CONNECTION(orgId, connectionId));
36549
36638
  }
36639
+ // ---- External IM channel (org-scoped) ----
36640
+ async createChannelConnection(orgId, input) {
36641
+ return this.request("POST", ENDPOINTS.CHANNEL_CONNECTIONS(orgId), input);
36642
+ }
36643
+ async listChannelConnections(orgId) {
36644
+ return this.request("GET", ENDPOINTS.CHANNEL_CONNECTIONS(orgId));
36645
+ }
36646
+ async getChannelConnection(orgId, connectionId) {
36647
+ return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
36648
+ }
36649
+ async updateChannelConnection(orgId, connectionId, patch) {
36650
+ return this.request("PATCH", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId), patch);
36651
+ }
36652
+ async archiveChannelConnection(orgId, connectionId) {
36653
+ return this.request("DELETE", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
36654
+ }
36655
+ async deliverChannelCredentials(orgId, connectionId, credentials) {
36656
+ return this.request("POST", ENDPOINTS.CHANNEL_CONNECTION_CREDENTIALS(orgId, connectionId), credentials);
36657
+ }
36658
+ async regenerateChannelIngressToken(orgId, connectionId) {
36659
+ return this.request("POST", ENDPOINTS.CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE(orgId, connectionId));
36660
+ }
36661
+ /** Start a Feishu one-click provisioning session (device flow QR). */
36662
+ async initiateChannelProvisioning(orgId, input) {
36663
+ return this.request("POST", ENDPOINTS.CHANNEL_PROVISIONING(orgId), input);
36664
+ }
36665
+ /**
36666
+ * Lazy status poll — server-side this may forward one provider poll, so
36667
+ * call it at the session's `poll_interval_seconds` cadence, not faster.
36668
+ */
36669
+ async getChannelProvisioningSession(orgId, sessionId) {
36670
+ return this.request("GET", ENDPOINTS.CHANNEL_PROVISIONING_SESSION(orgId, sessionId));
36671
+ }
36672
+ async cancelChannelProvisioning(orgId, sessionId) {
36673
+ return this.request("POST", ENDPOINTS.CHANNEL_PROVISIONING_CANCEL(orgId, sessionId));
36674
+ }
36675
+ async listChannelConversations(orgId, connectionId) {
36676
+ return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
36677
+ }
36678
+ async getChannelConversation(orgId, conversationId) {
36679
+ return this.request("GET", ENDPOINTS.CHANNEL_CONVERSATION(orgId, conversationId));
36680
+ }
36681
+ async listChannelConversationMessages(orgId, conversationId, limit) {
36682
+ return this.request("GET", ENDPOINTS.CHANNEL_CONVERSATION_MESSAGES(orgId, conversationId), void 0, { limit });
36683
+ }
36684
+ async setChannelConversationSession(orgId, conversationId, agentSessionId) {
36685
+ return this.request("PATCH", ENDPOINTS.CHANNEL_CONVERSATION_SESSION(orgId, conversationId), {
36686
+ agent_session_id: agentSessionId
36687
+ });
36688
+ }
36689
+ async getChannelMessage(orgId, messageId) {
36690
+ return this.request("GET", ENDPOINTS.CHANNEL_MESSAGE(orgId, messageId));
36691
+ }
36550
36692
  async getExternalTriggerSchema(orgId, connectionId) {
36551
36693
  return this.request("GET", ENDPOINTS.EXTERNAL_TRIGGER_SCHEMA(orgId, connectionId));
36552
36694
  }
@@ -36588,6 +36730,23 @@ var ParallClient = class _ParallClient {
36588
36730
  async getWiki(orgId, wikiId) {
36589
36731
  return this.request("GET", ENDPOINTS.WIKI(orgId, wikiId));
36590
36732
  }
36733
+ /** Soft-delete an entire wiki (org owner only; the default wiki is
36734
+ * protected → 403 WIKI_PROTECTED). Returns the deleted wiki. Recoverable
36735
+ * via restoreWiki within the retention window. */
36736
+ async deleteWiki(orgId, wikiId) {
36737
+ return this.request("DELETE", ENDPOINTS.WIKI(orgId, wikiId));
36738
+ }
36739
+ /** Restore a soft-deleted wiki (org owner only). 409 WIKI_PURGE_STARTED once
36740
+ * purge has been claimed (no longer restorable), or 404 once the row is gone.
36741
+ * Returns the restored wiki. */
36742
+ async restoreWiki(orgId, wikiId) {
36743
+ return this.request("POST", ENDPOINTS.WIKI_RESTORE(orgId, wikiId));
36744
+ }
36745
+ /** Recycle bin — list soft-deleted wikis for the org (owner only). */
36746
+ async getDeletedWikis(orgId) {
36747
+ const res = await this.request("GET", ENDPOINTS.WIKIS_DELETED(orgId));
36748
+ return res.data;
36749
+ }
36591
36750
  async getWikiTree(orgId, wikiId, params) {
36592
36751
  return this.request("GET", ENDPOINTS.WIKI_TREE(orgId, wikiId), void 0, params);
36593
36752
  }
@@ -36621,6 +36780,15 @@ var ParallClient = class _ParallClient {
36621
36780
  blob.signed_url = this.absoluteMediaUrl(blob.signed_url);
36622
36781
  return blob;
36623
36782
  }
36783
+ /**
36784
+ * Copy one wiki file to another path as an independent snapshot (no sync).
36785
+ * Used to "share a private file out": copy a file from the caller's personal
36786
+ * namespace (`users/{userId}/…`) into the public wiki, leaving the original
36787
+ * untouched. 409 `FILE_EXISTS` if `dest_path` already exists.
36788
+ */
36789
+ async copyWikiFile(orgId, wikiId, req) {
36790
+ return this.request("POST", ENDPOINTS.WIKI_COPY(orgId, wikiId), req);
36791
+ }
36624
36792
  async getWikiNodeSections(orgId, wikiId, params) {
36625
36793
  return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
36626
36794
  }
@@ -36894,6 +37062,19 @@ var ParallClient = class _ParallClient {
36894
37062
  const resp = await this.request("GET", ENDPOINTS.COMPUTE_PRICING());
36895
37063
  return resp.data;
36896
37064
  }
37065
+ /** Runtime capability table (public) — SSOT for the create/settings interlock. */
37066
+ async getRuntimes() {
37067
+ const resp = await this.request("GET", ENDPOINTS.RUNTIMES());
37068
+ return resp.data;
37069
+ }
37070
+ /**
37071
+ * Platform model catalog (public) — DB-backed, replaces the compile-time
37072
+ * PLATFORM_MODELS constant. Pass includeModel to keep an agent's pinned
37073
+ * hidden legacy model representable (settings picker).
37074
+ */
37075
+ async getModels(includeModel) {
37076
+ return this.request("GET", ENDPOINTS.MODELS(includeModel));
37077
+ }
36897
37078
  // ---- Clips ----
36898
37079
  async listClips(orgId) {
36899
37080
  const resp = await this.request("GET", ENDPOINTS.CLIPS(orgId));
@@ -36938,6 +37119,11 @@ var ParallClient = class _ParallClient {
36938
37119
  const resp = await this.request("GET", ENDPOINTS.CLIP_ONLINE(orgId));
36939
37120
  return resp.data;
36940
37121
  }
37122
+ /** Deployment-wide hosted browser runtime availability — drives whether the
37123
+ * create UI offers the platform-hosted placement. Fail-closed server-side. */
37124
+ async getBrowserRuntimeStatus(orgId) {
37125
+ return this.request("GET", ENDPOINTS.BROWSER_RUNTIME_STATUS(orgId));
37126
+ }
36941
37127
  /** Org-wide browser-profile discovery list. Returns the sanitized
36942
37128
  * {@link BrowserProfileListItem} shape (not the full domain model), each row
36943
37129
  * carrying a per-viewer `can_open` control hint. */
@@ -37046,6 +37232,50 @@ import { createRequire } from "node:module";
37046
37232
  import * as path from "node:path";
37047
37233
  import { fileURLToPath } from "node:url";
37048
37234
 
37235
+ // ts/daemon/dist/clip-runtime/browser-daemon-env.js
37236
+ var BROWSER_DAEMON_STRIPPED_ENV = [
37237
+ "PRLL_API_KEY",
37238
+ // machine key (mck_) — BYOC + agent control plane
37239
+ "PRLL_PROVIDER_CONFIG",
37240
+ // JSON ProviderConfig blob — may carry BYO LLM api keys
37241
+ "PRLL_ASSIGNMENT_TOKEN",
37242
+ // hosted pod platform assignment token (pba_)
37243
+ "PRLL_POOL_TOKEN",
37244
+ // hosted warm-pool bootstrap token (pba_)
37245
+ "AWS_ACCESS_KEY_ID",
37246
+ // hosted S3 browser-state STS creds (browser-pod.ts)
37247
+ "AWS_SECRET_ACCESS_KEY",
37248
+ "AWS_SESSION_TOKEN",
37249
+ // AWS credential-RESOLUTION vars: the SDK derives creds from these too (IRSA
37250
+ // web-identity, ECS/EKS container creds, named profile, on-disk cred/config
37251
+ // files), so a child inheriting them could assume the pod's role even without
37252
+ // the static keys above. bb-browser uses none of them.
37253
+ "AWS_WEB_IDENTITY_TOKEN_FILE",
37254
+ "AWS_ROLE_ARN",
37255
+ "AWS_ROLE_SESSION_NAME",
37256
+ "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI",
37257
+ "AWS_CONTAINER_CREDENTIALS_FULL_URI",
37258
+ "AWS_CONTAINER_AUTHORIZATION_TOKEN",
37259
+ "AWS_PROFILE",
37260
+ "AWS_SHARED_CREDENTIALS_FILE",
37261
+ "AWS_CONFIG_FILE",
37262
+ "AWS_REGION",
37263
+ // hosted browser-state config — bb-browser does not use it
37264
+ "PRLL_BROWSER_STATE_REGION",
37265
+ // browser-pod AWS_REGION fallback — same config family
37266
+ "PRLL_BROWSER_STATE_BUCKET",
37267
+ "PRLL_STATE_GENERATION",
37268
+ "PRLL_BROWSER_STATE_ENDPOINT"
37269
+ ];
37270
+ function buildBrowserDaemonEnv(baseEnv, homeDir) {
37271
+ const env = { ...baseEnv, BB_BROWSER_HOME: homeDir };
37272
+ clearAllProviderCreds(env);
37273
+ for (const key of BROWSER_DAEMON_STRIPPED_ENV) {
37274
+ delete env[key];
37275
+ }
37276
+ return env;
37277
+ }
37278
+
37049
37279
  // ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
37050
37280
  import { spawn } from "node:child_process";
37051
37281
 
@@ -37075,6 +37305,41 @@ async function findFreePort() {
37075
37305
  function sleep(ms) {
37076
37306
  return new Promise((resolve2) => setTimeout(resolve2, ms));
37077
37307
  }
37308
+ function waitForChildExit(child, log, graceMs = 5e3, hardCapMs = 15e3) {
37309
+ if (child.exitCode !== null || child.signalCode !== null)
37310
+ return Promise.resolve();
37311
+ return new Promise((resolve2) => {
37312
+ let killTimer;
37313
+ let capTimer;
37314
+ const finish = () => {
37315
+ child.removeListener("exit", finish);
37316
+ if (killTimer)
37317
+ clearTimeout(killTimer);
37318
+ if (capTimer)
37319
+ clearTimeout(capTimer);
37320
+ resolve2();
37321
+ };
37322
+ child.once("exit", finish);
37323
+ try {
37324
+ child.kill("SIGTERM");
37325
+ } catch {
37326
+ }
37327
+ killTimer = setTimeout(() => {
37328
+ if (child.exitCode === null && child.signalCode === null) {
37329
+ try {
37330
+ child.kill("SIGKILL");
37331
+ } catch {
37332
+ }
37333
+ }
37334
+ }, graceMs);
37335
+ capTimer = setTimeout(() => {
37336
+ log?.warn(`child did not exit within ${hardCapMs}ms after SIGKILL; proceeding`);
37337
+ finish();
37338
+ }, hardCapMs);
37339
+ killTimer.unref?.();
37340
+ capTimer.unref?.();
37341
+ });
37342
+ }
37078
37343
 
37079
37344
  // ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
37080
37345
  var BB_VIEWER_HEALTH_TIMEOUT_MS = 1e4;
@@ -37653,6 +37918,7 @@ function filterTabListToAccount(result, account) {
37653
37918
 
37654
37919
  // ts/daemon/dist/clip-runtime/browser-profile-manager.js
37655
37920
  var BB_BROWSER_DAEMON_START_TIMEOUT_MS = 15e3;
37921
+ var BB_BROWSER_DAEMON_START_ATTEMPTS = 3;
37656
37922
  var BrowserCommandError = class extends Error {
37657
37923
  method;
37658
37924
  account;
@@ -37668,6 +37934,11 @@ var BrowserProfileManager = class {
37668
37934
  daemon = null;
37669
37935
  starting = null;
37670
37936
  restarting = null;
37937
+ // Set by stop() and never reset (stop() is terminal — the pool disposes the
37938
+ // manager and the hosted pod exits). Makes a startDaemon() that resolves after
37939
+ // stop() began un-adoptable, so a late child can't be reassigned to this.daemon
37940
+ // and escape teardown.
37941
+ stopping = false;
37671
37942
  ensuredAccounts = /* @__PURE__ */ new Set();
37672
37943
  ensuringAccounts = /* @__PURE__ */ new Map();
37673
37944
  reportedStatuses = /* @__PURE__ */ new Map();
@@ -37718,9 +37989,12 @@ var BrowserProfileManager = class {
37718
37989
  throw err;
37719
37990
  }
37720
37991
  }
37721
- async ensureRuntime(profileId) {
37992
+ async ensureRuntime(profileId, opts = {}) {
37722
37993
  if (!profileId)
37723
37994
  throw new Error("browser profile id is required");
37995
+ if (opts.forceStatusReport) {
37996
+ this.reportedStatuses.delete(profileId);
37997
+ }
37724
37998
  try {
37725
37999
  await this.withDaemonRecovery(() => this.ensureAccount(profileId), `ensure runtime for ${profileId}`);
37726
38000
  this.reportStatus(profileId, "running");
@@ -37769,22 +38043,36 @@ var BrowserProfileManager = class {
37769
38043
  }
37770
38044
  }
37771
38045
  async stop() {
38046
+ this.stopping = true;
37772
38047
  this.viewer.shutdown();
38048
+ const inflight = this.starting;
38049
+ const restarting = this.restarting;
37773
38050
  const daemon = this.daemon;
37774
38051
  this.daemon = null;
37775
38052
  this.starting = null;
37776
38053
  this.ensuredAccounts.clear();
37777
38054
  this.ensuringAccounts.clear();
37778
38055
  this.reportedStatuses.clear();
37779
- if (!daemon)
37780
- return;
37781
- try {
37782
- await this.post("/shutdown", daemon, void 0, 3e3);
37783
- } catch {
38056
+ if (restarting) {
38057
+ try {
38058
+ await restarting;
38059
+ } catch {
38060
+ }
37784
38061
  }
37785
- if (daemon.child.exitCode === null && daemon.child.signalCode === null) {
37786
- daemon.child.kill("SIGTERM");
38062
+ let started = null;
38063
+ if (inflight) {
38064
+ try {
38065
+ started = await inflight;
38066
+ } catch {
38067
+ }
37787
38068
  }
38069
+ await Promise.all([daemon, started].filter((d5) => d5 !== null).map(async (d5) => {
38070
+ try {
38071
+ await this.post("/shutdown", d5, void 0, 3e3);
38072
+ } catch {
38073
+ }
38074
+ await waitForChildExit(d5.child, this.opts.log);
38075
+ }));
37788
38076
  }
37789
38077
  /**
37790
38078
  * Viewer control command entrypoint — delegates to BrowserViewerStreamer
@@ -38010,17 +38298,28 @@ var BrowserProfileManager = class {
38010
38298
  async ensureDaemon() {
38011
38299
  if (this.restarting)
38012
38300
  await this.restarting;
38301
+ if (this.stopping)
38302
+ throw new Error("browser profile manager is stopping");
38013
38303
  if (this.daemon && this.daemon.child.exitCode === null && this.daemon.child.signalCode === null) {
38014
38304
  return this.daemon;
38015
38305
  }
38016
- if (this.starting)
38017
- return this.starting;
38306
+ if (this.starting) {
38307
+ const started = await this.starting;
38308
+ if (this.stopping)
38309
+ throw new Error("browser profile manager is stopping");
38310
+ return started;
38311
+ }
38018
38312
  this.starting = this.startDaemon();
38313
+ const startPromise = this.starting;
38019
38314
  try {
38020
- this.daemon = await this.starting;
38315
+ const started = await startPromise;
38316
+ if (this.stopping)
38317
+ throw new Error("browser profile manager is stopping");
38318
+ this.daemon = started;
38021
38319
  return this.daemon;
38022
38320
  } finally {
38023
- this.starting = null;
38321
+ if (this.starting === startPromise)
38322
+ this.starting = null;
38024
38323
  }
38025
38324
  }
38026
38325
  async withDaemonRecovery(operation2, label) {
@@ -38056,20 +38355,46 @@ var BrowserProfileManager = class {
38056
38355
  await this.post("/shutdown", daemon, void 0, 3e3);
38057
38356
  } catch {
38058
38357
  }
38059
- if (daemon.child.exitCode === null && daemon.child.signalCode === null) {
38060
- daemon.child.kill("SIGTERM");
38061
- }
38358
+ await waitForChildExit(daemon.child, this.opts.log);
38062
38359
  }
38063
38360
  async startDaemon() {
38361
+ let lastErr;
38362
+ for (let attempt = 1; attempt <= BB_BROWSER_DAEMON_START_ATTEMPTS; attempt++) {
38363
+ try {
38364
+ return await this.startDaemonAttempt();
38365
+ } catch (err) {
38366
+ lastErr = err;
38367
+ if (attempt < BB_BROWSER_DAEMON_START_ATTEMPTS) {
38368
+ this.opts.log.warn(`[bb-browser] daemon start attempt ${attempt} failed; retrying with fresh ports: ${String(err)}`);
38369
+ }
38370
+ }
38371
+ }
38372
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
38373
+ }
38374
+ async startDaemonAttempt() {
38064
38375
  const host = "127.0.0.1";
38065
38376
  const port = await findFreePort();
38377
+ let cdpPort = await findFreePort();
38378
+ for (let i5 = 0; cdpPort === port && i5 < 5; i5++) {
38379
+ cdpPort = await findFreePort();
38380
+ }
38381
+ if (cdpPort === port) {
38382
+ throw new Error("bb-browser: could not allocate distinct HTTP and CDP ports");
38383
+ }
38066
38384
  const token = randomToken();
38067
38385
  const daemonPath = resolveBbBrowserDaemonPath();
38068
- const child = spawn2(process.execPath, [daemonPath, "--host", host, "--port", String(port), "--token", token], {
38069
- env: {
38070
- ...process.env,
38071
- BB_BROWSER_HOME: this.opts.homeDir
38072
- },
38386
+ const child = spawn2(process.execPath, [
38387
+ daemonPath,
38388
+ "--host",
38389
+ host,
38390
+ "--port",
38391
+ String(port),
38392
+ "--token",
38393
+ token,
38394
+ "--cdp-port",
38395
+ String(cdpPort)
38396
+ ], {
38397
+ env: buildBrowserDaemonEnv(process.env, this.opts.homeDir),
38073
38398
  stdio: ["ignore", "ignore", "pipe"]
38074
38399
  });
38075
38400
  let childError = null;
@@ -38098,7 +38423,7 @@ var BrowserProfileManager = class {
38098
38423
  await sleep(200);
38099
38424
  }
38100
38425
  }
38101
- child.kill("SIGTERM");
38426
+ await waitForChildExit(child, this.opts.log);
38102
38427
  throw new Error("bb-browser-daemon did not start in time");
38103
38428
  }
38104
38429
  async post(pathName, daemon, body, timeoutMs = 1e4) {