@inceptionstack/roundhouse 0.5.20 → 0.5.22
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
CHANGED
package/src/gateway/gateway.ts
CHANGED
|
@@ -814,34 +814,10 @@ export class Gateway {
|
|
|
814
814
|
|
|
815
815
|
// Only fire for the primary (first) chat
|
|
816
816
|
const primaryChatId = chatIds[0];
|
|
817
|
-
const threadId = `telegram:${primaryChatId}`;
|
|
818
817
|
const agentThreadId = "main";
|
|
819
818
|
|
|
820
|
-
// Create a
|
|
821
|
-
|
|
822
|
-
const token = process.env.TELEGRAM_BOT_TOKEN;
|
|
823
|
-
const syntheticThread = {
|
|
824
|
-
id: threadId,
|
|
825
|
-
adapter: {
|
|
826
|
-
telegramFetch: async (method: string, payload: Record<string, unknown>) => {
|
|
827
|
-
if (!token) return null;
|
|
828
|
-
const res = await fetch(`https://api.telegram.org/bot${token}/${method}`, {
|
|
829
|
-
method: "POST",
|
|
830
|
-
headers: { "Content-Type": "application/json" },
|
|
831
|
-
body: JSON.stringify({ chat_id: primaryChatId, ...payload }),
|
|
832
|
-
signal: AbortSignal.timeout(30_000),
|
|
833
|
-
});
|
|
834
|
-
if (!res.ok) return null;
|
|
835
|
-
const json = await res.json() as { result?: unknown };
|
|
836
|
-
return json.result ?? null;
|
|
837
|
-
},
|
|
838
|
-
},
|
|
839
|
-
post: async (content: string | { markdown: string }) => {
|
|
840
|
-
const text = typeof content === "string" ? content : content.markdown;
|
|
841
|
-
await this.transport.notify([primaryChatId], text);
|
|
842
|
-
},
|
|
843
|
-
startTyping: async () => {},
|
|
844
|
-
};
|
|
819
|
+
// Create a thread via the transport adapter — no transport-specific logic in gateway
|
|
820
|
+
const syntheticThread = this.transport.createThread(primaryChatId);
|
|
845
821
|
|
|
846
822
|
const bootPrompt = "You just came online after a restart. Say a brief hello in-character (1–2 sentences max). Check your workspace for any pending tasks.";
|
|
847
823
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { mkdir, stat } from "node:fs/promises";
|
|
2
|
+
import { mkdir, stat, readFile } from "node:fs/promises";
|
|
3
3
|
import type { ChildProcess } from "node:child_process";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
@@ -168,10 +168,29 @@ export class SubAgentOrchestratorImpl implements SubAgentOrchestrator, SubAgentL
|
|
|
168
168
|
|
|
169
169
|
const outcome = current.requestedOutcome
|
|
170
170
|
? this.terminationHandler.terminalStatusFor(current)
|
|
171
|
-
: (exitCode
|
|
171
|
+
: await this.inferOutcome(runId, exitCode);
|
|
172
172
|
|
|
173
173
|
await this.finalizer.finalizeRun(runId, outcome, { exitCode: exitCode ?? undefined });
|
|
174
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Determine terminal status for a child exit.
|
|
177
|
+
* If exit code is non-zero but the agent produced meaningful stdout output,
|
|
178
|
+
* treat as "complete" — the work succeeded but the process had a teardown error
|
|
179
|
+
* (e.g. pi-hard-no stale context exception on session close).
|
|
180
|
+
*/
|
|
181
|
+
private async inferOutcome(runId: string, exitCode: number | null): Promise<"complete" | "failed"> {
|
|
182
|
+
if (exitCode === 0) return "complete";
|
|
183
|
+
if (exitCode === null) return "failed"; // signal-killed, not a teardown error
|
|
184
|
+
try {
|
|
185
|
+
const stdoutPath = join(this.store.getRunDir(runId), "stdout.log");
|
|
186
|
+
const content = await readFile(stdoutPath, "utf-8");
|
|
187
|
+
// If stdout has substantial content (>50 chars), the agent did its job
|
|
188
|
+
if (content.trim().length > 50) return "complete";
|
|
189
|
+
} catch {
|
|
190
|
+
// No stdout file or can't read — fall through to failed
|
|
191
|
+
}
|
|
192
|
+
return "failed";
|
|
193
|
+
}
|
|
175
194
|
}
|
|
176
195
|
|
|
177
196
|
async function assertDirectoryExists(path: string): Promise<void> {
|
|
@@ -50,6 +50,33 @@ export class TelegramAdapter implements TransportAdapter {
|
|
|
50
50
|
return isTelegramThread(thread as any);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
createThread(chatId: number): ChatThread {
|
|
54
|
+
const token = process.env.TELEGRAM_BOT_TOKEN;
|
|
55
|
+
const threadId = `telegram:${chatId}`;
|
|
56
|
+
const telegramFetch = async (method: string, payload: Record<string, unknown>) => {
|
|
57
|
+
if (!token) return null;
|
|
58
|
+
const res = await fetch(`https://api.telegram.org/bot${token}/${method}`, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
body: JSON.stringify({ chat_id: chatId, ...payload }),
|
|
62
|
+
signal: AbortSignal.timeout(30_000),
|
|
63
|
+
});
|
|
64
|
+
if (!res.ok) return null;
|
|
65
|
+
const json = await res.json() as { result?: unknown };
|
|
66
|
+
return json.result ?? null;
|
|
67
|
+
};
|
|
68
|
+
const thread: ChatThread = {
|
|
69
|
+
id: threadId,
|
|
70
|
+
adapter: { telegramFetch },
|
|
71
|
+
post: async (content: string | { markdown: string }) => {
|
|
72
|
+
const text = typeof content === "string" ? content : content.markdown;
|
|
73
|
+
await postTelegramHtml(thread as any, text);
|
|
74
|
+
},
|
|
75
|
+
startTyping: async () => {},
|
|
76
|
+
};
|
|
77
|
+
return thread;
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
async notify(chatIds: number[], text: string, options?: { parseMode?: string }): Promise<void> {
|
|
54
81
|
if (!process.env.TELEGRAM_BOT_TOKEN) {
|
|
55
82
|
console.warn("[roundhouse] TELEGRAM_BOT_TOKEN not set — skipping notification");
|
package/src/transports/types.ts
CHANGED
|
@@ -56,6 +56,14 @@ export interface TransportAdapter {
|
|
|
56
56
|
/** Send notifications to configured recipients */
|
|
57
57
|
notify(chatIds: number[], text: string, options?: { parseMode?: string }): Promise<void>;
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Create a thread object for a given chat ID.
|
|
61
|
+
* Used by gateway for synthetic turns (boot turn, cron notifications)
|
|
62
|
+
* where no incoming message triggered the interaction.
|
|
63
|
+
* Returns a thread compatible with the streaming system.
|
|
64
|
+
*/
|
|
65
|
+
createThread(chatId: number): ChatThread;
|
|
66
|
+
|
|
59
67
|
/**
|
|
60
68
|
* Check if a pairing flow is pending.
|
|
61
69
|
* Gateway uses this to decide whether to attempt pairing on incoming messages.
|