@net-mesh/core 0.26.0 → 0.27.0-beta.2

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@net-mesh/core",
3
- "version": "0.26.0",
3
+ "version": "0.27.0-beta.2",
4
4
  "description": "High-performance, schema-agnostic event bus for AI runtime workloads",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -46,7 +46,7 @@
46
46
  "license": "Apache-2.0",
47
47
  "repository": {
48
48
  "type": "git",
49
- "url": "https://github.com/ai-2070/net"
49
+ "url": "git+https://github.com/ai-2070/net.git"
50
50
  },
51
51
  "keywords": [
52
52
  "event-bus",
@@ -97,13 +97,13 @@
97
97
  "node": ">=20"
98
98
  },
99
99
  "optionalDependencies": {
100
- "@net-mesh/core-win32-x64-msvc": "0.26.0",
101
- "@net-mesh/core-win32-arm64-msvc": "0.26.0",
102
- "@net-mesh/core-darwin-x64": "0.26.0",
103
- "@net-mesh/core-darwin-arm64": "0.26.0",
104
- "@net-mesh/core-linux-x64-gnu": "0.26.0",
105
- "@net-mesh/core-linux-x64-musl": "0.26.0",
106
- "@net-mesh/core-linux-arm64-gnu": "0.26.0",
107
- "@net-mesh/core-linux-arm64-musl": "0.26.0"
100
+ "@net-mesh/core-win32-x64-msvc": "0.27.0-beta.2",
101
+ "@net-mesh/core-win32-arm64-msvc": "0.27.0-beta.2",
102
+ "@net-mesh/core-darwin-x64": "0.27.0-beta.2",
103
+ "@net-mesh/core-darwin-arm64": "0.27.0-beta.2",
104
+ "@net-mesh/core-linux-x64-gnu": "0.27.0-beta.2",
105
+ "@net-mesh/core-linux-x64-musl": "0.27.0-beta.2",
106
+ "@net-mesh/core-linux-arm64-gnu": "0.27.0-beta.2",
107
+ "@net-mesh/core-linux-arm64-musl": "0.27.0-beta.2"
108
108
  }
109
109
  }
