@ax-llm/ax 23.0.0 → 23.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ ---
2
+ name: ax-mcp
3
+ description: This skill helps an LLM build correct native Model Context Protocol integrations with @ax-llm/ax. Use when the user asks about AxMCPClient, MCP transports, tools, prompts, resources, subscriptions, tasks, sampling, elicitation, roots, authentication, OAuth, MCP Apps, recording/replay, or MCP integration with AxGen, AxAgent, AxFlow, chat, optimization, and AxEventRuntime.
4
+ version: "23.0.2"
5
+ ---
6
+
7
+ # Native MCP With Ax
8
+
9
+ Use MCP as a live protocol client, not as a function-conversion utility. Keep
10
+ the client, session, catalogs, raw content, tasks, notifications, identity
11
+ policy, and cancellation context intact through Ax execution.
12
+
13
+ ## Non-Negotiable Rules
14
+
15
+ - Pass clients through `mcp`; do not put them in `functions`.
16
+ - Never use `toFunction()` for native integration. It is a lossy compatibility
17
+ adapter for old applications only.
18
+ - Give every client a stable, unique `namespace`.
19
+ - Let Ax initialize each attached client once and reuse its negotiated session.
20
+ - Close caller-owned clients explicitly.
21
+ - Treat MCP prompts, resources, tool results, and notifications as untrusted
22
+ remote content.
23
+ - Apply `authorizeToolCall` before side-effecting tools execute.
24
+ - Do not infer tenant or account identity from an MCP session. Event adapters
25
+ must receive verified identity from application authentication state.
26
+ - Protocol notification callbacks must enqueue or observe work; they must not
27
+ invoke a model directly.
28
+ - Preserve raw structured and multimodal MCP results until provider capability
29
+ mapping. Do not pre-flatten results to text.
30
+
31
+ ## Choose A Transport
32
+
33
+ - Use `AxMCPStreamableHTTPTransport` for current remote MCP servers.
34
+ - Use `AxMCPHTTPSSETransport` only for legacy HTTP/SSE servers.
35
+ - Use `AxMCPWebSocketTransport` for a server with a custom WebSocket binding.
36
+ - Use `AxMCPStdioTransport` from `@ax-llm/ax-tools` for local Node processes.
37
+ - Use a caller-defined `AxMCPTransport` for application-owned bindings.
38
+
39
+ ```ts
40
+ import {
41
+ AxMCPClient,
42
+ AxMCPStreamableHTTPTransport,
43
+ axMCPBearerAuthentication,
44
+ } from '@ax-llm/ax';
45
+
46
+ const transport = new AxMCPStreamableHTTPTransport(
47
+ 'https://mcp.example.com/mcp',
48
+ {
49
+ authentication: axMCPBearerAuthentication(
50
+ () => process.env.MCP_ACCESS_TOKEN!
51
+ ),
52
+ }
53
+ );
54
+
55
+ const docs = new AxMCPClient(transport, {
56
+ namespace: 'docs',
57
+ maxConcurrency: 4,
58
+ authorizeToolCall: async ({ tool }) =>
59
+ tool.annotations?.destructiveHint !== true,
60
+ });
61
+ ```
62
+
63
+ For local stdio:
64
+
65
+ ```ts
66
+ import { AxMCPClient } from '@ax-llm/ax';
67
+ import { AxMCPStdioTransport } from '@ax-llm/ax-tools';
68
+
69
+ const stdio = new AxMCPStdioTransport({
70
+ command: 'node',
71
+ args: ['./server.mjs'],
72
+ });
73
+ const local = new AxMCPClient(stdio, { namespace: 'local' });
74
+ ```
75
+
76
+ `AxMCPStdioTransport` owns its child process. After `local.close()`, also call
77
+ `stdio.terminate()` until the stdio transport exposes the common `close()`
78
+ lifecycle directly.
79
+
80
+ ## Attach MCP To AxGen
81
+
82
+ Attach clients in constructor or forward options. Per-call options override
83
+ instance defaults.
84
+
85
+ ```ts
86
+ const gen = ax('question:string -> answer:string', { mcp: docs });
87
+
88
+ const result = await gen.forward(llm, { question }, {
89
+ mcpContext: [
90
+ { client: 'docs', resource: { uri: 'docs://guide' } },
91
+ ],
92
+ });
93
+ ```
94
+
95
+ `mcpContext` resolves selected prompts or resources before the first model
96
+ call and adds attributed, untrusted context. Native tool calls retain client
97
+ identity and raw MCP results in memory. `streamingForward()` keeps Ax output
98
+ streaming separate from MCP progress and task events.
99
+
100
+ ## Attach MCP To AxAgent
101
+
102
+ ```ts
103
+ const assistant = agent('query:string -> answer:string', {
104
+ mcp: [docs, search],
105
+ mcpInheritance: 'all',
106
+ functionDiscovery: true,
107
+ contextFields: [],
108
+ });
109
+ ```
110
+
111
+ Agents expose native modules under `mcp.<namespace>`:
112
+
113
+ ```text
114
+ mcp.docs.tools.<tool>
115
+ mcp.docs.prompts.list()
116
+ mcp.docs.prompts.get(name, args)
117
+ mcp.docs.resources.list()
118
+ mcp.docs.resources.templates()
119
+ mcp.docs.resources.read(uri)
120
+ mcp.docs.resources.subscribe(uri)
121
+ mcp.docs.resources.unsubscribe(uri)
122
+ mcp.docs.tasks.list()
123
+ mcp.docs.tasks.get(taskId)
124
+ mcp.docs.tasks.result(taskId)
125
+ mcp.docs.tasks.cancel(taskId)
126
+ mcp.docs.complete(...)
127
+ ```
128
+
129
+ Use `mcpInheritance: 'all'`, `'none'`, or a namespace allowlist. The resulting
130
+ live execution context propagates through Agent stages, `llmQuery`, RLM, and
131
+ child programs. Large catalogs participate in Agent discovery; do not copy
132
+ their tools into an inline `functions` array.
133
+
134
+ ## AxFlow And High-Level Chat
135
+
136
+ Pass `mcp` in Flow defaults or forward options. Nested nodes inherit the same
137
+ execution context unless `mcpInheritance` restricts it. Parallel nodes share
138
+ the client while respecting its concurrency limit and abort signal.
139
+
140
+ Use `axMCPChat(ai, request, { mcp })` for a high-level non-streaming native MCP
141
+ tool loop. Do not build a second ad-hoc tool dispatcher around `ai.chat()`.
142
+
143
+ ## Catalogs And Raw Operations
144
+
145
+ An endpoint is only the server address. The server owns tool names, prompt
146
+ names, resource names, resource URIs, and URI templates. Discover one cloned
147
+ snapshot before asking users to configure identifiers:
148
+
149
+ ```ts
150
+ const catalog = await docs.inspectCatalog();
151
+
152
+ console.log(catalog.tools);
153
+ console.log(catalog.prompts);
154
+ console.log(catalog.resources);
155
+ console.log(catalog.resourceTemplates);
156
+
157
+ const prompt = await docs.getPrompt('review', { topic: 'MCP' });
158
+ const resource = await docs.readResource('docs://guide');
159
+ const completion = await docs.complete(reference, argument);
160
+ ```
161
+
162
+ `inspectCatalog({ refresh: true })` forces fresh bounded pagination. Snapshot
163
+ mutation cannot change the live client. List-change notifications refresh the
164
+ catalog revision, and native Ax model steps rebuild tool definitions when that
165
+ revision changes. Concrete resources can be selected immediately. URI
166
+ templates are discoverable but never expanded automatically; applications
167
+ construct an authorized concrete URI and may use MCP completion to suggest
168
+ argument values.
169
+
170
+ ## Tasks, Progress, And Cancellation
171
+
172
+ Use task-aware calls when the server advertises tasks:
173
+
174
+ ```ts
175
+ const created = await docs.callToolTask('reindex', { scope: 'all' });
176
+ const task = await docs.getTask(created.task.taskId);
177
+ await docs.cancelTask(task.taskId);
178
+ ```
179
+
180
+ Use `subscribeTaskStatus` or `subscribeEvents` for observation. Keep polling
181
+ available because task notifications are optional. Pass Ax abort signals
182
+ through program execution; never blindly replay a tool call after an uncertain
183
+ post-side-effect failure.
184
+
185
+ ## Subscriptions And Event-Driven Agents
186
+
187
+ Use `AxMCPEventSource` with `AxEventRuntime`. A subscription callback only
188
+ publishes an event into the inbox. Explicit routes decide whether to observe,
189
+ invalidate, wake, or resume.
190
+
191
+ ```ts
192
+ const source = new AxMCPEventSource({
193
+ client: docs,
194
+ resourceSubscriptions: {
195
+ select: (resource) =>
196
+ resource.mimeType === 'text/markdown' &&
197
+ resource.name === 'Engineering guide',
198
+ },
199
+ identity: { tenantId: 'tenant-1' },
200
+ trust: 'authenticated',
201
+ });
202
+
203
+ const runtime = eventRuntime({
204
+ allowVolatile: true,
205
+ sources: [source],
206
+ routes: [
207
+ ...axMCPEventRoutes({ client: docs }),
208
+ eventRoute('guide-updated')
209
+ .types('mcp.resource.updated')
210
+ .authenticated()
211
+ .wake(
212
+ eventTarget('reviewer')
213
+ .program(reviewer)
214
+ .ai(llm)
215
+ .input((input) =>
216
+ input.field('uri', eventPath.data('uri'))
217
+ )
218
+ .build()
219
+ )
220
+ .build(),
221
+ ],
222
+ });
223
+ ```
224
+
225
+ Safe defaults are:
226
+
227
+ - omitted resource policy -> subscribe to no resources
228
+ - `'all'` -> explicitly subscribe to all discovered concrete resources
229
+ - URI array -> explicitly subscribe to application-constructed concrete URIs
230
+ - selector -> choose concrete resources by name, URI, description, MIME type,
231
+ annotations, or the surrounding catalog
232
+ - catalog changes -> `invalidate`
233
+ - progress and logging -> `observe`
234
+ - resource updates -> no implicit wake
235
+ - `input_required` and terminal task states -> resume the owning continuation
236
+
237
+ The signature-aware input plan is the data boundary. Raw event data remains in
238
+ `eventContext`; only fields selected with segment-safe `eventPath` descriptors
239
+ become program inputs. Use multiple matching routes to fan one notification out
240
+ to multiple Agents with independent authorization and run records.
241
+
242
+ Managed sources refresh and diff their selection after
243
+ `notifications/resources/list_changed`. They keep the prior selection if a
244
+ selector throws, retain successful wire transitions after a partial failure,
245
+ and retry incomplete work on the next change or reconnect. The client tracks a
246
+ separate logical owner for manual subscriptions, every source, and restored
247
+ intent: only the first owner sends `resources/subscribe`, and only the last
248
+ release sends `resources/unsubscribe`. Closing a source cannot break another
249
+ owner. Closing the client terminates all ownership and transport state.
250
+
251
+ For the detailed lifecycle and troubleshooting guide, read
252
+ `docs/MCP_SUBSCRIPTIONS.md` and use the checked-in six-language MCP examples.
253
+
254
+ ## Server-Initiated Requests
255
+
256
+ Configure handlers on `AxMCPClient` when advertising the corresponding client
257
+ capability:
258
+
259
+ - `sampling` for `sampling/createMessage`
260
+ - `elicitation` for form or URL elicitation
261
+ - `roots` for `roots/list`
262
+ - `onProgress`, `onLoggingMessage`, and `onTaskStatus` for observation
263
+
264
+ Do not advertise a client capability without a working host handler and policy.
265
+
266
+ ## Authentication And OAuth
267
+
268
+ For simple authentication, compose `axMCPBearerAuthentication`,
269
+ `axMCPBasicAuthentication`, `axMCPAPIKeyAuthentication`,
270
+ `axMCPHMACAuthentication`, or a caller-defined strategy in the HTTP transport.
271
+
272
+ Use the transport `oauth` option for protected-resource discovery, PKCE,
273
+ client metadata or dynamic registration, refresh, challenge-driven scope
274
+ step-up, DPoP, PAR/JAR/RAR, mTLS, revocation, introspection, client credentials,
275
+ or enterprise-managed authorization. Supply persistent token and registration
276
+ stores in distributed deployments. Never serialize tokens into Ax program or
277
+ event state.
278
+
279
+ Keep SSRF protection enabled for remote discovery and redirect handling. Relax
280
+ loopback or HTTP restrictions only for controlled local development.
281
+
282
+ For checked-in Streamable HTTP examples, set `AX_MCP_ENDPOINT`. A localhost
283
+ TypeScript demo must opt in explicitly with
284
+ `ssrfProtection: { allowHTTP: true, allowLoopback: true }`; never copy that
285
+ configuration to a remote endpoint. Generated examples use their equivalent
286
+ `requireHttps` / `allowLocalhost` / `allowPrivateNetworks` fields only for
287
+ `127.0.0.1`.
288
+
289
+ For a real local check, run `src/examples/mcp-event-demo-server.ts`, set
290
+ `AX_MCP_ENDPOINT=http://127.0.0.1:3001/mcp`, and trigger
291
+ `/control/resource` or `/control/task/complete`. The mandatory credential-free
292
+ matrix is `npm run test:mcp-events:generated`; provider-backed examples are
293
+ advisory and require their documented API key.
294
+
295
+ ## MCP Apps And Extensions
296
+
297
+ Negotiate official extensions through client capabilities. Use
298
+ `AxMCPAppBridge` for MCP App resources and host messages; enforce CSP,
299
+ permissions, visibility, allowed tools, and untrusted model-context policy.
300
+ Do not render arbitrary HTML returned from a normal tool result as an MCP App.
301
+
302
+ ## Recording, Replay, And Evaluation
303
+
304
+ Wrap a real transport with `AxMCPRecordingTransport` to capture deterministic
305
+ protocol interactions. Use `AxMCPReplayTransport` for tests, optimization, and
306
+ evaluation. Live MCP evaluation is rejected by default because repeated model
307
+ runs could repeat external side effects; opt in only deliberately.
308
+
309
+ ## Testing Checklist
310
+
311
+ - Use a local deterministic protocol server or replay transport.
312
+ - Assert namespace and tool collisions fail before model execution.
313
+ - Test raw text, image, audio, resource-link, embedded-resource, metadata,
314
+ task, and error results.
315
+ - Test catalog changes during a multi-step run.
316
+ - Test authorization denial before transport execution.
317
+ - Test subscription reconnect and logical resubscription.
318
+ - Test anonymous events cannot match authenticated routes.
319
+ - Test terminal task events resume only the owning identity and correlation.
320
+ - Test cancellation and uncertain outcomes without duplicate side effects.
321
+ - Close clients, listening handles, runtimes, and local servers in `finally`.
322
+
323
+ Generated transports supervise real long-lived GET/SSE connections and resume
324
+ with `Last-Event-ID` when available. Generated event runtimes remain
325
+ host-driven: schedule delayed work with `nextDueAt()` and `runDue()`. Rust hosts
326
+ also call `AxMCPEventSource.poll()` to drain protocol callbacks on the host
327
+ thread. Close the source/runtime before the caller-owned client so unsubscribe
328
+ and cancellation messages can still be sent.
329
+
330
+ For generic inbox, continuation, store, and sink behavior, use the
331
+ `ax-event-runtime` skill. For program-specific behavior, combine this skill with
332
+ `ax-gen`, `ax-agent`, or `ax-flow`.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ax-playbook
3
3
  description: This skill helps an LLM generate correct playbook code using @ax-llm/ax. Use when the user asks about playbook(), AxPlaybook, context playbooks, evolving context, ACE / Agentic Context Engineering, agent.playbook(), or growing/applying task knowledge offline and online with evolve() and update().
