@oh-my-pi/pi-utils 17.0.4 → 17.0.6

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,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.0.5] - 2026-07-18
6
+
7
+ ### Changed
8
+
9
+ - Updated `installRuntimeModuleResolver` to return an uninstaller function that restores the stock `node:module` resolver once all runtime roots are unregistered.
10
+ - Added documentation regarding a known limitation with Bun 1.3.14's `createRequire` behavior when the module resolver patch is active.
11
+
5
12
  ## [17.0.2] - 2026-07-17
6
13
 
7
14
  ### Added
@@ -64,6 +64,20 @@ export declare function isTerminalHeadless(): boolean;
64
64
  * so callers can restore exact prior state (`const prev = setTerminalHeadless(false); … setTerminalHeadless(prev);`).
65
65
  */
66
66
  export declare function setTerminalHeadless(headless: boolean): boolean;
67
+ /**
68
+ * True when this process runs an interactive coding-agent host — the only
69
+ * context where the operator can browse the Agent Hub and focus a live
70
+ * subagent's session (`SessionFocusController`), so a subagent's session title
71
+ * can become operator-visible. Off by default (print/RPC/ACP/eval/SDK/`bun
72
+ * test` never render a focusable session tree); the interactive entrypoint
73
+ * flips it on with {@link setInteractiveHost}.
74
+ */
75
+ export declare function isInteractiveHost(): boolean;
76
+ /**
77
+ * Set the interactive-host flag and return the previous value so callers can
78
+ * restore exact prior state. See {@link isInteractiveHost}.
79
+ */
80
+ export declare function setInteractiveHost(interactive: boolean): boolean;
67
81
  /**
68
82
  * True when this code is running inside a `bun build --compile` standalone
69
83
  * binary. Detects via the embedded virtual-filesystem path markers
@@ -31,15 +31,25 @@ export interface RuntimeResolverOptions {
31
31
  * runtime caches. Stock resolution is tried first and kept for anything
32
32
  * outside the registered roots (bundled imports, node builtins, host or
33
33
  * extension trees). Multiple runtime roots may register; they are consulted
34
- * in registration order.
34
+ * in registration order. Returns an uninstaller that drops the registration
35
+ * and restores the stock resolver once no registrations remain.
35
36
  *
36
37
  * One stock "success" is distrusted: the compiled-binary resolver ignores
37
38
  * `main`/`exports` for real-FS packages (Bun #1763), so a package shipping
38
39
  * its TS source next to `dist/` (e.g. `@huggingface/hub`'s root `index.ts`)
39
40
  * resolves to the wrong file. When the stock hit lands inside a registered
40
41
  * runtime root, the manifest-aware resolution wins.
42
+ *
43
+ * KNOWN LIMITATION (Bun 1.3.14): while any JS override of
44
+ * `Module._resolveFilename` is installed, Bun routes `createRequire(...)`
45
+ * resolution through it with `parent === undefined` — the requester context is
46
+ * never passed, so relative requires from a `createRequire` require fail with
47
+ * "Cannot find module './x' from ''". The override cannot recover what it is
48
+ * never given. Keep this patch scoped to dedicated worker/runtime processes
49
+ * (tiny-inference, fastembed); never install it in the main agent process,
50
+ * where legacy-pi extensions rely on `createRequire` relative requires.
41
51
  */
42
- export declare function installRuntimeModuleResolver({ runtimeNodeModules, stubs }: RuntimeResolverOptions): void;
52
+ export declare function installRuntimeModuleResolver({ runtimeNodeModules, stubs }: RuntimeResolverOptions): () => void;
43
53
  /** Pinned dependency set materialized into a runtime cache directory. */
44
54
  export interface RuntimeInstallSpec {
45
55
  dependencies: Record<string, string>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-utils",
4
- "version": "17.0.4",
4
+ "version": "17.0.6",
5
5
  "description": "Shared utilities for pi packages",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -31,7 +31,7 @@
31
31
  "fmt": "biome format --write ."
32
32
  },
