@parall/daemon 1.52.1 → 1.52.2

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,6 +1,6 @@
1
1
  {
2
- "version": "1.52.1",
3
- "built_at": "2026-08-04T09:01:32.526Z",
2
+ "version": "1.52.2",
3
+ "built_at": "2026-08-05T06:13:33.099Z",
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": "811d1173f85dd877563eb8c99f8b3836e3f17bbf9a3af3dcc548877579fc9132",
20
- "size": 2913419
19
+ "sha256": "4d5b8c1f431963dac5ab39cbca769e63db91d0e0c15a118b9c8aa9fba0a19ecf",
20
+ "size": 2916134
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": "f478b2bdde1a2a995f4210d342bd4cf96b386d4063675b89907ef8671fa7c115",
28
- "size": 2773567
27
+ "sha256": "2b780cc9718945476c05ae4f173426c5f064150ce7af499dba339ed293b4afff",
28
+ "size": 2779447
29
29
  },
30
30
  "parall-codex-agent.js": {
31
- "sha256": "1025afe9a0dc14a77ffc8097d8e8a36d6a8b650c2af5b6089f38f2112cd8f46c",
32
- "size": 2804092
31
+ "sha256": "40ec7af38394abf1a926e4b982a08a110f3902cd609bdb1d9c5453f1e3d43434",
32
+ "size": 2810151
33
33
  },
34
34
  "parall-daemon.js": {
35
- "sha256": "99350ce670d97b1ddc5e335c357410fcbe15e8764af2260b8924758dfb592b53",
36
- "size": 2975019
35
+ "sha256": "b2e754ab09cf56e435b5947275968401721b3f194193701a6447e4d0ef2d61a9",
36
+ "size": 2977953
37
37
  },
38
38
  "parall-openclaw-agent.js": {
39
39
  "sha256": "856a1c3a9ae0ffef8efe2481fa1171dbeb1d67cccb88616aa9bf16da96a69355",
40
40
  "size": 9923
41
41
  }
42
42
  },
43
- "signature": "rhiOlCqFmHy4vtj3/iuilCmxdxe3r/5BvEQ49g79vTR39G/4VZeV8yRFhz98NHBe3AHs4w1i9WQar99SupfMCg=="
43
+ "signature": "d11VXm3N6e4KIaGx7lp08AdQA9IE3RNCbcj673a+Wd7u2ibl/5VgBCXAMlOaO9HS3PoCGF1IMh3lsFciaHQ7Ag=="
44
44
  }
@@ -63715,6 +63715,8 @@ var ENDPOINTS = {
63715
63715
  SLACK_HISTORY: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
63716
63716
  SLACK_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
63717
63717
  SLACK_STATUS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
63718
+ // WeChat tier-B read verb (agent-only; internal research preview).
63719
+ WECHAT_CONTACTS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/contacts`,
63718
63720
  // Invitations (org-scoped, admin)
63719
63721
  ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
63720
63722
  ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
@@ -63876,8 +63878,6 @@ var ENDPOINTS = {
63876
63878
  // Live viewer command — on api-server (API_BASE), not clip-service: it rides
63877
63879
  // the machine control-plane request/reply bridge that lives in api-server.
63878
63880
  BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
63879
- // Clip registry (global, served by clip-service → Pinix Hub proxy)
63880
- CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`,
63881
63881
  // Edge device endpoints
63882
63882
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
63883
63883
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
@@ -63894,15 +63894,20 @@ var ENDPOINTS = {
63894
63894
  ORG_EDGE_VIEWER_COMMAND: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
63895
63895
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
63896
63896
  CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
63897
- // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
63898
- // Pinix Hub catalog proxy above is a different, id-less surface)
63897
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries)
63899
63898
  ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
63899
+ ORG_CLIP_REGISTRY_CLIP: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}`,
63900
+ ORG_CLIP_REGISTRY_PUBLISH: (orgId) => `/api/v1/orgs/${orgId}/clip-registry/publish`,
63900
63901
  ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
63901
63902
  ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`,
63903
+ ORG_CLIP_UNINSTALL: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/uninstall`,
63904
+ // Per-org agent exec access for one clip (default all agents; PUT human-only)
63905
+ ORG_CLIP_EXEC_ACCESS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/exec-access`,
63902
63906
  // MCP clip server config (cap:clip-mcp; publisher-org only — cross-org gets
63903
63907
  // 403 MCP_CROSS_ORG_DISABLED on the whole family, reads included)
63904
63908
  ORG_CLIP_MCP_CONFIG: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config`,
63905
- ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`
63909
+ ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`,
63910
+ ORG_CLIP_MCP_OAUTH_DISCONNECT: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/oauth/disconnect`
63906
63911
  };
63907
63912
 
63908
63913
  // ts/sdk/dist/client.js