4
- version: "23.0.0"
4
+ version: "23.0.2"
5
5
  ---
6
6
 
7
7
  # Playbook Codegen Rules (@ax-llm/ax)
@@ -29,7 +29,10 @@ Use this skill to generate context-playbook code. A playbook grows an evolving b
29
29
  - `applyTo()` injects a `## Context Playbook` block into the program description; calling it repeatedly recomposes from the original base (no stacking).
30
30
  - Keep the offline `metric` deterministic and cheap, like a GEPA metric.
31
31
  - A playbook is plain JSON. Persist `pb.toJSON()` and `load(...)` it into a fresh program for production.
32
- - This is a TypeScript feature; do not suggest it for the generated (Python/Go/Rust/Java/C++) packages yet.
32
+ - The playbook engine, construction-time agent attachment, failure harvesting,
33
+ and verified agent evolution are available in TypeScript and the generated
34
+ Python, Java, C++, Go, and Rust packages. Use each package's native casing and
35
+ callback types.
33
36
 
34
37
  ## Offline Pattern (evolve)
35
38
 
@@ -68,13 +71,40 @@ playbook(prodProgram, { studentAI }).load(snapshot).applyTo(prodProgram);
68
71
 
69
72
  ## Agents
70
73
 
74
+ `a.playbook({ target })` returns an agent-aware `AxAgentPlaybook` (the stage `AxPlaybook` handle plus an agent-level `evolve`). The one playbook the agent renders into its prompt grows three ways:
75
+
76
+ - Continuous (trust): the construction-time `playbook` option (see `ax-agent`) harvests each run's failures automatically — no dataset.
77
+ - On-demand (trust): `apb.update({ example, prediction, feedback })`.
78
+ - Batch verified (proof): `apb.evolve(dataset, options)` runs the full agent over a task set, mines failure clusters, and proposes one playbook bullet per weakness; with `verify` (default on) it keeps a bullet only if held-in improves AND the `validation` held-out set does not regress, else exact rollback. `verify: false` = trust-batch. Bullets-only.
79
+
71
80
  ```typescript
72
81
  const a = agent('ticket:string -> reply:string', { ai });
73
- const apb = a.playbook({ target: 'actor' }); // 'actor' (default) or 'responder'
74
- await apb.update({ example, prediction, feedback }); // injected into the live stage prompt
82
+ const apb = a.playbook({ target: 'actor' }); // agent-aware handle; 'actor' (default) or 'responder'
83
+ await apb.update({ example, prediction, feedback }); // online: injected into the live stage prompt
84
+ const result = await apb.evolve(
85
+ { train, validation }, // AxAgentEvalDataset
86
+ { metric, runsPerTask: 2 }, // verify:true by default
87
+ );
75
88
  ```