33
33
  "dependencies": {
34
- "@oh-my-pi/pi-natives": "17.0.4",
34
+ "@oh-my-pi/pi-natives": "17.0.6",
35
35
  "handlebars": "^4.7.9",
36
36
  "winston": "^3.19.0",
37
37
  "winston-daily-rotate-file": "^5.0.0"
package/src/env.ts CHANGED
@@ -207,6 +207,30 @@ export function setTerminalHeadless(headless: boolean): boolean {
207
207
  return previous;
208
208
  }
209
209
 
210
+ let interactiveHost = false;
211
+
212
+ /**
213
+ * True when this process runs an interactive coding-agent host — the only
214
+ * context where the operator can browse the Agent Hub and focus a live
215
+ * subagent's session (`SessionFocusController`), so a subagent's session title
216
+ * can become operator-visible. Off by default (print/RPC/ACP/eval/SDK/`bun
217
+ * test` never render a focusable session tree); the interactive entrypoint
218
+ * flips it on with {@link setInteractiveHost}.
219
+ */
220
+ export function isInteractiveHost(): boolean {
221
+ return interactiveHost;
222
+ }
223
+
224
+ /**
225
+ * Set the interactive-host flag and return the previous value so callers can
226
+ * restore exact prior state. See {@link isInteractiveHost}.
227
+ */
228
+ export function setInteractiveHost(interactive: boolean): boolean {
229
+ const previous = interactiveHost;
230
+ interactiveHost = interactive;
231
+ return previous;
232
+ }
233
+
210
234
  /**
211
235
  * True when this code is running inside a `bun build --compile` standalone
212
236
  * binary. Detects via the embedded virtual-filesystem path markers
@@ -194,24 +194,41 @@ export interface RuntimeResolverOptions {
194
194
  * runtime caches. Stock resolution is tried first and kept for anything
195
195
  * outside the registered roots (bundled imports, node builtins, host or
196
196
  * extension trees). Multiple runtime roots may register; they are consulted
197
- * in registration order.
197
+ * in registration order. Returns an uninstaller that drops the registration
198
+ * and restores the stock resolver once no registrations remain.
198
199
  *
199
200
  * One stock "success" is distrusted: the compiled-binary resolver ignores
200
201
  * `main`/`exports` for real-FS packages (Bun #1763), so a package shipping
201
202
  * its TS source next to `dist/` (e.g. `@huggingface/hub`'s root `index.ts`)
202
203
  * resolves to the wrong file. When the stock hit lands inside a registered
203
204
  * runtime root, the manifest-aware resolution wins.
205
+ *
206
+ * KNOWN LIMITATION (Bun 1.3.14): while any JS override of
207
+ * `Module._resolveFilename` is installed, Bun routes `createRequire(...)`
208
+ * resolution through it with `parent === undefined` — the requester context is
209
+ * never passed, so relative requires from a `createRequire` require fail with
210
+ * "Cannot find module './x' from ''". The override cannot recover what it is
211
+ * never given. Keep this patch scoped to dedicated worker/runtime processes
212
+ * (tiny-inference, fastembed); never install it in the main agent process,
213
+ * where legacy-pi extensions rely on `createRequire` relative requires.
204
214
  */
205
- export function installRuntimeModuleResolver({ runtimeNodeModules, stubs = {} }: RuntimeResolverOptions): void {
215
+ export function installRuntimeModuleResolver({ runtimeNodeModules, stubs = {} }: RuntimeResolverOptions): () => void {
206
216
  const registry = resolverRegistry();
207
217
  const existing = registry.find(entry => entry.runtimeNodeModules === runtimeNodeModules);
208
218
  if (existing) Object.assign(existing.stubs, stubs);
209
219
  else registry.push({ runtimeNodeModules, stubs: { ...stubs } });
210
220
 
211
221
  const resolver = (Module as unknown as { default?: ModuleResolver } & ModuleResolver).default ?? Module;
212
- const target = resolver as unknown as ModuleResolver & { [PATCHED]?: boolean };
213
- if (target[PATCHED]) return;
214
- const original = target._resolveFilename.bind(target);
222
+ const target = resolver as unknown as ModuleResolver & { [PATCHED]?: () => void };
223
+ const uninstall = (): void => {
224
+ const entries = resolverRegistry();
225
+ const index = entries.findIndex(entry => entry.runtimeNodeModules === runtimeNodeModules);
226
+ if (index !== -1) entries.splice(index, 1);
227
+ if (entries.length === 0) target[PATCHED]?.();
228
+ };
229
+ if (target[PATCHED]) return uninstall;
230
+ const pristine = target._resolveFilename;
231
+ const original = pristine.bind(target);
215
232
  target._resolveFilename = (request: string, parent: unknown, isMain: boolean, options?: unknown): string => {
216
233
  let stockResolved: string | null = null;
217
234
  let stockError: unknown;
@@ -256,7 +273,11 @@ export function installRuntimeModuleResolver({ runtimeNodeModules, stubs = {} }:
256
273
  if (stockResolved) return stockResolved;
257
274
  throw stockError;
258
275
  };
259
- target[PATCHED] = true;
276
+ target[PATCHED] = () => {
277
+ target._resolveFilename = pristine;
278
+ delete target[PATCHED];
279
+ };
280
+ return uninstall;
260
281
  }
261
282
 
262
283
  /** Pinned dependency set materialized into a runtime cache directory. */