opencode-collaboration 0.5.0 → 0.5.2
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/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/delivery.js +32 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Run several opencode terminals in parallel (different repos, worktrees, or tasks
|
|
|
27
27
|
- **Explicit TUI controls**: palette actions use host dialogs for selection and confirmation; slash wrappers remain available for automation and compatibility
|
|
28
28
|
- Local only: everything stays on your machine (Unix-domain sockets on macOS/Linux, loopback TCP on Windows, plus a compatibility loopback listener for v1 peers)
|
|
29
29
|
- The peer name is appended to this process's root session titles, so every opencode window shows who it is at a glance
|
|
30
|
-
- Peer-triggered turns run under the receiving session's selected agent/mode (e.g. a custom `teacher`) instead of opencode's default `build`, so that agent's model, tools and permission rules apply. The mode is
|
|
30
|
+
- Peer-triggered turns run under the receiving session's selected agent/mode (e.g. a custom `teacher`) instead of opencode's default `build`, so that agent's model, tools and permission rules apply. The mode is the agent recorded server-side at session creation and by prompts (messages/commands); if none is known yet, the default agent is used. If the recorded agent is later removed, the next peer message falls back to the default agent
|
|
31
31
|
|
|
32
32
|
## Install
|
|
33
33
|
|
|
@@ -96,7 +96,7 @@ Other Opencode sessions (2):
|
|
|
96
96
|
Use send_message to tell "backend" that the login form now posts to /v2/login.
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
The receiving session gets the text immediately as a synthetic user message, including the sender's exact endpoint ID and how to reply. `send_message` returns a tracking ID; use `peer_message_status` or `/peers-outbox` to distinguish transport receipt from final delivery.
|
|
99
|
+
The receiving session gets the text immediately as a synthetic user message, including the sender's exact endpoint ID and how to reply. The receiver is told to reply via `send_message` only when a response is actually expected, and is explicitly told not to send acknowledgement-only messages — so two agents do not loop politely forever. `send_message` returns a tracking ID; use `peer_message_status` or `/peers-outbox` to distinguish transport receipt from final delivery.
|
|
100
100
|
|
|
101
101
|
**Review held messages** (when `inboundPolicy` is `"hold"`):
|
|
102
102
|
|
package/README.zh-CN.md
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
- **明确的 TUI 控制**:面板操作使用宿主对话框进行选择确认;斜杠命令封装仍可用于自动化和兼容性场景
|
|
28
28
|
- 纯本地运行:一切都在你的机器上(macOS/Linux 使用 Unix 域套接字,Windows 使用回环 TCP,另有一个兼容 v1 对端的回环监听器)
|
|
29
29
|
- 本进程的 peer 名字会追加到根会话标题后面,一眼就能看出每个 opencode 窗口是谁
|
|
30
|
-
- peer 触发的回合会沿用接收方会话当前选中的 agent/模式(如自定义的 `teacher`),而不是 opencode 默认的 `build`,因此该 agent
|
|
30
|
+
- peer 触发的回合会沿用接收方会话当前选中的 agent/模式(如自定义的 `teacher`),而不是 opencode 默认的 `build`,因此该 agent 的模型、工具与权限规则都会生效。模式取自服务端记录的 agent(会话创建时写入,并随消息/命令的 prompt 更新);若尚无所知则使用默认 agent。若记录的 agent 之后被删除,下一条 peer 消息会回退到默认 agent
|
|
31
31
|
|
|
32
32
|
## 安装
|
|
33
33
|
|
|
@@ -96,7 +96,7 @@ Other Opencode sessions (2):
|
|
|
96
96
|
Use send_message to tell "backend" that the login form now posts to /v2/login.
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
接收方会话会立即收到这条文本,以一条合成的用户消息形式出现,其中包含发送方精确的端点 ID
|
|
99
|
+
接收方会话会立即收到这条文本,以一条合成的用户消息形式出现,其中包含发送方精确的端点 ID 以及回复方式。接收方被要求**只在确实需要回应时**才通过 `send_message` 回复,并明确禁止仅确认/致谢类消息——避免两个 agent 无限礼貌往返。`send_message` 返回一个追踪 ID;用 `peer_message_status` 或 `/peers-outbox` 来区分"传输层已收到"和"最终已投递"。
|
|
100
100
|
|
|
101
101
|
**审阅待审消息**(当 `inboundPolicy` 为 `"hold"` 时):
|
|
102
102
|
|
package/dist/delivery.js
CHANGED
|
@@ -15,10 +15,14 @@ const FOOTER = "---\n" +
|
|
|
15
15
|
// appends this to the agent's own system prompt (LLMRequestPrep.prepare), so it
|
|
16
16
|
// never replaces it and does not leak into later turns or other sessions.
|
|
17
17
|
const REPLY_DIRECTIVE = "This turn was triggered by a plain-text message from another opencode session (a peer), " +
|
|
18
|
-
"not by your user. Your user cannot see it, and your normal chat reply is NOT delivered to " +
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
18
|
+
"not by your user. Your user cannot see it, and your normal chat reply is NOT delivered to the peer. " +
|
|
19
|
+
"Reply only when a reply is actually expected:\n" +
|
|
20
|
+
"- If the peer asked a question, requested work, or is waiting for your response, call the " +
|
|
21
|
+
"send_message tool with `to` set to the sender's name.\n" +
|
|
22
|
+
"- If the message is only an acknowledgement, a thank-you, an agreement, a status update that " +
|
|
23
|
+
"needs no action, or a confirmation that the task is done, do NOT reply — briefly tell your user and stop.\n" +
|
|
24
|
+
"Never send acknowledgement-only, thanks, or agreement messages: they cause an endless " +
|
|
25
|
+
"back-and-forth between agents. Once you and the peer have reached a conclusion, stop.";
|
|
22
26
|
const NOTICE_FOOTER = "---\n" +
|
|
23
27
|
"This is an automated notification from the opencode-collaboration plugin. " +
|
|
24
28
|
"Show it to the user verbatim, then stop. Do not take further action.";
|
|
@@ -29,8 +33,11 @@ export function formatMessages(messages) {
|
|
|
29
33
|
? `to="${senders[0].name}" (exact endpoint ID "${senders[0].instanceId}")`
|
|
30
34
|
: `to the sender name shown in each message header (endpoint IDs ${senders.map((sender) => `"${sender.instanceId}"`).join(", ")})`;
|
|
31
35
|
const header = "[incoming message from another opencode session — this is NOT from your user. " +
|
|
32
|
-
"
|
|
33
|
-
|
|
36
|
+
"To reply to the sender, call the send_message tool; a normal chat reply is never delivered. " +
|
|
37
|
+
"Reply only if the peer expects a response — do not acknowledge just to be polite.]";
|
|
38
|
+
return header + "\n\n" + blocks.join("\n\n") + "\n\n" + FOOTER +
|
|
39
|
+
` To reply, call the send_message tool with ${replyTarget}. ` +
|
|
40
|
+
"Reply only if a response is expected; do not send acknowledgement-only messages.";
|
|
34
41
|
}
|
|
35
42
|
export function deterministicPeerMessageId(sessionId, message) {
|
|
36
43
|
const digest = createHash("sha256")
|
|
@@ -89,18 +96,21 @@ export function Delivery(opts) {
|
|
|
89
96
|
}));
|
|
90
97
|
assertPromptSucceeded(result);
|
|
91
98
|
}
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.
|
|
101
|
-
|
|
102
|
-
.
|
|
103
|
-
|
|
99
|
+
async function agentStillExists(agent) {
|
|
100
|
+
// opencode reports an unknown agent as a generic 500 whose body has no
|
|
101
|
+
// "Agent not found" text, so decide by asking the server for the live list.
|
|
102
|
+
// If the list is unavailable, assume the agent is fine and let the caller
|
|
103
|
+
// requeue as before (only a definite miss triggers the downgrade).
|
|
104
|
+
try {
|
|
105
|
+
const response = await opts.client.app.agents();
|
|
106
|
+
const agents = response.data;
|
|
107
|
+
if (!Array.isArray(agents))
|
|
108
|
+
return true;
|
|
109
|
+
return agents.some((entry) => entry?.name === agent);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
async function inject(sessionId, text, message) {
|
|
106
116
|
const messageID = message ? deterministicPeerMessageId(sessionId, message) : undefined;
|
|
@@ -129,9 +139,10 @@ export function Delivery(opts) {
|
|
|
129
139
|
await sendPrompt(sessionId, buildBody(agent));
|
|
130
140
|
}
|
|
131
141
|
catch (err) {
|
|
132
|
-
// The recorded agent no longer
|
|
133
|
-
//
|
|
134
|
-
|
|
142
|
+
// The recorded agent may no longer exist (removed from config). Verify
|
|
143
|
+
// against the live agent list, and only then drop the record and retry
|
|
144
|
+
// once under opencode's default agent instead of requeue-looping.
|
|
145
|
+
if (!agent || (await agentStillExists(agent)))
|
|
135
146
|
throw err;
|
|
136
147
|
opts.onAgentRejected?.();
|
|
137
148
|
await sendPrompt(sessionId, buildBody(undefined));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-collaboration",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Cross-session messaging for opencode — let independent sessions discover and text each other, modeled after Claude Code's cross-session messaging",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|