@silkweave/core 3.0.0 → 3.2.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/README.md +4 -2
- package/build/index.d.mts +137 -8
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +132 -14
- package/build/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,14 +62,16 @@ Returns a builder with `.adapter()`, `.action()`, `.actions()`, `.set()`, and `.
|
|
|
62
62
|
| `name` | `string` | Unique action identifier |
|
|
63
63
|
| `description` | `string` | Human-readable description |
|
|
64
64
|
| `input` | `z.ZodObject` | Zod schema for input validation |
|
|
65
|
-
| `output` | `z.ZodObject` | Optional Zod schema for the return type (used by typegen and
|
|
65
|
+
| `output` | `z.ZodObject` | Optional Zod schema for the return type (used by typegen, Fastify OpenAPI, and as the MCP `outputSchema` contract when `disposition: 'structured'`). Mutually exclusive with `chunk`. |
|
|
66
66
|
| `chunk` | `z.ZodType` | Optional Zod schema for individual chunks yielded by a streaming `run`. Required (and `run` must be an `async function*`) to make this a streaming action. See [Streaming Actions](#streaming-actions). |
|
|
67
67
|
| `kind` | `'query' \| 'mutation'` | Optional. Defaults to `'mutation'`. Marks the action as a cacheable read for tRPC. |
|
|
68
68
|
| `method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE'` | Optional REST verb for `@silkweave/fastify` / `@silkweave/nestjs` `rest`. Defaults to `POST`, or `GET` when `kind: 'query'`. See [REST routing](#rest-routing). |
|
|
69
69
|
| `path` | `string` | Optional REST route template, may contain `:param` placeholders (e.g. `'spaces/:spaceId/users'`). Each placeholder must be a key of `input`. See [REST routing](#rest-routing). |
|
|
70
70
|
| `queryParams` | `(keyof I)[]` | Optional input fields read from the URL query string instead of the body (e.g. `['offset', 'limit']`). See [REST routing](#rest-routing). |
|
|
71
71
|
| `args` | `(keyof I)[]` | Fields to expose as CLI positional arguments |
|
|
72
|
-
| `disposition` | `'json' \| 'smart'` | Optional
|
|
72
|
+
| `disposition` | `'json' \| 'smart' \| 'structured'` | Optional MCP result format - `'json'` (default) ⇒ `jsonToolResult`, `'smart'` ⇒ `smartToolResult` (sideloads payloads > 4096 chars), `'structured'` ⇒ declares `output` as the tool's MCP `outputSchema` contract and ships schema-parsed `structuredContent`. A client's `_meta.disposition` overrides `'json'`/`'smart'`; `'structured'` ignores it (the contract is fixed at `tools/list` time). `'structured'` requires a non-streaming action with `output` (validated at registration by `validateActionDisposition()`). MCP adapters only. |
|
|
73
|
+
| `annotations` | `ToolAnnotations` | Optional MCP tool annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`, `title`) forwarded to `tools/list`. MCP adapters derive `readOnlyHint` from `kind` (`'query'` ⇒ `true`) and merge explicit annotations over the derived base. MCP adapters only. |
|
|
74
|
+
| `tags` | `string[]` | Optional free-form grouping labels (e.g. `['leads', 'write']`). Carried on the action for the MCP adapters' per-request `filterActions` (and other consumers) to match on; no behavior in core itself. |
|
|
73
75
|
| `isEnabled` | `(context) => boolean` | Gate action availability per adapter |
|
|
74
76
|
| `run` | `(input, context) => Promise<O>` *or* `async function*(input, context): AsyncGenerator<Chunk>` | The action implementation. Use a regular `async` function for buffered request/response; use an `async function*` and declare `chunk` to stream. |
|
|
75
77
|
| `toolResult` | `(response, context) => CallToolResult \| undefined` | Custom MCP result formatting (optional). For streaming actions, `response` is the buffered array of chunks (used when the client did not request streaming). |
|
package/build/index.d.mts
CHANGED
|
@@ -15,6 +15,25 @@ declare function createContext(store?: Record<string, unknown>): SilkweaveContex
|
|
|
15
15
|
//#region src/util/action.d.ts
|
|
16
16
|
type ActionKind = 'query' | 'mutation';
|
|
17
17
|
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
18
|
+
/**
|
|
19
|
+
* MCP tool annotations (spec `ToolAnnotations`) - client-facing hints about a
|
|
20
|
+
* tool's behavior, used by MCP hosts to group and permission-gate tools. All
|
|
21
|
+
* hints are advisory. Structurally compatible with the MCP SDK's
|
|
22
|
+
* `ToolAnnotations`, defined here so non-MCP packages can set them without a
|
|
23
|
+
* type dependency on the SDK.
|
|
24
|
+
*/
|
|
25
|
+
interface ToolAnnotations {
|
|
26
|
+
/** Human-readable title override for the tool. */
|
|
27
|
+
title?: string;
|
|
28
|
+
/** The tool does not modify its environment. */
|
|
29
|
+
readOnlyHint?: boolean;
|
|
30
|
+
/** The tool may perform destructive updates (only meaningful when not read-only). */
|
|
31
|
+
destructiveHint?: boolean;
|
|
32
|
+
/** Repeated calls with the same arguments have no additional effect. */
|
|
33
|
+
idempotentHint?: boolean;
|
|
34
|
+
/** The tool may interact with an open world of external entities. */
|
|
35
|
+
openWorldHint?: boolean;
|
|
36
|
+
}
|
|
18
37
|
type ActionRun<I, O> = (input: I, context: SilkweaveContext) => Promise<O>;
|
|
19
38
|
type ActionStreamRun<I, C> = (input: I, context: SilkweaveContext) => AsyncGenerator<C, void, void>;
|
|
20
39
|
interface Action<I extends object = any, O extends object = any, N extends string = string, K extends ActionKind = ActionKind, C = any> {
|
|
@@ -54,14 +73,33 @@ interface Action<I extends object = any, O extends object = any, N extends strin
|
|
|
54
73
|
queryParams?: (keyof I)[];
|
|
55
74
|
args?: (keyof I)[];
|
|
56
75
|
/**
|
|
57
|
-
*
|
|
58
|
-
* response with `jsonToolResult` (compact JSON text)
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* `
|
|
76
|
+
* MCP result disposition for this action. `'json'` (the default when unset)
|
|
77
|
+
* formats the response with `jsonToolResult` (compact JSON text); `'smart'`
|
|
78
|
+
* uses `smartToolResult` (inlines small payloads, offloads large ones to an
|
|
79
|
+
* embedded resource); `'structured'` declares the `output` schema as an MCP
|
|
80
|
+
* `outputSchema` contract - the result is parsed through `output` (stripping
|
|
81
|
+
* extra fields) and shipped as `structuredContent` alongside a JSON text
|
|
82
|
+
* mirror. `'json'`/`'smart'` are only defaults a client's `_meta.disposition`
|
|
83
|
+
* can override; a `'structured'` action ignores `_meta.disposition`, since
|
|
84
|
+
* its schema contract is fixed at `tools/list` time. `'structured'` requires
|
|
85
|
+
* a non-streaming action with an `output` schema (validated at registration
|
|
86
|
+
* via `validateActionDisposition()`). Ignored by non-MCP adapters.
|
|
87
|
+
*/
|
|
88
|
+
disposition?: 'json' | 'smart' | 'structured';
|
|
89
|
+
/**
|
|
90
|
+
* MCP tool annotations forwarded to `tools/list`. When unset, MCP adapters
|
|
91
|
+
* derive `readOnlyHint` from `kind` (`'query'` ⇒ read-only); explicit
|
|
92
|
+
* annotations are merged over the derived base, so setting e.g. only
|
|
93
|
+
* `destructiveHint` keeps the derived `readOnlyHint`. Ignored by non-MCP
|
|
62
94
|
* adapters.
|
|
63
95
|
*/
|
|
64
|
-
|
|
96
|
+
annotations?: ToolAnnotations;
|
|
97
|
+
/**
|
|
98
|
+
* Free-form grouping labels (e.g. `['leads', 'write']`). Carried on the
|
|
99
|
+
* action for per-request filtering (`filterActions` in the MCP adapters) and
|
|
100
|
+
* other consumers; no behavior in core itself.
|
|
101
|
+
*/
|
|
102
|
+
tags?: string[];
|
|
65
103
|
isEnabled?: (context: SilkweaveContext) => boolean;
|
|
66
104
|
run: ActionRun<I, O> | ActionStreamRun<I, C>;
|
|
67
105
|
toolResult?: (response: O, context: SilkweaveContext) => CallToolResult | undefined;
|
|
@@ -80,7 +118,9 @@ interface NonStreamingActionInput<I extends object, O extends object, N extends
|
|
|
80
118
|
path?: string;
|
|
81
119
|
queryParams?: (keyof I)[];
|
|
82
120
|
args?: (keyof I)[];
|
|
83
|
-
disposition?: 'json' | 'smart';
|
|
121
|
+
disposition?: 'json' | 'smart' | 'structured';
|
|
122
|
+
annotations?: ToolAnnotations;
|
|
123
|
+
tags?: string[];
|
|
84
124
|
isEnabled?: (context: SilkweaveContext) => boolean;
|
|
85
125
|
run: ActionRun<I, O>;
|
|
86
126
|
toolResult?: (response: O, context: SilkweaveContext) => CallToolResult | undefined;
|
|
@@ -98,6 +138,8 @@ interface StreamingActionInput<I extends object, C, N extends string, K extends
|
|
|
98
138
|
queryParams?: (keyof I)[];
|
|
99
139
|
args?: (keyof I)[];
|
|
100
140
|
disposition?: 'json' | 'smart';
|
|
141
|
+
annotations?: ToolAnnotations;
|
|
142
|
+
tags?: string[];
|
|
101
143
|
isEnabled?: (context: SilkweaveContext) => boolean;
|
|
102
144
|
run: ActionStreamRun<I, C>;
|
|
103
145
|
toolResult?: (response: C[], context: SilkweaveContext) => CallToolResult | undefined;
|
|
@@ -110,6 +152,15 @@ declare function createAction<I extends object, O extends object, N extends stri
|
|
|
110
152
|
* request/response and streaming chunk delivery.
|
|
111
153
|
*/
|
|
112
154
|
declare function isStreamingAction(action: Action): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Validate an action's MCP `disposition` at registration time. `'structured'`
|
|
157
|
+
* declares the `output` schema as a hard MCP `outputSchema` contract, so it
|
|
158
|
+
* requires a non-streaming action with an `output` schema - anything else is a
|
|
159
|
+
* misconfiguration surfaced at boot rather than a per-call protocol error.
|
|
160
|
+
* MCP adapters call this from `start()` (or, for per-request transports, when
|
|
161
|
+
* the handler is built).
|
|
162
|
+
*/
|
|
163
|
+
declare function validateActionDisposition(action: Action): void;
|
|
113
164
|
//#endregion
|
|
114
165
|
//#region src/util/adapter.d.ts
|
|
115
166
|
interface SilkweaveOptions {
|
|
@@ -215,6 +266,42 @@ declare function lintActions(actions: Action[]): ActionLintWarning[];
|
|
|
215
266
|
*/
|
|
216
267
|
declare function reportActionLint(actions: Action[], warn?: (message: string) => void): ActionLintWarning[];
|
|
217
268
|
//#endregion
|
|
269
|
+
//#region src/util/logger.d.ts
|
|
270
|
+
interface MessageOptions {
|
|
271
|
+
message: string;
|
|
272
|
+
}
|
|
273
|
+
interface ProgressOptions {
|
|
274
|
+
progress: number;
|
|
275
|
+
total?: number;
|
|
276
|
+
message?: string;
|
|
277
|
+
}
|
|
278
|
+
declare const LogLevels: readonly ["error", "debug", "info", "notice", "warning", "critical", "alert", "emergency"];
|
|
279
|
+
type LogLevel = typeof LogLevels[number];
|
|
280
|
+
type LogFn = (data: unknown) => void;
|
|
281
|
+
interface Logger extends Record<LogLevel, LogFn> {
|
|
282
|
+
progress: (options: ProgressOptions) => void;
|
|
283
|
+
}
|
|
284
|
+
interface CreateLoggerOptions {
|
|
285
|
+
name?: string;
|
|
286
|
+
/** Minimum severity to write to `stream`. Defaults to `'debug'` (everything). */
|
|
287
|
+
level?: LogLevel;
|
|
288
|
+
/**
|
|
289
|
+
* Destination stream for structured log lines, or `false` to discard them.
|
|
290
|
+
* Defaults to `process.stdout`. The `onLog` callback always fires regardless
|
|
291
|
+
* of this setting - the stream is just the diagnostic sink.
|
|
292
|
+
*/
|
|
293
|
+
stream?: NodeJS.WritableStream | false;
|
|
294
|
+
onLog?: (level: LogLevel, data: unknown) => void;
|
|
295
|
+
onProgress?: (options: ProgressOptions) => void;
|
|
296
|
+
}
|
|
297
|
+
declare function createLogger(options?: CreateLoggerOptions): Logger;
|
|
298
|
+
declare function buildLogLevels(fn: (level: LogLevel, data: unknown) => void): Record<LogLevel, LogFn>;
|
|
299
|
+
/**
|
|
300
|
+
* Human-readable `console`-backed logger for terminal contexts. Strings are
|
|
301
|
+
* written verbatim; objects are JSON-stringified. Zero dependencies.
|
|
302
|
+
*/
|
|
303
|
+
declare function createConsoleLogger(): Logger;
|
|
304
|
+
//#endregion
|
|
218
305
|
//#region src/util/streaming.d.ts
|
|
219
306
|
/**
|
|
220
307
|
* Drive a streaming action's async generator. Calls `onChunk` once per yielded
|
|
@@ -228,6 +315,48 @@ declare function reportActionLint(actions: Action[], warn?: (message: string) =>
|
|
|
228
315
|
*/
|
|
229
316
|
declare function runStreamingAction<C>(action: Action<any, any, string, any, C>, input: object, context: SilkweaveContext, onChunk?: (chunk: C, index: number) => void | Promise<void>): Promise<C[]>;
|
|
230
317
|
//#endregion
|
|
318
|
+
//#region src/util/telemetry.d.ts
|
|
319
|
+
/**
|
|
320
|
+
* One tool/procedure invocation, as reported to an `OnToolCall` hook. MCP
|
|
321
|
+
* adapters emit these from the tool registrar (where result formatting
|
|
322
|
+
* happens, so `resultBytes`/`sideloaded` are known); other transports emit
|
|
323
|
+
* from their own invocation seam and omit the MCP-only fields.
|
|
324
|
+
*/
|
|
325
|
+
interface ToolCallEvent {
|
|
326
|
+
/** Action name (e.g. `'leads.bulkUpdate'`). */
|
|
327
|
+
action: string;
|
|
328
|
+
/** Wire name the client called (e.g. `'LeadsBulkUpdate'` over MCP, `'leadsBulkUpdate'` over tRPC). */
|
|
329
|
+
tool: string;
|
|
330
|
+
transport: 'mcp' | 'trpc' | 'rest' | 'cli';
|
|
331
|
+
/** Wall-clock duration; for streaming actions this spans full generator consumption. */
|
|
332
|
+
durationMs: number;
|
|
333
|
+
/**
|
|
334
|
+
* `false` when the action threw or the formatted result is an MCP
|
|
335
|
+
* `isError` tool result. `errorCode`/`errorMessage` are set for thrown
|
|
336
|
+
* errors (a `SilkweaveError`'s `code`, else the error's `name`).
|
|
337
|
+
*/
|
|
338
|
+
ok: boolean;
|
|
339
|
+
errorCode?: string;
|
|
340
|
+
errorMessage?: string;
|
|
341
|
+
/** Serialized (JSON) size of the raw result - MCP-layer events only. */
|
|
342
|
+
resultBytes?: number;
|
|
343
|
+
/** Whether `smartToolResult` offloaded the payload to an embedded resource - MCP-layer events only. */
|
|
344
|
+
sideloaded?: boolean;
|
|
345
|
+
/** The per-call action context (access to `auth`/`request` for spaceId, apiKeyId, ...). */
|
|
346
|
+
context: SilkweaveContext;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Telemetry hook invoked once per tool call, fire-and-forget: adapters never
|
|
350
|
+
* await it on the result path and swallow (log) its errors, so the hook can
|
|
351
|
+
* never fail, slow, or reorder a call.
|
|
352
|
+
*/
|
|
353
|
+
type OnToolCall = (event: ToolCallEvent) => void | Promise<void>;
|
|
354
|
+
/**
|
|
355
|
+
* Invoke an `OnToolCall` hook with fire-and-forget semantics - sync throws and
|
|
356
|
+
* async rejections are logged to stderr and never propagate to the call path.
|
|
357
|
+
*/
|
|
358
|
+
declare function emitToolCall(hook: OnToolCall | undefined, event: ToolCallEvent): void;
|
|
359
|
+
//#endregion
|
|
231
360
|
//#region src/util/zod.d.ts
|
|
232
361
|
interface UnwrapResult {
|
|
233
362
|
defaultValue?: any;
|
|
@@ -237,5 +366,5 @@ interface UnwrapResult {
|
|
|
237
366
|
}
|
|
238
367
|
declare function unwrap(type: z.ZodTypeAny, result?: UnwrapResult): [z.ZodTypeAny, UnwrapResult];
|
|
239
368
|
//#endregion
|
|
240
|
-
export { Action, ActionInputSources, ActionKind, ActionLintCode, ActionLintWarning, ActionRun, ActionStreamRun, Adapter, AdapterFactory, AdapterGenerator, HttpMethod, NonStreamingActionInput, Silkweave, SilkweaveContext, SilkweaveError, type SilkweaveOptions, StreamingActionInput, UnwrapResult, actionMethod, badRequest, createAction, createContext, forbidden, internal, isStreamingAction, lintActions, methodHasBody, notFound, pathParamNames, reportActionLint, resolveActionInput, runStreamingAction, silkweave, unwrap, validateActionRouting };
|
|
369
|
+
export { Action, ActionInputSources, ActionKind, ActionLintCode, ActionLintWarning, ActionRun, ActionStreamRun, Adapter, AdapterFactory, AdapterGenerator, CreateLoggerOptions, HttpMethod, LogFn, LogLevel, LogLevels, Logger, MessageOptions, NonStreamingActionInput, OnToolCall, ProgressOptions, Silkweave, SilkweaveContext, SilkweaveError, type SilkweaveOptions, StreamingActionInput, ToolAnnotations, ToolCallEvent, UnwrapResult, actionMethod, badRequest, buildLogLevels, createAction, createConsoleLogger, createContext, createLogger, emitToolCall, forbidden, internal, isStreamingAction, lintActions, methodHasBody, notFound, pathParamNames, reportActionLint, resolveActionInput, runStreamingAction, silkweave, unwrap, validateActionDisposition, validateActionRouting };
|
|
241
370
|
//# sourceMappingURL=index.d.mts.map
|
package/build/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/util/context.ts","../src/util/action.ts","../src/util/adapter.ts","../src/lib/silkweave.ts","../src/util/error.ts","../src/util/http.ts","../src/util/lint.ts","../src/util/streaming.ts","../src/util/zod.ts"],"mappings":";;;;UAAiB,gBAAA;EACf,IAAA;EACA,GAAA,GAAM,GAAA;EACN,GAAA,MAAS,GAAA,aAAgB,CAAA;EACzB,WAAA,MAAiB,GAAA,aAAgB,CAAA;EACjC,GAAA,MAAS,GAAA,UAAa,KAAA,EAAO,CAAA;EAC7B,IAAA,GAAO,KAAA,GAAQ,MAAA,sBAA4B,gBAAA;AAAA;AAAA,iBAG7B,aAAA,CAAc,KAAA,GAAO,MAAA,oBAA+B,gBAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/util/context.ts","../src/util/action.ts","../src/util/adapter.ts","../src/lib/silkweave.ts","../src/util/error.ts","../src/util/http.ts","../src/util/lint.ts","../src/util/logger.ts","../src/util/streaming.ts","../src/util/telemetry.ts","../src/util/zod.ts"],"mappings":"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"}
|
package/build/index.mjs
CHANGED
|
@@ -141,19 +141,6 @@ function silkweave(options) {
|
|
|
141
141
|
return builder;
|
|
142
142
|
}
|
|
143
143
|
//#endregion
|
|
144
|
-
//#region src/util/action.ts
|
|
145
|
-
function createAction(action) {
|
|
146
|
-
return action;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Returns `true` when `action.run` is an async generator function (declared
|
|
150
|
-
* with `async function*`). Adapters use this to switch between buffered
|
|
151
|
-
* request/response and streaming chunk delivery.
|
|
152
|
-
*/
|
|
153
|
-
function isStreamingAction(action) {
|
|
154
|
-
return action.run?.constructor?.name === "AsyncGeneratorFunction";
|
|
155
|
-
}
|
|
156
|
-
//#endregion
|
|
157
144
|
//#region src/util/error.ts
|
|
158
145
|
var SilkweaveError = class extends Error {
|
|
159
146
|
constructor(message, code, statusCode = 500) {
|
|
@@ -176,6 +163,32 @@ function internal(message = "Internal error") {
|
|
|
176
163
|
return new SilkweaveError(message, "internal", 500);
|
|
177
164
|
}
|
|
178
165
|
//#endregion
|
|
166
|
+
//#region src/util/action.ts
|
|
167
|
+
function createAction(action) {
|
|
168
|
+
return action;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Returns `true` when `action.run` is an async generator function (declared
|
|
172
|
+
* with `async function*`). Adapters use this to switch between buffered
|
|
173
|
+
* request/response and streaming chunk delivery.
|
|
174
|
+
*/
|
|
175
|
+
function isStreamingAction(action) {
|
|
176
|
+
return action.run?.constructor?.name === "AsyncGeneratorFunction";
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Validate an action's MCP `disposition` at registration time. `'structured'`
|
|
180
|
+
* declares the `output` schema as a hard MCP `outputSchema` contract, so it
|
|
181
|
+
* requires a non-streaming action with an `output` schema - anything else is a
|
|
182
|
+
* misconfiguration surfaced at boot rather than a per-call protocol error.
|
|
183
|
+
* MCP adapters call this from `start()` (or, for per-request transports, when
|
|
184
|
+
* the handler is built).
|
|
185
|
+
*/
|
|
186
|
+
function validateActionDisposition(action) {
|
|
187
|
+
if (action.disposition !== "structured") return;
|
|
188
|
+
if (isStreamingAction(action) || action.chunk) throw new SilkweaveError(`Action '${action.name}': disposition 'structured' is not supported on a streaming action - there is no single result to validate against an output schema`, "invalid_action");
|
|
189
|
+
if (!action.output) throw new SilkweaveError(`Action '${action.name}': disposition 'structured' requires an 'output' schema - it becomes the tool's MCP outputSchema contract`, "invalid_action");
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
179
192
|
//#region src/util/http.ts
|
|
180
193
|
const PATH_PARAM_RE = /:([A-Za-z0-9_]+)/g;
|
|
181
194
|
/**
|
|
@@ -266,6 +279,95 @@ function resolveActionInput(action, sources) {
|
|
|
266
279
|
return merged;
|
|
267
280
|
}
|
|
268
281
|
//#endregion
|
|
282
|
+
//#region src/util/logger.ts
|
|
283
|
+
const LogLevels = [
|
|
284
|
+
"error",
|
|
285
|
+
"debug",
|
|
286
|
+
"info",
|
|
287
|
+
"notice",
|
|
288
|
+
"warning",
|
|
289
|
+
"critical",
|
|
290
|
+
"alert",
|
|
291
|
+
"emergency"
|
|
292
|
+
];
|
|
293
|
+
const LEVEL_SEVERITY = {
|
|
294
|
+
debug: 10,
|
|
295
|
+
info: 20,
|
|
296
|
+
notice: 30,
|
|
297
|
+
warning: 40,
|
|
298
|
+
error: 50,
|
|
299
|
+
critical: 60,
|
|
300
|
+
alert: 70,
|
|
301
|
+
emergency: 80
|
|
302
|
+
};
|
|
303
|
+
function createLogger(options = {}) {
|
|
304
|
+
const { name, level = "debug", stream, onLog, onProgress } = options;
|
|
305
|
+
const target = stream === false ? void 0 : stream ?? process.stdout;
|
|
306
|
+
const threshold = LEVEL_SEVERITY[level] ?? LEVEL_SEVERITY.debug;
|
|
307
|
+
const write = (logLevel, data) => {
|
|
308
|
+
if (target && LEVEL_SEVERITY[logLevel] >= threshold) {
|
|
309
|
+
const line = typeof data === "object" && data !== null ? {
|
|
310
|
+
level: logLevel,
|
|
311
|
+
time: Date.now(),
|
|
312
|
+
name,
|
|
313
|
+
...data
|
|
314
|
+
} : {
|
|
315
|
+
level: logLevel,
|
|
316
|
+
time: Date.now(),
|
|
317
|
+
name,
|
|
318
|
+
msg: data
|
|
319
|
+
};
|
|
320
|
+
target.write(`${JSON.stringify(line)}\n`);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
...Object.fromEntries(LogLevels.map((logLevel) => {
|
|
325
|
+
return [logLevel, (data) => {
|
|
326
|
+
write(logLevel, data);
|
|
327
|
+
onLog?.(logLevel, data);
|
|
328
|
+
}];
|
|
329
|
+
})),
|
|
330
|
+
progress: (progressOptions) => {
|
|
331
|
+
if (onProgress) onProgress(progressOptions);
|
|
332
|
+
else write("info", { ...progressOptions });
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function buildLogLevels(fn) {
|
|
337
|
+
return Object.fromEntries(LogLevels.map((level) => [level, (data) => {
|
|
338
|
+
fn(level, data);
|
|
339
|
+
}]));
|
|
340
|
+
}
|
|
341
|
+
const CONSOLE_LEVEL_MAP = {
|
|
342
|
+
debug: "log",
|
|
343
|
+
info: "info",
|
|
344
|
+
notice: "info",
|
|
345
|
+
warning: "warn",
|
|
346
|
+
error: "error",
|
|
347
|
+
critical: "error",
|
|
348
|
+
alert: "error",
|
|
349
|
+
emergency: "error"
|
|
350
|
+
};
|
|
351
|
+
/**
|
|
352
|
+
* Human-readable `console`-backed logger for terminal contexts. Strings are
|
|
353
|
+
* written verbatim; objects are JSON-stringified. Zero dependencies.
|
|
354
|
+
*/
|
|
355
|
+
function createConsoleLogger() {
|
|
356
|
+
const toString = (value) => typeof value === "string" ? value : JSON.stringify(value);
|
|
357
|
+
return {
|
|
358
|
+
...buildLogLevels((level, data) => {
|
|
359
|
+
console[CONSOLE_LEVEL_MAP[level]](toString(data));
|
|
360
|
+
}),
|
|
361
|
+
progress: ({ progress, total, message }) => {
|
|
362
|
+
console.info(toString({
|
|
363
|
+
progress,
|
|
364
|
+
total,
|
|
365
|
+
message
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
//#endregion
|
|
269
371
|
//#region src/util/streaming.ts
|
|
270
372
|
/**
|
|
271
373
|
* Drive a streaming action's async generator. Calls `onChunk` once per yielded
|
|
@@ -291,6 +393,22 @@ async function runStreamingAction(action, input, context, onChunk) {
|
|
|
291
393
|
return chunks;
|
|
292
394
|
}
|
|
293
395
|
//#endregion
|
|
294
|
-
|
|
396
|
+
//#region src/util/telemetry.ts
|
|
397
|
+
/**
|
|
398
|
+
* Invoke an `OnToolCall` hook with fire-and-forget semantics - sync throws and
|
|
399
|
+
* async rejections are logged to stderr and never propagate to the call path.
|
|
400
|
+
*/
|
|
401
|
+
function emitToolCall(hook, event) {
|
|
402
|
+
if (!hook) return;
|
|
403
|
+
try {
|
|
404
|
+
Promise.resolve(hook(event)).catch((error) => {
|
|
405
|
+
console.error("onToolCall hook error:", error);
|
|
406
|
+
});
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error("onToolCall hook error:", error);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
//#endregion
|
|
412
|
+
export { LogLevels, SilkweaveError, actionMethod, badRequest, buildLogLevels, createAction, createConsoleLogger, createContext, createLogger, emitToolCall, forbidden, internal, isStreamingAction, lintActions, methodHasBody, notFound, pathParamNames, reportActionLint, resolveActionInput, runStreamingAction, silkweave, unwrap, validateActionDisposition, validateActionRouting };
|
|
295
413
|
|
|
296
414
|
//# sourceMappingURL=index.mjs.map
|
package/build/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/util/context.ts","../src/util/zod.ts","../src/util/lint.ts","../src/lib/silkweave.ts","../src/util/action.ts","../src/util/error.ts","../src/util/http.ts","../src/util/streaming.ts"],"sourcesContent":["export interface SilkweaveContext {\n keys: () => string[]\n has: (key: string) => boolean\n get: <T>(key: string) => T\n getOptional: <T>(key: string) => T | undefined\n set: <T>(key: string, value: T) => void\n fork: (store?: Record<string, unknown>) => SilkweaveContext\n}\n\nexport function createContext(store: Record<string, unknown> = {}): SilkweaveContext {\n return {\n keys: () => {\n return Object.keys(store)\n },\n has: (key: string): boolean => {\n return store[key] != null\n },\n get: <T>(key: string): T => {\n const value = store[key]\n if (value == null) { throw new Error(`Invalid context key: ${key}`) }\n return value as T\n },\n getOptional: <T>(key: string): T | undefined => {\n return store[key] as T | undefined\n },\n set: <T>(key: string, value: T) => {\n store[key] = value\n },\n fork: (value?: Record<string, unknown>) => {\n return createContext({ ...store, ...value })\n }\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from 'zod/v4'\n\nexport interface UnwrapResult {\n defaultValue?: any\n isOptional?: boolean\n isNullable?: boolean\n isReadOnly?: boolean\n}\n\nexport function unwrap(\n type: z.ZodTypeAny,\n result: UnwrapResult = {}\n): [z.ZodTypeAny, UnwrapResult] {\n if (type instanceof z.ZodOptional) {\n result.isOptional = true\n return unwrap(type.unwrap() as z.ZodTypeAny, result)\n } else if (type instanceof z.ZodNullable) {\n result.isNullable = true\n return unwrap(type.unwrap() as z.ZodTypeAny, result)\n } else if (type instanceof z.ZodReadonly) {\n result.isReadOnly = true\n return unwrap(type.def.innerType as z.ZodTypeAny, result)\n } else if (type instanceof z.ZodDefault) {\n result.defaultValue = typeof type.def.defaultValue === 'function'\n ? type.def.defaultValue()\n : type.def.defaultValue\n return unwrap(type.unwrap() as z.ZodTypeAny, result)\n } else {\n return [type, result]\n }\n}\n","import { z } from 'zod/v4'\nimport { Action } from './action.js'\nimport { unwrap } from './zod.js'\n\nexport type ActionLintCode =\n | 'missing-description'\n | 'short-description'\n | 'undescribed-params'\n | 'some-undescribed-params'\n\nexport interface ActionLintWarning {\n action: string\n code: ActionLintCode\n message: string\n}\n\n/** A description shorter than this reads as a placeholder to an agent picking a tool. */\nconst MIN_DESCRIPTION_LENGTH = 12\n\n/** A field's `.describe()` text, looking through optional/default/nullable/readonly wrappers. */\nfunction describedText(field: z.ZodTypeAny): string | undefined {\n if (field.description) { return field.description }\n const [base] = unwrap(field)\n return base.description\n}\n\n/**\n * Static checks for agent-hostile action definitions - the cheap mistakes that\n * quietly degrade tool-use: a missing or throwaway description (the model reads\n * it to decide when to call the tool) and undescribed input parameters (the\n * model guesses what to pass). Pure + side-effect-free; `reportActionLint` wires\n * it to a warn sink, and `silkweave().start()` runs it automatically.\n */\nexport function lintActions(actions: Action[]): ActionLintWarning[] {\n const warnings: ActionLintWarning[] = []\n for (const action of actions) {\n const description = action.description?.trim() ?? ''\n if (!description) {\n warnings.push({\n action: action.name,\n code: 'missing-description',\n message: `Action \"${action.name}\" has no description - MCP clients show it to the model to decide when to call the tool. Add a clear, specific sentence.`\n })\n } else if (description.length < MIN_DESCRIPTION_LENGTH) {\n warnings.push({\n action: action.name,\n code: 'short-description',\n message: `Action \"${action.name}\" description is very short (\"${description}\"). A fuller sentence helps agents choose the right tool.`\n })\n }\n\n const shape = action.input?.shape ?? {}\n const params = Object.keys(shape)\n const undescribed = params.filter((name) => !describedText(shape[name]))\n if (params.length > 0 && undescribed.length === params.length) {\n warnings.push({\n action: action.name,\n code: 'undescribed-params',\n message: `Action \"${action.name}\" has no described input parameters (${params.join(', ')}). Add .describe() to each field so agents pass correct values.`\n })\n } else if (undescribed.length > 0) {\n warnings.push({\n action: action.name,\n code: 'some-undescribed-params',\n message: `Action \"${action.name}\" input fields lack descriptions: ${undescribed.join(', ')}. Add .describe() for better agent tool-use.`\n })\n }\n }\n return warnings\n}\n\n/**\n * Run `lintActions` and emit each warning through `warn` (default `console.warn`,\n * which goes to stderr - safe even for the stdio MCP transport). Returns the\n * warnings so callers can also inspect or suppress them.\n */\nexport function reportActionLint(\n actions: Action[],\n warn: (message: string) => void = (message) => { console.warn(message) }\n): ActionLintWarning[] {\n const warnings = lintActions(actions)\n for (const warning of warnings) {\n warn(`[silkweave] ${warning.message}`)\n }\n return warnings\n}\n","import { Action } from '../util/action.js'\nimport { Adapter, AdapterGenerator, SilkweaveOptions } from '../util/adapter.js'\nimport { createContext } from '../util/context.js'\nimport { reportActionLint } from '../util/lint.js'\n\nexport type { SilkweaveOptions } from '../util/adapter.js'\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface Silkweave<Actions extends Record<string, Action> = {}> {\n set: <T>(key: string, value: T) => Silkweave<Actions>\n adapter: (generator: AdapterGenerator) => Silkweave<Actions>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n action: <A extends Action<any, any, string, any>>(\n action: A\n ) => Silkweave<Actions & Record<A['name'], A>>\n actions: <Arr extends readonly Action[]>(\n actions: Arr\n ) => Silkweave<Actions & { [K in Arr[number] as K['name']]: K }>\n start: () => Promise<Silkweave<Actions>>\n}\n\nexport function silkweave(options: SilkweaveOptions): Silkweave {\n const adapters: Adapter[] = []\n const actions: Action[] = []\n const context = createContext()\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const builder: Silkweave<any> = {\n set: (key, value) => {\n context.set(key, value)\n return builder\n },\n adapter: (generator) => {\n adapters.push(generator(options, context))\n return builder\n },\n action: (value) => {\n actions.push(value)\n return builder\n },\n actions: (values) => {\n actions.push(...values)\n return builder\n },\n start: async () => {\n if (options.lint !== false) { reportActionLint(actions) }\n await Promise.all(adapters.map((adapter) => {\n const filtered = adapter.allActions\n ? actions\n : actions.filter((action) => {\n if (!action.isEnabled) { return true }\n return action.isEnabled(adapter.context)\n })\n return adapter.start(filtered)\n }))\n return builder\n }\n }\n return builder\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { type CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport z from 'zod/v4'\nimport { SilkweaveContext } from './context.js'\n\nexport type ActionKind = 'query' | 'mutation'\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'\n\nexport type ActionRun<I, O> = (input: I, context: SilkweaveContext) => Promise<O>\nexport type ActionStreamRun<I, C> = (input: I, context: SilkweaveContext) => AsyncGenerator<C, void, void>\n\nexport interface Action<\n I extends object = any,\n O extends object = any,\n N extends string = string,\n K extends ActionKind = ActionKind,\n C = any\n> {\n name: N\n description: string\n input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }\n output?: z.ZodType<O> & { shape: Record<string, z.ZodTypeAny> }\n /**\n * Schema for individual chunks yielded by a streaming `run`. Required when\n * `run` is an async generator; adapters detect streaming actions by checking\n * `isStreamingAction(action)` at runtime, but type-aware tooling (inferRouter,\n * typegen) uses the presence of this field.\n */\n chunk?: z.ZodType<C>\n kind?: K\n /**\n * HTTP verb for REST-style adapters (fastify, nestjs `rest`). Defaults to\n * `POST`, or `GET` when `kind` is `'query'`. An explicit `method` always wins.\n */\n method?: HttpMethod\n /**\n * Route path for REST-style adapters. May contain `:param` placeholders\n * (e.g. `'spaces/:spaceId/users'`); each placeholder must be a key of the\n * input schema and is resolved from the URL path at runtime. When unset, the\n * adapter derives the path from `name`.\n */\n path?: string\n /**\n * Input fields sourced from the URL query string instead of the request body\n * (e.g. `['offset', 'limit']`). Each must be a key of the input schema; their\n * required/optional status is validated by the input schema as usual.\n */\n queryParams?: (keyof I)[]\n args?: (keyof I)[]\n /**\n * Default MCP result disposition for this action - `'json'` formats the\n * response with `jsonToolResult` (compact JSON text), `'smart'` (the default\n * when unset) uses `smartToolResult` (inlines small payloads, offloads large\n * ones to an embedded resource). This is only a default: a client that sends\n * `_meta.disposition` on the tool call always wins. Ignored by non-MCP\n * adapters.\n */\n disposition?: 'json' | 'smart'\n isEnabled?: (context: SilkweaveContext) => boolean\n run: ActionRun<I, O> | ActionStreamRun<I, C>\n toolResult?: (response: O, context: SilkweaveContext) => CallToolResult | undefined\n}\n\nexport interface NonStreamingActionInput<I extends object, O extends object, N extends string, K extends ActionKind> {\n name: N\n description: string\n input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }\n output?: z.ZodType<O> & { shape: Record<string, z.ZodTypeAny> }\n kind?: K\n method?: HttpMethod\n path?: string\n queryParams?: (keyof I)[]\n args?: (keyof I)[]\n disposition?: 'json' | 'smart'\n isEnabled?: (context: SilkweaveContext) => boolean\n run: ActionRun<I, O>\n toolResult?: (response: O, context: SilkweaveContext) => CallToolResult | undefined\n}\n\nexport interface StreamingActionInput<I extends object, C, N extends string, K extends ActionKind> {\n name: N\n description: string\n input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }\n chunk: z.ZodType<C>\n kind?: K\n method?: HttpMethod\n path?: string\n queryParams?: (keyof I)[]\n args?: (keyof I)[]\n disposition?: 'json' | 'smart'\n isEnabled?: (context: SilkweaveContext) => boolean\n run: ActionStreamRun<I, C>\n toolResult?: (response: C[], context: SilkweaveContext) => CallToolResult | undefined\n}\n\nexport function createAction<\n I extends object,\n C,\n N extends string,\n K extends ActionKind = 'mutation'\n>(action: StreamingActionInput<I, C, N, K>): StreamingActionInput<I, C, N, K>\nexport function createAction<\n I extends object,\n O extends object,\n N extends string,\n K extends ActionKind = 'mutation'\n>(action: NonStreamingActionInput<I, O, N, K>): NonStreamingActionInput<I, O, N, K>\nexport function createAction(action: Action): Action {\n return action\n}\n\n/**\n * Returns `true` when `action.run` is an async generator function (declared\n * with `async function*`). Adapters use this to switch between buffered\n * request/response and streaming chunk delivery.\n */\nexport function isStreamingAction(action: Action): boolean {\n return action.run?.constructor?.name === 'AsyncGeneratorFunction'\n}\n","export class SilkweaveError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode = 500\n ) {\n super(message)\n this.name = 'SilkweaveError'\n }\n}\n\nexport function notFound(message = 'Not found') {\n return new SilkweaveError(message, 'not_found', 404)\n}\n\nexport function badRequest(message = 'Bad request') {\n return new SilkweaveError(message, 'bad_request', 400)\n}\n\nexport function forbidden(message = 'Forbidden') {\n return new SilkweaveError(message, 'forbidden', 403)\n}\n\nexport function internal(message = 'Internal error') {\n return new SilkweaveError(message, 'internal', 500)\n}\n","import type z from 'zod/v4'\nimport { type Action, type HttpMethod } from './action.js'\nimport { SilkweaveError } from './error.js'\nimport { unwrap } from './zod.js'\n\nconst PATH_PARAM_RE = /:([A-Za-z0-9_]+)/g\n\n/**\n * Resolve the HTTP verb for an action. An explicit `method` always wins;\n * otherwise `kind: 'query'` maps to `GET` and everything else to `POST`.\n */\nexport function actionMethod(action: Pick<Action, 'method' | 'kind'>): HttpMethod {\n if (action.method) { return action.method }\n return action.kind === 'query' ? 'GET' : 'POST'\n}\n\n/** Whether a request with this method carries a body (everything except GET). */\nexport function methodHasBody(method: HttpMethod): boolean {\n return method !== 'GET'\n}\n\n/** Extract the `:param` placeholder names from a route path template. */\nexport function pathParamNames(path: string | undefined): string[] {\n if (!path) { return [] }\n return [...path.matchAll(PATH_PARAM_RE)].map((match) => match[1])\n}\n\n/**\n * Assert that every `:param` in `action.path` and every `queryParams` entry is a\n * field of the input schema. Throws a `SilkweaveError` at registration time so\n * misconfigured routing fails fast instead of silently dropping values.\n */\nexport function validateActionRouting(action: Action): void {\n const shape = action.input.shape\n for (const param of pathParamNames(action.path)) {\n if (!(param in shape)) {\n throw new SilkweaveError(\n `Action \"${action.name}\" path \"${action.path}\" references \":${param}\" but the input schema has no \"${param}\" field`,\n 'invalid_action_routing'\n )\n }\n }\n for (const key of action.queryParams ?? []) {\n if (!(String(key) in shape)) {\n throw new SilkweaveError(\n `Action \"${action.name}\" lists query param \"${String(key)}\" but the input schema has no such field`,\n 'invalid_action_routing'\n )\n }\n }\n}\n\n/**\n * Coerce a raw string (from the URL path or query string) to the primitive the\n * field's schema expects. Non-string values pass through untouched (REST\n * frameworks like Fastify may have already coerced via JSON Schema), and a\n * failed coercion returns the original value so Zod surfaces a proper error.\n */\nfunction coerceScalar(field: z.ZodTypeAny | undefined, value: unknown): unknown {\n if (!field || typeof value !== 'string') { return value }\n const [base] = unwrap(field)\n const type = (base as { def?: { type?: string } }).def?.type\n if (type === 'number') {\n const num = Number(value)\n return value.trim() === '' || Number.isNaN(num) ? value : num\n }\n if (type === 'boolean') {\n if (value === 'true') { return true }\n if (value === 'false') { return false }\n return value\n }\n if (type === 'bigint') {\n try { return BigInt(value) } catch { return value }\n }\n return value\n}\n\nexport interface ActionInputSources {\n /** Path parameters keyed by `:param` name (e.g. Express/Fastify `req.params`). */\n params?: Record<string, string | undefined>\n /** Parsed query string (string values, or arrays/coerced values from a framework). */\n query?: Record<string, unknown>\n /** Parsed request body (already-typed JSON for body methods). */\n body?: unknown\n}\n\n/**\n * Merge an action's path params, query params, and body into a single input\n * object honouring its `method`/`path`/`queryParams` routing config:\n *\n * - body fields form the base layer (only for body-carrying methods),\n * - declared query params override (on bodyless GET requests every input field\n * that is not a path param is read from the query string),\n * - path params override last.\n *\n * String values from the path/query are coerced to the primitive their schema\n * expects. The result is not validated here - callers pass it to\n * `action.input.parse()` (or let a framework validate it).\n */\nexport function resolveActionInput(action: Action, sources: ActionInputSources): Record<string, unknown> {\n const shape = action.input.shape\n const method = actionMethod(action)\n const hasBody = methodHasBody(method)\n const params = sources.params ?? {}\n const query = sources.query ?? {}\n const pathParams = new Set(pathParamNames(action.path))\n const queryKeys = new Set((action.queryParams ?? []).map(String))\n\n const merged: Record<string, unknown> = {}\n\n if (hasBody && sources.body && typeof sources.body === 'object') {\n Object.assign(merged, sources.body as Record<string, unknown>)\n }\n\n for (const [key, raw] of Object.entries(query)) {\n if (raw === undefined) { continue }\n const declared = hasBody ? queryKeys.has(key) : (key in shape && !pathParams.has(key))\n if (!declared) { continue }\n merged[key] = Array.isArray(raw) ? raw : coerceScalar(shape[key], raw)\n }\n\n for (const key of pathParams) {\n const raw = params[key]\n if (raw === undefined) { continue }\n merged[key] = coerceScalar(shape[key], raw)\n }\n\n return merged\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Action, ActionStreamRun, isStreamingAction } from './action.js'\nimport { SilkweaveContext } from './context.js'\n\n/**\n * Drive a streaming action's async generator. Calls `onChunk` once per yielded\n * value and awaits it before pulling the next value from the generator - this\n * is what wires transport-level backpressure (SSE drain, stdout flush, tRPC\n * subscription consumer) back to the action.\n *\n * Returns the buffered array of all chunks. Use this for non-streaming\n * adapters or when a client has opted out of streaming (e.g. no MCP\n * `progressToken`). Pass `onChunk` to also emit each chunk on the wire.\n */\nexport async function runStreamingAction<C>(\n action: Action<any, any, string, any, C>,\n input: object,\n context: SilkweaveContext,\n onChunk?: (chunk: C, index: number) => void | Promise<void>\n): Promise<C[]> {\n if (!isStreamingAction(action)) {\n throw new Error(`Action ${action.name} is not a streaming action`)\n }\n const run = action.run as ActionStreamRun<object, C>\n const iter = run(input, context)\n const chunks: C[] = []\n let index = 0\n for await (const chunk of iter) {\n chunks.push(chunk)\n if (onChunk) { await onChunk(chunk, index) }\n index += 1\n }\n return chunks\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/util/context.ts","../src/util/zod.ts","../src/util/lint.ts","../src/lib/silkweave.ts","../src/util/error.ts","../src/util/action.ts","../src/util/http.ts","../src/util/logger.ts","../src/util/streaming.ts","../src/util/telemetry.ts"],"sourcesContent":["export interface SilkweaveContext {\n keys: () => string[]\n has: (key: string) => boolean\n get: <T>(key: string) => T\n getOptional: <T>(key: string) => T | undefined\n set: <T>(key: string, value: T) => void\n fork: (store?: Record<string, unknown>) => SilkweaveContext\n}\n\nexport function createContext(store: Record<string, unknown> = {}): SilkweaveContext {\n return {\n keys: () => {\n return Object.keys(store)\n },\n has: (key: string): boolean => {\n return store[key] != null\n },\n get: <T>(key: string): T => {\n const value = store[key]\n if (value == null) { throw new Error(`Invalid context key: ${key}`) }\n return value as T\n },\n getOptional: <T>(key: string): T | undefined => {\n return store[key] as T | undefined\n },\n set: <T>(key: string, value: T) => {\n store[key] = value\n },\n fork: (value?: Record<string, unknown>) => {\n return createContext({ ...store, ...value })\n }\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from 'zod/v4'\n\nexport interface UnwrapResult {\n defaultValue?: any\n isOptional?: boolean\n isNullable?: boolean\n isReadOnly?: boolean\n}\n\nexport function unwrap(\n type: z.ZodTypeAny,\n result: UnwrapResult = {}\n): [z.ZodTypeAny, UnwrapResult] {\n if (type instanceof z.ZodOptional) {\n result.isOptional = true\n return unwrap(type.unwrap() as z.ZodTypeAny, result)\n } else if (type instanceof z.ZodNullable) {\n result.isNullable = true\n return unwrap(type.unwrap() as z.ZodTypeAny, result)\n } else if (type instanceof z.ZodReadonly) {\n result.isReadOnly = true\n return unwrap(type.def.innerType as z.ZodTypeAny, result)\n } else if (type instanceof z.ZodDefault) {\n result.defaultValue = typeof type.def.defaultValue === 'function'\n ? type.def.defaultValue()\n : type.def.defaultValue\n return unwrap(type.unwrap() as z.ZodTypeAny, result)\n } else {\n return [type, result]\n }\n}\n","import { z } from 'zod/v4'\nimport { Action } from './action.js'\nimport { unwrap } from './zod.js'\n\nexport type ActionLintCode =\n | 'missing-description'\n | 'short-description'\n | 'undescribed-params'\n | 'some-undescribed-params'\n\nexport interface ActionLintWarning {\n action: string\n code: ActionLintCode\n message: string\n}\n\n/** A description shorter than this reads as a placeholder to an agent picking a tool. */\nconst MIN_DESCRIPTION_LENGTH = 12\n\n/** A field's `.describe()` text, looking through optional/default/nullable/readonly wrappers. */\nfunction describedText(field: z.ZodTypeAny): string | undefined {\n if (field.description) { return field.description }\n const [base] = unwrap(field)\n return base.description\n}\n\n/**\n * Static checks for agent-hostile action definitions - the cheap mistakes that\n * quietly degrade tool-use: a missing or throwaway description (the model reads\n * it to decide when to call the tool) and undescribed input parameters (the\n * model guesses what to pass). Pure + side-effect-free; `reportActionLint` wires\n * it to a warn sink, and `silkweave().start()` runs it automatically.\n */\nexport function lintActions(actions: Action[]): ActionLintWarning[] {\n const warnings: ActionLintWarning[] = []\n for (const action of actions) {\n const description = action.description?.trim() ?? ''\n if (!description) {\n warnings.push({\n action: action.name,\n code: 'missing-description',\n message: `Action \"${action.name}\" has no description - MCP clients show it to the model to decide when to call the tool. Add a clear, specific sentence.`\n })\n } else if (description.length < MIN_DESCRIPTION_LENGTH) {\n warnings.push({\n action: action.name,\n code: 'short-description',\n message: `Action \"${action.name}\" description is very short (\"${description}\"). A fuller sentence helps agents choose the right tool.`\n })\n }\n\n const shape = action.input?.shape ?? {}\n const params = Object.keys(shape)\n const undescribed = params.filter((name) => !describedText(shape[name]))\n if (params.length > 0 && undescribed.length === params.length) {\n warnings.push({\n action: action.name,\n code: 'undescribed-params',\n message: `Action \"${action.name}\" has no described input parameters (${params.join(', ')}). Add .describe() to each field so agents pass correct values.`\n })\n } else if (undescribed.length > 0) {\n warnings.push({\n action: action.name,\n code: 'some-undescribed-params',\n message: `Action \"${action.name}\" input fields lack descriptions: ${undescribed.join(', ')}. Add .describe() for better agent tool-use.`\n })\n }\n }\n return warnings\n}\n\n/**\n * Run `lintActions` and emit each warning through `warn` (default `console.warn`,\n * which goes to stderr - safe even for the stdio MCP transport). Returns the\n * warnings so callers can also inspect or suppress them.\n */\nexport function reportActionLint(\n actions: Action[],\n warn: (message: string) => void = (message) => { console.warn(message) }\n): ActionLintWarning[] {\n const warnings = lintActions(actions)\n for (const warning of warnings) {\n warn(`[silkweave] ${warning.message}`)\n }\n return warnings\n}\n","import { Action } from '../util/action.js'\nimport { Adapter, AdapterGenerator, SilkweaveOptions } from '../util/adapter.js'\nimport { createContext } from '../util/context.js'\nimport { reportActionLint } from '../util/lint.js'\n\nexport type { SilkweaveOptions } from '../util/adapter.js'\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface Silkweave<Actions extends Record<string, Action> = {}> {\n set: <T>(key: string, value: T) => Silkweave<Actions>\n adapter: (generator: AdapterGenerator) => Silkweave<Actions>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n action: <A extends Action<any, any, string, any>>(\n action: A\n ) => Silkweave<Actions & Record<A['name'], A>>\n actions: <Arr extends readonly Action[]>(\n actions: Arr\n ) => Silkweave<Actions & { [K in Arr[number] as K['name']]: K }>\n start: () => Promise<Silkweave<Actions>>\n}\n\nexport function silkweave(options: SilkweaveOptions): Silkweave {\n const adapters: Adapter[] = []\n const actions: Action[] = []\n const context = createContext()\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const builder: Silkweave<any> = {\n set: (key, value) => {\n context.set(key, value)\n return builder\n },\n adapter: (generator) => {\n adapters.push(generator(options, context))\n return builder\n },\n action: (value) => {\n actions.push(value)\n return builder\n },\n actions: (values) => {\n actions.push(...values)\n return builder\n },\n start: async () => {\n if (options.lint !== false) { reportActionLint(actions) }\n await Promise.all(adapters.map((adapter) => {\n const filtered = adapter.allActions\n ? actions\n : actions.filter((action) => {\n if (!action.isEnabled) { return true }\n return action.isEnabled(adapter.context)\n })\n return adapter.start(filtered)\n }))\n return builder\n }\n }\n return builder\n}\n","export class SilkweaveError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode = 500\n ) {\n super(message)\n this.name = 'SilkweaveError'\n }\n}\n\nexport function notFound(message = 'Not found') {\n return new SilkweaveError(message, 'not_found', 404)\n}\n\nexport function badRequest(message = 'Bad request') {\n return new SilkweaveError(message, 'bad_request', 400)\n}\n\nexport function forbidden(message = 'Forbidden') {\n return new SilkweaveError(message, 'forbidden', 403)\n}\n\nexport function internal(message = 'Internal error') {\n return new SilkweaveError(message, 'internal', 500)\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { type CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport z from 'zod/v4'\nimport { SilkweaveContext } from './context.js'\nimport { SilkweaveError } from './error.js'\n\nexport type ActionKind = 'query' | 'mutation'\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'\n\n/**\n * MCP tool annotations (spec `ToolAnnotations`) - client-facing hints about a\n * tool's behavior, used by MCP hosts to group and permission-gate tools. All\n * hints are advisory. Structurally compatible with the MCP SDK's\n * `ToolAnnotations`, defined here so non-MCP packages can set them without a\n * type dependency on the SDK.\n */\nexport interface ToolAnnotations {\n /** Human-readable title override for the tool. */\n title?: string\n /** The tool does not modify its environment. */\n readOnlyHint?: boolean\n /** The tool may perform destructive updates (only meaningful when not read-only). */\n destructiveHint?: boolean\n /** Repeated calls with the same arguments have no additional effect. */\n idempotentHint?: boolean\n /** The tool may interact with an open world of external entities. */\n openWorldHint?: boolean\n}\n\nexport type ActionRun<I, O> = (input: I, context: SilkweaveContext) => Promise<O>\nexport type ActionStreamRun<I, C> = (input: I, context: SilkweaveContext) => AsyncGenerator<C, void, void>\n\nexport interface Action<\n I extends object = any,\n O extends object = any,\n N extends string = string,\n K extends ActionKind = ActionKind,\n C = any\n> {\n name: N\n description: string\n input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }\n output?: z.ZodType<O> & { shape: Record<string, z.ZodTypeAny> }\n /**\n * Schema for individual chunks yielded by a streaming `run`. Required when\n * `run` is an async generator; adapters detect streaming actions by checking\n * `isStreamingAction(action)` at runtime, but type-aware tooling (inferRouter,\n * typegen) uses the presence of this field.\n */\n chunk?: z.ZodType<C>\n kind?: K\n /**\n * HTTP verb for REST-style adapters (fastify, nestjs `rest`). Defaults to\n * `POST`, or `GET` when `kind` is `'query'`. An explicit `method` always wins.\n */\n method?: HttpMethod\n /**\n * Route path for REST-style adapters. May contain `:param` placeholders\n * (e.g. `'spaces/:spaceId/users'`); each placeholder must be a key of the\n * input schema and is resolved from the URL path at runtime. When unset, the\n * adapter derives the path from `name`.\n */\n path?: string\n /**\n * Input fields sourced from the URL query string instead of the request body\n * (e.g. `['offset', 'limit']`). Each must be a key of the input schema; their\n * required/optional status is validated by the input schema as usual.\n */\n queryParams?: (keyof I)[]\n args?: (keyof I)[]\n /**\n * MCP result disposition for this action. `'json'` (the default when unset)\n * formats the response with `jsonToolResult` (compact JSON text); `'smart'`\n * uses `smartToolResult` (inlines small payloads, offloads large ones to an\n * embedded resource); `'structured'` declares the `output` schema as an MCP\n * `outputSchema` contract - the result is parsed through `output` (stripping\n * extra fields) and shipped as `structuredContent` alongside a JSON text\n * mirror. `'json'`/`'smart'` are only defaults a client's `_meta.disposition`\n * can override; a `'structured'` action ignores `_meta.disposition`, since\n * its schema contract is fixed at `tools/list` time. `'structured'` requires\n * a non-streaming action with an `output` schema (validated at registration\n * via `validateActionDisposition()`). Ignored by non-MCP adapters.\n */\n disposition?: 'json' | 'smart' | 'structured'\n /**\n * MCP tool annotations forwarded to `tools/list`. When unset, MCP adapters\n * derive `readOnlyHint` from `kind` (`'query'` ⇒ read-only); explicit\n * annotations are merged over the derived base, so setting e.g. only\n * `destructiveHint` keeps the derived `readOnlyHint`. Ignored by non-MCP\n * adapters.\n */\n annotations?: ToolAnnotations\n /**\n * Free-form grouping labels (e.g. `['leads', 'write']`). Carried on the\n * action for per-request filtering (`filterActions` in the MCP adapters) and\n * other consumers; no behavior in core itself.\n */\n tags?: string[]\n isEnabled?: (context: SilkweaveContext) => boolean\n run: ActionRun<I, O> | ActionStreamRun<I, C>\n toolResult?: (response: O, context: SilkweaveContext) => CallToolResult | undefined\n}\n\nexport interface NonStreamingActionInput<I extends object, O extends object, N extends string, K extends ActionKind> {\n name: N\n description: string\n input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }\n output?: z.ZodType<O> & { shape: Record<string, z.ZodTypeAny> }\n kind?: K\n method?: HttpMethod\n path?: string\n queryParams?: (keyof I)[]\n args?: (keyof I)[]\n disposition?: 'json' | 'smart' | 'structured'\n annotations?: ToolAnnotations\n tags?: string[]\n isEnabled?: (context: SilkweaveContext) => boolean\n run: ActionRun<I, O>\n toolResult?: (response: O, context: SilkweaveContext) => CallToolResult | undefined\n}\n\nexport interface StreamingActionInput<I extends object, C, N extends string, K extends ActionKind> {\n name: N\n description: string\n input: z.ZodType<I> & { shape: Record<string, z.ZodTypeAny> }\n chunk: z.ZodType<C>\n kind?: K\n method?: HttpMethod\n path?: string\n queryParams?: (keyof I)[]\n args?: (keyof I)[]\n disposition?: 'json' | 'smart'\n annotations?: ToolAnnotations\n tags?: string[]\n isEnabled?: (context: SilkweaveContext) => boolean\n run: ActionStreamRun<I, C>\n toolResult?: (response: C[], context: SilkweaveContext) => CallToolResult | undefined\n}\n\nexport function createAction<\n I extends object,\n C,\n N extends string,\n K extends ActionKind = 'mutation'\n>(action: StreamingActionInput<I, C, N, K>): StreamingActionInput<I, C, N, K>\nexport function createAction<\n I extends object,\n O extends object,\n N extends string,\n K extends ActionKind = 'mutation'\n>(action: NonStreamingActionInput<I, O, N, K>): NonStreamingActionInput<I, O, N, K>\nexport function createAction(action: Action): Action {\n return action\n}\n\n/**\n * Returns `true` when `action.run` is an async generator function (declared\n * with `async function*`). Adapters use this to switch between buffered\n * request/response and streaming chunk delivery.\n */\nexport function isStreamingAction(action: Action): boolean {\n return action.run?.constructor?.name === 'AsyncGeneratorFunction'\n}\n\n/**\n * Validate an action's MCP `disposition` at registration time. `'structured'`\n * declares the `output` schema as a hard MCP `outputSchema` contract, so it\n * requires a non-streaming action with an `output` schema - anything else is a\n * misconfiguration surfaced at boot rather than a per-call protocol error.\n * MCP adapters call this from `start()` (or, for per-request transports, when\n * the handler is built).\n */\nexport function validateActionDisposition(action: Action): void {\n if (action.disposition !== 'structured') { return }\n if (isStreamingAction(action) || action.chunk) {\n throw new SilkweaveError(\n `Action '${action.name}': disposition 'structured' is not supported on a streaming action - there is no single result to validate against an output schema`,\n 'invalid_action'\n )\n }\n if (!action.output) {\n throw new SilkweaveError(\n `Action '${action.name}': disposition 'structured' requires an 'output' schema - it becomes the tool's MCP outputSchema contract`,\n 'invalid_action'\n )\n }\n}\n","import type z from 'zod/v4'\nimport { type Action, type HttpMethod } from './action.js'\nimport { SilkweaveError } from './error.js'\nimport { unwrap } from './zod.js'\n\nconst PATH_PARAM_RE = /:([A-Za-z0-9_]+)/g\n\n/**\n * Resolve the HTTP verb for an action. An explicit `method` always wins;\n * otherwise `kind: 'query'` maps to `GET` and everything else to `POST`.\n */\nexport function actionMethod(action: Pick<Action, 'method' | 'kind'>): HttpMethod {\n if (action.method) { return action.method }\n return action.kind === 'query' ? 'GET' : 'POST'\n}\n\n/** Whether a request with this method carries a body (everything except GET). */\nexport function methodHasBody(method: HttpMethod): boolean {\n return method !== 'GET'\n}\n\n/** Extract the `:param` placeholder names from a route path template. */\nexport function pathParamNames(path: string | undefined): string[] {\n if (!path) { return [] }\n return [...path.matchAll(PATH_PARAM_RE)].map((match) => match[1])\n}\n\n/**\n * Assert that every `:param` in `action.path` and every `queryParams` entry is a\n * field of the input schema. Throws a `SilkweaveError` at registration time so\n * misconfigured routing fails fast instead of silently dropping values.\n */\nexport function validateActionRouting(action: Action): void {\n const shape = action.input.shape\n for (const param of pathParamNames(action.path)) {\n if (!(param in shape)) {\n throw new SilkweaveError(\n `Action \"${action.name}\" path \"${action.path}\" references \":${param}\" but the input schema has no \"${param}\" field`,\n 'invalid_action_routing'\n )\n }\n }\n for (const key of action.queryParams ?? []) {\n if (!(String(key) in shape)) {\n throw new SilkweaveError(\n `Action \"${action.name}\" lists query param \"${String(key)}\" but the input schema has no such field`,\n 'invalid_action_routing'\n )\n }\n }\n}\n\n/**\n * Coerce a raw string (from the URL path or query string) to the primitive the\n * field's schema expects. Non-string values pass through untouched (REST\n * frameworks like Fastify may have already coerced via JSON Schema), and a\n * failed coercion returns the original value so Zod surfaces a proper error.\n */\nfunction coerceScalar(field: z.ZodTypeAny | undefined, value: unknown): unknown {\n if (!field || typeof value !== 'string') { return value }\n const [base] = unwrap(field)\n const type = (base as { def?: { type?: string } }).def?.type\n if (type === 'number') {\n const num = Number(value)\n return value.trim() === '' || Number.isNaN(num) ? value : num\n }\n if (type === 'boolean') {\n if (value === 'true') { return true }\n if (value === 'false') { return false }\n return value\n }\n if (type === 'bigint') {\n try { return BigInt(value) } catch { return value }\n }\n return value\n}\n\nexport interface ActionInputSources {\n /** Path parameters keyed by `:param` name (e.g. Express/Fastify `req.params`). */\n params?: Record<string, string | undefined>\n /** Parsed query string (string values, or arrays/coerced values from a framework). */\n query?: Record<string, unknown>\n /** Parsed request body (already-typed JSON for body methods). */\n body?: unknown\n}\n\n/**\n * Merge an action's path params, query params, and body into a single input\n * object honouring its `method`/`path`/`queryParams` routing config:\n *\n * - body fields form the base layer (only for body-carrying methods),\n * - declared query params override (on bodyless GET requests every input field\n * that is not a path param is read from the query string),\n * - path params override last.\n *\n * String values from the path/query are coerced to the primitive their schema\n * expects. The result is not validated here - callers pass it to\n * `action.input.parse()` (or let a framework validate it).\n */\nexport function resolveActionInput(action: Action, sources: ActionInputSources): Record<string, unknown> {\n const shape = action.input.shape\n const method = actionMethod(action)\n const hasBody = methodHasBody(method)\n const params = sources.params ?? {}\n const query = sources.query ?? {}\n const pathParams = new Set(pathParamNames(action.path))\n const queryKeys = new Set((action.queryParams ?? []).map(String))\n\n const merged: Record<string, unknown> = {}\n\n if (hasBody && sources.body && typeof sources.body === 'object') {\n Object.assign(merged, sources.body as Record<string, unknown>)\n }\n\n for (const [key, raw] of Object.entries(query)) {\n if (raw === undefined) { continue }\n const declared = hasBody ? queryKeys.has(key) : (key in shape && !pathParams.has(key))\n if (!declared) { continue }\n merged[key] = Array.isArray(raw) ? raw : coerceScalar(shape[key], raw)\n }\n\n for (const key of pathParams) {\n const raw = params[key]\n if (raw === undefined) { continue }\n merged[key] = coerceScalar(shape[key], raw)\n }\n\n return merged\n}\n","export interface MessageOptions {\n message: string\n}\n\nexport interface ProgressOptions {\n progress: number\n total?: number\n message?: string\n}\n\nconst LogLevels = ['error', 'debug', 'info', 'notice', 'warning', 'critical', 'alert', 'emergency'] as const\n\nexport type LogLevel = typeof LogLevels[number]\n\nexport type LogFn = (data: unknown) => void\n\nexport interface Logger extends Record<LogLevel, LogFn> {\n progress: (options: ProgressOptions) => void\n}\n\nexport { LogLevels }\n\n// Severity ordering (lowest number = most verbose) used to gate writes by the\n// configured `level` threshold. Mirrors syslog-style precedence so a `level` of\n// e.g. `'warning'` suppresses `info`/`notice`/`debug`.\nconst LEVEL_SEVERITY: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n notice: 30,\n warning: 40,\n error: 50,\n critical: 60,\n alert: 70,\n emergency: 80\n}\n\nexport interface CreateLoggerOptions {\n name?: string\n /** Minimum severity to write to `stream`. Defaults to `'debug'` (everything). */\n level?: LogLevel\n /**\n * Destination stream for structured log lines, or `false` to discard them.\n * Defaults to `process.stdout`. The `onLog` callback always fires regardless\n * of this setting - the stream is just the diagnostic sink.\n */\n stream?: NodeJS.WritableStream | false\n onLog?: (level: LogLevel, data: unknown) => void\n onProgress?: (options: ProgressOptions) => void\n}\n\nexport function createLogger(options: CreateLoggerOptions = {}): Logger {\n const { name, level = 'debug', stream, onLog, onProgress } = options\n\n const target = stream === false ? undefined : stream ?? process.stdout\n const threshold = LEVEL_SEVERITY[level] ?? LEVEL_SEVERITY.debug\n\n const write = (logLevel: LogLevel, data: unknown) => {\n if (target && LEVEL_SEVERITY[logLevel] >= threshold) {\n const line = typeof data === 'object' && data !== null\n ? { level: logLevel, time: Date.now(), name, ...data }\n : { level: logLevel, time: Date.now(), name, msg: data }\n target.write(`${JSON.stringify(line)}\\n`)\n }\n }\n\n const logLevels = Object.fromEntries(LogLevels.map((logLevel) => {\n return [logLevel, (data: unknown) => {\n write(logLevel, data)\n onLog?.(logLevel, data)\n }]\n })) as Record<LogLevel, LogFn>\n\n return {\n ...logLevels,\n progress: (progressOptions) => {\n if (onProgress) {\n onProgress(progressOptions)\n } else {\n write('info', { ...progressOptions })\n }\n }\n }\n}\n\nexport function buildLogLevels(fn: (level: LogLevel, data: unknown) => void): Record<LogLevel, LogFn> {\n return Object.fromEntries(LogLevels.map((level) => [level, (data: unknown) => { fn(level, data) }])) as Record<LogLevel, LogFn>\n}\n\n// Maps each syslog level onto a `console` method for a human-readable terminal\n// logger. Used by the `cli` adapter and the MCP `cliProxy` client - a\n// zero-dependency replacement for a terminal-UI logging library.\nconst CONSOLE_LEVEL_MAP: Record<LogLevel, 'log' | 'info' | 'warn' | 'error'> = {\n debug: 'log',\n info: 'info',\n notice: 'info',\n warning: 'warn',\n error: 'error',\n critical: 'error',\n alert: 'error',\n emergency: 'error'\n}\n\n/**\n * Human-readable `console`-backed logger for terminal contexts. Strings are\n * written verbatim; objects are JSON-stringified. Zero dependencies.\n */\nexport function createConsoleLogger(): Logger {\n const toString = (value: unknown) => typeof value === 'string' ? value : JSON.stringify(value)\n return {\n ...buildLogLevels((level, data) => {\n console[CONSOLE_LEVEL_MAP[level]](toString(data))\n }),\n progress: ({ progress, total, message }) => {\n console.info(toString({ progress, total, message }))\n }\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Action, ActionStreamRun, isStreamingAction } from './action.js'\nimport { SilkweaveContext } from './context.js'\n\n/**\n * Drive a streaming action's async generator. Calls `onChunk` once per yielded\n * value and awaits it before pulling the next value from the generator - this\n * is what wires transport-level backpressure (SSE drain, stdout flush, tRPC\n * subscription consumer) back to the action.\n *\n * Returns the buffered array of all chunks. Use this for non-streaming\n * adapters or when a client has opted out of streaming (e.g. no MCP\n * `progressToken`). Pass `onChunk` to also emit each chunk on the wire.\n */\nexport async function runStreamingAction<C>(\n action: Action<any, any, string, any, C>,\n input: object,\n context: SilkweaveContext,\n onChunk?: (chunk: C, index: number) => void | Promise<void>\n): Promise<C[]> {\n if (!isStreamingAction(action)) {\n throw new Error(`Action ${action.name} is not a streaming action`)\n }\n const run = action.run as ActionStreamRun<object, C>\n const iter = run(input, context)\n const chunks: C[] = []\n let index = 0\n for await (const chunk of iter) {\n chunks.push(chunk)\n if (onChunk) { await onChunk(chunk, index) }\n index += 1\n }\n return chunks\n}\n","import { SilkweaveContext } from './context.js'\n\n/**\n * One tool/procedure invocation, as reported to an `OnToolCall` hook. MCP\n * adapters emit these from the tool registrar (where result formatting\n * happens, so `resultBytes`/`sideloaded` are known); other transports emit\n * from their own invocation seam and omit the MCP-only fields.\n */\nexport interface ToolCallEvent {\n /** Action name (e.g. `'leads.bulkUpdate'`). */\n action: string\n /** Wire name the client called (e.g. `'LeadsBulkUpdate'` over MCP, `'leadsBulkUpdate'` over tRPC). */\n tool: string\n transport: 'mcp' | 'trpc' | 'rest' | 'cli'\n /** Wall-clock duration; for streaming actions this spans full generator consumption. */\n durationMs: number\n /**\n * `false` when the action threw or the formatted result is an MCP\n * `isError` tool result. `errorCode`/`errorMessage` are set for thrown\n * errors (a `SilkweaveError`'s `code`, else the error's `name`).\n */\n ok: boolean\n errorCode?: string\n errorMessage?: string\n /** Serialized (JSON) size of the raw result - MCP-layer events only. */\n resultBytes?: number\n /** Whether `smartToolResult` offloaded the payload to an embedded resource - MCP-layer events only. */\n sideloaded?: boolean\n /** The per-call action context (access to `auth`/`request` for spaceId, apiKeyId, ...). */\n context: SilkweaveContext\n}\n\n/**\n * Telemetry hook invoked once per tool call, fire-and-forget: adapters never\n * await it on the result path and swallow (log) its errors, so the hook can\n * never fail, slow, or reorder a call.\n */\nexport type OnToolCall = (event: ToolCallEvent) => void | Promise<void>\n\n/**\n * Invoke an `OnToolCall` hook with fire-and-forget semantics - sync throws and\n * async rejections are logged to stderr and never propagate to the call path.\n */\nexport function emitToolCall(hook: OnToolCall | undefined, event: ToolCallEvent): void {\n if (!hook) { return }\n try {\n void Promise.resolve(hook(event)).catch((error: unknown) => { console.error('onToolCall hook error:', error) })\n } catch (error) {\n console.error('onToolCall hook error:', error)\n }\n}\n"],"mappings":"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"}
|