@shipers-dev/multi 0.6.7 → 0.6.8

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/dist/index.js CHANGED
@@ -5199,6 +5199,7 @@ async function runAcp(opts) {
5199
5199
  const stream2 = ndJsonStream(output, input);
5200
5200
  let activeSessionId = opts.sessionId || null;
5201
5201
  let recording = false;
5202
+ let chunkCount = 0;
5202
5203
  const alwaysAllow = new Set;
5203
5204
  const client = {
5204
5205
  async sessionUpdate(params) {
@@ -5263,12 +5264,33 @@ async function runAcp(opts) {
5263
5264
  }
5264
5265
  }
5265
5266
  recording = true;
5266
- const res = await conn.prompt({
5267
- sessionId: activeSessionId,
5268
- prompt: [{ type: "text", text: opts.prompt }]
5269
- });
5270
- await opts.onEvent({ event_type: "result", payload: { stopReason: res.stopReason } });
5271
- return { stopReason: res.stopReason, sessionId: activeSessionId };
5267
+ const runPrompt = async () => {
5268
+ chunkCount = 0;
5269
+ return await conn.prompt({
5270
+ sessionId: activeSessionId,
5271
+ prompt: [{ type: "text", text: opts.prompt }]
5272
+ });
5273
+ };
5274
+ let res = await runPrompt();
5275
+ let stopReason = res.stopReason;
5276
+ if (chunkCount === 0 && opts.sessionId) {
5277
+ await opts.onEvent({ event_type: "progress", payload: { message: `resumed session produced no output (stopReason=${stopReason}); retrying with fresh session` } });
5278
+ try {
5279
+ const fresh = await conn.newSession({ cwd: opts.cwd || process.cwd(), mcpServers: [] });
5280
+ activeSessionId = fresh.sessionId;
5281
+ if (opts.onSession)
5282
+ await opts.onSession(fresh.sessionId);
5283
+ res = await runPrompt();
5284
+ stopReason = res.stopReason;
5285
+ } catch (e) {
5286
+ await opts.onEvent({ event_type: "error", payload: { message: `retry with fresh session failed: ${String(e)}` } });
5287
+ }
5288
+ }
5289
+ if (chunkCount === 0) {
5290
+ await opts.onEvent({ event_type: "error", payload: { message: `agent produced no output (stopReason=${stopReason})` } });
5291
+ }
5292
+ await opts.onEvent({ event_type: "result", payload: { stopReason } });
5293
+ return { stopReason, sessionId: activeSessionId };
5272
5294
  } finally {
5273
5295
  try {
5274
5296
  child.kill();
@@ -5281,12 +5303,15 @@ async function runAcp(opts) {
5281
5303
  case "agent_message_chunk":
5282
5304
  case "agent_thought_chunk": {
5283
5305
  const text = extractText(u.content);
5284
- if (text)
5306
+ if (text) {
5307
+ chunkCount++;
5285
5308
  await o.onEvent({ event_type: "assistant_text", payload: { text } });
5309
+ }
5286
5310
  break;
5287
5311
  }
5288
5312
  case "tool_call":
5289
5313
  case "tool_call_update": {
5314
+ chunkCount++;
5290
5315
  await o.onEvent({ event_type: "tool_call", payload: {
5291
5316
  id: u.toolCallId || u.id,
5292
5317
  tool: u.title || u.toolName || "tool",
@@ -6395,6 +6420,11 @@ ${userPart}` : userPart;
6395
6420
  if (n > 0)
6396
6421
  log(` \uD83D\uDCCE uploaded ${n} output file(s)`);
6397
6422
  }
6423
+ if (!hasAssistantText && !hadError) {
6424
+ const stopReason = turn.result?.stopReason || "unknown";
6425
+ await postComment(`\u26A0\uFE0F Agent returned no output (stopReason=${stopReason}). Adapter may be stuck on a stale session \u2014 try starting a new issue or clearing session_id.`);
6426
+ log(` \u26A0 ${task.key} produced no assistant output (stopReason=${stopReason})`);
6427
+ }
6398
6428
  if (hadError) {
6399
6429
  await apiClient.post(`${apiUrl}/api/issues/${issueId}/fail`, {});
6400
6430
  log(` \u2717 ${task.key} failed`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipers-dev/multi",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "multi-agent": "./dist/index.js"
package/src/acp-runner.ts CHANGED
@@ -57,6 +57,7 @@ export async function runAcp(opts: AcpRunOpts): Promise<{ stopReason: string; se
57
57
 
58
58
  let activeSessionId: string | null = opts.sessionId || null;
59
59
  let recording = false; // only forward events after prompt() starts
60
+ let chunkCount = 0; // assistant_text + tool_call chunks seen during prompt()
60
61
  const alwaysAllow = new Set<string>(); // keys: toolName|kind that user said "always allow"
61
62
 
62
63
  const client: Client = {
@@ -122,13 +123,38 @@ export async function runAcp(opts: AcpRunOpts): Promise<{ stopReason: string; se
122
123
  }
123
124
 
124
125
  recording = true;
125
- const res = await conn.prompt({
126
- sessionId: activeSessionId!,
127
- prompt: [{ type: 'text', text: opts.prompt }],
128
- } as any);
126
+ const runPrompt = async () => {
127
+ chunkCount = 0;
128
+ return await conn.prompt({
129
+ sessionId: activeSessionId!,
130
+ prompt: [{ type: 'text', text: opts.prompt }],
131
+ } as any);
132
+ };
133
+
134
+ let res = await runPrompt();
135
+ let stopReason = (res as any).stopReason;
136
+
137
+ // Resumed session that returned zero chunks → session is likely stale in the adapter.
138
+ // Retry once with a fresh session so user gets an actual response.
139
+ if (chunkCount === 0 && opts.sessionId) {
140
+ await opts.onEvent({ event_type: 'progress', payload: { message: `resumed session produced no output (stopReason=${stopReason}); retrying with fresh session` } });
141
+ try {
142
+ const fresh = await conn.newSession({ cwd: opts.cwd || process.cwd(), mcpServers: [] } as any);
143
+ activeSessionId = fresh.sessionId;
144
+ if (opts.onSession) await opts.onSession(fresh.sessionId);
145
+ res = await runPrompt();
146
+ stopReason = (res as any).stopReason;
147
+ } catch (e) {
148
+ await opts.onEvent({ event_type: 'error', payload: { message: `retry with fresh session failed: ${String(e)}` } });
149
+ }
150
+ }
151
+
152
+ if (chunkCount === 0) {
153
+ await opts.onEvent({ event_type: 'error', payload: { message: `agent produced no output (stopReason=${stopReason})` } });
154
+ }
129
155
 
130
- await opts.onEvent({ event_type: 'result', payload: { stopReason: (res as any).stopReason } });
131
- return { stopReason: (res as any).stopReason, sessionId: activeSessionId! };
156
+ await opts.onEvent({ event_type: 'result', payload: { stopReason } });
157
+ return { stopReason, sessionId: activeSessionId! };
132
158
  } finally {
133
159
  try { child.kill(); } catch {}
134
160
  }
@@ -140,11 +166,12 @@ export async function runAcp(opts: AcpRunOpts): Promise<{ stopReason: string; se
140
166
  case 'agent_message_chunk':
141
167
  case 'agent_thought_chunk': {
142
168
  const text = extractText(u.content);
143
- if (text) await o.onEvent({ event_type: 'assistant_text', payload: { text } });
169
+ if (text) { chunkCount++; await o.onEvent({ event_type: 'assistant_text', payload: { text } }); }
144
170
  break;
145
171
  }
146
172
  case 'tool_call':
147
173
  case 'tool_call_update': {
174
+ chunkCount++;
148
175
  await o.onEvent({ event_type: 'tool_call', payload: {
149
176
  id: u.toolCallId || u.id, tool: u.title || u.toolName || 'tool', kind: u.kind, status: u.status, input: u.rawInput, locations: u.locations,
150
177
  }});
package/src/index.ts CHANGED
@@ -733,6 +733,13 @@ async function handleRunTask(apiUrl: string, deviceId: string, task: any, detect
733
733
  if (n > 0) log(` 📎 uploaded ${n} output file(s)`);
734
734
  }
735
735
 
736
+ // Visible fallback: agent ran but emitted nothing user-facing
737
+ if (!hasAssistantText && !hadError) {
738
+ const stopReason = turn.result?.stopReason || 'unknown';
739
+ await postComment(`⚠️ Agent returned no output (stopReason=${stopReason}). Adapter may be stuck on a stale session — try starting a new issue or clearing session_id.`);
740
+ log(` ⚠ ${task.key} produced no assistant output (stopReason=${stopReason})`);
741
+ }
742
+
736
743
  if (hadError) { await apiClient.post(`${apiUrl}/api/issues/${issueId}/fail`, {}); log(` ✗ ${task.key} failed`); }
737
744
  else { await apiClient.post(`${apiUrl}/api/issues/${issueId}/complete`, {}); log(` ✓ ${task.key} complete`); }
738
745
  } catch (e) {