package/tool.d.ts ADDED
@@ -0,0 +1,450 @@
1
+ import type { CallOptions, TypedMeshRpc } from './mesh_rpc';
2
+ import type { CapabilitySetJs } from './index';
3
+ /**
4
+ * Structural shape of the napi `NetMesh.listTools()` return value
5
+ * — field-for-field the same as [`ToolDescriptor`]. Declared here
6
+ * (instead of imported from `./index`) so this file compiles
7
+ * cleanly before `napi build` regenerates `index.d.ts` with the
8
+ * new `ToolDescriptorJs` export.
9
+ */
10
+ interface MeshWithListTools {
11
+ listTools(): ToolDescriptor[];
12
+ }
13
+ /**
14
+ * Structural shape of the napi `ToolWatchIter` — an async iterator
15
+ * over the substrate `watch_tools` stream. `next()` resolves to one
16
+ * JSON-encoded `ToolListChange`, or `null` when the stream ends /
17
+ * is closed. Declared here so this file compiles before `napi build`
18
+ * regenerates `index.d.ts` with the `ToolWatchIter` export.
19
+ */
20
+ interface NativeToolWatchIter {
21
+ next(): Promise<string | null>;
22
+ close(): void;
23
+ }
24
+ /**
25
+ * Structural shape of the napi `NetMesh.watchTools(intervalMs)`
26
+ * surface consumed by [`watchTools`].
27
+ */
28
+ interface MeshWithWatchTools {
29
+ watchTools(intervalMs?: number | null): Promise<NativeToolWatchIter>;
30
+ }
31
+ /**
32
+ * Discovery shape for an AI tool, as advertised on the capability
33
+ * fold. One row per `(tool_id, version)`; `nodeCount` is filled by
34
+ * the aggregating walk (`list_tools` once it lands).
35
+ *
36
+ * Schemas are stored as JSON-encoded strings (matching the Rust
37
+ * substrate's wire shape). Use `JSON.parse(desc.inputSchema)` to
38
+ * get the parsed JSON Schema object — most consumers want this
39
+ * for lowering into a provider tool definition.
40
+ *
41
+ * Wire-compatible 1:1 with `net::adapter::net::cortex::tool::ToolDescriptor`.
42
+ */
43
+ export interface ToolDescriptor {
44
+ /** nRPC service name. Same string `callTool(...)` takes. */
45
+ toolId: string;
46
+ /** Human-readable name. Defaults to `toolId` if unset. */
47
+ name: string;
48
+ /** Tool version (semver-ish). Defaults to `"1.0.0"`. */
49
+ version: string;
50
+ /** Human-readable description; LLMs read this to decide when to call. */
51
+ description?: string;
52
+ /** JSON-encoded JSON Schema (draft 2020-12) for the request body. */
53
+ inputSchema?: string;
54
+ /** JSON-encoded JSON Schema (draft 2020-12) for the response body. */
55
+ outputSchema?: string;
56
+ /** Required capabilities / dependencies (free-form strings). */
57
+ requires: string[];
58
+ /** Soft latency hint (ms); `0` = no estimate. */
59
+ estimatedTimeMs: number;
60
+ /** True if the tool is a pure function (no session state). */
61
+ stateless: boolean;
62
+ /** True if the tool is server-streaming (uses `serveToolStreaming`). */
63
+ streaming: boolean;
64
+ /** Free-form host-attached tags (e.g. `["web", "research"]`). */
65
+ tags: string[];
66
+ /** How many nodes currently serve this `(toolId, version)`. */
67
+ nodeCount: number;
68
+ }
69
+ /**
70
+ * One envelope on a streaming tool. Discriminated by `type`.
71
+ *
72
+ * Wire-compatible 1:1 with `net::adapter::net::cortex::tool::ToolEvent`
73
+ * (JSON tag form, `{"type": "start", …}` shape).
74
+ *
75
+ * Every stream ends with exactly one terminal event
76
+ * (`type: "result"` or `type: "error"`). Handlers that forget emit
77
+ * a synthesized `{type: "error", code: "missing_terminal", …}` from
78
+ * the Rust SDK's streaming wrapper.
79
+ */
80
+ export type ToolEvent = ToolEventStart | ToolEventProgress | ToolEventDelta | ToolEventResult | ToolEventError;
81
+ export interface ToolEventStart {
82
+ type: 'start';
83
+ toolId: string;
84
+ callId?: number;
85
+ metadata?: unknown;
86
+ }
87
+ export interface ToolEventProgress {
88
+ type: 'progress';
89
+ pct?: number;
90
+ message?: string;
91
+ }
92
+ export interface ToolEventDelta {
93
+ type: 'delta';
94
+ data: unknown;
95
+ }
96
+ export interface ToolEventResult {
97
+ type: 'result';
98
+ data: unknown;
99
+ }
100
+ export interface ToolEventError {
101
+ type: 'error';
102
+ code: string;
103
+ message: string;
104
+ details?: unknown;
105
+ }
106
+ /** True if `event` is a terminal envelope (`result` or `error`). */
107
+ export declare function isTerminalEvent(event: ToolEvent): boolean;
108
+ /**
109
+ * Options for `tool({...})` and `serveTool(rpc, options, handler)`.
110
+ * Mirror of the Rust `ToolMetadataBuilder` shape — caller supplies
111
+ * the fields that don't derive from a type signature in JS (no
112
+ * compile-time type system to introspect, unlike `schemars` in Rust).
113
+ *
114
+ * `inputSchema` / `outputSchema` are JSON-Schema-as-object (caller
115
+ * uses `zod-to-json-schema`, `pydantic`, or hand-rolls); we serialize
116
+ * to a string before stashing on the descriptor.
117
+ */
118
+ export interface ToolOptions {
119
+ /** nRPC service name + tool identifier. Required. */
120
+ name: string;
121
+ /** Human-readable description. Strongly recommended. */
122
+ description?: string;
123
+ /** Version. Defaults to `"1.0.0"`. */
124
+ version?: string;
125
+ /** JSON Schema object for the request. */
126
+ inputSchema?: object;
127
+ /** JSON Schema object for the response. */
128
+ outputSchema?: object;
129
+ /** Required capabilities / dependencies. */
130
+ requires?: string[];
131
+ /** Soft latency hint (ms). */
132
+ estimatedTimeMs?: number;
133
+ /** Pure-function flag. Default `true`. */
134
+ stateless?: boolean;
135
+ /** Free-form tags. */
136
+ tags?: string[];
137
+ }
138
+ /** Construct a [`ToolDescriptor`] from a `ToolOptions` literal. */
139
+ export declare function descriptorFrom(options: ToolOptions): ToolDescriptor;
140
+ /**
141
+ * Handler signature for `serveTool` — receives a decoded request,
142
+ * returns a decoded response (or a Promise of one).
143
+ */
144
+ export type ToolHandler<Req = unknown, Resp = unknown> = (req: Req) => Resp | Promise<Resp>;
145
+ /**
146
+ * Handle returned by `serveTool`. Calling `.close()` deregisters the
147
+ * underlying nRPC handler (mirror of the Rust `ToolServeHandle`'s
148
+ * Drop semantics). Calling `.close()` twice is idempotent; the
149
+ * second call is a no-op.
150
+ *
151
+ * NOTE: v1 does NOT yet integrate with the substrate-side
152
+ * `tool_registry`, so the `ai-tool:<toolId>` capability tag must be
153
+ * added to the caller's announce explicitly. See
154
+ * [`addToolCapabilitiesToAnnounce`] for the convention. Once the
155
+ * napi surface exposes `tool_registry()` insert/remove (a Wave 3
156
+ * follow-up), this handle will atomically reverse both the
157
+ * registry insert and the handler registration on `.close()`.
158
+ */
159
+ export interface ToolServeHandle {
160
+ /** The descriptor under which the tool was registered. */
161
+ readonly descriptor: ToolDescriptor;
162
+ /** Deregister the handler. Idempotent. */
163
+ close(): void;
164
+ }
165
+ /**
166
+ * Register an AI tool against `rpc`. The handler is registered as
167
+ * an nRPC service at `descriptor.toolId` with JSON codec (same as
168
+ * the Rust SDK's `Mesh::serve_tool`).
169
+ *
170
+ * Atomically:
171
+ * 1. Inserts the descriptor into a per-rpc local registry keyed on
172
+ * `toolId`. The next [`fetchToolMetadata`] call against this
173
+ * host can resolve the descriptor by name.
174
+ * 2. Registers the typed handler at `toolId` with JSON codec.
175
+ * 3. On the FIRST `serveTool` call against this rpc, lazy-
176
+ * installs the `tool.metadata.fetch` nRPC service handler so
177
+ * remote agents can pull the full descriptor for any
178
+ * registered tool. Subsequent `serveTool` calls reuse the
179
+ * same fetch handler. Mirrors the Rust SDK's
180
+ * `ensure_tool_metadata_fetch_installed` pattern.
181
+ *
182
+ * The caller is still responsible for announcing the tool to
183
+ * peers — use [`addToolCapabilitiesToAnnounce`] on the
184
+ * `CapabilitySetJs` you pass to `mesh.announceCapabilities(...)`.
185
+ *
186
+ * On `handle.close()`: removes the descriptor from the per-rpc
187
+ * registry and unregisters the handler. The lazy `tool.metadata.fetch`
188
+ * service stays installed for the lifetime of the rpc — harmless
189
+ * when empty (returns NotFound for every request).
190
+ */
191
+ export declare function serveTool<Req = unknown, Resp = unknown>(rpc: TypedMeshRpc, options: ToolOptions, handler: ToolHandler<Req, Resp>): ToolServeHandle;
192
+ /**
193
+ * Streaming-handler shape for `serveToolStreaming`. The handler
194
+ * is an async generator that yields `ToolEvent` envelopes — each
195
+ * yielded event is JSON-encoded and pushed onto the wire via the
196
+ * substrate's response sink. The generator returning normally is
197
+ * the "handler done" signal; if the generator never yields a
198
+ * terminal `result` / `error` envelope, callers see the
199
+ * synthesized `missing_terminal` error (the T-2 contract).
200
+ *
201
+ * Throwing inside the generator maps to a terminal error frame on
202
+ * the wire; the caller's `callToolStreaming` sees it as a normal
203
+ * `ToolEvent` with `type: "error"`.
204
+ */
205
+ export type StreamingToolHandler<Req = unknown> = (req: Req) => AsyncIterable<ToolEvent>;
206
+ /**
207
+ * Register a streaming tool handler. The handler is an async
208
+ * generator that yields ToolEvents — each yielded event is
209
+ * forwarded to the caller via `callToolStreaming` (or any other
210
+ * client that drains a `tool.metadata.fetch`-discoverable
211
+ * streaming service).
212
+ *
213
+ * Atomic register + lazy auto-install of `tool.metadata.fetch` —
214
+ * same pattern as `serveTool` for unary handlers. Stamps
215
+ * `streaming: true` on the descriptor so peers can discover the
216
+ * streaming variant explicitly.
217
+ */
218
+ export declare function serveToolStreaming<Req = unknown>(rpc: TypedMeshRpc, options: ToolOptions, handler: StreamingToolHandler<Req>): ToolServeHandle;
219
+ /**
220
+ * Capability-routed unary tool invocation. Encodes `req` as JSON
221
+ * (the codec every AI provider consumes for tool input/output),
222
+ * dispatches via `rpc.callService(toolId, req, opts)`.
223
+ *
224
+ * Throws `NoRouteError` if no host advertises `nrpc:<toolId>` in
225
+ * the local capability fold; bubbles handler errors as
226
+ * `RpcServerError` with the typed-handler status code.
227
+ */
228
+ export declare function callTool<Req = unknown, Resp = unknown>(rpc: TypedMeshRpc, toolId: string, req: Req, opts?: CallOptions): Promise<Resp>;
229
+ /**
230
+ * Capability-routed streaming tool invocation. Returns an
231
+ * `AsyncIterable<ToolEvent>` — drain via `for await (...)` until
232
+ * the stream terminates. The substrate routes the call via the
233
+ * cap-auth gate just like `callService`; the iterator yields each
234
+ * JSON-decoded `ToolEvent` envelope.
235
+ *
236
+ * Synthesizes a terminal `error` event with code
237
+ * `missing_terminal` if the stream ends without a `result` /
238
+ * `error` envelope — matches the Rust SDK's `serve_tool_streaming`
239
+ * contract and the T-2 cross-language fixture.
240
+ *
241
+ * Cancel mid-stream by aborting `opts.signal` (wired through the
242
+ * substrate's cancel-registry on the underlying RpcStream).
243
+ */
244
+ export declare function callToolStreaming<Req = unknown>(rpc: TypedMeshRpc, toolId: string, req: Req, opts?: CallOptions): AsyncGenerator<ToolEvent, void, void>;
245
+ /**
246
+ * Merge tool descriptors into a `CapabilitySetJs` so the next
247
+ * `mesh.announceCapabilities(caps)` carries:
248
+ *
249
+ * - `ai-tool:<toolId>` tag — peer fold's tag-prefix lookup hits.
250
+ * - A `ToolJs` entry — peer's `list_tools` walk sees the
251
+ * tool's tag-encoded fields.
252
+ *
253
+ * Caller still owns the `caps` object — pass it through
254
+ * `mesh.announceCapabilities(caps)` to publish. Returns the same
255
+ * object for chaining.
256
+ *
257
+ * This is a v1 convenience; once the napi surface exposes
258
+ * `tool_registry()`, the announce-time merge happens
259
+ * automatically and this helper becomes optional.
260
+ */
261
+ export declare function addToolCapabilitiesToAnnounce(caps: CapabilitySetJs, descriptors: ToolDescriptor[]): CapabilitySetJs;
262
+ /**
263
+ * Walk the local capability fold for every published AI tool.
264
+ * Returns one [`ToolDescriptor`] per `(toolId, version)` slot,
265
+ * with `nodeCount` filled in by the aggregating walk.
266
+ *
267
+ * Pure delegation to the napi binding's `NetMesh.listTools()` (B-3
268
+ * of the plan). Requires the napi binding's `tool` Cargo feature
269
+ * (default-on); throws if the underlying mesh wasn't built with it.
270
+ *
271
+ * Schemas come back as JSON-encoded strings on
272
+ * `descriptor.inputSchema` / `descriptor.outputSchema` — call
273
+ * `JSON.parse(...)` for the parsed shape that adapter packages
274
+ * consume when lowering into provider-specific tool definitions.
275
+ */
276
+ export declare function listTools(mesh: MeshWithListTools): ToolDescriptor[];
277
+ /**
278
+ * One change in the set of tools visible to the local capability
279
+ * fold. Discriminated union on `type`; identity for diffing is
280
+ * `(toolId, version)`.
281
+ *
282
+ * Wire-compatible 1:1 with the Rust SDK's `ToolListChange` enum
283
+ * (JSON tag-form `{ "type": "added", "descriptor": {...} }` /
284
+ * `"removed"` / `"node_count_changed"`).
285
+ */
286
+ export type ToolListChange = {
287
+ type: 'added';
288
+ descriptor: ToolDescriptor;
289
+ } | {
290
+ type: 'removed';
291
+ descriptor: ToolDescriptor;
292
+ } | {
293
+ type: 'node_count_changed';
294
+ descriptor: ToolDescriptor;
295
+ prevNodeCount: number;
296
+ };
297
+ /** Options for [`watchTools`]. */
298
+ export interface WatchToolsOptions {
299
+ /**
300
+ * Debounce ceiling in milliseconds — NOT a poll cadence.
301
+ *
302
+ * Omitted / `0` is pure event-driven: a change is delivered the
303
+ * moment the capability fold mutates, and an idle fold does zero
304
+ * periodic work. A positive value additionally guarantees a
305
+ * re-diff at least every `intervalMs` as a safety net.
306
+ */
307
+ intervalMs?: number;
308
+ /**
309
+ * `AbortSignal` to end the watch. Aborting closes the underlying
310
+ * native iterator, which ends the stream promptly.
311
+ */
312
+ signal?: AbortSignal;
313
+ }
314
+ /**
315
+ * Subscribe to a stream of [`ToolListChange`] events for every
316
+ * dynamic addition / removal / publisher-count change in the
317
+ * local capability fold's tool view.
318
+ *
319
+ * Event-driven: consumes the substrate's `MeshNode::watch_tools`
320
+ * stream via the napi `ToolWatchIter` — a change is delivered the
321
+ * moment the fold mutates (latency is bounded by fold-apply, not a
322
+ * timer), and an idle fold does zero periodic work. The diff happens
323
+ * substrate-side; this just `JSON.parse`s each emitted change. No
324
+ * client-side `setTimeout` / `listTools` re-diff loop.
325
+ *
326
+ * The native subscription is kicked off eagerly — when `watchTools`
327
+ * is *called*, not on the first iteration — so a change published
328
+ * between the call and the first `for await` is not lost (the prior
329
+ * version subscribed lazily and could drop that first event). Because
330
+ * the subscription is started at call time, the returned iterable
331
+ * holds a live substrate watch: consume it (or abort via `signal`) so
332
+ * it is closed. Call `listTools(mesh)` once for the starting shape.
333
+ *
334
+ * Mirror of the Rust SDK's `Mesh::watch_tools(matcher, interval)`
335
+ * and the Python `watch_tools` — all three are event-driven off the
336
+ * same substrate change signal, and all three subscribe eagerly.
337
+ *
338
+ * Returns an `AsyncIterable<ToolListChange>` suitable for
339
+ * `for await (const change of watchTools(mesh)) { ... }`. The
340
+ * iterator ends when `options.signal` aborts, when the stream is
341
+ * closed, or on an unrecoverable error.
342
+ */
343
+ export declare function watchTools(mesh: MeshWithWatchTools, options?: WatchToolsOptions): AsyncIterable<ToolListChange>;
344
+ /** nRPC service name for the on-demand tool-descriptor pull. */
345
+ export declare const TOOL_METADATA_FETCH_SERVICE = "tool.metadata.fetch";
346
+ /** Wire-shape variants of `ToolMetadataResponse` (JSON-tagged on
347
+ * `type`, snake_case). Pinned by the substrate's
348
+ * `cortex::tool::ToolMetadataResponse` enum.
349
+ */
350
+ export type ToolMetadataResponse = {
351
+ type: 'found';
352
+ descriptor: ToolDescriptor;
353
+ } | {
354
+ type: 'not_found';
355
+ name: string;
356
+ };
357
+ /**
358
+ * Pull a tool's full descriptor from a specific host by calling
359
+ * the auto-installed `tool.metadata.fetch` nRPC service. Useful
360
+ * when the local fold's capability-fold entry dropped the schema
361
+ * (size-budget-exceeded) and the agent needs the full
362
+ * input/output schemas for strict-mode provider lowering.
363
+ *
364
+ * Mirror of calling `mesh.call_typed(host, TOOL_METADATA_FETCH_SERVICE,
365
+ * { name: tool_id })` in the Rust SDK. The
366
+ * `tool.metadata.fetch` server-side handler is auto-installed on
367
+ * the host's first `serveTool` call.
368
+ */
369
+ export declare function fetchToolMetadata(rpc: TypedMeshRpc, hostNodeId: bigint, toolId: string, opts?: CallOptions): Promise<ToolMetadataResponse>;
370
+ /** Canonical hand-off between a provider adapter and `callTool`. */
371
+ export interface ToolCallSpec {
372
+ /** nRPC tool_id to invoke. */
373
+ name: string;
374
+ /** JSON-encoded arguments to pass to `callTool` (caller parses). */
375
+ argumentsJson: string;
376
+ /** Provider-supplied call id when present (for reply correlation). */
377
+ providerCallId?: string;
378
+ }
379
+ /** Thrown when a provider's tool-call reply doesn't match its spec. */
380
+ export declare class ToolCallParseError extends Error {
381
+ constructor(message: string);
382
+ }
383
+ /** OpenAI Chat Completions / Responses API `tools` array. */
384
+ export declare const openai: {
385
+ /**
386
+ * Lower a descriptor to an OpenAI tool definition. Shape:
387
+ * ```
388
+ * { type: "function", function: { name, description, parameters, strict } }
389
+ * ```
390
+ * `strict` is true when the descriptor carried an `inputSchema`.
391
+ */
392
+ toOpenaiTool(desc: ToolDescriptor): object;
393
+ /**
394
+ * Parse one OpenAI `tool_calls[]` entry into a `ToolCallSpec`.
395
+ * OpenAI's `function.arguments` is a JSON-encoded STRING; this
396
+ * helper validates it parses up front so malformed payloads fail
397
+ * fast instead of riding through `callTool`.
398
+ */
399
+ lowerOpenaiToolCall(call: Record<string, unknown>): ToolCallSpec;
400
+ };
401
+ /** Anthropic Messages API `tools` array + `tool_use` content blocks. */
402
+ export declare const anthropic: {
403
+ /**
404
+ * Lower a descriptor to an Anthropic tool definition. Shape:
405
+ * ```
406
+ * { name, description, input_schema }
407
+ * ```
408
+ * No tool-level `strict` flag — Anthropic relies on schema-
409
+ * validated tool input as the default.
410
+ */
411
+ toAnthropicTool(desc: ToolDescriptor): object;
412
+ /**
413
+ * Parse one Anthropic `tool_use` content block into a
414
+ * `ToolCallSpec`. `input` is already a parsed object (not a
415
+ * string like OpenAI); re-serializes once to preserve the
416
+ * `argumentsJson: string` invariant.
417
+ */
418
+ lowerAnthropicToolUse(block: Record<string, unknown>): ToolCallSpec;
419
+ };
420
+ /** Model Context Protocol `tools/list` + `tools/call`. */
421
+ export declare const mcp: {
422
+ /** Lower a descriptor to an MCP tool definition. Shape: `{ name, description, inputSchema }` (camelCase). */
423
+ toMcpTool(desc: ToolDescriptor): object;
424
+ /**
425
+ * Parse an MCP `tools/call` request's `params` into a
426
+ * `ToolCallSpec`. `providerCallId` is left `undefined` — MCP's
427
+ * JSON-RPC `id` lives one envelope layer up, threaded
428
+ * independently.
429
+ */
430
+ lowerMcpToolsCall(params: Record<string, unknown>): ToolCallSpec;
431
+ };
432
+ /** Gemini `generateContent` function-calling shape. */
433
+ export declare const gemini: {
434
+ /**
435
+ * Lower a descriptor to one Gemini `FunctionDeclaration`. Shape:
436
+ * ```
437
+ * { name, description, parameters }
438
+ * ```
439
+ * Caller wraps these into the outer
440
+ * `tools: [{ function_declarations: [ … ] }]` array.
441
+ */
442
+ toGeminiFunctionDeclaration(desc: ToolDescriptor): object;
443
+ /**
444
+ * Parse one Gemini `functionCall` part into a `ToolCallSpec`.
445
+ * Gemini has no per-call id; the spec leaves `providerCallId`
446
+ * `undefined` (multi-call sequences are positional).
447
+ */
448
+ lowerGeminiFunctionCall(call: Record<string, unknown>): ToolCallSpec;
449
+ };
450
+ export {};