76
89
 
77
- Offline `evolve(...)` on an agent stage scores that stage in isolation; for full-pipeline tuning of agent instructions and demos use `agent.optimize(...)` (GEPA).
90
+ The agent-level `evolve(dataset, options)` is distinct from the program-level `pb.evolve(examples, metric)` above: it takes an `AxAgentEvalDataset` plus options, runs the whole pipeline, and returns baseline/final held-in & held-out with per-bullet outcomes (no `{ bestScore }`). For full-pipeline tuning of agent instructions and demos (not the playbook) use `agent.optimize(...)` (GEPA).
91
+
92
+ Generated packages expose that same agent-bound loop with language-shaped APIs:
93
+
94
+ | Language | Agent-bound evolve call |
95
+ |---|---|
96
+ | Python | `agent.playbook().evolve(dataset, options)` |
97
+ | Java | `agent.playbook(null).evolve(dataset, options)` |
98
+ | C++ | `agent.get_playbook()->evolve(dataset, options)` |
99
+ | Go | `agent.GetPlaybook().EvolveAgent(ctx, dataset, options)` |
100
+ | Rust | `playbook.evolve_agent(&mut agent, client, dataset, options)` |
101
+
102
+ All five generated packages thread structured `failureSignals` through agent
103
+ evaluation predictions. The default verify gate accepts a proposed bullet only
104
+ when held-in score improves and held-out score stays within `epsilon`; rejection
105
+ restores the exact prior snapshot. Scoring is host-shaped: TypeScript uses its
106
+ metric, Python/Java/Go can accept a metric callback, and all generated ports can
107
+ use task `score`/`scores` values plus the agent evaluation result.
78
108
 
