@feniix/bridgekit 0.9.5 → 0.10.0

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
@@ -4,7 +4,45 @@ All notable changes to `@feniix/bridgekit` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [0.9.5] - Unreleased
7
+ ## [0.10.0] - Unreleased
8
+
9
+ ### Removed (breaking)
10
+
11
+ - `PortableToolHost<TExtension extends string>` type alias — replaced by the
12
+ fixed `PortableToolBuiltInHost = "pi" | "mcp" | "test"` union.
13
+ - `<THost extends string>` generic parameter on `PortableTool`,
14
+ `PortableToolContext`, `definePortableTool`, `executePortableTool`, and
15
+ `validatePortableToolArgs`. Host is now a fixed literal union; adapters
16
+ outside `"pi" | "mcp"` should cast at their adapter boundary instead of
17
+ extending the type union.
18
+
19
+ Migration: drop the second generic argument from any of those types.
20
+ `PortableTool<typeof params, "custom">` becomes `PortableTool<typeof params>`.
21
+ `PortableToolContext<"custom">` becomes `PortableToolContext` (cast `host`
22
+ at the consumer if a custom literal is needed).
23
+
24
+ Cast pattern (custom literal at the adapter boundary):
25
+
26
+ ```ts
27
+ // PortableToolContext.host is fixed to PortableToolBuiltInHost.
28
+ // A direct `as "custom-runtime"` fails under strict (TS2352 — no overlap);
29
+ // cast through `unknown` instead.
30
+ const ctx: PortableToolContext = {
31
+ host: "custom-runtime" as unknown as PortableToolBuiltInHost,
32
+ };
33
+ ```
34
+
35
+ Caveat: the cast lies at the type-system boundary, so
36
+ `switch (ctx.host) { ... default: assertNever(ctx.host); }` patterns will
37
+ fall through on the custom literal at runtime. For custom dispatch, carry
38
+ the host identifier on an adapter-owned field rather than on `ctx.host`,
39
+ or do a runtime allowlist check before exhaustive narrowing.
40
+
41
+ No consumer in tree or in the three known downstream consumers
42
+ (pi-sequential-thinking, pi-exa, pi-code-reasoning) used the generic.
43
+ Resolves [#5](https://github.com/feniix/bridgekit/issues/5).
44
+
45
+ ## [0.9.5] - 2026-05-27
8
46
 
9
47
  ### Documentation
10
48
 
package/README.md CHANGED
@@ -63,7 +63,7 @@ This package is ESM-only and supports Node.js 22.19.0 or newer. Published module
63
63
 
64
64
  ## Why
65
65
 
66
- - Define a tool once; ship it to pi and MCP (and custom hosts) without per-host forks.
66
+ - Define a tool once; ship it to pi and MCP without per-host forks.
67
67
  - Host-neutral tool files: no pi or MCP SDK imports in the tool definition itself.
68
68
  - TypeBox schemas pass through to MCP `inputSchema` directly — no JSON Schema conversion step.
69
69
  - Import-passive, `sideEffects: false`, three-entrypoint split (`.`, `./pi`, `./mcp`) so pi-only consumers do not pull the MCP SDK and vice versa.
@@ -80,7 +80,6 @@ import {
80
80
  type PortableTool,
81
81
  type PortableToolBuiltInHost,
82
82
  type PortableToolContext,
83
- type PortableToolHost,
84
83
  type PortableToolResult,
85
84
  type PortableValidationError,
86
85
  } from "@feniix/bridgekit";
@@ -232,41 +231,6 @@ Portable validation failures and portable `isError: true` results return `CallTo
232
231
 
233
232
  The two adapters now read in parallel: invalid args and portable `isError` results return `{ isError: true }` from both hosts by default, and the same result-guard helpers (`isValidationFailure`, `isDomainFailure`) narrow them on either side.
234
233
 
235
- ### Custom host typing
236
-
237
- Default portable tools accept the built-in host union:
238
-
239
- ```ts
240
- type BuiltIn = "pi" | "mcp" | "test";
241
- ```
242
-
243
- Custom adapters opt in explicitly:
244
-
245
- ```ts
246
- import { Type } from "typebox";
247
- import { definePortableTool, type PortableToolHost } from "@feniix/bridgekit";
248
-
249
- const params = Type.Object({ text: Type.String() });
250
-
251
- type CustomHost = "custom-runtime";
252
-
253
- export const customTool = definePortableTool<typeof params, CustomHost>({
254
- name: "custom_echo",
255
- title: "Custom Echo",
256
- description: "Echoes text in a custom runtime.",
257
- parameters: params,
258
- execute(args, ctx) {
259
- const host: CustomHost = ctx.host;
260
- return { text: `${host}: ${args.text}` };
261
- },
262
- });
263
-
264
- const hostValue: PortableToolHost<CustomHost> = "custom-runtime";
265
- void hostValue;
266
- ```
267
-
268
- Use `PortableToolHost<CustomHost>` for values that may be either a built-in host or your extension. Use the `PortableTool`/`PortableToolContext` generic when a tool or adapter is custom-host-only.
269
-
270
234
  ## Best practices
271
235
 
272
236
  Tool definition best practices:
@@ -306,5 +270,5 @@ Read these files in order:
306
270
 
307
271
  1. `README.md` — public API, contracts, and best practices.
308
272
  2. `llms.txt` — compact agent-facing usage rules and anti-patterns.
309
- 3. `examples/README.md` — copyable layouts for shared tools, pi extensions, MCP stdio servers, and custom hosts.
273
+ 3. `examples/README.md` — copyable layouts for shared tools, pi extensions, MCP stdio servers, and per-host metadata via `hostExtras`.
310
274
  4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, and `dist/src/mcp.d.ts` — canonical installed-package type contracts. In a source checkout, the matching `src/` files contain the same implementation context.
@@ -37,9 +37,8 @@ export interface PortableValidationError {
37
37
  message: string;
38
38
  }
39
39
  export type PortableToolBuiltInHost = "pi" | "mcp" | "test";
40
- export type PortableToolHost<TExtension extends string = never> = PortableToolBuiltInHost | TExtension;
41
- export interface PortableToolContext<THost extends string = PortableToolBuiltInHost> {
42
- host: THost;
40
+ export interface PortableToolContext {
41
+ host: PortableToolBuiltInHost;
43
42
  signal?: AbortSignal;
44
43
  progress?: (update: PortableToolResult) => void;
45
44
  }
@@ -139,12 +138,12 @@ export interface PortableToolHostExtras {
139
138
  pi?: PiHostExtras;
140
139
  mcp?: McpHostExtras;
141
140
  }
142
- export interface PortableTool<TParams extends TSchema = TSchema, THost extends string = PortableToolBuiltInHost> {
141
+ export interface PortableTool<TParams extends TSchema = TSchema> {
143
142
  name: string;
144
143
  title: string;
145
144
  description: string;
146
145
  parameters: TParams;
147
- execute: (args: Static<TParams>, ctx: PortableToolContext<THost>) => PortableToolResult | Promise<PortableToolResult>;
146
+ execute: (args: Static<TParams>, ctx: PortableToolContext) => PortableToolResult | Promise<PortableToolResult>;
148
147
  /**
149
148
  * Optional per-host metadata. Adapters consume the keys they recognise;
150
149
  * unknown host namespaces are ignored. Absent → no behavior change.
@@ -156,4 +155,4 @@ export interface PortableTool<TParams extends TSchema = TSchema, THost extends s
156
155
  */
157
156
  hostExtras?: PortableToolHostExtras;
158
157
  }
159
- export declare function definePortableTool<TParams extends TSchema, THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TParams, THost>): PortableTool<TParams, THost>;
158
+ export declare function definePortableTool<TParams extends TSchema>(tool: PortableTool<TParams>): PortableTool<TParams>;
@@ -1,9 +1,9 @@
1
1
  import type { TSchema } from "typebox";
2
- import type { PortableTool, PortableToolBuiltInHost, PortableToolContext, PortableToolResult, PortableValidationError } from "./define-tool.js";
3
- export declare function validatePortableToolArgs<THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TSchema, THost>, args: unknown): {
2
+ import type { PortableTool, PortableToolContext, PortableToolResult, PortableValidationError } from "./define-tool.js";
3
+ export declare function validatePortableToolArgs(tool: PortableTool<TSchema>, args: unknown): {
4
4
  ok: true;
5
5
  } | {
6
6
  ok: false;
7
7
  errors: PortableValidationError[];
8
8
  };
9
- export declare function executePortableTool<THost extends string = PortableToolBuiltInHost>(tool: PortableTool<TSchema, THost>, args: unknown, ctx: PortableToolContext<NoInfer<THost>>): Promise<PortableToolResult>;
9
+ export declare function executePortableTool(tool: PortableTool<TSchema>, args: unknown, ctx: PortableToolContext): Promise<PortableToolResult>;
@@ -1,3 +1,3 @@
1
- export { definePortableTool, type McpHostExtras, type PiHostExtras, type PortableTool, type PortableToolBuiltInHost, type PortableToolContext, type PortableToolErrorDetails, type PortableToolHost, type PortableToolHostExtras, type PortableToolResult, type PortableValidationError, } from "./core/define-tool.js";
1
+ export { definePortableTool, type McpHostExtras, type PiHostExtras, type PortableTool, type PortableToolBuiltInHost, type PortableToolContext, type PortableToolErrorDetails, type PortableToolHostExtras, type PortableToolResult, type PortableValidationError, } from "./core/define-tool.js";
2
2
  export { executePortableTool, validatePortableToolArgs } from "./core/execute-tool.js";
3
3
  export { isDomainFailure, isValidationFailure, type PortableDomainFailure, type PortableValidationFailure, } from "./core/result-guards.js";
@@ -283,53 +283,7 @@ MCP behavior:
283
283
 
284
284
  ---
285
285
 
286
- ## 4. Use custom host typing for custom adapters
287
-
288
- Default portable tools accept only built-in hosts: `"pi" | "mcp" | "test"`.
289
-
290
- If you are writing a custom adapter, opt in explicitly so the handler can safely narrow `ctx.host`:
291
-
292
- ```ts
293
- import { Type } from "typebox";
294
- import {
295
- definePortableTool,
296
- executePortableTool,
297
- type PortableTool,
298
- type PortableToolContext,
299
- type PortableToolHost,
300
- } from "@feniix/bridgekit";
301
-
302
- const params = Type.Object({ text: Type.String() });
303
-
304
- type CustomHost = "custom-runtime";
305
- type CustomTool = PortableTool<typeof params, CustomHost>;
306
-
307
- const customTool = definePortableTool<typeof params, CustomHost>({
308
- name: "custom_echo",
309
- title: "Custom Echo",
310
- description: "Echoes text in a custom runtime.",
311
- parameters: params,
312
- execute(args, ctx) {
313
- const host: CustomHost = ctx.host;
314
- return { text: `${host}: ${args.text}` };
315
- },
316
- });
317
-
318
- async function runCustomTool(tool: CustomTool, text: string) {
319
- const ctx: PortableToolContext<CustomHost> = { host: "custom-runtime" };
320
- return executePortableTool(tool, { text }, ctx);
321
- }
322
-
323
- const hostValue: PortableToolHost<CustomHost> = "custom-runtime";
324
- void hostValue;
325
- void customTool;
326
- ```
327
-
328
- Use `PortableToolHost<CustomHost>` for values that can be either a built-in host or your custom extension. Use `PortableToolContext<CustomHost>` or `PortableTool<Schema, CustomHost>` when a tool is custom-host-only.
329
-
330
- ---
331
-
332
- ## 5. Per-host metadata via `hostExtras`
286
+ ## 4. Per-host metadata via `hostExtras`
333
287
 
334
288
  When a tool needs host-specific metadata — pi's `pendingMessage` for a "Processing..." signal, MCP's annotations as advisory hints to clients — the canonical place is `PortableTool.hostExtras`. Each host has its own namespace; adapters read the keys they recognise and ignore the rest. Tools that omit `hostExtras` see no behavior change.
335
289
 
@@ -366,7 +320,7 @@ Best practices for `hostExtras`:
366
320
 
367
321
  - Keep tool *behavior* host-neutral. `hostExtras` carries *data* the adapter reads on the tool's behalf; do not embed callbacks or runtime logic.
368
322
  - Set only the fields the adapter recognises; unrecognised keys are silently ignored, but they add noise to the definition.
369
- - For custom-host adapters, extend `PortableToolHostExtras` via `declare module "@feniix/bridgekit"` so consumers of your adapter get type safety on the new namespace.
323
+ - If you ship an adapter for a host outside `"pi" | "mcp"`, cast `ctx.host` at the adapter boundary (the host union is fixed since 0.10.0). For type-safe per-host metadata in your adapter's namespace, extend `PortableToolHostExtras` via `declare module "@feniix/bridgekit"` so consumers of your adapter get type safety on the new namespace.
370
324
 
371
325
  See `docs/rfc-host-extras.md` for the full design rationale (which fields qualify, why a top-level field beats a sidecar map, the closure rule for future additions).
372
326
 
@@ -446,7 +400,7 @@ per-consumer migration deltas.
446
400
 
447
401
  ---
448
402
 
449
- ## 6. Package checklist
403
+ ## 5. Package checklist
450
404
 
451
405
  For publishable tool packages:
452
406
 
package/llms.txt CHANGED
@@ -6,7 +6,7 @@ Use this package when you need to define a tool once and expose it through both
6
6
 
7
7
  1. `README.md` — public API, contracts, best practices, packaging notes.
8
8
  2. `llms.txt` — compact agent-facing usage rules and anti-patterns.
9
- 3. `examples/README.md` — copyable end-to-end layouts for shared tools, pi extension wiring, MCP stdio server wiring, and custom hosts.
9
+ 3. `examples/README.md` — copyable end-to-end layouts for shared tools, pi extension wiring, MCP stdio server wiring, and per-host metadata via `hostExtras`.
10
10
  4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, and `dist/src/mcp.d.ts` — canonical installed-package type contracts. In a source checkout, the matching `src/` files contain implementation context.
11
11
 
12
12
  ## Import map
@@ -96,23 +96,6 @@ if (result.isError) {
96
96
  }
97
97
  ```
98
98
 
99
- ## Custom host typing
100
-
101
- Default tools accept only built-in hosts: `"pi" | "mcp" | "test"`.
102
- For a custom adapter, opt in explicitly:
103
-
104
- ```ts
105
- const customTool = definePortableTool<typeof params, "custom-host">({
106
- // ...
107
- execute(args, ctx) {
108
- const host: "custom-host" = ctx.host;
109
- return { text: host };
110
- },
111
- });
112
- ```
113
-
114
- Use `PortableToolHost<"custom-host">` when a value may be either a built-in host or that extension.
115
-
116
99
  ## Mixed source-loaded hosts and compiled MCP bins
117
100
 
118
101
  Some hosts, such as pi in source-extension packages, may intentionally load TypeScript source while MCP clients launched from npm need compiled JavaScript. In that case:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feniix/bridgekit",
3
- "version": "0.9.5",
3
+ "version": "0.10.0",
4
4
  "description": "BridgeKit defines TypeBox-backed tools once and adapts them to pi, MCP, and other hosts.",
5
5
  "keywords": [
6
6
  "pi",