@@ -65312,6 +65317,14 @@ var ParallClient = class _ParallClient {
65312
65317
  async setSlackStatus(orgId, input) {
65313
65318
  await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
65314
65319
  }
65320
+ /**
65321
+ * WeChat tier-B read verb (agent-only; internal research preview): the
65322
+ * account's address book — friend wxids, saved group room ids, followed
65323
+ * official accounts. Same live gate as the send verb.
65324
+ */
65325
+ async listWechatContacts(orgId) {
65326
+ return this.request("GET", ENDPOINTS.WECHAT_CONTACTS(orgId));
65327
+ }
65315
65328
  async listChannelConversations(orgId, connectionId) {
65316
65329
  return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
65317
65330
  }
@@ -65850,17 +65863,11 @@ var ParallClient = class _ParallClient {
65850
65863
  async revokeBrowserProfileConsent(orgId, profileId, clipId) {
65851
65864
  await this.request("DELETE", ENDPOINTS.BROWSER_PROFILE_CONSENT(orgId, profileId, clipId));
65852
65865
  }
65853
- async listRegistryClips(q2) {
65854
- const url = ENDPOINTS.CLIP_REGISTRY() + (q2 ? `?q=${encodeURIComponent(q2)}` : "");
65855
- const resp = await this.request("GET", url);
65856
- return resp.data;
65857
- }
65858
65866
  // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
65859
65867
  /**
65860
65868
  * List registry clips visible to the org: its own plus public+approved
65861
65869
  * cross-org entries. This is the surface `installRegistryClip` and clip
65862
- * connections operate on — NOT the Pinix catalog proxy
65863
- * ({@link listRegistryClips}), whose entries carry no `crg_` id.
65870
+ * connections operate on.
65864
65871
  */
65865
65872
  async listOrgRegistryClips(orgId) {
65866
65873
  const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
@@ -65875,11 +65882,58 @@ var ParallClient = class _ParallClient {
65875
65882
  async installRegistryClip(orgId, clipId) {
65876
65883
  return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
65877
65884
  }
65885
+ /**
65886
+ * Remove an org's install of a registry clip. Keyed by the REGISTRY id
65887
+ * (`crg_`), the same id {@link installRegistryClip} takes — the v2
65888
+ * `deleteClip` operates on a different table and cannot reach these.
65889
+ */
65890
+ async uninstallRegistryClip(orgId, clipId) {
65891
+ await this.request("DELETE", ENDPOINTS.ORG_CLIP_UNINSTALL(orgId, clipId));
65892
+ }
65878
65893
  /** List the org's installed registry clips (full entries). */
65879
65894
  async listInstalledRegistryClips(orgId) {
65880
65895
  const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
65881
65896
  return resp ?? [];
65882
65897
  }
65898
+ /**
65899
+ * Fetch one registry clip by its `crg_` id. For a clip OWNED by the calling
65900
+ * org the entry additionally carries `review_status`/`review_note` — the
65901
+ * state of the latest submitted version and the reviewer's rejection
65902
+ * feedback. This is the polling surface for "did platform review approve my
65903
+ * public publish yet"; cross-org callers see only public+approved entries
65904
+ * and never any review-state fields.
65905
+ */
65906
+ async getRegistryClip(orgId, clipId) {
65907
+ return this.request("GET", ENDPOINTS.ORG_CLIP_REGISTRY_CLIP(orgId, clipId));
65908
+ }
65909
+ /**
65910
+ * Publish (create, or author-only update) a clip in the org registry,
65911
+ * replacing its whole file set. See {@link PublishRegistryClipRequest} for
65912
+ * the visibility/review semantics; the returned entry carries
65913
+ * `review_status` when a public publish queued a review submission — poll
65914
+ * {@link getRegistryClip} for the outcome.
65915
+ */
65916
+ async publishRegistryClip(orgId, req) {
65917
+ return this.request("POST", ENDPOINTS.ORG_CLIP_REGISTRY_PUBLISH(orgId), req, void 0, false, {
65918
+ timeoutMs: 6e4
65919
+ });
65920
+ }
65921
+ /**
65922
+ * Read a clip's per-org agent exec access. Org-member readable (agents
65923
+ * included, so a denied agent can learn why exec answered
65924
+ * `CLIP_AGENT_NOT_ALLOWED`).
65925
+ */
65926
+ async getClipAgentExecAccess(orgId, clipId) {
65927
+ return this.request("GET", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId));
65928
+ }
65929
+ /**
65930
+ * Replace a clip's per-org agent exec access. Human-only (agent principals
65931
+ * get 403); under `all_agents` the `agent_ids` must be empty. Every granted
65932
+ * id must be an active agent of this org (else `400 INVALID_INPUT`).
65933
+ */
65934
+ async putClipAgentExecAccess(orgId, clipId, access) {
65935
+ return this.request("PUT", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId), access);
65936
+ }
65883
65937
  // ---- Edge devices ----
65884
65938
  async listEdgeDevices(orgId) {
65885
65939
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -51511,6 +51511,8 @@ var ENDPOINTS = {
51511
51511
  SLACK_HISTORY: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
51512
51512
  SLACK_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
51513
51513
  SLACK_STATUS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
51514
+ // WeChat tier-B read verb (agent-only; internal research preview).
51515
+ WECHAT_CONTACTS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/contacts`,
51514
51516
  // Invitations (org-scoped, admin)
51515
51517
  ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
51516
51518
  ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
@@ -51672,8 +51674,6 @@ var ENDPOINTS = {
51672
51674
  // Live viewer command — on api-server (API_BASE), not clip-service: it rides
51673
51675
  // the machine control-plane request/reply bridge that lives in api-server.
51674
51676
  BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
51675
- // Clip registry (global, served by clip-service → Pinix Hub proxy)
51676
- CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`,
51677
51677
  // Edge device endpoints
51678
51678
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
51679
51679
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
@@ -51690,15 +51690,20 @@ var ENDPOINTS = {
51690
51690
  ORG_EDGE_VIEWER_COMMAND: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
51691
51691
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
51692
51692
  CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
51693
- // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
51694
- // Pinix Hub catalog proxy above is a different, id-less surface)
51693
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries)
51695
51694
  ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
51695
+ ORG_CLIP_REGISTRY_CLIP: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}`,
51696
+ ORG_CLIP_REGISTRY_PUBLISH: (orgId) => `/api/v1/orgs/${orgId}/clip-registry/publish`,
51696
51697
  ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
51697
51698
  ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`,
51699
+ ORG_CLIP_UNINSTALL: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/uninstall`,
51700
+ // Per-org agent exec access for one clip (default all agents; PUT human-only)
51701
+ ORG_CLIP_EXEC_ACCESS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/exec-access`,
51698
51702
  // MCP clip server config (cap:clip-mcp; publisher-org only — cross-org gets
51699
51703
  // 403 MCP_CROSS_ORG_DISABLED on the whole family, reads included)
51700
51704
  ORG_CLIP_MCP_CONFIG: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config`,
51701
- ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`
51705
+ ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`,
51706
+ ORG_CLIP_MCP_OAUTH_DISCONNECT: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/oauth/disconnect`
51702
51707
  };
51703
51708
  var WS_EVENTS = {
51704
51709
  // Client -> Server
@@ -53194,6 +53199,14 @@ var ParallClient = class _ParallClient {
53194
53199
  async setSlackStatus(orgId, input) {
53195
53200
  await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
53196
53201
  }
53202
+ /**
53203
+ * WeChat tier-B read verb (agent-only; internal research preview): the
53204
+ * account's address book — friend wxids, saved group room ids, followed
53205
+ * official accounts. Same live gate as the send verb.
53206
+ */
53207
+ async listWechatContacts(orgId) {
53208
+ return this.request("GET", ENDPOINTS.WECHAT_CONTACTS(orgId));
53209
+ }
53197
53210
  async listChannelConversations(orgId, connectionId) {
53198
53211
  return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
53199
53212
  }
@@ -53732,17 +53745,11 @@ var ParallClient = class _ParallClient {
53732
53745
  async revokeBrowserProfileConsent(orgId, profileId, clipId) {
53733
53746
  await this.request("DELETE", ENDPOINTS.BROWSER_PROFILE_CONSENT(orgId, profileId, clipId));
53734
53747
  }
53735
- async listRegistryClips(q) {
53736
- const url = ENDPOINTS.CLIP_REGISTRY() + (q ? `?q=${encodeURIComponent(q)}` : "");
53737
- const resp = await this.request("GET", url);
53738
- return resp.data;
53739
- }
53740
53748
  // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
53741
53749
  /**
53742
53750
  * List registry clips visible to the org: its own plus public+approved
53743
53751
  * cross-org entries. This is the surface `installRegistryClip` and clip
53744
- * connections operate on — NOT the Pinix catalog proxy
53745
- * ({@link listRegistryClips}), whose entries carry no `crg_` id.
53752
+ * connections operate on.
53746
53753
  */
53747
53754
  async listOrgRegistryClips(orgId) {
53748
53755
  const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
@@ -53757,11 +53764,58 @@ var ParallClient = class _ParallClient {
53757
53764
  async installRegistryClip(orgId, clipId) {
53758
53765
  return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
53759
53766
  }
53767
+ /**
53768
+ * Remove an org's install of a registry clip. Keyed by the REGISTRY id
53769
+ * (`crg_`), the same id {@link installRegistryClip} takes — the v2
53770
+ * `deleteClip` operates on a different table and cannot reach these.
53771
+ */
53772
+ async uninstallRegistryClip(orgId, clipId) {
53773
+ await this.request("DELETE", ENDPOINTS.ORG_CLIP_UNINSTALL(orgId, clipId));
53774
+ }
53760
53775
  /** List the org's installed registry clips (full entries). */
53761
53776
  async listInstalledRegistryClips(orgId) {
53762
53777
  const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
53763
53778
  return resp ?? [];
53764
53779
  }
53780
+ /**
53781
+ * Fetch one registry clip by its `crg_` id. For a clip OWNED by the calling
53782
+ * org the entry additionally carries `review_status`/`review_note` — the
53783
+ * state of the latest submitted version and the reviewer's rejection
53784
+ * feedback. This is the polling surface for "did platform review approve my
53785
+ * public publish yet"; cross-org callers see only public+approved entries
53786
+ * and never any review-state fields.
53787
+ */
53788
+ async getRegistryClip(orgId, clipId) {
53789
+ return this.request("GET", ENDPOINTS.ORG_CLIP_REGISTRY_CLIP(orgId, clipId));
53790
+ }
53791
+ /**
53792
+ * Publish (create, or author-only update) a clip in the org registry,
53793
+ * replacing its whole file set. See {@link PublishRegistryClipRequest} for
53794
+ * the visibility/review semantics; the returned entry carries
53795
+ * `review_status` when a public publish queued a review submission — poll
53796
+ * {@link getRegistryClip} for the outcome.
53797
+ */
53798
+ async publishRegistryClip(orgId, req) {
53799
+ return this.request("POST", ENDPOINTS.ORG_CLIP_REGISTRY_PUBLISH(orgId), req, void 0, false, {
53800
+ timeoutMs: 6e4
53801
+ });
53802
+ }
53803
+ /**
53804
+ * Read a clip's per-org agent exec access. Org-member readable (agents
53805
+ * included, so a denied agent can learn why exec answered
53806
+ * `CLIP_AGENT_NOT_ALLOWED`).
53807
+ */
53808
+ async getClipAgentExecAccess(orgId, clipId) {
53809
+ return this.request("GET", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId));
53810
+ }
53811
+ /**
53812
+ * Replace a clip's per-org agent exec access. Human-only (agent principals
53813
+ * get 403); under `all_agents` the `agent_ids` must be empty. Every granted
53814
+ * id must be an active agent of this org (else `400 INVALID_INPUT`).
53815
+ */
53816
+ async putClipAgentExecAccess(orgId, clipId, access) {
53817
+ return this.request("PUT", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId), access);
53818
+ }
53765
53819
  // ---- Edge devices ----
53766
53820
  async listEdgeDevices(orgId) {
53767
53821
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -54511,10 +54565,16 @@ function buildSendMessageHint(event) {
54511
54565
  const replyTo = event.channelExternalMessageId ? ` --reply-to "${event.channelExternalMessageId}"` : "";
54512
54566
  return `
54513
54567
  <system-reminder>To reply, use the platform verb: \`parall slack send${channelArg}${replyTo} --text <your reply>\`. In channels --reply-to is REQUIRED (the reply lands in that message's thread); in DMs it is optional (DMs are linear). \`parall slack send\` is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54568
+ }
54569
+ if (event.channelProvider === "wechat") {
54570
+ const toArg = event.channelExternalConversationId ? ` --to "${event.channelExternalConversationId}"` : " --to <wxid from this event>";
54571
+ const atArg = event.channelConversationType === "group" ? event.senderId && event.senderId !== "external" ? ` --at "${event.senderId}"` : " --at <wxid of the person you are answering>" : "";
54572
+ return `
54573
+ <system-reminder>To reply, use the platform verb: \`parall wechat send${toArg}${atArg} --text <your reply>\`. In group chats, --at @-mentions the person you are answering. \`parall wechat send\` is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54514
54574
  }
54515
54575
  if (!event.channelProvider) {
54516
54576
  return `
54517
- <system-reminder>To reply, use the channel capability granted in your system prompt \u2014 for Feishu conversations that is \`lark-cli im\`, for Slack it is \`parall slack send\` (pass the message id from this event as --reply-to). That capability is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54577
+ <system-reminder>To reply, use the channel capability granted in your system prompt \u2014 for Feishu conversations that is \`lark-cli im\`, for Slack it is \`parall slack send\` (pass the message id from this event as --reply-to), for WeChat it is \`parall wechat send\`. That capability is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54518
54578
  }
54519
54579
  const convRef = event.channelExternalConversationId ? `chat_id "${event.channelExternalConversationId}"` : "the conversation id named in this event";
54520
54580
  const threadAlt = event.channelExternalMessageId ? ` To reply threaded to this specific message, reference message_id "${event.channelExternalMessageId}".` : "";
@@ -54993,13 +55053,33 @@ var LaneLedger = class {
54993
55053
  };
54994
55054
 
54995
55055
  // ts/agent-core/dist/gateway-lane-flow.js
55056
+ function shiftMainBufferGroup(host, groupKey, preserveInjectedFrameBoundary) {
55057
+ const events = [];
55058
+ while (host.dispatchState.mainBuffer[0] && host.dispatchGroupKey(host.dispatchState.mainBuffer[0]) === groupKey && (!preserveInjectedFrameBoundary || events.length === 0)) {
55059
+ events.push(host.dispatchState.mainBuffer.shift());
55060
+ }
55061
+ return events;
55062
+ }
55063
+ function hasUninjectedSameLaneEventAhead(host, event) {
55064
+ const eventIndex = host.dispatchState.mainBuffer.lastIndexOf(event);
55065
+ if (eventIndex <= 0)
55066
+ return false;
55067
+ const groupKey = host.dispatchGroupKey(event);
55068
+ return host.dispatchState.mainBuffer.slice(0, eventIndex).some((buffered) => host.dispatchGroupKey(buffered) === groupKey && !host.injectedMainBufferEvents.has(buffered));
55069
+ }
54996
55070
  async function steerLaneMessage(host, event) {
54997
55071
  const { laneLedger: ledger, opts } = host;
54998
55072
  const adapter = opts.dispatchAdapter;
54999
55073
  if (!ledger || !adapter.enqueueDuringDispatch)
55000
55074
  return;
55075
+ if (hasUninjectedSameLaneEventAhead(host, event))
55076
+ return;
55001
55077
  const folded = await ledger.steerLive(event);
55002
- if (folded && await adapter.enqueueDuringDispatch(opts.runtimeKey, buildEventBody(event), folded.inputLifecycle)) {
55078
+ if (!folded)
55079
+ return;
55080
+ const injected = await adapter.enqueueDuringDispatch(opts.runtimeKey, buildEventBody(event), folded.inputLifecycle);
55081
+ if (injected) {
55082
+ host.injectedMainBufferEvents.add(event);
55003
55083
  opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
55004
55084
  }
55005
55085
  }
@@ -55935,9 +56015,12 @@ import * as path2 from "node:path";
55935
56015
  import { fileURLToPath } from "node:url";
55936
56016
  var CAPABILITY_FEISHU_CLI = "feishu-cli";
55937
56017
  var CAPABILITY_SLACK_SEND = "slack-send";
56018
+ var CAPABILITY_WECHAT_SEND = "wechat-send";
55938
56019
  function channelCapabilityKeyFor(provider) {
55939
56020
  if (provider === "slack")
55940
56021
  return CAPABILITY_SLACK_SEND;
56022
+ if (provider === "wechat")
56023
+ return CAPABILITY_WECHAT_SEND;
55941
56024
  return `${provider}-cli`;
55942
56025
  }
55943
56026
  var CHANNEL_POINTER_MAGIC = "parall channel capability pointer";
@@ -57120,6 +57203,7 @@ var ParallAgentGateway = class {
57120
57203
  // (stable mapping; avoids one connection fetch per inbound message).
57121
57204
  channelConnectionProviders = /* @__PURE__ */ new Map();
57122
57205
  dispatchedMessages = /* @__PURE__ */ new Set();
57206
+ injectedMainBufferEvents = /* @__PURE__ */ new WeakSet();
57123
57207
  // Per-WorkItem failure backoff for typed dispatch consumption — see
57124
57208
  // LaneFlowHost.typedRedriveBackoff in gateway-lane-flow.ts.
57125
57209
  typedRedriveBackoff = /* @__PURE__ */ new Map();
@@ -58332,13 +58416,10 @@ var ParallAgentGateway = class {
58332
58416
  break;
58333
58417
  }
58334
58418
  const groupKey = this.dispatchGroupKey(this.dispatchState.mainBuffer[0]);
58335
- const events = [];
58336
- while (this.dispatchState.mainBuffer[0] && this.dispatchGroupKey(this.dispatchState.mainBuffer[0]) === groupKey) {
58337
- events.push(this.dispatchState.mainBuffer.shift());
58338
- }
58419
+ const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
58420
+ const events = shiftMainBufferGroup(this.laneFlowHost(), groupKey, hasPendingInjections && this.opts.dispatchAdapter.inputLifecycleMode === "explicit");
58339
58421
  const event = events[events.length - 1];
58340
58422
  const earlier = events.slice(0, -1);
58341
- const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
58342
58423
  const pendingFork = hasPendingInjections ? [] : this.dispatchState.pendingForkResults.splice(0);
58343
58424
  const forkPrefix = buildForkResultPrefix(pendingFork);
58344
58425
  this.dispatchState.mainCurrentTargetId = event.targetId;
@@ -59051,7 +59132,7 @@ var ParallAgentGateway = class {
59051
59132
  }
59052
59133
  }
59053
59134
  const keys = this.opts.getCapabilityKeys?.() ?? [];
59054
- const cliCapable = provider ? keys.includes(channelCapabilityKeyFor(provider)) : keys.some((k) => k.endsWith("-cli") || k === CAPABILITY_SLACK_SEND);
59135
+ const cliCapable = provider ? keys.includes(channelCapabilityKeyFor(provider)) : keys.some((k) => k.endsWith("-cli") || k === CAPABILITY_SLACK_SEND || k === CAPABILITY_WECHAT_SEND);
59055
59136
  const event = {
59056
59137
  type: "channel_message",
59057
59138
  targetId: conv.id,
@@ -60377,41 +60458,55 @@ CLI command results are JSON on stdout; mutation commands may emit auxiliary hin
60377
60458
  var PARALL_CLIPS_SKILL = `# Parall Clips
60378
60459
 
60379
60460
  Clips are packaged capabilities that let agents operate external systems \u2014
60380
- APIs and websites \u2014 through named commands installed in the org.
60461
+ APIs, websites, remote tools \u2014 through named commands installed in the org.
60381
60462
 
60382
60463
  ## Discover
60383
60464
 
60384
60465
  \`\`\`bash
60385
- parall clip list # clips installed in this org
60386
- parall clip info <alias> # commands, params, version
60466
+ parall clip list # installed clips + each clip's connections
60467
+ parall clip info <clip> # commands + per-command params (manifest)
60468
+ parall clip connections <clip> # one clip's connections, full rows
60469
+ parall clip tools <clip> # MCP clips only: live tool schemas
60387
60470
  \`\`\`
60388
60471
 
60389
- ## Invoke
60390
-
60391
- \`\`\`bash
60392
- parall clip invoke <alias> <command> [input] [--timeout <ms>] # timeout default 30s
60393
- # input: JSON string or plain text, per the command's params in \`info\`
60394
- parall clip invoke github-tools list-repos '{"org":"acme"}'
60395
- \`\`\`
60472
+ \`clip list\` answers both discovery questions at once: WHICH clip (name,
60473
+ description, version) and WHERE it can run \u2014 every connection with its
60474
+ \`ccn_\u2026\` id, alias, and target kind:
60396
60475
 
60397
- Results are JSON on stdout; failures print an error.
60476
+ - \`cloud\` \u2014 an org-shared cloud profile (a maintainer's signed-in browser)
60477
+ - \`desktop\` \u2014 a member's own device (only its owner can exec through it)
60478
+ - \`mcp\` \u2014 a remote MCP tool server
60479
+ - \`device\` \u2014 a device whose placement could not be resolved just now (the
60480
+ device list was unavailable); don't guess which kind it is \u2014 re-run
60481
+ discovery, and treat a persistent \`device\` like an unverified target
60482
+ - \`orphaned\` \u2014 the target device is gone; the connection is unusable
60398
60483
 
60399
- ## Execute on an Edge device (registry clips)
60484
+ Pick the connection whose alias names the account/device the task needs
60485
+ (e.g. \`ins-nyc\` vs \`ins-boston\`). A row without an alias can only be
60486
+ referenced by \`ccn_\` id \u2014 when aliases are missing and several connections
60487
+ could match, ask a human to name them in the Clip Console rather than
60488
+ guessing which signed-in account you are about to act through.
60400
60489
 
60401
- Registry clips run on an Edge \u2014 a member's desktop, or an org-shared cloud
60402
- profile. **Name the target explicitly.** A cloud profile has NO implicit
60403
- route; omitting the target entirely is a desktop-only legacy form that
60404
- reaches just YOUR OWN online desktop device \u2014 never a shared cloud profile.
60490
+ ## Execute
60405
60491
 
60406
60492
  \`\`\`bash
60407
- parall clip exec <clip> <command> [args] --connection <id|alias> # the normal form
60493
+ parall clip exec <clip> <command> [args] --connection <ccn_|alias> # the normal form
60408
60494
  parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-main
60409
60495
  \`\`\`
60410
60496
 
60497
+ Build \`args\` as JSON per the command's params from \`clip info\` (or the
60498
+ tool's \`inputSchema\` from \`clip tools\` for MCP clips \u2014 tool names are NOT
60499
+ frozen in the manifest, so never guess a tool name or its argument shape).
60500
+ Results are JSON on stdout; failures print a typed error.
60501
+
60502
+ **Name the target explicitly.** A cloud profile has NO implicit route;
60503
+ omitting the target entirely is a desktop-only legacy form that reaches just
60504
+ YOUR OWN online desktop device \u2014 never a shared cloud profile.
60505
+
60411
60506
  - A cloud (hosted) profile is reachable ONLY via \`--connection\` \u2014 the clip
60412
- connection its maintainer bound (\`ccn_\u2026\` id or alias). That binding IS your
60413
- authorization; without one the server answers \`HOSTED_CONNECTION_REQUIRED\`
60414
- and the fix is to ask an owner/admin to bind the clip, never to retry.
60507
+ connection its maintainer bound. That binding IS your authorization;
60508
+ without one the server answers \`HOSTED_CONNECTION_REQUIRED\` and the fix
60509
+ is to ask an owner/admin to bind the clip, never to retry.
60415
60510
  - \`--edge <edgeId>\` targets only a desktop device YOU own.
60416
60511
  - Waiting on a cloud profile is handled by the CLI: \`EDGE_ACTIVATING\` (cold
60417
60512
  start), \`EDGE_BUSY\` (another exec is running) and
@@ -60423,19 +60518,10 @@ parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-
60423
60518
 
60424
60519
  ## MCP clips (remote tool servers)
60425
60520
 
60426
- Some registry clips are backed by a remote MCP server instead of an Edge
60427
- device. The command is an MCP tool name and the args are that tool's JSON
60428
- arguments \u2014 but **MCP tool names are NOT frozen in \`clip info\`, so discover
60429
- them first; never guess a tool name or its argument shape**. Before invoking,
60430
- find the connection AND the tool schemas:
60431
-
60432
- \`\`\`bash
60433
- parall clip connections <alias> # the ccn_ id / alias to pass to --connection
60434
- parall clip tools <alias> # tool names + descriptions + inputSchema (JSON)
60435
- \`\`\`
60436
-
60437
- Read each tool's \`inputSchema\` from \`clip tools\` to build valid args, then
60438
- exec against that explicit target \u2014 same form as an Edge clip:
60521
+ A connection with target \`mcp\` routes to a remote MCP server; the command
60522
+ is an MCP tool name and the args are that tool's JSON arguments. Read each
60523
+ tool's \`inputSchema\` from \`clip tools\` first, then exec against the
60524
+ explicit target \u2014 the same form as any other clip:
60439
60525
 
60440
60526
  \`\`\`bash
60441
60527
  parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
@@ -60453,13 +60539,11 @@ parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
60453
60539
 
60454
60540
  ## Behavior rules
60455
60541
 
60456
- - An authorization error (clip not bound to you) is a fail-fast: ask the
60457
- clip's owner or an admin to bind it \u2014 do not retry or work around it.
60458
- - If the executing runtime is offline or the call times out, report that
60542
+ - An authorization error (\`HOSTED_CONNECTION_REQUIRED\`, \`FORBIDDEN\`) is a
60543
+ fail-fast: ask the clip's maintainer or an org admin to bind the clip or
60544
+ grant the connection \u2014 do not retry or work around it.
60545
+ - If the target device is offline or the call times out, report that
60459
60546
  plainly; do not queue, and never fabricate a result for a run that errored.
60460
- - Hosted browser activation is handled by the CLI: it waits (bounded) while a
60461
- cold hosted browser starts, so if the invoke still fails, report the error \u2014
60462
- do not blind-retry in a loop.
60463
60547
  - **\`OUTCOME_UNKNOWN\` is never retryable.** It means the command was
60464
60548
  dispatched and MAY HAVE EXECUTED even though no result came back. Retrying
60465
60549
  could post, order or delete twice. Verify the effect through the system you
@@ -60475,6 +60559,8 @@ parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
60475
60559
  - A clip may act through a person's real logged-in account \u2014 outward,
60476
60560
  irreversible, or spending actions (post, order, delete, pay) get the same
60477
60561
  caution as any shared-state change: confirm when intent isn't explicit.
60562
+ The connection's alias/target tells you WHICH account you are acting as \u2014
60563
+ if that is ambiguous, resolve it with a human before acting, not after.
60478
60564
  - Reach for \`parall clip list\` whenever a task needs capabilities beyond
60479
60565
  built-in tools.
60480
60566
  `;
@@ -60508,7 +60594,7 @@ var SKILLS = [
60508
60594
  },
60509
60595
  {
60510
60596
  name: "parall-clips",
60511
- description: "Parall clip operations: list installed clips, invoke clip commands, inspect clip details. Use when: the task requires external capabilities (GitHub, web search, etc.), user asks about available tools/clips, or you need to call a clip command.",
60597
+ description: "Parall clip operations: list installed clips and their connections, inspect clip commands/tools, execute clip commands on an explicit connection. Use when: the task requires external capabilities (GitHub, web search, etc.), user asks about available tools/clips, or you need to call a clip command.",
60512
60598
  content: PARALL_CLIPS_SKILL
60513
60599
  }
60514
60600
  ];
@@ -61189,6 +61275,15 @@ var ClaudeInputRegistry = class {
61189
61275
  getByCommand(commandUuid) {
61190
61276
  return this.byCommand.get(commandUuid);
61191
61277
  }
61278
+ findOverlapping(dispatchEventIds) {
61279
+ const ids = new Set(dispatchEventIds);
61280
+ for (const delivery of this.byKey.values()) {
61281
+ if (delivery.lifecycle?.dispatchEventIds.some((id) => ids.has(id))) {
61282
+ return delivery;
61283
+ }
61284
+ }
61285
+ return void 0;
61286
+ }
61192
61287
  values() {
61193
61288
  return this.byKey.values();
61194
61289
  }
@@ -61815,6 +61910,12 @@ var ClaudeCodeAdapter = class {
61815
61910
  return;
61816
61911
  }
61817
61912
  }
61913
+ if (existingState && inputLifecycle) {
61914
+ const overlap = existingState.inputs.findOverlapping(inputLifecycle.dispatchEventIds);
61915
+ if (overlap) {
61916
+ throw new Error(`buffered Claude delivery ${deliveryKey} overlaps active input ${overlap.deliveryKey}; refusing duplicate stdin write`);
61917
+ }
61918
+ }
61818
61919
  let promptBody = bodyForAgent;
61819
61920
  let releasePreparedAttachments = () => {
61820
61921
  };
@@ -51511,6 +51511,8 @@ var ENDPOINTS = {
51511
51511
  SLACK_HISTORY: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
51512
51512
  SLACK_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
51513
51513
  SLACK_STATUS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
51514
+ // WeChat tier-B read verb (agent-only; internal research preview).
51515
+ WECHAT_CONTACTS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/contacts`,
51514
51516
  // Invitations (org-scoped, admin)
51515
51517
  ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
51516
51518
  ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
@@ -51672,8 +51674,6 @@ var ENDPOINTS = {
51672
51674
  // Live viewer command — on api-server (API_BASE), not clip-service: it rides
51673
51675
  // the machine control-plane request/reply bridge that lives in api-server.
51674
51676
  BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
51675
- // Clip registry (global, served by clip-service → Pinix Hub proxy)
51676
- CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`,
51677
51677
  // Edge device endpoints
51678
51678
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
51679
51679
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
@@ -51690,15 +51690,20 @@ var ENDPOINTS = {
51690
51690
  ORG_EDGE_VIEWER_COMMAND: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
51691
51691
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
51692
51692
  CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
51693
- // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
51694
- // Pinix Hub catalog proxy above is a different, id-less surface)
51693
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries)
51695
51694
  ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
51695
+ ORG_CLIP_REGISTRY_CLIP: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}`,
51696
+ ORG_CLIP_REGISTRY_PUBLISH: (orgId) => `/api/v1/orgs/${orgId}/clip-registry/publish`,
51696
51697
  ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
51697
51698
  ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`,
51699
+ ORG_CLIP_UNINSTALL: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/uninstall`,
51700
+ // Per-org agent exec access for one clip (default all agents; PUT human-only)
51701
+ ORG_CLIP_EXEC_ACCESS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/exec-access`,
51698
51702
  // MCP clip server config (cap:clip-mcp; publisher-org only — cross-org gets
51699
51703
  // 403 MCP_CROSS_ORG_DISABLED on the whole family, reads included)
51700
51704
  ORG_CLIP_MCP_CONFIG: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config`,
51701
- ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`
51705
+ ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`,
51706
+ ORG_CLIP_MCP_OAUTH_DISCONNECT: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/oauth/disconnect`
51702
51707
  };
51703
51708
  var WS_EVENTS = {
51704
51709
  // Client -> Server
@@ -53194,6 +53199,14 @@ var ParallClient = class _ParallClient {
53194
53199
  async setSlackStatus(orgId, input) {
53195
53200
  await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
53196
53201
  }
53202
+ /**
53203
+ * WeChat tier-B read verb (agent-only; internal research preview): the
53204
+ * account's address book — friend wxids, saved group room ids, followed
53205
+ * official accounts. Same live gate as the send verb.
53206
+ */
53207
+ async listWechatContacts(orgId) {
53208
+ return this.request("GET", ENDPOINTS.WECHAT_CONTACTS(orgId));
53209
+ }
53197
53210
  async listChannelConversations(orgId, connectionId) {
53198
53211
  return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
53199
53212
  }
@@ -53732,17 +53745,11 @@ var ParallClient = class _ParallClient {
53732
53745
  async revokeBrowserProfileConsent(orgId, profileId, clipId) {
53733
53746
  await this.request("DELETE", ENDPOINTS.BROWSER_PROFILE_CONSENT(orgId, profileId, clipId));
53734
53747
  }
53735
- async listRegistryClips(q) {
53736
- const url = ENDPOINTS.CLIP_REGISTRY() + (q ? `?q=${encodeURIComponent(q)}` : "");
53737
- const resp = await this.request("GET", url);
53738
- return resp.data;
53739
- }
53740
53748
  // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
53741
53749
  /**
53742
53750
  * List registry clips visible to the org: its own plus public+approved
53743
53751
  * cross-org entries. This is the surface `installRegistryClip` and clip
53744
- * connections operate on — NOT the Pinix catalog proxy
53745
- * ({@link listRegistryClips}), whose entries carry no `crg_` id.
53752
+ * connections operate on.
53746
53753
  */
53747
53754
  async listOrgRegistryClips(orgId) {
53748
53755
  const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
@@ -53757,11 +53764,58 @@ var ParallClient = class _ParallClient {
53757
53764
  async installRegistryClip(orgId, clipId) {
53758
53765
  return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
53759
53766
  }
53767
+ /**
53768
+ * Remove an org's install of a registry clip. Keyed by the REGISTRY id
53769
+ * (`crg_`), the same id {@link installRegistryClip} takes — the v2
53770
+ * `deleteClip` operates on a different table and cannot reach these.
53771
+ */
53772
+ async uninstallRegistryClip(orgId, clipId) {
53773
+ await this.request("DELETE", ENDPOINTS.ORG_CLIP_UNINSTALL(orgId, clipId));
53774
+ }
53760
53775
  /** List the org's installed registry clips (full entries). */
53761
53776
  async listInstalledRegistryClips(orgId) {
53762
53777
  const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
53763
53778
  return resp ?? [];
53764
53779
  }
53780
+ /**
53781
+ * Fetch one registry clip by its `crg_` id. For a clip OWNED by the calling
53782
+ * org the entry additionally carries `review_status`/`review_note` — the
53783
+ * state of the latest submitted version and the reviewer's rejection
53784
+ * feedback. This is the polling surface for "did platform review approve my
53785
+ * public publish yet"; cross-org callers see only public+approved entries
53786
+ * and never any review-state fields.
53787
+ */
53788
+ async getRegistryClip(orgId, clipId) {
53789
+ return this.request("GET", ENDPOINTS.ORG_CLIP_REGISTRY_CLIP(orgId, clipId));
53790
+ }
53791
+ /**
53792
+ * Publish (create, or author-only update) a clip in the org registry,
53793
+ * replacing its whole file set. See {@link PublishRegistryClipRequest} for
53794
+ * the visibility/review semantics; the returned entry carries
53795
+ * `review_status` when a public publish queued a review submission — poll
53796
+ * {@link getRegistryClip} for the outcome.
53797
+ */
53798
+ async publishRegistryClip(orgId, req) {
53799
+ return this.request("POST", ENDPOINTS.ORG_CLIP_REGISTRY_PUBLISH(orgId), req, void 0, false, {
53800
+ timeoutMs: 6e4
53801
+ });
53802
+ }
53803
+ /**
53804
+ * Read a clip's per-org agent exec access. Org-member readable (agents
53805
+ * included, so a denied agent can learn why exec answered
53806
+ * `CLIP_AGENT_NOT_ALLOWED`).
53807
+ */
53808
+ async getClipAgentExecAccess(orgId, clipId) {
53809
+ return this.request("GET", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId));
53810
+ }
53811
+ /**
53812
+ * Replace a clip's per-org agent exec access. Human-only (agent principals
53813
+ * get 403); under `all_agents` the `agent_ids` must be empty. Every granted
53814
+ * id must be an active agent of this org (else `400 INVALID_INPUT`).
53815
+ */
53816
+ async putClipAgentExecAccess(orgId, clipId, access) {
53817
+ return this.request("PUT", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId), access);
53818
+ }
53765
53819
  // ---- Edge devices ----
53766
53820
  async listEdgeDevices(orgId) {
53767
53821
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -54511,10 +54565,16 @@ function buildSendMessageHint(event) {
54511
54565
  const replyTo = event.channelExternalMessageId ? ` --reply-to "${event.channelExternalMessageId}"` : "";
54512
54566
  return `
54513
54567
  <system-reminder>To reply, use the platform verb: \`parall slack send${channelArg}${replyTo} --text <your reply>\`. In channels --reply-to is REQUIRED (the reply lands in that message's thread); in DMs it is optional (DMs are linear). \`parall slack send\` is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54568
+ }
54569
+ if (event.channelProvider === "wechat") {
54570
+ const toArg = event.channelExternalConversationId ? ` --to "${event.channelExternalConversationId}"` : " --to <wxid from this event>";
54571
+ const atArg = event.channelConversationType === "group" ? event.senderId && event.senderId !== "external" ? ` --at "${event.senderId}"` : " --at <wxid of the person you are answering>" : "";
54572
+ return `
54573
+ <system-reminder>To reply, use the platform verb: \`parall wechat send${toArg}${atArg} --text <your reply>\`. In group chats, --at @-mentions the person you are answering. \`parall wechat send\` is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54514
54574
  }
54515
54575
  if (!event.channelProvider) {
54516
54576
  return `
54517
- <system-reminder>To reply, use the channel capability granted in your system prompt \u2014 for Feishu conversations that is \`lark-cli im\`, for Slack it is \`parall slack send\` (pass the message id from this event as --reply-to). That capability is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54577
+ <system-reminder>To reply, use the channel capability granted in your system prompt \u2014 for Feishu conversations that is \`lark-cli im\`, for Slack it is \`parall slack send\` (pass the message id from this event as --reply-to), for WeChat it is \`parall wechat send\`. That capability is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
54518
54578
  }
54519
54579
  const convRef = event.channelExternalConversationId ? `chat_id "${event.channelExternalConversationId}"` : "the conversation id named in this event";
54520
54580
  const threadAlt = event.channelExternalMessageId ? ` To reply threaded to this specific message, reference message_id "${event.channelExternalMessageId}".` : "";
@@ -54993,13 +55053,33 @@ var LaneLedger = class {
54993
55053
  };
54994
55054
 
54995
55055
  // ts/agent-core/dist/gateway-lane-flow.js
55056
+ function shiftMainBufferGroup(host, groupKey, preserveInjectedFrameBoundary) {
55057
+ const events = [];
55058
+ while (host.dispatchState.mainBuffer[0] && host.dispatchGroupKey(host.dispatchState.mainBuffer[0]) === groupKey && (!preserveInjectedFrameBoundary || events.length === 0)) {
55059
+ events.push(host.dispatchState.mainBuffer.shift());
55060
+ }
55061
+ return events;
55062
+ }
55063
+ function hasUninjectedSameLaneEventAhead(host, event) {
55064
+ const eventIndex = host.dispatchState.mainBuffer.lastIndexOf(event);
55065
+ if (eventIndex <= 0)
55066
+ return false;
55067
+ const groupKey = host.dispatchGroupKey(event);
55068
+ return host.dispatchState.mainBuffer.slice(0, eventIndex).some((buffered) => host.dispatchGroupKey(buffered) === groupKey && !host.injectedMainBufferEvents.has(buffered));
55069
+ }
54996
55070
  async function steerLaneMessage(host, event) {
54997
55071
  const { laneLedger: ledger, opts } = host;
54998
55072
  const adapter = opts.dispatchAdapter;
54999
55073
  if (!ledger || !adapter.enqueueDuringDispatch)
55000
55074
  return;
55075
+ if (hasUninjectedSameLaneEventAhead(host, event))
55076
+ return;
55001
55077
  const folded = await ledger.steerLive(event);
55002
- if (folded && await adapter.enqueueDuringDispatch(opts.runtimeKey, buildEventBody(event), folded.inputLifecycle)) {
55078
+ if (!folded)
55079
+ return;
55080
+ const injected = await adapter.enqueueDuringDispatch(opts.runtimeKey, buildEventBody(event), folded.inputLifecycle);
55081
+ if (injected) {
55082
+ host.injectedMainBufferEvents.add(event);
55003
55083
  opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
55004
55084
  }
55005
55085
  }
@@ -55935,9 +56015,12 @@ import * as path2 from "node:path";
55935
56015
  import { fileURLToPath } from "node:url";
55936
56016
  var CAPABILITY_FEISHU_CLI = "feishu-cli";
55937
56017
  var CAPABILITY_SLACK_SEND = "slack-send";
56018
+ var CAPABILITY_WECHAT_SEND = "wechat-send";
55938
56019
  function channelCapabilityKeyFor(provider) {
55939
56020
  if (provider === "slack")
55940
56021
  return CAPABILITY_SLACK_SEND;
56022
+ if (provider === "wechat")
56023
+ return CAPABILITY_WECHAT_SEND;
55941
56024
  return `${provider}-cli`;
55942
56025
  }
55943
56026
  var CHANNEL_POINTER_MAGIC = "parall channel capability pointer";
@@ -57120,6 +57203,7 @@ var ParallAgentGateway = class {
57120
57203
  // (stable mapping; avoids one connection fetch per inbound message).
57121
57204
  channelConnectionProviders = /* @__PURE__ */ new Map();
57122
57205
  dispatchedMessages = /* @__PURE__ */ new Set();
57206
+ injectedMainBufferEvents = /* @__PURE__ */ new WeakSet();
57123
57207
  // Per-WorkItem failure backoff for typed dispatch consumption — see
57124
57208
  // LaneFlowHost.typedRedriveBackoff in gateway-lane-flow.ts.
57125
57209
  typedRedriveBackoff = /* @__PURE__ */ new Map();
@@ -58332,13 +58416,10 @@ var ParallAgentGateway = class {
58332
58416
  break;
58333
58417
  }
58334
58418
  const groupKey = this.dispatchGroupKey(this.dispatchState.mainBuffer[0]);
58335
- const events = [];
58336
- while (this.dispatchState.mainBuffer[0] && this.dispatchGroupKey(this.dispatchState.mainBuffer[0]) === groupKey) {
58337
- events.push(this.dispatchState.mainBuffer.shift());
58338
- }
58419
+ const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
58420
+ const events = shiftMainBufferGroup(this.laneFlowHost(), groupKey, hasPendingInjections && this.opts.dispatchAdapter.inputLifecycleMode === "explicit");
58339
58421
  const event = events[events.length - 1];
58340
58422
  const earlier = events.slice(0, -1);
58341
- const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
58342
58423
  const pendingFork = hasPendingInjections ? [] : this.dispatchState.pendingForkResults.splice(0);
58343
58424
  const forkPrefix = buildForkResultPrefix(pendingFork);
58344
58425
  this.dispatchState.mainCurrentTargetId = event.targetId;
@@ -59051,7 +59132,7 @@ var ParallAgentGateway = class {
59051
59132
  }
59052
59133
  }
59053
59134
  const keys = this.opts.getCapabilityKeys?.() ?? [];
59054
- const cliCapable = provider ? keys.includes(channelCapabilityKeyFor(provider)) : keys.some((k) => k.endsWith("-cli") || k === CAPABILITY_SLACK_SEND);
59135
+ const cliCapable = provider ? keys.includes(channelCapabilityKeyFor(provider)) : keys.some((k) => k.endsWith("-cli") || k === CAPABILITY_SLACK_SEND || k === CAPABILITY_WECHAT_SEND);
59055
59136
  const event = {
59056
59137
  type: "channel_message",
59057
59138
  targetId: conv.id,
@@ -60377,41 +60458,55 @@ CLI command results are JSON on stdout; mutation commands may emit auxiliary hin
60377
60458
  var PARALL_CLIPS_SKILL = `# Parall Clips
60378
60459
 
60379
60460
  Clips are packaged capabilities that let agents operate external systems \u2014
60380
- APIs and websites \u2014 through named commands installed in the org.
60461
+ APIs, websites, remote tools \u2014 through named commands installed in the org.
60381
60462
 
60382
60463
  ## Discover
60383
60464
 
60384
60465
  \`\`\`bash
60385
- parall clip list # clips installed in this org
60386
- parall clip info <alias> # commands, params, version
60466
+ parall clip list # installed clips + each clip's connections
60467
+ parall clip info <clip> # commands + per-command params (manifest)
60468
+ parall clip connections <clip> # one clip's connections, full rows
60469
+ parall clip tools <clip> # MCP clips only: live tool schemas
60387
60470
  \`\`\`
60388
60471
 
60389
- ## Invoke
60472
+ \`clip list\` answers both discovery questions at once: WHICH clip (name,
60473
+ description, version) and WHERE it can run \u2014 every connection with its
60474
+ \`ccn_\u2026\` id, alias, and target kind:
60390
60475
 
60391
- \`\`\`bash
60392
- parall clip invoke <alias> <command> [input] [--timeout <ms>] # timeout default 30s
60393
- # input: JSON string or plain text, per the command's params in \`info\`
60394
- parall clip invoke github-tools list-repos '{"org":"acme"}'
60395
- \`\`\`
60396
-
60397
- Results are JSON on stdout; failures print an error.
60476
+ - \`cloud\` \u2014 an org-shared cloud profile (a maintainer's signed-in browser)
60477
+ - \`desktop\` \u2014 a member's own device (only its owner can exec through it)
60478
+ - \`mcp\` \u2014 a remote MCP tool server
60479
+ - \`device\` \u2014 a device whose placement could not be resolved just now (the
60480
+ device list was unavailable); don't guess which kind it is \u2014 re-run
60481
+ discovery, and treat a persistent \`device\` like an unverified target
60482
+ - \`orphaned\` \u2014 the target device is gone; the connection is unusable
60398
60483
 
60399
- ## Execute on an Edge device (registry clips)
60484
+ Pick the connection whose alias names the account/device the task needs
60485
+ (e.g. \`ins-nyc\` vs \`ins-boston\`). A row without an alias can only be
60486
+ referenced by \`ccn_\` id \u2014 when aliases are missing and several connections
60487
+ could match, ask a human to name them in the Clip Console rather than
60488
+ guessing which signed-in account you are about to act through.
60400
60489
 
60401
- Registry clips run on an Edge \u2014 a member's desktop, or an org-shared cloud
60402
- profile. **Name the target explicitly.** A cloud profile has NO implicit
60403
- route; omitting the target entirely is a desktop-only legacy form that
60404
- reaches just YOUR OWN online desktop device \u2014 never a shared cloud profile.
60490
+ ## Execute
60405
60491
 
60406
60492
  \`\`\`bash
60407
- parall clip exec <clip> <command> [args] --connection <id|alias> # the normal form
60493
+ parall clip exec <clip> <command> [args] --connection <ccn_|alias> # the normal form
60408
60494
  parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-main
60409
60495
  \`\`\`
60410
60496
 
60497
+ Build \`args\` as JSON per the command's params from \`clip info\` (or the
60498
+ tool's \`inputSchema\` from \`clip tools\` for MCP clips \u2014 tool names are NOT
60499
+ frozen in the manifest, so never guess a tool name or its argument shape).
60500
+ Results are JSON on stdout; failures print a typed error.
60501
+
60502
+ **Name the target explicitly.** A cloud profile has NO implicit route;
60503
+ omitting the target entirely is a desktop-only legacy form that reaches just
60504
+ YOUR OWN online desktop device \u2014 never a shared cloud profile.
60505
+
60411
60506
  - A cloud (hosted) profile is reachable ONLY via \`--connection\` \u2014 the clip
60412
- connection its maintainer bound (\`ccn_\u2026\` id or alias). That binding IS your
60413
- authorization; without one the server answers \`HOSTED_CONNECTION_REQUIRED\`
60414
- and the fix is to ask an owner/admin to bind the clip, never to retry.
60507
+ connection its maintainer bound. That binding IS your authorization;
60508
+ without one the server answers \`HOSTED_CONNECTION_REQUIRED\` and the fix
60509
+ is to ask an owner/admin to bind the clip, never to retry.
60415
60510
  - \`--edge <edgeId>\` targets only a desktop device YOU own.
60416
60511
  - Waiting on a cloud profile is handled by the CLI: \`EDGE_ACTIVATING\` (cold
60417
60512
  start), \`EDGE_BUSY\` (another exec is running) and
@@ -60423,19 +60518,10 @@ parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-
60423
60518
 
60424
60519
  ## MCP clips (remote tool servers)
60425
60520
 
60426
- Some registry clips are backed by a remote MCP server instead of an Edge
60427
- device. The command is an MCP tool name and the args are that tool's JSON
60428
- arguments \u2014 but **MCP tool names are NOT frozen in \`clip info\`, so discover
60429
- them first; never guess a tool name or its argument shape**. Before invoking,
60430
- find the connection AND the tool schemas:
60431
-
60432
- \`\`\`bash
60433
- parall clip connections <alias> # the ccn_ id / alias to pass to --connection
60434
- parall clip tools <alias> # tool names + descriptions + inputSchema (JSON)
60435
- \`\`\`
60436
-
60437
- Read each tool's \`inputSchema\` from \`clip tools\` to build valid args, then
60438
- exec against that explicit target \u2014 same form as an Edge clip:
60521
+ A connection with target \`mcp\` routes to a remote MCP server; the command
60522
+ is an MCP tool name and the args are that tool's JSON arguments. Read each
60523
+ tool's \`inputSchema\` from \`clip tools\` first, then exec against the
60524
+ explicit target \u2014 the same form as any other clip:
60439
60525
 
60440
60526
  \`\`\`bash
60441
60527
  parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
@@ -60453,13 +60539,11 @@ parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
60453
60539
 
60454
60540
  ## Behavior rules
60455
60541
 
60456
- - An authorization error (clip not bound to you) is a fail-fast: ask the
60457
- clip's owner or an admin to bind it \u2014 do not retry or work around it.
60458
- - If the executing runtime is offline or the call times out, report that
60542
+ - An authorization error (\`HOSTED_CONNECTION_REQUIRED\`, \`FORBIDDEN\`) is a
60543
+ fail-fast: ask the clip's maintainer or an org admin to bind the clip or
60544
+ grant the connection \u2014 do not retry or work around it.
60545
+ - If the target device is offline or the call times out, report that
60459
60546
  plainly; do not queue, and never fabricate a result for a run that errored.
60460
- - Hosted browser activation is handled by the CLI: it waits (bounded) while a
60461
- cold hosted browser starts, so if the invoke still fails, report the error \u2014
60462
- do not blind-retry in a loop.
60463
60547
  - **\`OUTCOME_UNKNOWN\` is never retryable.** It means the command was
60464
60548
  dispatched and MAY HAVE EXECUTED even though no result came back. Retrying
60465
60549
  could post, order or delete twice. Verify the effect through the system you
@@ -60475,6 +60559,8 @@ parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
60475
60559
  - A clip may act through a person's real logged-in account \u2014 outward,
60476
60560
  irreversible, or spending actions (post, order, delete, pay) get the same
60477
60561
  caution as any shared-state change: confirm when intent isn't explicit.
60562
+ The connection's alias/target tells you WHICH account you are acting as \u2014
60563
+ if that is ambiguous, resolve it with a human before acting, not after.
60478
60564
  - Reach for \`parall clip list\` whenever a task needs capabilities beyond
60479
60565
  built-in tools.
60480
60566
  `;
@@ -60508,7 +60594,7 @@ var SKILLS = [
60508
60594
  },
60509
60595
  {
60510
60596
  name: "parall-clips",
60511
- description: "Parall clip operations: list installed clips, invoke clip commands, inspect clip details. Use when: the task requires external capabilities (GitHub, web search, etc.), user asks about available tools/clips, or you need to call a clip command.",
60597
+ description: "Parall clip operations: list installed clips and their connections, inspect clip commands/tools, execute clip commands on an explicit connection. Use when: the task requires external capabilities (GitHub, web search, etc.), user asks about available tools/clips, or you need to call a clip command.",
60512
60598
  content: PARALL_CLIPS_SKILL
60513
60599
  }
60514
60600
  ];
@@ -62483,6 +62569,10 @@ var CodexAppServerAdapter = class {
62483
62569
  const forkParams = {
62484
62570
  threadId: parentThreadId,
62485
62571
  ephemeral: true,
62572
+ // The fork still inherits the parent's model-visible history; omit
62573
+ // only the display turns from the JSON-RPC response. Large persisted
62574
+ // threads can otherwise produce an equally large one-line payload.
62575
+ excludeTurns: true,
62486
62576
  // Forks must inherit the bridge's approval/sandbox contract. thread/fork
62487
62577
  // does NOT inherit them from the parent thread — omitting them falls
62488
62578
  // back to codex defaults (on-request approval, workspace-write), and an
@@ -62653,7 +62743,13 @@ var CodexAppServerAdapter = class {
62653
62743
  try {
62654
62744
  await client.sendRequest("initialize", {
62655
62745
  clientInfo: { name: "parall-codex-agent", version: "1" },
62656
- capabilities: { experimentalApi: false }
62746
+ // Required by thread/resume.excludeTurns and thread/fork.excludeTurns.
62747
+ // The bridge sends no other experimental request fields. It does not
62748
+ // consume thread status snapshots, so suppress them at the source.
62749
+ capabilities: {
62750
+ experimentalApi: true,
62751
+ optOutNotificationMethods: ["thread/status/changed"]
62752
+ }
62657
62753
  });
62658
62754
  client.sendNotification("initialized", {});
62659
62755
  } catch (err) {
@@ -62712,7 +62808,13 @@ var CodexAppServerAdapter = class {
62712
62808
  commonParams.config = { model_reasoning_effort: this.opts.reasoningEffort };
62713
62809
  }
62714
62810
  const method = opts.resumeId ? "thread/resume" : "thread/start";
62715
- const params = opts.resumeId ? { threadId: opts.resumeId, ...commonParams } : { cwd: this.opts.workspaceDir, ...commonParams };
62811
+ const params = opts.resumeId ? {
62812
+ threadId: opts.resumeId,
62813
+ // Resume the complete model-visible history, but do not serialize
62814
+ // its display turns back across stdio. The bridge only needs the id.
62815
+ excludeTurns: true,
62816
+ ...commonParams
62817
+ } : { cwd: this.opts.workspaceDir, ...commonParams };
62716
62818
  const result = await client.sendRequest(method, params);
62717
62819
  const threadId = extractThreadId(result) ?? opts.resumeId;
62718
62820
  if (!threadId) {
@@ -353,6 +353,8 @@ var init_constants = __esm({
353
353
  SLACK_HISTORY: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
354
354
  SLACK_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
355
355
  SLACK_STATUS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
356
+ // WeChat tier-B read verb (agent-only; internal research preview).
357
+ WECHAT_CONTACTS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/contacts`,
356
358
  // Invitations (org-scoped, admin)
357
359
  ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
358
360
  ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
@@ -514,8 +516,6 @@ var init_constants = __esm({
514
516
  // Live viewer command — on api-server (API_BASE), not clip-service: it rides
515
517
  // the machine control-plane request/reply bridge that lives in api-server.
516
518
  BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
517
- // Clip registry (global, served by clip-service → Pinix Hub proxy)
518
- CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`,
519
519
  // Edge device endpoints
520
520
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
521
521
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
@@ -532,15 +532,20 @@ var init_constants = __esm({
532
532
  ORG_EDGE_VIEWER_COMMAND: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
533
533
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
534
534
  CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
535
- // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
536
- // Pinix Hub catalog proxy above is a different, id-less surface)
535
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries)
537
536
  ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
537
+ ORG_CLIP_REGISTRY_CLIP: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}`,
538
+ ORG_CLIP_REGISTRY_PUBLISH: (orgId) => `/api/v1/orgs/${orgId}/clip-registry/publish`,
538
539
  ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
539
540
  ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`,
541
+ ORG_CLIP_UNINSTALL: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/uninstall`,
542
+ // Per-org agent exec access for one clip (default all agents; PUT human-only)
543
+ ORG_CLIP_EXEC_ACCESS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/exec-access`,
540
544
  // MCP clip server config (cap:clip-mcp; publisher-org only — cross-org gets
541
545
  // 403 MCP_CROSS_ORG_DISABLED on the whole family, reads included)
542
546
  ORG_CLIP_MCP_CONFIG: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config`,
543
- ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`
547
+ ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`,
548
+ ORG_CLIP_MCP_OAUTH_DISCONNECT: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/oauth/disconnect`
544
549
  };
545
550
  WS_EVENTS = {
546
551
  // Client -> Server
@@ -2081,6 +2086,14 @@ var init_client = __esm({
2081
2086
  async setSlackStatus(orgId, input) {
2082
2087
  await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
2083
2088
  }
2089
+ /**
2090
+ * WeChat tier-B read verb (agent-only; internal research preview): the
2091
+ * account's address book — friend wxids, saved group room ids, followed
2092
+ * official accounts. Same live gate as the send verb.
2093
+ */
2094
+ async listWechatContacts(orgId) {
2095
+ return this.request("GET", ENDPOINTS.WECHAT_CONTACTS(orgId));
2096
+ }
2084
2097
  async listChannelConversations(orgId, connectionId) {
2085
2098
  return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
2086
2099
  }
@@ -2619,17 +2632,11 @@ var init_client = __esm({
2619
2632
  async revokeBrowserProfileConsent(orgId, profileId, clipId) {
2620
2633
  await this.request("DELETE", ENDPOINTS.BROWSER_PROFILE_CONSENT(orgId, profileId, clipId));
2621
2634
  }
2622
- async listRegistryClips(q) {
2623
- const url = ENDPOINTS.CLIP_REGISTRY() + (q ? `?q=${encodeURIComponent(q)}` : "");
2624
- const resp = await this.request("GET", url);
2625
- return resp.data;
2626
- }
2627
2635
  // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
2628
2636
  /**
2629
2637
  * List registry clips visible to the org: its own plus public+approved
2630
2638
  * cross-org entries. This is the surface `installRegistryClip` and clip
2631
- * connections operate on — NOT the Pinix catalog proxy
2632
- * ({@link listRegistryClips}), whose entries carry no `crg_` id.
2639
+ * connections operate on.
2633
2640
  */
2634
2641
  async listOrgRegistryClips(orgId) {
2635
2642
  const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
@@ -2644,11 +2651,58 @@ var init_client = __esm({
2644
2651
  async installRegistryClip(orgId, clipId) {
2645
2652
  return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
2646
2653
  }
2654
+ /**
2655
+ * Remove an org's install of a registry clip. Keyed by the REGISTRY id
2656
+ * (`crg_`), the same id {@link installRegistryClip} takes — the v2
2657
+ * `deleteClip` operates on a different table and cannot reach these.
2658
+ */
2659
+ async uninstallRegistryClip(orgId, clipId) {
2660
+ await this.request("DELETE", ENDPOINTS.ORG_CLIP_UNINSTALL(orgId, clipId));
2661
+ }
2647
2662
  /** List the org's installed registry clips (full entries). */
2648
2663
  async listInstalledRegistryClips(orgId) {
2649
2664
  const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
2650
2665
  return resp ?? [];
2651
2666
  }
2667
+ /**
2668
+ * Fetch one registry clip by its `crg_` id. For a clip OWNED by the calling
2669
+ * org the entry additionally carries `review_status`/`review_note` — the
2670
+ * state of the latest submitted version and the reviewer's rejection
2671
+ * feedback. This is the polling surface for "did platform review approve my
2672
+ * public publish yet"; cross-org callers see only public+approved entries
2673
+ * and never any review-state fields.
2674
+ */
2675
+ async getRegistryClip(orgId, clipId) {
2676
+ return this.request("GET", ENDPOINTS.ORG_CLIP_REGISTRY_CLIP(orgId, clipId));
2677
+ }
2678
+ /**
2679
+ * Publish (create, or author-only update) a clip in the org registry,
2680
+ * replacing its whole file set. See {@link PublishRegistryClipRequest} for
2681
+ * the visibility/review semantics; the returned entry carries
2682
+ * `review_status` when a public publish queued a review submission — poll
2683
+ * {@link getRegistryClip} for the outcome.
2684
+ */
2685
+ async publishRegistryClip(orgId, req) {
2686
+ return this.request("POST", ENDPOINTS.ORG_CLIP_REGISTRY_PUBLISH(orgId), req, void 0, false, {
2687
+ timeoutMs: 6e4
2688
+ });
2689
+ }
2690
+ /**
2691
+ * Read a clip's per-org agent exec access. Org-member readable (agents
2692
+ * included, so a denied agent can learn why exec answered
2693
+ * `CLIP_AGENT_NOT_ALLOWED`).
2694
+ */
2695
+ async getClipAgentExecAccess(orgId, clipId) {
2696
+ return this.request("GET", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId));
2697
+ }
2698
+ /**
2699
+ * Replace a clip's per-org agent exec access. Human-only (agent principals
2700
+ * get 403); under `all_agents` the `agent_ids` must be empty. Every granted
2701
+ * id must be an active agent of this org (else `400 INVALID_INPUT`).
2702
+ */
2703
+ async putClipAgentExecAccess(orgId, clipId, access) {
2704
+ return this.request("PUT", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId), access);
2705
+ }
2652
2706
  // ---- Edge devices ----
2653
2707
  async listEdgeDevices(orgId) {
2654
2708
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/daemon",
3
- "version": "1.52.1",
3
+ "version": "1.52.2",
4
4
  "description": "Parall local agent runtime — daemon supervisor + bridge runtimes, bundled as standalone JS files",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,11 +33,11 @@
33
33
  "@aws-sdk/client-s3": "3.984.0",
34
34
  "@pinixai/bb-browser-pro": "0.15.0",
35
35
  "ws": "^8.18.0",
36
- "@parall/sdk": "1.52.1",
37
- "@parall/agent-core": "1.52.1",
38
- "@parall/claude-agent": "1.52.1",
39
- "@parall/codex-agent": "1.52.1",
40
- "@parall/openclaw-agent": "1.52.1"
36
+ "@parall/agent-core": "1.52.2",
37
+ "@parall/sdk": "1.52.2",
38
+ "@parall/claude-agent": "1.52.2",
39
+ "@parall/codex-agent": "1.52.2",
40
+ "@parall/openclaw-agent": "1.52.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^22.0.0",