@inceptionstack/roundhouse 0.5.26 → 0.5.27

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to `@inceptionstack/roundhouse` are documented here.
4
4
 
5
+ ## [0.5.27] — 2026-05-14
6
+
7
+ ### Fixed
8
+ - **Self-update no longer falsely fails on mise/nvm hosts** — on systems where Node is managed by mise (or nvm), `npm install -g` triggers a post-install reshim hook that exits 127 when its tool isn't on PATH, causing `execSync` to throw even though the package was written to disk correctly. The user-visible bug: "Self-update failed: Command failed: npm install -g …" plus `/status` continuing to show the old version forever (because the gateway never restarted). Fix: when the install command throws, consult `npm list -g <pkg>` and trust the on-disk version. If it matches the target, treat the install as successful. Same logic applied to extension updates. (#128)
9
+ - **Side effect:** `/update` now fires its existing 'restarting…' branch on this case, so `/status` reflects the new version on next boot.
10
+
11
+ ### Changed
12
+ - **DRY in `cli/update.ts`:** extracted `getInstalledVersion()` helper used by both pre-install version check and post-failure verification; introduced `SELF_PACKAGE` constant; fixed stale `commands/update.ts` header comment.
13
+
14
+ ## [0.5.26] — 2026-05-14
15
+
16
+ ### Fixed
17
+ - **Emergency compact loop — output-cap mismatch + summarization input overflow.** Two compounding bugs caused infinite emergency-compact loops on Haiku 4.5 sessions near the context limit. (1) `reserveTokens=150000` + Haiku's 64k output cap produced `maxTokens=120000`, which Bedrock rejected. (2) `hardTokens=200k`/`softTokens=180k` against a 200k window left no headroom for the summarizer prompt itself. Fix: lower thresholds to 150k/130k, add `COMPACT_HEADROOM_TOKENS=50k`, force `thinkingLevel:off` in `compactWithModel`, drop `reserveTokens` to 78k. State is now loaded once and reused; phase timing is hoisted; telemetry is accurate on failure. (#126)
18
+
5
19
  ## [0.5.25] — 2026-05-12
6
20
 
7
21
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inceptionstack/roundhouse",
3
- "version": "0.5.26",
3
+ "version": "0.5.27",
4
4
  "type": "module",
5
5
  "description": "Multi-platform chat gateway that routes messages through a configured AI agent",
6
6
  "license": "MIT",
package/src/cli/update.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * commands/update.ts — Handle the /update command
2
+ * cli/update.ts — Handle the /update command
3
3
  *
4
4
  * Transport-agnostic: receives a ProgressReporter interface,
5
5
  * not a Telegram-specific thread object.
@@ -15,6 +15,28 @@ const GLOBAL_PI_EXTENSION_PACKAGES = [
15
15
  "@inceptionstack/pi-branch-enforcer",
16
16
  ];
17
17
 
18
+ const SELF_PACKAGE = "@inceptionstack/roundhouse";
19
+
20
+ /**
21
+ * Read globally-installed version of a package from disk.
22
+ * Returns empty string if the package is not installed or query fails.
23
+ *
24
+ * Used both for pre-install version checks and for post-failure verification
25
+ * (mise/nvm/npm reshim hooks can fail with exit 127 even after `npm install -g`
26
+ * actually wrote the new version to disk — see PR fix/self-update-verify-on-failure).
27
+ */
28
+ function getInstalledVersion(pkg: string): string {
29
+ try {
30
+ const out = execSync(`npm list -g ${pkg} --json --depth=0 2>/dev/null`, {
31
+ timeout: 10_000,
32
+ encoding: "utf8",
33
+ });
34
+ return JSON.parse(out)?.dependencies?.[pkg]?.version ?? "";
35
+ } catch {
36
+ return "";
37
+ }
38
+ }
39
+
18
40
  export interface UpdateProgress {
19
41
  update(text: string): Promise<void>;
20
42
  }