79
109
  ## Playbook vs optimize()
80
110
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ax-refine
3
3
  description: Use this skill when writing or reviewing Ax bestOfN/refine code, reward functions, thresholds, native sample selection, serial attempts, generated advice, and attempt diagnostics.
4
- version: "23.0.0"
4
+ version: "23.0.2"
5
5
  ---
6
6
 
7
7
  # Ax Refine And BestOfN
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ax-signature
3
3
  description: This skill helps an LLM generate correct DSPy signature code using @ax-llm/ax. Use when the user asks about signatures, s(), f(), field types, string syntax, fluent builder API, validation constraints, or type-safe inputs/outputs.
4
- version: "23.0.0"
4
+ version: "23.0.2"
5
5
  ---
6
6
 
7
7
  # Ax Signature Reference
@@ -41,6 +41,113 @@ Date, datetime, and range fields are AI-friendly but strict. They accept ISO-sty
41
41
  'problem:string -> reasoning!:string, solution:string' // internal with !
42
42
  ```
43
43
 
44
+ ## Extended String Grammar (Modifier Bags + Nested Objects)
45
+
46
+ The string form is constraint-complete: everything the fluent API expresses
47
+ (except Standard Schema fields) can be written in the string. A type takes an
48
+ optional comma-separated, order-free **modifier bag** in parentheses, and
49
+ objects declare structured fields inline.
50
+
51
+ ```typescript
52
+ `userAge:number(min 0, max 120), contactEmail:string(format email, cache), codeSnippet:code(python)
53
+ -> userName:string(pattern "^[a-z_]+$" "lowercase name"), tagList:string(item "a short tag")[] "all tags",
54
+ profileList:object{ fullName:string, userAge?:number(min 0) }[] "matched profiles"`
55
+ ```
56
+
57
+ | Modifier | Applies to | Effect |
58
+ |----------|-----------|--------|
59
+ | `min N` / `max N` | `string`, `number` | String length bounds / numeric value bounds |
60
+ | `format email\|uri\|date\|date-time` | `string` | Format validation |
61
+ | `pattern "regex" ["desc"]` | `string` | Regex validation with optional description |
62
+ | `cache` | top-level input | Prefix-cache breakpoint |
63
+ | `item "desc"` | arrays | Per-item description: `tags:string(item "a tag")[]` |
64
+ | `<language>` | `code` | Language of the snippet: `snippet:code(python)` |
65
+
66
+ - `object{ field:type, opt?:type }` nests recursively; append `[]` for an array of objects.
67
+ - Optional goes on the **name** (`userAge?:number`), never after the type.
68
+ - The string API is **strict**: a modifier that does not apply to its type (e.g. `min` on a boolean) is a parse error, where the fluent API silently ignores it.
69
+ - Inside `object{ ... }`, the `!` internal marker, media types, `cache`, and `item` are rejected (they only apply at the top level).
70
+ - In quoted values, backslashes are doubled — a regex `\d` is written `pattern "\\d+"`.
71
+ - `AxSignature.toString()` renders every construct back to this grammar losslessly, so a signature round-trips — this is what lets a whole flow serialize its node contracts into mermaid `%%ax` directives (see the ax-flow skill).
72
+
73
+ ## Signature Gallery
74
+
75
+ Real-world contracts, one line each — every entry below parses with `s()` as written (`#` lines are captions, not part of the signature):
76
+
77
+ ```text
78
+ # Support triage: several class outputs plus a capped reply draft
79
+ ticketText:string -> priorityClass:class "p0, p1, p2", sentimentClass:class "angry, neutral, happy", replyDraft:string(max 500)
80
+
81
+ # Invoice extraction: regex-validated id, bounded totals, structured line items
82
+ invoiceText:string -> invoiceNumber:string(pattern "^INV-\\d+$" "INV- then digits"), totalAmount:number(min 0), lineItems:object{ description:string, quantity:number(min 1), unitPrice:number }[]
83
+
84
+ # Contact enrichment: optional format-validated outputs
85
+ bioText:string -> contactEmail?:string(format email), websiteUrl?:string(format uri), birthDate?:string(format date)
86
+
87
+ # RAG: cached corpus input plus per-item described citations
88
+ corpusText:string(cache), userQuestion:string -> answerText:string, citedChunks:string(item "verbatim quote")[]
89
+
90
+ # Code generation: language-tagged code outputs
91
+ taskBrief:string -> pythonScript:code(python), testCases:code(python), riskNotes?:string
92
+
93
+ # Chain of thought: internal reasoning stripped from the result
94
+ problemText:string -> reasoning!:string, solutionText:string
95
+
96
+ # Resume parsing: nested objects inside nested arrays
97
+ resumeText:string -> candidateProfile:object{ fullName:string, yearsExperience:number(min 0), skillList:string[], education:object{ schoolName:string, degreeName?:string }[] }
98
+
99
+ # Lead scoring: signature-level description, bounded score, class next step
100
+ "Score sales leads" leadNotes:string -> fitScore:number(min 0, max 100) "0-100 fit", nextStep:class "call, email, drop"
101
+
102
+ # Multimodal: top-level image input with an optional question
103
+ productPhoto:image, question?:string -> productDescription:string, detectedObjects:string[]
104
+
105
+ # Meeting audio: audio input, capped summary, per-item action list
106
+ meetingAudio:audio -> meetingSummary:string(max 1000), actionItems:string(item "one action item")[]
107
+
108
+ # Moderation: class verdict plus structured flagged spans
109
+ postText:string -> moderationVerdict:class "allow, review, block", flaggedSpans:object{ spanText:string, reasonNote:string }[]
110
+
111
+ # Translation: optional locale input
112
+ sourceText:string, targetLocale?:string -> translatedText:string, glossaryHits:string[]
113
+
114
+ # Text-to-SQL: cached schema plus SQL-tagged output
115
+ schemaText:string(cache), questionText:string -> sqlQuery:code(sql), queryNotes?:string(max 200)
116
+
117
+ # Calendar extraction: datetime fields and an optional end
118
+ emailText:string -> eventTitle:string, startsAt:datetime, endsAt?:datetime, attendeeNames:string[]
119
+
120
+ # Booking window: date range, bounded party size, and flexibility flag
121
+ requestText:string -> stayWindow:dateRange, partySize:number(min 1, max 12), flexibleDates:boolean
122
+
123
+ # Contract dates: date fields plus bounded notice period
124
+ contractText:string -> effectiveDate:date, expiryDate?:date, autoRenews:boolean, noticeDays?:number(min 0)
125
+
126
+ # Link audit: URL arrays and an optional primary URL
127
+ pageText:string -> referencedUrls:url[], primaryUrl?:url
128
+
129
+ # Config generation: JSON output plus per-item warnings
130
+ requirementsText:string -> serviceConfig:json, setupWarnings:string(item "one warning")[]
131
+
132
+ # Claims gate: cached policy, bounded confidence, and optional citation
133
+ claimText:string, policyText:string(cache) -> isCovered:boolean, confidenceScore:number(min 0, max 1), citedClause?:string
134
+
135
+ # Earnings extraction: structured period data plus a class outlook
136
+ filingText:string(cache) -> revenueByPeriod:object{ periodLabel:string, amountUsd:number }[], guidanceTone:class "raise, hold, cut"
137
+
138
+ # Pull request review: diff code, cached guide, structured comments, and verdict
139
+ diffText:code(diff), styleGuide?:string(cache) -> reviewComments:object{ filePath:string, lineNumber:number(min 1), commentText:string(max 300) }[], overallVerdict:class "approve, revise"
140
+
141
+ # Incident triage: severity class, optional service, and per-item runbook steps
142
+ alertLog:string -> incidentSeverity:class "sev1, sev2, sev3", suspectedService?:string, runbookSteps:string(item "one step")[]
143
+
144
+ # Product listing: image and file inputs with constrained listing outputs
145
+ productPhoto:image, priceSheet?:file -> listingTitle:string(max 80), bulletPoints:string(item "one selling point")[], priceUsd?:number(min 0)
146
+
147
+ # Study cards: nested object array with an optional difficulty tag
148
+ chapterText:string -> flashCards:object{ questionText:string, answerText:string, difficultyTag?:string }[]
149
+ ```
150
+
44
151
  ## Four Ways to Create Signatures
