@parall/parall 1.36.0 → 1.37.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.
- package/dist/hooks.js +2 -2
- package/dist/index.bundle.mjs +289 -9
- package/package.json +3 -3
- package/skills/parall-platform/SKILL.md +77 -6
- package/src/hooks.ts +2 -2
package/dist/hooks.js
CHANGED
|
@@ -72,9 +72,9 @@ To respond, you **must** use the Parall CLI via the exec (Bash) tool. If \`paral
|
|
|
72
72
|
|
|
73
73
|
### Event types and where to reply
|
|
74
74
|
|
|
75
|
-
- **\`[Event: message.new]\`** — includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat:
|
|
75
|
+
- **\`[Event: message.new]\`** — includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat. Write the reply with your file tool first (shell-safe), then send it — don't wrap message content in \`--text "..."\`, since the shell expands \`$1,000\`→\`,000\` and runs \`$(...)\` inside double quotes:
|
|
76
76
|
|
|
77
|
-
parall messages send prll://cht_xxx --text
|
|
77
|
+
parall messages send prll://cht_xxx --text-file /tmp/reply.md
|
|
78
78
|
|
|
79
79
|
- **\`[Event: task.assigned]\` / \`[Event: task.comment.created]\`** — includes \`[Task: ... (prll://tsk_xxx)]\`. Act on the task; use task CLI subcommands (\`tasks update\`, \`tasks comment\`). See the \`parall-tasks\` skill.
|
|
80
80
|
|
package/dist/index.bundle.mjs
CHANGED
|
@@ -26662,6 +26662,17 @@ function buildEventBody(event) {
|
|
|
26662
26662
|
if (event.attachedUri)
|
|
26663
26663
|
lines.push(`[Attached: ${sanitizeMeta(event.attachedUri)}]`);
|
|
26664
26664
|
lines.push("", event.body);
|
|
26665
|
+
} else if (event.type === "channel_message") {
|
|
26666
|
+
lines.push(`[Event: channel.message]`);
|
|
26667
|
+
const providerLabel = sanitizeMeta(event.channelProvider ?? "external IM");
|
|
26668
|
+
const convLabel = event.channelExternalConversationId ? `${sanitizeMeta(event.channelExternalConversationId)} (${sanitizeMeta(event.channelConversationType ?? "conversation")})` : sanitizeMeta(event.channelConversationType ?? "conversation");
|
|
26669
|
+
lines.push(`[Channel: ${providerLabel} | conversation: ${convLabel}]`);
|
|
26670
|
+
lines.push(`[From: ${sanitizeMeta(event.senderName)} (external user, not a Parall member)]`);
|
|
26671
|
+
if (event.channelExternalMessageId) {
|
|
26672
|
+
lines.push(`[External message ID: ${sanitizeMeta(event.channelExternalMessageId)}]`);
|
|
26673
|
+
}
|
|
26674
|
+
lines.push(`[Audience: this conversation lives on ${providerLabel}, OUTSIDE Parall. Readers cannot open prll:// links, Parall cards, or internal attachments \u2014 never include them in replies. Write plain conversational text.]`);
|
|
26675
|
+
lines.push("", event.body);
|
|
26665
26676
|
} else if (event.type === "external_trigger") {
|
|
26666
26677
|
lines.push(`[Event: external.trigger]`);
|
|
26667
26678
|
lines.push(`[Trigger: prll://${event.targetId}]`);
|
|
@@ -26703,7 +26714,7 @@ function buildSendMessageHint(event) {
|
|
|
26703
26714
|
}
|
|
26704
26715
|
if (event.targetId.startsWith("cht_")) {
|
|
26705
26716
|
return `
|
|
26706
|
-
<system-reminder>To reply, run
|
|
26717
|
+
<system-reminder>To reply, run \`parall messages send prll://${event.targetId} --text-file - <<'EOF'\` \u2026 \`EOF\` \u2014 the quoted heredoc keeps \`$\`, backticks and apostrophes literal (plain \`--text "$1,000"\` sends \`,000\`). Your plain text output is not delivered to the chat.</system-reminder>`;
|
|
26707
26718
|
}
|
|
26708
26719
|
if (event.targetId.startsWith("tsk_")) {
|
|
26709
26720
|
return `
|
|
@@ -26712,6 +26723,14 @@ function buildSendMessageHint(event) {
|
|
|
26712
26723
|
if (event.targetId.startsWith("sch_")) {
|
|
26713
26724
|
return `
|
|
26714
26725
|
<system-reminder>To communicate, use the CLI: \`parall messages send\` / \`parall dm\`. Your plain text output is not delivered.</system-reminder>`;
|
|
26726
|
+
}
|
|
26727
|
+
if (event.type === "channel_message") {
|
|
26728
|
+
const clipLabel = event.channelProvider ? `the \`${event.channelProvider}\` clip's` : "your channel provider clip's";
|
|
26729
|
+
const apiLabel = event.channelProvider ?? "external platform";
|
|
26730
|
+
const target = event.channelExternalConversationId ? `{"chat_id": "${event.channelExternalConversationId}", "text": "..."}` : `{"chat_id": "<conversation id>", "text": "..."}`;
|
|
26731
|
+
const threadAlt = event.channelExternalMessageId ? ` To reply in-thread to this specific message, use {"message_id": "${event.channelExternalMessageId}", "text": "..."} instead.` : "";
|
|
26732
|
+
return `
|
|
26733
|
+
<system-reminder>To reply, invoke ${clipLabel} \`send_message\` command with ${target} \u2014 your plain text output is NOT delivered to the external conversation.${threadAlt} The same clip's \`call\` command reaches the wider ${apiLabel} API when needed.</system-reminder>`;
|
|
26715
26734
|
}
|
|
26716
26735
|
if (event.type === "external_trigger" || event.targetId.startsWith("xtr_")) {
|
|
26717
26736
|
return `
|
|
@@ -26919,6 +26938,8 @@ Or upload first and reuse across chats:
|
|
|
26919
26938
|
parall messages send prll://cht_aaa --attachment att_yyy --text "Report"
|
|
26920
26939
|
parall messages send prll://cht_bbb --attachment att_yyy --text "FYI"
|
|
26921
26940
|
|
|
26941
|
+
The \`--text\` captions above are safe short literals. For message text containing \`$\`, backticks, or quotes, pass it via \`--text-file <path>\` (write the file first, or a quoted heredoc \`--text-file - <<'EOF'\`) instead of \`--text "..."\` \u2014 inside double quotes the shell turns \`$1,000\` into \`,000\` and executes \`$(...)\`.
|
|
26942
|
+
|
|
26922
26943
|
### When to reference
|
|
26923
26944
|
|
|
26924
26945
|
- **Origin** \u2014 always link the message or task that triggered your work
|
|
@@ -27033,6 +27054,7 @@ var ENDPOINTS = {
|
|
|
27033
27054
|
// Org-scoped
|
|
27034
27055
|
ORG: (orgId) => `${API_BASE}/orgs/${orgId}`,
|
|
27035
27056
|
ORG_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/members`,
|
|
27057
|
+
ORG_MEMBERS_FORMER: (orgId) => `${API_BASE}/orgs/${orgId}/members/former`,
|
|
27036
27058
|
TEAMS: (orgId) => `${API_BASE}/orgs/${orgId}/teams`,
|
|
27037
27059
|
ORG_MEMBERS_ONLINE: (orgId) => `${API_BASE}/orgs/${orgId}/members/online`,
|
|
27038
27060
|
ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
|
|
@@ -27182,6 +27204,19 @@ var ENDPOINTS = {
|
|
|
27182
27204
|
EXTERNAL_TRIGGER: (orgId, triggerId) => `${API_BASE}/orgs/${orgId}/external-triggers/${triggerId}`,
|
|
27183
27205
|
EXTERNAL_TRIGGER_RUNS: (orgId) => `${API_BASE}/orgs/${orgId}/external-trigger-runs`,
|
|
27184
27206
|
EXTERNAL_TRIGGER_RUN: (orgId, runId) => `${API_BASE}/orgs/${orgId}/external-trigger-runs/${runId}`,
|
|
27207
|
+
// External IM channel (org-scoped, platform-mediated Feishu/Slack)
|
|
27208
|
+
CHANNEL_CONNECTIONS: (orgId) => `${API_BASE}/orgs/${orgId}/channel-connections`,
|
|
27209
|
+
CHANNEL_CONNECTION: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}`,
|
|
27210
|
+
CHANNEL_CONNECTION_CREDENTIALS: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/credentials`,
|
|
27211
|
+
CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/ingress-token/regenerate`,
|
|
27212
|
+
CHANNEL_CONNECTION_CONVERSATIONS: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/conversations`,
|
|
27213
|
+
CHANNEL_CONVERSATION: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}`,
|
|
27214
|
+
CHANNEL_CONVERSATION_MESSAGES: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/messages`,
|
|
27215
|
+
CHANNEL_CONVERSATION_SESSION: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/session`,
|
|
27216
|
+
CHANNEL_MESSAGE: (orgId, messageId) => `${API_BASE}/orgs/${orgId}/channel-messages/${messageId}`,
|
|
27217
|
+
CHANNEL_PROVISIONING: (orgId) => `${API_BASE}/orgs/${orgId}/channel-provisioning`,
|
|
27218
|
+
CHANNEL_PROVISIONING_SESSION: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}`,
|
|
27219
|
+
CHANNEL_PROVISIONING_CANCEL: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}/cancel`,
|
|
27185
27220
|
// Invitations (org-scoped, admin)
|
|
27186
27221
|
ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
|
|
27187
27222
|
ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
|
|
@@ -27199,9 +27234,16 @@ var ENDPOINTS = {
|
|
|
27199
27234
|
INVITE_LINK_JOIN: `${API_BASE}/invite-link/join`,
|
|
27200
27235
|
// Wikis (org-scoped, served by wiki-service)
|
|
27201
27236
|
WIKIS: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis`,
|
|
27237
|
+
// Recycle bin — soft-deleted wikis (owner only). Must precede WIKI in the
|
|
27238
|
+
// backend router so `deleted` isn't captured as a {wikiId}; the SDK builder
|
|
27239
|
+
// is just a string.
|
|
27240
|
+
WIKIS_DELETED: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis/deleted`,
|
|
27241
|
+
// Detail URL — also reused for DELETE (soft-delete) and `${WIKI}/restore`.
|
|
27202
27242
|
WIKI: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}`,
|
|
27243
|
+
WIKI_RESTORE: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/restore`,
|
|
27203
27244
|
WIKI_TREE: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/tree`,
|
|
27204
27245
|
WIKI_BLOB: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/blob`,
|
|
27246
|
+
WIKI_COPY: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/copy`,
|
|
27205
27247
|
WIKI_NODE_SECTIONS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/node-sections`,
|
|
27206
27248
|
WIKI_SEARCH: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/search`,
|
|
27207
27249
|
WIKI_PAGE_INDEX: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/page-index`,
|
|
@@ -27263,6 +27305,7 @@ var ENDPOINTS = {
|
|
|
27263
27305
|
// References (org-scoped)
|
|
27264
27306
|
REFS_RESOLVE: (orgId) => `${API_BASE}/orgs/${orgId}/refs/resolve`,
|
|
27265
27307
|
REFS_BACKLINKS: (orgId) => `${API_BASE}/orgs/${orgId}/refs/backlinks`,
|
|
27308
|
+
REFS_GRAPH: (orgId) => `${API_BASE}/orgs/${orgId}/refs/graph`,
|
|
27266
27309
|
REFS_CHECK: (orgId) => `${API_BASE}/orgs/${orgId}/refs/check`,
|
|
27267
27310
|
// Platform config (agent-scoped, not org-scoped)
|
|
27268
27311
|
PLATFORM_CONFIG: `${API_BASE}/agents/platform-config`,
|
|
@@ -27283,6 +27326,14 @@ var ENDPOINTS = {
|
|
|
27283
27326
|
BILLING_AUTO_RELOAD: (orgId) => `${API_BASE}/orgs/${orgId}/billing/auto-reload`,
|
|
27284
27327
|
BILLING_SETUP_INTENT: (orgId) => `${API_BASE}/orgs/${orgId}/billing/setup-intent`,
|
|
27285
27328
|
COMPUTE_PRICING: () => `${API_BASE}/billing/compute-pricing`,
|
|
27329
|
+
// Runtime capability table (public, no auth) — SSOT for the create/settings
|
|
27330
|
+
// interlock: per-runtime compute modes, native model family, cross-family
|
|
27331
|
+
// availability, and recommended model.
|
|
27332
|
+
RUNTIMES: () => `${API_BASE}/runtimes`,
|
|
27333
|
+
// Model catalog (public, no auth) — served from the server's DB-backed
|
|
27334
|
+
// snapshot. Optional include_model keeps an agent's pinned hidden legacy
|
|
27335
|
+
// model representable in settings pickers.
|
|
27336
|
+
MODELS: (includeModel) => includeModel ? `${API_BASE}/models?include_model=${encodeURIComponent(includeModel)}` : `${API_BASE}/models`,
|
|
27286
27337
|
// ADMIN_GRANTS intentionally NOT exported here — it sits under the
|
|
27287
27338
|
// unauthenticated `/internal/admin/*` surface and must not bleed into the
|
|
27288
27339
|
// public SDK. Admin dashboard hits the URL directly from its own client.
|
|
@@ -27295,6 +27346,7 @@ var ENDPOINTS = {
|
|
|
27295
27346
|
AGENT_CLIP: (orgId, agentId, clipId) => `${CLIP_BASE}/orgs/${orgId}/agents/${agentId}/clips/${clipId}`,
|
|
27296
27347
|
CLIP_INVOKE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/invoke`,
|
|
27297
27348
|
CLIP_ONLINE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/online`,
|
|
27349
|
+
BROWSER_RUNTIME_STATUS: (orgId) => `${CLIP_BASE}/orgs/${orgId}/browser-runtime/status`,
|
|
27298
27350
|
BROWSER_PROFILES: (orgId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles`,
|
|
27299
27351
|
BROWSER_PROFILE: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}`,
|
|
27300
27352
|
BROWSER_PROFILE_OPEN: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/open`,
|
|
@@ -27348,10 +27400,13 @@ var WS_EVENTS = {
|
|
|
27348
27400
|
INVITATION_REVOKED: "invitation.revoked",
|
|
27349
27401
|
ORG_JOIN_REQUEST_NEW: "org.join_request.new",
|
|
27350
27402
|
ORG_INVITE_LINK_JOINED: "org.invite_link.joined",
|
|
27403
|
+
ORG_MEMBER_REMOVED: "org.member.removed",
|
|
27351
27404
|
AGENT_CONFIG_UPDATE: "agent_config.update",
|
|
27352
27405
|
PRESENCE_UPDATE: "presence.update",
|
|
27353
27406
|
WIKI_CHANGESET_CREATED: "wiki.changeset.created",
|
|
27354
27407
|
WIKI_CHANGESET_UPDATED: "wiki.changeset.updated",
|
|
27408
|
+
WIKI_DELETED: "wiki.deleted",
|
|
27409
|
+
WIKI_RESTORED: "wiki.restored",
|
|
27355
27410
|
COMMENT_CREATED: "comment.created",
|
|
27356
27411
|
COMMENT_UPDATED: "comment.updated",
|
|
27357
27412
|
COMMENT_DELETED: "comment.deleted",
|
|
@@ -27379,6 +27434,7 @@ var WS_EVENTS = {
|
|
|
27379
27434
|
MACHINE_FILESYSTEM_BROWSE: "machine.filesystem.browse",
|
|
27380
27435
|
MACHINE_UPDATE: "machine.update",
|
|
27381
27436
|
MACHINE_CONFIG_UPDATED: "machine.config.updated",
|
|
27437
|
+
MACHINE_AGENT_CONFIG_UPDATED: "machine.agent_config.updated",
|
|
27382
27438
|
MACHINE_CLIP_SYNC: "machine.clip.sync",
|
|
27383
27439
|
MACHINE_BROWSER_PROFILE_LIFECYCLE: "machine.browser_profile.lifecycle",
|
|
27384
27440
|
MACHINE_BROWSER_PROFILE_VIEWER: "machine.browser_profile.viewer",
|
|
@@ -27718,6 +27774,13 @@ var ParallClient = class _ParallClient {
|
|
|
27718
27774
|
const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS(orgId));
|
|
27719
27775
|
return res.data;
|
|
27720
27776
|
}
|
|
27777
|
+
/** User IDs of soft-removed (former) org members — for marking their avatar /
|
|
27778
|
+
* name as "left" where dormant relations still surface them (DMs, task
|
|
27779
|
+
* assignees, message history). */
|
|
27780
|
+
async getFormerMemberIds(orgId) {
|
|
27781
|
+
const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS_FORMER(orgId));
|
|
27782
|
+
return res.user_ids ?? [];
|
|
27783
|
+
}
|
|
27721
27784
|
async getTeams(orgId) {
|
|
27722
27785
|
const res = await this.request("GET", ENDPOINTS.TEAMS(orgId));
|
|
27723
27786
|
return res.data;
|
|
@@ -28199,26 +28262,32 @@ var ParallClient = class _ParallClient {
|
|
|
28199
28262
|
const res = await this.request("GET", ENDPOINTS.MACHINES_ME_CLIPS);
|
|
28200
28263
|
return res.data;
|
|
28201
28264
|
}
|
|
28202
|
-
/** `GET /machines/me/browser-profiles` — browser profiles hosted by this machine.
|
|
28265
|
+
/** `GET /machines/me/browser-profiles` — browser profiles hosted by this machine.
|
|
28266
|
+
* Returns the daemon DTO (carries proxy_password for bb-browser replay). */
|
|
28203
28267
|
async listMachineBrowserProfiles() {
|
|
28204
28268
|
const res = await this.request("GET", ENDPOINTS.MACHINES_ME_BROWSER_PROFILES);
|
|
28205
28269
|
return res.data;
|
|
28206
28270
|
}
|
|
28207
28271
|
/** `POST /machines/me/browser-profiles/{profileId}/status` — report runtime status. */
|
|
28208
|
-
async reportBrowserProfileStatus(profileId, status, errorMsg) {
|
|
28272
|
+
async reportBrowserProfileStatus(profileId, status, errorMsg, generation) {
|
|
28209
28273
|
await this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_STATUS(profileId), {
|
|
28210
28274
|
status,
|
|
28211
|
-
...errorMsg ? { error_msg: errorMsg } : {}
|
|
28275
|
+
...errorMsg ? { error_msg: errorMsg } : {},
|
|
28276
|
+
...generation !== void 0 ? { generation } : {}
|
|
28212
28277
|
});
|
|
28213
28278
|
}
|
|
28214
28279
|
/**
|
|
28215
28280
|
* `POST /machines/me/health` — bump the Machine's `updated_at` to now and
|
|
28216
|
-
* report daemon state. The daemon
|
|
28217
|
-
*
|
|
28281
|
+
* report daemon state. The daemon calls this on startup and again whenever
|
|
28282
|
+
* periodic runtime re-detection produces a CHANGED result (there is no
|
|
28283
|
+
* fixed-cadence keepalive loop today — steady state posts nothing). All
|
|
28218
28284
|
* fields are optional and only persisted when changed:
|
|
28219
28285
|
* - `daemonVersion` — the running bundle/launcher version.
|
|
28220
28286
|
* - `selfUpdateCapable` — whether the daemon can act on a machine.update
|
|
28221
28287
|
* signal (true under a service manager, false for bare `npx` foreground).
|
|
28288
|
+
* - `detectedRuntimes` — runtime CLIs found on the host. Omitted = no
|
|
28289
|
+
* report this beat (server keeps the stored value); [] = detection ran
|
|
28290
|
+
* and found nothing (server clears to empty).
|
|
28222
28291
|
*/
|
|
28223
28292
|
async postMachineHeartbeat(opts) {
|
|
28224
28293
|
const body = {};
|
|
@@ -28226,6 +28295,8 @@ var ParallClient = class _ParallClient {
|
|
|
28226
28295
|
body.daemon_version = opts.daemonVersion;
|
|
28227
28296
|
if (opts?.selfUpdateCapable !== void 0)
|
|
28228
28297
|
body.self_update_capable = opts.selfUpdateCapable;
|
|
28298
|
+
if (opts?.detectedRuntimes !== void 0)
|
|
28299
|
+
body.detected_runtimes = opts.detectedRuntimes;
|
|
28229
28300
|
return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH, Object.keys(body).length > 0 ? body : void 0);
|
|
28230
28301
|
}
|
|
28231
28302
|
async reportAgentWorkspaceState(agentId, state) {
|
|
@@ -28544,6 +28615,59 @@ var ParallClient = class _ParallClient {
|
|
|
28544
28615
|
async deleteExternalConnection(orgId, connectionId) {
|
|
28545
28616
|
return this.request("DELETE", ENDPOINTS.EXTERNAL_CONNECTION(orgId, connectionId));
|
|
28546
28617
|
}
|
|
28618
|
+
// ---- External IM channel (org-scoped) ----
|
|
28619
|
+
async createChannelConnection(orgId, input) {
|
|
28620
|
+
return this.request("POST", ENDPOINTS.CHANNEL_CONNECTIONS(orgId), input);
|
|
28621
|
+
}
|
|
28622
|
+
async listChannelConnections(orgId) {
|
|
28623
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONNECTIONS(orgId));
|
|
28624
|
+
}
|
|
28625
|
+
async getChannelConnection(orgId, connectionId) {
|
|
28626
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
|
|
28627
|
+
}
|
|
28628
|
+
async updateChannelConnection(orgId, connectionId, patch) {
|
|
28629
|
+
return this.request("PATCH", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId), patch);
|
|
28630
|
+
}
|
|
28631
|
+
async archiveChannelConnection(orgId, connectionId) {
|
|
28632
|
+
return this.request("DELETE", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
|
|
28633
|
+
}
|
|
28634
|
+
async deliverChannelCredentials(orgId, connectionId, credentials) {
|
|
28635
|
+
return this.request("POST", ENDPOINTS.CHANNEL_CONNECTION_CREDENTIALS(orgId, connectionId), credentials);
|
|
28636
|
+
}
|
|
28637
|
+
async regenerateChannelIngressToken(orgId, connectionId) {
|
|
28638
|
+
return this.request("POST", ENDPOINTS.CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE(orgId, connectionId));
|
|
28639
|
+
}
|
|
28640
|
+
/** Start a Feishu one-click provisioning session (device flow QR). */
|
|
28641
|
+
async initiateChannelProvisioning(orgId, input) {
|
|
28642
|
+
return this.request("POST", ENDPOINTS.CHANNEL_PROVISIONING(orgId), input);
|
|
28643
|
+
}
|
|
28644
|
+
/**
|
|
28645
|
+
* Lazy status poll — server-side this may forward one provider poll, so
|
|
28646
|
+
* call it at the session's `poll_interval_seconds` cadence, not faster.
|
|
28647
|
+
*/
|
|
28648
|
+
async getChannelProvisioningSession(orgId, sessionId) {
|
|
28649
|
+
return this.request("GET", ENDPOINTS.CHANNEL_PROVISIONING_SESSION(orgId, sessionId));
|
|
28650
|
+
}
|
|
28651
|
+
async cancelChannelProvisioning(orgId, sessionId) {
|
|
28652
|
+
return this.request("POST", ENDPOINTS.CHANNEL_PROVISIONING_CANCEL(orgId, sessionId));
|
|
28653
|
+
}
|
|
28654
|
+
async listChannelConversations(orgId, connectionId) {
|
|
28655
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
|
|
28656
|
+
}
|
|
28657
|
+
async getChannelConversation(orgId, conversationId) {
|
|
28658
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONVERSATION(orgId, conversationId));
|
|
28659
|
+
}
|
|
28660
|
+
async listChannelConversationMessages(orgId, conversationId, limit) {
|
|
28661
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONVERSATION_MESSAGES(orgId, conversationId), void 0, { limit });
|
|
28662
|
+
}
|
|
28663
|
+
async setChannelConversationSession(orgId, conversationId, agentSessionId) {
|
|
28664
|
+
return this.request("PATCH", ENDPOINTS.CHANNEL_CONVERSATION_SESSION(orgId, conversationId), {
|
|
28665
|
+
agent_session_id: agentSessionId
|
|
28666
|
+
});
|
|
28667
|
+
}
|
|
28668
|
+
async getChannelMessage(orgId, messageId) {
|
|
28669
|
+
return this.request("GET", ENDPOINTS.CHANNEL_MESSAGE(orgId, messageId));
|
|
28670
|
+
}
|
|
28547
28671
|
async getExternalTriggerSchema(orgId, connectionId) {
|
|
28548
28672
|
return this.request("GET", ENDPOINTS.EXTERNAL_TRIGGER_SCHEMA(orgId, connectionId));
|
|
28549
28673
|
}
|
|
@@ -28585,6 +28709,23 @@ var ParallClient = class _ParallClient {
|
|
|
28585
28709
|
async getWiki(orgId, wikiId) {
|
|
28586
28710
|
return this.request("GET", ENDPOINTS.WIKI(orgId, wikiId));
|
|
28587
28711
|
}
|
|
28712
|
+
/** Soft-delete an entire wiki (org owner only; the default wiki is
|
|
28713
|
+
* protected → 403 WIKI_PROTECTED). Returns the deleted wiki. Recoverable
|
|
28714
|
+
* via restoreWiki within the retention window. */
|
|
28715
|
+
async deleteWiki(orgId, wikiId) {
|
|
28716
|
+
return this.request("DELETE", ENDPOINTS.WIKI(orgId, wikiId));
|
|
28717
|
+
}
|
|
28718
|
+
/** Restore a soft-deleted wiki (org owner only). 409 WIKI_PURGE_STARTED once
|
|
28719
|
+
* purge has been claimed (no longer restorable), or 404 once the row is gone.
|
|
28720
|
+
* Returns the restored wiki. */
|
|
28721
|
+
async restoreWiki(orgId, wikiId) {
|
|
28722
|
+
return this.request("POST", ENDPOINTS.WIKI_RESTORE(orgId, wikiId));
|
|
28723
|
+
}
|
|
28724
|
+
/** Recycle bin — list soft-deleted wikis for the org (owner only). */
|
|
28725
|
+
async getDeletedWikis(orgId) {
|
|
28726
|
+
const res = await this.request("GET", ENDPOINTS.WIKIS_DELETED(orgId));
|
|
28727
|
+
return res.data;
|
|
28728
|
+
}
|
|
28588
28729
|
async getWikiTree(orgId, wikiId, params) {
|
|
28589
28730
|
return this.request("GET", ENDPOINTS.WIKI_TREE(orgId, wikiId), void 0, params);
|
|
28590
28731
|
}
|
|
@@ -28618,6 +28759,15 @@ var ParallClient = class _ParallClient {
|
|
|
28618
28759
|
blob.signed_url = this.absoluteMediaUrl(blob.signed_url);
|
|
28619
28760
|
return blob;
|
|
28620
28761
|
}
|
|
28762
|
+
/**
|
|
28763
|
+
* Copy one wiki file to another path as an independent snapshot (no sync).
|
|
28764
|
+
* Used to "share a private file out": copy a file from the caller's personal
|
|
28765
|
+
* namespace (`users/{userId}/…`) into the public wiki, leaving the original
|
|
28766
|
+
* untouched. 409 `FILE_EXISTS` if `dest_path` already exists.
|
|
28767
|
+
*/
|
|
28768
|
+
async copyWikiFile(orgId, wikiId, req) {
|
|
28769
|
+
return this.request("POST", ENDPOINTS.WIKI_COPY(orgId, wikiId), req);
|
|
28770
|
+
}
|
|
28621
28771
|
async getWikiNodeSections(orgId, wikiId, params) {
|
|
28622
28772
|
return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
|
|
28623
28773
|
}
|
|
@@ -28786,6 +28936,16 @@ var ParallClient = class _ParallClient {
|
|
|
28786
28936
|
async getBacklinks(orgId, params) {
|
|
28787
28937
|
return this.request("GET", ENDPOINTS.REFS_BACKLINKS(orgId), void 0, params);
|
|
28788
28938
|
}
|
|
28939
|
+
/**
|
|
28940
|
+
* Bounded multi-hop walk of the prll:// reference graph around `uri`. `uri`
|
|
28941
|
+
* must be an entity-level prll:// URI — a refined URI (path/query/fragment) is
|
|
28942
|
+
* rejected with 400 UNSUPPORTED_REFINED_URI. `depth` is clamped server-side to
|
|
28943
|
+
* [1, 4]; breadth (node/edge counts) is capped server-side and surfaced via
|
|
28944
|
+
* `truncated`. ACL is applied per hop (chat membership + wiki path scope).
|
|
28945
|
+
*/
|
|
28946
|
+
async getRefsGraph(orgId, params) {
|
|
28947
|
+
return this.request("GET", ENDPOINTS.REFS_GRAPH(orgId), void 0, params);
|
|
28948
|
+
}
|
|
28789
28949
|
async checkBrokenRefs(orgId) {
|
|
28790
28950
|
return this.request("GET", ENDPOINTS.REFS_CHECK(orgId));
|
|
28791
28951
|
}
|
|
@@ -28881,6 +29041,19 @@ var ParallClient = class _ParallClient {
|
|
|
28881
29041
|
const resp = await this.request("GET", ENDPOINTS.COMPUTE_PRICING());
|
|
28882
29042
|
return resp.data;
|
|
28883
29043
|
}
|
|
29044
|
+
/** Runtime capability table (public) — SSOT for the create/settings interlock. */
|
|
29045
|
+
async getRuntimes() {
|
|
29046
|
+
const resp = await this.request("GET", ENDPOINTS.RUNTIMES());
|
|
29047
|
+
return resp.data;
|
|
29048
|
+
}
|
|
29049
|
+
/**
|
|
29050
|
+
* Platform model catalog (public) — DB-backed, replaces the compile-time
|
|
29051
|
+
* PLATFORM_MODELS constant. Pass includeModel to keep an agent's pinned
|
|
29052
|
+
* hidden legacy model representable (settings picker).
|
|
29053
|
+
*/
|
|
29054
|
+
async getModels(includeModel) {
|
|
29055
|
+
return this.request("GET", ENDPOINTS.MODELS(includeModel));
|
|
29056
|
+
}
|
|
28884
29057
|
// ---- Clips ----
|
|
28885
29058
|
async listClips(orgId) {
|
|
28886
29059
|
const resp = await this.request("GET", ENDPOINTS.CLIPS(orgId));
|
|
@@ -28925,6 +29098,11 @@ var ParallClient = class _ParallClient {
|
|
|
28925
29098
|
const resp = await this.request("GET", ENDPOINTS.CLIP_ONLINE(orgId));
|
|
28926
29099
|
return resp.data;
|
|
28927
29100
|
}
|
|
29101
|
+
/** Deployment-wide hosted browser runtime availability — drives whether the
|
|
29102
|
+
* create UI offers the platform-hosted placement. Fail-closed server-side. */
|
|
29103
|
+
async getBrowserRuntimeStatus(orgId) {
|
|
29104
|
+
return this.request("GET", ENDPOINTS.BROWSER_RUNTIME_STATUS(orgId));
|
|
29105
|
+
}
|
|
28928
29106
|
/** Org-wide browser-profile discovery list. Returns the sanitized
|
|
28929
29107
|
* {@link BrowserProfileListItem} shape (not the full domain model), each row
|
|
28930
29108
|
* carrying a per-viewer `can_open` control hint. */
|
|
@@ -29584,6 +29762,9 @@ function resolveStepTarget(event) {
|
|
|
29584
29762
|
if (event.type === "external_trigger" || event.targetId.startsWith("xtr_")) {
|
|
29585
29763
|
return { target_type: "external_trigger", target_id: event.targetId };
|
|
29586
29764
|
}
|
|
29765
|
+
if (event.type === "channel_message" || event.targetId.startsWith("chv_")) {
|
|
29766
|
+
return { target_type: "channel_conversation", target_id: event.targetId };
|
|
29767
|
+
}
|
|
29587
29768
|
if (event.type === "wiki_comment") {
|
|
29588
29769
|
return { target_type: "wiki", target_id: event.targetId || void 0 };
|
|
29589
29770
|
}
|
|
@@ -29627,6 +29808,9 @@ var ParallAgentGateway = class {
|
|
|
29627
29808
|
opts;
|
|
29628
29809
|
chatInfoMap = /* @__PURE__ */ new Map();
|
|
29629
29810
|
dispatchedTasks = /* @__PURE__ */ new Set();
|
|
29811
|
+
// connection id → provider alias, for channel_message prompt labeling
|
|
29812
|
+
// (stable mapping; avoids one connection fetch per inbound message).
|
|
29813
|
+
channelConnectionProviders = /* @__PURE__ */ new Map();
|
|
29630
29814
|
dispatchedMessages = /* @__PURE__ */ new Set();
|
|
29631
29815
|
forkStates = /* @__PURE__ */ new Map();
|
|
29632
29816
|
dispatchState = {
|
|
@@ -29800,6 +29984,18 @@ var ParallAgentGateway = class {
|
|
|
29800
29984
|
} catch (err) {
|
|
29801
29985
|
this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
29802
29986
|
}
|
|
29987
|
+
} else if (data.event_type === "channel_message") {
|
|
29988
|
+
if (!data.source_id)
|
|
29989
|
+
return;
|
|
29990
|
+
try {
|
|
29991
|
+
const dispatched = await this.fetchAndHandleChannelMessage(data.source_id);
|
|
29992
|
+
if (dispatched) {
|
|
29993
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
29994
|
+
});
|
|
29995
|
+
}
|
|
29996
|
+
} catch (err) {
|
|
29997
|
+
this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
29998
|
+
}
|
|
29803
29999
|
} else if (data.event_type === "approval_decided") {
|
|
29804
30000
|
if (!data.source_id)
|
|
29805
30001
|
return;
|
|
@@ -29878,12 +30074,17 @@ var ParallAgentGateway = class {
|
|
|
29878
30074
|
target_type: target.target_type,
|
|
29879
30075
|
target_id: target.target_id,
|
|
29880
30076
|
content: {
|
|
29881
|
-
trigger_type: event.type === "task" ? "task_assign" : event.type === "task_comment" ? "task_comment" : event.type === "wiki_comment" ? "wiki_comment" : event.type === "schedule" ? "schedule_fire" : event.type === "external_trigger" ? "external_trigger" : event.type === "approval" ? "approval_decided" : "mention",
|
|
30077
|
+
trigger_type: event.type === "task" ? "task_assign" : event.type === "task_comment" ? "task_comment" : event.type === "wiki_comment" ? "wiki_comment" : event.type === "schedule" ? "schedule_fire" : event.type === "external_trigger" ? "external_trigger" : event.type === "channel_message" ? "channel_message" : event.type === "approval" ? "approval_decided" : "mention",
|
|
29882
30078
|
trigger_ref: event.type === "task" ? { task_id: event.targetId } : event.type === "task_comment" ? { comment_id: event.messageId, task_id: event.targetId } : event.type === "wiki_comment" ? { comment_id: event.messageId, target_uri: event.replyTargetUri } : event.type === "schedule" ? { schedule_id: event.targetId, run_id: event.messageId } : event.type === "external_trigger" ? {
|
|
29883
30079
|
trigger_id: event.targetId,
|
|
29884
30080
|
run_id: event.messageId,
|
|
29885
30081
|
connection_id: event.externalConnectionId,
|
|
29886
30082
|
ingress_event_id: event.externalIngressEventId
|
|
30083
|
+
} : event.type === "channel_message" ? {
|
|
30084
|
+
conversation_id: event.targetId,
|
|
30085
|
+
channel_message_id: event.messageId,
|
|
30086
|
+
provider: event.channelProvider,
|
|
30087
|
+
external_conversation_id: event.channelExternalConversationId
|
|
29887
30088
|
} : event.type === "approval" ? { approval_id: event.messageId } : { message_id: event.messageId },
|
|
29888
30089
|
sender_id: event.senderId,
|
|
29889
30090
|
sender_name: event.senderName,
|
|
@@ -30134,7 +30335,15 @@ var ParallAgentGateway = class {
|
|
|
30134
30335
|
context: dispatchContext
|
|
30135
30336
|
})) {
|
|
30136
30337
|
if (runtimeEvent.type === "runtime_session") {
|
|
30338
|
+
const priorAgentSessionId = binding?.agentSessionId;
|
|
30137
30339
|
binding = await this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath);
|
|
30340
|
+
if (event.targetType === "channel_conversation" && binding.agentSessionId !== priorAgentSessionId) {
|
|
30341
|
+
try {
|
|
30342
|
+
await this.opts.client.setChannelConversationSession(this.opts.config.org_id, event.targetId, binding.agentSessionId);
|
|
30343
|
+
} catch (err) {
|
|
30344
|
+
this.opts.log?.warn(`failed to record session mapping for channel conversation ${event.targetId}: ${String(err)}`);
|
|
30345
|
+
}
|
|
30346
|
+
}
|
|
30138
30347
|
if (!inputStepsCreated) {
|
|
30139
30348
|
if (earlierEvents.length > 0) {
|
|
30140
30349
|
await this.createInputStepsForEarlierEvents(binding.agentSessionId, earlierEvents);
|
|
@@ -30937,6 +31146,75 @@ var ParallAgentGateway = class {
|
|
|
30937
31146
|
return true;
|
|
30938
31147
|
return this.handleExternalTriggerRun(run);
|
|
30939
31148
|
}
|
|
31149
|
+
// fetchAndHandleChannelMessage resolves a channel_message dispatch to its
|
|
31150
|
+
// durable ChannelMessage + conversation and hands it to the inbound
|
|
31151
|
+
// pipeline. targetId = the ChannelConversation id, so per-conversation
|
|
31152
|
+
// multi-turn continuity rides the same per-target session mechanics as
|
|
31153
|
+
// chats. Design: docs/engineering-design/external-im-channel-design.md.
|
|
31154
|
+
async fetchAndHandleChannelMessage(messageId) {
|
|
31155
|
+
if (this.shuttingDown)
|
|
31156
|
+
return false;
|
|
31157
|
+
const claimKey = `channel_message:${messageId}`;
|
|
31158
|
+
if (!this.tryClaimMessage(claimKey))
|
|
31159
|
+
return false;
|
|
31160
|
+
let msg = null;
|
|
31161
|
+
let conv = null;
|
|
31162
|
+
try {
|
|
31163
|
+
msg = await this.opts.client.getChannelMessage(this.opts.config.org_id, messageId);
|
|
31164
|
+
conv = await this.opts.client.getChannelConversation(this.opts.config.org_id, msg.conversation_id);
|
|
31165
|
+
} catch (err) {
|
|
31166
|
+
const status = err?.status;
|
|
31167
|
+
if (status === 404) {
|
|
31168
|
+
this.opts.log?.warn(`channel message ${messageId} not accessible (404), acking stale dispatch`);
|
|
31169
|
+
return true;
|
|
31170
|
+
}
|
|
31171
|
+
this.dispatchedMessages.delete(claimKey);
|
|
31172
|
+
this.opts.log?.warn(`channel message fetch failed for ${messageId}, leaving pending: ${String(err)}`);
|
|
31173
|
+
return false;
|
|
31174
|
+
}
|
|
31175
|
+
if (!msg || !conv) {
|
|
31176
|
+
return true;
|
|
31177
|
+
}
|
|
31178
|
+
this.opts.log?.info(`channel message: ${msg.id} (conversation ${conv.id})`);
|
|
31179
|
+
let provider = this.channelConnectionProviders.get(conv.connection_id);
|
|
31180
|
+
if (!provider) {
|
|
31181
|
+
try {
|
|
31182
|
+
const connection = await this.opts.client.getChannelConnection(this.opts.config.org_id, conv.connection_id);
|
|
31183
|
+
provider = connection.provider;
|
|
31184
|
+
this.channelConnectionProviders.set(conv.connection_id, provider);
|
|
31185
|
+
} catch {
|
|
31186
|
+
provider = void 0;
|
|
31187
|
+
}
|
|
31188
|
+
}
|
|
31189
|
+
const event = {
|
|
31190
|
+
type: "channel_message",
|
|
31191
|
+
targetId: conv.id,
|
|
31192
|
+
targetName: conv.external_user_name || conv.external_conversation_id,
|
|
31193
|
+
targetType: "channel_conversation",
|
|
31194
|
+
senderId: msg.external_user_id || "external",
|
|
31195
|
+
senderName: msg.external_user_name || msg.external_user_id || "external user",
|
|
31196
|
+
messageId: msg.id,
|
|
31197
|
+
body: msg.text,
|
|
31198
|
+
sentAt: msg.received_at,
|
|
31199
|
+
channelProvider: provider,
|
|
31200
|
+
channelConversationType: conv.conversation_type || void 0,
|
|
31201
|
+
channelExternalConversationId: conv.external_conversation_id,
|
|
31202
|
+
channelExternalMessageId: msg.external_message_id,
|
|
31203
|
+
ackSourceType: "channel_message",
|
|
31204
|
+
ackSourceId: msg.id
|
|
31205
|
+
};
|
|
31206
|
+
let dispatched;
|
|
31207
|
+
try {
|
|
31208
|
+
dispatched = await this.handleInboundEvent(event);
|
|
31209
|
+
} catch (err) {
|
|
31210
|
+
this.dispatchedMessages.delete(claimKey);
|
|
31211
|
+
throw err;
|
|
31212
|
+
}
|
|
31213
|
+
if (!dispatched) {
|
|
31214
|
+
this.dispatchedMessages.delete(claimKey);
|
|
31215
|
+
}
|
|
31216
|
+
return dispatched;
|
|
31217
|
+
}
|
|
30940
31218
|
async handleExternalTriggerRun(run) {
|
|
30941
31219
|
if (this.shuttingDown)
|
|
30942
31220
|
return false;
|
|
@@ -31088,6 +31366,8 @@ var ParallAgentGateway = class {
|
|
|
31088
31366
|
dispatched = await this.fetchAndHandleScheduleFire(item.source_id, item.actor_id);
|
|
31089
31367
|
} else if (item.event_type === "external_trigger" && item.source_id) {
|
|
31090
31368
|
dispatched = await this.fetchAndHandleExternalTriggerRun(item.source_id);
|
|
31369
|
+
} else if (item.event_type === "channel_message" && item.source_id) {
|
|
31370
|
+
dispatched = await this.fetchAndHandleChannelMessage(item.source_id);
|
|
31091
31371
|
} else if (item.event_type === "approval_decided" && item.source_id) {
|
|
31092
31372
|
dispatched = await this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null);
|
|
31093
31373
|
} else if (item.event_type === "message" && item.source_id && item.chat_id) {
|
|
@@ -33105,9 +33385,9 @@ To respond, you **must** use the Parall CLI via the exec (Bash) tool. If \`paral
|
|
|
33105
33385
|
|
|
33106
33386
|
### Event types and where to reply
|
|
33107
33387
|
|
|
33108
|
-
- **\`[Event: message.new]\`** \u2014 includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat:
|
|
33388
|
+
- **\`[Event: message.new]\`** \u2014 includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat. Write the reply with your file tool first (shell-safe), then send it \u2014 don't wrap message content in \`--text "..."\`, since the shell expands \`$1,000\`\u2192\`,000\` and runs \`$(...)\` inside double quotes:
|
|
33109
33389
|
|
|
33110
|
-
parall messages send prll://cht_xxx --text
|
|
33390
|
+
parall messages send prll://cht_xxx --text-file /tmp/reply.md
|
|
33111
33391
|
|
|
33112
33392
|
- **\`[Event: task.assigned]\` / \`[Event: task.comment.created]\`** \u2014 includes \`[Task: ... (prll://tsk_xxx)]\`. Act on the task; use task CLI subcommands (\`tasks update\`, \`tasks comment\`). See the \`parall-tasks\` skill.
|
|
33113
33393
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/parall",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.37.0",
|
|
4
4
|
"description": "OpenClaw channel plugin for Parall IM",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"openclaw.plugin.json"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@parall/sdk": "1.
|
|
20
|
-
"@parall/agent-core": "1.
|
|
19
|
+
"@parall/sdk": "1.37.0",
|
|
20
|
+
"@parall/agent-core": "1.37.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^22.0.0",
|
|
@@ -69,22 +69,69 @@ parall machines logs prll://mch_xxx --lines 100
|
|
|
69
69
|
```bash
|
|
70
70
|
parall chats list # List all chats
|
|
71
71
|
parall messages list prll://cht_xxx # Read chat message history
|
|
72
|
+
parall messages list prll://cht_xxx --since 2026-01-01 # Only messages at/after a date (RFC3339 or YYYY-MM-DD)
|
|
72
73
|
```
|
|
73
74
|
|
|
75
|
+
## Org-Context Search
|
|
76
|
+
|
|
77
|
+
Before deciding or starting non-trivial work, search the org's real history —
|
|
78
|
+
past discussions, decisions, tasks, and wiki notes — so you don't re-litigate
|
|
79
|
+
settled questions or repeat known mistakes. This searches live org data
|
|
80
|
+
(semantic + keyword), not a local copy, and is permission-filtered to what you
|
|
81
|
+
can see.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Semantic + keyword search across messages, tasks, and wiki
|
|
85
|
+
parall search "auth v5 upgrade"
|
|
86
|
+
|
|
87
|
+
# Restrict entity types (m=message, t=task, w=wiki). --channel narrows the
|
|
88
|
+
# MESSAGE hits to one chat (tasks/wiki are unaffected by it).
|
|
89
|
+
parall search "auth v5 upgrade" --types m,w --channel prll://cht_eng
|
|
90
|
+
|
|
91
|
+
# Time-box to recent activity (RFC3339 or YYYY-MM-DD). Narrows messages + tasks;
|
|
92
|
+
# wiki is always matched by relevance (the index has no authored timestamp).
|
|
93
|
+
parall search "auth v5 upgrade" --since 2026-01-01
|
|
94
|
+
|
|
95
|
+
# Narrow wiki hits to a frontmatter document type
|
|
96
|
+
parall search "deploy steps" --types w --wiki-type Runbook
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Even with zero curated notes, the raw message + task history is searchable — the
|
|
100
|
+
original discussion and its approval/rejection IS the precedent.
|
|
101
|
+
|
|
74
102
|
## Sending Messages
|
|
75
103
|
|
|
76
104
|
Each `[Event: message.new]` includes `[Chat: ... (prll://cht_xxx)]` — use that chat URI to reply.
|
|
77
105
|
|
|
106
|
+
> **How you pass the message body matters — your command runs through a shell.**
|
|
107
|
+
> Inside double quotes the shell expands `$`, backticks, and `$(...)` *before*
|
|
108
|
+
> the CLI sees them: `--text "That costs $1,000"` sends `That costs ,000`, and
|
|
109
|
+
> `--text "$(cmd)"` runs `cmd`. Single quotes instead break on apostrophes
|
|
110
|
+
> (`I'm`, `don't`). So do **not** wrap real message content in quotes — pass it
|
|
111
|
+
> through `--text-file` (a written file, or a quoted heredoc `<<'EOF'` that
|
|
112
|
+
> disables all expansion). Reserve `--text "..."` for short literals with no
|
|
113
|
+
> `$`, backtick, or apostrophe.
|
|
114
|
+
|
|
78
115
|
```bash
|
|
79
|
-
#
|
|
80
|
-
|
|
116
|
+
# One-off reply → quoted heredoc into stdin. The quoted delimiter <<'EOF'
|
|
117
|
+
# disables ALL shell expansion, so $, backticks and apostrophes pass verbatim.
|
|
118
|
+
parall messages send prll://cht_xxx --text-file - <<'PARALL_EOF'
|
|
119
|
+
Sure — that's $1,000, and $(whoami) stays literal. I'm on it.
|
|
120
|
+
PARALL_EOF
|
|
81
121
|
|
|
82
|
-
#
|
|
83
|
-
|
|
122
|
+
# Longer / multi-line reply → write it with your file tool (no shell touches
|
|
123
|
+
# the body), then point --text-file at the file.
|
|
124
|
+
parall messages send prll://cht_xxx --text-file /tmp/reply.md
|
|
125
|
+
|
|
126
|
+
# Short literal with no $, backtick, or apostrophe → --text is fine.
|
|
127
|
+
parall messages send prll://cht_xxx --text "On it"
|
|
128
|
+
|
|
129
|
+
# Direct message by user URI or display name (same --text-file / heredoc rules)
|
|
130
|
+
parall dm prll://usr_xxx --text-file /tmp/reply.md
|
|
84
131
|
parall dm "Alice" --text "Hello"
|
|
85
132
|
|
|
86
133
|
# Thread reply
|
|
87
|
-
parall messages send prll://cht_xxx --text
|
|
134
|
+
parall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id 01JWC...
|
|
88
135
|
|
|
89
136
|
# FYI message (no response expected — the recipient sees `[Hint: no_reply]`)
|
|
90
137
|
parall messages send prll://cht_xxx --text "FYI: done" --no-reply
|
|
@@ -116,7 +163,9 @@ parall messages send prll://cht_xxx --attachment att_xxx --text "See attached"
|
|
|
116
163
|
parall dm "Alice" --file /tmp/report.pdf --text "Report attached"
|
|
117
164
|
```
|
|
118
165
|
|
|
119
|
-
`--file` and `--attachment` are mutually exclusive. `--text`
|
|
166
|
+
`--file` and `--attachment` are mutually exclusive. A caption (`--text` for
|
|
167
|
+
short literals, or `--text-file` for anything with `$`, backticks, or quotes)
|
|
168
|
+
can be combined with either.
|
|
120
169
|
|
|
121
170
|
## Approvals
|
|
122
171
|
|
|
@@ -172,4 +221,26 @@ When a message or event references `prll://sch_xxx` or `prll://srn_xxx`, or when
|
|
|
172
221
|
|
|
173
222
|
When a message or event references `prll://xcn_xxx`, `prll://xin_xxx`, `prll://xtr_xxx`, or `prll://xrn_xxx`, or when you receive `[Event: external.trigger]`, switch to the **parall-external-triggers** skill for the CLI commands (connections / triggers / events / runs).
|
|
174
223
|
|
|
224
|
+
## References (relationship graph)
|
|
225
|
+
|
|
226
|
+
`prll://` references between entities form a graph — a message cites a task, a
|
|
227
|
+
task cites a wiki page, and so on. Walk it to answer "what is this decision /
|
|
228
|
+
entity connected to". All results are permission-filtered to what you can see.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Resolve URIs to entity metadata (titles, status, previews)
|
|
232
|
+
parall refs resolve prll://tsk_xxx prll://wik_xxx
|
|
233
|
+
|
|
234
|
+
# Single hop — who references X
|
|
235
|
+
parall refs backlinks prll://tsk_xxx
|
|
236
|
+
|
|
237
|
+
# Multi-hop — the connected sub-graph around X (entity-level URI only — no
|
|
238
|
+
# path/anchor; depth 1–4, default 2)
|
|
239
|
+
parall refs graph prll://tsk_xxx --depth 2
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`refs graph` traverses both directions (inbound + outbound) and returns `nodes`
|
|
243
|
+
and `edges` with each node's hop `depth`. `truncated: true` means a size cap clipped
|
|
244
|
+
the result — narrow it with a smaller `--depth`.
|
|
245
|
+
|
|
175
246
|
CLI success output is JSON. Errors print a JSON line (`{"error","status","code",...}`) and, on a `PERMISSION_DENIED`, may add a plain-text `Request approval:` line — read both.
|
package/src/hooks.ts
CHANGED
|
@@ -90,9 +90,9 @@ To respond, you **must** use the Parall CLI via the exec (Bash) tool. If \`paral
|
|
|
90
90
|
|
|
91
91
|
### Event types and where to reply
|
|
92
92
|
|
|
93
|
-
- **\`[Event: message.new]\`** — includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat:
|
|
93
|
+
- **\`[Event: message.new]\`** — includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat. Write the reply with your file tool first (shell-safe), then send it — don't wrap message content in \`--text "..."\`, since the shell expands \`$1,000\`→\`,000\` and runs \`$(...)\` inside double quotes:
|
|
94
94
|
|
|
95
|
-
parall messages send prll://cht_xxx --text
|
|
95
|
+
parall messages send prll://cht_xxx --text-file /tmp/reply.md
|
|
96
96
|
|
|
97
97
|
- **\`[Event: task.assigned]\` / \`[Event: task.comment.created]\`** — includes \`[Task: ... (prll://tsk_xxx)]\`. Act on the task; use task CLI subcommands (\`tasks update\`, \`tasks comment\`). See the \`parall-tasks\` skill.
|
|
98
98
|
|