@parall/parall 1.23.0 → 1.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/parall",
3
- "version": "1.23.0",
3
+ "version": "1.24.0",
4
4
  "description": "OpenClaw channel plugin for Parall IM",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -15,8 +15,8 @@
15
15
  "openclaw.plugin.json"
16
16
  ],
17
17
  "dependencies": {
18
- "@parall/sdk": "1.23.0",
19
- "@parall/agent-core": "1.23.0"
18
+ "@parall/agent-core": "1.24.0",
19
+ "@parall/sdk": "1.24.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.0.0",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: parall-platform
3
- description: "Parall platform queries: list org members, agents, chats, read message history, check identity. Use when: user asks about org members, who's online, chat history, agent list, or identity/auth questions."
3
+ description: "Parall platform queries and lightweight agent provisioning: list org members, agents, chats, read message history, check identity, or create another agent. Use when: user asks about org members, who's online, chat history, agent list, creating an agent, or identity/auth questions."
4
4
  ---
5
5
 
6
6
  # Parall Platform
@@ -10,22 +10,65 @@ Query organization data via the Parall CLI. Auth is pre-configured.
10
10
  ## Identity
11
11
 
12
12
  ```bash
13
- npx --yes @parall/cli@latest whoami
13
+ parall whoami
14
14
  ```
15
15
 
16
16
  ## Members & Agents
17
17
 
18
18
  ```bash
19
- npx --yes @parall/cli@latest members list # All org members (humans + agents)
20
- npx --yes @parall/cli@latest agents list # Agents only
21
- npx --yes @parall/cli@latest users get prll://usr_xxx # Get user details by ID
19
+ parall members list # All org members (humans + agents)
20
+ parall agents list # Agents only
21
+ parall users get prll://usr_xxx # Get user details by ID
22
+ ```
23
+
24
+ Create a hosted agent when the user asks for a Parall-managed runtime. Hosted
25
+ provisioning is asynchronous: creation means the agent identity, API key, and
26
+ machine record were accepted, not that the runtime is online yet. Use `--wait`
27
+ to wait until the machine reaches `running`, and use `--wait-online` when the
28
+ task requires the child agent to be connected before you report completion.
29
+ For hosted agents, use `--discard-api-key`; the server injects the one-time key
30
+ into the hosted runtime, so the parent agent must not print or persist it.
31
+
32
+ Create a self-hosted agent only when the runtime will be connected outside
33
+ Parall-managed compute. In that case, write the one-time `api_key` to
34
+ `--api-key-file` so it is not captured in tool-result logs. Treat `api_key` as a
35
+ secret: do not print, read aloud, post it in shared chats, or echo the file
36
+ contents. Include the `user.id` in normal responses, and pass the key file only
37
+ through an explicit secure runtime handoff when connection is required. Never
38
+ use `--show-api-key` from an agent runtime. Agent callers cannot set provider
39
+ overrides until the dedicated fine-grained permission flow lands.
40
+
41
+ ```bash
42
+ # Hosted runtime (Parall-managed compute)
43
+ parall agents create \
44
+ --name "Research Agent" \
45
+ --runtime-type codex \
46
+ --machine-type cloud \
47
+ --machine-label standard \
48
+ --discard-api-key \
49
+ --wait \
50
+ --wait-online
51
+
52
+ # Self-hosted runtime
53
+ parall agents create --name "Research Agent" --runtime-type codex --api-key-file /tmp/research-agent.api-key
54
+ ```
55
+
56
+ Inspect hosted provisioning directly when a create command returns before the
57
+ runtime is online, or when you need logs for a failed machine. If `agents create`
58
+ exits non-zero after creating a hosted agent, read the printed `user.id` and
59
+ `machine.id`, then use these commands to decide whether to wait, inspect logs,
60
+ or report the failed machine for retry.
61
+
62
+ ```bash
63
+ parall machines status prll://mch_xxx
64
+ parall machines logs prll://mch_xxx --lines 100
22
65
  ```
23
66
 
24
67
  ## Chats & Messages
25
68
 
26
69
  ```bash
27
- npx --yes @parall/cli@latest chats list # List all chats
28
- npx --yes @parall/cli@latest messages list prll://cht_xxx # Read chat message history
70
+ parall chats list # List all chats
71
+ parall messages list prll://cht_xxx # Read chat message history
29
72
  ```
30
73
 
31
74
  ## Sending Messages
@@ -34,22 +77,22 @@ Each `[Event: message.new]` includes `[Chat: ... (prll://cht_xxx)]` — use that
34
77
 
35
78
  ```bash
36
79
  # Reply to a chat (use the chat URI from the event)
37
- npx --yes @parall/cli@latest messages send prll://cht_xxx --text "Your reply"
80
+ parall messages send prll://cht_xxx --text "Your reply"
38
81
 
39
82
  # Direct message by user URI or display name
40
- npx --yes @parall/cli@latest dm prll://usr_xxx --text "Hello"
41
- npx --yes @parall/cli@latest dm "Alice" --text "Hello"
83
+ parall dm prll://usr_xxx --text "Hello"
84
+ parall dm "Alice" --text "Hello"
42
85
 
43
86
  # Thread reply
44
- npx --yes @parall/cli@latest messages send prll://cht_xxx --text "Reply" --thread-root-id 01JWC...
87
+ parall messages send prll://cht_xxx --text "Reply" --thread-root-id 01JWC...
45
88
 
46
89
  # FYI message (no response expected — the recipient sees `[Hint: no_reply]`)
47
- npx --yes @parall/cli@latest messages send prll://cht_xxx --text "FYI: done" --no-reply
90
+ parall messages send prll://cht_xxx --text "FYI: done" --no-reply
48
91
 
49
92
  # Silence this turn entirely — no chat message produced. Use when you receive
50
93
  # `[Hint: no_reply]` or otherwise decide the turn needs no visible reply.
51
94
  # Run BEFORE any `messages send` / `dm`; those still deliver real messages.
52
- npx --yes @parall/cli@latest no-reply --reason "ack only, nothing to add"
95
+ parall no-reply --reason "ack only, nothing to add"
53
96
  ```
54
97
 
55
98
  ## Files & Attachments
@@ -58,19 +101,19 @@ Attachments appear in events as `[Attachment: prll://att_xxx | mime | size | nam
58
101
 
59
102
  ```bash
60
103
  # Download an attachment
61
- npx --yes @parall/cli@latest files download att_xxx --output /tmp/file.png
104
+ parall files download att_xxx --output /tmp/file.png
62
105
 
63
106
  # Upload a file (returns attachment_id)
64
- npx --yes @parall/cli@latest files upload /tmp/report.pdf
107
+ parall files upload /tmp/report.pdf
65
108
 
66
109
  # Send a message with a file
67
- npx --yes @parall/cli@latest messages send prll://cht_xxx --file /tmp/output.png --text "Done"
110
+ parall messages send prll://cht_xxx --file /tmp/output.png --text "Done"
68
111
 
69
112
  # Send an existing attachment to another chat
70
- npx --yes @parall/cli@latest messages send prll://cht_xxx --attachment att_xxx --text "See attached"
113
+ parall messages send prll://cht_xxx --attachment att_xxx --text "See attached"
71
114
 
72
115
  # DM with a file
73
- npx --yes @parall/cli@latest dm "Alice" --file /tmp/report.pdf --text "Report attached"
116
+ parall dm "Alice" --file /tmp/report.pdf --text "Report attached"
74
117
  ```
75
118
 
76
119
  `--file` and `--attachment` are mutually exclusive. `--text` can be combined with either.
@@ -17,50 +17,50 @@ Three spec types — pick exactly one:
17
17
 
18
18
  ```bash
19
19
  # Recurring cron (weekdays 10am New York)
20
- npx --yes @parall/cli@latest schedules create \
20
+ parall schedules create \
21
21
  --name "Daily standup" \
22
22
  --description "Ask the team for their plan today; see prll://wik_xxx for the standup template" \
23
- --target-id prll://usr_xxx \
23
+ --target-ids prll://usr_xxx \
24
24
  --cron-expr "0 10 * * 1-5" \
25
25
  --timezone America/New_York \
26
26
  --attached-to-uri prll://cht_xxx
27
27
 
28
28
  # Every 30 minutes
29
- npx --yes @parall/cli@latest schedules create \
29
+ parall schedules create \
30
30
  --name "CI watch" \
31
31
  --description "Check the deploy status and flag failures" \
32
- --target-id prll://usr_xxx \
32
+ --target-ids prll://usr_xxx \
33
33
  --interval-seconds 1800
34
34
 
35
35
  # One-shot at a future RFC3339 time
36
- npx --yes @parall/cli@latest schedules create \
36
+ parall schedules create \
37
37
  --name "Followup" \
38
38
  --description "Remind the user about the PR review if still pending" \
39
- --target-id prll://usr_xxx \
39
+ --target-ids prll://usr_xxx \
40
40
  --run-at 2026-04-19T15:00:00Z
41
41
  ```
42
42
 
43
- `--target-id` is who receives the fire (usually yourself when you're self-scheduling; another agent or human when delegating). `--attached-to-uri` optionally anchors the schedule to a task / chat / project / wiki page — when that resource is archived or deleted, the schedule auto-cancels (`cancel_reason=attached_gone`).
43
+ `--target-ids` is who receives the fire (usually yourself when you're self-scheduling; another agent or human when delegating). `--attached-to-uri` optionally anchors the schedule to a task / chat / project / wiki page — when that resource is archived or deleted, the schedule auto-cancels (`cancel_reason=attached_gone`).
44
44
 
45
45
  ## Listing / inspecting
46
46
 
47
47
  ```bash
48
- npx --yes @parall/cli@latest schedules list --status active,paused
49
- npx --yes @parall/cli@latest schedules list --attached-to prll://tsk_xxx
50
- npx --yes @parall/cli@latest schedules list --target-id prll://usr_xxx
51
- npx --yes @parall/cli@latest schedules get prll://sch_xxx
52
- npx --yes @parall/cli@latest schedules runs prll://sch_xxx # fire history
53
- npx --yes @parall/cli@latest schedules run prll://srn_xxx # single run incl. fire-time snapshot
48
+ parall schedules list --status active,paused
49
+ parall schedules list --attached-to prll://tsk_xxx
50
+ parall schedules list --attendee-id prll://usr_xxx
51
+ parall schedules get prll://sch_xxx
52
+ parall schedules runs prll://sch_xxx # fire history
53
+ parall schedules run prll://srn_xxx # single run incl. fire-time snapshot
54
54
  ```
55
55
 
56
56
  ## Lifecycle
57
57
 
58
58
  ```bash
59
- npx --yes @parall/cli@latest schedules update prll://sch_xxx --description "New prompt"
60
- npx --yes @parall/cli@latest schedules pause prll://sch_xxx # reversible
61
- npx --yes @parall/cli@latest schedules resume prll://sch_xxx # does NOT catch up missed slots
62
- npx --yes @parall/cli@latest schedules cancel prll://sch_xxx # terminal; row + runs preserved, prll://sch_ ref stays valid
63
- npx --yes @parall/cli@latest schedules delete prll://sch_xxx # hard-delete; requires status=cancelled AND run_count=0. Once a schedule has fired, it is permanently undeletable (409 SCHEDULE_HAS_RUNS) — cancel it and leave the audit trail. Delete is for never-fired test/accidental schedules only.
59
+ parall schedules update prll://sch_xxx --description "New prompt"
60
+ parall schedules pause prll://sch_xxx # reversible
61
+ parall schedules resume prll://sch_xxx # does NOT catch up missed slots
62
+ parall schedules cancel prll://sch_xxx # terminal; row + runs preserved, prll://sch_ ref stays valid
63
+ parall schedules delete prll://sch_xxx # hard-delete; requires status=cancelled AND run_count=0. Once a schedule has fired, it is permanently undeletable (409 SCHEDULE_HAS_RUNS) — cancel it and leave the audit trail. Delete is for never-fired test/accidental schedules only.
64
64
  ```
65
65
 
66
66
  `spec_type` cannot be changed via update — if you need to switch between cron / interval / one_shot, cancel the old one and create a new schedule.
@@ -79,6 +79,6 @@ Your job is to interpret the description and act:
79
79
 
80
80
  Do not treat schedule fires as "tasks assigned to you" — there's no status to transition, no acknowledgment required. If the work warrants a task (multi-step, needs tracking), create one from within the response.
81
81
 
82
- **Fetching the run explicitly** (optional): `schedules run prll://srn_xxx` returns the same snapshot plus `dispatch_event_id` / `inbox_item_id` for audit. If you call it and get 404 (because the schedule was cancelled or its target/attachment changed after the fire), drop the request and continue — don't retry.
82
+ **Fetching the run explicitly** (optional): `schedules run prll://srn_xxx` returns the same snapshot plus delivery records (reverse-lookable via `source_id=srn_xxx`) for audit. If you call it and get 404 (because the schedule was cancelled or its target/attachment changed after the fire), drop the request and continue — don't retry.
83
83
 
84
84
  CLI command results are JSON on stdout; mutation commands may emit auxiliary hints on stderr (for example, `Created: prll://sch_xxx`).
@@ -11,25 +11,25 @@ Manage tasks and projects via the Parall CLI. Auth and runtime context are pre-c
11
11
 
12
12
  ```bash
13
13
  # List tasks (filterable by status)
14
- npx @parall/cli@latest tasks list
15
- npx @parall/cli@latest tasks list --status todo
16
- npx @parall/cli@latest tasks list --status in_progress
14
+ parall tasks list
15
+ parall tasks list --status todo
16
+ parall tasks list --status in_progress
17
17
 
18
18
  # Create a task
19
- npx @parall/cli@latest tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--project-id prll://prj_xxx]
19
+ parall tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--project-id prll://prj_xxx]
20
20
 
21
21
  # Update task status
22
- npx @parall/cli@latest tasks update prll://tsk_xxx --status in_progress
23
- npx @parall/cli@latest tasks update prll://tsk_xxx --status done
22
+ parall tasks update prll://tsk_xxx --status in_progress
23
+ parall tasks update prll://tsk_xxx --status done
24
24
 
25
25
  # Add a comment
26
- npx @parall/cli@latest tasks comments add prll://tsk_xxx --body "Progress update..."
26
+ parall tasks comments add prll://tsk_xxx --body "Progress update..."
27
27
  ```
28
28
 
29
29
  ## Project Commands
30
30
 
31
31
  ```bash
32
- npx @parall/cli@latest projects list
32
+ parall projects list
33
33
  ```
34
34
 
35
35
  ## Watching Tasks
@@ -38,13 +38,13 @@ Subscribe to a task to receive notifications when others comment on it.
38
38
 
39
39
  ```bash
40
40
  # Watch a task (you'll receive dispatch events for new comments)
41
- npx @parall/cli@latest tasks watch prll://tsk_xxx
41
+ parall tasks watch prll://tsk_xxx
42
42
 
43
43
  # Unwatch
44
- npx @parall/cli@latest tasks unwatch prll://tsk_xxx
44
+ parall tasks unwatch prll://tsk_xxx
45
45
 
46
46
  # List who is watching
47
- npx @parall/cli@latest tasks watchers prll://tsk_xxx
47
+ parall tasks watchers prll://tsk_xxx
48
48
  ```
49
49
 
50
50
  Note: task creators are automatically watching their tasks.
@@ -10,9 +10,9 @@ Manage organization wikis via the Parall CLI. Auth is pre-configured.
10
10
  ## Browsing (no local state needed)
11
11
 
12
12
  ```bash
13
- npx @parall/cli@latest wiki list # List all wikis
14
- npx @parall/cli@latest wiki tree <slug> # List files and directories
15
- npx @parall/cli@latest wiki tree <slug> --path docs/ # List a subdirectory
13
+ parall wiki list # List all wikis
14
+ parall wiki tree <slug> # List files and directories
15
+ parall wiki tree <slug> --path docs/ # List a subdirectory
16
16
  ```
17
17
 
18
18
  ## Editing
@@ -24,7 +24,7 @@ a changeset.
24
24
  ### Step 1: Sync
25
25
 
26
26
  ```bash
27
- npx @parall/cli@latest wiki sync <slug>
27
+ parall wiki sync <slug>
28
28
  ```
29
29
 
30
30
  This downloads wiki files to a local directory. The output includes the
@@ -36,7 +36,7 @@ The sync output JSON contains `synced[].path` — the absolute path where files
36
36
  live. You can also check it anytime with:
37
37
 
38
38
  ```bash
39
- npx @parall/cli@latest wiki status <slug>
39
+ parall wiki status <slug>
40
40
  ```
41
41
 
42
42
  The output includes `Mount: /absolute/path/to/<slug>`.
@@ -52,8 +52,8 @@ Use standard file tools (`read`, `write`, `edit`) on files under the mount path.
52
52
  ### Step 4: Review changes
53
53
 
54
54
  ```bash
55
- npx @parall/cli@latest wiki diff <slug> # Shows unified diff of all local changes
56
- npx @parall/cli@latest wiki status <slug> # Shows which files changed with +/- line counts
55
+ parall wiki diff <slug> # Shows unified diff of all local changes
56
+ parall wiki status <slug> # Shows which files changed with +/- line counts
57
57
  ```
58
58
 
59
59
  Always review before proposing.
@@ -61,7 +61,7 @@ Always review before proposing.
61
61
  ### Step 5: Propose changeset
62
62
 
63
63
  ```bash
64
- npx @parall/cli@latest wiki changeset create <slug> --title "Description of changes"
64
+ parall wiki changeset create <slug> --title "Description of changes"
65
65
  ```
66
66
 
67
67
  This uploads your local changes. Unprotected paths auto-merge immediately; the
@@ -71,15 +71,15 @@ paths remain as a changeset for review; follow the returned `next_action`.
71
71
  ## Changeset Management
72
72
 
73
73
  ```bash
74
- npx @parall/cli@latest wiki changeset list <slug> # List all changesets
75
- npx @parall/cli@latest wiki changeset show <changesetId> <slug> # Show detail + feedback
76
- npx @parall/cli@latest wiki changeset diff <changesetId> <slug> # Show changeset diff
74
+ parall wiki changeset list <slug> # List all changesets
75
+ parall wiki changeset show <changesetId> <slug> # Show detail + feedback
76
+ parall wiki changeset diff <changesetId> <slug> # Show changeset diff
77
77
  ```
78
78
 
79
79
  If a changeset is rejected, fix the files locally and re-propose:
80
80
 
81
81
  ```bash
82
- npx @parall/cli@latest wiki changeset create <slug> --update <changesetId>
82
+ parall wiki changeset create <slug> --update <changesetId>
83
83
  ```
84
84
 
85
85
  The title is inherited from the original changeset — no need to repeat it.
@@ -100,21 +100,21 @@ stderr prints one line per conflict. Recovery:
100
100
  **Accept upstream** (drop your edit):
101
101
  ```bash
102
102
  cp .parall-wiki/conflicts/<path>.remote <path>
103
- npx @parall/cli@latest wiki sync
103
+ parall wiki sync
104
104
  ```
105
105
 
106
106
  **Keep yours** (or hand-merge, then propose):
107
107
  ```bash
108
108
  # edit <path> to final content
109
- npx @parall/cli@latest wiki diff <slug>
110
- npx @parall/cli@latest wiki changeset create <slug> --title "Reconcile <path>"
111
- npx @parall/cli@latest wiki sync # fast-forwards after server merges
109
+ parall wiki diff <slug>
110
+ parall wiki changeset create <slug> --title "Reconcile <path>"
111
+ parall wiki sync # fast-forwards after server merges
112
112
  ```
113
113
 
114
114
  **Accept server delete** (`.remote-deleted` only):
115
115
  ```bash
116
116
  rm <path>
117
- npx @parall/cli@latest wiki sync
117
+ parall wiki sync
118
118
  ```
119
119
 
120
120
  Sync failures (`download`, `shape-conflict`) set exit code 1 and retry on
@@ -123,13 +123,13 @@ next sync. Conflicts exit 0 — they need your decision, not a retry.
123
123
  ## Discarding local changes
124
124
 
125
125
  ```bash
126
- npx @parall/cli@latest wiki reset <slug> # Restore all files to last synced state
126
+ parall wiki reset <slug> # Restore all files to last synced state
127
127
  ```
128
128
 
129
129
  ## History
130
130
 
131
131
  ```bash
132
- npx @parall/cli@latest wiki log <slug> # Recent wiki operations
132
+ parall wiki log <slug> # Recent wiki operations
133
133
  ```
134
134
 
135
- All CLI output is JSON. Run `npx @parall/cli@latest wiki --help` for full options.
135
+ All CLI output is JSON. Run `parall wiki --help` for full options.
package/src/gateway.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  type ParallEvent,
11
11
  type RuntimeEvent,
12
12
  } from "@parall/agent-core";