@@ -28,14 +50,11 @@ export interface UpdateResult {
28
50
 
29
51
  export async function updateExtensions(progress: UpdateProgress): Promise<void> {
30
52
  for (const extensionPackage of GLOBAL_PI_EXTENSION_PACKAGES) {
53
+ let latestExtVersion = "";
31
54
  try {
32
55
  // Check if already at latest
33
- const installed = execSync(`npm list -g ${extensionPackage} --json 2>/dev/null`, {
34
- timeout: 10_000,
35
- encoding: "utf8",
36
- });
37
- const installedVersion = JSON.parse(installed)?.dependencies?.[extensionPackage]?.version ?? "";
38
- const latestExtVersion = execSync(`npm view ${extensionPackage} version 2>/dev/null`, {
56
+ const installedVersion = getInstalledVersion(extensionPackage);
57
+ latestExtVersion = execSync(`npm view ${extensionPackage} version 2>/dev/null`, {
39
58
  timeout: 10_000,
40
59
  encoding: "utf8",
41
60
  }).trim();
@@ -57,6 +76,14 @@ export async function updateExtensions(progress: UpdateProgress): Promise<void>
57
76
  await progress.update(`✅ ${extensionPackage} updated`);
58
77
  } catch (e) {
59
78
  const msg = e instanceof Error ? e.message : String(e);
79
+ // Verify-after-fail: post-install reshim hooks (mise/nvm) can exit non-zero
80
+ // even when the package landed on disk correctly.
81
+ const onDisk = getInstalledVersion(extensionPackage);
82
+ if (onDisk && (!latestExtVersion || onDisk === latestExtVersion)) {
83
+ console.warn(`[roundhouse] ${extensionPackage} install reported failure but v${onDisk} is on disk — treating as success:`, msg);
84
+ await progress.update(`✅ ${extensionPackage} updated to v${onDisk} (post-install hook warned, ignored)`);
85
+ continue;
86
+ }
60
87
  console.warn(`[roundhouse] failed to update extension ${extensionPackage}:`, msg);
61
88
  await progress.update(`⚠️ Failed to update ${extensionPackage}: ${msg.slice(0, 150)}`);
62
89
  }
@@ -71,13 +98,20 @@ export async function updateSelf(
71
98
  await progress.update(`📦 Updating v${currentVersion} → v${latestVersion}...`);
72
99
 
73
100
  try {
74
- execSync("npm install -g @inceptionstack/roundhouse@latest 2>&1", {
101
+ execSync(`npm install -g ${SELF_PACKAGE}@latest 2>&1`, {
75
102
  timeout: 120_000,
76
103
  encoding: "utf8",
77
104
  });
78
105
  return undefined;
79
106
  } catch (e) {
80
107
  const msg = e instanceof Error ? e.message : String(e);
108
+ // Verify-after-fail: mise/nvm post-install reshim can exit 127 even when
109
+ // npm wrote the new version to disk. Trust the on-disk state over the exit code.
110
+ const onDisk = getInstalledVersion(SELF_PACKAGE);
111
+ if (onDisk === latestVersion) {
112
+ console.warn(`[roundhouse] self-update install reported failure but v${onDisk} is on disk — treating as success:`, msg);
113
+ return undefined;
114
+ }
81
115
  console.warn("[roundhouse] self-update failed:", msg);
82
116
  return `Self-update failed: ${msg}`;
83
117
  }
@@ -106,7 +140,7 @@ export async function performUpdate(progress: UpdateProgress): Promise<UpdateRes
106
140
 
107
141
  let latestVersion: string;
108
142
  try {
109
- latestVersion = execSync("npm view @inceptionstack/roundhouse version 2>/dev/null", {
143
+ latestVersion = execSync(`npm view ${SELF_PACKAGE} version 2>/dev/null`, {
110
144
  timeout: 30_000,
111
145
  encoding: "utf8",
112
146
  }).trim();