45
152
 
46
153
  ### 1. String-Based (Recommended for simple cases)
@@ -280,12 +387,20 @@ Bad: `text`, `data`, `input`, `output`, `a`, `x`, `val` (too generic), `1field`
280
387
  // Data Extraction
281
388
  'invoiceText:string -> invoiceNumber:string, totalAmount:number, lineItems:json[]'
282
389
 
390
+ // Constrained string form (no fluent builder needed)
391
+ 'reviewText:string(max 2000) -> rating:number(min 1, max 5), themes:string(item "a theme")[]'
392
+
393
+ // Nested object output in the string form
394
+ 'profileText:string -> profile:object{ fullName:string, age?:number(min 0) }'
395
+
283
396
  // With description
284
397
  '"Answer TypeScript questions" question:string -> answer:string, confidence:number'
285
398
  ```
286
399
 
287
400
  ## Critical Rules
288
401
 
402
+ - The string form is constraint-complete: reach for modifier bags (`string(max 500)`, `number(min 0, max 10)`, `string(format email)`) and inline `object{ ... }` before switching to fluent/zod just for constraints. Reserve fluent/Standard Schema for zod/valibot-backed fields.
403
+ - The string API is strict — a modifier that does not apply to its type is a parse error (the fluent API silently ignores it).
289
404
  - Use `f()` fluent builder, NOT nested `f.array(f.string())` -- those are removed.
290
405
  - Field names must be descriptive (not generic like `text`, `data`, `input`).
291
406
  - Image/file media types are input-only, top-level only; audio may also be a single top-level output.