13
- import { ParallClient, ParallWs } from "@parall/sdk";
13
+ import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
14
14
  import type { ParallChannelConfig } from "./types.js";
15
15
  import * as crypto from "node:crypto";
16
16
  import * as os from "node:os";
@@ -36,6 +36,18 @@ function resolveWsUrl(account: ResolvedParallAccount): string {
36
36
  return `${wsBase}/ws`;
37
37
  }
38
38
 
39
+ async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string) {
40
+ try {
41
+ return await client.getAgentMe(orgId);
42
+ } catch (err) {
43
+ if (!(err instanceof ApiError && err.status === 404)) {
44
+ throw err;
45
+ }
46
+ const user = await client.getMe();
47
+ return { ...user, agent_profile: null };
48
+ }
49
+ }
50
+
39
51
  function buildInboundCtx(
40
52
  core: PluginRuntime,
41
53
  accountId: string,
@@ -236,9 +248,13 @@ export const parallGateway: ChannelGatewayAdapter<ResolvedParallAccount> = {
236
248
  swimlaneName: process.env.PRLL_SWIMLANE_NAME,
237
249
  });
238
250
 
239
- const me = await client.getMe();
251
+ const me = await getAgentMeWithLegacyFallback(client, config.org_id);
240
252
  const agentUserId = me.id;
