@parall/agent-core 1.55.4 → 1.56.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.
@@ -1,4 +1,5 @@
1
1
  import type { ForkResult, ParallEvent } from './types.js';
2
+ export declare function formatApprovalDecisionReason(status: string | undefined, reason: string | null | undefined): string;
2
3
  export declare function buildEventBody(event: ParallEvent): string;
3
4
  export declare function buildEventBodyForForkResult(event: ParallEvent): string;
4
5
  export declare function buildForkScopePrefix(event: ParallEvent): string;
@@ -1 +1 @@
1
- {"version":3,"file":"event-format.d.ts","sourceRoot":"","sources":["../src/event-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA+B1D,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA0IzD;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEtE;AAyGD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAK/D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAkBnE"}
1
+ {"version":3,"file":"event-format.d.ts","sourceRoot":"","sources":["../src/event-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA+B1D,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,MAAM,CAMR;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA2IzD;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEtE;AAyGD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAK/D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAkBnE"}
@@ -22,6 +22,14 @@ function sanitizeMeta(value) {
22
22
  .replace(/[[\]|]/g, ' ')
23
23
  .trim();
24
24
  }
25
+ export function formatApprovalDecisionReason(status, reason) {
26
+ const normalized = reason?.trim();
27
+ if (status !== 'rejected' || !normalized)
28
+ return '';
29
+ // JSON string encoding preserves the reviewer's text while preventing
30
+ // newlines or bracket markers from forging another structured event line.
31
+ return `Reason (JSON): ${JSON.stringify(normalized)}`;
32
+ }
25
33
  export function buildEventBody(event) {
26
34
  const lines = [];
27
35
  if (event.type === 'message') {
@@ -47,6 +55,8 @@ export function buildEventBody(event) {
47
55
  .join(' | ');
48
56
  lines.push(`[Thread: ${threadMeta}]`);
49
57
  }
58
+ if (event.deliveryReason)
59
+ lines.push(`[Delivery: ${sanitizeMeta(event.deliveryReason)}]`);
50
60
  if (event.unreadCount != null && event.unreadCount > 1) {
51
61
  const countStr = event.unreadCount >= 1000 ? '999+' : String(event.unreadCount);
52
62
  const sinceStr = event.unreadSince ? ` | since: prll://${event.unreadSince}` : '';
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { ApiError, mentionTargetsUser } from '@parall/sdk';
6
- import { buildEventBody, buildEventBodyForForkResult, buildForkResultPrefix, buildForkScopePrefix, } from './event-format.js';
6
+ import { buildEventBody, buildEventBodyForForkResult, buildForkResultPrefix, buildForkScopePrefix, formatApprovalDecisionReason, } from './event-format.js';
7
7
  import { CAPABILITY_SLACK_SEND, CAPABILITY_WECHAT_SEND, channelCapabilityKeyFor, } from './channel-capability.js';
8
8
  import { buildErrorStepContent, } from './dispatch-adapter.js';
9
9
  import { clearForkContinuationRetries, createActiveForkState, resolveForkContinuationPrefix, retainForkContinuationRetries, } from './fork-state.js';
@@ -2061,11 +2061,45 @@ export class ParallAgentGateway {
2061
2061
  const hasAttachments = message.attachments?.length;
2062
2062
  if (!body && !hasAttachments)
2063
2063
  return { action: 'skip' };
2064
- if (chatInfo.type === 'group' && chatInfo.agentRoutingMode !== 'active') {
2065
- const mentions = content.mentions ?? [];
2066
- const isMentioned = mentions.some((mention) => mentionTargetsUser(mention.user_id, this.opts.agentUserId, 'agent'));
2067
- if (!isMentioned)
2064
+ const mentions = content.mentions ?? [];
2065
+ const isMentioned = mentions.some((mention) => mentionTargetsUser(mention.user_id, this.opts.agentUserId, 'agent'));
2066
+ let deliveryReason;
2067
+ if (message.thread_root_id) {
2068
+ // Thread domain: admission mirrors the server's thread fan-out —
2069
+ // mention or thread watcher — never the chat's routing mode
2070
+ // (thread-subscription-design.md §1: routing modes govern top-level
2071
+ // messages only). The watch probe is a fresh authoritative read on
2072
+ // every call: this decision also runs on the WorkItem-terminating
2073
+ // redrive path, where a skip acks the row for good, so it must not
2074
+ // be answered from a cache (agent-reply-policy-design.md §4.2).
2075
+ if (isMentioned) {
2076
+ deliveryReason = 'mention';
2077
+ }
2078
+ else {
2079
+ let watching;
2080
+ try {
2081
+ watching = await this.opts.client.isWatchingThread(message.thread_root_id);
2082
+ }
2083
+ catch {
2084
+ // Uncertain — a skip here would terminally ack a watcher delivery
2085
+ // on the redrive path. Leave the WorkItem pending for retry.
2086
+ return { action: 'retry' };
2087
+ }
2088
+ if (!watching)
2089
+ return { action: 'skip' };
2090
+ deliveryReason = 'watcher';
2091
+ }
2092
+ }
2093
+ else if (chatInfo.type === 'group') {
2094
+ if (chatInfo.agentRoutingMode !== 'active' && !isMentioned)
2068
2095
  return { action: 'skip' };
2096
+ // Annotated whenever mentioned, in every routing mode: the server's
2097
+ // mention fan-out writes the WorkItem first (reason=mention), so parel
2098
+ // renders it — active mode must not diverge. Mode-admitted deliveries
2099
+ // stay bare. DM top-level stays bare too: D5 skips agent mention
2100
+ // fan-out there, so its WorkItem carries no reason on either family.
2101
+ if (isMentioned)
2102
+ deliveryReason = 'mention';
2069
2103
  }
2070
2104
  const attachments = (message.attachments ?? []).map((a) => ({
2071
2105
  id: a.id,
@@ -2113,6 +2147,7 @@ export class ParallAgentGateway {
2113
2147
  messageId: message.id,
2114
2148
  body: body || '[attachment]',
2115
2149
  threadRootId: message.thread_root_id ?? undefined,
2150
+ deliveryReason,
2116
2151
  noReply: message.hints?.no_reply ?? false,
2117
2152
  attachments: attachments.length > 0 ? attachments : undefined,
2118
2153
  sentAt: message.created_at,
@@ -2648,7 +2683,9 @@ export class ParallAgentGateway {
2648
2683
  this.opts.log?.info(`approval decided: ${approval.id} (${approval.status})`);
2649
2684
  const statusLabel = approval.status === 'approved' ? 'Approved' : 'Rejected';
2650
2685
  const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : '';
2651
- const body = `${statusLabel}: ${approval.title}${execInfo}`;
2686
+ const reasonLine = formatApprovalDecisionReason(approval.status, approval.decision_reason);
2687
+ const reasonInfo = reasonLine ? `\n${reasonLine}` : '';
2688
+ const body = `${statusLabel}: ${approval.title}${execInfo}${reasonInfo}`;
2652
2689
  const event = {
2653
2690
  type: 'approval',
2654
2691
  targetId: chatId ?? approval.chat_id,
@@ -17,7 +17,7 @@ import { PARALL_CLIP_AUTHORING_SKILL } from './parall-clip-authoring.js';
17
17
  export const SKILLS = [
18
18
  {
19
19
  name: 'parall-platform',
20
- description: "Parall platform queries and lightweight agent provisioning: list org members, agents, chats, read message history, check identity, create another agent, or walk the prll:// reference graph (resolve URIs, backlinks, multi-hop graph). Use when: user asks about org members, who's online, chat history, agent list, creating an agent, identity/auth questions, or you need to find what an entity is connected to / who references it.",
20
+ description: "Parall platform queries and lightweight agent provisioning: list org members, agents, chats, read message history, watch/unwatch threads, check identity, create another agent, or walk the prll:// reference graph (resolve URIs, backlinks, multi-hop graph). Use when: user asks about org members, who's online, chat history, agent list, creating an agent, identity/auth questions, you want to follow or mute a thread (e.g. after `[Delivery: watcher]` wake-ups), or you need to find what an entity is connected to / who references it.",
21
21
  content: PARALL_PLATFORM_SKILL,
22
22
  },
23
23
  {
@@ -1,2 +1,2 @@
1
- export declare const PARALL_PLATFORM_SKILL = "# Parall Platform\n\nQuery organization data via the Parall CLI. Auth is pre-configured.\n\n## Identity\n\n```bash\nparall whoami\n```\n\n## Members & Agents\n\n```bash\nparall members list # All org members (humans + agents)\nparall agents list # Agents only\nparall users get prll://usr_xxx # Get user details by ID\nparall members profile prll://usr_xxx # A member's public profile (title / about)\nparall agents instructions # Your own private Instructions (admin-maintained)\n```\n\nEvery member carries an org-scoped public profile: `title` (role, e.g.\n\"Platform Lead\") and `description` (a short about). `members list` includes\nboth \u2014 use them to route work to the right person or agent. Your own system\nprompt already contains your public profile and your private Instructions;\nchanging yours takes a human decision \u2014 run `parall profile set` and follow\nthe approval flow it prints (see \"Your Profile\" below).\n\nCreate a hosted agent when the user asks for a Parall-managed runtime. Hosted\nprovisioning is asynchronous: creation means the agent identity, API key, and\nmachine record were accepted, not that the runtime is online yet. Use `--wait`\nto wait until the machine reaches `running`, and use `--wait-online` when the\ntask requires the child agent to be connected before you report completion.\nFor hosted agents, use `--discard-api-key`; the server injects the one-time key\ninto the hosted runtime, so the parent agent must not print or persist it.\n\nCreate a self-hosted agent only when the runtime will be connected outside\nParall-managed compute. In that case, write the one-time `api_key` to\n`--api-key-file` so it is not captured in tool-result logs. Treat `api_key` as a\nsecret: do not print, read aloud, post it in shared chats, or echo the file\ncontents. Include the `user.id` in normal responses, and pass the key file only\nthrough an explicit secure runtime handoff when connection is required. Never\nuse `--show-api-key` from an agent runtime. Agent callers cannot set provider\noverrides until the dedicated fine-grained permission flow lands.\n\n```bash\n# Hosted runtime (Parall-managed compute)\nparall agents create \\\n --name \"Research Agent\" \\\n --runtime-type codex \\\n --machine-type cloud \\\n --machine-label standard \\\n --discard-api-key \\\n --wait \\\n --wait-online\n\n# Self-hosted runtime\nparall agents create --name \"Research Agent\" --runtime-type codex --api-key-file /tmp/research-agent.api-key\n```\n\nInspect hosted provisioning directly when a create command returns before the\nruntime is online, or when you need logs for a failed machine. If `agents create`\nexits non-zero after creating a hosted agent, read the printed `user.id` and\n`machine.id`, then use these commands to decide whether to wait, inspect logs,\nor report the failed machine for retry.\n\n```bash\nparall machines status prll://mch_xxx\nparall machines logs prll://mch_xxx --lines 100\n```\n\n## Chats & Messages\n\n```bash\nparall chats list # List all chats\nparall messages list prll://cht_xxx # Read chat message history\nparall messages list prll://cht_xxx --since 2026-01-01 # Only messages at/after a date (RFC3339 or YYYY-MM-DD)\n```\n\n## Org-Context Search\n\nBefore deciding or starting non-trivial work, search the org's real history \u2014\npast discussions, decisions, tasks, and wiki notes \u2014 so you don't re-litigate\nsettled questions or repeat known mistakes. This searches live org data\n(semantic + keyword), not a local copy, and is permission-filtered to what you\ncan see.\n\n```bash\n# Semantic + keyword search across messages, tasks, wiki, and comments\nparall search \"auth v5 upgrade\"\n\n# Restrict entity types (m=message, t=task, w=wiki, c=comment). --channel\n# narrows the MESSAGE hits to one chat (tasks/wiki/comments are unaffected).\nparall search \"auth v5 upgrade\" --types m,w --channel prll://cht_eng\n\n# Time-box to recent activity (RFC3339 or YYYY-MM-DD). Narrows messages + tasks;\n# wiki is always matched by relevance (the index has no authored timestamp).\nparall search \"auth v5 upgrade\" --since 2026-01-01\n\n# Narrow wiki hits to a frontmatter document type\nparall search \"deploy steps\" --types w --wiki-type Runbook\n```\n\nEven with zero curated notes, the raw message + task history is searchable \u2014 the\noriginal discussion and its approval/rejection IS the precedent.\n\n## Sending Messages\n\nEach `[Event: message.new]` includes `[Chat: ... (prll://cht_xxx)]` \u2014 use that chat URI to reply.\n\n> **How you pass the message body matters \u2014 your command runs through a shell.**\n> Inside double quotes the shell expands `$`, backticks, and `$(...)` *before*\n> the CLI sees them: `--text \"That costs $1,000\"` sends `That costs ,000`, and\n> `--text \"$(cmd)\"` runs `cmd`. Single quotes instead break on apostrophes\n> (`I'm`, `don't`). So do **not** wrap real message content in quotes \u2014 pass it\n> through `--text-file` (a written file, or a quoted heredoc `<<'EOF'` that\n> disables all expansion). Reserve `--text \"...\"` for short literals with no\n> `$`, backtick, or apostrophe.\n\n```bash\n# One-off reply \u2192 quoted heredoc into stdin. The quoted delimiter <<'EOF'\n# disables ALL shell expansion, so $, backticks and apostrophes pass verbatim.\nparall messages send prll://cht_xxx --text-file - <<'PARALL_EOF'\nSure \u2014 that's $1,000, and $(whoami) stays literal. I'm on it.\nPARALL_EOF\n\n# Longer / multi-line reply \u2192 write it with your file tool (no shell touches\n# the body), then point --text-file at the file.\nparall messages send prll://cht_xxx --text-file /tmp/reply.md\n\n# Short literal with no $, backtick, or apostrophe \u2192 --text is fine.\nparall messages send prll://cht_xxx --text \"On it\"\n\n# Direct message by user URI or display name (same --text-file / heredoc rules)\nparall dm prll://usr_xxx --text-file /tmp/reply.md\nparall dm \"Alice\" --text \"Hello\"\n\n# Thread reply\nparall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id 01JWC...\n\n# FYI message (no response expected \u2014 the recipient sees `[Hint: no_reply]`)\nparall messages send prll://cht_xxx --text \"FYI: done\" --no-reply\n\n# Silence this turn entirely \u2014 no chat message produced. Use when you receive\n# `[Hint: no_reply]` or otherwise decide the turn needs no visible reply.\n# Run BEFORE any `messages send` / `dm`; those still deliver real messages.\nparall no-reply --reason \"ack only, nothing to add\"\n```\n\n## Files & Attachments\n\nAttachments appear in events as `[Attachment: prll://att_xxx | mime | size | name]`.\n\n```bash\n# Download an attachment\nparall files download att_xxx --output /tmp/file.png\n\n# Upload a file (returns attachment_id)\nparall files upload /tmp/report.pdf\n\n# Send a message with a file\nparall messages send prll://cht_xxx --file /tmp/output.png --text \"Done\"\n\n# Send an existing attachment to another chat\nparall messages send prll://cht_xxx --attachment att_xxx --text \"See attached\"\n\n# DM with a file\nparall dm \"Alice\" --file /tmp/report.pdf --text \"Report attached\"\n```\n\n`--file` and `--attachment` are mutually exclusive. A caption (`--text` for\nshort literals, or `--text-file` for anything with `$`, backticks, or quotes)\ncan be combined with either.\n\n## Approvals\n\nWhen a CLI command returns a `PERMISSION_DENIED` error, the output includes the denied `action` and `resource_uri`. Whether that action can be approved is decided by the server (there is no fixed allowlist):\n- If it IS approvable, a `Request approval:` line with a `parall approvals request` command follows \u2014 fill in the placeholders it shows (`--chat`, `--title`, `--reason`) and run it.\n- If it is NOT approvable, the output says so \u2014 ask a human with permission instead of requesting approval.\n\nA different `INVALID_TARGET` error means you addressed the wrong kind of thing (e.g. a `usr_` id where a chat is expected). Follow the message (e.g. use `parall dm` to message a user) \u2014 do not request approval for it.\n\n```bash\n# Request approval (use action and resource_uri from the error)\nparall approvals request --action chat.archive --resource prll://cht_xxx --chat prll://cht_yyy --title \"Archive old channel\" --reason \"No activity in 6 months\"\n\n# Check a specific approval's status\nparall approvals get prll://apr_xxx\n\n# Wait for a decision (blocks until approved/rejected/timeout)\nparall approvals wait prll://apr_xxx --timeout 300\n\n# List all your pending approvals\nparall approvals list\n\n# List available approvable actions\nparall approvals actions\n\n# Cancel a pending request you made\nparall approvals cancel prll://apr_xxx\n```\n\nOnly request approval after receiving an actual `PERMISSION_DENIED` error \u2014 never preemptively. The `--chat` flag specifies where the approval card appears; use the chat where the conversation is happening.\n\n## Your Profile\n\nYou can read everything about yourself, and you edit it the same way you do\nanything else: run the command. Your edits need a human decision, so the\nattempt answers PERMISSION_DENIED with a ready-made `parall approvals\nrequest` command that already carries exactly what you tried to write \u2014 run\nit, picking a chat your manager (or an org admin) is in.\n\n```bash\nparall profile show # identity, org profile, Instructions, manager\nparall profile set --title \"Release captain\"\nparall profile set --about \"I watch deploys and chase regressions\"\nparall profile set --instructions-file /tmp/new-instructions.md\nparall profile set --display-name \"Pai\"\n```\n\nThe approval card shows the approver the exact before \u2192 after; the decision\narrives as an `approval.decided` event. Avatar changes have no proposal\npath \u2014 ask your manager or an org admin.\n\n## Reference URIs\n\nEvery entity is addressable with a `prll://` URI. Common prefixes you'll see in events, messages, and schedule descriptions:\n\n| Prefix | Entity | Skill |\n|--------|--------|-------|\n| `prll://usr_` | User (human or agent) | parall-platform |\n| `prll://cht_` | Chat | parall-platform |\n| `prll://msg_` | Message | parall-platform |\n| `prll://cmt_` | Comment (on tasks, wiki pages, changesets) | by target: task comment \u2192 parall-tasks, wiki/changeset comment \u2192 parall-wiki |\n| `prll://ase_` | Agent session | parall-platform |\n| `prll://tsk_` | Task | parall-tasks |\n| `prll://prj_` | Project | parall-tasks |\n| `prll://sch_` | Schedule (time trigger) | parall-schedules |\n| `prll://srn_` | Schedule run (single fire audit record; carries fire-time snapshot) | parall-schedules |\n| `prll://xcn_` | External Trigger Connection (incoming endpoint) | parall-external-triggers |\n| `prll://xin_` | External Trigger Event (single incoming event audit record) | parall-external-triggers |\n| `prll://xtr_` | External Trigger (incoming trigger configuration) | parall-external-triggers |\n| `prll://xrn_` | External Trigger run (single matched dispatch audit record) | parall-external-triggers |\n| `prll://wik_` | Wiki | parall-wiki |\n| `prll://att_` | Attachment | parall-platform (files) |\n\nWhen a message or event references `prll://sch_xxx` or `prll://srn_xxx`, or when you receive `[Event: schedule.fired]`, switch to the **parall-schedules** skill for the CLI commands (create / list / pause / resume / cancel / runs).\n\nWhen 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).\n\n## References (relationship graph)\n\n`prll://` references between entities form a graph \u2014 a message cites a task, a\ntask cites a wiki page, and so on. Walk it to answer \"what is this decision /\nentity connected to\". All results are permission-filtered to what you can see.\n\n```bash\n# Resolve URIs to entity metadata (titles, status, previews)\nparall refs resolve prll://tsk_xxx prll://wik_xxx\n\n# Complete text for authorized message refs (default preview is 100 characters)\nparall refs resolve --full prll://msg_xxx\n\n# Single hop \u2014 who references X\nparall refs backlinks prll://tsk_xxx\n\n# Multi-hop \u2014 the connected sub-graph around X (entity-level URI only \u2014 no\n# path/anchor; depth 1\u20134, default 2)\nparall refs graph prll://tsk_xxx --depth 2\n```\n\n`refs graph` traverses both directions (inbound + outbound) and returns `nodes`\nand `edges` with each node's hop `depth`. `truncated: true` means a size cap clipped\nthe result \u2014 narrow it with a smaller `--depth`. Edges carry `context` \u2014 the\nauthor's annotation from `[context](prll://...)` \u2014 telling you *why* two\nentities are linked, not just that they are.\n\nThe graph returns bare node URIs (no titles). The usual two-step: `refs graph`\nfor topology, then batch-`refs resolve` the node URIs you care about for\ntitles/status. If graph rejects your URI with a path/anchor error, strip it to\nthe entity root (`prll://wik_xxx/docs/a.md` \u2192 `prll://wik_xxx`) and re-query \u2014\nbut note this WIDENS the query to the whole entity, not that one file: the\ngraph seeds from the wiki id, so a specific file's outbound links may sit\ndeeper in the result (or past the size caps). For refs pointing AT one file\n(inbound), `refs backlinks` on the full file URI is precise. There is no\nprecise query for one file's OUTBOUND edges today \u2014 the widened root graph is\nbest-effort for those, or read the file itself for its `prll://` links.\nWiki-file nodes inside a graph *result* do legitimately carry paths.\n\n`refs backlinks` items include a `snippet` of the referencing content \u2014 often\nenough to judge relevance without fetching the source entity.\n\nCLI 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 \u2014 read both.\n";
1
+ export declare const PARALL_PLATFORM_SKILL = "# Parall Platform\n\nQuery organization data via the Parall CLI. Auth is pre-configured.\n\n## Identity\n\n```bash\nparall whoami\n```\n\n## Members & Agents\n\n```bash\nparall members list # All org members (humans + agents)\nparall agents list # Agents only\nparall users get prll://usr_xxx # Get user details by ID\nparall members profile prll://usr_xxx # A member's public profile (title / about)\nparall agents instructions # Your own private Instructions (admin-maintained)\n```\n\nEvery member carries an org-scoped public profile: `title` (role, e.g.\n\"Platform Lead\") and `description` (a short about). `members list` includes\nboth \u2014 use them to route work to the right person or agent. Your own system\nprompt already contains your public profile and your private Instructions;\nchanging yours takes a human decision \u2014 run `parall profile set` and follow\nthe approval flow it prints (see \"Your Profile\" below).\n\nCreate a hosted agent when the user asks for a Parall-managed runtime. Hosted\nprovisioning is asynchronous: creation means the agent identity, API key, and\nmachine record were accepted, not that the runtime is online yet. Use `--wait`\nto wait until the machine reaches `running`, and use `--wait-online` when the\ntask requires the child agent to be connected before you report completion.\nFor hosted agents, use `--discard-api-key`; the server injects the one-time key\ninto the hosted runtime, so the parent agent must not print or persist it.\n\nCreate a self-hosted agent only when the runtime will be connected outside\nParall-managed compute. In that case, write the one-time `api_key` to\n`--api-key-file` so it is not captured in tool-result logs. Treat `api_key` as a\nsecret: do not print, read aloud, post it in shared chats, or echo the file\ncontents. Include the `user.id` in normal responses, and pass the key file only\nthrough an explicit secure runtime handoff when connection is required. Never\nuse `--show-api-key` from an agent runtime. Agent callers cannot set provider\noverrides until the dedicated fine-grained permission flow lands.\n\n```bash\n# Hosted runtime (Parall-managed compute)\nparall agents create \\\n --name \"Research Agent\" \\\n --runtime-type codex \\\n --machine-type cloud \\\n --machine-label standard \\\n --discard-api-key \\\n --wait \\\n --wait-online\n\n# Self-hosted runtime\nparall agents create --name \"Research Agent\" --runtime-type codex --api-key-file /tmp/research-agent.api-key\n```\n\nInspect hosted provisioning directly when a create command returns before the\nruntime is online, or when you need logs for a failed machine. If `agents create`\nexits non-zero after creating a hosted agent, read the printed `user.id` and\n`machine.id`, then use these commands to decide whether to wait, inspect logs,\nor report the failed machine for retry.\n\n```bash\nparall machines status prll://mch_xxx\nparall machines logs prll://mch_xxx --lines 100\n```\n\n## Chats & Messages\n\n```bash\nparall chats list # List all chats\nparall messages list prll://cht_xxx # Read chat message history\nparall messages list prll://cht_xxx --since 2026-01-01 # Only messages at/after a date (RFC3339 or YYYY-MM-DD)\n```\n\n## Org-Context Search\n\nBefore deciding or starting non-trivial work, search the org's real history \u2014\npast discussions, decisions, tasks, and wiki notes \u2014 so you don't re-litigate\nsettled questions or repeat known mistakes. This searches live org data\n(semantic + keyword), not a local copy, and is permission-filtered to what you\ncan see.\n\n```bash\n# Semantic + keyword search across messages, tasks, wiki, and comments\nparall search \"auth v5 upgrade\"\n\n# Restrict entity types (m=message, t=task, w=wiki, c=comment). --channel\n# narrows the MESSAGE hits to one chat (tasks/wiki/comments are unaffected).\nparall search \"auth v5 upgrade\" --types m,w --channel prll://cht_eng\n\n# Time-box to recent activity (RFC3339 or YYYY-MM-DD). Narrows messages + tasks;\n# wiki is always matched by relevance (the index has no authored timestamp).\nparall search \"auth v5 upgrade\" --since 2026-01-01\n\n# Narrow wiki hits to a frontmatter document type\nparall search \"deploy steps\" --types w --wiki-type Runbook\n```\n\nEven with zero curated notes, the raw message + task history is searchable \u2014 the\noriginal discussion and its approval/rejection IS the precedent.\n\n## Sending Messages\n\nEach `[Event: message.new]` includes `[Chat: ... (prll://cht_xxx)]` \u2014 use that chat URI to reply.\n\n> **How you pass the message body matters \u2014 your command runs through a shell.**\n> Inside double quotes the shell expands `$`, backticks, and `$(...)` *before*\n> the CLI sees them: `--text \"That costs $1,000\"` sends `That costs ,000`, and\n> `--text \"$(cmd)\"` runs `cmd`. Single quotes instead break on apostrophes\n> (`I'm`, `don't`). So do **not** wrap real message content in quotes \u2014 pass it\n> through `--text-file` (a written file, or a quoted heredoc `<<'EOF'` that\n> disables all expansion). Reserve `--text \"...\"` for short literals with no\n> `$`, backtick, or apostrophe.\n\n```bash\n# One-off reply \u2192 quoted heredoc into stdin. The quoted delimiter <<'EOF'\n# disables ALL shell expansion, so $, backticks and apostrophes pass verbatim.\nparall messages send prll://cht_xxx --text-file - <<'PARALL_EOF'\nSure \u2014 that's $1,000, and $(whoami) stays literal. I'm on it.\nPARALL_EOF\n\n# Longer / multi-line reply \u2192 write it with your file tool (no shell touches\n# the body), then point --text-file at the file.\nparall messages send prll://cht_xxx --text-file /tmp/reply.md\n\n# Short literal with no $, backtick, or apostrophe \u2192 --text is fine.\nparall messages send prll://cht_xxx --text \"On it\"\n\n# Direct message by user URI or display name (same --text-file / heredoc rules)\nparall dm prll://usr_xxx --text-file /tmp/reply.md\nparall dm \"Alice\" --text \"Hello\"\n\n# Thread reply\nparall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id prll://msg_xxx\n\n# FYI message (no response expected \u2014 the recipient sees `[Hint: no_reply]`)\nparall messages send prll://cht_xxx --text \"FYI: done\" --no-reply\n\n# Silence this turn entirely \u2014 no chat message produced. Use when you receive\n# `[Hint: no_reply]` or otherwise decide the turn needs no visible reply.\n# Run BEFORE any `messages send` / `dm`; those still deliver real messages.\nparall no-reply --reason \"ack only, nothing to add\"\n```\n\n## Thread Subscriptions\n\nReplying in a thread subscribes you to it: every later reply reaches you as\n`[Event: message.new]` with a `[Thread: ...]` line, marked\n`[Delivery: watcher]`. You are also auto-subscribed when a thread reply\n@-mentions you personally, and when someone replies to a thread whose root\nmessage you wrote. `[Delivery: mention]` means you were @-mentioned in that\nmessage.\n\n```bash\nparall messages watch prll://msg_xxx # follow a thread without replying (ID = the thread's root message)\nparall messages unwatch prll://msg_xxx # stop receiving a thread's replies\nparall messages watchers prll://msg_xxx # list who is watching a thread\n```\n\nUnwatch is sticky: no automatic source (including later @-mentions \u2014 you\nstill get the one-off mention, just no subscription) re-subscribes you.\nOnly replying in the thread yourself or an explicit `watch` does. Unwatch\nthreads you have nothing more to contribute to instead of ignoring their\nwake-ups.\n\n## Files & Attachments\n\nAttachments appear in events as `[Attachment: prll://att_xxx | mime | size | name]`.\n\n```bash\n# Download an attachment\nparall files download att_xxx --output /tmp/file.png\n\n# Upload a file (returns attachment_id)\nparall files upload /tmp/report.pdf\n\n# Send a message with a file\nparall messages send prll://cht_xxx --file /tmp/output.png --text \"Done\"\n\n# Send an existing attachment to another chat\nparall messages send prll://cht_xxx --attachment att_xxx --text \"See attached\"\n\n# DM with a file\nparall dm \"Alice\" --file /tmp/report.pdf --text \"Report attached\"\n```\n\n`--file` and `--attachment` are mutually exclusive. A caption (`--text` for\nshort literals, or `--text-file` for anything with `$`, backticks, or quotes)\ncan be combined with either.\n\n## Approvals\n\nWhen a CLI command returns a `PERMISSION_DENIED` error, the output includes the denied `action` and `resource_uri`. Whether that action can be approved is decided by the server (there is no fixed allowlist):\n- If it IS approvable, a `Request approval:` line with a `parall approvals request` command follows \u2014 fill in the placeholders it shows (`--chat`, `--title`, `--reason`) and run it.\n- If it is NOT approvable, the output says so \u2014 ask a human with permission instead of requesting approval.\n\nA different `INVALID_TARGET` error means you addressed the wrong kind of thing (e.g. a `usr_` id where a chat is expected). Follow the message (e.g. use `parall dm` to message a user) \u2014 do not request approval for it.\n\n```bash\n# Request approval (use action and resource_uri from the error)\nparall approvals request --action chat.archive --resource prll://cht_xxx --chat prll://cht_yyy --title \"Archive old channel\" --reason \"No activity in 6 months\"\n\n# Check a specific approval's status\nparall approvals get prll://apr_xxx\n\n# Wait for a decision (blocks until approved/rejected/timeout)\nparall approvals wait prll://apr_xxx --timeout 300\n\n# List all your pending approvals\nparall approvals list\n\n# List available approvable actions\nparall approvals actions\n\n# Cancel a pending request you made\nparall approvals cancel prll://apr_xxx\n```\n\nOnly request approval after receiving an actual `PERMISSION_DENIED` error \u2014 never preemptively. The `--chat` flag specifies where the approval card appears; use the chat where the conversation is happening.\n\n## Your Profile\n\nYou can read everything about yourself, and you edit it the same way you do\nanything else: run the command. Your edits need a human decision, so the\nattempt answers PERMISSION_DENIED with a ready-made `parall approvals\nrequest` command that already carries exactly what you tried to write \u2014 run\nit, picking a chat your manager (or an org admin) is in.\n\n```bash\nparall profile show # identity, org profile, Instructions, manager\nparall profile set --title \"Release captain\"\nparall profile set --about \"I watch deploys and chase regressions\"\nparall profile set --instructions-file /tmp/new-instructions.md\nparall profile set --display-name \"Pai\"\n```\n\nThe approval card shows the approver the exact before \u2192 after; the decision\narrives as an `approval.decided` event. Avatar changes have no proposal\npath \u2014 ask your manager or an org admin.\n\n## Reference URIs\n\nEvery entity is addressable with a `prll://` URI. Common prefixes you'll see in events, messages, and schedule descriptions:\n\n| Prefix | Entity | Skill |\n|--------|--------|-------|\n| `prll://usr_` | User (human or agent) | parall-platform |\n| `prll://cht_` | Chat | parall-platform |\n| `prll://msg_` | Message | parall-platform |\n| `prll://cmt_` | Comment (on tasks, wiki pages, changesets) | by target: task comment \u2192 parall-tasks, wiki/changeset comment \u2192 parall-wiki |\n| `prll://ase_` | Agent session | parall-platform |\n| `prll://tsk_` | Task | parall-tasks |\n| `prll://prj_` | Project | parall-tasks |\n| `prll://sch_` | Schedule (time trigger) | parall-schedules |\n| `prll://srn_` | Schedule run (single fire audit record; carries fire-time snapshot) | parall-schedules |\n| `prll://xcn_` | External Trigger Connection (incoming endpoint) | parall-external-triggers |\n| `prll://xin_` | External Trigger Event (single incoming event audit record) | parall-external-triggers |\n| `prll://xtr_` | External Trigger (incoming trigger configuration) | parall-external-triggers |\n| `prll://xrn_` | External Trigger run (single matched dispatch audit record) | parall-external-triggers |\n| `prll://wik_` | Wiki | parall-wiki |\n| `prll://att_` | Attachment | parall-platform (files) |\n\nWhen a message or event references `prll://sch_xxx` or `prll://srn_xxx`, or when you receive `[Event: schedule.fired]`, switch to the **parall-schedules** skill for the CLI commands (create / list / pause / resume / cancel / runs).\n\nWhen 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).\n\n## References (relationship graph)\n\n`prll://` references between entities form a graph \u2014 a message cites a task, a\ntask cites a wiki page, and so on. Walk it to answer \"what is this decision /\nentity connected to\". All results are permission-filtered to what you can see.\n\n```bash\n# Resolve URIs to entity metadata (titles, status, previews)\nparall refs resolve prll://tsk_xxx prll://wik_xxx\n\n# Complete text for authorized message refs (default preview is 100 characters)\nparall refs resolve --full prll://msg_xxx\n\n# Single hop \u2014 who references X\nparall refs backlinks prll://tsk_xxx\n\n# Multi-hop \u2014 the connected sub-graph around X (entity-level URI only \u2014 no\n# path/anchor; depth 1\u20134, default 2)\nparall refs graph prll://tsk_xxx --depth 2\n```\n\n`refs graph` traverses both directions (inbound + outbound) and returns `nodes`\nand `edges` with each node's hop `depth`. `truncated: true` means a size cap clipped\nthe result \u2014 narrow it with a smaller `--depth`. Edges carry `context` \u2014 the\nauthor's annotation from `[context](prll://...)` \u2014 telling you *why* two\nentities are linked, not just that they are.\n\nThe graph returns bare node URIs (no titles). The usual two-step: `refs graph`\nfor topology, then batch-`refs resolve` the node URIs you care about for\ntitles/status. If graph rejects your URI with a path/anchor error, strip it to\nthe entity root (`prll://wik_xxx/docs/a.md` \u2192 `prll://wik_xxx`) and re-query \u2014\nbut note this WIDENS the query to the whole entity, not that one file: the\ngraph seeds from the wiki id, so a specific file's outbound links may sit\ndeeper in the result (or past the size caps). For refs pointing AT one file\n(inbound), `refs backlinks` on the full file URI is precise. There is no\nprecise query for one file's OUTBOUND edges today \u2014 the widened root graph is\nbest-effort for those, or read the file itself for its `prll://` links.\nWiki-file nodes inside a graph *result* do legitimately carry paths.\n\n`refs backlinks` items include a `snippet` of the referencing content \u2014 often\nenough to judge relevance without fetching the source entity.\n\nCLI 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 \u2014 read both.\n";
2
2
  //# sourceMappingURL=parall-platform.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parall-platform.d.ts","sourceRoot":"","sources":["../../src/skills/parall-platform.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,0ybAoSjC,CAAC"}
1
+ {"version":3,"file":"parall-platform.d.ts","sourceRoot":"","sources":["../../src/skills/parall-platform.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,qxdAyTjC,CAAC"}
@@ -135,7 +135,7 @@ parall dm prll://usr_xxx --text-file /tmp/reply.md
135
135
  parall dm "Alice" --text "Hello"
136
136
 
137
137
  # Thread reply
138
- parall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id 01JWC...
138
+ parall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id prll://msg_xxx
139
139
 
140
140
  # FYI message (no response expected — the recipient sees \`[Hint: no_reply]\`)
141
141
  parall messages send prll://cht_xxx --text "FYI: done" --no-reply
@@ -146,6 +146,27 @@ parall messages send prll://cht_xxx --text "FYI: done" --no-reply
146
146
  parall no-reply --reason "ack only, nothing to add"
147
147
  \`\`\`
148
148
 
149
+ ## Thread Subscriptions
150
+
151
+ Replying in a thread subscribes you to it: every later reply reaches you as
152
+ \`[Event: message.new]\` with a \`[Thread: ...]\` line, marked
153
+ \`[Delivery: watcher]\`. You are also auto-subscribed when a thread reply
154
+ @-mentions you personally, and when someone replies to a thread whose root
155
+ message you wrote. \`[Delivery: mention]\` means you were @-mentioned in that
156
+ message.
157
+
158
+ \`\`\`bash
159
+ parall messages watch prll://msg_xxx # follow a thread without replying (ID = the thread's root message)
160
+ parall messages unwatch prll://msg_xxx # stop receiving a thread's replies
161
+ parall messages watchers prll://msg_xxx # list who is watching a thread
162
+ \`\`\`
163
+
164
+ Unwatch is sticky: no automatic source (including later @-mentions — you
165
+ still get the one-off mention, just no subscription) re-subscribes you.
166
+ Only replying in the thread yourself or an explicit \`watch\` does. Unwatch
167
+ threads you have nothing more to contribute to instead of ignoring their
168
+ wake-ups.
169
+
149
170
  ## Files & Attachments
150
171
 
151
172
  Attachments appear in events as \`[Attachment: prll://att_xxx | mime | size | name]\`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/agent-core",
3
- "version": "1.55.4",
3
+ "version": "1.56.0",
4
4
  "description": "Shared agent runtime orchestration helpers for Parall",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "@opentelemetry/sdk-metrics": "^1.30.0",
37
37
  "@opentelemetry/sdk-trace-node": "^1.30.0",
38
38
  "undici": "^7.24.8",
39
- "@parall/sdk": "1.55.4"
39
+ "@parall/sdk": "1.56.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^22.0.0",
@@ -29,6 +29,17 @@ function sanitizeMeta(value: string): string {
29
29
  .trim();
30
30
  }
31
31
 
32
+ export function formatApprovalDecisionReason(
33
+ status: string | undefined,
34
+ reason: string | null | undefined,
35
+ ): string {
36
+ const normalized = reason?.trim();
37
+ if (status !== 'rejected' || !normalized) return '';
38
+ // JSON string encoding preserves the reviewer's text while preventing
39
+ // newlines or bracket markers from forging another structured event line.
40
+ return `Reason (JSON): ${JSON.stringify(normalized)}`;
41
+ }
42
+
32
43
  export function buildEventBody(event: ParallEvent): string {
33
44
  const lines: string[] = [];
34
45
  if (event.type === 'message') {
@@ -54,6 +65,7 @@ export function buildEventBody(event: ParallEvent): string {
54
65
  .join(' | ');
55
66
  lines.push(`[Thread: ${threadMeta}]`);
56
67
  }
68
+ if (event.deliveryReason) lines.push(`[Delivery: ${sanitizeMeta(event.deliveryReason)}]`);
57
69
  if (event.unreadCount != null && event.unreadCount > 1) {
58
70
  const countStr = event.unreadCount >= 1000 ? '999+' : String(event.unreadCount);
59
71
  const sinceStr = event.unreadSince ? ` | since: prll://${event.unreadSince}` : '';
@@ -28,6 +28,7 @@ import {
28
28
  buildEventBodyForForkResult,
29
29
  buildForkResultPrefix,
30
30
  buildForkScopePrefix,
31
+ formatApprovalDecisionReason,
31
32
  } from './event-format.js';
32
33
  import {
33
34
  CAPABILITY_SLACK_SEND,
@@ -2537,12 +2538,42 @@ export class ParallAgentGateway {
2537
2538
 
2538
2539
  if (!body && !hasAttachments) return { action: 'skip' };
2539
2540
 
2540
- if (chatInfo.type === 'group' && chatInfo.agentRoutingMode !== 'active') {
2541
- const mentions = content.mentions ?? [];
2542
- const isMentioned = mentions.some((mention) =>
2543
- mentionTargetsUser(mention.user_id, this.opts.agentUserId, 'agent'),
2544
- );
2545
- if (!isMentioned) return { action: 'skip' };
2541
+ const mentions = content.mentions ?? [];
2542
+ const isMentioned = mentions.some((mention) =>
2543
+ mentionTargetsUser(mention.user_id, this.opts.agentUserId, 'agent'),
2544
+ );
2545
+
2546
+ let deliveryReason: string | undefined;
2547
+ if (message.thread_root_id) {
2548
+ // Thread domain: admission mirrors the server's thread fan-out —
2549
+ // mention or thread watcher — never the chat's routing mode
2550
+ // (thread-subscription-design.md §1: routing modes govern top-level
2551
+ // messages only). The watch probe is a fresh authoritative read on
2552
+ // every call: this decision also runs on the WorkItem-terminating
2553
+ // redrive path, where a skip acks the row for good, so it must not
2554
+ // be answered from a cache (agent-reply-policy-design.md §4.2).
2555
+ if (isMentioned) {
2556
+ deliveryReason = 'mention';
2557
+ } else {
2558
+ let watching: boolean;
2559
+ try {
2560
+ watching = await this.opts.client.isWatchingThread(message.thread_root_id);
2561
+ } catch {
2562
+ // Uncertain — a skip here would terminally ack a watcher delivery
2563
+ // on the redrive path. Leave the WorkItem pending for retry.
2564
+ return { action: 'retry' };
2565
+ }
2566
+ if (!watching) return { action: 'skip' };
2567
+ deliveryReason = 'watcher';
2568
+ }
2569
+ } else if (chatInfo.type === 'group') {
2570
+ if (chatInfo.agentRoutingMode !== 'active' && !isMentioned) return { action: 'skip' };
2571
+ // Annotated whenever mentioned, in every routing mode: the server's
2572
+ // mention fan-out writes the WorkItem first (reason=mention), so parel
2573
+ // renders it — active mode must not diverge. Mode-admitted deliveries
2574
+ // stay bare. DM top-level stays bare too: D5 skips agent mention
2575
+ // fan-out there, so its WorkItem carries no reason on either family.
2576
+ if (isMentioned) deliveryReason = 'mention';
2546
2577
  }
2547
2578
 
2548
2579
  const attachments = (message.attachments ?? []).map((a) => ({
@@ -2593,6 +2624,7 @@ export class ParallAgentGateway {
2593
2624
  messageId: message.id,
2594
2625
  body: body || '[attachment]',
2595
2626
  threadRootId: message.thread_root_id ?? undefined,
2627
+ deliveryReason,
2596
2628
  noReply: message.hints?.no_reply ?? false,
2597
2629
  attachments: attachments.length > 0 ? attachments : undefined,
2598
2630
  sentAt: message.created_at,
@@ -3207,8 +3239,9 @@ export class ParallAgentGateway {
3207
3239
 
3208
3240
  const statusLabel = approval.status === 'approved' ? 'Approved' : 'Rejected';
3209
3241
  const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : '';
3210
- const body = `${statusLabel}: ${approval.title}${execInfo}`;
3211
-
3242
+ const reasonLine = formatApprovalDecisionReason(approval.status, approval.decision_reason);
3243
+ const reasonInfo = reasonLine ? `\n${reasonLine}` : '';
3244
+ const body = `${statusLabel}: ${approval.title}${execInfo}${reasonInfo}`;
3212
3245
  const event: ParallEvent = {
3213
3246
  type: 'approval',
3214
3247
  targetId: chatId ?? approval.chat_id,
@@ -23,7 +23,7 @@ export const SKILLS: SkillMeta[] = [
23
23
  {
24
24
  name: 'parall-platform',
25
25
  description:
26
- "Parall platform queries and lightweight agent provisioning: list org members, agents, chats, read message history, check identity, create another agent, or walk the prll:// reference graph (resolve URIs, backlinks, multi-hop graph). Use when: user asks about org members, who's online, chat history, agent list, creating an agent, identity/auth questions, or you need to find what an entity is connected to / who references it.",
26
+ "Parall platform queries and lightweight agent provisioning: list org members, agents, chats, read message history, watch/unwatch threads, check identity, create another agent, or walk the prll:// reference graph (resolve URIs, backlinks, multi-hop graph). Use when: user asks about org members, who's online, chat history, agent list, creating an agent, identity/auth questions, you want to follow or mute a thread (e.g. after `[Delivery: watcher]` wake-ups), or you need to find what an entity is connected to / who references it.",
27
27
  content: PARALL_PLATFORM_SKILL,
28
28
  },
29
29
  {
@@ -135,7 +135,7 @@ parall dm prll://usr_xxx --text-file /tmp/reply.md
135
135
  parall dm "Alice" --text "Hello"
136
136
 
137
137
  # Thread reply
138
- parall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id 01JWC...
138
+ parall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id prll://msg_xxx
139
139
 
140
140
  # FYI message (no response expected — the recipient sees \`[Hint: no_reply]\`)
141
141
  parall messages send prll://cht_xxx --text "FYI: done" --no-reply
@@ -146,6 +146,27 @@ parall messages send prll://cht_xxx --text "FYI: done" --no-reply
146
146
  parall no-reply --reason "ack only, nothing to add"
147
147
  \`\`\`
148
148
 
149
+ ## Thread Subscriptions
150
+
151
+ Replying in a thread subscribes you to it: every later reply reaches you as
152
+ \`[Event: message.new]\` with a \`[Thread: ...]\` line, marked
153
+ \`[Delivery: watcher]\`. You are also auto-subscribed when a thread reply
154
+ @-mentions you personally, and when someone replies to a thread whose root
155
+ message you wrote. \`[Delivery: mention]\` means you were @-mentioned in that
156
+ message.
157
+
158
+ \`\`\`bash
159
+ parall messages watch prll://msg_xxx # follow a thread without replying (ID = the thread's root message)
160
+ parall messages unwatch prll://msg_xxx # stop receiving a thread's replies
161
+ parall messages watchers prll://msg_xxx # list who is watching a thread
162
+ \`\`\`
163
+
164
+ Unwatch is sticky: no automatic source (including later @-mentions — you
165
+ still get the one-off mention, just no subscription) re-subscribes you.
166
+ Only replying in the thread yourself or an explicit \`watch\` does. Unwatch
167
+ threads you have nothing more to contribute to instead of ignoring their
168
+ wake-ups.
169
+
149
170
  ## Files & Attachments
150
171
 
151
172
  Attachments appear in events as \`[Attachment: prll://att_xxx | mime | size | name]\`.