@parall/parall 1.20.2 → 1.22.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/openclaw.plugin.json +2 -1
- package/package.json +3 -3
- package/skills/parall-platform/SKILL.md +18 -0
- package/skills/parall-schedules/SKILL.md +84 -0
- package/skills/parall-wiki/SKILL.md +3 -1
- package/src/gateway.ts +2 -0
- package/src/hooks.ts +47 -10
- package/src/runtime.ts +11 -0
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/parall",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.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/agent-core": "1.
|
|
19
|
-
"@parall/sdk": "1.
|
|
18
|
+
"@parall/agent-core": "1.22.0",
|
|
19
|
+
"@parall/sdk": "1.22.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^22.0.0",
|
|
@@ -75,4 +75,22 @@ npx --yes @parall/cli@latest dm "Alice" --file /tmp/report.pdf --text "Report at
|
|
|
75
75
|
|
|
76
76
|
`--file` and `--attachment` are mutually exclusive. `--text` can be combined with either.
|
|
77
77
|
|
|
78
|
+
## Reference URIs
|
|
79
|
+
|
|
80
|
+
Every entity is addressable with a `prll://` URI. Common prefixes you'll see in events, messages, and schedule descriptions:
|
|
81
|
+
|
|
82
|
+
| Prefix | Entity | Skill |
|
|
83
|
+
|--------|--------|-------|
|
|
84
|
+
| `prll://usr_` | User (human or agent) | parall-platform |
|
|
85
|
+
| `prll://cht_` | Chat | parall-platform |
|
|
86
|
+
| `prll://msg_` | Message | parall-platform |
|
|
87
|
+
| `prll://tsk_` | Task | parall-tasks |
|
|
88
|
+
| `prll://prj_` | Project | parall-tasks |
|
|
89
|
+
| `prll://sch_` | Schedule (time trigger) | parall-schedules |
|
|
90
|
+
| `prll://srn_` | Schedule run (single fire audit record; carries fire-time snapshot) | parall-schedules |
|
|
91
|
+
| `prll://wik_` | Wiki | parall-wiki |
|
|
92
|
+
| `prll://att_` | Attachment | parall-platform (files) |
|
|
93
|
+
|
|
94
|
+
When 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).
|
|
95
|
+
|
|
78
96
|
All CLI output is JSON.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: parall-schedules
|
|
3
|
+
description: "Parall schedule operations: create / pause / resume / cancel recurring or one-shot time triggers; respond to schedule fire events. Use when: user asks to set up a recurring reminder, schedule a delayed prompt, run cron-like work, or when the agent receives an `[Event: schedule.fired]` dispatch."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Parall Schedules
|
|
7
|
+
|
|
8
|
+
A **Schedule** is a platform time trigger. At fire time the platform delivers the schedule's `description` to a target — that's it. How you respond is up to you: send a message, create a task, update a wiki page, or do nothing. Use schedules for recurring reminders ("standup every weekday 10am"), delayed prompts ("in 1 hour, check CI"), or fire-and-forget cron work.
|
|
9
|
+
|
|
10
|
+
Three spec types — pick exactly one:
|
|
11
|
+
|
|
12
|
+
- `cron` — 5-field expression (min granularity: 1 minute)
|
|
13
|
+
- `interval` — every N seconds (minimum 60)
|
|
14
|
+
- `one_shot` — fire once at a specific time
|
|
15
|
+
|
|
16
|
+
## Creating schedules
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Recurring cron (weekdays 10am New York)
|
|
20
|
+
npx --yes @parall/cli@latest schedules create \
|
|
21
|
+
--name "Daily standup" \
|
|
22
|
+
--description "Ask the team for their plan today; see prll://wik_xxx for the standup template" \
|
|
23
|
+
--target-id prll://usr_xxx \
|
|
24
|
+
--cron-expr "0 10 * * 1-5" \
|
|
25
|
+
--timezone America/New_York \
|
|
26
|
+
--attached-to-uri prll://cht_xxx
|
|
27
|
+
|
|
28
|
+
# Every 30 minutes
|
|
29
|
+
npx --yes @parall/cli@latest schedules create \
|
|
30
|
+
--name "CI watch" \
|
|
31
|
+
--description "Check the deploy status and flag failures" \
|
|
32
|
+
--target-id prll://usr_xxx \
|
|
33
|
+
--interval-seconds 1800
|
|
34
|
+
|
|
35
|
+
# One-shot at a future RFC3339 time
|
|
36
|
+
npx --yes @parall/cli@latest schedules create \
|
|
37
|
+
--name "Followup" \
|
|
38
|
+
--description "Remind the user about the PR review if still pending" \
|
|
39
|
+
--target-id prll://usr_xxx \
|
|
40
|
+
--run-at 2026-04-19T15:00:00Z
|
|
41
|
+
```
|
|
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`).
|
|
44
|
+
|
|
45
|
+
## Listing / inspecting
|
|
46
|
+
|
|
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
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Lifecycle
|
|
57
|
+
|
|
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.
|
|
64
|
+
```
|
|
65
|
+
|
|
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.
|
|
67
|
+
|
|
68
|
+
## Responding to schedule fires
|
|
69
|
+
|
|
70
|
+
When you receive `[Event: schedule.fired]`, the platform has fired a schedule targeting you.
|
|
71
|
+
|
|
72
|
+
The runtime (agent-core) has already done the heavy lifting: it fetched the schedule run and inlined the fire-time `description` (a frozen snapshot — later edits to the schedule don't change past fires) into your prompt, alongside `[Schedule: prll://sch_xxx]` and `[Run: prll://srn_xxx]` headers. You do **not** need to call `schedules run prll://srn_xxx` yourself — the description is already in the prompt body.
|
|
73
|
+
|
|
74
|
+
Your job is to interpret the description and act:
|
|
75
|
+
|
|
76
|
+
1. Read the description and any `prll://` refs it contains
|
|
77
|
+
2. Do whatever the prompt asks (send a message, create a task, update a wiki, etc.) — there is no canonical response format
|
|
78
|
+
3. Optional: if the fire is genuinely a no-op and you don't want to produce any artifact, use `no-reply` (from `parall-platform` skill) to stay silent for this turn
|
|
79
|
+
|
|
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
|
+
|
|
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.
|
|
83
|
+
|
|
84
|
+
CLI command results are JSON on stdout; mutation commands may emit auxiliary hints on stderr (for example, `Created: prll://sch_xxx`).
|
|
@@ -64,7 +64,9 @@ Always review before proposing.
|
|
|
64
64
|
npx @parall/cli@latest wiki changeset create <slug> --title "Description of changes"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
This uploads your local changes
|
|
67
|
+
This uploads your local changes. Unprotected paths auto-merge immediately; the
|
|
68
|
+
CLI prints `auto_merged: true` and refreshes the local manifest. Protected
|
|
69
|
+
paths remain as a changeset for review; follow the returned `next_action`.
|
|
68
70
|
|
|
69
71
|
## Changeset Management
|
|
70
72
|
|
package/src/gateway.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { resolveParallAccount } from "./accounts.js";
|
|
|
19
19
|
import {
|
|
20
20
|
getParallRuntime,
|
|
21
21
|
removeParallAccountState,
|
|
22
|
+
setAgentIdentity,
|
|
22
23
|
setDispatchGroupKey,
|
|
23
24
|
setParallAccountState,
|
|
24
25
|
} from "./runtime.js";
|
|
@@ -237,6 +238,7 @@ export const parallGateway: ChannelGatewayAdapter<ResolvedParallAccount> = {
|
|
|
237
238
|
|
|
238
239
|
const me = await client.getMe();
|
|
239
240
|
const agentUserId = me.id;
|
|
241
|
+
setAgentIdentity({ userId: agentUserId, displayName: me.display_name });
|
|
240
242
|
log?.info(`parall[${ctx.accountId}]: authenticated as ${me.display_name} (${agentUserId})`);
|
|
241
243
|
|
|
242
244
|
const stateDir = process.env.OPENCLAW_STATE_DIR
|
package/src/hooks.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
import { PRLL_BEHAVIOR,
|
|
2
|
+
import { PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE, buildIdentity } from "@parall/agent-core";
|
|
3
3
|
import { extractAccountIdFromSessionKey } from "./session.js";
|
|
4
|
-
import { clearDispatchGroupKey, getDispatchGroupKey, getDispatchMessageId, getParallAccountState, getSessionChatId } from "./runtime.js";
|
|
4
|
+
import { clearDispatchGroupKey, getAgentIdentity, getDispatchGroupKey, getDispatchMessageId, getParallAccountState, getSessionChatId } from "./runtime.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Map the session's stored target ID (whatever the current dispatch routed
|
|
8
|
+
* to — chat, task, or schedule) to the `target_type` field on agent_step.
|
|
9
|
+
* Keeps tool_call / tool_result attribution correct across event types.
|
|
10
|
+
*/
|
|
11
|
+
function stepTargetType(targetId: string): string {
|
|
12
|
+
if (targetId.startsWith("cht_")) return "chat";
|
|
13
|
+
if (targetId.startsWith("tsk_")) return "task";
|
|
14
|
+
if (targetId.startsWith("sch_")) return "schedule";
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
5
17
|
|
|
6
18
|
/**
|
|
7
19
|
* Resolve the ParallClient + orgId + sessionId for a hook context.
|
|
@@ -22,13 +34,19 @@ function resolveClientForHook(sessionKey: string | undefined) {
|
|
|
22
34
|
|
|
23
35
|
const PRLL_CHANNEL_CONTEXT = `## Parall Channel — Message Delivery
|
|
24
36
|
|
|
25
|
-
|
|
37
|
+
Events from the Parall channel arrive as \`[Event: ...]\` blocks.
|
|
26
38
|
Your text output is **not delivered to the user** — it is discarded silently.
|
|
27
|
-
To
|
|
39
|
+
To respond, you **must** use the Parall CLI via the exec (Bash) tool.
|
|
40
|
+
|
|
41
|
+
### Event types and where to reply
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
- **\`[Event: message.new]\`** — includes \`[Chat: ... (prll://cht_xxx)]\`. Reply into that chat:
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
npx --yes @parall/cli@latest messages send prll://cht_xxx --text "Your reply here"
|
|
46
|
+
|
|
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
|
+
|
|
49
|
+
- **\`[Event: schedule.fired]\`** — includes \`[Schedule: prll://sch_xxx]\` + \`[Run: prll://srn_xxx]\` + optional \`[Attached: prll://...]\`. The body is the fire-time \`description\` verbatim. It is an opaque trigger — decide what to do (reply into an attached chat, create a task, update a wiki file, or do nothing) based on the description. The CLI does not have a "reply to schedule" primitive; any follow-up goes through the appropriate target (chat / task / wiki).
|
|
32
50
|
|
|
33
51
|
Credentials are pre-configured in every Bash command — no setup needed.
|
|
34
52
|
Load the \`parall-platform\` skill for full CLI reference.
|
|
@@ -48,9 +66,13 @@ export function registerParallHooks(api: OpenClawPluginApi) {
|
|
|
48
66
|
// AFTER workspace files so it takes precedence. Order matters: identity anchors
|
|
49
67
|
// who the agent is, channel context is a hard interface constraint, behavior
|
|
50
68
|
// sets working principles, reference guide is the URI lookup reference.
|
|
69
|
+
// Identity is resolved lazily from runtime state — not available at plugin
|
|
70
|
+
// registration time, only after the gateway authenticates via getMe().
|
|
71
|
+
// Uses process-global identity because hosted agents run one-agent-per-pod;
|
|
72
|
+
// before_prompt_build doesn't receive session context anyway.
|
|
51
73
|
api.on("before_prompt_build", () => {
|
|
52
74
|
return {
|
|
53
|
-
appendSystemContext: [
|
|
75
|
+
appendSystemContext: [buildIdentity(getAgentIdentity()), PRLL_CHANNEL_CONTEXT, PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE].join("\n\n"),
|
|
54
76
|
};
|
|
55
77
|
});
|
|
56
78
|
|
|
@@ -66,7 +88,7 @@ export function registerParallHooks(api: OpenClawPluginApi) {
|
|
|
66
88
|
try {
|
|
67
89
|
const step = await resolved.client.createAgentStep(resolved.orgId, resolved.agentUserId, resolved.sessionId, {
|
|
68
90
|
step_type: "tool_call",
|
|
69
|
-
target_type:
|
|
91
|
+
target_type: stepTargetType(resolved.chatId),
|
|
70
92
|
target_id: resolved.chatId || undefined,
|
|
71
93
|
content: {
|
|
72
94
|
call_id: event.toolCallId,
|
|
@@ -102,7 +124,22 @@ export function registerParallHooks(api: OpenClawPluginApi) {
|
|
|
102
124
|
injectedEnv.PRLL_AGENT_ID = state.agentUserId;
|
|
103
125
|
if (state.activeSessionId) injectedEnv.PRLL_SESSION_ID = state.activeSessionId;
|
|
104
126
|
if (stepId) injectedEnv.PRLL_STEP_ID = stepId;
|
|
105
|
-
if (chatId)
|
|
127
|
+
if (chatId) {
|
|
128
|
+
if (chatId.startsWith("sch_")) {
|
|
129
|
+
// Schedule-triggered dispatch: expose PRLL_SCHEDULE_ID so the agent
|
|
130
|
+
// can resolve the schedule; deliberately do NOT set PRLL_CHAT_ID,
|
|
131
|
+
// because schedule events have no chat to reply into, and leaving
|
|
132
|
+
// chatId=sch_* would mislead scripts into `messages send
|
|
133
|
+
// prll://sch_*` (a non-existent CLI path). The run snapshot is
|
|
134
|
+
// available via PRLL_TRIGGER_MESSAGE_ID (srn_*) for agents that
|
|
135
|
+
// want to re-fetch fire-time content.
|
|
136
|
+
injectedEnv.PRLL_SCHEDULE_ID = chatId;
|
|
137
|
+
} else {
|
|
138
|
+
// Chat and task dispatches continue to populate PRLL_CHAT_ID for
|
|
139
|
+
// backward compatibility with existing agent scripts.
|
|
140
|
+
injectedEnv.PRLL_CHAT_ID = chatId;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
106
143
|
if (triggerMsgId) injectedEnv.PRLL_TRIGGER_MESSAGE_ID = triggerMsgId;
|
|
107
144
|
if (state.wikiMountRoot) injectedEnv.PRLL_WIKI_MOUNT_ROOT = state.wikiMountRoot;
|
|
108
145
|
|
|
@@ -127,7 +164,7 @@ export function registerParallHooks(api: OpenClawPluginApi) {
|
|
|
127
164
|
try {
|
|
128
165
|
await resolved.client.createAgentStep(resolved.orgId, resolved.agentUserId, resolved.sessionId, {
|
|
129
166
|
step_type: "tool_result",
|
|
130
|
-
target_type:
|
|
167
|
+
target_type: stepTargetType(resolved.chatId),
|
|
131
168
|
target_id: resolved.chatId || undefined,
|
|
132
169
|
content: {
|
|
133
170
|
call_id: event.toolCallId,
|
package/src/runtime.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
2
|
import type { ParallClient, ParallWs } from "@parall/sdk";
|
|
3
|
+
import type { AgentIdentity } from "@parall/agent-core";
|
|
3
4
|
export type { ForkResult, DispatchState, ParallEvent } from "@parall/agent-core";
|
|
4
5
|
export {
|
|
5
6
|
setSessionChatId,
|
|
@@ -62,3 +63,13 @@ export function getParallAccountState(accountId: string): ParallAccountState | u
|
|
|
62
63
|
export function getAllParallAccountStates(): ReadonlyMap<string, ParallAccountState> {
|
|
63
64
|
return new Map(accountStates);
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
let agentIdentity: AgentIdentity | undefined;
|
|
68
|
+
|
|
69
|
+
export function setAgentIdentity(identity: AgentIdentity) {
|
|
70
|
+
agentIdentity = identity;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getAgentIdentity(): AgentIdentity | undefined {
|
|
74
|
+
return agentIdentity;
|
|
75
|
+
}
|