241
- setAgentIdentity({ userId: agentUserId, displayName: me.display_name });
253
+ setAgentIdentity({
254
+ userId: agentUserId,
255
+ displayName: me.display_name,
256
+ description: me.agent_profile?.description ?? undefined,
257
+ });
242
258
  log?.info(`parall[${ctx.accountId}]: authenticated as ${me.display_name} (${agentUserId})`);
243
259
 
244
260
  const stateDir = process.env.OPENCLAW_STATE_DIR
package/src/hooks.ts CHANGED
@@ -36,13 +36,13 @@ const PRLL_CHANNEL_CONTEXT = `## Parall Channel — Message Delivery
36
36
 
37
37
  Events from the Parall channel arrive as \`[Event: ...]\` blocks.
38
38
  Your text output is **not delivered to the user** — it is discarded silently.
39
- To respond, you **must** use the Parall CLI via the exec (Bash) tool.
39
+ To respond, you **must** use the Parall CLI via the exec (Bash) tool. If \`parall\` is not on PATH, use \`npx --yes @parall/cli@latest\` instead.
40
40
 
41
41
  ### Event types and where to reply
42
42
 
43
43
  - **\`[Event: message.new]\`** — includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat:
44
44
 
45
- npx --yes @parall/cli@latest messages send prll://cht_xxx --text "Your reply here"
45
+ parall messages send prll://cht_xxx --text "Your reply here"
46
46
 
47
47
  - **\`[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.
48
48
 
@@ -55,7 +55,7 @@ A dispatch may contain multiple events — only skip replies to the hinted ones.
55
55
 
56
56
  To silence a turn without running any \`messages send\` / \`dm\`, call:
57
57
 
58
- npx --yes @parall/cli@latest no-reply [--reason "..."]
58
+ parall no-reply [--reason "..."]
59
59
 
60
60
  Do NOT emit polite acknowledgements like "No response needed" via \`messages send\` — they become real messages and start agent-to-agent loops.`;
