@inceptionstack/roundhouse 0.4.3 → 0.4.5
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/README.md +29 -20
- package/architecture.md +10 -4
- package/package.json +1 -6
- package/pi/extensions/web-search.ts +5 -6
- package/src/agents/base-adapter.ts +92 -0
- package/src/agents/index.ts +7 -0
- package/src/agents/kiro/acp/client.ts +117 -0
- package/src/agents/kiro/acp/index.ts +8 -0
- package/src/agents/kiro/acp/process.ts +111 -0
- package/src/agents/kiro/acp/types.ts +83 -0
- package/src/agents/kiro/kiro-adapter.ts +449 -0
- package/src/agents/kiro/session.ts +124 -0
- package/src/agents/kiro/tool-names.ts +37 -0
- package/src/agents/{pi.ts → pi/pi-adapter.ts} +8 -5
- package/src/agents/registry.ts +26 -4
- package/src/bundle.ts +42 -2
- package/src/cli/cli.ts +50 -3
- package/src/cli/doctor/checks/credentials.ts +2 -2
- package/src/cli/env-file.ts +25 -0
- package/src/cli/setup.ts +39 -22
- package/src/config.ts +13 -0
- package/src/gateway.ts +1 -5
- package/src/types.ts +66 -20
package/src/bundle.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { resolve, dirname } from "node:path";
|
|
10
|
-
import { readFileSync, mkdirSync, writeFileSync, existsSync, readdirSync } from "node:fs";
|
|
10
|
+
import { readFileSync, mkdirSync, writeFileSync, existsSync, readdirSync, copyFileSync } from "node:fs";
|
|
11
11
|
import { execFileSync } from "node:child_process";
|
|
12
12
|
import { randomBytes } from "node:crypto";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
@@ -192,14 +192,54 @@ export function provisionBundle(opts: ProvisionOpts = {}): void {
|
|
|
192
192
|
provisionPlaywright(opts);
|
|
193
193
|
provisionUvx(opts);
|
|
194
194
|
provisionMcporterConfig(opts);
|
|
195
|
+
provisionExtensionFiles(opts);
|
|
195
196
|
provisionExtensions(opts);
|
|
196
197
|
}
|
|
197
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Copy shipped extension files to ~/.pi/agent/extensions/ if not already present.
|
|
201
|
+
* Does NOT overwrite existing files — user's version always wins.
|
|
202
|
+
*/
|
|
203
|
+
export function provisionExtensionFiles(opts: ProvisionOpts = {}): void {
|
|
204
|
+
const { log = consoleLog } = opts;
|
|
205
|
+
const shippedDir = resolve(dirname(fileURLToPath(import.meta.url)), "..", "pi", "extensions");
|
|
206
|
+
const targetDir = resolve(homedir(), ".pi", "agent", "extensions");
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
if (!existsSync(shippedDir)) return;
|
|
210
|
+
const files = readdirSync(shippedDir).filter(f => f.endsWith(".ts"));
|
|
211
|
+
if (files.length === 0) return;
|
|
212
|
+
|
|
213
|
+
mkdirSync(targetDir, { recursive: true });
|
|
214
|
+
|
|
215
|
+
let installed = 0;
|
|
216
|
+
let skipped = 0;
|
|
217
|
+
for (const file of files) {
|
|
218
|
+
const target = resolve(targetDir, file);
|
|
219
|
+
if (existsSync(target)) {
|
|
220
|
+
skipped++;
|
|
221
|
+
} else {
|
|
222
|
+
copyFileSync(resolve(shippedDir, file), target);
|
|
223
|
+
installed++;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (installed > 0) {
|
|
228
|
+
log.ok(`${installed} extension(s) installed to ~/.pi/agent/extensions/`);
|
|
229
|
+
}
|
|
230
|
+
if (skipped > 0 && installed === 0) {
|
|
231
|
+
log.ok(`extensions (${skipped} already present, not overwritten)`);
|
|
232
|
+
}
|
|
233
|
+
} catch (err: any) {
|
|
234
|
+
log.warn(`extension file provisioning failed: ${err.message}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
198
238
|
/**
|
|
199
239
|
* Ensure core extensions are listed in ~/.pi/agent/settings.json packages array.
|
|
200
240
|
*/
|
|
201
241
|
export function provisionExtensions(opts: ProvisionOpts = {}): void {
|
|
202
|
-
const { log =
|
|
242
|
+
const { log = consoleLog } = opts;
|
|
203
243
|
const settingsPath = resolve(homedir(), ".pi", "agent", "settings.json");
|
|
204
244
|
|
|
205
245
|
const coreExtensions = [
|
package/src/cli/cli.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "../config";
|
|
25
25
|
import { getAgentSdkPackage } from "../agents/registry";
|
|
26
26
|
import { threadIdToDir } from "../util";
|
|
27
|
-
import { parseEnvFile, serializeEnvFile, envQuote } from "./env-file";
|
|
27
|
+
import { parseEnvFile, serializeEnvFile, envQuote, unquoteEnvValue } from "./env-file";
|
|
28
28
|
import {
|
|
29
29
|
SERVICE_PATH,
|
|
30
30
|
systemctl,
|
|
@@ -72,14 +72,33 @@ async function cmdStart() {
|
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// No systemd service — fall back to foreground
|
|
75
|
+
// No systemd service — fall back to foreground. Check config before launching.
|
|
76
|
+
if (!(await fileExists(CONFIG_PATH))) {
|
|
77
|
+
console.error("No config found. Run 'roundhouse setup --telegram' first.");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
console.log("No systemd service found. Running in foreground (use Ctrl+C to stop)...");
|
|
77
|
-
|
|
82
|
+
if (process.platform !== "darwin") {
|
|
83
|
+
console.log(" Tip: run 'roundhouse install' to set up the systemd daemon.\n");
|
|
84
|
+
} else {
|
|
85
|
+
console.log("");
|
|
86
|
+
}
|
|
78
87
|
await cmdRun();
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
async function cmdRun() {
|
|
91
|
+
// Guard: check config exists before launching gateway
|
|
92
|
+
if (!(await fileExists(CONFIG_PATH))) {
|
|
93
|
+
console.error("No config found. Run 'roundhouse setup --telegram' first.");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
82
97
|
process.env.ROUNDHOUSE_CONFIG = CONFIG_PATH;
|
|
98
|
+
|
|
99
|
+
// Load .env file so secrets (TELEGRAM_BOT_TOKEN, etc.) are available
|
|
100
|
+
await loadEnvFile();
|
|
101
|
+
|
|
83
102
|
const indexPath = resolve(__dirname, "..", "index.ts");
|
|
84
103
|
const jsPath = resolve(__dirname, "..", "dist", "index.js");
|
|
85
104
|
|
|
@@ -94,7 +113,35 @@ async function cmdRun() {
|
|
|
94
113
|
}
|
|
95
114
|
}
|
|
96
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Load the roundhouse .env file into process.env.
|
|
118
|
+
* Does NOT override existing env vars (explicit env takes precedence).
|
|
119
|
+
*/
|
|
120
|
+
async function loadEnvFile(): Promise<void> {
|
|
121
|
+
const envPath = await resolveEnvFilePath();
|
|
122
|
+
if (!(await fileExists(envPath))) return;
|
|
123
|
+
try {
|
|
124
|
+
const entries = parseEnvFile(await readFile(envPath, "utf8"));
|
|
125
|
+
for (const [key, raw] of entries) {
|
|
126
|
+
if (!process.env[key]) {
|
|
127
|
+
process.env[key] = unquoteEnvValue(raw);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch (e: any) {
|
|
131
|
+
console.warn(`[roundhouse] warning: failed to load ${envPath}: ${e.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
97
135
|
async function cmdInstall() {
|
|
136
|
+
if (process.platform === "darwin") {
|
|
137
|
+
console.log("[roundhouse] macOS detected — systemd is not available.\n");
|
|
138
|
+
console.log(" On macOS, use 'roundhouse start' to run in foreground,");
|
|
139
|
+
console.log(" or set up a launchd plist manually.\n");
|
|
140
|
+
console.log(" Tip: run 'roundhouse setup --telegram' to configure first.");
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
98
145
|
console.log("[roundhouse] Installing as systemd daemon...\n");
|
|
99
146
|
|
|
100
147
|
await mkdir(CONFIG_DIR, { recursive: true });
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { readFile } from "node:fs/promises";
|
|
7
|
-
import { parseEnvFile } from "../../env-file";
|
|
7
|
+
import { parseEnvFile, unquoteEnvValue } from "../../env-file";
|
|
8
8
|
import type { DoctorCheck, DoctorContext } from "../types";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -17,7 +17,7 @@ export async function resolveToken(ctx: DoctorContext): Promise<string | null> {
|
|
|
17
17
|
try {
|
|
18
18
|
const entries = parseEnvFile(await readFile(ctx.envFilePath, "utf8"));
|
|
19
19
|
const raw = entries.get("TELEGRAM_BOT_TOKEN");
|
|
20
|
-
if (raw) token = raw
|
|
20
|
+
if (raw) token = unquoteEnvValue(raw);
|
|
21
21
|
} catch {}
|
|
22
22
|
}
|
|
23
23
|
return token || null;
|
package/src/cli/env-file.ts
CHANGED
|
@@ -38,3 +38,28 @@ export function envQuote(value: string): string {
|
|
|
38
38
|
.replace(/\n/g, "\\n");
|
|
39
39
|
return `"${escaped}"`;
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Reverse of envQuote: strip surrounding quotes and unescape interior sequences.
|
|
44
|
+
*/
|
|
45
|
+
export function unquoteEnvValue(raw: string): string {
|
|
46
|
+
// Strip surrounding double quotes (only if matched pair)
|
|
47
|
+
let value = raw;
|
|
48
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
49
|
+
value = value.slice(1, -1);
|
|
50
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
51
|
+
value = value.slice(1, -1);
|
|
52
|
+
}
|
|
53
|
+
// Single-pass unescape to handle \\ correctly (avoids double-replacement)
|
|
54
|
+
value = value.replace(/\\([\\"$`n])/g, (_match, ch) => {
|
|
55
|
+
switch (ch) {
|
|
56
|
+
case "n": return "\n";
|
|
57
|
+
case "\\": return "\\";
|
|
58
|
+
case '"': return '"';
|
|
59
|
+
case "$": return "$";
|
|
60
|
+
case "`": return "`";
|
|
61
|
+
default: return ch;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return value;
|
|
65
|
+
}
|
package/src/cli/setup.ts
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
ENV_FILE_PATH as ENV_PATH,
|
|
23
23
|
fileExists,
|
|
24
24
|
} from "../config";
|
|
25
|
-
import { envQuote, parseEnvFile } from "./env-file";
|
|
25
|
+
import { envQuote, parseEnvFile, unquoteEnvValue } from "./env-file";
|
|
26
26
|
import {
|
|
27
27
|
whichSync,
|
|
28
28
|
systemctl,
|
|
@@ -132,10 +132,12 @@ function resolveAgentForSetup(opts: SetupOptions): AgentDefinition {
|
|
|
132
132
|
// Ensure packages array exists
|
|
133
133
|
if (!Array.isArray(settings.packages)) settings.packages = [];
|
|
134
134
|
|
|
135
|
-
// Add roundhouse itself (ships extensions via pi.extensions in package.json)
|
|
136
|
-
const selfPkg = "npm:@inceptionstack/roundhouse";
|
|
137
135
|
const pkgs = settings.packages as string[];
|
|
138
|
-
|
|
136
|
+
|
|
137
|
+
// Remove stale self-reference (roundhouse no longer ships pi extensions)
|
|
138
|
+
const selfPkg = "npm:@inceptionstack/roundhouse";
|
|
139
|
+
const selfIdx = pkgs.indexOf(selfPkg);
|
|
140
|
+
if (selfIdx !== -1) pkgs.splice(selfIdx, 1);
|
|
139
141
|
|
|
140
142
|
// Add code review + branch protection extensions
|
|
141
143
|
const coreExtensions = [
|
|
@@ -441,7 +443,7 @@ async function stepValidateToken(opts: SetupOptions): Promise<BotInfo> {
|
|
|
441
443
|
}
|
|
442
444
|
|
|
443
445
|
async function stepStopGateway(): Promise<void> {
|
|
444
|
-
step("
|
|
446
|
+
step("④", "Checking for running gateway...");
|
|
445
447
|
|
|
446
448
|
if (platform() !== "linux") {
|
|
447
449
|
ok("Not Linux — skipping service check");
|
|
@@ -462,7 +464,7 @@ async function stepStopGateway(): Promise<void> {
|
|
|
462
464
|
}
|
|
463
465
|
|
|
464
466
|
async function stepInstallPackages(opts: SetupOptions, agent: AgentDefinition): Promise<void> {
|
|
465
|
-
step("
|
|
467
|
+
step("⑤", "Installing packages...");
|
|
466
468
|
|
|
467
469
|
// Roundhouse
|
|
468
470
|
const rhInstalled = whichSync("roundhouse");
|
|
@@ -594,12 +596,12 @@ async function stepInstallPackages(opts: SetupOptions, agent: AgentDefinition):
|
|
|
594
596
|
|
|
595
597
|
async function stepStoreSecrets(opts: SetupOptions, botInfo: BotInfo): Promise<void> {
|
|
596
598
|
if (!opts.psst) {
|
|
597
|
-
step("
|
|
599
|
+
step("⑧", "Storing secrets...");
|
|
598
600
|
ok("Skipped (default — use --with-psst to enable)");
|
|
599
601
|
return;
|
|
600
602
|
}
|
|
601
603
|
|
|
602
|
-
step("
|
|
604
|
+
step("⑧", "Storing secrets in psst...");
|
|
603
605
|
|
|
604
606
|
const secrets: [string, string][] = [
|
|
605
607
|
["TELEGRAM_BOT_TOKEN", opts.botToken],
|
|
@@ -638,7 +640,7 @@ async function stepStoreSecrets(opts: SetupOptions, botInfo: BotInfo): Promise<v
|
|
|
638
640
|
// ── Bundle install ──────────────────────────────────────────────────
|
|
639
641
|
|
|
640
642
|
async function stepInstallBundle(opts: SetupOptions): Promise<void> {
|
|
641
|
-
step("⑥
|
|
643
|
+
step("⑥", "Installing bundle (skills + CLI tools)...");
|
|
642
644
|
|
|
643
645
|
const bundleLog: ProvisionLog = {
|
|
644
646
|
info: (msg) => log(` ${msg}`),
|
|
@@ -655,7 +657,7 @@ async function stepConfigure(
|
|
|
655
657
|
pairResult: PairResult | null,
|
|
656
658
|
agent: AgentDefinition,
|
|
657
659
|
): Promise<void> {
|
|
658
|
-
step("
|
|
660
|
+
step("⑨", "Configuring...");
|
|
659
661
|
|
|
660
662
|
await mkdir(ROUNDHOUSE_DIR, { recursive: true });
|
|
661
663
|
|
|
@@ -764,7 +766,7 @@ async function stepConfigure(
|
|
|
764
766
|
}
|
|
765
767
|
|
|
766
768
|
async function stepPair(opts: SetupOptions, botInfo: BotInfo): Promise<PairResult | null> {
|
|
767
|
-
step("
|
|
769
|
+
step("⑦", "Pairing with Telegram...");
|
|
768
770
|
|
|
769
771
|
// Skip if chat IDs already known
|
|
770
772
|
if (opts.notifyChatIds.length > 0) {
|
|
@@ -820,13 +822,13 @@ async function stepPair(opts: SetupOptions, botInfo: BotInfo): Promise<PairResul
|
|
|
820
822
|
}
|
|
821
823
|
|
|
822
824
|
async function stepRegisterCommands(opts: SetupOptions): Promise<void> {
|
|
823
|
-
step("
|
|
825
|
+
step("⑩", "Registering bot commands...");
|
|
824
826
|
await registerBotCommands(opts.botToken);
|
|
825
827
|
ok(`${BOT_COMMANDS.length} commands registered with Telegram`);
|
|
826
828
|
}
|
|
827
829
|
|
|
828
830
|
async function stepInstallSystemd(opts: SetupOptions): Promise<void> {
|
|
829
|
-
step("
|
|
831
|
+
step("⑩b", "Installing systemd service...");
|
|
830
832
|
|
|
831
833
|
if (!opts.systemd) {
|
|
832
834
|
ok("Skipped (--no-systemd)");
|
|
@@ -871,7 +873,7 @@ async function stepInstallSystemd(opts: SetupOptions): Promise<void> {
|
|
|
871
873
|
}
|
|
872
874
|
|
|
873
875
|
async function stepPostflight(): Promise<void> {
|
|
874
|
-
step("
|
|
876
|
+
step("⑪", "Postflight checks...");
|
|
875
877
|
|
|
876
878
|
if (platform() === "linux") {
|
|
877
879
|
if (isServiceActive()) {
|
|
@@ -892,6 +894,11 @@ async function stepPostflight(): Promise<void> {
|
|
|
892
894
|
if (!whichSync("ffmpeg")) {
|
|
893
895
|
warn("ffmpeg not found (install for voice support)");
|
|
894
896
|
}
|
|
897
|
+
|
|
898
|
+
if (!process.env.TAVILY_API_KEY) {
|
|
899
|
+
warn("TAVILY_API_KEY not set — web search extension won't work");
|
|
900
|
+
log(" Get a free key at https://tavily.com and add to ~/.roundhouse/.env");
|
|
901
|
+
}
|
|
895
902
|
}
|
|
896
903
|
|
|
897
904
|
// ── BotFather Guide ──────────────────────────────────
|
|
@@ -949,11 +956,11 @@ async function runInteractiveTelegramSetup(opts: SetupOptions): Promise<void> {
|
|
|
949
956
|
// Step 5: Install packages
|
|
950
957
|
await stepInstallPackages(opts, agent);
|
|
951
958
|
|
|
952
|
-
// Step
|
|
959
|
+
// Step 6: Install bundle (skills + CLI tools)
|
|
953
960
|
await stepInstallBundle(opts);
|
|
954
961
|
|
|
955
|
-
// Step
|
|
956
|
-
step("
|
|
962
|
+
// Step 7: Pair via Telegram
|
|
963
|
+
step("⑦", "Pairing with Telegram...");
|
|
957
964
|
const nonce = createPairingNonce();
|
|
958
965
|
const pairingLink = createPairingLink(botInfo.username, nonce);
|
|
959
966
|
log(`\n Open this link to pair:\n`);
|
|
@@ -962,6 +969,16 @@ async function runInteractiveTelegramSetup(opts: SetupOptions): Promise<void> {
|
|
|
962
969
|
log(` Or send /start ${nonce} to @${botInfo.username}`);
|
|
963
970
|
log("");
|
|
964
971
|
|
|
972
|
+
// Auto-open the pairing link on macOS
|
|
973
|
+
if (process.platform === "darwin") {
|
|
974
|
+
try {
|
|
975
|
+
execFileSync("open", [pairingLink], { stdio: "ignore" });
|
|
976
|
+
log(" (Opened in Telegram — switch to the app to complete pairing)");
|
|
977
|
+
} catch { /* ignore if open fails */ }
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
log(" Waiting for you to tap the link in Telegram...");
|
|
981
|
+
|
|
965
982
|
const pairResult = await pairTelegram(
|
|
966
983
|
opts.botToken, botInfo.username, opts.users,
|
|
967
984
|
300_000, log, { nonce, showLink: false },
|
|
@@ -975,17 +992,17 @@ async function runInteractiveTelegramSetup(opts: SetupOptions): Promise<void> {
|
|
|
975
992
|
}
|
|
976
993
|
}
|
|
977
994
|
|
|
978
|
-
// Step
|
|
995
|
+
// Step 8: Store secrets
|
|
979
996
|
await stepStoreSecrets(opts, botInfo);
|
|
980
997
|
|
|
981
|
-
// Step
|
|
998
|
+
// Step 9: Write config
|
|
982
999
|
await stepConfigure(opts, botInfo, pairResult, agent);
|
|
983
1000
|
|
|
984
|
-
// Step
|
|
1001
|
+
// Step 10: Register commands + install service
|
|
985
1002
|
await stepRegisterCommands(opts);
|
|
986
1003
|
await stepInstallSystemd(opts);
|
|
987
1004
|
|
|
988
|
-
// Step
|
|
1005
|
+
// Step 11: Verify
|
|
989
1006
|
await stepPostflight();
|
|
990
1007
|
|
|
991
1008
|
// Done!
|
|
@@ -1255,7 +1272,7 @@ export async function cmdPair(argv: string[]): Promise<void> {
|
|
|
1255
1272
|
try {
|
|
1256
1273
|
const entries = parseEnvFile(await readFile(ENV_PATH, "utf8"));
|
|
1257
1274
|
const raw = entries.get("TELEGRAM_BOT_TOKEN");
|
|
1258
|
-
if (raw) token = raw
|
|
1275
|
+
if (raw) token = unquoteEnvValue(raw);
|
|
1259
1276
|
} catch {}
|
|
1260
1277
|
}
|
|
1261
1278
|
|
package/src/config.ts
CHANGED
|
@@ -8,8 +8,21 @@
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
import { readFile, access } from "node:fs/promises";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
11
14
|
import type { GatewayConfig } from "./types";
|
|
12
15
|
|
|
16
|
+
// ── Version ──────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const __configDir = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
/** Roundhouse package version (read from package.json at startup) */
|
|
21
|
+
export const ROUNDHOUSE_VERSION: string = (() => {
|
|
22
|
+
try { return JSON.parse(readFileSync(join(__configDir, "..", "package.json"), "utf8")).version; }
|
|
23
|
+
catch { return "unknown"; }
|
|
24
|
+
})();
|
|
25
|
+
|
|
13
26
|
// ── Path constants ───────────────────────────────────
|
|
14
27
|
|
|
15
28
|
/** New canonical config root */
|
package/src/gateway.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { isTelegramThread, postTelegramHtml, handleTelegramHtmlStream } from "./
|
|
|
13
13
|
import { SttService, enrichAttachmentsWithTranscripts, DEFAULT_STT_CONFIG } from "./voice/stt-service";
|
|
14
14
|
import { sendTelegramToMany } from "./notify/telegram";
|
|
15
15
|
import { runDoctor, formatDoctorTelegram, createDoctorContext } from "./cli/doctor/runner";
|
|
16
|
-
import { ROUNDHOUSE_DIR } from "./config";
|
|
16
|
+
import { ROUNDHOUSE_DIR, ROUNDHOUSE_VERSION } from "./config";
|
|
17
17
|
import { CronSchedulerService } from "./cron/scheduler";
|
|
18
18
|
import { isBuiltinJob } from "./cron/helpers";
|
|
19
19
|
import { formatSchedule, formatRunCounts, jobEnabledIcon } from "./cron/format";
|
|
@@ -65,10 +65,6 @@ import { join, dirname, basename } from "node:path";
|
|
|
65
65
|
import { fileURLToPath } from "node:url";
|
|
66
66
|
|
|
67
67
|
const __gatewayDir = dirname(fileURLToPath(import.meta.url));
|
|
68
|
-
const ROUNDHOUSE_VERSION: string = (() => {
|
|
69
|
-
try { return JSON.parse(readFileSync(join(__gatewayDir, "..", "package.json"), "utf8")).version; }
|
|
70
|
-
catch { return "unknown"; }
|
|
71
|
-
})();
|
|
72
68
|
|
|
73
69
|
function telegramChatIdFromThreadId(threadId: unknown): number | null {
|
|
74
70
|
if (typeof threadId !== "string") return null;
|
package/src/types.ts
CHANGED
|
@@ -45,41 +45,87 @@ export type AgentStreamEvent =
|
|
|
45
45
|
| { type: "agent_end" }
|
|
46
46
|
| { type: "custom_message"; customType: string; content: string };
|
|
47
47
|
|
|
48
|
+
// ── AdapterInfo ──────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Information returned by getInfo(). All fields optional.
|
|
52
|
+
* Consumers (gateway /status, memory lifecycle) read these keys.
|
|
53
|
+
*/
|
|
54
|
+
export interface AdapterInfo {
|
|
55
|
+
/** Agent SDK/CLI version string */
|
|
56
|
+
version?: string;
|
|
57
|
+
/** Currently active model identifier */
|
|
58
|
+
model?: string;
|
|
59
|
+
/** Working directory the agent operates in */
|
|
60
|
+
cwd?: string;
|
|
61
|
+
/** Number of active sessions managed by this adapter */
|
|
62
|
+
activeSessions?: number;
|
|
63
|
+
|
|
64
|
+
// ── Context usage (drives memory pressure detection) ─
|
|
65
|
+
|
|
66
|
+
/** Current token count in context */
|
|
67
|
+
contextTokens?: number | null;
|
|
68
|
+
/** Maximum context window size in tokens */
|
|
69
|
+
contextWindow?: number | null;
|
|
70
|
+
/** Percentage of context used (0-100) */
|
|
71
|
+
contextPercent?: number | null;
|
|
72
|
+
|
|
73
|
+
// ── Memory system integration ──────────────────────
|
|
74
|
+
|
|
75
|
+
/** Whether agent has its own memory extension (determines roundhouse memory mode) */
|
|
76
|
+
hasMemoryExtension?: boolean;
|
|
77
|
+
/** Names of memory-related tools the agent exposes */
|
|
78
|
+
memoryTools?: string[];
|
|
79
|
+
|
|
80
|
+
// ── Extensions / capabilities ──────────────────────
|
|
81
|
+
|
|
82
|
+
/** List of loaded extension paths/names */
|
|
83
|
+
extensions?: string[];
|
|
84
|
+
|
|
85
|
+
/** Additional adapter-specific fields */
|
|
86
|
+
[key: string]: unknown;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* AgentAdapter interface — the contract between gateway and adapters.
|
|
91
|
+
*
|
|
92
|
+
* New adapters should extend BaseAdapter (from ./agents/base-adapter.ts)
|
|
93
|
+
* which provides default implementations for optional methods.
|
|
94
|
+
*/
|
|
48
95
|
export interface AgentAdapter {
|
|
49
96
|
/** Unique agent name, e.g. "pi", "kiro" */
|
|
50
|
-
name: string;
|
|
97
|
+
readonly name: string;
|
|
98
|
+
|
|
99
|
+
// ── Required ─────────────────────────────────────────
|
|
51
100
|
|
|
52
101
|
/** Send a user message and return the full assistant response */
|
|
53
102
|
prompt(threadId: string, message: AgentMessage): Promise<AgentResponse>;
|
|
54
103
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
104
|
+
/** Send a user message and stream back events in real time. */
|
|
105
|
+
promptStream(threadId: string, message: AgentMessage): AsyncIterable<AgentStreamEvent>;
|
|
106
|
+
|
|
107
|
+
/** Tear down all sessions and release resources. */
|
|
108
|
+
dispose(): Promise<void>;
|
|
109
|
+
|
|
110
|
+
// ── Optional (have defaults in BaseAdapter) ──────────
|
|
60
111
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
* Falls back to prompt() if not implemented.
|
|
64
|
-
*/
|
|
65
|
-
promptStream?(threadId: string, message: AgentMessage): AsyncIterable<AgentStreamEvent>;
|
|
112
|
+
/** Prompt using a specific model (e.g. Haiku for memory flush). */
|
|
113
|
+
promptWithModel?(threadId: string, message: AgentMessage, modelId: string): Promise<AgentResponse>;
|
|
66
114
|
|
|
67
|
-
/** Dispose the session for a thread and start fresh on next prompt */
|
|
115
|
+
/** Dispose the session for a thread and start fresh on next prompt. */
|
|
68
116
|
restart?(threadId: string): Promise<void>;
|
|
69
117
|
|
|
70
|
-
/** Compact the session context for a thread */
|
|
118
|
+
/** Compact the session context for a thread. */
|
|
71
119
|
compact?(threadId: string): Promise<{ tokensBefore: number; tokensAfter: number | null } | null>;
|
|
72
|
-
|
|
120
|
+
|
|
121
|
+
/** Compact with a specific model. */
|
|
73
122
|
compactWithModel?(threadId: string, modelId: string): Promise<{ tokensBefore: number; tokensAfter: number | null } | null>;
|
|
74
123
|
|
|
75
|
-
/** Abort the current agent run for a thread */
|
|
124
|
+
/** Abort the current agent run for a thread. */
|
|
76
125
|
abort?(threadId: string): Promise<void>;
|
|
77
126
|
|
|
78
|
-
/** Return runtime info about the agent (model, version, etc.) */
|
|
79
|
-
getInfo?(threadId?: string):
|
|
80
|
-
|
|
81
|
-
/** Tear down all sessions */
|
|
82
|
-
dispose(): Promise<void>;
|
|
127
|
+
/** Return runtime info about the agent (model, version, etc.). */
|
|
128
|
+
getInfo?(threadId?: string): AdapterInfo;
|
|
83
129
|
}
|
|
84
130
|
|
|
85
131
|
export interface AgentResponse {
|