@stagewhisper/stagewhisper 0.36.0 → 0.38.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 +17 -2
- package/src/client.ts +15 -0
- package/src/service.ts +125 -52
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -147,7 +147,19 @@ export const stagewhisperPlugin = createChatChannelPlugin<StageWhisperAccount>(
|
|
|
147
147
|
threading: { topLevelReplyToMode: "reply" },
|
|
148
148
|
|
|
149
149
|
outbound: {
|
|
150
|
-
base: {
|
|
150
|
+
base: {
|
|
151
|
+
deliveryMode: "direct",
|
|
152
|
+
resolveTarget: (params: {
|
|
153
|
+
cfg?: OpenClawConfig;
|
|
154
|
+
to?: string;
|
|
155
|
+
allowFrom?: string[];
|
|
156
|
+
accountId?: string | null;
|
|
157
|
+
mode?: string;
|
|
158
|
+
}) => {
|
|
159
|
+
if (!params.to) return { ok: false as const, error: new Error("No delivery target") };
|
|
160
|
+
return { ok: true as const, to: params.to };
|
|
161
|
+
},
|
|
162
|
+
},
|
|
151
163
|
attachedResults: {
|
|
152
164
|
channel: "stagewhisper",
|
|
153
165
|
sendText: async (ctx) => {
|
|
@@ -158,7 +170,10 @@ export const stagewhisperPlugin = createChatChannelPlugin<StageWhisperAccount>(
|
|
|
158
170
|
account.relayToken,
|
|
159
171
|
);
|
|
160
172
|
|
|
161
|
-
const
|
|
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-/, "");
|
|
162
177
|
if (!taskId) {
|
|
163
178
|
return { messageId: `sw-noop-${Date.now()}`, ok: true };
|
|
164
179
|
}
|
package/src/client.ts
CHANGED
|
@@ -118,4 +118,19 @@ export class StageWhisperClient {
|
|
|
118
118
|
streamHeaders(): Record<string, string> {
|
|
119
119
|
return { Authorization: `Bearer ${this.relayToken}` };
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
async testReply(testId: string, content: string): Promise<void> {
|
|
123
|
+
const res = await fetch(
|
|
124
|
+
`${this.baseUrl}/api/v1/openclaw/integrations/${this.integrationId}/test-reply`,
|
|
125
|
+
{
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers: this.headers(),
|
|
128
|
+
body: JSON.stringify({ test_id: testId, content }),
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const text = await res.text();
|
|
133
|
+
throw new Error(`Test reply failed (${res.status}): ${text}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
121
136
|
}
|
package/src/service.ts
CHANGED
|
@@ -78,7 +78,7 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function isTestTask(task: TaskPayload): boolean {
|
|
81
|
-
return task.action_type === "
|
|
81
|
+
return task.action_type === "connectivity_test";
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
async function updateStatus(
|
|
@@ -96,19 +96,50 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
96
96
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
97
97
|
const msg = messages[i] as Record<string, unknown> | undefined;
|
|
98
98
|
if (!msg) continue;
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const role = msg["role"];
|
|
100
|
+
if (role !== "assistant" && role !== "model") continue;
|
|
101
|
+
|
|
102
|
+
const content = msg["content"];
|
|
103
|
+
if (typeof content === "string") return content;
|
|
104
|
+
|
|
105
|
+
if (Array.isArray(content)) {
|
|
106
|
+
for (const part of content) {
|
|
107
|
+
if (
|
|
108
|
+
typeof part === "object" &&
|
|
109
|
+
part !== null &&
|
|
110
|
+
(part as Record<string, unknown>)["type"] === "text" &&
|
|
111
|
+
typeof (part as Record<string, unknown>)["text"] === "string"
|
|
112
|
+
) {
|
|
113
|
+
return (part as Record<string, unknown>)["text"] as string;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
return null;
|
|
104
119
|
}
|
|
105
120
|
|
|
106
|
-
async function
|
|
121
|
+
async function extractReplyWithRetry(
|
|
122
|
+
sessionKey: string,
|
|
123
|
+
maxAttempts: number = 3,
|
|
124
|
+
): Promise<string | null> {
|
|
125
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
126
|
+
if (attempt > 0) {
|
|
127
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
128
|
+
}
|
|
129
|
+
const session = await api.runtime.subagent.getSessionMessages({
|
|
130
|
+
sessionKey,
|
|
131
|
+
limit: 20,
|
|
132
|
+
});
|
|
133
|
+
const reply = extractAssistantReply(session.messages);
|
|
134
|
+
if (reply) return reply;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function handleNormalTask(
|
|
107
140
|
task: TaskPayload,
|
|
108
141
|
client: StageWhisperClient,
|
|
109
142
|
): Promise<void> {
|
|
110
|
-
api.logger.info(`Received task: ${task.title} (${task.id})`);
|
|
111
|
-
|
|
112
143
|
try {
|
|
113
144
|
await updateStatus(client, task, "delivered");
|
|
114
145
|
} catch (err) {
|
|
@@ -116,57 +147,101 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
116
147
|
}
|
|
117
148
|
|
|
118
149
|
const messageContent = buildTaskMessage(task);
|
|
150
|
+
const sessionKey = buildAgentSessionKey({
|
|
151
|
+
agentId: "default",
|
|
152
|
+
channel: "stagewhisper",
|
|
153
|
+
peer: { kind: "direct", id: `sw-task-${task.id}` },
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const result = await api.runtime.subagent.run({
|
|
157
|
+
sessionKey,
|
|
158
|
+
message: messageContent,
|
|
159
|
+
deliver: true,
|
|
160
|
+
idempotencyKey: randomUUID(),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
api.logger.info(
|
|
164
|
+
`Task ${task.id} dispatched to agent session (runId: ${result.runId})`,
|
|
165
|
+
);
|
|
119
166
|
|
|
120
167
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
});
|
|
168
|
+
await updateStatus(client, task, "running");
|
|
169
|
+
} catch (err) {
|
|
170
|
+
api.logger.warn(`Failed to mark task as running: ${err}`);
|
|
171
|
+
}
|
|
126
172
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
173
|
+
api.runtime.subagent
|
|
174
|
+
.waitForRun({ runId: result.runId, timeoutMs: 120_000 })
|
|
175
|
+
.then(async (waitResult) => {
|
|
176
|
+
if (waitResult.status === "ok") {
|
|
177
|
+
await updateStatus(client, task, "completed").catch(() => {});
|
|
178
|
+
api.logger.info(`Task ${task.id} completed`);
|
|
179
|
+
} else {
|
|
180
|
+
api.logger.error(
|
|
181
|
+
`Agent run failed for task ${task.id}: ${waitResult.error}`,
|
|
182
|
+
);
|
|
183
|
+
await updateStatus(client, task, "failed").catch(() => {});
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
.catch((err) => {
|
|
187
|
+
api.logger.error(`Failed to track task ${task.id}: ${err}`);
|
|
132
188
|
});
|
|
189
|
+
}
|
|
133
190
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
191
|
+
async function handleTestTask(
|
|
192
|
+
task: TaskPayload,
|
|
193
|
+
client: StageWhisperClient,
|
|
194
|
+
): Promise<void> {
|
|
195
|
+
const messageContent = buildTaskMessage(task);
|
|
196
|
+
const sessionKey = buildAgentSessionKey({
|
|
197
|
+
agentId: "default",
|
|
198
|
+
channel: "stagewhisper",
|
|
199
|
+
peer: { kind: "direct", id: `sw-test-${task.id}` },
|
|
200
|
+
});
|
|
137
201
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
202
|
+
const result = await api.runtime.subagent.run({
|
|
203
|
+
sessionKey,
|
|
204
|
+
message: messageContent,
|
|
205
|
+
deliver: false,
|
|
206
|
+
idempotencyKey: randomUUID(),
|
|
207
|
+
});
|
|
143
208
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
});
|
|
209
|
+
api.logger.info(
|
|
210
|
+
`Test task ${task.id} dispatched (runId: ${result.runId})`,
|
|
211
|
+
);
|
|
148
212
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
213
|
+
const waitResult = await api.runtime.subagent.waitForRun({
|
|
214
|
+
runId: result.runId,
|
|
215
|
+
timeoutMs: 120_000,
|
|
216
|
+
});
|
|
154
217
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
api.logger.info(`Task ${task.id} completed with reply`);
|
|
161
|
-
} else {
|
|
162
|
-
api.logger.warn(`Task ${task.id} completed but no assistant reply found`);
|
|
163
|
-
await updateStatus(client, task, "completed").catch(() => {});
|
|
164
|
-
}
|
|
218
|
+
if (waitResult.status === "ok") {
|
|
219
|
+
const reply = await extractReplyWithRetry(sessionKey);
|
|
220
|
+
if (reply) {
|
|
221
|
+
await client.testReply(task.id, reply);
|
|
222
|
+
api.logger.info(`Test task ${task.id} completed with reply`);
|
|
165
223
|
} else {
|
|
166
|
-
api.logger.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
224
|
+
api.logger.warn(`Test task ${task.id} completed but no reply found`);
|
|
225
|
+
await client.testReply(task.id, "(no reply extracted)");
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
api.logger.error(
|
|
229
|
+
`Agent run failed for test task ${task.id}: ${waitResult.error}`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function handleTask(
|
|
235
|
+
task: TaskPayload,
|
|
236
|
+
client: StageWhisperClient,
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
api.logger.info(`Received task: ${task.title} (${task.id})`);
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
if (isTestTask(task)) {
|
|
242
|
+
await handleTestTask(task, client);
|
|
243
|
+
} else {
|
|
244
|
+
await handleNormalTask(task, client);
|
|
170
245
|
}
|
|
171
246
|
} catch (err) {
|
|
172
247
|
api.logger.error(`Failed to process task ${task.id}: ${err}`);
|
|
@@ -222,11 +297,9 @@ export function createRelayService(api: OpenClawPluginApi) {
|
|
|
222
297
|
|
|
223
298
|
try {
|
|
224
299
|
const task = JSON.parse(jsonStr) as TaskPayload;
|
|
225
|
-
handleTask(task, client)
|
|
226
|
-
|
|
227
|
-
});
|
|
228
|
-
} catch (parseErr) {
|
|
229
|
-
api.logger.warn(`Failed to parse stream event: ${parseErr}`);
|
|
300
|
+
await handleTask(task, client);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
api.logger.error(`Error processing task from stream: ${err}`);
|
|
230
303
|
}
|
|
231
304
|
}
|
|
232
305
|
}
|