@parall/claude-agent 1.16.0 → 1.18.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 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAiDF,qBAAa,iBAAkB,YAAW,eAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAEpD,QAAQ,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAwGjG,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,SAAS;CAkClB"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAEF,qBAAa,iBAAkB,YAAW,eAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAEpD,QAAQ,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAwFjG,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,SAAS;CAkClB"}
package/dist/dispatch.js CHANGED
@@ -1,60 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
3
  import { parseClaudeStreamJson } from "./output-parser.js";
4
- function extractBashCommand(input) {
5
- if (!input || typeof input !== "object")
6
- return undefined;
7
- const command = input.command;
8
- return typeof command === "string" && command.trim() ? command.trim() : undefined;
9
- }
10
- // Returns the non-flag tokens that follow a Parall CLI invocation in `command`,
11
- // or null if the command isn't a Parall CLI call. Handles all the documented
12
- // forms — bare `parall`, `npx [--yes|-y] @parall/cli[@version]`,
13
- // `pnpm (exec|dlx) parall` — and skips `npx` flags so that `npx --yes`
14
- // matches as well as `npx -y`. Tokenization avoids the regex false-match
15
- // where `messages send --no-reply` looked like the receiver-side `no-reply`.
16
- function parseParallCliInvocation(command) {
17
- const tokens = command.replace(/\s+/g, " ").trim().split(" ");
18
- let i = 0;
19
- if (tokens[i] === "parall") {
20
- i++;
21
- }
22
- else if (tokens[i] === "npx") {
23
- i++;
24
- while (i < tokens.length && tokens[i].startsWith("-"))
25
- i++;
26
- if (i >= tokens.length || !/^@parall\/cli(?:@.+)?$/.test(tokens[i]))
27
- return null;
28
- i++;
29
- }
30
- else if (tokens[i] === "pnpm") {
31
- i++;
32
- if (i < tokens.length && (tokens[i] === "exec" || tokens[i] === "dlx"))
33
- i++;
34
- if (i >= tokens.length || tokens[i] !== "parall")
35
- return null;
36
- i++;
37
- }
38
- else {
39
- return null;
40
- }
41
- return tokens.slice(i).filter((t) => !t.startsWith("-"));
42
- }
43
- function isParallSendCommand(command) {
44
- if (!command)
45
- return false;
46
- const sub = parseParallCliInvocation(command);
47
- if (!sub || sub.length === 0)
48
- return false;
49
- // `dm <user>` or `messages send <chat>`
50
- return sub[0] === "dm" || (sub[0] === "messages" && sub[1] === "send");
51
- }
52
- function isParallNoReplyCommand(command) {
53
- if (!command)
54
- return false;
55
- const sub = parseParallCliInvocation(command);
56
- return sub?.[0] === "no-reply";
57
- }
58
4
  export class ClaudeCodeAdapter {
59
5
  opts;
60
6
  constructor(opts) {
@@ -93,11 +39,6 @@ export class ClaudeCodeAdapter {
93
39
  });
94
40
  const groupKey = randomUUID();
95
41
  let sawError = false;
96
- let suppressProjectedText = false;
97
- // Sticky for the whole dispatch: set when the agent explicitly calls
98
- // `parall no-reply` to silence the turn. All subsequent text events are
99
- // emitted with project=false so the bridge never leaks a chat message.
100
- let noReplyCalled = false;
101
42
  let completed = false;
102
43
  try {
103
44
  for await (const event of parseClaudeStreamJson(proc.stdout)) {
@@ -105,30 +46,19 @@ export class ClaudeCodeAdapter {
105
46
  this.opts.sessionManager.recordSessionId(sessionKey, event.sessionId);
106
47
  continue;
107
48
  }
108
- if (event.type === "tool_call") {
109
- const cmd = extractBashCommand(event.input);
110
- if (isParallNoReplyCommand(cmd)) {
111
- noReplyCalled = true;
112
- }
113
- else if (isParallSendCommand(cmd)) {
114
- suppressProjectedText = true;
115
- }
116
- }
117
49
  if (event.type === "error") {
118
50
  sawError = true;
119
51
  yield event;
120
52
  continue;
121
53
  }
122
54
  if (event.type === "text") {
123
- const project = noReplyCalled
124
- ? false
125
- : suppressProjectedText
126
- ? false
127
- : event.project;
128
- suppressProjectedText = false;
55
+ // Runtime output contract (symmetric with OpenClaw channel): Claude's
56
+ // plain text is never projected as a chat message. To reply, the agent
57
+ // must explicitly run `parall messages send` / `dm` via Bash. Text
58
+ // events are still recorded as suppressed session steps for audit.
129
59
  yield {
130
60
  ...event,
131
- project,
61
+ project: false,
132
62
  groupKey,
133
63
  };
134
64
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAiCA,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,QA0BtC"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAkCA,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,QA0BtC"}
package/dist/workspace.js CHANGED
@@ -2,32 +2,33 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  const CLAUDE_MD = `# CLAUDE.md
4
4
 
5
- You are an agent in Parall IM. You participate in chats, handle tasks, and can use Parall CLI commands for advanced operations.
5
+ You are an agent in Parall IM. You participate in chats, handle tasks, and interact exclusively through the Parall CLI.
6
6
 
7
7
  ## Message Model
8
8
 
9
9
  Incoming events are rendered as structured \`[Event: ...]\` blocks.
10
- Reply with plain text when you want Parall to project your response back into the triggering chat.
10
+ Each event includes \`[Chat: ... (prll://cht_xxx)]\` use that chat ID (or full URI) when replying.
11
+
12
+ **Your plain-text output is not delivered to anyone** — it is recorded as suppressed thinking in your session steps and discarded from the chat.
13
+ To say something in a chat, you **must** invoke the Parall CLI via the Bash tool. To stay silent, simply do not invoke it.
11
14
 
12
15
  ## Parall CLI
13
16
 
14
- Use CLI only when you need an explicit side effect instead of plain-text projection:
17
+ All outbound interactions go through \`@parall/cli\`. Credentials are pre-injected as environment variables no setup needed.
15
18
 
16
- - \`npx --yes @parall/cli@latest messages send CHT_ID --text "..." \`
17
- - \`npx --yes @parall/cli@latest tasks update TSK_ID --status in_progress\`
18
- - \`npx --yes @parall/cli@latest dm USER_ID --text "..." --no-reply\`
19
- - \`npx --yes @parall/cli@latest no-reply [--reason "..."]\` — end this turn without any visible reply
19
+ - \`npx --yes @parall/cli@latest messages send prll://cht_xxx --text "..."\` — reply into the triggering chat
20
+ - \`npx --yes @parall/cli@latest dm prll://usr_xxx --text "..." [--no-reply]\` — direct message another user
21
+ - \`npx --yes @parall/cli@latest tasks update prll://tsk_xxx --status in_progress\` — task state
22
+ - \`npx --yes @parall/cli@latest no-reply [--reason "..."]\` — explicitly declare this turn silent (audit signal; not required for silence, just clarifies intent)
20
23
 
21
- Credentials are already injected through environment variables:
22
- \`PRLL_API_URL\`, \`PRLL_API_KEY\`, \`PRLL_ORG_ID\`, \`PRLL_SESSION_ID\`, \`PRLL_CHAT_ID\`, \`PRLL_TRIGGER_MESSAGE_ID\`, \`PRLL_STEP_ID_FILE\`.
24
+ Available env: \`PRLL_API_URL\`, \`PRLL_API_KEY\`, \`PRLL_ORG_ID\`, \`PRLL_SESSION_ID\`, \`PRLL_CHAT_ID\`, \`PRLL_TRIGGER_MESSAGE_ID\`, \`PRLL_STEP_ID_FILE\`.
23
25
 
24
26
  ## Guardrails
25
27
 
26
- - If an event includes \`[Hint: no_reply]\`, **run \`npx --yes @parall/cli@latest no-reply\` as your first action** and do not type anything not even "No response needed" / "Noted" / "OK". Plain text becomes a projected chat message; only the \`no-reply\` command silences the turn.
27
- - Text emitted BEFORE \`no-reply\` in the same turn is already projected as a message and cannot be retracted; text AFTER is dropped by the bridge.
28
- - A single dispatch may carry several events coalesced together. Once you call \`no-reply\` in a dispatch, **plain text suppression is sticky for the whole turn** if some events still need a reply, send them explicitly via \`messages send\` / \`dm\`. Plain-text replies after \`no-reply\` are dropped silently.
29
- - Prefer plain text over CLI for normal chat replies.
30
- - Keep replies concise and task-focused.
28
+ - A dispatch may coalesce multiple events. Decide per event whether to reply via \`messages send\` / \`dm\` events you do not act on simply receive no reply.
29
+ - If an event carries \`[Hint: no_reply]\`, do not send anything for that event. \`no-reply\` is optional and only useful as an explicit intent marker.
30
+ - Never try to "speak" by typing sentences like "No response needed" / "Noted" / "OK"they are discarded, so they accomplish nothing except polluting your session log.
31
+ - Keep CLI replies concise and task-focused.
31
32
  `;
32
33
  export function ensureClaudeWorkspace(workspaceDir, log) {
33
34
  fs.mkdirSync(workspaceDir, { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/claude-agent",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "Claude Code bridge runtime for self-hosted Parall agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,9 +25,9 @@
25
25
  "src"
26
26
  ],
27
27
  "dependencies": {
28
- "@parall/agent-core": "1.16.0",
29
- "@parall/cli": "1.16.0",
30
- "@parall/sdk": "1.16.0"
28
+ "@parall/agent-core": "1.18.0",
29
+ "@parall/sdk": "1.18.0",
30
+ "@parall/cli": "1.18.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.0.0",
package/src/dispatch.ts CHANGED
@@ -26,53 +26,6 @@ type ClaudeCodeAdapterOptions = Pick<
26
26
  sessionManager: ClaudeSessionManager;
27
27
  };
28
28
 
29
- function extractBashCommand(input: unknown): string | undefined {
30
- if (!input || typeof input !== "object") return undefined;
31
- const command = (input as { command?: unknown }).command;
32
- return typeof command === "string" && command.trim() ? command.trim() : undefined;
33
- }
34
-
35
- // Returns the non-flag tokens that follow a Parall CLI invocation in `command`,
36
- // or null if the command isn't a Parall CLI call. Handles all the documented
37
- // forms — bare `parall`, `npx [--yes|-y] @parall/cli[@version]`,
38
- // `pnpm (exec|dlx) parall` — and skips `npx` flags so that `npx --yes`
39
- // matches as well as `npx -y`. Tokenization avoids the regex false-match
40
- // where `messages send --no-reply` looked like the receiver-side `no-reply`.
41
- function parseParallCliInvocation(command: string): string[] | null {
42
- const tokens = command.replace(/\s+/g, " ").trim().split(" ");
43
- let i = 0;
44
- if (tokens[i] === "parall") {
45
- i++;
46
- } else if (tokens[i] === "npx") {
47
- i++;
48
- while (i < tokens.length && tokens[i].startsWith("-")) i++;
49
- if (i >= tokens.length || !/^@parall\/cli(?:@.+)?$/.test(tokens[i])) return null;
50
- i++;
51
- } else if (tokens[i] === "pnpm") {
52
- i++;
53
- if (i < tokens.length && (tokens[i] === "exec" || tokens[i] === "dlx")) i++;
54
- if (i >= tokens.length || tokens[i] !== "parall") return null;
55
- i++;
56
- } else {
57
- return null;
58
- }
59
- return tokens.slice(i).filter((t) => !t.startsWith("-"));
60
- }
61
-
62
- function isParallSendCommand(command: string | undefined): boolean {
63
- if (!command) return false;
64
- const sub = parseParallCliInvocation(command);
65
- if (!sub || sub.length === 0) return false;
66
- // `dm <user>` or `messages send <chat>`
67
- return sub[0] === "dm" || (sub[0] === "messages" && sub[1] === "send");
68
- }
69
-
70
- function isParallNoReplyCommand(command: string | undefined): boolean {
71
- if (!command) return false;
72
- const sub = parseParallCliInvocation(command);
73
- return sub?.[0] === "no-reply";
74
- }
75
-
76
29
  export class ClaudeCodeAdapter implements DispatchAdapter {
77
30
  constructor(private readonly opts: ClaudeCodeAdapterOptions) {}
78
31
 
@@ -115,11 +68,6 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
115
68
 
116
69
  const groupKey = randomUUID();
117
70
  let sawError = false;
118
- let suppressProjectedText = false;
119
- // Sticky for the whole dispatch: set when the agent explicitly calls
120
- // `parall no-reply` to silence the turn. All subsequent text events are
121
- // emitted with project=false so the bridge never leaks a chat message.
122
- let noReplyCalled = false;
123
71
  let completed = false;
124
72
 
125
73
  try {
@@ -129,15 +77,6 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
129
77
  continue;
130
78
  }
131
79
 
132
- if (event.type === "tool_call") {
133
- const cmd = extractBashCommand(event.input);
134
- if (isParallNoReplyCommand(cmd)) {
135
- noReplyCalled = true;
136
- } else if (isParallSendCommand(cmd)) {
137
- suppressProjectedText = true;
138
- }
139
- }
140
-
141
80
  if (event.type === "error") {
142
81
  sawError = true;
143
82
  yield event;
@@ -145,15 +84,13 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
145
84
  }
146
85
 
147
86
  if (event.type === "text") {
148
- const project = noReplyCalled
149
- ? false
150
- : suppressProjectedText
151
- ? false
152
- : event.project;
153
- suppressProjectedText = false;
87
+ // Runtime output contract (symmetric with OpenClaw channel): Claude's
88
+ // plain text is never projected as a chat message. To reply, the agent
89
+ // must explicitly run `parall messages send` / `dm` via Bash. Text
90
+ // events are still recorded as suppressed session steps for audit.
154
91
  yield {
155
92
  ...event,
156
- project,
93
+ project: false,
157
94
  groupKey,
158
95
  };
159
96
  continue;
package/src/workspace.ts CHANGED
@@ -3,32 +3,33 @@ import * as path from "node:path";
3
3
 
4
4
  const CLAUDE_MD = `# CLAUDE.md
5
5
 
6
- You are an agent in Parall IM. You participate in chats, handle tasks, and can use Parall CLI commands for advanced operations.
6
+ You are an agent in Parall IM. You participate in chats, handle tasks, and interact exclusively through the Parall CLI.
7
7
 
8
8
  ## Message Model
9
9
 
10
10
  Incoming events are rendered as structured \`[Event: ...]\` blocks.
11
- Reply with plain text when you want Parall to project your response back into the triggering chat.
11
+ Each event includes \`[Chat: ... (prll://cht_xxx)]\` use that chat ID (or full URI) when replying.
12
+
13
+ **Your plain-text output is not delivered to anyone** — it is recorded as suppressed thinking in your session steps and discarded from the chat.
14
+ To say something in a chat, you **must** invoke the Parall CLI via the Bash tool. To stay silent, simply do not invoke it.
12
15
 
13
16
  ## Parall CLI
14
17
 
15
- Use CLI only when you need an explicit side effect instead of plain-text projection:
18
+ All outbound interactions go through \`@parall/cli\`. Credentials are pre-injected as environment variables no setup needed.
16
19
 
17
- - \`npx --yes @parall/cli@latest messages send CHT_ID --text "..." \`
18
- - \`npx --yes @parall/cli@latest tasks update TSK_ID --status in_progress\`
19
- - \`npx --yes @parall/cli@latest dm USER_ID --text "..." --no-reply\`
20
- - \`npx --yes @parall/cli@latest no-reply [--reason "..."]\` — end this turn without any visible reply
20
+ - \`npx --yes @parall/cli@latest messages send prll://cht_xxx --text "..."\` — reply into the triggering chat
21
+ - \`npx --yes @parall/cli@latest dm prll://usr_xxx --text "..." [--no-reply]\` — direct message another user
22
+ - \`npx --yes @parall/cli@latest tasks update prll://tsk_xxx --status in_progress\` — task state
23
+ - \`npx --yes @parall/cli@latest no-reply [--reason "..."]\` — explicitly declare this turn silent (audit signal; not required for silence, just clarifies intent)
21
24
 
22
- Credentials are already injected through environment variables:
23
- \`PRLL_API_URL\`, \`PRLL_API_KEY\`, \`PRLL_ORG_ID\`, \`PRLL_SESSION_ID\`, \`PRLL_CHAT_ID\`, \`PRLL_TRIGGER_MESSAGE_ID\`, \`PRLL_STEP_ID_FILE\`.
25
+ Available env: \`PRLL_API_URL\`, \`PRLL_API_KEY\`, \`PRLL_ORG_ID\`, \`PRLL_SESSION_ID\`, \`PRLL_CHAT_ID\`, \`PRLL_TRIGGER_MESSAGE_ID\`, \`PRLL_STEP_ID_FILE\`.
24
26
 
25
27
  ## Guardrails
26
28
 
27
- - If an event includes \`[Hint: no_reply]\`, **run \`npx --yes @parall/cli@latest no-reply\` as your first action** and do not type anything not even "No response needed" / "Noted" / "OK". Plain text becomes a projected chat message; only the \`no-reply\` command silences the turn.
28
- - Text emitted BEFORE \`no-reply\` in the same turn is already projected as a message and cannot be retracted; text AFTER is dropped by the bridge.
29
- - A single dispatch may carry several events coalesced together. Once you call \`no-reply\` in a dispatch, **plain text suppression is sticky for the whole turn** if some events still need a reply, send them explicitly via \`messages send\` / \`dm\`. Plain-text replies after \`no-reply\` are dropped silently.
30
- - Prefer plain text over CLI for normal chat replies.
31
- - Keep replies concise and task-focused.
29
+ - A dispatch may coalesce multiple events. Decide per event whether to reply via \`messages send\` / \`dm\` events you do not act on simply receive no reply.
30
+ - If an event carries \`[Hint: no_reply]\`, do not send anything for that event. \`no-reply\` is optional and only useful as an explicit intent marker.
31
+ - Never try to "speak" by typing sentences like "No response needed" / "Noted" / "OK"they are discarded, so they accomplish nothing except polluting your session log.
32
+ - Keep CLI replies concise and task-focused.
32
33
  `;
33
34
 
34
35
  export function ensureClaudeWorkspace(