@agentproto/driver 0.1.1

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.
@@ -0,0 +1,383 @@
1
+ import { ToolContext, ToolHandle, DriverKind } from '@agentproto/tool';
2
+
3
+ /**
4
+ * Body signature derived from a contract handle's generics.
5
+ *
6
+ * Analogous to a Solidity function `override` against an interface
7
+ * declaration: `input` matches the contract's `inputSchema` type,
8
+ * `context` matches `contextSchema`, and the return is checked against
9
+ * `outputSchema`. Drift between the body and its contract is a compile
10
+ * error, not a runtime one.
11
+ *
12
+ * Compare with the legacy {@link ExecuteFn} — `unknown`-typed inputs
13
+ * that require manual `as InputType` casts at every body. That form
14
+ * stays available for `.md`-driven dynamic loading where the contract
15
+ * handle isn't in scope; TypeScript authors should prefer the typed
16
+ * form via {@link implementTool}.
17
+ */
18
+ type TypedExecuteFn<TInput, TOutput, TContext extends ToolContext> = (args: {
19
+ input: TInput;
20
+ context: TContext;
21
+ driverCtx: DriverContext;
22
+ signal: AbortSignal;
23
+ }) => Promise<TOutput> | TOutput;
24
+ /**
25
+ * Typed `(contract, body)` pair — the missing "concrete tool" layer
26
+ * between AIP-14 `ITool` (the contract handle) and AIP-30 PROVIDER
27
+ * (the shared-infra bundle).
28
+ *
29
+ * Authored via {@link implementTool}, consumed by {@link defineDriver}
30
+ * via `implementations: [...]` and by per-surface adapters
31
+ * (`toAiSdkTool`, `toMastraTool`) that close the body over a
32
+ * per-request `context`.
33
+ */
34
+ interface ToolImplementation<TInput = unknown, TOutput = unknown, TContext extends ToolContext = ToolContext> {
35
+ /** The AIP-14 contract handle this implementation binds to. */
36
+ readonly tool: ToolHandle<TInput, TOutput, TContext>;
37
+ /** Typed body — see {@link TypedExecuteFn}. */
38
+ readonly body: TypedExecuteFn<TInput, TOutput, TContext>;
39
+ }
40
+ /**
41
+ * Bind a typed body to an AIP-14 `ITool` contract handle.
42
+ *
43
+ * Equivalent to Solidity's `MyToken is IERC20` pattern: the compiler
44
+ * enforces that the body's input/output/context match the contract's
45
+ * generics. No string-keyed indirection, no manual `as` casts at
46
+ * call sites.
47
+ *
48
+ * The returned {@link ToolImplementation} is the canonical typed
49
+ * binding; downstream surfaces consume it via:
50
+ *
51
+ * - {@link defineDriver} — bundle ≥1 implementations into a
52
+ * shared-infra provider for the AIP-30 resolver to dispatch through.
53
+ * - `toAiSdkTool(impl, { context })` — adapt to AI SDK's
54
+ * `tool({...})` shape for `streamText` / `generateText` consumers.
55
+ * - `toMastraTool(impl, { context })` — adapt to Mastra's `createTool`
56
+ * for `agent.stream` / `agent.generate` consumers.
57
+ *
58
+ * The same `impl` can be re-adapted with different per-request
59
+ * contexts; the body is captured once and the context flows through
60
+ * the closure each time.
61
+ */
62
+ declare function implementTool<TInput, TOutput, TContext extends ToolContext = ToolContext>(tool: ToolHandle<TInput, TOutput, TContext>, body: TypedExecuteFn<TInput, TOutput, TContext>): ToolImplementation<TInput, TOutput, TContext>;
63
+
64
+ /**
65
+ * AIP-30 DriverDefinition — what an author hands to {@link defineDriver}.
66
+ *
67
+ * Field set mirrors `DRIVER.md` frontmatter. Bodies live in the
68
+ * `execute` map keyed by tool id; one provider MAY implement multiple
69
+ * tools.
70
+ */
71
+ interface DriverDefinition {
72
+ /** Machine identifier. Lowercase, digits, dashes, dots. 2–80 chars. */
73
+ id: string;
74
+ /** Human-readable display name. */
75
+ name: string;
76
+ /** One-paragraph purpose. */
77
+ description: string;
78
+ /** Spec version of THIS provider (semver). */
79
+ version?: string;
80
+ /** Concrete subtype. */
81
+ kind: DriverKind;
82
+ /** Per-tool dispatch bindings. ≥1 entry. */
83
+ implements: readonly ImplementsEntry[];
84
+ /**
85
+ * Per-tool execute bodies, keyed by `implements[].tool` id. Legacy
86
+ * form — used for `.md`-driven dynamic loading where the contract
87
+ * handle isn't in scope at module load time. TypeScript authors
88
+ * SHOULD prefer {@link DriverDefinition.implementations} for
89
+ * compile-time type safety against the contract's generics.
90
+ *
91
+ * `defineDriver` accepts either field; both can coexist when a
92
+ * provider mixes typed and dynamic bodies. On the same tool id,
93
+ * `implementations` wins (typed beats untyped).
94
+ */
95
+ execute?: Record<string, ExecuteFn>;
96
+ /**
97
+ * Typed implementations bound to their contract handles via
98
+ * {@link implementTool}(handle, body). Each carries `impl.tool.id`,
99
+ * so the author doesn't restate the binding as a string key.
100
+ * `defineDriver` reads `impl.tool.id` to populate the runtime
101
+ * execute map.
102
+ *
103
+ * Equivalent in role to Solidity's `is IERC20` declarations on a
104
+ * contract: the compiler enforces shape match between the body
105
+ * and the interface it claims to implement.
106
+ *
107
+ * Type note: the array element generics are erased to
108
+ * `ToolImplementation<any, any, any>` because heterogeneous
109
+ * implementations (different contracts, different input/output
110
+ * types) must coexist. Per-impl type safety is enforced at
111
+ * `implementTool(handle, body)` call sites, not at the array
112
+ * collection level. Without `any` here TS's contravariant function
113
+ * positions reject heterogeneous arrays — see TS issue #21534.
114
+ */
115
+ implementations?: readonly ToolImplementation<any, any, any>[];
116
+ /** Universal lifecycle / policy / sandbox blocks (subset of frontmatter). */
117
+ install?: readonly InstallMethod[];
118
+ versionCheck?: VersionCheck;
119
+ auth?: AuthConfig;
120
+ network?: {
121
+ egress?: readonly string[];
122
+ ingress?: readonly string[];
123
+ };
124
+ region?: readonly string[];
125
+ policyTags?: readonly string[];
126
+ costOverride?: CostOverride;
127
+ timeoutOverrideMs?: number;
128
+ retryOverride?: RetryPolicy;
129
+ healthCheck?: HealthCheckConfig;
130
+ /** Optional behavioural adapters. */
131
+ login?: (args: LoginArgs) => Promise<LoginResult>;
132
+ refresh?: (args: RefreshArgs) => Promise<RefreshResult>;
133
+ parseOutput?: (args: ParseOutputArgs) => ParseOutputResult;
134
+ detectExpiry?: (args: DetectExpiryArgs) => boolean;
135
+ /** Bookkeeping. */
136
+ tags?: readonly string[];
137
+ metadata?: Record<string, unknown>;
138
+ }
139
+ interface ImplementsEntry {
140
+ /** Workspace-relative path or registry id of a TOOL.md. */
141
+ tool: string;
142
+ /** Contract semver range (npm-style). */
143
+ version: string;
144
+ /** Optional: drop optional inputs/outputs the provider doesn't support. */
145
+ schemaNarrowing?: {
146
+ dropInputs?: readonly string[];
147
+ dropOutputs?: readonly string[];
148
+ };
149
+ /**
150
+ * Optional: rename or transform contract-input keys to provider-arg keys.
151
+ * String value = identity/rename. Object = `{ from, transform }` calling
152
+ * a named transformer exposed by the provider's entry.
153
+ */
154
+ mapping?: Record<string, MappingValue>;
155
+ /** Per-tool overrides; fall through to provider-level overrides. */
156
+ costOverride?: CostOverride;
157
+ timeoutOverrideMs?: number;
158
+ retryOverride?: RetryPolicy;
159
+ /** Per-tool, kind-specific dispatch hints (argv / endpoint / mcp_tool_name / function_ref). */
160
+ metadata?: Record<string, unknown>;
161
+ }
162
+ type MappingValue = string | {
163
+ from: string;
164
+ transform?: string;
165
+ };
166
+ type ExecuteFn = (args: ExecuteArgs) => Promise<unknown> | unknown;
167
+ interface ExecuteArgs {
168
+ /** Validated input matching the contract's `inputSchema` (post-narrowing+mapping). */
169
+ input: unknown;
170
+ /** Per-call context, validated against the contract's `contextSchema` when declared. */
171
+ context: ToolContext;
172
+ /** Resolved provider state — auth, secrets, sandbox handle, region. */
173
+ driverCtx: DriverContext;
174
+ /** Caller-set abort signal — MUST be honoured. */
175
+ signal: AbortSignal;
176
+ }
177
+ interface DriverContext {
178
+ /** Resolved secrets keyed by env-var name. Hosts MUST NOT log values. */
179
+ secrets: Record<string, string>;
180
+ /** The provider's auth state at dispatch time. */
181
+ authState: "unknown" | "unauthed" | "authed" | "expired";
182
+ /** Free-form, namespaced (CLI runtimes stash sandbox handle, HTTP runtimes stash baseUrl, …). */
183
+ [key: string]: unknown;
184
+ }
185
+ interface AuthConfig {
186
+ /** Path to a SECRETS.md inventory (AIP-19). */
187
+ ref?: string;
188
+ state?: {
189
+ paths?: readonly string[];
190
+ env?: readonly string[];
191
+ };
192
+ login?: AuthLoginConfig;
193
+ refresh?: AuthRefreshConfig;
194
+ expiry?: {
195
+ detect?: string;
196
+ };
197
+ }
198
+ interface AuthLoginConfig {
199
+ cmd?: string;
200
+ url?: string;
201
+ interactive?: boolean;
202
+ requiresCallbackUrl?: boolean;
203
+ completesWhen?: {
204
+ cmd?: string;
205
+ exitCode?: number;
206
+ http?: {
207
+ method: string;
208
+ url: string;
209
+ expectStatus: number;
210
+ };
211
+ };
212
+ }
213
+ interface AuthRefreshConfig {
214
+ cmd?: string;
215
+ url?: string;
216
+ every?: string;
217
+ }
218
+ interface InstallMethod {
219
+ method: string;
220
+ package?: string;
221
+ url?: string;
222
+ path?: string;
223
+ extractBin?: string;
224
+ verifySha256?: string;
225
+ global?: boolean;
226
+ user?: boolean;
227
+ }
228
+ interface VersionCheck {
229
+ cmd: string;
230
+ parse: string;
231
+ range: string;
232
+ timeoutMs?: number;
233
+ }
234
+ interface CostOverride {
235
+ costClass?: "trivial" | "metered" | "expensive";
236
+ costUnitsPerCall?: number;
237
+ currency?: string;
238
+ }
239
+ interface RetryPolicy {
240
+ maxAttempts: number;
241
+ backoff?: "fixed" | "exponential";
242
+ initialMs?: number;
243
+ }
244
+ interface HealthCheckConfig {
245
+ method: "ping" | "exec" | "http" | "noop";
246
+ cmd?: string;
247
+ http?: {
248
+ method: string;
249
+ url: string;
250
+ expectStatus: number;
251
+ };
252
+ expectExit?: number;
253
+ every?: string;
254
+ timeoutMs?: number;
255
+ }
256
+ interface LoginArgs {
257
+ context: DriverContext;
258
+ signal: AbortSignal;
259
+ }
260
+ type LoginResult = {
261
+ ok: true;
262
+ } | {
263
+ ok: false;
264
+ reason: "user_cancelled" | "callback_failed" | "upstream_error";
265
+ message?: string;
266
+ };
267
+ interface RefreshArgs {
268
+ context: DriverContext;
269
+ signal: AbortSignal;
270
+ }
271
+ type RefreshResult = {
272
+ ok: true;
273
+ nextRefreshAt?: string;
274
+ } | {
275
+ ok: false;
276
+ reason: "auth_expired" | "upstream_error";
277
+ message?: string;
278
+ };
279
+ interface ParseOutputArgs {
280
+ exitCode?: number;
281
+ stdout: string | Uint8Array;
282
+ stderr: string;
283
+ expected: {
284
+ format: "text" | "json" | "yaml" | "binary";
285
+ };
286
+ }
287
+ interface ParseOutputResult {
288
+ ok: boolean;
289
+ value?: unknown;
290
+ error?: {
291
+ code: string;
292
+ message: string;
293
+ retryable?: boolean;
294
+ };
295
+ }
296
+ interface DetectExpiryArgs {
297
+ exitCode?: number;
298
+ httpStatus?: number;
299
+ exception?: {
300
+ name: string;
301
+ message: string;
302
+ };
303
+ headers?: Record<string, string>;
304
+ }
305
+ /**
306
+ * Host-registrable provider handle returned by {@link defineDriver}.
307
+ * Resolver consumes this; kind-specific runtimes wrap their dispatch
308
+ * around the handle's `execute` map.
309
+ */
310
+ interface DriverHandle {
311
+ readonly id: string;
312
+ readonly name: string;
313
+ readonly description: string;
314
+ readonly version?: string;
315
+ readonly kind: DriverKind;
316
+ readonly implements: readonly ImplementsEntry[];
317
+ readonly install: readonly InstallMethod[];
318
+ readonly versionCheck?: VersionCheck;
319
+ readonly auth?: AuthConfig;
320
+ readonly network: {
321
+ egress: readonly string[];
322
+ ingress: readonly string[];
323
+ };
324
+ readonly region: readonly string[];
325
+ readonly policyTags: readonly string[];
326
+ readonly costOverride?: CostOverride;
327
+ readonly timeoutOverrideMs?: number;
328
+ readonly retryOverride?: RetryPolicy;
329
+ readonly healthCheck?: HealthCheckConfig;
330
+ readonly tags: readonly string[];
331
+ readonly metadata: Record<string, unknown>;
332
+ /** Execute body for tool id; throws if the id isn't in `implements[]`. */
333
+ readonly execute: Record<string, ExecuteFn>;
334
+ /** Optional behavioural adapters. */
335
+ readonly login?: (args: LoginArgs) => Promise<LoginResult>;
336
+ readonly refresh?: (args: RefreshArgs) => Promise<RefreshResult>;
337
+ readonly parseOutput?: (args: ParseOutputArgs) => ParseOutputResult;
338
+ readonly detectExpiry?: (args: DetectExpiryArgs) => boolean;
339
+ }
340
+ /**
341
+ * Resolver decision: which provider to dispatch a contract call to.
342
+ */
343
+ interface ResolverInput {
344
+ tool: ToolHandle;
345
+ /** Set of registered providers implementing this tool. */
346
+ candidates: readonly DriverHandle[];
347
+ /** Per-call context (workspace policy, region constraint, pinned provider, user). */
348
+ context: ResolverContext;
349
+ }
350
+ interface ResolverContext {
351
+ /** Workspace-level policy filter. */
352
+ policyAllowedTags?: readonly string[];
353
+ policyForbiddenTags?: readonly string[];
354
+ /** Region constraint (e.g. "EU"). */
355
+ regionConstraint?: string;
356
+ /** Caller-pinned provider id (overrides cost ranking). */
357
+ pinnedProvider?: string;
358
+ /** User context for tier-aware routing. */
359
+ user?: {
360
+ id?: string;
361
+ tier?: string;
362
+ };
363
+ /** Free-form additional context. */
364
+ [key: string]: unknown;
365
+ }
366
+ type ResolverResult = {
367
+ ok: true;
368
+ provider: DriverHandle;
369
+ implementsEntry: ImplementsEntry;
370
+ } | {
371
+ ok: false;
372
+ error: {
373
+ code: "no_route" | "pinned_provider_unavailable" | "policy_violation" | "region_mismatch";
374
+ message: string;
375
+ rejected?: ReadonlyArray<{
376
+ providerId: string;
377
+ phase: number;
378
+ reason: string;
379
+ }>;
380
+ };
381
+ };
382
+
383
+ export { type AuthConfig as A, type CostOverride as C, type DriverDefinition as D, type ExecuteArgs as E, type HealthCheckConfig as H, type ImplementsEntry as I, type LoginArgs as L, type MappingValue as M, type ParseOutputArgs as P, type ResolverContext as R, type ToolImplementation as T, type VersionCheck as V, type DriverHandle as a, type ResolverResult as b, type AuthLoginConfig as c, type AuthRefreshConfig as d, type DetectExpiryArgs as e, type DriverContext as f, type ExecuteFn as g, type InstallMethod as h, type LoginResult as i, type ParseOutputResult as j, type RefreshArgs as k, type RefreshResult as l, type ResolverInput as m, type RetryPolicy as n, type TypedExecuteFn as o, implementTool as p };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@agentproto/driver",
3
+ "version": "0.1.1",
4
+ "description": "agentproto/provider-runtime — reference implementation of AIP-30 DRIVER.md `defineDriver` contract. Resolves multi-provider routing, applies schema-narrowing + mapping, and dispatches contract calls to provider bodies. Composes with @agentproto/tool (AIP-14 contracts) and the kind-specific runtime packages (provider-cli, provider-http, provider-mcp, provider-sdk). Framework adapters (Mastra, AI SDK, …) live in sibling `@agentproto/adapter-*` packages.",
5
+ "keywords": [
6
+ "agentproto",
7
+ "aip-30",
8
+ "provider",
9
+ "provider-runtime",
10
+ "defineDriver",
11
+ "resolver",
12
+ "open-standard",
13
+ "agentic"
14
+ ],
15
+ "homepage": "https://agentproto.sh/docs/aip-30",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/agentproto/ts"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/agentproto/ts/issues"
22
+ },
23
+ "license": "MIT",
24
+ "type": "module",
25
+ "main": "dist/index.mjs",
26
+ "module": "dist/index.mjs",
27
+ "types": "dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.mjs",
32
+ "default": "./dist/index.mjs"
33
+ },
34
+ "./manifest": {
35
+ "types": "./dist/manifest/index.d.ts",
36
+ "import": "./dist/manifest/index.mjs",
37
+ "default": "./dist/manifest/index.mjs"
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "README.md",
44
+ "LICENSE"
45
+ ],
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "dependencies": {
50
+ "gray-matter": "^4.0.3",
51
+ "zod": "^4.4.3",
52
+ "@agentproto/define-doctype": "0.1.0",
53
+ "@agentproto/manifest": "0.2.0",
54
+ "@agentproto/tool": "0.1.1"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^25.6.2",
58
+ "tsup": "^8.5.1",
59
+ "typescript": "^5.9.3",
60
+ "vitest": "^3.2.4",
61
+ "@agentproto/tooling": "0.1.0-alpha.0"
62
+ },
63
+ "scripts": {
64
+ "dev": "tsup --watch",
65
+ "build": "tsup",
66
+ "clean": "rm -rf dist",
67
+ "check-types": "tsc --noEmit",
68
+ "test": "vitest run",
69
+ "test:watch": "vitest"
70
+ }
71
+ }