@stagewhisper/stagewhisper 0.38.0 → 0.40.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 +1 -1
- package/package.json +1 -1
- package/src/channel.ts +8 -17
- package/src/service.ts +65 -16
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
DEFAULT_ACCOUNT_ID,
|
|
5
5
|
} from "openclaw/plugin-sdk/core";
|
|
6
6
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
7
|
-
import { StageWhisperClient } from "./client.js";
|
|
8
7
|
|
|
9
8
|
export type StageWhisperAccount = {
|
|
10
9
|
accountId: string | null;
|
|
@@ -163,23 +162,15 @@ export const stagewhisperPlugin = createChatChannelPlugin<StageWhisperAccount>(
|
|
|
163
162
|
attachedResults: {
|
|
164
163
|
channel: "stagewhisper",
|
|
165
164
|
sendText: async (ctx) => {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
account.integrationId,
|
|
170
|
-
account.relayToken,
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
const target = (ctx as Record<string, unknown>).to as string | undefined;
|
|
174
|
-
const threadId = ctx.threadId as string | undefined;
|
|
175
|
-
const raw = target ?? threadId ?? "";
|
|
176
|
-
const taskId = raw.replace(/^sw-task-/, "");
|
|
177
|
-
if (!taskId) {
|
|
178
|
-
return { messageId: `sw-noop-${Date.now()}`, ok: true };
|
|
165
|
+
const target = (ctx as Record<string, unknown>).to as string | undefined ?? "";
|
|
166
|
+
if (target.startsWith("sw-session-")) {
|
|
167
|
+
return { messageId: `sw-relay-ack-${Date.now()}`, ok: true };
|
|
179
168
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
169
|
+
console.warn(
|
|
170
|
+
`[stagewhisper] sendText called for unrecognised target "${target}"; ` +
|
|
171
|
+
`StageWhisper channel is inbound-only — task replies are routed by the relay service`,
|
|
172
|
+
);
|
|
173
|
+
return { messageId: `sw-dropped-${Date.now()}`, ok: false };
|
|
183
174
|
},
|
|
184
175
|
},
|
|
185
176
|
},
|
package/src/service.ts
CHANGED
|
@@ -90,6 +90,27 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
90
90
|
await client.updateTaskStatus(task.id, status);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
function extractContentFromMessage(
|
|
94
|
+
msg: Record<string, unknown>,
|
|
95
|
+
): string | null {
|
|
96
|
+
const content = msg["content"];
|
|
97
|
+
if (typeof content === "string") return content;
|
|
98
|
+
|
|
99
|
+
if (Array.isArray(content)) {
|
|
100
|
+
for (const part of content) {
|
|
101
|
+
if (
|
|
102
|
+
typeof part === "object" &&
|
|
103
|
+
part !== null &&
|
|
104
|
+
(part as Record<string, unknown>)["type"] === "text" &&
|
|
105
|
+
typeof (part as Record<string, unknown>)["text"] === "string"
|
|
106
|
+
) {
|
|
107
|
+
return (part as Record<string, unknown>)["text"] as string;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
93
114
|
function extractAssistantReply(
|
|
94
115
|
messages: unknown[],
|
|
95
116
|
): string | null {
|
|
@@ -98,20 +119,39 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
98
119
|
if (!msg) continue;
|
|
99
120
|
const role = msg["role"];
|
|
100
121
|
if (role !== "assistant" && role !== "model") continue;
|
|
122
|
+
return extractContentFromMessage(msg);
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
101
126
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
async function extractReplyForTask(
|
|
128
|
+
sessionKey: string,
|
|
129
|
+
taskId: string,
|
|
130
|
+
maxAttempts: number = 3,
|
|
131
|
+
): Promise<string | null> {
|
|
132
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
133
|
+
if (attempt > 0) {
|
|
134
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
135
|
+
}
|
|
136
|
+
const session = await api.runtime.subagent.getSessionMessages({
|
|
137
|
+
sessionKey,
|
|
138
|
+
limit: 50,
|
|
139
|
+
});
|
|
140
|
+
const messages = session.messages as Record<string, unknown>[];
|
|
141
|
+
|
|
142
|
+
for (let i = 0; i < messages.length; i++) {
|
|
143
|
+
const msg = messages[i];
|
|
144
|
+
if (msg["role"] !== "user") continue;
|
|
145
|
+
const text = extractContentFromMessage(msg) ?? "";
|
|
146
|
+
if (!text.includes(`StageWhisper task: ${taskId}`)) continue;
|
|
147
|
+
|
|
148
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
149
|
+
const reply = messages[j];
|
|
150
|
+
const role = reply["role"];
|
|
151
|
+
if (role === "assistant" || role === "model") {
|
|
152
|
+
return extractContentFromMessage(reply);
|
|
114
153
|
}
|
|
154
|
+
if (role === "user") break;
|
|
115
155
|
}
|
|
116
156
|
}
|
|
117
157
|
}
|
|
@@ -143,14 +183,16 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
143
183
|
try {
|
|
144
184
|
await updateStatus(client, task, "delivered");
|
|
145
185
|
} catch (err) {
|
|
146
|
-
api.logger.warn(`Failed to mark task as delivered: ${err}`);
|
|
186
|
+
api.logger.warn(`Failed to mark task as delivered, skipping to prevent duplicates: ${err}`);
|
|
187
|
+
return;
|
|
147
188
|
}
|
|
148
189
|
|
|
149
190
|
const messageContent = buildTaskMessage(task);
|
|
191
|
+
const peerId = `sw-session-${task.session_id}`;
|
|
150
192
|
const sessionKey = buildAgentSessionKey({
|
|
151
193
|
agentId: "default",
|
|
152
194
|
channel: "stagewhisper",
|
|
153
|
-
peer: { kind: "direct", id:
|
|
195
|
+
peer: { kind: "direct", id: peerId },
|
|
154
196
|
});
|
|
155
197
|
|
|
156
198
|
const result = await api.runtime.subagent.run({
|
|
@@ -174,8 +216,14 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
174
216
|
.waitForRun({ runId: result.runId, timeoutMs: 120_000 })
|
|
175
217
|
.then(async (waitResult) => {
|
|
176
218
|
if (waitResult.status === "ok") {
|
|
219
|
+
const reply = await extractReplyForTask(sessionKey, task.id);
|
|
220
|
+
if (reply) {
|
|
221
|
+
await client.postReply(task.id, reply);
|
|
222
|
+
api.logger.info(`Task ${task.id} completed with reply`);
|
|
223
|
+
} else {
|
|
224
|
+
api.logger.warn(`Task ${task.id} completed but no reply found`);
|
|
225
|
+
}
|
|
177
226
|
await updateStatus(client, task, "completed").catch(() => {});
|
|
178
|
-
api.logger.info(`Task ${task.id} completed`);
|
|
179
227
|
} else {
|
|
180
228
|
api.logger.error(
|
|
181
229
|
`Agent run failed for task ${task.id}: ${waitResult.error}`,
|
|
@@ -183,8 +231,9 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
183
231
|
await updateStatus(client, task, "failed").catch(() => {});
|
|
184
232
|
}
|
|
185
233
|
})
|
|
186
|
-
.catch((err) => {
|
|
234
|
+
.catch(async (err) => {
|
|
187
235
|
api.logger.error(`Failed to track task ${task.id}: ${err}`);
|
|
236
|
+
await updateStatus(client, task, "failed").catch(() => {});
|
|
188
237
|
});
|
|
189
238
|
}
|
|
190
239
|
|