@beignet/core 0.0.34 → 0.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +145 -15
- package/dist/agent-capabilities/index.d.ts +197 -0
- package/dist/agent-capabilities/index.d.ts.map +1 -0
- package/dist/agent-capabilities/index.js +282 -0
- package/dist/agent-capabilities/index.js.map +1 -0
- package/dist/application/index.d.ts +2 -2
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +13 -3
- package/dist/application/index.js.map +1 -1
- package/dist/client/client.d.ts +2 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +33 -16
- package/dist/client/client.js.map +1 -1
- package/dist/contracts/contract-builder.d.ts +5 -3
- package/dist/contracts/contract-builder.d.ts.map +1 -1
- package/dist/contracts/contract-builder.js +3 -12
- package/dist/contracts/contract-builder.js.map +1 -1
- package/dist/contracts/contract-group.d.ts +2 -2
- package/dist/contracts/contract-group.d.ts.map +1 -1
- package/dist/contracts/contract-group.js +2 -1
- package/dist/contracts/contract-group.js.map +1 -1
- package/dist/contracts/metadata.d.ts +3 -0
- package/dist/contracts/metadata.d.ts.map +1 -0
- package/dist/contracts/metadata.js +14 -0
- package/dist/contracts/metadata.js.map +1 -0
- package/dist/contracts/types.d.ts +20 -0
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.js.map +1 -1
- package/dist/events/index.d.ts +4 -4
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +5 -5
- package/dist/events/index.js.map +1 -1
- package/dist/idempotency/index.d.ts.map +1 -1
- package/dist/idempotency/index.js +14 -9
- package/dist/idempotency/index.js.map +1 -1
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +10 -4
- package/dist/jobs/index.js.map +1 -1
- package/dist/openapi/index.js +15 -8
- package/dist/openapi/index.js.map +1 -1
- package/dist/ports/auth.d.ts +2 -0
- package/dist/ports/auth.d.ts.map +1 -1
- package/dist/ports/auth.js +2 -0
- package/dist/ports/auth.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/provider.d.ts +9 -1
- package/dist/providers/provider.d.ts.map +1 -1
- package/dist/providers/provider.js.map +1 -1
- package/dist/schedules/index.d.ts +6 -13
- package/dist/schedules/index.d.ts.map +1 -1
- package/dist/schedules/index.js +24 -44
- package/dist/schedules/index.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/request-executor.d.ts +72 -0
- package/dist/server/request-executor.d.ts.map +1 -0
- package/dist/server/request-executor.js +781 -0
- package/dist/server/request-executor.js.map +1 -0
- package/dist/server/request-preparation.d.ts +39 -0
- package/dist/server/request-preparation.d.ts.map +1 -0
- package/dist/server/request-preparation.js +248 -0
- package/dist/server/request-preparation.js.map +1 -0
- package/dist/server/response-finalization.d.ts +34 -0
- package/dist/server/response-finalization.d.ts.map +1 -0
- package/dist/server/response-finalization.js +279 -0
- package/dist/server/response-finalization.js.map +1 -0
- package/dist/server/route-definitions.d.ts +139 -0
- package/dist/server/route-definitions.d.ts.map +1 -0
- package/dist/server/route-definitions.js +94 -0
- package/dist/server/route-definitions.js.map +1 -0
- package/dist/server/route-matching.d.ts +15 -0
- package/dist/server/route-matching.d.ts.map +1 -0
- package/dist/server/route-matching.js +46 -0
- package/dist/server/route-matching.js.map +1 -0
- package/dist/server/server.d.ts +6 -147
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +29 -1350
- package/dist/server/server.js.map +1 -1
- package/dist/server/use-case-route.d.ts +1 -1
- package/dist/tracing/index.d.ts +1 -1
- package/dist/tracing/index.d.ts.map +1 -1
- package/dist/tracing/index.js.map +1 -1
- package/dist/uploads/client.js +4 -7
- package/dist/uploads/client.js.map +1 -1
- package/dist/uploads/index.d.ts.map +1 -1
- package/dist/uploads/index.js +59 -12
- package/dist/uploads/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/app-architecture/SKILL.md +27 -1
- package/src/agent-capabilities/index.ts +643 -0
- package/src/application/index.ts +19 -5
- package/src/client/client.ts +41 -17
- package/src/contracts/contract-builder.ts +8 -16
- package/src/contracts/contract-group.ts +4 -6
- package/src/contracts/metadata.ts +20 -0
- package/src/contracts/types.ts +39 -0
- package/src/events/index.ts +16 -20
- package/src/idempotency/index.ts +17 -9
- package/src/jobs/index.ts +8 -3
- package/src/openapi/index.ts +24 -12
- package/src/ports/auth.ts +2 -0
- package/src/providers/index.ts +1 -0
- package/src/providers/provider.ts +17 -2
- package/src/schedules/index.ts +57 -63
- package/src/server/index.ts +4 -2
- package/src/server/request-executor.ts +1170 -0
- package/src/server/request-preparation.ts +409 -0
- package/src/server/response-finalization.ts +385 -0
- package/src/server/route-definitions.ts +338 -0
- package/src/server/route-matching.ts +64 -0
- package/src/server/server.ts +63 -2139
- package/src/server/use-case-route.ts +1 -1
- package/src/tracing/index.ts +1 -0
- package/src/uploads/client.ts +4 -7
- package/src/uploads/index.ts +70 -12
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import {
|
|
3
|
+
createProviderInstrumentation,
|
|
4
|
+
type ProviderInstrumentationTarget,
|
|
5
|
+
} from "../providers/index.js";
|
|
6
|
+
import { runWithResolvedTracingContext } from "../tracing/execution.js";
|
|
7
|
+
import type { TracingPort } from "../tracing/index.js";
|
|
8
|
+
|
|
9
|
+
/** Any Standard Schema compatible validator. */
|
|
10
|
+
export type AgentCapabilitySchema = StandardSchemaV1<unknown, unknown>;
|
|
11
|
+
|
|
12
|
+
/** Value or promise of that value. */
|
|
13
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
14
|
+
|
|
15
|
+
/** Infer the input accepted by a capability schema. */
|
|
16
|
+
export type InferAgentCapabilitySchemaInput<Schema extends StandardSchemaV1> =
|
|
17
|
+
StandardSchemaV1.InferInput<Schema>;
|
|
18
|
+
|
|
19
|
+
/** Infer the parsed output produced by a capability schema. */
|
|
20
|
+
export type InferAgentCapabilitySchemaOutput<Schema extends StandardSchemaV1> =
|
|
21
|
+
StandardSchemaV1.InferOutput<Schema>;
|
|
22
|
+
|
|
23
|
+
/** A typed action that an authenticated agent transport may expose. */
|
|
24
|
+
export interface AgentCapabilityDef<
|
|
25
|
+
Name extends string = string,
|
|
26
|
+
Input extends AgentCapabilitySchema = AgentCapabilitySchema,
|
|
27
|
+
Output extends AgentCapabilitySchema = AgentCapabilitySchema,
|
|
28
|
+
Ctx = unknown,
|
|
29
|
+
Principal = unknown,
|
|
30
|
+
> {
|
|
31
|
+
readonly kind: "agentCapability";
|
|
32
|
+
readonly name: Name;
|
|
33
|
+
readonly description: string;
|
|
34
|
+
readonly input: Input;
|
|
35
|
+
readonly output: Output;
|
|
36
|
+
readonly handle: (args: {
|
|
37
|
+
capability: AgentCapabilityDef<Name, Input, Output, Ctx, Principal>;
|
|
38
|
+
ctx: Ctx;
|
|
39
|
+
principal: Principal;
|
|
40
|
+
input: InferAgentCapabilitySchemaOutput<Input>;
|
|
41
|
+
}) => MaybePromise<InferAgentCapabilitySchemaInput<Output>>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Broad capability definition shape tied to one context and principal. */
|
|
45
|
+
export interface AgentCapabilityFor<Ctx, Principal> {
|
|
46
|
+
readonly kind: "agentCapability";
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly description: string;
|
|
49
|
+
readonly input: AgentCapabilitySchema;
|
|
50
|
+
readonly output: AgentCapabilitySchema;
|
|
51
|
+
readonly handle: (args: {
|
|
52
|
+
capability: never;
|
|
53
|
+
ctx: Ctx;
|
|
54
|
+
principal: Principal;
|
|
55
|
+
input: never;
|
|
56
|
+
}) => MaybePromise<unknown>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Broad capability definition shape used by registries and adapters. */
|
|
60
|
+
export type AnyAgentCapabilityDef = AgentCapabilityFor<never, never>;
|
|
61
|
+
|
|
62
|
+
/** Infer a capability's application context. */
|
|
63
|
+
export type AgentCapabilityContext<Capability extends AnyAgentCapabilityDef> =
|
|
64
|
+
Capability extends { handle(args: { ctx: infer Ctx }): unknown }
|
|
65
|
+
? Ctx
|
|
66
|
+
: never;
|
|
67
|
+
|
|
68
|
+
/** Infer a capability's verified principal. */
|
|
69
|
+
export type AgentCapabilityPrincipal<Capability extends AnyAgentCapabilityDef> =
|
|
70
|
+
Capability extends { handle(args: { principal: infer Principal }): unknown }
|
|
71
|
+
? Principal
|
|
72
|
+
: never;
|
|
73
|
+
|
|
74
|
+
/** Infer a capability's raw input. */
|
|
75
|
+
export type AgentCapabilityInput<Capability extends AnyAgentCapabilityDef> =
|
|
76
|
+
InferAgentCapabilitySchemaInput<Capability["input"]>;
|
|
77
|
+
|
|
78
|
+
/** Infer the parsed input passed to capability context and execution. */
|
|
79
|
+
export type AgentCapabilityParsedInput<
|
|
80
|
+
Capability extends AnyAgentCapabilityDef,
|
|
81
|
+
> = InferAgentCapabilitySchemaOutput<Capability["input"]>;
|
|
82
|
+
|
|
83
|
+
/** Infer a capability's validated output. */
|
|
84
|
+
export type AgentCapabilityOutput<Capability extends AnyAgentCapabilityDef> =
|
|
85
|
+
InferAgentCapabilitySchemaOutput<Capability["output"]>;
|
|
86
|
+
|
|
87
|
+
/** Options accepted by `defineAgentCapability(...)`. */
|
|
88
|
+
export interface DefineAgentCapabilityOptions<
|
|
89
|
+
Name extends string,
|
|
90
|
+
Input extends AgentCapabilitySchema,
|
|
91
|
+
Output extends AgentCapabilitySchema,
|
|
92
|
+
Ctx,
|
|
93
|
+
Principal,
|
|
94
|
+
> {
|
|
95
|
+
description: string;
|
|
96
|
+
input: Input;
|
|
97
|
+
output: Output;
|
|
98
|
+
handle(args: {
|
|
99
|
+
capability: AgentCapabilityDef<Name, Input, Output, Ctx, Principal>;
|
|
100
|
+
ctx: Ctx;
|
|
101
|
+
principal: Principal;
|
|
102
|
+
input: InferAgentCapabilitySchemaOutput<Input>;
|
|
103
|
+
}): MaybePromise<InferAgentCapabilitySchemaInput<Output>>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Context-bound capability definition helpers. */
|
|
107
|
+
export interface AgentCapabilities<Ctx, Principal> {
|
|
108
|
+
defineAgentCapability<
|
|
109
|
+
const Name extends string,
|
|
110
|
+
Input extends AgentCapabilitySchema,
|
|
111
|
+
Output extends AgentCapabilitySchema,
|
|
112
|
+
>(
|
|
113
|
+
name: Name,
|
|
114
|
+
options: DefineAgentCapabilityOptions<Name, Input, Output, Ctx, Principal>,
|
|
115
|
+
): AgentCapabilityDef<Name, Input, Output, Ctx, Principal>;
|
|
116
|
+
defineAgentCapabilityRegistry<
|
|
117
|
+
const Definitions extends readonly AgentCapabilityFor<Ctx, Principal>[],
|
|
118
|
+
>(definitions: Definitions): AgentCapabilityRegistry<Definitions>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Bind application context and principal types once for capability definitions. */
|
|
122
|
+
export function createAgentCapabilities<Ctx, Principal>(): AgentCapabilities<
|
|
123
|
+
Ctx,
|
|
124
|
+
Principal
|
|
125
|
+
> {
|
|
126
|
+
return {
|
|
127
|
+
defineAgentCapability(name, options) {
|
|
128
|
+
return {
|
|
129
|
+
kind: "agentCapability",
|
|
130
|
+
name,
|
|
131
|
+
description: options.description,
|
|
132
|
+
input: options.input,
|
|
133
|
+
output: options.output,
|
|
134
|
+
handle: options.handle,
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
defineAgentCapabilityRegistry,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Error thrown when a capability registry is ambiguous. */
|
|
142
|
+
export class AgentCapabilityRegistryError extends Error {
|
|
143
|
+
constructor(message: string) {
|
|
144
|
+
super(message);
|
|
145
|
+
this.name = "AgentCapabilityRegistryError";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Registered capability definitions available to execution adapters. */
|
|
150
|
+
export interface AgentCapabilityRegistry<
|
|
151
|
+
Definitions extends
|
|
152
|
+
readonly AnyAgentCapabilityDef[] = readonly AnyAgentCapabilityDef[],
|
|
153
|
+
> {
|
|
154
|
+
readonly definitions: Definitions;
|
|
155
|
+
get(name: string): Definitions[number] | undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Build a capability registry and reject duplicate external names. */
|
|
159
|
+
function defineAgentCapabilityRegistry<
|
|
160
|
+
const Definitions extends readonly AnyAgentCapabilityDef[],
|
|
161
|
+
>(definitions: Definitions): AgentCapabilityRegistry<Definitions> {
|
|
162
|
+
const byName = new Map<string, Definitions[number]>();
|
|
163
|
+
|
|
164
|
+
for (const capability of definitions) {
|
|
165
|
+
if (byName.has(capability.name)) {
|
|
166
|
+
throw new AgentCapabilityRegistryError(
|
|
167
|
+
`Duplicate agent capability definition "${capability.name}" in registry.`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
byName.set(capability.name, capability);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
definitions: [...definitions] as unknown as Definitions,
|
|
175
|
+
get(name) {
|
|
176
|
+
return byName.get(name);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Stable capability execution failure categories. */
|
|
182
|
+
export type AgentCapabilityErrorCode =
|
|
183
|
+
| "unknown_capability"
|
|
184
|
+
| "invalid_input"
|
|
185
|
+
| "invalid_output"
|
|
186
|
+
| "execution_failed";
|
|
187
|
+
|
|
188
|
+
/** Error raised by capability lookup, validation, or execution. */
|
|
189
|
+
export class AgentCapabilityError extends Error {
|
|
190
|
+
readonly code: AgentCapabilityErrorCode;
|
|
191
|
+
readonly capabilityName: string;
|
|
192
|
+
readonly issues?: readonly StandardSchemaV1.Issue[];
|
|
193
|
+
override readonly cause?: unknown;
|
|
194
|
+
|
|
195
|
+
constructor(args: {
|
|
196
|
+
code: AgentCapabilityErrorCode;
|
|
197
|
+
capabilityName: string;
|
|
198
|
+
message: string;
|
|
199
|
+
issues?: readonly StandardSchemaV1.Issue[];
|
|
200
|
+
cause?: unknown;
|
|
201
|
+
}) {
|
|
202
|
+
super(args.message);
|
|
203
|
+
this.name = "AgentCapabilityError";
|
|
204
|
+
this.code = args.code;
|
|
205
|
+
this.capabilityName = args.capabilityName;
|
|
206
|
+
this.issues = args.issues;
|
|
207
|
+
this.cause = args.cause;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** A parsed invocation passed to the app-owned context resolver. */
|
|
212
|
+
export type AgentCapabilityContextRequest<
|
|
213
|
+
Definitions extends readonly AnyAgentCapabilityDef[],
|
|
214
|
+
Principal,
|
|
215
|
+
> = {
|
|
216
|
+
[Capability in Definitions[number] as Capability["name"]]: {
|
|
217
|
+
capability: Capability;
|
|
218
|
+
name: Capability["name"];
|
|
219
|
+
principal: Principal;
|
|
220
|
+
input: InferAgentCapabilitySchemaOutput<Capability["input"]>;
|
|
221
|
+
};
|
|
222
|
+
}[Definitions[number]["name"]];
|
|
223
|
+
|
|
224
|
+
/** Stage reached by an agent capability attempt. */
|
|
225
|
+
export type AgentCapabilityRunStage =
|
|
226
|
+
| "lookup"
|
|
227
|
+
| "input"
|
|
228
|
+
| "authorization"
|
|
229
|
+
| "context"
|
|
230
|
+
| "handler"
|
|
231
|
+
| "output";
|
|
232
|
+
|
|
233
|
+
/** Successful capability lifecycle event with validated application data. */
|
|
234
|
+
export type AgentCapabilityCompletedRunEvent<
|
|
235
|
+
Ctx,
|
|
236
|
+
Principal,
|
|
237
|
+
Definitions extends
|
|
238
|
+
readonly AnyAgentCapabilityDef[] = readonly AnyAgentCapabilityDef[],
|
|
239
|
+
Capability extends Definitions[number] = Definitions[number],
|
|
240
|
+
> = Capability extends Definitions[number]
|
|
241
|
+
? {
|
|
242
|
+
name: Capability["name"];
|
|
243
|
+
phase: "end";
|
|
244
|
+
stage: "output";
|
|
245
|
+
capability: Capability;
|
|
246
|
+
ctx: Ctx;
|
|
247
|
+
principal: Principal;
|
|
248
|
+
input: AgentCapabilityParsedInput<Capability>;
|
|
249
|
+
output: AgentCapabilityOutput<Capability>;
|
|
250
|
+
durationMs: number;
|
|
251
|
+
error?: undefined;
|
|
252
|
+
}
|
|
253
|
+
: never;
|
|
254
|
+
|
|
255
|
+
/** Lifecycle event observed around a capability attempt. */
|
|
256
|
+
export type AgentCapabilityRunEvent<
|
|
257
|
+
Ctx,
|
|
258
|
+
Principal,
|
|
259
|
+
Definitions extends
|
|
260
|
+
readonly AnyAgentCapabilityDef[] = readonly AnyAgentCapabilityDef[],
|
|
261
|
+
> =
|
|
262
|
+
| {
|
|
263
|
+
name: string;
|
|
264
|
+
phase: "start";
|
|
265
|
+
stage: "lookup";
|
|
266
|
+
capability?: Definitions[number];
|
|
267
|
+
ctx?: undefined;
|
|
268
|
+
principal: Principal;
|
|
269
|
+
input?: undefined;
|
|
270
|
+
output?: undefined;
|
|
271
|
+
durationMs?: undefined;
|
|
272
|
+
error?: undefined;
|
|
273
|
+
}
|
|
274
|
+
| AgentCapabilityCompletedRunEvent<Ctx, Principal, Definitions>
|
|
275
|
+
| {
|
|
276
|
+
name: string;
|
|
277
|
+
phase: "error";
|
|
278
|
+
stage: AgentCapabilityRunStage;
|
|
279
|
+
capability?: Definitions[number];
|
|
280
|
+
ctx?: Ctx;
|
|
281
|
+
principal: Principal;
|
|
282
|
+
/** Present only when input validation completed successfully. */
|
|
283
|
+
input?: unknown;
|
|
284
|
+
output?: undefined;
|
|
285
|
+
durationMs: number;
|
|
286
|
+
error: unknown;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** Best-effort lifecycle observer for capability execution. */
|
|
290
|
+
export type AgentCapabilityHook<
|
|
291
|
+
Ctx,
|
|
292
|
+
Principal,
|
|
293
|
+
Definitions extends
|
|
294
|
+
readonly AnyAgentCapabilityDef[] = readonly AnyAgentCapabilityDef[],
|
|
295
|
+
> = (
|
|
296
|
+
event: AgentCapabilityRunEvent<Ctx, Principal, Definitions>,
|
|
297
|
+
) => MaybePromise<void>;
|
|
298
|
+
|
|
299
|
+
/** Options for a capability executor. */
|
|
300
|
+
export interface CreateAgentCapabilityExecutorOptions<
|
|
301
|
+
Ctx,
|
|
302
|
+
Principal,
|
|
303
|
+
Definitions extends readonly AnyAgentCapabilityDef[],
|
|
304
|
+
> {
|
|
305
|
+
registry: AgentCapabilityRegistry<Definitions>;
|
|
306
|
+
createContext(
|
|
307
|
+
request: AgentCapabilityContextRequest<Definitions, Principal>,
|
|
308
|
+
): MaybePromise<Ctx>;
|
|
309
|
+
hooks?: readonly AgentCapabilityHook<Ctx, Principal, Definitions>[];
|
|
310
|
+
/** Independent target used to observe failures before context exists. */
|
|
311
|
+
instrumentation?: ProviderInstrumentationTarget;
|
|
312
|
+
tracing?: TracingPort;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Raw dynamic invocation accepted by protocol adapters. */
|
|
316
|
+
export interface DynamicAgentCapabilityInvocation<Principal> {
|
|
317
|
+
name: string;
|
|
318
|
+
principal: Principal;
|
|
319
|
+
input: unknown;
|
|
320
|
+
/**
|
|
321
|
+
* Transport-owned authorization against the exact parsed input that will be
|
|
322
|
+
* passed to context construction and the capability handler.
|
|
323
|
+
*/
|
|
324
|
+
authorize?(
|
|
325
|
+
request: DynamicAgentCapabilityAuthorization<Principal>,
|
|
326
|
+
): MaybePromise<void>;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Parsed invocation exposed to a dynamic transport authorization callback. */
|
|
330
|
+
export interface DynamicAgentCapabilityAuthorization<Principal> {
|
|
331
|
+
capability: AnyAgentCapabilityDef;
|
|
332
|
+
name: string;
|
|
333
|
+
principal: Principal;
|
|
334
|
+
input: unknown;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Typed and dynamic execution surface for one capability registry. */
|
|
338
|
+
export interface AgentCapabilityExecutor<
|
|
339
|
+
Principal,
|
|
340
|
+
Definitions extends readonly AnyAgentCapabilityDef[],
|
|
341
|
+
> {
|
|
342
|
+
execute<Name extends Definitions[number]["name"]>(args: {
|
|
343
|
+
name: Name;
|
|
344
|
+
principal: Principal;
|
|
345
|
+
input: AgentCapabilityInput<Extract<Definitions[number], { name: Name }>>;
|
|
346
|
+
}): Promise<
|
|
347
|
+
AgentCapabilityOutput<Extract<Definitions[number], { name: Name }>>
|
|
348
|
+
>;
|
|
349
|
+
executeDynamic(
|
|
350
|
+
args: DynamicAgentCapabilityInvocation<Principal>,
|
|
351
|
+
): Promise<unknown>;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function formatPath(path: StandardSchemaV1.Issue["path"]): string {
|
|
355
|
+
if (!path?.length) return "";
|
|
356
|
+
return path
|
|
357
|
+
.map((segment) =>
|
|
358
|
+
typeof segment === "object" && segment !== null && "key" in segment
|
|
359
|
+
? String(segment.key)
|
|
360
|
+
: String(segment),
|
|
361
|
+
)
|
|
362
|
+
.join(".");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function formatIssues(issues: readonly StandardSchemaV1.Issue[]): string {
|
|
366
|
+
return issues
|
|
367
|
+
.map((issue) => {
|
|
368
|
+
const path = formatPath(issue.path);
|
|
369
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
370
|
+
})
|
|
371
|
+
.join("; ");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function parseCapabilitySchema<Schema extends AgentCapabilitySchema>(
|
|
375
|
+
schema: Schema,
|
|
376
|
+
input: unknown,
|
|
377
|
+
args: {
|
|
378
|
+
capabilityName: string;
|
|
379
|
+
code: "invalid_input" | "invalid_output";
|
|
380
|
+
},
|
|
381
|
+
): Promise<InferAgentCapabilitySchemaOutput<Schema>> {
|
|
382
|
+
const result = await schema["~standard"].validate(input);
|
|
383
|
+
if (result.issues?.length) {
|
|
384
|
+
throw new AgentCapabilityError({
|
|
385
|
+
code: args.code,
|
|
386
|
+
capabilityName: args.capabilityName,
|
|
387
|
+
message: `Agent capability "${args.capabilityName}" ${args.code === "invalid_input" ? "input" : "output"} validation failed: ${formatIssues(result.issues)}`,
|
|
388
|
+
issues: result.issues,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
if ("value" in result) {
|
|
392
|
+
return result.value as InferAgentCapabilitySchemaOutput<Schema>;
|
|
393
|
+
}
|
|
394
|
+
throw new AgentCapabilityError({
|
|
395
|
+
code: args.code,
|
|
396
|
+
capabilityName: args.capabilityName,
|
|
397
|
+
message: `Agent capability "${args.capabilityName}" schema returned no value.`,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function instrumentationTarget(ctx: unknown): ProviderInstrumentationTarget {
|
|
402
|
+
if (!ctx || typeof ctx !== "object") return undefined;
|
|
403
|
+
if ("ports" in ctx) {
|
|
404
|
+
return (ctx as { ports?: ProviderInstrumentationTarget }).ports;
|
|
405
|
+
}
|
|
406
|
+
return ctx as ProviderInstrumentationTarget;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function notifyHooks<
|
|
410
|
+
Ctx,
|
|
411
|
+
Principal,
|
|
412
|
+
Definitions extends readonly AnyAgentCapabilityDef[],
|
|
413
|
+
>(
|
|
414
|
+
hooks: readonly AgentCapabilityHook<Ctx, Principal, Definitions>[],
|
|
415
|
+
event: AgentCapabilityRunEvent<Ctx, Principal, Definitions>,
|
|
416
|
+
): Promise<void> {
|
|
417
|
+
await Promise.all(
|
|
418
|
+
hooks.map(async (hook) => {
|
|
419
|
+
try {
|
|
420
|
+
await hook(event);
|
|
421
|
+
} catch {
|
|
422
|
+
// Observers must not alter application behavior.
|
|
423
|
+
}
|
|
424
|
+
}),
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** Create a validated, traced executor for a capability registry. */
|
|
429
|
+
export function createAgentCapabilityExecutor<
|
|
430
|
+
const Definitions extends readonly AnyAgentCapabilityDef[],
|
|
431
|
+
>(
|
|
432
|
+
options: CreateAgentCapabilityExecutorOptions<
|
|
433
|
+
AgentCapabilityContext<Definitions[number]>,
|
|
434
|
+
AgentCapabilityPrincipal<Definitions[number]>,
|
|
435
|
+
Definitions
|
|
436
|
+
>,
|
|
437
|
+
): AgentCapabilityExecutor<
|
|
438
|
+
AgentCapabilityPrincipal<Definitions[number]>,
|
|
439
|
+
Definitions
|
|
440
|
+
> {
|
|
441
|
+
type Ctx = AgentCapabilityContext<Definitions[number]>;
|
|
442
|
+
type Principal = AgentCapabilityPrincipal<Definitions[number]>;
|
|
443
|
+
const hooks = options.hooks ?? [];
|
|
444
|
+
const instrumentationConfig = {
|
|
445
|
+
providerName: "agent-capabilities",
|
|
446
|
+
watcher: "agentCapabilities",
|
|
447
|
+
} as const;
|
|
448
|
+
|
|
449
|
+
async function executeDynamic(
|
|
450
|
+
invocation: DynamicAgentCapabilityInvocation<Principal>,
|
|
451
|
+
): Promise<unknown> {
|
|
452
|
+
const startedAt = Date.now();
|
|
453
|
+
let stage: AgentCapabilityRunStage = "lookup";
|
|
454
|
+
let ctx: Ctx | undefined;
|
|
455
|
+
let capability: Definitions[number] | undefined;
|
|
456
|
+
let input: unknown;
|
|
457
|
+
let hasValidatedInput = false;
|
|
458
|
+
let instrumentation = createProviderInstrumentation(
|
|
459
|
+
options.instrumentation,
|
|
460
|
+
instrumentationConfig,
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
capability = options.registry.get(invocation.name);
|
|
465
|
+
const observedName = capability?.name ?? "unknown";
|
|
466
|
+
instrumentation.custom({
|
|
467
|
+
name: "agentCapability.started",
|
|
468
|
+
label: `Agent capability ${observedName}`,
|
|
469
|
+
summary: "Capability execution started",
|
|
470
|
+
details: { capabilityName: observedName, phase: "start" },
|
|
471
|
+
});
|
|
472
|
+
await notifyHooks(hooks, {
|
|
473
|
+
name: invocation.name,
|
|
474
|
+
phase: "start",
|
|
475
|
+
stage,
|
|
476
|
+
principal: invocation.principal,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
if (!capability) {
|
|
480
|
+
throw new AgentCapabilityError({
|
|
481
|
+
code: "unknown_capability",
|
|
482
|
+
capabilityName: invocation.name,
|
|
483
|
+
message: `Unknown agent capability "${invocation.name}".`,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const registeredCapability = capability;
|
|
488
|
+
const runtimeCapability =
|
|
489
|
+
registeredCapability as unknown as AgentCapabilityDef<
|
|
490
|
+
string,
|
|
491
|
+
AgentCapabilitySchema,
|
|
492
|
+
AgentCapabilitySchema,
|
|
493
|
+
Ctx,
|
|
494
|
+
Principal
|
|
495
|
+
>;
|
|
496
|
+
return await runWithResolvedTracingContext({
|
|
497
|
+
tracing: options.tracing,
|
|
498
|
+
ctx: async () => {
|
|
499
|
+
stage = "input";
|
|
500
|
+
input = await parseCapabilitySchema(
|
|
501
|
+
runtimeCapability.input,
|
|
502
|
+
invocation.input,
|
|
503
|
+
{
|
|
504
|
+
capabilityName: runtimeCapability.name,
|
|
505
|
+
code: "invalid_input",
|
|
506
|
+
},
|
|
507
|
+
);
|
|
508
|
+
hasValidatedInput = true;
|
|
509
|
+
if (invocation.authorize) {
|
|
510
|
+
stage = "authorization";
|
|
511
|
+
await invocation.authorize({
|
|
512
|
+
capability: registeredCapability,
|
|
513
|
+
name: registeredCapability.name,
|
|
514
|
+
principal: invocation.principal,
|
|
515
|
+
input,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
stage = "context";
|
|
519
|
+
ctx = await options.createContext({
|
|
520
|
+
capability: registeredCapability,
|
|
521
|
+
name: registeredCapability.name,
|
|
522
|
+
principal: invocation.principal,
|
|
523
|
+
input,
|
|
524
|
+
} as AgentCapabilityContextRequest<Definitions, Principal>);
|
|
525
|
+
return ctx;
|
|
526
|
+
},
|
|
527
|
+
operation: {
|
|
528
|
+
name: `beignet.agent_capability ${registeredCapability.name}`,
|
|
529
|
+
type: "agentCapability",
|
|
530
|
+
kind: "internal",
|
|
531
|
+
attributes: {
|
|
532
|
+
"beignet.agent_capability.name": registeredCapability.name,
|
|
533
|
+
},
|
|
534
|
+
metricAttributes: {
|
|
535
|
+
"beignet.agent_capability.name": registeredCapability.name,
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
async run(resolvedCtx) {
|
|
539
|
+
ctx = resolvedCtx;
|
|
540
|
+
if (options.instrumentation === undefined) {
|
|
541
|
+
instrumentation = createProviderInstrumentation(
|
|
542
|
+
instrumentationTarget(ctx),
|
|
543
|
+
instrumentationConfig,
|
|
544
|
+
);
|
|
545
|
+
instrumentation.custom({
|
|
546
|
+
name: "agentCapability.started",
|
|
547
|
+
label: `Agent capability ${registeredCapability.name}`,
|
|
548
|
+
summary: "Capability execution started",
|
|
549
|
+
details: {
|
|
550
|
+
capabilityName: registeredCapability.name,
|
|
551
|
+
phase: "start",
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
stage = "handler";
|
|
557
|
+
const rawOutput = await runtimeCapability.handle({
|
|
558
|
+
capability: runtimeCapability,
|
|
559
|
+
ctx,
|
|
560
|
+
principal: invocation.principal,
|
|
561
|
+
input,
|
|
562
|
+
});
|
|
563
|
+
stage = "output";
|
|
564
|
+
const output = await parseCapabilitySchema(
|
|
565
|
+
runtimeCapability.output,
|
|
566
|
+
rawOutput,
|
|
567
|
+
{
|
|
568
|
+
capabilityName: registeredCapability.name,
|
|
569
|
+
code: "invalid_output",
|
|
570
|
+
},
|
|
571
|
+
);
|
|
572
|
+
const durationMs = Date.now() - startedAt;
|
|
573
|
+
instrumentation.custom({
|
|
574
|
+
name: "agentCapability.completed",
|
|
575
|
+
label: `Agent capability ${registeredCapability.name}`,
|
|
576
|
+
summary: "Capability execution completed",
|
|
577
|
+
details: {
|
|
578
|
+
capabilityName: registeredCapability.name,
|
|
579
|
+
phase: "end",
|
|
580
|
+
durationMs,
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
await notifyHooks(hooks, {
|
|
584
|
+
name: registeredCapability.name,
|
|
585
|
+
phase: "end",
|
|
586
|
+
stage,
|
|
587
|
+
capability: registeredCapability,
|
|
588
|
+
ctx,
|
|
589
|
+
principal: invocation.principal,
|
|
590
|
+
input,
|
|
591
|
+
output,
|
|
592
|
+
durationMs,
|
|
593
|
+
} as AgentCapabilityRunEvent<Ctx, Principal, Definitions>);
|
|
594
|
+
return output;
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
} catch (error) {
|
|
598
|
+
const wrapped =
|
|
599
|
+
error instanceof AgentCapabilityError
|
|
600
|
+
? error
|
|
601
|
+
: new AgentCapabilityError({
|
|
602
|
+
code: "execution_failed",
|
|
603
|
+
capabilityName: capability?.name ?? invocation.name,
|
|
604
|
+
message: `Agent capability "${capability?.name ?? invocation.name}" execution failed.`,
|
|
605
|
+
cause: error,
|
|
606
|
+
});
|
|
607
|
+
const durationMs = Date.now() - startedAt;
|
|
608
|
+
const observedName = capability?.name ?? "unknown";
|
|
609
|
+
instrumentation.custom({
|
|
610
|
+
name: "agentCapability.failed",
|
|
611
|
+
label: `Agent capability ${observedName}`,
|
|
612
|
+
summary: "Capability execution failed",
|
|
613
|
+
details: {
|
|
614
|
+
capabilityName: observedName,
|
|
615
|
+
phase: "error",
|
|
616
|
+
stage,
|
|
617
|
+
durationMs,
|
|
618
|
+
code: wrapped.code,
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
await notifyHooks(hooks, {
|
|
622
|
+
name: invocation.name,
|
|
623
|
+
phase: "error",
|
|
624
|
+
stage,
|
|
625
|
+
...(capability ? { capability } : {}),
|
|
626
|
+
ctx,
|
|
627
|
+
principal: invocation.principal,
|
|
628
|
+
durationMs,
|
|
629
|
+
error: wrapped,
|
|
630
|
+
...(hasValidatedInput ? { input } : {}),
|
|
631
|
+
} as AgentCapabilityRunEvent<Ctx, Principal, Definitions>);
|
|
632
|
+
throw wrapped;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return {
|
|
637
|
+
execute: executeDynamic as AgentCapabilityExecutor<
|
|
638
|
+
Principal,
|
|
639
|
+
Definitions
|
|
640
|
+
>["execute"],
|
|
641
|
+
executeDynamic,
|
|
642
|
+
};
|
|
643
|
+
}
|
package/src/application/index.ts
CHANGED
|
@@ -419,7 +419,7 @@ export interface CreateUseCaseOptions<Ctx> {
|
|
|
419
419
|
*
|
|
420
420
|
* Observers run in addition to the built-in instrumentation.
|
|
421
421
|
*/
|
|
422
|
-
onRun?: (event: UseCaseRunEvent<Ctx>) => void
|
|
422
|
+
onRun?: (event: UseCaseRunEvent<Ctx>) => void | Promise<void>;
|
|
423
423
|
|
|
424
424
|
/**
|
|
425
425
|
* Built-in use-case instrumentation.
|
|
@@ -442,6 +442,18 @@ export interface CreateUseCaseOptions<Ctx> {
|
|
|
442
442
|
validate?: boolean | { input?: boolean; output?: boolean };
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
+
function notifyUseCaseRunObserver<Ctx>(
|
|
446
|
+
observer: CreateUseCaseOptions<Ctx>["onRun"],
|
|
447
|
+
event: UseCaseRunEvent<Ctx>,
|
|
448
|
+
): void {
|
|
449
|
+
try {
|
|
450
|
+
const result = observer?.(event);
|
|
451
|
+
if (result) void result.catch(() => {});
|
|
452
|
+
} catch {
|
|
453
|
+
// Observers are best-effort instrumentation and cannot change execution.
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
445
457
|
type ValidationOptions = {
|
|
446
458
|
input: boolean;
|
|
447
459
|
output: boolean;
|
|
@@ -615,7 +627,9 @@ class UseCaseBuilder<
|
|
|
615
627
|
OutputSchema,
|
|
616
628
|
Emits
|
|
617
629
|
>,
|
|
618
|
-
private readonly onRun?: (
|
|
630
|
+
private readonly onRun?: (
|
|
631
|
+
event: UseCaseRunEvent<Ctx>,
|
|
632
|
+
) => void | Promise<void>,
|
|
619
633
|
private readonly validation: ValidationOptions = {
|
|
620
634
|
input: true,
|
|
621
635
|
output: true,
|
|
@@ -753,7 +767,7 @@ class UseCaseBuilder<
|
|
|
753
767
|
kind: useCaseKind,
|
|
754
768
|
})
|
|
755
769
|
: undefined;
|
|
756
|
-
onRun
|
|
770
|
+
notifyUseCaseRunObserver(onRun, {
|
|
757
771
|
name: useCaseName,
|
|
758
772
|
kind: useCaseKind,
|
|
759
773
|
phase: "start",
|
|
@@ -788,7 +802,7 @@ class UseCaseBuilder<
|
|
|
788
802
|
|
|
789
803
|
const durationMs = Date.now() - startedAt;
|
|
790
804
|
instrumentation?.end(durationMs);
|
|
791
|
-
onRun
|
|
805
|
+
notifyUseCaseRunObserver(onRun, {
|
|
792
806
|
name: useCaseName,
|
|
793
807
|
kind: useCaseKind,
|
|
794
808
|
phase: "end",
|
|
@@ -800,7 +814,7 @@ class UseCaseBuilder<
|
|
|
800
814
|
} catch (err) {
|
|
801
815
|
const durationMs = Date.now() - startedAt;
|
|
802
816
|
instrumentation?.error(durationMs, err);
|
|
803
|
-
onRun
|
|
817
|
+
notifyUseCaseRunObserver(onRun, {
|
|
804
818
|
name: useCaseName,
|
|
805
819
|
kind: useCaseKind,
|
|
806
820
|
phase: "error",
|