@inceptionstack/roundhouse 0.5.3 → 0.5.4
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/package.json +1 -1
- package/src/agents/pi/pi-adapter.ts +24 -0
- package/src/gateway/streaming.ts +25 -1
- package/src/types.ts +2 -1
package/package.json
CHANGED
|
@@ -205,6 +205,23 @@ export const createPiAgentAdapter: AgentAdapterFactory = (config) => {
|
|
|
205
205
|
} else {
|
|
206
206
|
console.log(`[pi-agent] no memory extension detected — roundhouse memory will manage`);
|
|
207
207
|
}
|
|
208
|
+
|
|
209
|
+
// Warn about pi extensions that bridge a chat platform directly.
|
|
210
|
+
// They hijack agent_start/message_update/agent_end and short-circuit
|
|
211
|
+
// Roundhouse's streaming pipeline — Telegram shows "typing" forever.
|
|
212
|
+
const conflicting = extNames.filter((n) => /pi-telegram(\b|[\/\\])/i.test(n));
|
|
213
|
+
if (conflicting.length > 0) {
|
|
214
|
+
const lines = [
|
|
215
|
+
"",
|
|
216
|
+
"\u26a0\ufe0f CONFLICT: detected pi extension(s) that bridge a chat platform directly:",
|
|
217
|
+
...conflicting.map((n) => ` - ${n}`),
|
|
218
|
+
" Roundhouse already drives Telegram. Loading a bridge extension inside",
|
|
219
|
+
" the pi session causes lost replies (typing indicator without text).",
|
|
220
|
+
" Remove the extension from ~/.pi/agent/extensions or pi config and restart.",
|
|
221
|
+
"",
|
|
222
|
+
];
|
|
223
|
+
for (const line of lines) console.warn(line);
|
|
224
|
+
}
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
return entry;
|
|
@@ -353,6 +370,13 @@ export const createPiAgentAdapter: AgentAdapterFactory = (config) => {
|
|
|
353
370
|
streamEvent = { type: "tool_end", toolName: event.toolName, toolCallId: event.toolCallId, isError: event.isError };
|
|
354
371
|
} else if (event.type === "turn_end") {
|
|
355
372
|
streamEvent = { type: "turn_end" };
|
|
373
|
+
} else if (event.type === "message_end") {
|
|
374
|
+
// Pi records provider failures (auth, throttling, etc.) on the
|
|
375
|
+
// assistant message instead of throwing — surface them.
|
|
376
|
+
const msg = (event as any).message;
|
|
377
|
+
if (msg?.role === "assistant" && msg.stopReason === "error" && msg.errorMessage) {
|
|
378
|
+
streamEvent = { type: "model_error", message: msg.errorMessage };
|
|
379
|
+
}
|
|
356
380
|
}
|
|
357
381
|
}
|
|
358
382
|
|
package/src/gateway/streaming.ts
CHANGED
|
@@ -116,6 +116,8 @@ export async function handleStreaming(
|
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
let hasTextInCurrentTurn = false;
|
|
119
|
+
let hasContentThisTurn = false;
|
|
120
|
+
let modelErrorPosted = false;
|
|
119
121
|
let eventCount = 0;
|
|
120
122
|
let drainingNotified = false;
|
|
121
123
|
|
|
@@ -125,8 +127,9 @@ export async function handleStreaming(
|
|
|
125
127
|
break;
|
|
126
128
|
}
|
|
127
129
|
|
|
130
|
+
eventCount++;
|
|
131
|
+
|
|
128
132
|
if (DEBUG_STREAM) {
|
|
129
|
-
eventCount++;
|
|
130
133
|
const preview = event.type === "text_delta" ? `"${event.text.slice(0, 30)}"`
|
|
131
134
|
: event.type === "custom_message" ? `${event.customType}:${event.content.slice(0, 30)}`
|
|
132
135
|
: event.type === "tool_start" || event.type === "tool_end" ? event.toolName
|
|
@@ -139,12 +142,14 @@ export async function handleStreaming(
|
|
|
139
142
|
ensureStream();
|
|
140
143
|
currentPush!(event.text);
|
|
141
144
|
hasTextInCurrentTurn = true;
|
|
145
|
+
hasContentThisTurn = true;
|
|
142
146
|
break;
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
case "tool_start": {
|
|
146
150
|
activeTools.set(event.toolCallId, event.toolName);
|
|
147
151
|
if (!READ_ONLY_TOOLS.has(event.toolName)) usedFileModifyingTools = true;
|
|
152
|
+
hasContentThisTurn = true;
|
|
148
153
|
if (verbose) {
|
|
149
154
|
try { await thread.post(`${toolIcon(event.toolName)} Running \`${event.toolName}\`…`); } catch {}
|
|
150
155
|
}
|
|
@@ -161,10 +166,22 @@ export async function handleStreaming(
|
|
|
161
166
|
await flushCurrentStream();
|
|
162
167
|
hasTextInCurrentTurn = false;
|
|
163
168
|
}
|
|
169
|
+
hasContentThisTurn = true;
|
|
164
170
|
await postWithFallback(thread, event.content);
|
|
165
171
|
break;
|
|
166
172
|
}
|
|
167
173
|
|
|
174
|
+
case "model_error": {
|
|
175
|
+
await flushCurrentStream();
|
|
176
|
+
hasTextInCurrentTurn = false;
|
|
177
|
+
hasContentThisTurn = true;
|
|
178
|
+
modelErrorPosted = true;
|
|
179
|
+
const safeMsg = event.message.split("\n")[0].slice(0, 400);
|
|
180
|
+
console.warn(`[roundhouse] model error: ${safeMsg}`);
|
|
181
|
+
try { await thread.post(`\u26a0\ufe0f Agent error: ${safeMsg}`); } catch {}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
|
|
168
185
|
case "turn_end": {
|
|
169
186
|
if (hasTextInCurrentTurn) {
|
|
170
187
|
await flushCurrentStream();
|
|
@@ -207,5 +224,12 @@ export async function handleStreaming(
|
|
|
207
224
|
await flushCurrentStream();
|
|
208
225
|
}
|
|
209
226
|
|
|
227
|
+
// Safety net: if the entire turn produced no visible content and no error
|
|
228
|
+
// was already reported, notify the user so they don't stare at "typing" forever.
|
|
229
|
+
if (!hasContentThisTurn && !modelErrorPosted) {
|
|
230
|
+
console.warn(`[roundhouse] agent returned no content this turn (${eventCount} events received)`);
|
|
231
|
+
try { await thread.post("\u26a0\ufe0f Agent returned no response. Check roundhouse logs."); } catch {}
|
|
232
|
+
}
|
|
233
|
+
|
|
210
234
|
return { usedTools: usedFileModifyingTools };
|
|
211
235
|
}
|
package/src/types.ts
CHANGED
|
@@ -43,7 +43,8 @@ export type AgentStreamEvent =
|
|
|
43
43
|
| { type: "draining" }
|
|
44
44
|
| { type: "drain_complete" }
|
|
45
45
|
| { type: "agent_end" }
|
|
46
|
-
| { type: "custom_message"; customType: string; content: string }
|
|
46
|
+
| { type: "custom_message"; customType: string; content: string }
|
|
47
|
+
| { type: "model_error"; message: string };
|
|
47
48
|
|
|
48
49
|
// ── AdapterInfo ──────────────────────────────────────
|
|
49
50
|
|