61
61
 
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { spawn, spawnSync } from "node:child_process";
2
2
  import path from "node:path";
3
3
 
4
4
  type Logger = {
@@ -29,6 +29,19 @@ function isCommandMissing(error: unknown): boolean {
29
29
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
30
30
  }
31
31
 
32
+ // Prefer bare `parall` (globally installed in hosted images). Fall back to
33
+ // npx for self-hosted environments that haven't installed @parall/cli yet.
34
+ let _cli: { cmd: string; prefix: string[] } | undefined;
35
+ function resolveParallCli(): { cmd: string; prefix: string[] } {
36
+ if (!_cli) {
37
+ const r = spawnSync("parall", ["--version"], { stdio: "ignore", timeout: 5_000 });
38
+ _cli = r.error
39
+ ? { cmd: "npx", prefix: ["--yes", "@parall/cli@latest"] }
40
+ : { cmd: "parall", prefix: [] };
41
+ }
42
+ return _cli;
43
+ }
44
+
32
45
  function resolveMountRoot(stateDir: string): string {
33
46
  return process.env.PRLL_WIKI_MOUNT_ROOT?.trim() || path.join(stateDir, "workspace");
34
47
  }
@@ -58,7 +71,8 @@ async function runWikiSync(params: StartWikiHelperParams, env: NodeJS.ProcessEnv
58
71
  let timedOut = false;
59
72
  let settled = false;
60
73
 
61
- const child = spawn("npx", ["--yes", "@parall/cli@latest", "wiki", "sync"], {
74
+ const { cmd, prefix } = resolveParallCli();
75
+ const child = spawn(cmd, [...prefix, "wiki", "sync"], {
62
76
  env,
63
77
  stdio: "ignore",
64
78
  });
@@ -121,7 +135,8 @@ export async function startWikiHelper(params: StartWikiHelperParams): Promise<Wi
121
135
 
122
136
  const intervalSec = resolveWatchIntervalSec();
123
137
  let stopped = false;
124
- const watch = spawn("npx", ["--yes", "@parall/cli@latest", "wiki", "watch", "--interval-sec", String(intervalSec)], {
138
+ const { cmd, prefix } = resolveParallCli();
139
+ const watch = spawn(cmd, [...prefix, "wiki", "watch", "--interval-sec", String(intervalSec)], {
125
140
  env,
126
141
  stdio: "ignore",
127
142
  });