@stagewhisper/stagewhisper 0.32.0 → 0.33.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.
@@ -2,7 +2,7 @@
2
2
  "id": "stagewhisper",
3
3
  "name": "StageWhisper",
4
4
  "description": "Turn live call moments into assistant tasks via StageWhisper",
5
- "version": "0.32.0",
5
+ "version": "0.33.0",
6
6
  "channels": [
7
7
  "stagewhisper"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stagewhisper/stagewhisper",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "type": "module",
5
5
  "description": "OpenClaw channel plugin that connects StageWhisper live calls to your AI assistant",
6
6
  "license": "MIT",
package/plugin-main.ts CHANGED
@@ -120,7 +120,7 @@ export default definePluginEntry({
120
120
  if (!configured) {
121
121
  console.log("\nStageWhisper: not paired\n");
122
122
  console.log(
123
- " Run: openclaw stagewhisper pair --code <CODE>\n",
123
+ " Run: openclaw stagewhisper pair --code <CODE> [--api-url <URL>]\n",
124
124
  );
125
125
  console.log(
126
126
  " Get the pairing code from StageWhisper desktop: Settings → Assistant → Generate Pairing Code\n",
package/src/service.ts CHANGED
@@ -77,6 +77,32 @@ export function createRelayService(api: OpenClawPluginApi) {
77
77
  return lines.join("\n");
78
78
  }
79
79
 
80
+ function isTestTask(task: TaskPayload): boolean {
81
+ return task.action_type === "test";
82
+ }
83
+
84
+ async function updateStatus(
85
+ client: StageWhisperClient,
86
+ task: TaskPayload,
87
+ status: string,
88
+ ): Promise<void> {
89
+ if (isTestTask(task)) return;
90
+ await client.updateTaskStatus(task.id, status);
91
+ }
92
+
93
+ function extractAssistantReply(
94
+ messages: unknown[],
95
+ ): string | null {
96
+ for (let i = messages.length - 1; i >= 0; i--) {
97
+ const msg = messages[i] as Record<string, unknown> | undefined;
98
+ if (!msg) continue;
99
+ if (msg["role"] === "assistant" && typeof msg["content"] === "string") {
100
+ return msg["content"];
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+
80
106
  async function handleTask(
81
107
  task: TaskPayload,
82
108
  client: StageWhisperClient,
@@ -84,7 +110,7 @@ export function createRelayService(api: OpenClawPluginApi) {
84
110
  api.logger.info(`Received task: ${task.title} (${task.id})`);
85
111
 
86
112
  try {
87
- await client.updateTaskStatus(task.id, "delivered");
113
+ await updateStatus(client, task, "delivered");
88
114
  } catch (err) {
89
115
  api.logger.warn(`Failed to mark task as delivered: ${err}`);
90
116
  }
@@ -95,23 +121,56 @@ export function createRelayService(api: OpenClawPluginApi) {
95
121
  const sessionKey = buildAgentSessionKey({
96
122
  agentId: "default",
97
123
  channel: "stagewhisper",
98
- peer: { kind: "direct", id: `sw-session-${task.session_id}` },
124
+ peer: { kind: "direct", id: `sw-task-${task.id}` },
99
125
  });
100
126
 
101
127
  const result = await api.runtime.subagent.run({
102
128
  sessionKey,
103
129
  message: messageContent,
104
- deliver: true,
130
+ deliver: false,
105
131
  idempotencyKey: randomUUID(),
106
132
  });
107
133
 
108
134
  api.logger.info(
109
135
  `Task ${task.id} dispatched to agent session (runId: ${result.runId})`,
110
136
  );
111
- await client.updateTaskStatus(task.id, "running");
137
+
138
+ try {
139
+ await updateStatus(client, task, "running");
140
+ } catch (err) {
141
+ api.logger.warn(`Failed to mark task as running: ${err}`);
142
+ }
143
+
144
+ const waitResult = await api.runtime.subagent.waitForRun({
145
+ runId: result.runId,
146
+ timeoutMs: 120_000,
147
+ });
148
+
149
+ if (waitResult.status === "ok") {
150
+ const session = await api.runtime.subagent.getSessionMessages({
151
+ sessionKey,
152
+ limit: 20,
153
+ });
154
+
155
+ const reply = extractAssistantReply(session.messages);
156
+ if (reply) {
157
+ if (!isTestTask(task)) {
158
+ await client.postReply(task.id, reply);
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
+ }
165
+ } else {
166
+ api.logger.error(
167
+ `Agent run failed for task ${task.id}: ${waitResult.error}`,
168
+ );
169
+ await updateStatus(client, task, "failed").catch(() => {});
170
+ }
112
171
  } catch (err) {
113
- api.logger.error(`Failed to dispatch task to agent: ${err}`);
114
- await client.updateTaskStatus(task.id, "failed").catch(() => {});
172
+ api.logger.error(`Failed to process task ${task.id}: ${err}`);
173
+ await updateStatus(client, task, "failed").catch(() => {});
115
174
  }
116
175
  }
117
176
 
@@ -163,7 +222,9 @@ export function createRelayService(api: OpenClawPluginApi) {
163
222
 
164
223
  try {
165
224
  const task = JSON.parse(jsonStr) as TaskPayload;
166
- await handleTask(task, client);
225
+ handleTask(task, client).catch((err) => {
226
+ api.logger.error(`Unhandled error processing task: ${err}`);
227
+ });
167
228
  } catch (parseErr) {
168
229
  api.logger.warn(`Failed to parse stream event: ${parseErr}`);
169
230
  }